diff options
Diffstat (limited to 'src')
| -rw-r--r-- | src/export.rs | 416 | ||||
| -rw-r--r-- | src/lib.rs | 123 | ||||
| -rw-r--r-- | src/tq.rs | 179 |
3 files changed, 0 insertions, 718 deletions
diff --git a/src/export.rs b/src/export.rs deleted file mode 100644 index 6f2a1b6..0000000 --- a/src/export.rs +++ /dev/null @@ -1,416 +0,0 @@ -#![allow(clippy::inline_always)] -use core::{ - cell::Cell, - sync::atomic::{AtomicBool, Ordering}, -}; - -pub use crate::tq::{NotReady, TimerQueue}; -pub use bare_metal::CriticalSection; -pub use cortex_m::{ - asm::nop, - asm::wfi, - interrupt, - peripheral::{scb::SystemHandler, DWT, NVIC, SCB, SYST}, - Peripherals, -}; -pub use heapless::sorted_linked_list::SortedLinkedList; -pub use heapless::spsc::Queue; -pub use heapless::BinaryHeap; -pub use rtic_monotonic as monotonic; - -pub type SCFQ<const N: usize> = Queue<u8, N>; -pub type SCRQ<T, const N: usize> = Queue<(T, u8), N>; - -/// Mask is used to store interrupt masks on systems without a BASEPRI register (M0, M0+, M23). -/// It needs to be large enough to cover all the relevant interrupts in use. -/// For M0/M0+ there are only 32 interrupts so we only need one u32 value. -/// For M23 there can be as many as 480 interrupts. -/// Rather than providing space for all possible interrupts, we just detect the highest interrupt in -/// use at compile time and allocate enough u32 chunks to cover them. -#[derive(Copy, Clone)] -pub struct Mask<const M: usize>([u32; M]); - -impl<const M: usize> core::ops::BitOrAssign for Mask<M> { - fn bitor_assign(&mut self, rhs: Self) { - for i in 0..M { - self.0[i] |= rhs.0[i]; - } - } -} - -#[cfg(not(have_basepri))] -impl<const M: usize> Mask<M> { - /// Set a bit inside a Mask. - const fn set_bit(mut self, bit: u32) -> Self { - let block = bit / 32; - - if block as usize >= M { - panic!("Generating masks for thumbv6/thumbv8m.base failed! Are you compiling for thumbv6 on an thumbv7 MCU or using an unsupported thumbv8m.base MCU?"); - } - - let offset = bit - (block * 32); - self.0[block as usize] |= 1 << offset; - self - } -} - -#[cfg(have_basepri)] -use cortex_m::register::basepri; - -#[cfg(have_basepri)] -#[inline(always)] -pub fn run<F>(priority: u8, f: F) -where - F: FnOnce(), -{ - if priority == 1 { - // If the priority of this interrupt is `1` then BASEPRI can only be `0` - f(); - unsafe { basepri::write(0) } - } else { - let initial = basepri::read(); - f(); - unsafe { basepri::write(initial) } - } -} - -#[cfg(not(have_basepri))] -#[inline(always)] -pub fn run<F>(_priority: u8, f: F) -where - F: FnOnce(), -{ - f(); -} - -pub struct Barrier { - inner: AtomicBool, -} - -impl Barrier { - pub const fn new() -> Self { - Barrier { - inner: AtomicBool::new(false), - } - } - - pub fn release(&self) { - self.inner.store(true, Ordering::Release); - } - - pub fn wait(&self) { - while !self.inner.load(Ordering::Acquire) { - core::hint::spin_loop() - } - } -} - -// Newtype over `Cell` that forbids mutation through a shared reference -pub struct Priority { - inner: Cell<u8>, -} - -impl Priority { - /// Create a new Priority - /// - /// # Safety - /// - /// Will overwrite the current Priority - #[inline(always)] - pub unsafe fn new(value: u8) -> Self { - Priority { - inner: Cell::new(value), - } - } - - /// Change the current priority to `value` - // These two methods are used by `lock` (see below) but can't be used from the RTIC application - #[inline(always)] - fn set(&self, value: u8) { - self.inner.set(value); - } - - /// Get the current priority - #[inline(always)] - fn get(&self) -> u8 { - self.inner.get() - } -} - -/// Const helper to check architecture -pub const fn have_basepri() -> bool { - #[cfg(have_basepri)] - { - true - } - - #[cfg(not(have_basepri))] - { - false - } -} - -#[inline(always)] -pub fn assert_send<T>() -where - T: Send, -{ -} - -#[inline(always)] -pub fn assert_sync<T>() -where - T: Sync, -{ -} - -#[inline(always)] -pub fn assert_monotonic<T>() -where - T: monotonic::Monotonic, -{ -} - -/// Lock implementation using BASEPRI and global Critical Section (CS) -/// -/// # Safety -/// -/// The system ceiling is raised from current to ceiling -/// by either -/// - raising the BASEPRI to the ceiling value, or -/// - disable all interrupts in case we want to -/// mask interrupts with maximum priority -/// -/// Dereferencing a raw pointer inside CS -/// -/// The priority.set/priority.get can safely be outside the CS -/// as being a context local cell (not affected by preemptions). -/// It is merely used in order to omit masking in case current -/// priority is current priority >= ceiling. -/// -/// Lock Efficiency: -/// Experiments validate (sub)-zero cost for CS implementation -/// (Sub)-zero as: -/// - Either zero OH (lock optimized out), or -/// - Amounting to an optimal assembly implementation -/// - The BASEPRI value is folded to a constant at compile time -/// - CS entry, single assembly instruction to write BASEPRI -/// - CS exit, single assembly instruction to write BASEPRI -/// - priority.set/get optimized out (their effect not) -/// - On par or better than any handwritten implementation of SRP -/// -/// Limitations: -/// The current implementation reads/writes BASEPRI once -/// even in some edge cases where this may be omitted. -/// Total OH of per task is max 2 clock cycles, negligible in practice -/// but can in theory be fixed. -/// -#[cfg(have_basepri)] -#[inline(always)] -pub unsafe fn lock<T, R, const M: usize>( - ptr: *mut T, - priority: &Priority, - ceiling: u8, - nvic_prio_bits: u8, - _mask: &[Mask<M>; 3], - f: impl FnOnce(&mut T) -> R, -) -> R { - let current = priority.get(); - - if current < ceiling { - if ceiling == (1 << nvic_prio_bits) { - priority.set(u8::max_value()); - let r = interrupt::free(|_| f(&mut *ptr)); - priority.set(current); - r - } else { - priority.set(ceiling); - basepri::write(logical2hw(ceiling, nvic_prio_bits)); - let r = f(&mut *ptr); - basepri::write(logical2hw(current, nvic_prio_bits)); - priority.set(current); - r - } - } else { - f(&mut *ptr) - } -} - -/// Lock implementation using interrupt masking -/// -/// # Safety -/// -/// The system ceiling is raised from current to ceiling -/// by computing a 32 bit `mask` (1 bit per interrupt) -/// 1: ceiling >= priority > current -/// 0: else -/// -/// On CS entry, `clear_enable_mask(mask)` disables interrupts -/// On CS exit, `set_enable_mask(mask)` re-enables interrupts -/// -/// The priority.set/priority.get can safely be outside the CS -/// as being a context local cell (not affected by preemptions). -/// It is merely used in order to omit masking in case -/// current priority >= ceiling. -/// -/// Dereferencing a raw pointer is done safely inside the CS -/// -/// Lock Efficiency: -/// Early experiments validate (sub)-zero cost for CS implementation -/// (Sub)-zero as: -/// - Either zero OH (lock optimized out), or -/// - Amounting to an optimal assembly implementation -/// - if ceiling == (1 << nvic_prio_bits) -/// - we execute the closure in a global critical section (interrupt free) -/// - CS entry cost, single write to core register -/// - CS exit cost, single write to core register -/// else -/// - The `mask` value is folded to a constant at compile time -/// - CS entry, single write of the 32 bit `mask` to the `icer` register -/// - CS exit, single write of the 32 bit `mask` to the `iser` register -/// - priority.set/get optimized out (their effect not) -/// - On par or better than any hand written implementation of SRP -/// -/// Limitations: -/// Current implementation does not allow for tasks with shared resources -/// to be bound to exception handlers, as these cannot be masked in HW. -/// -/// Possible solutions: -/// - Mask exceptions by global critical sections (interrupt::free) -/// - Temporary lower exception priority -/// -/// These possible solutions are set goals for future work -#[cfg(not(have_basepri))] -#[inline(always)] -pub unsafe fn lock<T, R, const M: usize>( - ptr: *mut T, - priority: &Priority, - ceiling: u8, - _nvic_prio_bits: u8, - masks: &[Mask<M>; 3], - f: impl FnOnce(&mut T) -> R, -) -> R { - let current = priority.get(); - if current < ceiling { - if ceiling >= 4 { - // safe to manipulate outside critical section - priority.set(ceiling); - // execute closure under protection of raised system ceiling - let r = interrupt::free(|_| f(&mut *ptr)); - // safe to manipulate outside critical section - priority.set(current); - r - } else { - // safe to manipulate outside critical section - priority.set(ceiling); - let mask = compute_mask(current, ceiling, masks); - clear_enable_mask(mask); - - // execute closure under protection of raised system ceiling - let r = f(&mut *ptr); - - set_enable_mask(mask); - - // safe to manipulate outside critical section - priority.set(current); - r - } - } else { - // execute closure without raising system ceiling - f(&mut *ptr) - } -} - -#[cfg(not(have_basepri))] -#[inline(always)] -fn compute_mask<const M: usize>(from_prio: u8, to_prio: u8, masks: &[Mask<M>; 3]) -> Mask<M> { - let mut res = Mask([0; M]); - masks[from_prio as usize..to_prio as usize] - .iter() - .for_each(|m| res |= *m); - res -} - -// enables interrupts -#[cfg(not(have_basepri))] -#[inline(always)] -unsafe fn set_enable_mask<const M: usize>(mask: Mask<M>) { - for i in 0..M { - // This check should involve compile time constants and be optimized out. - if mask.0[i] != 0 { - (*NVIC::PTR).iser[i].write(mask.0[i]); - } - } -} - -// disables interrupts -#[cfg(not(have_basepri))] -#[inline(always)] -unsafe fn clear_enable_mask<const M: usize>(mask: Mask<M>) { - for i in 0..M { - // This check should involve compile time constants and be optimized out. - if mask.0[i] != 0 { - (*NVIC::PTR).icer[i].write(mask.0[i]); - } - } -} - -#[inline] -#[must_use] -pub fn logical2hw(logical: u8, nvic_prio_bits: u8) -> u8 { - ((1 << nvic_prio_bits) - logical) << (8 - nvic_prio_bits) -} - -#[cfg(have_basepri)] -pub const fn create_mask<const N: usize, const M: usize>(_: [u32; N]) -> Mask<M> { - Mask([0; M]) -} - -#[cfg(not(have_basepri))] -pub const fn create_mask<const N: usize, const M: usize>(list_of_shifts: [u32; N]) -> Mask<M> { - let mut mask = Mask([0; M]); - let mut i = 0; - - while i < N { - let shift = list_of_shifts[i]; - i += 1; - mask = mask.set_bit(shift); - } - - mask -} - -#[cfg(have_basepri)] -pub const fn compute_mask_chunks<const L: usize>(_: [u32; L]) -> usize { - 0 -} - -/// Compute the number of u32 chunks needed to store the Mask value. -/// On M0, M0+ this should always end up being 1. -/// On M23 we will pick a number that allows us to store the highest index used by the code. -/// This means the amount of overhead will vary based on the actually interrupts used by the code. -#[cfg(not(have_basepri))] -pub const fn compute_mask_chunks<const L: usize>(ids: [u32; L]) -> usize { - let mut max: usize = 0; - let mut i = 0; - - while i < L { - let id = ids[i] as usize; - i += 1; - - if id > max { - max = id; - } - } - (max + 32) / 32 -} - -#[cfg(have_basepri)] -pub const fn no_basepri_panic() { - // For non-v6 all is fine -} - -#[cfg(not(have_basepri))] -pub const fn no_basepri_panic() { - panic!("Exceptions with shared resources are not allowed when compiling for thumbv6 or thumbv8m.base. Use local resources or `#[lock_free]` shared resources"); -} diff --git a/src/lib.rs b/src/lib.rs deleted file mode 100644 index 0c0d0cc..0000000 --- a/src/lib.rs +++ /dev/null @@ -1,123 +0,0 @@ -//! Real-Time Interrupt-driven Concurrency (RTIC) framework for ARM Cortex-M microcontrollers. -//! -//! **IMPORTANT**: This crate is published as [`cortex-m-rtic`] on crates.io but the name of the -//! library is `rtic`. -//! -//! [`cortex-m-rtic`]: https://crates.io/crates/cortex-m-rtic -//! -//! The user level documentation can be found [here]. -//! -//! [here]: https://rtic.rs -//! -//! Don't forget to check the documentation of the `#[app]` attribute (listed under the reexports -//! section), which is the main component of the framework. -//! -//! # Minimum Supported Rust Version (MSRV) -//! -//! This crate is compiled and tested with the latest toolchain (rolling) as of the release date. -//! If you run into compilation errors, try the latest stable release of the rust toolchain. -//! -//! # Semantic Versioning -//! -//! Like the Rust project, this crate adheres to [SemVer]: breaking changes in the API and semantics -//! require a *semver bump* (since 1.0.0 a new major version release), with the exception of breaking changes -//! that fix soundness issues -- those are considered bug fixes and can be landed in a new patch -//! release. -//! -//! [SemVer]: https://semver.org/spec/v2.0.0.html - -#![deny(missing_docs)] -#![deny(rust_2021_compatibility)] -#![deny(rust_2018_compatibility)] -#![deny(rust_2018_idioms)] -#![no_std] -#![doc( - html_logo_url = "https://raw.githubusercontent.com/rtic-rs/cortex-m-rtic/master/book/en/src/RTIC.svg", - html_favicon_url = "https://raw.githubusercontent.com/rtic-rs/cortex-m-rtic/master/book/en/src/RTIC.svg" -)] -//deny_warnings_placeholder_for_ci -#![allow(clippy::inline_always)] - -use cortex_m::{interrupt::InterruptNumber, peripheral::NVIC}; -pub use cortex_m_rtic_macros::app; -pub use rtic_core::{prelude as mutex_prelude, Exclusive, Mutex}; -pub use rtic_monotonic::{self, Monotonic}; - -/// module `mutex::prelude` provides `Mutex` and multi-lock variants. Recommended over `mutex_prelude` -pub mod mutex { - pub use rtic_core::prelude; - pub use rtic_core::Mutex; -} - -#[doc(hidden)] -pub mod export; -#[doc(hidden)] -mod tq; - -/// Sets the given `interrupt` as pending -/// -/// This is a convenience function around -/// [`NVIC::pend`](../cortex_m/peripheral/struct.NVIC.html#method.pend) -pub fn pend<I>(interrupt: I) -where - I: InterruptNumber, -{ - NVIC::pend(interrupt); -} - -use core::cell::UnsafeCell; - -/// Internal replacement for `static mut T` -/// -/// Used to represent RTIC Resources -/// -/// Soundness: -/// 1) Unsafe API for internal use only -/// 2) ``get_mut(&self) -> *mut T`` -/// returns a raw mutable pointer to the inner T -/// casting to &mut T is under control of RTIC -/// RTIC ensures &mut T to be unique under Rust aliasing rules. -/// -/// Implementation uses the underlying ``UnsafeCell<T>`` -/// self.0.get() -> *mut T -/// -/// 3) get(&self) -> *const T -/// returns a raw immutable (const) pointer to the inner T -/// casting to &T is under control of RTIC -/// RTIC ensures &T to be shared under Rust aliasing rules. -/// -/// Implementation uses the underlying ``UnsafeCell<T>`` -/// self.0.get() -> *mut T, demoted to *const T -/// -#[repr(transparent)] -pub struct RacyCell<T>(UnsafeCell<T>); - -impl<T> RacyCell<T> { - /// Create a ``RacyCell`` - #[inline(always)] - pub const fn new(value: T) -> Self { - RacyCell(UnsafeCell::new(value)) - } - - /// Get `*mut T` - /// - /// # Safety - /// - /// See documentation notes for [`RacyCell`] - #[inline(always)] - pub unsafe fn get_mut(&self) -> *mut T { - self.0.get() - } - - /// Get `*const T` - /// - /// # Safety - /// - /// See documentation notes for [`RacyCell`] - #[inline(always)] - pub unsafe fn get(&self) -> *const T { - self.0.get() - } -} - -unsafe impl<T> Sync for RacyCell<T> {} diff --git a/src/tq.rs b/src/tq.rs deleted file mode 100644 index 0f585ba..0000000 --- a/src/tq.rs +++ /dev/null @@ -1,179 +0,0 @@ -use crate::Monotonic; -use core::cmp::Ordering; -use heapless::sorted_linked_list::{LinkedIndexU16, Min, SortedLinkedList}; - -pub struct TimerQueue<Mono, Task, const N: usize>( - pub SortedLinkedList<NotReady<Mono, Task>, LinkedIndexU16, Min, N>, -) -where - Mono: Monotonic, - Task: Copy; - -impl<Mono, Task, const N: usize> TimerQueue<Mono, Task, N> -where - Mono: Monotonic, - Task: Copy, -{ - /// # Safety - /// - /// Writing to memory with a transmute in order to enable - /// interrupts of the ``SysTick`` timer - /// - /// Enqueue a task without checking if it is full - #[inline] - pub unsafe fn enqueue_unchecked<F1, F2>( - &mut self, - nr: NotReady<Mono, Task>, - enable_interrupt: F1, - pend_handler: F2, - mono: Option<&mut Mono>, - ) where - F1: FnOnce(), - F2: FnOnce(), - { - // Check if the top contains a non-empty element and if that element is - // greater than nr - let if_heap_max_greater_than_nr = - self.0.peek().map_or(true, |head| nr.instant < head.instant); - - if if_heap_max_greater_than_nr { - if Mono::DISABLE_INTERRUPT_ON_EMPTY_QUEUE && self.0.is_empty() { - if let Some(mono) = mono { - mono.enable_timer(); - } - enable_interrupt(); - } - - pend_handler(); - } - - self.0.push_unchecked(nr); - } - - /// Check if the timer queue is empty. - #[inline] - pub fn is_empty(&self) -> bool { - self.0.is_empty() - } - - /// Cancel the marker value - pub fn cancel_marker(&mut self, marker: u32) -> Option<(Task, u8)> { - if let Some(val) = self.0.find_mut(|nr| nr.marker == marker) { - let nr = val.pop(); - - Some((nr.task, nr.index)) - } else { - None - } - } - - /// Update the instant at an marker value to a new instant - #[allow(clippy::result_unit_err)] - pub fn update_marker<F: FnOnce()>( - &mut self, - marker: u32, - new_marker: u32, - instant: Mono::Instant, - pend_handler: F, - ) -> Result<(), ()> { - if let Some(mut val) = self.0.find_mut(|nr| nr.marker == marker) { - val.instant = instant; - val.marker = new_marker; - - // On update pend the handler to reconfigure the next compare match - pend_handler(); - - Ok(()) - } else { - Err(()) - } - } - - /// Dequeue a task from the ``TimerQueue`` - pub fn dequeue<F>(&mut self, disable_interrupt: F, mono: &mut Mono) -> Option<(Task, u8)> - where - F: FnOnce(), - { - mono.clear_compare_flag(); - - if let Some(instant) = self.0.peek().map(|p| p.instant) { - if instant <= mono.now() { - // task became ready - let nr = unsafe { self.0.pop_unchecked() }; - - Some((nr.task, nr.index)) - } else { - // Set compare - mono.set_compare(instant); - - // Double check that the instant we set is really in the future, else - // dequeue. If the monotonic is fast enough it can happen that from the - // read of now to the set of the compare, the time can overflow. This is to - // guard against this. - if instant <= mono.now() { - let nr = unsafe { self.0.pop_unchecked() }; - - Some((nr.task, nr.index)) - } else { - None - } - } - } else { - // The queue is empty, disable the interrupt. - if Mono::DISABLE_INTERRUPT_ON_EMPTY_QUEUE { - disable_interrupt(); - mono.disable_timer(); - } - - None - } - } -} - -pub struct NotReady<Mono, Task> -where - Task: Copy, - Mono: Monotonic, -{ - pub index: u8, - pub instant: Mono::Instant, - pub task: Task, - pub marker: u32, -} - -impl<Mono, Task> Eq for NotReady<Mono, Task> -where - Task: Copy, - Mono: Monotonic, -{ -} - -impl<Mono, Task> Ord for NotReady<Mono, Task> -where - Task: Copy, - Mono: Monotonic, -{ - fn cmp(&self, other: &Self) -> Ordering { - self.instant.cmp(&other.instant) - } -} - -impl<Mono, Task> PartialEq for NotReady<Mono, Task> -where - Task: Copy, - Mono: Monotonic, -{ - fn eq(&self, other: &Self) -> bool { - self.instant == other.instant - } -} - -impl<Mono, Task> PartialOrd for NotReady<Mono, Task> -where - Task: Copy, - Mono: Monotonic, -{ - fn partial_cmp(&self, other: &Self) -> Option<Ordering> { - Some(self.cmp(other)) - } -} |
