Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

xtimer_core: reduce idle time #8990

Closed
wants to merge 1 commit into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
17 changes: 7 additions & 10 deletions boards/jiminy-mega256rfr2/include/board.h
Original file line number Diff line number Diff line change
Expand Up @@ -88,17 +88,13 @@ extern "C" {
#define XTIMER_CHAN (0)
#define XTIMER_WIDTH (16)
#define XTIMER_HZ (125000UL)
/** @} */

/**
* @name Indicate Watchdog cleared in bootloader an
*
* AVR CPUs need to reset the Watchdog as fast as possible.
* This flag indicates that the watchdog is reseted in the bootloader
* and that the MCUSR value is stored in register 2 (r2)
* @{
*/
#define BOOTLOADER_CLEARS_WATCHDOG_AND_PASSES_MCUSR 1
#define XTIMER_OVERHEAD (25)
#define XTIMER_SHOOT_OVERHEAD (18)
#define XTIMER_BACKOFF_OVERHEAD (16)
#define XTIMER_BACKOFF (2*(XTIMER_OVERHEAD+XTIMER_SHOOT_OVERHEAD))
#define XTIMER_ISR_BACKOFF (XTIMER_OVERHEAD+XTIMER_SHOOT_OVERHEAD)

/** @} */

/**
Expand All @@ -112,6 +108,7 @@ extern "C" {
#define BOOTLOADER_CLEARS_WATCHDOG_AND_PASSES_MCUSR 1
/** @} */


/**
* @name CPU clock scale for jiminy-megarfr256rfr2
*
Expand Down
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
16 changes: 6 additions & 10 deletions sys/include/xtimer/implementation.h
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@
#ifndef XTIMER_H
#error "Do not include this file directly! Use xtimer.h instead"
#endif

#include <stdio.h>
#include "periph/timer.h"

#ifdef __cplusplus
Expand Down Expand Up @@ -71,16 +71,12 @@ uint64_t _xtimer_now64(void);
/**
* @brief Sets the timer to the appropriate timer_list or list_head.
*
* @note The target to set the timer to has to be at least bigger then the
* ticks needed to jump into the function and calculate '_xtimer_now()'.
* So that 'now' did not pass the target.
* This is crucial when using low CPU frequencies and/or when the
* '_xtimer_now()' call needs multiple xtimer ticks to evaluate.
*
* @param[in] timer pointer to xtimer_t which is added to the list.
* @param[in] target Absolute target value in ticks.
* @param[in] timer pointer to xtimer_t which is added to the list.
* @param[in] offset Offset in ticks.
* @param[in] irq_state Interrupt state after ensuring lltimer overflow is
* not happening in XTIMER_BACKOFF ticks.
*/
int _xtimer_set_absolute(xtimer_t *timer, uint32_t target);
void _xtimer_set_absolute(xtimer_t *timer, uint32_t offset, unsigned irq_state);
void _xtimer_set(xtimer_t *timer, uint32_t offset);
void _xtimer_set64(xtimer_t *timer, uint32_t offset, uint32_t long_offset);
void _xtimer_periodic_wakeup(uint32_t *last_wakeup, uint32_t period);
Expand Down
67 changes: 35 additions & 32 deletions sys/xtimer/xtimer.c
Original file line number Diff line number Diff line change
Expand Up @@ -73,65 +73,68 @@ void _xtimer_tsleep(uint32_t offset, uint32_t long_offset)
void _xtimer_periodic_wakeup(uint32_t *last_wakeup, uint32_t period) {
xtimer_t timer;
mutex_t mutex = MUTEX_INIT;
uint32_t mult;

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

uint32_t target = (*last_wakeup) + period;

/* wait for hardware timer overflow */
uint32_t max = _xtimer_lltimer_mask(0xFFFFFFFF) - XTIMER_BACKOFF;
/* make sure the timer counter arrives in the next timer period */
if(_xtimer_lltimer_now()>= max){}

unsigned irq_state = irq_disable();
uint32_t now = _xtimer_now();

/* make sure we're not setting a value in the past */
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;
/* last_wakeup < target, base overflowed but target not, target passed. */
/* target <= now , both overflowed, target passed. */
if ( (*last_wakeup < target) || (target <= now) ) {
//now = _xtimer_now();
/* now - target, will always be the difference. (modulo power of two) */
mult = (now - target) / period;
/* Skip missed targets */
*last_wakeup = (mult * period) + target;
irq_restore(irq_state);
return;
}
}
else {
/* base timer did not overflow */
if ((((*last_wakeup) <= target) && (target <= now))) {
/* target time has already passed */
goto out;
/* last_wakeup < now, base timer did not overflow */
/* target <= now AND target did not overflow, target passed */
if ( (target <= now) && ((*last_wakeup) <= target) ) {
//now = _xtimer_now();
/* now - target, will always be the difference. (modulo power of two) */
mult = (now - target)/ period;
/* Skip missed targets */
*last_wakeup = (mult * period) + target;
irq_restore(irq_state);
return;
}
}

/*
/* 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) {
irq_restore(irq_state);
_xtimer_spin(offset);
}
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()
*
* 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);
period = target - _xtimer_now();
DEBUG("xps, target: %" PRIu32 " period %" PRIu32 "\n", target, period);
_xtimer_set_absolute(&timer, period, irq_state);
mutex_lock(&mutex);
}
out:

*last_wakeup = target;
}

Expand Down
Loading