aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/export.rs416
-rw-r--r--src/lib.rs123
-rw-r--r--src/tq.rs179
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))
- }
-}