esp_hal/timer/
systimer.rs

1//! # System Timer (SYSTIMER)
2//!
3//! ## Overview
4//! The System Timer is a
5#![cfg_attr(esp32s2, doc = "64-bit")]
6#![cfg_attr(not(esp32s2), doc = "52-bit")]
7//! timer which can be used, for example, to generate tick interrupts for an
8//! operating system, or simply as a general-purpose timer.
9//!
10//! ## Configuration
11//!
12//! The timer consists of two counters, `Unit0` and `Unit1`. The counter values
13//! can be monitored by 3 [`Alarm`]s
14//!
15//! It is recommended to pass the [`Alarm`]s into a high level driver like
16//! [`OneShotTimer`](super::OneShotTimer) and
17//! [`PeriodicTimer`](super::PeriodicTimer). Using the System timer directly is
18//! only possible through the low level [`Timer`](crate::timer::Timer) trait.
19
20use core::fmt::Debug;
21
22use super::{Error, Timer as _};
23use crate::{
24    asynch::AtomicWaker,
25    interrupt::{self, InterruptHandler},
26    peripheral::Peripheral,
27    peripherals::{Interrupt, SYSTIMER},
28    sync::{lock, RawMutex},
29    system::{Cpu, Peripheral as PeripheralEnable, PeripheralClockControl},
30    time::{Duration, Instant},
31};
32
33/// The configuration of a unit.
34#[derive(Copy, Clone)]
35pub enum UnitConfig {
36    /// Unit is not counting.
37    Disabled,
38
39    /// Unit is counting unless the Cpu is stalled.
40    DisabledIfCpuIsStalled(Cpu),
41
42    /// Unit is counting.
43    Enabled,
44}
45
46/// System Timer driver.
47pub struct SystemTimer {
48    /// Alarm 0.
49    pub alarm0: Alarm,
50
51    /// Alarm 1.
52    pub alarm1: Alarm,
53
54    /// Alarm 2.
55    pub alarm2: Alarm,
56}
57
58impl SystemTimer {
59    cfg_if::cfg_if! {
60        if #[cfg(esp32s2)] {
61            /// Bitmask to be applied to the raw register value.
62            pub const BIT_MASK: u64 = u64::MAX;
63            // Bitmask to be applied to the raw period register value.
64            const PERIOD_MASK: u64 = 0x1FFF_FFFF;
65        } else {
66            /// Bitmask to be applied to the raw register value.
67            pub const BIT_MASK: u64 = 0xF_FFFF_FFFF_FFFF;
68            // Bitmask to be applied to the raw period register value.
69            const PERIOD_MASK: u64 = 0x3FF_FFFF;
70        }
71    }
72
73    /// Returns the tick frequency of the underlying timer unit.
74    #[inline]
75    pub fn ticks_per_second() -> u64 {
76        cfg_if::cfg_if! {
77            if #[cfg(esp32s2)] {
78                const MULTIPLIER: u32 = 2;
79                const DIVIDER: u32 = 1;
80            } else if #[cfg(esp32h2)] {
81                // The counters and comparators are driven using `XTAL_CLK`.
82                // The average clock frequency is fXTAL_CLK/2, which is 16 MHz.
83                // The timer counting is incremented by 1/16 μs on each `CNT_CLK` cycle.
84                const MULTIPLIER: u32 = 1;
85                const DIVIDER: u32 = 2;
86            } else {
87                // The counters and comparators are driven using `XTAL_CLK`.
88                // The average clock frequency is fXTAL_CLK/2.5, which is 16 MHz.
89                // The timer counting is incremented by 1/16 μs on each `CNT_CLK` cycle.
90                const MULTIPLIER: u32 = 4;
91                const DIVIDER: u32 = 10;
92            }
93        }
94        let xtal_freq_mhz = crate::clock::Clocks::xtal_freq().as_hz();
95        ((xtal_freq_mhz * MULTIPLIER) / DIVIDER) as u64
96    }
97
98    /// Create a new instance.
99    pub fn new(_systimer: SYSTIMER) -> Self {
100        // Don't reset Systimer as it will break `time::Instant::now`, only enable it
101        PeripheralClockControl::enable(PeripheralEnable::Systimer);
102
103        #[cfg(soc_etm)]
104        etm::enable_etm();
105
106        Self {
107            alarm0: Alarm::new(0),
108            alarm1: Alarm::new(1),
109            alarm2: Alarm::new(2),
110        }
111    }
112
113    /// Get the current count of the given unit in the System Timer.
114    pub fn unit_value(unit: Unit) -> u64 {
115        // This should be safe to access from multiple contexts
116        // worst case scenario the second accessor ends up reading
117        // an older time stamp
118
119        unit.read_count()
120    }
121
122    #[cfg(not(esp32s2))]
123    /// Configures when this counter can run.
124    /// It can be configured to stall or continue running when CPU stalls
125    /// or enters on-chip-debugging mode.
126    ///
127    /// # Safety
128    ///
129    /// - Disabling a `Unit` whilst [`Alarm`]s are using it will affect the
130    ///   [`Alarm`]s operation.
131    /// - Disabling Unit0 will affect [`Instant::now`].
132    pub unsafe fn configure_unit(unit: Unit, config: UnitConfig) {
133        unit.configure(config)
134    }
135
136    /// Set the value of the counter immediately. If the unit is at work,
137    /// the counter will continue to count up from the new reloaded value.
138    ///
139    /// This can be used to load back the sleep time recorded by RTC timer
140    /// via software after Light-sleep
141    ///
142    /// # Safety
143    ///
144    /// - Modifying a unit's count whilst [`Alarm`]s are using it may cause
145    ///   unexpected behaviour
146    /// - Any modification of the unit0 count will affect [`Instant::now`]
147    pub unsafe fn set_unit_value(unit: Unit, value: u64) {
148        unit.set_count(value)
149    }
150}
151
152/// A
153#[cfg_attr(esp32s2, doc = "64-bit")]
154#[cfg_attr(not(esp32s2), doc = "52-bit")]
155/// counter.
156#[derive(Copy, Clone, Debug, PartialEq, Eq)]
157#[cfg_attr(feature = "defmt", derive(defmt::Format))]
158pub enum Unit {
159    /// Unit 0
160    Unit0 = 0,
161    #[cfg(not(esp32s2))]
162    /// Unit 1
163    Unit1 = 1,
164}
165
166impl Unit {
167    #[inline]
168    fn channel(&self) -> u8 {
169        *self as _
170    }
171
172    #[cfg(not(esp32s2))]
173    fn configure(&self, config: UnitConfig) {
174        lock(&CONF_LOCK, || {
175            SYSTIMER::regs().conf().modify(|_, w| match config {
176                UnitConfig::Disabled => match self.channel() {
177                    0 => w.timer_unit0_work_en().clear_bit(),
178                    1 => w.timer_unit1_work_en().clear_bit(),
179                    _ => unreachable!(),
180                },
181                UnitConfig::DisabledIfCpuIsStalled(cpu) => match self.channel() {
182                    0 => {
183                        w.timer_unit0_work_en().set_bit();
184                        w.timer_unit0_core0_stall_en().bit(cpu == Cpu::ProCpu);
185                        w.timer_unit0_core1_stall_en().bit(cpu != Cpu::ProCpu)
186                    }
187                    1 => {
188                        w.timer_unit1_work_en().set_bit();
189                        w.timer_unit1_core0_stall_en().bit(cpu == Cpu::ProCpu);
190                        w.timer_unit1_core1_stall_en().bit(cpu != Cpu::ProCpu)
191                    }
192                    _ => unreachable!(),
193                },
194                UnitConfig::Enabled => match self.channel() {
195                    0 => {
196                        w.timer_unit0_work_en().set_bit();
197                        w.timer_unit0_core0_stall_en().clear_bit();
198                        w.timer_unit0_core1_stall_en().clear_bit()
199                    }
200                    1 => {
201                        w.timer_unit1_work_en().set_bit();
202                        w.timer_unit1_core0_stall_en().clear_bit();
203                        w.timer_unit1_core1_stall_en().clear_bit()
204                    }
205                    _ => unreachable!(),
206                },
207            });
208        });
209    }
210
211    fn set_count(&self, value: u64) {
212        let systimer = SYSTIMER::regs();
213        #[cfg(not(esp32s2))]
214        {
215            let unitload = systimer.unitload(self.channel() as _);
216            let unit_load = systimer.unit_load(self.channel() as _);
217
218            unitload.hi().write(|w| w.load_hi().set((value << 32) as _));
219            unitload
220                .lo()
221                .write(|w| w.load_lo().set((value & 0xFFFF_FFFF) as _));
222
223            unit_load.write(|w| w.load().set_bit());
224        }
225        #[cfg(esp32s2)]
226        {
227            systimer
228                .load_hi()
229                .write(|w| w.load_hi().set((value << 32) as _));
230            systimer
231                .load_lo()
232                .write(|w| w.load_lo().set((value & 0xFFFF_FFFF) as _));
233
234            systimer.load().write(|w| w.load().set_bit());
235        }
236    }
237
238    fn read_count(&self) -> u64 {
239        // This can be a shared reference as long as this type isn't Sync.
240
241        let channel = self.channel() as usize;
242        let systimer = SYSTIMER::regs();
243
244        systimer.unit_op(channel).write(|w| w.update().set_bit());
245        while !systimer.unit_op(channel).read().value_valid().bit_is_set() {}
246
247        // Read LO, HI, then LO again, check that LO returns the same value.
248        // This accounts for the case when an interrupt may happen between reading
249        // HI and LO values (or the other core updates the counter mid-read), and this
250        // function may get called from the ISR. In this case, the repeated read
251        // will return consistent values.
252        let unit_value = systimer.unit_value(channel);
253        let mut lo_prev = unit_value.lo().read().bits();
254        loop {
255            let lo = lo_prev;
256            let hi = unit_value.hi().read().bits();
257            lo_prev = unit_value.lo().read().bits();
258
259            if lo == lo_prev {
260                return ((hi as u64) << 32) | lo as u64;
261            }
262        }
263    }
264}
265
266/// An alarm unit
267#[derive(Debug)]
268#[cfg_attr(feature = "defmt", derive(defmt::Format))]
269pub struct Alarm {
270    comp: u8,
271    unit: Unit,
272}
273
274impl Alarm {
275    const fn new(comp: u8) -> Self {
276        Alarm {
277            comp,
278            unit: Unit::Unit0,
279        }
280    }
281
282    /// Returns the comparator's number.
283    #[inline]
284    fn channel(&self) -> u8 {
285        self.comp
286    }
287
288    /// Enables/disables the comparator. If enabled, this means
289    /// it will generate interrupt based on its configuration.
290    fn set_enable(&self, enable: bool) {
291        lock(&CONF_LOCK, || {
292            #[cfg(not(esp32s2))]
293            SYSTIMER::regs().conf().modify(|_, w| match self.channel() {
294                0 => w.target0_work_en().bit(enable),
295                1 => w.target1_work_en().bit(enable),
296                2 => w.target2_work_en().bit(enable),
297                _ => unreachable!(),
298            });
299        });
300
301        // Note: The ESP32-S2 doesn't require a lock because each
302        // comparator's enable bit in a different register.
303        #[cfg(esp32s2)]
304        SYSTIMER::regs()
305            .target_conf(self.channel() as usize)
306            .modify(|_r, w| w.work_en().bit(enable));
307    }
308
309    /// Returns true if the comparator has been enabled. This means
310    /// it will generate interrupt based on its configuration.
311    fn is_enabled(&self) -> bool {
312        #[cfg(not(esp32s2))]
313        match self.channel() {
314            0 => SYSTIMER::regs().conf().read().target0_work_en().bit(),
315            1 => SYSTIMER::regs().conf().read().target1_work_en().bit(),
316            2 => SYSTIMER::regs().conf().read().target2_work_en().bit(),
317            _ => unreachable!(),
318        }
319
320        #[cfg(esp32s2)]
321        SYSTIMER::regs()
322            .target_conf(self.channel() as usize)
323            .read()
324            .work_en()
325            .bit()
326    }
327
328    /// Sets the unit this comparator uses as a reference count.
329    #[cfg(not(esp32s2))]
330    pub fn set_unit(&self, unit: Unit) {
331        SYSTIMER::regs()
332            .target_conf(self.channel() as usize)
333            .modify(|_, w| w.timer_unit_sel().bit(matches!(unit, Unit::Unit1)));
334    }
335
336    /// Set the mode of the comparator to be either target or periodic.
337    fn set_mode(&self, mode: ComparatorMode) {
338        let is_period_mode = match mode {
339            ComparatorMode::Period => true,
340            ComparatorMode::Target => false,
341        };
342        SYSTIMER::regs()
343            .target_conf(self.channel() as usize)
344            .modify(|_, w| w.period_mode().bit(is_period_mode));
345    }
346
347    /// Get the current mode of the comparator, which is either target or
348    /// periodic.
349    fn mode(&self) -> ComparatorMode {
350        if SYSTIMER::regs()
351            .target_conf(self.channel() as usize)
352            .read()
353            .period_mode()
354            .bit()
355        {
356            ComparatorMode::Period
357        } else {
358            ComparatorMode::Target
359        }
360    }
361
362    /// Set how often the comparator should generate an interrupt when in
363    /// periodic mode.
364    fn set_period(&self, value: u32) {
365        let systimer = SYSTIMER::regs();
366        let tconf = systimer.target_conf(self.channel() as usize);
367        unsafe { tconf.modify(|_, w| w.period().bits(value)) };
368        #[cfg(not(esp32s2))]
369        {
370            let comp_load = systimer.comp_load(self.channel() as usize);
371            comp_load.write(|w| w.load().set_bit());
372        }
373    }
374
375    /// Set when the comparator should generate an interrupt in target mode.
376    fn set_target(&self, value: u64) {
377        let systimer = SYSTIMER::regs();
378        let target = systimer.trgt(self.channel() as usize);
379        target.hi().write(|w| w.hi().set((value >> 32) as u32));
380        target
381            .lo()
382            .write(|w| w.lo().set((value & 0xFFFF_FFFF) as u32));
383        #[cfg(not(esp32s2))]
384        {
385            let comp_load = systimer.comp_load(self.channel() as usize);
386            comp_load.write(|w| w.load().set_bit());
387        }
388    }
389
390    /// Set the interrupt handler for this comparator.
391    fn set_interrupt_handler(&self, handler: InterruptHandler) {
392        let interrupt = match self.channel() {
393            0 => Interrupt::SYSTIMER_TARGET0,
394            1 => Interrupt::SYSTIMER_TARGET1,
395            2 => Interrupt::SYSTIMER_TARGET2,
396            _ => unreachable!(),
397        };
398
399        for core in crate::system::Cpu::other() {
400            crate::interrupt::disable(core, interrupt);
401        }
402
403        #[cfg(not(esp32s2))]
404        unsafe {
405            interrupt::bind_interrupt(interrupt, handler.handler());
406        }
407
408        #[cfg(esp32s2)]
409        {
410            // ESP32-S2 Systimer interrupts are edge triggered. Our interrupt
411            // handler calls each of the handlers, regardless of which one triggered the
412            // interrupt. This mess registers an intermediate handler that
413            // checks if an interrupt is active before calling the associated
414            // handler functions.
415
416            static mut HANDLERS: [Option<extern "C" fn()>; 3] = [None, None, None];
417
418            #[crate::ram]
419            unsafe extern "C" fn _handle_interrupt<const CH: u8>() {
420                if SYSTIMER::regs().int_raw().read().target(CH).bit_is_set() {
421                    let handler = unsafe { HANDLERS[CH as usize] };
422                    if let Some(handler) = handler {
423                        handler();
424                    }
425                }
426            }
427
428            unsafe {
429                HANDLERS[self.channel() as usize] = Some(handler.handler());
430                let handler = match self.channel() {
431                    0 => _handle_interrupt::<0>,
432                    1 => _handle_interrupt::<1>,
433                    2 => _handle_interrupt::<2>,
434                    _ => unreachable!(),
435                };
436                interrupt::bind_interrupt(interrupt, handler);
437            }
438        }
439        unwrap!(interrupt::enable(interrupt, handler.priority()));
440    }
441}
442
443/// The modes of a comparator.
444#[derive(Copy, Clone)]
445enum ComparatorMode {
446    /// The comparator will generate interrupts periodically.
447    Period,
448
449    /// The comparator will generate an interrupt when the unit reaches the
450    /// target.
451    Target,
452}
453
454impl super::Timer for Alarm {
455    fn start(&self) {
456        self.set_enable(true);
457    }
458
459    fn stop(&self) {
460        self.set_enable(false);
461    }
462
463    fn reset(&self) {
464        #[cfg(esp32s2)]
465        // Run at XTAL freq, not 80 * XTAL freq:
466        SYSTIMER::regs()
467            .step()
468            .modify(|_, w| unsafe { w.xtal_step().bits(0x1) });
469
470        #[cfg(not(esp32s2))]
471        SYSTIMER::regs()
472            .conf()
473            .modify(|_, w| w.timer_unit0_core0_stall_en().clear_bit());
474    }
475
476    fn is_running(&self) -> bool {
477        self.is_enabled()
478    }
479
480    fn now(&self) -> Instant {
481        // This should be safe to access from multiple contexts; worst case
482        // scenario the second accessor ends up reading an older time stamp.
483
484        let ticks = self.unit.read_count();
485
486        let us = ticks / (SystemTimer::ticks_per_second() / 1_000_000);
487
488        Instant::from_ticks(us)
489    }
490
491    fn load_value(&self, value: Duration) -> Result<(), Error> {
492        let mode = self.mode();
493
494        let us = value.as_micros();
495        let ticks = us * (SystemTimer::ticks_per_second() / 1_000_000);
496
497        if matches!(mode, ComparatorMode::Period) {
498            // Period mode
499
500            // The `SYSTIMER_TARGETx_PERIOD` field is 26-bits wide (or
501            // 29-bits on the ESP32-S2), so we must ensure that the provided
502            // value is not too wide:
503            if (ticks & !SystemTimer::PERIOD_MASK) != 0 {
504                return Err(Error::InvalidTimeout);
505            }
506
507            self.set_period(ticks as u32);
508
509            // Clear and then set SYSTIMER_TARGETx_PERIOD_MODE to configure COMPx into
510            // period mode
511            self.set_mode(ComparatorMode::Target);
512            self.set_mode(ComparatorMode::Period);
513        } else {
514            // Target mode
515
516            // The counters/comparators are 52-bits wide (except on ESP32-S2,
517            // which is 64-bits), so we must ensure that the provided value
518            // is not too wide:
519            #[cfg(not(esp32s2))]
520            if (ticks & !SystemTimer::BIT_MASK) != 0 {
521                return Err(Error::InvalidTimeout);
522            }
523
524            let v = self.unit.read_count();
525            let t = v + ticks;
526
527            self.set_target(t);
528        }
529
530        Ok(())
531    }
532
533    fn enable_auto_reload(&self, auto_reload: bool) {
534        // If `auto_reload` is true use Period Mode, otherwise use Target Mode:
535        let mode = if auto_reload {
536            ComparatorMode::Period
537        } else {
538            ComparatorMode::Target
539        };
540        self.set_mode(mode)
541    }
542
543    fn enable_interrupt(&self, state: bool) {
544        lock(&INT_ENA_LOCK, || {
545            SYSTIMER::regs()
546                .int_ena()
547                .modify(|_, w| w.target(self.channel()).bit(state));
548        });
549    }
550
551    fn clear_interrupt(&self) {
552        SYSTIMER::regs()
553            .int_clr()
554            .write(|w| w.target(self.channel()).clear_bit_by_one());
555    }
556
557    fn is_interrupt_set(&self) -> bool {
558        SYSTIMER::regs()
559            .int_raw()
560            .read()
561            .target(self.channel())
562            .bit_is_set()
563    }
564
565    fn async_interrupt_handler(&self) -> InterruptHandler {
566        match self.channel() {
567            0 => asynch::target0_handler,
568            1 => asynch::target1_handler,
569            2 => asynch::target2_handler,
570            _ => unreachable!(),
571        }
572    }
573
574    fn peripheral_interrupt(&self) -> Interrupt {
575        match self.channel() {
576            0 => Interrupt::SYSTIMER_TARGET0,
577            1 => Interrupt::SYSTIMER_TARGET1,
578            2 => Interrupt::SYSTIMER_TARGET2,
579            _ => unreachable!(),
580        }
581    }
582
583    fn set_interrupt_handler(&self, handler: InterruptHandler) {
584        self.set_interrupt_handler(handler)
585    }
586
587    fn waker(&self) -> &AtomicWaker {
588        asynch::waker(self)
589    }
590}
591
592impl Peripheral for Alarm {
593    type P = Self;
594
595    #[inline]
596    unsafe fn clone_unchecked(&self) -> Self::P {
597        Alarm {
598            comp: self.comp,
599            unit: self.unit,
600        }
601    }
602}
603
604impl crate::private::Sealed for Alarm {}
605
606static CONF_LOCK: RawMutex = RawMutex::new();
607static INT_ENA_LOCK: RawMutex = RawMutex::new();
608
609// Async functionality of the system timer.
610mod asynch {
611    use procmacros::handler;
612
613    use super::*;
614    use crate::asynch::AtomicWaker;
615
616    const NUM_ALARMS: usize = 3;
617    static WAKERS: [AtomicWaker; NUM_ALARMS] = [const { AtomicWaker::new() }; NUM_ALARMS];
618
619    pub(super) fn waker(alarm: &Alarm) -> &AtomicWaker {
620        &WAKERS[alarm.channel() as usize]
621    }
622
623    #[inline]
624    fn handle_alarm(alarm: u8) {
625        Alarm {
626            comp: alarm,
627            unit: Unit::Unit0,
628        }
629        .enable_interrupt(false);
630
631        WAKERS[alarm as usize].wake();
632    }
633
634    #[handler]
635    pub(crate) fn target0_handler() {
636        handle_alarm(0);
637    }
638
639    #[handler]
640    pub(crate) fn target1_handler() {
641        handle_alarm(1);
642    }
643
644    #[handler]
645    pub(crate) fn target2_handler() {
646        handle_alarm(2);
647    }
648}
649
650#[cfg(soc_etm)]
651pub mod etm {
652    //! # Event Task Matrix Function
653    //!
654    //! ## Overview
655    //!
656    //! The system timer supports the Event Task Matrix (ETM) function, which
657    //! allows the system timer’s ETM events to trigger any peripherals’ ETM
658    //! tasks.
659    //!
660    //!    The system timer can generate the following ETM events:
661    //!    - SYSTIMER_EVT_CNT_CMPx: Indicates the alarm pulses generated by
662    //!      COMPx
663    //! ## Example
664    //! ```rust, no_run
665    #![doc = crate::before_snippet!()]
666    //! # use esp_hal::timer::systimer::{etm::Event, SystemTimer};
667    //! # use esp_hal::timer::PeriodicTimer;
668    //! # use esp_hal::etm::Etm;
669    //! # use esp_hal::gpio::{
670    //! #     etm::{Channels, OutputConfig},
671    //! #     Level,
672    //! #     Pull,
673    //! # };
674    //! let syst = SystemTimer::new(peripherals.SYSTIMER);
675    //! let etm = Etm::new(peripherals.SOC_ETM);
676    //! let gpio_ext = Channels::new(peripherals.GPIO_SD);
677    //! let alarm0 = syst.alarm0;
678    //! let mut led = peripherals.GPIO1;
679    //!
680    //! let timer_event = Event::new(&alarm0);
681    //! let led_task = gpio_ext.channel0_task.toggle(
682    //!     &mut led,
683    //!     OutputConfig {
684    //!         open_drain: false,
685    //!         pull: Pull::None,
686    //!         initial_state: Level::High,
687    //!     },
688    //! );
689    //!
690    //! let _configured_etm_channel = etm.channel0.setup(&timer_event,
691    //! &led_task);
692    //!
693    //! let timer = PeriodicTimer::new(alarm0);
694    //! // configure the timer as usual
695    //! // when it fires it will toggle the GPIO
696    //! # Ok(())
697    //! # }
698    //! ```
699
700    use super::*;
701
702    /// An ETM controlled SYSTIMER event
703    pub struct Event {
704        id: u8,
705    }
706
707    impl Event {
708        /// Creates an ETM event from the given [Alarm]
709        pub fn new(alarm: &Alarm) -> Self {
710            Self {
711                id: 50 + alarm.channel(),
712            }
713        }
714    }
715
716    impl crate::private::Sealed for Event {}
717
718    impl crate::etm::EtmEvent for Event {
719        fn id(&self) -> u8 {
720            self.id
721        }
722    }
723
724    pub(super) fn enable_etm() {
725        SYSTIMER::regs().conf().modify(|_, w| w.etm_en().set_bit());
726    }
727}