zynq7000_hal/
priv_tim.rs

1//! # CPU private timer module
2//!
3//! ## Examples
4//!
5//! - Private timer as delay provider in [blinky](https://egit.irs.uni-stuttgart.de/rust/zynq7000-rs/src/branch/main/zynq/examples/simple/src/bin/blinky.rs)
6use core::{marker::PhantomData, sync::atomic::AtomicBool};
7
8use zynq7000::priv_tim::InterruptStatus;
9
10use crate::{clocks::ArmClocks, time::Hertz};
11
12static CORE_0_TIM_TAKEN: AtomicBool = AtomicBool::new(false);
13static CORE_1_TIM_TAKEN: AtomicBool = AtomicBool::new(false);
14
15/// High-level CPU private timer driver.
16pub struct CpuPrivateTimer {
17    regs: zynq7000::priv_tim::MmioCpuPrivateTimer<'static>,
18    cpu_3x2x_clock: Hertz,
19    // Add this marker to explicitely opt-out of Send and Sync.
20    //
21    // This is a CPU private timer and thus should not be sent to other threads.
22    _not_send: PhantomData<*const ()>,
23}
24
25impl CpuPrivateTimer {
26    /// Take the CPU private timer for a given core.
27    ///
28    /// This function can only be called once for each given core.
29    pub fn take(clocks: &ArmClocks) -> Option<Self> {
30        let mpidr = cortex_ar::register::mpidr::Mpidr::read();
31        let core = mpidr.0 & 0xff;
32        if core != 0 && core != 1 {
33            return None;
34        }
35        if (core == 0 && CORE_0_TIM_TAKEN.swap(true, core::sync::atomic::Ordering::Relaxed))
36            || (core == 1 && CORE_1_TIM_TAKEN.swap(true, core::sync::atomic::Ordering::Relaxed))
37        {
38            return None;
39        }
40
41        Some(Self::steal(clocks))
42    }
43
44    /// Create a new CPU private timer driver.
45    ///
46    /// # Safety
47    ///
48    /// This function allows to potentially create an arbitrary amount of timers for both cores.
49    /// It also does not check the current core ID.
50    pub fn steal(clocks: &ArmClocks) -> Self {
51        Self {
52            regs: unsafe { zynq7000::priv_tim::CpuPrivateTimer::new_mmio_fixed() },
53            cpu_3x2x_clock: clocks.cpu_3x2x_clk(),
54            _not_send: PhantomData,
55        }
56    }
57
58    /// Write reload value which is set by the hardware when the timer reaches zero and
59    /// auto reload is enabled.
60    #[inline]
61    pub fn write_reload(&mut self, value: u32) {
62        self.regs.write_reload(value);
63    }
64
65    #[inline]
66    pub fn write_counter(&mut self, value: u32) {
67        self.regs.write_counter(value);
68    }
69
70    #[inline]
71    pub fn counter(&self) -> u32 {
72        self.regs.read_counter()
73    }
74}
75
76impl embedded_hal::delay::DelayNs for CpuPrivateTimer {
77    fn delay_ns(&mut self, ns: u32) {
78        // Even for a value of 1000 MHz for CPU 3x2x and u32::MAX for nanoseconds, this will
79        // never overflow.
80        let ticks = (ns as u64 * self.cpu_3x2x_clock.raw() as u64) / 1_000_000_000;
81
82        // Split the total delay into manageable chunks (u32::MAX ticks max).
83        let mut remaining = ticks;
84
85        self.regs.modify_control(|mut val| {
86            val.set_enable(false);
87            // The event flag is still set, which is all we care about.
88            val.set_interrupt_enable(false);
89            val.set_auto_reload(false);
90            val
91        });
92        while remaining > 0 {
93            let chunk = (remaining as u32).min(u32::MAX - 1);
94
95            // Clear the timer flag by writing 1 to it.
96            self.regs
97                .write_interrupt_status(InterruptStatus::builder().with_event_flag(true).build());
98
99            // Load the timer with the chunk value and start it.
100            self.write_reload(chunk);
101            self.write_counter(chunk);
102            self.regs.modify_control(|mut val| {
103                val.set_enable(true);
104                val
105            });
106
107            // Wait for the timer to count down to zero.
108            while !self.regs.read_interrupt_status().event_flag() {}
109
110            remaining -= chunk as u64;
111        }
112    }
113}