zephyr/doc/kernel/timing/clocks.rst

174 lines
5.7 KiB
ReStructuredText

.. _clocks_v2:
Kernel Clocks
#############
The kernel's clocks are the foundation for all of its time-based services.
.. contents::
:local:
:depth: 2
Concepts
********
The kernel supports two distinct clocks.
* The 32-bit **hardware clock** is a high precision counter that tracks time
in unspecified units called **cycles**. The duration of a cycle is determined
by the board hardware used by the kernel, and is typically measured
in nanoseconds.
* The 64-bit **system clock** is a counter that tracks the number of
**ticks** that have elapsed since the kernel was initialized. The duration
of a tick is is configurable, and typically ranges from 1 millisecond to
100 milliseconds.
The kernel also provides a number of variables that can be used
to convert the time units used by the clocks into standard time units
(e.g. seconds, milliseconds, nanoseconds, etc), and to convert between
the two types of clock time units.
The system clock is used by most of the kernel's time-based services, including
kernel timer objects and the timeouts supported by other kernel object types.
For convenience, the kernel's APIs allow time durations to be specified
in milliseconds, and automatically converts them to the corresponding
number of ticks.
The hardware clock can be used to measure time with higher precision than
that provided by kernel services based on the system clock.
.. _clock_limitations:
Clock Limitations
=================
The system clock's tick count is derived from the hardware clock's cycle
count. The kernel determines how many clock cycles correspond to the desired
tick frequency, then programs the hardware clock to generate an interrupt
after that many cycles; each interrupt corresponds to a single tick.
.. note::
Configuring a smaller tick duration permits finer-grained timing,
but also increases the amount of work the kernel has to do to process
tick interrupts since they occur more frequently. Setting the tick
duration to zero disables *both* kernel clocks, as well as their
associated services.
Any millisecond-based time interval specified using a kernel API
represents the **minimum** delay that will occur,
and may actually take longer than the amount of time requested.
For example, specifying a timeout delay of 100 ms when attempting to take
a semaphore means that the kernel will never terminate the operation
and report failure before at least 100 ms have elapsed. However,
it is possible that the operation may take longer than 100 ms to complete,
and may either complete successfully during the additional time
or fail at the end of the added time.
The amount of added time that occurs during a kernel object operation
depends on the following factors.
* The added time introduced by rounding up the specified time interval
when converting from milliseconds to ticks. For example, if a tick duration
of 10 ms is being used, a specified delay of 25 ms will be rounded up
to 30 ms.
* The added time introduced by having to wait for the next tick interrupt
before a delay can be properly tracked. For example, if a tick duration
of 10 ms is being used, a specified delay of 20 ms requires the kernel
to wait for 3 ticks to occur (rather than only 2), since the first tick
can occur at any time from the next fraction of a millisecond to just
slightly less than 10 ms; only after the first tick has occurred does
the kernel know the next 2 ticks will take 20 ms.
Implementation
**************
Measuring Time with Normal Precision
====================================
This code uses the system clock to determine how much time has elapsed
between two points in time.
.. code-block:: c
int64_t time_stamp;
int64_t milliseconds_spent;
/* capture initial time stamp */
time_stamp = k_uptime_get();
/* do work for some (extended) period of time */
...
/* compute how long the work took (also updates the time stamp) */
milliseconds_spent = k_uptime_delta(&time_stamp);
Measuring Time with High Precision
==================================
This code uses the hardware clock to determine how much time has elapsed
between two points in time.
.. code-block:: c
uint32_t start_time;
uint32_t stop_time;
uint32_t cycles_spent;
uint32_t nanoseconds_spent;
/* capture initial time stamp */
start_time = k_cycle_get_32();
/* do work for some (short) period of time */
...
/* capture final time stamp */
stop_time = k_cycle_get_32();
/* compute how long the work took (assumes no counter rollover) */
cycles_spent = stop_time - start_time;
nanoseconds_spent = SYS_CLOCK_HW_CYCLES_TO_NS(cycles_spent);
Suggested Uses
**************
Use services based on the system clock for time-based processing
that does not require high precision,
such as :ref:`timer objects <timers_v2>` or :ref:`thread_sleeping`.
Use services based on the hardware clock for time-based processing
that requires higher precision than the system clock can provide,
such as :ref:`busy_waiting` or fine-grained time measurements.
.. note::
The high frequency of the hardware clock, combined with its 32-bit size,
means that counter rollover must be taken into account when taking
high-precision measurements over an extended period of time.
Configuration
*************
Related configuration options:
* :option:`CONFIG_SYS_CLOCK_TICKS_PER_SEC`
APIs
****
The following kernel clock APIs are provided by :file:`kernel.h`:
* :cpp:func:`k_uptime_get()`
* :cpp:func:`k_uptime_get_32()`
* :cpp:func:`k_uptime_delta()`
* :cpp:func:`k_uptime_delta_32()`
* :cpp:func:`k_cycle_get_32()`
* :c:macro:`SYS_CLOCK_HW_CYCLES_TO_NS`
* :c:macro:`K_NO_WAIT`
* :c:macro:`K_MSEC`
* :c:macro:`K_SECONDS`
* :c:macro:`K_MINUTES`
* :c:macro:`K_HOURS`
* :c:macro:`K_FOREVER`