zephyr/
time.rs

1// Copyright (c) 2024 Linaro LTD
2// SPDX-License-Identifier: Apache-2.0
3
4//! Time types designed for Zephyr, inspired by `std::time`.
5//!
6//! In `std`, there are two primary time types: `Duration`, representing a span of time, and
7//! `Instant`, representing a specific point in time. Both have nanosecond precision, which is
8//! well-suited to more powerful machines. However, on embedded systems like Zephyr, this precision
9//! can lead to performance issues, often requiring divisions whenever timeouts are used.
10//!
11//! In the Rust embedded ecosystem, the `fugit` crate is commonly used for handling time. It
12//! provides both `Duration` and `Instant` types, but with parameters that allow the representation
13//! to match the time slice used, enabling compile-time conversion and storing time directly as
14//! tick counts.
15//!
16//! Zephyr manages time in terms of system tick intervals, derived from
17//! `sys_clock_hw_cycles_per_sec()`.  This model aligns well with `fugit`, especially when the
18//! types are properly parameterized.
19//!
20//! It's important to note that Rust’s `std::Instant` requires time to be monotonically increasing.
21//!
22//! Zephyr’s `sys/time_units.h` provides a variety of optimized macros for manipulating time
23//! values, converting between human-readable units and ticks, and minimizing divisions (especially
24//! by non-constant values).  Similarly, the `fugit` crate offers constructors that aim to result
25//! in constants when possible, avoiding costly division operations.
26
27use zephyr_sys::{k_ticks_t, k_timeout_t, k_uptime_ticks};
28
29use core::fmt::Debug;
30
31// The system ticks, is mostly a constant, but there are some boards that use a dynamic tick
32// frequency, and thus need to read this at runtime.
33#[cfg(CONFIG_TIMER_READS_ITS_FREQUENCY_AT_RUNTIME)]
34compile_error!("Rust does not (yet) support dynamic frequency timer");
35
36// Given the above not defined, the system time base comes from a kconfig.
37/// The system time base.  The system clock has this many ticks per second.
38pub const SYS_FREQUENCY: u32 = crate::kconfig::CONFIG_SYS_CLOCK_TICKS_PER_SEC as u32;
39
40/// Zephyr can be configured for either 64-bit or 32-bit time values.  Use the appropriate type
41/// internally to match.  This should end up the same size as `k_ticks_t`, but unsigned instead of
42/// signed.
43#[cfg(CONFIG_TIMEOUT_64BIT)]
44pub type Tick = u64;
45#[cfg(not(CONFIG_TIMEOUT_64BIT))]
46pub type Tick = u32;
47
48/// Duration appropriate for Zephyr calls that expect `k_timeout_t`.  The result will be a time
49/// interval from "now" (when the call is made).
50pub type Duration = fugit::Duration<Tick, 1, SYS_FREQUENCY>;
51
52/// An Instant appropriate for Zephyr calls that expect a `k_timeout_t`.  The result will be an
53/// absolute time in terms of system ticks.
54#[cfg(CONFIG_TIMEOUT_64BIT)]
55pub type Instant = fugit::Instant<Tick, 1, SYS_FREQUENCY>;
56
57/// Retrieve the current scheduler time as an Instant.  This can be used to schedule timeouts at
58/// absolute points in time.
59pub fn now() -> Instant {
60    Instant::from_ticks(unsafe { k_uptime_ticks() as u64 })
61}
62
63// The Zephyr `k_timeout_t` represents several different types of intervals, based on the range of
64// the value.  It is a signed number of the same size as the Tick here, which effectively means it
65// is one bit less.
66//
67// 0: K_NO_WAIT: indicates the operation should not wait.
68// 1 .. Max: indicates a duration in ticks of a delay from "now".
69// -1: K_FOREVER: a time that never expires.
70// MIN .. -2: A wait for an absolute amount of ticks from the start of the system.
71//
72// The absolute time offset is only implemented when time is a 64-bit value.  This also means that
73// "Instant" isn't available when time is defined as a 32-bit value.
74
75/// Wrapper around the timeout type, so we can implement From/Info.
76///
77/// This wrapper allows us to implement `From` and `Info` from the Fugit types into the Zephyr
78/// types.  This allows various methods to accept either value, as well as the `Forever` and
79/// `NoWait` values defined here.
80#[derive(Clone, Copy, Debug)]
81pub struct Timeout(pub k_timeout_t);
82
83impl PartialEq for Timeout {
84    fn eq(&self, other: &Self) -> bool {
85        self.0.ticks == other.0.ticks
86    }
87}
88
89impl Eq for Timeout {}
90
91// `From` allows methods to take a time of various types and convert it into a Zephyr timeout.
92impl From<Duration> for Timeout {
93    fn from(value: Duration) -> Timeout {
94        let ticks: k_ticks_t = checked_cast(value.ticks());
95        debug_assert_ne!(ticks, crate::sys::K_FOREVER.ticks);
96        debug_assert_ne!(ticks, crate::sys::K_NO_WAIT.ticks);
97        Timeout(k_timeout_t { ticks })
98    }
99}
100
101#[cfg(CONFIG_TIMEOUT_64BIT)]
102impl From<Instant> for Timeout {
103    fn from(value: Instant) -> Timeout {
104        let ticks: k_ticks_t = checked_cast(value.ticks());
105        debug_assert_ne!(ticks, crate::sys::K_FOREVER.ticks);
106        debug_assert_ne!(ticks, crate::sys::K_NO_WAIT.ticks);
107        Timeout(k_timeout_t {
108            ticks: -1 - 1 - ticks,
109        })
110    }
111}
112
113/// A sleep that waits forever.  This is its own type, that is `Into<Timeout>` and can be used
114/// anywhere a timeout is needed.
115pub struct Forever;
116
117impl From<Forever> for Timeout {
118    fn from(_value: Forever) -> Timeout {
119        Timeout(crate::sys::K_FOREVER)
120    }
121}
122
123/// A sleep that doesn't ever wait.  This is its own type, that is `Info<Timeout>` and can be used
124/// anywhere a timeout is needed.
125pub struct NoWait;
126
127impl From<NoWait> for Timeout {
128    fn from(_valued: NoWait) -> Timeout {
129        Timeout(crate::sys::K_NO_WAIT)
130    }
131}
132
133/// Put the current thread to sleep, for the given duration.  Uses `k_sleep` for the actual sleep.
134/// Returns a duration roughly representing the remaining amount of time if the sleep was woken.
135pub fn sleep<T>(timeout: T) -> Duration
136where
137    T: Into<Timeout>,
138{
139    let timeout: Timeout = timeout.into();
140    let rest = unsafe { crate::raw::k_sleep(timeout.0) };
141    Duration::millis(rest as Tick)
142}
143
144/// Convert from the Tick time type, which is unsigned, to the `k_ticks_t` type. When debug
145/// assertions are enabled, it will panic on overflow.
146fn checked_cast<I, O>(tick: I) -> O
147where
148    I: TryInto<O>,
149    I::Error: Debug,
150    O: Default,
151{
152    if cfg!(debug_assertions) {
153        tick.try_into().expect("Overflow in time conversion")
154    } else {
155        tick.try_into().unwrap_or(O::default())
156    }
157}