[dv/alert_handler] Add a direct sequence to enhance ping mechanism

This PR is based on issue #14556 where it is hard to hit the timing
corner case in the current mask setting.
To be able to cover this case faster, in block level I create a direct
sequence test to simulate the chip level scenario but force the
`wait_cyc_mask_i` to a much smaller value.
This will increase the chance of finding the corner case like issue #14556.

Signed-off-by: Cindy Chen <chencindy@opentitan.org>
diff --git a/hw/ip_templates/alert_handler/data/alert_handler_testplan.hjson b/hw/ip_templates/alert_handler/data/alert_handler_testplan.hjson
index 735c7a6..bda03db 100644
--- a/hw/ip_templates/alert_handler/data/alert_handler_testplan.hjson
+++ b/hw/ip_templates/alert_handler/data/alert_handler_testplan.hjson
@@ -128,6 +128,21 @@
       stage: V2
       tests: ["alert_handler_stress_all"]
     }
+    {
+      name: alert_handler_entropy_stress_test
+      desc: '''
+            Stress the alert_handler's entropy request and make sure there is no spurious alert.
+
+            Stimulus:
+            - Randomly force the `wait_cyc_mask_i` to a legal value to stress the ping requests.
+            - Wait for all alerts at least being pinged for a few times.
+            Checks:
+            - Check alert_cause and loc_alert_cause registers to make sure there is no spurious
+              alert being fired.
+            '''
+      stage: V2
+      tests: ["alert_handler_entropy_stress"]
+    }
   ]
 
   covergroups: [
diff --git a/hw/ip_templates/alert_handler/dv/alert_handler_sim_cfg.hjson.tpl b/hw/ip_templates/alert_handler/dv/alert_handler_sim_cfg.hjson.tpl
index 800cb22..07a712e 100644
--- a/hw/ip_templates/alert_handler/dv/alert_handler_sim_cfg.hjson.tpl
+++ b/hw/ip_templates/alert_handler/dv/alert_handler_sim_cfg.hjson.tpl
@@ -106,6 +106,15 @@
     }
 
     {
+      name: alert_handler_entropy_stress
+      uvm_test_seq: alert_handler_entropy_stress_vseq
+      // This sequence forces signal `wait_cyc_mask_i` to a much smaller value.
+      // So all the timings are not accurate and we need to disable the scb.
+      run_opts: ["+en_scb=0"]
+      reseed: 20
+    }
+
+    {
       name: alert_handler_stress_all
       run_opts: ["+test_timeout_ns=15_000_000_000"]
     }
diff --git a/hw/ip_templates/alert_handler/dv/env/alert_handler_env.core b/hw/ip_templates/alert_handler/dv/env/alert_handler_env.core
index d5c9d89..d6db6f8 100644
--- a/hw/ip_templates/alert_handler/dv/env/alert_handler_env.core
+++ b/hw/ip_templates/alert_handler/dv/env/alert_handler_env.core
@@ -31,6 +31,7 @@
       - seq_lib/alert_handler_ping_timeout_vseq.sv: {is_include_file: true}
       - seq_lib/alert_handler_lpg_vseq.sv: {is_include_file: true}
       - seq_lib/alert_handler_lpg_stub_clk_vseq.sv: {is_include_file: true}
+      - seq_lib/alert_handler_entropy_stress_vseq.sv: {is_include_file: true}
       - seq_lib/alert_handler_stress_all_vseq.sv: {is_include_file: true}
     file_type: systemVerilogSource
 
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 f582ee6..1cb36b8 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
@@ -41,4 +41,12 @@
                                    NLpg, index))
     end
   endfunction
+
+  task automatic set_wait_cyc_mask(logic [PING_CNT_DW-1:0] val);
+    static logic [PING_CNT_DW-1:0] val_static;
+    begin
+      val_static = val;
+      force tb.dut.u_ping_timer.wait_cyc_mask_i = val_static;
+    end
+  endtask
 endinterface
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 7536525..8c414e2 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
@@ -65,8 +65,8 @@
                                         .value(loc_alert_class[i]));
   endtask
 
-  virtual task alert_handler_rand_wr_class_ctrl(bit [NUM_ALERT_CLASSES-1:0] lock_bit);
-    bit [NUM_ALERT_CLASSES-1:0] class_en = $urandom();
+  virtual task alert_handler_rand_wr_class_ctrl(bit [NUM_ALERT_CLASSES-1:0] lock_bit,
+                                                bit [NUM_ALERT_CLASSES-1:0] class_en = $urandom());
     if (class_en[0]) `RAND_WRITE_CLASS_CTRL(a, lock_bit[0])
     if (class_en[1]) `RAND_WRITE_CLASS_CTRL(b, lock_bit[1])
     if (class_en[2]) `RAND_WRITE_CLASS_CTRL(c, lock_bit[2])
@@ -343,7 +343,7 @@
   endtask
 
   // This task will response to all alert_ping
-  virtual task run_alert_ping_rsp_seq_nonblocking(bit [NUM_ALERTS-1:0] alert_int_err);
+  virtual task run_alert_ping_rsp_seq_nonblocking(bit [NUM_ALERTS-1:0] alert_int_err = 0);
     foreach (cfg.alert_host_cfg[i]) begin
       automatic int index = i;
       fork
diff --git a/hw/ip_templates/alert_handler/dv/env/seq_lib/alert_handler_entropy_stress_vseq.sv b/hw/ip_templates/alert_handler/dv/env/seq_lib/alert_handler_entropy_stress_vseq.sv
new file mode 100644
index 0000000..400aff3
--- /dev/null
+++ b/hw/ip_templates/alert_handler/dv/env/seq_lib/alert_handler_entropy_stress_vseq.sv
@@ -0,0 +1,97 @@
+// Copyright lowRISC contributors.
+// Licensed under the Apache License, Version 2.0, see LICENSE for details.
+// SPDX-License-Identifier: Apache-2.0
+
+// This sequence uses a fixed setting to enable all alerts and locks them to class A.
+// Then enable all local alerts and locks them to class B.
+// Randomly force the `wait_cyc_mask_i` from design to a valid small number to fasten the ping
+// request mechanism.
+// Finally this sequence wait until alerts are pinged certain times.
+class alert_handler_entropy_stress_vseq extends alert_handler_smoke_vseq;
+  `uvm_object_utils(alert_handler_entropy_stress_vseq)
+
+  `uvm_object_new
+
+  rand bit [7:0] forced_mask_val;
+  rand int num_pings;
+
+  constraint valid_mask_val_c {
+    forced_mask_val >= 'h7;
+    $onehot(32'(forced_mask_val) + 1) == 1;
+  };
+
+  constraint num_pings_c {
+    if (forced_mask_val > 'hf0) {
+      num_pings inside {[1 : 2]};
+    } else {
+      num_pings inside {[1 : 3]};
+    }
+  }
+
+  virtual task pre_start();
+    `DV_CHECK_RANDOMIZE_FATAL(this)
+    cfg.alert_handler_vif.set_wait_cyc_mask(forced_mask_val);
+
+    foreach (cfg.alert_host_cfg[i]) begin
+      cfg.alert_host_cfg[i].alert_delay_max = 0;
+      cfg.alert_host_cfg[i].ping_delay_max = 0;
+    end
+    super.pre_start();
+  endtask
+
+  task body();
+    bit [NUM_LOCAL_ALERTS-1:0][NUM_ALERT_CLASSES-1:0] loc_alert_class;
+
+    foreach (loc_alert_class[i]) loc_alert_class[i] = 1;
+
+    `uvm_info(`gfn, "Test started", UVM_LOW)
+
+    run_esc_rsp_seq_nonblocking();
+    run_alert_ping_rsp_seq_nonblocking();
+
+    alert_handler_init(.intr_en('1),                       // Enable all interrupts
+                       .alert_en('1),                      // Enable all alerts
+                       .alert_class(0),                    // Set all alerts to class A
+                       .loc_alert_en('1),                  // Enable all local alerts
+                       .loc_alert_class(loc_alert_class)); // Set all local alerts to class B
+
+    // Enable all classes and lock them.
+    alert_handler_rand_wr_class_ctrl('1, '1);
+
+    // Enable ping timer.
+    csr_wr(.ptr(ral.ping_timer_en_shadowed), .value(1));
+
+    // Lock alerts and configurations.
+    alert_handler_wr_regwen_regs(.regwen(0),
+                                 .alert_regwen(0),
+                                 .loc_alert_regwen(0),
+                                 .ping_timer_regwen(0),
+                                 .class_regwen(0));
+
+    // Wait for all alerts to be pinged at least once.
+    fork begin : isolation_fork
+      int num_alerts = NUM_ALERTS;
+      for (int i = 0; i < NUM_ALERTS; i++) begin
+        automatic int index = i;
+        fork begin
+          repeat (num_pings) cfg.alert_host_cfg[index].vif.wait_alert_ping();
+          num_alerts--;
+          `uvm_info(`gfn, $sformatf("alert %0d received %0d ping request.\n %0d alerts remaining.",
+                    index, num_pings, num_alerts), UVM_LOW);
+        end join_none
+      end
+      wait fork;
+    end join
+
+    cfg.clk_rst_vif.wait_clks($urandom_range(50, 500));
+
+    // Check no error or local alerts triggered.
+    foreach (ral.alert_cause[i]) begin
+      csr_rd_check(.ptr(ral.alert_cause[i]), .compare_value(0));
+    end
+    foreach (ral.loc_alert_cause[i]) begin
+      csr_rd_check(.ptr(ral.loc_alert_cause[i]), .compare_value(0));
+    end
+  endtask
+
+endclass : alert_handler_entropy_stress_vseq
diff --git a/hw/ip_templates/alert_handler/dv/env/seq_lib/alert_handler_vseq_list.sv b/hw/ip_templates/alert_handler/dv/env/seq_lib/alert_handler_vseq_list.sv
index 0735ff8..0d67972 100644
--- a/hw/ip_templates/alert_handler/dv/env/seq_lib/alert_handler_vseq_list.sv
+++ b/hw/ip_templates/alert_handler/dv/env/seq_lib/alert_handler_vseq_list.sv
@@ -14,4 +14,5 @@
 `include "alert_handler_ping_timeout_vseq.sv"
 `include "alert_handler_lpg_vseq.sv"
 `include "alert_handler_lpg_stub_clk_vseq.sv"
+`include "alert_handler_entropy_stress_vseq.sv"
 `include "alert_handler_stress_all_vseq.sv"
diff --git a/hw/top_earlgrey/ip_autogen/alert_handler/data/alert_handler_testplan.hjson b/hw/top_earlgrey/ip_autogen/alert_handler/data/alert_handler_testplan.hjson
index 735c7a6..bda03db 100644
--- a/hw/top_earlgrey/ip_autogen/alert_handler/data/alert_handler_testplan.hjson
+++ b/hw/top_earlgrey/ip_autogen/alert_handler/data/alert_handler_testplan.hjson
@@ -128,6 +128,21 @@
       stage: V2
       tests: ["alert_handler_stress_all"]
     }
+    {
+      name: alert_handler_entropy_stress_test
+      desc: '''
+            Stress the alert_handler's entropy request and make sure there is no spurious alert.
+
+            Stimulus:
+            - Randomly force the `wait_cyc_mask_i` to a legal value to stress the ping requests.
+            - Wait for all alerts at least being pinged for a few times.
+            Checks:
+            - Check alert_cause and loc_alert_cause registers to make sure there is no spurious
+              alert being fired.
+            '''
+      stage: V2
+      tests: ["alert_handler_entropy_stress"]
+    }
   ]
 
   covergroups: [
diff --git a/hw/top_earlgrey/ip_autogen/alert_handler/dv/alert_handler_sim_cfg.hjson b/hw/top_earlgrey/ip_autogen/alert_handler/dv/alert_handler_sim_cfg.hjson
index a38a4f0..b64cf1d 100644
--- a/hw/top_earlgrey/ip_autogen/alert_handler/dv/alert_handler_sim_cfg.hjson
+++ b/hw/top_earlgrey/ip_autogen/alert_handler/dv/alert_handler_sim_cfg.hjson
@@ -106,6 +106,15 @@
     }
 
     {
+      name: alert_handler_entropy_stress
+      uvm_test_seq: alert_handler_entropy_stress_vseq
+      // This sequence forces signal `wait_cyc_mask_i` to a much smaller value.
+      // So all the timings are not accurate and we need to disable the scb.
+      run_opts: ["+en_scb=0"]
+      reseed: 20
+    }
+
+    {
       name: alert_handler_stress_all
       run_opts: ["+test_timeout_ns=15_000_000_000"]
     }
diff --git a/hw/top_earlgrey/ip_autogen/alert_handler/dv/env/alert_handler_env.core b/hw/top_earlgrey/ip_autogen/alert_handler/dv/env/alert_handler_env.core
index d5c9d89..d6db6f8 100644
--- a/hw/top_earlgrey/ip_autogen/alert_handler/dv/env/alert_handler_env.core
+++ b/hw/top_earlgrey/ip_autogen/alert_handler/dv/env/alert_handler_env.core
@@ -31,6 +31,7 @@
       - seq_lib/alert_handler_ping_timeout_vseq.sv: {is_include_file: true}
       - seq_lib/alert_handler_lpg_vseq.sv: {is_include_file: true}
       - seq_lib/alert_handler_lpg_stub_clk_vseq.sv: {is_include_file: true}
+      - seq_lib/alert_handler_entropy_stress_vseq.sv: {is_include_file: true}
       - seq_lib/alert_handler_stress_all_vseq.sv: {is_include_file: true}
     file_type: systemVerilogSource
 
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 f582ee6..1cb36b8 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
@@ -41,4 +41,12 @@
                                    NLpg, index))
     end
   endfunction
+
+  task automatic set_wait_cyc_mask(logic [PING_CNT_DW-1:0] val);
+    static logic [PING_CNT_DW-1:0] val_static;
+    begin
+      val_static = val;
+      force tb.dut.u_ping_timer.wait_cyc_mask_i = val_static;
+    end
+  endtask
 endinterface
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 7536525..8c414e2 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
@@ -65,8 +65,8 @@
                                         .value(loc_alert_class[i]));
   endtask
 
-  virtual task alert_handler_rand_wr_class_ctrl(bit [NUM_ALERT_CLASSES-1:0] lock_bit);
-    bit [NUM_ALERT_CLASSES-1:0] class_en = $urandom();
+  virtual task alert_handler_rand_wr_class_ctrl(bit [NUM_ALERT_CLASSES-1:0] lock_bit,
+                                                bit [NUM_ALERT_CLASSES-1:0] class_en = $urandom());
     if (class_en[0]) `RAND_WRITE_CLASS_CTRL(a, lock_bit[0])
     if (class_en[1]) `RAND_WRITE_CLASS_CTRL(b, lock_bit[1])
     if (class_en[2]) `RAND_WRITE_CLASS_CTRL(c, lock_bit[2])
@@ -343,7 +343,7 @@
   endtask
 
   // This task will response to all alert_ping
-  virtual task run_alert_ping_rsp_seq_nonblocking(bit [NUM_ALERTS-1:0] alert_int_err);
+  virtual task run_alert_ping_rsp_seq_nonblocking(bit [NUM_ALERTS-1:0] alert_int_err = 0);
     foreach (cfg.alert_host_cfg[i]) begin
       automatic int index = i;
       fork
diff --git a/hw/top_earlgrey/ip_autogen/alert_handler/dv/env/seq_lib/alert_handler_entropy_stress_vseq.sv b/hw/top_earlgrey/ip_autogen/alert_handler/dv/env/seq_lib/alert_handler_entropy_stress_vseq.sv
new file mode 100644
index 0000000..400aff3
--- /dev/null
+++ b/hw/top_earlgrey/ip_autogen/alert_handler/dv/env/seq_lib/alert_handler_entropy_stress_vseq.sv
@@ -0,0 +1,97 @@
+// Copyright lowRISC contributors.
+// Licensed under the Apache License, Version 2.0, see LICENSE for details.
+// SPDX-License-Identifier: Apache-2.0
+
+// This sequence uses a fixed setting to enable all alerts and locks them to class A.
+// Then enable all local alerts and locks them to class B.
+// Randomly force the `wait_cyc_mask_i` from design to a valid small number to fasten the ping
+// request mechanism.
+// Finally this sequence wait until alerts are pinged certain times.
+class alert_handler_entropy_stress_vseq extends alert_handler_smoke_vseq;
+  `uvm_object_utils(alert_handler_entropy_stress_vseq)
+
+  `uvm_object_new
+
+  rand bit [7:0] forced_mask_val;
+  rand int num_pings;
+
+  constraint valid_mask_val_c {
+    forced_mask_val >= 'h7;
+    $onehot(32'(forced_mask_val) + 1) == 1;
+  };
+
+  constraint num_pings_c {
+    if (forced_mask_val > 'hf0) {
+      num_pings inside {[1 : 2]};
+    } else {
+      num_pings inside {[1 : 3]};
+    }
+  }
+
+  virtual task pre_start();
+    `DV_CHECK_RANDOMIZE_FATAL(this)
+    cfg.alert_handler_vif.set_wait_cyc_mask(forced_mask_val);
+
+    foreach (cfg.alert_host_cfg[i]) begin
+      cfg.alert_host_cfg[i].alert_delay_max = 0;
+      cfg.alert_host_cfg[i].ping_delay_max = 0;
+    end
+    super.pre_start();
+  endtask
+
+  task body();
+    bit [NUM_LOCAL_ALERTS-1:0][NUM_ALERT_CLASSES-1:0] loc_alert_class;
+
+    foreach (loc_alert_class[i]) loc_alert_class[i] = 1;
+
+    `uvm_info(`gfn, "Test started", UVM_LOW)
+
+    run_esc_rsp_seq_nonblocking();
+    run_alert_ping_rsp_seq_nonblocking();
+
+    alert_handler_init(.intr_en('1),                       // Enable all interrupts
+                       .alert_en('1),                      // Enable all alerts
+                       .alert_class(0),                    // Set all alerts to class A
+                       .loc_alert_en('1),                  // Enable all local alerts
+                       .loc_alert_class(loc_alert_class)); // Set all local alerts to class B
+
+    // Enable all classes and lock them.
+    alert_handler_rand_wr_class_ctrl('1, '1);
+
+    // Enable ping timer.
+    csr_wr(.ptr(ral.ping_timer_en_shadowed), .value(1));
+
+    // Lock alerts and configurations.
+    alert_handler_wr_regwen_regs(.regwen(0),
+                                 .alert_regwen(0),
+                                 .loc_alert_regwen(0),
+                                 .ping_timer_regwen(0),
+                                 .class_regwen(0));
+
+    // Wait for all alerts to be pinged at least once.
+    fork begin : isolation_fork
+      int num_alerts = NUM_ALERTS;
+      for (int i = 0; i < NUM_ALERTS; i++) begin
+        automatic int index = i;
+        fork begin
+          repeat (num_pings) cfg.alert_host_cfg[index].vif.wait_alert_ping();
+          num_alerts--;
+          `uvm_info(`gfn, $sformatf("alert %0d received %0d ping request.\n %0d alerts remaining.",
+                    index, num_pings, num_alerts), UVM_LOW);
+        end join_none
+      end
+      wait fork;
+    end join
+
+    cfg.clk_rst_vif.wait_clks($urandom_range(50, 500));
+
+    // Check no error or local alerts triggered.
+    foreach (ral.alert_cause[i]) begin
+      csr_rd_check(.ptr(ral.alert_cause[i]), .compare_value(0));
+    end
+    foreach (ral.loc_alert_cause[i]) begin
+      csr_rd_check(.ptr(ral.loc_alert_cause[i]), .compare_value(0));
+    end
+  endtask
+
+endclass : alert_handler_entropy_stress_vseq
diff --git a/hw/top_earlgrey/ip_autogen/alert_handler/dv/env/seq_lib/alert_handler_vseq_list.sv b/hw/top_earlgrey/ip_autogen/alert_handler/dv/env/seq_lib/alert_handler_vseq_list.sv
index 0735ff8..0d67972 100644
--- a/hw/top_earlgrey/ip_autogen/alert_handler/dv/env/seq_lib/alert_handler_vseq_list.sv
+++ b/hw/top_earlgrey/ip_autogen/alert_handler/dv/env/seq_lib/alert_handler_vseq_list.sv
@@ -14,4 +14,5 @@
 `include "alert_handler_ping_timeout_vseq.sv"
 `include "alert_handler_lpg_vseq.sv"
 `include "alert_handler_lpg_stub_clk_vseq.sv"
+`include "alert_handler_entropy_stress_vseq.sv"
 `include "alert_handler_stress_all_vseq.sv"