229 lines
		
	
	
		
			7.0 KiB
		
	
	
	
		
			Python
		
	
	
		
			Executable File
		
	
	
			
		
		
	
	
			229 lines
		
	
	
		
			7.0 KiB
		
	
	
	
		
			Python
		
	
	
		
			Executable File
		
	
	
| #!/usr/bin/env python3
 | |
| 
 | |
| from random import seed, randrange
 | |
| from pathlib import Path
 | |
| 
 | |
| XLEN = 32
 | |
| XLEN_MASK = (1 << XLEN) - 1
 | |
| 
 | |
| all_onehot = [1 << i for i in range(XLEN)]
 | |
| all_onehot0 = [0] + all_onehot
 | |
| all_onehot0_neg = all_onehot0 + [~i & XLEN_MASK for i in all_onehot0]
 | |
| all_shamt = list(range(XLEN))
 | |
| 
 | |
| seed(XLEN_MASK // 29)
 | |
| RAND_COUNT = 20
 | |
| def get_random():
 | |
| 	return [randrange(0, 1 << XLEN) for i in range(RAND_COUNT)]
 | |
| 
 | |
| # Lists of instructions and their test inputs
 | |
| 
 | |
| instr_one_operand = [
 | |
| 	("clz"    , [*all_onehot0_neg, *get_random()]),
 | |
| 	("cpop"   , [*all_onehot0_neg, *get_random()]),
 | |
| 	("ctz"    , [*all_onehot0_neg, *get_random()]),
 | |
| 	("orc.b"  , [*all_onehot0_neg, *get_random()]),
 | |
| 	("rev8"   , [*all_onehot0_neg, *get_random()]),
 | |
| 	("sext.b" , [*all_onehot0_neg, *get_random()]),
 | |
| 	("sext.h" , [*all_onehot0_neg, *get_random()]),
 | |
| 	("zext.h" , [*all_onehot0_neg, *get_random()]),
 | |
| ]
 | |
| 
 | |
| instr_reg_reg = [
 | |
| 	("sh1add" , [*all_onehot0_neg, *get_random()], [*all_onehot0_neg, *all_shamt, *get_random()]),
 | |
| 	("sh2add" , [*all_onehot0_neg, *get_random()], [*all_onehot0_neg, *all_shamt, *get_random()]),
 | |
| 	("sh3add" , [*all_onehot0_neg, *get_random()], [*all_onehot0_neg, *all_shamt, *get_random()]),
 | |
| 	("andn"   , [*all_onehot0_neg, *get_random()], [*all_onehot0_neg, *all_shamt, *get_random()]),
 | |
| 	("max"    , [*all_onehot0_neg, *get_random()], [*all_onehot0_neg, *all_shamt, *get_random()]),
 | |
| 	("maxu"   , [*all_onehot0_neg, *get_random()], [*all_onehot0_neg, *all_shamt, *get_random()]),
 | |
| 	("min"    , [*all_onehot0_neg, *get_random()], [*all_onehot0_neg, *all_shamt, *get_random()]),
 | |
| 	("minu"   , [*all_onehot0_neg, *get_random()], [*all_onehot0_neg, *all_shamt, *get_random()]),
 | |
| 	("orn"    , [*all_onehot0_neg, *get_random()], [*all_onehot0_neg, *all_shamt, *get_random()]),
 | |
| 	("rol"    , [*all_onehot0_neg, *get_random()], [*all_onehot0_neg, *all_shamt, *get_random()]),
 | |
| 	("ror"    , [*all_onehot0_neg, *get_random()], [*all_onehot0_neg, *all_shamt, *get_random()]),
 | |
| 	("xnor"   , [*all_onehot0_neg, *get_random()], [*all_onehot0_neg, *all_shamt, *get_random()]),
 | |
| 	("clmul"  , [*all_onehot0_neg, *get_random()], [*all_onehot0_neg, *all_shamt, *get_random()]),
 | |
| 	("clmulh" , [*all_onehot0_neg, *get_random()], [*all_onehot0_neg, *all_shamt, *get_random()]),
 | |
| 	("clmulr" , [*all_onehot0_neg, *get_random()], [*all_onehot0_neg, *all_shamt, *get_random()]),
 | |
| 	("bclr"   , [*all_onehot0_neg, *get_random()], [*all_onehot0_neg, *all_shamt, *get_random()]),
 | |
| 	("bext"   , [*all_onehot0_neg, *get_random()], [*all_onehot0_neg, *all_shamt, *get_random()]),
 | |
| 	("binv"   , [*all_onehot0_neg, *get_random()], [*all_onehot0_neg, *all_shamt, *get_random()]),
 | |
| 	("bset"   , [*all_onehot0_neg, *get_random()], [*all_onehot0_neg, *all_shamt, *get_random()]),
 | |
| ]
 | |
| 
 | |
| instr_reg_imm = [
 | |
| 	("rori"  , [*all_onehot0_neg, *get_random()], all_shamt),
 | |
| 	("bclri" , [*all_onehot0_neg, *get_random()], all_shamt),
 | |
| 	("bexti" , [*all_onehot0_neg, *get_random()], all_shamt),
 | |
| 	("binvi" , [*all_onehot0_neg, *get_random()], all_shamt),
 | |
| 	("bseti" , [*all_onehot0_neg, *get_random()], all_shamt),
 | |
| ]
 | |
| 
 | |
| # Generate input vector programs
 | |
| 
 | |
| prolog = """
 | |
| .option norelax
 | |
| 
 | |
| // Automatically-generated test vector. Don't edit.
 | |
| 
 | |
| #define IO_BASE 0x80000000
 | |
| #define IO_PRINT_CHAR (IO_BASE + 0x0)
 | |
| #define IO_PRINT_U32  (IO_BASE + 0x4)
 | |
| #define IO_EXIT       (IO_BASE + 0x8)
 | |
| 
 | |
| .section .text
 | |
| 
 | |
| .global _start
 | |
| _start:
 | |
| 	la sp, test_signature_start
 | |
| 
 | |
| """
 | |
| 
 | |
| interlude = """
 | |
| 
 | |
| // Hazard3 sim returns exit status if you do this:
 | |
| 
 | |
| test_end:
 | |
| 	li a0, IO_EXIT
 | |
| 	sw zero, (a0)
 | |
| 
 | |
| // Catch other simulators with or without debug support:
 | |
| 
 | |
| 	la a0, it_dead
 | |
| 	csrw mtvec, a0
 | |
| .p2align 2
 | |
| it_dead:
 | |
| 	ebreak
 | |
| 	j it_dead
 | |
| 
 | |
| .section .testdata, "wa"
 | |
| 
 | |
| .p2align 2
 | |
| .global test_signature_start
 | |
| test_signature_start:
 | |
| """
 | |
| 
 | |
| epilog = """
 | |
| .global test_signature_end
 | |
| test_signature_end:
 | |
| """
 | |
| 
 | |
| def sanitise(name):
 | |
| 	return "".join(c if ("_" + c).isidentifier() else "_" for c in name)
 | |
| 
 | |
| Path("test").mkdir(exist_ok = True)
 | |
| Path("refgen").mkdir(exist_ok = True)
 | |
| 
 | |
| for instr, data in instr_one_operand:
 | |
| 	with open(f"test/{sanitise(instr)}.S", "w") as f:
 | |
| 		f.write(prolog)
 | |
| 		i = 0
 | |
| 		for d in data:
 | |
| 			f.write(f"test{i}:\n")
 | |
| 			f.write(f"\tli a1, 0x{d:08x}\n")
 | |
| 			f.write(f"\t{instr} a0, a1\n")
 | |
| 			f.write( "\tsw a0, (sp)\n")
 | |
| 			f.write(f"\taddi sp, sp, {XLEN // 8}\n\n")
 | |
| 			i = i + 1
 | |
| 		f.write(interlude)
 | |
| 		# Label the output locations for easier debug
 | |
| 		i = 0
 | |
| 		for d in data:
 | |
| 			f.write(f"test{i}_{sanitise(instr)}_{d:08x}:\n")
 | |
| 			f.write("\t.word 0\n" if XLEN == 32 else "\tdword 0\n")
 | |
| 			i = i + 1
 | |
| 		f.write(epilog)
 | |
| 
 | |
| for instr, rs1_list, rs2_list in instr_reg_reg:
 | |
| 	with open(f"test/{sanitise(instr)}.S", "w") as f:
 | |
| 		f.write(prolog)
 | |
| 		i = 0
 | |
| 		for rs1 in rs1_list:
 | |
| 			for rs2 in rs2_list:
 | |
| 				f.write(f"test{i}:\n")
 | |
| 				f.write(f"\tli a1, 0x{rs1:08x}\n")
 | |
| 				f.write(f"\tli a2, 0x{rs2:08x}\n")
 | |
| 				f.write(f"\t{instr} a0, a1, a2\n")
 | |
| 				f.write( "\tsw a0, (sp)\n")
 | |
| 				f.write(f"\taddi sp, sp, {XLEN // 8}\n\n")
 | |
| 				i = i + 1
 | |
| 		f.write(interlude)
 | |
| 		i = 0
 | |
| 		for rs1 in rs1_list:
 | |
| 			for rs2 in rs2_list:
 | |
| 				f.write(f"test{i}_{sanitise(instr)}_{rs1:08x}__{rs2:08x}:\n")
 | |
| 				f.write("\t.word 0\n" if XLEN == 32 else "\tdword 0\n")
 | |
| 				i = i + 1
 | |
| 		f.write(epilog)
 | |
| 
 | |
| for instr, rs1_list, imm_list in instr_reg_imm:
 | |
| 	with open(f"test/{sanitise(instr)}.S", "w") as f:
 | |
| 		f.write(prolog)
 | |
| 		i = 0
 | |
| 		for rs1 in rs1_list:
 | |
| 			for imm in imm_list:
 | |
| 				f.write(f"test{i}:\n")
 | |
| 				f.write(f"\tli a1, 0x{rs1:08x}\n")
 | |
| 				f.write(f"\t{instr} a0, a1, 0x{imm:02x}\n")
 | |
| 				f.write( "\tsw a0, (sp)\n")
 | |
| 				f.write(f"\taddi sp, sp, {XLEN // 8}\n\n")
 | |
| 				i = i + 1
 | |
| 		f.write(interlude)
 | |
| 		i = 0
 | |
| 		for rs1 in rs1_list:
 | |
| 			for imm in imm_list:
 | |
| 				f.write(f"test{i}_{sanitise(instr)}_{rs1:08x}__{imm:02x}:\n")
 | |
| 				f.write("\t.word 0\n" if XLEN == 32 else "\tdword 0\n")
 | |
| 				i = i + 1
 | |
| 		f.write(epilog)
 | |
| 
 | |
| # Generate reference vector programs for running on spike + pk
 | |
| 
 | |
| # (I spent a while fighting spike to just be a processor + physical memory +
 | |
| # some MMIO, so I could run the same binaries, but this is easier)
 | |
| 
 | |
| c_prolog = """
 | |
| #include <stdio.h>
 | |
| 
 | |
| // Automatically-generated test vector. Don't edit.
 | |
| 
 | |
| int main() {
 | |
| 	unsigned int rd, rs1, rs2;
 | |
| """
 | |
| c_output_result = '\tprintf("%08x\\n", rd);\n\n'
 | |
| 
 | |
| c_epilog = """
 | |
| 	return 0;
 | |
| }
 | |
| """
 | |
| 
 | |
| for instr, data in instr_one_operand:
 | |
| 	with open(f"refgen/{sanitise(instr)}.c", "w") as f:
 | |
| 		f.write(c_prolog)
 | |
| 		for d in data:
 | |
| 			f.write(f"\trs1 = 0x{d:08x};\n");
 | |
| 			f.write(f'\tasm("{instr} %0, %1" : "=r" (rd) : "r" (rs1));\n')
 | |
| 			f.write(c_output_result)
 | |
| 		f.write(c_epilog)
 | |
| 
 | |
| for instr, rs1_list, rs2_list in instr_reg_reg:
 | |
| 	with open(f"refgen/{sanitise(instr)}.c", "w") as f:
 | |
| 		f.write(c_prolog)
 | |
| 		for rs1 in rs1_list:
 | |
| 			for rs2 in rs2_list:
 | |
| 				f.write(f"\trs1 = 0x{rs1:08x};\n");
 | |
| 				f.write(f"\trs2 = 0x{rs2:08x};\n");
 | |
| 				f.write(f'\tasm("{instr} %0, %1, %2" : "=r" (rd) : "r" (rs1), "r" (rs2));\n')
 | |
| 				f.write(c_output_result)
 | |
| 		f.write(c_epilog)
 | |
| 
 | |
| for instr, rs1_list, imm_list in instr_reg_imm:
 | |
| 	with open(f"refgen/{sanitise(instr)}.c", "w") as f:
 | |
| 		f.write(c_prolog)
 | |
| 		for rs1 in rs1_list:
 | |
| 			for imm in imm_list:
 | |
| 				f.write(f"\trs1 = 0x{rs1:08x};\n");
 | |
| 				f.write(f'\tasm("{instr} %0, %1, %2" : "=r" (rd) : "r" (rs1), "i" ({imm}));\n')
 | |
| 				f.write(c_output_result)
 | |
| 		f.write(c_epilog)
 |