Software interrupts runtime binding (#1398)

* Split software interrupts

* Make swint0 unavailable on a multi_core target when using thread-executor

* Clarify why embassy_multiprio example needs two executors

* Make interrupt-executor require a SoftwareInterrupt<n>

* Improve code

* CHANGELOG.md

* Don't use `#[interrupt]` in thread-executor

* More docs

* Typo fixed
This commit is contained in:
Björn Quentin 2024-04-05 14:33:21 +02:00 committed by GitHub
parent d26b1bd504
commit b3bc28efef
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
11 changed files with 349 additions and 333 deletions

View File

@ -55,6 +55,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
- SYSTIMER and TIMG instances can now be created in async or blocking mode (#1348) - SYSTIMER and TIMG instances can now be created in async or blocking mode (#1348)
- Runtime ISR binding for TWAI (#1384) - Runtime ISR binding for TWAI (#1384)
- Runtime ISR binding for assist_debug (#1395) - Runtime ISR binding for assist_debug (#1395)
- Runtime ISR binding for software interrupts, software interrupts are split now, interrupt-executor takes the software interrupt to use, interrupt-executor is easier to use (#1398)
### Removed ### Removed

View File

@ -1,127 +1,95 @@
//! Interrupt-mode executor. //! Interrupt-mode executor.
use core::{cell::UnsafeCell, marker::PhantomData, mem::MaybeUninit}; use core::{cell::UnsafeCell, mem::MaybeUninit};
use embassy_executor::{raw, SendSpawner}; use embassy_executor::{raw, SendSpawner};
#[cfg(any(esp32c6, esp32h2))]
use peripherals::INTPRI as SystemPeripheral;
#[cfg(not(any(esp32c6, esp32h2)))]
use peripherals::SYSTEM as SystemPeripheral;
use portable_atomic::{AtomicUsize, Ordering}; use portable_atomic::{AtomicUsize, Ordering};
use crate::{get_core, interrupt, peripherals}; use crate::{
get_core,
interrupt::{self, InterruptHandler},
system::SoftwareInterrupt,
};
static FROM_CPU_IRQ_USED: AtomicUsize = AtomicUsize::new(0); static mut EXECUTORS: [CallbackContext; 4] = [
CallbackContext::new(),
pub trait SwPendableInterrupt { CallbackContext::new(),
fn enable(priority: interrupt::Priority); CallbackContext::new(),
fn number() -> usize; CallbackContext::new(),
fn pend(); ];
fn clear();
}
macro_rules! from_cpu {
($irq:literal) => {
paste::paste! {
pub struct [<FromCpu $irq>];
impl [<FromCpu $irq>] {
fn set_bit(value: bool) {
let system = unsafe { &*SystemPeripheral::PTR };
system
.[<cpu_intr_from_cpu_ $irq>]()
.write(|w| w.[<cpu_intr_from_cpu_ $irq>]().bit(value));
}
}
impl SwPendableInterrupt for [<FromCpu $irq>] {
fn enable(priority: interrupt::Priority) {
let mask = 1 << $irq;
// We don't allow using the same interrupt for multiple executors.
if FROM_CPU_IRQ_USED.fetch_or(mask, Ordering::SeqCst) & mask != 0 {
panic!("FROM_CPU_{} is already used by a different executor.", $irq);
}
unwrap!(interrupt::enable(peripherals::Interrupt::[<FROM_CPU_INTR $irq>], priority));
}
fn number() -> usize {
$irq
}
fn pend() {
Self::set_bit(true);
}
fn clear() {
Self::set_bit(false);
}
}
}
};
}
// from_cpu!(0); // reserve 0 for thread mode & multi-core
from_cpu!(1);
from_cpu!(2);
from_cpu!(3);
/// Interrupt mode executor. /// Interrupt mode executor.
/// ///
/// This executor runs tasks in interrupt mode. The interrupt handler is set up /// 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 /// to poll tasks, and when a task is woken the interrupt is pended from
/// software. /// software.
/// pub struct InterruptExecutor<const SWI: u8> {
/// # 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, core: AtomicUsize,
executor: UnsafeCell<MaybeUninit<raw::Executor>>, executor: UnsafeCell<MaybeUninit<raw::Executor>>,
_interrupt: PhantomData<SWI>, interrupt: SoftwareInterrupt<SWI>,
} }
unsafe impl<SWI: SwPendableInterrupt> Send for InterruptExecutor<SWI> {} unsafe impl<const SWI: u8> Send for InterruptExecutor<SWI> {}
unsafe impl<SWI: SwPendableInterrupt> Sync for InterruptExecutor<SWI> {} unsafe impl<const SWI: u8> Sync for InterruptExecutor<SWI> {}
impl<SWI> InterruptExecutor<SWI> struct CallbackContext {
where raw_executor: UnsafeCell<*mut raw::Executor>,
SWI: SwPendableInterrupt, }
{
/// Create a new `InterruptExecutor`. impl CallbackContext {
#[inline] const fn new() -> Self {
pub const fn new() -> Self {
Self { Self {
core: AtomicUsize::new(usize::MAX), raw_executor: UnsafeCell::new(core::ptr::null_mut()),
executor: UnsafeCell::new(MaybeUninit::uninit()),
_interrupt: PhantomData,
} }
} }
/// Executor interrupt callback. fn get(&self) -> *mut raw::Executor {
/// unsafe { (*self.raw_executor.get()) as *mut raw::Executor }
/// # Safety }
///
/// You MUST call this from the interrupt handler, and from nowhere else. fn set(&self, executor: *mut raw::Executor) {
// TODO: it would be pretty sweet if we could register our own interrupt handler unsafe {
// when vectoring is enabled. The user shouldn't need to provide the handler for self.raw_executor.get().write(executor);
// us. }
pub unsafe fn on_interrupt(&'static self) { }
SWI::clear(); }
let executor = unsafe { (*self.executor.get()).assume_init_ref() };
fn handle_interrupt<const NUM: u8>() {
let mut swi = unsafe { SoftwareInterrupt::<NUM>::steal() };
swi.reset();
unsafe {
let executor = EXECUTORS[NUM as usize].get().as_mut().unwrap();
executor.poll(); executor.poll();
} }
}
extern "C" fn swi_handler0() {
handle_interrupt::<0>();
}
extern "C" fn swi_handler1() {
handle_interrupt::<1>();
}
extern "C" fn swi_handler2() {
handle_interrupt::<2>();
}
extern "C" fn swi_handler3() {
handle_interrupt::<3>();
}
impl<const SWI: u8> InterruptExecutor<SWI> {
/// Create a new `InterruptExecutor`.
/// This takes the software interrupt to be used internally.
#[inline]
pub const fn new(interrupt: SoftwareInterrupt<SWI>) -> Self {
Self {
core: AtomicUsize::new(usize::MAX),
executor: UnsafeCell::new(MaybeUninit::uninit()),
interrupt,
}
}
/// Start the executor at the given priority level. /// Start the executor at the given priority level.
/// ///
@ -135,18 +103,7 @@ where
/// ///
/// To obtain a [`Spawner`] for this executor, use /// To obtain a [`Spawner`] for this executor, use
/// [`Spawner::for_current_executor()`] from a task running in it. /// [`Spawner::for_current_executor()`] from a task running in it.
/// pub fn start(&'static mut self, priority: interrupt::Priority) -> SendSpawner {
/// # 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 if self
.core .core
.compare_exchange( .compare_exchange(
@ -163,10 +120,21 @@ where
unsafe { unsafe {
(*self.executor.get()) (*self.executor.get())
.as_mut_ptr() .as_mut_ptr()
.write(raw::Executor::new(SWI::number() as *mut ())) .write(raw::Executor::new(SWI as *mut ()));
EXECUTORS[SWI as usize].set((*self.executor.get()).as_mut_ptr());
} }
SWI::enable(priority); let swi_handler = match SWI {
0 => swi_handler0,
1 => swi_handler1,
2 => swi_handler2,
3 => swi_handler3,
_ => unreachable!(),
};
self.interrupt
.set_interrupt_handler(InterruptHandler::new(swi_handler, priority));
let executor = unsafe { (*self.executor.get()).assume_init_ref() }; let executor = unsafe { (*self.executor.get()).assume_init_ref() };
executor.spawner().make_send() executor.spawner().make_send()

View File

@ -16,6 +16,9 @@ pub use interrupt::*;
))] ))]
#[export_name = "__pender"] #[export_name = "__pender"]
fn __pender(context: *mut ()) { fn __pender(context: *mut ()) {
#[cfg(feature = "embassy-executor-interrupt")]
use crate::system::SoftwareInterrupt;
let context = (context as usize).to_le_bytes(); let context = (context as usize).to_le_bytes();
cfg_if::cfg_if! { cfg_if::cfg_if! {
@ -23,9 +26,12 @@ fn __pender(context: *mut ()) {
match context[0] { match context[0] {
#[cfg(feature = "embassy-executor-thread")] #[cfg(feature = "embassy-executor-thread")]
0 => thread::pend_thread_mode(context[1] as usize), 0 => thread::pend_thread_mode(context[1] as usize),
1 => FromCpu1::pend(),
2 => FromCpu2::pend(), #[cfg(not(feature = "embassy-executor-thread"))]
3 => FromCpu3::pend(), 0 => unsafe { SoftwareInterrupt::<0>::steal().raise() },
1 => unsafe { SoftwareInterrupt::<1>::steal().raise() },
2 => unsafe { SoftwareInterrupt::<2>::steal().raise() },
3 => unsafe { SoftwareInterrupt::<3>::steal().raise() },
_ => {} _ => {}
} }
} else { } else {

View File

@ -3,13 +3,12 @@ use core::marker::PhantomData;
use embassy_executor::{raw, Spawner}; use embassy_executor::{raw, Spawner};
use portable_atomic::{AtomicBool, Ordering}; use portable_atomic::{AtomicBool, Ordering};
use crate::{get_core, prelude::interrupt};
#[cfg(multi_core)] #[cfg(multi_core)]
use crate::{ use procmacros::handler;
interrupt,
peripherals::{self, SYSTEM}, use crate::get_core;
}; #[cfg(multi_core)]
use crate::peripherals::SYSTEM;
/// global atomic used to keep track of whether there is work to do since sev() /// global atomic used to keep track of whether there is work to do since sev()
/// is not available on either Xtensa or RISC-V /// is not available on either Xtensa or RISC-V
@ -18,19 +17,17 @@ static SIGNAL_WORK_THREAD_MODE: [AtomicBool; 1] = [AtomicBool::new(false)];
#[cfg(multi_core)] #[cfg(multi_core)]
static SIGNAL_WORK_THREAD_MODE: [AtomicBool; 2] = [AtomicBool::new(false), AtomicBool::new(false)]; static SIGNAL_WORK_THREAD_MODE: [AtomicBool; 2] = [AtomicBool::new(false), AtomicBool::new(false)];
#[interrupt] #[cfg(multi_core)]
fn FROM_CPU_INTR0() { #[handler]
#[cfg(multi_core)] fn software0_interrupt() {
{ // This interrupt is fired when the thread-mode executor's core needs to be
// This interrupt is fired when the thread-mode executor's core needs to be // woken. It doesn't matter which core handles this interrupt first, the
// woken. It doesn't matter which core handles this interrupt first, the // point is just to wake up the core that is currently executing
// point is just to wake up the core that is currently executing // `waiti`.
// `waiti`. let system = unsafe { &*SYSTEM::PTR };
let system = unsafe { &*SYSTEM::PTR }; system
system .cpu_intr_from_cpu_0()
.cpu_intr_from_cpu_0() .write(|w| w.cpu_intr_from_cpu_0().bit(false));
.write(|w| w.cpu_intr_from_cpu_0().bit(false));
}
} }
pub(crate) fn pend_thread_mode(core: usize) { pub(crate) fn pend_thread_mode(core: usize) {
@ -60,12 +57,15 @@ pub struct Executor {
impl Executor { impl Executor {
/// Create a new Executor. /// Create a new Executor.
///
/// On multi_core systems this will use software-interrupt 0 which isn't
/// available for anything else.
pub fn new() -> Self { pub fn new() -> Self {
#[cfg(multi_core)] #[cfg(multi_core)]
unwrap!(interrupt::enable( unsafe {
peripherals::Interrupt::FROM_CPU_INTR0, crate::system::SoftwareInterrupt::<0>::steal()
interrupt::Priority::Priority1, .set_interrupt_handler(software0_interrupt)
)); }
Self { Self {
inner: raw::Executor::new(usize::from_le_bytes([0, get_core() as u8, 0, 0]) as *mut ()), inner: raw::Executor::new(usize::from_le_bytes([0, get_core() as u8, 0, 0]) as *mut ()),
@ -107,6 +107,7 @@ impl Executor {
} }
} }
#[doc(hidden)]
#[cfg(xtensa)] #[cfg(xtensa)]
pub fn wait_impl(cpu: usize) { pub fn wait_impl(cpu: usize) {
// Manual critical section implementation that only masks interrupts handlers. // Manual critical section implementation that only masks interrupts handlers.
@ -138,6 +139,7 @@ impl Executor {
} }
} }
#[doc(hidden)]
#[cfg(riscv)] #[cfg(riscv)]
pub fn wait_impl(cpu: usize) { pub fn wait_impl(cpu: usize) {
// we do not care about race conditions between the load and store operations, // we do not care about race conditions between the load and store operations,

View File

@ -46,7 +46,7 @@ use core::{
/// dedicated struct is memory efficiency: /// dedicated struct is memory efficiency:
/// ///
/// Peripheral singletons are typically either zero-sized (for concrete /// Peripheral singletons are typically either zero-sized (for concrete
/// peripehrals like `PA9` or `Spi4`) or very small (for example `AnyPin` which /// peripherals like `PA9` or `Spi4`) or very small (for example `AnyPin` which
/// is 1 byte). However `&mut T` is always 4 bytes for 32-bit targets, even if T /// is 1 byte). However `&mut T` is always 4 bytes for 32-bit targets, even if T
/// is zero-sized. PeripheralRef stores a copy of `T` instead, so it's the same /// is zero-sized. PeripheralRef stores a copy of `T` instead, so it's the same
/// size. /// size.

View File

@ -7,12 +7,11 @@
//! configure chip clocks, and control radio peripherals. //! configure chip clocks, and control radio peripherals.
//! //!
//! ### Software Interrupts //! ### Software Interrupts
//! The `SoftwareInterrupt` enum represents the available software interrupt //! The `SoftwareInterruptControl` struct gives access to the available software
//! sources. //! interrupts.
//! //!
//! The SoftwareInterruptControl struct allows raising or resetting software //! The `SoftwareInterrupt` struct allows raising or resetting software
//! interrupts using the `raise()` and `reset()` methods. The behavior of these //! interrupts using the `raise()` and `reset()` methods.
//! methods depends on the specific chip variant.
//! //!
//! ### Peripheral Clock Control //! ### Peripheral Clock Control
//! The `PeripheralClockControl` struct controls the enablement of peripheral //! The `PeripheralClockControl` struct controls the enablement of peripheral
@ -28,14 +27,7 @@
//! let clocks = ClockControl::boot_defaults(system.clock_control).freeze(); //! let clocks = ClockControl::boot_defaults(system.clock_control).freeze();
//! ``` //! ```
use crate::{peripheral::PeripheralRef, peripherals::SYSTEM}; use crate::{interrupt::InterruptHandler, peripheral::PeripheralRef, peripherals::SYSTEM};
pub enum SoftwareInterrupt {
SoftwareInterrupt0,
SoftwareInterrupt1,
SoftwareInterrupt2,
SoftwareInterrupt3,
}
/// Peripherals which can be enabled via `PeripheralClockControl` /// Peripherals which can be enabled via `PeripheralClockControl`
pub enum Peripheral { pub enum Peripheral {
@ -107,68 +99,141 @@ pub enum Peripheral {
LcdCam, LcdCam,
} }
pub struct SoftwareInterruptControl { /// A software interrupt can be triggered by software.
_private: (), #[non_exhaustive]
} pub struct SoftwareInterrupt<const NUM: u8> {}
impl SoftwareInterruptControl { impl<const NUM: u8> SoftwareInterrupt<NUM> {
pub fn raise(&mut self, interrupt: SoftwareInterrupt) { /// Sets the interrupt handler for this software-interrupt
// TODO interrupts missing in PAC or named wrong for P4
#[cfg(not(esp32p4))]
pub fn set_interrupt_handler(&mut self, handler: InterruptHandler) {
let interrupt = match NUM {
0 => crate::peripherals::Interrupt::FROM_CPU_INTR0,
1 => crate::peripherals::Interrupt::FROM_CPU_INTR1,
2 => crate::peripherals::Interrupt::FROM_CPU_INTR2,
3 => crate::peripherals::Interrupt::FROM_CPU_INTR3,
_ => unreachable!(),
};
unsafe {
crate::interrupt::bind_interrupt(interrupt, handler.handler());
crate::interrupt::enable(interrupt, handler.priority()).unwrap();
}
}
/// Trigger this software-interrupt
pub fn raise(&mut self) {
#[cfg(not(any(esp32c6, esp32h2)))] #[cfg(not(any(esp32c6, esp32h2)))]
let system = unsafe { &*SYSTEM::PTR }; let system = unsafe { &*SYSTEM::PTR };
#[cfg(any(esp32c6, esp32h2))] #[cfg(any(esp32c6, esp32h2))]
let system = unsafe { &*crate::peripherals::INTPRI::PTR }; let system = unsafe { &*crate::peripherals::INTPRI::PTR };
match interrupt { match NUM {
SoftwareInterrupt::SoftwareInterrupt0 => { 0 => {
system system
.cpu_intr_from_cpu_0() .cpu_intr_from_cpu_0()
.write(|w| w.cpu_intr_from_cpu_0().set_bit()); .write(|w| w.cpu_intr_from_cpu_0().set_bit());
} }
SoftwareInterrupt::SoftwareInterrupt1 => { 1 => {
system system
.cpu_intr_from_cpu_1() .cpu_intr_from_cpu_1()
.write(|w| w.cpu_intr_from_cpu_1().set_bit()); .write(|w| w.cpu_intr_from_cpu_1().set_bit());
} }
SoftwareInterrupt::SoftwareInterrupt2 => { 2 => {
system system
.cpu_intr_from_cpu_2() .cpu_intr_from_cpu_2()
.write(|w| w.cpu_intr_from_cpu_2().set_bit()); .write(|w| w.cpu_intr_from_cpu_2().set_bit());
} }
SoftwareInterrupt::SoftwareInterrupt3 => { 3 => {
system system
.cpu_intr_from_cpu_3() .cpu_intr_from_cpu_3()
.write(|w| w.cpu_intr_from_cpu_3().set_bit()); .write(|w| w.cpu_intr_from_cpu_3().set_bit());
} }
_ => unreachable!(),
} }
} }
pub fn reset(&mut self, interrupt: SoftwareInterrupt) { /// Resets this software-interrupt
pub fn reset(&mut self) {
#[cfg(not(any(esp32c6, esp32h2)))] #[cfg(not(any(esp32c6, esp32h2)))]
let system = unsafe { &*SYSTEM::PTR }; let system = unsafe { &*SYSTEM::PTR };
#[cfg(any(esp32c6, esp32h2))] #[cfg(any(esp32c6, esp32h2))]
let system = unsafe { &*crate::peripherals::INTPRI::PTR }; let system = unsafe { &*crate::peripherals::INTPRI::PTR };
match interrupt { match NUM {
SoftwareInterrupt::SoftwareInterrupt0 => { 0 => {
system system
.cpu_intr_from_cpu_0() .cpu_intr_from_cpu_0()
.write(|w| w.cpu_intr_from_cpu_0().clear_bit()); .write(|w| w.cpu_intr_from_cpu_0().clear_bit());
} }
SoftwareInterrupt::SoftwareInterrupt1 => { 1 => {
system system
.cpu_intr_from_cpu_1() .cpu_intr_from_cpu_1()
.write(|w| w.cpu_intr_from_cpu_1().clear_bit()); .write(|w| w.cpu_intr_from_cpu_1().clear_bit());
} }
SoftwareInterrupt::SoftwareInterrupt2 => { 2 => {
system system
.cpu_intr_from_cpu_2() .cpu_intr_from_cpu_2()
.write(|w| w.cpu_intr_from_cpu_2().clear_bit()); .write(|w| w.cpu_intr_from_cpu_2().clear_bit());
} }
SoftwareInterrupt::SoftwareInterrupt3 => { 3 => {
system system
.cpu_intr_from_cpu_3() .cpu_intr_from_cpu_3()
.write(|w| w.cpu_intr_from_cpu_3().clear_bit()); .write(|w| w.cpu_intr_from_cpu_3().clear_bit());
} }
_ => unreachable!(),
}
}
/// Unsafely create an instance of this peripheral out of thin air.
///
/// # Safety
///
/// You must ensure that you're only using one instance of this type at a
/// time.
#[inline]
pub unsafe fn steal() -> Self {
Self {}
}
}
impl<const NUM: u8> crate::peripheral::Peripheral for SoftwareInterrupt<NUM> {
type P = SoftwareInterrupt<NUM>;
#[inline]
unsafe fn clone_unchecked(&mut self) -> Self::P {
Self::steal()
}
}
impl<const NUM: u8> crate::private::Sealed for SoftwareInterrupt<NUM> {}
/// This gives access to the available software interrupts.
///
/// Please note: Software interrupt 0 is not available when using the
/// `embassy-executor-thread` feature
#[non_exhaustive]
pub struct SoftwareInterruptControl {
#[cfg(not(all(feature = "embassy-executor-thread", multi_core)))]
pub software_interrupt0: SoftwareInterrupt<0>,
pub software_interrupt1: SoftwareInterrupt<1>,
pub software_interrupt2: SoftwareInterrupt<2>,
pub software_interrupt3: SoftwareInterrupt<3>,
}
impl SoftwareInterruptControl {
fn new_internal() -> Self {
// the thread-executor uses SW-INT0 when used on a multi-core system
// we cannot easily require `software_interrupt0` there since it's created
// before `main` via proc-macro
SoftwareInterruptControl {
#[cfg(not(all(feature = "embassy-executor-thread", multi_core)))]
software_interrupt0: SoftwareInterrupt {},
software_interrupt1: SoftwareInterrupt {},
software_interrupt2: SoftwareInterrupt {},
software_interrupt3: SoftwareInterrupt {},
} }
} }
} }
@ -1025,6 +1090,7 @@ pub struct CpuControl {
_private: (), _private: (),
} }
/// Enumeration of the available radio peripherals for this chip.
pub enum RadioPeripherals { pub enum RadioPeripherals {
#[cfg(phy)] #[cfg(phy)]
Phy, Phy,
@ -1036,6 +1102,7 @@ pub enum RadioPeripherals {
Ieee802154, Ieee802154,
} }
/// Functionality of clocks controlling the radio peripherals.
pub struct RadioClockControl { pub struct RadioClockControl {
_private: (), _private: (),
} }
@ -1087,7 +1154,7 @@ impl<'d, T: crate::peripheral::Peripheral<P = SYSTEM> + 'd> SystemExt<'d> for T
clock_control: SystemClockControl { _private: () }, clock_control: SystemClockControl { _private: () },
cpu_control: CpuControl { _private: () }, cpu_control: CpuControl { _private: () },
radio_clock_control: RadioClockControl { _private: () }, radio_clock_control: RadioClockControl { _private: () },
software_interrupt_control: SoftwareInterruptControl { _private: () }, software_interrupt_control: SoftwareInterruptControl::new_internal(),
} }
} }
} }

View File

@ -11,10 +11,10 @@ use esp_hal::{
interrupt::{self, CpuInterrupt, Priority}, interrupt::{self, CpuInterrupt, Priority},
peripherals::{Interrupt, Peripherals}, peripherals::{Interrupt, Peripherals},
prelude::*, prelude::*,
system::{SoftwareInterrupt, SoftwareInterruptControl}, system::SoftwareInterrupt,
}; };
static SWINT: Mutex<RefCell<Option<SoftwareInterruptControl>>> = Mutex::new(RefCell::new(None)); static SWINT0: Mutex<RefCell<Option<SoftwareInterrupt<0>>>> = Mutex::new(RefCell::new(None));
#[entry] #[entry]
fn main() -> ! { fn main() -> ! {
@ -31,7 +31,11 @@ fn main() -> ! {
let system = peripherals.SYSTEM.split(); let system = peripherals.SYSTEM.split();
let sw_int = system.software_interrupt_control; let sw_int = system.software_interrupt_control;
critical_section::with(|cs| SWINT.borrow_ref_mut(cs).replace(sw_int)); critical_section::with(|cs| {
SWINT0
.borrow_ref_mut(cs)
.replace(sw_int.software_interrupt0)
});
interrupt::enable_direct( interrupt::enable_direct(
Interrupt::FROM_CPU_INTR0, Interrupt::FROM_CPU_INTR0,
Priority::Priority3, Priority::Priority3,
@ -67,14 +71,10 @@ fn main() -> ! {
} }
#[no_mangle] #[no_mangle]
fn cpu_int_20_handler() { fn interrupt20() {
unsafe { asm!("csrrwi x0, 0x7e1, 0 #disable timer") } unsafe { asm!("csrrwi x0, 0x7e1, 0 #disable timer") }
critical_section::with(|cs| { critical_section::with(|cs| {
SWINT SWINT0.borrow_ref_mut(cs).as_mut().unwrap().reset();
.borrow_ref_mut(cs)
.as_mut()
.unwrap()
.reset(SoftwareInterrupt::SoftwareInterrupt0);
}); });
let mut perf_counter: u32 = 0; let mut perf_counter: u32 = 0;

View File

@ -18,10 +18,7 @@ use esp_backtrace as _;
use esp_hal::{ use esp_hal::{
clock::ClockControl, clock::ClockControl,
cpu_control::{CpuControl, Stack}, cpu_control::{CpuControl, Stack},
embassy::{ embassy::{self, executor::InterruptExecutor},
self,
executor::{FromCpu1, FromCpu2, InterruptExecutor},
},
get_core, get_core,
gpio::{GpioPin, Output, PushPull, IO}, gpio::{GpioPin, Output, PushPull, IO},
interrupt::Priority, interrupt::Priority,
@ -34,19 +31,6 @@ use static_cell::make_static;
static mut APP_CORE_STACK: Stack<8192> = Stack::new(); static mut APP_CORE_STACK: Stack<8192> = Stack::new();
static INT_EXECUTOR_CORE_0: InterruptExecutor<FromCpu1> = InterruptExecutor::new();
static INT_EXECUTOR_CORE_1: InterruptExecutor<FromCpu2> = InterruptExecutor::new();
#[interrupt]
fn FROM_CPU_INTR1() {
unsafe { INT_EXECUTOR_CORE_0.on_interrupt() }
}
#[interrupt]
fn FROM_CPU_INTR2() {
unsafe { INT_EXECUTOR_CORE_1.on_interrupt() }
}
/// Waits for a message that contains a duration, then flashes a led for that /// Waits for a message that contains a duration, then flashes a led for that
/// duration of time. /// duration of time.
#[embassy_executor::task] #[embassy_executor::task]
@ -101,8 +85,13 @@ fn main() -> ! {
let led_ctrl_signal = &*make_static!(Signal::new()); let led_ctrl_signal = &*make_static!(Signal::new());
let led = io.pins.gpio0.into_push_pull_output(); let led = io.pins.gpio0.into_push_pull_output();
let executor_core1 =
InterruptExecutor::new(system.software_interrupt_control.software_interrupt1);
let executor_core1 = make_static!(executor_core1);
let cpu1_fnctn = move || { let cpu1_fnctn = move || {
let spawner = INT_EXECUTOR_CORE_1.start(Priority::Priority1); let spawner = executor_core1.start(Priority::Priority1);
spawner.spawn(control_led(led, led_ctrl_signal)).ok(); spawner.spawn(control_led(led, led_ctrl_signal)).ok();
@ -113,7 +102,11 @@ fn main() -> ! {
.start_app_core(unsafe { &mut *addr_of_mut!(APP_CORE_STACK) }, cpu1_fnctn) .start_app_core(unsafe { &mut *addr_of_mut!(APP_CORE_STACK) }, cpu1_fnctn)
.unwrap(); .unwrap();
let spawner = INT_EXECUTOR_CORE_0.start(Priority::Priority1); let executor_core0 =
InterruptExecutor::new(system.software_interrupt_control.software_interrupt0);
let executor_core0 = make_static!(executor_core0);
let spawner = executor_core0.start(Priority::Priority1);
spawner.spawn(enable_disable_led(led_ctrl_signal)).ok(); spawner.spawn(enable_disable_led(led_ctrl_signal)).ok();
// Just loop to show that the main thread does not need to poll the executor. // Just loop to show that the main thread does not need to poll the executor.

View File

@ -11,8 +11,11 @@
//! demonstrates that this task will continue to run even while the low //! demonstrates that this task will continue to run even while the low
//! priority blocking task is running. //! priority blocking task is running.
// HINT: At first it looks a bit suspicious that we need *two* executor features enabled here but that's because we are really using
// both together. The thread-executor is created by the `#[main]` macro and is used to spawn `low_prio_async` and `low_prio_blocking`.
// The interrupt-executor is created in `main` and is used to spawn `high_prio`.
//% CHIPS: esp32 esp32c2 esp32c3 esp32c6 esp32h2 esp32s2 esp32s3 //% CHIPS: esp32 esp32c2 esp32c3 esp32c6 esp32h2 esp32s2 esp32s3
// FIXME: We should not need *two* executor features enabled here...
//% FEATURES: embassy embassy-executor-interrupt embassy-executor-thread embassy-time-timg0 embassy-generic-timers //% FEATURES: embassy embassy-executor-interrupt embassy-executor-thread embassy-time-timg0 embassy-generic-timers
#![no_std] #![no_std]
@ -24,23 +27,14 @@ use embassy_time::{Duration, Instant, Ticker, Timer};
use esp_backtrace as _; use esp_backtrace as _;
use esp_hal::{ use esp_hal::{
clock::ClockControl, clock::ClockControl,
embassy::{ embassy::{self, executor::InterruptExecutor},
self,
executor::{FromCpu1, InterruptExecutor},
},
interrupt::Priority, interrupt::Priority,
peripherals::Peripherals, peripherals::Peripherals,
prelude::*, prelude::*,
timer::TimerGroup, timer::TimerGroup,
}; };
use esp_println::println; use esp_println::println;
use static_cell::make_static;
static INT_EXECUTOR_0: InterruptExecutor<FromCpu1> = InterruptExecutor::new();
#[interrupt]
fn FROM_CPU_INTR1() {
unsafe { INT_EXECUTOR_0.on_interrupt() }
}
/// Periodically print something. /// Periodically print something.
#[embassy_executor::task] #[embassy_executor::task]
@ -79,6 +73,7 @@ async fn low_prio_async() {
#[main] #[main]
async fn main(low_prio_spawner: Spawner) { async fn main(low_prio_spawner: Spawner) {
esp_println::logger::init_logger_from_env();
println!("Init!"); println!("Init!");
let peripherals = Peripherals::take(); let peripherals = Peripherals::take();
let system = peripherals.SYSTEM.split(); let system = peripherals.SYSTEM.split();
@ -87,7 +82,10 @@ async fn main(low_prio_spawner: Spawner) {
let timg0 = TimerGroup::new_async(peripherals.TIMG0, &clocks); let timg0 = TimerGroup::new_async(peripherals.TIMG0, &clocks);
embassy::init(&clocks, timg0); embassy::init(&clocks, timg0);
let spawner = INT_EXECUTOR_0.start(Priority::Priority2); let executor = InterruptExecutor::new(system.software_interrupt_control.software_interrupt2);
let executor = make_static!(executor);
let spawner = executor.start(Priority::Priority3);
spawner.must_spawn(high_prio()); spawner.must_spawn(high_prio());
println!("Spawning low-priority tasks"); println!("Spawning low-priority tasks");

View File

@ -13,27 +13,48 @@ use core::cell::RefCell;
use critical_section::Mutex; use critical_section::Mutex;
use esp_backtrace as _; use esp_backtrace as _;
use esp_hal::{ use esp_hal::{peripherals::Peripherals, prelude::*, system::SoftwareInterrupt};
interrupt::{self, Priority},
peripherals::{Interrupt, Peripherals},
prelude::*,
system::{SoftwareInterrupt, SoftwareInterruptControl},
};
static SWINT: Mutex<RefCell<Option<SoftwareInterruptControl>>> = Mutex::new(RefCell::new(None)); static SWINT0: Mutex<RefCell<Option<SoftwareInterrupt<0>>>> = Mutex::new(RefCell::new(None));
static SWINT1: Mutex<RefCell<Option<SoftwareInterrupt<1>>>> = Mutex::new(RefCell::new(None));
static SWINT2: Mutex<RefCell<Option<SoftwareInterrupt<2>>>> = Mutex::new(RefCell::new(None));
static SWINT3: Mutex<RefCell<Option<SoftwareInterrupt<3>>>> = Mutex::new(RefCell::new(None));
#[entry] #[entry]
fn main() -> ! { fn main() -> ! {
let peripherals = Peripherals::take(); let peripherals = Peripherals::take();
let system = peripherals.SYSTEM.split(); let system = peripherals.SYSTEM.split();
let sw_int = system.software_interrupt_control; let mut sw_int = system.software_interrupt_control;
critical_section::with(|cs| SWINT.borrow_ref_mut(cs).replace(sw_int)); critical_section::with(|cs| {
sw_int
.software_interrupt0
.set_interrupt_handler(swint0_handler);
SWINT0
.borrow_ref_mut(cs)
.replace(sw_int.software_interrupt0);
interrupt::enable(Interrupt::FROM_CPU_INTR0, Priority::Priority1).unwrap(); sw_int
interrupt::enable(Interrupt::FROM_CPU_INTR1, Priority::Priority2).unwrap(); .software_interrupt1
interrupt::enable(Interrupt::FROM_CPU_INTR2, Priority::Priority2).unwrap(); .set_interrupt_handler(swint1_handler);
interrupt::enable(Interrupt::FROM_CPU_INTR3, Priority::Priority3).unwrap(); SWINT1
.borrow_ref_mut(cs)
.replace(sw_int.software_interrupt1);
sw_int
.software_interrupt2
.set_interrupt_handler(swint2_handler);
SWINT2
.borrow_ref_mut(cs)
.replace(sw_int.software_interrupt2);
sw_int
.software_interrupt3
.set_interrupt_handler(swint3_handler);
SWINT3
.borrow_ref_mut(cs)
.replace(sw_int.software_interrupt3);
});
// Raise mid priority interrupt. // Raise mid priority interrupt.
// //
@ -42,77 +63,44 @@ fn main() -> ! {
// exiting the handler Once the handler is exited we expect to see same // exiting the handler Once the handler is exited we expect to see same
// priority and low priority interrupts served in that order. // priority and low priority interrupts served in that order.
critical_section::with(|cs| { critical_section::with(|cs| {
SWINT SWINT1.borrow_ref_mut(cs).as_mut().unwrap().raise();
.borrow_ref_mut(cs)
.as_mut()
.unwrap()
.raise(SoftwareInterrupt::SoftwareInterrupt1);
}); });
loop {} loop {}
} }
#[interrupt] #[handler(priority = esp_hal::interrupt::Priority::Priority1)]
fn FROM_CPU_INTR0() { fn swint0_handler() {
esp_println::println!("SW interrupt0"); esp_println::println!("SW interrupt0");
critical_section::with(|cs| { critical_section::with(|cs| {
SWINT SWINT0.borrow_ref_mut(cs).as_mut().unwrap().reset();
.borrow_ref_mut(cs)
.as_mut()
.unwrap()
.reset(SoftwareInterrupt::SoftwareInterrupt0);
}); });
} }
#[interrupt] #[handler(priority = esp_hal::interrupt::Priority::Priority2)]
fn FROM_CPU_INTR1() { fn swint1_handler() {
esp_println::println!("SW interrupt1 entry"); esp_println::println!("SW interrupt1 entry");
critical_section::with(|cs| { critical_section::with(|cs| {
SWINT SWINT1.borrow_ref_mut(cs).as_mut().unwrap().reset();
.borrow_ref_mut(cs) SWINT2.borrow_ref_mut(cs).as_mut().unwrap().raise(); // raise interrupt at same priority
.as_mut() SWINT3.borrow_ref_mut(cs).as_mut().unwrap().raise(); // raise interrupt at higher priority
.unwrap() SWINT0.borrow_ref_mut(cs).as_mut().unwrap().raise(); // raise interrupt at lower priority
.reset(SoftwareInterrupt::SoftwareInterrupt1);
SWINT
.borrow_ref_mut(cs)
.as_mut()
.unwrap()
.raise(SoftwareInterrupt::SoftwareInterrupt2); // raise interrupt at same priority
SWINT
.borrow_ref_mut(cs)
.as_mut()
.unwrap()
.raise(SoftwareInterrupt::SoftwareInterrupt3); // raise interrupt at higher priority
SWINT
.borrow_ref_mut(cs)
.as_mut()
.unwrap()
.raise(SoftwareInterrupt::SoftwareInterrupt0); // raise interrupt at
// lower priority
}); });
esp_println::println!("SW interrupt1 exit"); esp_println::println!("SW interrupt1 exit");
} }
#[interrupt] #[handler(priority = esp_hal::interrupt::Priority::Priority2)]
fn FROM_CPU_INTR2() { fn swint2_handler() {
esp_println::println!("SW interrupt2"); esp_println::println!("SW interrupt2");
critical_section::with(|cs| { critical_section::with(|cs| {
SWINT SWINT2.borrow_ref_mut(cs).as_mut().unwrap().reset();
.borrow_ref_mut(cs)
.as_mut()
.unwrap()
.reset(SoftwareInterrupt::SoftwareInterrupt2);
}); });
} }
#[interrupt] #[handler(priority = esp_hal::interrupt::Priority::Priority3)]
fn FROM_CPU_INTR3() { fn swint3_handler() {
esp_println::println!("SW interrupt3"); esp_println::println!("SW interrupt3");
critical_section::with(|cs| { critical_section::with(|cs| {
SWINT SWINT3.borrow_ref_mut(cs).as_mut().unwrap().reset();
.borrow_ref_mut(cs)
.as_mut()
.unwrap()
.reset(SoftwareInterrupt::SoftwareInterrupt3);
}); });
} }

View File

@ -16,13 +16,15 @@ use esp_backtrace as _;
use esp_hal::{ use esp_hal::{
clock::ClockControl, clock::ClockControl,
delay::Delay, delay::Delay,
interrupt::{self, Priority}, peripherals::Peripherals,
peripherals::{Interrupt, Peripherals},
prelude::*, prelude::*,
system::{SoftwareInterrupt, SoftwareInterruptControl}, system::SoftwareInterrupt,
}; };
static SWINT: Mutex<RefCell<Option<SoftwareInterruptControl>>> = Mutex::new(RefCell::new(None)); static SWINT0: Mutex<RefCell<Option<SoftwareInterrupt<0>>>> = Mutex::new(RefCell::new(None));
static SWINT1: Mutex<RefCell<Option<SoftwareInterrupt<1>>>> = Mutex::new(RefCell::new(None));
static SWINT2: Mutex<RefCell<Option<SoftwareInterrupt<2>>>> = Mutex::new(RefCell::new(None));
static SWINT3: Mutex<RefCell<Option<SoftwareInterrupt<3>>>> = Mutex::new(RefCell::new(None));
#[entry] #[entry]
fn main() -> ! { fn main() -> ! {
@ -30,13 +32,36 @@ fn main() -> ! {
let system = peripherals.SYSTEM.split(); let system = peripherals.SYSTEM.split();
let clocks = ClockControl::boot_defaults(system.clock_control).freeze(); let clocks = ClockControl::boot_defaults(system.clock_control).freeze();
let sw_int = system.software_interrupt_control; let mut sw_int = system.software_interrupt_control;
critical_section::with(|cs| SWINT.borrow_ref_mut(cs).replace(sw_int)); critical_section::with(|cs| {
sw_int
.software_interrupt0
.set_interrupt_handler(swint0_handler);
SWINT0
.borrow_ref_mut(cs)
.replace(sw_int.software_interrupt0);
interrupt::enable(Interrupt::FROM_CPU_INTR0, Priority::Priority3).unwrap(); sw_int
interrupt::enable(Interrupt::FROM_CPU_INTR1, Priority::Priority3).unwrap(); .software_interrupt1
interrupt::enable(Interrupt::FROM_CPU_INTR2, Priority::Priority3).unwrap(); .set_interrupt_handler(swint1_handler);
interrupt::enable(Interrupt::FROM_CPU_INTR3, Priority::Priority3).unwrap(); SWINT1
.borrow_ref_mut(cs)
.replace(sw_int.software_interrupt1);
sw_int
.software_interrupt2
.set_interrupt_handler(swint2_handler);
SWINT2
.borrow_ref_mut(cs)
.replace(sw_int.software_interrupt2);
sw_int
.software_interrupt3
.set_interrupt_handler(swint3_handler);
SWINT3
.borrow_ref_mut(cs)
.replace(sw_int.software_interrupt3);
});
let delay = Delay::new(&clocks); let delay = Delay::new(&clocks);
let mut counter = 0; let mut counter = 0;
@ -45,33 +70,17 @@ fn main() -> ! {
delay.delay_millis(500); delay.delay_millis(500);
match counter { match counter {
0 => critical_section::with(|cs| { 0 => critical_section::with(|cs| {
SWINT SWINT0.borrow_ref_mut(cs).as_mut().unwrap().raise();
.borrow_ref_mut(cs)
.as_mut()
.unwrap()
.raise(SoftwareInterrupt::SoftwareInterrupt0);
}), }),
1 => critical_section::with(|cs| { 1 => critical_section::with(|cs| {
SWINT SWINT1.borrow_ref_mut(cs).as_mut().unwrap().raise();
.borrow_ref_mut(cs)
.as_mut()
.unwrap()
.raise(SoftwareInterrupt::SoftwareInterrupt1);
}), }),
2 => critical_section::with(|cs| { 2 => critical_section::with(|cs| {
SWINT SWINT2.borrow_ref_mut(cs).as_mut().unwrap().raise();
.borrow_ref_mut(cs)
.as_mut()
.unwrap()
.raise(SoftwareInterrupt::SoftwareInterrupt2);
}), }),
3 => { 3 => {
critical_section::with(|cs| { critical_section::with(|cs| {
SWINT SWINT3.borrow_ref_mut(cs).as_mut().unwrap().raise();
.borrow_ref_mut(cs)
.as_mut()
.unwrap()
.raise(SoftwareInterrupt::SoftwareInterrupt3);
}); });
counter = -1 counter = -1
} }
@ -81,50 +90,34 @@ fn main() -> ! {
} }
} }
#[interrupt] #[handler]
fn FROM_CPU_INTR0() { fn swint0_handler() {
esp_println::println!("SW interrupt0"); esp_println::println!("SW interrupt0");
critical_section::with(|cs| { critical_section::with(|cs| {
SWINT SWINT0.borrow_ref_mut(cs).as_mut().unwrap().reset();
.borrow_ref_mut(cs)
.as_mut()
.unwrap()
.reset(SoftwareInterrupt::SoftwareInterrupt0);
}); });
} }
#[interrupt] #[handler]
fn FROM_CPU_INTR1() { fn swint1_handler() {
esp_println::println!("SW interrupt1"); esp_println::println!("SW interrupt1");
critical_section::with(|cs| { critical_section::with(|cs| {
SWINT SWINT1.borrow_ref_mut(cs).as_mut().unwrap().reset();
.borrow_ref_mut(cs)
.as_mut()
.unwrap()
.reset(SoftwareInterrupt::SoftwareInterrupt1);
}); });
} }
#[interrupt] #[handler]
fn FROM_CPU_INTR2() { fn swint2_handler() {
esp_println::println!("SW interrupt2"); esp_println::println!("SW interrupt2");
critical_section::with(|cs| { critical_section::with(|cs| {
SWINT SWINT2.borrow_ref_mut(cs).as_mut().unwrap().reset();
.borrow_ref_mut(cs)
.as_mut()
.unwrap()
.reset(SoftwareInterrupt::SoftwareInterrupt2);
}); });
} }
#[interrupt] #[handler]
fn FROM_CPU_INTR3() { fn swint3_handler() {
esp_println::println!("SW interrupt3"); esp_println::println!("SW interrupt3");
critical_section::with(|cs| { critical_section::with(|cs| {
SWINT SWINT3.borrow_ref_mut(cs).as_mut().unwrap().reset();
.borrow_ref_mut(cs)
.as_mut()
.unwrap()
.reset(SoftwareInterrupt::SoftwareInterrupt3);
}); });
} }