Skip to content

Commit 7b3dc48

Browse files
pabigotnashif
authored andcommitted
doc: add a section for timeutil APIs
Describe the role of these APIs, key concepts that they depend on, and expose the low-level API. Signed-off-by: Peter Bigot <[email protected]>
1 parent f6d7595 commit 7b3dc48

File tree

4 files changed

+261
-0
lines changed

4 files changed

+261
-0
lines changed

doc/reference/index.rst

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@ API Reference
2929
resource_management/index.rst
3030
shell/index.rst
3131
storage/index.rst
32+
misc/timeutil.rst
3233
usb/index.rst
3334
usermode/index.rst
3435
util/index.rst

doc/reference/kernel/timing/clocks.rst

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -66,6 +66,8 @@ multiples of each other and where the output fits within a single
6666
word, these conversions expand to a 2-4 operation sequence, requiring
6767
full precision only where actually required and requested.
6868

69+
.. _kernel_timing_uptime:
70+
6971
Uptime
7072
======
7173

doc/reference/misc/timeutil.rst

Lines changed: 240 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,240 @@
1+
.. _timeutil_api:
2+
3+
Time Utilities
4+
##############
5+
6+
Overview
7+
********
8+
9+
:ref:`kernel_timing_uptime` in Zephyr is based on the a tick counter. With
10+
the default :option:`CONFIG_TICKLESS_KERNEL` this counter advances at a
11+
nominally constant rate from zero at the instant the system started. The POSIX
12+
equivalent to this counter is something like ``CLOCK_MONOTONIC`` or, in Linux,
13+
``CLOCK_MONOTONIC_RAW``. :c:func:`k_uptime_get()` provides a millisecond
14+
representation of this time.
15+
16+
Applications often need to correlate the Zephyr internal time with external
17+
time scales used in daily life, such as local time or Coordinated Universal
18+
Time. These systems interpret time in different ways and may have
19+
discontinuities due to `leap seconds <https://what-if.xkcd.com/26/>`__ and
20+
local time offsets like daylight saving time.
21+
22+
Because of these discontinuities, as well as significant inaccuracies in the
23+
clocks underlying the cycle counter, the offset between time estimated from
24+
the Zephyr clock and the actual time in a "real" civil time scale is not
25+
constant and can vary widely over the runtime of a Zephyr application.
26+
27+
The time utilities API supports:
28+
29+
* :ref:`converting between time representations <timeutil_repr>`
30+
* :ref:`synchronizing and aligning time scales <timeutil_sync>`
31+
32+
For terminology and concepts that support these functions see
33+
:ref:`timeutil_concepts`.
34+
35+
Time Utility APIs
36+
*****************
37+
38+
.. _timeutil_repr:
39+
40+
Representation Transformation
41+
=============================
42+
43+
Time scale instants can be represented in multiple ways including:
44+
45+
* Seconds since an epoch. POSIX representations of time in this form include
46+
``time_t`` and ``struct timespec``, which are generally interpreted as a
47+
representation of `"UNIX Time"
48+
<https://tools.ietf.org/html/rfc8536#section-2>`__.
49+
50+
* Calendar time as a year, month, day, hour, minutes, and seconds relative to
51+
an epoch. POSIX representations of time in this form include ``struct tm``.
52+
53+
Keep in mind that these are simply time representations that must be
54+
interpreted relative to a time scale which may be local time, UTC, or some
55+
other continuous or discontinuous scale.
56+
57+
Some necessary transformations are available in standard C library
58+
routines. For example, ``time_t`` measuring seconds since the POSIX EPOCH is
59+
converted to ``struct tm`` representing calendar time with `gmtime()
60+
<https://pubs.opengroup.org/onlinepubs/9699919799/functions/gmtime.html>`__.
61+
Sub-second timestamps like ``struct timespec`` can also use this to produce
62+
the calendar time representation and deal with sub-second offsets separately.
63+
64+
The inverse transformation is not standardized: APIs like ``mktime()`` expect
65+
information about time zones. Zephyr provides this transformation with
66+
:c:func:`timeutil_timegm` and :c:func:`timeutil_timegm64`.
67+
68+
.. doxygengroup:: timeutil_repr_apis
69+
:project: Zephyr
70+
71+
.. _timeutil_sync:
72+
73+
Time Scale Synchronization
74+
==========================
75+
76+
There are several factors that affect synchronizing time scales:
77+
78+
* The rate of discrete instant representation change. For example Zephyr
79+
uptime is tracked in ticks which advance at events that nominally occur at
80+
:option:`CONFIG_SYS_CLOCK_TICKS_PER_SEC` Hertz, while an external time
81+
source may provide data in whole or fractional seconds (e.g. microseconds).
82+
* The absolute offset required to align the two scales at a single instant.
83+
* The relative error between observable instants in each scale, required to
84+
align multiple instants consistently. For example a reference clock that's
85+
conditioned by a 1-pulse-per-second GPS signal will be much more accurate
86+
than a Zephyr system clock driven by a RC oscillator with a +/- 250 ppm
87+
error.
88+
89+
Synchronization or alignment between time scales is done with a multi-step
90+
process:
91+
92+
* An instant in a time scale is represented by an (unsigned) 64-bit integer,
93+
assumed to advance at a fixed nominal rate.
94+
* :c:struct:`timeutil_sync_config` records the nominal rates of a reference
95+
time scale/source (e.g. TAI) and a local time source
96+
(e.g. :c:func:`k_uptime_ticks`).
97+
* :c:struct:`timeutil_sync_instant` records the representation of a single
98+
instant in both the reference and local time scales.
99+
* :c:struct:`timeutil_sync_state` provides storage for an initial instant, a
100+
recently received second observation, and a skew that can adjust for
101+
relative errors in the actual rate of each time scale.
102+
* :c:func:`timeutil_sync_ref_from_local()` and
103+
:c:func:`timeutil_sync_local_from_ref()` convert instants in one time scale
104+
to another taking into account skew that can be estimated from the two
105+
instances stored in the state structure by
106+
:c:func:`timeutil_sync_estimate_skew`.
107+
108+
.. doxygengroup:: timeutil_sync_apis
109+
:project: Zephyr
110+
111+
.. _timeutil_concepts:
112+
113+
Concepts Underlying Time Support in Zephyr
114+
******************************************
115+
116+
Terms from `ISO/TC 154/WG 5 N0038
117+
<https://www.loc.gov/standards/datetime/iso-tc154-wg5_n0038_iso_wd_8601-1_2016-02-16.pdf>`__
118+
(ISO/WD 8601-1) and elsewhere:
119+
120+
* A *time axis* is a representation of time as an ordered sequence of
121+
instants.
122+
* A *time scale* is a way of representing an instant relative to an origin
123+
that serves as the epoch.
124+
* A time scale is *monotonic* (increasing) if the representation of successive
125+
time instants never decreases in value.
126+
* A time scale is *continuous* if the representation has no abrupt changes in
127+
value, e.g. jumping forward or back when going between successive instants.
128+
* `Civil time <https://en.wikipedia.org/wiki/Civil_time>`__ generally refers
129+
to time scales that legally defined by civil authorities, like local
130+
governments, often to align local midnight to solar time.
131+
132+
Relevant Time Scales
133+
====================
134+
135+
`International Atomic Time
136+
<https://en.wikipedia.org/wiki/International_Atomic_Time>`__ (TAI) is a time
137+
scale based on averaging clocks that count in SI seconds. TAI is a montonic
138+
and continuous time scale.
139+
140+
`Universal Time <https://en.wikipedia.org/wiki/Universal_Time>`__ (UT) is a
141+
time scale based on Earth’s rotation. UT is a discontinuous time scale as it
142+
requires occasional adjustments (`leap seconds
143+
<https://en.wikipedia.org/wiki/Leap_second>`__) to maintain alignment to
144+
changes in Earth’s rotation. Thus the difference between TAI and UT varies
145+
over time. There are several variants of UT, with `UTC
146+
<https://en.wikipedia.org/wiki/Coordinated_Universal_Time>`__ being the most
147+
common.
148+
149+
UT times are independent of location. UT is the basis for Standard Time
150+
(or "local time") which is the time at a particular location. Standard
151+
time has a fixed offset from UT at any given instant, primarily
152+
influenced by longitude, but the offset may be adjusted ("daylight
153+
saving time") to align standard time to the local solar time. In a sense
154+
local time is "more discontinuous" than UT.
155+
156+
`POSIX Time <https://tools.ietf.org/html/rfc8536#section-2>`__ is a time scale
157+
that counts seconds since the "POSIX epoch" at 1970-01-01T00:00:00Z (i.e. the
158+
start of 1970 UTC). `UNIX Time
159+
<https://tools.ietf.org/html/rfc8536#section-2>`__ is an extension of POSIX
160+
time using negative values to represent times before the POSIX epoch. Both of
161+
these scales assume that every day has exactly 86400 seconds. In normal use
162+
instants in these scales correspond to times in the UTC scale, so they inherit
163+
the discontinuity.
164+
165+
The continuous analogue is `UNIX Leap Time
166+
<https://tools.ietf.org/html/rfc8536#section-2>`__ which is UNIX time plus all
167+
leap-second corrections added after the POSIX epoch (when TAI-UTC was 8 s).
168+
169+
Example of Time Scale Differences
170+
---------------------------------
171+
172+
A positive leap second was introduced at the end of 2016, increasing the
173+
difference between TAI and UTC from 36 seconds to 37 seconds. There was
174+
no leap second introduced at the end of 1999, when the difference
175+
between TAI and UTC was only 32 seconds. The following table shows
176+
relevant civil and epoch times in several scales:
177+
178+
==================== ========== =================== ======= ==============
179+
UTC Date UNIX time TAI Date TAI-UTC UNIX Leap Time
180+
==================== ========== =================== ======= ==============
181+
1970-01-01T00:00:00Z 0 1970-01-01T00:00:08 +8 0
182+
1999-12-31T23:59:28Z 946684768 2000-01-01T00:00:00 +32 946684792
183+
1999-12-31T23:59:59Z 946684799 2000-01-01T00:00:31 +32 946684823
184+
2000-01-01T00:00:00Z 946684800 2000-01-01T00:00:32 +32 946684824
185+
2016-12-31T23:59:59Z 1483228799 2017-01-01T00:00:35 +36 1483228827
186+
2016-12-31T23:59:60Z undefined 2017-01-01T00:00:36 +36 1483228828
187+
2017-01-01T00:00:00Z 1483228800 2017-01-01T00:00:37 +37 1483228829
188+
==================== ========== =================== ======= ==============
189+
190+
Functional Requirements
191+
-----------------------
192+
193+
The Zephyr tick counter has no concept of leap seconds or standard time
194+
offsets and is a continuous time scale. However it can be relatively
195+
inaccurate, with drifts as much as three minutes per hour (assuming an RC
196+
timer with 5% tolerance).
197+
198+
There are two stages required to support conversion between Zephyr time and
199+
common human time scales:
200+
201+
* Translation between the continuous but inaccurate Zephyr time scale and an
202+
accurate external stable time scale;
203+
* Translation between the stable time scale and the (possibly discontinuous)
204+
civil time scale.
205+
206+
The API around :c:func:`timeutil_sync_state_update()` supports the first step
207+
of converting between continuous time scales.
208+
209+
The second step requires external information including schedules of leap
210+
seconds and local time offset changes. This may be best provided by an
211+
external library, and is not currently part of the time utility APIs.
212+
213+
Selecting an External Source and Time Scale
214+
-------------------------------------------
215+
216+
If an application requires civil time accuracy within several seconds then UTC
217+
could be used as the stable time source. However, if the external source
218+
adjusts to a leap second there will be a discontinuity: the elapsed time
219+
between two observations taken at 1 Hz is not equal to the numeric difference
220+
between their timestamps.
221+
222+
For precise activities a continuous scale that is independent of local and
223+
solar adjustments simplifies things considerably. Suitable continuous scales
224+
include:
225+
226+
- GPS time: epoch of 1980-01-06T00:00:00Z, continuous following TAI with an
227+
offset of TAI-GPS=19 s.
228+
- Bluetooth mesh time: epoch of 2000-01-01T00:00:00Z, continuous following TAI
229+
with an offset of -32.
230+
- UNIX Leap Time: epoch of 1970-01-01T00:00:00Z, continuous following TAI with
231+
an offset of -8.
232+
233+
Because C and Zephyr library functions support conversion between integral and
234+
calendar time representations using the UNIX epoch, UNIX Leap Time is an ideal
235+
choice for the external time scale.
236+
237+
The mechanism used to populate synchronization points is not relevant: it may
238+
involve reading from a local high-precision RTC peripheral, exchanging packets
239+
over a network using a protocol like NTP or PTP, or processing NMEA messages
240+
received a GPS with or without a 1pps signal.

include/sys/timeutil.h

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,13 @@
2929
extern "C" {
3030
#endif
3131

32+
/**
33+
* @defgroup timeutil_apis Time Utility APIs
34+
* @defgroup timeutil_repr_apis Time Representation APIs
35+
* @ingroup timeutil_apis
36+
* @{
37+
*/
38+
3239
/**
3340
* @brief Convert broken-down time to a POSIX epoch offset in seconds.
3441
*
@@ -53,6 +60,13 @@ int64_t timeutil_timegm64(const struct tm *tm);
5360
*/
5461
time_t timeutil_timegm(const struct tm *tm);
5562

63+
/**
64+
* @}
65+
* @defgroup timeutil_sync_apis Time Synchronization APIs
66+
* @ingroup timeutil_apis
67+
* @{
68+
*/
69+
5670
/**
5771
* @brief Immutable state for synchronizing two clocks.
5872
*
@@ -287,4 +301,8 @@ int32_t timeutil_sync_skew_to_ppb(float skew);
287301
}
288302
#endif
289303

304+
/**
305+
* @}
306+
*/
307+
290308
#endif /* ZEPHYR_INCLUDE_SYS_TIMEUTIL_H_ */

0 commit comments

Comments
 (0)