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

@ -0,0 +1,47 @@
[package]
name = "embassy-cortex-m"
version = "0.1.0"
authors = ["Dario Nieuwenhuis <dirbaio@dirbaio.net>"]
edition = "2018"
resolver = "2"
[package.metadata.embassy_docs]
src_base = "https://github.com/embassy-rs/embassy/blob/embassy-cortex-m-v$VERSION/embassy-cortex-m/src/"
src_base_git = "https://github.com/embassy-rs/embassy/blob/master/embassy-cortex-m/src/"
features = []
flavors = [
{ name = "thumbv6m-none-eabi", target = "thumbv6m-none-eabi", features = [] },
{ name = "thumbv7m-none-eabi", target = "thumbv7m-none-eabi", features = [] },
{ name = "thumbv7em-none-eabi", target = "thumbv7em-none-eabi", features = [] },
{ name = "thumbv7em-none-eabihf", target = "thumbv7em-none-eabihf", features = [] },
{ name = "thumbv8m.base-none-eabi", target = "thumbv8m.base-none-eabi", features = [] },
{ name = "thumbv8m.main-none-eabi", target = "thumbv8m.main-none-eabi", features = [] },
{ name = "thumbv8m.main-none-eabihf", target = "thumbv8m.main-none-eabihf", features = [] },
]
[features]
default = []
# Define the number of NVIC priority bits.
prio-bits-0 = []
prio-bits-1 = []
prio-bits-2 = []
prio-bits-3 = []
prio-bits-4 = []
prio-bits-5 = []
prio-bits-6 = []
prio-bits-7 = []
prio-bits-8 = []
[dependencies]
defmt = { version = "0.3", optional = true }
log = { version = "0.4.14", optional = true }
embassy = { version = "0.1.0", path = "../embassy"}
embassy-macros = { version = "0.1.0", path = "../embassy-macros"}
embassy-hal-common = { version = "0.1.0", path = "../embassy-hal-common"}
atomic-polyfill = "0.1.5"
critical-section = "0.2.5"
cfg-if = "1.0.0"
cortex-m = "0.7.3"

29
embassy-cortex-m/build.rs Normal file
View File

@ -0,0 +1,29 @@
use std::env;
fn main() {
let target = env::var("TARGET").unwrap();
if target.starts_with("thumbv6m-") {
println!("cargo:rustc-cfg=cortex_m");
println!("cargo:rustc-cfg=armv6m");
} else if target.starts_with("thumbv7m-") {
println!("cargo:rustc-cfg=cortex_m");
println!("cargo:rustc-cfg=armv7m");
} else if target.starts_with("thumbv7em-") {
println!("cargo:rustc-cfg=cortex_m");
println!("cargo:rustc-cfg=armv7m");
println!("cargo:rustc-cfg=armv7em"); // (not currently used)
} else if target.starts_with("thumbv8m.base") {
println!("cargo:rustc-cfg=cortex_m");
println!("cargo:rustc-cfg=armv8m");
println!("cargo:rustc-cfg=armv8m_base");
} else if target.starts_with("thumbv8m.main") {
println!("cargo:rustc-cfg=cortex_m");
println!("cargo:rustc-cfg=armv8m");
println!("cargo:rustc-cfg=armv8m_main");
}
if target.ends_with("-eabihf") {
println!("cargo:rustc-cfg=has_fpu");
}
}

View File

@ -0,0 +1,89 @@
use core::marker::PhantomData;
use crate::interrupt::{Interrupt, InterruptExt};
use embassy::executor::{raw, SendSpawner};
pub use embassy::executor::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()
}
}

228
embassy-cortex-m/src/fmt.rs Normal file
View File

@ -0,0 +1,228 @@
#![macro_use]
#![allow(unused_macros)]
#[cfg(all(feature = "defmt", feature = "log"))]
compile_error!("You may not enable both `defmt` and `log` features.");
macro_rules! assert {
($($x:tt)*) => {
{
#[cfg(not(feature = "defmt"))]
::core::assert!($($x)*);
#[cfg(feature = "defmt")]
::defmt::assert!($($x)*);
}
};
}
macro_rules! assert_eq {
($($x:tt)*) => {
{
#[cfg(not(feature = "defmt"))]
::core::assert_eq!($($x)*);
#[cfg(feature = "defmt")]
::defmt::assert_eq!($($x)*);
}
};
}
macro_rules! assert_ne {
($($x:tt)*) => {
{
#[cfg(not(feature = "defmt"))]
::core::assert_ne!($($x)*);
#[cfg(feature = "defmt")]
::defmt::assert_ne!($($x)*);
}
};
}
macro_rules! debug_assert {
($($x:tt)*) => {
{
#[cfg(not(feature = "defmt"))]
::core::debug_assert!($($x)*);
#[cfg(feature = "defmt")]
::defmt::debug_assert!($($x)*);
}
};
}
macro_rules! debug_assert_eq {
($($x:tt)*) => {
{
#[cfg(not(feature = "defmt"))]
::core::debug_assert_eq!($($x)*);
#[cfg(feature = "defmt")]
::defmt::debug_assert_eq!($($x)*);
}
};
}
macro_rules! debug_assert_ne {
($($x:tt)*) => {
{
#[cfg(not(feature = "defmt"))]
::core::debug_assert_ne!($($x)*);
#[cfg(feature = "defmt")]
::defmt::debug_assert_ne!($($x)*);
}
};
}
macro_rules! todo {
($($x:tt)*) => {
{
#[cfg(not(feature = "defmt"))]
::core::todo!($($x)*);
#[cfg(feature = "defmt")]
::defmt::todo!($($x)*);
}
};
}
macro_rules! unreachable {
($($x:tt)*) => {
{
#[cfg(not(feature = "defmt"))]
::core::unreachable!($($x)*);
#[cfg(feature = "defmt")]
::defmt::unreachable!($($x)*);
}
};
}
macro_rules! panic {
($($x:tt)*) => {
{
#[cfg(not(feature = "defmt"))]
::core::panic!($($x)*);
#[cfg(feature = "defmt")]
::defmt::panic!($($x)*);
}
};
}
macro_rules! trace {
($s:literal $(, $x:expr)* $(,)?) => {
{
#[cfg(feature = "log")]
::log::trace!($s $(, $x)*);
#[cfg(feature = "defmt")]
::defmt::trace!($s $(, $x)*);
#[cfg(not(any(feature = "log", feature="defmt")))]
let _ = ($( & $x ),*);
}
};
}
macro_rules! debug {
($s:literal $(, $x:expr)* $(,)?) => {
{
#[cfg(feature = "log")]
::log::debug!($s $(, $x)*);
#[cfg(feature = "defmt")]
::defmt::debug!($s $(, $x)*);
#[cfg(not(any(feature = "log", feature="defmt")))]
let _ = ($( & $x ),*);
}
};
}
macro_rules! info {
($s:literal $(, $x:expr)* $(,)?) => {
{
#[cfg(feature = "log")]
::log::info!($s $(, $x)*);
#[cfg(feature = "defmt")]
::defmt::info!($s $(, $x)*);
#[cfg(not(any(feature = "log", feature="defmt")))]
let _ = ($( & $x ),*);
}
};
}
macro_rules! warn {
($s:literal $(, $x:expr)* $(,)?) => {
{
#[cfg(feature = "log")]
::log::warn!($s $(, $x)*);
#[cfg(feature = "defmt")]
::defmt::warn!($s $(, $x)*);
#[cfg(not(any(feature = "log", feature="defmt")))]
let _ = ($( & $x ),*);
}
};
}
macro_rules! error {
($s:literal $(, $x:expr)* $(,)?) => {
{
#[cfg(feature = "log")]
::log::error!($s $(, $x)*);
#[cfg(feature = "defmt")]
::defmt::error!($s $(, $x)*);
#[cfg(not(any(feature = "log", feature="defmt")))]
let _ = ($( & $x ),*);
}
};
}
#[cfg(feature = "defmt")]
macro_rules! unwrap {
($($x:tt)*) => {
::defmt::unwrap!($($x)*)
};
}
#[cfg(not(feature = "defmt"))]
macro_rules! unwrap {
($arg:expr) => {
match $crate::fmt::Try::into_result($arg) {
::core::result::Result::Ok(t) => t,
::core::result::Result::Err(e) => {
::core::panic!("unwrap of `{}` failed: {:?}", ::core::stringify!($arg), e);
}
}
};
($arg:expr, $($msg:expr),+ $(,)? ) => {
match $crate::fmt::Try::into_result($arg) {
::core::result::Result::Ok(t) => t,
::core::result::Result::Err(e) => {
::core::panic!("unwrap of `{}` failed: {}: {:?}", ::core::stringify!($arg), ::core::format_args!($($msg,)*), e);
}
}
}
}
#[cfg(feature = "defmt-timestamp-uptime")]
defmt::timestamp! {"{=u64:us}", crate::time::Instant::now().as_micros() }
#[derive(Debug, Copy, Clone, Eq, PartialEq)]
pub struct NoneError;
pub trait Try {
type Ok;
type Error;
fn into_result(self) -> Result<Self::Ok, Self::Error>;
}
impl<T> Try for Option<T> {
type Ok = T;
type Error = NoneError;
#[inline]
fn into_result(self) -> Result<T, NoneError> {
self.ok_or(NoneError)
}
}
impl<T, E> Try for Result<T, E> {
type Ok = T;
type Error = E;
#[inline]
fn into_result(self) -> Self {
self
}
}

View File

@ -0,0 +1,735 @@
use atomic_polyfill::{compiler_fence, AtomicPtr, Ordering};
use core::mem;
use core::ptr;
use cortex_m::peripheral::NVIC;
use embassy_hal_common::Unborrow;
pub use embassy_macros::cortex_m_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: Unborrow<Target = Self> {
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) -> Priority;
fn set_priority(&self, prio: 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) -> Priority {
Priority::from(NVIC::get_priority(NrWrap(self.number())))
}
#[inline]
fn set_priority(&self, prio: Priority) {
unsafe {
let mut nvic: cortex_m::peripheral::NVIC = mem::transmute(());
nvic.set_priority(NrWrap(self.number()), prio.into())
}
}
}
impl From<u8> for Priority {
fn from(priority: u8) -> Self {
unsafe { mem::transmute(priority & PRIO_MASK) }
}
}
impl From<Priority> for u8 {
fn from(p: Priority) -> Self {
p as u8
}
}
#[cfg(feature = "prio-bits-0")]
const PRIO_MASK: u8 = 0x00;
#[cfg(feature = "prio-bits-1")]
const PRIO_MASK: u8 = 0x80;
#[cfg(feature = "prio-bits-2")]
const PRIO_MASK: u8 = 0xc0;
#[cfg(feature = "prio-bits-3")]
const PRIO_MASK: u8 = 0xe0;
#[cfg(feature = "prio-bits-4")]
const PRIO_MASK: u8 = 0xf0;
#[cfg(feature = "prio-bits-5")]
const PRIO_MASK: u8 = 0xf8;
#[cfg(feature = "prio-bits-6")]
const PRIO_MASK: u8 = 0xfc;
#[cfg(feature = "prio-bits-7")]
const PRIO_MASK: u8 = 0xfe;
#[cfg(feature = "prio-bits-8")]
const PRIO_MASK: u8 = 0xff;
#[cfg(feature = "prio-bits-0")]
#[derive(Debug, Copy, Clone, Eq, PartialEq, Ord, PartialOrd)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
#[repr(u8)]
pub enum Priority {
P0 = 0x0,
}
#[cfg(feature = "prio-bits-1")]
#[derive(Debug, Copy, Clone, Eq, PartialEq, Ord, PartialOrd)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
#[repr(u8)]
pub enum Priority {
P0 = 0x0,
P1 = 0x80,
}
#[cfg(feature = "prio-bits-2")]
#[derive(Debug, Copy, Clone, Eq, PartialEq, Ord, PartialOrd)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
#[repr(u8)]
pub enum Priority {
P0 = 0x0,
P1 = 0x40,
P2 = 0x80,
P3 = 0xc0,
}
#[cfg(feature = "prio-bits-3")]
#[derive(Debug, Copy, Clone, Eq, PartialEq, Ord, PartialOrd)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
#[repr(u8)]
pub enum Priority {
P0 = 0x0,
P1 = 0x20,
P2 = 0x40,
P3 = 0x60,
P4 = 0x80,
P5 = 0xa0,
P6 = 0xc0,
P7 = 0xe0,
}
#[cfg(feature = "prio-bits-4")]
#[derive(Debug, Copy, Clone, Eq, PartialEq, Ord, PartialOrd)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
#[repr(u8)]
pub enum Priority {
P0 = 0x0,
P1 = 0x10,
P2 = 0x20,
P3 = 0x30,
P4 = 0x40,
P5 = 0x50,
P6 = 0x60,
P7 = 0x70,
P8 = 0x80,
P9 = 0x90,
P10 = 0xa0,
P11 = 0xb0,
P12 = 0xc0,
P13 = 0xd0,
P14 = 0xe0,
P15 = 0xf0,
}
#[cfg(feature = "prio-bits-5")]
#[derive(Debug, Copy, Clone, Eq, PartialEq, Ord, PartialOrd)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
#[repr(u8)]
pub enum Priority {
P0 = 0x0,
P1 = 0x8,
P2 = 0x10,
P3 = 0x18,
P4 = 0x20,
P5 = 0x28,
P6 = 0x30,
P7 = 0x38,
P8 = 0x40,
P9 = 0x48,
P10 = 0x50,
P11 = 0x58,
P12 = 0x60,
P13 = 0x68,
P14 = 0x70,
P15 = 0x78,
P16 = 0x80,
P17 = 0x88,
P18 = 0x90,
P19 = 0x98,
P20 = 0xa0,
P21 = 0xa8,
P22 = 0xb0,
P23 = 0xb8,
P24 = 0xc0,
P25 = 0xc8,
P26 = 0xd0,
P27 = 0xd8,
P28 = 0xe0,
P29 = 0xe8,
P30 = 0xf0,
P31 = 0xf8,
}
#[cfg(feature = "prio-bits-6")]
#[derive(Debug, Copy, Clone, Eq, PartialEq, Ord, PartialOrd)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
#[repr(u8)]
pub enum Priority {
P0 = 0x0,
P1 = 0x4,
P2 = 0x8,
P3 = 0xc,
P4 = 0x10,
P5 = 0x14,
P6 = 0x18,
P7 = 0x1c,
P8 = 0x20,
P9 = 0x24,
P10 = 0x28,
P11 = 0x2c,
P12 = 0x30,
P13 = 0x34,
P14 = 0x38,
P15 = 0x3c,
P16 = 0x40,
P17 = 0x44,
P18 = 0x48,
P19 = 0x4c,
P20 = 0x50,
P21 = 0x54,
P22 = 0x58,
P23 = 0x5c,
P24 = 0x60,
P25 = 0x64,
P26 = 0x68,
P27 = 0x6c,
P28 = 0x70,
P29 = 0x74,
P30 = 0x78,
P31 = 0x7c,
P32 = 0x80,
P33 = 0x84,
P34 = 0x88,
P35 = 0x8c,
P36 = 0x90,
P37 = 0x94,
P38 = 0x98,
P39 = 0x9c,
P40 = 0xa0,
P41 = 0xa4,
P42 = 0xa8,
P43 = 0xac,
P44 = 0xb0,
P45 = 0xb4,
P46 = 0xb8,
P47 = 0xbc,
P48 = 0xc0,
P49 = 0xc4,
P50 = 0xc8,
P51 = 0xcc,
P52 = 0xd0,
P53 = 0xd4,
P54 = 0xd8,
P55 = 0xdc,
P56 = 0xe0,
P57 = 0xe4,
P58 = 0xe8,
P59 = 0xec,
P60 = 0xf0,
P61 = 0xf4,
P62 = 0xf8,
P63 = 0xfc,
}
#[cfg(feature = "prio-bits-7")]
#[derive(Debug, Copy, Clone, Eq, PartialEq, Ord, PartialOrd)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
#[repr(u8)]
pub enum Priority {
P0 = 0x0,
P1 = 0x2,
P2 = 0x4,
P3 = 0x6,
P4 = 0x8,
P5 = 0xa,
P6 = 0xc,
P7 = 0xe,
P8 = 0x10,
P9 = 0x12,
P10 = 0x14,
P11 = 0x16,
P12 = 0x18,
P13 = 0x1a,
P14 = 0x1c,
P15 = 0x1e,
P16 = 0x20,
P17 = 0x22,
P18 = 0x24,
P19 = 0x26,
P20 = 0x28,
P21 = 0x2a,
P22 = 0x2c,
P23 = 0x2e,
P24 = 0x30,
P25 = 0x32,
P26 = 0x34,
P27 = 0x36,
P28 = 0x38,
P29 = 0x3a,
P30 = 0x3c,
P31 = 0x3e,
P32 = 0x40,
P33 = 0x42,
P34 = 0x44,
P35 = 0x46,
P36 = 0x48,
P37 = 0x4a,
P38 = 0x4c,
P39 = 0x4e,
P40 = 0x50,
P41 = 0x52,
P42 = 0x54,
P43 = 0x56,
P44 = 0x58,
P45 = 0x5a,
P46 = 0x5c,
P47 = 0x5e,
P48 = 0x60,
P49 = 0x62,
P50 = 0x64,
P51 = 0x66,
P52 = 0x68,
P53 = 0x6a,
P54 = 0x6c,
P55 = 0x6e,
P56 = 0x70,
P57 = 0x72,
P58 = 0x74,
P59 = 0x76,
P60 = 0x78,
P61 = 0x7a,
P62 = 0x7c,
P63 = 0x7e,
P64 = 0x80,
P65 = 0x82,
P66 = 0x84,
P67 = 0x86,
P68 = 0x88,
P69 = 0x8a,
P70 = 0x8c,
P71 = 0x8e,
P72 = 0x90,
P73 = 0x92,
P74 = 0x94,
P75 = 0x96,
P76 = 0x98,
P77 = 0x9a,
P78 = 0x9c,
P79 = 0x9e,
P80 = 0xa0,
P81 = 0xa2,
P82 = 0xa4,
P83 = 0xa6,
P84 = 0xa8,
P85 = 0xaa,
P86 = 0xac,
P87 = 0xae,
P88 = 0xb0,
P89 = 0xb2,
P90 = 0xb4,
P91 = 0xb6,
P92 = 0xb8,
P93 = 0xba,
P94 = 0xbc,
P95 = 0xbe,
P96 = 0xc0,
P97 = 0xc2,
P98 = 0xc4,
P99 = 0xc6,
P100 = 0xc8,
P101 = 0xca,
P102 = 0xcc,
P103 = 0xce,
P104 = 0xd0,
P105 = 0xd2,
P106 = 0xd4,
P107 = 0xd6,
P108 = 0xd8,
P109 = 0xda,
P110 = 0xdc,
P111 = 0xde,
P112 = 0xe0,
P113 = 0xe2,
P114 = 0xe4,
P115 = 0xe6,
P116 = 0xe8,
P117 = 0xea,
P118 = 0xec,
P119 = 0xee,
P120 = 0xf0,
P121 = 0xf2,
P122 = 0xf4,
P123 = 0xf6,
P124 = 0xf8,
P125 = 0xfa,
P126 = 0xfc,
P127 = 0xfe,
}
#[cfg(feature = "prio-bits-8")]
#[derive(Debug, Copy, Clone, Eq, PartialEq, Ord, PartialOrd)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
#[repr(u8)]
pub enum Priority {
P0 = 0x0,
P1 = 0x1,
P2 = 0x2,
P3 = 0x3,
P4 = 0x4,
P5 = 0x5,
P6 = 0x6,
P7 = 0x7,
P8 = 0x8,
P9 = 0x9,
P10 = 0xa,
P11 = 0xb,
P12 = 0xc,
P13 = 0xd,
P14 = 0xe,
P15 = 0xf,
P16 = 0x10,
P17 = 0x11,
P18 = 0x12,
P19 = 0x13,
P20 = 0x14,
P21 = 0x15,
P22 = 0x16,
P23 = 0x17,
P24 = 0x18,
P25 = 0x19,
P26 = 0x1a,
P27 = 0x1b,
P28 = 0x1c,
P29 = 0x1d,
P30 = 0x1e,
P31 = 0x1f,
P32 = 0x20,
P33 = 0x21,
P34 = 0x22,
P35 = 0x23,
P36 = 0x24,
P37 = 0x25,
P38 = 0x26,
P39 = 0x27,
P40 = 0x28,
P41 = 0x29,
P42 = 0x2a,
P43 = 0x2b,
P44 = 0x2c,
P45 = 0x2d,
P46 = 0x2e,
P47 = 0x2f,
P48 = 0x30,
P49 = 0x31,
P50 = 0x32,
P51 = 0x33,
P52 = 0x34,
P53 = 0x35,
P54 = 0x36,
P55 = 0x37,
P56 = 0x38,
P57 = 0x39,
P58 = 0x3a,
P59 = 0x3b,
P60 = 0x3c,
P61 = 0x3d,
P62 = 0x3e,
P63 = 0x3f,
P64 = 0x40,
P65 = 0x41,
P66 = 0x42,
P67 = 0x43,
P68 = 0x44,
P69 = 0x45,
P70 = 0x46,
P71 = 0x47,
P72 = 0x48,
P73 = 0x49,
P74 = 0x4a,
P75 = 0x4b,
P76 = 0x4c,
P77 = 0x4d,
P78 = 0x4e,
P79 = 0x4f,
P80 = 0x50,
P81 = 0x51,
P82 = 0x52,
P83 = 0x53,
P84 = 0x54,
P85 = 0x55,
P86 = 0x56,
P87 = 0x57,
P88 = 0x58,
P89 = 0x59,
P90 = 0x5a,
P91 = 0x5b,
P92 = 0x5c,
P93 = 0x5d,
P94 = 0x5e,
P95 = 0x5f,
P96 = 0x60,
P97 = 0x61,
P98 = 0x62,
P99 = 0x63,
P100 = 0x64,
P101 = 0x65,
P102 = 0x66,
P103 = 0x67,
P104 = 0x68,
P105 = 0x69,
P106 = 0x6a,
P107 = 0x6b,
P108 = 0x6c,
P109 = 0x6d,
P110 = 0x6e,
P111 = 0x6f,
P112 = 0x70,
P113 = 0x71,
P114 = 0x72,
P115 = 0x73,
P116 = 0x74,
P117 = 0x75,
P118 = 0x76,
P119 = 0x77,
P120 = 0x78,
P121 = 0x79,
P122 = 0x7a,
P123 = 0x7b,
P124 = 0x7c,
P125 = 0x7d,
P126 = 0x7e,
P127 = 0x7f,
P128 = 0x80,
P129 = 0x81,
P130 = 0x82,
P131 = 0x83,
P132 = 0x84,
P133 = 0x85,
P134 = 0x86,
P135 = 0x87,
P136 = 0x88,
P137 = 0x89,
P138 = 0x8a,
P139 = 0x8b,
P140 = 0x8c,
P141 = 0x8d,
P142 = 0x8e,
P143 = 0x8f,
P144 = 0x90,
P145 = 0x91,
P146 = 0x92,
P147 = 0x93,
P148 = 0x94,
P149 = 0x95,
P150 = 0x96,
P151 = 0x97,
P152 = 0x98,
P153 = 0x99,
P154 = 0x9a,
P155 = 0x9b,
P156 = 0x9c,
P157 = 0x9d,
P158 = 0x9e,
P159 = 0x9f,
P160 = 0xa0,
P161 = 0xa1,
P162 = 0xa2,
P163 = 0xa3,
P164 = 0xa4,
P165 = 0xa5,
P166 = 0xa6,
P167 = 0xa7,
P168 = 0xa8,
P169 = 0xa9,
P170 = 0xaa,
P171 = 0xab,
P172 = 0xac,
P173 = 0xad,
P174 = 0xae,
P175 = 0xaf,
P176 = 0xb0,
P177 = 0xb1,
P178 = 0xb2,
P179 = 0xb3,
P180 = 0xb4,
P181 = 0xb5,
P182 = 0xb6,
P183 = 0xb7,
P184 = 0xb8,
P185 = 0xb9,
P186 = 0xba,
P187 = 0xbb,
P188 = 0xbc,
P189 = 0xbd,
P190 = 0xbe,
P191 = 0xbf,
P192 = 0xc0,
P193 = 0xc1,
P194 = 0xc2,
P195 = 0xc3,
P196 = 0xc4,
P197 = 0xc5,
P198 = 0xc6,
P199 = 0xc7,
P200 = 0xc8,
P201 = 0xc9,
P202 = 0xca,
P203 = 0xcb,
P204 = 0xcc,
P205 = 0xcd,
P206 = 0xce,
P207 = 0xcf,
P208 = 0xd0,
P209 = 0xd1,
P210 = 0xd2,
P211 = 0xd3,
P212 = 0xd4,
P213 = 0xd5,
P214 = 0xd6,
P215 = 0xd7,
P216 = 0xd8,
P217 = 0xd9,
P218 = 0xda,
P219 = 0xdb,
P220 = 0xdc,
P221 = 0xdd,
P222 = 0xde,
P223 = 0xdf,
P224 = 0xe0,
P225 = 0xe1,
P226 = 0xe2,
P227 = 0xe3,
P228 = 0xe4,
P229 = 0xe5,
P230 = 0xe6,
P231 = 0xe7,
P232 = 0xe8,
P233 = 0xe9,
P234 = 0xea,
P235 = 0xeb,
P236 = 0xec,
P237 = 0xed,
P238 = 0xee,
P239 = 0xef,
P240 = 0xf0,
P241 = 0xf1,
P242 = 0xf2,
P243 = 0xf3,
P244 = 0xf4,
P245 = 0xf5,
P246 = 0xf6,
P247 = 0xf7,
P248 = 0xf8,
P249 = 0xf9,
P250 = 0xfa,
P251 = 0xfb,
P252 = 0xfc,
P253 = 0xfd,
P254 = 0xfe,
P255 = 0xff,
}

View File

@ -0,0 +1,8 @@
#![no_std]
// This mod MUST go first, so that the others see its macros.
pub(crate) mod fmt;
pub mod executor;
pub mod interrupt;
pub mod peripheral;

View File

@ -0,0 +1,134 @@
use core::marker::PhantomData;
use core::mem::MaybeUninit;
use cortex_m::peripheral::scb::VectActive;
use cortex_m::peripheral::{NVIC, SCB};
use crate::interrupt::{Interrupt, InterruptExt, Priority};
/// A type which can be used as state with `PeripheralMutex`.
///
/// It needs to be `Send` because `&mut` references are sent back and forth between the 'thread' which owns the `PeripheralMutex` and the interrupt,
/// and `&mut T` is only `Send` where `T: Send`.
pub trait PeripheralState: Send {
type Interrupt: Interrupt;
fn on_interrupt(&mut self);
}
pub struct StateStorage<S>(MaybeUninit<S>);
impl<S> StateStorage<S> {
pub const fn new() -> Self {
Self(MaybeUninit::uninit())
}
}
pub struct PeripheralMutex<'a, S: PeripheralState> {
state: *mut S,
_phantom: PhantomData<&'a mut S>,
irq: S::Interrupt,
}
/// Whether `irq` can be preempted by the current interrupt.
pub(crate) fn can_be_preempted(irq: &impl Interrupt) -> bool {
match SCB::vect_active() {
// Thread mode can't preempt anything.
VectActive::ThreadMode => false,
// Exceptions don't always preempt interrupts,
// but there isn't much of a good reason to be keeping a `PeripheralMutex` in an exception anyway.
VectActive::Exception(_) => true,
VectActive::Interrupt { irqn } => {
#[derive(Clone, Copy)]
struct NrWrap(u16);
unsafe impl cortex_m::interrupt::InterruptNumber for NrWrap {
fn number(self) -> u16 {
self.0
}
}
NVIC::get_priority(NrWrap(irqn.into())) < irq.get_priority().into()
}
}
}
impl<'a, S: PeripheralState> PeripheralMutex<'a, S> {
/// Create a new `PeripheralMutex` wrapping `irq`, with `init` initializing the initial state.
///
/// Registers `on_interrupt` as the `irq`'s handler, and enables it.
pub fn new(
irq: S::Interrupt,
storage: &'a mut StateStorage<S>,
init: impl FnOnce() -> S,
) -> Self {
if can_be_preempted(&irq) {
panic!("`PeripheralMutex` cannot be created in an interrupt with higher priority than the interrupt it wraps");
}
let state_ptr = storage.0.as_mut_ptr();
// Safety: The pointer is valid and not used by anyone else
// because we have the `&mut StateStorage`.
unsafe { state_ptr.write(init()) };
irq.disable();
irq.set_handler(|p| unsafe {
// Safety: it's OK to get a &mut to the state, since
// - We checked that the thread owning the `PeripheralMutex` can't preempt us in `new`.
// Interrupts' priorities can only be changed with raw embassy `Interrupts`,
// which can't safely store a `PeripheralMutex` across invocations.
// - We can't have preempted a with() call because the irq is disabled during it.
let state = &mut *(p as *mut S);
state.on_interrupt();
});
irq.set_handler_context(state_ptr as *mut ());
irq.enable();
Self {
irq,
state: state_ptr,
_phantom: PhantomData,
}
}
pub fn with<R>(&mut self, f: impl FnOnce(&mut S) -> R) -> R {
self.irq.disable();
// Safety: it's OK to get a &mut to the state, since the irq is disabled.
let state = unsafe { &mut *self.state };
let r = f(state);
self.irq.enable();
r
}
/// Returns whether the wrapped interrupt is currently in a pending state.
pub fn is_pending(&self) -> bool {
self.irq.is_pending()
}
/// Forces the wrapped interrupt into a pending state.
pub fn pend(&self) {
self.irq.pend()
}
/// Forces the wrapped interrupt out of a pending state.
pub fn unpend(&self) {
self.irq.unpend()
}
/// Gets the priority of the wrapped interrupt.
pub fn priority(&self) -> Priority {
self.irq.get_priority()
}
}
impl<'a, S: PeripheralState> Drop for PeripheralMutex<'a, S> {
fn drop(&mut self) {
self.irq.disable();
self.irq.remove_handler();
// safety:
// - we initialized the state in `new`, so we know it's initialized.
// - the irq is disabled, so it won't preempt us while dropping.
unsafe { self.state.drop_in_place() }
}
}