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>> {}