Skip to content

Commit 213032f

Browse files
committed
rust: Duration and Timeout types
Use specific instances of `fugit::Duration` and `fugit::Instant`, with the clock parameter based on the configured clock rate. As the fugit times are fixed, this precludes using these time types with dynamically changing clocks. With this, is a local Timeout type that simply wraps `k_timeout_t`. Having this wrapper allows us to implement `From` from each of the timeout types. The zephyr timeout type can support either durations or instants, as well as two magic times, forever and no wait. Forever and NoWait are implemented as singleton types that also have conversions. The `zephyr::time::sleep` function then can take anything that can be converted to this wrapped Timeout type, including Forever, and NoWait. This template will be used for future API calls that need a timeout. Signed-off-by: David Brown <[email protected]>
1 parent 337a2d1 commit 213032f

File tree

3 files changed

+109
-0
lines changed

3 files changed

+109
-0
lines changed

lib/rust/zephyr-sys/build.rs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -67,6 +67,9 @@ fn main() -> Result<()> {
6767
.derive_copy(false)
6868
.allowlist_function("k_.*")
6969
.allowlist_function("gpio_.*")
70+
.allowlist_function("sys_.*")
71+
// Deprecated
72+
.blocklist_function("sys_clock_timeout_end_calc")
7073
.parse_callbacks(Box::new(bindgen::CargoCallbacks::new()))
7174
.generate()
7275
.expect("Unable to generate bindings");

lib/rust/zephyr/src/lib.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,8 @@
99
#![no_std]
1010
#![allow(unexpected_cfgs)]
1111

12+
pub mod time;
13+
1214
// Bring in the generated kconfig module
1315
include!(concat!(env!("OUT_DIR"), "/kconfig.rs"));
1416

lib/rust/zephyr/src/time.rs

Lines changed: 104 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,104 @@
1+
//! Time types similar to `std::time` types.
2+
//!
3+
//! However, the rust-embedded world tends to use `fugit` for time. This has a Duration and
4+
//! Instant type, but they are parameterized by the slice used, and try hard to do the conversion
5+
//! at compile time.
6+
//!
7+
//! std has two time types, a Duration which represents an elapsed amount of time, and Instant,
8+
//! which represents a specific instance in time.
9+
//!
10+
//! Zephyr typically coordinates time in terms of a system tick interval, which comes from
11+
//! `sys_clock_hw_cycles_per_sec()`.
12+
//!
13+
//! The Rust/std semantics require Instant to be monotonically increasing.
14+
//!
15+
//! Zephyr's `sys/time_units.h` header contains numerous optimized macros for manipulating time in
16+
//! these units, specifically for converting between human time units and ticks, trying to avoid
17+
//! division, especially division by non-constants.
18+
19+
use zephyr_sys::k_timeout_t;
20+
21+
// The system ticks, is mostly a constant, but there are some boards that use a dynamic tick
22+
// frequency, and thus need to read this at runtime.
23+
#[cfg(CONFIG_TIMER_READS_ITS_FREQUENCY_AT_RUNTIME)]
24+
compile_error!("Rust does not (yet) support dynamic frequency timer");
25+
26+
// Given the above not defined, the system time base comes from a kconfig.
27+
/// The system time base. The system clock has this many ticks per second.
28+
pub const SYS_FREQUENCY: u32 = crate::kconfig::CONFIG_SYS_CLOCK_TICKS_PER_SEC as u32;
29+
30+
/// Zephyr can be configured for either 64-bit or 32-bit time values. Use the appropriate type
31+
/// internally to match. This should end up the same size as `k_ticks_t`, but unsigned instead of
32+
/// signed.
33+
#[cfg(CONFIG_TIMEOUT_64BIT)]
34+
pub type Tick = u64;
35+
#[cfg(not(CONFIG_TIMEOUT_64BIT))]
36+
pub type Tick = u32;
37+
38+
/// Duration appropraite for Zephyr calls that expect `k_timeout_t`. The result will be a time
39+
/// interval from "now" (when the call is made).
40+
pub type Duration = fugit::Duration<Tick, 1, SYS_FREQUENCY>;
41+
42+
/// An Instant appropriate for Zephyr calls that expect a `k_timeout_t`. The result will be an
43+
/// absolute time in terms of system ticks.
44+
#[cfg(CONFIG_TIMEOUT_64BIT)]
45+
pub type Instant = fugit::Instant<Tick, 1, SYS_FREQUENCY>;
46+
47+
// The zephry k_timeout_t represents several different types of intervals, based on the range of
48+
// the value. It is a signed number of the same size as the Tick here, which effectively means it
49+
// is one bit less.
50+
//
51+
// 0: K_NO_WAIT: indicates the operation should not wait.
52+
// 1 .. Max: indicates a duration in ticks of a delay from "now".
53+
// -1: K_FOREVER: a time that never expires.
54+
// MIN .. -2: A wait for an absolute amount of ticks from the start of the system.
55+
//
56+
// The absolute time offset is only implemented when time is a 64 bit value. This also means that
57+
// "Instant" isn't available when time is defined as a 32-bit value.
58+
59+
// Wrapper around the timeout type, so we can implement From/Info.
60+
pub struct Timeout(pub k_timeout_t);
61+
62+
// From allows methods to take a time of various types and convert it into a Zephyr timeout.
63+
impl From<Duration> for Timeout {
64+
fn from(value: Duration) -> Timeout {
65+
Timeout(k_timeout_t { ticks: value.ticks() as i64 })
66+
}
67+
}
68+
69+
#[cfg(CONFIG_TIMEOUT_64BIT)]
70+
impl From<Instant> for Timeout {
71+
fn from(value: Instant) -> Timeout {
72+
Timeout(k_timeout_t { ticks: -1 - 1 - (value.ticks() as i64) })
73+
}
74+
}
75+
76+
/// A sleep that waits forever. This is its own type, that is `Into<Timeout>` and can be used
77+
/// anywhere a timeout is needed.
78+
pub struct Forever;
79+
80+
impl From<Forever> for Timeout {
81+
fn from(_value: Forever) -> Timeout {
82+
Timeout(crate::sys::K_FOREVER)
83+
}
84+
}
85+
86+
/// A sleep that doesn't ever wait. This is its own type, that is `Info<Timeout>` and can be used
87+
/// anywhere a timeout is needed.
88+
pub struct NoWait;
89+
90+
impl From<NoWait> for Timeout {
91+
fn from(_valued: NoWait) -> Timeout {
92+
Timeout(crate::sys::K_NO_WAIT)
93+
}
94+
}
95+
96+
/// Put the current thread to sleep, for the given duration. Uses `k_sleep` for the actual sleep.
97+
/// Returns a duration roughly representing the remaining amount of time if the sleep was woken.
98+
pub fn sleep<T>(timeout: T) -> Duration
99+
where T: Into<Timeout>,
100+
{
101+
let timeout: Timeout = timeout.into();
102+
let rest = unsafe { crate::raw::k_sleep(timeout.0) };
103+
Duration::millis(rest as Tick)
104+
}

0 commit comments

Comments
 (0)