From 3e29c6b6351bd4abfe497e8e3ae377558f9262e6 Mon Sep 17 00:00:00 2001 From: Josarn Date: Tue, 15 May 2018 14:51:42 +0200 Subject: [PATCH] This commit handles multiple issues of the xtimer implementation. 1. Make it interrupt save. 2. Make periodic_wakeup to skip lost wakeups, solving a priodic race. 3. Remove xtimer_spin_until as this may lead to hanging conditions. 4. Improve/reduce delay of sleeps by introducing XTIMER_SHOOT_OVERHEAD, XTIMER_BACKOFF_OVERHEAD. 5. Implement a tests\xtimer_configuration whcih helps to properly set the values for the xtimer. 6. Introduce a second pin for tests/xtimer_hang to distinguish between worker 1 and 2. --- boards/jiminy-mega256rfr2/include/board.h | 17 +- sys/include/xtimer.h | 52 +++- sys/include/xtimer/implementation.h | 16 +- sys/xtimer/xtimer.c | 67 ++--- sys/xtimer/xtimer_core.c | 159 ++++++------ tests/xtimer_configuration/Makefile | 23 ++ tests/xtimer_configuration/README.md | 141 +++++++++++ tests/xtimer_configuration/main.c | 275 +++++++++++++++++++++ tests/xtimer_configuration/tests/01-run.py | 75 ++++++ tests/xtimer_hang/Makefile | 13 +- tests/xtimer_hang/main.c | 30 ++- 11 files changed, 702 insertions(+), 166 deletions(-) create mode 100644 tests/xtimer_configuration/Makefile create mode 100644 tests/xtimer_configuration/README.md create mode 100644 tests/xtimer_configuration/main.c create mode 100644 tests/xtimer_configuration/tests/01-run.py diff --git a/boards/jiminy-mega256rfr2/include/board.h b/boards/jiminy-mega256rfr2/include/board.h index 5379ffff0d0e..5e4ee7c301dc 100644 --- a/boards/jiminy-mega256rfr2/include/board.h +++ b/boards/jiminy-mega256rfr2/include/board.h @@ -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) + /** @} */ /** @@ -112,6 +108,7 @@ extern "C" { #define BOOTLOADER_CLEARS_WATCHDOG_AND_PASSES_MCUSR 1 /** @} */ + /** * @name CPU clock scale for jiminy-megarfr256rfr2 * diff --git a/sys/include/xtimer.h b/sys/include/xtimer.h index cb2e63036772..20bc170f7b9c 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 10baf7c5d3ba..18801e84ed45 100644 --- a/sys/include/xtimer/implementation.h +++ b/sys/include/xtimer/implementation.h @@ -26,7 +26,7 @@ #ifndef XTIMER_H #error "Do not include this file directly! Use xtimer.h instead" #endif - +#include #include "periph/timer.h" #ifdef __cplusplus @@ -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); diff --git a/sys/xtimer/xtimer.c b/sys/xtimer/xtimer.c index ff6dccf0f492..42d57dacb1cb 100644 --- a/sys/xtimer/xtimer.c +++ b/sys/xtimer/xtimer.c @@ -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; } diff --git a/sys/xtimer/xtimer_core.c b/sys/xtimer/xtimer_core.c index 841155839979..d6b17db7042c 100644 --- a/sys/xtimer/xtimer_core.c +++ b/sys/xtimer/xtimer_core.c @@ -17,6 +17,7 @@ * @author Kaspar Schleiser * @author Joakim NohlgÄrd * @author Josua Arndt + * * @} */ @@ -40,8 +41,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; @@ -63,15 +62,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 */ @@ -135,8 +125,11 @@ void _xtimer_set64(xtimer_t *timer, uint32_t offset, uint32_t long_offset) void _xtimer_set(xtimer_t *timer, uint32_t offset) { + uint32_t old_now; + DEBUG("timer_set(): offset=%" PRIu32 " now=%" PRIu32 " (%" PRIu32 ")\n", offset, xtimer_now().ticks32, _xtimer_lltimer_now()); + if (!timer->callback) { DEBUG("timer_set(): timer has no callback.\n"); return; @@ -144,14 +137,30 @@ 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); + /* 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( (old_now =_xtimer_lltimer_now()) >= max ){ + /* offset shorter then XTIMER_BACKOFF*/ + if(offset < XTIMER_BACKOFF) { + _xtimer_spin(offset); + }else { + /* wait for overflow */ + while (_xtimer_lltimer_now() >= max ){} + /* correct offset */ + old_now = _xtimer_lltimer_mask(_xtimer_lltimer_now() - old_now); + if( old_now <= offset) { + offset -= old_now; + }else { + /* thread was delayed by higher prior task and timer allready past */ + return; + } + } } + + unsigned irq_state = irq_disable(); + + _xtimer_set_absolute(timer, offset, irq_state); } static void _periph_timer_callback(void *arg, int chan) @@ -175,42 +184,31 @@ static inline void _lltimer_set(uint32_t target) timer_set_absolute(XTIMER_DEV, XTIMER_CHAN, _xtimer_lltimer_mask(target)); } -int _xtimer_set_absolute(xtimer_t *timer, uint32_t target) +void _xtimer_set_absolute(xtimer_t *timer, uint32_t offset, unsigned irq_state) { uint32_t now = _xtimer_now(); - int res = 0; - - timer->next = NULL; - /* Ensure that offset is bigger than 'XTIMER_BACKOFF', - * 'target - now' will allways be the offset no matter if target < or > now. - * - * This expects that target was not set too close to now and overrun now, so - * from setting target up until the call of '_xtimer_now()' above now has not - * become equal or bigger than target. - * This is crucial when using low CPU frequencies so reaching the '_xtimer_now()' - * call needs multiple xtimer ticks. - * - * '_xtimer_set()' and `_xtimer_periodic_wakeup()` ensure this by already - * backing off for small values. */ - uint32_t offset = (target - now); - - DEBUG("timer_set_absolute(): now=%" PRIu32 " target=%" PRIu32 " offset=%" PRIu32 "\n", - now, target, offset); + DEBUG("timer_set_absolute(): now=%" PRIu32 " offset=%" PRIu32 "\n", + now, offset); if (offset <= XTIMER_BACKOFF) { /* backoff */ - xtimer_spin_until(target); + irq_restore(irq_state); + if (XTIMER_BACKOFF_OVERHEAD < offset) { + offset -= XTIMER_BACKOFF_OVERHEAD; + } + _xtimer_spin(offset); _shoot(timer); - return 0; + return; } - unsigned state = irq_disable(); if (_is_set(timer)) { _remove(timer); } + /* Ensure timer is not integrated in the list anymore. */ + timer->next = NULL; - timer->target = target; + timer->target = now + offset; timer->long_target = _long_cnt; /* Ensure timer is fired in right timer period. @@ -220,7 +218,7 @@ int _xtimer_set_absolute(xtimer_t *timer, uint32_t target) * If `target < XTIMER_OVERHEAD` the new target will be at the end of this * 32bit period, as `target - XTIMER_OVERHEAD` is a big number instead of a * small at the beginning of the next period. */ - target = target - XTIMER_OVERHEAD; + uint32_t target = timer->target - XTIMER_OVERHEAD; /* 32 bit target overflow, target is in next 32bit period */ if (target < now) { @@ -247,9 +245,7 @@ int _xtimer_set_absolute(xtimer_t *timer, uint32_t target) } } - irq_restore(state); - - return res; + irq_restore(irq_state); } static void _add_timer_to_list(xtimer_t **list_head, xtimer_t *timer) @@ -470,7 +466,7 @@ static void _timer_callback(void) _in_handler = 1; - DEBUG("_timer_callback() now=%" PRIu32 " (%" PRIu32 ")pleft=%" PRIu32 "\n", + DEBUG("_timer_callback() now=%" PRIu32 " (%" PRIu32 ") left=%" PRIu32 "\n", xtimer_now().ticks32, _xtimer_lltimer_mask(xtimer_now().ticks32), _xtimer_lltimer_mask(0xffffffff - xtimer_now().ticks32)); @@ -482,9 +478,7 @@ static void _timer_callback(void) * In this case, we advance to the next timer period. */ _next_period(); - reference = 0; - /* make sure the timer counter also arrived * in the next timer period */ while (_xtimer_lltimer_now() == _xtimer_lltimer_mask(0xFFFFFFFF)) {} @@ -496,13 +490,14 @@ static void _timer_callback(void) /* set our period reference to the current time. */ reference = _xtimer_lltimer_now(); } - 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; @@ -512,31 +507,26 @@ static void _timer_callback(void) /* make sure timer is recognized as being already fired */ timer->target = 0; timer->long_target = 0; + /* Ensure timer is not integrated in the list anymore. */ + timer->next = NULL; + + /* new reference for now and for next timer */ + reference = _xtimer_lltimer_now(); + /* make sure we don't fire too early. But correct time needed to shoot. */ + uint32_t offset = _xtimer_lltimer_mask(_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); } - /* 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; @@ -546,26 +536,17 @@ 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; - } - } } - + /* 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(); + /* wait till overflow */ + while( reference < _xtimer_lltimer_now()){} + reference = 0; + goto overflow; + } _in_handler = 0; /* set low level timer */ diff --git a/tests/xtimer_configuration/Makefile b/tests/xtimer_configuration/Makefile new file mode 100644 index 000000000000..ef251088e8cd --- /dev/null +++ b/tests/xtimer_configuration/Makefile @@ -0,0 +1,23 @@ +include ../Makefile.tests_common + +USEMODULE += xtimer + +# Port and pin configuration for probing with oscilloscope +# Define Test pin for hardware timer interrupt, hardware dependent +# For all ATmega Platforms +CFLAGS += -DDEBUG_TIMER_PORT=PORTF +CFLAGS += -DDEBUG_TIMER_DDR=DDRF +CFLAGS += -DDEBUG_TIMER_PIN=PORTF4 + +# Define test probing pins GPIO API based. +# Port number should be found in port enum e.g in cpu/include/periph_cpu.h +FEATURES_REQUIRED += periph_gpio +# Jiminy probing Pins +CFLAGS += -DSLEEP_PIN_1=GPIO_PIN\(5,5\) +CFLAGS += -DSLEEP_PIN_2=GPIO_PIN\(5,6\) +CFLAGS += -DSLEEP_PIN_3=GPIO_PIN\(5,7\) + +include $(RIOTBASE)/Makefile.include + +test: + ./tests/01-run.py diff --git a/tests/xtimer_configuration/README.md b/tests/xtimer_configuration/README.md new file mode 100644 index 000000000000..264183f31553 --- /dev/null +++ b/tests/xtimer_configuration/README.md @@ -0,0 +1,141 @@ +# xtimer_configuration test application + +This test tests `_xtimer_tsleep32()` and `xtimer_periodic_wakeup` against the +timings of of the xtimer. +And agains the timings of the external host (PC). + +This test can be used to adjust `XTIMER_SHOOT_OVERHEAD`, `XTIMER_OVERHEAD`, +`XTIMER_BACKOFF` and `XTIMER_ISR_BACKOFF`. +This is to ensure that the xtimer interrupt is only as long as neccessary. + +## Usage +``` +make BOARD= flash test +``` + +## Find right values + +1. Configure parameter in board.h as follow: +`XTIMER_ISR_BACKOFF` has to be twice `XTIMER_OVERHEAD` +``` +#define XTIMER_OVERHEAD (50) +#define XTIMER_SHOOT_OVERHEAD (0) +#define XTIMER_BACKOFF_OVERHEAD (1) +#define XTIMER_BACKOFF (2*(XTIMER_OVERHEAD+XTIMER_SHOOT_OVERHEAD)) +#define XTIMER_ISR_BACKOFF (XTIMER_OVERHEAD+XTIMER_SHOOT_OVERHEAD) +``` + +run the test. +If the output is as follows +``` +xxxx-xx-xx xx:xx:xx,xxx - INFO # 240876 243436 slept for 2560 ticks (20480 us) expected 2560 ticks, diff 27 ticks +Increase value for XTIMER_SHOOT_OVERHEAD or XTIMER_OVERHEAD 27 +... +xxxx-xx-xx xx:xx:xx,xxx - INFO # 266798 266845 slept for 47 ticks (376 us) expected 47 ticks, diff 14 ticks +Increase value for XTIMER_BACKOFF_OVERHEAD 14 +``` + +Add the new value for `XTIMER_SHOOT_OVERHEAD` and `XTIMER_BACKOFF_OVERHEAD` to the board.h and set `XTIMER_OVERHEAD` to zero. + +``` + +#define XTIMER_OVERHEAD (0) +#define XTIMER_SHOOT_OVERHEAD (27) +#define XTIMER_BACKOFF_OVERHEAD (14) +#define XTIMER_BACKOFF (2*(XTIMER_OVERHEAD+XTIMER_SHOOT_OVERHEAD)) +#define XTIMER_ISR_BACKOFF (XTIMER_OVERHEAD+XTIMER_SHOOT_OVERHEAD) +``` + +2. run test again + + +If the output is as follows + +``` +xxxx-xx-xx xx:xx:xx,xxx - INFO # 240876 243436 slept for 2560 ticks (20480 us)expected 2560 ticks, diff 33 ticks +Increase value for XTIMER_SHOOT_OVERHEAD or XTIMER_OVERHEAD 33 +``` + +Set `XTIMER_OVERHEAD` to new value. +Set 33 as value for `XTIMER_OVERHEAD` and +`XTIMER_BACKOFF` to `2*(XTIMER_OVERHEAD+XTIMER_SHOOT_OVERHEAD)`. +Also set `XTIMER_ISR_BACKOFF` to `XTIMER_OVERHEAD+XTIMER_SHOOT_OVERHEAD`. + +``` +#define XTIMER_OVERHEAD (33) +#define XTIMER_SHOOT_OVERHEAD (27) +#define XTIMER_BACKOFF_OVERHEAD (14) + +#define XTIMER_BACKOFF (2*(XTIMER_OVERHEAD+XTIMER_SHOOT_OVERHEAD)) +#define XTIMER_ISR_BACKOFF (XTIMER_OVERHEAD+XTIMER_SHOOT_OVERHEAD) +``` + +3. Run the test again and check if there are values which are far off or values which are smaller than expected. + + + +now run `tests/xtimer_usleep_short` and `tests/xtimer_usleep`. + +The expectet result should be similar to this. + +``` +xxxx-xx-xx xx:xx:xx,xxx - INFO # main(): This is RIOT! (Version: 2018.07-devel-197-g413845-josua-VBox-pr/xtimer_core) +xxxx-xx-xx xx:xx:xx,xxx - INFO # Running test 5 times with 9 distinct sleep times +xxxx-xx-xx xx:xx:xx,xxx - INFO # Please hit any key and then ENTER to continue +a +a +xxxx-xx-xx xx:xx:xx,xxx - INFO # Testing Periodic xtimer with absolute timestamp. +xxxx-xx-xx xx:xx:xx,xxx - INFO # 237061 239621 slept for 2560 ticks (20480 us) expected 2560 ticks, diff 0 ticks +xxxx-xx-xx xx:xx:xx,xxx - INFO # 239621 242181 slept for 2560 ticks (20480 us) expected 2560 ticks, diff 0 ticks +xxxx-xx-xx xx:xx:xx,xxx - INFO # 242181 244741 slept for 2560 ticks (20480 us) expected 2560 ticks, diff 0 ticks +xxxx-xx-xx xx:xx:xx,xxx - INFO # 244741 247301 slept for 2560 ticks (20480 us) expected 2560 ticks, diff 0 ticks +xxxx-xx-xx xx:xx:xx,xxx - INFO # 247301 249861 slept for 2560 ticks (20480 us) expected 2560 ticks, diff 0 ticks +xxxx-xx-xx xx:xx:xx,xxx - INFO # 249861 252421 slept for 2560 ticks (20480 us) expected 2560 ticks, diff 0 ticks +xxxx-xx-xx xx:xx:xx,xxx - INFO # 252421 254981 slept for 2560 ticks (20480 us) expected 2560 ticks, diff 0 ticks +xxxx-xx-xx xx:xx:xx,xxx - INFO # 254981 257541 slept for 2560 ticks (20480 us) expected 2560 ticks, diff 0 ticks +xxxx-xx-xx xx:xx:xx,xxx - INFO # 257541 260101 slept for 2560 ticks (20480 us) expected 2560 ticks, diff 0 ticks +Increase value for XTIMER_SHOOT_OVERHEAD or XTIMER_OVERHEAD 0 +xxxx-xx-xx xx:xx:xx,xxx - INFO # Testing Periodic xtimer with relative timestamp. +xxxx-xx-xx xx:xx:xx,xxx - INFO # 285054 285567 slept for 513 ticks (4104 us) expected 511 ticks, diff 2 ticks +xxxx-xx-xx xx:xx:xx,xxx - INFO # 285567 286080 slept for 513 ticks (4104 us) expected 511 ticks, diff 2 ticks +xxxx-xx-xx xx:xx:xx,xxx - INFO # 286080 286593 slept for 513 ticks (4104 us) expected 511 ticks, diff 2 ticks +xxxx-xx-xx xx:xx:xx,xxx - INFO # 286593 287106 slept for 513 ticks (4104 us) expected 511 ticks, diff 2 ticks +xxxx-xx-xx xx:xx:xx,xxx - INFO # 287106 287619 slept for 513 ticks (4104 us) expected 511 ticks, diff 2 ticks +xxxx-xx-xx xx:xx:xx,xxx - INFO # 287619 288132 slept for 513 ticks (4104 us) expected 511 ticks, diff 2 ticks +xxxx-xx-xx xx:xx:xx,xxx - INFO # 288132 288645 slept for 513 ticks (4104 us) expected 511 ticks, diff 2 ticks +xxxx-xx-xx xx:xx:xx,xxx - INFO # 288645 289158 slept for 513 ticks (4104 us) expected 511 ticks, diff 2 ticks +xxxx-xx-xx xx:xx:xx,xxx - INFO # 289158 289671 slept for 513 ticks (4104 us) expected 511 ticks, diff 2 ticks +xxxx-xx-xx xx:xx:xx,xxx - INFO # Testing Periodic xtimer spin. +xxxx-xx-xx xx:xx:xx,xxx - INFO # 313129 313226 slept for 97 ticks (776 us) expected 95 ticks, diff 2 ticks +xxxx-xx-xx xx:xx:xx,xxx - INFO # 313224 313321 slept for 97 ticks (776 us) expected 95 ticks, diff 2 ticks +xxxx-xx-xx xx:xx:xx,xxx - INFO # 313319 313416 slept for 97 ticks (776 us) expected 95 ticks, diff 2 ticks +xxxx-xx-xx xx:xx:xx,xxx - INFO # 313414 313511 slept for 97 ticks (776 us) expected 95 ticks, diff 2 ticks +xxxx-xx-xx xx:xx:xx,xxx - INFO # 313509 313606 slept for 97 ticks (776 us) expected 95 ticks, diff 2 ticks +xxxx-xx-xx xx:xx:xx,xxx - INFO # 313604 313701 slept for 97 ticks (776 us) expected 95 ticks, diff 2 ticks +xxxx-xx-xx xx:xx:xx,xxx - INFO # 313699 313796 slept for 97 ticks (776 us) expected 95 ticks, diff 2 ticks +xxxx-xx-xx xx:xx:xx,xxx - INFO # 313794 313890 slept for 96 ticks (768 us) expected 95 ticks, diff 1 ticks +xxxx-xx-xx xx:xx:xx,xxx - INFO # 313889 313985 slept for 96 ticks (768 us) expected 95 ticks, diff 1 ticks +xxxx-xx-xx xx:xx:xx,xxx - INFO # Testing xtimer sleep 32 spin. +xxxx-xx-xx xx:xx:xx,xxx - INFO # 336566 336614 slept for 48 ticks (384 us) expected 47 ticks, diff 1 ticks +Increase value for XTIMER_BACKOFF_OVERHEAD 1 +xxxx-xx-xx xx:xx:xx,xxx - INFO # 338980 339030 slept for 50 ticks (400 us) expected 49 ticks, diff 1 ticks +xxxx-xx-xx xx:xx:xx,xxx - INFO # 341396 341499 slept for 103 ticks (824 us) expected 96 ticks, diff 7 ticks +xxxx-xx-xx xx:xx:xx,xxx - INFO # 343897 409440 slept for 65543 ticks (524344 us) expected 65536 ticks, diff 7 ticks +xxxx-xx-xx xx:xx:xx,xxx - INFO # 412098 413105 slept for 1007 ticks (8056 us) expected 1000 ticks, diff 7 ticks +xxxx-xx-xx xx:xx:xx,xxx - INFO # 415633 417985 slept for 2352 ticks (18816 us) expected 2345 ticks, diff 7 ticks +xxxx-xx-xx xx:xx:xx,xxx - INFO # 420546 470553 slept for 50007 ticks (400056 us) expected 50000 ticks, diff 7 ticks +xxxx-xx-xx xx:xx:xx,xxx - INFO # 473211 573218 slept for 100007 ticks (800056 us) expected 100000 ticks, diff 7 ticks +xxxx-xx-xx xx:xx:xx,xxx - INFO # 575941 699404 slept for 123463 ticks (987704 us) expected 123456 ticks, diff 7 ticks +xxxx-xx-xx xx:xx:xx,xxx - INFO # Testing XTIMER_ISR_BACKOFF. +xxxx-xx-xx xx:xx:xx,xxx - INFO # thread(): waiting going to sleep! +xxxx-xx-xx xx:xx:xx,xxx - INFO # 704206 744216 slept for 40010 ticks (320080 us) expected 40000 ticks, diff 10 ticks +xxxx-xx-xx xx:xx:xx,xxx - INFO # 704223 744261 slept for 40038 ticks (320304 us) expected 40028 ticks, diff 10 ticks +xxxx-xx-xx xx:xx:xx,xxx - INFO # 704242 744302 slept for 40060 ticks (320480 us) expected 40056 ticks, diff 4 ticks +xxxx-xx-xx xx:xx:xx,xxx - INFO # 704263 744350 slept for 40087 ticks (320696 us) expected 40084 ticks, diff 3 ticks +xxxx-xx-xx xx:xx:xx,xxx - INFO # 704285 744400 slept for 40115 ticks (320920 us) expected 40112 ticks, diff 3 ticks +xxxx-xx-xx xx:xx:xx,xxx - INFO # 704308 744452 slept for 40144 ticks (321152 us) expected 40140 ticks, diff 4 ticks +xxxx-xx-xx xx:xx:xx,xxx - INFO # 704333 744505 slept for 40172 ticks (321376 us) expected 40168 ticks, diff 4 ticks +xxxx-xx-xx xx:xx:xx,xxx - INFO # 704176 744226 slept for 40050 ticks (320400 us) expected 40029 ticks, diff 21 ticks +xxxx-xx-xx xx:xx:xx,xxx - INFO # 704190 744579 slept for 40389 ticks (323112 us) expected 40387 ticks, diff 2 ticks +xxxx-xx-xx xx:xx:xx,xxx - INFO # Test ran for 4267104 us +``` \ No newline at end of file diff --git a/tests/xtimer_configuration/main.c b/tests/xtimer_configuration/main.c new file mode 100644 index 000000000000..8f0453c09df8 --- /dev/null +++ b/tests/xtimer_configuration/main.c @@ -0,0 +1,275 @@ +/* + * Copyright (C) 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 + * directory for more details. + */ + +/** + * @ingroup tests + * @{ + * + * @file + * @brief xtimer_configuration test application + * + * @author Josua Arndt + + * @} + */ + +#include +#include + +#include "xtimer.h" +#include "timex.h" +#include "thread.h" + +#include "periph/gpio.h" + +#define RUNS (5U) +#define SLEEP_TIMES_NUMOF (sizeof(sleep_times) / sizeof(sleep_times[0])) + +static const uint32_t sleep_times[] = { + XTIMER_BACKOFF_OVERHEAD,/* spin and shoot */ + XTIMER_BACKOFF - 1, /* spin adjust XTIMER_BACKOFF_OVERHEAD*/ + XTIMER_BACKOFF + 1, /* no backoff test */ + XTIMER_PERIODIC_SPIN, /* for sure no backoff */ + 1000, + 2345, + 50000, + 100000, + 123456, +}; + +xtimer_ticks32_t start_kept[5 * SLEEP_TIMES_NUMOF]; +xtimer_ticks32_t test_times[5 * SLEEP_TIMES_NUMOF]; +volatile xtimer_ticks32_t end_times[SLEEP_TIMES_NUMOF]; +volatile uint8_t thread_sleeps; + +static char stack[THREAD_STACKSIZE_MAIN]; +static void *_thread(void *arg) +{ + (void)arg; + + puts("thread(): waiting going to sleep!"); + + for ( thread_sleeps = 0; thread_sleeps < SLEEP_TIMES_NUMOF; thread_sleeps++) { + thread_sleep(); + end_times[thread_sleeps] = xtimer_now(); + } + return NULL; +} + +int main(void) +{ +#if defined(SLEEP_PIN_1) + gpio_t sleep_pin_1 = SLEEP_PIN_1; + gpio_init(sleep_pin_1, GPIO_OUT); +#endif +#if defined(SLEEP_PIN_2) + gpio_t sleep_pin_2 = SLEEP_PIN_2; + gpio_init(sleep_pin_2, GPIO_OUT); +#endif +#if defined(SLEEP_PIN_3) + gpio_t sleep_pin_3 = SLEEP_PIN_3; + gpio_init(sleep_pin_3, GPIO_OUT); +#endif + + uint32_t start_test, testtime; + + printf("Running test %u times with %u distinct sleep times\n", RUNS, + (unsigned)SLEEP_TIMES_NUMOF); + puts("Please hit any key and then ENTER to continue"); + getchar(); + start_test = xtimer_now_usec(); + + puts("Testing Periodic xtimer with absolute timestamp."); + /* Testing Periodic with fixed time + * Periodic wakeup has the fastest return time as the absolute value is set + * as timestamp. + */ + xtimer_ticks32_t periode_ticks, start_periode; + uint32_t periode_us; + + /* Testing xtimer_periodic_wakeup usage absolute timestamp*/ + periode_ticks.ticks32 = 5 * XTIMER_PERIODIC_RELATIVE; + periode_us = xtimer_usec_from_ticks(periode_ticks); + + start_periode = xtimer_now(); + for (unsigned n = 0; n < SLEEP_TIMES_NUMOF; n++) { + start_kept[n] = start_periode; + xtimer_periodic_wakeup(&start_periode, periode_us); + test_times[n] = xtimer_now(); + } + for (unsigned n = 0; n < SLEEP_TIMES_NUMOF; n++) { + xtimer_ticks32_t diff = xtimer_diff(test_times[n], start_kept[n]); + printf("%" PRIu32 " %" PRIu32 " slept for %6" PRIu32 " ticks ( %6" PRIu32 + " us) expected %6" PRIu32 " ticks, diff %3" PRIu32 " ticks\n", + start_kept[n].ticks32, test_times[n].ticks32, diff.ticks32, + xtimer_usec_from_ticks(diff), periode_ticks.ticks32, + xtimer_diff(diff, periode_ticks).ticks32); + } + + puts("\nTesting Periodic xtimer with relative timestamp."); + /* Testing xtimer_periodic_wakeup usage relativ timestamp*/ + periode_ticks.ticks32 = XTIMER_PERIODIC_RELATIVE - 1; + periode_us = xtimer_usec_from_ticks(periode_ticks); + + start_periode = xtimer_now(); + for (unsigned n = 0; n < SLEEP_TIMES_NUMOF; n++) { + start_kept[n] = start_periode; + xtimer_periodic_wakeup(&start_periode, periode_us); + test_times[n] = xtimer_now(); + } + for (unsigned n = 0; n < SLEEP_TIMES_NUMOF; n++) { + xtimer_ticks32_t diff = xtimer_diff(test_times[n], start_kept[n]); + printf("%" PRIu32 " %" PRIu32 " slept for %6" PRIu32 " ticks ( %6" PRIu32 + " us) expected %6" PRIu32 " ticks, diff %3" PRIu32 " ticks\n", + start_kept[n].ticks32, test_times[n].ticks32, diff.ticks32, + xtimer_usec_from_ticks(diff), periode_ticks.ticks32, + xtimer_diff(diff, periode_ticks).ticks32); + } + + puts("\nTesting Periodic xtimer spin."); + /* Testing xtimer_periodic_wakeup usage spin*/ + periode_ticks.ticks32 = XTIMER_PERIODIC_SPIN - 1; + periode_us = xtimer_usec_from_ticks(periode_ticks); + + start_periode = xtimer_now(); + for (unsigned n = 0; n < SLEEP_TIMES_NUMOF; n++) { + start_kept[n] = start_periode; + xtimer_periodic_wakeup(&start_periode, periode_us); + test_times[n] = xtimer_now(); + } + for (unsigned n = 0; n < SLEEP_TIMES_NUMOF; n++) { + xtimer_ticks32_t diff = xtimer_diff(test_times[n], start_kept[n]); + printf("%" PRIu32 " %" PRIu32 " slept for %6" PRIu32 " ticks ( %6" PRIu32 + " us) expected %6" PRIu32 " ticks, diff %3" PRIu32 " ticks\n", + start_kept[n].ticks32, test_times[n].ticks32, diff.ticks32, + xtimer_usec_from_ticks(diff), periode_ticks.ticks32, + xtimer_diff(diff, periode_ticks).ticks32); + } + + puts("\nTesting xtimer sleep 32 spin. Bigger diff values expected."); + /* Testing ticks */ + /* will always have some ticks delay as absolute timestamp is calculated + * after calling multiple functions. + * _xtimer_tsleep32 + * _xtimer_tsleep + * _xtimer_set64 + * _xtimer_set + * */ + for (unsigned n = 0; n < SLEEP_TIMES_NUMOF; n++) { + xtimer_ticks32_t start, end, diff, sleep; + sleep.ticks32 = sleep_times[n]; + + start = xtimer_now(); +PORTF |= (1 << PINF7); + xtimer_tsleep32(sleep); +PORTF &= ~(1 << PINF7); + end = xtimer_now(); + + diff = xtimer_diff(end, start); + printf("%" PRIu32 " %" PRIu32 " slept for %6" PRIu32 " ticks ( %6" PRIu32 + " us) expected %6" PRIu32 " ticks, diff %3" PRIu32 " ticks\n", + start.ticks32, end.ticks32, diff.ticks32, xtimer_usec_from_ticks(diff), + sleep.ticks32, xtimer_diff(diff, sleep).ticks32); + } + + puts("\nTesting XTIMER_ISR_BACKOFF."); + /* Testing XTIMER_ISR_BACKOFF */ + /* The delay is not predictabel as multiple xtimer gets fired in one ISR. + * When setting xtimer with xtimer_set_wakeup the delay is smaller + * as less functions are called untill absolute timestamp is calculated + * then for _xtimer_tsleep32. + * _xtimer_set_wakeup + * _xtimer_set + */ + xtimer_t xtimer[SLEEP_TIMES_NUMOF]; + xtimer_ticks32_t start[SLEEP_TIMES_NUMOF]; + uint32_t interval[SLEEP_TIMES_NUMOF]; + + kernel_pid_t pid_main = thread_getpid(); + + kernel_pid_t pid_1 = thread_create(stack, + sizeof(stack), + THREAD_PRIORITY_MAIN - 1, + THREAD_CREATE_STACKTEST, + _thread, + NULL, + "second_thread"); + + /* timer to colide with first timer from other thread */ + periode_ticks.ticks32 = 40000 + XTIMER_ISR_BACKOFF / 2; + interval[SLEEP_TIMES_NUMOF - 2] = xtimer_usec_from_ticks(periode_ticks); + + /* timer to wake up main after all threads are fired */ + periode_ticks.ticks32 = 40000 + (XTIMER_BACKOFF * SLEEP_TIMES_NUMOF * 2); + interval[SLEEP_TIMES_NUMOF - 1] = xtimer_usec_from_ticks(periode_ticks); + + for (unsigned n = 0; n < (SLEEP_TIMES_NUMOF - 2); n++) { + periode_ticks.ticks32 = 40000 + (XTIMER_BACKOFF * n * 2); + interval[n] = xtimer_usec_from_ticks(periode_ticks); + } + + /* wait for a nice ticks number */ + xtimer_ticks32_t sleep = xtimer_now(); + xtimer_ticks32_t next; + next.ticks32 = 1; + + while (sleep.ticks32) { + sleep.ticks32 = sleep.ticks32 / 10; + next.ticks32 *= 10; + } + + sleep = xtimer_diff(next, xtimer_now()); + xtimer_tsleep32(sleep); + + start[SLEEP_TIMES_NUMOF - 2] = xtimer_now(); + xtimer_set_wakeup(&xtimer[SLEEP_TIMES_NUMOF - 2], + interval[SLEEP_TIMES_NUMOF - 2], pid_main); + + start[SLEEP_TIMES_NUMOF - 1] = xtimer_now(); + xtimer_set_wakeup(&xtimer[SLEEP_TIMES_NUMOF - 1], + interval[SLEEP_TIMES_NUMOF - 1], pid_main); + + for (unsigned n = 0; n < (SLEEP_TIMES_NUMOF - 2); n++) { + start[n] = xtimer_now(); + xtimer_set_wakeup(&xtimer[n], interval[n], pid_1); + } + + /* timer colided with first from other thread */ + thread_sleep(); + end_times[SLEEP_TIMES_NUMOF - 2] = xtimer_now(); + + /* wait for all timers to fire */ + thread_sleep(); + end_times[SLEEP_TIMES_NUMOF - 1] = xtimer_now(); + + if ( thread_sleeps != (SLEEP_TIMES_NUMOF - 2)) { + printf("Error: expected %u thread wake ups but only %u happend in time.\n", + (uint8_t)(SLEEP_TIMES_NUMOF-2), thread_sleeps); + puts("Perhaps increasing XTIMER_BACKOFF helps.\n\n"); + } + + for (unsigned n = 0; n < SLEEP_TIMES_NUMOF; n++) { + if ((n == 0) || (n == SLEEP_TIMES_NUMOF - 2)) { + puts("Two timer fired in same ISR, different diff expected"); + } + + xtimer_ticks32_t diff = xtimer_diff(end_times[n], start[n]); + xtimer_ticks32_t interval_ticks = xtimer_ticks_from_usec(interval[n]); + + printf("%" PRIu32 " %" PRIu32 " slept for %6" PRIu32 " ticks ( %6" PRIu32 + " us) expected %6" PRIu32 " ticks, diff %3" PRIu32 " ticks\n", + start[n].ticks32, end_times[n].ticks32, diff.ticks32, + xtimer_usec_from_ticks(diff), interval_ticks.ticks32, + xtimer_diff(diff, interval_ticks).ticks32); + } + + testtime = xtimer_now_usec() - start_test; + printf("Test ran for %" PRIu32 " us\n", testtime); + + return 0; +} diff --git a/tests/xtimer_configuration/tests/01-run.py b/tests/xtimer_configuration/tests/01-run.py new file mode 100644 index 000000000000..0a2c0424bafb --- /dev/null +++ b/tests/xtimer_configuration/tests/01-run.py @@ -0,0 +1,75 @@ +#!/usr/bin/env python3 +# -*- coding: utf-8 -*- +# vim:fenc=utf-8 + +# Copyright (C) 2017 Francisco Acosta +# 2017 Freie UniversitÀt Berlin +# +# 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 +# directory for more details. + +import os +import sys +import time + +US_PER_SEC = 1000000 +INTERNAL_JITTER = 0.05 +EXTERNAL_JITTER = 0.15 + + +class InvalidTimeout(Exception): + pass + + +def testfunc(child): + child.expect(u"Running test (\\d+) times with (\\d+) distinct sleep times") + RUNS = int(child.match.group(1)) + SLEEP_TIMES_NUMOF = int(child.match.group(2)) + try: + child.expect_exact(u"Please hit any key and then ENTER to continue") + child.sendline(u"a") + start_test = time.time() + for m in range(RUNS): + for n in range(SLEEP_TIMES_NUMOF): + child.expect(u"(\\d+) (\\d+) slept for\\s+(\\d+) ticks \(\\s+(\\d+) us\) expected" + + "\\s+(\\d+) ticks, diff\\s+(\\d+) ticks") + start = int(child.match.group(1)) + end = int(child.match.group(2)) + sleep_time = int(child.match.group(3)) + exp = int(child.match.group(5)) + lower_bound = exp - (exp * EXTERNAL_JITTER) + upper_bound = exp + (exp * EXTERNAL_JITTER) + time.sleep(0.001) + + if m == 0 and n == SLEEP_TIMES_NUMOF-1: + print("Increase value for XTIMER_SHOOT_OVERHEAD or " + "XTIMER_OVERHEAD %d" % (sleep_time-exp)) + elif m == 3 and n == 1: + print("Increase value for XTIMER_BACKOFF_OVERHEAD %d" % (sleep_time-exp)) + elif exp/10 < 0 and not (lower_bound < sleep_time < upper_bound): + raise InvalidTimeout("Invalid timeout %d, expected %d, xtimer " + + "compared to host clock has to much deviation" + % (sleep_time, exp)) + + if end - start < exp: + raise InvalidTimeout("To big values %d < %d, xtimer fires too early." + % (sleep_time, exp)) + + testtime = (time.time() - start_test) * US_PER_SEC + child.expect(u"Test ran for (\\d+) us") + exp = int(child.match.group(1)) + lower_bound = exp - (exp * EXTERNAL_JITTER) + upper_bound = exp + (exp * EXTERNAL_JITTER) + if not (lower_bound < testtime < upper_bound): + raise InvalidTimeout("Host timer measured %d us (client measured %d us)" + % (testtime, exp)) + except InvalidTimeout as e: + print(e) + sys.exit(1) + + +if __name__ == "__main__": + sys.path.append(os.path.join(os.environ['RIOTBASE'], 'dist/tools/testrunner')) + from testrunner import run + sys.exit(run(testfunc)) diff --git a/tests/xtimer_hang/Makefile b/tests/xtimer_hang/Makefile index fa58b99b80d6..f009af43c222 100644 --- a/tests/xtimer_hang/Makefile +++ b/tests/xtimer_hang/Makefile @@ -9,15 +9,16 @@ TEST_ON_CI_WHITELIST += all # Port and pin configuration for probing with oscilloscope # Define Test pin for hardware timer interrupt, hardware dependent # For all ATmega Platforms -#CFLAGS += -DDEBUG_TIMER_PORT=PORTF -#CFLAGS += -DDEBUG_TIMER_DDR=DDRF -#CFLAGS += -DDEBUG_TIMER_PIN=PORTF4 +CFLAGS += -DDEBUG_TIMER_PORT=PORTF +CFLAGS += -DDEBUG_TIMER_DDR=DDRF +CFLAGS += -DDEBUG_TIMER_PIN=PORTF4 # Define test probing pins GPIO API based. # Port number should be found in port enum e.g in cpu/include/periph_cpu.h -#FEATURES_REQUIRED += periph_gpio +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 diff --git a/tests/xtimer_hang/main.c b/tests/xtimer_hang/main.c index f661dddd81c8..87522acacc9e 100644 --- a/tests/xtimer_hang/main.c +++ b/tests/xtimer_hang/main.c @@ -38,21 +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( *(uint32_t*)(arg) == TEST_SLEEP_TIME_1) { +#if defined(WORKER_THREAD_PIN_1) + gpio_set(worker_pin_1); + gpio_clear(worker_pin_1); +#endif + } else{ +#if defined(WORKER_THREAD_PIN_2) + gpio_set(worker_pin_2); + gpio_clear(worker_pin_2); #endif + } xtimer_usleep(*(uint32_t *)(arg)); } } @@ -65,8 +79,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,