Add machine-mode timer interrupts to mcause tests

This commit is contained in:
Domenico Ottolia 2021-04-29 16:39:18 -04:00
parent 6fc04768f5
commit c8a81779ca
3 changed files with 189 additions and 155 deletions

View File

@ -345,7 +345,7 @@ module testbench();
}; };
string tests64p[] = '{ string tests64p[] = '{
"rv64p/WALLY-MCAUSE", "2000", "rv64p/WALLY-MCAUSE", "4000",
"rv64p/WALLY-SCAUSE", "2000", "rv64p/WALLY-SCAUSE", "2000",
"rv64p/WALLY-MEPC", "5000", "rv64p/WALLY-MEPC", "5000",
"rv64p/WALLY-SEPC", "4000", "rv64p/WALLY-SEPC", "4000",
@ -360,7 +360,7 @@ module testbench();
}; };
string tests32p[] = '{ string tests32p[] = '{
"rv32p/WALLY-MCAUSE", "2000", "rv32p/WALLY-MCAUSE", "4000",
"rv32p/WALLY-SCAUSE", "2000", "rv32p/WALLY-SCAUSE", "2000",
"rv32p/WALLY-MEPC", "5000", "rv32p/WALLY-MEPC", "5000",
"rv32p/WALLY-SEPC", "4000", "rv32p/WALLY-SEPC", "4000",

View File

@ -1,6 +1,6 @@
#!/usr/bin/python3 #!/usr/bin/python3
################################## ##################################
# testgen-CAUSE.py # testgen-CAUSE.py (new)
# #
# dottolia@hmc.edu 1 Mar 2021 # dottolia@hmc.edu 1 Mar 2021
# #
@ -9,8 +9,12 @@
# #
################################## ##################################
# DOCUMENTATION: # DOCUMENTATION:
# Most of the comments explaining what everything #
# does can be found in testgen-TVAL.py # The most up-to-date comments explaining what everything
# does and the layout of the privileged tests
# can be found in testgen-TVAL.py. This and
# other files do not have as many comments
#
################################### ###################################
################################## ##################################
@ -39,6 +43,35 @@ def randRegs():
def writeVectors(storecmd, returningInstruction): def writeVectors(storecmd, returningInstruction):
global testnum global testnum
if testMode == "m":
if fromMode == "m":
expectedCode = 7 if fromMode == "m" else 5
clintAddr = "0x2004000"
writeTest(storecmd, f, r, f"""
li x1, 0x8
csrrs x0, {fromMode}status, x1
la x18, {clintAddr}
lw x11, 0(x18)
li x1, 0x3fffffffffffffff
{storecmd} x1, 0(x18)
li x1, 0x80
csrrs x0, {fromMode}ie, x1
{storecmd} x0, 0(x18)
""", True, expectedCode, f"""
li x1, 0x80
csrrc x0, {fromMode}ie, x1
li x1, 0x8
csrrc x0, {fromMode}status, x1
la x18, {clintAddr}
{storecmd} x0, 0(x18)
""")
# Page 6 of unpriviledged spec # Page 6 of unpriviledged spec
# For both CSRRS and CSRRC, if rs1=x0, then the instruction will not write to the CSR at all, and so shall not cause any of the side effects # For both CSRRS and CSRRC, if rs1=x0, then the instruction will not write to the CSR at all, and so shall not cause any of the side effects
@ -58,27 +91,28 @@ def writeVectors(storecmd, returningInstruction):
# """) # """)
# User Timer Interrupt: True, 4 # User Timer Interrupt: True, 4
# TODO: THIS NEEDS TO BE IMPLEMENTED
# Supervior timer interrupt: True, 5 # Supervior timer interrupt: True, 5
# TODO: THIS NEEDS TO BE IMPLEMENTED
# Machine timer interrupt: True, 7 # Machine timer interrupt: True, 7
# TODO: THIS NEEDS TO BE IMPLEMENTED
# if fromMode == "m": # if fromMode == "m":
# clintAddr = "0x2004000" # clintAddr = "0x2004000"
# writeTest(storecmd, f, r, f"""
# li x1, 0x8
# csrrs x0, {fromMode}status, x1
# la x18, {clintAddr} # writeTest(storecmd, f, r, f"""
# lw x11, 0(x18) # # li x1, 0x8
# li x1, 1 # # csrrs x0, mstatus, x1
# # li x1, 0x80
# # csrrs x0, mie, x1
# # la x18, {clintAddr}
# # lw x11, 0(x18)
# # lw x12, 4(x18)
# # li x1, 1
# # {storecmd} x1, 0(x18) # # {storecmd} x1, 0(x18)
# nop
# li x1, 0x80 # sub x1, x2, x3
# csrrs x0, {fromMode}ie, x1 # sub x2, x3, x4
# sub x3, x4, x5
# nop # nop
# nop # nop
# nop # nop
@ -86,35 +120,18 @@ def writeVectors(storecmd, returningInstruction):
# nop # nop
# nop # nop
# nop # nop
# """, True, 4, f"""
# li x1, 0x80
# # csrrc x0, {fromMode}ie, x1
# li x1, 0x8
# # csrrc x0, {fromMode}status, x1
# la x18, {clintAddr}
# {storecmd} x11, 0(x18)
# """)
# writeTest(storecmd, f, r, f"""
# li x10, MASK_XLEN(0x8)
# csrrs x0, mstatus, x10
# li x11, MASK_XLEN(0x80)
# csrrs x0, mie, x11
# la x18, 0x2004000
# lw x11, 0(x18)
# lw x12, 4(x18)
# {storecmd} x0, 0(x18)
# {storecmd} x0, 4(x18)
# nop # nop
# nop # nop
# """, True, 7, "m", f""" # nop
# la x18, 0x2004000 # nop
# {storecmd} x11, 0(x18) # nop
# {storecmd} x12, 4(x18) # nop
# nop
# nop
# nop
# """, True, 7, f"""
# # la x18, {clintAddr}
# # {storecmd} x11, 0(x18)
# """) # """)
#writeTest(storecmd, f, r, f""" #writeTest(storecmd, f, r, f"""
@ -157,17 +174,10 @@ def writeVectors(storecmd, returningInstruction):
# """) # """)
# User external input: True, 8 # User external input: True, 8
# TODO: THIS NEEDS TO BE IMPLEMENTED
# Supervisor external input: True, 9 # Supervisor external input: True, 9
# TODO: THIS NEEDS TO BE IMPLEMENTED
# Machine externa input: True, 11 # Machine externa input: True, 11
# TODO: THIS NEEDS TO BE IMPLEMENTED
# Instruction address misaligned: False, 0 # Instruction address misaligned: False, 0
# TODO: THIS NEEDS TO BE IMPLEMENTED
# looks like this is giving us an infinite loop for wally # looks like this is giving us an infinite loop for wally
# BUG: jumping to a misaligned instruction address doesn't cause an exception: we actually jump... # BUG: jumping to a misaligned instruction address doesn't cause an exception: we actually jump...
# Either that, or somehow at the end we always end up at 0x80004002 # Either that, or somehow at the end we always end up at 0x80004002
@ -178,7 +188,6 @@ def writeVectors(storecmd, returningInstruction):
# """, False, 0) # """, False, 0)
# Instruction access fault: False, 1 # Instruction access fault: False, 1
# TODO: THIS NEEDS TO BE IMPLEMENTED
# Illegal Instruction # Illegal Instruction
writeTest(storecmd, f, r, f""" writeTest(storecmd, f, r, f"""
@ -197,81 +206,82 @@ def writeVectors(storecmd, returningInstruction):
""", False, 4) """, False, 4)
# Load Access fault: False, 5 # Load Access fault: False, 5
# TODO: THIS NEEDS TO BE IMPLEMENTED
# Store/AMO address misaligned # Store/AMO address misaligned
writeTest(storecmd, f, r, f""" writeTest(storecmd, f, r, f"""
sw x0, 11(x0) sw x0, 11(x0)
""", False, 6) """, False, 6)
# Environment call # 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 returningInstruction != "ecall":
if fromMode == "u": if fromMode == "u":
writeTest(storecmd, f, r, f""" writeTest(storecmd, f, r, f"""
ecall ecall
""", False, 8, "u") """, False, 8)
# Environment call from s-mode # Environment call from s-mode
if fromMode == "s": if fromMode == "s":
writeTest(storecmd, f, r, f""" writeTest(storecmd, f, r, f"""
ecall ecall
""", False, 9, "s") """, False, 9)
# Environment call from m-mode # Environment call from m-mode
if fromMode == "m": if fromMode == "m":
writeTest(storecmd, f, r, f""" writeTest(storecmd, f, r, f"""
ecall ecall
""", False, 11, "m") """, False, 11)
# Instruction page fault: 12 # Instruction page fault: 12
# TODO: THIS NEEDS TO BE IMPLEMENTED
# Load page fault: 13 # Load page fault: 13
# TODO: THIS NEEDS TO BE IMPLEMENTED
# Store/AMO page fault: 15 # Store/AMO page fault: 15
# TODO: THIS NEEDS TO BE IMPLEMENTED
def writeTest(storecmd, f, r, test, interrupt, code, mode = "m", resetHander = ""): def writeTest(storecmd, f, r, test, interrupt, code, resetHander = ""):
global testnum global testnum, storeAddressOffset
global testMode
expected = code expected = code
if(interrupt): if(interrupt):
expected+=(1 << (xlen - 1)) expected+=(1 << (xlen - 1))
# The code we actually change for our test
trapEnd = ""
before = ""
if mode != "m":
before = f"""
li x1, 0b110000000000
csrrc x28, {testMode}status, x1
li x1, 0b{"01" if mode == "s" else "00"}00000000000
csrrs x28, {testMode}status, x1
auipc x1, 0
addi x1, x1, 16 # x1 is now right after the mret instruction
csrrw x27, {testMode}epc, x1
{testMode}ret
# From {testMode}, we're now in {mode} mode...
"""
trapEnd = f"""j _jend{testnum}"""
lines = f""" lines = f"""
li x25, 0xDEADBEA7 csrr x25, {testMode}cause
{test} """
_jend{testnum}: # Boilerplate
#
# x28 is the address that our trap handler will jump to before returning.
# This is where we can do our actual tests. After we're done computing and storing
# what we want, we jump to x27, which continues with the trap handling code (look at the _j_x_trap_... labels)
#
lines = f"""
la x28, _jtest{testnum}
j _jdo{testnum}
_jtest{testnum}:
{lines}
{resetHander}
jr x27
_jdo{testnum}:
li x25, 0xDEADBEA7
li gp, 0
{test}
"""
# We expect x25 to be 0 always. This is because of the code we wrote at the begining
# of this function
# Store the expected value of x25 to memory and in the .reference_output file
lines += f"""
{storecmd} x25, {testnum * wordsize}(x6)
""" """
lines += storecmd + " x25, " + str(wordsize*testnum) + "(x6)\n"
f.write(lines) f.write(lines)
if (xlen == 32): if (xlen == 32):
line = formatrefstr.format(expected)+"\n" line = formatrefstr.format(expected)+"\n"
@ -284,13 +294,13 @@ def writeTest(storecmd, f, r, test, interrupt, code, mode = "m", resetHander = "
# main body # main body
################################## ##################################
# change these to suite your tests
author = "dottolia@hmc.edu" author = "dottolia@hmc.edu"
xlens = [32, 64] xlens = [32, 64]
numrand = 4; testCount = 16;
# setup # setup
seed(0xC365DDEB9173AB42) # make tests reproducible # Change this seed to a different constant value for every test
seed(0xC363DAEB9193AB45) # make tests reproducible
# generate files for each test # generate files for each test
for xlen in xlens: for xlen in xlens:
@ -304,13 +314,14 @@ for xlen in xlens:
storecmd = "sd" storecmd = "sd"
wordsize = 8 wordsize = 8
# testMode can be m, s, and u. User mode traps are deprecated, so this should likely just be ["m", "s"]
for testMode in ["m", "s"]: for testMode in ["m", "s"]:
imperaspath = "../../../imperas-riscv-tests/riscv-test-suite/rv" + str(xlen) + "p/" imperaspath = "../../../imperas-riscv-tests/riscv-test-suite/rv" + str(xlen) + "p/"
basename = "WALLY-" + testMode.upper() + "CAUSE" basename = "WALLY-" + testMode.upper() + "CAUSE"
fname = imperaspath + "src/" + basename + ".S" fname = imperaspath + "src/" + basename + ".S"
refname = imperaspath + "references/" + basename + ".reference_output" refname = imperaspath + "references/" + basename + ".reference_output"
testnum = 0 testnum = 0
storeAddressOffset = 0
# print custom header part # print custom header part
f = open(fname, "w") f = open(fname, "w")
@ -337,19 +348,38 @@ for xlen in xlens:
# two different returning instructions. # two different returning instructions.
# #
# Current code is written to only support ebreak and ecall. # Current code is written to only support ebreak and ecall.
for returningInstruction in ["ebreak", "ecall"]: #
# 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"]:
# All registers used: # All registers used:
# x30: set to 1 if we should return to & stay in machine mode after trap, 0 otherwise # x30: set to 1 if we should return to & stay in machine mode after trap, 0 otherwise
# ... # ...
# x26: expected epc value # 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 # x25: value to write to memory
# ... # ...
# x20: intermediate value in trap handler. Don't overwrite this!
# x19: mtvec old value # x19: mtvec old value
# x18: medeleg old value # x18: medeleg old value
# x17: sedeleg 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""" lines = f"""
add x7, x6, x0 add x7, x6, x0
csrr x19, mtvec csrr x19, mtvec
@ -363,90 +393,89 @@ for xlen in xlens:
csrs sedeleg, x9 csrs sedeleg, x9
""" """
clintAddr = "0x2004000" # Code that will jump to the test (x28 is set in writeTest above)
testJumpCode = f"""
auipc x27, 0
addi x27, x27, 12
jr x28
"""
# Code for handling traps in different modes
# Some comments are inside of the below strings (prefixed with a #, as you might expected)
lines += f""" 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 li x30, 0
# Set up
la x1, _j_m_trap_{returningInstruction} la x1, _j_m_trap_{returningInstruction}
csrw mtvec, x1 csrw mtvec, x1
la x1, _j_s_trap_{returningInstruction} la x1, _j_s_trap_{returningInstruction}
csrw stvec, x1 csrw stvec, x1
la x1, _j_u_trap_{returningInstruction} la x1, _j_u_trap_{returningInstruction}
csrw utvec, x1 # csrw utvec, x1 # user mode traps are not supported
# Start the tests!
j _j_t_begin_{returningInstruction} j _j_t_begin_{returningInstruction}
# Machine mode traps
_j_m_trap_{returningInstruction}: _j_m_trap_{returningInstruction}:
{testJumpCode if testMode == "m" else "li x25, 0xBAD00003"}
#li x1, 0x20 csrrs x20, mepc, x0
#csrrw x0, mie, x1 addi x20, x20, 4
csrrw x0, mepc, x20
li x11, 0x3fffffffffffffff
la x18, {clintAddr}
{storecmd} x11, 0(x18)
li x1, 0x8
csrrc x0, mstatus, x1
sub x1, x2, x3
sub x1, x2, x3
sub x1, x2, x3
sub x1, x2, x3
sub x1, x2, x3
sub x1, x2, x3
sub x1, x2, x3
sub x1, x2, x3
sub x1, x2, x3
sub x1, x2, x3
sub x1, x2, x3
sub x1, x2, x3
sub x1, x2, x3
sub x1, x2, x3
csrrs x1, mepc, x0
{"csrr x25, mcause" if testMode == "m" else "li x25, 0xBAD00003"}
addi x1, x1, 4
csrrw x0, mepc, x1
bnez x30, _j_all_end_{returningInstruction} bnez x30, _j_all_end_{returningInstruction}
mret mret
# Supervisor mode traps
_j_s_trap_{returningInstruction}: _j_s_trap_{returningInstruction}:
csrrs x1, sepc, x0 {testJumpCode if testMode == "s" else "li x25, 0xBAD00001"}
{"csrr x25, scause" if testMode == "s" else "li x25, 0xBAD00001"}
addi x1, x1, 4 csrrs x20, sepc, x0
csrrw x0, sepc, x1 addi x20, x20, 4
csrrw x0, sepc, x20
bnez x30, _j_goto_machine_mode_{returningInstruction} bnez x30, _j_goto_machine_mode_{returningInstruction}
sret sret
# Unused: user mode traps are no longer supported
_j_u_trap_{returningInstruction}: _j_u_trap_{returningInstruction}:
csrrs x1, uepc, x0 {testJumpCode if testMode == "u" else "li x25, 0xBAD00000"}
{"csrr x25, ucause" if testMode == "u" else "li x25, 0xBAD00000"}
addi x1, x1, 4 csrrs x20, uepc, x0
csrrw x0, uepc, x1 addi x20, x20, 4
csrrw x0, uepc, x20
bnez x30, _j_goto_supervisor_mode_{returningInstruction} bnez x30, _j_goto_supervisor_mode_{returningInstruction}
uret 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_goto_supervisor_mode_{returningInstruction}:
j _j_goto_machine_mode_{returningInstruction} j _j_goto_machine_mode_{returningInstruction}
_j_goto_machine_mode_{returningInstruction}: _j_goto_machine_mode_{returningInstruction}:
li x30, 1 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} {returningInstruction} # ... this instruction!
# Run the actual tests!
_j_t_begin_{returningInstruction}: _j_t_begin_{returningInstruction}:
""" """
fromModeOptions = ["m", "s", "u"] if testMode == "m" else (["s", "u"] if testMode == "s" else ["u"]) 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" medelegMask = "0b1111111111110111" if returningInstruction == "ebreak" else "0b1111000011111111"
# Set medeleg and mideleg
lines += f""" lines += f"""
csrr x18, medeleg csrr x18, medeleg
li x9, {medelegMask if testMode == "s" or testMode == "u" else "0"} li x9, {medelegMask if testMode == "s" or testMode == "u" else "0"}
csrw medeleg, x9 csrw medeleg, x9
csrr x16, mideleg
li x9, {"0xffffffff" if testMode == "s" or testMode == "u" else "0"}
csrw mideleg, x9
""" """
f.write(lines) f.write(lines)
@ -454,6 +483,7 @@ for xlen in xlens:
for fromMode in fromModeOptions: for fromMode in fromModeOptions:
lines = "" lines = ""
# Code to bring us down to supervisor mode
if fromMode == "s" or fromMode == "u": if fromMode == "s" or fromMode == "u":
lines += f""" lines += f"""
li x1, 0b110000000000 li x1, 0b110000000000
@ -469,6 +499,7 @@ for xlen in xlens:
# We're now in supervisor mode... # We're now in supervisor mode...
""" """
# Code to bring us down to user mode
if fromMode == "u": if fromMode == "u":
lines += f""" lines += f"""
@ -483,25 +514,33 @@ for xlen in xlens:
# We're now in user mode... # We're now in user mode...
""" """
# print directed and random test vectors
f.write(lines) f.write(lines)
for i in range(0,numrand): for i in range(0,testCount):
writeVectors(storecmd, returningInstruction) 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""" f.write(f"""
li x30, 1 li x30, 1
li gp, 0
{returningInstruction} {returningInstruction}
_j_all_end_{returningInstruction}: _j_all_end_{returningInstruction}:
# Reset trap handling csrs to old values
csrw mtvec, x19 csrw mtvec, x19
csrw medeleg, x18 csrw medeleg, x18
csrw mideleg, x16
""") """)
# if we're in supervisor mode, this leaves the ebreak instruction untested (we need a way to)
# get back to machine mode.
# print footer # print footer
h = open("../testgen_footer.S", "r") h = open("../testgen_footer.S", "r")
for line in h: for line in h:
@ -513,6 +552,3 @@ for xlen in xlens:
f.write(lines) f.write(lines)
f.close() f.close()
r.close() r.close()

View File

@ -52,8 +52,6 @@ def writeVectors(storecmd):
# Instruction access fault: False, 1 # Instruction access fault: False, 1
# Illegal Instruction # Illegal Instruction
#writeTest(storecmd, f, r, "ecall", False, 11)
writeTest(storecmd, f, r, f""" writeTest(storecmd, f, r, f"""
.fill 1, 4, 0 .fill 1, 4, 0
""", False, 2) """, False, 2)