blob: ec11c6b5656d74eb49ba55de6fb81c2d49f99f43 [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/runtime/ibex.h"
#include "sw/device/lib/runtime/irq.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/rand_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.
OTTF_DEFINE_TEST_CONFIG();
/**
* 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;
/**
* Clear event trigger and recoverable 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);
};
enum {
// Counter for event index.
kCounterEventIdx,
// Counter for number of events tested.
kCounterNumTests,
// Max number of events to test per run.
kNumTestsMax = SENSOR_CTRL_PARAM_NUM_ALERT_EVENTS >> 1,
};
static uint32_t get_next_event_to_test(void) {
uint32_t event_idx;
// Reseed so that we don't see the same sequence after each reset.
rand_testutils_reseed();
do {
event_idx = flash_ctrl_testutils_counter_get(kCounterEventIdx);
flash_ctrl_testutils_counter_increment(&flash_ctrl, kCounterEventIdx);
// Drop each event randomly to reduce run time.
} while (rand_testutils_gen32() <= UINT32_MAX >> 1 &&
event_idx < SENSOR_CTRL_PARAM_NUM_ALERT_EVENTS);
return event_idx;
}
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));
// 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);
// Make sure we do not try to test more than half of all available events
// in a single test. Testing too many would just make the run time too
// long.
uint32_t event_idx = get_next_event_to_test();
if (event_idx == SENSOR_CTRL_PARAM_NUM_ALERT_EVENTS ||
flash_ctrl_testutils_counter_get(kCounterNumTests) >= kNumTestsMax) {
LOG_INFO("Tested all events");
return true;
} else {
LOG_INFO("Testing event %d", event_idx);
}
// 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
flash_ctrl_testutils_counter_increment(&flash_ctrl, kCounterNumTests);
// 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 false;
}