Skip to content

sys/walltime: add module for system time#21343

Merged
benpicco merged 6 commits intoRIOT-OS:masterfrom
benpicco:walltime
Dec 18, 2025
Merged

sys/walltime: add module for system time#21343
benpicco merged 6 commits intoRIOT-OS:masterfrom
benpicco:walltime

Conversation

@benpicco
Copy link
Copy Markdown
Contributor

@benpicco benpicco commented Apr 1, 2025

Contribution description

Currently applications use the periph_rtc API directly if they want to track wall clock time.
This leads to problems when the time is not provided by periph_rtc but some external RTC or when we want to perform actions e.g. on setting the time - then this has to be hooked up with every periph_rtc implementation.

Instead, provide a wrapper API that applications can use to obtain the current system time without accessing the RTC API directly.

This allows to write applications that are agnostic to the availability of periph_rtc and still can provide some proper timestamps.

Testing procedure

Enable the walltime module.
This will provide a walltime command to get / set the time and display the time since last boot.

Issues/PRs references

Alternative to #17416, #21366

@github-actions github-actions bot added the Area: sys Area: System label Apr 1, 2025
@maribu maribu self-requested a review April 1, 2025 17:25
Copy link
Copy Markdown
Contributor

@crasbe crasbe left a comment

Choose a reason for hiding this comment

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

I like this addition a lot, some small comments and observations from testing the PR below.

Edit: For reference: I tested this with an nRF52840DK with USEMODULE+=walltime BOARD=nrf52840dk make -C tests/sys/shell flash term.

* @brief Common functions to access the wall-clock / real time clock
* @{
* @file
*
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

It would be nice to have some documentation about what this module does and how to use it. More or less what the PR description says for example.

Currently it's a bit sparse and if you don't know what it does, it's a bit hard to get into it (IMO):
image

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Also since there is no documentation for struct tm (I think time.h is part of the standard library?), it might be good to include a link to a reference for it.

This is the link used by drivers/periph/rtc.h: https://pubs.opengroup.org/onlinepubs/7908799/xsh/time.h.html

* @note
* The values used for setting and getting the time/alarm should
* conform to the `struct tm` specification.
* Compare: http://pubs.opengroup.org/onlinepubs/7908799/xsh/time.h.html

@crasbe crasbe added Type: new feature The issue requests / The PR implemements a new feature for RIOT CI: ready for build If set, CI server will compile all applications for all available boards for the labeled PR labels Apr 1, 2025
@riot-ci
Copy link
Copy Markdown

riot-ci commented Apr 1, 2025

Murdock results

✔️ PASSED

33b8ec8 cpu/esp8266: implement cpu_woke_from_backup()

Success Failures Total Runtime
10966 0 10968 10m:43s

Artifacts

Copy link
Copy Markdown
Contributor

@mguetschow mguetschow left a comment

Choose a reason for hiding this comment

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

Nice, I like this! Some first thoughts on it below.

#endif

static uint32_t _boottime;
#ifdef BACKUP_RAM
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Is that macro documented somewhere?

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Does this count?

$ rg BACKUP_RAM -B 5 features.yaml
417-- title: RAM Related Features
418-  help: These features indicate presence of special RAM regions or features
419-  features:
420-  - name: backup_ram
421-    help: A special portion of RAM is retained during deep sleep.
422:          Variables can be placed there by annotating them with the `BACKUP_RAM`

See https://doc.riot-os.org/feature-list.html#autotoc_md2404 for the output

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Hum, that's indeed something, but probably nothing doxygen would find when using @ref BACKUP_RAM


void walltime_set(struct tm *time)
{
int32_t diff = rtc_mktime(time) - walltime_get_riot(NULL);
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

I think the int32_t could lead to unexpected results. For example the extreme case 0 - 4294967295 becomes 1.
I would expect -4294967295
You could figure out the sign and compute the number as uint32_t higher - lower.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

diff --git a/sys/walltime/walltime.c b/sys/walltime/walltime.c
index 903e856a16..5171cb0770 100644
--- a/sys/walltime/walltime.c
+++ b/sys/walltime/walltime.c
@@ -17,6 +17,8 @@
  *
  * @}
  */
+#include <stdio.h>
+
 #include "auto_init.h"
 #include "auto_init_utils.h"
 #include "auto_init_priorities.h"
@@ -96,7 +98,10 @@ time_t walltime_get_unix(uint16_t *ms)
 
 void walltime_set(struct tm *time)
 {
-    int32_t diff = rtc_mktime(time) - walltime_get_riot(NULL);
+    uint32_t t;
+    int32_t w;
+    int32_t diff = (t = rtc_mktime(time)) - (w = walltime_get_riot(NULL));
+    printf("t %"PRIu32" - w %"PRId32" = diff %"PRId32"\n", t, w, diff);
     _boottime += diff;
 #ifdef BACKUP_RAM
     _boottime_bkup += diff;
> 2025-05-06 15:29:20,168 # main(): This is RIOT! (Version: 2025.04-devel-522-g90fc5e-walltime)
> walltime set 4000-01-01 00:00:00
2025-05-06 15:29:23,464 # walltime set 4000-01-01 00:00:00
2025-05-06 15:29:23,480 # t 2353209856 - w 3 = diff -1941757443
2025-05-06 15:29:23,481 # time changed by -1941757443 sec, 0 ms

So when going very very very far in the future, the diff becomes negative.
I know that no one actually cares about the year 4000, but consider some user input or malformed network packet ... whatever.

return now - _boottime;
}

static void auto_init_uptime(void)
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

maybe guard with IS_USED(MODULE_AUTO_INIT_WALLTIME/UPTIME)

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

We always want to auto-init this module

@fabian18
Copy link
Copy Markdown
Contributor

fabian18 commented Apr 7, 2025

This allows to write applications that are independent of the RTC
peripheral and allows the implementation of on-time change callbacks.

How would I implement an on-time change callback?
You expect that the backend for walltime implements the RTC API, don't you?
This limits to only only one RTC API. If I want to implement my application rtc_set_time around the RTC rtc_set_time, it does not work. I would have gone with function pointers for walltime_backend->rtc_set_time and walltime_backend->rtc_get_time

@benpicco
Copy link
Copy Markdown
Contributor Author

benpicco commented Apr 7, 2025

How would I implement an on-time change callback?

We can add that to the walltime module as a 2nd step, then we would only need to add it in one place instead of adding it to every RTC implementation.

I wanted to get the core API in first, but might as well already add this here.

If I want to implement my application rtc_set_time around the RTC rtc_set_time, it does not work.

Yea applications must not use the periph_rtc API if the walltime module is used to access the RTC instead - but that should be a simple change.

I would have gone with function pointers for walltime_backend->rtc_set_time and walltime_backend->rtc_get_time

There only is a single wall time/system time. I'm happy to hook up different backends like ds1307 or even dcf77 or gnss. The nice thing about the high level API is that the internals can be as complex as needed, to the user it's still just a simple get time / set time API.

@benpicco benpicco requested a review from kaspar030 as a code owner April 11, 2025 17:37
@github-actions github-actions bot added the Area: tests Area: tests and testing framework label Apr 24, 2025
@benpicco benpicco force-pushed the walltime branch 2 times, most recently from d3999cd to 90fc5e4 Compare April 24, 2025 16:57
@crasbe crasbe added the State: waiting for author State: Action by the author of the PR is required label Sep 12, 2025
@benpicco
Copy link
Copy Markdown
Contributor Author

benpicco commented Dec 4, 2025

If the walltime would not use the RTC API but use a driver struct with function callbacks, then the walltime could be instanciated with either the periph RTC or the external RTC driver. Then periph RTC features could still be used.

Since there will only be a single active walltime source, we might as well make that a compile-time selection inside the walltime module.

Or a weak default implementation that can be overwritten by other backends?

@maribu
Copy link
Copy Markdown
Member

maribu commented Dec 4, 2025

+1 for the weak default implementation. That would allow us to hook in our I2C RTC into the walltime module quite easily.

@github-actions github-actions bot added the Area: build system Area: Build system label Dec 4, 2025
@benpicco
Copy link
Copy Markdown
Contributor Author

benpicco commented Dec 4, 2025

That would allow us to hook in our I2C RTC into the walltime module quite easily.

It's a shame we don't have that upstream though…

@benpicco benpicco removed the State: waiting for author State: Action by the author of the PR is required label Dec 8, 2025
__attribute__((weak))
int walltime_impl_get(struct tm *time, uint16_t *ms)
{
uint32_t now = ztimer_now(ZTIMER_MSEC);
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

could benefit from #20494

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

The walltime module is good from my perspective. As the xtimer_compat was merged, it would be nice to have it here.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Hm we don't have a xtimer_now_msec(), but I could add that.

Copy link
Copy Markdown
Contributor

@fabian18 fabian18 Dec 17, 2025

Choose a reason for hiding this comment

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

Ah I thought there is. It's a logic addition to xtimer_now_usec. It should probably favor ZTIMER_MSEC over ZTIMER_USEC.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

but tbh I'm not sure how useful a non-msec based backend would be here.
ZTIMER_USEC wraps after 1h and 11 minutes.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

If there is no implication of using ztimer_msec towards not using ztimer_msec then yes msec is better than usec here.
Then ztimer_msec should be added to the Makefile.dep.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

ahh but only in the fallback case... I dont know ... either you squash or add xtimer_now_msec if you like

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

I added it to #21958

@maribu
Copy link
Copy Markdown
Member

maribu commented Dec 12, 2025

Would you mind sprinkling some SPDX-license-identifier fairy dust over the new code?

@benpicco benpicco added this pull request to the merge queue Dec 17, 2025
@github-merge-queue github-merge-queue bot removed this pull request from the merge queue due to failed status checks Dec 17, 2025
@benpicco benpicco requested a review from gschorcht as a code owner December 18, 2025 13:08
@github-actions github-actions bot added Platform: ESP Platform: This PR/issue effects ESP-based platforms Area: cpu Area: CPU/MCU ports labels Dec 18, 2025
@benpicco
Copy link
Copy Markdown
Contributor Author

cpu_woke_from_backup() was not implemented, doing to caused a merge conflict - let's see if CI is happy now.

@benpicco benpicco enabled auto-merge December 18, 2025 13:20
@benpicco benpicco added this pull request to the merge queue Dec 18, 2025
Merged via the queue into RIOT-OS:master with commit 42050c5 Dec 18, 2025
25 checks passed
@benpicco benpicco deleted the walltime branch December 18, 2025 16:48
@leandrolanzieri leandrolanzieri added this to the Release 2026.01 milestone Jan 13, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Area: build system Area: Build system Area: cpu Area: CPU/MCU ports Area: sys Area: System Area: tests Area: tests and testing framework CI: ready for build If set, CI server will compile all applications for all available boards for the labeled PR Platform: ESP Platform: This PR/issue effects ESP-based platforms Type: new feature The issue requests / The PR implemements a new feature for RIOT

Projects

None yet

Development

Successfully merging this pull request may close these issues.

8 participants