Skip to main content

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, marker::PhantomData, num::NonZeroU32};
21
22use esp_sync::RawMutex;
23
24use super::{Error, Timer as _};
25use crate::{
26    asynch::AtomicWaker,
27    interrupt::{self, InterruptHandler},
28    peripherals::{Interrupt, SYSTIMER},
29    system::{Cpu, Peripheral as PeripheralEnable, PeripheralClockControl},
30    time::{Duration, Instant},
31};
32
33// System Timer is only clocked by XTAL divided by 2 or 2.5 (or RC_FAST_CLK which is not supported
34// yet). Some XTAL options (most, in fact) divided by this value may not be an integer multiple of
35// 1_000_000. Because the timer API works with microseconds, we need to correct for this. To avoid
36// u64 division as much as possible, we use the two highest bits of the divisor to determine the
37// division method.
38// - If these flags are 0b00, we divide by the divisor.
39// - If these flags are 0b01, we multiply by a constant before dividing by the divisor to improve
40//   accuracy.
41// - If these flags are 0b10, we shift the timestamp by the lower bits.
42//
43// Apart from the S2, the System Timer clock divider outputs a 16MHz timer clock when using
44// the "default" XTAL configuration, so this method will commonly use the shifting based
45// division.
46//
47// On a 26MHz C2, the divider outputs 10.4MHz. On a 32MHz C3, the divider outputs 12.8MHz.
48//
49// Time is unreliable before `init_timestamp_scaler` is called.
50//
51// Because a single crate version can have "rt" enabled, `ESP_HAL_SYSTIMER_CORRECTION` needs
52// to be shared between versions. This aspect of this driver must be therefore kept stable.
53#[unsafe(no_mangle)]
54#[cfg(feature = "rt")]
55static mut ESP_HAL_SYSTIMER_CORRECTION: NonZeroU32 = NonZeroU32::new(SHIFT_TIMESTAMP_FLAG).unwrap(); // Shift-by-0 = no-op
56
57#[cfg(not(feature = "rt"))]
58unsafe extern "Rust" {
59    static mut ESP_HAL_SYSTIMER_CORRECTION: NonZeroU32;
60}
61
62const SHIFT_TIMESTAMP_FLAG: u32 = 0x8000_0000;
63const SHIFT_MASK: u32 = 0x0000_FFFF;
64// If the tick rate is not an integer number of microseconds: Since the divider is 2.5,
65// and we assume XTAL is an integer number of MHz, we can multiply by 5, then divide by
66// 5 to improve accuracy. On H2 the divider is 2, so we can multiply by 2, then divide by
67// 2.
68const UNEVEN_DIVIDER_FLAG: u32 = 0x4000_0000;
69const UNEVEN_MULTIPLIER: u32 = if cfg!(esp32h2) { 2 } else { 5 };
70const UNEVEN_DIVIDER_MASK: u32 = 0x0000_FFFF;
71
72/// The configuration of a unit.
73#[derive(Copy, Clone)]
74pub enum UnitConfig {
75    /// Unit is not counting.
76    Disabled,
77
78    /// Unit is counting unless the Cpu is stalled.
79    DisabledIfCpuIsStalled(Cpu),
80
81    /// Unit is counting.
82    Enabled,
83}
84
85/// System Timer driver.
86pub struct SystemTimer<'d> {
87    /// Alarm 0.
88    pub alarm0: Alarm<'d>,
89
90    /// Alarm 1.
91    pub alarm1: Alarm<'d>,
92
93    /// Alarm 2.
94    pub alarm2: Alarm<'d>,
95}
96
97impl<'d> SystemTimer<'d> {
98    cfg_select! {
99        esp32s2 => {
100            /// Bitmask to be applied to the raw register value.
101            pub const BIT_MASK: u64 = u64::MAX;
102            // Bitmask to be applied to the raw period register value.
103            const PERIOD_MASK: u64 = 0x1FFF_FFFF;
104        }
105        _ => {
106            /// Bitmask to be applied to the raw register value.
107            pub const BIT_MASK: u64 = 0xF_FFFF_FFFF_FFFF;
108            // Bitmask to be applied to the raw period register value.
109            const PERIOD_MASK: u64 = 0x3FF_FFFF;
110        }
111    }
112
113    /// One-time initialization for the timestamp conversion/scaling.
114    #[cfg(feature = "rt")]
115    pub(crate) fn init_timestamp_scaler() {
116        // Maximum tick rate is 80MHz (S2), which fits in a u32, so let's narrow the type.
117        let systimer_rate = Self::ticks_per_second();
118
119        // Select the optimal way to divide timestamps.
120        let packed_rate_and_method = if systimer_rate.is_multiple_of(1_000_000) {
121            let ticks_per_us = systimer_rate as u32 / 1_000_000;
122            if ticks_per_us.is_power_of_two() {
123                // Turn the division into a shift
124                SHIFT_TIMESTAMP_FLAG | (ticks_per_us.ilog2() & SHIFT_MASK)
125            } else {
126                // We need to divide by an integer :(
127                ticks_per_us
128            }
129        } else {
130            // The rate is not a multiple of 1 MHz, we need to scale it up to prevent precision
131            // loss.
132            let multiplied_ticks_per_us = (systimer_rate * UNEVEN_MULTIPLIER as u64) / 1_000_000;
133            UNEVEN_DIVIDER_FLAG | (multiplied_ticks_per_us as u32)
134        };
135
136        // Safety: we only ever write ESP_HAL_SYSTIMER_CORRECTION in `init_timestamp_scaler`, which
137        // is called once and only once during startup, from `time_init`.
138        unsafe {
139            let correction_ptr = &raw mut ESP_HAL_SYSTIMER_CORRECTION;
140            *correction_ptr = unwrap!(NonZeroU32::new(packed_rate_and_method));
141        }
142    }
143
144    #[inline]
145    pub(crate) fn ticks_to_us(ticks: u64) -> u64 {
146        // Safety: we only ever write ESP_HAL_SYSTIMER_CORRECTION in `init_timestamp_scaler`, which
147        // is called once and only once during startup.
148        let correction = unsafe { ESP_HAL_SYSTIMER_CORRECTION };
149
150        let correction = correction.get();
151        match correction & (SHIFT_TIMESTAMP_FLAG | UNEVEN_DIVIDER_FLAG) {
152            v if v == SHIFT_TIMESTAMP_FLAG => ticks >> (correction & SHIFT_MASK),
153            v if v == UNEVEN_DIVIDER_FLAG => {
154                // Not only that, but we need to multiply the timestamp first otherwise
155                // we'd count slower than the timer.
156                let multiplied = if UNEVEN_MULTIPLIER.is_power_of_two() {
157                    ticks << UNEVEN_MULTIPLIER.ilog2()
158                } else {
159                    ticks * UNEVEN_MULTIPLIER as u64
160                };
161
162                let divider = correction & UNEVEN_DIVIDER_MASK;
163                multiplied / divider as u64
164            }
165            _ => ticks / correction as u64,
166        }
167    }
168
169    #[inline]
170    pub(crate) fn us_to_ticks(us: u64) -> u64 {
171        // Safety: we only ever write ESP_HAL_SYSTIMER_CORRECTION in `init_timestamp_scaler`, which
172        // is called once and only once during startup.
173        let correction = unsafe { ESP_HAL_SYSTIMER_CORRECTION };
174
175        let correction = correction.get();
176        match correction & (SHIFT_TIMESTAMP_FLAG | UNEVEN_DIVIDER_FLAG) {
177            v if v == SHIFT_TIMESTAMP_FLAG => us << (correction & SHIFT_MASK),
178            v if v == UNEVEN_DIVIDER_FLAG => {
179                let multiplier = correction & UNEVEN_DIVIDER_MASK;
180                let multiplied = us * multiplier as u64;
181
182                // Not only that, but we need to divide the timestamp first otherwise
183                // we'd return a slightly too-big value.
184                if UNEVEN_MULTIPLIER.is_power_of_two() {
185                    multiplied >> UNEVEN_MULTIPLIER.ilog2()
186                } else {
187                    multiplied / UNEVEN_MULTIPLIER as u64
188                }
189            }
190            _ => us * correction as u64,
191        }
192    }
193
194    /// Returns the tick frequency of the underlying timer unit.
195    #[inline]
196    pub fn ticks_per_second() -> u64 {
197        // FIXME: this requires a critical section. We can probably do better, if we can formulate
198        // invariants well.
199        cfg_select! {
200            esp32c5 => {
201                // Assuming SYSTIMER runs from XTAL, the hardware always runs at 16 MHz.
202                16_000_000
203            }
204            _ => {
205                cfg_select! {
206                    esp32s2 => crate::soc::clocks::apb_clk_frequency() as u64,
207                    esp32h2 => (crate::soc::clocks::xtal_clk_frequency() / 2) as u64,
208                    _ => (crate::soc::clocks::xtal_clk_frequency() * 10 / 25) as u64,
209                }
210            }
211        }
212    }
213
214    /// Create a new instance.
215    pub fn new(_systimer: SYSTIMER<'d>) -> Self {
216        // Don't reset Systimer as it will break `time::Instant::now`, only enable it
217        if PeripheralClockControl::enable(PeripheralEnable::Systimer) {
218            PeripheralClockControl::reset(PeripheralEnable::Systimer);
219        } else {
220            // Refcount was more than 0. Decrement to avoid overflow because we don't handle
221            // dropping the driver.
222            PeripheralClockControl::disable(PeripheralEnable::Systimer);
223        }
224
225        #[cfg(etm_driver_supported)]
226        etm::enable_etm();
227
228        Self {
229            alarm0: Alarm::new(Comparator::Comparator0),
230            alarm1: Alarm::new(Comparator::Comparator1),
231            alarm2: Alarm::new(Comparator::Comparator2),
232        }
233    }
234
235    /// Get the current count of the given unit in the System Timer.
236    #[inline]
237    pub fn unit_value(unit: Unit) -> u64 {
238        // This should be safe to access from multiple contexts
239        // worst case scenario the second accessor ends up reading
240        // an older time stamp
241
242        unit.read_count()
243    }
244
245    #[cfg(not(esp32s2))]
246    /// Configures when this counter can run.
247    /// It can be configured to stall or continue running when CPU stalls
248    /// or enters on-chip-debugging mode.
249    ///
250    /// # Safety
251    ///
252    /// - Disabling a `Unit` whilst [`Alarm`]s are using it will affect the [`Alarm`]s operation.
253    /// - Disabling Unit0 will affect [`Instant::now`].
254    pub unsafe fn configure_unit(unit: Unit, config: UnitConfig) {
255        unit.configure(config)
256    }
257
258    /// Set the value of the counter immediately. If the unit is at work,
259    /// the counter will continue to count up from the new reloaded value.
260    ///
261    /// This can be used to load back the sleep time recorded by RTC timer
262    /// via software after Light-sleep
263    ///
264    /// # Safety
265    ///
266    /// - Modifying a unit's count whilst [`Alarm`]s are using it may cause unexpected behaviour
267    /// - Any modification of the unit0 count will affect [`Instant::now`]
268    pub unsafe fn set_unit_value(unit: Unit, value: u64) {
269        unit.set_count(value)
270    }
271}
272
273/// A
274#[cfg_attr(esp32s2, doc = "64-bit")]
275#[cfg_attr(not(esp32s2), doc = "52-bit")]
276/// counter.
277#[derive(Copy, Clone, Debug, PartialEq, Eq)]
278#[cfg_attr(feature = "defmt", derive(defmt::Format))]
279pub enum Unit {
280    /// Unit 0
281    Unit0 = 0,
282    #[cfg(not(esp32s2))]
283    /// Unit 1
284    Unit1 = 1,
285}
286
287impl Unit {
288    #[inline]
289    fn channel(&self) -> u8 {
290        *self as _
291    }
292
293    #[cfg(not(esp32s2))]
294    fn configure(&self, config: UnitConfig) {
295        CONF_LOCK.lock(|| {
296            SYSTIMER::regs().conf().modify(|_, w| match config {
297                UnitConfig::Disabled => match self.channel() {
298                    0 => w.timer_unit0_work_en().clear_bit(),
299                    1 => w.timer_unit1_work_en().clear_bit(),
300                    _ => unreachable!(),
301                },
302                UnitConfig::DisabledIfCpuIsStalled(cpu) => match self.channel() {
303                    0 => {
304                        w.timer_unit0_work_en().set_bit();
305                        w.timer_unit0_core0_stall_en().bit(cpu == Cpu::ProCpu);
306                        w.timer_unit0_core1_stall_en().bit(cpu != Cpu::ProCpu)
307                    }
308                    1 => {
309                        w.timer_unit1_work_en().set_bit();
310                        w.timer_unit1_core0_stall_en().bit(cpu == Cpu::ProCpu);
311                        w.timer_unit1_core1_stall_en().bit(cpu != Cpu::ProCpu)
312                    }
313                    _ => unreachable!(),
314                },
315                UnitConfig::Enabled => match self.channel() {
316                    0 => {
317                        w.timer_unit0_work_en().set_bit();
318                        w.timer_unit0_core0_stall_en().clear_bit();
319                        w.timer_unit0_core1_stall_en().clear_bit()
320                    }
321                    1 => {
322                        w.timer_unit1_work_en().set_bit();
323                        w.timer_unit1_core0_stall_en().clear_bit();
324                        w.timer_unit1_core1_stall_en().clear_bit()
325                    }
326                    _ => unreachable!(),
327                },
328            });
329        });
330    }
331
332    fn set_count(&self, value: u64) {
333        let systimer = SYSTIMER::regs();
334
335        let value_lo = (value & 0xFFFF_FFFF) as _;
336        let value_hi = (value >> 32) as _;
337
338        cfg_select! {
339            esp32s2 => {
340                systimer.load_hi().write(|w| w.load_hi().set(value_hi));
341                systimer.load_lo().write(|w| w.load_lo().set(value_lo));
342
343                systimer.load().write(|w| w.load().set_bit());
344            }
345            _ => {
346                let unitload = systimer.unitload(self.channel() as _);
347                let unit_load = systimer.unit_load(self.channel() as _);
348
349                unitload.hi().write(|w| w.load_hi().set(value_hi));
350                unitload.lo().write(|w| w.load_lo().set(value_lo));
351
352                unit_load.write(|w| w.load().set_bit());
353            }
354        }
355    }
356
357    #[inline]
358    fn read_count(&self) -> u64 {
359        // This can be a shared reference as long as this type isn't Sync.
360
361        let channel = self.channel() as usize;
362        let systimer = SYSTIMER::regs();
363
364        systimer.unit_op(channel).write(|w| w.update().set_bit());
365        while !systimer.unit_op(channel).read().value_valid().bit_is_set() {}
366
367        // Read LO, HI, then LO again, check that LO returns the same value.
368        // This accounts for the case when an interrupt may happen between reading
369        // HI and LO values (or the other core updates the counter mid-read), and this
370        // function may get called from the ISR. In this case, the repeated read
371        // will return consistent values.
372        let unit_value = systimer.unit_value(channel);
373        let mut lo_prev = unit_value.lo().read().bits();
374        loop {
375            let lo = lo_prev;
376            let hi = unit_value.hi().read().bits();
377            lo_prev = unit_value.lo().read().bits();
378
379            if lo == lo_prev {
380                return ((hi as u64) << 32) | lo as u64;
381            }
382        }
383    }
384}
385
386#[derive(Debug, Clone, Copy, PartialEq, Eq)]
387#[cfg_attr(feature = "defmt", derive(defmt::Format))]
388enum Comparator {
389    Comparator0,
390    Comparator1,
391    Comparator2,
392}
393
394/// An alarm unit
395#[derive(Debug)]
396#[cfg_attr(feature = "defmt", derive(defmt::Format))]
397pub struct Alarm<'d> {
398    comp: Comparator,
399    unit: Unit,
400    _lifetime: PhantomData<&'d mut ()>,
401}
402
403impl Alarm<'_> {
404    const fn new(comp: Comparator) -> Self {
405        Alarm {
406            comp,
407            unit: Unit::Unit0,
408            _lifetime: PhantomData,
409        }
410    }
411
412    /// Unsafely clone this peripheral reference.
413    ///
414    /// # Safety
415    ///
416    /// You must ensure that you're only using one instance of this type at a
417    /// time.
418    pub unsafe fn clone_unchecked(&self) -> Self {
419        Self {
420            comp: self.comp,
421            unit: self.unit,
422            _lifetime: PhantomData,
423        }
424    }
425
426    /// Creates a new peripheral reference with a shorter lifetime.
427    ///
428    /// Use this method if you would like to keep working with the peripheral
429    /// after you dropped the driver that consumes this.
430    ///
431    /// See [Peripheral singleton] section for more information.
432    ///
433    /// [Peripheral singleton]: crate#peripheral-singletons
434    pub fn reborrow(&mut self) -> Alarm<'_> {
435        unsafe { self.clone_unchecked() }
436    }
437
438    /// Returns the comparator's number.
439    #[inline]
440    fn channel(&self) -> u8 {
441        self.comp as u8
442    }
443
444    /// Enables/disables the comparator. If enabled, this means
445    /// it will generate interrupt based on its configuration.
446    fn set_enable(&self, enable: bool) {
447        CONF_LOCK.lock(|| {
448            #[cfg(not(esp32s2))]
449            SYSTIMER::regs().conf().modify(|_, w| match self.channel() {
450                0 => w.target0_work_en().bit(enable),
451                1 => w.target1_work_en().bit(enable),
452                2 => w.target2_work_en().bit(enable),
453                _ => unreachable!(),
454            });
455        });
456
457        // Note: The ESP32-S2 doesn't require a lock because each
458        // comparator's enable bit in a different register.
459        #[cfg(esp32s2)]
460        SYSTIMER::regs()
461            .target_conf(self.channel() as usize)
462            .modify(|_r, w| w.work_en().bit(enable));
463    }
464
465    /// Returns true if the comparator has been enabled. This means
466    /// it will generate interrupt based on its configuration.
467    fn is_enabled(&self) -> bool {
468        #[cfg(not(esp32s2))]
469        match self.channel() {
470            0 => SYSTIMER::regs().conf().read().target0_work_en().bit(),
471            1 => SYSTIMER::regs().conf().read().target1_work_en().bit(),
472            2 => SYSTIMER::regs().conf().read().target2_work_en().bit(),
473            _ => unreachable!(),
474        }
475
476        #[cfg(esp32s2)]
477        SYSTIMER::regs()
478            .target_conf(self.channel() as usize)
479            .read()
480            .work_en()
481            .bit()
482    }
483
484    /// Sets the unit this comparator uses as a reference count.
485    #[cfg(not(esp32s2))]
486    pub fn set_unit(&self, unit: Unit) {
487        SYSTIMER::regs()
488            .target_conf(self.channel() as usize)
489            .modify(|_, w| w.timer_unit_sel().bit(matches!(unit, Unit::Unit1)));
490    }
491
492    /// Set the mode of the comparator to be either target or periodic.
493    fn set_mode(&self, mode: ComparatorMode) {
494        let is_period_mode = match mode {
495            ComparatorMode::Period => true,
496            ComparatorMode::Target => false,
497        };
498        SYSTIMER::regs()
499            .target_conf(self.channel() as usize)
500            .modify(|_, w| w.period_mode().bit(is_period_mode));
501    }
502
503    /// Get the current mode of the comparator, which is either target or
504    /// periodic.
505    fn mode(&self) -> ComparatorMode {
506        if SYSTIMER::regs()
507            .target_conf(self.channel() as usize)
508            .read()
509            .period_mode()
510            .bit()
511        {
512            ComparatorMode::Period
513        } else {
514            ComparatorMode::Target
515        }
516    }
517
518    /// Set how often the comparator should generate an interrupt when in
519    /// periodic mode.
520    fn set_period(&self, value: u32) {
521        let systimer = SYSTIMER::regs();
522        let tconf = systimer.target_conf(self.channel() as usize);
523        unsafe { tconf.modify(|_, w| w.period().bits(value)) };
524        #[cfg(not(esp32s2))]
525        {
526            let comp_load = systimer.comp_load(self.channel() as usize);
527            comp_load.write(|w| w.load().set_bit());
528        }
529    }
530
531    /// Set when the comparator should generate an interrupt in target mode.
532    fn set_target(&self, value: u64) {
533        let systimer = SYSTIMER::regs();
534        let target = systimer.trgt(self.channel() as usize);
535        target.hi().write(|w| w.hi().set((value >> 32) as u32));
536        target
537            .lo()
538            .write(|w| w.lo().set((value & 0xFFFF_FFFF) as u32));
539        #[cfg(not(esp32s2))]
540        {
541            let comp_load = systimer.comp_load(self.channel() as usize);
542            comp_load.write(|w| w.load().set_bit());
543        }
544    }
545
546    /// Set the interrupt handler for this comparator.
547    fn set_interrupt_handler(&self, handler: InterruptHandler) {
548        let interrupt = match self.channel() {
549            0 => Interrupt::SYSTIMER_TARGET0,
550            1 => Interrupt::SYSTIMER_TARGET1,
551            2 => Interrupt::SYSTIMER_TARGET2,
552            _ => unreachable!(),
553        };
554
555        for core in crate::system::Cpu::other() {
556            crate::interrupt::disable(core, interrupt);
557        }
558
559        #[cfg(not(esp32s2))]
560        interrupt::bind_handler(interrupt, handler);
561
562        #[cfg(esp32s2)]
563        {
564            // ESP32-S2 Systimer interrupts are edge triggered. Our interrupt
565            // handler calls each of the handlers, regardless of which one triggered the
566            // interrupt. This mess registers an intermediate handler that
567            // checks if an interrupt is active before calling the associated
568            // handler functions.
569
570            static mut HANDLERS: [Option<crate::interrupt::IsrCallback>; 3] = [None, None, None];
571
572            #[crate::ram]
573            extern "C" fn _handle_interrupt<const CH: u8>() {
574                if SYSTIMER::regs().int_raw().read().target(CH).bit_is_set() {
575                    let handler = unsafe { HANDLERS[CH as usize] };
576                    if let Some(handler) = handler {
577                        (handler.callback())();
578                    }
579                }
580            }
581
582            let priority = handler.priority();
583            unsafe {
584                HANDLERS[self.channel() as usize] = Some(handler.handler());
585            }
586            let handler = match self.channel() {
587                0 => _handle_interrupt::<0>,
588                1 => _handle_interrupt::<1>,
589                2 => _handle_interrupt::<2>,
590                _ => unreachable!(),
591            };
592            interrupt::bind_handler(
593                interrupt,
594                crate::interrupt::InterruptHandler::new(handler, priority),
595            );
596        }
597    }
598}
599
600/// The modes of a comparator.
601#[derive(Copy, Clone)]
602enum ComparatorMode {
603    /// The comparator will generate interrupts periodically.
604    Period,
605
606    /// The comparator will generate an interrupt when the unit reaches the
607    /// target.
608    Target,
609}
610
611impl super::Timer for Alarm<'_> {
612    fn start(&self) {
613        self.set_enable(true);
614    }
615
616    fn stop(&self) {
617        self.set_enable(false);
618    }
619
620    fn reset(&self) {
621        #[cfg(esp32s2)]
622        // Run at XTAL freq, not 80 * XTAL freq:
623        SYSTIMER::regs()
624            .step()
625            .modify(|_, w| unsafe { w.xtal_step().bits(0x1) });
626
627        #[cfg(not(esp32s2))]
628        SYSTIMER::regs()
629            .conf()
630            .modify(|_, w| w.timer_unit0_core0_stall_en().clear_bit());
631    }
632
633    fn is_running(&self) -> bool {
634        self.is_enabled()
635    }
636
637    fn now(&self) -> Instant {
638        // This should be safe to access from multiple contexts; worst case
639        // scenario the second accessor ends up reading an older time stamp.
640
641        let ticks = self.unit.read_count();
642
643        let us = SystemTimer::ticks_to_us(ticks);
644
645        Instant::from_ticks(us)
646    }
647
648    fn load_value(&self, value: Duration) -> Result<(), Error> {
649        let mode = self.mode();
650
651        let us = value.as_micros();
652        let ticks = SystemTimer::us_to_ticks(us);
653
654        if matches!(mode, ComparatorMode::Period) {
655            // Period mode
656
657            // The `SYSTIMER_TARGETx_PERIOD` field is 26-bits wide (or
658            // 29-bits on the ESP32-S2), so we must ensure that the provided
659            // value is not too wide:
660            if (ticks & !SystemTimer::PERIOD_MASK) != 0 {
661                return Err(Error::InvalidTimeout);
662            }
663
664            self.set_period(ticks as u32);
665
666            // Clear and then set SYSTIMER_TARGETx_PERIOD_MODE to configure COMPx into
667            // period mode
668            self.set_mode(ComparatorMode::Target);
669            self.set_mode(ComparatorMode::Period);
670        } else {
671            // Target mode
672
673            // The counters/comparators are 52-bits wide (except on ESP32-S2,
674            // which is 64-bits), so we must ensure that the provided value
675            // is not too wide:
676            #[cfg(not(esp32s2))]
677            if (ticks & !SystemTimer::BIT_MASK) != 0 {
678                return Err(Error::InvalidTimeout);
679            }
680
681            let v = self.unit.read_count();
682            let t = v + ticks;
683
684            self.set_target(t);
685        }
686
687        Ok(())
688    }
689
690    fn enable_auto_reload(&self, auto_reload: bool) {
691        // If `auto_reload` is true use Period Mode, otherwise use Target Mode:
692        let mode = if auto_reload {
693            ComparatorMode::Period
694        } else {
695            ComparatorMode::Target
696        };
697        self.set_mode(mode)
698    }
699
700    fn enable_interrupt(&self, state: bool) {
701        INT_ENA_LOCK.lock(|| {
702            SYSTIMER::regs()
703                .int_ena()
704                .modify(|_, w| w.target(self.channel()).bit(state));
705        });
706    }
707
708    fn clear_interrupt(&self) {
709        SYSTIMER::regs()
710            .int_clr()
711            .write(|w| w.target(self.channel()).clear_bit_by_one());
712    }
713
714    fn is_interrupt_set(&self) -> bool {
715        SYSTIMER::regs()
716            .int_raw()
717            .read()
718            .target(self.channel())
719            .bit_is_set()
720    }
721
722    fn async_interrupt_handler(&self) -> InterruptHandler {
723        match self.channel() {
724            0 => asynch::target0_handler,
725            1 => asynch::target1_handler,
726            2 => asynch::target2_handler,
727            _ => unreachable!(),
728        }
729    }
730
731    fn peripheral_interrupt(&self) -> Interrupt {
732        match self.channel() {
733            0 => Interrupt::SYSTIMER_TARGET0,
734            1 => Interrupt::SYSTIMER_TARGET1,
735            2 => Interrupt::SYSTIMER_TARGET2,
736            _ => unreachable!(),
737        }
738    }
739
740    fn set_interrupt_handler(&self, handler: InterruptHandler) {
741        self.set_interrupt_handler(handler)
742    }
743
744    fn waker(&self) -> &AtomicWaker {
745        asynch::waker(self)
746    }
747}
748
749impl crate::private::Sealed for Alarm<'_> {}
750
751static CONF_LOCK: RawMutex = RawMutex::new();
752static INT_ENA_LOCK: RawMutex = RawMutex::new();
753
754// Async functionality of the system timer.
755mod asynch {
756    use core::marker::PhantomData;
757
758    use procmacros::handler;
759
760    use super::*;
761    use crate::asynch::AtomicWaker;
762
763    const NUM_ALARMS: usize = 3;
764    static WAKERS: [AtomicWaker; NUM_ALARMS] = [const { AtomicWaker::new() }; NUM_ALARMS];
765
766    pub(super) fn waker(alarm: &Alarm<'_>) -> &'static AtomicWaker {
767        &WAKERS[alarm.channel() as usize]
768    }
769
770    #[inline]
771    fn handle_alarm(comp: Comparator) {
772        Alarm {
773            comp,
774            unit: Unit::Unit0,
775            _lifetime: PhantomData,
776        }
777        .enable_interrupt(false);
778
779        WAKERS[comp as usize].wake();
780    }
781
782    #[handler]
783    pub(crate) fn target0_handler() {
784        handle_alarm(Comparator::Comparator0);
785    }
786
787    #[handler]
788    pub(crate) fn target1_handler() {
789        handle_alarm(Comparator::Comparator1);
790    }
791
792    #[handler]
793    pub(crate) fn target2_handler() {
794        handle_alarm(Comparator::Comparator2);
795    }
796}
797
798#[cfg(etm_driver_supported)]
799pub mod etm {
800    #![cfg_attr(docsrs, procmacros::doc_replace)]
801    //! # Event Task Matrix Function
802    //!
803    //! ## Overview
804    //!
805    //! The system timer supports the Event Task Matrix (ETM) function, which
806    //! allows the system timer’s ETM events to trigger any peripherals’ ETM
807    //! tasks.
808    //!
809    //! The system timer can generate the following ETM events:
810    //! - SYSTIMER_EVT_CNT_CMPx: Indicates the alarm pulses generated by COMPx
811    //! ## Example
812    //! ```rust, no_run
813    //! # {before_snippet}
814    //! # use esp_hal::timer::systimer::{etm::Event, SystemTimer};
815    //! # use esp_hal::timer::PeriodicTimer;
816    //! # use esp_hal::etm::Etm;
817    //! # use esp_hal::gpio::{
818    //! #     etm::{Channels, OutputConfig},
819    //! #     Level,
820    //! #     Pull,
821    //! # };
822    //! let syst = SystemTimer::new(peripherals.SYSTIMER);
823    //! let etm = Etm::new(peripherals.ETM);
824    //! let gpio_ext = Channels::new(peripherals.GPIO_SD);
825    //! let alarm0 = syst.alarm0;
826    //! let mut led = peripherals.GPIO1;
827    //!
828    //! let timer_event = Event::new(&alarm0);
829    //! let led_task = gpio_ext.channel0_task.toggle(
830    //!     led,
831    //!     OutputConfig {
832    //!         open_drain: false,
833    //!         pull: Pull::None,
834    //!         initial_state: Level::High,
835    //!     },
836    //! );
837    //!
838    //! let _configured_etm_channel = etm.channel0.setup(&timer_event, &led_task);
839    //!
840    //! let timer = PeriodicTimer::new(alarm0);
841    //! // configure the timer as usual
842    //! // when it fires it will toggle the GPIO
843    //! # {after_snippet}
844    //! ```
845
846    use super::*;
847
848    /// An ETM controlled SYSTIMER event
849    pub struct Event {
850        id: u8,
851    }
852
853    impl Event {
854        /// Creates an ETM event from the given [Alarm]
855        pub fn new(alarm: &Alarm<'_>) -> Self {
856            Self {
857                id: 50 + alarm.channel(),
858            }
859        }
860    }
861
862    impl crate::private::Sealed for Event {}
863
864    impl crate::etm::EtmEvent for Event {
865        fn id(&self) -> u8 {
866            self.id
867        }
868    }
869
870    pub(super) fn enable_etm() {
871        SYSTIMER::regs().conf().modify(|_, w| w.etm_en().set_bit());
872    }
873}