nrf: add new interrupt binding traits and macro.

This commit is contained in:
Dario Nieuwenhuis 2023-02-26 22:42:22 +01:00
parent a054891263
commit 42c13c8c3d
2 changed files with 59 additions and 4 deletions

View File

@ -13,6 +13,36 @@ pub mod _export {
pub use embassy_macros::{cortex_m_interrupt as interrupt, cortex_m_interrupt_declare as declare}; pub use embassy_macros::{cortex_m_interrupt as interrupt, cortex_m_interrupt_declare as declare};
} }
/// Interrupt handler trait.
///
/// Drivers that need to handle interrupts implement this trait.
/// The user must ensure `on_interrupt()` is called every time the interrupt fires.
/// Drivers must use use [`Binding`] to assert at compile time that the user has done so.
pub trait Handler<I: Interrupt> {
/// Interrupt handler function.
///
/// Must be called every time the `I` interrupt fires, synchronously from
/// the interrupt handler context.
///
/// # Safety
///
/// This function must ONLY be called from the interrupt handler for `I`.
unsafe fn on_interrupt();
}
/// Compile-time assertion that an interrupt has been bound to a handler.
///
/// For the vast majority of cases, you should use the `bind_interrupts!`
/// macro instead of writing `unsafe impl`s of this trait.
///
/// # Safety
///
/// By implementing this trait, you are asserting that you have arranged for `H::on_interrupt()`
/// to be called every time the `I` interrupt fires.
///
/// This allows drivers to check bindings at compile-time.
pub unsafe trait Binding<I: Interrupt, H: Handler<I>> {}
/// Implementation detail, do not use outside embassy crates. /// Implementation detail, do not use outside embassy crates.
#[doc(hidden)] #[doc(hidden)]
pub struct DynHandler { pub struct DynHandler {

View File

@ -95,14 +95,39 @@ pub mod wdt;
#[cfg_attr(feature = "_nrf9160", path = "chips/nrf9160.rs")] #[cfg_attr(feature = "_nrf9160", path = "chips/nrf9160.rs")]
mod chip; mod chip;
pub use chip::EASY_DMA_SIZE;
pub mod interrupt { pub mod interrupt {
//! nRF interrupts for cortex-m devices. //! Interrupt definitions and macros to bind them.
pub use cortex_m::interrupt::{CriticalSection, Mutex}; pub use cortex_m::interrupt::{CriticalSection, Mutex};
pub use embassy_cortex_m::interrupt::*; pub use embassy_cortex_m::interrupt::*;
pub use crate::chip::irqs::*; pub use crate::chip::irqs::*;
/// Macro to bind interrupts to handlers.
///
/// This defines the right interrupt handlers, and creates a unit struct (like `struct Irqs;`)
/// and implements the right [`Binding`]s for it. You can pass this struct to drivers to
/// prove at compile-time that the right interrupts have been bound.
// developer note: this macro can't be in `embassy-cortex-m` due to the use of `$crate`.
#[macro_export]
macro_rules! bind_interrupts {
($vis:vis struct $name:ident { $($irq:ident => $($handler:ty),*;)* }) => {
$vis struct $name;
$(
#[allow(non_snake_case)]
#[no_mangle]
unsafe extern "C" fn $irq() {
$(
<$handler as $crate::interrupt::Handler<$crate::interrupt::$irq>>::on_interrupt();
)*
}
$(
unsafe impl $crate::interrupt::Binding<$crate::interrupt::$irq, $handler> for $name {}
)*
)*
};
}
} }
// Reexports // Reexports
@ -111,7 +136,7 @@ pub mod interrupt {
pub use chip::pac; pub use chip::pac;
#[cfg(not(feature = "unstable-pac"))] #[cfg(not(feature = "unstable-pac"))]
pub(crate) use chip::pac; pub(crate) use chip::pac;
pub use chip::{peripherals, Peripherals}; pub use chip::{peripherals, Peripherals, EASY_DMA_SIZE};
pub use embassy_cortex_m::executor; pub use embassy_cortex_m::executor;
pub use embassy_cortex_m::interrupt::_export::interrupt; pub use embassy_cortex_m::interrupt::_export::interrupt;
pub use embassy_hal_common::{into_ref, Peripheral, PeripheralRef}; pub use embassy_hal_common::{into_ref, Peripheral, PeripheralRef};