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

* [dvsim] Introduce {self_dir} as variable (Philipp Wagner)
* [dvsim] Small cleanups (Philipp Wagner)
* [prim_lfsr] Minor lint fix (Michael Schaffner)
* [dv] Update sec_cm testplan (Weicai Yang)
* [prim/lint] Move waiver to correct waiver file (Michael Schaffner)
* [prim_assert] Relocate waivers to dedicated prim_assert.waiver file
  (Michael Schaffner)
* [alert_handler] Lint fixes and waiver updates (Michael Schaffner)
* [prim_lc_receiver] Add parameter to select reset value (Michael
  Schaffner)
* [lint] Add lint waiver for IP regfiles with shadow resets (Michael
  Schaffner)
* [fpv] Fix Verible lint errors (Philipp Wagner)
* [prim_lfsr] Minor lint fixes (Timothy Chen)
* [clkmgr] Fix measurement control CDC (Timothy Chen)
* [fpv/prim_counter] Pad one bit to include overflow case (Cindy Chen)
* [fpv] Fix issue lowRISC#8371 (Zeeshan Rafique)
* [flash_ctrl] Flash ctrl security hardening (Timothy Chen)
* [dv] Fix CI error (Cindy Chen)
* [prim_alert_*] Extend SVAs for FPV (Michael Schaffner)
* [prim_alert_*] Update DV TB to respect initialization timing
  (Michael Schaffner)
* [prim_alert_rxtx_fpv] Update FPV environment and fix SVAs (Michael
  Schaffner)
* [prim_alert_sender] Update sender to support in-band reset mechanism
  (Michael Schaffner)
* [prim_alert_sender] Simplify sender and clear ping req upon sigint
  (Michael Schaffner)
* [prim_lc_sender] Add option to select reset value (Michael
  Schaffner)
* [prim] Correct assertion valid term (Timothy Chen)
* [prim_lc_combine] Align behavior of lc combine with mubi functions
  (Michael Schaffner)
* [fpv/tool] Support GUI mode on dvsim (Cindy Chen)
* [prim_lfsr] Further permutation refinements for SBox layer (Michael
  Schaffner)
* [dv/shadow_reg] Shadow register write by field (Cindy Chen)
* [prim] Fix the edge type (Eunchan Kim)
* [checklist] Updates to checklist for D2 status (Tom Roberts)
* [prim_mubi_pkg] Add a generic multibit type and associated functions
  (Michael Schaffner)
* [prim] Minor fix and clarification to prim_count (Timothy Chen)
* [keymgr/dv] Update testplan and covergroup plan (Weicai Yang)
* [prim_lc_combine] Fix parameterization error (Michael Schaffner)
* [fpv/prim_count] Small update on prim_count assertions (Cindy Chen)
* [dv] Add ip_name in reg_block (Weicai Yang)
* [keymgr] Finalize keymgr hardening (Timothy Chen)
* [prim_lc_combine] Add a prim to compute logical AND/OR for LC
  signals (Michael Schaffner)
* [dv] Remove common_cov_excl.el from unr.cfg (Weicai Yang)
* [dv/top_level] Loop through the SW test multiple times (Cindy Chen)
* [flash_ctrl] Various clean-up and updates (Timothy Chen)
* [prim] Change prim_reg_cdc assertions (Timothy Chen)
* [prim, keymgr] Migrate keymgr_cnt to prim_count (Timothy Chen)
* [sw dv] Multi-site support for Verilator (Martin Lueker-Boden)
* [dv/csr] Update write exclusion wdata value (Cindy Chen)
* [dv/dv_base_reg] remove debug display (Cindy Chen)
* [dv/shadow_reg] Fix alert shadow_reg regression error (Cindy Chen)
* [top] Integrate ast into fpga (Timothy Chen)
* [prim_lfsr] Improve statistics of non-linear output (Michael
  Schaffner)
* [prim_esc_receiver] Fix response toggling corner case (Michael
  Schaffner)
* option to use partner ast_pkg (Sharon Topaz)
* [dv/prim_esc] Double the ping timeout cycles (Cindy Chen)
* [dv] Use sed to add -elfile for each excl file (Weicai Yang)
* [dv] Fix coverage report error (Weicai Yang)
* [dv] Update common exclusion file (Weicai Yang)
* [dv/prim_esc] Improve FSM coverage (Cindy Chen)
* [reggen] Add a check to limit the swaccess type for shadow regs
  (Michael Schaffner)
* [prim_subreg_shadow] Fix for W1S/W0C corner case (Michael Schaffner)
* [prim_subreg_shadow] Disallow phase updates when storage err is
  present (Michael Schaffner)
* [dvsim] Add passing count by milestone in reports (Srikrishna Iyer)
* [dv/tool] Include toggle coverage for prim_alert_sender in
  cover_reg_top (Cindy Chen)
* [clkmgr] Harden clock manager through frequency measurements
  (Timothy Chen)
* [dv] Only enable VCS -kdb when dumping waves (Weicai Yang)
* [dv] Fix shadow reg (Weicai Yang)
* [dvsim] Allow non-integral values of --reseed-multiplier (Rupert
  Swarbrick)
* [ast] Fixes for various ast issues (Timothy Chen)
* [prim_esc_receiver] Assert escalation in case of sigint error
  (Michael Schaffner)
* [prim_esc_receiver] Minor signal renaming for consistency (Michael
  Schaffner)
* [dv/alert_handler] Support shadow register sequence (Cindy Chen)
* [verilator] Use FileSz rather than MemSz when flattening ELF files
  (Michael Munday)
* [prim_subreg_shadow] Only assert QE when committed_reg is written
  (Michael Schaffner)
* [dv,verilator] Round up SV_MEM_WIDTH_BYTES to a multiple of 4
  (Rupert Swarbrick)
* [prim] Add missing include (Pirmin Vogel)
* [dv/cover_cfg] Exclude prim_alert/esc from xcelium (Cindy Chen)
* [dv/cover_cfg] Exclude prim_alert/esc pairs (Cindy Chen)
* [clkmgr] Use local BUFHCE clock gates on FPGA (Pirmin Vogel)
* [prim_prince] Mark "leaf" functions in prince_ref.h as static inline
  (Rupert Swarbrick)
* [dv/shadow_reg] Check status after shadow_reg write (Cindy Chen)
* [dv/shadwo_reg] Shadow reg common sequence update (Cindy Chen)
* [otp_ctrl/lc_ctrl] Add 32bit OTP vendor test ctrl/status regs to LC
  TAP (Michael Schaffner)
* [otp_ctrl] Add VENDOR_TEST partition (Michael Schaffner)
* [prim] Edge Detector (Eunchan Kim)
* [prim_diff_decode] Fix asynchronous assertions (Michael Schaffner)
* [spi_device] Instantiate Upload module (Eunchan Kim)
* [dv] Add sv_flist_gen_flags HJson var for FuseSoc (Srikrishna Iyer)
* [dv, xcelium] Properly pass excl files to IMC (Srikrishna Iyer)
* [reg] Fix shadow reg update during storage error (Timothy Chen)
* [regfile] Refactor cdc handling to the reg level (Timothy Chen)
* [dv/prim_esc] Add a testplan and increase coverage (Cindy Chen)
* [dv] Update TLUL and EDN frequency (Weicai Yang)
* [rstmgr, top] Add support for shadow resets (Timothy Chen)
* [dv] Update Xcelium cover ccf (Srikrishna Iyer)
* [dv] reduce seeds for CSR tests (Weicai Yang)
* [usb/top] Remove AND gates on non-AON domain and rename 3.3V signal
  (Michael Schaffner)
* [dv/prim_alert] Improvement on prim_alert tb (Cindy Chen)
* [prim] FIFO SRAM Adapter fix (Eunchan Kim)
* [prim] Add Write Mask port (Eunchan Kim)
* [dv] Fix timescale issue with Xcelium (Weicai Yang)
* [dv/prim_esc] Fix prim_esc regression error (Cindy Chen)
* [dv/dv_base_reg] change from uvm_low to uvm_high (Cindy Chen)
* [sram_ctrl] Harden initialization counter (Michael Schaffner)
* [tools/uvmdvgen] Fix path in testplan inclusion (Guillermo Maturana)
* [dv] Change stress_all_with_rand_reset to V3 (Weicai Yang)
* [dv] fix tl error coverage (Weicai Yang)
* [dv] Add macro DV_GET_ENUM_PLUSARG (Weicai Yang)
* [prim] SRAM Async FIFO (Eunchan Kim)
* [dv, xcelium] Fix statement coverage extraction (Srikrishna Iyer)
* [dvsim] Minor fixes to coverage extraction (Srikrishna Iyer)
* [prim_lfsr] Do not shadow |state| variable (Philipp Wagner)
* [prim] Add non-linear out option to prim_lfsr (Timothy Chen)
* [dv] Constrain TLUL to 24Mhz or higher (Weicai Yang)
* [primgen] Instantiate tech libs in stable order (Philipp Wagner)
* [primgen] Actually find the Verible Python wrapper (Philipp Wagner)
* [dv/prim_esc] fix regression error (Cindy Chen)
* [dv] Fix shadow reg predict (Weicai Yang)
* [dv/common] Exclude assertion coverage from IP level testbench
  (Cindy Chen)
* [dv/prince] hit additional toggle coverpoints (Udi Jonnalagadda)
* [sram_ctrl] Update docs (Michael Schaffner)
* [sram_ctrl] Absorb prim_ram_1p_scr (Michael Schaffner)
* [dv/prim_alert/esc] Improvements for prim_alert/esc_tb (Cindy Chen)
* [dv/dvsim] Add "testfile" grading option (Guillermo Maturana)
* [dv/prim_esc] Direct test for prim_rx/tx (Cindy Chen)
* [dv/utils] added 6MHz to clk_freq_mhz_e (Dror Kabely)
* [prim_xor2/lint] Add waiver for .* use in generated prim (Michael
  Schaffner)
* [dv, doc] Replace all 'dv.plan' with testplan (Srikrishna Iyer)
* Fix the testplan link in dvsim code (Srikrishna Iyer)
* [dv/dsim] Add dsim workaround for issue 242 (Guillermo Maturana)
* [util, reggen] Support standardized cdc handling for regfile
  (Timothy Chen)
* [dv/shadow_reg] Align shadow_reg field update behavior (Cindy Chen)
* [dvsim] Fix publish report summary typo (Cindy Chen)
* [rtl/prim_alert_sender] Allow ping_req to stay high without error
  (Cindy Chen)
* [dvsim] Separate publish report from dvsim flow [PART3] (Cindy Chen)
* [dv/prim_alert] Add a testbench for prim_alert (Cindy Chen)
* [otp_ctrl] Connect test-related GPIO signal (Michael Schaffner)
* [prim_subreg_shadow] Make local parameter a localparam (Philipp
  Wagner)
* [prim_subreg] Make software access type an enum (Philipp Wagner)
* [rtl/prim_diff_decode] Add prim_flop_2sync dependency (Cindy Chen)
* [otp_ctrl] Update AscentLint waiver file (Michael Schaffner)
* [edn] Add MaxLatency assertion (Eunchan Kim)
* [prim_subreg_shadow] Correct write data signal usage (Michael
  Schaffner)
* [script/dvsim] Separate publish report from dvsim flow [PART2]
  (Cindy Chen)
* [prim_lfsr] Fix assertion issue occuring right after reset (Michael
  Schaffner)
* [dv/shadow_reg] Handle CSR automated sequence write abort (Cindy
  Chen)
* [dv/dv_lib] Add post_apply_reset for extra delay (Guillermo
  Maturana)
* [dv] Add function coverage plan for tl_errors, tl_intg_err (Weicai
  Yang)
* [dv] Remove tl_intg_err in top-level and increase seeds for
  tl_intg_err (Weicai Yang)
* [dv/shadow_reg] Fix alert shadow reg regression error (Cindy Chen)

Signed-off-by: Rupert Swarbrick <rswarbrick@lowrisc.org>
This commit is contained in:
Rupert Swarbrick 2021-10-05 12:35:08 +01:00 committed by Rupert Swarbrick
parent e70add7228
commit b66f199151
151 changed files with 5374 additions and 883 deletions

View file

@ -16,6 +16,7 @@
// Shared lowRISC code
+incdir+${PRJ_DIR}/vendor/lowrisc_ip/ip/prim/rtl
${PRJ_DIR}/vendor/lowrisc_ip/ip/prim/rtl/prim_assert.sv
${PRJ_DIR}/vendor/lowrisc_ip/ip/prim/rtl/prim_cipher_pkg.sv
${PRJ_DIR}/vendor/lowrisc_ip/ip/prim/rtl/prim_lfsr.sv
${PRJ_DIR}/vendor/lowrisc_ip/ip/prim/rtl/prim_secded_28_22_enc.sv
${PRJ_DIR}/vendor/lowrisc_ip/ip/prim/rtl/prim_secded_28_22_dec.sv

View file

@ -14,7 +14,7 @@ class ibex_icache_env_cfg extends dv_base_env_cfg;
// Force the clock frequency to 50MHz. The clock frequency doesn't really matter for ICache
// testing and 50MHz dumped waves are nice to read because clock edges are multiples of 10ns.
constraint clk_freq_50_c {
clk_freq_mhz == ClkFreq50Mhz;
clk_freq_mhz == 50;
}
// Config objects for ECC components (see create_ecc_agent_cfgs). Since these are created after

View file

@ -9,6 +9,6 @@
upstream:
{
url: https://github.com/lowRISC/opentitan
rev: da3ac7c4eb23a92194874ad2daf2e5f9e3330572
rev: ad629e3e6e70c5eaa3c2dd68457b0a020448b35f
}
}

View file

@ -200,7 +200,7 @@ class csr_write_seq extends csr_base_seq;
test_csrs[i].get_full_name()), UVM_MEDIUM)
`DV_CHECK_STD_RANDOMIZE_FATAL(wdata)
wdata &= get_mask_excl_fields(test_csrs[i], CsrExclWrite, CsrHwResetTest);
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
@ -232,6 +232,7 @@ class csr_rw_seq extends csr_base_seq;
uvm_reg_data_t wdata;
uvm_reg_data_t compare_mask;
uvm_reg_field test_fields[$];
dv_base_reg test_dv_csr;
// check if parent block or register is excluded from write
if (is_excl(test_csrs[i], CsrExclWrite, CsrRwTest)) begin
@ -244,7 +245,7 @@ class csr_rw_seq extends csr_base_seq;
test_csrs[i].get_full_name()), UVM_MEDIUM)
`DV_CHECK_STD_RANDOMIZE_FATAL(wdata)
wdata &= get_mask_excl_fields(test_csrs[i], CsrExclWrite, CsrRwTest);
wdata = get_csr_wdata_with_write_excl(test_csrs[i], wdata, CsrRwTest);
// if external checker is not enabled and writes are made non-blocking, then we need to
// pre-predict so that the mirrored value will be updated. if we dont, then csr_rd_check task
@ -253,6 +254,14 @@ class csr_rw_seq extends csr_base_seq;
// register is getting the updated access information.
csr_wr(.ptr(test_csrs[i]), .value(wdata), .blocking(0), .predict(!external_checker));
// Shadow register requires two writes with the same value to write registers into DUT.
// In `csr_wr` task, the `predict` task is triggered after two shadow writes are done.
// To avoid non-blocking access where shadow register read might be triggered between two
// consecutive shadow register write, we will wait until all outstanding accesses finish,
// then issue a shadow register read.
`downcast(test_dv_csr, test_csrs[i])
if (test_dv_csr.get_is_shadowed) wait_no_outstanding_access();
do_check_csr_or_field_rd(.csr(test_csrs[i]),
.blocking(0),
.compare(!external_checker),
@ -413,7 +422,7 @@ class csr_aliasing_seq extends csr_base_seq;
test_csrs[i].get_full_name()), UVM_MEDIUM)
`DV_CHECK_STD_RANDOMIZE_FATAL(wdata)
wdata &= get_mask_excl_fields(test_csrs[i], CsrExclWrite, CsrAliasingTest);
wdata = get_csr_wdata_with_write_excl(test_csrs[i], wdata, CsrAliasingTest);
csr_wr(.ptr(test_csrs[i]), .value(wdata), .blocking(0), .predict(!external_checker));
all_csrs.shuffle();

View file

@ -125,14 +125,6 @@ package csr_utils_pkg;
return result;
endfunction : decode_csr_or_field
// mask and shift data to extract the value specific to that supplied field
function automatic uvm_reg_data_t get_field_val(uvm_reg_field field,
uvm_reg_data_t value);
uvm_reg_data_t mask = (1 << field.get_n_bits()) - 1;
uint shift = field.get_lsb_pos();
get_field_val = (value >> shift) & mask;
endfunction
// get updated reg value by using new specific field value
function automatic uvm_reg_data_t get_csr_val_with_updated_field(uvm_reg_field field,
uvm_reg_data_t csr_value,
@ -152,6 +144,8 @@ package csr_utils_pkg;
csr.get_full_name(), csr.m_is_busy), UVM_HIGH)
endtask
// Use `csr_wr` to construct `csr_update` to avoid replicated codes to handle nonblocking,
// shadow writes etc
task automatic csr_update(input uvm_reg csr,
input uvm_check_e check = default_csr_check,
input uvm_path_e path = UVM_DEFAULT_PATH,
@ -159,53 +153,21 @@ package csr_utils_pkg;
input uint timeout_ns = default_timeout_ns,
input uvm_reg_map map = null,
input bit en_shadow_wr = 1);
if (blocking) begin
csr_update_sub(csr, check, path, timeout_ns, map, en_shadow_wr);
end else begin
fork
csr_update_sub(csr, check, path, timeout_ns, map, en_shadow_wr);
join_none
// Add #0 to ensure that this thread starts executing before any subsequent call
#0;
uvm_reg_field fields[$];
uvm_reg_data_t value;
// below is partial replication of the uvm_reg_field::update() logic in UVM1.2 source code
if (!csr.needs_update()) return;
csr.get_fields(fields);
// Concatenate the write-to-update values from each field
// Fields are stored in LSB or MSB order
value = 0;
foreach (fields[i]) begin
value |= fields[i].XupdateX() << fields[i].get_lsb_pos();
end
endtask
// subroutine of csr_update, don't use it directly
task automatic csr_update_sub(input uvm_reg csr,
input uvm_check_e check = default_csr_check,
input uvm_path_e path = UVM_DEFAULT_PATH,
input uint timeout_ns = default_timeout_ns,
input uvm_reg_map map = null,
input bit en_shadow_wr = 1);
fork
begin : isolation_fork
uvm_status_e status;
string msg_id = {csr_utils_pkg::msg_id, "::csr_update"};
fork
begin
increment_outstanding_access();
csr_pre_write_sub(csr, en_shadow_wr);
csr.update(.status(status), .path(path), .map(map), .prior(100));
csr_post_write_sub(csr, en_shadow_wr);
// when reset occurs, all items will be dropped immediately. This may end up getting
// d_error = 1 from previous item on the bus. Skip checking it during reset
if (check == UVM_CHECK && !under_reset) begin
`DV_CHECK_EQ(status, UVM_IS_OK,
$sformatf("trying to update csr %0s", csr.get_full_name()),
error, msg_id)
end
decrement_outstanding_access();
end
begin
wait_timeout(timeout_ns, msg_id,
$sformatf("Timeout waiting to csr_update %0s (addr=0x%0h)",
csr.get_full_name(), csr.get_address()));
end
join_any
disable fork;
end : isolation_fork
join
csr_wr(.ptr(csr), .value(value), .check(check), .path(path), .blocking(blocking), .backdoor(0),
.timeout_ns(timeout_ns), .predict(0), .map(map), .en_shadow_wr(en_shadow_wr));
endtask
task automatic csr_wr(input uvm_object ptr,
@ -254,25 +216,24 @@ package csr_utils_pkg;
input bit en_shadow_wr = 1);
fork
begin : isolation_fork
uvm_status_e status;
string msg_id = {csr_utils_pkg::msg_id, "::csr_wr"};
fork
begin
dv_base_reg dv_reg;
`downcast(dv_reg, csr, "", fatal, msg_id)
increment_outstanding_access();
csr_pre_write_sub(csr, en_shadow_wr);
csr.write(.status(status), .value(value), .path(path), .map(map), .prior(100));
csr_wr_and_predict_sub(.csr(csr), .value(value), .check(check), .path(path),
.predict(predict), .map(map));
if (en_shadow_wr && dv_reg.get_is_shadowed()) begin
csr_wr_and_predict_sub(.csr(csr), .value(value), .check(check), .path(path),
.predict(predict), .map(map));
end
csr_post_write_sub(csr, en_shadow_wr);
if (check == UVM_CHECK && !under_reset) begin
`DV_CHECK_EQ(status, UVM_IS_OK,
$sformatf("trying to write csr %0s", csr.get_full_name()),
error, msg_id)
end
// Only update the predicted value if status is ok (otherwise the write isn't completed
// successfully and the design shouldn't have accepted the written value)
if (status == UVM_IS_OK && predict) begin
void'(csr.predict(.value(value), .kind(UVM_PREDICT_WRITE)));
end
decrement_outstanding_access();
end
begin
@ -286,27 +247,42 @@ package csr_utils_pkg;
join
endtask
task automatic csr_pre_write_sub(ref uvm_reg csr, bit en_shadow_wr);
dv_base_reg dv_reg;
`downcast(dv_reg, csr, "", fatal, msg_id)
if (dv_reg.get_is_shadowed()) begin
if (en_shadow_wr) increment_outstanding_access();
dv_reg.atomic_en_shadow_wr.get(1);
dv_reg.set_en_shadow_wr(en_shadow_wr);
// internal task, don't use it directly
task automatic csr_wr_and_predict_sub(uvm_reg csr,
uvm_reg_data_t value,
uvm_check_e check,
uvm_path_e path,
bit predict,
uvm_reg_map map);
uvm_status_e status;
csr.write(.status(status), .value(value), .path(path), .map(map), .prior(100));
if (under_reset) return;
if (check == UVM_CHECK) begin
`DV_CHECK_EQ(status, UVM_IS_OK,
$sformatf("trying to write csr %0s", csr.get_full_name()),
error, msg_id)
end
// Only update the predicted value if status is ok (otherwise the write isn't completed
// successfully and the design shouldn't have accepted the written value)
if (status == UVM_IS_OK && predict) begin
void'(csr.predict(.value(value), .kind(UVM_PREDICT_WRITE)));
end
endtask
task automatic csr_post_write_sub(ref uvm_reg csr, bit en_shadow_wr);
task automatic csr_pre_write_sub(uvm_reg csr, bit en_shadow_wr);
dv_base_reg dv_reg;
`downcast(dv_reg, csr, "", fatal, msg_id)
if (dv_reg.get_is_shadowed()) begin
// try setting en_shadow_wr back to default value 1, this function will only work if the
// shadow reg finished both writes
dv_reg.set_en_shadow_wr(1);
if (dv_reg.get_is_shadowed() && en_shadow_wr) begin
dv_reg.atomic_en_shadow_wr.get(1);
end
endtask
task automatic csr_post_write_sub(uvm_reg csr, bit en_shadow_wr);
dv_base_reg dv_reg;
`downcast(dv_reg, csr, "", fatal, msg_id)
if (dv_reg.get_is_shadowed() && en_shadow_wr) begin
dv_reg.atomic_en_shadow_wr.put(1);
if (en_shadow_wr) begin
decrement_outstanding_access();
end
end
endtask
@ -726,6 +702,30 @@ package csr_utils_pkg;
end
endfunction
// Returns the write data value masked with excluded fields.
//
// Some fields in the CSR may be excluded from writes. In that case, we need to revert those
// fields to their mirrored values and write the rest of the fields with the given value.
function automatic uvm_reg_data_t get_csr_wdata_with_write_excl(
uvm_reg csr,
uvm_reg_data_t wdata,
csr_test_type_e csr_test_type,
csr_excl_item m_csr_excl_item = get_excl_item(csr)
);
uvm_reg_field flds[$];
csr.get_fields(flds);
foreach (flds[i]) begin
if (m_csr_excl_item.is_excl(flds[i], CsrExclWrite, csr_test_type)) begin
`uvm_info(msg_id, $sformatf(
"Retain mirrored value 0x%0h for field %0s due to CsrExclWrite exclusion",
`gmv(flds[i]), flds[i].get_full_name()), UVM_MEDIUM)
wdata = get_csr_val_with_updated_field(flds[i], wdata, `gmv(flds[i]));
end
end
return wdata;
endfunction
function automatic csr_excl_item get_excl_item(uvm_object ptr);
csr_field_t csr_or_fld;
dv_base_reg_block blk;

View file

@ -8,20 +8,17 @@ class dv_base_reg extends uvm_reg;
// hence, backdoor write isn't available
local bit is_ext_reg;
local uvm_reg_data_t staged_shadow_val, committed_val, shadowed_val;
local bit is_shadowed;
local bit shadow_wr_staged; // stage the first shadow reg write
local bit shadow_update_err;
local bit en_shadow_wr = 1;
// In certain shadow reg (e.g. in AES), fatal error can lock write access
local bit backdoor_write_shadow_val; // flag to avoid predict `shadow_wr_staged`
// Update internal shadow committed and shadowed values when register access is not `RW`.
local bit do_update_shadow_vals;
// In certain shadow reg (e.g. in AES), fatal error can lock write access.
local bit shadow_fatal_lock;
local string update_err_alert_name;
local string storage_err_alert_name;
// atomic_shadow_wr: semaphore to guarantee atomicity of the two writes for shadowed registers.
// In case a parallel thread writing a different value to the same reg causing an update_err
semaphore atomic_shadow_wr;
// atomic_en_shadow_wr: semaphore to guarantee setting or resetting en_shadow_wr is unchanged
// through the 1st/2nd (or both) writes
semaphore atomic_en_shadow_wr;
@ -31,13 +28,16 @@ class dv_base_reg extends uvm_reg;
int has_coverage);
super.new(name, n_bits, has_coverage);
atomic_en_shadow_wr = new(1);
atomic_shadow_wr = new(1);
endfunction : new
function void get_dv_base_reg_fields(ref dv_base_reg_field dv_fields[$]);
foreach (m_fields[i]) `downcast(dv_fields[i], m_fields[i])
endfunction
function dv_base_reg_block get_dv_base_reg_block();
`downcast(get_dv_base_reg_block, get_parent())
endfunction
// get_n_bits will return number of all the bits in the csr
// while this function will return actual number of bits used in reg field
function uint get_n_used_bits();
@ -65,6 +65,15 @@ class dv_base_reg extends uvm_reg;
return dv_fld;
endfunction
// Return a mask of valid bits in the register.
virtual function uvm_reg_data_t get_reg_mask();
dv_base_reg_field flds[$];
this.get_dv_base_reg_fields(flds);
foreach (flds[i]) begin
get_reg_mask |= flds[i].get_field_mask();
end
endfunction
// this function can only be called when this reg is intr_state reg
// Example: ral.intr_state.get_intr_pins_exp_value(). And it returns value of
// intr_state & intr_enable, which represents value of interrupt pins
@ -138,19 +147,12 @@ class dv_base_reg extends uvm_reg;
is_shadowed = 1;
endfunction
function uvm_reg_data_t get_staged_shadow_val();
return staged_shadow_val;
endfunction
function void set_en_shadow_wr(bit val);
// do not update en_shadow_wr if shadow register write is in process
if ((en_shadow_wr ^ val) && shadow_wr_staged) begin
`uvm_info(`gfn,
$sformatf("unable to %0s en_shadow_wr because register already completed first write",
val ? "set" : "clear"), UVM_HIGH)
return;
// A helper function for shadow register or field read to clear the `shadow_wr_staged` flag.
virtual function void clear_shadow_wr_staged();
if (is_shadowed) begin
if (shadow_wr_staged) `uvm_info(`gfn, "clear shadow_wr_staged", UVM_HIGH)
shadow_wr_staged = 0;
end
en_shadow_wr = val;
endfunction
function bit get_is_shadowed();
@ -162,53 +164,71 @@ class dv_base_reg extends uvm_reg;
endfunction
function bit get_shadow_storage_err();
uvm_reg_data_t mask = (1'b1 << (get_msb_pos() + 1)) - 1;
uvm_reg_data_t shadowed_val_temp = (~shadowed_val) & mask;
uvm_reg_data_t committed_val_temp = committed_val & mask;
`uvm_info(`gfn, $sformatf("shadow_val %0h, commmit_val %0h", shadowed_val_temp,
committed_val_temp), UVM_DEBUG)
return shadowed_val_temp != committed_val_temp;
dv_base_reg_field flds[$];
this.get_dv_base_reg_fields(flds);
foreach (flds[i]) begin
get_shadow_storage_err |= flds[i].get_shadow_storage_err();
end
endfunction
virtual function void clear_shadow_update_err();
shadow_update_err = 0;
endfunction
// post_write callback to handle special regs:
// do_predict callback to handle special regs. This function doesn't update mirror values, but
// update local variables used for the special regs.
// - shadow register: shadow reg won't be updated until the second write has no error
// - lock register: if wen_fld is set to 0, change access policy to all the lockable_flds
// TODO: create an `enable_field_access_policy` variable and set the template code during
// automation.
virtual task post_write(uvm_reg_item rw);
dv_base_reg_field fields[$];
virtual function void pre_do_predict(uvm_reg_item rw, uvm_predict_e kind);
// no need to update shadow value or access type if access is not OK, as access is aborted
if (rw.status != UVM_IS_OK) return;
// Skip updating shadow value or access type if:
// 1). Access is not OK, as access is aborted.
// 2). The operation is not write.
// 3). The update is triggered by backdoor poke.
if (rw.status != UVM_IS_OK || kind != UVM_PREDICT_WRITE || backdoor_write_shadow_val) return;
if (is_shadowed && !shadow_fatal_lock) begin
// first write
if (!shadow_wr_staged) begin
shadow_wr_staged = 1;
// rw.value is a dynamic array
staged_shadow_val = rw.value[0];
return;
end begin
// second write
shadow_wr_staged = 0;
if (staged_shadow_val != rw.value[0]) begin
shadow_update_err = 1;
return;
dv_base_reg_field flds[$];
this.get_dv_base_reg_fields(flds);
foreach (flds[i]) begin
// `rw.value` is a dynamic array.
uvm_reg_data_t wr_data = get_field_val(flds[i], rw.value[0]);
// Skip updating shadow value or access type for this field if:
// 1). A storage error is triggered, which means the field is locked to error status.
// 2). The register is "RO". This condition is used to cover enable register locks shadowed
// register's write access.
if (flds[i].get_shadow_storage_err() || flds[i].get_access() == "RO") continue;
// first write
if (!shadow_wr_staged) begin
flds[i].update_staged_val(wr_data);
continue;
end begin
// second write
if (flds[i].get_staged_val() == wr_data) begin
flds[i].update_committed_val(wr_data);
flds[i].update_shadowed_val(~wr_data);
end else begin
shadow_update_err = 1;
end
end
committed_val = staged_shadow_val;
shadowed_val = ~committed_val;
end
if (!shadow_wr_staged) shadow_wr_staged = 1;
else shadow_wr_staged = 0;
// Update committed and shadowed values if the field access is not "RW".
// Used for register with special access policy such as `RW1S`.
if (!shadow_wr_staged && flds[0].get_access() != "RW") do_update_shadow_vals = 1;
end
lock_lockable_flds(rw.value[0]);
endtask
endfunction
// shadow register read will clear its phase tracker
virtual task post_read(uvm_reg_item rw);
if (is_shadowed) shadow_wr_staged = 0;
if (rw.status == UVM_IS_OK) clear_shadow_wr_staged();
endtask
virtual function void set_is_ext_reg(bit is_ext);
@ -219,42 +239,45 @@ class dv_base_reg extends uvm_reg;
return is_ext_reg;
endfunction
// if it is a shadowed register, and is enabled to write it twice, this task will write the
// register twice with the same value and address.
virtual task write(output uvm_status_e status,
input uvm_reg_data_t value,
input uvm_path_e path = UVM_DEFAULT_PATH,
input uvm_reg_map map = null,
input uvm_sequence_base parent=null,
input int prior = -1,
input uvm_object extension = null,
input string fname = "",
input int lineno = 0);
if (is_shadowed) atomic_shadow_wr.get(1);
super.write(status, value, path, map, parent, prior, extension, fname, lineno);
if (is_shadowed && en_shadow_wr) begin
super.write(status, value, path, map, parent, prior, extension, fname, lineno);
end
if (is_shadowed) atomic_shadow_wr.put(1);
endtask
// Override do_predict function to support shadow_reg.
// Skip predict in one of the following conditions:
// 1). It is shadow_reg's first write.
// 2). It is shadow_reg's second write with an update_err.
// 2). The shadow_reg is locked due to fatal storage error and it is not a backdoor write.
// Note that if shadow_register write has update error, we only update the value with correct
// fields.
virtual function void do_predict(uvm_reg_item rw,
uvm_predict_e kind = UVM_PREDICT_DIRECT,
uvm_reg_byte_en_t be = -1);
if (is_shadowed && kind != UVM_PREDICT_READ && (shadow_wr_staged || shadow_update_err ||
(shadow_fatal_lock && rw.path != UVM_BACKDOOR))) begin
`uvm_info(`gfn, $sformatf(
"skip predict %s: due to shadow_reg_first_wr=%0b, update_err=%0b, shadow_fatal_lock=%0b",
get_name(), shadow_wr_staged, shadow_update_err, shadow_fatal_lock), UVM_HIGH)
return;
pre_do_predict(rw, kind);
if (is_shadowed && kind != UVM_PREDICT_READ) begin
if (shadow_wr_staged || (shadow_fatal_lock && rw.path != UVM_BACKDOOR)) begin
`uvm_info(`gfn, $sformatf(
"skip predict %s: due to shadow_reg_first_wr=%0b, shadow_fatal_lock=%0b",
get_name(), shadow_wr_staged, shadow_fatal_lock), UVM_HIGH)
return;
end else begin
`uvm_info(`gfn, $sformatf(
"Shadow reg %0s has update error, update rw.value from %0h to %0h",
get_name(), rw.value[0], get_committed_val()), UVM_HIGH)
rw.value[0] = get_committed_val();
end
end
super.do_predict(rw, kind, be);
// For register with special access policies, update committed and shadowed value again with
// the actual predicted value.
if (do_update_shadow_vals) begin
dv_base_reg_field flds[$];
this.get_dv_base_reg_fields(flds);
foreach (flds[i]) begin
if (!flds[i].get_shadow_storage_err()) begin
flds[i].update_committed_val(`gmv(flds[i]));
flds[i].update_shadowed_val(~`gmv(flds[i]));
end
end
do_update_shadow_vals = 0;
end
lock_lockable_flds(rw.value[0]);
endfunction
// This function is used for wen_reg to lock its lockable flds by changing the lockable flds'
@ -287,31 +310,35 @@ class dv_base_reg extends uvm_reg;
input uvm_object extension = null,
input string fname = "",
input int lineno = 0);
if (kind == "BkdrRegPathRtlShadow") shadowed_val = value;
else if (kind == "BkdrRegPathRtlCommitted") committed_val = value;
dv_base_reg_field flds[$];
this.get_dv_base_reg_fields(flds);
foreach (flds[i]) begin
if (kind == "BkdrRegPathRtlShadow") begin
flds[i].update_shadowed_val(get_field_val(flds[i], value));
backdoor_write_shadow_val = 1;
end else if (kind == "BkdrRegPathRtlCommitted") begin
flds[i].update_committed_val(get_field_val(flds[i], value));
backdoor_write_shadow_val = 1;
end
end
super.poke(status, value, kind, parent, extension, fname, lineno);
backdoor_write_shadow_val = 0;
endtask
virtual function uvm_reg_data_t get_committed_val();
dv_base_reg_field flds[$];
this.get_dv_base_reg_fields(flds);
foreach (flds[i]) begin
get_committed_val |= flds[i].get_committed_val() << flds[i].get_lsb_pos();
end
endfunction
// Callback function to update shadowed values according to specific design.
// Should only be called after post-write.
// If a shadow reg is locked due to fatal error, this function will return without updates
virtual function void update_shadowed_val(uvm_reg_data_t val, bit do_predict = 1);
if (shadow_fatal_lock) return;
if (shadow_wr_staged) begin
// update value after first write
staged_shadow_val = val;
end else begin
// update value after second write
if (staged_shadow_val != val) begin
shadow_update_err = 1;
end else begin
shadow_update_err = 0;
committed_val = staged_shadow_val;
shadowed_val = ~committed_val;
end
end
if (do_predict) void'(predict(val));
// TODO: find a better way to support for AES.
endfunction
virtual function void reset(string kind = "HARD");
@ -320,12 +347,8 @@ class dv_base_reg extends uvm_reg;
shadow_update_err = 0;
shadow_wr_staged = 0;
shadow_fatal_lock = 0;
committed_val = get_mirrored_value();
shadowed_val = ~committed_val;
// in case reset is issued during shadowed writes
void'(atomic_shadow_wr.try_get(1));
void'(atomic_en_shadow_wr.try_get(1));
atomic_shadow_wr.put(1);
atomic_en_shadow_wr.put(1);
end
endfunction
@ -339,13 +362,11 @@ class dv_base_reg extends uvm_reg;
endfunction
function string get_update_err_alert_name();
string parent_name = this.get_parent().get_name();
// block level alert name is input alert name from hjson
if (get_parent().get_parent() == null) return update_err_alert_name;
// top-level alert name is ${block_name} + alert name from hjson
return ($sformatf("%0s_%0s", parent_name, update_err_alert_name));
// top-level alert name is ${ip_name} + alert name from hjson
return ($sformatf("%0s_%0s", get_dv_base_reg_block().get_ip_name(), update_err_alert_name));
endfunction
function void lock_shadow_reg();
@ -357,13 +378,13 @@ class dv_base_reg extends uvm_reg;
endfunction
function string get_storage_err_alert_name();
string parent_name = this.get_parent().get_name();
string ip_name;
// block level alert name is input alert name from hjson
if (get_parent().get_parent() == null) return storage_err_alert_name;
// top-level alert name is ${block_name} + alert name from hjson
return ($sformatf("%0s_%0s", parent_name, storage_err_alert_name));
// top-level alert name is ${ip_name} + alert name from hjson
return ($sformatf("%0s_%0s", get_dv_base_reg_block().get_ip_name(), storage_err_alert_name));
endfunction
endclass

View file

@ -6,6 +6,12 @@
class dv_base_reg_block extends uvm_reg_block;
`uvm_object_utils(dv_base_reg_block)
// 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
// name as {ip_name}_{alert_name}. Hence, we need this ip_name in reg_block
local string ip_name;
csr_excl_item csr_excl;
// The address mask for the register block specific to a map. This will be (1 << K) - 1 for some
@ -29,6 +35,17 @@ class dv_base_reg_block extends uvm_reg_block;
super.new(name, has_coverage);
endfunction
function void set_ip_name(string name);
ip_name = name;
endfunction
function string get_ip_name();
// `DV_CHECK_NE_FATAL can't take "" as an input
string empty_str = "";
`DV_CHECK_NE_FATAL(ip_name, empty_str, "ip_name hasn't been set yet")
return ip_name;
endfunction
// provide build function to supply base addr
virtual function void build(uvm_reg_addr_t base_addr,
csr_excl_item csr_excl = null);
@ -277,4 +294,13 @@ class dv_base_reg_block extends uvm_reg_block;
return (word_aligned ? get_word_aligned_addr(byte_offset) : byte_offset) + map.get_base_addr();
endfunction
// The design ignores the address bits that aren't enabled by addr_mask.
// Normalize these ignored bits to enable locating which CSR/mem is at the returned address.
function uvm_reg_addr_t get_normalized_addr(uvm_reg_addr_t byte_addr, uvm_reg_map map = null);
if (map == null) map = get_default_map();
return get_addr_from_offset(.byte_offset(byte_addr & addr_mask[map]),
.word_aligned(1),
.map(map));
endfunction
endclass

View file

@ -7,6 +7,7 @@ class dv_base_reg_field extends uvm_reg_field;
local string m_original_access;
local dv_base_reg_field lockable_flds[$];
local bit is_intr_test_fld;
local uvm_reg_data_t staged_val, committed_val, shadowed_val;
`uvm_object_utils(dv_base_reg_field)
`uvm_object_new
@ -34,6 +35,7 @@ class dv_base_reg_field extends uvm_reg_field;
value.rand_mode(is_rand);
is_intr_test_fld = !(uvm_re_match("intr_test*", get_parent().get_name()));
shadowed_val = ~committed_val;
endfunction
virtual function dv_base_reg get_dv_base_reg_parent();
@ -126,11 +128,50 @@ class dv_base_reg_field extends uvm_reg_field;
lockable_flds_q = lockable_flds;
endfunction
// shadow register field read will clear its phase tracker
virtual task post_read(uvm_reg_item rw);
if (rw.status == UVM_IS_OK) begin
dv_base_reg parent_csr = get_dv_base_reg_parent();
parent_csr.clear_shadow_wr_staged();
end
endtask
function bit get_shadow_storage_err();
uvm_reg_data_t mask = (1 << get_n_bits()) - 1;
uvm_reg_data_t shadowed_val_temp = (~shadowed_val) & mask;
uvm_reg_data_t committed_val_temp = committed_val & mask;
`uvm_info(`gfn, $sformatf("shadow_val %0h, commmit_val %0h", shadowed_val_temp,
committed_val_temp), UVM_HIGH)
return shadowed_val_temp != committed_val_temp;
endfunction
function void update_staged_val(uvm_reg_data_t val);
staged_val = val;
endfunction
function uvm_reg_data_t get_staged_val();
return staged_val;
endfunction
function void update_shadowed_val(uvm_reg_data_t val);
shadowed_val = val;
endfunction
function void update_committed_val(uvm_reg_data_t val);
committed_val = val;
endfunction
function uvm_reg_data_t get_committed_val();
return committed_val;
endfunction
// override RAL's reset function to support enable registers
// when reset issued - the lockable field's access will be reset to original access
virtual function void reset(string kind = "HARD");
super.reset(kind);
set_fld_access(0);
committed_val = get_mirrored_value();
shadowed_val = ~committed_val;
endfunction
// this function can only be called when this reg is intr_test reg

View file

@ -73,4 +73,12 @@ package dv_base_reg_pkg;
end
endfunction
// mask and shift data to extract the value specific to that supplied field
function automatic uvm_reg_data_t get_field_val(uvm_reg_field field,
uvm_reg_data_t value);
uvm_reg_data_t mask = (1 << field.get_n_bits()) - 1;
uint shift = field.get_lsb_pos();
get_field_val = (value >> shift) & mask;
endfunction
endpackage

View file

@ -43,8 +43,15 @@ class dv_base_env_cfg #(type RAL_T = dv_base_reg_block) extends uvm_object;
// clk_rst_vif and clk_freq_mhz can be found from the associative arrays
virtual clk_rst_if clk_rst_vif;
virtual clk_rst_if clk_rst_vifs[string];
rand clk_freq_mhz_e clk_freq_mhz;
rand clk_freq_mhz_e clk_freqs_mhz[string];
rand uint clk_freq_mhz;
rand uint clk_freqs_mhz[string];
constraint clk_freq_mhz_c {
`DV_COMMON_CLK_CONSTRAINT(clk_freq_mhz)
foreach (clk_freqs_mhz[i]) {
`DV_COMMON_CLK_CONSTRAINT(clk_freqs_mhz[i])
}
}
`uvm_object_param_utils_begin(dv_base_env_cfg #(RAL_T))
`uvm_field_int (is_active, UVM_DEFAULT)
@ -74,7 +81,7 @@ class dv_base_env_cfg #(type RAL_T = dv_base_reg_block) extends uvm_object;
// add items to clk_freqs_mhz before randomizing it
foreach (ral_model_names[i]) begin
clk_freqs_mhz[ral_model_names[i]] = ClkFreq24Mhz;
clk_freqs_mhz[ral_model_names[i]] = 0;
end
endfunction

View file

@ -78,8 +78,10 @@ class dv_base_vseq #(type RAL_T = dv_base_reg_block,
* startup, reset and shutdown related tasks
*/
virtual task dut_init(string reset_kind = "HARD");
if (do_apply_reset) apply_reset(reset_kind);
else if (do_wait_for_reset) wait_for_reset(reset_kind);
if (do_apply_reset) begin
apply_reset(reset_kind);
post_apply_reset(reset_kind);
end else if (do_wait_for_reset) wait_for_reset(reset_kind);
// delay after reset for tl agent check seq_item_port empty
#1ps;
endtask
@ -137,6 +139,11 @@ class dv_base_vseq #(type RAL_T = dv_base_reg_block,
end
endtask
// This is called after apply_reset in this class and after apply_resets_concurrently
// in cip_base_vseq::run_stress_all_with_rand_reset_vseq.
virtual task post_apply_reset(string reset_kind = "HARD");
endtask
virtual task wait_for_reset(string reset_kind = "HARD",
bit wait_for_assert = 1,
bit wait_for_deassert = 1);

View file

@ -409,6 +409,24 @@
end
`endif
// This macro converts a string input from plusarg to an enum variable
// ENUM_: the name of enum type
// PLUSARG_: the name of the plusargs, which is also the name of the enum variable
// CHECK_EXIST_: set to 1, `$value$plusargs()` should return true
`ifndef DV_GET_ENUM_PLUSARG
`define DV_GET_ENUM_PLUSARG(ENUM_, PLUSARG_, CHECK_EXIST_ = 0, ID_ = `gfn) \
begin \
string str; \
if ($value$plusargs("``PLUSARG_``=%0s", str)) begin \
if (!uvm_enum_wrapper#(ENUM_)::from_name(str, PLUSARG_)) begin \
`uvm_fatal(ID_, $sformatf("Cannot find %s from enum ``ENUM_``", PLUSARG_.name)) \
end \
end else if (CHECK_EXIST_) begin \
`uvm_fatal(ID_, "Can't find plusargs ``PLUSARG_``") \
end \
end
`endif
// Enable / disable assertions at a module hierarchy identified by LABEL_.
//
// This goes in conjunction with `DV_ASSERT_CTRL() macro above, but is invoked in the entity that is
@ -492,3 +510,18 @@
`endif
`endif // UVM
// Macros for constrain clk with common frequencies
// constrain clock to run at 24Mhz - 100Mhz and use higher weights on 24, 25, 48, 50, 100
`ifndef DV_COMMON_CLK_CONSTRAINT
`define DV_COMMON_CLK_CONSTRAINT(FREQ_) \
FREQ_ dist { \
[24:25] :/ 2, \
[26:47] :/ 1, \
[48:50] :/ 2, \
[51:95] :/ 1, \
96 :/ 1, \
[97:99] :/ 1, \
100 :/ 1 \
};
`endif

View file

@ -44,15 +44,6 @@ package dv_utils_pkg;
Device
} if_mode_e;
// speed for the clock
typedef enum int {
ClkFreq24Mhz = 24,
ClkFreq25Mhz = 25,
ClkFreq48Mhz = 48,
ClkFreq50Mhz = 50,
ClkFreq100Mhz = 100
} clk_freq_mhz_e;
// compare operator types
typedef enum {
CompareOpEq,
@ -85,6 +76,14 @@ package dv_utils_pkg;
HostReqReadWrite = 3
} host_req_type_e;
// Enum representing clock frequency difference on 2 clocks
typedef enum bit [1:0] {
ClkFreqDiffNone,
ClkFreqDiffSmall,
ClkFreqDiffBig,
ClkFreqDiffAny
} clk_freq_diff_e;
string msg_id = "dv_utils_pkg";
// return the smaller value of 2 inputs

View file

@ -137,19 +137,19 @@
// Project defaults for VCS
vcs_cov_cfg_file: "{{build_mode}_vcs_cov_cfg_file}"
vcs_unr_cfg_file: "{dv_root}/tools/vcs/unr.cfg"
vcs_cov_excl_files: ["{dv_root}/tools/vcs/common_cov_excl.el"]
vcs_cov_excl_files: []
// Build-specific coverage cfg files for VCS.
default_vcs_cov_cfg_file: "-cm_hier {dv_root}/tools/vcs/cover.cfg"
cover_reg_top_vcs_cov_cfg_file: "-cm_hier {dv_root}/tools/vcs/cover_reg_top.cfg"
default_vcs_cov_cfg_file: "-cm_hier {dv_root}/tools/vcs/cover.cfg+{dv_root}/tools/vcs/common_cov_excl.cfg"
cover_reg_top_vcs_cov_cfg_file: "-cm_hier {dv_root}/tools/vcs/cover_reg_top.cfg+{dv_root}/tools/vcs/common_cov_excl.cfg"
// Project defaults for Xcelium
xcelium_cov_cfg_file: "{dv_root}/tools/xcelium/xcelium.ccf"
xcelium_cov_refine_files: []
xcelium_cov_cfg_file: "{{build_mode}_xcelium_cov_cfg_file}"
xcelium_unr_cfg_file: "{dv_root}/tools/xcelium/unr.cfg"
xcelium_common_excl_file: ["{dv_root}/tools/xcelium/exclude.tcl"]
xcelium_cov_excl_script: "{dv_root}/tools/xcelium/common_cov_excl.tcl"
xcelium_cov_refine_files: []
// Build-specific coverage cfg files for Xcelium.
// default_xcelium_cov_cfg_file: "-covfile {dv_root}/tools/xcelium/cover.ccf"
// cover_reg_top_xcelium_cov_cfg_file: "-covfile {dv_root}/tools/xcelium/cover_reg_top.ccf"
default_xcelium_cov_cfg_file: "{dv_root}/tools/xcelium/cover.ccf"
cover_reg_top_xcelium_cov_cfg_file: "{dv_root}/tools/xcelium/cover_reg_top.ccf"
}

View file

@ -20,6 +20,9 @@
// List multiple tops for the simulation. Prepend each top level with `-top`.
"{eval_cmd} echo {sim_tops} | sed -E 's/(\\S+)/-top \\1/g'",
"+incdir+{build_dir}",
// TODO Remove buggy optimization when dsim fixes bug
// https://gitlab.metrics.ca/google/google/-/issues/242.
"-noopt-task-func",
// Suppress following DSim errors and warnings:
// EnumMustBePositive - UVM 1.2 violates this
"-suppress EnumMustBePositive"]

View file

@ -6,11 +6,12 @@
fusesoc_core_: "{eval_cmd} echo \"{fusesoc_core}\" | tr ':' '_'"
sv_flist_gen_opts: ["{fusesoc_cores_root_dirs}",
"run",
"--flag=fileset_{design_level}",
"{sv_flist_gen_flags}",
"--target=sim",
"--build-root={build_dir}",
"--setup {fusesoc_core}"]
fusesoc_cores_root_dirs: ["--cores-root {proj_root}"]
sv_flist_gen_dir: "{build_dir}/sim-vcs"
sv_flist: "{sv_flist_gen_dir}/{fusesoc_core_}.scr"
sv_flist_gen_flags: ["--flag=fileset_{design_level}"]
}

View file

@ -0,0 +1,52 @@
// Copyright lowRISC contributors.
// Licensed under the Apache License, Version 2.0, see LICENSE for details.
// SPDX-License-Identifier: Apache-2.0
{
// TODO, split this into several testplan for each CM
testpoints: [
{
name: one_hot_check
desc: ''' Verify design behavior is correct when one-hot coding is violated.
Stimulus:
- Backdoor force one-hot coding signals to not one-hot.
- Randomly flip the value back to ensure the error is latched and won't go away until
reset.
Checks:
- Check that fatal alert is triggered.
- Check that err_code/fault_status is updated correctly and preserved until reset.
- Check the following operation should be failed if applicable.'''
milestone: V2
tests: ["{name}_sec_cm"]
}
{
name: redundant_coding_fsm_check
desc: ''' Verify design behavior is correct when the redundant FSM enters an invalid state.
Stimulus:
- Backdoor force the FSM to any of the undefined values.
- Randomly flip the value back to a defined state to ensure the error is latched and
won't go away until reset.
Same checks as `one_hot_check`'''
milestone: V2
tests: ["{name}_sec_cm"]
}
{
name: hardened_counter_check
desc: ''' Verify design behavior is correct when the harden counter is changed to an
unexpected value.
Stimulus:
- At the falling edge (non-active edge), force the counter to a different value.
- Randomly flip the value back to any other value to ensure the error is latched and
won't go away until reset.
Same checks as `one_hot_check`'''
milestone: V2
tests: ["{name}_sec_cm"]
}
]
}

View file

@ -5,23 +5,47 @@
testpoints: [
{
// this testplan should be imported by all IPs containing shadowed CSRs
name: shadow_reg_errors
desc: '''
Verify shadow registers' update and storage errors.
- Issue reset at random to clear all the internal stored values and phases trackers
in shadow registers.
- Select all the shadow registers and a random amount of the rest of the non-excluded
registers. Shuffle and write random values to the selected registers.
For shadow registers, the second write is not enabled.
There is a 50% possibility that the shadow register's write value is identical to its
previous write value.
If shadow register's second write value does not match the first write value,
ensure that the update error alert is triggered.
- Randomly inject storage errors by modifying the shadow register's staged or committed
values via backdoor method. Ensure that the storage error alert is triggered.
- Randomly decide to read all the non-excluded registers or fields. Then check the read
values against predicted values.
A read on a shadow register will clear its phase tracker.
name: shadow_reg_update_error
desc: '''Verify shadowed registers' update error.
- Randomly pick a shadowed register in the DUT.
- Write it twice with different values.
- Verify that the update error alert is triggered and the register value remains
unchanged.
- Verify the update_error status register field is set to 1.
- Repeat the above steps a bunch of times.
'''
milestone: V1
tests: ["{name}_shadow_reg_errors"]
}
{
name: shadow_reg_read_clear_staged_value
desc: '''Verify reading a shadowed register will clear its staged value.
- Randomly pick a shadowed register in the DUT.
- Write it once and read it back to clear the staged value.
- Then write it twice with the same new value (but different from the previous step).
- Read it back to verify the new value and ensure that the update error alert did not
trigger.
- Verify the update_error status register field remains the same value.
- Repeat the above steps a bunch of times.
'''
milestone: V1
tests: ["{name}_shadow_reg_errors"]
}
{
name: shadow_reg_storage_error
desc: '''Verify shadowed registers' storage error.
- Randomly pick a shadowed register in the DUT.
- Backdoor write to shadowed or committed flops to create a storage fatal alert.
- Check if fatal alert continuously fires until reset.
- Verify that all other frontdoor write attempts are blocked during the storage error.
- Verify that storage_error status register field is set to 1.
- Reset the DUT.
- Read all CSRs to ensure the DUT is properly reset.
- Repeat the above steps a bunch of times.
'''
milestone: V1

View file

@ -8,7 +8,7 @@
desc: '''This test runs 3 parallel threads - stress_all, tl_errors and random reset.
After reset is asserted, the test will read and check all valid CSR registers.
'''
milestone: V2
milestone: V3
tests: ["{name}_stress_all_with_rand_reset"]
}
]

View file

@ -27,7 +27,8 @@
- write a CSR with unaligned address, e.g. `a_address[1:0] != 0`
- write a CSR less than its width, e.g. when CSR is 2 bytes wide, only write 1 byte
- write a memory with `a_mask != '1` when it doesn't support partial accesses
- read a WO (write-only) memory'''
- read a WO (write-only) memory
- write a RO (read-only) memory'''
milestone: V2
tests: ["{name}_tl_errors"]
}
@ -63,5 +64,30 @@
tests: ["{name}_tl_intg_err"]
}
]
covergroups: [
{
name: tl_errors_cg
desc: '''Cover the following error cases on TL-UL bus:
- TL-UL protocol error cases.
- OpenTitan defined error cases, refer to testpoint `tl_d_illegal_access`.
'''
}
{
name: tl_intg_err_cg
desc: '''Cover all kinds of integrity errors (command, data or both) and cover number of
error bits on each integrity check.
'''
}
{
name: tl_intg_err_mem_subword_cg
desc: '''Cover the kinds of integrity errors with byte enabled write on memory.
Some memories store the integrity values. When there is a subword write, design
re-calculate the integrity with full word data and update integrity in the memory.
This coverage ensures that memory byte write has been issued and the related design
logic has been verfied.
'''
}
]
}

View file

@ -22,6 +22,7 @@
build_mode: "cover_reg_top"
run_opts: ["+csr_hw_reset"]
en_run_modes: ["csr_tests_mode"]
reseed: 5
}
{
@ -29,6 +30,7 @@
build_mode: "cover_reg_top"
run_opts: ["+csr_rw"]
en_run_modes: ["csr_tests_mode"]
reseed: 20
}
{
@ -36,6 +38,7 @@
build_mode: "cover_reg_top"
run_opts: ["+csr_bit_bash"]
en_run_modes: ["csr_tests_mode"]
reseed: 5
}
{
@ -43,6 +46,7 @@
build_mode: "cover_reg_top"
run_opts: ["+csr_aliasing"]
en_run_modes: ["csr_tests_mode"]
reseed: 5
}
{
@ -50,6 +54,7 @@
build_mode: "cover_reg_top"
run_opts: ["+run_same_csr_outstanding"]
en_run_modes: ["csr_tests_mode"]
reseed: 20
}
{
@ -57,6 +62,7 @@
build_mode: "cover_reg_top"
run_opts: ["+run_csr_mem_rw_with_rand_reset", "+test_timeout_ns=10000000000"]
en_run_modes: ["csr_tests_mode"]
reseed: 20
}
]

View file

@ -13,6 +13,7 @@
name: mem_tests_mode
uvm_test_seq: "{name}_common_vseq"
run_opts: ["+en_scb=0"]
reseed: 5
}
]

View file

@ -21,9 +21,7 @@
build_mode: "cover_reg_top"
uvm_test_seq: "{name}_common_vseq"
run_opts: ["+run_tl_intg_err", "+en_scb=0"]
// TODO, lower the reseed to avoid having many failed tests in regression as not all the IPs
// have the integrity alert connected
reseed: 1
reseed: 20
}
]
}

View file

@ -6,7 +6,7 @@
build_ex: "{build_dir}/simv"
run_cmd: "{job_prefix} {build_ex}"
build_opts: ["-sverilog -full64 -licqueue -kdb -ntb_opts uvm-1.2",
build_opts: ["-sverilog -full64 -licqueue -ntb_opts uvm-1.2",
"-timescale=1ns/1ps",
"-Mdir={build_ex}.csrc",
"-o {build_ex}",
@ -155,8 +155,10 @@
"+urg+lic+wait",
// Lists all the tests that covered a given object.
"-show tests",
// Enable test grading.
"-grade index",
// Enable test grading using the "index" scheme, and the
// generation of the list of contributing tests with
// "testfile".
"-grade index testfile",
// Use simple ratio of total covered bins over total bins across cps & crs,
"-group ratio",
// Compute overall coverage for per-instance covergroups individually rather
@ -165,7 +167,8 @@
"-dir {cov_merge_db_dir}",
"-line nocasedef",
"-format both",
"-elfile {vcs_cov_excl_files}",
// Prepend each el file with `-elfile`.
"{eval_cmd} echo {vcs_cov_excl_files} | sed -E 's/(\\S+)/-elfile \\1/g'",
"-report {cov_report_dir}"]
cov_report_txt: "{cov_report_dir}/dashboard.txt"
cov_report_page: "dashboard.html"
@ -246,7 +249,9 @@
{
name: vcs_waves
is_sim_mode: 1
build_opts: ["-debug_access"]
build_opts: [// Enable generating Verdi Knowledge Database
"-kdb",
"-debug_access"]
}
{
name: vcs_cov

View file

@ -21,6 +21,9 @@
"-snapshot {tb}",
// for uvm_hdl_* used by csr backdoor
"-access +rw",
// This is to fix a timescale issue due to using `resetall to reset timescale and
// `-sv` compile option. #7178
"-enable_strict_timescale",
// Use this to conditionally compile for Xcelium (example: LRM interpretations differ
// across tools).
"+define+XCELIUM",
@ -76,8 +79,10 @@
// Note that this needs to be set as -covfile <file>.
xcelium_cov_cfg_file: ""
// Supply the cov refinement files.
// Note that this needs to be set as -load_refinement <file>.
// Supply the cov exclusion tcl script - passed on to IMC using the -init switch.
xcelium_cov_excl_script: ""
// Supply the cov refinement files - passed on to IMC using the -load_refinement switch.
xcelium_cov_refine_files: []
// Set the coverage directories.
@ -103,8 +108,10 @@
cov_report_cmd: "{job_prefix} imc"
cov_report_opts: ["-64bit",
"-licqueue",
"-exec {dv_root}/tools/xcelium/cov_report.tcl",
"{xcelium_cov_refine_files}"]
"-load {cov_merge_db_dir}",
" {eval_cmd} echo {xcelium_cov_excl_script} | sed -E 's/(\\S+)/-init \\1/g' ",
" {eval_cmd} echo {xcelium_cov_refine_files} | sed -E 's/(\\S+)/-load_refinement \\1/g' ",
"-exec {dv_root}/tools/xcelium/cov_report.tcl"]
cov_report_txt: "{cov_report_dir}/cov_report.txt"
cov_report_page: "index.html"
@ -112,11 +119,10 @@
// GUI for visual analysis.
cov_analyze_dir: "{scratch_path}/cov_analyze"
cov_analyze_cmd: "{job_prefix} imc"
cov_analyze_opts: ["-gui",
"-64bit",
cov_analyze_opts: ["-64bit",
"-licqueue",
"-load {cov_merge_db_dir}",
"-init {xcelium_common_excl_file}",
" {eval_cmd} echo {xcelium_cov_excl_script} | sed -E 's/(\\S+)/-init \\1/g' ",
" {eval_cmd} echo {xcelium_cov_refine_files} | sed -E 's/(\\S+)/-load_refinement \\1/g' "]
cov_unr_dir: "{scratch_path}/cov_unr"
@ -171,7 +177,10 @@
// Limit the scope of coverage collection to the DUT.
"-covdut {dut}",
// Set the coverage configuration file
"-covfile {xcelium_cov_cfg_file}"]
"-covfile {xcelium_cov_cfg_file}",
// Don't warn about the switches we set that will be default in future releases.
"-nowarn COVDEF",
]
run_opts: [// Put the coverage model (*.ucm) and the database (*.ucd) together.
"-covmodeldir {cov_db_test_dir}",
// Coverage database output location.

View file

@ -0,0 +1,16 @@
// Copyright lowRISC contributors.
// Licensed under the Apache License, Version 2.0, see LICENSE for details.
// SPDX-License-Identifier: Apache-2.0
// [UNSUPPORTED] Based on our Comportable IP spec, these TL pins are reserved / unused and hence,
// tied off.
-node tb.dut *tl_i.a_user.rsvd
-node tb.dut *tl_i.a_param
// [UNR] design ties these outputs to zeros.
-node tb.dut *tl_o.d_param
-node tb.dut *tl_o.d_opcode[1]
-node tb.dut *tl_o.d_opcode[2]
-node tb.dut *tl_o.d_sink
// [LOW_RISK] Verified in prim_alert_receiver TB."
-node tb.dut *alert_rx_*.ping_*

View file

@ -1,16 +0,0 @@
// Copyright lowRISC contributors.
// Licensed under the Apache License, Version 2.0, see LICENSE for details.
// SPDX-License-Identifier: Apache-2.0
// Exclude below TL toggle coverage as these pins won't be changed in the comportable IPs.
// Exclude alert_sender as it is fully verified in FPV. Note that user might also need to exclude
// prim_diff_decode module, but it is not listed here because modules other than prim_alert_sender
// might instantiate this prim_diff_decode as well.
INSTANCE: tb.dut
Toggle tl_o.d_opcode [1] "logic tl_o.d_opcode[2:0]"
Toggle tl_o.d_opcode [2] "logic tl_o.d_opcode[2:0]"
Toggle tl_o.d_param "logic tl_o.d_param[2:0]"
Toggle tl_o.d_sink "logic tl_o.d_sink[0:0]"
Toggle tl_i.a_param "logic tl_i.a_param[2:0]"

View file

@ -8,7 +8,11 @@
+tree tb.dut
-module pins_if // DV construct.
-module clk_rst_if // DV construct.
-moduletree prim_alert_sender // prim_alert_sender is verified in FPV.
// Prim_alert/esc pairs are verified in FPV and DV testbenches.
-moduletree prim_alert_sender
-moduletree prim_alert_receiver
-moduletree prim_esc_sender
-moduletree prim_esc_receiver
-moduletree prim_prince // prim_prince is verified in a separate DV environment.
-moduletree prim_lfsr // prim_lfsr is verified in FPV.
@ -16,6 +20,20 @@ begin tgl
-tree tb
+tree tb.dut 1
+module prim_alert_sender
+module prim_alert_receiver
+module prim_esc_sender
+module prim_esc_receiver
+module prim_prince
+module prim_lfsr
end
begin assert
// These three assertions in prim_lc_sync check when `lc_ctrl_pkg::lc_tx_e` input is neither `On`
// or `Off`, it is interrupted to the correct `On` or `Off` after one clock cycle. This behavior
// is implemented outside of IP level design thus these assertions are not covered in IP level
// testbenchs.
// TODO: check these assertions in top-level or FPV.
-assert PrimLcSyncCheckTransients_A
-assert PrimLcSyncCheckTransients0_A
-assert PrimLcSyncCheckTransients1_A
end

View file

@ -8,7 +8,10 @@
+moduletree *_reg_top
+node tb.dut tl_*
// Remove everything else from toggle coverage.
// Remove everything else from toggle coverage except:
// - `prim_alert_sender`: the `alert_test` task under `cip_base_vseq` drives `alert_test_i` and
// verifies `alert_rx/tx` handshake in each IP.
begin tgl
-tree tb
+module prim_alert_sender
end

View file

@ -13,9 +13,6 @@
# Black box some of the modules
# -blackBoxes -type design *
# Include common el file, so that it doesn't generate reviewed common exclusions
-covEL $dv_root/tools/vcs/common_cov_excl.el
# Name of the generated exclusion file
-save_exclusion $SCRATCH_PATH/cov_unr/unr_exclude.el

View file

@ -0,0 +1,57 @@
// Copyright lowRISC contributors.
// Licensed under the Apache License, Version 2.0, see LICENSE for details.
// SPDX-License-Identifier: Apache-2.0
// Common coverage commands that apply to all DUTs.
//
// This coverge config file is provided by Xcelium and is located at:
// ${XCELIUM_HOME}/tools/icc/include/all_coverage.ccf
// Xcelium recommends including it, since it bundles together the common set of commands that enable
// coverage collection on various design elements, that are otherwise turned off by default. We
// maintain it locally with minor amends.
// Enables expression coverage of various Verilog operators.
set_expr_coverable_operators -all -event_or
// Enables expression coverage of operators in various conditions and assignments.
set_expr_coverable_statements -all
// Enables scoring of Verilog modules compiled with -v/-y or -libcell option but continues to
// disable the scoring of Verilog modules defined with the 'celldefine compiler directive.
set_libcell_scoring
// Enables scoring of block and expression coverage for functions and tasks defined directly inside
// SystemVerilog packages.
set_subprogram_scoring -svpackage
// Enables scoring of SystemVerilog continuous assignments, which is by disabled by default.
set_assign_scoring
// Scores branches together with block coverage.
set_branch_scoring
// Scores statements within a block.
set_statement_scoring
// Enables expression coverage for expression containing structs (packed and unpacked).
set_expr_scoring -struct
// Enables Toggle scoring and reporting of SystemVerilog enumerations and multidimensional static
// arrays , vectors, packed union, modport and generate blocks.
set_toggle_scoring -sv_enum enable_mda -sv_struct_with_enum -sv_modport -sv_mda 16 -sv_mda_of_struct -sv_generate -sv_packed_union
// Enables scoring of reset states and transitions for identified FSMs.
set_fsm_reset_scoring
// Enables scoring of immediate assertions inside a class in a package and assertions inside AMS
// modules.
select_functional -ams_control -imm_asrt_class_package
// Improve the scoping and naming of covergroup instances.
set_covergroup -new_instance_reporting
// Enable toggle coverage only on ports.
set_toggle_portsonly
// Enable scoring of FSM arcs (state transitions).
set_fsm_arc_scoring

View file

@ -0,0 +1,9 @@
# Copyright lowRISC contributors.
# Licensed under the Apache License, Version 2.0, see LICENSE for details.
# SPDX-License-Identifier: Apache-2.0
exclude -inst $::env(DUT_TOP) -toggle '*tl_i.a_user.rsvd' -comment "\[UNSUPPORTED\] Based on our Comportability Spec. Exercising this will result in assertion errors thrown."
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 '*alert_rx_*.ping_*' -comment "\[LOW_RISK\] Verified in prim_alert_receiver TB."

View file

@ -8,14 +8,14 @@
# using the env var 'cov_db_dirs' (which is a space separated list of directories).
# Append each of these directories with /* wildcard at the end to allow the tool to
# find all available test databases.
set cov_db_dirs_env [string trim $::env(cov_db_dirs) " \""]
set cov_db_dirs_env [string trim $::env(cov_db_dirs) " \"'"]
foreach i $cov_db_dirs_env { append cov_db_dirs "[string trim $i]/* "; }
puts "Input coverage directories:\n$cov_db_dirs"
# Set the output directory for the merged database using the env var 'cov_merge_db_dir'.
# The supplied env var may have quotes or spaces that needs to be trimmed.
puts "Output directory for merged coverage:"
set cov_merge_db_dir [string trim $::env(cov_merge_db_dir) " \""]
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

View file

@ -3,22 +3,18 @@
# SPDX-License-Identifier: Apache-2.0
# Generate reports for the merged coverage in HTML and text format.
# Set the input merged coverage database directory using the env var 'cov_merge_db_dir'.
# The supplied env var may have quotes or spaces that needs to be trimmed.
set cov_merge_db_dir [string trim $::env(cov_merge_db_dir) " \""]
#
# This file is passed to IMC using the -exec switch. Ensure that the merged coverage database, the
# exclusion script and the coverage refinement files are passed to the IMC invocation using the
# -load, -init and -load_refinement switches respectively (whichever ones are applicable).
# Set the output directory for the reports database using the env var 'cov_report_dir'.
# The supplied env var may have quotes or spaces that needs to be trimmed.
set cov_report_dir [string trim $::env(cov_report_dir) " \""]
set cov_report_dir [string trim $::env(cov_report_dir) " \"'"]
# Set the DUT name.
set dut [string trim $::env(DUT_TOP)]
set dut_uc [string toupper $dut]
set dut_instance [string trim $::env(dut_instance)]
# Load the merged coverage database.
load -run $cov_merge_db_dir
# Generate the text report (summary is sufficient).
report -summary \
@ -35,7 +31,6 @@ report -summary \
-type \
-all \
-metrics covergroup \
-kind abstract \
-source off \
-out $cov_report_dir/cov_report_cg.txt

View file

@ -0,0 +1,26 @@
// Copyright lowRISC contributors.
// Licensed under the Apache License, Version 2.0, see LICENSE for details.
// SPDX-License-Identifier: Apache-2.0
// Include our common coverage CCF.
include_ccf ${dv_root}/tools/xcelium/common.ccf
// Black-box pre-verified IPs from coverage collection.
deselect_coverage -betfs -module pins_if
deselect_coverage -betfs -module clk_rst_if
deselect_coverage -betfs -module prim_alert_sender...
deselect_coverage -betfs -module prim_alert_receiver...
deselect_coverage -betfs -module prim_esc_sender...
deselect_coverage -betfs -module prim_esc_receiver...
deselect_coverage -betfs -module prim_prince...
deselect_coverage -betfs -module prim_lfsr...
// Only collect toggle coverage on the DUT and the black-boxed IP (above) ports.
deselect_coverage -toggle -module ${DUT_TOP}...
select_coverage -toggle -module ${DUT_TOP}
select_coverage -toggle -module prim_alert_sender
select_coverage -toggle -module prim_alert_receiver
select_coverage -toggle -module prim_esc_sender
select_coverage -toggle -module prim_esc_receiver
select_coverage -toggle -module prim_prince
select_coverage -toggle -module prim_lfsr

View file

@ -0,0 +1,21 @@
// Copyright lowRISC contributors.
// Licensed under the Apache License, Version 2.0, see LICENSE for details.
// SPDX-License-Identifier: Apache-2.0
// Include our common coverage CCF.
include_ccf ${dv_root}/tools/xcelium/common.ccf
// Only collect code coverage on the *_reg_top instance.
deselect_coverage -betfs -module ${DUT_TOP}...
select_coverage -befs -module *_reg_top...
// Include toggle coverage on `prim_alert_sender` because the `alert_test` task under
// `cip_base_vseq` drives `alert_test_i` and verifies `alert_rx/tx` handshake in each IP.
select_coverage -toggle -module prim_alert_sender
// TODO: The intent below is to only enable coverage on the DUT's TL interfaces (tests using this
// ccf file are meant to fully exercise the TL interfaces, but they do not verify the rest of the
// functionality of the block). We enable coverage on all DUT ports but exclude ports that do not
// contain tl_i or tl_o in the port name using a separate excludefile that supports regexes.
select_coverage -toggle -module ${DUT_TOP}
set_toggle_excludefile ${dv_root}/tools/xcelium/cover_reg_top_toggle_excl

View file

@ -0,0 +1,7 @@
// Copyright lowRISC contributors.
// Licensed under the Apache License, Version 2.0, see LICENSE for details.
// SPDX-License-Identifier: Apache-2.0
// Exclude all but the TL ports of the DUT.
// TODO: This does not work.
-ere module ^((?!tl_i$|tl_o$).)*$

View file

@ -1,11 +0,0 @@
# Copyright lowRISC contributors.
# Licensed under the Apache License, Version 2.0, see LICENSE for details.
# SPDX-License-Identifier: Apache-2.0
exclude -inst $::env(DUT_TOP) -toggle 'tl_i.a_user.parity'
exclude -inst $::env(DUT_TOP) -toggle 'tl_i.a_user.parity_en'
exclude -inst $::env(DUT_TOP) -toggle 'tl_i.a_param'
exclude -inst $::env(DUT_TOP) -toggle 'tl_o.d_param'
exclude -inst $::env(DUT_TOP) -toggle 'tl_o.d_sink'
exclude -inst $::env(DUT_TOP) -toggle 'tl_o.d_user'
exclude -inst $::env(DUT_TOP) -toggle 'tl_i.a_user.rsvd1'

View file

@ -1,18 +0,0 @@
// Copyright lowRISC contributors.
// Licensed under the Apache License, Version 2.0, see LICENSE for details.
// SPDX-License-Identifier: Apache-2.0
// only collect toggle coverage on dut
deselect_coverage -toggle -instance tb.dut.*...
// only collect toggle coverage on ports
set_toggle_portsonly
// enable toggle scoring of structs and multidim arrays + MDA structs
set_toggle_scoring -sv_mda -sv_mda_of_struct
// filter glitches
set_toggle_strobe 0ps
// Filter unreachable/statically constant blocks
set_com -log

View file

@ -146,7 +146,7 @@ static std::vector<uint8_t> FlattenElfFile(const std::string &filepath) {
continue;
}
if (phdr.p_memsz == 0) {
if (phdr.p_filesz == 0) {
continue;
}
@ -154,11 +154,11 @@ static std::vector<uint8_t> FlattenElfFile(const std::string &filepath) {
low = phdr.p_paddr;
}
Elf32_Addr seg_top = phdr.p_paddr + (phdr.p_memsz - 1);
Elf32_Addr seg_top = phdr.p_paddr + (phdr.p_filesz - 1);
if (seg_top < phdr.p_paddr) {
std::ostringstream oss;
oss << "phdr for segment " << i << " has start 0x" << std::hex
<< phdr.p_paddr << " and size 0x" << phdr.p_memsz
<< phdr.p_paddr << " and size 0x" << phdr.p_filesz
<< ", which overflows the address space.";
throw ElfError(filepath, oss.str());
}
@ -201,15 +201,12 @@ static std::vector<uint8_t> FlattenElfFile(const std::string &filepath) {
throw ElfError(filepath, oss.str());
}
uint32_t off = phdr.p_paddr - low;
uint32_t dst_len = phdr.p_memsz;
uint32_t src_len = std::min(phdr.p_filesz, dst_len);
if (!dst_len)
if (phdr.p_filesz == 0)
continue;
std::vector<uint8_t> seg(dst_len, 0);
memcpy(&seg[0], file_data + phdr.p_offset, src_len);
uint32_t off = phdr.p_paddr - low;
std::vector<uint8_t> seg(phdr.p_filesz, 0);
memcpy(&seg[0], file_data + phdr.p_offset, phdr.p_filesz);
ret.AddSegment(off, std::move(seg));
}
@ -473,11 +470,11 @@ void DpiMemUtil::StageElf(bool verbose, const std::string &path) {
if (phdr.p_type != PT_LOAD)
continue;
if (phdr.p_memsz == 0)
if (phdr.p_filesz == 0)
continue;
size_t mem_area_idx =
GetRegionForSegment(path, i, phdr.p_paddr, phdr.p_memsz);
GetRegionForSegment(path, i, phdr.p_paddr, phdr.p_filesz);
const MemArea &mem_area = *mem_areas_[mem_area_idx];
uint32_t mem_area_base = base_addrs_[mem_area_idx];
@ -518,8 +515,8 @@ void DpiMemUtil::StageElf(bool verbose, const std::string &path) {
StagedMem &staged_mem = staging_area_[name];
const char *seg_data = file_data + phdr.p_offset;
std::vector<uint8_t> vec(phdr.p_memsz, 0);
memcpy(&vec[0], seg_data, std::min(phdr.p_filesz, phdr.p_memsz));
std::vector<uint8_t> vec(phdr.p_filesz, 0);
memcpy(&vec[0], seg_data, phdr.p_filesz);
staged_mem.AddSegment(local_base, std::move(vec));
}

View file

@ -12,7 +12,11 @@
// This is the maximum width of a memory that's supported by the code in
// prim_util_memload.svh
#define SV_MEM_WIDTH_BITS 312
#define SV_MEM_WIDTH_BYTES ((SV_MEM_WIDTH_BITS + 7) / 8)
// This is the number of bytes to reserve for buffers used to pass data between
// C++ and SystemVerilog using prim_util_memload.svh. Since this goes over DPI
// using the svBitVecVal type, we have to round up to the next 32-bit word.
#define SV_MEM_WIDTH_BYTES (4 * ((SV_MEM_WIDTH_BITS + 31) / 32))
/**
* A "memory area", representing a memory in the simulated design.

View file

@ -0,0 +1,158 @@
// Copyright lowRISC contributors.
// Licensed under the Apache License, Version 2.0, see LICENSE for details.
// SPDX-License-Identifier: Apache-2.0
//
// ------------------- W A R N I N G: A U T O - G E N E R A T E D C O D E !! -------------------//
// PLEASE DO NOT HAND-EDIT THIS FILE. IT HAS BEEN AUTO-GENERATED WITH THE FOLLOWING COMMAND:
//
// hw/ip/prim/util/generate_prim_mubi_pkg.py hw/ip/prim/data/prim_mubi_pkg.sv.tpl >
// hw/ip/prim/rtl/prim_mubi_pkg.sv
//
// This package defines common multibit signal types, active high and active low values and
// the corresponding functions to test whether the values are set or not.
package prim_mubi_pkg;
% for n in range(1, n_max_nibbles + 1):
<%
nbits = n * 4
hi_val = ''
lo_val = ''
for k in range(1,n+1):
hi_val = ('5' if (k % 2) else 'A') + hi_val
lo_val = ('A' if (k % 2) else '5') + lo_val
%>\
//////////////////////////////////////////////
// ${nbits} Bit Multibit Type and Functions //
//////////////////////////////////////////////
parameter int MuBi${nbits}Width = ${nbits};
typedef enum logic [MuBi${nbits}Width-1:0] {
MuBi${nbits}Hi = ${nbits}'h${hi_val}, // enabled
MuBi${nbits}Lo = ${nbits}'h${lo_val} // disabled
} mubi${nbits}_e;
// make a typedef such that this can be used as an intersignal type as well
typedef mubi${nbits}_e mubi${nbits}_t;
// Return the multibit value to signal "enabled".
function automatic mubi${nbits}_e mubi${nbits}_hi_value();
return MuBi${nbits}Hi;
endfunction : mubi${nbits}_hi_value
// Return the multibit value to signal "disabled".
function automatic mubi${nbits}_e mubi${nbits}_lo_value();
return MuBi${nbits}Lo;
endfunction : mubi${nbits}_lo_value
// Test whether the multibit value signals an "enabled" condition.
// The strict version of this function requires
// the multibit value to equal Hi.
function automatic logic mubi${nbits}_tst_hi_strict(mubi${nbits}_e val);
return MuBi${nbits}Hi == val;
endfunction : mubi${nbits}_tst_hi_strict
// Test whether the multibit value signals a "disabled" condition.
// The strict version of this function requires
// the multibit value to equal Lo.
function automatic logic mubi${nbits}_tst_lo_strict(mubi${nbits}_e val);
return MuBi${nbits}Lo == val;
endfunction : mubi${nbits}_tst_lo_strict
// Test whether the multibit value signals an "enabled" condition.
// The loose version of this function interprets all
// values other than Lo as "enabled".
function automatic logic mubi${nbits}_tst_hi_loose(mubi${nbits}_e val);
return MuBi${nbits}Lo != val;
endfunction : mubi${nbits}_tst_hi_loose
// Test whether the multibit value signals a "disabled" condition.
// The loose version of this function interprets all
// values other than Hi as "disabled".
function automatic logic mubi${nbits}_tst_lo_loose(mubi${nbits}_e val);
return MuBi${nbits}Hi != val;
endfunction : mubi${nbits}_tst_lo_loose
// Performs a logical OR operation between two multibit values.
// This treats "act" as logical 1, and all other values are
// treated as 0. Truth table:
//
// A | B | OUT
//------+------+-----
// !act | !act | !act
// act | !act | act
// !act | act | act
// act | act | act
//
function automatic mubi${nbits}_e mubi${nbits}_or(mubi${nbits}_e a, mubi${nbits}_e b, mubi${nbits}_e act);
logic [MuBi${nbits}Width-1:0] a_in, b_in, act_in, out;
a_in = a;
b_in = b;
act_in = act;
for (int k = 0; k < MuBi${nbits}Width; k++) begin
if (act_in[k]) begin
out[k] = a_in[k] || b_in[k];
end else begin
out[k] = a_in[k] && b_in[k];
end
end
return mubi${nbits}_e'(out);
endfunction : mubi${nbits}_or
// Performs a logical AND operation between two multibit values.
// This treats "act" as logical 1, and all other values are
// treated as 0. Truth table:
//
// A | B | OUT
//------+------+-----
// !act | !act | !act
// act | !act | !act
// !act | act | !act
// act | act | act
//
function automatic mubi${nbits}_e mubi${nbits}_and(mubi${nbits}_e a, mubi${nbits}_e b, mubi${nbits}_e act);
logic [MuBi${nbits}Width-1:0] a_in, b_in, act_in, out;
a_in = a;
b_in = b;
act_in = act;
for (int k = 0; k < MuBi${nbits}Width; k++) begin
if (act_in[k]) begin
out[k] = a_in[k] && b_in[k];
end else begin
out[k] = a_in[k] || b_in[k];
end
end
return mubi${nbits}_e'(out);
endfunction : mubi${nbits}_and
// Performs a logical OR operation between two multibit values.
// This treats "Hi" as logical 1, and all other values are
// treated as 0.
function automatic mubi${nbits}_e mubi${nbits}_or_hi(mubi${nbits}_e a, mubi${nbits}_e b);
return mubi${nbits}_or(a, b, MuBi${nbits}Hi);
endfunction : mubi${nbits}_or_hi
// Performs a logical AND operation between two multibit values.
// This treats "Hi" as logical 1, and all other values are
// treated as 0.
function automatic mubi${nbits}_e mubi${nbits}_and_hi(mubi${nbits}_e a, mubi${nbits}_e b);
return mubi${nbits}_and(a, b, MuBi${nbits}Hi);
endfunction : mubi${nbits}_and_hi
// Performs a logical OR operation between two multibit values.
// This treats "Lo" as logical 1, and all other values are
// treated as 0.
function automatic mubi${nbits}_e mubi${nbits}_or_lo(mubi${nbits}_e a, mubi${nbits}_e b);
return mubi${nbits}_or(a, b, MuBi${nbits}Lo);
endfunction : mubi${nbits}_or_lo
// Performs a logical AND operation between two multibit values.
// Tlos treats "Lo" as logical 1, and all other values are
// treated as 0.
function automatic mubi${nbits}_e mubi${nbits}_and_lo(mubi${nbits}_e a, mubi${nbits}_e b);
return mubi${nbits}_and(a, b, MuBi${nbits}Lo);
endfunction : mubi${nbits}_and_lo
% endfor
endpackage : prim_mubi_pkg

View file

@ -0,0 +1,125 @@
---
title: "Primitive Component: SRAM Scrambler"
---
# Overview
The scrambling primitive `prim_ram_1p_scr` employs a reduced-round (5 instead of 11) PRINCE block cipher in CTR mode to scramble the data.
The PRINCE lightweight block cipher has been selected due to its low latency and low area characteristics, see also [prim_prince]({{< relref "hw/ip/prim/doc/prim_prince" >}}) for more information on PRINCE.
The number of rounds is reduced to 5 in order to ease timing pressure and ensure single cycle operation (the number of rounds can always be increased if it turns out that there is enough timing slack).
In [CTR mode](https://en.wikipedia.org/wiki/Block_cipher_mode_of_operation#Counter_(CTR)), the block cipher is used to encrypt a 64bit IV with the scrambling key in order to create a 64bit keystream block that is bitwise XOR'ed with the data in order to transform plaintext into ciphertext and vice versa.
The IV is assembled by concatenating a nonce with the word address.
If the word width of the scrambled memory is smaller than 64bit, the keystream block is truncated to fit the data width.
If the word width is wider than 64bit, the scrambling primitive by default instantiates multiple PRINCE primitives in order to create a unique keystream for the full datawidth.
For area constrained settings, the parameter `ReplicateKeyStream` in `prim_ram_1p_scr` can be set to 1 in order to replicate the keystream block generated by one single primitive instead of using multiple parallel PRINCE instances (but it should be understood that this lowers the level of security).
Since plain CTR mode does not diffuse the data bits due to the bitwise XOR, the scheme is augmented by passing each individual word through a two-layer substitution-permutation (S&P) network implemented with the `prim_subst_perm` primitive (the diffusion chunk width can be parameterized via the `DiffWidth` parameter).
The S&P network employed is similar to the one employed in PRESENT and will be explained in more detail [further below]({{< relref "#custom-substitution-permutation-network" >}}).
Note that if individual bytes need to be writable without having to perform a read-modify-write operation, the diffusion chunk width should be set to 8.
Another CTR mode augmentation that is aimed at breaking the linear address space is SRAM address scrambling.
The same two-layer S&P network that is used for byte diffusion is leveraged to non-linearly remap the SRAM address as shown in the block diagram above.
As opposed to the byte diffusion S&P networks, this particular address scrambling network additionally XOR's in a nonce that has the same width as the address.
## Parameters
The following table lists the instantiation parameters of the `prim_ram_1p_scr` primitive.
These are not exposed in the `sram_ctrl` IP, but have to be set directly when instantiating `prim_ram_1p_scr` in the top.
Parameter | Default (Max) | Top Earlgrey | Description
----------------------------|-----------------------|--------------|---------------
`Depth` | 512 | multiple | SRAM depth, needs to be a power of 2 if `NumAddrScrRounds` > 0.
`Width` | 32 | 32 | Effective SRAM width without redundancy.
`DataBitsPerMask` | 8 | 8 | Number of data bits per write mask.
`EnableParity` | 1 | 1 | This parameter enables byte parity.
`CfgWidth` | 8 | 8 | Width of SRAM attributes field.
`NumPrinceRoundsHalf` | 2 (5) | 2 | Number of PRINCE half-rounds.
`NumDiffRounds` | 2 | 2 | Number of additional diffusion rounds, set to 0 to disable.
`DiffWidth` | 8 | 8 | Width of additional diffusion rounds, set to 8 for intra-byte diffusion.
`NumAddrScrRounds` | 2 | 2 | Number of address scrambling rounds, set to 0 to disable.
`ReplicateKeyStream` | 0 (1) | 0 | If set to 1, the same 64bit key stream is replicated if the data port is wider than 64bit. Otherwise, multiple PRINCE primitives are employed to generate a unique keystream for the full data width.
## Signal Interfaces
Signal | Direction | Type | Description
---------------------------|------------------|------------------------------------|---------------
`key_valid_i` | `input` | `logic` | Indicates whether the key and nonce are considered valid. New memory requests are blocked if this is set to 0.
`key_i` | `input` | `logic [127:0]` | Scrambling key.
`nonce_i` | `input` | `logic [NonceWidth-1:0]` | Scrambling nonce.
`req_i` | `input` | `logic` | Memory request indication signal (from TL-UL SRAM adapter).
`gnt_o` | `output` | `logic` | Grant signal for memory request (to TL-UL SRAM adapter)
`write_i` | `input` | `logic` | Indicates that this is a write operation (from TL-UL SRAM adapter).
`addr_i` | `input` | `logic [AddrWidth-1:0]` | Address for memory op (from TL-UL SRAM adapter).
`wdata_i` | `input` | `logic [Width-1:0]` | Write data (from TL-UL SRAM adapter).
`wmask_i` | `input` | `logic [Width-1:0]` | Write mask (from TL-UL SRAM adapter).
`intg_error_i` | `input` | `logic` | Indicates whether the incoming transaction has an integrity error
`rdata_o` | `output` | `logic [Width-1:0]` | Read data output (to TL-UL SRAM adapter).
`rvalid_o` | `output` | `logic` | Read data valid indication (to TL-UL SRAM adapter).
`rerror_o` | `output` | `logic [1:0]` | Error indication (to TL-UL SRAM adapter). Bit 0 indicates a correctable and bit 1 an uncorrectable error. Note that at this time, only uncorrectable errors are reported, since the scrambling device only supports byte parity.
`raddr_o` | `output` | `logic [31:0]` | Address of the faulty read operation.
`cfg_i` | `input` | `logic [CfgWidth-1:0]` | Attributes for physical memory macro.
## Custom Substitution Permutation Network
In addition to the PRINCE primitive, `prim_ram_1p_scr` employs a custom S&P network for byte diffusion and address scrambling.
The structure of that S&P network is similar to the one used in PRESENT, but it uses a modified permutation function that makes it possible to parameterize the network to arbitrary data widths as shown in the pseudo code below.
```c++
NUM_ROUNDS = 2;
DATA_WIDTH = 8; // bitwidth of the data
// Apply PRESENT Sbox4 on all nibbles, leave uppermost bits unchanged
// if the width is not divisible by 4.
state_t sbox4_layer(state) {
for (int i = 0; i < DATA_WIDTH/4; i ++) {
nibble_t nibble = get_nibble(state, i);
nibble = present_sbox4(nibble)
set_nibble(state, i, nibble);
}
return state;
}
// Reverses the bit order.
state_t flip_vector(state) {
state_t state_flipped;
for (int i = 0; i < DATA_WIDTH; i ++) {
state_flipped[i] = state[width-1-i];
}
return state_flipped;
}
// Gather all even bits and put them into the lower half.
// Gather all odd bits and put them into the upper half.
state_t perm_layer(state) {
// Initialize with input state.
// If the number of bits is odd, the uppermost bit
// will stay in position, as intended.
state_t state_perm = state;
for (int i = 0; i < DATA_WIDTH/2; i++) {
state_perm[i] = state[i * 2];
state_perm[i + DATA_WIDTH/2] = state[i * 2 + 1];
}
return state_perm;
}
state_t prim_subst_perm(data_i, key_i) {
state_t state = data_i;
for (int i = 0; i < NUM_ROUNDS; i++) {
state ^= key_i;
state = sbox4_layer(state);
// The vector flip and permutation operations have the
// combined effect that all bits will be passed through an
// Sbox4 eventually, even if the number of bits in data_i
// is not aligned with 4.
state = flip_vector(state);
state = perm_layer(state);
}
return state ^ key_i;
}
```

View file

@ -0,0 +1,10 @@
// Copyright lowRISC contributors.
// Licensed under the Apache License, Version 2.0, see LICENSE for details.
// SPDX-License-Identifier: Apache-2.0
+module prim_alert_sender
+module prim_alert_receiver
begin tgl(portsonly)
+module prim_alert_sender
+module prim_alert_receiver
end

View file

@ -0,0 +1,76 @@
// Copyright lowRISC contributors.
// Licensed under the Apache License, Version 2.0, see LICENSE for details.
// SPDX-License-Identifier: Apache-2.0
{
name: "prim_alert"
testpoints: [
{
name: prim_alert_request_test
desc: '''Verify alert request from prim_alert_sender.
- Send an alert request by driving `alert_req` pin to 1.
- Verify that `alert_ack` signal is set and the alert handshake completes.
- If the alert is fatal, verify if the alert continuous fires until a reset is
issued.
'''
milestone: V1
tests: ["prim_async_alert",
"prim_async_fatal_alert",
"prim_sync_alert",
"prim_sync_fatal_alert"]
}
{
name: prim_alert_test
desc: '''Verify alert test request from prim_alert_sender.
- Send an alert test request by driving `alert_test` pin to 1.
- Verify that alert handshake completes and `alert_ack` signal stays low.
'''
milestone: V1
tests: ["prim_async_alert",
"prim_async_fatal_alert",
"prim_sync_alert",
"prim_sync_fatal_alert"]
}
{
name: prim_alert_ping_request_test
desc: '''Verify ping request from prim_alert_sender.
- 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.
'''
milestone: V1
tests: ["prim_async_alert",
"prim_async_fatal_alert",
"prim_sync_alert",
"prim_sync_fatal_alert"]
}
{
name: prim_alert_integrity_errors_test
desc: '''Verify the integrity errors from the prim_alert_sender and prim_alert_receiver pair.
1). `Ack_p/n` integrity error:
- Send an alert reqeust by driving `alert_req` pin to 1.
- Force `ack_p` signal to stay low to trigger an integrity error.
- Verify that prim_alert_receiver can identify the integrity error by setting
`integ_fail_o` output to 1.
2). `Ping_p/n` integrity error:
- Send a ping request by driving `ping_req` to 1.
- Force `ping_n` signal to 1 to trigger an integrity error.
- Verify that prim_alert_receiver can identify the integrity error by setting
`integ_fail_o` output to 1.
'''
milestone: V1
tests: ["prim_async_alert",
"prim_async_fatal_alert",
"prim_sync_alert",
"prim_sync_fatal_alert"]
}
]
}

View file

@ -0,0 +1,32 @@
CAPI=2:
# Copyright lowRISC contributors.
# Licensed under the Apache License, Version 2.0, see LICENSE for details.
# SPDX-License-Identifier: Apache-2.0
name: "lowrisc:dv:prim_alert_sim:0.1"
description: "ALERT DV sim target"
filesets:
files_rtl:
depend:
- lowrisc:prim:alert
file_type: systemVerilogSource
files_dv:
depend:
- lowrisc:dv:dv_utils
- lowrisc:dv:dv_test_status
- lowrisc:dv:common_ifs
files:
- tb/prim_alert_tb.sv
file_type: systemVerilogSource
targets:
sim: &sim_target
toplevel: prim_alert_tb
filesets:
- files_rtl
- files_dv
default_tool: vcs
lint:
<<: *sim_target

View file

@ -0,0 +1,75 @@
// Copyright lowRISC contributors.
// Licensed under the Apache License, Version 2.0, see LICENSE for details.
// SPDX-License-Identifier: Apache-2.0
{
// Name of the sim cfg - typically same as the name of the DUT.
name: prim_alert
// Top level dut name (sv module).
dut: prim_alert
// Top level testbench name (sv module).
tb: prim_alert_tb
// Simulator used to sign off this block
tool: vcs
// Fusesoc core file used for building the file list.
fusesoc_core: lowrisc:dv:prim_alert_sim:0.1
// Testplan hjson file.
testplan: "{proj_root}/hw/ip/prim/dv/prim_alert/data/prim_alert_testplan.hjson"
// Import additional common sim cfg files.
import_cfgs: ["{proj_root}/hw/dv/tools/dvsim/common_sim_cfg.hjson"]
// Default iterations for all tests - each test entry can override this.
reseed: 1
build_modes: [
{
name: sync_alert
build_opts: ["+define+IS_SYNC"]
}
{
name: fatal_alert
build_opts: ["+define+IS_FATAL"]
}
{
name: sync_fatal_alert
build_opts: ["+define+IS_FATAL", "+define+IS_SYNC"]
}
]
// List of test specifications.
tests: [
{
name: prim_async_alert
}
{
name: prim_async_fatal_alert
build_mode: fatal_alert
}
{
name: prim_sync_alert
build_mode: sync_alert
}
{
name: prim_sync_fatal_alert
build_mode: sync_fatal_alert
}
]
// List of regressions.
regressions: [
{
name: smoke
tests: ["prim_async_alert"]
}
]
overrides: [
{
name: vcs_cov_cfg_file
value: "-cm_hier {proj_root}/hw/ip/prim/dv/prim_alert/data/prim_alert_cover.cfg"
}
]
}

View file

@ -0,0 +1,320 @@
// Copyright lowRISC contributors.
// Licensed under the Apache License, Version 2.0, see LICENSE for details.
// SPDX-License-Identifier: Apache-2.0
//
// Testbench module for prim_alert_sender and prim_alert_receiver pair.
//
// This direct test has five test sequences:
// 1). Alert request sequence.
// 2). Alert test sequence.
// 3). Ping request sequence.
// 4). `Ack_p/n` integrity check sequence.
// 5). `Ping_p/n` integrity check sequence.
//
// This direct sequence did not drive `ack_p/n` to trigger integrity error because this sequence is
// covered in alert_handler IP level test.
module prim_alert_tb;
//////////////////////////////////////////////////////
// config
//////////////////////////////////////////////////////
// this can be overriden on the command line
`ifdef IS_SYNC
localparam bit IsAsync = 0;
`else
localparam bit IsAsync = 1;
`endif
`ifdef IS_FATAL
localparam bit IsFatal = 1;
`else
localparam bit IsFatal = 0;
`endif
localparam time ClkPeriod = 10000;
localparam int WaitCycle = IsAsync ? 3 : 1;
// Minimal cycles to wait between each sequence.
// The main concern here is the minimal wait cycles between each handshake.
localparam int MinHandshakeWait = 2 + WaitCycle;
typedef enum bit [3:0]{
AlertSet,
AlertAckSet,
AlertReset,
AlertAckReset
} alert_handshake_e;
typedef enum bit[1:0] {
PingPair,
AlertPair,
AckPair
} alert_signal_pair_e;
//////////////////////////////////////////////////////
// Clock and Reset
//////////////////////////////////////////////////////
wire clk, rst_n;
clk_rst_if main_clk (
.clk,
.rst_n
);
//////////////////////////////////////////////////////
// DUTs
//////////////////////////////////////////////////////
logic alert_test, alert_req, alert_ack, alert_state;
logic ping_req, ping_ok, integ_fail, alert_o;
prim_alert_pkg::alert_rx_t alert_rx;
prim_alert_pkg::alert_tx_t alert_tx;
prim_alert_sender #(
.AsyncOn(IsAsync),
.IsFatal(IsFatal)
) i_alert_sender (
.clk_i(clk),
.rst_ni(rst_n),
.alert_test_i(alert_test),
.alert_req_i(alert_req),
.alert_ack_o(alert_ack),
.alert_state_o(alert_state),
.alert_rx_i(alert_rx),
.alert_tx_o(alert_tx)
);
prim_alert_receiver #(
.AsyncOn(IsAsync)
) i_alert_receiver (
.clk_i(clk),
.rst_ni(rst_n),
// TODO: randomly trigger this
.init_trig_i(lc_ctrl_pkg::Off),
.ping_req_i(ping_req),
.ping_ok_o(ping_ok),
.integ_fail_o(integ_fail),
.alert_o(alert_o),
.alert_rx_o(alert_rx),
.alert_tx_i(alert_tx)
);
//////////////////////////////////////////////////////
// Helper Functions/Tasks and Variables
//////////////////////////////////////////////////////
logic error = 0;
// `Alert`, `Ack`, and `Ping` are all differential signal pairs with postfix `_p` and `_n`.
function automatic void check_diff_pair(bit exp_p, alert_signal_pair_e signal_pair);
bit exp_n = ~exp_p;
bit act_p, act_n;
string err_msg;
case (signal_pair)
PingPair: begin
act_p = alert_rx.ping_p;
act_n = alert_rx.ping_n;
err_msg = "alert_rx.ping mismatch";
end
AlertPair: begin
act_p = alert_tx.alert_p;
act_n = alert_tx.alert_n;
err_msg = "alert_tx.alert mismatch";
end
AckPair: begin
act_p = alert_rx.ack_p;
act_n = alert_rx.ack_n;
err_msg = "alert_rx.ack mismatch";
end
default: begin
$error($sformatf("Invalid signal_pair value %0d", signal_pair));
error = 1;
end
endcase
if (exp_p != act_p) begin
error = 1;
$error($sformatf("%0s: exp_p=%0d act_p=%0d", err_msg, exp_p, act_p));
end
if (exp_n != act_n) begin
error = 1;
$error($sformatf("%0s: exp_n=%0d act_n=%0d", err_msg, exp_n, act_n));
end
endfunction
// Check `alert`, `ack`, and `ping` differential pairs with given alert_handshake stage and
// expected ping value.
function automatic void check_alert_rxtx(alert_handshake_e alert_handshake, bit exp_ping);
case (alert_handshake)
AlertSet: begin
check_diff_pair(1, AlertPair);
check_diff_pair(0, AckPair);
end
AlertAckSet: begin
check_diff_pair(1, AlertPair);
check_diff_pair(1, AckPair);
end
AlertReset: begin
check_diff_pair(0, AlertPair);
check_diff_pair(1, AckPair);
end
AlertAckReset: begin
check_diff_pair(0, AlertPair);
check_diff_pair(0, AckPair);
end
default: begin
$error($sformatf("Invalid alert_handshake value %0d", alert_handshake));
error = 1;
end
endcase
check_diff_pair(exp_ping, PingPair);
endfunction
// Verify the alert handshake protocol with the following pattern:
// 1). alert_p = 1, alert_n = 0;
// 2). ack_p = 1, ack_n = 0;
// 3). ack_p = 0, ack_n = 1;
// 4). alert_p = 0, alert_n = 1;
// There is a fixed cycles of delay between each sequence depending on if the alert is sync or
// async mode.
task automatic check_alert_handshake(bit exp_ping_value);
check_alert_rxtx(AlertSet, exp_ping_value);
main_clk.wait_clks(WaitCycle);
check_alert_rxtx(AlertAckSet, exp_ping_value);
main_clk.wait_clks(WaitCycle);
check_alert_rxtx(AlertReset, exp_ping_value);
main_clk.wait_clks(WaitCycle);
check_alert_rxtx(AlertAckReset, exp_ping_value);
endtask
//////////////////////////////////////////////////////
// Stimuli Application / Response Checking
//////////////////////////////////////////////////////
initial begin: p_stimuli
alert_test = 0;
alert_req = 0;
ping_req = 0;
main_clk.set_period_ps(ClkPeriod);
main_clk.set_active();
main_clk.apply_reset();
// Wait for initialization sequence to end
// This should take no more than 20 cycles
// if the sender / receiver clocks are on
// the same clock domain.
main_clk.wait_clks(20);
// Sequence 1). Alert request sequence.
main_clk.wait_clks($urandom_range(0, 10));
alert_req = 1;
fork
begin
main_clk.wait_clks(1);
check_alert_handshake(.exp_ping_value(0));
end
begin
wait (alert_ack == 1);
alert_req = 0;
end
join
// If alert is fatal, check alert will continuously fire until reset.
if (IsFatal) begin
main_clk.wait_clks($urandom_range(10, 1000));
wait (alert_tx.alert_p == 0);
wait (alert_tx.alert_p == 1);
main_clk.wait_clks(1);
check_alert_handshake(.exp_ping_value(0));
main_clk.apply_reset();
end
$display("Alert request sequence finished!");
// Sequence 2). Alert test sequence.
main_clk.wait_clks($urandom_range(MinHandshakeWait, 10));
alert_test = 1;
fork : isolation_fork
begin: isolation_fork
fork
begin
main_clk.wait_clks(1);
alert_test = 0;
check_alert_handshake(.exp_ping_value(0));
// wait random clocks to ensure alert_ack is not set after alert handshake finishes.
main_clk.wait_clks($urandom_range(10, 20));
end
forever begin
main_clk.wait_clks(1);
if (alert_ack == 1) begin
$error("Alert ack should not set high during alert_test sequence!");
error = 1;
end
end
join_any
disable fork;
end
join
$display("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
// Ping is level triggered, so the two exp_ping value should be 1 and 0.
automatic bit exp_ping = (i == 0);
main_clk.wait_clks($urandom_range(MinHandshakeWait, 10));
ping_req = 1;
fork
begin
main_clk.wait_clks(1);
check_diff_pair(exp_ping, PingPair);
main_clk.wait_clks(WaitCycle);
check_alert_handshake(.exp_ping_value(exp_ping));
end
begin
wait (ping_ok == 1);
ping_req = 0;
end
join
$display($sformatf("Ping request sequence[%0d] finished!", i));
end
// Sequence 4) `Ack_p/n` integrity check sequence.
// Note that alert_tx signal interigy errors are verified in alert_handler testbench.
main_clk.wait_clks($urandom_range(MinHandshakeWait, 10));
alert_req = 1;
$assertoff(0, prim_alert_tb.i_alert_receiver.AckDiffOk_A);
force i_alert_receiver.alert_rx_o.ack_p = 0;
wait (integ_fail == 1);
alert_req = 0;
release i_alert_receiver.alert_rx_o.ack_p;
// Wait until async or sync signal propogate from alert to ack.
main_clk.wait_clks(WaitCycle);
$asserton(0, prim_alert_tb.i_alert_receiver.AckDiffOk_A);
$display("Ack signal integrity error sequence finished!");
// Sequence 5) `Ping_p/n` integrity check sequence.
// Disable the assertion at least two clock cycles before sending the ping request, because the
// `PingDiffOk_A` assertion has ##2 delay.
$assertoff(2, prim_alert_tb.i_alert_receiver.PingDiffOk_A);
main_clk.wait_clks($urandom_range(MinHandshakeWait, 10));
ping_req = 1;
force i_alert_receiver.alert_rx_o.ping_n = 1;
wait (integ_fail == 1);
ping_req = 0;
release i_alert_receiver.alert_rx_o.ping_p;
// Ping is the first signal of the handshake, so we can directly turn on the assertion once the
// forced ping signal is released.
$asserton(0, prim_alert_tb.i_alert_receiver.PingDiffOk_A);
$display("Ping signal integrity error sequence finished!");
dv_test_status_pkg::dv_test_status(.passed(!error));
$finish();
end
endmodule

View file

@ -0,0 +1,10 @@
// Copyright lowRISC contributors.
// Licensed under the Apache License, Version 2.0, see LICENSE for details.
// SPDX-License-Identifier: Apache-2.0
+module prim_esc_sender
+module prim_esc_receiver
begin tgl(portsonly)
+module prim_esc_sender
+module prim_esc_receiver
end

View file

@ -0,0 +1,91 @@
// Copyright lowRISC contributors.
// Licensed under the Apache License, Version 2.0, see LICENSE for details.
// SPDX-License-Identifier: Apache-2.0
{
name: "prim_esc"
testpoints: [
{
name: prim_esc_request_test
desc: '''Verify escalation request from prim_esc_sender.
- Send an escalation request by driving `esc_req` pin to 1.
- Wait random length of cycles and verify `esc_en` output is set and `integ_fail`
output remains 0.
'''
milestone: V1
tests: ["prim_esc_test"]
}
{
name: prim_ping_req_interrupted_by_esc_req_test
desc: '''Verify prim_esc will process the esc_req when ping handshake is in progress.
- Send a ping request by driving `ping_req` pin to 1.
- Randomly wait a few cycles before the ping handshake is completed.
- Send an escalation request by driving `esc_req` pin to 1.
- Wait for `ping_ok` to set and `esc_req_out` to set.
- Check the sequence completes without any signal integrity error.
'''
milestone: V1
tests: ["prim_esc_test"]
}
{
name: prim_esc_tx_integrity_errors_test
desc: '''Verify `esc_tx` signal integrity error.
- Send an escalation request by driving `esc_req` pin to 1.
- Force `esc_n` signal to stay high to trigger an integrity error.
- Verify that prim_esc_sender identifies the error by setting `integ_fail` signal.
- Release the `esc_n` signal.
- Send a ping request and repeat the above sequence and checkings.
'''
milestone: V1
tests: ["prim_esc_test"]
}
{
name: prim_esc_reverse_ping_timeout_test
desc: '''Verify prim_esc_receiver detects ping timeout.
- Send a ping request by driving `ping_req` pin to 1.
- Wait for ping handshake to finish and `ping_ok` signal is set.
- Verify that `esc_en` output remains 0 and `integ_fail` output remains 0.
- Drive `ping_req` signal back to 0 and wait until ping counter timeout.
- Verify that `prim_esc_receiver` detects the ping timeout by setting `esc_en` output
to 1.
- Reset the DUT to clear `esc_en` output.
'''
milestone: V1
tests: ["prim_esc_test"]
}
{
name: prim_esc_receiver_counter_fail_test
desc: '''Verify prim_esc_receiver detects counter mismatch.
- Send a ping request by driving `ping_req` pin to 1.
- Wait until `ping_ok` output sets to 1, which means the two counters start.
- Force one of the two identical counters to 0.
- Verify that prim_esc_receiver detects the counter mismatch and set `esc_en` signal to
1.
'''
milestone: V1
tests: ["prim_esc_test"]
}
{
name: prim_esc_handshake_with_rand_reset_test
desc: '''Verify alert request from prim_alert_sender.
- Send a ping request by driving `ping_req` pin to 1.
- Randomly issue reset during the escalation handshake.
- Verify that after reset, the prim_esc_sender and prim_esc_receiver pair functions
correctly by issuing the tests above.
'''
milestone: V1
tests: ["prim_esc_test"]
}
]
}

View file

@ -0,0 +1,31 @@
CAPI=2:
# Copyright lowRISC contributors.
# Licensed under the Apache License, Version 2.0, see LICENSE for details.
# SPDX-License-Identifier: Apache-2.0
name: "lowrisc:dv:prim_esc_sim:0.1"
description: "ESCALATOR DV sim target"
filesets:
files_rtl:
depend:
- lowrisc:prim:esc
file_type: systemVerilogSource
files_dv:
depend:
- lowrisc:dv:dv_utils
- lowrisc:dv:dv_test_status
- lowrisc:dv:common_ifs
files:
- tb/prim_esc_tb.sv
file_type: systemVerilogSource
targets:
sim: &sim_target
toplevel: prim_esc_tb
filesets:
- files_rtl
- files_dv
default_tool: vcs
lint:
<<: *sim_target

View file

@ -0,0 +1,51 @@
// Copyright lowRISC contributors.
// Licensed under the Apache License, Version 2.0, see LICENSE for details.
// SPDX-License-Identifier: Apache-2.0
{
// Name of the sim cfg - typically same as the name of the DUT.
name: prim_esc
// Top level dut name (sv module).
dut: prim_esc
// Top level testbench name (sv module).
tb: prim_esc_tb
// Simulator used to sign off this block
tool: vcs
// Fusesoc core file used for building the file list.
fusesoc_core: lowrisc:dv:prim_esc_sim:0.1
// Testplan hjson file.
testplan: "{proj_root}/hw/ip/prim/dv/prim_esc/data/prim_esc_testplan.hjson"
// Import additional common sim cfg files.
import_cfgs: ["{proj_root}/hw/dv/tools/dvsim/common_sim_cfg.hjson"]
// Default iterations for all tests - each test entry can override this.
reseed: 20
// List of test specifications.
tests: [
{
name: prim_esc_test
run_opts: ["+test_timeout_ns=1_000"]
}
]
// List of regressions.
regressions: [
{
name: smoke
tests: ["prim_esc_test"]
}
]
overrides: [
{
name: vcs_cov_cfg_file
value: "-cm_hier {proj_root}/hw/ip/prim/dv/prim_esc/data/prim_esc_cover.cfg"
}
]
}

View file

@ -0,0 +1,197 @@
// Copyright lowRISC contributors.
// Licensed under the Apache License, Version 2.0, see LICENSE for details.
// SPDX-License-Identifier: Apache-2.0
//
// Testbench module for prim_esc_sender and prim_esc_receiver_pair.
//
// This test has five sequnces:
// 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.
// 5). Escalation reverse ping timeout sequence.
// 6). Escalation receiver counter fail sequence.
module prim_esc_tb;
//////////////////////////////////////////////////////
// config
//////////////////////////////////////////////////////
localparam time ClkPeriod = 10000;
localparam int PING_CNT_DW = 1;
localparam int TIMEOUT_CYCLES = 1 << (PING_CNT_DW + 6);
//////////////////////////////////////////////////////
// Clock and Reset
//////////////////////////////////////////////////////
wire clk, rst_n;
clk_rst_if main_clk (
.clk,
.rst_n
);
//////////////////////////////////////////////////////
// DUTs
//////////////////////////////////////////////////////
logic ping_req, ping_ok, integ_fail, esc_req, esc_req_out;
prim_esc_pkg::esc_tx_t esc_tx;
prim_esc_pkg::esc_rx_t esc_rx;
prim_esc_sender i_esc_sender (
.clk_i(clk),
.rst_ni(rst_n),
.ping_req_i(ping_req),
.ping_ok_o(ping_ok),
.integ_fail_o(integ_fail),
.esc_req_i(esc_req),
.esc_rx_i(esc_rx),
.esc_tx_o(esc_tx)
);
prim_esc_receiver #(
.N_ESC_SEV(4),
// Set to 1 to avoid long wait period to check ping request reverse timeout.
.PING_CNT_DW(PING_CNT_DW)
) i_esc_receiver (
.clk_i(clk),
.rst_ni(rst_n),
.esc_req_o(esc_req_out),
.esc_rx_o(esc_rx),
.esc_tx_i(esc_tx)
);
//////////////////////////////////////////////////////
// Helper Functions/Tasks and Variables
//////////////////////////////////////////////////////
logic error = 0;
function automatic void test_error(string msg);
$error($sformatf("%time: %0s", $realtime, msg));
error = 1;
endfunction
//////////////////////////////////////////////////////
// Stimuli Application / Response Checking
//////////////////////////////////////////////////////
initial begin: p_stimuli
ping_req = 0;
esc_req = 0;
main_clk.set_period_ps(ClkPeriod);
main_clk.set_active();
main_clk.apply_reset();
// 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);
main_clk.apply_reset();
ping_req = 0;
$display("Random reset during escalation handshake sequence finished!");
// Sequence 2. Escalation request sequence.
esc_req = 1;
// Drive random length of esc_req and check `esc_req_out` and `integ_fail` outputs.
main_clk.wait_clks($urandom_range(1, 20));
if (integ_fail) test_error("Esc_req unexpected signal integrity error!");
if (!esc_req_out) test_error("Esc_req did not set esc_req!");
esc_req = 0;
$display("Escalation request sequence finished!");
// Sequence 3. Ping request interrupted by escalation request.
main_clk.wait_clks($urandom_range(1, 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));
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!");
// Sequence 4.1 `Esc_tx` 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);
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("Escalation esc_p/n integrity sequence during escalation request finished!");
// 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);
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!");
if (ping_ok) test_error("Ping error!");
ping_req = 0;
$display("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.
main_clk.wait_clks($urandom_range(2, 20));
ping_req = 1;
fork
begin
// After one ping_req, esc_receiver will start a counter to expect next ping_req. If the
// counter reaches its max value but no ping_req has been received, design will set
// `esc_req_out` signal.
main_clk.wait_clks(TIMEOUT_CYCLES + 1);
if (!esc_req_out) test_error("Design failed to detect ping request timeout!");
end
begin
// Wait for a ping handshake to complete.
wait (ping_ok);
main_clk.wait_clks(2);
ping_req = 0;
if (integ_fail) test_error("Ping_req unexpected signal integrity error!");
if (esc_req_out) test_error("Ping request should not set esc_req_out!");
end
join
main_clk.apply_reset();
$display("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);
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 (integ_fail) test_error("Escalation receiver counter unexpected signal integrity error!");
release prim_esc_tb.i_esc_receiver.cnt_q[1];
$display("Escalation couter error sequence finished!");
dv_test_status_pkg::dv_test_status(.passed(!error));
$finish();
end
endmodule

View file

@ -78,12 +78,10 @@ static uint64_t prince_k0_to_k0_prime(const uint64_t k0) {
}
static uint64_t prince_round_constant(const unsigned int round) {
uint64_t rc[] = {UINT64_C(0x0000000000000000), UINT64_C(0x13198a2e03707344),
UINT64_C(0xa4093822299f31d0), UINT64_C(0x082efa98ec4e6c89),
UINT64_C(0x452821e638d01377), UINT64_C(0xbe5466cf34e90c6c),
UINT64_C(0x7ef84f78fd955cb1), UINT64_C(0x85840851f1ac43aa),
UINT64_C(0xc882d32f25323c54), UINT64_C(0x64a51195e0e3610d),
UINT64_C(0xd3b5a399ca0c2399), UINT64_C(0xc0ac29b7c97c50dd)};
uint64_t rc[] = {0x0000000000000000, 0x13198a2e03707344, 0xa4093822299f31d0,
0x082efa98ec4e6c89, 0x452821e638d01377, 0xbe5466cf34e90c6c,
0x7ef84f78fd955cb1, 0x85840851f1ac43aa, 0xc882d32f25323c54,
0x64a51195e0e3610d, 0xd3b5a399ca0c2399, 0xc0ac29b7c97c50dd};
return rc[round];
}
@ -149,7 +147,7 @@ static uint64_t gf2_mat_mult16_1(const uint64_t in, const uint64_t mat[16]) {
/**
* Build Prince's 16 bit matrices M0 and M1.
*/
static void prince_m16_matrices(uint64_t m16[2][16]) {
static inline void prince_m16_matrices(uint64_t m16[2][16]) {
// 4 bits matrices m0 to m3 are stored in array m4
const uint64_t m4[4][4] = {// m0
{0x0, 0x2, 0x4, 0x8},
@ -198,7 +196,7 @@ static uint64_t prince_m_prime_layer(const uint64_t m_prime_in) {
* The shift row and inverse shift row of the Prince cipher.
*/
static uint64_t prince_shift_rows(const uint64_t in, int inverse) {
const uint64_t row_mask = UINT64_C(0xF000F000F000F000);
const uint64_t row_mask = 0xF000F000F000F000;
uint64_t shift_rows_out = 0;
for (unsigned int i = 0; i < 4; i++) {
const uint64_t row = in & (row_mask >> (4 * i));
@ -278,7 +276,7 @@ static uint64_t prince_core(const uint64_t core_input, const uint64_t k0_new,
uint64_t prince_enc_dec_uint64(const uint64_t input, const uint64_t enc_k0,
const uint64_t enc_k1, int decrypt,
int num_half_rounds, int old_key_schedule) {
const uint64_t prince_alpha = UINT64_C(0xc0ac29b7c97c50dd);
const uint64_t prince_alpha = 0xc0ac29b7c97c50dd;
const uint64_t k1 = enc_k1 ^ (decrypt ? prince_alpha : 0);
const uint64_t k0_new =
(old_key_schedule) ? k1 : enc_k0 ^ (decrypt ? prince_alpha : 0);
@ -320,9 +318,10 @@ static void prince_enc_dec(const uint8_t in_bytes[8],
* key_bytes 0 to 7 must contain K0.
* key_bytes 8 to 15 must contain K1.
*/
static void prince_encrypt(const uint8_t in_bytes[8],
const uint8_t key_bytes[16], uint8_t out_bytes[8],
int num_half_rounds, int old_key_schedule) {
static inline void prince_encrypt(const uint8_t in_bytes[8],
const uint8_t key_bytes[16],
uint8_t out_bytes[8], int num_half_rounds,
int old_key_schedule) {
prince_enc_dec(in_bytes, key_bytes, out_bytes, 0, num_half_rounds,
old_key_schedule);
}
@ -333,9 +332,10 @@ static void prince_encrypt(const uint8_t in_bytes[8],
* key_bytes 0 to 7 must contain K0.
* key_bytes 8 to 15 must contain K1.
*/
static void prince_decrypt(const uint8_t in_bytes[8],
const uint8_t key_bytes[16], uint8_t out_bytes[8],
int num_half_rounds, int old_key_schedule) {
static inline void prince_decrypt(const uint8_t in_bytes[8],
const uint8_t key_bytes[16],
uint8_t out_bytes[8], int num_half_rounds,
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];

View file

@ -124,7 +124,7 @@ module prim_prince_tb;
// Drive input into encryption instances.
key_in = key;
dec_in = 0;
valid_in = 1;
valid_in = 0;
for (int unsigned i = 0; i < 2; i++) begin
for (int unsigned j = 0; j < 2; j++) begin
for (int unsigned k = 0; k < NumRoundsHalf; k++) begin
@ -132,10 +132,14 @@ module prim_prince_tb;
end
end
end
// wait some time before signaling that the inputs are valid
clk_if.wait_clks($urandom_range(0, 10));
valid_in = 1;
// Wait for the DUTs to finish calculations.
clk_if.wait_clks(2);
wait(&valid_out == 1);
valid_in = 0;
clk_if.wait_clks(1);
// query DPI model for expected encrypted output.
for (int i = 0; i < 2; i++) begin
for (int j = 0; j < 2; j++) begin
@ -162,7 +166,7 @@ module prim_prince_tb;
// Drive input into decryption instances.
key_in = key;
dec_in = 1;
valid_in = 1;
valid_in = 0;
for (int unsigned i = 0; i < 2; i++) begin
for (int unsigned j = 0; j < 2; j++) begin
for (int unsigned k = 0; k < NumRoundsHalf; k++) begin
@ -170,6 +174,9 @@ module prim_prince_tb;
end
end
end
// wait some time before signaling that the inputs are valid
clk_if.wait_clks($urandom_range(0, 10));
valid_in = 1;
// Wait for the DUTs to finish calculations.
clk_if.wait_clks(2);
wait(&valid_out == 1);
@ -243,11 +250,16 @@ module prim_prince_tb;
bit [KeyWidth/2-1:0] k0, k1;
bit [DataWidth-1:0] plaintext;
$timeformat(-12, 0, " ps", 12);
clk_if.set_period_ps(ClkPeriod);
clk_if.set_active();
// Toggle reset twice at start of the test to hit a small toggle coverage point:
// - rst_ni: 1 -> 0
// No additional functional impact
clk_if.apply_reset();
$timeformat(-12, 0, " ps", 12);
clk_if.wait_clks(10);
clk_if.wait_clks(5);
clk_if.apply_reset();
clk_if.wait_clks(5);
/////////////////////////////
// Test the 5 golden vectors.

View file

@ -19,7 +19,7 @@ targets:
default: &default_target
# note, this setting is just used
# to generate a file list for jg
formal: icarus
default_tool: icarus
filesets:
- files_formal
toplevel:

View file

@ -0,0 +1,27 @@
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:fpv:prim_fifo_async_sram_adapter_fpv:0.1"
description: "prim_fifo_async_sram_adapter FPV target"
filesets:
files_formal:
depend:
- lowrisc:prim:all
- lowrisc:prim:ram_2p_async_adv
files:
- tb/prim_fifo_async_sram_adapter_fpv.sv
file_type: systemVerilogSource
targets:
default: &default_target
default_tool: icarus
filesets:
- files_formal
toplevel: prim_fifo_async_sram_adapter_fpv
formal:
<<: *default_target
lint:
<<: *default_target

View file

@ -10,7 +10,6 @@ filesets:
depend:
- lowrisc:prim:all
files:
- ../rtl/prim_packer.sv
- tb/prim_packer_fpv.sv
file_type: systemVerilogSource

View file

@ -19,6 +19,7 @@ module prim_alert_rxtx_async_bind_fpv;
.alert_err_ni,
.alert_skew_i,
.alert_test_i,
.init_trig_i,
.alert_req_i,
.alert_ack_o,
.alert_state_o,

View file

@ -19,6 +19,7 @@ module prim_alert_rxtx_async_fatal_bind_fpv;
.alert_err_ni,
.alert_skew_i,
.alert_test_i,
.init_trig_i,
.alert_req_i,
.alert_ack_o,
.alert_state_o,

View file

@ -23,6 +23,7 @@ module prim_alert_rxtx_async_fatal_fpv
// normal I/Os
input alert_test_i,
input alert_req_i,
input lc_ctrl_pkg::lc_tx_t init_trig_i,
input ping_req_i,
output logic alert_ack_o,
output logic alert_state_o,
@ -72,8 +73,8 @@ module prim_alert_rxtx_async_fatal_fpv
.AsyncOn ( AsyncOn ),
.IsFatal ( IsFatal )
) i_prim_alert_sender (
.clk_i ,
.rst_ni ,
.clk_i,
.rst_ni,
.alert_test_i,
.alert_req_i,
.alert_ack_o,
@ -85,12 +86,13 @@ module prim_alert_rxtx_async_fatal_fpv
prim_alert_receiver #(
.AsyncOn ( AsyncOn )
) i_prim_alert_receiver (
.clk_i ,
.rst_ni ,
.ping_req_i ,
.ping_ok_o ,
.integ_fail_o ,
.alert_o ,
.clk_i,
.rst_ni,
.init_trig_i,
.ping_req_i,
.ping_ok_o,
.integ_fail_o,
.alert_o,
.alert_rx_o ( alert_rx_out ),
.alert_tx_i ( alert_tx_in )
);

View file

@ -23,6 +23,7 @@ module prim_alert_rxtx_async_fpv
// normal I/Os
input alert_test_i,
input alert_req_i,
input lc_ctrl_pkg::lc_tx_t init_trig_i,
input ping_req_i,
output logic alert_ack_o,
output logic alert_state_o,
@ -33,6 +34,7 @@ module prim_alert_rxtx_async_fpv
// asynchronous case
localparam bit AsyncOn = 1'b1;
localparam bit IsFatal = 1'b0;
logic ping_pd;
logic ping_nd;
@ -68,10 +70,11 @@ module prim_alert_rxtx_async_fpv
assign alert_tx_in.alert_n = alert_nq[alert_skew_i[1]] ^ alert_err_ni;
prim_alert_sender #(
.AsyncOn ( AsyncOn )
.AsyncOn ( AsyncOn ),
.IsFatal ( IsFatal )
) i_prim_alert_sender (
.clk_i ,
.rst_ni ,
.clk_i,
.rst_ni,
.alert_test_i,
.alert_req_i,
.alert_ack_o,
@ -83,16 +86,18 @@ module prim_alert_rxtx_async_fpv
prim_alert_receiver #(
.AsyncOn ( AsyncOn )
) i_prim_alert_receiver (
.clk_i ,
.rst_ni ,
.ping_req_i ,
.ping_ok_o ,
.integ_fail_o ,
.alert_o ,
.clk_i,
.rst_ni,
.init_trig_i,
.ping_req_i,
.ping_ok_o,
.integ_fail_o,
.alert_o,
.alert_rx_o ( alert_rx_out ),
.alert_tx_i ( alert_tx_in )
);
always_ff @(posedge clk_i or negedge rst_ni) begin : p_skew_delay
if (!rst_ni) begin
ping_pq <= '0;

View file

@ -19,6 +19,7 @@ module prim_alert_rxtx_bind_fpv;
.alert_req_i,
.alert_ack_o,
.alert_state_o,
.init_trig_i,
.ping_req_i,
.ping_ok_o,
.integ_fail_o,

View file

@ -20,6 +20,7 @@ module prim_alert_rxtx_fatal_bind_fpv;
.alert_req_i,
.alert_ack_o,
.alert_state_o,
.init_trig_i,
.ping_req_i,
.ping_ok_o,
.integ_fail_o,

View file

@ -20,6 +20,7 @@ module prim_alert_rxtx_fatal_fpv
// normal I/Os
input alert_test_i,
input alert_req_i,
input lc_ctrl_pkg::lc_tx_t init_trig_i,
input ping_req_i,
output logic alert_ack_o,
output logic alert_state_o,
@ -47,8 +48,8 @@ module prim_alert_rxtx_fatal_fpv
.AsyncOn ( AsyncOn ),
.IsFatal ( IsFatal )
) i_prim_alert_sender (
.clk_i ,
.rst_ni ,
.clk_i,
.rst_ni,
.alert_test_i,
.alert_req_i,
.alert_ack_o,
@ -60,12 +61,13 @@ module prim_alert_rxtx_fatal_fpv
prim_alert_receiver #(
.AsyncOn ( AsyncOn )
) i_prim_alert_receiver (
.clk_i ,
.rst_ni ,
.ping_req_i ,
.ping_ok_o ,
.integ_fail_o ,
.alert_o ,
.clk_i,
.rst_ni,
.init_trig_i,
.ping_req_i,
.ping_ok_o,
.integ_fail_o,
.alert_o,
.alert_rx_o ( alert_rx_out ),
.alert_tx_i ( alert_tx_in )
);

View file

@ -20,6 +20,7 @@ module prim_alert_rxtx_fpv
// normal I/Os
input alert_test_i,
input alert_req_i,
input lc_ctrl_pkg::lc_tx_t init_trig_i,
input ping_req_i,
output logic alert_ack_o,
output logic alert_state_o,
@ -30,6 +31,7 @@ module prim_alert_rxtx_fpv
// synchronous case
localparam bit AsyncOn = 1'b0;
localparam bit IsFatal = 1'b0;
alert_rx_t alert_rx_out, alert_rx_in;
alert_tx_t alert_tx_out, alert_tx_in;
@ -43,10 +45,11 @@ module prim_alert_rxtx_fpv
assign alert_tx_in.alert_n = alert_tx_out.alert_n ^ alert_err_ni;
prim_alert_sender #(
.AsyncOn ( AsyncOn )
.AsyncOn ( AsyncOn ),
.IsFatal ( IsFatal )
) i_prim_alert_sender (
.clk_i ,
.rst_ni ,
.clk_i,
.rst_ni,
.alert_test_i,
.alert_req_i,
.alert_ack_o,
@ -58,12 +61,13 @@ module prim_alert_rxtx_fpv
prim_alert_receiver #(
.AsyncOn ( AsyncOn )
) i_prim_alert_receiver (
.clk_i ,
.rst_ni ,
.ping_req_i ,
.ping_ok_o ,
.integ_fail_o ,
.alert_o ,
.clk_i,
.rst_ni,
.init_trig_i,
.ping_req_i,
.ping_ok_o,
.integ_fail_o,
.alert_o,
.alert_rx_o ( alert_rx_out ),
.alert_tx_i ( alert_tx_in )
);

View file

@ -17,7 +17,7 @@ module prim_esc_rxtx_bind_fpv;
.ping_req_i ,
.ping_ok_o ,
.integ_fail_o,
.esc_en_o
.esc_req_o
);
endmodule : prim_esc_rxtx_bind_fpv

View file

@ -20,7 +20,7 @@ module prim_esc_rxtx_fpv
input ping_req_i,
output logic ping_ok_o,
output logic integ_fail_o,
output logic esc_en_o
output logic esc_req_o
);
esc_rx_t esc_rx_in, esc_rx_out;
@ -49,7 +49,7 @@ module prim_esc_rxtx_fpv
) u_prim_esc_receiver (
.clk_i ,
.rst_ni ,
.esc_en_o ,
.esc_req_o ,
.esc_rx_o ( esc_rx_out ),
.esc_tx_i ( esc_tx_in )
);

View file

@ -0,0 +1,204 @@
// Copyright lowRISC contributors.
// Licensed under the Apache License, Version 2.0, see LICENSE for details.
// SPDX-License-Identifier: Apache-2.0
//
// Testbench module for prim_fifo_sram_async
module prim_fifo_async_sram_adapter_fpv #(
parameter int unsigned Width = 32,
parameter int unsigned Depth = 16,
parameter int unsigned FpgaSram = 0 // Use FF based
) (
input clk_wr_i,
input clk_rd_i,
input rst_ni,
input wvalid_i,
output logic wready_o,
input [Width-1:0] wdata_i,
output logic rvalid_o,
input rready_i,
output logic [Width-1:0] rdata_o
);
localparam int unsigned SramAw = 7;
localparam int unsigned SramDw = 32;
localparam logic [SramAw-1:0] SramBaseAddr = 'h 30;
logic w_sram_req;
logic w_sram_gnt;
logic w_sram_write;
logic [SramAw-1:0] w_sram_addr;
logic [SramDw-1:0] w_sram_wdata;
logic [SramDw-1:0] w_sram_wmask;
logic w_sram_rvalid; // not used
logic [SramDw-1:0] w_sram_rdata; // not used
logic [1:0] w_sram_rerror; // not used
logic r_sram_req;
logic r_sram_gnt;
logic r_sram_write;
logic [SramAw-1:0] r_sram_addr;
logic [SramDw-1:0] r_sram_wdata; // not used
logic [SramDw-1:0] r_sram_wmask; // not used
logic r_sram_rvalid;
logic [SramDw-1:0] r_sram_rdata;
logic [1:0] r_sram_rerror;
prim_fifo_async_sram_adapter #(
.Width (Width),
.Depth (Depth),
.SramAw (SramAw), // TODO: random with constraint
.SramDw (SramDw), // TODO: random with constraint
.SramBaseAddr(SramBaseAddr) // TODO: random?
) dut (
.clk_wr_i,
.rst_wr_ni (rst_ni),
.clk_rd_i,
.rst_rd_ni (rst_ni),
.wvalid_i,
.wready_o,
.wdata_i,
.wdepth_o (),
.rvalid_o,
.rready_i,
.rdata_o,
.rdepth_o (),
// Sram Interface
.w_sram_req_o (w_sram_req ),
.w_sram_gnt_i (w_sram_gnt ),
.w_sram_write_o (w_sram_write ),
.w_sram_addr_o (w_sram_addr ),
.w_sram_wdata_o (w_sram_wdata ),
.w_sram_wmask_o (w_sram_wmask ),
.w_sram_rvalid_i(w_sram_rvalid), // not used
.w_sram_rdata_i (w_sram_rdata ), // not used
.w_sram_rerror_i(w_sram_rerror), // not used
// Read SRAM port
.r_sram_req_o (r_sram_req ),
.r_sram_gnt_i (r_sram_gnt ),
.r_sram_write_o (r_sram_write ),
.r_sram_addr_o (r_sram_addr ),
.r_sram_wdata_o (r_sram_wdata ), // not used
.r_sram_wmask_o (r_sram_wmask ), // not used
.r_sram_rvalid_i(r_sram_rvalid),
.r_sram_rdata_i (r_sram_rdata ),
.r_sram_rerror_i(r_sram_rerror),
.r_full_o(),
.r_notempty_o(),
.w_full_o()
);
if (FpgaSram == 1) begin : g_sram_fpga
// FPV looks like does not like the style of ram_2p.
prim_ram_2p #(
.Depth (2**SramAw),
.Width (SramDw),
.DataBitsPerMask (1)
) u_sram (
.clk_a_i (clk_wr_i),
.clk_b_i (clk_rd_i),
.a_req_i (w_sram_req ),
.a_write_i (w_sram_write ),
.a_addr_i (w_sram_addr ),
.a_wdata_i (w_sram_wdata ),
.a_wmask_i (w_sram_wmask ),
.a_rdata_o (w_sram_rdata ),
.b_req_i (r_sram_req ),
.b_write_i (r_sram_write ),
.b_addr_i (r_sram_addr ),
.b_wdata_i (r_sram_wdata ),
.b_wmask_i (r_sram_wmask ),
.b_rdata_o (r_sram_rdata ),
.cfg_i ('0)
);
end else begin : g_sram_ff
logic [SramDw-1:0] mem [2**SramAw];
always_ff @(posedge clk_wr_i) begin
if (w_sram_req && w_sram_write) begin
for (int unsigned i = 0; i < SramDw ; i++) begin
if (w_sram_wmask[i]) mem[w_sram_addr][i] <= w_sram_wdata[i];
end // for
end // if
end
always_ff @(posedge clk_rd_i) begin
if (r_sram_req && !r_sram_write) begin
r_sram_rdata <= mem[r_sram_addr];
end
end
end // !FpgaSram
assign w_sram_gnt = w_sram_req;
always_ff @(posedge clk_wr_i) begin
w_sram_rvalid <= w_sram_req && !w_sram_write;
end
assign w_sram_rerror = '0;
assign r_sram_gnt = r_sram_req;
always_ff @(posedge clk_rd_i) begin
r_sram_rvalid <= r_sram_req && !r_sram_write;
end
assign r_sram_rerror = '0;
`ASSUME_FPV(WdataBins_M,
wvalid_i |-> wdata_i inside {
32'h DEAD_BEEF, 32'h5A5A_A5A5, 32'h 1234_5678
},
clk_wr_i, !rst_ni)
`ifdef FPV_ON
logic [Width-1:0] storage [Depth];
logic [Width-1:0] rdata;
logic [$clog2(Depth)-1:0] wptr, rptr;
logic wack, rack;
assign wack = wvalid_i && wready_o;
assign rack = rvalid_o && rready_i;
always_ff @(posedge clk_wr_i or negedge rst_ni) begin
if (!rst_ni) wptr <= '0;
else if (wack) wptr <= wptr+1;
end
always_ff @(posedge clk_rd_i or negedge rst_ni) begin
if (!rst_ni) rptr <= '0;
else if (rack) rptr <= rptr+1;
end
always_ff @(posedge clk_wr_i or negedge rst_ni) begin
if (!rst_ni) storage <= '{default:'0};
else if (wack) begin
storage[wptr] <= wdata_i;
end
end
assign rdata = storage[rptr];
`ASSERT(DataIntegrityCheck_A,
rack |-> rdata_o == rdata,
clk_rd_i, !rst_ni)
`endif // FPV_ON
endmodule : prim_fifo_async_sram_adapter_fpv

View file

@ -20,8 +20,8 @@ module prim_lfsr_fpv #(
localparam int unsigned GalMaxGtFibMax = GalXorMaxLfsrDw > FibXnorMaxLfsrDw,
localparam int unsigned MaxLfsrDw = GalXorMaxLfsrDw * GalMaxGtFibMax +
FibXnorMaxLfsrDw * (1 - GalMaxGtFibMax),
localparam int unsigned NumDuts = FibXnorMaxLfsrDw - FibXnorMinLfsrDw + 1 +
GalXorMaxLfsrDw - GalXorMinLfsrDw + 1
localparam int unsigned NumDuts = 2*(FibXnorMaxLfsrDw - FibXnorMinLfsrDw + 1) +
2*(GalXorMaxLfsrDw - GalXorMinLfsrDw + 1)
) (
input clk_i,
input rst_ni,
@ -33,7 +33,7 @@ module prim_lfsr_fpv #(
);
for (genvar k = GalXorMinLfsrDw; k <= GalXorMaxLfsrDw; k++) begin : gen_gal_xor_duts
localparam int unsigned idx = k - GalXorMinLfsrDw;
localparam int unsigned Idx = k - GalXorMinLfsrDw;
prim_lfsr #(.LfsrType("GAL_XOR"),
.LfsrDw ( k ),
.EntropyDw ( EntropyDw ),
@ -41,19 +41,43 @@ module prim_lfsr_fpv #(
.DefaultSeed ( 1 ),
// disabled for large LFSRs since this becomes prohibitive in runtime
.MaxLenSVA ( k <= MaxLenSVAThresh )
) i_prim_lfsr (
) u_prim_lfsr (
.clk_i,
.rst_ni,
.seed_en_i ( load_ext_en_i[idx] ),
.seed_i ( k'(seed_ext_i[idx]) ),
.lfsr_en_i ( lfsr_en_i[idx] ),
.entropy_i ( entropy_i[idx] ),
.state_o ( state_o[idx] )
.seed_en_i ( load_ext_en_i[Idx] ),
.seed_i ( k'(seed_ext_i[Idx]) ),
.lfsr_en_i ( lfsr_en_i[Idx] ),
.entropy_i ( entropy_i[Idx] ),
.state_o ( state_o[Idx] )
);
end
// same as above but with non-linear output enabled.
// only power-of-two widths are allowed.
for (genvar k = 16; k <= GalXorMaxLfsrDw; k = k*2)
begin : gen_gal_xor_duts_nonlinear
localparam int unsigned Idx = k - GalXorMinLfsrDw;
prim_lfsr #(.LfsrType("GAL_XOR"),
.LfsrDw ( k ),
.EntropyDw ( EntropyDw ),
.StateOutDw ( StateOutDw ),
.DefaultSeed ( 1 ),
// disabled for large LFSRs since this becomes prohibitive in runtime
.MaxLenSVA ( k <= MaxLenSVAThresh ),
.NonLinearOut (1)
) u_prim_lfsr (
.clk_i,
.rst_ni,
.seed_en_i ( load_ext_en_i[Idx] ),
.seed_i ( k'(seed_ext_i[Idx]) ),
.lfsr_en_i ( lfsr_en_i[Idx] ),
.entropy_i ( entropy_i[Idx] ),
.state_o ( state_o[Idx] )
);
end
for (genvar k = FibXnorMinLfsrDw; k <= FibXnorMaxLfsrDw; k++) begin : gen_fib_xnor_duts
localparam int unsigned idx = k - FibXnorMinLfsrDw + GalXorMaxLfsrDw - GalXorMinLfsrDw + 1;
localparam int unsigned Idx = k - FibXnorMinLfsrDw + GalXorMaxLfsrDw - GalXorMinLfsrDw + 1;
prim_lfsr #(.LfsrType("FIB_XNOR"),
.LfsrDw ( k ),
.EntropyDw ( EntropyDw ),
@ -61,14 +85,38 @@ module prim_lfsr_fpv #(
.DefaultSeed ( 1 ),
// disabled for large LFSRs since this becomes prohibitive in runtime
.MaxLenSVA ( k <= MaxLenSVAThresh )
) i_prim_lfsr (
) u_prim_lfsr (
.clk_i,
.rst_ni,
.seed_en_i ( load_ext_en_i[idx] ),
.seed_i ( k'(seed_ext_i[idx]) ),
.lfsr_en_i ( lfsr_en_i[idx] ),
.entropy_i ( entropy_i[idx] ),
.state_o ( state_o[idx] )
.seed_en_i ( load_ext_en_i[Idx] ),
.seed_i ( k'(seed_ext_i[Idx]) ),
.lfsr_en_i ( lfsr_en_i[Idx] ),
.entropy_i ( entropy_i[Idx] ),
.state_o ( state_o[Idx] )
);
end
// same as above but with non-linear output enabled.
// only power-of-two widths are allowed.
for (genvar k = 16; k <= FibXnorMaxLfsrDw; k = k*2)
begin : gen_fib_xnor_duts_nonlinear
localparam int unsigned Idx = k - FibXnorMinLfsrDw + GalXorMaxLfsrDw - GalXorMinLfsrDw + 1;
prim_lfsr #(.LfsrType("FIB_XNOR"),
.LfsrDw ( k ),
.EntropyDw ( EntropyDw ),
.StateOutDw ( StateOutDw ),
.DefaultSeed ( 1 ),
// disabled for large LFSRs since this becomes prohibitive in runtime
.MaxLenSVA ( k <= MaxLenSVAThresh ),
.NonLinearOut (1)
) u_prim_lfsr (
.clk_i,
.rst_ni,
.seed_en_i ( load_ext_en_i[Idx] ),
.seed_i ( k'(seed_ext_i[Idx]) ),
.lfsr_en_i ( lfsr_en_i[Idx] ),
.entropy_i ( entropy_i[Idx] ),
.state_o ( state_o[Idx] )
);
end

View file

@ -22,6 +22,7 @@ module prim_alert_rxtx_assert_fpv (
input alert_req_i,
input alert_ack_o,
input alert_state_o,
input lc_ctrl_pkg::lc_tx_t init_trig_i,
input ping_req_i,
input ping_ok_o,
input integ_fail_o,
@ -33,6 +34,12 @@ module prim_alert_rxtx_assert_fpv (
ack_err_pi | ack_err_ni |
alert_err_pi | alert_err_ni;
logic init_pending;
assign init_pending = init_trig_i == lc_ctrl_pkg::On ||
prim_alert_rxtx_fpv.i_prim_alert_receiver.state_q inside {
prim_alert_rxtx_fpv.i_prim_alert_receiver.InitReq,
prim_alert_rxtx_fpv.i_prim_alert_receiver.InitAckWait};
// note: we can only detect sigint errors where one wire is flipped.
`ASSUME_FPV(PingErrorsAreOH_M, $onehot0({ping_err_pi, ping_err_ni}), clk_i, !rst_ni)
`ASSUME_FPV(AckErrorsAreOH_M, $onehot0({ack_err_pi, ack_err_ni}), clk_i, !rst_ni)
@ -55,64 +62,81 @@ module prim_alert_rxtx_assert_fpv (
endsequence
// note: injected errors may lockup the FSMs, and hence the full HS can
// only take place if both FSMs are in a sane state
// only take place if both FSMs are in a good state
`ASSERT(PingHs_A, ##1 $changed(prim_alert_rxtx_fpv.alert_rx_out.ping_p) &&
(prim_alert_rxtx_fpv.i_prim_alert_sender.state_q ==
prim_alert_rxtx_fpv.i_prim_alert_sender.Idle) &&
(prim_alert_rxtx_fpv.i_prim_alert_receiver.state_q ==
prim_alert_rxtx_fpv.i_prim_alert_receiver.Idle) |=> FullHandshake_S,
clk_i, !rst_ni || error_present)
clk_i, !rst_ni || error_present || init_trig_i == lc_ctrl_pkg::On)
`ASSERT(AlertHs_A, alert_req_i &&
(prim_alert_rxtx_fpv.i_prim_alert_sender.state_q ==
prim_alert_rxtx_fpv.i_prim_alert_sender.Idle) &&
(prim_alert_rxtx_fpv.i_prim_alert_receiver.state_q ==
prim_alert_rxtx_fpv.i_prim_alert_receiver.Idle) |=>
FullHandshake_S |-> alert_ack_o, clk_i, !rst_ni || error_present)
FullHandshake_S |-> alert_ack_o,
clk_i, !rst_ni || error_present || init_trig_i == lc_ctrl_pkg::On)
`ASSERT(AlertTestHs_A, alert_test_i &&
(prim_alert_rxtx_fpv.i_prim_alert_sender.state_q ==
prim_alert_rxtx_fpv.i_prim_alert_sender.Idle) &&
(prim_alert_rxtx_fpv.i_prim_alert_receiver.state_q ==
prim_alert_rxtx_fpv.i_prim_alert_receiver.Idle) |=>
FullHandshake_S, clk_i, !rst_ni || error_present)
FullHandshake_S,
clk_i, !rst_ni || error_present || init_trig_i == lc_ctrl_pkg::On)
// Make sure we eventually get an ACK
`ASSERT(AlertReqAck_A, alert_req_i &&
(prim_alert_rxtx_fpv.i_prim_alert_sender.state_q ==
prim_alert_rxtx_fpv.i_prim_alert_sender.Idle) &&
(prim_alert_rxtx_fpv.i_prim_alert_receiver.state_q ==
prim_alert_rxtx_fpv.i_prim_alert_receiver.Idle) |-> strong(##[1:$] alert_ack_o),
clk_i, !rst_ni || error_present)
clk_i, !rst_ni || error_present || init_trig_i == lc_ctrl_pkg::On)
// transmission of pings
// note: the complete transmission of pings only happen when no ping handshake is in progress
`ASSERT(AlertPingOk_A, !(prim_alert_rxtx_fpv.i_prim_alert_sender.state_q inside {
prim_alert_rxtx_fpv.i_prim_alert_sender.PingHsPhase1,
prim_alert_rxtx_fpv.i_prim_alert_sender.PingHsPhase2}) && $rose(ping_req_i) |->
##[1:9] ping_ok_o, clk_i, !rst_ni || error_present)
##[1:9] ping_ok_o,
clk_i, !rst_ni || error_present || init_pending)
`ASSERT(AlertPingIgnored_A, (prim_alert_rxtx_fpv.i_prim_alert_sender.state_q inside {
prim_alert_rxtx_fpv.i_prim_alert_sender.PingHsPhase1,
prim_alert_rxtx_fpv.i_prim_alert_sender.PingHsPhase2}) && $rose(ping_req_i) |->
ping_ok_o == 0 throughout ping_req_i [->1], clk_i, !rst_ni || error_present)
ping_ok_o == 0 throughout ping_req_i [->1],
clk_i, !rst_ni || error_present || init_trig_i == lc_ctrl_pkg::On)
// transmission of alerts in case of no collision with ping enable
`ASSERT(AlertCheck0_A, !ping_req_i [*3] ##0 ($rose(alert_req_i) || $rose(alert_test_i)) &&
(prim_alert_rxtx_fpv.i_prim_alert_sender.state_q ==
prim_alert_rxtx_fpv.i_prim_alert_sender.Idle) |=>
alert_o, clk_i, !rst_ni || error_present || ping_req_i)
alert_o,
clk_i, !rst_ni || error_present || ping_req_i || init_pending)
// transmission of alerts in the general case which can include continous ping collisions
`ASSERT(AlertCheck1_A, alert_req_i || alert_test_i |=>
strong(##[1:$] ((prim_alert_rxtx_fpv.i_prim_alert_sender.state_q ==
prim_alert_rxtx_fpv.i_prim_alert_sender.Idle) && !ping_req_i) ##1 alert_o),
clk_i, !rst_ni || error_present || prim_alert_rxtx_fpv.i_prim_alert_sender.alert_clr)
clk_i,
!rst_ni || error_present || prim_alert_rxtx_fpv.i_prim_alert_sender.alert_clr ||
init_trig_i == lc_ctrl_pkg::On)
// basic liveness of FSMs in case no errors are present
`ASSERT(FsmLivenessSender_A,
(prim_alert_rxtx_fpv.i_prim_alert_sender.state_q !=
prim_alert_rxtx_fpv.i_prim_alert_sender.Idle) |->
strong(##[1:$] (prim_alert_rxtx_fpv.i_prim_alert_sender.state_q ==
prim_alert_rxtx_fpv.i_prim_alert_sender.Idle)), clk_i, !rst_ni || error_present)
prim_alert_rxtx_fpv.i_prim_alert_sender.Idle)),
clk_i, !rst_ni || error_present || init_trig_i == lc_ctrl_pkg::On)
`ASSERT(FsmLivenessReceiver_A,
(prim_alert_rxtx_fpv.i_prim_alert_receiver.state_q !=
prim_alert_rxtx_fpv.i_prim_alert_receiver.Idle) |->
strong(##[1:$] (prim_alert_rxtx_fpv.i_prim_alert_receiver.state_q ==
prim_alert_rxtx_fpv.i_prim_alert_receiver.Idle)),clk_i, !rst_ni || error_present)
prim_alert_rxtx_fpv.i_prim_alert_receiver.Idle)),
clk_i, !rst_ni || error_present || init_trig_i == lc_ctrl_pkg::On)
// check that the in-band reset moves sender FSM into Idle state.
`ASSERT(InBandInitFromReceiverToSender_A,
init_trig_i == lc_ctrl_pkg::On
|->
##[1:20] (prim_alert_rxtx_fpv.i_prim_alert_sender.state_q ==
prim_alert_rxtx_fpv.i_prim_alert_sender.Idle),
clk_i, !rst_ni || error_present)
endmodule : prim_alert_rxtx_assert_fpv

View file

@ -22,6 +22,7 @@ module prim_alert_rxtx_async_assert_fpv (
input [1:0] alert_skew_i,
// normal I/Os
input alert_test_i,
input lc_ctrl_pkg::lc_tx_t init_trig_i,
input alert_req_i,
input alert_ack_o,
input alert_state_o,
@ -36,6 +37,12 @@ module prim_alert_rxtx_async_assert_fpv (
ack_err_pi | ack_err_ni |
alert_err_pi | alert_err_ni;
logic init_pending;
assign init_pending = init_trig_i == lc_ctrl_pkg::On ||
prim_alert_rxtx_async_fpv.i_prim_alert_receiver.state_q inside {
prim_alert_rxtx_async_fpv.i_prim_alert_receiver.InitReq,
prim_alert_rxtx_async_fpv.i_prim_alert_receiver.InitAckWait};
// used to check that an error has never occured so far
// this is used to check the handshake below. the handshake can lock up
// the protocol FSMs causing the handshake to never complete.
@ -59,10 +66,9 @@ module prim_alert_rxtx_async_assert_fpv (
// ping will stay high until ping ok received, then it must be deasserted
// TODO: this excludes the case where no ping ok will be returned due to an error
`ASSUME_FPV(PingDeassert_M, ping_req_i && ping_ok_o |=> !ping_req_i, clk_i, !rst_ni)
`ASSUME_FPV(PingDeassert_M, ping_req_i && ping_ok_o |=> !ping_req_i)
`ASSUME_FPV(PingEn_M, $rose(ping_req_i) |-> ping_req_i throughout
(ping_ok_o || error_present)[->1] ##1 $fell(ping_req_i),
clk_i, !rst_ni)
(ping_ok_o || error_present)[->1] ##1 $fell(ping_req_i))
// Note: the sequence lengths of the handshake and the following properties needs to
// be parameterized accordingly if different clock ratios are to be used here.
@ -78,32 +84,32 @@ module prim_alert_rxtx_async_assert_fpv (
endsequence
// note: injected errors may lockup the FSMs, and hence the full HS can
// only take place if both FSMs are in a sane state
// only take place if both FSMs are in a good state
`ASSERT(PingHs_A, ##1 $changed(prim_alert_rxtx_async_fpv.ping_pd) &&
(prim_alert_rxtx_async_fpv.i_prim_alert_sender.state_q ==
prim_alert_rxtx_async_fpv.i_prim_alert_sender.Idle) &&
(prim_alert_rxtx_async_fpv.i_prim_alert_receiver.state_q ==
prim_alert_rxtx_async_fpv.i_prim_alert_receiver.Idle) |-> ##[0:5] FullHandshake_S,
clk_i, !rst_ni || error_setreg_q)
clk_i, !rst_ni || error_setreg_q || init_pending)
`ASSERT(AlertHs_A, alert_req_i &&
(prim_alert_rxtx_async_fpv.i_prim_alert_sender.state_q ==
prim_alert_rxtx_async_fpv.i_prim_alert_sender.Idle) &&
(prim_alert_rxtx_async_fpv.i_prim_alert_receiver.state_q ==
prim_alert_rxtx_async_fpv.i_prim_alert_receiver.Idle) |-> ##[0:5] FullHandshake_S,
clk_i, !rst_ni || error_setreg_q)
clk_i, !rst_ni || error_setreg_q || init_pending)
`ASSERT(AlertTestHs_A, alert_test_i &&
(prim_alert_rxtx_async_fpv.i_prim_alert_sender.state_q ==
prim_alert_rxtx_async_fpv.i_prim_alert_sender.Idle) &&
(prim_alert_rxtx_async_fpv.i_prim_alert_receiver.state_q ==
prim_alert_rxtx_async_fpv.i_prim_alert_receiver.Idle) |-> ##[0:5] FullHandshake_S,
clk_i, !rst_ni || error_setreg_q)
clk_i, !rst_ni || error_setreg_q || init_pending)
// Make sure we eventually get an ACK
`ASSERT(AlertReqAck_A, alert_req_i &&
(prim_alert_rxtx_async_fpv.i_prim_alert_sender.state_q ==
prim_alert_rxtx_async_fpv.i_prim_alert_sender.Idle) &&
(prim_alert_rxtx_async_fpv.i_prim_alert_receiver.state_q ==
prim_alert_rxtx_async_fpv.i_prim_alert_receiver.Idle) |-> strong(##[1:$] alert_ack_o),
clk_i, !rst_ni || error_setreg_q)
clk_i, !rst_ni || error_setreg_q || init_pending)
// transmission of pings
// this bound is relatively large as in the worst case, we need to resolve
@ -112,34 +118,47 @@ module prim_alert_rxtx_async_assert_fpv (
`ASSERT(AlertPingOk_A, !(prim_alert_rxtx_async_fpv.i_prim_alert_sender.state_q inside {
prim_alert_rxtx_async_fpv.i_prim_alert_sender.PingHsPhase1,
prim_alert_rxtx_async_fpv.i_prim_alert_sender.PingHsPhase2}) && $rose(ping_req_i) |->
##[1:23] ping_ok_o, clk_i, !rst_ni || error_setreg_q)
##[1:23] ping_ok_o,
clk_i, !rst_ni || error_setreg_q || init_pending)
`ASSERT(AlertPingIgnored_A, (prim_alert_rxtx_async_fpv.i_prim_alert_sender.state_q inside {
prim_alert_rxtx_async_fpv.i_prim_alert_sender.PingHsPhase1,
prim_alert_rxtx_async_fpv.i_prim_alert_sender.PingHsPhase2}) && $rose(ping_req_i) |->
ping_ok_o == 0 throughout ping_req_i[->1], clk_i, !rst_ni || error_setreg_q)
ping_ok_o == 0 throughout ping_req_i[->1],
clk_i, !rst_ni || error_setreg_q)
// transmission of first alert assertion (no ping collision)
`ASSERT(AlertCheck0_A, !ping_req_i [*10] ##1 ($rose(alert_req_i) || $rose(alert_test_i)) &&
(prim_alert_rxtx_async_fpv.i_prim_alert_sender.state_q ==
prim_alert_rxtx_async_fpv.i_prim_alert_sender.Idle) |->
##[3:5] alert_o, clk_i, !rst_ni || ping_req_i || error_setreg_q)
##[3:5] alert_o,
clk_i, !rst_ni || ping_req_i || error_setreg_q || init_pending)
// eventual transmission of alerts in the general case which can include continous ping
// collisions
`ASSERT(AlertCheck1_A, alert_req_i || alert_test_i |->
strong(##[1:$] (prim_alert_rxtx_async_fpv.i_prim_alert_sender.state_q ==
prim_alert_rxtx_async_fpv.i_prim_alert_sender.Idle && !ping_req_i) ##[3:5] alert_o),
clk_i, !rst_ni || error_setreg_q ||
prim_alert_rxtx_async_fpv.i_prim_alert_sender.alert_clr)
prim_alert_rxtx_async_fpv.i_prim_alert_sender.alert_clr || init_pending)
// basic liveness of FSMs in case no errors are present
`ASSERT(FsmLivenessSender_A, !error_present [*2] ##1 !error_present &&
(prim_alert_rxtx_async_fpv.i_prim_alert_sender.state_q !=
prim_alert_rxtx_async_fpv.i_prim_alert_sender.Idle) |->
strong(##[1:$] (prim_alert_rxtx_async_fpv.i_prim_alert_sender.state_q ==
prim_alert_rxtx_async_fpv.i_prim_alert_sender.Idle)), clk_i, !rst_ni || error_present)
prim_alert_rxtx_async_fpv.i_prim_alert_sender.Idle)),
clk_i, !rst_ni || error_present || init_pending)
`ASSERT(FsmLivenessReceiver_A, !error_present [*2] ##1 !error_present &&
(prim_alert_rxtx_async_fpv.i_prim_alert_receiver.state_q !=
prim_alert_rxtx_async_fpv.i_prim_alert_receiver.Idle) |->
strong(##[1:$] (prim_alert_rxtx_async_fpv.i_prim_alert_receiver.state_q ==
prim_alert_rxtx_async_fpv.i_prim_alert_receiver.Idle)),clk_i, !rst_ni || error_present)
prim_alert_rxtx_async_fpv.i_prim_alert_receiver.Idle)),
clk_i, !rst_ni || error_present || init_pending)
// check that the in-band reset moves sender FSM into Idle state.
`ASSERT(InBandInitFromReceiverToSender_A,
init_trig_i == lc_ctrl_pkg::On
|->
##[1:30] (prim_alert_rxtx_async_fpv.i_prim_alert_sender.state_q ==
prim_alert_rxtx_async_fpv.i_prim_alert_sender.Idle),
clk_i, !rst_ni || error_present)
endmodule : prim_alert_rxtx_async_assert_fpv

View file

@ -20,7 +20,7 @@ module prim_esc_rxtx_assert_fpv (
input ping_req_i,
input ping_ok_o,
input integ_fail_o,
input esc_en_o
input esc_req_o
);
logic error_present;
@ -103,7 +103,7 @@ module prim_esc_rxtx_assert_fpv (
`ASSERT(EscCheck_A,
##1 esc_req_i
|->
##[0:1] esc_en_o,
##[0:1] esc_req_o,
clk_i,
!rst_ni ||
error_present)
@ -143,10 +143,10 @@ module prim_esc_rxtx_assert_fpv (
`ASSERT(AutoEscalation0_A,
ping_req_i &&
ping_ok_o &&
!esc_en_o ##1
!esc_req_o ##1
!ping_req_i [*0 : 2**prim_esc_rxtx_fpv.u_prim_esc_receiver.TimeoutCntDw - 4]
|->
!esc_en_o,
!esc_req_o,
clk_i,
!rst_ni ||
error_d ||
@ -157,10 +157,10 @@ module prim_esc_rxtx_assert_fpv (
`ASSERT(AutoEscalation1_A,
ping_req_i &&
ping_ok_o &&
!esc_en_o ##1
!esc_req_o ##1
!ping_req_i [* 2**prim_esc_rxtx_fpv.u_prim_esc_receiver.TimeoutCntDw - 3 : $]
|->
esc_en_o,
esc_req_o,
clk_i,
!rst_ni ||
error_d ||

View file

@ -73,7 +73,7 @@ module prim_fifo_sync_assert_fpv #(
if (Pass) begin : gen_pass
assign ref_rdata = (ref_depth == 0) ? wdata_i : fifo;
end else begin : no_pass
end else begin : gen_no_pass
assign ref_rdata = fifo;
end
@ -119,7 +119,7 @@ module prim_fifo_sync_assert_fpv #(
if (Pass) begin : gen_pass
assign ref_rdata = (ref_depth == 0) ? wdata_i : fifo[rptr];
end else begin : no_pass
end else begin : gen_no_pass
assign ref_rdata = fifo[rptr];
end
@ -150,7 +150,8 @@ module prim_fifo_sync_assert_fpv #(
// this is unreachable in depth 1 no-pass through mode
if (Depth == 1 && Pass) begin : gen_d1_passthru
// check simultaneous write and read
`ASSERT(WriteAndRead_A, wready_o && wvalid_i && rvalid_o && rready_i |=> depth_o == $past(depth_o),
`ASSERT(WriteAndRead_A,
wready_o && wvalid_i && rvalid_o && rready_i |=> depth_o == $past(depth_o),
clk_i, !rst_ni || clr_i)
end

View file

@ -4,14 +4,6 @@
#
# waiver file for prim
# prim_assert
waive -rules {UNDEF_MACRO_REF} -location {prim_assert.sv} -regexp {Macro definition for 'ASSERT_RPT' includes expansion of undefined macro '__(FILE|LINE)__'} \
-comment "This is an UVM specific macro inside our assertion shortcuts"
# unfortunately most tools do not support line wrapping within the declaration of macro functions, hence we have to waive
# line length violations.
waive -rules {LINE_LENGTH} -location {prim_assert.sv} -msg {Line length of} \
-comment "Some macros cannot be line-wrapped, as some tools do not support that."
# prim_packer
waive -rules INTEGER -location {prim_packer.sv} -msg {'i' of type int used as a non-constant value} \
-comment "This assigns int i (signed) to a multibit logic variable (unsigned), which is fine"
@ -28,3 +20,7 @@ waive -rules {HIER_BRANCH_NOT_READ} -location {tlul_fifo_sync.sv} -regexp {Conne
#
#waive -rules NOT_READ -location {prim_ram_*_wrapper*} -regexp {(a|b)_rdata_(q|d)\[38} \
# -comment "Syndrome is not going out to the interface"
# prim_sram_arbiter
waive -rules CONST_OUTPUT -location {prim_sram_arbiter.sv} -regexp {rsp_error.* is driven by constant} \
-comment "SRAM protection is not yet implemented"

View file

@ -9,8 +9,5 @@ waive -rules PARTIAL_CONST_ASSIGN -location {prim_arbiter_*.sv} -regexp {'mask.0
waive -rules CONST_FF -location {prim_arbiter_*.sv} -regexp {Flip-flop 'mask.0.' is driven by constant} \
-comment "makes the code more readable"
waive -rules CONST_OUTPUT -location {prim_sram_arbiter.sv} -regexp {rsp_error.* is driven by constant} \
-comment "SRAM protection is not yet implemented"
waive -rules {HIER_BRANCH_NOT_READ INPUT_NOT_READ} -location {prim_arbiter_fixed.sv} -regexp {.*'(clk_i|rst_ni)' is not read from in module 'prim_arbiter_fixed'.*} \
-comment "clk_ and rst_ni are only used for assertions in this module."

View file

@ -0,0 +1,5 @@
// Copyright lowRISC contributors.
// Licensed under the Apache License, Version 2.0, see LICENSE for details.
// SPDX-License-Identifier: Apache-2.0
`verilator_config

View file

@ -0,0 +1,12 @@
# 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_assert
waive -rules {UNDEF_MACRO_REF} -location {prim_assert.sv} -regexp {Macro definition for 'ASSERT_RPT' includes expansion of undefined macro '__(FILE|LINE)__'} \
-comment "This is an UVM specific macro inside our assertion shortcuts"
# unfortunately most tools do not support line wrapping within the declaration of macro functions, hence we have to waive
# line length violations.
waive -rules {LINE_LENGTH} -location {prim_assert.sv} -msg {Line length of} \
-comment "Some macros cannot be line-wrapped, as some tools do not support that."

View file

@ -24,3 +24,8 @@ waive -rules VAR_INDEX_RANGE -location {prim_fifo_*sync.sv} -regexp {maximu
waive -rules EXPLICIT_BITLEN -location {prim_fifo_*sync.sv} -regexp {Bit length not specified for constant '1'} \
-comment "index is protected by control logic"
## prim_fifo_async_sram_adapter
waive -rules ARITH_CONTEXT -location {prim_fifo_async_sram_adapter.sv} \
-regexp {(r|w)_wptr_v.*_rptr_v} \
-comment "The pointer value width is determined. Remove the casting for readability"

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_xor2
waive -rules {STAR_PORT_CONN_USE} -location {prim_xor2.sv} -regexp {.*wild card port connection encountered on instance.*} \
-comment "Generated prims may have wildcard connections."

View file

@ -12,6 +12,7 @@ filesets:
- lowrisc:prim:diff_decode
- lowrisc:prim:buf
- lowrisc:prim:flop
- lowrisc:ip:lc_ctrl_pkg
files:
- rtl/prim_alert_pkg.sv
- rtl/prim_alert_receiver.sv

View file

@ -14,7 +14,31 @@ filesets:
- rtl/prim_assert_standard_macros.svh : {is_include_file : true}
file_type: systemVerilogSource
files_verilator_waiver:
depend:
# common waivers
- lowrisc:lint:common
files:
- lint/prim_assert.vlt
file_type: vlt
files_ascentlint_waiver:
depend:
# common waivers
- lowrisc:lint:common
files:
- lint/prim_assert.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

View file

@ -0,0 +1,21 @@
CAPI=2:
# Copyright lowRISC contributors.
# Licensed under the Apache License, Version 2.0, see LICENSE for details.
# SPDX-License-Identifier: Apache-2.0
name: "lowrisc:prim:measure"
description: "Clock measurement"
filesets:
files_rtl:
depend:
- lowrisc:prim:all
- lowrisc:prim:flop_2sync
- lowrisc:prim:assert
files:
- rtl/prim_clock_meas.sv
file_type: systemVerilogSource
targets:
default:
filesets:
- files_rtl

View file

@ -0,0 +1,38 @@
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:count"
description: ""
filesets:
files_rtl:
depend:
- lowrisc:prim:assert
- lowrisc:prim:count_pkg:0.1
files:
- rtl/prim_count.sv
file_type: systemVerilogSource
files_verilator_waiver:
depend:
# common waivers
- lowrisc:lint:common
files_ascentlint_waiver:
depend:
# common waivers
- lowrisc:lint:common
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

View file

@ -0,0 +1,37 @@
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:count_pkg:0.1"
description: "Hardened counter package"
filesets:
files_rtl:
files:
- rtl/prim_count_pkg.sv
file_type: systemVerilogSource
files_verilator_waiver:
depend:
# common waivers
- lowrisc:lint:common
files_ascentlint_waiver:
depend:
# common waivers
- lowrisc:lint:common
files:
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

View file

@ -9,6 +9,7 @@ filesets:
files_rtl:
depend:
- lowrisc:prim:assert
- lowrisc:prim:flop_2sync
files:
- rtl/prim_diff_decode.sv
file_type: systemVerilogSource

View file

@ -0,0 +1,44 @@
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:edge_detector:0.1"
description: "Positive/ Negative Edge detector"
filesets:
files_rtl:
depend:
- lowrisc:prim:assert
- lowrisc:prim:flop_2sync
files:
- rtl/prim_edge_detector.sv
file_type: systemVerilogSource
files_verilator_waiver:
depend:
# common waivers
- lowrisc:lint:common
files:
- lint/prim.vlt
file_type: vlt
files_ascentlint_waiver:
depend:
# common waivers
- lowrisc:lint:common
files:
- lint/prim.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

View file

@ -12,6 +12,7 @@ filesets:
- lowrisc:prim:util
- lowrisc:prim:flop_2sync
files:
- rtl/prim_fifo_async_sram_adapter.sv
- rtl/prim_fifo_async.sv
- rtl/prim_fifo_sync.sv
file_type: systemVerilogSource

View file

@ -0,0 +1,53 @@
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:lc_combine:0.1"
description: "Logic combination of life cycle control signals."
filesets:
files_rtl:
depend:
- lowrisc:ip:lc_ctrl_pkg
files:
- rtl/prim_lc_combine.sv
file_type: systemVerilogSource
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_lc_combine.waiver
file_type: waiver
files_veriblelint_waiver:
depend:
# common waivers
- lowrisc:lint:common
targets:
default: &default_target
filesets:
- tool_verilator ? (files_verilator_waiver)
- tool_ascentlint ? (files_ascentlint_waiver)
- tool_veriblelint ? (files_veriblelint_waiver)
- files_rtl
lint:
<<: *default_target
default_tool: verilator
parameters:
- SYNTHESIS=true
tools:
verilator:
mode: lint-only
verilator_options:
- "-Wall"

View file

@ -9,6 +9,7 @@ filesets:
files_rtl:
depend:
- lowrisc:prim:assert
- lowrisc:prim:cipher_pkg
files:
- rtl/prim_lfsr.sv
file_type: systemVerilogSource

View file

@ -0,0 +1,17 @@
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:mubi:0.1"
description: "Multibit types and functions"
filesets:
files_rtl:
files:
- rtl/prim_mubi_pkg.sv
file_type: systemVerilogSource
targets:
default:
filesets:
- files_rtl

View file

@ -8,9 +8,14 @@ description: "Register slices"
filesets:
files_rtl:
files:
- rtl/prim_subreg_pkg.sv
- rtl/prim_subreg_arb.sv
- rtl/prim_subreg_cdc.sv
- rtl/prim_reg_cdc.sv
- rtl/prim_subreg.sv
- rtl/prim_subreg_async.sv
- rtl/prim_subreg_ext.sv
- rtl/prim_subreg_ext_async.sv
- rtl/prim_subreg_shadow.sv
file_type: systemVerilogSource

View file

@ -22,6 +22,8 @@ filesets:
depend:
# common waivers
- lowrisc:lint:common
files:
- lint/prim_xor2.waiver
file_type: waiver
files_veriblelint_waiver:

View file

@ -37,6 +37,9 @@ module prim_alert_receiver
) (
input clk_i,
input rst_ni,
// if set to lc_ctrl_pkg::On, this triggers the in-band alert channel
// reset, which resets both the sender and receiver FSMs into IDLE.
input lc_ctrl_pkg::lc_tx_t init_trig_i,
// this triggers a ping test. keep asserted
// until ping_ok_o is asserted.
input ping_req_i,
@ -85,22 +88,25 @@ module prim_alert_receiver
/////////////////////////////////////////////////////
// main protocol FSM that drives the diff outputs //
/////////////////////////////////////////////////////
typedef enum logic [1:0] {Idle, HsAckWait, Pause0, Pause1} state_e;
typedef enum logic [2:0] {Idle, HsAckWait, Pause0, Pause1, InitReq, InitAckWait} state_e;
state_e state_d, state_q;
logic ping_rise;
logic ping_tog_pd, ping_tog_pq, ping_tog_dn, ping_tog_nq;
logic ack_pd, ack_pq, ack_dn, ack_nq;
logic ping_req_d, ping_req_q;
logic ping_pending_d, ping_pending_q;
logic send_init;
// 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_tog_pd = (ping_rise) ? ~ping_tog_pq : ping_tog_pq;
assign ping_tog_pd = (send_init) ? 1'b0 :
(ping_rise) ? ~ping_tog_pq : ping_tog_pq;
assign ack_dn = ~ack_pd;
assign ping_tog_dn = ~ping_tog_pd;
// 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;
// This prevents further tool optimizations of the differential signal.
prim_generic_flop #(
@ -152,6 +158,7 @@ module prim_alert_receiver
ping_ok_o = 1'b0;
integ_fail_o = 1'b0;
alert_o = 1'b0;
send_init = 1'b0;
unique case (state_q)
Idle: begin
@ -178,22 +185,62 @@ module prim_alert_receiver
// pause cycles between back-to-back handshakes
Pause0: state_d = Pause1;
Pause1: state_d = Idle;
default : ; // full case
// this state is only reached if an in-band reset is
// requested via the low-power logic.
InitReq: begin
// we deliberately place a sigint error on the ack and ping lines in this case.
send_init = 1'b1;
// 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
// in the initialization sequence.
if (init_trig_i == lc_ctrl_pkg::On) begin
ping_ok_o = ping_pending_q;
// the sender will respond to the sigint error above with a sigint error on the alert lines.
// hence we treat the alert_sigint like an acknowledgement in this case.
end else if (alert_sigint) begin
state_d = InitAckWait;
end
end
// We get here if the sender has responded with alert_sigint, and init_trig_i==lc_ctrl_pkg::On
// has been deasserted. At this point, we need to wait for the alert_sigint to drop again
// before resuming normal operation.
InitAckWait: begin
if (!alert_sigint) begin
state_d = Pause0;
end
end
default: state_d = Idle;
endcase
// override in case of sigint
if (alert_sigint) begin
state_d = Idle;
ack_pd = 1'b0;
ping_ok_o = 1'b0;
integ_fail_o = 1'b1;
alert_o = 1'b0;
// once the initialization sequence has been triggered,
// overrides are not allowed anymore until the initialization has been completed.
if (!(state_q inside {InitReq, InitAckWait})) begin
// in this case, abort and jump into the initialization sequence
if (init_trig_i == lc_ctrl_pkg::On) begin
state_d = InitReq;
ack_pd = 1'b0;
ping_ok_o = 1'b0;
integ_fail_o = 1'b0;
alert_o = 1'b0;
send_init = 1'b1;
// if we're not busy with an init request, we clamp down all outputs
// and indicate an integrity failure.
end else if (alert_sigint) begin
state_d = Idle;
ack_pd = 1'b0;
ping_ok_o = 1'b0;
integ_fail_o = 1'b1;
alert_o = 1'b0;
end
end
end
always_ff @(posedge clk_i or negedge rst_ni) begin : p_reg
if (!rst_ni) begin
state_q <= Idle;
// Reset into the init request so that an alert handler reset implicitly
// triggers an in-band reset of all alert channels.
state_q <= InitReq;
ping_req_q <= 1'b0;
ping_pending_q <= 1'b0;
end else begin
@ -214,11 +261,13 @@ module prim_alert_receiver
`ASSERT_KNOWN(AlertKnownO_A, alert_o)
`ASSERT_KNOWN(PingPKnownO_A, alert_rx_o)
// check encoding of outgoing diffpairs
`ASSERT(PingDiffOk_A, alert_rx_o.ping_p ^ alert_rx_o.ping_n)
`ASSERT(AckDiffOk_A, alert_rx_o.ack_p ^ alert_rx_o.ack_n)
// 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(AckDiffOk_A, ##2 $past(send_init) ^ alert_rx_o.ack_p ^ alert_rx_o.ack_n)
// ping request at input -> need to see encoded ping request
`ASSERT(PingRequest0_A, ##1 $rose(ping_req_i) |=> $changed(alert_rx_o.ping_p))
`ASSERT(PingRequest0_A, ##1 $rose(ping_req_i) && !send_init |=> $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
@ -226,25 +275,91 @@ module prim_alert_receiver
if (AsyncOn) begin : gen_async_assert
// signal integrity check propagation
`ASSERT(SigInt_A, alert_tx_i.alert_p == alert_tx_i.alert_n [*2] |->
##2 integ_fail_o)
`ASSERT(SigInt_A,
alert_tx_i.alert_p == alert_tx_i.alert_n [*2] ##2
!(state_q inside {InitReq, InitAckWait}) &&
init_trig_i != lc_ctrl_pkg::On
|->
integ_fail_o)
// TODO: need to add skewed cases as well, the assertions below assume no skew at the moment
// ping response
`ASSERT(PingResponse1_A, ##1 $rose(alert_tx_i.alert_p) &&
(alert_tx_i.alert_p ^ alert_tx_i.alert_n) ##2 state_q == Idle && ping_pending_q |->
ping_ok_o, clk_i, !rst_ni || integ_fail_o)
`ASSERT(PingResponse1_A,
##1 $rose(alert_tx_i.alert_p) &&
(alert_tx_i.alert_p ^ alert_tx_i.alert_n) ##2
state_q == Idle && ping_pending_q
|->
ping_ok_o,
clk_i, !rst_ni || integ_fail_o || init_trig_i == lc_ctrl_pkg::On)
// alert
`ASSERT(Alert_A, ##1 $rose(alert_tx_i.alert_p) && (alert_tx_i.alert_p ^ alert_tx_i.alert_n) ##2
state_q == Idle && !ping_pending_q |-> alert_o, clk_i, !rst_ni || integ_fail_o)
`ASSERT(Alert_A,
##1 $rose(alert_tx_i.alert_p) &&
(alert_tx_i.alert_p ^ alert_tx_i.alert_n) ##2
state_q == Idle &&
!ping_pending_q
|->
alert_o,
clk_i, !rst_ni || integ_fail_o || init_trig_i == lc_ctrl_pkg::On)
end else begin : gen_sync_assert
// signal integrity check propagation
`ASSERT(SigInt_A, alert_tx_i.alert_p == alert_tx_i.alert_n |-> integ_fail_o)
`ASSERT(SigInt_A,
alert_tx_i.alert_p == alert_tx_i.alert_n &&
!(state_q inside {InitReq, InitAckWait}) &&
init_trig_i != lc_ctrl_pkg::On
|->
integ_fail_o)
// ping response
`ASSERT(PingResponse1_A, ##1 $rose(alert_tx_i.alert_p) && state_q == Idle && ping_pending_q |->
ping_ok_o, clk_i, !rst_ni || integ_fail_o)
`ASSERT(PingResponse1_A,
##1 $rose(alert_tx_i.alert_p) &&
state_q == Idle &&
ping_pending_q
|->
ping_ok_o,
clk_i, !rst_ni || integ_fail_o || init_trig_i == lc_ctrl_pkg::On)
// alert
`ASSERT(Alert_A, ##1 $rose(alert_tx_i.alert_p) && state_q == Idle && !ping_pending_q |->
alert_o, clk_i, !rst_ni || integ_fail_o)
`ASSERT(Alert_A,
##1 $rose(alert_tx_i.alert_p) &&
state_q == Idle &&
!ping_pending_q
|->
alert_o,
clk_i, !rst_ni || integ_fail_o || init_trig_i == lc_ctrl_pkg::On)
end
// check in-band init request is always accepted
`ASSERT(InBandInitRequest_A,
init_trig_i == lc_ctrl_pkg::On &&
state_q != InitAckWait
|=>
state_q == InitReq)
// check in-band init sequence moves FSM into IDLE state
`ASSERT(InBandInitSequence_A,
(state_q == InitReq &&
init_trig_i == lc_ctrl_pkg::On [*1:$]) ##1
(alert_sigint &&
init_trig_i != lc_ctrl_pkg::On) [*1:$] ##1
(!alert_sigint &&
init_trig_i != lc_ctrl_pkg::On) [*3]
|=>
state_q == Idle)
// check there are no spurious alerts during init
`ASSERT(NoSpuriousAlertsDuringInit_A,
init_trig_i == lc_ctrl_pkg::On ||
(state_q inside {InitReq, InitAckWait})
|->
!alert_o)
// check that there are no spurious ping OKs
`ASSERT(NoSpuriousPingOksDuringInit_A,
(init_trig_i == lc_ctrl_pkg::On ||
(state_q inside {InitReq, InitAckWait})) &&
!ping_pending_q
|->
!ping_ok_o)
// check ping request is bypassed when in init state
`ASSERT(PingOkBypassDuringInit_A,
$rose(ping_req_i) ##1
state_q == InitReq &&
init_trig_i == lc_ctrl_pkg::On
|->
ping_ok_o)
endmodule : prim_alert_receiver

View file

@ -131,7 +131,6 @@ module prim_alert_sender
AlertHsPhase2,
PingHsPhase1,
PingHsPhase2,
SigInt,
Pause0,
Pause1
} state_e;
@ -234,34 +233,22 @@ module prim_alert_sender
Pause0: begin
state_d = Pause1;
end
// clear and ack alert request if it was set
Pause1: begin
state_d = Idle;
end
// we have a signal integrity issue at one of
// the incoming diff pairs. this condition is
// signalled by setting the output diffpair
// to the same value and continuously toggling
// them.
SigInt: begin
state_d = Idle;
if (sigint_detected) begin
state_d = SigInt;
alert_pd = ~alert_pq;
alert_nd = ~alert_pq;
end
end
// catch parasitic states
default : state_d = Idle;
endcase
// bail out if a signal integrity issue has been detected
if (sigint_detected && (state_q != SigInt)) begin
state_d = SigInt;
// we have a signal integrity issue at one of the incoming diff pairs. this condition is
// signalled by setting the output diffpair to zero. If the sigint has disappeared, we clear
// the ping request state of this sender and go back to idle.
if (sigint_detected) begin
state_d = Idle;
alert_pd = 1'b0;
alert_nd = 1'b0;
ping_clr = 1'b0;
ping_clr = 1'b1;
alert_clr = 1'b0;
end
end
@ -296,15 +283,28 @@ module prim_alert_sender
// assertions //
////////////////
// however, since we use sequence constructs below, we need to wrap the entire block again.
// typically, the ASSERT macros already contain this INC_ASSERT macro.
`ifdef INC_ASSERT
// check whether all outputs have a good known state after reset
`ASSERT_KNOWN(AlertPKnownO_A, alert_tx_o)
if (AsyncOn) begin : gen_async_assert
sequence PingSigInt_S;
alert_rx_i.ping_p == alert_rx_i.ping_n [*2];
endsequence
sequence AckSigInt_S;
alert_rx_i.ping_p == alert_rx_i.ping_n [*2];
endsequence
// check propagation of sigint issues to output within three cycles
`ASSERT(SigIntPing_A, alert_rx_i.ping_p == alert_rx_i.ping_n [*2] |->
// shift sequence to the right to avoid reset effects.
`ASSERT(SigIntPing_A, ##1 PingSigInt_S |->
##3 alert_tx_o.alert_p == alert_tx_o.alert_n)
`ASSERT(SigIntAck_A, alert_rx_i.ack_p == alert_rx_i.ack_n [*2] |->
`ASSERT(SigIntAck_A, ##1 AckSigInt_S |->
##3 alert_tx_o.alert_p == alert_tx_o.alert_n)
// Test in-band FSM reset request (via signal integrity error)
`ASSERT(InBandInitFsm_A, PingSigInt_S or AckSigInt_S |-> ##3 state_q == Idle)
`ASSERT(InBandInitPing_A, PingSigInt_S or AckSigInt_S |-> ##3 !ping_set_q)
// output must be driven diff unless sigint issue detected
`ASSERT(DiffEncoding_A, (alert_rx_i.ack_p ^ alert_rx_i.ack_n) &&
(alert_rx_i.ping_p ^ alert_rx_i.ping_n) |->
@ -318,11 +318,20 @@ module prim_alert_sender
(alert_rx_i.ping_p ^ alert_rx_i.ping_n) ##2 state_q == Idle |=>
$rose(alert_tx_o.alert_p), clk_i, !rst_ni || (alert_tx_o.alert_p == alert_tx_o.alert_n))
end else begin : gen_sync_assert
sequence PingSigInt_S;
alert_rx_i.ping_p == alert_rx_i.ping_n;
endsequence
sequence AckSigInt_S;
alert_rx_i.ping_p == alert_rx_i.ping_n;
endsequence
// check propagation of sigint issues to output within one cycle
`ASSERT(SigIntPing_A, alert_rx_i.ping_p == alert_rx_i.ping_n |=>
`ASSERT(SigIntPing_A, PingSigInt_S |=>
alert_tx_o.alert_p == alert_tx_o.alert_n)
`ASSERT(SigIntAck_A, alert_rx_i.ack_p == alert_rx_i.ack_n |=>
`ASSERT(SigIntAck_A, AckSigInt_S |=>
alert_tx_o.alert_p == alert_tx_o.alert_n)
// Test in-band FSM reset request (via signal integrity error)
`ASSERT(InBandInitFsm_A, PingSigInt_S or AckSigInt_S |=> state_q == Idle)
`ASSERT(InBandInitPing_A, PingSigInt_S or AckSigInt_S |=> !ping_set_q)
// output must be driven diff unless sigint issue detected
`ASSERT(DiffEncoding_A, (alert_rx_i.ack_p ^ alert_rx_i.ack_n) &&
(alert_rx_i.ping_p ^ alert_rx_i.ping_n) |=> alert_tx_o.alert_p ^ alert_tx_o.alert_n)
@ -355,5 +364,6 @@ module prim_alert_sender
// if alert_test_i is true, handshakes should be continuously repeated
`ASSERT(AlertTestHs_A, alert_test_i && state_q == Idle |=> $rose(alert_tx_o.alert_p),
clk_i, !rst_ni || (alert_tx_o.alert_p == alert_tx_o.alert_n))
`endif
endmodule : prim_alert_sender

View file

@ -0,0 +1,137 @@
// Copyright lowRISC contributors.
// Licensed under the Apache License, Version 2.0, see LICENSE for details.
// SPDX-License-Identifier: Apache-2.0
//
// prim_clk_meas is a generic module that measures two clocks against each other.
// One clock is the reference, and another is the input.
// If the input clocks becomes too fast or too slow, an error condition is created.
// The default parameters assume the reference clock is significantly slower than
// the input clock
`include "prim_assert.sv"
module prim_clock_meas #(
// Maximum value of input clock counts over measurement period
parameter int Cnt = 16,
// Maximum value of reference clock counts over measurement period
parameter int RefCnt = 1,
localparam int CntWidth = prim_util_pkg::vbits(Cnt),
localparam int RefCntWidth = prim_util_pkg::vbits(RefCnt)
) (
input clk_i,
input rst_ni,
input clk_ref_i,
input rst_ref_ni,
input en_i,
input [CntWidth-1:0] max_cnt,
input [CntWidth-1:0] min_cnt,
// the output valid and fast/slow indications are all on the
// input clock domain
output logic valid_o,
output logic fast_o,
output logic slow_o
);
//////////////////////////
// Reference clock logic
//////////////////////////
logic ref_en;
prim_flop_2sync #(
.Width(1)
) u_ref_meas_en_sync (
.d_i(en_i),
.clk_i(clk_ref_i),
.rst_ni(rst_ref_ni),
.q_o(ref_en)
);
logic ref_valid;
logic [RefCntWidth-1:0] ref_cnt;
always_ff @(posedge clk_ref_i or negedge rst_ref_ni) begin
if (!rst_ref_ni) begin
ref_cnt <= '0;
ref_valid <= '0;
end else if (!ref_en && |ref_cnt) begin
ref_cnt <= '0;
ref_valid <= '0;
end else if (ref_en && (ref_cnt == RefCnt - 1)) begin
// restart count and measure
ref_cnt <= '0;
ref_valid <= 1'b1;
end else if (ref_en) begin
ref_cnt <= ref_cnt + 1'b1;
ref_valid <= 1'b0;
end
end
//////////////////////////
// Input Clock Logic
//////////////////////////
logic valid;
// The valid pulse causes the count to reset and start counting again
// for each reference cycle.
// The count obtained during the the last refernece cycle is then used
// to measure how fast/slow the input clock is.
prim_pulse_sync u_sync_ref (
.clk_src_i(clk_ref_i),
.rst_src_ni(rst_ref_ni),
.src_pulse_i(ref_valid),
.clk_dst_i(clk_i),
.rst_dst_ni(rst_ni),
.dst_pulse_o(valid)
);
logic cnt_en;
always_ff @(posedge clk_i or negedge rst_ni) begin
if (!rst_ni) begin
cnt_en <= '0;
end else if (!en_i) begin
cnt_en <= '0;
end else if (en_i && valid) begin
cnt_en <= 1'b1;
end
end
logic cnt_ovfl;
logic [CntWidth-1:0] cnt;
always_ff @(posedge clk_i or negedge rst_ni) begin
if (!rst_ni) begin
cnt <= '0;
cnt_ovfl <= '0;
end else if (!cnt_en && |cnt) begin
cnt <= '0;
cnt_ovfl <= '0;
end else if (valid_o) begin
cnt <= '0;
cnt_ovfl <= '0;
end else if (cnt_en && cnt_ovfl) begin
cnt <= '{default: '1};
end else if (cnt_en) begin
{cnt_ovfl, cnt} <= cnt + 1'b1;
end
end
assign valid_o = en_i & valid & |cnt;
assign fast_o = valid_o & ((cnt > max_cnt) | cnt_ovfl);
assign slow_o = valid_o & (cnt < min_cnt);
//////////////////////////
// Assertions
//////////////////////////
`ASSERT_INIT(RefCntVal_A, RefCnt >= 1)
// if we've reached the max count, enable must be 0 next.
// Otherwise the width of the counter is too small to accommodate the usecase
`ASSERT(MaxWidth_A, (cnt == Cnt-1) |=> !cnt_en )
endmodule // prim_clk_meas

View file

@ -0,0 +1,152 @@
// Copyright lowRISC contributors.
// Licensed under the Apache License, Version 2.0, see LICENSE for details.
// SPDX-License-Identifier: Apache-2.0
//
// Primitive hardened counter module
//
// This module implements two styles of hardened counting
// 1. Duplicate count
// 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
//
// 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.
//
// 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.
//
// 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.
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
) (
input clk_i,
input rst_ni,
input clr_i,
input set_i,
input [Width-1:0] set_cnt_i,
input en_i,
input [Width-1:0] step_i, // increment/decrement step when enabled
output logic [Width-1:0] cnt_o,
output logic err_o
);
// if output selects downcount, it MUST be the cross count style
`ASSERT_INIT(CntStyleMatch_A, OutSelDnCnt ? CntStyle == CrossCnt : 1'b1)
localparam int CntCopies = (CntStyle == DupCnt) ? 2 : 1;
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;
end
end
for (genvar i = 0; i < CntCopies; i++) begin : gen_cnts
// up-count
assign up_cnt_d[i] = (clr_i) ? '0 :
(set_i & CntStyle == DupCnt) ? set_cnt_i :
(en_i & up_cnt_q[i] < max_val) ? up_cnt_q[i] + step_i :
up_cnt_q[i];
prim_buf #(
.Width(Width)
) u_buf (
.in_i(up_cnt_d[i]),
.out_o(up_cnt_d_buf[i])
);
prim_flop #(
.Width(Width),
.ResetValue('0)
) u_cnt_flop (
.clk_i,
.rst_ni,
.d_i(up_cnt_d_buf[i]),
.q_o(up_cnt_q[i])
);
end
if (CntStyle == CrossCnt) begin : gen_cross_cnt_hardening
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;
end else if (clr_i) begin
down_cnt <= '0;
end else if (set_i) begin
down_cnt <= set_cnt_i;
end else if (en_i && down_cnt > '0) begin
down_cnt <= down_cnt - step_i;
end
end
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;
`ASSERT(CrossCntErrForward_A,
(cmp_valid == CmpValid) && ((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}))
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]);
`ASSERT(DupCntErrForward_A, up_cnt_q[0] != up_cnt_q[1] |-> err_o)
`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;
// Clear and set should not be seen at the same time
`ASSERT(SimulClrSet_A, clr_i || set_i |-> clr_i != set_i)
// Max value must be an integer multiple of the step size during cross count
`ASSERT(DownCntStepInt_A, (CntStyle == CrossCnt) & (cmp_valid == CmpValid)
|-> max_val % step_i == 0)
// If using DupCnt, the count can never overflow
logic [Width:0] unused_cnt;
assign unused_cnt = up_cnt_q[0] + step_i;
logic unused_incr_cnt;
assign unused_incr_cnt = (CntStyle == DupCnt) & (cmp_valid == CmpValid) & !clr_i & !set_i;
`ASSERT(UpCntOverFlow_A, unused_incr_cnt |-> ~unused_cnt[Width])
endmodule // keymgr_cnt

View file

@ -0,0 +1,22 @@
// Copyright lowRISC contributors.
// Licensed under the Apache License, Version 2.0, see LICENSE for details.
// SPDX-License-Identifier: Apache-2.0
//
// Package for primitive hardened counter module
//
package prim_count_pkg;
// Enumeration for hardened count style
typedef enum logic {
CrossCnt, // up count and down count
DupCnt // duplicate counters
} prim_count_style_e;
// Enumeration for differential valid
typedef enum logic [1:0] {
CmpInvalid = 2'b01,
CmpValid = 2'b10
} cmp_valid_e;
endpackage //

View file

@ -218,33 +218,56 @@ module prim_diff_decode #(
`ASSERT(SigintFallCheck_A, sigint_o |-> !fall_o)
if (AsyncOn) begin : gen_async_assert
`ifdef INC_ASSERT
// assertions for asynchronous case
// correctly detect sigint issue (only one transition cycle of permissible due to skew)
`ASSERT(SigintCheck0_A, diff_pi == diff_ni [*2] |-> ##[1:2] sigint_o)
// the synchronizer adds 2 cycles of latency
`ASSERT(SigintCheck1_A, ##1 (diff_pi ^ diff_ni) && $stable(diff_pi) && $stable(diff_ni) ##1
$rose(diff_pi) && $stable(diff_ni) ##1 $stable(diff_pi) && $fell(diff_ni) |->
##2 rise_o)
`ASSERT(SigintCheck2_A, ##1 (diff_pi ^ diff_ni) && $stable(diff_pi) && $stable(diff_ni) ##1
$fell(diff_pi) && $stable(diff_ni) ##1 $stable(diff_pi) && $rose(diff_ni) |->
##2 fall_o)
`ASSERT(SigintCheck3_A, ##1 (diff_pi ^ diff_ni) && $stable(diff_pi) && $stable(diff_ni) ##1
$rose(diff_ni) && $stable(diff_pi) ##1 $stable(diff_ni) && $fell(diff_pi) |->
##2 fall_o)
`ASSERT(SigintCheck4_A, ##1 (diff_pi ^ diff_ni) && $stable(diff_pi) && $stable(diff_ni) ##1
$fell(diff_ni) && $stable(diff_pi) ##1 $stable(diff_ni) && $rose(diff_pi) |->
##2 rise_o)
// correctly detect edges
`ASSERT(RiseCheck_A, ##1 $rose(diff_pi) && (diff_pi ^ diff_ni) |->
##[2:3] rise_o, clk_i, !rst_ni || sigint_o)
`ASSERT(FallCheck_A, ##1 $fell(diff_pi) && (diff_pi ^ diff_ni) |->
##[2:3] fall_o, clk_i, !rst_ni || sigint_o)
`ASSERT(EventCheck_A, ##1 $changed(diff_pi) && (diff_pi ^ diff_ni) |->
##[2:3] event_o, clk_i, !rst_ni || sigint_o)
// correctly detect level
`ASSERT(LevelCheck0_A, !sigint_o && (diff_pi ^ diff_ni) [*3] |=> $past(diff_pi, 2) == level_o,
clk_i, !rst_ni || sigint_o)
// in this case we need to sample the input signals onto the local clock to avoid race
// conditions between the RTL and assertion sampling in simulation.
logic hlp_diff_pq, hlp_diff_nq;
always_ff @(posedge clk_i or negedge rst_ni) begin : p_edge_reg
if (!rst_ni) begin
hlp_diff_pq <= 1'b0;
hlp_diff_nq <= 1'b1;
end else begin
hlp_diff_pq <= diff_pi;
hlp_diff_nq <= diff_ni;
end
end
// correctly detect sigint issue (only one transition cycle of permissible due to skew)
`ASSERT(SigintCheck0_A, hlp_diff_pq == hlp_diff_nq [*2] |-> ##[0:1] sigint_o)
// the synchronizer adds 2 cycles of latency with respect to input signals.
`ASSERT(SigintCheck1_A,
##1 (hlp_diff_pq ^ hlp_diff_nq) && $stable(hlp_diff_pq) && $stable(hlp_diff_nq) ##1
$rose(hlp_diff_pq) && $stable(hlp_diff_nq) ##1 $stable(hlp_diff_pq) && $fell(hlp_diff_nq)
|->
##1 rise_o)
`ASSERT(SigintCheck2_A,
##1 (hlp_diff_pq ^ hlp_diff_nq) && $stable(hlp_diff_pq) && $stable(hlp_diff_nq) ##1
$fell(hlp_diff_pq) && $stable(hlp_diff_nq) ##1 $stable(hlp_diff_pq) && $rose(hlp_diff_nq)
|->
##1 fall_o)
`ASSERT(SigintCheck3_A,
##1 (hlp_diff_pq ^ hlp_diff_nq) && $stable(hlp_diff_pq) && $stable(hlp_diff_nq) ##1
$rose(hlp_diff_nq) && $stable(hlp_diff_pq) ##1 $stable(hlp_diff_nq) && $fell(hlp_diff_pq)
|->
##1 fall_o)
`ASSERT(SigintCheck4_A,
##1 (hlp_diff_pq ^ hlp_diff_nq) && $stable(hlp_diff_pq) && $stable(hlp_diff_nq) ##1
$fell(hlp_diff_nq) && $stable(hlp_diff_pq) ##1 $stable(hlp_diff_nq) && $rose(hlp_diff_pq)
|->
##1 rise_o)
// correctly detect edges
`ASSERT(RiseCheck_A, ##1 $rose(hlp_diff_pq) && (hlp_diff_pq ^ hlp_diff_nq) |->
##[1:2] rise_o, clk_i, !rst_ni || sigint_o)
`ASSERT(FallCheck_A, ##1 $fell(hlp_diff_pq) && (hlp_diff_pq ^ hlp_diff_nq) |->
##[1:2] fall_o, clk_i, !rst_ni || sigint_o)
`ASSERT(EventCheck_A, ##1 $changed(hlp_diff_pq) && (hlp_diff_pq ^ hlp_diff_nq) |->
##[1:2] event_o, clk_i, !rst_ni || sigint_o)
// correctly detect level
`ASSERT(LevelCheck0_A, !sigint_o && (hlp_diff_pq ^ hlp_diff_nq) [*3] |=>
$past(hlp_diff_pq, 1) == level_o,
clk_i, !rst_ni || sigint_o)
`endif
end else begin : gen_sync_assert
// assertions for synchronous case
// correctly detect sigint issue

View file

@ -0,0 +1,55 @@
// Copyright lowRISC contributors.
// Licensed under the Apache License, Version 2.0, see LICENSE for details.
// SPDX-License-Identifier: Apache-2.0
//
// Edge Detector
module prim_edge_detector #(
parameter int unsigned Width = 1,
parameter logic [Width-1:0] ResetValue = '0,
// EnSync
//
// Enable Synchronizer to the input signal.
// It is assumed that the input signal is glitch free (registered input).
parameter bit EnSync = 1'b 1
) (
input clk_i,
input rst_ni,
input [Width-1:0] d_i,
output logic [Width-1:0] q_sync_o,
output logic [Width-1:0] q_posedge_pulse_o,
output logic [Width-1:0] q_negedge_pulse_o
);
logic [Width-1:0] q_sync_d, q_sync_q;
if (EnSync) begin : g_sync
prim_flop_2sync #(
.Width (Width),
.ResetValue (ResetValue)
) u_sync (
.clk_i,
.rst_ni,
.d_i,
.q_o (q_sync_d)
);
end : g_sync
else begin : g_nosync
assign q_sync_d = d_i;
end : g_nosync
assign q_sync_o = q_sync_d;
always_ff @(posedge clk_i or negedge rst_ni) begin
if (!rst_ni) q_sync_q <= ResetValue;
else q_sync_q <= q_sync_d;
end
assign q_posedge_pulse_o = q_sync_d & ~q_sync_q;
assign q_negedge_pulse_o = ~q_sync_d & q_sync_q;
endmodule : prim_edge_detector

Some files were not shown because too many files have changed in this diff Show more