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