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