Skip to main content

ws63_hal/
time.rs

1//! Timekeeping types for WS63 HAL.
2//!
3//! Provides `Instant`, `Duration`, and `Rate` types for timing operations.
4//! Uses the TCXO 64-bit counter as the hardware time source.
5
6use crate::peripherals::Tcxo;
7
8/// A measurement of a monotonically non-decreasing clock.
9/// Opaque and useful with `Duration`.
10#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
11pub struct Instant(u64);
12
13impl Instant {
14    /// Returns an instant corresponding to "now".
15    pub fn now() -> Self {
16        // SAFETY: TCXO PAC pointer is a valid physical MMIO address (0x4400_04C0)
17        let tcxo = unsafe { &*Tcxo::ptr() };
18        // Latch the counter
19        let status = tcxo.tcxo_status().read().bits();
20        unsafe {
21            tcxo.tcxo_status().write(|w| w.bits(status | 0x01));
22        }
23        while tcxo.tcxo_status().read().bits() & 0x10 == 0 {}
24        // Read 64-bit counter
25        let c0 = tcxo.tcxo_count0().read().bits() as u64;
26        let c1 = tcxo.tcxo_count1().read().bits() as u64;
27        let c2 = tcxo.tcxo_count2().read().bits() as u64;
28        let c3 = tcxo.tcxo_count3().read().bits() as u64;
29        Instant((c3 << 48) | (c2 << 32) | (c1 << 16) | c0)
30    }
31
32    /// Returns the amount of time elapsed since this instant.
33    pub fn elapsed(&self) -> Duration {
34        Instant::now().checked_duration_since(*self).unwrap_or(Duration::from_micros(0))
35    }
36
37    /// Returns the amount of time elapsed from another instant to this one.
38    pub fn duration_since(&self, earlier: Instant) -> Duration {
39        self.checked_duration_since(earlier).unwrap_or(Duration::from_micros(0))
40    }
41
42    /// Returns `Some(Duration)` if `earlier` is earlier than `self`, or `None` otherwise.
43    pub fn checked_duration_since(&self, earlier: Instant) -> Option<Duration> {
44        if self.0 >= earlier.0 { Some(Duration::from_micros(self.0 - earlier.0)) } else { None }
45    }
46
47    /// Returns the raw counter value.
48    pub fn raw(&self) -> u64 {
49        self.0
50    }
51}
52
53/// A duration of time.
54#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Default)]
55pub struct Duration(u64); // microseconds
56
57impl Duration {
58    /// Create a duration from microseconds.
59    pub const fn from_micros(micros: u64) -> Self {
60        Duration(micros)
61    }
62
63    /// Create a duration from milliseconds.
64    pub const fn from_millis(millis: u64) -> Self {
65        Duration(millis * 1_000)
66    }
67
68    /// Create a duration from seconds.
69    pub const fn from_secs(secs: u64) -> Self {
70        Duration(secs * 1_000_000)
71    }
72
73    /// Return the total number of microseconds.
74    pub const fn as_micros(&self) -> u64 {
75        self.0
76    }
77
78    /// Return the total number of milliseconds.
79    pub const fn as_millis(&self) -> u64 {
80        self.0 / 1_000
81    }
82
83    /// Return the total number of seconds.
84    pub const fn as_secs(&self) -> u64 {
85        self.0 / 1_000_000
86    }
87}
88
89impl core::ops::Add for Duration {
90    type Output = Self;
91    fn add(self, rhs: Self) -> Self {
92        Duration(self.0 + rhs.0)
93    }
94}
95
96impl core::ops::AddAssign for Duration {
97    fn add_assign(&mut self, rhs: Self) {
98        self.0 += rhs.0;
99    }
100}
101
102impl core::ops::Sub for Duration {
103    type Output = Self;
104    fn sub(self, rhs: Self) -> Self {
105        Duration(self.0.saturating_sub(rhs.0))
106    }
107}
108
109/// A rate in Hz.
110#[derive(Debug, Clone, Copy, PartialEq, Eq)]
111pub struct Rate(u32);
112
113impl Rate {
114    pub const fn from_hz(hz: u32) -> Self {
115        Rate(hz)
116    }
117    pub const fn from_khz(khz: u32) -> Self {
118        Rate(khz * 1_000)
119    }
120    pub const fn from_mhz(mhz: u32) -> Self {
121        Rate(mhz * 1_000_000)
122    }
123    pub const fn to_hz(&self) -> u32 {
124        self.0
125    }
126    pub const fn to_khz(&self) -> u32 {
127        self.0 / 1_000
128    }
129}
130
131// ── Tests ────────────────────────────────────────────────────────
132
133#[cfg(test)]
134mod tests {
135    use super::*;
136
137    #[test]
138    fn test_duration_constructors() {
139        assert_eq!(Duration::from_micros(500).as_micros(), 500);
140        assert_eq!(Duration::from_millis(1).as_micros(), 1000);
141        assert_eq!(Duration::from_secs(1).as_micros(), 1_000_000);
142        assert_eq!(Duration::from_micros(1500).as_millis(), 1);
143    }
144
145    #[test]
146    fn test_duration_add_sub() {
147        let a = Duration::from_micros(100);
148        let b = Duration::from_micros(50);
149        assert_eq!((a + b).as_micros(), 150);
150        assert_eq!((a - b).as_micros(), 50);
151        // Saturating subtract
152        assert_eq!((b - a).as_micros(), 0);
153    }
154
155    #[test]
156    fn test_duration_default() {
157        assert_eq!(Duration::default().as_micros(), 0);
158    }
159
160    #[test]
161    fn test_rate_constructors() {
162        assert_eq!(Rate::from_hz(1000).to_hz(), 1000);
163        assert_eq!(Rate::from_khz(1).to_hz(), 1000);
164        assert_eq!(Rate::from_mhz(1).to_hz(), 1_000_000);
165        assert_eq!(Rate::from_mhz(240).to_khz(), 240_000);
166    }
167
168    #[test]
169    fn test_instant_checked_duration() {
170        let early = Instant(100);
171        let late = Instant(200);
172        assert_eq!(late.checked_duration_since(early).unwrap().as_micros(), 100);
173        assert!(early.checked_duration_since(late).is_none());
174    }
175}