From bf6fbc2121d1e9620d1be0b02e78146cc43eabb4 Mon Sep 17 00:00:00 2001
From: Andrea Caforio <andrea.caforio@lowrisc.org>
Date: Thu, 12 Dec 2024 12:45:38 +0100
Subject: [PATCH] [alert, sival] chip_sw_alert_handler_escalation

This commit generalizes the existing `alert_handler_escalation_test`
to all simulation targets to cover the
`chip_sw_alert_handler_escalation` testplan item (as specified in
`hw/top_earlgrey/data/ip/chip_alert_handler_testplan.hjson`).

Signed-off-by: Andrea Caforio <andrea.caforio@lowrisc.org>
---
 .../data/ip/chip_alert_handler_testplan.hjson |   2 +-
 hw/top_earlgrey/dv/chip_sim_cfg.hjson         |   2 +-
 sw/device/tests/BUILD                         |  35 +++++
 ...tion.c => alert_handler_escalation_test.c} | 142 +++++++++++-------
 sw/device/tests/sim_dv/BUILD                  |  25 ---
 5 files changed, 123 insertions(+), 83 deletions(-)
 rename sw/device/tests/{sim_dv/alert_handler_escalation.c => alert_handler_escalation_test.c} (55%)

diff --git a/hw/top_earlgrey/data/ip/chip_alert_handler_testplan.hjson b/hw/top_earlgrey/data/ip/chip_alert_handler_testplan.hjson
index 971109772d61d..8e8b34f77bccf 100644
--- a/hw/top_earlgrey/data/ip/chip_alert_handler_testplan.hjson
+++ b/hw/top_earlgrey/data/ip/chip_alert_handler_testplan.hjson
@@ -43,7 +43,7 @@
       si_stage: SV3
       lc_states: ["PROD"]
       tests: ["chip_sw_alert_handler_escalation"]
-      bazel: []
+      bazel: ["//sw/device/tests:alert_handler_escalation_test"]
       otp_mutate: true
       host_support: true
     }
diff --git a/hw/top_earlgrey/dv/chip_sim_cfg.hjson b/hw/top_earlgrey/dv/chip_sim_cfg.hjson
index 4a03f59d24ffe..9f53798d59cef 100644
--- a/hw/top_earlgrey/dv/chip_sim_cfg.hjson
+++ b/hw/top_earlgrey/dv/chip_sim_cfg.hjson
@@ -1308,7 +1308,7 @@
     {
       name: chip_sw_alert_handler_escalation
       uvm_test_seq: chip_sw_alert_handler_escalation_vseq
-      sw_images: ["//sw/device/tests/sim_dv:alert_handler_escalation_test:1:new_rules"]
+      sw_images: ["//sw/device/tests:alert_handler_escalation_test:1:new_rules"]
       en_run_modes: ["sw_test_mode_test_rom"]
       // Disable scoreboard to avoid incorrect alert prediction from the alert_monitor. Due to the
       // cross-domain alert senders and receivers, the monitor from the chip level did not support
diff --git a/sw/device/tests/BUILD b/sw/device/tests/BUILD
index 724cc3d9f8dd0..9c189d2efe7ef 100644
--- a/sw/device/tests/BUILD
+++ b/sw/device/tests/BUILD
@@ -277,6 +277,41 @@ opentitan_test(
     ],
 )
 
+opentitan_test(
+    name = "alert_handler_escalation_test",
+    srcs = ["alert_handler_escalation_test.c"],
+    exec_env = dicts.add(
+        EARLGREY_TEST_ENVS,
+        EARLGREY_SILICON_OWNER_ROM_EXT_ENVS,
+        {
+            "//hw/top_earlgrey:fpga_cw310_sival": None,
+            "//hw/top_earlgrey:silicon_creator": None,
+        },
+    ),
+    verilator = verilator_params(
+        timeout = "long",
+    ),
+    deps = [
+        "//hw/top_earlgrey:alert_handler_c_regs",
+        "//hw/top_earlgrey/sw/autogen:top_earlgrey",
+        "//sw/device/lib/arch:device",
+        "//sw/device/lib/base:memory",
+        "//sw/device/lib/base:mmio",
+        "//sw/device/lib/dif:alert_handler",
+        "//sw/device/lib/dif:clkmgr",
+        "//sw/device/lib/dif:keymgr",
+        "//sw/device/lib/dif:rstmgr",
+        "//sw/device/lib/dif:rv_core_ibex",
+        "//sw/device/lib/runtime:hart",
+        "//sw/device/lib/runtime:ibex",
+        "//sw/device/lib/runtime:log",
+        "//sw/device/lib/testing:alert_handler_testutils",
+        "//sw/device/lib/testing:keymgr_testutils",
+        "//sw/device/lib/testing:rstmgr_testutils",
+        "//sw/device/lib/testing/test_framework:ottf_main",
+    ],
+)
+
 opentitan_test(
     name = "alert_handler_ping_timeout_test",
     srcs = ["alert_handler_ping_timeout_test.c"],
diff --git a/sw/device/tests/sim_dv/alert_handler_escalation.c b/sw/device/tests/alert_handler_escalation_test.c
similarity index 55%
rename from sw/device/tests/sim_dv/alert_handler_escalation.c
rename to sw/device/tests/alert_handler_escalation_test.c
index 45e7b8669b0ee..015ae75355a2c 100644
--- a/sw/device/tests/sim_dv/alert_handler_escalation.c
+++ b/sw/device/tests/alert_handler_escalation_test.c
@@ -23,18 +23,6 @@
 #include "hw/top_earlgrey/sw/autogen/top_earlgrey.h"
 #include "rv_core_ibex_regs.h"  // Generated.
 
-/*
-  - Verify the first escalation results in NMI interrupt serviced by the CPU.
-  - Verify the second results in device being put in escalate state, via the LC
-  JTAG TAP.
-  - Verify the third results in chip reset.
-  - Ensure that all escalation handshakes complete without errors.
-
-  The first escalation is checked via the entry of the NMI handler and polling
-  by dv. The second escalation is directly checked by dv. The third escalation
-  is checked via reset reason.
- */
-
 OTTF_DEFINE_TEST_CONFIG();
 
 static dif_clkmgr_t clkmgr;
@@ -44,47 +32,33 @@ static dif_alert_handler_t alert_handler;
 static dif_rv_core_ibex_t rv_core_ibex;
 static dif_uart_t uart;
 
-typedef struct node {
-  const char *name;
-  dif_alert_handler_alert_t alert;
-  dif_alert_handler_class_t class;
-} node_t;
-
-static const dif_alert_handler_escalation_phase_t kEscProfiles[] = {
-    // TODO:
-    // this first/second duration must be long enough to
-    // accommodate a jtag transaction
-    // how can this be done in a non-hardcoded way?
-    {.phase = kDifAlertHandlerClassStatePhase0,
-     .signal = 0,
-     .duration_cycles = 10000},
-    {.phase = kDifAlertHandlerClassStatePhase1,
-     .signal = 1,
-     .duration_cycles = 10000},
-    {.phase = kDifAlertHandlerClassStatePhase2,
-     .signal = 3,
-     .duration_cycles = 3000}};
-
-static const dif_alert_handler_class_config_t kConfigProfiles[] = {{
-    .auto_lock_accumulation_counter = kDifToggleDisabled,
-    .accumulator_threshold = 0,
-    .irq_deadline_cycles = 0,
-    .escalation_phases = kEscProfiles,
-    .escalation_phases_len = 3,
-    .crashdump_escalation_phase = kDifAlertHandlerClassStatePhase1,
-}};
-
 /**
  * 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.
+ *
+ * @param exc_info Execution info.
  */
 void ottf_external_nmi_handler(uint32_t *exc_info) {
   // DO NOT REMOVE, DV sync message
   LOG_INFO("You are experiencing an NMI");
 
+  dif_rv_core_ibex_nmi_state_t nmi_state = (dif_rv_core_ibex_nmi_state_t){0};
+
+  CHECK_DIF_OK(dif_rv_core_ibex_get_nmi_state(
+      &rv_core_ibex, (dif_rv_core_ibex_nmi_state_t *)&nmi_state));
+
+  CHECK(nmi_state.alert_enabled && nmi_state.alert_raised,
+        "Alert handler NMI state not expected:\n\t"
+        "alert_enable:%x\n\talert_raised:%x\n",
+        nmi_state.alert_enabled, nmi_state.alert_raised);
+
+  dif_alert_handler_class_state_t state;
+  CHECK_DIF_OK(dif_alert_handler_get_class_state(
+      &alert_handler, kDifAlertHandlerClassA, &state));
+
   // Now intentionally hang the device
   CHECK_DIF_OK(dif_clkmgr_gateable_clock_set_enabled(
       &clkmgr, kTopEarlgreyGateableClocksIoDiv4Peri, kDifToggleDisabled));
@@ -94,7 +68,10 @@ void ottf_external_nmi_handler(uint32_t *exc_info) {
   LOG_FATAL("This message should never be seen");
 }
 
-bool test_main(void) {
+/**
+ * Initialize the dif handles required for this test.
+ */
+static void init_peripheral_handles(void) {
   CHECK_DIF_OK(dif_clkmgr_init(
       mmio_region_from_addr(TOP_EARLGREY_CLKMGR_AON_BASE_ADDR), &clkmgr));
 
@@ -114,6 +91,71 @@ bool test_main(void) {
 
   CHECK_DIF_OK(dif_keymgr_init(
       mmio_region_from_addr(TOP_EARLGREY_KEYMGR_BASE_ADDR), &keymgr));
+}
+
+/**
+ * Configure the alert handler. The escalation phases (NMI interrupt, LC scrap
+ * state, chip reset) are assigned to alert class A.
+ */
+static void config_alert_handler(void) {
+  // Escalation phase 0 is the NMI interrupt whose timeout value before
+  // progressing to phase 1 differs depending on the simulation device. For
+  // example, on the ChipWhisperer 1000000 cycles are enough to prevent a
+  // premature cancellation of the NMI interrupt handler (see
+  // `ottf_external_nmi_handler`).
+  uint32_t phase0DurationCycles = 1000000;
+  if (kDeviceType == kDeviceSimVerilator) {
+    phase0DurationCycles /= 10;
+  } else if (kDeviceType == kDeviceSimDV) {
+    phase0DurationCycles /= 100;
+  }
+
+  dif_alert_handler_escalation_phase_t escalationProfiles[] = {
+      {.phase = kDifAlertHandlerClassStatePhase0,
+       .signal = 0,
+       .duration_cycles = phase0DurationCycles},
+      {.phase = kDifAlertHandlerClassStatePhase1,
+       .signal = 1,
+       .duration_cycles = 10000},
+      {.phase = kDifAlertHandlerClassStatePhase2,
+       .signal = 3,
+       .duration_cycles = 10000},
+  };
+  dif_alert_handler_class_config_t configProfiles[] = {{
+      .auto_lock_accumulation_counter = kDifToggleDisabled,
+      .accumulator_threshold = 0,
+      .irq_deadline_cycles = 0,
+      .escalation_phases = escalationProfiles,
+      .escalation_phases_len = 3,
+      .crashdump_escalation_phase = kDifAlertHandlerClassStatePhase1,
+  }};
+
+  // set the alert we care about to class A
+  CHECK_DIF_OK(dif_alert_handler_configure_alert(
+      &alert_handler, kTopEarlgreyAlertIdRvCoreIbexRecovSwErr,
+      kDifAlertHandlerClassA, /*enabled=*/kDifToggleEnabled,
+      /*locked=*/kDifToggleEnabled));
+
+  // configure class A
+  CHECK_DIF_OK(dif_alert_handler_configure_class(
+      &alert_handler, kDifAlertHandlerClassA, configProfiles[0],
+      /*enabled=*/kDifToggleEnabled,
+      /*locked=*/kDifToggleEnabled));
+}
+
+/**
+ * - Verify the first escalation results in NMI interrupt serviced by the CPU.
+ * - Verify the second results in device being put in escalate state, via the LC
+ *   JTAG TAP.
+ * - Verify the third results in chip reset.
+ * - Ensure that all escalation handshakes complete without errors.
+ *
+ * The first escalation is checked via the entry of the NMI handler and polling
+ * by dv. The second escalation is directly checked by dv. The third escalation
+ * is checked via reset reason.
+ */
+bool test_main(void) {
+  init_peripheral_handles();
 
   // Check if there was a HW reset caused by the escalation.
   dif_rstmgr_reset_info_bitfield_t rst_info;
@@ -121,17 +163,7 @@ bool test_main(void) {
   rstmgr_testutils_reason_clear();
 
   if (rst_info & kDifRstmgrResetInfoPor) {
-    // set the alert we care about to class A
-    CHECK_DIF_OK(dif_alert_handler_configure_alert(
-        &alert_handler, kTopEarlgreyAlertIdRvCoreIbexRecovSwErr,
-        kDifAlertHandlerClassA, /*enabled=*/kDifToggleEnabled,
-        /*locked=*/kDifToggleEnabled));
-
-    // configurate class A
-    CHECK_DIF_OK(dif_alert_handler_configure_class(
-        &alert_handler, kDifAlertHandlerClassA, kConfigProfiles[0],
-        /*enabled=*/kDifToggleEnabled,
-        /*locked=*/kDifToggleEnabled));
+    config_alert_handler();
 
     // Initialize keymgr with otp contents
     CHECK_STATUS_OK(keymgr_testutils_advance_state(&keymgr, NULL));
@@ -151,12 +183,10 @@ bool test_main(void) {
     wait_for_interrupt();
     LOG_ERROR("Should have reset before this line");
     return false;
-
   } else if (rst_info & kDifRstmgrResetInfoEscalation) {
     // DO NOT REMOVE, DV sync message
     LOG_INFO("Reset due to alert escalation");
     return true;
-
   } else {
     LOG_ERROR("Unexpected reset info %d", rst_info);
   }
diff --git a/sw/device/tests/sim_dv/BUILD b/sw/device/tests/sim_dv/BUILD
index c1a2004d8ce3b..e007bcbfac9f7 100644
--- a/sw/device/tests/sim_dv/BUILD
+++ b/sw/device/tests/sim_dv/BUILD
@@ -649,31 +649,6 @@ opentitan_test(
     ],
 )
 
-opentitan_test(
-    name = "alert_handler_escalation_test",
-    srcs = ["alert_handler_escalation.c"],
-    exec_env = {"//hw/top_earlgrey:sim_dv": None},
-    deps = [
-        "//hw/top_earlgrey:alert_handler_c_regs",
-        "//hw/top_earlgrey/sw/autogen:top_earlgrey",
-        "//sw/device/lib/arch:device",
-        "//sw/device/lib/base:memory",
-        "//sw/device/lib/base:mmio",
-        "//sw/device/lib/dif:alert_handler",
-        "//sw/device/lib/dif:clkmgr",
-        "//sw/device/lib/dif:keymgr",
-        "//sw/device/lib/dif:rstmgr",
-        "//sw/device/lib/dif:rv_core_ibex",
-        "//sw/device/lib/runtime:hart",
-        "//sw/device/lib/runtime:ibex",
-        "//sw/device/lib/runtime:log",
-        "//sw/device/lib/testing:alert_handler_testutils",
-        "//sw/device/lib/testing:keymgr_testutils",
-        "//sw/device/lib/testing:rstmgr_testutils",
-        "//sw/device/lib/testing/test_framework:ottf_main",
-    ],
-)
-
 opentitan_test(
     name = "alert_handler_entropy_test",
     srcs = ["alert_handler_entropy_test.c"],