[dv/otp_ctrl] otp init failure test
This PR adds a otp_init failure test. This test will cause otp init
failure by creating init check errors. Scb is disabled in this test.
Signed-off-by: Cindy Chen <chencindy@google.com>
diff --git a/hw/ip/otp_ctrl/data/otp_ctrl_testplan.hjson b/hw/ip/otp_ctrl/data/otp_ctrl_testplan.hjson
index 6cb9c99..ba6d9c4 100644
--- a/hw/ip/otp_ctrl/data/otp_ctrl_testplan.hjson
+++ b/hw/ip/otp_ctrl/data/otp_ctrl_testplan.hjson
@@ -72,6 +72,23 @@
tests: ["otp_ctrl_check_fail"]
}
{
+ name: init_fail
+ desc: '''
+ This test is based on OTP_CTRL smoke test and creats OTP_CTRL's initialization failure:
+ - Write and read OTP memory via DAI interface
+ - Lock HW partitions
+ - Keep writing to OTP memory via DAI interface
+ - Issue reset and power initialization
+
+ This test will check:
+ - Otp_initialization failure triggers fatal alert
+ - Status register reflect the correct error
+ - Otp_ctrl's power init output stays 0
+ '''
+ milestone: V2
+ tests: ["otp_ctrl_init_fail"]
+ }
+ {
name: regwen_during_otp_init
desc: '''
DIRECT_ACCESS_REGWEN is RO reg and it gates bunch of write access of other registers,
diff --git a/hw/ip/otp_ctrl/dv/env/otp_ctrl_env.core b/hw/ip/otp_ctrl/dv/env/otp_ctrl_env.core
index 2ca7b2a..f381319 100644
--- a/hw/ip/otp_ctrl/dv/env/otp_ctrl_env.core
+++ b/hw/ip/otp_ctrl/dv/env/otp_ctrl_env.core
@@ -25,6 +25,7 @@
- seq_lib/otp_ctrl_wake_up_vseq.sv: {is_include_file: true}
- seq_lib/otp_ctrl_smoke_vseq.sv: {is_include_file: true}
- seq_lib/otp_ctrl_partition_walk_vseq.sv: {is_include_file: true}
+ - seq_lib/otp_ctrl_init_fail_vseq.sv: {is_include_file: true}
- seq_lib/otp_ctrl_dai_lock_vseq.sv: {is_include_file: true}
- seq_lib/otp_ctrl_dai_errs_vseq.sv: {is_include_file: true}
- seq_lib/otp_ctrl_macro_errs_vseq.sv: {is_include_file: true}
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 95a121f..fdaf3f0 100644
--- a/hw/ip/otp_ctrl/dv/env/otp_ctrl_scoreboard.sv
+++ b/hw/ip/otp_ctrl/dv/env/otp_ctrl_scoreboard.sv
@@ -76,71 +76,73 @@
// 2. After reset deasserted, if power otp_init request is on, and if testbench uses backdoor to
// clear OTP memory to all zeros, clear all digests and re-calculate secret partitions
virtual task process_otp_power_up();
- forever begin
- @(posedge cfg.otp_ctrl_vif.pwr_otp_init_i) begin
- if (cfg.backdoor_clear_mem && cfg.en_scb) begin
- bit [SCRAMBLE_DATA_SIZE-1:0] data = descramble_data(0, Secret0Idx);
- otp_a = '{default:0};
- otp_lc_data = '{default:0};
- // secret partitions have been scrambled before writing to OTP.
- // here calculate the pre-srambled raw data when clearing internal OTP to all 0s.
- for (int i = SECRET0_START_ADDR; i <= SECRET0_END_ADDR; i++) begin
- otp_a[i] = ((i - SECRET0_START_ADDR) % 2) ? data[SCRAMBLE_DATA_SIZE-1:TL_DW] :
- data[TL_DW-1:0];
+ if (cfg.en_scb) begin
+ forever begin
+ @(posedge cfg.otp_ctrl_vif.pwr_otp_init_i) begin
+ if (cfg.backdoor_clear_mem) begin
+ bit [SCRAMBLE_DATA_SIZE-1:0] data = descramble_data(0, Secret0Idx);
+ otp_a = '{default:0};
+ otp_lc_data = '{default:0};
+ // secret partitions have been scrambled before writing to OTP.
+ // here calculate the pre-srambled raw data when clearing internal OTP to all 0s.
+ for (int i = SECRET0_START_ADDR; i <= SECRET0_END_ADDR; i++) begin
+ otp_a[i] = ((i - SECRET0_START_ADDR) % 2) ? data[SCRAMBLE_DATA_SIZE-1:TL_DW] :
+ data[TL_DW-1:0];
+ end
+ data = descramble_data(0, Secret1Idx);
+ for (int i = SECRET1_START_ADDR; i <= SECRET1_END_ADDR; i++) begin
+ otp_a[i] = ((i - SECRET1_START_ADDR) % 2) ? data[SCRAMBLE_DATA_SIZE-1:TL_DW] :
+ data[TL_DW-1:0];
+ end
+ data = descramble_data(0, Secret2Idx);
+ for (int i = SECRET2_START_ADDR; i <= SECRET2_END_ADDR; i++) begin
+ otp_a[i] = ((i - SECRET2_START_ADDR) % 2) ? data[SCRAMBLE_DATA_SIZE-1:TL_DW] :
+ data[TL_DW-1:0];
+ end
+ predict_digest_csrs();
+ `uvm_info(`gfn, "clear internal memory and digest", UVM_HIGH)
end
- data = descramble_data(0, Secret1Idx);
- for (int i = SECRET1_START_ADDR; i <= SECRET1_END_ADDR; i++) begin
- otp_a[i] = ((i - SECRET1_START_ADDR) % 2) ? data[SCRAMBLE_DATA_SIZE-1:TL_DW] :
- data[TL_DW-1:0];
- end
- data = descramble_data(0, Secret2Idx);
- for (int i = SECRET2_START_ADDR; i <= SECRET2_END_ADDR; i++) begin
- otp_a[i] = ((i - SECRET2_START_ADDR) % 2) ? data[SCRAMBLE_DATA_SIZE-1:TL_DW] :
- data[TL_DW-1:0];
- end
- predict_digest_csrs();
- `uvm_info(`gfn, "clear internal memory and digest", UVM_HIGH)
end
- end
- @(posedge cfg.otp_ctrl_vif.pwr_otp_done_o || cfg.under_reset) begin
- if (!cfg.under_reset) begin
- otp_ctrl_part_pkg::otp_hw_cfg_data_t exp_hwcfg_data;
- otp_ctrl_pkg::otp_keymgr_key_t exp_keymgr_data;
- bit [otp_ctrl_pkg::KeyMgrKeyWidth-1:0] exp_keymgr_key0, exp_keymgr_key1;
+ @(posedge cfg.otp_ctrl_vif.pwr_otp_done_o || cfg.under_reset) begin
+ if (!cfg.under_reset) begin
+ otp_ctrl_part_pkg::otp_hw_cfg_data_t exp_hwcfg_data;
+ otp_ctrl_pkg::otp_keymgr_key_t exp_keymgr_data;
+ bit [otp_ctrl_pkg::KeyMgrKeyWidth-1:0] exp_keymgr_key0, exp_keymgr_key1;
- predict_digest_csrs();
+ predict_digest_csrs();
- if (cfg.otp_ctrl_vif.lc_esc_on == 0) begin
- // Dai access is unlocked because the power init is done
- void'(ral.direct_access_regwen.predict(1));
+ if (cfg.otp_ctrl_vif.lc_esc_on == 0) begin
+ // Dai access is unlocked because the power init is done
+ void'(ral.direct_access_regwen.predict(1));
- // Dai idle is set because the otp init is done
- exp_status[OtpDaiIdleIdx] = 1;
- end
+ // Dai idle is set because the otp init is done
+ exp_status[OtpDaiIdleIdx] = 1;
+ end
- // Hwcfg_o gets data from OTP HW cfg partition
- exp_hwcfg_data = cfg.otp_ctrl_vif.lc_esc_on ?
- otp_ctrl_part_pkg::PartInvDefault[HwCfgOffset*8 +: HwCfgSize*8] :
- otp_hw_cfg_data_t'({<<32 {otp_a[HwCfgOffset/4 +: HwCfgSize/4]}});
- `DV_CHECK_EQ(cfg.otp_ctrl_vif.otp_hw_cfg_o.data, exp_hwcfg_data)
+ // Hwcfg_o gets data from OTP HW cfg partition
+ exp_hwcfg_data = cfg.otp_ctrl_vif.lc_esc_on ?
+ otp_ctrl_part_pkg::PartInvDefault[HwCfgOffset*8 +: HwCfgSize*8] :
+ otp_hw_cfg_data_t'({<<32 {otp_a[HwCfgOffset/4 +: HwCfgSize/4]}});
+ `DV_CHECK_EQ(cfg.otp_ctrl_vif.otp_hw_cfg_o.data, exp_hwcfg_data)
- // Otp_keymgr outputs creator root key shares from the secret2 partition.
- // Depends on lc_seed_hw_rd_en_i, it will output the real keys or a constant
- exp_keymgr_data.valid = get_otp_digest_val(Secret2Idx) != 0;
- if (cfg.otp_ctrl_vif.lc_seed_hw_rd_en_i == lc_ctrl_pkg::On) begin
- exp_keymgr_data.key_share0 =
- {<<32 {otp_a[CreatorRootKeyShare0Offset/4 +: CreatorRootKeyShare0Size/4]}};
- exp_keymgr_data.key_share1 =
- {<<32 {otp_a[CreatorRootKeyShare1Offset/4 +: CreatorRootKeyShare1Size/4]}};
- end else begin
- exp_keymgr_data.key_share0 =
- PartInvDefault[CreatorRootKeyShare0Offset*8 +: CreatorRootKeyShare0Size*8];
- exp_keymgr_data.key_share1 =
- PartInvDefault[CreatorRootKeyShare1Offset*8 +: CreatorRootKeyShare1Size*8];
- end
- // Check keymgr_key_o in otp_ctrl_if
- if (cfg.otp_ctrl_vif.lc_esc_on == 0) begin
- `DV_CHECK_EQ(cfg.otp_ctrl_vif.keymgr_key_o, exp_keymgr_data)
+ // Otp_keymgr outputs creator root key shares from the secret2 partition.
+ // Depends on lc_seed_hw_rd_en_i, it will output the real keys or a constant
+ exp_keymgr_data.valid = get_otp_digest_val(Secret2Idx) != 0;
+ if (cfg.otp_ctrl_vif.lc_seed_hw_rd_en_i == lc_ctrl_pkg::On) begin
+ exp_keymgr_data.key_share0 =
+ {<<32 {otp_a[CreatorRootKeyShare0Offset/4 +: CreatorRootKeyShare0Size/4]}};
+ exp_keymgr_data.key_share1 =
+ {<<32 {otp_a[CreatorRootKeyShare1Offset/4 +: CreatorRootKeyShare1Size/4]}};
+ end else begin
+ exp_keymgr_data.key_share0 =
+ PartInvDefault[CreatorRootKeyShare0Offset*8 +: CreatorRootKeyShare0Size*8];
+ exp_keymgr_data.key_share1 =
+ PartInvDefault[CreatorRootKeyShare1Offset*8 +: CreatorRootKeyShare1Size*8];
+ end
+ // Check keymgr_key_o in otp_ctrl_if
+ if (cfg.otp_ctrl_vif.lc_esc_on == 0) begin
+ `DV_CHECK_EQ(cfg.otp_ctrl_vif.keymgr_key_o, exp_keymgr_data)
+ end
end
end
end
diff --git a/hw/ip/otp_ctrl/dv/env/seq_lib/otp_ctrl_init_fail_vseq.sv b/hw/ip/otp_ctrl/dv/env/seq_lib/otp_ctrl_init_fail_vseq.sv
new file mode 100644
index 0000000..8d9cdd9
--- /dev/null
+++ b/hw/ip/otp_ctrl/dv/env/seq_lib/otp_ctrl_init_fail_vseq.sv
@@ -0,0 +1,99 @@
+// Copyright lowRISC contributors.
+// Licensed under the Apache License, Version 2.0, see LICENSE for details.
+// SPDX-License-Identifier: Apache-2.0
+
+// Otp_ctrl_init_fail_vseq is developed to check if OTP_CTRL reacts correctly if initialization
+// failed.
+// Note that coreboard is disabled in this test and all checks are done within sequence.
+// This test writes and reads to OTP_memory via DAI interface, then triggers digest calculations.
+// Afterwards instead of issuing reset, this sequence continues to write to DAI interface.
+// If any of the hardware partition is updated, then in next power cycle, the initialization will
+// fail.
+// This sequence will check the following items:
+// - Otp_initialization failure triggers fatal alert
+// - Status register reflect the correct error
+// - Otp_ctrl's power init output stays 0
+
+class otp_ctrl_init_fail_vseq extends otp_ctrl_smoke_vseq;
+ `uvm_object_utils(otp_ctrl_init_fail_vseq)
+
+ `uvm_object_new
+
+ rand uint num_to_lock_digests;
+ bit [NumPart-1:0] init_err;
+
+ constraint lock_digest_c {num_to_lock_digests < num_dai_op;}
+ constraint num_iterations_c {num_dai_op inside {[20:100]};}
+
+ virtual task pre_start();
+ super.pre_start();
+ num_to_lock_digests.rand_mode(0);
+ endtask
+
+ task body();
+ for (uint i = 1; i <= num_dai_op; i++) begin
+ bit [TL_DW-1:0] tlul_val;
+
+ `DV_CHECK_RANDOMIZE_FATAL(this)
+
+ // recalculate part_idx in case some test turn off constraint dai_wr_legal_addr_c
+ part_idx = get_part_index(dai_addr);
+ `uvm_info(`gfn, $sformatf("starting dai access seq %0d/%0d with addr %0h in partition %0d %0d",
+ i, num_dai_op, dai_addr, part_idx, num_to_lock_digests), UVM_MEDIUM)
+
+ // OTP write via DAI
+ dai_wr(dai_addr, wdata0, wdata1);
+ used_dai_addr_q.push_back(dai_addr);
+
+ if (i > num_to_lock_digests && part_idx inside {[HwCfgIdx: Secret2Idx]}) begin
+ init_err[part_idx] = 1;
+ end
+
+ // OTP read via DAI, check data in scb
+ dai_rd_check(dai_addr, wdata0, wdata1);
+
+ // If write sw partitions, check tlul window
+ if (is_sw_part(dai_addr)) begin
+ uvm_reg_addr_t tlul_addr = cfg.ral.get_addr_from_offset(get_sw_window_offset(dai_addr));
+ tl_access(.addr(tlul_addr), .write(0), .data(tlul_val), .blocking(1), .check_rsp(1),
+ .check_exp_data(1), .exp_data(wdata0));
+ end
+
+ if (i == num_to_lock_digests) cal_hw_digests('1);
+
+ csr_rd_check(.ptr(ral.status), .compare_value(1'b1 << OtpDaiIdleIdx));
+ end
+
+ do_otp_ctrl_init = 0;
+ do_otp_pwr_init = (init_err == 0) ? 1 : 0;
+ dut_init();
+
+ if (init_err) begin
+ bit [TL_DW-1:0] exp_status;
+
+ cfg.otp_ctrl_vif.drive_pwr_otp_init(1);
+ // Wait until OTP_INIT process the error
+ wait(cfg.m_alert_agent_cfg["fatal_check_error"].vif.alert_tx_final.alert_p);
+ check_fatal_alert_nonblocking("fatal_check_error");
+
+ cfg.otp_ctrl_vif.drive_pwr_otp_init(0);
+
+ // Wait until all partitions finish initialization
+ cfg.clk_rst_vif.wait_clks($urandom_range(2000, 4000));
+
+ foreach(init_err[i]) begin
+ if (init_err[i]) exp_status |= 1'b1 << i;
+ end
+ csr_rd_check(.ptr(ral.status), .compare_value(exp_status));
+
+ `DV_CHECK_EQ(cfg.otp_ctrl_vif.pwr_otp_done_o, 0)
+ `DV_CHECK_EQ(cfg.otp_ctrl_vif.pwr_otp_idle_o, 0)
+
+ // Issue reset to stop fatal alert
+ dut_init();
+
+ end else begin
+ csr_rd_check(.ptr(ral.status), .compare_value(1'b1 << OtpDaiIdleIdx));
+ end
+ endtask
+endclass
diff --git a/hw/ip/otp_ctrl/dv/env/seq_lib/otp_ctrl_vseq_list.sv b/hw/ip/otp_ctrl/dv/env/seq_lib/otp_ctrl_vseq_list.sv
index 9e466da..cdd70f7 100644
--- a/hw/ip/otp_ctrl/dv/env/seq_lib/otp_ctrl_vseq_list.sv
+++ b/hw/ip/otp_ctrl/dv/env/seq_lib/otp_ctrl_vseq_list.sv
@@ -7,6 +7,7 @@
`include "otp_ctrl_smoke_vseq.sv"
`include "otp_ctrl_common_vseq.sv"
`include "otp_ctrl_partition_walk_vseq.sv"
+`include "otp_ctrl_init_fail_vseq.sv"
`include "otp_ctrl_dai_lock_vseq.sv"
`include "otp_ctrl_dai_errs_vseq.sv"
`include "otp_ctrl_macro_errs_vseq.sv"
diff --git a/hw/ip/otp_ctrl/dv/otp_ctrl_sim_cfg.hjson b/hw/ip/otp_ctrl/dv/otp_ctrl_sim_cfg.hjson
index b86fe5b..b1c97d7 100644
--- a/hw/ip/otp_ctrl/dv/otp_ctrl_sim_cfg.hjson
+++ b/hw/ip/otp_ctrl/dv/otp_ctrl_sim_cfg.hjson
@@ -66,6 +66,12 @@
}
{
+ name: otp_ctrl_init_fail
+ uvm_test_seq: otp_ctrl_init_fail_vseq
+ run_opts: ["+en_scb=0"]
+ }
+
+ {
name: otp_ctrl_parallel_lc_req
uvm_test_seq: otp_ctrl_parallel_lc_req_vseq
}