[dv/otp] Probe internal mubi interface
Force internal mubi types to values that are not true or false.
Make sure design locked the part accesses.
Signed-off-by: Cindy Chen <chencindy@opentitan.org>
diff --git a/hw/ip/otp_ctrl/dv/cov/otp_ctrl_cov_bind.sv b/hw/ip/otp_ctrl/dv/cov/otp_ctrl_cov_bind.sv
index d33f727..915e892 100644
--- a/hw/ip/otp_ctrl/dv/cov/otp_ctrl_cov_bind.sv
+++ b/hw/ip/otp_ctrl/dv/cov/otp_ctrl_cov_bind.sv
@@ -3,7 +3,28 @@
// SPDX-License-Identifier: Apache-2.0
//
// Binds OTP_CTRL functional coverage interaface to the top level OTP_CTRL module.
+`define PART_MUBI_COV(__part_name, __index) \
+ bind otp_ctrl cip_mubi_cov_if #(.Width(8)) ``__part_name``_read_lock_mubi_cov_if ( \
+ .rst_ni (rst_ni), \
+ .mubi (part_access[``__index``].read_lock) \
+ ); \
+ bind otp_ctrl cip_mubi_cov_if #(.Width(8)) ``__part_name``_write_lock_mubi_cov_if ( \
+ .rst_ni (rst_ni), \
+ .mubi (part_access[``__index``].write_lock) \
+ );
+
+`define DAI_MUBI_COV(__part_name, __index) \
+ bind otp_ctrl cip_mubi_cov_if #(.Width(8)) dai_``__part_name``_read_lock_mubi_cov_if ( \
+ .rst_ni (rst_ni), \
+ .mubi (part_access_dai[``__index``].read_lock) \
+ ); \
+ bind otp_ctrl cip_mubi_cov_if #(.Width(8)) dai_``__part_name``_write_lock_mubi_cov_if ( \
+ .rst_ni (rst_ni), \
+ .mubi (part_access_dai[``__index``].write_lock) \
+ );
+
module otp_ctrl_cov_bind;
+ import otp_ctrl_part_pkg::*;
bind otp_ctrl otp_ctrl_cov_if u_otp_ctrl_cov_if (
.pwr_otp_o (pwr_otp_o),
@@ -38,4 +59,25 @@
.rst_ni (rst_ni),
.mubi (lc_check_byp_en_i)
);
+
+ // Mubi internal coverage for buffered and unbuffered partitions.
+ `PART_MUBI_COV(vendor_test, VendorTestIdx)
+ `PART_MUBI_COV(creator_sw, CreatorSwCfgIdx)
+ `PART_MUBI_COV(owner_sw, OwnerSwCfgIdx)
+ `PART_MUBI_COV(hw_cfg, HwCfgIdx)
+ `PART_MUBI_COV(secret0, Secret0Idx)
+ `PART_MUBI_COV(secret1, Secret1Idx)
+ `PART_MUBI_COV(secret2, Secret2Idx)
+
+ // Mubi internal coverage for DAI interface access
+ `DAI_MUBI_COV(vendor_test, VendorTestIdx)
+ `DAI_MUBI_COV(creator_sw, CreatorSwCfgIdx)
+ `DAI_MUBI_COV(owner_sw, OwnerSwCfgIdx)
+ `DAI_MUBI_COV(hw_cfg, HwCfgIdx)
+ `DAI_MUBI_COV(secret0, Secret0Idx)
+ `DAI_MUBI_COV(secret1, Secret1Idx)
+ `DAI_MUBI_COV(secret2, Secret2Idx)
+
+`undef PART_MUBI_COV
+`undef DAI_MUBI_COV
endmodule
diff --git a/hw/ip/otp_ctrl/dv/env/otp_ctrl_env.sv b/hw/ip/otp_ctrl/dv/env/otp_ctrl_env.sv
index 2275ffe..95c895d 100644
--- a/hw/ip/otp_ctrl/dv/env/otp_ctrl_env.sv
+++ b/hw/ip/otp_ctrl/dv/env/otp_ctrl_env.sv
@@ -79,6 +79,9 @@
`uvm_fatal(`gfn, "failed to get otp_ctrl_vif from uvm_config_db")
end
+ // Check if `NumPart` constant is assigned to the correct value.
+ `DV_CHECK(NumPart == (LifeCycleIdx + 1))
+
endfunction
function void connect_phase(uvm_phase phase);
diff --git a/hw/ip/otp_ctrl/dv/env/otp_ctrl_env_cfg.sv b/hw/ip/otp_ctrl/dv/env/otp_ctrl_env_cfg.sv
index 5c91bba..2c2e5b4 100644
--- a/hw/ip/otp_ctrl/dv/env/otp_ctrl_env_cfg.sv
+++ b/hw/ip/otp_ctrl/dv/env/otp_ctrl_env_cfg.sv
@@ -40,8 +40,9 @@
// TODO: reggen tool optimization. Temp mannual setup for prim_tl_agent.
rand tl_agent_cfg prim_tl_agent_cfg;
+
// Introduce this flag to avoid close source conflict.
- bit create_prim_tl_agent = 1;
+ bit create_prim_tl_agent = 1;
`uvm_object_utils_begin(otp_ctrl_env_cfg)
`uvm_object_utils_end
diff --git a/hw/ip/otp_ctrl/dv/env/otp_ctrl_env_pkg.sv b/hw/ip/otp_ctrl/dv/env/otp_ctrl_env_pkg.sv
index 457414a..c493b0c 100644
--- a/hw/ip/otp_ctrl/dv/env/otp_ctrl_env_pkg.sv
+++ b/hw/ip/otp_ctrl/dv/env/otp_ctrl_env_pkg.sv
@@ -177,6 +177,11 @@
OtpMacroAlert
} otp_alert_e;
+ typedef struct packed {
+ bit read_lock;
+ bit write_lock;
+ } otp_part_access_lock_t;
+
typedef virtual otp_ctrl_if otp_ctrl_vif;
parameter otp_err_code_e OTP_TERMINAL_ERRS[4] = {OtpMacroEccUncorrError,
diff --git a/hw/ip/otp_ctrl/dv/env/otp_ctrl_if.sv b/hw/ip/otp_ctrl/dv/env/otp_ctrl_if.sv
index 620c59c..90aa84b 100644
--- a/hw/ip/otp_ctrl/dv/env/otp_ctrl_if.sv
+++ b/hw/ip/otp_ctrl/dv/env/otp_ctrl_if.sv
@@ -217,6 +217,36 @@
endcase
endtask
+ // This task forces otp_ctrl's internal mubi signals to values that are not mubi::true or mubi::
+ // false. Then scb will check if design treats these values as locking the partition access.
+ task automatic force_part_access_mubi(otp_part_access_lock_t forced_part_access_sel[NumPart-1]);
+ static part_access_t [NumPart-1:0] part_access_val, part_access_dai_val;
+ @(posedge clk_i);
+
+ part_access_val = tb.dut.part_access;
+ part_access_dai_val = tb.dut.part_access_dai;
+
+ foreach (forced_part_access_sel[i]) begin
+ if (forced_part_access_sel[i].read_lock) begin
+ part_access_val[i].read_lock = get_rand_mubi8_val(.t_weight(0), .f_weight(0));
+ part_access_dai_val[i].read_lock = get_rand_mubi8_val(.t_weight(0), .f_weight(0));
+ end
+ if (forced_part_access_sel[i].write_lock) begin
+ part_access_val[i].write_lock = get_rand_mubi8_val(.t_weight(0), .f_weight(0));
+ part_access_dai_val[i].write_lock = get_rand_mubi8_val(.t_weight(0), .f_weight(0));
+ end
+ end
+
+ force tb.dut.part_access = part_access_val;
+ force tb.dut.part_access_dai = part_access_dai_val;
+ endtask
+
+ task automatic release_part_access_mubi();
+ @(posedge clk_i);
+ release tb.dut.part_access;
+ release tb.dut.part_access_dai;
+ endtask
+
// In open source environment, `otp_alert_o` to is tied to 2'b01 (alert_p is 0 and alert_n is 1).
if (`PRIM_DEFAULT_IMPL == prim_pkg::ImplGeneric) `ASSERT(OtpAstAlertO_A, otp_alert_o == 2'b01)
diff --git a/hw/ip/otp_ctrl/dv/env/otp_ctrl_scoreboard.sv b/hw/ip/otp_ctrl/dv/env/otp_ctrl_scoreboard.sv
index 3822b2a..2ccfa76 100644
--- a/hw/ip/otp_ctrl/dv/env/otp_ctrl_scoreboard.sv
+++ b/hw/ip/otp_ctrl/dv/env/otp_ctrl_scoreboard.sv
@@ -120,9 +120,9 @@
@(posedge cfg.otp_ctrl_vif.pwr_otp_done_o || cfg.under_reset ||
cfg.otp_ctrl_vif.alert_reqs) begin
if (!cfg.under_reset && !cfg.otp_ctrl_vif.alert_reqs) begin
- otp_ctrl_part_pkg::otp_hw_cfg_data_t exp_hwcfg_data;
- otp_ctrl_pkg::otp_keymgr_key_t exp_keymgr_data;
- otp_ctrl_pkg::otp_lc_data_t exp_lc_data;
+ otp_ctrl_part_pkg::otp_hw_cfg_data_t exp_hwcfg_data;
+ otp_ctrl_pkg::otp_keymgr_key_t exp_keymgr_data;
+ otp_ctrl_pkg::otp_lc_data_t exp_lc_data;
bit [otp_ctrl_pkg::KeyMgrKeyWidth-1:0] exp_keymgr_key0, exp_keymgr_key1;
if (dai_digest_ip != LifeCycleIdx) begin
diff --git a/hw/ip/otp_ctrl/dv/env/seq_lib/otp_ctrl_base_vseq.sv b/hw/ip/otp_ctrl/dv/env/seq_lib/otp_ctrl_base_vseq.sv
index 0ec4950..7229012 100644
--- a/hw/ip/otp_ctrl/dv/env/seq_lib/otp_ctrl_base_vseq.sv
+++ b/hw/ip/otp_ctrl/dv/env/seq_lib/otp_ctrl_base_vseq.sv
@@ -64,6 +64,7 @@
// Cfg errors are cleared after reset
virtual task apply_reset(string kind = "HARD");
super.apply_reset(kind);
+ cfg.otp_ctrl_vif.release_part_access_mubi();
endtask
virtual task dut_shutdown();
@@ -257,6 +258,61 @@
csr_rd(.ptr(ral.secret2_digest[1]), .value(val));
endtask
+ // If the partition is read/write locked, there is 20% chance we will force the internal mubi
+ // access signal to the values other than mubi::true or mubi::false.
+ virtual task force_mubi_part_access();
+ otp_part_access_lock_t forced_mubi_part_access[NumPart-1];
+
+ if (`gmv(ral.vendor_test_digest[0]) || `gmv(ral.vendor_test_digest[1])) begin
+ if (!$urandom_range(0, 4)) forced_mubi_part_access[VendorTestIdx].write_lock = 1;
+ end
+ if (`gmv(ral.vendor_test_read_lock) == 0) begin
+ if (!$urandom_range(0, 4)) forced_mubi_part_access[VendorTestIdx].read_lock = 1;
+ end
+
+ if (`gmv(ral.creator_sw_cfg_digest[0]) || `gmv(ral.creator_sw_cfg_digest[1])) begin
+ if (!$urandom_range(0, 4)) forced_mubi_part_access[CreatorSwCfgIdx].write_lock = 1;
+ end
+ if (`gmv(ral.creator_sw_cfg_read_lock) == 0) begin
+ if (!$urandom_range(0, 4)) forced_mubi_part_access[CreatorSwCfgIdx].read_lock = 1;
+ end
+
+ if (`gmv(ral.owner_sw_cfg_digest[0]) || `gmv(ral.owner_sw_cfg_digest[1])) begin
+ if ($urandom_range(0, 4) == 0) forced_mubi_part_access[OwnerSwCfgIdx].write_lock = 1;
+ end
+ if (`gmv(ral.owner_sw_cfg_read_lock) == 0) begin
+ if (!$urandom_range(0, 4)) forced_mubi_part_access[OwnerSwCfgIdx].read_lock = 1;
+ end
+
+ if (`gmv(ral.hw_cfg_digest[0]) || `gmv(ral.hw_cfg_digest[1])) begin
+ // TODO: hw_cfg part cannot be read locked.
+ // if (!$urandom_range(0, 4)) cfg.forced_mubi_part_access[HwCfgIdx].read_lock = 1;
+ if (!$urandom_range(0, 4)) forced_mubi_part_access[HwCfgIdx].write_lock = 1;
+ end
+
+ if (`gmv(ral.secret0_digest[0]) || `gmv(ral.secret0_digest[1])) begin
+ if (!$urandom_range(0, 4)) forced_mubi_part_access[Secret0Idx].read_lock = 1;
+ if (!$urandom_range(0, 4)) forced_mubi_part_access[Secret0Idx].write_lock = 1;
+ end
+
+ if (`gmv(ral.secret1_digest[0]) || `gmv(ral.secret1_digest[1])) begin
+ if (!$urandom_range(0, 4)) forced_mubi_part_access[Secret1Idx].read_lock = 1;
+ if (!$urandom_range(0, 4)) forced_mubi_part_access[Secret1Idx].write_lock = 1;
+ end
+
+ if (`gmv(ral.secret2_digest[0]) || `gmv(ral.secret2_digest[1])) begin
+ if (!$urandom_range(0, 4)) forced_mubi_part_access[Secret2Idx].read_lock = 1;
+ if (!$urandom_range(0, 4)) forced_mubi_part_access[Secret2Idx].write_lock = 1;
+ end
+
+ foreach (forced_mubi_part_access[i]) begin
+ `uvm_info(`gfn, $sformatf("partition %0d inject mubi value: read=%0b, write=%0b", i,
+ forced_mubi_part_access[i].read_lock, forced_mubi_part_access[i].write_lock), UVM_HIGH)
+ end
+
+ cfg.otp_ctrl_vif.force_part_access_mubi(forced_mubi_part_access);
+ endtask
+
// This function backdoor inject error according to ecc_err.
// For example, if err_mask is set to 'b01, bit 1 in OTP macro will be flipped.
// This function will output original backdoor read data for the given address.
diff --git a/hw/ip/otp_ctrl/dv/env/seq_lib/otp_ctrl_smoke_vseq.sv b/hw/ip/otp_ctrl/dv/env/seq_lib/otp_ctrl_smoke_vseq.sv
index b304d08..5a4f201 100644
--- a/hw/ip/otp_ctrl/dv/env/seq_lib/otp_ctrl_smoke_vseq.sv
+++ b/hw/ip/otp_ctrl/dv/env/seq_lib/otp_ctrl_smoke_vseq.sv
@@ -118,6 +118,11 @@
end
trigger_checks(.val(check_trigger_val), .wait_done(1), .ecc_err(ecc_chk_err));
+ if ($urandom_range(0, 1) && access_locked_parts) write_sw_rd_locks();
+
+ // Backdoor write mubi to values that are not true or false.
+ force_mubi_part_access();
+
if (do_req_keys && !cfg.otp_ctrl_vif.alert_reqs) begin
req_otbn_key();
req_flash_addr_key();
@@ -130,8 +135,6 @@
if (cfg.otp_ctrl_vif.lc_prog_req == 0) csr_rd(.ptr(ral.err_code[0]), .value(tlul_val));
end
- if ($urandom_range(0, 1) && access_locked_parts) write_sw_rd_locks();
-
for (int i = 0; i < num_dai_op; i++) begin
bit [TL_DW-1:0] rdata0, rdata1, backdoor_rd_val;