Update code from upstream repository
https://github.com/lowRISC/opentitan to revision
7c4f8b3fde4bb625ac3330ff52d3f66507190fe5

* Revert "[dv] Allow using memutil_dpi_scrambled even without
  prim_ram_1p_scr" (Rupert Swarbrick)
* [dv] Fix some signed/unsigned comparison warnings (Rupert Swarbrick)
* [dv] Make an implicit up-conversion explicit (Rupert Swarbrick)
* [dv] Remove an unused array variable in prince_ref.h (Rupert
  Swarbrick)
* [prim/security] Improve the code for prim_sparse_fsm security check
  (Cindy Chen)
* [dv] Apply VCS option `-xprop=mmsopt` only when wave dump is off
  (Weicai Yang)
* [all] variety of minor lint fixes (Timothy Chen)
* [dv] Add options to improve VCS runtime (Weicai Yang)
* [rv_dm] CSR test fixes (Srikrishna Iyer)
* [dvsim] Fix pass/fail status for synthesis regression (Michael
  Schaffner)
* [prim] Minor lint fixes for unused clocks / resets (Timothy Chen)
* [dv] Flag illegal ENUMASSIGN warnings as errors (Michael Schaffner)
* [flash_ctrl] Correct erase suspend interface behavior (Timothy Chen)
* [rstmgr] Address several d2s review items (Timothy Chen)
* [fpv/sec] Add some workaround logic for $cast keyword (Cindy Chen)
* [dv] CSR seq lib - support for adapter-less RAL (Srikrishna Iyer)
* [dv] Prepare codebase for UVM REG changes (Srikrishna Iyer)
* [dv] Print computed CSR stuff in RAL (Srikrishna Iyer)
* [dv] Allow CSR tests to run on custom RALs (Srikrishna Iyer)
* [fpv/rom_ctrl] Check connectivity for alerts in rom_ctrl (Cindy
  Chen)
* [prim] Add prim_and2 primitive (Pirmin Vogel)
* [prim_dom_and_2share] Remove EnNegedge parameter (Pirmin Vogel)
* [prim_dom_and_2share] Use prim_xor2 and prim_flop_en primitives
  (Pirmin Vogel)
* [prim_dom_and_2share] Switch to single randomness input (Pirmin
  Vogel)
* [util/dvsim] Fix confusing error message (Guillermo Maturana)
* [dvsim] Minor changes to SynCfg results reporting (Michael
  Schaffner)
* [fpv] V2S formal support (Cindy Chen)
* [tools/xcelium] updated common coverage exclusions to exclude single
  bit correctly (Rasmus Madsen)
* [dv] Clean up enable_reg_testplan (Weicai Yang)
* [top] Hook-up flash/otp control and observation bus to ast (Timothy
  Chen)
* [lint] Increase the unroll count (Eunchan Kim)
* [entropy_src] Document & Implement THRESHOLD_SCOPE (Martin Lueker-
  Boden)
* [AST] USB Observe, Clocks & POR_NI logic update (Jacob Levy)
* [prim] Add new assertion macro for generating static lint errors
  (Pirmin Vogel)
* [dv] csr_seq_lib fixes (Srikrishna Iyer)
* [dv] dv_base_reg_block - Add special knobs (Srikrishna Iyer)
* [dv] dv_base_mem - add special knobs (Srikrishna Iyer)
* [prim] Move sec_cm assertion to an include file in prim_assert
  (Weicai Yang)
* [flash_ctrl] Fixes for erase suspend (Timothy Chen)
* [dv] exclude d_user.rsp_intg[6] for xcelium (Weicai Yang)
* [prim_flop_en] Dependency fix (Michael Schaffner)
* [dv] add mubi coverage for CSR and update reggen (Weicai Yang)
* [prim] Add option for secure buffers in prim_mubi (Timothy Chen)
* [prim] Add option for hand instantiated buffers for prim_flop_en
  (Timothy Chen)
* [dv/shadow_reg] Move shadow_reg to V2S (Cindy Chen)
* [prim_count] Updated comments to reflect all changes in
  lowRISC/opentitan#10378 (Michael Tempelmeier)
* [dv] Teach ECC32 flavours of mem_area to write with integrity bits
  (Rupert Swarbrick)
* [dv/shadow_reg] update milestone for shadow reg tests (Cindy Chen)
* [checklists] Update V2S checklists (Srikrishna Iyer)
* [tools/xcelium] updated xcelium flow to vcs for coverage test
  grading (Rasmus Madsen)
* [prim] Add stub flops to remove lint warnings (Timothy Chen)
* [dv] Add automatic covergroup for all regwen CSRs (Weicai Yang)
* [dvsim] Add support for tags in testplan (Srikrishna Iyer)
* [dv] Enable xcelium to include X for toggle coverage (Weicai Yang)
* [dv] Clean up mem_bkdr_util__sram (Weicai Yang)
* [util, testplan] Allow relative testplan imports (Srikrishna Iyer)
* [prim] Add phase output to shadow register primitive (Pirmin Vogel)
* [dv] Add assertion to check double_lfsr err triggers an alert
  (Weicai Yang)
* [dv] Fix foundary failure (Weicai Yang)
* [prim] update prim_count comment (Timothy Chen)
* [prim_flop_2sync] Make the prim a standard non-generated prim
  (Michael Schaffner)
* [dv/prim_max_tree] Fix xcelium compile error (Cindy Chen)
* [dv] Fixes to enable foundry database pwrmgr_smoketest (Timothy
  Chen)
* [dv] Add countermeasure verification for double_lfsr (Weicai Yang)
* [dv] Update countermeasure verification (Weicai Yang)
* [doc] Update V2S items (Weicai Yang)
* [prim_max_tree] Remove dedicated FPV TB since all SVAs are embedded
  (Michael Schaffner)
* [prim_max_tree/fpv] Add a simple formal testbench (Michael
  Schaffner)
* [prim_max_tree] Create a primitive that calculates maxima (Michael
  Schaffner)
* [dv] CSR / RAL model fixes (Srikrishna Iyer)
* [uvmdvgen] bug fix (Srikrishna Iyer)
* [dv] Fix some Xcelium warnings (Srikrishna Iyer)
* [dv] Disable some benign warnings (Srikrishna Iyer)
* [prim_mubi*_sender] Add option to omit sender flops (Michael
  Schaffner)
* [dv, mem_bkdr_util] Fix ECC-computed backdoor WRs (Srikrishna Iyer)
* [keymgr] sparsify the data control fsm (Timothy Chen)
* [prim_lc_sender] Add AsyncOn parameter (Michael Schaffner)
* [prim] Update behavior of prim_count (Timothy Chen)
* [flash_ctrl] Minor fixes to flash foundry failure (Timothy Chen)
* [sw,tests,pwrmgr] Improve synchronization (Guillermo Maturana)
* [sw,tests] SRAM execution test DV integration (Dave Williams)
* [dv] Update common_cov_excl to exclude d_user.rsp_intg[6] (Weicai
  Yang)
* [otbn, dv] Added otbn_passthru_mem_tl_intg_err testcase (Prajwala
  Puttappa)
* [rom_ctrl, dv] Fixes regression failures in
  rom_ctrl_passthru_mem_tl_intg_err (Prajwala Puttappa)
* [dv/chip] Add jtag_csr_rw seq (Cindy Chen)
* [chip dv] Remove xcelium build opt (Srikrishna Iyer)
* [doc] Reorder D2S checklist items (Michael Schaffner)
* [reggen] Add support for validation of RTL CM annotation (Michael
  Schaffner)
* [all] various simple lint fixes (Timothy Chen)
* [mem_bkdr,dv] Add missing type to otp_write_lc_partition_cnt (Rupert
  Swarbrick)
* [dv/csr_utils_pkg] Clone ral map with top-level submaps (Cindy Chen)
* [clkmgr] various spec and parameter updates (Timothy Chen)
* [dv] Add ASSERT_NET to check net value (Weicai Yang)
* [dv] revert lowRISC/opentitan#9050 and lowRISC/opentitan#9934
  (Weicai Yang)
* [primgen] Update AscentLint waiver in generated abstract prim
  wrappers (Pirmin Vogel)
* [prim_generic] Fix lint errors (Pirmin Vogel)
* [prim_count] Fix lint warnings (Pirmin Vogel)
* [prim_alert_receiver] Fix ping during init sequence bug (Michael
  Schaffner)
* [rom_ctrl, dv] Added passthru mem test (Prajwala Puttappa)
* [prim_assert,dv] Use if condition in assert_init (Srikrishna Iyer)
* [prim_filter_cnt] Make threshold runtime programmable (Michael
  Schaffner)
* [prim_filter*] Optionally instantiate a 2-stage sync in prim_filter*
  (Michael Schaffner)
* [dv] intg_err test cleanup and change passthru_mem_tl_intg_err to
  V2S (Weicai Yang)
* [prim_xilinx] Replace KEEP with DONT_TOUCH attributes (Pirmin Vogel)
* [sram/dv] Enable the integrity test for passthru (Weicai Yang)
* [dv] Add integrity test for passthru mem (Weicai Yang)
* [dv/tools] Fix alert ping exclusion (Cindy Chen)
* [dv/mem_bkdr_util] added backdoor write of LC counter into LC
  partition in OTP (Dror Kabely)
* [prim_pad_wrapper] Add dual pad wrapper for USB (Michael Schaffner)
* [prim_clock_mux] Model generic mux with boolean ops (Michael
  Schaffner)
* [prim_buf] Ensure generic primitives contain a logic cell (Michael
  Schaffner)
* [prim_count] improved documentation and style (Michael Tempelmeier)
* Revert "[dv] Replace fileset_partner flag with fileset_ast flag"
  (Michael Schaffner)
* [dv] Replace fileset_partner flag with fileset_ast flag (Sharon
  Topaz)
* [dv] Pass data_intg_passthru to dv_base_mem (Weicai Yang)
* [dv/prim_alert] Add V3 item to testplan (Cindy Chen)
* [dv/prim_count] Add an assertion to check max count stable (Cindy
  Chen)
* [dv] Fix typo in uvmdvgen comment (Rupert Swarbrick)
* [mem_bkdr_util] Use inverted integrity in rom_encrypt_write32_integ
  (Rupert Swarbrick)
* [doc/checklist] Template fix (Cindy Chen)
* [mem_bkdr_util,rom_ctrl] Fix how we call encrypt_sram_data (Rupert
  Swarbrick)
* [rom/ram/xbar/otbn] Switch end-end bus integrity to inverted ECC
  codes (Michael Schaffner)
* [dv/prim_alert_tb] Modify the seq to ensure alert always sends
  (Cindy Chen)
* [dv,xcelium] Fix lowRISC/opentitan#4230: Xcelium compile error.
  (Timothy Trippel)
* [dv/prim_alert] Add randomization in ping request sequence (Cindy
  Chen)
* [prim_alert_receiver] Only check for ping requests after
  initialization (Michael Schaffner)
* [doc] Update D2S checklist template and description (Michael
  Schaffner)
* [prim_esc_receiver] Switch to standardized prim_count (Michael
  Schaffner)
* [prim_count] Add option to disable the connection SVA (Michael
  Schaffner)
* [otbn, rtl] Lint fixes (Greg Chadwick)
* [sram/dv] Better support partial write in scb (Weicai Yang)
* [dv/mem_bkdr_util] Fix ECC width error in OTP foundary test (Cindy
  Chen)
* [secded/lint] Fix lint errors (Michael Schaffner)
* [dv/prim_esc] Add more stimulus to reach coverage goal (Cindy Chen)
* [alert_handler] Switch to sparse fsm primitive (Michael Schaffner)
* [prim_sparse_fsm_flop] Add a parameter to disable SVA (Michael
  Schaffner)

Signed-off-by: Prajwala Puttappa <prajwalaputtappa@lowrisc.org>
This commit is contained in:
Prajwala Puttappa 2022-03-08 16:02:51 +00:00 committed by prajwalaputtappa
parent e6eb4fb11d
commit 15da12dfd6
160 changed files with 3018 additions and 1412 deletions

View file

@ -211,7 +211,7 @@ interface clk_rst_if #(
// Scales the clock frequency up and down on every edge.
function automatic void apply_freq_scaling();
real scaling;
real mult = $urandom_range(0, clk_freq_scale_up) ? 1.0 : -1.0;
real mult = $urandom_range(0, int'(clk_freq_scale_up)) ? 1.0 : -1.0;
if ($urandom_range(1, 100) <= clk_freq_scaling_chance_pc) begin
scaling = 1.0 + mult * real'($urandom_range(0, clk_freq_scaling_pc)) / 100;

View file

@ -23,9 +23,9 @@
class csr_base_seq extends uvm_reg_sequence #(uvm_sequence #(uvm_reg_item));
`uvm_object_utils(csr_base_seq)
uvm_reg_block models[string];
uvm_reg all_csrs[$];
uvm_reg test_csrs[$];
dv_base_reg_block models[$];
uvm_reg all_csrs[$];
uvm_reg test_csrs[$];
// By default, assume external checker (example, scoreboard) is turned off. If that is the case,
// then writes are followed by call to predict function to update the mirrored value. Reads are
@ -81,8 +81,8 @@ class csr_base_seq extends uvm_reg_sequence #(uvm_sequence #(uvm_reg_item));
end
if (!(test_csr_chunk inside {[1:num_csr_chunks]})) begin
`uvm_fatal(`gtn, $sformatf({{"invalid opt +test_csr_chunk=%0d, +num_csr_chunks=%0d "},
{"(1 <= test_csr_chunk <= num_csr_chunks)"}},
`uvm_fatal(`gtn, $sformatf({{"Invalid opt +test_csr_chunk=%0d, +num_csr_chunks=%0d "},
{"(1 <= test_csr_chunk <= num_csr_chunks)."}},
test_csr_chunk, num_csr_chunks))
end
chunk_size = (num_test_csrs != 0) ? num_test_csrs : (all_csrs.size / num_csr_chunks + 1);
@ -92,11 +92,11 @@ class csr_base_seq extends uvm_reg_sequence #(uvm_sequence #(uvm_reg_item));
end_idx = all_csrs.size() - 1;
test_csrs = all_csrs[start_idx:end_idx];
`uvm_info(`gtn, $sformatf("testing %0d csrs [%0d - %0d] in all supplied models",
test_csrs.size(), start_idx, end_idx), UVM_MEDIUM)
`uvm_info(`gtn, $sformatf("Testing %0d csrs [%0d - %0d] in all supplied models.",
test_csrs.size(), start_idx, end_idx), UVM_MEDIUM)
foreach (test_csrs[i]) begin
`uvm_info(`gtn, $sformatf("test_csrs list: %0s, reset: 0x%0x", test_csrs[i].get_full_name(),
test_csrs[i].get_mirrored_value()), UVM_HIGH)
`uvm_info(`gtn, $sformatf("Testing CSR %0s, reset: 0x%0x.", test_csrs[i].get_full_name(),
test_csrs[i].get_mirrored_value()), UVM_HIGH)
end
test_csrs.shuffle();
endfunction
@ -142,15 +142,18 @@ class csr_hw_reset_seq extends csr_base_seq;
test_csrs[i].get_full_name()), UVM_MEDIUM)
compare_mask = get_mask_excl_fields(test_csrs[i], CsrExclInitCheck, CsrHwResetTest);
// Read twice, one from backdoor, the other from frontdoor.
// Reading from backdoor can ensure that we deposit value into the storage rather than just
// a net. If we mistakenly deposit to a net, reset can't clear it and this check will fail.
csr_rd_check(.ptr (test_csrs[i]),
.backdoor (1),
.compare (!external_checker),
.compare_vs_ral(1'b1),
.compare_mask (compare_mask));
// same read but using frontdoor
// Read CSR twice, one from backdoor (if path available), the other from frontdoor.
if (test_csrs[i].has_hdl_path()) begin
// Reading from backdoor can ensure that we deposit value into the storage rather than just
// a net. If we mistakenly deposit to a net, reset can't clear it and this check will fail.
csr_rd_check(.ptr (test_csrs[i]),
.backdoor (1),
.compare (!external_checker),
.compare_vs_ral(1'b1),
.compare_mask (compare_mask));
end
// Read and check value via frontdoor.
csr_rd_check(.ptr (test_csrs[i]),
.backdoor (0),
.blocking (0),
@ -178,18 +181,21 @@ class csr_write_seq extends csr_base_seq;
uvm_reg_data_t wdata;
// check all hdl paths are valid
// TODO: Move this check to env::end_of_elaboration_phase instead. Regular tests may also choose
// to access CSRs via backdoor.
if (!test_backdoor_path_done) begin
bkdr_reg_path_e path_kind;
uvm_reg_mem_hdl_paths_seq hdl_check_seq;
hdl_check_seq = uvm_reg_mem_hdl_paths_seq::type_id::create("hdl_check_seq");
// add all the supported path types
do begin
hdl_check_seq.abstractions.push_back(path_kind.name);
path_kind = path_kind.next;
end while (path_kind != path_kind.first);
foreach (models[i]) begin
bkdr_reg_path_e path_kind;
uvm_reg_mem_hdl_paths_seq hdl_check_seq;
hdl_check_seq = uvm_reg_mem_hdl_paths_seq::type_id::create("hdl_check_seq");
do begin
// Test the HDL paths for correctness if they exist.
if (models[i].has_hdl_path(path_kind.name)) begin
hdl_check_seq.abstractions.push_back(path_kind.name);
end
path_kind = path_kind.next;
end while (path_kind != path_kind.first);
hdl_check_seq.model = models[i];
hdl_check_seq.start(null);
end
@ -213,9 +219,8 @@ class csr_write_seq extends csr_base_seq;
wdata = get_csr_wdata_with_write_excl(test_csrs[i], wdata, CsrHwResetTest);
`downcast(dv_csr, test_csrs[i])
if (en_rand_backdoor_write && !dv_csr.get_is_ext_reg()) begin
`DV_CHECK_STD_RANDOMIZE_WITH_FATAL(backdoor,
backdoor dist {0 :/ 7, 1 :/ 3};)
if (en_rand_backdoor_write && test_csrs[i].has_hdl_path() && !dv_csr.get_is_ext_reg()) begin
`DV_CHECK_STD_RANDOMIZE_WITH_FATAL(backdoor, backdoor dist {0 :/ 7, 1 :/ 3};)
end
if (backdoor) begin
@ -258,9 +263,20 @@ class csr_rw_seq extends csr_base_seq;
virtual task body();
foreach (test_csrs[i]) begin
uvm_reg_data_t wdata;
uvm_reg_data_t compare_mask;
uvm_reg_field test_fields[$];
dv_base_reg test_dv_csr;
bit supports_byte_enable;
bit do_field_rd_check;
// The test_csrs list may be jumbled from different RAL models. Hence, we find at runtime, if
// the interface supports byte_accesses.
begin
uvm_reg_map map = test_csrs[i].get_default_map();
uvm_reg_adapter adapter = map.get_adapter();
if (adapter != null) begin
supports_byte_enable = adapter.supports_byte_enable;
end
end
// check if parent block or register is excluded from write
if (is_excl(test_csrs[i], CsrExclWrite, CsrRwTest)) begin
@ -290,10 +306,13 @@ class csr_rw_seq extends csr_base_seq;
`downcast(test_dv_csr, test_csrs[i])
if (test_dv_csr.get_is_shadowed) wait_no_outstanding_access();
`DV_CHECK_STD_RANDOMIZE_WITH_FATAL(do_field_rd_check,
!supports_byte_enable -> !do_field_rd_check;)
do_check_csr_or_field_rd(.csr(test_csrs[i]),
.blocking(0),
.compare(!external_checker),
.compare_vs_ral(1),
.do_csr_field_rd_check(do_field_rd_check),
.csr_excl_type(CsrExclWriteCheck),
.csr_test_type(CsrRwTest));
@ -498,6 +517,7 @@ class csr_mem_walk_seq extends csr_base_seq;
virtual task body();
mem_walk_seq = uvm_mem_walk_seq::type_id::create("mem_walk_seq");
foreach (models[i]) begin
`uvm_info(`gfn, $sformatf("Testing model %0s", models[i].get_full_name()), UVM_LOW)
mem_walk_seq.model = models[i];
mem_walk_seq.start(null);
end

View file

@ -736,13 +736,14 @@ package csr_utils_pkg;
// Clone the submaps by calling this function recursively
map.get_submaps(submaps);
if (submaps.size()) `dv_warning("clone_reg_map: submaps are not yet tested", "DV_UTILS_PKG")
while (submaps.size()) begin
uvm_reg_map submap, submap_clone;
submap = submaps.pop_front();
submap_clone = clone_reg_map(.name(name), .map(submap), .base_addr(submap.get_base_addr()),
.n_bytes(submap.get_n_bytes()), .endian(endian));
clone.add_submap(.child_map(submap_clone), .offset(clone.get_submap_offset(submap)));
// Use `UVM_NO_HIER` argument to get the n_bytes from the submap instead of the system
// level value.
.n_bytes(submap.get_n_bytes(UVM_NO_HIER)), .endian(endian));
clone.add_submap(.child_map(submap_clone), .offset(map.get_submap_offset(submap)));
end
// Clone the registers

View file

@ -0,0 +1,50 @@
// Copyright lowRISC contributors.
// Licensed under the Apache License, Version 2.0, see LICENSE for details.
// SPDX-License-Identifier: Apache-2.0
//
// coverage object of lockable CSR field and its regwen
class dv_base_lockable_field_cov extends uvm_object;
// mirrored value of the lockable field, which is used to know if new value is written.
uvm_reg_data_t field_mirrored_val;
// when new written value is different from mirrored value, this will be set.
bit is_new_val_written;
// latched the regwen value when a new value is written to this lockable CSR.
bit regwen_val_when_new_value_written;
`uvm_object_utils(dv_base_lockable_field_cov)
// Cover these 2 cases
// 1. When regwen = 1, write a non-mirrored value to lockable CSR and read it back
// 2. When regwen = 0, write a non-mirrored value to lockable CSR and read it back
covergroup regwen_val_when_new_value_written_cg(string name) with function sample();
option.per_instance = 1;
option.name = name;
cp_regwen: coverpoint regwen_val_when_new_value_written;
endgroup : regwen_val_when_new_value_written_cg
// use reg_field name as this name
function new(string name = "");
regwen_val_when_new_value_written_cg = new($sformatf("lockable_field_cov_of_%s", name));
endfunction : new
virtual function void reset(uvm_reg_data_t reset_val);
field_mirrored_val = reset_val;
is_new_val_written = 0;
endfunction
// invoke at dv_base_reg_field::post_write
virtual function void post_write(uvm_reg_data_t new_val, bit regwen_val);
if (field_mirrored_val != new_val) begin
is_new_val_written = 1;
regwen_val_when_new_value_written = regwen_val;
field_mirrored_val = new_val;
end
endfunction
// invoke at dv_base_reg_field::post_read
virtual function void post_read();
if (is_new_val_written) regwen_val_when_new_value_written_cg.sample();
endfunction
endclass : dv_base_lockable_field_cov

View file

@ -11,6 +11,15 @@ class dv_base_mem extends uvm_mem;
// if mem doesn't support partial write, doing that will result d_error = 1
local bit mem_partial_write_support;
// Modifies the expectation of writes / reads to RO / WO mem. By default it should be an error
// response, but some implementations may choose to just ignore it.
local bit write_to_ro_mem_ok;
local bit read_to_wo_mem_ok;
// if data integrity is passthru, mem stores integrity along with data but it won't check the
// data integrity
local bit data_intg_passthru;
function new(string name,
longint unsigned size,
int unsigned n_bits,
@ -28,6 +37,30 @@ class dv_base_mem extends uvm_mem;
return mem_partial_write_support;
endfunction : get_mem_partial_write_support
function void set_data_intg_passthru(bit enable);
data_intg_passthru = enable;
endfunction : set_data_intg_passthru
function bit get_data_intg_passthru();
return data_intg_passthru;
endfunction : get_data_intg_passthru
function void set_write_to_ro_mem_ok(bit ok);
write_to_ro_mem_ok = ok;
endfunction
function bit get_write_to_ro_mem_ok();
return write_to_ro_mem_ok;
endfunction
function void set_read_to_wo_mem_ok(bit ok);
read_to_wo_mem_ok = ok;
endfunction
function bit get_read_to_wo_mem_ok();
return read_to_wo_mem_ok;
endfunction
// rewrite this function to support "WO" access type for mem
function void configure(uvm_reg_block parent,
string hdl_path="");

View file

@ -0,0 +1,88 @@
// Copyright lowRISC contributors.
// Licensed under the Apache License, Version 2.0, see LICENSE for details.
// SPDX-License-Identifier: Apache-2.0
//
// coverage object for a fixed width mubi
class mubi_cov #(parameter int Width = 4,
parameter int ValueTrue = prim_mubi_pkg::MuBi4True,
parameter int ValueFalse = prim_mubi_pkg::MuBi4False
) extends uvm_object;
`uvm_object_param_utils(mubi_cov #(Width, ValueTrue, ValueFalse))
// Collect true, false and at least N other values (N = Width)
covergroup mubi_cg(string name) with function sample(bit [Width-1:0] value);
option.per_instance = 1;
option.name = name;
cp_value: coverpoint value {
bins true = {ValueTrue};
bins false = {ValueFalse};
bins others[Width] = {[0:{Width{1'b1}}]} with (!(item inside {ValueTrue, ValueFalse}));
}
endgroup : mubi_cg
// use reg_field name as this name
function new(string name = "");
mubi_cg = new($sformatf("mubi%0d_cov_of_%s", Width, name));
endfunction : new
virtual function void sample(bit [Width-1:0] value);
mubi_cg.sample(value);
endfunction : sample
endclass : mubi_cov
typedef mubi_cov #(.Width(4),
.ValueTrue(prim_mubi_pkg::MuBi4True),
.ValueFalse(prim_mubi_pkg::MuBi4False)) mubi4_cov;
typedef mubi_cov #(.Width(8),
.ValueTrue(prim_mubi_pkg::MuBi8True),
.ValueFalse(prim_mubi_pkg::MuBi8False)) mubi8_cov;
typedef mubi_cov #(.Width(12),
.ValueTrue(prim_mubi_pkg::MuBi12True),
.ValueFalse(prim_mubi_pkg::MuBi12False)) mubi12_cov;
typedef mubi_cov #(.Width(16),
.ValueTrue(prim_mubi_pkg::MuBi16True),
.ValueFalse(prim_mubi_pkg::MuBi16False)) mubi16_cov;
// a mubi coverage object, which allows to dynamically select the width of mubi
class dv_base_mubi_cov extends uvm_object;
int mubi_width = 0;
// declare all mubi types, but only one will be created
mubi4_cov m_mubi4_cov;
mubi8_cov m_mubi8_cov;
mubi12_cov m_mubi12_cov;
mubi16_cov m_mubi16_cov;
`uvm_object_utils(dv_base_mubi_cov)
`uvm_object_new
// use reg_field name as this name
function void create_cov(int mubi_width);
string cov_name = $sformatf("mubi%0d_cov_of_%s", mubi_width, `gfn);
// create_cov can be invoked only once
`DV_CHECK_EQ(this.mubi_width, 0)
this.mubi_width = mubi_width;
case (mubi_width)
4: m_mubi4_cov = mubi4_cov::type_id::create(cov_name);
8: m_mubi8_cov = mubi8_cov::type_id::create(cov_name);
12: m_mubi12_cov = mubi12_cov::type_id::create(cov_name);
16: m_mubi16_cov = mubi16_cov::type_id::create(cov_name);
default: `uvm_fatal(`gfn, $sformatf("Unsupported mubi width (%0d) is used", mubi_width))
endcase
endfunction : create_cov
virtual function void sample(int value);
case (mubi_width)
4: m_mubi4_cov.sample(value);
8: m_mubi8_cov.sample(value);
12: m_mubi12_cov.sample(value);
16: m_mubi16_cov.sample(value);
default: `uvm_fatal(`gfn, $sformatf("Unsupported mubi width (%0d) is used", mubi_width))
endcase
endfunction : sample
endclass : dv_base_mubi_cov

View file

@ -9,6 +9,7 @@ filesets:
files_dv:
depend:
- lowrisc:dv:dv_utils
- lowrisc:prim:mubi
files:
- dv_base_reg_pkg.sv
- csr_excl_item.sv: {is_include_file: true}
@ -17,6 +18,8 @@ filesets:
- dv_base_mem.sv: {is_include_file: true}
- dv_base_reg_block.sv: {is_include_file: true}
- dv_base_reg_map.sv: {is_include_file: true}
- dv_base_lockable_field_cov.sv: {is_include_file: true}
- dv_base_mubi_cov.sv: {is_include_file: true}
file_type: systemVerilogSource
targets:

View file

@ -6,6 +6,20 @@
class dv_base_reg_block extends uvm_reg_block;
`uvm_object_utils(dv_base_reg_block)
// The default addr, data and byte widths.
//
// The UVM reg data structures are built on `UVM_REG_ADDR|DATA|BYTEENABLE_WIDTH settings which
// are compile time, i.e. fixed for all RAL models across the project. What if we have multiple
// RAL models that have different width needs? The solution is to let the UVM data structures be
// created with large enough widths to accommodate a heterogeneous set of RAL models, and use
// these runtime settings below instead, to perform associated computations. Values retrieved
// for comparisons, such as uvm_reg::get_mirrored_value() may return data that is wider than
// needed. It would then be upto the testbench to cast it to the appropriate width if necessary.
// TODO: These are ideally passed via `build` method.
uint addr_width = `UVM_REG_ADDR_WIDTH;
uint data_width = `UVM_REG_DATA_WIDTH;
uint be_width = `UVM_REG_BYTENABLE_WIDTH;
// Since an IP may contains more than one reg block we construct reg_block name as
// {ip_name}_{reg_interface_name}.
// All the reg_blocks in the IP share the same alert. In top-level, We construct the alert
@ -29,6 +43,19 @@ class dv_base_reg_block extends uvm_reg_block;
addr_range_t mapped_addr_ranges[$];
// Indicates whether accesses to unmapped regions of this block returns an error response (0).
protected bit unmapped_access_ok;
// Indicates whether byte writes are supported (enabled by default).
// TODO: Remove this in future - this is really a property of the physical interface that is used
// to access design through this RAL model (a.k.a, the map & adapter), and not the RAL model
// itself. This information should be sought using uvm_reg_adapter::supports_byte_enable instead.
// This is added for ease of rv_dm testbench development.
protected bit supports_byte_enable = 1'b1;
// Custom RAL models may support sub-word CSR writes smaller than CSR width.
protected bit supports_sub_word_csr_writes = 1'b0;
bit has_unmapped_addrs;
addr_range_t unmapped_addr_ranges[$];
@ -52,6 +79,30 @@ class dv_base_reg_block extends uvm_reg_block;
return csr_excl;
endfunction
function void set_unmapped_access_ok(bit ok);
unmapped_access_ok = ok;
endfunction
function bit get_unmapped_access_ok();
return unmapped_access_ok;
endfunction
function void set_supports_byte_enable(bit enable);
supports_byte_enable = enable;
endfunction
function bit get_supports_byte_enable();
return supports_byte_enable;
endfunction
function void set_supports_sub_word_csr_writes(bit enable);
supports_sub_word_csr_writes = enable;
endfunction
function bit get_supports_sub_word_csr_writes();
return supports_sub_word_csr_writes;
endfunction
// provide build function to supply base addr
virtual function void build(uvm_reg_addr_t base_addr,
csr_excl_item csr_excl = null);
@ -133,6 +184,7 @@ class dv_base_reg_block extends uvm_reg_block;
foreach (csrs[i]) begin
csr_addrs.push_back(csrs[i].get_address());
end
`uvm_info(`gfn, $sformatf("csr_addrs: %0p", csr_addrs), UVM_HIGH)
endfunction
// Internal function, used to get a list of all valid memory ranges
@ -146,6 +198,7 @@ class dv_base_reg_block extends uvm_reg_block;
mems[i].get_size() * mems[i].get_n_bytes() - 1;
mem_ranges.push_back(mem_range);
end
`uvm_info(`gfn, $sformatf("mem_ranges: %0p", mem_ranges), UVM_HIGH)
endfunction
// Used to get a list of all valid address ranges covered by this reg block
@ -169,7 +222,7 @@ class dv_base_reg_block extends uvm_reg_block;
// 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);
`uvm_info(`gfn, $sformatf("mapped_addr_ranges: %0p", mapped_addr_ranges), UVM_HIGH)
endfunction
// Used to get a list of all invalid address ranges in this reg block
@ -216,6 +269,7 @@ class dv_base_reg_block extends uvm_reg_block;
end
end
has_unmapped_addrs = (unmapped_addr_ranges.size() > 0);
`uvm_info(`gfn, $sformatf("unmapped_addr_ranges: %0p", unmapped_addr_ranges), UVM_HIGH)
endfunction
// Return the offset of the highest byte contained in either a register or a memory
@ -258,24 +312,26 @@ class dv_base_reg_block extends uvm_reg_block;
// Set the base address for the given register map
//
// Check that base_addr is aligned as required by the register block. If the supplied base_addr is
// the "magic" address '1, randomly pick an appropriately aligned base address and set it for the
// specified map.
function void set_base_addr(uvm_reg_addr_t base_addr = '1, uvm_reg_map map = null);
// Checks if the provided base_addr is aligned as required by the register block. If
// randomize_base_addr arg is set, then the base_addr arg is ignored - the function randomizes and
// sets the base_addr itself.
function void set_base_addr(uvm_reg_addr_t base_addr, uvm_reg_map map = null,
bit randomize_base_addr = 0);
uvm_reg_addr_t mask;
if (map == null) map = get_default_map();
mask = get_addr_mask(map);
// If base_addr is '1, randomly pick an aligned base address
if (base_addr == '1) begin
`DV_CHECK_STD_RANDOMIZE_WITH_FATAL(base_addr, (base_addr & mask) == '0;)
// If randomize_base_addr is set, randomly pick an aligned base address.
if (randomize_base_addr) begin
`DV_CHECK_STD_RANDOMIZE_WITH_FATAL(base_addr,
(base_addr & mask) == '0;
base_addr >> addr_width == 0;)
end else begin
`DV_CHECK_FATAL((base_addr & mask) == '0)
`DV_CHECK_FATAL((base_addr >> addr_width) == '0)
end
// Check base addr alignment (which should be guaranteed if we just picked it, but needs
// checking if not).
`DV_CHECK_FATAL((base_addr & mask) == '0)
`uvm_info(`gfn, $sformatf("Setting register base address to 0x%0h", base_addr), UVM_HIGH)
map.set_base_addr(base_addr);
endfunction
@ -286,7 +342,7 @@ class dv_base_reg_block extends uvm_reg_block;
// This is useful if you have a possibly misaligned address and you want to know whether it hits a
// register (since get_reg_by_offset needs the aligned address for the start of the register).
function uvm_reg_addr_t get_word_aligned_addr(uvm_reg_addr_t byte_addr);
uvm_reg_addr_t shift = $clog2(`UVM_REG_BYTENABLE_WIDTH);
uvm_reg_addr_t shift = $clog2(be_width);
return (byte_addr >> shift) << shift;
endfunction
@ -309,4 +365,11 @@ class dv_base_reg_block extends uvm_reg_block;
.map(map));
endfunction
// Set default map for this block and all its sub-blocks.
function void set_default_map_w_subblks(uvm_reg_map map);
dv_base_reg_block subblks[$];
set_default_map(map);
get_dv_base_reg_blocks(subblks);
foreach (subblks[i]) subblks[i].set_default_map_w_subblks(map);
endfunction
endclass

View file

@ -9,6 +9,14 @@ class dv_base_reg_field extends uvm_reg_field;
local bit is_intr_test_fld;
local uvm_reg_data_t staged_val, committed_val, shadowed_val;
// if this is lockable field, here is its corresponding regwen object
// if not, below 2 object will be null
local dv_base_reg_field regwen_fld;
local dv_base_lockable_field_cov lockable_field_cov;
// variable for mubi coverage, which is only created when this is a mubi reg
dv_base_mubi_cov mubi_cov;
`uvm_object_utils(dv_base_reg_field)
`uvm_object_new
@ -99,7 +107,20 @@ class dv_base_reg_field extends uvm_reg_field;
uvm_reg_block ral = this.get_parent().get_parent();
`DV_CHECK_EQ_FATAL(ral.is_locked(), 0, "RAL is locked, cannot add lockable reg or fld!")
get_flds_from_uvm_object(lockable_obj, `gfn, flds);
foreach (flds[i]) lockable_flds.push_back(flds[i]);
foreach (flds[i]) begin
lockable_flds.push_back(flds[i]);
flds[i].regwen_fld = this;
flds[i].create_lockable_fld_cov();
end
endfunction
function void create_lockable_fld_cov();
lockable_field_cov = dv_base_lockable_field_cov::type_id::create(`gfn);
endfunction
function void create_mubi_cov(int mubi_width);
mubi_cov = dv_base_mubi_cov::type_id::create(`gfn);
mubi_cov.create_cov(mubi_width);
endfunction
// Returns true if this field can lock the specified register/field, else return false.
@ -133,6 +154,18 @@ class dv_base_reg_field extends uvm_reg_field;
if (rw.status == UVM_IS_OK) begin
dv_base_reg parent_csr = get_dv_base_reg_parent();
parent_csr.clear_shadow_wr_staged();
if (lockable_field_cov != null) lockable_field_cov.post_read();
end
endtask
virtual task post_write(uvm_reg_item rw);
if (rw.status == UVM_IS_OK) begin
uvm_reg_data_t field_val = rw.value[0] & ((1 << get_n_bits()) - 1);
if (lockable_field_cov != null) lockable_field_cov.post_write(field_val, `gmv(regwen_fld));
if (mubi_cov != null) mubi_cov.sample(field_val);
end
endtask
@ -172,6 +205,7 @@ class dv_base_reg_field extends uvm_reg_field;
set_fld_access(0);
committed_val = get_mirrored_value();
shadowed_val = ~committed_val;
if (lockable_field_cov != null) lockable_field_cov.reset(get_reset());
endfunction
// this function can only be called when this reg is intr_test reg

View file

@ -113,6 +113,8 @@ package dv_base_reg_pkg;
endfunction : get_field_val
`include "csr_excl_item.sv"
`include "dv_base_lockable_field_cov.sv"
`include "dv_base_mubi_cov.sv"
`include "dv_base_reg_field.sv"
`include "dv_base_reg.sv"
`include "dv_base_mem.sv"

View file

@ -28,8 +28,9 @@ class dv_base_env_cfg #(type RAL_T = dv_base_reg_block) extends uvm_object;
}
// reg model & q of valid csr addresses
RAL_T ral;
dv_base_reg_block ral_models[string];
RAL_T ral;
dv_base_reg_block ral_models[string];
// A queue of the names of RAL models that should be created in the `initialize` function
// Related agents, adapters will be created in env as well as connecting them with scb
// For example, if the IP has an additional RAL model named `ral1`, add it into the list as below
@ -109,11 +110,15 @@ class dv_base_env_cfg #(type RAL_T = dv_base_reg_block) extends uvm_object;
csr_utils_pkg::reset_deasserted();
endfunction
// Creates RAL models and sets their base address based on the supplied arg.
//
// csr_base_addr is the base address to set to the RAL models. If it is all 1s, then we treat that
// as an indication to randomize the base address internally instead.
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;
bit randomize_base_addr = &csr_base_addr;
dv_base_reg_block reg_blk = create_ral_by_name(ral_name);
if (reg_blk.get_name() == RAL_T::type_name) `downcast(ral, reg_blk)
@ -122,31 +127,19 @@ class dv_base_env_cfg #(type RAL_T = dv_base_reg_block) extends uvm_object;
// later.
pre_build_ral_settings(reg_blk);
reg_blk.build(.base_addr(0), .csr_excl(null));
reg_blk.addr_width = bus_params_pkg::BUS_AW;
reg_blk.data_width = bus_params_pkg::BUS_DW;
reg_blk.be_width = bus_params_pkg::BUS_DBW;
post_build_ral_settings(reg_blk);
reg_blk.lock_model();
// Now the model is locked, we know its layout. Set the base address for the register block.
// The function internally picks a random one if we pass '1 to it, and performs an integrity
// check on the set address.
//
// The definition of base_addr explicitly casts from a bus address to a uvm_reg_addr_t (to
// correctly handle the case where a bus address is narrower than a uvm_reg_addr_t).
base_addr = (&csr_base_addr ?
{`UVM_REG_ADDR_WIDTH{1'b1}} :
{{(`UVM_REG_ADDR_WIDTH - bus_params_pkg::BUS_AW){1'b0}}, csr_base_addr});
reg_blk.set_base_addr(base_addr);
reg_blk.set_base_addr(.base_addr(`UVM_REG_ADDR_WIDTH'(csr_base_addr)),
.randomize_base_addr(randomize_base_addr));
// Get list of valid csr addresses (useful in seq to randomize addr as well as in scb checks)
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

@ -166,20 +166,22 @@ class dv_base_vseq #(type RAL_T = dv_base_reg_block,
// wrapper task around run_csr_vseq - the purpose is to be able to call this directly for actual
// csr tests (as opposed to higher level stress test that could also run csr seq as a fork by
// calling run_csr_vseq(..) task)
virtual task run_csr_vseq_wrapper(int num_times = 1);
virtual task run_csr_vseq_wrapper(int num_times = 1, dv_base_reg_block models[$] = {});
string csr_test_type;
// env needs to have a ral instance
`DV_CHECK_GE_FATAL(cfg.ral_models.size(), 1)
// Env needs to have a ral instance.
if (models.size() == 0) begin
`DV_CHECK_GE_FATAL(cfg.ral_models.size(), 1)
end
// get csr_test_type from plusarg
// Get csr_test_type from plusarg
void'($value$plusargs("csr_%0s", csr_test_type));
// run the csr seq
for (int i = 1; i <= num_times; i++) begin
`uvm_info(`gfn, $sformatf("running csr %0s vseq iteration %0d/%0d",
`uvm_info(`gfn, $sformatf("Running csr %0s vseq iteration %0d/%0d.",
csr_test_type, i, num_times), UVM_LOW)
run_csr_vseq(.csr_test_type(csr_test_type));
run_csr_vseq(.csr_test_type(csr_test_type), .models(models));
end
endtask
@ -187,24 +189,27 @@ class dv_base_vseq #(type RAL_T = dv_base_reg_block,
// arg csr_test_type: what csr test to run {hw_reset, rw, bit_bash, aliasing}
// arg num_test_csrs:instead of testing the entire ral model or passing test chunk info via
// plusarg, provide ability to set a random number of csrs to test from higher level sequence
virtual task run_csr_vseq(string csr_test_type = "",
// `models` is the list of RAL models to run the common sequences on.
// `ral_name` is the string name of the RAL to run the common sequences on.
// Both of these inputs are 'null' by default. If externally set, `models` takes precedence over
// `ral_name`.
virtual task run_csr_vseq(string csr_test_type,
int num_test_csrs = 0,
bit do_rand_wr_and_reset = 1,
dv_base_reg_block models[$] = {},
string ral_name = "");
csr_base_seq m_csr_seq;
dv_base_reg_block models[string];
csr_base_seq m_csr_seq;
// env needs to have at least one ral instance
`DV_CHECK_GE_FATAL(cfg.ral_models.size(), 1)
// If ral_name is specified, only use registers on that named interface. Otherwise, use all
// registers.
if (ral_name != "") begin
`DV_CHECK_FATAL(cfg.ral_models.exists(ral_name))
models[ral_name] = cfg.ral_models[ral_name];
end else begin
foreach (cfg.ral_models[i]) begin
models[i] = cfg.ral_models[i];
// Filter out the RAL blocks under test with the supplied name, if available.
if (models.size() == 0) begin
if (ral_name != "") begin
`DV_CHECK_FATAL(cfg.ral_models.exists(ral_name))
models.push_back(cfg.ral_models[ral_name]);
end else begin
foreach (cfg.ral_models[i]) models.push_back(cfg.ral_models[i]);
end
end
@ -238,11 +243,7 @@ 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");
// 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 (models[i]) begin
m_csr_write_seq.models[i] = models[i];
end
m_csr_write_seq.models = models;
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);
@ -259,11 +260,7 @@ 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");
// 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 (models[i]) begin
m_csr_seq.models[i] = models[i];
end
m_csr_seq.models = models;
m_csr_seq.external_checker = cfg.en_scb;
m_csr_seq.num_test_csrs = num_test_csrs;
m_csr_seq.start(null);

View file

@ -57,8 +57,11 @@ class mem_bkdr_util extends uvm_object;
event writememh_event;
// Initialize the class instance.
// `extra_bits_per_subword` is the width any additional metadata that is not captured in secded
// package.
function new(string name = "", string path, int unsigned depth,
longint unsigned n_bits, err_detection_e err_detection_scheme);
longint unsigned n_bits, err_detection_e err_detection_scheme,
int extra_bits_per_subword = 0);
bit res;
super.new(name);
@ -79,7 +82,8 @@ class mem_bkdr_util extends uvm_object;
prim_secded_e secded_eds = prim_secded_e'(err_detection_scheme);
int non_ecc_bits_per_subword = get_ecc_data_width(secded_eds);
int ecc_bits_per_subword = get_ecc_parity_width(secded_eds);
int bits_per_subword = non_ecc_bits_per_subword + ecc_bits_per_subword;
int bits_per_subword = non_ecc_bits_per_subword + ecc_bits_per_subword +
extra_bits_per_subword;
int subwords_per_word;
// We shouldn't truncate the actual data word. This check ensures that err_detection_scheme
@ -252,6 +256,59 @@ class mem_bkdr_util extends uvm_object;
return {read128(addr + 16), read128(addr)};
endfunction
// Returns data with correctly computed ECC.
virtual function uvm_hdl_data_t get_ecc_computed_data(uvm_hdl_data_t data);
case (err_detection_scheme)
ErrDetectionNone: ;
Ecc_22_16: begin
data = prim_secded_pkg::prim_secded_22_16_enc(data[15:0]);
end
EccHamming_22_16: begin
data = prim_secded_pkg::prim_secded_hamming_22_16_enc(data[15:0]);
end
Ecc_39_32: begin
data = prim_secded_pkg::prim_secded_39_32_enc(data[31:0]);
end
EccHamming_39_32: begin
data = prim_secded_pkg::prim_secded_hamming_39_32_enc(data[31:0]);
end
Ecc_72_64: begin
data = prim_secded_pkg::prim_secded_72_64_enc(data[63:0]);
end
EccHamming_72_64: begin
data = prim_secded_pkg::prim_secded_hamming_72_64_enc(data[63:0]);
end
EccHamming_76_68: begin
data = prim_secded_pkg::prim_secded_hamming_76_68_enc(data[67:0]);
end
EccInv_22_16: begin
data = prim_secded_pkg::prim_secded_inv_22_16_enc(data[15:0]);
end
EccInvHamming_22_16: begin
data = prim_secded_pkg::prim_secded_inv_hamming_22_16_enc(data[15:0]);
end
EccInv_39_32: begin
data = prim_secded_pkg::prim_secded_inv_39_32_enc(data[31:0]);
end
EccInvHamming_39_32: begin
data = prim_secded_pkg::prim_secded_inv_hamming_39_32_enc(data[31:0]);
end
EccInv_72_64: begin
data = prim_secded_pkg::prim_secded_inv_72_64_enc(data[63:0]);
end
EccInvHamming_72_64: begin
data = prim_secded_pkg::prim_secded_inv_hamming_72_64_enc(data[63:0]);
end
EccInvHamming_76_68: begin
data = prim_secded_pkg::prim_secded_inv_hamming_76_68_enc(data[67:0]);
end
default: begin
`uvm_fatal(`gfn, $sformatf("ECC scheme %0s is unsupported.", err_detection_scheme))
end
endcase
return data;
endfunction
// Write the entire word at the given address with the specified data.
//
// addr is the byte address starting at offset 0. Mask the upper address bits as needed before
@ -263,7 +320,7 @@ class mem_bkdr_util extends uvm_object;
uint32_t index;
if (!check_addr_valid(addr)) return;
index = addr >> addr_lsb;
res = uvm_hdl_deposit($sformatf("%0s[%0d]", path, index), data);
res = uvm_hdl_deposit($sformatf("%0s[%0d]", path, index), data);
`DV_CHECK_EQ(res, 1, $sformatf("uvm_hdl_deposit failed at index %0d", index))
endfunction
@ -289,55 +346,13 @@ class mem_bkdr_util extends uvm_object;
return;
end
// Update the byte index with the new value.
rw_data[byte_idx * 8 +: 8] = data;
case (err_detection_scheme)
ErrDetectionNone: ;
Ecc_22_16: begin
rw_data = prim_secded_pkg::prim_secded_22_16_enc(rw_data[15:0]);
end
EccHamming_22_16: begin
rw_data = prim_secded_pkg::prim_secded_hamming_22_16_enc(rw_data[15:0]);
end
Ecc_39_32: begin
rw_data = prim_secded_pkg::prim_secded_39_32_enc(rw_data[31:0]);
end
EccHamming_39_32: begin
rw_data = prim_secded_pkg::prim_secded_hamming_39_32_enc(rw_data[31:0]);
end
Ecc_72_64: begin
rw_data = prim_secded_pkg::prim_secded_72_64_enc(rw_data[63:0]);
end
EccHamming_72_64: begin
rw_data = prim_secded_pkg::prim_secded_hamming_72_64_enc(rw_data[63:0]);
end
EccHamming_76_68: begin
rw_data = prim_secded_pkg::prim_secded_hamming_76_68_enc(rw_data[63:0]);
end
EccInv_22_16: begin
rw_data = prim_secded_pkg::prim_secded_inv_22_16_enc(rw_data[15:0]);
end
EccInvHamming_22_16: begin
rw_data = prim_secded_pkg::prim_secded_inv_hamming_22_16_enc(rw_data[15:0]);
end
EccInv_39_32: begin
rw_data = prim_secded_pkg::prim_secded_inv_39_32_enc(rw_data[31:0]);
end
EccInvHamming_39_32: begin
rw_data = prim_secded_pkg::prim_secded_inv_hamming_39_32_enc(rw_data[31:0]);
end
EccInv_72_64: begin
rw_data = prim_secded_pkg::prim_secded_inv_72_64_enc(rw_data[63:0]);
end
EccInvHamming_72_64: begin
rw_data = prim_secded_pkg::prim_secded_inv_hamming_72_64_enc(rw_data[63:0]);
end
EccInvHamming_76_68: begin
rw_data = prim_secded_pkg::prim_secded_inv_hamming_76_68_enc(rw_data[63:0]);
end
default: begin
`uvm_error(`gfn, $sformatf("ECC scheme %0s is unsupported.", err_detection_scheme))
end
endcase
// Compute & set the new ECC value.
rw_data = get_ecc_computed_data(rw_data);
// Write the whole array back to the memory.
write(addr, rw_data);
endfunction
@ -481,43 +496,50 @@ class mem_bkdr_util extends uvm_object;
->writememh_event;
endfunction
// print mem
// Print the contents of the memory.
virtual function void print_mem();
`uvm_info(`gfn, "Print memory", UVM_LOW)
for (int i = 0; i < depth; i++) begin
`uvm_info(`gfn, $sformatf("mem[%0d] = 0x%0h", i, read(i)), UVM_LOW)
uvm_hdl_data_t data = read(i * bytes_per_word);
`uvm_info(`gfn, $sformatf("mem[%0d] = 0x%0h", i, data), UVM_LOW)
end
endfunction
// clear or set memory
// Clear the memory to all 0s.
virtual function void clear_mem();
`uvm_info(`gfn, "Clear memory", UVM_LOW)
for (int i = 0; i < size_bytes; i++) begin
write8(i, '0);
for (int i = 0; i < depth; i++) begin
uvm_hdl_data_t data = '{default:0};
write(i * bytes_per_word, data);
end
endfunction
// Set the memory to all 1s.
virtual function void set_mem();
`uvm_info(`gfn, "Set memory", UVM_LOW)
for (int i = 0; i < size_bytes; i++) begin
write8(i, '1);
for (int i = 0; i < depth; i++) begin
uvm_hdl_data_t data = '{default:1};
write(i * bytes_per_word, data);
end
endfunction
// randomize the memory
// Randomize the memory with correct ECC.
virtual function void randomize_mem();
logic [7:0] rand_val;
`uvm_info(`gfn, "Randomizing mem contents", UVM_LOW)
for (int i = 0; i < size_bytes; i++) begin
`DV_CHECK_STD_RANDOMIZE_FATAL(rand_val, "Randomization failed!", path)
write8(i, rand_val);
for (int i = 0; i < depth; i++) begin
uvm_hdl_data_t data;
`DV_CHECK_STD_RANDOMIZE_FATAL(data, "Randomization failed!", path)
data = get_ecc_computed_data(data);
write(i * bytes_per_word, data);
end
endfunction
// invalidate the memory.
// Invalidate the memory.
virtual function void invalidate_mem();
`uvm_info(`gfn, "Invalidating (Xs) mem contents", UVM_LOW)
for (int i = 0; i < size_bytes; i++) begin
write8(i, 'x);
for (int i = 0; i < depth; i++) begin
uvm_hdl_data_t data;
write(i * bytes_per_word, data);
end
endfunction

View file

@ -5,36 +5,76 @@
// Wrapper functions to write different partitions in OTP.
// This file is included in `mem_bkdr_util.sv` as a continuation of `mem_bkdr_util` class.
virtual function void otp_write_lc_partition(lc_ctrl_state_pkg::lc_state_e lc_state);
for (int i = 0; i < LcStateSize; i+=4) begin
virtual function void otp_write_lc_partition_state(lc_ctrl_state_pkg::lc_state_e lc_state);
for (int i = 0; i < LcStateSize; i += 4) begin
write32(i + LcStateOffset, lc_state[i*8+:32]);
end
endfunction
virtual function void otp_write_lc_partition_cnt(lc_ctrl_state_pkg::lc_cnt_e lc_cnt);
for (int i = 0; i < LcTransitionCntSize; i += 4) begin
write32(i + LcTransitionCntOffset, lc_cnt[i*8+:32]);
end
endfunction : otp_write_lc_partition_cnt
function void otp_write_lc_partition(lc_ctrl_state_pkg::lc_cnt_e lc_cnt,
lc_ctrl_state_pkg::lc_state_e lc_state);
otp_write_lc_partition_cnt(lc_cnt);
otp_write_lc_partition_state(lc_state);
endfunction : otp_write_lc_partition
// The following steps are needed to backdoor write a secret partition:
// 1). Scramble the RAW input data.
// 2). Backdoor write the scrambled input data to OTP memory.
// 3). Calculate the correct digest for the secret partition.
// 4). Backdoor write digest data to OTP memory.
virtual function void otp_write_secret0_partition(bit [TestUnlockTokenSize*8-1:0] unlock_token,
bit [TestExitTokenSize*8-1:0] exit_token);
virtual function void otp_write_secret0_partition(
bit [TestUnlockTokenSize*8-1:0] unlock_token,
bit [TestExitTokenSize*8-1:0] exit_token);
bit [Secret0DigestSize*8-1:0] digest;
bit [TestUnlockTokenSize*8-1:0] scrambled_unlock_token;
bit [TestExitTokenSize*8-1:0] scrambled_exit_token;
bit [bus_params_pkg::BUS_DW-1:0] secret_data[$];
for (int i = 0; i < TestUnlockTokenSize; i+=8) begin
for (int i = 0; i < TestUnlockTokenSize; i += 8) begin
scrambled_unlock_token[i*8+:64] = scramble_data(unlock_token[i*8+:64], Secret0Idx);
write64(i + TestUnlockTokenOffset, scrambled_unlock_token[i*8+:64]);
end
for (int i = 0; i < TestExitTokenSize; i+=8) begin
for (int i = 0; i < TestExitTokenSize; i += 8) begin
scrambled_exit_token[i*8+:64] = scramble_data(exit_token[i*8+:64], Secret0Idx);
write64(i + TestExitTokenOffset, scrambled_exit_token[i*8+:64]);
end
secret_data = {<<32 {scrambled_exit_token, scrambled_unlock_token}};
secret_data = {<<32{scrambled_exit_token, scrambled_unlock_token}};
digest = cal_digest(Secret0Idx, secret_data);
write64(Secret0DigestOffset, digest);
endfunction
virtual function void otp_write_hw_cfg_partition(
bit [DeviceIdSize*8-1:0] device_id, bit [ManufStateSize*8-1:0] manuf_state,
bit [EnSramIfetchSize*8-1:0] en_sram_ifetch,
bit [EnCsrngSwAppReadSize*8-1:0] en_csrng_sw_app_read,
bit [EnEntropySrcFwReadSize*8-1:0] en_entropy_src_fw_read,
bit [EnEntropySrcFwOverSize*8-1:0] en_entropy_src_fw_over);
bit [HwCfgDigestSize*8-1:0] digest;
bit [bus_params_pkg::BUS_DW-1:0] hw_cfg_data[$];
for (int i = 0; i < DeviceIdSize; i += 4) begin
write32(i + DeviceIdOffset, device_id[i*8+:32]);
end
for (int i = 0; i < ManufStateSize; i += 4) begin
write32(i + ManufStateOffset, manuf_state[i*8+:32]);
end
write32(EnSramIfetchOffset,
{en_entropy_src_fw_over, en_entropy_src_fw_read, en_csrng_sw_app_read, en_sram_ifetch});
hw_cfg_data = {<<32 {32'h0, en_entropy_src_fw_over, en_entropy_src_fw_read,
en_csrng_sw_app_read, en_sram_ifetch, manuf_state, device_id}};
digest = cal_digest(HwCfgIdx, hw_cfg_data);
write64(HwCfgDigestOffset, digest);
endfunction

View file

@ -72,7 +72,8 @@ virtual function void rom_encrypt_write32_integ(logic [bus_params_pkg::BUS_AW-1:
logic [31:0] data,
logic [SRAM_KEY_WIDTH-1:0] key,
logic [SRAM_BLOCK_WIDTH-1:0] nonce,
bit scramble_data);
bit scramble_data,
bit [38:0] flip_bits = 0);
logic [bus_params_pkg::BUS_AW-1:0] bus_addr = '0;
logic [38:0] integ_data;
logic [38:0] scrambled_data;
@ -95,12 +96,15 @@ virtual function void rom_encrypt_write32_integ(logic [bus_params_pkg::BUS_AW-1:
if(scramble_data) begin
// Calculate the integrity constant
integ_data = prim_secded_pkg::prim_secded_39_32_enc(data);
integ_data = prim_secded_pkg::prim_secded_inv_39_32_enc(data);
// flip some bits to inject integrity fault
integ_data ^= flip_bits;
// Calculate the scrambled data
wdata_arr = {<<{integ_data}};
wdata_arr = sram_scrambler_pkg::encrypt_sram_data(
wdata_arr, 39, 0, rom_addr, addr_width, key_arr, nonce_arr
wdata_arr, 39, 39, rom_addr, addr_width, key_arr, nonce_arr
);
scrambled_data = {<<{wdata_arr}};
end
@ -115,8 +119,6 @@ virtual function void rom_encrypt_write32_integ(logic [bus_params_pkg::BUS_AW-1:
for (int i = 0; i < addr_width; i++) begin
bus_addr[addr_lsb + i] = scrambled_addr[i];
end
// Write the scrambled data to memory
write39integ(bus_addr, scrambled_data);
endfunction

View file

@ -5,391 +5,120 @@
// Wrapper functions for SRAM's encrypted read/write operations.
// This file is included in `mem_bkdr_util.sv` as a continuation of `mem_bkdr_util` class.
virtual function logic [7:0] sram_encrypt_read8(logic [bus_params_pkg::BUS_AW-1:0] addr,
logic [SRAM_KEY_WIDTH-1:0] key,
logic [SRAM_BLOCK_WIDTH-1:0] nonce);
logic [bus_params_pkg::BUS_AW-1:0] bus_addr = '0;
logic [7:0] rdata = '0;
function logic [bus_params_pkg::BUS_AW-1:0] get_sram_encrypt_addr (
logic [bus_params_pkg::BUS_AW-1:0] addr,
logic [SRAM_BLOCK_WIDTH-1:0] nonce,
logic [31:0] extra_addr_bits = '0);
logic rdata_arr [] = new[8];
logic scrambled_addr[] = new[addr_width];
logic sram_addr [] = new[addr_width];
logic key_arr [] = new[SRAM_KEY_WIDTH];
logic nonce_arr [] = new[SRAM_BLOCK_WIDTH];
int full_addr_width = addr_width + extra_addr_bits;
key_arr = {<<{key}};
nonce_arr = {<<{nonce}};
for (int i = 0; i < addr_width; i++) begin
sram_addr[i] = addr[addr_lsb + i];
end
// Calculate the scrambled address
scrambled_addr = sram_scrambler_pkg::encrypt_sram_addr(sram_addr, addr_width, nonce_arr);
// Construct bus representation of the address
for (int i = 0; i < addr_lsb; i++) begin
bus_addr[i] = addr[i];
end
for (int i = 0; i < addr_width; i++) begin
bus_addr[addr_lsb + i] = scrambled_addr[i];
end
// Read memory, and return the decrypted data
rdata = read8(bus_addr);
rdata_arr = {<<{rdata}};
rdata_arr = sram_scrambler_pkg::decrypt_sram_data(
rdata_arr, 8, 8, sram_addr, addr_width, key_arr, nonce_arr
);
rdata = {<<{rdata_arr}};
return rdata;
endfunction
virtual function logic [15:0] sram_encrypt_read16(logic [bus_params_pkg::BUS_AW-1:0] addr,
logic [SRAM_KEY_WIDTH-1:0] key,
logic [SRAM_BLOCK_WIDTH-1:0] nonce);
logic [bus_params_pkg::BUS_AW-1:0] bus_addr = '0;
logic [15:0] rdata = '0;
logic rdata_arr [] = new[16];
logic scrambled_addr[] = new[addr_width];
logic sram_addr [] = new[addr_width];
logic key_arr [] = new[SRAM_KEY_WIDTH];
logic nonce_arr [] = new[SRAM_BLOCK_WIDTH];
key_arr = {<<{key}};
nonce_arr = {<<{nonce}};
for (int i = 0; i < addr_width; i++) begin
sram_addr[i] = addr[addr_lsb + i];
end
// Calculate the scrambled address
scrambled_addr = sram_scrambler_pkg::encrypt_sram_addr(sram_addr, addr_width, nonce_arr);
// Construct bus representation of the address
for (int i = 0; i < addr_lsb; i++) begin
bus_addr[i] = addr[i];
end
for (int i = 0; i < addr_width; i++) begin
bus_addr[addr_lsb + i] = scrambled_addr[i];
end
// Read memory and return the decrypted data
rdata = read16(bus_addr);
rdata_arr = {<<{rdata}};
rdata_arr = sram_scrambler_pkg::decrypt_sram_data(
rdata_arr, 16, 8, sram_addr, addr_width, key_arr, nonce_arr
);
rdata = {<<{rdata_arr}};
return rdata;
endfunction
virtual function logic [31:0] sram_encrypt_read32(logic [bus_params_pkg::BUS_AW-1:0] addr,
logic [SRAM_KEY_WIDTH-1:0] key,
logic [SRAM_BLOCK_WIDTH-1:0] nonce);
logic [bus_params_pkg::BUS_AW-1:0] bus_addr = '0;
logic [31:0] rdata = '0;
logic rdata_arr [] = new[32];
logic scrambled_addr[] = new[addr_width];
logic sram_addr [] = new[addr_width];
logic key_arr [] = new[SRAM_KEY_WIDTH];
logic nonce_arr [] = new[SRAM_BLOCK_WIDTH];
key_arr = {<<{key}};
nonce_arr = {<<{nonce}};
for (int i = 0; i < addr_width; i++) begin
sram_addr[i] = addr[addr_lsb + i];
end
// Calculate the scrambled address
scrambled_addr = sram_scrambler_pkg::encrypt_sram_addr(sram_addr, addr_width, nonce_arr);
// Construct bus representation of the address
for (int i = 0; i < addr_lsb; i++) begin
bus_addr[i] = addr[i];
end
for (int i = 0; i < addr_width; i++) begin
bus_addr[addr_lsb + i] = scrambled_addr[i];
end
// Read memory and return the decrypted data
rdata = read32(bus_addr);
rdata_arr = {<<{rdata}};
rdata_arr = sram_scrambler_pkg::decrypt_sram_data(
rdata_arr, 32, 8, sram_addr, addr_width, key_arr, nonce_arr
);
rdata = {<<{rdata_arr}};
return rdata;
endfunction
virtual function logic [38:0] sram_encrypt_read32_integ(logic [bus_params_pkg::BUS_AW-1:0] addr,
logic [SRAM_KEY_WIDTH-1:0] key,
logic [SRAM_BLOCK_WIDTH-1:0] nonce);
logic [bus_params_pkg::BUS_AW-1:0] bus_addr = '0;
logic [38:0] rdata = '0;
logic rdata_arr [] = new[39];
logic scrambled_addr[] = new[addr_width];
logic sram_addr [] = new[addr_width];
logic key_arr [] = new[SRAM_KEY_WIDTH];
logic nonce_arr [] = new[SRAM_BLOCK_WIDTH];
key_arr = {<<{key}};
nonce_arr = {<<{nonce}};
for (int i = 0; i < addr_width; i++) begin
sram_addr[i] = addr[addr_lsb + i];
end
// Calculate the scrambled address
scrambled_addr = sram_scrambler_pkg::encrypt_sram_addr(sram_addr, addr_width, nonce_arr);
// Construct bus representation of the address
for (int i = 0; i < addr_lsb; i++) begin
bus_addr[i] = addr[i];
end
for (int i = 0; i < addr_width; i++) begin
bus_addr[addr_lsb + i] = scrambled_addr[i];
end
// Read memory and return the decrypted data
rdata = read39integ(bus_addr);
`uvm_info(`gfn, $sformatf("scr data: 0x%0x", rdata), UVM_HIGH)
rdata_arr = {<<{rdata}};
rdata_arr = sram_scrambler_pkg::decrypt_sram_data(
rdata_arr, 39, 39, sram_addr, addr_width, key_arr, nonce_arr
);
rdata = {<<{rdata_arr}};
// Only return the data payload without ECC bits.
return rdata[31:0];
endfunction
virtual function logic [63:0] sram_encrypt_read64(logic [bus_params_pkg::BUS_AW-1:0] addr,
logic [SRAM_KEY_WIDTH-1:0] key,
logic [SRAM_BLOCK_WIDTH-1:0] nonce);
logic [bus_params_pkg::BUS_AW-1:0] bus_addr = '0;
logic [63:0] rdata = '0;
logic rdata_arr [] = new[64];
logic scrambled_addr[] = new[addr_width];
logic sram_addr [] = new[addr_width];
logic key_arr [] = new[SRAM_KEY_WIDTH];
logic nonce_arr [] = new[SRAM_BLOCK_WIDTH];
key_arr = {<<{key}};
nonce_arr = {<<{nonce}};
for (int i = 0; i < addr_width; i++) begin
sram_addr[i] = addr[addr_lsb + i];
end
// Calculate the scrambled address
scrambled_addr = sram_scrambler_pkg::encrypt_sram_addr(sram_addr, addr_width, nonce_arr);
// Construct bus representation of the address
for (int i = 0; i < addr_lsb; i++) begin
bus_addr[i] = addr[i];
end
for (int i = 0; i < addr_width; i++) begin
bus_addr[addr_lsb + i] = scrambled_addr[i];
end
// Read memory and return the decrypted data
rdata = read64(bus_addr);
rdata_arr = {<<{rdata}};
rdata_arr = sram_scrambler_pkg::decrypt_sram_data(
rdata_arr, 64, 8, sram_addr, addr_width, key_arr, nonce_arr
);
rdata = {<<{rdata_arr}};
return rdata;
endfunction
virtual function void sram_encrypt_write8(logic [bus_params_pkg::BUS_AW-1:0] addr,
logic [7:0] data,
logic [SRAM_KEY_WIDTH-1:0] key,
logic [SRAM_BLOCK_WIDTH-1:0] nonce);
logic [bus_params_pkg::BUS_AW-1:0] bus_addr = '0;
logic [7:0] scrambled_data;
logic wdata_arr [] = new[8];
logic scrambled_addr [] = new[addr_width];
logic sram_addr [] = new[addr_width];
logic key_arr [] = new[SRAM_KEY_WIDTH];
logic [bus_params_pkg::BUS_AW-1:0] scr_addr;
logic scr_addr_arr [] = new[full_addr_width];
logic addr_arr [] = new[full_addr_width];
logic nonce_arr [] = new[SRAM_BLOCK_WIDTH];
key_arr = {<<{key}};
nonce_arr = {<<{nonce}};
for (int i = 0; i < addr_width; i++) begin
sram_addr[i] = addr[addr_lsb + i];
for (int i = 0; i < full_addr_width; i++) begin
addr_arr[i] = addr[addr_lsb + i];
end
// Calculate the scrambled address
scrambled_addr = sram_scrambler_pkg::encrypt_sram_addr(sram_addr, addr_width, nonce_arr);
// calculate scrambled address
scr_addr_arr = sram_scrambler_pkg::encrypt_sram_addr(addr_arr, full_addr_width, nonce_arr);
// Calculate the scrambled data
wdata_arr = {<<{data}};
wdata_arr = sram_scrambler_pkg::encrypt_sram_data(
wdata_arr, 8, 8, sram_addr, addr_width, key_arr, nonce_arr
);
scrambled_data = {<<{wdata_arr}};
// Construct bus representation of the address
// convert to bus address output
for (int i = 0; i < addr_lsb; i++) begin
bus_addr[i] = addr[i];
end
for (int i = 0; i < addr_width; i++) begin
bus_addr[addr_lsb + i] = scrambled_addr[i];
scr_addr[i] = addr[i];
end
// Write the scrambled data to memory
write8(bus_addr, scrambled_data);
endfunction
virtual function void sram_encrypt_write16(logic [bus_params_pkg::BUS_AW-1:0] addr,
logic [15:0] data,
logic [SRAM_KEY_WIDTH-1:0] key,
logic [SRAM_BLOCK_WIDTH-1:0] nonce);
logic [bus_params_pkg::BUS_AW-1:0] bus_addr = '0;
logic [15:0] scrambled_data;
logic wdata_arr [] = new[16];
logic scrambled_addr [] = new[addr_width];
logic sram_addr [] = new[addr_width];
logic key_arr [] = new[SRAM_KEY_WIDTH];
logic nonce_arr [] = new[SRAM_BLOCK_WIDTH];
key_arr = {<<{key}};
nonce_arr = {<<{nonce}};
for (int i = 0; i < addr_width; i++) begin
sram_addr[i] = addr[addr_lsb + i];
for (int i = 0; i < full_addr_width; i++) begin
scr_addr[addr_lsb + i] = scr_addr_arr[i];
end
// Calculate the scrambled address
scrambled_addr = sram_scrambler_pkg::encrypt_sram_addr(sram_addr, addr_width, nonce_arr);
return scr_addr;
// Calculate the scrambled data
wdata_arr = {<<{data}};
wdata_arr = sram_scrambler_pkg::encrypt_sram_data(
wdata_arr, 16, 8, sram_addr, addr_width, key_arr, nonce_arr
);
scrambled_data = {<<{wdata_arr}};
endfunction // get_sram_encrypt_addr
// Construct bus representation of the address
for (int i = 0; i < addr_lsb; i++) begin
bus_addr[i] = addr[i];
end
for (int i = 0; i < addr_width; i++) begin
bus_addr[addr_lsb + i] = scrambled_addr[i];
end
function logic [38:0] get_sram_encrypt32_intg_data (
logic [bus_params_pkg::BUS_AW-1:0] addr,
logic [31:0] data,
logic [SRAM_KEY_WIDTH-1:0] key,
logic [SRAM_BLOCK_WIDTH-1:0] nonce,
int extra_addr_bits=0);
// Write the scrambled data to memory
write16(bus_addr, scrambled_data);
endfunction
virtual function void sram_encrypt_write32(logic [bus_params_pkg::BUS_AW-1:0] addr,
logic [31:0] data,
logic [SRAM_KEY_WIDTH-1:0] key,
logic [SRAM_BLOCK_WIDTH-1:0] nonce);
logic [bus_params_pkg::BUS_AW-1:0] bus_addr = '0;
logic [31:0] scrambled_data;
logic wdata_arr [] = new[32];
logic scrambled_addr [] = new[addr_width];
logic sram_addr [] = new[addr_width];
logic key_arr [] = new[SRAM_KEY_WIDTH];
logic nonce_arr [] = new[SRAM_BLOCK_WIDTH];
key_arr = {<<{key}};
nonce_arr = {<<{nonce}};
for (int i = 0; i < addr_width; i++) begin
sram_addr[i] = addr[addr_lsb + i];
end
// Calculate the scrambled address
scrambled_addr = sram_scrambler_pkg::encrypt_sram_addr(sram_addr, addr_width, nonce_arr);
// Calculate the scrambled data
wdata_arr = {<<{data}};
wdata_arr = sram_scrambler_pkg::encrypt_sram_data(
wdata_arr, 32, 8, sram_addr, addr_width, key_arr, nonce_arr
);
scrambled_data = {<<{wdata_arr}};
// Construct bus representation of the address
for (int i = 0; i < addr_lsb; i++) begin
bus_addr[i] = addr[i];
end
for (int i = 0; i < addr_width; i++) begin
bus_addr[addr_lsb + i] = scrambled_addr[i];
end
// Write the scrambled data to memory
write32(bus_addr, scrambled_data);
endfunction
virtual function void sram_encrypt_write32_integ(logic [bus_params_pkg::BUS_AW-1:0] addr,
logic [31:0] data,
logic [SRAM_KEY_WIDTH-1:0] key,
logic [SRAM_BLOCK_WIDTH-1:0] nonce);
logic [bus_params_pkg::BUS_AW-1:0] bus_addr = '0;
logic [38:0] integ_data;
logic [38:0] scrambled_data;
int full_addr_width = addr_width + extra_addr_bits;
logic wdata_arr [] = new[39];
logic scrambled_addr [] = new[addr_width];
logic sram_addr [] = new[addr_width];
logic addr_arr [] = new[full_addr_width];
logic key_arr [] = new[SRAM_KEY_WIDTH];
logic nonce_arr [] = new[SRAM_BLOCK_WIDTH];
key_arr = {<<{key}};
nonce_arr = {<<{nonce}};
for (int i = 0; i < addr_width; i++) begin
sram_addr[i] = addr[addr_lsb + i];
for (int i = 0; i < full_addr_width; i++) begin
addr_arr[i] = addr[addr_lsb + i];
end
// Calculate the scrambled address
scrambled_addr = sram_scrambler_pkg::encrypt_sram_addr(sram_addr, addr_width, nonce_arr);
// Calculate the integrity constant
integ_data = prim_secded_pkg::prim_secded_inv_39_32_enc(data);
// Calculate the scrambled data
wdata_arr = {<<{integ_data}};
wdata_arr = sram_scrambler_pkg::encrypt_sram_data(
wdata_arr, 39, 39, sram_addr, addr_width, key_arr, nonce_arr
wdata_arr, 39, 39, addr_arr, full_addr_width, key_arr, nonce_arr
);
scrambled_data = {<<{wdata_arr}};
// Construct bus representation of the address
for (int i = 0; i < addr_lsb; i++) begin
bus_addr[i] = addr[i];
end
return scrambled_data;
endfunction // get_sram_encrypt32_intg_data
virtual function logic [38:0] sram_encrypt_read32_integ(logic [bus_params_pkg::BUS_AW-1:0] addr,
logic [SRAM_KEY_WIDTH-1:0] key,
logic [SRAM_BLOCK_WIDTH-1:0] nonce);
logic [bus_params_pkg::BUS_AW-1:0] scr_addr;
logic [38:0] rdata = '0;
logic rdata_arr [] = new[39];
logic addr_arr [] = new[addr_width];
logic key_arr [] = new[SRAM_KEY_WIDTH];
logic nonce_arr [] = new[SRAM_BLOCK_WIDTH];
key_arr = {<<{key}};
nonce_arr = {<<{nonce}};
for (int i = 0; i < addr_width; i++) begin
bus_addr[addr_lsb + i] = scrambled_addr[i];
addr_arr[i] = addr[addr_lsb + i];
end
// Write the scrambled data to memory
write39integ(bus_addr, scrambled_data);
// Calculate the scrambled address
scr_addr = get_sram_encrypt_addr(addr, nonce);
// Read memory and return the decrypted data
rdata = read39integ(scr_addr);
`uvm_info(`gfn, $sformatf("scr data: 0x%0x", rdata), UVM_HIGH)
rdata_arr = {<<{rdata}};
rdata_arr = sram_scrambler_pkg::decrypt_sram_data(
rdata_arr, 39, 39, addr_arr, addr_width, key_arr, nonce_arr
);
rdata = {<<{rdata_arr}};
// Only return the data payload without ECC bits.
return rdata[31:0];
endfunction
virtual function void sram_encrypt_write64(logic [bus_params_pkg::BUS_AW-1:0] addr,
logic [63:0] data,
logic [SRAM_KEY_WIDTH-1:0] key,
logic [SRAM_BLOCK_WIDTH-1:0] nonce);
logic [bus_params_pkg::BUS_AW-1:0] bus_addr = '0;
logic [63:0] scrambled_data;
virtual function void sram_encrypt_write32_integ(logic [bus_params_pkg::BUS_AW-1:0] addr,
logic [31:0] data,
logic [SRAM_KEY_WIDTH-1:0] key,
logic [SRAM_BLOCK_WIDTH-1:0] nonce,
bit [38:0] flip_bits = 0);
logic [bus_params_pkg::BUS_AW-1:0] scr_addr;
logic [38:0] integ_data;
logic [38:0] scrambled_data;
logic wdata_arr [] = new[64];
logic scrambled_addr [] = new[addr_width];
logic sram_addr [] = new[addr_width];
logic wdata_arr [] = new[39];
logic addr_arr [] = new[addr_width];
logic key_arr [] = new[SRAM_KEY_WIDTH];
logic nonce_arr [] = new[SRAM_BLOCK_WIDTH];
@ -397,27 +126,25 @@ virtual function void sram_encrypt_write64(logic [bus_params_pkg::BUS_AW-1:0] ad
nonce_arr = {<<{nonce}};
for (int i = 0; i < addr_width; i++) begin
sram_addr[i] = addr[addr_lsb + i];
addr_arr[i] = addr[addr_lsb + i];
end
// Calculate the scrambled address
scrambled_addr = sram_scrambler_pkg::encrypt_sram_addr(sram_addr, addr_width, nonce_arr);
scr_addr = get_sram_encrypt_addr(addr, nonce);
// Calculate the integrity constant
integ_data = prim_secded_pkg::prim_secded_inv_39_32_enc(data);
// flip some bits to inject integrity fault
integ_data ^= flip_bits;
// Calculate the scrambled data
wdata_arr = {<<{data}};
wdata_arr = {<<{integ_data}};
wdata_arr = sram_scrambler_pkg::encrypt_sram_data(
wdata_arr, 64, 8, sram_addr, addr_width, key_arr, nonce_arr
wdata_arr, 39, 39, addr_arr, addr_width, key_arr, nonce_arr
);
scrambled_data = {<<{wdata_arr}};
// Construct bus representation of the address
for (int i = 0; i < addr_lsb; i++) begin
bus_addr[i] = addr[i];
end
for (int i = 0; i < addr_width; i++) begin
bus_addr[addr_lsb + i] = scrambled_addr[i];
end
// Write the scrambled data to memory
write64(bus_addr, scrambled_data);
write39integ(scr_addr, scrambled_data);
endfunction

View file

@ -16,16 +16,16 @@ package mem_bkdr_util_pkg;
// Represents the various forms of error detection / correction supported.
typedef enum int {
ErrDetectionNone = prim_secded_pkg::SecdedNone,
Ecc_22_16 = prim_secded_pkg::Secded_22_16,
Ecc_28_22 = prim_secded_pkg::Secded_28_22,
Ecc_39_32 = prim_secded_pkg::Secded_39_32,
Ecc_64_57 = prim_secded_pkg::Secded_64_57,
Ecc_72_64 = prim_secded_pkg::Secded_72_64,
EccHamming_22_16 = prim_secded_pkg::SecdedHamming_22_16,
EccHamming_39_32 = prim_secded_pkg::SecdedHamming_39_32,
EccHamming_72_64 = prim_secded_pkg::SecdedHamming_72_64,
EccHamming_76_68 = prim_secded_pkg::SecdedHamming_76_68,
ErrDetectionNone = prim_secded_pkg::SecdedNone,
Ecc_22_16 = prim_secded_pkg::Secded_22_16,
Ecc_28_22 = prim_secded_pkg::Secded_28_22,
Ecc_39_32 = prim_secded_pkg::Secded_39_32,
Ecc_64_57 = prim_secded_pkg::Secded_64_57,
Ecc_72_64 = prim_secded_pkg::Secded_72_64,
EccHamming_22_16 = prim_secded_pkg::SecdedHamming_22_16,
EccHamming_39_32 = prim_secded_pkg::SecdedHamming_39_32,
EccHamming_72_64 = prim_secded_pkg::SecdedHamming_72_64,
EccHamming_76_68 = prim_secded_pkg::SecdedHamming_76_68,
EccInv_22_16 = prim_secded_pkg::SecdedInv_22_16,
EccInv_28_22 = prim_secded_pkg::SecdedInv_28_22,
EccInv_39_32 = prim_secded_pkg::SecdedInv_39_32,

View file

@ -206,12 +206,34 @@ package sram_scrambler_pkg;
for (int i = 0; i < addr_width; i++) begin
nonce[i] = full_nonce[SRAM_BLOCK_WIDTH - addr_width + i];
end
encrypted_addr = sp_encrypt(addr, addr_width, nonce);
return encrypted_addr;
endfunction : encrypt_sram_addr
// Deccrypts the target SRAM address using the custom S&P network.
function automatic state_t decrypt_sram_addr(logic addr[], int addr_width,
logic full_nonce[]);
logic nonce[] = new[addr_width];
logic encrypted_addr[] = new[addr_width];
// The address encryption nonce is the same width as the address,
// and is constructed from the top addr_width bits of the full nonce.
//
// `with` syntax is currently unsupported by Verible,
// uncomment once support has been added
//
// nonce = {>> {full_nonce with [SRAM_BLOCK_WIDTH - addr_width +: addr_width]}};
for (int i = 0; i < addr_width; i++) begin
nonce[i] = full_nonce[SRAM_BLOCK_WIDTH - addr_width + i];
end
encrypted_addr = sp_decrypt(addr, addr_width, nonce);
return encrypted_addr;
endfunction : decrypt_sram_addr
// SRAM data encryption is more involved, we need to run 2 rounds of PRINCE on the nonce and key
// and then XOR the result with the data.
//

View file

@ -38,13 +38,13 @@ class mem_model #(int AddrWidth = bus_params_pkg::BUS_AW,
endfunction
function void write_byte(mem_addr_t addr, bit [7:0] data);
`uvm_info(`gfn, $sformatf("Write Mem : Addr[0x%0h], Data[0x%0h]", addr, data), UVM_MEDIUM)
`uvm_info(`gfn, $sformatf("Write Mem : Addr[0x%0h], Data[0x%0h]", addr, data), UVM_HIGH)
system_memory[addr] = data;
endfunction
function void compare_byte(mem_addr_t addr, bit [7:0] act_data);
`uvm_info(`gfn, $sformatf("Compare Mem : Addr[0x%0h], Act Data[0x%0h], Exp Data[0x%0h]",
addr, act_data, system_memory[addr]), UVM_MEDIUM)
addr, act_data, system_memory[addr]), UVM_HIGH)
`DV_CHECK_EQ(act_data, system_memory[addr], $sformatf("addr 0x%0h read out mismatch", addr))
endfunction

View file

@ -17,6 +17,11 @@
is_sim_mode: 1
en_build_modes: ["{tool}_waves"]
}
{
name: waves_off
is_sim_mode: 1
en_build_modes: ["{tool}_waves_off"]
}
{
name: cov
is_sim_mode: 1

View file

@ -113,6 +113,11 @@
// Dump unpacked structs and arrays.
"-dump-agg"]
}
{
name: dsim_waves_off
is_sim_mode: 1
build_opts: []
}
// TODO: support coverage mode
// Note: no specific build or run options are required for dsim to produce functional
// coverage. Code coverage support is evolving.

View file

@ -73,6 +73,11 @@
name: riviera_waves
is_sim_mode: 1
}
{
name: riviera_waves_off
is_sim_mode: 1
build_opts: []
}
// TODO support coverage for riviera
{
name: riviera_cov

View file

@ -80,6 +80,38 @@
milestone: V1
tests: ["{name}{intf}_csr_mem_rw_with_rand_reset"]
}
{
name: regwen_csr_and_corresponding_lockable_csr
desc: '''
Verify regwen CSR and its corresponding lockable CSRs.
- Randomly access all CSRs
- Test when regwen CSR is set, its corresponding lockable CSRs become
read-only registers
Note:
- If regwen CSR is HW read-only, this feature can be fully tested by common
CSR tests - csr_rw and csr_aliasing.
- If regwen CSR is HW updated, a separate test should be created to test it.
This is only applicable if the block contains regwen and locakable CSRs.
'''
milestone: V1
tests: ["{name}{intf}_csr_rw", "{name}{intf}_csr_aliasing"]
}
]
covergroups: [
{
name: regwen_val_when_new_value_written_cg
desc: '''
Cover each lockable reg field with these 2 cases:
- When regwen = 1, a different value is written to the lockable CSR field, and a read
occurs after that.
- When regwen = 0, a different value is written to the lockable CSR field, and a read
occurs after that.
This is only applicable if the block contains regwen and locakable CSRs.
'''
}
]
}

View file

@ -1,19 +0,0 @@
// Copyright lowRISC contributors.
// Licensed under the Apache License, Version 2.0, see LICENSE for details.
// SPDX-License-Identifier: Apache-2.0
{
testpoints: [
{
name: enable_reg
desc: '''
The CSR test sequences will read and write accessible CSRs including the enable
registers and their locked registers. The RAL model supports predicting the correct
value of the locked registers based on their enable registers.
'''
milestone: V2
tests: ["{name}{intf}_csr_rw",
"{name}{intf}_csr_bit_bash",
"{name}{intf}_csr_aliasing"]
}
]
}

View file

@ -0,0 +1,24 @@
// Copyright lowRISC contributors.
// Licensed under the Apache License, Version 2.0, see LICENSE for details.
// SPDX-License-Identifier: Apache-2.0
{
testpoints: [
{
name: passthru_mem_tl_intg_err
desc: '''Verify data integrity is stored in the passthru memory rather than generated after
a read.
- Randomly read a memory location and check the data integrity is correct.
- Backdoor inject fault into this location.
- Check the data integrity is incorrect but there is no d_error as the memory block
should just pass the stored data and integrity to the processor where the
integrity is compared.
- Above sequences will be run with `csr_rw_vseq` to ensure it won't affect CSR
accesses.
'''
milestone: V2S
tests: ["{name}_passthru_mem_tl_intg_err"]
}
]
}

View file

@ -0,0 +1,29 @@
// Copyright lowRISC contributors.
// Licensed under the Apache License, Version 2.0, see LICENSE for details.
// SPDX-License-Identifier: Apache-2.0
{
testpoints: [
{
name: prim_double_lfsr_check
desc: ''' Verify that violating prim_double_lfsr LFSR properties generate a fatal alert.
Stimulus:
- At the falling edge (non-active edge), force one of the LFSR to a different value than
the other's.
- Randomly force the LFSR back to a normal value to ensure the error is latched and
won't go away until reset.
- Within the next few cycles, the violation of hardened LFSR property should
generate a fatal alert.
- Repeat for ALL prim_double_lfsr instances in the DUT.
Checks:
- Check that fatal alert is triggered.
- Check that err_code/fault_status is updated correctly and preserved until reset.
- Verify any operations that follow fail (as applicable).
'''
milestone: V2S
tests: ["{name}_sec_cm"]
}
]
}

View file

@ -15,7 +15,7 @@
- Verify the update_error status register field is set to 1.
- Repeat the above steps a bunch of times.
'''
milestone: V1
milestone: V2S
tests: ["{name}_shadow_reg_errors"]
}
@ -31,7 +31,7 @@
- Verify the update_error status register field remains the same value.
- Repeat the above steps a bunch of times.
'''
milestone: V1
milestone: V2S
tests: ["{name}_shadow_reg_errors"]
}
@ -48,7 +48,7 @@
- Read all CSRs to ensure the DUT is properly reset.
- Repeat the above steps a bunch of times.
'''
milestone: V1
milestone: V2S
tests: ["{name}_shadow_reg_errors"]
}
@ -65,7 +65,7 @@
- Read all CSRs to ensure the DUT is properly reset.
- Repeat the above steps a bunch of times.
'''
milestone: V1
milestone: V2S
tests: ["{name}_shadow_reg_errors"]
}
@ -79,7 +79,7 @@
shadowed registers' write/read to be executed without aborting.
- Repeat the above steps a bunch of times.
'''
milestone: V1
milestone: V2S
tests: ["{name}_shadow_reg_errors_with_csr_rw"]
}
]

View file

@ -0,0 +1,20 @@
// Copyright lowRISC contributors.
// Licensed under the Apache License, Version 2.0, see LICENSE for details.
// SPDX-License-Identifier: Apache-2.0
{
build_modes: [
{
name: cover_reg_top
}
]
tests: [
{
name: "{name}_passthru_mem_tl_intg_err"
build_mode: "cover_reg_top"
uvm_test_seq: "{name}_common_vseq"
run_opts: ["+run_passthru_mem_tl_intg_err"]
reseed: 20
}
]
}

View file

@ -94,6 +94,10 @@
"-error=TMPO",
// Class objects must not hide other class members due to same name
"-error=SV-OHCM",
// ENUMASSIGN issues can cause LEC warnings later down the road (see #10083
// and #10952 for context). The warning is therefore promoted to an error
// in simulations in order to catch this as early as possible.
"-error=ENUMASSIGN"
]
run_opts: ["-licqueue",
@ -253,6 +257,12 @@
"-kdb",
"-debug_access"]
}
{
name: vcs_waves_off
is_sim_mode: 1
build_opts: [// disable dumping assertion failures to improve runtime performance
"-assert novpi+dbgopt"]
}
{
name: vcs_cov
is_sim_mode: 1
@ -274,7 +284,10 @@
// Ignore warnings about not applying cm_glitch to path and FSM
"+warn=noVCM-OPTIGN",
// Coverage database output location
"-cm_dir {cov_db_dir}"]
"-cm_dir {cov_db_dir}",
// The following option is to improve runtime performance
"-Xkeyopt=rtopt"
]
run_opts: [// Enable the required cov metrics
"-cm {cov_metrics}",

View file

@ -35,6 +35,14 @@
"-nowarn DSEMEL",
// Ignore hierarchial ref warnings in interfaces
"-nowarn CUVIHR",
// Ignore warning "Potentially reversed dist range may cause it to be treated as
// empty". This is thrown when and expression like `var - 1` is added to a
// randomization distribution, which can potentially be a negative value.
"-nowarn REVRNG",
// Ignore warning "Include directory <path> given but not used". This is benign.
"-nowarn SPDUSD",
// Needed for including "secded_enc.h".
"-I{build_dir}/src/lowrisc_dv_secded_enc_0",
]
run_opts: ["-input {run_script}",
@ -171,6 +179,11 @@
is_sim_mode: 1
build_opts: ["-access +c"]
}
{
name: xcelium_waves_off
is_sim_mode: 1
build_opts: []
}
{
name: xcelium_cov
is_sim_mode: 1

View file

@ -11,6 +11,9 @@
-node tb.dut *tl_o.d_opcode[1]
-node tb.dut *tl_o.d_opcode[2]
-node tb.dut *tl_o.d_sink
// [UNR] due to the ECC logics
-node tb.dut *tl_o.d_user.rsp_intg[6]
// [LOW_RISK] Verified in prim_alert_receiver TB."
-node tb.dut *alert_rx_*.ping_*
// [LOW_RISK] Verified in prim_alert_sender/receiver TB."
-node tb.dut* *alert_rx_i*.ping_p
-node tb.dut* *alert_rx_i*.ping_n

View file

@ -55,3 +55,6 @@ set_toggle_portsonly
// Enable scoring of FSM arcs (state transitions).
set_fsm_arc_scoring
// Include X->1|0 for toggle coverage collection. #10332
set_toggle_includex

View file

@ -6,4 +6,5 @@ exclude -inst $::env(DUT_TOP) -toggle '*tl_i.a_user.rsvd' -comment "\[UNSUPPORTE
exclude -inst $::env(DUT_TOP) -toggle '*tl_i.a_param' -comment "\[UNSUPPORTED\] Based on our Comportability Spec. Exercising this will result in assertion errors thrown."
exclude -inst $::env(DUT_TOP) -toggle '*tl_o.d_param' -comment "\[UNR\] Follows tl_i.a_param which is unsupported."
exclude -inst $::env(DUT_TOP) -toggle '*tl_o.d_sink' -comment "\[UNR\] Based on our Comportability Spec."
exclude -inst $::env(DUT_TOP) -toggle '*tl_o.d_user.rsp_intg'\[6\] -comment "\[UNR\] Due to the ECC logics"
exclude -inst $::env(DUT_TOP) -toggle '*alert_rx_*.ping_*' -comment "\[LOW_RISK\] Verified in prim_alert_receiver TB."

View file

@ -19,3 +19,8 @@ set cov_merge_db_dir [string trim $::env(cov_merge_db_dir) " \"'"]
# Run the merge command.
merge $cov_db_dirs -out $cov_merge_db_dir -overwrite
# Create a file with the path to the cover dirs
set filepointer [open "$cov_merge_db_dir/runs.txt" w]
puts $filepointer "$cov_db_dirs"
close $filepointer

View file

@ -47,3 +47,6 @@ report_metrics \
-assertionStatus \
-allAssertionCounters \
-all
# rank the test runs
rank -runfile $cov_merge_db_dir/runs.txt -html -out $cov_report_dir/grading

View file

@ -52,6 +52,37 @@ Ecc32MemArea::EccWords Ecc32MemArea::ReadWithIntegrity(
return ret;
}
void Ecc32MemArea::WriteWithIntegrity(uint32_t word_offset,
const EccWords &data) const {
// See MemArea::Write for an explanation for this buffer.
uint8_t minibuf[SV_MEM_WIDTH_BYTES];
memset(minibuf, 0, sizeof minibuf);
assert(width_byte_ <= sizeof minibuf);
uint32_t width_32 = width_byte_ / 4;
uint32_t to_write = data.size() / width_32;
assert((data.size() % width_32) == 0);
assert(word_offset + to_write <= num_words_);
for (uint32_t i = 0; i < to_write; ++i) {
uint32_t dst_word = word_offset + i;
uint32_t phys_addr = ToPhysAddr(dst_word);
WriteBufferWithIntegrity(minibuf, data, i * width_32, dst_word);
WriteFromMinibuf(phys_addr, minibuf, dst_word);
}
}
// Zero enough of the buffer to fill it with a word using insert_bits
static void zero_buffer(uint8_t buf[SV_MEM_WIDTH_BYTES], uint32_t width_byte) {
// The insert_bits routine assumes that the buffer will have been zeroed, so
// do that here. Note that this buffer has (width_byte / 4) words, each of
// which is 39 bits long. Divide this by 8, rounding up.
size_t phys_size_bytes = (39 * (width_byte / 4) + 7) / 8;
memset(buf, 0, phys_size_bytes);
}
// Add bits to buf at bit_idx
//
// buf is assumed to be little-endian, so bit_idx 0 will refer to the bottom
@ -83,6 +114,16 @@ static void insert_bits(uint8_t *buf, unsigned bit_idx, uint8_t new_bits,
}
}
// Add 4 bytes to buf from bytes at bit_idx, plus check bits
static void insert_word(uint8_t *buf, unsigned bit_idx, const uint8_t *bytes,
uint8_t check_bits) {
assert((check_bits >> 7) == 0);
for (int i = 0; i < 4; ++i) {
insert_bits(buf, bit_idx + 8 * i, bytes[i], 8);
}
insert_bits(buf, bit_idx + 8 * 4, check_bits, 7);
}
// Extract bits from buf at bit_idx
static uint8_t extract_bits(const uint8_t *buf, unsigned bit_idx,
unsigned count) {
@ -115,26 +156,40 @@ static uint8_t extract_bits(const uint8_t *buf, unsigned bit_idx,
void Ecc32MemArea::WriteBuffer(uint8_t buf[SV_MEM_WIDTH_BYTES],
const std::vector<uint8_t> &data,
size_t start_idx, uint32_t dst_word) const {
// The insert_bits routine assumes that the buffer will have been zeroed, so
// do that here. Note that this buffer has (width_byte_ / 4) words, each of
// which is 39 bits long. Divide this by 8, rounding up.
size_t phys_size_bytes = (39 * (width_byte_ / 4) + 7) / 8;
memset(buf, 0, phys_size_bytes);
zero_buffer(buf, width_byte_);
for (uint32_t i = 0; i < width_byte_ / 4; ++i) {
const uint8_t *src_data = &data[start_idx + 4 * i];
insert_word(buf, 39 * i, src_data, enc_secded_inv_39_32(src_data));
}
}
for (int i = 0; i < width_byte_ / 4; ++i) {
uint8_t check_bits = enc_secded_39_32(&data[start_idx + 4 * i]);
for (int j = 0; j < 4; ++j) {
insert_bits(buf, 39 * i + 8 * j, data[start_idx + 4 * i + j], 8);
void Ecc32MemArea::WriteBufferWithIntegrity(uint8_t buf[SV_MEM_WIDTH_BYTES],
const EccWords &data,
size_t start_idx,
uint32_t dst_word) const {
uint8_t src_data[4];
zero_buffer(buf, width_byte_);
for (uint32_t i = 0; i < width_byte_ / 4; ++i) {
const EccWord &word = data[start_idx + i];
for (uint32_t j = 0; j < 4; ++j) {
src_data[j] = (word.second >> 8 * j) & 0xff;
}
insert_bits(buf, 39 * i + 32, check_bits, 7);
uint8_t check_bits = enc_secded_inv_39_32(src_data);
// Invert (and thus corrupt) check bits if needed
if (!word.first)
check_bits ^= 0x7f;
insert_word(buf, 39 * i, src_data, check_bits);
}
}
void Ecc32MemArea::ReadBuffer(std::vector<uint8_t> &data,
const uint8_t buf[SV_MEM_WIDTH_BYTES],
uint32_t src_word) const {
for (int i = 0; i < width_byte_ / 4; ++i) {
for (int j = 0; j < 4; ++j) {
for (uint32_t i = 0; i < width_byte_ / 4; ++i) {
for (uint32_t j = 0; j < 4; ++j) {
data.push_back(extract_bits(buf, 39 * i + 8 * j, 8));
}
}
@ -143,16 +198,16 @@ void Ecc32MemArea::ReadBuffer(std::vector<uint8_t> &data,
void Ecc32MemArea::ReadBufferWithIntegrity(
EccWords &data, const uint8_t buf[SV_MEM_WIDTH_BYTES],
uint32_t src_word) const {
for (int i = 0; i < width_byte_ / 4; ++i) {
for (uint32_t i = 0; i < width_byte_ / 4; ++i) {
uint8_t buf32[4];
uint32_t w32 = 0;
for (int j = 0; j < 4; ++j) {
for (uint32_t j = 0; j < 4; ++j) {
uint8_t byte = extract_bits(buf, 39 * i + 8 * j, 8);
buf32[j] = byte;
w32 |= (uint32_t)byte << 8 * j;
}
uint8_t exp_check_bits = enc_secded_39_32(buf32);
uint8_t exp_check_bits = enc_secded_inv_39_32(buf32);
uint8_t check_bits = extract_bits(buf, 39 * i + 32, 7);
bool good = check_bits == exp_check_bits;

View file

@ -40,6 +40,19 @@ class Ecc32MemArea : public MemArea {
*/
EccWords ReadWithIntegrity(uint32_t word_offset, uint32_t num_words) const;
/** Write data with validity bits, starting at the given offset
*
* This is equivalent to MemArea's Write method, but takes a vector of pairs
* with a bit saying whether the integrity bits for the word are valid or
* not.
*
* @param word_offset The offset, in words, of the first word that should be
* written.
*
* @param data The data that should be written.
*/
void WriteWithIntegrity(uint32_t word_offset, const EccWords &data) const;
protected:
void WriteBuffer(uint8_t buf[SV_MEM_WIDTH_BYTES],
const std::vector<uint8_t> &data, size_t start_idx,
@ -62,6 +75,20 @@ class Ecc32MemArea : public MemArea {
virtual void ReadBufferWithIntegrity(EccWords &data,
const uint8_t buf[SV_MEM_WIDTH_BYTES],
uint32_t src_word) const;
/** Insert a memory word into buf from one or more 32-bit words in data.
*
* @param buf Destination buffer (physical memory bits)
*
* @param data Source data, from which the 32-bit words should be read
*
* @param start_idx The index of the first 32-bit word in data to read
*
* @param dst_word Logical address of the location being written
*/
virtual void WriteBufferWithIntegrity(uint8_t buf[SV_MEM_WIDTH_BYTES],
const EccWords &data, size_t start_idx,
uint32_t dst_word) const;
};
#endif // OPENTITAN_HW_DV_VERILATOR_CPP_ECC32_MEM_AREA_H_

View file

@ -46,19 +46,7 @@ void MemArea::Write(uint32_t word_offset,
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_ << ".";
throw std::runtime_error(oss.str());
}
WriteFromMinibuf(phys_addr, minibuf, dst_word);
}
}
@ -122,3 +110,14 @@ void MemArea::ReadToMinibuf(uint8_t *minibuf, uint32_t phys_addr) const {
throw std::runtime_error(oss.str());
}
}
void MemArea::WriteFromMinibuf(uint32_t phys_addr, const uint8_t *minibuf,
uint32_t dst_word) const {
SVScoped scoped(scope_);
if (!simutil_set_mem(phys_addr, (const svBitVecVal *)minibuf)) {
std::ostringstream oss;
oss << "Could not set memory at byte offset 0x" << std::hex
<< dst_word * width_byte_ << ".";
throw std::runtime_error(oss.str());
}
}

View file

@ -49,7 +49,7 @@ class MemArea {
* written.
*
* @param data The data that should be written. If the length is not a
* multiple of \p word_offset, the last word will be
* multiple of \p width_byte, the last word will be
* zero-extended.
*/
virtual void Write(uint32_t word_offset,
@ -137,6 +137,14 @@ class MemArea {
* implementation of MemArea::Write() for the details.
*/
void ReadToMinibuf(uint8_t *minibuf, uint32_t phys_addr) const;
/** Write from minibuf to the memory word at phys_addr
*
* minibuf should be at least SV_MEM_WIDTH_BYTES in size. See the
* implementation of MemArea::Write() for the details.
*/
void WriteFromMinibuf(uint32_t phys_addr, const uint8_t *minibuf,
uint32_t dst_word) const;
};
#endif // OPENTITAN_HW_DV_VERILATOR_CPP_MEM_AREA_H_

View file

@ -23,7 +23,7 @@ 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) {
for (uint32_t i = 0; i < addr_width_bytes; ++i) {
addr_bytes[i] = addr & 0xff;
addr >>= 8;
}
@ -37,8 +37,8 @@ static uint32_t AddrBytesToInt(const std::vector<uint8_t> &addr) {
uint32_t addr_out = 0;
int cur_shift = 0;
for (int i = 0; i < addr.size(); ++i) {
addr_out |= (addr[i] << cur_shift);
for (uint8_t byte : addr) {
addr_out |= ((uint32_t)byte << cur_shift);
cur_shift += 8;
}
@ -50,7 +50,7 @@ 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) {
for (uint32_t i = 0; i < bytes; ++i) {
vec[i] = (sv_val[i / 4] >> shift) & 0xff;
shift += 8;
@ -81,27 +81,12 @@ static uint32_t vbits(uint32_t size) {
return width;
}
// These functions come from SV code, exposed over DPI. They are defined inside
// a module (prim_ram1p_scr) and, awkwardly, if a design doesn't happen to use
// that module then some simulators (Verilator!) will discard it, together with
// the DPI functions.
//
// We'd like to be able to use the memutil_dpi_scrambled.core whether or not we
// actually instantiated prim_ram1p_scr: we'll just spit out an error if we
// call GetScrambleKey() or GetScrambleNonce() if we didn't instantiate it. To
// make this work, we mark both symbols weak.
extern "C" {
int __attribute__((weak)) simutil_get_scramble_key(svBitVecVal *key);
int __attribute__((weak)) simutil_get_scramble_nonce(svBitVecVal *nonce);
int simutil_get_scramble_key(svBitVecVal *key);
int simutil_get_scramble_nonce(svBitVecVal *nonce);
}
std::vector<uint8_t> ScrambledEcc32MemArea::GetScrambleKey() const {
if (!simutil_get_scramble_key) {
throw std::runtime_error(
"No definition of simutil_get_scramble_key. "
"Does the design actually use prim_ram1p_scr?");
}
SVScoped scoped(scr_scope_);
svBitVecVal key_minibuf[((kPrinceWidthByte * 2) + 3) / 4];
@ -117,12 +102,6 @@ std::vector<uint8_t> ScrambledEcc32MemArea::GetScrambleKey() const {
std::vector<uint8_t> ScrambledEcc32MemArea::GetScrambleNonce() const {
assert(GetNonceWidthByte() <= kScrMaxNonceWidthByte);
if (!simutil_get_scramble_nonce) {
throw std::runtime_error(
"No definition of simutil_get_scramble_nonce. "
"Does the design actually use prim_ram1p_scr?");
}
SVScoped scoped(scr_scope_);
svBitVecVal nonce_minibuf[(kScrMaxNonceWidthByte + 3) / 4];
@ -177,17 +156,7 @@ void ScrambledEcc32MemArea::WriteBuffer(uint8_t buf[SV_MEM_WIDTH_BYTES],
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]);
ScrambleBuffer(buf, dst_word);
}
std::vector<uint8_t> ScrambledEcc32MemArea::ReadUnscrambled(
@ -210,10 +179,29 @@ void ScrambledEcc32MemArea::ReadBufferWithIntegrity(
EccWords &data, const uint8_t buf[SV_MEM_WIDTH_BYTES],
uint32_t src_word) const {
std::vector<uint8_t> unscrambled_data = ReadUnscrambled(buf, src_word);
// Strip integrity to give final result
Ecc32MemArea::ReadBufferWithIntegrity(data, &unscrambled_data[0], src_word);
}
void ScrambledEcc32MemArea::WriteBufferWithIntegrity(
uint8_t buf[SV_MEM_WIDTH_BYTES], const EccWords &data, size_t start_idx,
uint32_t dst_word) const {
Ecc32MemArea::WriteBufferWithIntegrity(buf, data, start_idx, dst_word);
ScrambleBuffer(buf, dst_word);
}
void ScrambledEcc32MemArea::ScrambleBuffer(uint8_t buf[SV_MEM_WIDTH_BYTES],
uint32_t dst_word) const {
std::vector<uint8_t> scramble_buf(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]);
}
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_),

View file

@ -48,6 +48,12 @@ class ScrambledEcc32MemArea : public Ecc32MemArea {
const uint8_t buf[SV_MEM_WIDTH_BYTES],
uint32_t src_word) const override;
void WriteBufferWithIntegrity(uint8_t buf[SV_MEM_WIDTH_BYTES],
const EccWords &data, size_t start_idx,
uint32_t dst_word) const override;
void ScrambleBuffer(uint8_t buf[SV_MEM_WIDTH_BYTES], uint32_t dst_word) const;
uint32_t ToPhysAddr(uint32_t logical_addr) const override;
uint32_t GetPhysWidth() const;