Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[chip-test] alert_handler_reverse_ping_in_deep_sleep #14663

Merged
merged 1 commit into from
Sep 13, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This LGTM!

'''
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 @@ -778,15 +778,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.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is this something that we track in an issue to fix later (if not, should it be?)?

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.
moidx marked this conversation as resolved.
Show resolved Hide resolved
#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,
cindychip marked this conversation as resolved.
Show resolved Hide resolved
};

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