forked from RIOT-OS/RIOT
-
Notifications
You must be signed in to change notification settings - Fork 3
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
xtimer: add cooperative clocking feature
- Loading branch information
Hyungsin
committed
Oct 27, 2017
1 parent
2e0917c
commit ea14350
Showing
3 changed files
with
176 additions
and
28 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -12,6 +12,7 @@ | |
* @brief xtimer core functionality | ||
* @author Kaspar Schleiser <[email protected]> | ||
* @author Joakim Nohlgård <[email protected]> | ||
* @author Hyung-Sin Kim <[email protected]> | ||
* @} | ||
*/ | ||
|
||
|
@@ -35,6 +36,12 @@ static volatile uint32_t _long_cnt = 0; | |
volatile uint32_t _xtimer_high_cnt = 0; | ||
#endif | ||
|
||
#if (XTIMER_HZ < 1000000ul) && (STIMER_HZ >= 1000000ul) | ||
volatile uint32_t prev_s = 0xffffffff; | ||
volatile uint32_t prev_x = 0xffffffff; | ||
bool init = false; | ||
#endif | ||
|
||
static inline void xtimer_spin_until(uint32_t value); | ||
|
||
static xtimer_t *timer_list_head = NULL; | ||
|
@@ -46,7 +53,7 @@ static void _add_timer_to_long_list(xtimer_t **list_head, xtimer_t *timer); | |
static void _shoot(xtimer_t *timer); | ||
static void _remove(xtimer_t *timer); | ||
static inline void _lltimer_set(uint32_t target); | ||
static uint32_t _time_left(uint32_t target, uint32_t reference); | ||
static uint32_t _time_left(uint32_t target, uint32_t reference, uint32_t now); | ||
|
||
static void _timer_callback(void); | ||
static void _periph_timer_callback(void *arg, int chan); | ||
|
@@ -64,27 +71,50 @@ static inline void xtimer_spin_until(uint32_t target) { | |
#endif | ||
while (_xtimer_lltimer_now() > target); | ||
while (_xtimer_lltimer_now() < target); | ||
#if (XTIMER_HZ < 1000000ul) && (STIMER_HZ >= 1000000ul) | ||
prev_x = _xtimer_lltimer_now(); | ||
prev_s = _stimer_lltimer_now(); | ||
#endif | ||
} | ||
|
||
void xtimer_init(void) | ||
{ | ||
/* initialize low-level timer */ | ||
timer_init(XTIMER_DEV, XTIMER_HZ, _periph_timer_callback, NULL); | ||
|
||
#if (XTIMER_HZ < 1000000ul) && (STIMER_HZ >= 1000000ul) | ||
/* initialize low-level timer for STIMER to support slow XTIMER operation. | ||
* for this setup, XTIMER is expected to slow (e.g., 32 kHz) and | ||
* STIMER must be faster than XTIMER (e.g., 8 MHz or 48 MHz). | ||
*/ | ||
timer_init(STIMER_DEV, STIMER_HZ, _periph_timer_callback, NULL); | ||
prev_x = _xtimer_lltimer_now(); | ||
prev_s = _stimer_lltimer_now(); | ||
#endif | ||
/* register initial overflow tick */ | ||
_lltimer_set(0xFFFFFFFF); | ||
} | ||
|
||
#if (XTIMER_HZ < 1000000ul) && (STIMER_HZ >= 1000000ul) | ||
/** | ||
* @brief time difference of STIMER | ||
*/ | ||
static uint32_t _stimer_diff(uint32_t prev, uint32_t now) { | ||
if (now >= prev) { | ||
return now - prev; | ||
} else { | ||
return (0xFFFFFFFF-prev) + now; | ||
} | ||
} | ||
#endif | ||
|
||
static void _xtimer_now_internal(uint32_t *short_term, uint32_t *long_term) | ||
{ | ||
uint32_t before, after, long_value; | ||
|
||
/* loop to cope with possible overflow of _xtimer_now() */ | ||
do { | ||
before = _xtimer_now(); | ||
long_value = _long_cnt; | ||
after = _xtimer_now(); | ||
|
||
} while(before > after); | ||
|
||
*short_term = after; | ||
|
@@ -142,8 +172,20 @@ void _xtimer_set(xtimer_t *timer, uint32_t offset) | |
_shoot(timer); | ||
} | ||
else { | ||
uint32_t target = _xtimer_now() + offset; | ||
_xtimer_set_absolute(timer, target); | ||
#if (XTIMER_HZ < 1000000ul) && (STIMER_HZ >= 1000000ul) | ||
if (!init) { | ||
prev_x = _xtimer_lltimer_now(); | ||
prev_s = _stimer_lltimer_now(); | ||
init = true; | ||
} | ||
prev_x = _xtimer_now(); | ||
uint32_t target = prev_x + offset; | ||
_xtimer_set_absolute(timer, target, prev_x); | ||
#else | ||
uint32_t now = _xtimer_now(); | ||
uint32_t target = now + offset; | ||
_xtimer_set_absolute(timer, target, now); | ||
#endif | ||
} | ||
} | ||
|
||
|
@@ -168,13 +210,15 @@ 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) | ||
int _xtimer_set_absolute(xtimer_t *timer, uint32_t target, uint32_t now) | ||
{ | ||
uint32_t now = _xtimer_now(); | ||
#if (XTIMER_HZ < 1000000ul) && (STIMER_HZ >= 1000000ul) | ||
#else | ||
now = _xtimer_now(); | ||
#endif | ||
int res = 0; | ||
|
||
DEBUG("timer_set_absolute(): now=%" PRIu32 " target=%" PRIu32 "\n", now, target); | ||
|
||
timer->next = NULL; | ||
if ((target >= now) && ((target - XTIMER_BACKOFF) < now)) { | ||
/* backoff */ | ||
|
@@ -213,7 +257,6 @@ int _xtimer_set_absolute(xtimer_t *timer, uint32_t target) | |
} | ||
} | ||
} | ||
|
||
irq_restore(state); | ||
|
||
return res; | ||
|
@@ -286,10 +329,8 @@ void xtimer_remove(xtimer_t *timer) | |
irq_restore(state); | ||
} | ||
|
||
static uint32_t _time_left(uint32_t target, uint32_t reference) | ||
static uint32_t _time_left(uint32_t target, uint32_t reference, uint32_t now) | ||
{ | ||
uint32_t now = _xtimer_lltimer_now(); | ||
|
||
if (now < reference) { | ||
return 0; | ||
} | ||
|
@@ -425,13 +466,19 @@ static void _next_period(void) | |
_select_long_timers(); | ||
} | ||
|
||
|
||
/** | ||
* @brief main xtimer callback function | ||
*/ | ||
static void _timer_callback(void) | ||
{ | ||
uint32_t next_target; | ||
uint32_t reference; | ||
uint32_t now; | ||
#if (XTIMER_HZ < 1000000ul) && (STIMER_HZ >= 1000000ul) | ||
uint32_t now_s; | ||
uint32_t diff_s; | ||
#endif | ||
|
||
_in_handler = 1; | ||
|
||
|
@@ -461,12 +508,32 @@ static void _timer_callback(void) | |
/* set our period reference to the current time. */ | ||
reference = _xtimer_lltimer_now(); | ||
} | ||
|
||
now = reference; | ||
#if (XTIMER_HZ < 1000000ul) && (STIMER_HZ >= 1000000ul) | ||
prev_x = now; | ||
#endif | ||
overflow: | ||
#if (XTIMER_HZ < 1000000ul) && (STIMER_HZ >= 1000000ul) | ||
prev_s = _stimer_lltimer_now(); | ||
#endif | ||
|
||
/* 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)) { | ||
while (timer_list_head && (_time_left(_xtimer_lltimer_mask(timer_list_head->target), reference, now) < XTIMER_ISR_BACKOFF)) { | ||
/* make sure we don't fire too early */ | ||
while (_time_left(_xtimer_lltimer_mask(timer_list_head->target), reference)) {} | ||
while (_time_left(_xtimer_lltimer_mask(timer_list_head->target), reference, now)) { | ||
/* update current time */ | ||
#if (XTIMER_HZ < 1000000ul) && (STIMER_HZ >= 1000000ul) | ||
do { | ||
now_s = _stimer_lltimer_now(); | ||
diff_s = _stimer_diff(prev_s, now_s); | ||
} while (diff_s < STIMER_HZ/XTIMER_HZ); | ||
now = now + diff_s*XTIMER_HZ/STIMER_HZ; | ||
prev_x = now; | ||
prev_s = now_s; | ||
#else | ||
now = _xtimer_lltimer_now(); | ||
#endif | ||
} | ||
|
||
/* pick first timer in list */ | ||
xtimer_t *timer = timer_list_head; | ||
|
@@ -480,17 +547,34 @@ static void _timer_callback(void) | |
|
||
/* fire timer */ | ||
_shoot(timer); | ||
|
||
/* update current time after executing callback */ | ||
#if (XTIMER_HZ < 1000000ul) && (STIMER_HZ >= 1000000ul) | ||
now_s = _stimer_lltimer_now(); | ||
diff_s = _stimer_diff(prev_s, now_s); | ||
if (diff_s >= STIMER_HZ/XTIMER_HZ) { | ||
now = now + diff_s*XTIMER_HZ/STIMER_HZ; | ||
prev_x = now; | ||
prev_s = now_s; | ||
} | ||
#else | ||
now = _xtimer_lltimer_now(); | ||
#endif | ||
} | ||
|
||
/* possibly executing all callbacks took enough | ||
* time to overflow. In that case we advance to | ||
* next timer period and check again for expired | ||
* timers.*/ | ||
if (reference > _xtimer_lltimer_now()) { | ||
if (reference > now) { | ||
DEBUG("_timer_callback: overflowed while executing callbacks. %i\n", | ||
timer_list_head != NULL); | ||
_next_period(); | ||
reference = 0; | ||
now = _xtimer_lltimer_now(); | ||
#if (XTIMER_HZ < 1000000ul) && (STIMER_HZ >= 1000000ul) | ||
prev_x = now; | ||
#endif | ||
goto overflow; | ||
} | ||
|
||
|
@@ -499,20 +583,39 @@ static void _timer_callback(void) | |
next_target = timer_list_head->target - XTIMER_OVERHEAD; | ||
|
||
/* make sure we're not setting a time in the past */ | ||
if (next_target < (_xtimer_lltimer_now() + XTIMER_ISR_BACKOFF)) { | ||
if (next_target < (now + XTIMER_ISR_BACKOFF)) { | ||
now = _xtimer_lltimer_now(); | ||
#if (XTIMER_HZ < 1000000ul) && (STIMER_HZ >= 1000000ul) | ||
prev_x = now; | ||
#endif | ||
goto overflow; | ||
} | ||
_in_handler = 0; | ||
|
||
/* set low level timer */ | ||
_lltimer_set(next_target); | ||
} | ||
else { | ||
else if (overflow_list_head != NULL) { | ||
/* there's no timer planned for this timer period */ | ||
/* schedule callback on next overflow */ | ||
/* schedule callback on next overflow */ | ||
next_target = _xtimer_lltimer_mask(0xFFFFFFFF); | ||
uint32_t now = _xtimer_lltimer_now(); | ||
|
||
|
||
/* update current time */ | ||
#if (XTIMER_HZ < 1000000ul) && (STIMER_HZ >= 1000000ul) | ||
now_s = _stimer_lltimer_now(); | ||
diff_s = _stimer_diff(prev_s, now_s); | ||
now = now + diff_s*XTIMER_HZ/STIMER_HZ; | ||
#else | ||
now = _xtimer_lltimer_now(); | ||
#endif | ||
/* check for overflow again */ | ||
if (now < reference) { | ||
_next_period(); | ||
reference = 0; | ||
now = _xtimer_lltimer_now(); | ||
#if (XTIMER_HZ < 1000000ul) && (STIMER_HZ >= 1000000ul) | ||
prev_x = now; | ||
#endif | ||
goto overflow; | ||
} | ||
else { | ||
|
@@ -522,13 +625,20 @@ static void _timer_callback(void) | |
while (_xtimer_lltimer_now() >= now) {} | ||
_next_period(); | ||
reference = 0; | ||
now = _xtimer_lltimer_now(); | ||
#if (XTIMER_HZ < 1000000ul) && (STIMER_HZ >= 1000000ul) | ||
prev_x = now; | ||
#endif | ||
goto overflow; | ||
} | ||
} | ||
} | ||
_in_handler = 0; | ||
|
||
_in_handler = 0; | ||
/* set low level timer */ | ||
_lltimer_set(next_target); | ||
} else { | ||
/* No need to set any timer */ | ||
_in_handler = 0; | ||
} | ||
|
||
/* set low level timer */ | ||
_lltimer_set(next_target); | ||
} |