Update google_riscv-dv to google/riscv-dv@5b1dd4e (#523)

Update code from upstream repository https://github.com/google/riscv-
dv to revision 5b1dd4e2eb11d49d3275da80953efc0c50f90447

* Add compliance mode to coverage model (google/riscv-dv#361)
  (taoliug)
* Revert " Make assign_operand become a method of class
  RiscvInstructionTraceEntry  (google/riscv-dv#357)" (google/riscv-
  dv#360) (taoliug)
* Fix script issue (google/riscv-dv#358) (taoliug)
*  Make assign_operand become a method of class
  RiscvInstructionTraceEntry  (google/riscv-dv#357) (Hai Hoang Dang)
* Remove unused variable (google/riscv-dv#348) (Hai Hoang Dang)
* Functional coverage improvement (google/riscv-dv#356) (taoliug)
* Fix list access exception thrown when parsing ovpsim illegal
  instruction (Udi Jonnalagadda)
* Add compliance mode and RTL mode to the coverage model
  (google/riscv-dv#354) (taoliug)
* Fix lr/sc sequence (google/riscv-dv#353) (taoliug)
* Fix minor illegal instruction issue (google/riscv-dv#351) (taoliug)
* Migrate to new instruction class (google/riscv-dv#350) (taoliug)
* misc issue fixes (google/riscv-dv#349) (taoliug)
* Update README to add contact info for the collaboration request
  (google/riscv-dv#347) (taoliug)
* Support running specific multiple tests (google/riscv-dv#346) (Hai
  Hoang Dang)
* Fix rv64 coverage model issue (google/riscv-dv#344) (taoliug)
* Fix the link for yaml/base_testlist.yaml (google/riscv-dv#343) (Hai
  Hoang Dang)
* refactor debug ROM generation (Udi Jonnalagadda)
* Add a runtime option to run with experimental features
  (google/riscv-dv#341) (taoliug)
* Fix a few issues with the new instruction class (google/riscv-
  dv#340) (taoliug)
* Improve performance of new experimental instruction class
  (google/riscv-dv#339) (taoliug)
* Fix CSR randomization bug when generating loops (google/riscv-
  dv#337) (udinator)
* Add support coverage flow for qrun, and minor fix for cov.py
  (google/riscv-dv#335) (Hai Hoang Dang)
* [ovpsim] Coding style fixes, fix floating point compare mismatch
  (google/riscv-dv#334) (taoliug)
* Fix ius flow issue (google/riscv-dv#333) (taoliug)
* Fix a few new instruction class issues (google/riscv-dv#332)
  (taoliug)
* Added two includes and starting variables for adding bitmanip
  extension (google/riscv-dv#328) (simond-imperas)
* Integrate experimental instruction class (google/riscv-dv#331)
  (taoliug)
* Minor fixes to run.py (google/riscv-dv#330) (taoliug)
* Run.py: minor refactor the code for compile, and simulate
  (google/riscv-dv#326) (Hai Hoang Dang)
* Add requirements for install dependencies (google/riscv-dv#325) (Hai
  Hoang Dang)
* Adding support qrun simulator (google/riscv-dv#324) (Hai Hoang Dang)
* Add new experimental instruction class (google/riscv-dv#323)
  (taoliug)
* Added command line control of coverage and added hooks for vector
  coverage development (google/riscv-dv#317) (simond-imperas)
* Fix compilation issue (google/riscv-dv#322) (taoliug)

Signed-off-by: Udi <udij@google.com>
This commit is contained in:
udinator 2019-12-16 11:47:53 -08:00 committed by GitHub
parent 8568e6b3b5
commit 0d6ccbf1f6
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
72 changed files with 5763 additions and 1403 deletions

View file

@ -9,6 +9,6 @@
upstream:
{
url: https://github.com/google/riscv-dv
rev: d69190682078470bc6d5661d72f873ae9850ae53
rev: 5b1dd4e2eb11d49d3275da80953efc0c50f90447
}
}

View file

@ -7,3 +7,7 @@ work/
/.project
/outdir/
ucli.key
qrun.out/
qrun.log
*.ucdb
*.vstf

View file

@ -23,6 +23,14 @@ A CSR test generation script written in Python is also provided, to generate a
directed test suite that stresses all CSR instructions on all of the CSRs that
the core implements.
## External contributions and collaborations
This repository is still under active development. We hope the RISC-V processor
verification platform development to be a collaborative effort of the RISC-V
community. Free feel to submit issues, feature requests, pull requests through
Github. You can also send your private collaboration request to [riscv_dv_dev@google.com](riscv_dv_dev@google.com).
Please refer to CONTRIBUTING.md for license related questions.
## Getting Started
### Prerequisites
@ -32,10 +40,10 @@ which supports SystemVerilog and UVM 1.2. This generator has been verified with
Synopsys VCS, Cadence Incisive/Xcelium, and Mentor Questa simulators. Please
make sure the EDA tool environment is properly setup before running the generator.
Install YAML python package:
Install dependencies needed for running:
```bash
pip3 install PyYAML
pip3 install -r requirements.txt
```
### Setup RISCV-GCC compiler toolchain
@ -101,8 +109,9 @@ python3 run.py --test riscv_arithmetic_basic_test --simulator ius
python3 run.py --test riscv_arithmetic_basic_test --simulator vcs
python3 run.py --test riscv_arithmetic_basic_test --simulator questa
python3 run.py --test riscv_arithmetic_basic_test --simulator dsim
python3 run.py --test riscv_arithmetic_basic_test --simulator qrun
```
The complete test list can be found in [yaml/testlist.yaml](https://github.com/google/riscv-dv/blob/master/yaml/testlist.yaml). To run a full
The complete test list can be found in [yaml/base_testlist.yaml](https://github.com/google/riscv-dv/blob/master/yaml/base_testlist.yaml). To run a full
regression, simply use below command
```bash
@ -121,6 +130,9 @@ Here's a few more examples of the run command:
# Run a single test 10 times
python3 run.py --test riscv_arithmetic_basic_test --iterations 10
# Run multiple tests
python3 run.py --test riscv_arithmetic_basic_test,riscv_rand_instr_test
# Run a test with verbose logging
python3 run.py --test riscv_arithmetic_basic_test --verbose
@ -373,16 +385,6 @@ matching rv32/rv64/rv128 entry and fill in the appropriate CSR field entries.
### Privileged CSR Test Generation (optional)
To be able to run the CSR generation script, the open-source `bitstring`
Python library is required ([bitstring](https://github.com/scott-griffiths/bitstring)).
To install this library, either clone the repository and run the `setup.py`
setup script, or run only one of the below commands:
```bash
sudo apt-get install python3-bitstring (or your OS-specific package manager)
pip install bitstring
```
The CSR generation script is located at
[scripts/gen_csr_test.py](https://github.com/google/riscv-dv/blob/master/scripts/gen_csr_test.py).
The CSR test code that this script generates will execute every CSR instruction
@ -446,9 +448,13 @@ upstream changes to a minimum.
different directory, you can use "-ext <user_extension_path>" to override the
user extension path.
- Create a new target directory and customize the setting and testlist
- Run the generator with "--custom_target <target_dir> --isa <isa> --mabi <mabi>"
- Run the generator with `--custom_target <target_dir> --isa <isa> --mabi <mabi>`
- Use command line type override to use your extended classes.
--sim_opts="+uvm_set_type_override=<upstream_class>,<extended_class>"
`--sim_opts="+uvm_set_type_override=<upstream_class>,<extended_class>"`
- If extending `riscv_asm_program_gen` class is desired, must use this command
line override:
`--sim_opts="+uvm_set_inst_override=riscv_asm_program_gen,<extended
class>,'uvm_test_top.asm_gen'"`
You can refer to [riscv-dv extension for ibex](https://github.com/lowRISC/ibex/blob/master/dv/uvm/Makefile#L68) for a working example.
@ -521,7 +527,7 @@ implmentation.
```bash
# Randomly generate 100000 instructions, split to 20000 instructions per batch
python3 cov.py -d -i 100000 -bz 20000
python3 cov.py -d -i 100000 -bz 20000 --isa rv32imc
```
@ -531,13 +537,6 @@ Please file an issue under this repository for any bug report / integration
issue / feature request. We are looking forward to knowing your experience of
using this flow and how we can make it better together.
## External contributions
We definitely welcome external contributions. We hope it could be a
collaborative effort to build a strong open source RISC-V processor
verification platform. Free feel to submit your pull request for review.
Please refer to CONTRIBUTING.md for license related questions.
## Disclaimer
This is not an officially supported Google product.

View file

@ -23,7 +23,6 @@ import re
import sys
import logging
from datetime import date
from scripts.lib import *
from scripts.spike_log_to_trace_csv import *
from scripts.ovpsim_log_to_trace_csv import *
@ -31,11 +30,13 @@ from scripts.sail_log_to_trace_csv import *
LOGGER = logging.getLogger()
def collect_cov(log_dir, out, core, iss, testlist, batch_size, lsf_cmd, steps, \
opts, timeout, simulator, simulator_yaml, custom_target, \
isa, target, stop_on_first_error):
isa, target, stop_on_first_error,
dont_truncate_after_first_ecall,
vector_options, coverage_options, compliance_mode):
"""Collect functional coverage from the instruction trace
Args:
log_dir : Trace log directory
out : Output directory
@ -53,6 +54,9 @@ def collect_cov(log_dir, out, core, iss, testlist, batch_size, lsf_cmd, steps, \
isa : RISC-V ISA variant
target : Predefined target
stop_on_first_error : will end run on first error detected
vector_options : Enable Vectors and set vector config options
coverage_options : Set coverage config options
compliance_mode : Run coverage model for compliance test
"""
cwd = os.path.dirname(os.path.realpath(__file__))
log_list = []
@ -87,28 +91,40 @@ def collect_cov(log_dir, out, core, iss, testlist, batch_size, lsf_cmd, steps, \
if iss == "spike":
process_spike_sim_log(log, csv, 1)
elif iss == "ovpsim":
process_ovpsim_sim_log(log, csv, 1, stop_on_first_error)
process_ovpsim_sim_log(log, csv, 1, stop_on_first_error,
dont_truncate_after_first_ecall)
else:
logging.error("Full trace for %s is not supported yet" % iss)
sys.exit(1)
sys.exit(RET_FAIL)
if steps == "all" or re.match("cov", steps):
opts_vec = ""
opts_cov = ""
if vector_options:
opts_vec = ("%0s" % vector_options)
if coverage_options:
opts_cov = ("%0s" % coverage_options)
if compliance_mode:
opts_cov += " +define+COMPLIANCE_MODE"
build_cmd = ("python3 %s/run.py --simulator %s --simulator_yaml %s "
" --co -o %s --cov -tl %s %s " %
(cwd, simulator, simulator_yaml, out, testlist, opts))
" --co -o %s --cov -tl %s %s --cmp_opts \"%s %s\" " %
(cwd, simulator, simulator_yaml, out, testlist, opts,
opts_vec, opts_cov))
base_sim_cmd = ("python3 %s/run.py --simulator %s --simulator_yaml %s "
"--so -o %s --cov -tl %s %s "
"-tn riscv_instr_cov_test --steps gen --sim_opts \"<trace_csv_opts>\"" %
(cwd, simulator, simulator_yaml, out, testlist, opts))
"-tn riscv_instr_cov_test --steps gen --sim_opts \"<trace_csv_opts> %s %s\" " %
(cwd, simulator, simulator_yaml, out, testlist, opts,
opts_vec, opts_cov))
if target:
build_cmd += (" --target %s" % target)
if custom_target:
build_cmd += (" --custom_target %s" % custom_target)
if target:
base_sim_cmd += (" --target %s" % target)
if custom_target:
build_cmd += (" --custom_target %s" % custom_target)
base_sim_cmd += (" --custom_target %s" % custom_target)
if stop_on_first_error:
build_cmd += (" --stop_on_first_error")
base_sim_cmd += (" --stop_on_first_error")
logging.info("Building the coverage collection framework")
output = run_cmd(build_cmd)
run_cmd(build_cmd)
file_idx = 0
trace_idx = 0
trace_csv_opts = ""
@ -119,12 +135,11 @@ def collect_cov(log_dir, out, core, iss, testlist, batch_size, lsf_cmd, steps, \
batch_cnt = (len(csv_list)+batch_size-1)/batch_size;
logging.info("Batch size: %0d, Batch cnt:%0d" % (batch_size, batch_cnt))
for i in range(len(csv_list)):
file_idx = 0
trace_idx = i
if batch_size > 0:
file_idx = i / batch_size;
trace_idx = i % batch_size;
else:
file_idx = 0
trace_idx = i
trace_csv_opts += (" +trace_csv_%0d=%s" % (trace_idx, csv_list[i]))
if ((i == len(csv_list)-1) or ((batch_size > 0) and (trace_idx == batch_size-1))):
sim_cmd = base_sim_cmd.replace("<trace_csv_opts>", trace_csv_opts)
@ -165,34 +180,30 @@ def run_cov_debug_test(out, instr_cnt, testlist, batch_size, opts, lsf_cmd,\
build_cmd = ("python3 %s/run.py --simulator %s --simulator_yaml %s "
"--co -o %s --cov -tl %s %s" %
(cwd, simulator, simulator_yaml, out, testlist, opts))
if target:
build_cmd += (" --target %s" % target)
if custom_target:
build_cmd += (" --custom_target %s" % custom_target)
run_cmd(build_cmd)
base_sim_cmd = ("python3 %s/run.py --simulator %s --simulator_yaml %s "
"--so -o %s --cov -tl %s --isa %s%s "
"--so -o %s --cov -tl %s --isa %s %s "
"-tn riscv_instr_cov_debug_test --steps gen "
"--sim_opts \"+num_of_iterations=<instr_cnt>\"" %
(cwd, simulator, simulator_yaml, out, testlist, isa, opts))
if target:
build_cmd += (" --target %s" % target)
base_sim_cmd += (" --target %s" % target)
if custom_target:
build_cmd += (" --custom_target %s" % custom_target)
base_sim_cmd += (" --custom_target %s" % custom_target)
logging.info("Building the coverage collection framework")
run_cmd(build_cmd)
batch_cnt = 1
if batch_size > 0:
batch_cnt = int((instr_cnt+batch_size-1)/batch_size)
logging.info("Batch size: %0d, Batch cnt:%0d" % (batch_size, batch_cnt))
else:
batch_cnt = 1
logging.info("Randomizing %0d instructions in %0d batches", instr_cnt, batch_cnt)
for i in range(batch_cnt):
batch_instr_cnt = instr_cnt
if batch_size > 0:
batch_instr_cnt = batch_size
if i == batch_cnt - 1:
batch_instr_cnt = instr_cnt - batch_size * (batch_cnt - 1)
else:
batch_instr_cnt = batch_size
else:
batch_instr_cnt = instr_cnt
sim_cmd = base_sim_cmd.replace("<instr_cnt>", str(batch_instr_cnt))
sim_cmd += (" --log_suffix _%d" % i)
if lsf_cmd == "":
@ -223,6 +234,8 @@ def setup_parser():
help="Number of CSV to process per run")
parser.add_argument("-d", "--debug_mode", dest="debug_mode", action="store_true",
help="Debug mode, randomize and sample the coverage directly")
parser.add_argument("--compliance_mode", action="store_true",
help="Run the coverage model in compliance test mode")
parser.add_argument("-i", "--instr_cnt", dest="instr_cnt", type=int, default=0,
help="Random instruction count for debug mode")
parser.add_argument("-to", "--timeout", dest="timeout", type=int, default=1000,
@ -256,11 +269,21 @@ def setup_parser():
help="Path for the riscv_core_setting.sv")
parser.add_argument("--stop_on_first_error", dest="stop_on_first_error",
action="store_true", help="Stop on detecting first error")
parser.set_defaults(verbose=False)
parser.set_defaults(debug_mode=False)
parser.set_defaults(stop_on_first_error=False)
parser.add_argument("--dont_truncate_after_first_ecall", dest="dont_truncate_after_first_ecall",
action="store_true", help="Do not truncate log and csv file on first ecall")
parser.add_argument("--noclean", action="store_true",
help="Do not clean the output of the previous runs")
parser.add_argument("--vector_options", type=str, default="",
help="Enable Vectors and set options")
parser.add_argument("--coverage_options", type=str, default="",
help="Controlling coverage coverpoints")
parser.set_defaults(verbose=False)
parser.set_defaults(debug_mode=False)
parser.set_defaults(compliance_mode=False)
parser.set_defaults(stop_on_first_error=False)
parser.set_defaults(dont_truncate_after_first_ecall=False)
parser.set_defaults(vector_options="")
parser.set_defaults(coverage_options="")
return parser
def main():
@ -273,9 +296,6 @@ def main():
if args.verbose:
args.opts += "-v"
if not args.testlist:
args.testlist = cwd + "/yaml/cov_testlist.yaml"
if not args.simulator_yaml:
args.simulator_yaml = cwd + "/yaml/simulator.yaml"
@ -301,14 +321,12 @@ def main():
args.testlist = cwd + "/yaml/cov_testlist.yaml" ## needed if need to force
# Create output directory
if args.o is None:
output_dir = "cov_out_" + str(date.today())
else:
output_dir = args.o
output_dir = create_output(args.o, "cov_out_")
if args.noclean is False:
os.system("rm -rf %s" % output_dir)
logging.info("Creating output directory: %s" % output_dir)
subprocess.run(["mkdir", "-p", output_dir])
if args.debug_mode:
@ -320,7 +338,11 @@ def main():
collect_cov(args.dir, output_dir, args.core, args.iss, args.testlist,
args.batch_size, args.lsf_cmd, args.steps, args.opts, args.timeout,
args.simulator, args.simulator_yaml, args.custom_target,
args.isa, args.target, args.stop_on_first_error)
args.isa, args.target, args.stop_on_first_error,
args.dont_truncate_after_first_ecall,
args.vector_options,
args.coverage_options,
args.compliance_mode)
logging.info("Coverage results are saved to %s" % output_dir)
if __name__ == "__main__":

10
vendor/google_riscv-dv/qrun_option.f vendored Normal file
View file

@ -0,0 +1,10 @@
-64
-uvmhome uvm-1.2
-sv
-mfcu
-cuname design_cuname
+define+UVM_REGEX_NO_DPI
-debug
+designfile
-o design_opt
-optimize

View file

@ -0,0 +1,2 @@
PyYAML
bitstring

View file

@ -23,7 +23,6 @@ import re
import sys
import logging
from datetime import date
from scripts.lib import *
from scripts.spike_log_to_trace_csv import *
from scripts.ovpsim_log_to_trace_csv import *
@ -33,13 +32,14 @@ from scripts.instr_trace_compare import *
LOGGER = logging.getLogger()
def get_generator_cmd(simulator, simulator_yaml, cov):
def get_generator_cmd(simulator, simulator_yaml, cov, exp):
""" Setup the compile and simulation command for the generator
Args:
simulator : RTL simulator used to run instruction generator
simulator_yaml : RTL simulator configuration file in YAML format
cov : Enable functional coverage
exp : Use experimental version
Returns:
compile_cmd : RTL simulator command to compile the instruction generator
@ -58,6 +58,8 @@ def get_generator_cmd(simulator, simulator_yaml, cov):
compile_cmd[i] = re.sub('<cov_opts>', compile_spec['cov_opts'].rstrip(), compile_cmd[i])
else:
compile_cmd[i] = re.sub('<cov_opts>', '', compile_cmd[i])
if exp:
compile_cmd[i] += " +define+EXPERIMENTAL "
sim_cmd = entry['sim']['cmd']
if ('cov_opts' in entry['sim']) and cov:
sim_cmd = re.sub('<cov_opts>', entry['sim']['cov_opts'].rstrip(), sim_cmd)
@ -70,7 +72,7 @@ def get_generator_cmd(simulator, simulator_yaml, cov):
sim_cmd = re.sub("<"+env_var+">", get_env_var(env_var), sim_cmd)
return compile_cmd, sim_cmd
logging.error("Cannot find RTL simulator %0s" % simulator)
sys.exit(1)
sys.exit(RET_FAIL)
def parse_iss_yaml(iss, iss_yaml, isa, setting_dir):
@ -87,7 +89,6 @@ def parse_iss_yaml(iss, iss_yaml, isa, setting_dir):
"""
logging.info("Processing ISS setup file : %s" % iss_yaml)
yaml_data = read_yaml(iss_yaml)
cwd = os.path.dirname(os.path.realpath(__file__))
# Search for matched ISS
for entry in yaml_data:
if entry['iss'] == iss:
@ -110,7 +111,7 @@ def parse_iss_yaml(iss, iss_yaml, isa, setting_dir):
cmd = re.sub("\<variant\>", isa, cmd)
return cmd
logging.error("Cannot find ISS %0s" % iss)
sys.exit(1)
sys.exit(RET_FAIL)
def get_iss_cmd(base_cmd, elf, log):
@ -128,17 +129,142 @@ def get_iss_cmd(base_cmd, elf, log):
cmd += (" &> %s" % log)
return cmd
def do_compile(compile_cmd, test_list, core_setting_dir, cwd, ext_dir, cmp_opts, output_dir):
"""Compile the instruction generator
Args:
compile_cmd : Compile command for the generator
test_list : List of assembly programs to be compiled
core_setting_dir : Path for riscv_core_setting.sv
cwd : Filesystem path to RISCV-DV repo
ext_dir : User extension directory
cmd_opts : Compile options for the generator
output_dir : Output directory of the ELF files
"""
if (not((len(test_list) == 1) and (test_list[0]['test'] == 'riscv_csr_test'))):
logging.info("Building RISC-V instruction generator")
for cmd in compile_cmd:
cmd = re.sub("<out>", os.path.abspath(output_dir), cmd)
cmd = re.sub("<setting>", core_setting_dir, cmd)
if ext_dir == "":
cmd = re.sub("<user_extension>", "<cwd>/user_extension", cmd)
else:
cmd = re.sub("<user_extension>", ext_dir, cmd)
cmd = re.sub("<cwd>", cwd, cmd)
cmd = re.sub("<cmp_opts>", cmp_opts, cmd)
logging.debug("Compile command: %s" % cmd)
run_cmd(cmd)
def run_csr_test(cmd_list, cwd, csr_file, isa, iterations, lsf_cmd,
end_signature_addr, timeout_s, output_dir):
"""Run CSR test
It calls a separate python script to generate directed CSR test code,
located at scripts/gen_csr_test.py.
"""
cmd = "python3 " + cwd + "/scripts/gen_csr_test.py" + \
(" --csr_file %s" % csr_file) + \
(" --xlen %s" % re.search(r"(?P<xlen>[0-9]+)", isa).group("xlen")) + \
(" --iterations %i" % iterations) + \
(" --out %s/asm_tests" % output_dir) + \
(" --end_signature_addr %s" % end_signature_addr)
if lsf_cmd:
cmd_list.append(cmd)
else:
run_cmd(cmd, timeout_s)
def do_simulate(sim_cmd, test_list, cwd, sim_opts, seed_yaml, seed, csr_file,
isa, end_signature_addr, lsf_cmd, timeout_s, log_suffix,
batch_size, output_dir, verbose, check_return_code):
"""Run the instruction generator
Args:
sim_cmd : Simulate command for the generator
test_list : List of assembly programs to be compiled
cwd : Filesystem path to RISCV-DV repo
sim_opts : Simulation options for the generator
seed_yaml : Seed specification from a prior regression
seed : Seed to the instruction generator
csr_file : YAML file containing description of all CSRs
isa : Processor supported ISA subset
end_signature_addr : Address that tests will write pass/fail signature to at end of test
lsf_cmd : LSF command used to run the instruction generator
timeout_s : Timeout limit in seconds
log_suffix : Simulation log file name suffix
batch_size : Number of tests to generate per run
output_dir : Output directory of the ELF files
check_return_code : Check return code of the command
"""
cmd_list = []
sim_cmd = re.sub("<out>", os.path.abspath(output_dir), sim_cmd)
sim_cmd = re.sub("<cwd>", cwd, sim_cmd)
sim_cmd = re.sub("<sim_opts>", sim_opts, sim_cmd)
rerun_seed = {}
if seed_yaml:
rerun_seed = read_yaml(seed_yaml)
logging.info("Running RISC-V instruction generator")
sim_seed = {}
for test in test_list:
iterations = test['iterations']
logging.info("Generating %d %s" % (iterations, test['test']))
if iterations > 0:
# Running a CSR test
if test['test'] == 'riscv_csr_test':
run_csr_test(cmd_list, cwd, csr_file, isa, iterations, lsf_cmd,
end_signature_addr, timeout_s, output_dir)
else:
batch_cnt = 1
if batch_size > 0:
batch_cnt = int((iterations + batch_size - 1) / batch_size);
logging.info("Running %s with %0d batches" % (test['test'], batch_cnt))
for i in range(0, batch_cnt):
test_id = '%0s_%0d' % (test['test'], i)
if test_id in rerun_seed:
rand_seed = rerun_seed[test_id]
else:
rand_seed = get_seed(seed)
if i < batch_cnt - 1:
test_cnt = batch_size
else:
test_cnt = iterations - i * batch_size;
cmd = lsf_cmd + " " + sim_cmd.rstrip() + \
(" +UVM_TESTNAME=%s " % test['gen_test']) + \
(" +num_of_tests=%i " % test_cnt) + \
(" +start_idx=%d " % (i*batch_size)) + \
(" +asm_file_name=%s/asm_tests/%s " % (output_dir, test['test'])) + \
(" -l %s/sim_%s_%d%s.log " % (output_dir, test['test'], i, log_suffix))
if verbose:
cmd += "+UVM_VERBOSITY=UVM_HIGH "
cmd = re.sub("<seed>", str(rand_seed), cmd)
sim_seed[test_id] = str(rand_seed)
if "gen_opts" in test:
cmd += test['gen_opts']
if not re.search("c", isa):
cmd += "+disable_compressed_instr=1 ";
if lsf_cmd:
cmd_list.append(cmd)
else:
logging.info("Running %s, batch %0d/%0d, test_cnt:%0d" %
(test['test'], i+1, batch_cnt, test_cnt))
run_cmd(cmd, timeout_s, check_return_code = check_return_code)
if sim_seed:
with open(('%s/seed.yaml' % os.path.abspath(output_dir)) , 'w') as outfile:
yaml.dump(sim_seed, outfile, default_flow_style=False)
if lsf_cmd:
run_parallel_cmd(cmd_list, timeout_s, check_return_code = check_return_code)
def gen(test_list, csr_file, end_signature_addr, isa, simulator,
simulator_yaml, output_dir, sim_only, compile_only, lsf_cmd, seed,
cwd, cmp_opts, sim_opts, timeout_s, core_setting_dir, ext_dir, cov,
log_suffix, batch_size, seed_yaml, stop_on_first_error, verbose=False):
log_suffix, batch_size, seed_yaml, verbose, exp):
"""Run the instruction generator
Args:
test_list : List of assembly programs to be compiled
csr_file : YAML file containing description of all CSRs
end_signature_addr : Address that tests will write pass/fail signature to at end of test
end_signature_addr : Address that tests will write pass/fail signature to
isa : Processor supported ISA subset
simulator : RTL simulator used to run instruction generator
simulator_yaml : RTL simulator configuration file in YAML format
@ -157,105 +283,30 @@ def gen(test_list, csr_file, end_signature_addr, isa, simulator,
log_suffix : Simulation log file name suffix
batch_size : Number of tests to generate per run
seed_yaml : Seed specification from a prior regression
stop_on_first_error : will end run on first error detected
exp : Enable experimental features
"""
check_return_code = True
if simulator == "ius":
# Incisive return non-zero return code even test passes
check_return_code = False
logging.debug("Disable return_code checking for %s" % simulator)
# Mutually exclusive options between compile_only and sim_only
if compile_only and sim_only:
logging.error("argument -co is not allowed with argument -so")
if ((compile_only == 0) and (len(test_list) == 0)):
return
# Setup the compile and simulation command for the generator
compile_cmd = []
sim_cmd = ""
compile_cmd, sim_cmd = get_generator_cmd(simulator, simulator_yaml, cov);
if ((compile_only == 0) and (len(test_list) == 0)):
return
compile_cmd, sim_cmd = get_generator_cmd(simulator, simulator_yaml, cov, exp);
# Compile the instruction generator
if not sim_only:
if (not((len(test_list) == 1) and (test_list[0]['test'] == 'riscv_csr_test'))):
logging.info("Building RISC-V instruction generator")
for cmd in compile_cmd:
cmd = re.sub("<out>", os.path.abspath(output_dir), cmd)
cmd = re.sub("<setting>", core_setting_dir, cmd)
if ext_dir == "":
cmd = re.sub("<user_extension>", "<cwd>/user_extension", cmd)
else:
cmd = re.sub("<user_extension>", ext_dir, cmd)
cmd = re.sub("<cwd>", cwd, cmd)
cmd = re.sub("<cmp_opts>", cmp_opts, cmd)
logging.debug("Compile command: %s" % cmd)
output = run_cmd(cmd)
do_compile(compile_cmd, test_list, core_setting_dir, cwd, ext_dir, cmp_opts, output_dir)
# Run the instruction generator
if not compile_only:
cmd_list = []
sim_cmd = re.sub("<out>", os.path.abspath(output_dir), sim_cmd)
sim_cmd = re.sub("<cwd>", cwd, sim_cmd)
sim_cmd = re.sub("<sim_opts>", sim_opts, sim_cmd)
if seed_yaml:
rerun_seed = read_yaml(seed_yaml)
else:
rerun_seed = {}
logging.info("Running RISC-V instruction generator")
sim_seed = {}
for test in test_list:
iterations = test['iterations']
logging.info("Generating %d %s" % (iterations, test['test']))
if iterations > 0:
"""
If we are running a CSR test, need to call a separate python script
to generate directed CSR test code, located at scripts/gen_csr_test.py.
"""
if test['test'] == 'riscv_csr_test':
cmd = "python3 " + cwd + "/scripts/gen_csr_test.py" + \
(" --csr_file %s" % csr_file) + \
(" --xlen %s" % re.search(r"(?P<xlen>[0-9]+)", isa).group("xlen")) + \
(" --iterations %i" % iterations) + \
(" --out %s/asm_tests" % output_dir) + \
(" --end_signature_addr %s" % end_signature_addr)
if lsf_cmd:
cmd_list.append(cmd)
else:
output = run_cmd(cmd, timeout_s)
else:
if batch_size > 0:
batch_cnt = int((iterations + batch_size - 1) / batch_size);
else:
batch_cnt = 1
logging.info("Running %s with %0d batches" % (test['test'], batch_cnt))
for i in range(0, batch_cnt):
test_id = '%0s_%0d' % (test['test'], i)
if test_id in rerun_seed:
rand_seed = rerun_seed[test_id]
else:
rand_seed = get_seed(seed)
if i < batch_cnt - 1:
test_cnt = batch_size
else:
test_cnt = iterations - i * batch_size;
cmd = lsf_cmd + " " + sim_cmd.rstrip() + \
(" +UVM_TESTNAME=%s " % test['gen_test']) + \
(" +num_of_tests=%i " % test_cnt) + \
(" +start_idx=%d " % (i*batch_size)) + \
(" +asm_file_name=%s/asm_tests/%s " % (output_dir, test['test'])) + \
(" -l %s/sim_%s_%d%s.log " % (output_dir, test['test'], i, log_suffix))
if verbose:
cmd += "+UVM_VERBOSITY=UVM_HIGH "
cmd = re.sub("<seed>", str(rand_seed), cmd)
sim_seed[test_id] = str(rand_seed)
if "gen_opts" in test:
cmd += test['gen_opts']
if not re.search("c", isa):
cmd += "+disable_compressed_instr=1 ";
if lsf_cmd:
cmd_list.append(cmd)
else:
logging.info("Running %s, batch %0d/%0d, test_cnt:%0d" %
(test['test'], i+1, batch_cnt, test_cnt))
output = run_cmd(cmd, timeout_s)
if sim_seed:
with open(('%s/seed.yaml' % os.path.abspath(output_dir)) , 'w') as outfile:
yaml.dump(sim_seed, outfile, default_flow_style=False)
if lsf_cmd:
run_parallel_cmd(cmd_list, timeout_s)
do_simulate(sim_cmd, test_list, cwd, sim_opts, seed_yaml, seed, csr_file,
isa, end_signature_addr, lsf_cmd, timeout_s, log_suffix,
batch_size, output_dir, verbose, check_return_code)
def gcc_compile(test_list, output_dir, isa, mabi, opts):
@ -377,14 +428,13 @@ def iss_sim(test_list, output_dir, iss_list, iss_yaml, isa, setting_dir, timeout
logging.debug(cmd)
def iss_cmp(test_list, iss, output_dir, isa, stop_on_first_error):
def iss_cmp(test_list, iss, output_dir, stop_on_first_error):
"""Compare ISS simulation reult
Args:
test_list : List of assembly programs to be compiled
iss : List of instruction set simulators
output_dir : Output directory of the ELF files
isa : ISA
stop_on_first_error : will end run on first error detected
"""
iss_list = iss.split(",")
@ -406,14 +456,14 @@ def iss_cmp(test_list, iss, output_dir, isa, stop_on_first_error):
if iss == "spike":
process_spike_sim_log(log, csv)
elif iss == "ovpsim":
process_ovpsim_sim_log(log, csv, 1, stop_on_first_error)
process_ovpsim_sim_log(log, csv, 0, stop_on_first_error)
elif iss == "sail":
process_sail_sim_log(log, csv)
elif iss == "whisper":
process_whisper_sim_log(log, csv)
else:
logging.error("Unsupported ISS" % iss)
sys.exit(1)
sys.exit(RET_FAIL)
compare_trace_csv(csv_list[0], csv_list[1], iss_list[0], iss_list[1], report)
passed_cnt = run_cmd("grep PASSED %s | wc -l" % report).strip()
failed_cnt = run_cmd("grep FAILED %s | wc -l" % report).strip()
@ -496,6 +546,8 @@ def setup_parser():
help="Directed assembly test")
parser.add_argument("--log_suffix", type=str, default="",
help="Simulation log name suffix")
parser.add_argument("--exp", action="store_true",
help="Run generator with experimental features")
parser.add_argument("-bz", "--batch_size", type=int, default=0,
help="Number of tests to generate per run. You can split a big"
" job to small batches with this option")
@ -505,6 +557,7 @@ def setup_parser():
parser.set_defaults(so=False)
parser.set_defaults(verbose=False)
parser.set_defaults(cov=False)
parser.set_defaults(exp=False)
parser.set_defaults(stop_on_first_error=False)
return parser
@ -553,8 +606,11 @@ def main():
elif args.target == "ml":
args.mabi = "lp64"
args.isa = "rv64imc"
elif args.target == "exp":
args.mabi = "lp64"
args.isa = "rv64gc"
else:
print ("Unsupported pre-defined target: %0s" % args.target)
sys.exit("Unsupported pre-defined target: %0s" % args.target)
else:
if re.match(".*gcc_compile.*", args.steps) or re.match(".*iss_sim.*", args.steps):
if (not args.mabi) or (not args.isa):
@ -567,11 +623,8 @@ def main():
return
# Create output directory
if args.o is None:
output_dir = "out_" + str(date.today())
else:
output_dir = args.o
output_dir = create_output(args.o)
logging.info("Creating output directory: %s" % output_dir)
subprocess.run(["mkdir", "-p", output_dir])
subprocess.run(["mkdir", "-p", ("%s/asm_tests" % output_dir)])
@ -590,7 +643,7 @@ def main():
args.co, args.lsf_cmd, args.seed, cwd, args.cmp_opts,
args.sim_opts, args.gen_timeout, args.core_setting_dir,
args.user_extension_dir, args.cov, args.log_suffix, args.batch_size,
args.seed_yaml, args.stop_on_first_error, args.verbose)
args.seed_yaml, args.verbose, args.exp)
if not args.co:
# Compile the assembly program to ELF, convert to plain binary
@ -604,7 +657,7 @@ def main():
# Compare ISS simulation result
if args.steps == "all" or re.match(".*iss_cmp.*", args.steps):
iss_cmp(matched_list, args.iss, output_dir, args.isa, args.stop_on_first_error)
iss_cmp(matched_list, args.iss, output_dir, args.stop_on_first_error)
if __name__ == "__main__":
main()

View file

@ -32,12 +32,13 @@ import yaml
import argparse
import random
import copy
from lib import *
try:
from bitstring import BitArray as bitarray
except ImportError as e:
logging.error("Please install bitstring package: sudo apt-get install python3-bitstring")
sys.exit(1)
sys.exit(RET_FAIL)
"""
Defines the test's success/failure values, one of which will be written to
@ -176,25 +177,20 @@ def predict_csr_val(csr_op, rs1_val, csr_val, csr_write_mask, csr_read_mask):
prediction = None
# create a zero bitarray to zero extend immediates
zero = bitarray(uint=0, length=csr_val.len - 5)
prediction = csr_read(csr_val, csr_read_mask)
if csr_op == 'csrrw':
prediction = csr_read(csr_val, csr_read_mask)
csr_write(rs1_val, csr_val, csr_write_mask)
elif csr_op == 'csrrs':
prediction = csr_read(csr_val, csr_read_mask)
csr_write(rs1_val | prediction, csr_val, csr_write_mask)
elif csr_op == 'csrrc':
prediction = csr_read(csr_val, csr_read_mask)
csr_write((~rs1_val) & prediction, csr_val, csr_write_mask)
elif csr_op == 'csrrwi':
prediction = csr_read(csr_val, csr_read_mask)
zero.append(rs1_val[-5:])
csr_write(zero, csr_val, csr_write_mask)
elif csr_op == 'csrrsi':
prediction = csr_read(csr_val, csr_read_mask)
zero.append(rs1_val[-5:])
csr_write(zero | prediction, csr_val, csr_write_mask)
elif csr_op == 'csrrci':
prediction = csr_read(csr_val, csr_read_mask)
zero.append(rs1_val[-5:])
csr_write((~zero) & prediction, csr_val, csr_write_mask)
return f"0x{prediction.hex}"

View file

@ -25,6 +25,12 @@ import time
import yaml
import logging
from datetime import date
RET_SUCCESS = 0
RET_FAIL = 1
RET_FATAL = -1
def setup_logging(verbose):
"""Setup the root logger.
@ -55,7 +61,7 @@ def read_yaml(yaml_file):
yaml_data = yaml.safe_load(f)
except yaml.YAMLError as exc:
logging.error(exc)
sys.exit(1)
sys.exit(RET_FAIL)
return yaml_data
@ -72,7 +78,7 @@ def get_env_var(var):
val = os.environ[var]
except KeyError:
logging.warning("Please set the environment variable %0s" % var)
sys.exit(1)
sys.exit(RET_FAIL)
return val
@ -87,11 +93,10 @@ def get_seed(seed):
"""
if seed >= 0:
return seed
else:
return random.getrandbits(32)
return random.getrandbits(32)
def run_cmd(cmd, timeout_s = 999, exit_on_error = 1):
def run_cmd(cmd, timeout_s = 999, exit_on_error = 1, check_return_code = True):
"""Run a command and return output
Args:
@ -108,9 +113,9 @@ def run_cmd(cmd, timeout_s = 999, exit_on_error = 1):
universal_newlines=True,
stdout=subprocess.PIPE,
stderr=subprocess.STDOUT)
except subprocess.CalledProcessError as exc:
except subprocess.CalledProcessError:
logging.error(ps.communicate()[0])
sys.exit(1)
sys.exit(RET_FAIL)
try:
output = ps.communicate(timeout = timeout_s)[0]
except subprocess.TimeoutExpired:
@ -118,17 +123,16 @@ def run_cmd(cmd, timeout_s = 999, exit_on_error = 1):
output = ""
ps.kill()
rc = ps.returncode
if rc:
if rc > 0:
logging.info(output)
logging.error("ERROR return code: %d, cmd:%s" % (rc, cmd))
if exit_on_error:
sys.exit(1)
if rc and check_return_code and rc > 0:
logging.info(output)
logging.error("ERROR return code: %d/%d, cmd:%s" % (check_return_code, rc, cmd))
if exit_on_error:
sys.exit(RET_FAIL)
logging.debug(output)
return output
def run_parallel_cmd(cmd_list, timeout_s = 999, exit_on_error = 0):
def run_parallel_cmd(cmd_list, timeout_s = 999, exit_on_error = 0, check_return_code = True):
"""Run a list of commands in parallel
Args:
@ -155,12 +159,11 @@ def run_parallel_cmd(cmd_list, timeout_s = 999, exit_on_error = 0):
logging.error("Timeout[%ds]: %s" % (timeout_s, cmd))
children[i].kill()
rc = children[i].returncode
if rc:
if rc > 0:
logging.info(output)
logging.error("ERROR return code: %d, cmd:%s" % (rc, cmd))
if exit_on_error:
sys.exit(1)
if rc and check_return_code and rc > 0:
logging.info(output)
logging.error("ERROR return code: %d, cmd:%s" % (rc, cmd))
if exit_on_error:
sys.exit(RET_FAIL)
# Restore stty setting otherwise the terminal may go crazy
os.system("stty sane")
logging.debug(output)
@ -180,15 +183,30 @@ def process_regression_list(testlist, test, iterations, matched_list, riscv_dv_r
"""
logging.info("Processing regression test list : %s, test: %s" % (testlist, test))
yaml_data = read_yaml(testlist)
mult_test = test.split(',')
for entry in yaml_data:
if 'import' in entry:
sub_list = re.sub('<riscv_dv_root>', riscv_dv_root, entry['import'])
process_regression_list(sub_list, test, iterations, matched_list, riscv_dv_root)
else:
if (entry['test'] == test) or (test == "all"):
if (entry['test'] in mult_test) or (test == "all"):
if (iterations > 0 and entry['iterations'] > 0):
entry['iterations'] = iterations
if entry['iterations'] > 0:
logging.info("Found matched tests: %s, iterations:%0d" %
(entry['test'], entry['iterations']))
matched_list.append(entry)
def create_output(output, prefix = "out_"):
""" Create output directory
Args:
output : Name of specified output directory
Returns:
Output directory
"""
# Create output directory
if output is None:
return prefix + str(date.today())
return output

View file

@ -27,46 +27,54 @@ from lib import *
from riscv_trace_csv import *
try:
from ovpsim_log_to_trace_csv_vectors import *
from ovpsim_log_to_trace_csv_vectors import *
except:
def assign_operand_vector(a,b,c,d):
""" stub version when no vector processing included """
logging.info("No OVPsim vector instruction processing included")
if stop_on_first_error:
sys.exit(-1)
def assign_operand_vector(a,b,c,d):
""" stub version when no vector processing included """
logging.info("No OVPsim vector instruction processing included")
if stop_on_first_error:
sys.exit(RET_FATAL)
def is_an_extension_instruction(instr):
if instr and 'v' in instr[0]:
return True
return False
stop_on_first_error = 0
def fatal (s):
""" ensure we end if a problem """
logging.fatal("ERROR: "+s)
sys.exit(-1)
""" ensure we end if a problem """
logging.fatal("ERROR: "+s)
sys.exit(RET_FATAL)
def convert_mode(pri, line):
""" OVPsim uses text string, convert to numeric """
if "Machine" in pri: return str(3)
if "Supervisor" in pri: return str(1)
if "User" in pri: return str(0)
logging.error("convert_mode = UNKNOWN PRIV MODE [%s]: %s" % (pri, line))
if stop_on_first_error:
sys.exit(-1)
""" OVPsim uses text string, convert to numeric """
if "Machine" in pri: return str(3)
if "Supervisor" in pri: return str(1)
if "User" in pri: return str(0)
logging.error("convert_mode = UNKNOWN PRIV MODE [%s]: %s" % (pri, line))
if stop_on_first_error:
sys.exit(RET_FATAL)
REGS = ["zero","ra","sp","gp","tp","t0","t1","t2","s0","s1",
"a0","a1","a2","a3","a4","a5","a6","a7",
"s2","s3","s4","s5","s6","s7","s8","s9","s10","s11",
"t3","t4","t5","t6"]
FREGS = ["ft0","ft1","ft2","ft3","ft4","ft5","ft6","ft7","fs0","fs1","fa0",
"fa1","fa2","fa3","fa4","fa5","fa6","fa7","fs2","fs3","fs4","fs5",
"fs6","fs7","fs8","fs9","fs10","fs11","ft8","ft9","ft10","ft11"]
def process_jal(trace, operands, gpr):
""" correctly process jal """
# TODO need to merge with jalr
## jal rd, imm
if len(operands) == 2:
trace.rd = operands[0]
trace.rd_val = gpr[trace.rd]
trace.imm = get_imm_hex_val("0x" + operands[1])
else:
fatal("process_jal(%s) wrong num operands (%d)" %
(trace.instr, len(operands)))
""" correctly process jal """
# TODO need to merge with jalr
## jal rd, imm
if len(operands) == 2:
trace.rd = operands[0]
trace.rd_val = gpr[trace.rd]
trace.imm = get_imm_hex_val("0x" + operands[1])
else:
fatal("process_jal(%s) wrong num operands (%d)" %
(trace.instr, len(operands)))
def process_jalr(trace, operands, gpr):
""" process jalr """
@ -134,64 +142,69 @@ pseudos={
}
def check_conversion(entry):
""" after conversion check that the entry was converted correctly """
instr_str_0 =entry.instr_str.split(" ")[0]
instr =entry.instr.split(" ")[0]
if "c." in instr[0:2]:
instr = instr[2:]
if instr in instr_str_0:
return # same
#logging.debug("converted pseudo %10s -> %s" % (instr_str_0, instr))
if instr_str_0 in pseudos:
p_instr = pseudos[instr_str_0]
if p_instr in instr:
return # is pseudo, converted ok
logging.error(
"converted %10s -> %s <<-- not correct pseudo (%s)" %
(instr_str_0, instr, p_instr))
if stop_on_first_error:
sys.exit(-1)
logging.error("converted %10s -> %s <<-- not correct at all" %
(instr_str_0, instr))
""" after conversion check that the entry was converted correctly """
instr_str_0 =entry.instr_str.split(" ")[0]
instr =entry.instr.split(" ")[0]
if "c." in instr[0:2]:
instr = instr[2:]
if instr in instr_str_0:
return # same
#logging.debug("converted pseudo %10s -> %s" % (instr_str_0, instr))
if instr_str_0 in pseudos:
p_instr = pseudos[instr_str_0]
if p_instr in instr:
return # is pseudo, converted ok
logging.error(
"converted %10s -> %s <<-- not correct pseudo (%s)" %
(instr_str_0, instr, p_instr))
if stop_on_first_error:
sys.exit(-1)
sys.exit(RET_FATAL)
logging.error("converted %10s -> %s <<-- not correct at all" %
(instr_str_0, instr))
if stop_on_first_error:
sys.exit(RET_FATAL)
operands_list = ["rd","rs1","rs2","vd","vs1","vs2","vs3","fd","fs1","fs2"]
def update_operands_values(trace, gpr):
""" ensure operands have been updated """
for op in operands_list:
exec("if trace.%0s in gpr: trace.%0s_val = gpr[trace.%0s]" % (op, op, op))
def show_line_instr(line, i):
""" show line """
if i.instr_str[0] in ['v']:
logging.debug("%s" % (line.strip()))
logging.debug(
" -->> instr_str(%s) binary(%s) addr(%s) mode(%s) instr(%s)"
% ( i.instr_str, i.binary, i.addr, i.privileged_mode,
i.instr))
""" show line """
if is_an_extension_instruction(i.instr):
logging.debug("%s" % (line.strip()))
logging.debug(
" -->> instr_str(%s) binary(%s) addr(%s) mode(%s) instr(%s)"
% ( i.instr_str, i.binary, i.addr, i.privileged_mode,i.instr))
def check_num_operands(instr_str, num_operands, n):
""" ensure consistency """
if n != num_operands:
fatal("%s: num operands wrong, expected (%d) got (%d)" % (instr_str,
n, num_operands))
""" ensure consistency """
if n != num_operands:
fatal("%s: num operands wrong, expected (%d) got (%d)" % (instr_str,
n, num_operands))
def is_csr(r):
""" see if r is a csr """
# add more as needed
if r in ["mtvec","pmpaddr0","pmpcfg0","mstatus","mepc","mscratch","mcause",
"mtval","vl","vtype"]:
return True
else:
return False
""" see if r is a csr """
# TODO add more as needed - could look in the enum privileged_reg_t or the cores settings: implemented_csr[]
if r in ["mtvec","pmpaddr0","pmpcfg0","mstatus","mepc","mscratch","mcause",
"mtval","vl","vtype"]:
return True
else:
return False
def process_branch_offset (opn, operands, prev_trace):
""" convert from ovpsim logs branch offsets as absolute to relative """
addr = operands[opn]
pc = prev_trace.addr
offset_dec = int(addr, 16) - int(pc, 16)
offset = hex(offset_dec)
operands[opn] = offset
""" convert from ovpsim logs branch offsets as absolute to relative """
addr = operands[opn]
pc = prev_trace.addr
offset_dec = int(addr, 16) - int(pc, 16)
offset = hex(offset_dec)
operands[opn] = offset
def process_ovpsim_sim_log(ovpsim_log, csv, full_trace = 1, stop = 0,
dont_truncate_after_first_ecall = 0,
verbose2 = False):
dont_truncate_after_first_ecall = 0,
verbose2 = False):
"""Process OVPsim simulation log.
Extract instruction and affected register information from ovpsim simulation
@ -200,10 +213,12 @@ def process_ovpsim_sim_log(ovpsim_log, csv, full_trace = 1, stop = 0,
stop_on_first_error = stop
logging.info("Processing ovpsim log [%s %s %s]: %s" %
("full_trace" if full_trace else "", "stop_on_first_error" if stop else "",
"dont_truncate_after_first_ecall" if dont_truncate_after_first_ecall else
"", ovpsim_log))
logging.info("Processing ovpsim log : %s" % ovpsim_log)
logging.debug("Flags [%s %s %s]" %
("full_trace" if full_trace else "",
"stop_on_first_error" if stop else "",
"dont_truncate_after_first_ecall" if dont_truncate_after_first_ecall else ""))
# Remove the header part of ovpsim log
cmd = ("sed -i '/Info 1:/,$!d' %s" % ovpsim_log)
@ -217,14 +232,16 @@ def process_ovpsim_sim_log(ovpsim_log, csv, full_trace = 1, stop = 0,
os.system(cmd)
# storage and initial values of gpr and csr
gpr = {}
csr = {}
for g in REGS: # base base isa gprs
for g in REGS: # base isa gprs
gpr[g] = 0
for i in range(32): # add in v0-v31 gprs
gpr["v"+str(i)] = 0
for f in FREGS: # floating point gprs
gpr[f] = 0
csr["vl"] = 0
csr["vtype"] = 0
@ -238,22 +255,22 @@ def process_ovpsim_sim_log(ovpsim_log, csv, full_trace = 1, stop = 0,
for line in f:
# Extract instruction infromation
m = re.search(r"riscvOVPsim.*, 0x(?P<addr>.*?)(?P<section>\(.*\): ?)" \
"(?P<mode>[A-Za-z]*?)\s+(?P<bin>[a-f0-9]*?)\s+(?P<instr_str>.*?)$",
line)
"(?P<mode>[A-Za-z]*?)\s+(?P<bin>[a-f0-9]*?)\s+(?P<instr_str>.*?)$", line)
if m:
# its instruction disassembly line
if prev_trace: # write out the previous one when find next one
check_conversion(prev_trace)
instr_cnt += 1
trace_csv.write_trace_entry(prev_trace)
if verbose2:
logging.debug("prev_trace:: "+str(prev_trace.__dict__))
logging.debug("csr :: "+str(csr))
logging.debug("gpr :: "+str(gpr))
if logit:
# fatal ("stop for now")
pass
prev_trace = 0
check_conversion(prev_trace)
update_operands_values(prev_trace, gpr)
instr_cnt += 1
trace_csv.write_trace_entry(prev_trace)
if verbose2:
logging.debug("prev_trace:: "+str(prev_trace.__dict__))
logging.debug("csr :: "+str(csr))
logging.debug("gpr :: "+str(gpr))
if logit:
# fatal ("stop for now")
pass
prev_trace = 0
prev_trace = RiscvInstructionTraceEntry()
prev_trace.instr_str = m.group("instr_str")
prev_trace.instr = prev_trace.instr_str.split(" ")[0]
@ -263,127 +280,119 @@ def process_ovpsim_sim_log(ovpsim_log, csv, full_trace = 1, stop = 0,
prev_trace.privileged_mode = convert_mode(m.group("mode"), line)
prev_trace.updated_csr = []
prev_trace.updated_gpr = []
#if prev_trace.instr in ["vsetvli"]:
#if prev_trace.instr in ["vlh.v"]:
#if prev_trace.instr in ["vmul.vx"]:
if prev_trace.instr in ["vmul.vx_XXX"]:
logit = 1
verbose2 = True
if prev_trace.instr in ["vsetvl"]:
logit = 1
verbose2 = True
show_line_instr(line, prev_trace)
if full_trace:
# TODO - when got full ins decode remove this
if "fsriw" in line or \
"fsw" in line or \
"fsd" in line or \
"fnmsub.d" in line or \
"flw" in line:
logging.debug ("Ignoring ins...(%s) " % (line))
continue
logging.debug("Processing [%s]: %s" %
(prev_trace.instr, prev_trace.instr_str))
process_if_compressed(prev_trace)
o = re.search (r"(?P<instr>[a-z]*?)\s(?P<operand>.*)",
prev_trace.instr_str)
if o:
operand_str = o.group("operand").replace(" ", "")
operands = operand_str.split(",")
if (prev_trace.instr in ['jalr', 'c.jalr']):
process_jalr(prev_trace, operands, gpr)
elif (prev_trace.instr in ['jal','c.jal']):
process_jal(prev_trace, operands, gpr)
else:
if 'v' in prev_trace.instr[0]:
assign_operand_vector(prev_trace, operands, gpr, stop_on_first_error)
elif 'f' in prev_trace.instr[0] or "c.f" in prev_trace.instr[0:3]:
pass # ignore floating point. TODO include them
else:
if prev_trace.instr in [
'beq', 'bne', 'blt', 'bge', 'bltu', 'bgeu']:
process_branch_offset (2, operands, prev_trace)
elif prev_trace.instr in [
'c.beqz', 'c.bnez', 'beqz', 'bnez', 'bgez',
'bltz', 'blez', 'bgtz']:
process_branch_offset (1, operands, prev_trace)
if prev_trace.instr in ['j', 'c.j']:
operands[0] = "0x" + operands[0]
assign_operand(prev_trace, operands, gpr, stop_on_first_error)
# TODO - when got full ins decode remove this
if "fsw" in line or \
"fnmsub.d" in line or \
"fsd" in line:
# "fsriw" in line or \
# "flw" in line or \
logging.debug ("Ignoring ins...(%s) " % (line))
continue
process_if_compressed(prev_trace)
o = re.search (r"(?P<instr>[a-z]*?)\s(?P<operand>.*)",
prev_trace.instr_str)
if o:
operand_str = o.group("operand").replace(" ", "")
operands = operand_str.split(",")
if (prev_trace.instr in ['jalr', 'c.jalr']):
process_jalr(prev_trace, operands, gpr)
elif (prev_trace.instr in ['jal','c.jal']):
process_jal(prev_trace, operands, gpr)
else:
# logging.debug("no operand for [%s] in [%s]" % (trace_instr,
# trace_instr_str))
pass
if is_an_extension_instruction(prev_trace.instr):
assign_operand_vector(prev_trace, operands, gpr, stop_on_first_error)
elif 'f' in prev_trace.instr[0] or "c.f" in prev_trace.instr[0:3]:
pass # ignore floating point. TODO include them
else:
if prev_trace.instr in ['beq', 'bne', 'blt', 'bge', 'bltu', 'bgeu']:
process_branch_offset (2, operands, prev_trace)
elif prev_trace.instr in [
'c.beqz', 'c.bnez', 'beqz', 'bnez', 'bgez', 'bltz', 'blez', 'bgtz']:
process_branch_offset (1, operands, prev_trace)
if prev_trace.instr in ['j', 'c.j']:
operands[0] = "0x" + operands[0] # ovpsim has no '0x' so need to add it.
assign_operand(prev_trace, operands, gpr, stop_on_first_error)
else:
# logging.debug("no operand for [%s] in [%s]" % (trace_instr,
# trace_instr_str))
pass
else:
# its a csr, gpr new value or report
if 0: logging.debug ("reg change... [%s]" % (line.strip()))
if verbose2:
logging.debug ("reg change... [%s]" % (line.strip()))
# Extract register change value information
c = re.search(r" (?P<r>[a-z]*[0-9]{0,2}?) (?P<pre>[a-f0-9]+?)" \
" -> (?P<val>[a-f0-9]+?)$", line)
if c and is_csr (c.group("r")):
csr[c.group("r")] = c.group("val")
if verbose2: logging.debug("c:csr %0s = %0s" %
(c.group("r"), c.group("val")))
csr[c.group("r")] = c.group("val")
# prev_trace.updated_csr.append(c.group("r"))
prev_trace.updated_csr.append([c.group("r"), c.group("val")])
continue
csr[c.group("r")] = c.group("val")
if verbose2:
logging.debug("c:csr %0s = %0s" % (c.group("r"), c.group("val")))
csr[c.group("r")] = c.group("val")
# prev_trace.updated_csr.append(c.group("r"))
prev_trace.updated_csr.append([c.group("r"), c.group("val")])
continue
n = re.search(r" (?P<r>[a-z]{1,3}[0-9]{0,2}?) (?P<pre>[a-f0-9]+?)" \
" -> (?P<val>[a-f0-9]+?)$", line)
if n: # gpr
if verbose2: logging.debug(("n:gpr %0s = %0s" %
(n.group("r"), n.group("val"))))
if verbose2:
logging.debug(("n:gpr %0s = %0s" % (n.group("r"), n.group("val"))))
if n.group("r") != "frm":
# prev_trace.updated_gpr.append(n.group("r"))
prev_trace.updated_gpr.append([n.group("r"), n.group("val")])
if 'v' in prev_trace.instr[0]:
gpr[n.group("r")] = n.group("val")
if is_an_extension_instruction(prev_trace.instr):
gpr[n.group("r")] = n.group("val")
else:
# backwards compatible
prev_trace.rd = n.group("r")
prev_trace.rd_val = n.group("val")
gpr[prev_trace.rd] = prev_trace.rd_val
# backwards compatible
prev_trace.rd = n.group("r")
prev_trace.rd_val = n.group("val")
gpr[prev_trace.rd] = prev_trace.rd_val
if 0:
print (
"write entry [[%d]]: rd[%s] val[%s] instr(%s) bin(%s) addr(%s)"
print("write entry [[%d]]: rd[%s] val[%s] instr(%s) bin(%s) addr(%s)"
% (instr_cnt, rv_instr_trace.rd, rv_instr_trace.rd_val,
trace_instr_str, prev_trace.binary, prev_trace.addr))
trace_instr_str, prev_trace.binary, prev_trace.addr))
print (rv_instr_trace.__dict__)
sys.exit(-1)
sys.exit(RET_FATAL)
else:
line = line.strip()
if verbose2: logging.debug("ignoring line: [%s] %s " %
(str(instr_cnt), line))
line = re.sub(' +', ' ', line)
split = line.split(" ")
if len(split) == 1: continue
item = split[1]
if "----" in item: continue
if "REPORT" in line or item in [ # TODO sort csrs
"mtvec","pmpaddr0","pmpcfg0","mstatus","mepc","mscratch",
"mcause","mtval","vl","vtype","sstatus"]:
logging.debug("Ignoring: [%d] [[%s]]" % (instr_cnt, line))
pass
elif "Warning (RISCV_" in line:
logging.debug("Skipping: [%d] (%s) [[%s]]" %
(instr_cnt, prev_trace.instr_str, line))
prev_trace.instr = "nop"
prev_trace.instr_str = "nop"
else:
logging.debug("<unknown> (%s) in line: [%s] %s " %
(item, str(instr_cnt), line))
if stop_on_first_error:
fatal ("")
line = line.strip()
if verbose2: logging.debug("ignoring line: [%s] %s " %
(str(instr_cnt), line))
line = re.sub(' +', ' ', line)
split = line.split(" ")
if len(split) == 1: continue
item = split[1]
if "----" in item: continue
if "REPORT" in line or item in [ # TODO sort csrs
"mtvec","pmpaddr0","pmpcfg0","mstatus","mepc","mscratch",
"mcause","mtval","vl","vtype","sstatus"]:
logging.debug("Ignoring: [%d] [[%s]]" % (instr_cnt, line))
pass
elif "Warning (RISCV_" in line:
logging.debug("Skipping: [%d] (%s) [[%s]]" %
(instr_cnt, prev_trace.instr_str, line))
prev_trace.instr = "nop"
prev_trace.instr_str = "nop"
else:
logging.debug("<unknown> (%s) in line: [%s] %s " %
(item, str(instr_cnt), line))
if stop_on_first_error:
fatal ("")
logging.info("Processed instruction count : %d " % instr_cnt)
if instr_cnt == 0:
logging.error ("No Instructions in logfile: %s" % ovpsim_log)
sys.exit(-1)
sys.exit(RET_FATAL)
logging.info("CSV saved to : %s" % csv)
def main():
""" if used standalone set up for testing """
instr_trace = []
# Parse input arguments
parser = argparse.ArgumentParser()
parser.add_argument("--log", type=str, help="Input ovpsim simulation log")

View file

@ -20,6 +20,7 @@ import csv
import re
import logging
import sys
from lib import *
class RiscvInstructionTraceEntry(object):
"""RISC-V instruction trace entry"""
@ -46,12 +47,20 @@ class RiscvInstructionTraceEntry(object):
self.vs2_val = ""
self.vs3 = ""
self.vs3_val = ""
self.vtype_e = ""
self.vtype_m = ""
self.vtype_d = ""
self.rs3 = ""
self.rs3_val = ""
self.vtype_vsew = ""
self.vtype_vmul = ""
self.vtype_vediv = ""
self.vm = ""
self.updated_csr = ""
self.updated_gpr = ""
self.fd = ""
self.fd_val = ""
self.fs1 = ""
self.fs1_val = ""
self.fs2 = ""
self.fs2_val = ""
def get_trace_string(self):
"""Return a short string of the trace entry"""
@ -73,9 +82,10 @@ class RiscvInstructionTraceCsv(object):
"""Create a CSV file handle for a new trace"""
fields = [
"instr", "rd", "rd_val", "rs1", "rs1_val", "rs2", "rs2_val",
"imm", "str", "addr", "binary", "csr", "mode",
"rs3", "rs3_val", "imm", "str", "addr", "binary", "csr", "mode",
"vd", "vd_val", "vs1", "vs1_val","vs2", "vs2_val","vs3", "vs3_val",
"vtype_e", "vtype_m", "vtype_d", "vm", "updated_csr", "updated_gpr"]
"vtype_vsew", "vtype_vmul", "vtype_vediv", "vm", "updated_csr", "updated_gpr",
"fd", "fd_val", "fs1", "fs1_val","fs2", "fs2_val"]
self.csv_writer = csv.DictWriter(self.csv_fd, fieldnames=fields)
self.csv_writer.writeheader()
@ -96,33 +106,41 @@ class RiscvInstructionTraceCsv(object):
def write_trace_entry(self, entry):
"""Write a new trace entry to CSV"""
self.gpr[entry.rd] = entry.rd_val
self.csv_writer.writerow({'str' : entry.instr_str,
'rd' : entry.rd,
'rd_val' : entry.rd_val,
'rs1' : entry.rs1,
'rs1_val' : entry.rs1_val,
'rs2' : entry.rs2,
'rs2_val' : entry.rs2_val,
'addr' : entry.addr,
'instr' : entry.instr,
'imm' : entry.imm,
'csr' : entry.csr,
'binary' : entry.binary,
'mode' : entry.privileged_mode,
'vd' : entry.vd,
'vd_val' : entry.vd_val,
'vs1' : entry.vs1,
'vs1_val' : entry.vs1_val,
'vs2' : entry.vs2,
'vs2_val' : entry.vs2_val,
'vs3' : entry.vs3,
'vs3_val' : entry.vs3_val,
'vtype_e' : entry.vtype_e,
'vtype_m' : entry.vtype_m,
'vtype_d' : entry.vtype_d,
'vm' : entry.vm,
'updated_csr': entry.updated_csr,
'updated_gpr': entry.updated_gpr,
self.csv_writer.writerow({'str' : entry.instr_str,
'rd' : entry.rd,
'rd_val' : entry.rd_val,
'rs1' : entry.rs1,
'rs1_val' : entry.rs1_val,
'rs2' : entry.rs2,
'rs2_val' : entry.rs2_val,
'rs3' : entry.rs3,
'rs3_val' : entry.rs3_val,
'addr' : entry.addr,
'instr' : entry.instr,
'imm' : entry.imm,
'csr' : entry.csr,
'binary' : entry.binary,
'mode' : entry.privileged_mode,
'vd' : entry.vd,
'vd_val' : entry.vd_val,
'vs1' : entry.vs1,
'vs1_val' : entry.vs1_val,
'vs2' : entry.vs2,
'vs2_val' : entry.vs2_val,
'vs3' : entry.vs3,
'vs3_val' : entry.vs3_val,
'vtype_vsew' : entry.vtype_vsew,
'vtype_vmul' : entry.vtype_vmul,
'vtype_vediv' : entry.vtype_vediv,
'vm' : entry.vm,
'updated_csr' : entry.updated_csr,
'updated_gpr' : entry.updated_gpr,
'fd' : entry.fd,
'fd_val' : entry.fd_val,
'fs1' : entry.fs1,
'fs1_val' : entry.fs1_val,
'fs2' : entry.fs2,
'fs2_val' : entry.fs2_val,
})
@ -493,4 +511,4 @@ def assign_operand(trace, operands, gpr, stop_on_first_error = 0):
logging.debug("Unsupported instr : %s (%s)" %
(trace.instr, trace.instr_str))
if stop_on_first_error:
sys.exit(-1)
sys.exit(RET_FATAL)

View file

@ -40,7 +40,6 @@ def process_sail_sim_log(sail_log, csv):
"""
logging.info("Processing sail log : %s" % sail_log)
instr_cnt = 0
sail_instr = ""
with open(sail_log, "r") as f, open(csv, "w") as csv_fd:
search_start = 0
@ -84,7 +83,6 @@ def process_sail_sim_log(sail_log, csv):
def main():
instr_trace = []
# Parse input arguments
parser = argparse.ArgumentParser()
parser.add_argument("--log", type=str, help="Input sail simulation log")

View file

@ -112,7 +112,6 @@ def process_spike_sim_log(spike_log, csv, full_trace = 0):
def main():
instr_trace = []
# Parse input arguments
parser = argparse.ArgumentParser()
parser.add_argument("--log", type=str, help="Input spike simulation log")

View file

@ -72,7 +72,6 @@ def process_whisper_sim_log(whisper_log, csv, full_trace = 0):
def main():
instr_trace = []
# Parse input arguments
parser = argparse.ArgumentParser()
parser.add_argument("--log", type=str, help="Input whisper simulation log")

View file

@ -0,0 +1,142 @@
/*
* Copyright 2019 Google LLC
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
// Base class for AMO instruction stream
class riscv_amo_base_instr_stream extends riscv_mem_access_stream;
rand int unsigned num_amo;
rand int unsigned num_mixed_instr;
rand int base;
rand riscv_reg_t rs1_reg;
rand int unsigned data_page_id;
rand int max_load_store_offset;
// User can specify a small group of available registers to generate various hazard condition
rand riscv_reg_t avail_regs[];
`uvm_object_utils(riscv_amo_base_instr_stream)
constraint rs1_c {
!(rs1_reg inside {cfg.reserved_regs, reserved_rd, ZERO});
}
constraint addr_range_c {
data_page_id < max_data_page_id;
base inside {[0 : max_load_store_offset-1]};
}
constraint aligned_amo_c {
if (XLEN == 32) {
base % 4 == 0;
} else {
base % 8 == 0;
}
}
function new(string name = "");
super.new(name);
endfunction
function void post_randomize();
gen_amo_instr();
// rs1 cannot be modified by other instructions
if(!(rs1_reg inside {reserved_rd})) begin
reserved_rd = {reserved_rd, rs1_reg};
end
add_mixed_instr(num_mixed_instr);
add_rs1_init_la_instr(rs1_reg, data_page_id);
super.post_randomize();
endfunction
// AMO instruction generation
virtual function void gen_amo_instr();
endfunction
endclass
// A pair of LR/SC instruction
class riscv_lr_sc_instr_stream extends riscv_amo_base_instr_stream;
riscv_rand_instr lr_instr;
riscv_rand_instr sc_instr;
constraint legal_c {
num_amo == 1;
num_mixed_instr inside {[0:15]};
}
`uvm_object_utils(riscv_lr_sc_instr_stream)
function new(string name = "");
super.new(name);
lr_instr = riscv_rand_instr::type_id::create("lr_instr");
sc_instr = riscv_rand_instr::type_id::create("sc_instr");
endfunction
virtual function void gen_amo_instr();
lr_instr.cfg = cfg;
sc_instr.cfg = cfg;
lr_instr.disable_a_extension_c.constraint_mode(0);
sc_instr.disable_a_extension_c.constraint_mode(0);
`DV_CHECK_RANDOMIZE_WITH_FATAL(lr_instr,
rs1 == rs1_reg;
rd != rs1_reg;
instr_name inside {LR_W, LR_D};)
`DV_CHECK_RANDOMIZE_WITH_FATAL(sc_instr,
rs1 == rs1_reg;
rd != rs1_reg;
instr_name inside {SC_W, SC_D};)
instr_list.push_front(lr_instr);
instr_list.push_front(sc_instr);
endfunction
endclass
class riscv_amo_instr_stream extends riscv_amo_base_instr_stream;
riscv_rand_instr amo_instr[];
constraint reasonable_c {
solve num_amo before num_mixed_instr;
num_amo inside {[1:10]};
num_mixed_instr inside {[0:2*num_amo]};
}
`uvm_object_utils(riscv_amo_instr_stream)
`uvm_object_new
virtual function void gen_amo_instr();
amo_instr = new[num_amo];
foreach (amo_instr[i]) begin
amo_instr[i] = riscv_rand_instr::type_id::create($sformatf("amo_instr_%0d", i));
amo_instr[i].cfg = cfg;
amo_instr[i].disable_a_extension_c.constraint_mode(0);
`ifdef DSIM
`DV_CHECK_RANDOMIZE_WITH_FATAL(amo_instr[i],
rs1 == rs1_reg;
rd != rs1_reg;
instr_name inside {[AMOSWAP_W:AMOMAXU_D]};)
`else
`DV_CHECK_RANDOMIZE_WITH_FATAL(amo_instr[i],
rs1 == rs1_reg;
rd != rs1_reg;
category == AMO;)
`endif
instr_list.push_front(amo_instr[i]);
end
endfunction
endclass

View file

@ -0,0 +1,531 @@
/*
* Copyright 2018 Google LLC
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
// Base class for directed instruction stream
class riscv_directed_instr_stream extends riscv_rand_instr_stream;
`uvm_object_utils(riscv_directed_instr_stream)
string label;
function new(string name = "");
super.new(name);
endfunction
function void post_randomize();
foreach(instr_list[i]) begin
instr_list[i].has_label = 1'b0;
instr_list[i].atomic = 1'b1;
end
instr_list[0].comment = $sformatf("Start %0s", get_name());
instr_list[$].comment = $sformatf("End %0s", get_name());
if(label!= "") begin
instr_list[0].label = label;
instr_list[0].has_label = 1'b1;
end
endfunction
endclass
// Base class for memory access stream
class riscv_mem_access_stream extends riscv_directed_instr_stream;
int max_data_page_id;
mem_region_t data_page[$];
`uvm_object_utils(riscv_mem_access_stream)
`uvm_object_new
function void pre_randomize();
if(kernel_mode) begin
data_page = cfg.s_mem_region;
end else begin
data_page = cfg.mem_region;
end
max_data_page_id = data_page.size();
endfunction
// Use "la" instruction to initialize the base regiseter
virtual function void add_rs1_init_la_instr(riscv_reg_t gpr, int id, int base = 0);
riscv_pseudo_instr la_instr;
la_instr = riscv_pseudo_instr::type_id::create("la_instr");
la_instr.pseudo_instr_name = LA;
la_instr.rd = gpr;
if(kernel_mode) begin
la_instr.imm_str = $sformatf("%s+%0d", cfg.s_mem_region[id].name, base);
end else begin
la_instr.imm_str = $sformatf("%s+%0d", cfg.mem_region[id].name, base);
end
instr_list.push_front(la_instr);
endfunction
// Insert some other instructions to mix with mem_access instruction
virtual function void add_mixed_instr(int instr_cnt);
riscv_instr_base instr;
setup_allowed_instr(1, 1);
for(int i = 0; i < instr_cnt; i ++) begin
instr = riscv_instr_base::type_id::create("instr");
randomize_instr(instr);
insert_instr(instr);
end
endfunction
endclass
// Jump instruction (JAL, JALR)
// la rd0, jump_tagert_label
// addi rd1, offset, rd0
// jalr rd, offset, rd1
// For JAL, restore the stack before doing the jump
class riscv_jump_instr extends riscv_directed_instr_stream;
riscv_instr_base jump;
riscv_instr_base addi;
riscv_pseudo_instr la;
riscv_instr_base branch;
rand riscv_reg_t gpr;
rand int imm;
rand bit enable_branch;
rand int mixed_instr_cnt;
riscv_instr_base stack_exit_instr[];
string target_program_label;
int idx;
bit use_jalr;
constraint instr_c {
!(gpr inside {cfg.reserved_regs, ZERO});
imm inside {[-1023:1023]};
mixed_instr_cnt inside {[5:10]};
}
`uvm_object_utils(riscv_jump_instr)
function new(string name = "");
super.new(name);
jump = riscv_instr_base::type_id::create("jump");
la = riscv_pseudo_instr::type_id::create("la");
addi = riscv_instr_base::type_id::create("addi");
branch = riscv_instr_base::type_id::create("branch");
endfunction
function void post_randomize();
riscv_instr_base instr[];
`DV_CHECK_RANDOMIZE_WITH_FATAL(jump,
(use_jalr) -> (instr_name == JALR);
instr_name dist {JAL := 2, JALR := 6, C_JALR := 2};
if (cfg.disable_compressed_instr || (cfg.ra != RA)) {
instr_name != C_JALR;
}
rd == cfg.ra;
rs1 == gpr;
)
`DV_CHECK_RANDOMIZE_WITH_FATAL(addi,
rs1 == gpr;
instr_name == ADDI;
rd == gpr;
)
`DV_CHECK_RANDOMIZE_WITH_FATAL(branch,
instr_name inside {BEQ, BNE, BLT, BGE, BLTU, BGEU};)
la.pseudo_instr_name = LA;
la.imm_str = target_program_label;
la.rd = gpr;
// Generate some random instructions to mix with jump instructions
reserved_rd = {gpr};
initialize_instr_list(mixed_instr_cnt);
gen_instr(1'b1);
addi.imm_str = $sformatf("%0d", imm);
jump.imm_str = $sformatf("%0d", -imm);
// The branch instruction is always inserted right before the jump instruction to avoid
// skipping other required instructions like restore stack, load jump base etc.
// The purse of adding the branch instruction here is to cover branch -> jump scenario.
if(enable_branch) instr = {branch};
// Restore stack before unconditional jump
if(jump.rd == ZERO) begin
instr= {stack_exit_instr, instr};
end
if(jump.instr_name == JAL) begin
jump.imm_str = target_program_label;
end else if (jump.instr_name == C_JALR) begin
instr = {la, instr};
end else begin
instr = {la, addi, instr};
end
mix_instr_stream(instr);
instr_list = {instr_list, jump};
foreach(instr_list[i]) begin
instr_list[i].has_label = 1'b0;
instr_list[i].atomic = 1'b1;
end
jump.has_label = 1'b1;
jump.label = $sformatf("j_%0s_%0s_%0d", label, target_program_label, idx);
branch.imm_str = jump.label;
branch.comment = "branch to jump instr";
branch.branch_assigned = 1'b1;
endfunction
endclass
// Stress back to back jump instruction
class riscv_jal_instr extends riscv_rand_instr_stream;
riscv_instr_base jump[];
riscv_instr_base jump_start;
riscv_instr_base jump_end;
rand int unsigned num_of_jump_instr;
riscv_instr_name_t jal[$];
constraint instr_c {
num_of_jump_instr inside {[10:30]};
}
`uvm_object_utils(riscv_jal_instr)
function new(string name = "");
super.new(name);
endfunction
function void post_randomize();
int order[];
order = new[num_of_jump_instr];
jump = new[num_of_jump_instr];
foreach (order[i]) begin
order[i] = i;
end
order.shuffle();
setup_allowed_instr(1, 1);
jal = {JAL};
if (!cfg.disable_compressed_instr) begin
jal.push_back(C_J);
if (XLEN == 32) begin
jal.push_back(C_JAL);
end
end
// First instruction
jump_start = riscv_instr_base::type_id::create("jump_start");
`DV_CHECK_RANDOMIZE_WITH_FATAL(jump_start,
instr_name == JAL;
rd == cfg.ra;
)
jump_start.imm_str = $sformatf("%0df", order[0]);
jump_start.label = label;
// Last instruction
jump_end = riscv_instr_base::type_id::create("jump_end");
randomize_instr(jump_end);
jump_end.label = $sformatf("%0d", num_of_jump_instr);
foreach (jump[i]) begin
jump[i] = riscv_instr_base::type_id::create($sformatf("jump_%0d", i));
`DV_CHECK_RANDOMIZE_WITH_FATAL(jump[i],
instr_name inside {jal};
rd dist {RA := 5, T1 := 2, [SP:T0] :/ 1, [T2:T6] :/ 2};
!(rd inside {cfg.reserved_regs});
)
jump[i].label = $sformatf("%0d", i);
end
foreach (order[i]) begin
if (i == num_of_jump_instr - 1) begin
jump[order[i]].imm_str = $sformatf("%0df", num_of_jump_instr);
end else begin
if (order[i+1] > order[i]) begin
jump[order[i]].imm_str = $sformatf("%0df", order[i+1]);
end else begin
jump[order[i]].imm_str = $sformatf("%0db", order[i+1]);
end
end
end
instr_list = {jump_start, jump, jump_end};
foreach (instr_list[i]) begin
instr_list[i].has_label = 1'b1;
instr_list[i].atomic = 1'b1;
end
endfunction
endclass
// Push stack instruction stream
class riscv_push_stack_instr extends riscv_rand_instr_stream;
int stack_len;
int num_of_reg_to_save;
int num_of_redudant_instr;
riscv_instr_base push_stack_instr[];
riscv_reg_t saved_regs[];
rand riscv_rand_instr branch_instr;
rand bit enable_branch;
string push_start_label;
`uvm_object_utils(riscv_push_stack_instr)
function new(string name = "");
super.new(name);
endfunction
function void init();
// Save RA, T0
reserved_rd = {cfg.ra};
saved_regs = {cfg.ra};
num_of_reg_to_save = saved_regs.size();
if(num_of_reg_to_save * (XLEN/8) > stack_len) begin
`uvm_fatal(get_full_name(), $sformatf("stack len [%0d] is not enough to store %d regs",
stack_len, num_of_reg_to_save))
end
num_of_redudant_instr = $urandom_range(3,10);
initialize_instr_list(num_of_redudant_instr);
endfunction
virtual function void gen_push_stack_instr(int stack_len, bit allow_branch = 1);
this.stack_len = stack_len;
init();
gen_instr(1'b1);
push_stack_instr = new[num_of_reg_to_save+1];
foreach(push_stack_instr[i]) begin
push_stack_instr[i] = riscv_instr_base::type_id::
create($sformatf("push_stack_instr_%0d", i));
end
// addi sp,sp,-imm
`DV_CHECK_RANDOMIZE_WITH_FATAL(push_stack_instr[0],
instr_name == ADDI; rd == cfg.sp; rs1 == cfg.sp;
imm == (~stack_len + 1);)
push_stack_instr[0].imm_str = $sformatf("-%0d", stack_len);
foreach(saved_regs[i]) begin
if(XLEN == 32) begin
`DV_CHECK_RANDOMIZE_WITH_FATAL(push_stack_instr[i+1],
instr_name == SW; rs2 == saved_regs[i]; rs1 == cfg.sp; imm == 4 * (i+1);)
end else begin
`DV_CHECK_RANDOMIZE_WITH_FATAL(push_stack_instr[i+1],
instr_name == SD; rs2 == saved_regs[i]; rs1 == cfg.sp; imm == 8 * (i+1);)
end
push_stack_instr[i+1].process_load_store = 0;
end
if (allow_branch) begin
`DV_CHECK_STD_RANDOMIZE_FATAL(enable_branch)
end else begin
enable_branch = 0;
end
if(enable_branch) begin
// Cover jal -> branch scenario, the branch is added before push stack operation
branch_instr = riscv_rand_instr::type_id::create("branch_instr");
branch_instr.cfg = cfg;
`ifdef DSIM
`DV_CHECK_RANDOMIZE_WITH_FATAL(branch_instr,
instr_name inside {[BEQ:BGEU], C_BEQZ, C_BNEZ};)
`else
`DV_CHECK_RANDOMIZE_WITH_FATAL(branch_instr, category == BRANCH;)
`endif
branch_instr.imm_str = push_start_label;
branch_instr.branch_assigned = 1'b1;
push_stack_instr[0].label = push_start_label;
push_stack_instr[0].has_label = 1'b1;
push_stack_instr = {branch_instr, push_stack_instr};
end
mix_instr_stream(push_stack_instr);
foreach(instr_list[i]) begin
instr_list[i].atomic = 1'b1;
if(instr_list[i].label == "")
instr_list[i].has_label = 1'b0;
end
endfunction
endclass
// Pop stack instruction stream
class riscv_pop_stack_instr extends riscv_rand_instr_stream;
int stack_len;
int num_of_reg_to_save;
int num_of_redudant_instr;
riscv_instr_base pop_stack_instr[];
riscv_reg_t saved_regs[];
`uvm_object_utils(riscv_pop_stack_instr)
function new(string name = "");
super.new(name);
endfunction
function void init();
reserved_rd = {cfg.ra};
num_of_reg_to_save = saved_regs.size();
if(num_of_reg_to_save * 4 > stack_len) begin
`uvm_fatal(get_full_name(), $sformatf("stack len [%0d] is not enough to store %d regs",
stack_len, num_of_reg_to_save))
end
num_of_redudant_instr = $urandom_range(3,10);
initialize_instr_list(num_of_redudant_instr);
endfunction
virtual function void gen_pop_stack_instr(int stack_len, riscv_reg_t saved_regs[]);
this.stack_len = stack_len;
this.saved_regs = saved_regs;
init();
gen_instr(1'b1);
pop_stack_instr = new[num_of_reg_to_save+1];
foreach(pop_stack_instr[i]) begin
pop_stack_instr[i] = riscv_instr_base::type_id::
create($sformatf("pop_stack_instr_%0d", i));
end
foreach(saved_regs[i]) begin
if(XLEN == 32) begin
`DV_CHECK_RANDOMIZE_WITH_FATAL(pop_stack_instr[i],
instr_name == LW; rd == saved_regs[i]; rs1 == cfg.sp; imm == 4 * (i+1);)
end else begin
`DV_CHECK_RANDOMIZE_WITH_FATAL(pop_stack_instr[i],
instr_name == LD; rd == saved_regs[i]; rs1 == cfg.sp; imm == 8 * (i+1);)
end
pop_stack_instr[i].process_load_store = 0;
end
// addi sp,sp,imm
`DV_CHECK_RANDOMIZE_WITH_FATAL(pop_stack_instr[num_of_reg_to_save],
instr_name == ADDI; rd == cfg.sp; rs1 == cfg.sp; imm == stack_len;)
pop_stack_instr[num_of_reg_to_save].imm_str = $sformatf("%0d", stack_len);
mix_instr_stream(pop_stack_instr);
foreach(instr_list[i]) begin
instr_list[i].atomic = 1'b1;
instr_list[i].has_label = 1'b0;
end
endfunction
endclass
// Cover the long fprward and backward jump
class riscv_long_branch_instr extends riscv_rand_instr_stream;
int branch_instr_stream_len = 100;
int branch_instr_offset = 999;
riscv_rand_instr_stream forward_branch_instr_stream;
riscv_rand_instr_stream backward_branch_instr_stream;
riscv_instr_base jump_instr;
`uvm_object_utils(riscv_long_branch_instr)
function new(string name = "");
super.new(name);
forward_branch_instr_stream = riscv_rand_instr_stream::type_id::
create("forward_branch_instr_stream");
backward_branch_instr_stream = riscv_rand_instr_stream::type_id::
create("backward_branch_instr_stream");
jump_instr = riscv_instr_base::type_id::create("jump_instr");
endfunction
function void init(int instr_len);
branch_instr_stream_len = instr_len;
initialize_instr_list(branch_instr_offset-branch_instr_stream_len);
forward_branch_instr_stream.cfg = cfg;
backward_branch_instr_stream.cfg = cfg;
forward_branch_instr_stream.initialize_instr_list(branch_instr_stream_len);
backward_branch_instr_stream.initialize_instr_list(branch_instr_stream_len);
endfunction
virtual function void gen_instr(bit no_branch = 1'b0, bit no_load_store = 1'b1,
bit is_debug_program = 1'b0);
int branch_offset;
super.gen_instr(1'b1);
forward_branch_instr_stream.gen_instr();
backward_branch_instr_stream.gen_instr();
`DV_CHECK_RANDOMIZE_WITH_FATAL(jump_instr, instr_name == JAL;)
jump_instr.imm_str = "test_done";
instr_list = {forward_branch_instr_stream.instr_list, instr_list,
jump_instr, backward_branch_instr_stream.instr_list};
foreach(instr_list[i]) begin
instr_list[i].atomic = 1'b1;
if(!instr_list[i].is_branch_target) begin
instr_list[i].has_label = 1'b0;
end
if(instr_list[i].category == BRANCH) begin
if(i < branch_instr_stream_len)
branch_offset = branch_instr_offset;
else
branch_offset = -branch_instr_offset;
instr_list[i].imm_str = $sformatf("target_%0d", i);
instr_list[i].branch_assigned = 1'b1;
// Avoid dead loop
if(((instr_list[i+branch_offset].category == BRANCH) ||
instr_list[i+branch_offset].is_branch_target) && (branch_offset < 0))
branch_offset = branch_offset + 1;
`uvm_info(get_full_name(), $sformatf("Branch [%0d] %0s -> [%0d] %0s", i,
instr_list[i].convert2asm(), i+branch_offset,
instr_list[i+branch_offset].convert2asm()), UVM_LOW)
if(i < -branch_offset)
`uvm_fatal(get_name(), $sformatf("Unexpected branch instr at %0d", i))
instr_list[i+branch_offset].label = $sformatf("target_%0d", i);
instr_list[i+branch_offset].has_label = 1'b1;
instr_list[i+branch_offset].is_branch_target = 1;
end
end
endfunction
endclass
class riscv_sw_interrupt_instr extends riscv_directed_instr_stream;
rand bit usip;
rand bit ssip;
rand bit msip;
rand privileged_reg_t ip_reg;
rand riscv_pseudo_instr li_instr;
rand riscv_instr_base csr_instr;
riscv_privil_reg ip;
rand riscv_reg_t rs1_reg;
constraint ip_reg_c {
if(cfg.init_privileged_mode == MACHINE_MODE) {
ip_reg == MIP;
} else {
ip_reg == SIP;
}
(ip_reg == MIP) -> (usip || ssip || msip);
(ip_reg == SIP) -> (usip || ssip);
}
constraint instr_c {
!(rs1_reg inside {cfg.reserved_regs});
rs1_reg != ZERO;
li_instr.pseudo_instr_name == LI;
li_instr.rd == rs1_reg;
csr_instr.instr_name == CSRRW;
csr_instr.rs1 == rs1_reg;
// TODO: Support non-zero rd for SIP, MIP
// csr_instr.rd inside {cfg.avail_regs};
csr_instr.rd == ZERO;
csr_instr.csr == ip_reg;
}
`uvm_object_utils(riscv_sw_interrupt_instr)
function new(string name = "");
super.new(name);
li_instr = riscv_pseudo_instr::type_id::create("li_instr");
csr_instr = riscv_instr_base::type_id::create("csr_instr");
ip = riscv_privil_reg::type_id::create("ip");
endfunction
function void post_randomize();
// TODO: Support UIP
if(cfg.init_privileged_mode == USER_MODE) return;
ip.init_reg(ip_reg);
if(ip_reg == SIP) begin
ip.set_field("USIP", usip);
ip.set_field("SSIP", ssip);
end else begin
ip.set_field("USIP", usip);
ip.set_field("SSIP", ssip);
ip.set_field("MSIP", msip);
end
li_instr.imm_str = $sformatf("0x%0x", ip.get_val());
csr_instr.comment = ip_reg.name();
instr_list = {li_instr, csr_instr};
super.post_randomize();
endfunction
endclass

View file

@ -32,9 +32,9 @@ class riscv_instr_base extends uvm_object;
rand bit [31:0] imm;
rand imm_t imm_type;
rand bit [4:0] imm_len;
rand bit is_pseudo_instr;
rand bit aq;
rand bit rl;
bit is_pseudo_instr;
bit is_branch_target;
bit has_label = 1'b1;
bit atomic = 0;
@ -58,11 +58,11 @@ class riscv_instr_base extends uvm_object;
string label;
bit is_local_numeric_label;
int idx = -1;
`VECTOR_INCLUDE("riscv_instr_base_inc_riscv_instr_base_declares.sv")
`uvm_object_utils(riscv_instr_base)
constraint default_c {
soft is_pseudo_instr == 0;
instr_name != INVALID_INSTR;
}
@ -369,6 +369,7 @@ class riscv_instr_base extends uvm_object;
`add_instr(C_ADDI4SPN, CIW_FORMAT, ARITHMETIC, RV32C, NZUIMM)
`add_instr(C_ADDI, CI_FORMAT, ARITHMETIC, RV32C, NZIMM)
`add_instr(C_ADDI16SP, CI_FORMAT, ARITHMETIC, RV32C, NZIMM)
`add_instr(C_LI, CI_FORMAT, ARITHMETIC, RV32C)
`add_instr(C_LUI, CI_FORMAT, ARITHMETIC, RV32C, NZUIMM)
`add_instr(C_SUB, CA_FORMAT, ARITHMETIC, RV32C)
@ -449,6 +450,8 @@ class riscv_instr_base extends uvm_object;
// Supervisor Instructions
`add_instr(SFENCE_VMA, R_FORMAT,SYNCH,RV32I)
`VECTOR_INCLUDE("riscv_instr_base_inc_add_instr.sv")
function void post_randomize();
if (group inside {RV32C, RV64C, RV128C, RV32DC, RV32FC}) begin
is_compressed = 1'b1;
@ -519,6 +522,9 @@ class riscv_instr_base extends uvm_object;
has_rs1 = 1'b1;
end
end
`VECTOR_INCLUDE("riscv_instr_base_inc_post_randomize.sv")
endfunction
function void mask_imm();
@ -758,6 +764,7 @@ class riscv_instr_base extends uvm_object;
end else begin
asm_str = $sformatf("%0s %0s, %0s, (%0s)", asm_str, rd.name(), rs2.name(), rs1.name());
end
`VECTOR_INCLUDE("riscv_instr_base_inc_convert2asm.sv")
end else begin
// For EBREAK,C.EBREAK, making sure pc+4 is a valid instruction boundary
// This is needed to resume execution from epc+4 after ebreak handling
@ -1202,42 +1209,7 @@ class riscv_instr_base extends uvm_object;
this.has_fs3 = obj.has_fs3;
this.has_fd = obj.has_fd;
this.is_floating_point = obj.is_floating_point;
endfunction
endclass
// Psuedo instructions are used to simplify assembly program writing
class riscv_pseudo_instr extends riscv_instr_base;
rand riscv_pseudo_instr_name_t pseudo_instr_name;
constraint default_c {
is_pseudo_instr == 1'b1;
}
`add_pseudo_instr(LI, I_FORMAT, LOAD, RV32I)
`add_pseudo_instr(LA, I_FORMAT, LOAD, RV32I)
`uvm_object_utils(riscv_pseudo_instr)
function new(string name = "");
super.new(name);
process_load_store = 0;
endfunction
// Convert the instruction to assembly code
virtual function string convert2asm(string prefix = "");
string asm_str;
asm_str = format_string(get_instr_name(), MAX_INSTR_STR_LEN);
// instr rd,imm
asm_str = $sformatf("%0s%0s, %0s", asm_str, rd.name(), get_imm());
if(comment != "")
asm_str = {asm_str, " #",comment};
return asm_str.tolower();
endfunction
virtual function string get_instr_name();
return pseudo_instr_name.name();
endfunction
`VECTOR_INCLUDE("riscv_instr_base_inc_copy_base_instr.sv")
endfunction
endclass

View file

@ -0,0 +1,700 @@
/*
* Copyright 2018 Google LLC
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
//-----------------------------------------------------------------------------
// RISC-V assembly program generator configuration class
//-----------------------------------------------------------------------------
class riscv_instr_gen_config extends uvm_object;
//-----------------------------------------------------------------------------
// Random instruction generation settings
//-----------------------------------------------------------------------------
// Instruction count of the main program
rand int main_program_instr_cnt;
// Instruction count of each sub-program
rand int sub_program_instr_cnt[];
// Instruction count of the debug rom
rand int debug_program_instr_cnt;
// Instruction count of debug sub-programs
rand int debug_sub_program_instr_cnt[];
// Pattern of data section: RAND_DATA, ALL_ZERO, INCR_VAL
rand data_pattern_t data_page_pattern;
// Associate array for delegation configuration for each exception and interrupt
// When the bit is 1, the corresponding delegation is enabled.
rand bit m_mode_exception_delegation[exception_cause_t];
rand bit s_mode_exception_delegation[exception_cause_t];
rand bit m_mode_interrupt_delegation[interrupt_cause_t];
rand bit s_mode_interrupt_delegation[interrupt_cause_t];
// Priviledged mode after boot
rand privileged_mode_t init_privileged_mode;
rand bit[XLEN-1:0] mstatus, mie,
sstatus, sie,
ustatus, uie;
// Key fields in xSTATUS
// Memory protection bits
rand bit mstatus_mprv;
rand bit mstatus_mxr;
rand bit mstatus_sum;
rand bit mstatus_tvm;
rand bit [1:0] mstatus_fs;
rand mtvec_mode_t mtvec_mode;
// Floating point rounding mode
rand f_rounding_mode_t fcsr_rm;
// Enable sfence.vma instruction
rand bit enable_sfence;
// Reserved register
// Reserved for various hardcoded routines
rand riscv_reg_t gpr[4];
// Used by any DCSR operations inside of the debug rom
rand riscv_reg_t scratch_reg;
// Use a random register for stack pointer/thread pointer
rand riscv_reg_t sp;
rand riscv_reg_t tp;
rand riscv_reg_t ra;
// Options for privileged mode CSR checking
// Below checking can be made optional as the ISS implementation could be different with the
// processor.
bit check_misa_init_val = 1'b0;
bit check_xstatus = 1'b1;
// Virtual address translation is on for this test
rand bit virtual_addr_translation_on;
//-----------------------------------------------------------------------------
// User space memory region and stack setting
//-----------------------------------------------------------------------------
mem_region_t mem_region[$] = '{
'{name:"region_0", size_in_bytes: 4096, xwr: 3'b111},
'{name:"region_1", size_in_bytes: 4096 * 4, xwr: 3'b111},
'{name:"region_2", size_in_bytes: 4096 * 2, xwr: 3'b111},
'{name:"region_3", size_in_bytes: 512, xwr: 3'b111},
'{name:"region_4", size_in_bytes: 4096, xwr: 3'b111}
};
// Stack section word length
int stack_len = 5000;
//-----------------------------------------------------------------------------
// Kernel section setting, used by supervisor mode programs
//-----------------------------------------------------------------------------
mem_region_t s_mem_region[$] = '{
'{name:"s_region_0", size_in_bytes: 4096, xwr: 3'b111},
'{name:"s_region_1", size_in_bytes: 4096, xwr: 3'b111}};
// Kernel Stack section word length
int kernel_stack_len = 4000;
// Number of instructions for each kernel program
int kernel_program_instr_cnt = 400;
//-----------------------------------------------------------------------------
// Instruction list based on the config, generate by build_instruction_template
//-----------------------------------------------------------------------------
riscv_instr_base instr_template[riscv_instr_name_t];
riscv_instr_name_t basic_instr[$];
riscv_instr_name_t instr_group[riscv_instr_group_t][$];
riscv_instr_name_t instr_category[riscv_instr_category_t][$];
// Queue of all the main implemented CSRs that the boot privilege mode cannot access
// e.g. these CSRs are in higher privilege modes - access should raise an exception
privileged_reg_t invalid_priv_mode_csrs[$];
//-----------------------------------------------------------------------------
// Command line options or control knobs
//-----------------------------------------------------------------------------
// Main options for RISC-V assembly program generation
// Number of sub-programs per test
int num_of_sub_program = 5;
int instr_cnt = 200;
int num_of_tests = 1;
// For tests doesn't involve load/store, the data section generation could be skipped
bit no_data_page;
// Options to turn off some specific types of instructions
bit no_branch_jump; // No branch/jump instruction
bit no_load_store; // No load/store instruction
bit no_csr_instr; // No csr instruction
bit no_ebreak = 1; // No ebreak instruction
bit no_dret = 1; // No dret instruction
bit no_fence; // No fence instruction
bit no_wfi = 1; // No WFI instruction
bit enable_unaligned_load_store;
int illegal_instr_ratio;
int hint_instr_ratio;
// Directed boot privileged mode, u, m, s
string boot_mode_opts;
int enable_page_table_exception;
bit no_directed_instr;
// A name suffix for the generated assembly program
string asm_test_suffix;
// Enable interrupt bit in MSTATUS (MIE, SIE, UIE)
bit enable_interrupt;
// We need a separate control knob for enabling timer interrupts, as Spike
// throws an exception if xIE.xTIE is enabled
bit enable_timer_irq;
// Generate a bare program without any init/exit/error handling/page table routines
// The generated program can be integrated with a larger program.
// Note that the bare mode program is not expected to run in standalone mode
bit bare_program_mode;
// Enable accessing illegal CSR instruction
// - Accessing non-existence CSR
// - Accessing CSR with wrong privileged mode
bit enable_illegal_csr_instruction;
// Enable accessing CSRs at an invalid privilege level
bit enable_access_invalid_csr_level;
// Enable some dummy writes to main system CSRs (xSTATUS/xIE) at beginning of test
// to check repeated writes
bit enable_dummy_csr_write;
bit randomize_csr = 0;
// sfence support
bit allow_sfence_exception = 0;
// Interrupt/Exception Delegation
bit no_delegation = 1;
bit force_m_delegation = 0;
bit force_s_delegation = 0;
bit support_supervisor_mode;
bit disable_compressed_instr;
// "Memory mapped" address that when written to will indicate some event to
// the testbench - testbench will take action based on the value written
int signature_addr = 32'hdead_beef;
bit require_signature_addr = 1'b0;
// Enable a full or empty debug_rom section.
// Full debug_rom will contain random instruction streams.
// Empty debug_rom will contain just dret instruction and will return immediately.
// Will be empty by default.
bit gen_debug_section = 1'b0;
// Enable generation of a directed sequence of instructions containing
// ebreak inside the debug_rom.
// Disabled by default.
bit enable_ebreak_in_debug_rom = 1'b0;
// Enable setting dcsr.ebreak(m/s/u)
bit set_dcsr_ebreak = 1'b0;
// Number of sub programs in the debug rom
int num_debug_sub_program = 0;
// Enable debug single stepping
bit enable_debug_single_step = 0;
// Number of single stepping iterations
rand int single_step_iterations;
// Enable mstatus.tw bit - causes u-mode WFI to raise illegal instruction exceptions
bit set_mstatus_tw;
// Stack space allocated to each program, need to be enough to store necessary context
// Example: RA, SP, T0
int min_stack_len_per_program = 10 * (XLEN/8);
int max_stack_len_per_program = 16 * (XLEN/8);
// Maximum branch distance, avoid skipping large portion of the code
int max_branch_step = 20;
// Maximum directed instruction stream sequence count
int max_directed_instr_stream_seq = 20;
// Reserved registers
riscv_reg_t reserved_regs[];
// Floating point support
bit enable_floating_point;
//-----------------------------------------------------------------------------
// Command line options for instruction distribution control
//-----------------------------------------------------------------------------
int dist_control_mode;
int unsigned category_dist[riscv_instr_category_t];
uvm_cmdline_processor inst;
constraint default_c {
sub_program_instr_cnt.size() == num_of_sub_program;
debug_sub_program_instr_cnt.size() == num_debug_sub_program;
main_program_instr_cnt inside {[10 : instr_cnt]};
foreach(sub_program_instr_cnt[i]) {
sub_program_instr_cnt[i] inside {[10 : instr_cnt]};
}
// Disable sfence if the program is not boot to supervisor mode
// If sfence exception is allowed, we can enable sfence instruction in any priviledged mode.
// When MSTATUS.TVM is set, executing sfence.vma will be treate as illegal instruction
if(allow_sfence_exception) {
enable_sfence == 1'b1;
(init_privileged_mode != SUPERVISOR_MODE) || (mstatus_tvm == 1'b1);
} else {
(init_privileged_mode != SUPERVISOR_MODE || !riscv_instr_pkg::support_sfence || mstatus_tvm || no_fence)
-> (enable_sfence == 1'b0);
}
}
constraint debug_mode_c {
if (riscv_instr_pkg::support_debug_mode) {
debug_program_instr_cnt inside {[100 : 300]};
foreach(debug_sub_program_instr_cnt[i]) {
debug_sub_program_instr_cnt[i] inside {[100 : 300]};
}
}
`ifndef DSIM
main_program_instr_cnt + sub_program_instr_cnt.sum() == instr_cnt;
`else
// dsim has some issue supporting sum(), use some approximate constraint to generate
// instruction cnt
if (num_of_sub_program > 0) {
main_program_instr_cnt inside {[10:instr_cnt/2]};
foreach (sub_program_instr_cnt[i]) {
sub_program_instr_cnt[i] inside {[10:instr_cnt/num_of_sub_program]};
}
} else {
main_program_instr_cnt == instr_cnt;
}
`endif
}
// Keep the number of single step iterations relatively small
constraint debug_single_step_c {
if (enable_debug_single_step) {
single_step_iterations inside {[10 : 50]};
}
}
// Boot privileged mode distribution
constraint boot_privileged_mode_dist_c {
// Boot to higher privileged mode more often
if(riscv_instr_pkg::supported_privileged_mode.size() == 2) {
init_privileged_mode dist {riscv_instr_pkg::supported_privileged_mode[0] := 6,
riscv_instr_pkg::supported_privileged_mode[1] := 4};
} else if (riscv_instr_pkg::supported_privileged_mode.size() == 3) {
init_privileged_mode dist {riscv_instr_pkg::supported_privileged_mode[0] := 4,
riscv_instr_pkg::supported_privileged_mode[1] := 3,
riscv_instr_pkg::supported_privileged_mode[2] := 3};
} else {
init_privileged_mode == riscv_instr_pkg::supported_privileged_mode[0];
}
}
constraint mtvec_c {
mtvec_mode inside {supported_interrupt_mode};
}
constraint mstatus_c {
// This is default disabled at setup phase. It can be enabled in the exception and interrupt
// handling routine
mstatus_mprv == 1'b0;
}
// Exception delegation setting
constraint exception_delegation_c {
// Do not delegate instructino page fault to supervisor/user mode because this may introduce
// dead loop. All the subsequent instruction fetches may fail and program cannot recover.
m_mode_exception_delegation[INSTRUCTION_PAGE_FAULT] == 1'b0;
if(force_m_delegation) {
foreach(m_mode_exception_delegation[i]) {
soft m_mode_exception_delegation[i] == 1'b1;
}
foreach(m_mode_interrupt_delegation[i]) {
soft m_mode_interrupt_delegation[i] == 1'b1;
}
}
if(force_s_delegation) {
foreach(s_mode_exception_delegation[i]) {
soft s_mode_exception_delegation[i] == 1'b1;
}
foreach(s_mode_interrupt_delegation[i]) {
soft s_mode_interrupt_delegation[i] == 1'b1;
}
}
}
// Spike only supports a subset of exception and interrupt delegation
// You can modify this constraint if your ISS support different set of delegations
constraint delegation_c {
foreach(m_mode_exception_delegation[i]) {
if(!support_supervisor_mode || no_delegation) {
m_mode_exception_delegation[i] == 0;
}
if(!(i inside {INSTRUCTION_ADDRESS_MISALIGNED, BREAKPOINT, ECALL_UMODE,
INSTRUCTION_PAGE_FAULT, LOAD_PAGE_FAULT, STORE_AMO_PAGE_FAULT})) {
m_mode_exception_delegation[i] == 0;
}
}
foreach(m_mode_interrupt_delegation[i]) {
if(!support_supervisor_mode || no_delegation) {
m_mode_interrupt_delegation[i] == 0;
}
if(!(i inside {S_SOFTWARE_INTR, S_TIMER_INTR, S_EXTERNAL_INTR})) {
m_mode_interrupt_delegation[i] == 0;
}
}
}
constraint ra_c {
ra dist {RA := 5, T1 := 2, [SP:T0] :/ 1, [T2:T6] :/ 2};
ra != sp;
ra != tp;
ra != ZERO;
}
constraint sp_tp_c {
sp != tp;
sp dist {SP := 6, RA := 1, [GP:T6] :/ 3};
!(sp inside {GP, RA, ZERO});
!(tp inside {GP, RA, ZERO});
}
constraint reserve_scratch_reg_c {
scratch_reg != ZERO;
scratch_reg != sp;
scratch_reg != tp;
}
constraint gpr_c {
foreach (gpr[i]) {
!(gpr[i] inside {sp, tp, scratch_reg, ZERO, RA, GP});
}
unique {gpr};
}
constraint addr_translaction_c {
solve init_privileged_mode before virtual_addr_translation_on;
if ((init_privileged_mode != MACHINE_MODE) && (SATP_MODE != BARE)) {
virtual_addr_translation_on == 1'b1;
} else {
virtual_addr_translation_on == 1'b0;
}
}
constraint floating_point_c {
if (enable_floating_point) {
mstatus_fs == 2'b01;
} else {
mstatus_fs == 2'b00;
}
}
`uvm_object_utils_begin(riscv_instr_gen_config)
`uvm_field_int(main_program_instr_cnt, UVM_DEFAULT)
`uvm_field_sarray_int(sub_program_instr_cnt, UVM_DEFAULT)
`uvm_field_int(debug_program_instr_cnt, UVM_DEFAULT)
`uvm_field_enum(data_pattern_t, data_page_pattern, UVM_DEFAULT)
`uvm_field_enum(privileged_mode_t, init_privileged_mode, UVM_DEFAULT)
`uvm_field_array_enum(riscv_reg_t, reserved_regs, UVM_DEFAULT)
`uvm_object_utils_end
function new (string name = "");
string s;
super.new(name);
init_delegation();
inst = uvm_cmdline_processor::get_inst();
get_int_arg_value("+num_of_tests=", num_of_tests);
get_int_arg_value("+enable_page_table_exception=", enable_page_table_exception);
get_bool_arg_value("+enable_interrupt=", enable_interrupt);
get_bool_arg_value("+enable_timer_irq=", enable_timer_irq);
get_int_arg_value("+num_of_sub_program=", num_of_sub_program);
get_int_arg_value("+instr_cnt=", instr_cnt);
get_bool_arg_value("+no_ebreak=", no_ebreak);
get_bool_arg_value("+no_dret=", no_dret);
get_bool_arg_value("+no_wfi=", no_wfi);
get_bool_arg_value("+no_branch_jump=", no_branch_jump);
get_bool_arg_value("+no_load_store=", no_load_store);
get_bool_arg_value("+no_csr_instr=", no_csr_instr);
get_bool_arg_value("+enable_illegal_csr_instruction=", enable_illegal_csr_instruction);
get_bool_arg_value("+enable_access_invalid_csr_level=", enable_access_invalid_csr_level);
get_bool_arg_value("+enable_dummy_csr_write=", enable_dummy_csr_write);
get_bool_arg_value("+allow_sfence_exception=", allow_sfence_exception);
get_bool_arg_value("+no_data_page=", no_data_page);
get_bool_arg_value("+no_directed_instr=", no_directed_instr);
get_bool_arg_value("+no_fence=", no_fence);
get_bool_arg_value("+no_delegation=", no_delegation);
get_int_arg_value("+illegal_instr_ratio=", illegal_instr_ratio);
get_int_arg_value("+hint_instr_ratio=", hint_instr_ratio);
get_bool_arg_value("+enable_unaligned_load_store=", enable_unaligned_load_store);
get_bool_arg_value("+force_m_delegation=", force_m_delegation);
get_bool_arg_value("+force_s_delegation=", force_s_delegation);
get_bool_arg_value("+require_signature_addr=", require_signature_addr);
get_bool_arg_value("+disable_compressed_instr=", disable_compressed_instr);
get_bool_arg_value("+randomize_csr=", randomize_csr);
if (this.require_signature_addr) begin
get_hex_arg_value("+signature_addr=", signature_addr);
end
get_bool_arg_value("+gen_debug_section=", gen_debug_section);
get_bool_arg_value("+bare_program_mode=", bare_program_mode);
get_int_arg_value("+num_debug_sub_program=", num_debug_sub_program);
get_bool_arg_value("+enable_ebreak_in_debug_rom=", enable_ebreak_in_debug_rom);
get_bool_arg_value("+set_dcsr_ebreak=", set_dcsr_ebreak);
get_bool_arg_value("+enable_debug_single_step=", enable_debug_single_step);
get_bool_arg_value("+set_mstatus_tw=", set_mstatus_tw);
get_bool_arg_value("+enable_floating_point=", enable_floating_point);
if(inst.get_arg_value("+boot_mode=", boot_mode_opts)) begin
`uvm_info(get_full_name(), $sformatf(
"Got boot mode option - %0s", boot_mode_opts), UVM_LOW)
case(boot_mode_opts)
"m" : init_privileged_mode = MACHINE_MODE;
"s" : init_privileged_mode = SUPERVISOR_MODE;
"u" : init_privileged_mode = USER_MODE;
default: `uvm_fatal(get_full_name(),
$sformatf("Illegal boot mode option - %0s", boot_mode_opts))
endcase
init_privileged_mode.rand_mode(0);
end
`uvm_info(`gfn, $sformatf("riscv_instr_pkg::supported_privileged_mode = %0d",
riscv_instr_pkg::supported_privileged_mode.size()), UVM_LOW)
void'(inst.get_arg_value("+asm_test_suffix=", asm_test_suffix));
// Directed march list from the runtime options, ex. RV32I, RV32M etc.
void'(inst.get_arg_value("+march=", s));
if(s != "") begin
string cmdline_march_list[$];
riscv_instr_group_t march;
uvm_split_string(s, ",", cmdline_march_list);
riscv_instr_pkg::supported_isa.delete();
foreach(cmdline_march_list[i]) begin
if(uvm_enum_wrapper#(riscv_instr_group_t)::from_name(
cmdline_march_list[i].toupper(), march)) begin
riscv_instr_pkg::supported_isa.push_back(march);
end else begin
`uvm_fatal(get_full_name(), $sformatf(
"Invalid march %0s specified in command line", cmdline_march_list[i]))
end
end
end
if (!(RV32C inside {supported_isa})) begin
disable_compressed_instr = 1;
end
setup_instr_distribution();
get_invalid_priv_lvl_csr();
endfunction
function void setup_instr_distribution();
string opts;
int val;
get_int_arg_value("+dist_control_mode=", dist_control_mode);
if (dist_control_mode == 1) begin
riscv_instr_category_t category;
category = category.first;
do begin
opts = {$sformatf("dist_%0s=", category.name()), "%d"};
opts = opts.tolower();
if ($value$plusargs(opts, val)) begin
category_dist[category] = val;
end else begin
category_dist[category] = 10; // Default ratio
end
`uvm_info(`gfn, $sformatf("Set dist[%0s] = %0d",
category.name(), category_dist[category]), UVM_LOW)
category = category.next;
end
while(category != category.first);
end
endfunction
// Initialize the exception/interrupt delegation associate array, set all delegation default to 0
virtual function void init_delegation();
exception_cause_t cause;
interrupt_cause_t intr_cause;
cause = cause.first;
// Init exception delegation array
do begin
m_mode_exception_delegation[cause] = 1'b0;
s_mode_exception_delegation[cause] = 1'b0;
cause = cause.next;
end
while(cause != cause.first);
// Init interrupt delegation array
intr_cause = intr_cause.first;
do begin
m_mode_interrupt_delegation[intr_cause] = 1'b0;
s_mode_interrupt_delegation[intr_cause] = 1'b0;
intr_cause = intr_cause.next;
end
while(intr_cause != intr_cause.first);
endfunction
function void pre_randomize();
foreach (riscv_instr_pkg::supported_privileged_mode[i]) begin
if(riscv_instr_pkg::supported_privileged_mode[i] == SUPERVISOR_MODE)
support_supervisor_mode = 1;
end
endfunction
function void get_non_reserved_gpr();
endfunction
function void post_randomize();
// Setup the list all reserved registers
reserved_regs = {tp, sp, scratch_reg};
// Need to save all loop registers, and RA/T0
min_stack_len_per_program = 2 * (XLEN/8);
// Check if the setting is legal
check_setting();
// WFI is not supported in umode
if (init_privileged_mode == USER_MODE) begin
no_wfi = 1'b1;
end
endfunction
function void check_setting();
bit support_64b;
bit support_128b;
foreach (riscv_instr_pkg::supported_isa[i]) begin
if (riscv_instr_pkg::supported_isa[i] inside {RV64I, RV64M, RV64A, RV64F, RV64D, RV64C}) begin
support_64b = 1'b1;
end else if (riscv_instr_pkg::supported_isa[i] inside {RV128I, RV128C}) begin
support_128b = 1'b1;
end
end
if (support_128b && XLEN != 128) begin
`uvm_fatal(`gfn, "XLEN should be set to 128 based on riscv_instr_pkg::supported_isa setting")
end
if (!support_128b && support_64b && XLEN != 64) begin
`uvm_fatal(`gfn, "XLEN should be set to 64 based on riscv_instr_pkg::supported_isa setting")
end
if (!(support_128b || support_64b) && XLEN != 32) begin
`uvm_fatal(`gfn, "XLEN should be set to 32 based on riscv_instr_pkg::supported_isa setting")
end
if (!(support_128b || support_64b) && !(SATP_MODE inside {SV32, BARE})) begin
`uvm_fatal(`gfn, $sformatf("SATP mode %0s is not supported for RV32G ISA", SATP_MODE.name()))
end
endfunction
// Populate invalid_priv_mode_csrs with the main implemented CSRs for each supported privilege mode
// TODO(udi) - include performance/pmp/trigger CSRs?
virtual function void get_invalid_priv_lvl_csr();
string invalid_lvl[$];
string csr_name;
privileged_reg_t csr;
// Debug CSRs are inaccessible from all but Debug Mode, and we cannot boot into Debug Mode
invalid_lvl.push_back("D");
case (init_privileged_mode)
MACHINE_MODE: begin
end
SUPERVISOR_MODE: begin
invalid_lvl.push_back("M");
end
USER_MODE: begin
invalid_lvl.push_back("S");
invalid_lvl.push_back("M");
end
default: begin
`uvm_fatal(`gfn, "Unsupported initialization privilege mode")
end
endcase
foreach (implemented_csr[i]) begin
privileged_reg_t csr = implemented_csr[i];
csr_name = csr.name();
if (csr_name[0] inside {invalid_lvl}) begin
invalid_priv_mode_csrs.push_back(implemented_csr[i]);
end
end
endfunction
// Get an integer argument from comand line
function void get_int_arg_value(string cmdline_str, ref int val);
string s;
if(inst.get_arg_value(cmdline_str, s)) begin
val = s.atoi();
end
endfunction
// Get a bool argument from comand line
function void get_bool_arg_value(string cmdline_str, ref bit val);
string s;
if(inst.get_arg_value(cmdline_str, s)) begin
val = s.atobin();
end
endfunction
// Get a hex argument from command line
function void get_hex_arg_value(string cmdline_str, ref int val);
string s;
if(inst.get_arg_value(cmdline_str, s)) begin
val = s.atohex();
end
endfunction
// Build instruction template
virtual function void build_instruction_template(bit skip_instr_exclusion = 0);
riscv_instr_name_t instr_name;
riscv_instr_name_t excluded_instr[$];
excluded_instr = {INVALID_INSTR};
if (!skip_instr_exclusion) begin
get_excluded_instr(excluded_instr);
end
instr_name = instr_name.first;
do begin
riscv_instr_base instr;
if (!(instr_name inside {unsupported_instr, excluded_instr})) begin
instr = riscv_instr_base::type_id::create("instr");
`DV_CHECK_RANDOMIZE_WITH_FATAL(instr, instr_name == local::instr_name;)
if ((instr.group inside {supported_isa}) &&
!(disable_compressed_instr && instr.is_compressed) &&
!(!enable_floating_point && (instr.group inside {RV32F, RV64F, RV32D, RV64D}))) begin
`uvm_info(`gfn, $sformatf("Adding [%s] %s to the list",
instr.group.name(), instr.instr_name.name()), UVM_HIGH)
instr_group[instr.group].push_back(instr_name);
instr_category[instr.category].push_back(instr_name);
instr_template[instr_name] = instr;
end
end
instr_name = instr_name.next;
end
while (instr_name != instr_name.first);
endfunction
virtual function void build_instruction_list();
basic_instr = {instr_category[SHIFT], instr_category[ARITHMETIC],
instr_category[LOGICAL], instr_category[COMPARE]};
basic_instr = {basic_instr, EBREAK};
foreach(riscv_instr_pkg::supported_isa[i]) begin
if (riscv_instr_pkg::supported_isa[i] inside {RV32C, RV64C, RV128C, RV32DC, RV32FC}) begin
basic_instr = {basic_instr, C_EBREAK};
break;
end
end
if (no_dret == 0) begin
basic_instr = {basic_instr, DRET};
end
if (no_fence == 0) begin
basic_instr = {basic_instr, instr_category[SYNCH]};
end
// TODO: Support CSR instruction in other mode
if ((no_csr_instr == 0) && (init_privileged_mode == MACHINE_MODE)) begin
`uvm_info(`gfn, $sformatf("Adding CSR instr, mode: %0s", init_privileged_mode.name()), UVM_LOW)
basic_instr = {basic_instr, instr_category[CSR]};
end
if (no_wfi == 0) begin
basic_instr = {basic_instr, WFI};
end
endfunction
virtual function void get_excluded_instr(ref riscv_instr_name_t excluded[$]);
// Below instrutions will modify stack pointer, not allowed in normal instruction stream.
// It can be used in stack operation instruction stream.
excluded = {excluded, C_SWSP, C_SDSP, C_ADDI16SP};
if (!enable_sfence) begin
excluded = {excluded, SFENCE_VMA};
end
if (no_fence) begin
excluded = {excluded, FENCE, FENCE_I, SFENCE_VMA};
end
// TODO: Support C_ADDI4SPN
excluded = {excluded, C_ADDI4SPN};
endfunction
endclass

View file

@ -0,0 +1,306 @@
/*
* Copyright 2018 Google LLC
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
// Base class for RISC-V instruction stream
// A instruction stream here is a queue of RISC-V basic instructions.
// This class also provides some functions to manipulate the instruction stream, like insert a new
// instruction, mix two instruction streams etc.
class riscv_instr_stream extends uvm_object;
`INSTR instr_list[$];
int unsigned instr_cnt;
string label = "";
// User can specify a small group of available registers to generate various hazard condition
rand riscv_reg_t avail_regs[];
// Some additional reserved registers that should not be used as rd register
// by this instruction stream
riscv_reg_t reserved_rd[];
`uvm_object_utils(riscv_instr_stream)
`uvm_object_new
// Initialize the instruction stream, create each instruction instance
function void initialize_instr_list(int unsigned instr_cnt);
instr_list = {};
this.instr_cnt = instr_cnt;
create_instr_instance();
endfunction
virtual function void create_instr_instance();
`INSTR instr;
for(int i = 0; i < instr_cnt; i++) begin
instr = `INSTR::type_id::create($sformatf("instr_%0d", i));
instr_list.push_back(instr);
end
endfunction
// Insert an instruction to the existing instruction stream at the given index
// When index is -1, the instruction is injected at a random location
function void insert_instr(`INSTR instr, int idx = -1);
int current_instr_cnt = instr_list.size();
if(idx == -1) begin
idx = $urandom_range(0, current_instr_cnt-1);
while(instr_list[idx].atomic) begin
idx += 1;
if (idx == current_instr_cnt - 1) begin
instr_list = {instr_list, instr};
return;
end
end
end else if((idx > current_instr_cnt) || (idx < 0)) begin
`uvm_error(`gfn, $sformatf("Cannot insert instr:%0s at idx %0d",
instr.convert2asm(), idx))
end
instr_list.insert(idx, instr);
endfunction
// Insert an instruction to the existing instruction stream at the given index
// When index is -1, the instruction is injected at a random location
// When replace is 1, the original instruction at the inserted position will be replaced
function void insert_instr_stream(`INSTR new_instr[], int idx = -1, bit replace = 1'b0);
int current_instr_cnt = instr_list.size();
int new_instr_cnt = new_instr.size();
if(current_instr_cnt == 0) begin
instr_list = new_instr;
return;
end
if(idx == -1) begin
idx = $urandom_range(0, current_instr_cnt-1);
repeat(10) begin
if (instr_list[idx].atomic) break;
idx = $urandom_range(0, current_instr_cnt-1);
end
if (instr_list[idx].atomic) begin
foreach (instr_list[i]) begin
if (!instr_list[i].atomic) begin
idx = i;
break;
end
end
if (instr_list[idx].atomic) begin
`uvm_fatal(`gfn, $sformatf("Cannot inject the instruction"))
end
end
end else if((idx > current_instr_cnt) || (idx < 0)) begin
`uvm_error(`gfn, $sformatf("Cannot insert instr stream at idx %0d", idx))
end
// When replace is 1, the original instruction at this index will be removed. The label of the
// original instruction will be copied to the head of inserted instruction stream.
if(replace) begin
new_instr[0].label = instr_list[idx].label;
new_instr[0].has_label = instr_list[idx].has_label;
if (idx == 0) begin
instr_list = {new_instr, instr_list[idx+1:current_instr_cnt-1]};
end else begin
instr_list = {instr_list[0:idx-1], new_instr, instr_list[idx+1:current_instr_cnt-1]};
end
end else begin
if (idx == 0) begin
instr_list = {new_instr, instr_list[idx:current_instr_cnt-1]};
end else begin
instr_list = {instr_list[0:idx-1], new_instr, instr_list[idx:current_instr_cnt-1]};
end
end
endfunction
// Mix the input instruction stream with the original instruction, the instruction order is
// preserved. When 'contained' is set, the original instruction stream will be inside the
// new instruction stream with the first and last instruction from the input instruction stream.
function void mix_instr_stream(`INSTR new_instr[], bit contained = 1'b0);
int current_instr_cnt = instr_list.size();
int insert_instr_position[];
int new_instr_cnt = new_instr.size();
insert_instr_position = new[new_instr_cnt];
`DV_CHECK_STD_RANDOMIZE_WITH_FATAL(insert_instr_position,
foreach(insert_instr_position[i]) {
insert_instr_position[i] inside {[0:current_instr_cnt-1]};
})
if (insert_instr_position.size() > 0) begin
insert_instr_position.sort();
end
if(contained) begin
insert_instr_position[0] = 0;
if(new_instr_cnt > 1)
insert_instr_position[new_instr_cnt-1] = current_instr_cnt-1;
end
foreach(new_instr[i]) begin
insert_instr(new_instr[i], insert_instr_position[i] + i);
end
endfunction
function string convert2string();
string str;
foreach(instr_list[i])
str = {str, instr_list[i].convert2asm(), "\n"};
return str;
endfunction
endclass
// Generate a random instruction stream based on the configuration
// There are two ways to use this class to generate instruction stream
// 1. For short instruction stream, you can call randomize() directly.
// 2. For long instruction stream (>1K), randomize() all instructions together might take a long
// time for the constraint solver. In this case, you can call gen_instr to generate instructions
// one by one. The time only grows linearly with the instruction count
class riscv_rand_instr_stream extends riscv_instr_stream;
riscv_instr_gen_config cfg;
bit kernel_mode;
riscv_instr_name_t allowed_instr[$];
int unsigned category_dist[riscv_instr_category_t];
`uvm_object_utils(riscv_rand_instr_stream)
`uvm_object_new
virtual function void create_instr_instance();
`INSTR instr;
for (int i = 0; i < instr_cnt; i++) begin
instr = `INSTR::type_id::create($sformatf("instr_%0d", i));
instr_list.push_back(instr);
end
endfunction
virtual function void setup_allowed_instr(bit no_branch = 1'b0, bit no_load_store = 1'b1);
allowed_instr = cfg.basic_instr;
if (no_branch == 0) begin
allowed_instr = {allowed_instr, cfg.instr_category[BRANCH]};
end
if (no_load_store == 0) begin
allowed_instr = {allowed_instr, cfg.instr_category[LOAD], cfg.instr_category[STORE]};
end
setup_instruction_dist(no_branch, no_load_store);
endfunction
function void setup_instruction_dist(bit no_branch = 1'b0, bit no_load_store = 1'b1);
if (cfg.dist_control_mode) begin
category_dist = cfg.category_dist;
if (no_branch) begin
category_dist[BRANCH] = 0;
end
if (no_load_store) begin
category_dist[LOAD] = 0;
category_dist[STORE] = 0;
end
`uvm_info(`gfn, $sformatf("setup_instruction_dist: %0d", category_dist.size()), UVM_LOW)
end
endfunction
virtual function void gen_instr(bit no_branch = 1'b0, bit no_load_store = 1'b1,
bit is_debug_program = 1'b0);
setup_allowed_instr(no_branch, no_load_store);
foreach(instr_list[i]) begin
randomize_instr(instr_list[i], is_debug_program);
end
// Do not allow branch instruction as the last instruction because there's no
// forward branch target
while (instr_list[$].category == BRANCH) begin
void'(instr_list.pop_back());
if (instr_list.size() == 0) break;
end
endfunction
function void randomize_instr(riscv_instr_base instr,
bit is_in_debug = 1'b0,
bit skip_rs1 = 1'b0,
bit skip_rs2 = 1'b0,
bit skip_rd = 1'b0,
bit skip_imm = 1'b0,
bit skip_csr = 1'b0,
bit disable_dist = 1'b0);
riscv_instr_name_t instr_name;
if ((cfg.dist_control_mode == 1) && !disable_dist) begin
riscv_instr_category_t category;
int unsigned idx;
`DV_CHECK_STD_RANDOMIZE_WITH_FATAL(category,
category dist {LOAD := category_dist[LOAD],
STORE := category_dist[STORE],
SHIFT := category_dist[SHIFT],
ARITHMETIC := category_dist[ARITHMETIC],
LOGICAL := category_dist[LOGICAL],
COMPARE := category_dist[COMPARE],
BRANCH := category_dist[BRANCH],
SYNCH := category_dist[SYNCH],
CSR := category_dist[CSR]};)
idx = $urandom_range(0, cfg.instr_category[category].size() - 1);
instr_name = cfg.instr_category[category][idx];
// if set_dcsr_ebreak is set, we do not want to generate any ebreak
// instructions inside the debug_rom
end else if ((cfg.no_ebreak && !is_in_debug) ||
(!cfg.enable_ebreak_in_debug_rom && is_in_debug)) begin
`DV_CHECK_STD_RANDOMIZE_WITH_FATAL(instr_name,
instr_name inside {allowed_instr};
!(instr_name inside {EBREAK, C_EBREAK});)
end else begin
`DV_CHECK_STD_RANDOMIZE_WITH_FATAL(instr_name,
instr_name inside {allowed_instr};)
end
instr.copy_base_instr(cfg.instr_template[instr_name]);
`uvm_info(`gfn, $sformatf("%s: rs1:%0d, rs2:%0d, rd:%0d, imm:%0d",
instr.instr_name.name(),
instr.has_rs1,
instr.has_rs2,
instr.has_rd,
instr.has_imm), UVM_FULL)
if (instr.has_imm && !skip_imm) begin
instr.gen_rand_imm();
end
if (instr.has_rs1 && !skip_rs1) begin
if (instr.is_compressed) begin
// Compressed instruction could use the same register for rs1 and rd
instr.rs1 = instr.gen_rand_gpr(
.included_reg(avail_regs),
.excluded_reg({reserved_rd, cfg.reserved_regs}));
end else begin
instr.rs1 = instr.gen_rand_gpr(.included_reg(avail_regs));
end
end
if (instr.has_rs2 && !skip_rs2) begin
instr.rs2 = instr.gen_rand_gpr(.included_reg(avail_regs));
end
if (instr.has_rd && !skip_rd) begin
if (instr_name == C_LUI) begin
instr.rd = instr.gen_rand_gpr(
.included_reg(avail_regs),
.excluded_reg({reserved_rd, cfg.reserved_regs, SP}));
end else begin
instr.rd = instr.gen_rand_gpr(
.included_reg(avail_regs),
.excluded_reg({reserved_rd, cfg.reserved_regs}));
end
end
if ((instr.category == CSR) && !skip_csr) begin
instr.gen_rand_csr(.privileged_mode(cfg.init_privileged_mode),
.enable_floating_point(cfg.enable_floating_point),
.illegal_csr_instr(cfg.enable_illegal_csr_instruction),
.legal_invalid_csr_instr(cfg.enable_access_invalid_csr_level),
.invalid_csrs(cfg.invalid_priv_mode_csrs));
end
if (instr.has_fs1) begin
instr.fs1 = instr.gen_rand_fpr();
end
if (instr.has_fs2) begin
instr.fs2 = instr.gen_rand_fpr();
end
if (instr.has_fs3) begin
instr.fs3 = instr.gen_rand_fpr();
end
if (instr.has_fd) begin
instr.fd = instr.gen_rand_fpr();
end
endfunction
endclass

View file

@ -0,0 +1,454 @@
/*
* Copyright 2018 Google LLC
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
// Base class for all load/store instruction stream
class riscv_load_store_base_instr_stream extends riscv_mem_access_stream;
typedef enum bit [1:0] {
NARROW,
HIGH,
MEDIUM,
SPARSE
} locality_e;
rand int unsigned num_load_store;
rand int unsigned num_mixed_instr;
rand int base;
int offset[];
int addr[];
riscv_instr_base load_store_instr[$];
rand int unsigned data_page_id;
rand riscv_reg_t rs1_reg;
rand locality_e locality;
rand int max_load_store_offset;
`uvm_object_utils(riscv_load_store_base_instr_stream)
constraint rs1_c {
!(rs1_reg inside {cfg.reserved_regs, reserved_rd, ZERO});
}
constraint addr_c {
solve data_page_id before max_load_store_offset;
solve max_load_store_offset before base;
data_page_id < max_data_page_id;
foreach (data_page[i]) {
if (i == data_page_id) {
max_load_store_offset == data_page[i].size_in_bytes;
}
}
base inside {[0 : max_load_store_offset-1]};
}
function new(string name = "");
super.new(name);
endfunction
virtual function void randomize_offset();
int offset_, addr_;
offset = new[num_load_store];
addr = new[num_load_store];
for (int i=0; i<num_load_store; i++) begin
if (!std::randomize(offset_, addr_) with {
// Locality
if (locality == NARROW) {
soft offset_ inside {[-16:16]};
} else if (locality == HIGH) {
soft offset_ inside {[-64:64]};
} else if (locality == MEDIUM) {
soft offset_ inside {[-256:256]};
} else if (locality == SPARSE) {
soft offset_ inside {[-2048:2047]};
}
addr_ == base + offset_;
addr_ inside {[0 : max_load_store_offset - 1]};
}) begin
`uvm_fatal(`gfn, "Cannot randomize load/store offset")
end
offset[i] = offset_;
addr[i] = addr_;
end
endfunction
function void post_randomize();
randomize_offset();
// rs1 cannot be modified by other instructions
if(!(rs1_reg inside {reserved_rd})) begin
reserved_rd = {reserved_rd, rs1_reg};
end
gen_load_store_instr();
add_mixed_instr(num_mixed_instr);
add_rs1_init_la_instr(rs1_reg, data_page_id, base);
super.post_randomize();
endfunction
// Generate each load/store instruction
virtual function void gen_load_store_instr();
bit enable_compressed_load_store;
riscv_instr_base instr;
if(avail_regs.size() > 0) begin
`DV_CHECK_STD_RANDOMIZE_WITH_FATAL(avail_regs,
unique{avail_regs};
avail_regs[0] inside {[S0 : A5]};
foreach(avail_regs[i]) {
!(avail_regs[i] inside {cfg.reserved_regs, reserved_rd});
},
"Cannot randomize avail_regs")
end
if ((rs1_reg inside {[S0 : A5]}) && !cfg.disable_compressed_instr) begin
enable_compressed_load_store = 1;
end
foreach(addr[i]) begin
instr = riscv_instr_base::type_id::create("instr");
// Assign the allowed load/store instructions based on address alignment
// This is done separately rather than a constraint to improve the randomization performance
allowed_instr = {LB, LBU, SB};
if (!cfg.enable_unaligned_load_store) begin
if (addr[i][0] == 1'b0) begin
allowed_instr = {LH, LHU, SH, allowed_instr};
end
if (addr[i] % 4 == 0) begin
allowed_instr = {LW, SW, allowed_instr};
if (cfg.enable_floating_point) begin
allowed_instr = {FLW, FSW, allowed_instr};
end
if((offset[i] inside {[0:127]}) && (offset[i] % 4 == 0) &&
(RV32C inside {riscv_instr_pkg::supported_isa}) &&
enable_compressed_load_store) begin
allowed_instr = {C_LW, C_SW, allowed_instr};
if (cfg.enable_floating_point && (RV32FC inside {supported_isa})) begin
allowed_instr = {C_FLW, C_FSW, allowed_instr};
end
end
end
if ((XLEN >= 64) && (addr[i] % 8 == 0)) begin
allowed_instr = {LWU, LD, SD, allowed_instr};
if (cfg.enable_floating_point && (RV32D inside {supported_isa})) begin
allowed_instr = {FLD, FSD, allowed_instr};
end
if((offset[i] inside {[0:255]}) && (offset[i] % 8 == 0) &&
(RV64C inside {riscv_instr_pkg::supported_isa} &&
enable_compressed_load_store)) begin
allowed_instr = {C_LD, C_SD, allowed_instr};
if (cfg.enable_floating_point && (RV32DC inside {supported_isa})) begin
allowed_instr = {C_FLD, C_FSD, allowed_instr};
end
end
end
end else begin
allowed_instr = {LW, SW, LH, LHU, SH, allowed_instr};
if ((offset[i] inside {[0:127]}) && (offset[i] % 4 == 0) &&
(RV32C inside {riscv_instr_pkg::supported_isa}) &&
enable_compressed_load_store) begin
allowed_instr = {C_LW, C_SW, allowed_instr};
end
if (XLEN >= 64) begin
allowed_instr = {LWU, LD, SD, allowed_instr};
if ((offset[i] inside {[0:255]}) && (offset[i] % 8 == 0) &&
(RV64C inside {riscv_instr_pkg::supported_isa}) &&
enable_compressed_load_store) begin
allowed_instr = {C_LD, C_SD, allowed_instr};
end
end
end
randomize_instr(instr, .skip_rs1(1'b1), .skip_imm(1'b1), .disable_dist(1'b1));
instr.rs1 = rs1_reg;
instr.set_imm(offset[i]);
instr.process_load_store = 0;
instr_list.push_back(instr);
load_store_instr.push_back(instr);
end
endfunction
endclass
// A single load/store instruction
class riscv_single_load_store_instr_stream extends riscv_load_store_base_instr_stream;
constraint legal_c {
num_load_store == 1;
num_mixed_instr < 5;
}
`uvm_object_utils(riscv_single_load_store_instr_stream)
`uvm_object_new
endclass
// Back to back load/store instructions
class riscv_load_store_stress_instr_stream extends riscv_load_store_base_instr_stream;
int unsigned max_instr_cnt = 30;
int unsigned min_instr_cnt = 10;
constraint legal_c {
num_load_store inside {[min_instr_cnt:max_instr_cnt]};
num_mixed_instr == 0;
}
`uvm_object_utils(riscv_load_store_stress_instr_stream)
`uvm_object_new
endclass
// Random load/store sequence
// A random mix of load/store instructions and other instructions
class riscv_load_store_rand_instr_stream extends riscv_load_store_base_instr_stream;
constraint legal_c {
num_load_store inside {[10:30]};
num_mixed_instr inside {[10:30]};
}
`uvm_object_utils(riscv_load_store_rand_instr_stream)
`uvm_object_new
endclass
// Use a small set of GPR to create various WAW, RAW, WAR hazard scenario
class riscv_hazard_instr_stream extends riscv_load_store_base_instr_stream;
int unsigned num_of_avail_regs = 6;
constraint legal_c {
num_load_store inside {[10:30]};
num_mixed_instr inside {[10:30]};
}
`uvm_object_utils(riscv_hazard_instr_stream)
`uvm_object_new
function void pre_randomize();
avail_regs = new[num_of_avail_regs];
super.pre_randomize();
endfunction
endclass
// Use a small set of address to create various load/store hazard sequence
// This instruction stream focus more on hazard handling of load store unit.
class riscv_load_store_hazard_instr_stream extends riscv_load_store_base_instr_stream;
rand int avail_addr[];
constraint legal_c {
num_load_store inside {[10:30]};
num_mixed_instr inside {[10:30]};
}
constraint avail_addr_c {
avail_addr.size() inside {[1:3]};
foreach(avail_addr[i]) {
avail_addr[i] inside {[0 : max_load_store_offset - 1]};
}
}
`uvm_object_utils(riscv_load_store_hazard_instr_stream)
`uvm_object_new
// Randomize each address in the post_randomize to reduce the complexity of solving everything
// in one shot.
function void post_randomize();
int temp_addr;
foreach(addr[i]) begin
`DV_CHECK_STD_RANDOMIZE_WITH_FATAL(temp_addr,
temp_addr inside {avail_addr};,
"Cannot randomize address")
addr[i] = temp_addr;
end
endfunction
endclass
// Back to back access to multiple data pages
// This is useful to test data TLB switch and replacement
class riscv_multi_page_load_store_instr_stream extends riscv_mem_access_stream;
riscv_load_store_stress_instr_stream load_store_instr_stream[];
rand int unsigned num_of_instr_stream;
rand int unsigned data_page_id[];
rand riscv_reg_t rs1_reg[];
constraint default_c {
foreach(data_page_id[i]) {
data_page_id[i] < max_data_page_id;
}
data_page_id.size() == num_of_instr_stream;
rs1_reg.size() == num_of_instr_stream;
unique {rs1_reg};
foreach(rs1_reg[i]) {
!(rs1_reg[i] inside {cfg.reserved_regs, ZERO});
}
}
constraint page_c {
solve num_of_instr_stream before data_page_id;
num_of_instr_stream inside {[1 : max_data_page_id]};
unique {data_page_id};
}
// Avoid accessing a large number of pages because we may run out of registers for rs1
// Each page access needs a reserved register as the base address of load/store instruction
constraint reasonable_c {
num_of_instr_stream inside {[2:8]};
}
`uvm_object_utils(riscv_multi_page_load_store_instr_stream)
`uvm_object_new
// Generate each load/store seq, and mix them together
function void post_randomize();
load_store_instr_stream = new[num_of_instr_stream];
foreach(load_store_instr_stream[i]) begin
load_store_instr_stream[i] = riscv_load_store_stress_instr_stream::type_id::
create($sformatf("load_store_instr_stream_%0d", i));
load_store_instr_stream[i].min_instr_cnt = 5;
load_store_instr_stream[i].max_instr_cnt = 10;
load_store_instr_stream[i].cfg = cfg;
// Make sure each load/store sequence doesn't override the rs1 of other sequences.
foreach(rs1_reg[j]) begin
if(i != j) begin
load_store_instr_stream[i].reserved_rd =
{load_store_instr_stream[i].reserved_rd, rs1_reg[j]};
end
end
`DV_CHECK_RANDOMIZE_WITH_FATAL(load_store_instr_stream[i],
rs1_reg == local::rs1_reg[i];
data_page_id == local::data_page_id[i];,
"Cannot randomize load/store instruction")
// Mix the instruction stream of different page access, this could trigger the scenario of
// frequent data TLB switch
if(i == 0) begin
instr_list = load_store_instr_stream[i].instr_list;
end else begin
mix_instr_stream(load_store_instr_stream[i].instr_list);
end
end
endfunction
endclass
// Access the different locations of the same memory regions
class riscv_mem_region_stress_test extends riscv_multi_page_load_store_instr_stream;
`uvm_object_utils(riscv_mem_region_stress_test)
`uvm_object_new
constraint page_c {
num_of_instr_stream inside {[2:5]};
foreach (data_page_id[i]) {
if (i > 0) {
data_page_id[i] == data_page_id[i-1];
}
}
}
endclass
// Random load/store sequence to full address range
// The address range is not preloaded with data pages, use store instruction to initialize first
class riscv_load_store_rand_addr_instr_stream extends riscv_load_store_base_instr_stream;
rand bit [XLEN-1:0] addr_offset;
// Find an unused 4K page from address 1M onward
constraint addr_offset_c {
|addr_offset[XLEN-1:20] == 1'b1;
// TODO(taliu) Support larger address range
addr_offset[XLEN-1:31] == 0;
addr_offset[11:0] == 0;
}
constraint legal_c {
num_load_store inside {[5:10]};
num_mixed_instr inside {[5:10]};
}
`uvm_object_utils(riscv_load_store_rand_addr_instr_stream)
virtual function void randomize_offset();
int offset_, addr_;
offset = new[num_load_store];
addr = new[num_load_store];
for (int i=0; i<num_load_store; i++) begin
if (!std::randomize(offset_) with {
offset_ inside {[-2048:2047]};
}
) begin
`uvm_fatal(`gfn, "Cannot randomize load/store offset")
end
offset[i] = offset_;
addr[i] = addr_offset + offset_;
end
endfunction `uvm_object_new
virtual function void add_rs1_init_la_instr(riscv_reg_t gpr, int id, int base = 0);
riscv_instr_base instr[$];
riscv_pseudo_instr li_instr;
riscv_instr_base store_instr;
riscv_instr_base add_instr;
int min_offset[$];
int max_offset[$];
min_offset = offset.min();
max_offset = offset.max();
// Use LI to initialize the address offset
li_instr = riscv_pseudo_instr::type_id::create("li_instr");
`DV_CHECK_RANDOMIZE_WITH_FATAL(li_instr,
pseudo_instr_name == LI;
rd inside {cfg.gpr};
rd != gpr;
)
li_instr.imm_str = $sformatf("0x%0x", addr_offset);
// Add offset to the base address
add_instr = riscv_instr_base::type_id::create("add_instr");
`DV_CHECK_RANDOMIZE_WITH_FATAL(add_instr,
instr_name == ADD;
rs1 == gpr;
rs2 == li_instr.rd;
rd == gpr;
)
instr.push_back(li_instr);
instr.push_back(add_instr);
// Create SW instruction template
store_instr = riscv_instr_base::type_id::create("store_instr");
`DV_CHECK_RANDOMIZE_WITH_FATAL(store_instr,
instr_name == SB;
rs1 == gpr;
)
// Initialize the location which used by load instruction later
foreach (load_store_instr[i]) begin
if (load_store_instr[i].category == LOAD) begin
riscv_instr_base store;
store = riscv_instr_base::type_id::create("store");
store.copy_base_instr(store_instr);
store.rs2 = riscv_reg_t'(i % 32);
store.imm_str = load_store_instr[i].imm_str;
case (load_store_instr[i].instr_name) inside
LB, LBU : store.instr_name = SB;
LH, LHU : store.instr_name = SH;
LW, C_LW, FLW, C_FLW : store.instr_name = SW;
LD, C_LD, FLD, C_FLD, LWU : store.instr_name = SD;
default : `uvm_fatal(`gfn, $sformatf("Unexpected op: %0s",
load_store_instr[i].convert2asm()))
endcase
instr.push_back(store);
end
end
instr_list = {instr, instr_list};
super.add_rs1_init_la_instr(gpr, id, 0);
endfunction
endclass

View file

@ -0,0 +1,212 @@
/*
* Copyright 2018 Google LLC
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
// TODO: Support other loop counter update instruction other than ADDI
// TODO: Support forward branch inside the loop
class riscv_loop_instr extends riscv_rand_instr_stream;
rand riscv_reg_t loop_cnt_reg[];
rand riscv_reg_t loop_limit_reg[];
rand int loop_init_val[];
rand int loop_step_val[];
rand int loop_limit_val[];
rand bit [2:0] num_of_nested_loop;
rand int num_of_instr_in_loop;
rand riscv_instr_name_t branch_type[];
riscv_instr_base loop_init_instr[];
riscv_instr_base loop_update_instr[];
riscv_instr_base loop_branch_instr[];
riscv_rand_instr loop_branch_target_instr[];
// Aggregated loop instruction stream
riscv_instr_base loop_instr[];
constraint legal_loop_regs_c {
solve num_of_nested_loop before loop_cnt_reg;
solve num_of_nested_loop before loop_limit_reg;
foreach (loop_cnt_reg[i]) {
loop_cnt_reg[i] != ZERO;
foreach (cfg.reserved_regs[j]) {
loop_cnt_reg[i] != cfg.reserved_regs[j];
}
}
foreach (loop_limit_reg[i]) {
foreach (cfg.reserved_regs[j]) {
loop_limit_reg[i] != cfg.reserved_regs[j];
}
}
unique {loop_cnt_reg, loop_limit_reg};
loop_cnt_reg.size() == num_of_nested_loop;
loop_limit_reg.size() == num_of_nested_loop;
}
constraint loop_c {
solve num_of_nested_loop before loop_init_val;
solve num_of_nested_loop before loop_step_val;
solve num_of_nested_loop before loop_limit_val;
solve loop_limit_val before loop_limit_reg;
solve branch_type before loop_init_val;
solve branch_type before loop_step_val;
solve branch_type before loop_limit_val;
num_of_instr_in_loop inside {[1:25]};
num_of_nested_loop inside {[1:2]};
loop_init_val.size() == num_of_nested_loop;
loop_step_val.size() == num_of_nested_loop;
loop_limit_val.size() == num_of_nested_loop;
branch_type.size() == num_of_nested_loop;
foreach (branch_type[i]) {
if (!cfg.disable_compressed_instr) {
branch_type[i] inside {C_BNEZ, C_BEQZ, BEQ, BNE, BLTU, BLT, BGEU, BGE};
} else {
branch_type[i] inside {BEQ, BNE, BLTU, BLT, BGEU, BGE};
}
}
foreach(loop_init_val[i]) {
if (branch_type[i] inside {C_BNEZ, C_BEQZ}) {
loop_limit_val[i] == 0;
loop_limit_reg[i] == ZERO;
loop_cnt_reg[i] inside {riscv_instr_pkg::compressed_gpr};
} else {
loop_limit_val[i] inside {[-20:20]};
loop_limit_reg[i] != ZERO;
}
if (branch_type[i] inside {C_BNEZ, C_BEQZ, BEQ, BNE}) {
((loop_limit_val[i] - loop_init_val[i]) % loop_step_val[i] == 0) &&
(loop_limit_val[i] != loop_init_val[i]);
} else if (branch_type[i] == BGE) {
loop_step_val[i] < 0;
} else if (branch_type[i] == BGEU) {
loop_step_val[i] < 0;
loop_init_val[i] > 0;
// Avoid count to negative
loop_step_val[i] + loop_limit_val[i] > 0;
} else if (branch_type[i] == BLT) {
loop_step_val[i] > 0;
} else if (branch_type[i] == BLTU) {
loop_step_val[i] > 0;
loop_limit_val[i] > 0;
}
loop_init_val[i] inside {[-10:10]};
loop_step_val[i] inside {[-10:10]};
if(loop_init_val[i] < loop_limit_val[i]) {
loop_step_val[i] > 0;
} else {
loop_step_val[i] < 0;
}
}
}
`uvm_object_utils(riscv_loop_instr)
`uvm_object_new
function void post_randomize();
reserved_rd = {loop_cnt_reg, loop_limit_reg};
// Generate instructions that mixed with the loop instructions
initialize_instr_list(num_of_instr_in_loop);
gen_instr(1'b1);
// Randomize the key loop instructions
loop_init_instr = new[num_of_nested_loop*2];
loop_update_instr = new[num_of_nested_loop];
loop_branch_instr = new[num_of_nested_loop];
loop_branch_target_instr = new[num_of_nested_loop];
for(int i = 0; i < num_of_nested_loop; i++) begin
// Instruction to init the loop counter
loop_init_instr[2*i] = riscv_instr_base::type_id::create("loop_init_instr");
`DV_CHECK_RANDOMIZE_WITH_FATAL(loop_init_instr[2*i],
instr_name == ADDI;
rd == loop_cnt_reg[i];
rs1 == ZERO;
imm == loop_init_val[i];,
"Cannot randomize loop init insturction")
loop_init_instr[2*i].comment = $sformatf("init loop %0d counter", i);
// Instruction to init loop limit
loop_init_instr[2*i+1] = riscv_instr_base::type_id::create("loop_init_instr");
`DV_CHECK_RANDOMIZE_WITH_FATAL(loop_init_instr[2*i+1],
instr_name == ADDI;
rd == loop_limit_reg[i];
rs1 == ZERO;
imm == loop_limit_val[i];,
"Cannot randomize init loop instruction")
loop_init_instr[2*i+1].comment = $sformatf("init loop %0d limit", i);
// Branch target instruction, can be anything
loop_branch_target_instr[i] = riscv_rand_instr::type_id::create("loop_branch_target_instr");
loop_branch_target_instr[i].cfg = cfg;
loop_branch_target_instr[i].reserved_rd = reserved_rd;
`DV_CHECK_RANDOMIZE_WITH_FATAL(loop_branch_target_instr[i],
!(category inside {LOAD, STORE, BRANCH, JUMP});
// TODO: allow CSR instructions in all modes
if (cfg.init_privileged_mode == MACHINE_MODE) {
if (category == CSR) {
csr == MSCRATCH;
}
} else {
!(category == CSR);
},
"Cannot randomize branch target instruction")
loop_branch_target_instr[i].label = $sformatf("%0s_%0d_t", label, i);
// Instruction to update loop counter
loop_update_instr[i] = riscv_instr_base::type_id::create("loop_update_instr");
`DV_CHECK_RANDOMIZE_WITH_FATAL(loop_update_instr[i],
instr_name == ADDI;
rd == loop_cnt_reg[i];
rs1== loop_cnt_reg[i];
imm == loop_step_val[i];,
"Cannot randomize loop update instruction")
loop_update_instr[i].comment = $sformatf("update loop %0d counter", i);
// Backward branch instruction
loop_branch_instr[i] = riscv_instr_base::type_id::create("loop_branch_instr");
`DV_CHECK_RANDOMIZE_WITH_FATAL(loop_branch_instr[i],
instr_name == branch_type[i];
rs1 == loop_cnt_reg[i];
if (!(branch_type[i] inside {C_BEQZ, C_BNEZ})) {
rs2 == loop_limit_reg[i];
},
"Cannot randomize backward branch instruction")
loop_branch_instr[i].comment = $sformatf("branch for loop %0d", i);
loop_branch_instr[i].imm_str = loop_branch_target_instr[i].label;
loop_branch_instr[i].branch_assigned = 1'b1;
end
// Randomly distribute the loop instruction in the existing instruction stream
build_loop_instr_stream();
mix_instr_stream(loop_instr, 1'b1);
foreach(instr_list[i]) begin
if(instr_list[i].label != "")
instr_list[i].has_label = 1'b1;
else
instr_list[i].has_label = 1'b0;
instr_list[i].atomic = 1'b1;
end
endfunction
// Build the whole loop structure from innermost loop to the outermost loop
function void build_loop_instr_stream();
loop_instr.delete;
for(int i = 0; i < num_of_nested_loop; i++) begin
loop_instr = {loop_init_instr[2*i],
loop_init_instr[2*i+1],
loop_branch_target_instr[i],
loop_update_instr[i],
loop_instr,
loop_branch_instr[i]};
end
`uvm_info(get_full_name(), $sformatf("Totally %0d instructions have been added",
loop_instr.size()), UVM_HIGH)
endfunction
endclass

View file

@ -67,3 +67,11 @@
`define DV_CHECK_STD_RANDOMIZE_WITH_FATAL(VAR_, WITH_C_,MSG_="Randomization failed!",ID_=`gfn) \
`DV_CHECK_FATAL(std::randomize(VAR_), MSG_, ID_, with { WITH_C_ })
`endif
// for vector processing
`ifndef VECTOR_INCLUDE
`define VECTOR_INCLUDE(VCE_INC) \
`ifdef ENABLE_VECTORS \
`include VCE_INC \
`endif
`endif

View file

@ -0,0 +1,60 @@
class riscv_amo_instr extends riscv_instr;
rand bit aq;
rand bit rl;
constraint aq_rl_c {
aq && rl == 0;
}
`uvm_object_utils(riscv_amo_instr)
function new(string name = "");
super.new(name);
endfunction
virtual function string get_instr_name();
get_instr_name = instr_name.name();
if (group == RV32A) begin
get_instr_name = {get_instr_name.substr(0, get_instr_name.len() - 3), ".w"};
get_instr_name = aq ? {get_instr_name, ".aq"} :
rl ? {get_instr_name, ".rl"} : get_instr_name;
end else if (group == RV64A) begin
get_instr_name = {get_instr_name.substr(0, get_instr_name.len() - 3), ".d"};
get_instr_name = aq ? {get_instr_name, ".aq"} :
rl ? {get_instr_name, ".rl"} : get_instr_name;
end else begin
`uvm_fatal(`gfn, $sformatf("Unexpected amo instr group: %0s / %0s",
group.name(), instr_name.name()))
end
return get_instr_name;
endfunction : get_instr_name
// Convert the instruction to assembly code
virtual function string convert2asm(string prefix = "");
string asm_str;
asm_str = format_string(get_instr_name(), MAX_INSTR_STR_LEN);
if (group inside {RV32A, RV64A}) begin
if (instr_name inside {LR_W, LR_D}) begin
asm_str = $sformatf("%0s %0s, (%0s)", asm_str, rd.name(), rs1.name());
end else begin
asm_str = $sformatf("%0s %0s, %0s, (%0s)", asm_str, rd.name(), rs2.name(), rs1.name());
end
end else begin
`uvm_fatal(`gfn, $sformatf("Unexpected amo instr group: %0s / %0s",
group.name(), instr_name.name()))
end
if(comment != "")
asm_str = {asm_str, " #",comment};
return asm_str.tolower();
endfunction : convert2asm
virtual function void do_copy(uvm_object rhs);
riscv_amo_instr rhs_;
super.copy(rhs);
assert($cast(rhs_, rhs));
this.aq = rhs_.aq;
this.rl = rhs_.rl;
endfunction : do_copy
endclass

View file

@ -0,0 +1,384 @@
class riscv_compressed_instr extends riscv_instr;
int imm_align;
constraint rvc_csr_c {
// Registers specified by the three-bit rs1, rs2, and rd
if (format inside {CIW_FORMAT, CL_FORMAT, CS_FORMAT, CB_FORMAT, CA_FORMAT}) {
if (has_rs1) {
rs1 inside {[S0:A5]};
}
if (has_rs2) {
rs2 inside {[S0:A5]};
}
if (has_rd) {
rd inside {[S0:A5]};
}
}
// C_ADDI16SP is only valid when rd == SP
if (instr_name == C_ADDI16SP) {
rd == SP;
}
if (instr_name inside {C_JR, C_JALR}) {
rs2 == ZERO;
rs1 != ZERO;
}
}
constraint imm_val_c {
if(imm_type inside {NZIMM, NZUIMM}) {
imm[5:0] != 0;
if (instr_name == C_LUI) {
// TODO(taliu) Check why bit 6 cannot be zero
imm[31:5] == 0;
}
if (instr_name inside {C_SRAI, C_SRLI, C_SLLI}) {
imm[31:5] == 0;
}
}
}
// C_JAL is RV32C only instruction
constraint jal_c {
if (XLEN != 32) {
instr_name != C_JAL;
}
}
// Avoid generating HINT or illegal instruction by default as it's not supported by the compiler
constraint no_hint_illegal_instr_c {
if (instr_name inside {C_ADDI, C_ADDIW, C_LI, C_LUI, C_SLLI, C_SLLI64,
C_LQSP, C_LDSP, C_MV, C_ADD, C_LWSP}) {
rd != ZERO;
}
if (instr_name == C_JR) {
rs1 != ZERO;
}
if (instr_name inside {C_ADD, C_MV}) {
rs2 != ZERO;
}
(instr_name == C_LUI) -> (rd != SP);
}
`uvm_object_utils(riscv_compressed_instr)
function new(string name = "");
super.new(name);
rs1 = S0;
rs2 = S0;
rd = S0;
is_compressed = 1'b1;
endfunction : new
virtual function void set_imm_len();
if (format inside {CI_FORMAT, CSS_FORMAT}) begin
imm_len = 6;
end else if (format inside {CL_FORMAT, CS_FORMAT}) begin
imm_len = 5;
end else if (format inside {CJ_FORMAT}) begin
imm_len = 11;
end else if (format inside {CB_FORMAT}) begin
if (instr_name == C_ANDI) begin
imm_len = 6;
end else begin
imm_len = 7;
end
end else if (format inside {CB_FORMAT, CIW_FORMAT}) begin
imm_len = 8;
end
if (instr_name inside {C_SQ, C_LQ, C_LQSP, C_SQSP, C_ADDI16SP}) begin
imm_align = 4;
end else if (instr_name inside {C_SD, C_LD, C_LDSP, C_SDSP}) begin
imm_align = 3;
end else if (instr_name inside {C_SW, C_LW, C_LWSP, C_SWSP, C_ADDI4SPN}) begin
imm_align = 2;
end else if (instr_name inside {C_LUI}) begin
imm_align = 12;
end else if (instr_name inside {C_J, C_JAL, C_BNEZ, C_BEQZ}) begin
imm_align = 1;
end
endfunction : set_imm_len
virtual function void do_copy(uvm_object rhs);
riscv_compressed_instr rhs_;
super.copy(rhs);
assert($cast(rhs_, rhs));
this.imm_align = rhs_.imm_align;
endfunction : do_copy
virtual function void extend_imm();
if (instr_name != C_LUI) begin
super.extend_imm();
imm = imm << imm_align;
end
endfunction : extend_imm
virtual function void set_rand_mode();
case (format) inside
CR_FORMAT : begin
if (category == JUMP) begin
has_rd = 1'b0;
end else begin
has_rs1 = 1'b0;
end
has_imm = 1'b0;
end
CI_FORMAT : begin
has_rs2 = 1'b0;
has_rs1 = 1'b0;
end
CSS_FORMAT : begin
has_rs1 = 1'b0;
has_rd = 1'b0;
end
CL_FORMAT: begin
has_rs2 = 1'b0;
end
CS_FORMAT : begin
has_rd = 1'b0;
end
CA_FORMAT: begin
has_rs1 = 1'b0;
has_imm = 1'b0;
end
CIW_FORMAT: begin
has_rs1 = 1'b0;
has_rs2 = 1'b0;
end
CJ_FORMAT: begin
has_rs1 = 1'b0;
has_rs2 = 1'b0;
has_rd = 1'b0;
end
CB_FORMAT: begin
if (instr_name != C_ANDI) has_rd = 1'b0;
has_rs2 = 1'b0;
end
endcase
endfunction
// Convert the instruction to assembly code
virtual function string convert2asm(string prefix = "");
string asm_str;
asm_str = format_string(get_instr_name(), MAX_INSTR_STR_LEN);
if (category != SYSTEM) begin
case(format)
CI_FORMAT, CIW_FORMAT:
if (instr_name == C_NOP)
asm_str = "c.nop";
else if (instr_name == C_ADDI16SP)
asm_str = $sformatf("%0ssp, %0s", asm_str, get_imm());
else if (instr_name inside {C_LDSP, C_LWSP, C_LQSP})
asm_str = $sformatf("%0s%0s, %0s(sp)", asm_str, rd.name(), get_imm());
else
asm_str = $sformatf("%0s%0s, %0s", asm_str, rd.name(), get_imm());
CL_FORMAT:
asm_str = $sformatf("%0s%0s, %0s(%0s)", asm_str, rd.name(), get_imm(), rs1.name());
CS_FORMAT:
if (category == STORE)
asm_str = $sformatf("%0s%0s, %0s(%0s)", asm_str, rs2.name(), get_imm(), rs1.name());
else
asm_str = $sformatf("%0s%0s, %0s", asm_str, rs1.name(), rs2.name());
CA_FORMAT:
asm_str = $sformatf("%0s%0s, %0s", asm_str, rd.name(), rs2.name());
CB_FORMAT:
asm_str = $sformatf("%0s%0s, %0s", asm_str, rs1.name(), get_imm());
CSS_FORMAT:
if (category == STORE)
asm_str = $sformatf("%0s%0s, %0s(sp)", asm_str, rs2.name(), get_imm());
else
asm_str = $sformatf("%0s%0s, %0s", asm_str, rs2.name(), get_imm());
CR_FORMAT:
if (instr_name inside {C_JR, C_JALR}) begin
asm_str = $sformatf("%0s%0s", asm_str, rs1.name());
end else begin
asm_str = $sformatf("%0s%0s, %0s", asm_str, rd.name(), rs2.name());
end
CJ_FORMAT:
asm_str = $sformatf("%0s%0s", asm_str, get_imm());
endcase
end else begin
// For EBREAK,C.EBREAK, making sure pc+4 is a valid instruction boundary
// This is needed to resume execution from epc+4 after ebreak handling
if (instr_name == C_EBREAK) begin
asm_str = "c.ebreak;c.nop;";
end
end
if (comment != "")
asm_str = {asm_str, " #",comment};
return asm_str.tolower();
endfunction : convert2asm
// Convert the instruction to assembly code
virtual function string convert2bin(string prefix = "");
string binary;
case (instr_name) inside
C_ADDI4SPN:
binary = $sformatf("%4h", {get_func3(), imm[5:4], imm[9:6],
imm[2], imm[3], get_c_gpr(rd), get_c_opcode()});
C_LQ:
binary = $sformatf("%4h", {get_func3(), imm[5:4], imm[8],
get_c_gpr(rs1), imm[7:6], get_c_gpr(rd), get_c_opcode()});
C_FLD, C_LD:
binary = $sformatf("%4h", {get_func3(), imm[5:3], get_c_gpr(rs1),
imm[7:6], get_c_gpr(rd), get_c_opcode()});
C_LW, C_FLW:
binary = $sformatf("%4h", {get_func3(), imm[5:3], get_c_gpr(rs1),
imm[2], imm[6], get_c_gpr(rd), get_c_opcode()});
C_SQ:
binary = $sformatf("%4h", {get_func3(), imm[5:4], imm[8],
get_c_gpr(rs1), imm[7:6], get_c_gpr(rs2), get_c_opcode()});
C_FSD, C_SD:
binary = $sformatf("%4h", {get_func3(), imm[5:3], get_c_gpr(rs1),
imm[7:6], get_c_gpr(rs2), get_c_opcode()});
C_SW, C_FSW:
binary = $sformatf("%4h", {get_func3(), imm[5:3], get_c_gpr(rs1),
imm[2], imm[6], get_c_gpr(rs2), get_c_opcode()});
C_NOP, C_ADDI, C_LI, C_ADDIW:
binary = $sformatf("%4h", {get_func3(), imm[5], rd, imm[4:0], get_c_opcode()});
C_JAL, C_J:
binary = $sformatf("%4h", {get_func3(), imm[11], imm[4], imm[9:8],
imm[10], imm[6], imm[7], imm[3:1], imm[5], get_c_opcode()});
C_ADDI16SP:
binary = $sformatf("%4h", {get_func3(), imm[9], 5'b10,
imm[4], imm[6], imm[8:7], imm[5], get_c_opcode()});
C_LUI:
binary = $sformatf("%4h", {get_func3(), imm[5], rd, imm[4:0], get_c_opcode()});
C_SRLI:
binary = $sformatf("%4h", {get_func3(), imm[5],
2'b0, get_c_gpr(rd), imm[4:0], get_c_opcode()});
C_SRLI64:
binary = $sformatf("%4h", {get_func3(), 3'b0, get_c_gpr(rd), 5'b0, get_c_opcode()});
C_SRAI:
binary = $sformatf("%4h", {get_func3(), imm[5],
2'b01, get_c_gpr(rd), imm[4:0], get_c_opcode()});
C_SRAI64:
binary = $sformatf("%4h", {get_func3(), 3'b001,
get_c_gpr(rd), 5'b0, get_c_opcode()});
C_ANDI:
binary = $sformatf("%4h", {get_func3(), imm[5],
2'b10, get_c_gpr(rd), imm[4:0], get_c_opcode()});
C_SUB:
binary = $sformatf("%4h", {get_func3(), 3'b011, get_c_gpr(rd),
2'b00, get_c_gpr(rs2), get_c_opcode()});
C_XOR:
binary = $sformatf("%4h", {get_func3(), 3'b011, get_c_gpr(rd),
2'b01, get_c_gpr(rs2), get_c_opcode()});
C_OR:
binary = $sformatf("%4h", {get_func3(), 3'b011, get_c_gpr(rd),
2'b10, get_c_gpr(rs2), get_c_opcode()});
C_AND:
binary = $sformatf("%4h", {get_func3(), 3'b011, get_c_gpr(rd),
2'b11, get_c_gpr(rs2), get_c_opcode()});
C_SUBW:
binary = $sformatf("%4h", {get_func3(), 3'b111, get_c_gpr(rd),
2'b00, get_c_gpr(rs2), get_c_opcode()});
C_ADDW:
binary = $sformatf("%4h", {get_func3(), 3'b111, get_c_gpr(rd),
2'b01, get_c_gpr(rs2), get_c_opcode()});
C_BEQZ, C_BNEZ:
binary = $sformatf("%4h", {get_func3(), imm[8], imm[4:3],
get_c_gpr(rs1), imm[7:6], imm[2:1], imm[5], get_c_opcode()});
C_SLLI:
binary = $sformatf("%4h", {get_func3(), imm[5], rd, imm[4:0], get_c_opcode()});
C_SLLI64:
binary = $sformatf("%4h", {get_func3(), 1'b0, rd, 5'b0, get_c_opcode()});
C_FLDSP, C_LDSP:
binary = $sformatf("%4h", {get_func3(), imm[5], rd, imm[4:3], imm[8:6], get_c_opcode()});
C_LQSP:
binary = $sformatf("%4h", {get_func3(), imm[5], rd, imm[4], imm[9:6], get_c_opcode()});
C_LWSP, C_FLWSP:
binary = $sformatf("%4h", {get_func3(), imm[5], rd, imm[4:2], imm[7:6], get_c_opcode()});
C_JR:
binary = $sformatf("%4h", {get_func3(), 1'b0, rs1, 5'b0, get_c_opcode()});
C_MV:
binary = $sformatf("%4h", {get_func3(), 1'b0, rd, rs2, get_c_opcode()});
C_EBREAK:
binary = $sformatf("%4h", {get_func3(), 1'b1, 10'b0, get_c_opcode()});
C_JALR:
binary = $sformatf("%4h", {get_func3(), 1'b1, 10'b0, get_c_opcode()});
C_ADD:
binary = $sformatf("%4h", {get_func3(), 1'b1, rd, rs2, get_c_opcode()});
C_FSDSP, C_SDSP:
binary = $sformatf("%4h", {get_func3(), 1'b0, imm[5:3], imm[8:6], rs2, get_c_opcode()});
C_SQSP:
binary = $sformatf("%4h", {get_func3(), 1'b0, imm[5:4], imm[9:6], rs2, get_c_opcode()});
C_SWSP, C_FSWSP:
binary = $sformatf("%4h", {get_func3(), 1'b0, imm[5:2], imm[7:6], rs2, get_c_opcode()});
default : `uvm_fatal(`gfn, $sformatf("Unsupported instruction %0s", instr_name.name()))
endcase
return {prefix, binary};
endfunction : convert2bin
// Get opcode for compressed instruction
virtual function bit [1:0] get_c_opcode();
case (instr_name) inside
C_ADDI4SPN, C_FLD, C_FLD, C_LQ, C_LW, C_FLW,
C_LD, C_FSD, C_SQ, C_SW, C_FSW, C_SD : get_c_opcode = 2'b00;
C_NOP, C_ADDI, C_JAL, C_ADDIW, C_LI, C_ADDI16SP,
C_LUI, C_SRLI, C_SRLI64, C_SRAI, C_SRAI64,
C_ANDI, C_SUB, C_XOR, C_OR, C_AND, C_SUBW,
C_ADDW, C_J, C_BEQZ, C_BNEZ : get_c_opcode = 2'b01;
C_SLLI, C_SLLI64, C_FLDSP, C_LQSP, C_LWSP,
C_FLWSP, C_LDSP, C_JR, C_MV, C_EBREAK, C_JALR,
C_ADD, C_FSDSP, C_SQSP, C_SWSP, C_FSWSP, C_SDSP : get_c_opcode = 2'b10;
default : `uvm_fatal(`gfn, $sformatf("Unsupported instruction %0s", instr_name.name()))
endcase
endfunction : get_c_opcode
virtual function bit [2:0] get_func3();
case (instr_name) inside
C_ADDI4SPN : get_func3 = 3'b000;
C_FLD : get_func3 = 3'b001;
C_LQ : get_func3 = 3'b001;
C_LW : get_func3 = 3'b010;
C_FLW : get_func3 = 3'b011;
C_LD : get_func3 = 3'b011;
C_FSD : get_func3 = 3'b101;
C_SQ : get_func3 = 3'b101;
C_SW : get_func3 = 3'b110;
C_FSW : get_func3 = 3'b111;
C_SD : get_func3 = 3'b111;
C_NOP : get_func3 = 3'b000;
C_ADDI : get_func3 = 3'b000;
C_JAL : get_func3 = 3'b001;
C_ADDIW : get_func3 = 3'b001;
C_LI : get_func3 = 3'b010;
C_ADDI16SP : get_func3 = 3'b011;
C_LUI : get_func3 = 3'b011;
C_SRLI : get_func3 = 3'b100;
C_SRLI64 : get_func3 = 3'b100;
C_SRAI : get_func3 = 3'b100;
C_SRAI64 : get_func3 = 3'b100;
C_ANDI : get_func3 = 3'b100;
C_SUB : get_func3 = 3'b100;
C_XOR : get_func3 = 3'b100;
C_OR : get_func3 = 3'b100;
C_AND : get_func3 = 3'b100;
C_SUBW : get_func3 = 3'b100;
C_ADDW : get_func3 = 3'b100;
C_J : get_func3 = 3'b101;
C_BEQZ : get_func3 = 3'b110;
C_BNEZ : get_func3 = 3'b111;
C_SLLI : get_func3 = 3'b000;
C_SLLI64 : get_func3 = 3'b000;
C_FLDSP : get_func3 = 3'b001;
C_LQSP : get_func3 = 3'b001;
C_LWSP : get_func3 = 3'b010;
C_FLWSP : get_func3 = 3'b011;
C_LDSP : get_func3 = 3'b011;
C_JR : get_func3 = 3'b100;
C_MV : get_func3 = 3'b100;
C_EBREAK : get_func3 = 3'b100;
C_JALR : get_func3 = 3'b100;
C_ADD : get_func3 = 3'b100;
C_FSDSP : get_func3 = 3'b101;
C_SQSP : get_func3 = 3'b101;
C_SWSP : get_func3 = 3'b110;
C_FSWSP : get_func3 = 3'b111;
C_SDSP : get_func3 = 3'b111;
default : `uvm_fatal(`gfn, $sformatf("Unsupported instruction %0s", instr_name.name()))
endcase
endfunction : get_func3
endclass : riscv_compressed_instr

View file

@ -0,0 +1,70 @@
class riscv_floating_point_instr extends riscv_instr;
rand riscv_fpr_t fs1;
rand riscv_fpr_t fs2;
rand riscv_fpr_t fs3;
rand riscv_fpr_t fd;
`uvm_object_utils(riscv_floating_point_instr)
function new(string name = "");
super.new(name);
rs2.rand_mode(0);
endfunction
// Convert the instruction to assembly code
virtual function string convert2asm(string prefix = "");
string asm_str;
asm_str = format_string(get_instr_name(), MAX_INSTR_STR_LEN);
case (format)
I_FORMAT:
if (category == LOAD) begin
asm_str = $sformatf("%0s%0s, %0s(%0s)", asm_str, fd.name(), get_imm(), rs1.name());
end else if (instr_name inside {FMV_X_W, FMV_X_D, FCVT_W_S, FCVT_WU_S,
FCVT_L_S, FCVT_LU_S, FCVT_L_D, FCVT_LU_D, FCVT_LU_S,
FCVT_W_D, FCVT_WU_D}) begin
asm_str = $sformatf("%0s%0s, %0s", asm_str, rd.name(), fs1.name());
end else if (instr_name inside {FMV_W_X, FMV_D_X, FCVT_S_W, FCVT_S_WU,
FCVT_S_L, FCVT_D_L, FCVT_S_LU, FCVT_D_W,
FCVT_D_LU, FCVT_D_WU}) begin
asm_str = $sformatf("%0s%0s, %0s", asm_str, fd.name(), rs1.name());
end else begin
asm_str = $sformatf("%0s%0s, %0s", asm_str, fd.name(), fs1.name());
end
S_FORMAT:
asm_str = $sformatf("%0s%0s, %0s(%0s)", asm_str, fs2.name(), get_imm(), rs1.name());
R_FORMAT:
if (category == COMPARE) begin
asm_str = $sformatf("%0s%0s, %0s, %0s", asm_str, rd.name(), fs1.name(), fs2.name());
end else if (instr_name inside {FCLASS_S, FCLASS_D}) begin
asm_str = $sformatf("%0s%0s, %0s", asm_str, rd.name(), fs1.name());
end else begin
asm_str = $sformatf("%0s%0s, %0s, %0s", asm_str, fd.name(), fs1.name(), fs2.name());
end
R4_FORMAT:
asm_str = $sformatf("%0s%0s, %0s, %0s, %0s", asm_str, fd.name(), fs1.name(),
fs2.name(), fs3.name());
CL_FORMAT:
asm_str = $sformatf("%0s%0s, %0s(%0s)", asm_str, fd.name(), get_imm(), rs1.name());
CS_FORMAT:
asm_str = $sformatf("%0s%0s, %0s(%0s)", asm_str, fs2.name(), get_imm(), rs1.name());
default:
`uvm_fatal(`gfn, $sformatf("Unsupported floating point format: %0s", format.name()))
endcase
if(comment != "")
asm_str = {asm_str, " #",comment};
return asm_str.tolower();
endfunction
virtual function void do_copy(uvm_object rhs);
riscv_floating_point_instr rhs_;
super.copy(rhs);
assert($cast(rhs_, rhs));
this.fs3 = rhs_.fs3;
this.fs2 = rhs_.fs2;
this.fs1 = rhs_.fs1;
this.fd = rhs_.fd;
endfunction : do_copy
endclass

View file

@ -0,0 +1,622 @@
/*
* Copyright 2019 Google LLC
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
class riscv_instr extends uvm_object;
// All derived instructions
static bit instr_registry[riscv_instr_name_t];
// Instruction list
static riscv_instr_name_t instr_names[$];
// Categorized instruction list
static riscv_instr_name_t instr_group[riscv_instr_group_t][$];
static riscv_instr_name_t instr_category[riscv_instr_category_t][$];
static riscv_instr_name_t basic_instr[$];
static riscv_instr instr_template[riscv_instr_name_t];
// Privileged CSR filter
static privileged_reg_t exclude_reg[];
static privileged_reg_t include_reg[];
// Instruction attributes
riscv_instr_group_t group;
riscv_instr_format_t format;
riscv_instr_category_t category;
riscv_instr_name_t instr_name;
imm_t imm_type;
bit [4:0] imm_len;
// Operands
rand bit [11:0] csr;
rand riscv_reg_t rs2;
rand riscv_reg_t rs1;
rand riscv_reg_t rd;
rand bit [31:0] imm;
// Helper fields
bit [31:0] imm_mask = 32'hFFFF_FFFF;
bit is_branch_target;
bit has_label = 1'b1;
bit atomic = 0;
bit branch_assigned;
bit process_load_store = 1'b1;
bit is_compressed;
bit is_illegal_instr;
bit is_hint_instr;
bit is_floating_point;
string imm_str;
string comment;
string label;
bit is_local_numeric_label;
int idx = -1;
bit has_rs1 = 1'b1;
bit has_rs2 = 1'b1;
bit has_rd = 1'b1;
bit has_imm = 1'b1;
constraint imm_c {
if (instr_name inside {SLLI, SRLI, SRAI, SLLIW, SRLIW, SRAIW}) {
imm[11:5] == 0;
}
}
constraint csr_c {
if (category == CSR) {
if (include_reg.size() > 0) {
csr inside {include_reg};
}
if (exclude_reg.size() > 0) {
!(csr inside {exclude_reg});
}
}
}
`uvm_object_utils(riscv_instr)
`uvm_object_new
static function bit register(riscv_instr_name_t instr_name);
`uvm_info("riscv_instr", $sformatf("Registering %0s", instr_name.name()), UVM_LOW)
instr_registry[instr_name] = 1;
return 1;
endfunction : register
// Create the list of instructions based on the supported ISA extensions and configuration of the
// generator.
static function void create_instr_list(riscv_instr_gen_config cfg);
instr_names.delete();
instr_group.delete();
instr_category.delete();
foreach (instr_registry[instr_name]) begin
riscv_instr instr_inst;
instr_inst = create_instr(instr_name);
instr_template[instr_name] = instr_inst;
// C_JAL is RV32C only instruction
if ((XLEN != 32) && (instr_name == C_JAL)) continue;
if ((SP inside {cfg.reserved_regs}) && (instr_name inside {C_ADDI16SP})) begin
continue;
end
if (!cfg.enable_sfence && instr_name == SFENCE_VMA) continue;
if (cfg.no_fence && (instr_name inside {FENCE, FENCE_I, SFENCE_VMA})) continue;
// TODO: gcc compile issue, support c.addi4spn later
if (instr_name == C_ADDI4SPN) continue;
if ((instr_inst.group inside {supported_isa}) &&
!(cfg.disable_compressed_instr &&
(instr_inst.group inside {RV32C, RV64C, RV32DC, RV32FC, RV128C})) &&
!(!cfg.enable_floating_point &&
(instr_inst.group inside {RV32F, RV64F, RV32D, RV64D}))) begin
instr_category[instr_inst.category].push_back(instr_name);
instr_group[instr_inst.group].push_back(instr_name);
instr_names.push_back(instr_name);
end
end
build_basic_instruction_list(cfg);
create_csr_filter(cfg);
endfunction : create_instr_list
static function void create_csr_filter(riscv_instr_gen_config cfg);
include_reg.delete();
exclude_reg.delete();
if (cfg.enable_illegal_csr_instruction) begin
exclude_reg = implemented_csr;
end else if (cfg.enable_access_invalid_csr_level) begin
include_reg = cfg.invalid_priv_mode_csrs;
end else begin
// Use scratch register to avoid the side effect of modifying other privileged mode CSR.
if (cfg.init_privileged_mode == MACHINE_MODE) begin
include_reg = {MSCRATCH};
end else if (cfg.init_privileged_mode == SUPERVISOR_MODE) begin
include_reg = {SSCRATCH};
end else begin
include_reg = {USCRATCH};
end
end
endfunction : create_csr_filter
static function riscv_instr create_instr(riscv_instr_name_t instr_name);
uvm_object obj;
riscv_instr inst;
string instr_class_name;
uvm_coreservice_t coreservice = uvm_coreservice_t::get();
uvm_factory factory = coreservice.get_factory();
instr_class_name = {"riscv_", instr_name.name(), "_instr"};
obj = factory.create_object_by_name(instr_class_name, "riscv_instr", instr_class_name);
if (obj == null) begin
`uvm_fatal("riscv_instr", $sformatf("Failed to create instr: %0s", instr_class_name))
end
if (!$cast(inst, obj)) begin
`uvm_fatal("riscv_instr", $sformatf("Failed to cast instr: %0s", instr_class_name))
end
return inst;
endfunction : create_instr
static function void build_basic_instruction_list(riscv_instr_gen_config cfg);
basic_instr = {instr_category[SHIFT], instr_category[ARITHMETIC],
instr_category[LOGICAL], instr_category[COMPARE]};
if (!cfg.no_ebreak) begin
basic_instr = {basic_instr, EBREAK};
foreach (riscv_instr_pkg::supported_isa[i]) begin
if (RV32C inside {riscv_instr_pkg::supported_isa[i]}) begin
basic_instr = {basic_instr, C_EBREAK};
break;
end
end
end
if (cfg.no_dret == 0) begin
basic_instr = {basic_instr, DRET};
end
if (cfg.no_fence == 0) begin
basic_instr = {basic_instr, instr_category[SYNCH]};
end
if ((cfg.no_csr_instr == 0) && (cfg.init_privileged_mode == MACHINE_MODE)) begin
basic_instr = {basic_instr, instr_category[CSR]};
end
if (cfg.no_wfi == 0) begin
basic_instr = {basic_instr, WFI};
end
endfunction : build_basic_instruction_list
static function riscv_instr get_rand_instr(riscv_instr instr_h = null,
riscv_instr_name_t include_instr[] = {},
riscv_instr_name_t exclude_instr[] = {},
riscv_instr_category_t include_category[] = {},
riscv_instr_category_t exclude_category[] = {},
riscv_instr_group_t include_group[] = {},
riscv_instr_group_t exclude_group[] = {});
int unsigned idx;
riscv_instr_name_t name;
riscv_instr_name_t allowed_instr[];
riscv_instr_name_t disallowed_instr[];
riscv_instr_category_t allowed_categories[];
foreach (include_category[i]) begin
allowed_instr = {allowed_instr, instr_category[include_category[i]]};
end
foreach (exclude_category[i]) begin
disallowed_instr = {disallowed_instr, instr_category[exclude_category[i]]};
end
foreach (include_group[i]) begin
allowed_instr = {allowed_instr, instr_group[include_group[i]]};
end
foreach (exclude_group[i]) begin
disallowed_instr = {disallowed_instr, instr_group[exclude_group[i]]};
end
disallowed_instr = {disallowed_instr, exclude_instr};
if (disallowed_instr.size() == 0) begin
if (include_instr.size() > 0) begin
idx = $urandom_range(0, include_instr.size()-1);
name = include_instr[idx];
end else if (allowed_instr.size() > 0) begin
idx = $urandom_range(0, allowed_instr.size()-1);
name = allowed_instr[idx];
end else begin
idx = $urandom_range(0, instr_names.size()-1);
name = instr_names[idx];
end
end else begin
if (!std::randomize(name) with {
name inside {instr_names};
if (include_instr.size() > 0) {
name inside {include_instr};
}
if (allowed_instr.size() > 0) {
name inside {allowed_instr};
}
if (disallowed_instr.size() > 0) {
!(name inside {disallowed_instr});
}
}) begin
`uvm_fatal("riscv_instr", "Cannot generate random instruction")
end
end
// Shallow copy for all relevant fields, avoid using create() to improve performance
instr_h = new instr_template[name];
return instr_h;
endfunction : get_rand_instr
static function riscv_instr get_load_store_instr(riscv_instr_name_t load_store_instr[] = {});
riscv_instr instr_h;
int unsigned idx;
riscv_instr_name_t name;
if (load_store_instr.size() == 0) begin
load_store_instr = {instr_category[LOAD], instr_category[STORE]};
end
idx = $urandom_range(0, load_store_instr.size()-1);
name = load_store_instr[idx];
// Shallow copy for all relevant fields, avoid using create() to improve performance
instr_h = new instr_template[name];
return instr_h;
endfunction : get_load_store_instr
// Disable the rand mode for unused operands to randomization performance
virtual function void set_rand_mode();
case (format) inside
R_FORMAT : has_imm = 1'b0;
I_FORMAT : has_rs2 = 1'b0;
S_FORMAT, B_FORMAT : has_rd = 1'b0;
U_FORMAT, J_FORMAT : begin
has_rs1 = 1'b0;
has_rs2 = 1'b0;
end
endcase
if (category == CSR) begin
has_rs2 = 1'b0;
if (format == I_FORMAT) begin
has_rs1 = 1'b0;
end
end
endfunction
function void pre_randomize();
rs1.rand_mode(has_rs1);
rs2.rand_mode(has_rs2);
rd.rand_mode(has_rd);
imm.rand_mode(has_imm);
if (category != CSR) begin
csr.rand_mode(0);
end
endfunction
virtual function void set_imm_len();
if(format inside {U_FORMAT, J_FORMAT}) begin
imm_len = 20;
end else if(format inside {I_FORMAT, S_FORMAT, B_FORMAT}) begin
if(imm_type == UIMM) begin
imm_len = 5;
end else begin
imm_len = 11;
end
end
imm_mask = imm_mask << imm_len;
endfunction
virtual function void extend_imm();
bit sign;
imm = imm << (32 - imm_len);
sign = imm[31];
imm = imm >> (32 - imm_len);
// Signed extension
if (sign && !((format == U_FORMAT) || (imm_type inside {UIMM, NZUIMM}))) begin
imm = imm_mask | imm;
end
endfunction : extend_imm
function void post_randomize();
extend_imm();
update_imm_str();
endfunction : post_randomize
// Convert the instruction to assembly code
virtual function string convert2asm(string prefix = "");
string asm_str;
asm_str = format_string(get_instr_name(), MAX_INSTR_STR_LEN);
if(category != SYSTEM) begin
case(format)
J_FORMAT, U_FORMAT : // instr rd,imm
asm_str = $sformatf("%0s%0s, %0s", asm_str, rd.name(), get_imm());
I_FORMAT: // instr rd,rs1,imm
if(instr_name == NOP)
asm_str = "nop";
else if(instr_name == WFI)
asm_str = "wfi";
else if(instr_name == FENCE)
asm_str = $sformatf("fence"); // TODO: Support all fence combinations
else if(instr_name == FENCE_I)
asm_str = "fence.i";
else if(category == LOAD) // Use psuedo instruction format
asm_str = $sformatf("%0s%0s, %0s(%0s)", asm_str, rd.name(), get_imm(), rs1.name());
else if(category == CSR)
asm_str = $sformatf("%0s%0s, 0x%0x, %0s", asm_str, rd.name(), csr, get_imm());
else
asm_str = $sformatf("%0s%0s, %0s, %0s", asm_str, rd.name(), rs1.name(), get_imm());
S_FORMAT, B_FORMAT: // instr rs1,rs2,imm
if(category == STORE) // Use psuedo instruction format
asm_str = $sformatf("%0s%0s, %0s(%0s)", asm_str, rs2.name(), get_imm(), rs1.name());
else
asm_str = $sformatf("%0s%0s, %0s, %0s", asm_str, rs1.name(), rs2.name(), get_imm());
R_FORMAT: // instr rd,rs1,rs2
if(category == CSR) begin
asm_str = $sformatf("%0s%0s, 0x%0x, %0s", asm_str, rd.name(), csr, rs1.name());
end else if(instr_name == SFENCE_VMA) begin
asm_str = "sfence.vma x0, x0"; // TODO: Support all possible sfence
end else begin
asm_str = $sformatf("%0s%0s, %0s, %0s", asm_str, rd.name(), rs1.name(), rs2.name());
end
endcase
end else begin
// For EBREAK,C.EBREAK, making sure pc+4 is a valid instruction boundary
// This is needed to resume execution from epc+4 after ebreak handling
if(instr_name == EBREAK) begin
asm_str = ".4byte 0x00100073 # ebreak";
end
end
if(comment != "")
asm_str = {asm_str, " #",comment};
return asm_str.tolower();
endfunction
function bit [6:0] get_opcode();
case (instr_name) inside
LUI : get_opcode = 7'b0110111;
AUIPC : get_opcode = 7'b0010111;
JAL : get_opcode = 7'b1101111;
JALR : get_opcode = 7'b1100111;
BEQ, BNE, BLT, BGE, BLTU, BGEU : get_opcode = 7'b1100011;
LB, LH, LW, LBU, LHU, LWU, LD : get_opcode = 7'b0000011;
SB, SH, SW, SD : get_opcode = 7'b0100011;
ADDI, SLTI, SLTIU, XORI, ORI, ANDI, SLLI, SRLI, SRAI, NOP : get_opcode = 7'b0010011;
ADD, SUB, SLL, SLT, SLTU, XOR, SRL, SRA, OR, AND, MUL,
MULH, MULHSU, MULHU, DIV, DIVU, REM, REMU : get_opcode = 7'b0110011;
ADDIW, SLLIW, SRLIW, SRAIW : get_opcode = 7'b0011011;
MULH, MULHSU, MULHU, DIV, DIVU, REM, REMU : get_opcode = 7'b0110011;
FENCE, FENCE_I : get_opcode = 7'b0001111;
ECALL, EBREAK, CSRRW, CSRRS, CSRRC, CSRRWI, CSRRSI, CSRRCI : get_opcode = 7'b1110011;
ADDW, SUBW, SLLW, SRLW, SRAW, MULW, DIVW, DIVUW, REMW, REMUW : get_opcode = 7'b0111011;
ECALL, EBREAK, URET, SRET, MRET, DRET, WFI, SFENCE_VMA : get_opcode = 7'b1110011;
default : `uvm_fatal(`gfn, $sformatf("Unsupported instruction %0s", instr_name.name()))
endcase
endfunction
virtual function bit [2:0] get_func3();
case (instr_name) inside
JALR : get_func3 = 3'b000;
BEQ : get_func3 = 3'b000;
BNE : get_func3 = 3'b001;
BLT : get_func3 = 3'b100;
BGE : get_func3 = 3'b101;
BLTU : get_func3 = 3'b110;
BGEU : get_func3 = 3'b111;
LB : get_func3 = 3'b000;
LH : get_func3 = 3'b001;
LW : get_func3 = 3'b010;
LBU : get_func3 = 3'b100;
LHU : get_func3 = 3'b101;
SB : get_func3 = 3'b000;
SH : get_func3 = 3'b001;
SW : get_func3 = 3'b010;
ADDI : get_func3 = 3'b000;
NOP : get_func3 = 3'b000;
SLTI : get_func3 = 3'b010;
SLTIU : get_func3 = 3'b011;
XORI : get_func3 = 3'b100;
ORI : get_func3 = 3'b110;
ANDI : get_func3 = 3'b111;
SLLI : get_func3 = 3'b001;
SRLI : get_func3 = 3'b101;
SRAI : get_func3 = 3'b101;
ADD : get_func3 = 3'b000;
SUB : get_func3 = 3'b000;
SLL : get_func3 = 3'b001;
SLT : get_func3 = 3'b010;
SLTU : get_func3 = 3'b011;
XOR : get_func3 = 3'b100;
SRL : get_func3 = 3'b101;
SRA : get_func3 = 3'b101;
OR : get_func3 = 3'b110;
AND : get_func3 = 3'b111;
FENCE : get_func3 = 3'b000;
FENCE_I : get_func3 = 3'b001;
ECALL : get_func3 = 3'b000;
EBREAK : get_func3 = 3'b000;
CSRRW : get_func3 = 3'b001;
CSRRS : get_func3 = 3'b010;
CSRRC : get_func3 = 3'b011;
CSRRWI : get_func3 = 3'b101;
CSRRSI : get_func3 = 3'b110;
CSRRCI : get_func3 = 3'b111;
LWU : get_func3 = 3'b110;
LD : get_func3 = 3'b011;
SD : get_func3 = 3'b011;
ADDIW : get_func3 = 3'b000;
SLLIW : get_func3 = 3'b001;
SRLIW : get_func3 = 3'b101;
SRAIW : get_func3 = 3'b101;
ADDW : get_func3 = 3'b000;
SUBW : get_func3 = 3'b000;
SLLW : get_func3 = 3'b001;
SRLW : get_func3 = 3'b101;
SRAW : get_func3 = 3'b101;
MUL : get_func3 = 3'b000;
MULH : get_func3 = 3'b001;
MULHSU : get_func3 = 3'b010;
MULHU : get_func3 = 3'b011;
DIV : get_func3 = 3'b100;
DIVU : get_func3 = 3'b101;
REM : get_func3 = 3'b110;
REMU : get_func3 = 3'b111;
MULW : get_func3 = 3'b000;
DIVW : get_func3 = 3'b100;
DIVUW : get_func3 = 3'b101;
REMW : get_func3 = 3'b110;
REMUW : get_func3 = 3'b111;
ECALL, EBREAK, URET, SRET, MRET, DRET, WFI, SFENCE_VMA : get_func3 = 3'b000;
default : `uvm_fatal(`gfn, $sformatf("Unsupported instruction %0s", instr_name.name()))
endcase
endfunction
function bit [6:0] get_func7();
case (instr_name)
SLLI : get_func7 = 7'b0000000;
SRLI : get_func7 = 7'b0000000;
SRAI : get_func7 = 7'b0100000;
ADD : get_func7 = 7'b0000000;
SUB : get_func7 = 7'b0100000;
SLL : get_func7 = 7'b0000000;
SLT : get_func7 = 7'b0000000;
SLTU : get_func7 = 7'b0000000;
XOR : get_func7 = 7'b0000000;
SRL : get_func7 = 7'b0000000;
SRA : get_func7 = 7'b0100000;
OR : get_func7 = 7'b0000000;
AND : get_func7 = 7'b0000000;
FENCE : get_func7 = 7'b0000000;
FENCE_I : get_func7 = 7'b0000000;
SLLIW : get_func7 = 7'b0000000;
SRLIW : get_func7 = 7'b0000000;
SRAIW : get_func7 = 7'b0100000;
ADDW : get_func7 = 7'b0000000;
SUBW : get_func7 = 7'b0100000;
SLLW : get_func7 = 7'b0000000;
SRLW : get_func7 = 7'b0000000;
SRAW : get_func7 = 7'b0100000;
MUL : get_func7 = 7'b0000001;
MULH : get_func7 = 7'b0000001;
MULHSU : get_func7 = 7'b0000001;
MULHU : get_func7 = 7'b0000001;
DIV : get_func7 = 7'b0000001;
DIVU : get_func7 = 7'b0000001;
REM : get_func7 = 7'b0000001;
REMU : get_func7 = 7'b0000001;
MULW : get_func7 = 7'b0000001;
DIVW : get_func7 = 7'b0000001;
DIVUW : get_func7 = 7'b0000001;
REMW : get_func7 = 7'b0000001;
REMUW : get_func7 = 7'b0000001;
ECALL : get_func7 = 7'b0000000;
EBREAK : get_func7 = 7'b0000000;
URET : get_func7 = 7'b0000000;
SRET : get_func7 = 7'b0001000;
MRET : get_func7 = 7'b0011000;
DRET : get_func7 = 7'b0111101;
WFI : get_func7 = 7'b0001000;
SFENCE_VMA: get_func7 = 7'b0001001;
default : `uvm_fatal(`gfn, $sformatf("Unsupported instruction %0s", instr_name.name()))
endcase
endfunction
// Convert the instruction to assembly code
virtual function string convert2bin(string prefix = "");
string binary;
case(format)
J_FORMAT: begin
binary = $sformatf("%8h", {imm[20], imm[10:1], imm[11], imm[19:12], rd, get_opcode()});
end
U_FORMAT: begin
binary = $sformatf("%8h", {imm[31:12], rd, get_opcode()});
end
I_FORMAT: begin
if(instr_name inside {FENCE, FENCE_I})
binary = $sformatf("%8h", {17'b0, get_func3(), 5'b0, get_opcode()});
else if(category == CSR)
binary = $sformatf("%8h", {csr[10:0], imm[4:0], get_func3(), rd, get_opcode()});
else if(instr_name == ECALL)
binary = $sformatf("%8h", {get_func7(), 18'b0, get_opcode()});
else if(instr_name inside {URET, SRET, MRET})
binary = $sformatf("%8h", {get_func7(), 5'b10, 13'b0, get_opcode()});
else if(instr_name inside {DRET})
binary = $sformatf("%8h", {get_func7(), 5'b10010, 13'b0, get_opcode()});
else if(instr_name == EBREAK)
binary = $sformatf("%8h", {get_func7(), 5'b01, 13'b0, get_opcode()});
else if(instr_name == WFI)
binary = $sformatf("%8h", {get_func7(), 5'b101, 13'b0, get_opcode()});
else
binary = $sformatf("%8h", {imm[11:0], rs1, get_func3(), rd, get_opcode()});
end
S_FORMAT: begin
binary = $sformatf("%8h", {imm[11:5], rs2, rs1, get_func3(), imm[4:0], get_opcode()});
end
B_FORMAT: begin
binary = $sformatf("%8h",
{imm[12], imm[10:5], rs2, rs1, get_func3(),
imm[4:1], imm[11], get_opcode()});
end
R_FORMAT: begin
if(category == CSR)
binary = $sformatf("%8h", {csr[10:0], rs1, get_func3(), rd, get_opcode()});
else if(instr_name == SFENCE_VMA)
binary = $sformatf("%8h", {get_func7(), 18'b0, get_opcode()});
else
binary = $sformatf("%8h", {get_func7(), rs2, rs1, get_func3(), rd, get_opcode()});
end
endcase
return {prefix, binary};
endfunction
virtual function string get_instr_name();
get_instr_name = instr_name.name();
foreach(get_instr_name[i]) begin
if (get_instr_name[i] == "_") begin
get_instr_name[i] = ".";
end
end
return get_instr_name;
endfunction
// Get RVC register name for CIW, CL, CS, CB format
function bit [2:0] get_c_gpr(riscv_reg_t gpr);
return gpr[2:0];
endfunction
// Default return imm value directly, can be overriden to use labels and symbols
// Example: %hi(symbol), %pc_rel(label) ...
virtual function string get_imm();
return imm_str;
endfunction
virtual function void clear_unused_label();
if(has_label && !is_branch_target && is_local_numeric_label) begin
has_label = 1'b0;
end
endfunction
virtual function void do_copy(uvm_object rhs);
riscv_instr rhs_;
super.copy(rhs);
assert($cast(rhs_, rhs));
this.group = rhs_.group;
this.format = rhs_.format;
this.category = rhs_.category;
this.instr_name = rhs_.instr_name;
this.rs2 = rhs_.rs2;
this.rs1 = rhs_.rs1;
this.rd = rhs_.rd;
this.imm = rhs_.imm;
this.imm_type = rhs_.imm_type;
this.imm_len = rhs_.imm_len;
this.imm_mask = rhs_.imm_mask;
this.imm_str = rhs_.imm_str;
this.imm_mask = rhs_.imm_mask;
this.is_compressed = rhs_.is_compressed;
this.has_rs2 = rhs_.has_rs2;
this.has_rs1 = rhs_.has_rs1;
this.has_rd = rhs_.has_rd;
this.has_imm = rhs_.has_imm;
endfunction : do_copy
virtual function void update_imm_str();
imm_str = $sformatf("%0d", $signed(imm));
endfunction
endclass

View file

@ -0,0 +1,25 @@
class riscv_vector_instr extends riscv_instr;
// TODO : Add vector instruction operands here
`uvm_object_utils(riscv_vector_instr)
function new(string name = "");
super.new(name);
endfunction
virtual function string get_instr_name();
// TODO : Implement this function for vector instructions
endfunction
// Convert the instruction to assembly code
virtual function string convert2asm(string prefix = "");
string asm_str;
asm_str = format_string(get_instr_name(), MAX_INSTR_STR_LEN);
// TODO : Implement this function for vector instructions
if(comment != "")
asm_str = {asm_str, " #",comment};
return asm_str.tolower();
endfunction
endclass

View file

@ -0,0 +1,7 @@
`DEFINE_C_INSTR(C_SRLI64, CB_FORMAT, SHIFT, RV128C, NZUIMM)
`DEFINE_C_INSTR(C_SRAI64, CB_FORMAT, SHIFT, RV128C, NZUIMM)
`DEFINE_C_INSTR(C_SLLI64, CI_FORMAT, SHIFT, RV128C, NZUIMM)
`DEFINE_C_INSTR(C_LQ, CL_FORMAT, LOAD, RV32DC, UIMM)
`DEFINE_C_INSTR(C_SQ, CS_FORMAT, STORE, RV32DC, UIMM)
`DEFINE_C_INSTR(C_LQSP, CI_FORMAT, LOAD, RV32DC, UIMM)
`DEFINE_C_INSTR(C_SQSP, CSS_FORMAT, STORE, RV32DC, UIMM)

View file

@ -0,0 +1,11 @@
`DEFINE_AMO_INSTR(LR_W, R_FORMAT, LOAD, RV32A)
`DEFINE_AMO_INSTR(SC_W, R_FORMAT, STORE, RV32A)
`DEFINE_AMO_INSTR(AMOSWAP_W, R_FORMAT, AMO, RV32A)
`DEFINE_AMO_INSTR(AMOADD_W, R_FORMAT, AMO, RV32A)
`DEFINE_AMO_INSTR(AMOAND_W, R_FORMAT, AMO, RV32A)
`DEFINE_AMO_INSTR(AMOOR_W, R_FORMAT, AMO, RV32A)
`DEFINE_AMO_INSTR(AMOXOR_W, R_FORMAT, AMO, RV32A)
`DEFINE_AMO_INSTR(AMOMIN_W, R_FORMAT, AMO, RV32A)
`DEFINE_AMO_INSTR(AMOMAX_W, R_FORMAT, AMO, RV32A)
`DEFINE_AMO_INSTR(AMOMINU_W, R_FORMAT, AMO, RV32A)
`DEFINE_AMO_INSTR(AMOMAXU_W, R_FORMAT, AMO, RV32A)

View file

@ -0,0 +1,27 @@
`DEFINE_C_INSTR(C_LW, CL_FORMAT, LOAD, RV32C, UIMM)
`DEFINE_C_INSTR(C_SW, CS_FORMAT, STORE, RV32C, UIMM)
`DEFINE_C_INSTR(C_LWSP, CI_FORMAT, LOAD, RV32C, UIMM)
`DEFINE_C_INSTR(C_SWSP, CSS_FORMAT, STORE, RV32C, UIMM)
`DEFINE_C_INSTR(C_ADDI4SPN, CIW_FORMAT, ARITHMETIC, RV32C, NZUIMM)
`DEFINE_C_INSTR(C_ADDI, CI_FORMAT, ARITHMETIC, RV32C, NZIMM)
`DEFINE_C_INSTR(C_ADDI16SP, CI_FORMAT, ARITHMETIC, RV32C, NZIMM)
`DEFINE_C_INSTR(C_LI, CI_FORMAT, ARITHMETIC, RV32C)
`DEFINE_C_INSTR(C_LUI, CI_FORMAT, ARITHMETIC, RV32C, NZIMM)
`DEFINE_C_INSTR(C_SUB, CA_FORMAT, ARITHMETIC, RV32C)
`DEFINE_C_INSTR(C_ADD, CR_FORMAT, ARITHMETIC, RV32C)
`DEFINE_C_INSTR(C_NOP, CI_FORMAT, ARITHMETIC, RV32C)
`DEFINE_C_INSTR(C_MV, CR_FORMAT, ARITHMETIC, RV32C)
`DEFINE_C_INSTR(C_ANDI, CB_FORMAT, LOGICAL, RV32C)
`DEFINE_C_INSTR(C_XOR, CA_FORMAT, LOGICAL, RV32C)
`DEFINE_C_INSTR(C_OR, CA_FORMAT, LOGICAL, RV32C)
`DEFINE_C_INSTR(C_AND, CA_FORMAT, LOGICAL, RV32C)
`DEFINE_C_INSTR(C_BEQZ, CB_FORMAT, BRANCH, RV32C)
`DEFINE_C_INSTR(C_BNEZ, CB_FORMAT, BRANCH, RV32C)
`DEFINE_C_INSTR(C_SRLI, CB_FORMAT, SHIFT, RV32C, NZUIMM)
`DEFINE_C_INSTR(C_SRAI, CB_FORMAT, SHIFT, RV32C, NZUIMM)
`DEFINE_C_INSTR(C_SLLI, CI_FORMAT, SHIFT, RV32C, NZUIMM)
`DEFINE_C_INSTR(C_J, CJ_FORMAT, JUMP, RV32C)
`DEFINE_C_INSTR(C_JAL, CJ_FORMAT, JUMP, RV32C)
`DEFINE_C_INSTR(C_JR, CR_FORMAT, JUMP, RV32C)
`DEFINE_C_INSTR(C_JALR, CR_FORMAT, JUMP, RV32C)
`DEFINE_C_INSTR(C_EBREAK, CI_FORMAT, SYSTEM, RV32C)

View file

@ -0,0 +1,26 @@
`DEFINE_FP_INSTR(FLD, I_FORMAT, LOAD, RV32D)
`DEFINE_FP_INSTR(FSD, S_FORMAT, STORE, RV32D)
`DEFINE_FP_INSTR(FMADD_D, R4_FORMAT, ARITHMETIC, RV32D)
`DEFINE_FP_INSTR(FMSUB_D, R4_FORMAT, ARITHMETIC, RV32D)
`DEFINE_FP_INSTR(FNMSUB_D, R4_FORMAT, ARITHMETIC, RV32D)
`DEFINE_FP_INSTR(FNMADD_D, R4_FORMAT, ARITHMETIC, RV32D)
`DEFINE_FP_INSTR(FADD_D, R_FORMAT, ARITHMETIC, RV32D)
`DEFINE_FP_INSTR(FSUB_D, R_FORMAT, ARITHMETIC, RV32D)
`DEFINE_FP_INSTR(FMUL_D, R_FORMAT, ARITHMETIC, RV32D)
`DEFINE_FP_INSTR(FDIV_D, R_FORMAT, ARITHMETIC, RV32D)
`DEFINE_FP_INSTR(FSQRT_D, I_FORMAT, ARITHMETIC, RV32D)
`DEFINE_FP_INSTR(FSGNJ_D, R_FORMAT, ARITHMETIC, RV32D)
`DEFINE_FP_INSTR(FSGNJN_D, R_FORMAT, ARITHMETIC, RV32D)
`DEFINE_FP_INSTR(FSGNJX_D, R_FORMAT, ARITHMETIC, RV32D)
`DEFINE_FP_INSTR(FMIN_D, R_FORMAT, ARITHMETIC, RV32D)
`DEFINE_FP_INSTR(FMAX_D, R_FORMAT, ARITHMETIC, RV32D)
`DEFINE_FP_INSTR(FCVT_S_D, I_FORMAT, ARITHMETIC, RV32D)
`DEFINE_FP_INSTR(FCVT_D_S, I_FORMAT, ARITHMETIC, RV32D)
`DEFINE_FP_INSTR(FEQ_D, R_FORMAT, COMPARE, RV32D)
`DEFINE_FP_INSTR(FLT_D, R_FORMAT, COMPARE, RV32D)
`DEFINE_FP_INSTR(FLE_D, R_FORMAT, COMPARE, RV32D)
`DEFINE_FP_INSTR(FCLASS_D, R_FORMAT, ARITHMETIC, RV32D)
`DEFINE_FP_INSTR(FCVT_W_D, I_FORMAT, ARITHMETIC, RV32D)
`DEFINE_FP_INSTR(FCVT_WU_D, I_FORMAT, ARITHMETIC, RV32D)
`DEFINE_FP_INSTR(FCVT_D_W, I_FORMAT, ARITHMETIC, RV32D)
`DEFINE_FP_INSTR(FCVT_D_WU, I_FORMAT, ARITHMETIC, RV32D)

View file

@ -0,0 +1,4 @@
`DEFINE_FC_INSTR(C_FLD, CL_FORMAT, LOAD, RV32DC, UIMM)
`DEFINE_FC_INSTR(C_FSD, CS_FORMAT, STORE, RV32DC, UIMM)
`DEFINE_FC_INSTR(C_FLDSP, CI_FORMAT, LOAD, RV32DC, UIMM)
`DEFINE_FC_INSTR(C_FSDSP, CSS_FORMAT, STORE, RV32DC, UIMM)

View file

@ -0,0 +1,26 @@
`DEFINE_FP_INSTR(FLW, I_FORMAT, LOAD, RV32F)
`DEFINE_FP_INSTR(FSW, S_FORMAT, STORE, RV32F)
`DEFINE_FP_INSTR(FMADD_S, R4_FORMAT, ARITHMETIC, RV32F)
`DEFINE_FP_INSTR(FMSUB_S, R4_FORMAT, ARITHMETIC, RV32F)
`DEFINE_FP_INSTR(FNMSUB_S, R4_FORMAT, ARITHMETIC, RV32F)
`DEFINE_FP_INSTR(FNMADD_S, R4_FORMAT, ARITHMETIC, RV32F)
`DEFINE_FP_INSTR(FADD_S, R_FORMAT, ARITHMETIC, RV32F)
`DEFINE_FP_INSTR(FSUB_S, R_FORMAT, ARITHMETIC, RV32F)
`DEFINE_FP_INSTR(FMUL_S, R_FORMAT, ARITHMETIC, RV32F)
`DEFINE_FP_INSTR(FDIV_S, R_FORMAT, ARITHMETIC, RV32F)
`DEFINE_FP_INSTR(FSQRT_S, I_FORMAT, ARITHMETIC, RV32F)
`DEFINE_FP_INSTR(FSGNJ_S, R_FORMAT, ARITHMETIC, RV32F)
`DEFINE_FP_INSTR(FSGNJN_S, R_FORMAT, ARITHMETIC, RV32F)
`DEFINE_FP_INSTR(FSGNJX_S, R_FORMAT, ARITHMETIC, RV32F)
`DEFINE_FP_INSTR(FMIN_S, R_FORMAT, ARITHMETIC, RV32F)
`DEFINE_FP_INSTR(FMAX_S, R_FORMAT, ARITHMETIC, RV32F)
`DEFINE_FP_INSTR(FCVT_W_S, I_FORMAT, ARITHMETIC, RV32F)
`DEFINE_FP_INSTR(FCVT_WU_S, I_FORMAT, ARITHMETIC, RV32F)
`DEFINE_FP_INSTR(FMV_X_W, I_FORMAT, ARITHMETIC, RV32F)
`DEFINE_FP_INSTR(FEQ_S, R_FORMAT, COMPARE, RV32F)
`DEFINE_FP_INSTR(FLT_S, R_FORMAT, COMPARE, RV32F)
`DEFINE_FP_INSTR(FLE_S, R_FORMAT, COMPARE, RV32F)
`DEFINE_FP_INSTR(FCLASS_S, R_FORMAT, ARITHMETIC, RV32F)
`DEFINE_FP_INSTR(FCVT_S_W, I_FORMAT, ARITHMETIC, RV32F)
`DEFINE_FP_INSTR(FCVT_S_WU, I_FORMAT, ARITHMETIC, RV32F)
`DEFINE_FP_INSTR(FMV_W_X, I_FORMAT, ARITHMETIC, RV32F)

View file

@ -0,0 +1,4 @@
`DEFINE_FC_INSTR(C_FLW, CL_FORMAT, LOAD, RV32FC, UIMM)
`DEFINE_FC_INSTR(C_FSW, CS_FORMAT, STORE, RV32FC, UIMM)
`DEFINE_FC_INSTR(C_FLWSP, CI_FORMAT, LOAD, RV32FC, UIMM)
`DEFINE_FC_INSTR(C_FSWSP, CSS_FORMAT, STORE, RV32FC, UIMM)

View file

@ -0,0 +1,65 @@
// LOAD instructions
`DEFINE_INSTR(LB, I_FORMAT, LOAD, RV32I)
`DEFINE_INSTR(LH, I_FORMAT, LOAD, RV32I)
`DEFINE_INSTR(LW, I_FORMAT, LOAD, RV32I)
`DEFINE_INSTR(LBU, I_FORMAT, LOAD, RV32I)
`DEFINE_INSTR(LHU, I_FORMAT, LOAD, RV32I)
// STORE instructions
`DEFINE_INSTR(SB, S_FORMAT, STORE, RV32I)
`DEFINE_INSTR(SH, S_FORMAT, STORE, RV32I)
`DEFINE_INSTR(SW, S_FORMAT, STORE, RV32I)
// SHIFT intructions
`DEFINE_INSTR(SLL, R_FORMAT, SHIFT, RV32I)
`DEFINE_INSTR(SLLI, I_FORMAT, SHIFT, RV32I)
`DEFINE_INSTR(SRL, R_FORMAT, SHIFT, RV32I)
`DEFINE_INSTR(SRLI, I_FORMAT, SHIFT, RV32I)
`DEFINE_INSTR(SRA, R_FORMAT, SHIFT, RV32I)
`DEFINE_INSTR(SRAI, I_FORMAT, SHIFT, RV32I)
// ARITHMETIC intructions
`DEFINE_INSTR(ADD, R_FORMAT, ARITHMETIC, RV32I)
`DEFINE_INSTR(ADDI, I_FORMAT, ARITHMETIC, RV32I)
`DEFINE_INSTR(NOP, I_FORMAT, ARITHMETIC, RV32I)
`DEFINE_INSTR(SUB, R_FORMAT, ARITHMETIC, RV32I)
`DEFINE_INSTR(LUI, U_FORMAT, ARITHMETIC, RV32I, UIMM)
`DEFINE_INSTR(AUIPC, U_FORMAT, ARITHMETIC, RV32I, UIMM)
// LOGICAL instructions
`DEFINE_INSTR(XOR, R_FORMAT, LOGICAL, RV32I)
`DEFINE_INSTR(XORI, I_FORMAT, LOGICAL, RV32I)
`DEFINE_INSTR(OR, R_FORMAT, LOGICAL, RV32I)
`DEFINE_INSTR(ORI, I_FORMAT, LOGICAL, RV32I)
`DEFINE_INSTR(AND, R_FORMAT, LOGICAL, RV32I)
`DEFINE_INSTR(ANDI, I_FORMAT, LOGICAL, RV32I)
// COMPARE instructions
`DEFINE_INSTR(SLT, R_FORMAT, COMPARE, RV32I)
`DEFINE_INSTR(SLTI, I_FORMAT, COMPARE, RV32I)
`DEFINE_INSTR(SLTU, R_FORMAT, COMPARE, RV32I)
`DEFINE_INSTR(SLTIU, I_FORMAT, COMPARE, RV32I)
// BRANCH instructions
`DEFINE_INSTR(BEQ, B_FORMAT, BRANCH, RV32I)
`DEFINE_INSTR(BNE, B_FORMAT, BRANCH, RV32I)
`DEFINE_INSTR(BLT, B_FORMAT, BRANCH, RV32I)
`DEFINE_INSTR(BGE, B_FORMAT, BRANCH, RV32I)
`DEFINE_INSTR(BLTU, B_FORMAT, BRANCH, RV32I)
`DEFINE_INSTR(BGEU, B_FORMAT, BRANCH, RV32I)
// JUMP instructions
`DEFINE_INSTR(JAL, J_FORMAT, JUMP, RV32I)
`DEFINE_INSTR(JALR, I_FORMAT, JUMP, RV32I)
// SYNCH instructions
`DEFINE_INSTR(FENCE, I_FORMAT, SYNCH, RV32I)
`DEFINE_INSTR(FENCE_I, I_FORMAT, SYNCH, RV32I)
`DEFINE_INSTR(SFENCE_VMA, R_FORMAT, SYNCH, RV32I)
// SYSTEM instructions
`DEFINE_INSTR(ECALL, I_FORMAT, SYSTEM, RV32I)
`DEFINE_INSTR(EBREAK, I_FORMAT, SYSTEM, RV32I)
`DEFINE_INSTR(URET, I_FORMAT, SYSTEM, RV32I)
`DEFINE_INSTR(SRET, I_FORMAT, SYSTEM, RV32I)
`DEFINE_INSTR(MRET, I_FORMAT, SYSTEM, RV32I)
`DEFINE_INSTR(DRET, I_FORMAT, SYSTEM, RV32I)
`DEFINE_INSTR(WFI, I_FORMAT, INTERRUPT, RV32I)
// CSR instructions
`DEFINE_INSTR(CSRRW, R_FORMAT, CSR, RV32I, UIMM)
`DEFINE_INSTR(CSRRS, R_FORMAT, CSR, RV32I, UIMM)
`DEFINE_INSTR(CSRRC, R_FORMAT, CSR, RV32I, UIMM)
`DEFINE_INSTR(CSRRWI, I_FORMAT, CSR, RV32I, UIMM)
`DEFINE_INSTR(CSRRSI, I_FORMAT, CSR, RV32I, UIMM)
`DEFINE_INSTR(CSRRCI, I_FORMAT, CSR, RV32I, UIMM)

View file

@ -0,0 +1,9 @@
//////////// RV32M instructions //////////////
`DEFINE_INSTR(MUL, R_FORMAT, ARITHMETIC, RV32M)
`DEFINE_INSTR(MULH, R_FORMAT, ARITHMETIC, RV32M)
`DEFINE_INSTR(MULHSU, R_FORMAT, ARITHMETIC, RV32M)
`DEFINE_INSTR(MULHU, R_FORMAT, ARITHMETIC, RV32M)
`DEFINE_INSTR(DIV, R_FORMAT, ARITHMETIC, RV32M)
`DEFINE_INSTR(DIVU, R_FORMAT, ARITHMETIC, RV32M)
`DEFINE_INSTR(REM, R_FORMAT, ARITHMETIC, RV32M)
`DEFINE_INSTR(REMU, R_FORMAT, ARITHMETIC, RV32M)

View file

@ -0,0 +1,11 @@
`DEFINE_AMO_INSTR(LR_D, R_FORMAT, LOAD, RV64A)
`DEFINE_AMO_INSTR(SC_D, R_FORMAT, STORE, RV64A)
`DEFINE_AMO_INSTR(AMOSWAP_D, R_FORMAT, AMO, RV64A)
`DEFINE_AMO_INSTR(AMOADD_D, R_FORMAT, AMO, RV64A)
`DEFINE_AMO_INSTR(AMOAND_D, R_FORMAT, AMO, RV64A)
`DEFINE_AMO_INSTR(AMOOR_D, R_FORMAT, AMO, RV64A)
`DEFINE_AMO_INSTR(AMOXOR_D, R_FORMAT, AMO, RV64A)
`DEFINE_AMO_INSTR(AMOMIN_D, R_FORMAT, AMO, RV64A)
`DEFINE_AMO_INSTR(AMOMAX_D, R_FORMAT, AMO, RV64A)
`DEFINE_AMO_INSTR(AMOMINU_D, R_FORMAT, AMO, RV64A)
`DEFINE_AMO_INSTR(AMOMAXU_D, R_FORMAT, AMO, RV64A)

View file

@ -0,0 +1,7 @@
`DEFINE_C_INSTR(C_ADDIW, CI_FORMAT, ARITHMETIC, RV64C)
`DEFINE_C_INSTR(C_SUBW, CA_FORMAT, ARITHMETIC, RV64C)
`DEFINE_C_INSTR(C_ADDW, CA_FORMAT, ARITHMETIC, RV64C)
`DEFINE_C_INSTR(C_LD, CL_FORMAT, LOAD, RV64C, UIMM)
`DEFINE_C_INSTR(C_SD, CS_FORMAT, STORE, RV64C, UIMM)
`DEFINE_C_INSTR(C_LDSP, CI_FORMAT, LOAD, RV64C, UIMM)
`DEFINE_C_INSTR(C_SDSP, CSS_FORMAT, STORE, RV64C, UIMM)

View file

@ -0,0 +1,6 @@
`DEFINE_FP_INSTR(FMV_X_D, I_FORMAT, ARITHMETIC, RV64D)
`DEFINE_FP_INSTR(FMV_D_X, I_FORMAT, ARITHMETIC, RV64D)
`DEFINE_FP_INSTR(FCVT_L_D, I_FORMAT, ARITHMETIC, RV64D)
`DEFINE_FP_INSTR(FCVT_LU_D, I_FORMAT, ARITHMETIC, RV64D)
`DEFINE_FP_INSTR(FCVT_D_L, I_FORMAT, ARITHMETIC, RV64D)
`DEFINE_FP_INSTR(FCVT_D_LU, I_FORMAT, ARITHMETIC, RV64D)

View file

@ -0,0 +1,4 @@
`DEFINE_FP_INSTR(FCVT_L_S, I_FORMAT, ARITHMETIC, RV64F)
`DEFINE_FP_INSTR(FCVT_LU_S, I_FORMAT, ARITHMETIC, RV64F)
`DEFINE_FP_INSTR(FCVT_S_L, I_FORMAT, ARITHMETIC, RV64F)
`DEFINE_FP_INSTR(FCVT_S_LU, I_FORMAT, ARITHMETIC, RV64F)

View file

@ -0,0 +1,14 @@
`DEFINE_INSTR(LWU, I_FORMAT, LOAD, RV64I)
`DEFINE_INSTR(LD, I_FORMAT, LOAD, RV64I)
`DEFINE_INSTR(SD, S_FORMAT, STORE, RV64I)
// SHIFT intructions
`DEFINE_INSTR(SLLW, R_FORMAT, SHIFT, RV64I)
`DEFINE_INSTR(SLLIW, I_FORMAT, SHIFT, RV64I)
`DEFINE_INSTR(SRLW, R_FORMAT, SHIFT, RV64I)
`DEFINE_INSTR(SRLIW, I_FORMAT, SHIFT, RV64I)
`DEFINE_INSTR(SRAW, R_FORMAT, SHIFT, RV64I)
`DEFINE_INSTR(SRAIW, I_FORMAT, SHIFT, RV64I)
// ARITHMETIC intructions
`DEFINE_INSTR(ADDW, R_FORMAT, ARITHMETIC, RV64I)
`DEFINE_INSTR(ADDIW, I_FORMAT, ARITHMETIC, RV64I)
`DEFINE_INSTR(SUBW, R_FORMAT, ARITHMETIC, RV64I)

View file

@ -0,0 +1,5 @@
`DEFINE_INSTR(MULW, R_FORMAT, ARITHMETIC, RV64M)
`DEFINE_INSTR(DIVW, R_FORMAT, ARITHMETIC, RV64M)
`DEFINE_INSTR(DIVUW, R_FORMAT, ARITHMETIC, RV64M)
`DEFINE_INSTR(REMW, R_FORMAT, ARITHMETIC, RV64M)
`DEFINE_INSTR(REMUW, R_FORMAT, ARITHMETIC, RV64M)

View file

@ -65,13 +65,13 @@ class riscv_amo_base_instr_stream extends riscv_mem_access_stream;
virtual function void gen_amo_instr();
endfunction
endclass
endclass : riscv_amo_base_instr_stream
// A pair of LR/SC instruction
class riscv_lr_sc_instr_stream extends riscv_amo_base_instr_stream;
riscv_rand_instr lr_instr;
riscv_rand_instr sc_instr;
riscv_instr lr_instr;
riscv_instr sc_instr;
constraint legal_c {
num_amo == 1;
@ -82,32 +82,40 @@ class riscv_lr_sc_instr_stream extends riscv_amo_base_instr_stream;
function new(string name = "");
super.new(name);
lr_instr = riscv_rand_instr::type_id::create("lr_instr");
sc_instr = riscv_rand_instr::type_id::create("sc_instr");
endfunction
virtual function void gen_amo_instr();
lr_instr.cfg = cfg;
sc_instr.cfg = cfg;
lr_instr.disable_a_extension_c.constraint_mode(0);
sc_instr.disable_a_extension_c.constraint_mode(0);
lr_instr = riscv_instr::get_rand_instr(.include_instr({LR_W, LR_D}));
sc_instr = riscv_instr::get_rand_instr(.include_instr({SC_W, SC_D}));
`DV_CHECK_RANDOMIZE_WITH_FATAL(lr_instr,
rs1 == rs1_reg;
rd != rs1_reg;
instr_name inside {LR_W, LR_D};)
rs1 == rs1_reg;
if (reserved_rd.size() > 0) {
!(rd inside {reserved_rd});
}
if (cfg.reserved_regs.size() > 0) {
!(rd inside {cfg.reserved_regs});
}
rd != rs1_reg;
)
`DV_CHECK_RANDOMIZE_WITH_FATAL(sc_instr,
rs1 == rs1_reg;
rd != rs1_reg;
instr_name inside {SC_W, SC_D};)
instr_list.push_front(lr_instr);
instr_list.push_front(sc_instr);
rs1 == rs1_reg;
if (reserved_rd.size() > 0) {
!(rd inside {reserved_rd});
}
if (cfg.reserved_regs.size() > 0) {
!(rd inside {cfg.reserved_regs});
}
rd != rs1_reg;
)
instr_list.push_back(lr_instr);
instr_list.push_back(sc_instr);
endfunction
endclass
endclass : riscv_lr_sc_instr_stream
class riscv_amo_instr_stream extends riscv_amo_base_instr_stream;
riscv_rand_instr amo_instr[];
riscv_instr amo_instr[];
constraint reasonable_c {
solve num_amo before num_mixed_instr;
@ -121,22 +129,19 @@ class riscv_amo_instr_stream extends riscv_amo_base_instr_stream;
virtual function void gen_amo_instr();
amo_instr = new[num_amo];
foreach (amo_instr[i]) begin
amo_instr[i] = riscv_rand_instr::type_id::create($sformatf("amo_instr_%0d", i));
amo_instr[i].cfg = cfg;
amo_instr[i].disable_a_extension_c.constraint_mode(0);
`ifdef DSIM
`DV_CHECK_RANDOMIZE_WITH_FATAL(amo_instr[i],
rs1 == rs1_reg;
rd != rs1_reg;
instr_name inside {[AMOSWAP_W:AMOMAXU_D]};)
`else
`DV_CHECK_RANDOMIZE_WITH_FATAL(amo_instr[i],
rs1 == rs1_reg;
rd != rs1_reg;
category == AMO;)
`endif
amo_instr[i] = riscv_instr::get_rand_instr(.include_category({AMO}));
`DV_CHECK_RANDOMIZE_WITH_FATAL(amo_instr[i],
if (reserved_rd.size() > 0) {
!(rd inside {reserved_rd});
}
if (cfg.reserved_regs.size() > 0) {
!(rd inside {cfg.reserved_regs});
}
rs1 == rs1_reg;
rd != rs1_reg;
)
instr_list.push_front(amo_instr[i]);
end
endfunction
endclass
endclass : riscv_amo_instr_stream

View file

@ -29,8 +29,7 @@ class riscv_asm_program_gen extends uvm_object;
// User mode programs
riscv_instr_sequence main_program;
riscv_instr_sequence sub_program[];
riscv_instr_sequence debug_program;
riscv_instr_sequence debug_sub_program[];
riscv_asm_program_gen debug_rom;
// Kernel programs
// These programs are called in the interrupt/exception handling routine based on the privileged
// mode settings. For example, when the interrupt/exception is delegated to S-mode, if both SUM
@ -110,9 +109,9 @@ class riscv_asm_program_gen extends uvm_object;
// Privileged mode switch routine
gen_privileged_mode_switch_routine();
// Generate debug rom section
gen_debug_rom();
// Generate debug mode exception handler
gen_debug_exception_handler();
if (riscv_instr_pkg::support_debug_mode) begin
gen_debug_rom();
end
end
// Starting point of data section
gen_data_page_begin();
@ -340,7 +339,7 @@ class riscv_asm_program_gen extends uvm_object;
init_floating_point_gpr();
end
core_is_initialized();
gen_dummy_csr_write();
gen_dummy_csr_write(); // TODO add a way to disable xStatus read
endfunction
// Setup MISA based on supported extensions
@ -522,7 +521,7 @@ class riscv_asm_program_gen extends uvm_object;
string instr[$];
string csr_handshake[$];
string ret_instr;
if(riscv_instr_pkg::supported_privileged_mode[i] < cfg.init_privileged_mode) continue;
if(riscv_instr_pkg::supported_privileged_mode[i] != cfg.init_privileged_mode) continue;
`uvm_info(`gfn, $sformatf("Generating privileged mode routing for %0s",
riscv_instr_pkg::supported_privileged_mode[i].name()), UVM_LOW)
// Enter privileged mode
@ -668,8 +667,9 @@ class riscv_asm_program_gen extends uvm_object;
SUPERVISOR_MODE:
gen_trap_handler_section("s", SCAUSE, STVEC, STVAL, SEPC, SSCRATCH, SSTATUS, SIE, SIP);
USER_MODE:
if(riscv_instr_pkg::support_umode_trap)
if(riscv_instr_pkg::support_umode_trap) begin
gen_trap_handler_section("u", UCAUSE, UTVEC, UTVAL, UEPC, USCRATCH, USTATUS, UIE, UIP);
end
endcase
end
// Ebreak handler
@ -963,7 +963,8 @@ class riscv_asm_program_gen extends uvm_object;
privileged_reg_t status, ip, ie, scratch;
string interrupt_handler_instr[$];
ls_unit = (XLEN == 32) ? "w" : "d";
if(mode < cfg.init_privileged_mode) return;
if (mode < cfg.init_privileged_mode) return;
if (mode == USER_MODE && !riscv_instr_pkg::support_umode_trap) return;
case(mode)
MACHINE_MODE: begin
mode_prefix = "m";
@ -1039,11 +1040,16 @@ class riscv_asm_program_gen extends uvm_object;
// Dump performance CSRs if applicable
virtual function void dump_perf_stats();
string perf_stats[$];
foreach(implemented_csr[i]) begin
if (implemented_csr[i] inside {[MCYCLE:MHPMCOUNTER31H]}) begin
gen_signature_handshake(.instr(instr_stream), .signature_type(WRITE_CSR), .csr(implemented_csr[i]));
gen_signature_handshake(.instr(perf_stats),
.signature_type(WRITE_CSR),
.csr(implemented_csr[i]));
end
end
format_section(perf_stats);
instr_stream = {instr_stream, perf_stats};
endfunction
// Write the generated program to a file
@ -1215,208 +1221,15 @@ class riscv_asm_program_gen extends uvm_object;
endfunction
//---------------------------------------------------------------------------------------
// Generate the debug rom, and any related programs
// TODO - refactor such that debug_rom is generated by a separate class
// Generate the debug ROM, and any related programs
//---------------------------------------------------------------------------------------
// Generate the program in the debug ROM
// Processor will fetch instruction from here upon receiving debug request from debug module
virtual function void gen_debug_rom();
string instr[$];
string debug_end[$];
string dret;
string debug_sub_program_name[$] = {};
string str[$];
if (riscv_instr_pkg::support_debug_mode) begin
dret = {format_string(" ", LABEL_STR_LEN), "dret"};
// The main debug rom
if (!cfg.gen_debug_section) begin
// If the debug section should not be generated, we just populate it
// with a dret instruction.
instr = {dret};
gen_section("debug_rom", instr);
end else begin
if (cfg.enable_ebreak_in_debug_rom) begin
// As execution of ebreak in D mode causes core to
// re-enter D mode, this directed sequence will be a loop that ensures the
// ebreak instruction will only be executed once to prevent infinitely
// looping back to the beginning of the debug rom.
// Write dscratch to random GPR and branch to debug_end if greater
// than 0, for ebreak loops.
// Use dscratch1 to store original GPR value.
str = {$sformatf("csrw 0x%0x, x%0d", DSCRATCH1, cfg.scratch_reg),
$sformatf("csrr x%0d, 0x%0x", cfg.scratch_reg, DSCRATCH0),
$sformatf("beq x%0d, x0, 1f", cfg.scratch_reg),
$sformatf("j debug_end"),
$sformatf("1: csrr x%0d, 0x%0x", cfg.scratch_reg, DSCRATCH1)};
instr = {instr, str};
end
// Need to save off GPRs to avoid modifying program flow
push_gpr_to_kernel_stack(MSTATUS, MSCRATCH, cfg.mstatus_mprv, cfg.sp, cfg.tp, instr);
// Signal that the core entered debug rom only if the rom is actually
// being filled with random instructions to prevent stress tests from
// having to execute unnecessary push/pop of GPRs on the stack ever
// time a debug request is sent
gen_signature_handshake(instr, CORE_STATUS, IN_DEBUG_MODE);
if (cfg.enable_ebreak_in_debug_rom) begin
// send dpc and dcsr to testbench, as this handshake will be
// executed twice due to the ebreak loop, there should be no change
// in their values as by the Debug Mode Spec Ch. 4.1.8
gen_signature_handshake(.instr(instr), .signature_type(WRITE_CSR), .csr(DCSR));
gen_signature_handshake(.instr(instr), .signature_type(WRITE_CSR), .csr(DPC));
end
if (cfg.set_dcsr_ebreak) begin
// We want to set dcsr.ebreak(m/s/u) to 1'b1, depending on what modes
// are available.
// TODO(udinator) - randomize the dcsr.ebreak setup
gen_dcsr_ebreak(instr);
end
if (cfg.enable_debug_single_step) begin
// To enable debug single stepping, we must set dcsr.step to 1.
// We will repeat the debug single stepping process a random number
// of times, using a dscratch CSR as the counter, and decrement
// this counter by 1 every time we enter debug mode, until this
// counter reaches 0, at which point we set dcsr.step to 0 until
// the next debug stimulus is asserted.
// Store our designated scratch_reg to dscratch1
str = {$sformatf("csrw 0x%0x, x%0d", DSCRATCH1, cfg.scratch_reg),
// Only un-set dcsr.step if it is 1 and the iterations counter
// is at 0 (has finished iterating)
$sformatf("csrr x%0d, 0x%0x", cfg.scratch_reg, DCSR),
$sformatf("andi x%0d, x%0d, 4", cfg.scratch_reg, cfg.scratch_reg),
// If dcsr.step is 0, set to 1 and set the counter
$sformatf("beqz x%0d, 1f", cfg.scratch_reg),
$sformatf("csrr x%0d, 0x%0x", cfg.scratch_reg, DSCRATCH0),
// if the counter is greater than 0, decrement and continue single stepping
$sformatf("bgtz x%0d, 2f", cfg.scratch_reg),
$sformatf("csrc 0x%0x, 0x4", DCSR),
$sformatf("beqz x0, 3f"),
// Set dcsr.step and the num_iterations counter
$sformatf("1: csrs 0x%0x, 0x4", DCSR),
$sformatf("li x%0d, %0d", cfg.scratch_reg, cfg.single_step_iterations),
$sformatf("csrw 0x%0x, x%0d", DSCRATCH0, cfg.scratch_reg),
$sformatf("beqz x0, 3f"),
// Decrement dscratch counter
$sformatf("2: csrr x%0d, 0x%0x", cfg.scratch_reg, DSCRATCH0),
$sformatf("addi x%0d, x%0d, -1", cfg.scratch_reg, cfg.scratch_reg),
$sformatf("csrw 0x%0x, x%0d", DSCRATCH0, cfg.scratch_reg),
// Restore scratch_reg value from dscratch1
$sformatf("3: csrr x%0d, 0x%0x", cfg.scratch_reg, DSCRATCH1)
};
instr = {instr, str};
// write dpc to testbench
gen_signature_handshake(.instr(instr), .signature_type(WRITE_CSR), .csr(DPC));
// write out the counter to the testbench
gen_signature_handshake(.instr(instr), .signature_type(WRITE_CSR), .csr(DSCRATCH0));
end
// Check dcsr.cause, and update dpc by 0x4 if the cause is ebreak, as
// ebreak will set set dpc to its own address, which will cause an
// infinite loop.
str = {$sformatf("csrr x%0d, 0x%0x", cfg.scratch_reg, DCSR),
$sformatf("slli x%0d, x%0d, 0x17", cfg.scratch_reg, cfg.scratch_reg),
$sformatf("srli x%0d, x%0d, 0x1d", cfg.scratch_reg, cfg.scratch_reg),
$sformatf("li x%0d, 0x1", cfg.gpr[0]),
$sformatf("bne x%0d, x%0d, 4f", cfg.scratch_reg, cfg.gpr[0])};
instr = {instr, str};
increment_csr(DPC, 4, instr);
str = {"4: nop"};
instr = {instr, str};
// write DCSR to the testbench for any analysis
gen_signature_handshake(.instr(instr), .signature_type(WRITE_CSR), .csr(DCSR));
// Increment dscratch0 by 1 to update the loop counter for all ebreak
// tests
if (cfg.enable_ebreak_in_debug_rom || cfg.set_dcsr_ebreak) begin
// Add 1 to dscratch0
increment_csr(DSCRATCH0, 1, instr);
str = {$sformatf("csrr x%0d, 0x%0x", cfg.scratch_reg, DSCRATCH1)};
instr = {instr, str};
end
format_section(instr);
gen_sub_program(debug_sub_program, debug_sub_program_name,
cfg.num_debug_sub_program, 1'b1, "debug_sub");
debug_program = riscv_instr_sequence::type_id::create("debug_program");
debug_program.instr_cnt = cfg.debug_program_instr_cnt;
debug_program.is_debug_program = 1;
debug_program.cfg = cfg;
`DV_CHECK_RANDOMIZE_FATAL(debug_program)
debug_program.gen_instr(.is_main_program(1'b1), .no_branch(cfg.no_branch_jump));
gen_callstack(debug_program, debug_sub_program, debug_sub_program_name,
cfg.num_debug_sub_program);
debug_program.post_process_instr();
debug_program.generate_instr_stream(.no_label(1'b1));
insert_sub_program(debug_sub_program, instr_stream);
instr = {instr, debug_program.instr_string_list};
gen_section("debug_rom", instr);
// Set dscratch0 back to 0x0 to prepare for the next entry into debug
// mode, and write dscratch0 and dcsr to the testbench for any
// analysis
if (cfg.enable_ebreak_in_debug_rom) begin
// send dpc and dcsr to testbench, as this handshake will be
// executed twice due to the ebreak loop, there should be no change
// in their values as by the Debug Mode Spec Ch. 4.1.8
gen_signature_handshake(.instr(debug_end), .signature_type(WRITE_CSR), .csr(DCSR));
gen_signature_handshake(.instr(debug_end), .signature_type(WRITE_CSR), .csr(DPC));
str = {$sformatf("csrwi 0x%0x, 0x0", DSCRATCH0)};
debug_end = {debug_end, str};
end
pop_gpr_from_kernel_stack(MSTATUS, MSCRATCH, cfg.mstatus_mprv,
cfg.sp, cfg.tp, debug_end);
// We have been using dscratch1 to store the
// value of our given scratch register for use in ebreak loop, so we
// need to restore its value before returning from D mode
if (cfg.enable_ebreak_in_debug_rom) begin
str = {$sformatf("csrr x%0d, 0x%0x", cfg.scratch_reg, DSCRATCH1)};
debug_end = {debug_end, str};
end
format_section(debug_end);
debug_end = {debug_end, dret};
gen_section("debug_end", debug_end);
end
end
endfunction
// Generate exception handling routine for debug ROM
virtual function void gen_debug_exception_handler();
if (riscv_instr_pkg::support_debug_mode) begin
string instr[];
instr = {"dret"};
gen_section("debug_exception", instr);
end
endfunction
// Set dcsr.ebreak(m/s/u)
// TODO(udinator) - randomize the setup for these fields
virtual function void gen_dcsr_ebreak(ref string instr[$]);
string str;
if (MACHINE_MODE inside {riscv_instr_pkg::supported_privileged_mode}) begin
str = $sformatf("li x%0d, 0x8000", cfg.scratch_reg);
instr.push_back(str);
str = $sformatf("csrs dcsr, x%0d", cfg.scratch_reg);
instr.push_back(str);
end
if (SUPERVISOR_MODE inside {riscv_instr_pkg::supported_privileged_mode}) begin
str = $sformatf("li x%0d, 0x2000", cfg.scratch_reg);
instr.push_back(str);
str = $sformatf("csrs dcsr, x%0d", cfg.scratch_reg);
instr.push_back(str);
end
if (USER_MODE inside {riscv_instr_pkg::supported_privileged_mode}) begin
str = $sformatf("li x%0d, 0x1000", cfg.scratch_reg);
instr.push_back(str);
str = $sformatf("csrs dcsr, x%0d", cfg.scratch_reg);
instr.push_back(str);
end
endfunction
virtual function void increment_csr(privileged_reg_t csr, int val, ref string instr[$]);
string str;
str = $sformatf("csrr x%0d, 0x%0x", cfg.scratch_reg, csr);
instr.push_back(str);
str = $sformatf("addi x%0d, x%0d, 0x%0x", cfg.scratch_reg, cfg.scratch_reg, val);
instr.push_back(str);
str = $sformatf("csrw 0x%0x, x%0d", csr, cfg.scratch_reg);
instr.push_back(str);
`uvm_info(`gfn, "Creating debug ROM", UVM_LOW)
debug_rom = riscv_asm_program_gen::type_id::create("debug_rom", , {"uvm_test_top", ".", `gfn});
debug_rom.cfg = cfg;
debug_rom.gen_program();
instr_stream = {instr_stream, debug_rom.instr_stream};
endfunction
endclass

View file

@ -0,0 +1,247 @@
/*
* Copyright 2019 Google LLC
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
//---------------------------------------------------------------------------------------
// RISC-V debug ROM class
//
// This is the main class to generate a test debug ROM, which includes control knobs to
// toggle various configuration fields of DCSR.
//---------------------------------------------------------------------------------------
class riscv_debug_rom_gen extends riscv_asm_program_gen;
string debug_main[$];
string debug_end[$];
string str[$];
string dret;
`uvm_object_utils(riscv_debug_rom_gen)
function new(string name = "");
super.new(name);
dret = "dret";
endfunction
//-------------------------------------------------------------------------------------
// Main function to generate whole debug ROM
//-------------------------------------------------------------------------------------
virtual function void gen_program();
string sub_program_name[$] = {};
if (!cfg.gen_debug_section) begin
// If the debug section should not be generated, we just populate it
// with a dret instruction.
debug_main = {dret};
gen_section("debug_rom", debug_main);
end else begin
if (cfg.enable_ebreak_in_debug_rom) begin
gen_ebreak_header();
end
// Need to save off GPRs to avoid modifying program flow
push_gpr_to_kernel_stack(MSTATUS, MSCRATCH, cfg.mstatus_mprv, cfg.sp, cfg.tp, debug_main);
// Signal that the core entered debug rom only if the rom is actually
// being filled with random instructions to prevent stress tests from
// having to execute unnecessary push/pop of GPRs on the stack ever
// time a debug request is sent
gen_signature_handshake(debug_main, CORE_STATUS, IN_DEBUG_MODE);
if (cfg.enable_ebreak_in_debug_rom) begin
// send dpc and dcsr to testbench, as this handshake will be
// executed twice due to the ebreak loop, there should be no change
// in their values as by the Debug Mode Spec Ch. 4.1.8
gen_signature_handshake(.instr(debug_main), .signature_type(WRITE_CSR), .csr(DCSR));
gen_signature_handshake(.instr(debug_main), .signature_type(WRITE_CSR), .csr(DPC));
end
if (cfg.set_dcsr_ebreak) begin
// We want to set dcsr.ebreak(m/s/u) to 1'b1, depending on what modes
// are available.
// TODO(udinator) - randomize the dcsr.ebreak setup
gen_dcsr_ebreak();
end
if (cfg.enable_debug_single_step) begin
gen_single_step_logic();
end
gen_dpc_update();
// write DCSR to the testbench for any analysis
gen_signature_handshake(.instr(debug_main), .signature_type(WRITE_CSR), .csr(DCSR));
if (cfg.enable_ebreak_in_debug_rom || cfg.set_dcsr_ebreak) begin
gen_increment_ebreak_counter();
end
format_section(debug_main);
gen_sub_program(sub_program, sub_program_name,
cfg.num_debug_sub_program, 1'b1, "debug_sub");
main_program = riscv_instr_sequence::type_id::create("debug_program");
main_program.instr_cnt = cfg.debug_program_instr_cnt;
main_program.is_debug_program = 1;
main_program.cfg = cfg;
`DV_CHECK_RANDOMIZE_FATAL(main_program)
main_program.gen_instr(.is_main_program(1'b1), .no_branch(cfg.no_branch_jump));
gen_callstack(main_program, sub_program, sub_program_name,
cfg.num_debug_sub_program);
main_program.post_process_instr();
main_program.generate_instr_stream(.no_label(1'b1));
insert_sub_program(sub_program, debug_main);
debug_main = {debug_main, main_program.instr_string_list};
gen_section("debug_rom", debug_main);
if (cfg.enable_ebreak_in_debug_rom) begin
gen_ebreak_footer();
end
pop_gpr_from_kernel_stack(MSTATUS, MSCRATCH, cfg.mstatus_mprv,
cfg.sp, cfg.tp, debug_end);
if (cfg.enable_ebreak_in_debug_rom) begin
gen_restore_ebreak_scratch_reg();
end
//format_section(debug_end);
debug_end = {debug_end, dret};
gen_section("debug_end", debug_end);
end
gen_debug_exception_handler();
endfunction
// Generate exception handling routine for debug ROM
// TODO(udinator) - remains empty for now, only a DRET
virtual function void gen_debug_exception_handler();
str = {"dret"};
gen_section("debug_exception", str);
endfunction
//-------------------------------------------------------------------------------------
// Helper functions to generate smaller sections of code
//-------------------------------------------------------------------------------------
// As execution of ebreak in D mode causes core to re-enter D mode, this directed
// sequence will be a loop that ensures the ebreak instruction will only be executed
// once to prevent infinitely looping back to the beginning of the debug rom.
// Write dscratch to random GPR and branch to debug_end if greater than 0, for ebreak loops.
// Use dscratch1 to store original GPR value.
virtual function void gen_ebreak_header();
str = {$sformatf("csrw 0x%0x, x%0d", DSCRATCH1, cfg.scratch_reg),
$sformatf("csrr x%0d, 0x%0x", cfg.scratch_reg, DSCRATCH0),
$sformatf("beq x%0d, x0, 1f", cfg.scratch_reg),
$sformatf("j debug_end"),
$sformatf("1: csrr x%0d, 0x%0x", cfg.scratch_reg, DSCRATCH1)};
debug_main = {debug_main, str};
endfunction
// Set dscratch0 back to 0x0 to prepare for the next entry into debug
// mode, and write dscratch0 and dcsr to the testbench for any
// analysis
virtual function void gen_ebreak_footer();
// send dpc and dcsr to testbench, as this handshake will be
// executed twice due to the ebreak loop, there should be no change
// in their values as by the Debug Mode Spec Ch. 4.1.8
gen_signature_handshake(.instr(debug_end), .signature_type(WRITE_CSR), .csr(DCSR));
gen_signature_handshake(.instr(debug_end), .signature_type(WRITE_CSR), .csr(DPC));
str = {$sformatf("csrwi 0x%0x, 0x0", DSCRATCH0)};
debug_end = {debug_end, str};
endfunction
// Increment dscratch0 by 1 to update the loop counter for all ebreak tests
virtual function void gen_increment_ebreak_counter();
// Add 1 to dscratch0
increment_csr(DSCRATCH0, 1, debug_main);
str = {$sformatf("csrr x%0d, 0x%0x", cfg.scratch_reg, DSCRATCH1)};
debug_main = {debug_main, str};
endfunction
// We have been using dscratch1 to store the
// value of our given scratch register for use in ebreak loop, so we
// need to restore its value before returning from D mode
virtual function void gen_restore_ebreak_scratch_reg();
str = {$sformatf("csrr x%0d, 0x%0x", cfg.scratch_reg, DSCRATCH1)};
debug_end = {debug_end, str};
endfunction
// To enable debug single stepping, we must set dcsr.step to 1.
// We will repeat the debug single stepping process a random number of times,
// using a dscratch CSR as the counter, and decrement this counter by 1 every time we
// enter debug mode, until this counter reaches 0, at which point we set
// dcsr.step to 0 until the next debug stimulus is asserted.
// Store our designated scratch_reg to dscratch1
virtual function void gen_single_step_logic();
str = {$sformatf("csrw 0x%0x, x%0d", DSCRATCH1, cfg.scratch_reg),
// Only un-set dcsr.step if it is 1 and the iterations counter
// is at 0 (has finished iterating)
$sformatf("csrr x%0d, 0x%0x", cfg.scratch_reg, DCSR),
$sformatf("andi x%0d, x%0d, 4", cfg.scratch_reg, cfg.scratch_reg),
// If dcsr.step is 0, set to 1 and set the counter
$sformatf("beqz x%0d, 1f", cfg.scratch_reg),
$sformatf("csrr x%0d, 0x%0x", cfg.scratch_reg, DSCRATCH0),
// if the counter is greater than 0, decrement and continue single stepping
$sformatf("bgtz x%0d, 2f", cfg.scratch_reg),
$sformatf("csrc 0x%0x, 0x4", DCSR),
$sformatf("beqz x0, 3f"),
// Set dcsr.step and the num_iterations counter
$sformatf("1: csrs 0x%0x, 0x4", DCSR),
$sformatf("li x%0d, %0d", cfg.scratch_reg, cfg.single_step_iterations),
$sformatf("csrw 0x%0x, x%0d", DSCRATCH0, cfg.scratch_reg),
$sformatf("beqz x0, 3f"),
// Decrement dscratch counter
$sformatf("2: csrr x%0d, 0x%0x", cfg.scratch_reg, DSCRATCH0),
$sformatf("addi x%0d, x%0d, -1", cfg.scratch_reg, cfg.scratch_reg),
$sformatf("csrw 0x%0x, x%0d", DSCRATCH0, cfg.scratch_reg),
// Restore scratch_reg value from dscratch1
$sformatf("3: csrr x%0d, 0x%0x", cfg.scratch_reg, DSCRATCH1)
};
debug_main = {debug_main, str};
// write dpc to testbench
gen_signature_handshake(.instr(debug_main), .signature_type(WRITE_CSR), .csr(DPC));
// write out the counter to the testbench
gen_signature_handshake(.instr(debug_main), .signature_type(WRITE_CSR), .csr(DSCRATCH0));
endfunction
// Check dcsr.cause, and update dpc by 0x4 if the cause is ebreak, as
// ebreak will set set dpc to its own address, which will cause an
// infinite loop.
virtual function void gen_dpc_update();
str = {$sformatf("csrr x%0d, 0x%0x", cfg.scratch_reg, DCSR),
$sformatf("slli x%0d, x%0d, 0x17", cfg.scratch_reg, cfg.scratch_reg),
$sformatf("srli x%0d, x%0d, 0x1d", cfg.scratch_reg, cfg.scratch_reg),
$sformatf("li x%0d, 0x1", cfg.gpr[0]),
$sformatf("bne x%0d, x%0d, 4f", cfg.scratch_reg, cfg.gpr[0])};
debug_main = {debug_main, str};
increment_csr(DPC, 4, debug_main);
str = {"4: nop"};
debug_main = {debug_main, str};
endfunction
// Set dcsr.ebreak(m/s/u)
// TODO(udinator) - randomize the setup for these fields
virtual function void gen_dcsr_ebreak();
if (MACHINE_MODE inside {riscv_instr_pkg::supported_privileged_mode}) begin
str = {$sformatf("li x%0d, 0x8000", cfg.scratch_reg),
$sformatf("csrs dcsr, x%0d", cfg.scratch_reg)};
debug_main = {debug_main, str};
end
if (SUPERVISOR_MODE inside {riscv_instr_pkg::supported_privileged_mode}) begin
str = {$sformatf("li x%0d, 0x2000", cfg.scratch_reg),
$sformatf("csrs dcsr, x%0d", cfg.scratch_reg)};
debug_main = {debug_main, str};
end
if (USER_MODE inside {riscv_instr_pkg::supported_privileged_mode}) begin
str = {$sformatf("li x%0d, 0x1000", cfg.scratch_reg),
$sformatf("csrs dcsr, x%0d", cfg.scratch_reg)};
debug_main = {debug_main, str};
end
endfunction
virtual function void increment_csr(privileged_reg_t csr, int val, ref string instr[$]);
str = {$sformatf("csrr x%0d, 0x%0x", cfg.scratch_reg, csr),
$sformatf("addi x%0d, x%0d, 0x%0x", cfg.scratch_reg, cfg.scratch_reg, val),
$sformatf("csrw 0x%0x, x%0d", csr, cfg.scratch_reg)};
instr = {instr, str};
endfunction
endclass

View file

@ -33,3 +33,47 @@
} \
}
`define INSTR_BODY(instr_n, instr_format, instr_category, instr_group, imm_tp = IMM) \
static bit valid = riscv_instr::register(instr_n); \
`uvm_object_utils(riscv_``instr_n``_instr) \
function new(string name = ""); \
super.new(name); \
this.instr_name = ``instr_n; \
this.format = ``instr_format; \
this.group = ``instr_group; \
this.category = ``instr_category; \
this.imm_type = ``imm_tp; \
set_imm_len(); \
set_rand_mode(); \
endfunction \
endclass
// Regular integer instruction
`define DEFINE_INSTR(instr_n, instr_format, instr_category, instr_group, imm_tp = IMM) \
class riscv_``instr_n``_instr extends riscv_instr; \
`INSTR_BODY(instr_n, instr_format, instr_category, instr_group, imm_tp)
// Floating point instruction
`define DEFINE_FP_INSTR(instr_n, instr_format, instr_category, instr_group, imm_tp = IMM) \
class riscv_``instr_n``_instr extends riscv_floating_point_instr; \
`INSTR_BODY(instr_n, instr_format, instr_category, instr_group, imm_tp)
// A-extension instruction
`define DEFINE_AMO_INSTR(instr_n, instr_format, instr_category, instr_group, imm_tp = IMM) \
class riscv_``instr_n``_instr extends riscv_amo_instr; \
`INSTR_BODY(instr_n, instr_format, instr_category, instr_group, imm_tp)
// Compressed instruction
`define DEFINE_C_INSTR(instr_n, instr_format, instr_category, instr_group, imm_tp = IMM) \
class riscv_``instr_n``_instr extends riscv_compressed_instr; \
`INSTR_BODY(instr_n, instr_format, instr_category, instr_group, imm_tp)
// Floating point compressed instruction
`define DEFINE_FC_INSTR(instr_n, instr_format, instr_category, instr_group, imm_tp = IMM) \
class riscv_``instr_n``_instr extends riscv_compressed_instr; \
`INSTR_BODY(instr_n, instr_format, instr_category, instr_group, imm_tp)
// Vector instruction
`define DEFINE_V_INSTR(instr_n, instr_format, instr_category, instr_group, imm_tp = IMM) \
class riscv_``instr_n``_instr extends riscv_vector_instr; \
`INSTR_BODY(instr_n, instr_format, instr_category, instr_group, imm_tp)

View file

@ -74,10 +74,10 @@ class riscv_mem_access_stream extends riscv_directed_instr_stream;
// Insert some other instructions to mix with mem_access instruction
virtual function void add_mixed_instr(int instr_cnt);
riscv_instr_base instr;
riscv_instr instr;
setup_allowed_instr(1, 1);
for(int i = 0; i < instr_cnt; i ++) begin
instr = riscv_instr_base::type_id::create("instr");
instr = riscv_instr::type_id::create("instr");
randomize_instr(instr);
insert_instr(instr);
end
@ -92,15 +92,15 @@ endclass
// For JAL, restore the stack before doing the jump
class riscv_jump_instr extends riscv_directed_instr_stream;
riscv_instr_base jump;
riscv_instr_base addi;
riscv_instr jump;
riscv_instr addi;
riscv_pseudo_instr la;
riscv_instr_base branch;
riscv_instr branch;
rand riscv_reg_t gpr;
rand int imm;
rand bit enable_branch;
rand int mixed_instr_cnt;
riscv_instr_base stack_exit_instr[];
riscv_instr stack_exit_instr[];
string target_program_label;
int idx;
bit use_jalr;
@ -115,30 +115,32 @@ class riscv_jump_instr extends riscv_directed_instr_stream;
function new(string name = "");
super.new(name);
jump = riscv_instr_base::type_id::create("jump");
la = riscv_pseudo_instr::type_id::create("la");
addi = riscv_instr_base::type_id::create("addi");
branch = riscv_instr_base::type_id::create("branch");
endfunction
function void pre_randomize();
if (use_jalr) begin
jump = riscv_instr::get_rand_instr(.include_instr({JALR}));
end else if (cfg.disable_compressed_instr || (cfg.ra != RA)) begin
jump = riscv_instr::get_rand_instr(.include_instr({JAL, JALR}));
end else begin
jump = riscv_instr::get_rand_instr(.include_instr({JAL, JALR, C_JALR}));
end
addi = riscv_instr::get_rand_instr(.include_instr({ADDI}));
branch = riscv_instr::get_rand_instr(.include_instr({BEQ, BNE, BLT, BGE, BLTU, BGEU}));
endfunction
function void post_randomize();
riscv_instr_base instr[];
riscv_instr instr[];
`DV_CHECK_RANDOMIZE_WITH_FATAL(jump,
(use_jalr) -> (instr_name == JALR);
instr_name dist {JAL := 2, JALR := 6, C_JALR := 2};
if (cfg.disable_compressed_instr || (cfg.ra != RA)) {
instr_name != C_JALR;
if (has_rd) {
rd == cfg.ra;
}
rd == cfg.ra;
rs1 == gpr;
)
`DV_CHECK_RANDOMIZE_WITH_FATAL(addi,
rs1 == gpr;
instr_name == ADDI;
rd == gpr;
)
`DV_CHECK_RANDOMIZE_WITH_FATAL(branch,
instr_name inside {BEQ, BNE, BLT, BGE, BLTU, BGEU};)
if (has_rs1) {
rs1 == gpr;
})
`DV_CHECK_RANDOMIZE_WITH_FATAL(addi, rs1 == gpr; rd == gpr;)
`DV_CHECK_RANDOMIZE_FATAL(branch)
la.pseudo_instr_name = LA;
la.imm_str = target_program_label;
la.rd = gpr;
@ -180,9 +182,9 @@ endclass
// Stress back to back jump instruction
class riscv_jal_instr extends riscv_rand_instr_stream;
riscv_instr_base jump[];
riscv_instr_base jump_start;
riscv_instr_base jump_end;
riscv_instr jump[];
riscv_instr jump_start;
riscv_instr jump_end;
rand int unsigned num_of_jump_instr;
riscv_instr_name_t jal[$];
@ -213,23 +215,20 @@ class riscv_jal_instr extends riscv_rand_instr_stream;
end
end
// First instruction
jump_start = riscv_instr_base::type_id::create("jump_start");
`DV_CHECK_RANDOMIZE_WITH_FATAL(jump_start,
instr_name == JAL;
rd == cfg.ra;
)
jump_start = riscv_instr::get_rand_instr(.include_instr({JAL}));
`DV_CHECK_RANDOMIZE_WITH_FATAL(jump_start, rd == cfg.ra;)
jump_start.imm_str = $sformatf("%0df", order[0]);
jump_start.label = label;
// Last instruction
jump_end = riscv_instr_base::type_id::create("jump_end");
randomize_instr(jump_end);
jump_end.label = $sformatf("%0d", num_of_jump_instr);
foreach (jump[i]) begin
jump[i] = riscv_instr_base::type_id::create($sformatf("jump_%0d", i));
jump[i] = riscv_instr::get_rand_instr(.include_instr({jal}));
`DV_CHECK_RANDOMIZE_WITH_FATAL(jump[i],
instr_name inside {jal};
rd dist {RA := 5, T1 := 2, [SP:T0] :/ 1, [T2:T6] :/ 2};
!(rd inside {cfg.reserved_regs});
if (has_rd) {
rd dist {RA := 5, T1 := 2, [SP:T0] :/ 1, [T2:T6] :/ 2};
!(rd inside {cfg.reserved_regs});
}
)
jump[i].label = $sformatf("%0d", i);
end
@ -258,9 +257,9 @@ class riscv_push_stack_instr extends riscv_rand_instr_stream;
int stack_len;
int num_of_reg_to_save;
int num_of_redudant_instr;
riscv_instr_base push_stack_instr[];
riscv_instr push_stack_instr[];
riscv_reg_t saved_regs[];
rand riscv_rand_instr branch_instr;
riscv_instr branch_instr;
rand bit enable_branch;
string push_start_label;
@ -289,19 +288,22 @@ class riscv_push_stack_instr extends riscv_rand_instr_stream;
gen_instr(1'b1);
push_stack_instr = new[num_of_reg_to_save+1];
foreach(push_stack_instr[i]) begin
push_stack_instr[i] = riscv_instr_base::type_id::
create($sformatf("push_stack_instr_%0d", i));
push_stack_instr[i] = riscv_instr::type_id::
create($sformatf("push_stack_instr_%0d", i));
end
// addi sp,sp,-imm
push_stack_instr[0] = riscv_instr::get_rand_instr(.include_instr({ADDI}));
`DV_CHECK_RANDOMIZE_WITH_FATAL(push_stack_instr[0],
instr_name == ADDI; rd == cfg.sp; rs1 == cfg.sp;
rd == cfg.sp; rs1 == cfg.sp;
imm == (~stack_len + 1);)
push_stack_instr[0].imm_str = $sformatf("-%0d", stack_len);
foreach(saved_regs[i]) begin
if(XLEN == 32) begin
push_stack_instr[i+1] = riscv_instr::get_rand_instr(.include_instr({SW}));
`DV_CHECK_RANDOMIZE_WITH_FATAL(push_stack_instr[i+1],
instr_name == SW; rs2 == saved_regs[i]; rs1 == cfg.sp; imm == 4 * (i+1);)
rs2 == saved_regs[i]; rs1 == cfg.sp; imm == 4 * (i+1);)
end else begin
push_stack_instr[i+1] = riscv_instr::get_rand_instr(.include_instr({SD}));
`DV_CHECK_RANDOMIZE_WITH_FATAL(push_stack_instr[i+1],
instr_name == SD; rs2 == saved_regs[i]; rs1 == cfg.sp; imm == 8 * (i+1);)
end
@ -313,15 +315,8 @@ class riscv_push_stack_instr extends riscv_rand_instr_stream;
enable_branch = 0;
end
if(enable_branch) begin
// Cover jal -> branch scenario, the branch is added before push stack operation
branch_instr = riscv_rand_instr::type_id::create("branch_instr");
branch_instr.cfg = cfg;
`ifdef DSIM
`DV_CHECK_RANDOMIZE_WITH_FATAL(branch_instr,
instr_name inside {[BEQ:BGEU], C_BEQZ, C_BNEZ};)
`else
`DV_CHECK_RANDOMIZE_WITH_FATAL(branch_instr, category == BRANCH;)
`endif
branch_instr = riscv_instr::get_rand_instr(.include_category({BRANCH}));
`DV_CHECK_RANDOMIZE_FATAL(branch_instr)
branch_instr.imm_str = push_start_label;
branch_instr.branch_assigned = 1'b1;
push_stack_instr[0].label = push_start_label;
@ -341,11 +336,11 @@ endclass
// Pop stack instruction stream
class riscv_pop_stack_instr extends riscv_rand_instr_stream;
int stack_len;
int num_of_reg_to_save;
int num_of_redudant_instr;
riscv_instr_base pop_stack_instr[];
riscv_reg_t saved_regs[];
int stack_len;
int num_of_reg_to_save;
int num_of_redudant_instr;
riscv_instr pop_stack_instr[];
riscv_reg_t saved_regs[];
`uvm_object_utils(riscv_pop_stack_instr)
@ -371,22 +366,25 @@ class riscv_pop_stack_instr extends riscv_rand_instr_stream;
gen_instr(1'b1);
pop_stack_instr = new[num_of_reg_to_save+1];
foreach(pop_stack_instr[i]) begin
pop_stack_instr[i] = riscv_instr_base::type_id::
create($sformatf("pop_stack_instr_%0d", i));
pop_stack_instr[i] = riscv_instr::type_id::
create($sformatf("pop_stack_instr_%0d", i));
end
foreach(saved_regs[i]) begin
if(XLEN == 32) begin
pop_stack_instr[i] = riscv_instr::get_rand_instr(.include_instr({LW}));
`DV_CHECK_RANDOMIZE_WITH_FATAL(pop_stack_instr[i],
instr_name == LW; rd == saved_regs[i]; rs1 == cfg.sp; imm == 4 * (i+1);)
rd == saved_regs[i]; rs1 == cfg.sp; imm == 4 * (i+1);)
end else begin
pop_stack_instr[i] = riscv_instr::get_rand_instr(.include_instr({LD}));
`DV_CHECK_RANDOMIZE_WITH_FATAL(pop_stack_instr[i],
instr_name == LD; rd == saved_regs[i]; rs1 == cfg.sp; imm == 8 * (i+1);)
rd == saved_regs[i]; rs1 == cfg.sp; imm == 8 * (i+1);)
end
pop_stack_instr[i].process_load_store = 0;
end
// addi sp,sp,imm
pop_stack_instr[num_of_reg_to_save] = riscv_instr::get_rand_instr(.include_instr({ADDI}));
`DV_CHECK_RANDOMIZE_WITH_FATAL(pop_stack_instr[num_of_reg_to_save],
instr_name == ADDI; rd == cfg.sp; rs1 == cfg.sp; imm == stack_len;)
rd == cfg.sp; rs1 == cfg.sp; imm == stack_len;)
pop_stack_instr[num_of_reg_to_save].imm_str = $sformatf("%0d", stack_len);
mix_instr_stream(pop_stack_instr);
foreach(instr_list[i]) begin
@ -396,136 +394,3 @@ class riscv_pop_stack_instr extends riscv_rand_instr_stream;
endfunction
endclass
// Cover the long fprward and backward jump
class riscv_long_branch_instr extends riscv_rand_instr_stream;
int branch_instr_stream_len = 100;
int branch_instr_offset = 999;
riscv_rand_instr_stream forward_branch_instr_stream;
riscv_rand_instr_stream backward_branch_instr_stream;
riscv_instr_base jump_instr;
`uvm_object_utils(riscv_long_branch_instr)
function new(string name = "");
super.new(name);
forward_branch_instr_stream = riscv_rand_instr_stream::type_id::
create("forward_branch_instr_stream");
backward_branch_instr_stream = riscv_rand_instr_stream::type_id::
create("backward_branch_instr_stream");
jump_instr = riscv_instr_base::type_id::create("jump_instr");
endfunction
function void init(int instr_len);
branch_instr_stream_len = instr_len;
initialize_instr_list(branch_instr_offset-branch_instr_stream_len);
forward_branch_instr_stream.cfg = cfg;
backward_branch_instr_stream.cfg = cfg;
forward_branch_instr_stream.initialize_instr_list(branch_instr_stream_len);
backward_branch_instr_stream.initialize_instr_list(branch_instr_stream_len);
endfunction
virtual function void gen_instr(bit no_branch = 1'b0, bit no_load_store = 1'b1,
bit is_debug_program = 1'b0);
int branch_offset;
super.gen_instr(1'b1);
forward_branch_instr_stream.gen_instr();
backward_branch_instr_stream.gen_instr();
`DV_CHECK_RANDOMIZE_WITH_FATAL(jump_instr, instr_name == JAL;)
jump_instr.imm_str = "test_done";
instr_list = {forward_branch_instr_stream.instr_list, instr_list,
jump_instr, backward_branch_instr_stream.instr_list};
foreach(instr_list[i]) begin
instr_list[i].atomic = 1'b1;
if(!instr_list[i].is_branch_target) begin
instr_list[i].has_label = 1'b0;
end
if(instr_list[i].category == BRANCH) begin
if(i < branch_instr_stream_len)
branch_offset = branch_instr_offset;
else
branch_offset = -branch_instr_offset;
instr_list[i].imm_str = $sformatf("target_%0d", i);
instr_list[i].branch_assigned = 1'b1;
// Avoid dead loop
if(((instr_list[i+branch_offset].category == BRANCH) ||
instr_list[i+branch_offset].is_branch_target) && (branch_offset < 0))
branch_offset = branch_offset + 1;
`uvm_info(get_full_name(), $sformatf("Branch [%0d] %0s -> [%0d] %0s", i,
instr_list[i].convert2asm(), i+branch_offset,
instr_list[i+branch_offset].convert2asm()), UVM_LOW)
if(i < -branch_offset)
`uvm_fatal(get_name(), $sformatf("Unexpected branch instr at %0d", i))
instr_list[i+branch_offset].label = $sformatf("target_%0d", i);
instr_list[i+branch_offset].has_label = 1'b1;
instr_list[i+branch_offset].is_branch_target = 1;
end
end
endfunction
endclass
class riscv_sw_interrupt_instr extends riscv_directed_instr_stream;
rand bit usip;
rand bit ssip;
rand bit msip;
rand privileged_reg_t ip_reg;
rand riscv_pseudo_instr li_instr;
rand riscv_instr_base csr_instr;
riscv_privil_reg ip;
rand riscv_reg_t rs1_reg;
constraint ip_reg_c {
if(cfg.init_privileged_mode == MACHINE_MODE) {
ip_reg == MIP;
} else {
ip_reg == SIP;
}
(ip_reg == MIP) -> (usip || ssip || msip);
(ip_reg == SIP) -> (usip || ssip);
}
constraint instr_c {
!(rs1_reg inside {cfg.reserved_regs});
rs1_reg != ZERO;
li_instr.pseudo_instr_name == LI;
li_instr.rd == rs1_reg;
csr_instr.instr_name == CSRRW;
csr_instr.rs1 == rs1_reg;
// TODO: Support non-zero rd for SIP, MIP
// csr_instr.rd inside {cfg.avail_regs};
csr_instr.rd == ZERO;
csr_instr.csr == ip_reg;
}
`uvm_object_utils(riscv_sw_interrupt_instr)
function new(string name = "");
super.new(name);
li_instr = riscv_pseudo_instr::type_id::create("li_instr");
csr_instr = riscv_instr_base::type_id::create("csr_instr");
ip = riscv_privil_reg::type_id::create("ip");
endfunction
function void post_randomize();
// TODO: Support UIP
if(cfg.init_privileged_mode == USER_MODE) return;
ip.init_reg(ip_reg);
if(ip_reg == SIP) begin
ip.set_field("USIP", usip);
ip.set_field("SSIP", ssip);
end else begin
ip.set_field("USIP", usip);
ip.set_field("SSIP", ssip);
ip.set_field("MSIP", msip);
end
li_instr.imm_str = $sformatf("0x%0x", ip.get_val());
csr_instr.comment = ip_reg.name();
instr_list = {li_instr, csr_instr};
super.post_randomize();
endfunction
endclass

View file

@ -281,6 +281,9 @@ class riscv_illegal_instr extends uvm_object;
if (!compressed) {
if (exception == kIllegalFunc7) {
!(func7 inside {7'b0, 7'b0100000, 7'b1});
if (opcode == 7'b001001) { // SLLI, SRLI, SRAI
!(func7[6:1] inside {6'b0, 6'b010000});
}
} else {
func7 inside {7'b0, 7'b0100000, 7'b1};
}

View file

@ -1,4 +1,4 @@
class riscv_instr_cov_item extends riscv_instr_base;
class riscv_instr_cov_item extends `INSTR;
typedef enum bit[1:0] {
POSITIVE, NEGATIVE
@ -23,6 +23,9 @@ class riscv_instr_cov_item extends riscv_instr_base;
rand bit [XLEN-1:0] rs1_value;
rand bit [XLEN-1:0] rs2_value;
rand bit [XLEN-1:0] rd_value;
rand bit [XLEN-1:0] fs1_value;
rand bit [XLEN-1:0] fs2_value;
rand bit [XLEN-1:0] fd_value;
bit [31:0] binary;
bit [XLEN-1:0] pc;
bit [XLEN-1:0] mem_addr;
@ -46,6 +49,8 @@ class riscv_instr_cov_item extends riscv_instr_base;
logical_similarity_e logical_similarity;
string trace;
`VECTOR_INCLUDE("riscv_instr_cov_item_inc_declares.sv")
`uvm_object_utils(riscv_instr_cov_item)
`uvm_object_new

View file

@ -14,6 +14,14 @@
* limitations under the License.
*/
`ifndef COMPLIANCE_MODE
`define DV(TEXT) TEXT
`else
`define DV(TEXT)
`endif
`define SAMPLE(cg, val) \
if (cg != null) cg.sample(val);
`define INSTR_CG_BEGIN(INSTR_NAME) \
covergroup ``INSTR_NAME``_cg with function sample(riscv_instr_cov_item instr);
@ -26,7 +34,7 @@
cp_rs1_sign : coverpoint instr.rs1_sign; \
cp_rs2_sign : coverpoint instr.rs2_sign; \
cp_rd_sign : coverpoint instr.rd_sign; \
cp_gpr_hazard : coverpoint instr.gpr_hazard; \
`DV(cp_gpr_hazard : coverpoint instr.gpr_hazard;) \
`define CMP_INSTR_CG_BEGIN(INSTR_NAME) \
`INSTR_CG_BEGIN(INSTR_NAME) \
@ -34,7 +42,7 @@
cp_rd : coverpoint instr.rd; \
cp_rs1_sign : coverpoint instr.rs1_sign; \
cp_result : coverpoint instr.rd_value[0]; \
cp_gpr_hazard : coverpoint instr.gpr_hazard; \
`DV(cp_gpr_hazard : coverpoint instr.gpr_hazard;) \
`define SB_INSTR_CG_BEGIN(INSTR_NAME) \
`INSTR_CG_BEGIN(INSTR_NAME) \
@ -45,35 +53,35 @@
cp_imm_sign : coverpoint instr.imm_sign; \
cp_branch_hit : coverpoint instr.branch_hit; \
cp_sign_cross : cross cp_rs1_sign, cp_rs2_sign; \
cp_gpr_hazard : coverpoint instr.gpr_hazard { \
`DV(cp_gpr_hazard : coverpoint instr.gpr_hazard { \
bins valid_hazard[] = {NO_HAZARD, RAW_HAZARD}; \
}
}) \
`define STORE_INSTR_CG_BEGIN(INSTR_NAME) \
`INSTR_CG_BEGIN(INSTR_NAME) \
cp_rs1 : coverpoint instr.rs1 { \
ignore_bins zero = {ZERO}; \
`DV(ignore_bins zero = {ZERO};) \
} \
cp_rs2 : coverpoint instr.rs2; \
cp_imm_sign : coverpoint instr.imm_sign; \
cp_gpr_hazard : coverpoint instr.gpr_hazard { \
`DV(cp_gpr_hazard : coverpoint instr.gpr_hazard { \
bins valid_hazard[] = {NO_HAZARD, RAW_HAZARD}; \
} \
cp_lsu_hazard : coverpoint instr.lsu_hazard { \
}) \
`DV(cp_lsu_hazard : coverpoint instr.lsu_hazard { \
bins valid_hazard[] = {NO_HAZARD, WAR_HAZARD, WAW_HAZARD}; \
}
}) \
`define LOAD_INSTR_CG_BEGIN(INSTR_NAME) \
`INSTR_CG_BEGIN(INSTR_NAME) \
cp_rs1 : coverpoint instr.rs1 { \
ignore_bins zero = {ZERO}; \
`DV(ignore_bins zero = {ZERO};) \
} \
cp_rd : coverpoint instr.rd; \
cp_imm_sign : coverpoint instr.imm_sign; \
cp_gpr_hazard : coverpoint instr.gpr_hazard; \
cp_lsu_hazard : coverpoint instr.lsu_hazard { \
`DV(cp_gpr_hazard : coverpoint instr.gpr_hazard;) \
`DV(cp_lsu_hazard : coverpoint instr.lsu_hazard { \
bins valid_hazard[] = {NO_HAZARD, RAW_HAZARD}; \
}
}) \
`define I_INSTR_CG_BEGIN(INSTR_NAME) \
`INSTR_CG_BEGIN(INSTR_NAME) \
@ -82,14 +90,13 @@
cp_rs1_sign : coverpoint instr.rs1_sign; \
cp_rd_sign : coverpoint instr.rd_sign; \
cp_imm_sign : coverpoint instr.imm_sign; \
cp_gpr_hazard : coverpoint instr.gpr_hazard;
`DV(cp_gpr_hazard : coverpoint instr.gpr_hazard;)
`define U_INSTR_CG_BEGIN(INSTR_NAME) \
`INSTR_CG_BEGIN(INSTR_NAME) \
cp_rd : coverpoint instr.rd; \
cp_rd_sign : coverpoint instr.rd_sign; \
cp_gpr_hazard : coverpoint instr.gpr_hazard;
`DV(cp_gpr_hazard : coverpoint instr.gpr_hazard;)
`define J_INSTR_CG_BEGIN(INSTR_NAME) \
`INSTR_CG_BEGIN(INSTR_NAME) \
@ -97,35 +104,34 @@
cp_rd : coverpoint instr.rd; \
cp_rd_align : coverpoint instr.rd_value[1];
`define CSR_INSTR_CG_BEGIN(INSTR_NAME) \
`INSTR_CG_BEGIN(INSTR_NAME) \
cp_rd : coverpoint instr.rd; \
cp_gpr_hazard : coverpoint instr.gpr_hazard;
`DV(cp_gpr_hazard : coverpoint instr.gpr_hazard;)
`define CR_INSTR_CG_BEGIN(INSTR_NAME) \
`INSTR_CG_BEGIN(INSTR_NAME) \
cp_rs2 : coverpoint instr.rs2; \
cp_rd : coverpoint instr.rd; \
cp_rs2_sign : coverpoint instr.rs2_sign; \
cp_gpr_hazard : coverpoint instr.gpr_hazard;
`DV(cp_gpr_hazard : coverpoint instr.gpr_hazard;)
`define CI_INSTR_CG_BEGIN(INSTR_NAME) \
`INSTR_CG_BEGIN(INSTR_NAME) \
cp_rd : coverpoint instr.rd; \
cp_imm_sign : coverpoint instr.imm_sign; \
cp_gpr_hazard : coverpoint instr.gpr_hazard { \
`DV(cp_gpr_hazard : coverpoint instr.gpr_hazard { \
bins valid_hazard[] = {NO_HAZARD, WAR_HAZARD, WAW_HAZARD}; \
}
})
`define CSS_INSTR_CG_BEGIN(INSTR_NAME) \
`INSTR_CG_BEGIN(INSTR_NAME) \
cp_rs2 : coverpoint instr.rs2; \
cp_imm_sign : coverpoint instr.imm_sign; \
cp_rs2_sign : coverpoint instr.rs2_sign; \
cp_gpr_hazard : coverpoint instr.gpr_hazard { \
`DV(cp_gpr_hazard : coverpoint instr.gpr_hazard { \
bins valid_hazard[] = {NO_HAZARD, RAW_HAZARD}; \
}
})
`define CIW_INSTR_CG_BEGIN(INSTR_NAME) \
`INSTR_CG_BEGIN(INSTR_NAME) \
@ -133,9 +139,9 @@
cp_rd : coverpoint instr.rd { \
bins gpr[] = {S0, S1, A0, A1, A2, A3, A4, A5}; \
} \
cp_gpr_hazard : coverpoint instr.gpr_hazard { \
`DV(cp_gpr_hazard : coverpoint instr.gpr_hazard { \
bins valid_hazard[] = {NO_HAZARD, WAR_HAZARD, WAW_HAZARD}; \
}
})
`define CL_INSTR_CG_BEGIN(INSTR_NAME) \
`INSTR_CG_BEGIN(INSTR_NAME) \
@ -145,10 +151,10 @@
cp_rd : coverpoint instr.rd { \
bins gpr[] = {S0, S1, A0, A1, A2, A3, A4, A5}; \
} \
cp_gpr_hazard : coverpoint instr.gpr_hazard; \
cp_lsu_hazard : coverpoint instr.lsu_hazard { \
`DV(cp_gpr_hazard : coverpoint instr.gpr_hazard;) \
`DV(cp_lsu_hazard : coverpoint instr.lsu_hazard { \
bins valid_hazard[] = {NO_HAZARD, RAW_HAZARD}; \
}
})
`define CL_SP_INSTR_CG_BEGIN(INSTR_NAME) \
`INSTR_CG_BEGIN(INSTR_NAME) \
@ -164,12 +170,12 @@
cp_rs2 : coverpoint instr.rs2 { \
bins gpr[] = {S0, S1, A0, A1, A2, A3, A4, A5}; \
} \
cp_gpr_hazard : coverpoint instr.gpr_hazard { \
`DV(cp_gpr_hazard : coverpoint instr.gpr_hazard { \
bins valid_hazard[] = {NO_HAZARD, RAW_HAZARD}; \
} \
cp_lsu_hazard : coverpoint instr.lsu_hazard { \
}) \
`DV(cp_lsu_hazard : coverpoint instr.lsu_hazard { \
bins valid_hazard[] = {NO_HAZARD, WAR_HAZARD, WAW_HAZARD}; \
}
})
`define CS_SP_INSTR_CG_BEGIN(INSTR_NAME) \
`INSTR_CG_BEGIN(INSTR_NAME) \
@ -185,7 +191,7 @@
cp_rs2 : coverpoint instr.rs2 { \
bins gpr[] = {S0, S1, A0, A1, A2, A3, A4, A5}; \
} \
cp_gpr_hazard : coverpoint instr.gpr_hazard;
`DV(cp_gpr_hazard : coverpoint instr.gpr_hazard;) \
`define CB_INSTR_CG_BEGIN(INSTR_NAME) \
@ -193,9 +199,9 @@
cp_rs1 : coverpoint instr.rs1 { \
bins gpr[] = {S0, S1, A0, A1, A2, A3, A4, A5}; \
} \
cp_gpr_hazard : coverpoint instr.gpr_hazard { \
`DV(cp_gpr_hazard : coverpoint instr.gpr_hazard { \
bins valid_hazard[] = {NO_HAZARD, RAW_HAZARD}; \
}
})
`define CJ_INSTR_CG_BEGIN(INSTR_NAME) \
`INSTR_CG_BEGIN(INSTR_NAME) \
@ -203,6 +209,12 @@
`define CG_END endgroup
`define CG_SELECTOR_BEGIN(CG_ISA) \
if ((CG_ISA inside {supported_isa}) && (!select_isa || (cov_isa == CG_ISA))) begin
`define CG_SELECTOR_END \
end
class riscv_instr_cover_group;
riscv_instr_gen_config cfg;
@ -212,6 +224,25 @@ class riscv_instr_cover_group;
int unsigned instr_cnt;
int unsigned branch_instr_cnt;
bit [4:0] branch_hit_history; // The last 5 branch result
exception_cause_t ignored_exceptions[];
// Mode of the coverage model
// In complicance mode, all the micro-architecture related covergroups are removed. Only the ones
// related to RISC-V specification compliance is sampled.
bit compliance_mode;
// By default the coverage model run with instruction trace from ISS simulation. When simulating
// with ISS, certain covergroups like debug/interrupt could not be hit as these are not simulated
// with ISS. You can turn off this mode by adding +iss_mode=0 if you use the instruction trace
// from RTL simulation.
bit iss_mode = 1'b1;
// Select an ISA extension to cover
bit select_isa;
riscv_instr_group_t cov_isa;
`VECTOR_INCLUDE("riscv_instr_cover_group_inc_cpu_declare.sv")
///////////// RV32I instruction functional coverage //////////////
@ -252,7 +283,7 @@ class riscv_instr_cover_group;
cp_rd : coverpoint instr.rd;
cp_rs1_sign : coverpoint instr.rs1_sign;
cp_rd_sign : coverpoint instr.rd_sign;
cp_gpr_hazard : coverpoint instr.gpr_hazard;
`DV(cp_gpr_hazard : coverpoint instr.gpr_hazard;)
`CG_END
`INSTR_CG_BEGIN(slli)
@ -260,7 +291,7 @@ class riscv_instr_cover_group;
cp_rd : coverpoint instr.rd;
cp_rs1_sign : coverpoint instr.rs1_sign;
cp_rd_sign : coverpoint instr.rd_sign;
cp_gpr_hazard : coverpoint instr.gpr_hazard;
`DV(cp_gpr_hazard : coverpoint instr.gpr_hazard;)
`CG_END
`INSTR_CG_BEGIN(srli)
@ -268,7 +299,7 @@ class riscv_instr_cover_group;
cp_rd : coverpoint instr.rd;
cp_rs1_sign : coverpoint instr.rs1_sign;
cp_rd_sign : coverpoint instr.rd_sign;
cp_gpr_hazard : coverpoint instr.gpr_hazard;
`DV(cp_gpr_hazard : coverpoint instr.gpr_hazard;)
`CG_END
// Logical instructions
@ -422,7 +453,6 @@ class riscv_instr_cover_group;
endgroup
// RV32M
`R_INSTR_CG_BEGIN(mul)
cp_sign_cross: cross cp_rs1_sign, cp_rs2_sign;
`CG_END
@ -546,7 +576,7 @@ class riscv_instr_cover_group;
cp_rd : coverpoint instr.rd;
cp_rs1_sign : coverpoint instr.rs1_sign;
cp_rd_sign : coverpoint instr.rd_sign;
cp_gpr_hazard : coverpoint instr.gpr_hazard;
`DV(cp_gpr_hazard : coverpoint instr.gpr_hazard;)
`CG_END
`INSTR_CG_BEGIN(slliw)
@ -554,7 +584,7 @@ class riscv_instr_cover_group;
cp_rd : coverpoint instr.rd;
cp_rs1_sign : coverpoint instr.rs1_sign;
cp_rd_sign : coverpoint instr.rd_sign;
cp_gpr_hazard : coverpoint instr.gpr_hazard;
`DV(cp_gpr_hazard : coverpoint instr.gpr_hazard;)
`CG_END
`INSTR_CG_BEGIN(srliw)
@ -562,7 +592,7 @@ class riscv_instr_cover_group;
cp_rd : coverpoint instr.rd;
cp_rs1_sign : coverpoint instr.rs1_sign;
cp_rd_sign : coverpoint instr.rd_sign;
cp_gpr_hazard : coverpoint instr.gpr_hazard;
`DV(cp_gpr_hazard : coverpoint instr.gpr_hazard;)
`CG_END
`R_INSTR_CG_BEGIN(addw)
@ -599,9 +629,9 @@ class riscv_instr_cover_group;
`INSTR_CG_BEGIN(c_addi16sp)
cp_imm_sign : coverpoint instr.imm_sign;
cp_gpr_hazard : coverpoint instr.gpr_hazard {
`DV(cp_gpr_hazard : coverpoint instr.gpr_hazard {
bins valid_hazard[] = {NO_HAZARD, WAR_HAZARD, WAW_HAZARD};
}
})
`CG_END
`CI_INSTR_CG_BEGIN(c_li)
@ -609,7 +639,7 @@ class riscv_instr_cover_group;
`INSTR_CG_BEGIN(c_lui)
cp_rd : coverpoint instr.rd {
ignore_bins bin = {ZERO, SP};
`DV(ignore_bins bin = {ZERO, SP};)
}
`CG_END
@ -651,9 +681,9 @@ class riscv_instr_cover_group;
`INSTR_CG_BEGIN(c_slli)
cp_rd : coverpoint instr.rd;
cp_gpr_hazard : coverpoint instr.gpr_hazard {
`DV(cp_gpr_hazard : coverpoint instr.gpr_hazard {
bins valid_hazard[] = {NO_HAZARD, RAW_HAZARD};
}
})
`CG_END
`CJ_INSTR_CG_BEGIN(c_j)
@ -664,13 +694,13 @@ class riscv_instr_cover_group;
`INSTR_CG_BEGIN(c_jr)
cp_rs1 : coverpoint instr.rs1 {
ignore_bins zero = {ZERO};
`DV(ignore_bins zero = {ZERO};)
}
`CG_END
`INSTR_CG_BEGIN(c_jalr)
cp_rs1 : coverpoint instr.rs1 {
ignore_bins zero = {ZERO};
`DV(ignore_bins zero = {ZERO};)
}
cp_rd_align : coverpoint instr.rd_value[1];
`CG_END
@ -783,7 +813,8 @@ class riscv_instr_cover_group;
// Privileged CSR covergroup
covergroup mcause_exception_cg with function sample(exception_cause_t exception);
cp_exception: coverpoint exception {
bins exception[] = cp_exception with (item inside {implemented_exception});
bins exception[] = cp_exception with ((item inside {implemented_exception}) &&
!(item inside {ignored_exceptions}));
}
endgroup
@ -805,65 +836,113 @@ class riscv_instr_cover_group;
cp_mpp : coverpoint val[12:11];
endgroup
`VECTOR_INCLUDE("riscv_instr_cover_group_inc_cg_add.sv")
function new(riscv_instr_gen_config cfg);
string opts;
this.cfg = cfg;
cur_instr = riscv_instr_cov_item::type_id::create("cur_instr");
pre_instr = riscv_instr_cov_item::type_id::create("pre_instr");
build_instr_list();
// RV32I instruction functional coverage instantiation
add_cg = new();
sub_cg = new();
addi_cg = new();
lui_cg = new();
auipc_cg = new();
sll_cg = new();
srl_cg = new();
sra_cg = new();
slli_cg = new();
srli_cg = new();
srai_cg = new();
and_cg = new();
or_cg = new();
xor_cg = new();
andi_cg = new();
ori_cg = new();
xori_cg = new();
slt_cg = new();
sltu_cg = new();
slti_cg = new();
sltiu_cg = new();
jal_cg = new();
jalr_cg = new();
beq_cg = new();
bne_cg = new();
blt_cg = new();
bge_cg = new();
bgeu_cg = new();
bltu_cg = new();
lb_cg = new();
lh_cg = new();
lw_cg = new();
lbu_cg = new();
lhu_cg = new();
sb_cg = new();
sh_cg = new();
sw_cg = new();
csrrw_cg = new();
csrrs_cg = new();
csrrc_cg = new();
csrrwi_cg = new();
csrrsi_cg = new();
csrrci_cg = new();
// instr_trans_cg = new();
branch_hit_history_cg = new();
rv32i_misc_cg = new();
if (RV32C inside {supported_isa}) begin
illegal_compressed_instr_cg = new();
compressed_opcode_cg = new();
hint_cg = new();
`ifdef COMPLIANCE_MODE
compliance_mode = 1;
`endif
// process coverage options
void'($value$plusargs("iss_mode=%0d", iss_mode));
if ($value$plusargs("cov_isa=%0s", opts)) begin
if (!uvm_enum_wrapper#(riscv_instr_group_t)::from_name(opts, cov_isa)) begin
`uvm_fatal("riscv_instr_covergroup",
$sformatf("Cannot find enum for specifed cov_isa=%0s", opts))
end
select_isa = 1'b1;
end
opcode_cg = new();
if (RV32M inside {supported_isa}) begin
// TODO if we want to selectively enable/disable coverage based on categories...
// e.g. +cov_category=OPV_CONFIG
if ($test$plusargs("cov_category=")) begin
string cov_category_str;
void'($value$plusargs("cov_category=%0s", cov_category_str));
$display("coverage option: +cov_category=%0s", cov_category_str);
// used to further subset coverage (used by vectors)
end
if ($test$plusargs("stop_on_first_error")) begin
$display("coverage option: +stop_on_first_error");
end
`VECTOR_INCLUDE("riscv_instr_cover_group_inc_cg_instantiation.sv")
// RV32I instruction functional coverage instantiation
`CG_SELECTOR_BEGIN(RV32I)
add_cg = new();
sub_cg = new();
addi_cg = new();
lui_cg = new();
auipc_cg = new();
sll_cg = new();
srl_cg = new();
sra_cg = new();
slli_cg = new();
srli_cg = new();
srai_cg = new();
and_cg = new();
or_cg = new();
xor_cg = new();
andi_cg = new();
ori_cg = new();
xori_cg = new();
slt_cg = new();
sltu_cg = new();
slti_cg = new();
sltiu_cg = new();
jal_cg = new();
jalr_cg = new();
beq_cg = new();
bne_cg = new();
blt_cg = new();
bge_cg = new();
bgeu_cg = new();
bltu_cg = new();
lb_cg = new();
lh_cg = new();
lw_cg = new();
lbu_cg = new();
lhu_cg = new();
sb_cg = new();
sh_cg = new();
sw_cg = new();
`CG_SELECTOR_END
// TODO sort when there is a RV32ZICSR isa enum
if (RV32I inside {supported_isa}) begin
if (!compliance_mode) begin
csrrw_cg = new();
csrrs_cg = new();
csrrc_cg = new();
csrrwi_cg = new();
csrrsi_cg = new();
csrrci_cg = new();
end
end
if (!compliance_mode) begin
// instr_trans_cg = new();
branch_hit_history_cg = new();
rv32i_misc_cg = new();
opcode_cg = new();
end
if (RV32C inside {supported_isa} || RV64C inside {supported_isa}) begin
if (!compliance_mode) begin
compressed_opcode_cg = new();
hint_cg = new();
if (!cfg.disable_compressed_instr) begin
illegal_compressed_instr_cg = new();
end
end
end
`CG_SELECTOR_BEGIN(RV32M)
mul_cg = new();
mulh_cg = new();
mulhsu_cg = new();
@ -872,15 +951,17 @@ class riscv_instr_cover_group;
divu_cg = new();
rem_cg = new();
remu_cg = new();
end
if (RV64M inside {supported_isa}) begin
`CG_SELECTOR_END
`CG_SELECTOR_BEGIN(RV64M)
mulw_cg = new();
divw_cg = new();
divuw_cg = new();
remw_cg = new();
remuw_cg = new();
end
if (RV64I inside {supported_isa}) begin
`CG_SELECTOR_END
`CG_SELECTOR_BEGIN(RV64I)
lwu_cg = new();
ld_cg = new();
sd_cg = new();
@ -896,8 +977,9 @@ class riscv_instr_cover_group;
addw_cg = new();
addiw_cg = new();
subw_cg = new();
end
if (RV32C inside {supported_isa}) begin
`CG_SELECTOR_END
`CG_SELECTOR_BEGIN(RV32C)
c_lw_cg = new();
c_sw_cg = new();
c_lwsp_cg = new();
@ -925,8 +1007,9 @@ class riscv_instr_cover_group;
end
c_jr_cg = new();
c_jalr_cg = new();
end
if (RV64C inside {supported_isa}) begin
`CG_SELECTOR_END
`CG_SELECTOR_BEGIN(RV64C)
c_ld_cg = new();
c_sd_cg = new();
c_ldsp_cg = new();
@ -934,14 +1017,27 @@ class riscv_instr_cover_group;
c_addiw_cg = new();
c_subw_cg = new();
c_addw_cg = new();
`CG_SELECTOR_END
// Ignore the exception which cannot be covered when running with ISS
if (iss_mode) begin
ignored_exceptions = {INSTRUCTION_ACCESS_FAULT, LOAD_ACCESS_FAULT};
if (support_unaligned_load_store) begin
ignored_exceptions = {ignored_exceptions, LOAD_ADDRESS_MISALIGNED};
end
end
privileged_csr_cg = new();
mcause_exception_cg = new();
mcause_interrupt_cg = new();
if (RV32C inside {supported_isa}) begin
mepc_alignment_cg = new();
if (!compliance_mode) begin
privileged_csr_cg = new();
mcause_exception_cg = new();
if (!iss_mode) begin
mcause_interrupt_cg = new();
end
if (!cfg.disable_compressed_instr) begin
mepc_alignment_cg = new();
end
mstatus_m_cg = new();
end
mstatus_m_cg = new();
endfunction
function void sample(riscv_instr_cov_item instr);
@ -950,134 +1046,143 @@ class riscv_instr_cover_group;
instr.check_hazard_condition(pre_instr);
end
if ((instr.binary[1:0] != 2'b11) && (RV32C inside {supported_isa})) begin
hint_cg.sample(instr);
compressed_opcode_cg.sample(instr.binary[15:0]);
if (!compliance_mode) begin
hint_cg.sample(instr);
compressed_opcode_cg.sample(instr.binary[15:0]);
end
end
if (instr.binary[1:0] == 2'b11) begin
opcode_cg.sample(instr.binary[6:2]);
if (!compliance_mode) begin
opcode_cg.sample(instr.binary[6:2]);
end
end
case (instr.instr_name)
ADD : add_cg.sample(instr);
SUB : sub_cg.sample(instr);
ADDI : addi_cg.sample(instr);
LUI : lui_cg.sample(instr);
AUIPC : auipc_cg.sample(instr);
SLL : sll_cg.sample(instr);
SRL : srl_cg.sample(instr);
SRA : sra_cg.sample(instr);
ADD : `SAMPLE(add_cg, instr)
SUB : `SAMPLE(sub_cg, instr)
ADDI : `SAMPLE(addi_cg, instr)
LUI : `SAMPLE(lui_cg, instr)
AUIPC : `SAMPLE(auipc_cg, instr)
SLL : `SAMPLE(sll_cg, instr)
SRL : `SAMPLE(srl_cg, instr)
SRA : `SAMPLE(sra_cg, instr)
SLLI : begin
slli_cg.sample(instr);
`SAMPLE(slli_cg, instr)
if (RV64I inside {supported_isa}) begin
slli64_cg.sample(instr);
`SAMPLE(slli64_cg, instr)
end
end
SRLI : begin
srli_cg.sample(instr);
`SAMPLE(srli_cg, instr)
if (RV64I inside {supported_isa}) begin
srli64_cg.sample(instr);
`SAMPLE(srli64_cg, instr)
end
end
SRAI : begin
srai_cg.sample(instr);
`SAMPLE(srai_cg, instr)
if (RV64I inside {supported_isa}) begin
srai64_cg.sample(instr);
`SAMPLE(srai64_cg, instr)
end
end
AND : and_cg.sample(instr);
OR : or_cg.sample(instr);
XOR : xor_cg.sample(instr);
ANDI : andi_cg.sample(instr);
ORI : ori_cg.sample(instr);
XORI : xori_cg.sample(instr);
SLT : slt_cg.sample(instr);
SLTU : sltu_cg.sample(instr);
SLTI : slti_cg.sample(instr);
SLTIU : sltiu_cg.sample(instr);
JAL : jal_cg.sample(instr);
JALR : jalr_cg.sample(instr);
BEQ : beq_cg.sample(instr);
BNE : bne_cg.sample(instr);
BLT : blt_cg.sample(instr);
BGE : bge_cg.sample(instr);
BLTU : bltu_cg.sample(instr);
BGEU : bgeu_cg.sample(instr);
LW : lw_cg.sample(instr);
LH : lh_cg.sample(instr);
LB : lb_cg.sample(instr);
LBU : lbu_cg.sample(instr);
LHU : lhu_cg.sample(instr);
SW : sw_cg.sample(instr);
SH : sh_cg.sample(instr);
SB : sb_cg.sample(instr);
CSRRW : csrrw_cg.sample(instr);
CSRRS : csrrs_cg.sample(instr);
CSRRC : csrrc_cg.sample(instr);
CSRRWI : csrrwi_cg.sample(instr);
CSRRSI : csrrsi_cg.sample(instr);
CSRRCI : csrrci_cg.sample(instr);
MUL : mul_cg.sample(instr);
MULH : mulh_cg.sample(instr);
MULHSU : mulhsu_cg.sample(instr);
MULHU : mulhu_cg.sample(instr);
DIV : div_cg.sample(instr);
DIVU : divu_cg.sample(instr);
REM : rem_cg.sample(instr);
REMU : remu_cg.sample(instr);
MULW : mulw_cg.sample(instr);
DIVW : divw_cg.sample(instr);
DIVUW : divuw_cg.sample(instr);
REMW : remw_cg.sample(instr);
REMUW : remuw_cg.sample(instr);
LWU : lwu_cg.sample(instr);
LD : ld_cg.sample(instr);
SD : sd_cg.sample(instr);
SLLW : sllw_cg.sample(instr);
SLLIW : slliw_cg.sample(instr);
SRLW : srlw_cg.sample(instr);
SRLIW : srliw_cg.sample(instr);
SRAW : sraw_cg.sample(instr);
SRAIW : sraiw_cg.sample(instr);
ADDW : addw_cg.sample(instr);
ADDIW : addiw_cg.sample(instr);
SUBW : subw_cg.sample(instr);
C_LW : c_lw_cg.sample(instr);
C_SW : c_sw_cg.sample(instr);
C_LWSP : c_lwsp_cg.sample(instr);
C_SWSP : c_swsp_cg.sample(instr);
C_ADDI4SPN : c_addi4spn_cg.sample(instr);
C_ADDI : c_addi_cg.sample(instr);
C_ADDI16SP : c_addi16sp_cg.sample(instr);
C_LI : c_li_cg.sample(instr);
C_LUI : c_lui_cg.sample(instr);
C_SUB : c_sub_cg.sample(instr);
C_ADD : c_add_cg.sample(instr);
C_MV : c_mv_cg.sample(instr);
C_ANDI : c_andi_cg.sample(instr);
C_XOR : c_xor_cg.sample(instr);
C_OR : c_or_cg.sample(instr);
C_AND : c_and_cg.sample(instr);
C_BEQZ : c_beqz_cg.sample(instr);
C_BNEZ : c_bnez_cg.sample(instr);
C_SRLI : c_srli_cg.sample(instr);
C_SRAI : c_srai_cg.sample(instr);
C_SLLI : c_slli_cg.sample(instr);
C_J : c_j_cg.sample(instr);
C_JAL : c_jal_cg.sample(instr);
C_JR : c_jr_cg.sample(instr);
C_JALR : c_jalr_cg.sample(instr);
C_LD : c_ld_cg.sample(instr);
C_SD : c_sd_cg.sample(instr);
C_LDSP : c_ldsp_cg.sample(instr);
C_SDSP : c_sdsp_cg.sample(instr);
C_SUBW : c_subw_cg.sample(instr);
C_ADDW : c_addw_cg.sample(instr);
C_ADDIW : c_addiw_cg.sample(instr);
AND : `SAMPLE(and_cg, instr)
OR : `SAMPLE(or_cg, instr)
XOR : `SAMPLE(xor_cg, instr)
ANDI : `SAMPLE(andi_cg, instr)
ORI : `SAMPLE(ori_cg, instr)
XORI : `SAMPLE(xori_cg, instr)
SLT : `SAMPLE(slt_cg, instr)
SLTU : `SAMPLE(sltu_cg, instr)
SLTI : `SAMPLE(slti_cg, instr)
SLTIU : `SAMPLE(sltiu_cg, instr)
JAL : `SAMPLE(jal_cg, instr)
JALR : `SAMPLE(jalr_cg, instr)
BEQ : `SAMPLE(beq_cg, instr)
BNE : `SAMPLE(bne_cg, instr)
BLT : `SAMPLE(blt_cg, instr)
BGE : `SAMPLE(bge_cg, instr)
BLTU : `SAMPLE(bltu_cg, instr)
BGEU : `SAMPLE(bgeu_cg, instr)
LW : `SAMPLE(lw_cg, instr)
LH : `SAMPLE(lh_cg, instr)
LB : `SAMPLE(lb_cg, instr)
LBU : `SAMPLE(lbu_cg, instr)
LHU : `SAMPLE(lhu_cg, instr)
SW : `SAMPLE(sw_cg, instr)
SH : `SAMPLE(sh_cg, instr)
SB : `SAMPLE(sb_cg, instr)
CSRRW : `SAMPLE(csrrw_cg, instr)
CSRRS : `SAMPLE(csrrs_cg, instr)
CSRRC : `SAMPLE(csrrc_cg, instr)
CSRRWI : `SAMPLE(csrrwi_cg, instr)
CSRRSI : `SAMPLE(csrrsi_cg, instr)
CSRRCI : `SAMPLE(csrrci_cg, instr)
MUL : `SAMPLE(mul_cg, instr)
MULH : `SAMPLE(mulh_cg, instr)
MULHSU : `SAMPLE(mulhsu_cg, instr)
MULHU : `SAMPLE(mulhu_cg, instr)
DIV : `SAMPLE(div_cg, instr)
DIVU : `SAMPLE(divu_cg, instr)
REM : `SAMPLE(rem_cg, instr)
REMU : `SAMPLE(remu_cg, instr)
MULW : `SAMPLE(mulw_cg, instr)
DIVW : `SAMPLE(divw_cg, instr)
DIVUW : `SAMPLE(divuw_cg, instr)
REMW : `SAMPLE(remw_cg, instr)
REMUW : `SAMPLE(remuw_cg, instr)
LWU : `SAMPLE(lwu_cg, instr)
LD : `SAMPLE(ld_cg, instr)
SD : `SAMPLE(sd_cg, instr)
SLLW : `SAMPLE(sllw_cg, instr)
SLLIW : `SAMPLE(slliw_cg, instr)
SRLW : `SAMPLE(srlw_cg, instr)
SRLIW : `SAMPLE(srliw_cg, instr)
SRAW : `SAMPLE(sraw_cg, instr)
SRAIW : `SAMPLE(sraiw_cg, instr)
ADDW : `SAMPLE(addw_cg, instr)
ADDIW : `SAMPLE(addiw_cg, instr)
SUBW : `SAMPLE(subw_cg, instr)
C_LW : `SAMPLE(c_lw_cg, instr)
C_SW : `SAMPLE(c_sw_cg, instr)
C_LWSP : `SAMPLE(c_lwsp_cg, instr)
C_SWSP : `SAMPLE(c_swsp_cg, instr)
C_ADDI4SPN : `SAMPLE(c_addi4spn_cg, instr)
C_ADDI : `SAMPLE(c_addi_cg, instr)
C_ADDI16SP : `SAMPLE(c_addi16sp_cg, instr)
C_LI : `SAMPLE(c_li_cg, instr)
C_LUI : `SAMPLE(c_lui_cg, instr)
C_SUB : `SAMPLE(c_sub_cg, instr)
C_ADD : `SAMPLE(c_add_cg, instr)
C_MV : `SAMPLE(c_mv_cg, instr)
C_ANDI : `SAMPLE(c_andi_cg, instr)
C_XOR : `SAMPLE(c_xor_cg, instr)
C_OR : `SAMPLE(c_or_cg, instr)
C_AND : `SAMPLE(c_and_cg, instr)
C_BEQZ : `SAMPLE(c_beqz_cg, instr)
C_BNEZ : `SAMPLE(c_bnez_cg, instr)
C_SRLI : `SAMPLE(c_srli_cg, instr)
C_SRAI : `SAMPLE(c_srai_cg, instr)
C_SLLI : `SAMPLE(c_slli_cg, instr)
C_J : `SAMPLE(c_j_cg, instr)
C_JAL : `SAMPLE(c_jal_cg, instr)
C_JR : `SAMPLE(c_jr_cg, instr)
C_JALR : `SAMPLE(c_jalr_cg, instr)
C_LD : `SAMPLE(c_ld_cg, instr)
C_SD : `SAMPLE(c_sd_cg, instr)
C_LDSP : `SAMPLE(c_ldsp_cg, instr)
C_SDSP : `SAMPLE(c_sdsp_cg, instr)
C_SUBW : `SAMPLE(c_subw_cg, instr)
C_ADDW : `SAMPLE(c_addw_cg, instr)
C_ADDIW : `SAMPLE(c_addiw_cg, instr)
`VECTOR_INCLUDE("riscv_instr_cover_group_inc_cg_sample.sv")
default: begin
if (RV32C inside {supported_isa}) begin
illegal_compressed_instr_cg.sample(instr.binary);
if (!compliance_mode) begin
illegal_compressed_instr_cg.sample(instr.binary);
end
end
if (instr.group == RV32I) begin
rv32i_misc_cg.sample(instr);
if (!compliance_mode) begin
rv32i_misc_cg.sample(instr);
end
end
end
endcase
@ -1085,39 +1190,54 @@ class riscv_instr_cover_group;
branch_hit_history = (branch_hit_history << 1) | instr.branch_hit;
branch_instr_cnt += 1;
if (branch_instr_cnt >= $bits(branch_hit_history)) begin
branch_hit_history_cg.sample();
if (!compliance_mode) begin
branch_hit_history_cg.sample();
end
end
end
if (instr.category == CSR) begin
privileged_csr_cg.sample(instr.csr);
if (!compliance_mode) begin
privileged_csr_cg.sample(instr.csr);
end
case (instr.csr)
MCAUSE: begin
if (instr.rd_value[XLEN-1]) begin
interrupt_cause_t interrupt;
if ($cast(interrupt, instr.rd_value[3:0])) begin
mcause_interrupt_cg.sample(interrupt);
if (!compliance_mode && !iss_mode) begin
mcause_interrupt_cg.sample(interrupt);
end
end
end else begin
exception_cause_t exception;
if ($cast(exception, instr.rd_value[3:0])) begin
mcause_exception_cg.sample(exception);
if (!compliance_mode) begin
mcause_exception_cg.sample(exception);
end
end
end
end
MEPC: begin
if (RV32C inside {supported_isa}) begin
mepc_alignment_cg.sample(instr.rd_value);
if (!compliance_mode) begin
mepc_alignment_cg.sample(instr.rd_value);
end
end
end
MSTATUS: begin
mstatus_m_cg.sample(instr.rd_value);
if (!compliance_mode) begin
mstatus_m_cg.sample(instr.rd_value);
end
end
endcase
end
if (instr_cnt > 1) begin
// instr_trans_cg.sample();
if (!compliance_mode) begin
//instr_trans_cg.sample();
end
end
pre_instr.copy_base_instr(instr);
`VECTOR_INCLUDE("riscv_instr_cover_group_inc_sample.sv")
pre_instr.copy(instr);
pre_instr.mem_addr = instr.mem_addr;
endfunction
@ -1144,15 +1264,20 @@ class riscv_instr_cover_group;
riscv_instr_name_t instr_name;
instr_name = instr_name.first;
do begin
riscv_instr_base instr;
`INSTR instr;
if (!(instr_name inside {unsupported_instr}) && (instr_name != INVALID_INSTR)) begin
instr = riscv_instr_base::type_id::create("instr");
if (!instr.randomize() with {instr_name == local::instr_name;}) begin
`uvm_fatal("riscv_instr_cover_group",
$sformatf("Instruction %0s randomization failure", instr_name.name()))
end
`ifdef DEPRECATED
instr = riscv_instr_base::type_id::create("instr");
if (!instr.randomize() with {instr_name == local::instr_name;}) begin
`uvm_fatal("riscv_instr_cover_group",
$sformatf("Instruction %0s randomization failure", instr_name.name()))
end
`else
instr = riscv_instr::create_instr(instr_name);
`endif
if ((instr.group inside {supported_isa}) &&
(instr.group inside {RV32I, RV32M, RV64M, RV64I, RV32C, RV64C})) begin
(instr.group inside {RV32I, RV32M, RV64M, RV64I, RV32C, RV64C,
RV32V, RV64V, RV64B, RV32B})) begin
if (((instr_name inside {URET}) && !support_umode_trap) ||
((instr_name inside {SRET, SFENCE_VMA}) &&
!(SUPERVISOR_MODE inside {supported_privileged_mode})) ||
@ -1174,6 +1299,13 @@ class riscv_instr_cover_group;
instr_cnt = 0;
branch_instr_cnt = 0;
branch_hit_history = '0;
`VECTOR_INCLUDE("riscv_instr_cover_group_inc_cpu_reset.sv")
endfunction
function void fatal(string str);
if ($test$plusargs("stop_on_first_error")) begin
`uvm_fatal("riscv_instr_cover_group", $sformatf("FATAL Error: %0s", str))
end
endfunction
endclass

View file

@ -116,14 +116,6 @@ class riscv_instr_gen_config extends uvm_object;
// Number of instructions for each kernel program
int kernel_program_instr_cnt = 400;
//-----------------------------------------------------------------------------
// Instruction list based on the config, generate by build_instruction_template
//-----------------------------------------------------------------------------
riscv_instr_base instr_template[riscv_instr_name_t];
riscv_instr_name_t basic_instr[$];
riscv_instr_name_t instr_group[riscv_instr_group_t][$];
riscv_instr_name_t instr_category[riscv_instr_category_t][$];
// Queue of all the main implemented CSRs that the boot privilege mode cannot access
// e.g. these CSRs are in higher privilege modes - access should raise an exception
privileged_reg_t invalid_priv_mode_csrs[$];
@ -390,8 +382,54 @@ class riscv_instr_gen_config extends uvm_object;
}
`uvm_object_utils_begin(riscv_instr_gen_config)
`uvm_field_int(main_program_instr_cnt, UVM_DEFAULT)
`uvm_field_int(main_program_instr_cnt, UVM_DEFAULT)
`uvm_field_sarray_int(sub_program_instr_cnt, UVM_DEFAULT)
`uvm_field_int(debug_program_instr_cnt, UVM_DEFAULT)
`uvm_field_enum(data_pattern_t, data_page_pattern, UVM_DEFAULT)
`uvm_field_enum(privileged_mode_t, init_privileged_mode, UVM_DEFAULT)
`uvm_field_array_enum(riscv_reg_t, reserved_regs, UVM_DEFAULT)
`uvm_field_enum(riscv_reg_t, ra, UVM_DEFAULT)
`uvm_field_enum(riscv_reg_t, sp, UVM_DEFAULT)
`uvm_field_enum(riscv_reg_t, tp, UVM_DEFAULT)
`uvm_field_int(no_data_page, UVM_DEFAULT)
`uvm_field_int(no_branch_jump, UVM_DEFAULT)
`uvm_field_int(no_load_store, UVM_DEFAULT)
`uvm_field_int(no_csr_instr, UVM_DEFAULT)
`uvm_field_int(no_ebreak, UVM_DEFAULT)
`uvm_field_int(no_dret, UVM_DEFAULT)
`uvm_field_int(no_fence, UVM_DEFAULT)
`uvm_field_int(no_wfi, UVM_DEFAULT)
`uvm_field_int(enable_unaligned_load_store, UVM_DEFAULT)
`uvm_field_int(illegal_instr_ratio, UVM_DEFAULT)
`uvm_field_int(hint_instr_ratio, UVM_DEFAULT)
`uvm_field_string(boot_mode_opts, UVM_DEFAULT)
`uvm_field_int(enable_page_table_exception, UVM_DEFAULT)
`uvm_field_int(no_directed_instr, UVM_DEFAULT)
`uvm_field_int(enable_interrupt, UVM_DEFAULT)
`uvm_field_int(enable_timer_irq, UVM_DEFAULT)
`uvm_field_int(bare_program_mode, UVM_DEFAULT)
`uvm_field_int(enable_illegal_csr_instruction, UVM_DEFAULT)
`uvm_field_int(enable_access_invalid_csr_level, UVM_DEFAULT)
`uvm_field_int(enable_dummy_csr_write, UVM_DEFAULT)
`uvm_field_int(randomize_csr, UVM_DEFAULT)
`uvm_field_int(allow_sfence_exception, UVM_DEFAULT)
`uvm_field_int(no_delegation, UVM_DEFAULT)
`uvm_field_int(force_m_delegation, UVM_DEFAULT)
`uvm_field_int(force_s_delegation, UVM_DEFAULT)
`uvm_field_int(support_supervisor_mode, UVM_DEFAULT)
`uvm_field_int(disable_compressed_instr, UVM_DEFAULT)
`uvm_field_int(signature_addr, UVM_DEFAULT)
`uvm_field_int(require_signature_addr, UVM_DEFAULT)
`uvm_field_int(gen_debug_section, UVM_DEFAULT)
`uvm_field_int(enable_ebreak_in_debug_rom, UVM_DEFAULT)
`uvm_field_int(set_dcsr_ebreak, UVM_DEFAULT)
`uvm_field_int(num_debug_sub_program, UVM_DEFAULT)
`uvm_field_int(enable_debug_single_step, UVM_DEFAULT)
`uvm_field_int(single_step_iterations, UVM_DEFAULT)
`uvm_field_int(set_mstatus_tw, UVM_DEFAULT)
`uvm_field_int(max_branch_step, UVM_DEFAULT)
`uvm_field_int(max_directed_instr_stream_seq, UVM_DEFAULT)
`uvm_field_int(enable_floating_point, UVM_DEFAULT)
`uvm_object_utils_end
function new (string name = "");
@ -539,6 +577,10 @@ class riscv_instr_gen_config extends uvm_object;
min_stack_len_per_program = 2 * (XLEN/8);
// Check if the setting is legal
check_setting();
// WFI is not supported in umode
if (init_privileged_mode == USER_MODE) begin
no_wfi = 1'b1;
end
endfunction
function void check_setting();
@ -569,7 +611,8 @@ class riscv_instr_gen_config extends uvm_object;
// TODO(udi) - include performance/pmp/trigger CSRs?
virtual function void get_invalid_priv_lvl_csr();
string invalid_lvl[$];
string csr;
string csr_name;
privileged_reg_t csr;
// Debug CSRs are inaccessible from all but Debug Mode, and we cannot boot into Debug Mode
invalid_lvl.push_back("D");
case (init_privileged_mode)
@ -587,8 +630,9 @@ class riscv_instr_gen_config extends uvm_object;
end
endcase
foreach (implemented_csr[i]) begin
csr = implemented_csr[i].name();
if (csr[0] inside {invalid_lvl}) begin
privileged_reg_t csr = implemented_csr[i];
csr_name = csr.name();
if (csr_name[0] inside {invalid_lvl}) begin
invalid_priv_mode_csrs.push_back(implemented_csr[i]);
end
end
@ -618,73 +662,4 @@ class riscv_instr_gen_config extends uvm_object;
end
endfunction
// Build instruction template
virtual function void build_instruction_template(bit skip_instr_exclusion = 0);
riscv_instr_name_t instr_name;
riscv_instr_name_t excluded_instr[$];
excluded_instr = {INVALID_INSTR};
if (!skip_instr_exclusion) begin
get_excluded_instr(excluded_instr);
end
instr_name = instr_name.first;
do begin
riscv_instr_base instr;
if (!(instr_name inside {unsupported_instr, excluded_instr})) begin
instr = riscv_instr_base::type_id::create("instr");
`DV_CHECK_RANDOMIZE_WITH_FATAL(instr, instr_name == local::instr_name;)
if ((instr.group inside {supported_isa}) &&
!(disable_compressed_instr && instr.is_compressed) &&
!(!enable_floating_point && (instr.group inside {RV32F, RV64F, RV32D, RV64D}))) begin
`uvm_info(`gfn, $sformatf("Adding [%s] %s to the list",
instr.group.name(), instr.instr_name.name()), UVM_HIGH)
instr_group[instr.group].push_back(instr_name);
instr_category[instr.category].push_back(instr_name);
instr_template[instr_name] = instr;
end
end
instr_name = instr_name.next;
end
while (instr_name != instr_name.first);
endfunction
virtual function void build_instruction_list();
basic_instr = {instr_category[SHIFT], instr_category[ARITHMETIC],
instr_category[LOGICAL], instr_category[COMPARE]};
basic_instr = {basic_instr, EBREAK};
foreach(riscv_instr_pkg::supported_isa[i]) begin
if (riscv_instr_pkg::supported_isa[i] inside {RV32C, RV64C, RV128C, RV32DC, RV32FC}) begin
basic_instr = {basic_instr, C_EBREAK};
break;
end
end
if (no_dret == 0) begin
basic_instr = {basic_instr, DRET};
end
if (no_fence == 0) begin
basic_instr = {basic_instr, instr_category[SYNCH]};
end
// TODO: Support CSR instruction in other mode
if ((no_csr_instr == 0) && (init_privileged_mode == MACHINE_MODE)) begin
`uvm_info(`gfn, $sformatf("Adding CSR instr, mode: %0s", init_privileged_mode.name()), UVM_LOW)
basic_instr = {basic_instr, instr_category[CSR]};
end
if (no_wfi == 0) begin
basic_instr = {basic_instr, WFI};
end
endfunction
virtual function void get_excluded_instr(ref riscv_instr_name_t excluded[$]);
// Below instrutions will modify stack pointer, not allowed in normal instruction stream.
// It can be used in stack operation instruction stream.
excluded = {excluded, C_SWSP, C_SDSP, C_ADDI16SP};
if (!enable_sfence) begin
excluded = {excluded, SFENCE_VMA};
end
if (no_fence) begin
excluded = {excluded, FENCE, FENCE_I, SFENCE_VMA};
end
// TODO: Support C_ADDI4SPN
excluded = {excluded, C_ADDI4SPN};
endfunction
endclass

View file

@ -86,7 +86,11 @@ package riscv_instr_pkg;
RV32C,
RV64C,
RV128I,
RV128C
RV128C,
RV32V,
RV32B,
RV64V,
RV64B
} riscv_instr_group_t;
typedef enum {
@ -318,6 +322,7 @@ package riscv_instr_pkg;
SRET,
WFI,
SFENCE_VMA,
`VECTOR_INCLUDE("riscv_instr_pkg_inc_riscv_instr_name_t.sv")
// You can add other instructions here
INVALID_INSTR
} riscv_instr_name_t;
@ -331,12 +336,14 @@ package riscv_instr_pkg;
S2, S3, S4, S5, S6, S7, S8, S9, S10, S11, T3, T4, T5, T6
} riscv_reg_t;
`VECTOR_INCLUDE("riscv_instr_pkg_inc_enum.sv")
typedef enum bit [4:0] {
F0, F1, F2, F3, F4, F5, F6, F7, F8, F9, F10, F11, F12, F13, F14, F15,
F16, F17, F18, F19, F20, F21, F22, F23, F24, F25, F26, F27, F28, F29, F30, F31
} riscv_fpr_t;
typedef enum bit [3:0] {
typedef enum bit [5:0] {
J_FORMAT = 0,
U_FORMAT,
I_FORMAT,
@ -352,10 +359,11 @@ package riscv_instr_pkg;
CL_FORMAT,
CS_FORMAT,
CSS_FORMAT,
CIW_FORMAT
`VECTOR_INCLUDE("riscv_instr_pkg_inc_riscv_instr_format_t.sv")
CIW_FORMAT // (last one)
} riscv_instr_format_t;
typedef enum bit [3:0] {
typedef enum bit [5:0] {
LOAD = 0,
STORE,
SHIFT,
@ -371,7 +379,8 @@ package riscv_instr_pkg;
CHANGELEVEL,
TRAP,
INTERRUPT,
AMO
`VECTOR_INCLUDE("riscv_instr_pkg_inc_riscv_instr_category_t.sv")
AMO // (last one)
} riscv_instr_category_t;
typedef bit [11:0] riscv_csr_t;
@ -602,7 +611,8 @@ package riscv_instr_pkg;
DCSR = 'h7B0, // Debug control and status register
DPC = 'h7B1, // Debug PC
DSCRATCH0 = 'h7B2, // Debug scratch register
DSCRATCH1 = 'h7B3 // Debug scratch register
`VECTOR_INCLUDE("riscv_instr_pkg_inc_privileged_reg_t.sv")
DSCRATCH1 = 'h7B3 // Debug scratch register (last one)
} privileged_reg_t;
typedef enum bit [5:0] {
@ -715,6 +725,21 @@ package riscv_instr_pkg;
`include "riscv_core_setting.sv"
// TODO hardware target definition so should move to riscv_core_setting.sv
`ifdef ENABLE_VECTORS
parameter bit has_vector_engine = 'b1;
parameter int VLEN = `VLEN;
parameter int ELEN = `ELEN;
parameter int SLEN = `SLEN;
`else
parameter bit has_vector_engine = 'b0;
parameter int VLEN = 512;
parameter int ELEN = 64;
parameter int SLEN = 64;
`endif
`VECTOR_INCLUDE("riscv_instr_pkg_inc_variables.sv")
typedef bit [15:0] program_id_t;
// xSTATUS bit mask
@ -844,8 +869,42 @@ package riscv_instr_pkg;
riscv_reg_t compressed_gpr[] = {S0, S1, A0, A1, A2, A3, A4, A5};
`include "riscv_instr_base.sv"
`include "riscv_instr_gen_config.sv"
riscv_instr_category_t all_categories[] = {
LOAD, STORE, SHIFT, ARITHMETIC, LOGICAL, COMPARE, BRANCH, JUMP,
SYNCH, SYSTEM, COUNTER, CSR, CHANGELEVEL, TRAP, INTERRUPT, AMO
};
`ifdef DEPRECATED
`define INSTR riscv_instr_base
`include "deprecated/riscv_instr_base.sv"
`include "deprecated/riscv_instr_gen_config.sv"
`else
`define INSTR riscv_instr
typedef class riscv_instr;
`include "riscv_instr_gen_config.sv"
`include "isa/riscv_instr.sv"
`include "isa/riscv_amo_instr.sv"
`include "isa/riscv_floating_point_instr.sv"
`include "isa/riscv_vector_instr.sv"
`include "isa/riscv_compressed_instr.sv"
`include "isa/rv32a_instr.sv"
`include "isa/rv32c_instr.sv"
`include "isa/rv32dc_instr.sv"
`include "isa/rv32d_instr.sv"
`include "isa/rv32fc_instr.sv"
`include "isa/rv32f_instr.sv"
`include "isa/rv32i_instr.sv"
`include "isa/rv32m_instr.sv"
`include "isa/rv64a_instr.sv"
`include "isa/rv64c_instr.sv"
`include "isa/rv64d_instr.sv"
`include "isa/rv64f_instr.sv"
`include "isa/rv64i_instr.sv"
`include "isa/rv64m_instr.sv"
`include "isa/rv128c_instr.sv"
`endif
`include "riscv_pseudo_instr.sv"
`include "riscv_illegal_instr.sv"
`include "riscv_reg.sv"
`include "riscv_privil_reg.sv"
@ -856,14 +915,25 @@ package riscv_instr_pkg;
`include "riscv_privileged_common_seq.sv"
`include "riscv_callstack_gen.sv"
`include "riscv_data_page_gen.sv"
`include "riscv_rand_instr.sv"
`ifdef DEPRECATED
`include "deprecated/riscv_rand_instr.sv"
`include "deprecated/riscv_instr_stream.sv"
`include "deprecated/riscv_loop_instr.sv"
`include "deprecated/riscv_directed_instr_lib.sv"
`include "deprecated/riscv_load_store_instr_lib.sv"
`include "deprecated/riscv_amo_instr_lib.sv"
`else
`include "riscv_instr_stream.sv"
`include "riscv_loop_instr.sv"
`include "riscv_directed_instr_lib.sv"
`include "riscv_load_store_instr_lib.sv"
`include "riscv_amo_instr_lib.sv"
`endif
`include "riscv_instr_sequence.sv"
`include "riscv_asm_program_gen.sv"
`include "riscv_debug_rom_gen.sv"
`include "riscv_instr_cov_item.sv"
`include "riscv_instr_cover_group.sv"
`include "user_extension.svh"

View file

@ -20,7 +20,7 @@
// instruction, mix two instruction streams etc.
class riscv_instr_stream extends uvm_object;
riscv_instr_base instr_list[$];
`INSTR instr_list[$];
int unsigned instr_cnt;
string label = "";
// User can specify a small group of available registers to generate various hazard condition
@ -40,16 +40,16 @@ class riscv_instr_stream extends uvm_object;
endfunction
virtual function void create_instr_instance();
riscv_instr_base instr;
`INSTR instr;
for(int i = 0; i < instr_cnt; i++) begin
instr = riscv_instr_base::type_id::create($sformatf("instr_%0d", i));
instr = `INSTR::type_id::create($sformatf("instr_%0d", i));
instr_list.push_back(instr);
end
endfunction
// Insert an instruction to the existing instruction stream at the given index
// When index is -1, the instruction is injected at a random location
function void insert_instr(riscv_instr_base instr, int idx = -1);
function void insert_instr(`INSTR instr, int idx = -1);
int current_instr_cnt = instr_list.size();
if(idx == -1) begin
idx = $urandom_range(0, current_instr_cnt-1);
@ -70,7 +70,7 @@ class riscv_instr_stream extends uvm_object;
// Insert an instruction to the existing instruction stream at the given index
// When index is -1, the instruction is injected at a random location
// When replace is 1, the original instruction at the inserted position will be replaced
function void insert_instr_stream(riscv_instr_base new_instr[], int idx = -1, bit replace = 1'b0);
function void insert_instr_stream(`INSTR new_instr[], int idx = -1, bit replace = 1'b0);
int current_instr_cnt = instr_list.size();
int new_instr_cnt = new_instr.size();
if(current_instr_cnt == 0) begin
@ -119,7 +119,7 @@ class riscv_instr_stream extends uvm_object;
// Mix the input instruction stream with the original instruction, the instruction order is
// preserved. When 'contained' is set, the original instruction stream will be inside the
// new instruction stream with the first and last instruction from the input instruction stream.
function void mix_instr_stream(riscv_instr_base new_instr[], bit contained = 1'b0);
function void mix_instr_stream(`INSTR new_instr[], bit contained = 1'b0);
int current_instr_cnt = instr_list.size();
int insert_instr_position[];
int new_instr_cnt = new_instr.size();
@ -167,25 +167,25 @@ class riscv_rand_instr_stream extends riscv_instr_stream;
`uvm_object_new
virtual function void create_instr_instance();
riscv_instr_base instr;
for(int i = 0; i < instr_cnt; i++) begin
instr = riscv_instr_base::type_id::create($sformatf("instr_%0d", i));
instr_list.push_back(instr);
`INSTR instr;
for (int i = 0; i < instr_cnt; i++) begin
instr_list.push_back(null);
end
endfunction
virtual function void setup_allowed_instr(bit no_branch = 1'b0, bit no_load_store = 1'b1);
allowed_instr = cfg.basic_instr;
allowed_instr = riscv_instr::basic_instr;
if (no_branch == 0) begin
allowed_instr = {allowed_instr, cfg.instr_category[BRANCH]};
allowed_instr = {allowed_instr, riscv_instr::instr_category[BRANCH]};
end
if (no_load_store == 0) begin
allowed_instr = {allowed_instr, cfg.instr_category[LOAD], cfg.instr_category[STORE]};
allowed_instr = {allowed_instr, riscv_instr::instr_category[LOAD],
riscv_instr::instr_category[STORE]};
end
setup_instruction_dist(no_branch, no_load_store);
endfunction
function setup_instruction_dist(bit no_branch = 1'b0, bit no_load_store = 1'b1);
function void setup_instruction_dist(bit no_branch = 1'b0, bit no_load_store = 1'b1);
if (cfg.dist_control_mode) begin
category_dist = cfg.category_dist;
if (no_branch) begin
@ -213,94 +213,51 @@ class riscv_rand_instr_stream extends riscv_instr_stream;
end
endfunction
function void randomize_instr(riscv_instr_base instr,
bit is_in_debug = 1'b0,
bit skip_rs1 = 1'b0,
bit skip_rs2 = 1'b0,
bit skip_rd = 1'b0,
bit skip_imm = 1'b0,
bit skip_csr = 1'b0,
bit disable_dist = 1'b0);
riscv_instr_name_t instr_name;
if ((cfg.dist_control_mode == 1) && !disable_dist) begin
riscv_instr_category_t category;
int unsigned idx;
`DV_CHECK_STD_RANDOMIZE_WITH_FATAL(category,
category dist {LOAD := category_dist[LOAD],
STORE := category_dist[STORE],
SHIFT := category_dist[SHIFT],
ARITHMETIC := category_dist[ARITHMETIC],
LOGICAL := category_dist[LOGICAL],
COMPARE := category_dist[COMPARE],
BRANCH := category_dist[BRANCH],
SYNCH := category_dist[SYNCH],
CSR := category_dist[CSR]};)
idx = $urandom_range(0, cfg.instr_category[category].size() - 1);
instr_name = cfg.instr_category[category][idx];
// if set_dcsr_ebreak is set, we do not want to generate any ebreak
// instructions inside the debug_rom
end else if ((cfg.no_ebreak && !is_in_debug) ||
(!cfg.enable_ebreak_in_debug_rom && is_in_debug)) begin
`DV_CHECK_STD_RANDOMIZE_WITH_FATAL(instr_name,
instr_name inside {allowed_instr};
!(instr_name inside {EBREAK, C_EBREAK});)
end else begin
`DV_CHECK_STD_RANDOMIZE_WITH_FATAL(instr_name,
instr_name inside {allowed_instr};)
end
instr.copy_base_instr(cfg.instr_template[instr_name]);
`uvm_info(`gfn, $sformatf("%s: rs1:%0d, rs2:%0d, rd:%0d, imm:%0d",
instr.instr_name.name(),
instr.has_rs1,
instr.has_rs2,
instr.has_rd,
instr.has_imm), UVM_FULL)
if (instr.has_imm && !skip_imm) begin
instr.gen_rand_imm();
end
if (instr.has_rs1 && !skip_rs1) begin
if (instr.is_compressed) begin
// Compressed instruction could use the same register for rs1 and rd
instr.rs1 = instr.gen_rand_gpr(
.included_reg(avail_regs),
.excluded_reg({reserved_rd, cfg.reserved_regs}));
end else begin
instr.rs1 = instr.gen_rand_gpr(.included_reg(avail_regs));
end
end
if (instr.has_rs2 && !skip_rs2) begin
instr.rs2 = instr.gen_rand_gpr(.included_reg(avail_regs));
end
if (instr.has_rd && !skip_rd) begin
if (instr_name == C_LUI) begin
instr.rd = instr.gen_rand_gpr(
.included_reg(avail_regs),
.excluded_reg({reserved_rd, cfg.reserved_regs, SP}));
end else begin
instr.rd = instr.gen_rand_gpr(
.included_reg(avail_regs),
.excluded_reg({reserved_rd, cfg.reserved_regs}));
end
end
if ((instr.category == CSR) && !skip_csr) begin
instr.gen_rand_csr(.privileged_mode(cfg.init_privileged_mode),
.enable_floating_point(cfg.enable_floating_point),
.illegal_csr_instr(cfg.enable_illegal_csr_instruction),
.legal_invalid_csr_instr(cfg.enable_access_invalid_csr_level),
.invalid_csrs(cfg.invalid_priv_mode_csrs));
end
if (instr.has_fs1) begin
instr.fs1 = instr.gen_rand_fpr();
end
if (instr.has_fs2) begin
instr.fs2 = instr.gen_rand_fpr();
end
if (instr.has_fs3) begin
instr.fs3 = instr.gen_rand_fpr();
end
if (instr.has_fd) begin
instr.fd = instr.gen_rand_fpr();
function void randomize_instr(output riscv_instr instr,
input bit is_in_debug = 1'b0,
input bit disable_dist = 1'b0);
riscv_instr_name_t exclude_instr[];
if ((SP inside {reserved_rd, cfg.reserved_regs}) ||
((avail_regs.size() > 0) && !(SP inside {avail_regs}))) begin
exclude_instr = {C_ADDI4SPN, C_ADDI16SP, C_LWSP, C_LDSP};
end
instr = riscv_instr::get_rand_instr(.include_instr(allowed_instr),
.exclude_instr(exclude_instr));
randomize_gpr(instr);
endfunction
function void randomize_gpr(ref riscv_instr instr);
`DV_CHECK_RANDOMIZE_WITH_FATAL(instr,
if (avail_regs.size() > 0) {
if (has_rs1) {
rs1 inside {avail_regs};
}
if (has_rs2) {
rs2 inside {avail_regs};
}
if (has_rd) {
rd inside {avail_regs};
}
}
if (reserved_rd.size() > 0) {
if (has_rd) {
!(rd inside {reserved_rd});
}
if (format == CB_FORMAT) {
!(rs1 inside {reserved_rd});
}
}
if (cfg.reserved_regs.size() > 0) {
if (has_rd) {
!(rd inside {cfg.reserved_regs});
}
if (format == CB_FORMAT) {
!(rs1 inside {cfg.reserved_regs});
}
}
// TODO: Add constraint for CSR, floating point register
)
endfunction
endclass

View file

@ -30,14 +30,23 @@ class riscv_load_store_base_instr_stream extends riscv_mem_access_stream;
rand int base;
int offset[];
int addr[];
riscv_instr_base load_store_instr[$];
riscv_instr load_store_instr[$];
rand int unsigned data_page_id;
rand riscv_reg_t rs1_reg;
rand locality_e locality;
rand int max_load_store_offset;
rand bit use_sp_as_rs1;
`uvm_object_utils(riscv_load_store_base_instr_stream)
constraint sp_c {
solve use_sp_as_rs1 before rs1_reg;
use_sp_as_rs1 dist {1 := 1, 0 := 2};
if (use_sp_as_rs1) {
rs1_reg == SP;
}
}
constraint rs1_c {
!(rs1_reg inside {cfg.reserved_regs, reserved_rd, ZERO});
}
@ -64,7 +73,6 @@ class riscv_load_store_base_instr_stream extends riscv_mem_access_stream;
addr = new[num_load_store];
for (int i=0; i<num_load_store; i++) begin
if (!std::randomize(offset_, addr_) with {
// Locality
if (locality == NARROW) {
soft offset_ inside {[-16:16]};
} else if (locality == HIGH) {
@ -84,6 +92,14 @@ class riscv_load_store_base_instr_stream extends riscv_mem_access_stream;
end
endfunction
function void pre_randomize();
super.pre_randomize();
if (SP inside {cfg.reserved_regs, reserved_rd}) begin
use_sp_as_rs1 = 0;
use_sp_as_rs1.rand_mode(0);
end
endfunction
function void post_randomize();
randomize_offset();
// rs1 cannot be modified by other instructions
@ -99,7 +115,7 @@ class riscv_load_store_base_instr_stream extends riscv_mem_access_stream;
// Generate each load/store instruction
virtual function void gen_load_store_instr();
bit enable_compressed_load_store;
riscv_instr_base instr;
riscv_instr instr;
if(avail_regs.size() > 0) begin
`DV_CHECK_STD_RANDOMIZE_WITH_FATAL(avail_regs,
unique{avail_regs};
@ -109,11 +125,10 @@ class riscv_load_store_base_instr_stream extends riscv_mem_access_stream;
},
"Cannot randomize avail_regs")
end
if ((rs1_reg inside {[S0 : A5]}) && !cfg.disable_compressed_instr) begin
if ((rs1_reg inside {[S0 : A5], SP}) && !cfg.disable_compressed_instr) begin
enable_compressed_load_store = 1;
end
foreach(addr[i]) begin
instr = riscv_instr_base::type_id::create("instr");
// Assign the allowed load/store instructions based on address alignment
// This is done separately rather than a constraint to improve the randomization performance
allowed_instr = {LB, LBU, SB};
@ -129,9 +144,14 @@ class riscv_load_store_base_instr_stream extends riscv_mem_access_stream;
if((offset[i] inside {[0:127]}) && (offset[i] % 4 == 0) &&
(RV32C inside {riscv_instr_pkg::supported_isa}) &&
enable_compressed_load_store) begin
allowed_instr = {C_LW, C_SW, allowed_instr};
if (cfg.enable_floating_point && (RV32FC inside {supported_isa})) begin
allowed_instr = {C_FLW, C_FSW, allowed_instr};
if (rs1_reg == SP) begin
`uvm_info(`gfn, "Add LWSP/SWSP to allowed instr", UVM_LOW)
allowed_instr = {C_LWSP, C_SWSP};
end else begin
allowed_instr = {C_LW, C_SW, allowed_instr};
if (cfg.enable_floating_point && (RV32FC inside {supported_isa})) begin
allowed_instr = {C_FLW, C_FSW, allowed_instr};
end
end
end
end
@ -143,31 +163,47 @@ class riscv_load_store_base_instr_stream extends riscv_mem_access_stream;
if((offset[i] inside {[0:255]}) && (offset[i] % 8 == 0) &&
(RV64C inside {riscv_instr_pkg::supported_isa} &&
enable_compressed_load_store)) begin
allowed_instr = {C_LD, C_SD, allowed_instr};
if (cfg.enable_floating_point && (RV32DC inside {supported_isa})) begin
allowed_instr = {C_FLD, C_FSD, allowed_instr};
if (rs1_reg == SP) begin
allowed_instr = {C_LDSP, C_SDSP};
end else begin
allowed_instr = {C_LD, C_SD, allowed_instr};
if (cfg.enable_floating_point && (RV32DC inside {supported_isa})) begin
allowed_instr = {C_FLD, C_FSD, allowed_instr};
end
end
end
end
end else begin
end else begin // unaligned load/store
allowed_instr = {LW, SW, LH, LHU, SH, allowed_instr};
// Compressed load/store still needs to be aligned
if ((offset[i] inside {[0:127]}) && (offset[i] % 4 == 0) &&
(RV32C inside {riscv_instr_pkg::supported_isa}) &&
enable_compressed_load_store) begin
allowed_instr = {C_LW, C_SW, allowed_instr};
if (rs1_reg == SP) begin
allowed_instr = {C_LWSP, C_SWSP};
end else begin
allowed_instr = {C_LW, C_SW, allowed_instr};
end
end
if (XLEN >= 64) begin
allowed_instr = {LWU, LD, SD, allowed_instr};
if ((offset[i] inside {[0:255]}) && (offset[i] % 8 == 0) &&
(RV64C inside {riscv_instr_pkg::supported_isa}) &&
enable_compressed_load_store) begin
allowed_instr = {C_LD, C_SD, allowed_instr};
if (rs1_reg == SP) begin
allowed_instr = {C_LWSP, C_SWSP};
end else begin
allowed_instr = {C_LD, C_SD, allowed_instr};
end
end
end
end
randomize_instr(instr, .skip_rs1(1'b1), .skip_imm(1'b1), .disable_dist(1'b1));
instr = riscv_instr::get_load_store_instr(allowed_instr);
instr.has_rs1 = 0;
instr.has_imm = 0;
randomize_gpr(instr);
instr.rs1 = rs1_reg;
instr.set_imm(offset[i]);
instr.imm_str = $sformatf("%0d", $signed(offset[i]));
instr.process_load_store = 0;
instr_list.push_back(instr);
load_store_instr.push_back(instr);
@ -318,6 +354,7 @@ class riscv_multi_page_load_store_instr_stream extends riscv_mem_access_stream;
load_store_instr_stream[i].min_instr_cnt = 5;
load_store_instr_stream[i].max_instr_cnt = 10;
load_store_instr_stream[i].cfg = cfg;
load_store_instr_stream[i].sp_c.constraint_mode(0);
// Make sure each load/store sequence doesn't override the rs1 of other sequences.
foreach(rs1_reg[j]) begin
if(i != j) begin
@ -396,10 +433,10 @@ class riscv_load_store_rand_addr_instr_stream extends riscv_load_store_base_inst
endfunction `uvm_object_new
virtual function void add_rs1_init_la_instr(riscv_reg_t gpr, int id, int base = 0);
riscv_instr_base instr[$];
riscv_instr instr[$];
riscv_pseudo_instr li_instr;
riscv_instr_base store_instr;
riscv_instr_base add_instr;
riscv_instr store_instr;
riscv_instr add_instr;
int min_offset[$];
int max_offset[$];
min_offset = offset.min();
@ -413,9 +450,8 @@ class riscv_load_store_rand_addr_instr_stream extends riscv_load_store_base_inst
)
li_instr.imm_str = $sformatf("0x%0x", addr_offset);
// Add offset to the base address
add_instr = riscv_instr_base::type_id::create("add_instr");
add_instr = riscv_instr::get_rand_instr(.include_instr({ADD}));
`DV_CHECK_RANDOMIZE_WITH_FATAL(add_instr,
instr_name == ADD;
rs1 == gpr;
rs2 == li_instr.rd;
rd == gpr;
@ -423,7 +459,7 @@ class riscv_load_store_rand_addr_instr_stream extends riscv_load_store_base_inst
instr.push_back(li_instr);
instr.push_back(add_instr);
// Create SW instruction template
store_instr = riscv_instr_base::type_id::create("store_instr");
store_instr = riscv_instr::get_rand_instr(.include_instr({SB}));
`DV_CHECK_RANDOMIZE_WITH_FATAL(store_instr,
instr_name == SB;
rs1 == gpr;
@ -431,9 +467,9 @@ class riscv_load_store_rand_addr_instr_stream extends riscv_load_store_base_inst
// Initialize the location which used by load instruction later
foreach (load_store_instr[i]) begin
if (load_store_instr[i].category == LOAD) begin
riscv_instr_base store;
store = riscv_instr_base::type_id::create("store");
store.copy_base_instr(store_instr);
riscv_instr store;
store = riscv_instr::type_id::create("store");
store.copy(store_instr);
store.rs2 = riscv_reg_t'(i % 32);
store.imm_str = load_store_instr[i].imm_str;
case (load_store_instr[i].instr_name) inside

View file

@ -27,12 +27,12 @@ class riscv_loop_instr extends riscv_rand_instr_stream;
rand bit [2:0] num_of_nested_loop;
rand int num_of_instr_in_loop;
rand riscv_instr_name_t branch_type[];
riscv_instr_base loop_init_instr[];
riscv_instr_base loop_update_instr[];
riscv_instr_base loop_branch_instr[];
riscv_rand_instr loop_branch_target_instr[];
riscv_instr loop_init_instr[];
riscv_instr loop_update_instr[];
riscv_instr loop_branch_instr[];
riscv_instr loop_branch_target_instr[];
// Aggregated loop instruction stream
riscv_instr_base loop_instr[];
riscv_instr loop_instr[];
constraint legal_loop_regs_c {
solve num_of_nested_loop before loop_cnt_reg;
@ -124,9 +124,8 @@ class riscv_loop_instr extends riscv_rand_instr_stream;
loop_branch_target_instr = new[num_of_nested_loop];
for(int i = 0; i < num_of_nested_loop; i++) begin
// Instruction to init the loop counter
loop_init_instr[2*i] = riscv_instr_base::type_id::create("loop_init_instr");
loop_init_instr[2*i] = riscv_instr::get_rand_instr(.include_instr({ADDI}));
`DV_CHECK_RANDOMIZE_WITH_FATAL(loop_init_instr[2*i],
instr_name == ADDI;
rd == loop_cnt_reg[i];
rs1 == ZERO;
imm == loop_init_val[i];,
@ -134,9 +133,8 @@ class riscv_loop_instr extends riscv_rand_instr_stream;
loop_init_instr[2*i].comment = $sformatf("init loop %0d counter", i);
// Instruction to init loop limit
loop_init_instr[2*i+1] = riscv_instr_base::type_id::create("loop_init_instr");
loop_init_instr[2*i+1] = riscv_instr::get_rand_instr(.include_instr({ADDI}));
`DV_CHECK_RANDOMIZE_WITH_FATAL(loop_init_instr[2*i+1],
instr_name == ADDI;
rd == loop_limit_reg[i];
rs1 == ZERO;
imm == loop_limit_val[i];,
@ -144,28 +142,30 @@ class riscv_loop_instr extends riscv_rand_instr_stream;
loop_init_instr[2*i+1].comment = $sformatf("init loop %0d limit", i);
// Branch target instruction, can be anything
loop_branch_target_instr[i] = riscv_rand_instr::type_id::create("loop_branch_target_instr");
loop_branch_target_instr[i].cfg = cfg;
loop_branch_target_instr[i].reserved_rd = reserved_rd;
loop_branch_target_instr[i] = riscv_instr::get_rand_instr(
.include_category({ARITHMETIC, LOGICAL, COMPARE}),
.exclude_instr({C_ADDI16SP}));
`DV_CHECK_RANDOMIZE_WITH_FATAL(loop_branch_target_instr[i],
!(category inside {LOAD, STORE, BRANCH, JUMP});,
"Cannot randomize branch target instruction")
if (format == CB_FORMAT) {
!(rs1 inside {reserved_rd, cfg.reserved_regs});
}
if (has_rd) {
!(rd inside {reserved_rd, cfg.reserved_regs});
}, "Cannot randomize branch target instruction")
loop_branch_target_instr[i].label = $sformatf("%0s_%0d_t", label, i);
// Instruction to update loop counter
loop_update_instr[i] = riscv_instr_base::type_id::create("loop_update_instr");
loop_update_instr[i] = riscv_instr::get_rand_instr(.include_instr({ADDI}));
`DV_CHECK_RANDOMIZE_WITH_FATAL(loop_update_instr[i],
instr_name == ADDI;
rd == loop_cnt_reg[i];
rs1== loop_cnt_reg[i];
rs1 == loop_cnt_reg[i];
imm == loop_step_val[i];,
"Cannot randomize loop update instruction")
loop_update_instr[i].comment = $sformatf("update loop %0d counter", i);
// Backward branch instruction
loop_branch_instr[i] = riscv_instr_base::type_id::create("loop_branch_instr");
loop_branch_instr[i] = riscv_instr::get_rand_instr(.include_instr({branch_type[i]}));
`DV_CHECK_RANDOMIZE_WITH_FATAL(loop_branch_instr[i],
instr_name == branch_type[i];
rs1 == loop_cnt_reg[i];
if (!(branch_type[i] inside {C_BEQZ, C_BNEZ})) {
rs2 == loop_limit_reg[i];

View file

@ -0,0 +1,32 @@
// Psuedo instructions are used to simplify assembly program writing
class riscv_pseudo_instr extends `INSTR;
rand riscv_pseudo_instr_name_t pseudo_instr_name;
`add_pseudo_instr(LI, I_FORMAT, LOAD, RV32I)
`add_pseudo_instr(LA, I_FORMAT, LOAD, RV32I)
`uvm_object_utils(riscv_pseudo_instr)
function new(string name = "");
super.new(name);
process_load_store = 0;
this.format = I_FORMAT;
endfunction
// Convert the instruction to assembly code
virtual function string convert2asm(string prefix = "");
string asm_str;
asm_str = format_string(get_instr_name(), MAX_INSTR_STR_LEN);
// instr rd,imm
asm_str = $sformatf("%0s%0s, %0s", asm_str, rd.name(), get_imm());
if(comment != "")
asm_str = {asm_str, " #",comment};
return asm_str.tolower();
endfunction
virtual function string get_instr_name();
return pseudo_instr_name.name();
endfunction
endclass

View file

@ -0,0 +1,18 @@
# riscOVPsim configuration file converted from YAML
--variant RV64GC
--override riscvOVPsim/cpu/misa_MXL=2
--override riscvOVPsim/cpu/misa_MXL_mask=0x0 # 0
--override riscvOVPsim/cpu/misa_Extensions_mask=0x0 # 0
--override riscvOVPsim/cpu/unaligned=T
--override riscvOVPsim/cpu/mtvec_mask=0x0 # 0
--override riscvOVPsim/cpu/user_version=2.3
--override riscvOVPsim/cpu/priv_version=1.11
--override riscvOVPsim/cpu/mvendorid=0
--override riscvOVPsim/cpu/marchid=0
--override riscvOVPsim/cpu/mimpid=0
--override riscvOVPsim/cpu/mhartid=0
--override riscvOVPsim/cpu/cycle_undefined=F
--override riscvOVPsim/cpu/instret_undefined=F
--override riscvOVPsim/cpu/time_undefined=T
--override riscvOVPsim/cpu/reset_address=0x80000000
--override riscvOVPsim/cpu/simulateexceptions=T

View file

@ -0,0 +1,149 @@
/*
* Copyright 2019 Google LLC
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
`define EXPERIMENTAL
//-----------------------------------------------------------------------------
// Processor feature configuration
//-----------------------------------------------------------------------------
// XLEN
parameter int XLEN = 64;
// Parameter for SATP mode, set to BARE if address translation is not supported
parameter satp_mode_t SATP_MODE = SV39;
// Supported Privileged mode
privileged_mode_t supported_privileged_mode[] = {USER_MODE, SUPERVISOR_MODE, MACHINE_MODE};
// Unsupported instructions
riscv_instr_name_t unsupported_instr[];
// ISA supported by the processor
riscv_instr_group_t supported_isa[$] = {RV32I, RV32M, RV64I, RV64M, RV32C, RV64C, RV32A, RV64A,
RV32F, RV64F, RV32D, RV64D};
// Interrupt mode support
mtvec_mode_t supported_interrupt_mode[$] = {DIRECT, VECTORED};
// The number of interrupt vectors to be generated, only used if VECTORED interrupt mode is
// supported
int max_interrupt_vector_num = 16;
// Debug mode support
bit support_debug_mode = 0;
// Support delegate trap to user mode
bit support_umode_trap = 0;
// Support sfence.vma instruction
bit support_sfence = 1;
// Support unaligned load/store
bit support_unaligned_load_store = 1'b1;
// ----------------------------------------------------------------------------
// Previleged CSR implementation
// ----------------------------------------------------------------------------
// Implemented previlieged CSR list
`ifdef DSIM
privileged_reg_t implemented_csr[] = {
`else
parameter privileged_reg_t implemented_csr[] = {
`endif
// User mode CSR
USTATUS, // User status
UIE, // User interrupt-enable register
UTVEC, // User trap-handler base address
USCRATCH, // Scratch register for user trap handlers
UEPC, // User exception program counter
UCAUSE, // User trap cause
UTVAL, // User bad address or instruction
UIP, // User interrupt pending
// Supervisor mode CSR
SSTATUS, // Supervisor status
SEDELEG, // Supervisor exception delegation register
SIDELEG, // Supervisor interrupt delegation register
SIE, // Supervisor interrupt-enable register
STVEC, // Supervisor trap-handler base address
SCOUNTEREN, // Supervisor counter enable
SSCRATCH, // Scratch register for supervisor trap handlers
SEPC, // Supervisor exception program counter
SCAUSE, // Supervisor trap cause
STVAL, // Supervisor bad address or instruction
SIP, // Supervisor interrupt pending
SATP, // Supervisor address translation and protection
// Machine mode mode CSR
MVENDORID, // Vendor ID
MARCHID, // Architecture ID
MIMPID, // Implementation ID
MHARTID, // Hardware thread ID
MSTATUS, // Machine status
MISA, // ISA and extensions
MEDELEG, // Machine exception delegation register
MIDELEG, // Machine interrupt delegation register
MIE, // Machine interrupt-enable register
MTVEC, // Machine trap-handler base address
MCOUNTEREN, // Machine counter enable
MSCRATCH, // Scratch register for machine trap handlers
MEPC, // Machine exception program counter
MCAUSE, // Machine trap cause
MTVAL, // Machine bad address or instruction
MIP, // Machine interrupt pending
// Floating point CSR
FCSR // Floating point control and status
};
// ----------------------------------------------------------------------------
// Supported interrupt/exception setting, used for functional coverage
// ----------------------------------------------------------------------------
`ifdef DSIM
interrupt_cause_t implemented_interrupt[] = {
`else
parameter interrupt_cause_t implemented_interrupt[] = {
`endif
U_SOFTWARE_INTR,
S_SOFTWARE_INTR,
M_SOFTWARE_INTR,
U_TIMER_INTR,
S_TIMER_INTR,
M_TIMER_INTR,
U_EXTERNAL_INTR,
S_EXTERNAL_INTR,
M_EXTERNAL_INTR
};
`ifdef DSIM
exception_cause_t implemented_exception[] = {
`else
parameter exception_cause_t implemented_exception[] = {
`endif
INSTRUCTION_ADDRESS_MISALIGNED,
INSTRUCTION_ACCESS_FAULT,
ILLEGAL_INSTRUCTION,
BREAKPOINT,
LOAD_ADDRESS_MISALIGNED,
LOAD_ACCESS_FAULT,
STORE_AMO_ADDRESS_MISALIGNED,
STORE_AMO_ACCESS_FAULT,
ECALL_UMODE,
ECALL_SMODE,
ECALL_MMODE,
INSTRUCTION_PAGE_FAULT,
LOAD_PAGE_FAULT,
STORE_AMO_PAGE_FAULT
};

View file

@ -0,0 +1,42 @@
# Copyright Google LLC
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
# ================================================================================
# Regression test list format
# --------------------------------------------------------------------------------
# test : Assembly test name
# description : Description of this test
# gen_opts : Instruction generator options
# iterations : Number of iterations of this test
# no_iss : Enable/disable ISS simulator (Optional)
# gen_test : Test name used by the instruction generator
# rtl_test : RTL simulation test name
# cmp_opts : Compile options passed to the instruction generator
# sim_opts : Simulation options passed to the instruction generator
# no_post_compare : Enable/disable comparison of trace log and ISS log (Optional)
# compare_opts : Options for the RTL & ISS trace comparison
# gcc_opts : gcc compile options
# --------------------------------------------------------------------------------
- import: <riscv_dv_root>/target/rv64gc/testlist.yaml
- test: riscv_instr_test
description: >
New experimental instruction class test
iterations: 0
gen_test: riscv_instr_test
gen_opts: >
+instr_cnt=10000
+num_of_sub_program=5
+boot_mode=m

View file

@ -48,6 +48,9 @@ bit support_umode_trap = 0;
// Support sfence.vma instruction
bit support_sfence = 0;
// Support unaligned load/store
bit support_unaligned_load_store = 1'b1;
// ----------------------------------------------------------------------------
// Previleged CSR implementation
// ----------------------------------------------------------------------------

View file

@ -48,6 +48,9 @@ bit support_umode_trap = 0;
// Support sfence.vma instruction
bit support_sfence = 0;
// Support unaligned load/store
bit support_unaligned_load_store = 1'b1;
// ----------------------------------------------------------------------------
// Previleged CSR implementation
// ----------------------------------------------------------------------------

View file

@ -48,6 +48,9 @@ bit support_umode_trap = 0;
// Support sfence.vma instruction
bit support_sfence = 0;
// Support unaligned load/store
bit support_unaligned_load_store = 1'b1;
// ----------------------------------------------------------------------------
// Previleged CSR implementation
// ----------------------------------------------------------------------------

View file

@ -32,7 +32,6 @@ riscv_instr_name_t unsupported_instr[];
// ISA supported by the processor
riscv_instr_group_t supported_isa[$] = {RV32I, RV32M, RV64I, RV64M, RV32C, RV64C, RV32A, RV64A,
RV32F, RV64F, RV32D, RV64D};
// Interrupt mode support
mtvec_mode_t supported_interrupt_mode[$] = {DIRECT, VECTORED};
@ -49,6 +48,9 @@ bit support_umode_trap = 0;
// Support sfence.vma instruction
bit support_sfence = 1;
// Support unaligned load/store
bit support_unaligned_load_store = 1'b1;
// ----------------------------------------------------------------------------
// Previleged CSR implementation
// ----------------------------------------------------------------------------

View file

@ -60,7 +60,6 @@
iterations: 2
gen_test: riscv_rand_instr_test
gen_opts: >
+require_signature_addr=1
+instr_cnt=6000
+num_of_sub_program=0
+enable_access_invalid_csr_level=1
@ -122,8 +121,15 @@
Enable floating point instructions
gen_opts: >
+enable_floating_point=1
+instr_cnt=10000
+num_of_sub_program=5
+directed_instr_0=riscv_load_store_rand_instr_stream,4
+directed_instr_1=riscv_loop_instr,4
+directed_instr_2=riscv_multi_page_load_store_instr_stream,4
+directed_instr_3=riscv_mem_region_stress_test,4
+directed_instr_4=riscv_jal_instr,4
iterations: 1
gen_test: riscv_rand_instr_test
gen_test: riscv_instr_base_test
rtl_test: core_base_test
- test: riscv_floating_point_mmu_stress_test

View file

@ -48,6 +48,9 @@ bit support_umode_trap = 0;
// Support sfence.vma instruction
bit support_sfence = 0;
// Support unaligned load/store
bit support_unaligned_load_store = 1'b1;
// ----------------------------------------------------------------------------
// Previleged CSR implementation
// ----------------------------------------------------------------------------

View file

@ -24,6 +24,8 @@ class riscv_instr_base_test extends uvm_test;
riscv_asm_program_gen asm_gen;
string instr_seq;
int start_idx;
uvm_coreservice_t coreservice;
uvm_factory factory;
`uvm_component_utils(riscv_instr_base_test)
@ -35,6 +37,8 @@ class riscv_instr_base_test extends uvm_test;
virtual function void build_phase(uvm_phase phase);
super.build_phase(phase);
coreservice = uvm_coreservice_t::get();
factory = coreservice.get_factory();
`uvm_info(`gfn, "Create configuration instance", UVM_LOW)
cfg = riscv_instr_gen_config::type_id::create("cfg");
`uvm_info(`gfn, "Create configuration instance...done", UVM_LOW)
@ -43,10 +47,13 @@ class riscv_instr_base_test extends uvm_test;
asm_file_name = {asm_file_name, ".", cfg.asm_test_suffix};
// Override the default riscv instruction sequence
if($value$plusargs("instr_seq=%0s", instr_seq)) begin
uvm_coreservice_t coreservice = uvm_coreservice_t::get();
uvm_factory factory = coreservice.get_factory();
factory.set_type_override_by_name("riscv_instr_sequence", instr_seq);
end
if (riscv_instr_pkg::support_debug_mode) begin
factory.set_inst_override_by_name("riscv_asm_program_gen",
"riscv_debug_rom_gen",
{`gfn, ".asm_gen.debug_rom"});
end
endfunction
function void report_phase(uvm_phase phase);
@ -73,12 +80,18 @@ class riscv_instr_base_test extends uvm_test;
task run_phase(uvm_phase phase);
int fd;
cfg.build_instruction_template();
`ifdef DEPRECATED
cfg.build_instruction_template();
`endif
for(int i = 0; i < cfg.num_of_tests; i++) begin
string test_name;
randomize_cfg();
cfg.build_instruction_list();
asm_gen = riscv_asm_program_gen::type_id::create("asm_gen");
`ifdef DEPRECATED
cfg.build_instruction_list();
`else
riscv_instr::create_instr_list(cfg);
`endif
asm_gen = riscv_asm_program_gen::type_id::create("asm_gen", , `gfn);
asm_gen.cfg = cfg;
asm_gen.get_directed_instr_stream();
test_name = $sformatf("%0s_%0d.S", asm_file_name, i+start_idx);

View file

@ -4,6 +4,7 @@ class riscv_instr_cov_test extends uvm_test;
typedef uvm_enum_wrapper#(riscv_instr_name_t) instr_enum;
typedef uvm_enum_wrapper#(riscv_reg_t) gpr_enum;
typedef uvm_enum_wrapper#(privileged_reg_t) preg_enum;
`VECTOR_INCLUDE("riscv_instr_cov_test_inc_typedef.sv")
riscv_instr_gen_config cfg;
riscv_instr_cover_group instr_cg;
@ -38,11 +39,17 @@ class riscv_instr_cov_test extends uvm_test;
cfg = riscv_instr_gen_config::type_id::create("cfg");
// disable_compressed_instr is not relevant to coverage test
cfg.disable_compressed_instr = 0;
cfg.build_instruction_template(.skip_instr_exclusion(1));
`ifdef DEPRECATED
cfg.build_instruction_template(.skip_instr_exclusion(1));
`else
riscv_instr::create_instr_list(cfg);
`endif
instr = riscv_instr_cov_item::type_id::create("instr");
instr.rand_mode(0);
`ifdef DEPRECATED
instr.no_hint_illegal_instr_c.constraint_mode(0);
instr.imm_val_c.constraint_mode(0);
`endif
instr_cg = new(cfg);
`uvm_info(`gfn, $sformatf("%0d CSV trace files to be processed", trace_csv.size()), UVM_LOW)
foreach (trace_csv[i]) begin
@ -81,16 +88,11 @@ class riscv_instr_cov_test extends uvm_test;
// TODO: Enable functional coverage for AMO test
continue;
end
`uvm_info(`gfn, $sformatf("Processing line[%0d] : %0s",
entry_cnt, trace["str"]), UVM_FULL)
if (!sample()) begin
if (expect_illegal_instr) begin
`uvm_info(`gfn, $sformatf("Found illegal instr: %0s [%0s]",
trace["instr"], line), UVM_HIGH)
end else begin
unexpected_illegal_instr_cnt += 1;
`uvm_error(`gfn, $sformatf("Found illegal instr: %0s [%0s]",
trace["instr"], line))
`uvm_info(`gfn, $sformatf("Found illegal instr: %0s [%0s]",
trace["instr"], line), UVM_HIGH)
if (!expect_illegal_instr) begin
unexpected_illegal_instr_cnt++;
end
end
end
@ -108,39 +110,55 @@ class riscv_instr_cov_test extends uvm_test;
if ((skipped_cnt > 0) || (unexpected_illegal_instr_cnt > 0)) begin
`uvm_error(`gfn, $sformatf("%0d instructions skipped, %0d illegal instruction",
skipped_cnt, unexpected_illegal_instr_cnt))
end else begin
`uvm_info(`gfn, "TEST PASSED", UVM_NONE);
end
endtask
virtual function void post_process_trace();
endfunction
function void fatal (string str);
`uvm_info(`gfn, str, UVM_NONE);
if ($test$plusargs("stop_on_first_error")) begin
`uvm_fatal(`gfn, "Errors: *. Warnings: * (written by riscv_instr_cov.sv)")
end
endfunction
function bit sample();
riscv_instr_name_t instr_name;
bit [XLEN-1:0] val;
if (instr_enum::from_name(process_instr_name(trace["instr"]), instr_name)) begin
`ifdef DEPRECATED
if (cfg.instr_template.exists(instr_name)) begin
instr.copy_base_instr(cfg.instr_template[instr_name]);
`else
if (riscv_instr::instr_template.exists(instr_name)) begin
instr.copy(riscv_instr::instr_template[instr_name]);
`endif
assign_trace_info_to_instr(instr);
instr.pre_sample();
instr_cg.sample(instr);
return 1'b1;
end
end
`uvm_info(`gfn, $sformatf("Cannot find opcode: %0s",
process_instr_name(trace["instr"])), UVM_LOW)
get_val(trace["binary"], val);
if ((val[1:0] != 2'b11) && (RV32C inside {supported_isa})) begin
instr_cg.compressed_opcode_cg.sample(val[15:0]);
instr_cg.illegal_compressed_instr_cg.sample(val);
`SAMPLE(instr_cg.compressed_opcode_cg, val[15:0])
`SAMPLE(instr_cg.illegal_compressed_instr_cg, val)
end
if (val[1:0] == 2'b11) begin
`uvm_info("DBG", $sformatf("Sample illegal opcode: %0x [%0s]",
val[6:2], trace["instr_str"]), UVM_LOW)
instr_cg.opcode_cg.sample(val[6:2]);
`uvm_info("DBG", $sformatf("Sample opcode: %0x [%0s]", val[6:2], trace["instr"]), UVM_LOW)
`SAMPLE(instr_cg.opcode_cg, val[6:2])
end
return 1'b0;
endfunction
virtual function void assign_trace_info_to_instr(riscv_instr_cov_item instr);
riscv_reg_t gpr;
`VECTOR_INCLUDE("riscv_instr_cov_test_inc_assign_trace_info_to_instr_declares.sv")
privileged_reg_t preg;
get_val(trace["addr"], instr.pc);
get_val(trace["binary"], instr.binary);
@ -154,7 +172,7 @@ class riscv_instr_cov_test extends uvm_test;
instr.rs2 = gpr;
get_val(trace["rs2_val"], instr.rs2_value);
end else begin
`uvm_error(`gfn, $sformatf("Unrecoganized rs2: [%0s] (%0s)",
`uvm_error(`gfn, $sformatf("Unrecognized rs2: [%0s] (%0s)",
trace["rs2"], trace["csv_entry"]))
end
end
@ -163,7 +181,7 @@ class riscv_instr_cov_test extends uvm_test;
instr.rd = gpr;
get_val(trace["rd_val"], instr.rd_value);
end else begin
`uvm_error(`gfn, $sformatf("Unrecoganized rd: [%0s] (%0s)",
`uvm_error(`gfn, $sformatf("Unrecognized rd: [%0s] (%0s)",
trace["rd"], trace["csv_entry"]))
end
end
@ -176,7 +194,7 @@ class riscv_instr_cov_test extends uvm_test;
instr.rs1 = gpr;
get_val(trace["rs1_val"], instr.rs1_value);
end else begin
`uvm_error(`gfn, $sformatf("Unrecoganized rs1: [%0s] (%0s)",
`uvm_error(`gfn, $sformatf("Unrecognized rs1: [%0s] (%0s)",
trace["rs1"], trace["csv_entry"]))
end
end
@ -204,6 +222,9 @@ class riscv_instr_cov_test extends uvm_test;
instr.mem_addr = instr.rs1_value + {padding, instr.imm};
end
end
`VECTOR_INCLUDE("riscv_instr_cov_test_inc_assign_trace_info_to_instr.sv")
endfunction
function bit get_gpr(input string str, output riscv_reg_t gpr);
@ -215,6 +236,8 @@ class riscv_instr_cov_test extends uvm_test;
end
endfunction
`VECTOR_INCLUDE("riscv_instr_cov_test_inc_assign_trace_info_to_instr_functions.sv")
function void get_val(input string str, output bit [XLEN-1:0] val);
val = str.atohex();
endfunction

View file

@ -0,0 +1,58 @@
/*
* Copyright 2019 Google LLC
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
// Sanity test for riscv_instr_test class
class riscv_instr_test extends riscv_instr_base_test;
`uvm_component_utils(riscv_instr_test)
`uvm_component_new
task run_phase(uvm_phase phase);
int fd;
riscv_instr instr;
riscv_instr_name_t instr_name;
string test_name = $sformatf("%0s_0.S", asm_file_name);
fd = $fopen(test_name,"w");
`uvm_info(`gfn, "Creating instruction list", UVM_LOW)
riscv_instr::create_instr_list(cfg);
`uvm_info(`gfn, "Randomizing instruction list now...", UVM_LOW)
repeat (10000) begin
instr = riscv_instr::get_rand_instr();
`DV_CHECK_RANDOMIZE_FATAL(instr);
$fwrite(fd, {instr.convert2asm(),"\n"});
end
repeat (10000) begin
instr = riscv_instr::get_rand_instr(.include_category({LOAD, STORE}));
`DV_CHECK_RANDOMIZE_FATAL(instr);
$fwrite(fd, {instr.convert2asm(),"\n"});
end
repeat (10000) begin
instr = riscv_instr::get_rand_instr(.exclude_category({LOAD, STORE , BRANCH}),
.include_group({RV32I, RV32M}));
`DV_CHECK_RANDOMIZE_FATAL(instr);
$fwrite(fd, {instr.convert2asm(),"\n"});
end
$fclose(fd);
`uvm_info(get_full_name(), $sformatf("%0s is generated", test_name), UVM_LOW)
endtask
virtual function void randomize_cfg();
`DV_CHECK_RANDOMIZE_FATAL(cfg);
`uvm_info(`gfn, $sformatf("riscv_instr_gen_config is randomized:\n%0s",
cfg.sprint()), UVM_LOW)
endfunction
endclass

View file

@ -23,5 +23,8 @@ package riscv_instr_test_pkg;
`include "riscv_instr_test_lib.sv"
`include "riscv_instr_cov_debug_test.sv"
`include "riscv_instr_cov_test.sv"
`ifdef EXPERIMENTAL
`include "riscv_instr_test.sv"
`endif
endpackage

View file

@ -59,7 +59,6 @@
+directed_instr_4=riscv_multi_page_load_store_instr_stream,4
+directed_instr_5=riscv_mem_region_stress_test,4
+directed_instr_6=riscv_jal_instr,4
+directed_instr_7=riscv_load_store_rand_addr_instr_stream,4
rtl_test: core_base_test
- test: riscv_jump_stress_test
@ -91,7 +90,7 @@
gen_test: riscv_instr_base_test
gen_opts: >
+instr_cnt=10000
+num_of_sub_program=20
+num_of_sub_program=10
+directed_instr_0=riscv_load_store_rand_instr_stream,8
rtl_test: core_base_test
@ -151,10 +150,8 @@
gen_opts: >
+instr_cnt=6000
+no_ebreak=0
+require_signature_addr=1
rtl_test: core_base_test
sim_opts: >
+require_signature_addr=1
+enable_debug_seq=1
compare_opts: >
+compare_final_value_only=1
@ -166,11 +163,9 @@
gen_test: riscv_rand_instr_test
gen_opts: >
+enable_interrupt=1
+require_signature_addr=1
rtl_test: core_base_test
sim_opts: >
+enable_irq_seq=1
+require_signature_addr=1
compare_opts: >
+compare_final_value_only=1

View file

@ -68,6 +68,7 @@
cov_opts: >
-do "coverage save -onexit <out>/cov.ucdb;"
# TODO: Remove +DEPRECATED for dsim
- tool: dsim
env_var: DSIM,DSIM_LIB_PATH
compile:
@ -76,6 +77,7 @@
- "<DSIM> -sv -work <out>/dsim
-genimage image
+incdir+$UVM_HOME/src
+define+DEPRECATED
$UVM_HOME/src/uvm_pkg.sv
+define+DSIM
+incdir+<setting>
@ -86,3 +88,17 @@
cmd: >
<DSIM> <sim_opts> -sv_seed <seed> -pli_lib <DSIM_LIB_PATH>/libuvm_dpi.so +acc+rwb -image image -work <out>/dsim
- tool: qrun
compile:
cmd:
- "qrun -f <cwd>/qrun_option.f
+incdir+<setting>
+incdir+<user_extension>
-f <cwd>/files.f <cmp_opts>
-l <out>/qrun_compile_optimize.log
-outdir <out>/qrun.out"
sim:
cmd: >
qrun -64 -simulate -snapshot design_opt -c <cov_opts> <sim_opts> -sv_seed <seed> -outdir <out>/qrun.out
cov_opts: >
-coverage -ucdb <out>/cov.ucdb