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