[dv/lc_ctrl] Add lc_ctrl sequence to trigger otp_prog_failure alert

This PR adds a lc_ctrl sequence to test scoreboard alert checkings. The
sequence will be optmized further once the push-pull agent support data
constraints. The purpose of this commit is to create a testcase for
cip_base_scb alert checking.

Signed-off-by: Cindy Chen <chencindy@google.com>
diff --git a/hw/ip/lc_ctrl/data/lc_ctrl_testplan.hjson b/hw/ip/lc_ctrl/data/lc_ctrl_testplan.hjson
index abec17e..1471060 100644
--- a/hw/ip/lc_ctrl/data/lc_ctrl_testplan.hjson
+++ b/hw/ip/lc_ctrl/data/lc_ctrl_testplan.hjson
@@ -65,7 +65,7 @@
             - Check if lc_state moves to escalation state
             '''
       milestone: V2
-      tests: []
+      tests: ["lc_ctrl_prog_failure"]
     }
     {
       name: lc_state_failure
diff --git a/hw/ip/lc_ctrl/dv/env/lc_ctrl_env.core b/hw/ip/lc_ctrl/dv/env/lc_ctrl_env.core
index 73ab837..89cd570 100644
--- a/hw/ip/lc_ctrl/dv/env/lc_ctrl_env.core
+++ b/hw/ip/lc_ctrl/dv/env/lc_ctrl_env.core
@@ -22,6 +22,7 @@
       - seq_lib/lc_ctrl_base_vseq.sv: {is_include_file: true}
       - seq_lib/lc_ctrl_common_vseq.sv: {is_include_file: true}
       - seq_lib/lc_ctrl_smoke_vseq.sv: {is_include_file: true}
+      - seq_lib/lc_ctrl_prog_failure_vseq.sv: {is_include_file: true}
     file_type: systemVerilogSource
 
 generate:
diff --git a/hw/ip/lc_ctrl/dv/env/lc_ctrl_if.sv b/hw/ip/lc_ctrl/dv/env/lc_ctrl_if.sv
index b18e7a4..df4db66 100644
--- a/hw/ip/lc_ctrl/dv/env/lc_ctrl_if.sv
+++ b/hw/ip/lc_ctrl/dv/env/lc_ctrl_if.sv
@@ -10,6 +10,7 @@
   import otp_ctrl_part_pkg::*;
 
   logic tdo_oe; // TODO: add assertions
+  logic prog_err; // TODO: remove once push-pull can constrain data
   otp_ctrl_pkg::otp_lc_data_t     otp_i;
   otp_ctrl_part_pkg::otp_hw_cfg_t otp_hw_cfg_i;
 
@@ -54,6 +55,7 @@
 
     clk_byp_ack_i = clk_byp_ack;
     flash_rma_ack_i = flash_rma_ack;
+    prog_err = 0;
   endtask
 
   task automatic set_clk_byp_ack(lc_ctrl_pkg::lc_tx_t val);
diff --git a/hw/ip/lc_ctrl/dv/env/lc_ctrl_scoreboard.sv b/hw/ip/lc_ctrl/dv/env/lc_ctrl_scoreboard.sv
index b49ad8a..9d22a29 100644
--- a/hw/ip/lc_ctrl/dv/env/lc_ctrl_scoreboard.sv
+++ b/hw/ip/lc_ctrl/dv/env/lc_ctrl_scoreboard.sv
@@ -40,6 +40,7 @@
     super.run_phase(phase);
     fork
       check_lc_output();
+      process_otp_prog_rsp();
     join_none
   endtask
 
@@ -70,6 +71,17 @@
     end
   endtask
 
+  virtual task process_otp_prog_rsp();
+    forever begin
+      push_pull_item#(.HostDataWidth(OTP_PROG_HDATA_WIDTH),
+                      .DeviceDataWidth(OTP_PROG_DDATA_WIDTH)) item_rcv;
+      otp_prog_fifo.get(item_rcv);
+      if (cfg.lc_ctrl_vif.prog_err == 1) begin
+        void'(set_exp_alert(.alert_name("lc_programming_failure"), .max_delay(0), .always_on(1)));
+      end
+    end
+  endtask
+
   // check lc outputs, default all off
   virtual function void check_lc_outputs(lc_outputs_t exp_o = '{default:lc_ctrl_pkg::Off},
                                          string       msg   = "expect all output OFF");
diff --git a/hw/ip/lc_ctrl/dv/env/seq_lib/lc_ctrl_base_vseq.sv b/hw/ip/lc_ctrl/dv/env/seq_lib/lc_ctrl_base_vseq.sv
index 343d48a..c304599 100644
--- a/hw/ip/lc_ctrl/dv/env/seq_lib/lc_ctrl_base_vseq.sv
+++ b/hw/ip/lc_ctrl/dv/env/seq_lib/lc_ctrl_base_vseq.sv
@@ -96,14 +96,24 @@
     join_none
   endtask
 
-  virtual task sw_transition_req(bit [TL_DW-1:0] next_lc_state, bit [TL_DW*3-1:0] token_val);
+  virtual task sw_transition_req(bit [TL_DW-1:0] next_lc_state,
+                                 bit [TL_DW*3-1:0] token_val,
+                                 bit trans_success = 1);
     csr_wr(ral.claim_transition_if, CLAIM_TRANS_VAL);
     csr_wr(ral.transition_target, next_lc_state);
     csr_wr(ral.transition_token_0, token_val[TL_DW-1:0]);
     csr_wr(ral.transition_token_1, token_val[TL_DW*2-1:TL_DW]);
     csr_wr(ral.transition_token_2, token_val[TL_DW*3-1:TL_DW*2]);
     csr_wr(ral.transition_cmd, 'h01);
-    csr_spinwait(ral.status.transition_successful, 1);
+    if (trans_success) begin
+      csr_spinwait(ral.status.transition_successful, 1);
+    end else begin
+      // TODO: temp support only for otp_error
+      csr_spinwait(ral.status.otp_error, 1);
+      // always on alert, set time delay to make sure alert triggered for at least for one
+      // handshake cycle
+      cfg.clk_rst_vif.wait_clks($urandom_range(20, 50));
+    end
   endtask
 
   // checking of these two CSRs are done in scb
diff --git a/hw/ip/lc_ctrl/dv/env/seq_lib/lc_ctrl_prog_failure_vseq.sv b/hw/ip/lc_ctrl/dv/env/seq_lib/lc_ctrl_prog_failure_vseq.sv
new file mode 100644
index 0000000..37e0bd7
--- /dev/null
+++ b/hw/ip/lc_ctrl/dv/env/seq_lib/lc_ctrl_prog_failure_vseq.sv
@@ -0,0 +1,44 @@
+// Copyright lowRISC contributors.
+// Licensed under the Apache License, Version 2.0, see LICENSE for details.
+// SPDX-License-Identifier: Apache-2.0
+
+// This sequence triggers prog_failure alert by setting the error bit in otp_program_rsp
+// Then check in scb if the alert is triggered correctly
+class lc_ctrl_prog_failure_vseq extends lc_ctrl_smoke_vseq;
+  `uvm_object_utils(lc_ctrl_prog_failure_vseq)
+
+  `uvm_object_new
+
+  virtual function void configure_vseq();
+    this.trans_success_c.constraint_mode(0);
+  endfunction
+
+  virtual task body();
+    fork
+      super.body();
+      set_prog_failure();
+    join_any
+    disable fork;
+  endtask
+
+  virtual task post_start();
+    super.post_start();
+    // trigger dut_init to make sure always on alert is not firing forever
+    dut_init();
+  endtask
+
+  task set_prog_failure();
+    forever begin
+      wait(cfg.m_otp_prog_pull_agent_cfg.vif.req == 1);
+      if (trans_success) begin
+        cfg.lc_ctrl_vif.prog_err = 0;
+      end else begin
+        cfg.lc_ctrl_vif.prog_err = 1;
+      end
+      wait (cfg.m_otp_prog_pull_agent_cfg.vif.req == 0);
+      cfg.clk_rst_vif.wait_clks(2);
+      cfg.lc_ctrl_vif.prog_err = 0;
+    end
+  endtask
+
+endclass
diff --git a/hw/ip/lc_ctrl/dv/env/seq_lib/lc_ctrl_smoke_vseq.sv b/hw/ip/lc_ctrl/dv/env/seq_lib/lc_ctrl_smoke_vseq.sv
index 1a1f56e..aea9243 100644
--- a/hw/ip/lc_ctrl/dv/env/seq_lib/lc_ctrl_smoke_vseq.sv
+++ b/hw/ip/lc_ctrl/dv/env/seq_lib/lc_ctrl_smoke_vseq.sv
@@ -10,17 +10,18 @@
 
   rand bit clk_byp_error_rsp;
   rand bit flash_rma_error_rsp;
+  rand bit trans_success;
   dec_lc_state_e next_lc_state;
 
-  constraint lc_cnt_c {
-    lc_state != LcStRaw -> lc_cnt != LcCntRaw;
-  }
-
   constraint no_err_rsps_c {
     clk_byp_error_rsp   == 0;
     flash_rma_error_rsp == 0;
   }
 
+  constraint trans_success_c {
+    trans_success == 1;
+  }
+
   task body();
     run_clk_byp_rsp_nonblocking(clk_byp_error_rsp);
     run_flash_rma_rsp_nonblocking(flash_rma_error_rsp);
@@ -41,7 +42,7 @@
         randomize_next_lc_state(dec_lc_state(lc_state));
         `uvm_info(`gfn, $sformatf("next_LC_state is %0s, input token is %0h", next_lc_state.name,
                                   token_val), UVM_DEBUG)
-        sw_transition_req(next_lc_state, token_val);
+        sw_transition_req(next_lc_state, token_val, trans_success);
       end else begin
         // wait at least two clks for scb to finish checking lc outputs
         cfg.clk_rst_vif.wait_clks($urandom_range(2, 10));
diff --git a/hw/ip/lc_ctrl/dv/env/seq_lib/lc_ctrl_vseq_list.sv b/hw/ip/lc_ctrl/dv/env/seq_lib/lc_ctrl_vseq_list.sv
index 237a209..1322696 100644
--- a/hw/ip/lc_ctrl/dv/env/seq_lib/lc_ctrl_vseq_list.sv
+++ b/hw/ip/lc_ctrl/dv/env/seq_lib/lc_ctrl_vseq_list.sv
@@ -5,3 +5,4 @@
 `include "lc_ctrl_base_vseq.sv"
 `include "lc_ctrl_smoke_vseq.sv"
 `include "lc_ctrl_common_vseq.sv"
+`include "lc_ctrl_prog_failure_vseq.sv"
diff --git a/hw/ip/lc_ctrl/dv/lc_ctrl_sim_cfg.hjson b/hw/ip/lc_ctrl/dv/lc_ctrl_sim_cfg.hjson
index d97aa54..95c5a31 100644
--- a/hw/ip/lc_ctrl/dv/lc_ctrl_sim_cfg.hjson
+++ b/hw/ip/lc_ctrl/dv/lc_ctrl_sim_cfg.hjson
@@ -51,7 +51,11 @@
       uvm_test_seq: lc_ctrl_smoke_vseq
     }
 
-    // TODO: add more tests here
+    {
+      name: lc_ctrl_prog_failure
+      uvm_test_seq: lc_ctrl_prog_failure_vseq
+    }
+
   ]
 
   // List of regressions.
diff --git a/hw/ip/lc_ctrl/dv/tb.sv b/hw/ip/lc_ctrl/dv/tb.sv
index 1ac9db1..b8a7c87 100644
--- a/hw/ip/lc_ctrl/dv/tb.sv
+++ b/hw/ip/lc_ctrl/dv/tb.sv
@@ -39,7 +39,7 @@
   `DV_ALERT_IF_CONNECT
 
   // TODO: remove once OTP_PROG_DDATA_WIDTH is set to 1
-  assign otp_prog_rsp.err = 0;
+  assign otp_prog_rsp.err = lc_ctrl_if.prog_err;
   assign otp_prog_rsp.ack = otp_prog_if.ack;
   assign otp_token_rsp.ack = otp_token_if.ack;
   // TODO: temp constraint to 0 because it has to equal to otp_lc_data_i tokens