zephyr icon indicating copy to clipboard operation
zephyr copied to clipboard

lib: sys: time: Add native real-time time library

Open bjarki-andreasen opened this issue 1 year ago • 9 comments

Introduction

This PR adds native APIs for getting and setting real-time, either formatted as a timestamp or a struct rtc_time formatted datetime, abstracting both conversions between the two time formats, and the interactions with the underlying hardware which will be used to keep time.

The API

/** Get universal coordinated time timestamp */
int sys_time_get_timestamp(int64_t *timestamp);
/** Set universal coordinated time timestamp */
int sys_time_set_timestamp(const int64_t *timestamp);

/** Get universal coordinated time datetime */
int sys_time_get_datetime(struct rtc_time *datetime);
/** Set universal coordinated time datetime */
int sys_time_set_datetime(const struct rtc_time *datetime);

/** Convert universal coordinated time datetime to timestamp */
int sys_time_datetime_to_timestamp(int64_t *timestamp, const struct rtc_time *datetime);
/** Convert universal coordinated time timestamp to datetime */
int sys_time_timestamp_to_datetime(struct rtc_time *datetime, const int64_t *timestamp);

The timestamp is a UTC timestamp, stored in an int64_t. Datetime (clock+calendar) formatted time is stored in a struct rtc_time, which is 1-1 mapped to the struct tm.

Hardware support

RTC

Using an RTC is very straight forward, simply point to the RTC with a chosen node, select CONFIG_TIME, and you are good to go:

/ {
	chosen {
		zephyr,real-time-clock = &rtc;
	};
};

Counter

Using a counter is similarly simple to configure. The counter just needs to support setting a counter-compare (CC) alarm, and be up-counting (basically any real-time counter / low-power counter).

/ {
	chosen {
		zephyr,real-time-counter = &rtc;
	};
};

fixes: #35333

bjarki-andreasen avatar Apr 30 '24 19:04 bjarki-andreasen

Update

Added counter support

Using a low-power counter / real-time counter as timekeeper is now supported!

Added test suite for time conversion functions

The test suite tests conversions from timestamps all the way back from year -1 to year 2399

bjarki-andreasen avatar May 20 '24 20:05 bjarki-andreasen

Note to self, namespace SYS_TIME

bjarki-andreasen avatar May 21 '24 15:05 bjarki-andreasen

@cfriedt yep :) we could up the resolution to ms as well, we got the bits for it

bjarki-andreasen avatar May 22 '24 05:05 bjarki-andreasen

How about naming it sys_realtime? Its a library handling interactions with and conversions between real-time clocks and real-time counters?

bjarki-andreasen avatar May 22 '24 11:05 bjarki-andreasen

It would be kind of great to have 1 header file for converting between zephyr time representations.

Naming things is hard, but if it can be added to an existing header, it's much easier.

cfriedt avatar May 22 '24 11:05 cfriedt

It would be kind of great to have 1 header file for converting between zephyr time representations.

Naming things is hard, but if it can be added to an existing header, it's much easier.

I agree, that would be great, but the problem has been underestimated by many in the project already. There are lots of different ways to convert between pairs of time representations, especially when they come from different layers in the clock hierarchy as is the case here: counter layer vs. synchronized time layer - leaving out the syntonization layer in between w/o even mentioning it.

If you don't have good intermediate representations (single point of truth) on those three layers as well as APIs to manipulate them properly, then you get a combinatoric explosion of time types and conversion approaches. Hub and spoke is the only approach that works for proper time conversion IMHO.

Just think of leap second smearing (or whatever of the many approaches) in the UTC clock and the large amount of established syntonization approaches across system borders which are both huge problems for long-running high-precision timing systems as those driven by Zephyr. And there are many more.

Due to our lack of proper time representation in Zephyr, we currently experience that kind of combinatoric explosion. This PR is just one example of many. Linux has gone a long way to converge towards proper intermediate time representations. But our challenge is even bigger, as we cannot sensibly let go of truely deterministic timing even in higher clock layers (syntonization, synchronization), something Linux mostly fails to deliver today.

Wrt this PR: If we cannot avoid another arbitrary experimental time conversion library, then I vote for keeping it somewhere down in one driver subsystem where it originates. At least this would properly document the dependency of POSIX time (which seems to be the implied use case) onto one or two driver subsystems.

In any case I vote against another flawed approach of "general" time conversion clogging our common namespace,

ghost avatar May 23 '24 09:05 ghost

How about naming it sys_realtime?

"Realtime" is still much too overloaded as a namespace. As long as you don't keep the promise of actually dealing with what "realtime" means in other systems, as long as we don't even reach the Linux benchmark for real time (much less deterministic synchronized clocks as required by several RTOS use cases), then this name promises far too much.

Let's face it: This is an RTC driver/hardware (plus maybe counter subsystem) specific conversion library that might even solve an immediate problem in the POSIX layer for a little while but that will have to be replaced as soon as POSIX APIs want to expose something that is similar to what "real time" means on other systems. But it doesn't even remotely integrate the existing time representations in Zephyr not to speak of those that are clearly on the horizon.

I'm totally open to help you start with the nucleus of a well-defined time representation in Zephyr, but this PR is not the way to go to achieve this IMO. We have experimental implementations and specific APIs that go far beyond this PR already, so you'd first have to go beyond those.

ghost avatar May 23 '24 09:05 ghost

@cfriedt Just so I don't pull too much in the wrong direction.

Another idea: What if we conceived this API as being part of the internal POSIX layer implementation (but not more), just remove the dependencies on specific hardware from the header files and hide the RTC (or other current hardware dependencies) in the .c files rather than in the .h files and let the .h files refer to POSIX concepts only. Then this API would again be well encapsulated inside a subsystem - which would also be fine from my pov.

If we ever introduce proper intermediate time representations we can just introduce them in the implementation w/o having to change anything in the header files or outside the POSIX subsystem.

ghost avatar May 23 '24 10:05 ghost

I would say, the basis for POSIX time is struct timespec rather than a 64-bit timestamp.

struct rtctime could be converted to a 64 bit ms counter since the epoch and then that could be converted into a struct timespec to set CLOCK_REALTIME for example.

That would provide a kind of coarse granularity "clock recovery" on power-on (in the absence of synchronizing via e.g. NTP or PTP).

Additionally, I think that would make a great SYS_INIT() feature of an rtc subsystem, along with shell utilities and so on. From that perspective, this code would be a consumer of the POSIX API.

Hard to say where to park this, but since it's not part of any standard, I would steer clear of sys/time.h or time.h since they are standard and kind of overloaded already.

cfriedt avatar May 23 '24 10:05 cfriedt

What about "rtcutil" as a prefix instead of "sys_realtime"? Just a thought

cfriedt avatar Jun 02 '24 00:06 cfriedt

I propose s/sys{_realtime}?/rtc/g, then place the API in rtc.h, see similar conversion APIs in other driver subsystems as counter and sensor. Let it mature there and expose it more prominently once it has proven useful to at least one more subsystem. This feels much more natural and resolves the encapsulation and dependency issue nicely.

If the POSIX layer or an application want to introduce a direct dependency to the RTC driver subsys (rtc.h) as one of several possible realtime sources then it can still do so - but then it's not a forced dependency and one that is well visible as being bound to a specific hardware type by looking at the names.

If that's not fine, then let's rediscuss in the architecture WG. But please document the rtc.h dependency in your diagram above, so we can properly see what's actually going on in that API.

ghost avatar Jun 03 '24 10:06 ghost

I propose we wait until I have had time to address the existing comments, before discussing further :) I'm working on it ;)

bjarki-andreasen avatar Jun 03 '24 10:06 bjarki-andreasen

This pull request has been marked as stale because it has been open (more than) 60 days with no activity. Remove the stale label or add a comment saying that you would like to have the label removed otherwise this pull request will automatically be closed in 14 days. Note, that you can always re-open a closed pull request at any time.

github-actions[bot] avatar Aug 09 '24 00:08 github-actions[bot]

Is this still moving forward? Right now we're having to do yet another roll-your-own solution to handle the fact that the only concept of time Zephyr has is its own uptime. Uptime alone doesn't really work for network enabled applications or even for system event logging, and something better than an application-specific offset + uptime workaround would be greatly appreciated.

madscientist159 avatar Nov 19 '24 16:11 madscientist159

Its quite dormant since I am spending most of my time with power management currently, I think the solution in this PR is nice, the only thing I remember needing to add was handling day of week and day of year conversions (if anyone wants that), would you like to help finalize this?

bjarki-andreasen avatar Nov 20 '24 12:11 bjarki-andreasen

oh, and also, there was a posix clock test which was failing for some target run in Renode, 99% certain it was just some CI instability, but that was the last thing I was dealing with before switching tasks :)

bjarki-andreasen avatar Nov 20 '24 12:11 bjarki-andreasen