| /* |
| * Copyright (c) 2017-2018 Oticon A/S |
| * |
| * SPDX-License-Identifier: Apache-2.0 |
| */ |
| #include "NRF_HW_model_top.h" |
| #include "NRF_HWLowL.h" |
| #include "bs_tracing.h" |
| #include "bs_types.h" |
| #include "bs_utils.h" |
| |
| /* Note: All timers are relative to hw_time and NOT to 'now' */ |
| extern bs_time_t timer_nrf_main_timer; |
| |
| /* The events priorities are as in this list from top to bottom |
| * Priority being which timer executes first if several trigger at the same |
| * instant |
| */ |
| static enum { |
| NRF_HW_MAIN_TIMER = 0, |
| NUMBER_OF_TIMERS, |
| NONE |
| } next_timer_index = NONE; |
| |
| static bs_time_t *Timer_list[NUMBER_OF_TIMERS] = { |
| &timer_nrf_main_timer, |
| }; |
| static bs_time_t next_timer_time = TIME_NEVER; |
| |
| /* |
| * Current absolute time of this device, as the device knows it. |
| * It is never reset: |
| */ |
| static bs_time_t now; |
| /* Current time the HW of this device things it is */ |
| static bs_time_t hw_time; |
| /* |
| * Offset between the current absolute time of the device and the HW time |
| * That is, the absolute time when the HW_time got reset |
| */ |
| static bs_time_t hw_time_delta; |
| |
| /* Last time we synchronized with the bsim PHY, in device abs time */ |
| static bs_time_t last_bsim_phy_sync_time; |
| |
| #define BSIM_DEFAULT_PHY_MAX_RESYNC_OFFSET 1000000 |
| /* At least every second we will inform the simulator about our timing */ |
| static bs_time_t max_resync_offset = BSIM_DEFAULT_PHY_MAX_RESYNC_OFFSET; |
| |
| /** |
| * Set the maximum amount of time the device will spend without talking |
| * (synching) with the phy. |
| * This does not change the functional behavior of the Zephyr code or of the |
| * radio emulation, and it is only relevant if special test code running in the |
| * device interacts behind the scenes with other devices test code. |
| * Setting for example a value of 5ms will ensure that this device time |
| * will never be more than 5ms away from the phy. Setting it in all devices |
| * to 5ms would then ensure no device time is farther apart than 5ms from any |
| * other. |
| * |
| * Note that setting low values has a performance penalty. |
| */ |
| void tm_set_phy_max_resync_offset(bs_time_t offset_in_us) |
| { |
| max_resync_offset = offset_in_us; |
| } |
| |
| /** |
| * Return the absolute current time (no HW model except the RADIO |
| * should look into this) |
| */ |
| bs_time_t tm_get_abs_time(void) |
| { |
| return now; |
| } |
| |
| /** |
| * Return the current HW time |
| */ |
| bs_time_t tm_get_hw_time(void) |
| { |
| return hw_time; |
| } |
| |
| bs_time_t posix_get_hw_cycle(void) |
| { |
| return tm_get_hw_time(); |
| } |
| |
| /** |
| * Reset the HW time |
| */ |
| static void tm_reset_hw_time(void) |
| { |
| hw_time = 0; |
| hw_time_delta = now; |
| if (now != 0) { |
| bs_trace_error_line("Reset not supposed to happen after " |
| "initialization\n"); |
| } |
| } |
| |
| /** |
| * Update the current hw_time value given the absolute time |
| */ |
| INLINE void tm_update_HW_time(void) |
| { |
| hw_time = now - hw_time_delta; |
| } |
| |
| bs_time_t tm_get_hw_time_from_abs_time(bs_time_t abstime) |
| { |
| if (abstime == TIME_NEVER) { |
| return TIME_NEVER; |
| } |
| return abstime - hw_time_delta; |
| } |
| |
| /* |
| * Reset the HW time |
| */ |
| void tm_reset_hw_times(void) |
| { |
| tm_reset_hw_time(); |
| } |
| |
| /** |
| * Advance the internal time values of this device until time |
| */ |
| static void tm_sleep_until_abs_time(bs_time_t time) |
| { |
| if (time >= now) { |
| /* |
| * Ensure that at least we sync with the phy |
| * every max_resync_offset |
| */ |
| if (time > last_bsim_phy_sync_time + max_resync_offset) { |
| hwll_sync_time_with_phy(time); |
| last_bsim_phy_sync_time = time; |
| } |
| now = time; |
| } else { |
| /* LCOV_EXCL_START */ |
| bs_trace_warning_manual_time_line(now, "next_time_time " |
| "corrupted (%"PRItime"<= %"PRItime", timer idx=%i)\n", |
| time, now, next_timer_index); |
| /* LCOV_EXCL_STOP */ |
| } |
| tm_update_HW_time(); |
| } |
| |
| /** |
| * Keep track of the last time we synchronized the time with the scheduler |
| */ |
| void tm_update_last_phy_sync_time(bs_time_t abs_time) |
| { |
| last_bsim_phy_sync_time = abs_time; |
| } |
| |
| /** |
| * Advance the internal time values of this device |
| * until the HW time reaches hw_time |
| */ |
| static void tm_sleep_until_hw_time(bs_time_t hw_time) |
| { |
| bs_time_t next_time = TIME_NEVER; |
| |
| if (hw_time != TIME_NEVER) { |
| next_time = hw_time + hw_time_delta; |
| } |
| tm_sleep_until_abs_time(next_time); |
| } |
| |
| /** |
| * Look into all timers and update next_timer accordingly |
| * To be called each time a "timed process" updates its timer |
| */ |
| void tm_find_next_timer_to_trigger(void) |
| { |
| next_timer_time = *Timer_list[0]; |
| next_timer_index = 0; |
| |
| for (uint i = 1; i < NUMBER_OF_TIMERS ; i++) { |
| if (next_timer_time > *Timer_list[i]) { |
| next_timer_time = *Timer_list[i]; |
| next_timer_index = i; |
| } |
| } |
| } |
| |
| bs_time_t tm_get_next_timer_abstime(void) |
| { |
| return next_timer_time + hw_time_delta; |
| } |
| |
| bs_time_t tm_hw_time_to_abs_time(bs_time_t hwtime) |
| { |
| if (hwtime == TIME_NEVER) { |
| return TIME_NEVER; |
| } |
| return hwtime + hw_time_delta; |
| } |
| |
| bs_time_t tm_abs_time_to_hw_time(bs_time_t abstime) |
| { |
| if (abstime == TIME_NEVER) { |
| return TIME_NEVER; |
| } |
| return abstime - hw_time_delta; |
| } |
| |
| /** |
| * Run ahead: Run the HW models and advance time as needed |
| * Note that this function does not return |
| */ |
| void tm_run_forever(void) |
| { |
| while (1) { |
| tm_sleep_until_hw_time(next_timer_time); |
| switch (next_timer_index) { |
| case NRF_HW_MAIN_TIMER: |
| nrf_hw_some_timer_reached(); |
| break; |
| default: |
| bs_trace_error_time_line("next_timer_index " |
| "corrupted\n"); |
| break; |
| } |
| tm_find_next_timer_to_trigger(); |
| } |
| } |