From 4dfa32b1e0572c03a5f97f0ed4a4a0acd6f12cca Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Mon, 27 Feb 2023 01:08:16 +0100 Subject: [PATCH] cortex-m/executor: don't use the owned interrupts system. Preparation for #1224. --- embassy-cortex-m/src/executor.rs | 99 ++++++++++++------- examples/nrf52840/src/bin/multiprio.rs | 32 +++--- examples/stm32f0/Cargo.toml | 2 +- .../src/bin/{priority.rs => multiprio.rs} | 32 +++--- examples/stm32f3/src/bin/multiprio.rs | 36 ++++--- examples/stm32f4/src/bin/multiprio.rs | 36 ++++--- 6 files changed, 152 insertions(+), 85 deletions(-) rename examples/stm32f0/src/bin/{priority.rs => multiprio.rs} (85%) diff --git a/embassy-cortex-m/src/executor.rs b/embassy-cortex-m/src/executor.rs index 0d1745d8..558539e7 100644 --- a/embassy-cortex-m/src/executor.rs +++ b/embassy-cortex-m/src/executor.rs @@ -1,18 +1,22 @@ //! Executor specific to cortex-m devices. -use core::marker::PhantomData; +use core::cell::UnsafeCell; +use core::mem::MaybeUninit; + +use atomic_polyfill::{AtomicBool, Ordering}; +use cortex_m::interrupt::InterruptNumber; +use cortex_m::peripheral::NVIC; pub use embassy_executor::*; -use crate::interrupt::{Interrupt, InterruptExt}; +#[derive(Clone, Copy)] +struct N(u16); +unsafe impl cortex_m::interrupt::InterruptNumber for N { + fn number(self) -> u16 { + self.0 + } +} fn pend_by_number(n: u16) { - #[derive(Clone, Copy)] - struct N(u16); - unsafe impl cortex_m::interrupt::InterruptNumber for N { - fn number(self) -> u16 { - self.0 - } - } cortex_m::peripheral::NVIC::pend(N(n)) } @@ -37,26 +41,37 @@ fn pend_by_number(n: u16) { /// /// It is somewhat more complex to use, it's recommended to use the thread-mode /// [`Executor`] instead, if it works for your use case. -pub struct InterruptExecutor { - irq: I, - inner: raw::Executor, - not_send: PhantomData<*mut ()>, +pub struct InterruptExecutor { + started: AtomicBool, + executor: UnsafeCell>, } -impl InterruptExecutor { - /// Create a new Executor. - pub fn new(irq: I) -> Self { - let ctx = irq.number() as *mut (); +unsafe impl Send for InterruptExecutor {} +unsafe impl Sync for InterruptExecutor {} + +impl InterruptExecutor { + /// Create a new, not started `InterruptExecutor`. + #[inline] + pub const fn new() -> Self { Self { - irq, - inner: raw::Executor::new(|ctx| pend_by_number(ctx as u16), ctx), - not_send: PhantomData, + started: AtomicBool::new(false), + executor: UnsafeCell::new(MaybeUninit::uninit()), } } + /// Executor interrupt callback. + /// + /// # Safety + /// + /// You MUST call this from the interrupt handler, and from nowhere else. + pub unsafe fn on_interrupt(&'static self) { + let executor = unsafe { (&*self.executor.get()).assume_init_ref() }; + executor.poll(); + } + /// Start the executor. /// - /// This initializes the executor, configures and enables the interrupt, and returns. + /// This initializes the executor, enables the interrupt, and returns. /// The executor keeps running in the background through the interrupt. /// /// This returns a [`SendSpawner`] you can use to spawn tasks on it. A [`SendSpawner`] @@ -67,23 +82,35 @@ impl InterruptExecutor { /// To obtain a [`Spawner`](embassy_executor::Spawner) for this executor, use [`Spawner::for_current_executor()`](embassy_executor::Spawner::for_current_executor()) from /// a task running in it. /// - /// This function requires `&'static mut self`. This means you have to store the - /// Executor instance in a place where it'll live forever and grants you mutable - /// access. There's a few ways to do this: + /// # Interrupt requirements /// - /// - a [StaticCell](https://docs.rs/static_cell/latest/static_cell/) (safe) - /// - a `static mut` (unsafe) - /// - a local variable in a function you know never returns (like `fn main() -> !`), upgrading its lifetime with `transmute`. (unsafe) - pub fn start(&'static mut self) -> SendSpawner { - self.irq.disable(); + /// You must write the interrupt handler yourself, and make it call [`on_interrupt()`](Self::on_interrupt). + /// + /// This method already enables (unmasks) the interrupt, you must NOT do it yourself. + /// + /// You must set the interrupt priority before calling this method. You MUST NOT + /// do it after. + /// + pub fn start(&'static self, irq: impl InterruptNumber) -> SendSpawner { + if self + .started + .compare_exchange(false, true, Ordering::Acquire, Ordering::Relaxed) + .is_err() + { + panic!("InterruptExecutor::start() called multiple times on the same executor."); + } - self.irq.set_handler(|ctx| unsafe { - let executor = &*(ctx as *const raw::Executor); - executor.poll(); - }); - self.irq.set_handler_context(&self.inner as *const _ as _); - self.irq.enable(); + unsafe { + (&mut *self.executor.get()).as_mut_ptr().write(raw::Executor::new( + |ctx| pend_by_number(ctx as u16), + irq.number() as *mut (), + )) + } - self.inner.spawner().make_send() + let executor = unsafe { (&*self.executor.get()).assume_init_ref() }; + + unsafe { NVIC::unmask(irq) } + + executor.spawner().make_send() } } diff --git a/examples/nrf52840/src/bin/multiprio.rs b/examples/nrf52840/src/bin/multiprio.rs index 25806ae4..851e189e 100644 --- a/examples/nrf52840/src/bin/multiprio.rs +++ b/examples/nrf52840/src/bin/multiprio.rs @@ -57,11 +57,14 @@ #![no_main] #![feature(type_alias_impl_trait)] +use core::mem; + +use cortex_m::peripheral::NVIC; use cortex_m_rt::entry; use defmt::{info, unwrap}; use embassy_nrf::executor::{Executor, InterruptExecutor}; use embassy_nrf::interrupt; -use embassy_nrf::interrupt::InterruptExt; +use embassy_nrf::pac::Interrupt; use embassy_time::{Duration, Instant, Timer}; use static_cell::StaticCell; use {defmt_rtt as _, panic_probe as _}; @@ -108,28 +111,35 @@ async fn run_low() { } } -static EXECUTOR_HIGH: StaticCell> = StaticCell::new(); -static EXECUTOR_MED: StaticCell> = StaticCell::new(); +static EXECUTOR_HIGH: InterruptExecutor = InterruptExecutor::new(); +static EXECUTOR_MED: InterruptExecutor = InterruptExecutor::new(); static EXECUTOR_LOW: StaticCell = StaticCell::new(); +#[interrupt] +unsafe fn SWI1_EGU1() { + EXECUTOR_HIGH.on_interrupt() +} + +#[interrupt] +unsafe fn SWI0_EGU0() { + EXECUTOR_MED.on_interrupt() +} + #[entry] fn main() -> ! { info!("Hello World!"); let _p = embassy_nrf::init(Default::default()); + let mut nvic: NVIC = unsafe { mem::transmute(()) }; // High-priority executor: SWI1_EGU1, priority level 6 - let irq = interrupt::take!(SWI1_EGU1); - irq.set_priority(interrupt::Priority::P6); - let executor = EXECUTOR_HIGH.init(InterruptExecutor::new(irq)); - let spawner = executor.start(); + unsafe { nvic.set_priority(Interrupt::SWI1_EGU1, 6 << 5) }; + let spawner = EXECUTOR_HIGH.start(Interrupt::SWI1_EGU1); unwrap!(spawner.spawn(run_high())); // Medium-priority executor: SWI0_EGU0, priority level 7 - let irq = interrupt::take!(SWI0_EGU0); - irq.set_priority(interrupt::Priority::P7); - let executor = EXECUTOR_MED.init(InterruptExecutor::new(irq)); - let spawner = executor.start(); + unsafe { nvic.set_priority(Interrupt::SWI0_EGU0, 7 << 5) }; + let spawner = EXECUTOR_MED.start(Interrupt::SWI0_EGU0); unwrap!(spawner.spawn(run_med())); // Low priority executor: runs in thread mode, using WFE/SEV diff --git a/examples/stm32f0/Cargo.toml b/examples/stm32f0/Cargo.toml index d4afbb8f..89d99b6d 100644 --- a/examples/stm32f0/Cargo.toml +++ b/examples/stm32f0/Cargo.toml @@ -15,5 +15,5 @@ panic-probe = "0.3" embassy-sync = { version = "0.1.0", path = "../../embassy-sync", features = ["defmt"] } embassy-executor = { version = "0.1.0", path = "../../embassy-executor", features = ["defmt", "integrated-timers"] } embassy-time = { version = "0.1.0", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime", "tick-hz-32_768"] } -embassy-stm32 = { version = "0.1.0", path = "../../embassy-stm32", features = ["nightly", "defmt", "memory-x", "stm32f091rc", "time-driver-any", "exti"] } +embassy-stm32 = { version = "0.1.0", path = "../../embassy-stm32", features = ["nightly", "defmt", "memory-x", "stm32f091rc", "time-driver-any", "exti", "unstable-pac"] } static_cell = "1.0" diff --git a/examples/stm32f0/src/bin/priority.rs b/examples/stm32f0/src/bin/multiprio.rs similarity index 85% rename from examples/stm32f0/src/bin/priority.rs rename to examples/stm32f0/src/bin/multiprio.rs index 7fed6a77..e0dc8c98 100644 --- a/examples/stm32f0/src/bin/priority.rs +++ b/examples/stm32f0/src/bin/multiprio.rs @@ -57,11 +57,14 @@ #![no_main] #![feature(type_alias_impl_trait)] +use core::mem; + +use cortex_m::peripheral::NVIC; use cortex_m_rt::entry; use defmt::*; use embassy_stm32::executor::{Executor, InterruptExecutor}; use embassy_stm32::interrupt; -use embassy_stm32::interrupt::InterruptExt; +use embassy_stm32::pac::Interrupt; use embassy_time::{Duration, Instant, Timer}; use static_cell::StaticCell; use {defmt_rtt as _, panic_probe as _}; @@ -108,27 +111,34 @@ async fn run_low() { } } -static EXECUTOR_HIGH: StaticCell> = StaticCell::new(); -static EXECUTOR_MED: StaticCell> = StaticCell::new(); +static EXECUTOR_HIGH: InterruptExecutor = InterruptExecutor::new(); +static EXECUTOR_MED: InterruptExecutor = InterruptExecutor::new(); static EXECUTOR_LOW: StaticCell = StaticCell::new(); +#[interrupt] +unsafe fn USART1() { + EXECUTOR_HIGH.on_interrupt() +} + +#[interrupt] +unsafe fn USART2() { + EXECUTOR_MED.on_interrupt() +} + #[entry] fn main() -> ! { // Initialize and create handle for devicer peripherals let _p = embassy_stm32::init(Default::default()); + let mut nvic: NVIC = unsafe { mem::transmute(()) }; // High-priority executor: USART1, priority level 6 - let irq = interrupt::take!(USART1); - irq.set_priority(interrupt::Priority::P6); - let executor = EXECUTOR_HIGH.init(InterruptExecutor::new(irq)); - let spawner = executor.start(); + unsafe { nvic.set_priority(Interrupt::USART1, 6 << 4) }; + let spawner = EXECUTOR_HIGH.start(Interrupt::USART1); unwrap!(spawner.spawn(run_high())); // Medium-priority executor: USART2, priority level 7 - let irq = interrupt::take!(USART2); - irq.set_priority(interrupt::Priority::P7); - let executor = EXECUTOR_MED.init(InterruptExecutor::new(irq)); - let spawner = executor.start(); + unsafe { nvic.set_priority(Interrupt::USART2, 7 << 4) }; + let spawner = EXECUTOR_MED.start(Interrupt::USART2); unwrap!(spawner.spawn(run_med())); // Low priority executor: runs in thread mode, using WFE/SEV diff --git a/examples/stm32f3/src/bin/multiprio.rs b/examples/stm32f3/src/bin/multiprio.rs index 9e8228a4..77df51ac 100644 --- a/examples/stm32f3/src/bin/multiprio.rs +++ b/examples/stm32f3/src/bin/multiprio.rs @@ -57,11 +57,14 @@ #![no_main] #![feature(type_alias_impl_trait)] +use core::mem; + +use cortex_m::peripheral::NVIC; use cortex_m_rt::entry; use defmt::*; use embassy_stm32::executor::{Executor, InterruptExecutor}; use embassy_stm32::interrupt; -use embassy_stm32::interrupt::InterruptExt; +use embassy_stm32::pac::Interrupt; use embassy_time::{Duration, Instant, Timer}; use static_cell::StaticCell; use {defmt_rtt as _, panic_probe as _}; @@ -108,28 +111,35 @@ async fn run_low() { } } -static EXECUTOR_HIGH: StaticCell> = StaticCell::new(); -static EXECUTOR_MED: StaticCell> = StaticCell::new(); +static EXECUTOR_HIGH: InterruptExecutor = InterruptExecutor::new(); +static EXECUTOR_MED: InterruptExecutor = InterruptExecutor::new(); static EXECUTOR_LOW: StaticCell = StaticCell::new(); +#[interrupt] +unsafe fn UART4() { + EXECUTOR_HIGH.on_interrupt() +} + +#[interrupt] +unsafe fn UART5() { + EXECUTOR_MED.on_interrupt() +} + #[entry] fn main() -> ! { info!("Hello World!"); let _p = embassy_stm32::init(Default::default()); + let mut nvic: NVIC = unsafe { mem::transmute(()) }; - // High-priority executor: SWI1_EGU1, priority level 6 - let irq = interrupt::take!(UART4); - irq.set_priority(interrupt::Priority::P6); - let executor = EXECUTOR_HIGH.init(InterruptExecutor::new(irq)); - let spawner = executor.start(); + // High-priority executor: UART4, priority level 6 + unsafe { nvic.set_priority(Interrupt::UART4, 6 << 4) }; + let spawner = EXECUTOR_HIGH.start(Interrupt::UART4); unwrap!(spawner.spawn(run_high())); - // Medium-priority executor: SWI0_EGU0, priority level 7 - let irq = interrupt::take!(UART5); - irq.set_priority(interrupt::Priority::P7); - let executor = EXECUTOR_MED.init(InterruptExecutor::new(irq)); - let spawner = executor.start(); + // Medium-priority executor: UART5, priority level 7 + unsafe { nvic.set_priority(Interrupt::UART5, 7 << 4) }; + let spawner = EXECUTOR_MED.start(Interrupt::UART5); unwrap!(spawner.spawn(run_med())); // Low priority executor: runs in thread mode, using WFE/SEV diff --git a/examples/stm32f4/src/bin/multiprio.rs b/examples/stm32f4/src/bin/multiprio.rs index 9e8228a4..77df51ac 100644 --- a/examples/stm32f4/src/bin/multiprio.rs +++ b/examples/stm32f4/src/bin/multiprio.rs @@ -57,11 +57,14 @@ #![no_main] #![feature(type_alias_impl_trait)] +use core::mem; + +use cortex_m::peripheral::NVIC; use cortex_m_rt::entry; use defmt::*; use embassy_stm32::executor::{Executor, InterruptExecutor}; use embassy_stm32::interrupt; -use embassy_stm32::interrupt::InterruptExt; +use embassy_stm32::pac::Interrupt; use embassy_time::{Duration, Instant, Timer}; use static_cell::StaticCell; use {defmt_rtt as _, panic_probe as _}; @@ -108,28 +111,35 @@ async fn run_low() { } } -static EXECUTOR_HIGH: StaticCell> = StaticCell::new(); -static EXECUTOR_MED: StaticCell> = StaticCell::new(); +static EXECUTOR_HIGH: InterruptExecutor = InterruptExecutor::new(); +static EXECUTOR_MED: InterruptExecutor = InterruptExecutor::new(); static EXECUTOR_LOW: StaticCell = StaticCell::new(); +#[interrupt] +unsafe fn UART4() { + EXECUTOR_HIGH.on_interrupt() +} + +#[interrupt] +unsafe fn UART5() { + EXECUTOR_MED.on_interrupt() +} + #[entry] fn main() -> ! { info!("Hello World!"); let _p = embassy_stm32::init(Default::default()); + let mut nvic: NVIC = unsafe { mem::transmute(()) }; - // High-priority executor: SWI1_EGU1, priority level 6 - let irq = interrupt::take!(UART4); - irq.set_priority(interrupt::Priority::P6); - let executor = EXECUTOR_HIGH.init(InterruptExecutor::new(irq)); - let spawner = executor.start(); + // High-priority executor: UART4, priority level 6 + unsafe { nvic.set_priority(Interrupt::UART4, 6 << 4) }; + let spawner = EXECUTOR_HIGH.start(Interrupt::UART4); unwrap!(spawner.spawn(run_high())); - // Medium-priority executor: SWI0_EGU0, priority level 7 - let irq = interrupt::take!(UART5); - irq.set_priority(interrupt::Priority::P7); - let executor = EXECUTOR_MED.init(InterruptExecutor::new(irq)); - let spawner = executor.start(); + // Medium-priority executor: UART5, priority level 7 + unsafe { nvic.set_priority(Interrupt::UART5, 7 << 4) }; + let spawner = EXECUTOR_MED.start(Interrupt::UART5); unwrap!(spawner.spawn(run_med())); // Low priority executor: runs in thread mode, using WFE/SEV