[dv/otp_ctrl] Fix lc sequence mismatch

This PR fixed nightly regression lc sequence mismatch to align with
design behavior in PR #5433

Signed-off-by: Cindy Chen <chencindy@google.com>
diff --git a/hw/ip/otp_ctrl/dv/env/otp_ctrl_if.sv b/hw/ip/otp_ctrl/dv/env/otp_ctrl_if.sv
index 7c57570..b8778e0 100644
--- a/hw/ip/otp_ctrl/dv/env/otp_ctrl_if.sv
+++ b/hw/ip/otp_ctrl/dv/env/otp_ctrl_if.sv
@@ -22,8 +22,9 @@
 
   // connect with lc_prog push-pull interface
   logic                lc_prog_req, lc_prog_err;
-  logic                lc_prog_err_dly1, lc_prog_no_intr_check;
+  logic                lc_prog_err_dly1, lc_prog_no_sta_check;
 
+  // Lc_err could trigger during LC program, so check intr and status after lc_req is finished.
   // Lc_err takes one clock cycle to propogate to intr signal. So avoid intr check if it happens
   // during the transition.
   always_ff @(posedge clk_i or negedge rst_ni) begin
@@ -33,7 +34,7 @@
       lc_prog_err_dly1 <= lc_prog_err;
     end
   end
-  assign lc_prog_no_intr_check = lc_prog_err | lc_prog_err_dly1;
+  assign lc_prog_no_sta_check = lc_prog_err | lc_prog_err_dly1 | lc_prog_req;
 
   // TODO: for lc_tx, except esc_en signal, all value different from On is treated as Off,
   // technically we can randomize values here once scb supports
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 19eccf1..3be3eb1 100644
--- a/hw/ip/otp_ctrl/dv/env/otp_ctrl_scoreboard.sv
+++ b/hw/ip/otp_ctrl/dv/env/otp_ctrl_scoreboard.sv
@@ -13,7 +13,7 @@
   bit [TL_DW-1:0] otp_a [OTP_ARRAY_SIZE];
 
   // lc_state and lc_cnt that stored in OTP
-  bit [LC_PROG_DATA_SIZE-1:0] otp_lc_data;
+  bit [7:0] otp_lc_data[LifeCycleSize];
   bit key_size_80 = SCRAMBLE_KEY_SIZE == 80;
   bit [EDN_BUS_WIDTH-1:0] edn_data_q[$];
 
@@ -28,7 +28,7 @@
 
   // Status related variables
   bit under_chk, under_dai_access;
-  bit [TL_DW-1:0] exp_status;
+  bit [TL_DW-1:0] exp_status, status_mask;
 
   bit macro_alert_triggered;
 
@@ -85,9 +85,9 @@
       @(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};
-          digests = '{default:0};
-          otp_lc_data = 0;
+          otp_a        = '{default:0};
+          digests      = '{default:0};
+          otp_lc_data  = '{default:0};
           sw_read_lock = 0;
           // secret partitions have been scrambled before writing to OTP.
           // here calculate the pre-srambled raw data when clearing internal OTP to all 0s.
@@ -143,27 +143,25 @@
   virtual task process_lc_prog_req();
     forever begin
       push_pull_item#(.DeviceDataWidth(1), .HostDataWidth(LC_PROG_DATA_SIZE)) rcv_item;
-      bit exp_err_bit;
-      string err_msg;
+      bit       exp_err_bit;
+      bit [7:0] lc_wr_data[LifeCycleSize];
 
       lc_prog_fifo.get(rcv_item);
-      err_msg = $sformatf("current lc_data %0h, request program lc_data %0h",
-                          otp_lc_data, rcv_item.h_data);
 
-      // LC program request data is valid, no OTP macro error
-      if ((otp_lc_data & rcv_item.h_data) == otp_lc_data) begin
-        `DV_CHECK_EQ(rcv_item.d_data, 0, err_msg)
-        otp_lc_data = rcv_item.h_data;
-        exp_status[OtpLciErrIdx] = 0;
-      end else begin
-        `DV_CHECK_EQ(rcv_item.d_data, 1, err_msg)
-        fork
-          begin
-            cfg.clk_rst_vif.wait_n_clks(1);
-            predict_status_err(.dai_err(0), .lc_err(1));
-          end
-        join
+      // Even if lci_error happened, design will continue to program the rest of LC partitions.
+      lc_wr_data = {>>byte{rcv_item.h_data}};
+      foreach (otp_lc_data[i]) begin
+        if ((otp_lc_data[i] & lc_wr_data[i]) == otp_lc_data[i]) begin
+          otp_lc_data[i] = lc_wr_data[i];
+        end else begin
+          exp_err_bit = 1;
+        end
       end
+
+      // 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;
     end
   endtask
 
@@ -545,10 +543,9 @@
       "status": begin
         if (addr_phase_read) begin
           void'(ral.status.predict(.value(exp_status), .kind(UVM_PREDICT_READ)));
-        end else if (data_phase_read) begin
-          bit [TL_DW-1:0] status_mask;
-          if (item.d_data[OtpDaiIdleIdx]) check_otp_idle(1);
 
+          // update status mask
+          status_mask = 0;
           // Mask out check_pending field - we do not know how long it takes to process checks.
           if (under_chk) status_mask[OtpCheckPendingIdx] = 1;
 
@@ -559,6 +556,12 @@
             status_mask[OtpDaiErrIdx]  = 1;
           end
 
+          // Mask out LCI error bit if lc_req is set.
+          if (cfg.otp_ctrl_vif.lc_prog_no_sta_check) status_mask[OtpLciErrIdx] = 1;
+
+        end else if (data_phase_read) begin
+          if (item.d_data[OtpDaiIdleIdx]) check_otp_idle(1);
+
           // STATUS register check with mask
           `DV_CHECK_EQ((csr.get_mirrored_value() | status_mask), (item.d_data | status_mask),
                        $sformatf("reg name: status, compare_mask %0h", status_mask))
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 615606fb..91d6bcd 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
@@ -238,9 +238,10 @@
 
   virtual task rd_and_clear_intrs();
     bit [TL_DW-1:0] val;
-    wait(cfg.otp_ctrl_vif.lc_prog_no_intr_check == 0);
-    csr_rd(ral.intr_state, val);
-    csr_wr(ral.intr_state, val);
+    if (cfg.otp_ctrl_vif.lc_prog_no_sta_check == 0) begin
+      csr_rd(ral.intr_state, val);
+      csr_wr(ral.intr_state, val);
+    end
   endtask
 
   virtual task req_sram_key(int index);