Skip to main content

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