[dv/alert_esc_agent] support lpg in alert_esc_agent

This PR supports lpg in alert_esc_agent. In alert_handler testbench, if
lpg is enabled and alert triggered from the alert sender side,
alert_handler testbench needs to ignore the alert.

Signed-off-by: Cindy Chen <chencindy@opentitan.org>
diff --git a/hw/dv/sv/alert_esc_agent/alert_esc_agent_cfg.sv b/hw/dv/sv/alert_esc_agent/alert_esc_agent_cfg.sv
index aa5cee8..c10da26 100644
--- a/hw/dv/sv/alert_esc_agent/alert_esc_agent_cfg.sv
+++ b/hw/dv/sv/alert_esc_agent/alert_esc_agent_cfg.sv
@@ -13,6 +13,7 @@
   bit is_async        = 0;
   bit en_ping_cov     = 1;
   bit alert_init_done = 0;
+  bit en_alert_lpg    = 0;
 
   // dut clk frequency, used to generate alert async_clk frequency
   int clk_freq_mhz;
diff --git a/hw/dv/sv/alert_esc_agent/alert_monitor.sv b/hw/dv/sv/alert_esc_agent/alert_monitor.sv
index fbd6bc3..3164666 100644
--- a/hw/dv/sv/alert_esc_agent/alert_monitor.sv
+++ b/hw/dv/sv/alert_esc_agent/alert_monitor.sv
@@ -53,15 +53,31 @@
   // process. However, it can still block alert handshake via the `cfg.alert_init_done` flag.
   // To handle the scenario where reset is issued during alert init, we use a fork join_any thread.
   virtual task wait_alert_init_done();
-    `DV_SPINWAIT_EXIT(
-        wait (cfg.vif.monitor_cb.alert_tx_final.alert_p ==
-              cfg.vif.monitor_cb.alert_tx_final.alert_n);
-        wait (cfg.vif.monitor_cb.alert_tx_final.alert_p !=
-              cfg.vif.monitor_cb.alert_tx_final.alert_n);
-        `uvm_info("alert_monitor", "Alert init done!", UVM_HIGH)
-        cfg.alert_init_done = 1;
-        under_reset = 0;,
-        @(negedge cfg.vif.rst_n);)
+    fork begin
+      fork
+        begin
+          wait (cfg.vif.monitor_cb.alert_tx_final.alert_p ==
+                cfg.vif.monitor_cb.alert_tx_final.alert_n);
+          wait (cfg.vif.monitor_cb.alert_tx_final.alert_p !=
+                cfg.vif.monitor_cb.alert_tx_final.alert_n);
+          `uvm_info("alert_monitor", "Alert init done!", UVM_HIGH)
+          cfg.alert_init_done = 1;
+          under_reset = 0;
+        end
+        begin
+          @(negedge cfg.vif.rst_n);
+        end
+        // Clear `under_reset` and `alert_init_done` when en_alert_lpg is on, because alert_sender
+        // can still send alerts, and alert_handler should ignore the alert_tx request.
+        begin
+          wait (cfg.en_alert_lpg == 1);
+          cfg.alert_init_done = 1;
+          under_reset = 0;
+        end
+      join_any
+      disable fork;
+    end
+    join
   endtask
 
   virtual task ping_thread();
@@ -92,7 +108,7 @@
                 under_ping_rsp = 0;
               end
               begin
-                wait(under_reset);
+                wait (under_reset || cfg.en_alert_lpg);
               end
             join_any
             // wait 1ps in case 'wait_ping_handshake' and 'wait_ping_timeout' thread finish at the
@@ -104,7 +120,7 @@
 
         `uvm_info("alert_monitor", $sformatf("[%s]: handshake status is %s",
             req.alert_esc_type.name(), req.alert_handshake_sta.name()), UVM_HIGH)
-        if (!under_reset) begin
+        if (!under_reset && !cfg.en_alert_lpg) begin
           alert_esc_port.write(req);
           if (cfg.en_cov && cfg.en_ping_cov) cov.m_alert_esc_trans_cg.sample(req.alert_esc_type);
 
@@ -156,7 +172,7 @@
                 req.alert_handshake_sta = AlertAckComplete;
               end
               begin
-                wait(under_reset);
+                wait (under_reset || cfg.en_alert_lpg);
               end
             join_any
             disable fork;
@@ -165,7 +181,7 @@
 
         `uvm_info("alert_monitor", $sformatf("[%s]: handshake status is %s",
             req.alert_esc_type.name(), req.alert_handshake_sta.name()), UVM_HIGH)
-        if (!under_reset) alert_esc_port.write(req);
+        if (!under_reset && !cfg.en_alert_lpg) alert_esc_port.write(req);
         if (cfg.en_cov) begin
           cov.m_alert_handshake_complete_cg.sample(req.alert_esc_type, req.alert_handshake_sta);
           if (cfg.en_ping_cov) cov.m_alert_esc_trans_cg.sample(req.alert_esc_type);
@@ -181,7 +197,8 @@
     bit prev_err;
     forever @(cfg.vif.monitor_cb) begin
       // use prev_err to exclude the async clk skew
-      if (!under_reset && is_sig_int_err() && (!cfg.is_async || prev_err != 0)) begin
+      if (!under_reset && !cfg.en_alert_lpg && is_sig_int_err() &&
+          (!cfg.is_async || prev_err != 0)) begin
         fork
           begin
             req = alert_esc_seq_item::type_id::create("req");
diff --git a/hw/dv/sv/alert_esc_agent/alert_sender_driver.sv b/hw/dv/sv/alert_esc_agent/alert_sender_driver.sv
index b4590ec..c723b35 100644
--- a/hw/dv/sv/alert_esc_agent/alert_sender_driver.sv
+++ b/hw/dv/sv/alert_esc_agent/alert_sender_driver.sv
@@ -39,24 +39,30 @@
     join_none
   endtask : drive_req
 
+  // Two conditions can trigger alert init:
+  // 1). Reset deassert;   2). LPG disabled.
   virtual task alert_init_thread();
     do_alert_tx_init();
-    forever @(posedge cfg.vif.rst_n) begin
-      do_alert_tx_init();
-    end
+    fork
+      forever @(posedge cfg.vif.rst_n) begin
+        do_alert_tx_init();
+      end
+      forever @(negedge cfg.en_alert_lpg) begin
+        do_alert_tx_init();
+      end
+    join_none
   endtask : alert_init_thread
 
   virtual task send_alert();
     forever begin
       alert_esc_seq_item req, rsp;
-      wait(s_alert_send_q.size() > 0 && !under_reset);
+      wait (s_alert_send_q.size() > 0 && !under_reset);
       req = s_alert_send_q.pop_front();
       `downcast(rsp, req.clone());
       rsp.set_id_info(req);
       `uvm_info(`gfn,
           $sformatf("starting to send sender item, alert_send=%0b, ping_rsp=%0b, int_err=%0b",
           req.s_alert_send, req.s_alert_ping_rsp, req.int_err), UVM_HIGH)
-
       fork
         begin : isolation_fork
           fork
@@ -66,7 +72,7 @@
               alert_atomic.put(1);
             end
             begin
-              wait(under_reset);
+              wait (under_reset);
             end
           join_any
           disable fork;
@@ -83,7 +89,7 @@
   virtual task rsp_ping();
     forever begin
       alert_esc_seq_item req, rsp;
-      wait(s_alert_ping_rsp_q.size() > 0 && !under_reset);
+      wait (s_alert_ping_rsp_q.size() > 0 && !under_reset && !cfg.en_alert_lpg);
       req = s_alert_ping_rsp_q.pop_front();
       `downcast(rsp, req.clone());
       rsp.set_id_info(req);
@@ -105,7 +111,7 @@
               alert_atomic.put(1);
             end
             begin
-              wait(under_reset);
+              wait (under_reset || cfg.en_alert_lpg);
             end
           join_any
           disable fork;
@@ -161,14 +167,18 @@
       begin : isolation_fork
         fork
           begin : alert_timeout
-            repeat (cfg.handshake_timeout_cycle) wait_sender_clk();
+            repeat (cfg.handshake_timeout_cycle) begin
+              wait_sender_clk();
+              // If alert_lpg is enabled, alert rx request is ignored by the alert_receiver.
+              if (cfg.en_alert_lpg) break;
+            end
           end
           begin : wait_alert_handshake
-            wait(cfg.vif.alert_rx.ack_p == 1'b1);
+            wait (cfg.vif.alert_rx.ack_p == 1'b1);
             wait_sender_clk();
             repeat (ack_delay) wait_sender_clk();
             reset_alert();
-            wait(cfg.vif.alert_rx.ack_p == 1'b0);
+            wait (cfg.vif.alert_rx.ack_p == 1'b0);
           end
         join_any
         disable fork;
@@ -229,13 +239,28 @@
   // After alert_receiver is reset, it will send a signal integrity fail via `ping_n` and `ack_n`,
   // alert_sender acknowledged the init via sending an `alert_n` integrity fail.
   virtual task do_alert_tx_init();
-    `DV_SPINWAIT_EXIT(
-        wait (cfg.vif.alert_rx.ping_p == cfg.vif.alert_rx.ping_n);
-        cfg.vif.alert_tx_int.alert_n <= 1'b0;
-        wait (cfg.vif.alert_rx.ping_p != cfg.vif.alert_rx.ping_n);
-        cfg.vif.alert_tx_int.alert_n <= 1'b1;
-        under_reset = 0;,
-        @(negedge cfg.vif.rst_n);)
+    fork begin
+      fork
+        begin
+          wait (cfg.vif.alert_rx.ping_p == cfg.vif.alert_rx.ping_n);
+          cfg.vif.alert_tx_int.alert_n <= 1'b0;
+          wait (cfg.vif.alert_rx.ping_p != cfg.vif.alert_rx.ping_n);
+          cfg.vif.alert_tx_int.alert_n <= 1'b1;
+          under_reset = 0;
+        end
+        begin
+          @(negedge cfg.vif.rst_n);
+        end
+        begin
+          // Clear `under_reset` when en_alert_lpg is on, because alert_sender can still send
+          // alerts, and alert_handler should ignore the alert_tx request.
+          wait (cfg.en_alert_lpg == 1);
+          under_reset = 0;
+        end
+      join_any
+      disable fork;
+    end
+    join
   endtask
 
 endclass : alert_sender_driver
diff --git a/hw/ip_templates/alert_handler/dv/env/alert_handler_if.sv b/hw/ip_templates/alert_handler/dv/env/alert_handler_if.sv
index c2ea2d9..112726a 100644
--- a/hw/ip_templates/alert_handler/dv/env/alert_handler_if.sv
+++ b/hw/ip_templates/alert_handler/dv/env/alert_handler_if.sv
@@ -17,6 +17,11 @@
     lpg_rst_en = '{default: MuBi4False};
   endfunction
 
+  function automatic bit get_lpg_status(int index);
+    check_lpg_index(index);
+    return (lpg_cg_en[index] == MuBi4True || lpg_rst_en[index] == MuBi4True);
+  endfunction
+
   // TODO: randomize all values outside of the mubi4 enum.
   function automatic void set_lpg_cg_en(int index);
     check_lpg_index(index);
diff --git a/hw/ip_templates/alert_handler/dv/env/seq_lib/alert_handler_base_vseq.sv b/hw/ip_templates/alert_handler/dv/env/seq_lib/alert_handler_base_vseq.sv
index 33ba638..30f86b3 100644
--- a/hw/ip_templates/alert_handler/dv/env/seq_lib/alert_handler_base_vseq.sv
+++ b/hw/ip_templates/alert_handler/dv/env/seq_lib/alert_handler_base_vseq.sv
@@ -199,7 +199,9 @@
 
   virtual task wait_alert_handshake_done();
     cfg.clk_rst_vif.wait_clks(2);
-    foreach (cfg.alert_host_cfg[i]) cfg.alert_host_cfg[i].vif.wait_ack_complete();
+    foreach (cfg.alert_host_cfg[i]) begin
+      if (!cfg.alert_host_cfg[i].en_alert_lpg) cfg.alert_host_cfg[i].vif.wait_ack_complete();
+    end
   endtask
 
   virtual function bit check_esc_done(bit[TL_DW-1:0] vals[$]);
@@ -254,6 +256,31 @@
     join
   endtask
 
+  function void enable_lpg_group(bit [NUM_ALERTS-1:0] alert_en_i);
+    foreach (alert_en_i[i]) begin
+      if (alert_en_i[i]) set_alert_lpg(i);
+    end
+  endfunction
+
+  // Enable alert's LPG based on alert_i input.
+  //
+  // Only enable this alert's LPG if the lgp input `lpg_cg_en` or `lpg_rst_en` if not Mubi4True.
+  // Because one LPG will turn off a set of alert sensers. So this task will also set all LPG's
+  // alert_host_cfgs' `en_alert_lpg` to 1.
+  virtual function void set_alert_lpg(int alert_i);
+    int       lpg_i = alert_handler_reg_pkg::LpgMap[alert_i];
+    bit [1:0] set_lpg;
+
+    if (cfg.alert_handler_vif.get_lpg_status(lpg_i) == 0) begin
+      `DV_CHECK_STD_RANDOMIZE_WITH_FATAL(set_lpg, set_lpg > 0;);
+      if (set_lpg[0]) cfg.alert_handler_vif.set_lpg_cg_en(lpg_i);
+      if (set_lpg[1]) cfg.alert_handler_vif.set_lpg_rst_en(lpg_i);
+      foreach (alert_handler_reg_pkg::LpgMap[i]) begin
+        if (alert_handler_reg_pkg::LpgMap[i] == lpg_i) cfg.alert_host_cfg[i].en_alert_lpg = 1;
+      end
+    end
+  endfunction
+
   virtual task alert_handler_crashdump_phases(bit [1:0] classa_phase = $urandom(),
                                               bit [1:0] classb_phase = $urandom(),
                                               bit [1:0] classc_phase = $urandom(),
diff --git a/hw/ip_templates/alert_handler/dv/env/seq_lib/alert_handler_lpg_vseq.sv b/hw/ip_templates/alert_handler/dv/env/seq_lib/alert_handler_lpg_vseq.sv
index 68b2b83..22a4944 100644
--- a/hw/ip_templates/alert_handler/dv/env/seq_lib/alert_handler_lpg_vseq.sv
+++ b/hw/ip_templates/alert_handler/dv/env/seq_lib/alert_handler_lpg_vseq.sv
@@ -22,10 +22,6 @@
     esc_ping_timeout   == 0;
   }
 
-  constraint alert_trigger_c {
-    alert_trigger == 0;
-  }
-
   // disable interrupt timeout
   constraint esc_intr_timeout_c {
     foreach (intr_timeout_cyc[i]) {intr_timeout_cyc[i] == 0;}
@@ -37,19 +33,11 @@
     verbosity = UVM_HIGH;
   endfunction
 
-  function void disable_lpg_group(bit [NUM_ALERTS-1:0] alert_en_i);
-    foreach (alert_en_i[i]) begin
-      int index = alert_handler_reg_pkg::LpgMap[i];
-      if ($urandom_range(0, 1)) cfg.alert_handler_vif.set_lpg_cg_en(index);
-      if ($urandom_range(0, 1)) cfg.alert_handler_vif.set_lpg_rst_en(index);
-    end
-  endfunction
-
   task body();
     fork
       begin : isolation_fork
         trigger_non_blocking_seqs();
-        disable_lpg_group(alert_en);
+        enable_lpg_group(alert_en);
         run_smoke_seq();
         disable fork; // disable non-blocking seqs for stress_all tests
       end // end fork
diff --git a/hw/top_earlgrey/ip_autogen/alert_handler/dv/env/alert_handler_if.sv b/hw/top_earlgrey/ip_autogen/alert_handler/dv/env/alert_handler_if.sv
index c2ea2d9..112726a 100644
--- a/hw/top_earlgrey/ip_autogen/alert_handler/dv/env/alert_handler_if.sv
+++ b/hw/top_earlgrey/ip_autogen/alert_handler/dv/env/alert_handler_if.sv
@@ -17,6 +17,11 @@
     lpg_rst_en = '{default: MuBi4False};
   endfunction
 
+  function automatic bit get_lpg_status(int index);
+    check_lpg_index(index);
+    return (lpg_cg_en[index] == MuBi4True || lpg_rst_en[index] == MuBi4True);
+  endfunction
+
   // TODO: randomize all values outside of the mubi4 enum.
   function automatic void set_lpg_cg_en(int index);
     check_lpg_index(index);
diff --git a/hw/top_earlgrey/ip_autogen/alert_handler/dv/env/seq_lib/alert_handler_base_vseq.sv b/hw/top_earlgrey/ip_autogen/alert_handler/dv/env/seq_lib/alert_handler_base_vseq.sv
index 33ba638..30f86b3 100644
--- a/hw/top_earlgrey/ip_autogen/alert_handler/dv/env/seq_lib/alert_handler_base_vseq.sv
+++ b/hw/top_earlgrey/ip_autogen/alert_handler/dv/env/seq_lib/alert_handler_base_vseq.sv
@@ -199,7 +199,9 @@
 
   virtual task wait_alert_handshake_done();
     cfg.clk_rst_vif.wait_clks(2);
-    foreach (cfg.alert_host_cfg[i]) cfg.alert_host_cfg[i].vif.wait_ack_complete();
+    foreach (cfg.alert_host_cfg[i]) begin
+      if (!cfg.alert_host_cfg[i].en_alert_lpg) cfg.alert_host_cfg[i].vif.wait_ack_complete();
+    end
   endtask
 
   virtual function bit check_esc_done(bit[TL_DW-1:0] vals[$]);
@@ -254,6 +256,31 @@
     join
   endtask
 
+  function void enable_lpg_group(bit [NUM_ALERTS-1:0] alert_en_i);
+    foreach (alert_en_i[i]) begin
+      if (alert_en_i[i]) set_alert_lpg(i);
+    end
+  endfunction
+
+  // Enable alert's LPG based on alert_i input.
+  //
+  // Only enable this alert's LPG if the lgp input `lpg_cg_en` or `lpg_rst_en` if not Mubi4True.
+  // Because one LPG will turn off a set of alert sensers. So this task will also set all LPG's
+  // alert_host_cfgs' `en_alert_lpg` to 1.
+  virtual function void set_alert_lpg(int alert_i);
+    int       lpg_i = alert_handler_reg_pkg::LpgMap[alert_i];
+    bit [1:0] set_lpg;
+
+    if (cfg.alert_handler_vif.get_lpg_status(lpg_i) == 0) begin
+      `DV_CHECK_STD_RANDOMIZE_WITH_FATAL(set_lpg, set_lpg > 0;);
+      if (set_lpg[0]) cfg.alert_handler_vif.set_lpg_cg_en(lpg_i);
+      if (set_lpg[1]) cfg.alert_handler_vif.set_lpg_rst_en(lpg_i);
+      foreach (alert_handler_reg_pkg::LpgMap[i]) begin
+        if (alert_handler_reg_pkg::LpgMap[i] == lpg_i) cfg.alert_host_cfg[i].en_alert_lpg = 1;
+      end
+    end
+  endfunction
+
   virtual task alert_handler_crashdump_phases(bit [1:0] classa_phase = $urandom(),
                                               bit [1:0] classb_phase = $urandom(),
                                               bit [1:0] classc_phase = $urandom(),
diff --git a/hw/top_earlgrey/ip_autogen/alert_handler/dv/env/seq_lib/alert_handler_lpg_vseq.sv b/hw/top_earlgrey/ip_autogen/alert_handler/dv/env/seq_lib/alert_handler_lpg_vseq.sv
index 68b2b83..22a4944 100644
--- a/hw/top_earlgrey/ip_autogen/alert_handler/dv/env/seq_lib/alert_handler_lpg_vseq.sv
+++ b/hw/top_earlgrey/ip_autogen/alert_handler/dv/env/seq_lib/alert_handler_lpg_vseq.sv
@@ -22,10 +22,6 @@
     esc_ping_timeout   == 0;
   }
 
-  constraint alert_trigger_c {
-    alert_trigger == 0;
-  }
-
   // disable interrupt timeout
   constraint esc_intr_timeout_c {
     foreach (intr_timeout_cyc[i]) {intr_timeout_cyc[i] == 0;}
@@ -37,19 +33,11 @@
     verbosity = UVM_HIGH;
   endfunction
 
-  function void disable_lpg_group(bit [NUM_ALERTS-1:0] alert_en_i);
-    foreach (alert_en_i[i]) begin
-      int index = alert_handler_reg_pkg::LpgMap[i];
-      if ($urandom_range(0, 1)) cfg.alert_handler_vif.set_lpg_cg_en(index);
-      if ($urandom_range(0, 1)) cfg.alert_handler_vif.set_lpg_rst_en(index);
-    end
-  endfunction
-
   task body();
     fork
       begin : isolation_fork
         trigger_non_blocking_seqs();
-        disable_lpg_group(alert_en);
+        enable_lpg_group(alert_en);
         run_smoke_seq();
         disable fork; // disable non-blocking seqs for stress_all tests
       end // end fork