diff --git a/vendor/google_riscv-dv.lock.hjson b/vendor/google_riscv-dv.lock.hjson index a4e15b6a..fdfbb511 100644 --- a/vendor/google_riscv-dv.lock.hjson +++ b/vendor/google_riscv-dv.lock.hjson @@ -9,6 +9,6 @@ upstream: { url: https://github.com/google/riscv-dv - rev: 7b38e54c5e833f147edc03717b3fd711be923026 + rev: 1ad73cc43f8f84d93d49040f8b2928e74efdd854 } } diff --git a/vendor/google_riscv-dv/HANDSHAKE.md b/vendor/google_riscv-dv/HANDSHAKE.md deleted file mode 100644 index c18bd786..00000000 --- a/vendor/google_riscv-dv/HANDSHAKE.md +++ /dev/null @@ -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. diff --git a/vendor/google_riscv-dv/docs/source/handshake.rst b/vendor/google_riscv-dv/docs/source/handshake.rst new file mode 100644 index 00000000..6ed2ac1c --- /dev/null +++ b/vendor/google_riscv-dv/docs/source/handshake.rst @@ -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 `_. +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 `_. +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 `_, 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 `_. +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 `_. +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 `_, as these are heavily used to verify the core's response to external debug and interrupt stimulus. diff --git a/vendor/google_riscv-dv/docs/source/index.rst b/vendor/google_riscv-dv/docs/source/index.rst index 48200902..2ef50272 100644 --- a/vendor/google_riscv-dv/docs/source/index.rst +++ b/vendor/google_riscv-dv/docs/source/index.rst @@ -20,6 +20,7 @@ Welcome to riscv-dv's documentation! customize_extend_generator class_reference cmd_line_reference + handshake appendix diff --git a/vendor/google_riscv-dv/src/isa/riscv_b_instr.sv b/vendor/google_riscv-dv/src/isa/riscv_b_instr.sv index 1b9120f2..0a18c15a 100755 --- a/vendor/google_riscv-dv/src/isa/riscv_b_instr.sv +++ b/vendor/google_riscv-dv/src/isa/riscv_b_instr.sv @@ -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 diff --git a/vendor/google_riscv-dv/src/isa/riscv_floating_point_instr.sv b/vendor/google_riscv-dv/src/isa/riscv_floating_point_instr.sv index e65c5b1f..35853f05 100644 --- a/vendor/google_riscv-dv/src/isa/riscv_floating_point_instr.sv +++ b/vendor/google_riscv-dv/src/isa/riscv_floating_point_instr.sv @@ -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 diff --git a/vendor/google_riscv-dv/src/isa/riscv_instr_cov.svh b/vendor/google_riscv-dv/src/isa/riscv_instr_cov.svh index 63f20de8..e828f817 100644 --- a/vendor/google_riscv-dv/src/isa/riscv_instr_cov.svh +++ b/vendor/google_riscv-dv/src/isa/riscv_instr_cov.svh @@ -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 diff --git a/vendor/google_riscv-dv/src/riscv_pmp_cfg.sv b/vendor/google_riscv-dv/src/riscv_pmp_cfg.sv index c2b93b32..1bcadb83 100644 --- a/vendor/google_riscv-dv/src/riscv_pmp_cfg.sv +++ b/vendor/google_riscv-dv/src/riscv_pmp_cfg.sv @@ -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 . - // 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 . - // If the MPP field is set to M-mode jump to the end of the handler, - // otherwise jump to . - $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 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 // , 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 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 diff --git a/vendor/google_riscv-dv/test/riscv_instr_test_lib.sv b/vendor/google_riscv-dv/test/riscv_instr_test_lib.sv index cf83ffad..c7614859 100644 --- a/vendor/google_riscv-dv/test/riscv_instr_test_lib.sv +++ b/vendor/google_riscv-dv/test/riscv_instr_test_lib.sv @@ -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);