Add bitmanip test vector generation script
This commit is contained in:
		
							parent
							
								
									5d093487b7
								
							
						
					
					
						commit
						6f1a10724b
					
				| 
						 | 
					@ -0,0 +1 @@
 | 
				
			||||||
 | 
					test
 | 
				
			||||||
| 
						 | 
					@ -0,0 +1,9 @@
 | 
				
			||||||
 | 
					Random Bitmanip Test Vectors
 | 
				
			||||||
 | 
					============================
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					At time of writing there are no upstream compliance tests for the new bitmanip extensions.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					So, generate some constrained-random input vectors, and run them against the [reference simulator](https://github.com/riscv-software-src/riscv-isa-sim) to get golden output vectors. Not perfect, but enough to give me some confidence in my implementation until the official tests come out.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					People on the mailing lists seem to defer to spike over the pseudocode in the spec anyway, so that is probably a better verification target than trying to write my own vectors based on my own interpretation of the spec.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -0,0 +1,155 @@
 | 
				
			||||||
 | 
					#!/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 output files
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					prolog = """
 | 
				
			||||||
 | 
					.option norelax
 | 
				
			||||||
 | 
					.section .text
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// 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)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					.global _start
 | 
				
			||||||
 | 
					_start:
 | 
				
			||||||
 | 
						la sp, test_signature_start
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					"""
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					interlude = """
 | 
				
			||||||
 | 
					test_end:
 | 
				
			||||||
 | 
						li a0, IO_EXIT
 | 
				
			||||||
 | 
						sw zero, (a0)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					.section .data
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					.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)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					for instr, data in instr_one_operand:
 | 
				
			||||||
 | 
						with open(f"test/{sanitise(instr)}.S", "w") as f:
 | 
				
			||||||
 | 
							f.write(prolog)
 | 
				
			||||||
 | 
							for d in data:
 | 
				
			||||||
 | 
								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")
 | 
				
			||||||
 | 
							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)
 | 
				
			||||||
 | 
							for rs1 in rs1_list:
 | 
				
			||||||
 | 
								for rs2 in rs2_list:
 | 
				
			||||||
 | 
									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")
 | 
				
			||||||
 | 
							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)
 | 
				
			||||||
 | 
							for rs1 in rs1_list:
 | 
				
			||||||
 | 
								for imm in imm_list:
 | 
				
			||||||
 | 
									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")
 | 
				
			||||||
 | 
							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)
 | 
				
			||||||
		Loading…
	
		Reference in New Issue