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

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

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

View file

@ -9,6 +9,6 @@
upstream:
{
url: https://github.com/lowRISC/opentitan
rev: be1359d27d0e826e28e6611f318c286253cd05f1
rev: 7c4f8b3fde4bb625ac3330ff52d3f66507190fe5
}
}

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

@ -46,19 +46,7 @@ void MemArea::Write(uint32_t word_offset,
uint32_t phys_addr = ToPhysAddr(dst_word);
WriteBuffer(minibuf, data, i * width_byte_, dst_word);
// Both ToPhysAddr and WriteBuffer might set the scope with `SVScoped` so
// only construct `SVScoped` once they've both been called so they don't
// interact causing incorrect relative path behaviour. If this fails to set
// scope, it will throw an error which should be caught at this function's
// callsite.
SVScoped scoped(scope_);
if (!simutil_set_mem(phys_addr, (svBitVecVal *)minibuf)) {
std::ostringstream oss;
oss << "Could not set memory at byte offset 0x" << std::hex
<< dst_word * width_byte_ << ".";
throw std::runtime_error(oss.str());
}
WriteFromMinibuf(phys_addr, minibuf, dst_word);
}
}
@ -122,3 +110,14 @@ void MemArea::ReadToMinibuf(uint8_t *minibuf, uint32_t phys_addr) const {
throw std::runtime_error(oss.str());
}
}
void MemArea::WriteFromMinibuf(uint32_t phys_addr, const uint8_t *minibuf,
uint32_t dst_word) const {
SVScoped scoped(scope_);
if (!simutil_set_mem(phys_addr, (const svBitVecVal *)minibuf)) {
std::ostringstream oss;
oss << "Could not set memory at byte offset 0x" << std::hex
<< dst_word * width_byte_ << ".";
throw std::runtime_error(oss.str());
}
}

View file

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

View file

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

View file

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

View file

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

View file

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

View file

@ -30,7 +30,6 @@
tests: [
{
name: prim_esc_test
run_opts: ["+test_timeout_ns=1_000"]
}
]

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View 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."

View file

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

View 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*"

View 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."

View 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."

View file

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

View 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'"

View 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."

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

View 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*"

View file

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

View 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'"

View 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."

View file

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

View 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

View file

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

View file

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

View file

@ -24,6 +24,9 @@ filesets:
depend:
# common waivers
- lowrisc:lint:common
files:
- lint/prim_double_lfsr.waiver
file_type: waiver
files_veriblelint_waiver:
depend:

View file

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

View file

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

View 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

View file

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

View 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

View file

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

View file

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

View file

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

View file

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

View file

@ -0,0 +1,29 @@
// Copyright lowRISC contributors.
// Licensed under the Apache License, Version 2.0, see LICENSE for details.
// SPDX-License-Identifier: Apache-2.0
// // 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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View 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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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