Add test script to make it easier to add software testcases
This commit is contained in:
		
							parent
							
								
									7d2fa6a049
								
							
						
					
					
						commit
						3d2c912b4f
					
				|  | @ -863,6 +863,7 @@ always @ (*) begin | ||||||
| 		{MEMOP_LBU , 2'b10}: m_rdata_pick_sext = {{24{1'b0           }}, bus_rdata_d[23:16]}; | 		{MEMOP_LBU , 2'b10}: m_rdata_pick_sext = {{24{1'b0           }}, bus_rdata_d[23:16]}; | ||||||
| 		{MEMOP_LBU , 2'b11}: m_rdata_pick_sext = {{24{1'b0           }}, bus_rdata_d[31:24]}; | 		{MEMOP_LBU , 2'b11}: m_rdata_pick_sext = {{24{1'b0           }}, bus_rdata_d[31:24]}; | ||||||
| 		{MEMOP_LW  , 2'bzz}: m_rdata_pick_sext = bus_rdata_d; | 		{MEMOP_LW  , 2'bzz}: m_rdata_pick_sext = bus_rdata_d; | ||||||
|  | 		{MEMOP_LR_W, 2'bzz}: m_rdata_pick_sext = bus_rdata_d; | ||||||
| 		default:             m_rdata_pick_sext = 32'hxxxx_xxxx; | 		default:             m_rdata_pick_sext = 32'hxxxx_xxxx; | ||||||
| 	endcase | 	endcase | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -1,5 +0,0 @@ | ||||||
| SRCS := ../common/init.S main.c |  | ||||||
| APP  := amo_timer_irq |  | ||||||
| CCFLAGS = -march=rv32imac -Os |  | ||||||
| 
 |  | ||||||
| include ../common/src_only_app.mk |  | ||||||
|  | @ -12,6 +12,7 @@ CROSS_PREFIX ?= riscv32-unknown-elf- | ||||||
| TBDIR        ?= ../tb_cxxrtl | TBDIR        ?= ../tb_cxxrtl | ||||||
| INCDIR       ?= ../common | INCDIR       ?= ../common | ||||||
| MAX_CYCLES   ?= 100000 | MAX_CYCLES   ?= 100000 | ||||||
|  | TMP_PREFIX   ?= tmp/ | ||||||
| 
 | 
 | ||||||
| ###############################################################################
 | ###############################################################################
 | ||||||
| 
 | 
 | ||||||
|  | @ -20,29 +21,30 @@ MAX_CYCLES   ?= 100000 | ||||||
| 
 | 
 | ||||||
| all: run | all: run | ||||||
| 
 | 
 | ||||||
| run: $(APP).bin | run: $(TMP_PREFIX)$(APP).bin | ||||||
| 	$(TBDIR)/tb $(APP).bin $(APP)_run.vcd --cycles $(MAX_CYCLES) | 	$(TBDIR)/tb $(TMP_PREFIX)$(APP).bin $(TMP_PREFIX)$(APP)_run.vcd --cycles $(MAX_CYCLES) | ||||||
| 
 | 
 | ||||||
| view: run | view: run | ||||||
| 	gtkwave $(APP)_run.vcd | 	gtkwave $(TMP_PREFIX)$(APP)_run.vcd | ||||||
| 
 | 
 | ||||||
| bin: $(APP).bin | bin: $(TMP_PREFIX)$(APP).bin | ||||||
| 
 | 
 | ||||||
| tb: | tb: | ||||||
| 	$(MAKE) -C $(TBDIR) tb | 	$(MAKE) -C $(TBDIR) tb | ||||||
| 
 | 
 | ||||||
| clean: | clean: | ||||||
| 	rm -f $(APP).elf $(APP).bin $(APP).dis $(APP)_run.vcd | 	rm -rf $(TMP_PREFIX) | ||||||
| 
 | 
 | ||||||
| clean_tb: clean | clean_tb: clean | ||||||
| 	$(MAKE) -C $(TBDIR) clean | 	$(MAKE) -C $(TBDIR) clean | ||||||
| 
 | 
 | ||||||
| ###############################################################################
 | ###############################################################################
 | ||||||
| 
 | 
 | ||||||
| $(APP).bin: $(APP).elf | $(TMP_PREFIX)$(APP).bin: $(TMP_PREFIX)$(APP).elf | ||||||
| 	$(CROSS_PREFIX)objcopy -O binary $^ $@ | 	$(CROSS_PREFIX)objcopy -O binary $^ $@ | ||||||
| 	$(CROSS_PREFIX)objdump -h $(APP).elf > $(APP).dis | 	$(CROSS_PREFIX)objdump -h $^ > $(TMP_PREFIX)$(APP).dis | ||||||
| 	$(CROSS_PREFIX)objdump -d $(APP).elf >> $(APP).dis | 	$(CROSS_PREFIX)objdump -d $^ >> $(TMP_PREFIX)$(APP).dis | ||||||
| 
 | 
 | ||||||
| $(APP).elf: $(SRCS) $(wildcard %.h) | $(TMP_PREFIX)$(APP).elf: $(SRCS) $(wildcard %.h) | ||||||
| 	$(CROSS_PREFIX)gcc $(CCFLAGS) $(SRCS) -T $(LDSCRIPT) $(addprefix -I,$(INCDIR)) -o $(APP).elf | 	mkdir -p $(TMP_PREFIX) | ||||||
|  | 	$(CROSS_PREFIX)gcc $(CCFLAGS) $(SRCS) -T $(LDSCRIPT) $(addprefix -I,$(INCDIR)) -o $@ | ||||||
|  |  | ||||||
|  | @ -55,4 +55,6 @@ static inline void tb_printf(const char *fmt, ...) { | ||||||
| 	va_end(args); | 	va_end(args); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | #define tb_assert(cond, ...) if (!(cond)) {tb_printf(__VA_ARGS__); tb_exit(-1);} | ||||||
|  | 
 | ||||||
| #endif | #endif | ||||||
|  |  | ||||||
|  | @ -1,5 +0,0 @@ | ||||||
| SRCS := ../common/init.S main.c |  | ||||||
| APP  := ecall_simple |  | ||||||
| CCFLAGS = -march=rv32ic |  | ||||||
| 
 |  | ||||||
| include ../common/src_only_app.mk |  | ||||||
|  | @ -1,5 +0,0 @@ | ||||||
| SRCS := ../common/init.S main.c |  | ||||||
| APP  := lr_sc_smoke |  | ||||||
| CCFLAGS = -march=rv32imac -Os |  | ||||||
| 
 |  | ||||||
| include ../common/src_only_app.mk |  | ||||||
|  | @ -0,0 +1 @@ | ||||||
|  | tmp | ||||||
|  | @ -1,5 +1,5 @@ | ||||||
| SRCS := ../common/init.S main.c | APP  := hellow | ||||||
| APP  := amo_smoke | SRCS := ../common/init.S $(APP).c | ||||||
| CCFLAGS = -march=rv32imac -Os | CCFLAGS = -march=rv32imac -Os | ||||||
| 
 | 
 | ||||||
| include ../common/src_only_app.mk | include ../common/src_only_app.mk | ||||||
|  | @ -0,0 +1,20 @@ | ||||||
|  | Software Testcases | ||||||
|  | ================== | ||||||
|  | 
 | ||||||
|  | A smorgasbord of software testcases for various features and cases that aren't well-covered by upstream tests such as `riscv-arch-test`, the `riscv-test` end-to-end debug tests or `riscv-formal`. Each test consists of one C file. | ||||||
|  | 
 | ||||||
|  | Some tests have an expected text output associated with them -- the test passes if this text output matches, and `main()` exits with a zero return code. Other tests are completely self-checking, reporting pass/fail only with the return code from `main()`. This means there is _no point_ running these tests if the processor is in a fundamentally broken state (e.g. doesn't pass ISA compliance) and can't be trusted to check itself.  | ||||||
|  | 
 | ||||||
|  | To run the tests: | ||||||
|  | 
 | ||||||
|  | ```bash | ||||||
|  | ./runtests | ||||||
|  | ``` | ||||||
|  | 
 | ||||||
|  | This will first rebuild the simulator (`../tb_cxxrtl/`) if needed, then build and run all the software testcases, then print out a summary of test pass/fail status. The `./run_tests` executable itself returns a successful exit code if and only if all tests passed. A VCD trace and printf log will be created for each test, with the same name as the test, for debugging failures. | ||||||
|  | 
 | ||||||
|  | To clean up the junk: | ||||||
|  | 
 | ||||||
|  | ```bash | ||||||
|  | ./cleantests | ||||||
|  | ``` | ||||||
|  | @ -3,8 +3,6 @@ | ||||||
| 
 | 
 | ||||||
| volatile uint32_t scratch[2]; | volatile uint32_t scratch[2]; | ||||||
| 
 | 
 | ||||||
| #define test_assert(cond, ...) if (!(cond)) {tb_printf(__VA_ARGS__); return -1;} |  | ||||||
| 
 |  | ||||||
| int main() { | int main() { | ||||||
| 
 | 
 | ||||||
| 	scratch[0] = 0; | 	scratch[0] = 0; | ||||||
|  | @ -0,0 +1,12 @@ | ||||||
|  | Initial value: 0 | ||||||
|  | amoadd.w rd, 1, (&addr) -> fetched 0 | ||||||
|  | amoadd.w rd, 2, (&addr) -> fetched 1 | ||||||
|  | amoadd.w rd, 3, (&addr) -> fetched 3 | ||||||
|  | amoadd.w rd, 4, (&addr) -> fetched 6 | ||||||
|  | amoadd.w rd, 5, (&addr) -> fetched 10 | ||||||
|  | amoadd.w rd, 6, (&addr) -> fetched 15 | ||||||
|  | amoadd.w rd, 7, (&addr) -> fetched 21 | ||||||
|  | amoadd.w rd, 8, (&addr) -> fetched 28 | ||||||
|  | amoadd.w rd, 9, (&addr) -> fetched 36 | ||||||
|  | amoadd.w rd, 10, (&addr) -> fetched 45 | ||||||
|  | Final value: 55 | ||||||
|  | @ -2,10 +2,9 @@ | ||||||
| [*] GTKWave Analyzer v3.3.103 (w)1999-2019 BSI | [*] GTKWave Analyzer v3.3.103 (w)1999-2019 BSI | ||||||
| [*] Sat Dec  4 23:25:19 2021 | [*] Sat Dec  4 23:25:19 2021 | ||||||
| [*] | [*] | ||||||
| [dumpfile] "/home/luke/proj/hazard3/test/sim/amo_smoke/amo_smoke_run.vcd" | [dumpfile] "tmp/amo_smoke_run.vcd" | ||||||
| [dumpfile_mtime] "Sat Dec  4 23:21:36 2021" | [dumpfile_mtime] "Sat Dec  4 23:21:36 2021" | ||||||
| [dumpfile_size] 6246687 | [dumpfile_size] 6246687 | ||||||
| [savefile] "/home/luke/proj/hazard3/test/sim/amo_smoke/amo_smoke.gtkw" |  | ||||||
| [timestart] 420 | [timestart] 420 | ||||||
| [size] 1975 1095 | [size] 1975 1095 | ||||||
| [pos] -1 -1 | [pos] -1 -1 | ||||||
|  | @ -7,8 +7,6 @@ | ||||||
| #define IRQ_INTERVAL 201 | #define IRQ_INTERVAL 201 | ||||||
| #define N_AMOS 1000 | #define N_AMOS 1000 | ||||||
| 
 | 
 | ||||||
| #define test_assert(cond, ...) if (!(cond)) {tb_printf(__VA_ARGS__); return -1;} |  | ||||||
| 
 |  | ||||||
| volatile uint32_t amo_count, irq_count; | volatile uint32_t amo_count, irq_count; | ||||||
| 
 | 
 | ||||||
| void __attribute__((interrupt)) isr_machine_timer() { | void __attribute__((interrupt)) isr_machine_timer() { | ||||||
|  | @ -32,14 +30,14 @@ int main() { | ||||||
| 			: "=r" (fetch) | 			: "=r" (fetch) | ||||||
| 			: "r" (1), "r" (&amo_count) | 			: "r" (1), "r" (&amo_count) | ||||||
| 		); | 		); | ||||||
| 		test_assert(fetch == i, "Bad fetch, expected %u, got %u\n", i, fetch); | 		tb_assert(fetch == i, "Bad fetch, expected %u, got %u\n", i, fetch); | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	asm volatile ("csrci mstatus, 0x8"); | 	asm volatile ("csrci mstatus, 0x8"); | ||||||
| 	uint32_t current_time = mm_timer->mtime; | 	uint32_t current_time = mm_timer->mtime; | ||||||
| 	tb_printf("At time %u, received %u IRQs\n", current_time, irq_count); | 	tb_printf("At time %u, received %u IRQs\n", current_time, irq_count); | ||||||
| 	test_assert(current_time / IRQ_INTERVAL + 1 == irq_count, "Bad IRQ count\n"); | 	tb_assert(current_time / IRQ_INTERVAL + 1 == irq_count, "Bad IRQ count\n"); | ||||||
| 	test_assert(amo_count == N_AMOS, "Bad final AMO count %u\n", N_AMOS); | 	tb_assert(amo_count == N_AMOS, "Bad final AMO count %u\n", N_AMOS); | ||||||
| 
 | 
 | ||||||
| 	return 0; | 	return 0; | ||||||
| } | } | ||||||
|  | @ -0,0 +1,2 @@ | ||||||
|  | #!/bin/bash | ||||||
|  | rm -rf tmp | ||||||
|  | @ -5,7 +5,7 @@ | ||||||
| 
 | 
 | ||||||
| #define read_csr(csrname) ({ \ | #define read_csr(csrname) ({ \ | ||||||
|   uint32_t __csr_tmp_u32; \ |   uint32_t __csr_tmp_u32; \ | ||||||
|   __asm__ ("csrr %0, " #csrname : "=r" (__csr_tmp_u32)); \ |   asm volatile ("csrr %0, " #csrname : "=r" (__csr_tmp_u32)); \ | ||||||
|   __csr_tmp_u32; \ |   __csr_tmp_u32; \ | ||||||
| }) | }) | ||||||
| 
 | 
 | ||||||
|  | @ -0,0 +1,11 @@ | ||||||
|  | mcause initial value: | ||||||
|  | 00000000 | ||||||
|  | Handling ecall. Call number: | ||||||
|  | 00000123 | ||||||
|  | Handling ecall. Call number: | ||||||
|  | 00000456 | ||||||
|  | Handling ecall. Call number: | ||||||
|  | deadbeef | ||||||
|  | Finished making calls. | ||||||
|  | mcause final value: | ||||||
|  | 0000000b | ||||||
|  | @ -2,10 +2,9 @@ | ||||||
| [*] GTKWave Analyzer v3.3.103 (w)1999-2019 BSI | [*] GTKWave Analyzer v3.3.103 (w)1999-2019 BSI | ||||||
| [*] Sat May 22 07:37:18 2021 | [*] Sat May 22 07:37:18 2021 | ||||||
| [*] | [*] | ||||||
| [dumpfile] "/home/luke/proj/hazard3/test/ecall_simple/ecall_simple_run.vcd" | [dumpfile] "tmp/ecall_simple_run.vcd" | ||||||
| [dumpfile_mtime] "Sat May 22 07:33:26 2021" | [dumpfile_mtime] "Sat May 22 07:33:26 2021" | ||||||
| [dumpfile_size] 1269546 | [dumpfile_size] 1269546 | ||||||
| [savefile] "/home/luke/proj/hazard3/test/ecall_simple/ecall_simple_run.gtkw" |  | ||||||
| [timestart] 314 | [timestart] 314 | ||||||
| [size] 2560 1403 | [size] 2560 1403 | ||||||
| [pos] -1 -1 | [pos] -1 -1 | ||||||
|  | @ -0,0 +1,6 @@ | ||||||
|  | #include "tb_cxxrtl_io.h" | ||||||
|  | 
 | ||||||
|  | int main() { | ||||||
|  | 	tb_puts("Hello world from Hazard3 + CXXRTL!\n"); | ||||||
|  | 	return 0; | ||||||
|  | } | ||||||
|  | @ -0,0 +1 @@ | ||||||
|  | Hello world from Hazard3 + CXXRTL! | ||||||
|  | @ -2,10 +2,9 @@ | ||||||
| [*] GTKWave Analyzer v3.3.103 (w)1999-2019 BSI | [*] GTKWave Analyzer v3.3.103 (w)1999-2019 BSI | ||||||
| [*] Sat Dec  4 14:31:51 2021 | [*] Sat Dec  4 14:31:51 2021 | ||||||
| [*] | [*] | ||||||
| [dumpfile] "/home/luke/proj/hazard3/test/sim/lr_sc_smoke/lr_sc_smoke_run.vcd" | [dumpfile] "tmp/lr_sc_smoke_run.vcd" | ||||||
| [dumpfile_mtime] "Sat Dec  4 14:20:04 2021" | [dumpfile_mtime] "Sat Dec  4 14:20:04 2021" | ||||||
| [dumpfile_size] 2577335 | [dumpfile_size] 2577335 | ||||||
| [savefile] "/home/luke/proj/hazard3/test/sim/lr_sc_smoke/lr_sc_smoke.gtkw" |  | ||||||
| [timestart] 842 | [timestart] 842 | ||||||
| [size] 1975 1095 | [size] 1975 1095 | ||||||
| [pos] -1 -1 | [pos] -1 -1 | ||||||
|  | @ -0,0 +1,76 @@ | ||||||
|  | #!/usr/bin/env python3 | ||||||
|  | 
 | ||||||
|  | import os | ||||||
|  | import subprocess | ||||||
|  | import sys | ||||||
|  | 
 | ||||||
|  | testlist = [] | ||||||
|  | 
 | ||||||
|  | for path in os.listdir(): | ||||||
|  | 	if os.path.isfile(path) and path.endswith(".c"): | ||||||
|  | 		testlist.append(path[:-2]) | ||||||
|  | 
 | ||||||
|  | testlist = sorted(testlist) | ||||||
|  | 
 | ||||||
|  | tb_build_ret = subprocess.run( | ||||||
|  | 	["make", "-C", "../tb_cxxrtl", "tb"], | ||||||
|  | 	timeout=120 | ||||||
|  | ) | ||||||
|  | if tb_build_ret.returncode != 0: | ||||||
|  | 	sys.exit("Failed.") | ||||||
|  | 
 | ||||||
|  | all_passed = True | ||||||
|  | 
 | ||||||
|  | for test in testlist: | ||||||
|  | 	sys.stdout.write(f"{test:<30}") | ||||||
|  | 
 | ||||||
|  | 	test_build_ret = subprocess.run( | ||||||
|  | 		["make", f"APP={test}", f"tmp/{test}.bin"], | ||||||
|  | 		stdout=subprocess.DEVNULL | ||||||
|  | 	) | ||||||
|  | 	if test_build_ret.returncode != 0: | ||||||
|  | 		print("\033[33m[MK ERR]\033[39m") | ||||||
|  | 		all_passed = False | ||||||
|  | 		continue | ||||||
|  | 
 | ||||||
|  | 	test_run_ret = subprocess.run( | ||||||
|  | 		["../tb_cxxrtl/tb", f"tmp/{test}.bin", f"tmp/{test}_run.vcd", "--cycles", "1000000"], | ||||||
|  | 		stdout = subprocess.PIPE, | ||||||
|  | 		timeout=10 | ||||||
|  | 	) | ||||||
|  | 	with open(f"tmp/{test}.log", "wb") as f: | ||||||
|  | 		f.write(test_run_ret.stdout) | ||||||
|  | 
 | ||||||
|  | 	# Testbench itself should always exit successfully. | ||||||
|  | 	if test_run_ret.returncode != 0: | ||||||
|  | 		sys.exit("Negative return code from testbench!") | ||||||
|  | 
 | ||||||
|  | 	# Pass if the program under test has zero exit code AND its output matches | ||||||
|  | 	# the expected output (if there is an expected_output file) | ||||||
|  | 
 | ||||||
|  | 	output_lines = test_run_ret.stdout.decode("utf-8").strip().splitlines() | ||||||
|  | 	returncode = -1 | ||||||
|  | 	if len(output_lines) >= 2: | ||||||
|  | 		exit_line = output_lines[-2] | ||||||
|  | 		if exit_line.startswith("CPU requested halt"): | ||||||
|  | 			try: | ||||||
|  | 				returncode = int(exit_line.split(" ")[-1]) | ||||||
|  | 			except: | ||||||
|  | 				pass | ||||||
|  | 	if returncode != 0: | ||||||
|  | 		print("\033[31m[BADRET]\033[39m") | ||||||
|  | 		all_passed = False | ||||||
|  | 		continue | ||||||
|  | 
 | ||||||
|  | 	if os.path.exists(f"{test}.expected_output"): | ||||||
|  | 		expected_lines = open(f"{test}.expected_output").read().strip().splitlines() | ||||||
|  | 		if expected_lines != output_lines[:-2]: | ||||||
|  | 			print("\033[31m[BADOUT]\033[39m") | ||||||
|  | 			all_passed = False | ||||||
|  | 			continue | ||||||
|  | 
 | ||||||
|  | 	print("\033[32m[PASSED]\033[39m") | ||||||
|  | 
 | ||||||
|  | sys.exit(not all_passed) | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | @ -0,0 +1,12 @@ | ||||||
|  | Enabling IRQS... | ||||||
|  | IRQ 1 | ||||||
|  | IRQ 2 | ||||||
|  | IRQ 3 | ||||||
|  | IRQ 4 | ||||||
|  | IRQ 5 | ||||||
|  | IRQ 6 | ||||||
|  | IRQ 7 | ||||||
|  | IRQ 8 | ||||||
|  | IRQ 9 | ||||||
|  | IRQ 10 | ||||||
|  | Took 10 IRQs, span 9 times | ||||||
|  | @ -16,7 +16,7 @@ MUL_FAST         := 1 | ||||||
| MULH_FAST        := 1 | MULH_FAST        := 1 | ||||||
| REDUCED_BYPASS   := 0 | REDUCED_BYPASS   := 0 | ||||||
| 
 | 
 | ||||||
| .PHONY: clean tb all | .PHONY: clean all | ||||||
| 
 | 
 | ||||||
| all: tb | all: tb | ||||||
| 
 | 
 | ||||||
|  | @ -36,7 +36,7 @@ SYNTH_CMD += chparam -set MUL_FAST $(MUL_FAST) $(TOP); | ||||||
| SYNTH_CMD += chparam -set MULH_FAST $(MULH_FAST) $(TOP); | SYNTH_CMD += chparam -set MULH_FAST $(MULH_FAST) $(TOP); | ||||||
| SYNTH_CMD += write_cxxrtl dut.cpp | SYNTH_CMD += write_cxxrtl dut.cpp | ||||||
| 
 | 
 | ||||||
| dut.cpp: | dut.cpp: $(shell listfiles ../../../hdl/hazard3.f); | ||||||
| 	yosys -p "$(SYNTH_CMD)" 2>&1 > cxxrtl.log | 	yosys -p "$(SYNTH_CMD)" 2>&1 > cxxrtl.log | ||||||
| 
 | 
 | ||||||
| clean:: | clean:: | ||||||
|  |  | ||||||
|  | @ -1,5 +0,0 @@ | ||||||
| SRCS := ../common/init.S main.c |  | ||||||
| APP  := wfi_loop |  | ||||||
| CCFLAGS = -march=rv32imc -Os |  | ||||||
| 
 |  | ||||||
| include ../common/src_only_app.mk |  | ||||||
		Loading…
	
		Reference in New Issue