From 036e6ae30c9e772ef8ef20439f121e108b9106f0 Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Fri, 28 Jul 2023 13:23:22 +0200 Subject: Rename embassy-hal-common to embassy-hal-internal, document it's for internal use only. (#1700) --- embassy-hal-internal/Cargo.toml | 29 + embassy-hal-internal/README.md | 16 + embassy-hal-internal/build.rs | 29 + embassy-hal-internal/src/atomic_ring_buffer.rs | 556 ++++++++++++++++ embassy-hal-internal/src/drop.rs | 51 ++ embassy-hal-internal/src/fmt.rs | 225 +++++++ embassy-hal-internal/src/interrupt.rs | 846 +++++++++++++++++++++++++ embassy-hal-internal/src/lib.rs | 17 + embassy-hal-internal/src/macros.rs | 123 ++++ embassy-hal-internal/src/peripheral.rs | 174 +++++ embassy-hal-internal/src/ratio.rs | 129 ++++ embassy-hal-internal/src/ring_buffer.rs | 136 ++++ 12 files changed, 2331 insertions(+) create mode 100644 embassy-hal-internal/Cargo.toml create mode 100644 embassy-hal-internal/README.md create mode 100644 embassy-hal-internal/build.rs create mode 100644 embassy-hal-internal/src/atomic_ring_buffer.rs create mode 100644 embassy-hal-internal/src/drop.rs create mode 100644 embassy-hal-internal/src/fmt.rs create mode 100644 embassy-hal-internal/src/interrupt.rs create mode 100644 embassy-hal-internal/src/lib.rs create mode 100644 embassy-hal-internal/src/macros.rs create mode 100644 embassy-hal-internal/src/peripheral.rs create mode 100644 embassy-hal-internal/src/ratio.rs create mode 100644 embassy-hal-internal/src/ring_buffer.rs (limited to 'embassy-hal-internal') diff --git a/embassy-hal-internal/Cargo.toml b/embassy-hal-internal/Cargo.toml new file mode 100644 index 000000000..42e03199c --- /dev/null +++ b/embassy-hal-internal/Cargo.toml @@ -0,0 +1,29 @@ +[package] +name = "embassy-hal-internal" +version = "0.1.0" +edition = "2021" +license = "MIT OR Apache-2.0" + +[features] + +# 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 = [] + +cortex-m = ["dep:cortex-m", "dep:critical-section"] + +[dependencies] +defmt = { version = "0.3", optional = true } +log = { version = "0.4.14", optional = true } + +num-traits = { version = "0.2.14", default-features = false } + +cortex-m = { version = "0.7.6", optional = true } +critical-section = { version = "1", optional = true } \ No newline at end of file diff --git a/embassy-hal-internal/README.md b/embassy-hal-internal/README.md new file mode 100644 index 000000000..d6539701b --- /dev/null +++ b/embassy-hal-internal/README.md @@ -0,0 +1,16 @@ +# embassy-macros + +An [Embassy](https://embassy.dev) project. + +Internal implementation details for Embassy HALs. DO NOT USE DIRECTLY. Embassy HALs (`embassy-nrf`, `embassy-stm32`, `embassy-rp`) already reexport +everything you need to use them effectively. + +## License + +This work is licensed under either of + +- Apache License, Version 2.0 ([LICENSE-APACHE](LICENSE-APACHE) or + ) +- MIT license ([LICENSE-MIT](LICENSE-MIT) or ) + +at your option. diff --git a/embassy-hal-internal/build.rs b/embassy-hal-internal/build.rs new file mode 100644 index 000000000..6fe82b44f --- /dev/null +++ b/embassy-hal-internal/build.rs @@ -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"); + } +} diff --git a/embassy-hal-internal/src/atomic_ring_buffer.rs b/embassy-hal-internal/src/atomic_ring_buffer.rs new file mode 100644 index 000000000..ea84925c4 --- /dev/null +++ b/embassy-hal-internal/src/atomic_ring_buffer.rs @@ -0,0 +1,556 @@ +use core::slice; +use core::sync::atomic::{AtomicPtr, AtomicUsize, Ordering}; + +/// Atomic reusable ringbuffer +/// +/// This ringbuffer implementation is designed to be stored in a `static`, +/// therefore all methods take `&self` and not `&mut self`. +/// +/// It is "reusable": when created it has no backing buffer, you can give it +/// one with `init` and take it back with `deinit`, and init it again in the +/// future if needed. This is very non-idiomatic, but helps a lot when storing +/// it in a `static`. +/// +/// One concurrent writer and one concurrent reader are supported, even at +/// different execution priorities (like main and irq). +pub struct RingBuffer { + pub buf: AtomicPtr, + pub len: AtomicUsize, + + // start and end wrap at len*2, not at len. + // This allows distinguishing "full" and "empty". + // full is when start+len == end (modulo len*2) + // empty is when start == end + // + // This avoids having to consider the ringbuffer "full" at len-1 instead of len. + // The usual solution is adding a "full" flag, but that can't be made atomic + pub start: AtomicUsize, + pub end: AtomicUsize, +} + +pub struct Reader<'a>(&'a RingBuffer); +pub struct Writer<'a>(&'a RingBuffer); + +impl RingBuffer { + /// Create a new empty ringbuffer. + pub const fn new() -> Self { + Self { + buf: AtomicPtr::new(core::ptr::null_mut()), + len: AtomicUsize::new(0), + start: AtomicUsize::new(0), + end: AtomicUsize::new(0), + } + } + + /// Initialize the ring buffer with a buffer. + /// + /// # Safety + /// - The buffer (`buf .. buf+len`) must be valid memory until `deinit` is called. + /// - Must not be called concurrently with any other methods. + pub unsafe fn init(&self, buf: *mut u8, len: usize) { + // Ordering: it's OK to use `Relaxed` because this is not called + // concurrently with other methods. + self.buf.store(buf, Ordering::Relaxed); + self.len.store(len, Ordering::Relaxed); + self.start.store(0, Ordering::Relaxed); + self.end.store(0, Ordering::Relaxed); + } + + /// Deinitialize the ringbuffer. + /// + /// After calling this, the ringbuffer becomes empty, as if it was + /// just created with `new()`. + /// + /// # Safety + /// - Must not be called concurrently with any other methods. + pub unsafe fn deinit(&self) { + // Ordering: it's OK to use `Relaxed` because this is not called + // concurrently with other methods. + self.len.store(0, Ordering::Relaxed); + self.start.store(0, Ordering::Relaxed); + self.end.store(0, Ordering::Relaxed); + } + + /// Create a reader. + /// + /// # Safety + /// + /// Only one reader can exist at a time. + pub unsafe fn reader(&self) -> Reader<'_> { + Reader(self) + } + + /// Create a writer. + /// + /// # Safety + /// + /// Only one writer can exist at a time. + pub unsafe fn writer(&self) -> Writer<'_> { + Writer(self) + } + + pub fn len(&self) -> usize { + self.len.load(Ordering::Relaxed) + } + + pub fn is_full(&self) -> bool { + let len = self.len.load(Ordering::Relaxed); + let start = self.start.load(Ordering::Relaxed); + let end = self.end.load(Ordering::Relaxed); + + self.wrap(start + len) == end + } + + pub fn is_empty(&self) -> bool { + let start = self.start.load(Ordering::Relaxed); + let end = self.end.load(Ordering::Relaxed); + + start == end + } + + fn wrap(&self, mut n: usize) -> usize { + let len = self.len.load(Ordering::Relaxed); + + if n >= len * 2 { + n -= len * 2 + } + n + } +} + +impl<'a> Writer<'a> { + /// Push data into the buffer in-place. + /// + /// The closure `f` is called with a free part of the buffer, it must write + /// some data to it and return the amount of bytes written. + pub fn push(&mut self, f: impl FnOnce(&mut [u8]) -> usize) -> usize { + let (p, n) = self.push_buf(); + let buf = unsafe { slice::from_raw_parts_mut(p, n) }; + let n = f(buf); + self.push_done(n); + n + } + + /// Push one data byte. + /// + /// Returns true if pushed successfully. + pub fn push_one(&mut self, val: u8) -> bool { + let n = self.push(|f| match f { + [] => 0, + [x, ..] => { + *x = val; + 1 + } + }); + n != 0 + } + + /// Get a buffer where data can be pushed to. + /// + /// Equivalent to [`Self::push_buf`] but returns a slice. + pub fn push_slice(&mut self) -> &mut [u8] { + let (data, len) = self.push_buf(); + unsafe { slice::from_raw_parts_mut(data, len) } + } + + /// Get up to two buffers where data can be pushed to. + /// + /// Equivalent to [`Self::push_bufs`] but returns slices. + pub fn push_slices(&mut self) -> [&mut [u8]; 2] { + let [(d0, l0), (d1, l1)] = self.push_bufs(); + unsafe { [slice::from_raw_parts_mut(d0, l0), slice::from_raw_parts_mut(d1, l1)] } + } + + /// Get a buffer where data can be pushed to. + /// + /// Write data to the start of the buffer, then call `push_done` with + /// however many bytes you've pushed. + /// + /// The buffer is suitable to DMA to. + /// + /// If the ringbuf is full, size=0 will be returned. + /// + /// The buffer stays valid as long as no other `Writer` method is called + /// and `init`/`deinit` aren't called on the ringbuf. + pub fn push_buf(&mut self) -> (*mut u8, usize) { + // Ordering: popping writes `start` last, so we read `start` first. + // Read it with Acquire ordering, so that the next accesses can't be reordered up past it. + let mut start = self.0.start.load(Ordering::Acquire); + let buf = self.0.buf.load(Ordering::Relaxed); + let len = self.0.len.load(Ordering::Relaxed); + let mut end = self.0.end.load(Ordering::Relaxed); + + let empty = start == end; + + if start >= len { + start -= len + } + if end >= len { + end -= len + } + + if start == end && !empty { + // full + return (buf, 0); + } + let n = if start > end { start - end } else { len - end }; + + trace!(" ringbuf: push_buf {:?}..{:?}", end, end + n); + (unsafe { buf.add(end) }, n) + } + + /// Get up to two buffers where data can be pushed to. + /// + /// Write data starting at the beginning of the first buffer, then call + /// `push_done` with however many bytes you've pushed. + /// + /// The buffers are suitable to DMA to. + /// + /// If the ringbuf is full, both buffers will be zero length. + /// If there is only area available, the second buffer will be zero length. + /// + /// The buffer stays valid as long as no other `Writer` method is called + /// and `init`/`deinit` aren't called on the ringbuf. + pub fn push_bufs(&mut self) -> [(*mut u8, usize); 2] { + // Ordering: as per push_buf() + let mut start = self.0.start.load(Ordering::Acquire); + let buf = self.0.buf.load(Ordering::Relaxed); + let len = self.0.len.load(Ordering::Relaxed); + let mut end = self.0.end.load(Ordering::Relaxed); + + let empty = start == end; + + if start >= len { + start -= len + } + if end >= len { + end -= len + } + + if start == end && !empty { + // full + return [(buf, 0), (buf, 0)]; + } + let n0 = if start > end { start - end } else { len - end }; + let n1 = if start <= end { start } else { 0 }; + + trace!(" ringbuf: push_bufs [{:?}..{:?}, {:?}..{:?}]", end, end + n0, 0, n1); + [(unsafe { buf.add(end) }, n0), (buf, n1)] + } + + pub fn push_done(&mut self, n: usize) { + trace!(" ringbuf: push {:?}", n); + let end = self.0.end.load(Ordering::Relaxed); + + // Ordering: write `end` last, with Release ordering. + // The ordering ensures no preceding memory accesses (such as writing + // the actual data in the buffer) can be reordered down past it, which + // will guarantee the reader sees them after reading from `end`. + self.0.end.store(self.0.wrap(end + n), Ordering::Release); + } +} + +impl<'a> Reader<'a> { + /// Pop data from the buffer in-place. + /// + /// The closure `f` is called with the next data, it must process + /// some data from it and return the amount of bytes processed. + pub fn pop(&mut self, f: impl FnOnce(&[u8]) -> usize) -> usize { + let (p, n) = self.pop_buf(); + let buf = unsafe { slice::from_raw_parts(p, n) }; + let n = f(buf); + self.pop_done(n); + n + } + + /// Pop one data byte. + /// + /// Returns true if popped successfully. + pub fn pop_one(&mut self) -> Option { + let mut res = None; + self.pop(|f| match f { + &[] => 0, + &[x, ..] => { + res = Some(x); + 1 + } + }); + res + } + + /// Get a buffer where data can be popped from. + /// + /// Equivalent to [`Self::pop_buf`] but returns a slice. + pub fn pop_slice(&mut self) -> &mut [u8] { + let (data, len) = self.pop_buf(); + unsafe { slice::from_raw_parts_mut(data, len) } + } + + /// Get a buffer where data can be popped from. + /// + /// Read data from the start of the buffer, then call `pop_done` with + /// however many bytes you've processed. + /// + /// The buffer is suitable to DMA from. + /// + /// If the ringbuf is empty, size=0 will be returned. + /// + /// The buffer stays valid as long as no other `Reader` method is called + /// and `init`/`deinit` aren't called on the ringbuf. + pub fn pop_buf(&mut self) -> (*mut u8, usize) { + // Ordering: pushing writes `end` last, so we read `end` first. + // Read it with Acquire ordering, so that the next accesses can't be reordered up past it. + // This is needed to guarantee we "see" the data written by the writer. + let mut end = self.0.end.load(Ordering::Acquire); + let buf = self.0.buf.load(Ordering::Relaxed); + let len = self.0.len.load(Ordering::Relaxed); + let mut start = self.0.start.load(Ordering::Relaxed); + + if start == end { + return (buf, 0); + } + + if start >= len { + start -= len + } + if end >= len { + end -= len + } + + let n = if end > start { end - start } else { len - start }; + + trace!(" ringbuf: pop_buf {:?}..{:?}", start, start + n); + (unsafe { buf.add(start) }, n) + } + + pub fn pop_done(&mut self, n: usize) { + trace!(" ringbuf: pop {:?}", n); + + let start = self.0.start.load(Ordering::Relaxed); + + // Ordering: write `start` last, with Release ordering. + // The ordering ensures no preceding memory accesses (such as reading + // the actual data) can be reordered down past it. This is necessary + // because writing to `start` is effectively freeing the read part of the + // buffer, which "gives permission" to the writer to write to it again. + // Therefore, all buffer accesses must be completed before this. + self.0.start.store(self.0.wrap(start + n), Ordering::Release); + } +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn push_pop() { + let mut b = [0; 4]; + let rb = RingBuffer::new(); + unsafe { + rb.init(b.as_mut_ptr(), 4); + + assert_eq!(rb.is_empty(), true); + assert_eq!(rb.is_full(), false); + + rb.writer().push(|buf| { + assert_eq!(4, buf.len()); + buf[0] = 1; + buf[1] = 2; + buf[2] = 3; + buf[3] = 4; + 4 + }); + + assert_eq!(rb.is_empty(), false); + assert_eq!(rb.is_full(), true); + + rb.writer().push(|buf| { + // If it's full, we can push 0 bytes. + assert_eq!(0, buf.len()); + 0 + }); + + assert_eq!(rb.is_empty(), false); + assert_eq!(rb.is_full(), true); + + rb.reader().pop(|buf| { + assert_eq!(4, buf.len()); + assert_eq!(1, buf[0]); + 1 + }); + + assert_eq!(rb.is_empty(), false); + assert_eq!(rb.is_full(), false); + + rb.reader().pop(|buf| { + assert_eq!(3, buf.len()); + 0 + }); + + assert_eq!(rb.is_empty(), false); + assert_eq!(rb.is_full(), false); + + rb.reader().pop(|buf| { + assert_eq!(3, buf.len()); + assert_eq!(2, buf[0]); + assert_eq!(3, buf[1]); + 2 + }); + rb.reader().pop(|buf| { + assert_eq!(1, buf.len()); + assert_eq!(4, buf[0]); + 1 + }); + + assert_eq!(rb.is_empty(), true); + assert_eq!(rb.is_full(), false); + + rb.reader().pop(|buf| { + assert_eq!(0, buf.len()); + 0 + }); + + rb.writer().push(|buf| { + assert_eq!(4, buf.len()); + buf[0] = 10; + 1 + }); + + rb.writer().push(|buf| { + assert_eq!(3, buf.len()); + buf[0] = 11; + buf[1] = 12; + 2 + }); + + assert_eq!(rb.is_empty(), false); + assert_eq!(rb.is_full(), false); + + rb.writer().push(|buf| { + assert_eq!(1, buf.len()); + buf[0] = 13; + 1 + }); + + assert_eq!(rb.is_empty(), false); + assert_eq!(rb.is_full(), true); + } + } + + #[test] + fn zero_len() { + let rb = RingBuffer::new(); + unsafe { + assert_eq!(rb.is_empty(), true); + assert_eq!(rb.is_full(), true); + + rb.writer().push(|buf| { + assert_eq!(0, buf.len()); + 0 + }); + + rb.reader().pop(|buf| { + assert_eq!(0, buf.len()); + 0 + }); + } + } + + #[test] + fn push_slices() { + let mut b = [0; 4]; + let rb = RingBuffer::new(); + unsafe { + rb.init(b.as_mut_ptr(), 4); + + /* push 3 -> [1 2 3 x] */ + let mut w = rb.writer(); + let ps = w.push_slices(); + assert_eq!(4, ps[0].len()); + assert_eq!(0, ps[1].len()); + ps[0][0] = 1; + ps[0][1] = 2; + ps[0][2] = 3; + w.push_done(3); + drop(w); + + /* pop 2 -> [x x 3 x] */ + rb.reader().pop(|buf| { + assert_eq!(3, buf.len()); + assert_eq!(1, buf[0]); + assert_eq!(2, buf[1]); + assert_eq!(3, buf[2]); + 2 + }); + + /* push 3 -> [5 6 3 4] */ + let mut w = rb.writer(); + let ps = w.push_slices(); + assert_eq!(1, ps[0].len()); + assert_eq!(2, ps[1].len()); + ps[0][0] = 4; + ps[1][0] = 5; + ps[1][1] = 6; + w.push_done(3); + drop(w); + + /* buf is now full */ + let mut w = rb.writer(); + let ps = w.push_slices(); + assert_eq!(0, ps[0].len()); + assert_eq!(0, ps[1].len()); + + /* pop 2 -> [5 6 x x] */ + rb.reader().pop(|buf| { + assert_eq!(2, buf.len()); + assert_eq!(3, buf[0]); + assert_eq!(4, buf[1]); + 2 + }); + + /* should now have one push slice again */ + let mut w = rb.writer(); + let ps = w.push_slices(); + assert_eq!(2, ps[0].len()); + assert_eq!(0, ps[1].len()); + drop(w); + + /* pop 2 -> [x x x x] */ + rb.reader().pop(|buf| { + assert_eq!(2, buf.len()); + assert_eq!(5, buf[0]); + assert_eq!(6, buf[1]); + 2 + }); + + /* should now have two push slices */ + let mut w = rb.writer(); + let ps = w.push_slices(); + assert_eq!(2, ps[0].len()); + assert_eq!(2, ps[1].len()); + drop(w); + + /* make sure we exercise all wrap around cases properly */ + for _ in 0..10 { + /* should be empty, push 1 */ + let mut w = rb.writer(); + let ps = w.push_slices(); + assert_eq!(4, ps[0].len() + ps[1].len()); + w.push_done(1); + drop(w); + + /* should have 1 element */ + let mut w = rb.writer(); + let ps = w.push_slices(); + assert_eq!(3, ps[0].len() + ps[1].len()); + drop(w); + + /* pop 1 */ + rb.reader().pop(|buf| { + assert_eq!(1, buf.len()); + 1 + }); + } + } + } +} diff --git a/embassy-hal-internal/src/drop.rs b/embassy-hal-internal/src/drop.rs new file mode 100644 index 000000000..7cd16aaec --- /dev/null +++ b/embassy-hal-internal/src/drop.rs @@ -0,0 +1,51 @@ +use core::mem; +use core::mem::MaybeUninit; + +#[must_use = "to delay the drop handler invokation to the end of the scope"] +pub struct OnDrop { + f: MaybeUninit, +} + +impl OnDrop { + pub fn new(f: F) -> Self { + Self { f: MaybeUninit::new(f) } + } + + pub fn defuse(self) { + mem::forget(self) + } +} + +impl Drop for OnDrop { + fn drop(&mut self) { + unsafe { self.f.as_ptr().read()() } + } +} + +/// An explosive ordinance that panics if it is improperly disposed of. +/// +/// This is to forbid dropping futures, when there is absolutely no other choice. +/// +/// To correctly dispose of this device, call the [defuse](struct.DropBomb.html#method.defuse) +/// method before this object is dropped. +#[must_use = "to delay the drop bomb invokation to the end of the scope"] +pub struct DropBomb { + _private: (), +} + +impl DropBomb { + pub fn new() -> Self { + Self { _private: () } + } + + /// Defuses the bomb, rendering it safe to drop. + pub fn defuse(self) { + mem::forget(self) + } +} + +impl Drop for DropBomb { + fn drop(&mut self) { + panic!("boom") + } +} diff --git a/embassy-hal-internal/src/fmt.rs b/embassy-hal-internal/src/fmt.rs new file mode 100644 index 000000000..066970813 --- /dev/null +++ b/embassy-hal-internal/src/fmt.rs @@ -0,0 +1,225 @@ +#![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); + } + } + } +} + +#[derive(Debug, Copy, Clone, Eq, PartialEq)] +pub struct NoneError; + +pub trait Try { + type Ok; + type Error; + fn into_result(self) -> Result; +} + +impl Try for Option { + type Ok = T; + type Error = NoneError; + + #[inline] + fn into_result(self) -> Result { + self.ok_or(NoneError) + } +} + +impl Try for Result { + type Ok = T; + type Error = E; + + #[inline] + fn into_result(self) -> Self { + self + } +} diff --git a/embassy-hal-internal/src/interrupt.rs b/embassy-hal-internal/src/interrupt.rs new file mode 100644 index 000000000..b970aa2cd --- /dev/null +++ b/embassy-hal-internal/src/interrupt.rs @@ -0,0 +1,846 @@ +//! Interrupt handling for cortex-m devices. +use core::mem; +use core::sync::atomic::{compiler_fence, Ordering}; + +use cortex_m::interrupt::InterruptNumber; +use cortex_m::peripheral::NVIC; + +/// Generate a standard `mod interrupt` for a HAL. +#[macro_export] +macro_rules! interrupt_mod { + ($($irqs:ident),* $(,)?) => { + #[cfg(feature = "rt")] + pub use cortex_m_rt::interrupt; + + /// Interrupt definitions. + pub mod interrupt { + pub use $crate::interrupt::{InterruptExt, Priority}; + pub use crate::pac::Interrupt::*; + pub use crate::pac::Interrupt; + + /// Type-level interrupt infrastructure. + /// + /// This module contains one *type* per interrupt. This is used for checking at compile time that + /// the interrupts are correctly bound to HAL drivers. + /// + /// As an end user, you shouldn't need to use this module directly. Use the [`crate::bind_interrupts!`] macro + /// to bind interrupts, and the [`crate::interrupt`] module to manually register interrupt handlers and manipulate + /// interrupts directly (pending/unpending, enabling/disabling, setting the priority, etc...) + pub mod typelevel { + use super::InterruptExt; + + mod sealed { + pub trait Interrupt {} + } + + /// Type-level interrupt. + /// + /// This trait is implemented for all typelevel interrupt types in this module. + pub trait Interrupt: sealed::Interrupt { + + /// Interrupt enum variant. + /// + /// This allows going from typelevel interrupts (one type per interrupt) to + /// non-typelevel interrupts (a single `Interrupt` enum type, with one variant per interrupt). + const IRQ: super::Interrupt; + + /// Enable the interrupt. + #[inline] + unsafe fn enable() { + Self::IRQ.enable() + } + + /// Disable the interrupt. + #[inline] + fn disable() { + Self::IRQ.disable() + } + + /// Check if interrupt is enabled. + #[inline] + fn is_enabled() -> bool { + Self::IRQ.is_enabled() + } + + /// Check if interrupt is pending. + #[inline] + fn is_pending() -> bool { + Self::IRQ.is_pending() + } + + /// Set interrupt pending. + #[inline] + fn pend() { + Self::IRQ.pend() + } + + /// Unset interrupt pending. + #[inline] + fn unpend() { + Self::IRQ.unpend() + } + + /// Get the priority of the interrupt. + #[inline] + fn get_priority() -> crate::interrupt::Priority { + Self::IRQ.get_priority() + } + + /// Set the interrupt priority. + #[inline] + fn set_priority(prio: crate::interrupt::Priority) { + Self::IRQ.set_priority(prio) + } + } + + $( + #[allow(non_camel_case_types)] + #[doc=stringify!($irqs)] + #[doc=" typelevel interrupt."] + pub enum $irqs {} + impl sealed::Interrupt for $irqs{} + impl Interrupt for $irqs { + const IRQ: super::Interrupt = super::Interrupt::$irqs; + } + )* + + /// Interrupt handler trait. + /// + /// Drivers that need to handle interrupts implement this trait. + /// The user must ensure `on_interrupt()` is called every time the interrupt fires. + /// Drivers must use use [`Binding`] to assert at compile time that the user has done so. + pub trait Handler { + /// Interrupt handler function. + /// + /// Must be called every time the `I` interrupt fires, synchronously from + /// the interrupt handler context. + /// + /// # Safety + /// + /// This function must ONLY be called from the interrupt handler for `I`. + unsafe fn on_interrupt(); + } + + /// Compile-time assertion that an interrupt has been bound to a handler. + /// + /// For the vast majority of cases, you should use the `bind_interrupts!` + /// macro instead of writing `unsafe impl`s of this trait. + /// + /// # Safety + /// + /// By implementing this trait, you are asserting that you have arranged for `H::on_interrupt()` + /// to be called every time the `I` interrupt fires. + /// + /// This allows drivers to check bindings at compile-time. + pub unsafe trait Binding> {} + } + } + }; +} + +/// Represents an interrupt type that can be configured by embassy to handle +/// interrupts. +pub unsafe trait InterruptExt: InterruptNumber + Copy { + /// Enable the interrupt. + #[inline] + unsafe fn enable(self) { + compiler_fence(Ordering::SeqCst); + NVIC::unmask(self) + } + + /// Disable the interrupt. + #[inline] + fn disable(self) { + NVIC::mask(self); + compiler_fence(Ordering::SeqCst); + } + + /// Check if interrupt is being handled. + #[inline] + #[cfg(not(armv6m))] + fn is_active(self) -> bool { + NVIC::is_active(self) + } + + /// Check if interrupt is enabled. + #[inline] + fn is_enabled(self) -> bool { + NVIC::is_enabled(self) + } + + /// Check if interrupt is pending. + #[inline] + fn is_pending(self) -> bool { + NVIC::is_pending(self) + } + + /// Set interrupt pending. + #[inline] + fn pend(self) { + NVIC::pend(self) + } + + /// Unset interrupt pending. + #[inline] + fn unpend(self) { + NVIC::unpend(self) + } + + /// Get the priority of the interrupt. + #[inline] + fn get_priority(self) -> Priority { + Priority::from(NVIC::get_priority(self)) + } + + /// Set the interrupt priority. + #[inline] + fn set_priority(self, prio: Priority) { + critical_section::with(|_| unsafe { + let mut nvic: cortex_m::peripheral::NVIC = mem::transmute(()); + nvic.set_priority(self, prio.into()) + }) + } +} + +unsafe impl InterruptExt for T {} + +impl From for Priority { + fn from(priority: u8) -> Self { + unsafe { mem::transmute(priority & PRIO_MASK) } + } +} + +impl From 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; + +/// The interrupt priority level. +/// +/// NOTE: The contents of this enum differ according to the set `prio-bits-*` Cargo feature. +#[cfg(feature = "prio-bits-0")] +#[derive(Debug, Copy, Clone, Eq, PartialEq, Ord, PartialOrd)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] +#[repr(u8)] +#[allow(missing_docs)] +pub enum Priority { + P0 = 0x0, +} + +/// The interrupt priority level. +/// +/// NOTE: The contents of this enum differ according to the set `prio-bits-*` Cargo feature. +#[cfg(feature = "prio-bits-1")] +#[derive(Debug, Copy, Clone, Eq, PartialEq, Ord, PartialOrd)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] +#[repr(u8)] +#[allow(missing_docs)] +pub enum Priority { + P0 = 0x0, + P1 = 0x80, +} + +/// The interrupt priority level. +/// +/// NOTE: The contents of this enum differ according to the set `prio-bits-*` Cargo feature. +#[cfg(feature = "prio-bits-2")] +#[derive(Debug, Copy, Clone, Eq, PartialEq, Ord, PartialOrd)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] +#[repr(u8)] +#[allow(missing_docs)] +pub enum Priority { + P0 = 0x0, + P1 = 0x40, + P2 = 0x80, + P3 = 0xc0, +} + +/// The interrupt priority level. +/// +/// NOTE: The contents of this enum differ according to the set `prio-bits-*` Cargo feature. +#[cfg(feature = "prio-bits-3")] +#[derive(Debug, Copy, Clone, Eq, PartialEq, Ord, PartialOrd)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] +#[repr(u8)] +#[allow(missing_docs)] +pub enum Priority { + P0 = 0x0, + P1 = 0x20, + P2 = 0x40, + P3 = 0x60, + P4 = 0x80, + P5 = 0xa0, + P6 = 0xc0, + P7 = 0xe0, +} + +/// The interrupt priority level. +/// +/// NOTE: The contents of this enum differ according to the set `prio-bits-*` Cargo feature. +#[cfg(feature = "prio-bits-4")] +#[derive(Debug, Copy, Clone, Eq, PartialEq, Ord, PartialOrd)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] +#[repr(u8)] +#[allow(missing_docs)] +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, +} + +/// The interrupt priority level. +/// +/// NOTE: The contents of this enum differ according to the set `prio-bits-*` Cargo feature. +#[cfg(feature = "prio-bits-5")] +#[derive(Debug, Copy, Clone, Eq, PartialEq, Ord, PartialOrd)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] +#[repr(u8)] +#[allow(missing_docs)] +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, +} + +/// The interrupt priority level. +/// +/// NOTE: The contents of this enum differ according to the set `prio-bits-*` Cargo feature. +#[cfg(feature = "prio-bits-6")] +#[derive(Debug, Copy, Clone, Eq, PartialEq, Ord, PartialOrd)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] +#[repr(u8)] +#[allow(missing_docs)] +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, +} + +/// The interrupt priority level. +/// +/// NOTE: The contents of this enum differ according to the set `prio-bits-*` Cargo feature. +#[cfg(feature = "prio-bits-7")] +#[derive(Debug, Copy, Clone, Eq, PartialEq, Ord, PartialOrd)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] +#[repr(u8)] +#[allow(missing_docs)] +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, +} + +/// The interrupt priority level. +/// +/// NOTE: The contents of this enum differ according to the set `prio-bits-*` Cargo feature. +#[cfg(feature = "prio-bits-8")] +#[derive(Debug, Copy, Clone, Eq, PartialEq, Ord, PartialOrd)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] +#[repr(u8)] +#[allow(missing_docs)] +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, +} diff --git a/embassy-hal-internal/src/lib.rs b/embassy-hal-internal/src/lib.rs new file mode 100644 index 000000000..3640ea184 --- /dev/null +++ b/embassy-hal-internal/src/lib.rs @@ -0,0 +1,17 @@ +#![no_std] +#![allow(clippy::new_without_default)] +#![doc = include_str!("../README.md")] + +// This mod MUST go first, so that the others see its macros. +pub(crate) mod fmt; + +pub mod atomic_ring_buffer; +pub mod drop; +mod macros; +mod peripheral; +pub mod ratio; +pub mod ring_buffer; +pub use peripheral::{Peripheral, PeripheralRef}; + +#[cfg(feature = "cortex-m")] +pub mod interrupt; diff --git a/embassy-hal-internal/src/macros.rs b/embassy-hal-internal/src/macros.rs new file mode 100644 index 000000000..f06b46002 --- /dev/null +++ b/embassy-hal-internal/src/macros.rs @@ -0,0 +1,123 @@ +#[macro_export] +macro_rules! peripherals_definition { + ($($(#[$cfg:meta])? $name:ident),*$(,)?) => { + /// Types for the peripheral singletons. + pub mod peripherals { + $( + $(#[$cfg])? + #[allow(non_camel_case_types)] + #[doc = concat!(stringify!($name), " peripheral")] + pub struct $name { _private: () } + + $(#[$cfg])? + impl $name { + /// 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{ _private: ()} + } + } + + $(#[$cfg])? + $crate::impl_peripheral!($name); + )* + } + }; +} + +#[macro_export] +macro_rules! peripherals_struct { + ($($(#[$cfg:meta])? $name:ident),*$(,)?) => { + /// Struct containing all the peripheral singletons. + /// + /// To obtain the peripherals, you must initialize the HAL, by calling [`crate::init`]. + #[allow(non_snake_case)] + pub struct Peripherals { + $( + #[doc = concat!(stringify!($name), " peripheral")] + $(#[$cfg])? + pub $name: peripherals::$name, + )* + } + + impl Peripherals { + ///Returns all the peripherals *once* + #[inline] + pub(crate) fn take() -> Self { + + #[no_mangle] + static mut _EMBASSY_DEVICE_PERIPHERALS: bool = false; + + critical_section::with(|_| unsafe { + if _EMBASSY_DEVICE_PERIPHERALS { + panic!("init called more than once!") + } + _EMBASSY_DEVICE_PERIPHERALS = true; + Self::steal() + }) + } + } + + impl Peripherals { + /// 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 { + $( + $(#[$cfg])? + $name: peripherals::$name::steal(), + )* + } + } + } + }; +} + +#[macro_export] +macro_rules! peripherals { + ($($(#[$cfg:meta])? $name:ident),*$(,)?) => { + $crate::peripherals_definition!( + $( + $(#[$cfg])? + $name, + )* + ); + $crate::peripherals_struct!( + $( + $(#[$cfg])? + $name, + )* + ); + }; +} + +#[macro_export] +macro_rules! into_ref { + ($($name:ident),*) => { + $( + let mut $name = $name.into_ref(); + )* + } +} + +#[macro_export] +macro_rules! impl_peripheral { + ($type:ident) => { + impl $crate::Peripheral for $type { + type P = $type; + + #[inline] + unsafe fn clone_unchecked(&self) -> Self::P { + $type { ..*self } + } + } + }; +} diff --git a/embassy-hal-internal/src/peripheral.rs b/embassy-hal-internal/src/peripheral.rs new file mode 100644 index 000000000..38b4c452e --- /dev/null +++ b/embassy-hal-internal/src/peripheral.rs @@ -0,0 +1,174 @@ +use core::marker::PhantomData; +use core::ops::{Deref, DerefMut}; + +/// An exclusive reference to a peripheral. +/// +/// This is functionally the same as a `&'a mut T`. There's a few advantages in having +/// a dedicated struct instead: +/// +/// - Memory efficiency: Peripheral singletons are typically either zero-sized (for concrete +/// 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 zero-sized. +/// PeripheralRef stores a copy of `T` instead, so it's the same size. +/// - Code size efficiency. If the user uses the same driver with both `SPI4` and `&mut SPI4`, +/// the driver code would be monomorphized two times. With PeripheralRef, the driver is generic +/// over a lifetime only. `SPI4` becomes `PeripheralRef<'static, SPI4>`, and `&mut SPI4` becomes +/// `PeripheralRef<'a, SPI4>`. Lifetimes don't cause monomorphization. +pub struct PeripheralRef<'a, T> { + inner: T, + _lifetime: PhantomData<&'a mut T>, +} + +impl<'a, T> PeripheralRef<'a, T> { + #[inline] + pub fn new(inner: T) -> Self { + Self { + inner, + _lifetime: PhantomData, + } + } + + /// Unsafely clone (duplicate) a peripheral singleton. + /// + /// # Safety + /// + /// This returns an owned clone of the peripheral. You must manually ensure + /// only one copy of the peripheral is in use at a time. For example, don't + /// create two SPI drivers on `SPI1`, because they will "fight" each other. + /// + /// You should strongly prefer using `reborrow()` instead. It returns a + /// `PeripheralRef` that borrows `self`, which allows the borrow checker + /// to enforce this at compile time. + pub unsafe fn clone_unchecked(&self) -> PeripheralRef<'a, T> + where + T: Peripheral

, + { + PeripheralRef::new(self.inner.clone_unchecked()) + } + + /// Reborrow into a "child" PeripheralRef. + /// + /// `self` will stay borrowed until the child PeripheralRef is dropped. + pub fn reborrow(&mut self) -> PeripheralRef<'_, T> + where + T: Peripheral

, + { + // safety: we're returning the clone inside a new PeripheralRef that borrows + // self, so user code can't use both at the same time. + PeripheralRef::new(unsafe { self.inner.clone_unchecked() }) + } + + /// Map the inner peripheral using `Into`. + /// + /// This converts from `PeripheralRef<'a, T>` to `PeripheralRef<'a, U>`, using an + /// `Into` impl to convert from `T` to `U`. + /// + /// For example, this can be useful to degrade GPIO pins: converting from PeripheralRef<'a, PB11>` to `PeripheralRef<'a, AnyPin>`. + #[inline] + pub fn map_into(self) -> PeripheralRef<'a, U> + where + T: Into, + { + PeripheralRef { + inner: self.inner.into(), + _lifetime: PhantomData, + } + } +} + +impl<'a, T> Deref for PeripheralRef<'a, T> { + type Target = T; + + #[inline] + fn deref(&self) -> &Self::Target { + &self.inner + } +} + +impl<'a, T> DerefMut for PeripheralRef<'a, T> { + #[inline] + fn deref_mut(&mut self) -> &mut Self::Target { + &mut self.inner + } +} + +/// Trait for any type that can be used as a peripheral of type `P`. +/// +/// This is used in driver constructors, to allow passing either owned peripherals (e.g. `TWISPI0`), +/// or borrowed peripherals (e.g. `&mut TWISPI0`). +/// +/// For example, if you have a driver with a constructor like this: +/// +/// ```ignore +/// impl<'d, T: Instance> Twim<'d, T> { +/// pub fn new( +/// twim: impl Peripheral

+ 'd, +/// irq: impl Peripheral

+ 'd, +/// sda: impl Peripheral

+ 'd, +/// scl: impl Peripheral

+ 'd, +/// config: Config, +/// ) -> Self { .. } +/// } +/// ``` +/// +/// You may call it with owned peripherals, which yields an instance that can live forever (`'static`): +/// +/// ```ignore +/// let mut twi: Twim<'static, ...> = Twim::new(p.TWISPI0, irq, p.P0_03, p.P0_04, config); +/// ``` +/// +/// Or you may call it with borrowed peripherals, which yields an instance that can only live for as long +/// as the borrows last: +/// +/// ```ignore +/// let mut twi: Twim<'_, ...> = Twim::new(&mut p.TWISPI0, &mut irq, &mut p.P0_03, &mut p.P0_04, config); +/// ``` +/// +/// # Implementation details, for HAL authors +/// +/// When writing a HAL, the intended way to use this trait is to take `impl Peripheral

` in +/// the HAL's public API (such as driver constructors), calling `.into_ref()` to obtain a `PeripheralRef`, +/// and storing that in the driver struct. +/// +/// `.into_ref()` on an owned `T` yields a `PeripheralRef<'static, T>`. +/// `.into_ref()` on an `&'a mut T` yields a `PeripheralRef<'a, T>`. +pub trait Peripheral: Sized { + /// Peripheral singleton type + type P; + + /// Unsafely clone (duplicate) a peripheral singleton. + /// + /// # Safety + /// + /// This returns an owned clone of the peripheral. You must manually ensure + /// only one copy of the peripheral is in use at a time. For example, don't + /// create two SPI drivers on `SPI1`, because they will "fight" each other. + /// + /// You should strongly prefer using `into_ref()` instead. It returns a + /// `PeripheralRef`, which allows the borrow checker to enforce this at compile time. + unsafe fn clone_unchecked(&self) -> Self::P; + + /// Convert a value into a `PeripheralRef`. + /// + /// When called on an owned `T`, yields a `PeripheralRef<'static, T>`. + /// When called on an `&'a mut T`, yields a `PeripheralRef<'a, T>`. + #[inline] + fn into_ref<'a>(self) -> PeripheralRef<'a, Self::P> + where + Self: 'a, + { + PeripheralRef::new(unsafe { self.clone_unchecked() }) + } +} + +impl<'b, T: DerefMut> Peripheral for T +where + T::Target: Peripheral, +{ + type P = ::P; + + #[inline] + unsafe fn clone_unchecked(&self) -> Self::P { + self.deref().clone_unchecked() + } +} diff --git a/embassy-hal-internal/src/ratio.rs b/embassy-hal-internal/src/ratio.rs new file mode 100644 index 000000000..9a8808a33 --- /dev/null +++ b/embassy-hal-internal/src/ratio.rs @@ -0,0 +1,129 @@ +use core::ops::{Add, Div, Mul}; + +use num_traits::{CheckedAdd, CheckedDiv, CheckedMul}; + +/// Represents the ratio between two numbers. +#[derive(Copy, Clone, Debug)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] +pub struct Ratio { + /// Numerator. + numer: T, + /// Denominator. + denom: T, +} + +impl Ratio { + /// Creates a new `Ratio`. + #[inline(always)] + pub const fn new_raw(numer: T, denom: T) -> Ratio { + Ratio { numer, denom } + } + + /// Gets an immutable reference to the numerator. + #[inline(always)] + pub const fn numer(&self) -> &T { + &self.numer + } + + /// Gets an immutable reference to the denominator. + #[inline(always)] + pub const fn denom(&self) -> &T { + &self.denom + } +} + +impl Ratio { + /// Converts to an integer, rounding towards zero. + #[inline(always)] + pub fn to_integer(&self) -> T { + unwrap!(self.numer().checked_div(self.denom())) + } +} + +impl Div for Ratio { + type Output = Self; + + #[inline(always)] + fn div(mut self, rhs: T) -> Self::Output { + self.denom = unwrap!(self.denom().checked_mul(&rhs)); + self + } +} + +impl Mul for Ratio { + type Output = Self; + + #[inline(always)] + fn mul(mut self, rhs: T) -> Self::Output { + self.numer = unwrap!(self.numer().checked_mul(&rhs)); + self + } +} + +impl Add for Ratio { + type Output = Self; + + #[inline(always)] + fn add(mut self, rhs: T) -> Self::Output { + self.numer = unwrap!(unwrap!(self.denom().checked_mul(&rhs)).checked_add(self.numer())); + self + } +} + +macro_rules! impl_from_for_float { + ($from:ident) => { + impl From> for f32 { + #[inline(always)] + fn from(r: Ratio<$from>) -> Self { + (r.numer as f32) / (r.denom as f32) + } + } + + impl From> for f64 { + #[inline(always)] + fn from(r: Ratio<$from>) -> Self { + (r.numer as f64) / (r.denom as f64) + } + } + }; +} + +impl_from_for_float!(u8); +impl_from_for_float!(u16); +impl_from_for_float!(u32); +impl_from_for_float!(u64); +impl_from_for_float!(u128); +impl_from_for_float!(i8); +impl_from_for_float!(i16); +impl_from_for_float!(i32); +impl_from_for_float!(i64); +impl_from_for_float!(i128); + +impl core::fmt::Display for Ratio { + fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { + core::write!(f, "{} / {}", self.numer(), self.denom()) + } +} + +#[cfg(test)] +mod tests { + use super::Ratio; + + #[test] + fn basics() { + let mut r = Ratio::new_raw(1, 2) + 2; + assert_eq!(*r.numer(), 5); + assert_eq!(*r.denom(), 2); + assert_eq!(r.to_integer(), 2); + + r = r * 2; + assert_eq!(*r.numer(), 10); + assert_eq!(*r.denom(), 2); + assert_eq!(r.to_integer(), 5); + + r = r / 2; + assert_eq!(*r.numer(), 10); + assert_eq!(*r.denom(), 4); + assert_eq!(r.to_integer(), 2); + } +} diff --git a/embassy-hal-internal/src/ring_buffer.rs b/embassy-hal-internal/src/ring_buffer.rs new file mode 100644 index 000000000..fcad68bb1 --- /dev/null +++ b/embassy-hal-internal/src/ring_buffer.rs @@ -0,0 +1,136 @@ +pub struct RingBuffer<'a> { + buf: &'a mut [u8], + start: usize, + end: usize, + empty: bool, +} + +impl<'a> RingBuffer<'a> { + pub fn new(buf: &'a mut [u8]) -> Self { + Self { + buf, + start: 0, + end: 0, + empty: true, + } + } + + pub fn push_buf(&mut self) -> &mut [u8] { + if self.start == self.end && !self.empty { + trace!(" ringbuf: push_buf empty"); + return &mut self.buf[..0]; + } + + let n = if self.start <= self.end { + self.buf.len() - self.end + } else { + self.start - self.end + }; + + trace!(" ringbuf: push_buf {:?}..{:?}", self.end, self.end + n); + &mut self.buf[self.end..self.end + n] + } + + pub fn push(&mut self, n: usize) { + trace!(" ringbuf: push {:?}", n); + if n == 0 { + return; + } + + self.end = self.wrap(self.end + n); + self.empty = false; + } + + pub fn pop_buf(&mut self) -> &mut [u8] { + if self.empty { + trace!(" ringbuf: pop_buf empty"); + return &mut self.buf[..0]; + } + + let n = if self.end <= self.start { + self.buf.len() - self.start + } else { + self.end - self.start + }; + + trace!(" ringbuf: pop_buf {:?}..{:?}", self.start, self.start + n); + &mut self.buf[self.start..self.start + n] + } + + pub fn pop(&mut self, n: usize) { + trace!(" ringbuf: pop {:?}", n); + if n == 0 { + return; + } + + self.start = self.wrap(self.start + n); + self.empty = self.start == self.end; + } + + pub fn is_full(&self) -> bool { + self.start == self.end && !self.empty + } + + pub fn is_empty(&self) -> bool { + self.empty + } + + pub fn clear(&mut self) { + self.start = 0; + self.end = 0; + self.empty = true; + } + + fn wrap(&self, n: usize) -> usize { + assert!(n <= self.buf.len()); + if n == self.buf.len() { + 0 + } else { + n + } + } +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn push_pop() { + let mut b = [0; 4]; + let mut rb = RingBuffer::new(&mut b); + let buf = rb.push_buf(); + assert_eq!(4, buf.len()); + buf[0] = 1; + buf[1] = 2; + buf[2] = 3; + buf[3] = 4; + rb.push(4); + + let buf = rb.pop_buf(); + assert_eq!(4, buf.len()); + assert_eq!(1, buf[0]); + rb.pop(1); + + let buf = rb.pop_buf(); + assert_eq!(3, buf.len()); + assert_eq!(2, buf[0]); + rb.pop(1); + + let buf = rb.pop_buf(); + assert_eq!(2, buf.len()); + assert_eq!(3, buf[0]); + rb.pop(1); + + let buf = rb.pop_buf(); + assert_eq!(1, buf.len()); + assert_eq!(4, buf[0]); + rb.pop(1); + + let buf = rb.pop_buf(); + assert_eq!(0, buf.len()); + + let buf = rb.push_buf(); + assert_eq!(4, buf.len()); + } +} -- cgit