[dv,chip_sw] Add clkmgr_escalation_reset test

Fixes #14107

Signed-off-by: Guillermo Maturana <maturana@google.com>
diff --git a/hw/top_earlgrey/data/chip_testplan.hjson b/hw/top_earlgrey/data/chip_testplan.hjson
index 9e7d636..e7e8293 100644
--- a/hw/top_earlgrey/data/chip_testplan.hjson
+++ b/hw/top_earlgrey/data/chip_testplan.hjson
@@ -2542,7 +2542,7 @@
       name: chip_sw_flash_lc_escalate_en
       desc: '''Verify the lc_escalate_en signal from LC ctrl.
 
-            - Trigger an LC escalatation signal by generating an alert.
+            - Trigger an LC escalation signal by generating an alert.
             - Verify that all flash accesses are disabled when the escalation kicks in.
             - The CPU cannot read the flash either once escalation kicks in. This is unfortunately
               not possible because the CPU is also disabled when the escalation is triggered.
diff --git a/hw/top_earlgrey/dv/chip_sim_cfg.hjson b/hw/top_earlgrey/dv/chip_sim_cfg.hjson
index 664b793..fbee96c 100644
--- a/hw/top_earlgrey/dv/chip_sim_cfg.hjson
+++ b/hw/top_earlgrey/dv/chip_sim_cfg.hjson
@@ -27,6 +27,7 @@
   sim_tops: ["clkmgr_bind",
              "pwrmgr_bind",
              "rstmgr_bind",
+             "top_earlgrey_error_injection_ifs_bind",
              "top_earlgrey_bind",
              "xbar_main_bind",
              "xbar_peri_bind"]
@@ -297,6 +298,13 @@
       en_run_modes: ["sw_test_mode_test_rom"]
     }
     {
+      name: chip_sw_clkmgr_escalation_reset
+      uvm_test_seq: chip_sw_clkmgr_escalation_reset_vseq
+      sw_images: ["//sw/device/tests/sim_dv:clkmgr_escalation_reset_test:1"]
+      en_run_modes: ["sw_test_mode_test_rom"]
+      run_opts: ["+bypass_alert_ready_to_end_check=1"]
+    }
+    {
       name: chip_sw_sleep_pwm_pulses
       uvm_test_seq: chip_sw_pwm_pulses_vseq
       sw_images: ["//sw/device/tests:sleep_pwm_pulses_test:1"]
diff --git a/hw/top_earlgrey/dv/env/chip_env.core b/hw/top_earlgrey/dv/env/chip_env.core
index 1f65810..d83848c 100644
--- a/hw/top_earlgrey/dv/env/chip_env.core
+++ b/hw/top_earlgrey/dv/env/chip_env.core
@@ -47,6 +47,7 @@
       - seq_lib/chip_sw_base_vseq.sv: {is_include_file: true}
       - seq_lib/chip_jtag_base_vseq.sv: {is_include_file: true}
       - seq_lib/chip_sw_alert_handler_escalation_vseq.sv: {is_include_file: true}
+      - seq_lib/chip_sw_clkmgr_escalation_reset_vseq.sv: {is_include_file: true}
       - seq_lib/chip_sw_full_aon_reset_vseq.sv: {is_include_file: true}
       - seq_lib/chip_sw_deep_power_glitch_vseq.sv: {is_include_file: true}
       - seq_lib/chip_sw_main_power_glitch_vseq.sv: {is_include_file: true}
@@ -87,6 +88,7 @@
       - ast_ext_clk_if.sv
       - ast_supply_if.sv
       - pwrmgr_low_power_if.sv
+      - top_earlgrey_error_injection_ifs_bind.sv
     file_type: systemVerilogSource
 
 generate:
diff --git a/hw/top_earlgrey/dv/env/chip_env.sv b/hw/top_earlgrey/dv/env/chip_env.sv
index 829fbc9..2be2059 100644
--- a/hw/top_earlgrey/dv/env/chip_env.sv
+++ b/hw/top_earlgrey/dv/env/chip_env.sv
@@ -160,6 +160,11 @@
         )) begin
       `uvm_fatal(`gfn, "failed to get por_rstn_vif from uvm_config_db")
     end
+    if (!uvm_config_db#(virtual prim_count_if)::get(
+            this, "", "clkmgr_prim_count_vif", cfg.clkmgr_prim_count_vif
+        )) begin
+      `uvm_fatal(`gfn, "failed to get clkmgr_prim_count_vif from uvm_config_db")
+    end
 
     // disable alert_esc_agent's driver and only use its monitor
     foreach (LIST_OF_ALERTS[i]) begin
diff --git a/hw/top_earlgrey/dv/env/chip_env_cfg.sv b/hw/top_earlgrey/dv/env/chip_env_cfg.sv
index 838ff28..4d164ec 100644
--- a/hw/top_earlgrey/dv/env/chip_env_cfg.sv
+++ b/hw/top_earlgrey/dv/env/chip_env_cfg.sv
@@ -19,18 +19,19 @@
   bit                 use_spi_load_bootstrap = 0;
 
   // chip top interfaces
-  gpio_vif            gpio_vif;
-  virtual pins_if#(2) tap_straps_vif;
-  virtual pins_if#(2) dft_straps_vif;
-  virtual pins_if#(3) sw_straps_vif;
-  virtual pins_if#(1) rst_n_mon_vif;
-  virtual clk_rst_if  cpu_clk_rst_vif;
-  virtual pins_if#(1) pinmux_wkup_vif;
-  virtual pins_if#(1) por_rstn_vif;
-  virtual pins_if#(1) pwrb_in_vif;
-  virtual pins_if#(1) ec_rst_vif;
-  virtual pins_if#(1) flash_wp_vif;
-  virtual pins_if#(8) sysrst_ctrl_vif;
+  gpio_vif              gpio_vif;
+  virtual pins_if#(2)   tap_straps_vif;
+  virtual pins_if#(2)   dft_straps_vif;
+  virtual pins_if#(3)   sw_straps_vif;
+  virtual pins_if#(1)   rst_n_mon_vif;
+  virtual clk_rst_if    cpu_clk_rst_vif;
+  virtual pins_if#(1)   pinmux_wkup_vif;
+  virtual pins_if#(1)   por_rstn_vif;
+  virtual pins_if#(1)   pwrb_in_vif;
+  virtual pins_if#(1)   ec_rst_vif;
+  virtual pins_if#(1)   flash_wp_vif;
+  virtual pins_if#(8)   sysrst_ctrl_vif;
+  virtual prim_count_if clkmgr_prim_count_vif;
 
   // pwrmgr probe interface
   virtual pwrmgr_low_power_if   pwrmgr_low_power_vif;
diff --git a/hw/top_earlgrey/dv/env/seq_lib/chip_sw_clkmgr_escalation_reset_vseq.sv b/hw/top_earlgrey/dv/env/seq_lib/chip_sw_clkmgr_escalation_reset_vseq.sv
new file mode 100644
index 0000000..a2a7d90
--- /dev/null
+++ b/hw/top_earlgrey/dv/env/seq_lib/chip_sw_clkmgr_escalation_reset_vseq.sv
@@ -0,0 +1,19 @@
+// Copyright lowRISC contributors.
+// Licensed under the Apache License, Version 2.0, see LICENSE for details.
+// SPDX-License-Identifier: Apache-2.0
+
+class chip_sw_clkmgr_escalation_reset_vseq extends chip_sw_base_vseq;
+  `uvm_object_utils(chip_sw_clkmgr_escalation_reset_vseq)
+
+  `uvm_object_new
+
+  virtual task body();
+    // This sequence will trigger a fatal sec_cm failure.
+    super.body();
+
+    `uvm_info(`gfn, "Waiting to inject error", UVM_MEDIUM)
+    `DV_WAIT(cfg.sw_logger_vif.printed_log == "Ready for error injection",
+             "Timeout waiting for error injection request.")
+    cfg.clkmgr_prim_count_vif.if_proxy.inject_fault();
+   endtask
+endclass
diff --git a/hw/top_earlgrey/dv/env/seq_lib/chip_vseq_list.sv b/hw/top_earlgrey/dv/env/seq_lib/chip_vseq_list.sv
index 1162245..87d473b 100644
--- a/hw/top_earlgrey/dv/env/seq_lib/chip_vseq_list.sv
+++ b/hw/top_earlgrey/dv/env/seq_lib/chip_vseq_list.sv
@@ -12,6 +12,7 @@
 `include "chip_sw_base_vseq.sv"
 `include "chip_jtag_base_vseq.sv"
 `include "chip_prim_tl_access_vseq.sv"
+`include "chip_sw_clkmgr_escalation_reset_vseq.sv"
 `include "chip_sw_full_aon_reset_vseq.sv"
 `include "chip_sw_deep_power_glitch_vseq.sv"
 `include "chip_sw_main_power_glitch_vseq.sv"
diff --git a/hw/top_earlgrey/dv/env/top_earlgrey_error_injection_ifs_bind.sv b/hw/top_earlgrey/dv/env/top_earlgrey_error_injection_ifs_bind.sv
new file mode 100644
index 0000000..da7167a
--- /dev/null
+++ b/hw/top_earlgrey/dv/env/top_earlgrey_error_injection_ifs_bind.sv
@@ -0,0 +1,10 @@
+// Copyright lowRISC contributors.
+// Licensed under the Apache License, Version 2.0, see LICENSE for details.
+// SPDX-License-Identifier: Apache-2.0
+
+// This adds interfaces that enable error injection in selected modules.
+module top_earlgrey_error_injection_ifs_bind;
+
+  // This is used to inject count errors in clkmgr.
+  bind top_earlgrey.u_clkmgr_aon.u_clk_main_aes_trans.u_idle_cnt prim_count_if prim_count_if(.*);
+endmodule
diff --git a/hw/top_earlgrey/dv/tb/tb.sv b/hw/top_earlgrey/dv/tb/tb.sv
index e7c11b2..bdd6b5f 100644
--- a/hw/top_earlgrey/dv/tb/tb.sv
+++ b/hw/top_earlgrey/dv/tb/tb.sv
@@ -380,7 +380,9 @@
     uvm_config_db#(virtual pins_if #(8))::set(
         null, "*.env", "sysrst_ctrl_vif", sysrst_ctrl_if);
 
-
+    uvm_config_db#(virtual prim_count_if)::set(
+        null, "*.env", "clkmgr_prim_count_vif",
+        dut.top_earlgrey.u_clkmgr_aon.u_clk_main_aes_trans.u_idle_cnt.prim_count_if);
 
     // temp disable pinmux assertion AonWkupReqKnownO_A because driving X in spi_device.sdi and
     // WkupPadSel choose IO_DPS1 in MIO will trigger this assertion
diff --git a/sw/device/lib/testing/aon_timer_testutils.c b/sw/device/lib/testing/aon_timer_testutils.c
index ee1167b..3382d4c 100644
--- a/sw/device/lib/testing/aon_timer_testutils.c
+++ b/sw/device/lib/testing/aon_timer_testutils.c
@@ -15,7 +15,8 @@
 #include "hw/top_earlgrey/sw/autogen/top_earlgrey.h"
 
 uint32_t aon_timer_testutils_get_aon_cycles_from_us(uint64_t microseconds) {
-  uint64_t cycles = udiv64_slow(microseconds * kClockFreqAonHz, 1000000, NULL);
+  uint64_t cycles = udiv64_slow(microseconds * kClockFreqAonHz, 1000000,
+                                /*rem_out=*/NULL);
   CHECK(cycles < UINT32_MAX,
         "The value 0x%08x%08x can't fit into the 32 bits timer counter.",
         (cycles >> 32), (uint32_t)cycles);
@@ -23,7 +24,8 @@
 }
 
 uint32_t aon_timer_testutils_get_us_from_aon_cycles(uint64_t cycles) {
-  uint64_t uss = udiv64_slow(cycles * 1000000, kClockFreqAonHz, NULL);
+  uint64_t uss = udiv64_slow(cycles * 1000000, kClockFreqAonHz,
+                             /*rem_out=*/NULL);
   CHECK(uss < UINT32_MAX,
         "The value 0x%08x%08x can't fit into the 32 bits timer counter.",
         (uss >> 32), (uint32_t)uss);
diff --git a/sw/device/lib/testing/clkmgr_testutils.c b/sw/device/lib/testing/clkmgr_testutils.c
index 79ca6a4..d053c28 100644
--- a/sw/device/lib/testing/clkmgr_testutils.c
+++ b/sw/device/lib/testing/clkmgr_testutils.c
@@ -44,15 +44,22 @@
   // Notice the ratios are small enough to fit a uint32_t, even if the Hz number
   // is in uint64_t.
   const uint32_t kDeviceCpuCount =
-      cast_safely(udiv64_slow(kClockFreqCpuHz, kClockFreqAonHz, NULL));
-  const uint32_t kDeviceIoCount = cast_safely(
-      udiv64_slow(kClockFreqPeripheralHz, kClockFreqAonHz, NULL) * 4);
-  const uint32_t kDeviceIoDiv2Count = cast_safely(
-      udiv64_slow(kClockFreqPeripheralHz, kClockFreqAonHz, NULL) * 2);
+      cast_safely(udiv64_slow(kClockFreqCpuHz, kClockFreqAonHz,
+                              /*rem_out=*/NULL));
+  const uint32_t kDeviceIoCount =
+      cast_safely(udiv64_slow(kClockFreqPeripheralHz, kClockFreqAonHz,
+                              /*rem_out=*/NULL) *
+                  4);
+  const uint32_t kDeviceIoDiv2Count =
+      cast_safely(udiv64_slow(kClockFreqPeripheralHz, kClockFreqAonHz,
+                              /*rem_out=*/NULL) *
+                  2);
   const uint32_t kDeviceIoDiv4Count =
-      cast_safely(udiv64_slow(kClockFreqPeripheralHz, kClockFreqAonHz, NULL));
+      cast_safely(udiv64_slow(kClockFreqPeripheralHz, kClockFreqAonHz,
+                              /*rem_out=*/NULL));
   const uint32_t kDeviceUsbCount =
-      cast_safely(udiv64_slow(kClockFreqUsbHz, kClockFreqAonHz, NULL));
+      cast_safely(udiv64_slow(kClockFreqUsbHz, kClockFreqAonHz,
+                              /*rem_out=*/NULL));
 
   // The expected counts are derived from the ratios of the frequencies of the
   // various clocks to the AON clock. For example, 48 Mhz / 200 kHz = 240, so
diff --git a/sw/device/tests/aon_timer_wdog_lc_escalate_test.c b/sw/device/tests/aon_timer_wdog_lc_escalate_test.c
index a8e4eed..72b80cc 100644
--- a/sw/device/tests/aon_timer_wdog_lc_escalate_test.c
+++ b/sw/device/tests/aon_timer_wdog_lc_escalate_test.c
@@ -32,7 +32,7 @@
 /**
  * Program the alert handler to escalate on alerts upto phase 2 (i.e. reset) but
  * the phase 1 (i.e. wipe secrets) should occur and last during the time the
- * wdog is programed to bark.
+ * wdog is programmed to bark.
  */
 enum {
   kWdogBarkMicros = 3 * 100,          // 300 us
diff --git a/sw/device/tests/sensor_ctrl_alerts.c b/sw/device/tests/sensor_ctrl_alerts.c
index dfc52ba..cf359bf 100644
--- a/sw/device/tests/sensor_ctrl_alerts.c
+++ b/sw/device/tests/sensor_ctrl_alerts.c
@@ -80,7 +80,7 @@
 };
 
 /**
- *  Clear event trigger and recoverble status.
+ *  Clear event trigger and recoverable status.
  */
 static void clear_event(uint32_t idx, dif_toggle_t fatal) {
   CHECK_DIF_OK(dif_sensor_ctrl_set_ast_event_trigger(&sensor_ctrl, idx,
diff --git a/sw/device/tests/sim_dv/BUILD b/sw/device/tests/sim_dv/BUILD
index 3bb972b..411d54a 100644
--- a/sw/device/tests/sim_dv/BUILD
+++ b/sw/device/tests/sim_dv/BUILD
@@ -277,6 +277,29 @@
 )
 
 opentitan_functest(
+    name = "clkmgr_escalation_reset_test",
+    srcs = ["clkmgr_escalation_reset_test.c"],
+    targets = ["dv"],
+    deps = [
+        "//hw/top_earlgrey/sw/autogen:top_earlgrey",
+        "//sw/device/lib/base:memory",
+        "//sw/device/lib/base:mmio",
+        "//sw/device/lib/dif:alert_handler",
+        "//sw/device/lib/dif:aon_timer",
+        "//sw/device/lib/dif:clkmgr",
+        "//sw/device/lib/dif:rstmgr",
+        "//sw/device/lib/dif:rv_plic",
+        "//sw/device/lib/runtime:log",
+        "//sw/device/lib/testing:alert_handler_testutils",
+        "//sw/device/lib/testing:aon_timer_testutils",
+        "//sw/device/lib/testing:rstmgr_testutils",
+        "//sw/device/lib/testing:rv_plic_testutils",
+        "//sw/device/lib/testing/test_framework:check",
+        "//sw/device/lib/testing/test_framework:ottf_main",
+    ],
+)
+
+opentitan_functest(
     name = "clkmgr_external_clk_src_for_lc_test",
     srcs = ["clkmgr_external_clk_src_for_lc_test.c"],
     targets = ["dv"],
diff --git a/sw/device/tests/sim_dv/clkmgr_escalation_reset_test.c b/sw/device/tests/sim_dv/clkmgr_escalation_reset_test.c
new file mode 100644
index 0000000..df85b57
--- /dev/null
+++ b/sw/device/tests/sim_dv/clkmgr_escalation_reset_test.c
@@ -0,0 +1,273 @@
+// Copyright lowRISC contributors.
+// Licensed under the Apache License, Version 2.0, see LICENSE for details.
+// SPDX-License-Identifier: Apache-2.0
+
+#include <assert.h>
+#include <limits.h>
+#include <stdbool.h>
+#include <stdint.h>
+
+#include "sw/device/lib/base/math.h"
+#include "sw/device/lib/base/mmio.h"
+#include "sw/device/lib/dif/dif_alert_handler.h"
+#include "sw/device/lib/dif/dif_aon_timer.h"
+#include "sw/device/lib/dif/dif_clkmgr.h"
+#include "sw/device/lib/dif/dif_rstmgr.h"
+#include "sw/device/lib/dif/dif_rv_plic.h"
+#include "sw/device/lib/irq.h"
+#include "sw/device/lib/runtime/log.h"
+#include "sw/device/lib/testing/alert_handler_testutils.h"
+#include "sw/device/lib/testing/aon_timer_testutils.h"
+#include "sw/device/lib/testing/rstmgr_testutils.h"
+#include "sw/device/lib/testing/rv_plic_testutils.h"
+#include "sw/device/lib/testing/test_framework/FreeRTOSConfig.h"
+#include "sw/device/lib/testing/test_framework/check.h"
+#include "sw/device/lib/testing/test_framework/ottf_main.h"
+
+#include "hw/top_earlgrey/sw/autogen/top_earlgrey.h"
+
+OTTF_DEFINE_TEST_CONFIG();
+
+/**
+ * Program the alert handler to escalate on alerts upto phase 2 (i.e. reset).
+ * Also program the aon timer with:
+ * - bark after escalation starts, so the interrupt is suppressed by escalation,
+ * - bite after escalation reset, so we should not get timer reset.
+ */
+enum {
+  kWdogBarkMicros = 3 * 100,          // 300 us
+  kWdogBiteMicros = 8 * 100,          // 800 us
+  kEscalationPhase0Micros = 2 * 100,  // 200 us
+  // The cpu value is set slightly larger, since if they are the same it is
+  // possible busy_spin_micros in execute_test can complete before the
+  // interrupt.
+  kEscalationPhase0MicrosCpu = kEscalationPhase0Micros + 20,  // 220 us
+  kEscalationPhase1Micros = 2 * 100,                          // 200 us
+  kEscalationPhase2Micros = 100,                              // 100 us
+};
+
+static_assert(kWdogBarkMicros < kWdogBiteMicros &&
+                  kWdogBarkMicros > kEscalationPhase0Micros &&
+                  kWdogBarkMicros <
+                      (kEscalationPhase0Micros + kEscalationPhase1Micros) &&
+                  kWdogBiteMicros >
+                      (kEscalationPhase0Micros + kEscalationPhase1Micros),
+              "The wdog bite shall happen only if escalation reset fails.");
+
+/**
+ * Objects to access the peripherals used in this test via dif API.
+ */
+static const uint32_t kPlicTarget = kTopEarlgreyPlicTargetIbex0;
+static dif_aon_timer_t aon_timer;
+static dif_rv_plic_t plic;
+static dif_clkmgr_t clkmgr;
+static dif_rstmgr_t rstmgr;
+static dif_alert_handler_t alert_handler;
+
+volatile bool interrupt_seen = false;
+const dif_alert_handler_alert_t expected_alert =
+    kTopEarlgreyAlertIdClkmgrAonFatalFault;
+
+/**
+ * External ISR.
+ *
+ * Handles all peripheral interrupts on Ibex. PLIC asserts an external interrupt
+ * line to the CPU, which results in a call to this OTTF ISR. This ISR
+ * overrides the default OTTF implementation.
+ */
+void ottf_external_isr(void) {
+  interrupt_seen = true;
+  dif_rv_plic_irq_id_t irq_id;
+  CHECK_DIF_OK(dif_rv_plic_irq_claim(&plic, kPlicTarget, &irq_id));
+
+  top_earlgrey_plic_peripheral_t peripheral = (top_earlgrey_plic_peripheral_t)
+      top_earlgrey_plic_interrupt_for_peripheral[irq_id];
+
+  if (peripheral == kTopEarlgreyPlicPeripheralAonTimerAon) {
+    uint32_t irq =
+        (irq_id - (dif_rv_plic_irq_id_t)
+                      kTopEarlgreyPlicIrqIdAonTimerAonWkupTimerExpired);
+
+    // We should not get aon timer interrupts since escalation suppresses them.
+    CHECK(false, "Unexpected aon timer interrupt %0d", irq);
+  } else if (peripheral == kTopEarlgreyPlicPeripheralAlertHandler) {
+    // Check the class.
+    dif_alert_handler_class_state_t state;
+    CHECK_DIF_OK(dif_alert_handler_get_class_state(
+        &alert_handler, kDifAlertHandlerClassA, &state));
+    CHECK(state == kDifAlertHandlerClassStatePhase0, "Wrong phase %d", state);
+
+    uint32_t irq =
+        (irq_id -
+         (dif_rv_plic_irq_id_t)kTopEarlgreyPlicIrqIdAlertHandlerClassa);
+
+    // Check we get the expected alert.
+    bool is_cause = false;
+    CHECK_DIF_OK(dif_alert_handler_alert_is_cause(&alert_handler,
+                                                  expected_alert, &is_cause));
+    CHECK(is_cause);
+    // Fatal alerts are only cleared by reset.
+
+    // Check the clkmgr has a fatal error.
+    dif_clkmgr_fatal_err_codes_t codes;
+    CHECK_DIF_OK(dif_clkmgr_fatal_err_code_get_codes(&clkmgr, &codes));
+    CHECK(codes == kDifClkmgrFatalErrTypeIdleCount);
+
+    CHECK_DIF_OK(dif_alert_handler_irq_acknowledge(&alert_handler, irq));
+  }
+
+  // Complete the IRQ by writing the IRQ source to the Ibex specific CC
+  // register.
+  CHECK_DIF_OK(dif_rv_plic_irq_complete(&plic, kPlicTarget, irq_id));
+}
+
+/**
+ * Initialize the peripherals used in this test.
+ */
+void init_peripherals(void) {
+  CHECK_DIF_OK(dif_clkmgr_init(
+      mmio_region_from_addr(TOP_EARLGREY_CLKMGR_AON_BASE_ADDR), &clkmgr));
+
+  CHECK_DIF_OK(dif_rstmgr_init(
+      mmio_region_from_addr(TOP_EARLGREY_RSTMGR_AON_BASE_ADDR), &rstmgr));
+
+  CHECK_DIF_OK(dif_aon_timer_init(
+      mmio_region_from_addr(TOP_EARLGREY_AON_TIMER_AON_BASE_ADDR), &aon_timer));
+
+  CHECK_DIF_OK(dif_rv_plic_init(
+      mmio_region_from_addr(TOP_EARLGREY_RV_PLIC_BASE_ADDR), &plic));
+
+  CHECK_DIF_OK(dif_alert_handler_init(
+      mmio_region_from_addr(TOP_EARLGREY_ALERT_HANDLER_BASE_ADDR),
+      &alert_handler));
+}
+
+/**
+ * Program the alert handler to escalate on alerts upto phase 2 (i.e. reset) but
+ * the phase 1 (i.e. wipe secrets) should occur and last during the time the
+ * wdog is programed to bark.
+ */
+static void alert_handler_config(void) {
+  dif_alert_handler_alert_t alerts[] = {expected_alert};
+  dif_alert_handler_class_t alert_classes[] = {kDifAlertHandlerClassA};
+
+  dif_alert_handler_escalation_phase_t esc_phases[] = {
+      {.phase = kDifAlertHandlerClassStatePhase0,
+       .signal = 0,
+       .duration_cycles = udiv64_slow(
+           kEscalationPhase0Micros * kClockFreqPeripheralHz, 1000000,
+           /*rem_out=*/NULL)},
+      {.phase = kDifAlertHandlerClassStatePhase1,
+       .signal = 1,
+       .duration_cycles = udiv64_slow(
+           kEscalationPhase1Micros * kClockFreqPeripheralHz, 1000000,
+           /*rem_out=*/NULL)},
+      {.phase = kDifAlertHandlerClassStatePhase2,
+       .signal = 3,
+       .duration_cycles = udiv64_slow(
+           kEscalationPhase2Micros * kClockFreqPeripheralHz, 1000000,
+           /*rem_out=*/NULL)}};
+
+  dif_alert_handler_class_config_t class_config[] = {{
+      .auto_lock_accumulation_counter = kDifToggleDisabled,
+      .accumulator_threshold = 0,
+      .irq_deadline_cycles =
+          udiv64_slow(10 * kClockFreqPeripheralHz, 1000000, /*rem_out=*/NULL),
+      .escalation_phases = esc_phases,
+      .escalation_phases_len = ARRAYSIZE(esc_phases),
+      .crashdump_escalation_phase = kDifAlertHandlerClassStatePhase3,
+  }};
+
+  dif_alert_handler_class_t classes[] = {kDifAlertHandlerClassA};
+  dif_alert_handler_config_t config = {
+      .alerts = alerts,
+      .alert_classes = alert_classes,
+      .alerts_len = ARRAYSIZE(alerts),
+      .classes = classes,
+      .class_configs = class_config,
+      .classes_len = ARRAYSIZE(class_config),
+      .ping_timeout = 0,
+  };
+
+  alert_handler_testutils_configure_all(&alert_handler, config,
+                                        kDifToggleEnabled);
+  // Enables alert handler irq.
+  CHECK_DIF_OK(dif_alert_handler_irq_set_enabled(
+      &alert_handler, kDifAlertHandlerIrqClassa, kDifToggleEnabled));
+}
+
+/**
+ * Execute the aon timer interrupt test.
+ */
+static void execute_test(dif_aon_timer_t *aon_timer) {
+  uint32_t bark_cycles =
+      aon_timer_testutils_get_aon_cycles_from_us(kWdogBarkMicros);
+  uint32_t bite_cycles =
+      aon_timer_testutils_get_aon_cycles_from_us(kWdogBiteMicros);
+
+  LOG_INFO(
+      "Wdog will bark after %u/%u us/cycles and bite after %u/%u us/cycles",
+      (uint32_t)kWdogBarkMicros, bark_cycles, (uint32_t)kWdogBiteMicros,
+      bite_cycles);
+
+  // Setup the wdog bark and bite timeouts.
+  aon_timer_testutils_watchdog_config(aon_timer, bark_cycles, bite_cycles,
+                                      /*pause_in_sleep=*/false);
+
+  // Trigger the clkmgr fatal error to start escalation.
+  LOG_INFO("Ready for error injection");
+
+  busy_spin_micros(kEscalationPhase0MicrosCpu);
+
+  // This should never run since escalation turns the CPU off.
+  CHECK(false, "The alert handler failed to escalate");
+}
+
+bool test_main(void) {
+  // Enable global and external IRQ at Ibex.
+  irq_global_ctrl(true);
+  irq_external_ctrl(true);
+
+  init_peripherals();
+
+  // Enable all the interrupts used in this test.
+  rv_plic_testutils_irq_range_enable(
+      &plic, kPlicTarget, kTopEarlgreyPlicIrqIdAonTimerAonWkupTimerExpired,
+      kTopEarlgreyPlicIrqIdAonTimerAonWdogTimerBark);
+  rv_plic_testutils_irq_range_enable(&plic, kPlicTarget,
+                                     kTopEarlgreyPlicIrqIdAlertHandlerClassa,
+                                     kTopEarlgreyPlicIrqIdAlertHandlerClassd);
+
+  alert_handler_config();
+
+  // Check if there was a HW reset caused by the escalation.
+  dif_rstmgr_reset_info_bitfield_t rst_info;
+  rst_info = rstmgr_testutils_reason_get();
+  rstmgr_testutils_reason_clear();
+
+  CHECK(rst_info == kDifRstmgrResetInfoPor ||
+            rst_info == kDifRstmgrResetInfoEscalation,
+        "Wrong reset reason %02X", rst_info);
+
+  if (rst_info == kDifRstmgrResetInfoPor) {
+    LOG_INFO("Booting for the first time, starting test");
+    execute_test(&aon_timer);
+  } else if (rst_info == kDifRstmgrResetInfoEscalation) {
+    LOG_INFO("Booting for the second time due to escalation reset");
+    // Check the clkmgr has no fatal errors due to reset.
+    dif_clkmgr_fatal_err_codes_t codes;
+    CHECK_DIF_OK(dif_clkmgr_fatal_err_code_get_codes(&clkmgr, &codes));
+    CHECK(codes == 0, "Fatal error codes should be cleared upon reset");
+    // Check the alert handler cause is also cleared.
+    bool is_cause = true;
+    CHECK_DIF_OK(dif_alert_handler_alert_is_cause(&alert_handler,
+                                                  expected_alert, &is_cause));
+    CHECK(!is_cause);
+
+  } else {
+    LOG_ERROR("Unexpected rst_info=0x%x", rst_info);
+    return false;
+  }
+
+  return true;
+}