blob: 9d75be2a5184a3db82946fb2129aa884d17db365 [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/abs_mmio.h"
#include "sw/device/lib/base/mmio.h"
#include "sw/device/lib/dif/dif_alert_handler.h"
#include "sw/device/lib/dif/dif_flash_ctrl.h"
#include "sw/device/lib/dif/dif_rstmgr.h"
#include "sw/device/lib/dif/dif_rv_plic.h"
#include "sw/device/lib/dif/dif_sensor_ctrl.h"
#include "sw/device/lib/irq.h"
#include "sw/device/lib/runtime/ibex.h"
#include "sw/device/lib/runtime/log.h"
#include "sw/device/lib/testing/flash_ctrl_testutils.h"
#include "sw/device/lib/testing/pwrmgr_testutils.h"
#include "sw/device/lib/testing/test_framework/check.h"
#include "sw/device/lib/testing/test_framework/ottf_main.h"
#include "hw/top_earlgrey/sw/autogen/top_earlgrey.h"
#include "sensor_ctrl_regs.h" // Generated.
/**
* This test checks that incoming ast events can be
* configured as both recoverable and fatal.
* Further, this test checks that recoverable and fatal
* events are able to reach their proper alert_handler
* destination.
*
* Since fatal events do not stop firing once asserted,
* this test performs a self reset after every fatal
* event. In order to keep track of how far the test
* has advanced, a non-volatile counter in flash is
* used to track current progress.
*/
static dif_rstmgr_t rstmgr;
static dif_flash_ctrl_state_t flash_ctrl;
static dif_sensor_ctrl_t sensor_ctrl;
static dif_alert_handler_t alert_handler;
/**
* The events_vector stores the tested events in bit strike fashion.
* This means at first the flash word is 0xFFFF_FFFF.
* As events are tested, they are marked as 0 in the vector,
* so we go from 0xFFFF_FFFF -> 0xFFFF_FFFE -> 0xFFFF_FFFC
*/
__attribute__((section(".non_volatile_scratch")))
const volatile uint32_t events_vector = -1;
/**
* Extracts current event id from the bit-strike counter.
*/
static uint32_t event_to_test(void) {
uint32_t addr = (uint32_t)(&events_vector);
uint32_t val = abs_mmio_read32(addr);
for (size_t i = 0; i < 32; ++i) {
if (val >> i & 0x1) {
return i;
}
}
return -1;
};
/**
* Increment flash bit-strike counter.
*/
static bool incr_flash_cnt(uint32_t tested_idx) {
uint32_t addr = (uint32_t)(&events_vector);
// set the tested bit to 0
uint32_t val = abs_mmio_read32(addr) & ~(1 << tested_idx);
// program the word into flash
return flash_ctrl_testutils_write(&flash_ctrl, addr, 0, &val,
kDifFlashCtrlPartitionTypeData, 1);
};
/**
* Clear event trigger and recoverble status.
*/
static void clear_event(uint32_t idx, dif_toggle_t fatal) {
CHECK_DIF_OK(dif_sensor_ctrl_set_ast_event_trigger(&sensor_ctrl, idx,
kDifToggleDisabled));
if (!dif_toggle_to_bool(fatal)) {
CHECK_DIF_OK(dif_sensor_ctrl_clear_recov_event(&sensor_ctrl, idx));
}
}
static uint32_t get_events(dif_toggle_t fatal) {
dif_sensor_ctrl_events_t events = 0;
if (dif_toggle_to_bool(fatal)) {
CHECK_DIF_OK(dif_sensor_ctrl_get_fatal_events(&sensor_ctrl, &events));
} else {
CHECK_DIF_OK(dif_sensor_ctrl_get_recov_events(&sensor_ctrl, &events));
}
return events;
}
/**
* Check alert cause registers are correctly set
*/
static void check_alert_state(dif_toggle_t fatal) {
bool fatal_cause = false;
bool recov_cause = false;
CHECK_DIF_OK(dif_alert_handler_alert_is_cause(
&alert_handler, kTopEarlgreyAlertIdSensorCtrlFatalAlert, &fatal_cause));
CHECK_DIF_OK(dif_alert_handler_alert_is_cause(
&alert_handler, kTopEarlgreyAlertIdSensorCtrlRecovAlert, &recov_cause));
if (dif_toggle_to_bool(fatal)) {
CHECK(fatal_cause & !recov_cause,
"Fatal alert not correctly observed in alert handler");
} else {
CHECK(recov_cause & !fatal_cause,
"Recov alert not correctly observed in alert handler");
}
CHECK_DIF_OK(dif_alert_handler_alert_acknowledge(
&alert_handler, kTopEarlgreyAlertIdSensorCtrlRecovAlert));
CHECK_DIF_OK(dif_alert_handler_alert_acknowledge(
&alert_handler, kTopEarlgreyAlertIdSensorCtrlFatalAlert));
};
/**
* First configure fatality of the desired event.
* Then trigger the event from sensor_ctrl to ast.
* Next poll for setting of correct events inside sensor_ctrl status.
* When a recoverable event is triggerd, make sure only recoverable
* status is seen, likewise for fatal events.
* Finally, check for correct capture of cause in alert handler.
*/
static void test_event(uint32_t idx, dif_toggle_t fatal) {
// Configure event fatality
CHECK_DIF_OK(dif_sensor_ctrl_set_alert_fatal(&sensor_ctrl, idx, fatal));
// Trigger event
CHECK_DIF_OK(dif_sensor_ctrl_set_ast_event_trigger(&sensor_ctrl, idx,
kDifToggleEnabled));
// wait for events to set
IBEX_SPIN_FOR(get_events(fatal) > 0, 1);
// Check for the event in ast sensor_ctrl
// if the event is not set, error
CHECK(((get_events(fatal) >> idx) & 0x1) == 1, "Event %d not observed in AST",
idx);
// check the opposite fatality setting, should not be set
CHECK(((get_events(!fatal) >> idx) & 0x1) == 0,
"Event %d observed in AST when it should not be", idx);
// clear event trigger
clear_event(idx, fatal);
// check whether alert handler captured the event
check_alert_state(fatal);
};
bool test_main(void) {
// Initialize flash_ctrl
CHECK_DIF_OK(dif_flash_ctrl_init_state(
&flash_ctrl,
mmio_region_from_addr(TOP_EARLGREY_FLASH_CTRL_CORE_BASE_ADDR)));
// Initialize sensor_ctrl
CHECK_DIF_OK(dif_sensor_ctrl_init(
mmio_region_from_addr(TOP_EARLGREY_SENSOR_CTRL_BASE_ADDR), &sensor_ctrl));
// Initialize alert_handler
CHECK_DIF_OK(dif_alert_handler_init(
mmio_region_from_addr(TOP_EARLGREY_ALERT_HANDLER_BASE_ADDR),
&alert_handler));
CHECK_DIF_OK(dif_rstmgr_init(
mmio_region_from_addr(TOP_EARLGREY_RSTMGR_AON_BASE_ADDR), &rstmgr));
// Enable both recoverable and fatal alerts
CHECK_DIF_OK(dif_alert_handler_configure_alert(
&alert_handler, kTopEarlgreyAlertIdSensorCtrlRecovAlert,
kDifAlertHandlerClassA, kDifToggleEnabled, kDifToggleEnabled));
CHECK_DIF_OK(dif_alert_handler_configure_alert(
&alert_handler, kTopEarlgreyAlertIdSensorCtrlFatalAlert,
kDifAlertHandlerClassA, kDifToggleEnabled, kDifToggleEnabled));
// First check the flash stored value
uint32_t event_idx = event_to_test();
// Capture the number of events to test
uint32_t sensor_ctrl_events = SENSOR_CTRL_PARAM_NUM_ALERT_EVENTS;
if (event_idx == sensor_ctrl_events) {
LOG_INFO("Tested all events");
return true;
} else {
LOG_INFO("Testing event %d", event_idx);
}
// 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);
// test recoverable event
test_event(event_idx, /*fatal*/ kDifToggleDisabled);
// test fatal event
test_event(event_idx, /*fatal*/ kDifToggleEnabled);
// increment flash counter to know where we are
if (incr_flash_cnt(event_idx)) {
LOG_ERROR("Error when incrementing flash counter");
}
// Now request system to reset and test again
LOG_INFO("Rebooting system");
CHECK_DIF_OK(dif_rstmgr_software_device_reset(&rstmgr));
wait_for_interrupt();
return true;
}