[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; +}