diff --git a/dv/uvm/core_ibex/ibex_dv.f b/dv/uvm/core_ibex/ibex_dv.f index e7bf4438..c3dc7aa7 100644 --- a/dv/uvm/core_ibex/ibex_dv.f +++ b/dv/uvm/core_ibex/ibex_dv.f @@ -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 diff --git a/dv/uvm/icache/dv/env/ibex_icache_env_cfg.sv b/dv/uvm/icache/dv/env/ibex_icache_env_cfg.sv index 72129f41..59460325 100644 --- a/dv/uvm/icache/dv/env/ibex_icache_env_cfg.sv +++ b/dv/uvm/icache/dv/env/ibex_icache_env_cfg.sv @@ -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 diff --git a/vendor/lowrisc_ip.lock.hjson b/vendor/lowrisc_ip.lock.hjson index 4dca714f..3ac03f87 100644 --- a/vendor/lowrisc_ip.lock.hjson +++ b/vendor/lowrisc_ip.lock.hjson @@ -9,6 +9,6 @@ upstream: { url: https://github.com/lowRISC/opentitan - rev: da3ac7c4eb23a92194874ad2daf2e5f9e3330572 + rev: ad629e3e6e70c5eaa3c2dd68457b0a020448b35f } } diff --git a/vendor/lowrisc_ip/dv/sv/csr_utils/csr_seq_lib.sv b/vendor/lowrisc_ip/dv/sv/csr_utils/csr_seq_lib.sv index 203e2d75..2140c625 100644 --- a/vendor/lowrisc_ip/dv/sv/csr_utils/csr_seq_lib.sv +++ b/vendor/lowrisc_ip/dv/sv/csr_utils/csr_seq_lib.sv @@ -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(); diff --git a/vendor/lowrisc_ip/dv/sv/csr_utils/csr_utils_pkg.sv b/vendor/lowrisc_ip/dv/sv/csr_utils/csr_utils_pkg.sv index 70dca5c8..31e45b92 100644 --- a/vendor/lowrisc_ip/dv/sv/csr_utils/csr_utils_pkg.sv +++ b/vendor/lowrisc_ip/dv/sv/csr_utils/csr_utils_pkg.sv @@ -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; diff --git a/vendor/lowrisc_ip/dv/sv/dv_base_reg/dv_base_reg.sv b/vendor/lowrisc_ip/dv/sv/dv_base_reg/dv_base_reg.sv index 8a2b885b..91f59c58 100644 --- a/vendor/lowrisc_ip/dv/sv/dv_base_reg/dv_base_reg.sv +++ b/vendor/lowrisc_ip/dv/sv/dv_base_reg/dv_base_reg.sv @@ -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 diff --git a/vendor/lowrisc_ip/dv/sv/dv_base_reg/dv_base_reg_block.sv b/vendor/lowrisc_ip/dv/sv/dv_base_reg/dv_base_reg_block.sv index 8538ec33..ff63ee4d 100644 --- a/vendor/lowrisc_ip/dv/sv/dv_base_reg/dv_base_reg_block.sv +++ b/vendor/lowrisc_ip/dv/sv/dv_base_reg/dv_base_reg_block.sv @@ -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 diff --git a/vendor/lowrisc_ip/dv/sv/dv_base_reg/dv_base_reg_field.sv b/vendor/lowrisc_ip/dv/sv/dv_base_reg/dv_base_reg_field.sv index 8886a67c..e4103aa3 100644 --- a/vendor/lowrisc_ip/dv/sv/dv_base_reg/dv_base_reg_field.sv +++ b/vendor/lowrisc_ip/dv/sv/dv_base_reg/dv_base_reg_field.sv @@ -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 diff --git a/vendor/lowrisc_ip/dv/sv/dv_base_reg/dv_base_reg_pkg.sv b/vendor/lowrisc_ip/dv/sv/dv_base_reg/dv_base_reg_pkg.sv index ca543c5a..71a43ddc 100644 --- a/vendor/lowrisc_ip/dv/sv/dv_base_reg/dv_base_reg_pkg.sv +++ b/vendor/lowrisc_ip/dv/sv/dv_base_reg/dv_base_reg_pkg.sv @@ -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 diff --git a/vendor/lowrisc_ip/dv/sv/dv_lib/dv_base_env_cfg.sv b/vendor/lowrisc_ip/dv/sv/dv_lib/dv_base_env_cfg.sv index 7d0fe2eb..dc77b9a6 100644 --- a/vendor/lowrisc_ip/dv/sv/dv_lib/dv_base_env_cfg.sv +++ b/vendor/lowrisc_ip/dv/sv/dv_lib/dv_base_env_cfg.sv @@ -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 diff --git a/vendor/lowrisc_ip/dv/sv/dv_lib/dv_base_vseq.sv b/vendor/lowrisc_ip/dv/sv/dv_lib/dv_base_vseq.sv index a4946b05..7206e84b 100644 --- a/vendor/lowrisc_ip/dv/sv/dv_lib/dv_base_vseq.sv +++ b/vendor/lowrisc_ip/dv/sv/dv_lib/dv_base_vseq.sv @@ -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); diff --git a/vendor/lowrisc_ip/dv/sv/dv_utils/dv_macros.svh b/vendor/lowrisc_ip/dv/sv/dv_utils/dv_macros.svh index ccd513b2..f0ce29df 100644 --- a/vendor/lowrisc_ip/dv/sv/dv_utils/dv_macros.svh +++ b/vendor/lowrisc_ip/dv/sv/dv_utils/dv_macros.svh @@ -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 diff --git a/vendor/lowrisc_ip/dv/sv/dv_utils/dv_utils_pkg.sv b/vendor/lowrisc_ip/dv/sv/dv_utils/dv_utils_pkg.sv index 800461a0..c88fd4f6 100644 --- a/vendor/lowrisc_ip/dv/sv/dv_utils/dv_utils_pkg.sv +++ b/vendor/lowrisc_ip/dv/sv/dv_utils/dv_utils_pkg.sv @@ -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 diff --git a/vendor/lowrisc_ip/dv/tools/dvsim/common_sim_cfg.hjson b/vendor/lowrisc_ip/dv/tools/dvsim/common_sim_cfg.hjson index 6d7a44cd..d9d982e2 100644 --- a/vendor/lowrisc_ip/dv/tools/dvsim/common_sim_cfg.hjson +++ b/vendor/lowrisc_ip/dv/tools/dvsim/common_sim_cfg.hjson @@ -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" } diff --git a/vendor/lowrisc_ip/dv/tools/dvsim/dsim.hjson b/vendor/lowrisc_ip/dv/tools/dvsim/dsim.hjson index 44f4ab01..198681fd 100644 --- a/vendor/lowrisc_ip/dv/tools/dvsim/dsim.hjson +++ b/vendor/lowrisc_ip/dv/tools/dvsim/dsim.hjson @@ -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"] diff --git a/vendor/lowrisc_ip/dv/tools/dvsim/fusesoc.hjson b/vendor/lowrisc_ip/dv/tools/dvsim/fusesoc.hjson index dd72506b..c085052b 100644 --- a/vendor/lowrisc_ip/dv/tools/dvsim/fusesoc.hjson +++ b/vendor/lowrisc_ip/dv/tools/dvsim/fusesoc.hjson @@ -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}"] } diff --git a/vendor/lowrisc_ip/dv/tools/dvsim/testplans/sec_cm_testplan.hjson b/vendor/lowrisc_ip/dv/tools/dvsim/testplans/sec_cm_testplan.hjson new file mode 100644 index 00000000..a2838871 --- /dev/null +++ b/vendor/lowrisc_ip/dv/tools/dvsim/testplans/sec_cm_testplan.hjson @@ -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"] + } + ] +} + diff --git a/vendor/lowrisc_ip/dv/tools/dvsim/testplans/shadow_reg_errors_testplan.hjson b/vendor/lowrisc_ip/dv/tools/dvsim/testplans/shadow_reg_errors_testplan.hjson index 0e6ca1f2..0afc9340 100644 --- a/vendor/lowrisc_ip/dv/tools/dvsim/testplans/shadow_reg_errors_testplan.hjson +++ b/vendor/lowrisc_ip/dv/tools/dvsim/testplans/shadow_reg_errors_testplan.hjson @@ -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 diff --git a/vendor/lowrisc_ip/dv/tools/dvsim/testplans/stress_all_with_reset_testplan.hjson b/vendor/lowrisc_ip/dv/tools/dvsim/testplans/stress_all_with_reset_testplan.hjson index 10f7e123..c67acacf 100644 --- a/vendor/lowrisc_ip/dv/tools/dvsim/testplans/stress_all_with_reset_testplan.hjson +++ b/vendor/lowrisc_ip/dv/tools/dvsim/testplans/stress_all_with_reset_testplan.hjson @@ -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"] } ] diff --git a/vendor/lowrisc_ip/dv/tools/dvsim/testplans/tl_device_access_types_testplan.hjson b/vendor/lowrisc_ip/dv/tools/dvsim/testplans/tl_device_access_types_testplan.hjson index b471c112..4d87064c 100644 --- a/vendor/lowrisc_ip/dv/tools/dvsim/testplans/tl_device_access_types_testplan.hjson +++ b/vendor/lowrisc_ip/dv/tools/dvsim/testplans/tl_device_access_types_testplan.hjson @@ -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. + ''' + } + ] } diff --git a/vendor/lowrisc_ip/dv/tools/dvsim/tests/csr_tests.hjson b/vendor/lowrisc_ip/dv/tools/dvsim/tests/csr_tests.hjson index 93e6e010..2f17aec2 100644 --- a/vendor/lowrisc_ip/dv/tools/dvsim/tests/csr_tests.hjson +++ b/vendor/lowrisc_ip/dv/tools/dvsim/tests/csr_tests.hjson @@ -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 } ] diff --git a/vendor/lowrisc_ip/dv/tools/dvsim/tests/mem_tests.hjson b/vendor/lowrisc_ip/dv/tools/dvsim/tests/mem_tests.hjson index 65210280..b3760144 100644 --- a/vendor/lowrisc_ip/dv/tools/dvsim/tests/mem_tests.hjson +++ b/vendor/lowrisc_ip/dv/tools/dvsim/tests/mem_tests.hjson @@ -13,6 +13,7 @@ name: mem_tests_mode uvm_test_seq: "{name}_common_vseq" run_opts: ["+en_scb=0"] + reseed: 5 } ] diff --git a/vendor/lowrisc_ip/dv/tools/dvsim/tests/tl_access_tests.hjson b/vendor/lowrisc_ip/dv/tools/dvsim/tests/tl_access_tests.hjson index 28aeaa37..af3e1d04 100644 --- a/vendor/lowrisc_ip/dv/tools/dvsim/tests/tl_access_tests.hjson +++ b/vendor/lowrisc_ip/dv/tools/dvsim/tests/tl_access_tests.hjson @@ -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 } ] } diff --git a/vendor/lowrisc_ip/dv/tools/dvsim/vcs.hjson b/vendor/lowrisc_ip/dv/tools/dvsim/vcs.hjson index 04908a05..2c02cfa6 100644 --- a/vendor/lowrisc_ip/dv/tools/dvsim/vcs.hjson +++ b/vendor/lowrisc_ip/dv/tools/dvsim/vcs.hjson @@ -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 diff --git a/vendor/lowrisc_ip/dv/tools/dvsim/xcelium.hjson b/vendor/lowrisc_ip/dv/tools/dvsim/xcelium.hjson index 5c43d931..fe20faa1 100644 --- a/vendor/lowrisc_ip/dv/tools/dvsim/xcelium.hjson +++ b/vendor/lowrisc_ip/dv/tools/dvsim/xcelium.hjson @@ -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 . xcelium_cov_cfg_file: "" - // Supply the cov refinement files. - // Note that this needs to be set as -load_refinement . + // 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. diff --git a/vendor/lowrisc_ip/dv/tools/vcs/common_cov_excl.cfg b/vendor/lowrisc_ip/dv/tools/vcs/common_cov_excl.cfg new file mode 100644 index 00000000..175b6b3d --- /dev/null +++ b/vendor/lowrisc_ip/dv/tools/vcs/common_cov_excl.cfg @@ -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_* diff --git a/vendor/lowrisc_ip/dv/tools/vcs/common_cov_excl.el b/vendor/lowrisc_ip/dv/tools/vcs/common_cov_excl.el deleted file mode 100644 index 14765357..00000000 --- a/vendor/lowrisc_ip/dv/tools/vcs/common_cov_excl.el +++ /dev/null @@ -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]" diff --git a/vendor/lowrisc_ip/dv/tools/vcs/cover.cfg b/vendor/lowrisc_ip/dv/tools/vcs/cover.cfg index 89a44b99..ddee4d34 100644 --- a/vendor/lowrisc_ip/dv/tools/vcs/cover.cfg +++ b/vendor/lowrisc_ip/dv/tools/vcs/cover.cfg @@ -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 diff --git a/vendor/lowrisc_ip/dv/tools/vcs/cover_reg_top.cfg b/vendor/lowrisc_ip/dv/tools/vcs/cover_reg_top.cfg index 8cc1df68..de68401a 100644 --- a/vendor/lowrisc_ip/dv/tools/vcs/cover_reg_top.cfg +++ b/vendor/lowrisc_ip/dv/tools/vcs/cover_reg_top.cfg @@ -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 diff --git a/vendor/lowrisc_ip/dv/tools/vcs/unr.cfg b/vendor/lowrisc_ip/dv/tools/vcs/unr.cfg index 5a38a03a..0f7d0108 100644 --- a/vendor/lowrisc_ip/dv/tools/vcs/unr.cfg +++ b/vendor/lowrisc_ip/dv/tools/vcs/unr.cfg @@ -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 diff --git a/vendor/lowrisc_ip/dv/tools/xcelium/common.ccf b/vendor/lowrisc_ip/dv/tools/xcelium/common.ccf new file mode 100644 index 00000000..2cfa7ba5 --- /dev/null +++ b/vendor/lowrisc_ip/dv/tools/xcelium/common.ccf @@ -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 diff --git a/vendor/lowrisc_ip/dv/tools/xcelium/common_cov_excl.tcl b/vendor/lowrisc_ip/dv/tools/xcelium/common_cov_excl.tcl new file mode 100644 index 00000000..c32f9f15 --- /dev/null +++ b/vendor/lowrisc_ip/dv/tools/xcelium/common_cov_excl.tcl @@ -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." diff --git a/vendor/lowrisc_ip/dv/tools/xcelium/cov_merge.tcl b/vendor/lowrisc_ip/dv/tools/xcelium/cov_merge.tcl index e2b80583..f48356c0 100644 --- a/vendor/lowrisc_ip/dv/tools/xcelium/cov_merge.tcl +++ b/vendor/lowrisc_ip/dv/tools/xcelium/cov_merge.tcl @@ -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 diff --git a/vendor/lowrisc_ip/dv/tools/xcelium/cov_report.tcl b/vendor/lowrisc_ip/dv/tools/xcelium/cov_report.tcl index a6184e94..975e9336 100644 --- a/vendor/lowrisc_ip/dv/tools/xcelium/cov_report.tcl +++ b/vendor/lowrisc_ip/dv/tools/xcelium/cov_report.tcl @@ -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 diff --git a/vendor/lowrisc_ip/dv/tools/xcelium/cover.ccf b/vendor/lowrisc_ip/dv/tools/xcelium/cover.ccf new file mode 100644 index 00000000..a1393f41 --- /dev/null +++ b/vendor/lowrisc_ip/dv/tools/xcelium/cover.ccf @@ -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 diff --git a/vendor/lowrisc_ip/dv/tools/xcelium/cover_reg_top.ccf b/vendor/lowrisc_ip/dv/tools/xcelium/cover_reg_top.ccf new file mode 100644 index 00000000..d0fcf808 --- /dev/null +++ b/vendor/lowrisc_ip/dv/tools/xcelium/cover_reg_top.ccf @@ -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 diff --git a/vendor/lowrisc_ip/dv/tools/xcelium/cover_reg_top_toggle_excl b/vendor/lowrisc_ip/dv/tools/xcelium/cover_reg_top_toggle_excl new file mode 100644 index 00000000..ae30c553 --- /dev/null +++ b/vendor/lowrisc_ip/dv/tools/xcelium/cover_reg_top_toggle_excl @@ -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$).)*$ diff --git a/vendor/lowrisc_ip/dv/tools/xcelium/exclude.tcl b/vendor/lowrisc_ip/dv/tools/xcelium/exclude.tcl deleted file mode 100644 index 925c9757..00000000 --- a/vendor/lowrisc_ip/dv/tools/xcelium/exclude.tcl +++ /dev/null @@ -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' diff --git a/vendor/lowrisc_ip/dv/tools/xcelium/xcelium.ccf b/vendor/lowrisc_ip/dv/tools/xcelium/xcelium.ccf deleted file mode 100644 index 9e6afed5..00000000 --- a/vendor/lowrisc_ip/dv/tools/xcelium/xcelium.ccf +++ /dev/null @@ -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 diff --git a/vendor/lowrisc_ip/dv/verilator/cpp/dpi_memutil.cc b/vendor/lowrisc_ip/dv/verilator/cpp/dpi_memutil.cc index c57a3eec..ab14462d 100644 --- a/vendor/lowrisc_ip/dv/verilator/cpp/dpi_memutil.cc +++ b/vendor/lowrisc_ip/dv/verilator/cpp/dpi_memutil.cc @@ -146,7 +146,7 @@ static std::vector FlattenElfFile(const std::string &filepath) { continue; } - if (phdr.p_memsz == 0) { + if (phdr.p_filesz == 0) { continue; } @@ -154,11 +154,11 @@ static std::vector 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 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 seg(dst_len, 0); - memcpy(&seg[0], file_data + phdr.p_offset, src_len); + uint32_t off = phdr.p_paddr - low; + std::vector 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 vec(phdr.p_memsz, 0); - memcpy(&vec[0], seg_data, std::min(phdr.p_filesz, phdr.p_memsz)); + std::vector vec(phdr.p_filesz, 0); + memcpy(&vec[0], seg_data, phdr.p_filesz); staged_mem.AddSegment(local_base, std::move(vec)); } diff --git a/vendor/lowrisc_ip/dv/verilator/cpp/mem_area.h b/vendor/lowrisc_ip/dv/verilator/cpp/mem_area.h index b73d4310..f1c8a6be 100644 --- a/vendor/lowrisc_ip/dv/verilator/cpp/mem_area.h +++ b/vendor/lowrisc_ip/dv/verilator/cpp/mem_area.h @@ -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. diff --git a/vendor/lowrisc_ip/ip/prim/data/prim_mubi_pkg.sv.tpl b/vendor/lowrisc_ip/ip/prim/data/prim_mubi_pkg.sv.tpl new file mode 100644 index 00000000..b3977d34 --- /dev/null +++ b/vendor/lowrisc_ip/ip/prim/data/prim_mubi_pkg.sv.tpl @@ -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 diff --git a/vendor/lowrisc_ip/ip/prim/doc/prim_ram_1p_scr.md b/vendor/lowrisc_ip/ip/prim/doc/prim_ram_1p_scr.md new file mode 100644 index 00000000..0da475eb --- /dev/null +++ b/vendor/lowrisc_ip/ip/prim/doc/prim_ram_1p_scr.md @@ -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; +} + +``` diff --git a/vendor/lowrisc_ip/ip/prim/dv/prim_alert/data/prim_alert_cover.cfg b/vendor/lowrisc_ip/ip/prim/dv/prim_alert/data/prim_alert_cover.cfg new file mode 100644 index 00000000..597ed647 --- /dev/null +++ b/vendor/lowrisc_ip/ip/prim/dv/prim_alert/data/prim_alert_cover.cfg @@ -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 diff --git a/vendor/lowrisc_ip/ip/prim/dv/prim_alert/data/prim_alert_testplan.hjson b/vendor/lowrisc_ip/ip/prim/dv/prim_alert/data/prim_alert_testplan.hjson new file mode 100644 index 00000000..009d6c6a --- /dev/null +++ b/vendor/lowrisc_ip/ip/prim/dv/prim_alert/data/prim_alert_testplan.hjson @@ -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"] + } + + ] +} diff --git a/vendor/lowrisc_ip/ip/prim/dv/prim_alert/prim_alert_sim.core b/vendor/lowrisc_ip/ip/prim/dv/prim_alert/prim_alert_sim.core new file mode 100644 index 00000000..19676375 --- /dev/null +++ b/vendor/lowrisc_ip/ip/prim/dv/prim_alert/prim_alert_sim.core @@ -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 + diff --git a/vendor/lowrisc_ip/ip/prim/dv/prim_alert/prim_alert_sim_cfg.hjson b/vendor/lowrisc_ip/ip/prim/dv/prim_alert/prim_alert_sim_cfg.hjson new file mode 100644 index 00000000..dc450dab --- /dev/null +++ b/vendor/lowrisc_ip/ip/prim/dv/prim_alert/prim_alert_sim_cfg.hjson @@ -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" + } + ] +} diff --git a/vendor/lowrisc_ip/ip/prim/dv/prim_alert/tb/prim_alert_tb.sv b/vendor/lowrisc_ip/ip/prim/dv/prim_alert/tb/prim_alert_tb.sv new file mode 100644 index 00000000..be140a2d --- /dev/null +++ b/vendor/lowrisc_ip/ip/prim/dv/prim_alert/tb/prim_alert_tb.sv @@ -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 diff --git a/vendor/lowrisc_ip/ip/prim/dv/prim_esc/data/prim_esc_cover.cfg b/vendor/lowrisc_ip/ip/prim/dv/prim_esc/data/prim_esc_cover.cfg new file mode 100644 index 00000000..38463867 --- /dev/null +++ b/vendor/lowrisc_ip/ip/prim/dv/prim_esc/data/prim_esc_cover.cfg @@ -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 diff --git a/vendor/lowrisc_ip/ip/prim/dv/prim_esc/data/prim_esc_testplan.hjson b/vendor/lowrisc_ip/ip/prim/dv/prim_esc/data/prim_esc_testplan.hjson new file mode 100644 index 00000000..9e5cbaaa --- /dev/null +++ b/vendor/lowrisc_ip/ip/prim/dv/prim_esc/data/prim_esc_testplan.hjson @@ -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"] + } + + ] +} diff --git a/vendor/lowrisc_ip/ip/prim/dv/prim_esc/prim_esc_sim.core b/vendor/lowrisc_ip/ip/prim/dv/prim_esc/prim_esc_sim.core new file mode 100644 index 00000000..ef00ab6f --- /dev/null +++ b/vendor/lowrisc_ip/ip/prim/dv/prim_esc/prim_esc_sim.core @@ -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 diff --git a/vendor/lowrisc_ip/ip/prim/dv/prim_esc/prim_esc_sim_cfg.hjson b/vendor/lowrisc_ip/ip/prim/dv/prim_esc/prim_esc_sim_cfg.hjson new file mode 100644 index 00000000..8c844dad --- /dev/null +++ b/vendor/lowrisc_ip/ip/prim/dv/prim_esc/prim_esc_sim_cfg.hjson @@ -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" + } + ] +} diff --git a/vendor/lowrisc_ip/ip/prim/dv/prim_esc/tb/prim_esc_tb.sv b/vendor/lowrisc_ip/ip/prim/dv/prim_esc/tb/prim_esc_tb.sv new file mode 100644 index 00000000..cd867694 --- /dev/null +++ b/vendor/lowrisc_ip/ip/prim/dv/prim_esc/tb/prim_esc_tb.sv @@ -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 diff --git a/vendor/lowrisc_ip/ip/prim/dv/prim_prince/crypto_dpi_prince/prince_ref.h b/vendor/lowrisc_ip/ip/prim/dv/prim_prince/crypto_dpi_prince/prince_ref.h index 3491053e..32c8e1a2 100644 --- a/vendor/lowrisc_ip/ip/prim/dv/prim_prince/crypto_dpi_prince/prince_ref.h +++ b/vendor/lowrisc_ip/ip/prim/dv/prim_prince/crypto_dpi_prince/prince_ref.h @@ -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]; diff --git a/vendor/lowrisc_ip/ip/prim/dv/prim_prince/tb/prim_prince_tb.sv b/vendor/lowrisc_ip/ip/prim/dv/prim_prince/tb/prim_prince_tb.sv index 9f18f551..654b34a1 100644 --- a/vendor/lowrisc_ip/ip/prim/dv/prim_prince/tb/prim_prince_tb.sv +++ b/vendor/lowrisc_ip/ip/prim/dv/prim_prince/tb/prim_prince_tb.sv @@ -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. diff --git a/vendor/lowrisc_ip/ip/prim/fpv/prim_esc_rxtx_fpv.core b/vendor/lowrisc_ip/ip/prim/fpv/prim_esc_rxtx_fpv.core index 31f422d1..80b55621 100644 --- a/vendor/lowrisc_ip/ip/prim/fpv/prim_esc_rxtx_fpv.core +++ b/vendor/lowrisc_ip/ip/prim/fpv/prim_esc_rxtx_fpv.core @@ -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: diff --git a/vendor/lowrisc_ip/ip/prim/fpv/prim_fifo_async_sram_adapter_fpv.core b/vendor/lowrisc_ip/ip/prim/fpv/prim_fifo_async_sram_adapter_fpv.core new file mode 100644 index 00000000..1b546d83 --- /dev/null +++ b/vendor/lowrisc_ip/ip/prim/fpv/prim_fifo_async_sram_adapter_fpv.core @@ -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 diff --git a/vendor/lowrisc_ip/ip/prim/fpv/prim_packer_fpv.core b/vendor/lowrisc_ip/ip/prim/fpv/prim_packer_fpv.core index c1f882b2..73c521ed 100644 --- a/vendor/lowrisc_ip/ip/prim/fpv/prim_packer_fpv.core +++ b/vendor/lowrisc_ip/ip/prim/fpv/prim_packer_fpv.core @@ -10,7 +10,6 @@ filesets: depend: - lowrisc:prim:all files: - - ../rtl/prim_packer.sv - tb/prim_packer_fpv.sv file_type: systemVerilogSource diff --git a/vendor/lowrisc_ip/ip/prim/fpv/tb/prim_alert_rxtx_async_bind_fpv.sv b/vendor/lowrisc_ip/ip/prim/fpv/tb/prim_alert_rxtx_async_bind_fpv.sv index b5471fc9..db33bf28 100644 --- a/vendor/lowrisc_ip/ip/prim/fpv/tb/prim_alert_rxtx_async_bind_fpv.sv +++ b/vendor/lowrisc_ip/ip/prim/fpv/tb/prim_alert_rxtx_async_bind_fpv.sv @@ -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, diff --git a/vendor/lowrisc_ip/ip/prim/fpv/tb/prim_alert_rxtx_async_fatal_bind_fpv.sv b/vendor/lowrisc_ip/ip/prim/fpv/tb/prim_alert_rxtx_async_fatal_bind_fpv.sv index bc0d39a9..3a66f270 100644 --- a/vendor/lowrisc_ip/ip/prim/fpv/tb/prim_alert_rxtx_async_fatal_bind_fpv.sv +++ b/vendor/lowrisc_ip/ip/prim/fpv/tb/prim_alert_rxtx_async_fatal_bind_fpv.sv @@ -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, diff --git a/vendor/lowrisc_ip/ip/prim/fpv/tb/prim_alert_rxtx_async_fatal_fpv.sv b/vendor/lowrisc_ip/ip/prim/fpv/tb/prim_alert_rxtx_async_fatal_fpv.sv index fb7aa879..e5b7f43e 100644 --- a/vendor/lowrisc_ip/ip/prim/fpv/tb/prim_alert_rxtx_async_fatal_fpv.sv +++ b/vendor/lowrisc_ip/ip/prim/fpv/tb/prim_alert_rxtx_async_fatal_fpv.sv @@ -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 ) ); diff --git a/vendor/lowrisc_ip/ip/prim/fpv/tb/prim_alert_rxtx_async_fpv.sv b/vendor/lowrisc_ip/ip/prim/fpv/tb/prim_alert_rxtx_async_fpv.sv index 30a1b105..c1e229c8 100644 --- a/vendor/lowrisc_ip/ip/prim/fpv/tb/prim_alert_rxtx_async_fpv.sv +++ b/vendor/lowrisc_ip/ip/prim/fpv/tb/prim_alert_rxtx_async_fpv.sv @@ -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; diff --git a/vendor/lowrisc_ip/ip/prim/fpv/tb/prim_alert_rxtx_bind_fpv.sv b/vendor/lowrisc_ip/ip/prim/fpv/tb/prim_alert_rxtx_bind_fpv.sv index a3d3236c..f3ac2efd 100644 --- a/vendor/lowrisc_ip/ip/prim/fpv/tb/prim_alert_rxtx_bind_fpv.sv +++ b/vendor/lowrisc_ip/ip/prim/fpv/tb/prim_alert_rxtx_bind_fpv.sv @@ -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, diff --git a/vendor/lowrisc_ip/ip/prim/fpv/tb/prim_alert_rxtx_fatal_bind_fpv.sv b/vendor/lowrisc_ip/ip/prim/fpv/tb/prim_alert_rxtx_fatal_bind_fpv.sv index b02e57fe..c3f33b65 100644 --- a/vendor/lowrisc_ip/ip/prim/fpv/tb/prim_alert_rxtx_fatal_bind_fpv.sv +++ b/vendor/lowrisc_ip/ip/prim/fpv/tb/prim_alert_rxtx_fatal_bind_fpv.sv @@ -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, diff --git a/vendor/lowrisc_ip/ip/prim/fpv/tb/prim_alert_rxtx_fatal_fpv.sv b/vendor/lowrisc_ip/ip/prim/fpv/tb/prim_alert_rxtx_fatal_fpv.sv index e32e4e89..4e52cda6 100644 --- a/vendor/lowrisc_ip/ip/prim/fpv/tb/prim_alert_rxtx_fatal_fpv.sv +++ b/vendor/lowrisc_ip/ip/prim/fpv/tb/prim_alert_rxtx_fatal_fpv.sv @@ -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 ) ); diff --git a/vendor/lowrisc_ip/ip/prim/fpv/tb/prim_alert_rxtx_fpv.sv b/vendor/lowrisc_ip/ip/prim/fpv/tb/prim_alert_rxtx_fpv.sv index f029e2e1..16b42513 100644 --- a/vendor/lowrisc_ip/ip/prim/fpv/tb/prim_alert_rxtx_fpv.sv +++ b/vendor/lowrisc_ip/ip/prim/fpv/tb/prim_alert_rxtx_fpv.sv @@ -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 ) ); diff --git a/vendor/lowrisc_ip/ip/prim/fpv/tb/prim_esc_rxtx_bind_fpv.sv b/vendor/lowrisc_ip/ip/prim/fpv/tb/prim_esc_rxtx_bind_fpv.sv index 1657b8de..80c47bbd 100644 --- a/vendor/lowrisc_ip/ip/prim/fpv/tb/prim_esc_rxtx_bind_fpv.sv +++ b/vendor/lowrisc_ip/ip/prim/fpv/tb/prim_esc_rxtx_bind_fpv.sv @@ -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 diff --git a/vendor/lowrisc_ip/ip/prim/fpv/tb/prim_esc_rxtx_fpv.sv b/vendor/lowrisc_ip/ip/prim/fpv/tb/prim_esc_rxtx_fpv.sv index df9999b1..c9a17d4a 100644 --- a/vendor/lowrisc_ip/ip/prim/fpv/tb/prim_esc_rxtx_fpv.sv +++ b/vendor/lowrisc_ip/ip/prim/fpv/tb/prim_esc_rxtx_fpv.sv @@ -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 ) ); diff --git a/vendor/lowrisc_ip/ip/prim/fpv/tb/prim_fifo_async_sram_adapter_fpv.sv b/vendor/lowrisc_ip/ip/prim/fpv/tb/prim_fifo_async_sram_adapter_fpv.sv new file mode 100644 index 00000000..fb3dda98 --- /dev/null +++ b/vendor/lowrisc_ip/ip/prim/fpv/tb/prim_fifo_async_sram_adapter_fpv.sv @@ -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 diff --git a/vendor/lowrisc_ip/ip/prim/fpv/tb/prim_lfsr_fpv.sv b/vendor/lowrisc_ip/ip/prim/fpv/tb/prim_lfsr_fpv.sv index b747eddc..0e3e9a6d 100644 --- a/vendor/lowrisc_ip/ip/prim/fpv/tb/prim_lfsr_fpv.sv +++ b/vendor/lowrisc_ip/ip/prim/fpv/tb/prim_lfsr_fpv.sv @@ -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 diff --git a/vendor/lowrisc_ip/ip/prim/fpv/vip/prim_alert_rxtx_assert_fpv.sv b/vendor/lowrisc_ip/ip/prim/fpv/vip/prim_alert_rxtx_assert_fpv.sv index f0e5b009..5bd7ebe2 100644 --- a/vendor/lowrisc_ip/ip/prim/fpv/vip/prim_alert_rxtx_assert_fpv.sv +++ b/vendor/lowrisc_ip/ip/prim/fpv/vip/prim_alert_rxtx_assert_fpv.sv @@ -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 diff --git a/vendor/lowrisc_ip/ip/prim/fpv/vip/prim_alert_rxtx_async_assert_fpv.sv b/vendor/lowrisc_ip/ip/prim/fpv/vip/prim_alert_rxtx_async_assert_fpv.sv index 568bfa0c..23a58ca4 100644 --- a/vendor/lowrisc_ip/ip/prim/fpv/vip/prim_alert_rxtx_async_assert_fpv.sv +++ b/vendor/lowrisc_ip/ip/prim/fpv/vip/prim_alert_rxtx_async_assert_fpv.sv @@ -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 diff --git a/vendor/lowrisc_ip/ip/prim/fpv/vip/prim_esc_rxtx_assert_fpv.sv b/vendor/lowrisc_ip/ip/prim/fpv/vip/prim_esc_rxtx_assert_fpv.sv index 40a1547f..165f85ee 100644 --- a/vendor/lowrisc_ip/ip/prim/fpv/vip/prim_esc_rxtx_assert_fpv.sv +++ b/vendor/lowrisc_ip/ip/prim/fpv/vip/prim_esc_rxtx_assert_fpv.sv @@ -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 || diff --git a/vendor/lowrisc_ip/ip/prim/fpv/vip/prim_fifo_sync_assert_fpv.sv b/vendor/lowrisc_ip/ip/prim/fpv/vip/prim_fifo_sync_assert_fpv.sv index e3fa4e16..8cd7a3f4 100644 --- a/vendor/lowrisc_ip/ip/prim/fpv/vip/prim_fifo_sync_assert_fpv.sv +++ b/vendor/lowrisc_ip/ip/prim/fpv/vip/prim_fifo_sync_assert_fpv.sv @@ -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 diff --git a/vendor/lowrisc_ip/ip/prim/lint/prim.waiver b/vendor/lowrisc_ip/ip/prim/lint/prim.waiver index bdcc3658..4365fb74 100644 --- a/vendor/lowrisc_ip/ip/prim/lint/prim.waiver +++ b/vendor/lowrisc_ip/ip/prim/lint/prim.waiver @@ -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" diff --git a/vendor/lowrisc_ip/ip/prim/lint/prim_arbiter.waiver b/vendor/lowrisc_ip/ip/prim/lint/prim_arbiter.waiver index f1bf4c21..278740ca 100644 --- a/vendor/lowrisc_ip/ip/prim/lint/prim_arbiter.waiver +++ b/vendor/lowrisc_ip/ip/prim/lint/prim_arbiter.waiver @@ -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." diff --git a/vendor/lowrisc_ip/ip/prim/lint/prim_assert.vlt b/vendor/lowrisc_ip/ip/prim/lint/prim_assert.vlt new file mode 100644 index 00000000..affe7509 --- /dev/null +++ b/vendor/lowrisc_ip/ip/prim/lint/prim_assert.vlt @@ -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 diff --git a/vendor/lowrisc_ip/ip/prim/lint/prim_assert.waiver b/vendor/lowrisc_ip/ip/prim/lint/prim_assert.waiver new file mode 100644 index 00000000..8ae9e4a2 --- /dev/null +++ b/vendor/lowrisc_ip/ip/prim/lint/prim_assert.waiver @@ -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." diff --git a/vendor/lowrisc_ip/ip/prim/lint/prim_fifo.waiver b/vendor/lowrisc_ip/ip/prim/lint/prim_fifo.waiver index 6c5dc98a..bf1bb227 100644 --- a/vendor/lowrisc_ip/ip/prim/lint/prim_fifo.waiver +++ b/vendor/lowrisc_ip/ip/prim/lint/prim_fifo.waiver @@ -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" diff --git a/vendor/lowrisc_ip/ip/prim/lint/prim_xor2.waiver b/vendor/lowrisc_ip/ip/prim/lint/prim_xor2.waiver new file mode 100644 index 00000000..c2625ae0 --- /dev/null +++ b/vendor/lowrisc_ip/ip/prim/lint/prim_xor2.waiver @@ -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." diff --git a/vendor/lowrisc_ip/ip/prim/prim_alert.core b/vendor/lowrisc_ip/ip/prim/prim_alert.core index e1befe3d..b748bdb4 100644 --- a/vendor/lowrisc_ip/ip/prim/prim_alert.core +++ b/vendor/lowrisc_ip/ip/prim/prim_alert.core @@ -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 diff --git a/vendor/lowrisc_ip/ip/prim/prim_assert.core b/vendor/lowrisc_ip/ip/prim/prim_assert.core index 3eec05b6..837977ec 100644 --- a/vendor/lowrisc_ip/ip/prim/prim_assert.core +++ b/vendor/lowrisc_ip/ip/prim/prim_assert.core @@ -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 diff --git a/vendor/lowrisc_ip/ip/prim/prim_clock_meas.core b/vendor/lowrisc_ip/ip/prim/prim_clock_meas.core new file mode 100644 index 00000000..67bb3f7d --- /dev/null +++ b/vendor/lowrisc_ip/ip/prim/prim_clock_meas.core @@ -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 diff --git a/vendor/lowrisc_ip/ip/prim/prim_count.core b/vendor/lowrisc_ip/ip/prim/prim_count.core new file mode 100644 index 00000000..3b6a2e22 --- /dev/null +++ b/vendor/lowrisc_ip/ip/prim/prim_count.core @@ -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 diff --git a/vendor/lowrisc_ip/ip/prim/prim_count_pkg.core b/vendor/lowrisc_ip/ip/prim/prim_count_pkg.core new file mode 100644 index 00000000..d4d515d4 --- /dev/null +++ b/vendor/lowrisc_ip/ip/prim/prim_count_pkg.core @@ -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 diff --git a/vendor/lowrisc_ip/ip/prim/prim_diff_decode.core b/vendor/lowrisc_ip/ip/prim/prim_diff_decode.core index eda0fe7c..a91367f7 100644 --- a/vendor/lowrisc_ip/ip/prim/prim_diff_decode.core +++ b/vendor/lowrisc_ip/ip/prim/prim_diff_decode.core @@ -9,6 +9,7 @@ filesets: files_rtl: depend: - lowrisc:prim:assert + - lowrisc:prim:flop_2sync files: - rtl/prim_diff_decode.sv file_type: systemVerilogSource diff --git a/vendor/lowrisc_ip/ip/prim/prim_edge_detector.core b/vendor/lowrisc_ip/ip/prim/prim_edge_detector.core new file mode 100644 index 00000000..1d6d52fb --- /dev/null +++ b/vendor/lowrisc_ip/ip/prim/prim_edge_detector.core @@ -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 diff --git a/vendor/lowrisc_ip/ip/prim/prim_fifo.core b/vendor/lowrisc_ip/ip/prim/prim_fifo.core index 169e247d..8f2b0776 100644 --- a/vendor/lowrisc_ip/ip/prim/prim_fifo.core +++ b/vendor/lowrisc_ip/ip/prim/prim_fifo.core @@ -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 diff --git a/vendor/lowrisc_ip/ip/prim/prim_lc_combine.core b/vendor/lowrisc_ip/ip/prim/prim_lc_combine.core new file mode 100644 index 00000000..1437edc2 --- /dev/null +++ b/vendor/lowrisc_ip/ip/prim/prim_lc_combine.core @@ -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" diff --git a/vendor/lowrisc_ip/ip/prim/prim_lfsr.core b/vendor/lowrisc_ip/ip/prim/prim_lfsr.core index 8d1c453d..70aec74a 100644 --- a/vendor/lowrisc_ip/ip/prim/prim_lfsr.core +++ b/vendor/lowrisc_ip/ip/prim/prim_lfsr.core @@ -9,6 +9,7 @@ filesets: files_rtl: depend: - lowrisc:prim:assert + - lowrisc:prim:cipher_pkg files: - rtl/prim_lfsr.sv file_type: systemVerilogSource diff --git a/vendor/lowrisc_ip/ip/prim/prim_mubi.core b/vendor/lowrisc_ip/ip/prim/prim_mubi.core new file mode 100644 index 00000000..f400a3e2 --- /dev/null +++ b/vendor/lowrisc_ip/ip/prim/prim_mubi.core @@ -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 diff --git a/vendor/lowrisc_ip/ip/prim/prim_subreg.core b/vendor/lowrisc_ip/ip/prim/prim_subreg.core index a2d4d686..f817fc74 100644 --- a/vendor/lowrisc_ip/ip/prim/prim_subreg.core +++ b/vendor/lowrisc_ip/ip/prim/prim_subreg.core @@ -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 diff --git a/vendor/lowrisc_ip/ip/prim/prim_xor2.core b/vendor/lowrisc_ip/ip/prim/prim_xor2.core index 83c459e3..5da73c79 100644 --- a/vendor/lowrisc_ip/ip/prim/prim_xor2.core +++ b/vendor/lowrisc_ip/ip/prim/prim_xor2.core @@ -22,6 +22,8 @@ filesets: depend: # common waivers - lowrisc:lint:common + files: + - lint/prim_xor2.waiver file_type: waiver files_veriblelint_waiver: diff --git a/vendor/lowrisc_ip/ip/prim/rtl/prim_alert_receiver.sv b/vendor/lowrisc_ip/ip/prim/rtl/prim_alert_receiver.sv index 338387c0..5420d15e 100644 --- a/vendor/lowrisc_ip/ip/prim/rtl/prim_alert_receiver.sv +++ b/vendor/lowrisc_ip/ip/prim/rtl/prim_alert_receiver.sv @@ -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 diff --git a/vendor/lowrisc_ip/ip/prim/rtl/prim_alert_sender.sv b/vendor/lowrisc_ip/ip/prim/rtl/prim_alert_sender.sv index f3d7d5bc..70bd9141 100644 --- a/vendor/lowrisc_ip/ip/prim/rtl/prim_alert_sender.sv +++ b/vendor/lowrisc_ip/ip/prim/rtl/prim_alert_sender.sv @@ -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 diff --git a/vendor/lowrisc_ip/ip/prim/rtl/prim_clock_meas.sv b/vendor/lowrisc_ip/ip/prim/rtl/prim_clock_meas.sv new file mode 100644 index 00000000..1c78eadb --- /dev/null +++ b/vendor/lowrisc_ip/ip/prim/rtl/prim_clock_meas.sv @@ -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 diff --git a/vendor/lowrisc_ip/ip/prim/rtl/prim_count.sv b/vendor/lowrisc_ip/ip/prim/rtl/prim_count.sv new file mode 100644 index 00000000..491d0130 --- /dev/null +++ b/vendor/lowrisc_ip/ip/prim/rtl/prim_count.sv @@ -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 diff --git a/vendor/lowrisc_ip/ip/prim/rtl/prim_count_pkg.sv b/vendor/lowrisc_ip/ip/prim/rtl/prim_count_pkg.sv new file mode 100644 index 00000000..f49a270c --- /dev/null +++ b/vendor/lowrisc_ip/ip/prim/rtl/prim_count_pkg.sv @@ -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 // diff --git a/vendor/lowrisc_ip/ip/prim/rtl/prim_diff_decode.sv b/vendor/lowrisc_ip/ip/prim/rtl/prim_diff_decode.sv index c06a77d7..ca040157 100644 --- a/vendor/lowrisc_ip/ip/prim/rtl/prim_diff_decode.sv +++ b/vendor/lowrisc_ip/ip/prim/rtl/prim_diff_decode.sv @@ -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 diff --git a/vendor/lowrisc_ip/ip/prim/rtl/prim_edge_detector.sv b/vendor/lowrisc_ip/ip/prim/rtl/prim_edge_detector.sv new file mode 100644 index 00000000..2b0a92fb --- /dev/null +++ b/vendor/lowrisc_ip/ip/prim/rtl/prim_edge_detector.sv @@ -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 diff --git a/vendor/lowrisc_ip/ip/prim/rtl/prim_edn_req.sv b/vendor/lowrisc_ip/ip/prim/rtl/prim_edn_req.sv index ad5b2ec2..77f57d44 100644 --- a/vendor/lowrisc_ip/ip/prim/rtl/prim_edn_req.sv +++ b/vendor/lowrisc_ip/ip/prim/rtl/prim_edn_req.sv @@ -16,7 +16,17 @@ module prim_edn_req import prim_alert_pkg::*; #( - parameter int OutWidth = 32 + parameter int OutWidth = 32, + + // EDN Request latency checker + // + // Each consumer IP may have the maximum expected latency. MaxLatency + // parameter describes the expected latency in terms of the consumer clock + // cycles. If the edn request comes later than that, the assertion will be + // fired. + // + // The default value is 0, which disables the assertion. + parameter int unsigned MaxLatency = 0 ) ( // Design side input clk_i, @@ -94,4 +104,41 @@ module prim_edn_req end assign fips_o = fips_q; + //////////////// + // Assertions // + //////////////// + + // EDN Max Latency Checker +`ifndef SYNTHESIS + if (MaxLatency != 0) begin: g_maxlatency_assertion + localparam int unsigned LatencyW = $clog2(MaxLatency+1); + logic [LatencyW-1:0] latency_counter; + logic reset_counter; + logic enable_counter; + + always_ff @(posedge clk_i or negedge rst_ni) begin + if (!rst_ni) latency_counter <= '0; + else if (reset_counter) latency_counter <= '0; + else if (enable_counter) latency_counter <= latency_counter + 1'b1; + end + + assign reset_counter = ack_o; + assign enable_counter = req_i; + + `ASSERT(MaxLatency_A, latency_counter <= MaxLatency) + + // TODO: Is it worth to check req & ack pair? + // _________________________________ + // req __/ \______ + // ____ + // ack ____________________________________/ \_ + // + // | error + + end // g_maxlatency_assertion +`else // SYNTHESIS + logic unused_param_maxlatency; + assign unused_param_maxlatency = ^MaxLatency; +`endif // SYNTHESIS + endmodule : prim_edn_req diff --git a/vendor/lowrisc_ip/ip/prim/rtl/prim_esc_receiver.sv b/vendor/lowrisc_ip/ip/prim/rtl/prim_esc_receiver.sv index 775c710c..f137e06c 100644 --- a/vendor/lowrisc_ip/ip/prim/rtl/prim_esc_receiver.sv +++ b/vendor/lowrisc_ip/ip/prim/rtl/prim_esc_receiver.sv @@ -49,7 +49,7 @@ module prim_esc_receiver input clk_i, input rst_ni, // escalation enable - output logic esc_en_o, + output logic esc_req_o, // escalation / ping response output esc_rx_t esc_rx_o, // escalation output diff pair @@ -90,7 +90,7 @@ module prim_esc_receiver // Ping Monitor Counter / Auto Escalation // //////////////////////////////////////////// - logic ping_en, esc_en; + logic ping_en, esc_req; logic [1:0][TimeoutCntDw-1:0] cnt_q; for (genvar k = 0; k < 2; k++) begin : gen_timeout_cnt @@ -117,9 +117,9 @@ module prim_esc_receiver // - requested via the escalation sender/receiver path, // - the ping monitor timeout is reached, // - the two ping monitor counters are in an inconsistent state. - assign esc_en_o = esc_en || - (&cnt_q[0]) || - (cnt_q[0] != cnt_q[1]); + assign esc_req_o = esc_req || + (&cnt_q[0]) || + (cnt_q[0] != cnt_q[1]); ///////////////// // RX/TX Logic // @@ -149,7 +149,7 @@ module prim_esc_receiver state_d = state_q; resp_pd = 1'b0; resp_nd = 1'b1; - esc_en = 1'b0; + esc_req = 1'b0; ping_en = 1'b0; unique case (state_q) @@ -157,29 +157,31 @@ module prim_esc_receiver Idle: begin if (esc_level) begin state_d = Check; - resp_pd = 1'b1; - resp_nd = 1'b0; + resp_pd = ~resp_pq; + resp_nd = resp_pq; end end // we decide here whether this is only a ping request or // whether this is an escalation enable Check: begin state_d = PingResp; + resp_pd = ~resp_pq; + resp_nd = resp_pq; if (esc_level) begin state_d = EscResp; - esc_en = 1'b1; + esc_req = 1'b1; end end // finish ping response. in case esc_level is again asserted, // we got an escalation signal (pings cannot occur back to back) PingResp: begin state_d = Idle; - resp_pd = 1'b1; - resp_nd = 1'b0; + resp_pd = ~resp_pq; + resp_nd = resp_pq; ping_en = 1'b1; if (esc_level) begin state_d = EscResp; - esc_en = 1'b1; + esc_req = 1'b1; end end // we have got an escalation enable pulse, @@ -190,7 +192,7 @@ module prim_esc_receiver state_d = EscResp; resp_pd = ~resp_pq; resp_nd = resp_pq; - esc_en = 1'b1; + esc_req = 1'b1; end end // we have a signal integrity issue at one of @@ -200,6 +202,7 @@ module prim_esc_receiver // toggling them. SigInt: begin state_d = Idle; + esc_req = 1'b1; if (sigint_detected) begin state_d = SigInt; resp_pd = ~resp_pq; @@ -235,18 +238,20 @@ module prim_esc_receiver //////////////// // check whether all outputs have a good known state after reset - `ASSERT_KNOWN(EscEnKnownO_A, esc_en_o) + `ASSERT_KNOWN(EscEnKnownO_A, esc_req_o) `ASSERT_KNOWN(RespPKnownO_A, esc_rx_o) `ASSERT(SigIntCheck0_A, esc_tx_i.esc_p == esc_tx_i.esc_n |=> - esc_rx_o.resp_p == esc_rx_o.resp_n, clk_i, !rst_ni) + esc_rx_o.resp_p == esc_rx_o.resp_n) `ASSERT(SigIntCheck1_A, esc_tx_i.esc_p == esc_tx_i.esc_n |=> state_q == SigInt) + // auto-escalate in case of signal integrity issue + `ASSERT(SigIntCheck2_A, esc_tx_i.esc_p == esc_tx_i.esc_n |=> esc_req_o) // correct diff encoding `ASSERT(DiffEncCheck_A, esc_tx_i.esc_p ^ esc_tx_i.esc_n |=> esc_rx_o.resp_p ^ esc_rx_o.resp_n) - // disable in case of ping integrity issue - `ASSERT(PingRespCheck_A, $rose(esc_tx_i.esc_p) |=> $fell(esc_tx_i.esc_p) |-> - $rose(esc_rx_o.resp_p) |=> $fell(esc_rx_o.resp_p), + // disable in case of signal integrity issue + `ASSERT(PingRespCheck_A, state_q == Idle ##1 $rose(esc_tx_i.esc_p) ##1 $fell(esc_tx_i.esc_p) |-> + $rose(esc_rx_o.resp_p) ##1 $fell(esc_rx_o.resp_p), clk_i, !rst_ni || (esc_tx_i.esc_p == esc_tx_i.esc_n)) // escalation response needs to continuously toggle `ASSERT(EscRespCheck_A, esc_tx_i.esc_p && $past(esc_tx_i.esc_p) && @@ -254,10 +259,10 @@ module prim_esc_receiver |=> esc_rx_o.resp_p != $past(esc_rx_o.resp_p)) // detect escalation pulse `ASSERT(EscEnCheck_A, esc_tx_i.esc_p && (esc_tx_i.esc_p ^ esc_tx_i.esc_n) && state_q != SigInt - |=> esc_tx_i.esc_p && (esc_tx_i.esc_p ^ esc_tx_i.esc_n) |-> esc_en_o) + ##1 esc_tx_i.esc_p && (esc_tx_i.esc_p ^ esc_tx_i.esc_n) |-> esc_req_o) // make sure the counter does not wrap around `ASSERT(EscCntWrap_A, &cnt_q[0] |=> cnt_q[0] != 0) // if the counter expires, escalation should be asserted - `ASSERT(EscCntEsc_A, &cnt_q[0] |-> esc_en_o) + `ASSERT(EscCntEsc_A, &cnt_q[0] |-> esc_req_o) endmodule : prim_esc_receiver diff --git a/vendor/lowrisc_ip/ip/prim/rtl/prim_esc_sender.sv b/vendor/lowrisc_ip/ip/prim/rtl/prim_esc_sender.sv index aae622ed..7fa354c1 100644 --- a/vendor/lowrisc_ip/ip/prim/rtl/prim_esc_sender.sv +++ b/vendor/lowrisc_ip/ip/prim/rtl/prim_esc_sender.sv @@ -114,7 +114,7 @@ module prim_esc_sender Idle: begin if (esc_req_i) begin state_d = CheckEscRespHi; - end else if (ping_req_i) begin + end else if (ping_req_d & ~ping_req_q) begin state_d = CheckPingResp0; end // any assertion of the response signal diff --git a/vendor/lowrisc_ip/ip/prim/rtl/prim_fifo_async.sv b/vendor/lowrisc_ip/ip/prim/rtl/prim_fifo_async.sv index 97fbc581..e2f2d07a 100644 --- a/vendor/lowrisc_ip/ip/prim/rtl/prim_fifo_async.sv +++ b/vendor/lowrisc_ip/ip/prim/rtl/prim_fifo_async.sv @@ -278,9 +278,9 @@ module prim_fifo_async #( end // TODO: assertions on full, empty - `ASSERT(GrayWptr_A, $countones(fifo_wptr_gray_q ^ $past(fifo_wptr_gray_q)) <= 1, + `ASSERT(GrayWptr_A, ##1 $countones(fifo_wptr_gray_q ^ $past(fifo_wptr_gray_q)) <= 1, clk_wr_i, !rst_wr_ni) - `ASSERT(GrayRptr_A, $countones(fifo_rptr_gray_q ^ $past(fifo_rptr_gray_q)) <= 1, + `ASSERT(GrayRptr_A, ##1 $countones(fifo_rptr_gray_q ^ $past(fifo_rptr_gray_q)) <= 1, clk_rd_i, !rst_rd_ni) endmodule diff --git a/vendor/lowrisc_ip/ip/prim/rtl/prim_fifo_async_sram_adapter.sv b/vendor/lowrisc_ip/ip/prim/rtl/prim_fifo_async_sram_adapter.sv new file mode 100644 index 00000000..4771ffde --- /dev/null +++ b/vendor/lowrisc_ip/ip/prim/rtl/prim_fifo_async_sram_adapter.sv @@ -0,0 +1,417 @@ +// Copyright lowRISC contributors. +// Licensed under the Apache License, Version 2.0, see LICENSE for details. +// SPDX-License-Identifier: Apache-2.0 +// +// Generic Asynchronous SRAM FIFO (Dual port SRAM) + +`include "prim_assert.sv" + +module prim_fifo_async_sram_adapter #( + parameter int unsigned Width = 32, + parameter int unsigned Depth = 16, + + // SRAM parameters + parameter int unsigned SramAw = 16, + + // If SramDw > Width, upper data bits are 0. + parameter int unsigned SramDw = 32, + parameter logic [SramAw-1:0] SramBaseAddr = 'h 0, + + // derived + localparam int unsigned DepthW = $clog2(Depth+1) +) ( + // Write port + input clk_wr_i, + input rst_wr_ni, + input wvalid_i, + output logic wready_o, + input [Width-1:0] wdata_i, + output logic [DepthW-1:0] wdepth_o, + + // Read port + input clk_rd_i, + input rst_rd_ni, + output logic rvalid_o, + input rready_i, + output logic [Width-1:0] rdata_o, + output logic [DepthW-1:0] rdepth_o, + + output logic r_full_o, + output logic r_notempty_o, + + output logic w_full_o, + + // TODO: watermark(threshold) ? + + // SRAM interface + // Write SRAM port + output logic w_sram_req_o, + input w_sram_gnt_i, + output logic w_sram_write_o, + output logic [SramAw-1:0] w_sram_addr_o, + output logic [SramDw-1:0] w_sram_wdata_o, + output logic [SramDw-1:0] w_sram_wmask_o, + input w_sram_rvalid_i, // not used + input [SramDw-1:0] w_sram_rdata_i, // not used + input [1:0] w_sram_rerror_i, // not used + + // Read SRAM port + output logic r_sram_req_o, + input r_sram_gnt_i, + output logic r_sram_write_o, + output logic [SramAw-1:0] r_sram_addr_o, + output logic [SramDw-1:0] r_sram_wdata_o, // not used + output logic [SramDw-1:0] r_sram_wmask_o, // not used + input r_sram_rvalid_i, + input [SramDw-1:0] r_sram_rdata_i, + input [1:0] r_sram_rerror_i +); + + //////////////// + // Definition // + //////////////// + // w_: write clock domain signals + // r_: read clock domain signals + + // PtrVW: Pointer Value (without msb, flip) width + localparam int unsigned PtrVW = $clog2(Depth); + // PtrW: Read/Write pointer with flip bit + localparam int unsigned PtrW = PtrVW+1; + + //////////// + // Signal // + //////////// + + logic [PtrW-1:0] w_wptr_q, w_wptr_d, w_wptr_gray_d, w_wptr_gray_q; + logic [PtrW-1:0] r_wptr_gray, r_wptr; + logic [PtrVW-1:0] w_wptr_v, r_wptr_v; + logic w_wptr_p, r_wptr_p; // phase + + logic [PtrW-1:0] r_rptr_q, r_rptr_d, r_rptr_gray_d, r_rptr_gray_q; + logic [PtrW-1:0] w_rptr_gray, w_rptr; + logic [PtrVW-1:0] r_rptr_v, w_rptr_v; + logic r_rptr_p, w_rptr_p; // phase + + logic w_wptr_inc, r_rptr_inc; + + logic w_full, r_full, r_empty; + + // SRAM response one clock delayed. So store the value into read clock + // domain + logic stored; + logic [Width-1:0] rdata_q, rdata_d; + + // SRAM has another read pointer (for address of SRAM req) + // It is -1 of r_rptr if stored, else same to r_rptr + logic r_sram_rptr_inc; + logic [PtrW-1:0] r_sram_rptr; + + // r_sram_rptr == r_wptr + // Used to determine r_sram_req + logic r_sramrptr_empty; + + logic rfifo_ack; // Used to check if FIFO read interface consumes a data + logic rsram_ack; + + ////////////// + // Datapath // + ////////////// + + // Begin: Write pointer sync to read clock ======================== + assign w_wptr_inc = wvalid_i & wready_o; + + assign w_wptr_d = w_wptr_q + PtrW'(1); + + always_ff @(posedge clk_wr_i or negedge rst_wr_ni) begin + if (!rst_wr_ni) begin + w_wptr_q <= PtrW'(0); + w_wptr_gray_q <= PtrW'(0); + end else if (w_wptr_inc) begin + w_wptr_q <= w_wptr_d; + w_wptr_gray_q <= w_wptr_gray_d; + end + end + + assign w_wptr_v = w_wptr_q[0+:PtrVW]; + assign w_wptr_p = w_wptr_q[PtrW-1]; + + assign w_wptr_gray_d = dec2gray(w_wptr_d); + + prim_flop_2sync #( + .Width (PtrW) + ) u_sync_wptr_gray ( + .clk_i (clk_rd_i), + .rst_ni (rst_rd_ni), + .d_i (w_wptr_gray_q), + .q_o (r_wptr_gray) + ); + + assign r_wptr = gray2dec(r_wptr_gray); + assign r_wptr_p = r_wptr[PtrW-1]; + assign r_wptr_v = r_wptr[0+:PtrVW]; + + assign wdepth_o = (w_wptr_p == w_rptr_p) + ? DepthW'(w_wptr_v - w_rptr_v) + : DepthW'({1'b1, w_wptr_v} - {1'b 0, w_rptr_v}); + // End: Write pointer sync to read clock ------------------------ + + // Begin: Read pointer sync to write clock ======================== + //assign r_rptr_inc = rvalid_o & rready_i; + //assign r_rptr_inc = r_sram_req_o && r_sram_gnt_i; + // Increase the read pointer (crossing the clock domain) only when the + // reader acked. + assign r_rptr_inc = rfifo_ack; + + assign r_rptr_d = r_rptr_q + PtrW'(1); + + always_ff @(posedge clk_rd_i or negedge rst_rd_ni) begin + if (!rst_rd_ni) begin + r_rptr_q <= PtrW'(0); + r_rptr_gray_q <= PtrW'(0); + end else if (r_rptr_inc) begin + r_rptr_q <= r_rptr_d; + r_rptr_gray_q <= r_rptr_gray_d; + end + end + + assign r_rptr_v = r_rptr_q[0+:PtrVW]; + assign r_rptr_p = r_rptr_q[PtrW-1]; + + assign r_rptr_gray_d = dec2gray(r_rptr_d); + + prim_flop_2sync #( + .Width (PtrW) + ) u_sync_rptr_gray ( + .clk_i (clk_wr_i), + .rst_ni (rst_wr_ni), + .d_i (r_rptr_gray_q), + .q_o (w_rptr_gray) + ); + + assign w_rptr = gray2dec(w_rptr_gray); + assign w_rptr_p = w_rptr[PtrW-1]; + assign w_rptr_v = w_rptr[0+:PtrVW]; + + assign rdepth_o = (r_wptr_p == r_rptr_p) + ? DepthW'(r_wptr_v - r_rptr_v) + : DepthW'({1'b1, r_wptr_v} - {1'b 0, r_rptr_v}); + // End: Read pointer sync to write clock ------------------------ + + // Begin: SRAM Read pointer + assign r_sram_rptr_inc = rsram_ack; + + always_ff @(posedge clk_rd_i or negedge rst_rd_ni) begin + if (!rst_rd_ni) begin + r_sram_rptr <= PtrW'(0); + end else if (r_sram_rptr_inc) begin + r_sram_rptr <= r_sram_rptr + PtrW'(1); + end + end + + assign r_sramrptr_empty = (r_wptr == r_sram_rptr); + // End: SRAM Read pointer + + // Full/ Empty + // Lint complains PtrW'(1) << (PtrW-1). So changed as below + localparam logic [PtrW-1:0] XorMask = {1'b 1, {PtrW-1{1'b0}}}; + assign w_full = (w_wptr_q == (w_rptr ^ XorMask)); + assign r_full = (r_wptr == (r_rptr_q ^ XorMask)); + assign r_empty = (r_wptr == r_rptr_q); + + assign r_full_o = r_full; + assign r_notempty_o = !r_empty; + assign w_full_o = w_full; + + assign rsram_ack = r_sram_req_o && r_sram_gnt_i; + assign rfifo_ack = rvalid_o && rready_i; + + // SRAM Write Request + assign w_sram_req_o = wvalid_i && !w_full; + assign wready_o = !w_full && w_sram_gnt_i; + assign w_sram_write_o = 1'b 1; // Always write + assign w_sram_addr_o = SramBaseAddr + SramAw'(w_wptr_v); + + assign w_sram_wdata_o = SramDw'(wdata_i); + assign w_sram_wmask_o = SramDw'({Width{1'b1}}); + + logic unused_w_sram; + assign unused_w_sram = ^{w_sram_rvalid_i, w_sram_rdata_i, w_sram_rerror_i}; + + // SRAM Read Request + // Request Scenario (!r_empty): + // - storage empty: Send request if + // !r_sram_rvalid_i || (rfifo_ack && r_sram_rvalid_i); + // - storage !empty: depends on the rfifo_ack: + // - r_rptr_inc: Can request more + // - !r_rptr_inc: Can't request + always_comb begin : r_sram_req + r_sram_req_o = 1'b 0; + // Karnough Map (!empty): sram_req + // {sram_rv, rfifo_ack} | 00 | 01 | 11 | 10 + // ---------------------------------------------------------- + // stored | 0 | 1 | impossible | 1 | 0 + // | 1 | 0 | 1 | X | impossible + // + // req_o = r_ptr_inc || (!stored && !r_sram_rvalid_i) + + if (stored) begin + // storage has data. depends on rfifo_ack + // rfifo_ack can be replaced to rready_i as `rvalid_o` is 1 + r_sram_req_o = !r_sramrptr_empty && rfifo_ack; + end else begin + // storage has no data. + // Can send request only when the reader accept the request or no + // previous request sent out. + r_sram_req_o = !r_sramrptr_empty && !(r_sram_rvalid_i ^ rfifo_ack); + end + end : r_sram_req + + assign rvalid_o = stored || r_sram_rvalid_i; + assign r_sram_write_o = 1'b 0; // always read + assign r_sram_wdata_o = '0; + assign r_sram_wmask_o = '0; + + // Send SRAM request with sram read pointer. + assign r_sram_addr_o = SramBaseAddr + SramAw'(r_sram_rptr[0+:PtrVW]); + + assign rdata_d = (r_sram_rvalid_i) ? r_sram_rdata_i[0+:Width] : Width'(0); + + assign rdata_o = (stored) ? rdata_q : rdata_d; + + logic unused_rsram; + assign unused_rsram = ^{r_sram_rerror_i}; + + if (Width < SramDw) begin : g_unused_rdata + logic unused_rdata; + assign unused_rdata = ^r_sram_rdata_i[SramDw-1:Width]; + end : g_unused_rdata + + // read clock domain rdata storage + logic store; + + // Karnough Map (r_sram_rvalid_i): + // rfifo_ack | 0 | 1 | + // --------------------- + // stored 0 | 1 | 0 | + // 1 | 0 | 1 | + // + // stored = s.r.v && XNOR(stored, rptr_inc) + assign store = r_sram_rvalid_i && !(stored ^ rfifo_ack); + + always_ff @(posedge clk_rd_i or negedge rst_rd_ni) begin + if (!rst_rd_ni) begin + stored <= 1'b 0; + rdata_q <= Width'(0); + end else if (store) begin + stored <= 1'b 1; + rdata_q <= rdata_d; + end else if (!r_sram_rvalid_i && rfifo_ack) begin + // No request sent, host reads the data + stored <= 1'b 0; + rdata_q <= Width'(0); + end + end + + ////////////// + // Function // + ////////////// + + // dec2gray / gray2dec copied from prim_fifo_async.sv + function automatic [PtrW-1:0] dec2gray(input logic [PtrW-1:0] decval); + logic [PtrW-1:0] decval_sub; + logic [PtrW-1:0] decval_in; + logic unused_decval_msb; + + decval_sub = (PtrW)'(Depth) - {1'b0, decval[PtrW-2:0]} - 1'b1; + + decval_in = decval[PtrW-1] ? decval_sub : decval; + + // We do not care about the MSB, hence we mask it out + unused_decval_msb = decval_in[PtrW-1]; + decval_in[PtrW-1] = 1'b0; + + // Perform the XOR conversion + dec2gray = decval_in; + dec2gray ^= (decval_in >> 1); + + // Override the MSB + dec2gray[PtrW-1] = decval[PtrW-1]; + endfunction + + // Algorithm walks up from 0..N-1 then flips the upper bit and walks down from N-1 to 0. + function automatic [PtrW-1:0] gray2dec(input logic [PtrW-1:0] grayval); + logic [PtrW-1:0] dec_tmp, dec_tmp_sub; + logic unused_decsub_msb; + + dec_tmp = '0; + for (int i = PtrW-2; i >= 0; i--) begin + dec_tmp[i] = dec_tmp[i+1] ^ grayval[i]; + end + dec_tmp_sub = (PtrW)'(Depth) - dec_tmp - 1'b1; + if (grayval[PtrW-1]) begin + gray2dec = dec_tmp_sub; + // Override MSB + gray2dec[PtrW-1] = 1'b1; + unused_decsub_msb = dec_tmp_sub[PtrW-1]; + end else begin + gray2dec = dec_tmp; + end + endfunction + + /////////////// + // Assertion // + /////////////// + + `ASSERT_INIT(ParamCheckDepth_A, (Depth == 2**$clog2(Depth))) + + // Use FF if less than 4. + `ASSERT_INIT(MinDepth_A, Depth >= 4) + + // SramDw greather than or equal to Width + `ASSERT_INIT(WidthMatch_A, SramDw >= Width) + + // Not stored, Not read valid, but rptr_inc case is impossible + `ASSERT(RptrIncDataValid_A, + r_rptr_inc |-> stored || r_sram_rvalid_i, + clk_rd_i, !rst_rd_ni) + `ASSERT(SramRvalid_A, + r_sram_rvalid_i |-> !stored || r_rptr_inc, + clk_rd_i, !rst_rd_ni) + + // FIFO interface + `ASSERT(NoWAckInFull_A, w_wptr_inc |-> !w_full, + clk_wr_i, !rst_wr_ni) + + `ASSERT(WptrIncrease_A, + w_wptr_inc |=> w_wptr_v == PtrVW'($past(w_wptr_v) + 1), + clk_wr_i, !rst_wr_ni) + `ASSERT(WptrGrayOneBitAtATime_A, + w_wptr_inc |=> $countones(w_wptr_gray_q ^ $past(w_wptr_gray_q)) == 1, + clk_wr_i, !rst_wr_ni) + + `ASSERT(NoRAckInEmpty_A, r_rptr_inc |-> !r_empty, + clk_rd_i, !rst_rd_ni) + + `ASSERT(RptrIncrease_A, + r_rptr_inc |=> PtrVW'($past(r_rptr_v) + 1) == r_rptr_v, + clk_rd_i, !rst_rd_ni) + `ASSERT(RptrGrayOneBitAtATime_A, + r_rptr_inc |=> $countones(r_rptr_gray_q ^ $past(r_rptr_gray_q)) == 1, + clk_rd_i, !rst_rd_ni) + + // SRAM interface + `ASSERT(WSramRvalid_A, !w_sram_rvalid_i, clk_wr_i, !rst_wr_ni) + `ASSUME_FPV(WSramRdataError_M, w_sram_rdata_i == '0 && w_sram_rerror_i == '0, + clk_wr_i, !rst_wr_ni) + + `ASSUME(RSramRvalidOneCycle_M, + r_sram_req_o && r_sram_gnt_i |=> r_sram_rvalid_i, + clk_rd_i, !rst_rd_ni) + `ASSUME_FPV(RErrorTied_M, r_sram_rerror_i == '0, + clk_rd_i, !rst_rd_ni) + + + // FPV coverage + `COVER_FPV(WFull_C, w_full, clk_wr_i, !rst_wr_ni) + +endmodule : prim_fifo_async_sram_adapter diff --git a/vendor/lowrisc_ip/ip/prim/rtl/prim_lc_combine.sv b/vendor/lowrisc_ip/ip/prim/rtl/prim_lc_combine.sv new file mode 100644 index 00000000..67b9d825 --- /dev/null +++ b/vendor/lowrisc_ip/ip/prim/rtl/prim_lc_combine.sv @@ -0,0 +1,73 @@ +// Copyright lowRISC contributors. +// Licensed under the Apache License, Version 2.0, see LICENSE for details. +// SPDX-License-Identifier: Apache-2.0 +// +// Perform logical OR or AND between two life cycle multibit signals. + +module prim_lc_combine #( + // 0: use the ON value as active value for the logical combination + // 1: use the OFF value as active value for the logical combination + parameter bit ActiveLow = 0, + // 0: logical combination is an OR function + // 1: logical combination is an AND function + parameter bit CombineMode = 0 +) ( + input lc_ctrl_pkg::lc_tx_t lc_en_a_i, + input lc_ctrl_pkg::lc_tx_t lc_en_b_i, + output lc_ctrl_pkg::lc_tx_t lc_en_o +); + + // Determine whether which multibit value is considered "active" for the + // purpose of the logical function below. + parameter lc_ctrl_pkg::lc_tx_t ActiveValue = (ActiveLow) ? lc_ctrl_pkg::Off : lc_ctrl_pkg::On; + // Truth tables: + // + // ActiveLow: 0, CombineMode: 0 (active-high "OR") + // + // A | B | OUT + //------+------+----- + // !On | !On | !On + // On | !On | On + // !On | On | On + // On | On | On + // + // ActiveLow: 0, CombineMode: 1 (active-high "AND") + // + // A | B | OUT + //------+------+----- + // !On | !On | !On + // On | !On | !On + // !On | On | !On + // On | On | On + // + // ActiveLow: 1, CombineMode: 0 (active-low "OR") + // + // A | B | OUT + //------+------+----- + // !Off | !Off | !Off + // Off | !Off | Off + // !Off | Off | Off + // Off | Off | Off + // + // ActiveLow: 1, CombineMode: 1 (active-low "AND") + // + // A | B | OUT + //------+------+----- + // !Off | !Off | !Off + // Off | !Off | !Off + // !Off | Off | !Off + // Off | Off | Off + // + // Note: the inactive value (e.g. !On) can be any multibit value + // different from the active value. + // + for (genvar k = 0; k < $bits(ActiveValue); k++) begin : gen_loop + if (CombineMode && ActiveValue[k] || + (!CombineMode && !ActiveValue[k])) begin : gen_and_gate + assign lc_en_o[k] = lc_en_a_i[k] && lc_en_b_i[k]; + end else begin : gen_or_gate + assign lc_en_o[k] = lc_en_a_i[k] || lc_en_b_i[k]; + end + end + +endmodule : prim_lc_combine diff --git a/vendor/lowrisc_ip/ip/prim/rtl/prim_lc_sender.sv b/vendor/lowrisc_ip/ip/prim/rtl/prim_lc_sender.sv index 6502f836..601df4d7 100644 --- a/vendor/lowrisc_ip/ip/prim/rtl/prim_lc_sender.sv +++ b/vendor/lowrisc_ip/ip/prim/rtl/prim_lc_sender.sv @@ -10,19 +10,26 @@ `include "prim_assert.sv" -module prim_lc_sender ( +module prim_lc_sender #( + // 0: reset value is lc_ctrl_pkg::Off + // 1: reset value is lc_ctrl_pkg::On + parameter bit ResetValueIsOn = 0 +) ( input clk_i, input rst_ni, input lc_ctrl_pkg::lc_tx_t lc_en_i, output lc_ctrl_pkg::lc_tx_t lc_en_o ); + localparam lc_ctrl_pkg::lc_tx_t ResetValue = (ResetValueIsOn) ? lc_ctrl_pkg::On : + lc_ctrl_pkg::Off; + logic [lc_ctrl_pkg::TxWidth-1:0] lc_en, lc_en_out; assign lc_en = lc_ctrl_pkg::TxWidth'(lc_en_i); prim_flop #( .Width(lc_ctrl_pkg::TxWidth), - .ResetValue(lc_ctrl_pkg::TxWidth'(lc_ctrl_pkg::Off)) + .ResetValue(lc_ctrl_pkg::TxWidth'(ResetValue)) ) u_prim_flop ( .clk_i, .rst_ni, diff --git a/vendor/lowrisc_ip/ip/prim/rtl/prim_lc_sync.sv b/vendor/lowrisc_ip/ip/prim/rtl/prim_lc_sync.sv index 72b825b3..6a0cbe6f 100644 --- a/vendor/lowrisc_ip/ip/prim/rtl/prim_lc_sync.sv +++ b/vendor/lowrisc_ip/ip/prim/rtl/prim_lc_sync.sv @@ -19,7 +19,10 @@ module prim_lc_sync #( // This instantiates the synchronizer flops if set to 1. // In special cases where the receiver is in the same clock domain as the sender, // this can be set to 0. However, it is recommended to leave this at 1. - parameter bit AsyncOn = 1 + parameter bit AsyncOn = 1, + // 0: reset value is lc_ctrl_pkg::Off + // 1: reset value is lc_ctrl_pkg::On + parameter bit ResetValueIsOn = 0 ) ( input clk_i, input rst_ni, @@ -27,13 +30,16 @@ module prim_lc_sync #( output lc_ctrl_pkg::lc_tx_t [NumCopies-1:0] lc_en_o ); + localparam lc_ctrl_pkg::lc_tx_t ResetValue = (ResetValueIsOn) ? lc_ctrl_pkg::On : + lc_ctrl_pkg::Off; + `ASSERT_INIT(NumCopiesMustBeGreaterZero_A, NumCopies > 0) logic [lc_ctrl_pkg::TxWidth-1:0] lc_en; if (AsyncOn) begin : gen_flops prim_flop_2sync #( .Width(lc_ctrl_pkg::TxWidth), - .ResetValue(lc_ctrl_pkg::TxWidth'(lc_ctrl_pkg::Off)) + .ResetValue(lc_ctrl_pkg::TxWidth'(ResetValue)) ) u_prim_flop_2sync ( .clk_i, .rst_ni, @@ -68,20 +74,22 @@ module prim_lc_sync #( // If the multibit signal is in a transient state, we expect it // to be stable again within one clock cycle. - `ASSERT(CheckTransients_A, + // DV will exclude these three assertions by name, thus added a module name prefix to make it + // harder to accidentally replicate in other modules. + `ASSERT(PrimLcSyncCheckTransients_A, !(lc_en_i inside {lc_ctrl_pkg::On, lc_ctrl_pkg::Off}) |=> (lc_en_i inside {lc_ctrl_pkg::On, lc_ctrl_pkg::Off})) // If a signal departs from passive state, we expect it to move to the active state // with only one transient cycle in between. - `ASSERT(CheckTransients0_A, + `ASSERT(PrimLcSyncCheckTransients0_A, $past(lc_en_i == lc_ctrl_pkg::Off) && !(lc_en_i inside {lc_ctrl_pkg::On, lc_ctrl_pkg::Off}) |=> (lc_en_i == lc_ctrl_pkg::On)) - `ASSERT(CheckTransients1_A, + `ASSERT(PrimLcSyncCheckTransients1_A, $past(lc_en_i == lc_ctrl_pkg::On) && !(lc_en_i inside {lc_ctrl_pkg::On, lc_ctrl_pkg::Off}) |=> diff --git a/vendor/lowrisc_ip/ip/prim/rtl/prim_lfsr.sv b/vendor/lowrisc_ip/ip/prim/rtl/prim_lfsr.sv index be43175e..e17e1126 100644 --- a/vendor/lowrisc_ip/ip/prim/rtl/prim_lfsr.sv +++ b/vendor/lowrisc_ip/ip/prim/rtl/prim_lfsr.sv @@ -31,6 +31,8 @@ module prim_lfsr #( parameter LfsrType = "GAL_XOR", // Lfsr width parameter int unsigned LfsrDw = 32, + // Derived parameter, do not override + localparam int unsigned LfsrIdxDw = $clog2(LfsrDw), // Width of the entropy input to be XOR'd into state (lfsr_q[EntropyDw-1:0]) parameter int unsigned EntropyDw = 8, // Width of output tap (from lfsr_q[StateOutDw-1:0]) @@ -39,17 +41,27 @@ module prim_lfsr #( parameter logic [LfsrDw-1:0] DefaultSeed = LfsrDw'(1), // Custom polynomial coeffs parameter logic [LfsrDw-1:0] CustomCoeffs = '0, - // If StatePermEn is set to 1, the custom permutation specified via StatePerm is applied - // to the state output, in order to break linear shifting patterns of the LFSR. - parameter bit StatePermEn = 1'b0, - parameter logic [LfsrDw-1:0][$clog2(LfsrDw)-1:0] StatePerm = '0, + // If StatePermEn is set to 1, the custom permutation specified via StatePerm is applied to the + // state output, in order to break linear shifting patterns of the LFSR. Note that this + // permutation represents a way of customizing the LFSR via a random netlist constant. This is + // different from the NonLinearOut feature below which just transforms the output non-linearly + // with a fixed function. In most cases, designers should consider enabling StatePermEn as it + // comes basically "for free" in terms of area and timing impact. NonLinearOut on the other hand + // has area and timing implications and designers should consider whether the use of that feature + // is justified. + parameter bit StatePermEn = 1'b0, + parameter logic [LfsrDw-1:0][LfsrIdxDw-1:0] StatePerm = '0, // Enable this for DV, disable this for long LFSRs in FPV parameter bit MaxLenSVA = 1'b1, // Can be disabled in cases where seed and entropy // inputs are unused in order to not distort coverage // (the SVA will be unreachable in such cases) parameter bit LockupSVA = 1'b1, - parameter bit ExtSeedSVA = 1'b1 + parameter bit ExtSeedSVA = 1'b1, + // Introduce non-linearity to lfsr output. Note, unlike StatePermEn, this feature is not "for + // free". Please double check that this feature is indeed required. Also note that this feature + // is only available for LFSRs that have a power-of-two width greater or equal 16bit. + parameter bit NonLinearOut = 1'b0 ) ( input clk_i, input rst_ni, @@ -370,12 +382,140 @@ module prim_lfsr #( (lfsr_en_i) ? next_lfsr_state : lfsr_q; - if (StatePermEn) begin : gen_state_perm - for (genvar k = 0; k < StateOutDw; k++) begin : gen_perm_loop - assign state_o[k] = lfsr_q[StatePerm[k]]; + logic [LfsrDw-1:0] sbox_out; + if (NonLinearOut) begin : gen_out_non_linear + // The "aligned" permutation ensures that adjacent bits do not go into the same SBox. It is + // different from the state permutation that can be specified via the StatePerm parameter. The + // permutation taps out 4 SBox input bits at regular stride intervals. E.g., for a 16bit + // vector, the input assignment looks as follows: + // + // SBox0: 0, 4, 8, 12 + // SBox1: 1, 5, 9, 13 + // SBox2: 2, 6, 10, 14 + // SBox3: 3, 7, 11, 15 + // + // Note that this permutation can be produced by filling the input vector into matrix columns + // and reading out the SBox inputs as matrix rows. + localparam int NumSboxes = LfsrDw / 4; + // Fill in the input vector in col-major order. + logic [3:0][NumSboxes-1:0][LfsrIdxDw-1:0] matrix_indices; + for (genvar j = 0; j < LfsrDw; j++) begin : gen_input_idx_map + assign matrix_indices[j / NumSboxes][j % NumSboxes] = j; end + // Due to the LFSR shifting pattern, the above permutation has the property that the output of + // SBox(n) is going to be equal to SBox(n+1) in the subsequent cycle (unless the LFSR polynomial + // modifies some of the associated shifted bits via an XOR tap). + // We therefore tweak this permutation by rotating and reversing some of the assignment matrix + // columns. The rotation and reversion operations have been chosen such that this + // generalizes to all power of two widths supported by the LFSR primitive. For 16bit, this + // looks as follows: + // + // SBox0: 0, 6, 11, 14 + // SBox1: 1, 7, 10, 13 + // SBox2: 2, 4, 9, 12 + // SBox3: 3, 5, 8, 15 + // + // This can be achieved by: + // 1) down rotating the second column by NumSboxes/2 + // 2) reversing the third column + // 3) down rotating the fourth column by 1 and reversing it + // + logic [3:0][NumSboxes-1:0][LfsrIdxDw-1:0] matrix_rotrev_indices; + typedef logic [NumSboxes-1:0][LfsrIdxDw-1:0] matrix_col_t; + + // left-rotates a matrix column by the shift amount + function automatic matrix_col_t lrotcol(matrix_col_t col, integer shift); + matrix_col_t out; + for (int k = 0; k < NumSboxes; k++) begin + out[(k + shift) % NumSboxes] = col[k]; + end + return out; + endfunction : lrotcol + + // reverses a matrix column + function automatic matrix_col_t revcol(matrix_col_t col); + return {< StateOutDw) begin : gen_tieoff_unused + logic unused_sbox_out; + assign unused_sbox_out = ^sbox_out; + end + end else begin : gen_no_state_perm - assign state_o = lfsr_q[StateOutDw-1:0]; + assign state_o = StateOutDw'(sbox_out); end always_ff @(posedge clk_i or negedge rst_ni) begin : p_reg @@ -396,41 +536,50 @@ module prim_lfsr #( // the code below is not meant to be synthesized, // but it is intended to be used in simulation and FPV `ifndef SYNTHESIS - function automatic logic[LfsrDw-1:0] compute_next_state(logic[LfsrDw-1:0] lfsrcoeffs, - logic[EntropyDw-1:0] entropy, - logic[LfsrDw-1:0] state); + function automatic logic [LfsrDw-1:0] compute_next_state(logic [LfsrDw-1:0] lfsrcoeffs, + logic [EntropyDw-1:0] entropy, + logic [LfsrDw-1:0] current_state); logic state0; + logic [LfsrDw-1:0] next_state; + + next_state = current_state; // Galois XOR if (64'(LfsrType) == 64'("GAL_XOR")) begin - if (state == 0) begin - state = DefaultSeed; + if (next_state == 0) begin + next_state = DefaultSeed; end else begin - state0 = state[0]; - state = state >> 1; - if (state0) state ^= lfsrcoeffs; - state ^= LfsrDw'(entropy); + state0 = next_state[0]; + next_state = next_state >> 1; + if (state0) next_state ^= lfsrcoeffs; + next_state ^= LfsrDw'(entropy); end // Fibonacci XNOR end else if (64'(LfsrType) == "FIB_XNOR") begin - if (&state) begin - state = DefaultSeed; + if (&next_state) begin + next_state = DefaultSeed; end else begin - state0 = ~(^(state & lfsrcoeffs)); - state = state << 1; - state[0] = state0; - state ^= LfsrDw'(entropy); + state0 = ~(^(next_state & lfsrcoeffs)); + next_state = next_state << 1; + next_state[0] = state0; + next_state ^= LfsrDw'(entropy); end end else begin $error("unknown lfsr type"); end - return state; + return next_state; endfunction : compute_next_state // check whether next state is computed correctly - `ASSERT(NextStateCheck_A, lfsr_en_i && !seed_en_i |=> lfsr_q == - compute_next_state(coeffs, $past(entropy_i,1), $past(lfsr_q,1))) + // we shift the assertion by one clock cycle (##1) in order to avoid + // erroneous SVA triggers right after reset deassertion in cases where + // the precondition is true throughout the reset. + // this can happen since the disable_iff evaluates using unsampled values, + // meaning that the assertion may already read rst_ni == 1 on an active + // clock edge while the flops in the design have not yet changed state. + `ASSERT(NextStateCheck_A, ##1 lfsr_en_i && !seed_en_i |=> lfsr_q == + compute_next_state(coeffs, $past(entropy_i), $past(lfsr_q))) // Only check this if enabled. if (StatePermEn) begin : gen_perm_check @@ -456,7 +605,7 @@ module prim_lfsr #( // output check `ASSERT_KNOWN(OutputKnown_A, state_o) - if (!StatePermEn) begin : gen_output_sva + if (!StatePermEn && !NonLinearOut) begin : gen_output_sva `ASSERT(OutputCheck_A, state_o == StateOutDw'(lfsr_q)) end // if no external input changes the lfsr state, a lockup must not occur (by design) @@ -479,8 +628,12 @@ module prim_lfsr #( `ASSERT(LfsrLockupCheck_A, lfsr_en_i && lockup && !seed_en_i |=> !lockup) end - if (MaxLenSVA) begin : gen_max_len_sva + // If non-linear output requested, the LFSR width must be a power of 2 and greater than 16. + if(NonLinearOut) begin : gen_nonlinear_align_check_sva + `ASSERT_INIT(SboxByteAlign_A, 2**$clog2(LfsrDw) == LfsrDw && LfsrDw >= 16) + end + if (MaxLenSVA) begin : gen_max_len_sva `ifndef SYNTHESIS // the code below is a workaround to enable long sequences to be checked. // some simulators do not support SVA sequences longer than 2**32-1. diff --git a/vendor/lowrisc_ip/ip/prim/rtl/prim_mubi_pkg.sv b/vendor/lowrisc_ip/ip/prim/rtl/prim_mubi_pkg.sv new file mode 100644 index 00000000..62cdb185 --- /dev/null +++ b/vendor/lowrisc_ip/ip/prim/rtl/prim_mubi_pkg.sv @@ -0,0 +1,545 @@ +// 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; + + ////////////////////////////////////////////// + // 4 Bit Multibit Type and Functions // + ////////////////////////////////////////////// + + parameter int MuBi4Width = 4; + typedef enum logic [MuBi4Width-1:0] { + MuBi4Hi = 4'h5, // enabled + MuBi4Lo = 4'hA // disabled + } mubi4_e; + + // make a typedef such that this can be used as an intersignal type as well + typedef mubi4_e mubi4_t; + + // Return the multibit value to signal "enabled". + function automatic mubi4_e mubi4_hi_value(); + return MuBi4Hi; + endfunction : mubi4_hi_value + + // Return the multibit value to signal "disabled". + function automatic mubi4_e mubi4_lo_value(); + return MuBi4Lo; + endfunction : mubi4_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 mubi4_tst_hi_strict(mubi4_e val); + return MuBi4Hi == val; + endfunction : mubi4_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 mubi4_tst_lo_strict(mubi4_e val); + return MuBi4Lo == val; + endfunction : mubi4_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 mubi4_tst_hi_loose(mubi4_e val); + return MuBi4Lo != val; + endfunction : mubi4_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 mubi4_tst_lo_loose(mubi4_e val); + return MuBi4Hi != val; + endfunction : mubi4_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 mubi4_e mubi4_or(mubi4_e a, mubi4_e b, mubi4_e act); + logic [MuBi4Width-1:0] a_in, b_in, act_in, out; + a_in = a; + b_in = b; + act_in = act; + for (int k = 0; k < MuBi4Width; 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 mubi4_e'(out); + endfunction : mubi4_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 mubi4_e mubi4_and(mubi4_e a, mubi4_e b, mubi4_e act); + logic [MuBi4Width-1:0] a_in, b_in, act_in, out; + a_in = a; + b_in = b; + act_in = act; + for (int k = 0; k < MuBi4Width; 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 mubi4_e'(out); + endfunction : mubi4_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 mubi4_e mubi4_or_hi(mubi4_e a, mubi4_e b); + return mubi4_or(a, b, MuBi4Hi); + endfunction : mubi4_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 mubi4_e mubi4_and_hi(mubi4_e a, mubi4_e b); + return mubi4_and(a, b, MuBi4Hi); + endfunction : mubi4_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 mubi4_e mubi4_or_lo(mubi4_e a, mubi4_e b); + return mubi4_or(a, b, MuBi4Lo); + endfunction : mubi4_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 mubi4_e mubi4_and_lo(mubi4_e a, mubi4_e b); + return mubi4_and(a, b, MuBi4Lo); + endfunction : mubi4_and_lo + + ////////////////////////////////////////////// + // 8 Bit Multibit Type and Functions // + ////////////////////////////////////////////// + + parameter int MuBi8Width = 8; + typedef enum logic [MuBi8Width-1:0] { + MuBi8Hi = 8'hA5, // enabled + MuBi8Lo = 8'h5A // disabled + } mubi8_e; + + // make a typedef such that this can be used as an intersignal type as well + typedef mubi8_e mubi8_t; + + // Return the multibit value to signal "enabled". + function automatic mubi8_e mubi8_hi_value(); + return MuBi8Hi; + endfunction : mubi8_hi_value + + // Return the multibit value to signal "disabled". + function automatic mubi8_e mubi8_lo_value(); + return MuBi8Lo; + endfunction : mubi8_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 mubi8_tst_hi_strict(mubi8_e val); + return MuBi8Hi == val; + endfunction : mubi8_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 mubi8_tst_lo_strict(mubi8_e val); + return MuBi8Lo == val; + endfunction : mubi8_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 mubi8_tst_hi_loose(mubi8_e val); + return MuBi8Lo != val; + endfunction : mubi8_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 mubi8_tst_lo_loose(mubi8_e val); + return MuBi8Hi != val; + endfunction : mubi8_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 mubi8_e mubi8_or(mubi8_e a, mubi8_e b, mubi8_e act); + logic [MuBi8Width-1:0] a_in, b_in, act_in, out; + a_in = a; + b_in = b; + act_in = act; + for (int k = 0; k < MuBi8Width; 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 mubi8_e'(out); + endfunction : mubi8_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 mubi8_e mubi8_and(mubi8_e a, mubi8_e b, mubi8_e act); + logic [MuBi8Width-1:0] a_in, b_in, act_in, out; + a_in = a; + b_in = b; + act_in = act; + for (int k = 0; k < MuBi8Width; 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 mubi8_e'(out); + endfunction : mubi8_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 mubi8_e mubi8_or_hi(mubi8_e a, mubi8_e b); + return mubi8_or(a, b, MuBi8Hi); + endfunction : mubi8_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 mubi8_e mubi8_and_hi(mubi8_e a, mubi8_e b); + return mubi8_and(a, b, MuBi8Hi); + endfunction : mubi8_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 mubi8_e mubi8_or_lo(mubi8_e a, mubi8_e b); + return mubi8_or(a, b, MuBi8Lo); + endfunction : mubi8_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 mubi8_e mubi8_and_lo(mubi8_e a, mubi8_e b); + return mubi8_and(a, b, MuBi8Lo); + endfunction : mubi8_and_lo + + ////////////////////////////////////////////// + // 12 Bit Multibit Type and Functions // + ////////////////////////////////////////////// + + parameter int MuBi12Width = 12; + typedef enum logic [MuBi12Width-1:0] { + MuBi12Hi = 12'h5A5, // enabled + MuBi12Lo = 12'hA5A // disabled + } mubi12_e; + + // make a typedef such that this can be used as an intersignal type as well + typedef mubi12_e mubi12_t; + + // Return the multibit value to signal "enabled". + function automatic mubi12_e mubi12_hi_value(); + return MuBi12Hi; + endfunction : mubi12_hi_value + + // Return the multibit value to signal "disabled". + function automatic mubi12_e mubi12_lo_value(); + return MuBi12Lo; + endfunction : mubi12_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 mubi12_tst_hi_strict(mubi12_e val); + return MuBi12Hi == val; + endfunction : mubi12_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 mubi12_tst_lo_strict(mubi12_e val); + return MuBi12Lo == val; + endfunction : mubi12_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 mubi12_tst_hi_loose(mubi12_e val); + return MuBi12Lo != val; + endfunction : mubi12_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 mubi12_tst_lo_loose(mubi12_e val); + return MuBi12Hi != val; + endfunction : mubi12_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 mubi12_e mubi12_or(mubi12_e a, mubi12_e b, mubi12_e act); + logic [MuBi12Width-1:0] a_in, b_in, act_in, out; + a_in = a; + b_in = b; + act_in = act; + for (int k = 0; k < MuBi12Width; 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 mubi12_e'(out); + endfunction : mubi12_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 mubi12_e mubi12_and(mubi12_e a, mubi12_e b, mubi12_e act); + logic [MuBi12Width-1:0] a_in, b_in, act_in, out; + a_in = a; + b_in = b; + act_in = act; + for (int k = 0; k < MuBi12Width; 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 mubi12_e'(out); + endfunction : mubi12_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 mubi12_e mubi12_or_hi(mubi12_e a, mubi12_e b); + return mubi12_or(a, b, MuBi12Hi); + endfunction : mubi12_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 mubi12_e mubi12_and_hi(mubi12_e a, mubi12_e b); + return mubi12_and(a, b, MuBi12Hi); + endfunction : mubi12_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 mubi12_e mubi12_or_lo(mubi12_e a, mubi12_e b); + return mubi12_or(a, b, MuBi12Lo); + endfunction : mubi12_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 mubi12_e mubi12_and_lo(mubi12_e a, mubi12_e b); + return mubi12_and(a, b, MuBi12Lo); + endfunction : mubi12_and_lo + + ////////////////////////////////////////////// + // 16 Bit Multibit Type and Functions // + ////////////////////////////////////////////// + + parameter int MuBi16Width = 16; + typedef enum logic [MuBi16Width-1:0] { + MuBi16Hi = 16'hA5A5, // enabled + MuBi16Lo = 16'h5A5A // disabled + } mubi16_e; + + // make a typedef such that this can be used as an intersignal type as well + typedef mubi16_e mubi16_t; + + // Return the multibit value to signal "enabled". + function automatic mubi16_e mubi16_hi_value(); + return MuBi16Hi; + endfunction : mubi16_hi_value + + // Return the multibit value to signal "disabled". + function automatic mubi16_e mubi16_lo_value(); + return MuBi16Lo; + endfunction : mubi16_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 mubi16_tst_hi_strict(mubi16_e val); + return MuBi16Hi == val; + endfunction : mubi16_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 mubi16_tst_lo_strict(mubi16_e val); + return MuBi16Lo == val; + endfunction : mubi16_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 mubi16_tst_hi_loose(mubi16_e val); + return MuBi16Lo != val; + endfunction : mubi16_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 mubi16_tst_lo_loose(mubi16_e val); + return MuBi16Hi != val; + endfunction : mubi16_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 mubi16_e mubi16_or(mubi16_e a, mubi16_e b, mubi16_e act); + logic [MuBi16Width-1:0] a_in, b_in, act_in, out; + a_in = a; + b_in = b; + act_in = act; + for (int k = 0; k < MuBi16Width; 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 mubi16_e'(out); + endfunction : mubi16_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 mubi16_e mubi16_and(mubi16_e a, mubi16_e b, mubi16_e act); + logic [MuBi16Width-1:0] a_in, b_in, act_in, out; + a_in = a; + b_in = b; + act_in = act; + for (int k = 0; k < MuBi16Width; 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 mubi16_e'(out); + endfunction : mubi16_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 mubi16_e mubi16_or_hi(mubi16_e a, mubi16_e b); + return mubi16_or(a, b, MuBi16Hi); + endfunction : mubi16_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 mubi16_e mubi16_and_hi(mubi16_e a, mubi16_e b); + return mubi16_and(a, b, MuBi16Hi); + endfunction : mubi16_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 mubi16_e mubi16_or_lo(mubi16_e a, mubi16_e b); + return mubi16_or(a, b, MuBi16Lo); + endfunction : mubi16_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 mubi16_e mubi16_and_lo(mubi16_e a, mubi16_e b); + return mubi16_and(a, b, MuBi16Lo); + endfunction : mubi16_and_lo + +endpackage : prim_mubi_pkg + diff --git a/vendor/lowrisc_ip/ip/prim/rtl/prim_ram_1p_scr.sv b/vendor/lowrisc_ip/ip/prim/rtl/prim_ram_1p_scr.sv index 3f38f106..8e3fe196 100644 --- a/vendor/lowrisc_ip/ip/prim/rtl/prim_ram_1p_scr.sv +++ b/vendor/lowrisc_ip/ip/prim/rtl/prim_ram_1p_scr.sv @@ -45,11 +45,6 @@ module prim_ram_1p_scr import prim_ram_1p_pkg::*; #( // If set to 0, the cipher primitive is replicated, and together with a wider nonce input, // a unique keystream is generated for the full data width. parameter bit ReplicateKeyStream = 1'b0, - // Width of lfsr seed used for random init - parameter int LfsrWidth = 8, - parameter logic [LfsrWidth-1:0][$clog2(LfsrWidth)-1:0] StatePerm = { - 24'h988eab - }, // Derived parameters localparam int AddrWidth = prim_util_pkg::vbits(Depth), // Depending on the data width, we need to instantiate multiple parallel cipher primitives to @@ -69,9 +64,6 @@ module prim_ram_1p_scr import prim_ram_1p_pkg::*; #( input key_valid_i, input [DataKeyWidth-1:0] key_i, input [NonceWidth-1:0] nonce_i, - input [LfsrWidth-1:0] init_seed_i, - input init_req_i, - output logic init_ack_o, // Interface to TL-UL SRAM adapter input req_i, @@ -89,7 +81,6 @@ module prim_ram_1p_scr import prim_ram_1p_pkg::*; #( output logic rvalid_o, // Read response (rdata_o) is valid output logic [1:0] rerror_o, // Bit1: Uncorrectable, Bit0: Correctable output logic [31:0] raddr_o, // Read address for error reporting. - output logic intg_error_o, // config input ram_1p_cfg_t cfg_i @@ -104,95 +95,6 @@ module prim_ram_1p_scr import prim_ram_1p_pkg::*; #( `ASSERT_INIT(DiffWidthMinimum_A, DiffWidth >= 4) `ASSERT_INIT(DiffWidthWithParity_A, EnableParity && (DiffWidth == 8) || !EnableParity) - ////////////////////////////// - // Integrity error latching // - ////////////////////////////// - - logic intg_err_q; - always_ff @(posedge clk_i or negedge rst_ni) begin - if (!rst_ni) begin - intg_err_q <= '0; - end else if (intg_error_i) begin - intg_err_q <= 1'b1; - end - end - - prim_buf u_intg_err_out ( - .in_i(intg_error_i | intg_err_q), - .out_o(intg_error_o) - ); - - /////////////////////////// - // Lfsr for random init // - /////////////////////////// - - logic init_req_q, load_seed; - logic [AddrWidth-1:0] addr_cnt_q; - logic [LfsrWidth-1:0] lfsr_out; - logic init_sel; - - // input muxed addr/data/mask - logic [AddrWidth-1:0] addr; - logic [Width-1:0] wdata; - logic [Width-1:0] wmask; - - always_ff @(posedge clk_i or negedge rst_ni) begin - if (!rst_ni) begin - init_req_q <= '0; - end else begin - init_req_q <= init_req_i; - end - end - - assign load_seed = init_req_i & ~init_req_q; - - prim_lfsr #( - .LfsrDw(LfsrWidth), - .StateOutDw(LfsrWidth), - .StatePermEn(1'b0), - .StatePerm(StatePerm) - ) u_lfsr ( - .clk_i, - .rst_ni, - .lfsr_en_i(init_req_i), - .seed_en_i(load_seed), - .seed_i(init_seed_i), - .entropy_i('0), - .state_o(lfsr_out) - ); - - // TODO: Need to harden these counters long term - assign init_ack_o = init_req_q && addr_cnt_q == Depth - 1; - - always_ff @(posedge clk_i or negedge rst_ni) begin - if (!rst_ni) begin - addr_cnt_q <= '0; - end else if (init_ack_o) begin - addr_cnt_q <= '0; - end else if (init_req_q && addr_cnt_q < Depth - 1) begin - addr_cnt_q <= addr_cnt_q + AddrWidth'(1); - end - end - - // The lfsr width and the width of the data may not be completely aligned - localparam int LfsrMult = (Width % LfsrWidth > 0) ? Width / LfsrWidth + 1 : - Width / LfsrWidth; - localparam int FullRandWidth = LfsrMult * LfsrWidth; - - // The random value may be larger than what is needed - logic [FullRandWidth-1:0] rand_val; - assign rand_val = {LfsrMult{lfsr_out}}; - - if (LfsrMult * LfsrWidth > Width) begin : gen_rand_tieoffs - logic unused_rand; - assign unused_rand = ^rand_val[FullRandWidth-1:Width]; - end - - assign init_sel = init_req_q; - assign addr = init_sel ? addr_cnt_q : addr_i; - assign wdata = init_sel ? rand_val[Width-1:0] : wdata_i; - assign wmask = init_sel ? '1 : wmask_i; - ///////////////////////////////////////// // Pending Write and Address Registers // ///////////////////////////////////////// @@ -207,22 +109,22 @@ module prim_ram_1p_scr import prim_ram_1p_pkg::*; #( // Read / write strobes logic read_en, write_en_d, write_en_q; - assign gnt_o = req_i & key_valid_i & ~init_sel; + assign gnt_o = req_i & key_valid_i; assign read_en = gnt_o & ~write_i; - assign write_en_d = gnt_o & write_i | init_sel; + assign write_en_d = gnt_o & write_i; logic write_pending_q; logic addr_collision_d, addr_collision_q; logic [AddrWidth-1:0] waddr_q; - assign addr_collision_d = read_en & (write_en_q | write_pending_q) & (addr == waddr_q); + assign addr_collision_d = read_en & (write_en_q | write_pending_q) & (addr_i == waddr_q); // Macro requests and write strobe // The macro operation is silenced if an integrity error is seen logic macro_req; logic intg_err_macro_req; prim_buf u_intg_err_macro_req ( - .in_i(intg_error_i | intg_err_q), + .in_i(intg_error_i), .out_o(intg_err_macro_req) ); assign macro_req = ~intg_err_macro_req & (read_en | write_en_q | write_pending_q); @@ -239,16 +141,15 @@ module prim_ram_1p_scr import prim_ram_1p_pkg::*; #( // We only select the pending write address in case there is no incoming read transaction. logic [AddrWidth-1:0] addr_mux; - assign addr_mux = (read_en) ? addr : waddr_q; + assign addr_mux = (read_en) ? addr_i : waddr_q; // This creates a bijective address mapping using a substitution / permutation network. logic [AddrWidth-1:0] addr_scr; if (NumAddrScrRounds > 0) begin : gen_addr_scr - // TODO, expand this into copies with another primitive logic intg_err_addr_scr; prim_buf u_intg_err_addr_scr ( - .in_i(intg_error_i | intg_err_q), + .in_i(intg_error_i), .out_o(intg_err_addr_scr) ); @@ -292,10 +193,9 @@ module prim_ram_1p_scr import prim_ram_1p_pkg::*; #( logic [NumParScr*64-1:0] keystream; logic [NumParScr-1:0][DataNonceWidth-1:0] data_scr_nonce; - // TODO, expand this into copies with another primitive logic intg_err_data_scr; prim_buf u_intg_err_data_scr ( - .in_i(intg_error_i | intg_err_q), + .in_i(intg_error_i), .out_o(intg_err_data_scr) ); @@ -321,7 +221,7 @@ module prim_ram_1p_scr import prim_ram_1p_pkg::*; #( .valid_i ( gnt_o ), // The IV is composed of a nonce and the row address //.data_i ( {nonce_i[k * (64 - AddrWidth) +: (64 - AddrWidth)], addr} ), - .data_i ( {data_scr_nonce[k], addr} ), + .data_i ( {data_scr_nonce[k], addr_i} ), // All parallel scramblers use the same key .key_i, // Since we operate in counter mode, this can always be set to encryption mode @@ -437,16 +337,18 @@ module prim_ram_1p_scr import prim_ram_1p_pkg::*; #( logic rvalid_q; assign rvalid_o = rvalid_q; + logic intg_error_q; logic [Width-1:0] wmask_q; always_comb begin : p_forward_mux rdata_o = '0; - // regular reads - if (rvalid_q) begin + // regular reads. note that we just return zero in case + // an integrity error was signalled. + if (rvalid_q && !intg_error_q) begin rdata_o = rdata; end // In case of a collision, we forward the valid bytes of the write data from the unscrambled // holding register. - if (addr_collision_q) begin + if (addr_collision_q && !intg_error_q) begin for (int k = 0; k < Width; k++) begin if (wmask_q[k]) begin rdata_o[k] = wdata_q[k]; @@ -465,6 +367,7 @@ module prim_ram_1p_scr import prim_ram_1p_pkg::*; #( addr_collision_q <= 1'b0; rvalid_q <= 1'b0; write_en_q <= 1'b0; + intg_error_q <= 1'b0; raddr_q <= '0; waddr_q <= '0; wmask_q <= '0; @@ -475,14 +378,15 @@ module prim_ram_1p_scr import prim_ram_1p_pkg::*; #( addr_collision_q <= addr_collision_d; rvalid_q <= read_en; write_en_q <= write_en_d; + intg_error_q <= intg_error_i; if (read_en) begin - raddr_q <= addr; + raddr_q <= addr_i; end if (write_en_d) begin - waddr_q <= addr; - wmask_q <= wmask; - wdata_q <= wdata; + waddr_q <= addr_i; + wmask_q <= wmask_i; + wdata_q <= wdata_i; end if (rw_collision) begin wdata_scr_q <= wdata_scr_d; diff --git a/vendor/lowrisc_ip/ip/prim/rtl/prim_reg_cdc.sv b/vendor/lowrisc_ip/ip/prim/rtl/prim_reg_cdc.sv new file mode 100644 index 00000000..9222408f --- /dev/null +++ b/vendor/lowrisc_ip/ip/prim/rtl/prim_reg_cdc.sv @@ -0,0 +1,156 @@ +// Copyright lowRISC contributors. +// Licensed under the Apache License, Version 2.0, see LICENSE for details. +// SPDX-License-Identifier: Apache-2.0 +// +// Component handling register CDC + +`include "prim_assert.sv" + +module prim_reg_cdc #( + parameter int DataWidth = 32, + parameter logic [DataWidth-1:0] ResetVal = 32'h0, + parameter logic [DataWidth-1:0] BitMask = 32'hFFFFFFFF +) ( + input clk_src_i, + input rst_src_ni, + input clk_dst_i, + input rst_dst_ni, + + input src_update_i, + input src_regwen_i, + input src_we_i, + input src_re_i, + input [DataWidth-1:0] src_wd_i, + output logic src_busy_o, + output logic [DataWidth-1:0] src_qs_o, + input [DataWidth-1:0] dst_d_i, + output logic dst_we_o, + output logic dst_re_o, + output logic dst_regwen_o, + output logic [DataWidth-1:0] dst_wd_o +); + + //////////////////////////// + // Source domain + //////////////////////////// + localparam int TxnWidth = 3; + + logic src_ack; + logic src_busy_q; + logic [DataWidth-1:0] src_q; + logic [TxnWidth-1:0] txn_bits_q; + logic src_req; + + assign src_req = src_we_i | src_re_i; + + // busy indication back-pressures upstream if the register is accessed + // again. The busy indication is also used as a "commit" indication for + // resolving software and hardware write conflicts + always_ff @(posedge clk_src_i or negedge rst_src_ni) begin + if (!rst_src_ni) begin + src_busy_q <= '0; + end else if (src_req) begin + src_busy_q <= 1'b1; + end else if (src_busy_q && src_ack) begin + src_busy_q <= 1'b0; + end + end + + assign src_busy_o = src_busy_q; + + // src_q acts as both the write holding register and the software read back + // register. + // When software performs a write, the write data is captured in src_q for + // CDC purposes. When not performing a write, the src_q periodically + // samples the destination domain using the src_update_i indication. + // + // To resolve software and hardware conflicts, the process is as follows: + // When software issues a write, this module asserts "busy". While busy, + // src_q does not sample the destination value. Since the + // logic has committed to updating based on software command, there is an irreversible + // window from which hardware writes are ignored. Once the busy window completes, + // the cdc portion then begins sampling once more. + // + // This is consistent with prim_subreg_arb where during software / hardware conflicts, + // software is always prioritized. The main difference is the conflict resolution window + // is now larger instead of just one destination clock cycle. + + logic busy; + assign busy = src_busy_q & !src_ack; + + always_ff @(posedge clk_src_i or negedge rst_src_ni) begin + if (!rst_src_ni) begin + src_q <= ResetVal; + txn_bits_q <= '0; + end else if (src_req && !busy) begin + src_q <= src_wd_i & BitMask; + txn_bits_q <= {src_we_i, src_re_i, src_regwen_i}; + end else if (src_busy_q && src_ack || src_update_i && !busy) begin + // sample data whenever a busy transaction finishes OR + // when an update pulse is seen. + // TODO: We should add a cover group to test different sync timings + // between src_ack and src_update. Ie, there can be 3 scearios: + // 1. update one cycle before ack + // 2. ack one cycle before update + // 3. update / ack on the same cycle + // During all 3 cases the read data should be correct + src_q <= dst_d_i; + txn_bits_q <= '0; + end + end + + // reserved bits are not used + logic unused_wd; + assign unused_wd = ^src_wd_i; + + // src_q is always updated in the clk_src domain. + // when performing an update to the destination domain, it is guaranteed + // to not change by protocol. + assign src_qs_o = src_q; + assign dst_wd_o = src_q; + + //////////////////////////// + // CDC handling + //////////////////////////// + logic dst_req; + prim_sync_reqack u_prim_sync ( + .clk_src_i, + .rst_src_ni, + .clk_dst_i, + .rst_dst_ni, + .req_chk_i(1'b0), + // prim_sync_reqack does not natively handle single + // pulse requests, so use src_busy to even it out. + .src_req_i(src_req | src_busy_q), + .src_ack_o(src_ack), + .dst_req_o(dst_req), + // immediately ack on destination once request is seen + .dst_ack_i(dst_req) + ); + + // Each is valid only when destination request pulse is high + assign {dst_we_o, dst_re_o, dst_regwen_o} = txn_bits_q & {TxnWidth{dst_req}}; + + `ASSERT_KNOWN(SrcBusyKnown_A, src_busy_o, clk_src_i, !rst_src_ni) + `ASSERT_KNOWN(DstReqKnown_A, dst_req, clk_dst_i, !rst_dst_ni) + + // If busy goes high, we must eventually see an ack + `ASSERT(HungHandShake_A, $rose(src_busy_o) |-> strong(##[0:$] src_ack), clk_src_i, !rst_src_ni) + + `ifdef SIMULATION + logic async_flag; + always_ff @(posedge clk_src_i or negedge rst_src_ni) begin + if (!rst_src_ni) begin + async_flag <= '0; + end else if (src_req) begin + async_flag <= 1'b1; + end else if (dst_req) begin + async_flag <= '0; + end + end + + `ASSERT(ReqTimeout_A, $rose(async_flag) |-> strong(##[0:3] dst_req), clk_dst_i, !rst_dst_ni) + `endif + + +endmodule // prim_subreg_cdc diff --git a/vendor/lowrisc_ip/ip/prim/rtl/prim_sram_arbiter.sv b/vendor/lowrisc_ip/ip/prim/rtl/prim_sram_arbiter.sv index 50d2762d..acb9b8a2 100644 --- a/vendor/lowrisc_ip/ip/prim/rtl/prim_sram_arbiter.sv +++ b/vendor/lowrisc_ip/ip/prim/rtl/prim_sram_arbiter.sv @@ -12,18 +12,20 @@ `include "prim_assert.sv" module prim_sram_arbiter #( - parameter int N = 4, - parameter int SramDw = 32, - parameter int SramAw = 12, - parameter ArbiterImpl = "PPC" + parameter int unsigned N = 4, + parameter int unsigned SramDw = 32, + parameter int unsigned SramAw = 12, + parameter ArbiterImpl = "PPC", + parameter bit EnMask = 1'b 0 // Disable wmask if 0 ) ( input clk_i, input rst_ni, input [ N-1:0] req_i, input [SramAw-1:0] req_addr_i [N], - input req_write_i[N], + input [ N-1:0] req_write_i, input [SramDw-1:0] req_wdata_i[N], + input [SramDw-1:0] req_wmask_i[N], output logic [ N-1:0] gnt_o, output logic [ N-1:0] rsp_rvalid_o, // Pulse @@ -35,30 +37,50 @@ module prim_sram_arbiter #( output logic [SramAw-1:0] sram_addr_o, output logic sram_write_o, output logic [SramDw-1:0] sram_wdata_o, + output logic [SramDw-1:0] sram_wmask_o, input sram_rvalid_i, input [SramDw-1:0] sram_rdata_i, input [1:0] sram_rerror_i ); - typedef struct packed { logic write; logic [SramAw-1:0] addr; logic [SramDw-1:0] wdata; + logic [SramDw-1:0] wmask; } req_t; - localparam int ARB_DW = $bits(req_t); - req_t req_packed [N]; for (genvar i = 0 ; i < N ; i++) begin : gen_reqs - assign req_packed[i] = {req_write_i[i], req_addr_i[i], req_wdata_i[i]}; + assign req_packed[i] = { + req_write_i[i], + req_addr_i [i], + req_wdata_i[i], + (EnMask) ? req_wmask_i[i] : {SramDw{1'b1}} + }; end + localparam int ARB_DW = $bits(req_t); + req_t sram_packed; assign sram_write_o = sram_packed.write; assign sram_addr_o = sram_packed.addr; assign sram_wdata_o = sram_packed.wdata; + assign sram_wmask_o = (EnMask) ? sram_packed.wmask : {SramDw{1'b1}}; + + if (EnMask == 1'b 0) begin : g_unused + logic unused_wmask; + + always_comb begin + unused_wmask = 1'b 1; + for (int unsigned i = 0 ; i < N ; i++) begin + unused_wmask ^= ^req_wmask_i[i]; + end + unused_wmask ^= ^sram_packed.wmask; + end + end + if (ArbiterImpl == "PPC") begin : gen_arb_ppc prim_arbiter_ppc #( diff --git a/vendor/lowrisc_ip/ip/prim/rtl/prim_subreg.sv b/vendor/lowrisc_ip/ip/prim/rtl/prim_subreg.sv index d1fab641..70d049de 100644 --- a/vendor/lowrisc_ip/ip/prim/rtl/prim_subreg.sv +++ b/vendor/lowrisc_ip/ip/prim/rtl/prim_subreg.sv @@ -4,10 +4,12 @@ // // Register slice conforming to Comportibility guide. -module prim_subreg #( - parameter int DW = 32 , - parameter SWACCESS = "RW", // {RW, RO, WO, W1C, W1S, W0C, RC} - parameter logic [DW-1:0] RESVAL = '0 // Reset value +module prim_subreg + import prim_subreg_pkg::*; +#( + parameter int DW = 32, + parameter sw_access_e SwAccess = SwAccessRW, + parameter logic [DW-1:0] RESVAL = '0 // reset value ) ( input clk_i, input rst_ni, @@ -32,7 +34,7 @@ module prim_subreg #( prim_subreg_arb #( .DW ( DW ), - .SWACCESS ( SWACCESS ) + .SwAccess ( SwAccess ) ) wr_en_data_arb ( .we, .wd, diff --git a/vendor/lowrisc_ip/ip/prim/rtl/prim_subreg_arb.sv b/vendor/lowrisc_ip/ip/prim/rtl/prim_subreg_arb.sv index 410cf2d1..80033366 100644 --- a/vendor/lowrisc_ip/ip/prim/rtl/prim_subreg_arb.sv +++ b/vendor/lowrisc_ip/ip/prim/rtl/prim_subreg_arb.sv @@ -4,9 +4,11 @@ // // Write enable and data arbitration logic for register slice conforming to Comportibility guide. -module prim_subreg_arb #( - parameter int DW = 32 , - parameter SWACCESS = "RW" // {RW, RO, WO, W1C, W1S, W0C, RC} +module prim_subreg_arb + import prim_subreg_pkg::*; +#( + parameter int DW = 32, + parameter sw_access_e SwAccess = SwAccessRW ) ( // From SW: valid for RW, WO, W1C, W1S, W0C, RC. // In case of RC, top connects read pulse to we. @@ -25,13 +27,13 @@ module prim_subreg_arb #( output logic [DW-1:0] wr_data ); - if ((SWACCESS == "RW") || (SWACCESS == "WO")) begin : gen_w + if (SwAccess inside {SwAccessRW, SwAccessWO}) begin : gen_w assign wr_en = we | de; assign wr_data = (we == 1'b1) ? wd : d; // SW higher priority // Unused q - Prevent lint errors. logic [DW-1:0] unused_q; assign unused_q = q; - end else if (SWACCESS == "RO") begin : gen_ro + end else if (SwAccess == SwAccessRO) begin : gen_ro assign wr_en = de; assign wr_data = d; // Unused we, wd, q - Prevent lint errors. @@ -41,22 +43,22 @@ module prim_subreg_arb #( assign unused_we = we; assign unused_wd = wd; assign unused_q = q; - end else if (SWACCESS == "W1S") begin : gen_w1s - // If SWACCESS is W1S, then assume hw tries to clear. + end else if (SwAccess == SwAccessW1S) begin : gen_w1s + // If SwAccess is W1S, then assume hw tries to clear. // So, give a chance HW to clear when SW tries to set. // If both try to set/clr at the same bit pos, SW wins. assign wr_en = we | de; assign wr_data = (de ? d : q) | (we ? wd : '0); - end else if (SWACCESS == "W1C") begin : gen_w1c - // If SWACCESS is W1C, then assume hw tries to set. + end else if (SwAccess == SwAccessW1C) begin : gen_w1c + // If SwAccess is W1C, then assume hw tries to set. // So, give a chance HW to set when SW tries to clear. // If both try to set/clr at the same bit pos, SW wins. assign wr_en = we | de; assign wr_data = (de ? d : q) & (we ? ~wd : '1); - end else if (SWACCESS == "W0C") begin : gen_w0c + end else if (SwAccess == SwAccessW0C) begin : gen_w0c assign wr_en = we | de; assign wr_data = (de ? d : q) & (we ? wd : '1); - end else if (SWACCESS == "RC") begin : gen_rc + end else if (SwAccess == SwAccessRC) begin : gen_rc // This swtype is not recommended but exists for compatibility. // WARN: we signal is actually read signal not write enable. assign wr_en = we | de; diff --git a/vendor/lowrisc_ip/ip/prim/rtl/prim_subreg_async.sv b/vendor/lowrisc_ip/ip/prim/rtl/prim_subreg_async.sv new file mode 100644 index 00000000..b9d1ae13 --- /dev/null +++ b/vendor/lowrisc_ip/ip/prim/rtl/prim_subreg_async.sv @@ -0,0 +1,85 @@ +// Copyright lowRISC contributors. +// Licensed under the Apache License, Version 2.0, see LICENSE for details. +// SPDX-License-Identifier: Apache-2.0 +// +// Asynchronous implementation of prim_subreg + +module prim_subreg_async + import prim_subreg_pkg::*; +#( + parameter int DW = 32 , + parameter sw_access_e SwAccess = SwAccessRW, + parameter logic [DW-1:0] RESVAL = '0 // Reset value +) ( + input clk_src_i, + input rst_src_ni, + input clk_dst_i, + input rst_dst_ni, + + // destination sample pulse + input src_update_i, + + // From SW: valid for RW, WO, W1C, W1S, W0C, RC + // In case of RC, Top connects Read Pulse to we + input src_we_i, + input [DW-1:0] src_wd_i, + + // From HW: valid for HRW, HWO + input dst_de_i, + input [DW-1:0] dst_d_i, + + // output to Reg Read + output logic src_busy_o, + output logic [DW-1:0] src_qs_o, + + // output to HW read + output logic dst_qe_o, + // This output does not follow comportable convention to work with + // current DV assumptions. + output logic [DW-1:0] q +); + + logic dst_we; + logic [DW-1:0] dst_wdata; + logic [DW-1:0] q_int; + + prim_subreg_cdc #( + .DW(DW), + .RESVAL(RESVAL) + ) u_reg_cdc ( + .clk_src_i, + .rst_src_ni, + .clk_dst_i, + .rst_dst_ni, + .src_update_i, + .src_req_i(src_we_i), + // data that crosses domain + .src_data_i(src_wd_i), + // data readback to software + .src_data_o(src_qs_o), + .src_busy_o, + .dst_req_o(dst_we), + // hardware written data + .dst_data_i(q_int), + // data to write to hardware + .dst_data_o(dst_wdata) + ); + + prim_subreg #( + .DW(DW), + .SwAccess(SwAccess), + .RESVAL(RESVAL) + ) u_subreg ( + .clk_i(clk_dst_i), + .rst_ni(rst_dst_ni), + .we(dst_we), + .wd(dst_wdata), + .de(dst_de_i), + .d(dst_d_i), + .qe(dst_qe_o), + .q(q_int), + .qs() + ); + assign q = q_int; + +endmodule diff --git a/vendor/lowrisc_ip/ip/prim/rtl/prim_subreg_cdc.sv b/vendor/lowrisc_ip/ip/prim/rtl/prim_subreg_cdc.sv new file mode 100644 index 00000000..18618be8 --- /dev/null +++ b/vendor/lowrisc_ip/ip/prim/rtl/prim_subreg_cdc.sv @@ -0,0 +1,135 @@ +// Copyright lowRISC contributors. +// Licensed under the Apache License, Version 2.0, see LICENSE for details. +// SPDX-License-Identifier: Apache-2.0 +// +// Component handling register CDC + +`include "prim_assert.sv" + +module prim_subreg_cdc #( + parameter int DW = 32, + parameter logic [DW-1:0] RESVAL = '0 // Reset value +) ( + input clk_src_i, + input rst_src_ni, + input clk_dst_i, + input rst_dst_ni, + + input src_update_i, + input src_req_i, + input [DW-1:0] src_data_i, + output logic src_busy_o, + output logic [DW-1:0] src_data_o, + + input [DW-1:0] dst_data_i, + output logic dst_req_o, + output logic [DW-1:0] dst_data_o +); + + //////////////////////////// + // Source domain + //////////////////////////// + logic src_ack; + logic src_busy_q; + logic [DW-1:0] src_q; + + // busy indication back-pressures upstream if the register is accessed + // again. The busy indication is also used as a "commit" indication for + // resolving software and hardware write conflicts + always_ff @(posedge clk_src_i or negedge rst_src_ni) begin + if (!rst_src_ni) begin + src_busy_q <= '0; + end else if (src_req_i) begin + src_busy_q <= 1'b1; + end else if (src_busy_q && src_ack) begin + src_busy_q <= 1'b0; + end + end + + assign src_busy_o = src_busy_q; + + // src_q acts as both the write holding register and the software read back + // register. + // When software performs a write, the write data is captured in src_q for + // CDC purposes. When not performing a write, the src_q periodically + // samples the destination domain using the src_update_i indication. + // + // To resolve software and hardware conflicts, the process is as follows: + // When software issues a write, this module asserts "busy". While busy, + // src_q does not sample the destination value. Since the + // logic has committed to updating based on software command, there is an irreversible + // window from which hardware writes are ignored. Once the busy window completes, + // the cdc portion then begins sampling once more. + // + // This is consistent with prim_subreg_arb where during software / hardware conflicts, + // software is always prioritized. The main difference is the conflict resolution window + // is now larger instead of just one destination clock cycle. + + logic busy; + assign busy = src_busy_q & !src_ack; + + always_ff @(posedge clk_src_i or negedge rst_src_ni) begin + if (!rst_src_ni) begin + src_q <= RESVAL; + end else if (src_req_i && !busy) begin + src_q <= src_data_i; + end else if (src_busy_q && src_ack || src_update_i && !busy) begin + // sample data whenever a busy transaction finishes OR + // when an update pulse is seen. + // TODO: We should add a cover group to test different sync timings + // between src_ack and src_update. Ie, there can be 3 scearios: + // 1. update one cycle before ack + // 2. ack one cycle before update + // 3. update / ack on the same cycle + // During all 3 cases the read data should be correct + src_q <= dst_data_i; + end + end + + // src_q is always updated in the clk_src domain. + // when performing an update to the destination domain, it is guaranteed + // to not change by protocol. + assign src_data_o = src_q; + assign dst_data_o = src_q; + + //////////////////////////// + // CDC handling + //////////////////////////// + prim_sync_reqack u_prim_sync ( + .clk_src_i, + .rst_src_ni, + .clk_dst_i, + .rst_dst_ni, + .req_chk_i(1'b0), + // prim_sync_reqack does not natively handle single + // pulse requests, so use src_busy to even it out. + .src_req_i(src_req_i | src_busy_q), + .src_ack_o(src_ack), + .dst_req_o(dst_req_o), + // immediately ack on destination once request is seen + .dst_ack_i(dst_req_o) + ); + + `ASSERT_KNOWN(SrcBusyKnown_A, src_busy_o, clk_src_i, !rst_src_ni) + `ASSERT_KNOWN(DstReqKnown_A, dst_req_o, clk_dst_i, !rst_dst_ni) + + // If busy goes high, we must eventually see an ack + `ASSERT(HungHandShake_A, $rose(src_busy_o) |-> strong(##[0:$] src_ack), clk_src_i, !rst_src_ni) + + `ifdef SIMULATION + logic async_flag; + always_ff @(posedge src_req_i or posedge dst_req_o or + negedge rst_src_ni or negedge rst_dst_ni) begin + if (!rst_src_ni && !rst_dst_ni) begin + async_flag <= '0; + end else if (src_req_i) begin + async_flag <= 1'b1; + end else if (dst_req_o) begin + async_flag <= 1'b0; + end + end + `ASSERT(ReqTimeout_A, $rose(async_flag) |-> strong(##[0:3] dst_req_o), clk_dst_i, !rst_dst_ni) + `endif + + +endmodule // prim_subreg_cdc diff --git a/vendor/lowrisc_ip/ip/prim/rtl/prim_subreg_ext_async.sv b/vendor/lowrisc_ip/ip/prim/rtl/prim_subreg_ext_async.sv new file mode 100644 index 00000000..7b8576e3 --- /dev/null +++ b/vendor/lowrisc_ip/ip/prim/rtl/prim_subreg_ext_async.sv @@ -0,0 +1,68 @@ +// Copyright lowRISC contributors. +// Licensed under the Apache License, Version 2.0, see LICENSE for details. +// SPDX-License-Identifier: Apache-2.0 +// +// Asynchronous implementation of prim_subreg_ext + +module prim_subreg_ext_async #( + parameter int unsigned DW = 32 +) ( + input clk_src_i, + input rst_src_ni, + input clk_dst_i, + input rst_dst_ni, + + // source domain signals + input re, + input we, + input [DW-1:0] wd, + input src_update_i, + output logic src_busy_o, + + // destination domain signals + input [DW-1:0] d, + + // outputs to destination domain + output logic qe, + output logic qre, + output logic [DW-1:0] q, + + // outputs to source domain + output logic [DW-1:0] qs +); + + logic dst_req; + logic [DW-1:0] dst_wdata; + logic dst_we; + logic unused_src_we; + + // Capture both data and write-enable + // write enable is needed to determine whether qe or qre should be generated + // in the desitnation domain. + prim_subreg_cdc #( + .DW(DW + 1) + ) u_reg_cdc ( + .clk_src_i, + .rst_src_ni, + .clk_dst_i, + .rst_dst_ni, + .src_update_i, + .src_req_i(re | we), + .src_data_i({wd, we}), + .src_data_o({qs, unused_src_we}), + .src_busy_o, + .dst_req_o(dst_req), + .dst_data_i({d, 1'b0}), + .dst_data_o({dst_wdata, dst_we}) + ); + + ///////////////////// + // Destination domain + ///////////////////// + + assign qe = dst_req ? dst_we : '0; + assign qre = dst_req ? ~dst_we : '0; + assign q = dst_req ? dst_wdata : '0; + + +endmodule diff --git a/vendor/lowrisc_ip/ip/prim/rtl/prim_subreg_pkg.sv b/vendor/lowrisc_ip/ip/prim/rtl/prim_subreg_pkg.sv new file mode 100644 index 00000000..6e1da043 --- /dev/null +++ b/vendor/lowrisc_ip/ip/prim/rtl/prim_subreg_pkg.sv @@ -0,0 +1,17 @@ +// Copyright lowRISC contributors. +// Licensed under the Apache License, Version 2.0, see LICENSE for details. +// SPDX-License-Identifier: Apache-2.0 + +package prim_subreg_pkg; + + // Register access specifier + typedef enum logic [2:0] { + SwAccessRW = 3'd0, // Read-write + SwAccessRO = 3'd1, // Read-only + SwAccessWO = 3'd2, // Write-only + SwAccessW1C = 3'd3, // Write 1 to clear + SwAccessW1S = 3'd4, // Write 1 to set + SwAccessW0C = 3'd5, // Write 0 to clear + SwAccessRC = 3'd6 // Read to clear. Do not use, only exists for compatibility. + } sw_access_e; +endpackage diff --git a/vendor/lowrisc_ip/ip/prim/rtl/prim_subreg_shadow.sv b/vendor/lowrisc_ip/ip/prim/rtl/prim_subreg_shadow.sv index 69764191..bf1bd4cb 100644 --- a/vendor/lowrisc_ip/ip/prim/rtl/prim_subreg_shadow.sv +++ b/vendor/lowrisc_ip/ip/prim/rtl/prim_subreg_shadow.sv @@ -4,16 +4,21 @@ // // Shadowed register slice conforming to Comportibility guide. -module prim_subreg_shadow #( - parameter int DW = 32 , - parameter SWACCESS = "RW", // {RW, RO, WO, W1C, W1S, W0C, RC} +`include "prim_assert.sv" + +module prim_subreg_shadow + import prim_subreg_pkg::*; +#( + parameter int DW = 32, + parameter sw_access_e SwAccess = SwAccessRW, parameter logic [DW-1:0] RESVAL = '0 // reset value ) ( input clk_i, input rst_ni, + input rst_shadowed_ni, // From SW: valid for RW, WO, W1C, W1S, W0C, RC. - // SW reads clear phase unless SWACCESS is RO. + // SW reads clear phase unless SwAccess is RO. input re, // In case of RC, top connects read pulse to we. input we, @@ -33,12 +38,21 @@ module prim_subreg_shadow #( output logic err_storage ); - // Since the shadow and staging registers work with the 1's complement value, + // Since the shadow register works with the 1's complement value, // we need to invert the polarity of the SW access if it is either "W1S" or "W0C". // W1C is forbidden since the W0S complement is not implemented. - `ASSERT_INIT(CheckSwAccessIsLegal_A, SWACCESS inside {"RW", "RO", "WO", "W1S", "W0C", "RC"}) - parameter bit [3*8-1:0] INVERTED_SWACCESS = (SWACCESS == "W1S") ? "W0C" : - (SWACCESS == "W0C") ? "W1S" : SWACCESS; + `ASSERT_INIT(CheckSwAccessIsLegal_A, + SwAccess inside {SwAccessRW, SwAccessRO, SwAccessWO, SwAccessW1S, SwAccessW0C}) + localparam sw_access_e InvertedSwAccess = (SwAccess == SwAccessW1S) ? SwAccessW0C : + (SwAccess == SwAccessW0C) ? SwAccessW1S : SwAccess; + + // For the staging register, we set the SwAccess to RW in case of W1S and W0C in + // order to always capture the data value on the first write operation - no matter + // whether the data value will actually have an effect. That way, we can still capture + // inconsistent double writes which would otherwise be ignored due to the data value filtering + // effect that W1S and W0C can have. + localparam sw_access_e StagedSwAccess = (SwAccess == SwAccessW1S) ? SwAccessRW : + (SwAccess == SwAccessW0C) ? SwAccessRW : SwAccess; // Subreg control signals logic phase_clear; @@ -47,18 +61,18 @@ module prim_subreg_shadow #( logic staged_de, shadow_de, committed_de; // Subreg status and data signals - logic staged_qe, shadow_qe, committed_qe; + logic committed_qe; logic [DW-1:0] staged_q, shadow_q, committed_q; logic [DW-1:0] committed_qs; // Effective write enable and write data signals. - // These depend on we, de and wd, d, q as well as SWACCESS. + // These depend on we, de and wd, d, q as well as SwAccess. logic wr_en; logic [DW-1:0] wr_data; prim_subreg_arb #( .DW ( DW ), - .SWACCESS ( SWACCESS ) + .SwAccess ( SwAccess ) ) wr_en_data_arb ( .we ( we ), .wd ( wd ), @@ -70,9 +84,9 @@ module prim_subreg_shadow #( ); // Phase clearing: - // - SW reads clear phase unless SWACCESS is RO. + // - SW reads clear phase unless SwAccess is RO. // - In case of RO, SW should not interfere with update process. - assign phase_clear = (SWACCESS == "RO") ? 1'b0 : re; + assign phase_clear = (SwAccess == SwAccessRO) ? 1'b0 : re; // Phase tracker: // - Reads from SW clear the phase back to 0. @@ -80,9 +94,9 @@ module prim_subreg_shadow #( always_ff @(posedge clk_i or negedge rst_ni) begin : phase_reg if (!rst_ni) begin phase_q <= 1'b0; - end else if (wr_en) begin + end else if (wr_en && !err_storage) begin phase_q <= ~phase_q; - end else if (phase_clear) begin + end else if (phase_clear || err_storage) begin phase_q <= 1'b0; end end @@ -90,20 +104,21 @@ module prim_subreg_shadow #( // The staged register: // - Holds the 1's complement value. // - Written in Phase 0. - assign staged_we = we & ~phase_q; - assign staged_de = de & ~phase_q; + // - Once storage error occurs, do not allow any further update until reset + assign staged_we = we & ~phase_q & ~err_storage; + assign staged_de = de & ~phase_q & ~err_storage; prim_subreg #( - .DW ( DW ), - .SWACCESS ( INVERTED_SWACCESS ), - .RESVAL ( ~RESVAL ) + .DW ( DW ), + .SwAccess ( StagedSwAccess ), + .RESVAL ( ~RESVAL ) ) staged_reg ( .clk_i ( clk_i ), .rst_ni ( rst_ni ), .we ( staged_we ), - .wd ( ~wd ), + .wd ( ~wr_data ), .de ( staged_de ), .d ( ~d ), - .qe ( staged_qe ), + .qe ( ), .q ( staged_q ), .qs ( ) ); @@ -113,22 +128,23 @@ module prim_subreg_shadow #( // - Written in Phase 1. // - Writes are ignored in case of update errors. // - Gets the value from the staged register. - assign shadow_we = we & phase_q & ~err_update; - assign shadow_de = de & phase_q & ~err_update; + // - Once storage error occurs, do not allow any further update until reset + assign shadow_we = we & phase_q & ~err_update & ~err_storage; + assign shadow_de = de & phase_q & ~err_update & ~err_storage; prim_subreg #( - .DW ( DW ), - .SWACCESS ( INVERTED_SWACCESS ), - .RESVAL ( ~RESVAL ) + .DW ( DW ), + .SwAccess ( InvertedSwAccess ), + .RESVAL ( ~RESVAL ) ) shadow_reg ( - .clk_i ( clk_i ), - .rst_ni ( rst_ni ), - .we ( shadow_we ), - .wd ( staged_q ), - .de ( shadow_de ), - .d ( staged_q ), - .qe ( shadow_qe ), - .q ( shadow_q ), - .qs ( ) + .clk_i ( clk_i ), + .rst_ni ( rst_shadowed_ni ), + .we ( shadow_we ), + .wd ( staged_q ), + .de ( shadow_de ), + .d ( staged_q ), + .qe ( ), + .q ( shadow_q ), + .qs ( ) ); // The committed register: @@ -138,13 +154,13 @@ module prim_subreg_shadow #( assign committed_de = shadow_de; prim_subreg #( .DW ( DW ), - .SWACCESS ( SWACCESS ), + .SwAccess ( SwAccess ), .RESVAL ( RESVAL ) ) committed_reg ( .clk_i ( clk_i ), .rst_ni ( rst_ni ), .we ( committed_we ), - .wd ( wd ), + .wd ( wr_data ), .de ( committed_de ), .d ( d ), .qe ( committed_qe ), @@ -157,7 +173,7 @@ module prim_subreg_shadow #( assign err_storage = (~shadow_q != committed_q); // Remaining output assignments - assign qe = staged_qe | shadow_qe | committed_qe; + assign qe = committed_qe; assign q = committed_q; assign qs = committed_qs; diff --git a/vendor/lowrisc_ip/ip/prim/util/generate_prim_mubi_pkg.py b/vendor/lowrisc_ip/ip/prim/util/generate_prim_mubi_pkg.py new file mode 100755 index 00000000..705cac00 --- /dev/null +++ b/vendor/lowrisc_ip/ip/prim/util/generate_prim_mubi_pkg.py @@ -0,0 +1,41 @@ +#!/usr/bin/env python3 +# Copyright lowRISC contributors. +# Licensed under the Apache License, Version 2.0, see LICENSE for details. +# SPDX-License-Identifier: Apache-2.0 +r"""Convert mako template to Hjson register description +""" +import argparse +import sys +from io import StringIO + +from mako.template import Template + + +def main(): + parser = argparse.ArgumentParser(prog="generate_prim_mubi_pkg") + parser.add_argument('input', + nargs='?', + metavar='file', + type=argparse.FileType('r'), + default=sys.stdin, + help='input template file') + parser.add_argument('--n_max_nibbles', + type=int, + help='The script will create all multibit types with 1 to n_max_nibbles.', + default=4) + + args = parser.parse_args() + + # Determine output: if stdin then stdout if not then ?? + out = StringIO() + + reg_tpl = Template(args.input.read()) + out.write(reg_tpl.render(n_max_nibbles=args.n_max_nibbles)) + + print(out.getvalue()) + + out.close() + + +if __name__ == "__main__": + main() diff --git a/vendor/lowrisc_ip/ip/prim/util/primgen.py b/vendor/lowrisc_ip/ip/prim/util/primgen.py index eeac8d5d..c2b265b2 100755 --- a/vendor/lowrisc_ip/ip/prim/util/primgen.py +++ b/vendor/lowrisc_ip/ip/prim/util/primgen.py @@ -7,7 +7,7 @@ import os import sys # Make vendored packages available in the search path. -sys.path.append('vendor') +sys.path.append(os.path.join(os.path.dirname(__file__), 'vendor')) import re import shutil @@ -328,9 +328,12 @@ def _instance_sv(prim_name, techlib, parameters): def _create_instances(prim_name, techlibs, parameters): """ Build SystemVerilog code instantiating primitives from the techlib """ - techlibs_wo_generic = [ + + # Sort list of technology libraries to produce a stable ordering in the + # generated wrapper. + techlibs_wo_generic = sorted([ techlib for techlib in techlibs if techlib != 'generic' - ] + ]) techlibs_generic_last = techlibs_wo_generic + ['generic'] if not techlibs_wo_generic: diff --git a/vendor/lowrisc_ip/ip/prim_generic/lint/prim_generic_clock_gating.vlt b/vendor/lowrisc_ip/ip/prim_generic/lint/prim_generic_clock_gating.vlt index b2a1580a..c38cafc5 100644 --- a/vendor/lowrisc_ip/ip/prim_generic/lint/prim_generic_clock_gating.vlt +++ b/vendor/lowrisc_ip/ip/prim_generic/lint/prim_generic_clock_gating.vlt @@ -6,3 +6,4 @@ `verilator_config lint_off -rule UNUSED -file "*/rtl/prim_generic_clock_gating.sv" -match "Parameter is not used: 'NoFpgaGate'" +lint_off -rule UNUSED -file "*/rtl/prim_generic_clock_gating.sv" -match "Parameter is not used: 'FpgaBufGlobal'" diff --git a/vendor/lowrisc_ip/ip/prim_generic/lint/prim_generic_clock_gating.waiver b/vendor/lowrisc_ip/ip/prim_generic/lint/prim_generic_clock_gating.waiver index b73c7e49..469cd42b 100644 --- a/vendor/lowrisc_ip/ip/prim_generic/lint/prim_generic_clock_gating.waiver +++ b/vendor/lowrisc_ip/ip/prim_generic/lint/prim_generic_clock_gating.waiver @@ -9,3 +9,5 @@ waive -rules COMBO_NBA -location {prim_generic_clock_gating.sv} -rege -comment "clock gating cell creates a latch" waive -rules PARAM_NOT_USED -location {prim_generic_clock_gating.sv} -regexp {Parameter 'NoFpgaGate' not used} \ -comment "parameter unused but required to maintain uniform interface" +waive -rules PARAM_NOT_USED -location {prim_generic_clock_gating.sv} -regexp {Parameter 'FpgaBufGlobal' not used} \ + -comment "parameter unused but required to maintain uniform interface" diff --git a/vendor/lowrisc_ip/ip/prim_generic/lint/prim_generic_flash.waiver b/vendor/lowrisc_ip/ip/prim_generic/lint/prim_generic_flash.waiver index 16bdd05d..c9602d59 100644 --- a/vendor/lowrisc_ip/ip/prim_generic/lint/prim_generic_flash.waiver +++ b/vendor/lowrisc_ip/ip/prim_generic/lint/prim_generic_flash.waiver @@ -3,3 +3,7 @@ # SPDX-License-Identifier: Apache-2.0 # # waiver file for prim_generic_flash + +# The prim generic module does not make use of the IO ports +waive -rules INOUT_AS_IN -location {prim_generic_flash.sv} \ + -regexp {Inout port 'flash_.*_io' has no driver} diff --git a/vendor/lowrisc_ip/ip/prim_generic/lint/prim_generic_otp.waiver b/vendor/lowrisc_ip/ip/prim_generic/lint/prim_generic_otp.waiver index cba6e283..c85cee67 100644 --- a/vendor/lowrisc_ip/ip/prim_generic/lint/prim_generic_otp.waiver +++ b/vendor/lowrisc_ip/ip/prim_generic/lint/prim_generic_otp.waiver @@ -3,8 +3,14 @@ # SPDX-License-Identifier: Apache-2.0 # -waive -rules {CONST_FF} -location {prim_generic_otp.sv} -msg {Flip-flop 'err_q[3]' is driven by constant zero} \ - -comment "Due to the error encoding, this bit is always constant in this module." +waive -rules {CONST_FF} -location {prim_ram_1p_adv.sv} \ + -msg {Flip-flop 'rerror_q' is driven by constant zeros in module 'prim_ram_1p_adv' (Depth=1024,Width=22,EnableInputPipeline=1,EnableOutputPipeline=1)} \ + -comment "The read error bits are unused and hence set to zero." -waive -rules {INOUT_AS_IN} -location {prim_generic_otp.sv} -msg {Inout port 'ext_voltage_io' has no driver in module 'prim_generic_otp'} \ +waive -rules {INOUT_AS_IN} -location {prim_generic_otp.sv} \ + -msg {Inout port 'ext_voltage_io' has no driver in module 'prim_generic_otp'} \ -comment "This signal is not driven in the generic model." + +waive -rules {PARAM_NOT_USED} -location {prim_generic_otp.sv} \ + -regexp {Parameter '(VendorTestOffset|VendorTestSize)' not used in module 'prim_generic_otp'} \ + -comment "These two parameters are not used in the generic model." diff --git a/vendor/lowrisc_ip/ip/prim_generic/prim_generic_flash.core b/vendor/lowrisc_ip/ip/prim_generic/prim_generic_flash.core index fd4a82fe..7cf5c305 100644 --- a/vendor/lowrisc_ip/ip/prim_generic/prim_generic_flash.core +++ b/vendor/lowrisc_ip/ip/prim_generic/prim_generic_flash.core @@ -10,7 +10,8 @@ filesets: depend: - lowrisc:ip:tlul - lowrisc:prim:ram_1p - - lowrisc:systems:ast_pkg + - "fileset_partner ? (partner:systems:ast_pkg)" + - "!fileset_partner ? (lowrisc:systems:ast_pkg)" - lowrisc:ip:flash_ctrl_pkg files: - rtl/prim_generic_flash_bank.sv diff --git a/vendor/lowrisc_ip/ip/prim_generic/prim_generic_otp.core b/vendor/lowrisc_ip/ip/prim_generic/prim_generic_otp.core index d7feab86..0639a34f 100644 --- a/vendor/lowrisc_ip/ip/prim_generic/prim_generic_otp.core +++ b/vendor/lowrisc_ip/ip/prim_generic/prim_generic_otp.core @@ -11,6 +11,7 @@ filesets: - lowrisc:prim:all - lowrisc:prim:util - lowrisc:prim:ram_1p_adv + - lowrisc:systems:ast_pkg - lowrisc:prim:otp_pkg files: - rtl/prim_generic_otp.sv diff --git a/vendor/lowrisc_ip/ip/prim_generic/rtl/prim_generic_clock_buf.sv b/vendor/lowrisc_ip/ip/prim_generic/rtl/prim_generic_clock_buf.sv index dd5adf0e..ea58ee8d 100644 --- a/vendor/lowrisc_ip/ip/prim_generic/rtl/prim_generic_clock_buf.sv +++ b/vendor/lowrisc_ip/ip/prim_generic/rtl/prim_generic_clock_buf.sv @@ -4,7 +4,9 @@ `include "prim_assert.sv" -module prim_generic_clock_buf ( +module prim_generic_clock_buf #( + parameter bit NoFpgaBuf = 1'b0 // serves no function in generic +) ( input clk_i, output logic clk_o ); diff --git a/vendor/lowrisc_ip/ip/prim_generic/rtl/prim_generic_clock_gating.sv b/vendor/lowrisc_ip/ip/prim_generic/rtl/prim_generic_clock_gating.sv index 4ae0f906..d4b64568 100644 --- a/vendor/lowrisc_ip/ip/prim_generic/rtl/prim_generic_clock_gating.sv +++ b/vendor/lowrisc_ip/ip/prim_generic/rtl/prim_generic_clock_gating.sv @@ -8,7 +8,8 @@ // synchronizer before en_i). module prim_generic_clock_gating #( - parameter bit NoFpgaGate = 1'b0 // this parameter has no function in generic + parameter bit NoFpgaGate = 1'b0, // this parameter has no function in generic + parameter bit FpgaBufGlobal = 1'b1 // this parameter has no function in generic ) ( input clk_i, input en_i, diff --git a/vendor/lowrisc_ip/ip/prim_generic/rtl/prim_generic_otp.sv b/vendor/lowrisc_ip/ip/prim_generic/rtl/prim_generic_otp.sv index 5e4a8cae..ea1728d1 100644 --- a/vendor/lowrisc_ip/ip/prim_generic/rtl/prim_generic_otp.sv +++ b/vendor/lowrisc_ip/ip/prim_generic/rtl/prim_generic_otp.sv @@ -16,12 +16,17 @@ module prim_generic_otp // Number of Test TL-UL words parameter int TlDepth = 16, // Width of vendor-specific test control signal - parameter int TestCtrlWidth = 8, + parameter int TestCtrlWidth = 32, + parameter int TestStatusWidth = 32, + parameter int TestVectWidth = 8, // Derived parameters localparam int AddrWidth = prim_util_pkg::vbits(Depth), localparam int IfWidth = 2**SizeWidth*Width, // VMEM file to initialize the memory with - parameter MemInitFile = "" + parameter MemInitFile = "", + // Vendor test partition offset and size (both in bytes) + parameter int VendorTestOffset, + parameter int VendorTestSize ) ( input clk_i, input rst_ni, @@ -30,10 +35,12 @@ module prim_generic_otp input [PwrSeqWidth-1:0] pwr_seq_h_i, // External programming voltage inout wire ext_voltage_io, - // Test interface - input [TestCtrlWidth-1:0] test_ctrl_i, - input tlul_pkg::tl_h2d_t test_tl_i, - output tlul_pkg::tl_d2h_t test_tl_o, + // Test interfaces + input [TestCtrlWidth-1:0] test_ctrl_i, + output logic [TestStatusWidth-1:0] test_status_o, + output logic [TestVectWidth-1:0] test_vect_o, + input tlul_pkg::tl_h2d_t test_tl_i, + output tlul_pkg::tl_d2h_t test_tl_o, // Other DFT signals input lc_ctrl_pkg::lc_tx_t scanmode_i, // Scan Mode input input scan_en_i, // Scan Shift @@ -73,6 +80,9 @@ module prim_generic_otp assign otp_alert_src_o = '{p: '0, n: '1}; + assign test_vect_o = '0; + assign test_status_o = '0; + //////////////////////////////////// // TL-UL Test Interface Emulation // //////////////////////////////////// diff --git a/vendor/lowrisc_ip/ip/prim_generic/rtl/prim_generic_usb_diff_rx.sv b/vendor/lowrisc_ip/ip/prim_generic/rtl/prim_generic_usb_diff_rx.sv index 43ddde80..d49c06e2 100644 --- a/vendor/lowrisc_ip/ip/prim_generic/rtl/prim_generic_usb_diff_rx.sv +++ b/vendor/lowrisc_ip/ip/prim_generic/rtl/prim_generic_usb_diff_rx.sv @@ -13,7 +13,7 @@ module prim_generic_usb_diff_rx #( input wire input_pi, // differential input input wire input_ni, // differential input input input_en_i, // input buffer enable - input core_pok_i, // core power indication at VCC level + input core_pok_h_i, // core power indication at VCC level input pullup_p_en_i, // pullup enable for P input pullup_n_en_i, // pullup enable for N input [CalibW-1:0] calibration_i, // calibration input @@ -29,6 +29,6 @@ module prim_generic_usb_diff_rx #( assign unused_calibration = calibration_i; assign unused_pullup_p_en = pullup_p_en_i; assign unused_pullup_n_en = pullup_n_en_i; - assign unused_core_pok = core_pok_i; + assign unused_core_pok = core_pok_h_i; endmodule : prim_generic_usb_diff_rx diff --git a/vendor/lowrisc_ip/ip/prim_xilinx/rtl/prim_xilinx_clock_buf.sv b/vendor/lowrisc_ip/ip/prim_xilinx/rtl/prim_xilinx_clock_buf.sv index 05777ca3..f3ff43d8 100644 --- a/vendor/lowrisc_ip/ip/prim_xilinx/rtl/prim_xilinx_clock_buf.sv +++ b/vendor/lowrisc_ip/ip/prim_xilinx/rtl/prim_xilinx_clock_buf.sv @@ -2,14 +2,21 @@ // Licensed under the Apache License, Version 2.0, see LICENSE for details. // SPDX-License-Identifier: Apache-2.0 -module prim_xilinx_clock_buf ( +module prim_xilinx_clock_buf #( + parameter bit NoFpgaBuf = 1'b0 +) ( input clk_i, output logic clk_o ); - BUFG bufg_i ( - .I(clk_i), - .O(clk_o) - ); + if (NoFpgaBuf) begin : gen_no_fpga_buf + assign clk_o = clk_i; + end else begin : gen_fpga_buf + BUFG bufg_i ( + .I(clk_i), + .O(clk_o) + ); + end + endmodule diff --git a/vendor/lowrisc_ip/ip/prim_xilinx/rtl/prim_xilinx_clock_gating.sv b/vendor/lowrisc_ip/ip/prim_xilinx/rtl/prim_xilinx_clock_gating.sv index 23c6257f..501db88d 100644 --- a/vendor/lowrisc_ip/ip/prim_xilinx/rtl/prim_xilinx_clock_gating.sv +++ b/vendor/lowrisc_ip/ip/prim_xilinx/rtl/prim_xilinx_clock_gating.sv @@ -3,7 +3,8 @@ // SPDX-License-Identifier: Apache-2.0 module prim_xilinx_clock_gating #( - parameter bit NoFpgaGate = 1'b0 + parameter bit NoFpgaGate = 1'b0, + parameter bit FpgaBufGlobal = 1'b1 ) ( input clk_i, input en_i, @@ -14,13 +15,28 @@ module prim_xilinx_clock_gating #( if (NoFpgaGate) begin : gen_no_gate assign clk_o = clk_i; end else begin : gen_gate - BUFGCE #( - .SIM_DEVICE("7SERIES") - ) u_bufgce ( - .I (clk_i), - .CE(en_i | test_en_i), - .O (clk_o) - ); + if (FpgaBufGlobal) begin : gen_bufgce + // By default, we use BUFG(CE)s, i.e., global clock buffers (with enable input). + // These resources are scarce (32 in monolithic 7 series devices) and under some + // circumstances cannot be cascaded. They should especially be used for (gating) + // clocks that span big parts of the design/multiple clock regions. + BUFGCE #( + .SIM_DEVICE("7SERIES") + ) u_bufgce ( + .I (clk_i), + .CE(en_i | test_en_i), + .O (clk_o) + ); + end else begin : gen_bufhce + // The BUFH(CE) is a horizontal or local clock buffer (with enable input). Every clock + // region has 12 of these buffers. They should be used for (gating) clocks that are + // being used locally. + BUFHCE u_bufhce ( + .I (clk_i), + .CE(en_i | test_en_i), + .O (clk_o) + ); + end end diff --git a/vendor/lowrisc_ip/lint/tools/ascentlint/comportable.waiver b/vendor/lowrisc_ip/lint/tools/ascentlint/comportable.waiver index 43ebcfc7..dcf62a97 100644 --- a/vendor/lowrisc_ip/lint/tools/ascentlint/comportable.waiver +++ b/vendor/lowrisc_ip/lint/tools/ascentlint/comportable.waiver @@ -18,4 +18,5 @@ waive -rules CASE_SEL_CONST -location {*_reg_top*} \ -comment "addr_hit is one hot encoded." waive -rules LINE_LENGTH -location {*_reg_top*} -regexp {Line length of .* exceeds .* character limit} \ -comment "These files are one-liners in order to comply with our SV style guide." - +waive -rules CONST_FF -location {*_reg_top*} -regexp {Flip-flop '(shadow_|)rst_done' is driven by constant one} \ + -comment "This flop implements a reset done indication and is therefore driven by a constant 1." diff --git a/vendor/lowrisc_ip/util/dvsim/CfgFactory.py b/vendor/lowrisc_ip/util/dvsim/CfgFactory.py index 2064fbb0..e50c6b0f 100644 --- a/vendor/lowrisc_ip/util/dvsim/CfgFactory.py +++ b/vendor/lowrisc_ip/util/dvsim/CfgFactory.py @@ -4,6 +4,7 @@ import logging as log import sys +import os from CfgJson import load_hjson @@ -82,7 +83,10 @@ def make_cfg(path, args, proj_root): of the project. ''' - initial_values = {'proj_root': proj_root} + initial_values = { + 'proj_root': proj_root, + 'self_dir': os.path.dirname(path), + } if args.tool is not None: initial_values['tool'] = args.tool diff --git a/vendor/lowrisc_ip/util/dvsim/Deploy.py b/vendor/lowrisc_ip/util/dvsim/Deploy.py index dac9788b..2a120a7f 100644 --- a/vendor/lowrisc_ip/util/dvsim/Deploy.py +++ b/vendor/lowrisc_ip/util/dvsim/Deploy.py @@ -612,21 +612,16 @@ class CovReport(Deploy): def post_finish(self, status): """Extract the coverage results summary for the dashboard. - If that fails for some reason, report the job as a failure. + If the extraction fails, an appropriate exception is raised, which must + be caught by the caller to mark the job as a failure. """ if self.dry_run or status != 'P': return - results, self.cov_total, ex_msg = get_cov_summary_table( + results, self.cov_total = get_cov_summary_table( self.cov_report_txt, self.sim_cfg.tool) - if ex_msg: - self.launcher.fail_msg += ex_msg - log.error(ex_msg) - return - - # Succeeded in obtaining the coverage data. colalign = (("center", ) * len(results[0])) self.cov_results = tabulate(results, headers="firstrow", diff --git a/vendor/lowrisc_ip/util/dvsim/FlowCfg.py b/vendor/lowrisc_ip/util/dvsim/FlowCfg.py index 809f0bba..fd64b0e9 100644 --- a/vendor/lowrisc_ip/util/dvsim/FlowCfg.py +++ b/vendor/lowrisc_ip/util/dvsim/FlowCfg.py @@ -15,7 +15,7 @@ from CfgJson import set_target_attribute from LauncherFactory import get_launcher_cls from Scheduler import Scheduler from utils import (VERBOSE, clean_odirs, find_and_substitute_wildcards, md_results_to_html, - mk_path, rm_path, subst_wildcards) + mk_path, mk_symlink, rm_path, subst_wildcards) # Interface class for extensions. @@ -134,6 +134,8 @@ class FlowCfg(): self.results_path = os.path.join(self.scratch_base_path, "reports", self.rel_path, self.timestamp) + self.symlink_path = os.path.join(self.scratch_base_path, "reports", self.rel_path, + "latest") # Process overrides before substituting wildcards self._process_overrides() @@ -401,7 +403,7 @@ class FlowCfg(): ''' for item in self.cfgs: result = item._gen_results(results) - item.write_results_html() + item.write_results_html("results.html", item.results_md) log.info("[results]: [%s]:\n%s\n", item.name, result) log.info("[scratch_path]: [%s] [%s]", item.name, item.scratch_path) log.log(VERBOSE, "[results_path]: [%s] [%s]", item.name, item.results_path) @@ -409,7 +411,7 @@ class FlowCfg(): if self.is_primary_cfg: self.gen_results_summary() - self.write_results_html() + self.write_results_html("summary.html", self.results_summary_md) self.gen_email_html_summary() def gen_results_summary(self): @@ -417,25 +419,30 @@ class FlowCfg(): ''' return - def write_results_html(self): + def write_results_html(self, filename, text_md): + '''Convert given text_md to html format and write it to file with given filename. + ''' # Prepare workspace to generate reports. # Keep up to 2 weeks results. - clean_odirs(odir=self.results_path, max_odirs=14) + # If path exists, skip clean_odirs and directly update the files in the existing path. + if not (os.path.exists(self.results_path)): + clean_odirs(odir=self.results_path, max_odirs=14) mk_path(self.results_path) + mk_path(self.symlink_path) + + # Prepare results and paths. + text_html = md_results_to_html(self.results_title, self.css_file, text_md) + result_path = os.path.join(self.results_path, filename) + symlink_path = os.path.join(self.symlink_path, filename) # Write results to the report area. - if self.is_primary_cfg: - results_html = md_results_to_html(self.results_title, self.css_file, - self.results_summary_md) - result_path = os.path.join(self.results_path, "summary.html") - else: - results_html = md_results_to_html(self.results_title, self.css_file, - self.results_md) - result_path = os.path.join(self.results_path, "results.html") with open(result_path, "w") as results_file: - results_file.write(results_html) + results_file.write(text_html) log.log(VERBOSE, "[results page]: [%s][%s], self.name, results_path") + # Link the `/latest` folder with the latest reports. + mk_symlink(result_path, symlink_path) + def _get_results_page_link(self, link_text): if not self.args.publish: return link_text @@ -579,13 +586,8 @@ class FlowCfg(): # Publish the results page. # First, write the results html file to the scratch area. - results_html_file = self.results_path + "/publish_results_" + self.timestamp + \ - ".html" - f = open(results_html_file, 'w') - f.write( - md_results_to_html(self.results_title, self.css_file, - publish_results_md)) - f.close() + self.write_results_html("publish.html", publish_results_md) + results_html_file = os.path.join(self.results_path, "publish.html") log.info("Publishing results to %s", results_page_url) cmd = (self.results_server_cmd + " cp " + results_html_file + " " + diff --git a/vendor/lowrisc_ip/util/dvsim/FormalCfg.py b/vendor/lowrisc_ip/util/dvsim/FormalCfg.py index 7e01ce8b..6a33fac5 100644 --- a/vendor/lowrisc_ip/util/dvsim/FormalCfg.py +++ b/vendor/lowrisc_ip/util/dvsim/FormalCfg.py @@ -19,6 +19,9 @@ class FormalCfg(OneShotCfg): flow = 'formal' def __init__(self, flow_cfg_file, hjson_data, args, mk_config): + # Options set from command line + self.batch_mode_prefix = "" if args.gui else "-batch" + super().__init__(flow_cfg_file, hjson_data, args, mk_config) self.header = ["name", "errors", "warnings", "proven", "cex", "undetermined", "covered", "unreachable", "pass_rate", "cov_rate"] diff --git a/vendor/lowrisc_ip/util/dvsim/Launcher.py b/vendor/lowrisc_ip/util/dvsim/Launcher.py index 5580ebe5..cc9d3083 100644 --- a/vendor/lowrisc_ip/util/dvsim/Launcher.py +++ b/vendor/lowrisc_ip/util/dvsim/Launcher.py @@ -9,7 +9,7 @@ import re import sys from pathlib import Path -from utils import VERBOSE, clean_odirs, rm_path +from utils import VERBOSE, clean_odirs, mk_symlink, rm_path class LauncherError(Exception): @@ -134,6 +134,12 @@ class Launcher: # Store the deploy object handle. self.deploy = deploy + # Status of the job. This is primarily determined by the + # _check_status() method, but eventually updated by the _post_finish() + # method, in case any of the cleanup tasks fails. This value is finally + # returned to the Scheduler by the poll() method. + self.status = None + # Return status of the process running the job. self.exit_code = None @@ -160,14 +166,7 @@ class Launcher: """ dest = Path(self.deploy.sim_cfg.links[status], self.deploy.qual_name) - - # If dest exists, then atomically remove it and link the odir again. - while True: - try: - os.symlink(self.deploy.odir, dest) - break - except FileExistsError: - rm_path(dest) + mk_symlink(self.deploy.odir, dest) # Delete the symlink from dispatched directory if it exists. if status != "D": @@ -308,11 +307,24 @@ class Launcher: """ assert status in ['P', 'F', 'K'] - if status in ['P', 'F']: - self._link_odir(status) - self.deploy.post_finish(status) + self._link_odir(status) log.debug("Item %s has completed execution: %s", self, status) - if status != "P": + + try: + # Run the target-specific cleanup tasks regardless of the job's + # outcome. + self.deploy.post_finish(status) + except Exception as e: + # If the job had already failed, then don't do anything. If it's + # cleanup task failed, then mark the job as failed. + if status == "P": + status = "F" + err_msg = ErrorMessage(line_number=None, + message=f"{e}", + context=[]) + + self.status = status + if self.status != "P": assert err_msg and isinstance(err_msg, ErrorMessage) self.fail_msg = err_msg log.log(VERBOSE, err_msg.message) diff --git a/vendor/lowrisc_ip/util/dvsim/LocalLauncher.py b/vendor/lowrisc_ip/util/dvsim/LocalLauncher.py index 0a096825..03f997af 100644 --- a/vendor/lowrisc_ip/util/dvsim/LocalLauncher.py +++ b/vendor/lowrisc_ip/util/dvsim/LocalLauncher.py @@ -80,7 +80,7 @@ class LocalLauncher(Launcher): self.exit_code = self.process.returncode status, err_msg = self._check_status() self._post_finish(status, err_msg) - return status + return self.status def kill(self): '''Kill the running process. @@ -104,9 +104,9 @@ class LocalLauncher(Launcher): context=[])) def _post_finish(self, status, err_msg): - super()._post_finish(status, err_msg) self._close_process() self.process = None + super()._post_finish(status, err_msg) def _close_process(self): '''Close the file descriptors associated with the process.''' diff --git a/vendor/lowrisc_ip/util/dvsim/LsfLauncher.py b/vendor/lowrisc_ip/util/dvsim/LsfLauncher.py index e7119f07..1069ecb9 100644 --- a/vendor/lowrisc_ip/util/dvsim/LsfLauncher.py +++ b/vendor/lowrisc_ip/util/dvsim/LsfLauncher.py @@ -119,10 +119,6 @@ class LsfLauncher(Launcher): def __init__(self, deploy): super().__init__(deploy) - # Set the status. Only update after the job is done - i.e. status will - # transition from None to P/F/K. - self.status = None - # Maintain the job script output as an instance variable for polling # and cleanup. self.bsub_out = None @@ -390,7 +386,6 @@ class LsfLauncher(Launcher): def _post_finish(self, status, err_msg): if self.bsub_out_fd: self.bsub_out_fd.close() - self.status = status if self.exit_code is None: self.exit_code = 0 if status == 'P' else 1 super()._post_finish(status, err_msg) diff --git a/vendor/lowrisc_ip/util/dvsim/SimCfg.py b/vendor/lowrisc_ip/util/dvsim/SimCfg.py index dd816509..7f166ab9 100644 --- a/vendor/lowrisc_ip/util/dvsim/SimCfg.py +++ b/vendor/lowrisc_ip/util/dvsim/SimCfg.py @@ -392,8 +392,11 @@ class SimCfg(FlowCfg): if self.reseed_ovrd is not None: test.reseed = self.reseed_ovrd - # Apply reseed multiplier if set on the command line. - test.reseed *= self.reseed_multiplier + # Apply reseed multiplier if set on the command line. This is + # always positive but might not be an integer. Round to nearest, + # but make sure there's always at least one iteration. + scaled = round(test.reseed * self.reseed_multiplier) + test.reseed = max(1, scaled) # Create the unique set of builds needed. if test.build_mode.name not in build_list_names: @@ -621,7 +624,7 @@ class SimCfg(FlowCfg): else: testplan = "https://{}/{}".format(self.doc_server, self.rel_path) - testplan = testplan.replace("/dv", "/doc/dv_plan/#testplan") + testplan = testplan.replace("/dv", "/doc/dv/#testplan") results_str += f"### [Testplan]({testplan})\n" diff --git a/vendor/lowrisc_ip/util/dvsim/Testplan.py b/vendor/lowrisc_ip/util/dvsim/Testplan.py index c859f711..84dfb1d7 100644 --- a/vendor/lowrisc_ip/util/dvsim/Testplan.py +++ b/vendor/lowrisc_ip/util/dvsim/Testplan.py @@ -177,7 +177,7 @@ class Testpoint(Element): self.test_results = [Result(name=self.name, passing=0, total=0)] -class Testplan(): +class Testplan: """The full testplan The list of Testpoints and Covergroups make up the testplan. @@ -192,7 +192,7 @@ class Testplan(): try: return hjson.load(open(filename, 'rU')) except IOError as e: - print(f"IO Error when opening fie {filename}\n{e}") + print(f"IO Error when opening file {filename}\n{e}") except hjson.scanner.HjsonDecodeError as e: print(f"Error: Unable to decode HJSON with file {filename}:\n{e}") sys.exit(1) @@ -287,8 +287,9 @@ class Testplan(): self.progress = {} for key in Testpoint.milestones: self.progress[key] = { - "written": 0, "total": 0, + "written": 0, + "passing": 0, "progress": 0.0, } @@ -440,6 +441,8 @@ class Testplan(): # Compute the testplan progress. self.progress[ms]["total"] += 1 if tr.total != 0: + if tr.passing == tr.total: + self.progress[ms]["passing"] += 1 self.progress[ms]["written"] += 1 # Compute the milestone total & the grand total. @@ -500,7 +503,7 @@ class Testplan(): self.progress.pop(ms) continue - stat["progress"] = self._get_percentage(stat["written"], + stat["progress"] = self._get_percentage(stat["passing"], stat["total"]) self.test_results_mapped = True @@ -527,8 +530,9 @@ class Testplan(): written += 1 self.progress["Covergroups"] = { - "written": written, "total": total, + "written": written, + "passing": written, "progress": self._get_percentage(written, total), } diff --git a/vendor/lowrisc_ip/util/dvsim/dvsim.py b/vendor/lowrisc_ip/util/dvsim/dvsim.py index 3a6f5bf3..9a61b0cb 100755 --- a/vendor/lowrisc_ip/util/dvsim/dvsim.py +++ b/vendor/lowrisc_ip/util/dvsim/dvsim.py @@ -157,7 +157,7 @@ def get_proj_root(): "But this command has failed:\n" "{}".format(' '.join(cmd), result.stderr.decode("utf-8"))) sys.exit(1) - return (proj_root) + return proj_root def resolve_proj_root(args): @@ -251,6 +251,19 @@ def wrapped_docstring(): return '\n\n'.join(textwrap.fill(p) for p in paras) +def parse_reseed_multiplier(as_str: str) -> float: + '''Parse the argument for --reseed-multiplier''' + try: + ret = float(as_str) + except ValueError: + raise argparse.ArgumentTypeError('Invalid reseed multiplier: {!r}. ' + 'Must be a float.' + .format(as_str)) + if ret <= 0: + raise argparse.ArgumentTypeError('Reseed multiplier must be positive.') + return ret + + def parse_args(): parser = argparse.ArgumentParser( description=wrapped_docstring(), @@ -484,7 +497,7 @@ def parse_args(): seedg.add_argument("--reseed-multiplier", "-rx", - type=int, + type=parse_reseed_multiplier, default=1, metavar="N", help=('Scale each reseed value in the test ' diff --git a/vendor/lowrisc_ip/util/dvsim/sim_utils.py b/vendor/lowrisc_ip/util/dvsim/sim_utils.py index 94087787..bacc633c 100644 --- a/vendor/lowrisc_ip/util/dvsim/sim_utils.py +++ b/vendor/lowrisc_ip/util/dvsim/sim_utils.py @@ -16,21 +16,15 @@ from collections import OrderedDict # and the coverage was extracted successfully. It returns a tuple of: # List of metrics and values # Final coverage total -# Error message, if failed +# +# Raises the appropriate exception if the coverage summary extraction fails. def get_cov_summary_table(cov_report_txt, tool): - try: - with open(cov_report_txt, 'r') as f: - if tool == 'xcelium': - return xcelium_cov_summary_table(f) - if tool == 'vcs': - return vcs_cov_summary_table(f) - - err_msg = "Unsupported tool for cov extraction: {}".format(tool) - return None, None, err_msg - - except Exception as e: - err_msg = "Exception occurred: {}".format(str(e)) - return None, None, err_msg + with open(cov_report_txt, 'r') as f: + if tool == 'xcelium': + return xcelium_cov_summary_table(f) + if tool == 'vcs': + return vcs_cov_summary_table(f) + raise NotImplementedError(f"{tool} is unsupported for cov extraction.") # Same desc as above, but specific to Xcelium and takes an opened input stream. @@ -42,21 +36,23 @@ def xcelium_cov_summary_table(buf): # Change first item to 'Score'. metrics[0] = 'Score' - # metric. + # Gather the list of metrics. items = OrderedDict() for metric in metrics: items[metric] = {} items[metric]['covered'] = 0 items[metric]['total'] = 0 + # Next line is a separator. line = buf.readline() + # Subsequent lines are coverage items to be aggregated. for line in buf: line = re.sub(r"%\s+\(", "%(", line) values = line.strip().split() for i, value in enumerate(values): value = value.strip() - m = re.search(r"\((\d+)/(\d+)\)", value) + m = re.search(r"\((\d+)/(\d+).*\)", value) if m: items[metrics[i]]['covered'] += int(m.group(1)) items[metrics[i]]['total'] += int(m.group(2)) @@ -75,11 +71,10 @@ def xcelium_cov_summary_table(buf): values.append(value) if metric == 'Score': cov_total = value - return [metrics, values], cov_total, None + return [items.keys(), values], cov_total # If we reached here, then we were unable to extract the coverage. - err_msg = "ParseError: coverage data not found!" - return None, None, err_msg + raise SyntaxError(f"Coverage data not found in {buf.name}!") # Same desc as above, but specific to VCS and takes an opened input stream. @@ -100,8 +95,7 @@ def vcs_cov_summary_table(buf): values.append(val) # first row is coverage total cov_total = values[0] - return [metrics, values], cov_total, None + return [metrics, values], cov_total # If we reached here, then we were unable to extract the coverage. - err_msg = "ParseError: coverage data not found!" - return None, None, err_msg + raise SyntaxError(f"Coverage data not found in {buf.name}!") diff --git a/vendor/lowrisc_ip/util/dvsim/utils.py b/vendor/lowrisc_ip/util/dvsim/utils.py index 4916a8db..db539f63 100644 --- a/vendor/lowrisc_ip/util/dvsim/utils.py +++ b/vendor/lowrisc_ip/util/dvsim/utils.py @@ -569,6 +569,21 @@ def mk_path(path): sys.exit(1) +def mk_symlink(path, link): + '''Create a symlink from the given path. + + 'link' is a Path-like object. If it does exist, remove the existing link and + create a new symlink with this given path. + If it does not exist, the function creates the symlink with the given path. + ''' + while True: + try: + os.symlink(path, link) + break + except FileExistsError: + rm_path(link) + + def clean_odirs(odir, max_odirs, ts_format=TS_FORMAT): """Clean previous output directories. diff --git a/vendor/lowrisc_ip/util/uvmdvgen/README.md b/vendor/lowrisc_ip/util/uvmdvgen/README.md index eba370f1..956adc33 100644 --- a/vendor/lowrisc_ip/util/uvmdvgen/README.md +++ b/vendor/lowrisc_ip/util/uvmdvgen/README.md @@ -63,7 +63,7 @@ optional arguments: -eo [hw/ip/], --env-outdir [hw/ip/] Path to place the full tetsbench code. It creates 3 directories - dv, data and doc. The DV document and the - DV plan Hjson files are placed in the doc and data + testplan Hjson files are placed in the doc and data directories respectively. These are to be merged into the IP's root directory (with the existing data and doc directories). Under dv, it creates 3 sub- diff --git a/vendor/lowrisc_ip/util/uvmdvgen/checklist.md.tpl b/vendor/lowrisc_ip/util/uvmdvgen/checklist.md.tpl index 187dda70..851448e8 100644 --- a/vendor/lowrisc_ip/util/uvmdvgen/checklist.md.tpl +++ b/vendor/lowrisc_ip/util/uvmdvgen/checklist.md.tpl @@ -55,9 +55,7 @@ Code Quality | [LINT_PASS][] | Not Started | Code Quality | [CDC_SETUP][] | Not Started | Code Quality | [FPGA_TIMING][] | Not Started | Code Quality | [CDC_SYNCMACRO][] | Not Started | -Security | [SEC_CM_IMPLEMENTED][] | Not Started | -Security | [SEC_NON_RESET_FLOPS][] | Not Started | -Security | [SEC_SHADOW_REGS][] | Not Started | +Security | [SEC_CM_DOCUMENTED][] | Not Started | Security | [SEC_RND_CNST][] | Not Started | [NEW_FEATURES]: {{}} @@ -75,9 +73,7 @@ Security | [SEC_RND_CNST][] | Not Started | [CDC_SETUP]: {{}} [FPGA_TIMING]: {{}} [CDC_SYNCMACRO]: {{}} -[SEC_CM_IMPLEMENTED]: {{}} -[SEC_NON_RESET_FLOPS]: {{}} -[SEC_SHADOW_REGS]: {{}} +[SEC_CM_DOCUMENTED]: {{}} [SEC_RND_CNST]: {{}} <%text>### D3 @@ -94,6 +90,9 @@ Review | [REVIEW_SW_CSR][] | Not Started | Review | [REVIEW_SW_FATAL_ERR][] | Not Started | Review | [REVIEW_SW_CHANGE][] | Not Started | Review | [REVIEW_SW_ERRATA][] | Not Started | +Security | [SEC_CM_IMPLEMENTED][] | Not Started | +Security | [SEC_NON_RESET_FLOPS][] | Not Started | +Security | [SEC_SHADOW_REGS][] | Not Started | Review | Reviewer(s) | Not Started | Review | Signoff date | Not Started | @@ -108,6 +107,9 @@ Review | Signoff date | Not Started | [REVIEW_SW_FATAL_ERR]: {{}} [REVIEW_SW_CHANGE]: {{}} [REVIEW_SW_ERRATA]: {{}} +[SEC_CM_IMPLEMENTED]: {{}} +[SEC_NON_RESET_FLOPS]: {{}} +[SEC_SHADOW_REGS]: {{}} <%text>## Verification Checklist diff --git a/vendor/lowrisc_ip/util/uvmdvgen/index.md.tpl b/vendor/lowrisc_ip/util/uvmdvgen/index.md.tpl index 15f04a3c..f1cab93c 100644 --- a/vendor/lowrisc_ip/util/uvmdvgen/index.md.tpl +++ b/vendor/lowrisc_ip/util/uvmdvgen/index.md.tpl @@ -14,7 +14,7 @@ applicable. Once done, remove this comment before making a PR. --> ${'##'} Goals * **DV** * Verify all ${name.upper()} IP features by running dynamic simulations with a SV/UVM based testbench - * Develop and run all tests based on the [DV plan](#dv-plan) below towards closing code and functional coverage on the IP and all of its sub-modules + * Develop and run all tests based on the [testplan](#testplan) below towards closing code and functional coverage on the IP and all of its sub-modules * **FPV** * Verify TileLink device protocol compliance with an SVA based testbench @@ -128,7 +128,7 @@ Here's how to run a smoke test: $ $REPO_TOP/util/dvsim/dvsim.py $REPO_TOP/hw/ip/${name}/dv/${name}_sim_cfg.hjson -i ${name}_smoke ``` -${'##'} DV plan +${'##'} Testplan -{{} +{{} diff --git a/vendor/lowrisc_ip/util/uvmdvgen/uvmdvgen.py b/vendor/lowrisc_ip/util/uvmdvgen/uvmdvgen.py index 4cb297d5..64f67e56 100755 --- a/vendor/lowrisc_ip/util/uvmdvgen/uvmdvgen.py +++ b/vendor/lowrisc_ip/util/uvmdvgen/uvmdvgen.py @@ -107,7 +107,7 @@ def main(): "--env-outdir", metavar="[hw/ip/]", help="""Path to place the full testbench code. It creates 3 directories - - dv, data, and doc. The DV plan and the testplan Hjson files are placed + - dv, data, and doc. The DV doc and the testplan Hjson files are placed in the doc and data directories respectively. These are to be merged into the IP's root directory (with the existing data and doc directories). Under dv, it creates 3 sub-directories - env, tb, and