cvw/studies/testgen/testgen-JAL-JALR.py
2023-02-09 18:24:48 -08:00

287 lines
9.8 KiB
Python
Executable File

#!/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
##################################
# helper 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)
UnusedRegs.remove(rd)
OtherRegs = deepcopy(UnusedRegs)
if 0 in OtherRegs:
OtherRegs.remove(0)
if len(OtherRegs) == 0:
OtherRegs = deepcopy(AllRegs)
OtherRegs.remove(0)
rs = choice(OtherRegs)
OtherRegs = deepcopy(AllRegs)
OtherRegs.remove(StoreAdrReg)
OtherRegs.remove(rd)
if 0 in OtherRegs:
OtherRegs.remove(0)
if rs in OtherRegs:
OtherRegs.remove(rs)
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) and ("slli x" not in line):
CurrAdr += 8 if (xlen == 32) else 20
elif ("la x" in line):
CurrAdr += 8
else:
CurrAdr += 4
def expectValue(expectReg, expectVal, sigOffset):
global TestGroupSizes
TestGroupSizes[TestGroup-1] += 1
addInst(" "+storecmd+" x"+str(expectReg)+", "+str(wordsize*sigOffset)+"(x"+str(StoreAdrReg)+")\n")
f.write(" RVTEST_IO_ASSERT_GPR_EQ(x"+str(StoreAdrReg+1)+", x"+str(expectReg)+", "+formatstr.format(expectVal)+")\n")
if (xlen == 32):
r.write(formatrefstr.format(expectVal)+"\n")
else:
r.write(formatrefstr.format(expectVal % 2**32)+"\n" + formatrefstr.format(expectVal >> 32)+"\n")
def addJalr(rs,rd,dist):
target = CurrAdr + 20 + dist
target31_12 = CurrAdr >> 12 # 20 bits for lui
target11_0 = target - (target31_12 << 12) # 12 remaining bits
target31_16 = target31_12 >> 4 # lui sign extends, so shift in a leading 0
target15_12 = target31_12 - (target31_16 << 4) # the nibble we just lost
if target11_0 > 0:
offset = randint(-(1<<11)-1,(1<<11)-2-target11_0)
else:
offset = randint(-(1<<11)-1-target11_0,(1<<11)-2)
addInst(" lui x"+str(rs)+", 0x"+imm20formatstr.format(target31_16)+"\n")
addInst(" addi x"+str(rs)+", x"+str(rs)+", SEXT_IMM(0x0"+imm12formatstr.format(target15_12 << 8)+")\n")
addInst(" slli x"+str(rs)+", x"+str(rs)+", SEXT_IMM(4)\n")
addInst(" addi x"+str(rs)+", x"+str(rs)+", SEXT_IMM(0x"+imm12formatstr.format(0xfff&(offset+target11_0+randint(0,1)))+")\n")
addInst(" JALR x"+str(rd)+", x"+str(rs)+", SEXT_IMM(0x"+imm12formatstr.format(0xfff&(-offset))+")\n")
##################################
# test functions
##################################
def writeForwardsJumpVector(spacers,instr):
global TestNum
TestNum += 1
rd, rs, DataReg, OtherRd = registerSelect()
# Header
f.write("\n")
f.write(" # Testcase "+str(TestNum)+"\n")
# Test Code
addInst(" li x"+str(DataReg)+", "+formatstr.format(expected)+"\n")
if (instr=="JAL"):
addInst(" JAL x"+str(rd)+", 1f\n")
elif (instr=="JALR"):
dist = spacers*(8 if (xlen == 32) else 20) # Compute distance from linked adr to target adr
addJalr(rs,rd,dist);
else:
exit("invalid instruction")
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")
# Store values to be verified
expectValue(rd, LinkAdr, 2*TestNum+0)
expectValue(DataReg, expected, 2*TestNum+1)
def writeBackwardsJumpVector(spacers,instr):
global TestNum
TestNum += 1
rd, rs, DataReg, OtherRd = registerSelect()
# Header
f.write("\n")
f.write(" # Testcase "+str(TestNum)+"\n")
# Test Code
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")
if (instr=="JAL"):
addInst(" JAL x"+str(rd)+", 1b\n")
elif (instr=="JALR"):
dist = -20 - 4 - (1+spacers)*(8 if (xlen == 32) else 20) # Compute distance from linked adr to target adr
addJalr(rs,rd,dist);
else:
exit("invalid instruction")
LinkAdr = CurrAdr if (rd!=0) else 0 # rd's expected value
f.write("3:\n")
# Store values to be verified
expectValue(rd, LinkAdr, 2*TestNum+0)
expectValue(DataReg, expected, 2*TestNum+1)
def writeChainVector(repetitions,spacers):
global TestNum
TestNum += 1
rd, rs, DataReg,OtherRd = registerSelect()
# Header
f.write("\n")
f.write(" # Testcase "+str(TestNum)+"\n")
# Test Code
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")
# Store values to be verified
expectValue(rd, LinkAdr, 2*TestNum+0)
expectValue(DataReg, expected, 2*TestNum+1)
##################################
# main body
##################################
# change these to suite your tests
test = 0
tests = ["JAL","JALR"]
author = "Ben Bracker (bbracker@hmc.edu)"
xlens = [32,64]
numtests = 100
# setup
seed(0) # make tests reproducible
# generate files for each test
for test in tests:
for xlen in xlens:
print(test+" "+str(xlen))
CurrAdr = int("80000108",16)
TestNum = -1
TestGroup = 1
TestGroupSizes = [0]
AllRegs = list(range(0,32))
UnusedRegs = deepcopy(AllRegs)
StoreAdrReg = 6 # matches what's in header script
UnusedRegs.remove(6)
if (xlen==64):
expected = int("fedbca9876540000",16)
unexpected = int("ffff0000ffff0000",16)
else:
expected = int("fedbca98",16)
unexpected = int("ff00ff00",16)
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
imm20formatstr = "{:05x}"
imm12formatstr = "{:03x}"
if (xlen == 32):
storecmd = "sw"
wordsize = 4
else:
storecmd = "sd"
wordsize = 8
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(R) 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
if test == "JAL":
for i in range(0,31):
writeForwardsJumpVector(randint(0,4),"JAL")
for i in range(0,31):
writeBackwardsJumpVector(randint(0,4),"JAL")
writeForwardsJumpVector(100,"JAL")
writeBackwardsJumpVector(100,"JAL")
writeChainVector(6,True)
writeChainVector(16,False)
elif test == "JALR":
for i in range(0,31):
writeForwardsJumpVector(randint(0,4),"JALR")
for i in range(0,31):
writeBackwardsJumpVector(randint(0,4),"JALR")
# can't make these latter two too long else 12 bit immediate overflows
# (would need to lui or slli rs to achieve longer ranges)
writeForwardsJumpVector(15,"JALR")
writeBackwardsJumpVector(15,"JALR")
# 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()