mirror of
https://github.com/lowRISC/ibex.git
synced 2025-04-24 22:07:43 -04:00
Update lowrisc_ip to lowRISC/opentitan@7c4f8b3fd
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:
parent
e6eb4fb11d
commit
15da12dfd6
160 changed files with 3018 additions and 1412 deletions
|
@ -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;
|
||||
|
|
84
vendor/lowrisc_ip/dv/sv/csr_utils/csr_seq_lib.sv
vendored
84
vendor/lowrisc_ip/dv/sv/csr_utils/csr_seq_lib.sv
vendored
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
50
vendor/lowrisc_ip/dv/sv/dv_base_reg/dv_base_lockable_field_cov.sv
vendored
Normal file
50
vendor/lowrisc_ip/dv/sv/dv_base_reg/dv_base_lockable_field_cov.sv
vendored
Normal 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
|
|
@ -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="");
|
||||
|
|
88
vendor/lowrisc_ip/dv/sv/dv_base_reg/dv_base_mubi_cov.sv
vendored
Normal file
88
vendor/lowrisc_ip/dv/sv/dv_base_reg/dv_base_mubi_cov.sv
vendored
Normal 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
|
||||
|
||||
|
|
@ -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:
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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"
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
51
vendor/lowrisc_ip/dv/sv/dv_lib/dv_base_vseq.sv
vendored
51
vendor/lowrisc_ip/dv/sv/dv_lib/dv_base_vseq.sv
vendored
|
@ -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);
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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.
|
||||
//
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
5
vendor/lowrisc_ip/dv/tools/dvsim/dsim.hjson
vendored
5
vendor/lowrisc_ip/dv/tools/dvsim/dsim.hjson
vendored
|
@ -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.
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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.
|
||||
'''
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
|
|
|
@ -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"]
|
||||
}
|
||||
]
|
||||
}
|
24
vendor/lowrisc_ip/dv/tools/dvsim/testplans/passthru_mem_intg_testplan.hjson
vendored
Normal file
24
vendor/lowrisc_ip/dv/tools/dvsim/testplans/passthru_mem_intg_testplan.hjson
vendored
Normal 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"]
|
||||
}
|
||||
]
|
||||
}
|
||||
|
29
vendor/lowrisc_ip/dv/tools/dvsim/testplans/sec_cm_double_lfsr_testplan.hjson
vendored
Normal file
29
vendor/lowrisc_ip/dv/tools/dvsim/testplans/sec_cm_double_lfsr_testplan.hjson
vendored
Normal 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"]
|
||||
}
|
||||
]
|
||||
}
|
||||
|
|
@ -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"]
|
||||
}
|
||||
]
|
||||
|
|
20
vendor/lowrisc_ip/dv/tools/dvsim/tests/passthru_mem_intg_tests.hjson
vendored
Normal file
20
vendor/lowrisc_ip/dv/tools/dvsim/tests/passthru_mem_intg_tests.hjson
vendored
Normal 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
|
||||
}
|
||||
]
|
||||
}
|
15
vendor/lowrisc_ip/dv/tools/dvsim/vcs.hjson
vendored
15
vendor/lowrisc_ip/dv/tools/dvsim/vcs.hjson
vendored
|
@ -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}",
|
||||
|
|
13
vendor/lowrisc_ip/dv/tools/dvsim/xcelium.hjson
vendored
13
vendor/lowrisc_ip/dv/tools/dvsim/xcelium.hjson
vendored
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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."
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
|
|
@ -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_
|
||||
|
|
25
vendor/lowrisc_ip/dv/verilator/cpp/mem_area.cc
vendored
25
vendor/lowrisc_ip/dv/verilator/cpp/mem_area.cc
vendored
|
@ -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());
|
||||
}
|
||||
}
|
||||
|
|
10
vendor/lowrisc_ip/dv/verilator/cpp/mem_area.h
vendored
10
vendor/lowrisc_ip/dv/verilator/cpp/mem_area.h
vendored
|
@ -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_
|
||||
|
|
|
@ -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_),
|
||||
|
|
|
@ -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;
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue