diff --git a/drivers/timer/nrf_rtc_timer.c b/drivers/timer/nrf_rtc_timer.c index 5328e87e2ba..9b52f6600d9 100644 --- a/drivers/timer/nrf_rtc_timer.c +++ b/drivers/timer/nrf_rtc_timer.c @@ -13,6 +13,8 @@ #include #include #include +#include + #define EXT_CHAN_COUNT CONFIG_NRF_RTC_TIMER_USER_CHAN_COUNT #define CHAN_COUNT (EXT_CHAN_COUNT + 1) @@ -24,8 +26,7 @@ BUILD_ASSERT(CHAN_COUNT <= RTC_CH_COUNT, "Not enough compare channels"); -#define COUNTER_BIT_WIDTH 24U -#define COUNTER_SPAN BIT(COUNTER_BIT_WIDTH) +#define COUNTER_SPAN BIT(24) #define COUNTER_MAX (COUNTER_SPAN - 1U) #define COUNTER_HALF_SPAN (COUNTER_SPAN / 2U) #define CYC_PER_TICK (sys_clock_hw_cycles_per_sec() \ @@ -33,25 +34,18 @@ BUILD_ASSERT(CHAN_COUNT <= RTC_CH_COUNT, "Not enough compare channels"); #define MAX_TICKS ((COUNTER_HALF_SPAN - CYC_PER_TICK) / CYC_PER_TICK) #define MAX_CYCLES (MAX_TICKS * CYC_PER_TICK) -#define OVERFLOW_RISK_RANGE_END (COUNTER_SPAN / 16) -#define ANCHOR_RANGE_START (COUNTER_SPAN / 8) -#define ANCHOR_RANGE_END (7 * COUNTER_SPAN / 8) -#define TARGET_TIME_INVALID (UINT64_MAX) +static struct k_spinlock lock; -static volatile uint32_t overflow_cnt; -static volatile uint64_t anchor; -static uint64_t last_count; +static uint32_t last_count; struct z_nrf_rtc_timer_chan_data { z_nrf_rtc_timer_compare_handler_t callback; void *user_context; - volatile uint64_t target_time; }; static struct z_nrf_rtc_timer_chan_data cc_data[CHAN_COUNT]; static atomic_t int_mask; static atomic_t alloc_mask; -static atomic_t force_isr_mask; static uint32_t counter_sub(uint32_t a, uint32_t b) { @@ -88,33 +82,9 @@ static uint32_t counter(void) return nrf_rtc_counter_get(RTC); } -static uint32_t absolute_time_to_cc(uint64_t absolute_time) +uint32_t z_nrf_rtc_timer_read(void) { - /* 24 least significant bits represent target CC value */ - return absolute_time & COUNTER_MAX; -} - -static uint32_t full_int_lock(void) -{ - uint32_t mcu_critical_state; - - if (IS_ENABLED(CONFIG_ZERO_LATENCY_IRQS)) { - mcu_critical_state = __get_PRIMASK(); - __disable_irq(); - } else { - mcu_critical_state = irq_lock(); - } - - return mcu_critical_state; -} - -static void full_int_unlock(uint32_t mcu_critical_state) -{ - if (IS_ENABLED(CONFIG_ZERO_LATENCY_IRQS)) { - __set_PRIMASK(mcu_critical_state); - } else { - irq_unlock(mcu_critical_state); - } + return nrf_rtc_counter_get(RTC); } uint32_t z_nrf_rtc_timer_compare_evt_address_get(int32_t chan) @@ -123,42 +93,25 @@ uint32_t z_nrf_rtc_timer_compare_evt_address_get(int32_t chan) return nrf_rtc_event_address_get(RTC, nrf_rtc_compare_event_get(chan)); } -static bool compare_int_lock(int32_t chan) -{ - atomic_val_t prev = atomic_and(&int_mask, ~BIT(chan)); - - nrf_rtc_int_disable(RTC, RTC_CHANNEL_INT_MASK(chan)); - - __DMB(); - __ISB(); - - return prev & BIT(chan); -} - - bool z_nrf_rtc_timer_compare_int_lock(int32_t chan) { __ASSERT_NO_MSG(chan && chan < CHAN_COUNT); - return compare_int_lock(chan); -} + atomic_val_t prev = atomic_and(&int_mask, ~BIT(chan)); -static void compare_int_unlock(int32_t chan, bool key) -{ - if (key) { - atomic_or(&int_mask, BIT(chan)); - nrf_rtc_int_enable(RTC, RTC_CHANNEL_INT_MASK(chan)); - if (atomic_get(&force_isr_mask) & BIT(chan)) { - NVIC_SetPendingIRQ(RTC_IRQn); - } - } + nrf_rtc_int_disable(RTC, RTC_CHANNEL_INT_MASK(chan)); + + return prev & BIT(chan); } void z_nrf_rtc_timer_compare_int_unlock(int32_t chan, bool key) { __ASSERT_NO_MSG(chan && chan < CHAN_COUNT); - compare_int_unlock(chan, key); + if (key) { + atomic_or(&int_mask, BIT(chan)); + nrf_rtc_int_enable(RTC, RTC_CHANNEL_INT_MASK(chan)); + } } uint32_t z_nrf_rtc_timer_compare_read(int32_t chan) @@ -168,48 +121,41 @@ uint32_t z_nrf_rtc_timer_compare_read(int32_t chan) return nrf_rtc_cc_get(RTC, chan); } -uint64_t z_nrf_rtc_timer_get_ticks(k_timeout_t t) +int z_nrf_rtc_timer_get_ticks(k_timeout_t t) { - uint64_t curr_time; + uint32_t curr_count; int64_t curr_tick; int64_t result; int64_t abs_ticks; do { - curr_time = z_nrf_rtc_timer_read(); + curr_count = counter(); curr_tick = sys_clock_tick_get(); - } while (curr_time != z_nrf_rtc_timer_read()); + } while (curr_count != counter()); abs_ticks = Z_TICK_ABS(t.ticks); if (abs_ticks < 0) { /* relative timeout */ - return (t.ticks > COUNTER_SPAN) ? - -EINVAL : (curr_time + t.ticks); + return (t.ticks > COUNTER_HALF_SPAN) ? + -EINVAL : ((curr_count + t.ticks) & COUNTER_MAX); } /* absolute timeout */ result = abs_ticks - curr_tick; - if (result > COUNTER_SPAN) { + if ((result > COUNTER_HALF_SPAN) || + (result < -(int64_t)COUNTER_HALF_SPAN)) { return -EINVAL; } - return curr_time + result; + return (curr_count + result) & COUNTER_MAX; } -/** @brief Function safely sets absolute alarm. - * - * It assumes that provided value is less than COUNTER_HALF_SPAN from now. - * It detects late setting and also handle +1 cycle case. - * - * @param[in] chan A channel for which a new CC value is to be set. - * - * @param[in] abs_val An absolute value of CC register to be set. - * - * @returns CC value that was actually set. It is equal to @p abs_val or - * shifted ahead if @p abs_val was too near in the future (+1 case). +/* Function safely sets absolute alarm. It assumes that provided value is + * less than COUNTER_HALF_SPAN from now. It detects late setting and also + * handle +1 cycle case. */ -static uint32_t set_absolute_alarm(int32_t chan, uint32_t abs_val) +static void set_absolute_alarm(int32_t chan, uint32_t abs_val) { uint32_t now; uint32_t now2; @@ -233,6 +179,7 @@ static uint32_t set_absolute_alarm(int32_t chan, uint32_t abs_val) k_busy_wait(19); } + /* If requested cc_val is in the past or next tick, set to 2 * ticks from now. RTC may not generate event if CC is set for * 1 tick from now. @@ -255,144 +202,39 @@ static uint32_t set_absolute_alarm(int32_t chan, uint32_t abs_val) */ } while ((now2 != now) && (counter_sub(cc_val, now2 + 2) > COUNTER_HALF_SPAN)); - - return cc_val; } -static int compare_set_nolocks(int32_t chan, uint64_t target_time, +static void compare_set(int32_t chan, uint32_t cc_value, z_nrf_rtc_timer_compare_handler_t handler, void *user_data) { - int ret = 0; - uint32_t cc_value = absolute_time_to_cc(target_time); - uint64_t curr_time = z_nrf_rtc_timer_read(); - - if (curr_time < target_time) { - if (target_time - curr_time > COUNTER_SPAN) { - /* Target time is too distant. */ - return -EINVAL; - } - - if (target_time != cc_data[chan].target_time) { - /* Target time is valid and is different than currently set. - * Set CC value. - */ - uint32_t cc_set = set_absolute_alarm(chan, cc_value); - - target_time += counter_sub(cc_set, cc_value); - } - } else { - /* Force ISR handling when exiting from critical section. */ - atomic_or(&force_isr_mask, BIT(chan)); - } - - cc_data[chan].target_time = target_time; cc_data[chan].callback = handler; cc_data[chan].user_context = user_data; - return ret; + set_absolute_alarm(chan, cc_value); } -static int compare_set(int32_t chan, uint64_t target_time, - z_nrf_rtc_timer_compare_handler_t handler, - void *user_data) -{ - bool key; - - key = compare_int_lock(chan); - - int ret = compare_set_nolocks(chan, target_time, handler, user_data); - - compare_int_unlock(chan, key); - - return ret; -} - -int z_nrf_rtc_timer_set(int32_t chan, uint64_t target_time, - z_nrf_rtc_timer_compare_handler_t handler, - void *user_data) +void z_nrf_rtc_timer_compare_set(int32_t chan, uint32_t cc_value, + z_nrf_rtc_timer_compare_handler_t handler, + void *user_data) { __ASSERT_NO_MSG(chan && chan < CHAN_COUNT); - return compare_set(chan, target_time, handler, user_data); -} + bool key = z_nrf_rtc_timer_compare_int_lock(chan); -void z_nrf_rtc_timer_abort(int32_t chan) -{ - __ASSERT_NO_MSG(chan && chan < CHAN_COUNT); + compare_set(chan, cc_value, handler, user_data); - bool key = compare_int_lock(chan); - - cc_data[chan].target_time = TARGET_TIME_INVALID; - event_clear(chan); - event_disable(chan); - (void)atomic_and(&force_isr_mask, ~BIT(chan)); - - compare_int_unlock(chan, key); -} - -uint64_t z_nrf_rtc_timer_read(void) -{ - uint64_t val = ((uint64_t)overflow_cnt) << COUNTER_BIT_WIDTH; - - __DMB(); - - uint32_t cntr = counter(); - - val += cntr; - - if (cntr < OVERFLOW_RISK_RANGE_END) { - /* `overflow_cnt` can have incorrect value due to still unhandled overflow or - * due to possibility that this code preempted overflow interrupt before final write - * of `overflow_cnt`. Update of `anchor` occurs far in time from this moment, so - * `anchor` is considered valid and stable. Because of this timing there is no risk - * of incorrect `anchor` value caused by non-atomic read of 64-bit `anchor`. - */ - if (val < anchor) { - /* Unhandled overflow, detected, let's add correction */ - val += COUNTER_SPAN; - } - } else { - /* `overflow_cnt` is considered valid and stable in this range, no need to - * check validity using `anchor` - */ - } - - return val; -} - -static inline bool in_anchor_range(uint32_t cc_value) -{ - return (cc_value >= ANCHOR_RANGE_START) && (cc_value < ANCHOR_RANGE_END); -} - -static inline bool anchor_update(uint32_t cc_value) -{ - /* Update anchor when far from overflow */ - if (in_anchor_range(cc_value)) { - /* In this range `overflow_cnt` is considered valid and stable. - * Write of 64-bit `anchor` is non atomic. However it happens - * far in time from the moment the `anchor` is read in - * `z_nrf_rtc_timer_read`. - */ - anchor = (((uint64_t)overflow_cnt) << COUNTER_BIT_WIDTH) + cc_value; - return true; - } - - return false; + z_nrf_rtc_timer_compare_int_unlock(chan, key); } static void sys_clock_timeout_handler(int32_t chan, - uint64_t expire_time, + uint32_t cc_value, void *user_data) { - uint32_t cc_value = absolute_time_to_cc(expire_time); - uint64_t dticks = (expire_time - last_count) / CYC_PER_TICK; + uint32_t dticks = counter_sub(cc_value, last_count) / CYC_PER_TICK; last_count += dticks * CYC_PER_TICK; - bool anchor_updated = anchor_update(cc_value); - if (!IS_ENABLED(CONFIG_TICKLESS_KERNEL)) { /* protection is not needed because we are in the RTC interrupt * so it won't get preempted by the interrupt. @@ -402,80 +244,7 @@ static void sys_clock_timeout_handler(int32_t chan, } sys_clock_announce(IS_ENABLED(CONFIG_TICKLESS_KERNEL) ? - (int32_t)dticks : (dticks > 0)); - - if (cc_value == get_comparator(chan)) { - /* New value was not set. Set something that can update anchor. - * If anchor was updated we can enable same CC value to trigger - * interrupt after full cycle. Else set event in anchor update - * range. Since anchor was not updated we know that it's very - * far from mid point so setting is done without any protection. - */ - if (!anchor_updated) { - set_comparator(chan, COUNTER_HALF_SPAN); - } - event_enable(chan); - } -} - -static bool channel_processing_check_and_clear(int32_t chan) -{ - bool result = false; - - uint32_t mcu_critical_state = full_int_lock(); - - if (nrf_rtc_int_enable_check(RTC, RTC_CHANNEL_INT_MASK(chan))) { - /* The processing of channel can be caused by CC match - * or be forced. - */ - result = atomic_and(&force_isr_mask, ~BIT(chan)) || - nrf_rtc_event_check(RTC, RTC_CHANNEL_EVENT_ADDR(chan)); - - if (result) { - event_clear(chan); - } - } - - full_int_unlock(mcu_critical_state); - - return result; -} - -static void process_channel(int32_t chan) -{ - if (channel_processing_check_and_clear(chan)) { - void *user_context; - uint32_t mcu_critical_state; - uint64_t curr_time; - uint64_t expire_time; - z_nrf_rtc_timer_compare_handler_t handler = NULL; - - curr_time = z_nrf_rtc_timer_read(); - - /* This critical section is used to provide atomic access to - * cc_data structure and prevent higher priority contexts - * (including ZLIs) from overwriting it. - */ - mcu_critical_state = full_int_lock(); - - /* If target_time is in the past or is equal to current time - * value, execute the handler. - */ - expire_time = cc_data[chan].target_time; - if (curr_time >= expire_time) { - handler = cc_data[chan].callback; - user_context = cc_data[chan].user_context; - cc_data[chan].callback = NULL; - cc_data[chan].target_time = TARGET_TIME_INVALID; - event_disable(chan); - } - - full_int_unlock(mcu_critical_state); - - if (handler) { - handler(chan, expire_time, user_context); - } - } + dticks : (dticks > 0)); } /* Note: this function has public linkage, and MUST have this @@ -490,14 +259,34 @@ void rtc_nrf_isr(const void *arg) { ARG_UNUSED(arg); - if (nrf_rtc_int_enable_check(RTC, NRF_RTC_INT_OVERFLOW_MASK) && - nrf_rtc_event_check(RTC, NRF_RTC_EVENT_OVERFLOW)) { - nrf_rtc_event_clear(RTC, NRF_RTC_EVENT_OVERFLOW); - overflow_cnt++; - } - for (int32_t chan = 0; chan < CHAN_COUNT; chan++) { - process_channel(chan); + if (nrf_rtc_int_enable_check(RTC, RTC_CHANNEL_INT_MASK(chan)) && + nrf_rtc_event_check(RTC, RTC_CHANNEL_EVENT_ADDR(chan))) { + uint32_t cc_val; + uint32_t now; + z_nrf_rtc_timer_compare_handler_t handler; + + event_clear(chan); + event_disable(chan); + cc_val = get_comparator(chan); + now = counter(); + + /* Higher priority interrupt may already changed cc_val + * which now points to the future. In that case return + * current counter value. It is less precise than + * returning exact CC value but this one is already lost. + */ + if (counter_sub(now, cc_val) > COUNTER_HALF_SPAN) { + cc_val = now; + } + + handler = cc_data[chan].callback; + cc_data[chan].callback = NULL; + if (handler) { + handler(chan, cc_val, + cc_data[chan].user_context); + } + } } } @@ -523,7 +312,6 @@ void z_nrf_rtc_timer_chan_free(int32_t chan) atomic_or(&alloc_mask, BIT(chan)); } - void sys_clock_set_timeout(int32_t ticks, bool idle) { ARG_UNUSED(idle); @@ -536,7 +324,7 @@ void sys_clock_set_timeout(int32_t ticks, bool idle) ticks = (ticks == K_TICKS_FOREVER) ? MAX_TICKS : ticks; ticks = CLAMP(ticks - 1, 0, (int32_t)MAX_TICKS); - uint32_t unannounced = z_nrf_rtc_timer_read() - last_count; + uint32_t unannounced = counter_sub(counter(), last_count); /* If we haven't announced for more than half the 24-bit wrap * duration, then force an announce to avoid loss of a wrap @@ -561,9 +349,8 @@ void sys_clock_set_timeout(int32_t ticks, bool idle) cyc = MAX_CYCLES; } - uint64_t target_time = cyc + last_count; - - compare_set(0, target_time, sys_clock_timeout_handler, NULL); + cyc += last_count; + compare_set(0, cyc, sys_clock_timeout_handler, NULL); } uint32_t sys_clock_elapsed(void) @@ -572,12 +359,16 @@ uint32_t sys_clock_elapsed(void) return 0; } - return (z_nrf_rtc_timer_read() - last_count) / CYC_PER_TICK; + return counter_sub(counter(), last_count) / CYC_PER_TICK; } uint32_t sys_clock_cycle_get_32(void) { - return (uint32_t)z_nrf_rtc_timer_read(); + k_spinlock_key_t key = k_spin_lock(&lock); + uint32_t ret = counter_sub(counter(), last_count) + last_count; + + k_spin_unlock(&lock, key); + return ret; } static int sys_clock_driver_init(const struct device *dev) @@ -593,12 +384,9 @@ static int sys_clock_driver_init(const struct device *dev) /* TODO: replace with counter driver to access RTC */ nrf_rtc_prescaler_set(RTC, 0); for (int32_t chan = 0; chan < CHAN_COUNT; chan++) { - cc_data[chan].target_time = TARGET_TIME_INVALID; nrf_rtc_int_enable(RTC, RTC_CHANNEL_INT_MASK(chan)); } - nrf_rtc_int_enable(RTC, NRF_RTC_INT_OVERFLOW_MASK); - NVIC_ClearPendingIRQ(RTC_IRQn); IRQ_CONNECT(RTC_IRQn, DT_IRQ(DT_NODELABEL(RTC_LABEL), priority), @@ -613,12 +401,6 @@ static int sys_clock_driver_init(const struct device *dev) alloc_mask = BIT_MASK(EXT_CHAN_COUNT) << 1; } - uint32_t initial_timeout = IS_ENABLED(CONFIG_TICKLESS_KERNEL) ? - (COUNTER_HALF_SPAN - 1) : - (counter() + CYC_PER_TICK); - - compare_set(0, initial_timeout, sys_clock_timeout_handler, NULL); - if (!IS_ENABLED(CONFIG_TICKLESS_KERNEL)) { compare_set(0, counter() + CYC_PER_TICK, sys_clock_timeout_handler, NULL); diff --git a/include/drivers/timer/nrf_rtc_timer.h b/include/drivers/timer/nrf_rtc_timer.h index 1a0999299a2..f4b502edffc 100644 --- a/include/drivers/timer/nrf_rtc_timer.h +++ b/include/drivers/timer/nrf_rtc_timer.h @@ -11,25 +11,8 @@ extern "C" { #endif -/** @brief Maximum allowed time span that is considered to be in the future. - */ -#define NRF_RTC_TIMER_MAX_SCHEDULE_SPAN BIT(23) - -/** @brief RTC timer compare event handler. - * - * Called from RTC ISR context when processing a compare event. - * - * @param id Compare channel ID. - * - * @param expire_time An actual absolute expiration time set for a compare - * channel. It can differ from the requested target time - * and the difference can be used to determine whether the - * time set was delayed. - * - * @param user_data Pointer to a user context data. - */ typedef void (*z_nrf_rtc_timer_compare_handler_t)(int32_t id, - uint64_t expire_time, + uint32_t cc_value, void *user_data); /** @brief Allocate RTC compare channel. @@ -47,11 +30,11 @@ int32_t z_nrf_rtc_timer_chan_alloc(void); */ void z_nrf_rtc_timer_chan_free(int32_t chan); -/** @brief Read current absolute time. +/** @brief Read current RTC counter value. * - * @return Current absolute time. + * @return Current RTC counter value. */ -uint64_t z_nrf_rtc_timer_read(void); +uint32_t z_nrf_rtc_timer_read(void); /** @brief Get COMPARE event register address. * @@ -93,52 +76,38 @@ uint32_t z_nrf_rtc_timer_compare_read(int32_t chan); /** @brief Try to set compare channel to given value. * - * Provided value is absolute and cannot be further in the future than - * @c NRF_RTC_TIMER_MAX_SCHEDULE_SPAN. If given value is in the past then an RTC - * interrupt is triggered immediately. Otherwise function continuously retries - * to set compare register until value that is written is far enough in the - * future and will generate an event. Because of that, compare register value - * may be different than the one requested. During this operation interrupt - * from that compare channel is disabled. Other interrupts are not locked during - * this operation. + * Provided value is absolute and cannot be further in future than half span of + * the RTC counter. Function continouosly retries to set compare register until + * value that is written is far enough in the future and will generate an event. + * Because of that, compare register value may be different than the one + * requested. During this operation interrupt from that compare channel is + * disabled. Other interrupts are not locked during this operation. + * + * There is no option to abort the request once it is set. However, it can be + * overwritten. * * @param chan Channel ID between 1 and CONFIG_NRF_RTC_TIMER_USER_CHAN_COUNT. * - * @param target_time Absolute target time in ticks. + * @param cc_value Absolute value. Values which are further distanced from + * current counter value than half RTC span are considered in the past. * * @param handler User function called in the context of the RTC interrupt. * * @param user_data Data passed to the handler. - * - * @retval 0 if the compare channel was set successfully. - * @retval -EINVAL if provided target time was further than - * @c NRF_RTC_TIMER_MAX_SCHEDULE_SPAN ticks in the future. */ -int z_nrf_rtc_timer_set(int32_t chan, uint64_t target_time, - z_nrf_rtc_timer_compare_handler_t handler, - void *user_data); - -/** @brief Abort a timer requested with @ref z_nrf_rtc_timer_set. - * - * If an abort operation is performed too late it is still possible for an event - * to fire. The user can detect a spurious event by comparing absolute time - * provided in callback and a result of @ref z_nrf_rtc_timer_read. During this - * operation interrupt from that compare channel is disabled. Other interrupts - * are not locked during this operation. - * - * @param chan Channel ID between 1 and CONFIG_NRF_RTC_TIMER_USER_CHAN_COUNT. - */ -void z_nrf_rtc_timer_abort(int32_t chan); +void z_nrf_rtc_timer_compare_set(int32_t chan, uint32_t cc_value, + z_nrf_rtc_timer_compare_handler_t handler, + void *user_data); /** @brief Convert system clock time to RTC ticks. * - * @p t can be absolute or relative. @p t cannot be further into the future - * from now than the RTC range (e.g. 512 seconds if RTC is running at 32768 Hz). + * @p t can be absolute or relative. @p t cannot be further from now than half + * of the RTC range (e.g. 256 seconds if RTC is running at 32768 Hz). * * @retval Positive value represents @p t in RTC tick value. * @retval -EINVAL if @p t is out of range. */ -uint64_t z_nrf_rtc_timer_get_ticks(k_timeout_t t); +int z_nrf_rtc_timer_get_ticks(k_timeout_t t); #ifdef __cplusplus } diff --git a/tests/drivers/timer/nrf_rtc_timer/src/main.c b/tests/drivers/timer/nrf_rtc_timer/src/main.c index e2d3e1c1873..c33541276e7 100644 --- a/tests/drivers/timer/nrf_rtc_timer/src/main.c +++ b/tests/drivers/timer/nrf_rtc_timer/src/main.c @@ -5,12 +5,11 @@ */ #include #include -#include #include #include struct test_data { - uint64_t target_time; + uint32_t cc_val; uint32_t window; uint32_t delay; int err; @@ -54,33 +53,18 @@ static void stop_zli_timer0(void) nrf_timer_task_trigger(NRF_TIMER0, NRF_TIMER_TASK_STOP); } -static void inject_overflow(void) -{ - /* Bump overflow counter by 100. */ - uint32_t overflow_count = 100; - - while (overflow_count--) { - nrf_rtc_task_trigger(NRF_RTC1, NRF_RTC_TASK_TRIGGER_OVERFLOW); - /* Wait for RTC counter to reach overflow from 0xFFFFF0 and - * get handled. - */ - k_busy_wait(1000); - } -} - -static void timeout_handler(int32_t id, uint64_t expire_time, void *user_data) +static void timeout_handler(int32_t id, uint32_t cc_value, void *user_data) { struct test_data *data = user_data; - uint64_t now = z_nrf_rtc_timer_read(); - uint64_t diff = (now - expire_time); + uint32_t now = z_nrf_rtc_timer_read(); + uint32_t diff = (now - cc_value) & 0x00FFFFFF; zassert_true(diff <= data->delay, - "Handler called in wrong time (%llu), set time: %llu, " - "got time: %llu", - now, data->target_time, expire_time); + "Handler called in wrong time (%d), set cc: %d, got cc: %d", + now, data->cc_val, cc_value); - if ((expire_time >= data->target_time) && - (expire_time <= (data->target_time + data->window))) { + if ((cc_value >= data->cc_val) && + (cc_value <= (data->cc_val + data->window))) { data->err = 0; } timeout_handler_cnt++; @@ -88,15 +72,15 @@ static void timeout_handler(int32_t id, uint64_t expire_time, void *user_data) static void test_timeout(int32_t chan, k_timeout_t t, bool ext_window) { - int64_t ticks = z_nrf_rtc_timer_get_ticks(t); + int32_t cc_val = z_nrf_rtc_timer_get_ticks(t); struct test_data test_data = { - .target_time = ticks, + .cc_val = cc_val, .window = ext_window ? 100 : (Z_TICK_ABS(t.ticks) ? 0 : 32), .delay = ext_window ? 100 : 2, .err = -EINVAL }; - z_nrf_rtc_timer_set(chan, (uint64_t)ticks, timeout_handler, &test_data); + z_nrf_rtc_timer_compare_set(chan, cc_val, timeout_handler, &test_data); /* wait additional arbitrary time. */ k_busy_wait(1000); @@ -146,10 +130,10 @@ static void test_z_nrf_rtc_timer_compare_evt_address_get(void) static void test_int_disable_enabled(void) { - uint64_t now = z_nrf_rtc_timer_read(); - uint64_t t = 1000; + uint32_t now = z_nrf_rtc_timer_read(); + uint32_t t = 1000; struct test_data data = { - .target_time = now + t, + .cc_val = now + t, .window = 1000, .delay = 2000, .err = -EINVAL @@ -160,7 +144,7 @@ static void test_int_disable_enabled(void) chan = z_nrf_rtc_timer_chan_alloc(); zassert_true(chan >= 0, "Failed to allocate RTC channel."); - z_nrf_rtc_timer_set(chan, data.target_time, timeout_handler, &data); + z_nrf_rtc_timer_compare_set(chan, data.cc_val, timeout_handler, &data); zassert_equal(data.err, -EINVAL, "Unexpected err: %d", data.err); key = z_nrf_rtc_timer_compare_int_lock(chan); @@ -179,7 +163,7 @@ static void test_int_disable_enabled(void) static void test_get_ticks(void) { k_timeout_t t = K_MSEC(1); - uint64_t exp_ticks = z_nrf_rtc_timer_read() + t.ticks; + uint32_t exp_ticks = z_nrf_rtc_timer_read() + t.ticks; int ticks; /* Relative 1ms from now timeout converted to RTC */ @@ -202,21 +186,20 @@ static void test_get_ticks(void) "Unexpected result %d (expected: %d)", ticks, exp_ticks); /* too far in the future */ - t = Z_TIMEOUT_TICKS(sys_clock_tick_get() + 0x01000001); + t = Z_TIMEOUT_TICKS(sys_clock_tick_get() + 0x00800001); ticks = z_nrf_rtc_timer_get_ticks(t); zassert_equal(ticks, -EINVAL, "Unexpected ticks: %d", ticks); } -static void sched_handler(int32_t id, uint64_t expire_time, void *user_data) +static void sched_handler(int32_t id, uint32_t cc_val, void *user_data) { int64_t now = sys_clock_tick_get(); int rtc_ticks_now = z_nrf_rtc_timer_get_ticks(Z_TIMEOUT_TICKS(Z_TICK_ABS(now))); uint64_t *evt_uptime_us = user_data; - *evt_uptime_us = - k_ticks_to_us_floor64(now - (rtc_ticks_now - expire_time)); + *evt_uptime_us = k_ticks_to_us_floor64(now - (rtc_ticks_now - cc_val)); } static void test_absolute_scheduling(void) @@ -225,7 +208,7 @@ static void test_absolute_scheduling(void) int64_t now_us = k_ticks_to_us_floor64(sys_clock_tick_get()); uint64_t target_us = now_us + 5678; uint64_t evt_uptime_us; - uint64_t rtc_ticks; + int rtc_ticks; int32_t chan; chan = z_nrf_rtc_timer_chan_alloc(); @@ -233,9 +216,10 @@ static void test_absolute_scheduling(void) /* schedule event in 5678us from now */ t = Z_TIMEOUT_TICKS(Z_TICK_ABS(K_USEC(target_us).ticks)); - rtc_ticks = (uint64_t)z_nrf_rtc_timer_get_ticks(t); + rtc_ticks = z_nrf_rtc_timer_get_ticks(t); - z_nrf_rtc_timer_set(chan, rtc_ticks, sched_handler, &evt_uptime_us); + z_nrf_rtc_timer_compare_set(chan, rtc_ticks, + sched_handler, &evt_uptime_us); k_busy_wait(5678); @@ -246,9 +230,10 @@ static void test_absolute_scheduling(void) /* schedule event now. */ now_us = k_ticks_to_us_floor64(sys_clock_tick_get()); t = Z_TIMEOUT_TICKS(Z_TICK_ABS(K_USEC(now_us).ticks)); - rtc_ticks = (uint64_t)z_nrf_rtc_timer_get_ticks(t); + rtc_ticks = z_nrf_rtc_timer_get_ticks(t); - z_nrf_rtc_timer_set(chan, rtc_ticks, sched_handler, &evt_uptime_us); + z_nrf_rtc_timer_compare_set(chan, rtc_ticks, + sched_handler, &evt_uptime_us); k_busy_wait(200); @@ -304,7 +289,7 @@ static void test_stress(void) z_nrf_rtc_timer_chan_free(chan); } -static void test_resetting_cc(void) +static void test_reseting_cc(void) { uint32_t start = k_uptime_get_32(); uint32_t test_time = 1000; @@ -317,18 +302,19 @@ static void test_resetting_cc(void) timeout_handler_cnt = 0; do { - uint64_t now = z_nrf_rtc_timer_read(); + uint32_t now = z_nrf_rtc_timer_read(); struct test_data test_data = { - .target_time = now + 5, + .cc_val = now + 5, .window = 0, .delay = 0, .err = -EINVAL }; - /* Set timer but expect that it will never expire because + /* Set compare but expect that it will never expire because * it will be later on reset. */ - z_nrf_rtc_timer_set(chan, now + 2, timeout_handler, &test_data); + z_nrf_rtc_timer_compare_set(chan, now + 2, + timeout_handler, &test_data); /* Arbitrary variable delay to reset CC before expiring first * request but very close. @@ -336,7 +322,8 @@ static void test_resetting_cc(void) k_busy_wait(i); i = (i + 1) % 20; - z_nrf_rtc_timer_set(chan, now + 5, timeout_handler, &test_data); + z_nrf_rtc_timer_compare_set(chan, now + 5, + timeout_handler, &test_data); k_busy_wait((5 + 1)*31); cnt++; } while ((k_uptime_get_32() - start) < test_time); @@ -347,86 +334,6 @@ static void test_resetting_cc(void) z_nrf_rtc_timer_chan_free(chan); } -static void overflow_sched_handler(int32_t id, uint64_t expire_time, - void *user_data) -{ - uint64_t now = z_nrf_rtc_timer_read(); - uint64_t *evt_uptime = user_data; - - *evt_uptime = now - expire_time; -} - -/* This test is to be executed as the last, due to interference in overflow - * counter, resulting in nRF RTC timer ticks and kernel ticks desynchronization. - */ -static void test_overflow(void) -{ - PRINT("RTC ticks before overflow injection: %u\r\n", - (uint32_t)z_nrf_rtc_timer_read()); - - inject_overflow(); - - PRINT("RTC ticks after overflow injection: %u\r\n", - (uint32_t)z_nrf_rtc_timer_read()); - - uint64_t now; - uint64_t target_time; - uint64_t evt_uptime; - int32_t chan; - - chan = z_nrf_rtc_timer_chan_alloc(); - zassert_true(chan >= 0, "Failed to allocate RTC channel."); - - /* Schedule event in 5 ticks from now. */ - evt_uptime = UINT64_MAX; - now = z_nrf_rtc_timer_read(); - target_time = now + 5; - z_nrf_rtc_timer_set(chan, target_time, overflow_sched_handler, - &evt_uptime); - - k_busy_wait(k_ticks_to_us_floor64(5 + 1)); - - PRINT("RTC event scheduled at %llu ticks for %llu ticks," - "event occurred at %llu ticks (uptime)\n", - now, target_time, evt_uptime); - zassert_not_equal(UINT64_MAX, evt_uptime, - "Expired timer shall overwrite evt_uptime"); - - /* Schedule event now. */ - evt_uptime = UINT64_MAX; - now = z_nrf_rtc_timer_read(); - target_time = now; - - z_nrf_rtc_timer_set(chan, target_time, overflow_sched_handler, - &evt_uptime); - - k_busy_wait(200); - - zassert_not_equal(UINT64_MAX, evt_uptime, - "Expired timer shall overwrite evt_uptime"); - PRINT("RTC event scheduled at %llu ticks for %llu ticks," - "event occurred at %llu ticks (uptime)\n", - now, target_time, evt_uptime); - - /* Schedule event far in the past. */ - evt_uptime = UINT64_MAX; - now = z_nrf_rtc_timer_read(); - target_time = now - 2 * NRF_RTC_TIMER_MAX_SCHEDULE_SPAN; - - z_nrf_rtc_timer_set(chan, target_time, overflow_sched_handler, - &evt_uptime); - - k_busy_wait(200); - - zassert_not_equal(UINT64_MAX, evt_uptime, - "Expired timer shall overwrite evt_uptime"); - PRINT("RTC event scheduled at %llu ticks for %llu ticks," - "event occurred at %llu ticks (uptime)\n", - now, target_time, evt_uptime); - - z_nrf_rtc_timer_chan_free(chan); -} - void test_main(void) { init_zli_timer0(); @@ -439,8 +346,7 @@ void test_main(void) ztest_unit_test(test_absolute_scheduling), ztest_unit_test(test_alloc_free), ztest_unit_test(test_stress), - ztest_unit_test(test_resetting_cc), - ztest_unit_test(test_overflow) + ztest_unit_test(test_reseting_cc) ); ztest_run_test_suite(test_nrf_rtc_timer); }