mirror of
https://github.com/lowRISC/ibex.git
synced 2025-04-24 13:57:19 -04:00
Update lowrisc_ip to lowRISC/opentitan@da3ac7c4e
Update code from upstream repository https://github.com/lowRISC/opentitan to revision da3ac7c4eb23a92194874ad2daf2e5f9e3330572 * [memutil] Allow use without scrambled memories (Philipp Wagner) * [prim_prince] Fix comment (Philipp Wagner) * [memutil] Fix width mismatch (Philipp Wagner) * [prim] Allow disabling SVAs ensuring REQ is held until ACK at run time (Pirmin Vogel) * [prim] Fix typo that caused fifo_async to get stuck (Timothy Chen) * [prim] Add a missing ROM_CFG_DEFAULT to prim_rom_pkg.sv (Rupert Swarbrick) * [dvsim] Do not assume the build failed if "ERROR" is printed (Philipp Wagner) * [prim_subreg_shadow] Invert meaning of SWACCESS in shadow/stage regs (Michael Schaffner) * [prim_arb_tree/rv_plic_target] Remove TODOs due to a Vivado tool bug (Michael Schaffner) * [primgen] Remove unused import (Philipp Wagner) * [primgen] Add shebang (Philipp Wagner) * [primgen] Make primgen "portable" again (Philipp Wagner) * [dv] Small optimization in memutil (Philipp Wagner) * [tools/ascent] updated ascent to use the --job-prefix option (Rasmus Madsen) * [otp_ctrl] Remove invalid command error (Michael Schaffner) * [tlul] Add some missing dependencies (Michael Schaffner) * [otbn/otp_ctrl] Replicate dmem scrambling keystream (Michael Schaffner) * [adc_ctrl] Various preparation steps for d2 (Timothy Chen) * [tools/dvsim] Fix some VCS flags (Guillermo Maturana) * Revert "[prim] Do remove prim_esc.core from the dependencies" (Rupert Swarbrick) * [prim] Remove dependency of prim:esc on a hardware block (Rupert Swarbrick) * [lint] prim_ram_1p_scr verilator lint fixes (Greg Chadwick) * [dv] Add scrambled_ecc32_mem_area for memutils (Greg Chadwick) * [dv] Add C++ memory scrambling model (Greg Chadwick) * [tools/dsim] Fix non-LRM compliant code (Guillermo Maturana) * [prim] Do remove prim_esc.core from the dependencies (Michael Schaffner) * [dv/dv_utils] Improvement on `max` function (Cindy Chen) * [alert_handler] Implement reverse ping feature (Michael Schaffner) * [prim_esc] Split the prims into their own core file (Michael Schaffner) * [dvsim] Fix GUI mode and launcher creation fixes (Srikrishna Iyer) * [dv/common] Stress_all_with_rand_reset apply reset concurrently (Cindy Chen) * [dv/all] update scoreboard `csr_addrs` accesses (Udi Jonnalagadda) * [dv/csr_utils] update unmapped_addr calculation (Udi Jonnalagadda) * [dv] Update intg alert names (Weicai Yang) * [dv, flash_ctrl] Fix the intr test (Srikrishna Iyer) * [prim_fifo_async] Fix a width calculation issue in case of Depth = 1 (Michael Schaffner) * [dv] Update VCS opt for uvm_hdl_* (Weicai Yang) * [dv, util] Make poll_for_stop() opt-in (Srikrishna Iyer) * [dvsim] Separate publish report option [PART1] (Cindy Chen) * [dv/kmac/sram] reduce iterations of smoke test (Udi Jonnalagadda) * [dv/stress_all_with_reset] Revert back IPs that uses apply_reset (Cindy Chen) * [dv/edn_reset] Fix apply_reset to concurrently deassert resets (Cindy Chen) * [dv] Update VCS cov merge opts (Srikrishna Iyer) * [dv] Add TL integrity error test for CSR (Weicai Yang) * [dv, chip] Remove USB clk driver (Srikrishna Iyer) * [script/dvsim] Update output folder (Cindy Chen) * [dv/edn_reset] Update IPs that overrides apply_reset task (Cindy Chen) * [dv/edn_reset] Fix stress_all_with_rand_reset error (Cindy Chen) * [dv/dv_base_scoreboard] remove duplicated code (Cindy Chen) * [otbn,dv] Teach otbn_memutil to track expected end address (Rupert Swarbrick) * [dv, dv_utils_pkg] Fix common int typedefs (Srikrishna Iyer) * [prim_lfsr] Fix spyglass lint warnings (Michael Schaffner) * [prim_clock_gating] Target 7series Xilinx devices (Philipp Wagner) * [dv/edn_rst] Add coverage to collect edn reset and dut reset (Cindy Chen) * [otp_ctrl/lc_ctrl] Add LC TAP register to control OTP test mechanisms (Michael Schaffner) * [prim_alert*/prim_esc*] Rework placement of size_only bufs/flops (Michael Schaffner) * [dv] fix a typo in tl_device_access_types_testplan (Weicai Yang) * [prim_otp] Rework generic model to match new error behavior (Michael Schaffner) * [dv/tlul_common_test] Add a testplan for TLUL integrity check (Cindy Chen) * [dvsim] Allow recursive testplan import (Srikrishna Iyer) * [primgen] Use verible-verilog-syntax for parsing (Mariusz Glebocki) * [prim] Break always_comb block to avoid apparent loop (Rupert Swarbrick) * [dvsim] Fix testplan bugs (Srikrishna Iyer) * [fpv] update secded_gen (Cindy Chen) * [dv/template] small fixes on index.md format (Cindy Chen) * [prim_otp] Add a waiver for power signal unused in generic prim (Michael Schaffner) * [simutil_verilator] Improve timeout handling (Rupert Swarbrick) * [testplans] Rename entries with testpoints (Srikrishna Iyer) * [dvsim/testplan] Fix the rendered testplan (Srikrishna Iyer) * [dv/cov] exclude prim_lfsr and prim_prince (Udi Jonnalagadda) Signed-off-by: Philipp Wagner <phw@lowrisc.org>
This commit is contained in:
parent
270cd91b38
commit
d003d479ff
122 changed files with 3416 additions and 663 deletions
|
@ -60,28 +60,6 @@ package csr_utils_pkg;
|
|||
under_reset = 0;
|
||||
endfunction
|
||||
|
||||
// Get all valid csr addrs - useful to check if incoming addr falls in the csr range.
|
||||
function automatic void get_csr_addrs(input uvm_reg_block ral, ref uvm_reg_addr_t csr_addrs[$]);
|
||||
uvm_reg csrs[$];
|
||||
ral.get_registers(csrs);
|
||||
foreach (csrs[i]) begin
|
||||
csr_addrs.push_back(csrs[i].get_address());
|
||||
end
|
||||
endfunction
|
||||
|
||||
// Get all valid mem addr ranges - useful to check if incoming addr falls in the mem range.
|
||||
function automatic void get_mem_addr_ranges(uvm_reg_block ral, ref addr_range_t mem_ranges[$]);
|
||||
uvm_mem mems[$];
|
||||
ral.get_memories(mems);
|
||||
foreach (mems[i]) begin
|
||||
addr_range_t mem_range;
|
||||
mem_range.start_addr = mems[i].get_address();
|
||||
mem_range.end_addr = mem_range.start_addr +
|
||||
mems[i].get_size() * mems[i].get_n_bytes() - 1;
|
||||
mem_ranges.push_back(mem_range);
|
||||
end
|
||||
endfunction
|
||||
|
||||
// get mem object from address
|
||||
function automatic uvm_mem get_mem_by_addr(uvm_reg_block ral, uvm_reg_addr_t addr);
|
||||
uvm_mem mem;
|
||||
|
|
|
@ -16,6 +16,15 @@ class dv_base_reg_block extends uvm_reg_block;
|
|||
// This is set by compute_addr_mask(), which must run after locking the model.
|
||||
protected uvm_reg_addr_t addr_mask[uvm_reg_map];
|
||||
|
||||
uvm_reg_addr_t csr_addrs[$];
|
||||
|
||||
addr_range_t mem_ranges[$];
|
||||
|
||||
addr_range_t mapped_addr_ranges[$];
|
||||
|
||||
bit has_unmapped_addrs;
|
||||
addr_range_t unmapped_addr_ranges[$];
|
||||
|
||||
function new (string name = "", int has_coverage = UVM_NO_COVERAGE);
|
||||
super.new(name, has_coverage);
|
||||
endfunction
|
||||
|
@ -94,6 +103,98 @@ class dv_base_reg_block extends uvm_reg_block;
|
|||
`DV_CHECK_FATAL(addr_mask[map])
|
||||
endfunction
|
||||
|
||||
// Internal function, used to get a list of all valid CSR addresses.
|
||||
protected function void compute_csr_addrs();
|
||||
uvm_reg csrs[$];
|
||||
get_registers(csrs);
|
||||
foreach (csrs[i]) begin
|
||||
csr_addrs.push_back(csrs[i].get_address());
|
||||
end
|
||||
endfunction
|
||||
|
||||
// Internal function, used to get a list of all valid memory ranges
|
||||
protected function void compute_mem_addr_ranges();
|
||||
uvm_mem mems[$];
|
||||
get_memories(mems);
|
||||
foreach (mems[i]) begin
|
||||
addr_range_t mem_range;
|
||||
mem_range.start_addr = mems[i].get_address();
|
||||
mem_range.end_addr = mem_range.start_addr +
|
||||
mems[i].get_size() * mems[i].get_n_bytes() - 1;
|
||||
mem_ranges.push_back(mem_range);
|
||||
end
|
||||
endfunction
|
||||
|
||||
// Used to get a list of all valid address ranges covered by this reg block
|
||||
function void compute_mapped_addr_ranges();
|
||||
uvm_reg csrs[$];
|
||||
get_registers(csrs);
|
||||
|
||||
// Compute all CSR addresses and mem ranges known to this reg block
|
||||
compute_csr_addrs();
|
||||
compute_mem_addr_ranges();
|
||||
|
||||
// Convert each CSR into an address range
|
||||
foreach (csrs[i]) begin
|
||||
addr_range_t csr_addr_range;
|
||||
csr_addr_range.start_addr = csrs[i].get_address();
|
||||
csr_addr_range.end_addr = csr_addr_range.start_addr + csrs[i].get_n_bytes() - 1;
|
||||
mapped_addr_ranges.push_back(csr_addr_range);
|
||||
end
|
||||
|
||||
mapped_addr_ranges = {mapped_addr_ranges, mem_ranges};
|
||||
|
||||
// Sort the mapped address ranges in ascending order based on the start_addr of each range
|
||||
mapped_addr_ranges.sort(m) with (m.start_addr);
|
||||
|
||||
endfunction
|
||||
|
||||
// Used to get a list of all invalid address ranges in this reg block
|
||||
function void compute_unmapped_addr_ranges();
|
||||
addr_range_t range;
|
||||
|
||||
// convert the address mask into a relative address,
|
||||
// this is the highest addressable location in the register block
|
||||
uvm_reg_addr_t highest_addr = default_map.get_base_addr() + get_addr_mask();
|
||||
|
||||
// unmapped address ranges consist of:
|
||||
// - the address space between all mapped address ranges (if exists)
|
||||
// - space between csr_base_addr and the first mapped address range (if exists)
|
||||
// - space between the last mapped address and the highest address mapped by the address mask
|
||||
if (mapped_addr_ranges.size() == 0) begin
|
||||
range.start_addr = default_map.get_base_addr();
|
||||
range.end_addr = highest_addr;
|
||||
unmapped_addr_ranges.push_back(range);
|
||||
end else begin
|
||||
// 0 -> start_addr-1
|
||||
if (mapped_addr_ranges[0].start_addr - default_map.get_base_addr() > 0) begin
|
||||
range.start_addr = default_map.get_base_addr();
|
||||
range.end_addr = mapped_addr_ranges[0].start_addr - 1;
|
||||
unmapped_addr_ranges.push_back(range);
|
||||
end
|
||||
|
||||
// all address ranges in the "middle" - only applies if
|
||||
// there are more than 1 mapped address ranges
|
||||
if (mapped_addr_ranges.size() > 1) begin
|
||||
for (int i = 0; i < mapped_addr_ranges.size() - 1; i++) begin
|
||||
range.start_addr = mapped_addr_ranges[i].end_addr + 1;
|
||||
range.end_addr = mapped_addr_ranges[i+1].start_addr - 1;
|
||||
if (range.start_addr < range.end_addr) begin
|
||||
unmapped_addr_ranges.push_back(range);
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
// end_addr+1 -> highest_addr
|
||||
if (mapped_addr_ranges[$].end_addr < highest_addr) begin
|
||||
range.start_addr = mapped_addr_ranges[$].end_addr + 1;
|
||||
range.end_addr = highest_addr;
|
||||
unmapped_addr_ranges.push_back(range);
|
||||
end
|
||||
end
|
||||
has_unmapped_addrs = (unmapped_addr_ranges.size() > 0);
|
||||
endfunction
|
||||
|
||||
// Return the offset of the highest byte contained in either a register or a memory
|
||||
function uvm_reg_addr_t get_max_offset(uvm_reg_map map = null);
|
||||
uvm_reg_addr_t max_offset;
|
||||
|
@ -143,6 +244,7 @@ class dv_base_reg_block extends uvm_reg_block;
|
|||
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;)
|
||||
|
|
|
@ -17,7 +17,7 @@ class dv_base_env_cfg #(type RAL_T = dv_base_reg_block) extends uvm_object;
|
|||
// useful to keep the runtime down especially in time-sensitive runs such as CI, which is meant
|
||||
// to check the code health and not find design bugs. It is set via plusarg and retrieved in
|
||||
// `dv_base_test`.
|
||||
bit smoke_test = 0;
|
||||
bit smoke_test = 0;
|
||||
|
||||
// bit to configure all uvcs with zero delays to create high bw test
|
||||
rand bit zero_delays;
|
||||
|
@ -38,9 +38,6 @@ class dv_base_env_cfg #(type RAL_T = dv_base_reg_block) extends uvm_object;
|
|||
// super.initialize(csr_base_addr);
|
||||
string ral_model_names[$] = {RAL_T::type_name};
|
||||
|
||||
bit [bus_params_pkg::BUS_AW-1:0] csr_addrs[string][$];
|
||||
addr_range_t mem_ranges[string][$];
|
||||
|
||||
// clk_rst_if & freq
|
||||
// clk_rst_vif and clk_freq_mhz are used for default clk/rst. If more than one RAL, the other
|
||||
// clk_rst_vif and clk_freq_mhz can be found from the associative arrays
|
||||
|
@ -50,10 +47,10 @@ class dv_base_env_cfg #(type RAL_T = dv_base_reg_block) extends uvm_object;
|
|||
rand clk_freq_mhz_e clk_freqs_mhz[string];
|
||||
|
||||
`uvm_object_param_utils_begin(dv_base_env_cfg #(RAL_T))
|
||||
`uvm_field_int (is_active, UVM_DEFAULT)
|
||||
`uvm_field_int (en_scb, UVM_DEFAULT)
|
||||
`uvm_field_int (en_cov, UVM_DEFAULT)
|
||||
`uvm_field_int (zero_delays, UVM_DEFAULT)
|
||||
`uvm_field_int (is_active, UVM_DEFAULT)
|
||||
`uvm_field_int (en_scb, UVM_DEFAULT)
|
||||
`uvm_field_int (en_cov, UVM_DEFAULT)
|
||||
`uvm_field_int (zero_delays, UVM_DEFAULT)
|
||||
`uvm_object_utils_end
|
||||
|
||||
`uvm_object_new
|
||||
|
@ -98,6 +95,7 @@ class dv_base_env_cfg #(type RAL_T = dv_base_reg_block) extends uvm_object;
|
|||
endfunction
|
||||
|
||||
virtual function void create_ral_models(bit [bus_params_pkg::BUS_AW-1:0] csr_base_addr = '1);
|
||||
|
||||
foreach (ral_model_names[i]) begin
|
||||
string ral_name = ral_model_names[i];
|
||||
uvm_reg_addr_t base_addr;
|
||||
|
@ -123,8 +121,16 @@ class dv_base_env_cfg #(type RAL_T = dv_base_reg_block) extends uvm_object;
|
|||
reg_blk.set_base_addr(base_addr);
|
||||
|
||||
// Get list of valid csr addresses (useful in seq to randomize addr as well as in scb checks)
|
||||
get_csr_addrs(reg_blk, csr_addrs[ral_name]);
|
||||
get_mem_addr_ranges(reg_blk, mem_ranges[ral_name]);
|
||||
reg_blk.compute_mapped_addr_ranges();
|
||||
reg_blk.compute_unmapped_addr_ranges();
|
||||
`uvm_info(msg_id,
|
||||
$sformatf("RAL[%0s] mapped addresses: %0p",
|
||||
ral_name, reg_blk.mapped_addr_ranges),
|
||||
UVM_HIGH)
|
||||
`uvm_info(msg_id,
|
||||
$sformatf("RAL[%0s] unmapped addresses: %0p",
|
||||
ral_name, reg_blk.unmapped_addr_ranges),
|
||||
UVM_HIGH)
|
||||
ral_models[ral_name] = reg_blk;
|
||||
end
|
||||
|
||||
|
|
|
@ -25,6 +25,7 @@ class dv_base_scoreboard #(type RAL_T = dv_base_reg_block,
|
|||
super.run_phase(phase);
|
||||
fork
|
||||
monitor_reset();
|
||||
sample_resets();
|
||||
join_none
|
||||
endtask
|
||||
|
||||
|
@ -33,12 +34,10 @@ class dv_base_scoreboard #(type RAL_T = dv_base_reg_block,
|
|||
if (!cfg.clk_rst_vif.rst_n) begin
|
||||
`uvm_info(`gfn, "reset occurred", UVM_HIGH)
|
||||
cfg.reset_asserted();
|
||||
csr_utils_pkg::reset_asserted();
|
||||
@(posedge cfg.clk_rst_vif.rst_n);
|
||||
reset();
|
||||
cfg.reset_deasserted();
|
||||
csr_utils_pkg::clear_outstanding_access();
|
||||
csr_utils_pkg::reset_deasserted();
|
||||
`uvm_info(`gfn, "out of reset", UVM_HIGH)
|
||||
end
|
||||
else begin
|
||||
|
@ -48,6 +47,10 @@ class dv_base_scoreboard #(type RAL_T = dv_base_reg_block,
|
|||
end
|
||||
endtask
|
||||
|
||||
virtual task sample_resets();
|
||||
// Do nothing, actual coverage collection is under extended classes.
|
||||
endtask
|
||||
|
||||
virtual function void reset(string kind = "HARD");
|
||||
// reset the ral model
|
||||
foreach (cfg.ral_models[i]) cfg.ral_models[i].reset(kind);
|
||||
|
|
|
@ -14,7 +14,7 @@ class dv_base_test #(type CFG_T = dv_base_env_cfg,
|
|||
uint max_quit_count = 1;
|
||||
uint64 test_timeout_ns = 200_000_000; // 200ms
|
||||
uint drain_time_ns = 2_000; // 2us
|
||||
bit poll_for_stop = 1'b1;
|
||||
bit poll_for_stop = 1'b0;
|
||||
uint poll_for_stop_interval_ns = 1000;
|
||||
|
||||
`uvm_component_new
|
||||
|
|
47
vendor/lowrisc_ip/dv/sv/dv_lib/dv_base_vseq.sv
vendored
47
vendor/lowrisc_ip/dv/sv/dv_lib/dv_base_vseq.sv
vendored
|
@ -86,7 +86,7 @@ class dv_base_vseq #(type RAL_T = dv_base_reg_block,
|
|||
|
||||
virtual task apply_reset(string kind = "HARD");
|
||||
if (kind == "HARD") begin
|
||||
if (cfg.clk_rst_vifs.size > 0) begin
|
||||
if (cfg.clk_rst_vifs.size() > 0) begin
|
||||
fork
|
||||
begin : isolation_fork
|
||||
foreach (cfg.clk_rst_vifs[i]) begin
|
||||
|
@ -104,6 +104,39 @@ class dv_base_vseq #(type RAL_T = dv_base_reg_block,
|
|||
end // if (kind == "HARD")
|
||||
endtask
|
||||
|
||||
// Apply all resets in the DUT concurrently to generate a random in-test reset scenario.
|
||||
//
|
||||
// - Assert resets concurrently to make sure all resets are issued.
|
||||
// - Deassert resets concurrently is a specific requirement of the `stress_all_with_rand_reset`
|
||||
// sequence, which will randomly issue resets and terminate the parallel sequence once all DUT
|
||||
// resets are deasserted. If DUT resets are deasserted at different time, the parallel sequence
|
||||
// might send a transaction request to driver between different resets are deasserting. Then when
|
||||
// `stress_all_with_rand_reset` sequence tries to terminate the parallel sequence, an UVM_ERROR
|
||||
// will be thrown by the sequencer saying `task responsible for requesting a wait_for_grant has
|
||||
// been killed`.
|
||||
// In order to ensure all resets at least being asserted for one clock cycle, this task takes an
|
||||
// optional input `reset_duration_ps` if the DUT has additional resets. The task uses this input
|
||||
// to compute the minimal time required to keep all resets asserted.
|
||||
virtual task apply_resets_concurrently(int reset_duration_ps = 0);
|
||||
|
||||
// Has one or more RAL models in DUT.
|
||||
if (cfg.clk_rst_vifs.size() > 0) begin
|
||||
foreach (cfg.clk_rst_vifs[i]) begin
|
||||
cfg.clk_rst_vifs[i].drive_rst_pin(0);
|
||||
reset_duration_ps = max2(reset_duration_ps, cfg.clk_rst_vifs[i].clk_period_ps);
|
||||
end
|
||||
#(reset_duration_ps * $urandom_range(2, 10) * 1ps);
|
||||
foreach (cfg.clk_rst_vifs[i]) cfg.clk_rst_vifs[i].drive_rst_pin(1);
|
||||
|
||||
// No RAL model and only has default clk_rst_vif.
|
||||
end else begin
|
||||
cfg.clk_rst_vif.drive_rst_pin(0);
|
||||
reset_duration_ps = max2(reset_duration_ps, cfg.clk_rst_vif.clk_period_ps);
|
||||
#(reset_duration_ps * $urandom_range(2, 10) * 1ps);
|
||||
cfg.clk_rst_vif.drive_rst_pin(1);
|
||||
end
|
||||
endtask
|
||||
|
||||
virtual task wait_for_reset(string reset_kind = "HARD",
|
||||
bit wait_for_assert = 1,
|
||||
bit wait_for_deassert = 1);
|
||||
|
@ -179,7 +212,11 @@ class dv_base_vseq #(type RAL_T = dv_base_reg_block,
|
|||
|
||||
// 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;
|
||||
// We have to assign this array in a loop because the element types aren't equivalent, so
|
||||
// the array types aren't assignment compatible.
|
||||
foreach (cfg.ral_models[i]) begin
|
||||
m_csr_write_seq.models[i] = cfg.ral_models[i];
|
||||
end
|
||||
m_csr_write_seq.external_checker = cfg.en_scb;
|
||||
m_csr_write_seq.en_rand_backdoor_write = 1'b1;
|
||||
m_csr_write_seq.start(null);
|
||||
|
@ -196,7 +233,11 @@ class dv_base_vseq #(type RAL_T = dv_base_reg_block,
|
|||
|
||||
// create base csr seq and pass our ral
|
||||
m_csr_seq = csr_base_seq::type_id::create("m_csr_seq");
|
||||
m_csr_seq.models = cfg.ral_models;
|
||||
// We have to assign this array in a loop because the element types aren't equivalent, so
|
||||
// the array types aren't assignment compatible.
|
||||
foreach (cfg.ral_models[i]) begin
|
||||
m_csr_seq.models[i] = cfg.ral_models[i];
|
||||
end
|
||||
m_csr_seq.external_checker = cfg.en_scb;
|
||||
m_csr_seq.num_test_csrs = num_test_csrs;
|
||||
m_csr_seq.start(null);
|
||||
|
|
26
vendor/lowrisc_ip/dv/sv/dv_utils/dv_utils_pkg.sv
vendored
26
vendor/lowrisc_ip/dv/sv/dv_utils/dv_utils_pkg.sv
vendored
|
@ -14,8 +14,11 @@ package dv_utils_pkg;
|
|||
`endif
|
||||
|
||||
// common parameters used across all benches
|
||||
parameter int NUM_MAX_INTERRUPTS = 32;
|
||||
parameter int NUM_MAX_ALERTS = 32;
|
||||
parameter int NUM_MAX_INTERRUPTS = 32;
|
||||
typedef logic [NUM_MAX_INTERRUPTS-1:0] interrupt_t;
|
||||
|
||||
parameter int NUM_MAX_ALERTS = 32;
|
||||
typedef logic [NUM_MAX_ALERTS-1:0] alert_t;
|
||||
|
||||
// types & variables
|
||||
typedef bit [31:0] uint;
|
||||
|
@ -24,6 +27,13 @@ package dv_utils_pkg;
|
|||
typedef bit [31:0] uint32;
|
||||
typedef bit [63:0] uint64;
|
||||
|
||||
// TODO: The above typedefs violate the name rule, which is fixed below. Cleanup the codebase to
|
||||
// use the typedefs below and remove the ones above.
|
||||
typedef bit [7:0] uint8_t;
|
||||
typedef bit [15:0] uint16_t;
|
||||
typedef bit [31:0] uint32_t;
|
||||
typedef bit [63:0] uint64_t;
|
||||
|
||||
// typedef parameterized pins_if for ease of implementation for interrupts and alerts
|
||||
typedef virtual pins_if #(NUM_MAX_INTERRUPTS) intr_vif;
|
||||
typedef virtual pins_if #(1) devmode_vif;
|
||||
|
@ -87,6 +97,16 @@ package dv_utils_pkg;
|
|||
return (a > b) ? a : b;
|
||||
endfunction
|
||||
|
||||
// return the biggest value within the given queue of integers.
|
||||
function automatic int max(const ref int int_q[$]);
|
||||
`DV_CHECK_GT_FATAL(int_q.size(), 0, "max function cannot accept an empty queue of integers!",
|
||||
msg_id)
|
||||
// Assign the first value from the queue in case of negative integers.
|
||||
max = int_q[0];
|
||||
foreach (int_q[i]) max = max2(max, int_q[i]);
|
||||
return max;
|
||||
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;
|
||||
|
@ -182,7 +202,7 @@ package dv_utils_pkg;
|
|||
|
||||
// Periodically check for the existence of a magic file (dv.stop). Exit if it exists. This
|
||||
// provides a mechanism to gracefully kill a simulation without direct access to the process.
|
||||
task automatic poll_for_stop(uint interval_ns = 1000, string filename = "dv.stop");
|
||||
task automatic poll_for_stop(uint interval_ns = 10_000, string filename = "dv.stop");
|
||||
fork
|
||||
while (1) begin
|
||||
#(interval_ns * 1ns);
|
||||
|
|
|
@ -21,7 +21,9 @@
|
|||
|
||||
// pass and fail patterns
|
||||
build_pass_patterns: []
|
||||
build_fail_patterns: ["^ERROR:.*$"] // fusesoc build error
|
||||
// TODO: Add back FuseSoC fail pattern after
|
||||
// https://github.com/lowRISC/opentitan/issues/7348 is resolved.
|
||||
build_fail_patterns: []
|
||||
run_pass_patterns: ["^TEST PASSED (UVM_)?CHECKS$"]
|
||||
run_fail_patterns: ["^UVM_ERROR\\s[^:].*$",
|
||||
"^UVM_FATAL\\s[^:].*$",
|
||||
|
|
|
@ -12,21 +12,22 @@
|
|||
{
|
||||
name: tl_d_illegal_access
|
||||
desc: '''Drive unsupported requests via TL interface and verify correctness of response
|
||||
/ behavior. Below error cases are tested
|
||||
/ behavior. Below error cases are tested bases on the
|
||||
[TLUL spec]({{< relref "hw/ip/tlul/doc/_index.md#explicit-error-cases" >}})
|
||||
- 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.
|
||||
- invalid opcode
|
||||
- some mask bits not set when opcode is `PutFullData`
|
||||
- mask does not match the transfer size, e.g. `a_address = 0x00`, `a_size = 0`,
|
||||
`a_mask = 'b0010`
|
||||
- mask and address misaligned, e.g. `a_address = 0x01`, `a_mask = 'b0001`
|
||||
- address and size aren't aligned, e.g. `a_address = 0x01`, `a_size != 0`
|
||||
- size is greater than 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'''
|
||||
- access unmapped address, expect `d_error = 1` when `devmode_i == 1`
|
||||
- write a CSR with unaligned address, e.g. `a_address[1:0] != 0`
|
||||
- write a CSR less than its width, e.g. when CSR is 2 bytes wide, only write 1 byte
|
||||
- write a memory with `a_mask != '1` when it doesn't support partial accesses
|
||||
- read a WO (write-only) memory'''
|
||||
milestone: V2
|
||||
tests: ["{name}_tl_errors"]
|
||||
}
|
||||
|
@ -43,15 +44,24 @@
|
|||
}
|
||||
{
|
||||
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'''
|
||||
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"]
|
||||
}
|
||||
{
|
||||
name: tl_intg_err
|
||||
desc: ''' Verify that the data integrity check violation generates an alert.
|
||||
|
||||
Randomly inject errors on the control, data, or the ECC bits during CSR accesses.
|
||||
Verify that triggers the correct fatal alert.'''
|
||||
milestone: V3
|
||||
tests: ["{name}_tl_intg_err"]
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
|
|
|
@ -16,5 +16,14 @@
|
|||
run_opts: ["+run_tl_errors"]
|
||||
reseed: 20
|
||||
}
|
||||
{
|
||||
name: "{name}_tl_intg_err"
|
||||
build_mode: "cover_reg_top"
|
||||
uvm_test_seq: "{name}_common_vseq"
|
||||
run_opts: ["+run_tl_intg_err", "+en_scb=0"]
|
||||
// TODO, lower the reseed to avoid having many failed tests in regression as not all the IPs
|
||||
// have the integrity alert connected
|
||||
reseed: 1
|
||||
}
|
||||
]
|
||||
}
|
||||
|
|
55
vendor/lowrisc_ip/dv/tools/dvsim/vcs.hjson
vendored
55
vendor/lowrisc_ip/dv/tools/dvsim/vcs.hjson
vendored
|
@ -48,7 +48,10 @@
|
|||
// - Read capability on registers, variables, and nets
|
||||
// - Write (deposit) capability on registers and variables
|
||||
// - Force capability on registers, variables, and nets
|
||||
// TODO Trim this flag since +f hurts performance.
|
||||
"-debug_access+f",
|
||||
// This option is needed for uvm_hdl_*, when it accesses the array under `celldefine
|
||||
"-debug_region=cell+lib",
|
||||
// Use this to conditionally compile for VCS (example: LRM interpretations differ
|
||||
// across tools).
|
||||
"+define+VCS",
|
||||
|
@ -125,31 +128,47 @@
|
|||
cov_merge_dir: "{scratch_path}/cov_merge"
|
||||
cov_merge_db_dir: "{cov_merge_dir}/merged.vdb"
|
||||
cov_merge_cmd: "{job_prefix} urg"
|
||||
cov_merge_opts: ["-full64",
|
||||
"+urg+lic+wait",
|
||||
"-nocheck",
|
||||
cov_merge_opts: ["-lca",
|
||||
"-full64",
|
||||
// No need of generating report when merging coverage.
|
||||
"-noreport",
|
||||
"-flex_merge drop",
|
||||
"-group merge_across_scopes",
|
||||
// Merge results from tests in parallel.
|
||||
"-parallel",
|
||||
"-parallel_split 20",
|
||||
"-parallel_temproot {cov_merge_dir}",
|
||||
"+urg+lic+wait",
|
||||
// Merge same assert instances found in different VDBs.
|
||||
"-merge_across_libs",
|
||||
// This enables grading on the merged vdb.
|
||||
"-show tests",
|
||||
// Enable union mode of flexible merging for covergroups.
|
||||
"-flex_merge union",
|
||||
// Use cov_db_dirs var for dir args; append -dir in front of each
|
||||
"{eval_cmd} echo {cov_db_dirs} | sed -E 's/(\\S+)/-dir \\1/g'",
|
||||
"-dbname {cov_merge_db_dir}"]
|
||||
|
||||
// Generate coverage reports in text as well as html.
|
||||
cov_report_dir: "{scratch_path}/cov_report"
|
||||
cov_report_cmd: "{job_prefix} urg"
|
||||
cov_report_opts: ["-full64",
|
||||
"+urg+lic+wait",
|
||||
"-dir {cov_merge_db_dir}",
|
||||
"-group instcov_for_score",
|
||||
"-line nocasedef",
|
||||
"-format both",
|
||||
"-elfile {vcs_cov_excl_files}",
|
||||
"-report {cov_report_dir}"]
|
||||
cov_report_txt: "{cov_report_dir}/dashboard.txt"
|
||||
cov_report_page: "dashboard.html"
|
||||
cov_report_dir: "{scratch_path}/cov_report"
|
||||
cov_report_cmd: "{job_prefix} urg"
|
||||
cov_report_opts: ["-lca",
|
||||
"-full64",
|
||||
"+urg+lic+wait",
|
||||
// Lists all the tests that covered a given object.
|
||||
"-show tests",
|
||||
// Enable test grading.
|
||||
"-grade index",
|
||||
// Use simple ratio of total covered bins over total bins across cps & crs,
|
||||
"-group ratio",
|
||||
// Compute overall coverage for per-instance covergroups individually rather
|
||||
// than cumulatively.
|
||||
"-group instcov_for_score",
|
||||
"-dir {cov_merge_db_dir}",
|
||||
"-line nocasedef",
|
||||
"-format both",
|
||||
"-elfile {vcs_cov_excl_files}",
|
||||
"-report {cov_report_dir}"]
|
||||
cov_report_txt: "{cov_report_dir}/dashboard.txt"
|
||||
cov_report_page: "dashboard.html"
|
||||
|
||||
// UNR related.
|
||||
// All code coverage, assert isn't supported
|
||||
|
@ -227,7 +246,7 @@
|
|||
{
|
||||
name: vcs_waves
|
||||
is_sim_mode: 1
|
||||
build_opts: ["-debug_access+all"]
|
||||
build_opts: ["-debug_access"]
|
||||
}
|
||||
{
|
||||
name: vcs_cov
|
||||
|
|
4
vendor/lowrisc_ip/dv/tools/vcs/cover.cfg
vendored
4
vendor/lowrisc_ip/dv/tools/vcs/cover.cfg
vendored
|
@ -9,9 +9,13 @@
|
|||
-module pins_if // DV construct.
|
||||
-module clk_rst_if // DV construct.
|
||||
-moduletree prim_alert_sender // prim_alert_sender is verified in FPV.
|
||||
-moduletree prim_prince // prim_prince is verified in a separate DV environment.
|
||||
-moduletree prim_lfsr // prim_lfsr is verified in FPV.
|
||||
|
||||
begin tgl
|
||||
-tree tb
|
||||
+tree tb.dut 1
|
||||
+module prim_alert_sender
|
||||
+module prim_prince
|
||||
+module prim_lfsr
|
||||
end
|
||||
|
|
|
@ -431,7 +431,7 @@ void DpiMemUtil::LoadElfToMemories(bool verbose, const std::string &filepath) {
|
|||
|
||||
const MemArea &mem_area = *mem_areas_[mem_area_it->second];
|
||||
|
||||
for (const auto seg_pr : staged_mem.GetSegs()) {
|
||||
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;
|
||||
|
||||
|
@ -458,6 +458,9 @@ void DpiMemUtil::StageElf(bool verbose, const std::string &path) {
|
|||
|
||||
ElfFile elf(path);
|
||||
|
||||
// Allow subclasses to get at the loaded ELF data if they need it
|
||||
OnElfLoaded(elf.ptr_);
|
||||
|
||||
size_t file_size;
|
||||
const char *file_data = elf_rawfile(elf.ptr_, &file_size);
|
||||
assert(file_data);
|
||||
|
|
13
vendor/lowrisc_ip/dv/verilator/cpp/dpi_memutil.h
vendored
13
vendor/lowrisc_ip/dv/verilator/cpp/dpi_memutil.h
vendored
|
@ -13,6 +13,9 @@
|
|||
#include "mem_area.h"
|
||||
#include "ranged_map.h"
|
||||
|
||||
// Forward declaration for the Elf type from libelf.
|
||||
struct Elf;
|
||||
|
||||
enum MemImageType {
|
||||
kMemImageUnknown = 0,
|
||||
kMemImageElf,
|
||||
|
@ -59,6 +62,8 @@ class StagedMem {
|
|||
*/
|
||||
class DpiMemUtil {
|
||||
public:
|
||||
virtual ~DpiMemUtil() {}
|
||||
|
||||
/**
|
||||
* Register a memory as instantiated by generic ram
|
||||
*
|
||||
|
@ -128,6 +133,14 @@ class DpiMemUtil {
|
|||
*/
|
||||
const StagedMem &GetMemoryData(const std::string &mem_name) const;
|
||||
|
||||
protected:
|
||||
/**
|
||||
* A hook for subclasses to do extra computations with loaded ELF data. This
|
||||
* runs as part of StageElf: after loading the ELF file, but before reading
|
||||
* in the segments.
|
||||
*/
|
||||
virtual void OnElfLoaded(Elf *elf_file) {}
|
||||
|
||||
private:
|
||||
// Memory area registry. The maps give indices pointing into the vectors
|
||||
// (which all have the same number of elements). Note that mem_areas_ does
|
||||
|
|
|
@ -31,7 +31,7 @@ void Ecc32MemArea::LoadVmem(const std::string &path) const {
|
|||
|
||||
void Ecc32MemArea::WriteBuffer(uint8_t buf[SV_MEM_WIDTH_BYTES],
|
||||
const std::vector<uint8_t> &data,
|
||||
size_t start_idx) const {
|
||||
size_t start_idx, uint32_t dst_word) const {
|
||||
int log_width_32 = width_byte_ / 4;
|
||||
int phy_width_bits = 39 * log_width_32;
|
||||
int phy_width_bytes = (phy_width_bits + 7) / 8;
|
||||
|
@ -116,7 +116,8 @@ void Ecc32MemArea::WriteBuffer(uint8_t buf[SV_MEM_WIDTH_BYTES],
|
|||
}
|
||||
|
||||
void Ecc32MemArea::ReadBuffer(std::vector<uint8_t> &data,
|
||||
const uint8_t buf[SV_MEM_WIDTH_BYTES]) const {
|
||||
const uint8_t buf[SV_MEM_WIDTH_BYTES],
|
||||
uint32_t src_word) const {
|
||||
for (int i = 0; i < width_byte_; ++i) {
|
||||
int in_word = i / 4;
|
||||
|
||||
|
|
|
@ -24,13 +24,14 @@ class Ecc32MemArea : public MemArea {
|
|||
|
||||
void LoadVmem(const std::string &path) const override;
|
||||
|
||||
private:
|
||||
protected:
|
||||
void WriteBuffer(uint8_t buf[SV_MEM_WIDTH_BYTES],
|
||||
const std::vector<uint8_t> &data,
|
||||
size_t start_idx) const override;
|
||||
const std::vector<uint8_t> &data, size_t start_idx,
|
||||
uint32_t dst_word) const override;
|
||||
|
||||
void ReadBuffer(std::vector<uint8_t> &data,
|
||||
const uint8_t buf[SV_MEM_WIDTH_BYTES]) const override;
|
||||
const uint8_t buf[SV_MEM_WIDTH_BYTES],
|
||||
uint32_t src_word) const override;
|
||||
};
|
||||
|
||||
#endif // OPENTITAN_HW_DV_VERILATOR_CPP_ECC32_MEM_AREA_H_
|
||||
|
|
49
vendor/lowrisc_ip/dv/verilator/cpp/mem_area.cc
vendored
49
vendor/lowrisc_ip/dv/verilator/cpp/mem_area.cc
vendored
|
@ -27,10 +27,6 @@ MemArea::MemArea(const std::string &scope, uint32_t num_words,
|
|||
|
||||
void MemArea::Write(uint32_t word_offset,
|
||||
const std::vector<uint8_t> &data) const {
|
||||
// If this fails to set scope, it will throw an error which should
|
||||
// be caught at this function's callsite.
|
||||
SVScoped scoped(scope_);
|
||||
|
||||
// This "mini buffer" is used to transfer each write to SystemVerilog.
|
||||
// `simutil_set_mem` takes a fixed SV_MEM_WIDTH_BITS-bit vector but it will
|
||||
// only use the bits required for the RAM width. As an example, for a 32-bit
|
||||
|
@ -47,8 +43,17 @@ void MemArea::Write(uint32_t word_offset,
|
|||
|
||||
for (uint32_t i = 0; i < data_words; ++i) {
|
||||
uint32_t dst_word = word_offset + i;
|
||||
WriteBuffer(minibuf, data, i * width_byte_);
|
||||
if (!simutil_set_mem(dst_word, (svBitVecVal *)minibuf)) {
|
||||
uint32_t phys_addr = ToPhysAddr(dst_word);
|
||||
|
||||
WriteBuffer(minibuf, data, i * width_byte_, dst_word);
|
||||
|
||||
// Both ToPhysAddr and WriteBuffer might set the scope with `SVScoped` so
|
||||
// only construct `SVScoped` once they've both been called so they don't
|
||||
// interact causing incorrect relative path behaviour. If this fails to set
|
||||
// scope, it will throw an error which should be caught at this function's
|
||||
// callsite.
|
||||
SVScoped scoped(scope_);
|
||||
if (!simutil_set_mem(phys_addr, (svBitVecVal *)minibuf)) {
|
||||
std::ostringstream oss;
|
||||
oss << "Could not set memory at byte offset 0x" << std::hex
|
||||
<< dst_word * width_byte_ << ".";
|
||||
|
@ -64,8 +69,6 @@ std::vector<uint8_t> MemArea::Read(uint32_t word_offset,
|
|||
uint32_t num_bytes = width_byte_ * num_words;
|
||||
assert(num_words <= num_bytes);
|
||||
|
||||
SVScoped scoped(scope_);
|
||||
|
||||
// See Write for an explanation for this buffer.
|
||||
uint8_t minibuf[SV_MEM_WIDTH_BYTES];
|
||||
memset(minibuf, 0, sizeof minibuf);
|
||||
|
@ -76,13 +79,24 @@ std::vector<uint8_t> MemArea::Read(uint32_t word_offset,
|
|||
|
||||
for (uint32_t i = 0; i < num_words; ++i) {
|
||||
uint32_t src_word = word_offset + i;
|
||||
if (!simutil_get_mem(src_word, (svBitVecVal *)minibuf)) {
|
||||
std::ostringstream oss;
|
||||
oss << "Could not read memory at byte offset 0x" << std::hex
|
||||
<< src_word * width_byte_ << ".";
|
||||
throw std::runtime_error(oss.str());
|
||||
uint32_t phys_addr = ToPhysAddr(src_word);
|
||||
|
||||
{
|
||||
// Both ToPhysAddr and ReadBuffer might set the scope with `SVScoped`.
|
||||
// Keep the `SVScoped` here confined to an inner scope so they don't
|
||||
// interact causing incorrect relative path behaviour. If this fails to
|
||||
// set scope, it will throw an error which should be caught at this
|
||||
// function's callsite.
|
||||
SVScoped scoped(scope_);
|
||||
if (!simutil_get_mem(phys_addr, (svBitVecVal *)minibuf)) {
|
||||
std::ostringstream oss;
|
||||
oss << "Could not read memory at byte offset 0x" << std::hex
|
||||
<< src_word * width_byte_ << ".";
|
||||
throw std::runtime_error(oss.str());
|
||||
}
|
||||
}
|
||||
ReadBuffer(ret, minibuf);
|
||||
|
||||
ReadBuffer(ret, minibuf, src_word);
|
||||
}
|
||||
|
||||
return ret;
|
||||
|
@ -95,8 +109,8 @@ void MemArea::LoadVmem(const std::string &path) const {
|
|||
}
|
||||
|
||||
void MemArea::WriteBuffer(uint8_t buf[SV_MEM_WIDTH_BYTES],
|
||||
const std::vector<uint8_t> &data,
|
||||
size_t start_idx) const {
|
||||
const std::vector<uint8_t> &data, size_t start_idx,
|
||||
uint32_t dst_word) const {
|
||||
size_t words_left = data.size() - start_idx;
|
||||
size_t to_copy = std::min(words_left, (size_t)width_byte_);
|
||||
if (to_copy < width_byte_) {
|
||||
|
@ -106,7 +120,8 @@ void MemArea::WriteBuffer(uint8_t buf[SV_MEM_WIDTH_BYTES],
|
|||
}
|
||||
|
||||
void MemArea::ReadBuffer(std::vector<uint8_t> &data,
|
||||
const uint8_t buf[SV_MEM_WIDTH_BYTES]) const {
|
||||
const uint8_t buf[SV_MEM_WIDTH_BYTES],
|
||||
uint32_t src_word) const {
|
||||
// Append the first width_byte_ bytes of buf to data.
|
||||
std::copy_n(reinterpret_cast<const char *>(buf), width_byte_,
|
||||
std::back_inserter(data));
|
||||
|
|
26
vendor/lowrisc_ip/dv/verilator/cpp/mem_area.h
vendored
26
vendor/lowrisc_ip/dv/verilator/cpp/mem_area.h
vendored
|
@ -92,10 +92,11 @@ class MemArea {
|
|||
* @param buf Destination buffer
|
||||
* @param data A large buffer that contains the data to be written
|
||||
* @param start_idx An offset into \p data for the start of the memory word
|
||||
* @param dst_word Logical address of the location being written
|
||||
*/
|
||||
virtual void WriteBuffer(uint8_t buf[SV_MEM_WIDTH_BYTES],
|
||||
const std::vector<uint8_t> &data,
|
||||
size_t start_idx) const;
|
||||
const std::vector<uint8_t> &data, size_t start_idx,
|
||||
uint32_t dst_word) const;
|
||||
|
||||
/** Extract the logical memory contents corresponding to the physical
|
||||
* memory contents in \p buf and append them to \p data.
|
||||
|
@ -104,13 +105,26 @@ class MemArea {
|
|||
* across. Other implementations might undo scrambling, remove ECC bits or
|
||||
* similar.
|
||||
*
|
||||
* @param data The target, onto which the extracted memory contents should be
|
||||
* appended.
|
||||
* @param data The target, onto which the extracted memory contents should
|
||||
* be appended.
|
||||
*
|
||||
* @param buf Source buffer (physical memory bits)
|
||||
* @param buf Source buffer (physical memory bits)
|
||||
* @param src_word Logical address of the location being read
|
||||
*/
|
||||
virtual void ReadBuffer(std::vector<uint8_t> &data,
|
||||
const uint8_t buf[SV_MEM_WIDTH_BYTES]) const;
|
||||
const uint8_t buf[SV_MEM_WIDTH_BYTES],
|
||||
uint32_t src_word) const;
|
||||
|
||||
/** Convert a logical address to physical address
|
||||
*
|
||||
* Some memories may have a mapping between the address supplied on the
|
||||
* request and the physical address used to access the memory array (for
|
||||
* example scrambled memories). By default logical and physical address are
|
||||
* the same.
|
||||
*/
|
||||
virtual uint32_t ToPhysAddr(uint32_t logical_addr) const {
|
||||
return logical_addr;
|
||||
}
|
||||
};
|
||||
|
||||
#endif // OPENTITAN_HW_DV_VERILATOR_CPP_MEM_AREA_H_
|
||||
|
|
191
vendor/lowrisc_ip/dv/verilator/cpp/scrambled_ecc32_mem_area.cc
vendored
Normal file
191
vendor/lowrisc_ip/dv/verilator/cpp/scrambled_ecc32_mem_area.cc
vendored
Normal file
|
@ -0,0 +1,191 @@
|
|||
// Copyright lowRISC contributors.
|
||||
// Licensed under the Apache License, Version 2.0, see LICENSE for details.
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
#include "scrambled_ecc32_mem_area.h"
|
||||
|
||||
#include <algorithm>
|
||||
#include <cassert>
|
||||
#include <iostream>
|
||||
#include <sstream>
|
||||
|
||||
#include "scramble_model.h"
|
||||
#include "sv_scoped.h"
|
||||
|
||||
// This is the maximum width of a nonce that's supported by the code in
|
||||
// prim_util_get_scramble_key_nonce.svh
|
||||
static const uint32_t kScrMaxNonceWidth = 320;
|
||||
static const uint32_t kScrMaxNonceWidthByte = (kScrMaxNonceWidth + 7) / 8;
|
||||
|
||||
// Functions to convert from integer address to/from a little-endian vector of
|
||||
// bytes, addr_width is given in bits
|
||||
static std::vector<uint8_t> AddrIntToBytes(uint32_t addr, uint32_t addr_width) {
|
||||
uint32_t addr_width_bytes = (addr_width + 7) / 8;
|
||||
std::vector<uint8_t> addr_bytes(addr_width_bytes);
|
||||
|
||||
for (int i = 0; i < addr_width_bytes; ++i) {
|
||||
addr_bytes[i] = addr & 0xff;
|
||||
addr >>= 8;
|
||||
}
|
||||
|
||||
return addr_bytes;
|
||||
}
|
||||
|
||||
static uint32_t AddrBytesToInt(const std::vector<uint8_t> &addr) {
|
||||
assert(addr.size() <= 4);
|
||||
|
||||
uint32_t addr_out = 0;
|
||||
int cur_shift = 0;
|
||||
|
||||
for (int i = 0; i < addr.size(); ++i) {
|
||||
addr_out |= (addr[i] << cur_shift);
|
||||
cur_shift += 8;
|
||||
}
|
||||
|
||||
return addr_out;
|
||||
}
|
||||
|
||||
// Converts svBitVecVal (bit[m:n] SV type) into a byte vector
|
||||
static std::vector<uint8_t> ByteVecFromSV(svBitVecVal sv_val[],
|
||||
uint32_t bytes) {
|
||||
int shift = 0;
|
||||
std::vector<uint8_t> vec(bytes);
|
||||
for (int i = 0; i < bytes; ++i) {
|
||||
vec[i] = (sv_val[i / 4] >> shift) & 0xff;
|
||||
|
||||
shift += 8;
|
||||
if (shift == 32) {
|
||||
shift = 0;
|
||||
}
|
||||
}
|
||||
|
||||
return vec;
|
||||
}
|
||||
|
||||
// Analogous to the vbits SystemVerilog function from prim_util_pkg.sv. It
|
||||
// calculates the number of bits needed to address size items.
|
||||
static uint32_t vbits(uint32_t size) {
|
||||
assert(size > 0);
|
||||
|
||||
if (size == 1) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
size -= 1;
|
||||
uint32_t width = 0;
|
||||
while (size) {
|
||||
width++;
|
||||
size /= 2;
|
||||
}
|
||||
|
||||
return width;
|
||||
}
|
||||
|
||||
extern "C" {
|
||||
int simutil_get_scramble_key(svBitVecVal *key);
|
||||
int simutil_get_scramble_nonce(svBitVecVal *nonce);
|
||||
}
|
||||
|
||||
std::vector<uint8_t> ScrambledEcc32MemArea::GetScrambleKey() const {
|
||||
SVScoped scoped(scr_scope_);
|
||||
svBitVecVal key_minibuf[((kPrinceWidthByte * 2) + 3) / 4];
|
||||
|
||||
if (!simutil_get_scramble_key(key_minibuf)) {
|
||||
std::ostringstream oss;
|
||||
oss << "Could not read key at scope " << scr_scope_;
|
||||
throw std::runtime_error(oss.str());
|
||||
}
|
||||
|
||||
return ByteVecFromSV(key_minibuf, kPrinceWidthByte * 2);
|
||||
}
|
||||
|
||||
std::vector<uint8_t> ScrambledEcc32MemArea::GetScrambleNonce() const {
|
||||
assert(GetNonceWidthByte() <= kScrMaxNonceWidthByte);
|
||||
|
||||
SVScoped scoped(scr_scope_);
|
||||
svBitVecVal nonce_minibuf[(kScrMaxNonceWidthByte + 3) / 4];
|
||||
|
||||
if (!simutil_get_scramble_nonce((svBitVecVal *)nonce_minibuf)) {
|
||||
std::ostringstream oss;
|
||||
oss << "Could not read nonce at scope " << scr_scope_;
|
||||
throw std::runtime_error(oss.str());
|
||||
}
|
||||
|
||||
return ByteVecFromSV(nonce_minibuf, GetNonceWidthByte());
|
||||
}
|
||||
|
||||
ScrambledEcc32MemArea::ScrambledEcc32MemArea(const std::string &scope,
|
||||
uint32_t size, uint32_t width_32,
|
||||
bool repeat_keystream)
|
||||
: Ecc32MemArea(
|
||||
SVScoped::join_sv_scopes(
|
||||
scope, "u_prim_ram_1p_adv.u_mem.gen_generic.u_impl_generic"),
|
||||
size, width_32),
|
||||
scr_scope_(scope) {
|
||||
addr_width_ = vbits(size);
|
||||
repeat_keystream_ = repeat_keystream;
|
||||
}
|
||||
|
||||
uint32_t ScrambledEcc32MemArea::GetPhysWidth() const {
|
||||
return (GetWidthByte() / 4) * 39;
|
||||
}
|
||||
|
||||
uint32_t ScrambledEcc32MemArea::GetPhysWidthByte() const {
|
||||
return (GetPhysWidth() + 7) / 8;
|
||||
}
|
||||
|
||||
uint32_t ScrambledEcc32MemArea::GetPrinceReplications() const {
|
||||
if (repeat_keystream_) {
|
||||
return 1;
|
||||
} else {
|
||||
return (GetPhysWidthByte() + 7) / 8;
|
||||
}
|
||||
}
|
||||
|
||||
uint32_t ScrambledEcc32MemArea::GetNonceWidth() const {
|
||||
return GetPrinceReplications() * 64;
|
||||
}
|
||||
|
||||
uint32_t ScrambledEcc32MemArea::GetNonceWidthByte() const {
|
||||
return GetPrinceReplications() * 8;
|
||||
}
|
||||
|
||||
void ScrambledEcc32MemArea::WriteBuffer(uint8_t buf[SV_MEM_WIDTH_BYTES],
|
||||
const std::vector<uint8_t> &data,
|
||||
size_t start_idx,
|
||||
uint32_t dst_word) const {
|
||||
// Compute integrity
|
||||
Ecc32MemArea::WriteBuffer(buf, data, start_idx, dst_word);
|
||||
|
||||
std::vector<uint8_t> scramble_buf =
|
||||
std::vector<uint8_t>(buf, buf + GetPhysWidthByte());
|
||||
|
||||
// Scramble data with integrity
|
||||
scramble_buf = scramble_encrypt_data(
|
||||
scramble_buf, GetPhysWidth(), 39, AddrIntToBytes(dst_word, addr_width_),
|
||||
addr_width_, GetScrambleNonce(), GetScrambleKey(), repeat_keystream_);
|
||||
|
||||
// Copy scrambled data to write buffer
|
||||
std::copy(scramble_buf.begin(), scramble_buf.end(), &buf[0]);
|
||||
}
|
||||
|
||||
void ScrambledEcc32MemArea::ReadBuffer(std::vector<uint8_t> &data,
|
||||
const uint8_t buf[SV_MEM_WIDTH_BYTES],
|
||||
uint32_t src_word) const {
|
||||
// Unscramble data from read buffer
|
||||
std::vector<uint8_t> scrambled_data =
|
||||
std::vector<uint8_t>(buf, buf + GetPhysWidthByte());
|
||||
std::vector<uint8_t> unscrambled_data = scramble_decrypt_data(
|
||||
scrambled_data, GetPhysWidth(), 39, AddrIntToBytes(src_word, addr_width_),
|
||||
addr_width_, GetScrambleNonce(), GetScrambleKey(), repeat_keystream_);
|
||||
|
||||
// Strip integrity to give final result
|
||||
Ecc32MemArea::ReadBuffer(data, &unscrambled_data[0], src_word);
|
||||
}
|
||||
|
||||
uint32_t ScrambledEcc32MemArea::ToPhysAddr(uint32_t logical_addr) const {
|
||||
// Scramble logical address to get physical address
|
||||
return AddrBytesToInt(scramble_addr(AddrIntToBytes(logical_addr, addr_width_),
|
||||
addr_width_, GetScrambleNonce(),
|
||||
GetNonceWidth()));
|
||||
}
|
60
vendor/lowrisc_ip/dv/verilator/cpp/scrambled_ecc32_mem_area.h
vendored
Normal file
60
vendor/lowrisc_ip/dv/verilator/cpp/scrambled_ecc32_mem_area.h
vendored
Normal file
|
@ -0,0 +1,60 @@
|
|||
// 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_SCRAMBLED_ECC32_MEM_AREA_H_
|
||||
#define OPENTITAN_HW_DV_VERILATOR_CPP_SCRAMBLED_ECC32_MEM_AREA_H_
|
||||
|
||||
#include <vector>
|
||||
|
||||
#include "ecc32_mem_area.h"
|
||||
|
||||
/**
|
||||
* A memory that implements scrambling over a 32-bit ECC integrity protection
|
||||
* scheme storing 39 = 32 + 7 bits of physical data for each 32 bits of logical
|
||||
* data.
|
||||
*/
|
||||
class ScrambledEcc32MemArea : public Ecc32MemArea {
|
||||
public:
|
||||
/**
|
||||
* Constructor
|
||||
*
|
||||
* Create an ScrambledEcc32MemArea that will connect to a SystemVerilog memory
|
||||
* at scope. It is size words long. Each memory word is 4 * width_32 bytes
|
||||
* wide in the address space and 39 * width_32 bits wide in the physical
|
||||
* memory.
|
||||
*
|
||||
* If the keystream of one single PRINCE instance should be repeated,
|
||||
* set "repeat_keystream" to true. If this is set to false, multiple
|
||||
* PRINCE instances are employed to produce the keystream.
|
||||
*
|
||||
*/
|
||||
ScrambledEcc32MemArea(const std::string &scope, uint32_t size,
|
||||
uint32_t width_32, bool repeat_keystream = true);
|
||||
|
||||
private:
|
||||
void WriteBuffer(uint8_t buf[SV_MEM_WIDTH_BYTES],
|
||||
const std::vector<uint8_t> &data, size_t start_idx,
|
||||
uint32_t dst_word) const override;
|
||||
|
||||
void ReadBuffer(std::vector<uint8_t> &data,
|
||||
const uint8_t buf[SV_MEM_WIDTH_BYTES],
|
||||
uint32_t src_word) const override;
|
||||
|
||||
uint32_t ToPhysAddr(uint32_t logical_addr) const override;
|
||||
|
||||
uint32_t GetPhysWidth() const;
|
||||
uint32_t GetPhysWidthByte() const;
|
||||
uint32_t GetPrinceReplications() const;
|
||||
uint32_t GetNonceWidth() const;
|
||||
uint32_t GetNonceWidthByte() const;
|
||||
|
||||
std::vector<uint8_t> GetScrambleKey() const;
|
||||
std::vector<uint8_t> GetScrambleNonce() const;
|
||||
|
||||
std::string scr_scope_;
|
||||
uint32_t addr_width_;
|
||||
bool repeat_keystream_;
|
||||
};
|
||||
|
||||
#endif // OPENTITAN_HW_DV_VERILATOR_CPP_SCRAMBLED_ECC32_MEM_AREA_H_
|
|
@ -93,3 +93,12 @@ SVScoped::Error::Error(const std::string &scope_name)
|
|||
oss << "No such SystemVerilog scope: `" << scope_name << "'.";
|
||||
msg_ = oss.str();
|
||||
}
|
||||
|
||||
std::string SVScoped::join_sv_scopes(const std::string &a,
|
||||
const std::string &b) {
|
||||
assert(a.size() && b.size());
|
||||
// If a = ".." and b = "foo.bar", we want "..foo.bar". Otherwise, a
|
||||
// = "..foo" and b = "bar.baz", so we want "..foo.bar.baz"
|
||||
// (inserting a "." between the two)
|
||||
return (a.back() == '.') ? a + b : a + "." + b;
|
||||
}
|
||||
|
|
|
@ -5,6 +5,7 @@
|
|||
#ifndef OPENTITAN_HW_DV_VERILATOR_CPP_SV_SCOPED_H_
|
||||
#define OPENTITAN_HW_DV_VERILATOR_CPP_SV_SCOPED_H_
|
||||
|
||||
#include <cassert>
|
||||
#include <stdexcept>
|
||||
#include <string>
|
||||
#include <svdpi.h>
|
||||
|
@ -44,6 +45,9 @@ class SVScoped {
|
|||
std::string msg_;
|
||||
};
|
||||
|
||||
// helper function to join two, possibly relative, scopes correctly.
|
||||
static std::string join_sv_scopes(const std::string &a, const std::string &b);
|
||||
|
||||
private:
|
||||
svScope prev_scope_;
|
||||
};
|
||||
|
|
21
vendor/lowrisc_ip/dv/verilator/memutil_dpi_scrambled.core
vendored
Normal file
21
vendor/lowrisc_ip/dv/verilator/memutil_dpi_scrambled.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_scrambled"
|
||||
description: "DPI memory utilities for scrambled memories"
|
||||
filesets:
|
||||
files_cpp:
|
||||
depend:
|
||||
- lowrisc:dv_verilator:memutil_dpi
|
||||
- lowrisc:dv:scramble_model
|
||||
files:
|
||||
- cpp/scrambled_ecc32_mem_area.cc
|
||||
- cpp/scrambled_ecc32_mem_area.h: { is_include_file: true }
|
||||
file_type: cppSource
|
||||
|
||||
targets:
|
||||
default:
|
||||
filesets:
|
||||
- files_cpp
|
44
vendor/lowrisc_ip/dv/verilator/memutil_dpi_scrambled_opts.hjson
vendored
Normal file
44
vendor/lowrisc_ip/dv/verilator/memutil_dpi_scrambled_opts.hjson
vendored
Normal file
|
@ -0,0 +1,44 @@
|
|||
// Copyright lowRISC contributors.
|
||||
// Licensed under the Apache License, Version 2.0, see LICENSE for details.
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
{
|
||||
// Additional build-time options needed to compile C++ sources in
|
||||
// simulators such as VCS and Xcelium for anything that uses
|
||||
// memutil_dpi_scrambled.
|
||||
memutil_dpi_core: "lowrisc:dv_verilator:memutil_dpi:0"
|
||||
memutil_dpi_src_dir: "{eval_cmd} echo \"{memutil_dpi_core}\" | tr ':' '_'"
|
||||
|
||||
memutil_dpi_scrambled_core: "lowrisc:dv_verilator:memutil_dpi_scrambled:0"
|
||||
memutil_dpi_scrambled_src_dir: "{eval_cmd} echo \"{memutil_dpi_scrambled_core}\" | tr ':' '_'"
|
||||
|
||||
secded_enc_core: "lowrisc:dv:secded_enc:0"
|
||||
secded_enc_src_dir: "{eval_cmd} echo \"{secded_enc_core}\" | tr ':' '_'"
|
||||
|
||||
scramble_model_core: "lowrisc:dv:scramble_model:0"
|
||||
scramble_model_dir: "{eval_cmd} echo \"{scramble_model_core}\" | tr ':' '_'"
|
||||
|
||||
prince_ref_core: "lowrisc:dv:crypto_prince_ref:0.1"
|
||||
prince_ref_src_dir: "{eval_cmd} echo \"{prince_ref_core}\" | tr ':' '_'"
|
||||
|
||||
|
||||
build_modes: [
|
||||
{
|
||||
name: vcs_memutil_dpi_scrambled_build_opts
|
||||
build_opts: ["-CFLAGS -I{build_dir}/src/{memutil_dpi_src_dir}/cpp",
|
||||
"-CFLAGS -I{build_dir}/src/{memutil_dpi_scrambled_src_dir}/cpp",
|
||||
"-CFLAGS -I{build_dir}/src/{secded_enc_src_dir}",
|
||||
"-CFLAGS -I{build_dir}/src/{scramble_model_dir}",
|
||||
"-CFLAGS -I{build_dir}/src/{prince_ref_src_dir}",
|
||||
"-lelf"]
|
||||
}
|
||||
|
||||
{
|
||||
name: xcelium_memutil_dpi_scrambled_build_opts
|
||||
build_opts: ["-I{build_dir}/src/{memutil_dpi_src_dir}/cpp",
|
||||
"-I{build_dir}/src/{memutil_dpi_scrambled_src_dir}/cpp",
|
||||
"-I{build_dir}/src/{prince_ref_src_dir}",
|
||||
"-I{build_dir}/src/{scramble_model_dir}",
|
||||
"-lelf"]
|
||||
}
|
||||
]
|
||||
}
|
|
@ -60,6 +60,52 @@ std::pair<int, bool> VerilatorSimCtrl::Exec(int argc, char **argv) {
|
|||
return std::make_pair(retcode, true);
|
||||
}
|
||||
|
||||
static bool read_ul_arg(unsigned long *arg_val, const char *arg_name,
|
||||
const char *arg_text) {
|
||||
assert(arg_val && arg_name && arg_text);
|
||||
|
||||
bool bad_fmt = false;
|
||||
bool out_of_range = false;
|
||||
|
||||
// We have a stricter input format that strtoul: no leading space and no
|
||||
// leading plus or minus signs (strtoul has magic behaviour if the input
|
||||
// starts with a minus sign, but we don't want that). We're using auto base
|
||||
// detection, but a valid number will always start with 0-9 (since hex
|
||||
// constants start with "0x")
|
||||
if (!(('0' <= arg_text[0]) && (arg_text[0] <= '9'))) {
|
||||
bad_fmt = true;
|
||||
} else {
|
||||
char *txt_end;
|
||||
*arg_val = strtoul(arg_text, &txt_end, 0);
|
||||
|
||||
// If txt_end doesn't point at a \0 then we didn't read the entire
|
||||
// argument.
|
||||
if (*txt_end) {
|
||||
bad_fmt = true;
|
||||
} else {
|
||||
// If the value was too big to fit in an unsigned long, strtoul sets
|
||||
// errno to ERANGE.
|
||||
if (errno != 0) {
|
||||
assert(errno == ERANGE);
|
||||
out_of_range = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (bad_fmt) {
|
||||
std::cerr << "ERROR: Bad format for " << arg_name << " argument: `"
|
||||
<< arg_text << "' is not an unsigned integer.\n";
|
||||
return false;
|
||||
}
|
||||
if (out_of_range) {
|
||||
std::cerr << "ERROR: Bad format for " << arg_name << " argument: `"
|
||||
<< arg_text << "' is too big.\n";
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool VerilatorSimCtrl::ParseCommandArgs(int argc, char **argv, bool &exit_app) {
|
||||
const struct option long_options[] = {
|
||||
{"term-after-cycles", required_argument, nullptr, 'c'},
|
||||
|
@ -89,7 +135,10 @@ bool VerilatorSimCtrl::ParseCommandArgs(int argc, char **argv, bool &exit_app) {
|
|||
TraceOn();
|
||||
break;
|
||||
case 'c':
|
||||
term_after_cycles_ = atoi(optarg);
|
||||
if (!read_ul_arg(&term_after_cycles_, "term-after-cycles", optarg)) {
|
||||
exit_app = true;
|
||||
return false;
|
||||
}
|
||||
break;
|
||||
case 'h':
|
||||
PrintHelp();
|
||||
|
@ -159,6 +208,10 @@ void VerilatorSimCtrl::SetResetDuration(unsigned int cycles) {
|
|||
reset_duration_cycles_ = cycles;
|
||||
}
|
||||
|
||||
void VerilatorSimCtrl::SetTimeout(unsigned int cycles) {
|
||||
term_after_cycles_ = cycles;
|
||||
}
|
||||
|
||||
void VerilatorSimCtrl::RequestStop(bool simulation_success) {
|
||||
request_stop_ = true;
|
||||
simulation_success_ &= simulation_success;
|
||||
|
@ -217,7 +270,7 @@ void VerilatorSimCtrl::PrintHelp() const {
|
|||
" Write a trace file from the start\n\n";
|
||||
}
|
||||
std::cout << "-c|--term-after-cycles=N\n"
|
||||
" Terminate simulation after N cycles\n\n"
|
||||
" Terminate simulation after N cycles. 0 means no timeout.\n\n"
|
||||
"-h|--help\n"
|
||||
" Show help\n\n"
|
||||
"All arguments are passed to the design and can be used "
|
||||
|
|
|
@ -97,6 +97,15 @@ class VerilatorSimCtrl {
|
|||
*/
|
||||
void SetResetDuration(unsigned int cycles);
|
||||
|
||||
/**
|
||||
* Set a timeout in clock cycles.
|
||||
*
|
||||
* This can be overridden by the user (in either direction) with the
|
||||
* --term-after-cycles command-line argument. Setting to zero means
|
||||
* no timeout, which is the default behaviour.
|
||||
*/
|
||||
void SetTimeout(unsigned int cycles);
|
||||
|
||||
/**
|
||||
* Request the simulation to stop
|
||||
*/
|
||||
|
@ -129,7 +138,7 @@ class VerilatorSimCtrl {
|
|||
std::chrono::steady_clock::time_point time_begin_;
|
||||
std::chrono::steady_clock::time_point time_end_;
|
||||
VerilatedTracer tracer_;
|
||||
int term_after_cycles_;
|
||||
unsigned long term_after_cycles_;
|
||||
std::vector<SimCtrlExtension *> extension_array_;
|
||||
|
||||
/**
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue