Hi all,

I guess it is time to coordinate improving RIOT's timers - again.

IMO xtimer is a dead end. It was designed with good intentions, but unfortunately with not much real-world experience. It has also (d)evolved into a complex and inflexible #ifdef-mess...

Here's what I think is bad about it:

- it allows only one type of base timer. On most platforms it tries to use one 1MHz periph/timer. there are PR's and hacks to make it use e.g., 32KHz RTTs or low power timers, but they didn't make it to master. The API basically enforces 1us ticks, and mapping unchanged code bases to very different timers is just too error prone. Also, the API would require choosing at compile time, which is just too inflexible.

- xtimer still doesn't support pm

- by forcing 1us time, xtimer also requires 64bit time functions in order to support > 2**32us (~71min) timeouts

- xtimer is not ISR safe (xtimer_now() with <16bit base timers can break)

- xtimer_t is quite large (20b on 32bit platforms)

- xtimer's code has become very complex due to us/tick conversion and the many #ifdefs that grew into it

Here's what I propose:

- re-write from scratch

- in the API, allow "instances", e.g.:

timer_now(instance); timer_set(instance, timer_object);

where an "instance" provides state and function pointers.

- implement backends for periph/timer, rtt, rtc, lptim, systick, whatever

- provide global instance aliases that every board provides, e.g., "TIMER_USEC", "TIMER_MSEC", "TIMER_SEC". Map those to available timer backends. Make it possible to enable / disable them as needed.

- allow application specific timers to use the same API (e.g., allow setting up a very high speed timer, which can be used alongside "default" timers)

- provide stackable "converters"   e.g., if there's no MSEC resolution low power timer that can be mapped to "TIMER_MSEC", use "TIMER_USEC", but transparently convert all values   or if periph/timer doesn't support 32bit, create a "timer instance" that transparently handles the extension.   Maybe stack those (e.g., if the rtt backend only supports 1024hz and 16bit, stack a 1024 to 1000 converter on that, stack a 16bit to 32bit on that and map the result to "TIMER_MSEC". this would lead to re-usable components that can also be individually tested (think unittests using a software-controlled fake timer).

- provide either clear convention (e.g., TIMER_USEC stops when sleeping, unless a timer using it is set, TIMER_MSEC keeps running in low power mode) or controlling API (e.g., "timer_block_sleep(TIMER_USEC)" for behaviour in sleep modes

- try to slim down the timer struct (if possible)

- drop the 64bit API (usec resolution over 64bit range is unrealistic on real hardware. if, for larger ranges, TIMER_MSEC or TIMER_SEC can be used, the range that 64bit provides is not needed anymore. thus all the expensive 64bit arithmetic can be dropped)

- consider new insights like the clock domain issue fixed by [1]

- obviously, avoid long-standing xtimer bugs like the ISR unsafeness

The main point I would like to push is the introduction of "timer instances", which would basically make the timer interface object oriented. This would allow having multiple implementations with different tradeoffs without changing actual application code.

What do you think?


[1] https://github.com/RIOT-OS/RIOT/pull/7053


to make matters even more complicated:

Timers and PWM are the same thing on most platforms. It would be nice if one could choose whether to use interrupts or PWM pins or both per timer. (e.g. using both the interrupt and a PWM pin enables one to bitbang the protocol of WS2812 LEDs very elegantly)

This is probably impossible to get right cross-platform.

Sorry, PyroPeter


Yepp :slight_smile: Thats why we separated their interfaces… But there is no restriction in reusing code internally when implementing both periph/timer and periph/pwm interfaces… Cheers, Hauke


I think that giving more precise APIs and removing the low level implementation detail from it is a good thing. It means that the library can adapt to the current hardware and PM constraints.

I have questions regarding the transition to new timers. As several modules have dependencies to xtimer both in headers or source files, they would need to be adapted to the new api.

Do you think that the new API could be implemented on top of the current xtimer with some glue code as a temporary solution ? This would allow switching modules to the new API even if some boards still implement it with xtimer.

And just for precision regarding "removing 64bit time functions". You want to remove all the 64bit arguments in timer API ? This would mean that, when you want to say sleep "6hours" or "1 week", the developer should say he wants to use a 1second timer, and not the μs one. Then, if there is only a μs timer, it would internally still be implemented as storing a 64b value somehow but at least its removed from the API.

I think its a good idea to put this in the API, because it gives more information to the timer library on what the developer wants to do. In case where I have timers like "sleep(30 * USECS_PER_SEC)" in reality the timer does not need to even be precise at all.

The constraints I see with something like this, is give some precision control functionalities.

I would maybe want to control the trigger time offset if I repeat this sleep. Its not a problem if it has a 0.5 seconds offset, but I would not like to have this offset adding over time. So be sure that I can easily do, "trigger every N seconds, but do not drift over time".

Also, there may be cases where I would like to say "wake me up before XXX" instead of "wake me up after". This could allow adapting for the last seconds with a higher precision timer if I need a more precise trigger time than the timer gives me.



There might also be some kind of offset setter for the now() return value required, when considering that this new timer API should also be backended by RTC and/or RTT. This can come in handy to set the system time with time synchronization protocols like (S)NTP.

Cheers, Martine