mirror of
https://github.com/lowRISC/ibex.git
synced 2025-04-20 11:57:12 -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
2
vendor/lowrisc_ip.lock.hjson
vendored
2
vendor/lowrisc_ip.lock.hjson
vendored
|
@ -9,6 +9,6 @@
|
|||
upstream:
|
||||
{
|
||||
url: https://github.com/lowRISC/opentitan
|
||||
rev: be1359d27d0e826e28e6611f318c286253cd05f1
|
||||
rev: 7c4f8b3fde4bb625ac3330ff52d3f66507190fe5
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -35,15 +35,11 @@
|
|||
}
|
||||
|
||||
{
|
||||
name: prim_alert_init_trigger_test
|
||||
desc: '''Verify init_trigger input from prim_alert_receiver.
|
||||
name: prim_alert_ping_request_test
|
||||
desc: '''Verify ping request from prim_alert_sender.
|
||||
|
||||
Based on the prim_alert_test, this test adds a parallel sequence to randomly drive
|
||||
init_trigger_i in prim_alert_receiver.
|
||||
|
||||
Check if alert sender/receiver pairs can resume normal handshake after init_trigger_i
|
||||
is set.
|
||||
For fatal alert, check if fatal alerts keep firing until reset is issued.
|
||||
- Send a ping request by driving `ping_req` pin to 1.
|
||||
- Verify that `ping_ok` signal is set and ping handshake completes.
|
||||
'''
|
||||
milestone: V1
|
||||
tests: ["prim_async_alert",
|
||||
|
@ -53,15 +49,20 @@
|
|||
}
|
||||
|
||||
{
|
||||
name: prim_alert_ping_request_test
|
||||
desc: '''Verify ping request from prim_alert_sender.
|
||||
name: prim_alert_init_trigger_test
|
||||
desc: '''Verify init_trigger input from prim_alert_receiver.
|
||||
|
||||
- Send a ping request by driving `ping_req` pin to 1.
|
||||
- Verify that `ping_ok` signal is set and ping handshake completes.
|
||||
Because ping reqeust is level trigger, in order to cover toggle coverage for
|
||||
`alert_tx.ping_p/n` the above sequence will run twice.
|
||||
This test is based on previous tests:
|
||||
- Based on the prim_alert_request_test, this test adds a parallel sequence to randomly
|
||||
drive `init_trigger_i` in prim_alert_receiver.
|
||||
Check if `alert_ack_o` returns 1 and check if alert sender/receiver pairs can resume
|
||||
normal handshake after alert init finishes.
|
||||
For fatal alert, check if fatal alerts keep firing until reset is issued.
|
||||
- Based on prim_alert_ping_request_test, this test adds a parallel sequence to randomly
|
||||
drive `init_trigger_i` in prim_alert_receiver.
|
||||
Check `ping_ok` returns 1.
|
||||
'''
|
||||
milestone: V1
|
||||
milestone: V2
|
||||
tests: ["prim_async_alert",
|
||||
"prim_async_fatal_alert",
|
||||
"prim_sync_alert",
|
||||
|
@ -90,5 +91,18 @@
|
|||
"prim_sync_fatal_alert"]
|
||||
}
|
||||
|
||||
{
|
||||
name: prim_alert_gate_sender_clk_rst_test
|
||||
desc: '''Gate prim_alert_sender clk or reset during alert_handshake.
|
||||
|
||||
- During alert handshake, gate prim_alert_sender's clock or issue reset.
|
||||
- Verify alert handshake can resume after prim_alert_sender is re-initialized.
|
||||
- If clock was gated, verify alert handshake will resume when clock is active,
|
||||
and verify we will never miss or drop an alert handshake by expecting `alert_ack_o`
|
||||
to return 1 after `alert_req` is sent.
|
||||
'''
|
||||
milestone: V3
|
||||
tests: []
|
||||
}
|
||||
]
|
||||
}
|
||||
|
|
|
@ -144,28 +144,25 @@ module prim_alert_tb;
|
|||
|
||||
// Sequence 1). Alert request sequence.
|
||||
for (int num_trans = 1; num_trans <= 10; num_trans++) begin
|
||||
automatic int rand_wait_init_trig = $urandom_range(2, WaitAlertHandshakeDone + 10);
|
||||
alert_req = 1;
|
||||
fork
|
||||
begin
|
||||
main_clk.wait_clks($urandom_range(MinHandshakeWait, 10));
|
||||
alert_req = 1;
|
||||
`DV_SPINWAIT(wait (alert_ack == 1);, , , "Wait for alert_ack timeout");
|
||||
alert_req = 0;
|
||||
main_clk.wait_clks(WaitAlertHandshakeDone);
|
||||
end
|
||||
begin
|
||||
main_clk.wait_clks(rand_wait_init_trig);
|
||||
init_trig = prim_mubi_pkg::MuBi4True;
|
||||
if ($urandom_range(0, 1)) begin
|
||||
main_clk.wait_clks($urandom_range(MinHandshakeWait, 10));
|
||||
init_trig = prim_mubi_pkg::MuBi4True;
|
||||
main_clk.wait_clks($urandom_range(1, 10));
|
||||
init_trig = prim_mubi_pkg::MuBi4False;
|
||||
main_clk.wait_clks(WaitAlertInitDone);
|
||||
end
|
||||
end
|
||||
join_any
|
||||
disable fork;
|
||||
join
|
||||
|
||||
// Clean up sequence in case alert init or dut init was triggered.
|
||||
main_clk.wait_clks($urandom_range(1, 10));
|
||||
if (init_trig == prim_mubi_pkg::MuBi4True) begin
|
||||
alert_req = 0;
|
||||
init_trig = prim_mubi_pkg::MuBi4False;
|
||||
main_clk.wait_clks(WaitAlertInitDone);
|
||||
end
|
||||
if (IsFatal) begin
|
||||
// For fatal alert, ensure alert keeps firing until reset.
|
||||
// If only alert_init is triggered, alert_sender side still expect fatal alert to fire.
|
||||
|
@ -195,30 +192,26 @@ module prim_alert_tb;
|
|||
$display("[prim_alert_seq] Alert test sequence finished!");
|
||||
|
||||
// Sequence 3) Ping request sequence.
|
||||
// Loop the ping request twice to cover the alert_rx.ping_p/n toggle coverage.
|
||||
for (int i = 0; i < 2; i++) begin
|
||||
int rand_wait_init_trig = $urandom_range(1, WaitAlertHandshakeDone + 10);
|
||||
main_clk.wait_clks($urandom_range(MinHandshakeWait, 10));
|
||||
ping_req = 1;
|
||||
for (int num_trans = 0; num_trans < 10; num_trans++) begin
|
||||
fork
|
||||
begin
|
||||
main_clk.wait_clks($urandom_range(MinHandshakeWait, 10));
|
||||
ping_req = 1;
|
||||
`DV_SPINWAIT(wait (ping_ok == 1);, , , "Wait for ping_ok timeout");
|
||||
ping_req = 0;
|
||||
main_clk.wait_clks(WaitCycle + WaitAlertHandshakeDone);
|
||||
end
|
||||
begin
|
||||
main_clk.wait_clks(rand_wait_init_trig);
|
||||
init_trig = prim_mubi_pkg::MuBi4True;
|
||||
if ($urandom_range(0, 1)) begin
|
||||
main_clk.wait_clks($urandom_range(1, WaitAlertHandshakeDone + 10));
|
||||
init_trig = prim_mubi_pkg::MuBi4True;
|
||||
main_clk.wait_clks($urandom_range(0, 10));
|
||||
init_trig = prim_mubi_pkg::MuBi4False;
|
||||
main_clk.wait_clks(WaitAlertInitDone);
|
||||
end
|
||||
end
|
||||
join_any
|
||||
disable fork;
|
||||
if (init_trig == prim_mubi_pkg::MuBi4True) begin
|
||||
ping_req = 0;
|
||||
main_clk.wait_clks($urandom_range(0, 10));
|
||||
init_trig = prim_mubi_pkg::MuBi4False;
|
||||
main_clk.wait_clks(WaitAlertInitDone);
|
||||
end
|
||||
$display($sformatf("[prim_alert_seq] Ping request sequence[%0d] finished!", i));
|
||||
join
|
||||
$display($sformatf("[prim_alert_seq] Ping request sequence[%0d] finished!", num_trans));
|
||||
end
|
||||
|
||||
// Sequence 4) `Ack_p/n` integrity check sequence.
|
||||
|
|
|
@ -30,7 +30,6 @@
|
|||
tests: [
|
||||
{
|
||||
name: prim_esc_test
|
||||
run_opts: ["+test_timeout_ns=1_000"]
|
||||
}
|
||||
]
|
||||
|
||||
|
|
|
@ -8,12 +8,16 @@
|
|||
// 1). Random reset during escalation handshake sequence.
|
||||
// 2). Escalation request sequence.
|
||||
// 3). Ping request interrupted by escalation request sequence.
|
||||
// 4). `Esc_tx` integrity error sequence.
|
||||
// 4). Integrity error sequence.
|
||||
// 5). Escalation reverse ping timeout sequence.
|
||||
// 6). Escalation receiver counter fail sequence.
|
||||
// 7). Random esc_rx/tx signal without request error sequence.
|
||||
|
||||
module prim_esc_tb;
|
||||
|
||||
import dv_utils_pkg::*;
|
||||
`include "dv_macros.svh"
|
||||
|
||||
//////////////////////////////////////////////////////
|
||||
// config
|
||||
//////////////////////////////////////////////////////
|
||||
|
@ -21,6 +25,9 @@ module prim_esc_tb;
|
|||
localparam time ClkPeriod = 10000;
|
||||
localparam int PING_CNT_DW = 1;
|
||||
localparam int TIMEOUT_CYCLES = 1 << (PING_CNT_DW + 6);
|
||||
localparam string MSG_ID = $sformatf("%m");
|
||||
|
||||
uint default_spinwait_timeout_ns = 100_000;
|
||||
|
||||
//////////////////////////////////////////////////////
|
||||
// Clock and Reset
|
||||
|
@ -80,6 +87,8 @@ module prim_esc_tb;
|
|||
//////////////////////////////////////////////////////
|
||||
|
||||
initial begin: p_stimuli
|
||||
time delay_ps;
|
||||
|
||||
ping_req = 0;
|
||||
esc_req = 0;
|
||||
main_clk.set_period_ps(ClkPeriod);
|
||||
|
@ -89,11 +98,12 @@ module prim_esc_tb;
|
|||
// Sequence 1. Random reset during escalation handshake sequence.
|
||||
ping_req = 1;
|
||||
// Issue async reset between first and fifth clock cycle to reach FSM coverages.
|
||||
#(($urandom_range(1, ClkPeriod) + $urandom_range(1, 5) * ClkPeriod) * 1ps);
|
||||
`DV_CHECK_STD_RANDOMIZE_WITH_FATAL(delay_ps, delay_ps inside {[1:ClkPeriod]};, , MSG_ID)
|
||||
#((delay_ps + $urandom_range(1, 5) * ClkPeriod) * 1ps);
|
||||
main_clk.apply_reset();
|
||||
ping_req = 0;
|
||||
|
||||
$display("Random reset during escalation handshake sequence finished!");
|
||||
$display("[prim_esc_seq] Random reset during escalation handshake sequence finished!");
|
||||
|
||||
// Sequence 2. Escalation request sequence.
|
||||
esc_req = 1;
|
||||
|
@ -103,45 +113,66 @@ module prim_esc_tb;
|
|||
if (!esc_req_out) test_error("Esc_req did not set esc_req!");
|
||||
esc_req = 0;
|
||||
|
||||
$display("Escalation request sequence finished!");
|
||||
$display("[prim_esc_seq] Escalation request sequence finished!");
|
||||
|
||||
// Sequence 3. Ping request interrupted by escalation request.
|
||||
main_clk.wait_clks($urandom_range(1, 20));
|
||||
main_clk.wait_clks($urandom_range(2, 20));
|
||||
#1ns;
|
||||
ping_req = 1;
|
||||
`DV_SPINWAIT(wait (ping_ok == 1);, , , "Wait for ping_ok timeout");
|
||||
if (integ_fail) test_error("Expect no errors when trigger ping_req");
|
||||
main_clk.wait_clks($urandom_range(2, 20));
|
||||
ping_req = 0;
|
||||
|
||||
main_clk.wait_clks($urandom_range(2, 20));
|
||||
ping_req = 1;
|
||||
// Wait a max of 5 clock cycle to ensure esc_req is send during ping handshake.
|
||||
main_clk.wait_clks($urandom_range(0, 5));
|
||||
esc_req = 1;
|
||||
wait (ping_ok);
|
||||
wait (esc_req_out);
|
||||
main_clk.wait_clks($urandom_range(1, 20));
|
||||
`DV_SPINWAIT(wait (ping_ok == 1);, , , "Wait for ping_ok timeout");
|
||||
`DV_SPINWAIT(wait (esc_req_out == 1);, , , "Wait for esc_req_out timeout");
|
||||
main_clk.wait_clks(1);
|
||||
esc_req = 0;
|
||||
ping_req = 0;
|
||||
if (integ_fail) test_error("Expect no errors when esc_req interrupts ping_req");
|
||||
|
||||
$display("Ping request interrupted by escalation request sequence finished!");
|
||||
$display("[prim_esc_seq] Ping request interrupted by escalation request sequence finished!");
|
||||
|
||||
// Sequence 4.1 `Esc_tx` integrity error sequence during escalation request.
|
||||
// Sequence 4.1 Integrity error sequence during escalation request.
|
||||
main_clk.wait_clks($urandom_range(1, 20));
|
||||
esc_req = 1;
|
||||
// Randomly wait a few clock cycles then inject integrity error.
|
||||
main_clk.wait_clks($urandom_range(0, 5));
|
||||
// Force esc_tx signal to create a integrity fail error case.
|
||||
force esc_tx.esc_n = 1;
|
||||
wait (integ_fail);
|
||||
`DV_SPINWAIT(wait (integ_fail == 1);, , , "Wait for esc_tx.esc_n integ_fail timeout");
|
||||
main_clk.wait_clks(1);
|
||||
release esc_tx.esc_n;
|
||||
// Wait #1ps to avoid a race condition on clock edge.
|
||||
#1ps;
|
||||
if (!esc_req_out) test_error("Signal integrity error should set esc_req!");
|
||||
esc_req = 0;
|
||||
$display("[prim_esc_seq] Integrity esc_tx error sequence finished [1/2]!");
|
||||
|
||||
$display("Escalation esc_p/n integrity sequence during escalation request finished!");
|
||||
main_clk.wait_clks($urandom_range(1, 20));
|
||||
// Force esc_tx signal to create a integrity fail error case.
|
||||
force esc_rx.resp_n = 1;
|
||||
esc_req = 1;
|
||||
`DV_SPINWAIT(wait (integ_fail == 1);, , , "Wait for esc_rx.resp_n integ_fail timeout");
|
||||
main_clk.wait_clks(1);
|
||||
release esc_rx.resp_n;
|
||||
// Wait #1ps to avoid a race condition on clock edge.
|
||||
#1ps;
|
||||
if (!esc_req_out) test_error("Signal integrity error should set esc_req!");
|
||||
esc_req = 0;
|
||||
$display("[prim_esc_seq] Integrity esc_rx error sequence finished [2/2]!");
|
||||
|
||||
// Sequence 4.1 `Esc_tx` integrity error sequence during ping request.
|
||||
main_clk.wait_clks($urandom_range(1, 20));
|
||||
ping_req = 1;
|
||||
// Force esc_tx signal to create a integrity fail error case.
|
||||
force esc_tx.esc_n = 1;
|
||||
wait (integ_fail);
|
||||
`DV_SPINWAIT(wait (integ_fail == 1);, , , "Wait for ping request integ_fail timeout");
|
||||
release esc_tx.esc_n;
|
||||
// Wait #1ps to avoid a race condition on clock edge.
|
||||
#1ps;
|
||||
|
@ -149,7 +180,7 @@ module prim_esc_tb;
|
|||
if (ping_ok) test_error("Ping error!");
|
||||
ping_req = 0;
|
||||
|
||||
$display("Escalation esc_p/n integrity sequence during ping request finished!");
|
||||
$display("[prim_esc_seq] Escalation esc_p/n integrity sequence during ping request finished!");
|
||||
|
||||
// Sequence 5. Escalation reverse ping timeout sequence.
|
||||
// Wait at least two clock cycles for the previous sequence to finish its escalation request.
|
||||
|
@ -165,7 +196,7 @@ module prim_esc_tb;
|
|||
end
|
||||
begin
|
||||
// Wait for a ping handshake to complete.
|
||||
wait (ping_ok);
|
||||
`DV_SPINWAIT(wait (ping_ok == 1);, , , "Wait for ping_ok timeout");
|
||||
main_clk.wait_clks(2);
|
||||
ping_req = 0;
|
||||
if (integ_fail) test_error("Ping_req unexpected signal integrity error!");
|
||||
|
@ -174,21 +205,41 @@ module prim_esc_tb;
|
|||
join
|
||||
main_clk.apply_reset();
|
||||
|
||||
$display("Escalation ping request timeout sequence finished!");
|
||||
$display("[prim_esc_seq] Escalation ping request timeout sequence finished!");
|
||||
|
||||
// Sequence 6. Escalation receiver counter fail sequence.
|
||||
ping_req = 1;
|
||||
// Wait until ping request is acknowledged and counter starts to increment.
|
||||
wait (ping_ok);
|
||||
`DV_SPINWAIT(wait (ping_ok == 1);, , , "Wait for ping_ok timeout");
|
||||
main_clk.wait_clks(2);
|
||||
ping_req = 0;
|
||||
// If cnt_q[0] and cnt_q[1]'s value do not match, deisgn will set `esc_req_out` signal.
|
||||
force prim_esc_tb.i_esc_receiver.cnt_q[1] = 0;
|
||||
wait (esc_req_out);
|
||||
// If duplicate counter values are inconsistent, the design will set the `esc_req_out` signal.
|
||||
force prim_esc_tb.i_esc_receiver.u_prim_count.up_cnt_q[1] = '0;
|
||||
`DV_SPINWAIT(wait (esc_req_out == 1);, , , "Wait for esc_req_out timeout");
|
||||
if (integ_fail) test_error("Escalation receiver counter unexpected signal integrity error!");
|
||||
release prim_esc_tb.i_esc_receiver.cnt_q[1];
|
||||
release prim_esc_tb.i_esc_receiver.u_prim_count.up_cnt_q[1];
|
||||
|
||||
$display("Escalation couter error sequence finished!");
|
||||
$display("[prim_esc_seq] Escalation couter error sequence finished!");
|
||||
|
||||
// 7. Random esc_rx/tx signal without request error sequence.
|
||||
main_clk.wait_clks($urandom_range(1, 20));
|
||||
// Force esc_tx signals to create a integrity fail error case.
|
||||
force esc_rx.resp_p = 1;
|
||||
force esc_rx.resp_n = 0;
|
||||
`DV_SPINWAIT(wait (integ_fail == 1);, , , "Wait for esc_rx.resp_p/n integ_fail timeout");
|
||||
release esc_rx.resp_p;
|
||||
release esc_rx.resp_n;
|
||||
$display("[prim_esc_seq] Random esc_rx error sequence finished [1/2]!");
|
||||
|
||||
main_clk.wait_clks($urandom_range(1, 20));
|
||||
// Force esc_tx signals to create a integrity fail error case.
|
||||
force esc_tx.esc_p = 1;
|
||||
force esc_tx.esc_n = 0;
|
||||
`DV_SPINWAIT(wait (integ_fail == 1);, , , "Wait for esc_rx.resp_p/n integ_fail timeout");
|
||||
release esc_tx.esc_p;
|
||||
release esc_tx.esc_n;
|
||||
$display("[prim_esc_seq] Random esc_tx error sequence finished [1/2]!");
|
||||
main_clk.wait_clks(2);
|
||||
|
||||
dv_test_status_pkg::dv_test_status(.passed(!error));
|
||||
$finish();
|
||||
|
|
|
@ -216,9 +216,12 @@ module prim_lfsr_tb;
|
|||
|
||||
// TODO: perhaps wrap this in a macro?
|
||||
initial begin
|
||||
bit poll_for_stop = 1'b1;
|
||||
int unsigned poll_for_stop_interval_ns = 1000;
|
||||
bit poll_for_stop;
|
||||
int unsigned poll_for_stop_interval_ns;
|
||||
|
||||
poll_for_stop = 1'b1;
|
||||
void'($value$plusargs("poll_for_stop=%0b", poll_for_stop));
|
||||
poll_for_stop_interval_ns = 1000;
|
||||
void'($value$plusargs("poll_for_stop_interval_ns=%0d", poll_for_stop_interval_ns));
|
||||
if (poll_for_stop) dv_utils_pkg::poll_for_stop(.interval_ns(poll_for_stop_interval_ns));
|
||||
end
|
||||
|
|
|
@ -32,6 +32,8 @@ module prim_present_tb;
|
|||
localparam int unsigned KeyWidth = 128;
|
||||
`endif
|
||||
|
||||
localparam string MSG_ID = $sformatf("%m");
|
||||
|
||||
// Max number of rounds according to spec.
|
||||
// We redefine this parameter here to avoid clutter from the long package identifier.
|
||||
localparam int unsigned NumRounds = crypto_dpi_present_pkg::NumRounds;
|
||||
|
@ -206,7 +208,6 @@ module prim_present_tb;
|
|||
|
||||
initial begin : p_stimuli
|
||||
int num_trans;
|
||||
string msg_id = $sformatf("%m");
|
||||
|
||||
// The key and plaintext/ciphertext to be fed into PRESENT instances.
|
||||
bit [KeyWidth-1:0] key;
|
||||
|
@ -238,8 +239,8 @@ module prim_present_tb;
|
|||
void'($value$plusargs("smoke_test=%0b", smoke_test));
|
||||
num_trans = smoke_test ? 1 : $urandom_range(5000, 25000);
|
||||
for (int i = 0; i < num_trans; i++) begin
|
||||
`DV_CHECK_STD_RANDOMIZE_FATAL(plaintext, "", msg_id)
|
||||
`DV_CHECK_STD_RANDOMIZE_FATAL(key, "", msg_id)
|
||||
`DV_CHECK_STD_RANDOMIZE_FATAL(plaintext, "", MSG_ID)
|
||||
`DV_CHECK_STD_RANDOMIZE_FATAL(key, "", MSG_ID)
|
||||
test_present(plaintext, key);
|
||||
end
|
||||
|
||||
|
@ -251,9 +252,12 @@ module prim_present_tb;
|
|||
|
||||
// TODO: perhaps wrap this in a macro?
|
||||
initial begin
|
||||
bit poll_for_stop = 1'b1;
|
||||
int unsigned poll_for_stop_interval_ns = 1000;
|
||||
bit poll_for_stop;
|
||||
int unsigned poll_for_stop_interval_ns;
|
||||
|
||||
poll_for_stop = 1'b1;
|
||||
void'($value$plusargs("poll_for_stop=%0b", poll_for_stop));
|
||||
poll_for_stop_interval_ns = 1000;
|
||||
void'($value$plusargs("poll_for_stop_interval_ns=%0d", poll_for_stop_interval_ns));
|
||||
if (poll_for_stop) dv_utils_pkg::poll_for_stop(.interval_ns(poll_for_stop_interval_ns));
|
||||
end
|
||||
|
|
|
@ -232,7 +232,7 @@ static uint64_t prince_core(const uint64_t core_input, const uint64_t k0_new,
|
|||
PRINCE_PRINT(core_input);
|
||||
PRINCE_PRINT(k1);
|
||||
uint64_t round_input = core_input ^ k1 ^ prince_round_constant(0);
|
||||
for (unsigned int round = 1; round <= num_half_rounds; round++) {
|
||||
for (int round = 1; round <= num_half_rounds; round++) {
|
||||
PRINCE_PRINT(round_input);
|
||||
const uint64_t s_out = prince_s_layer(round_input);
|
||||
PRINCE_PRINT(s_out);
|
||||
|
@ -248,8 +248,7 @@ static uint64_t prince_core(const uint64_t core_input, const uint64_t k0_new,
|
|||
PRINCE_PRINT(m_prime_out);
|
||||
const uint64_t middle_round_s_inv_out = prince_s_inv_layer(m_prime_out);
|
||||
round_input = middle_round_s_inv_out;
|
||||
// for(unsigned int round = 6; round < num_half_rounds * 2 + 1; round ++){
|
||||
for (unsigned int round = 1; round <= num_half_rounds; round++) {
|
||||
for (int round = 1; round <= num_half_rounds; round++) {
|
||||
PRINCE_PRINT(round_input);
|
||||
const uint64_t constant_idx = 10 - num_half_rounds + round;
|
||||
const uint64_t m_inv_in =
|
||||
|
@ -338,7 +337,6 @@ static inline void prince_decrypt(const uint8_t in_bytes[8],
|
|||
int old_key_schedule) {
|
||||
prince_enc_dec(in_bytes, key_bytes, out_bytes, 1, num_half_rounds,
|
||||
old_key_schedule);
|
||||
uint64_t m16[2][16];
|
||||
}
|
||||
|
||||
#endif // OPENTITAN_HW_IP_PRIM_DV_PRIM_PRINCE_CRYPTO_DPI_PRINCE_PRINCE_REF_H_
|
||||
|
|
|
@ -32,6 +32,8 @@ module prim_prince_tb;
|
|||
localparam int unsigned KeyWidth = 128;
|
||||
`endif
|
||||
|
||||
localparam string MSG_ID = $sformatf("%m");
|
||||
|
||||
// Max number of half-rounds according to spec.
|
||||
// Duplicate parameter definition here to avoid clutter due to long identifier.
|
||||
localparam int unsigned NumRoundsHalf = crypto_dpi_prince_pkg::NumRoundsHalf;
|
||||
|
@ -244,7 +246,6 @@ module prim_prince_tb;
|
|||
|
||||
initial begin : p_stimuli
|
||||
int num_trans;
|
||||
string msg_id = $sformatf("%m");
|
||||
|
||||
// The key and plaintext/ciphertext to be fed into PRINCE instances.
|
||||
bit [KeyWidth/2-1:0] k0, k1;
|
||||
|
@ -294,9 +295,9 @@ module prim_prince_tb;
|
|||
void'($value$plusargs("smoke_test=%0b", smoke_test));
|
||||
num_trans = smoke_test ? 1 : $urandom_range(5000, 25000);
|
||||
for (int i = 0; i < num_trans; i++) begin
|
||||
`DV_CHECK_STD_RANDOMIZE_FATAL(plaintext, "", msg_id)
|
||||
`DV_CHECK_STD_RANDOMIZE_FATAL(k0, "", msg_id)
|
||||
`DV_CHECK_STD_RANDOMIZE_FATAL(k1, "", msg_id)
|
||||
`DV_CHECK_STD_RANDOMIZE_FATAL(plaintext, "", MSG_ID)
|
||||
`DV_CHECK_STD_RANDOMIZE_FATAL(k0, "", MSG_ID)
|
||||
`DV_CHECK_STD_RANDOMIZE_FATAL(k1, "", MSG_ID)
|
||||
test_prince(plaintext, {k1, k0});
|
||||
end
|
||||
|
||||
|
@ -308,9 +309,12 @@ module prim_prince_tb;
|
|||
|
||||
// TODO: perhaps wrap this in a macro?
|
||||
initial begin
|
||||
bit poll_for_stop = 1'b1;
|
||||
int unsigned poll_for_stop_interval_ns = 1000;
|
||||
bit poll_for_stop;
|
||||
int unsigned poll_for_stop_interval_ns;
|
||||
|
||||
poll_for_stop = 1'b1;
|
||||
void'($value$plusargs("poll_for_stop=%0b", poll_for_stop));
|
||||
poll_for_stop_interval_ns = 1000;
|
||||
void'($value$plusargs("poll_for_stop_interval_ns=%0d", poll_for_stop_interval_ns));
|
||||
if (poll_for_stop) dv_utils_pkg::poll_for_stop(.interval_ns(poll_for_stop_interval_ns));
|
||||
end
|
||||
|
|
|
@ -68,7 +68,7 @@ static std::vector<uint8_t> scramble_sbox_layer(const std::vector<uint8_t> &in,
|
|||
std::vector<uint8_t> out(in.size(), 0);
|
||||
|
||||
// Iterate through each 4 bit chunk of the data and apply the appropriate SBOX
|
||||
for (int i = 0; i < bit_width / 4; ++i) {
|
||||
for (uint32_t i = 0; i < bit_width / 4; ++i) {
|
||||
uint8_t sbox_in, sbox_out;
|
||||
|
||||
sbox_in = in[i / 2];
|
||||
|
@ -97,7 +97,7 @@ static std::vector<uint8_t> scramble_flip_layer(const std::vector<uint8_t> &in,
|
|||
assert(in.size() == ((bit_width + 7) / 8));
|
||||
std::vector<uint8_t> out(in.size(), 0);
|
||||
|
||||
for (int i = 0; i < bit_width; ++i) {
|
||||
for (uint32_t i = 0; i < bit_width; ++i) {
|
||||
or_vector_bit(out, bit_width - i - 1, read_vector_bit(in, i));
|
||||
}
|
||||
|
||||
|
@ -112,7 +112,7 @@ static std::vector<uint8_t> scramble_perm_layer(const std::vector<uint8_t> &in,
|
|||
assert(in.size() == ((bit_width + 7) / 8));
|
||||
std::vector<uint8_t> out(in.size(), 0);
|
||||
|
||||
for (int i = 0; i < bit_width / 2; ++i) {
|
||||
for (uint32_t i = 0; i < bit_width / 2; ++i) {
|
||||
if (invert) {
|
||||
or_vector_bit(out, i * 2, read_vector_bit(in, i));
|
||||
or_vector_bit(out, i * 2 + 1, read_vector_bit(in, i + (bit_width / 2)));
|
||||
|
@ -141,7 +141,7 @@ static std::vector<uint8_t> scramble_subst_perm_enc(
|
|||
|
||||
std::vector<uint8_t> state(in);
|
||||
|
||||
for (int i = 0; i < num_rounds; ++i) {
|
||||
for (uint32_t i = 0; i < num_rounds; ++i) {
|
||||
state = xor_vectors(state, key);
|
||||
|
||||
state = scramble_sbox_layer(state, bit_width, PRESENT_SBOX4);
|
||||
|
@ -164,7 +164,7 @@ static std::vector<uint8_t> scramble_subst_perm_dec(
|
|||
|
||||
std::vector<uint8_t> state(in);
|
||||
|
||||
for (int i = 0; i < num_rounds; ++i) {
|
||||
for (uint32_t i = 0; i < num_rounds; ++i) {
|
||||
state = xor_vectors(state, key);
|
||||
|
||||
state = scramble_perm_layer(state, bit_width, true);
|
||||
|
@ -199,12 +199,12 @@ static std::vector<uint8_t> scramble_gen_keystream(
|
|||
|
||||
std::vector<uint8_t> keystream;
|
||||
|
||||
for (int i = 0; i < num_princes; ++i) {
|
||||
for (uint32_t i = 0; i < num_princes; ++i) {
|
||||
// Initial vector is data for PRINCE to encrypt. Formed from nonce and data
|
||||
// address
|
||||
std::vector<uint8_t> iv(8, 0);
|
||||
|
||||
for (int j = 0; j < kPrinceWidth; ++j) {
|
||||
for (uint32_t j = 0; j < kPrinceWidth; ++j) {
|
||||
if (j < addr_width) {
|
||||
// Bottom addr_width bits of IV are address
|
||||
or_vector_bit(iv, j, read_vector_bit(addr, j));
|
||||
|
@ -228,7 +228,7 @@ static std::vector<uint8_t> scramble_gen_keystream(
|
|||
// Flip keystream into little endian order and add to keystream vector
|
||||
keystream_block = byte_reverse_vector(keystream_block);
|
||||
// Repeat the output of a single PRINCE instance if needed
|
||||
for (int k = 0; k < num_repetitions; ++k) {
|
||||
for (uint32_t k = 0; k < num_repetitions; ++k) {
|
||||
keystream.insert(keystream.end(), keystream_block.begin(),
|
||||
keystream_block.end());
|
||||
}
|
||||
|
@ -269,7 +269,7 @@ static std::vector<uint8_t> scramble_subst_perm_full_width(
|
|||
|
||||
auto sp_scrambler = enc ? scramble_subst_perm_enc : scramble_subst_perm_dec;
|
||||
|
||||
for (int i = 0; i < subst_perm_blocks; ++i) {
|
||||
for (uint32_t i = 0; i < subst_perm_blocks; ++i) {
|
||||
// Where bit_width does not evenly divide into subst_perm_width the
|
||||
// final block is smaller.
|
||||
uint32_t bits_so_far = subst_perm_width * i;
|
||||
|
@ -278,7 +278,7 @@ static std::vector<uint8_t> scramble_subst_perm_full_width(
|
|||
std::vector<uint8_t> subst_perm_data(subst_perm_bytes, 0);
|
||||
|
||||
// Extract bits from in for this chunk
|
||||
for (int j = 0; j < block_width; ++j) {
|
||||
for (uint32_t j = 0; j < block_width; ++j) {
|
||||
or_vector_bit(subst_perm_data, j,
|
||||
read_vector_bit(in, j + i * subst_perm_width));
|
||||
}
|
||||
|
@ -288,7 +288,7 @@ static std::vector<uint8_t> scramble_subst_perm_full_width(
|
|||
kNumDataSubstPermRounds);
|
||||
|
||||
// Write the result to the `out` vector
|
||||
for (int j = 0; j < block_width; ++j) {
|
||||
for (uint32_t j = 0; j < block_width; ++j) {
|
||||
or_vector_bit(out, j + i * subst_perm_width,
|
||||
read_vector_bit(subst_perm_out, j));
|
||||
}
|
||||
|
@ -308,7 +308,7 @@ std::vector<uint8_t> scramble_addr(const std::vector<uint8_t> &addr_in,
|
|||
// Address is scrambled by using substitution/permutation layer with the nonce
|
||||
// used as a key.
|
||||
// Extract relevant nonce bits for key
|
||||
for (int i = 0; i < addr_width; ++i) {
|
||||
for (uint32_t i = 0; i < addr_width; ++i) {
|
||||
or_vector_bit(addr_enc_nonce, i,
|
||||
read_vector_bit(nonce, nonce_width - addr_width + i));
|
||||
}
|
||||
|
|
8
vendor/lowrisc_ip/ip/prim/lint/prim_and2.waiver
vendored
Normal file
8
vendor/lowrisc_ip/ip/prim/lint/prim_and2.waiver
vendored
Normal file
|
@ -0,0 +1,8 @@
|
|||
# Copyright lowRISC contributors.
|
||||
# Licensed under the Apache License, Version 2.0, see LICENSE for details.
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
#
|
||||
# waiver file for prim_and2
|
||||
|
||||
waive -rules {STAR_PORT_CONN_USE} -location {prim_and2.sv} -regexp {.*wild card port connection encountered on instance.*} \
|
||||
-comment "Generated prims may have wildcard connections."
|
|
@ -16,3 +16,6 @@ waive -rules CLOCK_MUX -location {prim_clock_div.sv} -regexp {.*reaches a multip
|
|||
waive -rules CLOCK_USE -location {prim_clock_div.sv} -regexp {'clk_i' is connected to 'prim_clock_mux2' port 'clk1_i', and used as a clock} \
|
||||
-comment "This clock mux usage is OK."
|
||||
|
||||
waive -rules SAME_NAME_TYPE -location {prim_clock_div.sv} -regexp {'ResetValue' is used as a parameter here, and as an enumeration value at} \
|
||||
-comment "Reused parameter name."
|
||||
|
||||
|
|
8
vendor/lowrisc_ip/ip/prim/lint/prim_count.vlt
vendored
Normal file
8
vendor/lowrisc_ip/ip/prim/lint/prim_count.vlt
vendored
Normal file
|
@ -0,0 +1,8 @@
|
|||
// Copyright lowRISC contributors.
|
||||
// Licensed under the Apache License, Version 2.0, see LICENSE for details.
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
`verilator_config
|
||||
|
||||
// This parameter is only used in DV/FPV.
|
||||
lint_off -rule UNUSED -file "*/rtl/prim_count.sv" -match "*EnableAlertTriggerSVA*"
|
8
vendor/lowrisc_ip/ip/prim/lint/prim_count.waiver
vendored
Normal file
8
vendor/lowrisc_ip/ip/prim/lint/prim_count.waiver
vendored
Normal file
|
@ -0,0 +1,8 @@
|
|||
# Copyright lowRISC contributors.
|
||||
# Licensed under the Apache License, Version 2.0, see LICENSE for details.
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
#
|
||||
# waiver file for prim_count
|
||||
|
||||
waive -rules {PARAM_NOT_USED} -location {prim_count.sv} -regexp {.*EnableAlertTriggerSVA.*} \
|
||||
-comment "The disable parameter is used only during DV / FPV."
|
8
vendor/lowrisc_ip/ip/prim/lint/prim_double_lfsr.waiver
vendored
Normal file
8
vendor/lowrisc_ip/ip/prim/lint/prim_double_lfsr.waiver
vendored
Normal file
|
@ -0,0 +1,8 @@
|
|||
# Copyright lowRISC contributors.
|
||||
# Licensed under the Apache License, Version 2.0, see LICENSE for details.
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
#
|
||||
# waiver file for prim_double_lfsr
|
||||
|
||||
waive -rules {PARAM_NOT_USED} -location {prim_double_lfsr.sv} -regexp {.*EnableAlertTriggerSVA.*} \
|
||||
-comment "The disable parameter is used only during DV / FPV."
|
|
@ -4,5 +4,3 @@
|
|||
#
|
||||
# waiver file for prim_flop_2sync
|
||||
|
||||
waive -rules {STAR_PORT_CONN_USE} -location {prim_flop_2sync.sv} -regexp {.*wild card port connection encountered on instance.*} \
|
||||
-comment "Generated prims may have wildcard connections."
|
||||
|
|
18
vendor/lowrisc_ip/ip/prim/lint/prim_max_tree.vlt
vendored
Normal file
18
vendor/lowrisc_ip/ip/prim/lint/prim_max_tree.vlt
vendored
Normal file
|
@ -0,0 +1,18 @@
|
|||
// Copyright lowRISC contributors.
|
||||
// Licensed under the Apache License, Version 2.0, see LICENSE for details.
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
`verilator_config
|
||||
|
||||
// Tell the Verilator scheduler to split up these variables into
|
||||
// separate pieces when it's figuring out process scheduling. This
|
||||
// avoids spurious UNOPTFLAT warnings caused by the fact that the
|
||||
// arrays feed into themselves (with different bits for different
|
||||
// positions in the tree).
|
||||
split_var -module "prim_max_tree" -var "max_tree"
|
||||
split_var -module "prim_max_tree" -var "vld_tree"
|
||||
split_var -module "prim_max_tree" -var "idx_tree"
|
||||
|
||||
// The clock and reset are only used for assertions in this module.
|
||||
lint_off -rule UNUSED -file "*/rtl/prim_max_tree.sv" -match "Signal is not used: 'clk_i'"
|
||||
lint_off -rule UNUSED -file "*/rtl/prim_max_tree.sv" -match "Signal is not used: 'rst_ni'"
|
8
vendor/lowrisc_ip/ip/prim/lint/prim_max_tree.waiver
vendored
Normal file
8
vendor/lowrisc_ip/ip/prim/lint/prim_max_tree.waiver
vendored
Normal file
|
@ -0,0 +1,8 @@
|
|||
# Copyright lowRISC contributors.
|
||||
# Licensed under the Apache License, Version 2.0, see LICENSE for details.
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
#
|
||||
# waiver file for prim_arbiter
|
||||
|
||||
waive -rules {HIER_BRANCH_NOT_READ INPUT_NOT_READ} -location {prim_max_tree.sv} -regexp {.*'(clk_i|rst_ni)' is not read from in module 'prim_max_tree'.*} \
|
||||
-comment "clk_ and rst_ni are only used for assertions in this module."
|
11
vendor/lowrisc_ip/ip/prim/lint/prim_mubi.waiver
vendored
Normal file
11
vendor/lowrisc_ip/ip/prim/lint/prim_mubi.waiver
vendored
Normal file
|
@ -0,0 +1,11 @@
|
|||
# Copyright lowRISC contributors.
|
||||
# Licensed under the Apache License, Version 2.0, see LICENSE for details.
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
#
|
||||
# waiver file for prim_mubi modules
|
||||
|
||||
waive -rules SAME_NAME_TYPE -location {prim_mubi4_sync.sv} -regexp {'ResetValue' is used as an enumeration value here, and as a parameter at prim.*} \
|
||||
-comment "Parameter name reuse"
|
||||
|
||||
|
||||
|
8
vendor/lowrisc_ip/ip/prim/lint/prim_sparse_fsm_flop.vlt
vendored
Normal file
8
vendor/lowrisc_ip/ip/prim/lint/prim_sparse_fsm_flop.vlt
vendored
Normal file
|
@ -0,0 +1,8 @@
|
|||
// Copyright lowRISC contributors.
|
||||
// Licensed under the Apache License, Version 2.0, see LICENSE for details.
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
`verilator_config
|
||||
|
||||
// This parameter is only used in DV/FPV.
|
||||
lint_off -rule UNUSED -file "*/rtl/prim_sparse_fsm_flop.sv" -match "*EnableAlertTriggerSVA*"
|
|
@ -8,4 +8,10 @@ waive -rules {IFDEF_CODE} -location {prim_sparse_fsm_flop.sv} -regexp {.*unused_
|
|||
-comment "The unused_valid_st signal is used purely for DV only and is switched to a constant during lint / synth."
|
||||
|
||||
waive -rules {PARAM_NOT_USED} -location {prim_sparse_fsm_flop.sv} -regexp {.*StateEnumT.*} \
|
||||
-comment "The state enum is used only during DV / FPV."
|
||||
-comment "The state enum is used only during DV / FPV."
|
||||
|
||||
waive -rules {PARAM_NOT_USED} -location {prim_sparse_fsm_flop.sv} -regexp {.*EnableAlertTriggerSVA.*} \
|
||||
-comment "The disable parameter is used only during DV / FPV."
|
||||
|
||||
waive -rules {SAME_NAME_TYPE} -location {prim_sparse_fsm_flop.sv} -regexp {.*ResetValue.*} \
|
||||
-comment "The ResetValue parameter is a common name used by many prim types"
|
17
vendor/lowrisc_ip/ip/prim/lint/prim_sum_tree.vlt
vendored
Normal file
17
vendor/lowrisc_ip/ip/prim/lint/prim_sum_tree.vlt
vendored
Normal file
|
@ -0,0 +1,17 @@
|
|||
// Copyright lowRISC contributors.
|
||||
// Licensed under the Apache License, Version 2.0, see LICENSE for details.
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
`verilator_config
|
||||
|
||||
// Tell the Verilator scheduler to split up these variables into
|
||||
// separate pieces when it's figuring out process scheduling. This
|
||||
// avoids spurious UNOPTFLAT warnings caused by the fact that the
|
||||
// arrays feed into themselves (with different bits for different
|
||||
// positions in the tree).
|
||||
split_var -module "prim_sum_tree" -var "sum_tree"
|
||||
split_var -module "prim_sum_tree" -var "vld_tree"
|
||||
|
||||
// The clock and reset are only used for assertions in this module.
|
||||
lint_off -rule UNUSED -file "*/rtl/prim_sum_tree.sv" -match "Signal is not used: 'clk_i'"
|
||||
lint_off -rule UNUSED -file "*/rtl/prim_sum_tree.sv" -match "Signal is not used: 'rst_ni'"
|
8
vendor/lowrisc_ip/ip/prim/lint/prim_sum_tree.waiver
vendored
Normal file
8
vendor/lowrisc_ip/ip/prim/lint/prim_sum_tree.waiver
vendored
Normal file
|
@ -0,0 +1,8 @@
|
|||
# Copyright lowRISC contributors.
|
||||
# Licensed under the Apache License, Version 2.0, see LICENSE for details.
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
#
|
||||
# waiver file for prim_arbiter
|
||||
|
||||
waive -rules {HIER_BRANCH_NOT_READ INPUT_NOT_READ} -location {prim_sum_tree.sv} -regexp {.*'(clk_i|rst_ni)' is not read from in module 'prim_sum_tree'.*} \
|
||||
-comment "clk_ and rst_ni are only used for assertions in this module."
|
1
vendor/lowrisc_ip/ip/prim/prim.core
vendored
1
vendor/lowrisc_ip/ip/prim/prim.core
vendored
|
@ -26,6 +26,7 @@ filesets:
|
|||
- lowrisc:prim:subreg
|
||||
- lowrisc:prim:cipher
|
||||
- lowrisc:prim:xor2
|
||||
- lowrisc:prim:and2
|
||||
files:
|
||||
- rtl/prim_clock_gating_sync.sv
|
||||
- rtl/prim_sram_arbiter.sv
|
||||
|
|
48
vendor/lowrisc_ip/ip/prim/prim_and2.core
vendored
Normal file
48
vendor/lowrisc_ip/ip/prim/prim_and2.core
vendored
Normal file
|
@ -0,0 +1,48 @@
|
|||
CAPI=2:
|
||||
# Copyright lowRISC contributors.
|
||||
# Licensed under the Apache License, Version 2.0, see LICENSE for details.
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
name: "lowrisc:prim:and2"
|
||||
description: "Generic 2-input and"
|
||||
filesets:
|
||||
primgen_dep:
|
||||
depend:
|
||||
- lowrisc:prim:prim_pkg
|
||||
- lowrisc:prim:primgen
|
||||
|
||||
files_verilator_waiver:
|
||||
depend:
|
||||
# common waivers
|
||||
- lowrisc:lint:common
|
||||
files:
|
||||
file_type: vlt
|
||||
|
||||
files_ascentlint_waiver:
|
||||
depend:
|
||||
# common waivers
|
||||
- lowrisc:lint:common
|
||||
files:
|
||||
- lint/prim_and2.waiver
|
||||
file_type: waiver
|
||||
|
||||
files_veriblelint_waiver:
|
||||
depend:
|
||||
# common waivers
|
||||
- lowrisc:lint:common
|
||||
|
||||
generate:
|
||||
impl:
|
||||
generator: primgen
|
||||
parameters:
|
||||
prim_name: and2
|
||||
|
||||
targets:
|
||||
default:
|
||||
filesets:
|
||||
- tool_verilator ? (files_verilator_waiver)
|
||||
- tool_ascentlint ? (files_ascentlint_waiver)
|
||||
- tool_veriblelint ? (files_veriblelint_waiver)
|
||||
- primgen_dep
|
||||
generate:
|
||||
- impl
|
1
vendor/lowrisc_ip/ip/prim/prim_assert.core
vendored
1
vendor/lowrisc_ip/ip/prim/prim_assert.core
vendored
|
@ -12,6 +12,7 @@ filesets:
|
|||
- rtl/prim_assert_dummy_macros.svh : {is_include_file : true}
|
||||
- rtl/prim_assert_yosys_macros.svh : {is_include_file : true}
|
||||
- rtl/prim_assert_standard_macros.svh : {is_include_file : true}
|
||||
- rtl/prim_assert_sec_cm.svh : {is_include_file : true}
|
||||
file_type: systemVerilogSource
|
||||
|
||||
files_verilator_waiver:
|
||||
|
|
6
vendor/lowrisc_ip/ip/prim/prim_count.core
vendored
6
vendor/lowrisc_ip/ip/prim/prim_count.core
vendored
|
@ -18,11 +18,17 @@ filesets:
|
|||
depend:
|
||||
# common waivers
|
||||
- lowrisc:lint:common
|
||||
files:
|
||||
- lint/prim_count.vlt
|
||||
file_type: vlt
|
||||
|
||||
files_ascentlint_waiver:
|
||||
depend:
|
||||
# common waivers
|
||||
- lowrisc:lint:common
|
||||
files:
|
||||
- lint/prim_count.waiver
|
||||
file_type: waiver
|
||||
|
||||
files_veriblelint_waiver:
|
||||
depend:
|
||||
|
|
|
@ -24,6 +24,9 @@ filesets:
|
|||
depend:
|
||||
# common waivers
|
||||
- lowrisc:lint:common
|
||||
files:
|
||||
- lint/prim_double_lfsr.waiver
|
||||
file_type: waiver
|
||||
|
||||
files_veriblelint_waiver:
|
||||
depend:
|
||||
|
|
1
vendor/lowrisc_ip/ip/prim/prim_esc.core
vendored
1
vendor/lowrisc_ip/ip/prim/prim_esc.core
vendored
|
@ -11,6 +11,7 @@ filesets:
|
|||
- lowrisc:prim:assert
|
||||
- lowrisc:prim:diff_decode
|
||||
- lowrisc:prim:buf
|
||||
- lowrisc:prim:count
|
||||
- lowrisc:prim:flop
|
||||
- lowrisc:prim:sec_anchor
|
||||
files:
|
||||
|
|
20
vendor/lowrisc_ip/ip/prim/prim_flop_2sync.core
vendored
20
vendor/lowrisc_ip/ip/prim/prim_flop_2sync.core
vendored
|
@ -6,13 +6,15 @@ CAPI=2:
|
|||
name: "lowrisc:prim:flop_2sync"
|
||||
description: "Primitive synchronizer"
|
||||
filesets:
|
||||
primgen_dep:
|
||||
files_rtl:
|
||||
depend:
|
||||
- lowrisc:prim:prim_pkg
|
||||
- lowrisc:prim:primgen
|
||||
# Needed because the generic prim_generic_flop_2sync has a
|
||||
# Needed because the generic prim_flop_2sync has a
|
||||
# dependency on prim:flop.
|
||||
- lowrisc:prim:flop
|
||||
files:
|
||||
- rtl/prim_flop_2sync.sv
|
||||
file_type: systemVerilogSource
|
||||
|
||||
files_verilator_waiver:
|
||||
depend:
|
||||
|
@ -34,20 +36,10 @@ filesets:
|
|||
# common waivers
|
||||
- lowrisc:lint:common
|
||||
|
||||
|
||||
generate:
|
||||
impl:
|
||||
generator: primgen
|
||||
parameters:
|
||||
prim_name: flop_2sync
|
||||
|
||||
targets:
|
||||
default:
|
||||
filesets:
|
||||
- tool_verilator ? (files_verilator_waiver)
|
||||
- tool_ascentlint ? (files_ascentlint_waiver)
|
||||
- tool_veriblelint ? (files_veriblelint_waiver)
|
||||
- primgen_dep
|
||||
generate:
|
||||
- impl
|
||||
|
||||
- files_rtl
|
||||
|
|
49
vendor/lowrisc_ip/ip/prim/prim_max_tree.core
vendored
Normal file
49
vendor/lowrisc_ip/ip/prim/prim_max_tree.core
vendored
Normal file
|
@ -0,0 +1,49 @@
|
|||
CAPI=2:
|
||||
# Copyright lowRISC contributors.
|
||||
# Licensed under the Apache License, Version 2.0, see LICENSE for details.
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
name: "lowrisc:prim:max_tree"
|
||||
description: "Maximum computation primitive"
|
||||
filesets:
|
||||
files_rtl:
|
||||
depend:
|
||||
- lowrisc:prim:assert
|
||||
files:
|
||||
- rtl/prim_max_tree.sv
|
||||
file_type: systemVerilogSource
|
||||
|
||||
files_verilator_waiver:
|
||||
depend:
|
||||
# common waivers
|
||||
- lowrisc:lint:common
|
||||
files:
|
||||
- lint/prim_max_tree.vlt
|
||||
file_type: vlt
|
||||
|
||||
files_ascentlint_waiver:
|
||||
depend:
|
||||
# common waivers
|
||||
- lowrisc:lint:common
|
||||
files:
|
||||
- lint/prim_max_tree.waiver
|
||||
file_type: waiver
|
||||
|
||||
files_veriblelint_waiver:
|
||||
depend:
|
||||
# common waivers
|
||||
- lowrisc:lint:common
|
||||
|
||||
targets:
|
||||
default:
|
||||
filesets:
|
||||
- tool_verilator ? (files_verilator_waiver)
|
||||
- tool_ascentlint ? (files_ascentlint_waiver)
|
||||
- tool_veriblelint ? (files_veriblelint_waiver)
|
||||
- files_rtl
|
||||
|
||||
formal:
|
||||
filesets:
|
||||
- files_rtl
|
||||
toplevel: prim_max_tree
|
||||
|
|
@ -17,13 +17,16 @@ filesets:
|
|||
depend:
|
||||
# common waivers
|
||||
- lowrisc:lint:common
|
||||
files:
|
||||
- lint/prim_sparse_fsm_flop.vlt
|
||||
file_type: vlt
|
||||
|
||||
files_ascentlint_waiver:
|
||||
depend:
|
||||
# common waivers
|
||||
- lowrisc:lint:common
|
||||
files:
|
||||
- lint/prim_sparse_fsm.waiver
|
||||
- lint/prim_sparse_fsm_flop.waiver
|
||||
file_type: waiver
|
||||
|
||||
|
||||
|
|
48
vendor/lowrisc_ip/ip/prim/prim_sum_tree.core
vendored
Normal file
48
vendor/lowrisc_ip/ip/prim/prim_sum_tree.core
vendored
Normal file
|
@ -0,0 +1,48 @@
|
|||
CAPI=2:
|
||||
# Copyright lowRISC contributors.
|
||||
# Licensed under the Apache License, Version 2.0, see LICENSE for details.
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
name: "lowrisc:prim:sum_tree"
|
||||
description: "Summation primitive for arbitrary numbers of inputs"
|
||||
filesets:
|
||||
files_rtl:
|
||||
depend:
|
||||
- lowrisc:prim:assert
|
||||
files:
|
||||
- rtl/prim_sum_tree.sv
|
||||
file_type: systemVerilogSource
|
||||
|
||||
files_verilator_waiver:
|
||||
depend:
|
||||
# common waivers
|
||||
- lowrisc:lint:common
|
||||
files:
|
||||
- lint/prim_sum_tree.vlt
|
||||
file_type: vlt
|
||||
|
||||
files_ascentlint_waiver:
|
||||
depend:
|
||||
# common waivers
|
||||
- lowrisc:lint:common
|
||||
files:
|
||||
- lint/prim_sum_tree.waiver
|
||||
file_type: waiver
|
||||
|
||||
files_veriblelint_waiver:
|
||||
depend:
|
||||
# common waivers
|
||||
- lowrisc:lint:common
|
||||
|
||||
targets:
|
||||
default:
|
||||
filesets:
|
||||
- tool_verilator ? (files_verilator_waiver)
|
||||
- tool_ascentlint ? (files_ascentlint_waiver)
|
||||
- tool_veriblelint ? (files_veriblelint_waiver)
|
||||
- files_rtl
|
||||
|
||||
formal:
|
||||
filesets:
|
||||
- files_rtl
|
||||
toplevel: prim_sum_tree
|
|
@ -98,17 +98,18 @@ module prim_alert_receiver
|
|||
logic ping_req_d, ping_req_q;
|
||||
logic ping_pending_d, ping_pending_q;
|
||||
logic send_init;
|
||||
logic send_ping;
|
||||
|
||||
// signal ping request upon positive transition on ping_req_i
|
||||
// signalling is performed by a level change event on the diff output
|
||||
assign ping_req_d = ping_req_i;
|
||||
assign ping_rise = ping_req_i && !ping_req_q;
|
||||
assign ping_rise = ping_req_d && !ping_req_q;
|
||||
assign ping_tog_pd = (send_init) ? 1'b0 :
|
||||
(ping_rise) ? ~ping_tog_pq : ping_tog_pq;
|
||||
(send_ping) ? ~ping_tog_pq : ping_tog_pq;
|
||||
|
||||
// in-band reset is performed by sending out an integrity error on purpose.
|
||||
assign ack_dn = (send_init) ? ack_pd : ~ack_pd;
|
||||
assign ping_tog_dn = (send_init) ? ping_tog_pd : ~ping_tog_pd;
|
||||
assign ack_dn = (send_init) ? ack_pd : ~ack_pd;
|
||||
assign ping_tog_dn = ~ping_tog_pd;
|
||||
|
||||
// This prevents further tool optimizations of the differential signal.
|
||||
prim_sec_anchor_flop #(
|
||||
|
@ -135,7 +136,7 @@ module prim_alert_receiver
|
|||
ping_tog_pq})
|
||||
);
|
||||
|
||||
// the ping pending signal is used to in the FSM to distinguish whether the
|
||||
// the ping pending signal is used in the FSM to distinguish whether the
|
||||
// incoming handshake shall be treated as an alert or a ping response.
|
||||
// it is important that this is only set on a rising ping_en level change, since
|
||||
// otherwise the ping enable signal could be abused to "mask" all native alerts
|
||||
|
@ -161,6 +162,8 @@ module prim_alert_receiver
|
|||
integ_fail_o = 1'b0;
|
||||
alert_o = 1'b0;
|
||||
send_init = 1'b0;
|
||||
// by default, a ping request leads to a toogle on the differential ping pair
|
||||
send_ping = ping_rise;
|
||||
|
||||
unique case (state_q)
|
||||
Idle: begin
|
||||
|
@ -192,6 +195,8 @@ module prim_alert_receiver
|
|||
InitReq: begin
|
||||
// we deliberately place a sigint error on the ack and ping lines in this case.
|
||||
send_init = 1'b1;
|
||||
// suppress any toggles on the ping line while we are in the init phase.
|
||||
send_ping = 1'b0;
|
||||
// As long as init req is asserted, we remain in this state and acknowledge all incoming
|
||||
// ping requests. As soon as the init request is dropped however, ping requests are not
|
||||
// acked anymore such that the ping mechanism can also flag alert channels that got stuck
|
||||
|
@ -208,8 +213,14 @@ module prim_alert_receiver
|
|||
// has been deasserted. At this point, we need to wait for the alert_sigint to drop again
|
||||
// before resuming normal operation.
|
||||
InitAckWait: begin
|
||||
// suppress any toggles on the ping line while we are in the init phase.
|
||||
send_ping = 1'b0;
|
||||
if (!alert_sigint) begin
|
||||
state_d = Pause0;
|
||||
// If we get a ping request in this cycle, or if we realize that there is an unhandled
|
||||
// ping request that came in during initialization (but after init_trig_i has been
|
||||
// deasserted), we signal this to the alert sender by toggling the request line.
|
||||
send_ping = ping_rise || ping_pending_q;
|
||||
end
|
||||
end
|
||||
default: state_d = Idle;
|
||||
|
@ -270,13 +281,14 @@ module prim_alert_receiver
|
|||
// check encoding of outgoing diffpairs. note that during init, the outgoing diffpairs are
|
||||
// supposed to be incorrectly encoded on purpose.
|
||||
// shift sequence two cycles to the right to avoid reset effects.
|
||||
`ASSERT(PingDiffOk_A, ##2 $past(send_init) ^ alert_rx_o.ping_p ^ alert_rx_o.ping_n)
|
||||
`ASSERT(PingDiffOk_A, alert_rx_o.ping_p ^ alert_rx_o.ping_n)
|
||||
`ASSERT(AckDiffOk_A, ##2 $past(send_init) ^ alert_rx_o.ack_p ^ alert_rx_o.ack_n)
|
||||
`ASSERT(InitReq_A, mubi4_test_true_strict(init_trig_i) &&
|
||||
!(state_q inside {InitReq, InitAckWait}) |=> send_init)
|
||||
|
||||
// ping request at input -> need to see encoded ping request
|
||||
`ASSERT(PingRequest0_A, ##1 $rose(ping_req_i) && !send_init |=> $changed(alert_rx_o.ping_p))
|
||||
`ASSERT(PingRequest0_A, ##1 $rose(ping_req_i) && !state_q inside {InitReq, InitAckWait}
|
||||
|=> $changed(alert_rx_o.ping_p))
|
||||
// ping response implies it has been requested
|
||||
`ASSERT(PingResponse0_A, ping_ok_o |-> ping_pending_q)
|
||||
// correctly latch ping request
|
||||
|
|
|
@ -374,4 +374,11 @@ module prim_alert_sender
|
|||
clk_i, !rst_ni || (alert_tx_o.alert_p == alert_tx_o.alert_n))
|
||||
`endif
|
||||
|
||||
`ifdef FPV_SEC_CM_ON
|
||||
// Assumptions for FPV security countermeasures to ensure the alert protocol functions collectly.
|
||||
`ASSUME_FPV(AckPFollowsAlertP_S, alert_rx_i.ack_p == $past(alert_tx_o.alert_p))
|
||||
`ASSUME_FPV(AckNFollowsAlertN_S, alert_rx_i.ack_n == $past(alert_tx_o.alert_n))
|
||||
`ASSUME_FPV(TriggerAlertInit_S, $stable(rst_ni) == 0 |=> alert_rx_i.ping_p == alert_rx_i.ping_n)
|
||||
`ASSUME_FPV(PingDiffPair_S, ##2 alert_rx_i.ping_p != alert_rx_i.ping_n)
|
||||
`endif
|
||||
endmodule : prim_alert_sender
|
||||
|
|
14
vendor/lowrisc_ip/ip/prim/rtl/prim_assert.sv
vendored
14
vendor/lowrisc_ip/ip/prim/rtl/prim_assert.sv
vendored
|
@ -33,6 +33,16 @@
|
|||
`PRIM_STRINGIFY(__name)); \
|
||||
`endif
|
||||
|
||||
// This macro is suitable for conditionally triggering lint errors, e.g., if a Sec parameter takes
|
||||
// on a non-default value. This may be required for pre-silicon/FPGA evaluation but we don't want
|
||||
// to allow this for tapeout.
|
||||
`define ASSERT_STATIC_LINT_ERROR(__name, __prop) \
|
||||
localparam int __name = (__prop) ? 1 : 2; \
|
||||
always_comb begin \
|
||||
logic unused_assert_static_lint_error; \
|
||||
unused_assert_static_lint_error = __name'(1'b1); \
|
||||
end
|
||||
|
||||
// The basic helper macros are actually defined in "implementation headers". The macros should do
|
||||
// the same thing in each case (except for the dummy flavour), but in a way that the respective
|
||||
// tools support.
|
||||
|
@ -47,6 +57,8 @@
|
|||
//
|
||||
// ASSERT_INIT: Assertion in initial block. Can be used for things like parameter checking.
|
||||
//
|
||||
// ASSERT_INIT_NET: Assertion in initial block. Can be used for initial value of a net.
|
||||
//
|
||||
// ASSERT_FINAL: Assertion in final block. Can be used for things like queues being empty at end of
|
||||
// sim, all credits returned at end of sim, state machines in idle at end of sim.
|
||||
//
|
||||
|
@ -128,4 +140,6 @@
|
|||
`COVER(__name, __prop, __clk, __rst) \
|
||||
`endif
|
||||
|
||||
`include "prim_assert_sec_cm.svh"
|
||||
|
||||
`endif // PRIM_ASSERT_SV
|
||||
|
|
|
@ -7,6 +7,7 @@
|
|||
|
||||
`define ASSERT_I(__name, __prop)
|
||||
`define ASSERT_INIT(__name, __prop)
|
||||
`define ASSERT_INIT_NET(__name, __prop)
|
||||
`define ASSERT_FINAL(__name, __prop)
|
||||
`define ASSERT(__name, __prop, __clk = `ASSERT_DEFAULT_CLK, __rst = `ASSERT_DEFAULT_RST)
|
||||
`define ASSERT_NEVER(__name, __prop, __clk = `ASSERT_DEFAULT_CLK, __rst = `ASSERT_DEFAULT_RST)
|
||||
|
|
29
vendor/lowrisc_ip/ip/prim/rtl/prim_assert_sec_cm.svh
vendored
Normal file
29
vendor/lowrisc_ip/ip/prim/rtl/prim_assert_sec_cm.svh
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
|
||||
|
||||
// // Macros and helper code for security countermeasures.
|
||||
|
||||
`ifndef PRIM_ASSERT_SEC_CM_SVH
|
||||
`define PRIM_ASSERT_SEC_CM_SVH
|
||||
|
||||
// Helper macros
|
||||
`define ASSERT_ERROR_TRIGGER_ALERT(NAME_, PRIM_HIER_, ALERT_, MAX_CYCLES_, ERR_NAME_) \
|
||||
`ASSERT(FpvSecCm``NAME_``, $rose(PRIM_HIER_.ERR_NAME_) |-> ##[0:MAX_CYCLES_] (ALERT_.alert_p)) \
|
||||
`ifdef INC_ASSERT \
|
||||
assign PRIM_HIER_.unused_assert_connected = 1'b1; \
|
||||
`endif \
|
||||
`ASSUME_FPV(``NAME_``TriggerAfterAlertInit_S, $stable(rst_ni) == 0 |-> \
|
||||
PRIM_HIER_.ERR_NAME_ == 0 [*10])
|
||||
|
||||
// macros for security countermeasures
|
||||
`define ASSERT_PRIM_COUNT_ERROR_TRIGGER_ALERT(NAME_, PRIM_HIER_, ALERT_, MAX_CYCLES_ = 7) \
|
||||
`ASSERT_ERROR_TRIGGER_ALERT(NAME_, PRIM_HIER_, ALERT_, MAX_CYCLES_, err_o)
|
||||
|
||||
`define ASSERT_PRIM_DOUBLE_LFSR_ERROR_TRIGGER_ALERT(NAME_, PRIM_HIER_, ALERT_, MAX_CYCLES_ = 7) \
|
||||
`ASSERT_ERROR_TRIGGER_ALERT(NAME_, PRIM_HIER_, ALERT_, MAX_CYCLES_, err_o)
|
||||
|
||||
`define ASSERT_PRIM_FSM_ERROR_TRIGGER_ALERT(NAME_, PRIM_HIER_, ALERT_, MAX_CYCLES_ = 7) \
|
||||
`ASSERT_ERROR_TRIGGER_ALERT(NAME_, PRIM_HIER_, ALERT_, MAX_CYCLES_, unused_err_o)
|
||||
|
||||
`endif // PRIM_ASSERT_SEC_CM_SVH
|
|
@ -21,13 +21,6 @@
|
|||
(__name), (__prop)); \
|
||||
`else \
|
||||
initial begin \
|
||||
// #9017: Avoid race condition between the evaluation of assertion and `__prop`. \
|
||||
// \
|
||||
// According to IEEE 1800-2017 SystemVerilog LRM, immediate assertions, unlike \
|
||||
// concurrent assertions are evaluated in the active region set, as opposed to \
|
||||
// the observed region. They are hence, susceptible to race conditions \
|
||||
// (described in section 4.8). The #0 is an acceptable workaround for this. \
|
||||
#0; \
|
||||
__name: assert (__prop) \
|
||||
else begin \
|
||||
`ASSERT_ERROR(__name) \
|
||||
|
@ -35,6 +28,18 @@
|
|||
end \
|
||||
`endif
|
||||
|
||||
`define ASSERT_INIT_NET(__name, __prop) \
|
||||
initial begin \
|
||||
// When a net is assigned with a value, the assignment is evaluated after \
|
||||
// initial in Xcelium. Add 1ps delay to check value after the assignment is \
|
||||
// completed. \
|
||||
#1ps; \
|
||||
__name: assert (__prop) \
|
||||
else begin \
|
||||
`ASSERT_ERROR(__name) \
|
||||
end \
|
||||
end \
|
||||
|
||||
`define ASSERT_FINAL(__name, __prop) \
|
||||
final begin \
|
||||
__name: assert (__prop || $test$plusargs("disable_assert_final_checks")) \
|
||||
|
|
|
@ -15,6 +15,11 @@
|
|||
assert (__prop); \
|
||||
end
|
||||
|
||||
`define ASSERT_INIT_NET(__name, __prop) \
|
||||
initial begin : __name \
|
||||
#1ps assert (__prop); \
|
||||
end
|
||||
|
||||
// This doesn't make much sense for a formal tool (we never get to the final block!)
|
||||
`define ASSERT_FINAL(__name, __prop)
|
||||
|
||||
|
|
|
@ -149,10 +149,10 @@ module prim_clock_meas #(
|
|||
|
||||
// maximum cdc latency from the perspective of the reference clock
|
||||
// 1 ref cycle to output request
|
||||
// 2 cycles to sync + 1 cycle to ack is less than 1 cycle of ref based on assertion requirement
|
||||
// 2 cycles to sync + 1 cycle to ack are less than 1 cycle of ref based on assertion requirement
|
||||
// 2 ref cycles to sync ack
|
||||
// Double for margin
|
||||
localparam int MaxRefCdcLatency = (1 + 1 + 2)*2;
|
||||
// 2 extra ref cycles for margin
|
||||
localparam int MaxRefCdcLatency = 1 + 1 + 2 + 2;
|
||||
|
||||
if (RefTimeOutChkEn) begin : gen_ref_timeout_chk
|
||||
// check whether reference clock has timed out
|
||||
|
|
121
vendor/lowrisc_ip/ip/prim/rtl/prim_count.sv
vendored
121
vendor/lowrisc_ip/ip/prim/rtl/prim_count.sv
vendored
|
@ -9,25 +9,34 @@
|
|||
// There are two copies of the relevant counter and they are constantly compared.
|
||||
// 2. Cross count
|
||||
// There is an up count and a down count, and the combined value must always
|
||||
// combine to the same value
|
||||
// combine to the same value.
|
||||
//
|
||||
// This counter supports a generic clr / set / en interface.
|
||||
// When clr_i is set, the counter clears to 0
|
||||
// When set_i is set, the down count (if enabled) will set to the max value
|
||||
// When neither of the above is set, increment the up count(s) or decrement the down count.
|
||||
// This counter supports a generic clr / set / en interface, where
|
||||
// clr_i and set_i MUST NOT be set at the same time!
|
||||
//
|
||||
// Note there are many other flavor of functions that can be added, but this primitive
|
||||
// module initially favors the usage inside keymgr and flash_ctrl.
|
||||
// In duplicate count mode
|
||||
// - clr_i sets all (internal) counters to 0.
|
||||
// - set_i sets the up_count's starting value to set_cnt_i.
|
||||
// Note: the max_val is just the max possible value given by the counter's width.
|
||||
// - en_i increments the counter by step_i, if neither of the above is set.
|
||||
//
|
||||
// The usage of set_cnt_i is as follows:
|
||||
// When doing CrossCnt, set_cnt_i sets the maximum value as well as the starting value of down_cnt.
|
||||
// When doing DupCnt, set_cnt_i sets the starting value of up_cnt. Note during DupCnt, the maximum
|
||||
// value is just the max possible value given the counter width.
|
||||
// In cross count mode
|
||||
// - clr_i sets
|
||||
// -- the down_count to 0.
|
||||
// -- the up_count's max_val to the max possible value given by the counter's width.
|
||||
// - set_i sets
|
||||
// -- the up_count to 0 and the down_count to set_cnt_i,
|
||||
// -- the up_count's max_val to set_cnt_i.
|
||||
// - en_i increments/decrements the up_count/down_count by step_i, if neither of the above is
|
||||
// set.
|
||||
|
||||
module prim_count import prim_count_pkg::*; #(
|
||||
parameter int Width = 2,
|
||||
parameter bit OutSelDnCnt = 1, // 0 selects up count
|
||||
parameter prim_count_style_e CntStyle = CrossCnt
|
||||
parameter prim_count_style_e CntStyle = CrossCnt, // DupCnt or CrossCnt
|
||||
// This should only be disabled in special circumstances, for example
|
||||
// in non-comportable IPs where an error does not trigger an alert.
|
||||
parameter bit EnableAlertTriggerSVA = 1
|
||||
) (
|
||||
input clk_i,
|
||||
input rst_ni,
|
||||
|
@ -40,7 +49,7 @@ module prim_count import prim_count_pkg::*; #(
|
|||
output logic err_o
|
||||
);
|
||||
|
||||
// if output selects downcount, it MUST be the cross count style
|
||||
// if output selects down count, it MUST be the cross count style
|
||||
`ASSERT_INIT(CntStyleMatch_A, OutSelDnCnt ? CntStyle == CrossCnt : 1'b1)
|
||||
|
||||
localparam int CntCopies = (CntStyle == DupCnt) ? 2 : 1;
|
||||
|
@ -49,32 +58,36 @@ module prim_count import prim_count_pkg::*; #(
|
|||
// when the max value is re-set during cross count.
|
||||
logic clr_up_cnt;
|
||||
assign clr_up_cnt = clr_i |
|
||||
set_i & CntStyle == CrossCnt;
|
||||
(set_i & (CntStyle == CrossCnt));
|
||||
|
||||
// set up count to desired value only during duplicate counts.
|
||||
logic set_up_cnt;
|
||||
assign set_up_cnt = set_i & CntStyle == DupCnt;
|
||||
assign set_up_cnt = set_i & (CntStyle == DupCnt);
|
||||
|
||||
|
||||
cmp_valid_e cmp_valid;
|
||||
logic [CntCopies-1:0][Width-1:0] up_cnt_d, up_cnt_d_buf;
|
||||
logic [CntCopies-1:0][Width-1:0] up_cnt_q;
|
||||
logic [Width-1:0] max_val;
|
||||
logic err;
|
||||
|
||||
always_ff @(posedge clk_i or negedge rst_ni) begin
|
||||
if (!rst_ni) begin
|
||||
max_val <= '{default: '1};
|
||||
end else if (set_i && (CntStyle == CrossCnt)) begin
|
||||
max_val <= set_cnt_i;
|
||||
if (CntStyle == CrossCnt) begin : gen_crosscnt_max_val
|
||||
always_ff @(posedge clk_i or negedge rst_ni) begin
|
||||
if (!rst_ni) begin
|
||||
max_val <= '1;
|
||||
end else if (clr_i) begin
|
||||
max_val <= '1;
|
||||
end else if (set_i) begin
|
||||
max_val <= set_cnt_i;
|
||||
end
|
||||
end
|
||||
end else begin : gen_dupcnt_max_val
|
||||
assign max_val = '1;
|
||||
end
|
||||
|
||||
for (genvar i = 0; i < CntCopies; i++) begin : gen_cnts
|
||||
// up-count
|
||||
assign up_cnt_d[i] = (clr_up_cnt) ? '0 :
|
||||
(set_up_cnt) ? set_cnt_i :
|
||||
(en_i & up_cnt_q[i] < max_val) ? up_cnt_q[i] + step_i :
|
||||
assign up_cnt_d[i] = (clr_up_cnt) ? '0 :
|
||||
(set_up_cnt) ? set_cnt_i :
|
||||
(en_i & (up_cnt_q[i] < max_val)) ? up_cnt_q[i] + step_i :
|
||||
up_cnt_q[i];
|
||||
|
||||
prim_buf #(
|
||||
|
@ -99,25 +112,15 @@ module prim_count import prim_count_pkg::*; #(
|
|||
logic [Width-1:0] down_cnt;
|
||||
logic [Width-1:0] sum;
|
||||
|
||||
always_ff @(posedge clk_i or negedge rst_ni) begin
|
||||
if (!rst_ni) begin
|
||||
cmp_valid <= CmpInvalid;
|
||||
end else if (clr_i) begin
|
||||
cmp_valid <= CmpInvalid;
|
||||
end else if ((cmp_valid == CmpInvalid) && set_i) begin
|
||||
cmp_valid <= CmpValid;
|
||||
end
|
||||
end
|
||||
|
||||
// down-count
|
||||
always_ff @(posedge clk_i or negedge rst_ni) begin
|
||||
if (!rst_ni) begin
|
||||
down_cnt <= '0;
|
||||
down_cnt <= '{default: '1};
|
||||
end else if (clr_i) begin
|
||||
down_cnt <= '0;
|
||||
down_cnt <= '{default: '1};
|
||||
end else if (set_i) begin
|
||||
down_cnt <= set_cnt_i;
|
||||
end else if (en_i && down_cnt > '0) begin
|
||||
end else if (en_i && (down_cnt > '0)) begin
|
||||
down_cnt <= down_cnt - step_i;
|
||||
end
|
||||
end
|
||||
|
@ -125,29 +128,28 @@ module prim_count import prim_count_pkg::*; #(
|
|||
logic msb;
|
||||
assign {msb, sum} = down_cnt + up_cnt_q[0];
|
||||
assign cnt_o = OutSelDnCnt ? down_cnt : up_cnt_q[0];
|
||||
assign err = max_val != sum | msb;
|
||||
assign err = (max_val != sum) | msb;
|
||||
|
||||
`ASSERT(CrossCntErrForward_A,
|
||||
(cmp_valid == CmpValid) && ((down_cnt + up_cnt_q[0]) != {1'b0, max_val}) |-> err_o)
|
||||
((down_cnt + up_cnt_q[0]) != {1'b0, max_val}) |-> err_o)
|
||||
`ASSERT(CrossCntErrBackward_A, err_o |->
|
||||
(cmp_valid == CmpValid) && ((down_cnt + up_cnt_q[0]) != {1'b0, max_val}))
|
||||
((down_cnt + up_cnt_q[0]) != {1'b0, max_val}))
|
||||
|
||||
// Down counter assumption to control underflow
|
||||
// We can also constrain the down counter underflow via `down_cnt % step_i == 0`.
|
||||
// However, modulo operation can be very complex for formal analysis.
|
||||
`ASSUME(DownCntStepInt_A, cmp_valid == CmpValid |-> down_cnt == 0 || down_cnt >= step_i)
|
||||
`ASSUME(DownCntStepInt_A, down_cnt == 0 || down_cnt >= step_i)
|
||||
|
||||
// Up counter assumption to control overflow
|
||||
logic [Width:0] unused_cnt;
|
||||
assign unused_cnt = up_cnt_q[0] + step_i;
|
||||
logic unused_incr_cnt;
|
||||
assign unused_incr_cnt = (cmp_valid == CmpValid) & !clr_i & !set_i;
|
||||
assign unused_incr_cnt = !clr_i & !set_i & en_i;
|
||||
|
||||
`ASSUME(UpCntOverFlow_A, unused_incr_cnt && !err |-> ~unused_cnt[Width])
|
||||
|
||||
end else if (CntStyle == DupCnt) begin : gen_dup_cnt_hardening
|
||||
// duplicate count compare is always valid
|
||||
assign cmp_valid = CmpValid;
|
||||
assign cnt_o = up_cnt_q[0];
|
||||
assign err = (up_cnt_q[0] != up_cnt_q[1]);
|
||||
|
||||
|
@ -155,9 +157,7 @@ module prim_count import prim_count_pkg::*; #(
|
|||
`ASSERT(DupCntErrBackward_A, err_o |-> up_cnt_q[0] != up_cnt_q[1])
|
||||
end
|
||||
|
||||
// If the compare flag is not a valid enum, treat it like an error
|
||||
assign err_o = (cmp_valid == CmpValid) ? err :
|
||||
(cmp_valid == CmpInvalid) ? '0 : 1'b1;
|
||||
assign err_o = err;
|
||||
|
||||
// ASSERTIONS AND ASSUMPTIONS
|
||||
`ifdef INC_ASSERT
|
||||
|
@ -177,21 +177,28 @@ module prim_count import prim_count_pkg::*; #(
|
|||
// Clear and set should not be seen at the same time
|
||||
`ASSUME(SimulClrSet_A, clr_i || set_i |-> clr_i != set_i)
|
||||
|
||||
`ASSERT(OutClr_A, clr_i |=> cnt_o == 0)
|
||||
// when the counter is forced by TB, it can be any value, but we should see err_o is set.
|
||||
`ASSERT(OutClr_A, clr_i |=> (OutSelDnCnt ? &cnt_o : cnt_o == 0) || err_o)
|
||||
|
||||
// When `en_i` is set without `clr_i` and `set_i`, and counter does not reach max/min value,
|
||||
// we expect `cnt_o` to increment or decrement base on `step_i`.
|
||||
// we expect `cnt_o` to increment or decrement base on `step_i`, or error occurs
|
||||
`ASSERT(OutStep_A,
|
||||
!(clr_i ||set_i) throughout en_i ##[1:$] en_i && max_val > cnt_o && cnt_o > 0 |->
|
||||
(CntStyle == DupCnt || !OutSelDnCnt) ? cnt_o - past_cnt_o == past_step_i :
|
||||
past_cnt_o - cnt_o == past_step_i)
|
||||
((CntStyle == DupCnt || !OutSelDnCnt) ? cnt_o - past_cnt_o == past_step_i :
|
||||
past_cnt_o - cnt_o == past_step_i) || err_o)
|
||||
|
||||
// When `set_i` is set, at next clock cycle:
|
||||
// 1). For duplicate counter, sets the `cnt_o` to `set_cnt_i`.
|
||||
// 2). For cross up counter, sets the `max_value` to `set_cnt_i`.
|
||||
// 3). For cross down counter, sets the `cnt_o` and `max_value` to `set_cnt_i`.
|
||||
// 4). error occurs due to a fault injection
|
||||
`ASSERT(OutSet_A, ##1 set_i |=>
|
||||
(CntStyle == DupCnt || OutSelDnCnt) ? cnt_o == $past(set_cnt_i) : cnt_o == 0)
|
||||
((CntStyle == DupCnt || OutSelDnCnt) ? cnt_o == $past(set_cnt_i) : cnt_o == 0) || err_o)
|
||||
|
||||
// If the up counter reaches its max value, the value won't increment or change, unless there is
|
||||
// a fault injection
|
||||
`ASSERT(MaxUpCntStable_A, up_cnt_q[0] == max_val && !clr_i && !set_i |=>
|
||||
$stable(up_cnt_q[0]) || err_o || $past(err_o))
|
||||
|
||||
// This logic that will be assign to one, when user adds macro
|
||||
// ASSERT_PRIM_COUNT_ERROR_TRIGGER_ALERT to check the error with alert, in case that prim_count
|
||||
|
@ -199,15 +206,7 @@ module prim_count import prim_count_pkg::*; #(
|
|||
`ifdef INC_ASSERT
|
||||
logic unused_assert_connected;
|
||||
|
||||
// ASSERT_INIT can only be used for paramters/constants in FPV.
|
||||
`ifdef SIMULATION
|
||||
`ASSERT_INIT(AssertConnected_A, unused_assert_connected === 1'b1)
|
||||
`endif
|
||||
`ASSERT_INIT_NET(AssertConnected_A, unused_assert_connected === 1'b1 || !EnableAlertTriggerSVA)
|
||||
`endif
|
||||
endmodule // prim_count
|
||||
|
||||
`define ASSERT_PRIM_COUNT_ERROR_TRIGGER_ALERT(NAME_, PRIM_HIER_, ALERT_, MAX_CYCLES_ = 5) \
|
||||
`ASSERT(NAME_, $rose(PRIM_HIER_.err_o) |-> ##[1:MAX_CYCLES_] $rose(ALERT_.alert_p)) \
|
||||
`ifdef INC_ASSERT \
|
||||
assign PRIM_HIER_.unused_assert_connected = 1'b1; \
|
||||
`endif
|
||||
|
|
2
vendor/lowrisc_ip/ip/prim/rtl/prim_crc32.sv
vendored
2
vendor/lowrisc_ip/ip/prim/rtl/prim_crc32.sv
vendored
|
@ -312,7 +312,7 @@ module prim_crc32 #(
|
|||
end
|
||||
end
|
||||
|
||||
always @(posedge clk_i or negedge rst_ni) begin
|
||||
always_ff @(posedge clk_i or negedge rst_ni) begin
|
||||
if (!rst_ni) begin
|
||||
crc_q <= 32'hFFFFFFFF;
|
||||
end else if (crc_en) begin
|
||||
|
|
|
@ -26,8 +26,7 @@
|
|||
`include "prim_assert.sv"
|
||||
|
||||
module prim_dom_and_2share #(
|
||||
parameter int DW = 64, // Input width
|
||||
parameter int EnNegedge = 0 // Enable negedge of clk for register
|
||||
parameter int DW = 64 // Input width
|
||||
) (
|
||||
input clk_i,
|
||||
input rst_ni,
|
||||
|
@ -36,9 +35,8 @@ module prim_dom_and_2share #(
|
|||
input [DW-1:0] a1_i, // share1 of a
|
||||
input [DW-1:0] b0_i, // share0 of b
|
||||
input [DW-1:0] b1_i, // share1 of b
|
||||
input c_valid_i, // random number input validity
|
||||
input [DW-1:0] c0_i, // share0 of random number
|
||||
input [DW-1:0] c1_i, // share1 of random number
|
||||
input z_valid_i, // random number input validity
|
||||
input [DW-1:0] z_i, // random number
|
||||
|
||||
output logic [DW-1:0] q0_o, // share0 of q
|
||||
output logic [DW-1:0] q1_o // share1 of q
|
||||
|
@ -46,54 +44,62 @@ module prim_dom_and_2share #(
|
|||
|
||||
logic [DW-1:0] t0_d, t0_q, t1_d, t1_q;
|
||||
logic [DW-1:0] t_a0b1, t_a1b0, t_a0b0, t_a1b1;
|
||||
//synopsys keep_signal_name t_a0b1
|
||||
//synopsys keep_signal_name t_a1b0
|
||||
//synopsys keep_signal_name t_a0b0
|
||||
//synopsys keep_signal_name t_a1b1
|
||||
|
||||
// Preserve the logic sequence for XOR not to preceed the AND
|
||||
assign t_a0b1 = a0_i & b1_i;
|
||||
assign t_a1b0 = a1_i & b0_i;
|
||||
assign t0_d = t_a0b1 ^ c0_i;
|
||||
assign t1_d = t_a1b0 ^ c1_i;
|
||||
|
||||
if (EnNegedge == 1) begin: gen_negreg
|
||||
// TODO: Make inverted clock and use.
|
||||
always_ff @(negedge clk_i or negedge rst_ni) begin
|
||||
if (!rst_ni) begin
|
||||
t0_q <= '0;
|
||||
t1_q <= '0;
|
||||
end else if (c_valid_i) begin
|
||||
t0_q <= t0_d;
|
||||
t1_q <= t1_d;
|
||||
end
|
||||
end
|
||||
end else begin: gen_posreg
|
||||
always_ff @(posedge clk_i or negedge rst_ni) begin
|
||||
if (!rst_ni) begin
|
||||
t0_q <= '0;
|
||||
t1_q <= '0;
|
||||
end else if (c_valid_i) begin
|
||||
t0_q <= t0_d;
|
||||
t1_q <= t1_d;
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
/////////////////
|
||||
// Calculation //
|
||||
/////////////////
|
||||
// Inner-domain terms
|
||||
assign t_a0b0 = a0_i & b0_i;
|
||||
assign t_a1b1 = a1_i & b1_i;
|
||||
assign q0_o = t_a0b0 ^ t0_q;
|
||||
assign q1_o = t_a1b1 ^ t1_q;
|
||||
|
||||
// Negative Edge isn't yet supported. Need inverted clock and use
|
||||
// inside always_ff not `negedge clk_i`.
|
||||
`ASSERT_INIT(NegedgeNotSupported_A, EnNegedge == 0)
|
||||
// Cross-domain terms
|
||||
assign t_a0b1 = a0_i & b1_i;
|
||||
assign t_a1b0 = a1_i & b0_i;
|
||||
|
||||
///////////////
|
||||
// Resharing //
|
||||
///////////////
|
||||
// Resharing of cross-domain terms
|
||||
|
||||
// Preserve the logic sequence for XOR not to proceed cross-domain AND.
|
||||
prim_xor2 #(
|
||||
.Width ( DW*2 )
|
||||
) u_prim_xor_t01 (
|
||||
.in0_i ( {t_a0b1, t_a1b0} ),
|
||||
.in1_i ( {z_i, z_i} ),
|
||||
.out_o ( {t0_d, t1_d} )
|
||||
);
|
||||
|
||||
// Register stage
|
||||
prim_flop_en #(
|
||||
.Width ( DW*2 ),
|
||||
.ResetValue ( '0 )
|
||||
) u_prim_flop_t01 (
|
||||
.clk_i ( clk_i ),
|
||||
.rst_ni ( rst_ni ),
|
||||
.en_i ( z_valid_i ),
|
||||
.d_i ( {t0_d, t1_d} ),
|
||||
.q_o ( {t0_q, t1_q} )
|
||||
);
|
||||
|
||||
/////////////////
|
||||
// Integration //
|
||||
/////////////////
|
||||
|
||||
// Preserve the logic sequence for XOR not to proceed the inner-domain AND.
|
||||
prim_xor2 #(
|
||||
.Width ( DW*2 )
|
||||
) u_prim_xor_q01 (
|
||||
.in0_i ( {t_a0b0, t_a1b1} ),
|
||||
.in1_i ( {t0_q, t1_q} ),
|
||||
.out_o ( {q0_o, q1_o} )
|
||||
);
|
||||
|
||||
// DOM AND should be same as unmasked computation
|
||||
// TODO: Put assumption that input need to be stable for at least two cycles
|
||||
// The correct test sequence will be:
|
||||
// 1. inputs are changed
|
||||
// 2. check if c_valid_i,
|
||||
// 2. check if z_valid_i,
|
||||
// 3. at the next cycle, inputs are still stable (assumption)
|
||||
// 4. and results Q == A & B (assertion)
|
||||
|
||||
|
@ -101,11 +107,11 @@ module prim_dom_and_2share #(
|
|||
// equal to two cycles.
|
||||
`ASSUME_FPV(RandomReadyInShortTime_A,
|
||||
$changed(a0_i) || $changed(a1_i) || $changed(b0_i) || $changed(b1_i)
|
||||
|-> ##[0:2] c_valid_i,
|
||||
|-> ##[0:2] z_valid_i,
|
||||
clk_i, !rst_ni)
|
||||
`ASSERT(UnmaskedAndMatched_A,
|
||||
$changed(a0_i) || $changed(a1_i) || $changed(b0_i) || $changed(b1_i)
|
||||
|-> ##[0:$] c_valid_i
|
||||
|-> ##[0:$] z_valid_i
|
||||
|=> $stable(a0_i) && $stable(a1_i) && $stable(b0_i) && $stable(b1_i)
|
||||
|-> (q0_o ^ q1_o) == ((a0_i ^ a1_i) & (b0_i ^ b1_i)),
|
||||
clk_i, !rst_ni)
|
||||
|
|
|
@ -20,7 +20,10 @@ module prim_double_lfsr #(
|
|||
parameter bit MaxLenSVA = 1'b1,
|
||||
parameter bit LockupSVA = 1'b1,
|
||||
parameter bit ExtSeedSVA = 1'b1,
|
||||
parameter bit NonLinearOut = 1'b0
|
||||
parameter bit NonLinearOut = 1'b0,
|
||||
// This should only be disabled in special circumstances, for example
|
||||
// in non-comportable IPs where an error does not trigger an alert.
|
||||
parameter bit EnableAlertTriggerSVA = 1
|
||||
) (
|
||||
input clk_i,
|
||||
input rst_ni,
|
||||
|
@ -85,4 +88,12 @@ module prim_double_lfsr #(
|
|||
assign state_o = lfsr_state[0][StateOutDw-1:0];
|
||||
assign err_o = lfsr_state[0] != lfsr_state[1];
|
||||
|
||||
// This logic that will be assign to one, when user adds macro
|
||||
// ASSERT_PRIM_DOUBLE_LFSR_ERROR_TRIGGER_ALERT to check the error with alert, in case that
|
||||
// prim_double_lfsr is used in design without adding this assertion check.
|
||||
`ifdef INC_ASSERT
|
||||
logic unused_assert_connected;
|
||||
|
||||
`ASSERT_INIT_NET(AssertConnected_A, unused_assert_connected === 1'b1 || !EnableAlertTriggerSVA)
|
||||
`endif
|
||||
endmodule : prim_double_lfsr
|
||||
|
|
|
@ -90,37 +90,43 @@ module prim_esc_receiver
|
|||
// Ping Monitor Counter / Auto Escalation //
|
||||
////////////////////////////////////////////
|
||||
|
||||
logic ping_en, esc_req;
|
||||
logic [1:0][TimeoutCntDw-1:0] cnt_q;
|
||||
// The timeout counter is kicked off when the first ping occurs, and subsequent pings reset
|
||||
// the counter to 1. The counter keeps on counting when it is nonzero, and saturates when it
|
||||
// has reached its maximum (this state is terminal).
|
||||
logic ping_en, timeout_cnt_error;
|
||||
logic timeout_cnt_set, timeout_cnt_en;
|
||||
logic [TimeoutCntDw-1:0] timeout_cnt;
|
||||
assign timeout_cnt_set = (ping_en && !(&timeout_cnt));
|
||||
assign timeout_cnt_en = ((timeout_cnt > '0) && !(&timeout_cnt));
|
||||
|
||||
for (genvar k = 0; k < 2; k++) begin : gen_timeout_cnt
|
||||
|
||||
logic [TimeoutCntDw-1:0] cnt_d;
|
||||
|
||||
// The timeout counter is kicked off when the first ping occurs, and subsequent pings reset
|
||||
// the counter to 1. The counter keeps on counting when it is nonzero, and saturates when it
|
||||
// has reached its maximum (this state is terminal).
|
||||
assign cnt_d = (ping_en && !(&cnt_q[k])) ? TimeoutCntDw'(1) :
|
||||
((cnt_q[k] > '0) && !(&cnt_q[k])) ? cnt_q[k] + 1'b1 :
|
||||
cnt_q[k];
|
||||
prim_flop #(
|
||||
.Width(TimeoutCntDw)
|
||||
) u_prim_flop (
|
||||
.clk_i,
|
||||
.rst_ni,
|
||||
.d_i(cnt_d),
|
||||
.q_o(cnt_q[k])
|
||||
);
|
||||
end
|
||||
prim_count #(
|
||||
.Width(TimeoutCntDw),
|
||||
.OutSelDnCnt(0), // count up
|
||||
.CntStyle(prim_count_pkg::DupCnt),
|
||||
// The escalation receiver behaves differently than other comportable IP. I.e., instead of
|
||||
// sending out an alert signal, this condition is handled internally in the alert handler.
|
||||
.EnableAlertTriggerSVA(0)
|
||||
) u_prim_count (
|
||||
.clk_i,
|
||||
.rst_ni,
|
||||
.clr_i(1'b0),
|
||||
.set_i(timeout_cnt_set),
|
||||
.set_cnt_i(TimeoutCntDw'(1)),
|
||||
.en_i(timeout_cnt_en),
|
||||
.step_i(TimeoutCntDw'(1)),
|
||||
.cnt_o(timeout_cnt),
|
||||
.err_o(timeout_cnt_error)
|
||||
);
|
||||
|
||||
// Escalation is asserted if
|
||||
// - requested via the escalation sender/receiver path,
|
||||
// - the ping monitor timeout is reached,
|
||||
// - the two ping monitor counters are in an inconsistent state.
|
||||
logic esc_req;
|
||||
prim_sec_anchor_buf #(
|
||||
.Width(1)
|
||||
) u_prim_buf_esc_req (
|
||||
.in_i(esc_req | (&cnt_q[0]) | (cnt_q[0] != cnt_q[1])),
|
||||
.in_i(esc_req || (&timeout_cnt) || timeout_cnt_error),
|
||||
.out_o(esc_req_o)
|
||||
);
|
||||
|
||||
|
@ -264,8 +270,8 @@ module prim_esc_receiver
|
|||
`ASSERT(EscEnCheck_A, esc_tx_i.esc_p && (esc_tx_i.esc_p ^ esc_tx_i.esc_n) && state_q != SigInt
|
||||
##1 esc_tx_i.esc_p && (esc_tx_i.esc_p ^ esc_tx_i.esc_n) |-> esc_req_o)
|
||||
// make sure the counter does not wrap around
|
||||
`ASSERT(EscCntWrap_A, &cnt_q[0] |=> cnt_q[0] != 0)
|
||||
`ASSERT(EscCntWrap_A, &timeout_cnt |=> timeout_cnt != 0)
|
||||
// if the counter expires, escalation should be asserted
|
||||
`ASSERT(EscCntEsc_A, &cnt_q[0] |-> esc_req_o)
|
||||
`ASSERT(EscCntEsc_A, &timeout_cnt |-> esc_req_o)
|
||||
|
||||
endmodule : prim_esc_receiver
|
||||
|
|
42
vendor/lowrisc_ip/ip/prim/rtl/prim_filter.sv
vendored
42
vendor/lowrisc_ip/ip/prim/rtl/prim_filter.sv
vendored
|
@ -10,32 +10,54 @@
|
|||
// new input must be opposite value from stored value for
|
||||
// #Cycles before switching to new value.
|
||||
|
||||
module prim_filter #(parameter int Cycles = 4) (
|
||||
input clk_i,
|
||||
input rst_ni,
|
||||
input enable_i,
|
||||
input filter_i,
|
||||
output filter_o
|
||||
module prim_filter #(
|
||||
// If this parameter is set, an additional 2-stage synchronizer will be
|
||||
// added at the input.
|
||||
parameter bit AsyncOn = 0,
|
||||
parameter int unsigned Cycles = 4
|
||||
) (
|
||||
input clk_i,
|
||||
input rst_ni,
|
||||
input enable_i,
|
||||
input filter_i,
|
||||
output logic filter_o
|
||||
);
|
||||
|
||||
logic [Cycles-1:0] stored_vector_q, stored_vector_d;
|
||||
logic stored_value_q, update_stored_value;
|
||||
logic unused_stored_vector_q_msb;
|
||||
|
||||
logic filter_synced;
|
||||
|
||||
if (AsyncOn) begin : gen_async
|
||||
// Run this through a 2 stage synchronizer to
|
||||
// prevent metastability.
|
||||
prim_flop_2sync #(
|
||||
.Width(1)
|
||||
) prim_flop_2sync (
|
||||
.clk_i,
|
||||
.rst_ni,
|
||||
.d_i(filter_i),
|
||||
.q_o(filter_synced)
|
||||
);
|
||||
end else begin : gen_sync
|
||||
assign filter_synced = filter_i;
|
||||
end
|
||||
|
||||
always_ff @(posedge clk_i or negedge rst_ni) begin
|
||||
if (!rst_ni) begin
|
||||
stored_value_q <= 1'b0;
|
||||
end else if (update_stored_value) begin
|
||||
stored_value_q <= filter_i;
|
||||
stored_value_q <= filter_synced;
|
||||
end
|
||||
end
|
||||
|
||||
assign stored_vector_d = {stored_vector_q[Cycles-2:0],filter_i};
|
||||
assign stored_vector_d = {stored_vector_q[Cycles-2:0],filter_synced};
|
||||
assign unused_stored_vector_q_msb = stored_vector_q[Cycles-1];
|
||||
|
||||
always_ff @(posedge clk_i or negedge rst_ni) begin
|
||||
if (!rst_ni) begin
|
||||
stored_vector_q <= {Cycles{1'b0}};
|
||||
stored_vector_q <= '0;
|
||||
end else begin
|
||||
stored_vector_q <= stored_vector_d;
|
||||
end
|
||||
|
@ -45,7 +67,7 @@ module prim_filter #(parameter int Cycles = 4) (
|
|||
(stored_vector_d == {Cycles{1'b0}}) |
|
||||
(stored_vector_d == {Cycles{1'b1}});
|
||||
|
||||
assign filter_o = enable_i ? stored_value_q : filter_i;
|
||||
assign filter_o = enable_i ? stored_value_q : filter_synced;
|
||||
|
||||
endmodule
|
||||
|
||||
|
|
57
vendor/lowrisc_ip/ip/prim/rtl/prim_filter_ctr.sv
vendored
57
vendor/lowrisc_ip/ip/prim/rtl/prim_filter_ctr.sv
vendored
|
@ -12,25 +12,45 @@
|
|||
// new input must be opposite value from stored value for
|
||||
// #Cycles before switching to new value.
|
||||
|
||||
module prim_filter_ctr #(parameter int unsigned Cycles = 4) (
|
||||
input clk_i,
|
||||
input rst_ni,
|
||||
input enable_i,
|
||||
input filter_i,
|
||||
output filter_o
|
||||
module prim_filter_ctr #(
|
||||
// If this parameter is set, an additional 2-stage synchronizer will be
|
||||
// added at the input.
|
||||
parameter bit AsyncOn = 0,
|
||||
parameter int unsigned CntWidth = 2
|
||||
) (
|
||||
input clk_i,
|
||||
input rst_ni,
|
||||
input enable_i,
|
||||
input filter_i,
|
||||
input [CntWidth-1:0] thresh_i,
|
||||
output logic filter_o
|
||||
);
|
||||
|
||||
localparam int unsigned CTR_WIDTH = $clog2(Cycles);
|
||||
localparam logic [CTR_WIDTH-1:0] CYCLESM1 = (CTR_WIDTH)'(Cycles-1);
|
||||
|
||||
logic [CTR_WIDTH-1:0] diff_ctr_q, diff_ctr_d;
|
||||
logic [CntWidth-1:0] diff_ctr_q, diff_ctr_d;
|
||||
logic filter_q, stored_value_q, update_stored_value;
|
||||
|
||||
logic filter_synced;
|
||||
|
||||
if (AsyncOn) begin : gen_async
|
||||
// Run this through a 2 stage synchronizer to
|
||||
// prevent metastability.
|
||||
prim_flop_2sync #(
|
||||
.Width(1)
|
||||
) prim_flop_2sync (
|
||||
.clk_i,
|
||||
.rst_ni,
|
||||
.d_i(filter_i),
|
||||
.q_o(filter_synced)
|
||||
);
|
||||
end else begin : gen_sync
|
||||
assign filter_synced = filter_i;
|
||||
end
|
||||
|
||||
always_ff @(posedge clk_i or negedge rst_ni) begin
|
||||
if (!rst_ni) begin
|
||||
filter_q <= 1'b0;
|
||||
end else begin
|
||||
filter_q <= filter_i;
|
||||
filter_q <= filter_synced;
|
||||
end
|
||||
end
|
||||
|
||||
|
@ -38,26 +58,25 @@ module prim_filter_ctr #(parameter int unsigned Cycles = 4) (
|
|||
if (!rst_ni) begin
|
||||
stored_value_q <= 1'b0;
|
||||
end else if (update_stored_value) begin
|
||||
stored_value_q <= filter_i;
|
||||
stored_value_q <= filter_synced;
|
||||
end
|
||||
end
|
||||
|
||||
always_ff @(posedge clk_i or negedge rst_ni) begin
|
||||
if (!rst_ni) begin
|
||||
diff_ctr_q <= {CTR_WIDTH{1'b0}};
|
||||
diff_ctr_q <= '0;
|
||||
end else begin
|
||||
diff_ctr_q <= diff_ctr_d;
|
||||
end
|
||||
end
|
||||
|
||||
// always look for differences, even if not filter enabled
|
||||
assign diff_ctr_d =
|
||||
(filter_i != filter_q) ? '0 : // restart
|
||||
(diff_ctr_q == CYCLESM1) ? CYCLESM1 : // saturate
|
||||
(diff_ctr_q + 1'b1); // count up
|
||||
assign update_stored_value = (diff_ctr_d == CYCLESM1);
|
||||
assign update_stored_value = (diff_ctr_d == thresh_i);
|
||||
assign diff_ctr_d = (filter_synced != filter_q) ? '0 : // restart
|
||||
(diff_ctr_q >= thresh_i) ? thresh_i : // saturate
|
||||
(diff_ctr_q + 1'b1); // count up
|
||||
|
||||
assign filter_o = enable_i ? stored_value_q : filter_i;
|
||||
assign filter_o = enable_i ? stored_value_q : filter_synced;
|
||||
|
||||
endmodule
|
||||
|
||||
|
|
|
@ -6,7 +6,7 @@
|
|||
// This may need to be moved to prim_generic if libraries have a specific cell
|
||||
// for synchronization
|
||||
|
||||
module prim_generic_flop_2sync #(
|
||||
module prim_flop_2sync #(
|
||||
parameter int Width = 16,
|
||||
parameter logic [Width-1:0] ResetValue = '0
|
||||
) (
|
||||
|
@ -16,6 +16,8 @@ module prim_generic_flop_2sync #(
|
|||
output logic [Width-1:0] q_o
|
||||
);
|
||||
|
||||
// TODO(#10432): Add CDC instrumentation for simulations
|
||||
|
||||
logic [Width-1:0] intq;
|
||||
|
||||
prim_flop #(
|
||||
|
@ -38,4 +40,4 @@ module prim_generic_flop_2sync #(
|
|||
.q_o
|
||||
);
|
||||
|
||||
endmodule
|
||||
endmodule : prim_flop_2sync
|
44
vendor/lowrisc_ip/ip/prim/rtl/prim_lc_sender.sv
vendored
44
vendor/lowrisc_ip/ip/prim/rtl/prim_lc_sender.sv
vendored
|
@ -11,6 +11,10 @@
|
|||
`include "prim_assert.sv"
|
||||
|
||||
module prim_lc_sender #(
|
||||
// This flops the output if set to 1.
|
||||
// In special cases where the sender is in the same clock domain as the receiver,
|
||||
// this can be set to 0. However, it is recommended to leave this at 1.
|
||||
parameter bit AsyncOn = 1,
|
||||
// 0: reset value is lc_ctrl_pkg::Off
|
||||
// 1: reset value is lc_ctrl_pkg::On
|
||||
parameter bit ResetValueIsOn = 0
|
||||
|
@ -27,15 +31,37 @@ module prim_lc_sender #(
|
|||
logic [lc_ctrl_pkg::TxWidth-1:0] lc_en, lc_en_out;
|
||||
assign lc_en = lc_ctrl_pkg::TxWidth'(lc_en_i);
|
||||
|
||||
prim_sec_anchor_flop #(
|
||||
.Width(lc_ctrl_pkg::TxWidth),
|
||||
.ResetValue(lc_ctrl_pkg::TxWidth'(ResetValue))
|
||||
) u_prim_flop (
|
||||
.clk_i,
|
||||
.rst_ni,
|
||||
.d_i ( lc_en ),
|
||||
.q_o ( lc_en_out )
|
||||
);
|
||||
if (AsyncOn) begin : gen_flops
|
||||
prim_sec_anchor_flop #(
|
||||
.Width(lc_ctrl_pkg::TxWidth),
|
||||
.ResetValue(lc_ctrl_pkg::TxWidth'(ResetValue))
|
||||
) u_prim_flop (
|
||||
.clk_i,
|
||||
.rst_ni,
|
||||
.d_i ( lc_en ),
|
||||
.q_o ( lc_en_out )
|
||||
);
|
||||
end else begin : gen_no_flops
|
||||
for (genvar k = 0; k < lc_ctrl_pkg::TxWidth; k++) begin : gen_bits
|
||||
prim_sec_anchor_buf u_prim_buf (
|
||||
.in_i(lc_en[k]),
|
||||
.out_o(lc_en_out[k])
|
||||
);
|
||||
end
|
||||
|
||||
// This unused companion logic helps remove lint errors
|
||||
// for modules where clock and reset are used for assertions only
|
||||
// or nothing at all.
|
||||
// This logic will be removed for sythesis since it is unloaded.
|
||||
lc_ctrl_pkg::lc_tx_t unused_logic;
|
||||
always_ff @(posedge clk_i or negedge rst_ni) begin
|
||||
if (!rst_ni) begin
|
||||
unused_logic <= lc_ctrl_pkg::Off;
|
||||
end else begin
|
||||
unused_logic <= lc_en_i;
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
assign lc_en_o = lc_ctrl_pkg::lc_tx_t'(lc_en_out);
|
||||
|
||||
|
|
17
vendor/lowrisc_ip/ip/prim/rtl/prim_lc_sync.sv
vendored
17
vendor/lowrisc_ip/ip/prim/rtl/prim_lc_sync.sv
vendored
|
@ -47,10 +47,19 @@ module prim_lc_sync #(
|
|||
.q_o(lc_en)
|
||||
);
|
||||
end else begin : gen_no_flops
|
||||
logic unused_clk;
|
||||
logic unused_rst;
|
||||
assign unused_clk = clk_i;
|
||||
assign unused_rst = rst_ni;
|
||||
// This unused companion logic helps remove lint errors
|
||||
// for modules where clock and reset are used for assertions only
|
||||
// or nothing at all.
|
||||
// This logic will be removed for sythesis since it is unloaded.
|
||||
lc_ctrl_pkg::lc_tx_t unused_logic;
|
||||
always_ff @(posedge clk_i or negedge rst_ni) begin
|
||||
if (!rst_ni) begin
|
||||
unused_logic <= lc_ctrl_pkg::Off;
|
||||
end else begin
|
||||
unused_logic <= lc_en_i;
|
||||
end
|
||||
end
|
||||
|
||||
assign lc_en = lc_en_i;
|
||||
end
|
||||
|
||||
|
|
147
vendor/lowrisc_ip/ip/prim/rtl/prim_max_tree.sv
vendored
Normal file
147
vendor/lowrisc_ip/ip/prim/rtl/prim_max_tree.sv
vendored
Normal file
|
@ -0,0 +1,147 @@
|
|||
// Copyright lowRISC contributors.
|
||||
// Licensed under the Apache License, Version 2.0, see LICENSE for details.
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
//
|
||||
// The module implements a binary tree to find the maximal entry. the solution
|
||||
// has O(N) area and O(log(N)) delay complexity, and thus scales well with
|
||||
// many input sources.
|
||||
//
|
||||
// Note that only input values marked as "valid" are respected in the maximum computation.
|
||||
// If there are multiple valid inputs with the same value, the tree will always select the input
|
||||
// with the smallest index.
|
||||
//
|
||||
// If none of the input values are valid, the output index will be 0 and the output value will
|
||||
// be equal to the input value at index 0.
|
||||
|
||||
|
||||
`include "prim_assert.sv"
|
||||
|
||||
module prim_max_tree #(
|
||||
parameter int NumSrc = 32,
|
||||
parameter int Width = 8,
|
||||
// Derived parameters
|
||||
localparam int SrcWidth = $clog2(NumSrc)
|
||||
) (
|
||||
// The module is combinational - the clock and reset are only used for assertions.
|
||||
input clk_i,
|
||||
input rst_ni,
|
||||
input [NumSrc-1:0][Width-1:0] values_i, // Input values
|
||||
input [NumSrc-1:0] valid_i, // Input valid bits
|
||||
output logic [Width-1:0] max_value_o, // Maximum value
|
||||
output logic [SrcWidth-1:0] max_idx_o, // Index of the maximum value
|
||||
output logic max_valid_o // Whether any of the inputs is valid
|
||||
);
|
||||
|
||||
///////////////////////
|
||||
// Binary tree logic //
|
||||
///////////////////////
|
||||
|
||||
// This only works with 2 or more sources.
|
||||
`ASSERT_INIT(NumSources_A, NumSrc >= 2)
|
||||
|
||||
// Align to powers of 2 for simplicity.
|
||||
// A full binary tree with N levels has 2**N + 2**N-1 nodes.
|
||||
localparam int NumLevels = $clog2(NumSrc);
|
||||
logic [2**(NumLevels+1)-2:0] vld_tree;
|
||||
logic [2**(NumLevels+1)-2:0][SrcWidth-1:0] idx_tree;
|
||||
logic [2**(NumLevels+1)-2:0][Width-1:0] max_tree;
|
||||
|
||||
for (genvar level = 0; level < NumLevels+1; level++) begin : gen_tree
|
||||
//
|
||||
// level+1 C0 C1 <- "Base1" points to the first node on "level+1",
|
||||
// \ / these nodes are the children of the nodes one level below
|
||||
// level Pa <- "Base0", points to the first node on "level",
|
||||
// these nodes are the parents of the nodes one level above
|
||||
//
|
||||
// hence we have the following indices for the paPa, C0, C1 nodes:
|
||||
// Pa = 2**level - 1 + offset = Base0 + offset
|
||||
// C0 = 2**(level+1) - 1 + 2*offset = Base1 + 2*offset
|
||||
// C1 = 2**(level+1) - 1 + 2*offset + 1 = Base1 + 2*offset + 1
|
||||
//
|
||||
localparam int Base0 = (2**level)-1;
|
||||
localparam int Base1 = (2**(level+1))-1;
|
||||
|
||||
for (genvar offset = 0; offset < 2**level; offset++) begin : gen_level
|
||||
localparam int Pa = Base0 + offset;
|
||||
localparam int C0 = Base1 + 2*offset;
|
||||
localparam int C1 = Base1 + 2*offset + 1;
|
||||
|
||||
// This assigns the input values, their corresponding IDs and valid signals to the tree leafs.
|
||||
if (level == NumLevels) begin : gen_leafs
|
||||
if (offset < NumSrc) begin : gen_assign
|
||||
assign vld_tree[Pa] = valid_i[offset];
|
||||
assign idx_tree[Pa] = offset;
|
||||
assign max_tree[Pa] = values_i[offset];
|
||||
end else begin : gen_tie_off
|
||||
assign vld_tree[Pa] = '0;
|
||||
assign idx_tree[Pa] = '0;
|
||||
assign max_tree[Pa] = '0;
|
||||
end
|
||||
// This creates the node assignments.
|
||||
end else begin : gen_nodes
|
||||
logic sel; // Local helper variable
|
||||
// In case only one of the parents is valid, forward that one
|
||||
// In case both parents are valid, forward the one with higher value
|
||||
assign sel = (~vld_tree[C0] & vld_tree[C1]) |
|
||||
(vld_tree[C0] & vld_tree[C1] & logic'(max_tree[C1] > max_tree[C0]));
|
||||
// Forwarding muxes
|
||||
// Note: these ternaries have triggered a synthesis bug in Vivado versions older
|
||||
// than 2020.2. If the problem resurfaces again, have a look at issue #1408.
|
||||
assign vld_tree[Pa] = (sel) ? vld_tree[C1] : vld_tree[C0];
|
||||
assign idx_tree[Pa] = (sel) ? idx_tree[C1] : idx_tree[C0];
|
||||
assign max_tree[Pa] = (sel) ? max_tree[C1] : max_tree[C0];
|
||||
end
|
||||
end : gen_level
|
||||
end : gen_tree
|
||||
|
||||
|
||||
// The results can be found at the tree root
|
||||
assign max_valid_o = vld_tree[0];
|
||||
assign max_idx_o = idx_tree[0];
|
||||
assign max_value_o = max_tree[0];
|
||||
|
||||
////////////////
|
||||
// Assertions //
|
||||
////////////////
|
||||
|
||||
`ifdef INC_ASSERT
|
||||
// Helper functions for assertions below.
|
||||
function automatic logic [Width-1:0] max_value (input logic [NumSrc-1:0][Width-1:0] values_i,
|
||||
input logic [NumSrc-1:0] valid_i);
|
||||
logic [Width-1:0] value = '0;
|
||||
for (int k = 0; k < NumSrc; k++) begin
|
||||
if (valid_i[k] && values_i[k] > value) begin
|
||||
value = values_i[k];
|
||||
end
|
||||
end
|
||||
return value;
|
||||
endfunction : max_value
|
||||
|
||||
function automatic logic [SrcWidth-1:0] max_idx (input logic [NumSrc-1:0][Width-1:0] values_i,
|
||||
input logic [NumSrc-1:0] valid_i);
|
||||
logic [Width-1:0] value = '0;
|
||||
logic [SrcWidth-1:0] idx = '0;
|
||||
for (int k = NumSrc-1; k >= 0; k--) begin
|
||||
if (valid_i[k] && values_i[k] >= value) begin
|
||||
value = values_i[k];
|
||||
idx = k;
|
||||
end
|
||||
end
|
||||
return idx;
|
||||
endfunction : max_idx
|
||||
|
||||
logic [Width-1:0] max_value_exp;
|
||||
logic [SrcWidth-1:0] max_idx_exp;
|
||||
assign max_value_exp = max_value(values_i, valid_i);
|
||||
assign max_idx_exp = max_idx(values_i, valid_i);
|
||||
|
||||
// TODO(10588): Below syntax is not supported in xcelium, track xcelium cases #46591452.
|
||||
// `ASSERT(ValidInImpliesValidOut_A, |valid_i <-> max_valid_o)
|
||||
`ASSERT(ValidInImpliesValidOut_A, |valid_i === max_valid_o)
|
||||
`ASSERT(MaxComputation_A, max_valid_o |-> max_value_o == max_value_exp)
|
||||
`ASSERT(MaxComputationInvalid_A, !max_valid_o |-> max_value_o == values_i[0])
|
||||
`ASSERT(MaxIndexComputation_A, max_valid_o |-> max_idx_o == max_idx_exp)
|
||||
`ASSERT(MaxIndexComputationInvalid_A, !max_valid_o |-> max_idx_o == '0)
|
||||
`endif
|
||||
|
||||
endmodule : prim_max_tree
|
|
@ -15,6 +15,12 @@
|
|||
module prim_mubi12_sender
|
||||
import prim_mubi_pkg::*;
|
||||
#(
|
||||
// This flops the output if set to 1.
|
||||
// In special cases where the sender is in the same clock domain as the receiver,
|
||||
// this can be set to 0. However, it is recommended to leave this at 1.
|
||||
parameter bit AsyncOn = 1,
|
||||
// Enable anchor buffer
|
||||
parameter bit EnSecBuf = 0,
|
||||
// Reset value for the sender flops
|
||||
parameter mubi12_t ResetValue = MuBi12False
|
||||
) (
|
||||
|
@ -24,18 +30,57 @@ module prim_mubi12_sender
|
|||
output mubi12_t mubi_o
|
||||
);
|
||||
|
||||
logic [MuBi12Width-1:0] mubi, mubi_out;
|
||||
logic [MuBi12Width-1:0] mubi, mubi_int, mubi_out;
|
||||
assign mubi = MuBi12Width'(mubi_i);
|
||||
|
||||
prim_flop #(
|
||||
.Width(MuBi12Width),
|
||||
.ResetValue(MuBi12Width'(ResetValue))
|
||||
) u_prim_flop (
|
||||
.clk_i,
|
||||
.rst_ni,
|
||||
.d_i ( mubi ),
|
||||
.q_o ( mubi_out )
|
||||
);
|
||||
// first generation block decides whether a flop should be present
|
||||
if (AsyncOn) begin : gen_flops
|
||||
prim_flop #(
|
||||
.Width(MuBi12Width),
|
||||
.ResetValue(MuBi12Width'(ResetValue))
|
||||
) u_prim_flop (
|
||||
.clk_i,
|
||||
.rst_ni,
|
||||
.d_i ( mubi ),
|
||||
.q_o ( mubi_int )
|
||||
);
|
||||
end else begin : gen_no_flops
|
||||
assign mubi_int = mubi;
|
||||
|
||||
// This unused companion logic helps remove lint errors
|
||||
// for modules where clock and reset are used for assertions only
|
||||
// This logic will be removed for sythesis since it is unloaded.
|
||||
mubi12_t unused_logic;
|
||||
always_ff @(posedge clk_i or negedge rst_ni) begin
|
||||
if (!rst_ni) begin
|
||||
unused_logic <= MuBi12False;
|
||||
end else begin
|
||||
unused_logic <= mubi_i;
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
// second generation block determines output buffer type
|
||||
// 1. If EnSecBuf -> always leads to a sec buffer regardless of first block
|
||||
// 2. If not EnSecBuf and not AsyncOn -> use normal buffer
|
||||
// 3. If not EnSecBuf and AsyncOn -> feed through
|
||||
if (EnSecBuf) begin : gen_sec_buf
|
||||
prim_sec_anchor_buf #(
|
||||
.Width(12)
|
||||
) u_prim_sec_buf (
|
||||
.in_i(mubi_int),
|
||||
.out_o(mubi_out)
|
||||
);
|
||||
end else if (!AsyncOn) begin : gen_prim_buf
|
||||
prim_buf #(
|
||||
.Width(12)
|
||||
) u_prim_buf (
|
||||
.in_i(mubi_int),
|
||||
.out_o(mubi_out)
|
||||
);
|
||||
end else begin : gen_feedthru
|
||||
assign mubi_out = mubi_int;
|
||||
end
|
||||
|
||||
assign mubi_o = mubi12_t'(mubi_out);
|
||||
|
||||
|
|
|
@ -105,10 +105,19 @@ module prim_mubi12_sync
|
|||
assign mubi = mubi_sync;
|
||||
end
|
||||
end else begin : gen_no_flops
|
||||
logic unused_clk;
|
||||
logic unused_rst;
|
||||
assign unused_clk = clk_i;
|
||||
assign unused_rst = rst_ni;
|
||||
|
||||
// This unused companion logic helps remove lint errors
|
||||
// for modules where clock and reset are used for assertions only
|
||||
// This logic will be removed for synthesis since it is unloaded.
|
||||
mubi12_t unused_logic;
|
||||
always_ff @(posedge clk_i or negedge rst_ni) begin
|
||||
if (!rst_ni) begin
|
||||
unused_logic <= MuBi12False;
|
||||
end else begin
|
||||
unused_logic <= mubi_i;
|
||||
end
|
||||
end
|
||||
|
||||
assign mubi = MuBi12Width'(mubi_i);
|
||||
end
|
||||
|
||||
|
|
|
@ -15,6 +15,12 @@
|
|||
module prim_mubi16_sender
|
||||
import prim_mubi_pkg::*;
|
||||
#(
|
||||
// This flops the output if set to 1.
|
||||
// In special cases where the sender is in the same clock domain as the receiver,
|
||||
// this can be set to 0. However, it is recommended to leave this at 1.
|
||||
parameter bit AsyncOn = 1,
|
||||
// Enable anchor buffer
|
||||
parameter bit EnSecBuf = 0,
|
||||
// Reset value for the sender flops
|
||||
parameter mubi16_t ResetValue = MuBi16False
|
||||
) (
|
||||
|
@ -24,18 +30,57 @@ module prim_mubi16_sender
|
|||
output mubi16_t mubi_o
|
||||
);
|
||||
|
||||
logic [MuBi16Width-1:0] mubi, mubi_out;
|
||||
logic [MuBi16Width-1:0] mubi, mubi_int, mubi_out;
|
||||
assign mubi = MuBi16Width'(mubi_i);
|
||||
|
||||
prim_flop #(
|
||||
.Width(MuBi16Width),
|
||||
.ResetValue(MuBi16Width'(ResetValue))
|
||||
) u_prim_flop (
|
||||
.clk_i,
|
||||
.rst_ni,
|
||||
.d_i ( mubi ),
|
||||
.q_o ( mubi_out )
|
||||
);
|
||||
// first generation block decides whether a flop should be present
|
||||
if (AsyncOn) begin : gen_flops
|
||||
prim_flop #(
|
||||
.Width(MuBi16Width),
|
||||
.ResetValue(MuBi16Width'(ResetValue))
|
||||
) u_prim_flop (
|
||||
.clk_i,
|
||||
.rst_ni,
|
||||
.d_i ( mubi ),
|
||||
.q_o ( mubi_int )
|
||||
);
|
||||
end else begin : gen_no_flops
|
||||
assign mubi_int = mubi;
|
||||
|
||||
// This unused companion logic helps remove lint errors
|
||||
// for modules where clock and reset are used for assertions only
|
||||
// This logic will be removed for sythesis since it is unloaded.
|
||||
mubi16_t unused_logic;
|
||||
always_ff @(posedge clk_i or negedge rst_ni) begin
|
||||
if (!rst_ni) begin
|
||||
unused_logic <= MuBi16False;
|
||||
end else begin
|
||||
unused_logic <= mubi_i;
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
// second generation block determines output buffer type
|
||||
// 1. If EnSecBuf -> always leads to a sec buffer regardless of first block
|
||||
// 2. If not EnSecBuf and not AsyncOn -> use normal buffer
|
||||
// 3. If not EnSecBuf and AsyncOn -> feed through
|
||||
if (EnSecBuf) begin : gen_sec_buf
|
||||
prim_sec_anchor_buf #(
|
||||
.Width(16)
|
||||
) u_prim_sec_buf (
|
||||
.in_i(mubi_int),
|
||||
.out_o(mubi_out)
|
||||
);
|
||||
end else if (!AsyncOn) begin : gen_prim_buf
|
||||
prim_buf #(
|
||||
.Width(16)
|
||||
) u_prim_buf (
|
||||
.in_i(mubi_int),
|
||||
.out_o(mubi_out)
|
||||
);
|
||||
end else begin : gen_feedthru
|
||||
assign mubi_out = mubi_int;
|
||||
end
|
||||
|
||||
assign mubi_o = mubi16_t'(mubi_out);
|
||||
|
||||
|
|
|
@ -105,10 +105,19 @@ module prim_mubi16_sync
|
|||
assign mubi = mubi_sync;
|
||||
end
|
||||
end else begin : gen_no_flops
|
||||
logic unused_clk;
|
||||
logic unused_rst;
|
||||
assign unused_clk = clk_i;
|
||||
assign unused_rst = rst_ni;
|
||||
|
||||
// This unused companion logic helps remove lint errors
|
||||
// for modules where clock and reset are used for assertions only
|
||||
// This logic will be removed for synthesis since it is unloaded.
|
||||
mubi16_t unused_logic;
|
||||
always_ff @(posedge clk_i or negedge rst_ni) begin
|
||||
if (!rst_ni) begin
|
||||
unused_logic <= MuBi16False;
|
||||
end else begin
|
||||
unused_logic <= mubi_i;
|
||||
end
|
||||
end
|
||||
|
||||
assign mubi = MuBi16Width'(mubi_i);
|
||||
end
|
||||
|
||||
|
|
|
@ -15,6 +15,12 @@
|
|||
module prim_mubi4_sender
|
||||
import prim_mubi_pkg::*;
|
||||
#(
|
||||
// This flops the output if set to 1.
|
||||
// In special cases where the sender is in the same clock domain as the receiver,
|
||||
// this can be set to 0. However, it is recommended to leave this at 1.
|
||||
parameter bit AsyncOn = 1,
|
||||
// Enable anchor buffer
|
||||
parameter bit EnSecBuf = 0,
|
||||
// Reset value for the sender flops
|
||||
parameter mubi4_t ResetValue = MuBi4False
|
||||
) (
|
||||
|
@ -24,18 +30,57 @@ module prim_mubi4_sender
|
|||
output mubi4_t mubi_o
|
||||
);
|
||||
|
||||
logic [MuBi4Width-1:0] mubi, mubi_out;
|
||||
logic [MuBi4Width-1:0] mubi, mubi_int, mubi_out;
|
||||
assign mubi = MuBi4Width'(mubi_i);
|
||||
|
||||
prim_flop #(
|
||||
.Width(MuBi4Width),
|
||||
.ResetValue(MuBi4Width'(ResetValue))
|
||||
) u_prim_flop (
|
||||
.clk_i,
|
||||
.rst_ni,
|
||||
.d_i ( mubi ),
|
||||
.q_o ( mubi_out )
|
||||
);
|
||||
// first generation block decides whether a flop should be present
|
||||
if (AsyncOn) begin : gen_flops
|
||||
prim_flop #(
|
||||
.Width(MuBi4Width),
|
||||
.ResetValue(MuBi4Width'(ResetValue))
|
||||
) u_prim_flop (
|
||||
.clk_i,
|
||||
.rst_ni,
|
||||
.d_i ( mubi ),
|
||||
.q_o ( mubi_int )
|
||||
);
|
||||
end else begin : gen_no_flops
|
||||
assign mubi_int = mubi;
|
||||
|
||||
// This unused companion logic helps remove lint errors
|
||||
// for modules where clock and reset are used for assertions only
|
||||
// This logic will be removed for sythesis since it is unloaded.
|
||||
mubi4_t unused_logic;
|
||||
always_ff @(posedge clk_i or negedge rst_ni) begin
|
||||
if (!rst_ni) begin
|
||||
unused_logic <= MuBi4False;
|
||||
end else begin
|
||||
unused_logic <= mubi_i;
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
// second generation block determines output buffer type
|
||||
// 1. If EnSecBuf -> always leads to a sec buffer regardless of first block
|
||||
// 2. If not EnSecBuf and not AsyncOn -> use normal buffer
|
||||
// 3. If not EnSecBuf and AsyncOn -> feed through
|
||||
if (EnSecBuf) begin : gen_sec_buf
|
||||
prim_sec_anchor_buf #(
|
||||
.Width(4)
|
||||
) u_prim_sec_buf (
|
||||
.in_i(mubi_int),
|
||||
.out_o(mubi_out)
|
||||
);
|
||||
end else if (!AsyncOn) begin : gen_prim_buf
|
||||
prim_buf #(
|
||||
.Width(4)
|
||||
) u_prim_buf (
|
||||
.in_i(mubi_int),
|
||||
.out_o(mubi_out)
|
||||
);
|
||||
end else begin : gen_feedthru
|
||||
assign mubi_out = mubi_int;
|
||||
end
|
||||
|
||||
assign mubi_o = mubi4_t'(mubi_out);
|
||||
|
||||
|
|
17
vendor/lowrisc_ip/ip/prim/rtl/prim_mubi4_sync.sv
vendored
17
vendor/lowrisc_ip/ip/prim/rtl/prim_mubi4_sync.sv
vendored
|
@ -105,10 +105,19 @@ module prim_mubi4_sync
|
|||
assign mubi = mubi_sync;
|
||||
end
|
||||
end else begin : gen_no_flops
|
||||
logic unused_clk;
|
||||
logic unused_rst;
|
||||
assign unused_clk = clk_i;
|
||||
assign unused_rst = rst_ni;
|
||||
|
||||
// This unused companion logic helps remove lint errors
|
||||
// for modules where clock and reset are used for assertions only
|
||||
// This logic will be removed for synthesis since it is unloaded.
|
||||
mubi4_t unused_logic;
|
||||
always_ff @(posedge clk_i or negedge rst_ni) begin
|
||||
if (!rst_ni) begin
|
||||
unused_logic <= MuBi4False;
|
||||
end else begin
|
||||
unused_logic <= mubi_i;
|
||||
end
|
||||
end
|
||||
|
||||
assign mubi = MuBi4Width'(mubi_i);
|
||||
end
|
||||
|
||||
|
|
|
@ -15,6 +15,12 @@
|
|||
module prim_mubi8_sender
|
||||
import prim_mubi_pkg::*;
|
||||
#(
|
||||
// This flops the output if set to 1.
|
||||
// In special cases where the sender is in the same clock domain as the receiver,
|
||||
// this can be set to 0. However, it is recommended to leave this at 1.
|
||||
parameter bit AsyncOn = 1,
|
||||
// Enable anchor buffer
|
||||
parameter bit EnSecBuf = 0,
|
||||
// Reset value for the sender flops
|
||||
parameter mubi8_t ResetValue = MuBi8False
|
||||
) (
|
||||
|
@ -24,18 +30,57 @@ module prim_mubi8_sender
|
|||
output mubi8_t mubi_o
|
||||
);
|
||||
|
||||
logic [MuBi8Width-1:0] mubi, mubi_out;
|
||||
logic [MuBi8Width-1:0] mubi, mubi_int, mubi_out;
|
||||
assign mubi = MuBi8Width'(mubi_i);
|
||||
|
||||
prim_flop #(
|
||||
.Width(MuBi8Width),
|
||||
.ResetValue(MuBi8Width'(ResetValue))
|
||||
) u_prim_flop (
|
||||
.clk_i,
|
||||
.rst_ni,
|
||||
.d_i ( mubi ),
|
||||
.q_o ( mubi_out )
|
||||
);
|
||||
// first generation block decides whether a flop should be present
|
||||
if (AsyncOn) begin : gen_flops
|
||||
prim_flop #(
|
||||
.Width(MuBi8Width),
|
||||
.ResetValue(MuBi8Width'(ResetValue))
|
||||
) u_prim_flop (
|
||||
.clk_i,
|
||||
.rst_ni,
|
||||
.d_i ( mubi ),
|
||||
.q_o ( mubi_int )
|
||||
);
|
||||
end else begin : gen_no_flops
|
||||
assign mubi_int = mubi;
|
||||
|
||||
// This unused companion logic helps remove lint errors
|
||||
// for modules where clock and reset are used for assertions only
|
||||
// This logic will be removed for sythesis since it is unloaded.
|
||||
mubi8_t unused_logic;
|
||||
always_ff @(posedge clk_i or negedge rst_ni) begin
|
||||
if (!rst_ni) begin
|
||||
unused_logic <= MuBi8False;
|
||||
end else begin
|
||||
unused_logic <= mubi_i;
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
// second generation block determines output buffer type
|
||||
// 1. If EnSecBuf -> always leads to a sec buffer regardless of first block
|
||||
// 2. If not EnSecBuf and not AsyncOn -> use normal buffer
|
||||
// 3. If not EnSecBuf and AsyncOn -> feed through
|
||||
if (EnSecBuf) begin : gen_sec_buf
|
||||
prim_sec_anchor_buf #(
|
||||
.Width(8)
|
||||
) u_prim_sec_buf (
|
||||
.in_i(mubi_int),
|
||||
.out_o(mubi_out)
|
||||
);
|
||||
end else if (!AsyncOn) begin : gen_prim_buf
|
||||
prim_buf #(
|
||||
.Width(8)
|
||||
) u_prim_buf (
|
||||
.in_i(mubi_int),
|
||||
.out_o(mubi_out)
|
||||
);
|
||||
end else begin : gen_feedthru
|
||||
assign mubi_out = mubi_int;
|
||||
end
|
||||
|
||||
assign mubi_o = mubi8_t'(mubi_out);
|
||||
|
||||
|
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Add table
Add a link
Reference in a new issue