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:
Philipp Wagner 2021-07-20 10:59:36 +01:00 committed by Philipp Wagner
parent 270cd91b38
commit d003d479ff
122 changed files with 3416 additions and 663 deletions

View file

@ -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;

View file

@ -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;)

View file

@ -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

View file

@ -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);

View file

@ -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

View file

@ -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);

View file

@ -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);

View file

@ -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[^:].*$",

View file

@ -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"]
}
]
}

View file

@ -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
}
]
}

View file

@ -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

View file

@ -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

View file

@ -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);

View file

@ -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

View file

@ -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;

View file

@ -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_

View file

@ -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));

View file

@ -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_

View 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()));
}

View 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_

View file

@ -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;
}

View file

@ -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_;
};

View 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

View 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"]
}
]
}

View file

@ -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 "

View file

@ -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_;
/**