Add embassy-cortex-m crate.

- Move Interrupt and InterruptExecutor from `embassy` to `embassy-cortex-m`.
- Move Unborrow from `embassy` to `embassy-hal-common` (nothing in `embassy` requires it anymore)
- Move PeripheralMutex from `embassy-hal-common` to `embassy-cortex-m`.
This commit is contained in:
Dario Nieuwenhuis
2022-06-11 05:08:57 +02:00
parent db344c2bda
commit 5085100df2
104 changed files with 814 additions and 460 deletions

View File

@ -1,8 +1,7 @@
use core::marker::PhantomData;
use core::ptr;
use super::{raw, SendSpawner, Spawner};
use crate::interrupt::{Interrupt, InterruptExt};
use super::{raw, Spawner};
/// Thread mode executor, using WFE/SEV.
///
@ -55,86 +54,3 @@ impl Executor {
}
}
}
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))
}
/// 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.
///
/// This allows running async tasks at a priority higher than thread mode. One
/// use case is to leave thread mode free for non-async tasks. Another use case is
/// to run multiple executors: one in thread mode for low priority tasks and another in
/// interrupt mode for higher priority tasks. Higher priority tasks will preempt lower
/// priority ones.
///
/// It is even possible to run multiple interrupt mode executors at different priorities,
/// by assigning different priorities to the interrupts. For an example on how to do this,
/// See the 'multiprio' example for 'embassy-nrf'.
///
/// To use it, you have to pick an interrupt that won't be used by the hardware.
/// Some chips reserve some interrupts for this purpose, sometimes named "software interrupts" (SWI).
/// If this is not the case, you may use an interrupt from any unused peripheral.
///
/// 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<I: Interrupt> {
irq: I,
inner: raw::Executor,
not_send: PhantomData<*mut ()>,
}
impl<I: Interrupt> InterruptExecutor<I> {
/// Create a new Executor.
pub fn new(irq: I) -> Self {
let ctx = irq.number() as *mut ();
Self {
irq,
inner: raw::Executor::new(|ctx| pend_by_number(ctx as u16), ctx),
not_send: PhantomData,
}
}
/// Start the executor.
///
/// This initializes the executor, configures and 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.
///
/// 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:
///
/// - a [Forever](crate::util::Forever) (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();
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();
self.inner.spawner().make_send()
}
}

View File

@ -1,131 +0,0 @@
use atomic_polyfill::{compiler_fence, AtomicPtr, Ordering};
use core::mem;
use core::ptr;
use cortex_m::peripheral::NVIC;
pub use embassy_macros::interrupt_declare as declare;
pub use embassy_macros::interrupt_take as take;
/// Implementation detail, do not use outside embassy crates.
#[doc(hidden)]
pub struct Handler {
pub func: AtomicPtr<()>,
pub ctx: AtomicPtr<()>,
}
impl Handler {
pub const fn new() -> Self {
Self {
func: AtomicPtr::new(ptr::null_mut()),
ctx: AtomicPtr::new(ptr::null_mut()),
}
}
}
#[derive(Clone, Copy)]
pub(crate) struct NrWrap(pub(crate) u16);
unsafe impl cortex_m::interrupt::InterruptNumber for NrWrap {
fn number(self) -> u16 {
self.0
}
}
pub unsafe trait Interrupt: crate::util::Unborrow<Target = Self> {
type Priority: From<u8> + Into<u8> + Copy;
fn number(&self) -> u16;
unsafe fn steal() -> Self;
/// Implementation detail, do not use outside embassy crates.
#[doc(hidden)]
unsafe fn __handler(&self) -> &'static Handler;
}
pub trait InterruptExt: Interrupt {
fn set_handler(&self, func: unsafe fn(*mut ()));
fn remove_handler(&self);
fn set_handler_context(&self, ctx: *mut ());
fn enable(&self);
fn disable(&self);
#[cfg(not(armv6m))]
fn is_active(&self) -> bool;
fn is_enabled(&self) -> bool;
fn is_pending(&self) -> bool;
fn pend(&self);
fn unpend(&self);
fn get_priority(&self) -> Self::Priority;
fn set_priority(&self, prio: Self::Priority);
}
impl<T: Interrupt + ?Sized> InterruptExt for T {
fn set_handler(&self, func: unsafe fn(*mut ())) {
compiler_fence(Ordering::SeqCst);
let handler = unsafe { self.__handler() };
handler.func.store(func as *mut (), Ordering::Relaxed);
compiler_fence(Ordering::SeqCst);
}
fn remove_handler(&self) {
compiler_fence(Ordering::SeqCst);
let handler = unsafe { self.__handler() };
handler.func.store(ptr::null_mut(), Ordering::Relaxed);
compiler_fence(Ordering::SeqCst);
}
fn set_handler_context(&self, ctx: *mut ()) {
let handler = unsafe { self.__handler() };
handler.ctx.store(ctx, Ordering::Relaxed);
}
#[inline]
fn enable(&self) {
compiler_fence(Ordering::SeqCst);
unsafe {
NVIC::unmask(NrWrap(self.number()));
}
}
#[inline]
fn disable(&self) {
NVIC::mask(NrWrap(self.number()));
compiler_fence(Ordering::SeqCst);
}
#[inline]
#[cfg(not(armv6m))]
fn is_active(&self) -> bool {
NVIC::is_active(NrWrap(self.number()))
}
#[inline]
fn is_enabled(&self) -> bool {
NVIC::is_enabled(NrWrap(self.number()))
}
#[inline]
fn is_pending(&self) -> bool {
NVIC::is_pending(NrWrap(self.number()))
}
#[inline]
fn pend(&self) {
NVIC::pend(NrWrap(self.number()))
}
#[inline]
fn unpend(&self) {
NVIC::unpend(NrWrap(self.number()))
}
#[inline]
fn get_priority(&self) -> Self::Priority {
Self::Priority::from(NVIC::get_priority(NrWrap(self.number())))
}
#[inline]
fn set_priority(&self, prio: Self::Priority) {
unsafe {
let mut nvic: cortex_m::peripheral::NVIC = mem::transmute(());
nvic.set_priority(NrWrap(self.number()), prio.into())
}
}
}

View File

@ -11,8 +11,6 @@ pub(crate) mod fmt;
pub mod blocking_mutex;
pub mod channel;
pub mod executor;
#[cfg(cortex_m)]
pub mod interrupt;
pub mod mutex;
#[cfg(feature = "time")]
pub mod time;

View File

@ -3,11 +3,9 @@
mod forever;
mod select;
mod steal;
mod unborrow;
mod yield_now;
pub use forever::*;
pub use select::*;
pub use steal::*;
pub use unborrow::*;
pub use yield_now::*;

View File

@ -1,60 +0,0 @@
/// Unsafely unborrow an owned singleton out of a `&mut`.
///
/// It is intended to be implemented for owned peripheral singletons, such as `USART3` or `AnyPin`.
/// Unborrowing an owned `T` yields the same `T`. Unborrowing a `&mut T` yields a copy of the T.
///
/// This allows writing HAL drivers that either own or borrow their peripherals, but that don't have
/// to store pointers in the borrowed case.
///
/// Safety: this trait can be used to copy non-Copy types. Implementors must not cause
/// immediate UB when copied, and must not cause UB when copies are later used, provided they
/// are only used according the [`Self::unborrow`] safety contract.
///
pub unsafe trait Unborrow {
/// Unborrow result type
type Target;
/// Unborrow a value.
///
/// Safety: This returns a copy of a singleton that's normally not
/// copiable. The returned copy must ONLY be used while the lifetime of `self` is
/// valid, as if it were accessed through `self` every time.
unsafe fn unborrow(self) -> Self::Target;
}
unsafe impl<'a, T: Unborrow> Unborrow for &'a mut T {
type Target = T::Target;
unsafe fn unborrow(self) -> Self::Target {
T::unborrow(core::ptr::read(self))
}
}
macro_rules! unsafe_impl_unborrow_tuples {
($($t:ident),+) => {
unsafe impl<$($t),+> Unborrow for ($($t),+)
where
$(
$t: Unborrow<Target = $t>
),+
{
type Target = ($($t),+);
unsafe fn unborrow(self) -> Self::Target {
self
}
}
};
}
unsafe_impl_unborrow_tuples!(A, B);
unsafe_impl_unborrow_tuples!(A, B, C);
unsafe_impl_unborrow_tuples!(A, B, C, D);
unsafe_impl_unborrow_tuples!(A, B, C, D, E);
unsafe_impl_unborrow_tuples!(A, B, C, D, E, F);
unsafe_impl_unborrow_tuples!(A, B, C, D, E, F, G);
unsafe_impl_unborrow_tuples!(A, B, C, D, E, F, G, H);
unsafe_impl_unborrow_tuples!(A, B, C, D, E, F, G, H, I);
unsafe_impl_unborrow_tuples!(A, B, C, D, E, F, G, H, I, J);
unsafe_impl_unborrow_tuples!(A, B, C, D, E, F, G, H, I, J, K);
unsafe_impl_unborrow_tuples!(A, B, C, D, E, F, G, H, I, J, K, L);