[dv/otp_ctrl] initial commit for OTP sanity sequence
This PR supports OTP sanity sequence. This PR covers all the sequence
for DAI interface.
There will be a following PR that supports exercising all interfaces
within OTP.
Signed-off-by: Cindy Chen <chencindy@google.com>
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 4277f77..7cb1f80 100644
--- a/hw/ip/otp_ctrl/dv/env/otp_ctrl_env.sv
+++ b/hw/ip/otp_ctrl/dv/env/otp_ctrl_env.sv
@@ -17,6 +17,10 @@
if (!uvm_config_db#(pwr_otp_vif)::get(this, "", "pwr_otp_vif", cfg.pwr_otp_vif)) begin
`uvm_fatal(get_full_name(), "failed to get pwr_otp_vif from uvm_config_db")
end
+ if (!uvm_config_db#(lc_provision_en_vif)::get(this, "", "lc_provision_en_vif",
+ cfg.lc_provision_en_vif)) begin
+ `uvm_fatal(get_full_name(), "failed to get lc_provision_en_vif from uvm_config_db")
+ end
if (!uvm_config_db#(mem_bkdr_vif)::get(this, "", "mem_bkdr_vif", cfg.mem_bkdr_vif)) begin
`uvm_fatal(`gfn, "failed to get mem_bkdr_vif from uvm_config_db")
end
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 04502fd..e237d4d 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
@@ -5,9 +5,9 @@
class otp_ctrl_env_cfg extends cip_base_env_cfg #(.RAL_T(otp_ctrl_reg_block));
// ext component cfgs
- //pwr_vif pwr_vif;
- pwr_otp_vif pwr_otp_vif;
- mem_bkdr_vif mem_bkdr_vif;
+ pwr_otp_vif pwr_otp_vif;
+ lc_provision_en_vif lc_provision_en_vif;
+ mem_bkdr_vif mem_bkdr_vif;
`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 e8245ef..b1fa13d 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
@@ -12,23 +12,72 @@
import cip_base_pkg::*;
import csr_utils_pkg::*;
import otp_ctrl_ral_pkg::*;
+ import otp_ctrl_pkg::*;
+ import lc_ctrl_pkg::*;
// macro includes
`include "uvm_macros.svh"
`include "dv_macros.svh"
// parameters
+ parameter uint DIGEST_SIZE = 8;
+ parameter uint CREATOR_WINDOW_BASE_ADDR = 'h1000;
+ parameter uint OWNER_WINDOW_BASE_ADDR = 'h1300;
+ parameter uint TEST_ACCESS_BASE_ADDR = 'h2000;
+ parameter uint WINDOW_SIZE = 512 * 4;
+
+ // lc does not have digest
+ parameter bit[10:0] DIGESTS_ADDR [NumPart-1] = {
+ PartInfo[CreatorSwCfgIdx].offset + PartInfo[CreatorSwCfgIdx].size - DIGEST_SIZE,
+ PartInfo[OwnerSwCfgIdx].offset + PartInfo[OwnerSwCfgIdx].size - DIGEST_SIZE,
+ PartInfo[HwCfgIdx].offset + PartInfo[HwCfgIdx].size - DIGEST_SIZE,
+ PartInfo[Secret0Idx].offset + PartInfo[Secret0Idx].size - DIGEST_SIZE,
+ PartInfo[Secret1Idx].offset + PartInfo[Secret1Idx].size - DIGEST_SIZE,
+ PartInfo[Secret2Idx].offset + PartInfo[Secret2Idx].size - DIGEST_SIZE};
// types
typedef virtual pins_if #(3) pwr_otp_vif;
+ typedef virtual pins_if #(4) lc_provision_en_vif;
typedef virtual mem_bkdr_if mem_bkdr_vif;
- typedef enum {
+ typedef enum bit [1:0] {
OtpOperationDone,
- OtpErr
+ OtpErr,
+ NumOtpCtrlIntr
} otp_intr_e;
+ // TODO: needs update once design finalize
+ typedef enum bit [6:0] {
+ OtpCheckPending = 7'b0_000_001,
+ OtpIdle = 7'b0_000_010,
+ OtpKeyFsmErr = 7'b0_000_100,
+ OtpScrmblFsmErr = 7'b0_001_000,
+ OtpLfsrFsmErr = 7'b0_010_000,
+ OtpChkTimeout = 7'b0_100_000,
+ OptPartErrs = 7'b1_000_000
+ } otp_status_e;
+
// functions
+ function automatic int get_part_index(bit [TL_DW-1:0] addr);
+ int index;
+ for (index = 0; index < otp_ctrl_pkg::NumPart; index++) begin
+ if (PartInfo[index].offset > addr) begin
+ index--;
+ break;
+ end
+ end
+ return index;
+ endfunction
+
+ function automatic bit is_secret(bit [TL_DW-1:0] addr);
+ int part_index = get_part_index(addr);
+ if (part_index inside {[Secret0Idx:Secret2Idx]}) return 0;
+ else return 1;
+ endfunction
+
+ function automatic bit [TL_AW-1:0] get_sw_window_addr(bit [TL_AW-1:0] dai_addr);
+ get_sw_window_addr = dai_addr + CREATOR_WINDOW_BASE_ADDR;
+ endfunction
// package sources
`include "otp_ctrl_env_cfg.sv"
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 344d64d..7f635b0 100644
--- a/hw/ip/otp_ctrl/dv/env/otp_ctrl_scoreboard.sv
+++ b/hw/ip/otp_ctrl/dv/env/otp_ctrl_scoreboard.sv
@@ -33,10 +33,11 @@
virtual task process_tl_access(tl_seq_item item, tl_channels_e channel = DataChannel);
uvm_reg csr;
- bit do_read_check = 1'b1;
- bit write = item.is_write();
- uvm_reg_addr_t csr_addr = get_normalized_addr(item.a_addr);
-
+ bit do_read_check = 1'b0;
+ bit write = item.is_write();
+ uvm_reg_addr_t csr_addr = get_normalized_addr(item.a_addr);
+ bit [TL_AW-1:0] addr_mask = cfg.csr_addr_map_size - 1;
+
bit addr_phase_read = (!write && channel == AddrChannel);
bit addr_phase_write = (write && channel == AddrChannel);
bit data_phase_read = (!write && channel == DataChannel);
@@ -46,8 +47,13 @@
if (csr_addr inside {cfg.csr_addrs}) begin
csr = ral.default_map.get_reg_by_offset(csr_addr);
`DV_CHECK_NE_FATAL(csr, null)
- end
- else begin
+ // memories
+ // TODO: memory read check, change hardcoded to parameters once design finalized
+ end else if ((csr_addr & addr_mask) inside
+ {[CREATOR_WINDOW_BASE_ADDR : CREATOR_WINDOW_BASE_ADDR + WINDOW_SIZE],
+ [TEST_ACCESS_BASE_ADDR : TEST_ACCESS_BASE_ADDR + WINDOW_SIZE]}) begin
+ return;
+ end else begin
`uvm_fatal(`gfn, $sformatf("Access unexpected addr 0x%0h", csr_addr))
end
@@ -72,7 +78,7 @@
// FIXME
end
default: begin
- `uvm_fatal(`gfn, $sformatf("invalid csr: %0s", csr.get_full_name()))
+ //`uvm_fatal(`gfn, $sformatf("invalid csr: %0s", csr.get_full_name()))
end
endcase
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 d543184..bee784a 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
@@ -13,10 +13,15 @@
// various knobs to enable certain routines
bit do_otp_ctrl_init = 1'b1;
+ rand bit [NumOtpCtrlIntr-1:0] en_intr;
+
`uvm_object_new
virtual task dut_init(string reset_kind = "HARD");
- super.dut_init();
+ super.dut_init(reset_kind);
+ cfg.pwr_otp_vif.drive_pin(0, 0);
+ cfg.lc_provision_en_vif.drive(lc_ctrl_pkg::Off);
+ // reset power init pin and lc_provision_en pin
if (do_otp_ctrl_init) otp_ctrl_init();
endtask
@@ -27,9 +32,51 @@
// setup basic otp_ctrl features
virtual task otp_ctrl_init();
- cfg.pwr_otp_vif.drive_pin(0, 0);
// reset memory to avoid readout X
cfg.mem_bkdr_vif.clear_mem();
+ csr_wr(ral.intr_enable, en_intr);
+ endtask
+
+ // this task triggers an OTP write sequence via the DAI interface
+ virtual task dai_wr(bit [TL_DW-1:0] addr,
+ bit [TL_DW-1:0] wdata0,
+ bit [TL_DW-1:0] wdata1 = 0);
+ csr_wr(ral.direct_access_address, addr);
+ csr_wr(ral.direct_access_wdata_0, wdata0);
+ if (!is_secret(addr)) csr_wr(ral.direct_access_wdata_1, wdata1);
+ csr_wr(ral.direct_access_cmd, int'(otp_ctrl_pkg::DaiWrite));
+ csr_spinwait(ral.intr_state, 1 << OtpOperationDone);
+ csr_wr(ral.intr_state, 1'b1 << OtpOperationDone);
+ endtask : dai_wr
+
+ // this task triggers an OTP readout sequence via the DAI interface
+ virtual task dai_rd(bit [TL_DW-1:0] addr,
+ output bit [TL_DW-1:0] rdata0,
+ output bit [TL_DW-1:0] rdata1);
+ csr_wr(ral.direct_access_address, addr);
+ csr_wr(ral.direct_access_cmd, int'(otp_ctrl_pkg::DaiRead));
+ csr_spinwait(ral.intr_state, 1 << OtpOperationDone);
+
+ csr_rd(ral.direct_access_rdata_0, rdata0);
+ if (!is_secret(addr)) csr_rd(ral.direct_access_rdata_1, rdata1);
+ csr_wr(ral.intr_state, 1'b1 << OtpOperationDone);
+ endtask : dai_rd
+
+ // this task exercises an OTP digest calculation via the DAI interface
+ virtual task cal_digest(int part_idx);
+ csr_wr(ral.direct_access_address, DIGESTS_ADDR[part_idx]);
+ csr_wr(ral.direct_access_cmd, otp_ctrl_pkg::DaiDigest);
+ csr_spinwait(ral.intr_state, 1 << OtpOperationDone);
+ csr_wr(ral.intr_state, 1 << OtpOperationDone);
+ endtask
+
+ // this task provisions all HW partitions
+ // SW partitions could not be provisioned via DAI interface
+ // LC partitions cannot be locked
+ virtual task cal_hw_digests();
+ for (int i = int'(HwCfgIdx); i < int'(LifeCycleIdx); i++) begin
+ cal_digest(i);
+ end
endtask
endclass : otp_ctrl_base_vseq
diff --git a/hw/ip/otp_ctrl/dv/env/seq_lib/otp_ctrl_sanity_vseq.sv b/hw/ip/otp_ctrl/dv/env/seq_lib/otp_ctrl_sanity_vseq.sv
index 1e5f84a..3392e9d 100644
--- a/hw/ip/otp_ctrl/dv/env/seq_lib/otp_ctrl_sanity_vseq.sv
+++ b/hw/ip/otp_ctrl/dv/env/seq_lib/otp_ctrl_sanity_vseq.sv
@@ -3,17 +3,101 @@
// SPDX-License-Identifier: Apache-2.0
// basic sanity test vseq
+`define PART_ADDR_RANGE(i) \
+ {[PartInfo[``i``].offset : (PartInfo[``i``].offset + PartInfo[``i``].size - DIGEST_SIZE - 1)]}
+
class otp_ctrl_sanity_vseq extends otp_ctrl_base_vseq;
`uvm_object_utils(otp_ctrl_sanity_vseq)
`uvm_object_new
+ randc bit [TL_AW-1:0] dai_addr;
+ rand bit [TL_DW-1:0] wdata0, wdata1;
+ rand int num_dai_wr;
+ rand otp_ctrl_pkg::part_idx_e part_idx;
+
+ // TODO: temp -> no life-cycle partition involved
+ constraint partition_index_c {
+ part_idx inside {[CreatorSwCfgIdx:Secret2Idx]};
+ }
+
+ constraint dai_addr_c {
+ if (part_idx == CreatorSwCfgIdx) dai_addr inside `PART_ADDR_RANGE(CreatorSwCfgIdx);
+ if (part_idx == OwnerSwCfgIdx) dai_addr inside `PART_ADDR_RANGE(OwnerSwCfgIdx);
+ if (part_idx == HwCfgIdx) dai_addr inside `PART_ADDR_RANGE(HwCfgIdx);
+ if (part_idx == Secret0Idx) dai_addr inside `PART_ADDR_RANGE(Secret0Idx);
+ if (part_idx == Secret1Idx) dai_addr inside `PART_ADDR_RANGE(Secret1Idx);
+ if (part_idx == Secret2Idx) dai_addr inside `PART_ADDR_RANGE(Secret2Idx);
+ if (part_idx == LifeCycleIdx) dai_addr inside `PART_ADDR_RANGE(LifeCycleIdx);
+
+ dai_addr % 4 == 0;
+ if (part_idx inside {[Secret0Idx:Secret2Idx]}) dai_addr % 8 == 0;
+ }
+
+ constraint num_dai_wr_c {num_dai_wr inside {[1:20]};}
+
+ virtual task dut_init(string reset_kind = "HARD");
+ super.dut_init(reset_kind);
+ // drive pwr_otp_req pin
+ cfg.pwr_otp_vif.drive_pin(0, 1);
+ wait(cfg.pwr_otp_vif.pins[2] == 1);
+ cfg.lc_provision_en_vif.drive(lc_ctrl_pkg::On);
+ endtask
+
virtual task pre_start();
super.pre_start();
+ num_dai_wr.rand_mode(0);
endtask
task body();
+ for (int i = 1; i <= num_trans; i++) begin
+ bit [TL_DW-1:0] rdata, tlul_rdata;
+ `uvm_info(`gfn, $sformatf("starting seq %0d/%0d", i, num_trans), UVM_MEDIUM)
+ do_otp_ctrl_init = 1;
+ dut_init();
+ do_otp_ctrl_init = 0;
+
+ // after otp-init done, check status
+ cfg.clk_rst_vif.wait_clks(1);
+ csr_rd_check(.ptr(ral.status), .compare_value(OtpIdle));
+
+ for (int i = 0; i < num_dai_wr; i++) begin
+ bit [TL_DW-1:0] rdata0, rdata1;
+ `DV_CHECK_RANDOMIZE_FATAL(this)
+
+ // OTP write via DAI
+ dai_wr(dai_addr, wdata0, wdata1);
+
+ // OTP read via DAI
+ dai_rd(dai_addr, rdata0, rdata1);
+
+ // check read data
+ `DV_CHECK_EQ(wdata0, rdata0, $sformatf("read data0 mismatch at addr %0h", dai_addr))
+ if (!is_secret(dai_addr)) begin
+ `DV_CHECK_EQ(wdata1, rdata1, $sformatf("read data1 mismatch at addr %0h", dai_addr))
+ end
+
+ // if write sw partitions, check tlul window
+ if (part_idx inside {CreatorSwCfgIdx, OwnerSwCfgIdx}) begin
+ bit [TL_AW-1:0] tlul_addr = get_sw_window_addr(dai_addr);
+
+ // random issue reset, OTP content should not be cleared
+ if ($urandom_range(0, 1)) dut_init();
+ tl_access(.addr(tlul_addr), .write(0), .data(tlul_rdata), .blocking(1));
+ `DV_CHECK_EQ(tlul_rdata, rdata0, $sformatf("mem read out mismatch at addr %0h", tlul_addr))
+ end
+ end
+
+ // check no error
+ csr_rd_check(.ptr(ral.status), .compare_value(OtpIdle));
+
+ // lock HW digests
+ `uvm_info(`gfn, "Trigger HW digest calculation", UVM_HIGH)
+ cal_hw_digests();
+ csr_rd_check(.ptr(ral.status), .compare_value(OtpIdle));
+ end
endtask : body
endclass : otp_ctrl_sanity_vseq
+`undef PART_ADDR_RANGE
diff --git a/hw/ip/otp_ctrl/dv/tb.sv b/hw/ip/otp_ctrl/dv/tb.sv
index ab971ec..f22803e 100644
--- a/hw/ip/otp_ctrl/dv/tb.sv
+++ b/hw/ip/otp_ctrl/dv/tb.sv
@@ -15,6 +15,7 @@
wire clk, rst_n;
wire devmode;
+ wire [3:0] lc_provision_en;
// TODO: use standard req/rsp agent
wire [2:0] pwr_otp;
wire [NUM_MAX_INTERRUPTS-1:0] interrupts;
@@ -26,6 +27,7 @@
pins_if #(1) devmode_if(devmode);
// TODO: use standard req/rsp agent
pins_if #(3) pwr_otp_if(pwr_otp);
+ pins_if #(4) lc_provision_en_if(lc_provision_en);
tl_if tl_if(.clk(clk), .rst_n(rst_n));
// dut
@@ -38,19 +40,40 @@
// interrupt
.intr_otp_operation_done_o (intr_otp_operation_done),
.intr_otp_error_o (intr_otp_error),
- // TODO: add remaining IOs and hook them
+ // alert
.alert_rx_i ('0),
- .otp_edn_rsp_i ('0),
- .pwr_otp_req_i (pwr_otp[0]),
- .pwr_otp_rsp_o (pwr_otp[2:1]),
- .lc_otp_program_req_i ('0),
- .lc_otp_token_req_i ('0),
- .lc_escalate_en_i ('0),
- .lc_provision_en_i ('0),
+ .alert_tx_o ( ),
+ // ast
+ .otp_ast_pwr_seq_o ( ),
+ .otp_ast_pwr_seq_h_i ('0),
+ // edn
+ .otp_edn_o ( ),
+ .otp_edn_i ('0),
+ // pwrmgr
+ .pwr_otp_i (pwr_otp[0]),
+ .pwr_otp_o (pwr_otp[2:1]),
+ // lc
+ .lc_otp_program_i ('0),
+ .lc_otp_program_o ( ),
+ .lc_otp_token_i ('0),
+ .lc_otp_token_o ( ),
+ .lc_escalate_en_i (lc_ctrl_pkg::Off),
+ .lc_provision_en_i (lc_provision_en),
.lc_dft_en_i ('0),
- .flash_otp_key_req_i ('0),
- .sram_otp_key_req_i ('0),
- .otbn_otp_key_req_i ('0)
+ .otp_lc_data_o ( ),
+ // keymgr
+ .otp_keymgr_key_o ( ),
+ // flash
+ .flash_otp_key_i ('0),
+ .flash_otp_key_o ( ),
+ // sram
+ .sram_otp_key_i ('0),
+ .sram_otp_key_o ( ),
+ // otbn
+ .otbn_otp_key_i ('0),
+ .otbn_otp_key_o ( ),
+ // hw cfg
+ .hw_cfg_o ( )
);
// bind mem_bkdr_if
@@ -69,8 +92,11 @@
uvm_config_db#(intr_vif)::set(null, "*.env", "intr_vif", intr_if);
uvm_config_db#(pwr_otp_vif)::set(null, "*.env", "pwr_otp_vif", pwr_otp_if);
uvm_config_db#(devmode_vif)::set(null, "*.env", "devmode_vif", devmode_if);
+ uvm_config_db#(lc_provision_en_vif)::set(null, "*.env", "lc_provision_en_vif",
+ lc_provision_en_if);
uvm_config_db#(virtual tl_if)::set(null, "*.env.m_tl_agent*", "vif", tl_if);
- uvm_config_db#(mem_bkdr_vif)::set(null, "*.env", "mem_bkdr_vif", `OTP_CTRL_MEM_HIER.mem_bkdr_if);
+ uvm_config_db#(mem_bkdr_vif)::set(null, "*.env", "mem_bkdr_vif",
+ `OTP_CTRL_MEM_HIER.mem_bkdr_if);
$timeformat(-12, 0, " ps", 12);
run_test();
end