blob: 8c27ee8b973e75d48cd2489e51ba7a3d51496a59 [file] [log] [blame]
// Copyright lowRISC contributors.
// Licensed under the Apache License, Version 2.0, see LICENSE for details.
// SPDX-License-Identifier: Apache-2.0
#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_i2c.h"
#include "sw/device/lib/dif/dif_otp_ctrl.h"
#include "sw/device/lib/dif/dif_pwrmgr.h"
#include "sw/device/lib/dif/dif_rstmgr.h"
#include "sw/device/lib/dif/dif_rv_core_ibex.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_spi_host.h"
#include "sw/device/lib/dif/dif_uart.h"
#include "sw/device/lib/irq.h"
#include "sw/device/lib/runtime/log.h"
#include "sw/device/lib/testing/alert_handler_testutils.h"
#include "sw/device/lib/testing/aon_timer_testutils.h"
#include "sw/device/lib/testing/rstmgr_testutils.h"
#include "sw/device/lib/testing/rv_plic_testutils.h"
#include "sw/device/lib/testing/test_framework/FreeRTOSConfig.h"
#include "sw/device/lib/testing/test_framework/check.h"
#include "sw/device/lib/testing/test_framework/ottf_main.h"
#include "alert_handler_regs.h" // Generated.
#include "hw/top_earlgrey/sw/autogen/top_earlgrey.h"
/*
RSTMGR ALERT_INFO Test
This test runs some alert / dump scenario and check integrity of
ALERT_INFO coming from alert_hander to rstmgr.
From dif_rstmgr_reset_info_t, there are 8 different resets.
This test covers 3 of those (kDifRstmgrResetInfoSw,
kDifRstmgrResetInfoWatchdog, kDifRstmgrResetInfoEscalation).
kDifRstmgrResetInfoNdm will be added as a separate test after ndm request test
is available. Test goes for 3 rounds, and on each round it creates a different
reset scenario.
Round 1: Single Class profile
- Trigger alert from i2c0..2 by calling alert_force.
- This will trigger kDifAlertHandlerIrqClassa from alert_handler.
- Also alert_info start to transfer from alert_handler to rstmgr.
- Upon detecting interrupt, 'ottf_external_isr' request reset to rstmgr
- After reset, alert_info will be available at rstmgr.
- Read alert_info from rstmgr and compare with expected value.
Round 2: Multi Classes profile
- Trigger alert from uart0..3 and otp_ctrl.
- Setup the aon_timer wdog bark and bite timeouts.
- Let the timer expire to create watchdog bite.
- After reset, coalesced alert info from uarts and otp_ctrl
will be available from rstmgr.
- Read alert_info from rstmgr and compare with expected value.
Round 3: All Classes profile with alert escalation
- Trigger rv_core_ibex alert that leads to an interrupt.
- Trigger other alerts for all classes.
- rv_core_ibex alert will be escalated to phase2 then it will trigger
reset.
- Read alert_info from rstmgr and compare with expected value.
*/
OTTF_DEFINE_TEST_CONFIG();
enum {
kWdogBarkMicros = 200, // us
kWdogBiteMicros = 200, // us
kEscalationPhase0Micros = 300, // us
kEscalationPhase1Micros = 200, // us
kEscalationPhase2Micros = 100, // us
kRoundOneDelay = 100, // us
kRoundTwoDelay = 100, // us
kRoundThreeDelay = 1000, // us
};
static const uint32_t kPlicTarget = kTopEarlgreyPlicTargetIbex0;
static dif_rstmgr_t rstmgr;
static dif_alert_handler_t alert_handler;
static dif_uart_t uart0, uart1, uart2, uart3;
static dif_otp_ctrl_t otp_ctrl;
static dif_spi_host_t spi_host;
static dif_rv_plic_t plic;
static dif_rv_core_ibex_t rv_core_ibex;
static dif_aon_timer_t aon_timer;
static dif_pwrmgr_t pwrmgr;
static dif_i2c_t i2c0, i2c1, i2c2;
typedef struct node {
const char *name;
dif_alert_handler_alert_t alert;
dif_alert_handler_class_t class;
} node_t;
typedef enum cstate {
kCstateIdle = 0,
kCstateTimeout = 1,
kCstateTerminal = 3,
kCstatePhase0 = 4,
kCstatePhase1 = 5,
kCstatePhase2 = 6,
kCstatePhase3 = 7,
kCstateFsmError = 2
} cstate_t;
typedef enum test_round {
kRound1 = 0,
kRound2 = 1,
kRound3 = 2,
kRoundTotal = 3
} test_round_t;
typedef struct alert_info {
char *test_name;
uint64_t alert_cause; // 61bit
uint8_t loc_alert_cause; // 7bit
uint16_t class_accum_cnt[ALERT_HANDLER_PARAM_N_CLASSES]; // 4x16bit
uint32_t class_esc_cnt[ALERT_HANDLER_PARAM_N_CLASSES]; // 4x32bit
cstate_t class_esc_state[ALERT_HANDLER_PARAM_N_CLASSES]; // 4x3bit
} alert_info_t;
static volatile test_round_t global_test_round;
static volatile uint32_t global_alert_called;
static const dif_alert_handler_escalation_phase_t
kEscProfiles[][ALERT_HANDLER_PARAM_N_CLASSES] = {
[kDifAlertHandlerClassA] = {{.phase = kDifAlertHandlerClassStatePhase0,
.signal = 0,
.duration_cycles = 5000},
{.phase = kDifAlertHandlerClassStatePhase1,
.signal = 0,
.duration_cycles = 3000}},
[kDifAlertHandlerClassB] = {{.phase = kDifAlertHandlerClassStatePhase1,
.signal = 0,
.duration_cycles = 3000}},
[kDifAlertHandlerClassC] = {{.phase = kDifAlertHandlerClassStatePhase0,
.signal = 0,
.duration_cycles = 5000},
{.phase = kDifAlertHandlerClassStatePhase1,
.signal = 0,
.duration_cycles = 3000}},
[kDifAlertHandlerClassD] = {{.phase = kDifAlertHandlerClassStatePhase0,
.signal = 0,
.duration_cycles = 7200},
{.phase = kDifAlertHandlerClassStatePhase1,
.signal = 1,
.duration_cycles = 4800},
{.phase = kDifAlertHandlerClassStatePhase2,
.signal = 3,
.duration_cycles = 2400}}};
static const dif_alert_handler_class_config_t
kConfigProfiles[ALERT_HANDLER_PARAM_N_CLASSES] = {
[kDifAlertHandlerClassA] =
{
.auto_lock_accumulation_counter = kDifToggleDisabled,
.accumulator_threshold = 0,
.irq_deadline_cycles = 240,
.escalation_phases = kEscProfiles[kDifAlertHandlerClassA],
.escalation_phases_len = 2,
.crashdump_escalation_phase = kDifAlertHandlerClassStatePhase1,
},
[kDifAlertHandlerClassB] =
{
.auto_lock_accumulation_counter = kDifToggleDisabled,
.accumulator_threshold = 0,
.irq_deadline_cycles = 240,
.escalation_phases = kEscProfiles[kDifAlertHandlerClassB],
.escalation_phases_len = 1,
.crashdump_escalation_phase = kDifAlertHandlerClassStatePhase1,
},
[kDifAlertHandlerClassC] =
{
.auto_lock_accumulation_counter = kDifToggleDisabled,
.accumulator_threshold = 0,
.irq_deadline_cycles = 240,
.escalation_phases = kEscProfiles[kDifAlertHandlerClassC],
.escalation_phases_len = 2,
.crashdump_escalation_phase = kDifAlertHandlerClassStatePhase1,
},
[kDifAlertHandlerClassD] =
{
.auto_lock_accumulation_counter = kDifToggleDisabled,
.accumulator_threshold = 0,
.irq_deadline_cycles = 1000,
.escalation_phases = kEscProfiles[kDifAlertHandlerClassD],
.escalation_phases_len = 3,
.crashdump_escalation_phase = kDifAlertHandlerClassStatePhase3,
},
};
static const alert_info_t kExpectedInfo[kRoundTotal] = {
[kRound1] =
{
.test_name = "Single class(ClassA)",
.alert_cause = ((uint64_t)1 << kTopEarlgreyAlertIdI2c0FatalFault) |
((uint64_t)1 << kTopEarlgreyAlertIdI2c1FatalFault) |
((uint64_t)1 << kTopEarlgreyAlertIdI2c2FatalFault),
.class_accum_cnt = {3, 0, 0, 0},
.class_esc_state = {kCstatePhase0, kCstateIdle, kCstateIdle,
kCstateIdle},
},
[kRound2] =
{
.test_name = "Multi classes(ClassB,C)",
.alert_cause =
((uint64_t)1 << kTopEarlgreyAlertIdUart0FatalFault) |
((uint64_t)1 << kTopEarlgreyAlertIdUart1FatalFault) |
((uint64_t)1 << kTopEarlgreyAlertIdUart2FatalFault) |
((uint64_t)1 << kTopEarlgreyAlertIdUart3FatalFault) |
((uint64_t)1 << kTopEarlgreyAlertIdOtpCtrlFatalBusIntegError),
.class_accum_cnt = {0, 1, 4, 0},
.class_esc_state = {kCstateIdle, kCstatePhase1, kCstatePhase0,
kCstateIdle},
},
[kRound3] =
{
.test_name = "All classes",
.alert_cause =
((uint64_t)1 << kTopEarlgreyAlertIdRvCoreIbexRecovSwErr) |
((uint64_t)1 << kTopEarlgreyAlertIdUart0FatalFault) |
((uint64_t)1 << kTopEarlgreyAlertIdI2c0FatalFault) |
((uint64_t)1 << kTopEarlgreyAlertIdSpiHost0FatalFault),
.class_accum_cnt = {1, 1, 1, 1},
.class_esc_state = {kCstatePhase0, kCstatePhase1, kCstatePhase0,
kCstatePhase0},
},
};
static alert_info_t alert_info_dump2struct(
const dif_rstmgr_alert_info_dump_segment_t *dump) {
alert_info_t myinfo = {
.class_esc_state = {(cstate_t)(dump[0] & 0x7),
(cstate_t)((dump[0] >> 3) & 0x7),
(cstate_t)((dump[0] >> 6) & 0x7),
(cstate_t)((dump[0] >> 9) & 0x7)},
};
// class_esc_cnt
uint32_t upper, lower;
for (int i = 0; i < ALERT_HANDLER_PARAM_N_CLASSES; ++i) {
lower = dump[i] >> 12;
upper = dump[i + 1] << 20;
myinfo.class_esc_cnt[i] = upper | lower;
}
// class_accum_cnt
myinfo.class_accum_cnt[0] = (uint16_t)(dump[4] >> 12);
lower = dump[4] >> 28;
upper = dump[5] << 8;
myinfo.class_accum_cnt[1] = (uint16_t)(upper | lower);
myinfo.class_accum_cnt[2] = (uint16_t)(dump[5] >> 12);
lower = dump[5] >> 28;
upper = dump[6] << 8;
myinfo.class_accum_cnt[3] = (uint16_t)(upper | lower);
// loc_alert_cause
myinfo.loc_alert_cause = (dump[6] >> 12) & 0x7f;
// alert_cause
// dump[6] layout:
// 31 19,
// |--- alert_cause[12:0]---+
// 18 12,11 0
// +--loc_alert_cause[6:0]--+--class_accum_cnt[3][15:4]--|
//
// the lower 32-bit of the alert cause is made up of:
// upper 13 bits from dump[6]
// lower 19 bits from dump[7]
lower = dump[6] >> 19;
lower = lower | (dump[7] & ((1 << 20) - 1)) << 13;
// the upper 32-bit of the alert cause is made up of:
// upper 13 bits from dump[7]
// lower 16 bits from dump[8]
upper = dump[7] >> 19;
upper = upper | (dump[8] & 0xffff) << 13;
myinfo.alert_cause = (uint64_t)upper << 32 | lower;
return myinfo;
}
static node_t test_node[kTopEarlgreyAlertPeripheralLast];
static void set_extra_alert(volatile uint32_t *set) {
CHECK_DIF_OK(dif_uart_alert_force(&uart0, kDifUartAlertFatalFault));
CHECK_DIF_OK(dif_i2c_alert_force(&i2c0, kDifI2cAlertFatalFault));
CHECK_DIF_OK(dif_spi_host_alert_force(&spi_host, kDifSpiHostAlertFatalFault));
*set = 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_earlgrey_plic_peripheral_t peripheral;
dif_rv_plic_irq_id_t irq_id;
uint32_t irq = 0;
CHECK_DIF_OK(dif_rv_plic_irq_claim(&plic, kPlicTarget, &irq_id));
peripheral = (top_earlgrey_plic_peripheral_t)
top_earlgrey_plic_interrupt_for_peripheral[irq_id];
if (peripheral == kTopEarlgreyPlicPeripheralAonTimerAon) {
irq =
(dif_aon_timer_irq_t)(irq_id -
(dif_rv_plic_irq_id_t)
kTopEarlgreyPlicIrqIdAonTimerAonWkupTimerExpired);
CHECK_DIF_OK(dif_aon_timer_irq_acknowledge(&aon_timer, irq));
} else if (peripheral == kTopEarlgreyPlicPeripheralAlertHandler) {
irq = (irq_id -
(dif_rv_plic_irq_id_t)kTopEarlgreyPlicIrqIdAlertHandlerClassa);
switch (irq) {
case 0: // class a
LOG_INFO("IRQ: class A");
CHECK_DIF_OK(dif_alert_handler_alert_acknowledge(
&alert_handler, kDifI2cAlertFatalFault));
// check classes
dif_alert_handler_class_state_t state;
CHECK_DIF_OK(dif_alert_handler_get_class_state(
&alert_handler, kDifAlertHandlerClassA, &state));
// sw reset for round 1
if (global_test_round == kRound1) {
CHECK_DIF_OK(dif_rstmgr_software_device_reset(&rstmgr));
}
CHECK_DIF_OK(dif_alert_handler_irq_acknowledge(&alert_handler, irq));
break;
case 1:
LOG_INFO("IRQ: class B");
break;
case 2:
LOG_INFO("IRQ: class C");
break;
case 3:
if (global_alert_called == 0) {
set_extra_alert(&global_alert_called);
LOG_INFO("IRQ: class D");
LOG_INFO("IRQ: extra alert called");
}
break;
default:
LOG_FATAL("IRQ: unknown irq %d", 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));
}
static const uint32_t num_prints =
sizeof(((alert_info_t *)0)->alert_cause) / sizeof(uint32_t);
static void print_alert_cause(alert_info_t info) {
for (uint32_t i = 0; i < num_prints; ++i) {
LOG_INFO("alert_cause[%d]: 0x%x", i,
(uint32_t)(info.alert_cause >> (32 * i)));
}
}
/*
* Configure alert for i2c0..i2c2 s.t.
* .alert class = class A
* .escalation phase0,1
* .disable ping timer
*/
static void prgm_alert_handler_round1(void) {
dif_alert_handler_class_t alert_class = kDifAlertHandlerClassA;
for (int i = kTopEarlgreyAlertPeripheralI2c0;
i < kTopEarlgreyAlertPeripheralI2c2 + 1; ++i) {
CHECK_DIF_OK(dif_alert_handler_configure_alert(
&alert_handler, test_node[i].alert, test_node[i].class,
/*enabled=*/kDifToggleEnabled, /*locked=*/kDifToggleEnabled));
}
CHECK_DIF_OK(dif_alert_handler_configure_class(
&alert_handler, alert_class, kConfigProfiles[alert_class],
/*enabled=*/kDifToggleEnabled, /*locked=*/kDifToggleEnabled));
CHECK_DIF_OK(dif_alert_handler_configure_ping_timer(
&alert_handler, 0, /*enabled=*/kDifToggleEnabled,
/*locked=*/kDifToggleEnabled));
}
/*
* Configure alert for uart0..3
* .alert class = class C
* .escalation phase0,1
* Configure alert from aon timer
* .alert class = class B
* .escalation phases 1
*
* Set esc_phases.signal = 0 for all cases to avoid
* watchdog timer freeze.
*/
static void prgm_alert_handler_round2(void) {
dif_alert_handler_class_t alert_classes[] = {kDifAlertHandlerClassC,
kDifAlertHandlerClassB};
dif_alert_handler_class_config_t class_configs[] = {
kConfigProfiles[kDifAlertHandlerClassC],
kConfigProfiles[kDifAlertHandlerClassB]};
for (int i = kTopEarlgreyAlertPeripheralUart0;
i < kTopEarlgreyAlertPeripheralUart3 + 1; ++i) {
CHECK_DIF_OK(dif_alert_handler_configure_alert(
&alert_handler, test_node[i].alert, test_node[i].class,
/*enabled=*/kDifToggleEnabled, /*locked=*/kDifToggleEnabled));
}
CHECK_DIF_OK(dif_alert_handler_configure_alert(
&alert_handler, test_node[kTopEarlgreyAlertPeripheralOtpCtrl].alert,
test_node[kTopEarlgreyAlertPeripheralOtpCtrl].class,
/*enabled=*/kDifToggleEnabled,
/*locked=*/kDifToggleEnabled));
for (int i = 0; i < ARRAYSIZE(alert_classes); ++i) {
CHECK_DIF_OK(dif_alert_handler_configure_class(
&alert_handler, alert_classes[i], class_configs[i],
/*enabled=*/kDifToggleEnabled,
/*locked=*/kDifToggleEnabled));
}
}
/*
* Set I2c0 alert to class a
* spi_host alert to class b
* uart0 alert to class c and
* the rest to class d (including local alert)
*
* For class d, enable 3 phases and escalation reset will be
* triggered at phase2
*/
static void prgm_alert_handler_round3(void) {
// Enable all incoming alerts. This will create ping timeout event
// for all possible alerts and will see the timeout more often.
dif_alert_handler_class_t alert_class;
for (int i = 0; i < ALERT_HANDLER_PARAM_N_ALERTS; ++i) {
if (i == kTopEarlgreyAlertIdSpiHost0FatalFault) {
alert_class = kDifAlertHandlerClassB;
} else if (i == kTopEarlgreyAlertIdUart0FatalFault) {
alert_class = kDifAlertHandlerClassC;
} else if (i == kTopEarlgreyAlertIdI2c0FatalFault) {
alert_class = kDifAlertHandlerClassA;
} else {
alert_class = kDifAlertHandlerClassD;
}
CHECK_DIF_OK(dif_alert_handler_configure_alert(
&alert_handler, i, alert_class, /*enabled=*/kDifToggleEnabled,
/*locked=*/kDifToggleEnabled));
}
dif_alert_handler_class_t alert_classes[] = {
kDifAlertHandlerClassA, kDifAlertHandlerClassB, kDifAlertHandlerClassC,
kDifAlertHandlerClassD};
dif_alert_handler_class_config_t class_d_config =
kConfigProfiles[kDifAlertHandlerClassD];
dif_alert_handler_escalation_phase_t class_d_esc[3];
if (kDeviceType == kDeviceFpgaCw310) {
uint32_t cpu_freq = kClockFreqCpuHz;
uint32_t peri_freq = kClockFreqPeripheralHz;
uint32_t cycles = kUartTxFifoCpuCycles * (cpu_freq / peri_freq);
class_d_esc[0] = kEscProfiles[kDifAlertHandlerClassD][0];
class_d_esc[1] = kEscProfiles[kDifAlertHandlerClassD][1];
class_d_esc[2] = kEscProfiles[kDifAlertHandlerClassD][2];
// we must allow sufficient time for the device to complete uart
class_d_esc[0].duration_cycles = cycles;
class_d_config.escalation_phases = class_d_esc;
}
LOG_INFO("Escalation set to %d cycles",
class_d_config.escalation_phases[0].duration_cycles);
dif_alert_handler_class_config_t class_configs[] = {
kConfigProfiles[kDifAlertHandlerClassA],
kConfigProfiles[kDifAlertHandlerClassB],
kConfigProfiles[kDifAlertHandlerClassC], class_d_config};
for (int i = 0; i < ARRAYSIZE(alert_classes); ++i) {
CHECK_DIF_OK(dif_alert_handler_configure_class(
&alert_handler, alert_classes[i], class_configs[i],
/*enabled=*/kDifToggleEnabled,
/*locked=*/kDifToggleEnabled));
}
CHECK_DIF_OK(dif_alert_handler_configure_ping_timer(
&alert_handler, 1000, /*enabled=*/kDifToggleEnabled,
/*locked=*/kDifToggleEnabled));
CHECK_DIF_OK(dif_rv_core_ibex_alert_force(&rv_core_ibex,
kDifRvCoreIbexAlertRecovSwErr));
}
static void peripheral_init(void) {
CHECK_DIF_OK(dif_spi_host_init(
mmio_region_from_addr(TOP_EARLGREY_SPI_HOST0_BASE_ADDR), &spi_host));
CHECK_DIF_OK(
dif_i2c_init(mmio_region_from_addr(TOP_EARLGREY_I2C0_BASE_ADDR), &i2c0));
CHECK_DIF_OK(
dif_i2c_init(mmio_region_from_addr(TOP_EARLGREY_I2C1_BASE_ADDR), &i2c1));
CHECK_DIF_OK(
dif_i2c_init(mmio_region_from_addr(TOP_EARLGREY_I2C2_BASE_ADDR), &i2c2));
CHECK_DIF_OK(dif_uart_init(
mmio_region_from_addr(TOP_EARLGREY_UART0_BASE_ADDR), &uart0));
CHECK_DIF_OK(dif_uart_init(
mmio_region_from_addr(TOP_EARLGREY_UART1_BASE_ADDR), &uart1));
CHECK_DIF_OK(dif_uart_init(
mmio_region_from_addr(TOP_EARLGREY_UART2_BASE_ADDR), &uart2));
CHECK_DIF_OK(dif_uart_init(
mmio_region_from_addr(TOP_EARLGREY_UART3_BASE_ADDR), &uart3));
CHECK_DIF_OK(dif_otp_ctrl_init(
mmio_region_from_addr(TOP_EARLGREY_OTP_CTRL_CORE_BASE_ADDR), &otp_ctrl));
CHECK_DIF_OK(dif_aon_timer_init(
mmio_region_from_addr(TOP_EARLGREY_AON_TIMER_AON_BASE_ADDR), &aon_timer));
CHECK_DIF_OK(dif_pwrmgr_init(
mmio_region_from_addr(TOP_EARLGREY_PWRMGR_AON_BASE_ADDR), &pwrmgr));
CHECK_DIF_OK(dif_rv_core_ibex_init(
mmio_region_from_addr(TOP_EARLGREY_RV_CORE_IBEX_CFG_BASE_ADDR),
&rv_core_ibex));
// Set pwrmgr reset_en
CHECK_DIF_OK(dif_pwrmgr_set_request_sources(&pwrmgr, kDifPwrmgrReqTypeReset,
kDifPwrmgrResetRequestSourceTwo,
kDifToggleEnabled));
}
static void collect_alert_dump_and_compare(test_round_t round) {
dif_rstmgr_alert_info_dump_segment_t dump[DIF_RSTMGR_ALERT_INFO_MAX_SIZE];
size_t seg_size;
alert_info_t actual_info;
CHECK_DIF_OK(dif_rstmgr_alert_info_dump_read(
&rstmgr, dump, DIF_RSTMGR_ALERT_INFO_MAX_SIZE, &seg_size));
LOG_INFO("Testname: %s DUMP SIZE %d", kExpectedInfo[round].test_name,
seg_size);
for (int i = 0; i < seg_size; i++) {
LOG_INFO("DUMP:%d: 0x%x", i, dump[i]);
}
actual_info = alert_info_dump2struct(dump);
LOG_INFO("observed alert cause:");
print_alert_cause(actual_info);
LOG_INFO("expected alert cause:");
print_alert_cause(kExpectedInfo[round]);
CHECK(kExpectedInfo[round].alert_cause == actual_info.alert_cause);
for (int i = 0; i < ALERT_HANDLER_PARAM_N_CLASSES; ++i) {
CHECK(kExpectedInfo[round].class_accum_cnt[i] ==
actual_info.class_accum_cnt[i],
"alert_info.class_accum_cnt[%d] mismatch exp:0x%x obs:0x%x", i,
kExpectedInfo[round].class_accum_cnt[i],
actual_info.class_accum_cnt[i]);
}
for (int i = 0; i < ALERT_HANDLER_PARAM_N_CLASSES; ++i) {
// added '<' because expected state can be minimum phase but
// depends on simulation, sometimes it captures higher phase.
CHECK(kExpectedInfo[round].class_esc_state[i] <=
actual_info.class_esc_state[i],
"alert_info.class_esc_state[%d] mismatch exp:0x%x obs:0x%x", i,
kExpectedInfo[round].class_esc_state[i],
actual_info.class_esc_state[i]);
}
}
static node_t test_node[kTopEarlgreyAlertPeripheralLast] = {
[kTopEarlgreyAlertPeripheralSpiHost0] =
{
.name = "SPIHOST0",
.alert = kTopEarlgreyAlertIdSpiHost0FatalFault,
.class = kDifAlertHandlerClassB,
},
[kTopEarlgreyAlertPeripheralOtpCtrl] =
{
.name = "OTPCTRL",
.alert = kTopEarlgreyAlertIdOtpCtrlFatalBusIntegError,
.class = kDifAlertHandlerClassB,
},
[kTopEarlgreyAlertPeripheralKmac] =
{
.name = "KMAC",
.alert = kTopEarlgreyAlertIdKmacFatalFaultErr,
.class = kDifAlertHandlerClassB,
},
[kTopEarlgreyAlertPeripheralUart0] =
{
.name = "UART0",
.alert = kTopEarlgreyAlertIdUart0FatalFault,
.class = kDifAlertHandlerClassC,
},
[kTopEarlgreyAlertPeripheralUart1] =
{
.name = "UART1",
.alert = kTopEarlgreyAlertIdUart1FatalFault,
.class = kDifAlertHandlerClassC,
},
[kTopEarlgreyAlertPeripheralUart2] =
{
.name = "UART2",
.alert = kTopEarlgreyAlertIdUart2FatalFault,
.class = kDifAlertHandlerClassC,
},
[kTopEarlgreyAlertPeripheralUart3] =
{
.name = "UART3",
.alert = kTopEarlgreyAlertIdUart3FatalFault,
.class = kDifAlertHandlerClassC,
},
[kTopEarlgreyAlertPeripheralI2c0] =
{
.name = "I2C0",
.alert = kTopEarlgreyAlertIdI2c0FatalFault,
.class = kDifAlertHandlerClassA,
},
[kTopEarlgreyAlertPeripheralI2c1] =
{
.name = "I2C1",
.alert = kTopEarlgreyAlertIdI2c1FatalFault,
.class = kDifAlertHandlerClassA,
},
[kTopEarlgreyAlertPeripheralI2c2] =
{
.name = "I2C2",
.alert = kTopEarlgreyAlertIdI2c2FatalFault,
.class = kDifAlertHandlerClassA,
},
};
bool test_main(void) {
// Enable global and external IRQ at Ibex.
irq_global_ctrl(true);
irq_external_ctrl(true);
CHECK_DIF_OK(dif_rstmgr_init(
mmio_region_from_addr(TOP_EARLGREY_RSTMGR_AON_BASE_ADDR), &rstmgr));
CHECK_DIF_OK(dif_alert_handler_init(
mmio_region_from_addr(TOP_EARLGREY_ALERT_HANDLER_BASE_ADDR),
&alert_handler));
CHECK_DIF_OK(dif_rv_plic_init(
mmio_region_from_addr(TOP_EARLGREY_RV_PLIC_BASE_ADDR), &plic));
peripheral_init();
// Enable all interrupts used in this test.
rv_plic_testutils_irq_range_enable(&plic, kPlicTarget,
kTopEarlgreyPlicIrqIdAlertHandlerClassa,
kTopEarlgreyPlicIrqIdAlertHandlerClassd);
rv_plic_testutils_irq_range_enable(
&plic, kPlicTarget, kTopEarlgreyPlicIrqIdAonTimerAonWkupTimerExpired,
kTopEarlgreyPlicIrqIdAonTimerAonWdogTimerBark);
// enable alert info
CHECK_DIF_OK(dif_rstmgr_alert_info_set_enabled(&rstmgr, kDifToggleEnabled));
// Check if there was a HW reset caused by the escalation.
dif_rstmgr_reset_info_bitfield_t rst_info;
rst_info = rstmgr_testutils_reason_get();
rstmgr_testutils_reason_clear();
LOG_INFO("reset info = 0x%02X", rst_info);
global_alert_called = 0;
// TODO(#13098): Change to equality after #13277 is merged.
if (rst_info & kDifRstmgrResetInfoPor) {
global_test_round = kRound1;
prgm_alert_handler_round1();
CHECK_DIF_OK(dif_i2c_alert_force(&i2c0, kDifI2cAlertFatalFault));
CHECK_DIF_OK(dif_i2c_alert_force(&i2c1, kDifI2cAlertFatalFault));
CHECK_DIF_OK(dif_i2c_alert_force(&i2c2, kDifI2cAlertFatalFault));
CHECK_DIF_OK(dif_alert_handler_irq_set_enabled(
&alert_handler, kDifAlertHandlerIrqClassa, kDifToggleEnabled));
// Give an enough delay until sw rest happens.
busy_spin_micros(kRoundOneDelay);
CHECK(false, "Should have reset before this line");
} else if (rst_info == kDifRstmgrResetInfoSw) {
collect_alert_dump_and_compare(kRound1);
global_test_round = kRound2;
prgm_alert_handler_round2();
// Setup the aon_timer the wdog bark and bite timeouts.
uint64_t bark_cycles =
udiv64_slow(kWdogBarkMicros * kClockFreqAonHz, 1000000, NULL);
uint64_t bite_cycles =
udiv64_slow(kWdogBiteMicros * kClockFreqAonHz, 1000000, NULL);
aon_timer_testutils_watchdog_config(&aon_timer, bark_cycles, bite_cycles,
false);
CHECK_DIF_OK(dif_uart_alert_force(&uart0, kDifUartAlertFatalFault));
CHECK_DIF_OK(dif_uart_alert_force(&uart1, kDifUartAlertFatalFault));
CHECK_DIF_OK(dif_uart_alert_force(&uart2, kDifUartAlertFatalFault));
CHECK_DIF_OK(dif_uart_alert_force(&uart3, kDifUartAlertFatalFault));
CHECK_DIF_OK(dif_otp_ctrl_alert_force(&otp_ctrl,
kDifOtpCtrlAlertFatalBusIntegError));
CHECK_DIF_OK(dif_alert_handler_irq_set_enabled(
&alert_handler, kDifAlertHandlerIrqClassb, kDifToggleEnabled));
CHECK_DIF_OK(dif_alert_handler_irq_set_enabled(
&alert_handler, kDifAlertHandlerIrqClassc, kDifToggleEnabled));
busy_spin_micros(kRoundTwoDelay);
CHECK(false, "Should have reset before this line");
} else if (rst_info == kDifRstmgrResetInfoWatchdog) {
collect_alert_dump_and_compare(kRound2);
// Do not remove this log statement, as it is used to synchronize with the
// DV environment
LOG_INFO("Disable ping timer alert check");
global_test_round = kRound3;
prgm_alert_handler_round3();
CHECK_DIF_OK(dif_alert_handler_irq_set_enabled(
&alert_handler, kDifAlertHandlerIrqClassd, kDifToggleEnabled));
busy_spin_micros(kRoundThreeDelay);
CHECK(false, "Should have reset before this line");
} else if (rst_info == kDifRstmgrResetInfoEscalation) {
collect_alert_dump_and_compare(kRound3);
return true;
} else {
LOG_FATAL("unexpected reset info %d", rst_info);
}
return false;
}