atsamd_hal/async_hal/
interrupts.rs

1//! # Async interrupts
2//!
3//! This module provides APIs specific to working with interrupts in an async
4//! peripheral context.
5//!
6//! Asynchronous programming relies on tasks that can be paused and resumed
7//! without blocking the entire program. When an async task is waiting for a
8//! particular event, such as data from a peripheral, it enters a suspended
9//! state. It is crucial that the task is properly woken up when the expected
10//! event occurs to resume its execution.
11//!
12//! By having peripherals take interrupts, they can signal the occurrence of
13//! relevant events, effectively waking up the associated async tasks. This
14//! ensures that the async runtime can schedule and resume tasks in a timely
15//! manner, providing the responsiveness required in embedded systems.
16//!
17//! ## Typelevel and enum-level interrupts
18//!
19//! There are two main ways of representing interrupts in the HAL: either by
20//! using [`pac::Interrupt`], where each interrupt is represented as an enum
21//! variant, or by using the typelevel interrupts defined in this module. Each
22//! interrupt source *that is usable with async peripherals* is declared as a
23//! struct with the same name of the corresponsing [`pac::Interrupt`] variant.
24//! Therefore, two distinct traits are needed to perform basic tasks on
25//! interrupt types:
26//!
27//! * Use [`Interrupt`] when dealing with the typelevel interrupt types defined
28//!   in this module;
29//! * Use [`InterruptExt`] when dealing with enum-level interrupt types defined
30//!   in [`pac`].
31//!
32//! [`pac::Interrupt`]: crate::pac::Interrupt
33//! [`Interrupt`]: crate::async_hal::interrupts::Interrupt
34//! [`InterruptExt`]: crate::async_hal::interrupts::InterruptExt
35//! [`pac`]: crate::pac
36
37pub use crate::interrupt::*;
38
39use crate::typelevel::Sealed;
40use atsamd_hal_macros::hal_cfg;
41
42/// Marker trait indicating that an interrupt source has one binding and
43/// one handler.
44///
45/// May not be implemented outside of this HAL.
46pub trait SingleInterruptSource: Sealed {}
47
48/// Marker trait indicating that an interrupt source has multiple bindings and
49/// handlers.
50///
51/// May not be implemented outside of this HAL.
52pub trait MultipleInterruptSources: Sealed {}
53
54macro_rules! declare_interrupts {
55    ($($(#[$cfg:meta])* $irqs:ident),* $(,)?) => {
56        $(
57            $(#[$cfg])*
58            #[allow(non_camel_case_types)]
59            #[doc=stringify!($irqs)]
60            #[doc=" typelevel interrupt."]
61            pub enum $irqs {}
62
63            $(#[$cfg])*
64            impl $crate::typelevel::Sealed for $irqs{}
65
66            $(#[$cfg])*
67            impl $crate::async_hal::interrupts::Interrupt for $irqs {
68                const IRQ: crate::pac::Interrupt = crate::pac::Interrupt::$irqs;
69            }
70
71            $(#[$cfg])*
72            impl $crate::async_hal::interrupts::SingleInterruptSource for $irqs {}
73        )*
74    }
75}
76
77/// Useful when we need to bind multiple interrupt sources to the same handler.
78/// Calling the `InterruptSource` methods on the created struct will act on all
79/// interrupt sources at once.
80// Lint allowed because the macro is unused for thumbv6 devices.
81#[allow(unused_macros)]
82macro_rules! declare_multiple_interrupts {
83    ($(#[$cfg:meta])* $name:ident: [ $($irq:ident),+ $(,)? ]) => {
84        ::paste::paste! {
85            $(#[$cfg])*
86            pub enum $name {}
87
88            $(#[$cfg])*
89            impl $crate::typelevel::Sealed for $name {}
90
91            $(#[$cfg])*
92            impl $crate::async_hal::interrupts::InterruptSource for $name {
93                unsafe fn enable() {
94                    $($crate::pac::Interrupt::$irq.enable();)+
95                }
96
97                fn disable() {
98                    $($crate::pac::Interrupt::$irq.disable();)+
99                }
100
101                fn unpend() {
102                    $($crate::pac::Interrupt::$irq.unpend();)+
103                }
104
105                fn set_priority(prio: $crate::async_hal::interrupts::Priority){
106                    $($crate::pac::Interrupt::$irq.set_priority(prio);)+
107                }
108            }
109
110            $(#[$cfg])*
111            impl $crate::async_hal::interrupts::MultipleInterruptSources for $name {}
112        }
113    };
114}
115
116// ---------- DMAC Interrupts ---------- //
117#[cfg(feature = "dma")]
118#[hal_cfg("dmac-d5x")]
119declare_multiple_interrupts!(DMAC: [DMAC_0, DMAC_1, DMAC_2, DMAC_OTHER]);
120
121#[cfg(feature = "dma")]
122#[hal_cfg(any("dmac-d11", "dmac-d21"))]
123declare_interrupts!(DMAC);
124
125// ----------  SERCOM Interrupts ---------- //
126#[hal_cfg(any("sercom0-d11", "sercom0-d21"))]
127declare_interrupts!(SERCOM0);
128
129#[hal_cfg(any("sercom1-d11", "sercom1-d21"))]
130declare_interrupts!(SERCOM1);
131
132#[hal_cfg(any("sercom2-d11", "sercom2-d21"))]
133declare_interrupts!(SERCOM2);
134
135#[hal_cfg("sercom3-d21")]
136declare_interrupts!(SERCOM3);
137
138#[hal_cfg("sercom4-d21")]
139declare_interrupts!(SERCOM4);
140
141#[hal_cfg("sercom5-d21")]
142declare_interrupts!(SERCOM5);
143
144#[hal_cfg("sercom0-d5x")]
145declare_multiple_interrupts!(SERCOM0: [SERCOM0_0, SERCOM0_1, SERCOM0_2, SERCOM0_OTHER ]);
146
147#[hal_cfg("sercom1-d5x")]
148declare_multiple_interrupts!(SERCOM1: [SERCOM1_0, SERCOM1_1, SERCOM1_2, SERCOM1_OTHER ]);
149
150#[hal_cfg("sercom2-d5x")]
151declare_multiple_interrupts!(SERCOM2: [SERCOM0_2, SERCOM2_1, SERCOM2_2, SERCOM2_OTHER ]);
152
153#[hal_cfg("sercom3-d5x")]
154declare_multiple_interrupts!(SERCOM3: [SERCOM3_0, SERCOM3_1, SERCOM3_2, SERCOM3_OTHER ]);
155
156#[hal_cfg("sercom4-d5x")]
157declare_multiple_interrupts!(SERCOM4: [SERCOM4_0, SERCOM4_1, SERCOM4_2, SERCOM4_OTHER ]);
158
159#[hal_cfg("sercom5-d5x")]
160declare_multiple_interrupts!(SERCOM5: [SERCOM5_0, SERCOM5_1, SERCOM5_2, SERCOM5_OTHER ]);
161
162#[hal_cfg("sercom6-d5x")]
163declare_multiple_interrupts!(SERCOM6: [SERCOM6_0, SERCOM6_1, SERCOM6_2, SERCOM6_OTHER ]);
164
165#[hal_cfg("sercom7-d5x")]
166declare_multiple_interrupts!(SERCOM7: [SERCOM7_0, SERCOM7_1, SERCOM7_2, SERCOM7_OTHER ]);
167
168// ----------  TC Interrupts ---------- //
169
170#[hal_cfg("tc0")]
171declare_interrupts!(TC0);
172
173#[hal_cfg("tc1")]
174declare_interrupts!(TC1);
175
176#[hal_cfg("tc2")]
177declare_interrupts!(TC2);
178
179#[hal_cfg("tc3")]
180declare_interrupts!(TC3);
181
182#[hal_cfg("tc4")]
183declare_interrupts!(TC4);
184
185#[hal_cfg("tc5")]
186declare_interrupts!(TC5);
187
188#[hal_cfg("tc6")]
189declare_interrupts!(TC6);
190
191#[hal_cfg("tc7")]
192declare_interrupts!(TC7);
193
194// ----------  EIC Interrupt ---------- //
195#[hal_cfg(any("eic-d11", "eic-d21"))]
196declare_interrupts!(EIC);
197
198#[hal_cfg("eic-d5x")]
199seq_macro::seq!(N in 0..= 15 {
200    paste::paste! {
201        declare_interrupts! {
202            EIC_EXTINT_~N
203        }
204
205    }
206});
207
208/// An interrupt source that may have one or many interrupt bindings.
209///
210/// This trait may implemented directly when multiple interrupt sources are
211/// needed to operate a single peripheral (eg, SERCOM and DMAC for thumbv7
212/// devices). If using one interrupt source per peripheral,
213/// implement [`Interrupt`] instead. When implemented on a type that handles
214/// multiple interrupt sources, the methods will act on all interrupt sources at
215/// once.
216///
217/// May not be implemented outside of this HAL.
218pub trait InterruptSource: crate::typelevel::Sealed {
219    /// Enable the interrupt.
220    ///
221    /// # Safety
222    ///
223    /// Do not enable any interrupt inside a critical section.
224    unsafe fn enable();
225
226    /// Disable the interrupt.
227    fn disable();
228
229    /// Unset interrupt pending.
230    fn unpend();
231
232    /// Set the interrupt priority.
233    fn set_priority(prio: Priority);
234}
235
236impl<T: Interrupt> InterruptSource for T {
237    unsafe fn enable() {
238        Self::enable();
239    }
240
241    fn disable() {
242        Self::disable();
243    }
244
245    fn unpend() {
246        Self::unpend();
247    }
248
249    fn set_priority(prio: Priority) {
250        Self::set_priority(prio);
251    }
252}
253
254/// Type-level interrupt.
255///
256/// This trait is implemented for all typelevel single interrupt types defined
257/// in this module. May not be implemented outside of this HAL.
258pub trait Interrupt: crate::typelevel::Sealed {
259    /// Interrupt enum variant.
260    ///
261    /// This allows going from typelevel interrupts (one type per interrupt,
262    /// defined in [`this module`](self)) to non-typelevel interrupts (a
263    /// single [`Interrupt`](crate::pac::Interrupt) enum type, with one
264    /// variant per interrupt).
265    const IRQ: crate::pac::Interrupt;
266
267    /// Enable the interrupt.
268    ///
269    /// # Safety
270    ///
271    /// Do not enable any interrupt inside a critical section.
272    #[inline]
273    unsafe fn enable() {
274        Self::IRQ.enable()
275    }
276
277    /// Disable the interrupt.
278    #[inline]
279    fn disable() {
280        Self::IRQ.disable()
281    }
282
283    /// Check if interrupt is enabled.
284    #[inline]
285    fn is_enabled() -> bool {
286        Self::IRQ.is_enabled()
287    }
288
289    /// Check if interrupt is pending.
290    #[inline]
291    fn is_pending() -> bool {
292        Self::IRQ.is_pending()
293    }
294
295    /// Set interrupt pending.
296    #[inline]
297    fn pend() {
298        Self::IRQ.pend()
299    }
300
301    /// Unset interrupt pending.
302    #[inline]
303    fn unpend() {
304        Self::IRQ.unpend()
305    }
306
307    /// Get the priority of the interrupt.
308    #[inline]
309    fn get_priority() -> Priority {
310        Self::IRQ.get_priority()
311    }
312
313    /// Set the interrupt priority.
314    #[inline]
315    fn set_priority(prio: Priority) {
316        Self::IRQ.set_priority(prio)
317    }
318
319    /// Set the interrupt priority with an already-acquired critical section.
320    ///
321    /// Equivalent to [`set_priority`](Self::set_priority), except you pass a
322    /// [`CriticalSection`] to prove you've already acquired a critical
323    /// section. This prevents acquiring another one, which saves code size.
324    ///
325    /// [`CriticalSection`]: critical_section::CriticalSection
326    #[inline]
327    fn set_priority_with_cs(cs: critical_section::CriticalSection, prio: Priority) {
328        Self::IRQ.set_priority_with_cs(cs, prio)
329    }
330}
331
332/// Interrupt handler.
333///
334/// Drivers that need to handle interrupts implement this trait.
335/// The user must ensure `on_interrupt()` is called every time the interrupt
336/// fires. Drivers must use use [`Binding`] to assert at compile time that the
337/// user has done so.
338pub trait Handler<I: InterruptSource>: Sealed {
339    /// Interrupt handler function.
340    ///
341    /// Must be called every time the `I` interrupt fires, synchronously from
342    /// the interrupt handler context.
343    ///
344    /// # Safety
345    ///
346    /// This function must ONLY be called from the interrupt handler for `I`.
347    unsafe fn on_interrupt();
348}
349
350/// Compile-time assertion that an interrupt has been bound to a handler.
351///
352/// For the vast majority of cases, you should use the `bind_interrupts!`
353/// macro instead of writing `unsafe impl`s of this trait.
354///
355/// # Safety
356///
357/// By implementing this trait, you are asserting that you have arranged for
358/// `H::on_interrupt()` to be called every time the `I` interrupt fires.
359///
360/// This allows drivers to check bindings at compile-time.
361pub unsafe trait Binding<I: InterruptSource, H: Handler<I>> {}