From 308a146292087449ecb82e4b7935f789ab21c64b Mon Sep 17 00:00:00 2001 From: Rutherther Date: Sun, 12 Nov 2023 12:09:28 +0100 Subject: [PATCH] tests: add register dump, printing --- src/file_program_memory.sv | 2 +- src/ram.sv | 3 +- testbench/tb_cpu_program.sv | 25 +++-- tests/custom/custom_tests.py | 6 +- tests/official/env/p/riscv_test.h | 13 +-- tests/official/official_tests.py | 7 +- tests/run.py | 146 +++++++++++++++++++----------- tests/test_types.py | 7 +- 8 files changed, 133 insertions(+), 76 deletions(-) diff --git a/src/file_program_memory.sv b/src/file_program_memory.sv index 05e981d..8b051a4 100755 --- a/src/file_program_memory.sv +++ b/src/file_program_memory.sv @@ -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]; diff --git a/src/ram.sv b/src/ram.sv index 5fc65c1..fae66d1 100755 --- a/src/ram.sv +++ b/src/ram.sv @@ -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); diff --git a/testbench/tb_cpu_program.sv b/testbench/tb_cpu_program.sv index defae1e..047c3df 100755 --- a/testbench/tb_cpu_program.sv +++ b/testbench/tb_cpu_program.sv @@ -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; diff --git a/tests/custom/custom_tests.py b/tests/custom/custom_tests.py index ddd4e25..3627673 100755 --- a/tests/custom/custom_tests.py +++ b/tests/custom/custom_tests.py @@ -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, diff --git a/tests/official/env/p/riscv_test.h b/tests/official/env/p/riscv_test.h index b4c5f02..c375753 100755 --- a/tests/official/env/p/riscv_test.h +++ b/tests/official/env/p/riscv_test.h @@ -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; //----------------------------------------------------------------------- diff --git a/tests/official/official_tests.py b/tests/official/official_tests.py index 79412ee..c881359 100755 --- a/tests/official/official_tests.py +++ b/tests/official/official_tests.py @@ -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) diff --git a/tests/run.py b/tests/run.py index 1d25ea3..955debf 100755 --- a/tests/run.py +++ b/tests/run.py @@ -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) diff --git a/tests/test_types.py b/tests/test_types.py index e680d48..2315956 100755 --- a/tests/test_types.py +++ b/tests/test_types.py @@ -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}" -- 2.48.1