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