Skip to content

Commit

Permalink
xtimer: add cooperative clocking feature
Browse files Browse the repository at this point in the history
  • Loading branch information
Hyungsin committed Oct 27, 2017
1 parent 2e0917c commit ea14350
Show file tree
Hide file tree
Showing 3 changed files with 176 additions and 28 deletions.
40 changes: 39 additions & 1 deletion sys/include/xtimer/implementation.h
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,12 @@ extern "C" {
extern volatile uint32_t _xtimer_high_cnt;
#endif

#if (XTIMER_HZ < 1000000ul) && (STIMER_HZ >= 1000000ul)
extern volatile uint32_t prev_s;
extern volatile uint32_t prev_x;
#endif


/**
* @brief IPC message type for xtimer msg callback
*/
Expand All @@ -46,6 +52,16 @@ static inline uint32_t _xtimer_lltimer_now(void)
return timer_read(XTIMER_DEV);
}

#if (XTIMER_HZ < 1000000ul) && (STIMER_HZ >= 1000000ul)
/**
* @brief returns the (masked) low-level timer counter value for STIMER
*/
static inline uint32_t _stimer_lltimer_now(void)
{
return timer_read(STIMER_DEV);
}
#endif

/**
* @brief drop bits of a value that don't fit into the low-level timer.
*/
Expand All @@ -62,7 +78,7 @@ static inline uint32_t _xtimer_lltimer_mask(uint32_t val)
* @internal
*/
uint64_t _xtimer_now64(void);
int _xtimer_set_absolute(xtimer_t *timer, uint32_t target);
int _xtimer_set_absolute(xtimer_t *timer, uint32_t target, uint32_t now);
void _xtimer_set64(xtimer_t *timer, uint32_t offset, uint32_t long_offset);
void _xtimer_set(xtimer_t *timer, uint32_t offset);
void _xtimer_periodic_wakeup(uint32_t *last_wakeup, uint32_t period);
Expand Down Expand Up @@ -105,9 +121,27 @@ static inline uint32_t _xtimer_now(void)
} while (_xtimer_high_cnt != latched_high_cnt);

return latched_high_cnt | now;
#else
#if (XTIMER_HZ < 1000000ul) && (STIMER_HZ >= 1000000ul)
uint32_t diff_s;
uint32_t now_s;

do {
now_s = _stimer_lltimer_now();
if (now_s >= prev_s) {
diff_s = now_s - prev_s;
} else {
diff_s = (0xFFFFFFFF-prev_s) + now_s;
}
} while (diff_s < STIMER_HZ/XTIMER_HZ);

prev_x += diff_s*XTIMER_HZ/STIMER_HZ;
prev_s = now_s;
return prev_x;
#else
return _xtimer_lltimer_now();
#endif
#endif
}

static inline xtimer_ticks32_t xtimer_now(void)
Expand Down Expand Up @@ -142,6 +176,10 @@ static inline void _xtimer_spin(uint32_t offset) {
#else
while ((_xtimer_lltimer_now() - start) < offset);
#endif
#if (XTIMER_HZ < 1000000ul) && (STIMER_HZ >= 1000000ul)
prev_x = _xtimer_lltimer_now();
prev_s = _stimer_lltimer_now();
#endif
}

static inline void _xtimer_tsleep32(uint32_t ticks)
Expand Down
2 changes: 1 addition & 1 deletion sys/xtimer/xtimer.c
Original file line number Diff line number Diff line change
Expand Up @@ -127,7 +127,7 @@ void _xtimer_periodic_wakeup(uint32_t *last_wakeup, uint32_t period) {
}
mutex_lock(&mutex);
DEBUG("xps, abs: %" PRIu32 "\n", target);
_xtimer_set_absolute(&timer, target);
_xtimer_set_absolute(&timer, target, now);
mutex_lock(&mutex);
}
out:
Expand Down
162 changes: 136 additions & 26 deletions sys/xtimer/xtimer_core.c
Original file line number Diff line number Diff line change
Expand Up @@ -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]>
* @}
*/

Expand All @@ -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;
Expand All @@ -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);
Expand All @@ -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;
Expand Down Expand Up @@ -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
}
}

Expand All @@ -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 */
Expand Down Expand Up @@ -213,7 +257,6 @@ int _xtimer_set_absolute(xtimer_t *timer, uint32_t target)
}
}
}

irq_restore(state);

return res;
Expand Down Expand Up @@ -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;
}
Expand Down Expand Up @@ -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;

Expand Down Expand Up @@ -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;
Expand All @@ -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;
}

Expand All @@ -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 {
Expand All @@ -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);
}

0 comments on commit ea14350

Please sign in to comment.