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

Improve and debug Xtimer #10393

Closed
wants to merge 7 commits 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
49 changes: 34 additions & 15 deletions sys/xtimer/xtimer.c
Original file line number Diff line number Diff line change
Expand Up @@ -73,28 +73,43 @@ 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;

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

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 between last_wakeup and now */
if (!((now < target) && (target < (*last_wakeup)))) {
/* target time has already passed */
goto out;
/* last_wakeup < target, now overflowed but target not, target passed.
* now < last_wakeup < target */
/* target <= now , both overflowed, target passed.
* target < now < last_wakeup */
if ( (*last_wakeup < target) || (target <= now) ) {
/* now - target, will always be the difference. (modulo power of two) */
mult = ((uint32_t)(now - target)) / period;
/* Skip missed targets */
*last_wakeup = target + (mult * period);
return;
}
}
else {
/* base timer did not overflow */
if ((((*last_wakeup) <= target) && (target <= now))) {
/* target time has already passed */
goto out;
/* last_wakeup < now, now did not overflow
* target <= now AND target did not overflow, target passed
* last_wakeup <= target <= now */
/* The special case (*last_wakeup) = target happens when a period smaller
* then the minimum resolution of the xtimer_hz is requested. */
if ( (target <= now) && ((*last_wakeup) <= target) ) {
/* now - target, will always be the difference. (modulo power of two) */
mult = ((uint32_t)(now - target))/ period;
/* Skip missed targets */
*last_wakeup = target + (mult * period);
return;
}
}

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

/*
* For large offsets, set an absolute target time.
* As that might cause an underflow, for small offsets, set a relative
Expand All @@ -113,9 +128,13 @@ void _xtimer_periodic_wakeup(uint32_t *last_wakeup, uint32_t period) {
* 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);
DEBUG("xps, now: %9" PRIu32 ", tgt: %9" PRIu32 ", lst_wkp: %9" PRIu32 ", off: %9" PRIu32 "\n",
now, target, *last_wakeup, offset);
if (offset < XTIMER_PERIODIC_SPIN ) {
/* for short times skip */
if(offset != 0) {
_xtimer_spin(offset);
}
}
else {
if (offset < XTIMER_PERIODIC_RELATIVE) {
Expand All @@ -131,7 +150,7 @@ void _xtimer_periodic_wakeup(uint32_t *last_wakeup, uint32_t period) {
_xtimer_set_absolute(&timer, target);
mutex_lock(&mutex);
}
out:

*last_wakeup = target;
}

Expand Down
81 changes: 31 additions & 50 deletions sys/xtimer/xtimer_core.c
Original file line number Diff line number Diff line change
Expand Up @@ -320,20 +320,23 @@ void xtimer_remove(xtimer_t *timer)
irq_restore(state);
}

/* target < reference < now */
static uint32_t _time_left(uint32_t target, uint32_t reference)
{
uint32_t now = _xtimer_lltimer_now();

if (now < reference) {
return 0;
}

if (target > now) {
return target - now;
/* `target` is in next period
* AND `now` is at the end ot this period
* OR `now` still smaller than target */
if( (target < XTIMER_OVERHEAD)
&& ((reference < now) || (now < target))) {
return _xtimer_lltimer_mask(target-now);
}
else {
return 0;
/* OR Target is still bigger than now */
else if ( (reference < now) && (now < target) ){
return target-now;
}
return 0;
}

static inline int _this_high_period(uint32_t target)
Expand Down Expand Up @@ -484,17 +487,13 @@ static void _timer_callback(void)
_next_period();

reference = 0;

/* make sure the timer counter also arrived
* in the next timer period */
/* make sure the timer counter also arrived in the next timer period */
while (_xtimer_lltimer_now() == _xtimer_lltimer_mask(0xFFFFFFFF)) {}
}
else {
/* we ended up in _timer_callback and there is
* a timer waiting.
*/
/* set our period reference to the current time. */
reference = _xtimer_lltimer_now();
/* we ended up in _timer_callback and there is a timer waiting.
* set our period reference to the expected target. */
reference = _xtimer_lltimer_mask(timer_list_head->target - XTIMER_OVERHEAD);
}

overflow:
Expand All @@ -517,26 +516,9 @@ static void _timer_callback(void)
_shoot(timer);
}

/* possibly executing all callbacks took enough
* time to overflow. In that case we advance to
* next timer period and check again for expired
* timers.*/
/* check if the end of this period is very soon */
uint32_t now = _xtimer_lltimer_now() + XTIMER_ISR_BACKOFF;
if (now < reference) {
DEBUG("_timer_callback: overflowed while executing callbacks. %i\n",
timer_list_head != NULL);
_next_period();
/* wait till overflow */
while( reference < _xtimer_lltimer_now()){}
reference = 0;
goto overflow;
}

if (timer_list_head) {
/* schedule callback on next timer target time */
next_target = timer_list_head->target - XTIMER_OVERHEAD;

/* make sure we're not setting a time in the past */
if (next_target < (_xtimer_now() + XTIMER_ISR_BACKOFF)) {
goto overflow;
Expand All @@ -546,24 +528,23 @@ static void _timer_callback(void)
/* there's no timer planned for this timer period */
/* schedule callback on next overflow */
next_target = _xtimer_lltimer_mask(0xFFFFFFFF);
uint32_t now = _xtimer_lltimer_now();
}

/* check for overflow again */
if (now < reference) {
_next_period();
reference = 0;
goto overflow;
}
else {
/* check if the end of this period is very soon */
if (_xtimer_lltimer_mask(now + XTIMER_ISR_BACKOFF) < now) {
/* spin until next period, then advance */
while (_xtimer_lltimer_now() >= now) {}
_next_period();
reference = 0;
goto overflow;
}
}
/* possibly executing all callbacks took enough
* time to overflow. In that case we advance to
* next timer period and check again for expired
* timers.*/
/* check for overflow */
/* Or if the end of this period is very soon */
uint32_t now = _xtimer_lltimer_now();
if (now < reference || _xtimer_lltimer_mask(now + XTIMER_ISR_BACKOFF) < now) {
_next_period();
DEBUG("_timer_callback: overflowed while executing callbacks. %i\n",
timer_list_head != NULL);
/* wait till overflow */
while( reference < _xtimer_lltimer_now()){}
reference = 0;
goto overflow;
}

_in_handler = 0;
Expand Down
2 changes: 1 addition & 1 deletion tests/xtimer_drift/main.c
Original file line number Diff line number Diff line change
Expand Up @@ -163,7 +163,7 @@ int main(void)

/* create and trigger worker thread */
kernel_pid_t pid3 = thread_create(worker_stack, sizeof(worker_stack),
THREAD_PRIORITY_MAIN - 1,
THREAD_PRIORITY_MAIN - 2,
THREAD_CREATE_STACKTEST,
worker_thread, NULL, "worker");

Expand Down
5 changes: 3 additions & 2 deletions tests/xtimer_hang/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,8 @@ TEST_ON_CI_WHITELIST += all
# Port number should be found in port enum e.g in cpu/include/periph_cpu.h
#FEATURES_REQUIRED += periph_gpio
# Jiminy probing Pins
#CFLAGS += -DWORKER_THREAD_PIN=GPIO_PIN\(5,7\)
#CFLAGS += -DMAIN_THREAD_PIN=GPIO_PIN\(5,6\)
#CFLAGS += -DWORKER_THREAD_PIN_1=GPIO_PIN\(5,7\)
#CFLAGS += -DWORKER_THREAD_PIN_2=GPIO_PIN\(5,6\)
#CFLAGS += -DMAIN_THREAD_PIN=GPIO_PIN\(5,5\)

include $(RIOTBASE)/Makefile.include
38 changes: 28 additions & 10 deletions tests/xtimer_hang/main.c
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@
#include "thread.h"
#include "log.h"

#if defined(MAIN_THREAD_PIN) || defined(WORKER_THREAD_PIN)
#if defined(MAIN_THREAD_PIN) || defined(WORKER_THREAD_PIN_1) || defined(WORKER_THREAD_PIN_2)
#include "board.h"
#include "periph/gpio.h"
#endif
Expand All @@ -38,20 +38,35 @@
#define TEST_INTERVAL_MS (100LU)
#define TEST_TIMER_STACKSIZE (THREAD_STACKSIZE_DEFAULT)

#define TEST_SLEEP_TIME_1 (1000)
#define TEST_SLEEP_TIME_2 (1100)

char stack_timer1[TEST_TIMER_STACKSIZE];
char stack_timer2[TEST_TIMER_STACKSIZE];

void* timer_func(void* arg)
{
#if defined(WORKER_THREAD_PIN)
gpio_t worker_pin = WORKER_THREAD_PIN;
gpio_init(worker_pin, GPIO_OUT);
#if defined(WORKER_THREAD_PIN_1)
gpio_t worker_pin_1 = WORKER_THREAD_PIN_1;
gpio_init(worker_pin_1, GPIO_OUT);
#endif
#if defined(WORKER_THREAD_PIN_2)
gpio_t worker_pin_2 = WORKER_THREAD_PIN_2;
gpio_init(worker_pin_2, GPIO_OUT);
#endif
LOG_DEBUG("run thread %" PRIkernel_pid "\n", thread_getpid());


while(1) {
#if defined(WORKER_THREAD_PIN)
gpio_set(worker_pin);
gpio_clear(worker_pin);
#if defined(WORKER_THREAD_PIN_1) && defined(WORKER_THREAD_PIN_2)
if( *(uint32_t*)(arg) == TEST_SLEEP_TIME_1) {
gpio_set(worker_pin_1);
gpio_clear(worker_pin_1);
}
else {
gpio_set(worker_pin_2);
gpio_clear(worker_pin_2);
}
#endif
xtimer_usleep(*(uint32_t *)(arg));
}
Expand All @@ -65,8 +80,8 @@ int main(void)
#endif

LOG_DEBUG("[INIT]\n");
uint32_t sleep_timer1 = 1000;
uint32_t sleep_timer2 = 1100;
uint32_t sleep_timer1 = TEST_SLEEP_TIME_1;
uint32_t sleep_timer2 = TEST_SLEEP_TIME_2;

thread_create(stack_timer1, TEST_TIMER_STACKSIZE,
2, THREAD_CREATE_STACKTEST,
Expand All @@ -78,13 +93,16 @@ int main(void)
uint32_t now = 0;
uint32_t start = xtimer_now_usec();
uint32_t until = start + (TEST_TIME_S * US_PER_SEC);
uint32_t interval = TEST_INTERVAL_MS * US_PER_MS;
xtimer_ticks32_t last_wakeup = xtimer_now();

puts("[START]");
while((now = xtimer_now_usec()) < until) {
unsigned percent = (100 * (now - start)) / (until - start);
#if defined(MAIN_THREAD_PIN)
gpio_set(main_pin);
#endif
xtimer_usleep(TEST_INTERVAL_MS * US_PER_MS);
xtimer_periodic_wakeup(&last_wakeup, interval);
#if defined(MAIN_THREAD_PIN)
gpio_clear(main_pin);
#endif
Expand Down