zynq7000_hal/
gtc.rs

1//! # Global timer counter driver module
2//!
3//! ## Examples
4//!
5//! - [GTC ticks example](https://egit.irs.uni-stuttgart.de/rust/zynq7000-rs/src/branch/main/zynq/examples/simple/src/bin/gtc-ticks.rs)
6//! - [Embassy Timer Driver](https://egit.irs.uni-stuttgart.de/rust/zynq7000-rs/src/branch/main/zynq/zynq7000-embassy/src/lib.rs)
7use zynq7000::gtc::MmioGlobalTimerCounter;
8
9use crate::{clocks::ArmClocks, time::Hertz};
10
11/// High level GTC driver.
12///
13/// This structure also allows an optional clock member, which is required for the
14/// [frequency_to_ticks] function and the [embedded_hal::delay::DelayNs] implementation
15/// to work.
16pub struct GlobalTimerCounter {
17    regs: MmioGlobalTimerCounter<'static>,
18    cpu_3x2x_clock: Option<Hertz>,
19}
20
21unsafe impl Send for GlobalTimerCounter {}
22
23pub const fn frequency_to_ticks(clock: Hertz, frequency: Hertz) -> u32 {
24    clock.raw().div_ceil(frequency.raw())
25}
26
27impl GlobalTimerCounter {
28    /// Create a peripheral driver from a MMIO GTC block.
29    #[inline]
30    pub const fn new(_regs: MmioGlobalTimerCounter<'static>, clocks: &ArmClocks) -> Self {
31        unsafe { Self::steal_fixed(Some(clocks.cpu_3x2x_clk())) }
32    }
33
34    /// Steal the GTC from the PAC.
35    ///
36    /// This function still expect the GTC clock, which is the CPU 3x2x clock frequency.
37    ///
38    /// # Safety
39    ///
40    /// This function allows creating an arbitrary amount of memory-mapped peripheral drivers.
41    /// See the [zynq7000::gtc::GlobalTimerCounter::new_mmio] docs for more safety information.
42    #[inline]
43    pub const unsafe fn steal_fixed(cpu_3x2x_clk: Option<Hertz>) -> Self {
44        Self {
45            regs: unsafe { zynq7000::gtc::GlobalTimerCounter::new_mmio_fixed() },
46            cpu_3x2x_clock: cpu_3x2x_clk,
47        }
48    }
49
50    #[inline]
51    pub fn set_cpu_3x2x_clock(&mut self, clock: Hertz) {
52        self.cpu_3x2x_clock = Some(clock);
53    }
54
55    // TODO: Change this API once pure-reads work.
56    /// Read the 64-bit timer.
57    #[inline]
58    pub fn read_timer(&self) -> u64 {
59        // Safety: We require interior mutability here because even reads are unsafe.
60        // But we want to avoid a RefCell which would incur a run-time cost solely to make this
61        // function non-mut, so we steal the GTC here. Ownership is guaranteed or mandated
62        // by constructor.
63        let upper = self.regs.read_count_upper();
64        loop {
65            let lower = self.regs.read_count_lower();
66            if self.regs.read_count_upper() == upper {
67                return ((upper as u64) << 32) | (lower as u64);
68            }
69            // Overflow, read upper again.
70        }
71    }
72
73    /// Set the comparator which can be used to trigger an interrupt in the future.
74    #[inline]
75    pub fn set_comparator(&mut self, comparator: u64) {
76        self.regs.modify_ctrl(|mut ctrl| {
77            ctrl.set_comparator_enable(false);
78            ctrl
79        });
80        self.regs.write_comparator_upper((comparator >> 32) as u32);
81        self.regs.write_comparator_lower(comparator as u32);
82        self.regs.modify_ctrl(|mut ctrl| {
83            ctrl.set_comparator_enable(true);
84            ctrl
85        });
86    }
87
88    pub fn frequency_to_ticks(&self, frequency: Hertz) -> u32 {
89        if self.cpu_3x2x_clock.is_none() {
90            return 0;
91        }
92        frequency_to_ticks(self.cpu_3x2x_clock.unwrap(), frequency)
93    }
94
95    /// Set the auto-increment value which will be used by the hardware to automatically
96    /// increment the comparator value on a comparator interrupt, if the auto-increment is enabled.
97    #[inline]
98    pub fn set_auto_increment_value(&mut self, value: u32) {
99        self.regs.write_auto_increment(value);
100    }
101
102    #[inline]
103    pub fn set_auto_increment_value_for_frequency(&mut self, frequency: Hertz) {
104        self.regs
105            .write_auto_increment(self.frequency_to_ticks(frequency));
106    }
107
108    #[inline]
109    pub fn enable(&mut self) {
110        self.regs.modify_ctrl(|mut ctrl| {
111            ctrl.set_enable(true);
112            ctrl
113        });
114    }
115
116    #[inline]
117    pub fn enable_auto_increment(&mut self) {
118        self.regs.modify_ctrl(|mut ctrl| {
119            ctrl.set_auto_increment(true);
120            ctrl
121        });
122    }
123
124    #[inline]
125    pub fn set_prescaler(&mut self, prescaler: u8) {
126        self.regs.modify_ctrl(|mut ctrl| {
127            ctrl.set_prescaler(prescaler);
128            ctrl
129        });
130    }
131
132    #[inline]
133    pub fn disable(&mut self) {
134        self.regs.modify_ctrl(|mut ctrl| {
135            ctrl.set_enable(false);
136            ctrl
137        });
138    }
139
140    /// Enable the comparator interrupt.
141    #[inline]
142    pub fn enable_interrupt(&mut self) {
143        self.regs.modify_ctrl(|mut ctrl| {
144            ctrl.set_irq_enable(true);
145            ctrl
146        });
147    }
148
149    /// Disable the comparator interrupt.
150    #[inline]
151    pub fn disable_interrupt(&mut self) {
152        self.regs.modify_ctrl(|mut ctrl| {
153            ctrl.set_irq_enable(false);
154            ctrl
155        });
156    }
157}
158
159/// GTC can also be used for blocking delays.
160impl embedded_hal::delay::DelayNs for GlobalTimerCounter {
161    fn delay_ns(&mut self, ns: u32) {
162        if self.cpu_3x2x_clock.is_none() {
163            return;
164        }
165        let end_of_delay = self.read_timer()
166            + (((ns as u64) * self.cpu_3x2x_clock.unwrap().raw() as u64) / 1_000_000_000);
167        while self.read_timer() < end_of_delay {}
168    }
169}