|  | // 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/dif/dif_rv_timer.h" | 
|  | #include "sw/device/lib/irq.h" | 
|  | #include "sw/device/lib/runtime/hart.h" | 
|  | #include "sw/device/lib/runtime/ibex.h" | 
|  | #include "sw/device/lib/runtime/log.h" | 
|  | #include "sw/device/lib/testing/check.h" | 
|  | #include "sw/device/lib/testing/test_framework/ottf.h" | 
|  |  | 
|  | #include "hw/top_earlgrey/sw/autogen/top_earlgrey.h" | 
|  |  | 
|  | static dif_rv_timer_t timer; | 
|  |  | 
|  | // Flag for checking whether the interrupt handler was called. When the handler | 
|  | // is entered, this value *must* be set to false, to catch false positives. | 
|  | // | 
|  | // This variable is volatile, since it may suddenly change from the compiler's | 
|  | // perspective (e.g., due to an interrupt firing). | 
|  | static volatile bool irq_fired = true; | 
|  |  | 
|  | // NOTE: PLIC targets need not line up with hart ids; in the future, we should | 
|  | // generate hart ID constants elsewhere. | 
|  | static const uint32_t kHart = (uint32_t)kTopEarlgreyPlicTargetIbex0; | 
|  | static const uint32_t kComparator = 0; | 
|  |  | 
|  | static const uint64_t kTickFreqHz = 1000 * 1000;  // 1 MHz. | 
|  |  | 
|  | static void test_handler(void) { | 
|  | CHECK(!irq_fired, "Entered IRQ handler, but `irq_fired` was not false!"); | 
|  |  | 
|  | bool irq_flag; | 
|  | CHECK_DIF_OK(dif_rv_timer_irq_is_pending( | 
|  | &timer, kDifRvTimerIrqTimerExpiredHart0Timer0, &irq_flag)); | 
|  | CHECK(irq_flag, "Entered IRQ handler but the expected IRQ flag wasn't set!"); | 
|  |  | 
|  | CHECK_DIF_OK( | 
|  | dif_rv_timer_counter_set_enabled(&timer, kHart, kDifToggleDisabled)); | 
|  | CHECK_DIF_OK(dif_rv_timer_irq_acknowledge( | 
|  | &timer, kDifRvTimerIrqTimerExpiredHart0Timer0)); | 
|  |  | 
|  | irq_fired = true; | 
|  | } | 
|  |  | 
|  | // Override default OTTF timer ISR. | 
|  | void ottf_timer_isr(void) { | 
|  | LOG_INFO("Entering handler_irq_timer()"); | 
|  | test_handler(); | 
|  | LOG_INFO("Exiting handler_irq_timer()"); | 
|  | } | 
|  |  | 
|  | const test_config_t kTestConfig; | 
|  |  | 
|  | bool test_main(void) { | 
|  | irq_global_ctrl(true); | 
|  | irq_timer_ctrl(true); | 
|  |  | 
|  | CHECK_DIF_OK(dif_rv_timer_init( | 
|  | mmio_region_from_addr(TOP_EARLGREY_RV_TIMER_BASE_ADDR), &timer)); | 
|  | CHECK_DIF_OK(dif_rv_timer_reset(&timer)); | 
|  |  | 
|  | dif_rv_timer_tick_params_t tick_params; | 
|  | CHECK_DIF_OK(dif_rv_timer_approximate_tick_params(kClockFreqPeripheralHz, | 
|  | kTickFreqHz, &tick_params)); | 
|  | CHECK_DIF_OK(dif_rv_timer_set_tick_params(&timer, kHart, tick_params)); | 
|  | CHECK_DIF_OK(dif_rv_timer_irq_set_enabled( | 
|  | &timer, kDifRvTimerIrqTimerExpiredHart0Timer0, kDifToggleEnabled)); | 
|  |  | 
|  | uint64_t current_time; | 
|  | // Logs over UART incur a large runtime overhead. To accommodate that, the | 
|  | // timer deadline needs to be large as well. In DV simulations, logs are not | 
|  | // sent over UART, so we can reduce the runtime / sim time with a much shorter | 
|  | // deadline (30 ms vs 100 us). | 
|  | uint64_t kDeadline = | 
|  | (kDeviceType == kDeviceSimDV) ? 100 /* 100 us */ : 30000 /* 30 ms */; | 
|  | CHECK_DIF_OK(dif_rv_timer_counter_read(&timer, kHart, ¤t_time)); | 
|  | LOG_INFO("Current time: %d; timer theshold: %d", (uint32_t)current_time, | 
|  | (uint32_t)(current_time + kDeadline)); | 
|  | CHECK_DIF_OK( | 
|  | dif_rv_timer_arm(&timer, kHart, kComparator, current_time + kDeadline)); | 
|  |  | 
|  | irq_fired = false; | 
|  | CHECK_DIF_OK( | 
|  | dif_rv_timer_counter_set_enabled(&timer, kHart, kDifToggleEnabled)); | 
|  |  | 
|  | LOG_INFO("Waiting..."); | 
|  | while (!irq_fired) { | 
|  | wait_for_interrupt(); | 
|  | } | 
|  |  | 
|  | return true; | 
|  | } |