-
Notifications
You must be signed in to change notification settings - Fork 367
Multicore-aware executors #723
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
Merged
Changes from all commits
Commits
Show all changes
4 commits
Select commit
Hold shift + click to select a range
File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,5 @@ | ||
| #[cfg(xtensa)] | ||
| pub use xtensa::*; | ||
|
|
||
| #[cfg(xtensa)] | ||
| mod xtensa; |
179 changes: 179 additions & 0 deletions
179
esp-hal-common/src/embassy/executor/xtensa/interrupt.rs
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,179 @@ | ||
| //! Multicore-aware interrupt-mode executor. | ||
| use core::{ | ||
| cell::UnsafeCell, | ||
| marker::PhantomData, | ||
| mem::MaybeUninit, | ||
| ptr, | ||
| sync::atomic::{AtomicUsize, Ordering}, | ||
| }; | ||
|
|
||
| use embassy_executor::{ | ||
| raw::{self, Pender}, | ||
| SendSpawner, | ||
| }; | ||
| #[cfg(dport)] | ||
| use peripherals::DPORT as SystemPeripheral; | ||
| #[cfg(system)] | ||
| use peripherals::SYSTEM as SystemPeripheral; | ||
|
|
||
| use crate::{get_core, interrupt, peripherals}; | ||
|
|
||
| static FROM_CPU_IRQ_USED: AtomicUsize = AtomicUsize::new(0); | ||
|
|
||
| pub trait SwPendableInterrupt { | ||
| fn enable(priority: interrupt::Priority); | ||
| fn pend(); | ||
| fn clear(); | ||
| } | ||
|
|
||
| macro_rules! from_cpu { | ||
| ($cpu:literal) => { | ||
| paste::paste! { | ||
| pub struct [<FromCpu $cpu>]; | ||
|
|
||
| /// We don't allow using the same interrupt for multiple executors. | ||
|
|
||
| impl [<FromCpu $cpu>] { | ||
| fn set_bit(value: bool) { | ||
| let system = unsafe { &*SystemPeripheral::PTR }; | ||
| system | ||
| .[<cpu_intr_from_cpu_ $cpu>] | ||
| .write(|w| w.[<cpu_intr_from_cpu_ $cpu>]().bit(value)); | ||
| } | ||
| } | ||
|
|
||
| impl SwPendableInterrupt for [<FromCpu $cpu>] { | ||
| fn enable(priority: interrupt::Priority) { | ||
| let mask = 1 << $cpu; | ||
| if FROM_CPU_IRQ_USED.fetch_or(mask, Ordering::SeqCst) & mask != 0 { | ||
| panic!(concat!("FROM_CPU_", $cpu, " is already used by a different executor.")); | ||
| } | ||
|
|
||
| interrupt::enable(peripherals::Interrupt::[<FROM_CPU_INTR $cpu>], priority).unwrap(); | ||
| } | ||
|
|
||
| fn pend() { | ||
| Self::set_bit(true); | ||
| } | ||
|
|
||
| fn clear() { | ||
| Self::set_bit(false); | ||
| } | ||
| } | ||
| } | ||
| }; | ||
| } | ||
|
|
||
| from_cpu!(1); | ||
| from_cpu!(2); | ||
| from_cpu!(3); | ||
|
|
||
| /// Interrupt mode executor. | ||
| /// | ||
| /// This executor runs tasks in interrupt mode. The interrupt handler is set up | ||
| /// to poll tasks, and when a task is woken the interrupt is pended from | ||
| /// software. | ||
| /// | ||
| /// # Interrupt requirements | ||
| /// | ||
| /// You must write the interrupt handler yourself, and make it call | ||
| /// [`Self::on_interrupt()`] | ||
| /// | ||
| /// ```rust,ignore | ||
| /// #[interrupt] | ||
| /// fn FROM_CPU_INTR1() { | ||
| /// unsafe { INT_EXECUTOR.on_interrupt() } | ||
| /// } | ||
| /// ``` | ||
| pub struct InterruptExecutor<SWI> | ||
| where | ||
| SWI: SwPendableInterrupt, | ||
| { | ||
| core: AtomicUsize, | ||
| executor: UnsafeCell<MaybeUninit<raw::Executor>>, | ||
| _interrupt: PhantomData<SWI>, | ||
| } | ||
|
|
||
| unsafe impl<SWI: SwPendableInterrupt> Send for InterruptExecutor<SWI> {} | ||
| unsafe impl<SWI: SwPendableInterrupt> Sync for InterruptExecutor<SWI> {} | ||
|
|
||
| impl<SWI> InterruptExecutor<SWI> | ||
| where | ||
| SWI: SwPendableInterrupt, | ||
| { | ||
| /// Create a new `InterruptExecutor`. | ||
| #[inline] | ||
| pub const fn new() -> Self { | ||
| Self { | ||
| core: AtomicUsize::new(usize::MAX), | ||
| executor: UnsafeCell::new(MaybeUninit::uninit()), | ||
| _interrupt: PhantomData, | ||
| } | ||
| } | ||
|
|
||
| /// Executor interrupt callback. | ||
| /// | ||
| /// # Safety | ||
| /// | ||
| /// You MUST call this from the interrupt handler, and from nowhere else. | ||
| // TODO: it would be pretty sweet if we could register our own interrupt handler | ||
| // when vectoring is enabled. The user shouldn't need to provide the handler for | ||
| // us. | ||
| pub unsafe fn on_interrupt(&'static self) { | ||
| SWI::clear(); | ||
| let executor = unsafe { (*self.executor.get()).assume_init_ref() }; | ||
| executor.poll(); | ||
| } | ||
|
|
||
| /// Start the executor at the given priority level. | ||
| /// | ||
| /// 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`] is returned instead of a [`Spawner`] because the | ||
| /// executor effectively runs in a different "thread" (the interrupt), | ||
| /// so spawning tasks on it is effectively sending them. | ||
| /// | ||
| /// To obtain a [`Spawner`] for this executor, use | ||
| /// [`Spawner::for_current_executor()`] from a task running in it. | ||
| /// | ||
| /// # Interrupt requirements | ||
| /// | ||
| /// You must write the interrupt handler yourself, and make it call | ||
| /// [`Self::on_interrupt()`] | ||
| /// | ||
| /// This method already enables (unmasks) the interrupt, you must NOT do it | ||
| /// yourself. | ||
| /// | ||
| /// [`Spawner`]: embassy_executor::Spawner | ||
| /// [`Spawner::for_current_executor()`]: embassy_executor::Spawner::for_current_executor() | ||
| pub fn start(&'static self, priority: interrupt::Priority) -> SendSpawner { | ||
| if self | ||
| .core | ||
| .compare_exchange( | ||
| usize::MAX, | ||
| get_core() as usize, | ||
| Ordering::Acquire, | ||
| Ordering::Relaxed, | ||
| ) | ||
| .is_err() | ||
| { | ||
| panic!("InterruptExecutor::start() called multiple times on the same executor."); | ||
| } | ||
|
|
||
| unsafe { | ||
| (*self.executor.get()) | ||
| .as_mut_ptr() | ||
| .write(raw::Executor::new(Pender::new_from_callback( | ||
| |_| SWI::pend(), | ||
| ptr::null_mut(), | ||
| ))) | ||
| } | ||
|
|
||
| SWI::enable(priority); | ||
|
|
||
| let executor = unsafe { (*self.executor.get()).assume_init_ref() }; | ||
| executor.spawner().make_send() | ||
| } | ||
| } | ||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,13 @@ | ||
| //! Multicore-aware embassy executors. | ||
|
|
||
| #[cfg(feature = "embassy-executor-interrupt")] | ||
| pub mod interrupt; | ||
|
|
||
| #[cfg(feature = "embassy-executor-interrupt")] | ||
| pub use interrupt::*; | ||
|
|
||
| #[cfg(feature = "embassy-executor-thread")] | ||
| pub mod thread; | ||
|
|
||
| #[cfg(feature = "embassy-executor-thread")] | ||
| pub use thread::*; |
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This would be pretty nice, however, I can't think of a way to do it with the current interrupt registering mechanism. Unless we do some weird stuff with weak linkage, but that would introduce errors on its own. I think the examples for now explain the steps that need to be taken well enough.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm not sure about weak linkage, in theory (unless there are details I need to know but don't) we should be able to copy
peripherals::__INTERRUPTSinto RAM, change https://github.com/esp-rs/esp-hal/blob/6c2659f9e4db7fdc1d5431c267b56ec6caa02ebe/esp-hal-common/src/interrupt/xtensa.rs#L440C9-L440C87 to use the copied handlers and roll with that. I do agree that it's not absolutely necessary