Add pwrmgr reset tests and clean up sw/device/tests/sim_dv/BUILD Change-Id: I0dcd47459bbf1782e92d50703fe8cd5d145b18c0
diff --git a/hw/top_matcha/dv/chip_sim_cfg.hjson b/hw/top_matcha/dv/chip_sim_cfg.hjson index 164c73a..00a5a91 100644 --- a/hw/top_matcha/dv/chip_sim_cfg.hjson +++ b/hw/top_matcha/dv/chip_sim_cfg.hjson
@@ -1092,7 +1092,7 @@ { name: chip_sw_pwrmgr_random_sleep_all_reset_reqs uvm_test_seq: chip_sw_random_sleep_all_reset_vseq - sw_images: ["//sw/device/tests/sim_dv:pwrmgr_random_sleep_all_reset_reqs_test:1"] + sw_images: ["//sw/device/tests/sim_dv:pwrmgr_random_sleep_all_reset_reqs_test:1:matcha"] en_run_modes: ["sw_test_mode_test_rom"] run_opts: ["+sw_test_timeout_ns=50_000_000"] run_timeout_mins: 120 @@ -1100,7 +1100,7 @@ { name: chip_sw_pwrmgr_deep_sleep_all_reset_reqs uvm_test_seq: chip_sw_deep_sleep_all_reset_vseq - sw_images: ["//sw/device/tests/sim_dv:pwrmgr_deep_sleep_all_reset_reqs_test:1"] + sw_images: ["//sw/device/tests/sim_dv:pwrmgr_deep_sleep_all_reset_reqs_test:1:matcha"] en_run_modes: ["sw_test_mode_test_rom"] run_opts: ["+sw_test_timeout_ns=50_000_000"] run_timeout_mins: 120 @@ -1108,28 +1108,28 @@ { name: chip_sw_pwrmgr_normal_sleep_all_reset_reqs uvm_test_seq: chip_sw_deep_sleep_all_reset_vseq - sw_images: ["//sw/device/tests/sim_dv:pwrmgr_normal_sleep_all_reset_reqs_test:1"] + sw_images: ["//sw/device/tests/sim_dv:pwrmgr_normal_sleep_all_reset_reqs_test:1:matcha"] en_run_modes: ["sw_test_mode_test_rom"] run_timeout_mins: 120 } { name: chip_sw_pwrmgr_sleep_power_glitch_reset uvm_test_seq: chip_sw_main_power_glitch_vseq - sw_images: ["//sw/device/tests/sim_dv:pwrmgr_sleep_power_glitch_test:1"] + sw_images: ["//sw/device/tests/sim_dv:pwrmgr_sleep_power_glitch_test:1:matcha"] en_run_modes: ["sw_test_mode_test_rom"] run_opts: ["+bypass_alert_ready_to_end_check=1"] } { name: chip_sw_pwrmgr_deep_sleep_power_glitch_reset uvm_test_seq: chip_sw_deep_power_glitch_vseq - sw_images: ["//sw/device/tests/sim_dv:pwrmgr_deep_sleep_power_glitch_test:1"] + sw_images: ["//sw/device/tests/sim_dv:pwrmgr_deep_sleep_power_glitch_test:1:matcha"] en_run_modes: ["sw_test_mode_test_rom"] run_opts: ["+bypass_alert_ready_to_end_check=1"] } { name: chip_sw_pwrmgr_random_sleep_power_glitch_reset uvm_test_seq: chip_sw_random_power_glitch_vseq - sw_images: ["//sw/device/tests/sim_dv:pwrmgr_random_sleep_power_glitch_reset_test:1"] + sw_images: ["//sw/device/tests/sim_dv:pwrmgr_random_sleep_power_glitch_reset_test:1:matcha"] en_run_modes: ["sw_test_mode_test_rom"] run_opts: ["+bypass_alert_ready_to_end_check=1", "+sw_test_timeout_ns=24_000_000"] @@ -1629,7 +1629,7 @@ { name: chip_sw_pwrmgr_b2b_sleep_reset_req uvm_test_seq: chip_sw_repeat_reset_wkup_vseq - sw_images: ["//sw/device/tests/sim_dv:pwrmgr_b2b_sleep_reset_test:1"] + sw_images: ["//sw/device/tests/sim_dv:pwrmgr_b2b_sleep_reset_test:1:matcha"] en_run_modes: ["sw_test_mode_test_rom"] run_opts: ["+sw_test_timeout_ns=35_000_000"] run_timeout_mins: 120 @@ -2081,6 +2081,8 @@ "chip_sw_otbn_smoketest", "chip_sw_otp_ctrl_escalation", "chip_sw_otp_ctrl_smoketest", + "chip_sw_pwrmgr_deep_sleep_power_glitch_reset", + "chip_sw_pwrmgr_sleep_power_glitch_reset", "chip_sw_rom_ctrl_integrity_check", "chip_sw_rstmgr_smoketest", "chip_sw_rstmgr_sw_req", @@ -2150,6 +2152,13 @@ "chip_sw_otbn_smoketest", "chip_sw_otp_ctrl_escalation", "chip_sw_otp_ctrl_smoketest", + "chip_sw_pwrmgr_b2b_sleep_reset_req", + "chip_sw_pwrmgr_deep_sleep_all_reset_reqs", + "chip_sw_pwrmgr_deep_sleep_power_glitch_reset", + "chip_sw_pwrmgr_normal_sleep_all_reset_reqs", + "chip_sw_pwrmgr_random_sleep_all_reset_reqs", + "chip_sw_pwrmgr_random_sleep_power_glitch_reset", + "chip_sw_pwrmgr_sleep_power_glitch_reset", "chip_sw_pwrmgr_sleep_sensor_ctrl_alert_wakeup", "chip_sw_rom_ctrl_integrity_check", "chip_sw_rstmgr_smoketest",
diff --git a/sw/device/tests/sim_dv/BUILD b/sw/device/tests/sim_dv/BUILD index 8740d12..9bec762 100644 --- a/sw/device/tests/sim_dv/BUILD +++ b/sw/device/tests/sim_dv/BUILD
@@ -8,32 +8,22 @@ matcha_dv_test( name = "uart_tx_rx_test", srcs = ["uart_tx_rx_test.c"], - targets = ["dv"], deps = [ - "//hw/top_matcha/ip/clkmgr/data/autogen:clkmgr_regs", - "//hw/top_matcha/ip/pinmux/data/autogen:pinmux_regs", - "//hw/top_matcha/sw/autogen:top_matcha", "//sw/device/lib/dif:clkmgr", + "//sw/device/lib/dif:pinmux", "//sw/device/lib/dif:rv_plic_sec", "//sw/device/lib/testing:clkmgr_testutils", - "//sw/device/lib/testing/test_framework:ottf_main", + "//sw/device/tests:test_dv_lib", "@lowrisc_opentitan//hw/ip/lc_ctrl/data:lc_ctrl_regs", - "@lowrisc_opentitan//sw/device/lib/base:mmio", "@lowrisc_opentitan//sw/device/lib/dif:lc_ctrl", "@lowrisc_opentitan//sw/device/lib/dif:uart", - "@lowrisc_opentitan//sw/device/lib/runtime:hart", - "@lowrisc_opentitan//sw/device/lib/runtime:irq", - "@lowrisc_opentitan//sw/device/lib/runtime:log", ], ) matcha_dv_test( name = "rv_dm_ndm_reset_req", srcs = ["rv_dm_ndm_reset_req.c"], - targets = ["dv"], deps = [ - "//hw/top_matcha/ip/pinmux/data/autogen:pinmux_regs", - "//hw/top_matcha/sw/autogen:top_matcha", "//sw/device/lib/dif:pinmux", "//sw/device/lib/dif:rstmgr", "//sw/device/tests:test_dv_lib", @@ -54,26 +44,87 @@ matcha_dv_test( name = "i2c_host_tx_rx_test", srcs = ["i2c_host_tx_rx_test.c"], - targets = ["dv"], deps = [ - "//hw/top_matcha/ip/pinmux/data/autogen:pinmux_regs", "//sw/device/lib/dif:pinmux", "//sw/device/lib/dif:rstmgr", + "//sw/device/lib/testing:isr_testutils", "//sw/device/tests:test_dv_lib", "@lowrisc_opentitan//sw/device/lib/dif:i2c", "@lowrisc_opentitan//sw/device/lib/testing:i2c_testutils", - "//sw/device/lib/testing:isr_testutils", "@lowrisc_opentitan//sw/device/lib/testing:rand_testutils", ], ) +cc_library( + name = "pwrmgr_reset_test_lib", + deps = [ + "//sw/device/lib/dif:alert_handler", + "//sw/device/lib/dif:pinmux", + "//sw/device/lib/dif:rstmgr", + "//sw/device/lib/dif:rv_plic_sec", + "//sw/device/lib/testing:pinmux_testutils", + "//sw/device/tests:test_dv_lib", + "@lowrisc_opentitan//sw/device/lib/base:abs_mmio", + "@lowrisc_opentitan//sw/device/lib/dif:aon_timer", + "@lowrisc_opentitan//sw/device/lib/dif:flash_ctrl", + "@lowrisc_opentitan//sw/device/lib/dif:pwrmgr", + "@lowrisc_opentitan//sw/device/lib/dif:rv_timer", + "@lowrisc_opentitan//sw/device/lib/dif:sysrst_ctrl", + "@lowrisc_opentitan//sw/device/lib/testing:alert_handler_testutils", + "@lowrisc_opentitan//sw/device/lib/testing:aon_timer_testutils", + "@lowrisc_opentitan//sw/device/lib/testing:flash_ctrl_testutils", + "@lowrisc_opentitan//sw/device/lib/testing:pwrmgr_testutils", + "@lowrisc_opentitan//sw/device/lib/testing:rstmgr_testutils", + "@lowrisc_opentitan//sw/device/lib/testing:rv_plic_testutils", + ], +) + +matcha_dv_test( + name = "pwrmgr_random_sleep_all_reset_reqs_test", + srcs = ["pwrmgr_random_sleep_all_reset_reqs_test.c"], + deps = [ + ":pwrmgr_reset_test_lib", + ], +) + +matcha_dv_test( + name = "pwrmgr_deep_sleep_all_reset_reqs_test", + srcs = ["pwrmgr_deep_sleep_all_reset_reqs_test.c"], + deps = [ + ":pwrmgr_reset_test_lib", + ], +) + +matcha_dv_test( + name = "pwrmgr_normal_sleep_all_reset_reqs_test", + srcs = ["pwrmgr_normal_sleep_all_reset_reqs_test.c"], + deps = [ + ":pwrmgr_reset_test_lib", + ], +) + +matcha_dv_test( + name = "pwrmgr_random_sleep_power_glitch_reset_test", + srcs = ["pwrmgr_random_sleep_power_glitch_reset_test.c"], + deps = [ + ":pwrmgr_reset_test_lib", + ], +) + +matcha_dv_test( + name = "pwrmgr_b2b_sleep_reset_test", + srcs = ["pwrmgr_b2b_sleep_reset_test.c"], + deps = [ + ":pwrmgr_reset_test_lib", + ], +) + ################################################################################ # Place the Opentitan-sourced DV test below. # # Opentitan DV binary build flow does not support centOS7, so we pull the # # source code and build them here using `matcha_dv_test` rule. # ################################################################################ - matcha_dv_test( name = "spi_tx_rx_test", srcs = ["@lowrisc_opentitan//sw/device/tests/sim_dv:spi_tx_rx_test.c"], @@ -168,3 +219,27 @@ "@lowrisc_opentitan//sw/device/lib/testing:rv_plic_testutils", ], ) + +matcha_dv_test( + name = "pwrmgr_sleep_power_glitch_test", + srcs = ["@lowrisc_opentitan//sw/device/tests/sim_dv:pwrmgr_sleep_power_glitch_test.c"], + deps = [ + "//sw/device/lib/dif:rstmgr", + "//sw/device/tests:test_dv_lib_opentitan", + "@lowrisc_opentitan//sw/device/lib/dif:pwrmgr", + "@lowrisc_opentitan//sw/device/lib/testing:pwrmgr_testutils", + "@lowrisc_opentitan//sw/device/lib/testing:rstmgr_testutils", + ], +) + +matcha_dv_test( + name = "pwrmgr_deep_sleep_power_glitch_test", + srcs = ["@lowrisc_opentitan//sw/device/tests/sim_dv:pwrmgr_deep_sleep_power_glitch_test.c"], + deps = [ + "//sw/device/lib/dif:rstmgr", + "//sw/device/tests:test_dv_lib_opentitan", + "@lowrisc_opentitan//sw/device/lib/dif:pwrmgr", + "@lowrisc_opentitan//sw/device/lib/testing:pwrmgr_testutils", + "@lowrisc_opentitan//sw/device/lib/testing:rstmgr_testutils", + ], +)
diff --git a/sw/device/tests/sim_dv/pwrmgr_b2b_sleep_reset_test.c b/sw/device/tests/sim_dv/pwrmgr_b2b_sleep_reset_test.c new file mode 100644 index 0000000..35b3be0 --- /dev/null +++ b/sw/device/tests/sim_dv/pwrmgr_b2b_sleep_reset_test.c
@@ -0,0 +1,182 @@ +// Copyright 2023 Google LLC. +// Copyright lowRISC contributors. +// Licensed under the Apache License, Version 2.0, see LICENSE for details. +// SPDX-License-Identifier: Apache-2.0 + +#include "aon_timer_regs.h" +#include "hw/top_matcha/sw/autogen/top_matcha.h" +#include "sw/device/lib/base/abs_mmio.h" +#include "sw/device/lib/base/mmio.h" +#include "sw/device/lib/dif/dif_aon_timer.h" +#include "sw/device/lib/dif/dif_flash_ctrl.h" +#include "sw/device/lib/dif/dif_pinmux.h" +#include "sw/device/lib/dif/dif_pwrmgr.h" +#include "sw/device/lib/dif/dif_rstmgr.h" +#include "sw/device/lib/dif/dif_sysrst_ctrl.h" +#include "sw/device/lib/runtime/log.h" +#include "sw/device/lib/testing/aon_timer_testutils.h" +#include "sw/device/lib/testing/flash_ctrl_testutils.h" +#include "sw/device/lib/testing/pwrmgr_testutils.h" +#include "sw/device/lib/testing/rstmgr_testutils.h" +#include "sw/device/lib/testing/test_framework/check.h" +#include "sw/device/lib/testing/test_framework/ottf_main.h" +/* + PWRMGR BACK TO BACK DEEP SLEEP, RESET / WAKEUP TEST + + This test introduces reset or wakeup event close to (before or after) + entering low power state. + + 'kNumRound' is set to 10 by sv sequence (chip_sw_repeat_reset_wkup_vseq.sv) + + For the reset event, the sequence assert power on resest by driving POR_N + PAD. For the wake up event, the seqeunce assert power button in by driving + IOR13 PAD. + + */ +OTTF_DEFINE_TEST_CONFIG(); + +static volatile const uint8_t kNumRound; + +static dif_flash_ctrl_state_t flash_ctrl; +static dif_sysrst_ctrl_t sysrst_ctrl; +static dif_pinmux_t pinmux; + +/** + * sysrst_ctrl config for test #1 + * . set sysrst_ctrl.KEY_INTR_CTL.pwrb_in_H2L to 1 + * . use IOR13 as pwrb_in + */ +static void prgm_push_button_wakeup() { + dif_sysrst_ctrl_input_change_config_t config = { + .input_changes = kDifSysrstCtrlInputPowerButtonH2L, + .debounce_time_threshold = 1, // 5us + }; + CHECK_DIF_OK( + dif_sysrst_ctrl_input_change_detect_configure(&sysrst_ctrl, config)); + CHECK_DIF_OK(dif_pinmux_input_select( + &pinmux, kTopMatchaPinmuxPeripheralInSysrstCtrlAonPwrbIn, + kTopMatchaPinmuxInselIor13)); +} + +bool test_main(void) { + // Issue a wakeup signal in ~1.5 ms through the AON timer. + // This timer is needed when reset or wakeup fail to + // bring state machine back to active state. + // This can happen when wake up event comes before low power + // entry event. + uint32_t wakeup_threshold = kDeviceType == kDeviceSimVerilator ? 3000 : 300; + + // Initialize pwrmgr + dif_pwrmgr_t pwrmgr; + CHECK_DIF_OK(dif_pwrmgr_init( + mmio_region_from_addr(TOP_MATCHA_PWRMGR_AON_BASE_ADDR), &pwrmgr)); + + // Initialize rstmgr since this will check some registers. + dif_rstmgr_t rstmgr; + CHECK_DIF_OK(dif_rstmgr_init( + mmio_region_from_addr(TOP_MATCHA_RSTMGR_AON_BASE_ADDR), &rstmgr)); + + // Initialize flash_ctrl + CHECK_DIF_OK(dif_flash_ctrl_init_state( + &flash_ctrl, + mmio_region_from_addr(TOP_MATCHA_FLASH_CTRL_CORE_BASE_ADDR))); + + // Initialize sysrst_ctrl + CHECK_DIF_OK(dif_sysrst_ctrl_init( + mmio_region_from_addr(TOP_MATCHA_SYSRST_CTRL_AON_BASE_ADDR), + &sysrst_ctrl)); + + // Initialize pinmux + CHECK_DIF_OK(dif_pinmux_init( + mmio_region_from_addr(TOP_MATCHA_PINMUX_AON_BASE_ADDR), &pinmux)); + + // First check the flash stored value + uint32_t event_idx = flash_ctrl_testutils_counter_get(0); + // Enable flash access + flash_ctrl_testutils_default_region_access(&flash_ctrl, + /*rd_en*/ true, + /*prog_en*/ true, + /*erase_en*/ true, + /*scramble_en*/ false, + /*ecc_en*/ false, + /*he_en*/ false); + + // Increment flash counter to know where we are + flash_ctrl_testutils_counter_increment(&flash_ctrl, 0); + + // Read wakeup reason before check + dif_pwrmgr_wakeup_reason_t wakeup_reason; + CHECK_DIF_OK(dif_pwrmgr_wakeup_reason_get(&pwrmgr, &wakeup_reason)); + LOG_INFO("wakeup type:%d wakeup reason: 0x%02X", wakeup_reason.types, + wakeup_reason.request_sources); + + if (wakeup_reason.types == 0) { + // POR reset + CHECK(wakeup_reason.request_sources == 0); + } else if (wakeup_reason.types == kDifPwrmgrWakeupTypeRequest) { + // sysrst_ctrl or aon_timer + CHECK(wakeup_reason.request_sources == (kDifPwrmgrWakeupRequestSourceOne | + kDifPwrmgrWakeupRequestSourceFive)); + } else { + LOG_ERROR("unexpected wakeup_type: 0x%x", wakeup_reason.types); + } + + // Read reset info before check + dif_rstmgr_reset_info_bitfield_t rst_info; + rst_info = rstmgr_testutils_reason_get(); + rstmgr_testutils_reason_clear(); + LOG_INFO("reset info = 0x%02X", rst_info); + + if (rst_info != kDifRstmgrResetInfoPor && + rst_info != kDifRstmgrResetInfoLowPowerExit) { + LOG_ERROR("unexpected reset info: 0x%x", rst_info); + } + + dif_aon_timer_t aon_timer; + CHECK_DIF_OK(dif_aon_timer_init( + mmio_region_from_addr(TOP_MATCHA_AON_TIMER_AON_BASE_ADDR), &aon_timer)); + + // Status clean up + if (event_idx > 0) { + // aon timer clean up + CHECK_DIF_OK(dif_aon_timer_wakeup_stop(&aon_timer)); + // mmio_region_write32( + // mmio_region_from_addr(TOP_MATCHA_AON_TIMER_AON_BASE_ADDR), + // AON_TIMER_WKUP_CAUSE_REG_OFFSET, 0); + CHECK_DIF_OK(dif_aon_timer_clear_wakeup_cause(&aon_timer)); + // sysrst ctrl status clean up + CHECK_DIF_OK(dif_sysrst_ctrl_ulp_wakeup_clear_status(&sysrst_ctrl)); + } + + if (event_idx < kNumRound) { + LOG_INFO("Test round %d", event_idx); + } else { + LOG_INFO("Test finish"); + return true; + } + + // pin mux / sysrst ctrl set up for push button wakeup + prgm_push_button_wakeup(); + + // Prepare rstmgr for a reset. + rstmgr_testutils_pre_reset(&rstmgr); + + // This is mark for sv sequence to prepare to asserting parallel event. + LOG_INFO("ready for power down"); + busy_spin_micros(10); + // timer setup in case wake up comes before entering low power mode + aon_timer_testutils_wakeup_config(&aon_timer, wakeup_threshold); + + // Deep sleep. + pwrmgr_testutils_enable_low_power( + &pwrmgr, + (kDifPwrmgrWakeupRequestSourceOne | kDifPwrmgrWakeupRequestSourceTwo | + kDifPwrmgrWakeupRequestSourceThree | kDifPwrmgrWakeupRequestSourceFour | + kDifPwrmgrWakeupRequestSourceFive | kDifPwrmgrWakeupRequestSourceSix), + 0); + + // Enter low power mode. + wait_for_interrupt(); + + return false; +}
diff --git a/sw/device/tests/sim_dv/pwrmgr_deep_sleep_all_reset_reqs_test.c b/sw/device/tests/sim_dv/pwrmgr_deep_sleep_all_reset_reqs_test.c new file mode 100644 index 0000000..b5f3191 --- /dev/null +++ b/sw/device/tests/sim_dv/pwrmgr_deep_sleep_all_reset_reqs_test.c
@@ -0,0 +1,520 @@ +// Copyright 2023 Google LLC. +// 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 "hw/top_matcha/sw/autogen/top_matcha.h" +#include "sw/device/lib/base/abs_mmio.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_flash_ctrl.h" +#include "sw/device/lib/dif/dif_pinmux.h" +#include "sw/device/lib/dif/dif_pwrmgr.h" +#include "sw/device/lib/dif/dif_rstmgr.h" +#include "sw/device/lib/dif/dif_rv_plic.h" +#include "sw/device/lib/dif/dif_rv_timer.h" +#include "sw/device/lib/dif/dif_sysrst_ctrl.h" +#include "sw/device/lib/runtime/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/flash_ctrl_testutils.h" +#include "sw/device/lib/testing/pwrmgr_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" + +// In dvsim, one run +// with --waves can take +// 1.874h | 38.068ms +// without --waves, +// 38.072m | 39.484ms + +OTTF_DEFINE_TEST_CONFIG(); +static volatile const uint8_t RST_IDX[7] = {0, 1, 2, 3, 4, 5, 6}; +static const uint32_t kPlicTarget = kTopMatchaPlicTargetIbex0; + +/** + * Objects to access the peripherals used in this test via dif API. + */ +static dif_flash_ctrl_state_t flash_ctrl; +static dif_rv_plic_t plic; +static dif_alert_handler_t alert_handler; +static dif_aon_timer_t aon_timer; +static dif_pwrmgr_t pwrmgr; +static dif_sysrst_ctrl_t sysrst_ctrl_aon; +static dif_rstmgr_t rstmgr; + +/** + * 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. + * + * Notice these settings are suitable for sim_dv. For other platforms we scale + * the resulting cycles by a factor of 10. + */ +enum { + kWdogBarkMicros = 3 * 100, // 300 us + kWdogBiteMicros = 4 * 100, // 400 us + kEscalationPhase0Micros = 1 * 100, // 100 us + // The cpu value is slightly larger to avoid flakey results. + kEscalationPhase0MicrosCpu = kEscalationPhase0Micros + 20, // 120 us + kEscalationPhase1Micros = 5 * 100, // 500 us + kEscalationPhase2Micros = 50, // 50 us +}; + +static_assert( + kWdogBarkMicros < kWdogBiteMicros && + kWdogBarkMicros > kEscalationPhase0Micros && + kWdogBarkMicros < (kEscalationPhase0Micros + kEscalationPhase1Micros) && + kWdogBiteMicros < (kEscalationPhase0Micros + kEscalationPhase1Micros), + "The wdog bark and bite shall happens during the escalation phase 1"); + +/** + * 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) { + top_matcha_plic_peripheral_t peripheral; + dif_rv_plic_irq_id_t irq_id; + uint32_t irq = 0; + uint32_t alert = 0; + + CHECK_DIF_OK(dif_rv_plic_irq_claim(&plic, kPlicTarget, &irq_id)); + + peripheral = (top_matcha_plic_peripheral_t) + top_matcha_plic_interrupt_for_peripheral[irq_id]; + + if (peripheral == kTopMatchaPlicPeripheralAonTimerAon) { + irq = + (dif_aon_timer_irq_t)(irq_id - + (dif_rv_plic_irq_id_t) + kTopMatchaPlicIrqIdAonTimerAonWkupTimerExpired); + + // Stops escalation process. + CHECK_DIF_OK(dif_alert_handler_escalation_clear(&alert_handler, + kDifAlertHandlerClassA)); + CHECK_DIF_OK(dif_aon_timer_irq_acknowledge(&aon_timer, irq)); + + CHECK(irq != kTopMatchaPlicIrqIdAonTimerAonWdogTimerBark, + "AON Timer Wdog should not bark"); + + } else if (peripheral == kTopMatchaPlicPeripheralAlertHandler) { + irq = (dif_rv_plic_irq_id_t)(irq_id - + (dif_rv_plic_irq_id_t) + kTopMatchaPlicIrqIdAlertHandlerClassa); + + CHECK_DIF_OK(dif_alert_handler_alert_acknowledge(&alert_handler, alert)); + + 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); + + 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) { + // Initialize pwrmgr. + CHECK_DIF_OK(dif_pwrmgr_init( + mmio_region_from_addr(TOP_MATCHA_PWRMGR_AON_BASE_ADDR), &pwrmgr)); + + // Initialize sysrst_ctrl. + CHECK_DIF_OK(dif_sysrst_ctrl_init( + mmio_region_from_addr(TOP_MATCHA_SYSRST_CTRL_AON_BASE_ADDR), + &sysrst_ctrl_aon)); + + // Initialize rstmgr to check the reset reason. + CHECK_DIF_OK(dif_rstmgr_init( + mmio_region_from_addr(TOP_MATCHA_RSTMGR_AON_BASE_ADDR), &rstmgr)); + + // Initialize aon timer to use the wdog. + CHECK_DIF_OK(dif_aon_timer_init( + mmio_region_from_addr(TOP_MATCHA_AON_TIMER_AON_BASE_ADDR), &aon_timer)); + + // Initialize flash_ctrl + CHECK_DIF_OK(dif_flash_ctrl_init_state( + &flash_ctrl, + mmio_region_from_addr(TOP_MATCHA_FLASH_CTRL_CORE_BASE_ADDR))); + + // Initialize plic. + CHECK_DIF_OK(dif_rv_plic_init( + mmio_region_from_addr(TOP_MATCHA_RV_PLIC_BASE_ADDR), &plic)); + + rv_plic_testutils_irq_range_enable( + &plic, kPlicTarget, kTopMatchaPlicIrqIdAonTimerAonWkupTimerExpired, + kTopMatchaPlicIrqIdAonTimerAonWdogTimerBark); + + // Initialize alert handler. + CHECK_DIF_OK(dif_alert_handler_init( + mmio_region_from_addr(TOP_MATCHA_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[] = {kTopMatchaAlertIdPwrmgrAonFatalFault}; + dif_alert_handler_class_t alert_classes[] = {kDifAlertHandlerClassA}; + + dif_alert_handler_escalation_phase_t esc_phases[] = { + {.phase = kDifAlertHandlerClassStatePhase0, + .signal = 0, + .duration_cycles = + alert_handler_testutils_get_cycles_from_us(kEscalationPhase0Micros) * + alert_handler_testutils_cycle_rescaling_factor()}, + {.phase = kDifAlertHandlerClassStatePhase1, + .signal = 1, + .duration_cycles = + alert_handler_testutils_get_cycles_from_us(kEscalationPhase1Micros) * + alert_handler_testutils_cycle_rescaling_factor()}, + {.phase = kDifAlertHandlerClassStatePhase2, + .signal = 3, + .duration_cycles = + alert_handler_testutils_get_cycles_from_us(kEscalationPhase2Micros) * + alert_handler_testutils_cycle_rescaling_factor()}}; + + dif_alert_handler_class_config_t class_config[] = {{ + .auto_lock_accumulation_counter = kDifToggleDisabled, + .accumulator_threshold = 0, + .irq_deadline_cycles = alert_handler_testutils_get_cycles_from_us(10) * + alert_handler_testutils_cycle_rescaling_factor(), + .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 trigger_escalate(dif_aon_timer_t *aon_timer, + const dif_pwrmgr_t *pwrmgr) { + uint64_t bark_cycles = + udiv64_slow(kWdogBarkMicros * kClockFreqAonHz, 1000000, NULL) * + alert_handler_testutils_cycle_rescaling_factor(); + uint64_t bite_cycles = + udiv64_slow(kWdogBiteMicros * kClockFreqAonHz, 1000000, NULL) * + alert_handler_testutils_cycle_rescaling_factor(); + + CHECK(bite_cycles < UINT32_MAX, + "The value %u can't fit into the 32 bits timer counter.", bite_cycles); + + LOG_INFO( + "Wdog will bark after %u/%u us/cycles and bite after %u/%u us/cycles", + (uint32_t)kWdogBarkMicros, (uint32_t)bark_cycles, + (uint32_t)kWdogBiteMicros, (uint32_t)bite_cycles); + + // Setup the wdog bark and bite timeouts. + aon_timer_testutils_watchdog_config(aon_timer, bark_cycles, bite_cycles, + false); + + // Trigger the alert handler to escalate. + dif_pwrmgr_alert_t alert = kDifPwrmgrAlertFatalFault; + CHECK_DIF_OK(dif_pwrmgr_alert_force(pwrmgr, alert)); +} + +/** + * Configure the sysrst. + */ +static void config_sysrst(const dif_pwrmgr_t *pwrmgr, + const dif_sysrst_ctrl_t *sysrst_ctrl_aon) { + LOG_INFO("sysrst enabled"); + + // Set sysrst as a reset source. + CHECK_DIF_OK(dif_pwrmgr_set_request_sources(pwrmgr, kDifPwrmgrReqTypeReset, + kDifPwrmgrResetRequestSourceOne, + kDifToggleEnabled)); + LOG_INFO("Reset Request SourceOne is set"); + + // Configure sysrst key combo + // reset pulse : 50 us + // detect durration : 50 us + + dif_sysrst_ctrl_key_combo_config_t sysrst_ctrl_key_combo_config = { + .keys = kDifSysrstCtrlKeyAll, + .detection_time_threshold = 10, + .actions = kDifSysrstCtrlKeyComboActionAll, + .embedded_controller_reset_duration = 10}; + + CHECK_DIF_OK(dif_sysrst_ctrl_key_combo_detect_configure( + sysrst_ctrl_aon, kDifSysrstCtrlKeyCombo0, sysrst_ctrl_key_combo_config)); + // Configure sysrst input change + // debounce durration : 100 us + dif_sysrst_ctrl_input_change_config_t sysrst_ctrl_input_change_config = { + .input_changes = kDifSysrstCtrlInputAll, .debounce_time_threshold = 20}; + + // Configure pinmux + dif_pinmux_t pinmux; + CHECK_DIF_OK(dif_pinmux_init( + mmio_region_from_addr(TOP_MATCHA_PINMUX_AON_BASE_ADDR), &pinmux)); + + CHECK_DIF_OK(dif_sysrst_ctrl_input_change_detect_configure( + sysrst_ctrl_aon, sysrst_ctrl_input_change_config)); + + CHECK_DIF_OK(dif_pinmux_input_select( + &pinmux, kTopMatchaPinmuxPeripheralInSysrstCtrlAonKey0In, + kTopMatchaPinmuxInselIor13)); +} + +static void low_power_sysrst(const dif_pwrmgr_t *pwrmgr) { + // Program the pwrmgr to go to deep sleep state (clocks off). + pwrmgr_testutils_enable_low_power(pwrmgr, kDifPwrmgrWakeupRequestSourceOne, + 0); + LOG_INFO("Low power set for sysrst"); + + // Enter in low power mode. + wait_for_interrupt(); +} + +static void timer_on(uint32_t usec) { + busy_spin_micros(usec); + // If we arrive here the test must fail. + CHECK(false, "Timeout waiting for reset!"); +} + +/** + * Configure the wdog. + */ +static void config_wdog(const dif_aon_timer_t *aon_timer, + const dif_pwrmgr_t *pwrmgr, uint64_t bark_time_us, + uint64_t bite_time_us) { + uint32_t bark_cycles = + aon_timer_testutils_get_aon_cycles_from_us(bark_time_us); + uint32_t bite_cycles = + aon_timer_testutils_get_aon_cycles_from_us(bite_time_us); + + LOG_INFO("Wdog will bark after %u us and bite after %u us", + (uint32_t)bark_time_us, (uint32_t)bite_time_us); + // Setup the wdog bark and bite timeouts. + aon_timer_testutils_watchdog_config(aon_timer, bark_cycles, bite_cycles, + false); + // Set wdog as a reset source. + CHECK_DIF_OK(dif_pwrmgr_set_request_sources(pwrmgr, kDifPwrmgrReqTypeReset, + kDifPwrmgrResetRequestSourceTwo, + kDifToggleEnabled)); +} + +/** + * Execute the aon timer wdog bite reset test. + */ +static void wdog_bite_test(const dif_aon_timer_t *aon_timer, + const dif_pwrmgr_t *pwrmgr, uint64_t bark_time_us) { + uint64_t bite_time_us = bark_time_us * 2; + config_wdog(aon_timer, pwrmgr, bark_time_us, bite_time_us); + + // The `intr_state` takes 3 aon clock cycles to rise plus 2 extra cycles as a + // precaution. + uint32_t wait_us = + bark_time_us + + udiv64_slow(5 * 1000000 + kClockFreqAonHz - 1, kClockFreqAonHz, NULL); + + // Wait bark time and check that the bark interrupt requested. + busy_spin_micros(wait_us); + bool is_pending = false; + CHECK_DIF_OK(dif_aon_timer_irq_is_pending( + aon_timer, kDifAonTimerIrqWdogTimerBark, &is_pending)); + CHECK(is_pending, "Wdog bark irq did not rise after %u microseconds", + wait_us); + + // Wait for the remaining time to the wdog bite. + busy_spin_micros(wait_us); + // If we arrive here the test must fail. + CHECK(false, "Timeout waiting for Wdog bite reset!"); +} + +/** + * Execute the aon timer wdog bite reset during sleep test. + */ +static void sleep_wdog_bite_test(const dif_aon_timer_t *aon_timer, + const dif_pwrmgr_t *pwrmgr, + uint64_t bark_time_us) { + uint64_t bite_time_us = bark_time_us * 2; + config_wdog(aon_timer, pwrmgr, bark_time_us, bite_time_us); +} + +static void low_power_wdog(const dif_pwrmgr_t *pwrmgr) { + // Program the pwrmgr to go to deep sleep state (clocks off). + pwrmgr_testutils_enable_low_power(pwrmgr, kDifPwrmgrWakeupRequestSourceTwo, + 0); + LOG_INFO("Low power set for watch dog"); + wait_for_interrupt(); + // If we arrive here the test must fail. + CHECK(false, "Fail to enter in low power mode!"); +} + +static void low_power_por(const dif_pwrmgr_t *pwrmgr) { + // Set por as a reset source. + CHECK_DIF_OK(dif_pwrmgr_set_request_sources(pwrmgr, kDifPwrmgrReqTypeReset, + kDifPwrmgrResetRequestSourceTwo, + kDifToggleEnabled)); + + // Program the pwrmgr to go to deep sleep state (clocks off). + pwrmgr_testutils_enable_low_power( + pwrmgr, + (kDifPwrmgrWakeupRequestSourceOne | kDifPwrmgrWakeupRequestSourceTwo | + kDifPwrmgrWakeupRequestSourceThree | kDifPwrmgrWakeupRequestSourceFour | + kDifPwrmgrWakeupRequestSourceFive | kDifPwrmgrWakeupRequestSourceSix), + 0); + LOG_INFO("ready for pad por"); + + // Enter in low power mode. + wait_for_interrupt(); + // If we arrive here the test must fail. + CHECK(false, "Fail to enter in low power mode!"); +} + +bool test_main(void) { + // Enable global and external IRQ at Ibex. + irq_global_ctrl(true); + irq_external_ctrl(true); + + init_peripherals(); + + // Enable all the AON interrupts used in this test. + rv_plic_testutils_irq_range_enable(&plic, kPlicTarget, + kTopMatchaPlicIrqIdAlertHandlerClassa, + kTopMatchaPlicIrqIdAlertHandlerClassd); + + alert_handler_config(); + + // First check the flash stored value. + uint32_t event_idx = flash_ctrl_testutils_counter_get(0); + + // Enable flash access + flash_ctrl_testutils_default_region_access(&flash_ctrl, + /*rd_en*/ true, + /*prog_en*/ true, + /*erase_en*/ true, + /*scramble_en*/ false, + /*ecc_en*/ false, + /*he_en*/ false); + + // Increment flash counter to know where we are. + flash_ctrl_testutils_counter_increment(&flash_ctrl, 0); + + LOG_INFO("Test round %d", event_idx); + LOG_INFO("RST_IDX[%d] = %d", event_idx, RST_IDX[event_idx]); + + // Check if there was a HW reset caused by expected cases. + dif_rstmgr_reset_info_bitfield_t rst_info; + rst_info = rstmgr_testutils_reason_get(); + rstmgr_testutils_reason_clear(); + + CHECK(rst_info == kDifRstmgrResetInfoPor || + rst_info == kDifRstmgrResetInfoSysRstCtrl || + rst_info == kDifRstmgrResetInfoWatchdog || + rst_info == kDifRstmgrResetInfoEscalation || + rst_info == kDifRstmgrResetInfoLowPowerExit || + rst_info == (kDifRstmgrResetInfoSysRstCtrl | + kDifRstmgrResetInfoLowPowerExit) || + rst_info == + (kDifRstmgrResetInfoPor | kDifRstmgrResetInfoLowPowerExit) || + rst_info == (kDifRstmgrResetInfoWatchdog | + kDifRstmgrResetInfoLowPowerExit) || + rst_info == (kDifRstmgrResetInfoEscalation | + kDifRstmgrResetInfoLowPowerExit) || + rst_info == kDifRstmgrResetInfoSw, + "Wrong reset reason %02X", rst_info); + + switch (RST_IDX[event_idx]) { + case 0: + LOG_INFO("Booting and setting deep sleep followed by sysrst"); + config_sysrst(&pwrmgr, &sysrst_ctrl_aon); + low_power_sysrst(&pwrmgr); + break; + case 1: + LOG_INFO("Booting and setting deep sleep followed by hw por"); + low_power_por(&pwrmgr); + break; + case 2: + LOG_INFO( + "Booting and setting deep sleep mode followed for low_power entry " + "reset"); + LOG_INFO("Let SV wait timer reset"); + // Executing the wdog bite reset during sleep test. + // actually the same test as deep sleep + watchdog + rstmgr_testutils_pre_reset(&rstmgr); + sleep_wdog_bite_test(&aon_timer, &pwrmgr, 200); + low_power_wdog(&pwrmgr); + break; + case 3: + LOG_INFO( + "Booting and setting deep sleep followed by watchdog reset " + "combined " + "with sw_req"); + LOG_INFO("Let SV wait timer reset"); + // Executing the wdog bite reset during sleep test. + rstmgr_testutils_pre_reset(&rstmgr); + + // Assert reeset req + CHECK_DIF_OK(dif_rstmgr_software_device_reset(&rstmgr)); + LOG_INFO("Device reset from sw"); + sleep_wdog_bite_test(&aon_timer, &pwrmgr, 200); + low_power_wdog(&pwrmgr); + break; + case 4: + LOG_INFO("Booting and setting deep sleep followed by watchdog reset"); + LOG_INFO("Let SV wait timer reset"); + // Executing the wdog bite reset during sleep test. + rstmgr_testutils_pre_reset(&rstmgr); + sleep_wdog_bite_test(&aon_timer, &pwrmgr, 200); + low_power_wdog(&pwrmgr); + break; + case 5: + LOG_INFO("Booting and running normal sleep followed by escalation reset"); + LOG_INFO("Let SV wait timer reset"); + trigger_escalate(&aon_timer, &pwrmgr); + timer_on(kEscalationPhase0MicrosCpu); + break; + case 6: + LOG_INFO("Last Booting"); + return true; + break; + default: + LOG_INFO("Booting for undefined case"); + } + + return false; +}
diff --git a/sw/device/tests/sim_dv/pwrmgr_normal_sleep_all_reset_reqs_test.c b/sw/device/tests/sim_dv/pwrmgr_normal_sleep_all_reset_reqs_test.c new file mode 100644 index 0000000..151b9c5 --- /dev/null +++ b/sw/device/tests/sim_dv/pwrmgr_normal_sleep_all_reset_reqs_test.c
@@ -0,0 +1,532 @@ +// Copyright 2023 Google LLC. +// 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 "hw/top_matcha/sw/autogen/top_matcha.h" +#include "sw/device/lib/base/abs_mmio.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_flash_ctrl.h" +#include "sw/device/lib/dif/dif_pinmux.h" +#include "sw/device/lib/dif/dif_pwrmgr.h" +#include "sw/device/lib/dif/dif_rstmgr.h" +#include "sw/device/lib/dif/dif_rv_plic.h" +#include "sw/device/lib/dif/dif_rv_timer.h" +#include "sw/device/lib/dif/dif_sysrst_ctrl.h" +#include "sw/device/lib/runtime/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/flash_ctrl_testutils.h" +#include "sw/device/lib/testing/pwrmgr_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" + +OTTF_DEFINE_TEST_CONFIG(); +static volatile const uint8_t RST_IDX[7] = {0, 1, 2, 3, 4, 5, 6}; +static const uint32_t kPlicTarget = kTopMatchaPlicTargetIbex0; + +/** + * Objects to access the peripherals used in this test via dif API. + */ +static dif_flash_ctrl_state_t flash_ctrl; +static dif_rv_plic_t plic; +static dif_alert_handler_t alert_handler; +static dif_aon_timer_t aon_timer; +static dif_pwrmgr_t pwrmgr; +static dif_sysrst_ctrl_t sysrst_ctrl_aon; +static dif_rstmgr_t rstmgr; + +/** + * 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. + * + * Notice these settings are suitable for sim_dv. For other platforms we scale + * the resulting cycles by a factor of 10. + */ +enum { + kWdogBarkMicros = 3 * 100, // 300 us + kWdogBiteMicros = 4 * 100, // 400 us + kEscalationPhase0Micros = 1 * 100, // 100 us + // The cpu value is slightly larger to avoid flakey results. + kEscalationPhase0MicrosCpu = kEscalationPhase0Micros + 20, // 120 us + kEscalationPhase1Micros = 5 * 100, // 500 us + kEscalationPhase2Micros = 50, // 50 us +}; + +static_assert( + kWdogBarkMicros < kWdogBiteMicros && + kWdogBarkMicros > kEscalationPhase0Micros && + kWdogBarkMicros < (kEscalationPhase0Micros + kEscalationPhase1Micros) && + kWdogBiteMicros < (kEscalationPhase0Micros + kEscalationPhase1Micros), + "The wdog bark and bite shall happens during the escalation phase 1"); + +/** + * 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) { + top_matcha_plic_peripheral_t peripheral; + dif_rv_plic_irq_id_t irq_id; + uint32_t irq = 0; + uint32_t alert = 0; + + CHECK_DIF_OK(dif_rv_plic_irq_claim(&plic, kPlicTarget, &irq_id)); + + peripheral = (top_matcha_plic_peripheral_t) + top_matcha_plic_interrupt_for_peripheral[irq_id]; + + if (peripheral == kTopMatchaPlicPeripheralAonTimerAon) { + irq = + (dif_aon_timer_irq_t)(irq_id - + (dif_rv_plic_irq_id_t) + kTopMatchaPlicIrqIdAonTimerAonWkupTimerExpired); + + // Stops escalation process. + CHECK_DIF_OK(dif_alert_handler_escalation_clear(&alert_handler, + kDifAlertHandlerClassA)); + CHECK_DIF_OK(dif_aon_timer_irq_acknowledge(&aon_timer, irq)); + + CHECK(irq != kTopMatchaPlicIrqIdAonTimerAonWdogTimerBark, + "AON Timer Wdog should not bark"); + + } else if (peripheral == kTopMatchaPlicPeripheralAlertHandler) { + irq = (dif_aon_timer_irq_t)(irq_id - + (dif_rv_plic_irq_id_t) + kTopMatchaPlicIrqIdAlertHandlerClassa); + + CHECK_DIF_OK(dif_alert_handler_alert_acknowledge(&alert_handler, alert)); + + 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); + + 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) { + // Initialize pwrmgr. + CHECK_DIF_OK(dif_pwrmgr_init( + mmio_region_from_addr(TOP_MATCHA_PWRMGR_AON_BASE_ADDR), &pwrmgr)); + + // Initialize sysrst_ctrl. + CHECK_DIF_OK(dif_sysrst_ctrl_init( + mmio_region_from_addr(TOP_MATCHA_SYSRST_CTRL_AON_BASE_ADDR), + &sysrst_ctrl_aon)); + + // Initialize rstmgr to check the reset reason. + CHECK_DIF_OK(dif_rstmgr_init( + mmio_region_from_addr(TOP_MATCHA_RSTMGR_AON_BASE_ADDR), &rstmgr)); + + // Initialize aon timer to use the wdog. + CHECK_DIF_OK(dif_aon_timer_init( + mmio_region_from_addr(TOP_MATCHA_AON_TIMER_AON_BASE_ADDR), &aon_timer)); + + // Initialize flash_ctrl + CHECK_DIF_OK(dif_flash_ctrl_init_state( + &flash_ctrl, + mmio_region_from_addr(TOP_MATCHA_FLASH_CTRL_CORE_BASE_ADDR))); + + // Initialize plic. + CHECK_DIF_OK(dif_rv_plic_init( + mmio_region_from_addr(TOP_MATCHA_RV_PLIC_BASE_ADDR), &plic)); + + rv_plic_testutils_irq_range_enable( + &plic, kPlicTarget, kTopMatchaPlicIrqIdAonTimerAonWkupTimerExpired, + kTopMatchaPlicIrqIdAonTimerAonWdogTimerBark); + + // Initialize alert handler. + CHECK_DIF_OK(dif_alert_handler_init( + mmio_region_from_addr(TOP_MATCHA_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[] = {kTopMatchaAlertIdPwrmgrAonFatalFault}; + dif_alert_handler_class_t alert_classes[] = {kDifAlertHandlerClassA}; + + dif_alert_handler_escalation_phase_t esc_phases[] = { + {.phase = kDifAlertHandlerClassStatePhase0, + .signal = 0, + .duration_cycles = + alert_handler_testutils_get_cycles_from_us(kEscalationPhase0Micros) * + alert_handler_testutils_cycle_rescaling_factor()}, + {.phase = kDifAlertHandlerClassStatePhase1, + .signal = 1, + .duration_cycles = + alert_handler_testutils_get_cycles_from_us(kEscalationPhase1Micros) * + alert_handler_testutils_cycle_rescaling_factor()}, + {.phase = kDifAlertHandlerClassStatePhase2, + .signal = 3, + .duration_cycles = + alert_handler_testutils_get_cycles_from_us(kEscalationPhase2Micros) * + alert_handler_testutils_cycle_rescaling_factor()}}; + + dif_alert_handler_class_config_t class_config[] = {{ + .auto_lock_accumulation_counter = kDifToggleDisabled, + .accumulator_threshold = 0, + .irq_deadline_cycles = alert_handler_testutils_get_cycles_from_us(10) * + alert_handler_testutils_cycle_rescaling_factor(), + .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 config_escalate(dif_aon_timer_t *aon_timer, + const dif_pwrmgr_t *pwrmgr) { + uint64_t bark_cycles = + udiv64_slow(kWdogBarkMicros * kClockFreqAonHz, 1000000, NULL) * + alert_handler_testutils_cycle_rescaling_factor(); + uint64_t bite_cycles = + udiv64_slow(kWdogBiteMicros * kClockFreqAonHz, 1000000, NULL) * + alert_handler_testutils_cycle_rescaling_factor(); + + CHECK(bite_cycles < UINT32_MAX, + "The value %u can't fit into the 32 bits timer counter.", bite_cycles); + + LOG_INFO( + "Wdog will bark after %u/%u us/cycles and bite after %u/%u us/cycles", + (uint32_t)kWdogBarkMicros, (uint32_t)bark_cycles, + (uint32_t)kWdogBiteMicros, (uint32_t)bite_cycles); + + // Setup the wdog bark and bite timeouts. + aon_timer_testutils_watchdog_config(aon_timer, bark_cycles, bite_cycles, + false); + + // Trigger the alert handler to escalate. + dif_pwrmgr_alert_t alert = kDifPwrmgrAlertFatalFault; + CHECK_DIF_OK(dif_pwrmgr_alert_force(pwrmgr, alert)); +} + +/** + * Configure the sysrst. + */ +static void config_sysrst(const dif_pwrmgr_t *pwrmgr, + const dif_sysrst_ctrl_t *sysrst_ctrl_aon) { + LOG_INFO("sysrst enabled"); + + // Set sysrst as a reset source. + CHECK_DIF_OK(dif_pwrmgr_set_request_sources(pwrmgr, kDifPwrmgrReqTypeReset, + kDifPwrmgrResetRequestSourceOne, + kDifToggleEnabled)); + LOG_INFO("Reset Request SourceOne is set"); + + // Configure sysrst key combo + // reset pulse : 50 us + // detect durration : 50 us + + dif_sysrst_ctrl_key_combo_config_t sysrst_ctrl_key_combo_config = { + .keys = kDifSysrstCtrlKeyAll, + .detection_time_threshold = 10, + .actions = kDifSysrstCtrlKeyComboActionAll, + .embedded_controller_reset_duration = 10}; + + CHECK_DIF_OK(dif_sysrst_ctrl_key_combo_detect_configure( + sysrst_ctrl_aon, kDifSysrstCtrlKeyCombo0, sysrst_ctrl_key_combo_config)); + // Configure sysrst input change + // debounce durration : 100 us + dif_sysrst_ctrl_input_change_config_t sysrst_ctrl_input_change_config = { + .input_changes = kDifSysrstCtrlInputAll, .debounce_time_threshold = 20}; + + // Configure pinmux + dif_pinmux_t pinmux; + CHECK_DIF_OK(dif_pinmux_init( + mmio_region_from_addr(TOP_MATCHA_PINMUX_AON_BASE_ADDR), &pinmux)); + + CHECK_DIF_OK(dif_sysrst_ctrl_input_change_detect_configure( + sysrst_ctrl_aon, sysrst_ctrl_input_change_config)); + + CHECK_DIF_OK(dif_pinmux_input_select( + &pinmux, kTopMatchaPinmuxPeripheralInSysrstCtrlAonKey0In, + kTopMatchaPinmuxInselIor13)); +} + +static void normal_sleep_sysrst(const dif_pwrmgr_t *pwrmgr) { + // Place device into low power and immediately wake. + dif_pwrmgr_domain_config_t config; + config = kDifPwrmgrDomainOptionUsbClockInLowPower | + kDifPwrmgrDomainOptionCoreClockInLowPower | + kDifPwrmgrDomainOptionIoClockInLowPower | + kDifPwrmgrDomainOptionMainPowerInLowPower; + pwrmgr_testutils_enable_low_power(pwrmgr, kDifPwrmgrWakeupRequestSourceOne, + config); + LOG_INFO("Normal sleep set for sysrst"); + + // Enter in low power mode. + wait_for_interrupt(); +} + +static void timer_on(uint32_t usec) { + busy_spin_micros(usec); + // If we arrive here the test must fail. + CHECK(false, "Timeout waiting for reset!"); +} + +/** + * Configure the wdog. + */ +static void config_wdog(const dif_aon_timer_t *aon_timer, + const dif_pwrmgr_t *pwrmgr, uint64_t bark_time_us, + uint64_t bite_time_us) { + uint32_t bark_cycles = + aon_timer_testutils_get_aon_cycles_from_us(bark_time_us); + uint32_t bite_cycles = + aon_timer_testutils_get_aon_cycles_from_us(bite_time_us); + + LOG_INFO("Wdog will bark after %u us and bite after %u us", + (uint32_t)bark_time_us, (uint32_t)bite_time_us); + // Setup the wdog bark and bite timeouts. + aon_timer_testutils_watchdog_config(aon_timer, bark_cycles, bite_cycles, + false); + // Set wdog as a reset source. + CHECK_DIF_OK(dif_pwrmgr_set_request_sources(pwrmgr, kDifPwrmgrReqTypeReset, + kDifPwrmgrResetRequestSourceTwo, + kDifToggleEnabled)); +} + +/** + * Execute the aon timer wdog bite reset test. + */ +static void wdog_bite_test(const dif_aon_timer_t *aon_timer, + const dif_pwrmgr_t *pwrmgr, uint64_t bark_time_us) { + uint64_t bite_time_us = bark_time_us * 2; + config_wdog(aon_timer, pwrmgr, bark_time_us, bite_time_us); + + // The `intr_state` takes 3 aon clock cycles to rise plus 2 extra cycles as a + // precaution. + uint32_t wait_us = + bark_time_us + + udiv64_slow(5 * 1000000 + kClockFreqAonHz - 1, kClockFreqAonHz, NULL); + + // Wait bark time and check that the bark interrupt requested. + busy_spin_micros(wait_us); + bool is_pending = false; + CHECK_DIF_OK(dif_aon_timer_irq_is_pending( + aon_timer, kDifAonTimerIrqWdogTimerBark, &is_pending)); + CHECK(is_pending, "Wdog bark irq did not rise after %u microseconds", + wait_us); + + // Wait for the remaining time to the wdog bite. + busy_spin_micros(wait_us); + // If we arrive here the test must fail. + CHECK(false, "Timeout waiting for Wdog bite reset!"); +} + +/** + * Execute the aon timer wdog bite reset during sleep test. + */ +static void sleep_wdog_bite_test(const dif_aon_timer_t *aon_timer, + const dif_pwrmgr_t *pwrmgr, + uint64_t bark_time_us) { + uint64_t bite_time_us = bark_time_us * 2; + config_wdog(aon_timer, pwrmgr, bark_time_us, bite_time_us); +} + +static void normal_sleep_wdog(const dif_pwrmgr_t *pwrmgr) { + // Place device into low power and immediately wake. + dif_pwrmgr_domain_config_t config; + config = kDifPwrmgrDomainOptionUsbClockInLowPower | + kDifPwrmgrDomainOptionCoreClockInLowPower | + kDifPwrmgrDomainOptionIoClockInLowPower | + kDifPwrmgrDomainOptionMainPowerInLowPower; + + // Enter in low power mode. + pwrmgr_testutils_enable_low_power(pwrmgr, kDifPwrmgrWakeupRequestSourceTwo, + config); + LOG_INFO("Normal sleep set for watchdog"); + wait_for_interrupt(); +} + +static void normal_sleep_por(const dif_pwrmgr_t *pwrmgr) { + // Set por as a reset source. + CHECK_DIF_OK(dif_pwrmgr_set_request_sources(pwrmgr, kDifPwrmgrReqTypeReset, + kDifPwrmgrResetRequestSourceTwo, + kDifToggleEnabled)); + + // Place device into low power and immediately wake. + dif_pwrmgr_domain_config_t config; + config = kDifPwrmgrDomainOptionUsbClockInLowPower | + kDifPwrmgrDomainOptionCoreClockInLowPower | + kDifPwrmgrDomainOptionIoClockInLowPower | + kDifPwrmgrDomainOptionMainPowerInLowPower; + + // Program the pwrmgr to go to deep sleep state (clocks off). + pwrmgr_testutils_enable_low_power( + pwrmgr, + (kDifPwrmgrWakeupRequestSourceOne | kDifPwrmgrWakeupRequestSourceTwo | + kDifPwrmgrWakeupRequestSourceThree | kDifPwrmgrWakeupRequestSourceFour | + kDifPwrmgrWakeupRequestSourceFive | kDifPwrmgrWakeupRequestSourceSix), + config); + LOG_INFO("ready for pad por"); + // Enter in low power mode. + wait_for_interrupt(); +} + +bool test_main(void) { + // Enable global and external IRQ at Ibex. + irq_global_ctrl(true); + irq_external_ctrl(true); + + init_peripherals(); + + // Enable all the AON interrupts used in this test. + rv_plic_testutils_irq_range_enable(&plic, kPlicTarget, + kTopMatchaPlicIrqIdAlertHandlerClassa, + kTopMatchaPlicIrqIdAlertHandlerClassd); + + alert_handler_config(); + + // First check the flash stored value + uint32_t event_idx = flash_ctrl_testutils_counter_get(0); + + // Enable flash access + flash_ctrl_testutils_default_region_access(&flash_ctrl, + /*rd_en*/ true, + /*prog_en*/ true, + /*erase_en*/ true, + /*scramble_en*/ false, + /*ecc_en*/ false, + /*he_en*/ false); + + // Increment flash counter to know where we are + flash_ctrl_testutils_counter_increment(&flash_ctrl, 0); + + LOG_INFO("Test round %d", event_idx); + LOG_INFO("RST_IDX[%d] = %d", event_idx, RST_IDX[event_idx]); + + // Check if there was a HW reset caused by expected cases + dif_rstmgr_reset_info_bitfield_t rst_info; + rst_info = rstmgr_testutils_reason_get(); + rstmgr_testutils_reason_clear(); + + CHECK(rst_info == kDifRstmgrResetInfoPor || + rst_info == kDifRstmgrResetInfoSysRstCtrl || + rst_info == kDifRstmgrResetInfoWatchdog || + rst_info == kDifRstmgrResetInfoEscalation || + rst_info == kDifRstmgrResetInfoLowPowerExit || + rst_info == (kDifRstmgrResetInfoSysRstCtrl | + kDifRstmgrResetInfoLowPowerExit) || + rst_info == + (kDifRstmgrResetInfoPor | kDifRstmgrResetInfoLowPowerExit) || + rst_info == (kDifRstmgrResetInfoWatchdog | + kDifRstmgrResetInfoLowPowerExit) || + rst_info == (kDifRstmgrResetInfoEscalation | + kDifRstmgrResetInfoLowPowerExit) || + rst_info == kDifRstmgrResetInfoSw, + "Wrong reset reason %02X", rst_info); + + switch (RST_IDX[event_idx]) { + case 0: + LOG_INFO("Booting and setting normal sleep followed by sysrst"); + config_sysrst(&pwrmgr, &sysrst_ctrl_aon); + normal_sleep_sysrst(&pwrmgr); + timer_on(kWdogBiteMicros); + break; + case 1: + LOG_INFO("Booting and setting normal sleep followed by hw por"); + normal_sleep_por(&pwrmgr); + timer_on(kWdogBiteMicros); + break; + case 2: + LOG_INFO( + "Booting and setting normal sleep mode followed for low_power " + "entry reset"); + LOG_INFO("Let SV wait timer reset"); + // actually the same test as normal sleep + watchdog + rstmgr_testutils_pre_reset(&rstmgr); + sleep_wdog_bite_test(&aon_timer, &pwrmgr, 200); + normal_sleep_wdog(&pwrmgr); + timer_on(kEscalationPhase0MicrosCpu); + break; + case 3: + LOG_INFO( + "Booting and setting normal sleep followed by watchdog reset " + "combined " + "with sw_req"); + LOG_INFO("Let SV wait timer reset"); + // Executing the wdog bite reset during sleep test. + rstmgr_testutils_pre_reset(&rstmgr); + + // Assert reeset req + CHECK_DIF_OK(dif_rstmgr_software_device_reset(&rstmgr)); + LOG_INFO("Device reset from sw"); + sleep_wdog_bite_test(&aon_timer, &pwrmgr, 200); + normal_sleep_wdog(&pwrmgr); + timer_on(kEscalationPhase0MicrosCpu); + break; + case 4: + LOG_INFO("Booting and setting normal sleep followed by watchdog reset"); + LOG_INFO("Let SV wait timer reset"); + // Executing the wdog bite reset during sleep test. + rstmgr_testutils_pre_reset(&rstmgr); + sleep_wdog_bite_test(&aon_timer, &pwrmgr, 200); + normal_sleep_wdog(&pwrmgr); + timer_on(kEscalationPhase0MicrosCpu); + break; + case 5: + LOG_INFO("Booting and running normal sleep followed by escalation reset"); + LOG_INFO("Let SV wait timer reset"); + config_escalate(&aon_timer, &pwrmgr); + timer_on(kEscalationPhase0MicrosCpu); + break; + case 6: + LOG_INFO("Last Booting"); + return true; + break; + default: + LOG_INFO("Booting for undefined case"); + } + + return false; +}
diff --git a/sw/device/tests/sim_dv/pwrmgr_random_sleep_all_reset_reqs_test.c b/sw/device/tests/sim_dv/pwrmgr_random_sleep_all_reset_reqs_test.c new file mode 100644 index 0000000..440a84b --- /dev/null +++ b/sw/device/tests/sim_dv/pwrmgr_random_sleep_all_reset_reqs_test.c
@@ -0,0 +1,617 @@ +// Copyright 2023 Google LLC. +// 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 "hw/top_matcha/sw/autogen/top_matcha.h" +#include "sw/device/lib/base/abs_mmio.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_flash_ctrl.h" +#include "sw/device/lib/dif/dif_pinmux.h" +#include "sw/device/lib/dif/dif_pwrmgr.h" +#include "sw/device/lib/dif/dif_rstmgr.h" +#include "sw/device/lib/dif/dif_rv_plic.h" +#include "sw/device/lib/dif/dif_rv_timer.h" +#include "sw/device/lib/dif/dif_sysrst_ctrl.h" +#include "sw/device/lib/runtime/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/flash_ctrl_testutils.h" +#include "sw/device/lib/testing/pwrmgr_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" + +OTTF_DEFINE_TEST_CONFIG(); +static volatile const uint8_t RST_IDX[12] = {0, 1, 2, 3, 4, 5, + 6, 7, 8, 9, 10, 11}; +static const uint32_t kPlicTarget = kTopMatchaPlicTargetIbex0; + +/** + * Objects to access the peripherals used in this test via dif API. + */ +static dif_flash_ctrl_state_t flash_ctrl; +static dif_rv_plic_t plic; +static dif_alert_handler_t alert_handler; +static dif_aon_timer_t aon_timer; +static dif_pwrmgr_t pwrmgr; +static dif_sysrst_ctrl_t sysrst_ctrl_aon; +static dif_rstmgr_t rstmgr; + +/** + * 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. + */ +enum { + kWdogBarkMicros = 3 * 100, // 300 us + kWdogBiteMicros = 4 * 100, // 400 us + kEscalationPhase0Micros = 1 * 100, // 100 us + // The cpu value is slightly larger as the busy_spin_micros + // routine cycle count comes out slightly smaller due to the + // fact that it does not divide by exactly 1M + // see sw/device/lib/runtime/hart.c + kEscalationPhase0MicrosCpu = kEscalationPhase0Micros + 20, // 120 us + kEscalationPhase1Micros = 5 * 100, // 500 us + kEscalationPhase2Micros = 50, // 50 us +}; + +static_assert( + kWdogBarkMicros < kWdogBiteMicros && + kWdogBarkMicros > kEscalationPhase0Micros && + kWdogBarkMicros < (kEscalationPhase0Micros + kEscalationPhase1Micros) && + kWdogBiteMicros < (kEscalationPhase0Micros + kEscalationPhase1Micros), + "The wdog bark and bite shall happens during the escalation phase 1"); + +/** + * 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) { + top_matcha_plic_peripheral_t peripheral; + dif_rv_plic_irq_id_t irq_id; + uint32_t irq = 0; + uint32_t alert = 0; + + CHECK_DIF_OK(dif_rv_plic_irq_claim(&plic, kPlicTarget, &irq_id)); + + peripheral = (top_matcha_plic_peripheral_t) + top_matcha_plic_interrupt_for_peripheral[irq_id]; + + if (peripheral == kTopMatchaPlicPeripheralAonTimerAon) { + irq = + (dif_aon_timer_irq_t)(irq_id - + (dif_rv_plic_irq_id_t) + kTopMatchaPlicIrqIdAonTimerAonWkupTimerExpired); + + // Stops escalation process. + CHECK_DIF_OK(dif_alert_handler_escalation_clear(&alert_handler, + kDifAlertHandlerClassA)); + CHECK_DIF_OK(dif_aon_timer_irq_acknowledge(&aon_timer, irq)); + + CHECK(irq != kTopMatchaPlicIrqIdAonTimerAonWdogTimerBark, + "AON Timer Wdog should not bark"); + + } else if (peripheral == kTopMatchaPlicPeripheralAlertHandler) { + irq = (dif_rv_plic_irq_id_t)(irq_id - + (dif_rv_plic_irq_id_t) + kTopMatchaPlicIrqIdAlertHandlerClassa); + + CHECK_DIF_OK(dif_alert_handler_alert_acknowledge(&alert_handler, alert)); + + 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); + + 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) { + // Initialize pwrmgr. + CHECK_DIF_OK(dif_pwrmgr_init( + mmio_region_from_addr(TOP_MATCHA_PWRMGR_AON_BASE_ADDR), &pwrmgr)); + + // Initialize sysrst_ctrl. + CHECK_DIF_OK(dif_sysrst_ctrl_init( + mmio_region_from_addr(TOP_MATCHA_SYSRST_CTRL_AON_BASE_ADDR), + &sysrst_ctrl_aon)); + + // Initialize rstmgr to check the reset reason. + CHECK_DIF_OK(dif_rstmgr_init( + mmio_region_from_addr(TOP_MATCHA_RSTMGR_AON_BASE_ADDR), &rstmgr)); + + // Initialize aon timer to use the wdog. + CHECK_DIF_OK(dif_aon_timer_init( + mmio_region_from_addr(TOP_MATCHA_AON_TIMER_AON_BASE_ADDR), &aon_timer)); + + // Initialize flash_ctrl + CHECK_DIF_OK(dif_flash_ctrl_init_state( + &flash_ctrl, + mmio_region_from_addr(TOP_MATCHA_FLASH_CTRL_CORE_BASE_ADDR))); + + // Initialize plic. + CHECK_DIF_OK(dif_rv_plic_init( + mmio_region_from_addr(TOP_MATCHA_RV_PLIC_BASE_ADDR), &plic)); + + rv_plic_testutils_irq_range_enable( + &plic, kPlicTarget, kTopMatchaPlicIrqIdAonTimerAonWkupTimerExpired, + kTopMatchaPlicIrqIdAonTimerAonWdogTimerBark); + + // Initialize alert handler. + CHECK_DIF_OK(dif_alert_handler_init( + mmio_region_from_addr(TOP_MATCHA_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[] = {kTopMatchaAlertIdPwrmgrAonFatalFault}; + dif_alert_handler_class_t alert_classes[] = {kDifAlertHandlerClassA}; + + dif_alert_handler_escalation_phase_t esc_phases[] = { + {.phase = kDifAlertHandlerClassStatePhase0, + .signal = 0, + .duration_cycles = + alert_handler_testutils_get_cycles_from_us(kEscalationPhase0Micros) * + alert_handler_testutils_cycle_rescaling_factor()}, + {.phase = kDifAlertHandlerClassStatePhase1, + .signal = 1, + .duration_cycles = + alert_handler_testutils_get_cycles_from_us(kEscalationPhase1Micros) * + alert_handler_testutils_cycle_rescaling_factor()}, + {.phase = kDifAlertHandlerClassStatePhase2, + .signal = 3, + .duration_cycles = + alert_handler_testutils_get_cycles_from_us(kEscalationPhase2Micros) * + alert_handler_testutils_cycle_rescaling_factor()}}; + + dif_alert_handler_class_config_t class_config[] = {{ + .auto_lock_accumulation_counter = kDifToggleDisabled, + .accumulator_threshold = 0, + .irq_deadline_cycles = alert_handler_testutils_get_cycles_from_us(10) * + alert_handler_testutils_cycle_rescaling_factor(), + .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 config_escalate(dif_aon_timer_t *aon_timer, + const dif_pwrmgr_t *pwrmgr) { + uint64_t bark_cycles = + udiv64_slow(kWdogBarkMicros * kClockFreqAonHz, 1000000, NULL) * + alert_handler_testutils_cycle_rescaling_factor(); + uint64_t bite_cycles = + udiv64_slow(kWdogBiteMicros * kClockFreqAonHz, 1000000, NULL) * + alert_handler_testutils_cycle_rescaling_factor(); + + CHECK(bite_cycles < UINT32_MAX, + "The value %u can't fit into the 32 bits timer counter.", bite_cycles); + + LOG_INFO( + "Wdog will bark after %u/%u us/cycles and bite after %u/%u us/cycles", + (uint32_t)kWdogBarkMicros, (uint32_t)bark_cycles, + (uint32_t)kWdogBiteMicros, (uint32_t)bite_cycles); + + // Setup the wdog bark and bite timeouts. + aon_timer_testutils_watchdog_config(aon_timer, bark_cycles, bite_cycles, + false); + + // Trigger the alert handler to escalate. + dif_pwrmgr_alert_t alert = kDifPwrmgrAlertFatalFault; + CHECK_DIF_OK(dif_pwrmgr_alert_force(pwrmgr, alert)); +} + +/** + * Configure the sysrst. + */ +static void config_sysrst(const dif_pwrmgr_t *pwrmgr, + const dif_sysrst_ctrl_t *sysrst_ctrl_aon) { + LOG_INFO("sysrst enabled"); + + // Set sysrst as a reset source. + CHECK_DIF_OK(dif_pwrmgr_set_request_sources(pwrmgr, kDifPwrmgrReqTypeReset, + kDifPwrmgrResetRequestSourceOne, + kDifToggleEnabled)); + LOG_INFO("Reset Request SourceOne is set"); + + // Configure sysrst key combo + // reset pulse : 50 us + // detect durration : 50 us + + dif_sysrst_ctrl_key_combo_config_t sysrst_ctrl_key_combo_config = { + .keys = kDifSysrstCtrlKeyAll, + .detection_time_threshold = 10, + .actions = kDifSysrstCtrlKeyComboActionAll, + .embedded_controller_reset_duration = 10}; + + CHECK_DIF_OK(dif_sysrst_ctrl_key_combo_detect_configure( + sysrst_ctrl_aon, kDifSysrstCtrlKeyCombo0, sysrst_ctrl_key_combo_config)); + // Configure sysrst input change + // debounce durration : 100 us + dif_sysrst_ctrl_input_change_config_t sysrst_ctrl_input_change_config = { + .input_changes = kDifSysrstCtrlInputAll, .debounce_time_threshold = 20}; + + // Configure pinmux + dif_pinmux_t pinmux; + CHECK_DIF_OK(dif_pinmux_init( + mmio_region_from_addr(TOP_MATCHA_PINMUX_AON_BASE_ADDR), &pinmux)); + + CHECK_DIF_OK(dif_sysrst_ctrl_input_change_detect_configure( + sysrst_ctrl_aon, sysrst_ctrl_input_change_config)); + + CHECK_DIF_OK(dif_pinmux_input_select( + &pinmux, kTopMatchaPinmuxPeripheralInSysrstCtrlAonKey0In, + kTopMatchaPinmuxInselIor13)); +} + +static void low_power_sysrst(const dif_pwrmgr_t *pwrmgr) { + // Program the pwrmgr to go to deep sleep state (clocks off). + pwrmgr_testutils_enable_low_power(pwrmgr, kDifPwrmgrWakeupRequestSourceOne, + 0); + // Enter in low power mode. + wait_for_interrupt(); +} + +static void normal_sleep_sysrst(const dif_pwrmgr_t *pwrmgr) { + // Place device into low power and immediately wake. + dif_pwrmgr_domain_config_t config; + config = kDifPwrmgrDomainOptionUsbClockInLowPower | + kDifPwrmgrDomainOptionCoreClockInLowPower | + kDifPwrmgrDomainOptionIoClockInLowPower | + kDifPwrmgrDomainOptionMainPowerInLowPower; + pwrmgr_testutils_enable_low_power(pwrmgr, kDifPwrmgrWakeupRequestSourceOne, + config); + // Enter in low power mode. + wait_for_interrupt(); +} + +static void timer_on(uint32_t usec) { + busy_spin_micros(usec); + // If we arrive here the test must fail. + CHECK(false, "Timeout waiting for reset!"); +} + +/** + * Configure the wdog. + */ +static void config_wdog(const dif_aon_timer_t *aon_timer, + const dif_pwrmgr_t *pwrmgr, uint64_t bark_time_us, + uint64_t bite_time_us) { + uint32_t bark_cycles = + aon_timer_testutils_get_aon_cycles_from_us(bark_time_us); + uint32_t bite_cycles = + aon_timer_testutils_get_aon_cycles_from_us(bite_time_us); + + LOG_INFO("Wdog will bark after %u us and bite after %u us", + (uint32_t)bark_time_us, (uint32_t)bite_time_us); + // Setup the wdog bark and bite timeouts. + + aon_timer_testutils_watchdog_config(aon_timer, bark_cycles, bite_cycles, + false); + // Set wdog as a reset source. + CHECK_DIF_OK(dif_pwrmgr_set_request_sources(pwrmgr, kDifPwrmgrReqTypeReset, + kDifPwrmgrResetRequestSourceTwo, + kDifToggleEnabled)); +} + +/** + * Execute the aon timer wdog bite reset test. + */ +static void wdog_bite_test(const dif_aon_timer_t *aon_timer, + const dif_pwrmgr_t *pwrmgr, uint64_t bark_time_us) { + uint64_t bite_time_us = bark_time_us * 2; + config_wdog(aon_timer, pwrmgr, bark_time_us, bite_time_us); + + // The `intr_state` takes 3 aon clock cycles to rise plus 2 extra cycles as a + // precaution. + uint32_t wait_us = + bark_time_us + + udiv64_slow(5 * 1000000 + kClockFreqAonHz - 1, kClockFreqAonHz, NULL); + + // Wait bark time and check that the bark interrupt requested. + busy_spin_micros(wait_us); + bool is_pending = false; + CHECK_DIF_OK(dif_aon_timer_irq_is_pending( + aon_timer, kDifAonTimerIrqWdogTimerBark, &is_pending)); + CHECK(is_pending, "Wdog bark irq did not rise after %u microseconds", + wait_us); + + // Wait for the remaining time to the wdog bite. + busy_spin_micros(wait_us); + // If we arrive here the test must fail. + CHECK(false, "Timeout waiting for Wdog bite reset!"); +} + +/** + * Execute the aon timer wdog bite reset during sleep test. + */ +static void sleep_wdog_bite_test(const dif_aon_timer_t *aon_timer, + const dif_pwrmgr_t *pwrmgr, + uint64_t bark_time_us) { + uint64_t bite_time_us = bark_time_us * 2; + config_wdog(aon_timer, pwrmgr, bark_time_us, bite_time_us); +} + +static void low_power_wdog(const dif_pwrmgr_t *pwrmgr) { + // Program the pwrmgr to go to deep sleep state (clocks off). + // Enter in low power mode. + pwrmgr_testutils_enable_low_power(pwrmgr, kDifPwrmgrWakeupRequestSourceTwo, + 0); + LOG_INFO("Low power set for watch dog"); + wait_for_interrupt(); + // If we arrive here the test must fail. + CHECK(false, "Fail to enter in low power mode!"); +} + +static void normal_sleep_wdog(const dif_pwrmgr_t *pwrmgr) { + // Place device into low power and immediately wake. + dif_pwrmgr_domain_config_t config; + config = kDifPwrmgrDomainOptionUsbClockInLowPower | + kDifPwrmgrDomainOptionCoreClockInLowPower | + kDifPwrmgrDomainOptionIoClockInLowPower | + kDifPwrmgrDomainOptionMainPowerInLowPower; + + // Enter in low power mode. + pwrmgr_testutils_enable_low_power(pwrmgr, kDifPwrmgrWakeupRequestSourceTwo, + config); + LOG_INFO("Normal sleep set for watchdog"); + wait_for_interrupt(); +} + +static void low_power_por(const dif_pwrmgr_t *pwrmgr) { + // Set por as a reset source. + CHECK_DIF_OK(dif_pwrmgr_set_request_sources(pwrmgr, kDifPwrmgrReqTypeReset, + kDifPwrmgrResetRequestSourceTwo, + kDifToggleEnabled)); + + // Program the pwrmgr to go to deep sleep state (clocks off). + pwrmgr_testutils_enable_low_power( + pwrmgr, + (kDifPwrmgrWakeupRequestSourceOne | kDifPwrmgrWakeupRequestSourceTwo | + kDifPwrmgrWakeupRequestSourceThree | kDifPwrmgrWakeupRequestSourceFour | + kDifPwrmgrWakeupRequestSourceFive | kDifPwrmgrWakeupRequestSourceSix), + 0); + // Enter in low power mode. + wait_for_interrupt(); + // If we arrive here the test must fail. + CHECK(false, "Fail to enter in low power mode!"); +} + +static void normal_sleep_por(const dif_pwrmgr_t *pwrmgr) { + // Set por as a reset source. + CHECK_DIF_OK(dif_pwrmgr_set_request_sources(pwrmgr, kDifPwrmgrReqTypeReset, + kDifPwrmgrResetRequestSourceTwo, + kDifToggleEnabled)); + + // Place device into low power and immediately wake. + dif_pwrmgr_domain_config_t config; + config = kDifPwrmgrDomainOptionUsbClockInLowPower | + kDifPwrmgrDomainOptionCoreClockInLowPower | + kDifPwrmgrDomainOptionIoClockInLowPower | + kDifPwrmgrDomainOptionMainPowerInLowPower; + + // Program the pwrmgr to go to deep sleep state (clocks off). + pwrmgr_testutils_enable_low_power( + pwrmgr, + (kDifPwrmgrWakeupRequestSourceOne | kDifPwrmgrWakeupRequestSourceTwo | + kDifPwrmgrWakeupRequestSourceThree | kDifPwrmgrWakeupRequestSourceFour | + kDifPwrmgrWakeupRequestSourceFive | kDifPwrmgrWakeupRequestSourceSix), + config); + // Enter in low power mode. + wait_for_interrupt(); +} + +bool test_main(void) { + // Enable global and external IRQ at Ibex. + irq_global_ctrl(true); + irq_external_ctrl(true); + + init_peripherals(); + + // Enable all the AON interrupts used in this test. + rv_plic_testutils_irq_range_enable(&plic, kPlicTarget, + kTopMatchaPlicIrqIdAlertHandlerClassa, + kTopMatchaPlicIrqIdAlertHandlerClassd); + + alert_handler_config(); + + // First check the flash stored value + uint32_t event_idx = flash_ctrl_testutils_counter_get(0); + + // Enable flash access + flash_ctrl_testutils_default_region_access(&flash_ctrl, + /*rd_en*/ true, + /*prog_en*/ true, + /*erase_en*/ true, + /*scramble_en*/ false, + /*ecc_en*/ false, + /*he_en*/ false); + + // Increment flash counter to know where we are + flash_ctrl_testutils_counter_increment(&flash_ctrl, 0); + + LOG_INFO("Test round %d", event_idx); + LOG_INFO("RST_IDX[%d] = %d", event_idx, RST_IDX[event_idx]); + + // Check if there was a HW reset caused by expected cases + dif_rstmgr_reset_info_bitfield_t rst_info; + rst_info = rstmgr_testutils_reason_get(); + rstmgr_testutils_reason_clear(); + + CHECK(rst_info == kDifRstmgrResetInfoPor || + rst_info == kDifRstmgrResetInfoSysRstCtrl || + rst_info == kDifRstmgrResetInfoWatchdog || + rst_info == kDifRstmgrResetInfoEscalation || + rst_info == kDifRstmgrResetInfoLowPowerExit || + rst_info == (kDifRstmgrResetInfoSysRstCtrl | + kDifRstmgrResetInfoLowPowerExit) || + rst_info == + (kDifRstmgrResetInfoPor | kDifRstmgrResetInfoLowPowerExit) || + rst_info == (kDifRstmgrResetInfoWatchdog | + kDifRstmgrResetInfoLowPowerExit) || + rst_info == (kDifRstmgrResetInfoEscalation | + kDifRstmgrResetInfoLowPowerExit) || + rst_info == kDifRstmgrResetInfoSw, + "Wrong reset reason %02X", rst_info); + + LOG_INFO("New reset event"); + + switch (RST_IDX[event_idx] / 2) { + case 0: + if (RST_IDX[event_idx] % 2) { + LOG_INFO("Booting and setting normal sleep followed by sysrst"); + config_sysrst(&pwrmgr, &sysrst_ctrl_aon); + normal_sleep_sysrst(&pwrmgr); + timer_on(kWdogBiteMicros); + } else { + LOG_INFO("Booting and setting deep sleep followed by sysrst"); + config_sysrst(&pwrmgr, &sysrst_ctrl_aon); + low_power_sysrst(&pwrmgr); + } + break; + case 1: + if (RST_IDX[event_idx] % 2) { + LOG_INFO("Booting and setting normal sleep followed by hw por"); + normal_sleep_por(&pwrmgr); + timer_on(kWdogBiteMicros); + } else { + LOG_INFO("Booting and setting deep sleep followed by hw por"); + low_power_por(&pwrmgr); + } + break; + case 2: + if (RST_IDX[event_idx] % 2) { + LOG_INFO( + "Booting and setting normal sleep mode followed for low_power " + "entry reset"); + LOG_INFO("Let SV wait timer reset"); + // actually the same test as normal sleep + watchdog + rstmgr_testutils_pre_reset(&rstmgr); + sleep_wdog_bite_test(&aon_timer, &pwrmgr, 200); + normal_sleep_wdog(&pwrmgr); + timer_on(kEscalationPhase0MicrosCpu); + } else { + LOG_INFO( + "Booting and setting deep sleep mode followed for low_power entry " + "reset"); + LOG_INFO("Let SV wait timer reset"); + // Executing the wdog bite reset during sleep test. + // actually the same test as deep sleep + watchdog + rstmgr_testutils_pre_reset(&rstmgr); + sleep_wdog_bite_test(&aon_timer, &pwrmgr, 200); + low_power_wdog(&pwrmgr); + } + break; + + case 3: + if (RST_IDX[event_idx] % 2) { + LOG_INFO( + "Booting and setting normal sleep followed by watchdog reset " + "combined " + "with sw_req"); + LOG_INFO("Let SV wait timer reset"); + // Executing the wdog bite reset during sleep test. + rstmgr_testutils_pre_reset(&rstmgr); + CHECK_DIF_OK(dif_rstmgr_software_device_reset(&rstmgr)); + LOG_INFO("Device reset from sw"); + sleep_wdog_bite_test(&aon_timer, &pwrmgr, 200); + normal_sleep_wdog(&pwrmgr); + timer_on(kEscalationPhase0MicrosCpu); + } else { + LOG_INFO( + "Booting and setting deep sleep followed by watchdog reset " + "combined " + "with sw_req"); + LOG_INFO("Let SV wait timer reset"); + // Executing the wdog bite reset during sleep test. + rstmgr_testutils_pre_reset(&rstmgr); + CHECK_DIF_OK(dif_rstmgr_software_device_reset(&rstmgr)); + LOG_INFO("Device reset from sw"); + sleep_wdog_bite_test(&aon_timer, &pwrmgr, 200); + low_power_wdog(&pwrmgr); + } + break; + case 4: + if (RST_IDX[event_idx] % 2) { + LOG_INFO("Booting and setting normal sleep followed by watchdog reset"); + LOG_INFO("Let SV wait timer reset"); + // Executing the wdog bite reset during sleep test. + rstmgr_testutils_pre_reset(&rstmgr); + sleep_wdog_bite_test(&aon_timer, &pwrmgr, 200); + normal_sleep_wdog(&pwrmgr); + timer_on(kEscalationPhase0MicrosCpu); + } else { + LOG_INFO("Booting and setting deep sleep followed by watchdog reset"); + LOG_INFO("Let SV wait timer reset"); + // Executing the wdog bite reset during sleep test. + rstmgr_testutils_pre_reset(&rstmgr); + sleep_wdog_bite_test(&aon_timer, &pwrmgr, 200); + low_power_wdog(&pwrmgr); + } + break; + case 5: + if (RST_IDX[event_idx] % 2) { + LOG_INFO("Last Booting"); + + return true; + } else { + LOG_INFO( + "Booting and running normal sleep followed by escalation reset"); + config_escalate(&aon_timer, &pwrmgr); + timer_on(kEscalationPhase0MicrosCpu); + } + break; + default: + LOG_INFO("Booting for undefined case"); + } + return false; +}
diff --git a/sw/device/tests/sim_dv/pwrmgr_random_sleep_power_glitch_reset_test.c b/sw/device/tests/sim_dv/pwrmgr_random_sleep_power_glitch_reset_test.c new file mode 100644 index 0000000..3f16463 --- /dev/null +++ b/sw/device/tests/sim_dv/pwrmgr_random_sleep_power_glitch_reset_test.c
@@ -0,0 +1,577 @@ +// Copyright 2023 Google LLC. +// 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 "hw/top_matcha/sw/autogen/top_matcha.h" +#include "sw/device/lib/base/abs_mmio.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_flash_ctrl.h" +#include "sw/device/lib/dif/dif_pinmux.h" +#include "sw/device/lib/dif/dif_pwrmgr.h" +#include "sw/device/lib/dif/dif_rstmgr.h" +#include "sw/device/lib/dif/dif_rv_plic.h" +#include "sw/device/lib/dif/dif_rv_timer.h" +#include "sw/device/lib/dif/dif_sysrst_ctrl.h" +#include "sw/device/lib/runtime/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/flash_ctrl_testutils.h" +#include "sw/device/lib/testing/pwrmgr_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" + +OTTF_DEFINE_TEST_CONFIG(); +static volatile const uint8_t RST_IDX[12] = {0, 1, 2, 3, 4, 5, + 6, 7, 8, 9, 10, 11}; +static const uint32_t kPlicTarget = kTopMatchaPlicTargetIbex0; + +/** + * Objects to access the peripherals used in this test via dif API. + */ +static dif_flash_ctrl_state_t flash_ctrl; +static dif_rv_plic_t plic; +static dif_alert_handler_t alert_handler; +static dif_aon_timer_t aon_timer; +static dif_pwrmgr_t pwrmgr; +static dif_rstmgr_t rstmgr; + +/** + * 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. + */ +enum { + kWdogBarkMicros = 3 * 100, // 300 us + kWdogBiteMicros = 4 * 100, // 400 us + kEscalationPhase0Micros = 1 * 100, // 100 us + // The cpu value is slightly larger as the busy_spin_micros + // routine cycle count comes out slightly smaller due to the + // fact that it does not divide by exactly 1M + // see sw/device/lib/runtime/hart.c + kEscalationPhase0MicrosCpu = kEscalationPhase0Micros + 20, // 120 us + kEscalationPhase1Micros = 5 * 100, // 500 us + kEscalationPhase2Micros = 50, // 50 us +}; + +static_assert( + kWdogBarkMicros < kWdogBiteMicros && + kWdogBarkMicros > kEscalationPhase0Micros && + kWdogBarkMicros < (kEscalationPhase0Micros + kEscalationPhase1Micros) && + kWdogBiteMicros < (kEscalationPhase0Micros + kEscalationPhase1Micros), + "The wdog bark and bite shall happens during the escalation phase 1"); + +/** + * 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) { + top_matcha_plic_peripheral_t peripheral; + dif_rv_plic_irq_id_t irq_id; + uint32_t irq = 0; + uint32_t alert = 0; + + CHECK_DIF_OK(dif_rv_plic_irq_claim(&plic, kPlicTarget, &irq_id)); + + peripheral = (top_matcha_plic_peripheral_t) + top_matcha_plic_interrupt_for_peripheral[irq_id]; + + if (peripheral == kTopMatchaPlicPeripheralAonTimerAon) { + irq = + (dif_aon_timer_irq_t)(irq_id - + (dif_rv_plic_irq_id_t) + kTopMatchaPlicIrqIdAonTimerAonWkupTimerExpired); + + // Stops escalation process. + CHECK_DIF_OK(dif_alert_handler_escalation_clear(&alert_handler, + kDifAlertHandlerClassA)); + CHECK_DIF_OK(dif_aon_timer_irq_acknowledge(&aon_timer, irq)); + + CHECK(irq != kTopMatchaPlicIrqIdAonTimerAonWdogTimerBark, + "AON Timer Wdog should not bark"); + + } else if (peripheral == kTopMatchaPlicPeripheralAlertHandler) { + irq = (dif_rv_plic_irq_id_t)(irq_id - + (dif_rv_plic_irq_id_t) + kTopMatchaPlicIrqIdAlertHandlerClassa); + + CHECK_DIF_OK(dif_alert_handler_alert_acknowledge(&alert_handler, alert)); + + 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); + + 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) { + // Initialize pwrmgr. + CHECK_DIF_OK(dif_pwrmgr_init( + mmio_region_from_addr(TOP_MATCHA_PWRMGR_AON_BASE_ADDR), &pwrmgr)); + + // Initialize rstmgr to check the reset reason. + CHECK_DIF_OK(dif_rstmgr_init( + mmio_region_from_addr(TOP_MATCHA_RSTMGR_AON_BASE_ADDR), &rstmgr)); + + // Initialize aon timer to use the wdog. + CHECK_DIF_OK(dif_aon_timer_init( + mmio_region_from_addr(TOP_MATCHA_AON_TIMER_AON_BASE_ADDR), &aon_timer)); + + // Initialize flash_ctrl + CHECK_DIF_OK(dif_flash_ctrl_init_state( + &flash_ctrl, + mmio_region_from_addr(TOP_MATCHA_FLASH_CTRL_CORE_BASE_ADDR))); + + // Initialize plic. + CHECK_DIF_OK(dif_rv_plic_init( + mmio_region_from_addr(TOP_MATCHA_RV_PLIC_BASE_ADDR), &plic)); + + rv_plic_testutils_irq_range_enable( + &plic, kPlicTarget, kTopMatchaPlicIrqIdAonTimerAonWkupTimerExpired, + kTopMatchaPlicIrqIdAonTimerAonWdogTimerBark); + + // Initialize alert handler. + CHECK_DIF_OK(dif_alert_handler_init( + mmio_region_from_addr(TOP_MATCHA_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[] = {kTopMatchaAlertIdPwrmgrAonFatalFault}; + dif_alert_handler_class_t alert_classes[] = {kDifAlertHandlerClassA}; + + dif_alert_handler_escalation_phase_t esc_phases[] = { + {.phase = kDifAlertHandlerClassStatePhase0, + .signal = 0, + .duration_cycles = + alert_handler_testutils_get_cycles_from_us(kEscalationPhase0Micros) * + alert_handler_testutils_cycle_rescaling_factor()}, + {.phase = kDifAlertHandlerClassStatePhase1, + .signal = 1, + .duration_cycles = + alert_handler_testutils_get_cycles_from_us(kEscalationPhase1Micros) * + alert_handler_testutils_cycle_rescaling_factor()}, + {.phase = kDifAlertHandlerClassStatePhase2, + .signal = 3, + .duration_cycles = + alert_handler_testutils_get_cycles_from_us(kEscalationPhase2Micros) * + alert_handler_testutils_cycle_rescaling_factor()}}; + + dif_alert_handler_class_config_t class_config[] = {{ + .auto_lock_accumulation_counter = kDifToggleDisabled, + .accumulator_threshold = 0, + .irq_deadline_cycles = + alert_handler_testutils_get_cycles_from_us(kEscalationPhase0Micros) * + alert_handler_testutils_cycle_rescaling_factor(), + .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 config_escalate(dif_aon_timer_t *aon_timer, + const dif_pwrmgr_t *pwrmgr) { + uint64_t bark_cycles = + aon_timer_testutils_get_aon_cycles_from_us(kWdogBarkMicros) * + alert_handler_testutils_cycle_rescaling_factor(); + uint64_t bite_cycles = + aon_timer_testutils_get_aon_cycles_from_us(kWdogBiteMicros) * + alert_handler_testutils_cycle_rescaling_factor(); + + LOG_INFO( + "Wdog will bark after %u/%u us/cycles and bite after %u/%u us/cycles", + (uint32_t)kWdogBarkMicros, (uint32_t)bark_cycles, + (uint32_t)kWdogBiteMicros, (uint32_t)bite_cycles); + + // Setup the wdog bark and bite timeouts. + aon_timer_testutils_watchdog_config(aon_timer, bark_cycles, bite_cycles, + false); + + // Trigger the alert handler to escalate. + dif_pwrmgr_alert_t alert = kDifPwrmgrAlertFatalFault; + CHECK_DIF_OK(dif_pwrmgr_alert_force(pwrmgr, alert)); +} + +static void low_power_glitch_reset(const dif_pwrmgr_t *pwrmgr) { + // Program the pwrmgr to go to deep sleep state (clocks off). + pwrmgr_testutils_enable_low_power(pwrmgr, kDifPwrmgrWakeupRequestSourceFive, + 0); + // Enter in low power mode. + wait_for_interrupt(); +} + +static void normal_sleep_glitch_reset(const dif_pwrmgr_t *pwrmgr) { + // Place device into normal (shallow) power + dif_pwrmgr_domain_config_t config; + config = kDifPwrmgrDomainOptionUsbClockInLowPower | + kDifPwrmgrDomainOptionCoreClockInLowPower | + kDifPwrmgrDomainOptionIoClockInLowPower | + kDifPwrmgrDomainOptionMainPowerInLowPower; + pwrmgr_testutils_enable_low_power(pwrmgr, kDifPwrmgrWakeupRequestSourceFive, + config); + // Enter in low power mode. + wait_for_interrupt(); +} + +static void timer_on(uint32_t usec) { + busy_spin_micros(usec); + // If we arrive here the test must fail. + CHECK(false, "Timeout waiting for reset!"); +} + +/** + * Configure the wdog. + */ +static void config_wdog(const dif_aon_timer_t *aon_timer, + const dif_pwrmgr_t *pwrmgr, uint64_t bark_time_us, + uint64_t bite_time_us) { + uint32_t bark_cycles = + aon_timer_testutils_get_aon_cycles_from_us(bark_time_us); + uint32_t bite_cycles = + aon_timer_testutils_get_aon_cycles_from_us(bite_time_us); + + LOG_INFO("Wdog will bark after %u us and bite after %u us", + (uint32_t)bark_time_us, (uint32_t)bite_time_us); + // Setup the wdog bark and bite timeouts. + + aon_timer_testutils_watchdog_config(aon_timer, bark_cycles, bite_cycles, + false); + // Set wdog as a reset source. + CHECK_DIF_OK(dif_pwrmgr_set_request_sources(pwrmgr, kDifPwrmgrReqTypeReset, + kDifPwrmgrResetRequestSourceTwo, + kDifToggleEnabled)); +} + +/** + * Execute the aon timer wdog bite reset test. + */ +static void wdog_bite_test(const dif_aon_timer_t *aon_timer, + const dif_pwrmgr_t *pwrmgr, uint64_t bark_time_us) { + uint64_t bite_time_us = bark_time_us * 2; + config_wdog(aon_timer, pwrmgr, bark_time_us, bite_time_us); + + // The `intr_state` takes 3 aon clock cycles to rise plus 2 extra cycles as a + // precaution. + uint32_t wait_us = + bark_time_us + + alert_handler_testutils_get_cycles_from_us(kEscalationPhase0Micros) * + alert_handler_testutils_cycle_rescaling_factor(); + + // Wait bark time and check that the bark interrupt requested. + busy_spin_micros(wait_us); + bool is_pending = false; + CHECK_DIF_OK(dif_aon_timer_irq_is_pending( + aon_timer, kDifAonTimerIrqWdogTimerBark, &is_pending)); + CHECK(is_pending, "Wdog bark irq did not rise after %u microseconds", + wait_us); + + // Wait for the remaining time to the wdog bite. + busy_spin_micros(wait_us); + // If we arrive here the test must fail. + CHECK(false, "Timeout waiting for Wdog bite reset!"); +} + +/** + * Execute the aon timer wdog bite reset during sleep test. + */ +static void sleep_wdog_bite_test(const dif_aon_timer_t *aon_timer, + const dif_pwrmgr_t *pwrmgr, + uint64_t bark_time_us) { + uint64_t bite_time_us = bark_time_us * 2; + config_wdog(aon_timer, pwrmgr, bark_time_us, bite_time_us); +} + +static void low_power_wdog(const dif_pwrmgr_t *pwrmgr) { + // Program the pwrmgr to go to deep sleep state (clocks off). + // Enter in low power mode. + pwrmgr_testutils_enable_low_power(pwrmgr, kDifPwrmgrWakeupRequestSourceTwo, + 0); + LOG_INFO("Low power set for watch dog"); + wait_for_interrupt(); + // If we arrive here the test must fail. + CHECK(false, "Fail to enter in low power mode!"); +} + +static void normal_sleep_wdog(const dif_pwrmgr_t *pwrmgr) { + // Place device into low power and immediately wake. + dif_pwrmgr_domain_config_t config; + config = kDifPwrmgrDomainOptionUsbClockInLowPower | + kDifPwrmgrDomainOptionCoreClockInLowPower | + kDifPwrmgrDomainOptionIoClockInLowPower | + kDifPwrmgrDomainOptionMainPowerInLowPower; + + // Enter in low power mode. + pwrmgr_testutils_enable_low_power(pwrmgr, kDifPwrmgrWakeupRequestSourceTwo, + config); + LOG_INFO("Normal sleep set for watchdog"); + wait_for_interrupt(); +} + +static void low_power_por(const dif_pwrmgr_t *pwrmgr) { + // Set por as a reset source. + CHECK_DIF_OK(dif_pwrmgr_set_request_sources(pwrmgr, kDifPwrmgrReqTypeReset, + kDifPwrmgrResetRequestSourceTwo, + kDifToggleEnabled)); + + // Program the pwrmgr to go to deep sleep state (clocks off). + pwrmgr_testutils_enable_low_power( + pwrmgr, + (kDifPwrmgrWakeupRequestSourceOne | kDifPwrmgrWakeupRequestSourceTwo | + kDifPwrmgrWakeupRequestSourceThree | kDifPwrmgrWakeupRequestSourceFour | + kDifPwrmgrWakeupRequestSourceFive | kDifPwrmgrWakeupRequestSourceSix), + 0); + // Enter in low power mode. + wait_for_interrupt(); + // If we arrive here the test must fail. + CHECK(false, "Fail to enter in low power mode!"); +} + +static void normal_sleep_por(const dif_pwrmgr_t *pwrmgr) { + // Set por as a reset source. + CHECK_DIF_OK(dif_pwrmgr_set_request_sources(pwrmgr, kDifPwrmgrReqTypeReset, + kDifPwrmgrResetRequestSourceTwo, + kDifToggleEnabled)); + + // Place device into low power and immediately wake. + dif_pwrmgr_domain_config_t config; + config = kDifPwrmgrDomainOptionUsbClockInLowPower | + kDifPwrmgrDomainOptionCoreClockInLowPower | + kDifPwrmgrDomainOptionIoClockInLowPower | + kDifPwrmgrDomainOptionMainPowerInLowPower; + + // Program the pwrmgr to go to swallow sleep state (clocks on). + pwrmgr_testutils_enable_low_power( + pwrmgr, + (kDifPwrmgrWakeupRequestSourceOne | kDifPwrmgrWakeupRequestSourceTwo | + kDifPwrmgrWakeupRequestSourceThree | kDifPwrmgrWakeupRequestSourceFour | + kDifPwrmgrWakeupRequestSourceFive | kDifPwrmgrWakeupRequestSourceSix), + config); + // Enter in low power mode. + wait_for_interrupt(); +} + +bool test_main(void) { + // Enable global and external IRQ at Ibex. + irq_global_ctrl(true); + irq_external_ctrl(true); + + init_peripherals(); + + // Enable all the AON interrupts used in this test. + rv_plic_testutils_irq_range_enable(&plic, kPlicTarget, + kTopMatchaPlicIrqIdAlertHandlerClassa, + kTopMatchaPlicIrqIdAlertHandlerClassd); + + alert_handler_config(); + + // First check the flash stored value + uint32_t event_idx = flash_ctrl_testutils_counter_get(0); + + // Enable flash access + flash_ctrl_testutils_default_region_access(&flash_ctrl, + /*rd_en*/ true, + /*prog_en*/ true, + /*erase_en*/ true, + /*scramble_en*/ false, + /*ecc_en*/ false, + /*he_en*/ false); + + // Increment flash counter to know where we are + flash_ctrl_testutils_counter_increment(&flash_ctrl, 0); + + LOG_INFO("Test round %d", event_idx); + LOG_INFO("RST_IDX[%d] = %d", event_idx, RST_IDX[event_idx]); + + // Check if there was a HW reset caused by expected cases + dif_rstmgr_reset_info_bitfield_t rst_info; + rst_info = rstmgr_testutils_reason_get(); + rstmgr_testutils_reason_clear(); + LOG_INFO("reset info = %02X", rst_info); + + CHECK(rst_info == kDifRstmgrResetInfoPor || + rst_info == kDifRstmgrResetInfoSysRstCtrl || + rst_info == kDifRstmgrResetInfoWatchdog || + rst_info == kDifRstmgrResetInfoEscalation || + rst_info == kDifRstmgrResetInfoLowPowerExit || + rst_info == (kDifRstmgrResetInfoSysRstCtrl | + kDifRstmgrResetInfoLowPowerExit) || + rst_info == (kDifRstmgrResetInfoPowerUnstable | + kDifRstmgrResetInfoLowPowerExit) || + rst_info == (kDifRstmgrResetInfoPowerUnstable | + kDifRstmgrResetInfoWatchdog | + kDifRstmgrResetInfoLowPowerExit) || + rst_info == (kDifRstmgrResetInfoPowerUnstable | + kDifRstmgrResetInfoWatchdog) || + rst_info == kDifRstmgrResetInfoPowerUnstable || + rst_info == + (kDifRstmgrResetInfoPor | kDifRstmgrResetInfoLowPowerExit) || + rst_info == (kDifRstmgrResetInfoWatchdog | + kDifRstmgrResetInfoLowPowerExit) || + rst_info == (kDifRstmgrResetInfoEscalation | + kDifRstmgrResetInfoLowPowerExit) || + rst_info == kDifRstmgrResetInfoSw, + "Wrong reset reason %02X", rst_info); + + switch (RST_IDX[event_idx] / 2) { + case 0: + if (RST_IDX[event_idx] % 2) { + LOG_INFO("Booting and setting normal sleep followed by glitch reset"); + LOG_INFO("Let SV wait timer reset"); + normal_sleep_glitch_reset(&pwrmgr); + timer_on(kWdogBiteMicros); + } else { + LOG_INFO("Booting and setting deep sleep followed by glitch reset"); + LOG_INFO("Let SV wait timer reset"); + low_power_glitch_reset(&pwrmgr); + } + break; + case 1: + if (RST_IDX[event_idx] % 2) { + LOG_INFO("Booting and setting normal sleep followed by hw por"); + LOG_INFO("Let SV wait timer reset"); + normal_sleep_por(&pwrmgr); + timer_on(kWdogBiteMicros); + } else { + LOG_INFO("Booting and setting deep sleep followed by hw por"); + LOG_INFO("Let SV wait timer reset"); + low_power_por(&pwrmgr); + } + break; + case 2: + if (RST_IDX[event_idx] % 2) { + LOG_INFO( + "Booting and setting normal sleep mode followed for low_power " + "entry reset"); + LOG_INFO("Let SV wait timer reset"); + // actually the same test as normal sleep + watchdog + rstmgr_testutils_pre_reset(&rstmgr); + sleep_wdog_bite_test(&aon_timer, &pwrmgr, 200); + normal_sleep_wdog(&pwrmgr); + timer_on(kEscalationPhase0MicrosCpu); + } else { + LOG_INFO( + "Booting and setting deep sleep mode followed for low_power entry " + "reset"); + LOG_INFO("Let SV wait timer reset"); + // Executing the wdog bite reset during sleep test. + // actually the same test as deep sleep + watchdog + rstmgr_testutils_pre_reset(&rstmgr); + sleep_wdog_bite_test(&aon_timer, &pwrmgr, 200); + low_power_wdog(&pwrmgr); + } + break; + + case 3: + if (RST_IDX[event_idx] % 2) { + LOG_INFO( + "Booting and setting normal sleep followed by watchdog reset " + "combined " + "with sw_req"); + LOG_INFO("Let SV wait timer reset"); + // Executing the wdog bite reset during sleep test. + rstmgr_testutils_pre_reset(&rstmgr); + CHECK_DIF_OK(dif_rstmgr_software_device_reset(&rstmgr)); + LOG_INFO("Device reset from sw"); + sleep_wdog_bite_test(&aon_timer, &pwrmgr, 200); + normal_sleep_wdog(&pwrmgr); + timer_on(kEscalationPhase0MicrosCpu); + } else { + LOG_INFO( + "Booting and setting deep sleep followed by watchdog reset " + "combined " + "with sw_req"); + LOG_INFO("Let SV wait timer reset"); + // Executing the wdog bite reset during sleep test. + rstmgr_testutils_pre_reset(&rstmgr); + CHECK_DIF_OK(dif_rstmgr_software_device_reset(&rstmgr)); + LOG_INFO("Device reset from sw"); + sleep_wdog_bite_test(&aon_timer, &pwrmgr, 200); + low_power_wdog(&pwrmgr); + } + break; + case 4: + if (RST_IDX[event_idx] % 2) { + LOG_INFO("Booting and setting normal sleep followed by watchdog reset"); + LOG_INFO("Let SV wait timer reset"); + // Executing the wdog bite reset during sleep test. + rstmgr_testutils_pre_reset(&rstmgr); + sleep_wdog_bite_test(&aon_timer, &pwrmgr, 200); + normal_sleep_wdog(&pwrmgr); + timer_on(kEscalationPhase0MicrosCpu); + } else { + LOG_INFO("Booting and setting deep sleep followed by watchdog reset"); + LOG_INFO("Let SV wait timer reset"); + // Executing the wdog bite reset during sleep test. + rstmgr_testutils_pre_reset(&rstmgr); + sleep_wdog_bite_test(&aon_timer, &pwrmgr, 200); + low_power_wdog(&pwrmgr); + } + break; + case 5: + if (RST_IDX[event_idx] % 2) { + LOG_INFO("Last Booting"); + + return true; + } else { + LOG_INFO( + "Booting and running normal sleep followed by escalation reset"); + config_escalate(&aon_timer, &pwrmgr); + timer_on(kEscalationPhase0MicrosCpu); + } + break; + default: + LOG_INFO("Booting for undefined case"); + } + + return false; +}