From 8d7a515ae7bb4d71c767ba2171c3c69bf4a37135 Mon Sep 17 00:00:00 2001 From: Thomas Fleming Date: Thu, 4 Feb 2021 15:38:22 -0500 Subject: [PATCH 01/12] Complete STORE tests --- .../testbench/testbench-imperas.sv | 6 +- wally-pipelined/testgen/testgen-STORE.py | 282 ++++++++++++++++++ 2 files changed, 286 insertions(+), 2 deletions(-) create mode 100755 wally-pipelined/testgen/testgen-STORE.py diff --git a/wally-pipelined/testbench/testbench-imperas.sv b/wally-pipelined/testbench/testbench-imperas.sv index 67a896445..99078cb3b 100644 --- a/wally-pipelined/testbench/testbench-imperas.sv +++ b/wally-pipelined/testbench/testbench-imperas.sv @@ -136,7 +136,8 @@ string tests64iNOc[] = { "rv64i/I-XOR-01", "3000", "rv64i/I-XORI-01", "3000", "rv64i/WALLY-ADD", "4000", - "rv64i/WALLY-SUB", "4000" + "rv64i/WALLY-SUB", "4000", + "rv64i/WALLY-STORE", "3000" }; string tests32ic[] = '{ // "rv32ic/WALLY-C-ADHOC-01", "2000", @@ -217,7 +218,8 @@ string tests32i[] = { "rv32i/I-XOR-01","2000", "rv32i/I-XORI-01","2000", "rv32i/WALLY-ADD", "3000", - "rv32i/WALLY-SUB", "3000" + "rv32i/WALLY-SUB", "3000", + "rv32i/WALLY-STORE", "2000" }; string tests[]; diff --git a/wally-pipelined/testgen/testgen-STORE.py b/wally-pipelined/testgen/testgen-STORE.py new file mode 100755 index 000000000..d4a995d48 --- /dev/null +++ b/wally-pipelined/testgen/testgen-STORE.py @@ -0,0 +1,282 @@ +#!/usr/bin/python3 +################################## +# testgen-STORE.py +# +# Jessica Torrey 03 February 2021 +# Thomas Fleming 03 February 2021 +# +# Generate directed and random test vectors for RISC-V Design Validation. +################################## + +################################## +# libraries +################################## +from datetime import datetime +from random import randint, seed, getrandbits +from textwrap import dedent + +################################## +# global structures +################################## +size_to_store = {8: "sd", 4: "sw", 2: "sh", 1: "sb"} +size_to_load = {8: "ld", 4: "lw", 2: "lh", 1: "lb"} +store_to_size = {"sd": 8, "sw": 4, "sh": 2, "sb": 1} +testcase_num = 0 +signature_len = 2000 +signature = [0xff for _ in range(signature_len)] + +################################## +# functions +################################## + +def rand_reg(): + """ + Produce a random register from 1 to 31 (skipping 6, since r6 is used for + other things). + """ + r = randint(1,30) + if r >= 6: + r += 1 + return r + +def rand_regs(): + """ + Generate two random, unequal register numbers (skipping x6). + """ + rs1 = rand_reg() + rs2 = rand_reg() + while rs1 == rs2: + rs2 = rand_reg() + + return rs1, rs2 + +def generate_case(xlen, instruction, value_register, value, addr_register, offset, base_delta): + """ + Create assembly code for a given STORE test case, returned as a string. + + Generates an `xlen`-bit test case for `instruction` (one of sb, sh, sw, or + sd) that loads `value` into `value_register`, then attempts to store that + value in memory at address (x6 + `base_delta`). The test case + assumes the current base address for the signature is in register x6. + + The form of the STORE instruction is as follows: + + `instruction` `value_register` `offset`(`addr_register`) + """ + global testcase_num + + hex_value = f"{value:0{xlen // 4}x}" + + data = f"""# Testcase {testcase_num}: source: x{value_register} (value 0x{hex_value}), destination: {offset}(x{addr_register}) ({base_delta} bytes into signature) + addi x{addr_register}, x6, {base_delta} + li x{value_register}, MASK_XLEN({-offset}) + add x{addr_register}, x{addr_register}, x{value_register} + li x{value_register}, 0x{hex_value} + {instruction} x{value_register}, {offset}(x{addr_register}) + """ + + update_signature(store_to_size[instruction], value, base_delta) + + testcase_num += 1 + return data + +def validate_memory(scratch_register, value_register, value, base_delta, length): + """ + Create assembly code to verify that `length` bytes at mem[x6 + `base_delta`] + store `value`. + + Assumes x6 stores the current base address for the signature. + """ + truncated_value = value & (2**(length * 8) - 1) + hex_value = f"{truncated_value:0{length * 2}x}" + + load = size_to_load[length] + data = f"""addi x{scratch_register}, x6, {base_delta} + {load} x{value_register}, 0(x{scratch_register}) + RVTEST_IO_ASSERT_GPR_EQ(x{scratch_register}, x{value_register}, 0x{hex_value}) + + """ + return data + +def update_signature(length, value, base_delta): + """ + Write the lower `length` bytes of `value` to the little-endian signature + array, starting at byte `base_delta`. + """ + truncated_value = value & (2**(length * 8) - 1) + value_bytes = truncated_value.to_bytes(length, 'little') + #print("n: {}, value: {:x}, trunc: {:x}, length: {}, bd: {:x}".format(testcase_num, value, truncated_value, length, base_delta)) + for i in range(length): + signature[base_delta + i] = value_bytes[i] + +def write_signature(outfile): + """ + Writes successive 32-bit words from the signature array into a given file. + """ + for i in range(0, signature_len, 4): + word = list(reversed(signature[i:i+4])) + hexword = bytearray(word).hex() + outfile.write(f"{hexword}\n") + +def write_header(outfile): + """ + Write the name of the test file, authors, and creation date. + """ + outfile.write(dedent(f"""\ + /////////////////////////////////////////// + // + // WALLY-STORE + // + // Author: {author} + // + // Created {str(datetime.now())} + """)) + outfile.write(open("testgen_header.S", "r").read()) + +def write_footer(outfile): + """ + Write necessary closing code, including a data section for the signature. + """ + outfile.write(open("testgen_footer.S", 'r').read()) + data_section = dedent(f"""\ + \t.fill {signature_len}, 1, -1 + RV_COMPLIANCE_DATA_END + """) + outfile.write(data_section) + +################################## +# test cases +################################## + +def write_basic_tests(outfile, xlen, instr_len, num, base_delta): + """ + Test basic functionality of STORE, using random registers, offsets, and + values. + + Creates `num` tests for a single store instruction of length `instr_len`, + writing to memory at consecutive locations, starting `base_delta` bytes from + the start of the signature. + + Returns the number of bytes from the start of the signature where testing + ended. + """ + instruction = size_to_store[instr_len] + for i in range(num): + value_register, addr_register = rand_regs() + offset = randint(-2**(12 - 1), 2**(12 - 1) - 1) + value = randint(0, 2**(instr_len * 8) - 1) + test = generate_case(xlen, instruction, value_register, value, addr_register, offset, base_delta) + validate = validate_memory(addr_register, value_register, value, base_delta, instr_len) + outfile.write(test) + outfile.write(validate) + base_delta += instr_len + return base_delta + +def write_random_store_tests(outfile, xlen, instr_len, num, min_base_delta): + """ + Test random access of STORE, using random registers, offsets, values, and + memory locations. + + Creates `num` tests for a single store instruction of length `instr_len`, + writing to memory at random locations between `min_base_delta` bytes past + the start of the signature to the end of the signature. + """ + instruction = size_to_store[instr_len] + for i in range(num): + base_delta = randint(min_base_delta, signature_len - 1) + base_delta -= (base_delta % instr_len) + value_register, addr_register = rand_regs() + offset = randint(-2**(12 - 1), 2**(12 - 1) - 1) + value = randint(0, 2**(instr_len * 8) - 1) + + test = generate_case(xlen, instruction, value_register, value, addr_register, offset, base_delta) + validate = validate_memory(addr_register, value_register, value, base_delta, instr_len) + outfile.write(test) + outfile.write(validate) + +def write_repeated_store_tests(outfile, xlen, instr_len, num, base_delta): + """ + Test repeated access of STORE, using random registers, offsets, values, and a + single memory location. + + Creates `num` tests for a single store instruction of length `instr_len`, + writing to memory `base_delta` bytes past the start of the signature. + """ + instruction = size_to_store[instr_len] + for i in range(num): + value_register, addr_register = rand_regs() + offset = 0 + value = (1 << ((2 * i) % xlen)) + + test = generate_case(xlen, instruction, value_register, value, addr_register, offset, base_delta) + validate = validate_memory(addr_register, value_register, value, base_delta, instr_len) + + outfile.write(test) + outfile.write(validate) + +def write_corner_case_tests(outfile, xlen, instr_len, base_delta): + instruction = size_to_store[instr_len] + + corner_cases_32 = [0x0, 0x10000001, 0x01111111] + corner_cases_64 = [0x1000000000000001, 0x0111111111111111] + corner_cases = corner_cases_32 + if xlen == 64: + corner_cases += corner_cases_64 + + for offset in corner_cases: + for value in corner_cases: + value_register, addr_register = rand_regs() + test = generate_case(xlen, instruction, value_register, value, addr_register, offset, base_delta) + validate = validate_memory(addr_register, value_register, value, base_delta, instr_len) + + outfile.write(test) + outfile.write(validate) + + base_delta += instr_len + + return base_delta + +################################## +# main body +################################## + +instructions = ["sd", "sw", "sh", "sb"] +author = "Jessica Torrey & Thomas Fleming " +xlens = [32, 64] +numrand = 100 + +# setup +seed(0) # make tests reproducible + +for xlen in xlens: + if (xlen == 32): + wordsize = 4 + else: + wordsize = 8 + + fname = f"../../imperas-riscv-tests/riscv-test-suite/rv{xlen}i/src/WALLY-STORE.S" + refname = f"../../imperas-riscv-tests/riscv-test-suite/rv{xlen}i/references/WALLY-STORE.reference_output" + f = open(fname, "w") + r = open(refname, "w") + + write_header(f) + + base_delta = 0 + + for instruction in instructions: + if xlen == 32 and instruction == 'sd': + continue + instr_len = store_to_size[instruction] + base_delta = write_basic_tests(f, xlen, instr_len, 5, base_delta) + write_repeated_store_tests(f, xlen, instr_len, 32, base_delta) + write_random_store_tests(f, xlen, instr_len, 5, base_delta + wordsize) + + write_footer(f) + + write_signature(r) + f.close() + r.close() + + # Reset testcase_num and signature + testcase_num = 0 + signature = [0xff for _ in range(signature_len)] From 15c0b4af22e596e231805daa1b2ebe63c131e7f5 Mon Sep 17 00:00:00 2001 From: bbracker Date: Fri, 5 Feb 2021 08:08:42 -0500 Subject: [PATCH 06/12] JAL testing --- .../testbench/testbench-imperas.sv | 4 +- wally-pipelined/testgen/testgen-JAL.py | 260 ++++++++++++++++++ 2 files changed, 263 insertions(+), 1 deletion(-) create mode 100755 wally-pipelined/testgen/testgen-JAL.py diff --git a/wally-pipelined/testbench/testbench-imperas.sv b/wally-pipelined/testbench/testbench-imperas.sv index 99078cb3b..1ea532c22 100644 --- a/wally-pipelined/testbench/testbench-imperas.sv +++ b/wally-pipelined/testbench/testbench-imperas.sv @@ -137,6 +137,7 @@ string tests64iNOc[] = { "rv64i/I-XORI-01", "3000", "rv64i/WALLY-ADD", "4000", "rv64i/WALLY-SUB", "4000", + "rv64i/WALLY-JAL", "4000", "rv64i/WALLY-STORE", "3000" }; string tests32ic[] = '{ @@ -219,7 +220,8 @@ string tests32i[] = { "rv32i/I-XORI-01","2000", "rv32i/WALLY-ADD", "3000", "rv32i/WALLY-SUB", "3000", - "rv32i/WALLY-STORE", "2000" + "rv32i/WALLY-STORE", "2000", + "rv32i/WALLY-JAL", "3000" }; string tests[]; diff --git a/wally-pipelined/testgen/testgen-JAL.py b/wally-pipelined/testgen/testgen-JAL.py new file mode 100755 index 000000000..09f58a566 --- /dev/null +++ b/wally-pipelined/testgen/testgen-JAL.py @@ -0,0 +1,260 @@ +#!/usr/bin/python3 +################################## +# testgen-JAL.py +# +# Ben Bracker (bbracker@hmc.edu) 19 January 2021 +# Based on testgen-ADD-SUB.py by David Harris +# +# Generate directed and random test vectors for RISC-V Design Validation. +################################## + +################################## +# libraries +################################## +from datetime import datetime +from random import randint +from random import choice +from random import seed +from random import getrandbits +from copy import deepcopy + +################################## +# functions +################################## + +def InitTestGroup(): + global TestGroup,TestGroupSizes,AllRegs,UnusedRegs,StoreAdrReg + TestGroup += 1 + TestGroupSizes.append(0) + UnusedRegs = deepcopy(AllRegs) + oldStoreAdrReg = StoreAdrReg + while ((StoreAdrReg == oldStoreAdrReg) or (StoreAdrReg == 0) or (StoreAdrReg == 31)): + StoreAdrReg = choice(UnusedRegs) + UnusedRegs.remove(StoreAdrReg) + f.write("\n # ---------------------------------------------------------------------------------------------\n") + f.write(" # new register for address of test results\n") + addInst(" la x"+str(StoreAdrReg)+", test_1_res\n") + f.write(" # ---------------------------------------------------------------------------------------------\n") + +def registerSelect(): + # ensures that rd experiences all possible registers + # *** does not yet ensure that rs experiences all possible registers + # ensures that at least once rd = rs + global UnusedRegs + if len(UnusedRegs)==0: + InitTestGroup() + rd = choice(UnusedRegs) + rs = choice(UnusedRegs) + UnusedRegs.remove(rd) + OtherRegs = deepcopy(AllRegs) + OtherRegs.remove(StoreAdrReg) + OtherRegs.remove(rd) + try: + OtherRegs.remove(0) + except: + pass + try: + OtherRegs.remove(rs) + except: + pass + DataReg = choice(OtherRegs) + OtherRegs.remove(DataReg) + OtherRd = choice(OtherRegs) + return (rd,rs,DataReg,OtherRd) + +def addInst(line): + global CurrAdr + f.write(line) + if ("li x" in line): + CurrAdr += 8 if (xlen == 32) else 20 + elif ("la x" in line): + CurrAdr += 8 + else: + CurrAdr += 4 + +def writeForwardsJumpVector(spacers): + global TestNum + rd, rs, DataReg, OtherRd = registerSelect() + if (xlen==64): + expected = int("fedbca9876540000",16) + unexpected = int("ffff0000ffff0000",16) + else: + expected = int("fedbca98",16) + unexpected = int("ff00ff00",16) + + + f.write("\n") + f.write(" # Testcase "+str(TestNum)+" address cmp result rd:x"+str(rd)+"("+formatstr.format(CurrAdr+44)+") data result rd:x"+str(DataReg)+"("+formatstr.format(expected)+")\n") + addInst(" li x"+str(DataReg)+", "+formatstr.format(expected)+"\n") + addInst(" JAL x"+str(rd)+", 1f\n") + LinkAdr = CurrAdr if (rd!=0) else 0 # rd's expected value + for i in range(spacers): + addInst(" li x"+str(DataReg)+", "+formatstr.format(unexpected)+"\n") + f.write("1:\n") + addInst(" "+storecmd+" x"+str(rd)+", "+str(wordsize*(2*TestNum+0))+"(x"+str(StoreAdrReg)+")\n") + f.write(" RVTEST_IO_ASSERT_GPR_EQ(x"+str(StoreAdrReg+1)+", x"+str(rd)+", "+formatstr.format(LinkAdr)+")\n") + addInst(" "+storecmd+" x"+str(DataReg)+", "+str(wordsize*(2*TestNum+1))+"(x"+str(StoreAdrReg)+")\n") + f.write(" RVTEST_IO_ASSERT_GPR_EQ(x"+str(StoreAdrReg+1)+", x"+str(DataReg)+", "+formatstr.format(expected)+")\n") + writeExpectedToRef(LinkAdr) + writeExpectedToRef(expected) + TestNum = TestNum+1 + +def writeBackwardsJumpVector(spacers): + global TestNum + rd, rs, DataReg,OtherRd = registerSelect() + if (xlen==64): + expected = int("fedbca9876540000",16) + unexpected = int("ffff0000ffff0000",16) + else: + expected = int("fedbca98",16) + unexpected = int("ff00ff00",16) + + f.write("\n") + f.write(" # Testcase "+str(TestNum)+" address cmp result rd:x"+str(rd)+"("+formatstr.format(CurrAdr+20+8*spacers)+") data result rd:x"+str(DataReg)+"("+formatstr.format(expected)+")\n") + addInst(" JAL x"+str(OtherRd)+", 2f\n") + f.write("1:\n") + addInst(" li x"+str(DataReg)+", "+formatstr.format(expected)+"\n") + addInst(" JAL x"+str(OtherRd)+", 3f\n") + f.write("2:\n") + for i in range(spacers): + addInst(" li x"+str(DataReg)+", "+formatstr.format(unexpected)+"\n") + addInst(" JAL x"+str(rd)+", 1b\n") + LinkAdr = CurrAdr if (rd!=0) else 0 # rd's expected value + f.write("3:\n") + addInst(" "+storecmd+" x"+str(rd)+", "+str(wordsize*(2*TestNum+0))+"(x"+str(StoreAdrReg)+")\n") + f.write(" RVTEST_IO_ASSERT_GPR_EQ(x"+str(StoreAdrReg+1)+", x"+str(rd)+", "+formatstr.format(LinkAdr)+")\n") + addInst(" "+storecmd+" x"+str(DataReg)+", "+str(wordsize*(2*TestNum+1))+"(x"+str(StoreAdrReg)+")\n") + f.write(" RVTEST_IO_ASSERT_GPR_EQ(x"+str(StoreAdrReg+1)+", x"+str(DataReg)+", "+formatstr.format(expected)+")\n") + writeExpectedToRef(LinkAdr) + writeExpectedToRef(expected) + TestNum = TestNum+1 + +def writeChainVector(repetitions,spacers): + global TestNum + rd, rs, DataReg,OtherRd = registerSelect() + if (xlen==64): + expected = int("fedbca9876540000",16) + unexpected = int("ffff0000ffff0000",16) + else: + expected = int("fedbca98",16) + unexpected = int("ff00ff00",16) + + f.write("\n") + f.write(" # Testcase "+str(TestNum)+" address cmp result rd:x"+str(rd)+"(ugh; if you really wanted to, you could figure it out) data result rd:x"+str(DataReg)+"("+formatstr.format(expected)+")\n") + addInst(" li x"+str(DataReg)+", "+formatstr.format(expected)+"\n") + for i in range(repetitions): + addInst(" JAL x"+str(OtherRd)+", "+str(3*i+2)+"f\n") + if spacers: + for j in range(i): + addInst(" li x"+str(DataReg)+", "+formatstr.format(unexpected)+"\n") + f.write(str(3*i+1)+":\n") + addInst(" JAL x"+str(OtherRd)+", "+str(3*i+3)+"f\n") + if spacers: + for j in range(i): + addInst(" li x"+str(DataReg)+", "+formatstr.format(unexpected)+"\n") + f.write(str(3*i+2)+":\n") + addInst(" JAL x"+str(rd)+", "+str(3*i+1)+"b\n") + LinkAdr = CurrAdr if (rd!=0) else 0 # rd's expected value + if spacers: + for j in range(i): + addInst(" li x"+str(DataReg)+", "+formatstr.format(unexpected)+"\n") + f.write(str(3*i+3)+":\n") + addInst(" "+storecmd+" x"+str(rd)+", "+str(wordsize*(2*TestNum+0))+"(x"+str(StoreAdrReg)+")\n") + f.write(" RVTEST_IO_ASSERT_GPR_EQ(x"+str(StoreAdrReg+1)+", x"+str(rd)+", "+formatstr.format(LinkAdr)+")\n") + addInst(" "+storecmd+" x"+str(DataReg)+", "+str(wordsize*(2*TestNum+1))+"(x"+str(StoreAdrReg)+")\n") + f.write(" RVTEST_IO_ASSERT_GPR_EQ(x"+str(StoreAdrReg+1)+", x"+str(DataReg)+", "+formatstr.format(expected)+")\n") + writeExpectedToRef(LinkAdr) + writeExpectedToRef(expected) + TestNum = TestNum+1 + + +def writeExpectedToRef(expected): + global TestGroupSizes + TestGroupSizes[TestGroup-1] += 1 + if (xlen == 32): + r.write(formatrefstr.format(expected)+"\n") + else: + r.write(formatrefstr.format(expected % 2**32)+"\n" + formatrefstr.format(expected >> 32)+"\n") + +################################## +# main body +################################## + +# change these to suite your tests +tests = ["JAL"] +author = "Ben Bracker (bbracker@hmc.edu)" +xlens = [32,64] +numtests = 100; + +# setup +seed(0) # make tests reproducible + +# generate files for each test +for xlen in xlens: + CurrAdr = int("80000108",16) + TestNum = 0 + TestGroup = 1 + TestGroupSizes = [0] + AllRegs = list(range(0,32)) + UnusedRegs = deepcopy(AllRegs) + StoreAdrReg = 6 # matches what's in header script + UnusedRegs.remove(6) + + formatstrlen = str(int(xlen/4)) + formatstr = "0x{:0" + formatstrlen + "x}" # format as xlen-bit hexadecimal number + formatrefstr = "{:08x}" # format as xlen-bit hexadecimal number with no leading 0x + if (xlen == 32): + storecmd = "sw" + wordsize = 4 + else: + storecmd = "sd" + wordsize = 8 + for test in tests: + imperaspath = "../../imperas-riscv-tests/riscv-test-suite/rv" + str(xlen) + "i/" + basename = "WALLY-" + test + fname = imperaspath + "src/" + basename + ".S" + refname = imperaspath + "references/" + basename + ".reference_output" + + # print custom header part + f = open(fname, "w") + r = open(refname, "w") + f.write("///////////////////////////////////////////\n") + f.write("// "+fname+ "\n") + f.write("//\n") + f.write("// This file can be used to test the RISC-V JAL instruction.\n") + f.write("// But be warned that altering the test environment may break this test!\n") + f.write("// In order to work, this test expects that the first instruction (la)\n") + f.write("// be allocated at 0x80000100.\n") + f.write("//\n") + f.write("// " + author + "\n") + f.write("// Created "+str(datetime.now())+"\n") + + # insert generic header + h = open("testgen_header.S", "r") + for line in h: + f.write(line) + + # print directed test vectors + for i in range(0,31): + writeForwardsJumpVector(randint(0,4)) + for i in range(0,31): + writeBackwardsJumpVector(randint(0,4)) + writeForwardsJumpVector(100) + writeBackwardsJumpVector(100) + writeChainVector(6,True) + writeChainVector(16,False) + + # print footer + h = open("testgen_footer.S", "r") + for line in h: + f.write(line) + + # Finish + f.write(".fill "+str(sum(TestGroupSizes))+", "+str(wordsize)+", -1\n") + f.write("\nRV_COMPLIANCE_DATA_END\n") + f.close() + r.close() + + + + From a3f2f4c7bc2d43242e32cc493642b126163d9bc2 Mon Sep 17 00:00:00 2001 From: Jarred Allen Date: Sat, 6 Feb 2021 13:05:59 -0500 Subject: [PATCH 08/12] Add test vector set for load instructions --- wally-pipelined/testgen/testgen-LOAD.py | 177 +++++++++++++++++++++--- 1 file changed, 158 insertions(+), 19 deletions(-) diff --git a/wally-pipelined/testgen/testgen-LOAD.py b/wally-pipelined/testgen/testgen-LOAD.py index ed929c1e8..4a80fbfc7 100755 --- a/wally-pipelined/testgen/testgen-LOAD.py +++ b/wally-pipelined/testgen/testgen-LOAD.py @@ -18,15 +18,56 @@ from random import randint, seed, getrandbits ################################## def rand_reg(): - """Produce a random register (skipping 6, since r6 is used for other things""" - r = randint(1,30) + """Produce a random register (skipping 6 and 31, since they're used for other things)""" + r = randint(1,29) if r >= 6: r += 1 return r +def rand_value(width): + """Generate a random value which fits in the given width""" + return randint(0, (1 << width) - 1) + +def rand_offset(): + """Generate a random offset""" + ret = rand_value(12) + # print("Random offset: %d" % ret) + return ret + +def rand_source(): + """Generate a random value for the source register, such that the load address is in the test data""" + ret = randint(1 << 12, (1 << 12) + (1 << 10)) + # print("Random source: %d" % ret) + return ret + +def add_offset_to_source(source, offset): + """Find the address from the given source value and offset""" + if offset & 0x800: + offset -= 0x1000 + return source + offset + +def insert_into_data(test_data, source, offset, value, width, xlen): + """Insert the given value into the given location of the test data""" + address = add_offset_to_source(source, offset) + # print("Test #%d" % testcase_num) + # print(f"Source: {source}, Offset: {offset}, Value: {value}, Width: {width}, xlen: {xlen}, Addr: {address}") + if address < 0: + return False + word_offset = address % (xlen // 8) + word_address = address - word_offset + if word_address in test_data: + return False + test_data[word_address] = value * (1 << (word_offset*8)) + ((~(((1 << width)-1) << (word_offset*8))) & rand_value(xlen)) + # print(f"Word: {hex(test_data[word_address])}") + return True + +def align(address, width): + """Align the address to the given width, in bits""" + return address - (address % (width // 8)) + testcase_num = 0 def generate_case(xlen, instruction, load_register, source_register, source_register_value, offset, expected): - """Produce the specified test case and return it as a string""" + """Produce the specified test case and return it as a pair of strings, where the first is the test case and the second is the output""" global testcase_num if xlen == 64: store = "sd" @@ -36,35 +77,89 @@ def generate_case(xlen, instruction, load_register, source_register, source_regi store = "sw" else: raise Exception("Unknown xlen value: %s" % xlen) - data = f"""# Testcase {testcase_num}: source {offset}(x{source_register} == {source_register_value}), rresult: x{load_register} == {expected} + if offset >= 0x800: + offset -= 0x1000 + if widths[instruction] != xlen: + expected = expected % (1 << widths[instruction]) + if 'u' not in instruction: + if expected & (1 << (widths[instruction] - 1)): + expected = (expected + ~((1 << widths[instruction]) - 1)) & ((1 << xlen) - 1) + data = f"""# Testcase {testcase_num}: source {offset}(x{source_register} == {source_register_value}), result: x{load_register} == {expected} + la x31, test_data lui x{source_register}, {source_register_value // (1 << 12)} addi x{source_register}, x{source_register}, {source_register_value % (1 << 12)} + add x{source_register}, x{source_register}, x31 {instruction} x{load_register}, {offset}(x{source_register}) - {store} x{load_register}, {testcase_num}(x6) + {store} x{load_register}, {(testcase_num*xlen//8) % 0x800}(x6) RVTEST_IO_ASSERT_GPR_EQ(x8, x{load_register}, {expected}) + """ testcase_num += 1 - return data + if testcase_num*xlen//8 % 0x800 == 0: + data += "# Adjust x6 because we're storing too many things\naddi x6, x6, 1024\naddi x6, x6, 1024\n\n" + if xlen == 32: + reference_output = "{:08x}\n".format(expected) + elif xlen == 64: + reference_output = "{:08x}\n{:08x}\n".format(expected % (1 << 32), expected >> 32) + return (data, reference_output) def write_header(outfile): outfile.write(f"""/////////////////////////////////////////// - // - // WALLY-LOAD - // - // Author: f{author} - // - // Created {str(datetime.now())} - // - /////////////////////////////////////////// - - """) +// +// WALLY-LOAD +// +// Author: {author} +// +// Created {str(datetime.now())} +// +""") outfile.write(open("testgen_header.S", "r").read()) +def write_test_data(outfile, test_data, xlen): + # print("Begin writing test data:") + # print("{} entries, from address {} to {}".format(len(test_data), min(test_data.keys()), max(test_data.keys()))) + # print(test_data) + outfile.write(""" + .align 16 +test_data: + + """) + if xlen == 32: + data_word = ".word" + elif xlen == 64: + data_word = ".dword" + else: + raise Exception("Unknown xlen: %d" % xlen) + byte_width = xlen // 8 + for addr in [0] + sorted(test_data.keys()): + if addr in test_data: + word = f" {data_word} {hex(test_data[addr] % (1 << xlen))} # test_data+{hex(addr)}\n" + else: + word = "" + try: + fill_len = (min(k for k in test_data.keys() if k > addr) - addr) // byte_width - 1 + if word == "": + fill_len += 1 + fill = f" .fill {fill_len}, {byte_width}, 0x0\n" + except: + fill = "" + case = word+fill + outfile.write(case) + ################################## # main body ################################## -instructions = ["lb", "lbu", "lh", "lhu", "lw", "lwu", "ld"] +widths = { + "lb": 8, + "lbu": 8, + "lh": 16, + "lhu": 16, + "lw": 32, + "lwu": 32, + "ld": 64, +} +instructions = [i for i in widths] author = "Jarred Allen" xlens = [32, 64] numrand = 100; @@ -73,16 +168,60 @@ numrand = 100; seed(0) # make tests reproducible for xlen in xlens: + testcase_num = 0 fname = "../../imperas-riscv-tests/riscv-test-suite/rv{}i/src/WALLY-LOAD.S".format(xlen) - refname = "../../imperas-riscv-tests/riscv-test-suite/rv{}i/references/WALLY-LOAD.S.reference_output".format(xlen) + refname = "../../imperas-riscv-tests/riscv-test-suite/rv{}i/references/WALLY-LOAD.reference_output".format(xlen) f = open(fname, "w") r = open(refname, "w") write_header(f) test_data = dict() - corner_values = [0x00, 0xFF, 0xFFFF, 0xFFFFFFFF, 0x7F, 0x7FFF, 0x7FFFFFFF, 0x01] + corner_values = [0x00, 0xFFFFFFFF, 0x7F, 0x7FFF, 0x7FFFFFFF] if xlen == 64: corner_values += [0xFFFFFFFFFFFFFFFF, 0x7FFFFFFFFFFFFFF] + corner_offsets = [0x800, 0x000, 0x7FF] for instruction in instructions: + print("Running xlen: %d, instruction: %s" % (xlen, instruction)) if xlen == 32: if instruction in ["lwu", "ld"]: continue + for value in corner_values + [rand_value(widths[instruction]) for _ in range(3)]: + value = value % (1 << widths[instruction]) + source_reg = rand_source() + for offset in corner_offsets + [rand_offset() for _ in range(3)]: + offset = align(offset, widths[instruction]) + source_reg = align(source_reg, widths[instruction]) + if insert_into_data(test_data, source_reg, offset, value, widths[instruction], xlen): + data, output = generate_case(xlen, instruction, rand_reg(), rand_reg(), source_reg, offset, value) + f.write(data) + r.write(output) + while testcase_num % 4: + source = rand_source() + offset = rand_offset() + value = rand_value(widths[instruction]) + if insert_into_data(test_data, source, offset, value, widths['lb'], xlen): + data, output = generate_case(xlen, 'lb', rand_reg(), rand_reg(), source, offset, value) + f.write(data) + r.write(output) + f.write("""# --------------------------------------------------------------------------------------------- + + RVTEST_IO_WRITE_STR(x31, "Test End\\n") + + # --------------------------------------------------------------------------------------------- + + RV_COMPLIANCE_HALT + +RV_COMPLIANCE_CODE_END + + .data + # Input data section +""") + write_test_data(f, test_data, xlen) + f.write("""# Output data section. +RV_COMPLIANCE_DATA_BEGIN + +test_1_res: +""") + f.write(f".fill {testcase_num}, {xlen//8}, -1\n") + f.write("\nRV_COMPLIANCE_DATA_END\n") + f.close() + r.close() From 29b7a0cd25f735a9192e6cf9b38f630fcba4fc87 Mon Sep 17 00:00:00 2001 From: Jarred Allen Date: Sat, 6 Feb 2021 14:56:40 -0500 Subject: [PATCH 09/12] Actually run the WALLY-LOAD tests --- wally-pipelined/testbench/testbench-imperas.sv | 2 ++ 1 file changed, 2 insertions(+) diff --git a/wally-pipelined/testbench/testbench-imperas.sv b/wally-pipelined/testbench/testbench-imperas.sv index 1ea532c22..573d0a06c 100644 --- a/wally-pipelined/testbench/testbench-imperas.sv +++ b/wally-pipelined/testbench/testbench-imperas.sv @@ -137,6 +137,7 @@ string tests64iNOc[] = { "rv64i/I-XORI-01", "3000", "rv64i/WALLY-ADD", "4000", "rv64i/WALLY-SUB", "4000", + "rv64i/WALLY-LOAD", "11bf0", "rv64i/WALLY-JAL", "4000", "rv64i/WALLY-STORE", "3000" }; @@ -219,6 +220,7 @@ string tests32i[] = { "rv32i/I-XOR-01","2000", "rv32i/I-XORI-01","2000", "rv32i/WALLY-ADD", "3000", + "rv32i/WALLY-LOAD", "11c00", "rv32i/WALLY-SUB", "3000", "rv32i/WALLY-STORE", "2000", "rv32i/WALLY-JAL", "3000" From 805817cda4dcd54f3ae3126fe5d8a1f98070b927 Mon Sep 17 00:00:00 2001 From: Elizabeth Hedenberg Date: Sun, 7 Feb 2021 02:34:49 -0500 Subject: [PATCH 10/12] merge conflict? --- .../testbench/testbench-imperas.sv | 4 + .../testgen-ADDIW-SLLIW-SRLIW-SRAIW.py | 182 ++++++++++++++++++ 2 files changed, 186 insertions(+) create mode 100755 wally-pipelined/testgen/testgen-ADDIW-SLLIW-SRLIW-SRAIW.py diff --git a/wally-pipelined/testbench/testbench-imperas.sv b/wally-pipelined/testbench/testbench-imperas.sv index 573d0a06c..b7b769aef 100644 --- a/wally-pipelined/testbench/testbench-imperas.sv +++ b/wally-pipelined/testbench/testbench-imperas.sv @@ -140,6 +140,10 @@ string tests64iNOc[] = { "rv64i/WALLY-LOAD", "11bf0", "rv64i/WALLY-JAL", "4000", "rv64i/WALLY-STORE", "3000" + "rv64i/WALLY-ADDIW", "3000", + "rv64i/WALLY-SLLIW", "3000", + "rv64i/WALLY-SRLIW", "3000", + "rv64i/WALLY-SRAIW", "3000" }; string tests32ic[] = '{ // "rv32ic/WALLY-C-ADHOC-01", "2000", diff --git a/wally-pipelined/testgen/testgen-ADDIW-SLLIW-SRLIW-SRAIW.py b/wally-pipelined/testgen/testgen-ADDIW-SLLIW-SRLIW-SRAIW.py new file mode 100755 index 000000000..57a2bad77 --- /dev/null +++ b/wally-pipelined/testgen/testgen-ADDIW-SLLIW-SRLIW-SRAIW.py @@ -0,0 +1,182 @@ +#!/usr/bin/python3 +################################## +# testgen-ADDIW-SLLIW-SRLIW-SRAIW.py +# +# ehedenberg@hmc.edu 4 February 2021 +# heavily influenced by Prof Harris Code +# +# Generate directed and random test vectors for RISC-V Design Validation. +################################## + +################################## +# libraries +################################## +from datetime import datetime +from random import randint +from random import seed +from random import getrandbits +import sys + +################################## +# functions +################################## +def logical_rshift(signed_integer, places): + unsigned_integer=signed_integer%(1<<32) + return unsigned_integer >> places + +def toSigned12bit(n): + n=n & 0xfff + if (n&(1<<11)): + n=n|0xfffffffffffff000 + return n + +def toSigned32bit(n): + n=n & 0xffffffff + if (n&(1<<31)): + n=n|0xffffffff00000000 + return n + + +def computeExpected(a, b, test): + if (test == "ADDIW"): + b=toSigned12bit(b) + return a + b + elif (test == "SLLIW"): + return (a << b) + elif (test == "SRLIW"): + return logical_rshift(a, b) + elif(test == "SRAIW"): + a= toSigned32bit(a) + return a >> b + else: + die("bad test name ", test) + # exit(1) + +def randRegs(): + reg1 = randint(1,31) + reg2 = randint(1,31) + reg3 = randint(1,31) + if (reg1 == 6 or reg2 == 6 or reg3 == 6 or reg1 == reg2): + return randRegs() + else: + return reg1, reg2, reg3 + +def writeVector(a, b, storecmd): + global testnum + expected = computeExpected(a, b, test) + expected = expected % 2**64 # drop carry if necessary + if (expected < 0): # take twos complement + expected = 2**64 + expected + #expected=expected+2^32<<32 + if (expected >= (2**32)): + expected=expected%(2**32) + expected=toSigned32bit(expected) + reg1, reg2, reg3 = randRegs() + lines = "\n# Testcase " + str(testnum) + ": rs1:x" + str(reg1) + "(" + formatstr.format(a) + lines = lines + "), imm:"+formatstr.format(b) + lines = lines + ", result rd:x" + str(reg3) + "(" + formatstr.format(expected) +")\n" + lines = lines + "li x" + str(reg1) + ", MASK_XLEN(" + formatstr.format(a) + ")\n" + #lines = lines + "li x" + str(reg2) + ", MASK_XLEN(" + formatstr.format(b) + ")\n" + lines = lines + test + " x" + str(reg3) + ", x" + str(reg1) + ", SEXT_IMM(" + formatstr.format(b) + ")\n" + lines = lines + storecmd + " x" + str(reg3) + ", " + str(wordsize*testnum) + "(x6)\n" + lines = lines + "RVTEST_IO_ASSERT_GPR_EQ(x7, x" + str(reg3) +", "+formatstr.format(expected)+")\n" + #lines = lines + str(b)+"\n" + f.write(lines) + if (xlen == 32): + line = formatrefstr.format(expected)+"\n" + else: + line = formatrefstr.format(expected % 2**32)+"\n" + formatrefstr.format(expected >> 32) + "\n" + r.write(line) + testnum = testnum+1 + +################################## +# main body +################################## + +# change these to suite your tests +tests = ["ADDIW", "SLLIW", "SRLIW", "SRAIW"] +author = "Eizabeth Hedenberg" +xlens = [64] +shiftlen=5 +addlen=12 +numrand = 100 + +# setup +seed(0) # make tests reproducible + +# generate files for each test +for xlen in xlens: + formatstrlen = str(int(xlen/4)) + #formatstrlen6=str(int()) + formatstr = "0x{:0" + formatstrlen + "x}" # format as xlen-bit hexadecimal number + #formatstr6 = "0x{:0" + "2" + "x}" # format as xlen-bit hexadecimal number + formatrefstr = "{:08x}" # format as xlen-bit hexadecimal number with no leading 0x + if (xlen == 32): + storecmd = "sw" + wordsize = 4 + else: + storecmd = "sd" + wordsize = 8 + for test in tests: + cornersa = [0, 1, 2, 0xFF, 0x624B3E976C52DD14 % 2**xlen, 2**(xlen-1)-2, 2**(xlen-1)-1, + 2**(xlen-1), 2**(xlen-1)+1, 0xC365DDEB9173AB42 % 2**xlen, 2**(xlen)-2, 2**(xlen)-1] + #test both confined to top 32 and not + cornersshift=[0, 1, 2, 2**(shiftlen)-1, 2**(shiftlen)-2, 0b00101, 0b01110] + #6 bits: 0, 1, 2, largest, largest -1, largest -2, 21, 46 + cornersadd=[0, 1, 2, 2**(addlen-1), 2**(addlen-1)-1, 2**(addlen-1)-2, 2**(addlen-1)+1, 2**(addlen)-2, 2**(addlen)-1, 0b001010010101, 0b101011101111] + #12 bit, 0, 1, 2 argest positive, largest -1, largest -2, largest negative number, -2, -1, random + imperaspath = "../../imperas-riscv-tests/riscv-test-suite/rv" + str(xlen) + "i/" + basename = "WALLY-" + test + fname = imperaspath + "src/" + basename + ".S" + refname = imperaspath + "references/" + basename + ".reference_output" + testnum = 0 + + # print custom header part + f = open(fname, "w") + r = open(refname, "w") + line = "///////////////////////////////////////////\n" + f.write(line) + lines="// "+fname+ "\n// " + author + "\n" + f.write(lines) + line ="// Created " + str(datetime.now()) + f.write(line) + + # insert generic header + h = open("testgen_header.S", "r") + for line in h: + f.write(line) + + # print directed and random test vectors + if test=="ADDIW": + for a in cornersa: + for b in cornersadd: + writeVector(a, b, storecmd) + for i in range(0,numrand): + a = getrandbits(xlen) + b = getrandbits(12) + writeVector(a, b, storecmd) + else: + for a in cornersa: + for b in cornersshift: + writeVector(a, b, storecmd) + for i in range(0,numrand): + a = getrandbits(xlen) + b = getrandbits(5) + writeVector(a, b, storecmd) + + + # print footer + h = open("testgen_footer.S", "r") + for line in h: + f.write(line) + + # Finish + lines = ".fill " + str(testnum) + ", " + str(wordsize) + ", -1\n" + lines = lines + "\nRV_COMPLIANCE_DATA_END\n" + f.write(lines) + f.close() + r.close() + + + + From e334475ab57e7a2ff5fc92220ba86ac765f29f77 Mon Sep 17 00:00:00 2001 From: Jarred Allen Date: Sun, 7 Feb 2021 15:48:12 -0500 Subject: [PATCH 11/12] Fix compile error in imperas testbench --- wally-pipelined/testbench/testbench-imperas.sv | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/wally-pipelined/testbench/testbench-imperas.sv b/wally-pipelined/testbench/testbench-imperas.sv index b7b769aef..ec443b462 100644 --- a/wally-pipelined/testbench/testbench-imperas.sv +++ b/wally-pipelined/testbench/testbench-imperas.sv @@ -139,11 +139,11 @@ string tests64iNOc[] = { "rv64i/WALLY-SUB", "4000", "rv64i/WALLY-LOAD", "11bf0", "rv64i/WALLY-JAL", "4000", - "rv64i/WALLY-STORE", "3000" - "rv64i/WALLY-ADDIW", "3000", - "rv64i/WALLY-SLLIW", "3000", - "rv64i/WALLY-SRLIW", "3000", - "rv64i/WALLY-SRAIW", "3000" + "rv64i/WALLY-STORE", "3000", + "rv64i/WALLY-ADDIW", "3000", + "rv64i/WALLY-SLLIW", "3000", + "rv64i/WALLY-SRLIW", "3000", + "rv64i/WALLY-SRAIW", "3000" }; string tests32ic[] = '{ // "rv32ic/WALLY-C-ADHOC-01", "2000",