Zephyr provides a powerful and extensible time framework for obtaining and tracking timing events from hardware timing sources of any precision.
Time Units
The kernel time is tracked in the following time units:
-
Real Time -
Hardware Counter Cycles -
Ticks
Real Time
The kernel time is described in real-time units: milliseconds/microseconds, which is easy to understand but cannot match the highest precision of the underlying hardware.
Hardware Counter Cycles
The kernel time is described by the number of cycles of the hardware timer used by the kernel, which provides “cycle” counts through k_cycle_get_32()
and k_cycle_get_64()
. Typically, an SoC has dedicated hardware timers that operate at the same frequency as the CPU, and reading this timer is very fast. Therefore, the precision of kernel time described by hardware counter cycles is the highest, and these APIs can be used when high precision measurement of application code time is required. Normally, the frequency of this counter remains constant (CONFIG_SYS_CLOCK_HW_CYCLES_PER_SEC), and it can be obtained at runtime through sys_clock_hw_cycles_per_sec()
.
Ticks
For asynchronous timing, the kernel defines a concept of “ticks”. A “tick” is the smallest time unit for executing operating system threads. In most cases, an interrupt is triggered every time a tick arrives. The tick rate is configured through CONFIG_SYS_CLOCK_TICKS_PER_SEC
. The default value for most hardware platforms (that support setting any interrupt timeout) does not exceed 10 kHz, while software emulation platforms use a conventional 100 Hz.
Conversion Between Units
Zephyr provides APIs for converting between the above units, allowing conversions between real time, cycles, and ticks, with rounding occurring due to the relationship of conversion factors. The output precision can be specified as 32-bit or 64-bit. The conversion function API is defined in zephyr/include/zephyr/sys/time_units.h
, and the function form is as follows: k_<src>_to_<dst>_<rounding><precision></precision></rounding></dst></src>
-
Optional values for src and dst -
ms milliseconds -
us microseconds -
ns nanoseconds -
cyc cycles -
ticks tick count -
Rounding -
floor round down: 4.2, 4.9 rounds to 4 -
ceil round up: 4.2, 4.9 rounds to 5 -
near nearest rounding, rounding up or down, 4.5 rounds to 5, 4.4 rounds to 4 -
Precision -
32 32-bit output -
64 64-bit output
For example, if you want to convert us to ticks, output 32-bit, and round to nearest, then the API used would be k_us_to_ticks_near32()
. All conversion functions are ultimately implemented through the following function:
static TIME_CONSTEXPR ALWAYS_INLINE uint64_t z_tmcvt ( uint64_t t, uint32_t from_hz, uint32_t to_hz, bool const_hz, bool result32, bool round_up, bool round_off )
System Uptime
The kernel provides the system uptime API defined in zephyr/include/zephyr/kernel.h
, with function forms as follows:
__syscall int64_t k_uptime_ticks ( void ) ;
units in ticks
static inline int64_t k_uptime_get ( void )
units in ms
static inline uint32_t k_uptime_get_32 ( void )
units in ms
The following API can calculate the uptime from a reference time reftime
: static inline int64_t k_uptime_delta ( int64_t *reftime )
Timeout
Zephyr’s kernel objects extensively use timeout mechanisms, such as kernel object blocking and k_timer
delayed timing, where timeouts in Zephyr are specified in ticks. Zephyr uses k_timeout_t
to specify specific timeout values:
typedef struct { k_ticks_t ticks;} k_timeout_t;
k_timeout_t
is actually specified by ticks, and in Zephyr, the precision of ticks is specified by CONFIG_TIMEOUT_64BIT
as either 32-bit or 64-bit.
# ifdef CONFIG_TIMEOUT_64BITtypedef int64_t k_ticks_t;# elsetypedef uint32_t k_ticks_t;# endif
In general scenarios, due to different SoCs and configurations, the number of ticks per second varies, but users of APIs that define timeouts prefer to use real time, for example, waiting for a timeout of 100us is more intuitive than waiting for a timeout of 1 tick. Therefore, Zephyr provides a series of APIs in zephyr/include/zephyr/kernel.h
to convert real time to k_timeout_t
:
# define K_CYC ( t ) Z_TIMEOUT_CYC ( t )# define K_TICKS ( t ) Z_TIMEOUT_TICKS ( t )# define K_NSEC ( t ) Z_TIMEOUT_NS ( t )# define K_USEC ( t ) Z_TIMEOUT_US ( t )# define K_MSEC ( ms ) Z_TIMEOUT_MS ( ms )# define K_SECONDS ( s ) K_MSEC (( s ) * MSEC_PER_SEC )# define K_MINUTES ( m ) K_SECONDS (( m ) * 60 )# define K_HOURS ( h ) K_MINUTES (( h ) * 60 )
There are also two special timeouts, one for non-waiting and one for forever waiting:
# define K_NO_WAIT Z_TIMEOUT_NO_WAIT# define K_FOREVER Z_FOREVER
All the above are relative wait times, meaning how long to wait from now. In addition, Zephyr also provides APIs for calculating absolute wait times, which means ending after a certain time since boot. In the kernel, only relative waiting is implemented, and absolute waiting is achieved by subtracting the waiting time from the current time to calculate the relative waiting time. A series of APIs are provided in zephyr/include/zephyr/kernel.h
to convert absolute time to k_timeout_t
:
# define K_TIMEOUT_ABS_TICKS ( t ) \ Z_TIMEOUT_TICKS ( Z_TICK_ABS (( k_ticks_t ) MAX ( t, 0 )) )# define K_TIMEOUT_ABS_MS ( t ) K_TIMEOUT_ABS_TICKS ( k_ms_to_ticks_ceil64 ( t ))# define K_TIMEOUT_ABS_US ( t ) K_TIMEOUT_ABS_TICKS ( k_us_to_ticks_ceil64 ( t ))# define K_TIMEOUT_ABS_NS ( t ) K_TIMEOUT_ABS_TICKS ( k_ns_to_ticks_ceil64 ( t ))# define K_TIMEOUT_ABS_CYC ( t ) K_TIMEOUT_ABS_TICKS ( k_cyc_to_ticks_ceil64 ( t ))
If ticks are only 32-bit in precision, the system’s tick timing will overflow and recalculate in a short time, so absolute timeout APIs are only allowed when CONFIG_TIMEOUT_64BIT=y
, and ticks are 64-bit.
Implementation Details
Implementation details related to kernel time can be referenced in the following articles:
-
Introduction to Zephyr Kernel Timeout Module -
Analysis of Zephyr Thread Blocking and Timeout Mechanisms -
Introduction to Zephyr Tick Clock -
Analysis of Zephyr Kernel Time Slicing Implementation -
Detailed Explanation of Zephyr Kernel Tickless
These articles were completed earlier, and some API names are inconsistent with the latest code, for example, z_clock_<xxx></xxx>
has changed to sys_clock_<xxx></xxx>
, but the main flow has not changed and can still serve as a reference for understanding and analyzing the latest code.
References
https://docs.zephyrproject.org/3.4.0/kernel/services/timing/clocks.html