cvw/wally-pipelined/testgen/testgen-VIRTUALMEMORY.py
Thomas Fleming d8ac9034b7 Create virtual memory ad-hoc test
Test program is currently failing on ovpsim. There is no indication that ovpsim
is properly implementing virtual memory translation when satp is set accordingly.
Need to confirm whether this is a problem with ovpsim, how ovpsim is being
called, or the test itself.
2021-03-03 17:06:37 -05:00

285 lines
8.9 KiB
Python

#!/usr/bin/python3
##################################
# testgen-VIRTUALMEMORY.py
#
# Jessica Torrey <jtorrey@hmc.edu> 01 March 2021
# Thomas Fleming <tfleming@hmc.edu> 01 March 2021
#
# Generate an ad-hoc test program for RISC-V Design Validation. Tests the
# functionality of the memory management unit (MMU).
##################################
##################################
# libraries
##################################
from datetime import datetime
from random import randint, seed, getrandbits
from textwrap import dedent
from virtual_memory_util import *
##################################
# global structures
##################################
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 initialize_page_directory()
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
##################################
if __name__ == "__main__":
instructions = ["sd", "sw", "sh", "sb"]
author = "Jessica Torrey <jtorrey@hmc.edu> & Thomas Fleming <tfleming@hmc.edu>"
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-VIRTUALMEMORY.S"
refname = f"../../imperas-riscv-tests/riscv-test-suite/rv{xlen}i/references/WALLY-VIRTUALMEMORY.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)]