Skip to content

Commit

Permalink
[test] alert_handler_reverse_ping_in_deep_sleep
Browse files Browse the repository at this point in the history
Check that escalation receivers located inside always-on blocks do not
auto-escalate due to the reverse ping feature while the system is in deep
sleep.

Signed-off-by: Miguel Osorio <[email protected]>
  • Loading branch information
moidx committed Sep 13, 2022
1 parent f520fbb commit db5c836
Show file tree
Hide file tree
Showing 4 changed files with 334 additions and 1 deletion.
46 changes: 45 additions & 1 deletion hw/top_earlgrey/data/chip_testplan.hjson
Original file line number Diff line number Diff line change
Expand Up @@ -1415,9 +1415,53 @@

Check that escalation receivers located inside always-on blocks do not auto-escalate
due to the reverse ping feature while the system is in deep sleep.

## Reverse ping timeout calculation

The reverse ping timeout calculation is done using the following formula available in
`prim_esc_receiver`:

```
4 * N_ESC_SEV * (2 * 2 * 2^PING_CNT_DW)
```

`pwrmgr` is the only block consuming the `N_ESC_SEV` and `PING_CNT_DW` compile time
parameters:

```
alert_handler_reg_pkg::N_ESC_SEV = 4
alert_handler_reg_pkg::PING_CNT_DW = 16
```

The alert escalation responder inside `pwrmgr` is connected to the `io_div4` clock,
yielding a target 24MHz frequency. The result expected timeout based on the above
parameters is thus:

```
reverse_ping_timeout = 0.175s = (4 * 4 ( 2 * 2 * 2^16)) / 24e6
```

## Procedure

- On POR reset:
- Enable all alerts assigning them to ClassA.
- Enable all local alerts and assign to ClassB.
- Set escalation configuration to trigger before test wake up time.
- Set ping timeout to a time less than wake up time.
- Lock alert configuration and enable ping mechanism.
- Wait for polling counters to cycle through by busy polling on Ibex for
`reverse_ping_timeout >> 2` usec.
- Configure AON to wake up device at a later time, making sure it is greater than the
`reverse_ping_timeout` calculated in the previous section.
- Enter deep sleep.
- On wake up from sleep:
- Ensure reset status is low power exit. A `kDifRstmgrResetInfoEscalation` signals that
there was a local escalation and should result in test failure.
- Disable AON timer.
- Check there are no flagged local alerts.
'''
stage: V2
tests: []
tests: ["chip_sw_alert_handler_reverse_ping_in_deep_sleep"]
}

// LC_CTRL (pre-verified IP) integration tests:
Expand Down
19 changes: 19 additions & 0 deletions hw/top_earlgrey/dv/chip_sim_cfg.hjson
Original file line number Diff line number Diff line change
Expand Up @@ -776,15 +776,34 @@
uvm_test_seq: chip_sw_alert_handler_escalation_vseq
sw_images: ["//sw/device/tests/sim_dv:alert_handler_escalation_test:1"]
en_run_modes: ["sw_test_mode_test_rom"]
// Disable scoreboard to avoid incorrect alert prediction from the alert_monitor. Due to the
// cross-domain alert senders and receivers, the monitor from the chip level did not support
// processing alerts accurately from both ends.
run_opts: ["+en_scb=0", "+sw_test_timeout_ns=5000000", "+bypass_alert_ready_to_end_check=1"]
}
{
name: chip_sw_alert_handler_ping_timeout
uvm_test_seq: chip_sw_base_vseq
sw_images: ["//sw/device/tests:alert_handler_ping_timeout_test:1"]
en_run_modes: ["sw_test_mode_test_rom"]
// Disable scoreboard to avoid incorrect alert prediction from the alert_monitor. Due to the
// cross-domain alert senders and receivers, the monitor from the chip level did not support
// processing alerts accurately from both ends.
run_opts: ["+en_scb=0"]
}
{
name: chip_sw_alert_handler_reverse_ping_in_deep_sleep
uvm_test_seq: chip_sw_base_vseq
sw_images: ["//sw/device/tests:alert_handler_reverse_ping_in_deep_sleep_test:1"]
en_run_modes: ["sw_test_mode_test_rom"]
// Disable scoreboard to avoid incorrect alert prediction from the alert_monitor. Due to the
// cross-domain alert senders and receivers, the monitor from the chip level did not support
// processing alerts accurately from both ends.
// This test takes long due to the compile time configured reverse timeout. See test plan for
// more details.
run_opts: ["+en_scb=0", "+sw_test_timeout_ns=240_000_000"]
run_timeout_mins: 240
}
{
name: chip_sw_alert_handler_entropy
uvm_test_seq: chip_sw_alert_handler_entropy_vseq
Expand Down
33 changes: 33 additions & 0 deletions sw/device/tests/BUILD
Original file line number Diff line number Diff line change
Expand Up @@ -129,6 +129,39 @@ opentitan_functest(
],
)

opentitan_functest(
name = "alert_handler_reverse_ping_in_deep_sleep_test",
srcs = ["alert_handler_reverse_ping_in_deep_sleep_test.c"],
targets = [
# The test requires to run for > 0.2s, thus not recommended for
# Verilator as this will slow down CI too much. It should still
# be run as part of the DV nightly regression.
"cw310",
"dv",
],
deps = [
"//hw/top_earlgrey:alert_handler_regs",
"//hw/top_earlgrey/sw/autogen:top_earlgrey",
"//sw/device/lib/base:math",
"//sw/device/lib/base:mmio",
"//sw/device/lib/dif:alert_handler",
"//sw/device/lib/dif:aon_timer",
"//sw/device/lib/dif:pwrmgr",
"//sw/device/lib/dif:rstmgr",
"//sw/device/lib/dif:rv_plic",
"//sw/device/lib/runtime:ibex",
"//sw/device/lib/runtime:irq",
"//sw/device/lib/runtime:log",
"//sw/device/lib/testing:alert_handler_testutils",
"//sw/device/lib/testing:aon_timer_testutils",
"//sw/device/lib/testing:isr_testutils",
"//sw/device/lib/testing:pwrmgr_testutils",
"//sw/device/lib/testing:rstmgr_testutils",
"//sw/device/lib/testing:rv_plic_testutils",
"//sw/device/lib/testing/test_framework:ottf_main",
],
)

opentitan_functest(
name = "aon_timer_irq_test",
srcs = ["aon_timer_irq_test.c"],
Expand Down
237 changes: 237 additions & 0 deletions sw/device/tests/alert_handler_reverse_ping_in_deep_sleep_test.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,237 @@
// Copyright lowRISC contributors.
// Licensed under the Apache License, Version 2.0, see LICENSE for details.
// SPDX-License-Identifier: Apache-2.0

#include <assert.h>
#include <limits.h>
#include <stdbool.h>
#include <stdint.h>

#include "sw/device/lib/base/math.h"
#include "sw/device/lib/base/mmio.h"
#include "sw/device/lib/dif/dif_alert_handler.h"
#include "sw/device/lib/dif/dif_aon_timer.h"
#include "sw/device/lib/dif/dif_pwrmgr.h"
#include "sw/device/lib/dif/dif_rstmgr.h"
#include "sw/device/lib/dif/dif_rv_plic.h"
#include "sw/device/lib/runtime/hart.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/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"

#include "alert_handler_regs.h" // Generated.
#include "hw/top_earlgrey/sw/autogen/top_earlgrey.h"
#include "sw/device/lib/testing/autogen/isr_testutils.h"

OTTF_DEFINE_TEST_CONFIG();

enum {
// This time needs to be greater than 0.175s. See test plan for more details.
kTestParamWakeupThresholdUsec = 200000,

// This time is needed to cycle through all the alert pings. See test plan
// for more details.
kTestParamCycleThroughAllPingsUsec = kTestParamWakeupThresholdUsec >> 2,
kTestParamAlertHandlerIrqDeadlineUsec = 100,
kTestParamAlertHandlerPhase0EscalationDurationUsec = 100,
kTestParamAlertHandlerPingTimeoutUsec = 20,
};

static_assert(
kTestParamWakeupThresholdUsec > 175000,
"Invalid kTestParamWakeupThresholdUsec. See test plan for more details.");

static dif_rv_plic_t plic;
static dif_pwrmgr_t pwrmgr;
static dif_rstmgr_t rstmgr;
static dif_aon_timer_t aon_timer;
static dif_alert_handler_t alert_handler;
static const uint32_t kPlicTarget = kTopEarlgreyPlicTargetIbex0;

static plic_isr_ctx_t plic_ctx = {
.rv_plic = &plic,
.hart_id = kPlicTarget,
};

static volatile bool interrupt_serviced = false;

/**
* Initialize the peripherals used in this test.
*/
static void init_peripherals(void) {
CHECK_DIF_OK(dif_rv_plic_init(
mmio_region_from_addr(TOP_EARLGREY_RV_PLIC_BASE_ADDR), &plic));
CHECK_DIF_OK(dif_alert_handler_init(
mmio_region_from_addr(TOP_EARLGREY_ALERT_HANDLER_BASE_ADDR),
&alert_handler));
CHECK_DIF_OK(dif_pwrmgr_init(
mmio_region_from_addr(TOP_EARLGREY_PWRMGR_AON_BASE_ADDR), &pwrmgr));
CHECK_DIF_OK(dif_rstmgr_init(
mmio_region_from_addr(TOP_EARLGREY_RSTMGR_AON_BASE_ADDR), &rstmgr));
CHECK_DIF_OK(dif_aon_timer_init(
mmio_region_from_addr(TOP_EARLGREY_AON_TIMER_AON_BASE_ADDR), &aon_timer));

// Enable all the alert_handler interrupts used in this test.
rv_plic_testutils_irq_range_enable(&plic, kPlicTarget,
kTopEarlgreyPlicIrqIdAlertHandlerClassa,
kTopEarlgreyPlicIrqIdAlertHandlerClassd);
}

/**
* Program the alert handler to escalate on alerts upto phase 1 (i.e. wipe
* secret) but not trigger reset. Then CPU can check if the correct interrupt
* fires and check the local alert cause register.
*/
static void alert_handler_config(void) {
dif_alert_handler_alert_t alerts[ALERT_HANDLER_PARAM_N_ALERTS];
dif_alert_handler_class_t alert_classes[ALERT_HANDLER_PARAM_N_ALERTS];

// Enable all incoming alerts and configure them to classa.
// This alert should never fire because we do not expect any incoming alerts.
for (int i = 0; i < ALERT_HANDLER_PARAM_N_ALERTS; ++i) {
alerts[i] = i;
alert_classes[i] = kDifAlertHandlerClassA;
}

// Enable alert ping fail local alert and configure that to classb.
dif_alert_handler_local_alert_t loc_alerts[ALERT_HANDLER_PARAM_N_LOC_ALERT];
dif_alert_handler_class_t loc_alert_classes[ALERT_HANDLER_PARAM_N_LOC_ALERT];
for (int i = 0; i < ALERT_HANDLER_PARAM_N_LOC_ALERT; ++i) {
loc_alerts[i] = i;
loc_alert_classes[i] = kDifAlertHandlerClassB;
}

dif_alert_handler_escalation_phase_t esc_phases[] = {
{
.phase = kDifAlertHandlerClassStatePhase0,
.signal = 0,
.duration_cycles = alert_handler_testutils_get_cycles_from_us(
kTestParamAlertHandlerPhase0EscalationDurationUsec),
},
};

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(
kTestParamAlertHandlerIrqDeadlineUsec),
.escalation_phases = esc_phases,
.escalation_phases_len = ARRAYSIZE(esc_phases),
.crashdump_escalation_phase = kDifAlertHandlerClassStatePhase1,
};

dif_alert_handler_class_config_t class_configs[] = {class_config,
class_config};

dif_alert_handler_class_t classes[] = {kDifAlertHandlerClassA,
kDifAlertHandlerClassB};
dif_alert_handler_config_t config = {
.alerts = alerts,
.alert_classes = alert_classes,
.alerts_len = ARRAYSIZE(alerts),
.local_alerts = loc_alerts,
.local_alert_classes = loc_alert_classes,
.local_alerts_len = ARRAYSIZE(loc_alerts),
.classes = classes,
.class_configs = class_configs,
.classes_len = ARRAYSIZE(class_configs),
.ping_timeout = alert_handler_testutils_get_cycles_from_us(
kTestParamAlertHandlerPingTimeoutUsec),
};

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));

CHECK_DIF_OK(dif_alert_handler_irq_set_enabled(
&alert_handler, kDifAlertHandlerIrqClassb, kDifToggleEnabled));
}

/**
* Ensure there were no local alerts fired.
*/
static void check_local_alerts(void) {
for (int i = 0; i < ALERT_HANDLER_PARAM_N_LOC_ALERT; ++i) {
bool is_cause;
CHECK_DIF_OK(
dif_alert_handler_local_alert_is_cause(&alert_handler, i, &is_cause));
CHECK(!is_cause, "Unexpected local alert cause: %d", i);
}
}

/**
* External ISR.
*
* Handles all peripheral interrupts on Ibex. PLIC asserts an external interrupt
* line to the CPU, which results in a call to this OTTF ISR. This ISR
* overrides the default OTTF implementation.
*/
void ottf_external_isr(void) { interrupt_serviced = true; }

bool test_main(void) {
init_peripherals();

if (pwrmgr_testutils_is_wakeup_reason(&pwrmgr, 0)) {
LOG_INFO("POR reset");
CHECK(rstmgr_testutils_reset_info_any(&rstmgr, kDifRstmgrResetInfoPor));
rstmgr_testutils_pre_reset(&rstmgr);

alert_handler_config();

irq_global_ctrl(true);
irq_external_ctrl(true);

uint32_t wakeup_threshold = aon_timer_testutils_get_aon_cycles_from_us(
kTestParamWakeupThresholdUsec);

// Sleep longer in FPGA and silicon targets.
if (kDeviceType != kDeviceSimDV && kDeviceType != kDeviceSimVerilator) {
uint32_t wakeup_threshold_new = wakeup_threshold * 100;
CHECK(wakeup_threshold_new > wakeup_threshold,
"Detected wakeup_threshold overflow.");
wakeup_threshold = wakeup_threshold_new;
}

// Wait for the alert handler to cycle through all pings and make sure
// there were no interrupts fired during that time.
busy_spin_micros(kTestParamCycleThroughAllPingsUsec);
check_local_alerts();
CHECK(interrupt_serviced == false, "Unexpected interrupt triggered.");

// Enable and enter deep sleep.
aon_timer_testutils_wakeup_config(&aon_timer, wakeup_threshold);
pwrmgr_testutils_enable_low_power(&pwrmgr,
kDifPwrmgrWakeupRequestSourceFive, 0);
wait_for_interrupt();
CHECK(false, "Fail to enter in low power mode!");
OT_UNREACHABLE();
} else if (pwrmgr_testutils_is_wakeup_reason(
&pwrmgr, kDifPwrmgrWakeupRequestSourceFive)) {
LOG_INFO("Wakeup reset");
CHECK(rstmgr_testutils_is_reset_info(&rstmgr,
kDifRstmgrResetInfoLowPowerExit));
aon_timer_testutils_shutdown(&aon_timer);

// At this point the test has verified that the reset reason is low power
// exit, which discounts any resets triggered by local alert escalations.
// We check local alerts and interrupt flag one more time to ensure the
// alert handler resumes with the expected state.
check_local_alerts();
CHECK(interrupt_serviced == false, "Unexpected interrupt triggered.");
return true;
}
dif_pwrmgr_wakeup_reason_t wakeup_reason;
CHECK_DIF_OK(dif_pwrmgr_wakeup_reason_get(&pwrmgr, &wakeup_reason));
LOG_ERROR("Unexpected wakeup detected: type = %d, request_source = %d",
wakeup_reason.types, wakeup_reason.request_sources);
return false;
}

0 comments on commit db5c836

Please sign in to comment.