imxrt_hal/common/
pit.rs

1//! Periodic interrupt timers.
2//!
3//! PITs are countdown timers that run on the PERCLK clock root.
4//! When the timer elapses, it automatically restarts from the load value.
5//! There are four PIT channels for independent time tracking. The four
6//! channels share an interrupt.
7//!
8//! You can chain channels together by using [`Chained`](Chained).
9//! This doubles the width of the timer.
10//!
11//! # Example
12//!
13//! Note that these examples do not demonstrate how to configure the PIT
14//! clock gates, or PERCLK. For more information, see [the CCM peripheral clock
15//! module](crate::ccm::perclk_clk).
16//!
17//! Acquire the four PIT timer channels:
18//!
19//! ```no_run
20//! use imxrt_hal::pit;
21//! use imxrt_ral::pit::PIT;
22//!
23//! let (mut pit0, mut pit1, _, _) = pit::new(unsafe { PIT::instance() });
24//! ```
25//!
26//! Use `pit0` to implement a blocking delay:
27//!
28//! ```no_run
29//! # use imxrt_hal::pit;
30//! # use imxrt_ral::pit::PIT;
31//! # let (mut pit0, pit1, _, _) = pit::new(unsafe { PIT::instance() });
32//! # const DELAY_MS: u32 = 1;
33//! pit0.set_load_timer_value(DELAY_MS);
34//! pit0.enable();
35//!
36//! loop {
37//!     while !pit0.is_elapsed() {}
38//!     pit0.clear_elapsed();
39//!     // Do work...
40//! }
41//! ```
42//!
43//! Chain the channels together to form a larger timer:
44//!
45//! ```no_run
46//! # use imxrt_hal::pit;
47//! # use imxrt_ral::pit::PIT;
48//! # let (pit0, pit1, _, _) = pit::new(unsafe { PIT::instance() });
49//!
50//! let chained = pit::Chained01::new(pit0, pit1);
51//! ```
52
53/// Channel 0.
54pub type Pit0 = Pit<0>;
55/// Channel 1.
56pub type Pit1 = Pit<1>;
57/// Channel 2.
58pub type Pit2 = Pit<2>;
59/// Channel 3.
60pub type Pit3 = Pit<3>;
61
62/// All four channels.
63pub type Channels = (Pit0, Pit1, Pit2, Pit3);
64
65/// A periodic interrupt timer (PIT) channel.
66pub struct Pit<const CHAN: u8> {
67    instance: &'static crate::ral::pit::RegisterBlock,
68}
69
70/// Convert the PIT peripheral instances into four timer channels.
71///
72/// `new` will reset all timer control registers before returning
73/// the channels. It is is guaranteed to not touch the `FRZ` bit
74/// in `MCR`.
75pub fn new<const N: u8>(pit: crate::ral::pit::Instance<N>) -> Channels {
76    crate::ral::modify_reg!(crate::ral::pit, pit, MCR, MDIS: MDIS_0);
77    // Reset all PIT channels
78    //
79    // PIT channels may be used by a systems boot ROM, or another
80    // user. Set them to a known, good state.
81    crate::ral::write_reg!(crate::ral::pit::timer, &pit.TIMER[0], TCTRL, 0);
82    crate::ral::write_reg!(crate::ral::pit::timer, &pit.TIMER[1], TCTRL, 0);
83    crate::ral::write_reg!(crate::ral::pit::timer, &pit.TIMER[2], TCTRL, 0);
84    crate::ral::write_reg!(crate::ral::pit::timer, &pit.TIMER[3], TCTRL, 0);
85
86    // Safety: we own the larger PIT peripheral instance. The caller
87    // (or the user who fabricated the larger PIT instance) ensures
88    // that we're not aliasing that peripheral.
89    unsafe {
90        (
91            Pit::new(&pit),
92            Pit::new(&pit),
93            Pit::new(&pit),
94            Pit::new(&pit),
95        )
96    }
97}
98
99mod private {
100    #[allow(dead_code)]
101    pub trait Sealed {}
102}
103/// Describes a valid PIT channel.
104pub trait Valid {}
105/// Type-level constant for PIT channels.
106///
107/// When combined with [`Valid`], this can constrain an API to accept
108/// only valid PIT channel constants. See [`Pit::new()`](crate::pit::Pit::new)
109/// for an example.
110pub enum Const<const N: u8> {}
111impl private::Sealed for Const<0> {}
112impl private::Sealed for Const<1> {}
113impl private::Sealed for Const<2> {}
114impl private::Sealed for Const<3> {}
115impl Valid for Const<0> {}
116impl Valid for Const<1> {}
117impl Valid for Const<2> {}
118impl Valid for Const<3> {}
119
120impl<const CHAN: u8> Pit<CHAN> {
121    /// Fabricate a PIT channel instance.
122    ///
123    /// # Safety
124    ///
125    /// This allows you to produce multiple PIT channels that mutably
126    /// reference the same memory. You must ensure that writes to the
127    /// peripheral memory are synchronized across instances.
128    ///
129    /// Use the free function [`new()`](crate::pit::new) to safely
130    /// acquire the four PIT channels.
131    pub unsafe fn new<const N: u8>(instance: &crate::ral::pit::Instance<N>) -> Self
132    where
133        Const<CHAN>: Valid,
134    {
135        let register_block: &'_ crate::ral::pit::RegisterBlock = instance;
136        // Safety: extending lifetime when we know that the register block has
137        // static lifetime.
138        let register_block: &'static _ = unsafe { core::mem::transmute(register_block) };
139        Self {
140            instance: register_block,
141        }
142    }
143
144    /// Enable (true) or disable (false) interrupt generation.
145    pub fn set_interrupt_enable(&mut self, enable: bool) {
146        crate::ral::modify_reg!(
147            crate::ral::pit::timer,
148            &self.instance.TIMER[CHAN as usize],
149            TCTRL,
150            TIE: enable as u32
151        )
152    }
153
154    /// Indicates if timeouts will (true) or will not (false) generate interrupts.
155    pub fn is_interrupt_enabled(&self) -> bool {
156        crate::ral::read_reg!(
157            crate::ral::pit::timer,
158            &self.instance.TIMER[CHAN as usize],
159            TCTRL,
160            TIE == 1
161        )
162    }
163
164    /// Reads the current time value, in clock ticks.
165    ///
166    /// Returns `0` if the timer is disabled.
167    pub fn current_timer_value(&self) -> u32 {
168        if self.is_enabled() {
169            // Note in CVAL register docs: don't read CVAL if the timer
170            // is disable "because the value is unreliable."
171            crate::ral::read_reg!(
172                crate::ral::pit::timer,
173                &self.instance.TIMER[CHAN as usize],
174                CVAL
175            )
176        } else {
177            0
178        }
179    }
180
181    /// Loads the timer value for the next timer run.
182    ///
183    /// `ticks` is in clock ticks.
184    pub fn set_load_timer_value(&self, ticks: u32) {
185        crate::ral::write_reg!(
186            crate::ral::pit::timer,
187            &self.instance.TIMER[CHAN as usize],
188            LDVAL,
189            ticks.saturating_sub(1)
190        );
191    }
192
193    /// Returns the load timer value for the next timer run, in clock ticks.
194    pub fn load_timer_value(&self) -> u32 {
195        crate::ral::read_reg!(
196            crate::ral::pit::timer,
197            &self.instance.TIMER[CHAN as usize],
198            LDVAL
199        )
200        .saturating_add(1)
201    }
202
203    /// Enable the timer.
204    pub fn enable(&mut self) {
205        crate::ral::modify_reg!(crate::ral::pit::timer, &self.instance.TIMER[CHAN as usize], TCTRL, TEN: 1);
206    }
207
208    /// Disable the timer.
209    pub fn disable(&mut self) {
210        crate::ral::modify_reg!(crate::ral::pit::timer, &self.instance.TIMER[CHAN as usize], TCTRL, TEN: 0);
211    }
212
213    /// Returns `true` if the PIT channel is enabled.
214    pub fn is_enabled(&self) -> bool {
215        crate::ral::read_reg!(
216            crate::ral::pit::timer,
217            &self.instance.TIMER[CHAN as usize],
218            TCTRL,
219            TEN == 1
220        )
221    }
222
223    /// Returns `true` if the timer has elapsed.
224    pub fn is_elapsed(&self) -> bool {
225        crate::ral::read_reg!(
226            crate::ral::pit::timer,
227            &self.instance.TIMER[CHAN as usize],
228            TFLG,
229            TIF == 1
230        )
231    }
232
233    /// Clear the elapsed flag.
234    pub fn clear_elapsed(&self) {
235        crate::ral::write_reg!(crate::ral::pit::timer, &self.instance.TIMER[CHAN as usize], TFLG, TIF: 1)
236    }
237
238    /// Specify that this channel is chained to the previous channel.
239    ///
240    /// This affects how the timer counts. If you're looking to chain timers
241    /// easily, see [`Chained`](crate::pit::Chained).
242    pub fn set_chained(&mut self, chained: bool) {
243        crate::ral::modify_reg!(
244            crate::ral::pit::timer,
245            &self.instance.TIMER[CHAN as usize],
246            TCTRL,
247            CHN: chained as u32
248        );
249    }
250
251    /// Returns true if this channel is chained to the previous channel.
252    pub fn is_chained(&self) -> bool {
253        crate::ral::read_reg!(
254            crate::ral::pit::timer,
255            &self.instance.TIMER[CHAN as usize],
256            TCTRL,
257            CHN == 1
258        )
259    }
260}
261
262// Safety: reference to static MMIO can be moved across execution contexts.
263// Study of this module reveals that the safe API splits the larger PIT
264// peripheral instance into separate, non-aliasing channels.
265unsafe impl<const CHAN: u8> Send for Pit<CHAN> {}
266
267/// Two chained PIT timer channels.
268///
269/// When the low timer counts down to zero, the high timer is decremented
270/// by one. This doubles the width of the timer.
271///
272/// Timers must be chained in sequence. For example, timer 2 (high) can be
273/// chained to timer 1 (low). But, timer 3 cannot be chained to timer 1.
274///
275/// Chaining channel 1 and 0 enables the lifetime register. The lifetime
276/// register allows us to read two 32-bit registers without rollover.
277/// Otherwise, the implementation handles rollovers in software with a small
278/// loop and comparison.
279pub struct Chained<const L: u8, const H: u8> {
280    low: Pit<L>,
281    high: Pit<H>,
282}
283
284/// Chained channels 0 and 1.
285pub type Chained01 = Chained<0, 1>;
286/// Chained channels 1 and 2.
287pub type Chained12 = Chained<1, 2>;
288/// Chained channels 2 and 3.
289pub type Chained23 = Chained<2, 3>;
290
291impl Chained<0, 1> {
292    /// Chain together channels 0 and 1.
293    ///
294    /// This creates the lifetime timer.
295    pub fn new(low: Pit<0>, high: Pit<1>) -> Self {
296        chain(low, high)
297    }
298
299    /// Read the lifetime register value.
300    ///
301    /// This is only supported when chaining channels 0 and
302    /// channel 1. Returns `0` if the timer is disabled. The
303    /// lifetime registers account for rollover possibility
304    /// in hardware.
305    ///
306    /// This method implements the recommended fix for
307    /// errata ERR050130.
308    pub fn lifetime_value(&self) -> u64 {
309        if !self.is_enabled() {
310            return 0;
311        }
312
313        // Safety: there can only be one (safe) instance of this chained timer.
314        // We effectively own these registers. These addresses are valid MMIO
315        // registers.
316        let mut high = self.low.instance.LTMR64H.read();
317        let mut low = self.low.instance.LTMR64L.read();
318
319        let ldval0 = self.low.load_timer_value();
320        if low == ldval0 {
321            high = self.low.instance.LTMR64H.read();
322            low = self.low.instance.LTMR64L.read();
323        }
324
325        (u64::from(high) << 32) + u64::from(low)
326    }
327}
328
329impl Chained<1, 2> {
330    /// Chain together channels 1 and 2.
331    pub fn new(low: Pit<1>, high: Pit<2>) -> Self {
332        chain(low, high)
333    }
334}
335
336impl Chained<2, 3> {
337    /// Chain together channels 2 and 3.
338    pub fn new(low: Pit<2>, high: Pit<3>) -> Self {
339        chain(low, high)
340    }
341}
342
343/// Chain the low and high timers together.
344///
345/// This has no type safety to ensure valid channel chaining.
346fn chain<const L: u8, const H: u8>(mut low: Pit<L>, mut high: Pit<H>) -> Chained<L, H> {
347    low.disable();
348    high.disable();
349
350    low.clear_elapsed();
351    high.clear_elapsed();
352
353    low.set_chained(false);
354    low.set_interrupt_enable(false);
355    high.set_chained(true);
356    Chained { low, high }
357}
358
359impl<const L: u8, const H: u8> Chained<L, H> {
360    /// Release the chained timers.
361    ///
362    /// When `release` returns, the timer chain is disabled, and the
363    /// timer channels are disabled.
364    pub fn release(mut self) -> (Pit<L>, Pit<H>) {
365        self.low.disable();
366        self.high.disable();
367
368        self.high.set_chained(false);
369        self.high.set_interrupt_enable(false);
370        (self.low, self.high)
371    }
372
373    /// Enable (true) or disable (false) interrupt generation when
374    /// the chained timer expires.
375    pub fn set_interrupt_enable(&mut self, enable: bool) {
376        self.high.set_interrupt_enable(enable);
377    }
378
379    /// Indicates if timeouts will (true) or will not (false) generate interrupts.
380    pub fn is_interrupt_enabled(&self) -> bool {
381        self.high.is_interrupt_enabled()
382    }
383
384    /// Loads the timer value for the next timer run.
385    ///
386    /// `ticks` is in clock ticks.
387    pub fn set_load_timer_value(&mut self, ticks: u64) {
388        self.low.set_load_timer_value((ticks & 0xFFFF_FFFF) as u32);
389        self.high.set_load_timer_value((ticks >> 32) as u32);
390    }
391
392    /// Returns the load timer value for the next timer run, in ticks.
393    pub fn load_timer_value(&self) -> u64 {
394        let low = self.low.load_timer_value();
395        let high = self.high.load_timer_value();
396        (u64::from(high) << 32) + u64::from(low)
397    }
398
399    /// Reads the current timer value, in clock ticks.
400    ///
401    /// Returns `0` if the timer is disabled.
402    pub fn current_timer_value(&self) -> u64 {
403        if !self.is_enabled() {
404            return 0;
405        }
406
407        loop {
408            let tmp = self.high.current_timer_value();
409            let low = self.low.current_timer_value();
410            let high = self.high.current_timer_value();
411            if high == tmp {
412                // No rollover.
413                return (u64::from(high) << 32) + u64::from(low);
414            }
415        }
416    }
417
418    /// Enable the chained timer.
419    pub fn enable(&mut self) {
420        self.high.enable();
421        self.low.enable();
422    }
423
424    /// Disable the chained timer.
425    pub fn disable(&mut self) {
426        self.low.disable();
427        self.high.disable();
428    }
429
430    /// Returns `true` if the chained timer is enabled.
431    pub fn is_enabled(&self) -> bool {
432        self.high.is_enabled()
433    }
434
435    /// Returns `true` if the chained timer has elapsed.
436    pub fn is_elapsed(&self) -> bool {
437        self.high.is_elapsed()
438    }
439
440    /// Clear the elapsed flag.
441    pub fn clear_elapsed(&mut self) {
442        self.high.clear_elapsed();
443        self.low.clear_elapsed(); // Is this necessary?
444    }
445}
446
447/// ```compile_fail
448/// use imxrt_ral as ral;
449/// use imxrt_hal as hal;
450/// use hal::pit::Pit;
451///
452/// let p: Pit<4> = unsafe { Pit::new(&ral::pit::PIT::instance()) };
453/// ```
454#[cfg(doctest)]
455struct InvalidChannel;