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