[otp_ctrl] Disable OTP consistency check timeout during prog ops
This is done to avoid spurious consistency check timeouts. Programming
ops are much slower than readout operations in the OTP macro, and can
hence interfere with the consistency check timeout mechanism.
Note that this does not disable the consistency checks, and it also does
not affect integrity checks.
Signed-off-by: Michael Schaffner <msf@opentitan.org>
diff --git a/hw/ip/otp_ctrl/rtl/otp_ctrl.sv b/hw/ip/otp_ctrl/rtl/otp_ctrl.sv
index 4176a4d..95b4f15 100644
--- a/hw/ip/otp_ctrl/rtl/otp_ctrl.sv
+++ b/hw/ip/otp_ctrl/rtl/otp_ctrl.sv
@@ -303,7 +303,7 @@
// DAI-related CSRs //
//////////////////////
- logic dai_idle;
+ logic dai_idle, dai_prog_idle;
logic dai_req;
dai_cmd_e dai_cmd;
logic [OtpByteAddrWidth-1:0] dai_addr;
@@ -330,7 +330,7 @@
// power manager. This signal is flopped here as it has to
// cross a clock boundary to the power manager.
logic lci_idle, otp_idle_d, otp_idle_q;
- assign otp_idle_d = lci_idle & dai_idle;
+ assign otp_idle_d = lci_idle & dai_prog_idle;
assign pwr_otp_o.otp_idle = otp_idle_q;
always_ff @(posedge clk_i or negedge rst_ni) begin : p_idle_reg
@@ -491,6 +491,13 @@
// activated depends on the CSR configuration (by default
// they are switched off).
.timer_en_i ( pwr_otp_o.otp_done ),
+ // This idle signal is the same that is output to the power
+ // manager, and indicates whether there is an ongoing OTP programming
+ // operation. It is used to pause the consistency check timeout
+ // counter in order to prevent spurious timeouts (OTP programming
+ // operations are very slow compared to readout operations and can
+ // hence interfere with the timeout mechanism).
+ .otp_prog_busy_i ( ~otp_idle_d ),
.integ_chk_trig_i ( integ_chk_trig ),
.cnsty_chk_trig_i ( cnsty_chk_trig ),
.chk_pending_o ( chk_pending ),
@@ -795,6 +802,7 @@
.dai_req_i ( dai_req ),
.dai_wdata_i ( dai_wdata ),
.dai_idle_o ( dai_idle ),
+ .dai_prog_idle_o ( dai_prog_idle ),
.dai_cmd_done_o ( otp_operation_done ),
.dai_rdata_o ( dai_rdata ),
.otp_req_o ( part_otp_arb_req[DaiIdx] ),
diff --git a/hw/ip/otp_ctrl/rtl/otp_ctrl_dai.sv b/hw/ip/otp_ctrl/rtl/otp_ctrl_dai.sv
index a788dd6..3788be2 100644
--- a/hw/ip/otp_ctrl/rtl/otp_ctrl_dai.sv
+++ b/hw/ip/otp_ctrl/rtl/otp_ctrl_dai.sv
@@ -34,8 +34,9 @@
input dai_cmd_e dai_cmd_i,
input logic dai_req_i,
input [NumDaiWords-1:0][31:0] dai_wdata_i,
- output logic dai_idle_o, // wired to the status CSRs
- output logic dai_cmd_done_o, // this is used to raise an IRQ
+ output logic dai_idle_o, // wired to the status CSRs
+ output logic dai_prog_idle_o, // wired to lfsr timer and pwrmgr
+ output logic dai_cmd_done_o, // this is used to raise an IRQ
output logic [NumDaiWords-1:0][31:0] dai_rdata_o,
// OTP interface
output logic otp_req_o,
@@ -163,6 +164,7 @@
// DAI signals
dai_idle_o = 1'b0;
+ dai_prog_idle_o = 1'b1;
dai_cmd_done_o = 1'b0;
// OTP signals
@@ -358,6 +360,7 @@
// check is not needed in that case. The LC partition is
// permanently write locked and can hence not be written via the DAI.
WriteSt: begin
+ dai_prog_idle_o = 1'b0;
if (part_access_i[part_idx].write_lock == Unlocked &&
// If this is a HW digest write to a buffered partition.
((PartInfo[part_idx].variant == Buffered && PartInfo[part_idx].hw_digest &&
@@ -385,6 +388,7 @@
// OTP transaction fails, latch the OTP error code, and jump to
// terminal error state.
WriteWaitSt: begin
+ dai_prog_idle_o = 1'b0;
// Continuously check write access and bail out if this is not consistent.
if (part_access_i[part_idx].write_lock == Unlocked &&
// If this is a HW digest write to a buffered partition.
diff --git a/hw/ip/otp_ctrl/rtl/otp_ctrl_lfsr_timer.sv b/hw/ip/otp_ctrl/rtl/otp_ctrl_lfsr_timer.sv
index f6960eb..45e984a 100644
--- a/hw/ip/otp_ctrl/rtl/otp_ctrl_lfsr_timer.sv
+++ b/hw/ip/otp_ctrl/rtl/otp_ctrl_lfsr_timer.sv
@@ -44,6 +44,7 @@
input edn_ack_i, // ack from EDN
input [EdnDataWidth-1:0] edn_data_i, // from EDN
input timer_en_i, // enable timer
+ input otp_prog_busy_i, // indicates whether prog ops are in progress
input integ_chk_trig_i, // one-off trigger for integrity check
input cnsty_chk_trig_i, // one-off trigger for consistency check
output logic chk_pending_o, // indicates whether there are pending checks
@@ -111,20 +112,21 @@
logic [LfsrWidth-1:0] integ_mask, cnsty_mask;
logic integ_load_period, integ_load_timeout, integ_cnt_zero;
logic cnsty_load_period, cnsty_load_timeout, cnsty_cnt_zero;
- logic timeout_zero, integ_msk_zero, cnsty_msk_zero;
+ logic timeout_zero, integ_msk_zero, cnsty_msk_zero, cnsty_cnt_pause;
assign integ_mask = {integ_period_msk_i, {LfsrWidth-32{1'b1}}};
assign cnsty_mask = {cnsty_period_msk_i, {LfsrWidth-32{1'b1}}};
assign integ_cnt_d = (integ_load_period) ? lfsr_state & integ_mask :
- (integ_load_timeout) ? LfsrWidth'(timeout_i) :
+ (integ_load_timeout) ? LfsrWidth'(timeout_i) :
(integ_cnt_zero) ? '0 :
integ_cnt_q - 1'b1;
assign cnsty_cnt_d = (cnsty_load_period) ? lfsr_state & cnsty_mask :
- (cnsty_load_timeout) ? LfsrWidth'(timeout_i) :
+ (cnsty_load_timeout) ? LfsrWidth'(timeout_i) :
(cnsty_cnt_zero) ? '0 :
+ (cnsty_cnt_pause) ? cnsty_cnt_q :
cnsty_cnt_q - 1'b1;
assign timeout_zero = (timeout_i == '0);
@@ -192,6 +194,7 @@
cnsty_load_period = 1'b0;
integ_load_timeout = 1'b0;
cnsty_load_timeout = 1'b0;
+ cnsty_cnt_pause = 1'b0;
// Requests going to partitions.
set_all_integ_reqs = '0;
@@ -248,6 +251,12 @@
// if the timeout counter expires (this will raise an alert).
CnstyWaitSt: begin
chk_pending_o = 1'b1;
+ // Note that consistency checks go back and read from OTP. Hence,
+ // life cycle transitions and DAI programming operations
+ // may interfere with these checks and cause them to take longer
+ // than typically expected. Therefore, the timeout counter is stopped
+ // during programming operations.
+ cnsty_cnt_pause = otp_prog_busy_i;
if (!timeout_zero && cnsty_cnt_zero) begin
state_d = ErrorSt;
chk_timeout_d = 1'b1;