From 290d0f75220f71ff4a75e37ba247747550cef541 Mon Sep 17 00:00:00 2001 From: Josarn Date: Fri, 11 May 2018 02:44:29 +0200 Subject: [PATCH] xtimer_core: timing improved --- sys/include/xtimer.h | 52 +++++++++++++++++++------ sys/include/xtimer/implementation.h | 12 +++++- sys/xtimer/xtimer.c | 48 +++++++++++++++-------- sys/xtimer/xtimer_core.c | 59 +++++++++++++++++------------ 4 files changed, 119 insertions(+), 52 deletions(-) diff --git a/sys/include/xtimer.h b/sys/include/xtimer.h index cb2e63036772b..20bc170f7b9cb 100644 --- a/sys/include/xtimer.h +++ b/sys/include/xtimer.h @@ -484,28 +484,58 @@ void xtimer_set_timeout_flag(xtimer_t *t, uint32_t timeout); #endif /** - * @brief xtimer overhead value, in hardware ticks + * @brief xtimer backoff overhead, in hardware ticks + * + * This value specifies the time needed to reach the comparison for XTIMER_BACKOFF + * and to get back to the calling thread. + * + * xtimer automatically substracts XTIMER_BACKOFF_OVERHEAD from a timer's target + * time before executing the callback. Thus, setting a to high value will lead to + * early trigger. * - * This value specifies the time a timer will be late if uncorrected, e.g., - * the system-specific xtimer execution time from timer ISR to executing - * a timer's callback's first instruction. + * This is supposed to be defined per-device in e.g., periph_conf.h. + * Use `tests/xtimer_configuration` to test and evaluate XTIMER_BACKOFF_OVERHEAD. + */ +#ifndef XTIMER_BACKOFF_OVERHEAD +#define XTIMER_BACKOFF_OVERHEAD 1 +#endif + +/** + * @brief xtimer overhead value, in hardware ticks * - * E.g., with XTIMER_OVERHEAD == 0 - * start=xtimer_now(); - * xtimer_set(&timer, X); - * (in callback:) - * overhead=xtimer_now()-start-X; + * This value specifies the time interval which is subtracted from the target + * time to ensure that the timer is not executed to late. + * This is used to correct the system-specific xtimer execution time from timer + * ISR to executing a timer's callback's first instruction. * * xtimer automatically substracts XTIMER_OVERHEAD from a timer's target time, - * but when the timer triggers, xtimer will spin-lock until a timer's target - * time is reached, so timers will never trigger early. + * but when the timer triggers, xtimer will spin-lock until a timer's + * target - XTIMER_SHOOT_OVERHEAD is reached, so timers will trigger on spot. * * This is supposed to be defined per-device in e.g., periph_conf.h. + * Use `tests/xtimer_configuration` to test and evaluate XTIMER_OVERHEAD. */ #ifndef XTIMER_OVERHEAD #define XTIMER_OVERHEAD 20 #endif +/** + * @brief xtimer shoot overhead, in hardware ticks + * + * This value specifies the time needed to get out of the timer interrupt to the + * thread which waits to get activ. + * + * xtimer automatically substracts XTIMER_SHOOT_OVERHEAD from a timer's target + * time before executing the callback. Thus, setting a to high value will lead to + * early trigger. + * + * This is supposed to be defined per-device in e.g., periph_conf.h. + * Use `tests/xtimer_configuration` to test and evaluate XTIMER_SHOOT_OVERHEAD. + */ +#ifndef XTIMER_SHOOT_OVERHEAD +#define XTIMER_SHOOT_OVERHEAD 0 +#endif + #ifndef XTIMER_ISR_BACKOFF /** * @brief xtimer IRQ backoff time, in hardware ticks diff --git a/sys/include/xtimer/implementation.h b/sys/include/xtimer/implementation.h index ed16aa91ad714..54705c843aef5 100644 --- a/sys/include/xtimer/implementation.h +++ b/sys/include/xtimer/implementation.h @@ -140,8 +140,8 @@ static inline uint64_t xtimer_now_usec64(void) static inline void _xtimer_spin(uint32_t offset) { uint32_t start = _xtimer_lltimer_now(); #if XTIMER_MASK - offset = _xtimer_lltimer_mask(offset); - while (_xtimer_lltimer_mask(_xtimer_lltimer_now() - start) < offset); + offset = _xtimer_lltimer_mask(start+offset); + while (_xtimer_lltimer_mask(_xtimer_lltimer_now()) < offset); #else while ((_xtimer_lltimer_now() - start) < offset); #endif @@ -307,6 +307,14 @@ static inline bool xtimer_less64(xtimer_ticks64_t a, xtimer_ticks64_t b) return (a.ticks64 < b.ticks64); } +static inline void xtimer_spin_until(uint32_t target) { +#if XTIMER_MASK + target = _xtimer_lltimer_mask(target); +#endif + while (_xtimer_lltimer_now() > target); + while (_xtimer_lltimer_now() < target); +} + #endif /* !defined(DOXYGEN) */ #ifdef __cplusplus diff --git a/sys/xtimer/xtimer.c b/sys/xtimer/xtimer.c index 612bbc9622fff..24b8ac6f79dba 100644 --- a/sys/xtimer/xtimer.c +++ b/sys/xtimer/xtimer.c @@ -94,29 +94,31 @@ void _xtimer_periodic_wakeup(uint32_t *last_wakeup, uint32_t period) { } } - /* + /* very small interval allready reached.*/ + now = _xtimer_now(); + if(target <= now){ + goto out; + } + /* For very small offsets, spin. * For large offsets, set an absolute target time. * As that might cause an underflow, for small offsets, set a relative * target time. - * For very small offsets, spin. - */ - /* - * Note: last_wakeup _must never_ specify a time in the future after - * _xtimer_periodic_sleep returns. - * If this happens, last_wakeup may specify a time in the future when the - * next call to _xtimer_periodic_sleep is made, which in turn will trigger - * the overflow logic above and make the next timer fire too early, causing - * last_wakeup to point even further into the future, leading to a chain - * reaction. - * - * tl;dr Don't return too early! */ uint32_t offset = target - now; DEBUG("xps, now: %9" PRIu32 ", tgt: %9" PRIu32 ", off: %9" PRIu32 "\n", now, target, offset); if (offset < XTIMER_PERIODIC_SPIN) { - _xtimer_spin(offset); + /* xtimer_spin_until ensures no early trigger but for very short + * spin it will take a whole timer cicle (e.g. 65536 ticks), so leave immediately */ + if(XTIMER_BACKOFF_OVERHEAD <= offset){ + offset -= XTIMER_BACKOFF_OVERHEAD; + _xtimer_spin(offset); + }else{ + _xtimer_spin(offset); + goto out; + } } - else { + else + { if (offset < XTIMER_PERIODIC_RELATIVE) { /* NB: This will overshoot the target by the amount of time it took * to get here from the beginning of xtimer_periodic_wakeup() @@ -130,6 +132,22 @@ void _xtimer_periodic_wakeup(uint32_t *last_wakeup, uint32_t period) { _xtimer_set_absolute(&timer, target); mutex_lock(&mutex); } + /* + * Note: last_wakeup _must never_ specify a time in the future after + * _xtimer_periodic_sleep returns. + * If this happens, last_wakeup may specify a time in the future when the + * next call to _xtimer_periodic_sleep is made, which in turn will trigger + * the overflow logic above and make the next timer fire too early, causing + * last_wakeup to point even further into the future, leading to a chain + * reaction. + * + * tl;dr Don't return too early! + * + * This is ensured in xtimer_core _timer_callback with setting + * XTIMER_SHOOT_OVERHEAD + */ + // xtimer_spin_until(target); + out: *last_wakeup = target; } diff --git a/sys/xtimer/xtimer_core.c b/sys/xtimer/xtimer_core.c index d5765de89b84c..082097295d610 100644 --- a/sys/xtimer/xtimer_core.c +++ b/sys/xtimer/xtimer_core.c @@ -1,6 +1,8 @@ /** * Copyright (C) 2015 Kaspar Schleiser - * Copyright (C) 2016 Eistec AB + * 2016 Eistec AB + * 2018 Josua Arndt + * * * This file is subject to the terms and conditions of the GNU Lesser * General Public License v2.1. See the file LICENSE in the top level @@ -12,6 +14,8 @@ * @brief xtimer core functionality * @author Kaspar Schleiser * @author Joakim NohlgÄrd + * @author Josua Arndt + * * @} */ @@ -27,6 +31,7 @@ /* WARNING! enabling this will have side effects and can lead to timer underflows. */ #define ENABLE_DEBUG 0 #include "debug.h" +#include static volatile int _in_handler = 0; @@ -35,8 +40,6 @@ static volatile uint32_t _long_cnt = 0; volatile uint32_t _xtimer_high_cnt = 0; #endif -static inline void xtimer_spin_until(uint32_t value); - static xtimer_t *timer_list_head = NULL; static xtimer_t *overflow_list_head = NULL; static xtimer_t *long_list_head = NULL; @@ -58,14 +61,6 @@ static inline int _is_set(xtimer_t *timer) return (timer->target || timer->long_target); } -static inline void xtimer_spin_until(uint32_t target) { -#if XTIMER_MASK - target = _xtimer_lltimer_mask(target); -#endif - while (_xtimer_lltimer_now() > target); - while (_xtimer_lltimer_now() < target); -} - void xtimer_init(void) { /* initialize low-level timer */ @@ -128,6 +123,7 @@ void _xtimer_set64(xtimer_t *timer, uint32_t offset, uint32_t long_offset) void _xtimer_set(xtimer_t *timer, uint32_t offset) { + uint32_t now = _xtimer_now(); DEBUG("timer_set(): offset=%" PRIu32 " now=%" PRIu32 " (%" PRIu32 ")\n", offset, xtimer_now().ticks32, _xtimer_lltimer_now()); if (!timer->callback) { @@ -137,14 +133,8 @@ void _xtimer_set(xtimer_t *timer, uint32_t offset) xtimer_remove(timer); - if (offset < XTIMER_BACKOFF) { - _xtimer_spin(offset); - _shoot(timer); - } - else { - uint32_t target = _xtimer_now() + offset; - _xtimer_set_absolute(timer, target); - } + uint32_t target = now + offset; + _xtimer_set_absolute(timer, target); } static void _periph_timer_callback(void *arg, int chan) @@ -176,9 +166,19 @@ int _xtimer_set_absolute(xtimer_t *timer, uint32_t target) DEBUG("timer_set_absolute(): now=%" PRIu32 " target=%" PRIu32 "\n", now, target); timer->next = NULL; - if ((target >= now) && ((target - XTIMER_BACKOFF) < now)) { + /* if target is smaller or equal now shoot */ + if(target <= now){ + _shoot(timer); + return 0; + } + else if ((target - XTIMER_BACKOFF) < now) { /* backoff */ - xtimer_spin_until(target + XTIMER_BACKOFF); + /* target - now can only be positiv */ + uint32_t offset = target - now; + if(XTIMER_BACKOFF_OVERHEAD <= offset){ + offset -= XTIMER_BACKOFF_OVERHEAD; + _xtimer_spin(offset); + } _shoot(timer); return 0; } @@ -464,10 +464,13 @@ static void _timer_callback(void) overflow: /* check if next timers are close to expiring */ - while (timer_list_head && (_time_left(_xtimer_lltimer_mask(timer_list_head->target), reference) < XTIMER_ISR_BACKOFF)) { - /* make sure we don't fire too early */ - while (_time_left(_xtimer_lltimer_mask(timer_list_head->target), reference)) {} + while (timer_list_head + && (_time_left( + _xtimer_lltimer_mask(timer_list_head->target-XTIMER_SHOOT_OVERHEAD), + reference) < XTIMER_ISR_BACKOFF)) { + /* save target for later use */ + uint32_t target = timer_list_head->target; /* pick first timer in list */ xtimer_t *timer = timer_list_head; @@ -478,6 +481,14 @@ static void _timer_callback(void) timer->target = 0; timer->long_target = 0; + /* make sure we don't fire too early. But correct time needed to shoot. */ + uint32_t offset = _time_left(_xtimer_lltimer_mask(target),reference); + DEBUG("spin=%" PRIu32 "\n",offset); + + if(XTIMER_SHOOT_OVERHEAD < offset){ + offset -= XTIMER_SHOOT_OVERHEAD; + _xtimer_spin(offset); + } /* fire timer */ _shoot(timer); }