mirror of
https://github.com/lowRISC/ibex.git
synced 2025-04-22 04:47:25 -04:00
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:
parent
5319734180
commit
bdb089d456
43 changed files with 8555 additions and 0 deletions
14
vendor/google_riscv-dv.lock.hjson
vendored
Normal file
14
vendor/google_riscv-dv.lock.hjson
vendored
Normal 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
28
vendor/google_riscv-dv/CONTRIBUTING.md
vendored
Normal 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
201
vendor/google_riscv-dv/LICENSE.txt
vendored
Normal 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
190
vendor/google_riscv-dv/README.md
vendored
Normal 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
22
vendor/google_riscv-dv/files.f
vendored
Normal 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
50
vendor/google_riscv-dv/iss_cmp
vendored
Executable 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
179
vendor/google_riscv-dv/iss_sim
vendored
Executable 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
170
vendor/google_riscv-dv/run
vendored
Executable 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
|
53
vendor/google_riscv-dv/scripts/ibex_log_to_trace_csv.py
vendored
Normal file
53
vendor/google_riscv-dv/scripts/ibex_log_to_trace_csv.py
vendored
Normal 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)
|
122
vendor/google_riscv-dv/scripts/instr_trace_compare.py
vendored
Normal file
122
vendor/google_riscv-dv/scripts/instr_trace_compare.py
vendored
Normal 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
31
vendor/google_riscv-dv/scripts/link.ld
vendored
Normal 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 = .;
|
||||
}
|
70
vendor/google_riscv-dv/scripts/ovpsim_log_to_trace_csv.py
vendored
Normal file
70
vendor/google_riscv-dv/scripts/ovpsim_log_to_trace_csv.py
vendored
Normal 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)
|
112
vendor/google_riscv-dv/scripts/riscv_trace_csv.py
vendored
Normal file
112
vendor/google_riscv-dv/scripts/riscv_trace_csv.py
vendored
Normal 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")
|
65
vendor/google_riscv-dv/scripts/spike_log_to_trace_csv.py
vendored
Normal file
65
vendor/google_riscv-dv/scripts/spike_log_to_trace_csv.py
vendored
Normal 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)
|
69
vendor/google_riscv-dv/src/dv_defines.svh
vendored
Normal file
69
vendor/google_riscv-dv/src/dv_defines.svh
vendored
Normal 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
|
852
vendor/google_riscv-dv/src/riscv_asm_program_gen.sv
vendored
Normal file
852
vendor/google_riscv-dv/src/riscv_asm_program_gen.sv
vendored
Normal 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
|
182
vendor/google_riscv-dv/src/riscv_callstack_gen.sv
vendored
Normal file
182
vendor/google_riscv-dv/src/riscv_callstack_gen.sv
vendored
Normal 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
|
77
vendor/google_riscv-dv/src/riscv_core_setting.sv
vendored
Normal file
77
vendor/google_riscv-dv/src/riscv_core_setting.sv
vendored
Normal 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;
|
77
vendor/google_riscv-dv/src/riscv_data_page_gen.sv
vendored
Normal file
77
vendor/google_riscv-dv/src/riscv_data_page_gen.sv
vendored
Normal 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
|
35
vendor/google_riscv-dv/src/riscv_defines.svh
vendored
Normal file
35
vendor/google_riscv-dv/src/riscv_defines.svh
vendored
Normal 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; \
|
||||
} \
|
||||
}
|
||||
|
437
vendor/google_riscv-dv/src/riscv_directed_instr_lib.sv
vendored
Normal file
437
vendor/google_riscv-dv/src/riscv_directed_instr_lib.sv
vendored
Normal 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
|
||||
|
209
vendor/google_riscv-dv/src/riscv_illegal_instr.sv
vendored
Normal file
209
vendor/google_riscv-dv/src/riscv_illegal_instr.sv
vendored
Normal 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
|
887
vendor/google_riscv-dv/src/riscv_instr_base.sv
vendored
Normal file
887
vendor/google_riscv-dv/src/riscv_instr_base.sv
vendored
Normal 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
|
360
vendor/google_riscv-dv/src/riscv_instr_gen_config.sv
vendored
Normal file
360
vendor/google_riscv-dv/src/riscv_instr_gen_config.sv
vendored
Normal 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
|
753
vendor/google_riscv-dv/src/riscv_instr_pkg.sv
vendored
Normal file
753
vendor/google_riscv-dv/src/riscv_instr_pkg.sv
vendored
Normal 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
|
321
vendor/google_riscv-dv/src/riscv_instr_sequence.sv
vendored
Normal file
321
vendor/google_riscv-dv/src/riscv_instr_sequence.sv
vendored
Normal 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
|
186
vendor/google_riscv-dv/src/riscv_instr_stream.sv
vendored
Normal file
186
vendor/google_riscv-dv/src/riscv_instr_stream.sv
vendored
Normal 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
|
330
vendor/google_riscv-dv/src/riscv_load_store_instr_lib.sv
vendored
Normal file
330
vendor/google_riscv-dv/src/riscv_load_store_instr_lib.sv
vendored
Normal 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
|
168
vendor/google_riscv-dv/src/riscv_loop_instr.sv
vendored
Normal file
168
vendor/google_riscv-dv/src/riscv_loop_instr.sv
vendored
Normal 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
|
72
vendor/google_riscv-dv/src/riscv_page_table.sv
vendored
Normal file
72
vendor/google_riscv-dv/src/riscv_page_table.sv
vendored
Normal 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
|
193
vendor/google_riscv-dv/src/riscv_page_table_entry.sv
vendored
Normal file
193
vendor/google_riscv-dv/src/riscv_page_table_entry.sv
vendored
Normal 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
|
69
vendor/google_riscv-dv/src/riscv_page_table_exception_cfg.sv
vendored
Normal file
69
vendor/google_riscv-dv/src/riscv_page_table_exception_cfg.sv
vendored
Normal 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
|
510
vendor/google_riscv-dv/src/riscv_page_table_list.sv
vendored
Normal file
510
vendor/google_riscv-dv/src/riscv_page_table_list.sv
vendored
Normal 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
|
575
vendor/google_riscv-dv/src/riscv_privil_reg.sv
vendored
Normal file
575
vendor/google_riscv-dv/src/riscv_privil_reg.sv
vendored
Normal 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
|
169
vendor/google_riscv-dv/src/riscv_privileged_common_seq.sv
vendored
Normal file
169
vendor/google_riscv-dv/src/riscv_privileged_common_seq.sv
vendored
Normal 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
|
95
vendor/google_riscv-dv/src/riscv_rand_instr.sv
vendored
Normal file
95
vendor/google_riscv-dv/src/riscv_rand_instr.sv
vendored
Normal 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
159
vendor/google_riscv-dv/src/riscv_reg.sv
vendored
Normal 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
|
111
vendor/google_riscv-dv/test/riscv_instr_base_test.sv
vendored
Normal file
111
vendor/google_riscv-dv/test/riscv_instr_base_test.sv
vendored
Normal 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
|
28
vendor/google_riscv-dv/test/riscv_instr_gen_tb_top.sv
vendored
Normal file
28
vendor/google_riscv-dv/test/riscv_instr_gen_tb_top.sv
vendored
Normal 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
|
250
vendor/google_riscv-dv/test/riscv_instr_test_lib.sv
vendored
Normal file
250
vendor/google_riscv-dv/test/riscv_instr_test_lib.sv
vendored
Normal 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
|
26
vendor/google_riscv-dv/test/riscv_instr_test_pkg.sv
vendored
Normal file
26
vendor/google_riscv-dv/test/riscv_instr_test_pkg.sv
vendored
Normal 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
29
vendor/google_riscv-dv/testlist
vendored
Normal 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
|
||||
//------------------------------------------------
|
19
vendor/google_riscv-dv/vcs.compile.option.f
vendored
Normal file
19
vendor/google_riscv-dv/vcs.compile.option.f
vendored
Normal 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
|
Loading…
Add table
Add a link
Reference in a new issue