From 997c9ad5c0bf8bd393931a9a87207d643d1af42d Mon Sep 17 00:00:00 2001 From: Domenico Ottolia Date: Mon, 3 May 2021 22:19:50 -0400 Subject: [PATCH] Add mtvec and stvec tests to testbench --- .../testbench/testbench-imperas.sv | 4 + .../testgen/privileged/testgen-TVAL.py | 4 +- .../testgen/privileged/testgen-TVEC.py | 496 +++++++++--------- 3 files changed, 263 insertions(+), 241 deletions(-) diff --git a/wally-pipelined/testbench/testbench-imperas.sv b/wally-pipelined/testbench/testbench-imperas.sv index aeb0a9e8..e0876842 100644 --- a/wally-pipelined/testbench/testbench-imperas.sv +++ b/wally-pipelined/testbench/testbench-imperas.sv @@ -354,6 +354,8 @@ module testbench(); "rv64p/WALLY-SEPC", "4000", "rv64p/WALLY-MTVAL", "6000", "rv64p/WALLY-STVAL", "4000", + "rv64p/WALLY-MTVEC", "2000", + "rv64p/WALLY-STVEC", "2000", "rv64p/WALLY-MARCHID", "4000", "rv64p/WALLY-MIMPID", "4000", "rv64p/WALLY-MHARTID", "4000", @@ -371,6 +373,8 @@ module testbench(); // "rv32p/WALLY-MIMPID", "4000", // "rv32p/WALLY-MHARTID", "4000", // "rv32p/WALLY-MVENDORID", "4000" + // "rv32p/WALLY-MTVEC", "2000", + // "rv32p/WALLY-STVEC", "2000" }; string tests64periph[] = '{ diff --git a/wally-pipelined/testgen/privileged/testgen-TVAL.py b/wally-pipelined/testgen/privileged/testgen-TVAL.py index 9cb6b6d8..8a728f7a 100644 --- a/wally-pipelined/testgen/privileged/testgen-TVAL.py +++ b/wally-pipelined/testgen/privileged/testgen-TVAL.py @@ -190,7 +190,9 @@ for xlen in xlens: # This is the address we write results to # x6: Starting address we should write expected results to # ... - # x1 - x5 can be freely written + # x4 & x5 can be freely written + # x3 — DO NOT WRITE ANY NON-ZERO VALUE TO THIS — test exits on ecall if x3 = 1 (x3 is gp) + # x1 & x2 can be freely written diff --git a/wally-pipelined/testgen/privileged/testgen-TVEC.py b/wally-pipelined/testgen/privileged/testgen-TVEC.py index a6eef5d2..8b731be8 100644 --- a/wally-pipelined/testgen/privileged/testgen-TVEC.py +++ b/wally-pipelined/testgen/privileged/testgen-TVEC.py @@ -51,52 +51,39 @@ def writeVectors(storecmd, returningInstruction): # Illegal Instruction - writeTest(storecmd, f, r, f""" - .fill 1, 4, 0 - """, False, 0) + # writeTest(storecmd, f, r, f""" + # .fill 1, 4, 0 + # """, False, 0) - # Breakpoint - if returningInstruction != "ebreak": + # # Breakpoint + # if returningInstruction != "ebreak": + # writeTest(storecmd, f, r, f""" + # ebreak + # """, False, 0) + + # # Load Address Misaligned + # writeTest(storecmd, f, r, f""" + # lw x0, 11(x0) + # """, False, 0) + + # # Load Access fault: False, 5 + + # # Store/AMO address misaligned + # writeTest(storecmd, f, r, f""" + # sw x0, 11(x0) + # """, False, 0) + + # # Environment call from u-mode: only for when only M and U mode enabled? + # # writeTest(storecmd, f, r, f""" + # # ecall + # # """, False, 8, "u") + if returningInstruction != "ecall": writeTest(storecmd, f, r, f""" - ebreak + ecall """, False, 0) - # Load Address Misaligned - writeTest(storecmd, f, r, f""" - lw x0, 11(x0) - """, False, 0) - # Load Access fault: False, 5 - - # Store/AMO address misaligned - writeTest(storecmd, f, r, f""" - sw x0, 11(x0) - """, False, 0) - - # Environment call from u-mode: only for when only M and U mode enabled? - # writeTest(storecmd, f, r, f""" - # ecall - # """, False, 8, "u") - if returningInstruction != "ecall": - if fromMode == "u": - writeTest(storecmd, f, r, f""" - ecall - """, False, 0) - - # Environment call from s-mode - if fromMode == "s": - writeTest(storecmd, f, r, f""" - ecall - """, False, 0) - - # Environment call from m-mode - if fromMode == "m": - writeTest(storecmd, f, r, f""" - ecall - """, False, 0) - - - if fromMode == "m": + if fromMode == "m" and testMode == "m": expectedCode = 7 if fromMode == "m" else 5 clintAddr = "0x2004000" @@ -105,9 +92,9 @@ def writeVectors(storecmd, returningInstruction): csrrs x0, {fromMode}status, x1 la x18, {clintAddr} - lw x11, 0(x18) - li x1, 0x3fffffffffffffff - {storecmd} x1, 0(x18) + # lw x11, 0(x18) + # li x1, 0x3fffffffffffffff + # {storecmd} x1, 0(x18) li x1, 0x80 csrrs x0, {fromMode}ie, x1 @@ -132,7 +119,7 @@ def writeVectors(storecmd, returningInstruction): def writeTest(storecmd, f, r, test, interrupt, code, resetHander = ""): - global testnum, storeAddressOffset + global testnum, storeAddressOffset, xlen expected = code @@ -156,19 +143,27 @@ def writeTest(storecmd, f, r, test, interrupt, code, resetHander = ""): {test} """ - lines += f""" + + if not areVectoredTrapsSupported or not vectoredInterrupts: + expected = 0 + + writeGeneralTest(storecmd, f, r, lines, expected) + +def writeGeneralTest(storecmd, f, r, test, expected): + global testnum, storeAddressOffset, xlen + + lines = f""" + {test} {storecmd} x25, {testnum * wordsize}(x6) """ - if not areVectoredTrapsSupported: - expected = 0 - 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 ################################## @@ -177,7 +172,7 @@ def writeTest(storecmd, f, r, test, interrupt, code, resetHander = ""): author = "dottolia@hmc.edu" xlens = [32, 64] -testCount = 16; +testCount = 4; # setup # Change this seed to a different constant value for every test @@ -219,6 +214,25 @@ for xlen in xlens: for line in h: f.write(line) + # Ensure MODE of *tvec (last 2 bits) is either 00 or 01 + f.write(f""" + csrr x19, {testMode}tvec + """) + + for i in range(0, 16): + i = i; + trySet = i | 0b10; + expected = trySet & 0xFFFF_FFFFD; + writeGeneralTest(storecmd, f, r, f""" + li x1, {trySet} + csrw {testMode}tvec, x1 + csrr x25, {testMode}tvec + """, expected) + + f.write(f""" + csrw {testMode}tvec, x19 + """) + # We need to leave at least one bit in medeleg unset so that we have a way to get # back to machine mode when the tests are complete (otherwise we'll only ever be able # to get up to supervisor mode). @@ -233,211 +247,218 @@ for xlen in xlens: # For testgen-TVAL, we don't need to test ebreak, so we can use that as the sole # returning instruction. For others, like testgen-CAUSE, we'll need to put # both ebreak and ecall here. + + for returningInstruction in ["ebreak"]: + for vectoredInterrupts in [True, False]: - # All registers used: - # x30: set to 1 if we should return to & stay in machine mode after trap, 0 otherwise - # ... - # x28: address trap handler should jump to for the test - # x27: address the test should return to after the test - # ... - # x25: value to write to memory - # ... - # x20: intermediate value in trap handler. Don't overwrite this! - # x19: mtvec old value - # x18: medeleg old value - # x17: sedeleg old value (currently unused — user mode traps deprecated) - # x16: mideleg old value - # ... - # x10 - x14 can be freely written - # ... - # x7: copy of x6. Increment this instead of using an offset on x6. - # this allows us to create more than 2048/wordlen tests. - # This is the address we write results to - # x6: Starting address we should write expected results to - # ... - # x1 - x5 can be freely written + # All registers used: + # x30: set to 1 if we should return to & stay in machine mode after trap, 0 otherwise + # ... + # x28: address trap handler should jump to for the test + # x27: address the test should return to after the test + # ... + # x25: value to write to memory + # ... + # x20: intermediate value in trap handler. Don't overwrite this! + # x19: mtvec old value + # x18: medeleg old value + # x17: sedeleg old value (currently unused — user mode traps deprecated) + # x16: mideleg old value + # ... + # x10 - x14 can be freely written + # ... + # x7: copy of x6. Increment this instead of using an offset on x6. + # this allows us to create more than 2048/wordlen tests. + # This is the address we write results to + # x6: Starting address we should write expected results to + # ... + # x1 - x5 can be freely written - # Set up x7 and store old value of mtvec - lines = f""" - add x7, x6, x0 - csrr x19, mtvec - """ - - # Not used — user mode traps are deprecated - if testMode == "u": - lines += f""" - csrr x17, sedeleg - li x9, {"0b1100000000" if testMode == "u" else "0b0000000000"} - csrs sedeleg, x9 - """ - - # Code that will jump to the test (x28 is set in writeTest above) - testJumpCode = f""" - auipc x27, 0 - addi x27, x27, 12 - jr x28 - """ - - beforeCode = "" - for i in range(0, 16): - beforeCode+=f""" - nop - nop - li x25, {i} - j _j_m_trap_end_{returningInstruction} + # Set up x7 and store old value of mtvec + lines = f""" + # add x7, x6, x0 + csrr x19, mtvec """ - ######## - ####### FIXME: wally is causing exception code 1 when you put non 4-bit aligned into mtvec. Bad wally. - ######## - - # Code for handling traps in different modes - # Some comments are inside of the below strings (prefixed with a #, as you might expected) - lines += f""" - # Reset x30 to 0 so we can run the tests. We'll set this to 1 when tests are completed so we stay in machine mode - li x30, 0 - - # Set up - la x1, _j_m_trap_{returningInstruction} - #addi x1, 1 - csrw mtvec, x1 - la x1, _j_s_trap_{returningInstruction} - csrw stvec, x1 - la x1, _j_u_trap_{returningInstruction} - # csrw utvec, x1 # user mode traps are not supported - - # Start the tests! - j _j_t_begin_{returningInstruction} - - # Machine mode traps - _j_m_trap_{returningInstruction}: - {beforeCode} - - _j_m_trap_end_{returningInstruction}: - {testJumpCode} - - csrrs x20, mepc, x0 - addi x20, x20, 4 - csrrw x0, mepc, x20 - bnez x30, _j_all_end_{returningInstruction} - mret - - # Supervisor mode traps - _j_s_trap_{returningInstruction}: - {testJumpCode if testMode == "s" else "li x25, 0xBAD00001"} - - csrrs x20, sepc, x0 - addi x20, x20, 4 - csrrw x0, sepc, x20 - bnez x30, _j_goto_machine_mode_{returningInstruction} - sret - - # Unused: user mode traps are no longer supported - _j_u_trap_{returningInstruction}: - {testJumpCode if testMode == "u" else "li x25, 0xBAD00000"} - - csrrs x20, uepc, x0 - addi x20, x20, 4 - csrrw x0, uepc, x20 - bnez x30, _j_goto_supervisor_mode_{returningInstruction} - uret - - # Currently unused. Just jumps to _j_goto_machine_mode. If you actually - # want to implement this, you'll likely need to reset sedeleg here - # and then cause an exception with {returningInstruction} (based on my intuition. Try that first, but I could be missing something / just wrong) - _j_goto_supervisor_mode_{returningInstruction}: - j _j_goto_machine_mode_{returningInstruction} - - _j_goto_machine_mode_{returningInstruction}: - li x30, 1 # This will cause us to branch to _j_all_end_{returningInstruction} in the machine trap handler, which we'll get into by invoking... - {returningInstruction} # ... this instruction! - - # Run the actual tests! - _j_t_begin_{returningInstruction}: - """ - - fromModeOptions = ["m", "s", "u"] if testMode == "m" else (["s", "u"] if testMode == "s" else ["u"]) - - # We don't want to delegate our returning instruction. Otherwise, we'll have no way of getting - # back to machine mode at the end! (and we need to be in machine mode to complete the tests) - medelegMask = "0b1111111111110111" if returningInstruction == "ebreak" else "0b1111000011111111" - - # Set medeleg and mideleg - lines += f""" - csrr x18, medeleg - li x9, {medelegMask if testMode == "s" or testMode == "u" else "0"} - csrw medeleg, x9 - - csrr x16, mideleg - li x9, {"0xffffffff" if testMode == "s" or testMode == "u" else "0"} - csrw mideleg, x9 - """ - - f.write(lines) - - for fromMode in fromModeOptions: - lines = "" - - # Code to bring us down to supervisor mode - if fromMode == "s" or fromMode == "u": + # Not used — user mode traps are deprecated + if testMode == "u": lines += f""" - li x1, 0b110000000000 - csrrc x28, mstatus, x1 - li x1, 0b0100000000000 - csrrs x28, mstatus, x1 + csrr x17, sedeleg + li x9, {"0b1100000000" if testMode == "u" else "0b0000000000"} + csrs sedeleg, x9 + """ - auipc x1, 0 - addi x1, x1, 16 # x1 is now right after the mret instruction - csrw mepc, x1 - mret + # Code that will jump to the test (x28 is set in writeTest above) + testJumpCode = f""" + auipc x27, 0 + addi x27, x27, 12 + jr x28 + """ - # We're now in supervisor mode... - """ + beforeCode = {"m": "", "s": ""} + for pm in ["m", "s"]: + for i in range(0, 16): + beforeCode[pm] = beforeCode[pm] + f""" + nop + nop + li x25, {i} + j _j_{pm}_trap_end_{returningInstruction}_{vectoredInterrupts} + """ - # Code to bring us down to user mode - if fromMode == "u": - lines += f""" + # Code for handling traps in different modes + # Some comments are inside of the below strings (prefixed with a #, as you might expected) + enableVectored = "addi x1, x1, 1" if vectoredInterrupts else "" - li x1, 0b110000000000 - csrrc x28, sstatus, x1 + lines += f""" + # Reset x30 to 0 so we can run the tests. We'll set this to 1 when tests are completed so we stay in machine mode + li x30, 0 - auipc x1, 0 - addi x1, x1, 16 # x1 is now right after the sret instruction - csrw sepc, x1 + # Set up + la x1, _j_m_trap_{returningInstruction}_{vectoredInterrupts} + {enableVectored} # enable/don't enable vectored interrupts + csrw mtvec, x1 + la x1, _j_s_trap_{returningInstruction}_{vectoredInterrupts} + {enableVectored} # enable/don't enable vectored interrupts + csrw stvec, x1 + la x1, _j_u_trap_{returningInstruction}_{vectoredInterrupts} + {enableVectored} # enable/don't enable vectored interrupts + # csrw utvec, x1 # user mode traps are not supported + + # Start the tests! + j _j_t_begin_{returningInstruction}_{vectoredInterrupts} + + # Machine mode traps + _j_m_trap_{returningInstruction}_{vectoredInterrupts}: + {beforeCode['m']} + + _j_m_trap_end_{returningInstruction}_{vectoredInterrupts}: + {testJumpCode} + + csrrs x20, mepc, x0 + addi x20, x20, 4 + csrrw x0, mepc, x20 + bnez x30, _j_all_end_{returningInstruction}_{vectoredInterrupts} + mret + + # Supervisor mode traps + _j_s_trap_{returningInstruction}_{vectoredInterrupts}: + {beforeCode['s']} + + _j_s_trap_end_{returningInstruction}_{vectoredInterrupts}: + {testJumpCode} + + csrrs x20, sepc, x0 + addi x20, x20, 4 + csrrw x0, sepc, x20 + bnez x30, _j_goto_machine_mode_{returningInstruction}_{vectoredInterrupts} sret - # We're now in user mode... - """ + # Unused: user mode traps are no longer supported + _j_u_trap_{returningInstruction}_{vectoredInterrupts}: + {testJumpCode if testMode == "u" else "li x25, 0xBAD00000"} + + csrrs x20, uepc, x0 + addi x20, x20, 4 + csrrw x0, uepc, x20 + bnez x30, _j_goto_supervisor_mode_{returningInstruction}_{vectoredInterrupts} + uret + + # Currently unused. Just jumps to _j_goto_machine_mode. If you actually + # want to implement this, you'll likely need to reset sedeleg here + # and then cause an exception with {returningInstruction} (based on my intuition. Try that first, but I could be missing something / just wrong) + _j_goto_supervisor_mode_{returningInstruction}_{vectoredInterrupts}: + j _j_goto_machine_mode_{returningInstruction}_{vectoredInterrupts} + + _j_goto_machine_mode_{returningInstruction}_{vectoredInterrupts}: + li x30, 1 # This will cause us to branch to _j_all_end_{returningInstruction}_{vectoredInterrupts} in the machine trap handler, which we'll get into by invoking... + {returningInstruction} # ... this instruction! + + # Run the actual tests! + _j_t_begin_{returningInstruction}_{vectoredInterrupts}: + """ + + fromModeOptions = ["m", "s", "u"] if testMode == "m" else (["s", "u"] if testMode == "s" else ["u"]) + + # We don't want to delegate our returning instruction. Otherwise, we'll have no way of getting + # back to machine mode at the end! (and we need to be in machine mode to complete the tests) + medelegMask = "0b1111111111110111" if returningInstruction == "ebreak" else "0b1111000011111111" + + # Set medeleg and mideleg + lines += f""" + csrr x18, medeleg + li x9, {medelegMask if testMode == "s" or testMode == "u" else "0"} + csrw medeleg, x9 + + csrr x16, mideleg + li x9, {"0xffffffff" if testMode == "s" or testMode == "u" else "0"} + csrw mideleg, x9 + """ f.write(lines) - for i in range(0,testCount): - writeVectors(storecmd, returningInstruction) + + for fromMode in fromModeOptions: + lines = "" + + # Code to bring us down to supervisor mode + if fromMode == "s" or fromMode == "u": + lines += f""" + li x1, 0b110000000000 + csrrc x28, mstatus, x1 + li x1, 0b0100000000000 + csrrs x28, mstatus, x1 + + auipc x1, 0 + addi x1, x1, 16 # x1 is now right after the mret instruction + csrw mepc, x1 + mret + + # We're now in supervisor mode... + """ + + # Code to bring us down to user mode + if fromMode == "u": + lines += f""" + + li x1, 0b110000000000 + csrrc x28, sstatus, x1 + + auipc x1, 0 + addi x1, x1, 16 # x1 is now right after the sret instruction + csrw sepc, x1 + sret + + # We're now in user mode... + """ + + f.write(lines) + for i in range(0,testCount): + writeVectors(storecmd, returningInstruction) - # Very end of test. Bring us back up to machine mode - # We set x30 to 1, which will cause us to branch to _j_all_end in the - # machine mode trap handler, before executing the mret instruction. This will - # make us stay in machine mode. - # - # If we're currently in user mode, this will first bump us up to the supervisor mode - # trap handler, which will call returningInstruction again before it's sret instruction, - # bumping us up to machine mode - # - # Get into the trap handler by running returningInstruction (either an ecall or ebreak) - f.write(f""" - li x30, 1 - li gp, 0 - {returningInstruction} - _j_all_end_{returningInstruction}: + # Very end of test. Bring us back up to machine mode + # We set x30 to 1, which will cause us to branch to _j_all_end in the + # machine mode trap handler, before executing the mret instruction. This will + # make us stay in machine mode. + # + # If we're currently in user mode, this will first bump us up to the supervisor mode + # trap handler, which will call returningInstruction again before it's sret instruction, + # bumping us up to machine mode + # + # Get into the trap handler by running returningInstruction (either an ecall or ebreak) + f.write(f""" + li x30, 1 + li gp, 0 + {returningInstruction} + _j_all_end_{returningInstruction}_{vectoredInterrupts}: - # Reset trap handling csrs to old values - csrw mtvec, x19 - csrw medeleg, x18 - csrw mideleg, x16 - """) + # Reset trap handling csrs to old values + csrw mtvec, x19 + csrw medeleg, x18 + csrw mideleg, x16 + """) # print footer h = open("../testgen_footer.S", "r") @@ -450,8 +471,3 @@ for xlen in xlens: f.write(lines) f.close() r.close() - - - - -