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

[WIP, RFC] doc/memos: Added RDM on high level timer API requirements and common features #12970

Closed
wants to merge 22 commits into from
Closed
Changes from all commits
Commits
Show all changes
22 commits
Select commit Hold shift + click to select a range
d53b2fc
doc/memos: Added draft RDM on high level timer API
maribu Dec 16, 2019
8f4dd44
fixup! doc/memos: Added draft RDM on high level timer API
maribu Dec 17, 2019
65b1943
fixup! doc/memos: Added draft RDM on high level timer API
maribu Dec 17, 2019
d8cf7c9
fixup! doc/memos: Added draft RDM on high level timer API
maribu Dec 17, 2019
169723c
fixup! doc/memos: Added draft RDM on high level timer API
maribu Dec 17, 2019
d3aa905
fixup! doc/memos: Added draft RDM on high level timer API
maribu Dec 17, 2019
bd57915
fixup! doc/memos: Added draft RDM on high level timer API
maribu Dec 17, 2019
2f553b7
fixup! doc/memos: Added draft RDM on high level timer API
maribu Dec 17, 2019
ff3dcdc
fixup! doc/memos: Added draft RDM on high level timer API
maribu Dec 17, 2019
e796c32
fixup! doc/memos: Added draft RDM on high level timer API
maribu Dec 17, 2019
f0b75fe
fixup! doc/memos: Added draft RDM on high level timer API
maribu Dec 17, 2019
148be8b
fixup! doc/memos: Added draft RDM on high level timer API
maribu Jun 4, 2020
4b71ec1
fixup! doc/memos: Added draft RDM on high level timer API
maribu Jun 4, 2020
aebb1f3
fixup! doc/memos: Added draft RDM on high level timer API
maribu Jul 23, 2020
2f0046d
Apply suggestions from code review
kaspar030 Sep 11, 2020
b259522
Update doc/memos/rdm-draft-buschsieweke-timer-api.md
kaspar030 Sep 11, 2020
f5fae3e
Update doc/memos/rdm-draft-buschsieweke-timer-api.md
kaspar030 Sep 11, 2020
6c99879
Update doc/memos/rdm-draft-buschsieweke-timer-api.md
maribu Sep 20, 2020
1af6249
Apply suggestions from code review
maribu Sep 20, 2020
8e597ca
fixup! doc/memos: Added draft RDM on high level timer API
maribu Sep 20, 2020
8092ecd
Update doc/memos/rdm-draft-buschsieweke-timer-api.md
maribu Sep 21, 2020
6e30f5c
fixup! doc/memos: Added draft RDM on high level timer API
maribu Sep 21, 2020
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
259 changes: 259 additions & 0 deletions doc/memos/rdm-draft-buschsieweke-timer-api.md
@@ -0,0 +1,259 @@
- RDM: 2
- Title: High Level Timer API: Survey of Common Features and Requirement Analysis
- Author: Alexandre Abadie, José Alamos, Emmanuel Baccelli, Marian Buschsieweke,
Niels Gandraß, Martine Lenders, Michel Rottleuthner, Kaspar Schleiser,
Thomas C. Schmidt, Benjamin Valentin,
- Status: Active
- Type: Consensus
- Created: October 2018

## Abstract

This document presents use cases, basic requirements, and goals for a high level
timer API to be used in RIOT.

## Status

This document is a product of the community of RIOT maintainers, and aims to
represent the consensus within this community. The content of this document is
licensed with a Creative Commons CC-BY-SA license.

## Terminology

This document uses the [RFC2119](https://www.ietf.org/rfc/rfc2119.txt)
terminology and the following acronyms:

- RTC: Real Time Clock
- RTT: Real Time Timer

Throughout this document, the following terms should be understood as:

- Real Time Clock: A low power hardware clock that represents time broken up
into year, month, day, hour, minute, and second. It has a fixed resolution of
1 second and provides all mandatory features of the [RTC API](http://api.riot-os.org/group__drivers__periph__rtc.html)
- Real Time Timer: A low power hardware clock that is very similar to the RTC,
but operations on a time representation using a single integer. Unlike the
RTC, the resolution can differ from 1 second. RTTs provide all mandatory
features of the [RTT API](http://api.riot-os.org/group__drivers__periph__rtt.html)
- Software Timer: A mechanism to run code at a given point in time in the
future. The number of concurrently usable software timers is only limited by
their memory requirements and the load a system can handle.
- System Time: Common time reference of the local device, usually as a single
integer counting the clock ticks passed since some reference point

# 1. Introduction

## Motivation

Many applications and use cases for using timer hardware for various things
ranging from measuring time, delaying the execution of a thread, or running
code at specific points in time exists. Convenience, portability and ease of
use are therefore essential for a user friendly embedded OS.

Actual timer hardware however is difficult to use. One main reason is that
timer hardware can differ significantly starting with the clock frequency,
the timer width (8 bit, 16 bit, 24 bit, and 32 bit are all common), and their
time representation. Another reason is that hardware timers only allow setting
maribu marked this conversation as resolved.
Show resolved Hide resolved
a (very) small number of alarm callbacks.

A high level timer API is needed to provide convenient and portable access to
the hardware features and is essential when multiple virtual timers need to
be provided on top of a single hardware timer.

## Related Timer APIs and Commonly Provided Features

The following list of related APIs provided by other operating systems targeting
constraint devices of class C2 according to RFC 7228 terminology has been
studied:

- FreeRTOS:
- [Software Timer Feature description](https://www.freertos.org/RTOS-software-timer.html)
- [Software Timer API reference](https://www.freertos.org/FreeRTOS-Software-Timer-API-Functions.html)
- [Task Control API reference](https://www.freertos.org/a00112.html)
- [`xTaskGetTickCount()` API reference](https://www.freertos.org/a00021.html#xTaskGetTickCount)
- Zephyr:
- [Timer API documentation](https://docs.zephyrproject.org/1.12.0/kernel/timing/timers.html)
- [Kernel Clocks documentation](https://docs.zephyrproject.org/1.9.0/kernel/timing/clocks.html)
- Mbed OS:
- [Timer API documentation](https://os.mbed.com/docs/mbed-os/v5.14/apis/timer.html)
- [Ticker API documentation](https://os.mbed.com/docs/mbed-os/v5.14/apis/ticker.html)
- [`wait()` API documentation](https://os.mbed.com/docs/mbed-os/v5.14/apis/wait.html)
- Contiki
- [Wiki page on timers](https://github.com/contiki-os/contiki/wiki/Timers)
- TinyOS:
- [Timer header file](https://github.com/tinyos/tinyos-main/blob/master/tos/lib/timer/Timer.nc)
- [TOSThreads Tutorial](http://tinyos.stanford.edu/tinyos-wiki/index.php/TOSThreads_Tutorial)
- Mynewt:
- [OS Time API reference](https://mynewt.apache.org/latest/os/core_os/time/os_time.html)
- Xtimer, RIOT's (at the time of writing) current timer API
- [xtimer API documentation](http://api.riot-os.org/group__sys__xtimer.html)
- ztimer, RIOT's alternative timer API that aims to eventually replace xtimer
- [ztimer API documentation](https://api.riot-os.org/group__sys__ztimer.html)

maribu marked this conversation as resolved.
Show resolved Hide resolved
The following common feature sets provided by competing operating systems have
been identified:

- APIs related to Software Timers
- A given callback function is executed after a specific point in time
(in the future) has reached
- Some OS support both one-shot timers and periodic timers
- Depending on the OS callbacks are executed either in IRQ context or by
a specific thread
- APIs related to Time Tracking
- Getting the current system time
- Quantify time spans between to events
- APIs used to delay the execution flow

| Feature | FreeRTOS | Zephyr | Mbed OS | Contiki | TinyOS | Mynewt | xtimer | ztimer |
|:------------------------- |:------------- |:------------- |:--------- |:------------- |:--------- |:--------- |:--------- |:----------------- |
| Software Timers | ✓ | ✓ | ✓ | ✓ | ✓ | ✗ | ✓ | ✓ |
| Periodic Software Timers | ✓ | ✓ | ✓ | ● (2) | ✓ | ✗ | ● (6) | ● (6) |
| Context of Timer Callback | Thread | ? | ? | Both? (3) | ? | n.a. | Interrupt | Interrupt |
| System Time | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ● (8) |
| System Time Width | `TickType_t` | 32bit | 64 bit |`clock_time_t` | ? | 96 bit (7)| 64 | 64 (10) |
| System Time Unit | "Tick" | hw dependent | µs | hw dependent | ? | µs | µs | clock dependent |
| Portable System Time (9) | ✗ | ✗ | ✓ | ✗ | ? | ✓ | ✓ | ✗ |
| Delays | ✓ | ● (1) | ✓ | ● (4) | ✓ (5) | ✓ | ✓ | ✓ |

maribu marked this conversation as resolved.
Show resolved Hide resolved
✓ = 1st level API support, ● = supported by combining APIs, ✗ = no support

1. Threads can wait until a software timer fires. Software timers accept NULL
as empty callback. Combined, this can be used to delay execution.
2. Contiki provides with `ctimer_reset()`/`etimer_reset()` which restarts the
timer from the previous expiration time, allowing to easily achieve
periodic callbacks.
3. The Rtimer library seems to run the callbacks in IRQ context and the Ctimer
library in thread context.
4. The Etimer library posts events on timer expiration and a thread can block
until a specific event has been posted. Combined, this can be used to
delay execution
5. Only available when using TOSTThreads
6. `xtimer_periodic_wakeup()`/`ztimer_periodic_wakeup()` can be used, but
requires a dedicated thread allocated for the periodic task
7. Mynewt uses a `struct` with a members `tv_sec` (64 bit) and `tv_usec`
(32 bit)
8. `ztimer_now()` returns the current time of the given clock. This can be used
as system timer
9. "Portable System Time" means that two nodes can exchange time stamps over
network and they still refer to the same point in time, assuming the clocks
have been synchronized.
10. ztimer optionally keeps each clock's time in 64bit (module ztimer_now64)

kaspar030 marked this conversation as resolved.
Show resolved Hide resolved
# 2. Requirements

The following lists of requirements consist of qualitative and quantitative
requirements. For the quantitative requirements, any improvement will enable
more scenarios to use the timer API. Thus, no meaningful lower bounds can be
specified. Instead, a set of benchmarks will be developed that allow to
continuously improve the performance and to detect any possible regressions of
future changes.

## Feature Requirements

- Software Timers
- Upper bound for number of timers only given by available resources
- APIs to set and abort timers
- Functions for setting timers with both absolute time and relative time
- Support for periodic timers (support via explicit calls to some
`timer_reset()` like in Contiki is fine)
- Long term low resolution software timers that play well with power management
- Short term high resolution software timers
maribu marked this conversation as resolved.
Show resolved Hide resolved
- High resolution real time timers
- Timers that expire at a specific absolute portable time stamp (e.g. a UNIX timestamp)
- Time synchronization (when enabled and used) must update the timeouts, if needed
- Justification: E.g. sensor networks for pressure fluctuation monitoring need measurements to be performed synchronized with high accuracy.
- Note: Real time timers should be optional (so that only applicaitons actually using them pay the overhead in terms of ROM and runtime overhead).
- Delay of program execution
- API to block calling thread for long periods and allows other threads to run
- Long term delays must play well with power management
- Highly precise short term delays (e.g. for bit banging)
- System time
- APIs to get the current system time in reasonable, hardware independent
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

As discussed offline with @kaspar030, a function for setting an absolute timeout time is needed for most time sensitive MAC layers (LoRaWAN, TSCH).

Something like:

high_level_timer_set_absolute(&timer_struct, timestamp);

Usually the slot times for these protocols are known before hand.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done

units
- At least: seconds, milli seconds, micro seconds, nano seconds.
- Justification: Sensor networks e.g. for monitoring pressure fluctuations require the system time of nodes to be synchronized with high accuracy. E.g. the PTP protocol can provide sub microsecond synchronization accuracy. Thus, there are use cases requiring resolutions below micro seconds and there is hardware providing these resolutions and synchronization accuracy.
- UNIX epoch as common reference point for high interoperability
- Support for 64 bit time stamps
- Justification: Devices running for decades might want to log events
with milli second resolution timestamps on flash / sd card / ...
- Note: The rest of the API can be 32 bit, only for time stamps 64 bit
support is mandatory
- Optional feature to auto-initialize system time from RTC/RTT during boot
- APIs to allow implementing network time synchronization
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

+1 here

With LoRaWAN there are some commands to get the absolute timestamp from the network server (with < 10ms of accuracy). Implementing such an API for LoRaWAN would be straightforward.

maribu marked this conversation as resolved.
Show resolved Hide resolved
- The synchronization mechanism should be highly accurate. (E.g. consider the mechanism would read the value from hardware, modify it by adding a time offset determined by some network time synchronization mechanism to it, and store the new value in the hardware: The actually applied offset would be lower by the time that has passed between the read from the hardware and the write to it, reducing accuracy. The actual implementation should be smarter.)
- Only the system time (the portable 64 bit time stamps) need to be synchronized.
- Utilities for convenience
- Timer utilities
- Timer that send a message upon expiration
- Timer that post an event upon expiration (to allow execution in thread context)
- Timer that wakes up a thread upon expiration
- Timeout utilities
- Timeout for receiving a message
- Timeout for locking a mutex
- System time utilities
- Functions to convert between different time formats, e.g. UNIX and GPS timestamps

maribu marked this conversation as resolved.
Show resolved Hide resolved
## Real-Time Requirements

- The implementation must only disable IRQs for short periods of time. More
specifically: With *n* software timers used in parallel, the longest period of
time with interrupts disabled be at most *O(n)*
- The implementation must aim for a minimized number of IRQs caused
- The implementation must aim for keeping its ISRs short; with *n* software
timers used in parallel, the ISR should run for at most *O(n)*
- The implementation should provide means to execute callbacks in thread
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this is IMO out of scope

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

... or should maybe go to the utility functionality...

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

or should maybe go to the utility functionality

Yep. It is already in the feature requirements below utilities. To me, this is an important thing to improve real time capabilities in a convenient way. (Obviously, one could just write their own handlers to call event_post(); but I think having a utility function for that can be much easier to use.)

I'm not sure if it makes sense to have this written twice. To me this is both a feature requirement and a realtime requirement; so I just added it twice.

context

Justification: Real time capable systems require an upper bound for the latency
a high level timer implementation provides. For most application - if not all -
it is trivial to get an upper bound for the number of software timers being
active in parallel. Hence, an *O(f(n))* latency with *n* being the number of
active software timers makes the real time behavior of a system predictable and
easy to estimate. In addition, *f(n)* should increase as slowly as possible,
while not being to strict to rule out reasonable trade-offs e.g. between
ROM/RAM requirements and latency. An *O(n)* latency is e.g. feasible to
implement with a sorted linked list and therefore a reasonable lower bar.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

O(n) is an unbound function, so this justification is inconsistent. For real-time requirements ask for O(1), I suppose.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

For every concrete application, there is an upper bound for the number n of software timers being active at the same time. So for every concrete application, O(n) has an upper bound.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is true for any finite set of numbers. So we could also go for an O(exp(n))?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

O(n) is an unbound function, so this justification is inconsistent. For real-time requirements ask for O(1), I suppose.

This is intentionally written to not be O(1), because of the reasonable trade-offs as described. We know how to do O(n) by simply using linked lists and sorting on insert, and we understand the runtime implications well. We don't know how to do this in O(1) without limiting the number of allowed timers.

We do multiplexing here, so one hardware timer (or more, but a small number) needs to be used to schedule N timeouts.
So when adding one of these timeouts (using a set(interval) function), that new timeout needs to be added into the list of already queued timers. Using linked lists, this is an O(n) operation. Using an array and binary search, this is O(logn). We need to be careful what to require here, because if we break O(n*logn) for sorting n timers, we might attract a lot of attention.

It is possible to add an allowed timers limit (that is lower than available memory, e.g., 16 or 128) and enforce this.
We'd suddenly have O(1) on paper (meeting the requirements), but wouldn't have gained anything, other than that now a multitude of functions have an error case (timer_sleep() might fail with "maximum number of timers reached") that needs to be checked everywhere, which is unreasonable.

TL;DR let's not require something we don't know how to implement. This is phrased as "yadda yadda reasonable trade-offs".

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The complete timer management can be done in O(log(n)), which is almost constant in the number range we consider realistically. Further optimizations can apply, if algorithms account for the deadline of the next firing timer, which can be determined in constant time.
Current preliminary HiL-tests of @pokgak show a rather poor behavior of xtimer and a somewhat unclear signature of ztimer. We will publish once ready.
In any case, it is important that timer firing remains within predictable bounds independent of the number of instantiated timers - otherwise real-time gets out of control.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

log(25) ~ 1.39, exp(25) ~ 7.2 x 10**10.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

log(25) ~ 1.39, exp(25) ~ 7.2 x 10**10.

Both bounded, which is exactly what a realtime system needs.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Really?

Copy link
Member

@bergzand bergzand Oct 25, 2020

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Really?

Do you want to dive into the numerical analysis of the numbers you provided or can we agree here that both numbers are constants?

A realtime system does not require the system to be fast. It does however require the system to respond within the specified deadlines. This deadline can be in the order of microsecond or in the order of years, depending on the requirements.

Furthermore, a complex O(1) algorithm, taking 10 seconds performs worse compared to a simple O(n) algorithm taking 10 ms + n * 1 ms. when n is known to be bounded to 100.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

As a solution here, why don't we rephrase a bit and specify that it must be trivial to determine the maximum number of active timers for an application in order to ensure bounded execution time?

```

## Precision Requirements

- Timer callbacks should never fire prematurely (±1 tick of the underlying
timer)
- The accuracy of execution delay should be measured with single digit micro
maribu marked this conversation as resolved.
Show resolved Hide resolved
seconds for short delays in absence of other load on the system
- It is acceptable if platform specific tweaking is needed to achieve this
- It is acceptable if minimum hardware requirements are needed to achieve
maribu marked this conversation as resolved.
Show resolved Hide resolved
this
- Timer callbacks should fire closely after the timer expired
- In absence of other load on the system, accuracy in the order of single
digit micro seconds should be achievable when given hardware requirements
are fulfilled and platform specific tweaking is done

## Performance Requirements

- Efficient use of RAM/ROM
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@maribu @kaspar030 Here, and wherever else applicable in this section, what about giving some ballpark figures that we aim for, at the minimum?
For that, a (low) hanging fruit could be to use the output of some xtimer benchmarks.
In my mind, such figures would not aim to be "exhaustive", but rather to be indicative.
I think it could be helpful. What do you think?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

#12942 might provide some, but they're very dependent on both platform and configuration...

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

very dependent on both platform and configuration...

yup. That's why I was suggesting we do not claim these numbers capture exhaustively the requirements, but rather are put here as indicators, to give a rough idea?

- Optimized for power management
jue89 marked this conversation as resolved.
Show resolved Hide resolved
- The timer implementation should minimize system wake ups
Copy link
Contributor

@benpicco benpicco Sep 4, 2020

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Most MCUs have two kind of timers

  • coarse, low-power timers
  • high precision timers with higher power requirement

Now a timer API could combine both to always set the longest possible sleep with the low power timer and use the high precision timer for the rest.

e.g. if you have a low power 32kHz timer and a fast 1 MHz timer and want to sleep for 325 ticks and then use the high precision timer for the remaining 82 ticks.

How ideally you could also specify a minimum and a maximum wake up time, so wake-up events can be bundled to reduce the amount of total wake-ups.

For precise timings you'd have min == max

But the user shouldn't have to worry about which timer to use, that should be selected automatically based on the wait time.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

e.g. if you have a low power 32kHz timer and a fast 1 MHz timer and want to sleep for 325 ticks and then use the high precision timer for the remaining 82 ticks.

I'm not sure if there really is a use case for that. E.g. when you want to perform 3 measurements with a delay of exactly 10 ms and additionally want to repeat those three measurements every 30 minutes, this would be a good example for using both timers. But to me it makes more sense to have one 30 minutes timer, and then a 10 ms timer that is used for the second and third measurement of each row.

If you would transparently use both low frequency and high frequency timers (e.g. for a sleep of 30 minutes and 1337 µs, transparently set one 30 minute low frequency timer and afterwards a 1337 µs high frequency timer), there would be some issues:

  1. The overhead of setting the high frequency timer would reduce precision
  2. Low and high frequency domain might be driven from different crystals, so there might be a clock drift between those two. (In fact, having a separate 32.678 kHz crystal for the low frequency timer is quite common.)
  3. The chance to start a timer right on the clock tick is pretty low. In the worst case are off by just bellow clock tick. For a 1 MHz timer that would be up to 1 µs precision loss. On a 32.678 kHz timer that would be ~30 µs precision loss.

IMO mixing clocks should not be done transparently, as this might (depending on the availability of low frequency clocks) reduce the accuracy of timers in unexpected ways. Having this explicitly makes sure users are aware of power consumption, accuracy and precision of the used clock.

How ideally you could also specify a minimum and a maximum wake up time, so wake-up events can be bundled to reduce the amount of total wake-ups.

Sounds like this adds quite some complexity. I'm not sure if it is better to let the application developers optimize their use of timers. IMO core infrastructure like software timers should come with low ROM / RAM requirements and robust implementations, as one usually cannot opt out of using them.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah, this is something that seems useful in theory. In practice, in a non-idle system, it is difficult enough to get a 1MHz timer actually have better than the 30.5us accuracy that a 32kHz timer promises. See #14931. We tried debugging this, uncorrected, the (quite fast) nrf52840 has 30us offset on a simple ztimer_sleep(). There's a context switch, expensive timer reads, and ztimer's overhead. The time is spread more or less evenly across them. Yeah, that can be mostly corrected, but this is the simplest use-case.

A simple combination of timers won't gain anything, as setting a low-freq timer already quantizes on that timer's frequency, so the result is off by half that timer's period length, on average, anyhow. So using a HF timer "for the last microseconds", but being based on a, from the HF timer's perspective, arbitrary time in between a LF period, is useless.
There are ways to double sync, but that's expensive...

IMO, with all these theoretical timing needs, people need to realize that in most cases, 1KHz or 32786KHz is mightily sufficient. And those are easy to do with RTTs.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Also, as said in previous discussions, a sane abstraction of timers that use only a single backend (e.g., don't do these complex combinations of timers) are a nice base to put the complex stuff on top. At least for prototyping.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

But the user shouldn't have to worry about which timer to use, that should be selected automatically based on the wait time.

The user needs to choose the precision (the time base). There cannot be an API where the user says "sleep 100". It'll always be "sleep 100ms" or "sleep 100us".

IMO it is OK to simplify our lives (and reduce complexity) by asking the user to always choose the time base that is just enough to express the needs. E.g., if the user asks for "sleep 100ms", it is acceptable to expect them to call "sleep_ms(100)", and not do "sleep_us(100000)".

Copy link
Contributor

@kaspar030 kaspar030 Sep 11, 2020

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think this is re-hashing the mailing list discussions from a year ago :)

I'd be happy to see a prototype of what is proposed here.

I agree that for "sleep_ms(100)" vs "sleep_us(100000)", there are ways to maximize power efficiency. A simple if us > 1000: sleep_ms(us/1000), sleep_us(rest) might do it for many cases.

Otherwise, I see split between domains, clock drift boundaries, high precision LF, mentioned. Implement! :)

... seem to be only related to "thinking inside of the box" ;)
If you know when exactly a timeout of n ticks on the LF timer will fire (i.e. where in the period it is) you can adjust the remaining ticks for the HF timer accordingly.

Well, how do you do that without explicit synchronization? Assuming LF is being used as it is active during sleep, and HF is not active during sleep, they need to be synchronized at least on every transition between sleep and wake. I call that double sync.

I call that expensive because on some platforms (e.g., sam0), just reading the RTT "now" register needs to synchronize the clocks, in some cases taking a whole period (30us). sometimes, depending on what else is going on in the system. That dwarfs a lot of clock drift for reasonable time scales.

In fact, using the more accurate LF crystal for the significant (long) part of the timeout improves the overall drift (versus perfect reference) compared to only using a worse accuracy HF clock.

Yup. I'm strongly in favor of not making things complex, and teach users to use ms or 32kHz, unless there's a reeeeally good reason not to. Accurate ms or 32kHz timing goes very far. :)

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Are you guys available and interested in some joint discussion of these things during the summit? I don't think we need a separate breakout session for this but maybe chatting about this during a coffe-break or something helps understanding the different perspectives better? What do you think?

Sure!

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think this is re-hashing the mailing list discussions from a year ago :)

I know, and to be honest I don't want to bore people with lengthy discussions again. There is no point in these lengthy "abstract" discussions if not more of the developers jump in. Yet, the arguments are not less valid than before. It appears to me that we all hold single pieces of the big picture without an explicit agreement on how the big picture looks like. This document will eventually help with that I think.

Otherwise, I see split between domains, clock drift boundaries, high precision LF, mentioned. Implement! :)

On it (at least for some of these problems) just from a slightly different direction.. I'll explain during the summit.

Well, how do you do that without explicit synchronization? (..)

Not. At least not without any synchronization. Of course they need to be synchronized.
But why double sync? And who says that you need to do this explicitly? We may very well use an "opportunistic" approach where you sync based on events that happen anyway. I.e., you don't care how long reading 'now' takes for the slow timer if you read the fast timer close enough to a known event of the slow timer... I don't say this solves all cases everywhere. I'm just trying to think towards a solution instead of searching for reasons against it.

I call that expensive

Yes reading LF timer can indeed be expensive. Apart from reading not being necessarily required, some would argue transitioning to a low power mode for arbitrarily small periods is "expensive" because of the delay when leaving lpm. Again, it all depends on the perspective ;)

I'm strongly in favor of not making things complex, and teach users to use ms (..)

Fair enough. I get your point.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

BTW: why do I get these check-labels mail-notification after adding a comment?

[RIOT-OS/RIOT] Run failed: check-labels - timer-rdm (f5fae3e)

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In the end this will have to be a trade-off between:

  1. Overhead in terms of CPU cycles
  2. RAM and ROM consumption
  3. Complexity and maintainability of the implementation
  4. Give the developer using the API a solid understanding and full control about accuracy, resolution, and impact on power consumption for a timer
  5. Simplify API so that sleep_ms(1) is just an alias for sleep_us(1000)
  6. Allow optimization of power consumption, accuracy, etc. without replacing code. (E.g. via USEMODULE += ztimer_policy_powersafe sleeps could be moved to the low frequency clock where possible, etc. And one would not have to replace ztimer_sleep(ZTIMER_USEC, x) by ztimer_sleep(ZTIMER_MSEC, (x + 500) / 1000))

We just won't be able to get those all.

IMO we should first assemble a list design goals and vote on them to get some agreement of a prioritization of them. If overhead, memory, and maintainability get a higher priority than user friendliness and timer policy support, an explicit clock selection via the API makes more sense. Otherwise, adding heuristics and policies for clock selection makes more sense. (This might still be technically better implemented on top of a "low-level high-level timer API", but that would be an implementation detail.)

- Efficient use of CPU cycles

maribu marked this conversation as resolved.
Show resolved Hide resolved

## Acknowledgements

Thanks to K. Schleiser, J. Nohlgård, M. Rottleuthner, and all other contributors
to this document and RIOT's timer API.

TODO: Extend as needed.

## References

- [RFC7228 - Terminology for Constrained-Node Networks](https://tools.ietf.org/html/rfc7228)
- TODO: Extend as needed

## Contact

The authors of this memo can be contacted via email at ...
TODO: Add emails of authors prior merging

Additionally, the RIOT developer community can be reached via email at
devel@riot-os.org.