[dv/alert] Add fatal alert check if scb is disabled

This PR addes a fatal alert check (check if fatal alert continuously
triggered until reset in a nonblocking task).
This can be used in aes shadow reg tests and keymgr tests where the scb
is disabled.

Signed-off-by: Cindy Chen <chencindy@google.com>
diff --git a/hw/dv/sv/alert_esc_agent/alert_esc_agent_pkg.sv b/hw/dv/sv/alert_esc_agent/alert_esc_agent_pkg.sv
index 0d65ccc..811aa96 100644
--- a/hw/dv/sv/alert_esc_agent/alert_esc_agent_pkg.sv
+++ b/hw/dv/sv/alert_esc_agent/alert_esc_agent_pkg.sv
@@ -10,6 +10,10 @@
   import prim_alert_pkg::*;
   import prim_esc_pkg::*;
 
+  // 2 clock cycles between two alert handshakes, 1 clock cycle to go back to `Idle` state,
+  // 1 clock cycle for monitor to detect alert.
+  parameter uint ALERT_B2B_DELAY = 4;
+
   typedef class alert_esc_seq_item;
   typedef class alert_esc_agent_cfg;
 
diff --git a/hw/dv/sv/alert_esc_agent/alert_esc_if.sv b/hw/dv/sv/alert_esc_agent/alert_esc_if.sv
index fb5a0cd..dc550f3 100644
--- a/hw/dv/sv/alert_esc_agent/alert_esc_if.sv
+++ b/hw/dv/sv/alert_esc_agent/alert_esc_if.sv
@@ -79,6 +79,11 @@
     input esc_rx;
   endclocking
 
+  assign alert_tx = (if_mode == dv_utils_pkg::Host && is_alert)    ? alert_tx_int : 'z;
+  assign alert_rx = (if_mode == dv_utils_pkg::Device && is_alert)  ? alert_rx_int : 'z;
+  assign esc_tx   = (if_mode == dv_utils_pkg::Host && !is_alert)   ? esc_tx_int   : 'z;
+  assign esc_rx   = (if_mode == dv_utils_pkg::Device && !is_alert) ? esc_rx_int   : 'z;
+
   task automatic wait_ack_complete();
     while (alert_tx_final.alert_p === 1'b1) @(monitor_cb);
     while (alert_rx_final.ack_p === 1'b1)   @(monitor_cb);
@@ -92,11 +97,6 @@
     get_alert = (alert_tx_final.alert_p === 1'b1 && alert_tx_final.alert_n === 1'b0);
   endfunction : get_alert
 
-  assign alert_tx = (if_mode == dv_utils_pkg::Host && is_alert)    ? alert_tx_int : 'z;
-  assign alert_rx = (if_mode == dv_utils_pkg::Device && is_alert)  ? alert_rx_int : 'z;
-  assign esc_tx   = (if_mode == dv_utils_pkg::Host && !is_alert)   ? esc_tx_int   : 'z;
-  assign esc_rx   = (if_mode == dv_utils_pkg::Device && !is_alert) ? esc_rx_int   : 'z;
-
   // this task wait for alert_ping request.
   // alert_ping request is detected by level triggered "alert_rx.ping_p/n" signals pairs
   // and no sig_int_err
diff --git a/hw/dv/sv/cip_lib/cip_base_scoreboard.sv b/hw/dv/sv/cip_lib/cip_base_scoreboard.sv
index 12f419e..f44de00 100644
--- a/hw/dv/sv/cip_lib/cip_base_scoreboard.sv
+++ b/hw/dv/sv/cip_lib/cip_base_scoreboard.sv
@@ -155,9 +155,8 @@
   endtask
 
   virtual task check_alert_triggered(string alert_name);
-    // 2 clock cycles between two alert handshakes, 1 clock cycle to go back to `Idle` state,
-    // 1 clock cycle for monitor to detect alert, 1 negedge edge to make sure no race condition.
-    repeat(5 + alert_chk_max_delay[alert_name]) begin
+    // Add 1 extra negedge edge clock to make sure no race condition.
+    repeat(alert_esc_agent_pkg::ALERT_B2B_DELAY + 1 + alert_chk_max_delay[alert_name]) begin
       cfg.clk_rst_vif.wait_n_clks(1);
       if (under_alert_handshake[alert_name] || cfg.under_reset) return;
     end
diff --git a/hw/dv/sv/cip_lib/cip_base_vseq.sv b/hw/dv/sv/cip_lib/cip_base_vseq.sv
index 3b2f710..d658e0b 100644
--- a/hw/dv/sv/cip_lib/cip_base_vseq.sv
+++ b/hw/dv/sv/cip_lib/cip_base_vseq.sv
@@ -645,6 +645,23 @@
     gen_storage_err_val = gen_storage_err_val << shift_bits >> shift_bits;
   endfunction
 
+  virtual task check_fatal_alert_nonblocking(string alert_name);
+    fork
+      `DV_SPINWAIT_EXIT(
+          forever begin
+            // 1 extra cycle to make sure no race condition
+            repeat(alert_esc_agent_pkg::ALERT_B2B_DELAY + 1) begin
+              cfg.clk_rst_vif.wait_n_clks(1);
+              if (cfg.m_alert_agent_cfg[alert_name].vif.get_alert() == 1) break;
+            end
+            `DV_CHECK_EQ(cfg.m_alert_agent_cfg[alert_name].vif.get_alert(), 1,
+                         $sformatf("fatal error %0s does not trigger!", alert_name))
+            cfg.m_alert_agent_cfg[alert_name].vif.wait_ack_complete();
+          end,
+          wait(cfg.under_reset);)
+    join_none
+  endtask
+
   virtual task run_shadow_reg_errors(int num_times);
     csr_excl_item      csr_excl = add_and_return_csr_excl("csr_excl");
     dv_base_reg        shadowed_csrs[$], test_csrs[$];
@@ -731,22 +748,28 @@
 
                 if (has_storage_error && do_lock_shadow_reg) begin
                   shadowed_csrs[index].lock_shadow_reg();
+                  check_fatal_alert_nonblocking(alert_name);
+
+                  // Wait two clock cycles then backdoor write back original value.
+                  // This won't stop fatal alert from firing.
+                  cfg.clk_rst_vif.wait_clks(2);
+                  csr_poke(.csr(shadowed_csrs[index]), .value(origin_val), .kind(kind), .predict(1));
+                end else begin
+                  `DV_CHECK_EQ(has_storage_error, 1,
+                               "dv_base_reg did not predict shadow storage error");
+                  `DV_SPINWAIT(while (!cfg.m_alert_agent_cfg[alert_name].vif.get_alert())
+                               cfg.clk_rst_vif.wait_clks(1);,
+                               $sformatf("%0s shadow_reg storage_err alert not detected",
+                                         shadowed_csrs[index].get_name()));
+
+                  // backdoor write back original value to avoid alert keep firing
+                  csr_poke(.csr(shadowed_csrs[index]), .value(origin_val), .kind(kind), .predict(1));
+                  `DV_SPINWAIT(cfg.m_alert_agent_cfg[alert_name].vif.wait_ack_complete();,
+                               $sformatf("timeout for alert:%0s", alert_name))
+
+                  // wait at least two clock cycle between alert_handshakes
+                  cfg.clk_rst_vif.wait_clks(2);
                 end
-
-                `DV_CHECK_EQ(has_storage_error, 1,
-                             "dv_base_reg did not predict shadow storage error");
-                `DV_SPINWAIT(while (!cfg.m_alert_agent_cfg[alert_name].vif.get_alert())
-                             cfg.clk_rst_vif.wait_clks(1);,
-                             $sformatf("%0s shadow_reg storage_err alert not detected",
-                                       shadowed_csrs[index].get_name()));
-
-                // backdoor write back original value to avoid alert keep firing
-                csr_poke(.csr(shadowed_csrs[index]), .value(origin_val), .kind(kind), .predict(1));
-                `DV_SPINWAIT(cfg.m_alert_agent_cfg[alert_name].vif.wait_ack_complete();,
-                             $sformatf("timeout for alert:%0s", alert_name))
-
-                // wait at least two clock cycle between alert_handshakes
-                cfg.clk_rst_vif.wait_clks(2);
               end
             end
           end
diff --git a/hw/ip/keymgr/dv/env/seq_lib/keymgr_cmd_invalid_vseq.sv b/hw/ip/keymgr/dv/env/seq_lib/keymgr_cmd_invalid_vseq.sv
index 1d65871..dc9de94 100644
--- a/hw/ip/keymgr/dv/env/seq_lib/keymgr_cmd_invalid_vseq.sv
+++ b/hw/ip/keymgr/dv/env/seq_lib/keymgr_cmd_invalid_vseq.sv
@@ -23,7 +23,12 @@
       keymgr_operations(.clr_output(0), .wait_done(0));
     end
 
-    repeat (100) @(cfg.m_alert_agent_cfg["fatal_fault_err"].vif.alert_tx.alert_p);
+    // could not accurately predict when first fatal alert happen, so wait for the first fatal
+    // alert to trigger
+    wait(cfg.m_alert_agent_cfg["fatal_fault_err"].vif.alert_tx_final.alert_p);
+    check_fatal_alert_nonblocking("fatal_fault_err");
+
+    cfg.clk_rst_vif.wait_clks($urandom_range(1, 500));
     csr_rd_check(.ptr(ral.working_state), .compare_value(keymgr_pkg::StDisabled));
   endtask : trigger_error