Skip to content

Commit

Permalink
xtimer: Refactor xtimer_usleep_until and rename to xtimer_periodic_wa…
Browse files Browse the repository at this point in the history
…keup

Rewrote conditions for improved readability, and removed magic number 512
  • Loading branch information
Joakim Nohlgård committed Jul 29, 2016
1 parent 8518843 commit 4449ba4
Show file tree
Hide file tree
Showing 2 changed files with 74 additions and 36 deletions.
42 changes: 32 additions & 10 deletions sys/include/xtimer.h
Expand Up @@ -144,25 +144,24 @@ static void xtimer_nanosleep(uint32_t nanoseconds);
*/
static inline void xtimer_spin(uint32_t microseconds);

/**
/**
* @brief will cause the calling thread to be suspended until the absolute
* time (@p last_wakeup + @p interval).
* time (@p last_wakeup + @p period).
*
* When the function returns, @p last_wakeup is set to
* (@p last_wakeup + @p interval).
* (@p last_wakeup + @p period).
*
* This function can be used to create periodic wakeups.
* @c last_wakeup should be set to xtimer_now() before first call of the
* function.
*
* If the result of (@p last_wakeup + usecs) would be in the past, the function
* sets @p last_wakeup to @p last_wakeup + @p interval and returns immediately.
* If the result of (@p last_wakeup + @p period) would be in the past, the function
* sets @p last_wakeup to @p last_wakeup + @p period and returns immediately.
*
* @param[in] last_wakeup base time for the wakeup
* @param[in] usecs time in microseconds that will be added to
* last_wakeup
* @param[in] last_wakeup base time stamp for the wakeup
* @param[in] period time in microseconds that will be added to last_wakeup
*/
void xtimer_usleep_until(uint32_t *last_wakeup, uint32_t usecs);
void xtimer_periodic_wakeup(uint32_t *last_wakeup, uint32_t period);

/**
* @brief Set a timer that sends a message
Expand Down Expand Up @@ -326,6 +325,29 @@ int xtimer_msg_receive_timeout64(msg_t *msg, uint64_t us);
#define XTIMER_ISR_BACKOFF 20
#endif

#ifndef XTIMER_PERIODIC_SPIN
/**
* @brief xtimer_periodic_wakeup spin cutoff
*
* If the difference between target time and now is less than this value, then
* xtimer_periodic_wakeup will use xtimer_spin instead of setting a timer.
*/
#define XTIMER_PERIODIC_SPIN (XTIMER_BACKOFF * 2)
#endif

#ifndef XTIMER_PERIODIC_RELATIVE
/**
* @brief xtimer_periodic_wakeup relative target cutoff
*
* If the difference between target time and now is less than this value, then
* xtimer_periodic_wakeup will set a relative target time in the future instead
* of the true target.
*
* This is done to prevent target time underflows.
*/
#define XTIMER_PERIODIC_RELATIVE (512)
#endif

#ifndef XTIMER_SHIFT
/**
* @brief xtimer prescaler value
Expand Down Expand Up @@ -373,7 +395,6 @@ int xtimer_msg_receive_timeout64(msg_t *msg, uint64_t us);
#define XTIMER_WIDTH (32)
#endif

#if XTIMER_WIDTH != 32
/**
* @brief xtimer timer mask
*
Expand All @@ -383,6 +404,7 @@ int xtimer_msg_receive_timeout64(msg_t *msg, uint64_t us);
* For a 16bit timer, the mask would be 0xFFFF0000, for a 24bit timer, the mask
* would be 0xFF000000.
*/
#if XTIMER_WIDTH != 32
#define XTIMER_MASK ((0xffffffff >> XTIMER_WIDTH) << XTIMER_WIDTH)
#else
#define XTIMER_MASK (0)
Expand Down
68 changes: 42 additions & 26 deletions sys/xtimer/xtimer.c
Expand Up @@ -58,50 +58,66 @@ void _xtimer_sleep(uint32_t offset, uint32_t long_offset)
mutex_lock(&mutex);
}

void xtimer_usleep_until(uint32_t *last_wakeup, uint32_t interval) {
void xtimer_periodic_wakeup(uint32_t *last_wakeup, uint32_t period) {
xtimer_t timer;
mutex_t mutex = MUTEX_INIT;

timer.callback = _callback_unlock_mutex;
timer.arg = (void*) &mutex;

uint32_t target = *last_wakeup + interval;

uint32_t target = (*last_wakeup) + period;
uint32_t now = xtimer_now();
/* make sure we're not setting a value in the past */
if (now < *last_wakeup) {
/* base timer overflowed */
if (!((target < *last_wakeup) && (target > now))) {
if (now < (*last_wakeup)) {
/* base timer overflowed between last_wakeup and now */
if (!((now < target) && (target < (*last_wakeup)))) {
/* target time has already passed */
goto out;
}
}
else if (! ((target < *last_wakeup) || (target > now))) {
goto out;
else {
/* base timer did not overflow */
if ((((*last_wakeup) <= target) && (target <= now))) {
/* target time has already passed */
goto out;
}
}

/* For large offsets, set an absolute target time, as
* it is more exact.
*
* As that might cause an underflow, for small offsets,
* use a relative offset, as that can never underflow.
*
/*
* 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;

if (offset > (XTIMER_BACKOFF * 2)) {
mutex_lock(&mutex);
if (offset >> 9) { /* >= 512 */
offset = target;
}
else {
offset += xtimer_now();
}
_xtimer_set_absolute(&timer, offset);
mutex_lock(&mutex);
DEBUG("xps, now: %9" PRIu32 ", tgt: %9" PRIu32 ", off: %9" PRIu32 "\n", now, target, offset);
if (offset < XTIMER_PERIODIC_SPIN) {
xtimer_spin(offset);
}
else {
xtimer_spin(offset);
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()
*
* Since interrupts are normally enabled inside this function, this time may
* be undeterministic. */
target = xtimer_now() + offset;
}
mutex_lock(&mutex);
DEBUG("xps, abs: %" PRIu32 "\n", target);
_xtimer_set_absolute(&timer, target);
mutex_lock(&mutex);
}

out:
Expand Down

0 comments on commit 4449ba4

Please sign in to comment.