Skip to main content

zynq7000_embassy/
lib.rs

1#![no_std]
2use core::cell::{Cell, RefCell};
3
4use critical_section::{CriticalSection, Mutex};
5use embassy_time_driver::{Driver, TICK_HZ, time_driver_impl};
6use embassy_time_queue_utils::Queue;
7use once_cell::sync::OnceCell;
8
9use zynq7000_hal::{clocks::ArmClocks, gtc::GlobalTimerCounter, time::Hertz};
10
11static SCALE: OnceCell<u64> = OnceCell::new();
12static CPU_3X2X_CLK: OnceCell<Hertz> = OnceCell::new();
13
14struct AlarmState {
15    timestamp: Cell<u64>,
16}
17
18impl AlarmState {
19    const fn new() -> Self {
20        Self {
21            timestamp: Cell::new(u64::MAX),
22        }
23    }
24}
25
26unsafe impl Send for AlarmState {}
27
28/// This is the initialization method for the embassy time driver.
29///
30/// It should be called ONCE at system initialization.
31pub fn init(arm_clocks: &ArmClocks, gtc: GlobalTimerCounter) {
32    if SCALE.get().is_some() || CPU_3X2X_CLK.get().is_some() {
33        return;
34    }
35    unsafe { GTC_TIME_DRIVER.init(arm_clocks, gtc) };
36}
37
38/// This interrupt handler should be called ONCE in the interrupt handler on global timer
39/// interrupts.
40///
41/// # Safety
42///
43/// Needs to be called once in the global timer interrupt.
44pub unsafe fn on_interrupt() {
45    unsafe { GTC_TIME_DRIVER.on_interrupt() };
46}
47
48pub struct GtcTimerDriver {
49    gtc: Mutex<RefCell<GlobalTimerCounter>>,
50    // Timestamp at which to fire alarm. u64::MAX if no alarm is scheduled.
51    alarms: Mutex<AlarmState>,
52    queue: Mutex<RefCell<Queue>>,
53}
54
55impl GtcTimerDriver {
56    /// This is the initialization method for the embassy time driver.
57    ///
58    /// # Safety
59    ///
60    /// This has to be called ONCE at system initialization.
61    pub unsafe fn init(&'static self, arm_clock: &ArmClocks, mut gtc: GlobalTimerCounter) {
62        CPU_3X2X_CLK.set(arm_clock.cpu_3x2x_clk()).unwrap();
63        SCALE
64            .set(arm_clock.cpu_3x2x_clk().raw() as u64 / TICK_HZ)
65            .unwrap();
66        gtc.set_cpu_3x2x_clock(arm_clock.cpu_3x2x_clk());
67        gtc.set_prescaler(0);
68        gtc.enable();
69    }
70
71    /// Should be called inside the IRQ handler if the IRQ ID is equal to
72    /// [crate::hal::gic::PpiInterrupt::GlobalTimer].
73    ///
74    /// # Safety
75    ///
76    /// This function has to be called once for interrupt ID
77    /// [crate::hal::gic::PpiInterrupt::GlobalTimer].
78    pub unsafe fn on_interrupt(&self) {
79        critical_section::with(|cs| {
80            self.trigger_alarm(cs);
81        })
82    }
83
84    fn set_alarm(&self, cs: CriticalSection, timestamp: u64) -> bool {
85        if SCALE.get().is_none() {
86            return false;
87        }
88        let mut gtc = self.gtc.borrow(cs).borrow_mut();
89        let alarm = &self.alarms.borrow(cs);
90        alarm.timestamp.set(timestamp);
91
92        let t = self.now();
93        if timestamp <= t {
94            gtc.disable_interrupt();
95            alarm.timestamp.set(u64::MAX);
96            return false;
97        }
98
99        // If it hasn't triggered yet, setup the relevant reset value, regardless of whether
100        // the interrupts are enabled or not. When they are enabled at a later point, the
101        // right value is already set.
102
103        // If the timestamp is in the next few ticks, add a bit of buffer to be sure the alarm
104        // is not missed.
105        //
106        // This means that an alarm can be delayed for up to 2 ticks (from t+1 to t+3), but this is allowed
107        // by the Alarm trait contract. What's not allowed is triggering alarms *before* their scheduled time,
108        // and we don't do that here.
109        let safe_timestamp = timestamp.max(t + 3);
110        let opt_comparator = safe_timestamp.checked_mul(*SCALE.get().unwrap());
111        if opt_comparator.is_none() {
112            return true;
113        }
114        gtc.set_comparator(opt_comparator.unwrap());
115        gtc.enable_interrupt();
116        true
117    }
118
119    fn trigger_alarm(&self, cs: CriticalSection) {
120        let mut gtc = self.gtc.borrow(cs).borrow_mut();
121        gtc.disable_interrupt();
122        drop(gtc);
123
124        let alarm = &self.alarms.borrow(cs);
125        // Setting the maximum value disables the alarm.
126        alarm.timestamp.set(u64::MAX);
127
128        // Call after clearing alarm, so the callback can set another alarm.
129        let mut next = self
130            .queue
131            .borrow(cs)
132            .borrow_mut()
133            .next_expiration(self.now());
134        while !self.set_alarm(cs, next) {
135            next = self
136                .queue
137                .borrow(cs)
138                .borrow_mut()
139                .next_expiration(self.now());
140        }
141    }
142}
143impl Driver for GtcTimerDriver {
144    #[inline]
145    fn now(&self) -> u64 {
146        if SCALE.get().is_none() {
147            return 0;
148        }
149        // This is okay, we only read the GTC and do not re-configure it, avoids the
150        // need for a lock.
151        let gtc = unsafe { GlobalTimerCounter::steal_fixed(Some(*CPU_3X2X_CLK.get().unwrap())) };
152
153        gtc.read_timer() / SCALE.get().unwrap()
154    }
155
156    fn schedule_wake(&self, at: u64, waker: &core::task::Waker) {
157        critical_section::with(|cs| {
158            let mut queue = self.queue.borrow(cs).borrow_mut();
159
160            if queue.schedule_wake(at, waker) {
161                let mut next = queue.next_expiration(self.now());
162                while !self.set_alarm(cs, next) {
163                    next = queue.next_expiration(self.now());
164                }
165            }
166        })
167    }
168}
169
170time_driver_impl!(
171    // We assume ownership of the GTC, so it is okay to steal here.
172    static GTC_TIME_DRIVER: GtcTimerDriver = GtcTimerDriver {
173        gtc: Mutex::new(RefCell::new(unsafe { GlobalTimerCounter::steal_fixed(None)})),
174        alarms: Mutex::new(AlarmState::new()),
175        queue: Mutex::new(RefCell::new(Queue::new())),
176});