Skip to main content

acex_sim/
clock.rs

1// region: Imports
2
3// endregion: Imports
4
5// region: Instant
6
7/// A point in time represented as microseconds since an arbitrary epoch.
8///
9/// The epoch is not defined - only differences between `Instant` values
10/// are meaningful. On simulation targets the epoch is the start of the
11/// simulation. On real targets it is typically system boot.
12#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
13#[cfg_attr(feature = "defmt", derive(defmt::Format))]
14pub struct Instant(u64);
15
16impl Instant {
17    pub const ZERO: Self = Self(0);
18
19    #[inline]
20    pub fn from_micros(us: u64) -> Self {
21        Self(us)
22    }
23
24    #[inline]
25    pub fn as_micros(&self) -> u64 {
26        self.0
27    }
28
29    #[inline]
30    pub fn checked_duration_since(&self, earlier: Self) -> Option<Duration> {
31        self.0.checked_sub(earlier.0).map(Duration::from_micros)
32    }
33}
34
35// endregion: Instant
36
37// region: Duration
38
39/// A span of time in microseconds.
40#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
41#[cfg_attr(feature = "defmt", derive(defmt::Format))]
42pub struct Duration(u64);
43
44impl Duration {
45    pub const ZERO: Self = Self(0);
46
47    #[inline]
48    pub fn from_micros(us: u64) -> Self {
49        Self(us)
50    }
51
52    #[inline]
53    pub const fn from_millis(ms: u64) -> Self {
54        Self(ms * 1_000)
55    }
56
57    #[inline]
58    pub fn from_secs(s: u64) -> Self {
59        Self(s * 1_000_000)
60    }
61
62    #[inline]
63    pub fn as_micros(&self) -> u64 {
64        self.0
65    }
66
67    #[inline]
68    pub fn as_millis(&self) -> u64 {
69        self.0 / 1_000
70    }
71
72    #[inline]
73    pub fn as_secs(&self) -> u64 {
74        self.0 / 1_000_000
75    }
76}
77
78impl core::ops::Add<Duration> for Instant {
79    type Output = Instant;
80    fn add(self, rhs: Duration) -> Self::Output {
81        Instant(self.0 + rhs.0)
82    }
83}
84
85impl core::ops::Sub<Duration> for Instant {
86    type Output = Instant;
87    fn sub(self, rhs: Duration) -> Self::Output {
88        Instant(self.0.saturating_sub(rhs.0))
89    }
90}
91
92// endregion: Duration
93
94// region: Clock Trait
95
96/// Provides the current time as an [`Instant`].
97///
98/// Implementations must be deterministic within a simulation context -
99/// the same sequence of `now()` calls must return the same values given
100/// the same simulation inputs. On real targets this wraps the hardware
101/// timer or OS monotonic clock.
102pub trait Clock {
103    fn now(&self) -> Instant;
104}
105
106// endregion: Clock Trait
107
108// region: SimClock
109
110#[derive(Debug, Clone)]
111#[cfg_attr(feature = "defmt", derive(defmt::Format))]
112pub struct SimClock {
113    now: Instant,
114}
115
116impl SimClock {
117    pub fn new() -> Self {
118        Self { now: Instant::ZERO }
119    }
120
121    /// Advances the clock by `duration`.
122    pub fn advance(&mut self, duration: Duration) {
123        self.now = self.now + duration;
124    }
125
126    /// Sets the clock to an absolute instant.
127    pub fn set(&mut self, instant: Instant) {
128        self.now = instant
129    }
130}
131
132impl Default for SimClock {
133    fn default() -> Self {
134        Self::new()
135    }
136}
137
138impl Clock for SimClock {
139    fn now(&self) -> Instant {
140        self.now
141    }
142}
143
144// endregion: SimClock