Skip to content

Commit

Permalink
xtimer_core: timing improved
Browse files Browse the repository at this point in the history
  • Loading branch information
Josar committed May 11, 2018
1 parent 9020767 commit 290d0f7
Show file tree
Hide file tree
Showing 4 changed files with 119 additions and 52 deletions.
52 changes: 41 additions & 11 deletions sys/include/xtimer.h
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
12 changes: 10 additions & 2 deletions sys/include/xtimer/implementation.h
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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
Expand Down
48 changes: 33 additions & 15 deletions sys/xtimer/xtimer.c
Original file line number Diff line number Diff line change
Expand Up @@ -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()
Expand All @@ -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;
}
Expand Down
59 changes: 35 additions & 24 deletions sys/xtimer/xtimer_core.c
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
/**
* Copyright (C) 2015 Kaspar Schleiser <kaspar@schleiser.de>
* 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
Expand All @@ -12,6 +14,8 @@
* @brief xtimer core functionality
* @author Kaspar Schleiser <kaspar@schleiser.de>
* @author Joakim Nohlgård <joakim.nohlgard@eistec.se>
* @author Josua Arndt <jarndt@ias.rwth-aachen.de>
*
* @}
*/

Expand All @@ -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 <stdio.h>

static volatile int _in_handler = 0;

Expand All @@ -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;
Expand All @@ -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 */
Expand Down Expand Up @@ -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) {
Expand All @@ -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)
Expand Down Expand Up @@ -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;
}
Expand Down Expand Up @@ -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;

Expand All @@ -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);
}
Expand Down

0 comments on commit 290d0f7

Please sign in to comment.