[dv/otp_ctrl] support err_code checking
This PR adds support for err_code register checking
Signed-off-by: Cindy Chen <chencindy@google.com>
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 fd55a0e..8bf39e0 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
@@ -123,7 +123,18 @@
OtpCheckPendingIdx
} otp_status_e;
-typedef enum bit [1:0] {
+ typedef enum bit [2:0] {
+ OtpNoError,
+ OtpMacroError,
+ OtpMacroEccCorrError,
+ OtpMacroEccUncorrError,
+ OtpMacroWriteBlankError,
+ OtpAccessError,
+ OtpCheckFailError,
+ OtpFsmStateError
+ } otp_err_code_e;
+
+ typedef enum bit [1:0] {
OtpNoEccErr,
OtpEccCorrErr,
OtpEccUncorrErr
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 313e92f..fadd951 100644
--- a/hw/ip/otp_ctrl/dv/env/otp_ctrl_scoreboard.sv
+++ b/hw/ip/otp_ctrl/dv/env/otp_ctrl_scoreboard.sv
@@ -160,8 +160,8 @@
// LC program request data is valid means no OTP macro error.
`DV_CHECK_EQ(rcv_item.d_data, exp_err_bit)
- if (exp_err_bit) predict_status_err(.dai_err(0), .lc_err(1));
- else exp_status[OtpLciErrIdx] = 0;
+ if (exp_err_bit) predict_err(OtpLciErrIdx, OtpMacroWriteBlankError);
+ else predict_no_err(OtpLciErrIdx);
end
endtask
@@ -441,7 +441,7 @@
// LC partition cannot be access via DAI
if (part_idx == LifeCycleIdx) begin
- predict_status_err(.dai_err(1));
+ predict_err(OtpDaiErrIdx, OtpAccessError);
if (item.a_data == DaiRead) predict_rdata(is_secret(dai_addr), 0, 0);
end else begin
case (item.a_data)
@@ -452,23 +452,26 @@
// However, digest is always readable
if ((part_idx inside {CreatorSwCfgIdx, OwnerSwCfgIdx} && sw_read_lock[part_idx]) ||
(is_secret(dai_addr) && digests[part_idx] != 0) && !is_digest(dai_addr)) begin
- predict_status_err(.dai_err(1));
+ predict_err(OtpDaiErrIdx, OtpAccessError);
predict_rdata(is_secret(dai_addr), 0, 0);
end else begin
bit [TL_AW-1:0] otp_addr = get_scb_otp_addr();
- if (cfg.ecc_err == OtpNoEccErr) predict_dai_idle_status_wo_err();
- else predict_status_err(.dai_err(1));
-
+ if (cfg.ecc_err == OtpNoEccErr) begin
+ predict_no_err(OtpDaiErrIdx);
+ predict_rdata(is_secret(dai_addr) || is_digest(dai_addr),
+ otp_a[otp_addr], otp_a[otp_addr+1]);
+ end else if (cfg.ecc_err == OtpEccCorrErr) begin
+ predict_err(OtpDaiErrIdx, OtpMacroEccCorrError);
+ predict_rdata(is_secret(dai_addr) || is_digest(dai_addr),
+ otp_a[otp_addr], otp_a[otp_addr+1]);
// OTP macro uncorrectable error: DAI interface goes to error state
- if (cfg.ecc_err == OtpEccUncorrErr) begin
+ end else begin
+ predict_err(OtpDaiErrIdx, OtpMacroEccUncorrError);
// Max wait 20 clock cycles because scb did not know when exactly OTP will
// finish reading and reporting the uncorrectable error.
set_exp_alert("fatal_macro_error", 1, 20);
macro_alert_triggered = 1;
predict_rdata(1, 0, 0);
- end else begin
- predict_rdata(is_secret(dai_addr) || is_digest(dai_addr),
- otp_a[otp_addr], otp_a[otp_addr+1]);
end
end
end
@@ -476,9 +479,9 @@
bit[TL_AW-1:0] otp_addr = get_scb_otp_addr();
// check if write locked
if (get_digest_reg_val(part_idx) != 0) begin
- predict_status_err(.dai_err(1));
+ predict_err(OtpDaiErrIdx, OtpAccessError);
end else begin
- predict_dai_idle_status_wo_err();
+ predict_no_err(OtpDaiErrIdx);
// write digest
if (is_sw_digest(dai_addr)) begin
bit [TL_DW*2-1:0] curr_digest, prev_digest;
@@ -492,10 +495,10 @@
// wait a reset
update_sw_digests_to_otp(part_idx);
end else begin
- predict_status_err(.dai_err(1));
+ predict_err(OtpDaiErrIdx, OtpMacroWriteBlankError);
end
end else if (is_digest(dai_addr)) begin
- predict_status_err(.dai_err(1));
+ predict_err(OtpDaiErrIdx, OtpAccessError);
// write OTP memory
end else begin
if (!is_secret(dai_addr)) begin
@@ -504,8 +507,9 @@
if ((otp_a[otp_addr] & wr_data) == otp_a[otp_addr]) begin
otp_a[otp_addr] = wr_data;
check_otp_idle(.val(0), .wait_clks(3));
+ end else begin
+ predict_err(OtpDaiErrIdx, OtpMacroWriteBlankError);
end
- else predict_status_err(.dai_err(1));
end else begin
bit [SCRAMBLE_DATA_SIZE-1:0] secret_data = {otp_a[otp_addr + 1],
otp_a[otp_addr]};
@@ -519,7 +523,7 @@
// wait until secret scrambling is done
check_otp_idle(.val(0), .wait_clks(34));
end else begin
- predict_status_err(.dai_err(1));
+ predict_err(OtpDaiErrIdx, OtpMacroWriteBlankError);
end
end
end
@@ -595,16 +599,17 @@
exp_status[OtpCheckPendingIdx] = 1;
if (`gmv(ral.check_timeout) > 0 && `gmv(ral.check_timeout) <= CHK_TIMEOUT_CYC) begin
set_exp_alert("fatal_check_error", 1, `gmv(ral.check_timeout));
- predict_status_err(.timeout_err(1));
+ predict_err(OtpTimeoutErrIdx);
end
end
end
- "hw_cfg_digest_0", "hw_cfg_digest_1", "", "secret0_digest_0", "secret0_digest_1",
+ "hw_cfg_digest_0", "hw_cfg_digest_1", "secret0_digest_0", "secret0_digest_1",
"secret1_digest_0", "secret1_digest_1", "secret2_digest_0", "secret2_digest_1",
"creator_sw_cfg_digest_0", "creator_sw_cfg_digest_1", "owner_sw_cfg_digest_0",
- "owner_sw_cfg_digest_1", "direct_access_regwen", "direct_access_wdata_0", "check_timeout",
- "direct_access_wdata_1", "direct_access_address", "direct_access_rdata_0", "intr_enable",
- "direct_access_rdata_1", "check_regwen", "check_trigger_regwen", "check_trigger": begin
+ "owner_sw_cfg_digest_1", "direct_access_regwen", "direct_access_wdata_0",
+ "direct_access_wdata_1", "direct_access_address", "direct_access_rdata_0",
+ "direct_access_rdata_1", "check_regwen", "check_trigger_regwen", "check_trigger",
+ "check_timeout", "intr_enable", "err_code": begin
// Do nothing
end
default: begin
@@ -657,8 +662,8 @@
// use negedge to avoid race condition
cfg.clk_rst_vif.wait_n_clks(wait_clks + 1);
`uvm_error(`gfn,
- $sformatf("pwr_otp_idle output is %0b while expect %0b within %0d cycles %0b",
- cfg.otp_ctrl_vif.pwr_otp_idle_o, val, wait_clks, cfg.m_edn_pull_agent_cfg.vif.req))
+ $sformatf("pwr_otp_idle output is %0b while expect %0b within %0d cycles",
+ cfg.otp_ctrl_vif.pwr_otp_idle_o, val, wait_clks))
end
begin
wait(cfg.under_reset || cfg.otp_ctrl_vif.pwr_otp_idle_o == val ||
@@ -748,10 +753,10 @@
if (digests[part_idx] != 0 ||
part_idx inside {CreatorSwCfgIdx, OwnerSwCfgIdx, LifeCycleIdx}) begin
- predict_status_err(.dai_err(1));
+ predict_err(OtpDaiErrIdx, OtpAccessError);
return;
end else begin
- predict_dai_idle_status_wo_err();
+ predict_no_err(OtpDaiErrIdx);
end
case (part_idx)
HwCfgIdx: mem_q = otp_a[HW_CFG_START_ADDR:HW_CFG_END_ADDR];
@@ -865,19 +870,37 @@
dai_addr >> 2;
endfunction
- virtual function void predict_status_err(bit dai_err = 0, bit lc_err = 0, bit timeout_err = 0);
+ // This function predict OTP error related registers: intr_state, status, and err_code
+ virtual function void predict_err(otp_status_e status_err_idx,
+ otp_err_code_e err_code = OtpNoError);
+ // Update intr_state
void'(ral.intr_state.otp_error.predict(.value(1), .kind(UVM_PREDICT_READ)));
- if (dai_err) begin
- exp_status[OtpDaiIdleIdx] = 1;
- exp_status[OtpDaiErrIdx] = 1;
+
+ // Update status
+ exp_status[status_err_idx] = 1;
+
+ // Only first 8 status errors have corresponding err_code
+ if (status_err_idx <= OtpLciErrIdx) begin
+ dv_base_reg_field err_code_flds[$];
+ if (err_code == OtpNoError) begin
+ `uvm_error(`gfn, $sformatf("please set status error: %0s error code", status_err_idx.name))
+ end
+ ral.err_code.get_dv_base_reg_fields(err_code_flds);
+ void'(err_code_flds[status_err_idx].predict(err_code));
end
- if (lc_err) exp_status[OtpLciErrIdx] = 1;
- if (timeout_err) exp_status[OtpTimeoutErrIdx] = 1;
+
endfunction
- virtual function void predict_dai_idle_status_wo_err();
- exp_status[OtpDaiIdleIdx] = 1;
- exp_status[OtpDaiErrIdx] = 0;
+ // TODO: consider combine it with function predict_err()
+ virtual function void predict_no_err(otp_status_e status_err_idx);
+ exp_status[status_err_idx] = 0;
+ if (status_err_idx == OtpDaiErrIdx) exp_status[OtpDaiIdleIdx] = 1;
+
+ if (status_err_idx <= OtpLciErrIdx) begin
+ dv_base_reg_field err_code_flds[$];
+ ral.err_code.get_dv_base_reg_fields(err_code_flds);
+ void'(err_code_flds[status_err_idx].predict(OtpNoError));
+ end
endfunction
virtual function void predict_rdata(bit is_64_bits, bit [TL_DW-1:0] rdata0,
diff --git a/hw/ip/otp_ctrl/dv/env/seq_lib/otp_ctrl_parallel_lc_req_vseq.sv b/hw/ip/otp_ctrl/dv/env/seq_lib/otp_ctrl_parallel_lc_req_vseq.sv
index afd1869..8230aa0 100644
--- a/hw/ip/otp_ctrl/dv/env/seq_lib/otp_ctrl_parallel_lc_req_vseq.sv
+++ b/hw/ip/otp_ctrl/dv/env/seq_lib/otp_ctrl_parallel_lc_req_vseq.sv
@@ -19,6 +19,11 @@
do_lc_trans == 0;
}
+ // disable err_code check because cannot accurately predict when LC error is detected
+ constraint no_err_code_c {
+ check_err_code == 0;
+ }
+
virtual task run_parallel_seq(ref bit base_vseq_done);
forever
begin
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 de3cde6..a636b6a 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
@@ -13,7 +13,7 @@
bit collect_used_addr = 1;
- rand bit do_req_keys, do_lc_trans;
+ rand bit do_req_keys, do_lc_trans, check_err_code;
rand bit access_locked_parts;
rand bit [TL_AW-1:0] dai_addr;
rand bit [TL_DW-1:0] wdata0, wdata1;
@@ -133,11 +133,13 @@
if ($urandom_range(0, 1)) csr_rd(.ptr(ral.direct_access_regwen), .value(tlul_val));
if ($urandom_range(0, 1)) csr_rd(.ptr(ral.status), .value(tlul_val));
+ if (check_err_code) csr_rd(.ptr(ral.err_code), .value(tlul_val));
end
if (do_lc_trans) begin
req_lc_transition(do_lc_trans);
req_lc_token();
+ if (check_err_code) csr_rd(.ptr(ral.err_code), .value(tlul_val));
end
// lock digests
@@ -148,6 +150,8 @@
if ($urandom_range(0, 1)) csr_rd(.ptr(ral.status), .value(tlul_val));
write_sw_rd_locks();
+ if (check_err_code) csr_rd(.ptr(ral.err_code), .value(tlul_val));
+
if ($urandom_range(0, 1)) rd_digests();
dut_init();