gwr_engine/time/
clock.rs

1// Copyright (c) 2023 Graphcore Ltd. All rights reserved.
2
3//! This module represents the time during a simulation.
4//!
5//! Time is made up of a cycle count and a phase.
6
7use core::cmp::Ordering;
8use std::cell::RefCell;
9use std::future::Future;
10use std::pin::Pin;
11use std::rc::Rc;
12use std::task::{Context, Poll, Waker};
13
14use crate::traits::{Resolve, Resolver};
15
16/// ClockTick structure for representing a number of Clock ticks and a phase.
17#[derive(Clone, Copy, Debug, PartialEq, Eq)]
18pub struct ClockTick {
19    /// Clock ticks.
20    tick: u64,
21
22    #[cfg(feature = "phase")]
23    /// Clock phase.
24    phase: u32,
25}
26
27impl ClockTick {
28    #[must_use]
29    pub fn new() -> Self {
30        Self {
31            tick: 0,
32
33            #[cfg(feature = "phase")]
34            phase: 0,
35        }
36    }
37
38    /// Get the current clock tick.
39    #[must_use]
40    pub fn tick(&self) -> u64 {
41        self.tick
42    }
43
44    /// Get the current clock phase.
45    #[must_use]
46    #[cfg(feature = "phase")]
47    pub fn phase(&self) -> u32 {
48        self.phase
49    }
50
51    /// Change the default constructor value of `tick`.
52    pub fn set_tick(&mut self, tick: u64) -> ClockTick {
53        self.tick = tick;
54        *self
55    }
56
57    /// Change the default constructor value of `phase`.
58    #[cfg(feature = "phase")]
59    pub fn set_phase(&mut self, phase: u32) -> ClockTick {
60        self.phase = phase;
61        *self
62    }
63}
64
65impl Default for ClockTick {
66    fn default() -> Self {
67        Self::new()
68    }
69}
70
71/// Define the comparison operation for SimTime.
72impl Ord for ClockTick {
73    #[cfg(feature = "phase")]
74    fn cmp(&self, other: &Self) -> Ordering {
75        match self.tick.cmp(&other.tick) {
76            Ordering::Greater => Ordering::Greater,
77            Ordering::Less => Ordering::Less,
78            Ordering::Equal => self.phase.cmp(&other.phase),
79        }
80    }
81
82    #[cfg(not(feature = "phase"))]
83    fn cmp(&self, other: &Self) -> Ordering {
84        self.tick.cmp(&other.tick)
85    }
86}
87
88impl PartialOrd for ClockTick {
89    fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
90        Some(self.cmp(other))
91    }
92}
93
94impl std::fmt::Display for ClockTick {
95    #[cfg(feature = "phase")]
96    fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
97        write!(f, "{}.{:?}", self.tick, self.phase)
98    }
99    #[cfg(not(feature = "phase"))]
100    fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
101        write!(f, "{}", self.tick)
102    }
103}
104
105#[derive(Clone)]
106/// State representing a clock.
107pub struct Clock {
108    /// Frequency of the clock in MHz.
109    /// *Note*: Should never be changed as it is registered at this frequency.
110    freq_mhz: f64,
111
112    pub shared_state: Rc<ClockState>,
113}
114
115pub struct TaskWaker {
116    /// The Waker to use to make a task active again.
117    pub waker: Waker,
118
119    /// When a task is scheduled in the future it may be a background task
120    /// that will simply run forever in which case it will set `can_exit` to
121    /// true.
122    pub can_exit: bool,
123}
124
125/// Shared state between futures using a Clock and the Clock itself.
126pub struct ClockState {
127    now: RefCell<ClockTick>,
128
129    /// Queue of futures waiting for the right time.
130    pub waiting: RefCell<Vec<Vec<TaskWaker>>>,
131
132    /// Queue of times at which those futures are to be woken. This is kept
133    /// sorted by time so that the first entry is the next to be woken.
134    pub waiting_times: RefCell<Vec<ClockTick>>,
135
136    /// Registered [`Resolve`] functions.
137    pub to_resolve: RefCell<Vec<Rc<dyn Resolve + 'static>>>,
138}
139
140impl ClockState {
141    fn schedule(&self, schedule_time: ClockTick, cx: &mut Context<'_>, can_exit: bool) {
142        let mut waiting_times = self.waiting_times.borrow_mut();
143        let mut waiting = self.waiting.borrow_mut();
144        if let Some(index) = waiting_times.iter().position(|&x| x == schedule_time) {
145            // Time already exists, add this task
146            waiting[index].push(TaskWaker {
147                waker: cx.waker().clone(),
148                can_exit,
149            });
150        } else {
151            // Time not found, insert at the correct location
152            match waiting_times.iter().position(|x| *x < schedule_time) {
153                Some(index) => {
154                    // Insert at an arbitrary index
155                    waiting_times.insert(index, schedule_time);
156                    waiting.insert(
157                        index,
158                        vec![TaskWaker {
159                            waker: cx.waker().clone(),
160                            can_exit,
161                        }],
162                    );
163                }
164                None => {
165                    // Insert at the head
166                    waiting_times.push(schedule_time);
167                    waiting.push(vec![TaskWaker {
168                        waker: cx.waker().clone(),
169                        can_exit,
170                    }]);
171                }
172            }
173        }
174    }
175
176    fn advance_time(&self, to_time: ClockTick) {
177        self.resolve();
178        if to_time != *self.now.borrow() {
179            assert!(to_time >= *self.now.borrow(), "Time moving backwards");
180            *self.now.borrow_mut() = to_time;
181        }
182    }
183
184    fn resolve(&self) {
185        for r in self.to_resolve.borrow_mut().drain(..) {
186            r.resolve();
187        }
188    }
189}
190
191impl Clock {
192    /// Create a new [Clock] at the specified frequency.
193    #[must_use]
194    pub fn new(freq_mhz: f64) -> Self {
195        let shared_state = Rc::new(ClockState {
196            now: RefCell::new(ClockTick {
197                tick: 0,
198                #[cfg(feature = "phase")]
199                phase: 0,
200            }),
201            waiting: RefCell::new(Vec::new()),
202            waiting_times: RefCell::new(Vec::new()),
203            to_resolve: RefCell::new(Vec::new()),
204        });
205
206        Self {
207            freq_mhz,
208            shared_state,
209        }
210    }
211
212    /// Returns the clocks frequency in MHz.
213    #[must_use]
214    pub fn freq_mhz(&self) -> f64 {
215        self.freq_mhz
216    }
217
218    /// Returns the current [ClockTick].
219    #[must_use]
220    pub fn tick_now(&self) -> ClockTick {
221        *self.shared_state.now.borrow()
222    }
223
224    /// Returns the current time in `ns`.
225    #[must_use]
226    pub fn time_now_ns(&self) -> f64 {
227        let now = *self.shared_state.now.borrow();
228        self.to_ns(&now)
229    }
230
231    /// Returns the time in `ns` of the next event registered with this clock.
232    #[must_use]
233    pub fn time_of_next(&self) -> f64 {
234        match self.shared_state.waiting_times.borrow().first() {
235            Some(clock_time) => self.to_ns(clock_time),
236            None => f64::MAX,
237        }
238    }
239
240    /// Convert the given [ClockTick] to a time in `ns` for this clock.
241    #[must_use]
242    pub fn to_ns(&self, clock_time: &ClockTick) -> f64 {
243        clock_time.tick as f64 / self.freq_mhz * 1000.0
244    }
245
246    /// Returns a [ClockDelay] future which must be `await`ed to delay the
247    /// specified number of ticks.
248    #[must_use = "Futures do nothing unless you `.await` or otherwise use them"]
249    pub fn wait_ticks(&self, ticks: u64) -> ClockDelay {
250        let mut until = self.tick_now();
251        until.tick += ticks;
252        ClockDelay {
253            shared_state: self.shared_state.clone(),
254            until,
255            state: ClockDelayState::Pending,
256            can_exit: false,
257        }
258    }
259
260    /// Returns a [ClockDelay] future which must be `await`ed to delay the
261    /// specified number of ticks. However, if the remainder of the simulation
262    /// completes then this future is allowed to not complete. This allows the
263    /// user to create tasks that can run continuously as long as the rest of
264    /// the simulation continues to run.
265    #[must_use = "Futures do nothing unless you `.await` or otherwise use them"]
266    pub fn wait_ticks_or_exit(&self, ticks: u64) -> ClockDelay {
267        let mut until = self.tick_now();
268        until.tick += ticks;
269        ClockDelay {
270            shared_state: self.shared_state.clone(),
271            until,
272            state: ClockDelayState::Pending,
273            can_exit: true,
274        }
275    }
276
277    #[must_use = "Futures do nothing unless you `.await` or otherwise use them"]
278    #[cfg(feature = "phase")]
279    pub fn next_tick_and_phase(&self, phase: u32) -> ClockDelay {
280        let mut until = self.tick_now();
281        until.tick += 1;
282        until.phase = phase;
283        ClockDelay {
284            shared_state: self.shared_state.clone(),
285            until,
286            state: ClockDelayState::Pending,
287            can_exit: false,
288        }
289    }
290
291    #[must_use = "Futures do nothing unless you `.await` or otherwise use them"]
292    #[cfg(feature = "phase")]
293    pub fn wait_phase(&self, phase: u32) -> ClockDelay {
294        let mut until = self.tick_now();
295        assert!(phase > until.phase, "Time going backwards");
296        until.phase = phase;
297        ClockDelay {
298            shared_state: self.shared_state.clone(),
299            until,
300            state: ClockDelayState::Pending,
301            can_exit: false,
302        }
303    }
304
305    /// Advance to the next tick after the specified time.
306    pub fn advance_to(&self, time_ns: f64) {
307        let now_ns = self.time_now_ns();
308        assert!(now_ns < time_ns);
309        let diff_ns = time_ns - now_ns;
310        let ticks = (diff_ns * (self.freq_mhz / 1000.0)).ceil();
311
312        let mut until = self.tick_now();
313        until.tick += ticks as u64;
314
315        self.shared_state.advance_time(until);
316    }
317}
318
319/// The default clocks is simply to use a 1GHz clock so ticks are 1ns.
320impl Default for Clock {
321    fn default() -> Self {
322        Self::new(1000.0)
323    }
324}
325
326/// The comparison operators for Clocks - use the next pending Waker time.
327impl PartialEq for Clock {
328    fn eq(&self, other: &Self) -> bool {
329        self.time_of_next() == other.time_of_next()
330    }
331}
332impl Eq for Clock {}
333
334impl Ord for Clock {
335    fn cmp(&self, other: &Self) -> Ordering {
336        if self.time_of_next() < other.time_of_next() {
337            Ordering::Less
338        } else {
339            Ordering::Greater
340        }
341    }
342}
343
344impl PartialOrd for Clock {
345    fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
346        Some(self.cmp(other))
347    }
348}
349
350impl Resolver for Clock {
351    fn add_resolve(&self, resolve: Rc<dyn Resolve + 'static>) {
352        self.shared_state.to_resolve.borrow_mut().push(resolve);
353    }
354}
355
356/// Possible states of a ClockDelay.
357enum ClockDelayState {
358    Pending,
359    Running,
360}
361
362/// Future returned by the clock to manage advancing time using async functions.
363pub struct ClockDelay {
364    shared_state: Rc<ClockState>,
365    until: ClockTick,
366    state: ClockDelayState,
367    can_exit: bool,
368}
369
370impl Future for ClockDelay {
371    type Output = ();
372    fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
373        match self.state {
374            ClockDelayState::Pending => {
375                self.shared_state.schedule(self.until, cx, self.can_exit);
376                self.state = ClockDelayState::Running;
377                Poll::Pending
378            }
379            ClockDelayState::Running => {
380                self.shared_state.advance_time(self.until);
381                Poll::Ready(())
382            }
383        }
384    }
385}
386
387#[cfg(test)]
388mod tests {
389    use super::*;
390
391    #[test]
392    fn convert_to_ns() {
393        let clk_ghz = Clock::new(1000.0);
394        assert_eq!(1.0, clk_ghz.to_ns(&ClockTick::new().set_tick(1)));
395
396        let slow_clk = Clock::new(0.5);
397        assert_eq!(2000.0, slow_clk.to_ns(&ClockTick::new().set_tick(1)));
398    }
399}