Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions FemtoRV/RISCOF/FemtoRV/degu_isa.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
hart_ids: [0]
hart0:
ISA: RV32IC
User_Spec_Version: '2.3'
hw_data_misaligned_support: False
supported_xlen: [32]
physical_addr_sz: 32
10 changes: 10 additions & 0 deletions FemtoRV/RISCOF/FemtoRV/degu_platform.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
mtime:
implemented: true
address: 0xbff8
mtimecmp:
implemented: true
address: 0x4000
nmi:
label: nmi_vector
reset:
label: reset_vector
18 changes: 18 additions & 0 deletions FemtoRV/RISCOF/FemtoRV/env/link.ld
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
OUTPUT_ARCH( "riscv" )
ENTRY(rvtest_entry_point)

SECTIONS
{
. = 0x80000000;
.text.init : { *(.text.init) }
. = ALIGN(0x1000);
.tohost : { *(.tohost) }
. = ALIGN(0x1000);
.text : { *(.text) }
. = ALIGN(0x1000);
.data : { *(.data) }
.data.string : { *(.data.string) }
.bss : { *(.bss) }
_end = .;
}

53 changes: 53 additions & 0 deletions FemtoRV/RISCOF/FemtoRV/env/model_test.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
#ifndef _COMPLIANCE_MODEL_H
#define _COMPLIANCE_MODEL_H
#define RVMODEL_DATA_SECTION \
.pushsection .tohost,"aw",@progbits; \
.align 8; .global tohost; tohost: .dword 0; \
.align 8; .global fromhost; fromhost: .dword 0; \
.popsection; \
.align 8; .global begin_regstate; begin_regstate: \
.word 128; \
.align 8; .global end_regstate; end_regstate: \
.word 4;

//RV_COMPLIANCE_HALT
#define RVMODEL_HALT \
li x1, 1; \
write_tohost: \
sw x1, tohost, t5; \
j write_tohost;

#define RVMODEL_BOOT

//RV_COMPLIANCE_DATA_BEGIN
#define RVMODEL_DATA_BEGIN \
.align 4; .global begin_signature; begin_signature:

//RV_COMPLIANCE_DATA_END
#define RVMODEL_DATA_END \
.align 4; .global end_signature; end_signature: \
RVMODEL_DATA_SECTION \

//RVTEST_IO_INIT
#define RVMODEL_IO_INIT
//RVTEST_IO_WRITE_STR
#define RVMODEL_IO_WRITE_STR(_R, _STR)
//RVTEST_IO_CHECK
#define RVMODEL_IO_CHECK()
//RVTEST_IO_ASSERT_GPR_EQ
#define RVMODEL_IO_ASSERT_GPR_EQ(_S, _R, _I)
//RVTEST_IO_ASSERT_SFPR_EQ
#define RVMODEL_IO_ASSERT_SFPR_EQ(_F, _R, _I)
//RVTEST_IO_ASSERT_DFPR_EQ
#define RVMODEL_IO_ASSERT_DFPR_EQ(_D, _R, _I)

#define RVMODEL_SET_MSW_INT

#define RVMODEL_CLEAR_MSW_INT

#define RVMODEL_CLEAR_MTIMER_INT

#define RVMODEL_CLEAR_MEXT_INT


#endif // _COMPLIANCE_MODEL_H
7 changes: 7 additions & 0 deletions FemtoRV/RISCOF/FemtoRV/quark_isa.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
hart_ids: [0]
hart0:
ISA: RV32I
User_Spec_Version: '2.3'
hw_data_misaligned_support: False
supported_xlen: [32]
physical_addr_sz: 32
10 changes: 10 additions & 0 deletions FemtoRV/RISCOF/FemtoRV/quark_platform.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
mtime:
implemented: true
address: 0xbff8
mtimecmp:
implemented: true
address: 0x4000
nmi:
label: nmi_vector
reset:
label: reset_vector
257 changes: 257 additions & 0 deletions FemtoRV/RISCOF/FemtoRV/riscof_r5p.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,257 @@
import os
import logging

import riscof.utils as utils
import riscof.constants as constants
from riscof.pluginTemplate import pluginTemplate

logger = logging.getLogger()

class r5p(pluginTemplate):
__model__ = "r5p"

# TODO: please update the below to indicate family, version, etc of your DUT.
__version__ = "X.X.X"

def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)

config = kwargs.get('config')

# If the config node for this DUT is missing or empty. Raise an error.
# At minimum we need the paths to the ispec and pspec files.
if config is None:
print("Please enter input file paths in configuration.")
raise SystemExit(1)

# Number of parallel jobs that can be spawned off by RISCOF
# for various actions performed in later functions, specifically to run the tests in
# parallel on the DUT executable. Can also be used in the build function if required.
self.num_jobs = str(config['jobs'] if 'jobs' in config else 1)

# Path to the directory where this python file is located. Collect it from the config.ini.
self.pluginpath = os.path.abspath(config['pluginpath'])

# Collect the paths to the riscv-config absed ISA and platform yaml files.
# One can choose to hardcode these here itself instead of picking it from the config.ini file.
self.isa_spec = os.path.abspath(config['ispec'])
self.platform_spec = os.path.abspath(config['pspec'])

# We capture if the user would like the run the tests on the target or not.
# If you are interested in just compiling the tests and not running them on the target,
# then following variable should be set to False.
if 'target_run' in config and config['target_run']=='0':
self.target_run = False
else:
self.target_run = True

# Capture HDL simulator choice.
self.simulator = config['simulator']
self.dut = config['dut']

# Enable/disable debug functionality
self.debug = config['debug']

def initialise(self, suite, work_dir, archtest_env):

# Capture the working directory.
# Any artifacts that the DUT creates should be placed in this directory.
# Other artifacts from the framework and the Reference plugin will also be placed here.
self.work_dir = work_dir

# Capture the architectural test-suite directory.
self.suite_dir = suite

# Capture the environment.
self.archtest_env = archtest_env

# In case of an RTL based DUT, this would be point to the final binary executable of your
# test-bench produced by a simulator (like verilator, vcs, incisive, etc).
# In case of an iss or emulator, this variable could point to where the iss binary is located.
# If PATH variable is missing in the config.ini we can hardcode the alternate here.
# TODO: PATH?
if self.simulator == 'questa':
self.dut_exe = f'DUT={self.dut} make -C {os.path.join(self.work_dir, "../../sim/questa/")} -f Makefile'
elif self.simulator == 'verilator':
self.dut_exe = f'DUT={self.dut} make -C {os.path.join(self.work_dir, "../../sim/verilator/")} -f Makefile'
elif self.simulator == 'vivado':
self.dut_exe = f'DUT={self.dut} make -C {os.path.join(self.work_dir, "../../sim/vivado/")} -f Makefile'
else:
# TODO: __model__ ?
print("No simulator selected for '{__model__}'.")
raise SystemExit(1)

def build(self, isa_yaml, platform_yaml):

# load the isa yaml as a dictionary in python.
ispec = utils.load_yaml(isa_yaml)['hart0']

# Capture the XLEN value by picking the max value in 'supported_xlen' field of isa YAML.
# This will be useful in setting integer value in the compiler string (if not already hardcoded);
self.xlen = ('64' if 64 in ispec['supported_xlen'] else '32')

# For DUT start building the '--isa' argument.
# The self.isa is DUT specific and may not be useful for all DUTs.
self.isa = 'rv' + self.xlen
for ext in ['I', 'M', 'C', 'F', 'D']:
if ext in ispec["ISA"]:
self.isa += ext.lower()

# Note the march is not hardwired here, because it will change for each test.
# Similarly the output elf name and compile macros will be assigned later in the runTests function.
self.compile_exe = f'riscv{self.xlen}-unknown-elf-gcc'
self.objcopy_exe = f'riscv{self.xlen}-unknown-elf-objcopy'
self.objdump_exe = f'riscv{self.xlen}-unknown-elf-objdump'
self.symbols_exe = f'riscv{self.xlen}-unknown-elf-nm'

def runTests(self, testList):

# TUDO: figure out why there is an extra character in the name.
name = self.name[:-1]

# Delete Makefile if it already exists.
if os.path.exists(self.work_dir+ "/Makefile." + name):
os.remove(self.work_dir+ "/Makefile." + name)
# create an instance the makeUtil class that we will use to create targets.
make = utils.makeUtil(makefilePath=os.path.join(self.work_dir, "Makefile." + name))

# Set the make command that will be used.
# The num_jobs parameter was set in the __init__ function earlier
make.makeCommand = 'make -k -j' + self.num_jobs

# We will iterate over each entry in the testList.
# Each entry node will be referred to by the variable testname.
for testname in testList:

# For each testname we get all its fields (as described by the testList format).
testentry = testList[testname]

# We capture the path to the assembly file of this test.
test = testentry['test_path']

# Capture the directory where the artifacts of this test will be dumped/created.
# RISCOF is going to look into this directory for the signature files.
test_dir = testentry['work_dir']

# Name of the elf file after compilation of the test.
elf = 'dut.elf'

# Name of the signature file as per requirement of RISCOF.
# RISCOF expects the signature to be named as DUT-<dut-name>.signature.
# The below variable creates an absolute path of signature file.
sig_file = os.path.join(test_dir, name + ".signature")

# Name of the HDL testbench log file
trace_file = os.path.join(test_dir, "dut.log")

# For each test there are specific compile macros that need to be enabled.
# The macros in the testList node only contain the macros/values.
# For the gcc toolchain we need to prefix with "-D". The following does precisely that.
compile_macros = ' '.join([f'-D{macro}' for macro in testentry['macros']])

# Construct the command line for compiling testcase source assembly into an elf file.
compile_cmd = self.compile_exe + (
f' -mabi={'lp64' if (self.xlen == 64) else 'ilp32'}'
f' -march={testentry['isa'].lower()}'
f' -static -mcmodel=medany -fvisibility=hidden -nostdlib -nostartfiles -g'
f' -T {self.pluginpath}/env/link.ld'
f' -I {self.pluginpath}/env/'
f' -I {self.archtest_env}'
f' {compile_macros}'
f' {test} -o {elf}'
)

# Command for converting elf file into a binary/hex file for loading into HDL testbench memory.
# Uncomment either the binary or hex version, depending on your.
objcopy_cmd = self.objcopy_exe + f' -O binary {elf} {elf}.bin'
#objcopy_cmd = self.objcopy_exe + f' -O binary {elf} {elf}.hex'

# Disassemble the ELF file for debugging purposes
objdump_cmd = self.objdump_exe + (
f' -M no-aliases -M numeric'
f' -D {elf} > {elf}.disass'
)

# extract listed symbols
symbols_list = ['begin_signature', 'end_signature', 'tohost', 'fromhost']
# construct dictionary of listed symbols
symbols_cmd = []
# get symbol list from elf file
cmd = self.symbols_exe + f' {elf} > dut.symbols'
symbols_cmd.append(cmd)
for symbol in symbols_list:
# get symbols from symbol list file
cmd = f'{symbol}=$$(grep -w {symbol} dut.symbols | cut -c 1-8)'
symbols_cmd.append(cmd)

# Simulation define macros.
simulate_defines_dict = {}
if self.debug:
simulate_defines_dict.update({'TRACE_SPIKE': None})

# Convert define macro dictionary into CLI
# TODO: properly handle define macros without value
if self.simulator == 'questa':
simulate_defines = ' '.join([f'-defineall {key}={val}' for key, val in simulate_defines_dict.items()])
elif self.simulator == 'verilator':
simulate_defines = ' '.join([f'-D{key}={val}' for key, val in simulate_defines_dict.items()])
elif self.simulator == 'vivado':
simulate_defines = ' '.join([f'-d {key}={val}' for key, val in simulate_defines_dict.items()])

# Construct Verilog plusargs dictionary containing file paths.
simulate_plusargs_dict = {
'firmware': os.path.join(test_dir, elf)+'.bin',
'signature': sig_file,
'trace': trace_file
}

# provide ELF symbols as plusargs
for symbol in symbols_list:
simulate_plusargs_dict.update({symbol: f'$${symbol}'})

# Other DUT testbench specific Verilog plusargs can be added here.
simulate_plusargs_dict.update({})

# Convert Verilog plusargs dictionary into CLI
if self.simulator in ('questa', 'verilator'):
simulate_plusargs = ' '.join([f'+{key}={val}' for key, val in simulate_plusargs_dict.items()])
elif self.simulator == 'vivado':
simulate_plusargs = ' '.join([f'-testplusarg {key}={val}' for key, val in simulate_plusargs_dict.items()])

# If the user wants to disable running the tests and only compile the tests,
# then the "else" clause is executed below assigning the sim command to simple no action echo statement.
if self.target_run:
# set up the simulation command. Template is for spike. Please change.
simulate_cmd = self.dut_exe + (
f' RISCOF_DEFINES="{simulate_defines}"'
f' RISCOF_PLUSARGS="{simulate_plusargs}"'
)
else:
simulate_cmd = 'echo "NO RUN"'

# Concatenate all commands that need to be executed within a make-target.
execute = []
execute.append(f'cd {test_dir}')
execute.append(compile_cmd)
execute.append(objcopy_cmd)
execute.append(objdump_cmd)
execute += symbols_cmd
execute.append(simulate_cmd)

# Create a target.
# The makeutil will create a target with the name "TARGET<num>" where
# num starts from 0 and increments automatically for each new target that is added.
make.add_target('@' + ';\\\n'.join(execute))

# If you would like to exit the framework once the makefile generation is complete uncomment the following line.
# Note this will prevent any signature checking or report generation.
#raise SystemExit

# Once the make-targets are done and the makefile has been created,
# run all the targets in parallel using the make command set above.
make.execute_all(self.work_dir)

# If target runs are not required then we simply exit as this point after running all the makefile targets.
if not self.target_run:
raise SystemExit(0)

Loading