M src/file_program_memory.sv => src/file_program_memory.sv +1 -1
@@ 4,7 4,7 @@ module file_program_memory
output [31:0] instruction
);
parameter string FILE_NAME;
- parameter WIDTH = 15;
+ parameter WIDTH = 20;
parameter MEM_SIZE = 1 << (WIDTH - 2) - 1;
reg [31:0] imem[0:MEM_SIZE];
M src/ram.sv => src/ram.sv +2 -1
@@ 4,6 4,7 @@ module ram (
input clk, we,
input [31:0] a, wd,
input [3:0] write_byte_enable,
+ input dump,
output [31:0] rd);
reg [31:0] mask;
@@ 33,7 34,7 @@ module ram (
initial begin
if (WRITE_FILE == 1) begin
- wait (a == {32{1'b1}});
+ wait (dump == 1);
#5
$display("Writing memory to file %s.", WRITE_FILE_PATH);
$writememh(WRITE_FILE_PATH, memory);
M testbench/tb_cpu_program.sv => testbench/tb_cpu_program.sv +17 -8
@@ 3,7 3,7 @@ import cpu_types::*;
module tb_cpu_program();
reg clk, rst_n;
- wire [31:0] memory_address, cpu_memory_address, memory_write, memory_out;
+ wire [31:0] memory_address, memory_write, memory_out;
wire [3:0] memory_write_byte_enable;
wire memory_we;
@@ 13,15 13,16 @@ module tb_cpu_program();
wire ebreak;
parameter string CPU_PROGRAM_PATH;
- parameter string CPU_PROGRAM_NAME;
+
+ parameter string TRACE_FILE_PATH = "trace.vcd";
parameter MEMORY_LOAD_FILE = 0;
parameter string MEMORY_LOAD_FILE_PATH = "";
parameter MEMORY_WRITE_FILE = 0;
parameter string MEMORY_WRITE_FILE_PATH = "";
- // assign 0xFF... when ebreak. To save the memory to a file.
- assign memory_address = ebreak == 1'b1 ? {32{1'b1}} : cpu_memory_address;
+ parameter REGISTER_DUMP_FILE = 0;
+ parameter string REGISTER_DUMP_FILE_PATH = "";
cpu uut(
.clk(clk),
@@ 30,7 31,7 @@ module tb_cpu_program();
.instruction(instruction),
.pc(pc),
- .memory_address(cpu_memory_address),
+ .memory_address(memory_address),
.memory_out(memory_out),
.memory_write(memory_write),
.memory_byte_enable(memory_write_byte_enable),
@@ 50,18 51,26 @@ module tb_cpu_program();
.write_byte_enable(memory_write_byte_enable),
.we(memory_we),
.wd(memory_write),
- .rd(memory_out)
+ .rd(memory_out),
+ .dump(ebreak)
);
file_program_memory #(
.FILE_NAME(CPU_PROGRAM_PATH)
) prog_mem_inst(
- .addr(pc[14:0]),
+ .addr(pc[19:0]),
.instruction(instruction)
);
always_ff @ (posedge ebreak) begin
$display("ebreak at %d", pc);
+
+ if (REGISTER_DUMP_FILE == 1)
+ $writememh(REGISTER_DUMP_FILE_PATH, uut.register_file_inst.gprs);
+
+ for (int i = 1; i < 32; i++) begin
+ $display("R%0d:%0d", i, uut.register_file_inst.gprs[i]);
+ end
#15 $finish;
end
@@ 71,7 80,7 @@ module tb_cpu_program();
end
initial begin
- $dumpfile({"waves/cpu_program_", CPU_PROGRAM_NAME, ".vcd"});
+ $dumpfile(TRACE_FILE_PATH);
$dumpvars;
rst_n = 0;
M tests/custom/custom_tests.py => tests/custom/custom_tests.py +4 -2
@@ 37,9 37,10 @@ def find_tests(groups_dir: Path, programs_dir: Path, out_dir: Path, group_name:
group_dir / f"{test_name}-input.dat",
out_dir / f"{test_name}-output.dat",
group_dir / f"{test_name}-expected.dat",
+ out_dir / f"{test_name}-registers.dat",
)
- if not test.input_file.exists() or not test.expected_file.exists():
+ if not test.memory_in_file.exists() or not test.memory_exp_file.exists():
continue
tests.append(test)
@@ 49,7 50,8 @@ def find_tests(groups_dir: Path, programs_dir: Path, out_dir: Path, group_name:
return groups
-def compile_program(make_dir: Path, group: TestGroup) -> bool:
+def compile_program(make_dir: Path, test: Test) -> bool:
+ group = test.group
return subprocess.run(
["make", "-C", make_dir, group.dat_test_file.relative_to(make_dir)],
stdout = subprocess.DEVNULL,
M tests/official/env/p/riscv_test.h => tests/official/env/p/riscv_test.h +7 -6
@@ 139,16 139,17 @@ _start: \
//-----------------------------------------------------------------------
#define RVTEST_PASS \
- addi x0, zero, 0; \
- addi x1, zero, 0xFF; \
- sw x1, 0(x0); \
+ addi x1, zero, 0xAA; \
+ sw x1, 0(zero); \
+ nop; \
ebreak;
#define TESTNUM gp
#define RVTEST_FAIL \
- addi x0, zero, 0; \
- addi x1, zero, 0; \
- sw x1, 0(x0); \
+ addi x1, zero, 0xFF; \
+ sw x1, 0(zero); \
+ sw x1, 4(TESTNUM); \
+ nop; \
ebreak;
//-----------------------------------------------------------------------
M tests/official/official_tests.py => tests/official/official_tests.py +4 -3
@@ 29,9 29,10 @@ def find_tests(out_dir: Path) -> list[TestGroup]:
tests.append(Test(
group = group,
name = test_name,
- input_file = here / "input.dat",
- output_file = out_dir / f"{group.name}-output.dat",
- expected_file = here / "expected.dat",
+ memory_in_file = out_dir / f"rv32ui_{test_name}.dat",
+ memory_out_file = out_dir / f"rv32ui_{test_name}_output.dat",
+ memory_exp_file = here / "expected.dat",
+ register_dump_file = out_dir / f"rv32ui_{test_name}_registers.dat"
))
groups.append(group)
M tests/run.py => tests/run.py +94 -52
@@ 16,9 16,16 @@ sys.path.append('./official')
import custom_tests
import official_tests
+PROGRAM_FILE = "program.dat"
+MEMORY_WRITE_FILE = "memory_out.dat"
+MEMORY_LOAD_FILE = "memory_in.dat"
+REGISTER_FILE = "register_dump.dat"
+SIMULATE_EXE = "simulate_cpu_program"
+TRACE_FILE = "trace.vcd"
+
def validate_test(test: Test) -> Validation:
- expected = test.expected_file.read_text()
- actual = test.output_file.read_text()
+ expected = test.memory_exp_file.read_text()
+ actual = test.memory_out_file.read_text()
expected_arr = list(filter(lambda word: word != "", re.split(r"[\n ]+", expected)))
actual_arr = re.split(r"[\n ]+", actual)
@@ 32,14 39,26 @@ def validate_test(test: Test) -> Validation:
matches = (actual_arr == expected_arr)
)
-def compile(project_dir: Path, comp_list: Path, out_dir: Path) -> bool:
- program_path = out_dir / "program.dat"
- memory_write_file = out_dir / "memory_out.dat"
- memory_load_file = out_dir / "memory_in.dat"
+def print_registers(test: Test):
+ reg_names = [ "ze", "ra", "sp", "gp", "tp", "t0", "t1", "t2", "s0", "s1", "a0", "a1", "a2", "a3", "a4", "a5", "a6", "a7", "s2", "s3", "s4", "s5", "s6", "s7", "s8", "s9", "s10", "s11", "t3", "t4", "t5", "t6"]
+ values = ["00000000"] + re.split(r"[\n ]+", test.register_dump_file.read_text())
+
+ print(" ", end = '')
+ for i, (name, value) in enumerate(zip(reg_names, values)):
+ print(f"{name}: 0x{value}", end = '\n ' if i % 4 == 3 else ' ')
+
+def compile(project_dir: Path, comp_list: Path, out_dir: Path, trace: bool) -> bool:
+ program_path = out_dir / PROGRAM_FILE
+ memory_load_file = out_dir / MEMORY_LOAD_FILE
+ memory_write_file = out_dir / MEMORY_WRITE_FILE
+ register_file = out_dir / REGISTER_FILE
+ trace_file = out_dir / TRACE_FILE
generics = {
'CPU_PROGRAM_PATH': f"\\\"{program_path}\\\"",
- 'CPU_PROGRAM_NAME': f"\\\"testcase\\\"",
+ 'TRACE_FILE_PATH': f"\\\"{trace_file}\\\"",
+ 'REGISTER_DUMP_FILE': 1,
+ 'REGISTER_DUMP_FILE_PATH': f"\\\"{register_file}\\\"",
'MEMORY_LOAD_FILE': 1,
'MEMORY_LOAD_FILE_PATH': f"\\\"{memory_load_file}\\\"",
'MEMORY_WRITE_FILE': 1,
@@ 54,10 73,14 @@ def compile(project_dir: Path, comp_list: Path, out_dir: Path) -> bool:
params.append("--Mdir")
params.append(f"{out_dir}")
params.append("-o")
- params.append(f"simulate_cpu_program")
+ params.append(SIMULATE_EXE)
params.append("--top")
params.append("tb_cpu_program")
+ if trace:
+ params.append("--trace")
+ params.append("--trace-max-array 4096")
+
for line in comp_list.read_text().split('\n'):
if line != "":
params.append(f"{project_dir / line}")
@@ 70,24 93,36 @@ def compile(project_dir: Path, comp_list: Path, out_dir: Path) -> bool:
).returncode == 0
def run_test(out_dir: Path, test: Test) -> bool:
- program_path = out_dir / "program.dat"
- memory_write_file = out_dir / "memory_out.dat"
- memory_load_file = out_dir / "memory_in.dat"
+ program_path = out_dir / PROGRAM_FILE
+ memory_load_file = out_dir / MEMORY_LOAD_FILE
+ memory_write_file = out_dir / MEMORY_WRITE_FILE
+ register_file = out_dir / REGISTER_FILE
- shutil.copy(test.input_file, memory_load_file)
+ shutil.copy(test.memory_in_file, memory_load_file)
shutil.copy(test.group.dat_test_file, program_path)
subprocess.run(
- [out_dir / f"simulate_cpu_program"],
+ [out_dir / SIMULATE_EXE],
stdout = subprocess.DEVNULL,
shell = True,
check = True,
)
- shutil.copy(memory_write_file, test.output_file)
+ shutil.copy(memory_write_file, test.memory_out_file)
+ shutil.copy(register_file, test.register_dump_file)
return True
+def filter_tests(groups: list[TestGroup], group_name: str|None, test_name: str|None) -> list[TestGroup]:
+ if group_name is not None:
+ groups = list(filter(lambda g: g.name == group_name, groups))
+
+ if test_name is not None:
+ for group in groups:
+ group.tests = list(filter(lambda t: t.name == test_name, group.tests))
+
+ return groups
+
# Program
parser = argparse.ArgumentParser("Test simple RISC-V processor written in Verilog.")
parser.add_argument(
@@ 109,6 144,21 @@ parser.add_argument(
default = "custom",
help = "Type of the testcases, either custom testcases or official riscv selftests.",
)
+parser.add_argument(
+ "--trace",
+ action = "store_true",
+ help = "Trace, produce vcd file",
+)
+parser.add_argument(
+ "--print-registers",
+ action = "store_true",
+ help = "Trace, produce vcd file",
+)
+# parser.add_argument(
+# "--print-memory",
+# type = int,
+# help = "Trace, produce vcd file",
+# )
args = parser.parse_args()
@@ 118,55 168,47 @@ programs_dir = project_dir / "programs"
out_dir = here / "out"
groups_dir = here / "custom"
-# TODO support multiple tests
-group_name, test_name = args.filter[0].split('.') if args.filter is not None else (None, None)
+# TODO support multiple filters
+filt = args.filter[0].split('.') if args.filter is not None and len(args.filter) > 0 else [None, None]
+
+group_name = filt[0]
+test_name = None
+if len(filt) >= 2:
+ test_name = filt[1]
-compile(project_dir, here / "comp_list.lst", out_dir)
+compile(project_dir, here / "comp_list.lst", out_dir, args.trace)
if args.type == "custom":
test_groups: list[TestGroup] = custom_tests.find_tests(
groups_dir, programs_dir, out_dir, group_name, test_name
)
- if args.command == "list":
- print("Found these tests:")
- for group in test_groups:
- for test in group.tests:
- print(f" {test}")
- sys.exit(0)
-
- for group in test_groups:
- custom_tests.compile_program(project_dir, group)
- for test in group.tests:
- run_test(out_dir, test)
-
- validation = validate_test(test)
-
- if validation.matches:
- print(f"{test.group.name}.{test.name} {bcolors.OKGREEN}passed{bcolors.ENDC}")
- else:
- print(f"{test.group.name}.{test.name} {bcolors.FAIL}failed{bcolors.ENDC}")
- print(f" Got {validation.actual}. Expected {validation.expected}")
+ compile_program = custom_tests.compile_program
else: # official
test_groups: list[TestGroup] = official_tests.find_tests(
- here / "official" / "out"
+ here / "official" / "out",
)
+ test_groups = filter_tests(test_groups, group_name, test_name)
+ compile_program = official_tests.compile_program
- if args.command == "list":
- print("Found these tests:")
- for group in test_groups:
- for test in group.tests:
- print(f" {test}")
- sys.exit(0)
-
+if args.command == "list":
+ print("Found these tests:")
for group in test_groups:
for test in group.tests:
- official_tests.compile_program(project_dir, test)
- run_test(out_dir, test)
+ print(f" {test}")
+ sys.exit(0)
+
+for group in test_groups:
+ for test in group.tests:
+ compile_program(project_dir, test)
+ run_test(out_dir, test)
+
+ validation = validate_test(test)
- validation = validate_test(test)
+ if validation.matches:
+ print(f"{test.group.name}.{test.name} {bcolors.OKGREEN}passed{bcolors.ENDC}")
+ else:
+ print(f"{test.group.name}.{test.name} {bcolors.FAIL}failed{bcolors.ENDC}")
+ print(f" Got {validation.actual}. Expected {validation.expected}")
- if validation.matches:
- print(f"{test.group.name}.{test.name} {bcolors.OKGREEN}passed{bcolors.ENDC}")
- else:
- print(f"{test.group.name}.{test.name} {bcolors.FAIL}failed{bcolors.ENDC}")
- print(f" Got {validation.actual}. Expected {validation.expected}")
+ if args.print_registers:
+ print_registers(test)
M tests/test_types.py => tests/test_types.py +4 -3
@@ 31,9 31,10 @@ class Test:
group: TestGroup
name: str
- input_file: Path
- output_file: Path
- expected_file: Path
+ memory_in_file: Path
+ memory_out_file: Path
+ memory_exp_file: Path
+ register_dump_file: Path
def __str__(self):
return f"{self.group.name}.{self.name}"