mirror of
https://github.com/openhwgroup/cve2.git
synced 2025-04-22 13:07:46 -04:00
Update lowrisc_ip to lowRISC/opentitan@e619fc60
This updates the vendored code from OpenTitan and fixes up patches as we go. The biggest change is that the support files that were in dv/data have moved to dv/tools/dvsim (with a couple of other internal renames). The icache test code also needs the corresponding path change and to rename its regression from "sanity" to "smoke" (the new name for the default regression). Update code from upstream repository https://github.com/lowRISC/opentitan to revision e619fc60c6b9c755043eba65a41dc47815612834 * [dv] Remove duplicated keys from common_sim_cfg.hjson (Rupert Swarbrick) * [dv] two small fix in dv (Cindy Chen) * [dv] Comment out example build modes from common_sim_cfg.hjson (Rupert Swarbrick) * [dv/keymgr] Cleanup some warnings in xcelium (Weicai Yang) * [lc_ctrl] Reuse an instance of the RISC-V dmi_jtag as the LC TAP (Michael Schaffner) * [otp_ctrl] Update LC types within OTP (Michael Schaffner) * [lc_ctrl] Add first cut implementation (Michael Schaffner) * [flash_ctrl] update prim flash interface (Timothy Chen) * [flash_ctrl] Add support for isolated flash partition (Timothy Chen) * [dv/common] update naming from sanity to smoke (Cindy Chen) * [prim] update naming from sanity to smoke (Cindy Chen) * [dv/base] add get_reg_by_name support in dv_base_reg_block (Cindy Chen) * [cov methodology] Functional coverage prototype (Srikrishna Iyer) * [dv] Fix tpyo (Weicai Yang) * [dv common] Wave dumping improvements / fix (Srikrishna Iyer) * [dv] Fix for `--run-only` switch (Srikrishna Iyer) * [prim_present] Add support for iterative full-round PRESENT (Michael Schaffner) * [dv] Fix VCS compile error (Weicai Yang) * [sparse-fsm-encode] Switch to Safe Rust Encoding (Sam Elliott) * [sparse-fsm-encode] Disallow Complementary Encodings (Sam Elliott) * [prim/util] Fix parameter type when using prefixes (Pirmin Vogel) * [keymgr/prim_lfsr] Correct minor errors in core files (Michael Schaffner) * [design checklist] avoid using word sanity (Cindy Chen) * [prim_lc_sync] Add two stage sync for life cycle control signals (Michael Schaffner) * [flash] update flash program to support ack / done / last (Timothy Chen) * [prim] update prim flash to have ack / done support (Timothy Chen) * Fix typo in testplan template (Rupert Swarbrick) * [dv] Fix license header for some cfg files (Weicai Yang) * [dv] Only check scoreboard from pre_abort if we were in run phase (Rupert Swarbrick) * [doc] Add lint requirements to V1 checklist (Cindy Chen) * [dv common] Minor enhancements to dv_reg_block (Srikrishna Iyer) * [dv] Fix library paths for dsim (Srikrishna Iyer) * [keymgr/dv] Update testbench (Weicai Yang) * [dv/common] Add DV_ALERT_IF_CONNECT macro (Weicai Yang) * [dv, common] Promote VCS warning to error (Srikrishna Iyer) * [prim] update clock_mux prim to avoid using BUFG (Timothy Chen) * [clkmgr] Add divider bypass during test mode (Timothy Chen) * [opt_ctrl] Change state_q assignment to ease debugging (Michael Schaffner) * [doc] Update D2 checklist and propagate updates to IPs (Michael Schaffner) * [dv/dvsim] Fix -c option compile error (Cindy Chen) * [dv] Tidy up use of get_normalized_addr (Rupert Swarbrick) * [fpv] Fix fusesoc dependecy issue (Cindy Chen) * [lint] Fix lint warning (Cindy Chen) * [dv/lint] Add new DV TB to lint batch script (Cindy Chen) * [fpv] Add lint checking to FPV tb (Cindy Chen) * [dvsim] Remove process_exports() from the code (Srikrishna Iyer) * [dvsim] Fix HJson bugs (Srikrishna Iyer) * [fpv] alert_rx/tx updates (Cindy Chen) * [prim] slicer lint fix (Eunchan Kim) * [prim] Packer to remove unused parameter. (Eunchan Kim) * [prim_lfsr] Update prim_lfsr and testbench to use correct perm width (Michael Schaffner) * [prim_lfsr] Add script to generate seed and perm constants (Michael Schaffner) * [dv/common] Upgrade some VCS warnings to errors (Weicai Yang) * [dvsim] Document and slightly improve subst_wildcards in utils.py (Rupert Swarbrick) * [csrng/dv] Initial dv environment (Steve Nelson) * [sparse-fsm-encode] Update template to prevent JG compile error (Michael Schaffner) * Gracefully shut down Verilator when software test fails (Philipp Wagner) * [otp] fix FPV compile error (Cindy Chen) * [dvsim] Kill subprocesses more gracefully (Rupert Swarbrick) * [prim] Fix Verilator lint warnings (Pirmin Vogel) * [memutil] Allocate the right number of bytes in StagedMem::GetFlat() (Rupert Swarbrick) * [memutil] Load ELF files via a staging area (Rupert Swarbrick) * [memutil] Add iterator and merging insertion interfaces to RangedMap (Rupert Swarbrick) * [memutil] Factor out "ranged map" implementation from dpi_memutil (Rupert Swarbrick) * [alert_handler] update alert hander ports (Timothy Chen) * [otp_ctrl] Update OTP output data mapping (Michael Schaffner) * [otp_ctrl] Split partition metadata into separate package (Michael Schaffner) * [prim_otp] Add TL-UL regfile for testing (sim only) (Michael Schaffner) * [memutil] Split out the non-verilator part of verilator_memutil (Rupert Swarbrick) * [dv/common] Update DV_CHECK_* macros (Weicai Yang) * [dv/common] Fix testplan path (Weicai Yang) * [prim_assert] Fixed non-UVM part of `ASSERT_ERROR (Srikrishna Iyer) * [otp_ctrl] Simplify and consolidate OTP error codes (Michael Schaffner) * [kmac] Fix critical syntax errors. (Eunchan Kim) * [dv/common] Move testplan from tools directory to data (Weicai Yang) * [dvsim] Rename verbosity wildcards to something more informative (Rupert Swarbrick) * [dv/lfsr] Update prim_lfsr_sim_cfg.hjson and add coverage (Udi Jonnalagadda) * [dv common] Added string check macros (Srikrishna Iyer) * [rtl] Use platform-agnostic log macros prim_assert (Srikrishna Iyer) * [dv] Minor fixups to dv_Utils_pkg (Srikrishna Iyer) * [dv] Fix platform-agnostic log macros (Srikrishna Iyer) * [checklist] Upgrade wording for D1 milestone (Scott Johnson) * [entropy_src/rtl] fix for dv sanity test (Mark Branstad) * [lint] Add option to bail out on first invalid Tcl cmd (Michael Schaffner) * [sram_ctrl] Add first cut implementation (Michael Schaffner) * [prim] Fix AscentLint waiver that made the tool crash (Michael Schaffner) * [checklists] Clean up and align HW and SW checklists (Michael Schaffner) * [prim] Update signal name in lint waiver rule (Pirmin Vogel) * [flash_ctrl] Switch to new keyschedule in PRINCE (Michael Schaffner) * [lint] fix the waiver format (Eunchan Kim) * [dv] Waive lint warnings in dv_macros.svh (Srikrishna Iyer) * [dv common] Add platform-agnostic log macros (Srikrishna Iyer) * [util] Add Rust Enum Support to sparse-fsm-encode.py (Sam Elliott) * [util] Add C Enum Support to sparse-fsm-encode.py (Sam Elliott) * [sparse-fsm-encode] Expand error and help messages (Michael Schaffner) * [dv/common] TLUL agent function coverage (Weicai Yang) * [dv/shadow_reg] support alert handshake checking (Cindy Chen) * [prim_present/otp_ctrl] Add round index state IOs to primitive (Michael Schaffner) * [dv] Fix 2 regression failures (Weicai Yang) * [prim_multibit_sync] Add multibit synchronizer with consistency check (Michael Schaffner) * [prim] Fix Lint warning for prim_slicer (Eunchan Kim) * [prim_generic_otp] Add TL-UL test interface stub for DV (Michael Schaffner) * [doc] Improve documentation for common_ifs (Rupert Swarbrick) * [doc] Improve pins_if block diagram (Rupert Swarbrick) * [prim_prince/present] Remove TODOs (Michael Schaffner) * [dv/common] Change TL item content when it's not accepted (Weicai Yang) * [dv/uvmgen] update has_alerts (Cindy Chen) * [dv/common] Add run opt plusarg to enable file path in the log (Weicai Yang) * [prim] Add clock buffer primitive for Xilinx FPGAs (Pirmin Vogel) * [otp_ctrl] Provision power sequencing signals (Michael Schaffner) * [dv/common] Clean up old makefile flow (Weicai Yang) * [entropy_src/rtl] review round2 changes (Mark Branstad) * [otp_ctrl] Update all FSMs to use prim_flop for the state (Michael Schaffner) * [prim_xilinx_flop] Add a Xilinx version with keep attribute (Michael Schaffner) * [prim/util] Update sparse-fsm-encode and include FSM template (Michael Schaffner) * [DV macros] minor enhancement to `DV_SPINWAIT (Srikrishna Iyer) * [DV common] Add DV_ASSERT_CTRL macro (Srikrishna Iyer) * [DV common] Enhance `DV_CHECK_MEMBER_RANDOMIZE_*` (Srikrishna Iyer) * [otbn] Use relative scope names for OTBN scopes (Rupert Swarbrick) * [verilator simutil] Add support for relative scope names to SVScoped (Rupert Swarbrick) * [fpv/prim_packer] remove assumption (Cindy Chen) * [fpv/csr_assert] support all modules for CSR assert (Cindy Chen) * [memutil] Teach verilator_memutil to load multi-segment ELF files (Rupert Swarbrick) * [memutil] Simplify how we read ELF files in verilator_memutil.cc (Rupert Swarbrick) * [memutil] Add a "verbose" flag to detail memory loads (Rupert Swarbrick) * [memutil] Parse all arguments before loading anything (Rupert Swarbrick) * [memutil] Use override keyword, not virtual for overridden method (Rupert Swarbrick) * [memutil] Use exceptions to simplify error handling (Rupert Swarbrick) * [memutil] Store the width of memory areas in bytes, not bits (Rupert Swarbrick) * [memutil] Allow memory locations to have associated LMAs (Rupert Swarbrick) * [memutil] Improve type of ElfFileToBinary in verilator_memutil.cc (Rupert Swarbrick) * [verilator simutil] Move SVScoped class into dv/verilator/cpp (Rupert Swarbrick) * [memutil] Move static functions out of VerilatorMemUtil class (Rupert Swarbrick) * [memutil] Run clang-format on verilator_memutil.* (Rupert Swarbrick) * [dv:entropy_src] Initial rng_agent and integrated into entropy_src env (Steve Nelson) * [prim_ram_adv/fpv] fix assertion (Cindy Chen) * [prim_ram_1p_scr] Simplify nonce input and align to multiples of 64b (Michael Schaffner) * [fpv/csr_assert] add csr support for regwen (Cindy Chen) * [prim*] Various lint fixes in the prims (Michael Schaffner) * [prim] remove FPV related assertions (Eunchan Kim) * [prim_lfsr] Add option to supply custom output permutation (Michael Schaffner) * [dv/common] calculate addr map size in RAL (Weicai Yang) * [flash_ctrl] Add ECC to program / erase datapaths (Timothy Chen) * [otp_ctrl] First cut implementation of the OTP controller (Michael Schaffner) * Fix invalid read in verilator_memutil (Rupert Swarbrick) * [doc] Don't strip markdown headings from HW checklist (Philipp Wagner) * [site] Set lint title (Tobias Wölfel) * [dv/prim] add basic PRINCE testbench (Udi Jonnalagadda) * [flash_ctrl] Support the notion of a 'program-repair'. (Timothy Chen) * [prim/tlul] Various small lint fixes (Michael Schaffner) * [dv/uvmdvgen] update dvsim and remove Makefile (Cindy Chen) * [util] Add script for generating sparse FSM encodings (Michael Schaffner) * [prim] Add option to register output for interrupts (Timothy Chen) * [prim_otp] First cut implementation of FPGA emulation (Michael Schaffner) * [prim_ram_1p_adv] Add 16bit ECC mode (Michael Schaffner) * [chip dv] Fix for failing GPIO test (Srikrishna Iyer) * [RTl] Generic pad wrapper default behavior fix (Srikrishna Iyer) * [slicer] Select partial from bitstream (Eunchan Kim) * [util] Don't hack __repr__ in FlowCfg (Rupert Swarbrick) * [util] Fix lint in dvsim.py (Rupert Swarbrick) * [fpv/prim_packer] Add a FPV TB (Cindy Chen) * [Keccak] Keccak_f implementation (Eunchan Kim) * [dv/csr] add common task for csr_or_field_rd_check (Cindy Chen) * [keccak] Add valid signal to random value (Eunchan Kim) * [prim] Add primitive clock divider (Timothy Chen) * [dv/shadow_reg] update sequence for storage error (Cindy Chen) * [dv/lib] clear csr_outstanding_access after reset (Cindy Chen) * [sw] Ensure Headers are Correctly Ordered (Sam Elliott) * [dv] Fix csr_rd check during reset (Weicai Yang) * Adding the first update to coverage methodology (Rasmus Madsen) * [dv] TL agent supports no clock reset (Weicai Yang) * [tlul/dv] Update test plan for tl errors (Weicai Yang) * [fpv/alert] update namings for FPV tb (Cindy Chen) * [keccak] Masked/Unmasked Keccak single round (Eunchan Kim) * [lint/prim*] Waive STAR_PORT_CONN_USE errors in generated prims (Michael Schaffner) * [prim_usb_diff_rx] Carry over wrapper for USB diff receiver (Michael Schaffner) Signed-off-by: Rupert Swarbrick <rswarbrick@lowrisc.org>
This commit is contained in:
parent
31a18ad10c
commit
4852e307b7
251 changed files with 7914 additions and 3091 deletions
|
@ -25,7 +25,7 @@ default-seed := 123
|
|||
SEED=$(if $(filter 1,$(RESEED)),$(default-seed),)
|
||||
|
||||
# Specify which tests to run. Defaults to the empty string, which
|
||||
# means dvsim will run its default (the "sanity" suite).
|
||||
# means dvsim will run its default (the "smoke" suite of tests).
|
||||
TESTS=
|
||||
|
||||
ibex-top := ../../../..
|
||||
|
|
|
@ -23,7 +23,7 @@
|
|||
// Import additional common sim cfg files.
|
||||
import_cfgs: [
|
||||
// Project wide common sim cfg file
|
||||
"{proj_root}/vendor/lowrisc_ip/dv/data/common_sim_cfg.hjson"
|
||||
"{proj_root}/vendor/lowrisc_ip/dv/tools/dvsim/common_sim_cfg.hjson"
|
||||
]
|
||||
|
||||
build_modes: [
|
||||
|
@ -111,7 +111,7 @@
|
|||
// List of regressions.
|
||||
regressions: [
|
||||
{
|
||||
name: sanity
|
||||
name: smoke
|
||||
tests: ["ibex_icache_sanity",
|
||||
"ibex_icache_passthru",
|
||||
"ibex_icache_caching",
|
||||
|
|
2
vendor/lowrisc_ip.lock.hjson
vendored
2
vendor/lowrisc_ip.lock.hjson
vendored
|
@ -9,6 +9,6 @@
|
|||
upstream:
|
||||
{
|
||||
url: https://github.com/lowRISC/opentitan
|
||||
rev: 92e9242424c72c59008e267dd3779e2af5ec8e83
|
||||
rev: e619fc60c6b9c755043eba65a41dc47815612834
|
||||
}
|
||||
}
|
||||
|
|
19
vendor/lowrisc_ip.vendor.hjson
vendored
19
vendor/lowrisc_ip.vendor.hjson
vendored
|
@ -11,20 +11,10 @@
|
|||
}
|
||||
|
||||
mapping: [
|
||||
// We have to apply a patch to the vendored files from hw/dv/data,
|
||||
// because they contain an OpenTitan specific path.
|
||||
{
|
||||
from: "hw/dv/data",
|
||||
to: "dv/data",
|
||||
patch_dir: "dv_data"
|
||||
},
|
||||
|
||||
{from: "hw/dv/sv/common_ifs", to: "dv/sv/common_ifs"},
|
||||
{from: "hw/dv/sv/csr_utils", to: "dv/sv/csr_utils"},
|
||||
{from: "hw/dv/sv/dv_base_reg", to: "dv/sv/dv_base_reg"},
|
||||
{from: "hw/dv/sv/mem_model", to: "dv/sv/mem_model"},
|
||||
{from: "hw/dv/tools", to: "dv/tools"},
|
||||
{from: "hw/dv/verilator", to: "dv/verilator"},
|
||||
|
||||
// We apply a patch to fix the bus_params_pkg core file name when
|
||||
// vendoring in dv_lib and dv_utils. This allows us to have an
|
||||
|
@ -39,6 +29,15 @@
|
|||
to: "dv/sv/dv_utils",
|
||||
patch_dir: "dv_utils",
|
||||
},
|
||||
// We have to apply a patch to the vendored files from hw/dv/tools
|
||||
// because they contain OpenTitan specific paths.
|
||||
{
|
||||
from: "hw/dv/tools",
|
||||
to: "dv/tools",
|
||||
patch_dir: "dv_tools"
|
||||
},
|
||||
|
||||
{from: "hw/dv/verilator", to: "dv/verilator"},
|
||||
|
||||
{from: "hw/ip/prim", to: "ip/prim"},
|
||||
{from: "hw/ip/prim_generic", to: "ip/prim_generic"},
|
||||
|
|
113
vendor/lowrisc_ip/dv/sv/common_ifs/index.md
vendored
113
vendor/lowrisc_ip/dv/sv/common_ifs/index.md
vendored
|
@ -8,43 +8,55 @@ connecting dut signals. They are described in detail below.
|
|||
|
||||
### `clk_if`
|
||||
This is a passive clock interface that is used to wait for clock events in
|
||||
testbenches.
|
||||
This interface has two clocking blocks: `cb` and `cbn` for synchronizing to
|
||||
positive and negative clock edges. This interface consists of following tasks:
|
||||
testbenches. This interface has two clocking blocks, `cb` and `cbn`, for
|
||||
synchronizing to positive and negative clock edges, respectively. The interface
|
||||
also has the following tasks:
|
||||
* `wait_clks`: waits for specified number of positive clock edges
|
||||
* `wait_n_clks`: waits for specified number of negative clock edges
|
||||
|
||||
### `clk_rst_if`
|
||||
This interface provides the ability to drive / sample clock and reset signal.
|
||||
It provides various methods related to clock and reset generation. These
|
||||
methods can be categorized into `setup methods` and `drive / sample` methods.
|
||||
Following are `setup methods` of `pins_if`:
|
||||
* `set_freq_mhz`: set the clk frequency in mhz and calclate period in ns
|
||||
* `set_duty_cycle`: set the duty cycle (1-99)
|
||||
* `set_active`: enables `clk` and `rst_n` generation
|
||||
typically, called at t=0 (from tb top)
|
||||
* `set_period_ns`: set the clk period in ns and calculate frequency in mhz
|
||||
* `set_jitter_chance_pc`: set jitter chance in percentage (0 - 100)
|
||||
* 0: do not add any jitter
|
||||
* 100: add jitter on every clock edge
|
||||
* `set_max_jitter_ps`: set maximum jitter in ps
|
||||
Following are `drive / sample` methods of `pins_if`:
|
||||
* `wait_for_reset`: wait for rst_n to assert and then deassert
|
||||
* `apply_reset`: apply reset with specified scheme out of following:
|
||||
* fullly synchronous reset
|
||||
* async assert, sync dessert
|
||||
* async assert, async dessert
|
||||
* clk gated when reset asserted
|
||||
* `add_jitter`: add jitter to `clk_hi` and `clk_lo` half periods based on
|
||||
`jitter_chance_pc`
|
||||
* `start_clk`: start / ungate clock
|
||||
|
||||
Unlike `clk_if`, this interface can generate a clock and a reset signal. These
|
||||
are connected as `inout` signals and the interface observes them passively
|
||||
unless the `set_active` function is called.
|
||||
|
||||
Just like `clk_if`, this interface has clocking blocks `cb` and `cbn`, together
|
||||
with `wait_clks` and `wait_n_clks` utility tasks. It also has
|
||||
* `wait_for_reset`: wait for a reset signalled on `rst_n`
|
||||
|
||||
To generate a clock signal, call `set_active` at the start of the simulation.
|
||||
This is typically called from an `initial` block in the testbench. To configure
|
||||
the frequency and duty cycle of the generated clock, use the following
|
||||
functions:
|
||||
* `set_freq_mhz` / `set_freq_khz`: set the clock frequency in MHz / KHz. This
|
||||
is 50MHz by default.
|
||||
* `set_period_ns`: set the clock period in nanoseconds. This is 20ns by default
|
||||
(giving a clock period of 50MHz).
|
||||
* `set_duty_cycle`: set the duty cycle (as a percentage: 1 - 99). This is 50 by
|
||||
default.
|
||||
|
||||
The clock can also have jitter added. This is generated as an offset in
|
||||
picoseconds added to randomly selected clock half-periods. It can be enabled
|
||||
and configured with:
|
||||
* `set_jitter_chance_pc`: set the percentage probability of adding a jitter to
|
||||
a given half-period. By default, this is 0 and the clock has no jitter.
|
||||
* `set_max_jitter_ps`: set the maximum jitter to add to each clock half-period
|
||||
in picoseconds. This is 1000ps (1 ns) by default.
|
||||
|
||||
To start and stop the clock or apply a reset, use the following tasks. These
|
||||
will have no effect if `set_active` has not been called.
|
||||
* `start_clk`: start the clock. The clock is started by default, so this
|
||||
task is only needed after a call to `stop_clk`.
|
||||
* `stop_clk`: stop / gate the clk
|
||||
* `wait_clks`: waits for specified number of positive clock edges
|
||||
* `wait_n_clks`: waits for specified number of negative clock edges
|
||||
* `apply_reset`: signal a reset on `rst_n`. The length of this reset and
|
||||
whether it is synchronous or not can be configured with arguments to the
|
||||
function.
|
||||
|
||||
### `pins_if`
|
||||
This paramterized interface provides the ability to drive / sample any signal
|
||||
|
||||
This parameterized interface provides the ability to drive or sample any signal
|
||||
in the DUT.
|
||||
|
||||
```systemverilog
|
||||
interface pins_if #(
|
||||
parameter int Width = 1
|
||||
|
@ -52,30 +64,23 @@ interface pins_if #(
|
|||
inout [Width-1:0] pins
|
||||
);
|
||||
```
|
||||
The member `pins` is inout type and it can be connected to any of input or
|
||||
output port within of dut to drive or sample them. `pins` can be driven either
|
||||
internally using `pins_o` and `pins_oe` signals, that constitute a tri-state
|
||||
buffer implementation. This provide an ability to disconnects `pins` by driving
|
||||
them to high impedance state. `pins` may also be driven through an external
|
||||
driver that it gets connected to. This interface also provides capability
|
||||
to drive weak pull-up or pull-down on `pins` in case of no internal or external
|
||||
drivers. The members `pins_pu` and `pins_pd` control weak pull-up or pull-down
|
||||
functionality. Following diagram explains working of `pins_if`:
|
||||
|
||||
## `pins_if` block diagram
|
||||
By default, it behaves as a passive interface. The values of the pins can be
|
||||
read with the following functions:
|
||||
* `sample`: sample and return all the pin values
|
||||
* `sample_pin`: sample just the given pin
|
||||
|
||||
The interface can also be configured to drive, pull up, or pull down its
|
||||
outputs. To do this, call
|
||||
* `drive` / `drive_pin`: Drive the output to the given value.
|
||||
* `drive_en` / `drive_en_pin`: Configure output enable; when enabled, this
|
||||
drives value previously stored by a call to `drive` or `drive_pin`.
|
||||
* `set_pullup_en` / `set_pullup_en_pin`: Configure pull-up setting. If true and
|
||||
output enable is false, drives the output to `1`.
|
||||
* `set_pulldown_en` / `set_pulldown_en_pin`: Configure pull-down setting. If
|
||||
true and both output_enable and pull-up are false, drives the output to `0`.
|
||||
|
||||
The diagram below gives a schematic view of `pins_if`. The driver shown is
|
||||
replicated for each bit.
|
||||
|
||||

|
||||
|
||||
Some of the commonly used methods of `pins_if` are as follows:
|
||||
* `drive_en_pin`: Drive specified value `val` on specified index `idx` of
|
||||
`pins_oe` signal
|
||||
* `drive_en`: Drive `pins_oe` signal to specified value `val`
|
||||
* `drive_pin`: Drive specified index `idx` of pins_oe signal to 1, and the same
|
||||
index of `pins_o` to specified value `val`
|
||||
value
|
||||
* `drive`: Drive `pins_oe` to all 1's and specified value `val` on `pins_o`
|
||||
* `sample_pin`: Sample and return value of `pins[idx]` for specified index `idx`
|
||||
* `sample`: Sample and return value of `pins`
|
||||
* `set_pullup_en`: Implement pull-up on specific bits of `pins` based on
|
||||
specified value `val`
|
||||
* `set_pulldown_en`: Implement pull-down on specifc bits of `pins` based on
|
||||
specified value `val`
|
||||
|
|
33
vendor/lowrisc_ip/dv/sv/common_ifs/pins_if.sv
vendored
33
vendor/lowrisc_ip/dv/sv/common_ifs/pins_if.sv
vendored
|
@ -15,7 +15,6 @@ interface pins_if #(
|
|||
|
||||
|
||||
logic [Width-1:0] pins_o; // value to be driven out
|
||||
wire [Width-1:0] pins_int; // value of pin using internal pull-up / pull-down
|
||||
bit [Width-1:0] pins_oe = '0; // output enable
|
||||
bit [Width-1:0] pins_pd = '0; // pull down enable
|
||||
bit [Width-1:0] pins_pu = '0; // pull up enable
|
||||
|
@ -73,24 +72,26 @@ interface pins_if #(
|
|||
endfunction
|
||||
|
||||
// make connections
|
||||
generate
|
||||
for (genvar i = 0; i < Width; i++) begin : each_pin
|
||||
assign pins_int[i] = pins_pd[i] ? 1'b0 :
|
||||
pins_pu[i] ? 1'b1 : 1'bz;
|
||||
// If output enable is 1, strong driver assigns pin to 'value to be driven out';
|
||||
// the external strong driver can still affect pin, if exists.
|
||||
// Else if output enable is 0, weak pullup or pulldown is applied to pin.
|
||||
// By doing this, we make sure that weak pullup or pulldown does not override
|
||||
// any 'x' value on pin, that may result due to conflicting values
|
||||
// between 'value to be driven out' and the external driver's value.
|
||||
assign pins[i] = pins_oe[i] ? pins_o[i] : 1'bz;
|
||||
for (genvar i = 0; i < Width; i++) begin : each_pin
|
||||
`ifdef VERILATOR
|
||||
assign pins[i] = ~pins_oe[i] ? pins_int[i] : 1'bz;
|
||||
assign pins[i] = pins_oe[i] ? pins_o[i] :
|
||||
pins_pu[i] ? 1'b1 :
|
||||
pins_pd[i] ? 1'b0 : 1'bz;
|
||||
`else
|
||||
assign (pull0, pull1) pins[i] = ~pins_oe[i] ? pins_int[i] : 1'bz;
|
||||
// Drive the pin with pull strength based on whether pullup / pulldown is enabled.
|
||||
assign (pull0, pull1) pins[i] = ~pins_oe[i] ? (pins_pu[i] ? 1'b1 :
|
||||
pins_pd[i] ? 1'b0 : 1'bz) : 1'bz;
|
||||
|
||||
|
||||
// If output enable is 1, strong driver assigns pin to 'value to be driven out';
|
||||
// the external strong driver can still affect pin, if exists.
|
||||
// Else if output enable is 0, weak pullup or pulldown is applied to pin.
|
||||
// By doing this, we make sure that weak pullup or pulldown does not override
|
||||
// any 'x' value on pin, that may result due to conflicting values
|
||||
// between 'value to be driven out' and the external driver's value.
|
||||
assign pins[i] = pins_oe[i] ? pins_o[i] : 1'bz;
|
||||
`endif
|
||||
end
|
||||
endgenerate
|
||||
end
|
||||
|
||||
endinterface
|
||||
`endif
|
||||
|
|
97
vendor/lowrisc_ip/dv/sv/common_ifs/pins_if.svg
vendored
97
vendor/lowrisc_ip/dv/sv/common_ifs/pins_if.svg
vendored
File diff suppressed because one or more lines are too long
Before Width: | Height: | Size: 56 KiB After Width: | Height: | Size: 12 KiB |
2
vendor/lowrisc_ip/dv/sv/csr_utils/README.md
vendored
2
vendor/lowrisc_ip/dv/sv/csr_utils/README.md
vendored
|
@ -123,7 +123,7 @@ Supported CSR test sequences are:
|
|||
the read results with the expected values
|
||||
|
||||
### CSR exclusion methodology
|
||||
The CSR test sequences listed above intend to perform a sanity check to CSR
|
||||
The CSR test sequences listed above intend to perform a basic check to CSR
|
||||
read/write accesses, but do not intend to check specific DUT functionalities. Thus the
|
||||
sequences might need to exclude reading or writing certain CSRs depending on the
|
||||
specific testbench.
|
||||
|
|
60
vendor/lowrisc_ip/dv/sv/csr_utils/csr_seq_lib.sv
vendored
60
vendor/lowrisc_ip/dv/sv/csr_utils/csr_seq_lib.sv
vendored
|
@ -219,14 +219,6 @@ class csr_rw_seq extends csr_base_seq;
|
|||
|
||||
`uvm_object_new
|
||||
|
||||
rand bit do_csr_rd_check;
|
||||
rand bit do_csr_field_rd_check;
|
||||
|
||||
constraint csr_or_field_rd_check_c {
|
||||
// at least one of them should be set
|
||||
do_csr_rd_check || do_csr_field_rd_check;
|
||||
}
|
||||
|
||||
virtual task body();
|
||||
foreach (test_csrs[i]) begin
|
||||
uvm_reg_data_t wdata;
|
||||
|
@ -243,7 +235,6 @@ class csr_rw_seq extends csr_base_seq;
|
|||
`uvm_info(`gtn, $sformatf("Verifying register read/write for %0s",
|
||||
test_csrs[i].get_full_name()), UVM_MEDIUM)
|
||||
|
||||
`DV_CHECK_FATAL(randomize(do_csr_rd_check, do_csr_field_rd_check))
|
||||
`DV_CHECK_STD_RANDOMIZE_FATAL(wdata)
|
||||
wdata &= get_mask_excl_fields(test_csrs[i], CsrExclWrite, CsrRwTest, m_csr_excl_item);
|
||||
|
||||
|
@ -254,33 +245,14 @@ class csr_rw_seq extends csr_base_seq;
|
|||
// register is getting the updated access information.
|
||||
csr_wr(.csr(test_csrs[i]), .value(wdata), .blocking(0), .predict(!external_checker));
|
||||
|
||||
// check if parent block or register is excluded from read-check
|
||||
if (m_csr_excl_item.is_excl(test_csrs[i], CsrExclWriteCheck, CsrRwTest)) begin
|
||||
`uvm_info(`gtn, $sformatf("Skipping register %0s due to CsrExclWriteCheck exclusion",
|
||||
test_csrs[i].get_full_name()), UVM_MEDIUM)
|
||||
continue;
|
||||
end
|
||||
do_check_csr_or_field_rd(.csr(test_csrs[i]),
|
||||
.blocking(0),
|
||||
.compare(!external_checker),
|
||||
.compare_vs_ral(1),
|
||||
.csr_excl_type(CsrExclWriteCheck),
|
||||
.csr_test_type(CsrRwTest),
|
||||
.csr_excl_item(m_csr_excl_item));
|
||||
|
||||
compare_mask = get_mask_excl_fields(test_csrs[i], CsrExclWriteCheck, CsrRwTest,
|
||||
m_csr_excl_item);
|
||||
if (do_csr_rd_check) begin
|
||||
csr_rd_check(.ptr (test_csrs[i]),
|
||||
.blocking (0),
|
||||
.compare (!external_checker),
|
||||
.compare_vs_ral(1'b1),
|
||||
.compare_mask (compare_mask));
|
||||
end
|
||||
if (do_csr_field_rd_check) begin
|
||||
test_csrs[i].get_fields(test_fields);
|
||||
test_fields.shuffle();
|
||||
foreach (test_fields[j]) begin
|
||||
bit compare = !m_csr_excl_item.is_excl(test_fields[j], CsrExclWriteCheck, CsrRwTest);
|
||||
csr_rd_check(.ptr (test_fields[j]),
|
||||
.blocking (0),
|
||||
.compare (!external_checker && compare),
|
||||
.compare_vs_ral(1'b1));
|
||||
end
|
||||
end
|
||||
wait_if_max_outstanding_accesses_reached();
|
||||
end
|
||||
endtask
|
||||
|
@ -392,14 +364,7 @@ class csr_bit_bash_seq extends csr_base_seq;
|
|||
val = rg.get();
|
||||
val[k] = ~val[k];
|
||||
err_msg = $sformatf("Wrote %0s[%0d]: %0b", rg.get_full_name(), k, val[k]);
|
||||
csr_wr(.csr(rg), .value(val), .blocking(1));
|
||||
|
||||
// if external checker is not enabled and writes are made non-blocking, then we need to
|
||||
// pre-predict so that the mirrored value will be updated. if we dont, then csr_rd_check task
|
||||
// might pick up stale mirrored value
|
||||
if (!external_checker) begin
|
||||
void'(rg.predict(.value(val), .kind(UVM_PREDICT_WRITE)));
|
||||
end
|
||||
csr_wr(.csr(rg), .value(val), .blocking(1), .predict(!external_checker));
|
||||
|
||||
// TODO, outstanding access to same reg isn't supported in uvm_reg. Need to add another seq
|
||||
// uvm_reg waits until transaction is completed, before start another read/write in same reg
|
||||
|
@ -442,14 +407,7 @@ class csr_aliasing_seq extends csr_base_seq;
|
|||
|
||||
`DV_CHECK_STD_RANDOMIZE_FATAL(wdata)
|
||||
wdata &= get_mask_excl_fields(test_csrs[i], CsrExclWrite, CsrAliasingTest, m_csr_excl_item);
|
||||
csr_wr(.csr(test_csrs[i]), .value(wdata), .blocking(0));
|
||||
|
||||
// if external checker is not enabled and writes are made non-blocking, then we need to
|
||||
// pre-predict so that the mirrored value will be updated. if we dont, then csr_rd_check task
|
||||
// might pick up stale mirrored value
|
||||
if (!external_checker) begin
|
||||
void'(test_csrs[i].predict(.value(wdata), .kind(UVM_PREDICT_WRITE)));
|
||||
end
|
||||
csr_wr(.csr(test_csrs[i]), .value(wdata), .blocking(0), .predict(!external_checker));
|
||||
|
||||
all_csrs.shuffle();
|
||||
foreach (all_csrs[j]) begin
|
||||
|
|
173
vendor/lowrisc_ip/dv/sv/csr_utils/csr_utils_pkg.sv
vendored
173
vendor/lowrisc_ip/dv/sv/csr_utils/csr_utils_pkg.sv
vendored
|
@ -13,13 +13,14 @@ package csr_utils_pkg;
|
|||
`include "dv_macros.svh"
|
||||
|
||||
// local types and variables
|
||||
uint outstanding_accesses = 0;
|
||||
uint default_timeout_ns = 1_000_000; // 1ms
|
||||
uint default_spinwait_timeout_ns = 10_000_000; // 10ms
|
||||
string msg_id = "csr_utils";
|
||||
bit default_csr_blocking = 1;
|
||||
bit under_reset = 0;
|
||||
int max_outstanding_accesses = 100;
|
||||
uint outstanding_accesses = 0;
|
||||
uint default_timeout_ns = 1_000_000; // 1ms
|
||||
uint default_spinwait_timeout_ns = 10_000_000; // 10ms
|
||||
string msg_id = "csr_utils";
|
||||
bit default_csr_blocking = 1;
|
||||
uvm_check_e default_csr_check = UVM_CHECK;
|
||||
bit under_reset = 0;
|
||||
int max_outstanding_accesses = 100;
|
||||
|
||||
// csr field struct - hold field specific params
|
||||
typedef struct {
|
||||
|
@ -174,7 +175,7 @@ package csr_utils_pkg;
|
|||
endtask
|
||||
|
||||
task automatic csr_update(input uvm_reg csr,
|
||||
input uvm_check_e check = UVM_CHECK,
|
||||
input uvm_check_e check = default_csr_check,
|
||||
input uvm_path_e path = UVM_DEFAULT_PATH,
|
||||
input bit blocking = default_csr_blocking,
|
||||
input uint timeout_ns = default_timeout_ns,
|
||||
|
@ -193,7 +194,7 @@ package csr_utils_pkg;
|
|||
|
||||
// subroutine of csr_update, don't use it directly
|
||||
task automatic csr_update_sub(input uvm_reg csr,
|
||||
input uvm_check_e check = UVM_CHECK,
|
||||
input uvm_check_e check = default_csr_check,
|
||||
input uvm_path_e path = UVM_DEFAULT_PATH,
|
||||
input uint timeout_ns = default_timeout_ns,
|
||||
input uvm_reg_map map = null,
|
||||
|
@ -209,7 +210,9 @@ package csr_utils_pkg;
|
|||
csr_pre_write_sub(csr, en_shadow_wr);
|
||||
csr.update(.status(status), .path(path), .map(map), .prior(100));
|
||||
csr_post_write_sub(csr, en_shadow_wr);
|
||||
if (check == UVM_CHECK) begin
|
||||
// when reset occurs, all items will be dropped immediately. This may end up getting
|
||||
// d_error = 1 from previous item on the bus. Skip checking it during reset
|
||||
if (check == UVM_CHECK && !under_reset) begin
|
||||
`DV_CHECK_EQ(status, UVM_IS_OK, "", error, msg_id)
|
||||
end
|
||||
decrement_outstanding_access();
|
||||
|
@ -227,7 +230,7 @@ package csr_utils_pkg;
|
|||
|
||||
task automatic csr_wr(input uvm_reg csr,
|
||||
input uvm_reg_data_t value,
|
||||
input uvm_check_e check = UVM_CHECK,
|
||||
input uvm_check_e check = default_csr_check,
|
||||
input uvm_path_e path = UVM_DEFAULT_PATH,
|
||||
input bit blocking = default_csr_blocking,
|
||||
input bit backdoor = 0,
|
||||
|
@ -238,15 +241,11 @@ package csr_utils_pkg;
|
|||
if (backdoor) begin
|
||||
csr_poke(csr, value, check, predict);
|
||||
end else if (blocking) begin
|
||||
csr_wr_sub(csr, value, check, path, timeout_ns, map, en_shadow_wr);
|
||||
if (predict) void'(csr.predict(.value(value), .kind(UVM_PREDICT_WRITE)));
|
||||
csr_wr_sub(csr, value, check, path, timeout_ns, predict, map, en_shadow_wr);
|
||||
end else begin
|
||||
fork
|
||||
begin
|
||||
csr_wr_sub(csr, value, check, path, timeout_ns, map, en_shadow_wr);
|
||||
// predict after csr_wr_sub, to ensure predict after enable register overwrite the locked
|
||||
// registers' access information
|
||||
if (predict) void'(csr.predict(.value(value), .kind(UVM_PREDICT_WRITE)));
|
||||
csr_wr_sub(csr, value, check, path, timeout_ns, predict, map, en_shadow_wr);
|
||||
end
|
||||
join_none
|
||||
// Add #0 to ensure that this thread starts executing before any subsequent call
|
||||
|
@ -257,9 +256,10 @@ package csr_utils_pkg;
|
|||
// subroutine of csr_wr, don't use it directly
|
||||
task automatic csr_wr_sub(input uvm_reg csr,
|
||||
input uvm_reg_data_t value,
|
||||
input uvm_check_e check = UVM_CHECK,
|
||||
input uvm_check_e check = default_csr_check,
|
||||
input uvm_path_e path = UVM_DEFAULT_PATH,
|
||||
input uint timeout_ns = default_timeout_ns,
|
||||
input bit predict = 0,
|
||||
input uvm_reg_map map = null,
|
||||
input bit en_shadow_wr = 1);
|
||||
fork
|
||||
|
@ -273,9 +273,14 @@ package csr_utils_pkg;
|
|||
csr_pre_write_sub(csr, en_shadow_wr);
|
||||
csr.write(.status(status), .value(value), .path(path), .map(map), .prior(100));
|
||||
csr_post_write_sub(csr, en_shadow_wr);
|
||||
if (check == UVM_CHECK) begin
|
||||
if (check == UVM_CHECK && !under_reset) begin
|
||||
`DV_CHECK_EQ(status, UVM_IS_OK, "", error, msg_id)
|
||||
end
|
||||
// Only update the predicted value if status is ok (otherwise the write isn't completed
|
||||
// successfully and the design shouldn't have accepted the written value)
|
||||
if (status == UVM_IS_OK && predict) begin
|
||||
void'(csr.predict(.value(value), .kind(UVM_PREDICT_WRITE)));
|
||||
end
|
||||
decrement_outstanding_access();
|
||||
end
|
||||
begin
|
||||
|
@ -314,15 +319,16 @@ package csr_utils_pkg;
|
|||
endtask
|
||||
|
||||
// backdoor write csr
|
||||
task automatic csr_poke(input uvm_reg csr,
|
||||
input uvm_reg_data_t value,
|
||||
input uvm_check_e check = UVM_CHECK,
|
||||
input bit predict = 0);
|
||||
task automatic csr_poke(input uvm_reg csr,
|
||||
input uvm_reg_data_t value,
|
||||
input uvm_check_e check = default_csr_check,
|
||||
input bit predict = 0,
|
||||
input bkdr_reg_path_e kind = BkdrRegPathRtl);
|
||||
uvm_status_e status;
|
||||
string msg_id = {csr_utils_pkg::msg_id, "::csr_poke"};
|
||||
uvm_reg_data_t old_mirrored_val = csr.get_mirrored_value();
|
||||
|
||||
csr.poke(.status(status), .value(value));
|
||||
csr.poke(.status(status), .value(value), .kind(kind.name));
|
||||
if (check == UVM_CHECK && status != UVM_IS_OK) begin
|
||||
string str;
|
||||
uvm_hdl_path_concat paths[$];
|
||||
|
@ -332,26 +338,28 @@ package csr_utils_pkg;
|
|||
csr.get_full_name(), str))
|
||||
end
|
||||
// poke always updates predict value, if predict == 0, revert back to old mirrored value
|
||||
if (!predict) begin
|
||||
if (!predict || kind == BkdrRegPathRtlShadow) begin
|
||||
void'(csr.predict(.value(old_mirrored_val), .kind(UVM_PREDICT_DIRECT)));
|
||||
end
|
||||
endtask
|
||||
|
||||
task automatic csr_rd(input uvm_object ptr, // accept reg or field
|
||||
output uvm_reg_data_t value,
|
||||
input uvm_check_e check = UVM_CHECK,
|
||||
input uvm_check_e check = default_csr_check,
|
||||
input uvm_path_e path = UVM_DEFAULT_PATH,
|
||||
input bit blocking = default_csr_blocking,
|
||||
input bit backdoor = 0,
|
||||
input uint timeout_ns = default_timeout_ns,
|
||||
input uvm_reg_map map = null);
|
||||
if (backdoor) begin
|
||||
csr_peek(ptr, value, check);
|
||||
end else if (blocking) begin
|
||||
csr_rd_sub(ptr, value, check, path, timeout_ns, map);
|
||||
uvm_status_e status;
|
||||
if (blocking) begin
|
||||
csr_rd_sub(.ptr(ptr), .value(value), .status(status), .check(check), .path(path),
|
||||
.backdoor(backdoor), .timeout_ns(timeout_ns), .map(map));
|
||||
end else begin
|
||||
`DV_CHECK_EQ(backdoor, 0, "Don't enable backdoor with blocking = 0", error, msg_id)
|
||||
fork
|
||||
csr_rd_sub(ptr, value, check, path, timeout_ns, map);
|
||||
csr_rd_sub(.ptr(ptr), .value(value), .status(status), .check(check), .path(path),
|
||||
.backdoor(backdoor), .timeout_ns(timeout_ns), .map(map));
|
||||
join_none
|
||||
// Add #0 to ensure that this thread starts executing before any subsequent call
|
||||
#0;
|
||||
|
@ -361,14 +369,20 @@ package csr_utils_pkg;
|
|||
// subroutine of csr_rd, don't use it directly
|
||||
task automatic csr_rd_sub(input uvm_object ptr, // accept reg or field
|
||||
output uvm_reg_data_t value,
|
||||
input uvm_check_e check = UVM_CHECK,
|
||||
output uvm_status_e status,
|
||||
input bit backdoor = 0,
|
||||
input uvm_check_e check = default_csr_check,
|
||||
input uvm_path_e path = UVM_DEFAULT_PATH,
|
||||
input uint timeout_ns = default_timeout_ns,
|
||||
input uvm_reg_map map = null);
|
||||
if (backdoor) begin
|
||||
csr_peek(ptr, value, check);
|
||||
status = UVM_IS_OK;
|
||||
return;
|
||||
end
|
||||
fork
|
||||
begin : isolation_fork
|
||||
csr_field_t csr_or_fld;
|
||||
uvm_status_e status;
|
||||
string msg_id = {csr_utils_pkg::msg_id, "::csr_rd"};
|
||||
|
||||
fork
|
||||
|
@ -382,7 +396,7 @@ package csr_utils_pkg;
|
|||
csr_or_fld.csr.read(.status(status), .value(value), .path(path), .map(map),
|
||||
.prior(100));
|
||||
end
|
||||
if (check == UVM_CHECK) begin
|
||||
if (check == UVM_CHECK && !under_reset) begin
|
||||
`DV_CHECK_EQ(status, UVM_IS_OK, "", error, msg_id)
|
||||
end
|
||||
decrement_outstanding_access();
|
||||
|
@ -402,15 +416,16 @@ package csr_utils_pkg;
|
|||
// uvm_reg::peek() returns a 2-state value, directly get data from hdl path
|
||||
task automatic csr_peek(input uvm_object ptr,
|
||||
output uvm_reg_data_t value,
|
||||
input uvm_check_e check = UVM_CHECK);
|
||||
input uvm_check_e check = default_csr_check,
|
||||
input bkdr_reg_path_e kind = BkdrRegPathRtl);
|
||||
string msg_id = {csr_utils_pkg::msg_id, "::csr_peek"};
|
||||
csr_field_t csr_or_fld = decode_csr_or_field(ptr);
|
||||
uvm_reg csr = csr_or_fld.csr;
|
||||
|
||||
if (csr.has_hdl_path()) begin
|
||||
if (csr.has_hdl_path(kind.name)) begin
|
||||
uvm_hdl_path_concat paths[$];
|
||||
|
||||
csr.get_full_hdl_path(paths);
|
||||
csr.get_full_hdl_path(paths, kind.name);
|
||||
foreach (paths[0].slices[i]) begin
|
||||
uvm_reg_data_t field_val;
|
||||
if (uvm_hdl_read(paths[0].slices[i].path, field_val)) begin
|
||||
|
@ -421,7 +436,8 @@ package csr_utils_pkg;
|
|||
end
|
||||
end
|
||||
end else begin
|
||||
`uvm_fatal(msg_id, $sformatf("No backdoor defined for %0s", csr.get_full_name()))
|
||||
`uvm_fatal(msg_id, $sformatf("No backdoor defined for %0s path's %0s",
|
||||
csr.get_full_name(), kind.name))
|
||||
end
|
||||
|
||||
// if it's field, only return field value
|
||||
|
@ -429,7 +445,7 @@ package csr_utils_pkg;
|
|||
endtask
|
||||
|
||||
task automatic csr_rd_check(input uvm_object ptr,
|
||||
input uvm_check_e check = UVM_CHECK,
|
||||
input uvm_check_e check = default_csr_check,
|
||||
input uvm_path_e path = UVM_DEFAULT_PATH,
|
||||
input bit blocking = default_csr_blocking,
|
||||
input bit backdoor = 0,
|
||||
|
@ -452,8 +468,8 @@ package csr_utils_pkg;
|
|||
|
||||
csr_or_fld = decode_csr_or_field(ptr);
|
||||
|
||||
csr_rd(.ptr(ptr), .value(obs), .check(check), .path(path), .backdoor(backdoor),
|
||||
.blocking(1), .timeout_ns(timeout_ns), .map(map));
|
||||
csr_rd_sub(.ptr(ptr), .value(obs), .status(status), .check(check), .path(path),
|
||||
.backdoor(backdoor), .timeout_ns(timeout_ns), .map(map));
|
||||
|
||||
// get mirrored value after read to make sure the read reg access is updated
|
||||
if (csr_or_fld.field != null) begin
|
||||
|
@ -461,7 +477,7 @@ package csr_utils_pkg;
|
|||
end else begin
|
||||
exp = csr_or_fld.csr.get_mirrored_value();
|
||||
end
|
||||
if (compare && !under_reset) begin
|
||||
if (compare && status == UVM_IS_OK && !under_reset) begin
|
||||
obs = obs & compare_mask;
|
||||
exp = (compare_vs_ral ? exp : compare_value) & compare_mask;
|
||||
`DV_CHECK_EQ(obs, exp, {"Regname: ", ptr.get_full_name(), " ", err_msg},
|
||||
|
@ -485,10 +501,55 @@ package csr_utils_pkg;
|
|||
foreach (ral_csrs[i]) csr_rd_check(.ptr(ral_csrs[i]), .compare_vs_ral(1));
|
||||
endtask
|
||||
|
||||
// Checks the csr value against predicted value by reading the whole CSR or individual CSR
|
||||
// fields.
|
||||
// This task will skip read check if the CSR field is excluded.
|
||||
task automatic do_check_csr_or_field_rd(input uvm_reg csr,
|
||||
input bit do_csr_field_rd_check = $urandom(),
|
||||
input bit blocking = 0,
|
||||
input bit compare = 1,
|
||||
input bit compare_vs_ral = 1,
|
||||
input csr_excl_type_e csr_excl_type = CsrNoExcl,
|
||||
input csr_test_type_e csr_test_type = CsrRwTest,
|
||||
input csr_excl_item csr_excl_item = null);
|
||||
uvm_reg_data_t compare_mask;
|
||||
|
||||
// Check if parent block or register is excluded from read-check
|
||||
if (csr_excl_item != null && csr_excl_item.is_excl(csr, csr_excl_type, csr_test_type)) begin
|
||||
`uvm_info(msg_id, $sformatf("Skipping register %0s due to CsrExclWriteCheck exclusion",
|
||||
csr.get_full_name()), UVM_MEDIUM)
|
||||
return;
|
||||
end
|
||||
|
||||
compare_mask = get_mask_excl_fields(csr, csr_excl_type, csr_test_type, csr_excl_item);
|
||||
|
||||
if (!do_csr_field_rd_check) begin
|
||||
csr_rd_check(.ptr (csr),
|
||||
.blocking (blocking),
|
||||
.compare (compare),
|
||||
.compare_vs_ral(compare_vs_ral),
|
||||
.compare_mask (compare_mask));
|
||||
end else begin
|
||||
uvm_reg_field test_fields[$];
|
||||
csr.get_fields(test_fields);
|
||||
test_fields.shuffle();
|
||||
foreach (test_fields[i]) begin
|
||||
bit field_compare = 1;
|
||||
if (csr_excl_item != null) begin
|
||||
field_compare = !csr_excl_item.is_excl(test_fields[i], csr_excl_type, csr_test_type);
|
||||
end
|
||||
csr_rd_check(.ptr (test_fields[i]),
|
||||
.blocking (blocking),
|
||||
.compare (field_compare && compare),
|
||||
.compare_vs_ral(compare_vs_ral));
|
||||
end
|
||||
end
|
||||
endtask
|
||||
|
||||
// poll a csr or csr field continuously until it reads the expected value.
|
||||
task automatic csr_spinwait(input uvm_object ptr,
|
||||
input uvm_reg_data_t exp_data,
|
||||
input uvm_check_e check = UVM_CHECK,
|
||||
input uvm_check_e check = default_csr_check,
|
||||
input uvm_path_e path = UVM_DEFAULT_PATH,
|
||||
input uvm_reg_map map = null,
|
||||
input uint spinwait_delay_ns = 0,
|
||||
|
@ -538,7 +599,7 @@ package csr_utils_pkg;
|
|||
task automatic mem_rd(input uvm_mem ptr,
|
||||
input int offset,
|
||||
output bit[31:0] data,
|
||||
input uvm_check_e check = UVM_CHECK,
|
||||
input uvm_check_e check = default_csr_check,
|
||||
input bit blocking = default_csr_blocking,
|
||||
input uint timeout_ns = default_timeout_ns,
|
||||
input uvm_reg_map map = null);
|
||||
|
@ -556,7 +617,7 @@ package csr_utils_pkg;
|
|||
task automatic mem_rd_sub(input uvm_mem ptr,
|
||||
input int offset,
|
||||
output bit[31:0] data,
|
||||
input uvm_check_e check = UVM_CHECK,
|
||||
input uvm_check_e check = default_csr_check,
|
||||
input uint timeout_ns = default_timeout_ns,
|
||||
input uvm_reg_map map = null);
|
||||
fork
|
||||
|
@ -568,7 +629,7 @@ package csr_utils_pkg;
|
|||
begin
|
||||
increment_outstanding_access();
|
||||
ptr.read(.status(status), .offset(offset), .value(data), .map(map), .prior(100));
|
||||
if (check == UVM_CHECK) begin
|
||||
if (check == UVM_CHECK && !under_reset) begin
|
||||
`DV_CHECK_EQ(status, UVM_IS_OK, "", error, msg_id)
|
||||
end
|
||||
decrement_outstanding_access();
|
||||
|
@ -589,7 +650,7 @@ package csr_utils_pkg;
|
|||
input bit[31:0] data,
|
||||
input bit blocking = default_csr_blocking,
|
||||
input uint timeout_ns = default_timeout_ns,
|
||||
input uvm_check_e check = UVM_CHECK,
|
||||
input uvm_check_e check = default_csr_check,
|
||||
input uvm_reg_map map = null);
|
||||
if (blocking) begin
|
||||
mem_wr_sub(ptr, offset, data, timeout_ns, check, map);
|
||||
|
@ -606,7 +667,7 @@ package csr_utils_pkg;
|
|||
input int offset,
|
||||
input bit[31:0] data,
|
||||
input uint timeout_ns = default_timeout_ns,
|
||||
input uvm_check_e check = UVM_CHECK,
|
||||
input uvm_check_e check = default_csr_check,
|
||||
input uvm_reg_map map = null);
|
||||
fork
|
||||
begin : isolation_fork
|
||||
|
@ -617,7 +678,7 @@ package csr_utils_pkg;
|
|||
begin
|
||||
increment_outstanding_access();
|
||||
ptr.write(.status(status), .offset(offset), .value(data), .map(map), .prior(100));
|
||||
if (check == UVM_CHECK) begin
|
||||
if (check == UVM_CHECK && !under_reset) begin
|
||||
`DV_CHECK_EQ(status, UVM_IS_OK, "", error, msg_id)
|
||||
end
|
||||
decrement_outstanding_access();
|
||||
|
@ -641,12 +702,14 @@ package csr_utils_pkg;
|
|||
uvm_reg_field flds[$];
|
||||
csr.get_fields(flds);
|
||||
get_mask_excl_fields = '1;
|
||||
foreach (flds[i]) begin
|
||||
if (m_csr_excl_item.is_excl(flds[i], csr_excl_type, csr_test_type)) begin
|
||||
csr_field_t fld_params = decode_csr_or_field(flds[i]);
|
||||
`uvm_info(msg_id, $sformatf("Skipping field %0s due to %0s exclusion",
|
||||
flds[i].get_full_name(), csr_excl_type.name()), UVM_MEDIUM)
|
||||
get_mask_excl_fields &= ~(fld_params.mask << fld_params.shift);
|
||||
if (m_csr_excl_item != null) begin
|
||||
foreach (flds[i]) begin
|
||||
if (m_csr_excl_item.is_excl(flds[i], csr_excl_type, csr_test_type)) begin
|
||||
csr_field_t fld_params = decode_csr_or_field(flds[i]);
|
||||
`uvm_info(msg_id, $sformatf("Skipping field %0s due to %0s exclusion",
|
||||
flds[i].get_full_name(), csr_excl_type.name()), UVM_MEDIUM)
|
||||
get_mask_excl_fields &= ~(fld_params.mask << fld_params.shift);
|
||||
end
|
||||
end
|
||||
end
|
||||
endfunction
|
||||
|
|
|
@ -9,7 +9,7 @@ class dv_base_reg extends uvm_reg;
|
|||
local bit is_ext_reg;
|
||||
|
||||
local dv_base_reg locked_regs[$];
|
||||
local uvm_reg_data_t staged_shadow_val;
|
||||
local uvm_reg_data_t staged_shadow_val, committed_val, shadowed_val;
|
||||
local bit is_shadowed;
|
||||
local bit shadow_wr_staged; // stage the first shadow reg write
|
||||
local bit shadow_update_err;
|
||||
|
@ -111,6 +111,15 @@ class dv_base_reg extends uvm_reg;
|
|||
return shadow_update_err;
|
||||
endfunction
|
||||
|
||||
function bit get_shadow_storage_err();
|
||||
uvm_reg_data_t mask = (1'b1 << (get_msb_pos() + 1)) - 1;
|
||||
uvm_reg_data_t shadowed_val_temp = (~shadowed_val) & mask;
|
||||
uvm_reg_data_t committed_val_temp = committed_val & mask;
|
||||
`uvm_info(`gfn, $sformatf("shadow_val %0h, commmit_val %0h", shadowed_val_temp,
|
||||
committed_val_temp), UVM_DEBUG)
|
||||
return shadowed_val_temp != committed_val_temp;
|
||||
endfunction
|
||||
|
||||
virtual function void clear_shadow_update_err();
|
||||
shadow_update_err = 0;
|
||||
endfunction
|
||||
|
@ -123,6 +132,10 @@ class dv_base_reg extends uvm_reg;
|
|||
virtual task post_write(uvm_reg_item rw);
|
||||
dv_base_reg_field fields[$];
|
||||
string field_access;
|
||||
|
||||
// no need to update shadow value or access type if access is not OK, as access is aborted
|
||||
if (rw.status != UVM_IS_OK) return;
|
||||
|
||||
if (is_shadowed) begin
|
||||
// first write
|
||||
if (!shadow_wr_staged) begin
|
||||
|
@ -136,6 +149,8 @@ class dv_base_reg extends uvm_reg;
|
|||
shadow_update_err = 1;
|
||||
return;
|
||||
end
|
||||
committed_val = staged_shadow_val;
|
||||
shadowed_val = ~committed_val;
|
||||
end
|
||||
end
|
||||
if (is_enable_reg()) begin
|
||||
|
@ -197,11 +212,44 @@ class dv_base_reg extends uvm_reg;
|
|||
super.do_predict(rw, kind, be);
|
||||
endfunction
|
||||
|
||||
virtual task poke (output uvm_status_e status,
|
||||
input uvm_reg_data_t value,
|
||||
input string kind = "BkdrRegPathRtl",
|
||||
input uvm_sequence_base parent = null,
|
||||
input uvm_object extension = null,
|
||||
input string fname = "",
|
||||
input int lineno = 0);
|
||||
if (kind == "BkdrRegPathRtlShadow") shadowed_val = value;
|
||||
else if (kind == "BkdrRegPathRtlCommitted") committed_val = value;
|
||||
super.poke(status, value, kind, parent, extension, fname, lineno);
|
||||
endtask
|
||||
|
||||
// callback function to update shadowed values according to specific design
|
||||
// should only be called after post-write
|
||||
virtual function void update_shadowed_val(uvm_reg_data_t val, bit do_predict = 1);
|
||||
if (shadow_wr_staged) begin
|
||||
// update value after first write
|
||||
staged_shadow_val = val;
|
||||
end else begin
|
||||
// update value after second write
|
||||
if (staged_shadow_val != val) begin
|
||||
shadow_update_err = 1;
|
||||
end else begin
|
||||
shadow_update_err = 0;
|
||||
committed_val = staged_shadow_val;
|
||||
shadowed_val = ~committed_val;
|
||||
end
|
||||
end
|
||||
if (do_predict) void'(predict(val));
|
||||
endfunction
|
||||
|
||||
virtual function void reset(string kind = "HARD");
|
||||
super.reset(kind);
|
||||
if (is_shadowed) begin
|
||||
shadow_update_err = 0;
|
||||
shadow_wr_staged = 0;
|
||||
committed_val = get_mirrored_value();
|
||||
shadowed_val = ~committed_val;
|
||||
// in case reset is issued during shadowed writes
|
||||
void'(atomic_shadow_wr.try_get(1));
|
||||
void'(atomic_en_shadow_wr.try_get(1));
|
||||
|
|
|
@ -8,6 +8,14 @@ class dv_base_reg_block extends uvm_reg_block;
|
|||
|
||||
csr_excl_item csr_excl;
|
||||
|
||||
// The address mask for the register block specific to a map. This will be (1 << K) - 1 for some
|
||||
// K. All relative offsets in the register block have addresses less than (1 << K), so an address
|
||||
// decoder that starts by discarding all higher bits will work correctly so long as the chosen
|
||||
// base address of the register block has no bits in common with this mask.
|
||||
//
|
||||
// This is set by compute_addr_mask(), which must run after locking the model.
|
||||
protected uvm_reg_addr_t addr_mask[uvm_reg_map];
|
||||
|
||||
function new (string name = "", int has_coverage = UVM_NO_COVERAGE);
|
||||
super.new(name, has_coverage);
|
||||
endfunction
|
||||
|
@ -30,9 +38,18 @@ class dv_base_reg_block extends uvm_reg_block;
|
|||
foreach (ral_regs[i]) `downcast(dv_regs[i], ral_regs[i])
|
||||
endfunction
|
||||
|
||||
function dv_base_reg get_dv_base_reg_by_name(string csr_name, bit check_csr_exist = 1'b1);
|
||||
uvm_reg csr = get_reg_by_name(csr_name);
|
||||
`downcast(get_dv_base_reg_by_name, csr)
|
||||
if (check_csr_exist) begin
|
||||
`DV_CHECK_NE_FATAL(get_dv_base_reg_by_name, null,
|
||||
$sformatf("%0s does not exist in block %0s",csr_name, get_name()))
|
||||
end
|
||||
endfunction
|
||||
|
||||
function void get_enable_regs(ref dv_base_reg enable_regs[$]);
|
||||
dv_base_reg_block blks[$];
|
||||
this.get_dv_base_reg_blocks(blks);
|
||||
get_dv_base_reg_blocks(blks);
|
||||
if (blks.size() == 0) begin
|
||||
dv_base_reg all_regs[$];
|
||||
this.get_dv_base_regs(all_regs);
|
||||
|
@ -49,9 +66,121 @@ class dv_base_reg_block extends uvm_reg_block;
|
|||
// when reset issued - the locked registers' access will be reset to original access
|
||||
virtual function void reset(string kind = "HARD");
|
||||
dv_base_reg enable_regs[$];
|
||||
`uvm_info(`gfn, "Resetting RAL reg block", UVM_MEDIUM)
|
||||
super.reset(kind);
|
||||
get_enable_regs(enable_regs);
|
||||
foreach (enable_regs[i]) enable_regs[i].set_locked_regs_access();
|
||||
endfunction
|
||||
|
||||
// Internal function, used to compute the address mask for this register block.
|
||||
//
|
||||
// This is quite an expensive computation, so we memoize the results in addr_mask[map].
|
||||
// Use below to get the addr map size #3317
|
||||
// max2(biggest_reg_offset+reg_size, biggest_mem_offset+mem_size) and then round up to 2**N
|
||||
protected function void compute_addr_mask(uvm_reg_map map);
|
||||
uvm_reg_addr_t max_addr, max_offset;
|
||||
uvm_reg_block blocks[$];
|
||||
uvm_reg regs[$];
|
||||
uvm_mem mems[$];
|
||||
int unsigned alignment;
|
||||
|
||||
// TODO: assume IP only contains 1 reg block, find a better way to handle chip-level and IP
|
||||
// with multiple reg blocks
|
||||
get_blocks(blocks);
|
||||
if (blocks.size > 0) begin
|
||||
addr_mask[map] = '1;
|
||||
return;
|
||||
end
|
||||
|
||||
get_registers(regs);
|
||||
get_memories(mems);
|
||||
`DV_CHECK_GT_FATAL(regs.size() + mems.size(), 0)
|
||||
|
||||
// Walk the known registers and memories, calculating the largest byte address visible. Note
|
||||
// that the get_offset() calls will return absolute addresses, including any base address in the
|
||||
// default register map.
|
||||
max_addr = 0;
|
||||
foreach (regs[i]) begin
|
||||
max_addr = max2(regs[i].get_offset(map) + regs[i].get_n_bytes() - 1, max_addr);
|
||||
end
|
||||
|
||||
foreach (mems[i]) begin
|
||||
uvm_reg_addr_t mem_size;
|
||||
mem_size = mems[i].get_offset(.map(map)) + mems[i].get_size() * mems[i].get_n_bytes() - 1;
|
||||
max_addr = max2(mem_size, max_addr);
|
||||
end
|
||||
|
||||
// Subtract the base address in the default register map to get the maximum relative offset.
|
||||
max_offset = max_addr - map.get_base_addr();
|
||||
|
||||
// Set alignment to be ceil(log2(biggest_offset))
|
||||
alignment = 0;
|
||||
while (max_offset > 0) begin
|
||||
alignment++;
|
||||
max_offset = max_offset >> 1;
|
||||
end
|
||||
|
||||
// Note that we know alignment > 0 (because we've already checked that we have at least one
|
||||
// register or memory).
|
||||
`DV_CHECK_GT_FATAL(alignment, 0)
|
||||
|
||||
// Finally, extract a mask corresponding to the alignment
|
||||
addr_mask[map] = (1 << alignment) - 1;
|
||||
|
||||
// Computed mask must be non-zero.
|
||||
`DV_CHECK_FATAL(addr_mask[map])
|
||||
endfunction
|
||||
|
||||
// Get the address mask. This should only be called after locking the model (because it depends on
|
||||
// the layout of registers and memories in the block).
|
||||
function uvm_reg_addr_t get_addr_mask(uvm_reg_map map = null);
|
||||
`DV_CHECK_FATAL(is_locked())
|
||||
if (map == null) map = get_default_map();
|
||||
if (!addr_mask.exists(map)) compute_addr_mask(map);
|
||||
return addr_mask[map];
|
||||
endfunction
|
||||
|
||||
// Set the base address for the given register map
|
||||
//
|
||||
// Check that base_addr is aligned as required by the register block. If the supplied base_addr is
|
||||
// the "magic" address '1, randomly pick an appropriately aligned base address and set it for the
|
||||
// specified map.
|
||||
function void set_base_addr(uvm_reg_addr_t base_addr = '1, uvm_reg_map map = null);
|
||||
uvm_reg_addr_t mask;
|
||||
|
||||
if (map == null) map = get_default_map();
|
||||
mask = get_addr_mask(map);
|
||||
|
||||
// If base_addr is '1, randomly pick an aligned base address
|
||||
if (base_addr == '1) begin
|
||||
`DV_CHECK_STD_RANDOMIZE_WITH_FATAL(base_addr, (base_addr & mask) == '0;)
|
||||
end
|
||||
|
||||
// Check base addr alignment (which should be guaranteed if we just picked it, but needs
|
||||
// checking if not).
|
||||
`DV_CHECK_FATAL((base_addr & mask) == '0)
|
||||
`uvm_info(`gfn, $sformatf("Setting register base address to 0x%0h", base_addr), UVM_HIGH)
|
||||
map.set_base_addr(base_addr);
|
||||
endfunction
|
||||
|
||||
// Round the given address down to the start of the containing word. For example, if the address
|
||||
// is 'h123, it will be rounded down to 'h120.
|
||||
//
|
||||
// This is useful if you have a possibly misaligned address and you want to know whether it hits a
|
||||
// register (since get_reg_by_offset needs the aligned address for the start of the register).
|
||||
function uvm_reg_addr_t get_word_aligned_addr(uvm_reg_addr_t byte_addr);
|
||||
uvm_reg_addr_t shift = $clog2(`UVM_REG_BYTENABLE_WIDTH);
|
||||
return (byte_addr >> shift) << shift;
|
||||
endfunction
|
||||
|
||||
// Get the absolute address (in the default register map) for the given offset. For example, if
|
||||
// the base address is 'h100 and offset is 'h13, this will return 'h113 ('h110 if word_aligned is
|
||||
// set).
|
||||
function uvm_reg_addr_t get_addr_from_offset(uvm_reg_addr_t byte_offset,
|
||||
bit word_aligned = 1'b1,
|
||||
uvm_reg_map map = null);
|
||||
if (map == null) map = get_default_map();
|
||||
return (word_aligned ? get_word_aligned_addr(byte_offset) : byte_offset) + map.get_base_addr();
|
||||
endfunction
|
||||
|
||||
endclass
|
||||
|
|
|
@ -37,6 +37,15 @@ package dv_base_reg_pkg;
|
|||
CsrAllTests = 4'hf // all tests
|
||||
} csr_test_type_e;
|
||||
|
||||
typedef enum bit[2:0] {
|
||||
BkdrRegPathRtl, // backdoor path for reg's val in RTL
|
||||
BkdrRegPathRtlCommitted, // backdoor path for shadow reg's committed val in RTL
|
||||
BkdrRegPathRtlShadow, // backdoor path for shadow reg's shadow val in RTL
|
||||
BkdrRegPathGls, // backdoor path for reg's val in GLS
|
||||
BkdrRegPathGlsCommitted, // backdoor path for shadow reg's committed val in GLS
|
||||
BkdrRegPathGlsShdow // backdoor path for shadow reg's shadow val in GLS
|
||||
} bkdr_reg_path_e;
|
||||
|
||||
// package sources
|
||||
// base ral
|
||||
`include "csr_excl_item.sv"
|
||||
|
|
11
vendor/lowrisc_ip/dv/sv/dv_lib/dv_base_env.sv
vendored
11
vendor/lowrisc_ip/dv/sv/dv_lib/dv_base_env.sv
vendored
|
@ -46,15 +46,4 @@ class dv_base_env #(type CFG_T = dv_base_env_cfg,
|
|||
scoreboard.cov = cov;
|
||||
endfunction
|
||||
|
||||
virtual function void end_of_elaboration_phase(uvm_phase phase);
|
||||
super.end_of_elaboration_phase(phase);
|
||||
if (cfg.has_ral) begin
|
||||
// Lock the ral model
|
||||
cfg.ral.lock_model();
|
||||
// Get list of valid csr addresses (useful in seq to randomize addr as well as in scb checks)
|
||||
get_csr_addrs(cfg.ral, cfg.csr_addrs);
|
||||
get_mem_addr_ranges(cfg.ral, cfg.mem_ranges);
|
||||
end
|
||||
endfunction : end_of_elaboration_phase
|
||||
|
||||
endclass
|
||||
|
|
|
@ -26,11 +26,6 @@ class dv_base_env_cfg #(type RAL_T = dv_base_reg_block) extends uvm_object;
|
|||
bit [bus_params_pkg::BUS_AW-1:0] csr_addrs[$];
|
||||
addr_range_t mem_ranges[$];
|
||||
|
||||
// ral base address and size
|
||||
bit [bus_params_pkg::BUS_AW-1:0] csr_base_addr; // base address where csr map begins
|
||||
bit [bus_params_pkg::BUS_AW:0] csr_addr_map_size; // csr addr region allocated to the ip,
|
||||
// max: 1 << bus_params_pkg::BUS_AW
|
||||
|
||||
// clk_rst_if & freq
|
||||
virtual clk_rst_if clk_rst_vif;
|
||||
rand clk_freq_mhz_e clk_freq_mhz;
|
||||
|
@ -40,44 +35,36 @@ class dv_base_env_cfg #(type RAL_T = dv_base_reg_block) extends uvm_object;
|
|||
`uvm_field_int (en_scb, UVM_DEFAULT)
|
||||
`uvm_field_int (en_cov, UVM_DEFAULT)
|
||||
`uvm_field_int (zero_delays, UVM_DEFAULT)
|
||||
`uvm_field_int (csr_base_addr, UVM_DEFAULT)
|
||||
`uvm_field_int (csr_addr_map_size, UVM_DEFAULT)
|
||||
`uvm_field_enum (clk_freq_mhz_e, clk_freq_mhz, UVM_DEFAULT)
|
||||
`uvm_object_utils_end
|
||||
|
||||
`uvm_object_new
|
||||
|
||||
virtual function void initialize(bit [bus_params_pkg::BUS_AW-1:0] csr_base_addr = '1);
|
||||
initialize_csr_addr_map_size();
|
||||
`DV_CHECK_NE_FATAL(csr_addr_map_size, 0, "csr_addr_map_size can't be 0")
|
||||
// use locally randomized csr base address, unless provided as arg to this function
|
||||
if (csr_base_addr != '1) begin
|
||||
bit is_aligned;
|
||||
this.csr_base_addr = csr_base_addr;
|
||||
// check alignment
|
||||
is_aligned = ~|(this.csr_base_addr & (this.csr_addr_map_size - 1));
|
||||
`DV_CHECK_EQ_FATAL(is_aligned, 1'b1)
|
||||
end else begin
|
||||
// base address needs to be aligned to csr_addr_map_size
|
||||
`DV_CHECK_STD_RANDOMIZE_WITH_FATAL(csr_base_addr,
|
||||
~|(csr_base_addr & (csr_addr_map_size - 1));)
|
||||
this.csr_base_addr = csr_base_addr;
|
||||
end
|
||||
// build the ral model
|
||||
if (has_ral) begin
|
||||
uvm_reg_addr_t base_addr;
|
||||
|
||||
ral = RAL_T::type_id::create("ral");
|
||||
ral.build(this.csr_base_addr, null);
|
||||
|
||||
// Build the register block with an arbitrary base address (we choose 0). We'll change it
|
||||
// later.
|
||||
ral.build(.base_addr(0), .csr_excl(null));
|
||||
apply_ral_fixes();
|
||||
ral.lock_model();
|
||||
|
||||
// Now the model is locked, we know its layout. Set the base address for the register block.
|
||||
// The function internally picks a random one if we pass '1 to it, and performs an integrity
|
||||
// check on the set address.
|
||||
ral.set_base_addr(csr_base_addr);
|
||||
|
||||
// Get list of valid csr addresses (useful in seq to randomize addr as well as in scb checks)
|
||||
get_csr_addrs(ral, csr_addrs);
|
||||
get_mem_addr_ranges(ral, mem_ranges);
|
||||
ral_models.push_back(ral);
|
||||
end
|
||||
endfunction
|
||||
|
||||
// This function must be implemented in extended class to
|
||||
// initialize value of csr_addr_map_size member
|
||||
virtual function void initialize_csr_addr_map_size();
|
||||
`uvm_fatal(`gfn, "This task must be implemented in the extended class!")
|
||||
endfunction : initialize_csr_addr_map_size
|
||||
|
||||
// ral flow is limited in terms of setting correct field access policies and reset values
|
||||
// We apply those fixes here - please note these fixes need to be reflected in the scoreboard
|
||||
protected virtual function void apply_ral_fixes();
|
||||
|
@ -93,4 +80,5 @@ class dv_base_env_cfg #(type RAL_T = dv_base_reg_block) extends uvm_object;
|
|||
this.under_reset = 0;
|
||||
csr_utils_pkg::reset_deasserted();
|
||||
endfunction
|
||||
|
||||
endclass
|
||||
|
|
|
@ -5,13 +5,15 @@
|
|||
// TODO - We are enclosing generic covergroups inside class so that we can
|
||||
// take avoid tool limitation of not allowing arrays of covergroup
|
||||
// Refer to Issue#375 for more details
|
||||
class dv_base_generic_cov_obj;
|
||||
class bit_toggle_cg_wrap;
|
||||
|
||||
// Covergroup: bit_toggle_cg
|
||||
// Generic covergroup definition
|
||||
covergroup bit_toggle_cg(string name, bit toggle_cov_en = 1) with function sample(bit value);
|
||||
covergroup bit_toggle_cg(string name, string path = "", bit toggle_cov_en = 1) with function
|
||||
sample(bit value);
|
||||
option.per_instance = 1;
|
||||
option.name = name;
|
||||
option.name = (path == "") ? name : {path, "::", name};
|
||||
|
||||
cp_value: coverpoint value;
|
||||
cp_transitions: coverpoint value {
|
||||
option.weight = toggle_cov_en;
|
||||
|
@ -21,8 +23,8 @@ class dv_base_generic_cov_obj;
|
|||
endgroup : bit_toggle_cg
|
||||
|
||||
// Function: new
|
||||
function new(string name = "dv_base_generic_cov_obj", bit toggle_cov_en = 1);
|
||||
bit_toggle_cg = new(name, toggle_cov_en);
|
||||
function new(string name = "bit_toggle_cg_wrap", string path = "", bit toggle_cov_en = 1);
|
||||
bit_toggle_cg = new(name, path, toggle_cov_en);
|
||||
endfunction : new
|
||||
|
||||
// Function: sample
|
||||
|
@ -30,7 +32,7 @@ class dv_base_generic_cov_obj;
|
|||
bit_toggle_cg.sample(value);
|
||||
endfunction : sample
|
||||
|
||||
endclass : dv_base_generic_cov_obj
|
||||
endclass : bit_toggle_cg_wrap
|
||||
|
||||
class dv_base_env_cov #(type CFG_T = dv_base_env_cfg) extends uvm_component;
|
||||
`uvm_component_param_utils(dv_base_env_cov #(CFG_T))
|
||||
|
|
|
@ -52,12 +52,19 @@ class dv_base_scoreboard #(type RAL_T = dv_base_reg_block,
|
|||
|
||||
virtual function void pre_abort();
|
||||
super.pre_abort();
|
||||
// use under_pre_abort flag to prevent deadloop described below:
|
||||
// when fatal_err occurred, it will skip check_phase. We add the additional check_phase call
|
||||
// here to help debugging. But if inside the check_phase there are UVM_ERRORs, and the err cnt
|
||||
// is larger than max_err_cnt, then check_phase will call pre_abort again. This will end up
|
||||
// creating a deadloop.
|
||||
if (has_uvm_fatal_occurred() && !under_pre_abort) begin
|
||||
|
||||
// In UVM, a fatal error normally aborts the test completely and skips the check phase. However,
|
||||
// it's sometimes helpful to run that phase on the scoreboard anyway (it might make it easier to
|
||||
// debug whatever went wrong), so we do that here.
|
||||
//
|
||||
// We only run the check phase if we were in the run phase: if something went wrong in the build
|
||||
// or connect phase, there's not much point in "checking the run" further. We also have to be
|
||||
// careful to avoid an infinite loop, so we set a flag to avoid doing this a second time if we
|
||||
// have errors in the check phase.
|
||||
if (has_uvm_fatal_occurred() &&
|
||||
!under_pre_abort &&
|
||||
m_current_phase.is(uvm_run_phase::get())) begin
|
||||
|
||||
under_pre_abort = 1;
|
||||
check_phase(m_current_phase);
|
||||
under_pre_abort = 0;
|
||||
|
|
26
vendor/lowrisc_ip/dv/sv/dv_lib/dv_base_vseq.sv
vendored
26
vendor/lowrisc_ip/dv/sv/dv_lib/dv_base_vseq.sv
vendored
|
@ -88,6 +88,7 @@ class dv_base_vseq #(type RAL_T = dv_base_reg_block,
|
|||
if (kind == "HARD") begin
|
||||
csr_utils_pkg::reset_asserted();
|
||||
cfg.clk_rst_vif.apply_reset();
|
||||
csr_utils_pkg::clear_outstanding_access();
|
||||
csr_utils_pkg::reset_deasserted();
|
||||
end
|
||||
if (cfg.has_ral) begin
|
||||
|
@ -186,13 +187,19 @@ class dv_base_vseq #(type RAL_T = dv_base_reg_block,
|
|||
string reset_type = "HARD";
|
||||
csr_write_seq m_csr_write_seq;
|
||||
|
||||
// Writing random values to CSRs might trigger assertion errors. So we disable in the entire
|
||||
// DUT hierarchy and re-enable after resetting the DUT. See DV_ASSERT_CTRL macro defined in
|
||||
// hw/dv/sv/dv_utils/dv_macros.svh for more details.
|
||||
if (!enable_asserts_in_hw_reset_rand_wr) begin
|
||||
`DV_ASSERT_CTRL_REQ("dut_assert_en", 1'b0)
|
||||
end
|
||||
|
||||
// run write-only sequence to randomize the csr values
|
||||
m_csr_write_seq = csr_write_seq::type_id::create("m_csr_write_seq");
|
||||
m_csr_write_seq.models = cfg.ral_models;
|
||||
m_csr_write_seq.set_csr_excl_item(csr_excl);
|
||||
m_csr_write_seq.external_checker = cfg.en_scb;
|
||||
m_csr_write_seq.en_rand_backdoor_write = 1;
|
||||
if (!enable_asserts_in_hw_reset_rand_wr) $assertoff;
|
||||
m_csr_write_seq.en_rand_backdoor_write = 1'b1;
|
||||
m_csr_write_seq.set_csr_excl_item(csr_excl);
|
||||
m_csr_write_seq.start(null);
|
||||
|
||||
// run dut_shutdown before asserting reset
|
||||
|
@ -200,16 +207,23 @@ class dv_base_vseq #(type RAL_T = dv_base_reg_block,
|
|||
// issue reset
|
||||
void'($value$plusargs("do_reset=%0s", reset_type));
|
||||
dut_init(reset_type);
|
||||
if (!enable_asserts_in_hw_reset_rand_wr) $asserton;
|
||||
if (!enable_asserts_in_hw_reset_rand_wr) begin
|
||||
`DV_ASSERT_CTRL_REQ("dut_assert_en", 1'b1)
|
||||
end
|
||||
end
|
||||
|
||||
// create base csr seq and pass our ral
|
||||
m_csr_seq = csr_base_seq::type_id::create("m_csr_seq");
|
||||
m_csr_seq.num_test_csrs = num_test_csrs;
|
||||
m_csr_seq.models = cfg.ral_models;
|
||||
m_csr_seq.set_csr_excl_item(csr_excl);
|
||||
m_csr_seq.external_checker = cfg.en_scb;
|
||||
m_csr_seq.num_test_csrs = num_test_csrs;
|
||||
m_csr_seq.set_csr_excl_item(csr_excl);
|
||||
m_csr_seq.start(null);
|
||||
endtask
|
||||
|
||||
// enable/disable csr_assert
|
||||
virtual function void set_csr_assert_en(bit enable, string path = "*");
|
||||
uvm_config_db#(bit)::set(null, path, "csr_assert_en", enable);
|
||||
endfunction
|
||||
|
||||
endclass
|
||||
|
|
17
vendor/lowrisc_ip/dv/sv/dv_utils/dv_macros.core
vendored
Normal file
17
vendor/lowrisc_ip/dv/sv/dv_utils/dv_macros.core
vendored
Normal file
|
@ -0,0 +1,17 @@
|
|||
CAPI=2:
|
||||
# Copyright lowRISC contributors.
|
||||
# Licensed under the Apache License, Version 2.0, see LICENSE for details.
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
name: "lowrisc:dv:dv_macros"
|
||||
description: "A collection of macros used in DV."
|
||||
|
||||
filesets:
|
||||
files_dv:
|
||||
files:
|
||||
- dv_macros.svh: {is_include_file: true}
|
||||
file_type: systemVerilogSource
|
||||
|
||||
targets:
|
||||
default: &default_target
|
||||
filesets:
|
||||
- files_dv
|
234
vendor/lowrisc_ip/dv/sv/dv_utils/dv_macros.svh
vendored
234
vendor/lowrisc_ip/dv/sv/dv_utils/dv_macros.svh
vendored
|
@ -2,26 +2,38 @@
|
|||
// Licensed under the Apache License, Version 2.0, see LICENSE for details.
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
`ifndef __DV_MACROS_SVH__
|
||||
`define __DV_MACROS_SVH__
|
||||
|
||||
`ifdef UVM
|
||||
`include "uvm_macros.svh"
|
||||
`endif
|
||||
|
||||
// UVM speficic macros
|
||||
`ifndef gfn
|
||||
// verilog_lint: waive macro-name-style
|
||||
`define gfn get_full_name()
|
||||
`endif
|
||||
|
||||
`ifndef gtn
|
||||
// verilog_lint: waive macro-name-style
|
||||
`define gtn get_type_name()
|
||||
`endif
|
||||
|
||||
`ifndef gn
|
||||
// verilog_lint: waive macro-name-style
|
||||
`define gn get_name()
|
||||
`endif
|
||||
|
||||
`ifndef gmv
|
||||
// verilog_lint: waive macro-name-style
|
||||
`define gmv(csr) csr.get_mirrored_value()
|
||||
`endif
|
||||
|
||||
// cast base class obj holding extended class handle to extended class handle;
|
||||
// throw error if cast fails
|
||||
`ifndef downcast
|
||||
// verilog_lint: waive macro-name-style
|
||||
`define downcast(EXT_, BASE_, MSG_="", SEV_=fatal, ID_=`gfn) \
|
||||
begin \
|
||||
if (!$cast(EXT_, BASE_)) begin \
|
||||
|
@ -57,6 +69,11 @@
|
|||
_inst_name_ = _type_name_::type_id::create(`"_inst_name_`", this);
|
||||
`endif
|
||||
|
||||
// Convert arbitrary text / expression to string.
|
||||
`ifndef DV_STRINGIFY
|
||||
`define DV_STRINGIFY(I_) `"I_`"
|
||||
`endif
|
||||
|
||||
// Common check macros used by DV_CHECK error and fatal macros.
|
||||
// Note: Should not be called by user code
|
||||
`ifndef DV_CHECK
|
||||
|
@ -71,7 +88,7 @@
|
|||
`ifndef DV_CHECK_EQ
|
||||
`define DV_CHECK_EQ(ACT_, EXP_, MSG_="", SEV_=error, ID_=`gfn) \
|
||||
begin \
|
||||
if (!(ACT_ == EXP_)) begin \
|
||||
if (!((ACT_) == (EXP_))) begin \
|
||||
`uvm_``SEV_(ID_, $sformatf("Check failed %s == %s (%0d [0x%0h] vs %0d [0x%0h]) %s", \
|
||||
`"ACT_`", `"EXP_`", ACT_, ACT_, EXP_, EXP_, MSG_)) \
|
||||
end \
|
||||
|
@ -81,7 +98,7 @@
|
|||
`ifndef DV_CHECK_NE
|
||||
`define DV_CHECK_NE(ACT_, EXP_, MSG_="", SEV_=error, ID_=`gfn) \
|
||||
begin \
|
||||
if (!(ACT_ != EXP_)) begin \
|
||||
if (!((ACT_) != (EXP_))) begin \
|
||||
`uvm_``SEV_(ID_, $sformatf("Check failed %s != %s (%0d [0x%0h] vs %0d [0x%0h]) %s", \
|
||||
`"ACT_`", `"EXP_`", ACT_, ACT_, EXP_, EXP_, MSG_)) \
|
||||
end \
|
||||
|
@ -91,7 +108,7 @@
|
|||
`ifndef DV_CHECK_CASE_EQ
|
||||
`define DV_CHECK_CASE_EQ(ACT_, EXP_, MSG_="", SEV_=error, ID_=`gfn) \
|
||||
begin \
|
||||
if (!(ACT_ === EXP_)) begin \
|
||||
if (!((ACT_) === (EXP_))) begin \
|
||||
`uvm_``SEV_(ID_, $sformatf("Check failed %s === %s (0x%0h [%0b] vs 0x%0h [%0b]) %s", \
|
||||
`"ACT_`", `"EXP_`", ACT_, ACT_, EXP_, EXP_, MSG_)) \
|
||||
end \
|
||||
|
@ -101,7 +118,7 @@
|
|||
`ifndef DV_CHECK_CASE_NE
|
||||
`define DV_CHECK_CASE_NE(ACT_, EXP_, MSG_="", SEV_=error, ID_=`gfn) \
|
||||
begin \
|
||||
if (!(ACT_ !== EXP_)) begin \
|
||||
if (!((ACT_) !== (EXP_))) begin \
|
||||
`uvm_``SEV_(ID_, $sformatf("Check failed %s !== %s (%0d [0x%0h] vs %0d [0x%0h]) %s", \
|
||||
`"ACT_`", `"EXP_`", ACT_, ACT_, EXP_, EXP_, MSG_)) \
|
||||
end \
|
||||
|
@ -111,7 +128,7 @@
|
|||
`ifndef DV_CHECK_LT
|
||||
`define DV_CHECK_LT(ACT_, EXP_, MSG_="", SEV_=error, ID_=`gfn) \
|
||||
begin \
|
||||
if (!(ACT_ < EXP_)) begin \
|
||||
if (!((ACT_) < (EXP_))) begin \
|
||||
`uvm_``SEV_(ID_, $sformatf("Check failed %s < %s (%0d [0x%0h] vs %0d [0x%0h]) %s", \
|
||||
`"ACT_`", `"EXP_`", ACT_, ACT_, EXP_, EXP_, MSG_)) \
|
||||
end \
|
||||
|
@ -121,7 +138,7 @@
|
|||
`ifndef DV_CHECK_GT
|
||||
`define DV_CHECK_GT(ACT_, EXP_, MSG_="", SEV_=error, ID_=`gfn) \
|
||||
begin \
|
||||
if (!(ACT_ > EXP_)) begin \
|
||||
if (!((ACT_) > (EXP_))) begin \
|
||||
`uvm_``SEV_(ID_, $sformatf("Check failed %s > %s (%0d [0x%0h] vs %0d [0x%0h]) %s", \
|
||||
`"ACT_`", `"EXP_`", ACT_, ACT_, EXP_, EXP_, MSG_)) \
|
||||
end \
|
||||
|
@ -131,7 +148,7 @@
|
|||
`ifndef DV_CHECK_LE
|
||||
`define DV_CHECK_LE(ACT_, EXP_, MSG_="", SEV_=error, ID_=`gfn) \
|
||||
begin \
|
||||
if (!(ACT_ <= EXP_)) begin \
|
||||
if (!((ACT_) <= (EXP_))) begin \
|
||||
`uvm_``SEV_(ID_, $sformatf("Check failed %s <= %s (%0d [0x%0h] vs %0d [0x%0h]) %s", \
|
||||
`"ACT_`", `"EXP_`", ACT_, ACT_, EXP_, EXP_, MSG_)) \
|
||||
end \
|
||||
|
@ -141,13 +158,27 @@
|
|||
`ifndef DV_CHECK_GE
|
||||
`define DV_CHECK_GE(ACT_, EXP_, MSG_="", SEV_=error, ID_=`gfn) \
|
||||
begin \
|
||||
if (!(ACT_ >= EXP_)) begin \
|
||||
if (!((ACT_) >= (EXP_))) begin \
|
||||
`uvm_``SEV_(ID_, $sformatf("Check failed %s >= %s (%0d [0x%0h] vs %0d [0x%0h]) %s", \
|
||||
`"ACT_`", `"EXP_`", ACT_, ACT_, EXP_, EXP_, MSG_)) \
|
||||
end \
|
||||
end
|
||||
`endif
|
||||
|
||||
`ifndef DV_CHECK_STREQ
|
||||
`define DV_CHECK_STREQ(ACT_, EXP_, MSG_="", SEV_=error, ID_=`gfn) \
|
||||
if (!((ACT_) == (EXP_))) begin \
|
||||
`uvm_``SEV_(ID_, $sformatf("Check failed \"%s\" == \"%s\" %s", ACT_, EXP_, MSG_)); \
|
||||
end
|
||||
`endif
|
||||
|
||||
`ifndef DV_CHECK_STRNE
|
||||
`define DV_CHECK_STRNE(ACT_, EXP_, MSG_="", SEV_=error, ID_=`gfn) \
|
||||
if (!((ACT_) != (EXP_))) begin \
|
||||
`uvm_``SEV_(ID_, $sformatf("Check failed \"%s\" != \"%s\" %s", ACT_, EXP_, MSG_)); \
|
||||
end
|
||||
`endif
|
||||
|
||||
// Fatal version of the checks
|
||||
`ifndef DV_CHECK_FATAL
|
||||
`define DV_CHECK_FATAL(T_, MSG_="", ID_=`gfn) \
|
||||
|
@ -184,6 +215,16 @@
|
|||
`DV_CHECK_GE(ACT_, EXP_, MSG_, fatal, ID_)
|
||||
`endif
|
||||
|
||||
`ifndef DV_CHECK_STREQ_FATAL
|
||||
`define DV_CHECK_STREQ_FATAL(ACT_, EXP_, MSG_="", ID_=`gfn) \
|
||||
`DV_CHECK_STREQ(ACT_, EXP_, MSG_, fatal, ID_)
|
||||
`endif
|
||||
|
||||
`ifndef DV_CHECK_STRNE_FATAL
|
||||
`define DV_CHECK_STRNE_FATAL(ACT_, EXP_, MSG_="", ID_=`gfn) \
|
||||
`DV_CHECK_STRNE(ACT_, EXP_, MSG_, fatal, ID_)
|
||||
`endif
|
||||
|
||||
// Shorthand for common foo.randomize() + fatal check
|
||||
`ifndef DV_CHECK_RANDOMIZE_FATAL
|
||||
`define DV_CHECK_RANDOMIZE_FATAL(VAR_, MSG_="Randomization failed!", ID_=`gfn) \
|
||||
|
@ -208,16 +249,18 @@
|
|||
`DV_CHECK_FATAL(std::randomize(VAR_) with {WITH_C_}, MSG_, ID_)
|
||||
`endif
|
||||
|
||||
// Shorthand for common this.randomize(foo) + fatal check
|
||||
// Shorthand for common cls_inst.randomize(member) + fatal check
|
||||
// Randomizes a specific member of a class instance.
|
||||
`ifndef DV_CHECK_MEMBER_RANDOMIZE_FATAL
|
||||
`define DV_CHECK_MEMBER_RANDOMIZE_FATAL(VAR_, MSG_="Randomization failed!", ID_=`gfn) \
|
||||
`DV_CHECK_FATAL(this.randomize(VAR_), MSG_, ID_)
|
||||
`define DV_CHECK_MEMBER_RANDOMIZE_FATAL(VAR_, CLS_INST_=this, MSG_="Randomization failed!", ID_=`gfn) \
|
||||
`DV_CHECK_FATAL(CLS_INST_.randomize(VAR_), MSG_, ID_)
|
||||
`endif
|
||||
|
||||
// Shorthand for common this.randomize(foo) with { } + fatal check
|
||||
// Shorthand for common cls_inst.randomize(member) with { } + fatal check
|
||||
// Randomizes a specific member of a class instance with inline constraints.
|
||||
`ifndef DV_CHECK_MEMBER_RANDOMIZE_WITH_FATAL
|
||||
`define DV_CHECK_MEMBER_RANDOMIZE_WITH_FATAL(VAR_, C_, MSG_="Randomization failed!", ID_=`gfn) \
|
||||
`DV_CHECK_FATAL(this.randomize(VAR_) with {C_}, MSG_, ID_)
|
||||
`define DV_CHECK_MEMBER_RANDOMIZE_WITH_FATAL(VAR_, C_, CLS_INST_=this, MSG_="Randomization failed!", ID_=`gfn) \
|
||||
`DV_CHECK_FATAL(CLS_INST_.randomize(VAR_) with {C_}, MSG_, ID_)
|
||||
`endif
|
||||
|
||||
// print static/dynamic 1d array or queue
|
||||
|
@ -297,15 +340,16 @@
|
|||
`define GET_PARITY(val, odd=0) (^val ^ odd)
|
||||
`endif
|
||||
|
||||
// wait a task or statement with timer watchdog
|
||||
// Wait a task or statement with exit condition
|
||||
// Kill the thread when either the wait statement is completed or exit condition occurs
|
||||
// input WAIT_ need to be a statement. Here are some examples
|
||||
// `DV_SPINWAIT(wait(...);, "Wait for ...")
|
||||
// `DV_SPINWAIT(
|
||||
// while (1) begin
|
||||
// ...
|
||||
// end)
|
||||
`ifndef DV_SPINWAIT
|
||||
`define DV_SPINWAIT(WAIT_, MSG_ = "timeout occurred!", TIMEOUT_NS_ = default_spinwait_timeout_ns, ID_ =`gfn) \
|
||||
`ifndef DV_SPINWAIT_EXIT
|
||||
`define DV_SPINWAIT_EXIT(WAIT_, EXIT_, MSG_ = "exit condition occurred!", ID_ =`gfn) \
|
||||
begin \
|
||||
fork begin \
|
||||
fork \
|
||||
|
@ -313,10 +357,164 @@
|
|||
WAIT_ \
|
||||
end \
|
||||
begin \
|
||||
wait_timeout(TIMEOUT_NS_, ID_, MSG_); \
|
||||
EXIT_ \
|
||||
if (MSG_ != "") begin \
|
||||
`uvm_info(ID_, MSG_, UVM_HIGH) \
|
||||
end \
|
||||
end \
|
||||
join_any \
|
||||
disable fork; \
|
||||
end join \
|
||||
end
|
||||
`endif
|
||||
|
||||
// wait a task or statement with timer watchdog
|
||||
`ifndef DV_SPINWAIT
|
||||
`define DV_SPINWAIT(WAIT_, MSG_ = "timeout occurred!", TIMEOUT_NS_ = default_spinwait_timeout_ns, ID_ =`gfn) \
|
||||
`DV_SPINWAIT_EXIT(WAIT_, wait_timeout(TIMEOUT_NS_, ID_, MSG_);, "", ID_)
|
||||
`endif
|
||||
|
||||
// Control assertions in the DUT.
|
||||
//
|
||||
// This macro is invoked in top level testbench that instantiates the DUT. It spawns off an initial
|
||||
// block that forever waits for a resource of type bit named by the string arg ~LABEL_~ that
|
||||
// can be set by any entity in the testbench. Based on the value set, it enables or disables the
|
||||
// assertions at the hierarchy of the provided path. The entity setting the resource value invokes
|
||||
// uvm_config_db#(bit)::set(...) and this macro calls the corresponding get.
|
||||
//
|
||||
// LABEL_ : Name of the assertion control resource bit (string).
|
||||
// HIER_ : Path to the module within which the assertion is controlled.
|
||||
// LEVELS_: Number of levels within the module to control the assertions.
|
||||
// SCOPE_ : Hierarchical string path to the testbench where this macro is invoked, example: %m.
|
||||
// ID_ : Identifier string used for UVM logs.
|
||||
`ifndef DV_ASSERT_CTRL
|
||||
`define DV_ASSERT_CTRL(LABEL_, HIER_, LEVELS_ = 0, SCOPE_ = "", ID_ = "%m") \
|
||||
initial begin \
|
||||
bit assert_en; \
|
||||
forever begin \
|
||||
uvm_config_db#(bit)::wait_modified(null, SCOPE_, LABEL_); \
|
||||
if (!uvm_config_db#(bit)::get(null, SCOPE_, LABEL_, assert_en)) begin \
|
||||
`uvm_fatal(ID_, $sformatf("Failed to get \"%0s\" from uvm_config_db", LABEL_)) \
|
||||
end \
|
||||
if (assert_en) begin \
|
||||
`uvm_info(ID_, $sformatf("Enabling assertions: %0s", `DV_STRINGIFY(HIER_)), UVM_LOW) \
|
||||
$asserton(LEVELS_, HIER_); \
|
||||
end else begin \
|
||||
`uvm_info(ID_, $sformatf("Disabling assertions: %0s", `DV_STRINGIFY(HIER_)), UVM_LOW) \
|
||||
$assertoff(LEVELS_, HIER_); \
|
||||
end \
|
||||
end \
|
||||
end
|
||||
`endif
|
||||
|
||||
// Enable / disable assertions at a module hierarchy identified by LABEL_.
|
||||
//
|
||||
// This goes in conjunction with `DV_ASSERT_CTRL() macro above, but is invoked in the entity that is
|
||||
// sending the req to turn on / off the assertions. Note that that piece of code invoking this macro
|
||||
// does not have the information on the actual hierarchical path to the module or the levels - this
|
||||
// is 'wrapped' into the LABEL_ instead. DV user needs to uniquify the label sufficienly enough to
|
||||
// reflect it.
|
||||
//
|
||||
// LABEL_ : Name of the assertion control resource bit (string).
|
||||
// VALUE_ : Value of the control bit - 1 - enable assertions, 0 - disable assertions.
|
||||
// SCOPE_ : Hierarchical string path to the testbench where this macro is invoked, example: %m.
|
||||
`ifndef DV_ASSERT_CTRL_REQ
|
||||
`define DV_ASSERT_CTRL_REQ(LABEL_, VALUE_, SCOPE_="") \
|
||||
begin \
|
||||
uvm_config_db#(bit)::set(null, SCOPE_, LABEL_, VALUE_); \
|
||||
end
|
||||
`endif
|
||||
|
||||
// Macros for logging (info, warning, error and fatal severities).
|
||||
//
|
||||
// These are meant to be invoked in modules and interfaces that are shared between DV and Verilator
|
||||
// testbenches.
|
||||
`ifdef UVM
|
||||
`ifndef DV_INFO
|
||||
`define DV_INFO(MSG_, VERBOSITY_ = UVM_LOW, ID_ = $sformatf("%m")) \
|
||||
`uvm_info(ID_, MSG_, VERBOSITY_)
|
||||
`endif
|
||||
|
||||
`ifndef DV_WARNING
|
||||
`define DV_WARNING(MSG_, ID_ = $sformatf("%m")) \
|
||||
`uvm_warning(ID_, MSG_)
|
||||
`endif
|
||||
|
||||
`ifndef DV_ERROR
|
||||
`define DV_ERROR(MSG_, ID_ = $sformatf("%m")) \
|
||||
`uvm_error(ID_, MSG_)
|
||||
`endif
|
||||
|
||||
`ifndef DV_FATAL
|
||||
`define DV_FATAL(MSG_, ID_ = $sformatf("%m")) \
|
||||
`uvm_fatal(ID_, MSG_)
|
||||
`endif
|
||||
|
||||
`else // UVM
|
||||
|
||||
`ifndef DV_INFO
|
||||
`define DV_INFO(MSG_, VERBOSITY = DUMMY_, ID_ = $sformatf("%m")) \
|
||||
$display("%0t: (%0s:%0d) [%0s] %0s", $time, `__FILE__, `__LINE__, ID_, MSG_);
|
||||
`endif
|
||||
|
||||
`ifndef DV_WARNING
|
||||
`define DV_WARNING(MSG_, ID_ = $sformatf("%m")) \
|
||||
$warning("%0t: (%0s:%0d) [%0s] %0s", $time, `__FILE__, `__LINE__, ID_, MSG_);
|
||||
`endif
|
||||
|
||||
`ifndef DV_ERROR
|
||||
`define DV_ERROR(MSG_, ID_ = $sformatf("%m")) \
|
||||
$error("%0t: (%0s:%0d) [%0s] %0s", $time, `__FILE__, `__LINE__, ID_, MSG_);
|
||||
`endif
|
||||
|
||||
`ifndef DV_FATAL
|
||||
`define DV_FATAL(MSG_, ID_ = $sformatf("%m")) \
|
||||
$fatal("%0t: (%0s:%0d) [%0s] %0s", $time, `__FILE__, `__LINE__, ID_, MSG_);
|
||||
`endif
|
||||
|
||||
`endif // UVM
|
||||
|
||||
// Declare array of alert interface, using parameter NUM_ALERTS and LIST_OF_ALERTS, and connect to
|
||||
// arrays of wires (alert_tx and alert_rx). User need to manually connect these wires to DUT
|
||||
// Also set each alert_if to uvm_config_db to use in env
|
||||
`ifndef DV_ALERT_IF_CONNECT
|
||||
`define DV_ALERT_IF_CONNECT \
|
||||
alert_esc_if alert_if[NUM_ALERTS](.clk(clk), .rst_n(rst_n)); \
|
||||
prim_alert_pkg::alert_rx_t [NUM_ALERTS-1:0] alert_rx; \
|
||||
prim_alert_pkg::alert_tx_t [NUM_ALERTS-1:0] alert_tx; \
|
||||
for (genvar k = 0; k < NUM_ALERTS; k++) begin : connect_alerts_pins \
|
||||
assign alert_rx[k] = alert_if[k].alert_rx; \
|
||||
assign alert_if[k].alert_tx = alert_tx[k]; \
|
||||
initial begin \
|
||||
uvm_config_db#(virtual alert_esc_if)::set(null, $sformatf("*.env.m_alert_agent_%0s", \
|
||||
LIST_OF_ALERTS[k]), "vif", alert_if[k]); \
|
||||
end \
|
||||
end
|
||||
`endif
|
||||
|
||||
// Instantiates a covergroup in an interface or module.
|
||||
//
|
||||
// This macro assumes that a covergroup of the same name as the __CG_NAME arg is defined in the
|
||||
// interface or module. It just adds some extra signals and logic to control the creation of the
|
||||
// covergroup instance with ~bit en_<cg_name>~. This defaults to 0. It is ORed with the external
|
||||
// __COND signal. The testbench can modify it at t = 0 based on the test being run.
|
||||
// NOTE: This is not meant to be invoked inside a class.
|
||||
//
|
||||
// __CG_NAME : Name of the covergroup.
|
||||
// __COND : External condition / expr that controls the creation of the covergroup.
|
||||
// __CG_ARGS : Arguments to covergroup instance, if any. Args MUST BE wrapped in (..).
|
||||
`ifndef DV_INSTANTIATE_CG
|
||||
`define DV_INSTANTIATE_CG(__CG_NAME, __COND = 1'b1, __CG_ARGS = ()) \
|
||||
bit en_``__CG_NAME = 1'b0; \
|
||||
__CG_NAME __CG_NAME``_inst; \
|
||||
initial begin \
|
||||
/* The #1 delay below allows any part of the tb to control the conditions first at t = 0. */ \
|
||||
#1; \
|
||||
if ((en_``__CG_NAME) || (__COND)) begin \
|
||||
`DV_INFO({"Creating covergroup ", `"__CG_NAME`"}, UVM_MEDIUM) \
|
||||
__CG_NAME``_inst = new``__CG_ARGS; \
|
||||
end \
|
||||
end
|
||||
`endif
|
||||
|
||||
`endif // __DV_MACROS_SVH__
|
||||
|
|
|
@ -6,12 +6,15 @@
|
|||
class dv_report_server extends uvm_default_report_server;
|
||||
|
||||
bit show_file_line = 1'b1;
|
||||
// if enabled, show the relative path of the file. By default only show file name
|
||||
bit show_file_path = 1'b0;
|
||||
bit use_default_uvm_report_message_format = 1'b0;
|
||||
|
||||
function new (string name = "");
|
||||
super.new(name);
|
||||
// provide ability to override these knobs over cli
|
||||
void'($value$plusargs("show_file_line=%0b", show_file_line));
|
||||
void'($value$plusargs("show_file_path=%0b", show_file_path));
|
||||
void'($value$plusargs("use_default_uvm_report_message_format=%0b",
|
||||
use_default_uvm_report_message_format));
|
||||
endfunction
|
||||
|
@ -66,7 +69,7 @@ class dv_report_server extends uvm_default_report_server;
|
|||
string file_line;
|
||||
|
||||
if (show_file_line && filename != "") begin
|
||||
filename = get_no_hier_filename(filename);
|
||||
if (!show_file_path) filename = get_no_hier_filename(filename);
|
||||
file_line = $sformatf("(%0s:%0d) ", filename, line);
|
||||
end
|
||||
obj_name = {obj_name, ((obj_name != "") ? " " : "")};
|
||||
|
|
|
@ -8,13 +8,14 @@ description: "DV utilities"
|
|||
filesets:
|
||||
files_dv:
|
||||
depend:
|
||||
- lowrisc:dv:dv_macros
|
||||
- lowrisc:dv:common_ifs
|
||||
- lowrisc:prim:assert:0.1
|
||||
- lowrisc:ibex:bus_params_pkg
|
||||
files:
|
||||
- dv_utils_pkg.sv
|
||||
- dv_macros.svh: {is_include_file: true}
|
||||
- dv_report_server.sv: {is_include_file: true}
|
||||
- dv_vif_wrap.sv: {is_include_file: true}
|
||||
file_type: systemVerilogSource
|
||||
|
||||
targets:
|
||||
|
|
42
vendor/lowrisc_ip/dv/sv/dv_utils/dv_utils_pkg.sv
vendored
42
vendor/lowrisc_ip/dv/sv/dv_utils/dv_utils_pkg.sv
vendored
|
@ -85,6 +85,17 @@ package dv_utils_pkg;
|
|||
return (a > b) ? a : b;
|
||||
endfunction
|
||||
|
||||
// get absolute value of the input. Usage: absolute(val) or absolute(a - b)
|
||||
function automatic uint absolute(int val);
|
||||
return val >= 0 ? val : -val;
|
||||
endfunction
|
||||
|
||||
// endian swap
|
||||
function automatic logic [31:0] endian_swap(logic [31:0] data);
|
||||
return {<<8{data}};
|
||||
endfunction
|
||||
|
||||
`ifdef UVM
|
||||
// Simple function to set max errors before quitting sim
|
||||
function automatic void set_max_quit_count(int n);
|
||||
uvm_report_server report_server = uvm_report_server::get_server();
|
||||
|
@ -121,16 +132,6 @@ package dv_utils_pkg;
|
|||
end
|
||||
endfunction
|
||||
|
||||
// get absolute value of the input. Usage: absolute(val) or absolute(a - b)
|
||||
function automatic uint absolute(int val);
|
||||
return val >= 0 ? val : -val;
|
||||
endfunction
|
||||
|
||||
// endian swap
|
||||
function automatic logic [31:0] endian_swap(logic [31:0] data);
|
||||
return {<<8{data}};
|
||||
endfunction
|
||||
|
||||
// create a sequence by name and return the handle of uvm_sequence
|
||||
function automatic uvm_sequence create_seq_by_name(string seq_name);
|
||||
uvm_object obj;
|
||||
|
@ -149,8 +150,29 @@ package dv_utils_pkg;
|
|||
end
|
||||
return seq;
|
||||
endfunction
|
||||
`endif
|
||||
|
||||
// Returns the hierarchical path to the interface / module N levels up.
|
||||
//
|
||||
// Meant to be invoked inside a module or interface.
|
||||
// hier: String input of the interface / module, typically $sformatf("%m").
|
||||
// n_levels_up: Integer number of levels up the hierarchy to omit.
|
||||
// Example: if (hier = tb.dut.foo.bar, n_levels_up = 2), then return tb.dut
|
||||
function automatic string get_parent_hier(string hier, int n_levels_up = 1);
|
||||
int idx;
|
||||
int level;
|
||||
if (n_levels_up <= 0) return hier;
|
||||
for (idx = hier.len() - 1; idx >= 0; idx--) begin
|
||||
if (hier[idx] == ".") level++;
|
||||
if (level == n_levels_up) break;
|
||||
end
|
||||
return (hier.substr(0, idx - 1));
|
||||
endfunction
|
||||
|
||||
// sources
|
||||
`ifdef UVM
|
||||
`include "dv_report_server.sv"
|
||||
`include "dv_vif_wrap.sv"
|
||||
`endif
|
||||
|
||||
endpackage
|
||||
|
|
96
vendor/lowrisc_ip/dv/sv/dv_utils/dv_vif_wrap.sv
vendored
Normal file
96
vendor/lowrisc_ip/dv/sv/dv_utils/dv_vif_wrap.sv
vendored
Normal file
|
@ -0,0 +1,96 @@
|
|||
// Copyright lowRISC contributors.
|
||||
// Licensed under the Apache License, Version 2.0, see LICENSE for details.
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
// Abstract class meant to hold arbitrary virtual interface handles.
|
||||
//
|
||||
// Written primarily for an interface which implements functional coverage, this could be used
|
||||
// for other purposes as well. This abstract class provides utilities & macros to retrieve
|
||||
// virtual interface handles that are bound to a DUT's sub-modules. These sub-module interfaces must
|
||||
// self-register using the `DV_VIF_WRAP_SET_VIF()` macro (see details below). The extended class
|
||||
// then implements the `get_vifs()` method using the `DV_VIF_WRAP_GET_VIF*` macros below to retrieve
|
||||
// the sub-module interface handles it maintains.
|
||||
virtual class dv_vif_wrap;
|
||||
string hier; // Represents the hierarchy of the parent module or interface.
|
||||
string name; // Name of the class instance.
|
||||
|
||||
function new(string hier, string name = "");
|
||||
this.hier = hier;
|
||||
this.name = name;
|
||||
endfunction
|
||||
|
||||
// Abstract method implemented by the extended class. It is recommended to invoke the helper
|
||||
// macros below rather than manually define it.
|
||||
pure virtual task get_vifs();
|
||||
|
||||
endclass
|
||||
|
||||
// Helper macros.
|
||||
//
|
||||
// These are defined in the file itself since they are tightly coupled to the class definition
|
||||
// above. These are scoped globally so that extended classes can invoke them.
|
||||
|
||||
// Enable an interface to register itself (set its handle into the config db).
|
||||
//
|
||||
// Meant to be invoked from an interface. The macros invocation causes the interface to register
|
||||
// itself into the uvm_resource_db pool. The derived class of dv_vif_wrap retrieves the handle to
|
||||
// that interface handle from the uvm_resource db pool.
|
||||
//
|
||||
// SV LRM does not yet allow referencing the instance of an interface within itself as one
|
||||
// would in case of a class using the ~this~ keyword. However, most major simulators actually
|
||||
// support this in their own custom way. On VCS, a reference to self within the interface can be set
|
||||
// using `interface::self()` construct. On Xcelium, this is achieved by simply referencing the
|
||||
// interface name.
|
||||
//
|
||||
// _IF: The SV interface
|
||||
// _VIF: The corresponding virtual interface handle name
|
||||
// _LEVEL: # of hierarchical levels the module to which the SV interface is bound, starting at the
|
||||
// top level DUT instance.
|
||||
`define DV_VIF_WRAP_SET_VIF(_IF, _VIF, _LEVEL = 0) \
|
||||
import uvm_pkg::*; \
|
||||
function automatic void self_register(); \
|
||||
string path; \
|
||||
virtual _IF vif; \
|
||||
/* Initial block adds another level in the path hierarchy which needs to be split out. */ \
|
||||
/* Add one more to go one level up the interface instance. */ \
|
||||
/* Example: tb.dut.core.u_foo_if.self_register -> tb.dut.core. */ \
|
||||
path = dv_utils_pkg::get_parent_hier(.hier($sformatf("%m")), .n_levels_up(2 + _LEVEL)); \
|
||||
`ifdef VCS \
|
||||
vif = interface::self(); \
|
||||
`elsif XCELIUM \
|
||||
vif = _IF; \
|
||||
`else \
|
||||
vif = _IF; \
|
||||
`endif \
|
||||
uvm_pkg::uvm_resource_db#(virtual _IF)::set(path, `"_VIF`", vif); \
|
||||
endfunction \
|
||||
initial self_register();
|
||||
|
||||
// Enables the retrieval of individual vifs.
|
||||
//
|
||||
// The three macros below go together to define the _get_vifs() task in the extended class.
|
||||
`define DV_VIF_WRAP_GET_VIFS_BEGIN \
|
||||
task get_vifs(); \
|
||||
fork \
|
||||
|
||||
// To avoid race condition between the instant when an interface handle is set into the config db
|
||||
// and the instant when it is retrieved (in the same time step, at t = 0), the macro below invokes
|
||||
// uvm_config_db#(..)::wait_modified() to ensure that the retrieval is done only after the set.
|
||||
`define DV_VIF_WRAP_GET_VIF(_IF, _VIF) \
|
||||
begin \
|
||||
bit vif_found; \
|
||||
/* At most 2 retries. */ \
|
||||
repeat (2) begin \
|
||||
/* Force the evaluation at the end of the time step. */ \
|
||||
#0; \
|
||||
if (uvm_pkg::uvm_resource_db#(virtual _IF)::read_by_name(hier, `"_VIF`", _VIF)) begin \
|
||||
vif_found = 1'b1; \
|
||||
break; \
|
||||
end \
|
||||
end \
|
||||
`DV_CHECK_FATAL(vif_found, {`"_VIF`", " not found in the resource db"}, hier) \
|
||||
end
|
||||
|
||||
`define DV_VIF_WRAP_GET_VIFS_END \
|
||||
join \
|
||||
endtask
|
96
vendor/lowrisc_ip/dv/tools/Makefile
vendored
96
vendor/lowrisc_ip/dv/tools/Makefile
vendored
|
@ -1,96 +0,0 @@
|
|||
# Copyright lowRISC contributors.
|
||||
# Licensed under the Apache License, Version 2.0, see LICENSE for details.
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
# directory paths
|
||||
# _ROOT are paths indicating a 'starting point'
|
||||
# _PATH paths are intermediate paths leading to a _DIR
|
||||
# _LOC are directory names without the hierarchy
|
||||
# _DIR paths are final paths used in make rules
|
||||
# All these variables can be overridden from the command line
|
||||
# Test Makefile needs to set DV_DIR, DUT_TOP, TB_TOP, FUSESOC_CORE, COMPILE_KEY and TEST_NAME
|
||||
# at minimum
|
||||
|
||||
export SHELL := /bin/bash
|
||||
TIMESTAMP ?= $(shell date +%m.%d.%y_%H.%M.%S)
|
||||
MAKE_ROOT := $(shell dirname $(realpath $(lastword $(MAKEFILE_LIST))))
|
||||
export PROJ_ROOT := ${MAKE_ROOT}/../../..
|
||||
export PRJ_DIR := ${PROJ_ROOT}
|
||||
export CURR_DIR := $(shell pwd)
|
||||
SCRATCH_ROOT ?= ${DV_DIR}/scratch
|
||||
|
||||
SCRATCH_LOC ?= ${DUT_TOP}
|
||||
BUILD_LOC ?= ${COMPILE_KEY}
|
||||
RUN_LOC ?= ${TIMESTAMP}
|
||||
|
||||
SCRATCH_PATH ?= ${SCRATCH_ROOT}/${SCRATCH_LOC}
|
||||
BUILD_DIR := ${SCRATCH_PATH}/${BUILD_LOC}
|
||||
RUN_PATH ?= ${SCRATCH_PATH}/${TEST_NAME}
|
||||
RUN_DIR := ${RUN_PATH}/${RUN_LOC}
|
||||
COV_REPORT_DIR ?= ${SCRATCH_PATH}/cov_report
|
||||
SW_BUILD_DIR ?= ${RUN_DIR}/sw_build
|
||||
SW_ROOT_DIR := ${PRJ_DIR}/sw
|
||||
SW_BUILD_DEVICE ?= fpga_nexysvideo
|
||||
|
||||
JOB_OPTS ?=
|
||||
BUILD_JOB_OPTS ?= $(JOB_OPTS)
|
||||
RUN_JOB_OPTS ?= $(JOB_OPTS)
|
||||
|
||||
# limit run directories upto a limit specified below
|
||||
RUN_DIR_LIMIT ?= 5
|
||||
|
||||
# global simulation specific variables (but simulator independent)
|
||||
SEED ?= $(shell od -vAn -N4 -tu < /dev/random | xargs)
|
||||
BUILD_LOG ?= ${BUILD_DIR}/build.log
|
||||
RUN_LOG ?= ${RUN_DIR}/run.log
|
||||
UVM_VERBOSITY ?= UVM_LOW
|
||||
|
||||
TOPS ?= ${TB_TOP} ${DUT_TOP}_bind
|
||||
|
||||
# Options for SV build / C build / simulation run
|
||||
# CL_ prefix represents command line versions of these options - they should be empty and only
|
||||
# be set on the command line
|
||||
|
||||
# BUILD_OPTS are passed to the simulator during the SV testbench compile step
|
||||
CL_BUILD_OPTS +=
|
||||
BUILD_OPTS += $(addprefix -top , $(TOPS))
|
||||
|
||||
# RUN_OPTS are passed to the simulation executable that is invoked to run the simulation
|
||||
CL_RUN_OPTS +=
|
||||
RUN_OPTS += +UVM_NO_RELNOTES
|
||||
|
||||
# SW_OPTS are options for SW apps that come with their own Makefile (example coremark)
|
||||
CL_SW_OPTS +=
|
||||
SW_OPTS += ${CL_SW_OPTS}
|
||||
|
||||
# SW_FLAGS are compiler flags / directives that we can pass for each test using our own Makefiles
|
||||
CL_SW_FLAGS +=
|
||||
SW_FLAGS += ${CL_SW_FLAGS}
|
||||
|
||||
# default project-wide bus widths
|
||||
TL_AW ?= 32
|
||||
TL_DW ?= 32
|
||||
TL_DBW ?= 4
|
||||
|
||||
# add auxiliary / helper mk files
|
||||
# set simulator to vcs by default
|
||||
SIMULATOR ?= vcs
|
||||
|
||||
# common tests/seqs
|
||||
include ${MAKE_ROOT}/common_tests.mk
|
||||
|
||||
# tool to generate file list
|
||||
include ${MAKE_ROOT}/fusesoc.mk
|
||||
|
||||
# modes / switches (option groups) that can be turned on easily
|
||||
include ${MAKE_ROOT}/modes.mk
|
||||
|
||||
# add tool specific mk file
|
||||
ifeq (${SIMULATOR},vcs)
|
||||
include ${MAKE_ROOT}/vcs/vcs.mk
|
||||
else ifeq (${SIMULATOR},xcelium)
|
||||
include ${MAKE_ROOT}/xcelium/xcelium.mk
|
||||
# else add other tool specific mk files
|
||||
endif
|
||||
|
||||
# generic rules to sequence build and run flows
|
||||
include ${MAKE_ROOT}/rules.mk
|
227
vendor/lowrisc_ip/dv/tools/README.md
vendored
227
vendor/lowrisc_ip/dv/tools/README.md
vendored
|
@ -1,225 +1,2 @@
|
|||
# DV build and run flow
|
||||
The Makefile system implemented here is a standardized solution for building and
|
||||
running DV sims for all ip/block/top level testbenches. This is only a stop
|
||||
gap solution until a proper build and run tool is developed and implemented. The
|
||||
way the Makefiles are structured will hopefully make the eventual transition
|
||||
easier.
|
||||
|
||||
## Makefile Organization
|
||||
```
|
||||
- hw/ip/<ip>/dv/Makefile OR
|
||||
hw/top_earlgrey/dv/Makefile
|
||||
- hw/dv/tools/Makefile
|
||||
- ral_gen.mk
|
||||
- fusesoc.mk
|
||||
- modes.mk
|
||||
- rules.mk
|
||||
- vcs/vcs.mk # if SIMULATOR=vcs
|
||||
- xcelium/xcelium.mk # if SIMULATOR=xcelium
|
||||
```
|
||||
|
||||
### Test Makefile
|
||||
The test Makefile that serves as the entry point into the DV build and run
|
||||
flow is placed in individual 'dv' directories of each testbench. For example:
|
||||
```hw/ip/uart/dv/Makefile```
|
||||
This includes the top level Makefile, some mandatory variables that are to be
|
||||
set and exported and the actual tests.
|
||||
|
||||
### Top level Makefile
|
||||
This includes the top level Makefile which contains generic set of variable
|
||||
names (most of which can be overridden) to indicate where, what and how to build
|
||||
and run tests, some option groups and generic set of rules. This is located at
|
||||
hw/dv/tools/Makefile
|
||||
|
||||
### Generic Tool-independent Make variables
|
||||
The following is a list of Make variables that are used for specifying options
|
||||
to run simulations in a tool-independent way. Some variables are overridable in
|
||||
the Test Makefile (or command line) and some are appended to group / add options
|
||||
together. Please see following table for more details:
|
||||
|
||||
Make variable | Description | Overridable (O) or Appendable (A) |
|
||||
--------------|-------------|-----------------------------------|
|
||||
DV_DIR | This is the top level DV directory for the IP containing the Test Makefile. | |
|
||||
DUT_TOP | This is the top level dut module under test. | |
|
||||
TB_TOP | This is the top level tb module under test. This is used by the `<simulator>.tcl` file when dumping waves. | |
|
||||
FUSESOC_CORE | This is the testbench fusesoc .core name that contains the simulation target. This .core file is typically placed in the same directory as the test Makefile. | |
|
||||
COMPILE_KEY | Users need to define COMPILE_KEY sets for building Test Makefile, CL with a unique sets of compile time options. This is to be done in the Test Makefile in this way: <br>`ifeq ($(COMPILE_KEY),foo)`<br>  `BUILD_OPTS += +define+FOO` <br>`endif`<br> There is a 'default' compile key already added which implies no additional compile time options are required. Within each test specification, the COMPILE_KEY can be overridden to use the specific compile key. <br>`ifeq ($(TEST_NAME),foo_test)`<br>  `COMPILE_KEY = foo` <br>  `# other test opts` <br>`endif` | O (Within tests, & command line) |
|
||||
UVM_TEST | SV UVM test class to create in the test. This is set to the 'base_test' by default and is overridden in the test specifications if needed. | O (Test Makefile) |
|
||||
UVM_TEST_SEQ | SV UVM test sequence to create in the test. This is set to the 'base_test' by default and is overridden in the test specifications if needed. | O (Test Makefile) |
|
||||
TEST_NAME | Name of the test to run. These are specified in the test Makefile. By default, it is set to run the sanity test (simply calling 'make' will run this test). | O (command line only) |
|
||||
SIMULATOR | What simulator to use. Currently only 'vcs' is supported and is set by default. | O |
|
||||
BUILD_OPTS | Options to pass for build. | A (Test Makefile :: COMPILE_KEY specifications only) |
|
||||
CL_BUILD_OPTS | Pass additional build options on the command line. | A (command line only) |
|
||||
RUN_OPTS | Options to pass for run. | A (Test Makefile :: test specifications only) |
|
||||
CL_RUN_OPTS | Pass additional run options on the command line. | A (command line only) |
|
||||
WAVES | Enable wave dumps (fsdb). Set to 0 by default; override with WAVES=1. | O |
|
||||
SIMPROFILE | Turn on sim profiling (time option by default). Set to 0 by default; override with SIMPROFILE=1. | O |
|
||||
COV | Turn on coverage collection. Set to 0 by default; override with COV=1. | O |
|
||||
UVM_VERBOSITY | Verbosity level for UVM. Set to UVM_LOW by default; override with UVM_VERBOSITY=UVM_NONE / UVM_LOW / UVM_HIGH / UVM_DEBUG. | O |
|
||||
BUILD_LOC | Build directory name in the scratch area. This defaults to the COMPILE_KEY used by the corresponding test. You can override it to use a different directory name. This is helpful when there is a test / regression already running and you made some fixes and want to rerun some failing test from another terminal without affecting the existing running sims. | O |
|
||||
RUN_LOC | Run directory name in the scratch area. This defaults to a 'timestamp' value. You can override this to a specific name. Everytime a test is run, it creates a new directory with the current timestamp for the name. By overriding this with a specific name and rerunning the same test with the same directory name, you won't have to reopen waves - you can just reload. This is useful during test debug. | O |
|
||||
SCRATCH_ROOT | This is the path to the root scratch area for bulding and running tests. If SCRATCH_ROOT is not already set, it will create a `scratch` directory in `pwd` which typically is the same as `DV_DIR`. | O (command line only) |
|
||||
RUN_DIR_LIMIT | When you run tests, The flow creates a new `RUN_LOC` directory with the current timestamp (unless it is overridden). In course of debug, you may run the same test multiple times, which will eventually result in a large number of old timestamp directories in the scratch space. Without periodic cleanup, you may run out of scratch space. By default, this variable is set to 5, which means before running the test, it will prune the test area to contain no more than RUN_DIR_LIMIT number of most recent directories (including the newly created one). On the flipside, if you want to run the same test with a large number of iterations, you will need to override this variable to be set to that many iterations to prevent the flow from deleting actively running simulations. | O (command line only) |
|
||||
SEED | This is a run time parameter passed to the sim executable. It uses `od` command to generate a 32-bit random number to run the sim with a unique seed. You can override this variable on the command line to run the test with a specific seed for debug. | O (command line only) |
|
||||
XPROP | This is a compile time parameter to enable / disable X-Propagation. Set to 1 by default. | O (command line only) |
|
||||
|
||||
This is not an exhaustive list of Make variables. Please see `./Makefile` and
|
||||
`./*.mk` for more such variables in use.
|
||||
|
||||
### ral_gen.mk
|
||||
#### RAL generation tool specific mk file
|
||||
This lists tools and options to generate the ral model by simply running the
|
||||
command `make ral` from the same directory as the Test Makefile. For generating
|
||||
the UVM REG based RAL model, we use the same [in-house tool]({{< relref "doc/rm/register_tool" >}}) for autogenerating
|
||||
RTL with mako template.
|
||||
|
||||
### fusesoc.mk
|
||||
#### RTL/TB filelist generation tool specific mk file
|
||||
This lists tools and options to generate the flattened filelist to supply to the
|
||||
simulator during the build step.
|
||||
|
||||
### modes.mk
|
||||
#### Modes or option groups that can be turned on easily
|
||||
This lists common modes (which are groups of compile time and / or run time options)
|
||||
passed to the simulator for turning on a specific function. Modes listed in this
|
||||
file are meant to be common across all environments and simulator tools. Modes added
|
||||
in this file are `WAVES`, `COV`, `SIMPROFILE` and `UVM_TRACE`. These are truly
|
||||
global and can be turned on easily on the command line by setting them to 1: `COV=1`
|
||||
switch on the command line will turn on coverage. These modes may require options
|
||||
that are specific to the simulator tool, so the make variable `SIMULATOR` can be used
|
||||
to set tool specific options.
|
||||
|
||||
### rules.mk
|
||||
#### List of Make targets and recipes
|
||||
This is the only file in the make flow that contains targets and recipes that
|
||||
are completely agnostic to the tools used. It sequences the build and run set
|
||||
of targets.
|
||||
|
||||
### Simulator mk ({vcs, xcelium}.mk)
|
||||
#### Simulator tool specific options
|
||||
The top level Makefile uses `SIMULATOR` variable (which is set to `vcs` by
|
||||
default) to enable tool specific options. If support for new tools need to be
|
||||
added, then those tool specific mk files need to be supplied. It sets the
|
||||
following mandatory variables - `SIMCC` & `SIMX`. It also exports additional
|
||||
tool specific variables to the sub shell when the recipes are executed. This
|
||||
file has been placed in the respective tool directory.
|
||||
|
||||
### Simulation targets
|
||||
* **build**: Compile and elaborate the testbench
|
||||
* **gen_sv_flist**: Generate the full file list which will be used by the
|
||||
simulator to build the simulation executable
|
||||
* **run**: Run the test (the simulation executable needs to be built already)
|
||||
* **env/\<ip\>_reg_block.sv**: Generate the RAL model only
|
||||
* **sw_build**: Only compile the C SW test
|
||||
|
||||
### Building and running tests
|
||||
All of the below command examples are to be run from the 'dv' directory
|
||||
containing the Test Makefile.
|
||||
|
||||
##### Run the following command to build and run tests:
|
||||
```console
|
||||
$ make TEST_NAME=[test-name] [overrides]
|
||||
```
|
||||
|
||||
##### To only buld the simv:
|
||||
```console
|
||||
$ make build TEST_NAME=[test-name] [overrides]
|
||||
```
|
||||
|
||||
##### To run the sim after build is complete:
|
||||
```console
|
||||
$ make run TEST_NAME=[test-name] [overrides]
|
||||
```
|
||||
|
||||
##### To build and run the sanity test:
|
||||
```console
|
||||
$ make
|
||||
```
|
||||
This will work provided user has specified a set a 'default' value to the
|
||||
`TEST_NAME` Make variable.
|
||||
|
||||
##### Below are some examples
|
||||
|
||||
###### Build and run a test:
|
||||
This builds the 'default' compile key and runs the 'uart_sanity' test
|
||||
```console
|
||||
$ make TEST_NAME=uart_sanity
|
||||
```
|
||||
|
||||
###### Run with specific seed:
|
||||
```console
|
||||
$ make TEST_NAME=<test-name> SEED=123423334
|
||||
```
|
||||
|
||||
###### Dump waves:
|
||||
```console
|
||||
$ make TEST_NAME=<test-name> SEED=123423334 WAVES=1
|
||||
```
|
||||
|
||||
###### Run with coverage option enabled:
|
||||
```console
|
||||
$ make TEST_NAME=<test-name> COV=1
|
||||
```
|
||||
|
||||
The options.mk file lists several tool options passed to the build and run steps
|
||||
for enabling coverage collection. One of the options is adding a hier file (with
|
||||
a `-cm_hier` switch). By default, it looks for a file called `cover.cfg` in the
|
||||
'dv' directory set using the `CM_HIER` Make variable - if it exists, it adds the
|
||||
switch automatically when coverage is enabled. If another hier file is desired
|
||||
(or with another name), it can be placed anywhere as the user desires and user can
|
||||
add the following line to the Test Makefile:
|
||||
|
||||
```gnumake
|
||||
CM_HIER := <path-to-hier-file
|
||||
```
|
||||
|
||||
###### Enable sim profiling:
|
||||
```console
|
||||
$ make TEST_NAME=<test-name> SIMPROFILE=1
|
||||
```
|
||||
|
||||
###### Override UVM verbosity:
|
||||
```console
|
||||
$ make TEST_NAME=<test-name> WAVES=1 UVM_VERBOSITY=UVM_DEBUG
|
||||
```
|
||||
|
||||
###### Build only (This will build the 'default' compile key):
|
||||
```console
|
||||
$ make build
|
||||
```
|
||||
|
||||
###### Build 'foo' compile key instead:
|
||||
```console
|
||||
$ make build COMPILE_KEY=foo
|
||||
```
|
||||
|
||||
###### Build with 'FOO' preprocessor flag:
|
||||
```console
|
||||
$ make build CL_BUILD_OPTS+=+define+FOO
|
||||
```
|
||||
|
||||
###### Run with 'FOO' runtime plusarg:
|
||||
```console
|
||||
$ make TEST_NAME=<test-name> CL_RUN_OPTS+=+FOO
|
||||
```
|
||||
|
||||
###### Run test with Xcelium:
|
||||
```
|
||||
$ make TEST_NAME=<test-name> SIMULATOR=xcelium
|
||||
```
|
||||
|
||||
###### Run test with Xcelium and dump WAVES in fsdb:
|
||||
```console
|
||||
$ make TEST_NAME=<test-name> SIMULATOR=xcelium WAVES=1
|
||||
```
|
||||
|
||||
###### Run test with Xcelium and dump WAVES in shm:
|
||||
```console
|
||||
$ make TEST_NAME=<test-name> SIMULATOR=xcelium WAVES=1 DUMP=shm
|
||||
```
|
||||
|
||||
###### Run only (This assumes the sim executable for the specified compile key is available):
|
||||
|
||||
```console
|
||||
$ make run TEST_NAME=<test-name> SEED=123423334 WAVES=1
|
||||
```
|
||||
# DV simulation flow
|
||||
TODO
|
||||
|
|
61
vendor/lowrisc_ip/dv/tools/common.tcl
vendored
Normal file
61
vendor/lowrisc_ip/dv/tools/common.tcl
vendored
Normal file
|
@ -0,0 +1,61 @@
|
|||
# Copyright lowRISC contributors.
|
||||
# Licensed under the Apache License, Version 2.0, see LICENSE for details.
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
# Commonly used globals & procs. This file must be sourced first.
|
||||
set simulator ""
|
||||
if {[info exists ::env(SIMULATOR)]} {
|
||||
set simulator "$::env(SIMULATOR)"
|
||||
} else {
|
||||
puts "ERROR: tool script run without SIMULATOR environment variable."
|
||||
quit
|
||||
}
|
||||
|
||||
set waves "none"
|
||||
if {[info exists ::env(WAVES)]} {
|
||||
set waves "$::env(WAVES)"
|
||||
}
|
||||
|
||||
set tb_top "tb"
|
||||
if {[info exists ::(TB_TOP)]} {
|
||||
set tb_top "$::env(TB_TOP)"
|
||||
puts "WARNING: TB_TOP environment variable not set - using \"tb\" as the
|
||||
top level testbench hierarchy."
|
||||
}
|
||||
|
||||
# Checks if variable is defined, else throw an error and exit.
|
||||
proc checkVarExists {var} {
|
||||
upvar $var var_
|
||||
if {![info exists var_]} {
|
||||
puts "ERROR: Variable \"$var\" not found."
|
||||
quit
|
||||
}
|
||||
}
|
||||
|
||||
# If variable is defined, then use it, else set the default value.
|
||||
proc setDefault {var value} {
|
||||
upvar $var var_
|
||||
if {[info exists var_]} {
|
||||
puts "INFO: \"$var\" is already set to \"$var_\"."
|
||||
} else {
|
||||
puts "INFO: Setting \"$var\" to \"$value\"."
|
||||
set var_ $value
|
||||
}
|
||||
return $var_
|
||||
}
|
||||
|
||||
proc checkEq {var value} {
|
||||
upvar $var var_
|
||||
if {$var_ != $value} {
|
||||
puts "ERROR: Check failed \"$var\" == \"$value\"!. Actual: \"$var_\"."
|
||||
quit
|
||||
}
|
||||
}
|
||||
|
||||
proc checkNe {var value} {
|
||||
upvar $var var_
|
||||
if {$var_ == $value} {
|
||||
puts "ERROR: Check failed \"$var\" != \"$value\"!. Actual: \"$var_\"."
|
||||
quit
|
||||
}
|
||||
}
|
66
vendor/lowrisc_ip/dv/tools/common_tests.mk
vendored
66
vendor/lowrisc_ip/dv/tools/common_tests.mk
vendored
|
@ -1,66 +0,0 @@
|
|||
# Copyright lowRISC contributors.
|
||||
# Licensed under the Apache License, Version 2.0, see LICENSE for details.
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
# Makefile option groups that can be enabled by test Makefile / command line.
|
||||
# These are generic set of option groups that apply to all testbenches.
|
||||
# These are meant to be simulator agnostic
|
||||
# Please add tool specific options with appropriate ifeq's
|
||||
|
||||
TEST_PREFIX ?= ${DUT_TOP}
|
||||
|
||||
ifeq (${TEST_NAME},${TEST_PREFIX}_csr_hw_reset)
|
||||
UVM_TEST_SEQ = ${TEST_PREFIX}_common_vseq
|
||||
RUN_OPTS += +csr_hw_reset
|
||||
RUN_OPTS += +en_scb=0
|
||||
endif
|
||||
|
||||
ifeq (${TEST_NAME},${TEST_PREFIX}_csr_rw)
|
||||
UVM_TEST_SEQ = ${TEST_PREFIX}_common_vseq
|
||||
RUN_OPTS += +csr_rw
|
||||
RUN_OPTS += +en_scb=0
|
||||
endif
|
||||
|
||||
ifeq (${TEST_NAME},${TEST_PREFIX}_csr_bit_bash)
|
||||
UVM_TEST_SEQ = ${TEST_PREFIX}_common_vseq
|
||||
RUN_OPTS += +csr_bit_bash
|
||||
RUN_OPTS += +en_scb=0
|
||||
endif
|
||||
|
||||
ifeq (${TEST_NAME},${TEST_PREFIX}_csr_aliasing)
|
||||
UVM_TEST_SEQ = ${TEST_PREFIX}_common_vseq
|
||||
RUN_OPTS += +csr_aliasing
|
||||
RUN_OPTS += +en_scb=0
|
||||
endif
|
||||
|
||||
ifeq (${TEST_NAME},${TEST_PREFIX}_same_csr_outstanding)
|
||||
UVM_TEST_SEQ = ${TEST_PREFIX}_common_vseq
|
||||
RUN_OPTS += +run_same_csr_outstanding
|
||||
RUN_OPTS += +en_scb=0
|
||||
endif
|
||||
|
||||
# make sure DUT has memory and support this seq before run the test
|
||||
ifeq (${TEST_NAME},${TEST_PREFIX}_csr_mem_walk)
|
||||
UVM_TEST_SEQ = ${TEST_PREFIX}_common_vseq
|
||||
RUN_OPTS += +csr_mem_walk
|
||||
RUN_OPTS += +en_scb=0
|
||||
endif
|
||||
|
||||
ifeq (${TEST_NAME},${TEST_PREFIX}_tl_errors)
|
||||
UVM_TEST_SEQ = ${TEST_PREFIX}_common_vseq
|
||||
RUN_OPTS += +run_tl_errors
|
||||
endif
|
||||
|
||||
ifeq (${TEST_NAME},${TEST_PREFIX}_intr_test)
|
||||
UVM_TEST_SEQ = ${TEST_PREFIX}_common_vseq
|
||||
RUN_OPTS += +run_intr_test
|
||||
endif
|
||||
|
||||
ifeq (${TEST_NAME},${TEST_PREFIX}_stress_all_with_rand_reset)
|
||||
UVM_TEST_SEQ = ${TEST_PREFIX}_common_vseq
|
||||
RUN_OPTS += +run_stress_all_with_rand_reset
|
||||
// 10ms
|
||||
RUN_OPTS += +test_timeout_ns=10000000000
|
||||
RUN_OPTS += +stress_seq=${TEST_PREFIX}_stress_all_vseq
|
||||
endif
|
||||
|
||||
|
|
@ -6,12 +6,12 @@
|
|||
dv_root: "{proj_root}/vendor/lowrisc_ip/dv"
|
||||
|
||||
flow: sim
|
||||
flow_makefile: "{dv_root}/data/sim.mk"
|
||||
flow_makefile: "{dv_root}/tools/dvsim/sim.mk"
|
||||
|
||||
import_cfgs: ["{proj_root}/dv/uvm/common_project_cfg.hjson",
|
||||
"{dv_root}/data/common_modes.hjson",
|
||||
"{dv_root}/data/fusesoc.hjson",
|
||||
"{dv_root}/data/{tool}/{tool}.hjson"]
|
||||
"{dv_root}/tools/dvsim/common_modes.hjson",
|
||||
"{dv_root}/tools/dvsim/fusesoc.hjson",
|
||||
"{dv_root}/tools/dvsim/{tool}.hjson"]
|
||||
|
||||
// Default directory structure for the output
|
||||
build_dir: "{scratch_path}/{build_mode}"
|
||||
|
@ -37,11 +37,15 @@
|
|||
tl_dbw: 4
|
||||
|
||||
// Default UVM verbosity settings
|
||||
n: UVM_NONE
|
||||
l: UVM_LOW
|
||||
m: UVM_MEDIUM
|
||||
h: UVM_HIGH
|
||||
d: UVM_DEBUG
|
||||
expand_uvm_verbosity_n: UVM_NONE
|
||||
expand_uvm_verbosity_l: UVM_LOW
|
||||
expand_uvm_verbosity_m: UVM_MEDIUM
|
||||
expand_uvm_verbosity_h: UVM_HIGH
|
||||
expand_uvm_verbosity_d: UVM_DEBUG
|
||||
|
||||
// Default simulation verbosity (l => UVM_LOW). Can be overridden by
|
||||
// the --verbosity command-line argument.
|
||||
verbosity: l
|
||||
|
||||
// Path to the dut instance (this is used in a couple of places such as coverage cfg
|
||||
// file, xprop cfg file etc. If this is different for your block, then override it with
|
||||
|
@ -63,43 +67,38 @@
|
|||
"+define+UVM_REG_BYTENABLE_WIDTH={tl_dbw}"]
|
||||
|
||||
run_opts: ["+UVM_NO_RELNOTES",
|
||||
"+UVM_VERBOSITY={verbosity}"]
|
||||
"+UVM_VERBOSITY={expand_uvm_verbosity_{verbosity}}"]
|
||||
|
||||
// Default list of things to export to shell
|
||||
exports: [
|
||||
TOOL_SRCS_DIR: {tool_srcs_dir}
|
||||
EN_WAVES: {waves}
|
||||
DUMP_FMT: {dump_fmt}
|
||||
DUT_TOP: {dut}
|
||||
TB_TOP: {tb}
|
||||
dut_instance: {dut_instance}
|
||||
{ TOOL_SRCS_DIR: "{tool_srcs_dir}" },
|
||||
{ SIMULATOR: "{tool}" },
|
||||
{ WAVES: "{waves}" },
|
||||
{ DUT_TOP: "{dut}" },
|
||||
{ TB_TOP: "{tb}" },
|
||||
{ dut_instance: "{dut_instance}" }
|
||||
]
|
||||
|
||||
// Build modes are collection of build_opts and run_opts
|
||||
// A test can enable a specific build mode by setting 'use_build_mode' key
|
||||
build_modes: [
|
||||
{
|
||||
name: foo
|
||||
build_opts: ["+define+bx",
|
||||
"+define+by",
|
||||
"+define+bz"]
|
||||
run_opts: ["+rx=1",
|
||||
"+ry=2",
|
||||
"+rz=3"]
|
||||
}
|
||||
{
|
||||
name: bar
|
||||
build_opts: ["+define+bbaru1",
|
||||
"+define+bbaru2",
|
||||
"+define+bbaru3"]
|
||||
run_opts: ["+rbar1u=1",
|
||||
"+rbar2u=2",
|
||||
"+rbar3u=3"]
|
||||
}
|
||||
{
|
||||
name: cover_reg_top
|
||||
}
|
||||
]
|
||||
//
|
||||
// To define a build mode that overrides these flags, add something
|
||||
// like the following to the IP block's configuration:
|
||||
//
|
||||
// build_modes: [
|
||||
// {
|
||||
// name: foo
|
||||
// build_opts: ["+define+bx",
|
||||
// "+define+by",
|
||||
// "+define+bz"]
|
||||
// run_opts: ["+rx=1",
|
||||
// "+ry=2",
|
||||
// "+rz=3"]
|
||||
// }
|
||||
// ]
|
||||
//
|
||||
// To use a build mode for a specific test, set the 'use_build_mode' key.
|
||||
//
|
||||
build_modes: []
|
||||
|
||||
// Regressions are tests that can be grouped together and run in one shot
|
||||
// By default, two regressions are made available - "all" and "nightly". Both
|
||||
|
@ -110,7 +109,7 @@
|
|||
// tests.
|
||||
regressions: [
|
||||
{
|
||||
name: sanity
|
||||
name: smoke
|
||||
tests: []
|
||||
reseed: 1
|
||||
}
|
||||
|
@ -133,7 +132,9 @@
|
|||
// Add waves.tcl to the set of sources to be copied over to
|
||||
// {tool_srcs_dir}. This can be sourced by the tool-specific TCL
|
||||
// script to set up wave dumping.
|
||||
tool_srcs: ["{dv_root}/tools/waves.tcl"],
|
||||
tool_srcs: ["{dv_root}/tools/sim.tcl",
|
||||
"{dv_root}/tools/common.tcl",
|
||||
"{dv_root}/tools/waves.tcl"]
|
||||
|
||||
// Project defaults for VCS
|
||||
vcs_cov_cfg_file: "{{build_mode}_vcs_cov_cfg_file}"
|
|
@ -5,8 +5,13 @@
|
|||
build_cmd: "{job_prefix} dsim"
|
||||
run_cmd: "{job_prefix} dsim"
|
||||
|
||||
build_opts: [
|
||||
"-work {build_dir}/dsim_out",
|
||||
// Indicate the tool specific helper sources - these are copied over to the
|
||||
// {tool_srcs_dir} before running the simulation.
|
||||
// TODO, there is no dsim tool file, point to vcs for now to avoid error from script
|
||||
// tool_srcs: ["{dv_root}/tools/dsim/*"]
|
||||
|
||||
|
||||
build_opts: ["-work {build_dir}/dsim_out",
|
||||
"-genimage image",
|
||||
"-sv",
|
||||
// Set parallel compilation jobs limit
|
||||
|
@ -21,24 +26,23 @@
|
|||
"+incdir+{build_dir}",
|
||||
// Suppress following DSim errors and warnings:
|
||||
// EnumMustBePositive - UVM 1.2 violates this
|
||||
"-suppress EnumMustBePositive"
|
||||
]
|
||||
"-suppress EnumMustBePositive"]
|
||||
|
||||
run_opts: [
|
||||
"-work {build_dir}/dsim_out",
|
||||
run_opts: ["-work {build_dir}/dsim_out",
|
||||
"-image image",
|
||||
// UVM DPI
|
||||
"-sv_lib {DSIM_HOME}/lib/libuvm_dpi.so",
|
||||
"-sv_seed {seed}",
|
||||
// tell DSim to write line-buffered std output (lines will be written in proper order)
|
||||
// tell DSim to write line-buffered stdout (lines will be written in proper order)
|
||||
"-linebuf",
|
||||
"+UVM_TESTNAME={uvm_test}",
|
||||
"+UVM_TEST_SEQ={uvm_test_seq}"]
|
||||
|
||||
// Indicate the tool specific helper sources - these are copied over to the
|
||||
// {tool_srcs_dir} before running the simulation.
|
||||
// TODO, there is no dsim tool file, point to vcs for now to avoid error from script
|
||||
tool_srcs: ["{dv_root}/tools/vcs/*"]
|
||||
// Supported wave dumping formats (in order of preference).
|
||||
supported_wave_formats: ["vcd", "fst"]
|
||||
|
||||
// Default tcl script used when running the sim. Override if needed.
|
||||
run_script: ""
|
||||
|
||||
// TODO: refactor coverage configuration for DSim.
|
||||
|
||||
|
@ -56,13 +60,12 @@
|
|||
cov_merge_dir: "{scratch_base_path}/cov_merge"
|
||||
cov_merge_db_dir: "{cov_merge_dir}/merged.vdb"
|
||||
cov_merge_cmd: "{job_prefix} urg"
|
||||
cov_merge_opts: [
|
||||
]
|
||||
cov_merge_opts: []
|
||||
|
||||
// Generate coverage reports in text as well as html.
|
||||
cov_report_dir: "{scratch_base_path}/cov_report"
|
||||
cov_report_cmd: "{job_prefix} urg"
|
||||
cov_report_opts: [
|
||||
]
|
||||
cov_report_opts: []
|
||||
cov_report_txt: "{cov_report_dir}/dashboard.txt"
|
||||
cov_report_page: "dashboard.html"
|
||||
|
||||
|
@ -72,40 +75,36 @@
|
|||
cov_analyze_cmd: "{job_prefix} verdi"
|
||||
cov_analyze_opts: ["-cov",
|
||||
"-covdir {cov_merge_db_dir}",
|
||||
"-line nocasedef"
|
||||
"-line nocasedef",
|
||||
"-elfile {vcs_cov_excl_files}"]
|
||||
|
||||
// Vars that need to exported to the env.
|
||||
exports: [
|
||||
{ PATH: "{DSIM_HOME}:{PATH}" }
|
||||
{ LD_LIBRARY_PATH: "{DSIM_HOME}/lib:{DSIM_HOME}/system_lib:{LD_LIBRARY_PATH}" }
|
||||
]
|
||||
|
||||
// Defaults for DSim
|
||||
// TODO: there is currently no equivalent of "all" coverage metrics in DSim
|
||||
cov_metrics: ""
|
||||
cov_metrics: ""
|
||||
|
||||
// pass and fail patterns
|
||||
build_fail_patterns: ["^Error-.*$"]
|
||||
run_fail_patterns: ["^Error-.*$"] // Null pointer error
|
||||
|
||||
// waveform
|
||||
wave_type: "vcd"
|
||||
wave_file: "dsim_wave.{wave_type}"
|
||||
probe_file: "dsim.probe"
|
||||
probe_file: "dsim.probe"
|
||||
|
||||
build_modes: [
|
||||
{
|
||||
name: dsim_waves
|
||||
is_sim_mode: 1
|
||||
build_opts: [
|
||||
"+acc+b"
|
||||
]
|
||||
run_opts: [
|
||||
"-waves {wave_file}",
|
||||
// dsim.probe is currently undefined
|
||||
//"-wave-scope-specs {probe_file}",
|
||||
// Dump unpacked structs and arrays.
|
||||
"-dump-agg"
|
||||
]
|
||||
build_opts: ["+acc+b"]
|
||||
run_opts: ["-waves waves.{waves}",
|
||||
// dsim.probe is currently undefined
|
||||
//"-wave-scope-specs {probe_file}",
|
||||
// Dump unpacked structs and arrays.
|
||||
"-dump-agg"]
|
||||
}
|
||||
// TODO: support coverage mode
|
||||
// Note: no specific build or run options are required for dsim to produce functional
|
|
@ -7,7 +7,7 @@
|
|||
|
||||
// Indicate the tool specific helper sources - these are copied over to the
|
||||
// {tool_srcs_dir} before running the simulation.
|
||||
tool_srcs: ["{proj_root}/dv/uvm/data/riviera/*"]
|
||||
tool_srcs: ["{dv_root}/tools/riviera/*"]
|
||||
|
||||
build_opts: ["-sv",
|
||||
"-timescale 1ns/1ps",
|
||||
|
@ -20,7 +20,14 @@
|
|||
"-lib {sv_flist_gen_dir}/work",
|
||||
"+UVM_TESTNAME={uvm_test}",
|
||||
"+UVM_TEST_SEQ={uvm_test_seq}",
|
||||
"-do {tool_srcs_dir}/riviera_run.do"]
|
||||
"-do {run_script}"]
|
||||
|
||||
|
||||
// Supported wave dumping formats (in order of preference).
|
||||
supported_wave_formats: []
|
||||
|
||||
// Default tcl script used when running the sim. Override if needed.
|
||||
run_script: "{tool_srcs_dir}/riviera_run.do"
|
||||
|
||||
// Coverage related.
|
||||
// TODO: These options have to be filled in.
|
|
@ -19,11 +19,11 @@ prep_tool_srcs:
|
|||
mkdir -p ${tool_srcs_dir}
|
||||
${LOCK_TOOL_SRCS_DIR} "cp -Ru ${tool_srcs} ${tool_srcs_dir}/."
|
||||
|
||||
pre_compile:
|
||||
pre_compile: prep_tool_srcs
|
||||
@echo "[make]: pre_compile"
|
||||
mkdir -p ${build_dir}
|
||||
|
||||
gen_sv_flist: pre_compile prep_tool_srcs
|
||||
gen_sv_flist: pre_compile
|
||||
@echo "[make]: gen_sv_flist"
|
||||
cd ${build_dir} && ${sv_flist_gen_cmd} ${sv_flist_gen_opts}
|
||||
|
||||
|
@ -39,7 +39,7 @@ compile_result: post_compile
|
|||
|
||||
run: run_result
|
||||
|
||||
pre_run:
|
||||
pre_run: prep_tool_srcs
|
||||
@echo "[make]: pre_run"
|
||||
mkdir -p ${run_dir}
|
||||
ifneq (${sw_test},)
|
||||
|
@ -62,11 +62,13 @@ ifneq (${sw_test},)
|
|||
-n "rom" -o "${run_dir}"
|
||||
# Copy over the boot rom image to the run_dir.
|
||||
cp ${sw_build_dir}/build-out/sw/device/boot_rom/boot_rom_${sw_build_device}.32.vmem \
|
||||
${run_dir}/rom.32.vmem
|
||||
${run_dir}/rom.vmem
|
||||
cp ${sw_build_dir}/build-out/sw/device/boot_rom/boot_rom_${sw_build_device}.elf \
|
||||
${run_dir}/rom.elf
|
||||
|
||||
ifeq (${sw_test_is_prebuilt},1)
|
||||
# Copy over the sw test image and related sources to the run_dir.
|
||||
cp ${proj_root}/${sw_test}.64.vmem ${run_dir}/sw.64.vmem
|
||||
cp ${proj_root}/${sw_test}.64.vmem ${run_dir}/sw.vmem
|
||||
# Optionally, assume that ${sw_test}_logs.txt exists and copy over to the run_dir.
|
||||
# Ignore copy error if it actually doesn't exist. Likewise for ${sw_test}_rodata.txt.
|
||||
-cp ${proj_root}/${sw_test}_logs.txt ${run_dir}/sw_logs.txt
|
||||
|
@ -76,14 +78,23 @@ else
|
|||
# Compile the sw test code and generate the image.
|
||||
${LOCK_SW_BUILD} "ninja -C ${sw_build_dir}/build-out \
|
||||
${sw_test}_export_${sw_build_device}"
|
||||
# Convert sw image to frame format
|
||||
# TODO only needed for loading sw image through SPI. Can enhance this later
|
||||
${LOCK_SW_BUILD} "ninja -C ${sw_build_dir}/build-out sw/host/spiflash/spiflash_export"
|
||||
${LOCK_SW_BUILD} "${sw_build_dir}/build-bin/sw/host/spiflash/spiflash --input \
|
||||
${sw_build_dir}/build-bin/${sw_test}_${sw_build_device}.bin \
|
||||
--dump-frames=${run_dir}/sw.frames.bin"
|
||||
${LOCK_SW_BUILD} "srec_cat ${run_dir}/sw.frames.bin --binary \
|
||||
--offset 0x0 --byte-swap 4 --fill 0xff -within ${run_dir}/sw.frames.bin -binary -range-pad 4 \
|
||||
--output ${run_dir}/sw.frames.vmem --vmem"
|
||||
# Extract the sw test logs.
|
||||
${proj_root}/util/device_sw_utils/extract_sw_logs.py \
|
||||
-e "${sw_build_dir}/build-out/${sw_test}_${sw_build_device}.elf" \
|
||||
-f .logs.fields -r .rodata \
|
||||
-n "sw" -o "${run_dir}"
|
||||
# Copy over the sw test image to the run_dir.
|
||||
cp ${sw_build_dir}/build-out/${sw_test}_${sw_build_device}.64.vmem \
|
||||
${run_dir}/sw.64.vmem
|
||||
cp ${sw_build_dir}/build-out/${sw_test}_${sw_build_device}.64.vmem ${run_dir}/sw.vmem
|
||||
cp ${sw_build_dir}/build-out/${sw_test}_${sw_build_device}.elf ${run_dir}/sw.elf
|
||||
endif
|
||||
|
||||
endif
|
57
vendor/lowrisc_ip/dv/tools/dvsim/testplans/tl_device_access_types_testplan.hjson
vendored
Normal file
57
vendor/lowrisc_ip/dv/tools/dvsim/testplans/tl_device_access_types_testplan.hjson
vendored
Normal file
|
@ -0,0 +1,57 @@
|
|||
// Copyright lowRISC contributors.
|
||||
// Licensed under the Apache License, Version 2.0, see LICENSE for details.
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
{
|
||||
entries: [
|
||||
{
|
||||
name: tl_d_oob_addr_access
|
||||
desc: "Access out of bounds address and verify correctness of response / behavior"
|
||||
milestone: V2
|
||||
tests: ["{name}_tl_errors"]
|
||||
}
|
||||
{
|
||||
name: tl_d_illegal_access
|
||||
desc: '''Drive unsupported requests via TL interface and verify correctness of response
|
||||
/ behavior. Below error cases are tested
|
||||
- TL-UL protocol error cases
|
||||
- Unsupported opcode. e.g a_opcode isn't Get, PutPartialData or PutFullData
|
||||
- Mask isn't all active if opcode = PutFullData
|
||||
- Mask isn't in enabled lanes, e.g. a_address = 0x00, a_size = 0, a_mask = 'b0010
|
||||
- Mask doesn't align with address, e.g. a_address = 0x01, a_mask = 'b0001
|
||||
- Address and size aren't aligned, e.g. a_address = 0x01, a_size != 0
|
||||
- Size is over 2.
|
||||
- OpenTitan defined error cases
|
||||
- Access unmapped address, return d_error = 1 when devmode_i == 1
|
||||
- Write CSR with unaligned address, e.g. a_address[1:0] != 0
|
||||
- Write CSR less than its width, e.g. when CSR is 2 bytes wide, only write 1 byte
|
||||
- Write a memory without enabling all lanes (a_mask = '1) if memory doesn't support
|
||||
byte enabled write
|
||||
- Read a WO (write-only) memory'''
|
||||
milestone: V2
|
||||
tests: ["{name}_tl_errors"]
|
||||
}
|
||||
{
|
||||
name: tl_d_outstanding_access
|
||||
desc: '''Drive back-to-back requests without waiting for response to ensure there is one
|
||||
transaction outstanding within the TL device. Also, verify one outstanding when back-
|
||||
to-back accesses are made to the same address.'''
|
||||
milestone: V2
|
||||
tests: ["{name}_csr_hw_reset",
|
||||
"{name}_csr_rw",
|
||||
"{name}_csr_aliasing",
|
||||
"{name}_same_csr_outstanding"]
|
||||
}
|
||||
{
|
||||
name: tl_d_partial_access
|
||||
desc: '''Access CSR with one or more bytes of data
|
||||
For read, expect to return all word value of the CSR
|
||||
For write, enabling bytes should cover all CSR valid fields'''
|
||||
milestone: V2
|
||||
tests: ["{name}_csr_hw_reset",
|
||||
"{name}_csr_rw",
|
||||
"{name}_csr_aliasing",
|
||||
"{name}_same_csr_outstanding"]
|
||||
}
|
||||
]
|
||||
}
|
||||
|
|
@ -62,7 +62,7 @@
|
|||
|
||||
regressions: [
|
||||
{
|
||||
name: sanity
|
||||
name: smoke
|
||||
tests: ["{name}_csr_hw_reset", "{name}_csr_rw"]
|
||||
}
|
||||
|
|
@ -25,22 +25,74 @@
|
|||
// and final value of the selection input at the end of a simulation timestep.
|
||||
// See https://github.com/lowRISC/ibex/issues/845.
|
||||
"-xlrm uniq_prior_final",
|
||||
// Force DPI-C compilation in C99 mode
|
||||
"-CFLAGS \"--std=c99\"",
|
||||
// Force DPI-C compilation in C99 mode. The -fno-extended-identifiers flag tells g++
|
||||
// not to worry about unicode. For some bizarre reason, the VCS DPI code contains
|
||||
// preprocessor macros with smart quotes, which causes GCC 10.2 and later to choke
|
||||
// otherwise. The double-escaped quotes are because this needs to go inside a string
|
||||
// that gets passed to Make (stripping one level of quotes), and then needs to
|
||||
// result in an argument with an embedded space (the other one).
|
||||
"-CFLAGS \\\"--std=c99 -fno-extended-identifiers\\\"",
|
||||
// C++11 standard is enforced for all sources, including the DPI-C constructs.
|
||||
// TODO, may need to update to c++14 to meet our requirements
|
||||
// Refer to https://docs.opentitan.org/doc/ug/install_instructions
|
||||
"-CFLAGS --std=c++11",
|
||||
// Without this magic LDFLAGS argument below, we get compile time errors with
|
||||
// VCS on Google Linux machines that look like this:
|
||||
// .../libvcsnew.so: undefined reference to `snpsReallocFunc'
|
||||
// .../libvcsnew.so: undefined reference to `snpsCheckStrdupFunc'
|
||||
// .../libvcsnew.so: undefined reference to `snpsGetMemBytes'
|
||||
"-LDFLAGS \"-Wl,--no-as-needed\"",
|
||||
"-LDFLAGS -Wl,--no-as-needed",
|
||||
// This option enables the following: (needed for uvm_hdl_*)
|
||||
// - Read capability on registers, variables, and nets
|
||||
// - Write (deposit) capability on registers and variables
|
||||
// - Force capability on registers, variables, and nets
|
||||
"-debug_access+f"]
|
||||
"-debug_access+f",
|
||||
// Use this to conditionally compile for VCS (example: LRM interpretations differ
|
||||
// across tools).
|
||||
"+define+VCS",
|
||||
// Upgrade below warnings to errors to make VCS more strict on syntax to avoid
|
||||
// having issue with other simulators but passing with VCS
|
||||
//
|
||||
// Identifier previously declared
|
||||
"-error=IPDW",
|
||||
// Input Supply Port with no driver
|
||||
"-error=UPF_ISPND",
|
||||
// Invalid generic or parameter assignment
|
||||
"-error=IGPA",
|
||||
// Class scope used outside of class
|
||||
"-error=PCSRMIO",
|
||||
// Attempt to override undefined parameter
|
||||
"-error=AOUP",
|
||||
// Unbound component
|
||||
"-error=ELW_UNBOUND",
|
||||
// Illegal Use of Wildcard Index
|
||||
"-error=IUWI",
|
||||
// Index into non-array variable
|
||||
"-error=INAV",
|
||||
// Illegal Static Cast
|
||||
"-error=SV-ISC",
|
||||
// Obsolete System Verilog feature
|
||||
"-error=OSVF-NPVIUFPI",
|
||||
// Duplicate port in module instantiation
|
||||
"-error=DPIMI",
|
||||
// Identifier in ANSI port declaration
|
||||
"-error=IPDASP",
|
||||
// File not found
|
||||
"-error=CM-HIER-FNF",
|
||||
// Concatenations with unsized constants
|
||||
"-error=CWUC",
|
||||
// More arguments than needed
|
||||
"-error=MATN",
|
||||
// Specifying negative delays is invalid
|
||||
"-error=STASKW_NDTAZ1",
|
||||
// Too many parameter overrides
|
||||
"-error=TMPO",
|
||||
// Class objects must not hide other class members due to same name
|
||||
"-error=SV-OHCM",
|
||||
]
|
||||
|
||||
run_opts: ["-licqueue",
|
||||
"-ucli -do {tool_srcs_dir}/vcs.tcl",
|
||||
"-ucli -do {run_script}",
|
||||
"+ntb_random_seed={seed}",
|
||||
// Disable the display of the SystemVerilog assert and cover statement summary
|
||||
// at the end of simulation. This summary is list of assertions that started but
|
||||
|
@ -51,6 +103,12 @@
|
|||
"+UVM_TESTNAME={uvm_test}",
|
||||
"+UVM_TEST_SEQ={uvm_test_seq}"]
|
||||
|
||||
// Supported wave dumping formats (in order of preference).
|
||||
supported_wave_formats: ["fsdb", "vpd"]
|
||||
|
||||
// Default tcl script used when running the sim. Override if needed.
|
||||
run_script: "{tool_srcs_dir}/sim.tcl"
|
||||
|
||||
// Coverage related.
|
||||
cov_db_dir: "{scratch_path}/coverage/{build_mode}.vdb"
|
||||
|
||||
|
@ -105,8 +163,8 @@
|
|||
|
||||
// Vars that need to exported to the env.
|
||||
exports: [
|
||||
VCS_ARCH_OVERRIDE: linux
|
||||
VCS_LIC_EXPIRE_WARNING: 1
|
||||
{ VCS_ARCH_OVERRIDE: "linux" },
|
||||
{ VCS_LIC_EXPIRE_WARNING: 1 }
|
||||
]
|
||||
|
||||
// Defaults for VCS
|
|
@ -20,33 +20,51 @@
|
|||
"-xmlibdirname {build_dir}/xcelium.d",
|
||||
// for uvm_hdl_* used by csr backdoor
|
||||
"-access +rw",
|
||||
// Use this to conditionally compile for Xcelium (example: LRM interpretations differ
|
||||
// across tools).
|
||||
"+define+XCELIUM",
|
||||
// Ignore "timescale is not specified for the package" warning
|
||||
"-nowarn TSNSPK",
|
||||
// Ignore "IEEE 1800-2009 SystemVerilog simulation semantics" warning
|
||||
"-nowarn DSEMEL",
|
||||
// Ignore hierarchial ref warnings in interfaces
|
||||
"-nowarn CUVIHR",
|
||||
]
|
||||
|
||||
run_opts: ["-input {tool_srcs_dir}/xcelium.tcl",
|
||||
run_opts: ["-input {run_script}",
|
||||
"-licqueue",
|
||||
"-64bit -xmlibdirname {build_dir}/xcelium.d -R",
|
||||
"+SVSEED={seed}",
|
||||
"+UVM_TESTNAME={uvm_test}",
|
||||
"+UVM_TEST_SEQ={uvm_test_seq}"]
|
||||
"+UVM_TEST_SEQ={uvm_test_seq}",
|
||||
// Ignore "IEEE 1800-2009 SystemVerilog simulation semantics" warning
|
||||
"-nowarn DSEM2009",
|
||||
]
|
||||
|
||||
// Vars that need to exported to the env.
|
||||
exports: [
|
||||
// Poll for an available license in all servers.
|
||||
CDS_LIC_QUEUE_POLL: 1
|
||||
{ CDS_LIC_QUEUE_POLL: 1 },
|
||||
|
||||
// Poll for an available license every 1 min.
|
||||
CDS_LIC_QUEUE_POLL_INT: 60
|
||||
{ CDS_LIC_QUEUE_POLL_INT: 60 },
|
||||
|
||||
// X-prop related: these were suggested by Xcelium as warnings during the build time.
|
||||
// These enable array corruption when the index is out of range or invalid.
|
||||
VL_ENABLE_INVALID_IDX_XPROP: 1
|
||||
VL_ENABLE_OUTOFRANGE_IDX_XPROP: 1
|
||||
{ VL_ENABLE_INVALID_IDX_XPROP: 1 },
|
||||
{ VL_ENABLE_OUTOFRANGE_IDX_XPROP: 1 },
|
||||
|
||||
// Export the cov_report path so that the tcl file can read these as env vars.
|
||||
cov_merge_db_dir: "{cov_merge_db_dir}"
|
||||
cov_report_dir: "{cov_report_dir}"
|
||||
{ cov_merge_db_dir: "{cov_merge_db_dir}" },
|
||||
{ cov_report_dir: "{cov_report_dir}" }
|
||||
]
|
||||
|
||||
// Supported wave dumping formats (in order of preference).
|
||||
supported_wave_formats: ["shm", "fsdb", "vcd"]
|
||||
|
||||
// Default tcl script used when running the sim. Override if needed.
|
||||
run_script: "{tool_srcs_dir}/sim.tcl"
|
||||
|
||||
// Coverage related.
|
||||
// By default, collect all coverage metrics: block:expr:fsm:toggle:functional.
|
||||
cov_metrics: all
|
||||
|
@ -57,7 +75,7 @@
|
|||
|
||||
// Supply the cov refinement files.
|
||||
// Note that this needs to be set as -load_refinement <file>.
|
||||
xcelium_cov_refine_files: [""]
|
||||
xcelium_cov_refine_files: []
|
||||
|
||||
// Set the coverage directories.
|
||||
cov_work_dir: "{scratch_path}/coverage"
|
3
vendor/lowrisc_ip/dv/tools/fail_patterns
vendored
3
vendor/lowrisc_ip/dv/tools/fail_patterns
vendored
|
@ -1,3 +0,0 @@
|
|||
^TEST FAILED (UVM_)?CHECKS$
|
||||
^UVM_ERROR\s[^:].*$
|
||||
^\s*Offending '.*'
|
16
vendor/lowrisc_ip/dv/tools/fusesoc.mk
vendored
16
vendor/lowrisc_ip/dv/tools/fusesoc.mk
vendored
|
@ -1,16 +0,0 @@
|
|||
# Copyright lowRISC contributors.
|
||||
# Licensed under the Apache License, Version 2.0, see LICENSE for details.
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
# Make variables specific to FUSESOC tool used for generating the filelist
|
||||
# The following Make variables are to be set in the Test Makefile
|
||||
# FUSESOC_CORE: the top level fusesoc core file developed for the ip/top level testbench
|
||||
# see hw/ip/uart/dv/uart_sim.core as an example
|
||||
# Rest of the Make variables added here are intermeditiate ones used in the flow
|
||||
|
||||
# fusesoc tool and options
|
||||
SV_FLIST_GEN_TOOL ?= fusesoc
|
||||
SV_FLIST_GEN_OPTS += --cores-root ${PROJ_ROOT} --cores-root ${RAL_MODEL_DIR} \
|
||||
run --target=sim --setup ${FUSESOC_CORE}
|
||||
FUSESOC_CORE_ = $(shell echo "${FUSESOC_CORE}" | tr ':' '_')
|
||||
SV_FLIST_GEN_DIR = ${BUILD_DIR}/build/${FUSESOC_CORE_}/sim-vcs
|
||||
SV_FLIST := ${SV_FLIST_GEN_DIR}/${FUSESOC_CORE_}.scr
|
58
vendor/lowrisc_ip/dv/tools/modes.mk
vendored
58
vendor/lowrisc_ip/dv/tools/modes.mk
vendored
|
@ -1,58 +0,0 @@
|
|||
# Copyright lowRISC contributors.
|
||||
# Licensed under the Apache License, Version 2.0, see LICENSE for details.
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
# Makefile option groups that can be enabled by test Makefile / command line.
|
||||
# These are generic set of option groups that apply to all testbenches.
|
||||
# These are meant to be simulator agnostic
|
||||
# Please add tool specific options with appropriate ifeq's
|
||||
|
||||
# Distinguish UVM TB and the other environments for Verilator, FPGA etc
|
||||
BUILD_OPTS += +define+UVM
|
||||
# uvm specific - set default widths
|
||||
BUILD_OPTS += +define+UVM_NO_DEPRECATED
|
||||
BUILD_OPTS += +define+UVM_REGEX_NO_DPI
|
||||
BUILD_OPTS += +define+UVM_REG_ADDR_WIDTH=${TL_AW}
|
||||
BUILD_OPTS += +define+UVM_REG_DATA_WIDTH=${TL_DW}
|
||||
BUILD_OPTS += +define+UVM_REG_BYTENABLE_WIDTH=${TL_DBW}
|
||||
|
||||
# Enable UVM trace options
|
||||
UVM_TRACE ?= 0
|
||||
ifeq (${UVM_TRACE},1)
|
||||
RUN_OPTS += +UVM_PHASE_TRACE
|
||||
RUN_OPTS += +UVM_CONFIG_DB_TRACE
|
||||
RUN_OPTS += +UVM_OBJECTION_TRACE
|
||||
endif
|
||||
|
||||
# Options for generating waves / debugging.
|
||||
WAVES ?= 0
|
||||
DUMP ?= fsdb
|
||||
DUMP_FILE ?= waves.${DUMP}
|
||||
export WAVES
|
||||
export DUMP
|
||||
export DUMP_FILE
|
||||
|
||||
ifeq (${WAVES},1)
|
||||
ifeq (${SIMULATOR},vcs)
|
||||
VCS_WAVES = 1
|
||||
endif
|
||||
endif
|
||||
|
||||
# Enable simulation profiling
|
||||
SIMPROFILE ?= 0
|
||||
ifeq (${SIMPROFILE},1)
|
||||
ifeq (${SIMULATOR},vcs)
|
||||
VCS_SIMPROFILE = 1
|
||||
else ifeq (${SIMULATOR},xcelium)
|
||||
XCELIUM_SIMPROFILE = 1
|
||||
endif
|
||||
endif
|
||||
|
||||
# Enable coverage
|
||||
COV ?= 0
|
||||
ifeq (${COV},1)
|
||||
ifeq (${SIMULATOR},vcs)
|
||||
VCS_COV = 1
|
||||
else ifeq (${SIMULATOR},xcelium)
|
||||
XCELIUM_COV = 1
|
||||
endif
|
||||
endif
|
34
vendor/lowrisc_ip/dv/tools/pass_fail
vendored
34
vendor/lowrisc_ip/dv/tools/pass_fail
vendored
|
@ -1,34 +0,0 @@
|
|||
#!/usr/bin/env bash
|
||||
|
||||
# Copyright lowRISC contributors.
|
||||
# Licensed under the Apache License, Version 2.0, see LICENSE for details.
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
# this script checks for test pass/fail signatures at the end of the test
|
||||
|
||||
usage="Error: usage: $0 [path-to-run-log] [path-to-pass-patterns] [path-to-fail-patterns]";
|
||||
# need exactly 1 args (path to the run.log)
|
||||
if [[ $# -ne 3 || ! -f $1 || ! -f $2 || ! -f $3 ]]; then
|
||||
echo $usage
|
||||
exit 1;
|
||||
fi
|
||||
|
||||
# none of the fail patterns should be present
|
||||
while ifs= read -r fail_pattern; do
|
||||
grep_cmd="grep -c -m 1 -E '$fail_pattern' $1";
|
||||
fail_pattern_found=$(eval $grep_cmd);
|
||||
if [[ $fail_pattern_found -ne 0 ]]; then
|
||||
echo "Fail pattern '$fail_pattern' found"
|
||||
exit 1
|
||||
fi
|
||||
done < $3
|
||||
|
||||
# all pass patterns should be present
|
||||
while ifs= read -r pass_pattern; do
|
||||
grep_cmd="grep -c -m 1 -E '$pass_pattern' $1";
|
||||
pass_pattern_found=$(eval $grep_cmd);
|
||||
if [[ $pass_pattern_found -eq 0 ]]; then
|
||||
echo "Pass pattern '$pass_pattern' not found"
|
||||
exit 1
|
||||
fi
|
||||
done < $2
|
1
vendor/lowrisc_ip/dv/tools/pass_patterns
vendored
1
vendor/lowrisc_ip/dv/tools/pass_patterns
vendored
|
@ -1 +0,0 @@
|
|||
^TEST PASSED (UVM_)?CHECKS$
|
97
vendor/lowrisc_ip/dv/tools/rules.mk
vendored
97
vendor/lowrisc_ip/dv/tools/rules.mk
vendored
|
@ -1,97 +0,0 @@
|
|||
# Copyright lowRISC contributors.
|
||||
# Licensed under the Apache License, Version 2.0, see LICENSE for details.
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
.DEFAULT_GOAL := all
|
||||
|
||||
all: build run
|
||||
|
||||
###############################
|
||||
## sim build and run targets ##
|
||||
###############################
|
||||
build: compile_result
|
||||
|
||||
pre_compile:
|
||||
mkdir -p ${BUILD_DIR} && \
|
||||
env > ${BUILD_DIR}/env_vars
|
||||
|
||||
gen_sv_flist: pre_compile
|
||||
cd ${BUILD_DIR} && ${SV_FLIST_GEN_TOOL} ${SV_FLIST_GEN_OPTS}
|
||||
|
||||
compile: gen_sv_flist
|
||||
cd ${SV_FLIST_GEN_DIR} && $(BUILD_JOB_OPTS) ${SIMCC} ${BUILD_OPTS} ${CL_BUILD_OPTS}
|
||||
|
||||
post_compile: compile
|
||||
|
||||
compile_result: post_compile
|
||||
|
||||
run: run_result
|
||||
|
||||
pre_run:
|
||||
rm -rf ${RUN_PATH}/latest
|
||||
mkdir -p ${RUN_DIR}
|
||||
ln -s ${RUN_DIR} ${RUN_PATH}/latest
|
||||
/bin/bash ${MAKE_ROOT}/run_dir_limiter ${RUN_PATH} ${RUN_DIR_LIMIT}
|
||||
env > ${RUN_DIR}/env_vars
|
||||
|
||||
sw_build: pre_run
|
||||
ifneq (${SW_NAME},)
|
||||
# NOTE: Pass -f, since we're going to be re-building everything every time,
|
||||
# anyways.
|
||||
cd $(PROJ_ROOT) && \
|
||||
BUILD_ROOT=$(SW_BUILD_DIR) $(PROJ_ROOT)/meson_init.sh -f
|
||||
# NOTE: We're using the fpga platform for now, because there is no
|
||||
# such thing as a DV platform yet (nor does any code do anything
|
||||
# special for DV yet).
|
||||
ninja -C $(SW_BUILD_DIR)/build-out sw/device/boot_rom/boot_rom_export_$(SW_BUILD_DEVICE)
|
||||
ninja -C $(SW_BUILD_DIR)/build-out sw/device/$(SW_DIR)/$(SW_NAME)_export_$(SW_BUILD_DEVICE)
|
||||
|
||||
mkdir -p $(SW_BUILD_DIR)/sw $(SW_BUILD_DIR)/rom
|
||||
cp $(SW_BUILD_DIR)/build-out/sw/device/boot_rom/boot_rom_$(SW_BUILD_DEVICE).vmem \
|
||||
$(SW_BUILD_DIR)/rom/rom.vmem
|
||||
cp $(SW_BUILD_DIR)/build-out/sw/device/$(SW_DIR)/$(SW_NAME)_$(SW_BUILD_DEVICE).vmem \
|
||||
$(SW_BUILD_DIR)/sw/sw.vmem
|
||||
endif
|
||||
|
||||
simulate: sw_build
|
||||
cd ${RUN_DIR} && $(RUN_JOB_OPTS) ${SIMX} ${RUN_OPTS} ${CL_RUN_OPTS}
|
||||
|
||||
post_run: simulate
|
||||
|
||||
run_result: post_run
|
||||
/bin/bash ${MAKE_ROOT}/pass_fail ${RUN_LOG} ${MAKE_ROOT}/pass_patterns ${MAKE_ROOT}/fail_patterns
|
||||
|
||||
############################
|
||||
## coverage rated targets ##
|
||||
############################
|
||||
cov_merge:
|
||||
# TODO: add script to merge coverage in scratch scope
|
||||
|
||||
# open coverage tool to review and create report or exclusion file
|
||||
cov_analyze:
|
||||
cd ${SCRATCH_PATH} && ${COV_ANALYZE_TOOL} ${COV_ANALYZE_OPTS}
|
||||
|
||||
# generate coverage report directly
|
||||
cov_report:
|
||||
cd ${SCRATCH_PATH} && ${COV_REPORT_TOOL} ${COV_REPORT_OPTS}
|
||||
|
||||
clean:
|
||||
rm -rf ${SCRATCH_PATH}/*
|
||||
|
||||
.PHONY: ral \
|
||||
build \
|
||||
run \
|
||||
reg \
|
||||
pre_compile \
|
||||
compile \
|
||||
post_compile \
|
||||
compile_result \
|
||||
sw_build \
|
||||
pre_run \
|
||||
simulate \
|
||||
post_run \
|
||||
run_result \
|
||||
cov_merge \
|
||||
cov_analyze \
|
||||
cov_report \
|
||||
clean
|
28
vendor/lowrisc_ip/dv/tools/run_dir_limiter
vendored
28
vendor/lowrisc_ip/dv/tools/run_dir_limiter
vendored
|
@ -1,28 +0,0 @@
|
|||
#!/usr/bin/env bash
|
||||
|
||||
# Copyright lowRISC contributors.
|
||||
# Licensed under the Apache License, Version 2.0, see LICENSE for details.
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
# run_dir_limiter does what the name suggests - often, when tests are run multiple times, the number
|
||||
# of run directories created might end up getting large. This script finds and deletes N oldest
|
||||
# directories in a given path P, where P and N are the arguments to this script respedtively
|
||||
|
||||
usage="Error: usage: $0 [path-to-run-dir] [num-run-dir-limit]";
|
||||
# need exactly 2 args, 1st needs to be the path and 2nd, a number
|
||||
if [[ $# -ne 2 || ! -d $1 || ! $2 =~ ^[0-9]+$ ]]; then
|
||||
echo $usage
|
||||
exit 1;
|
||||
fi
|
||||
|
||||
run_dir=$(realpath $1);
|
||||
find_cmd="find $run_dir -mindepth 1 -maxdepth 1 -type d";
|
||||
num_dirs=$($find_cmd | wc -l);
|
||||
num_rm_dirs=$(($num_dirs - $2));
|
||||
if [[ $num_rm_dirs -gt 0 ]]; then
|
||||
dirs=$($find_cmd -printf '%T+ %p\n' | sort | head -n $num_rm_dirs | awk '{print $2}');
|
||||
for dir in $dirs; do
|
||||
echo "Removing $dir..."
|
||||
/bin/rm -rf $dir
|
||||
done
|
||||
fi
|
21
vendor/lowrisc_ip/dv/tools/sim.tcl
vendored
Normal file
21
vendor/lowrisc_ip/dv/tools/sim.tcl
vendored
Normal file
|
@ -0,0 +1,21 @@
|
|||
# Copyright lowRISC contributors.
|
||||
# Licensed under the Apache License, Version 2.0, see LICENSE for details.
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
# Common TCL script invoked at run-time by the simulator.
|
||||
# VCS syntax: -ucli -do <this file>
|
||||
# Xcelium syntax: -input <this file>
|
||||
|
||||
set tool_srcs_dir ""
|
||||
if {[info exists ::env(TOOL_SRCS_DIR)]} {
|
||||
set tool_srcs_dir "$::env(TOOL_SRCS_DIR)"
|
||||
} else {
|
||||
puts "ERROR: Script run without TOOL_SRCS_DIR environment variable."
|
||||
quit
|
||||
}
|
||||
|
||||
source "${tool_srcs_dir}/common.tcl"
|
||||
source "${tool_srcs_dir}/waves.tcl"
|
||||
|
||||
run
|
||||
quit
|
|
@ -1,40 +0,0 @@
|
|||
// Copyright lowRISC contributors.
|
||||
// Licensed under the Apache License, Version 2.0, see LICENSE for details.
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
{
|
||||
entries: [
|
||||
{
|
||||
name: oob_addr_access
|
||||
desc: "Access out of bounds address and verify correctness of response / behavior"
|
||||
milestone: V2
|
||||
tests: ["{name}_tl_errors"]
|
||||
}
|
||||
{
|
||||
name: illegal_access
|
||||
desc: '''Drive unsupported requests via TL interface and verify correctness of response
|
||||
/ behavior '''
|
||||
milestone: V2
|
||||
tests: ["{name}_tl_errors"]
|
||||
}
|
||||
{
|
||||
name: outstanding_access
|
||||
desc: '''Drive back-to-back requests without waiting for response to ensure there is one
|
||||
transaction outstanding within the TL device. Also, verify one outstanding when back-
|
||||
to-back accesses are made to the same address.'''
|
||||
milestone: V2
|
||||
tests: ["{name}{intf}_csr_hw_reset",
|
||||
"{name}{intf}_csr_rw",
|
||||
"{name}{intf}_csr_aliasing",
|
||||
"{name}{intf}_same_csr_outstanding"]
|
||||
}
|
||||
{
|
||||
name: partial_access
|
||||
desc: '''Do partial accesses.'''
|
||||
milestone: V2
|
||||
tests: ["{name}{intf}_csr_hw_reset",
|
||||
"{name}{intf}_csr_rw",
|
||||
"{name}{intf}_csr_aliasing"]
|
||||
}
|
||||
]
|
||||
}
|
||||
|
117
vendor/lowrisc_ip/dv/tools/vcs/vcs.mk
vendored
117
vendor/lowrisc_ip/dv/tools/vcs/vcs.mk
vendored
|
@ -1,117 +0,0 @@
|
|||
# Copyright lowRISC contributors.
|
||||
# Licensed under the Apache License, Version 2.0, see LICENSE for details.
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
# Makefile option groups that can be enabled by test Makefile / command line.
|
||||
# These are generic set of option groups that apply to all testbenches.
|
||||
# Simulator too specific options
|
||||
# Mandatory items to set (these are used by rules.mk):
|
||||
# SIMCC - Simulator compiler used to build / elaborate the bench
|
||||
# SIMX - Simulator executable used to run the tests
|
||||
|
||||
SIMCC := vcs
|
||||
SIMX ?= ${BUILD_DIR}/simv
|
||||
SIM_SETUP ?= ${MAKE_ROOT}/vcs/vcs.tcl
|
||||
|
||||
# set standard build options
|
||||
BUILD_OPTS += -sverilog -full64 -licqueue -timescale=1ns/1ps -kdb
|
||||
BUILD_OPTS += -ntb_opts uvm-1.2
|
||||
BUILD_OPTS += -Mdir=${BUILD_DIR}/simv.csrc
|
||||
BUILD_OPTS += -l ${BUILD_LOG}
|
||||
BUILD_OPTS += -o ${SIMX}
|
||||
BUILD_OPTS += -f ${SV_FLIST}
|
||||
BUILD_OPTS += +incdir+${BUILD_DIR}
|
||||
BUILD_OPTS += -debug_access+pp
|
||||
BUILD_OPTS += +warn=noUII-L
|
||||
# turn on warnings if functions are called with return value ignored
|
||||
BUILD_OPTS += +warn=SV-NFIVC
|
||||
# option below is required for $error / $fatal system calls
|
||||
BUILD_OPTS += -assert svaext
|
||||
# Force DPI-C compilation in C99 mode
|
||||
BUILD_OPTS += -CFLAGS "--std=c99"
|
||||
# Without this magic LDFLAGS argument below, we get compile time errors with
|
||||
# VCS on Google Linux machines that look like this:
|
||||
# .../libvcsnew.so: undefined reference to `snpsReallocFunc'
|
||||
# .../libvcsnew.so: undefined reference to `snpsCheckStrdupFunc'
|
||||
# .../libvcsnew.so: undefined reference to `snpsGetMemBytes'
|
||||
BUILD_OPTS += -LDFLAGS "-Wl,--no-as-needed"
|
||||
|
||||
# set standard run options
|
||||
RUN_OPTS += -licqueue
|
||||
RUN_OPTS += -ucli -do ${SIM_SETUP}
|
||||
RUN_OPTS += +ntb_random_seed=${SEED}
|
||||
RUN_OPTS += +UVM_VERBOSITY=${UVM_VERBOSITY}
|
||||
RUN_OPTS += +UVM_TESTNAME=${UVM_TEST}
|
||||
RUN_OPTS += +UVM_TEST_SEQ=${UVM_TEST_SEQ}
|
||||
RUN_OPTS += -l ${RUN_LOG}
|
||||
|
||||
#########################
|
||||
## Tool Specific Modes ##
|
||||
#########################
|
||||
|
||||
# Enable simulation profiling
|
||||
VCS_SIMPROFILE ?= 0
|
||||
ifeq (${VCS_SIMPROFILE},1)
|
||||
BUILD_OPTS += -simprofile
|
||||
RUN_OPTS += -simprofile time
|
||||
endif
|
||||
|
||||
# Enable waves
|
||||
VCS_WAVES ?= 0
|
||||
ifeq (${VCS_WAVES},1)
|
||||
BUILD_OPTS += -debug_access+all
|
||||
endif
|
||||
|
||||
# Enable coverage
|
||||
VCS_COV ?= 0
|
||||
ifeq (${VCS_COV},1)
|
||||
COV_METRICS ?= line+cond+fsm+tgl+branch+assert
|
||||
BUILD_OPTS += -cm ${COV_METRICS}
|
||||
CM_HIER ?= ${MAKE_ROOT}/vcs/cover.cfg
|
||||
# Add -cm_hier switch if ${CM_HIER} file exists
|
||||
BUILD_OPTS += $(shell if [ -f ${CM_HIER} ]; then echo "-cm_hier ${CM_HIER}"; fi)
|
||||
# Cover all continuous assignments
|
||||
BUILD_OPTS += -cm_line contassign
|
||||
# Dump toggle coverage on mdas, array of structs and on ports only
|
||||
BUILD_OPTS += -cm_tgl mda+structarr+portsonly
|
||||
# Ignore initial blocks for coverage
|
||||
BUILD_OPTS += -cm_report noinitial
|
||||
# Filter unreachable/statically constant blocks
|
||||
BUILD_OPTS += -cm_noconst
|
||||
# Don't count coverage that's coming from zero-time glitches
|
||||
BUILD_OPTS += -cm_glitch 0
|
||||
# Ignore warnings about not applying cm_glitch to path and FSM
|
||||
BUILD_OPTS += "+warn=noVCM-OPTIGN"
|
||||
# Coverage database output location
|
||||
BUILD_OPTS += -cm_dir ${BUILD_DIR}/cov.vdb
|
||||
|
||||
RUN_OPTS += -cm ${COV_METRICS}
|
||||
# Same directory as build
|
||||
RUN_OPTS += -cm_dir ${BUILD_DIR}/cov.vdb
|
||||
# Don't output cm.log which can be quite large
|
||||
RUN_OPTS += -cm_log /dev/null
|
||||
# Provide a name to the coverage collected for this test
|
||||
RUN_OPTS += -cm_name ${TEST_NAME}_${RUN_LOC}_${SEED}
|
||||
# Don't dump all the coverage assertion attempts at the end of simulation
|
||||
RUN_OPTS += -assert nopostproc
|
||||
endif
|
||||
|
||||
# Enable XPROP
|
||||
XPROP ?= 1
|
||||
ifeq (${XPROP},1)
|
||||
VCS_XPROP_CFG_FILE ?= ${MAKE_ROOT}/vcs/xprop.cfg
|
||||
BUILD_OPTS += -xprop=${VCS_XPROP_CFG_FILE}
|
||||
endif
|
||||
|
||||
# Coverage analyze/report options
|
||||
COV_COMMON_EXCL ?= ${MAKE_ROOT}/vcs/common_cov_excl.el
|
||||
COV_EXCL += ${COV_COMMON_EXCL} ${COV_DUT_EXCL}
|
||||
COV_ANALYZE_TOOL ?= verdi
|
||||
COV_REPORT_TOOL ?= urg
|
||||
COV_DIR ?= ${BUILD_DIR}/cov.vdb
|
||||
COV_EXCL_OPTS ?= -line nocasedef -elfile ${COV_EXCL}
|
||||
COV_ANALYZE_OPTS ?= -cov -covdir ${COV_DIR} ${COV_EXCL_OPTS}
|
||||
COV_REPORT_OPTS ?= -dir ${COV_DIR} ${COV_EXCL_OPTS} -report ${COV_REPORT_DIR}
|
||||
|
||||
# env variables to be exported for VCS
|
||||
export VCS_ARCH_OVERRIDE := linux
|
||||
export VCS_LIC_EXPIRE_WARNING := 1
|
26
vendor/lowrisc_ip/dv/tools/vcs/vcs.tcl
vendored
26
vendor/lowrisc_ip/dv/tools/vcs/vcs.tcl
vendored
|
@ -1,26 +0,0 @@
|
|||
# Copyright lowRISC contributors.
|
||||
# Licensed under the Apache License, Version 2.0, see LICENSE for details.
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
# TCL file invoked from VCS's simv at run-time using this: -ucli -do <this file>
|
||||
|
||||
# Get some environment variables we need.
|
||||
set en_waves 0
|
||||
set tool_srcs_dir ""
|
||||
if {[info exists ::env(EN_WAVES)]} {
|
||||
set en_waves "$::env(EN_WAVES)"
|
||||
}
|
||||
if {[info exists ::env(TOOL_SRCS_DIR)]} {
|
||||
set tool_srcs_dir "$::env(TOOL_SRCS_DIR)"
|
||||
} else {
|
||||
puts "ERROR: tool script run without TOOL_SRCS_DIR environment variable."
|
||||
quit
|
||||
}
|
||||
|
||||
# If wave dumping is enabled, run waves.tcl
|
||||
if {"$en_waves" == 1} {
|
||||
source "${tool_srcs_dir}/waves.tcl"
|
||||
}
|
||||
|
||||
run
|
||||
quit
|
4
vendor/lowrisc_ip/dv/tools/vcs/xprop.cfg
vendored
4
vendor/lowrisc_ip/dv/tools/vcs/xprop.cfg
vendored
|
@ -1,3 +1,7 @@
|
|||
// Copyright lowRISC contributors.
|
||||
// Licensed under the Apache License, Version 2.0, see LICENSE for details.
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
merge = xmerge;
|
||||
|
||||
// Turn on xprop for dut only
|
||||
|
|
181
vendor/lowrisc_ip/dv/tools/waves.tcl
vendored
181
vendor/lowrisc_ip/dv/tools/waves.tcl
vendored
|
@ -2,44 +2,157 @@
|
|||
# Licensed under the Apache License, Version 2.0, see LICENSE for details.
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
#
|
||||
# Generic TCL included by tool-specific scripts when wave dumping is
|
||||
# enabled.
|
||||
#
|
||||
# This is used by all supported simulators, and the driver scripts
|
||||
# This is sourced by all supported simulators. The driver scripts
|
||||
# (dvsim.py) need to make sure that we don't ask for an unsupported
|
||||
# dumping format (SHM with VCS, for example).
|
||||
# dumping format (SHM with VCS, for example). The adjoining common.tcl
|
||||
# must be sourced prior to sourcing this file.
|
||||
|
||||
if {[info proc setDefault] ne "setDefault"} {
|
||||
puts "ERROR: Please ensure that common.tcl is sourced."
|
||||
quit
|
||||
}
|
||||
|
||||
global simulator
|
||||
global waves
|
||||
global tb_top
|
||||
|
||||
set wavedump_db "waves.$waves"
|
||||
|
||||
# TODO: convert this to a proc?
|
||||
switch $waves {
|
||||
"none" {
|
||||
puts "INFO: Dumping waves is not enabled."
|
||||
}
|
||||
|
||||
"fsdb" {
|
||||
if {$simulator eq "xcelium"} {
|
||||
call fsdbDumpfile $wavedump_db
|
||||
} else {
|
||||
fsdbDumpfile $wavedump_db
|
||||
}
|
||||
}
|
||||
|
||||
"shm" {
|
||||
checkEq simulator "xcelium"
|
||||
database -open $wavedump_db -default -shm
|
||||
}
|
||||
|
||||
"vpd" {
|
||||
checkEq simulator "vcs"
|
||||
dump -file $wavedump_db -type VPD
|
||||
}
|
||||
|
||||
"vcd" {
|
||||
if {$simulator eq "xcelium"} {
|
||||
database -open $wavedump_db -default -vcd
|
||||
} else {
|
||||
puts "ERROR: Simulator $simulator does not support dumping waves in VCD."
|
||||
quit
|
||||
}
|
||||
}
|
||||
|
||||
"evcd" {
|
||||
if {$simulator eq "xcelium"} {
|
||||
database -open $wavedump_db -default -evcd
|
||||
} else {
|
||||
puts "ERROR: Simulator $simulator does not support dumping waves in EVCD."
|
||||
quit
|
||||
}
|
||||
}
|
||||
|
||||
default {
|
||||
puts "ERROR: Unknown wave format: ${waves}."
|
||||
quit
|
||||
}
|
||||
}
|
||||
|
||||
if {$waves ne "none"} {
|
||||
puts "INFO: Dumping waves in [string toupper $waves] format to $wavedump_db."
|
||||
}
|
||||
|
||||
# Provides wave-format-agnostic way to set a scope (design heirarchy).
|
||||
#
|
||||
# In large designs, dumping waves on the entire hierarchy can significantly slow down the
|
||||
# simulation. It is useful in that case to only dump the relevant scopes of interest during debug.
|
||||
#
|
||||
# scope : Design / testbench hierarchy to dump waves. Defaults to $tb_top.
|
||||
# depth : Levels in the hierarchy to dump waves. Defaults to 0 (dump all levels).
|
||||
# fsdb_flags : Additional string flags passed to fsdbDumpVars. Defaults to "+all".
|
||||
# probe_flags : Additional string flags passed to probe command (Xcelium). Defaults to "-all".
|
||||
# dump_flags : Additional string flags passed to dump command (VCS). Defaults to "-aggregates".
|
||||
#
|
||||
# Depending on the need, more such technlogy specific flags can be added in future.
|
||||
proc wavedumpScope {scope {depth 0} {fsdb_flags "+all"} {probe_flags "-all"}
|
||||
{dump_flags "-aggregates"}} {
|
||||
global simulator
|
||||
global waves
|
||||
global wavedump_db
|
||||
|
||||
set dump_fmt none
|
||||
if {[info exists ::env(DUMP_FMT)]} {
|
||||
set dump_fmt "$::env(DUMP_FMT)"
|
||||
} else {
|
||||
puts "ERROR: No DUMP_FMT specified for wave dumping."
|
||||
quit
|
||||
switch $waves {
|
||||
"none" {
|
||||
}
|
||||
|
||||
"fsdb" {
|
||||
# The fsdbDumpvars +all command dumps everything: memories, MDAs,
|
||||
# structs, unions, power, packed structs. In addition, also dump SVAs.
|
||||
if {$simulator eq "xcelium"} {
|
||||
call fsdbDumpvars $depth $scope $fsdb_flags
|
||||
call fsdbDumpSVA $depth $scope
|
||||
} else {
|
||||
fsdbDumpvars $depth $scope $fsdb_flags
|
||||
fsdbDumpSVA $depth $scope
|
||||
}
|
||||
}
|
||||
|
||||
"shm" {
|
||||
if {$depth == 0} {
|
||||
set depth "all"
|
||||
}
|
||||
probe "$scope" $probe_flags -depth $depth -shm
|
||||
}
|
||||
|
||||
"vpd" {
|
||||
# The dump command switch -aggregates enables dumping of structs &
|
||||
# arrays.
|
||||
dump -add "$scope" -depth $depth $dump_flags
|
||||
}
|
||||
|
||||
"vcd" {
|
||||
if {$simulator eq "xcelium"} {
|
||||
if {$depth == 0} {
|
||||
set depth "all"
|
||||
}
|
||||
probe "$scope" $probe_flags -depth $depth -vcd
|
||||
}
|
||||
}
|
||||
|
||||
"evcd" {
|
||||
if {$simulator eq "xcelium"} {
|
||||
if {$depth == 0} {
|
||||
set depth "all"
|
||||
}
|
||||
probe "$scope" $probe_flags -depth $depth -evcd
|
||||
}
|
||||
}
|
||||
|
||||
default {
|
||||
puts "ERROR: Unknown wave format: ${waves}."
|
||||
quit
|
||||
}
|
||||
}
|
||||
puts "INFO: Dumping waves in scope \"$scope:$depth\"."
|
||||
}
|
||||
|
||||
set tb_top "tb"
|
||||
if {[info exists ::(TB_TOP)]} {
|
||||
set tb_top "$::env(TB_TOP)"
|
||||
}
|
||||
# Decide whether to dump the entire testbench hierarchy by default.
|
||||
#
|
||||
# If this variable is not set externally, it is set to 1 by default here. When set to 1, it adds the
|
||||
# entire top-level testbench instance for dumping waves. For larger designs, this may slow down the
|
||||
# simulation. The user can if needed, set it to 0 in the external tcl script that sources this
|
||||
# script and manually add the hierarchies of interest in there, using the wavedumpScope proc.
|
||||
setDefault dump_tb_top 1
|
||||
|
||||
if {"$dump_fmt" == "fsdb"} {
|
||||
# The fsdbDumpvars +all command dumps everything: memories,
|
||||
# MDA signals, structs, unions, power and packed structs.
|
||||
puts "Dumping waves with VERDI to waves.fsdb"
|
||||
fsdbDumpfile "waves.fsdb"
|
||||
fsdbDumpvars 0 $tb_top +all
|
||||
fsdbDumpSVA 0 $tb_top
|
||||
} elseif {"$dump_fmt" == "shm"} {
|
||||
puts "Dumping waves in SHM format to waves.shm"
|
||||
database -open -default -shm "waves.shm"
|
||||
probe "$tb_top" -all -depth all -shm
|
||||
} elseif {"$dump_fmt" == "vpd"} {
|
||||
puts "Dumping waves in VCD+ format to waves.vpd"
|
||||
dump -file "waves.vpd"
|
||||
dump -add "$tb_top" -depth 0 -aggregates -scope "."
|
||||
} else {
|
||||
puts "ERROR: Unknown dump format: ${dump_fmt}"
|
||||
quit
|
||||
|
||||
# By default, add the full test bench scope for wavedump.
|
||||
if {$dump_tb_top == 1} {
|
||||
wavedumpScope $tb_top
|
||||
}
|
||||
|
|
53
vendor/lowrisc_ip/dv/tools/xcelium/xcelium.mk
vendored
53
vendor/lowrisc_ip/dv/tools/xcelium/xcelium.mk
vendored
|
@ -1,53 +0,0 @@
|
|||
# Copyright lowRISC contributors.
|
||||
# Licensed under the Apache License, Version 2.0, see LICENSE for details.
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
# Makefile option groups that can be enabled by test Makefile / command line.
|
||||
# These are generic set of option groups that apply to all testbenches.
|
||||
# Simulator too specific options
|
||||
# Mandatory items to set (these are used by rules.mk):
|
||||
# SIMCC - Simulator compiler used to build / elaborate the bench
|
||||
# SIMX - Simulator executable used to run the tests
|
||||
|
||||
SIMCC := xrun -elaborate
|
||||
SIMX ?= xrun
|
||||
SIM_SETUP ?= ${MAKE_ROOT}/xcelium/xcelium.tcl
|
||||
|
||||
# max number of errors before the run is stopped
|
||||
ERROR_MAX := 50
|
||||
# set standard build options
|
||||
BUILD_OPTS += -l ${BUILD_LOG}
|
||||
BUILD_OPTS += -access +r
|
||||
BUILD_OPTS += -messages
|
||||
BUILD_OPTS += -errormax ${ERROR_MAX}
|
||||
BUILD_OPTS += -sv
|
||||
BUILD_OPTS += -timescale 1ns/1ps
|
||||
BUILD_OPTS += -uvmhome ${UVM_HOME}
|
||||
BUILD_OPTS += -xmlibdirname ${SV_FLIST_GEN_DIR}/xcelium.d
|
||||
BUILD_OPTS += -f ${SV_FLIST}
|
||||
BUILD_OPTS += -64bit
|
||||
BUILD_OPTS += -xprop F # -xverbose << add to see which modules does not have xprop enabled
|
||||
|
||||
|
||||
# set standard run options
|
||||
RUN_OPTS += -input ${SIM_SETUP}
|
||||
RUN_OPTS += +SVSEED=${SEED}
|
||||
RUN_OPTS += +UVM_VERBOSITY=${UVM_VERBOSITY}
|
||||
RUN_OPTS += +UVM_TESTNAME=${UVM_TEST}
|
||||
RUN_OPTS += +UVM_TEST_SEQ=${UVM_TEST_SEQ}
|
||||
RUN_OPTS += -l ${RUN_LOG}
|
||||
RUN_OPTS += -xmlibdirname ${SV_FLIST_GEN_DIR}/xcelium.d -R
|
||||
RUN_OPTS += -64bit
|
||||
|
||||
|
||||
#########################
|
||||
## Tool Specific Modes ##
|
||||
#########################
|
||||
# Enable simulation profiling
|
||||
XCELIUM_SIMPROFILE ?= 0
|
||||
ifeq (${XCELIUM_SIMPROFILE},1)
|
||||
endif
|
||||
|
||||
# Enable coverage
|
||||
XCELIUM_COV ?= 0
|
||||
ifeq (${XCELIUM_COV},1)
|
||||
endif
|
26
vendor/lowrisc_ip/dv/tools/xcelium/xcelium.tcl
vendored
26
vendor/lowrisc_ip/dv/tools/xcelium/xcelium.tcl
vendored
|
@ -1,26 +0,0 @@
|
|||
# Copyright lowRISC contributors.
|
||||
# Licensed under the Apache License, Version 2.0, see LICENSE for details.
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
# TCL file invoked from xcelium simulations at run-time using this: -input <this file>
|
||||
|
||||
# Get some environment variables we need.
|
||||
set en_waves 0
|
||||
set tool_srcs_dir ""
|
||||
if {[info exists ::env(EN_WAVES)]} {
|
||||
set en_waves "$::env(EN_WAVES)"
|
||||
}
|
||||
if {[info exists ::env(TOOL_SRCS_DIR)]} {
|
||||
set tool_srcs_dir "$::env(TOOL_SRCS_DIR)"
|
||||
} else {
|
||||
puts "ERROR: tool script run without TOOL_SRCS_DIR environment variable."
|
||||
quit
|
||||
}
|
||||
|
||||
# If wave dumping is enabled, run waves.tcl
|
||||
if {"$en_waves" == 1} {
|
||||
source "${tool_srcs_dir}/waves.tcl"
|
||||
}
|
||||
|
||||
run
|
||||
quit
|
666
vendor/lowrisc_ip/dv/verilator/cpp/dpi_memutil.cc
vendored
Normal file
666
vendor/lowrisc_ip/dv/verilator/cpp/dpi_memutil.cc
vendored
Normal file
|
@ -0,0 +1,666 @@
|
|||
// Copyright lowRISC contributors.
|
||||
// Licensed under the Apache License, Version 2.0, see LICENSE for details.
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
#include "dpi_memutil.h"
|
||||
|
||||
#include <cassert>
|
||||
#include <cstring>
|
||||
#include <fcntl.h>
|
||||
#include <iostream>
|
||||
#include <libelf.h>
|
||||
#include <sstream>
|
||||
#include <sys/stat.h>
|
||||
#include <unistd.h>
|
||||
#include <vector>
|
||||
|
||||
#include "sv_scoped.h"
|
||||
|
||||
// DPI Exports
|
||||
extern "C" {
|
||||
|
||||
/**
|
||||
* Write |file| to a memory
|
||||
*
|
||||
* @param file path to a SystemVerilog $readmemh()-compatible file (VMEM file)
|
||||
*/
|
||||
extern void simutil_memload(const char *file);
|
||||
|
||||
/**
|
||||
* Write a 32 bit word |val| to memory at index |index|
|
||||
*
|
||||
* @return 1 if successful, 0 otherwise
|
||||
*/
|
||||
extern int simutil_set_mem(int index, const svBitVecVal *val);
|
||||
}
|
||||
|
||||
namespace {
|
||||
// Convenience class for runtime errors when loading an ELF file
|
||||
class ElfError : public std::exception {
|
||||
public:
|
||||
ElfError(const std::string &path, const std::string &msg) {
|
||||
std::ostringstream oss;
|
||||
oss << "Failed to load ELF file at `" << path << "': " << msg;
|
||||
msg_ = oss.str();
|
||||
}
|
||||
|
||||
const char *what() const noexcept override { return msg_.c_str(); }
|
||||
|
||||
private:
|
||||
std::string msg_;
|
||||
};
|
||||
|
||||
// Class wrapping an open ELF file
|
||||
class ElfFile {
|
||||
public:
|
||||
ElfFile(const std::string &path) : path_(path) {
|
||||
(void)elf_errno();
|
||||
if (elf_version(EV_CURRENT) == EV_NONE) {
|
||||
throw std::runtime_error(elf_errmsg(-1));
|
||||
}
|
||||
|
||||
fd_ = open(path.c_str(), O_RDONLY, 0);
|
||||
if (fd_ < 0) {
|
||||
throw ElfError(path, "could not open file.");
|
||||
}
|
||||
|
||||
ptr_ = elf_begin(fd_, ELF_C_READ, NULL);
|
||||
if (!ptr_) {
|
||||
close(fd_);
|
||||
throw ElfError(path, elf_errmsg(-1));
|
||||
}
|
||||
|
||||
if (elf_kind(ptr_) != ELF_K_ELF) {
|
||||
elf_end(ptr_);
|
||||
close(fd_);
|
||||
throw ElfError(path, "not an ELF file.");
|
||||
}
|
||||
}
|
||||
|
||||
~ElfFile() {
|
||||
elf_end(ptr_);
|
||||
close(fd_);
|
||||
}
|
||||
|
||||
size_t GetPhdrNum() {
|
||||
size_t phnum;
|
||||
if (elf_getphdrnum(ptr_, &phnum) != 0) {
|
||||
throw ElfError(path_, elf_errmsg(-1));
|
||||
}
|
||||
return phnum;
|
||||
}
|
||||
|
||||
const Elf32_Phdr *GetPhdrs() {
|
||||
const Elf32_Phdr *phdrs = elf32_getphdr(ptr_);
|
||||
if (!phdrs)
|
||||
throw ElfError(path_, elf_errmsg(-1));
|
||||
return phdrs;
|
||||
}
|
||||
|
||||
std::string path_;
|
||||
int fd_;
|
||||
Elf *ptr_;
|
||||
};
|
||||
} // namespace
|
||||
|
||||
// Convert a string to a MemImageType, throwing a std::runtime_error
|
||||
// if it's not a known name.
|
||||
static MemImageType GetMemImageTypeByName(const std::string &name) {
|
||||
if (name == "elf")
|
||||
return kMemImageElf;
|
||||
if (name == "vmem")
|
||||
return kMemImageVmem;
|
||||
|
||||
std::ostringstream oss;
|
||||
oss << "Unknown image type: `" << name << "'.";
|
||||
throw std::runtime_error(oss.str());
|
||||
}
|
||||
|
||||
// Return a MemImageType for the file at filepath or throw a std::runtime_error.
|
||||
// Never returns kMemImageUnknown.
|
||||
static MemImageType DetectMemImageType(const std::string &filepath) {
|
||||
size_t ext_pos = filepath.find_last_of(".");
|
||||
if (ext_pos == std::string::npos) {
|
||||
// Assume ELF files if no file extension is given.
|
||||
// TODO: Make this more robust by actually checking the file contents.
|
||||
return kMemImageElf;
|
||||
}
|
||||
|
||||
std::string ext = filepath.substr(ext_pos + 1);
|
||||
MemImageType image_type = GetMemImageTypeByName(ext);
|
||||
if (image_type == kMemImageUnknown) {
|
||||
std::ostringstream oss;
|
||||
oss << "Cannot auto-detect file type for `" << filepath << "'.";
|
||||
throw std::runtime_error(oss.str());
|
||||
}
|
||||
|
||||
return image_type;
|
||||
}
|
||||
|
||||
// Generate a single array of bytes representing the contents of PT_LOAD
|
||||
// segments of the ELF file. Like objcopy, this generates a single "giant
|
||||
// segment" whose first byte corresponds to the first byte of the lowest
|
||||
// addressed segment and whose last byte corresponds to the last byte of the
|
||||
// highest address.
|
||||
static std::vector<uint8_t> FlattenElfFile(const std::string &filepath) {
|
||||
ElfFile elf(filepath);
|
||||
|
||||
size_t phnum = elf.GetPhdrNum();
|
||||
const Elf32_Phdr *phdrs = elf.GetPhdrs();
|
||||
|
||||
// To mimic what objcopy does (that is, the binary target of BFD), we need to
|
||||
// iterate over all loadable program headers, find the lowest address, and
|
||||
// then copy in our loadable data based on their offset with respect to the
|
||||
// found base address.
|
||||
|
||||
bool any = false;
|
||||
Elf32_Addr low = 0, high = 0;
|
||||
for (size_t i = 0; i < phnum; i++) {
|
||||
const Elf32_Phdr &phdr = phdrs[i];
|
||||
|
||||
if (phdr.p_type != PT_LOAD) {
|
||||
std::cout << "Program header number " << i << " in `" << filepath
|
||||
<< "' is not of type PT_LOAD; ignoring." << std::endl;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (phdr.p_memsz == 0) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!any || phdr.p_paddr < low) {
|
||||
low = phdr.p_paddr;
|
||||
}
|
||||
|
||||
Elf32_Addr seg_top = phdr.p_paddr + (phdr.p_memsz - 1);
|
||||
if (seg_top < phdr.p_paddr) {
|
||||
std::ostringstream oss;
|
||||
oss << "phdr for segment " << i << " has start 0x" << std::hex
|
||||
<< phdr.p_paddr << " and size 0x" << phdr.p_memsz
|
||||
<< ", which overflows the address space.";
|
||||
throw ElfError(filepath, oss.str());
|
||||
}
|
||||
|
||||
if (!any || seg_top > high) {
|
||||
high = seg_top;
|
||||
}
|
||||
|
||||
any = true;
|
||||
}
|
||||
|
||||
// If any is false, there were no segments that contributed to the
|
||||
// file. Return nothing.
|
||||
if (!any)
|
||||
return std::vector<uint8_t>();
|
||||
|
||||
// Otherwise, we know every valid byte of data has an address in the
|
||||
// range [low, high] (inclusive).
|
||||
assert(low <= high);
|
||||
|
||||
size_t file_size;
|
||||
const char *file_data = elf_rawfile(elf.ptr_, &file_size);
|
||||
assert(file_data);
|
||||
|
||||
StagedMem ret;
|
||||
|
||||
for (size_t i = 0; i < phnum; i++) {
|
||||
const Elf32_Phdr &phdr = phdrs[i];
|
||||
|
||||
if (phdr.p_type != PT_LOAD) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// Check the segment actually fits in the file
|
||||
if (file_size < phdr.p_offset + phdr.p_filesz) {
|
||||
std::ostringstream oss;
|
||||
oss << "phdr for segment " << i << " claims to end at offset 0x"
|
||||
<< std::hex << phdr.p_offset + phdr.p_filesz
|
||||
<< ", but the file only has size 0x" << file_size << ".";
|
||||
throw ElfError(filepath, oss.str());
|
||||
}
|
||||
|
||||
uint32_t off = phdr.p_paddr - low;
|
||||
uint32_t dst_len = phdr.p_memsz;
|
||||
uint32_t src_len = std::min(phdr.p_filesz, dst_len);
|
||||
|
||||
if (!dst_len)
|
||||
continue;
|
||||
|
||||
std::vector<uint8_t> seg(dst_len, 0);
|
||||
memcpy(&seg[0], file_data + phdr.p_offset, src_len);
|
||||
ret.AddSegment(off, std::move(seg));
|
||||
}
|
||||
|
||||
return ret.GetFlat();
|
||||
}
|
||||
|
||||
// Write a "segment" of data to the given memory area.
|
||||
static void WriteSegment(const MemArea &m, uint32_t offset,
|
||||
const std::vector<uint8_t> &data) {
|
||||
assert(m.width_byte <= 32);
|
||||
assert(m.addr_loc.size == 0 || offset + data.size() <= m.addr_loc.size);
|
||||
assert((offset % m.width_byte) == 0);
|
||||
|
||||
// If this fails to set scope, it will throw an error which should
|
||||
// be caught at this function's callsite.
|
||||
SVScoped scoped(m.location.data());
|
||||
|
||||
// This "mini buffer" is used to transfer each write to SystemVerilog. It's
|
||||
// not massively efficient, but doing so ensures that we pass 256 bits (32
|
||||
// bytes) of initialised data each time. This is for simutil_set_mem (defined
|
||||
// in prim_util_memload.svh), whose "val" argument has SystemVerilog type bit
|
||||
// [255:0].
|
||||
uint8_t minibuf[32];
|
||||
memset(minibuf, 0, sizeof minibuf);
|
||||
assert(m.width_byte <= sizeof minibuf);
|
||||
|
||||
uint32_t all_words = (data.size() + m.width_byte - 1) / m.width_byte;
|
||||
uint32_t full_data_words = data.size() / m.width_byte;
|
||||
uint32_t part_data_word_len = data.size() % m.width_byte;
|
||||
bool has_part_data_word = part_data_word_len != 0;
|
||||
|
||||
uint32_t word_offset = offset / m.width_byte;
|
||||
|
||||
// Copy the full data words
|
||||
for (uint32_t i = 0; i < full_data_words; ++i) {
|
||||
uint32_t dst_word = word_offset + i;
|
||||
uint32_t src_byte = i * m.width_byte;
|
||||
memcpy(minibuf, &data[src_byte], m.width_byte);
|
||||
if (!simutil_set_mem(dst_word, (svBitVecVal *)minibuf)) {
|
||||
std::ostringstream oss;
|
||||
oss << "Could not set `" << m.name << "' memory at byte offset 0x"
|
||||
<< std::hex << dst_word * m.width_byte << ".";
|
||||
throw std::runtime_error(oss.str());
|
||||
}
|
||||
}
|
||||
|
||||
// Copy any partial data, zeroing minibuf first to ensure that the latter
|
||||
// bytes in the word are zero.
|
||||
if (has_part_data_word) {
|
||||
memset(minibuf, 0, sizeof minibuf);
|
||||
uint32_t dst_word = word_offset + full_data_words;
|
||||
uint32_t src_byte = full_data_words * m.width_byte;
|
||||
memcpy(minibuf, &data[src_byte], part_data_word_len);
|
||||
if (!simutil_set_mem(dst_word, (svBitVecVal *)minibuf)) {
|
||||
std::ostringstream oss;
|
||||
oss << "Could not set `" << m.name << "' memory at byte offset 0x"
|
||||
<< std::hex << dst_word * m.width_byte << " (partial data word).";
|
||||
throw std::runtime_error(oss.str());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void WriteElfToMem(const MemArea &m, const std::string &filepath) {
|
||||
WriteSegment(m, 0, FlattenElfFile(filepath));
|
||||
}
|
||||
|
||||
static void WriteVmemToMem(const MemArea &m, const std::string &filepath) {
|
||||
SVScoped scoped(m.location.data());
|
||||
// TODO: Add error handling.
|
||||
simutil_memload(filepath.data());
|
||||
}
|
||||
|
||||
// Merge seg0 and seg1, overwriting any overlapping data in seg0 with
|
||||
// that from seg1. rng0/rng1 is the base and top address of seg0/seg1,
|
||||
// respectively.
|
||||
static std::vector<uint8_t> MergeSegments(const AddrRange<uint32_t> &rng0,
|
||||
std::vector<uint8_t> &&seg0,
|
||||
const AddrRange<uint32_t> &rng1,
|
||||
std::vector<uint8_t> &&seg1) {
|
||||
// First, deal with the special case where seg1 completely contains
|
||||
// seg0 (since there's no copying needed at all).
|
||||
if (rng1.lo <= rng0.lo && rng0.hi <= rng1.hi) {
|
||||
return std::move(seg1);
|
||||
}
|
||||
|
||||
uint32_t new_bot = std::min(rng0.lo, rng1.lo);
|
||||
uint32_t new_top = std::max(rng0.hi, rng1.hi);
|
||||
assert(new_bot <= new_top);
|
||||
size_t new_len = 1 + (size_t)(new_top - new_bot);
|
||||
assert(seg0.size() <= new_len);
|
||||
assert(seg1.size() <= new_len);
|
||||
|
||||
// We want to avoid copying if possible. The next most efficient
|
||||
// case (after just returning seg1) is when seg0 doesn't stick out
|
||||
// the left hand end. In this case, we can extend seg1 to the right
|
||||
// (which might not cause a copy) and then copy just the bytes we
|
||||
// need from seg0.
|
||||
if (rng1.lo <= rng0.lo) {
|
||||
assert(rng1.hi < rng0.hi);
|
||||
assert(new_len == seg1.size() + (rng0.hi - rng1.hi));
|
||||
|
||||
size_t old_len = seg1.size();
|
||||
std::vector<uint8_t> ret = std::move(seg1);
|
||||
ret.resize(new_len);
|
||||
|
||||
// We know that that rng0 isn't completely contained in rng1 and
|
||||
// that rng0 doesn't stick out of the left hand end. That means it
|
||||
// must stick out of the right (so rng1.hi < rng0.hi). However, we
|
||||
// also know that the two ranges overlap, so rng0.lo <= rng1.hi.
|
||||
assert(rng0.lo <= rng1.hi);
|
||||
|
||||
// src_off is the index of the first byte that needs copying from
|
||||
// seg0. Note that this is always at least 1 (because there is an
|
||||
// actual overlap).
|
||||
uint32_t src_off = 1 + (rng1.hi - rng0.lo);
|
||||
|
||||
assert(seg0.size() == src_off + (rng0.hi - rng1.hi));
|
||||
|
||||
memcpy(&ret[old_len], &seg0[src_off], rng0.hi - rng1.hi);
|
||||
return ret;
|
||||
}
|
||||
|
||||
// In this final case, seg0 sticks out the left hand end. That means
|
||||
// we'll have to copy seg1 whatever happens (because we have to
|
||||
// shuffle its elements to the right). Work by resizing seg0 and
|
||||
// then writing seg1 where it's needed.
|
||||
std::vector<uint8_t> ret = std::move(seg0);
|
||||
ret.resize(new_len);
|
||||
|
||||
uint32_t off = rng1.lo - rng0.lo;
|
||||
memcpy(&ret[off], &seg1[0], seg1.size());
|
||||
return ret;
|
||||
}
|
||||
|
||||
void StagedMem::AddSegment(uint32_t offset, std::vector<uint8_t> &&seg) {
|
||||
if (seg.empty())
|
||||
return;
|
||||
|
||||
uint32_t seg_top = offset + seg.size() - 1;
|
||||
assert(seg_top >= offset);
|
||||
|
||||
min_addr_ = std::min(min_addr_, offset);
|
||||
max_addr_ = std::max(max_addr_, seg_top);
|
||||
segs_.Emplace(offset, seg_top, std::move(seg), MergeSegments);
|
||||
}
|
||||
|
||||
std::vector<uint8_t> StagedMem::GetFlat() const {
|
||||
// Since max_addr_ and min_addr_ are inclusive, the size to allocate
|
||||
// is 1+(max-min). We cast to size_t to make sure the +1 doesn't
|
||||
// overflow.
|
||||
size_t len = (size_t)1 + (max_addr_ - min_addr_);
|
||||
std::vector<uint8_t> ret(len, 0);
|
||||
|
||||
for (const auto &pr : segs_) {
|
||||
const AddrRange<uint32_t> &rng = pr.first;
|
||||
const std::vector<uint8_t> &seg = pr.second;
|
||||
assert(seg.size() == 1 + (rng.hi - rng.lo));
|
||||
assert(min_addr_ <= rng.lo);
|
||||
|
||||
uint32_t off = rng.lo - min_addr_;
|
||||
assert(off + seg.size() <= ret.size());
|
||||
|
||||
memcpy(&ret[off], &seg[0], seg.size());
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
bool DpiMemUtil::RegisterMemoryArea(const std::string name,
|
||||
const std::string location) {
|
||||
// Default to 32bit width and no address
|
||||
return RegisterMemoryArea(name, location, 32, nullptr);
|
||||
}
|
||||
|
||||
bool DpiMemUtil::RegisterMemoryArea(const std::string name,
|
||||
const std::string location,
|
||||
size_t width_bit,
|
||||
const MemAreaLoc *addr_loc) {
|
||||
assert((width_bit <= 256) &&
|
||||
"TODO: Memory loading only supported up to 256 bits.");
|
||||
assert(width_bit % 8 == 0);
|
||||
|
||||
// First, create and register the memory by name
|
||||
MemArea mem = {.name = name,
|
||||
.location = location,
|
||||
.width_byte = (uint32_t)width_bit / 8,
|
||||
.addr_loc = {.base = 0, .size = 0}};
|
||||
auto ret = name_to_mem_.emplace(name, mem);
|
||||
if (ret.second == false) {
|
||||
std::cerr << "ERROR: Can not register \"" << name << "\" at: \"" << location
|
||||
<< "\" (Previously defined at: \"" << ret.first->second.location
|
||||
<< "\")" << std::endl;
|
||||
return false;
|
||||
}
|
||||
|
||||
MemArea *stored_mem_area = &ret.first->second;
|
||||
|
||||
// If we have no address information, there's nothing more to do. However, if
|
||||
// we do have address information, we should add an entry to addr_to_mem_.
|
||||
if (!addr_loc) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// Check that the size of the new area is positive, and that we don't overflow
|
||||
// the address space.
|
||||
if (addr_loc->size == 0) {
|
||||
std::cerr << "ERROR: Can not register '" << name
|
||||
<< "' because it has zero size.\n";
|
||||
return false;
|
||||
}
|
||||
uint32_t addr_top = addr_loc->base + (addr_loc->size - 1);
|
||||
if (addr_top < addr_loc->base) {
|
||||
std::cerr << "ERROR: Can not register '" << name
|
||||
<< "' because it overflows the top of the address space.\n";
|
||||
return false;
|
||||
}
|
||||
|
||||
auto clash = addr_to_mem_.EmplaceDisjoint(addr_loc->base, addr_top,
|
||||
std::move(stored_mem_area));
|
||||
if (clash) {
|
||||
assert(*clash);
|
||||
std::cerr << "ERROR: Can not register '" << name
|
||||
<< "' because its address range overlaps the existing area `"
|
||||
<< (*clash)->name << "'.\n";
|
||||
return false;
|
||||
}
|
||||
stored_mem_area->addr_loc = *addr_loc;
|
||||
return true;
|
||||
}
|
||||
|
||||
MemImageType DpiMemUtil::GetMemImageType(const std::string &path,
|
||||
const char *type) {
|
||||
return type ? GetMemImageTypeByName(type) : DetectMemImageType(path);
|
||||
}
|
||||
|
||||
void DpiMemUtil::PrintMemRegions() const {
|
||||
std::cout << "Registered memory regions:" << std::endl;
|
||||
for (const auto &pr : name_to_mem_) {
|
||||
const MemArea &m = pr.second;
|
||||
std::cout << "\t'" << m.name << "' (" << m.width_byte * 8
|
||||
<< "bits) at location: '" << m.location << "'";
|
||||
if (m.addr_loc.size) {
|
||||
uint32_t low = m.addr_loc.base;
|
||||
uint32_t high = m.addr_loc.base + m.addr_loc.size - 1;
|
||||
std::cout << " (LMA range [0x" << std::hex << low << ", 0x" << high
|
||||
<< "])" << std::dec;
|
||||
}
|
||||
std::cout << std::endl;
|
||||
}
|
||||
}
|
||||
|
||||
void DpiMemUtil::LoadFileToNamedMem(bool verbose, const std::string &name,
|
||||
const std::string &filepath,
|
||||
MemImageType type) {
|
||||
// If the image type isn't specified, try to figure it out from the file name
|
||||
if (type == kMemImageUnknown) {
|
||||
type = DetectMemImageType(filepath);
|
||||
}
|
||||
assert(type != kMemImageUnknown);
|
||||
|
||||
// Search for corresponding registered memory based on the name
|
||||
auto it = name_to_mem_.find(name);
|
||||
if (it == name_to_mem_.end()) {
|
||||
std::ostringstream oss;
|
||||
oss << "`" << name
|
||||
<< ("' is not the name of a known memory region. "
|
||||
"Run with --meminit=list to get a list.");
|
||||
throw std::runtime_error(oss.str());
|
||||
}
|
||||
|
||||
if (verbose) {
|
||||
std::cout << "Loading data from file `" << filepath << "' into memory `"
|
||||
<< name << "'." << std::endl;
|
||||
}
|
||||
|
||||
const MemArea &m = it->second;
|
||||
|
||||
try {
|
||||
switch (type) {
|
||||
case kMemImageElf:
|
||||
WriteElfToMem(m, filepath);
|
||||
break;
|
||||
case kMemImageVmem:
|
||||
WriteVmemToMem(m, filepath);
|
||||
break;
|
||||
default:
|
||||
assert(0);
|
||||
}
|
||||
} catch (const SVScoped::Error &err) {
|
||||
std::ostringstream oss;
|
||||
oss << "No memory found at `" << err.scope_name_
|
||||
<< "' (the scope associated with region `" << m.name << "').";
|
||||
throw std::runtime_error(oss.str());
|
||||
}
|
||||
}
|
||||
|
||||
void DpiMemUtil::LoadElfToMemories(bool verbose, const std::string &filepath) {
|
||||
// Load the contents of the ELF file into the staging area
|
||||
StageElf(verbose, filepath);
|
||||
|
||||
for (const auto &pr : staging_area_) {
|
||||
const std::string &mem_name = pr.first;
|
||||
const StagedMem &staged_mem = pr.second;
|
||||
|
||||
auto mem_area_it = name_to_mem_.find(mem_name);
|
||||
assert(mem_area_it != name_to_mem_.end());
|
||||
|
||||
const MemArea &mem_area = mem_area_it->second;
|
||||
|
||||
for (const auto seg_pr : staged_mem.GetSegs()) {
|
||||
const AddrRange<uint32_t> &seg_rng = seg_pr.first;
|
||||
const std::vector<uint8_t> &seg_data = seg_pr.second;
|
||||
try {
|
||||
WriteSegment(mem_area, seg_rng.lo, seg_data);
|
||||
} catch (const SVScoped::Error &err) {
|
||||
std::ostringstream oss;
|
||||
oss << "No memory found at `" << err.scope_name_
|
||||
<< "' (the scope associated with region `" << mem_area.name
|
||||
<< "', used by a segment that starts at LMA 0x" << std::hex
|
||||
<< mem_area.addr_loc.base + seg_rng.lo << ").";
|
||||
throw std::runtime_error(oss.str());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void DpiMemUtil::StageElf(bool verbose, const std::string &path) {
|
||||
// Clear out anything that was in the staging area before
|
||||
staging_area_.clear();
|
||||
|
||||
ElfFile elf(path);
|
||||
|
||||
size_t file_size;
|
||||
const char *file_data = elf_rawfile(elf.ptr_, &file_size);
|
||||
assert(file_data);
|
||||
|
||||
size_t phnum = elf.GetPhdrNum();
|
||||
const Elf32_Phdr *phdrs = elf.GetPhdrs();
|
||||
|
||||
for (size_t i = 0; i < phnum; ++i) {
|
||||
const Elf32_Phdr &phdr = phdrs[i];
|
||||
if (phdr.p_type != PT_LOAD)
|
||||
continue;
|
||||
|
||||
if (phdr.p_memsz == 0)
|
||||
continue;
|
||||
|
||||
const MemArea &mem_area =
|
||||
GetRegionForSegment(path, i, phdr.p_paddr, phdr.p_memsz);
|
||||
|
||||
// Check that the segment is aligned correctly for the memory
|
||||
uint32_t local_base = phdr.p_paddr - mem_area.addr_loc.base;
|
||||
if (local_base % mem_area.width_byte) {
|
||||
std::ostringstream oss;
|
||||
oss << "Segment " << i << " has LMA 0x" << std::hex << phdr.p_paddr
|
||||
<< ", which starts at offset 0x" << local_base
|
||||
<< " in the memory region `" << mem_area.name
|
||||
<< "'. This offset is not aligned to the region's word width of "
|
||||
<< std::dec << 8 * mem_area.width_byte << " bits.";
|
||||
throw ElfError(path, oss.str());
|
||||
}
|
||||
|
||||
// Where does the segment finish in the file image? We don't need
|
||||
// to worry about overflow here, because we're adding two
|
||||
// uint32_t's into a size_t. But we do need to check the segment
|
||||
// actually fits in the file
|
||||
size_t off_end = (size_t)phdr.p_offset + phdr.p_filesz;
|
||||
if (file_size < off_end) {
|
||||
std::ostringstream oss;
|
||||
oss << "phdr for segment " << i << " claims to end at offset 0x"
|
||||
<< std::hex << off_end - 1 << ", but the file only has size 0x"
|
||||
<< file_size << ".";
|
||||
throw ElfError(path, oss.str());
|
||||
}
|
||||
|
||||
if (verbose) {
|
||||
std::cout << "Loading segment " << i << " from ELF file `" << path
|
||||
<< "' into memory `" << mem_area.name << "'." << std::endl;
|
||||
}
|
||||
|
||||
// Get the StagedMem object associated with this memory area. If
|
||||
// there isn't one, make a new empty one.
|
||||
StagedMem &staged_mem = staging_area_[mem_area.name];
|
||||
|
||||
const char *seg_data = file_data + phdr.p_offset;
|
||||
std::vector<uint8_t> vec(phdr.p_memsz, 0);
|
||||
memcpy(&vec[0], seg_data, std::min(phdr.p_filesz, phdr.p_memsz));
|
||||
|
||||
staged_mem.AddSegment(local_base, std::move(vec));
|
||||
}
|
||||
}
|
||||
|
||||
const StagedMem &DpiMemUtil::GetMemoryData(const std::string &mem_name) const {
|
||||
auto it = staging_area_.find(mem_name);
|
||||
return (it == staging_area_.end()) ? empty_ : it->second;
|
||||
}
|
||||
|
||||
const MemArea &DpiMemUtil::GetRegionForSegment(const std::string &path,
|
||||
int seg_idx, uint32_t lma,
|
||||
uint32_t mem_sz) const {
|
||||
assert(mem_sz > 0);
|
||||
|
||||
auto mem_area_it = addr_to_mem_.find(lma);
|
||||
if (mem_area_it == addr_to_mem_.end()) {
|
||||
std::ostringstream oss;
|
||||
oss << "No memory region is registered that contains the address 0x"
|
||||
<< std::hex << lma << " (the base address of segment " << seg_idx
|
||||
<< ").";
|
||||
throw ElfError(path, oss.str());
|
||||
}
|
||||
const MemArea *mem_area = mem_area_it->second;
|
||||
assert(mem_area);
|
||||
assert(mem_area->addr_loc.base <= lma);
|
||||
|
||||
uint32_t lma_top = lma + (mem_sz - 1);
|
||||
if (lma_top < lma) {
|
||||
std::ostringstream oss;
|
||||
oss << "Integer overflow for top address of segment " << seg_idx << ".";
|
||||
throw ElfError(path, oss.str());
|
||||
}
|
||||
|
||||
uint32_t local_base = lma - mem_area->addr_loc.base;
|
||||
uint32_t local_top = lma_top - mem_area->addr_loc.base;
|
||||
|
||||
if (mem_area->addr_loc.size <= local_top) {
|
||||
std::ostringstream oss;
|
||||
oss << "Segment " << seg_idx << " has size 0x" << std::hex << mem_sz
|
||||
<< " bytes. Its LMA of 0x" << lma << " is at offset 0x" << local_base
|
||||
<< " in the memory region `" << mem_area->name
|
||||
<< "', so the segment finishes at offset 0x" << local_top
|
||||
<< ", but the memory region is only 0x" << mem_area->addr_loc.size
|
||||
<< " bytes long.";
|
||||
throw ElfError(path, oss.str());
|
||||
}
|
||||
|
||||
return *mem_area;
|
||||
}
|
166
vendor/lowrisc_ip/dv/verilator/cpp/dpi_memutil.h
vendored
Normal file
166
vendor/lowrisc_ip/dv/verilator/cpp/dpi_memutil.h
vendored
Normal file
|
@ -0,0 +1,166 @@
|
|||
// Copyright lowRISC contributors.
|
||||
// Licensed under the Apache License, Version 2.0, see LICENSE for details.
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <map>
|
||||
#include <string>
|
||||
#include <svdpi.h>
|
||||
#include <vector>
|
||||
|
||||
#include "ranged_map.h"
|
||||
|
||||
enum MemImageType {
|
||||
kMemImageUnknown = 0,
|
||||
kMemImageElf,
|
||||
kMemImageVmem,
|
||||
};
|
||||
|
||||
// The "load" location of a memory area. base is the lowest address in
|
||||
// the area, and should correspond to an ELF file's LMA. size is the
|
||||
// length of the area in bytes.
|
||||
struct MemAreaLoc {
|
||||
uint32_t base;
|
||||
uint32_t size;
|
||||
};
|
||||
|
||||
struct MemArea {
|
||||
std::string name; // Unique identifier
|
||||
std::string location; // Design scope location
|
||||
uint32_t width_byte; // Memory width in bytes
|
||||
MemAreaLoc addr_loc; // Address location. If !size, location is unknown.
|
||||
};
|
||||
|
||||
// Staged data for a given memory area.
|
||||
//
|
||||
// This is represented as an ordered list of disjoint segments (as loaded from
|
||||
// an ELF file).
|
||||
//
|
||||
// Once it is nonempty, the class maintains the invariant that min_addr_ /
|
||||
// max_addr_ is the smallest / largest byte offset with valid data.
|
||||
class StagedMem {
|
||||
public:
|
||||
StagedMem() : min_addr_(~(uint32_t)0), max_addr_(0) {}
|
||||
|
||||
// Add a segment to the tracked memory
|
||||
void AddSegment(uint32_t offset, std::vector<uint8_t> &&seg);
|
||||
|
||||
// Glob together the tracked segments, interspersing them with
|
||||
// zeros, and return as a single flat array.
|
||||
std::vector<uint8_t> GetFlat() const;
|
||||
|
||||
typedef RangedMap<uint32_t, std::vector<uint8_t>> SegMap;
|
||||
|
||||
std::pair<uint32_t, uint32_t> GetBounds() const {
|
||||
return std::make_pair(min_addr_, max_addr_);
|
||||
}
|
||||
const SegMap &GetSegs() const { return segs_; }
|
||||
|
||||
private:
|
||||
uint32_t min_addr_, max_addr_;
|
||||
SegMap segs_;
|
||||
};
|
||||
|
||||
/**
|
||||
* Provide various memory loading utilities for verilog simulations
|
||||
*
|
||||
* These utilities require the corresponding DPI functions:
|
||||
* simutil_memload()
|
||||
* simutil_set_mem()
|
||||
* to be defined somewhere as SystemVerilog functions.
|
||||
*/
|
||||
class DpiMemUtil {
|
||||
public:
|
||||
/**
|
||||
* Register a memory as instantiated by generic ram
|
||||
*
|
||||
* The |name| must be a unique identifier. The function will return false if
|
||||
* |name| is already used. |location| is the path to the scope of the
|
||||
* instantiated memory, which needs to support the DPI-C interfaces
|
||||
* 'simutil_memload' and 'simutil_set_mem' used for 'vmem' and 'elf' files,
|
||||
* respectively.
|
||||
*
|
||||
* The |width_bit| argument specifies the with in bits of the target memory
|
||||
* instance (used for packing data). This must be a multiple of 8. If
|
||||
* |addr_loc| is not null, it gives the base and size of the memory for
|
||||
* loading in the address space (corresponding to LMAs in an ELF file).
|
||||
*
|
||||
* Memories must be registered before command arguments are parsed by
|
||||
* ParseCommandArgs() in order for them to be known.
|
||||
*/
|
||||
bool RegisterMemoryArea(const std::string name, const std::string location,
|
||||
size_t width_bit, const MemAreaLoc *addr_loc);
|
||||
|
||||
/**
|
||||
* Register a memory with default width (32bits)
|
||||
*/
|
||||
bool RegisterMemoryArea(const std::string name, const std::string location);
|
||||
|
||||
/**
|
||||
* Guess the type of the file at |path|.
|
||||
*
|
||||
* If |type| is non-null, it is the name of an image type and will be used.
|
||||
* Otherwise, the check is based on |path|. If |type| is not a valid name or
|
||||
* if the function can't guess from the path, throws a std::runtime_error
|
||||
* with a message about what went wrong.
|
||||
*
|
||||
* Never returns kMemImageUnknown.
|
||||
*/
|
||||
static MemImageType GetMemImageType(const std::string &path,
|
||||
const char *type);
|
||||
|
||||
/**
|
||||
* Print a list of all registered memory regions
|
||||
*
|
||||
* @see RegisterMemoryArea()
|
||||
*/
|
||||
void PrintMemRegions() const;
|
||||
|
||||
/**
|
||||
* Load the file at filepath into the named memory. If type is
|
||||
* kMemImageUnknown, the file type is determined from the path.
|
||||
*/
|
||||
void LoadFileToNamedMem(bool verbose, const std::string &name,
|
||||
const std::string &filepath, MemImageType type);
|
||||
|
||||
/**
|
||||
* Load an ELF file, placing segments in memories by LMA.
|
||||
*
|
||||
* Replaces any data currently in the staging area.
|
||||
*/
|
||||
void LoadElfToMemories(bool verbose, const std::string &filepath);
|
||||
|
||||
/**
|
||||
* Load an ELF file into a staging area in this object, which can then be
|
||||
* accessed with GetMemoryData().
|
||||
*
|
||||
* If the load fails, raises a std::exception with information about what
|
||||
* happened.
|
||||
*/
|
||||
void StageElf(bool verbose, const std::string &path);
|
||||
|
||||
/**
|
||||
* Get the contents of the staging area by memory name
|
||||
*/
|
||||
const StagedMem &GetMemoryData(const std::string &mem_name) const;
|
||||
|
||||
private:
|
||||
// Memory area registry
|
||||
std::map<std::string, MemArea> name_to_mem_;
|
||||
RangedMap<uint32_t, MemArea *> addr_to_mem_;
|
||||
|
||||
// Staging area, loaded by StageElf. The map is keyed by names of memories
|
||||
// stored in name_to_mem_. We also ensure that every segment in a StagedMem
|
||||
// for a memory starts at an address that's aligned for the word width of
|
||||
// that memory. Note: we don't also check segments' lengths are aligned.
|
||||
std::map<std::string, StagedMem> staging_area_;
|
||||
const StagedMem empty_;
|
||||
|
||||
/**
|
||||
* Find a region containing for the given segment's addresses.
|
||||
* Raises a std::exception if none is found.
|
||||
*/
|
||||
const MemArea &GetRegionForSegment(const std::string &path, int seg_idx,
|
||||
uint32_t lma, uint32_t mem_sz) const;
|
||||
};
|
181
vendor/lowrisc_ip/dv/verilator/cpp/ranged_map.h
vendored
Normal file
181
vendor/lowrisc_ip/dv/verilator/cpp/ranged_map.h
vendored
Normal file
|
@ -0,0 +1,181 @@
|
|||
// Copyright lowRISC contributors.
|
||||
// Licensed under the Apache License, Version 2.0, see LICENSE for details.
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
#pragma once
|
||||
|
||||
// Utility class representing disjoint segments of memory
|
||||
|
||||
#include <cassert>
|
||||
#include <map>
|
||||
|
||||
// The type used to represent address ranges. This is essentially a std::pair,
|
||||
// but we need a operator< custom for the internal map.
|
||||
template <typename addr_t>
|
||||
struct AddrRange {
|
||||
addr_t lo, hi;
|
||||
};
|
||||
|
||||
template <typename addr_t>
|
||||
bool operator<(const AddrRange<addr_t> &a, const AddrRange<addr_t> &b) {
|
||||
return a.lo < b.lo;
|
||||
}
|
||||
|
||||
template <typename addr_t, typename val_t>
|
||||
class RangedMap {
|
||||
public:
|
||||
using rng_t = AddrRange<addr_t>;
|
||||
|
||||
// A function used to merge overlapping segments. When called by
|
||||
// Emplace(), val1 will be the newer value and val0 will be the
|
||||
// older.
|
||||
typedef val_t (*MergeFun)(const rng_t &rng0, val_t &&val0, const rng_t &rng1,
|
||||
val_t &&val1);
|
||||
|
||||
// Insert an entry that covers the address range [min_addr, max_addr]
|
||||
// (inclusive) with value val.
|
||||
void Emplace(addr_t min_addr, addr_t max_addr, val_t &&new_val,
|
||||
MergeFun merge) {
|
||||
assert(min_addr <= max_addr);
|
||||
|
||||
// Construct hit_lo / hit_hi, a pair of iterators that bound the
|
||||
// segments that touch the new value.
|
||||
auto hit_lo = map_.end();
|
||||
auto hit_hi = map_.end();
|
||||
|
||||
rng_t rng = {.lo = min_addr, .hi = max_addr};
|
||||
|
||||
if (!map_.empty()) {
|
||||
// Start by finding the first region that starts strictly above min_addr.
|
||||
auto right_it = map_.upper_bound(rng);
|
||||
hit_hi = right_it;
|
||||
|
||||
// If hit_hi is map_.end(), every region starts at or below min_addr. If
|
||||
// not, the region it points to overlaps with the range if hit_hi->first
|
||||
// <= max_addr. Increment hit_hi until we get to the end or are no longer
|
||||
// overlapping. The result is the end iterator for our range.
|
||||
while (hit_hi != map_.end() && hit_hi->first.lo <= max_addr) {
|
||||
++hit_hi;
|
||||
}
|
||||
|
||||
// Now we need to find the low end of the range. Start at right_it and
|
||||
// decrement while we've not got to the beginning and while the previous
|
||||
// iterator has a top >= min_addr.
|
||||
hit_lo = right_it;
|
||||
while (hit_lo != map_.begin() &&
|
||||
min_addr <= std::prev(hit_lo)->first.hi) {
|
||||
--hit_lo;
|
||||
}
|
||||
}
|
||||
|
||||
// The entry is disjoint from all others iff hit_lo == hit_hi. In which
|
||||
// case, we can just insert it.
|
||||
if (hit_lo == hit_hi) {
|
||||
map_.insert(std::make_pair(rng, std::move(new_val)));
|
||||
return;
|
||||
}
|
||||
|
||||
// Otherwise, we use the merge function to merge everything together.
|
||||
// Accumulate into a new val_t and update min_addr / max_addr as we go.
|
||||
// Peel off the 1st iteration of the loop to avoid an unnecessary move/copy
|
||||
// of new_val.
|
||||
val_t acc = merge(hit_lo->first, std::move(hit_lo->second), rng,
|
||||
std::move(new_val));
|
||||
min_addr = std::min(min_addr, hit_lo->first.lo);
|
||||
max_addr = std::max(max_addr, hit_lo->first.hi);
|
||||
|
||||
for (auto it = std::next(hit_lo); it != hit_hi; ++it) {
|
||||
rng_t rng1 = {.lo = min_addr, .hi = max_addr};
|
||||
acc = merge(it->first, std::move(it->second), rng1, std::move(acc));
|
||||
min_addr = std::min(min_addr, it->first.lo);
|
||||
max_addr = std::max(max_addr, it->first.hi);
|
||||
}
|
||||
|
||||
// We've merged everything, and have possibly trashed the values pointed to
|
||||
// by all the iterators in the range. Throw that lot away and finally
|
||||
// insert the merged result.
|
||||
map_.erase(hit_lo, hit_hi);
|
||||
rng_t rng1 = {.lo = min_addr, .hi = max_addr};
|
||||
map_.insert(std::make_pair(rng1, std::move(acc)));
|
||||
}
|
||||
|
||||
// Try to insert an entry that covers the address range [min_addr, max_addr]
|
||||
// (inclusive) with value val.
|
||||
//
|
||||
// If there is an existing entry that overlaps with the range on either side,
|
||||
// the map and val are unchanged and a pointer to the existing entry is
|
||||
// returned. Otherwise, returns nullptr.
|
||||
const val_t *EmplaceDisjoint(addr_t min_addr, addr_t max_addr, val_t &&val) {
|
||||
assert(min_addr <= max_addr);
|
||||
rng_t rng = {.lo = min_addr, .hi = max_addr};
|
||||
|
||||
if (!map_.empty()) {
|
||||
// We start by checking for an overlap "from the right". This would be a
|
||||
// region that starts strictly above min_addr, but where it's low address
|
||||
// is still <= max_addr. We can use std::map::upper_bound to find the
|
||||
// first region strictly above min_addr (which returns the end iterator
|
||||
// if there isn't one).
|
||||
auto right_it = map_.upper_bound(rng);
|
||||
if (right_it != map_.end()) {
|
||||
addr_t right_min = right_it->first.lo;
|
||||
if (right_min <= max_addr) {
|
||||
return &right_it->second;
|
||||
}
|
||||
}
|
||||
|
||||
// We also need to check from the left side. This would be a region that
|
||||
// starts at or before min_addr and extends past it. If right_it is
|
||||
// mem_.begin(), there is no such region (because the lowest addressed
|
||||
// region already starts above min_addr). Otherwise, decrement right_it
|
||||
// to get the highest addressed region that starts at or before min_addr.
|
||||
// Note this still works if right_it is the end iterator: we just pick up
|
||||
// the last region, which we know exists because map_ is not empty.
|
||||
if (right_it != map_.begin()) {
|
||||
auto left_it = std::prev(right_it);
|
||||
addr_t left_max = left_it->first.hi;
|
||||
|
||||
if (min_addr <= left_max) {
|
||||
return &left_it->second;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Phew, no overlap!
|
||||
map_.insert(std::make_pair(rng, std::move(val)));
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
// Iteration interface
|
||||
using map_t = std::map<rng_t, val_t>;
|
||||
using const_iterator = typename map_t::const_iterator;
|
||||
|
||||
const_iterator begin() const { return const_iterator(map_.begin()); }
|
||||
const_iterator end() const { return const_iterator(map_.end()); }
|
||||
size_t size() const { return map_.size(); }
|
||||
|
||||
// Try to find an entry hitting the given address. Returns end() if there is
|
||||
// none.
|
||||
const_iterator find(addr_t addr) const {
|
||||
// To find the entry containing addr, use upper_bound to find the first
|
||||
// region strictly after it, and then std::prev to step backwards. This
|
||||
// fails if either the map is empty (obviously!) or if ub_it is already the
|
||||
// beginning of the map.
|
||||
if (map_.empty())
|
||||
return end();
|
||||
|
||||
rng_t diag = {.lo = addr, .hi = addr};
|
||||
auto it = map_.upper_bound(diag);
|
||||
if (it == map_.begin())
|
||||
return end();
|
||||
|
||||
--it;
|
||||
|
||||
// At this point, it will point at the right region if there is one. We
|
||||
// know that it->first.lo <= addr (because of how upper_bound works). We
|
||||
// now just need to check that addr <= it->first.hi.
|
||||
return (addr <= it->first.hi) ? const_iterator(it) : end();
|
||||
}
|
||||
|
||||
private:
|
||||
std::map<rng_t, val_t> map_;
|
||||
};
|
95
vendor/lowrisc_ip/dv/verilator/cpp/sv_scoped.cc
vendored
Normal file
95
vendor/lowrisc_ip/dv/verilator/cpp/sv_scoped.cc
vendored
Normal file
|
@ -0,0 +1,95 @@
|
|||
// Copyright lowRISC contributors.
|
||||
// Licensed under the Apache License, Version 2.0, see LICENSE for details.
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
#include "sv_scoped.h"
|
||||
|
||||
#include <cassert>
|
||||
#include <sstream>
|
||||
|
||||
// Set scope by name, returning the old scope. If the name doesn't describe a
|
||||
// valid scope, throw an SVScoped::Error.
|
||||
static svScope SetAbsScope(const std::string &name) {
|
||||
svScope new_scope = svGetScopeFromName(name.c_str());
|
||||
if (!new_scope)
|
||||
throw SVScoped::Error(name);
|
||||
return svSetScope(new_scope);
|
||||
}
|
||||
|
||||
// Resolve name to a scope, using the rules described in the comment above the
|
||||
// class in sv_scoped.h, and then set it. Returns the old scope.
|
||||
static svScope SetRelScope(const std::string &name) {
|
||||
// Absolute (or empty) names resolve to themselves
|
||||
if (name[0] != '.') {
|
||||
return SetAbsScope(name);
|
||||
}
|
||||
|
||||
svScope prev_scope = svGetScope();
|
||||
|
||||
// Special case: If name is ".", it means to use the current scope. Rather
|
||||
// than changing scope, we can just return where we are going to stay.
|
||||
if (name == ".")
|
||||
return prev_scope;
|
||||
|
||||
// For anything else, count how many dots appear after the first one (so
|
||||
// ..foo gives an up_count of 1; ...bar gives an up_count of 2).
|
||||
size_t first_not_dot = name.find_first_not_of('.', 1);
|
||||
if (first_not_dot == std::string::npos) {
|
||||
// name looks like "....": that's fine, it just means to go up some number
|
||||
// of steps from the current position and not down again. Amend
|
||||
// first_not_dot to point at the '\0'.
|
||||
first_not_dot = name.size();
|
||||
}
|
||||
size_t up_count = first_not_dot - 1;
|
||||
|
||||
// Get the name of the current scope, so that we can perform surgery.
|
||||
std::string scope_name = svGetNameFromScope(prev_scope);
|
||||
|
||||
// scope_name will look something like "TOP.foo.bar". Search up_count
|
||||
// dots from the end, setting last_dot to point at the last dot that should
|
||||
// appear in the resolved name.
|
||||
//
|
||||
// If up_count is too large, behave like "cd /; cd .." and stop at the
|
||||
// left-most dot.
|
||||
size_t last_dot = scope_name.size();
|
||||
for (size_t i = 0; i < up_count; ++i) {
|
||||
// This shouldn't ever trigger (because it would mean scope_name was
|
||||
// either empty or started with a "."), but allowing it makes the code a
|
||||
// bit more uniform.
|
||||
if (last_dot == 0)
|
||||
break;
|
||||
|
||||
size_t dot = scope_name.rfind('.', last_dot - 1);
|
||||
if (dot == std::string::npos)
|
||||
break;
|
||||
|
||||
last_dot = dot;
|
||||
}
|
||||
|
||||
// Delete everything from last_dot onwards. If we are actually pointing at a
|
||||
// dot, this will do something like "TOP.foo.bar" -> "TOP.foo". If up_count
|
||||
// was zero or there were no dots, last_dot will equal the size of the string
|
||||
// (which means, conveniently, that erase is a no-op).
|
||||
scope_name.erase(last_dot);
|
||||
|
||||
// If first_not_dot points inside name (so name looked like "..foo.bar"
|
||||
// rather than "..."), subtract one to point at the last dot of the initial
|
||||
// segment (we know there is one because name[0] == '.') and then append
|
||||
// everything to scope_name starting from there. For example, if scope_name
|
||||
// was "TOP.foo.bar.baz" and name was "..qux", we will have just amended
|
||||
// scope_name to be "TOP.foo.bar". Now we want to add ".qux".
|
||||
if (first_not_dot < name.size()) {
|
||||
scope_name.append(name, first_not_dot - 1, std::string::npos);
|
||||
}
|
||||
|
||||
return SetAbsScope(scope_name);
|
||||
}
|
||||
|
||||
SVScoped::SVScoped(const std::string &name) : prev_scope_(SetRelScope(name)) {}
|
||||
|
||||
SVScoped::Error::Error(const std::string &scope_name)
|
||||
: scope_name_(scope_name) {
|
||||
std::ostringstream oss;
|
||||
oss << "No such SystemVerilog scope: `" << scope_name << "'.";
|
||||
msg_ = oss.str();
|
||||
}
|
51
vendor/lowrisc_ip/dv/verilator/cpp/sv_scoped.h
vendored
Normal file
51
vendor/lowrisc_ip/dv/verilator/cpp/sv_scoped.h
vendored
Normal file
|
@ -0,0 +1,51 @@
|
|||
// Copyright lowRISC contributors.
|
||||
// Licensed under the Apache License, Version 2.0, see LICENSE for details.
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
#ifndef OPENTITAN_HW_DV_VERILATOR_CPP_SV_SCOPED_H_
|
||||
#define OPENTITAN_HW_DV_VERILATOR_CPP_SV_SCOPED_H_
|
||||
|
||||
#include <stdexcept>
|
||||
#include <string>
|
||||
#include <svdpi.h>
|
||||
|
||||
/**
|
||||
* Wrapper and guard class for SV Scope
|
||||
*
|
||||
* Call the constructor with a string for the scope that should be used. If
|
||||
* this string starts with '.', it is taken to be a name relative to the
|
||||
* current scope. Multiple periods at the start of the string move up in the
|
||||
* tree (like Python imports).
|
||||
*
|
||||
* If the resolved scope cannot be found, the constructor leaves the current
|
||||
* scope unchanged and throws an SVScoped::Error detailing the non-existent
|
||||
* scope it tried to use.
|
||||
*
|
||||
* For example, if the current scope has name "TOP.foo.bar", then the string
|
||||
* ".baz" resolves to the scope with name "TOP.foo.bar.baz". The string "..baz"
|
||||
* resolves to the scope with name "TOP.foo.baz". The string "qux" resolves to
|
||||
* the scope with name "qux".
|
||||
*
|
||||
* This guard restores the previous scope at destruction.
|
||||
*/
|
||||
class SVScoped {
|
||||
public:
|
||||
SVScoped(const std::string &name);
|
||||
~SVScoped() { svSetScope(prev_scope_); }
|
||||
|
||||
class Error : public std::exception {
|
||||
public:
|
||||
Error(const std::string &scope_name);
|
||||
const char *what() const noexcept override { return msg_.c_str(); }
|
||||
|
||||
std::string scope_name_;
|
||||
|
||||
private:
|
||||
std::string msg_;
|
||||
};
|
||||
|
||||
private:
|
||||
svScope prev_scope_;
|
||||
};
|
||||
|
||||
#endif // OPENTITAN_HW_DV_VERILATOR_CPP_SV_SCOPED_H_
|
|
@ -4,58 +4,92 @@
|
|||
|
||||
#include "verilator_memutil.h"
|
||||
|
||||
#include <fcntl.h>
|
||||
#include <gelf.h>
|
||||
#include <getopt.h>
|
||||
#include <libelf.h>
|
||||
#include <sys/stat.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include <array>
|
||||
#include <cassert>
|
||||
#include <cstring>
|
||||
#include <getopt.h>
|
||||
#include <iostream>
|
||||
#include <list>
|
||||
#include <sstream>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
// DPI Exports
|
||||
extern "C" {
|
||||
namespace {
|
||||
// An instruction to load the file at filepath to the memory called name. If
|
||||
// name is the empty string then type must be kMemImageElf and this is an
|
||||
// instruction to load an ELF file, picking memories by LMA.
|
||||
struct LoadArg {
|
||||
std::string name;
|
||||
std::string filepath;
|
||||
MemImageType type;
|
||||
};
|
||||
} // namespace
|
||||
|
||||
/**
|
||||
* Write |file| to a memory
|
||||
*
|
||||
* @param file path to a SystemVerilog $readmemh()-compatible file (VMEM file)
|
||||
*/
|
||||
extern void simutil_verilator_memload(const char *file);
|
||||
// Parse a meminit command-line argument. This should be of the form
|
||||
// mem_area,file[,type]. Throw a std::runtime_error if something looks wrong.
|
||||
static LoadArg ParseMemArg(std::string mem_argument) {
|
||||
std::array<std::string, 3> args;
|
||||
size_t pos = 0;
|
||||
size_t end_pos = 0;
|
||||
size_t i;
|
||||
|
||||
/**
|
||||
* Write a 32 bit word |val| to memory at index |index|
|
||||
*
|
||||
* @return 1 if successful, 0 otherwise
|
||||
*/
|
||||
extern int simutil_verilator_set_mem(int index, const svBitVecVal *val);
|
||||
}
|
||||
|
||||
bool VerilatorMemUtil::RegisterMemoryArea(const std::string name,
|
||||
const std::string location) {
|
||||
// Default to 32bit width
|
||||
return RegisterMemoryArea(name, location, 32);
|
||||
}
|
||||
|
||||
bool VerilatorMemUtil::RegisterMemoryArea(const std::string name,
|
||||
const std::string location,
|
||||
size_t width_bit) {
|
||||
MemArea mem = {.name = name, .location = location, .width_bit = width_bit};
|
||||
|
||||
assert((width_bit <= 256) &&
|
||||
"TODO: Memory loading only supported up to 256 bits.");
|
||||
|
||||
auto ret = mem_register_.emplace(name, mem);
|
||||
if (ret.second == false) {
|
||||
std::cerr << "ERROR: Can not register \"" << name << "\" at: \"" << location
|
||||
<< "\" (Previously defined at: \"" << ret.first->second.location
|
||||
<< "\")" << std::endl;
|
||||
return false;
|
||||
for (i = 0; i < 3; ++i) {
|
||||
end_pos = mem_argument.find(",", pos);
|
||||
// Check for possible exit conditions
|
||||
if (pos == end_pos) {
|
||||
std::ostringstream oss;
|
||||
oss << "empty field in: `" << mem_argument << "'.";
|
||||
throw std::runtime_error(oss.str());
|
||||
}
|
||||
if (end_pos == std::string::npos) {
|
||||
args[i] = mem_argument.substr(pos);
|
||||
break;
|
||||
}
|
||||
args[i] = mem_argument.substr(pos, end_pos - pos);
|
||||
pos = end_pos + 1;
|
||||
}
|
||||
return true;
|
||||
// mem_argument is not empty as getopt requires an argument,
|
||||
// but not a valid argument for memory initialization
|
||||
if (i == 0) {
|
||||
std::ostringstream oss;
|
||||
oss << "meminit must be in the format `name,file[,type]'. Got: `"
|
||||
<< mem_argument << "'.";
|
||||
throw std::runtime_error(oss.str());
|
||||
}
|
||||
|
||||
const char *str_type = (2 <= i) ? args[2].c_str() : nullptr;
|
||||
MemImageType type = DpiMemUtil::GetMemImageType(args[1], str_type);
|
||||
|
||||
return {.name = args[0], .filepath = args[1], .type = type};
|
||||
}
|
||||
|
||||
// Print a usage message to stdout
|
||||
static void PrintHelp() {
|
||||
std::cout << "Simulation memory utilities:\n\n"
|
||||
"-r|--rominit=FILE\n"
|
||||
" Initialize the ROM with FILE (elf/vmem)\n\n"
|
||||
"-m|--raminit=FILE\n"
|
||||
" Initialize the RAM with FILE (elf/vmem)\n\n"
|
||||
"-f|--flashinit=FILE\n"
|
||||
" Initialize the FLASH with FILE (elf/vmem)\n\n"
|
||||
"-l|--meminit=NAME,FILE[,TYPE]\n"
|
||||
" Initialize memory region NAME with FILE [of TYPE]\n"
|
||||
" TYPE is either 'elf' or 'vmem'\n\n"
|
||||
"-E|--load-elf=FILE\n"
|
||||
" Load ELF file, using segment LMAs to pick memory regions\n\n"
|
||||
"-l list|--meminit=list\n"
|
||||
" Print registered memory regions\n\n"
|
||||
"--verbose-mem-load\n"
|
||||
" Print a message for each memory load\n\n"
|
||||
"-h|--help\n"
|
||||
" Show help\n\n";
|
||||
}
|
||||
|
||||
VerilatorMemUtil::VerilatorMemUtil() : allocation_(new DpiMemUtil()) {
|
||||
mem_util_ = allocation_.get();
|
||||
}
|
||||
|
||||
VerilatorMemUtil::VerilatorMemUtil(DpiMemUtil *mem_util) : mem_util_(mem_util) {
|
||||
assert(mem_util);
|
||||
}
|
||||
|
||||
bool VerilatorMemUtil::ParseCLIArguments(int argc, char **argv,
|
||||
|
@ -65,14 +99,19 @@ bool VerilatorMemUtil::ParseCLIArguments(int argc, char **argv,
|
|||
{"raminit", required_argument, nullptr, 'm'},
|
||||
{"flashinit", required_argument, nullptr, 'f'},
|
||||
{"meminit", required_argument, nullptr, 'l'},
|
||||
{"verbose-mem-load", no_argument, nullptr, 'V'},
|
||||
{"load-elf", required_argument, nullptr, 'E'},
|
||||
{"help", no_argument, nullptr, 'h'},
|
||||
{nullptr, no_argument, nullptr, 0}};
|
||||
|
||||
std::vector<LoadArg> load_args;
|
||||
bool verbose = false;
|
||||
|
||||
// Reset the command parsing index in-case other utils have already parsed
|
||||
// some arguments
|
||||
optind = 1;
|
||||
while (1) {
|
||||
int c = getopt_long(argc, argv, ":r:m:f:l:h", long_options, nullptr);
|
||||
int c = getopt_long(argc, argv, ":r:m:f:l:E:h", long_options, nullptr);
|
||||
if (c == -1) {
|
||||
break;
|
||||
}
|
||||
|
@ -84,43 +123,39 @@ bool VerilatorMemUtil::ParseCLIArguments(int argc, char **argv,
|
|||
case 0:
|
||||
break;
|
||||
case 'r':
|
||||
if (!MemWrite("rom", optarg)) {
|
||||
std::cerr << "ERROR: Unable to initialize memory." << std::endl;
|
||||
return false;
|
||||
}
|
||||
load_args.push_back(
|
||||
{.name = "rom", .filepath = optarg, .type = kMemImageUnknown});
|
||||
break;
|
||||
case 'm':
|
||||
if (!MemWrite("ram", optarg)) {
|
||||
std::cerr << "ERROR: Unable to initialize memory." << std::endl;
|
||||
return false;
|
||||
}
|
||||
load_args.push_back(
|
||||
{.name = "ram", .filepath = optarg, .type = kMemImageUnknown});
|
||||
break;
|
||||
case 'f':
|
||||
if (!MemWrite("flash", optarg)) {
|
||||
std::cerr << "ERROR: Unable to initialize memory." << std::endl;
|
||||
return false;
|
||||
}
|
||||
load_args.push_back(
|
||||
{.name = "flash", .filepath = optarg, .type = kMemImageUnknown});
|
||||
break;
|
||||
case 'l': {
|
||||
case 'l':
|
||||
if (strcasecmp(optarg, "list") == 0) {
|
||||
PrintMemRegions();
|
||||
mem_util_->PrintMemRegions();
|
||||
exit_app = true;
|
||||
return true;
|
||||
}
|
||||
|
||||
std::string name;
|
||||
std::string filepath;
|
||||
MemImageType type;
|
||||
if (!ParseMemArg(optarg, name, filepath, type)) {
|
||||
std::cerr << "ERROR: Unable to parse meminit arguments." << std::endl;
|
||||
// --meminit / -l
|
||||
try {
|
||||
load_args.emplace_back(ParseMemArg(optarg));
|
||||
} catch (const std::runtime_error &err) {
|
||||
std::cerr << "ERROR: " << err.what() << std::endl;
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!MemWrite(name, filepath, type)) {
|
||||
std::cerr << "ERROR: Unable to initialize memory." << std::endl;
|
||||
return false;
|
||||
}
|
||||
} break;
|
||||
break;
|
||||
case 'V':
|
||||
verbose = true;
|
||||
break;
|
||||
case 'E':
|
||||
load_args.push_back(
|
||||
{.name = "", .filepath = optarg, .type = kMemImageElf});
|
||||
break;
|
||||
case 'h':
|
||||
PrintHelp();
|
||||
return true;
|
||||
|
@ -134,339 +169,20 @@ bool VerilatorMemUtil::ParseCLIArguments(int argc, char **argv,
|
|||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void VerilatorMemUtil::PrintMemRegions() const {
|
||||
std::cout << "Registered memory regions:" << std::endl;
|
||||
for (const auto &m : mem_register_) {
|
||||
std::cout << "\t'" << m.second.name << "' (" << m.second.width_bit
|
||||
<< "bits) at location: '" << m.second.location << "'"
|
||||
<< std::endl;
|
||||
}
|
||||
}
|
||||
|
||||
void VerilatorMemUtil::PrintHelp() const {
|
||||
std::cout << "Simulation memory utilities:\n\n"
|
||||
"-r|--rominit=FILE\n"
|
||||
" Initialize the ROM with FILE (elf/vmem)\n\n"
|
||||
"-m|--raminit=FILE\n"
|
||||
" Initialize the RAM with FILE (elf/vmem)\n\n"
|
||||
"-f|--flashinit=FILE\n"
|
||||
" Initialize the FLASH with FILE (elf/vmem)\n\n"
|
||||
"-l|--meminit=NAME,FILE[,TYPE]\n"
|
||||
" Initialize memory region NAME with FILE [of TYPE]\n"
|
||||
" TYPE is either 'elf' or 'vmem'\n\n"
|
||||
"-l list|--meminit=list\n"
|
||||
" Print registered memory regions\n\n"
|
||||
"-h|--help\n"
|
||||
" Show help\n\n";
|
||||
}
|
||||
|
||||
bool VerilatorMemUtil::ParseMemArg(std::string mem_argument, std::string &name,
|
||||
std::string &filepath, MemImageType &type) {
|
||||
std::array<std::string, 3> args;
|
||||
size_t pos = 0;
|
||||
size_t end_pos = 0;
|
||||
size_t i;
|
||||
|
||||
for (i = 0; i < 3; ++i) {
|
||||
end_pos = mem_argument.find(",", pos);
|
||||
// Check for possible exit conditions
|
||||
if (pos == end_pos) {
|
||||
std::cerr << "ERROR: empty field in: " << mem_argument << std::endl;
|
||||
for (const LoadArg &arg : load_args) {
|
||||
try {
|
||||
if (!arg.name.empty()) {
|
||||
mem_util_->LoadFileToNamedMem(verbose, arg.name, arg.filepath,
|
||||
arg.type);
|
||||
} else {
|
||||
assert(arg.type == kMemImageElf);
|
||||
mem_util_->LoadElfToMemories(verbose, arg.filepath);
|
||||
}
|
||||
} catch (const std::exception &err) {
|
||||
std::cerr << "ERROR: " << err.what() << std::endl;
|
||||
return false;
|
||||
}
|
||||
if (end_pos == std::string::npos) {
|
||||
args[i] = mem_argument.substr(pos);
|
||||
break;
|
||||
}
|
||||
args[i] = mem_argument.substr(pos, end_pos - pos);
|
||||
pos = end_pos + 1;
|
||||
}
|
||||
// mem_argument is not empty as getopt requires an argument,
|
||||
// but not a valid argument for memory initialization
|
||||
if (i == 0) {
|
||||
std::cerr << "ERROR: meminit must be in \"name,file[,type]\""
|
||||
<< " got: " << mem_argument << std::endl;
|
||||
return false;
|
||||
}
|
||||
|
||||
name = args[0];
|
||||
filepath = args[1];
|
||||
|
||||
if (i == 1) {
|
||||
// Type not set explicitly
|
||||
type = DetectMemImageType(filepath);
|
||||
} else {
|
||||
type = GetMemImageTypeByName(args[2]);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
MemImageType VerilatorMemUtil::DetectMemImageType(const std::string filepath) {
|
||||
size_t ext_pos = filepath.find_last_of(".");
|
||||
std::string ext = filepath.substr(ext_pos + 1);
|
||||
|
||||
if (ext_pos == std::string::npos) {
|
||||
// Assume ELF files if no file extension is given.
|
||||
// TODO: Make this more robust by actually checking the file contents.
|
||||
return kMemImageElf;
|
||||
}
|
||||
return GetMemImageTypeByName(ext);
|
||||
}
|
||||
|
||||
MemImageType VerilatorMemUtil::GetMemImageTypeByName(const std::string name) {
|
||||
if (name.compare("elf") == 0) {
|
||||
return kMemImageElf;
|
||||
}
|
||||
if (name.compare("vmem") == 0) {
|
||||
return kMemImageVmem;
|
||||
}
|
||||
return kMemImageUnknown;
|
||||
}
|
||||
|
||||
bool VerilatorMemUtil::IsFileReadable(std::string filepath) const {
|
||||
struct stat statbuf;
|
||||
return stat(filepath.data(), &statbuf) == 0;
|
||||
}
|
||||
|
||||
bool VerilatorMemUtil::ElfFileToBinary(const std::string &filepath,
|
||||
uint8_t **data,
|
||||
size_t &len_bytes) const {
|
||||
uint8_t *buf;
|
||||
bool retval, any = false;
|
||||
GElf_Phdr phdr;
|
||||
GElf_Addr high = 0;
|
||||
GElf_Addr low = (GElf_Addr)-1;
|
||||
Elf_Data *elf_data;
|
||||
size_t i;
|
||||
|
||||
(void)elf_errno();
|
||||
len_bytes = 0;
|
||||
|
||||
if (elf_version(EV_CURRENT) == EV_NONE) {
|
||||
std::cerr << elf_errmsg(-1) << std::endl;
|
||||
return false;
|
||||
}
|
||||
|
||||
int fd = open(filepath.c_str(), O_RDONLY, 0);
|
||||
if (fd < 0) {
|
||||
std::cerr << "Could not open file: " << filepath << std::endl;
|
||||
return false;
|
||||
}
|
||||
|
||||
Elf *elf_desc;
|
||||
elf_desc = elf_begin(fd, ELF_C_READ, NULL);
|
||||
if (elf_desc == NULL) {
|
||||
std::cerr << elf_errmsg(-1) << " in: " << filepath << std::endl;
|
||||
retval = false;
|
||||
goto return_fd_end;
|
||||
}
|
||||
if (elf_kind(elf_desc) != ELF_K_ELF) {
|
||||
std::cerr << "Not a ELF file: " << filepath << std::endl;
|
||||
retval = false;
|
||||
goto return_elf_end;
|
||||
}
|
||||
// TODO: add support for ELFCLASS64
|
||||
if (gelf_getclass(elf_desc) != ELFCLASS32) {
|
||||
std::cerr << "Not a 32-bit ELF file: " << filepath << std::endl;
|
||||
retval = false;
|
||||
goto return_elf_end;
|
||||
}
|
||||
|
||||
size_t phnum;
|
||||
if (elf_getphdrnum(elf_desc, &phnum) != 0) {
|
||||
std::cerr << elf_errmsg(-1) << " in: " << filepath << std::endl;
|
||||
retval = false;
|
||||
goto return_elf_end;
|
||||
}
|
||||
|
||||
//
|
||||
// To mimic what objcopy does (that is, the binary target of BFD), we need to
|
||||
// iterate over all loadable program headers, find the lowest address, and
|
||||
// then copy in our loadable sections based on their offset with respect to
|
||||
// the found base address.
|
||||
//
|
||||
for (i = 0; i < phnum; i++) {
|
||||
if (gelf_getphdr(elf_desc, i, &phdr) == NULL) {
|
||||
std::cerr << elf_errmsg(-1) << " segment number: " << i
|
||||
<< " in: " << filepath << std::endl;
|
||||
retval = false;
|
||||
goto return_elf_end;
|
||||
}
|
||||
|
||||
if (phdr.p_type != PT_LOAD) {
|
||||
std::cout << "Program header number " << i << " is not of type PT_LOAD; "
|
||||
<< "ignoring." << std::endl;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (phdr.p_filesz == 0) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!any || phdr.p_paddr < low) {
|
||||
low = phdr.p_paddr;
|
||||
}
|
||||
|
||||
if (!any || phdr.p_paddr + phdr.p_filesz > high) {
|
||||
high = phdr.p_paddr + phdr.p_filesz;
|
||||
}
|
||||
|
||||
any = true;
|
||||
}
|
||||
|
||||
len_bytes = high - low;
|
||||
buf = (uint8_t *)malloc(len_bytes);
|
||||
assert(buf != NULL);
|
||||
|
||||
for (i = 0; i < phnum; i++) {
|
||||
(void)gelf_getphdr(elf_desc, i, &phdr);
|
||||
|
||||
if (phdr.p_type != PT_LOAD || phdr.p_filesz == 0) {
|
||||
continue;
|
||||
}
|
||||
|
||||
elf_data = elf_getdata_rawchunk(elf_desc, phdr.p_offset, phdr.p_filesz,
|
||||
ELF_T_BYTE);
|
||||
|
||||
if (elf_data == NULL) {
|
||||
retval = false;
|
||||
free(buf);
|
||||
goto return_elf_end;
|
||||
}
|
||||
|
||||
memcpy(&buf[phdr.p_paddr - low], (uint8_t *)elf_data->d_buf,
|
||||
elf_data->d_size);
|
||||
}
|
||||
|
||||
*data = buf;
|
||||
retval = true;
|
||||
|
||||
return_elf_end:
|
||||
elf_end(elf_desc);
|
||||
return_fd_end:
|
||||
close(fd);
|
||||
return retval;
|
||||
}
|
||||
|
||||
bool VerilatorMemUtil::MemWrite(const std::string &name,
|
||||
const std::string &filepath) {
|
||||
MemImageType type = DetectMemImageType(filepath);
|
||||
if (type == kMemImageUnknown) {
|
||||
std::cerr << "ERROR: Unable to detect file type for: " << filepath
|
||||
<< std::endl;
|
||||
// Continuing for more error messages
|
||||
}
|
||||
return MemWrite(name, filepath, type);
|
||||
}
|
||||
|
||||
bool VerilatorMemUtil::MemWrite(const std::string &name,
|
||||
const std::string &filepath,
|
||||
MemImageType type) {
|
||||
// Search for corresponding registered memory based on the name
|
||||
auto it = mem_register_.find(name);
|
||||
if (it == mem_register_.end()) {
|
||||
std::cerr << "ERROR: Memory location not set for: '" << name << "'"
|
||||
<< std::endl;
|
||||
PrintMemRegions();
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!MemWrite(it->second, filepath, type)) {
|
||||
std::cerr << "ERROR: Setting memory '" << name << "' failed." << std::endl;
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool VerilatorMemUtil::MemWrite(const MemArea &m, const std::string &filepath,
|
||||
MemImageType type) {
|
||||
if (!IsFileReadable(filepath)) {
|
||||
std::cerr << "ERROR: Memory initialization file "
|
||||
<< "'" << filepath << "'"
|
||||
<< " is not readable." << std::endl;
|
||||
return false;
|
||||
}
|
||||
|
||||
svScope scope = svGetScopeFromName(m.location.data());
|
||||
if (!scope) {
|
||||
std::cerr << "ERROR: No memory found at " << m.location << std::endl;
|
||||
return false;
|
||||
}
|
||||
|
||||
if ((m.width_bit % 8) != 0) {
|
||||
std::cerr << "ERROR: width for: " << m.name
|
||||
<< "must be a multiple of 8 (was : " << m.width_bit << ")"
|
||||
<< std::endl;
|
||||
return false;
|
||||
}
|
||||
size_t size_byte = m.width_bit / 8;
|
||||
|
||||
switch (type) {
|
||||
case kMemImageElf:
|
||||
if (!WriteElfToMem(scope, filepath, size_byte)) {
|
||||
std::cerr << "ERROR: Writing ELF file to memory \"" << m.name << "\" ("
|
||||
<< m.location << ") failed." << std::endl;
|
||||
return false;
|
||||
}
|
||||
break;
|
||||
case kMemImageVmem:
|
||||
if (!WriteVmemToMem(scope, filepath)) {
|
||||
std::cerr << "ERROR: Writing VMEM file to memory \"" << m.name << "\" ("
|
||||
<< m.location << ") failed." << std::endl;
|
||||
return false;
|
||||
}
|
||||
break;
|
||||
case kMemImageUnknown:
|
||||
default:
|
||||
std::cerr << "ERROR: Unknown file type for " << m.name << std::endl;
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool VerilatorMemUtil::WriteElfToMem(const svScope &scope,
|
||||
const std::string &filepath,
|
||||
size_t size_byte) {
|
||||
bool retcode;
|
||||
svScope prev_scope = svSetScope(scope);
|
||||
|
||||
uint8_t *buf = nullptr;
|
||||
size_t len_bytes;
|
||||
|
||||
if (!ElfFileToBinary(filepath, &buf, len_bytes)) {
|
||||
std::cerr << "ERROR: Could not load: " << filepath << std::endl;
|
||||
retcode = false;
|
||||
goto ret;
|
||||
}
|
||||
for (int i = 0; i < (len_bytes + size_byte - 1) / size_byte; ++i) {
|
||||
if (!simutil_verilator_set_mem(i, (svBitVecVal *)&buf[size_byte * i])) {
|
||||
std::cerr << "ERROR: Could not set memory byte: " << i * size_byte << "/"
|
||||
<< len_bytes << "" << std::endl;
|
||||
|
||||
retcode = false;
|
||||
goto ret;
|
||||
}
|
||||
}
|
||||
|
||||
retcode = true;
|
||||
|
||||
ret:
|
||||
svSetScope(prev_scope);
|
||||
free(buf);
|
||||
return retcode;
|
||||
}
|
||||
|
||||
bool VerilatorMemUtil::WriteVmemToMem(const svScope &scope,
|
||||
const std::string &filepath) {
|
||||
svScope prev_scope = svSetScope(scope);
|
||||
|
||||
// TODO: Add error handling.
|
||||
simutil_verilator_memload(filepath.data());
|
||||
|
||||
svSetScope(prev_scope);
|
||||
return true;
|
||||
}
|
||||
|
|
|
@ -2,114 +2,41 @@
|
|||
// Licensed under the Apache License, Version 2.0, see LICENSE for details.
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
#ifndef OPENTITAN_HW_DV_VERILATOR_CPP_VERILATOR_MEMUTIL_H_
|
||||
#define OPENTITAN_HW_DV_VERILATOR_CPP_VERILATOR_MEMUTIL_H_
|
||||
#pragma once
|
||||
|
||||
//
|
||||
// A wrapper class that converts a VerilatorMemutil into a SimCtrlExtension
|
||||
//
|
||||
|
||||
#include <memory>
|
||||
|
||||
#include "dpi_memutil.h"
|
||||
#include "sim_ctrl_extension.h"
|
||||
|
||||
#include <vltstd/svdpi.h>
|
||||
|
||||
#include <map>
|
||||
#include <string>
|
||||
|
||||
enum MemImageType {
|
||||
kMemImageUnknown = 0,
|
||||
kMemImageElf,
|
||||
kMemImageVmem,
|
||||
};
|
||||
|
||||
struct MemArea {
|
||||
std::string name; // Unique identifier
|
||||
std::string location; // Design scope location
|
||||
size_t width_bit; // Memory width
|
||||
};
|
||||
|
||||
/**
|
||||
* Provide various memory loading utilities for Verilator simulations
|
||||
*
|
||||
* These utilities require the corresponding DPI functions:
|
||||
* simutil_verilator_memload()
|
||||
* simutil_verilator_set_mem()
|
||||
* to be defined somewhere as SystemVerilog functions.
|
||||
*/
|
||||
class VerilatorMemUtil : public SimCtrlExtension {
|
||||
public:
|
||||
/**
|
||||
* Register a memory as instantiated by generic ram
|
||||
*
|
||||
* The |name| must be a unique identifier. The function will return false
|
||||
* if |name| is already used. |location| is the path to the scope of the
|
||||
* instantiated memory, which needs to support the DPI-C interfaces
|
||||
* 'simutil_verilator_memload' and 'simutil_verilator_set_mem' used for
|
||||
* 'vmem' and 'elf' files, respectively.
|
||||
* The |width_bit| argument specifies the with in bits of the target memory
|
||||
* instance (used for packing data).
|
||||
*
|
||||
* Memories must be registered before command arguments are parsed by
|
||||
* ParseCommandArgs() in order for them to be known.
|
||||
*/
|
||||
// No-argument constructor makes a VerilatorMemUtil. Single-argument
|
||||
// constructor wraps its mem_util argument (but does not take ownership).
|
||||
VerilatorMemUtil();
|
||||
explicit VerilatorMemUtil(DpiMemUtil *mem_util);
|
||||
|
||||
// Declared in SimCtrlExtension
|
||||
bool ParseCLIArguments(int argc, char **argv, bool &exit_app) override;
|
||||
|
||||
// Get underlying DpiMemUtil object
|
||||
DpiMemUtil *GetUnderlying() { return mem_util_; }
|
||||
|
||||
// Pass-thru functions to underlying object
|
||||
bool RegisterMemoryArea(const std::string name, const std::string location,
|
||||
size_t width_bit);
|
||||
size_t width_bit, const MemAreaLoc *addr_loc) {
|
||||
return mem_util_->RegisterMemoryArea(name, location, width_bit, addr_loc);
|
||||
}
|
||||
|
||||
/**
|
||||
* Register a memory with default width (32bits)
|
||||
*/
|
||||
bool RegisterMemoryArea(const std::string name, const std::string location);
|
||||
|
||||
/**
|
||||
* Parse command line arguments
|
||||
*
|
||||
* Process all recognized command-line arguments from argc/argv.
|
||||
*
|
||||
* @param argc, argv Standard C command line arguments
|
||||
* @param exit_app Indicate that program should terminate
|
||||
* @return Return code, true == success
|
||||
*/
|
||||
virtual bool ParseCLIArguments(int argc, char **argv, bool &exit_app);
|
||||
bool RegisterMemoryArea(const std::string name, const std::string location) {
|
||||
return mem_util_->RegisterMemoryArea(name, location);
|
||||
}
|
||||
|
||||
private:
|
||||
std::map<std::string, MemArea> mem_register_;
|
||||
|
||||
/**
|
||||
* Print a list of all registered memory regions
|
||||
*
|
||||
* @see RegisterMemoryArea()
|
||||
*/
|
||||
void PrintMemRegions() const;
|
||||
|
||||
/**
|
||||
* Print help how to use this tool
|
||||
*/
|
||||
void PrintHelp() const;
|
||||
|
||||
/**
|
||||
* Parse argument section specific to memory initialization.
|
||||
*
|
||||
* Must be in the form of: name,file[,type].
|
||||
*/
|
||||
bool ParseMemArg(std::string mem_argument, std::string &name,
|
||||
std::string &filepath, MemImageType &type);
|
||||
|
||||
MemImageType DetectMemImageType(const std::string filepath);
|
||||
|
||||
MemImageType GetMemImageTypeByName(const std::string name);
|
||||
|
||||
bool IsFileReadable(std::string filepath) const;
|
||||
|
||||
/**
|
||||
* Dump an ELF file into a raw binary
|
||||
*/
|
||||
bool ElfFileToBinary(const std::string &filepath, uint8_t **data,
|
||||
size_t &len_bytes) const;
|
||||
|
||||
bool MemWrite(const std::string &name, const std::string &filepath);
|
||||
bool MemWrite(const std::string &name, const std::string &filepath,
|
||||
MemImageType type);
|
||||
bool MemWrite(const MemArea &m, const std::string &filepath,
|
||||
MemImageType type);
|
||||
bool WriteElfToMem(const svScope &scope, const std::string &filepath,
|
||||
size_t size_byte);
|
||||
bool WriteVmemToMem(const svScope &scope, const std::string &filepath);
|
||||
DpiMemUtil *mem_util_;
|
||||
std::unique_ptr<DpiMemUtil> allocation_;
|
||||
};
|
||||
|
||||
#endif // OPENTITAN_HW_DV_VERILATOR_CPP_VERILATOR_MEMUTIL_H_
|
||||
|
|
21
vendor/lowrisc_ip/dv/verilator/memutil_dpi.core
vendored
Normal file
21
vendor/lowrisc_ip/dv/verilator/memutil_dpi.core
vendored
Normal file
|
@ -0,0 +1,21 @@
|
|||
CAPI=2:
|
||||
# Copyright lowRISC contributors.
|
||||
# Licensed under the Apache License, Version 2.0, see LICENSE for details.
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
name: "lowrisc:dv_verilator:memutil_dpi"
|
||||
description: "DPI memory utilities"
|
||||
filesets:
|
||||
files_cpp:
|
||||
files:
|
||||
- cpp/ranged_map.h: { is_include_file: true }
|
||||
- cpp/dpi_memutil.cc
|
||||
- cpp/dpi_memutil.h: { is_include_file: true }
|
||||
- cpp/sv_scoped.cc
|
||||
- cpp/sv_scoped.h: { is_include_file: true }
|
||||
file_type: cppSource
|
||||
|
||||
targets:
|
||||
default:
|
||||
filesets:
|
||||
- files_cpp
|
|
@ -9,6 +9,7 @@ filesets:
|
|||
files_cpp:
|
||||
depend:
|
||||
- lowrisc:dv_verilator:simutil_verilator
|
||||
- lowrisc:dv_verilator:memutil_dpi
|
||||
files:
|
||||
- cpp/verilator_memutil.cc
|
||||
- cpp/verilator_memutil.h: { is_include_file: true }
|
||||
|
|
|
@ -22,6 +22,18 @@
|
|||
*/
|
||||
double sc_time_stamp() { return VerilatorSimCtrl::GetInstance().GetTime(); }
|
||||
|
||||
#ifdef VL_USER_STOP
|
||||
/**
|
||||
* A simulation stop was requested, e.g. through $stop() or $error()
|
||||
*
|
||||
* This function overrides Verilator's default implementation to more gracefully
|
||||
* shut down the simulation.
|
||||
*/
|
||||
void vl_stop(const char *filename, int linenum, const char *hier) VL_MT_UNSAFE {
|
||||
VerilatorSimCtrl::GetInstance().RequestStop(false);
|
||||
}
|
||||
#endif
|
||||
|
||||
VerilatorSimCtrl &VerilatorSimCtrl::GetInstance() {
|
||||
static VerilatorSimCtrl instance;
|
||||
return instance;
|
||||
|
|
67
vendor/lowrisc_ip/ip/prim/doc/prim_packer_fifo.md
vendored
Normal file
67
vendor/lowrisc_ip/ip/prim/doc/prim_packer_fifo.md
vendored
Normal file
|
@ -0,0 +1,67 @@
|
|||
---
|
||||
title: "Primitive Component: Packer FIFO"
|
||||
---
|
||||
|
||||
# Overview
|
||||
|
||||
`prim_packer_fifo` is a module that supports three modes of operation: packing,
|
||||
unpacking, and single depth FIFO modes. Packing mode is where the input
|
||||
data width is less than the output data width. Unpacking mode is where the input
|
||||
data width is greater than the output data width. Single depth FIFO is where
|
||||
the input and output data widths are the same. Because masking options are not
|
||||
supported, the larger data size must be an even multiple of the smaller size.
|
||||
The controls for this module are modeled after the `prim_fifo_sync` module,
|
||||
both in name and functional behavior.
|
||||
It is one of a set of shared primitive modules
|
||||
available for use within OpenTitan as referred to in the Comportability
|
||||
Specification section on shared primitives.
|
||||
|
||||
## Parameters
|
||||
|
||||
Name | type | Description
|
||||
-----|------|-------------
|
||||
InW | int | Input data width
|
||||
OutW | int | Output data width
|
||||
|
||||
## Signal Interfaces
|
||||
|
||||
Name | In/Out | Description
|
||||
-------------|--------|-------------
|
||||
clk_i | input | Input clock
|
||||
rst_ni | input | Input reset, negative active
|
||||
clr_i | input | Input clear, clears all internal flops.
|
||||
wvalid_i | input | Writes data into the first available position.
|
||||
wdata_i[InW] | input | Input data.
|
||||
wready_o | output | Indicates if prim_packer_fifo is able to accept data.
|
||||
rvalid_o | output | Indicates if output data is valid.
|
||||
rdata_o[OutW]| output | Output data.
|
||||
rready_i | input | Output data is popped from the FIFO.
|
||||
depth_o | output | Indicates the fullness of the FIFO.
|
||||
|
||||
# Theory of Opeations
|
||||
|
||||
```code
|
||||
/----------\
|
||||
wvalid_i | | rvalid_o
|
||||
---------->| |--------------->
|
||||
wdata_i | Flop | rdata_o
|
||||
=====/====>| FIFO |=======/=======>
|
||||
[InW] | | [OutW]
|
||||
| | depth_o
|
||||
| |--------------->
|
||||
wready_o | | rready_i
|
||||
<----------| |<---------------
|
||||
| |
|
||||
\----------/
|
||||
```
|
||||
|
||||
In pack mode, `prim_packer_fifo` accepts `InW` bits of data. On a `wvalid_i`/
|
||||
`wready_o` handshake, `wdata_i` is stored to internal registers and accumulated
|
||||
until `OutW` data has been gathered. Once the FIFO is full, a single pop (when
|
||||
rvalid_o and rready_i are coincident), will clear the data and depth values on
|
||||
the next clock cycle. The complimentary flow occurs when the`prim_packer_fifo`
|
||||
module is in unpack mode.
|
||||
|
||||
The internal register size is the greate of `InW` and `OutW` bits.
|
||||
Timing diagrams are shown in the header of the `prim_packer_fifo` module.
|
||||
|
42
vendor/lowrisc_ip/ip/prim/doc/prim_present.md
vendored
42
vendor/lowrisc_ip/ip/prim/doc/prim_present.md
vendored
|
@ -13,11 +13,14 @@ I.e., this primitive is only intended to be used as a lightweight data scramblin
|
|||
|
||||
## Parameters
|
||||
|
||||
Name | type | Description
|
||||
-------------|--------|----------------------------------------------------------
|
||||
DataWidth | int | Block size, can be 32 or 64
|
||||
KeyWidth | int | Key size, can be 64, 80 or 128
|
||||
NumRounds | int | Number of PRESENT rounds, has to be greater than 0
|
||||
Name | type | Description
|
||||
--------------|--------|----------------------------------------------------------
|
||||
DataWidth | int | Block size, can be 32 or 64
|
||||
KeyWidth | int | Key size, can be 64, 80 or 128
|
||||
NumRounds | int | Number of PRESENT rounds, has to be greater than 0
|
||||
NumPhysRounds | int | Number of physically instantiated PRESENT rounds (defaults to NumRounds)
|
||||
|
||||
Note that by setting `NumRounds = 31` and `NumPhysRounds = 1` we can construct a PRESENT primitive that is suitable for iterative evaluation of a full-round PRESENT pass, where each iteration evaluates one of the 31 rounds.
|
||||
|
||||
## Signal Interfaces
|
||||
|
||||
|
@ -25,19 +28,28 @@ Name | In/Out | Description
|
|||
-------------|--------|---------------------------------
|
||||
data_i | input | Plaintext input
|
||||
key_i | input | Key input
|
||||
idx_i | input | Round index input
|
||||
data_o | output | Output of the ciphertext
|
||||
key_o | output | Key output after keyschedule update
|
||||
idx_o | output | Round index output after keyschedule update
|
||||
|
||||
The `key_o` and `idx_o` are useful for iterative implementations where the state of the scheduled key, as well as the current round index have to be registered in between rounds.
|
||||
Note that `idx_i` should be initialized to 1 for encryption mode, and to `NumRounds` for decryption mode.
|
||||
|
||||
# Theory of Operations
|
||||
|
||||
```
|
||||
/---------------\
|
||||
| |
|
||||
idx_i | | idx_o
|
||||
=====/======>| |=====/======>
|
||||
[5] | | [5]
|
||||
| PRESENT |
|
||||
key_i | |
|
||||
=====/======>| DataWidth |
|
||||
[KeyWidth] | KeyWidth |
|
||||
key_i | | key_o
|
||||
=====/======>| DataWidth |=====/======>
|
||||
[KeyWidth] | KeyWidth | [KeyWidth]
|
||||
| NumRounds |
|
||||
data_i | | data_o
|
||||
data_i | NumPhysRounds | data_o
|
||||
=====/======>| |=====/=======>
|
||||
[DataWidth] | | [DataWidth]
|
||||
| |
|
||||
|
@ -49,21 +61,25 @@ The only inputs are the key and the plaintext, and the only output is the cipher
|
|||
|
||||
The internal construction follows the the algorithm described in the original [paper](http://www.lightweightcrypto.org/present/present_ches2007.pdf).
|
||||
The block size is 64bit and the key size can be either 80bit or 128bit, depending on the security requirements.
|
||||
In its original formulation, this cipher has 31 rounds comprised of an XOR operation with a round key, followed by the application of an s-box and a permutation layer:
|
||||
In its original formulation, this cipher has 31 rounds comprised of an XOR operation with a round key, followed by the application of an s-box and a permutation layer, as illustrated for the encryption pass below:
|
||||
|
||||
```c++
|
||||
NumRounds = 32;
|
||||
idx_i = 1;
|
||||
|
||||
round_keys = key_derivation(key_i);
|
||||
round_keys = key_derivation(key_i, idx_i);
|
||||
|
||||
state = data_i;
|
||||
|
||||
for (int i=1; i < 32; i++) {
|
||||
for (int i=0; i < NumRounds; i++) {
|
||||
state = state ^ round_keys[i];
|
||||
state = sbox4_layer(state);
|
||||
state = perm_layer(state);
|
||||
}
|
||||
|
||||
data_o = state ^ round_keys[32];
|
||||
data_o = state ^ round_keys[NumRounds-1];
|
||||
key_o = round_keys[NumRounds-1];
|
||||
idx_o = idx_i + NumRounds;
|
||||
```
|
||||
|
||||
The reduced 32bit block-size variant implemented is non-standard and should only be used for scrambling purposes, since it **is not secure**.
|
||||
|
|
48
vendor/lowrisc_ip/ip/prim/dv/prim_lfsr/data/prim_lfsr_cov_excl.el
vendored
Normal file
48
vendor/lowrisc_ip/ip/prim/dv/prim_lfsr/data/prim_lfsr_cov_excl.el
vendored
Normal file
|
@ -0,0 +1,48 @@
|
|||
// Copyright lowRISC contributors.
|
||||
// Licensed under the Apache License, Version 2.0, see LICENSE for details.
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
//==================================================
|
||||
// This file contains the Excluded objects
|
||||
// Generated By User: udij
|
||||
// Format Version: 2
|
||||
// Date: Fri Oct 16 15:45:46 2020
|
||||
// ExclMode: default
|
||||
//==================================================
|
||||
CHECKSUM: "2196990883"
|
||||
INSTANCE: prim_lfsr_tb.gen_duts[24].i_prim_lfsr
|
||||
ANNOTATION: "[EXTERNAL] seed/entropy inputs tied off in DV as they are covered in FPV."
|
||||
Assert gen_lockup_mechanism_sva.LfsrLockupCheck_A "assertion"
|
||||
ANNOTATION: "[EXTERNAL] seed/entropy inputs tied off in DV as they are covered in FPV."
|
||||
Assert gen_ext_seed_sva.ExtDefaultSeedInputCheck_A "assertion"
|
||||
CHECKSUM: "2196990883 1520894529"
|
||||
INSTANCE: prim_lfsr_tb.gen_duts[24].i_prim_lfsr
|
||||
ANNOTATION: "[EXTERNAL] Covered in FPV testbench."
|
||||
Condition 2 "2655198170" "((lfsr_en_i && lockup) ? DefaultSeed : (lfsr_en_i ? next_lfsr_state : lfsr_q)) 1 -1" (2 "1")
|
||||
ANNOTATION: "[EXTERNAL] Covered in FPV testbench."
|
||||
Condition 3 "1026054222" "(lfsr_en_i && lockup) 1 -1" (3 "11")
|
||||
ANNOTATION: "[EXTERNAL] Covered in FPV testbench."
|
||||
Condition 3 "1026054222" "(lfsr_en_i && lockup) 1 -1" (1 "01")
|
||||
ANNOTATION: "[EXTERNAL] Covered in FPV testbench."
|
||||
Condition 5 "4214161107" "((lfsr_en_i && lockup) ? '0 : ((lfsr_en_i && (gen_max_len_sva.cnt_q == gen_max_len_sva.cmp_val)) ? '0 : (lfsr_en_i ? ((gen_max_len_sva.cnt_q + 1'b1)) : gen_max_len_sva.cnt_q))) 1 -1" (2 "1")
|
||||
ANNOTATION: "[EXTERNAL] Covered in FPV testbench."
|
||||
Condition 6 "3622507969" "(lfsr_en_i && lockup) 1 -1" (3 "11")
|
||||
ANNOTATION: "[EXTERNAL] Covered in FPV testbench."
|
||||
Condition 6 "3622507969" "(lfsr_en_i && lockup) 1 -1" (1 "01")
|
||||
ANNOTATION: "[EXTERNAL] Covered in FPV testbench."
|
||||
Condition 8 "1510648413" "(lfsr_en_i && (gen_max_len_sva.cnt_q == gen_max_len_sva.cmp_val)) 1 -1" (1 "01")
|
||||
CHECKSUM: "2196990883 2214500533"
|
||||
INSTANCE: prim_lfsr_tb.gen_duts[24].i_prim_lfsr
|
||||
ANNOTATION: "[EXTERNAL] Covered in FPV testbench."
|
||||
Branch 0 "780369097" "seed_en_i" (0) "seed_en_i 1,-,-"
|
||||
ANNOTATION: "[EXTERNAL] Covered in FPV testbench."
|
||||
Branch 0 "780369097" "seed_en_i" (1) "seed_en_i 0,1,-"
|
||||
ANNOTATION: "[EXTERNAL] Covered in FPV testbench."
|
||||
Branch 1 "864650299" "(lfsr_en_i && lockup)" (0) "(lfsr_en_i && lockup) 1,-,-"
|
||||
CHECKSUM: "2196990883 528004685"
|
||||
INSTANCE: prim_lfsr_tb.gen_duts[24].i_prim_lfsr
|
||||
ANNOTATION: "[EXTERNAL] Covered in FPV testbench."
|
||||
Block 33 "1545234335" "state = DefaultSeed;"
|
||||
CHECKSUM: "2196990883 1321015592"
|
||||
INSTANCE: prim_lfsr_tb.gen_duts[24].i_prim_lfsr
|
||||
ANNOTATION: "[LOW_RISK] DV bench is meant to be extremely light-weight and already tests that prim_lfsr can come out of reset correctly"
|
||||
Toggle rst_ni "net rst_ni"
|
12
vendor/lowrisc_ip/ip/prim/dv/prim_lfsr/data/prim_lfsr_cover.cfg
vendored
Normal file
12
vendor/lowrisc_ip/ip/prim/dv/prim_lfsr/data/prim_lfsr_cover.cfg
vendored
Normal file
|
@ -0,0 +1,12 @@
|
|||
// Copyright lowRISC contributors.
|
||||
// Licensed under the Apache License, Version 2.0, see LICENSE for details.
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
//
|
||||
// We only collect coverage for the LFSR with width 24 as it is the largest
|
||||
// width tested in DV - FPV fully covers a large range of widths,
|
||||
// so this is ok.
|
||||
|
||||
+tree prim_lfsr_tb.gen_duts[24].i_prim_lfsr
|
||||
begin tgl(portsonly)
|
||||
+tree prim_lfsr_tb.gen_duts[24].i_prim_lfsr
|
||||
end
|
9
vendor/lowrisc_ip/ip/prim/dv/prim_lfsr/data/prim_lfsr_cover_assert.cfg
vendored
Normal file
9
vendor/lowrisc_ip/ip/prim/dv/prim_lfsr/data/prim_lfsr_cover_assert.cfg
vendored
Normal file
|
@ -0,0 +1,9 @@
|
|||
// Copyright lowRISC contributors.
|
||||
// Licensed under the Apache License, Version 2.0, see LICENSE for details.
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
//
|
||||
// We only collect coverage for the LFSR with width 24 as it is the largest
|
||||
// width tested in DV - FPV fully covers a large range of widths,
|
||||
// so this is ok.
|
||||
|
||||
+tree prim_lfsr_tb.gen_duts[24].i_prim_lfsr
|
|
@ -19,9 +19,12 @@ filesets:
|
|||
file_type: systemVerilogSource
|
||||
|
||||
targets:
|
||||
sim:
|
||||
sim: &sim_target
|
||||
toplevel: prim_lfsr_tb
|
||||
filesets:
|
||||
- files_rtl
|
||||
- files_dv
|
||||
default_tool: vcs
|
||||
|
||||
lint:
|
||||
<<: *sim_target
|
||||
|
|
|
@ -17,15 +17,20 @@
|
|||
// Fusesoc core file used for building the file list.
|
||||
fusesoc_core: lowrisc:dv:prim_lfsr_sim:0.1
|
||||
|
||||
// Testplan hjson file.
|
||||
testplan: "{proj_root}/hw/ip/prim/dv/prim_lfsr/prim_lfsr_testplan.hjson"
|
||||
|
||||
// Import additional common sim cfg files.
|
||||
import_cfgs: ["{proj_root}/hw/dv/data/common_sim_cfg.hjson"]
|
||||
import_cfgs: ["{proj_root}/hw/dv/tools/dvsim/common_sim_cfg.hjson"]
|
||||
|
||||
// Default iterations for all tests - each test entry can override this.
|
||||
reseed: 50
|
||||
|
||||
// Add the coverage configuration and exclusion files so they get copied
|
||||
// over to the scratch area.
|
||||
tool_srcs: ["{proj_root}/hw/ip/prim/dv/prim_lfsr/data/prim_lfsr_cover.cfg",
|
||||
"{proj_root}/hw/ip/prim/dv/prim_lfsr/data/prim_lfsr_cover_assert.cfg",
|
||||
"{proj_root}/hw/ip/prim/dv/prim_lfsr/data/prim_lfsr_cov_excl.el"]
|
||||
|
||||
vcs_cov_excl_files: ["{tool_srcs_dir}/prim_lfsr_cov_excl.el"]
|
||||
|
||||
build_modes: [
|
||||
{
|
||||
name: prim_lfsr_dw_8
|
||||
|
@ -37,10 +42,10 @@
|
|||
}
|
||||
]
|
||||
|
||||
// TODO: each build_mode needs to supply {build_mode}_{tool}_cov_cfg_file, else the
|
||||
// build breaks.
|
||||
// dw_8 is only used for "smoke" sims, so coverage collection is not needed.
|
||||
prim_lfsr_dw_8_vcs_cov_cfg_file: ""
|
||||
prim_lfsr_dw_24_vcs_cov_cfg_file: ""
|
||||
prim_lfsr_dw_24_vcs_cov_cfg_file: "-cm_hier {tool_srcs_dir}/prim_lfsr_cover.cfg"
|
||||
vcs_cov_assert_cfg_file: "-cm_assert_hier {tool_srcs_dir}/prim_lfsr_cover_assert.cfg"
|
||||
|
||||
prim_lfsr_dw_8_xcelium_cov_cfg_file: ""
|
||||
prim_lfsr_dw_24_xcelium_cov_cfg_file: ""
|
||||
|
@ -48,12 +53,12 @@
|
|||
// List of test specifications.
|
||||
tests: [
|
||||
{
|
||||
name: prim_lfsr_sanity
|
||||
name: prim_lfsr_smoke
|
||||
// limit max lfsr length to reduce private CI runtime.
|
||||
build_mode: prim_lfsr_dw_8
|
||||
}
|
||||
{
|
||||
name: prim_lfsr_basic_test
|
||||
name: prim_lfsr_test
|
||||
build_mode: prim_lfsr_dw_24
|
||||
}
|
||||
]
|
||||
|
@ -61,8 +66,12 @@
|
|||
// List of regressions.
|
||||
regressions: [
|
||||
{
|
||||
name: sanity
|
||||
tests: ["prim_lfsr_sanity"]
|
||||
name: smoke
|
||||
tests: ["prim_lfsr_smoke"]
|
||||
}
|
||||
{
|
||||
name: nightly
|
||||
tests:["prim_lfsr_test"]
|
||||
}
|
||||
]
|
||||
}
|
||||
|
|
|
@ -1,16 +0,0 @@
|
|||
// Copyright lowRISC contributors.
|
||||
// Licensed under the Apache License, Version 2.0, see LICENSE for details.
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
{
|
||||
name: "prim_lfsr"
|
||||
import_testplans: []
|
||||
entries: [
|
||||
{
|
||||
name: sanity
|
||||
desc: '''Simple LFSR test - sweep through all implementations within a
|
||||
certain range to check whether they are max length.'''
|
||||
milestone: V2
|
||||
tests: ["prim_lfsr_sanity"]
|
||||
}
|
||||
]
|
||||
}
|
|
@ -49,18 +49,96 @@ module prim_lfsr_tb;
|
|||
// DUTs
|
||||
//////////////////////////////////////////////////////
|
||||
|
||||
// The StatePerm below is only defined for LFSRs up to 256bit wide.
|
||||
`ASSERT_INIT(MaxStateSizeCheck_A, MaxLfsrDw < 256)
|
||||
|
||||
logic [MaxLfsrDw:0] lfsr_en, err;
|
||||
logic [MaxLfsrDw:MinLfsrDw][MaxLfsrDw-1:0] state_out;
|
||||
logic [MaxLfsrDw:MinLfsrDw][MaxLfsrDw-1:0] lfsr_periods;
|
||||
|
||||
for (genvar k = MinLfsrDw; k <= MaxLfsrDw; k++) begin : gen_duts
|
||||
// This is used to specify an identity permutation via the custom state output
|
||||
// permutation parameter for all LFSRs up to 256bit wide.
|
||||
localparam int Dw = $clog2(k);
|
||||
localparam logic [255:0][Dw-1:0] StatePerm = '{
|
||||
Dw'(32'd255), Dw'(32'd254), Dw'(32'd253), Dw'(32'd252),
|
||||
Dw'(32'd251), Dw'(32'd250), Dw'(32'd249), Dw'(32'd248),
|
||||
Dw'(32'd247), Dw'(32'd246), Dw'(32'd245), Dw'(32'd244),
|
||||
Dw'(32'd243), Dw'(32'd242), Dw'(32'd241), Dw'(32'd240),
|
||||
Dw'(32'd239), Dw'(32'd238), Dw'(32'd237), Dw'(32'd236),
|
||||
Dw'(32'd235), Dw'(32'd234), Dw'(32'd233), Dw'(32'd232),
|
||||
Dw'(32'd231), Dw'(32'd230), Dw'(32'd229), Dw'(32'd228),
|
||||
Dw'(32'd227), Dw'(32'd226), Dw'(32'd225), Dw'(32'd224),
|
||||
Dw'(32'd223), Dw'(32'd222), Dw'(32'd221), Dw'(32'd220),
|
||||
Dw'(32'd219), Dw'(32'd218), Dw'(32'd217), Dw'(32'd216),
|
||||
Dw'(32'd215), Dw'(32'd214), Dw'(32'd213), Dw'(32'd212),
|
||||
Dw'(32'd211), Dw'(32'd210), Dw'(32'd209), Dw'(32'd208),
|
||||
Dw'(32'd207), Dw'(32'd206), Dw'(32'd205), Dw'(32'd204),
|
||||
Dw'(32'd203), Dw'(32'd202), Dw'(32'd201), Dw'(32'd200),
|
||||
Dw'(32'd199), Dw'(32'd198), Dw'(32'd197), Dw'(32'd196),
|
||||
Dw'(32'd195), Dw'(32'd194), Dw'(32'd193), Dw'(32'd192),
|
||||
Dw'(32'd191), Dw'(32'd190), Dw'(32'd189), Dw'(32'd188),
|
||||
Dw'(32'd187), Dw'(32'd186), Dw'(32'd185), Dw'(32'd184),
|
||||
Dw'(32'd183), Dw'(32'd182), Dw'(32'd181), Dw'(32'd180),
|
||||
Dw'(32'd179), Dw'(32'd178), Dw'(32'd177), Dw'(32'd176),
|
||||
Dw'(32'd175), Dw'(32'd174), Dw'(32'd173), Dw'(32'd172),
|
||||
Dw'(32'd171), Dw'(32'd170), Dw'(32'd169), Dw'(32'd168),
|
||||
Dw'(32'd167), Dw'(32'd166), Dw'(32'd165), Dw'(32'd164),
|
||||
Dw'(32'd163), Dw'(32'd162), Dw'(32'd161), Dw'(32'd160),
|
||||
Dw'(32'd159), Dw'(32'd158), Dw'(32'd157), Dw'(32'd156),
|
||||
Dw'(32'd155), Dw'(32'd154), Dw'(32'd153), Dw'(32'd152),
|
||||
Dw'(32'd151), Dw'(32'd150), Dw'(32'd149), Dw'(32'd148),
|
||||
Dw'(32'd147), Dw'(32'd146), Dw'(32'd145), Dw'(32'd144),
|
||||
Dw'(32'd143), Dw'(32'd142), Dw'(32'd141), Dw'(32'd140),
|
||||
Dw'(32'd139), Dw'(32'd138), Dw'(32'd137), Dw'(32'd136),
|
||||
Dw'(32'd135), Dw'(32'd134), Dw'(32'd133), Dw'(32'd132),
|
||||
Dw'(32'd131), Dw'(32'd130), Dw'(32'd129), Dw'(32'd128),
|
||||
Dw'(32'd127), Dw'(32'd126), Dw'(32'd125), Dw'(32'd124),
|
||||
Dw'(32'd123), Dw'(32'd122), Dw'(32'd121), Dw'(32'd120),
|
||||
Dw'(32'd119), Dw'(32'd118), Dw'(32'd117), Dw'(32'd116),
|
||||
Dw'(32'd115), Dw'(32'd114), Dw'(32'd113), Dw'(32'd112),
|
||||
Dw'(32'd111), Dw'(32'd110), Dw'(32'd109), Dw'(32'd108),
|
||||
Dw'(32'd107), Dw'(32'd106), Dw'(32'd105), Dw'(32'd104),
|
||||
Dw'(32'd103), Dw'(32'd102), Dw'(32'd101), Dw'(32'd100),
|
||||
Dw'(32'd099), Dw'(32'd098), Dw'(32'd097), Dw'(32'd096),
|
||||
Dw'(32'd095), Dw'(32'd094), Dw'(32'd093), Dw'(32'd092),
|
||||
Dw'(32'd091), Dw'(32'd090), Dw'(32'd089), Dw'(32'd088),
|
||||
Dw'(32'd087), Dw'(32'd086), Dw'(32'd085), Dw'(32'd084),
|
||||
Dw'(32'd083), Dw'(32'd082), Dw'(32'd081), Dw'(32'd080),
|
||||
Dw'(32'd079), Dw'(32'd078), Dw'(32'd077), Dw'(32'd076),
|
||||
Dw'(32'd075), Dw'(32'd074), Dw'(32'd073), Dw'(32'd072),
|
||||
Dw'(32'd071), Dw'(32'd070), Dw'(32'd069), Dw'(32'd068),
|
||||
Dw'(32'd067), Dw'(32'd066), Dw'(32'd065), Dw'(32'd064),
|
||||
Dw'(32'd063), Dw'(32'd062), Dw'(32'd061), Dw'(32'd060),
|
||||
Dw'(32'd059), Dw'(32'd058), Dw'(32'd057), Dw'(32'd056),
|
||||
Dw'(32'd055), Dw'(32'd054), Dw'(32'd053), Dw'(32'd052),
|
||||
Dw'(32'd051), Dw'(32'd050), Dw'(32'd049), Dw'(32'd048),
|
||||
Dw'(32'd047), Dw'(32'd046), Dw'(32'd045), Dw'(32'd044),
|
||||
Dw'(32'd043), Dw'(32'd042), Dw'(32'd041), Dw'(32'd040),
|
||||
Dw'(32'd039), Dw'(32'd038), Dw'(32'd037), Dw'(32'd036),
|
||||
Dw'(32'd035), Dw'(32'd034), Dw'(32'd033), Dw'(32'd032),
|
||||
Dw'(32'd031), Dw'(32'd030), Dw'(32'd029), Dw'(32'd028),
|
||||
Dw'(32'd027), Dw'(32'd026), Dw'(32'd025), Dw'(32'd024),
|
||||
Dw'(32'd023), Dw'(32'd022), Dw'(32'd021), Dw'(32'd020),
|
||||
Dw'(32'd019), Dw'(32'd018), Dw'(32'd017), Dw'(32'd016),
|
||||
Dw'(32'd015), Dw'(32'd014), Dw'(32'd013), Dw'(32'd012),
|
||||
Dw'(32'd011), Dw'(32'd010), Dw'(32'd009), Dw'(32'd008),
|
||||
Dw'(32'd007), Dw'(32'd006), Dw'(32'd005), Dw'(32'd004),
|
||||
Dw'(32'd003), Dw'(32'd002), Dw'(32'd001), Dw'(32'd000)
|
||||
};
|
||||
|
||||
prim_lfsr #(
|
||||
.LfsrType ( LfsrType ),
|
||||
.LfsrDw ( k ),
|
||||
.EntropyDw ( 1 ),
|
||||
.StateOutDw ( k ),
|
||||
.DefaultSeed ( k'(SEED) ),
|
||||
// enable internal max length check
|
||||
// The case where this is disabled is already tested with FPV.
|
||||
// Hence we cover the enabled case with custom permutations
|
||||
// in this testbench.
|
||||
.StatePermEn ( 1'b1 ),
|
||||
.StatePerm ( StatePerm[MaxLfsrDw-1:0] ),
|
||||
// Enable internal max length check.
|
||||
.MaxLenSVA ( 1'b1 )
|
||||
) i_prim_lfsr (
|
||||
.clk_i ( clk ),
|
||||
|
|
|
@ -1,3 +1,7 @@
|
|||
// Copyright lowRISC contributors.
|
||||
// Licensed under the Apache License, Version 2.0, see LICENSE for details.
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
+module prim_present
|
||||
begin tgl(portsonly)
|
||||
-tree prim_present_tb
|
||||
|
|
|
@ -6,12 +6,12 @@
|
|||
import_testplans: []
|
||||
entries: [
|
||||
{
|
||||
name: sanity
|
||||
name: smoke
|
||||
desc: '''Simple PRESENT test - feed golden and random testvectors into
|
||||
both the encryption and decryption algorithms and verify that all
|
||||
outputs match those from the reference model.'''
|
||||
milestone: V2
|
||||
tests: ["prim_present_sanity"]
|
||||
tests: ["prim_present_smoke"]
|
||||
}
|
||||
]
|
||||
}
|
||||
|
|
|
@ -18,9 +18,12 @@ filesets:
|
|||
file_type: systemVerilogSource
|
||||
|
||||
targets:
|
||||
sim:
|
||||
sim: &sim_target
|
||||
toplevel: prim_present_tb
|
||||
filesets:
|
||||
- files_rtl
|
||||
- files_dv
|
||||
default_tool: vcs
|
||||
|
||||
lint:
|
||||
<<: *sim_target
|
||||
|
|
|
@ -21,7 +21,7 @@
|
|||
testplan: "{proj_root}/hw/ip/prim/dv/prim_present/data/prim_present_testplan.hjson"
|
||||
|
||||
// Import additional common sim cfg files.
|
||||
import_cfgs: ["{proj_root}/hw/dv/data/common_sim_cfg.hjson"]
|
||||
import_cfgs: ["{proj_root}/hw/dv/tools/dvsim/common_sim_cfg.hjson"]
|
||||
|
||||
// Default iterations for all tests - each test entry can override this.
|
||||
reseed: 50
|
||||
|
@ -39,15 +39,15 @@
|
|||
// List of test specifications.
|
||||
tests: [
|
||||
{
|
||||
name: prim_present_sanity
|
||||
name: prim_present_smoke
|
||||
}
|
||||
]
|
||||
|
||||
// List of regressions.
|
||||
regressions: [
|
||||
{
|
||||
name: sanity
|
||||
tests: ["prim_present_sanity"]
|
||||
name: smoke
|
||||
tests: ["prim_present_smoke"]
|
||||
}
|
||||
]
|
||||
}
|
||||
|
|
|
@ -40,7 +40,7 @@ module prim_present_tb;
|
|||
localparam bit Decrypt = 1'b1;
|
||||
|
||||
// this parameter is required for the DPI model.
|
||||
localparam KeySize80 = (KeyWidth == 80);
|
||||
localparam bit KeySize80 = (KeyWidth == 80);
|
||||
|
||||
|
||||
//////////////////////////////////////////////////////
|
||||
|
@ -51,11 +51,18 @@ module prim_present_tb;
|
|||
// Same scheme used for key_in, data_out, key_out.
|
||||
logic [1:0][NumRounds-1:0][DataWidth-1:0] data_in;
|
||||
logic [1:0][NumRounds-1:0][KeyWidth-1 :0] key_in;
|
||||
logic [1:0][NumRounds-1:0][4:0] idx_in;
|
||||
logic [1:0][NumRounds-1:0][DataWidth-1:0] data_out;
|
||||
logic [1:0][NumRounds-1:0][KeyWidth-1 :0] key_out;
|
||||
logic [1:0][NumRounds-1:0][4:0] idx_out;
|
||||
|
||||
for (genvar j = 0; j < 2; j++) begin : gen_encrypt_decrypt
|
||||
for (genvar k = 0; k < NumRounds; k++) begin : gen_duts
|
||||
if (j == 0) begin : gen_encrypt
|
||||
assign idx_in[j][k] = 5'd1;
|
||||
end else begin : gen_decrypt
|
||||
assign idx_in[j][k] = 5'(k+1);
|
||||
end
|
||||
prim_present #(
|
||||
.DataWidth ( DataWidth ),
|
||||
.KeyWidth ( KeyWidth ),
|
||||
|
@ -64,8 +71,10 @@ module prim_present_tb;
|
|||
) dut (
|
||||
.data_i ( data_in[j][k] ),
|
||||
.key_i ( key_in[j][k] ),
|
||||
.idx_i ( idx_in[j][k] ),
|
||||
.data_o ( data_out[j][k] ),
|
||||
.key_o ( key_out[j][k] )
|
||||
.key_o ( key_out[j][k] ),
|
||||
.idx_o ( idx_out[j][k] )
|
||||
);
|
||||
end
|
||||
end
|
||||
|
@ -77,8 +86,8 @@ module prim_present_tb;
|
|||
|
||||
// Top level API task that should be called to run a full pass
|
||||
// of encryption and decryption on some input data and key.
|
||||
task test_present(bit [DataWidth-1:0] plaintext,
|
||||
bit [KeyWidth-1:0] key);
|
||||
task automatic test_present(bit [DataWidth-1:0] plaintext,
|
||||
bit [KeyWidth-1:0] key);
|
||||
|
||||
bit [NumRounds:0][63:0] key_schedule;
|
||||
bit [NumRounds-1:0][DataWidth-1:0] encrypted_text;
|
||||
|
@ -97,10 +106,10 @@ module prim_present_tb;
|
|||
|
||||
// Helper task to drive plaintext and key into each encryption instance.
|
||||
// Calls a subroutine to perform checks on the outputs (once they are available).
|
||||
task check_encryption(input bit [DataWidth-1:0] plaintext,
|
||||
input bit [KeyWidth-1:0] key,
|
||||
input bit [NumRounds:0][63:0] key_schedule,
|
||||
output bit [NumRounds-1:0][DataWidth-1:0] expected_ciphertext);
|
||||
task automatic check_encryption(input bit [DataWidth-1:0] plaintext,
|
||||
input bit [KeyWidth-1:0] key,
|
||||
input bit [NumRounds:0][63:0] key_schedule,
|
||||
output bit [NumRounds-1:0][DataWidth-1:0] expected_ciphertext);
|
||||
|
||||
// Drive input into encryption instances.
|
||||
for (int unsigned i = 0; i < NumRounds; i++) begin
|
||||
|
@ -122,9 +131,9 @@ module prim_present_tb;
|
|||
|
||||
// Helper task to drive ciphertext and key into each decryption instance.
|
||||
// Calls a subroutine to perform checks on the outputs (once they are available).
|
||||
task check_decryption(input bit [NumRounds-1:0][DataWidth-1:0] ciphertext,
|
||||
input bit [KeyWidth-1:0] key,
|
||||
input bit [NumRounds-1:0][KeyWidth-1:0] decryption_keys);
|
||||
task automatic check_decryption(input bit [NumRounds-1:0][DataWidth-1:0] ciphertext,
|
||||
input bit [KeyWidth-1:0] key,
|
||||
input bit [NumRounds-1:0][KeyWidth-1:0] decryption_keys);
|
||||
|
||||
// the expected plaintext after decryption will be provided by the C model.
|
||||
bit [NumRounds-1:0][DataWidth-1:0] expected_plaintext;
|
||||
|
@ -161,11 +170,11 @@ module prim_present_tb;
|
|||
//
|
||||
// If any comparison error is seen, this task short-circuits immediately,
|
||||
// printing out some debug information and the correct failure signature.
|
||||
task check_output(input bit [NumRounds-1:0][63:0] expected_key,
|
||||
input bit [NumRounds-1:0][DataWidth-1:0] expected_text,
|
||||
input bit [NumRounds-1:0][KeyWidth-1:0] actual_key,
|
||||
input bit [NumRounds-1:0][DataWidth-1:0] actual_data,
|
||||
input string msg);
|
||||
task automatic check_output(input bit [NumRounds-1:0][63:0] expected_key,
|
||||
input bit [NumRounds-1:0][DataWidth-1:0] expected_text,
|
||||
input bit [NumRounds-1:0][KeyWidth-1:0] actual_key,
|
||||
input bit [NumRounds-1:0][DataWidth-1:0] actual_data,
|
||||
input string msg);
|
||||
|
||||
bit error = 1'b0;
|
||||
|
||||
|
|
33
vendor/lowrisc_ip/ip/prim/dv/prim_prince/crypto_dpi_prince/crypto_dpi_prince.c
vendored
Normal file
33
vendor/lowrisc_ip/ip/prim/dv/prim_prince/crypto_dpi_prince/crypto_dpi_prince.c
vendored
Normal file
|
@ -0,0 +1,33 @@
|
|||
// Copyright lowRISC contributors.
|
||||
// Licensed under the Apache License, Version 2.0, see LICENSE for details.
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
#ifdef _cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#include "prince_ref.h"
|
||||
#include "svdpi.h"
|
||||
|
||||
extern uint64_t c_dpi_prince_encrypt(uint64_t plaintext, uint64_t key0,
|
||||
uint64_t key1, int num_half_rounds,
|
||||
int old_key_schedule) {
|
||||
return prince_enc_dec_uint64(plaintext, key0, key1, 0, num_half_rounds,
|
||||
old_key_schedule);
|
||||
}
|
||||
|
||||
extern uint64_t c_dpi_prince_decrypt(const uint64_t ciphertext,
|
||||
const uint64_t key0, const uint64_t key1,
|
||||
int num_half_rounds,
|
||||
int old_key_schedule) {
|
||||
return prince_enc_dec_uint64(ciphertext, key0, key1, 1, num_half_rounds,
|
||||
old_key_schedule);
|
||||
}
|
||||
|
||||
#ifdef _cplusplus
|
||||
}
|
||||
#endif
|
17
vendor/lowrisc_ip/ip/prim/dv/prim_prince/crypto_dpi_prince/crypto_dpi_prince.core
vendored
Normal file
17
vendor/lowrisc_ip/ip/prim/dv/prim_prince/crypto_dpi_prince/crypto_dpi_prince.core
vendored
Normal file
|
@ -0,0 +1,17 @@
|
|||
CAPI=2:
|
||||
# Copyright lowRISC contributors.
|
||||
# Licensed under the Apache License, Version 2.0, see LICENSE for details.
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
name: "lowrisc:dv:crypto_dpi_prince:0.1"
|
||||
description: "PRINCE block cipher reference C implementation from Sebastien Riou"
|
||||
filesets:
|
||||
files_dv:
|
||||
files:
|
||||
- prince_ref.h: {file_type: cSource, is_include_file: true}
|
||||
- crypto_dpi_prince.c: {file_type: cSource}
|
||||
- crypto_dpi_prince_pkg.sv: {file_type: systemVerilogSource}
|
||||
|
||||
targets:
|
||||
default:
|
||||
filesets:
|
||||
- files_dv
|
64
vendor/lowrisc_ip/ip/prim/dv/prim_prince/crypto_dpi_prince/crypto_dpi_prince_pkg.sv
vendored
Normal file
64
vendor/lowrisc_ip/ip/prim/dv/prim_prince/crypto_dpi_prince/crypto_dpi_prince_pkg.sv
vendored
Normal file
|
@ -0,0 +1,64 @@
|
|||
// Copyright lowRISC contributors.
|
||||
// Licensed under the Apache License, Version 2.0, see LICENSE for details.
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
package crypto_dpi_prince_pkg;
|
||||
|
||||
// dep packages
|
||||
|
||||
// parameters
|
||||
// Max number of half-rounds.
|
||||
parameter int unsigned NumRoundsHalf = 5;
|
||||
|
||||
// DPI-C imports
|
||||
import "DPI-C" context function longint c_dpi_prince_encrypt(
|
||||
input longint unsigned data,
|
||||
input longint unsigned key0,
|
||||
input longint unsigned key1,
|
||||
input int unsigned num_half_rounds,
|
||||
input int unsigned new_key_schedule
|
||||
);
|
||||
|
||||
import "DPI-C" context function longint c_dpi_prince_decrypt(
|
||||
input longint unsigned data,
|
||||
input longint unsigned key0,
|
||||
input longint unsigned key1,
|
||||
input int unsigned num_half_rounds,
|
||||
input int unsigned new_key_schedule
|
||||
);
|
||||
|
||||
//////////////////////////////////////////////////////
|
||||
// SV wrapper functions to be used by the testbench //
|
||||
//////////////////////////////////////////////////////
|
||||
|
||||
function automatic void sv_dpi_prince_encrypt(
|
||||
input bit [63:0] plaintext,
|
||||
input bit [127:0] key,
|
||||
input bit old_key_schedule,
|
||||
output bit [NumRoundsHalf-1:0][63:0] ciphertext
|
||||
);
|
||||
for (int i = 0; i < NumRoundsHalf; i++) begin
|
||||
ciphertext[i] = c_dpi_prince_encrypt(plaintext,
|
||||
key[63:0],
|
||||
key[127:64],
|
||||
i+1,
|
||||
old_key_schedule);
|
||||
end
|
||||
endfunction
|
||||
|
||||
function automatic void sv_dpi_prince_decrypt(
|
||||
input bit [NumRoundsHalf-1:0][63:0] ciphertext,
|
||||
input bit [127:0] key,
|
||||
input bit old_key_schedule,
|
||||
output bit [NumRoundsHalf-1:0][63:0] plaintext
|
||||
);
|
||||
for (int i = 0; i < NumRoundsHalf; i++) begin
|
||||
plaintext[i] = c_dpi_prince_decrypt(ciphertext[i],
|
||||
key[63:0],
|
||||
key[127:64],
|
||||
i+1,
|
||||
old_key_schedule);
|
||||
end
|
||||
endfunction
|
||||
|
||||
endpackage
|
344
vendor/lowrisc_ip/ip/prim/dv/prim_prince/crypto_dpi_prince/prince_ref.h
vendored
Normal file
344
vendor/lowrisc_ip/ip/prim/dv/prim_prince/crypto_dpi_prince/prince_ref.h
vendored
Normal file
|
@ -0,0 +1,344 @@
|
|||
// Copyright lowRISC contributors.
|
||||
// Copyright 2016 Sebastien Riou
|
||||
// Licensed under the Apache License, Version 2.0, see LICENSE for details.
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
/*! \file prince_ref.h
|
||||
\brief Reference implementation of the Prince block cipher, complient to
|
||||
C99. 'Reference' here means straightforward, unoptimized, and checked against
|
||||
the few test vectors provided in the original paper
|
||||
(http://eprint.iacr.org/2012/529.pdf). By defining the macro PRINCE_PRINT,
|
||||
one can print out all successive internal states.
|
||||
*/
|
||||
|
||||
/*
|
||||
* This C-implementation of the PRINCE block cipher is taken from Sebastien
|
||||
* Riou's open-sourced 'prince-c-ref' GitHub repository, found here:
|
||||
* https://github.com/sebastien-riou/prince-c-ref/blob/master/include/prince_ref.h.
|
||||
*
|
||||
* Several modifications of varying severity have been made to this C model to
|
||||
* enable extra external parameterizations for more thorough verification of the
|
||||
* OpenTitan PRINCE hardware implementation.
|
||||
* These modifications, in no particular order, are:
|
||||
*
|
||||
* - Conversion of `prince_enc_dec_uint64_t(...)` to a non-static function to
|
||||
* allow usage by an external DPI-C model.
|
||||
* - Additional arguments added to `prince_enc_dec_uint64_t(...)`,
|
||||
* `prince_enc_dec(...)`, `prince_encrypt(...)` `prince_decrypt(...)`, and
|
||||
* `prince_core(...)` to allow an external DPI-C model to control the
|
||||
* number of cipher half-rounds and to toggle between the "legacy" key
|
||||
* schedule detailed in the original PRINCE paper and a newer key schedule.
|
||||
* - Modification of `prince_core(...)` to handle the new key schedule and
|
||||
* user-specified number of half-rounds.
|
||||
*/
|
||||
|
||||
#ifndef OPENTITAN_HW_IP_PRIM_DV_PRIM_PRINCE_CRYPTO_DPI_PRINCE_PRINCE_REF_H_
|
||||
#define OPENTITAN_HW_IP_PRIM_DV_PRIM_PRINCE_CRYPTO_DPI_PRINCE_PRINCE_REF_H_
|
||||
|
||||
#include <stdint.h>
|
||||
#include <string.h>
|
||||
|
||||
#ifndef PRINCE_PRINT
|
||||
#define PRINCE_PRINT(a) \
|
||||
do { \
|
||||
} while (0)
|
||||
#endif
|
||||
|
||||
/**
|
||||
* Converts a byte array into a 64-bit integer.
|
||||
*
|
||||
* The byte at index 0 is placed as the most significant byte.
|
||||
*/
|
||||
static uint64_t bytes_to_uint64(const uint8_t in[8]) {
|
||||
uint64_t out = 0;
|
||||
unsigned int i;
|
||||
for (i = 0; i < 8; i++)
|
||||
out = (out << 8) | in[i];
|
||||
return out;
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts a 64-bit integer into a byte array.
|
||||
*
|
||||
* The most significant byte is placed at index 0.
|
||||
*/
|
||||
static void uint64_to_bytes(const uint64_t in, uint8_t out[8]) {
|
||||
unsigned int i;
|
||||
for (i = 0; i < 8; i++)
|
||||
out[i] = in >> ((7 - i) * 8);
|
||||
}
|
||||
|
||||
/**
|
||||
* Computes K0' from K0.
|
||||
*/
|
||||
static uint64_t prince_k0_to_k0_prime(const uint64_t k0) {
|
||||
uint64_t k0_ror1 = (k0 >> 1) | (k0 << 63);
|
||||
uint64_t k0_prime = k0_ror1 ^ (k0 >> 63);
|
||||
return k0_prime;
|
||||
}
|
||||
|
||||
static uint64_t prince_round_constant(const unsigned int round) {
|
||||
uint64_t rc[] = {UINT64_C(0x0000000000000000), UINT64_C(0x13198a2e03707344),
|
||||
UINT64_C(0xa4093822299f31d0), UINT64_C(0x082efa98ec4e6c89),
|
||||
UINT64_C(0x452821e638d01377), UINT64_C(0xbe5466cf34e90c6c),
|
||||
UINT64_C(0x7ef84f78fd955cb1), UINT64_C(0x85840851f1ac43aa),
|
||||
UINT64_C(0xc882d32f25323c54), UINT64_C(0x64a51195e0e3610d),
|
||||
UINT64_C(0xd3b5a399ca0c2399), UINT64_C(0xc0ac29b7c97c50dd)};
|
||||
return rc[round];
|
||||
}
|
||||
|
||||
/**
|
||||
* The 4 bit Prince sbox.
|
||||
*
|
||||
* Only the 4 lsb are taken into account to preserve nibble granularity.
|
||||
*/
|
||||
static unsigned int prince_sbox(unsigned int nibble) {
|
||||
const unsigned int sbox[] = {0xb, 0xf, 0x3, 0x2, 0xa, 0xc, 0x9, 0x1,
|
||||
0x6, 0x7, 0x8, 0x0, 0xe, 0x5, 0xd, 0x4};
|
||||
return sbox[nibble & 0xF];
|
||||
}
|
||||
|
||||
/**
|
||||
* The 4 bit Prince inverse sbox.
|
||||
*
|
||||
* Only the 4 lsb are taken into account to preserve nibble granularity.
|
||||
*/
|
||||
static unsigned int prince_sbox_inv(unsigned int nibble) {
|
||||
const unsigned int sbox[] = {0xb, 0x7, 0x3, 0x2, 0xf, 0xd, 0x8, 0x9,
|
||||
0xa, 0x6, 0x4, 0x0, 0x5, 0xe, 0xc, 0x1};
|
||||
return sbox[nibble & 0xF];
|
||||
}
|
||||
|
||||
/**
|
||||
* The S step of the Prince cipher.
|
||||
*/
|
||||
static uint64_t prince_s_layer(const uint64_t s_in) {
|
||||
uint64_t s_out = 0;
|
||||
for (unsigned int i = 0; i < 16; i++) {
|
||||
const unsigned int shift = i * 4;
|
||||
const unsigned int sbox_in = s_in >> shift;
|
||||
const uint64_t sbox_out = prince_sbox(sbox_in);
|
||||
s_out |= sbox_out << shift;
|
||||
}
|
||||
return s_out;
|
||||
}
|
||||
|
||||
/**
|
||||
* The S^-1 step of the Prince cipher.
|
||||
*/
|
||||
static uint64_t prince_s_inv_layer(const uint64_t s_inv_in) {
|
||||
uint64_t s_inv_out = 0;
|
||||
for (unsigned int i = 0; i < 16; i++) {
|
||||
const unsigned int shift = i * 4;
|
||||
const unsigned int sbox_in = s_inv_in >> shift;
|
||||
const uint64_t sbox_out = prince_sbox_inv(sbox_in);
|
||||
s_inv_out |= sbox_out << shift;
|
||||
}
|
||||
return s_inv_out;
|
||||
}
|
||||
|
||||
static uint64_t gf2_mat_mult16_1(const uint64_t in, const uint64_t mat[16]) {
|
||||
uint64_t out = 0;
|
||||
for (unsigned int i = 0; i < 16; i++) {
|
||||
if ((in >> i) & 1)
|
||||
out ^= mat[i];
|
||||
}
|
||||
return out;
|
||||
}
|
||||
|
||||
/**
|
||||
* Build Prince's 16 bit matrices M0 and M1.
|
||||
*/
|
||||
static void prince_m16_matrices(uint64_t m16[2][16]) {
|
||||
// 4 bits matrices m0 to m3 are stored in array m4
|
||||
const uint64_t m4[4][4] = {// m0
|
||||
{0x0, 0x2, 0x4, 0x8},
|
||||
// m1
|
||||
{0x1, 0x0, 0x4, 0x8},
|
||||
// m2
|
||||
{0x1, 0x2, 0x0, 0x8},
|
||||
// m3
|
||||
{0x1, 0x2, 0x4, 0x0}};
|
||||
// build 16 bits matrices
|
||||
for (unsigned int i = 0; i < 16; i++) {
|
||||
const unsigned int base = i / 4;
|
||||
m16[0][i] =
|
||||
(m4[(base + 3) % 4][i % 4] << 8) | (m4[(base + 2) % 4][i % 4] << 4) |
|
||||
(m4[(base + 1) % 4][i % 4] << 0) | (m4[(base + 0) % 4][i % 4] << 12);
|
||||
m16[1][i] =
|
||||
(m16[0][i] >> 12) |
|
||||
(0xFFFF & (m16[0][i] << 4)); // m1 is just a rotated version of m0
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* The M' step of the Prince cipher.
|
||||
*/
|
||||
static uint64_t prince_m_prime_layer(const uint64_t m_prime_in) {
|
||||
// 16 bits matrices M0 and M1, generated using the code below
|
||||
// uint64_t m16[2][16];prince_m16_matrices(m16);
|
||||
// for(unsigned int i=0;i<16;i++) PRINCE_PRINT(m16[0][i]);
|
||||
// for(unsigned int i=0;i<16;i++) PRINCE_PRINT(m16[1][i]);
|
||||
static const uint64_t m16[2][16] = {
|
||||
{0x0111, 0x2220, 0x4404, 0x8088, 0x1011, 0x0222, 0x4440, 0x8808, 0x1101,
|
||||
0x2022, 0x0444, 0x8880, 0x1110, 0x2202, 0x4044, 0x0888},
|
||||
|
||||
{0x1110, 0x2202, 0x4044, 0x0888, 0x0111, 0x2220, 0x4404, 0x8088, 0x1011,
|
||||
0x0222, 0x4440, 0x8808, 0x1101, 0x2022, 0x0444, 0x8880}};
|
||||
const uint64_t chunk0 = gf2_mat_mult16_1(m_prime_in >> (0 * 16), m16[0]);
|
||||
const uint64_t chunk1 = gf2_mat_mult16_1(m_prime_in >> (1 * 16), m16[1]);
|
||||
const uint64_t chunk2 = gf2_mat_mult16_1(m_prime_in >> (2 * 16), m16[1]);
|
||||
const uint64_t chunk3 = gf2_mat_mult16_1(m_prime_in >> (3 * 16), m16[0]);
|
||||
const uint64_t m_prime_out = (chunk3 << (3 * 16)) | (chunk2 << (2 * 16)) |
|
||||
(chunk1 << (1 * 16)) | (chunk0 << (0 * 16));
|
||||
return m_prime_out;
|
||||
}
|
||||
|
||||
/**
|
||||
* The shift row and inverse shift row of the Prince cipher.
|
||||
*/
|
||||
static uint64_t prince_shift_rows(const uint64_t in, int inverse) {
|
||||
const uint64_t row_mask = UINT64_C(0xF000F000F000F000);
|
||||
uint64_t shift_rows_out = 0;
|
||||
for (unsigned int i = 0; i < 4; i++) {
|
||||
const uint64_t row = in & (row_mask >> (4 * i));
|
||||
const unsigned int shift = inverse ? i * 16 : 64 - i * 16;
|
||||
shift_rows_out |= (row >> shift) | (row << (64 - shift));
|
||||
}
|
||||
return shift_rows_out;
|
||||
}
|
||||
|
||||
/**
|
||||
* The M step of the Prince cipher.
|
||||
*/
|
||||
static uint64_t prince_m_layer(const uint64_t m_in) {
|
||||
const uint64_t m_prime_out = prince_m_prime_layer(m_in);
|
||||
const uint64_t shift_rows_out = prince_shift_rows(m_prime_out, 0);
|
||||
return shift_rows_out;
|
||||
}
|
||||
|
||||
/**
|
||||
* The M^-1 step of the Prince cipher.
|
||||
*/
|
||||
static uint64_t prince_m_inv_layer(const uint64_t m_inv_in) {
|
||||
const uint64_t shift_rows_out = prince_shift_rows(m_inv_in, 1);
|
||||
const uint64_t m_prime_out = prince_m_prime_layer(shift_rows_out);
|
||||
return m_prime_out;
|
||||
}
|
||||
|
||||
/**
|
||||
* The core function of the Prince cipher.
|
||||
*/
|
||||
static uint64_t prince_core(const uint64_t core_input, const uint64_t k0_new,
|
||||
const uint64_t k1, int num_half_rounds) {
|
||||
PRINCE_PRINT(core_input);
|
||||
PRINCE_PRINT(k1);
|
||||
uint64_t round_input = core_input ^ k1 ^ prince_round_constant(0);
|
||||
for (unsigned int round = 1; round <= num_half_rounds; round++) {
|
||||
PRINCE_PRINT(round_input);
|
||||
const uint64_t s_out = prince_s_layer(round_input);
|
||||
PRINCE_PRINT(s_out);
|
||||
const uint64_t m_out = prince_m_layer(s_out);
|
||||
PRINCE_PRINT(m_out);
|
||||
round_input = (round % 2 == 1)
|
||||
? m_out ^ k0_new ^ prince_round_constant(round)
|
||||
: m_out ^ k1 ^ prince_round_constant(round);
|
||||
}
|
||||
const uint64_t middle_round_s_out = prince_s_layer(round_input);
|
||||
PRINCE_PRINT(middle_round_s_out);
|
||||
const uint64_t m_prime_out = prince_m_prime_layer(middle_round_s_out);
|
||||
PRINCE_PRINT(m_prime_out);
|
||||
const uint64_t middle_round_s_inv_out = prince_s_inv_layer(m_prime_out);
|
||||
round_input = middle_round_s_inv_out;
|
||||
// for(unsigned int round = 6; round < num_half_rounds * 2 + 1; round ++){
|
||||
for (unsigned int round = 1; round <= num_half_rounds; round++) {
|
||||
PRINCE_PRINT(round_input);
|
||||
const uint64_t constant_idx = 10 - num_half_rounds + round;
|
||||
const uint64_t m_inv_in =
|
||||
((num_half_rounds + round + 1) % 2 == 1)
|
||||
? round_input ^ k0_new ^ prince_round_constant(constant_idx)
|
||||
: round_input ^ k1 ^ prince_round_constant(constant_idx);
|
||||
PRINCE_PRINT(m_inv_in);
|
||||
const uint64_t s_inv_in = prince_m_inv_layer(m_inv_in);
|
||||
PRINCE_PRINT(s_inv_in);
|
||||
const uint64_t s_inv_out = prince_s_inv_layer(s_inv_in);
|
||||
round_input = s_inv_out;
|
||||
}
|
||||
const uint64_t core_output = round_input ^ k1 ^ prince_round_constant(11);
|
||||
PRINCE_PRINT(core_output);
|
||||
return core_output;
|
||||
}
|
||||
|
||||
/**
|
||||
* Top level function for Prince encryption/decryption.
|
||||
*
|
||||
* enc_k0 and enc_k1 must be the same for encryption and decryption.
|
||||
* The handling of decryption is done internally.
|
||||
*/
|
||||
uint64_t prince_enc_dec_uint64(const uint64_t input, const uint64_t enc_k0,
|
||||
const uint64_t enc_k1, int decrypt,
|
||||
int num_half_rounds, int old_key_schedule) {
|
||||
const uint64_t prince_alpha = UINT64_C(0xc0ac29b7c97c50dd);
|
||||
const uint64_t k1 = enc_k1 ^ (decrypt ? prince_alpha : 0);
|
||||
const uint64_t k0_new =
|
||||
(old_key_schedule) ? k1 : enc_k0 ^ (decrypt ? prince_alpha : 0);
|
||||
const uint64_t enc_k0_prime = prince_k0_to_k0_prime(enc_k0);
|
||||
const uint64_t k0 = decrypt ? enc_k0_prime : enc_k0;
|
||||
const uint64_t k0_prime = decrypt ? enc_k0 : enc_k0_prime;
|
||||
PRINCE_PRINT(k0);
|
||||
PRINCE_PRINT(input);
|
||||
const uint64_t core_input = input ^ k0;
|
||||
const uint64_t core_output =
|
||||
prince_core(core_input, k0_new, k1, num_half_rounds);
|
||||
const uint64_t output = core_output ^ k0_prime;
|
||||
PRINCE_PRINT(k0_prime);
|
||||
PRINCE_PRINT(output);
|
||||
return output;
|
||||
}
|
||||
|
||||
/**
|
||||
* Byte oriented top level function for Prince encryption/decryption.
|
||||
*
|
||||
* key_bytes 0 to 7 must contain K0.
|
||||
* key_bytes 8 to 15 must contain K1.
|
||||
*/
|
||||
static void prince_enc_dec(const uint8_t in_bytes[8],
|
||||
const uint8_t key_bytes[16], uint8_t out_bytes[8],
|
||||
int decrypt, int num_half_rounds,
|
||||
int old_key_schedule) {
|
||||
const uint64_t input = bytes_to_uint64(in_bytes);
|
||||
const uint64_t enc_k0 = bytes_to_uint64(key_bytes);
|
||||
const uint64_t enc_k1 = bytes_to_uint64(key_bytes + 8);
|
||||
const uint64_t output = prince_enc_dec_uint64(
|
||||
input, enc_k0, enc_k1, decrypt, num_half_rounds, old_key_schedule);
|
||||
uint64_to_bytes(output, out_bytes);
|
||||
}
|
||||
|
||||
/**
|
||||
* Byte oriented top level function for Prince encryption.
|
||||
*
|
||||
* key_bytes 0 to 7 must contain K0.
|
||||
* key_bytes 8 to 15 must contain K1.
|
||||
*/
|
||||
static void prince_encrypt(const uint8_t in_bytes[8],
|
||||
const uint8_t key_bytes[16], uint8_t out_bytes[8],
|
||||
int num_half_rounds, int old_key_schedule) {
|
||||
prince_enc_dec(in_bytes, key_bytes, out_bytes, 0, num_half_rounds,
|
||||
old_key_schedule);
|
||||
}
|
||||
|
||||
/**
|
||||
* Byte oriented top level function for Prince decryption.
|
||||
*
|
||||
* key_bytes 0 to 7 must contain K0.
|
||||
* key_bytes 8 to 15 must contain K1.
|
||||
*/
|
||||
static void prince_decrypt(const uint8_t in_bytes[8],
|
||||
const uint8_t key_bytes[16], uint8_t out_bytes[8],
|
||||
int num_half_rounds, int old_key_schedule) {
|
||||
prince_enc_dec(in_bytes, key_bytes, out_bytes, 1, num_half_rounds,
|
||||
old_key_schedule);
|
||||
uint64_t m16[2][16];
|
||||
}
|
||||
|
||||
#endif // OPENTITAN_HW_IP_PRIM_DV_PRIM_PRINCE_CRYPTO_DPI_PRINCE_PRINCE_REF_H_
|
11
vendor/lowrisc_ip/ip/prim/dv/prim_prince/data/prim_prince_cover.cfg
vendored
Normal file
11
vendor/lowrisc_ip/ip/prim/dv/prim_prince/data/prim_prince_cover.cfg
vendored
Normal file
|
@ -0,0 +1,11 @@
|
|||
// Copyright lowRISC contributors.
|
||||
// Licensed under the Apache License, Version 2.0, see LICENSE for details.
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
// Enables coverage just only each of the prim_prince instances.
|
||||
// Limits toggle coverage to DUT IOs.
|
||||
|
||||
+module prim_prince
|
||||
begin tgl(portsonly)
|
||||
+module prim_prince
|
||||
end
|
31
vendor/lowrisc_ip/ip/prim/dv/prim_prince/prim_prince_sim.core
vendored
Normal file
31
vendor/lowrisc_ip/ip/prim/dv/prim_prince/prim_prince_sim.core
vendored
Normal file
|
@ -0,0 +1,31 @@
|
|||
CAPI=2:
|
||||
# Copyright lowRISC contributors.
|
||||
# Licensed under the Apache License, Version 2.0, see LICENSE for details.
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
name: "lowrisc:dv:prim_prince_sim:0.1"
|
||||
description: "PRINCE block cipher DV sim target"
|
||||
filesets:
|
||||
files_rtl:
|
||||
depend:
|
||||
- lowrisc:prim:all
|
||||
file_type: systemVerilogSource
|
||||
|
||||
files_dv:
|
||||
depend:
|
||||
- lowrisc:dv:crypto_dpi_prince:0.1
|
||||
- lowrisc:dv:dv_utils
|
||||
- lowrisc:dv:common_ifs
|
||||
files:
|
||||
- tb/prim_prince_tb.sv
|
||||
file_type: systemVerilogSource
|
||||
|
||||
targets:
|
||||
sim: &sim_target
|
||||
toplevel: prim_prince_tb
|
||||
filesets:
|
||||
- files_rtl
|
||||
- files_dv
|
||||
default_tool: vcs
|
||||
|
||||
lint:
|
||||
<<: *sim_target
|
57
vendor/lowrisc_ip/ip/prim/dv/prim_prince/prim_prince_sim_cfg.hjson
vendored
Normal file
57
vendor/lowrisc_ip/ip/prim/dv/prim_prince/prim_prince_sim_cfg.hjson
vendored
Normal file
|
@ -0,0 +1,57 @@
|
|||
// Copyright lowRISC contributors.
|
||||
// Licensed under the Apache License, Version 2.0, see LICENSE for details.
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
{
|
||||
// Name of the sim cfg - typically same as the name of the DUT.
|
||||
name: prim_prince
|
||||
|
||||
// Top level dut name (sv module).
|
||||
dut: prim_prince
|
||||
|
||||
// Top level testbench name (sv module).
|
||||
tb: prim_prince_tb
|
||||
|
||||
// Simulator used to sign off this block
|
||||
tool: vcs
|
||||
|
||||
// Fusesoc core file used for building the file list.
|
||||
fusesoc_core: lowrisc:dv:prim_prince_sim:0.1
|
||||
|
||||
// Import additional common sim cfg files.
|
||||
import_cfgs: ["{proj_root}/hw/dv/tools/dvsim/common_sim_cfg.hjson"]
|
||||
|
||||
// Default iterations for all tests - each test entry can override this.
|
||||
reseed: 50
|
||||
|
||||
tool_srcs: ["{proj_root}/hw/ip/prim/dv/prim_prince/data/prim_prince_cover.cfg"]
|
||||
|
||||
overrides: [
|
||||
{
|
||||
name: vcs_cov_cfg_file
|
||||
value: "-cm_hier {tool_srcs_dir}/prim_prince_cover.cfg"
|
||||
}
|
||||
]
|
||||
|
||||
// List of test specifications.
|
||||
tests: [
|
||||
{
|
||||
name: prim_prince_test
|
||||
}
|
||||
]
|
||||
|
||||
// List of regressions.
|
||||
regressions: [
|
||||
{
|
||||
name: smoke
|
||||
tests: ["prim_prince_test"]
|
||||
}
|
||||
{
|
||||
name: nightly
|
||||
// Run the same test as the "smoke" regression, just with a higher reseed value.
|
||||
// This higher reseed value is due to the rather large state space created by
|
||||
// the 128-bit key and 64-bit data values.
|
||||
reseed: 500
|
||||
}
|
||||
]
|
||||
}
|
||||
|
297
vendor/lowrisc_ip/ip/prim/dv/prim_prince/tb/prim_prince_tb.sv
vendored
Normal file
297
vendor/lowrisc_ip/ip/prim/dv/prim_prince/tb/prim_prince_tb.sv
vendored
Normal file
|
@ -0,0 +1,297 @@
|
|||
// Copyright lowRISC contributors.
|
||||
// Licensed under the Apache License, Version 2.0, see LICENSE for details.
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
//
|
||||
// Testbench module for prim_prince, drives various test vectors into all
|
||||
// implementations and compares intermediate and final output against
|
||||
// C-reference model, for both encryption and decryption.
|
||||
//
|
||||
// This testbench only tests the PRINCE block cipher in its 64-bit data
|
||||
// and 128-bit key configuration, other configurations with different sets of
|
||||
// widths remain untested.
|
||||
|
||||
module prim_prince_tb;
|
||||
|
||||
//////////////////////////////////////////////////////
|
||||
// config
|
||||
//////////////////////////////////////////////////////
|
||||
|
||||
// Default to {data_width:64, key_width:128} configuration.
|
||||
// Data width and key width can be overriden from command-line if desired.
|
||||
|
||||
`ifdef DATA_WIDTH
|
||||
localparam int unsigned DataWidth = `DATA_WIDTH;
|
||||
`else
|
||||
localparam int unsigned DataWidth = 64;
|
||||
`endif
|
||||
|
||||
`ifdef KEY_WIDTH
|
||||
localparam int unsigned KeyWidth = `KEY_WIDTH;
|
||||
`else
|
||||
localparam int unsigned KeyWidth = 128;
|
||||
`endif
|
||||
|
||||
// Max number of half-rounds according to spec.
|
||||
// Duplicate parameter definition here to avoid clutter due to long identifier.
|
||||
localparam int unsigned NumRoundsHalf = crypto_dpi_prince_pkg::NumRoundsHalf;
|
||||
|
||||
// Use these to index the data/key arrays.
|
||||
localparam bit Unregistered = 1'b0;
|
||||
localparam bit Registered = 1'b1;
|
||||
|
||||
localparam bit NewKeySched = 1'b0;
|
||||
localparam bit OldKeySched = 1'b1;
|
||||
|
||||
localparam time ClkPeriod = 10000;
|
||||
|
||||
//////////////////////////////////////////////////////
|
||||
// Clock interface
|
||||
//////////////////////////////////////////////////////
|
||||
|
||||
wire clk, rst_n;
|
||||
|
||||
clk_rst_if clk_if (
|
||||
.clk,
|
||||
.rst_n
|
||||
);
|
||||
|
||||
//////////////////////////////////////////////////////
|
||||
// DUTs for both encryption and decryption
|
||||
//////////////////////////////////////////////////////
|
||||
|
||||
logic [1:0][1:0][NumRoundsHalf-1:0][DataWidth-1:0] data_in;
|
||||
logic [1:0][1:0][NumRoundsHalf-1:0][DataWidth-1:0] data_out;
|
||||
logic [1:0][1:0][NumRoundsHalf-1:0] valid_out;
|
||||
logic valid_in;
|
||||
logic [KeyWidth-1:0] key_in;
|
||||
logic dec_in;
|
||||
|
||||
for (genvar i = 0; i < 2; i++) begin : gen_new_key_schedule
|
||||
for (genvar j = 0; j < 2; j++) begin : gen_registered_variant
|
||||
for (genvar k = 0; k < NumRoundsHalf; k++) begin : gen_duts
|
||||
prim_prince #(
|
||||
.DataWidth ( DataWidth ),
|
||||
.KeyWidth ( KeyWidth ),
|
||||
.NumRoundsHalf ( k+1 ),
|
||||
.UseOldKeySched ( i ),
|
||||
.HalfwayDataReg ( j ),
|
||||
.HalfwayKeyReg ( j )
|
||||
) dut (
|
||||
.clk_i ( clk ),
|
||||
.rst_ni ( rst_n ),
|
||||
.valid_i ( valid_in ),
|
||||
.data_i ( data_in[i][j][k] ),
|
||||
.key_i ( key_in ),
|
||||
.dec_i ( dec_in ),
|
||||
.valid_o ( valid_out[i][j][k] ),
|
||||
.data_o ( data_out[i][j][k] )
|
||||
);
|
||||
end : gen_duts
|
||||
end : gen_registered_variant
|
||||
end : gen_new_key_schedule
|
||||
|
||||
//////////////////////////////////////////////////////
|
||||
// API called by the testbench to drive/check stimulus
|
||||
//////////////////////////////////////////////////////
|
||||
|
||||
// Top level API task that should be called to run a full pass
|
||||
// of encryption and decryption on some input data and key.
|
||||
task automatic test_prince(bit [DataWidth-1:0] plaintext,
|
||||
bit [KeyWidth-1:0] key);
|
||||
|
||||
bit [1:0][1:0][NumRoundsHalf-1:0][DataWidth-1:0] encrypted_text;
|
||||
$display("Starting encryption with data[0x%0x] and key[0x%0x]!", plaintext, key);
|
||||
check_encryption(plaintext, key, encrypted_text);
|
||||
$display("Starting decryption pass!");
|
||||
check_decryption(encrypted_text, key);
|
||||
|
||||
endtask
|
||||
|
||||
|
||||
|
||||
// Helper task to drive plaintext and key into each encryption instance.
|
||||
// Calls a subroutine to perform checks on the outputs (once they are available).
|
||||
task automatic check_encryption(
|
||||
input bit [DataWidth-1:0] plaintext,
|
||||
input bit [KeyWidth-1:0] key,
|
||||
output bit [1:0][1:0][NumRoundsHalf-1:0][DataWidth-1:0] expected_cipher);
|
||||
|
||||
// Drive input into encryption instances.
|
||||
key_in = key;
|
||||
dec_in = 0;
|
||||
valid_in = 1;
|
||||
for (int unsigned i = 0; i < 2; i++) begin
|
||||
for (int unsigned j = 0; j < 2; j++) begin
|
||||
for (int unsigned k = 0; k < NumRoundsHalf; k++) begin
|
||||
data_in[i][j][k] = plaintext;
|
||||
end
|
||||
end
|
||||
end
|
||||
// Wait for the DUTs to finish calculations.
|
||||
clk_if.wait_clks(2);
|
||||
wait(&valid_out == 1);
|
||||
valid_in = 0;
|
||||
// query DPI model for expected encrypted output.
|
||||
for (int i = 0; i < 2; i++) begin
|
||||
for (int j = 0; j < 2; j++) begin
|
||||
crypto_dpi_prince_pkg::sv_dpi_prince_encrypt(plaintext, key, i,
|
||||
expected_cipher[i][j]);
|
||||
end
|
||||
end
|
||||
check_output(expected_cipher[OldKeySched],
|
||||
expected_cipher[NewKeySched],
|
||||
data_out[OldKeySched],
|
||||
data_out[NewKeySched],
|
||||
"Encryption");
|
||||
endtask
|
||||
|
||||
|
||||
// Helper task to drive ciphertext and key into each decryption instance.
|
||||
// Calls a subroutine to perform checks on the outputs (once they are available).
|
||||
task automatic check_decryption(
|
||||
input bit [1:0][1:0][NumRoundsHalf-1:0][DataWidth-1:0] ciphertext,
|
||||
input bit [KeyWidth-1:0] key);
|
||||
|
||||
// the expected plaintext after decryption will be provided by the C model.
|
||||
bit [1:0][1:0][NumRoundsHalf-1:0][DataWidth-1:0] expected_plaintext;
|
||||
// Drive input into decryption instances.
|
||||
key_in = key;
|
||||
dec_in = 1;
|
||||
valid_in = 1;
|
||||
for (int unsigned i = 0; i < 2; i++) begin
|
||||
for (int unsigned j = 0; j < 2; j++) begin
|
||||
for (int unsigned k = 0; k < NumRoundsHalf; k++) begin
|
||||
data_in[i][j][k] = ciphertext[i][j][k];
|
||||
end
|
||||
end
|
||||
end
|
||||
// Wait for the DUTs to finish calculations.
|
||||
clk_if.wait_clks(2);
|
||||
wait(&valid_out == 1);
|
||||
valid_in = 0;
|
||||
// query DPI model for expected decrypted output.
|
||||
for (int unsigned i = 0; i < 2; i++) begin
|
||||
for (int unsigned j = 0; j < 2; j++) begin
|
||||
crypto_dpi_prince_pkg::sv_dpi_prince_decrypt(ciphertext[i][j], key, i,
|
||||
expected_plaintext[i][j]);
|
||||
end
|
||||
end
|
||||
check_output(expected_plaintext[OldKeySched],
|
||||
expected_plaintext[NewKeySched],
|
||||
data_out[OldKeySched],
|
||||
data_out[NewKeySched],
|
||||
"Decryption");
|
||||
endtask
|
||||
|
||||
|
||||
// Helper subroutine to compare key and data output values from
|
||||
// the C-reference model and the DUTs.
|
||||
//
|
||||
// For each instance of PRINCE (whether using old or new key schedule),
|
||||
// we need to check that the output data matches the output of the reference model.
|
||||
//
|
||||
// If any comparison error is seen, this task short-circuits immediately,
|
||||
// printing out some debug information and the correct failure signature.
|
||||
task automatic check_output(
|
||||
input bit [1:0][NumRoundsHalf-1:0][DataWidth-1:0] expected_text_old_sched,
|
||||
input bit [1:0][NumRoundsHalf-1:0][DataWidth-1:0] expected_text_new_sched,
|
||||
input bit [1:0][NumRoundsHalf-1:0][DataWidth-1:0] actual_text_old_sched,
|
||||
input bit [1:0][NumRoundsHalf-1:0][DataWidth-1:0] actual_text_new_sched,
|
||||
input string msg);
|
||||
|
||||
string reg_msg;
|
||||
string err_msg;
|
||||
for (int unsigned i = 0; i < 2; i++) begin
|
||||
reg_msg = i ? "registered" : "unregistered";
|
||||
for (int unsigned j = 0; j < NumRoundsHalf; j++) begin
|
||||
// compare outputs generated using old key schedule.
|
||||
if (expected_text_old_sched[i][j] != actual_text_old_sched[i][j]) begin
|
||||
err_msg = {$sformatf("%s mismatch in %s design with %0d rounds and old key schedule!\n",
|
||||
msg, reg_msg, i+1),
|
||||
$sformatf("Expected[0x%0x] - Actual[0x%0x]\n", expected_text_old_sched[i][j],
|
||||
actual_text_old_sched[i][j]),
|
||||
"TEST FAILED CHECKS"};
|
||||
$fatal(err_msg);
|
||||
end
|
||||
// compare outputs generated using new key schedule.
|
||||
if (expected_text_new_sched[i][j] != actual_text_new_sched[i][j]) begin
|
||||
err_msg = {$sformatf("%s mismatch in %s design with %0d rounds and old key schedule!\n",
|
||||
msg, reg_msg, i+1),
|
||||
$sformatf("Expected[0x%0x] - Actual[0x%0x]\n", expected_text_new_sched[i][j],
|
||||
actual_text_new_sched[i][j]),
|
||||
"TEST FAILED CHECKS"};
|
||||
$fatal(err_msg);
|
||||
end
|
||||
end
|
||||
end
|
||||
endtask
|
||||
|
||||
|
||||
//////////////////////////////////////////////////////
|
||||
// main testbench body
|
||||
//////////////////////////////////////////////////////
|
||||
|
||||
initial begin : p_stimuli
|
||||
|
||||
// The key and plaintext/ciphertext to be fed into PRINCE instances.
|
||||
bit [KeyWidth/2-1:0] k0, k1;
|
||||
bit [DataWidth-1:0] plaintext;
|
||||
|
||||
clk_if.set_period_ns(ClkPeriod);
|
||||
clk_if.set_active();
|
||||
clk_if.apply_reset();
|
||||
$timeformat(-12, 0, " ps", 12);
|
||||
clk_if.wait_clks(10);
|
||||
|
||||
/////////////////////////////
|
||||
// Test the 5 golden vectors.
|
||||
/////////////////////////////
|
||||
|
||||
plaintext = '0;
|
||||
k0 = '1;
|
||||
k1 = '0;
|
||||
test_prince(plaintext, {k1, k0});
|
||||
|
||||
plaintext = '0;
|
||||
k0 = '0;
|
||||
k1 = '0;
|
||||
test_prince(plaintext, {k1, k0});
|
||||
|
||||
plaintext = '1;
|
||||
k0 = '0;
|
||||
k1 = '0;
|
||||
test_prince(plaintext, {k1, k0});
|
||||
|
||||
plaintext = '0;
|
||||
k0 = '0;
|
||||
k1 = '1;
|
||||
test_prince(plaintext, {k1, k0});
|
||||
|
||||
plaintext = 'h0123456789ABCDEF;
|
||||
k0 = '0;
|
||||
k1 = 'hFEDC_BA98_7654_3210;
|
||||
test_prince(plaintext, {k1, k0});
|
||||
|
||||
// Test random vectors
|
||||
for (int i = 0; i < 25000; i++) begin
|
||||
if (!std::randomize(plaintext)) begin
|
||||
$fatal("Randomization of plaintext failed!");
|
||||
end
|
||||
if (!std::randomize(k0)) begin
|
||||
$fatal("Randomization of k0 failed!");
|
||||
end
|
||||
if (!std::randomize(k1)) begin
|
||||
$fatal("Randomization of k1 failed!");
|
||||
end
|
||||
test_prince(plaintext, {k1, k0});
|
||||
end
|
||||
|
||||
|
||||
// Final error checking and print out the test signature (expected by simulation flow).
|
||||
$display("All encryption and decryption passes were successful!");
|
||||
$display("TEST PASSED CHECKS");
|
||||
$finish();
|
||||
end
|
||||
|
||||
|
||||
endmodule : prim_prince_tb
|
|
@ -26,3 +26,6 @@ targets:
|
|||
|
||||
formal:
|
||||
<<: *default_target
|
||||
|
||||
lint:
|
||||
<<: *default_target
|
||||
|
|
|
@ -26,3 +26,6 @@ targets:
|
|||
|
||||
formal:
|
||||
<<: *default_target
|
||||
|
||||
lint:
|
||||
<<: *default_target
|
||||
|
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Add table
Add a link
Reference in a new issue