Update google_riscv-dv to 215e064

Update code from upstream repository https://github.com/google/riscv-
dv to revision 215e0646ae9909aa0e78d6e91f4f33ed77f95e43
This commit is contained in:
Tao Liu 2019-05-31 07:09:52 -07:00 committed by Philipp Wagner
parent 5319734180
commit bdb089d456
43 changed files with 8555 additions and 0 deletions

14
vendor/google_riscv-dv.lock.hjson vendored Normal file
View file

@ -0,0 +1,14 @@
// Copyright lowRISC contributors.
// Licensed under the Apache License, Version 2.0, see LICENSE for details.
// SPDX-License-Identifier: Apache-2.0
// This file is generated by the vendor_hw script. Please do not modify it
// manually.
{
upstream:
{
url: https://github.com/google/riscv-dv
rev: 215e0646ae9909aa0e78d6e91f4f33ed77f95e43
}
}

28
vendor/google_riscv-dv/CONTRIBUTING.md vendored Normal file
View file

@ -0,0 +1,28 @@
# How to Contribute
We'd love to accept your patches and contributions to this project. There are
just a few small guidelines you need to follow.
## Contributor License Agreement
Contributions to this project must be accompanied by a Contributor License
Agreement. You (or your employer) retain the copyright to your contribution;
this simply gives us permission to use and redistribute your contributions as
part of the project. Head over to <https://cla.developers.google.com/> to see
your current agreements on file or to sign a new one.
You generally only need to submit a CLA once, so if you've already submitted one
(even if it was for a different project), you probably don't need to do it
again.
## Code reviews
All submissions, including submissions by project members, require review. We
use GitHub pull requests for this purpose. Consult
[GitHub Help](https://help.github.com/articles/about-pull-requests/) for more
information on using pull requests.
## Community Guidelines
This project follows
[Google's Open Source Community Guidelines](https://opensource.google.com/conduct/).

201
vendor/google_riscv-dv/LICENSE.txt vendored Normal file
View file

@ -0,0 +1,201 @@
Apache License
Version 2.0, January 2004
http://www.apache.org/licenses/
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
1. Definitions.
"License" shall mean the terms and conditions for use, reproduction,
and distribution as defined by Sections 1 through 9 of this document.
"Licensor" shall mean the copyright owner or entity authorized by
the copyright owner that is granting the License.
"Legal Entity" shall mean the union of the acting entity and all
other entities that control, are controlled by, or are under common
control with that entity. For the purposes of this definition,
"control" means (i) the power, direct or indirect, to cause the
direction or management of such entity, whether by contract or
otherwise, or (ii) ownership of fifty percent (50%) or more of the
outstanding shares, or (iii) beneficial ownership of such entity.
"You" (or "Your") shall mean an individual or Legal Entity
exercising permissions granted by this License.
"Source" form shall mean the preferred form for making modifications,
including but not limited to software source code, documentation
source, and configuration files.
"Object" form shall mean any form resulting from mechanical
transformation or translation of a Source form, including but
not limited to compiled object code, generated documentation,
and conversions to other media types.
"Work" shall mean the work of authorship, whether in Source or
Object form, made available under the License, as indicated by a
copyright notice that is included in or attached to the work
(an example is provided in the Appendix below).
"Derivative Works" shall mean any work, whether in Source or Object
form, that is based on (or derived from) the Work and for which the
editorial revisions, annotations, elaborations, or other modifications
represent, as a whole, an original work of authorship. For the purposes
of this License, Derivative Works shall not include works that remain
separable from, or merely link (or bind by name) to the interfaces of,
the Work and Derivative Works thereof.
"Contribution" shall mean any work of authorship, including
the original version of the Work and any modifications or additions
to that Work or Derivative Works thereof, that is intentionally
submitted to Licensor for inclusion in the Work by the copyright owner
or by an individual or Legal Entity authorized to submit on behalf of
the copyright owner. For the purposes of this definition, "submitted"
means any form of electronic, verbal, or written communication sent
to the Licensor or its representatives, including but not limited to
communication on electronic mailing lists, source code control systems,
and issue tracking systems that are managed by, or on behalf of, the
Licensor for the purpose of discussing and improving the Work, but
excluding communication that is conspicuously marked or otherwise
designated in writing by the copyright owner as "Not a Contribution."
"Contributor" shall mean Licensor and any individual or Legal Entity
on behalf of whom a Contribution has been received by Licensor and
subsequently incorporated within the Work.
2. Grant of Copyright License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
copyright license to reproduce, prepare Derivative Works of,
publicly display, publicly perform, sublicense, and distribute the
Work and such Derivative Works in Source or Object form.
3. Grant of Patent License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
(except as stated in this section) patent license to make, have made,
use, offer to sell, sell, import, and otherwise transfer the Work,
where such license applies only to those patent claims licensable
by such Contributor that are necessarily infringed by their
Contribution(s) alone or by combination of their Contribution(s)
with the Work to which such Contribution(s) was submitted. If You
institute patent litigation against any entity (including a
cross-claim or counterclaim in a lawsuit) alleging that the Work
or a Contribution incorporated within the Work constitutes direct
or contributory patent infringement, then any patent licenses
granted to You under this License for that Work shall terminate
as of the date such litigation is filed.
4. Redistribution. You may reproduce and distribute copies of the
Work or Derivative Works thereof in any medium, with or without
modifications, and in Source or Object form, provided that You
meet the following conditions:
(a) You must give any other recipients of the Work or
Derivative Works a copy of this License; and
(b) You must cause any modified files to carry prominent notices
stating that You changed the files; and
(c) You must retain, in the Source form of any Derivative Works
that You distribute, all copyright, patent, trademark, and
attribution notices from the Source form of the Work,
excluding those notices that do not pertain to any part of
the Derivative Works; and
(d) If the Work includes a "NOTICE" text file as part of its
distribution, then any Derivative Works that You distribute must
include a readable copy of the attribution notices contained
within such NOTICE file, excluding those notices that do not
pertain to any part of the Derivative Works, in at least one
of the following places: within a NOTICE text file distributed
as part of the Derivative Works; within the Source form or
documentation, if provided along with the Derivative Works; or,
within a display generated by the Derivative Works, if and
wherever such third-party notices normally appear. The contents
of the NOTICE file are for informational purposes only and
do not modify the License. You may add Your own attribution
notices within Derivative Works that You distribute, alongside
or as an addendum to the NOTICE text from the Work, provided
that such additional attribution notices cannot be construed
as modifying the License.
You may add Your own copyright statement to Your modifications and
may provide additional or different license terms and conditions
for use, reproduction, or distribution of Your modifications, or
for any such Derivative Works as a whole, provided Your use,
reproduction, and distribution of the Work otherwise complies with
the conditions stated in this License.
5. Submission of Contributions. Unless You explicitly state otherwise,
any Contribution intentionally submitted for inclusion in the Work
by You to the Licensor shall be under the terms and conditions of
this License, without any additional terms or conditions.
Notwithstanding the above, nothing herein shall supersede or modify
the terms of any separate license agreement you may have executed
with Licensor regarding such Contributions.
6. Trademarks. This License does not grant permission to use the trade
names, trademarks, service marks, or product names of the Licensor,
except as required for reasonable and customary use in describing the
origin of the Work and reproducing the content of the NOTICE file.
7. Disclaimer of Warranty. Unless required by applicable law or
agreed to in writing, Licensor provides the Work (and each
Contributor provides its Contributions) on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
implied, including, without limitation, any warranties or conditions
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
PARTICULAR PURPOSE. You are solely responsible for determining the
appropriateness of using or redistributing the Work and assume any
risks associated with Your exercise of permissions under this License.
8. Limitation of Liability. In no event and under no legal theory,
whether in tort (including negligence), contract, or otherwise,
unless required by applicable law (such as deliberate and grossly
negligent acts) or agreed to in writing, shall any Contributor be
liable to You for damages, including any direct, indirect, special,
incidental, or consequential damages of any character arising as a
result of this License or out of the use or inability to use the
Work (including but not limited to damages for loss of goodwill,
work stoppage, computer failure or malfunction, or any and all
other commercial damages or losses), even if such Contributor
has been advised of the possibility of such damages.
9. Accepting Warranty or Additional Liability. While redistributing
the Work or Derivative Works thereof, You may choose to offer,
and charge a fee for, acceptance of support, warranty, indemnity,
or other liability obligations and/or rights consistent with this
License. However, in accepting such obligations, You may act only
on Your own behalf and on Your sole responsibility, not on behalf
of any other Contributor, and only if You agree to indemnify,
defend, and hold each Contributor harmless for any liability
incurred by, or claims asserted against, such Contributor by reason
of your accepting any such warranty or additional liability.
END OF TERMS AND CONDITIONS
APPENDIX: How to apply the Apache License to your work.
To apply the Apache License to your work, attach the following
boilerplate notice, with the fields enclosed by brackets "[]"
replaced with your own identifying information. (Don't include
the brackets!) The text should be enclosed in the appropriate
comment syntax for the file format. We also recommend that a
file or class name and description of purpose be included on the
same "printed page" as the copyright notice for easier
identification within third-party archives.
Copyright [yyyy] [name of copyright owner]
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.

190
vendor/google_riscv-dv/README.md vendored Normal file
View file

@ -0,0 +1,190 @@
# RISCV-DV
RISCV-DV is a SV/UVM based open-source instruction generator for RISC-V
processor verification. It currently supports the following features:
- Supported instruction set: RV32IMC, RV64IMC
- Supported privileged mode: machine mode, supervisor mode, user mode
- Page table randomization and exception
- Privileged CSR setup randomization
- Trap/interrupt handling
- Test suite to stress test MMU
- Support sub-programs and random program calls
- Support illegal instruction and HINT instruction
- Random forward/backward branch instructions
- Support mix directed instruciton with random instruction stream
- Support co-simulation with multiple ISS : spike, riscv-ovpsim
## Getting Started
### Prerequisites
To be able to run the instruction generator, you need to have an RTL simulator
which supports Systemverilog and UVM 1.2. This generator has been verified with
Synopsys VCS and Cadence Incisive/Xcelium simulator. Please make sure the EDA
tool environment is properly setup before running the generator.
### Running the generator
A simple script "run" is provided for you to run a single test or a regression.
Here is the command to run a single test:
```
./run -test riscv_instr_base_test
```
You can specify the simulator by "-tool" option
```
./run -test riscv_instr_base_test -tool irun
./run -test riscv_instr_base_test -tool vcs
```
The complete test list can be found in testlist. To run a full regression, you
can just specify the test name to "all".
```
./run -test all
```
The script will run each test in the test list sequentially with the iteration
count specified in the "testlist". All the generated RISC-V assembly will be
listed when the regression is done. If it is successful, you should see the
following output:
```
===========================================================
Generated RISC-V assembly tests
----------------------------------------------------------
./out_2018-11-20/asm_tests/riscv_arithmetic_basic_test.0.S
./out_2018-11-20/asm_tests/riscv_machine_mode_rand_test.0.S
./out_2018-11-20/asm_tests/riscv_mmu_stress_test.0.S
./out_2018-11-20/asm_tests/riscv_mmu_stress_test.1.S
./out_2018-11-20/asm_tests/riscv_no_fence_test.0.S
./out_2018-11-20/asm_tests/riscv_page_table_exception_test.0.S
./out_2018-11-20/asm_tests/riscv_page_table_exception_test.1.S
./out_2018-11-20/asm_tests/riscv_privileged_mode_rand_test.0.S
./out_2018-11-20/asm_tests/riscv_privileged_mode_rand_test.1.S
./out_2018-11-20/asm_tests/riscv_rand_instr_test.0.S
./out_2018-11-20/asm_tests/riscv_rand_instr_test.1.S
./out_2018-11-20/asm_tests/riscv_rand_jump_test.0.S
./out_2018-11-20/asm_tests/riscv_sfence_exception_test.0.S
```
Here's a few more examples of the run command:
```
// Run a single test 10 times
./run -test riscv_page_table_exception_test -n 10
// Run a test with a specified seed
./run -test riscv_page_table_exception_test -seed 123
// Run a test with addtional runtime options, separated with comma
./run -test riscv_rand_instr_test -runo +instr_cnt=10000,+no_fence=1
// Two steps compile and simulation (Avoid multiple compilation)
./run -co # compile only
# Generate multiple tests
./run -so -test riscv_rand_instr_test -n 10
./run -so -test riscv_mmu_stress_test -n 20
....
```
### Use the generated test in your RTL and ISS simulation
You need to use the RISC-V gcc/llvm compiler to compile the assembly tests to an ELF
file and feed into your TB. We currently don't provide a reference TB for the
co-simulation as it could be quite different based on the processor and ISS
implementation. A reference script "iss_sim" is provided to compile the program
with the RISC-V gcc compiler and simulate with spike.
```
./run -test all; ./iss_sim
```
To run with ISS simulation for RV32IMC, you can specify ISA and ABI from command
line like this:
```
./iss_sim -isa rv32imc -abi ilp32
```
The default ISS is spike. Thanks for the great support from Imperas Software Ltd.,
we have added the support for [riscv-ovpsim](https://github.com/riscv/riscv-ovpsim).
You can use -iss to run with different ISS.
```
./iss_sim -iss spike # Use spike as ISS
./iss_sim -iss ovpsim # Use riscv-ovpsim as ISS
```
We have added a flow to run ISS simulation with both spike and riscv-ovpsim,
the instruction trace from these runs will be cross compared. This could greatly
speed up your development of new test without the need to simulate against a
real RISC-V processor.
```
./iss_sim -iss all # Run ISS simulation with spike + riscv-ovpsim
```
## Configure the generator to match your processor features
The default configuration of the instruction generator is for RV64IMC RISC-V
processors with address translation capability. You might want to configure the
generator according the feature of your processor.
The static setting of the processor src/riscv_core_setting.sv
```
// Bit width of RISC-V GPR
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};
...
```
## Runtime options of the generator
## Adding new instruction stream and test
Please refer to src/src/riscv_load_store_instr_lib.sv for an example on how to
add a new instruction stream.
```
virtual function void apply_directed_instr();
asm_gen.add_directed_instr_stream("my_new_instr_stream_class_name", 10);
endfunction
```
After the new instruction stream is created, you
can refer to test/riscv_instr_test_lib.sv to see how an instruction stream can
be mixed with existing random instruction stream.
## Supporting model
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.
## Future release plan
We have some work in progress which will be part of future releases:
- Privileged CSR test suite.
- Coverage model.
## Disclaimer
This is not an officially supported Google product.

22
vendor/google_riscv-dv/files.f vendored Normal file
View file

@ -0,0 +1,22 @@
// 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.
// HEADERS
+incdir+./src
+incdir+./test
// SOURCES
./src/riscv_instr_pkg.sv
./test/riscv_instr_test_pkg.sv
./test/riscv_instr_gen_tb_top.sv

50
vendor/google_riscv-dv/iss_cmp vendored Executable file
View file

@ -0,0 +1,50 @@
#!/bin/bash
# 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.
spike_log="$1"
ovpsim_log="$2"
report_file="$3"
# -----------------------------------------------------------------------------
# Convert spike log to standard instruction trace csv
# -----------------------------------------------------------------------------
# Remove all the init spike boot instructions
# 0xffffffff80000000 is the first user instruction
if [[ "$XLEN" == "32" ]]; then
sed -i '/0xffffffff80000000/,$!d' "$spike_log"
else
sed -i '/core.*0x0000000080000000/,$!d' "$spike_log"
fi
# Remove all instructions after ecall (end of program excecution)
sed -i '/ecall/q' "$spike_log"
# Convert the spike log to riscv_instr_trace.proto format
spike_csv=$(echo "$spike_log" | sed 's/\.log/.csv/g')
python scripts/spike_log_to_trace_csv.py --log $spike_log --csv $spike_csv
# -----------------------------------------------------------------------------
# Convert ovpsim log to standard instruction trace csv
# -----------------------------------------------------------------------------
# Remove the header part of ovpsim log
sed -i '/Info 1:/,$!d' "$ovpsim_log"
# Remove all instructions after ecall (end of program excecution)
sed -i '/ecall/q' "$ovpsim_log"
# Convert the spike log to riscv_instr_trace.proto format
ovpsim_csv=$(echo "$ovpsim_log" | sed 's/\.log/.csv/g')
python scripts/ovpsim_log_to_trace_csv.py --log $ovpsim_log --csv $ovpsim_csv
# -----------------------------------------------------------------------------
# Compare the trace log
# -----------------------------------------------------------------------------
python scripts/instr_trace_compare.py $spike_csv $ovpsim_csv "spike" "ovpsim" >> $report_file

179
vendor/google_riscv-dv/iss_sim vendored Executable file
View file

@ -0,0 +1,179 @@
#!/bin/bash
# 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.
# Path for RISC-V GCC toolchain
# You can install the toolchain from https://github.com/riscv/riscv-gcc
RISCV_TOOLCHAIN="XXX"
# GCC compile options
ABI="lp64"
ISA="rv64imc"
DATE=`date +%Y-%m-%d`
# Instruction set simulator
ISS="spike" # other options: ovpsim, all
# riscv-ovpsim options
OVPSIM_VARIANT="RV64GC"
RISCV_OVPSIM="YOUR_PATH_HERE/riscv-ovpsim/bin/Linux64/riscvOVPsim.exe"
# Directory of assemble tests
SRC_DIR="./out_${DATE}/asm_tests"
# Assembly test file name
TEST=""
# Regression report file name
REPORT="$SRC_DIR/regression_report.log"
# Clean the result of the previous runs
CLEAN=1
# Process command line options
while [[ $# -gt 0 ]]
do
key="$1"
case $key in
-iss)
ISS="$2"
shift
;;
-dir)
SRC_DIR="$2"
shift
;;
-toolchain)
RISCV_TOOLCHAIN="$2"
shift
;;
-isa)
ISA="$2"
shift
;;
-abi)
ABI="$2"
shift
;;
-test)
TEST="$2"
shift
;;
-report)
REPORT="$2"
shift
;;
-noclean)
CLEAN=0
shift
;;
*)
echo "unknown option $1"
return
;;
esac
shift
done
RISCV_GCC="$RISCV_TOOLCHAIN/bin/riscv64-unknown-elf-gcc"
RISCV_OBJCOPY="$RISCV_TOOLCHAIN/bin/riscv64-unknown-elf-objcopy"
RISCV_SPIKE="$RISCV_TOOLCHAIN/bin/spike"
mkdir -p "$SRC_DIR"
# If the test is specified through "-test" option, run a single test rather
# than all tests under SRC_DIR.
if [[ $TEST == "" ]]; then
find "$SRC_DIR" -name "*.S" > "$SRC_DIR/asm_test_list"
else
echo "$TEST" > "$SRC_DIR/asm_test_list"
fi
if [[ $ISA =~ 32 ]]; then
OVPSIM_VARIANT="RV32GC"
fi
# Clean up previous running result
if [[ $CLEAN == 1 ]]; then
rm -rf "$REPORT"
if [[ "$ISS" == "spike" ]] || [[ "$ISS" == "all" ]]; then
rm -rf "$SRC_DIR/spike_sim"
fi
if [[ "$ISS" == "ovpsim" ]] || [[ "$ISS" == "all" ]]; then
rm -rf "$SRC_DIR/riscv_ovpsim"
fi
fi
# GCC compile
while read asm_test; do
# Generate binary for RTL simulation
SRC="$asm_test"
OBJFILE="$asm_test.o"
BINFILE="$asm_test.bin"
GCC_CMD="$RISCV_GCC -march=$ISA -mabi=$ABI -static -mcmodel=medany \
-fvisibility=hidden -nostdlib \
-nostartfiles -I$RISCV_TESTS/env/p \
-Tscripts/link.ld $SRC -o $OBJFILE"
echo "riscv_gcc compiling : $SRC"
$($GCC_CMD)
echo "Convert $OBJFILE to $BINFILE"
# Convert the ELF to plain binary
# You can load this binary to your RTL simulation
"$RISCV_OBJCOPY" -O binary "$OBJFILE" "$BINFILE"
done <"$SRC_DIR/asm_test_list"
if [[ "$ISS" == "ovpsim" ]] || [[ "$ISS" == "all" ]]; then
mkdir -p "$SRC_DIR/riscv_ovpsim"
fi
if [[ "$ISS" == "spike" ]] || [[ "$ISS" == "all" ]]; then
mkdir -p "$SRC_DIR/spike_sim"
fi
# Run ISS simulation
while read asm_test; do
ELF="${asm_test}.o"
TEST_NAME=$(echo "$ELF" | sed 's/^.*\///g')
# Spike sim
if [[ "$ISS" == "spike" ]] || [[ "$ISS" == "all" ]]; then
echo "Running spike: $TEST_NAME"
SPIKE_LOG="$SRC_DIR/spike_sim/$TEST_NAME.log"
SPIKE_CMD="timeout 60s $RISCV_SPIKE --isa=$ISA -l $ELF &> $SPIKE_LOG"
$($SPIKE_CMD &> $SPIKE_LOG)
fi
# riscv_ovpsim sim
if [[ "$ISS" == "ovpsim" ]] || [[ "$ISS" == "all" ]]; then
OVPSIM_LOG="$SRC_DIR/riscv_ovpsim/$TEST_NAME.log"
echo "Running ovpsim: $TEST_NAME"
RISCV_OVPSIM_CMD="$RISCV_OVPSIM --variant $OVPSIM_VARIANT \
--override riscvOVPsim/cpu/PMP_registers=0 \
--override riscvOVPsim/cpu/simulateexceptions=T \
--trace --tracechange --traceshowicount --program $ELF \
--finishafter 500000"
$($RISCV_OVPSIM_CMD &> $OVPSIM_LOG)
fi
if [[ "$ISS" == "all" ]]; then
echo "Rerun command: ./iss_sim -test $asm_test -iss all" >> "$REPORT"
echo "spike : $SPIKE_LOG" >> "$REPORT"
echo "ovpsim : $OVPSIM_LOG" >> "$REPORT"
./iss_cmp "$SPIKE_LOG" "$OVPSIM_LOG" "$REPORT"
tail -1 "$REPORT"
echo "" >> "$REPORT"
fi
done <"$SRC_DIR/asm_test_list"
if [[ "$ISS" == "all" ]]; then
echo "Full regression report is saved to $REPORT"
cat "$REPORT"
fi

170
vendor/google_riscv-dv/run vendored Executable file
View file

@ -0,0 +1,170 @@
#!/bin/bash
# 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.
# This is simple run script to run a dedicated test or a regression
#
# Usage:
# Run a single test with irun
# ./run -tool irun -test riscv_instr_base_test
#
# Run regression with vcs
# ./run -test all
#
# Change output directory
# ./run -out my_output_dir
DATE=`date +%Y-%m-%d`
# RTL simulator, support vcs and irun
SIMULATOR="vcs"
# random seed
SEED=`date +%s`
# Test name, "all" means run all tests in the testlist
TEST="riscv_instr_base_test"
# Number of assembly programs to be generated for this test
# This option only apply to single test mode. For the regression mode, the number is specified in
# the testlist
NUM_TESTS=1
# Simulation output directory
OUT="./out_${DATE}"
# Simulation only
SIM_ONLY=0
# Compile only
CMP_ONLY=0
# Process command line options
while [[ $# -gt 0 ]]
do
key="$1"
case $key in
-tool)
SIMULATOR="$2"
shift
;;
-test)
TEST="$2"
shift
;;
-n)
NUM_TESTS="$2"
shift
;;
-seed)
SEED="$2"
shift
;;
-so)
SIM_ONLY=1
;;
-co)
CMP_ONLY=1
;;
-o)
OUT="$2"
shift
;;
*)
echo "unknown option $1"
exit 1
;;
esac
shift
done
# Generate compile and simulation commands
if [[ "$SIMULATOR" == "vcs" ]]; then
COMPILE_CMD="vcs -file ./vcs.compile.option.f \
-f ./files.f -full64 \
-l $OUT/compile.log \
-Mdir=$OUT/vcs_simv.csrc \
-o $OUT/vcs_simv"
SIM_CMD="$OUT/vcs_simv +UVM_TESTNAME="
elif [[ "$SIMULATOR" == "irun" ]]; then
COMPILE_CMD="irun -64bit \
-access +rwc \
-f ./files.f \
-q -sv -uvm \
-vlog_ext +.vh -I. \
-uvmhome CDNS-1.2 \
-l ${OUT}/compile.log"
SIM_CMD="irun -R +UVM_TESTNAME="
else
echo "unsupported simulator $SIMULATOR"
exit 1
fi
# Clean up previous runs
if [[ $SIM_ONLY == 0 ]]; then
rm -rf ${OUT}
fi
mkdir -p ${OUT}
mkdir -p ${OUT}/asm_tests
# Compilation
if [[ $SIM_ONLY == 0 ]]; then
${COMPILE_CMD}
fi
# Skip simulation if compilation only flag is set
if [[ $CMP_ONLY == 1 ]]; then
exit 0
fi
# Run sim
if [[ ${TEST} == "all" ]]; then
echo "Running regression with testlist:"
cat testlist
while read line; do
if ! [[ $line =~ ^\/\/ ]]; then
if [[ $line =~([a-z0-9_-]*)([[:space:]]*)\:([[:space:]]*)([0-9]*) ]]; then
SEED=`date +%s`
TEST=${BASH_REMATCH[1]}
ITERATION=${BASH_REMATCH[4]}
echo "Running ${TEST}, iteration count: ${ITERATION}"
if ! [[ $ITERATION == "0" ]]; then
${SIM_CMD}${TEST} +asm_file_name=${OUT}/asm_tests/${TEST} \
+ntb_random_seed=${SEED} \
-l ${OUT}/sim_${TEST}.log +num_of_tests=${ITERATION}
fi
fi
fi
done < testlist
else
echo "Running test ${TEST} with $SIMULATOR.."
${SIM_CMD}${TEST} +asm_file_name=${OUT}/asm_tests/${TEST} \
+ntb_random_seed=${SEED} \
-l ${OUT}/sim_${TEST}.log \
+num_of_tests=${NUM_TESTS}
fi
# List all generated assembly tests
echo "==========================================================="
echo " Generated RISC-V assembly tests"
echo " ----------------------------------------------------------"
find $OUT/asm_tests -name "*.S" | sort -k11

View file

@ -0,0 +1,53 @@
"""
Copyright lowRISC contributors.
Licensed under the Apache License, Version 2.0, see LICENSE for details.
SPDX-License-Identifier: Apache-2.0
Convert ibex log to the standard trace CSV format
"""
import re
import argparse
from riscv_trace_csv import *
def process_ibex_sim_log(ibex_log, csv):
"""Process ibex simulation log.
Extract instruction and affected register information from ibex simulation
log and save to a standard CSV format.
"""
print("Processing ibex log : %s" % ibex_log)
instr_cnt = 0
ibex_instr = ""
with open(ibex_log, "r") as f, open(csv, "w") as csv_fd:
trace_csv = RiscvInstructiontTraceCsv(csv_fd)
trace_csv.start_new_trace()
for line in f:
# Extract instruction infromation
m = re.search(r"^\s*(?P<time>\d+)\s+(?P<cycle>\d+) " \
"(?P<pc>[0-9a-f]+) (?P<bin>[0-9a-f]+) (?P<instr>.*)" \
"x(?P<rd>\d+)=(?P<val>[0-9a-f]+)", line)
if m:
# Write the extracted instruction to a csvcol buffer file
rv_instr_trace = RiscvInstructiontTraceEntry()
rv_instr_trace.rd = gpr_to_abi("x%0s" % m.group("rd"))
rv_instr_trace.rd_val = m.group("val")
rv_instr_trace.addr = m.group("pc")
rv_instr_trace.binary = m.group("bin")
rv_instr_trace.instr_str = m.group("instr")
trace_csv.write_trace_entry(rv_instr_trace)
#print("Processed instruction[%d] : %0s" % (instr_cnt, m.group("instr")))
instr_cnt += 1
print("Processed instruction count : %d" % instr_cnt)
instr_trace = []
# Parse input arguments
parser = argparse.ArgumentParser()
parser.add_argument("--log", type=str, help="Input ibex simulation log")
parser.add_argument("--csv", type=str, help="Output trace csv_buf file")
args = parser.parse_args()
# Process ibex log
process_ibex_sim_log(args.log, args.csv)

View file

@ -0,0 +1,122 @@
"""
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.
Convert ovpsim sim log to standard riscv instruction trace format
"""
import re
import argparse
from riscv_trace_csv import *
def compare_trace_csv(csv1, csv2, name1, name2):
"""Compare two trace CSV file"""
matched_cnt = 0
mismatch_cnt = 0
gpr_val_1 = {}
gpr_val_2 = {}
with open(csv1, "r") as fd1, open(csv2, "r") as fd2:
instr_trace_1 = []
instr_trace_2 = []
trace_csv_1 = RiscvInstructiontTraceCsv(fd1)
trace_csv_2 = RiscvInstructiontTraceCsv(fd2)
trace_csv_1.read_trace(instr_trace_1)
trace_csv_2.read_trace(instr_trace_2)
trace_1_index = 0
trace_2_index = 0
for trace in instr_trace_1:
trace_1_index += 1
# Check if there's a GPR change caused by this instruction
gpr_state_change_1 = check_update_gpr(trace.rd, trace.rd_val, gpr_val_1)
if gpr_state_change_1 == 0:
continue
# Move forward the other trace until a GPR update happens
gpr_state_change_2 = 0
while (gpr_state_change_2 == 0 and trace_2_index < len(instr_trace_2)):
gpr_state_change_2 = check_update_gpr(
instr_trace_2[trace_2_index].rd,
instr_trace_2[trace_2_index].rd_val,
gpr_val_2)
trace_2_index += 1
# Check if the GPR update is the same between trace 1 and 2
if gpr_state_change_2 == 0:
mismatch_cnt += 1
print("Mismatch[%d]:\n[%d] %s : %s -> %s(0x%s) addr:0x%s" %
(mismatch_cnt, trace_1_index, name1, trace.instr_str, trace.rd,
trace.rd_val, trace.addr))
print ("%0d instructions left in trace %0s" %
(len(instr_trace_1) - trace_1_index + 1, name1))
elif (trace.rd != instr_trace_2[trace_2_index-1].rd or
trace.rd_val != instr_trace_2[trace_2_index-1].rd_val):
mismatch_cnt += 1
# print first 5 mismatches
if mismatch_cnt <= 5:
print("Mismatch[%d]:\n[%d] %s : %s -> %s(0x%s) addr:0x%s" %
(mismatch_cnt, trace_2_index - 1, name1, trace.instr_str,
trace.rd, trace.rd_val, trace.addr))
print("[%d] %s : %s -> %s(0x%s) addr:0x%s" %
(trace_2_index - 1, name2,
instr_trace_2[trace_2_index-1].instr_str,
instr_trace_2[trace_2_index-1].rd,
instr_trace_2[trace_2_index-1].rd_val,
instr_trace_2[trace_2_index-1].addr))
else:
matched_cnt += 1
# Break the loop if it reaches the end of trace 2
if trace_2_index == len(instr_trace_2):
break
# Check if there's remaining instruction that change architectural state
if trace_2_index != len(instr_trace_2):
while (trace_2_index < len(instr_trace_2)):
gpr_state_change_2 = check_update_gpr(
instr_trace_2[trace_2_index].rd,
instr_trace_2[trace_2_index].rd_val,
gpr_val_2)
if gpr_state_change_2 == 1:
print ("%0d instructions left in trace %0s" %
(len(instr_trace_2) - trace_2_index, name2))
mismatch_cnt += len(instr_trace_2) - trace_2_index
break
trace_2_index += 1
if mismatch_cnt == 0:
compare_result = "PASSED"
else:
compare_result = "FAILED"
print("Compare result[%s]: %d matched, %d mismatch" %
(compare_result, matched_cnt, mismatch_cnt))
def check_update_gpr(rd, rd_val, gpr):
gpr_state_change = 0
if rd in gpr:
if rd_val != gpr[rd]:
gpr_state_change = 1
else:
if int(rd_val, 16) != 0:
gpr_state_change = 1
gpr[rd] = rd_val
return gpr_state_change
# Parse input arguments
parser = argparse.ArgumentParser()
parser.add_argument("csv_file_1", type=str, help="Instruction trace 1 CSV")
parser.add_argument("csv_file_2", type=str, help="Instruction trace 2 CSV")
parser.add_argument("csv_name_1", type=str, help="Instruction trace 1 name")
parser.add_argument("csv_name_2", type=str, help="Instruction trace 2 name")
args = parser.parse_args()
# Process ovpsim csv
compare_trace_csv(args.csv_file_1, args.csv_file_2,
args.csv_name_1, args.csv_name_2)

31
vendor/google_riscv-dv/scripts/link.ld vendored Normal file
View file

@ -0,0 +1,31 @@
/* 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.
*/
OUTPUT_ARCH( "riscv" )
ENTRY(_start)
SECTIONS
{
. = 0x80000000;
.text.init : { *(.text.init) }
. = ALIGN(0x1000);
.tohost : { *(.tohost) }
. = ALIGN(0x1000);
.text : { *(.text) }
. = ALIGN(0x1000);
.data : { *(.data) }
.bss : { *(.bss) }
_end = .;
}

View file

@ -0,0 +1,70 @@
"""
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.
Convert ovpsim sim log to standard riscv instruction trace format
"""
import re
import argparse
from riscv_trace_csv import *
def process_ovpsim_sim_log(ovpsim_log, csv):
"""Process SPIKE simulation log.
Extract instruction and affected register information from ovpsim simulation
log and save to a list.
"""
print("Processing ovpsim log : %s" % ovpsim_log)
instr_cnt = 0
trace_instr = ""
trace_bin = ""
trace_addr = ""
with open(ovpsim_log, "r") as f, open(csv, "w") as csv_fd:
trace_csv = RiscvInstructiontTraceCsv(csv_fd)
trace_csv.start_new_trace()
for line in f:
# Extract instruction infromation
m = re.search(r"riscvOVPsim.*, 0x(?P<addr>.*?)(?P<section>\(.*\): ?)" \
"(?P<bin>[a-f0-9]*?)\s+(?P<instr>.*?)$", line)
if m:
trace_bin = m.group("bin")
trace_instr = m.group("instr")
trace_addr = m.group("addr")
instr_cnt += 1
else:
# Extract register value information
n = re.search(r" (?P<rd>[a-z0-9]{2,3}?) (?P<pre>[a-f0-9]+?)" \
" -> (?P<val>[a-f0-9]+?)$", line)
if n:
# Write the extracted instruction to a csvcol buffer file
# print("%0s %0s = %0s" % (trace_instr, m.group("rd"), m.group("val")))
rv_instr_trace = RiscvInstructiontTraceEntry()
rv_instr_trace.rd = n.group("rd")
rv_instr_trace.rd_val = n.group("val")
rv_instr_trace.instr_str = trace_instr
rv_instr_trace.binary = trace_bin
rv_instr_trace.addr = trace_addr
trace_csv.write_trace_entry(rv_instr_trace)
print("Processed instruction count : %d" % instr_cnt)
instr_trace = []
# Parse input arguments
parser = argparse.ArgumentParser()
parser.add_argument("--log", type=str, help="Input ovpsim simulation log")
parser.add_argument("--csv", type=str, help="Output trace csv_buf file")
args = parser.parse_args()
# Process ovpsim log
process_ovpsim_sim_log(args.log, args.csv)

View file

@ -0,0 +1,112 @@
"""
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 for RISC-V instruction trace CSV
"""
import csv
class RiscvInstructiontTraceEntry(object):
"""RISC-V instruction trace entry"""
def __init__(self):
self.rd = ""
self.rd_val = ""
self.rs1 = ""
self.rs1_val = ""
self.rs2 = ""
self.rs2_val = ""
self.imm = ""
self.instr_name = ""
self.addr = ""
self.binary = ""
self.instr_str = ""
self.privileged_mode = ""
class RiscvInstructiontTraceCsv(object):
"""RISC-V instruction trace CSV class
This class provides functions to read/write trace CSV
"""
def __init__(self, csv_fd):
self.csv_fd = csv_fd
def start_new_trace(self):
"""Create a CSV file handle for a new trace"""
fields = ["instr", "rd", "rd_val", "rs1", "rs1_val", "rs2", "rs2_val",
"imm", "str", "addr", "binary", "mode"]
self.csv_writer = csv.DictWriter(self.csv_fd, fieldnames=fields)
self.csv_writer.writeheader()
def read_trace(self, trace):
"""Read instruction trace from CSV file"""
csv_reader = csv.DictReader(self.csv_fd)
for row in csv_reader:
new_trace = RiscvInstructiontTraceEntry()
new_trace.rd = row['rd']
new_trace.rd_val = row['rd_val']
new_trace.addr = row['addr']
new_trace.binary = row['binary']
new_trace.instr_str = row['str']
trace.append(new_trace)
def write_trace_entry(self, entry):
"""Write a new trace entry to CSV"""
self.csv_writer.writerow({'str' : entry.instr_str,
'rd' : entry.rd,
'rd_val' : entry.rd_val,
'binary' : entry.binary,
'addr' : entry.addr})
def gpr_to_abi(gpr):
"""Convert a general purpose register to its corresponding abi name"""
switcher = {
"x0" : "zero",
"x1" : "ra",
"x2" : "sp",
"x3" : "gp",
"x4" : "tp",
"x5" : "t0",
"x6" : "t1",
"x7" : "t2",
"x8" : "s0",
"x9" : "s1",
"x10" : "a0",
"x11" : "a1",
"x12" : "a2",
"x13" : "a3",
"x14" : "a4",
"x15" : "a5",
"x16" : "a6",
"x17" : "a7",
"x18" : "s2",
"x19" : "s3",
"x20" : "s4",
"x21" : "s5",
"x22" : "s6",
"x23" : "s7",
"x24" : "s8",
"x25" : "s9",
"x26" : "s10",
"x27" : "s11",
"x28" : "t3",
"x29" : "t4",
"x30" : "t5",
"x31" : "t6"
}
return switcher.get(gpr, "na")

View file

@ -0,0 +1,65 @@
"""
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.
Convert spike sim log to standard riscv instruction trace format
"""
import re
import argparse
from riscv_trace_csv import *
def process_spike_sim_log(spike_log, csv):
"""Process SPIKE simulation log.
Extract instruction and affected register information from spike simulation
log and save to a list.
"""
print("Processing spike log : %s" % spike_log)
instr_cnt = 0
spike_instr = ""
with open(spike_log, "r") as f, open(csv, "w") as csv_fd:
trace_csv = RiscvInstructiontTraceCsv(csv_fd)
trace_csv.start_new_trace()
for line in f:
# Extract instruction infromation
m = re.search(r"core(.*)\) (.*)", line)
if m:
spike_instr = m.group(2)
else:
# Extract register value information
m = re.search(r"(?P<pri>\d) 0x(?P<addr>[a-f0-9]+?) " \
"\((?P<bin>.*?)\) x\s*(?P<reg>\d*?) 0x(?P<val>.*)", line)
if m:
# Write the extracted instruction to a csvcol buffer file
instr_cnt += 1
rv_instr_trace = RiscvInstructiontTraceEntry()
rv_instr_trace.rd = gpr_to_abi("x%0s" % m.group("reg"))
rv_instr_trace.rd_val = m.group("val")
rv_instr_trace.privileged_mode = m.group("pri")
rv_instr_trace.addr = m.group("addr")
rv_instr_trace.binary = m.group("bin")
rv_instr_trace.instr_str = spike_instr
trace_csv.write_trace_entry(rv_instr_trace)
print("Processed instruction count : %d" % instr_cnt)
instr_trace = []
# Parse input arguments
parser = argparse.ArgumentParser()
parser.add_argument("--log", type=str, help="Input spike simulation log")
parser.add_argument("--csv", type=str, help="Output trace csv_buf file")
args = parser.parse_args()
# Process spike log
process_spike_sim_log(args.log, args.csv)

View file

@ -0,0 +1,69 @@
/*
* 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.
*/
`ifndef uvm_object_new
`define uvm_object_new \
function new (string name=""); \
super.new(name); \
endfunction : new
`endif
`ifndef uvm_component_new
`define uvm_component_new \
function new (string name="", uvm_component parent=null); \
super.new(name, parent); \
endfunction : new
`endif
`ifndef gfn
`define gfn get_full_name()
`endif
`ifndef DV_CHECK
`define DV_CHECK(T_, MSG_="", SEV_=error, ID_=`gfn) \
if (!(T_)) begin \
`uvm_``SEV_(ID_, $sformatf("Check failed (%s) %s ", `"T_`", MSG_)); \
end
`endif
`ifndef DV_CHECK_FATAL
`define DV_CHECK_FATAL(T_, MSG_="", ID_=`gfn) \
`DV_CHECK(T_, MSG_, fatal, ID_)
`endif
// Shorthand for common foo.randomize() + fatal check
`ifndef DV_CHECK_RANDOMIZE_FATAL
`define DV_CHECK_RANDOMIZE_FATAL(VAR_, MSG_="Randomization failed!", ID_=`gfn) \
`DV_CHECK_FATAL(VAR_.randomize(), MSG_, ID_)
`endif
// Shorthand for common std::randomize(foo) + fatal check
`ifndef DV_CHECK_STD_RANDOMIZE_FATAL
`define DV_CHECK_STD_RANDOMIZE_FATAL(VAR_, MSG_="Randomization failed!", ID_=`gfn) \
`DV_CHECK_FATAL(std::randomize(VAR_), MSG_, ID_)
`endif
// Shorthand for common foo.randomize() with { } + fatal check
`ifndef DV_CHECK_RANDOMIZE_WITH_FATAL
`define DV_CHECK_RANDOMIZE_WITH_FATAL(VAR_, WITH_C_, MSG_="Randomization failed!", ID_=`gfn) \
`DV_CHECK_FATAL(VAR_.randomize() with {WITH_C_}, MSG_, ID_)
`endif
// Shorthand for common std::randomize(foo) with { } + fatal check
`ifndef DV_CHECK_STD_RANDOMIZE_WITH_FATAL
`define DV_CHECK_STD_RANDOMIZE_WITH_FATAL(VAR_, WITH_C_,MSG_="Randomization failed!",ID_=`gfn) \
`DV_CHECK_FATAL(std::randomize(VAR_) with {WITH_C_}, MSG_, ID_)
`endif

View file

@ -0,0 +1,852 @@
/*
* 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
//
// This is the main class to generate a complete RISC-V program, including the init routine,
// instruction section, data section, stack section, page table, interrupt and exception
// handling etc. Check gen_program() function to see how the program is generated.
//-----------------------------------------------------------------------------------------
class riscv_asm_program_gen extends uvm_object;
riscv_instr_gen_config cfg;
riscv_data_page_gen data_page_gen;
// User mode programs
riscv_instr_sequence main_program;
riscv_instr_sequence sub_program[];
// Program in binary format, stored in the data section, used to inject illegal/HINT instruction
riscv_instr_sequence bin_program;
string instr_binary[$];
// 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
// and MPRV equal 1, kernel program can fetch/load/store from U-mode pages,
// smode_accessible_umode_program is designed for this purpose. There can be other cases that
// instruction can only be fetched from S-mode pages but load/store can access U-mode pages, or
// everything needs to be in S-mode pages.
riscv_instr_sequence smode_accessible_umode_program;
riscv_instr_sequence smode_program;
riscv_instr_sequence smode_ls_umem_program;
riscv_instr_stream directed_instr[];
string instr_stream[$];
riscv_callstack_gen callstack_gen;
riscv_privileged_common_seq privil_seq;
// Directed instruction ratio, occurance per 1000 instructions
int unsigned directed_instr_stream_ratio[string];
riscv_page_table_list#(SATP_MODE) page_table_list;
`uvm_object_utils(riscv_asm_program_gen)
function new (string name = "");
super.new(name);
endfunction
//---------------------------------------------------------------------------------------
// Main function to generate the whole program
//---------------------------------------------------------------------------------------
// This is the main function to generate all sections of the program.
virtual function void gen_program();
string sub_program_name[$];
instr_stream.delete();
// Generate program header
gen_program_header();
// Initialize general purpose registers
init_gpr();
// Create all page tables
create_page_table();
// Setup privileged mode registers and enter target privileged mode
pre_enter_privileged_mode();
// Generate sub program in binary format
// Illegal instruction and hint instruction cannot pass compilation, need to directly generate
// the instruction in binary format and store in data section to skip compilation.
if(cfg.enable_illegal_instruction || cfg.enable_hint_instruction) begin
bin_program = riscv_instr_sequence::type_id::create("bin_program");
bin_program.instr_cnt = cfg.bin_program_instr_cnt;
bin_program.label_name = bin_program.get_name();
bin_program.cfg = cfg;
if (cfg.enable_illegal_instruction) begin
bin_program.illegal_instr_pct = $urandom_range(5, 20);
end
if (cfg.enable_hint_instruction) begin
bin_program.hint_instr_pct = $urandom_range(5, 20);
end
`DV_CHECK_RANDOMIZE_FATAL(bin_program)
bin_program.gen_instr(.is_main_program(0), .enable_hint_instr(cfg.enable_hint_instruction));
bin_program.post_process_instr();
bin_program.generate_binary_stream(instr_binary);
end
// Init section
gen_init_section();
// Generate sub program
if(cfg.num_of_sub_program > 0) begin
sub_program = new[cfg.num_of_sub_program];
foreach(sub_program[i]) begin
sub_program[i] = riscv_instr_sequence::type_id::create($sformatf("sub_%0d",i+1));
sub_program[i].instr_cnt = cfg.sub_program_instr_cnt[i];
generate_directed_instr_stream(.label(sub_program[i].get_name()),
.original_instr_cnt(sub_program[i].instr_cnt),
.min_insert_cnt(0),
.instr_stream(sub_program[i].directed_instr));
sub_program[i].label_name = sub_program[i].get_name();
sub_program[i].cfg = cfg;
`DV_CHECK_RANDOMIZE_FATAL(sub_program[i])
sub_program[i].gen_instr(0);
sub_program_name.push_back(sub_program[i].label_name);
end
end
// Generate main program
main_program = riscv_instr_sequence::type_id::create("main_program");
main_program.instr_cnt = cfg.main_program_instr_cnt;
main_program.label_name = "_main";
generate_directed_instr_stream(.label("main"),
.original_instr_cnt(main_program.instr_cnt),
.min_insert_cnt(1),
.instr_stream(main_program.directed_instr));
main_program.cfg = cfg;
`DV_CHECK_RANDOMIZE_FATAL(main_program)
main_program.gen_instr(1);
// Setup jump instruction among main program and sub programs
if(cfg.num_of_sub_program != 0) begin
callstack_gen = riscv_callstack_gen::type_id::create("callstack_gen");
callstack_gen.init(cfg.num_of_sub_program+1);
`uvm_info(get_full_name(), "Randomizing call stack", UVM_LOW)
if(callstack_gen.randomize()) begin
program_id_t pid;
int idx;
// Insert the jump instruction based on the call stack
foreach(callstack_gen.program_h[i]) begin
foreach(callstack_gen.program_h[i].sub_program_id[j]) begin
idx++;
pid = callstack_gen.program_h[i].sub_program_id[j] - 1;
`uvm_info(get_full_name(), $sformatf(
"Gen jump instr %0d -> sub[%0d] %0d", i, j, pid+1), UVM_HIGH)
if(i == 0)
main_program.insert_jump_instr(sub_program_name[pid], idx);
else
sub_program[i-1].insert_jump_instr(sub_program_name[pid], idx);
end
end
end else begin
`uvm_fatal(get_full_name(), "Failed to generate callstack")
end
end
if (bin_program != null) begin
main_program.insert_jump_instr("sub_bin", 0);
end
main_program.post_process_instr();
main_program.generate_instr_stream();
instr_stream = {instr_stream, main_program.instr_string_list};
// Test done section
gen_test_done();
// Shuffle the sub programs and insert to the instruction stream
sub_program.shuffle();
foreach(sub_program[i]) begin
sub_program[i].post_process_instr();
sub_program[i].generate_instr_stream();
instr_stream = {instr_stream, sub_program[i].instr_string_list};
end
// Reserve some space to copy instruction from data section
if (instr_binary.size() > 0) begin
instr_stream.push_back(".align 2");
instr_stream.push_back("sub_bin:");
instr_stream.push_back({indent, $sformatf(".rept %0d", 2 * instr_binary.size())});
instr_stream.push_back({indent, "nop"});
instr_stream.push_back({indent, ".endr"});
instr_stream.push_back({indent, "ret"});
end
// Privileged mode switch routine
gen_privileged_mode_switch_routine();
// Program end
gen_program_end();
gen_data_page_begin();
// Generate the sub program in binary format
gen_bin_program();
// Page table
gen_page_table_section();
if(!cfg.no_data_page) begin
// Data section
gen_data_page();
end
gen_data_page_end();
// Stack section
gen_stack_section();
// Generate kernel program/data/stack section
gen_kernel_sections();
endfunction
//---------------------------------------------------------------------------------------
// Generate kernel program/data/stack sections
//---------------------------------------------------------------------------------------
virtual function void gen_kernel_sections();
instr_stream.push_back("_kernel_start: .align 12");
// Kernel programs
smode_accessible_umode_program = riscv_instr_sequence::type_id::
create("smode_accessible_umode_program");
smode_program = riscv_instr_sequence::type_id::create("smode_program");
gen_kernel_program(smode_accessible_umode_program);
gen_kernel_program(smode_program);
smode_ls_umem_program = riscv_instr_sequence::type_id::create("smode_ls_umem_program");
gen_kernel_program(smode_ls_umem_program);
// All trap/interrupt handling is in the kernel region
// Trap/interrupt delegation to user mode is not supported now
// Trap handler
gen_all_trap_handler();
// Interrupt handling subroutine
foreach(riscv_instr_pkg::supported_privileged_mode[i]) begin
gen_interrupt_handler_section(riscv_instr_pkg::supported_privileged_mode[i]);
end
// Kernel data pages
gen_kernel_data_page_begin();
if(!cfg.no_data_page) begin
// Data section
gen_data_page(.is_kernel(1'b1));
end
gen_data_page_end();
// Kernel stack section
gen_kernel_stack_section();
instr_stream.push_back("_kernel_end: nop");
endfunction
virtual function void gen_kernel_program(riscv_instr_sequence seq);
seq.instr_cnt = riscv_instr_pkg::kernel_program_instr_cnt;
generate_directed_instr_stream(.label(seq.get_name()),
.original_instr_cnt(seq.instr_cnt),
.min_insert_cnt(0),
.instr_stream(seq.directed_instr),
.access_u_mode_mem(1'b0));
seq.label_name = seq.get_name();
seq.cfg = cfg;
`DV_CHECK_RANDOMIZE_FATAL(seq)
seq.gen_instr(0);
seq.post_process_instr();
seq.generate_instr_stream();
instr_stream = {instr_stream, seq.instr_string_list};
endfunction
//---------------------------------------------------------------------------------------
// Major sections - init, stack, data, test_done etc.
//---------------------------------------------------------------------------------------
virtual function void gen_program_header();
// ------------- IBEX modification start --------------------
// The ibex core load the program from 0x80
// Some address is reserved for hardware interrupt handling, need to decide if we need to copy
// the init program from crt0.S later.
instr_stream.push_back(".macro init");
instr_stream.push_back(".endm");
instr_stream.push_back(".section .text.init");
instr_stream.push_back(".globl _start");
instr_stream.push_back("j _start");
// Align the start section to 0x80
instr_stream.push_back(".align 7");
instr_stream.push_back("_start:");
// ------------- IBEX modification end --------------------
endfunction
virtual function void gen_program_end();
// Use write_tohost to terminate spike simulation
gen_section("write_tohost", {"sw gp, tohost, t5"});
gen_section("_exit", {"j write_tohost"});
endfunction
virtual function void gen_data_page_begin();
instr_stream.push_back(".data");
instr_stream.push_back(".pushsection .tohost,\"aw\",@progbits;");
instr_stream.push_back(".align 6; .global tohost; tohost: .dword 0;");
instr_stream.push_back(".align 6; .global fromhost; fromhost: .dword 0;");
instr_stream.push_back(".popsection;");
instr_stream.push_back(".align 4;");
endfunction
virtual function void gen_kernel_data_page_begin();
instr_stream.push_back(".data");
instr_stream.push_back(".align 4;");
endfunction
virtual function void gen_data_page(bit is_kernel = 1'b0);
string data_page;
data_page_gen = riscv_data_page_gen::type_id::create("data_page_gen");
data_page_gen.cfg = cfg;
data_page_gen.gen_data_page(cfg.data_page_pattern, is_kernel);
instr_stream = {instr_stream, data_page_gen.data_page_str};
endfunction
virtual function void gen_data_page_end();
instr_stream.push_back(".align 4;");
endfunction
// Generate the user stack section
virtual function void gen_stack_section();
instr_stream.push_back($sformatf(".align %0d", $clog2(XLEN)));
instr_stream.push_back("_user_stack_start:");
instr_stream.push_back($sformatf(".rept %0d", riscv_instr_pkg::stack_len - 1));
instr_stream.push_back($sformatf(".%0dbyte 0x0", XLEN/8));
instr_stream.push_back(".endr");
instr_stream.push_back("_user_stack_end:");
instr_stream.push_back($sformatf(".%0dbyte 0x0", XLEN/8));
endfunction
// The kernal stack is used to save user program context before executing exception handling
virtual function void gen_kernel_stack_section();
instr_stream.push_back($sformatf(".align %0d", $clog2(XLEN)));
instr_stream.push_back("_kernel_stack_start:");
instr_stream.push_back($sformatf(".rept %0d", riscv_instr_pkg::kernel_stack_len - 1));
instr_stream.push_back($sformatf(".%0dbyte 0x0", XLEN/8));
instr_stream.push_back(".endr");
instr_stream.push_back("_kernel_stack_end:");
instr_stream.push_back($sformatf(".%0dbyte 0x0", XLEN/8));
endfunction
virtual function void gen_init_section();
string str;
str = format_string("_init:", LABEL_STR_LEN);
instr_stream.push_back(str);
// Init stack pointer to point to the end of the user stack
str = {indent, "la sp, _user_stack_end"};
instr_stream.push_back(str);
// Copy the instruction from data section to instruction section
if (instr_binary.size() > 0) begin
instr_stream.push_back({indent, "la x31, instr_bin"});
instr_stream.push_back({indent, "la x30, sub_bin"});
instr_stream.push_back({indent, $sformatf(".rept %0d", instr_binary.size())});
instr_stream.push_back({indent, "lw x29, 0(x31)"});
instr_stream.push_back({indent, "sw x29, 0(x30)"});
instr_stream.push_back({indent, "addi x31, x31, 4"});
instr_stream.push_back({indent, "addi x30, x30, 4"});
instr_stream.push_back({indent, ".endr"});
end
endfunction
virtual function void init_gpr();
string str;
bit [DATA_WIDTH-1:0] reg_val;
// Init general purpose registers with random values
for(int i = 0; i < 32; i++) begin
`DV_CHECK_STD_RANDOMIZE_WITH_FATAL(reg_val,
reg_val dist {
'h0 :/ 1,
'h8000_0000 :/ 1,
['h1 : 'hF] :/ 1,
['h10 : 'hEFFF_FFFF] :/ 1,
['hF000_0000 : 'hFFFF_FFFF] :/ 1
};)
str = $sformatf("%0sli x%0d, 0x%0x", indent, i, reg_val);
instr_stream.push_back(str);
end
endfunction
// Generate "test_done" section, test is finished by an ECALL instruction
// The ECALL trap handler will handle the clean up procedure before finishing the test.
virtual function void gen_test_done();
string str = format_string("test_done:", LABEL_STR_LEN);
instr_stream.push_back(str);
instr_stream.push_back({indent, "li gp, 1"});
instr_stream.push_back({indent, "ecall"});
endfunction
// Dump all GPR to the starting point of the program
// TB can check the GPR value for this memory location to compare with expected value generated
// by the ISA simulator. If the processor doesn't have a good tracer unit, it might not be
// possible to compare the GPR value after each instruction execution.
virtual function void gen_register_dump();
string str;
riscv_reg_t base_address_reg;
`DV_CHECK_STD_RANDOMIZE_WITH_FATAL(base_address_reg, base_address_reg != ZERO;)
// Load base address
str = {indent, $sformatf("la x%0d, _start", base_address_reg)};
instr_stream.push_back(str);
// Generate sw/sd instructions
for(int i = 0; i < 32; i++) begin
if(XLEN == 64) begin
str = {indent, $sformatf("sd x%0d, %0d(x%0d)", i, i*(XLEN/8), base_address_reg)};
end else begin
str = {indent, $sformatf("sw x%0d, %0d(x%0d)", i, i*(XLEN/8), base_address_reg)};
end
instr_stream.push_back(str);
end
endfunction
//---------------------------------------------------------------------------------------
// Privileged mode entering routine
//---------------------------------------------------------------------------------------
virtual function void pre_enter_privileged_mode();
string instr[];
// Setup kerenal stack pointer
gen_section("kernel_sp", {"la tp, _kernel_stack_end"});
// Setup interrupt and exception delegation
if(!cfg.no_delegation && (cfg.init_privileged_mode != MACHINE_MODE)) begin
gen_delegation();
end
// Setup trap vector register
trap_vector_init();
// Initialize PTE (link page table based on their real physical address)
if((SATP_MODE != BARE) && (cfg.init_privileged_mode != MACHINE_MODE)) begin
page_table_list.process_page_table(instr);
gen_section("process_pt", instr);
end
// Setup mepc register, jump to init entry
setup_epc();
endfunction
virtual function void gen_privileged_mode_switch_routine();
privil_seq = riscv_privileged_common_seq::type_id::create("privil_seq");
foreach(riscv_instr_pkg::supported_privileged_mode[i]) begin
string instr[$];
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
privil_seq.cfg = cfg;
`DV_CHECK_RANDOMIZE_FATAL(privil_seq)
privil_seq.enter_privileged_mode(riscv_instr_pkg::supported_privileged_mode[i], instr);
instr_stream = {instr_stream, instr};
end
endfunction
// Setup EPC before entering target privileged mode
virtual function void setup_epc();
string instr[];
string mode_name;
instr = {"la x10, _init"};
if(SATP_MODE != BARE && cfg.init_privileged_mode != MACHINE_MODE) begin
// For supervisor and user mode, use virtual address instead of physical address.
// Virtual address starts from address 0x0, here only the lower 12 bits are kept
// as virtual address offset.
instr = {instr,
$sformatf("slli x10, x10, %0d", XLEN - 12),
$sformatf("srli x10, x10, %0d", XLEN - 12)};
end
mode_name = cfg.init_privileged_mode.name();
instr = {instr,
"csrw mepc, x10",
$sformatf("j init_%0s", mode_name.tolower())
};
gen_section("mepc_setup", instr);
endfunction
//---------------------------------------------------------------------------------------
// Privileged CSR setup for interrupt and exception handling and delegation
//---------------------------------------------------------------------------------------
// Interrupt and exception delegation setting.
// The lower level exception and interrupt can be delegated to higher level handler.
virtual function void gen_delegation();
gen_delegation_instr(MEDELEG, MIDELEG,
cfg.m_mode_exception_delegation,
cfg.m_mode_interrupt_delegation);
if(riscv_instr_pkg::support_umode_trap) begin
gen_delegation_instr(SEDELEG, SIDELEG,
cfg.s_mode_exception_delegation,
cfg.s_mode_interrupt_delegation);
end
endfunction
virtual function void gen_delegation_instr(privileged_reg_t edeleg,
privileged_reg_t ideleg,
bit edeleg_enable[exception_cause_t],
bit ideleg_enable[interrupt_cause_t]);
bit [XLEN-1:0] deleg_val;
string section_name;
string instr[];
// Exception delegation setup
foreach(edeleg_enable[cause]) begin
if(edeleg_enable[cause]) begin
deleg_val = deleg_val | (1 << int'(cause));
end
end
instr = {$sformatf("li a0, 0x%0x", deleg_val),
$sformatf("csrw 0x%0x, a0 # %0s", edeleg, edeleg.name())};
// Interrupt delegation setup
deleg_val = '0;
foreach(ideleg_enable[cause]) begin
if(ideleg_enable[cause]) begin
deleg_val = deleg_val | (1 << int'(cause));
end
end
instr = {instr,
$sformatf("li a0, 0x%0x", deleg_val),
$sformatf("csrw 0x%0x, a0 # %0s", ideleg, ideleg.name())};
section_name = edeleg.name();
section_name = section_name.tolower();
gen_section($sformatf("%0s_setup", section_name), instr);
endfunction
// Setup trap vector - MTVEC, STVEC, UTVEC
virtual function void trap_vector_init();
string instr[];
privileged_reg_t trap_vec_reg;
string tvec_name;
foreach(riscv_instr_pkg::supported_privileged_mode[i]) begin
case(riscv_instr_pkg::supported_privileged_mode[i])
MACHINE_MODE: trap_vec_reg = MTVEC;
SUPERVISOR_MODE: trap_vec_reg = STVEC;
USER_MODE: trap_vec_reg = UTVEC;
endcase
// Skip utvec init if trap delegation to u_mode is not supported
// TODO: For now the default mode is direct mode, needs to support vector mode
if((riscv_instr_pkg::supported_privileged_mode[i] == USER_MODE) && !riscv_instr_pkg::support_umode_trap) continue;
if(riscv_instr_pkg::supported_privileged_mode[i] < cfg.init_privileged_mode) continue;
tvec_name = trap_vec_reg.name();
instr = {instr, $sformatf("la a0, %0s_handler", tvec_name.tolower())};
if(SATP_MODE != BARE && riscv_instr_pkg::supported_privileged_mode[i] != MACHINE_MODE) begin
// For supervisor and user mode, use virtual address instead of physical address.
// Virtual address starts from address 0x0, here only the lower 20 bits are kept
// as virtual address offset.
instr = {instr,
$sformatf("slli a0, a0, %0d", XLEN - 20),
$sformatf("srli a0, a0, %0d", XLEN - 20)};
end
instr = {instr, $sformatf("csrw 0x%0x, a0 # %0s", trap_vec_reg, trap_vec_reg.name())};
end
gen_section("trap_vec_init", instr);
endfunction
//---------------------------------------------------------------------------------------
// Exception handling routine
//---------------------------------------------------------------------------------------
// Trap handling routine
virtual function void gen_all_trap_handler();
string instr[$];
foreach(riscv_instr_pkg::supported_privileged_mode[i]) begin
if(riscv_instr_pkg::supported_privileged_mode[i] < cfg.init_privileged_mode) continue;
case(riscv_instr_pkg::supported_privileged_mode[i])
MACHINE_MODE:
gen_trap_handler_section("m", MCAUSE, MTVEC, MTVAL, MEPC, MSCRATCH, MSTATUS);
SUPERVISOR_MODE:
gen_trap_handler_section("s", SCAUSE, STVEC, STVAL, SEPC, SSCRATCH, SSTATUS);
USER_MODE:
if(riscv_instr_pkg::support_umode_trap)
gen_trap_handler_section("u", UCAUSE, UTVEC, UTVAL, UEPC, USCRATCH, USTATUS);
endcase
end
// Ecall handler
gen_ecall_handler();
// Illegal instruction handler
gen_illegal_instr_handler();
// Generate page table fault handling routine
// Page table fault is always handled in machine mode, as virtual address translation may be
// broken when page fault happens.
if(page_table_list != null) begin
page_table_list.gen_page_fault_handling_routine(instr);
end else begin
instr = {"nop"};
end
gen_section("pt_fault_handler", instr);
endfunction
// Generate the interrupt and trap handler for different privileged mode.
// The trap handler checks the xCAUSE to determine the type of the exception and jumps to
// corresponding exeception handling routine.
virtual function void gen_trap_handler_section(string mode,
privileged_reg_t cause, privileged_reg_t tvec,
privileged_reg_t tval, privileged_reg_t epc,
privileged_reg_t scratch, privileged_reg_t status);
bit is_interrupt = 'b1;
string tvec_name;
string instr[$];
// Push user mode GPR to kernel stack before executing exception handling, this is to avoid
// exception handling routine modify user program state unexpectedly
push_gpr_to_kernel_stack(status, scratch, cfg.mstatus_mprv, instr);
instr = {instr,
// Use scratch CSR to save a GPR value
// Check if the exception is caused by an interrupt, if yes, jump to interrupt handler
// Interrupt is indicated by xCause[XLEN-1]
$sformatf("csrr a1, 0x%0x # %0s", cause, cause.name()),
$sformatf("srli a1, a1, %0d", XLEN-1),
$sformatf("bne a1, x0, %0smode_intr_handler", mode),
// The trap is caused by an exception, read back xCAUSE, xEPC, xSTATUS to see if these
// CSR values are set properly. The checking is done by comparing against the log
// generated by ISA simulator (spike).
$sformatf("csrr a1, 0x%0x # %0s", cause, cause.name()),
$sformatf("csrr x31, 0x%0x # %0s", epc, epc.name()),
$sformatf("csrr x29, 0x%0x # %0s", status, status.name()),
// Check if it's an ECALL exception. Jump to ECALL exception handler
$sformatf("li a2, 0x%0x # ECALL_UMODE", ECALL_UMODE),
"beq a1, a2, ecall_handler",
$sformatf("li a2, 0x%0x # ECALL_SMODE", ECALL_SMODE),
"beq a1, a2, ecall_handler",
$sformatf("li a2, 0x%0x # ECALL_MMODE", ECALL_MMODE),
"beq a1, a2, ecall_handler",
// Page table fault or access fault conditions
$sformatf("li a2, 0x%0x", INSTRUCTION_ACCESS_FAULT),
"beq a1, a2, pt_fault_handler",
$sformatf("li a2, 0x%0x", LOAD_ACCESS_FAULT),
"beq a1, a2, pt_fault_handler",
$sformatf("li a2, 0x%0x", STORE_AMO_ACCESS_FAULT),
"beq a1, a2, pt_fault_handler",
$sformatf("li a2, 0x%0x", INSTRUCTION_PAGE_FAULT),
"beq a1, a2, pt_fault_handler",
$sformatf("li a2, 0x%0x", LOAD_PAGE_FAULT),
"beq a1, a2, pt_fault_handler",
$sformatf("li a2, 0x%0x", STORE_AMO_PAGE_FAULT),
"beq a1, a2, pt_fault_handler",
// Illegal instruction exception
$sformatf("li a2, 0x%0x # ILLEGAL_INSTRUCTION", ILLEGAL_INSTRUCTION),
"beq a1, a2, illegal_instr_handler",
// Skip checking tval for illegal instruction as it's implementation specific
$sformatf("csrr x30, 0x%0x # %0s", tval, tval.name()),
"1: jal x1, test_done "
};
// The trap handler will occupy one 4KB page, it will be allocated one entry in the page table
// with a specific privileged mode.
instr_stream.push_back(".align 12");
tvec_name = tvec.name();
gen_section($sformatf("%0s_handler", tvec_name.tolower()), instr);
endfunction
// ECALL trap handler
// It does some clean up like dump GPRs before communicating with host to terminate the test.
// User can extend this function if some custom clean up routine is needed.
virtual function void gen_ecall_handler();
string str;
str = format_string("ecall_handler:", LABEL_STR_LEN);
instr_stream.push_back(str);
dump_perf_stats();
gen_register_dump();
str = format_string(" ", LABEL_STR_LEN);
str = {str, "j write_tohost"};
instr_stream.push_back(str);
endfunction
// Illegal instruction handler
// Note: Save the illegal instruction to MTVAL is optional in the spec, and mepc could be
// a virtual address that cannot be used in machine mode handler. As a result, there's no way to
// know the illegal instruction is compressed or not. This hanlder just simply adds the PC by
// 4 and resumes execution. The way that the illegal instruction is injected guarantees that
// PC + 4 is a valid instruction boundary.
virtual function void gen_illegal_instr_handler();
string str;
string instr[$] = {
"csrr x31, mepc",
"addi x31, x31, 4",
"csrw mepc, x31"
};
pop_gpr_from_kernel_stack(MSTATUS, MSCRATCH, cfg.mstatus_mprv, instr);
instr.push_back("mret");
gen_section("illegal_instr_handler", instr);
endfunction
//---------------------------------------------------------------------------------------
// Page table setup
//---------------------------------------------------------------------------------------
// Create page table if virtual address translation is supported.
// The page is created based on the address translation mode - SV32, SV39, SV48
// Right now only the lowest level 4KB page table is configured as leaf page table entry (PTE),
// all the other super pages are link PTE.
virtual function void create_page_table();
string instr[];
if((SATP_MODE != BARE) && (cfg.init_privileged_mode != MACHINE_MODE)) begin
page_table_list = riscv_page_table_list#(SATP_MODE)::
type_id::create("page_table_list");
page_table_list.cfg = cfg;
page_table_list.create_page_table_list();
page_table_list.enable_exception = cfg.enable_page_table_exception;
`uvm_info(`gfn, $sformatf("Randomizing page tables, totally %0d page tables, mode = %0s",
page_table_list.page_table.size(), cfg.init_privileged_mode.name()), UVM_LOW)
page_table_list.privileged_mode = cfg.init_privileged_mode;
`DV_CHECK_RANDOMIZE_FATAL(page_table_list);
page_table_list.randomize_page_table();
`uvm_info(`gfn, "Finished creating page tables", UVM_LOW)
end
endfunction
// Generate the page table section of the program
// The page table is generated as a group of continuous 4KB data sections.
virtual function void gen_page_table_section();
string page_table_section[$];
if(page_table_list != null) begin
foreach(page_table_list.page_table[i]) begin
page_table_list.page_table[i].gen_page_table_section(page_table_section);
instr_stream = {instr_stream, page_table_section};
end
end
endfunction
// Only extend this function if the core utilizes a PLIC for handling interrupts
// In this case, the core will write to a specific location as the response to the interrupt, and
// external PLIC unit can detect this response and process the interrupt clean up accordingly.
virtual function void gen_plic_section(ref string interrupt_handler_instr[$]);
endfunction
// Interrupt handler routine
virtual function void gen_interrupt_handler_section(privileged_mode_t mode);
string mode_prefix;
string ls_unit;
privileged_reg_t status, ip, ie, scratch;
string interrupt_handler_instr[$];
ls_unit = (XLEN == 32) ? "w" : "d";
if(mode < cfg.init_privileged_mode) return;
case(mode)
MACHINE_MODE: begin
mode_prefix = "m";
status = MSTATUS;
ip = MIP;
ie = MIE;
scratch = MSCRATCH;
end
SUPERVISOR_MODE: begin
mode_prefix = "s";
status = SSTATUS;
ip = SIP;
ie = SIE;
scratch = SSCRATCH;
end
USER_MODE: begin
mode_prefix = "u";
status = USTATUS;
ip = UIP;
ie = UIE;
scratch = USCRATCH;
end
default: `uvm_fatal(get_full_name(), $sformatf("Unsupported mode: %0s", mode.name()))
endcase
// Read back interrupt related privileged CSR
// The value of these CSR are checked by comparing with spike simulation result.
interrupt_handler_instr = {
interrupt_handler_instr,
$sformatf("csrr a1, 0x%0x # %0s;", status, status.name()),
$sformatf("csrr a1, 0x%0x # %0s;", ie, ie.name()),
$sformatf("csrr a1, 0x%0x # %0s;", ip, ip.name()),
// Clean all the pending interrupt
$sformatf("csrrc a1, 0x%0x, a1 # %0s;", ip, ip.name())
};
gen_plic_section(interrupt_handler_instr);
// Restore user mode GPR value from kernel stack before return
pop_gpr_from_kernel_stack(status, scratch, cfg.mstatus_mprv, interrupt_handler_instr);
interrupt_handler_instr = {interrupt_handler_instr,
$sformatf("%0sret;", mode_prefix)
};
// The interrupt handler will use one 4KB page
instr_stream.push_back(".align 12");
gen_section($sformatf("%0smode_intr_handler", mode_prefix), interrupt_handler_instr);
endfunction
//---------------------------------------------------------------------------------------
// Helper functions
//---------------------------------------------------------------------------------------
// Generate a code section
virtual function void gen_section(string label, string instr[$]);
string str;
if(label != "") begin
str = format_string($sformatf("%0s:", label), LABEL_STR_LEN);
instr_stream.push_back(str);
end
foreach(instr[i]) begin
str = {indent, instr[i]};
instr_stream.push_back(str);
end
instr_stream.push_back("");
endfunction
// Dump performance CSRs if applicable
virtual function void dump_perf_stats();
endfunction
// Write the generated program to a file
function void gen_test_file(string test_name);
int fd;
fd = $fopen(test_name,"w");
foreach(instr_stream[i]) begin
$fwrite(fd, {instr_stream[i],"\n"});
end
$fclose(fd);
`uvm_info(get_full_name(), $sformatf("%0s is generated", test_name), UVM_LOW)
endfunction
//---------------------------------------------------------------------------------------
// Inject directed instruction stream
//---------------------------------------------------------------------------------------
virtual function void add_directed_instr_stream(string name, int unsigned ratio);
directed_instr_stream_ratio[name] = ratio;
endfunction
// Generate directed instruction stream based on the ratio setting
virtual function void generate_directed_instr_stream(input string label,
input int unsigned original_instr_cnt,
input int unsigned min_insert_cnt = 0,
input bit access_u_mode_mem = 1,
output riscv_instr_stream instr_stream[]);
uvm_object object_h;
riscv_rand_instr_stream new_instr_stream;
int unsigned instr_insert_cnt;
int unsigned idx;
uvm_coreservice_t coreservice = uvm_coreservice_t::get();
uvm_factory factory = coreservice.get_factory();
if(cfg.no_directed_instr) return;
foreach(directed_instr_stream_ratio[instr_stream_name]) begin
instr_insert_cnt = original_instr_cnt * directed_instr_stream_ratio[instr_stream_name] / 1000;
if(instr_insert_cnt <= min_insert_cnt) begin
instr_insert_cnt = min_insert_cnt;
end
`uvm_info(get_full_name(), $sformatf("Insert directed instr stream %0s %0d/%0d times",
instr_stream_name, instr_insert_cnt, original_instr_cnt), UVM_LOW)
for(int i = 0; i < instr_insert_cnt; i++) begin
string name = $sformatf("%0s_%0d", instr_stream_name, i);
object_h = factory.create_object_by_name(instr_stream_name, get_full_name(), name);
if(object_h == null) begin
`uvm_fatal(get_full_name(), $sformatf("Cannot create instr stream %0s", name))
end
if($cast(new_instr_stream, object_h)) begin
new_instr_stream.cfg = cfg;
new_instr_stream.label = $sformatf("%0s_instr_%0d", label, idx);
new_instr_stream.access_u_mode_mem = access_u_mode_mem;
`DV_CHECK_RANDOMIZE_FATAL(new_instr_stream)
instr_stream = {instr_stream, new_instr_stream};
end else begin
`uvm_fatal(get_full_name(), $sformatf("Cannot cast instr stream %0s", name))
end
idx++;
end
end
instr_stream.shuffle();
endfunction
// Generate sub-program in binary format, this is needed for illegal and HINT instruction
function void gen_bin_program();
if (bin_program != null) begin
string str;
instr_stream.push_back("instr_bin:");
instr_stream.push_back(".align 12");
foreach (instr_binary[i]) begin
if (((i+1) % 8 == 0) || (i == instr_binary.size() - 1)) begin
if (str != "")
instr_stream.push_back($sformatf(".word %0s, %0s", str, instr_binary[i]));
else
instr_stream.push_back($sformatf(".word %0s", instr_binary[i]));
str = "";
end else begin
if (str != "") begin
str = {str, ", ", instr_binary[i]};
end else begin
str = instr_binary[i];
end
end
end
end
endfunction
endclass

View file

@ -0,0 +1,182 @@
/*
* 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 abstract program class
//
// This class is used to model a node in a callstack tree, no actual instruction is included
// in this class.
//-----------------------------------------------------------------------------------------
class riscv_program extends uvm_object;
// ID of the current program
rand program_id_t program_id;
// The level of current program in the call stack tree
rand int unsigned call_stack_level;
// The list of all sub-programs of the current program
rand program_id_t sub_program_id[];
constraint legal_c {
unique{sub_program_id};
foreach(sub_program_id[i]) {
// Cannot call itself, recursive function call is not supported
sub_program_id[i] != program_id;
}
}
`uvm_object_utils(riscv_program)
function new (string name = "");
super.new(name);
endfunction
function string convert2string();
string str = $sformatf("PID[%0d] Lv[%0d] :", program_id, call_stack_level);
foreach(sub_program_id[i])
str = $sformatf("%0s %0d", str, sub_program_id[i]);
return str;
endfunction
endclass
//-----------------------------------------------------------------------------------------
// RISC-V assembly program call stack generator
//
// The call stack is generated as a tree structure to avoid dead call loop.
// Level 0: P0
// / | \
// Level 1: P1 P2 P3
// | \/ \
// Level 2: P4 P5 P6
// |
// Level 3: P7
//
// Rules: A program can only call the program in the next level.
// A program can be called many times by other upper level programs.
// A program can call the same lower level programs multiple times.
//-----------------------------------------------------------------------------------------
class riscv_callstack_gen extends uvm_object;
// Number of programs in the call stack
int program_cnt = 10;
// Handles of all programs
riscv_program program_h[];
// Maximum call stack level
int max_stack_level = 50;
// Call stack level of each program
rand bit[10:0] stack_level[];
constraint program_stack_level_c {
stack_level.size() == program_cnt;
// The stack level is assigned in ascending order to avoid call loop
stack_level[0] == 0;
foreach(stack_level[i]) {
if(i > 0) {
stack_level[i] inside {[1:program_cnt-1]};
stack_level[i] >= stack_level[i-1];
stack_level[i] <= stack_level[i-1]+1;
stack_level[i] <= max_stack_level;
}
}
}
`uvm_object_utils(riscv_callstack_gen)
function new (string name = "");
super.new(name);
endfunction
// Init all program instances before randomization
function void init(int program_cnt);
this.program_cnt = program_cnt;
program_h = new[program_cnt];
foreach(program_h[i])
program_h[i] = riscv_program::type_id::create($sformatf("program_%0d", i));
endfunction
// In the randomiation stage, only the stack level of each program is specified. The call stack
// generation process is to build the call relationship between different programs. This is
// implemented with post randomize rather than constraints for performance considerations.
// Solving a complex call stack with SV constraint could take considerable time for the solver.
function void post_randomize();
int last_level = stack_level[program_cnt-1];
foreach(program_h[i]) begin
program_h[i].program_id = i;
program_h[i].call_stack_level = stack_level[i];
end
// Top-down generate the entire call stack.
// A program can only call the programs in the next level.
for(int i = 0; i < last_level; i ++) begin
int total_sub_program_cnt;
int program_list[$];
int next_program_list[$];
int sub_program_id_pool[];
int sub_program_cnt[];
int idx;
program_list = stack_level.find_index() with (item == i);
next_program_list = stack_level.find_index() with (item == i+1);
// Randmly duplicate some sub programs in the pool to create a case that
// one sub program is called by multiple caller. Also it's possible to call
// the same sub program in one program multiple times.
total_sub_program_cnt = $urandom_range(next_program_list.size(),
next_program_list.size() + 1);
sub_program_id_pool = new[total_sub_program_cnt];
`DV_CHECK_STD_RANDOMIZE_WITH_FATAL(sub_program_id_pool,
foreach(sub_program_id_pool[j]) {
if(j < next_program_list.size()) {
sub_program_id_pool[j] == next_program_list[j];
} else {
sub_program_id_pool[j] inside {next_program_list};
}
})
sub_program_id_pool.shuffle();
sub_program_cnt = new[program_list.size()];
`uvm_info(get_full_name(), $sformatf("%0d programs @Lv%0d-> %0d programs at next level",
program_list.size(), i, sub_program_id_pool.size()), UVM_HIGH)
// Distribute the programs of the next level among the programs of current level
// Make sure all program has a caller so that no program is obsolete.
`DV_CHECK_STD_RANDOMIZE_WITH_FATAL(sub_program_cnt,
sub_program_cnt.sum() == sub_program_id_pool.size();
foreach(sub_program_cnt[j]) {
sub_program_cnt[j] inside {[0: sub_program_id_pool.size()]};
})
foreach(program_list[j]) begin
int id = program_list[j];
program_h[id].sub_program_id = new[sub_program_cnt[j]];
`uvm_info(get_full_name(), $sformatf("%0d sub programs are assigned to program[%0d]",
sub_program_cnt[j], id), UVM_HIGH)
foreach(program_h[id].sub_program_id[k]) begin
program_h[id].sub_program_id[k] = sub_program_id_pool[idx];
idx++;
end
end
end
endfunction
function void print_call_stack(program_id_t i, string str);
if(program_h[i].sub_program_id.size() == 0)
`uvm_info(get_full_name(), str, UVM_LOW)
else begin
foreach(program_h[i].sub_program_id[j]) begin
print_call_stack(program_h[i].sub_program_id[j],
$sformatf("%0s -> %0d", str, program_h[i].sub_program_id[j]));
end
end
endfunction
endclass

View file

@ -0,0 +1,77 @@
/*
* 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.
*/
//-----------------------------------------------------------------------------
// Processor feature configuration
//-----------------------------------------------------------------------------
// XLEN
parameter int XLEN = 32;
// Parameter for SATP mode, set to BARE if address translation is not supported
parameter satp_mode_t SATP_MODE = BARE;
// Supported Privileged mode
privileged_mode_t supported_privileged_mode[] = {MACHINE_MODE};
// Unsupported instructions
// Avoid generating these instructions in regular regression
// FENCE.I is intentionally treated as illegal instruction by ibex core
riscv_instr_name_t unsupported_instr[] = {FENCEI};
// ISA supported by the processor
riscv_instr_group_t supported_isa[$] = {RV32I, RV32M, RV32C};
// Support delegate trap to user mode
bit support_umode_trap = 0;
// Support sfence.vma instruction
bit support_sfence = 0;
// Cache line size (in bytes)
// If processor does not support caches, set to XLEN/8
int dcache_line_size_in_bytes = 128;
// Number of data section
// For processor that doesn't have data TLB, this can be set to 1
// For processor that supports data TLB, this should be set to be larger than the number
// of entries of dTLB to cover dTLB hit/miss scenario
int num_of_data_pages = 4;
// Data section byte size
// For processor with no dTLB and data cache, keep the value below 10K
// For processor with dTLB support, set it to the physical memory size that covers one entry
// of the dTLB
int data_page_size = 4096;
int data_page_alignment = $clog2(data_page_size);
// Stack section word length
int stack_len = 5000;
//-----------------------------------------------------------------------------
// Kernel section setting, used by supervisor mode programs
//-----------------------------------------------------------------------------
// Number of kernel data pages
int num_of_kernel_data_pages = 2;
// Byte size of kernel data pages
int kernel_data_page_size = 4096;
// Kernel Stack section word length
int kernel_stack_len = 5000;
// Number of instructions for each kernel program
int kernel_program_instr_cnt = 400;

View file

@ -0,0 +1,77 @@
/*
* 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 assmebly program data section generator
// There can be user mode and supervisor(kernel) mode data pages
//-----------------------------------------------------------------------------------------
class riscv_data_page_gen extends uvm_object;
riscv_instr_gen_config cfg;
string data_page_str[$];
`uvm_object_utils(riscv_data_page_gen)
function new (string name = "");
super.new(name);
endfunction
// The data section can be initialized with different data pattern:
// - Random value, incremental value, all zeros
virtual function void gen_data(input int idx,
input data_pattern_t pattern,
input int unsigned num_of_bytes,
output bit [7:0] data[]);
bit [7:0] temp_data;
data = new[num_of_bytes];
foreach(data[i]) begin
if(pattern == RAND_DATA) begin
`DV_CHECK_STD_RANDOMIZE_FATAL(temp_data)
data[i] = temp_data;
end else if (pattern == INCR_VAL) begin
data[i] = (idx + i) % 256;
end
end
endfunction
// Generate the assembly code for the data section
function void gen_data_page(data_pattern_t pattern, bit is_kernel = 1'b0);
string tmp_str;
bit [7:0] tmp_data[];
int page_cnt;
int page_size;
data_page_str = {};
page_cnt = is_kernel ? riscv_instr_pkg::num_of_kernel_data_pages :
riscv_instr_pkg::num_of_data_pages;
page_size = is_kernel ? riscv_instr_pkg::kernel_data_page_size :
riscv_instr_pkg::data_page_size;
for(int section_idx = 0; section_idx < page_cnt; section_idx++) begin
if(is_kernel) begin
data_page_str.push_back($sformatf("kernel_data_page_%0d:", section_idx));
end else begin
data_page_str.push_back($sformatf("data_page_%0d:", section_idx));
end
data_page_str.push_back($sformatf(".align %0d", riscv_instr_pkg::data_page_alignment));
for(int i = 0; i < page_size; i+= 32) begin
gen_data(.idx(i), .pattern(pattern), .num_of_bytes(32), .data(tmp_data));
tmp_str = format_string($sformatf(".word %0s", format_data(tmp_data)), LABEL_STR_LEN);
data_page_str.push_back(tmp_str);
end
end
endfunction
endclass

View file

@ -0,0 +1,35 @@
/*
* 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.
*/
`define add_instr(instr_n, instr_format, instr_category, instr_group, imm_tp = IMM) \
constraint riscv_``instr_group``_``instr_n``_c { \
if (instr_name == ``instr_n) { \
format == ``instr_format; \
category == ``instr_category; \
group == ``instr_group; \
imm_type == ``imm_tp; \
} \
}
`define add_pseudo_instr(instr_n, instr_format, instr_category, instr_group) \
constraint riscv_``instr_group``_``instr_n``_c { \
if (pseudo_instr_name == ``instr_n) { \
format == ``instr_format; \
category == ``instr_category; \
group == ``instr_group; \
} \
}

View file

@ -0,0 +1,437 @@
/*
* 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
// Create a infinte zero instruction loop, test if we can interrupt or
// enter debug mode while core is executing this loop
class riscv_infinte_loop_instr extends riscv_directed_instr_stream;
string label_prefix = "";
string label_name;
`uvm_object_utils(riscv_infinte_loop_instr)
constraint instr_c {
foreach(instr_list[i]) {
instr_list[i].imm == 0;
instr_list[i].category inside {JUMP, BRANCH};
instr_list[i].instr_name != JALR;
}
}
function new(string name = "");
super.new(name);
endfunction
function void pre_randomize();
riscv_rand_instr instr;
initialize_instr_list(5);
foreach(instr_list[i]) begin
$cast(instr, instr_list[i]);
instr.constraint_cfg_knob_c.constraint_mode(0);
end
endfunction
function void post_randomize();
foreach(instr_list[i]) begin
label_name = $sformatf("%0s_inf_loop_%0d", label_prefix, i);
instr_list[i].atomic = 1'b1;
instr_list[i].label = label_name;
instr_list[i].imm_str = label_name;
instr_list[i].has_label = 1'b1;
instr_list[i].branch_assigned = 1'b1;
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_rand_instr_stream;
rand riscv_instr_base jump;
rand riscv_instr_base addi;
rand riscv_pseudo_instr la;
rand riscv_instr_base branch;
rand bit enable_branch;
rand int mixed_instr_cnt;
riscv_instr_base stack_exit_instr[];
string target_program_label;
int idx;
constraint instr_c {
solve jump.instr_name before addi.imm;
solve jump.instr_name before addi.rs1;
jump.instr_name dist {JAL := 1, JALR := 1};
jump.rd == RA;
!(addi.rs1 inside {cfg.reserved_regs, ZERO});
addi.rs1 == la.rd;
addi.rd == la.rd;
// Avoid using negative offset -1024
addi.imm != 'hFFFF_FC00;
jump.imm == ~addi.imm + 1;
jump.rs1 == addi.rd;
addi.instr_name == ADDI;
branch.category == BRANCH;
la.pseudo_instr_name == LA;
soft 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");
instr_list.rand_mode(0);
endfunction
function void post_randomize();
riscv_instr_base instr[];
// Generate some random instructions to mix with jump instructions
reserved_rd = {addi.rs1};
initialize_instr_list(mixed_instr_cnt);
gen_instr(1'b1);
la.imm_str = target_program_label;
// 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 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
// 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_instr_base branch_instr;
rand bit enable_branch;
string push_start_label;
`uvm_object_utils(riscv_push_stack_instr)
function new(string name = "");
super.new(name);
branch_instr = riscv_instr_base::type_id::create("branch_instr");
endfunction
function void init();
// Save RA, T0 and all reserved loop regs
saved_regs = {RA, T0, cfg.loop_regs};
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 == SP; rs1 == 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 == 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 == 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
`DV_CHECK_RANDOMIZE_WITH_FATAL(branch_instr,
category == BRANCH;)
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();
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 == 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 == 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 == SP; rs1 == 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 enable_hint_instr = 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);
instr_list.rand_mode(0);
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

@ -0,0 +1,209 @@
/*
* 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.
*/
// ---------------------------------------------------------------------------------------------
// This class is used to generate illegal or HINT instructions.
// The illegal instruction will be generated in binary format and mixed with other valid instr.
// The mixed instruction stream will be stored in data section and loaded to instruction pages
// at run time.
// ---------------------------------------------------------------------------------------------
class riscv_illegal_instr extends uvm_object;
typedef enum bit [2:0] {
kIllegalOpcode,
kIllegalFunc3,
kIllegalFunc7,
kReservedCompressedInstr,
kHintInstr
} illegal_instr_type_e;
bit [6:0] legal_opcode[$] = '{7'b0000011,
7'b0000111,
7'b0001111,
7'b0010011,
7'b0010111,
7'b0011011,
7'b0100011,
7'b0100111,
7'b0101111,
7'b0110011,
7'b0110111,
7'b0111011,
7'b1000011,
7'b1000111,
7'b1001011,
7'b1001111,
7'b1010011,
7'b1100011,
7'b1100111,
7'b1101111,
7'b1110011};
rand illegal_instr_type_e exception;
rand bit [31:0] instr_bin;
rand bit [6:0] opcode;
rand bit compressed;
rand bit [2:0] func3;
rand bit [6:0] func7;
rand bit has_func3;
rand bit has_func7;
rand bit [1:0] c_op;
rand bit [2:0] c_msb;
riscv_instr_gen_config cfg;
constraint instr_bit_assignment_c {
solve opcode before instr_bin;
solve func3 before instr_bin;
solve func7 before instr_bin;
if (compressed) {
instr_bin[1:0] == c_op;
instr_bin[15:13] == c_msb;
} else {
instr_bin[6:0] == opcode;
if (has_func7) {
instr_bin[31:25] == func7;
}
if (has_func3) {
instr_bin[14:12] == func3;
}
}
}
constraint exception_type_c {
if (compressed) {
exception inside {kReservedCompressedInstr, kHintInstr};
} else {
exception inside {kIllegalOpcode, kIllegalFunc3, kIllegalFunc7};
}
if (!has_func7) {
exception != kIllegalFunc7;
}
if (!has_func3) {
exception != kIllegalFunc3;
}
}
constraint compressed_instr_op_c {
c_op != 2'b11;
}
constraint reserved_compressed_instr_c {
if (exception == kReservedCompressedInstr) {
((instr_bin[15:5] == '0) && (c_op == 2'b00)) ||
((c_msb == 3'b100) && (c_op == 2'b00)) ||
((instr_bin[15:10] == 6'b100111) && (instr_bin[6:5] == 2'b10) && (c_op == 2'b01)) ||
((instr_bin[15:10] == 6'b100111) && (instr_bin[6:5] == 2'b11) && (c_op == 2'b01)) ||
((c_msb == 3'b001) && (c_op == 2'b01) && (instr_bin[11:7] == 5'b0)) ||
((c_msb == 3'b011) && (c_op == 2'b01) && (instr_bin[12:2] == 11'h40)) ||
((c_msb == 3'b001) && (c_op == 2'b10) && (instr_bin[11:7] == 5'b0)) ||
((c_msb == 3'b010) && (c_op == 2'b10) && (instr_bin[11:7] == 5'b0)) ||
((c_msb == 3'b011) && (c_op == 2'b10) && (instr_bin[11:7] == 5'b0)) ||
((c_msb == 3'b100) && (c_op == 2'b10) && (instr_bin[11:7] == 5'b0));
}
}
constraint hint_instr_c {
if (exception == kHintInstr) {
((c_msb == 3'b000) && (c_op == 2'b01) && ({instr_bin[12], instr_bin[6:2]} == 6'b0)) ||
((c_msb == 3'b010) && (c_op == 2'b01) && (instr_bin[11:7] == 5'b0)) ||
((c_msb == 3'b011) && (c_op == 2'b01) && (instr_bin[11:7] == 5'b0)) ||
((c_msb == 3'b100) && (c_op == 2'b10) && (instr_bin[11:7] == 5'b0) &&
(instr_bin[6:2] != 0));
}
}
constraint illegal_opcode_c {
if (exception == kIllegalOpcode) {
!(opcode inside {legal_opcode});
opcode[1:0] == 2'b11;
} else {
opcode inside {legal_opcode};
}
}
constraint illegal_func3_c {
solve opcode before func3;
if (!compressed) {
if (exception == kIllegalFunc3) {
(opcode == 7'b1100111) -> (func3 != 3'b000);
(opcode == 7'b1100011) -> (func3 inside {3'b010, 3'b011});
(opcode == 7'b0000011) -> (func3 == 3'b111);
(opcode == 7'b0100011) -> (func3 >= 3'b011);
(opcode == 7'b0001111) -> (!(func3 inside {3'b000, 3'b001}));
(opcode == 7'b1110011) -> (func3 == 3'b100);
(opcode == 7'b0011011) -> (!(func3 inside {3'b000, 3'b001, 3'b101}));
(opcode == 7'b0111011) -> (func3 inside {3'b010, 3'b011});
} else {
(opcode == 7'b1100111) -> (func3 == 3'b000);
(opcode == 7'b1100011) -> (!(func3 inside {3'b010, 3'b011}));
(opcode == 7'b0000011) -> (func3 != 3'b111);
(opcode == 7'b0100011) -> (func3 < 3'b011);
(opcode == 7'b0001111) -> (func3 inside {3'b000, 3'b001});
(opcode == 7'b1110011) -> (func3 != 3'b100);
(opcode == 7'b0011011) -> (func3 inside {3'b000, 3'b001, 3'b101});
(opcode == 7'b0111011) -> (!(func3 inside {3'b010, 3'b011}));
}
}
}
constraint has_func7_c {
solve opcode before func7;
if (((opcode == 7'b0010011) && (func3 inside {3'b001, 3'b101})) ||
(opcode inside {7'b0110011, 7'b0111011})) {
has_func7 == 1'b1;
} else {
has_func7 == 1'b0;
}
}
constraint has_func3_c {
solve opcode before func7;
if ((opcode inside {7'b0110111, 7'b1101111})) {
has_func3 == 1'b0;
} else {
has_func3 == 1'b1;
}
}
constraint illegal_func7_c {
if (!compressed) {
if (exception == kIllegalFunc7) {
!(func7 inside {7'b0, 7'b0100000, 7'b1});
} else {
func7 inside {7'b0, 7'b0100000, 7'b1};
}
}
}
// Avoid generating unsupported extensions - F, A, D
constraint unsupported_isa_opcode_c{
!(opcode inside {7'b0101111, 7'b0000111, 7'b0100111, 7'b1000111,
7'b1001011, 7'b1001111, 7'b1010011, 7'b1000011});
}
`uvm_object_utils(riscv_illegal_instr)
`uvm_object_new
function string get_bin_str();
if (compressed) begin
get_bin_str = $sformatf("%4h", instr_bin[15:0]);
end else begin
get_bin_str = $sformatf("%8h", instr_bin[31:0]);
end
endfunction
endclass

View file

@ -0,0 +1,887 @@
/*
* 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.
*/
class riscv_instr_base extends uvm_object;
rand riscv_instr_group_t group;
rand riscv_instr_format_t format;
rand bit [3:0] latency;
rand riscv_instr_cateogry_t category;
rand riscv_instr_name_t instr_name;
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;
rand imm_t imm_type;
rand bit [4:0] imm_len;
rand bit is_pseudo_instr;
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;
string imm_str;
string comment;
string label;
bit is_local_numeric_label;
int idx = -1;
`uvm_object_utils_begin(riscv_instr_base)
`uvm_field_enum(riscv_instr_group_t, group, UVM_DEFAULT)
`uvm_field_enum(riscv_instr_format_t, format, UVM_DEFAULT)
`uvm_field_enum(riscv_instr_cateogry_t, category, UVM_DEFAULT)
`uvm_field_enum(riscv_instr_name_t, instr_name, UVM_DEFAULT)
`uvm_field_enum(riscv_reg_t, rs2, UVM_DEFAULT)
`uvm_field_enum(riscv_reg_t, rs1, UVM_DEFAULT)
`uvm_field_enum(riscv_reg_t, rd, UVM_DEFAULT)
`uvm_field_int(imm, UVM_DEFAULT)
`uvm_field_enum(imm_t, imm_type, UVM_DEFAULT)
`uvm_object_utils_end
constraint default_c {
soft latency == 1;
soft is_pseudo_instr == 0;
instr_name != INVALID_INSTR;
}
// Immediate bit length for different instruction format
constraint imm_len_c {
solve imm_type before imm_len;
if(format inside {U_FORMAT, J_FORMAT}) {
imm_len == 20;
}
if(format inside {I_FORMAT, S_FORMAT, B_FORMAT}) {
if(imm_type == UIMM) {
imm_len == 5;
} else {
imm_len == 11;
}
}
if(format inside {CI_FORMAT, CSS_FORMAT}) {
// TODO: gcc compiler seems to not support 6 bits unsigned imm for c.lui,
// hardcoded to 5 bits for now
if(instr_name == C_LUI) {
imm_len == 5;
} else {
imm_len == 6;
}
}
if(format inside {CL_FORMAT, CS_FORMAT}) {
imm_len == 5;
}
if(format inside {CJ_FORMAT}) {
imm_len == 11;
}
if(format inside {CB_FORMAT, CIW_FORMAT}) {
if(instr_name == C_ANDI) {
imm_len == 6;
} else {
imm_len == 8;
}
}
}
constraint imm_val_c {
if(imm_type inside {NZIMM, NZUIMM}) {
imm != 0;
}
}
// 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}) {
rd != ZERO;
}
if (instr_name == C_JR) {
rs1 != ZERO;
}
if (instr_name == C_MV) {
rs2 != ZERO;
}
}
// Registers specified by the three-bit rs1, rs2, and rd fields of the CIW, CL, CS,
// and CB formats
constraint compressed_three_bits_csr_c {
if(format inside {CIW_FORMAT, CL_FORMAT, CS_FORMAT, CB_FORMAT}) {
rs1 inside {[S0:A5]};
rs2 inside {[S0:A5]};
rd inside {[S0:A5]};
}
}
constraint fence_c {
if (instr_name == FENCE) {
rs1 == ZERO;
rd == ZERO;
imm == 0;
}
if (instr_name == FENCEI) {
rs1 == ZERO;
rd == ZERO;
imm == 0;
}
}
// Cannot shift more than the width of the bus
constraint shift_imm_val_c {
if(category == SHIFT) {
if(group == RV64I) {
// The new instruction in RV64I only handles 32 bits value
imm < 32;
} else {
imm < XLEN;
}
}
}
constraint load_store_c {
if(category inside {LOAD, STORE}) {
rs1 != ZERO; // x0 cannot be used to save the base address
}
}
constraint nop_c {
if(instr_name inside {NOP, C_NOP}) {
rs1 == ZERO;
rs2 == ZERO;
rd == ZERO;
}
}
constraint system_instr_c {
if (category inside {SYSTEM, SYNCH}) {
rd == ZERO;
rs1 == ZERO;
}
}
//////////// RV32I instructions //////////////
// LOAD instructions
`add_instr(LB, I_FORMAT, LOAD, RV32I)
`add_instr(LH, I_FORMAT, LOAD, RV32I)
`add_instr(LW, I_FORMAT, LOAD, RV32I)
`add_instr(LBU, I_FORMAT, LOAD, RV32I)
`add_instr(LHU, I_FORMAT, LOAD, RV32I)
// STORE instructions
`add_instr(SB, S_FORMAT, STORE, RV32I)
`add_instr(SH, S_FORMAT, STORE, RV32I)
`add_instr(SW, S_FORMAT, STORE, RV32I)
// SHIFT intructions
`add_instr(SLL, R_FORMAT, SHIFT, RV32I)
`add_instr(SLLI, I_FORMAT, SHIFT, RV32I)
`add_instr(SRL, R_FORMAT, SHIFT, RV32I)
`add_instr(SRLI, I_FORMAT, SHIFT, RV32I)
`add_instr(SRA, R_FORMAT, SHIFT, RV32I)
`add_instr(SRAI, I_FORMAT, SHIFT, RV32I)
// ARITHMETIC intructions
`add_instr(ADD, R_FORMAT, ARITHMETIC, RV32I)
`add_instr(ADDI, I_FORMAT, ARITHMETIC, RV32I)
`add_instr(NOP, I_FORMAT, ARITHMETIC, RV32I)
`add_instr(SUB, R_FORMAT, ARITHMETIC, RV32I)
`add_instr(LUI, U_FORMAT, ARITHMETIC, RV32I, UIMM)
`add_instr(AUIPC, U_FORMAT, ARITHMETIC, RV32I, UIMM)
// LOGICAL instructions
`add_instr(XOR, R_FORMAT, LOGICAL, RV32I)
`add_instr(XORI, I_FORMAT, LOGICAL, RV32I)
`add_instr(OR, R_FORMAT, LOGICAL, RV32I)
`add_instr(ORI, I_FORMAT, LOGICAL, RV32I)
`add_instr(AND, R_FORMAT, LOGICAL, RV32I)
`add_instr(ANDI, I_FORMAT, LOGICAL, RV32I)
// COMPARE instructions
`add_instr(SLT, R_FORMAT, COMPARE, RV32I)
`add_instr(SLTI, I_FORMAT, COMPARE, RV32I)
`add_instr(SLTU, R_FORMAT, COMPARE, RV32I)
`add_instr(SLTIU, I_FORMAT, COMPARE, RV32I)
// BRANCH instructions
`add_instr(BEQ, B_FORMAT, BRANCH, RV32I)
`add_instr(BNE, B_FORMAT, BRANCH, RV32I)
`add_instr(BLT, B_FORMAT, BRANCH, RV32I)
`add_instr(BGE, B_FORMAT, BRANCH, RV32I)
`add_instr(BLTU, B_FORMAT, BRANCH, RV32I)
`add_instr(BGEU, B_FORMAT, BRANCH, RV32I)
// JUMP instructions
`add_instr(JAL, J_FORMAT, JUMP, RV32I)
`add_instr(JALR, I_FORMAT, JUMP, RV32I)
// SYNCH instructions
`add_instr(FENCE, I_FORMAT, SYNCH, RV32I)
`add_instr(FENCEI, I_FORMAT, SYNCH, RV32I)
// SYSTEM instructions
`add_instr(ECALL, I_FORMAT, SYSTEM, RV32I)
`add_instr(EBREAK, I_FORMAT, SYSTEM, RV32I)
`add_instr(URET, I_FORMAT, SYSTEM, RV32I)
`add_instr(SRET, I_FORMAT, SYSTEM, RV32I)
`add_instr(MRET, I_FORMAT, SYSTEM, RV32I)
`add_instr(WFI, I_FORMAT, SYSTEM, RV32I)
// CSR instructions
`add_instr(CSRRW, R_FORMAT, CSR, RV32I, UIMM)
`add_instr(CSRRS, R_FORMAT, CSR, RV32I, UIMM)
`add_instr(CSRRC, R_FORMAT, CSR, RV32I, UIMM)
`add_instr(CSRRWI, I_FORMAT, CSR, RV32I, UIMM)
`add_instr(CSRRSI, I_FORMAT, CSR, RV32I, UIMM)
`add_instr(CSRRCI, I_FORMAT, CSR, RV32I, UIMM)
//////////// RV32M instructions //////////////
`add_instr(MUL, R_FORMAT, ARITHMETIC, RV32M)
`add_instr(MULH, R_FORMAT, ARITHMETIC, RV32M)
`add_instr(MULHSU, R_FORMAT, ARITHMETIC, RV32M)
`add_instr(MULHU, R_FORMAT, ARITHMETIC, RV32M)
`add_instr(DIV, R_FORMAT, ARITHMETIC, RV32M)
`add_instr(DIVU, R_FORMAT, ARITHMETIC, RV32M)
`add_instr(REM, R_FORMAT, ARITHMETIC, RV32M)
`add_instr(REMU, R_FORMAT, ARITHMETIC, RV32M)
//////////// RV64M instructions //////////////
`add_instr(MULW, R_FORMAT, ARITHMETIC, RV64M)
`add_instr(DIVW, R_FORMAT, ARITHMETIC, RV64M)
`add_instr(DIVUW, R_FORMAT, ARITHMETIC, RV64M)
`add_instr(REMW, R_FORMAT, ARITHMETIC, RV64M)
`add_instr(REMUW, R_FORMAT, ARITHMETIC, RV64M)
//////////// RV32F instructions //////////////
`add_instr(FLW, R_FORMAT, LOAD, RV32F)
`add_instr(FSW, R_FORMAT, STORE, RV32F)
`add_instr(FMADD_S, R_FORMAT, ARITHMETIC, RV32F)
`add_instr(FMSUB_S, R_FORMAT, ARITHMETIC, RV32F)
`add_instr(FNMSUB_S, R_FORMAT, ARITHMETIC, RV32F)
`add_instr(FNMADD_S, R_FORMAT, ARITHMETIC, RV32F)
`add_instr(FADD_S, R_FORMAT, ARITHMETIC, RV32F)
`add_instr(FSUB_S, R_FORMAT, ARITHMETIC, RV32F)
`add_instr(FMUL_S, R_FORMAT, ARITHMETIC, RV32F)
`add_instr(FDIV_S, R_FORMAT, ARITHMETIC, RV32F)
`add_instr(FSQRT_S, R_FORMAT, ARITHMETIC, RV32F)
`add_instr(FSGNJ_S, R_FORMAT, ARITHMETIC, RV32F)
`add_instr(FSGNJN_S, R_FORMAT, ARITHMETIC, RV32F)
`add_instr(FSGNJX_S, R_FORMAT, ARITHMETIC, RV32F)
`add_instr(FMIN_S, R_FORMAT, ARITHMETIC, RV32F)
`add_instr(FMAX_S, R_FORMAT, ARITHMETIC, RV32F)
`add_instr(FCVT_W_S, R_FORMAT, ARITHMETIC, RV32F)
`add_instr(FCVT_WU_S, R_FORMAT, ARITHMETIC, RV32F)
`add_instr(FMV_X_W, R_FORMAT, ARITHMETIC, RV32F)
`add_instr(FEQ_S, R_FORMAT, ARITHMETIC, RV32F)
`add_instr(FLT_S, R_FORMAT, ARITHMETIC, RV32F)
`add_instr(FLE_S, R_FORMAT, ARITHMETIC, RV32F)
`add_instr(FCLASS_S, R_FORMAT, ARITHMETIC, RV32F)
`add_instr(FCVT_S_W, R_FORMAT, ARITHMETIC, RV32F)
`add_instr(FCVT_S_WU, R_FORMAT, ARITHMETIC, RV32F)
`add_instr(FMV_W_X, R_FORMAT, ARITHMETIC, RV32F)
`add_instr(FCVT_L_S, R_FORMAT, ARITHMETIC, RV32F)
`add_instr(FCVT_LU_S, R_FORMAT, ARITHMETIC, RV32F)
`add_instr(FCVT_S_L, R_FORMAT, ARITHMETIC, RV32F)
`add_instr(FCVT_S_LU, R_FORMAT, ARITHMETIC, RV32F)
// RV64I instructions
// LOAD/STORE instructions
`add_instr(LWU, I_FORMAT, LOAD, RV64I)
`add_instr(LD, I_FORMAT, LOAD, RV64I)
`add_instr(SD, S_FORMAT, STORE, RV64I)
// SHIFT intructions
`add_instr(SLLW, R_FORMAT, SHIFT, RV64I)
`add_instr(SLLIW, I_FORMAT, SHIFT, RV64I)
`add_instr(SRLW, R_FORMAT, SHIFT, RV64I)
`add_instr(SRLIW, I_FORMAT, SHIFT, RV64I)
`add_instr(SRAW, R_FORMAT, SHIFT, RV64I)
`add_instr(SRAIW, I_FORMAT, SHIFT, RV64I)
// ARITHMETIC intructions
`add_instr(ADDW, R_FORMAT, ARITHMETIC, RV64I)
`add_instr(ADDIW, I_FORMAT, ARITHMETIC, RV64I)
`add_instr(SUBW, R_FORMAT, ARITHMETIC, RV64I)
// RV32IC
`add_instr(C_LW, CL_FORMAT, LOAD, RV32C, UIMM)
`add_instr(C_SW, CS_FORMAT, STORE, RV32C, UIMM)
`add_instr(C_LWSP, CI_FORMAT, LOAD, RV64C, UIMM)
`add_instr(C_SWSP, CSS_FORMAT, STORE, RV64C, UIMM)
`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, CS_FORMAT, ARITHMETIC, RV32C)
`add_instr(C_ADD, CS_FORMAT, ARITHMETIC, RV32C)
`add_instr(C_NOP, CI_FORMAT, ARITHMETIC, RV32C)
`add_instr(C_MV, CR_FORMAT, ARITHMETIC, RV32C)
`add_instr(C_ANDI, CB_FORMAT, LOGICAL, RV32C)
`add_instr(C_XOR, CS_FORMAT, LOGICAL, RV32C)
`add_instr(C_OR, CS_FORMAT, LOGICAL, RV32C)
`add_instr(C_AND, CS_FORMAT, LOGICAL, RV32C)
`add_instr(C_BEQZ, CB_FORMAT, BRANCH, RV32C)
`add_instr(C_BNEZ, CB_FORMAT, BRANCH, RV32C)
`add_instr(C_SRLI, CB_FORMAT, SHIFT, RV32C, NZUIMM)
`add_instr(C_SRAI, CB_FORMAT, SHIFT, RV32C, NZUIMM)
`add_instr(C_SLLI, CI_FORMAT, SHIFT, RV32C, NZUIMM)
`add_instr(C_J, CJ_FORMAT, JUMP, RV32C)
`add_instr(C_JAL, CJ_FORMAT, JUMP, RV32C)
`add_instr(C_JR, CR_FORMAT, JUMP, RV32C)
`add_instr(C_JALR, CR_FORMAT, JUMP, RV32C)
`add_instr(C_EBREAK, CI_FORMAT, SYSTEM, RV32C)
// RV64C
`add_instr(C_ADDIW, CI_FORMAT, ARITHMETIC, RV64C)
`add_instr(C_SUBW, CS_FORMAT, ARITHMETIC, RV64C)
`add_instr(C_ADDW, CS_FORMAT, ARITHMETIC, RV64C)
`add_instr(C_LD, CL_FORMAT, LOAD, RV64C, UIMM)
`add_instr(C_SD, CS_FORMAT, STORE, RV64C, UIMM)
`add_instr(C_LDSP, CI_FORMAT, LOAD, RV64C, UIMM)
`add_instr(C_SDSP, CSS_FORMAT, STORE, RV64C, UIMM)
// RV128C
`add_instr(C_SRLI64, CB_FORMAT, SHIFT, RV128C, NZUIMM)
`add_instr(C_SRAI64, CB_FORMAT, SHIFT, RV128C, NZUIMM)
`add_instr(C_SLLI64, CI_FORMAT, SHIFT, RV128C, NZUIMM)
`add_instr(C_LQ, CL_FORMAT, LOAD, RV32DC, UIMM)
`add_instr(C_SQ, CS_FORMAT, STORE, RV32DC, UIMM)
`add_instr(C_LQSP, CI_FORMAT, LOAD, RV32DC, UIMM)
`add_instr(C_SQSP, CSS_FORMAT, STORE, RV32DC, UIMM)
// RV32FC
`add_instr(C_FLW, CL_FORMAT, LOAD, RV32FC, UIMM)
`add_instr(C_FSW, CS_FORMAT, STORE, RV32FC, UIMM)
`add_instr(C_FLWSP, CI_FORMAT, LOAD, RV32FC, UIMM)
`add_instr(C_FSWSP, CSS_FORMAT, STORE, RV32FC, UIMM)
// RV32DC
`add_instr(C_FLD, CL_FORMAT, LOAD, RV32DC, UIMM)
`add_instr(C_FSD, CS_FORMAT, STORE, RV32DC, UIMM)
`add_instr(C_FLDSP, CI_FORMAT, LOAD, RV32DC, UIMM)
`add_instr(C_FSDSP, CSS_FORMAT, STORE, RV32DC, UIMM)
// Supervisor Instructions
`add_instr(SFENCE_VMA, R_FORMAT,SYNCH,RV32I)
function void post_randomize();
// Process the immediate value and sign extension
bit [31:0] imm_mask = '1;
imm_mask = imm_mask << imm_len;
if(imm_type inside {UIMM, NZUIMM}) begin
imm = imm & ~imm_mask;
end else begin
if(imm[imm_len-1])
imm = imm | imm_mask;
else
imm = imm & ~imm_mask;
end
// Give a random value if imm is zero after masking unexpectedly
if((imm_type inside {NZIMM, NZUIMM}) && (imm == '0)) begin
imm = $urandom_range(2 ** (imm_len-1) - 1, 1);
end
if (group inside {RV32C, RV64C, RV128C, RV32DC, RV32FC})
is_compressed = 1'b1;
if(imm_str == "")
imm_str = $sformatf("%0d", $signed(imm));
endfunction
function new(string name = "");
super.new(name);
endfunction
// Convert the instrunction to one-liner print message
virtual function string convert2string();
return convert2asm();
endfunction
virtual function void do_print (uvm_printer printer);
`uvm_info(get_type_name(), convert2string(), UVM_LOW)
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)
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 == FENCE)
asm_str = $sformatf("fence"); // TODO: Support all fence combinations
else if(instr_name == FENCEI)
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)
asm_str = $sformatf("%0s%0s, 0x%0x, %0s", asm_str, rd.name(), csr, rs1.name());
else if(instr_name == SFENCE_VMA)
asm_str = "sfence.vma x0, x0"; // TODO: Support all possible sfence
else
asm_str = $sformatf("%0s%0s, %0s, %0s", asm_str, rd.name(), rs1.name(), rs2.name());
CI_FORMAT, CIW_FORMAT:
if(instr_name == C_NOP)
asm_str = "c.nop";
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());
CB_FORMAT:
asm_str = $sformatf("%0s%0s, %0s", asm_str, rs1.name(), get_imm());
CSS_FORMAT:
asm_str = $sformatf("%0s%0s, %0s", asm_str, rs2.name(), get_imm());
CR_FORMAT:
asm_str = $sformatf("%0s%0s, %0s", asm_str, rd.name(), rs2.name());
CJ_FORMAT:
asm_str = $sformatf("%0s%0s", asm_str, get_imm());
endcase
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, FENCEI : 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, WFI, SFENCE_VMA : get_opcode = 7'b1110011;
default : `uvm_fatal(`gfn, $sformatf("Unsupported instruction %0s", instr_name.name()))
endcase
endfunction
// Get opcode for compressed instruction
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
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;
FENCEI : 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;
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;
ECALL, EBREAK, URET, SRET, MRET, 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;
FENCEI : get_func7 = 7'b0000000;
ECALL : get_func7 = 7'b0000000;
EBREAK : 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;
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;
if (!is_compressed) begin
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, FENCEI})
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 == 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
end else begin
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
end
return {prefix, binary};
endfunction
virtual function string get_instr_name();
get_instr_name = instr_name.name();
if(get_instr_name.substr(0, 1) == "C_") begin
get_instr_name = {"c.", get_instr_name.substr(2, get_instr_name.len() - 1)};
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
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
endclass

View file

@ -0,0 +1,360 @@
/*
* 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[];
// Pattern of data section: RAND_DATA, ALL_ZERO, INCR_VAL
rand data_pattern_t data_page_pattern;
// Max depth for the nested loops
rand bit [1:0] max_nested_loop;
// 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;
// Key fields in xSTATUS
// Memory protection bits
rand bit mstatus_mprv;
rand bit mstatus_mxr;
rand bit mstatus_sum;
rand bit mstatus_tvm;
// Enable sfence.vma instruction
rand bit enable_sfence;
//-----------------------------------------------------------------------------
// 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 = 1; // No csr instruction
bit no_ebreak = 1; // No ebreak instruction
bit no_fence; // No fence instruction
bit enable_illegal_instruction;
bit enable_hint_instruction;
int bin_program_instr_cnt = 200;
// 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)
int enable_interrupt;
// 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;
// Stack space allocated to each program, need to be enough to store necessary context
// Example: RA, SP, T0, loop registers
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;
// Reserved registers
// Default reserved registers, only used by special instructions
riscv_reg_t default_reserved_regs[];
// Reserve some registers for loop counter, make sure the loop can execute
// in a determinstic way and not affected by other random instructions
rand riscv_reg_t loop_regs[];
// All reserved regs
riscv_reg_t reserved_regs[];
uvm_cmdline_processor inst;
constraint default_c {
sub_program_instr_cnt.size() == num_of_sub_program;
main_program_instr_cnt + sub_program_instr_cnt.sum() == instr_cnt;
main_program_instr_cnt inside {[1 : instr_cnt]};
foreach(sub_program_instr_cnt[i]) {
sub_program_instr_cnt[i] inside {[1 : 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);
}
}
// 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] := 9,
riscv_instr_pkg::supported_privileged_mode[1] := 1};
} else if (riscv_instr_pkg::supported_privileged_mode.size() == 3) {
init_privileged_mode dist {riscv_instr_pkg::supported_privileged_mode[0] := 6,
riscv_instr_pkg::supported_privileged_mode[1] := 3,
riscv_instr_pkg::supported_privileged_mode[2] := 1};
} else {
init_privileged_mode == riscv_instr_pkg::supported_privileged_mode[0];
}
}
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) {
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) {
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 reserved_reg_c {
unique {loop_regs};
foreach(loop_regs[i]) {
!(loop_regs[i] inside {default_reserved_regs});
}
}
constraint legal_loop_regs_c {
soft max_nested_loop != 0;
// One register for loop counter, one for loop limit
loop_regs.size() == max_nested_loop*2;
unique {loop_regs};
foreach(loop_regs[i]) {
loop_regs[i] != ZERO;
}
}
`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_object_utils_end
function new (string name = "");
string s;
super.new(name);
setup_default_reserved_regs();
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_int_arg_value("+enable_interrupt=", enable_interrupt);
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_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("+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_bool_arg_value("+enable_illegal_instruction=", enable_illegal_instruction);
get_bool_arg_value("+enable_hint_instruction=", enable_hint_instruction);
get_bool_arg_value("+force_m_delegation=", force_m_delegation);
get_bool_arg_value("+force_s_delegation=", force_s_delegation);
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], 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
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
// Reserve below registers for special purpose instruction
// The other normal instruction cannot use them as destination register
virtual function void setup_default_reserved_regs();
default_reserved_regs = {RA, // x1, return address
SP, // x2, stack pointer (user stack)
TP, // x4, thread pointer, used as kernel stack pointer
T0 // x5, alternative link pointer
};
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 post_randomize();
// Setup the list all reserved registers
reserved_regs = {default_reserved_regs, loop_regs};
// Need to save all loop registers, and RA/T0
min_stack_len_per_program = (max_nested_loop * 2 + 2) * (XLEN/8);
// Check if the setting is legal
check_setting();
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
// 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))
val = s.atoi();
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))
val = s.atobin();
endfunction
endclass

View file

@ -0,0 +1,753 @@
/*
* 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.
*/
package riscv_instr_pkg;
import uvm_pkg::*;
`include "uvm_macros.svh"
`include "dv_defines.svh"
`include "riscv_defines.svh"
typedef enum bit [3:0] {
BARE = 4'b0000,
SV32 = 4'b0001,
SV39 = 4'b1000,
SV48 = 4'b1001,
SV57 = 4'b1010,
SV64 = 4'b1011
} satp_mode_t;
typedef enum bit [2:0] {
IMM, // Signed immediate
UIMM, // Unsigned immediate
NZUIMM, // Non-zero unsigned immediate
NZIMM // Non-zero signed immediate
} imm_t;
// Privileged mode
typedef enum bit [1:0] {
USER_MODE = 2'b00,
SUPERVISOR_MODE = 2'b01,
RESERVED_MODE = 2'b10,
MACHINE_MODE = 2'b11
} privileged_mode_t;
typedef enum bit [4:0] {
RV32I,
RV64I,
RV32M,
RV64M,
RV32A,
RV64A,
RV32F,
RV32FC,
RV64F,
RV32D,
RV32DC,
RV64D,
RV32C,
RV64C,
RV128I,
RV128C
} riscv_instr_group_t;
typedef enum {
// RV32I instructions
LUI,
AUIPC,
JAL,
JALR,
BEQ,
BNE,
BLT,
BGE,
BLTU,
BGEU,
LB,
LH,
LW,
LBU,
LHU,
SB,
SH,
SW,
ADDI,
SLTI,
SLTIU,
XORI,
ORI,
ANDI,
SLLI,
SRLI,
SRAI,
ADD,
SUB,
SLL,
SLT,
SLTU,
XOR,
SRL,
SRA,
OR,
AND,
NOP,
FENCE,
FENCEI,
ECALL,
EBREAK,
CSRRW,
CSRRS,
CSRRC,
CSRRWI,
CSRRSI,
CSRRCI,
// RV32M instructions
MUL,
MULH,
MULHSU,
MULHU,
DIV,
DIVU,
REM,
REMU,
// RV64M instructions
MULW,
DIVW,
DIVUW,
REMW,
REMUW,
// RV32F instructions
FLW,
FSW,
FMADD_S,
FMSUB_S,
FNMSUB_S,
FNMADD_S,
FADD_S,
FSUB_S,
FMUL_S,
FDIV_S,
FSQRT_S,
FSGNJ_S,
FSGNJN_S,
FSGNJX_S,
FMIN_S,
FMAX_S,
FCVT_W_S,
FCVT_WU_S,
FMV_X_W,
FEQ_S,
FLT_S,
FLE_S,
FCLASS_S,
FCVT_S_W,
FCVT_S_WU,
FMV_W_X,
FCVT_L_S,
FCVT_LU_S,
FCVT_S_L,
FCVT_S_LU,
// RV64I
LWU,
LD,
SD,
ADDIW,
SLLIW,
SRLIW,
SRAIW,
ADDW,
SUBW,
SLLW,
SRLW,
SRAW,
// RV32C
C_LW,
C_SW,
C_LWSP,
C_SWSP,
C_ADDI4SPN,
C_ADDI,
C_LI,
C_ADDI16SP,
C_LUI,
C_SRLI,
C_SRAI,
C_ANDI,
C_SUB,
C_XOR,
C_OR,
C_AND,
C_BEQZ,
C_BNEZ,
C_SLLI,
C_MV,
C_EBREAK,
C_ADD,
C_NOP,
C_J,
C_JAL,
C_JR,
C_JALR,
// RV64C
C_ADDIW,
C_SUBW,
C_ADDW,
C_LD,
C_SD,
C_LDSP,
C_SDSP,
// RV128C
C_SRLI64,
C_SRAI64,
C_SLLI64,
C_LQ,
C_SQ,
C_LQSP,
C_SQSP,
// RV32FC
C_FLW,
C_FSW,
C_FLWSP,
C_FSWSP,
// RV32DC
C_FLD,
C_FSD,
C_FLDSP,
C_FSDSP,
// Supervisor instruction
MRET,
URET,
SRET,
WFI,
SFENCE_VMA,
// You can add other instructions here
INVALID_INSTR
} riscv_instr_name_t;
`include "riscv_core_setting.sv"
// Maximum virtual address bits used by the program
parameter MAX_USED_VADDR_BITS = 30;
// xSTATUS bit mask
parameter bit [XLEN - 1 : 0] MPRV_BIT_MASK = 'h1 << 17;
parameter bit [XLEN - 1 : 0] SUM_BIT_MASK = 'h1 << 18;
parameter bit [XLEN - 1 : 0] MPP_BIT_MASK = 'h3 << 11;
typedef enum bit [4:0] {
ZERO = 5'b00000,
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
} riscv_reg_t;
typedef enum bit [3:0] {
J_FORMAT = 0,
U_FORMAT,
I_FORMAT,
B_FORMAT,
R_FORMAT,
S_FORMAT,
CI_FORMAT,
CB_FORMAT,
CJ_FORMAT,
CR_FORMAT,
CL_FORMAT,
CS_FORMAT,
CSS_FORMAT,
CIW_FORMAT
} riscv_instr_format_t;
typedef enum bit [3:0] {
LOAD = 0,
STORE,
SHIFT,
ARITHMETIC,
LOGICAL,
COMPARE,
BRANCH,
JUMP,
SYNCH,
SYSTEM,
COUNTER,
CSR,
CHANGELEVEL,
TRAP,
INTERRUPT
} riscv_instr_cateogry_t;
typedef bit [11:0] riscv_csr_t;
typedef enum bit [11:0] {
// User mode register
USTATUS = 'h000, // User status
UIE = 'h004, // User interrupt-enable register
UTVEC = 'h005, // User trap-handler base address
USCRATCH = 'h040, // Scratch register for user trap handlers
UEPC = 'h041, // User exception program counter
UCAUSE = 'h042, // User trap cause
UTVAL = 'h043, // User bad address or instruction
UIP = 'h044, // User interrupt pending
FFLAGS = 'h001, // Floating-Point Accrued Exceptions
FRM = 'h002, // Floating-Point Dynamic Rounding Mode
FCSR = 'h003, // Floating-Point Control/Status Register (FRM + FFLAGS)
CYCLE = 'hC00, // Cycle counter for RDCYCLE instruction
TIME = 'hC01, // Timer for RDTIME instruction
INSTRET = 'hC02, // Instructions-retired counter for RDINSTRET instruction
HPMCOUNTER3 = 'hC03, // Performance-monitoring counter
HPMCOUNTER4 = 'hC04, // Performance-monitoring counter
HPMCOUNTER5 = 'hC05, // Performance-monitoring counter
HPMCOUNTER6 = 'hC06, // Performance-monitoring counter
HPMCOUNTER7 = 'hC07, // Performance-monitoring counter
HPMCOUNTER8 = 'hC08, // Performance-monitoring counter
HPMCOUNTER9 = 'hC09, // Performance-monitoring counter
HPMCOUNTER10 = 'hC0A, // Performance-monitoring counter
HPMCOUNTER11 = 'hC0B, // Performance-monitoring counter
HPMCOUNTER12 = 'hC0C, // Performance-monitoring counter
HPMCOUNTER13 = 'hC0D, // Performance-monitoring counter
HPMCOUNTER14 = 'hC0E, // Performance-monitoring counter
HPMCOUNTER15 = 'hC0F, // Performance-monitoring counter
HPMCOUNTER16 = 'hC10, // Performance-monitoring counter
HPMCOUNTER17 = 'hC11, // Performance-monitoring counter
HPMCOUNTER18 = 'hC12, // Performance-monitoring counter
HPMCOUNTER19 = 'hC13, // Performance-monitoring counter
HPMCOUNTER20 = 'hC14, // Performance-monitoring counter
HPMCOUNTER21 = 'hC15, // Performance-monitoring counter
HPMCOUNTER22 = 'hC16, // Performance-monitoring counter
HPMCOUNTER23 = 'hC17, // Performance-monitoring counter
HPMCOUNTER24 = 'hC18, // Performance-monitoring counter
HPMCOUNTER25 = 'hC19, // Performance-monitoring counter
HPMCOUNTER26 = 'hC1A, // Performance-monitoring counter
HPMCOUNTER27 = 'hC1B, // Performance-monitoring counter
HPMCOUNTER28 = 'hC1C, // Performance-monitoring counter
HPMCOUNTER29 = 'hC1D, // Performance-monitoring counter
HPMCOUNTER30 = 'hC1E, // Performance-monitoring counter
HPMCOUNTER31 = 'hC1F, // Performance-monitoring counter
CYCLEH = 'hC80, // Upper 32 bits of CYCLE, RV32I only
TIMEH = 'hC81, // Upper 32 bits of TIME, RV32I only
INSTRETH = 'hC82, // Upper 32 bits of INSTRET, RV32I only
HPMCOUNTER3H = 'hC83, // Upper 32 bits of HPMCOUNTER3, RV32I only
HPMCOUNTER4H = 'hC84, // Upper 32 bits of HPMCOUNTER4, RV32I only
HPMCOUNTER5H = 'hC85, // Upper 32 bits of HPMCOUNTER5, RV32I only
HPMCOUNTER6H = 'hC86, // Upper 32 bits of HPMCOUNTER6, RV32I only
HPMCOUNTER7H = 'hC87, // Upper 32 bits of HPMCOUNTER7, RV32I only
HPMCOUNTER8H = 'hC88, // Upper 32 bits of HPMCOUNTER8, RV32I only
HPMCOUNTER9H = 'hC89, // Upper 32 bits of HPMCOUNTER9, RV32I only
HPMCOUNTER10H = 'hC8A, // Upper 32 bits of HPMCOUNTER10, RV32I only
HPMCOUNTER11H = 'hC8B, // Upper 32 bits of HPMCOUNTER11, RV32I only
HPMCOUNTER12H = 'hC8C, // Upper 32 bits of HPMCOUNTER12, RV32I only
HPMCOUNTER13H = 'hC8D, // Upper 32 bits of HPMCOUNTER13, RV32I only
HPMCOUNTER14H = 'hC8E, // Upper 32 bits of HPMCOUNTER14, RV32I only
HPMCOUNTER15H = 'hC8F, // Upper 32 bits of HPMCOUNTER15, RV32I only
HPMCOUNTER16H = 'hC90, // Upper 32 bits of HPMCOUNTER16, RV32I only
HPMCOUNTER17H = 'hC91, // Upper 32 bits of HPMCOUNTER17, RV32I only
HPMCOUNTER18H = 'hC92, // Upper 32 bits of HPMCOUNTER18, RV32I only
HPMCOUNTER19H = 'hC93, // Upper 32 bits of HPMCOUNTER19, RV32I only
HPMCOUNTER20H = 'hC94, // Upper 32 bits of HPMCOUNTER20, RV32I only
HPMCOUNTER21H = 'hC95, // Upper 32 bits of HPMCOUNTER21, RV32I only
HPMCOUNTER22H = 'hC96, // Upper 32 bits of HPMCOUNTER22, RV32I only
HPMCOUNTER23H = 'hC97, // Upper 32 bits of HPMCOUNTER23, RV32I only
HPMCOUNTER24H = 'hC98, // Upper 32 bits of HPMCOUNTER24, RV32I only
HPMCOUNTER25H = 'hC99, // Upper 32 bits of HPMCOUNTER25, RV32I only
HPMCOUNTER26H = 'hC9A, // Upper 32 bits of HPMCOUNTER26, RV32I only
HPMCOUNTER27H = 'hC9B, // Upper 32 bits of HPMCOUNTER27, RV32I only
HPMCOUNTER28H = 'hC9C, // Upper 32 bits of HPMCOUNTER28, RV32I only
HPMCOUNTER29H = 'hC9D, // Upper 32 bits of HPMCOUNTER29, RV32I only
HPMCOUNTER30H = 'hC9E, // Upper 32 bits of HPMCOUNTER30, RV32I only
HPMCOUNTER31H = 'hC9F, // Upper 32 bits of HPMCOUNTER31, RV32I only
// Supervisor mode register
SSTATUS = 'h100, // Supervisor status
SEDELEG = 'h102, // Supervisor exception delegation register
SIDELEG = 'h103, // Supervisor interrupt delegation register
SIE = 'h104, // Supervisor interrupt-enable register
STVEC = 'h105, // Supervisor trap-handler base address
SCOUNTEREN = 'h106, // Supervisor counter enable
SSCRATCH = 'h140, // Scratch register for supervisor trap handlers
SEPC = 'h141, // Supervisor exception program counter
SCAUSE = 'h142, // Supervisor trap cause
STVAL = 'h143, // Supervisor bad address or instruction
SIP = 'h144, // Supervisor interrupt pending
SATP = 'h180, // Supervisor address translation and protection
// Machine mode register
MVENDORID = 'hF11, // Vendor ID
MARCHID = 'hF12, // Architecture ID
MIMPID = 'hF13, // Implementation ID
MHARTID = 'hF14, // Hardware thread ID
MSTATUS = 'h300, // Machine status
MISA = 'h301, // ISA and extensions
MEDELEG = 'h302, // Machine exception delegation register
MIDELEG = 'h303, // Machine interrupt delegation register
MIE = 'h304, // Machine interrupt-enable register
MTVEC = 'h305, // Machine trap-handler base address
MCOUNTEREN = 'h306, // Machine counter enable
MSCRATCH = 'h340, // Scratch register for machine trap handlers
MEPC = 'h341, // Machine exception program counter
MCAUSE = 'h342, // Machine trap cause
MTVAL = 'h343, // Machine bad address or instruction
MIP = 'h344, // Machine interrupt pending
PMPCFG0 = 'h3A0, // Physical memory protection configuration
PMPCFG1 = 'h3A1, // Physical memory protection configuration, RV32 only
PMPCFG2 = 'h3A2, // Physical memory protection configuration
PMPCFG3 = 'h3A3, // Physical memory protection configuration, RV32 only
PMPADDR0 = 'h3B0, // Physical memory protection address register
PMPADDR1 = 'h3B1, // Physical memory protection address register
PMPADDR2 = 'h3B2, // Physical memory protection address register
PMPADDR3 = 'h3B3, // Physical memory protection address register
PMPADDR4 = 'h3B4, // Physical memory protection address register
PMPADDR5 = 'h3B5, // Physical memory protection address register
PMPADDR6 = 'h3B6, // Physical memory protection address register
PMPADDR7 = 'h3B7, // Physical memory protection address register
PMPADDR8 = 'h3B8, // Physical memory protection address register
PMPADDR9 = 'h3B9, // Physical memory protection address register
PMPADDR10 = 'h3BA, // Physical memory protection address register
PMPADDR11 = 'h3BB, // Physical memory protection address register
PMPADDR12 = 'h3BC, // Physical memory protection address register
PMPADDR13 = 'h3BD, // Physical memory protection address register
PMPADDR14 = 'h3BE, // Physical memory protection address register
PMPADDR15 = 'h3BF, // Physical memory protection address register
MCYCLE = 'hB00, // Machine cycle counter
MINSTRET = 'hB02, // Machine instructions-retired counter
MHPMCOUNTER3 = 'hB03, // Machine performance-monitoring counter
MHPMCOUNTER4 = 'hB04, // Machine performance-monitoring counter
MHPMCOUNTER5 = 'hB05, // Machine performance-monitoring counter
MHPMCOUNTER6 = 'hB06, // Machine performance-monitoring counter
MHPMCOUNTER7 = 'hB07, // Machine performance-monitoring counter
MHPMCOUNTER8 = 'hB08, // Machine performance-monitoring counter
MHPMCOUNTER9 = 'hB09, // Machine performance-monitoring counter
MHPMCOUNTER10 = 'hB0A, // Machine performance-monitoring counter
MHPMCOUNTER11 = 'hB0B, // Machine performance-monitoring counter
MHPMCOUNTER12 = 'hB0C, // Machine performance-monitoring counter
MHPMCOUNTER13 = 'hB0D, // Machine performance-monitoring counter
MHPMCOUNTER14 = 'hB0E, // Machine performance-monitoring counter
MHPMCOUNTER15 = 'hB0F, // Machine performance-monitoring counter
MHPMCOUNTER16 = 'hB10, // Machine performance-monitoring counter
MHPMCOUNTER17 = 'hB11, // Machine performance-monitoring counter
MHPMCOUNTER18 = 'hB12, // Machine performance-monitoring counter
MHPMCOUNTER19 = 'hB13, // Machine performance-monitoring counter
MHPMCOUNTER20 = 'hB14, // Machine performance-monitoring counter
MHPMCOUNTER21 = 'hB15, // Machine performance-monitoring counter
MHPMCOUNTER22 = 'hB16, // Machine performance-monitoring counter
MHPMCOUNTER23 = 'hB17, // Machine performance-monitoring counter
MHPMCOUNTER24 = 'hB18, // Machine performance-monitoring counter
MHPMCOUNTER25 = 'hB19, // Machine performance-monitoring counter
MHPMCOUNTER26 = 'hB1A, // Machine performance-monitoring counter
MHPMCOUNTER27 = 'hB1B, // Machine performance-monitoring counter
MHPMCOUNTER28 = 'hB1C, // Machine performance-monitoring counter
MHPMCOUNTER29 = 'hB1D, // Machine performance-monitoring counter
MHPMCOUNTER30 = 'hB1E, // Machine performance-monitoring counter
MHPMCOUNTER31 = 'hB1F, // Machine performance-monitoring counter
MCYCLEH = 'hB80, // Upper 32 bits of MCYCLE, RV32I only
MINSTRETH = 'hB82, // Upper 32 bits of MINSTRET, RV32I only
MHPMCOUNTER3H = 'hB83, // Upper 32 bits of HPMCOUNTER3, RV32I only
MHPMCOUNTER4H = 'hB84, // Upper 32 bits of HPMCOUNTER4, RV32I only
MHPMCOUNTER5H = 'hB85, // Upper 32 bits of HPMCOUNTER5, RV32I only
MHPMCOUNTER6H = 'hB86, // Upper 32 bits of HPMCOUNTER6, RV32I only
MHPMCOUNTER7H = 'hB87, // Upper 32 bits of HPMCOUNTER7, RV32I only
MHPMCOUNTER8H = 'hB88, // Upper 32 bits of HPMCOUNTER8, RV32I only
MHPMCOUNTER9H = 'hB89, // Upper 32 bits of HPMCOUNTER9, RV32I only
MHPMCOUNTER10H = 'hB8A, // Upper 32 bits of HPMCOUNTER10, RV32I only
MHPMCOUNTER11H = 'hB8B, // Upper 32 bits of HPMCOUNTER11, RV32I only
MHPMCOUNTER12H = 'hB8C, // Upper 32 bits of HPMCOUNTER12, RV32I only
MHPMCOUNTER13H = 'hB8D, // Upper 32 bits of HPMCOUNTER13, RV32I only
MHPMCOUNTER14H = 'hB8E, // Upper 32 bits of HPMCOUNTER14, RV32I only
MHPMCOUNTER15H = 'hB8F, // Upper 32 bits of HPMCOUNTER15, RV32I only
MHPMCOUNTER16H = 'hB90, // Upper 32 bits of HPMCOUNTER16, RV32I only
MHPMCOUNTER17H = 'hB91, // Upper 32 bits of HPMCOUNTER17, RV32I only
MHPMCOUNTER18H = 'hB92, // Upper 32 bits of HPMCOUNTER18, RV32I only
MHPMCOUNTER19H = 'hB93, // Upper 32 bits of HPMCOUNTER19, RV32I only
MHPMCOUNTER20H = 'hB94, // Upper 32 bits of HPMCOUNTER20, RV32I only
MHPMCOUNTER21H = 'hB95, // Upper 32 bits of HPMCOUNTER21, RV32I only
MHPMCOUNTER22H = 'hB96, // Upper 32 bits of HPMCOUNTER22, RV32I only
MHPMCOUNTER23H = 'hB97, // Upper 32 bits of HPMCOUNTER23, RV32I only
MHPMCOUNTER24H = 'hB98, // Upper 32 bits of HPMCOUNTER24, RV32I only
MHPMCOUNTER25H = 'hB99, // Upper 32 bits of HPMCOUNTER25, RV32I only
MHPMCOUNTER26H = 'hB9A, // Upper 32 bits of HPMCOUNTER26, RV32I only
MHPMCOUNTER27H = 'hB9B, // Upper 32 bits of HPMCOUNTER27, RV32I only
MHPMCOUNTER28H = 'hB9C, // Upper 32 bits of HPMCOUNTER28, RV32I only
MHPMCOUNTER29H = 'hB9D, // Upper 32 bits of HPMCOUNTER29, RV32I only
MHPMCOUNTER30H = 'hB9E, // Upper 32 bits of HPMCOUNTER30, RV32I only
MHPMCOUNTER31H = 'hB9F, // Upper 32 bits of HPMCOUNTER31, RV32I only
MHPMEVENT3 = 'h323, // Machine performance-monitoring event selector
MHPMEVENT4 = 'h324, // Machine performance-monitoring event selector
MHPMEVENT5 = 'h325, // Machine performance-monitoring event selector
MHPMEVENT6 = 'h326, // Machine performance-monitoring event selector
MHPMEVENT7 = 'h327, // Machine performance-monitoring event selector
MHPMEVENT8 = 'h328, // Machine performance-monitoring event selector
MHPMEVENT9 = 'h329, // Machine performance-monitoring event selector
MHPMEVENT10 = 'h32A, // Machine performance-monitoring event selector
MHPMEVENT11 = 'h32B, // Machine performance-monitoring event selector
MHPMEVENT12 = 'h32C, // Machine performance-monitoring event selector
MHPMEVENT13 = 'h32D, // Machine performance-monitoring event selector
MHPMEVENT14 = 'h32E, // Machine performance-monitoring event selector
MHPMEVENT15 = 'h32F, // Machine performance-monitoring event selector
MHPMEVENT16 = 'h330, // Machine performance-monitoring event selector
MHPMEVENT17 = 'h331, // Machine performance-monitoring event selector
MHPMEVENT18 = 'h332, // Machine performance-monitoring event selector
MHPMEVENT19 = 'h333, // Machine performance-monitoring event selector
MHPMEVENT20 = 'h334, // Machine performance-monitoring event selector
MHPMEVENT21 = 'h335, // Machine performance-monitoring event selector
MHPMEVENT22 = 'h336, // Machine performance-monitoring event selector
MHPMEVENT23 = 'h337, // Machine performance-monitoring event selector
MHPMEVENT24 = 'h338, // Machine performance-monitoring event selector
MHPMEVENT25 = 'h339, // Machine performance-monitoring event selector
MHPMEVENT26 = 'h33A, // Machine performance-monitoring event selector
MHPMEVENT27 = 'h33B, // Machine performance-monitoring event selector
MHPMEVENT28 = 'h33C, // Machine performance-monitoring event selector
MHPMEVENT29 = 'h33D, // Machine performance-monitoring event selector
MHPMEVENT30 = 'h33E, // Machine performance-monitoring event selector
MHPMEVENT31 = 'h33F, // Machine performance-monitoring event selector
TSELECT = 'h7A0, // Debug/Trace trigger register select
TDATA1 = 'h7A1, // First Debug/Trace trigger data register
TDATA2 = 'h7A2, // Second Debug/Trace trigger data register
TDATA3 = 'h7A3, // Third Debug/Trace trigger data register
DCSR = 'h7B0, // Debug control and status register
DPC = 'h7B1, // Debug PC
DSCRATCH = 'h7B2 // Debug scratch register
} privileged_reg_t;
typedef enum bit [5:0] {
RSVD, // Reserved field
MXL, // mis.mxl
EXTENSION, // mis.extension
MODE, // satp.mode
ASID, // satp.asid
PPN // satp.ppn
} privileged_reg_fld_t;
typedef enum bit [1:0] {
M_LEVEL = 2'b11, // Machine mode
S_LEVEL = 2'b01, // Supervisor mode
U_LEVEL = 2'b00 // User mode
} privileged_level_t;
typedef enum bit [1:0] {
WIRI, // Reserved Writes Ignored, Reads Ignore Value
WPRI, // Reserved Writes Preserve Values, Reads Ignore Value
WLRL, // Write/Read Only Legal Values
WARL // Write Any Values, Reads Legal Values
} reg_field_access_t;
//Pseudo instructions
typedef enum bit [7:0] {
LI = 0,
LA
} riscv_pseudo_instr_name_t;
// Data pattern of the memory model
typedef enum bit [1:0] {
RAND_DATA = 0,
ALL_ZERO,
INCR_VAL
} data_pattern_t;
typedef enum bit [2:0] {
NEXT_LEVEL_PAGE = 3'b000, // Pointer to next level of page table.
READ_ONLY_PAGE = 3'b001, // Read-only page.
READ_WRITE_PAGE = 3'b011, // Read-write page.
EXECUTE_ONLY_PAGE = 3'b100, // Execute-only page.
READ_EXECUTE_PAGE = 3'b101, // Read-execute page.
R_W_EXECUTE_PAGE = 3'b111 // Read-write-execute page
} pte_permission_t;
typedef enum bit [3:0] {
U_SOFTWARE_INTR = 4'h0,
S_SOFTWARE_INTR = 4'h1,
M_SOFTWARE_INTR = 4'h3,
U_TIMER_INTR = 4'h4,
S_TIMER_INTR = 4'h5,
M_TIMER_INTR = 4'h7,
U_EXTERNAL_INTR = 4'h8,
S_EXTERNAL_INTR = 4'h9,
M_EXTERNAL_INTR = 4'hB
} interrupt_cause_t;
typedef enum bit [3:0] {
INSTRUCTION_ADDRESS_MISALIGNED = 4'h0,
INSTRUCTION_ACCESS_FAULT = 4'h1,
ILLEGAL_INSTRUCTION = 4'h2,
BREAKPOINT = 4'h3,
LOAD_ADDRESS_MISALIGNED = 4'h4,
LOAD_ACCESS_FAULT = 4'h5,
STORE_AMO_ADDRESS_MISALIGNED = 4'h6,
STORE_AMO_ACCESS_FAULT = 4'h7,
ECALL_UMODE = 4'h8,
ECALL_SMODE = 4'h9,
ECALL_MMODE = 4'hB,
INSTRUCTION_PAGE_FAULT = 4'hC,
LOAD_PAGE_FAULT = 4'hD,
STORE_AMO_PAGE_FAULT = 4'hF
} exception_cause_t;
typedef bit [15:0] program_id_t;
parameter IMM25_WIDTH = 25;
parameter IMM12_WIDTH = 12;
parameter INSTR_WIDTH = 32;
parameter DATA_WIDTH = 32;
// Parameters for output assembly program formatting
parameter MAX_INSTR_STR_LEN = 11;
parameter LABEL_STR_LEN = 18;
// Parameter for program generation
parameter MAX_CALLSTACK_DEPTH = 20;
parameter MAX_SUB_PROGRAM_CNT = 20;
parameter MAX_CALL_PER_FUNC = 5;
string indent = {LABEL_STR_LEN{" "}};
// Format the string to a fixed length
function automatic string format_string(string str, int len = 10);
string formatted_str;
formatted_str = {len{" "}};
if(len < str.len()) return str;
formatted_str = {str, formatted_str.substr(0, len - str.len() - 1)};
return formatted_str;
endfunction
// Print the data in the following format
// 0xabcd, 0x1234, 0x3334 ...
function automatic string format_data(bit [7:0] data[], int unsigned byte_per_group = 4);
string str;
int cnt;
str = "0x";
foreach(data[i]) begin
if((i % byte_per_group == 0) && (i != data.size() - 1) && (i != 0)) begin
str = {str, ", 0x"};
end
str = {str, $sformatf("%2x", data[i])};
end
return str;
endfunction
// Get the instr name enum from a string
function automatic riscv_instr_name_t get_instr_name(string str);
riscv_instr_name_t instr = instr.first;
forever begin
if(str.toupper() == instr.name()) begin
return instr;
end
if(instr == instr.last) begin
return INVALID_INSTR;
end
instr = instr.next;
end
endfunction
// Push general purpose register to stack, this is needed before trap handling
function automatic void push_gpr_to_kernel_stack(privileged_reg_t status,
privileged_reg_t scratch,
bit mprv,
ref string instr[$]);
string store_instr = (XLEN == 32) ? "sw" : "sd";
// Use kernal stack for handling exceptions
// Save the user mode stack pointer to the scratch register
instr.push_back($sformatf("csrrw sp, 0x%0x, sp", scratch));
// Move TP to SP
instr.push_back("add sp, tp, zero");
// If MPRV is set and MPP is S/U mode, it means the address translation and memory protection
// for load/store instruction is the same as the mode indicated by MPP. In this case, we
// need to use the virtual address to access the kernel stack.
if((status == MSTATUS) && (SATP_MODE != BARE)) begin
// We temporarily use tp to check mstatus to avoid changing other GPR. The value of sp has
// been saved to xStatus and can be restored later.
if(mprv) begin
instr.push_back($sformatf("csrr tp, 0x%0x // MSTATUS", status));
instr.push_back("srli tp, tp, 11"); // Move MPP to bit 0
instr.push_back("andi tp, tp, 0x3"); // keep the MPP bits
instr.push_back("xori tp, tp, 0x3"); // Check if MPP equals to M-mode('b11)
instr.push_back("bnez tp, 1f"); // Use physical address for kernel SP
// Use virtual address for stack pointer
instr.push_back($sformatf("slli sp, sp, %0d", XLEN - MAX_USED_VADDR_BITS));
instr.push_back($sformatf("srli sp, sp, %0d", XLEN - MAX_USED_VADDR_BITS));
end
end
// Reserve space from kernel stack to save all 32 GPR
instr.push_back($sformatf("1: addi sp, sp, -%0d", 32 * (XLEN/8)));
// Push all GPRs to kernel stack
for(int i = 0; i < 32; i++) begin
instr.push_back($sformatf("%0s x%0d, %0d(sp)", store_instr, i, i * (XLEN/8)));
end
endfunction
// Pop general purpose register from stack, this is needed before returning to user program
function automatic void pop_gpr_from_kernel_stack(privileged_reg_t status,
privileged_reg_t scratch,
bit mprv,
ref string instr[$]);
string load_instr = (XLEN == 32) ? "lw" : "ld";
// Pop user mode GPRs from kernel stack
for(int i = 0; i < 32; i++) begin
instr.push_back($sformatf("%0s x%0d, %0d(sp)", load_instr, i, i * (XLEN/8)));
end
// Restore kernel stack pointer
instr.push_back($sformatf("addi sp, sp, %0d", 32 * (XLEN/8)));
// Move SP to TP
instr.push_back("add tp, sp, zero");
// Restore user mode stack pointer
instr.push_back($sformatf("csrrw sp, 0x%0x, sp", scratch));
endfunction
`include "riscv_instr_gen_config.sv"
`include "riscv_illegal_instr.sv"
`include "riscv_reg.sv"
`include "riscv_privil_reg.sv"
`include "riscv_page_table_entry.sv"
`include "riscv_page_table_exception_cfg.sv"
`include "riscv_page_table.sv"
`include "riscv_page_table_list.sv"
`include "riscv_privileged_common_seq.sv"
`include "riscv_callstack_gen.sv"
`include "riscv_instr_base.sv"
`include "riscv_data_page_gen.sv"
`include "riscv_rand_instr.sv"
`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_instr_sequence.sv"
`include "riscv_asm_program_gen.sv"
endpackage

View file

@ -0,0 +1,321 @@
/*
* 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 instruction sequence
//
// This class is used to generate a single instruction sequence for a RISC-V assembly program.
// It's used by riscv_asm_program_gen to generate the main program and all sub-programs. The
// flow is explained below:
// For main program:
// - Generate instruction sequence body.
// - Post-process the load/store/branch instructions.
// - Insert the jump instructions to its sub-programs (done by riscv_asm_program_gen).
// For sub program:
// - Generate the stack push instructions which are executed when entering this program.
// - Generate instruction sequence body.
// - Generate the stack pop instructions which are executed before exiting this program.
// - Post-process the load/store/branch instructions.
// - Insert the jump instructions to its sub-programs (done by riscv_asm_program_gen).
// - Generate a return instruction at the end of the program.
//-----------------------------------------------------------------------------------------
class riscv_instr_sequence extends uvm_sequence;
int unsigned instr_cnt; // Instruction count of this sequence
riscv_push_stack_instr instr_stack_enter; // Stack push instructions for sub-programs
riscv_pop_stack_instr instr_stack_exit; // Stack pop instructions for sub-programs
riscv_rand_instr_stream instr_stream; // Main instruction streams
bit is_main_program; // Type of this sequence (main or sub program)
string label_name; // Label of the sequence (program name)
riscv_instr_gen_config cfg; // Configuration class handle
string instr_string_list[$]; // Save the instruction list in string format
int program_stack_len; // Stack space allocated for this program
riscv_instr_stream directed_instr[]; // List of all directed instruction stream
riscv_illegal_instr illegal_instr; // Illegal instruction generator
int illegal_instr_pct; // Percentage of illegal instruction
bit enable_hint_instr; // Enable HINT instruction
int hint_instr_pct; // Percentage of HINT instruction
`uvm_object_utils(riscv_instr_sequence)
function new (string name = "");
super.new(name);
if(!uvm_config_db#(riscv_instr_gen_config)::get(null, "*", "instr_cfg", cfg))
`uvm_fatal(get_full_name(), "Cannot get instr_gen_cfg")
instr_stream = riscv_rand_instr_stream::type_id::create("instr_stream");
instr_stack_enter = riscv_push_stack_instr::type_id::create("instr_stack_enter");
instr_stack_exit = riscv_pop_stack_instr::type_id::create("instr_stack_exit");
illegal_instr = riscv_illegal_instr::type_id::create("illegal_instr");
endfunction
// Main function to generate the instruction stream
// The main random instruction stream is generated by instr_stream.gen_instr(), which generates
// each instruction one by one with a separate randomization call. It's not done by a single
// randomization call for the entire instruction stream because this solution won't scale if
// we have hundreds of thousands of instructions to generate. The constraint solver slows down
// considerably as the instruction stream becomes longer. The downside is we cannot specify
// constraints between instructions. The way to solve it is to have a dedicated directed
// instruction stream for such scenarios, like hazard sequence.
virtual function void gen_instr(bit is_main_program, bit enable_hint_instr = 1'b0);
this.is_main_program = is_main_program;
instr_stream.cfg = cfg;
instr_stream.initialize_instr_list(instr_cnt);
`uvm_info(get_full_name(), $sformatf("Start generating %0d instruction",
instr_stream.instr_list.size()), UVM_LOW)
// Do not generate load/store instruction here
// The load/store instruction will be inserted as directed instruction stream
instr_stream.gen_instr(.no_load_store(1'b1), .enable_hint_instr(enable_hint_instr));
if(!is_main_program) begin
gen_stack_enter_instr();
gen_stack_exit_instr();
end
`uvm_info(get_full_name(), "Finishing instruction generation", UVM_LOW)
endfunction
// Generate the stack push operations for this program
// It pushes the necessary context to the stack like RA, T0,loop registers etc. The stack
// pointer(SP) is reduced by the amount the stack space allocated to this program.
function void gen_stack_enter_instr();
bit allow_branch = ((illegal_instr_pct > 0) || (hint_instr_pct > 0)) ? 1'b0 : 1'b1;
`DV_CHECK_STD_RANDOMIZE_WITH_FATAL(program_stack_len,
program_stack_len inside {[cfg.min_stack_len_per_program : cfg.max_stack_len_per_program]};
// Keep stack len word aligned to avoid unaligned load/store
program_stack_len % (XLEN/8) == 0;,
"Cannot randomize program_stack_len")
instr_stack_enter.cfg = cfg;
instr_stack_enter.push_start_label = {label_name, "_stack_p"};
instr_stack_enter.gen_push_stack_instr(program_stack_len, .allow_branch(allow_branch));
instr_stream.instr_list = {instr_stack_enter.instr_list, instr_stream.instr_list};
endfunction
// Recover the saved GPR from the stack
// Advance the stack pointer(SP) to release the allocated stack space.
function void gen_stack_exit_instr();
instr_stack_exit.cfg = cfg;
instr_stack_exit.gen_pop_stack_instr(
program_stack_len, instr_stack_enter.saved_regs);
instr_stream.instr_list = {instr_stream.instr_list, instr_stack_exit.instr_list};
endfunction
//----------------------------------------------------------------------------------------------
// Instruction post-process
//
// Post-process is required for branch instructions:
//
// - Need to assign a valid branch target. This is done by picking a random instruction label in
// this sequence and assigning to the branch instruction. All the non-atomic instructions
// will have a unique numeric label as the local branch target identifier.
// - The atomic instruction streams don't have labels except for the first instruction. This is
// to avoid branching into an atomic instruction stream which breaks its atomicy. The
// definition of an atomic instruction stream here is a sequence of instructions which must be
// executed in-order.
// - In this sequence, only forward branch is handled. The backward branch target is implemented
// in a dedicated loop instruction sequence. Randomly choosing a backward branch target could
// lead to dead loops in the absence of proper loop exiting conditions.
//
//----------------------------------------------------------------------------------------------
virtual function void post_process_instr();
int i;
int label_idx;
int branch_target[string];
// Insert directed instructions, it's randomly mixed with the random instruction stream.
foreach (directed_instr[i]) begin
instr_stream.insert_instr_stream(directed_instr[i].instr_list);
end
// Assign an index for all instructions, these indexes won't change even a new instruction
// is injected in the post process.
foreach (instr_stream.instr_list[i]) begin
instr_stream.instr_list[i].idx = label_idx;
if (instr_stream.instr_list[i].has_label && !instr_stream.instr_list[i].atomic) begin
if ((illegal_instr_pct > 0) && (instr_stream.instr_list[i].is_illegal_instr == 0)) begin
// The illegal instruction generator always increase PC by 4 when resume execution, need
// to make sure PC + 4 is at the correct instruction boundary.
if (instr_stream.instr_list[i].is_compressed) begin
if (i < instr_stream.instr_list.size()-1) begin
if (instr_stream.instr_list[i+1].is_compressed) begin
instr_stream.instr_list[i].is_illegal_instr =
($urandom_range(0, 100) < illegal_instr_pct);
end
end
end else begin
instr_stream.instr_list[i].is_illegal_instr =
($urandom_range(0, 100) < illegal_instr_pct);
end
end
if ((hint_instr_pct > 0) && (instr_stream.instr_list[i].is_illegal_instr == 0)) begin
if (instr_stream.instr_list[i].is_compressed) begin
instr_stream.instr_list[i].is_hint_instr =
($urandom_range(0, 100) < hint_instr_pct);
end
end
instr_stream.instr_list[i].label = $sformatf("%0d", label_idx);
instr_stream.instr_list[i].is_local_numeric_label = 1'b1;
label_idx++;
end
end
// Generate branch target
while(i < instr_stream.instr_list.size()) begin
if((instr_stream.instr_list[i].category == BRANCH) &&
(!instr_stream.instr_list[i].branch_assigned) &&
(!instr_stream.instr_list[i].is_illegal_instr)) begin
// Post process the branch instructions to give a valid local label
// Here we only allow forward branch to avoid unexpected infinite loop
// The loop structure will be inserted with a separate routine using
// reserved loop registers
int branch_target_label;
int branch_byte_offset;
`DV_CHECK_STD_RANDOMIZE_WITH_FATAL(branch_target_label,
branch_target_label >= instr_stream.instr_list[i].idx+1;
branch_target_label <= label_idx-1;
branch_target_label <= instr_stream.instr_list[i].idx+cfg.max_branch_step;,
"Cannot randomize branch_target_label")
`uvm_info(get_full_name(),
$sformatf("Processing branch instruction[%0d]:%0s # %0d -> %0d",
i, instr_stream.instr_list[i].convert2asm(),
instr_stream.instr_list[i].idx, branch_target_label), UVM_HIGH)
instr_stream.instr_list[i].imm_str = $sformatf("%0df", branch_target_label);
// Below calculation is only needed for generating the instruction stream in binary format
for (int j = i + 1; j < instr_stream.instr_list.size(); j++) begin
branch_byte_offset = (instr_stream.instr_list[j-1].is_compressed) ?
branch_byte_offset + 2 : branch_byte_offset + 4;
if (instr_stream.instr_list[j].label == $sformatf("%0d", branch_target_label)) begin
instr_stream.instr_list[i].imm = branch_byte_offset;
break;
end else if (j == instr_stream.instr_list.size() - 1) begin
`uvm_fatal(`gfn, $sformatf("Cannot find target label : %0d", branch_target_label))
end
end
instr_stream.instr_list[i].branch_assigned = 1'b1;
branch_target[branch_target_label] = 1;
end
// Remove the local label which is not used as branch target
if(instr_stream.instr_list[i].has_label &&
instr_stream.instr_list[i].is_local_numeric_label) begin
int idx = instr_stream.instr_list[i].label.atoi();
if(!branch_target[idx]) begin
instr_stream.instr_list[i].has_label = 1'b0;
end
end
i++;
end
`uvm_info(get_full_name(), "Finished post-processing instructions", UVM_HIGH)
endfunction
// Inject a jump instruction stream
// This function is called by riscv_asm_program_gen with the target program label
// The jump routine is implmented with an atomic instruction stream(riscv_jump_instr). Similar
// to load/store instructions, JALR/JAL instructions also need a proper base address and offset
// as the jump target.
function void insert_jump_instr(string target_label, int idx);
riscv_jump_instr jump_instr;
jump_instr = riscv_jump_instr::type_id::create("jump_instr");
jump_instr.target_program_label = target_label;
if(!is_main_program)
jump_instr.stack_exit_instr = instr_stack_exit.pop_stack_instr;
jump_instr.cfg = cfg;
jump_instr.label = label_name;
jump_instr.idx = idx;
`DV_CHECK_RANDOMIZE_WITH_FATAL(jump_instr,
if(is_main_program) {
jump_instr.jump.rd == RA;
},
"Cannot randomize jump_instr")
`uvm_info(get_full_name(), $sformatf("%0s -> %0s",
jump_instr.jump.instr_name.name(), label_name), UVM_HIGH)
instr_stream.insert_instr_stream(jump_instr.instr_list);
endfunction
// Convert the instruction stream to the string format.
// Label is attached to the instruction if available, otherwise attach proper space to make
// the code indent consistent.
function void generate_instr_stream();
string prefix, str;
int i;
instr_string_list = {};
for(i = 0; i < instr_stream.instr_list.size(); i++) begin
if(i == 0) begin
prefix = format_string($sformatf("%0s:", label_name), LABEL_STR_LEN);
instr_stream.instr_list[i].has_label = 1'b1;
end else begin
if(instr_stream.instr_list[i].has_label) begin
prefix = format_string($sformatf("%0s:", instr_stream.instr_list[i].label),
LABEL_STR_LEN);
end else begin
prefix = format_string(" ", LABEL_STR_LEN);
end
end
str = {prefix, instr_stream.instr_list[i].convert2asm()};
instr_string_list.push_back(str);
end
prefix = format_string($sformatf("%0d:", i), LABEL_STR_LEN);
if(!is_main_program) begin
str = {prefix, "ret"};
instr_string_list.push_back(str);
end
endfunction
// Convert the instruction stream to binary format
function void generate_binary_stream(ref string binary[$]);
string instr_bin;
string remaining_bin;
string str;
illegal_instr.cfg = cfg;
foreach (instr_stream.instr_list[i]) begin
if (instr_stream.instr_list[i].is_illegal_instr) begin
// Replace the original instruction with illegal instruction binary
`DV_CHECK_RANDOMIZE_WITH_FATAL(illegal_instr,
exception != kHintInstr;
compressed == instr_stream.instr_list[i].is_compressed;)
str = illegal_instr.get_bin_str();
`uvm_info(`gfn, $sformatf("Inject %0s [%0d] %0s replaced with %0s",
illegal_instr.exception.name(), i,
instr_stream.instr_list[i].convert2bin() ,str), UVM_HIGH)
end else if (instr_stream.instr_list[i].is_hint_instr) begin
// Replace the original instruction with HINT instruction binary
`DV_CHECK_RANDOMIZE_WITH_FATAL(illegal_instr,
exception == kHintInstr;
compressed == instr_stream.instr_list[i].is_compressed;)
str = illegal_instr.get_bin_str();
`uvm_info(`gfn, $sformatf("Inject %0s [%0d] %0s replaced with %0s",
illegal_instr.exception.name(), i,
instr_stream.instr_list[i].convert2bin() ,str), UVM_HIGH)
end else begin
str = instr_stream.instr_list[i].convert2bin();
end
instr_bin = {str, remaining_bin};
// Handle various instruction alignment
if (instr_bin.len() == 8) begin
binary.push_back({"0x", instr_bin});
remaining_bin = "";
end else if (instr_bin.len() == 12) begin
binary.push_back({"0x", instr_bin.substr(4, 11)});
remaining_bin = instr_bin.substr(0, 3);
end else if (instr_bin.len() == 4) begin
remaining_bin = instr_bin;
end else begin
`uvm_fatal(`gfn, $sformatf("Unexpected binary length :%0d", instr_bin.len()))
end
`uvm_info("BIN", $sformatf("%0s : %0s", instr_stream.instr_list[i].convert2bin(),
instr_stream.instr_list[i].convert2asm()), UVM_HIGH)
end
// Attach a C_NOP(0x0001) to make the last entry 32b
if (remaining_bin != "") begin
binary.push_back({"0x0001", remaining_bin});
end
endfunction
endclass

View file

@ -0,0 +1,186 @@
/*
* 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;
rand riscv_instr_base instr_list[$];
int unsigned instr_cnt;
string label = "";
`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();
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);
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);
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 = $urandom_range(0, current_instr_cnt-1);
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(riscv_instr_base new_instr[], int idx = -1, bit replace = 1'b0);
int current_instr_cnt = instr_list.size();
int new_instr_cnt = new_instr.size();
if(idx == -1) begin
idx = $urandom_range(0, current_instr_cnt-1);
while(instr_list[idx].atomic) begin
idx = $urandom_range(0, current_instr_cnt-1);
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;
instr_list = {instr_list[0:idx-1], 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:current_instr_cnt-1]};
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(riscv_instr_base 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(i > 0) {
insert_instr_position[i] >= insert_instr_position[i-1];
}
})
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 access_u_mode_mem = 1'b1;
int max_load_store_offset;
int max_data_page_id;
// Some additional reserved registers that should not be used as rd register
// by this instruction stream
riscv_reg_t reserved_rd[];
constraint avoid_reserved_rd_c {
if(reserved_rd.size() > 0) {
foreach(instr_list[i]) {
!(instr_list[i].rd inside {reserved_rd});
}
}
}
`uvm_object_utils(riscv_rand_instr_stream)
`uvm_object_new
virtual function void create_instr_instance();
riscv_rand_instr instr;
if(cfg == null) begin
`uvm_fatal(get_full_name(), "cfg object is null")
end
for(int i = 0; i < instr_cnt; i++) begin
instr = riscv_rand_instr::type_id::create($sformatf("instr_%0d", i));
instr.cfg = cfg;
instr.reserved_rd = reserved_rd;
instr_list.push_back(instr);
end
endfunction
function void pre_randomize();
if(access_u_mode_mem) begin
max_load_store_offset = riscv_instr_pkg::data_page_size;
max_data_page_id = riscv_instr_pkg::num_of_data_pages;
end else begin
max_load_store_offset = riscv_instr_pkg::kernel_data_page_size;
max_data_page_id = riscv_instr_pkg::num_of_kernel_data_pages;
end
endfunction
virtual function void gen_instr(bit no_branch = 1'b0,
bit no_load_store = 1'b1,
bit enable_hint_instr = 1'b0);
foreach(instr_list[i]) begin
`DV_CHECK_RANDOMIZE_WITH_FATAL(instr_list[i],
// The last instruction cannot be branch instruction as there's no forward branch target.
if((i == instr_list.size() - 1) || no_branch) {
category != BRANCH;
}
if(no_load_store) {
!(category inside {LOAD, STORE});
})
end
endfunction
endclass

View file

@ -0,0 +1,330 @@
/*
* 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
// TODO: Support load/store from instruction section.
class riscv_load_store_base_instr_stream extends riscv_directed_instr_stream;
rand int unsigned num_load_store;
rand int unsigned num_mixed_instr;
rand int base;
rand int offset[];
rand int addr[];
rand int unsigned data_page_id;
rand riscv_reg_t rs1_reg;
riscv_reg_t reserved_rd[$];
// User can specify a small group of available registers to generate various hazard condition
rand riscv_reg_t avail_regs[];
`uvm_object_utils(riscv_load_store_base_instr_stream)
constraint size_c {
offset.size() == num_load_store;
addr.size() == num_load_store;
}
constraint rs1_c {
!(rs1_reg inside {cfg.reserved_regs, reserved_rd, ZERO});
}
constraint addr_c {
data_page_id < max_data_page_id;
base inside {[0 : max_load_store_offset-1]};
foreach(offset[i]) {
addr[i] == base + offset[i];
// Make sure address is still valid
addr[i] inside {[0 : max_load_store_offset - 1]};
offset[i] inside {[-2048:2047]};
}
}
function new(string name = "");
super.new(name);
instr_list.rand_mode(0);
endfunction
function void post_randomize();
gen_load_store_instr();
// rs1 cannot be modified by other instructions
if(!(rs1_reg inside {reserved_rd})) begin
reserved_rd.push_back(rs1_reg);
end
add_mixed_instr();
add_rs1_init_la_instr();
super.post_randomize();
endfunction
// Use "la" instruction to initialize the base regiseter
virtual function void add_rs1_init_la_instr();
riscv_pseudo_instr la_instr;
la_instr = riscv_pseudo_instr::type_id::create("la_instr");
`DV_CHECK_RANDOMIZE_WITH_FATAL(la_instr,
pseudo_instr_name == LA;
rd == rs1_reg;,
"Cannot randomize la_instr")
if(access_u_mode_mem) begin
la_instr.imm_str = $sformatf("data_page_%0d+%0d", data_page_id, base);
end else begin
la_instr.imm_str = $sformatf("kernel_data_page_%0d+%0d", data_page_id, base);
end
instr_list.push_front(la_instr);
endfunction
// Generate each load/store instruction
virtual function void gen_load_store_instr();
riscv_rand_instr rand_instr;
riscv_instr_name_t allowed_instr[];
if(avail_regs.size() > 0) begin
`DV_CHECK_STD_RANDOMIZE_WITH_FATAL(avail_regs,
unique{avail_regs};
foreach(avail_regs[i]) {
!(avail_regs[i] inside {cfg.reserved_regs});
},
"Cannot randomize avail_regs")
end
foreach(addr[i]) begin
rand_instr = riscv_rand_instr::type_id::create("rand_instr");
rand_instr.cfg = cfg;
rand_instr.reserved_rd = reserved_rd;
// 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 (addr[i][0] == 1'b0) begin
allowed_instr = {LH, LHU, SH, allowed_instr};
end
if (addr[i][1:0] == 2'b00) begin
allowed_instr = {LW, SW, LWU, allowed_instr};
if((offset[i] inside {[0:127]}) && (offset[i] % 4 == 0)) begin
allowed_instr = {C_LW, C_SW, allowed_instr};
end
end
if(addr[i][2:0] == 3'b000) begin
allowed_instr = {LD, SD, allowed_instr};
if((offset[i] inside {[0:255]}) && (offset[i] % 8 == 0)) begin
allowed_instr = {C_LD, C_SD, allowed_instr};
end
end
`DV_CHECK_RANDOMIZE_WITH_FATAL(rand_instr,
solve rs1 before rd;
rs1 == rs1_reg;
instr_name inside {allowed_instr};
if(avail_regs.size() > 0) {
rd inside {avail_regs};
}
rd != rs1;
)
rand_instr.process_load_store = 0;
rand_instr.imm_str = $sformatf("%0d", offset[i]);
instr_list.push_back(rand_instr);
end
endfunction
// Insert some other instructions to mix with load/store instruction
virtual function void add_mixed_instr();
riscv_rand_instr rand_instr;
for(int i = 0; i < num_mixed_instr; i ++) begin
rand_instr = riscv_rand_instr::type_id::create("rand_instr");
rand_instr.cfg = cfg;
rand_instr.reserved_rd = reserved_rd;
`DV_CHECK_RANDOMIZE_WITH_FATAL(rand_instr,
if(avail_regs.size() > 0) {
rs1 inside {avail_regs};
rd inside {avail_regs};
}
!(category inside {LOAD, STORE, BRANCH, JUMP});,
"Cannot randomize instruction")
insert_instr(rand_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_load_store_base_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 the same cache line
class riscv_cache_line_stress_instr_stream extends riscv_load_store_stress_instr_stream;
constraint same_cache_line_c {
base % riscv_instr_pkg::dcache_line_size_in_bytes == 0;
foreach(offset[i]) {
offset[i] inside {[0 : riscv_instr_pkg::dcache_line_size_in_bytes-1]};
}
}
`uvm_object_utils(riscv_cache_line_stress_instr_stream)
`uvm_object_new
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_directed_instr_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};
unique {data_page_id};
num_of_instr_stream inside {[1 : max_data_page_id]};
foreach(rs1_reg[i]) {
!(rs1_reg[i] inside {cfg.reserved_regs, ZERO});
}
}
// 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.push_back(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

View file

@ -0,0 +1,168 @@
/*
* 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 riscv_instr_name_t branch_type[];
rand int num_of_instr_in_loop;
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 loop_c {
solve num_of_nested_loop before loop_cnt_reg;
solve num_of_nested_loop before loop_limit_reg;
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;
num_of_instr_in_loop inside {[1:200]};
num_of_nested_loop inside {[1:cfg.max_nested_loop]};
loop_cnt_reg.size() == num_of_nested_loop;
loop_limit_reg.size() == num_of_nested_loop;
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(loop_init_val[i]) {
loop_cnt_reg[i] inside {cfg.loop_regs};
loop_limit_reg[i] inside {cfg.loop_regs};
loop_init_val[i] inside {[-10:10]};
loop_limit_val[i] inside {[-20:20]};
loop_step_val[i] inside {[-10:10]};
loop_step_val[i] != 0;
if(loop_init_val[i] < loop_limit_val[i]) {
loop_step_val[i] > 0;
} else {
loop_step_val[i] < 0;
}
// Select a reasonable branch instruction to avoid inifint loop
if(loop_init_val[i] < 0 || (loop_limit_val[i] + loop_step_val[i]) < 0) {
!(branch_type[i] inside {BLTU, BGEU});
}
if(((loop_limit_val[i] - loop_init_val[i]) % loop_step_val[i] != 0) ||
(loop_limit_val[i] == loop_init_val[i])) {
!(branch_type[i] inside {BEQ, BNE});
}
if(loop_step_val[i] > 0) {
branch_type[i] inside {BLTU, BNE, BLT, BEQ};
} else {
branch_type[i] inside {BGEU, BNE, BGE, BEQ};
}
}
unique {loop_cnt_reg, loop_limit_reg};
}
`uvm_object_utils(riscv_loop_instr)
`uvm_object_new
function void post_randomize();
// 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;
`DV_CHECK_RANDOMIZE_WITH_FATAL(loop_branch_target_instr[i],
!(category inside {LOAD, STORE, BRANCH, JUMP});,
"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];
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

@ -0,0 +1,72 @@
/*
* 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 page table class
// This class is defined based on RISC-V privileged spec 1.10, three page table structure is
// supported: SV32, SV39, SV48
// This class is used by riscv_page_table_list to generate all page tables the program
class riscv_page_table#(satp_mode_t MODE = SV39) extends uvm_object;
int unsigned num_of_pte; // Number of page table entry
int unsigned table_id; // Page table ID
bit [1:0] level; // Page table level
bit [XLEN-1:0] pte_binary[]; // Page table entry in binary format
rand riscv_page_table_entry#(MODE) pte[]; // List of all page table entries
`uvm_object_param_utils(riscv_page_table#(MODE))
`uvm_object_new
// Init page table
function void init_page_table(int unsigned num_of_pte = 1);
this.num_of_pte = num_of_pte;
pte = new[num_of_pte];
pte_binary = new[num_of_pte];
endfunction
// Generate the page table binary
function void gen_page_table_binary();
foreach(pte[i]) begin
pte_binary[i] = pte[i].bits;
end
endfunction
// Generate the page table section in the output assembly program
// Basically it's like a data section with all PTE binaries.
function void gen_page_table_section(output string instr[$]);
string str;
this.gen_page_table_binary();
// Align the page table to 4K boundary
str = ".align 12";
instr.push_back(str);
str = $sformatf("%0s:", get_name());
instr.push_back(str);
foreach(pte_binary[i]) begin
if (i % 8 == 0) begin
if (XLEN == 64) begin
str = $sformatf(".dword 0x%0x", pte_binary[i]);
end else begin
str = $sformatf(".word 0x%0x", pte_binary[i]);
end
end else begin
str = {str, $sformatf(", 0x%0x", pte_binary[i])};
end
if (((i + 1) % 8 == 0) || (i == pte_binary.size() - 1)) begin
instr.push_back(str);
end
end
endfunction
endclass

View file

@ -0,0 +1,193 @@
/*
* 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 Page Table Entry(PTE)
//
// Support SV32, SV39, SV48 PTE format defined in RISC-V privileged spec 1.10.
// -------------------------------------------------------------------------------------------
class riscv_page_table_entry#(satp_mode_t MODE = SV39) extends uvm_object;
// Note that only SV32, SV39, SV48 are supported
parameter PPN0_WIDTH = (MODE == SV32) ? 10 : 9;
parameter PPN1_WIDTH = (MODE == SV32) ? 12 : 9;
parameter PPN2_WIDTH = (MODE == SV39) ? 26 : ((MODE == SV48) ? 9 : 1);
parameter PPN3_WIDTH = (MODE == SV48) ? 9 : 1;
parameter RSVD_WIDTH = (MODE == SV32) ? 1 : 10;
parameter VPN_WIDTH = (MODE == SV32) ? 10 : 9;
// Spare bits in virtual address = XLEN - used virtual address bits
parameter VADDR_SPARE = (MODE == SV32) ? 0 : (MODE == SV39) ? 25 : 16;
// Virtual address bit width
parameter VADDR_WIDTH = (MODE == SV32) ? 31 : (MODE == SV39) ? 38 : 48;
rand bit v; // PTE is valid
rand pte_permission_t xwr; // PTE execute-write-read permission
rand bit u; // Accessible in User Mode
rand bit g; // Gloabal mapping
rand bit a; // Accessed flag
rand bit d; // Dirty flag
rand bit [1:0] rsw; // Reserved for future use
rand bit [PPN0_WIDTH-1:0] ppn0;
rand bit [PPN1_WIDTH-1:0] ppn1;
rand bit [PPN2_WIDTH-1:0] ppn2;
rand bit [PPN3_WIDTH-1:0] ppn3;
rand bit [XLEN-1:0] bits;
rand bit [RSVD_WIDTH-1:0] rsvd;
int child_table_id;
bit [XLEN-1:0] starting_pa; // Starting physical address
bit [XLEN-1:0] starting_va; // Starting virtual address offset
// This two bits are implementation specific, set them to 1 to avoid mismatching
constraint access_dirty_bit_c {
soft a == 1'b1;
soft d == 1'b1;
}
// Set reserved fields to 0
constraint reserved_bits_c {
soft rsw == '0;
soft rsvd == '0;
}
// PPN is assigned in the post-process
constraint ppn_zero_c {
soft ppn0 == '0;
soft ppn1 == '0;
soft ppn2 == '0;
soft ppn3 == '0;
}
constraint sw_legal_c {
// If the PTE is not a leaf page, U,A,D must be cleared by SW for future compatibility
if(xwr == NEXT_LEVEL_PAGE) {
u == 1'b0;
a == 1'b0;
d == 1'b0;
}
}
`uvm_object_param_utils(riscv_page_table_entry#(MODE))
`uvm_object_new
virtual function void turn_off_default_constraint();
access_dirty_bit_c.constraint_mode(0);
reserved_bits_c.constraint_mode(0);
ppn_zero_c.constraint_mode(0);
sw_legal_c.constraint_mode(0);
endfunction
function void post_randomize();
pack_entry();
endfunction
virtual function void do_copy(uvm_object rhs);
riscv_page_table_entry#(MODE) rhs_;
super.do_copy(rhs);
`DV_CHECK_FATAL($cast(rhs_, rhs), "Cast to page_table_entry failed!")
this.v = rhs_.v;
this.xwr = rhs_.xwr;
this.u = rhs_.u;
this.g = rhs_.g;
this.a = rhs_.a;
this.d = rhs_.d;
this.rsw = rhs_.rsw;
this.ppn0 = rhs_.ppn0;
this.ppn1 = rhs_.ppn1;
this.ppn2 = rhs_.ppn2;
this.ppn3 = rhs_.ppn3;
this.bits = rhs_.bits;
this.rsvd = rhs_.rsvd;
this.starting_pa = rhs_.starting_pa;
this.starting_va = rhs_.starting_va;
this.child_table_id = rhs_.child_table_id;
endfunction
virtual function string convert2string();
string str;
str = $sformatf("xwr: %0s, (v)alid:%0d, u: %0d, pa:0x%0x, va:0x%0x",
xwr.name(), v, u, starting_pa, starting_va);
case(MODE)
SV32: str = {str, $sformatf(", ppn[1:0] = %0d/%0d", ppn1, ppn0)};
SV39: str = {str, $sformatf(", ppn[2:0] = %0d/%0d/%0d", ppn2, ppn1, ppn0)};
SV48: str = {str, $sformatf(", ppn[3:0] = %0d/%0d/%0d/%0d", ppn3, ppn2, ppn1, ppn0)};
default: `uvm_fatal(get_full_name(), $sformatf("Unsupported mode %0x", MODE))
endcase
return str;
endfunction
// Pack the PTE to bit stream
virtual function void pack_entry();
case(MODE)
SV32: bits = {ppn1,ppn0,rsw,d,a,g,u,xwr,v};
SV39: bits = {rsvd,ppn2,ppn1,ppn0,rsw,d,a,g,u,xwr,v};
SV48: bits = {rsvd,ppn3,ppn2,ppn1,ppn0,rsw,d,a,g,u,xwr,v};
default: `uvm_fatal(get_full_name(), $sformatf("Unsupported mode %0x", MODE))
endcase
endfunction
// Return the PPN field offset based on the page level
function int get_ppn_offset(bit [1:0] page_level);
case(page_level)
0 : return 0;
1 : return PPN0_WIDTH;
2 : return PPN0_WIDTH + PPN1_WIDTH;
3 : return PPN0_WIDTH + PPN1_WIDTH + PPN2_WIDTH;
endcase
endfunction
// Assign each PPN field based on the input physical address. This function is used to setup the
// leaf PTE to map to the target physical address.
// start_pa : Start phyical address.
// pte_index : The PTE index of the input page level.
// page_level : The page level that this PTE belongs to.
function void set_ppn(bit [XLEN-1:0] base_pa, int pte_index, bit[1:0] page_level);
int pte_incr[4];
int pte_per_table = 4096 / (XLEN/8);
ppn0 = base_pa[12 +: PPN0_WIDTH];
ppn1 = base_pa[12 + PPN0_WIDTH +: PPN1_WIDTH];
if(MODE == SV39) begin
ppn2 = base_pa[12 + PPN0_WIDTH + PPN1_WIDTH +: PPN2_WIDTH];
end else if (MODE == SV48) begin
ppn2 = base_pa[12 + PPN0_WIDTH + PPN1_WIDTH +: PPN2_WIDTH];
ppn3 = base_pa[12 + PPN0_WIDTH + PPN1_WIDTH + PPN2_WIDTH +: PPN3_WIDTH];
end
foreach(pte_incr[i]) begin
if(i >= page_level) begin
pte_incr[i] = pte_index % pte_per_table;
pte_index = pte_index / pte_per_table;
end
end
ppn0 += pte_incr[0];
ppn1 += pte_incr[1];
ppn2 += pte_incr[2];
ppn3 += pte_incr[3];
starting_pa = get_starting_pa();
starting_va = starting_pa - base_pa;
endfunction
// Get the starting physical address covered by this PTE
function bit[XLEN-1:0] get_starting_pa();
case(MODE)
SV32: get_starting_pa = {ppn1, ppn0};
SV39: get_starting_pa = {ppn2, ppn1, ppn0};
SV48: get_starting_pa = {ppn3, ppn2, ppn1, ppn0};
default: `uvm_fatal(get_full_name(), $sformatf("Unsupported mode %0x", MODE))
endcase
get_starting_pa = get_starting_pa << 12;
endfunction
endclass

View file

@ -0,0 +1,69 @@
/*
* 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.
*/
class riscv_page_table_exception_cfg extends uvm_object;
bit enable_exception;
// Knobs for each type of exception
rand bit allow_page_access_control_exception;
rand bit allow_superpage_misaligned_exception;
rand bit allow_leaf_link_page_exception;
rand bit allow_invalid_page_exception;
rand bit allow_privileged_mode_exception;
rand bit allow_zero_access_bit_exception;
rand bit allow_zero_dirty_bit_exception;
// Exception ratio control
int unsigned page_access_fault_ratio = 10;
int unsigned misaligned_superpage_ratio = 10;
int unsigned leaf_link_page_ratio = 10;
int unsigned invalid_page_ratio = 10;
int unsigned privl_mode_fault_ratio = 10;
int unsigned zero_access_fault_ratio = 5;
int unsigned zero_dirty_fault_ratio = 5;
constraint exception_ratio_c {
if(enable_exception) {
allow_page_access_control_exception dist { 1 := page_access_fault_ratio,
0 := 100 - page_access_fault_ratio };
allow_superpage_misaligned_exception dist { 1 := misaligned_superpage_ratio,
0 := 100 - misaligned_superpage_ratio };
allow_leaf_link_page_exception dist { 1 := leaf_link_page_ratio,
0 := 100 - leaf_link_page_ratio };
allow_invalid_page_exception dist { 1 := invalid_page_ratio,
0 := 100 - invalid_page_ratio };
allow_privileged_mode_exception dist { 1 := privl_mode_fault_ratio,
0 := 100 - privl_mode_fault_ratio };
allow_zero_access_bit_exception dist { 1 := zero_access_fault_ratio,
0 := 100 - zero_access_fault_ratio };
allow_zero_dirty_bit_exception dist { 1 := zero_dirty_fault_ratio,
0 := 100 - zero_dirty_fault_ratio };
} else {
allow_page_access_control_exception == 0;
allow_superpage_misaligned_exception == 0;
allow_leaf_link_page_exception == 0;
allow_invalid_page_exception == 0;
allow_privileged_mode_exception == 0;
allow_zero_access_bit_exception == 0;
allow_zero_dirty_bit_exception == 0;
}
}
`uvm_object_utils(riscv_page_table_exception_cfg)
`uvm_object_new
endclass

View file

@ -0,0 +1,510 @@
/*
* 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.
*/
//----------------------------------------------------------------------------------------------
// Complete RISC-V page table generator
//
// This class is used to generate all the page tables and link them together.
// Below features are supported:
// - Multiple PTEs for each page table
// - Multiple tables at each level(except for root table)
// - Mixed leaf entry and non-leaf entry at any level
// - Allow injecting page table exceptions for any PTE
//----------------------------------------------------------------------------------------------
class riscv_page_table_list#(satp_mode_t MODE = SV39) extends uvm_object;
localparam PTE_SIZE = XLEN / 8;
localparam PTE_CNT = 4096 / PTE_SIZE;
localparam PAGE_LEVEL = (MODE == SV32) ? 2 : ((MODE == SV39) ? 3 : 4);
localparam LINK_PTE_PER_TABLE = 2;
localparam SUPER_LEAF_PTE_PER_TABLE = 2;
satp_mode_t mode = MODE;
// Privileged mode of the program
privileged_mode_t privileged_mode = USER_MODE;
// Starting physical address of the program.
bit [XLEN-1:0] start_pa = 'h8000_0000;
// Num of page table per level
int unsigned num_of_page_table[];
// Page table list, from highest level to the lowest level
riscv_page_table#(MODE) page_table[];
// Root page table PTE idx for the init code entry
int unsigned root_init_pte_idx;
// Instruction generator configuration
riscv_instr_gen_config cfg;
// Allow exception or not
bit enable_exception;
riscv_page_table_exception_cfg exception_cfg;
// Valid PTE entry for exception recovery
riscv_page_table_entry#(MODE) valid_leaf_pte;
riscv_page_table_entry#(MODE) valid_link_pte;
riscv_page_table_entry#(MODE) valid_data_leaf_pte;
riscv_page_table_entry#(MODE) illegal_pte;
// Registers used for page table exception handling
rand riscv_reg_t level_reg;
rand riscv_reg_t fault_vaddr_reg;
rand riscv_reg_t pte_addr_reg;
rand riscv_reg_t pte_reg;
rand riscv_reg_t tmp_reg;
rand riscv_reg_t mask_reg;
rand riscv_reg_t mpp_reg;
constraint page_table_exception_handling_reg_c {
unique {level_reg, fault_vaddr_reg, pte_addr_reg,
pte_reg, tmp_reg, mask_reg, mpp_reg};
!(level_reg inside {cfg.reserved_regs, ZERO});
!(fault_vaddr_reg inside {cfg.reserved_regs, ZERO});
!(pte_addr_reg inside {cfg.reserved_regs, ZERO});
!(pte_reg inside {cfg.reserved_regs, ZERO});
!(mask_reg inside {cfg.reserved_regs, ZERO});
!(mpp_reg inside {cfg.reserved_regs, ZERO});
!(tmp_reg inside {cfg.reserved_regs, ZERO});
}
`uvm_object_param_utils(riscv_page_table_list#(MODE))
function new(string name = "");
super.new(name);
default_page_table_setting();
exception_cfg = riscv_page_table_exception_cfg::type_id::create("exception_cfg");
valid_leaf_pte = riscv_page_table_entry#(MODE)::type_id::create("valid_leaf_pte");
valid_link_pte = riscv_page_table_entry#(MODE)::type_id::create("valid_link_pte");
valid_data_leaf_pte = riscv_page_table_entry#(MODE)::type_id::create("valid_link_pte");
illegal_pte = riscv_page_table_entry#(MODE)::type_id::create("illegal_pte");
endfunction
// To avoid large numbers of page tables, by default we limit the number of non-leaf PTE
// at higher level. To be more specific, all PTEs of level 0 page table is leaf PTE. For
// higher level page table, only PTE[0] and PTE[1] is non-leaf PTE, all other PTEs are leaf
// PTE. All leaf PTE should have PPN map to the real physical address of the instruction
// or data. For non-leaf PTE, the PPN should map to the physical address of the next PTE.
// Take SV39 for example: (PTE_SIZE = 8B)
// Table size is 4KB, PTE_SIZE=8B, entry count = 4K/8 = 512
// Level 2: Root table, 2 entries, PTE[0] and PTE[1] is non-leaf PTE, PTE[2] is leaf PTE, all
// other PTEs are invalid, totally 1 page table with 3 PTEs at this level.
// Level 1: Two page tables, map to PTE[0] and PTE[1] of the root table.
// Each table has 512 entries, PTE[0], PTE[1] are non-leaf PTE, cover 4MB memory
// space. PTE[2:511] are leaf PTE, cover 510 * 2MB memory space.
// Level 0: 4 page tables at this level(map to PTE[0] and PTE[1] of the previous level),
// each table has 512 leaf PTE.
// In summary, 7(1+2+4) tables are needed for SV39.
// Similarly, 3 (1+2) page tables for SV32, 15 (1 + 2 + 4 + 8) page tables for SV48.
// Note:
// - The number of randomization call is optmized to improve performance
// - PPN assignment is done at program run time
virtual function void randomize_page_table();
int pte_index;
exception_cfg.enable_exception = enable_exception;
create_valid_pte();
foreach(page_table[i]) begin
`uvm_info(`gfn, $sformatf("Randomizing page table %0d, num of PTE: %0d",
i, page_table[i].pte.size()), UVM_LOW)
if(i == 0) begin
pte_index = 0;
end else if(page_table[i].level != page_table[i-1].level) begin
pte_index = 0;
end
foreach(page_table[i].pte[j]) begin
if(page_table[i].level > 0) begin
// Superpage
if (j < LINK_PTE_PER_TABLE) begin
// First few super pages are link PTE to the next level
$cast(page_table[i].pte[j], valid_link_pte.clone());
end else if (j < SUPER_LEAF_PTE_PER_TABLE + LINK_PTE_PER_TABLE) begin
// Non-link superpage table entry
$cast(page_table[i].pte[j], valid_leaf_pte.clone());
end else begin
// Invalid unused PTEs
page_table[i].pte[j] = riscv_page_table_entry#(MODE)::type_id::
create($sformatf("pte_%0d_%0d",i, j));
page_table[i].pte[j].v = 1'b0;
end
end else begin
// Lowest level leaf pages
$cast(page_table[i].pte[j], valid_leaf_pte.clone());
end
if(page_table[i].pte[j].xwr != NEXT_LEVEL_PAGE) begin
page_table[i].pte[j].set_ppn(start_pa, pte_index, page_table[i].level);
end
pte_index++;
if(enable_exception) begin
inject_page_table_exception(page_table[i].pte[j], page_table[i].level);
end
page_table[i].pte[j].pack_entry();
`uvm_info(`gfn, $sformatf("%0s PT_%0d_%0d: %0s", privileged_mode.name(),
i, j, page_table[i].pte[j].convert2string()), UVM_HIGH)
end
end
endfunction
// Create the basic legal page table entries
virtual function void create_valid_pte();
// Randomize a valid leaf PTE entry
`DV_CHECK_RANDOMIZE_WITH_FATAL(valid_leaf_pte,
// Set the correct privileged mode
if(privileged_mode == USER_MODE) {
u == 1'b1;
} else {
// Accessing user mode page from supervisor mode is only allowed when MSTATUS.SUM and
// MSTATUS.MPRV are both 1
if(!(cfg.mstatus_sum && cfg.mstatus_mprv)) {
u == 1'b0;
}
}
// Set a,d bit to 1 avoid page/access fault exceptions
a == 1'b1;
d == 1'b1;
// Default: Readable, writable, executable page
soft xwr == R_W_EXECUTE_PAGE;
// Page is valid
v == 1'b1;
)
$cast(valid_link_pte, valid_leaf_pte.clone());
$cast(valid_data_leaf_pte, valid_leaf_pte.clone());
illegal_pte.turn_off_default_constraint();
valid_link_pte.xwr = NEXT_LEVEL_PAGE;
valid_link_pte.pack_entry();
// Set data page to read/write, but not executable
valid_data_leaf_pte.xwr = READ_WRITE_PAGE;
valid_data_leaf_pte.pack_entry();
endfunction
virtual function void inject_page_table_exception(riscv_page_table_entry#(MODE) pte, int level);
`DV_CHECK_RANDOMIZE_FATAL(exception_cfg)
`DV_CHECK_RANDOMIZE_WITH_FATAL(illegal_pte,
!(xwr inside {NEXT_LEVEL_PAGE, R_W_EXECUTE_PAGE});)
// Wrong privielge mode setting
if(exception_cfg.allow_privileged_mode_exception) begin
pte.u = ~pte.u;
end
// Random access control
// The link PTE is unchanged to avoid changing page table mappings
if(exception_cfg.allow_page_access_control_exception &&
(pte.xwr != NEXT_LEVEL_PAGE)) begin
pte.xwr = illegal_pte.xwr;
end
// Invalid page exception
if(exception_cfg.allow_invalid_page_exception) begin
pte.v = 0;
end
// Set "access" bit to zero
if(exception_cfg.allow_zero_access_bit_exception) begin
pte.a = 0;
end
// Set "dirty" bit to zero
if(exception_cfg.allow_zero_dirty_bit_exception) begin
pte.d = 0;
end
// Unaligned super leaf PTE
if(exception_cfg.allow_superpage_misaligned_exception &&
(level > 0) && (pte.xwr != NEXT_LEVEL_PAGE)) begin
bit [riscv_page_table_entry#(MODE)::VPN_WIDTH-1:0] fault_ppn;
`DV_CHECK_STD_RANDOMIZE_FATAL(fault_ppn)
if(level == 3) begin
pte.ppn2 = fault_ppn;
end else if (level == 2) begin
pte.ppn1 = fault_ppn;
end else begin
pte.ppn0 = fault_ppn;
end
end
// Illegal link PTE for the lowest level page table
if(exception_cfg.allow_leaf_link_page_exception && (level == 0)) begin
pte.xwr = NEXT_LEVEL_PAGE;
end
endfunction
// Page fault handling routine
// There are two types of page fault handling routine.
// 1. For page table error injection test, fix all PTE related to the virtual address by walking
// through the page table with the fault address.
// 2. For normal test, a page table fault typically means the program is accessing a large
// virtual address which currently not mapped a valid physical address. Need to do a
// memcpy to move data from lower physical address to the place the virtual address map to.
virtual function void gen_page_fault_handling_routine(output string instr[$]);
int unsigned level;
string load_store_unit;
bit[XLEN-1:0] bit_mask = '1;
if(MODE == SV48) begin
load_store_unit = "d";
level = 3;
bit_mask = bit_mask >> (riscv_page_table_entry#(MODE)::RSVD_WIDTH +
riscv_page_table_entry#(MODE)::PPN3_WIDTH);
end else if(MODE == SV39) begin
load_store_unit = "d";
level = 2;
bit_mask = bit_mask >> (riscv_page_table_entry#(MODE)::RSVD_WIDTH +
riscv_page_table_entry#(MODE)::PPN2_WIDTH);
end else if(MODE == SV32) begin
load_store_unit = "w";
level = 1;
bit_mask = bit_mask >> (riscv_page_table_entry#(MODE)::PPN1_WIDTH);
end else begin
`uvm_fatal(`gfn, "Unsupported MODE")
end
if(cfg.mstatus_mprv && (SATP_MODE != BARE)) begin
// Check if mstatus.mpp equals to machine mode(0x11)
// If MPP != Machine_mode and MSTATUS.MPRV = 1, load/store address translation is the same as
// the mode indicated by MPP
instr.push_back($sformatf("csrr x%0d, 0x%0x // MSTATUS", mpp_reg, MSTATUS));
instr.push_back($sformatf("srli x%0d, x%0d, 11", mpp_reg, mpp_reg));
instr.push_back($sformatf("andi x%0d, x%0d, 0x3", mpp_reg, mpp_reg));
instr.push_back($sformatf("xori x%0d, x%0d, 0x3", mpp_reg, mpp_reg));
end
// Flush TLB to force synchronization
instr.push_back("sfence.vma x0, x0");
// Start from root level, top-down fix all related PTEs
instr.push_back($sformatf("li x%0d, %0d", level_reg, level));
instr.push_back($sformatf("li x%0d, 0x%0x", mask_reg, bit_mask));
// Get the address that causes the page fault
instr.push_back($sformatf("csrr x%0d, 0x%0x # MTVAL", fault_vaddr_reg, MTVAL));
// Remove lower 4KB offset
instr.push_back($sformatf("srli x%0d, x%0d, 12", fault_vaddr_reg, fault_vaddr_reg));
// Remove the virtual address spare bits, align the VPN to the msb
instr.push_back($sformatf("slli x%0d, x%0d, %0d", fault_vaddr_reg, fault_vaddr_reg,
riscv_page_table_entry#(MODE)::VADDR_SPARE + 12));
// Starting from the root table
instr.push_back($sformatf("la x%0d, page_table_0", pte_addr_reg));
instr.push_back("fix_pte:");
// Get the VPN of the current level
// Note the VPN under process is on the msb, right shift XLEN - VPN_WIDTH to get the VPN value
instr.push_back($sformatf("srli x%0d, x%0d, %0d",
tmp_reg, fault_vaddr_reg,
XLEN - riscv_page_table_entry#(MODE)::VPN_WIDTH));
// Get the actual address offset within the page table
instr.push_back($sformatf("slli x%0d, x%0d, %0d",
tmp_reg, tmp_reg, $clog2(XLEN/8)));
// Add page table starting address and PTE offset to get PTE physical address
instr.push_back($sformatf("add x%0d, x%0d, x%0d",
pte_addr_reg, pte_addr_reg, tmp_reg));
// Load the PTE from the memory
instr.push_back($sformatf("l%0s x%0d, 0(x%0d)",
load_store_unit, pte_reg, pte_addr_reg));
// Check if the it's a link PTE (PTE[4:1] == 0)
instr.push_back($sformatf("slli x%0d, x%0d, %0d",
tmp_reg, pte_reg, XLEN - 4));
instr.push_back($sformatf("srli x%0d, x%0d, %0d",
tmp_reg, tmp_reg, XLEN - 3));
instr.push_back($sformatf("bne zero, x%0d, fix_leaf_pte", tmp_reg));
// Handle link PTE exceptions
// - If level == 0, change the link PTE to leaf PTE, and finish exception handling
instr.push_back($sformatf("beq zero, x%0d, fix_leaf_pte", level_reg));
// - If level != 0, fix the link PTE, and move to the PTE it points to
// - Override the low 10 bits with the correct link PTE setting
instr.push_back($sformatf("srli x%0d, x%0d, 10", pte_reg, pte_reg));
instr.push_back($sformatf("slli x%0d, x%0d, 10", pte_reg, pte_reg));
instr.push_back($sformatf("li x%0d, 0x%0x", tmp_reg, valid_link_pte.bits));
instr.push_back($sformatf("or x%0d, x%0d, x%0d", pte_reg, pte_reg, tmp_reg));
instr.push_back($sformatf("s%0s x%0d, 0(x%0d)", load_store_unit, pte_reg, pte_addr_reg));
// - Zero out 10 lower access control bits
instr.push_back($sformatf("srli x%0d, x%0d, 10", pte_addr_reg, pte_reg));
// - Left shift 12 bits to create the physical address
instr.push_back($sformatf("slli x%0d, x%0d, 12", pte_addr_reg, pte_addr_reg));
// - Remove the VPN of the current level
instr.push_back($sformatf("slli x%0d, x%0d, %0d", fault_vaddr_reg, fault_vaddr_reg,
riscv_page_table_entry#(MODE)::VPN_WIDTH));
// - Decrement the level, update the bit mask
instr.push_back($sformatf("addi x%0d, x%0d, -1", level_reg, level_reg));
instr.push_back($sformatf("srli x%0d, x%0d, %0d",
mask_reg, mask_reg, riscv_page_table_entry#(MODE)::VPN_WIDTH));
// - Jump to fix the PTE of the next level
instr.push_back("j fix_pte");
// fix_leaf_pte: Override the low 10 bits with the correct leaf PTE setting
instr.push_back("fix_leaf_pte:");
// Use mask to zero out lower 10 bits and unaligned VPN
instr.push_back($sformatf("not x%0d, x%0d", mask_reg, mask_reg));
instr.push_back($sformatf("and x%0d, x%0d, x%0d", pte_reg, pte_reg, mask_reg));
instr.push_back($sformatf("li x%0d, 0x%0x", tmp_reg, valid_leaf_pte.bits));
instr.push_back($sformatf("or x%0d, x%0d, x%0d", pte_reg, pte_reg, tmp_reg));
instr.push_back($sformatf("s%0s x%0d, 0(x%0d)", load_store_unit, pte_reg, pte_addr_reg));
instr.push_back("j fix_kernel_leaf_pte");
// Fix kernel leaf PTE
instr.push_back("fix_kernel_leaf_pte:");
// - Load the starting virtual address of the kernel space
instr.push_back($sformatf("la x%0d, _kernel_start", tmp_reg));
instr.push_back($sformatf("slli x%0d, x%0d, %0d", tmp_reg, tmp_reg,
XLEN - MAX_USED_VADDR_BITS));
instr.push_back($sformatf("srli x%0d, x%0d, %0d", tmp_reg, tmp_reg,
XLEN - MAX_USED_VADDR_BITS));
instr.push_back($sformatf("csrr x%0d, 0x%0x # MTVAL", fault_vaddr_reg, MTVAL));
// - Check if the fault virtual address is in the kernel space
instr.push_back($sformatf("bgeu x%0d, x%0d, fix_pte_done", tmp_reg, fault_vaddr_reg));
// - Set the PTE.u bit to 0 for kernel space PTE
instr.push_back($sformatf("li x%0d, 0x%0x", tmp_reg, 'h10));
instr.push_back($sformatf("not x%0d, x%0d", tmp_reg, tmp_reg));
instr.push_back($sformatf("and x%0d, x%0d, x%0d", pte_reg, tmp_reg, pte_reg));
instr.push_back($sformatf("s%0s x%0d, 0(x%0d)", load_store_unit, pte_reg, pte_addr_reg));
// End of page table fault handling
instr.push_back("fix_pte_done:");
// Randomly decide if run some kernel program before exiting from exception handling
// Use the low 2 bits of x30 to determine whether to skip it or not.
instr.push_back($sformatf("slli x30, x30, %0d", XLEN - 2));
instr.push_back("beqz x30, fix_pte_ret");
// Randomly decide if set MPRV to 1
instr.push_back($sformatf("slli x31, x31, %0d", XLEN - 2));
instr.push_back("beqz x30, check_mprv");
instr.push_back($sformatf("csrr x%0d, 0x%0x", tmp_reg, MSTATUS));
instr.push_back($sformatf("li x%0d, 0x%0x", mask_reg, MPRV_BIT_MASK));
instr.push_back($sformatf("not x%0d, x%0d", mask_reg, mask_reg));
instr.push_back($sformatf("or x%0d, x%0d, 0x%0x", tmp_reg, tmp_reg, mask_reg));
instr.push_back($sformatf("csrrw x%0d, 0x%0x, x%0d", tmp_reg, MSTATUS, tmp_reg));
// Run some kernel mode program before returning from exception handling
// If MPRV = 0, jump to regular kernel mode program
// If MPRV = 1, jump to kernel program with U mode mem load/store
instr.push_back($sformatf("check_mprv: li x%0d, 0x%0x", mask_reg, MPRV_BIT_MASK));
instr.push_back($sformatf("csrr x%0d, 0x%0x", tmp_reg, MSTATUS));
instr.push_back($sformatf("and x%0d, x%0d, x%0d", tmp_reg, tmp_reg, mask_reg));
instr.push_back($sformatf("beqz x%0d, j_smode", tmp_reg));
instr.push_back("jal ra, smode_ls_umem_program");
instr.push_back("j fix_pte_ret");
instr.push_back("j_smode: jal ra, smode_program");
instr.push_back("fix_pte_ret:");
// Recover the user mode GPR from kernal stack
pop_gpr_from_kernel_stack(MSTATUS, MSCRATCH, cfg.mstatus_mprv, instr);
instr.push_back("mret");
foreach(instr[i]) begin
instr[i] = instr[i].tolower();
end
endfunction
virtual function void default_page_table_setting();
num_of_page_table = new[PAGE_LEVEL];
foreach(num_of_page_table[i]) begin
num_of_page_table[i] = LINK_PTE_PER_TABLE ** (PAGE_LEVEL - i - 1);
end
endfunction
virtual function void create_page_table_list();
page_table = new[num_of_page_table.sum()];
foreach(page_table[i]) begin
page_table[i] = riscv_page_table#(MODE)::type_id::create($sformatf("page_table_%0d",i));
page_table[i].init_page_table(PTE_CNT);
page_table[i].table_id = i;
page_table[i].level = get_level(i);
end
endfunction
virtual function int get_1st_4k_table_id();
foreach(page_table[i]) begin
if(page_table[i].level == 0) return i;
end
return -1;
endfunction
// Link page table
virtual function void process_page_table(output string instr[$]);
string load_store_unit;
int pte_addr_offset;
bit [XLEN-1:0] ubit_mask = '1;
ubit_mask[4] = 1'b0; // U bit of PTE
load_store_unit = (XLEN == 32) ? "w" : "d";
// Assign the PPN of link PTE to link the page tables together
foreach(page_table[i]) begin
if (page_table[i].level == 0) break;
instr = {instr, $sformatf("la x21, page_table_%0d+2048 # Process PT_%0d", i, i)};
foreach(page_table[i].pte[j]) begin
if(j >= SUPER_LEAF_PTE_PER_TABLE) continue;
pte_addr_offset = (j * PTE_SIZE) - 2048;
`uvm_info(`gfn, $sformatf("Processing PT_%0d_PTE_%0d, v = %0d, level = %0d",
i, j, page_table[i].pte[j].v, page_table[i].level), UVM_LOW)
if(page_table[i].pte[j].xwr == NEXT_LEVEL_PAGE) begin
// Use the target table address as PPN of this PTE
// x20 holds the target table physical address
instr = {instr,
// Load the current PTE value
$sformatf("l%0s x22, %0d(x21)", load_store_unit, pte_addr_offset),
// Load the target page table physical address, PPN should be 0
$sformatf("la x20, page_table_%0d # Link PT_%0d_PTE_%0d -> PT_%0d",
get_child_table_id(i, j), i, j, get_child_table_id(i, j)),
// Right shift the address for 2 bits to the correct PPN position in PTE
$sformatf("srli x20, x20, 2"),
// Assign PPN
"or x22, x20, x22",
// Store the new PTE value
$sformatf("s%0s x22, %0d(x21)", load_store_unit, pte_addr_offset)};
end
end
end
// ---------------------------------------
// Set the kernel page u bit to 0
// ---------------------------------------
// Load the start and end address of the kernel space
if (cfg.support_supervisor_mode) begin
instr = {instr,
"la x20, _kernel_start",
"la x21, _kernel_end",
// Get the VPN of the physical address
$sformatf("slli x20, x20, %0d", XLEN - MAX_USED_VADDR_BITS),
$sformatf("srli x20, x20, %0d", XLEN - MAX_USED_VADDR_BITS + 12),
$sformatf("slli x20, x20, %0d", $clog2(XLEN)),
$sformatf("slli x21, x21, %0d", XLEN - MAX_USED_VADDR_BITS),
$sformatf("srli x21, x21, %0d", XLEN - MAX_USED_VADDR_BITS + 12),
$sformatf("slli x21, x21, %0d", $clog2(XLEN)),
// Starting from the first 4KB leaf page table
$sformatf("la x22, page_table_%0d", get_1st_4k_table_id()),
"add x20, x22, x20",
"add x21, x22, x21",
$sformatf("li x22, 0x%0x", ubit_mask),
"process_kernel_pte:",
// Load the PTE from the memory
$sformatf("l%0s x23, 0(x20)", load_store_unit),
// Unset U bit
"and x23, x23, x22",
// Save PTE back to memory
$sformatf("l%0s x23, 0(x20)", load_store_unit),
// Move to the next PTE
$sformatf("addi x20, x20, %0d", XLEN/8),
// If not the end of the kernel space, process the next PTE
"ble x20, x21, process_kernel_pte"};
end
endfunction
// If you want to create custom page table topology, override the below tasks to specify the
// level and parent of each table.
virtual function int get_level(int table_id);
for(int level = PAGE_LEVEL - 1; level >= 0; level--) begin
if(table_id < num_of_page_table[level]) return level;
table_id -= num_of_page_table[level];
end
endfunction
virtual function int get_child_table_id(int table_id, int pte_id);
return table_id * LINK_PTE_PER_TABLE + pte_id + 1;
endfunction
endclass

View file

@ -0,0 +1,575 @@
/*
* 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 privileged register class
class riscv_privil_reg extends riscv_reg#(privileged_reg_t);
`uvm_object_utils(riscv_privil_reg)
function new(string name = "");
super.new(name);
endfunction
function void init_reg(REG_T reg_name);
super.init_reg(reg_name);
case(reg_name) inside
/////////////// Machine mode reigster //////////////
// Machine ISA Register
MISA: begin
privil_level = M_LEVEL;
add_field("WARL0", 26, WARL);
add_field("WIRI", XLEN-28, WIRI);
add_field("MXL", 2, WARL);
end
// Machine Vendor ID Register
MVENDORID: begin
privil_level = M_LEVEL;
add_field("OFFSET", 7, WIRI);
add_field("BANK", XLEN-7, WIRI);
end
// Machine Architecture ID Register
MARCHID: begin
privil_level = M_LEVEL;
add_field("ARCHITECTURE_ID", XLEN, WIRI);
end
// Machine Implementation ID Register
MIMPID: begin
privil_level = M_LEVEL;
add_field("IMPLEMENTATION", XLEN, WIRI);
end
// Hart ID Register
MHARTID: begin
privil_level = M_LEVEL;
add_field("HART_ID", XLEN, WIRI);
end
// Machine Status Register
MSTATUS: begin
privil_level = M_LEVEL;
add_field("UIE", 1, WARL);
add_field("SIE", 1, WARL);
add_field("WPRI0", 1, WPRI);
add_field("MIE", 1, WARL);
add_field("UPIE", 1, WARL);
add_field("SPIE", 1, WARL);
add_field("WPRI1", 1, WPRI);
add_field("MPIE", 1, WARL);
add_field("SPP", 1, WLRL);
add_field("WPRI2", 2, WPRI);
add_field("MPP", 2, WLRL);
add_field("FS", 2, WARL);
add_field("XS", 2, WARL);
add_field("MPRV", 1, WARL);
add_field("SUM", 1, WARL);
add_field("MXR", 1, WARL);
add_field("TVM", 1, WARL);
add_field("TW", 1, WARL);
add_field("TSR", 1, WARL);
if(XLEN == 32) begin
add_field("WPRI3", 8, WPRI);
end else begin
add_field("WPRI3", 9, WPRI);
add_field("UXL", 2, WARL);
add_field("SXL", 2, WARL);
add_field("WPRI4", XLEN - 37, WPRI);
end
add_field("SD", 1, WARL);
end
// Machine Trap-Vector Base-Address Register
MTVEC: begin
privil_level = M_LEVEL;
add_field("MODE", 2, WARL);
add_field("BASE", XLEN - 2, WARL);
end
// Machine Exception Delegation Register
MEDELEG: begin
privil_level = M_LEVEL;
add_field("IAM", 1, WARL);
add_field("IAF", 1, WARL);
add_field("ILGL", 1, WARL);
add_field("BREAK", 1, WARL);
add_field("LAM", 1, WARL);
add_field("LAF", 1, WARL);
add_field("SAM", 1, WARL);
add_field("SAF", 1, WARL);
add_field("ECFU", 1, WARL);
add_field("ECFS", 1, WARL);
add_field("WARL0", 1, WARL);
add_field("ECFM", 1, WARL);
add_field("IPF", 1, WARL);
add_field("LPF", 1, WARL);
add_field("WARL1", 1, WARL);
add_field("SPF", 1, WARL);
add_field("WARL2", XLEN-16, WARL);
end
// Machine Interrupt Delegation Register
MIDELEG: begin
privil_level = M_LEVEL;
add_field("USIP", 1, WARL);
add_field("SSIP", 1, WARL);
add_field("WARL0", 1, WARL);
add_field("MSIP", 1, WARL);
add_field("UTIP", 1, WARL);
add_field("STIP", 1, WARL);
add_field("WARL1", 1, WARL);
add_field("MTIP", 1, WARL);
add_field("UEIP", 1, WARL);
add_field("SEIP", 1, WARL);
add_field("WARL2", 1, WARL);
add_field("MEIP", 1, WARL);
add_field("WARL3", XLEN-12, WARL);
end
// Machine trap-enable register
MIP: begin
privil_level = M_LEVEL;
add_field("USIP", 1, WARL);
add_field("SSIP", 1, WARL);
add_field("WIRI0", 1, WIRI);
add_field("MSIP", 1, WARL);
add_field("UTIP", 1, WARL);
add_field("STIP", 1, WARL);
add_field("WPRI1", 1, WPRI);
add_field("MTIP", 1, WARL);
add_field("UEIP", 1, WARL);
add_field("SEIP", 1, WARL);
add_field("WIRI2", 1, WIRI);
add_field("MEIP", 1, WARL);
add_field("WIRI3", XLEN - 12, WIRI);
end
// Machine interrupt-enable register
MIE: begin
privil_level = M_LEVEL;
add_field("USIE", 1, WARL);
add_field("SSIE", 1, WARL);
add_field("WPRI0", 1, WPRI);
add_field("MSIE", 1, WARL);
add_field("UTIE", 1, WARL);
add_field("STIE", 1, WARL);
add_field("WPRI1", 1, WPRI);
add_field("MTIE", 1, WARL);
add_field("UEIE", 1, WARL);
add_field("SEIE", 1, WARL);
add_field("WPRI2", 1, WPRI);
add_field("MEIE", 1, WARL);
add_field("WPRI3", XLEN - 12, WPRI);
end
// Cycle Count Register
MCYCLE: begin
privil_level = M_LEVEL;
add_field("MCYCLE", 64, WIRI);
end
// Instruction Count Register
MINSTRET: begin
privil_level = M_LEVEL;
add_field("MINSTRET", 64, WIRI);
end
// Cycle Count Register - RV32I only
MCYCLEH: begin
privil_level = M_LEVEL;
add_field("MCYCLEH", 32, WIRI);
end
// Instruction Count Register - RV32I only
MINSTRETH: begin
privil_level = M_LEVEL;
add_field("MINSTRETH", 32, WIRI);
end
// Hardware Performance Monitor Counters
[MHPMCOUNTER3:MHPMCOUNTER31]: begin
privil_level = M_LEVEL;
add_field($sformatf("%s", reg_name.name()), XLEN, WIRI);
end
// Hardware Performance Monitor Events
[MHPMEVENT3:MHPMEVENT31]: begin
privil_level = M_LEVEL;
add_field($sformatf("%s", reg_name.name()), XLEN, WARL);
end
// Hardware Performance Monitor Counters - RV32I only
[MHPMCOUNTER3H:MHPMCOUNTER31H]: begin
if(XLEN != 32) begin
`uvm_fatal(get_full_name(), $sformatf("Register %s is only in RV32I", reg_name.name()))
end
privil_level = M_LEVEL;
add_field($sformatf("%s", reg_name.name()), 32, WIRI);
end
// Machine Counter Enable Register
MCOUNTEREN: begin
privil_level = M_LEVEL;
add_field("CY", 1, WARL);
add_field("TM", 1, WARL);
add_field("IR", 1, WARL);
add_field("HPM3", 1, WARL);
add_field("HPM4", 1, WARL);
add_field("HPM5", 1, WARL);
add_field("HPM6", 1, WARL);
add_field("HPM7", 1, WARL);
add_field("HPM8", 1, WARL);
add_field("HPM9", 1, WARL);
add_field("HPM10", 1, WARL);
add_field("HPM11", 1, WARL);
add_field("HPM12", 1, WARL);
add_field("HPM13", 1, WARL);
add_field("HPM14", 1, WARL);
add_field("HPM15", 1, WARL);
add_field("HPM16", 1, WARL);
add_field("HPM17", 1, WARL);
add_field("HPM18", 1, WARL);
add_field("HPM19", 1, WARL);
add_field("HPM20", 1, WARL);
add_field("HPM21", 1, WARL);
add_field("HPM22", 1, WARL);
add_field("HPM23", 1, WARL);
add_field("HPM24", 1, WARL);
add_field("HPM25", 1, WARL);
add_field("HPM26", 1, WARL);
add_field("HPM27", 1, WARL);
add_field("HPM28", 1, WARL);
add_field("HPM29", 1, WARL);
add_field("HPM30", 1, WARL);
add_field("HPM31", 1, WARL);
if(XLEN == 64) begin
add_field("WPRI", 32, WPRI);
end
end
// Machine Scratch Register
MSCRATCH: begin
privil_level = M_LEVEL;
add_field("MSCRATCH", XLEN, WARL);
end
// Machine Exception Program Counter
MEPC: begin
privil_level = M_LEVEL;
add_field("BASE", XLEN, WARL);
end
// Machine Cause Register
MCAUSE: begin
privil_level = M_LEVEL;
add_field("CODE", 4, WLRL);
add_field("WLRL", XLEN-5, WLRL);
add_field("INTERRUPT", 1, WARL);
end
// Machine Trap Value
MTVAL: begin
privil_level = M_LEVEL;
add_field("VALUE", XLEN, WARL);
end
// Physical Memory Protection Configuration Register
PMPCFG0: begin
privil_level = M_LEVEL;
add_field("PMP0CFG", 8, WARL);
add_field("PMP1CFG", 8, WARL);
add_field("PMP2CFG", 8, WARL);
add_field("PMP3CFG", 8, WARL);
if(XLEN==64) begin
add_field("PMP4CFG", 8, WARL);
add_field("PMP5CFG", 8, WARL);
add_field("PMP6CFG", 8, WARL);
add_field("PMP7CFG", 8, WARL);
end
end
// Physical Memory Protection Configuration Register
PMPCFG1: begin
privil_level = M_LEVEL;
if(XLEN==64) begin
add_field("PMP8CFG", 8, WARL);
add_field("PMP9CFG", 8, WARL);
add_field("PMP10CFG", 8, WARL);
add_field("PMP11CFG", 8, WARL);
add_field("PMP12CFG", 8, WARL);
add_field("PMP13CFG", 8, WARL);
add_field("PMP14CFG", 8, WARL);
add_field("PMP15CFG", 8, WARL);
end else begin
add_field("PMP4CFG", 8, WARL);
add_field("PMP5CFG", 8, WARL);
add_field("PMP6CFG", 8, WARL);
add_field("PMP7CFG", 8, WARL);
end
end
// Physical Memory Protection Configuration Register
PMPCFG2: begin
if(XLEN!=32) begin
`uvm_fatal(get_full_name(), "CSR PMPCFG2 only exists in RV32.")
end
privil_level = M_LEVEL;
add_field("PMP8CFG", 8, WARL);
add_field("PMP9CFG", 8, WARL);
add_field("PMP10CFG", 8, WARL);
add_field("PMP11CFG", 8, WARL);
end
// Physical Memory Protection Configuration Register
PMPCFG3: begin
if(XLEN!=32) begin
`uvm_fatal(get_full_name(), "CSR PMPCFG3 only exists in RV32.")
end
privil_level = M_LEVEL;
add_field("PMP12CFG", 8, WARL);
add_field("PMP13CFG", 8, WARL);
add_field("PMP14CFG", 8, WARL);
add_field("PMP15CFG", 8, WARL);
end
// Physical Memory Protection Configuration Registers
[PMPADDR0:PMPADDR15]: begin
privil_level = M_LEVEL;
if(XLEN==64) begin
add_field("ADDRESS", 54, WARL);
add_field("WIRI", 10, WIRI);
end else begin
add_field("ADDRESS", 32, WARL);
end
end
/////////////// Supervisor mode reigster //////////////
// Supervisor status register
SSTATUS: begin
privil_level = S_LEVEL;
add_field("UIE", 1, WARL);
add_field("SIE", 1, WARL);
add_field("WPRI0", 2, WPRI);
add_field("UPIE", 1, WARL);
add_field("SPIE", 1, WARL);
add_field("WPRI1", 2, WPRI);
add_field("SPP", 1, WLRL);
add_field("WPRI2", 4, WPRI);
add_field("FS", 2, WARL);
add_field("XS", 2, WARL);
add_field("WPRI3", 1, WPRI);
add_field("SUM", 1, WARL);
add_field("MXR", 1, WARL);
if(XLEN == 32) begin
add_field("WPRI4", 11, WPRI);
end else begin
add_field("WPRI4", 12, WPRI);
add_field("UXL", 2, WARL);
add_field("WPRI4", XLEN - 35, WPRI);
end
add_field("SD", 1, WARL);
end
// Supervisor Trap Vector Base Address Register
STVEC: begin
privil_level = S_LEVEL;
add_field("MODE", 2, WARL);
add_field("BASE", XLEN-2, WLRL);
end
// Supervisor Exception Delegation Register
SEDELEG: begin
privil_level = S_LEVEL;
add_field("IAM", 1, WARL);
add_field("IAF", 1, WARL);
add_field("II", 1, WARL);
add_field("WPRI0", 1, WPRI);
add_field("LAM", 1, WARL);
add_field("LAF", 1, WARL);
add_field("SAM", 1, WARL);
add_field("SAF", 1, WARL);
add_field("ECFU", 1, WARL);
add_field("WPRI1", 1, WPRI);
add_field("WARL0", 1, WARL);
add_field("WPRI2", 1, WPRI);
add_field("IPF", 1, WARL);
add_field("LPF", 1, WARL);
add_field("WARL1", 1, WARL);
add_field("SPF", 1, WARL);
add_field("WARL2", XLEN-16, WARL);
end
// Supervisor Interrupt Delegation Register
SIDELEG: begin
privil_level = S_LEVEL;
add_field("USIP", 1, WARL);
add_field("SSIP", 1, WARL);
add_field("WARL0", 1, WARL);
add_field("WPRI0", 1, WPRI);
add_field("UTIP", 1, WARL);
add_field("STIP", 1, WARL);
add_field("WARL1", 1, WARL);
add_field("WPRI1", 1, WPRI);
add_field("UEIP", 1, WARL);
add_field("SEIP", 1, WARL);
add_field("WARL2", 1, WARL);
add_field("WPRI2", 1, WPRI);
add_field("WARL3", XLEN-12, WARL);
end
// Supervisor trap-enable register
SIP: begin
privil_level = S_LEVEL;
add_field("USIP", 1, WARL);
add_field("SSIP", 1, WARL);
add_field("WPRI0", 2, WPRI);
add_field("UTIP", 1, WARL);
add_field("STIP", 1, WARL);
add_field("WPRI1", 2, WPRI);
add_field("UEIP", 1, WARL);
add_field("SEIP", 1, WARL);
add_field("WPRI2", 2, WPRI);
add_field("WPRI3", XLEN - 12, WPRI);
end
// Supervisor interrupt-enable register
SIE: begin
privil_level = S_LEVEL;
add_field("USIE", 1, WARL);
add_field("SSIE", 1, WARL);
add_field("WPRI0", 2, WPRI);
add_field("UTIE", 1, WARL);
add_field("STIE", 1, WARL);
add_field("WPRI1", 2, WPRI);
add_field("UEIE", 1, WARL);
add_field("SEIE", 1, WARL);
add_field("WPRI2", XLEN - 10, WPRI);
end
// Supervisor Counter Enable Register
SCOUNTEREN: begin
privil_level = S_LEVEL;
add_field("CY", 1, WARL);
add_field("TM", 1, WARL);
add_field("IR", 1, WARL);
add_field("HPM3", 1, WARL);
add_field("HPM4", 1, WARL);
add_field("HPM5", 1, WARL);
add_field("HPM6", 1, WARL);
add_field("HPM7", 1, WARL);
add_field("HPM8", 1, WARL);
add_field("HPM9", 1, WARL);
add_field("HPM10", 1, WARL);
add_field("HPM11", 1, WARL);
add_field("HPM12", 1, WARL);
add_field("HPM13", 1, WARL);
add_field("HPM14", 1, WARL);
add_field("HPM15", 1, WARL);
add_field("HPM16", 1, WARL);
add_field("HPM17", 1, WARL);
add_field("HPM18", 1, WARL);
add_field("HPM19", 1, WARL);
add_field("HPM20", 1, WARL);
add_field("HPM21", 1, WARL);
add_field("HPM22", 1, WARL);
add_field("HPM23", 1, WARL);
add_field("HPM24", 1, WARL);
add_field("HPM25", 1, WARL);
add_field("HPM26", 1, WARL);
add_field("HPM27", 1, WARL);
add_field("HPM28", 1, WARL);
add_field("HPM29", 1, WARL);
add_field("HPM30", 1, WARL);
add_field("HPM31", 1, WARL);
if(XLEN == 64) begin
add_field("WPRI", 32, WPRI);
end
end
// Supervisor Scratch Register
SSCRATCH: begin
privil_level = S_LEVEL;
add_field("SSCRATCH", XLEN, WARL);
end
// Supervisor Exception Program Counter
SEPC: begin
privil_level = S_LEVEL;
add_field("BASE", XLEN, WARL);
end
// Supervisor Cause Register
SCAUSE: begin
privil_level = S_LEVEL;
add_field("CODE", 4, WLRL);
add_field("WLRL", XLEN-5, WLRL);
add_field("INTERRUPT", 1, WARL);
end
// Supervisor Trap Value
STVAL: begin
privil_level = S_LEVEL;
add_field("VALUE", XLEN, WARL);
end
// Supervisor Address Translation and Protection
SATP: begin
privil_level = S_LEVEL;
if(XLEN == 32) begin
add_field("PPN", 22, WARL);
add_field("ASID", 9, WARL);
add_field("MODE", 1, WARL);
end else begin
add_field("PPN", 44, WARL);
add_field("ASID", 16, WARL);
add_field("MODE", 4, WARL);
end
end
/////////////// User mode reigster //////////////
// User Status Register
USTATUS: begin
privil_level = U_LEVEL;
add_field("UIE", 1, WARL);
add_field("WPRI0", 3, WPRI);
add_field("UPIE", 1, WARL);
add_field("WPRI1", XLEN-5, WPRI);
end
// User Trap Vector Base Address Register
UTVEC: begin
privil_level = U_LEVEL;
add_field("MODE", 2, WARL);
add_field("BASE", XLEN-2, WLRL);
end
// User Interrupt-Enable register
UIE: begin
privil_level = U_LEVEL;
add_field("USIE", 1, WARL);
add_field("WPRI0", 3, WPRI);
add_field("UTIE", 1, WARL);
add_field("WPRI1", 3, WPRI);
add_field("UEIE", 1, WARL);
add_field("WPRI2", XLEN-9, WPRI);
end
// User Trap-Enable register
UIP: begin
privil_level = U_LEVEL;
add_field("USIP", 1, WARL);
add_field("WPRI0", 3, WPRI);
add_field("UTIP", 1, WARL);
add_field("WPRI1", 3, WPRI);
add_field("UEIP", 1, WARL);
add_field("WPRI2", XLEN-9, WPRI);
end
// User Scratch Register
USCRATCH: begin
privil_level = U_LEVEL;
add_field("MSCRATCH", XLEN, WARL);
end
// User Exception Program Counter
UEPC: begin
privil_level = U_LEVEL;
add_field("BASE", XLEN, WARL);
end
// User Cause Register
UCAUSE: begin
privil_level = U_LEVEL;
add_field("CODE", 4, WLRL);
add_field("WLRL", XLEN-5, WLRL);
add_field("INTERRUPT", 1, WARL);
end
// User Trap Value
UTVAL: begin
privil_level = U_LEVEL;
add_field("VALUE", XLEN, WARL);
end
default:
`uvm_fatal(get_full_name(), $sformatf("reg %0s is not supported yet", reg_name.name()))
endcase
set_wiri_wpri_fields();
endfunction
// Hardwire all WIRI and WPRI fields to '0
virtual function void set_wiri_wpri_fields();
foreach(fld[i]) begin
if(fld[i].access_type inside {WIRI, WPRI}) begin
set_field(fld[i].get_name(), '0, 1'b1);
end
end
endfunction
endclass

View file

@ -0,0 +1,169 @@
/*
* 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.
*/
// This class provides some common routines for privileged mode operations
class riscv_privileged_common_seq extends uvm_sequence;
riscv_instr_gen_config cfg;
riscv_privil_reg mstatus;
riscv_privil_reg mie;
riscv_privil_reg sstatus;
riscv_privil_reg sie;
riscv_privil_reg ustatus;
riscv_privil_reg uie;
`uvm_object_utils(riscv_privileged_common_seq)
function new(string name = "");
super.new(name);
endfunction
virtual function void enter_privileged_mode(input privileged_mode_t mode,
output string instrs[$]);
string label = format_string({"init_", mode.name(), ":"}, LABEL_STR_LEN);
string ret_instr[] = {"mret"};
riscv_privil_reg regs[$];
label = label.tolower();
setup_mmode_reg(mode, regs);
if(mode != MACHINE_MODE) begin
setup_smode_reg(mode, regs);
setup_satp(instrs);
ret_instr.shuffle();
end
gen_csr_instr(regs, instrs);
// Use mret/sret to switch to the target privileged mode
instrs.push_back(ret_instr[0]);
foreach(instrs[i]) begin
instrs[i] = {indent, instrs[i]};
end
instrs.push_front(label);
endfunction
virtual function void setup_mmode_reg(privileged_mode_t mode, ref riscv_privil_reg regs[$]);
mstatus = riscv_privil_reg::type_id::create("mstatus");
mie = riscv_privil_reg::type_id::create("mie");
mstatus.init_reg(MSTATUS);
mie.init_reg(MIE);
`DV_CHECK_RANDOMIZE_FATAL(mstatus, "cannot randomize mstatus");
if(XLEN==64) begin
mstatus.set_field("UXL", 2'b10);
mstatus.set_field("SXL", 2'b10);
end
mstatus.set_field("XS", 0);
mstatus.set_field("FS", 0);
mstatus.set_field("SD", 0);
mstatus.set_field("UIE", 0);
mstatus.set_field("MPRV", cfg.mstatus_mprv);
mstatus.set_field("MXR", cfg.mstatus_mxr);
mstatus.set_field("SUM", cfg.mstatus_sum);
mstatus.set_field("TVM", cfg.mstatus_tvm);
// Set the previous privileged mode as the target mode
mstatus.set_field("MPP", mode);
if(mode == USER_MODE)
mstatus.set_field("SPP", 0);
else
mstatus.set_field("SPP", 1);
// Enable interrupt
mstatus.set_field("MPIE", cfg.enable_interrupt);
mstatus.set_field("MIE", cfg.enable_interrupt);
mstatus.set_field("SPIE", cfg.enable_interrupt);
mstatus.set_field("SIE", cfg.enable_interrupt);
mstatus.set_field("UPIE", cfg.enable_interrupt);
mstatus.set_field("UIE", riscv_instr_pkg::support_umode_trap);
regs.push_back(mstatus);
// Enable external and timer interrupt
mie.set_field("UEIE", cfg.enable_interrupt);
mie.set_field("SEIE", cfg.enable_interrupt);
mie.set_field("MEIE", cfg.enable_interrupt);
mie.set_field("USIE", cfg.enable_interrupt);
mie.set_field("SSIE", cfg.enable_interrupt);
mie.set_field("MSIE", cfg.enable_interrupt);
regs.push_back(mie);
endfunction
virtual function void setup_smode_reg(privileged_mode_t mode, ref riscv_privil_reg regs[$]);
sstatus = riscv_privil_reg::type_id::create("sstatus");
sie = riscv_privil_reg::type_id::create("sie");
sstatus.init_reg(SSTATUS);
sie.init_reg(SIE);
`DV_CHECK_RANDOMIZE_FATAL(sstatus, "cannot randomize sstatus")
sstatus.set_field("SPIE", cfg.enable_interrupt);
sstatus.set_field("SIE", cfg.enable_interrupt);
sstatus.set_field("UPIE", cfg.enable_interrupt);
sstatus.set_field("UIE", riscv_instr_pkg::support_umode_trap);
if(XLEN==64) begin
sstatus.set_field("UXL", 2'b10);
end
sstatus.set_field("XS", 0);
sstatus.set_field("FS", 0);
sstatus.set_field("SD", 0);
sstatus.set_field("UIE", 0);
if(mode == USER_MODE)
sstatus.set_field("SPP", 0);
else
sstatus.set_field("SPP", 1);
regs.push_back(sstatus);
// Enable external and timer interrupt
sie.set_field("UEIE", cfg.enable_interrupt);
sie.set_field("SEIE", cfg.enable_interrupt);
sie.set_field("USIE", cfg.enable_interrupt);
sie.set_field("SSIE", cfg.enable_interrupt);
regs.push_back(sie);
endfunction
virtual function void setup_umode_reg(privileged_mode_t mode, ref riscv_privil_reg regs[$]);
ustatus = riscv_privil_reg::type_id::create("ustatus");
uie = riscv_privil_reg::type_id::create("uie");
ustatus.init_reg(USTATUS);
uie.init_reg(UIE);
`DV_CHECK_RANDOMIZE_FATAL(ustatus, "cannot randomize ustatus")
ustatus.set_field("UIE", cfg.enable_interrupt);
ustatus.set_field("UPIE", cfg.enable_interrupt);
regs.push_back(ustatus);
uie.set_field("UEIE", cfg.enable_interrupt);
uie.set_field("USIE", cfg.enable_interrupt);
regs.push_back(uie);
endfunction
virtual function void gen_csr_instr(riscv_privil_reg regs[$], ref string instrs[$]);
foreach(regs[i]) begin
instrs.push_back($sformatf("li a0, 0x%0x", regs[i].get_val()));
instrs.push_back($sformatf("csrw 0x%0x, a0 # %0s",
regs[i].reg_name, regs[i].reg_name.name()));
end
endfunction
virtual function void setup_satp(ref string instrs[$]);
riscv_privil_reg satp;
bit [XLEN-1:0] satp_ppn_mask;
if(SATP_MODE == BARE) return;
satp = riscv_privil_reg::type_id::create("satp");
satp.init_reg(SATP);
satp.set_field("MODE", SATP_MODE);
instrs.push_back($sformatf("li a0, 0x%0x", satp.get_val()));
instrs.push_back($sformatf("csrw 0x%0x, a0 // satp", SATP));
satp_ppn_mask = '1 >> (XLEN - satp.get_field_by_name("PPN").bit_width);
// Load the root page table physical address
instrs.push_back("la a0, page_table_0");
// Right shift to get PPN at 4k granularity
instrs.push_back("srli a0, a0, 12");
instrs.push_back($sformatf("li a1, 0x%0x", satp_ppn_mask));
instrs.push_back("and a0, a0, a1");
// Set the PPN field for SATP
instrs.push_back($sformatf("csrs 0x%0x, a0 // satp", SATP));
endfunction
endclass

View file

@ -0,0 +1,95 @@
/*
* 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.
*/
class riscv_rand_instr extends riscv_instr_base;
riscv_instr_gen_config cfg;
// Some additional reserved registers
riscv_reg_t reserved_rd[];
`uvm_object_utils(riscv_rand_instr)
constraint instr_c {
solve instr_name before imm;
solve instr_name before rs1;
solve instr_name before rs2;
!(instr_name inside {riscv_instr_pkg::unsupported_instr});
category inside {LOAD, STORE, SHIFT, ARITHMETIC, LOGICAL, BRANCH, COMPARE, CSR, SYSTEM, SYNCH};
group inside {riscv_instr_pkg::supported_isa};
// Avoid using any special purpose register as rd, those registers are reserved for
// special instructions
!(rd inside {cfg.reserved_regs});
if(reserved_rd.size() > 0) {
!(rd inside {reserved_rd});
}
// Compressed instruction may use the same CSR for both rs1 and rd
if(group inside {RV32C, RV64C, RV128C, RV32FC, RV32DC}) {
!(rs1 inside {cfg.reserved_regs, reserved_rd});
}
// Below instrutions will modify stack pointer, not allowed in normal instruction stream.
// It can be used in stack operation instruction stream.
!(instr_name inside {C_SWSP, C_SDSP, C_ADDI16SP});
// Avoid using reserved registers as rs1 (base address)
if(category inside {LOAD, STORE}) {
!(rs1 inside {reserved_rd, cfg.reserved_regs, ZERO});
}
if(!cfg.enable_sfence) {
instr_name != SFENCE_VMA;
}
if(cfg.no_fence) {
!(instr_name inside {FENCE, FENCEI, SFENCE_VMA});
}
// TODO: Support C_ADDI4SPN
instr_name != C_ADDI4SPN;
}
constraint rvc_csr_c {
// Registers specified by the three-bit rs1, rs2, and rd fields of the CIW, CL, CS,
// and CB formats
if(format inside {CIW_FORMAT, CL_FORMAT, CS_FORMAT, CB_FORMAT}) {
rs1 inside {[S0:A5]};
rs2 inside {[S0:A5]};
rd inside {[S0:A5]};
}
// C_ADDI16SP is only valid when rd == SP
if(instr_name == C_ADDI16SP) {
rd == SP;
}
}
constraint constraint_cfg_knob_c {
if(cfg.no_csr_instr == 1) {
category != CSR;
}
if(cfg.no_ebreak) {
instr_name != EBREAK;
instr_name != C_EBREAK;
}
// Below previleged instruction is not generated by default
!(instr_name inside {ECALL, WFI, URET, SRET, MRET});
if(cfg.no_load_store) {
category != LOAD;
category != STORE;
}
if(cfg.no_branch_jump) {
category != BRANCH;
}
}
`uvm_object_new
endclass

159
vendor/google_riscv-dv/src/riscv_reg.sv vendored Normal file
View file

@ -0,0 +1,159 @@
/*
* 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.
*/
// --------------------------------------------------
// Light weight RISC-V register class library
// --------------------------------------------------
// Base class for RISC-V register field
class riscv_reg_field extends uvm_object;
int unsigned bit_width;
bit [XLEN-1:0] reset_val;
rand bit [XLEN-1:0] val;
reg_field_access_t access_type;
bit hard_wired;
constraint zero_reserved_field_c {
(access_type == WPRI) -> (val == '0);
}
constraint hardwired_fld_c {
(hard_wired) -> (val == reset_val);
}
`uvm_object_utils(riscv_reg_field)
function new(string name = "");
super.new(name);
endfunction
function string convert2string();
return($sformatf("%0s bit_width:%0d val:0x%0x type:%0s",
get_name(), bit_width, val, access_type));
endfunction
function void post_randomize();
bit [XLEN-1:0] mask = '1;
mask = mask >> (XLEN-bit_width);
val = mask & val;
endfunction
endclass
// Base class for RISC-V register
class riscv_reg#(type REG_T = privileged_reg_t) extends uvm_object;
REG_T reg_name;
riscv_csr_t offset;
privileged_level_t privil_level;
bit [XLEN-1:0] val;
rand riscv_reg_field fld[$];
`uvm_object_param_utils(riscv_reg#(REG_T))
function new(string name = "");
super.new(name);
endfunction
virtual function void init_reg(REG_T reg_name);
this.reg_name = reg_name;
offset = riscv_csr_t'(reg_name);
endfunction
virtual function bit[XLEN-1:0] get_val();
int total_len;
total_len = fld.sum() with (item.bit_width);
if(total_len != XLEN) begin
foreach(fld[i])
$display(fld[i].convert2string());
`uvm_fatal(get_full_name(),
$sformatf("Total field length %0d != XLEN %0d", total_len, XLEN))
end
val = '0;
foreach(fld[i]) begin
val = (val << fld[i].bit_width) | fld[i].val;
end
return val;
endfunction
virtual function void add_field(string fld_name, int unsigned bit_width,
reg_field_access_t access_type,
bit [XLEN-1:0] reset_val = '0);
riscv_reg_field new_fld;
new_fld = riscv_reg_field::type_id::create(fld_name);
new_fld.bit_width = bit_width;
new_fld.access_type = access_type;
new_fld.reset_val = reset_val;
fld.push_front(new_fld);
endfunction
virtual function void set_field(string fld_name, bit[XLEN-1:0] val, bit hard_wired = 1'b0);
foreach(fld[i]) begin
if(fld_name == fld[i].get_name()) begin
fld[i].val = val;
fld[i].hard_wired = hard_wired;
if(hard_wired) begin
fld[i].reset_val = val;
end
return;
end
end
`uvm_fatal(get_full_name(), $sformatf("Cannot match found field %0s", fld_name))
endfunction
virtual function riscv_reg_field get_field_by_name(string fld_name);
foreach(fld[i]) begin
if(fld_name == fld[i].get_name()) begin
return fld[i];
end
end
`uvm_fatal(get_full_name(), $sformatf("Cannot match found field %0s", fld_name))
endfunction
virtual function void rand_field(string fld_name);
riscv_reg_field fld_hd = get_field_by_name(fld_name);
`DV_CHECK_RANDOMIZE_FATAL(fld_hd)
endfunction
virtual function void set_field_rand_mode(string fld_name, bit rand_on);
riscv_reg_field fld_hd = get_field_by_name(fld_name);
fld_hd.rand_mode(rand_on);
endfunction
virtual function void reset();
foreach(fld[i]) begin
fld[i].val = fld[i].reset_val;
end
endfunction
virtual function void set_val(bit [XLEN-1:0] val);
foreach(fld[i]) begin
if(!fld[i].hard_wired) begin
// Assign the valid msb to the field
fld[i].val = (val >> (XLEN - fld[i].bit_width));
`uvm_info(get_full_name(), $sformatf(
"Assign field %0s, bit_width:%0d, reg_val 0x%0x, fld_val:0x%0x",
fld[i].get_name(), fld[i].bit_width, val, fld[i].val), UVM_LOW)
end
val = val << fld[i].bit_width;
end
endfunction
virtual function void set_wiri_wpri_fields();
endfunction
endclass

View file

@ -0,0 +1,111 @@
/*
* 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 test
class riscv_instr_base_test extends uvm_test;
riscv_instr_gen_config cfg;
string test_opts;
string asm_file_name = "riscv_asm_test";
riscv_asm_program_gen asm_gen;
string instr_seq;
`uvm_component_utils(riscv_instr_base_test)
function new(string name="", uvm_component parent=null);
super.new(name, parent);
void'($value$plusargs("asm_file_name=%0s", asm_file_name));
endfunction
virtual function void build_phase(uvm_phase phase);
super.build_phase(phase);
cfg = riscv_instr_gen_config::type_id::create("cfg");
uvm_config_db#(riscv_instr_gen_config)::set(null, "*", "instr_cfg", cfg);
if(cfg.asm_test_suffix != "")
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
endfunction
function void report_phase(uvm_phase phase);
uvm_report_server rs;
int error_count;
rs = uvm_report_server::get_server();
error_count = rs.get_severity_count(UVM_WARNING) +
rs.get_severity_count(UVM_ERROR) +
rs.get_severity_count(UVM_FATAL);
if (error_count == 0) begin
`uvm_info("", "TEST PASSED", UVM_NONE);
end else begin
`uvm_info("", "TEST FAILED", UVM_NONE);
end
super.report_phase(phase);
endfunction
function void get_directed_instr_stream_opts();
string instr_name;
int ratio;
string cmd_opts_prefix;
int i = 0;
while(1) begin
cmd_opts_prefix = $sformatf("directed_instr_%0d", i);
if($value$plusargs({cmd_opts_prefix, "=%0s"}, instr_name) &&
$value$plusargs({cmd_opts_prefix, "_ratio=%0d"}, ratio)) begin
asm_gen.add_directed_instr_stream(instr_name, ratio);
end else begin
break;
end
`uvm_info(`gfn, $sformatf("Got directed instr[%0d] %0s, ratio = %0d/1000",
i, instr_name, ratio), UVM_LOW)
i++;
end
endfunction
virtual function void apply_directed_instr();
endfunction
task run_phase(uvm_phase phase);
int fd;
for(int i = 0; i < cfg.num_of_tests; i++) begin
string test_name;
cfg = riscv_instr_gen_config::type_id::create("cfg");
randomize_cfg();
asm_gen = riscv_asm_program_gen::type_id::create("asm_gen");
get_directed_instr_stream_opts();
asm_gen.cfg = cfg;
test_name = $sformatf("%0s.%0d.S", asm_file_name, i);
apply_directed_instr();
`uvm_info(`gfn, "All directed instruction is applied", UVM_LOW)
asm_gen.gen_program();
asm_gen.gen_test_file(test_name);
end
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

@ -0,0 +1,28 @@
/*
* 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.
*/
module riscv_instr_gen_tb_top;
`include "uvm_macros.svh"
import uvm_pkg::*;
import riscv_instr_test_pkg::*;
initial begin
run_test();
end
endmodule

View file

@ -0,0 +1,250 @@
/*
* 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 Test Library
//------------------------------------------------------------------------------------------------
// riscv_arithmetic_basic_test :
// - Simple arithmetic instruction test, [machine mode]
//
// riscv_machine_mode_rand_test :
// - Random instruction test, include sub-programs, branch, load/store [machine mode]
//
// riscv_privileged_mode_rand_test :
// - Similar to riscv_machine_mode_rand_test, run in supervisor mode or user mode if supported
//
// riscv_rand_instr_test :
// - A full random test, with a mix of directed instruction stream
//
// riscv_rand_jump_test :
// - Random jump among a large number of sub programs
//
// riscv_mmu_stress_test:
// - A mixed of intense load/store instructions
//
// riscv_page_table_exception_test:
// - Running test in privileged mode with page table exceptions
//
// riscv_no_fence_test
// - Random instruction with fence disabled, allow more intense instruction execution
//
// riscv_sfence_exception_test
// - Random instruction with sfence exception. sfence.vma could be excuted in non-supervisor mode
// or mstatus.tvm is set.
//
// riscv_illegal_instr_test:
// - Random instruction mixed with illegal instructions
//
// riscv_hint_instr_test:
// - Random instruction mixed with HINT instructions
//
// riscv_privileged_csr_test:
// - To be released soon
//
//================================================================================================
class riscv_arithmetic_basic_test extends riscv_instr_base_test;
`uvm_component_utils(riscv_arithmetic_basic_test)
`uvm_component_new
virtual function void randomize_cfg();
cfg.instr_cnt = 10000;
cfg.num_of_sub_program = 0;
cfg.no_fence = 1;
cfg.no_data_page = 1'b1;
cfg.no_branch_jump = 1'b1;
`DV_CHECK_RANDOMIZE_WITH_FATAL(cfg,
init_privileged_mode == MACHINE_MODE;
max_nested_loop == 0;)
`uvm_info(`gfn, $sformatf("riscv_instr_gen_config is randomized:\n%0s",
cfg.sprint()), UVM_LOW)
endfunction
endclass
class riscv_machine_mode_rand_test extends riscv_instr_base_test;
`uvm_component_utils(riscv_machine_mode_rand_test)
`uvm_component_new
virtual function void randomize_cfg();
cfg.instr_cnt = 10000;
cfg.num_of_sub_program = 5;
`DV_CHECK_RANDOMIZE_WITH_FATAL(cfg,
init_privileged_mode == MACHINE_MODE;
max_nested_loop == 0;)
`uvm_info(`gfn, $sformatf("riscv_instr_gen_config is randomized:\n%0s",
cfg.sprint()), UVM_LOW)
endfunction
virtual function void apply_directed_instr();
// Add load/store instruction stream
asm_gen.add_directed_instr_stream("riscv_load_store_rand_instr_stream", 10);
endfunction
endclass
class riscv_privileged_mode_rand_test extends riscv_instr_base_test;
`uvm_component_utils(riscv_privileged_mode_rand_test)
`uvm_component_new
virtual function void randomize_cfg();
cfg.instr_cnt = 10000;
cfg.num_of_sub_program = 5;
`DV_CHECK_RANDOMIZE_WITH_FATAL(cfg,
init_privileged_mode != MACHINE_MODE;
max_nested_loop == 0;)
`uvm_info(`gfn, $sformatf("riscv_instr_gen_config is randomized:\n%0s",
cfg.sprint()), UVM_LOW)
endfunction
virtual function void apply_directed_instr();
// Add load/store instruction stream
asm_gen.add_directed_instr_stream("riscv_load_store_rand_instr_stream", 10);
endfunction
endclass
class riscv_rand_instr_test extends riscv_instr_base_test;
`uvm_component_utils(riscv_rand_instr_test)
`uvm_component_new
virtual function void randomize_cfg();
cfg.instr_cnt = 10000;
cfg.num_of_sub_program = 5;
`DV_CHECK_RANDOMIZE_WITH_FATAL(cfg,
max_nested_loop == 2;)
`uvm_info(`gfn, $sformatf("riscv_instr_gen_config is randomized:\n%0s",
cfg.sprint()), UVM_LOW)
endfunction
virtual function void apply_directed_instr();
// Mix below directed instructino streams with the random instructions
asm_gen.add_directed_instr_stream("riscv_load_store_rand_instr_stream", 4);
asm_gen.add_directed_instr_stream("riscv_loop_instr", 4);
asm_gen.add_directed_instr_stream("riscv_hazard_instr_stream", 4);
asm_gen.add_directed_instr_stream("riscv_load_store_hazard_instr_stream", 4);
asm_gen.add_directed_instr_stream("riscv_cache_line_stress_instr_stream", 4);
asm_gen.add_directed_instr_stream("riscv_multi_page_load_store_instr_stream", 4);
endfunction
endclass
class riscv_rand_jump_test extends riscv_instr_base_test;
`uvm_component_utils(riscv_rand_jump_test)
`uvm_component_new
virtual function void randomize_cfg();
cfg.instr_cnt = 20000;
cfg.num_of_sub_program = 20;
`DV_CHECK_RANDOMIZE_WITH_FATAL(cfg,
max_nested_loop == 1;)
`uvm_info(`gfn, $sformatf("riscv_instr_gen_config is randomized:\n%0s",
cfg.sprint()), UVM_LOW)
endfunction
virtual function void apply_directed_instr();
asm_gen.add_directed_instr_stream("riscv_load_store_rand_instr_stream", 10);
endfunction
endclass
class riscv_mmu_stress_test extends riscv_instr_base_test;
`uvm_component_utils(riscv_mmu_stress_test)
`uvm_component_new
virtual function void randomize_cfg();
cfg.instr_cnt = 500;
cfg.num_of_sub_program = 0;
`DV_CHECK_RANDOMIZE_WITH_FATAL(cfg,
max_nested_loop == 0;)
`uvm_info(`gfn, $sformatf("riscv_instr_gen_config is randomized:\n%0s",
cfg.sprint()), UVM_LOW)
endfunction
virtual function void apply_directed_instr();
// Mix below directed instructino streams with the random instructions
asm_gen.add_directed_instr_stream("riscv_cache_line_stress_instr_stream", 80);
asm_gen.add_directed_instr_stream("riscv_load_store_hazard_instr_stream", 80);
asm_gen.add_directed_instr_stream("riscv_multi_page_load_store_instr_stream", 80);
endfunction
endclass
class riscv_page_table_exception_test extends riscv_rand_instr_test;
`uvm_component_utils(riscv_page_table_exception_test)
`uvm_component_new
virtual function void randomize_cfg();
cfg.enable_page_table_exception = 1'b1;
super.randomize_cfg();
endfunction
endclass
class riscv_no_fence_test extends riscv_rand_instr_test;
`uvm_component_utils(riscv_no_fence_test)
`uvm_component_new
virtual function void randomize_cfg();
cfg.no_fence = 1'b1;
super.randomize_cfg();
endfunction
endclass
class riscv_sfence_exception_test extends riscv_rand_instr_test;
`uvm_component_utils(riscv_sfence_exception_test)
`uvm_component_new
virtual function void randomize_cfg();
cfg.allow_sfence_exception = 1'b1;
super.randomize_cfg();
endfunction
endclass
class riscv_illegal_instr_test extends riscv_rand_instr_test;
`uvm_component_utils(riscv_illegal_instr_test)
`uvm_component_new
virtual function void randomize_cfg();
cfg.enable_illegal_instruction = 1'b1;
super.randomize_cfg();
endfunction
endclass
class riscv_hint_instr_test extends riscv_rand_instr_test;
`uvm_component_utils(riscv_hint_instr_test)
`uvm_component_new
virtual function void randomize_cfg();
cfg.enable_hint_instruction = 1'b1;
super.randomize_cfg();
endfunction
endclass

View file

@ -0,0 +1,26 @@
/*
* 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.
*/
package riscv_instr_test_pkg;
import uvm_pkg::*;
import riscv_instr_pkg::*;
`include "uvm_macros.svh"
`include "riscv_instr_base_test.sv"
`include "riscv_instr_test_lib.sv"
endpackage

29
vendor/google_riscv-dv/testlist vendored Normal file
View file

@ -0,0 +1,29 @@
// 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.
//================================================
// Test name : iteration count
//------------------------------------------------
riscv_arithmetic_basic_test : 10
riscv_machine_mode_rand_test : 10
riscv_privileged_mode_rand_test : 0
riscv_rand_instr_test : 10
riscv_rand_jump_test : 10
riscv_mmu_stress_test : 10
riscv_page_table_exception_test : 0
riscv_no_fence_test : 10
riscv_sfence_exception_test : 0
riscv_illegal_instr_test : 10
riscv_hint_instr_test : 10
//------------------------------------------------

View file

@ -0,0 +1,19 @@
// 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.
-sverilog
-ntb_opts uvm-1.2
-lca
+define+UVM_REGEX_NO_DPI
-timescale=1ns/10ps