mirror of
https://github.com/lowRISC/ibex.git
synced 2025-04-22 04:47:25 -04:00
Update google_riscv-dv to google/riscv-dv@1ad73cc
Update code from upstream repository https://github.com/google/riscv- dv to revision 1ad73cc43f8f84d93d49040f8b2928e74efdd854 * Fixes for ML tests (Udi Jonnalagadda) * Add missing default case to pmp_config (google/riscv-dv#583) (udinator) * various PMP exception handler fixes (google/riscv-dv#581) (udinator) * convert handshake doc to rst format (google/riscv-dv#580) (udinator) * Update coverage (google/riscv-dv#584) (weicaiyang) Signed-off-by: Udi <udij@google.com>
This commit is contained in:
parent
4b01580a7b
commit
f4366264e4
9 changed files with 430 additions and 377 deletions
2
vendor/google_riscv-dv.lock.hjson
vendored
2
vendor/google_riscv-dv.lock.hjson
vendored
|
@ -9,6 +9,6 @@
|
|||
upstream:
|
||||
{
|
||||
url: https://github.com/google/riscv-dv
|
||||
rev: 7b38e54c5e833f147edc03717b3fd711be923026
|
||||
rev: 1ad73cc43f8f84d93d49040f8b2928e74efdd854
|
||||
}
|
||||
}
|
||||
|
|
250
vendor/google_riscv-dv/HANDSHAKE.md
vendored
250
vendor/google_riscv-dv/HANDSHAKE.md
vendored
|
@ -1,250 +0,0 @@
|
|||
## Overview
|
||||
|
||||
While the `RISCV-DV` Instruction Generator provides a `debug_rom` section as
|
||||
well as various interrupt and exception handlers, from a verification
|
||||
standpoint this is not enough to help ensure that a core's internal state is being
|
||||
updated correctly upon receiving external stimulus such as interrupts or debug
|
||||
requests, such as the values of any relevant CSRs.
|
||||
To help with this issue, the instruction generator also provides a mechanism by
|
||||
which to communicate information to a RTL simulation environment by means of a
|
||||
handshaking protocol.
|
||||
|
||||
## Usage
|
||||
|
||||
Every handshake produced by the instruction generator is just a small segment of
|
||||
RISC-V assembly code, that end in one or more `sw` instructions to a specified memory
|
||||
address `signature_addr`.
|
||||
This `signature_addr` is completely customizable, and
|
||||
can be specified through a `plusarg` runtime option to the generator.
|
||||
There is also an enable bit `require_signature_addr` that must be set through
|
||||
another `plusarg` argument to enable these handshake code segments to be
|
||||
generated in the main random assembly program.
|
||||
|
||||
An RTL simulation environment that utilizes
|
||||
this handshaking mechanism should provide a basic set of tasks to monitor this
|
||||
`signature_addr` for any writes, as this will indicate that the core under test is
|
||||
executing a particular handshake assembly sequence and is transmitting some
|
||||
information to the testbench for analysis.
|
||||
As a result, this `signature_addr`
|
||||
acts as sort of memory-mapped address that the testbench will monitor, and as
|
||||
such, case should be taken when setting this address to ensure that the generator's
|
||||
program randomization will not somehow create a sequence of random load/store
|
||||
instructions that access the same `signature_addr`.
|
||||
A suggested value for this `signature_addr` is the value `0x8ffffffc`.
|
||||
More details, and an example, as to how to interface the testbench with this
|
||||
handshake mechanism will be provided below.
|
||||
|
||||
## Handshake Sequence Architecture
|
||||
|
||||
The function `gen_signature_handshake(...)` contained in
|
||||
[src/riscv_asm_program_gen.sv](https://github.com/google/riscv-dv/blob/master/src/riscv_asm_program_gen.sv)
|
||||
is used to actually generate the handshaking code and push it into the specified
|
||||
instruction queue. Its usage can be seen repeatedly throughout the program
|
||||
generation in various places, such as trap handlers and the debug ROM, where it
|
||||
is important to send information to a testbench for further verification.
|
||||
The `signature_type_t`, `core_status_t`, and `test_result_t` enums specified as
|
||||
input values to this function are defined in
|
||||
[src/riscv_signature_pkg.sv](https://github.com/google/riscv-dv/blob/master/src/riscv_signature_pkg.sv).
|
||||
Note that all of these definitions are within a standalone package, this is so
|
||||
that an RTL simulation environment can also import this package to gain access
|
||||
to these enums.
|
||||
The `signature_type_t` enum is by far the most important enum value, as
|
||||
this specifies what kind of handshake will be generated.
|
||||
|
||||
There are currently 4 defined values of `signature_type`, each corresponding
|
||||
to a different handshake type that will be generated; each will be explained below.
|
||||
Note that two GPRs must be used to temporarily hold the store address and the
|
||||
actual data to store to this address; the generator randomizes these two GPRs
|
||||
for every generated program, but for the purposes of this document, `x1` and
|
||||
`x2` will be used, and `0x8ffffffc` will be used as the example `signature_addr`.
|
||||
|
||||
#### CORE_STATUS
|
||||
|
||||
When the `signature_type` argument is specified as `CORE_STATUS`, a single word
|
||||
of data will be written to the `signature_addr`. As the actual `signature_type`
|
||||
value is 8 bits wide, as specified in the `riscv_signature_pkg`, this generated
|
||||
data word will contain the `CORE_STATUS` value in its bottom 8 bits, and will
|
||||
contain the specified value of `core_status_t` in the upper 24 bits. This
|
||||
signature handshake is intended to convey basic information about the core's
|
||||
execution state to an RTL simulation environment; a handshake containing a
|
||||
`core_status` of `IN_DEBUG_MODE` is added to the debug ROM to indicate to a
|
||||
testbench that the core has jumped into Debug Mode and is executing the debug
|
||||
ROM, a handshake containing a `core_status` of `ILLEGAL_INSTR_EXCEPTION` is
|
||||
added to the illegal instruction exception handler code created by the generator
|
||||
to indicate to a testbench that the core has trapped to and is executing the
|
||||
proper handler after encountering an illegal instruction, and so on for the rest
|
||||
of the defined `core_status_t` enum values.
|
||||
|
||||
Note that when generating these specific handshakes, it is only necessary to
|
||||
specify the parameters `instr`, `signature_type`, and `core_status`. For
|
||||
example, to generate this handshake to signal status `IN_MACHINE_MODE` to the
|
||||
testbench, the call to the function looks like this:
|
||||
```
|
||||
gen_signature_handshake(.instr(instr_string_queue),
|
||||
.signature_type(CORE_STATUS),
|
||||
.core_status(IN_MACHINE_MODE));
|
||||
```
|
||||
The sequence of assembly code generated by this call looks like the following:
|
||||
```
|
||||
# First, load the signature address into a GPR
|
||||
li x2, 0x8ffffffc
|
||||
# Load the intended core_status_t enum value into
|
||||
# a second GPR
|
||||
li x1, 0x2
|
||||
# Left-shift the core_status value by 8 bits
|
||||
# to make room for the signature_type
|
||||
slli x1, x1, 8
|
||||
# Load the intended signature_type_t enum value into
|
||||
# the bottom 8 bits of the data word
|
||||
addi x1, x1, 0x0
|
||||
# Store the data word to memory at the location of the signature_addr
|
||||
sw x1, 0(x2)
|
||||
```
|
||||
|
||||
#### TEST_RESULT
|
||||
|
||||
As before, when `signature_type` is set to `TEST_RESULT` a single word of data
|
||||
will be written to the signature address, and the value `TEST_RESULT` will be
|
||||
placed in the bottom 8 bits. The upper 24 bits will then contain a value of type
|
||||
`test_result_t`, either `TEST_PASS` or `TEST_FAIL`, to indicate to the testbench
|
||||
the exit status of the test. As the ISS co-simulation flow provides a robust
|
||||
end-of-test correctness check, the only time that this signature handshake is
|
||||
used is in the `riscv_csr_test`. Since this test is generated with a Python
|
||||
script and is entirely self-checking, we must send an exit status of `TEST_PASS`
|
||||
or `TEST_FAIL` to the testbench to indicate to either throw an error or end the
|
||||
test correctly.
|
||||
|
||||
Note that when generating these handshakes, the only arguments that need to be
|
||||
specified are `instr`, `signature_type`, and `test_result`. For example, to
|
||||
generate a handshake to communicate `TEST_PASS` to a testbench, the function
|
||||
call would look like this:
|
||||
```
|
||||
gen_signature_handshake(.instr(instr_string_queue),
|
||||
.signature_type(TEST_RESULT),
|
||||
.test_result(TEST_PASS));
|
||||
```
|
||||
The sequence of generated assembly code with this function call would look like
|
||||
the following:
|
||||
```
|
||||
# Load the signature address into a GPR
|
||||
li x2 0x8ffffffc
|
||||
# Load the intended test_result_t enum value
|
||||
li x1, 0x0
|
||||
# Left-shift the test_result value by 8 bits
|
||||
slli x1, x1, 8
|
||||
# Load the intended signature_type_t enum value into
|
||||
# the bottom 8 bits of the data word
|
||||
addi x1, x1, 0x1
|
||||
# Store this formatted word to memory at the signature address
|
||||
sw x1, 0(x2)
|
||||
```
|
||||
|
||||
#### WRITE_GPR
|
||||
|
||||
When a `signature_type` of `WRITE_GPR` is passed to the
|
||||
`gen_signature_handshake(...)` function, one data word will initially be written
|
||||
to the signature address, containing the `signature_type` of `WRITE_GPR` in the
|
||||
lower 8 bits. After this, the value held by each of the 32 RISC-V general
|
||||
purpose registers from `x0` to `x31` will be written to the signature address
|
||||
with `sw` instructions.
|
||||
|
||||
For this particular handshake, the only function arguments that need to be
|
||||
specified are `instr` and `signature_type`. A function call to generate this
|
||||
particular handshake would look like the following:
|
||||
```
|
||||
gen_signature_handshake(.instr(instr_string_queue),
|
||||
.signature_type(WRITE_GPR));
|
||||
```
|
||||
The generated assembly sequence would look like this:
|
||||
```
|
||||
# Load the signature address into a GPR
|
||||
li x2, 0x8ffffffc
|
||||
# Load the value of WRITE_GPR into a second GPR
|
||||
li x1, 0x2
|
||||
# Store this word to memory at the signature address
|
||||
sw x1, 0(x2)
|
||||
# Iterate through all 32 GPRs and write each one to
|
||||
# memory at the signature address
|
||||
sw x0, 0(x2)
|
||||
sw x1, 0(x2)
|
||||
sw x2, 0(x2)
|
||||
sw x3, 0(x2)
|
||||
...
|
||||
sw x30, 0(x2)
|
||||
sw x31, 0(x2)
|
||||
```
|
||||
|
||||
#### WRITE_CSR
|
||||
|
||||
When `gen_signature_handshake(...)` is called with `WRITE_CSR` as the
|
||||
`signature_type` argument, we will generate a first `sw` instruction that writes a
|
||||
data word to the `signature_addr` that contains the value `WRITE_CSR` in the
|
||||
bottom 8 bits, and the address of the desired CSR in the upper 24 bits, to
|
||||
indicate to the testbench which CSR will be written.
|
||||
This first generated `sw` instruction is then followed by a second one, which
|
||||
writes the actual data contained in the specified CSR to the signature address.
|
||||
|
||||
Note the only function arguments that have to be specified to generate this
|
||||
handshake are `instr`, `signature_type`, and `csr`. As an example, to generate a
|
||||
handshake that writes the value of the `mie` CSR to the RTL simulation
|
||||
environment, the function call would look like this:
|
||||
```
|
||||
gen_signature_handshake(.instr(instr_string_queue),
|
||||
.signature_type(WRITE_CSR),
|
||||
.csr(MIE));
|
||||
```
|
||||
The sequence of assembly generated by this call would look like the following:
|
||||
```
|
||||
# Load the signature address into a GPR
|
||||
li x2, 0x8ffffffc
|
||||
# Load the address of MIE into the second GPR
|
||||
li x1, 0x304
|
||||
# Left-shift the CSR address by 8 bits
|
||||
slli x1, x1, 8
|
||||
# Load the WRITE_CSR signature_type value into
|
||||
# the bottom 8 bits of the data word.
|
||||
# At this point, the data word is 0x00030403
|
||||
addi x1, x1, 0x3
|
||||
# Store this formatted word to memory at the signature address
|
||||
sw x1, 0(x2)
|
||||
# Read the actual CSR value into the second GPR
|
||||
csrr x1, 0x304
|
||||
# Write the value held by the CSR into memory at the signature address
|
||||
sw x1, 0(x2)
|
||||
```
|
||||
|
||||
## Sample Testbench Integration
|
||||
|
||||
Everything previously outlined has been relating to how this handshake
|
||||
generation is implemented from the perspective of the `RISCV-DV` instruction
|
||||
generator, but some work must be done in the RTL simulation environment to
|
||||
actually interface with and use these handshakes to improve verification with
|
||||
this mechanism.
|
||||
This handshaking mechanism has been put to use for verification of the [Ibex
|
||||
RISC-V core](https://github.com/lowRISC/ibex), in collaboration with LowRISC. To
|
||||
interface with the handshaking code produced in the generator, this testbench
|
||||
makes heavy use of the task `wait_for_mem_txn(...)` found in
|
||||
[core_ibex_base_test.sv](https://github.com/lowRISC/ibex/blob/master/dv/uvm/tests/core_ibex_base_test.sv).
|
||||
This task polls the Ibex core's data memory interface for any writes to the
|
||||
chosen signature address (`0x8ffffffc`), and then based on the value of
|
||||
`signature_type` encoded by the generated handshake code, this task takes
|
||||
appropriate action and stores the relevant data into a queue instantiated in the
|
||||
base test class.
|
||||
|
||||
For example upon detecting a transaction written to the
|
||||
signature address that has a `signature_type` of `WRITE_CSR`, it right-shifts
|
||||
the collected data word by 8 to obtain the CSR address, which is then stored to
|
||||
the local queue. However, since for `WRITE_CSR` signatures there is a second
|
||||
data word that gets written to memory at the signature address, the task waits
|
||||
for the second write containing the CSR data to arrive, and then stores that
|
||||
into the queue as well. After this task completes, it is now possible to pop
|
||||
the stored data off of the queue for analysis anywhere else in the test class,
|
||||
in this case examining the values of various CSR fields.
|
||||
|
||||
Additionally, the Ibex testbench provides a fairly basic API of some tasks
|
||||
wrapping `wait_for_mem_txn(...)` for frequently used functionalities in various
|
||||
test classes. This API is also found in
|
||||
[core_ibex_base_test.sv](https://github.com/lowRISC/ibex/blob/master/dv/uvm/tests/core_ibex_base_test.sv).
|
||||
Examples of use-cases for these API functions can be found throughout the
|
||||
library of tests written for the Ibex core, found at
|
||||
[core_ibex_test_lib.sv](https://github.com/lowRISC/ibex/blob/master/dv/uvm/tests/core_ibex_test_lib.sv), as these are heavily used to verify the core's response to external debug and interrupt stimulus.
|
270
vendor/google_riscv-dv/docs/source/handshake.rst
vendored
Normal file
270
vendor/google_riscv-dv/docs/source/handshake.rst
vendored
Normal file
|
@ -0,0 +1,270 @@
|
|||
Overview
|
||||
========
|
||||
|
||||
While the ``RISCV-DV`` Instruction Generator provides a ``debug_rom`` section as
|
||||
well as various interrupt and exception handlers, from a verification
|
||||
standpoint this is not enough to help ensure that a core's internal state is being
|
||||
updated correctly upon receiving external stimulus such as interrupts or debug
|
||||
requests, such as the values of any relevant CSRs.
|
||||
To help with this issue, the instruction generator also provides a mechanism by
|
||||
which to communicate information to a RTL simulation environment via a
|
||||
handshaking protocol.
|
||||
|
||||
Usage
|
||||
-----
|
||||
|
||||
Every handshake produced by the instruction generator is just a small segment of
|
||||
RISC-V assembly code, that end in one or more ``sw`` instructions to a specified memory
|
||||
address ``signature_addr``.
|
||||
This ``signature_addr`` is completely customizable, and
|
||||
can be specified through a ``plusarg`` runtime option to the generator.
|
||||
There is also an enable bit ``require_signature_addr`` that must be set through
|
||||
another ``plusarg`` argument to enable these handshake code segments to be
|
||||
generated in the main random assembly program.
|
||||
|
||||
A ``RISCV-DV`` based CPU verification environment that utilizes that handshaking mechanism should
|
||||
provide a basic set of tasks to monitor this ``signature_addr`` for any writes - this will indicate
|
||||
that the DUT is executing a particular handshake assembly sequence and is transmitting some
|
||||
information to the testbench for analysis.
|
||||
As a result, this ``signature_addr``
|
||||
acts as a memory-mapped address that the testbench will monitor, and as
|
||||
such, case should be taken when setting this address to ensure that the generator's
|
||||
program randomization will not somehow create a sequence of random load/store
|
||||
instructions that access the same ``signature_addr``.
|
||||
A suggested value for this ``signature_addr`` is the value ``0x8ffffffc``, but can be set
|
||||
to any other address depending on the CPU memory map.
|
||||
More details, and an example, as to how to interface the testbench with this
|
||||
handshake mechanism will be provided below.
|
||||
|
||||
Handshake Sequence Architecture
|
||||
-------------------------------
|
||||
|
||||
The function ``gen_signature_handshake(...)`` contained in
|
||||
`src/riscv_asm_program_gen.sv <https://github.com/google/riscv-dv/blob/master/src/riscv_asm_program_gen.sv>`_.
|
||||
is used to actually generate the handshaking code and push it into the specified
|
||||
instruction queue. Its usage can be seen repeatedly throughout the program
|
||||
generation in various places, such as trap handlers and the debug ROM, where it
|
||||
is important to send information to a testbench for further verification.
|
||||
The ``signature_type_t``, ``core_status_t``, and ``test_result_t`` enums specified as
|
||||
input values to this function are defined in
|
||||
`src/riscv_signature_pkg.sv <https://github.com/google/riscv-dv/blob/master/src/riscv_signature_pkg.sv>`_.
|
||||
Note that all of these definitions are within a standalone package, this is so
|
||||
that an RTL simulation environment can also import this package to gain access
|
||||
to these enums.
|
||||
The ``signature_type_t`` enum is by far the most important enum value, as
|
||||
this specifies what kind of handshake will be generated.
|
||||
|
||||
There are currently 4 defined values of ``signature_type``, each corresponding
|
||||
to a different handshake type that will be generated; each will be explained below.
|
||||
Note that two GPRs must be used to temporarily hold the store address and the
|
||||
actual data to store to this address; the generator randomizes these two GPRs
|
||||
for every generated program, but for the purposes of this document, ``x1`` and
|
||||
``x2`` will be used, and ``0x8ffffffc`` will be used as the example ``signature_addr``.
|
||||
|
||||
CORE_STATUS
|
||||
^^^^^^^^^^^
|
||||
|
||||
When the ``signature_type`` argument is specified as ``CORE_STATUS``, a single word
|
||||
of data will be written to the ``signature_addr``. As the actual ``signature_type``
|
||||
value is 8 bits wide, as specified in the ``riscv_signature_pkg``, this generated
|
||||
data word will contain the ``CORE_STATUS`` value in its bottom 8 bits, and will
|
||||
contain the specified value of ``core_status_t`` in the upper 24 bits. This
|
||||
signature handshake is intended to convey basic information about the core's
|
||||
execution state to an RTL simulation environment; a handshake containing a
|
||||
``core_status`` of ``IN_DEBUG_MODE`` is added to the debug ROM to indicate to a
|
||||
testbench that the core has jumped into Debug Mode and is executing the debug
|
||||
ROM, a handshake containing a ``core_status`` of ``ILLEGAL_INSTR_EXCEPTION`` is
|
||||
added to the illegal instruction exception handler code created by the generator
|
||||
to indicate to a testbench that the core has trapped to and is executing the
|
||||
proper handler after encountering an illegal instruction, and so on for the rest
|
||||
of the defined ``core_status_t`` enum values.
|
||||
|
||||
Note that when generating these specific handshakes, it is only necessary to
|
||||
specify the parameters ``instr``, ``signature_type``, and ``core_status``. For
|
||||
example, to generate this handshake to signal status ``IN_MACHINE_MODE`` to the
|
||||
testbench, the call to the function looks like this:
|
||||
|
||||
.. code-block:: verilog
|
||||
|
||||
gen_signature_handshake(.instr(instr_string_queue),
|
||||
.signature_type(CORE_STATUS),
|
||||
.core_status(IN_MACHINE_MODE));
|
||||
|
||||
The sequence of assembly code generated by this call looks like the following:
|
||||
|
||||
.. code-block:: verilog
|
||||
|
||||
// First, load the signature address into a GPR
|
||||
li x2, 0x8ffffffc
|
||||
// Load the intended core_status_t enum value into
|
||||
// a second GPR
|
||||
li x1, 0x2
|
||||
// Left-shift the core_status value by 8 bits
|
||||
// to make room for the signature_type
|
||||
slli x1, x1, 8
|
||||
// Load the intended signature_type_t enum value into
|
||||
// the bottom 8 bits of the data word
|
||||
addi x1, x1, 0x0
|
||||
// Store the data word to memory at the location of the signature_addr
|
||||
sw x1, 0(x2)
|
||||
|
||||
TEST_RESULT
|
||||
^^^^^^^^^^^
|
||||
|
||||
As before, when ``signature_type`` is set to ``TEST_RESULT`` a single word of data
|
||||
will be written to the signature address, and the value ``TEST_RESULT`` will be
|
||||
placed in the bottom 8 bits. The upper 24 bits will then contain a value of type
|
||||
``test_result_t``, either ``TEST_PASS`` or ``TEST_FAIL``, to indicate to the testbench
|
||||
the exit status of the test. As the ISS co-simulation flow provides a robust
|
||||
end-of-test correctness check, the only time that this signature handshake is
|
||||
used is in the ``riscv_csr_test``. Since this test is generated with a Python
|
||||
script and is entirely self-checking, we must send an exit status of ``TEST_PASS``
|
||||
or ``TEST_FAIL`` to the testbench to indicate to either throw an error or end the
|
||||
test correctly.
|
||||
|
||||
Note that when generating these handshakes, the only arguments that need to be
|
||||
specified are ``instr``, ``signature_type``, and ``test_result``. For example, to
|
||||
generate a handshake to communicate ``TEST_PASS`` to a testbench, the function
|
||||
call would look like this:
|
||||
|
||||
.. code-block:: verilog
|
||||
|
||||
gen_signature_handshake(.instr(instr_string_queue),
|
||||
.signature_type(TEST_RESULT),
|
||||
.test_result(TEST_PASS));
|
||||
|
||||
The sequence of generated assembly code with this function call would look like
|
||||
the following:
|
||||
|
||||
.. code-block:: verilog
|
||||
|
||||
// Load the signature address into a GPR
|
||||
li x2 0x8ffffffc
|
||||
// Load the intended test_result_t enum value
|
||||
li x1, 0x0
|
||||
// Left-shift the test_result value by 8 bits
|
||||
slli x1, x1, 8
|
||||
// Load the intended signature_type_t enum value into
|
||||
// the bottom 8 bits of the data word
|
||||
addi x1, x1, 0x1
|
||||
// Store this formatted word to memory at the signature address
|
||||
sw x1, 0(x2)
|
||||
|
||||
WRITE_GPR
|
||||
^^^^^^^^^
|
||||
|
||||
When a ``signature_type`` of ``WRITE_GPR`` is passed to the
|
||||
``gen_signature_handshake(...)`` function, one data word will initially be written
|
||||
to the signature address, containing the ``signature_type`` of ``WRITE_GPR`` in the
|
||||
lower 8 bits. After this, the value held by each of the 32 RISC-V general
|
||||
purpose registers from ``x0`` to ``x31`` will be written to the signature address
|
||||
with ``sw`` instructions.
|
||||
|
||||
For this particular handshake, the only function arguments that need to be
|
||||
specified are ``instr`` and ``signature_type``. A function call to generate this
|
||||
particular handshake would look like the following:
|
||||
|
||||
.. code-block:: verilog
|
||||
|
||||
gen_signature_handshake(.instr(instr_string_queue),
|
||||
.signature_type(WRITE_GPR));
|
||||
|
||||
The generated assembly sequence would look like this:
|
||||
|
||||
.. code-block:: verilog
|
||||
|
||||
// Load the signature address into a GPR
|
||||
li x2, 0x8ffffffc
|
||||
// Load the value of WRITE_GPR into a second GPR
|
||||
li x1, 0x2
|
||||
// Store this word to memory at the signature address
|
||||
sw x1, 0(x2)
|
||||
// Iterate through all 32 GPRs and write each one to
|
||||
// memory at the signature address
|
||||
sw x0, 0(x2)
|
||||
sw x1, 0(x2)
|
||||
sw x2, 0(x2)
|
||||
sw x3, 0(x2)
|
||||
...
|
||||
sw x30, 0(x2)
|
||||
sw x31, 0(x2)
|
||||
|
||||
WRITE_CSR
|
||||
^^^^^^^^^
|
||||
|
||||
When ``gen_signature_handshake(...)`` is called with ``WRITE_CSR`` as the
|
||||
``signature_type`` argument, we will generate a first ``sw`` instruction that writes a
|
||||
data word to the ``signature_addr`` that contains the value ``WRITE_CSR`` in the
|
||||
bottom 8 bits, and the address of the desired CSR in the upper 24 bits, to
|
||||
indicate to the testbench which CSR will be written.
|
||||
This first generated ``sw`` instruction is then followed by a second one, which
|
||||
writes the actual data contained in the specified CSR to the signature address.
|
||||
|
||||
Note the only function arguments that have to be specified to generate this
|
||||
handshake are ``instr``, ``signature_type``, and ``csr``. As an example, to generate a
|
||||
handshake that writes the value of the `mie` CSR to the RTL simulation
|
||||
environment, the function call would look like this:
|
||||
|
||||
.. code-block:: verilog
|
||||
|
||||
gen_signature_handshake(.instr(instr_string_queue),
|
||||
.signature_type(WRITE_CSR),
|
||||
.csr(MIE));
|
||||
|
||||
The sequence of assembly generated by this call would look like the following:
|
||||
|
||||
.. code-block:: verilog
|
||||
|
||||
// Load the signature address into a GPR
|
||||
li x2, 0x8ffffffc
|
||||
// Load the address of MIE into the second GPR
|
||||
li x1, 0x304
|
||||
// Left-shift the CSR address by 8 bits
|
||||
slli x1, x1, 8
|
||||
// Load the WRITE_CSR signature_type value into
|
||||
// the bottom 8 bits of the data word.
|
||||
// At this point, the data word is 0x00030403
|
||||
addi x1, x1, 0x3
|
||||
// Store this formatted word to memory at the signature address
|
||||
sw x1, 0(x2)
|
||||
// Read the actual CSR value into the second GPR
|
||||
csrr x1, 0x304
|
||||
// Write the value held by the CSR into memory at the signature address
|
||||
sw x1, 0(x2)
|
||||
|
||||
Sample Testbench Integration
|
||||
----------------------------
|
||||
|
||||
Everything previously outlined has been relating to how this handshake
|
||||
generation is implemented from the perspective of the ``RISCV-DV`` instruction
|
||||
generator, but some work must be done in the RTL simulation environment to
|
||||
actually interface with and use these handshakes to improve verification.
|
||||
|
||||
This handshaking mechanism has been put to use for verification of the `Ibex
|
||||
RISC-V core <https://github.com/lowRISC/ibex>`_, in collaboration with lowRISC. To
|
||||
interface with the handshaking code produced in the generator, this testbench
|
||||
makes heavy use of the task ``wait_for_mem_txn(...)`` found in
|
||||
`tests/core_ibex_base_test.sv <https://github.com/lowRISC/ibex/blob/master/dv/uvm/core_ibex/tests/core_ibex_base_test.sv>`_.
|
||||
This task polls the Ibex core's data memory interface for any writes to the
|
||||
chosen signature address (``0x8ffffffc``), and then based on the value of
|
||||
``signature_type`` encoded by the generated handshake code, this task takes
|
||||
appropriate action and stores the relevant data into a queue instantiated in the
|
||||
base test class.
|
||||
|
||||
For example upon detecting a transaction written to the
|
||||
signature address that has a ``signature_type`` of ``WRITE_CSR``, it right-shifts
|
||||
the collected data word by 8 to obtain the CSR address, which is then stored to
|
||||
the local queue. However, since for ``WRITE_CSR`` signatures there is a second
|
||||
data word that gets written to memory at the signature address, the task waits
|
||||
for the second write containing the CSR data to arrive, and then stores that
|
||||
into the queue as well. After this task completes, it is now possible to pop
|
||||
the stored data off of the queue for analysis anywhere else in the test class,
|
||||
in this case examining the values of various CSR fields.
|
||||
|
||||
Additionally, the Ibex testbench provides a fairly basic API of some tasks
|
||||
wrapping ``wait_for_mem_txn(...)`` for frequently used functionalities in various
|
||||
test classes. This API is also found in
|
||||
`tests/core_ibex_base_test.sv <https://github.com/lowRISC/ibex/blob/master/dv/uvm/core_ibex/tests/core_ibex_base_test.sv>`_.
|
||||
Examples of use-cases for these API functions can be found throughout the
|
||||
library of tests written for the Ibex core, found at
|
||||
`tests/core_ibex_test_lib.sv <https://github.com/lowRISC/ibex/blob/master/dv/uvm/core_ibex/tests/core_ibex_test_lib.sv>`_, as these are heavily used to verify the core's response to external debug and interrupt stimulus.
|
1
vendor/google_riscv-dv/docs/source/index.rst
vendored
1
vendor/google_riscv-dv/docs/source/index.rst
vendored
|
@ -20,6 +20,7 @@ Welcome to riscv-dv's documentation!
|
|||
customize_extend_generator
|
||||
class_reference
|
||||
cmd_line_reference
|
||||
handshake
|
||||
appendix
|
||||
|
||||
|
||||
|
|
32
vendor/google_riscv-dv/src/isa/riscv_b_instr.sv
vendored
32
vendor/google_riscv-dv/src/isa/riscv_b_instr.sv
vendored
|
@ -497,6 +497,38 @@ class riscv_b_instr extends riscv_instr;
|
|||
);
|
||||
endfunction
|
||||
|
||||
// coverage related functons
|
||||
virtual function void update_src_regs(string operands[$]);
|
||||
// handle special I_FORMAT (FSRI, FSRIW) and R4_FORMAT
|
||||
case(format)
|
||||
I_FORMAT: begin
|
||||
if (instr_name inside {FSRI, FSRIW}) begin
|
||||
`DV_CHECK_FATAL(operands.size() == 4, instr_name)
|
||||
// fsri rd, rs1, rs3, imm
|
||||
rs1 = get_gpr(operands[1]);
|
||||
rs1_value = get_gpr_state(operands[1]);
|
||||
rs3 = get_gpr(operands[2]);
|
||||
rs3_value = get_gpr_state(operands[2]);
|
||||
get_val(operands[3], imm);
|
||||
return;
|
||||
end
|
||||
end
|
||||
R4_FORMAT: begin
|
||||
`DV_CHECK_FATAL(operands.size() == 4)
|
||||
rs1 = get_gpr(operands[1]);
|
||||
rs1_value = get_gpr_state(operands[1]);
|
||||
rs2 = get_gpr(operands[2]);
|
||||
rs2_value = get_gpr_state(operands[2]);
|
||||
rs3 = get_gpr(operands[3]);
|
||||
rs3_value = get_gpr_state(operands[3]);
|
||||
return;
|
||||
end
|
||||
default: ;
|
||||
endcase
|
||||
// reuse base function to handle the other instructions
|
||||
super.update_src_regs(operands);
|
||||
endfunction : update_src_regs
|
||||
|
||||
endclass
|
||||
|
||||
|
||||
|
|
|
@ -150,4 +150,77 @@ class riscv_floating_point_instr extends riscv_instr;
|
|||
fd.rand_mode(has_fd);
|
||||
endfunction
|
||||
|
||||
// coverage related functons
|
||||
virtual function void update_src_regs(string operands[$]);
|
||||
if(category inside {LOAD, CSR}) begin
|
||||
super.update_src_regs(operands);
|
||||
return;
|
||||
end
|
||||
case(format)
|
||||
I_FORMAT: begin
|
||||
`DV_CHECK_FATAL(operands.size() == 2)
|
||||
if (has_fs1) begin
|
||||
fs1 = get_fpr(operands[1]);
|
||||
fs1_value = get_gpr_state(operands[1]);
|
||||
end else if (has_rs1) begin
|
||||
rs1 = get_gpr(operands[1]);
|
||||
rs1_value = get_gpr_state(operands[1]);
|
||||
end
|
||||
end
|
||||
S_FORMAT: begin
|
||||
`DV_CHECK_FATAL(operands.size() == 3)
|
||||
// FSW rs2 is fp
|
||||
fs2 = get_fpr(operands[0]);
|
||||
fs2_value = get_gpr_state(operands[0]);
|
||||
rs1 = get_gpr(operands[2]);
|
||||
rs1_value = get_gpr_state(operands[2]);
|
||||
get_val(operands[1], imm);
|
||||
end
|
||||
R_FORMAT: begin
|
||||
if (has_fs2 || category == CSR) begin
|
||||
`DV_CHECK_FATAL(operands.size() == 3)
|
||||
end else begin
|
||||
`DV_CHECK_FATAL(operands.size() == 2)
|
||||
end
|
||||
if(category != CSR) begin
|
||||
fs1 = get_fpr(operands[1]);
|
||||
fs1_value = get_gpr_state(operands[1]);
|
||||
if (has_fs2) begin
|
||||
fs2 = get_fpr(operands[2]);
|
||||
fs2_value = get_gpr_state(operands[2]);
|
||||
end
|
||||
end
|
||||
end
|
||||
R4_FORMAT: begin
|
||||
`DV_CHECK_FATAL(operands.size() == 4)
|
||||
fs1 = get_fpr(operands[1]);
|
||||
fs1_value = get_gpr_state(operands[1]);
|
||||
fs2 = get_fpr(operands[2]);
|
||||
fs2_value = get_gpr_state(operands[2]);
|
||||
fs3 = get_fpr(operands[3]);
|
||||
fs3_value = get_gpr_state(operands[3]);
|
||||
end
|
||||
default: `uvm_fatal(`gfn, $sformatf("Unsupported format %0s", format))
|
||||
endcase
|
||||
endfunction : update_src_regs
|
||||
|
||||
virtual function void update_dst_regs(string reg_name, string val_str);
|
||||
$display("update_dst_regs %0s", reg_name);
|
||||
get_val(val_str, gpr_state[reg_name], .hex(1));
|
||||
if (has_fd) begin
|
||||
fd = get_fpr(reg_name);
|
||||
fd_value = get_gpr_state(reg_name);
|
||||
end else if (has_rd) begin
|
||||
rd = get_gpr(reg_name);
|
||||
rd_value = get_gpr_state(reg_name);
|
||||
end
|
||||
endfunction : update_dst_regs
|
||||
|
||||
virtual function riscv_fpr_t get_fpr(input string str);
|
||||
str = str.toupper();
|
||||
if (!fpr_enum::from_name(str, get_fpr)) begin
|
||||
`uvm_fatal(`gfn, $sformatf("Cannot convert %0s to FPR", str))
|
||||
end
|
||||
endfunction : get_fpr
|
||||
|
||||
endclass
|
||||
|
|
|
@ -62,8 +62,7 @@
|
|||
logical_similarity_e logical_similarity;
|
||||
string trace;
|
||||
|
||||
// TODO, remove it?
|
||||
//`VECTOR_INCLUDE("riscv_instr_cov_item_inc_declares.sv")
|
||||
`VECTOR_INCLUDE("riscv_instr_cov_item_inc_declares.sv")
|
||||
|
||||
virtual function void pre_sample();
|
||||
unaligned_pc = (pc[1:0] != 2'b00);
|
||||
|
@ -274,13 +273,7 @@
|
|||
get_val(operands[1], imm);
|
||||
end
|
||||
I_FORMAT: begin
|
||||
// TODO, support I_FORMAT floating point later
|
||||
// if (group == RV32F) return;
|
||||
if (instr_name inside {FSRI, FSRIW}) begin
|
||||
`DV_CHECK_FATAL(operands.size() == 4, instr_name)
|
||||
end else begin
|
||||
`DV_CHECK_FATAL(operands.size() == 3, instr_name)
|
||||
end
|
||||
`DV_CHECK_FATAL(operands.size() == 3, instr_name)
|
||||
if(category == LOAD) begin
|
||||
// load rd, imm(rs1)
|
||||
rs1 = get_gpr(operands[2]);
|
||||
|
@ -294,13 +287,6 @@
|
|||
end else begin
|
||||
get_val(operands[1], csr);
|
||||
end
|
||||
//end else if (instr_name inside {FSRI, FSRIW}) begin
|
||||
// // fsri rd, rs1, rs3, imm
|
||||
// rs1 = get_gpr(operands[1]);
|
||||
// rs1_value = get_gpr_state(operands[1]);
|
||||
// rs3 = get_gpr(operands[2]);
|
||||
// rs3_value = get_gpr_state(operands[2]);
|
||||
// get_val(operands[3], imm);
|
||||
end else begin
|
||||
// addi rd, rs1, imm
|
||||
rs1 = get_gpr(operands[1]);
|
||||
|
@ -311,11 +297,6 @@
|
|||
S_FORMAT, B_FORMAT: begin
|
||||
`DV_CHECK_FATAL(operands.size() == 3)
|
||||
if(category == STORE) begin
|
||||
// sw rs2,imm(rs1)
|
||||
//update_instr_reg_by_abi_name(operands[0], // FSW rs2 is fp, TODO
|
||||
// rs2, rs2_value,
|
||||
// fs2, fs2_value);
|
||||
|
||||
rs2 = get_gpr(operands[0]);
|
||||
rs2_value = get_gpr_state(operands[0]);
|
||||
rs1 = get_gpr(operands[2]);
|
||||
|
@ -331,8 +312,7 @@
|
|||
end
|
||||
end
|
||||
R_FORMAT: begin
|
||||
if ((has_rs2 || category == CSR) &&
|
||||
!(instr_name inside {FCLASS_S, FCLASS_D})) begin
|
||||
if (has_rs2 || category == CSR) begin
|
||||
`DV_CHECK_FATAL(operands.size() == 3)
|
||||
end else begin
|
||||
`DV_CHECK_FATAL(operands.size() == 2)
|
||||
|
@ -347,16 +327,6 @@
|
|||
rs1 = get_gpr(operands[2]);
|
||||
rs1_value = get_gpr_state(operands[2]);
|
||||
end
|
||||
//else if (group inside {RV32F, RV64F, RV32D, RV64D}) begin // TODO
|
||||
// // fs1
|
||||
// fs1 = get_fpr(operands[1]);
|
||||
// fs1_value = get_gpr_state(operands[1]);
|
||||
// // fs2
|
||||
// if (!instr_name inside {FCLASS_S, FCLASS_D}) begin
|
||||
// fs2 = get_fpr(operands[2]);
|
||||
// fs2_value = get_gpr_state(operands[2]);
|
||||
// end
|
||||
//end
|
||||
else begin
|
||||
// add rd, rs1, rs2
|
||||
rs1 = get_gpr(operands[1]);
|
||||
|
@ -369,15 +339,6 @@
|
|||
end
|
||||
R4_FORMAT: begin
|
||||
`DV_CHECK_FATAL(operands.size() == 4)
|
||||
//update_instr_reg_by_abi_name(operands[1], // TODO
|
||||
// rs1, rs1_value,
|
||||
// fs1, fs1_value);
|
||||
//update_instr_reg_by_abi_name(operands[2],
|
||||
// rs2, rs2_value,
|
||||
// fs2, fs2_value);
|
||||
//update_instr_reg_by_abi_name(operands[3],
|
||||
// rs3, rs3_value,
|
||||
// fs3, fs3_value);
|
||||
rs1 = get_gpr(operands[1]);
|
||||
rs1_value = get_gpr_state(operands[1]);
|
||||
rs2 = get_gpr(operands[2]);
|
||||
|
@ -453,11 +414,11 @@
|
|||
// c.j imm
|
||||
get_val(operands[0], imm);
|
||||
end
|
||||
default: `uvm_fatal(`gfn, $sformatf("Unsupported format %0s", format))
|
||||
endcase
|
||||
endfunction : update_src_regs
|
||||
|
||||
virtual function void update_dst_regs(string reg_name, string val_str);
|
||||
// update_instr_reg_by_abi_name(pair[0], instr.rd, instr.rd_value, instr.fd, instr.fd_value);
|
||||
get_val(val_str, gpr_state[reg_name], .hex(1));
|
||||
rd = get_gpr(reg_name);
|
||||
rd_value = get_gpr_state(reg_name);
|
||||
|
@ -480,31 +441,3 @@
|
|||
return 0;
|
||||
end
|
||||
endfunction : get_gpr_state
|
||||
|
||||
// TODO
|
||||
// virtual function riscv_fpr_t get_fpr(input string str);
|
||||
// str = str.toupper();
|
||||
// if (!fpr_enum::from_name(str, get_fpr)) begin
|
||||
// `uvm_fatal(`gfn, $sformatf("Cannot convert %0s to FPR", str))
|
||||
// end
|
||||
// endfunction : get_fpr
|
||||
//
|
||||
// function bit is_fp_reg(input string str);
|
||||
// riscv_fpr_t tmp;
|
||||
// str = str.toupper();
|
||||
// return fpr_enum::from_name(str, tmp);
|
||||
// endfunction : is_fp_reg
|
||||
//
|
||||
// virtual function void update_instr_reg_by_abi_name(string abi_name,
|
||||
// ref riscv_reg_t rs,
|
||||
// ref bit [XLEN-1:0] rs_value,
|
||||
// ref riscv_fpr_t fs,
|
||||
// ref bit [XLEN-1:0] fs_value);
|
||||
// if (is_fp_reg(abi_name)) begin
|
||||
// fs = get_fpr(abi_name);
|
||||
// fs_value = get_gpr_state(abi_name);
|
||||
// end else begin
|
||||
// rs = get_gpr(abi_name);
|
||||
// rs_value = get_gpr_state(abi_name);
|
||||
// end
|
||||
// endfunction : update_instr_reg_by_abi_name
|
||||
|
|
100
vendor/google_riscv-dv/src/riscv_pmp_cfg.sv
vendored
100
vendor/google_riscv-dv/src/riscv_pmp_cfg.sv
vendored
|
@ -83,6 +83,7 @@ class riscv_pmp_cfg extends uvm_object;
|
|||
|
||||
constraint grain_addr_mode_c {
|
||||
foreach (pmp_cfg[i]) {
|
||||
(pmp_granularity == 0) -> (pmp_cfg[i].a != NAPOT);
|
||||
(pmp_granularity >= 1) -> (pmp_cfg[i].a != NA4);
|
||||
}
|
||||
}
|
||||
|
@ -121,10 +122,13 @@ class riscv_pmp_cfg extends uvm_object;
|
|||
super.new(name);
|
||||
cfg_per_csr = XLEN / 8;
|
||||
inst = uvm_cmdline_processor::get_inst();
|
||||
if (inst.get_arg_value("+pmp_num_regions=", s)) begin
|
||||
pmp_num_regions = s.atoi();
|
||||
pmp_num_regions.rand_mode(0);
|
||||
end
|
||||
get_int_arg_value("+pmp_granularity=", pmp_granularity);
|
||||
get_bool_arg_value("+pmp_randomize=", pmp_randomize);
|
||||
get_bool_arg_value("+pmp_allow_addr_overlap=", pmp_allow_addr_overlap);
|
||||
get_int_arg_value("+pmp_granularity=", pmp_granularity);
|
||||
get_int_arg_value("+pmp_num_regions=", pmp_num_regions);
|
||||
get_hex_arg_value("+pmp_max_offset=", pmp_max_offset);
|
||||
`uvm_info(`gfn, $sformatf("pmp max offset: 0x%0x", pmp_max_offset), UVM_LOW)
|
||||
pmp_cfg = new[pmp_num_regions];
|
||||
|
@ -417,17 +421,17 @@ class riscv_pmp_cfg extends uvm_object;
|
|||
// based on address match mode, branch to appropriate "handler" //
|
||||
//////////////////////////////////////////////////////////////////
|
||||
// pmpcfg[i].A == OFF
|
||||
$sformatf("beqz x%0d, 21f", scratch_reg[4]),
|
||||
$sformatf("beqz x%0d, 20f", scratch_reg[4]),
|
||||
// pmpcfg[i].A == TOR
|
||||
// scratch_reg[5] will contain pmpaddr[i-1]
|
||||
$sformatf("li x%0d, 1", scratch_reg[0]),
|
||||
$sformatf("beq x%0d, x%0d, 22f", scratch_reg[4], scratch_reg[0]),
|
||||
$sformatf("beq x%0d, x%0d, 21f", scratch_reg[4], scratch_reg[0]),
|
||||
// pmpcfg[i].A == NA4
|
||||
$sformatf("li x%0d, 2", scratch_reg[0]),
|
||||
$sformatf("beq x%0d, x%0d, 26f", scratch_reg[4], scratch_reg[0]),
|
||||
$sformatf("beq x%0d, x%0d, 25f", scratch_reg[4], scratch_reg[0]),
|
||||
// pmpcfg[i].A == NAPOT
|
||||
$sformatf("li x%0d, 3", scratch_reg[0]),
|
||||
$sformatf("beq x%0d, x%0d, 28f", scratch_reg[4], scratch_reg[0]),
|
||||
$sformatf("beq x%0d, x%0d, 27f", scratch_reg[4], scratch_reg[0]),
|
||||
// Error check, if no address modes match, something has gone wrong
|
||||
$sformatf("j test_done"),
|
||||
/////////////////////////////////////////////////////////////////
|
||||
|
@ -443,26 +447,14 @@ class riscv_pmp_cfg extends uvm_object;
|
|||
// load number of pmp regions - loop limit
|
||||
$sformatf("li x%0d, %0d", scratch_reg[1], pmp_num_regions),
|
||||
// if counter < pmp_num_regions => branch to beginning of loop,
|
||||
// otherwise jump to the end of the loop.
|
||||
// otherwise jump to the end of the loop
|
||||
$sformatf("ble x%0d, x%0d, 19f", scratch_reg[1], scratch_reg[0]),
|
||||
$sformatf("j 0b"),
|
||||
// If we reach here, it means that no PMP entry has matched the request.
|
||||
// If the request was made from S-mode or U-mode, jump immediately to <test_done>.
|
||||
// To determine the privilege mode of the access, we must read xSTATUS.xPP.
|
||||
//
|
||||
// TODO(udinator) - need to update to support execution of this handler in S-mode.
|
||||
$sformatf("19: csrr x%0d, 0x%0x", scratch_reg[0], MSTATUS),
|
||||
// Get mstatus.MPP by rightshifting and leftshifting the full CSR value.
|
||||
$sformatf("slli x%0d, x%0d, %0d", scratch_reg[0], scratch_reg[0], XLEN-13),
|
||||
$sformatf("srli x%0d, x%0d, %0d", scratch_reg[0], scratch_reg[0], XLEN-2),
|
||||
// If the MPP field is less than 2'b11 (e.g. S-mode, H-mode, or U-mode),
|
||||
// jump to <test_done>.
|
||||
// If the MPP field is set to M-mode jump to the end of the handler,
|
||||
// otherwise jump to <test_done>.
|
||||
$sformatf("li x%0d, 3", scratch_reg[1]),
|
||||
$sformatf("beq x%0d, x%0d, 20f", scratch_reg[0], scratch_reg[1]),
|
||||
$sformatf("j test_done"),
|
||||
$sformatf("20: j 34f")
|
||||
// We must immediately jump to <test_done> since the CPU is taking a PMP exception,
|
||||
// but this routine is unable to find a matching PMP region for the faulting access -
|
||||
// there is a bug somewhere.
|
||||
$sformatf("19: j test_done")
|
||||
};
|
||||
|
||||
/////////////////////////////////////////////////
|
||||
|
@ -477,35 +469,36 @@ class riscv_pmp_cfg extends uvm_object;
|
|||
|
||||
// Sub-section to deal with address matching mode OFF.
|
||||
// If entry is OFF, simply continue looping through other PMP CSR.
|
||||
instr = {instr, "21: j 18b"};
|
||||
instr = {instr, "20: j 18b"};
|
||||
|
||||
// Sub-section to handle address matching mode TOR.
|
||||
instr = {instr,
|
||||
|
||||
$sformatf("22: csrr x%0d, 0x%0x", scratch_reg[0], MSCRATCH),
|
||||
$sformatf("21: csrr x%0d, 0x%0x", scratch_reg[0], MSCRATCH),
|
||||
$sformatf("csrr x%0d, 0x%0x", scratch_reg[4], MTVAL),
|
||||
$sformatf("srli x%0d, x%0d, 2", scratch_reg[4], scratch_reg[4]),
|
||||
// If loop_counter==0, compare fault_addr to 0
|
||||
$sformatf("bnez x%0d, 23f", scratch_reg[0]),
|
||||
$sformatf("bnez x%0d, 22f", scratch_reg[0]),
|
||||
// If fault_addr < 0 : continue looping
|
||||
$sformatf("bltz x%0d, 18b", scratch_reg[4]),
|
||||
$sformatf("j 24f"),
|
||||
$sformatf("j 23f"),
|
||||
// If fault_addr < pmpaddr[i-1] : continue looping
|
||||
$sformatf("23: bgtu x%0d, x%0d, 18b", scratch_reg[5], scratch_reg[4]),
|
||||
$sformatf("22: bgtu x%0d, x%0d, 18b", scratch_reg[5], scratch_reg[4]),
|
||||
// If fault_addr >= pmpaddr[i] : continue looping
|
||||
$sformatf("24: bleu x%0d, x%0d, 18b", scratch_reg[1], scratch_reg[4]),
|
||||
$sformatf("23: bleu x%0d, x%0d, 18b", scratch_reg[1], scratch_reg[4]),
|
||||
// If we get here, there is a TOR match, if the entry is locked jump to
|
||||
// <test_done>, otherwise modify access bits and return
|
||||
$sformatf("andi x%0d, x%0d, 128", scratch_reg[4], scratch_reg[3]),
|
||||
$sformatf("beqz x%0d, 25f", scratch_reg[4]),
|
||||
$sformatf("beqz x%0d, 24f", scratch_reg[4]),
|
||||
$sformatf("j test_done"),
|
||||
$sformatf("25: j 30f")
|
||||
// TODO : update with correct label
|
||||
$sformatf("24: j 29f")
|
||||
};
|
||||
|
||||
// Sub-section to handle address matching mode NA4.
|
||||
// TODO(udinator) : add rv64 support
|
||||
instr = {instr,
|
||||
$sformatf("26: csrr x%0d, 0x%0x", scratch_reg[0], MTVAL),
|
||||
$sformatf("25: csrr x%0d, 0x%0x", scratch_reg[0], MTVAL),
|
||||
$sformatf("srli x%0d, x%0d, 2", scratch_reg[0], scratch_reg[0]),
|
||||
// Zero out pmpaddr[i][31:30]
|
||||
$sformatf("slli x%0d, x%0d, 2", scratch_reg[4], scratch_reg[1]),
|
||||
|
@ -516,14 +509,15 @@ class riscv_pmp_cfg extends uvm_object;
|
|||
// If we get here, there is an NA4 address match, jump to <test_done> if the
|
||||
// entry is locked, otherwise modify access bits
|
||||
$sformatf("andi x%0d, x%0d, 128", scratch_reg[4], scratch_reg[3]),
|
||||
$sformatf("beqz x%0d, 27f", scratch_reg[4]),
|
||||
$sformatf("beqz x%0d, 26f", scratch_reg[4]),
|
||||
$sformatf("j test_done"),
|
||||
$sformatf("27: j 30f")
|
||||
// TODO : update with correct label
|
||||
$sformatf("26: j 29f")
|
||||
};
|
||||
|
||||
// Sub-section to handle address matching mode NAPOT.
|
||||
instr = {instr,
|
||||
$sformatf("28: csrr x%0d, 0x%0x", scratch_reg[0], MTVAL),
|
||||
$sformatf("27: csrr x%0d, 0x%0x", scratch_reg[0], MTVAL),
|
||||
// get fault_addr[31:2]
|
||||
$sformatf("srli x%0d, x%0d, 2", scratch_reg[0], scratch_reg[0]),
|
||||
// mask the bottom pmp_granularity bits of fault_addr
|
||||
|
@ -542,22 +536,26 @@ class riscv_pmp_cfg extends uvm_object;
|
|||
$sformatf("andi x%0d, x%0d, 128", scratch_reg[4], scratch_reg[3]),
|
||||
$sformatf("beqz x%0d, 29f", scratch_reg[4]),
|
||||
$sformatf("j test_done"),
|
||||
$sformatf("29: j 30f")
|
||||
// TODO : update with correct label
|
||||
$sformatf("28: j 29f")
|
||||
};
|
||||
|
||||
// This case statement creates a bitmask that enables the correct access permissions
|
||||
// and ORs it with the 8-bit configuration fields.
|
||||
case (fault_type)
|
||||
INSTRUCTION_ACCESS_FAULT: begin
|
||||
instr.push_back($sformatf("30: ori x%0d, x%0d, 4", scratch_reg[3], scratch_reg[3]));
|
||||
instr.push_back($sformatf("29: ori x%0d, x%0d, 4", scratch_reg[3], scratch_reg[3]));
|
||||
end
|
||||
STORE_AMO_ACCESS_FAULT: begin
|
||||
// The combination of W:1 and R:0 is reserved, so if we are enabling write
|
||||
// permissions, also enable read permissions to adhere to the spec.
|
||||
instr.push_back($sformatf("30: ori x%0d, x%0d, 3", scratch_reg[3], scratch_reg[3]));
|
||||
instr.push_back($sformatf("29: ori x%0d, x%0d, 3", scratch_reg[3], scratch_reg[3]));
|
||||
end
|
||||
LOAD_ACCESS_FAULT: begin
|
||||
instr.push_back($sformatf("30: ori x%0d, x%0d, 1", scratch_reg[3], scratch_reg[3]));
|
||||
instr.push_back($sformatf("29: ori x%0d, x%0d, 1", scratch_reg[3], scratch_reg[3]));
|
||||
end
|
||||
default: begin
|
||||
`uvm_fatal(`gfn, "Invalid PMP fault type")
|
||||
end
|
||||
endcase
|
||||
instr = {instr,
|
||||
|
@ -587,23 +585,23 @@ class riscv_pmp_cfg extends uvm_object;
|
|||
// All other scratch_reg[*] can be used.
|
||||
// scratch_reg[0] contains the index of the correct pmpcfg CSR.
|
||||
// We simply check the index and then write to the correct pmpcfg CSR based on its value.
|
||||
$sformatf("beqz x%0d, 31f", scratch_reg[0]),
|
||||
$sformatf("beqz x%0d, 30f", scratch_reg[0]),
|
||||
$sformatf("li x%0d, 1", scratch_reg[4]),
|
||||
$sformatf("beq x%0d, x%0d, 32f", scratch_reg[0], scratch_reg[4]),
|
||||
$sformatf("beq x%0d, x%0d, 31f", scratch_reg[0], scratch_reg[4]),
|
||||
$sformatf("li x%0d, 2", scratch_reg[4]),
|
||||
$sformatf("beq x%0d, x%0d, 33f", scratch_reg[0], scratch_reg[4]),
|
||||
$sformatf("beq x%0d, x%0d, 32f", scratch_reg[0], scratch_reg[4]),
|
||||
$sformatf("li x%0d, 3", scratch_reg[4]),
|
||||
$sformatf("beq x%0d, x%0d, 34f", scratch_reg[0], scratch_reg[4]),
|
||||
$sformatf("31: csrw 0x%0x, x%0d", PMPCFG0, scratch_reg[2]),
|
||||
$sformatf("j 35f"),
|
||||
$sformatf("32: csrw 0x%0x, x%0d", PMPCFG1, scratch_reg[2]),
|
||||
$sformatf("j 35f"),
|
||||
$sformatf("33: csrw 0x%0x, x%0d", PMPCFG2, scratch_reg[2]),
|
||||
$sformatf("j 35f"),
|
||||
$sformatf("34: csrw 0x%0x, x%0d", PMPCFG3, scratch_reg[2]),
|
||||
$sformatf("beq x%0d, x%0d, 33f", scratch_reg[0], scratch_reg[4]),
|
||||
$sformatf("30: csrw 0x%0x, x%0d", PMPCFG0, scratch_reg[2]),
|
||||
$sformatf("j 34f"),
|
||||
$sformatf("31: csrw 0x%0x, x%0d", PMPCFG1, scratch_reg[2]),
|
||||
$sformatf("j 34f"),
|
||||
$sformatf("32: csrw 0x%0x, x%0d", PMPCFG2, scratch_reg[2]),
|
||||
$sformatf("j 34f"),
|
||||
$sformatf("33: csrw 0x%0x, x%0d", PMPCFG3, scratch_reg[2]),
|
||||
// End the pmp handler with a labeled nop instruction, this provides a branch target
|
||||
// for the internal routine after it has "fixed" the pmp configuration CSR.
|
||||
$sformatf("35: nop")
|
||||
$sformatf("34: nop")
|
||||
};
|
||||
|
||||
endfunction
|
||||
|
|
|
@ -47,10 +47,6 @@ class riscv_ml_test extends riscv_instr_base_test;
|
|||
`uvm_component_new
|
||||
|
||||
virtual function void randomize_cfg();
|
||||
cfg.no_fence = 0;
|
||||
cfg.init_privileged_mode = MACHINE_MODE;
|
||||
cfg.init_privileged_mode.rand_mode(0);
|
||||
cfg.enable_unaligned_load_store = 1'b1;
|
||||
cfg.addr_translaction_rnd_order_c.constraint_mode(0);
|
||||
`DV_CHECK_RANDOMIZE_FATAL(cfg)
|
||||
cfg.addr_translaction_rnd_order_c.constraint_mode(1);
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue