diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/cyccnt.rs | 205 | ||||
-rw-r--r-- | src/export.rs | 56 | ||||
-rw-r--r-- | src/lib.rs | 308 | ||||
-rw-r--r-- | src/tq.rs | 117 |
4 files changed, 362 insertions, 324 deletions
diff --git a/src/cyccnt.rs b/src/cyccnt.rs new file mode 100644 index 00000000..a2b216c1 --- /dev/null +++ b/src/cyccnt.rs @@ -0,0 +1,205 @@ +//! Data Watchpoint Trace (DWT) unit's CYCle CouNTer + +use core::{ + cmp::Ordering, + convert::{Infallible, TryInto}, + fmt, + marker::PhantomData, + ops, +}; + +use cortex_m::peripheral::DWT; + +/// A measurement of the CYCCNT. Opaque and useful only with `Duration` +/// +/// This data type is only available on ARMv7-M +#[derive(Clone, Copy, Eq, PartialEq)] +pub struct Instant { + inner: i32, + _not_send_or_sync: PhantomData<*mut ()>, +} + +unsafe impl Sync for Instant {} + +#[cfg(not(feature = "heterogeneous"))] +unsafe impl Send for Instant {} + +impl Instant { + /// Returns an instant corresponding to "now" + pub fn now() -> Self { + Instant { + inner: DWT::get_cycle_count() as i32, + _not_send_or_sync: PhantomData, + } + } + + /// Returns the amount of time elapsed since this instant was created. + pub fn elapsed(&self) -> Duration { + Instant::now() - *self + } + + /// Returns the amount of time elapsed from another instant to this one. + pub fn duration_since(&self, earlier: Instant) -> Duration { + let diff = self.inner - earlier.inner; + assert!(diff >= 0, "second instant is later than self"); + Duration { inner: diff as u32 } + } +} + +impl fmt::Debug for Instant { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.debug_tuple("Instant") + .field(&(self.inner as u32)) + .finish() + } +} + +impl ops::AddAssign<Duration> for Instant { + fn add_assign(&mut self, dur: Duration) { + debug_assert!(dur.inner < (1 << 31)); + self.inner = self.inner.wrapping_add(dur.inner as i32); + } +} + +impl ops::Add<Duration> for Instant { + type Output = Self; + + fn add(mut self, dur: Duration) -> Self { + self += dur; + self + } +} + +impl ops::SubAssign<Duration> for Instant { + fn sub_assign(&mut self, dur: Duration) { + // XXX should this be a non-debug assertion? + debug_assert!(dur.inner < (1 << 31)); + self.inner = self.inner.wrapping_sub(dur.inner as i32); + } +} + +impl ops::Sub<Duration> for Instant { + type Output = Self; + + fn sub(mut self, dur: Duration) -> Self { + self -= dur; + self + } +} + +impl ops::Sub<Instant> for Instant { + type Output = Duration; + + fn sub(self, other: Instant) -> Duration { + self.duration_since(other) + } +} + +impl Ord for Instant { + fn cmp(&self, rhs: &Self) -> Ordering { + self.inner.wrapping_sub(rhs.inner).cmp(&0) + } +} + +impl PartialOrd for Instant { + fn partial_cmp(&self, rhs: &Self) -> Option<Ordering> { + Some(self.cmp(rhs)) + } +} + +/// A `Duration` type to represent a span of time. +/// +/// This data type is only available on ARMv7-M +#[derive(Clone, Copy, Default, Eq, Ord, PartialEq, PartialOrd)] +pub struct Duration { + inner: u32, +} + +impl Duration { + /// Returns the total number of clock cycles contained by this `Duration` + pub fn as_cycles(&self) -> u32 { + self.inner + } +} + +impl TryInto<u32> for Duration { + type Error = Infallible; + + fn try_into(self) -> Result<u32, Infallible> { + Ok(self.as_cycles()) + } +} + +impl ops::AddAssign for Duration { + fn add_assign(&mut self, dur: Duration) { + self.inner += dur.inner; + } +} + +impl ops::Add<Duration> for Duration { + type Output = Self; + + fn add(self, other: Self) -> Self { + Duration { + inner: self.inner + other.inner, + } + } +} + +impl ops::SubAssign for Duration { + fn sub_assign(&mut self, rhs: Duration) { + self.inner -= rhs.inner; + } +} + +impl ops::Sub<Duration> for Duration { + type Output = Self; + + fn sub(self, rhs: Self) -> Self { + Duration { + inner: self.inner - rhs.inner, + } + } +} + +/// Adds the `cycles` method to the `u32` type +/// +/// This trait is only available on ARMv7-M +pub trait U32Ext { + /// Converts the `u32` value into clock cycles + fn cycles(self) -> Duration; +} + +impl U32Ext for u32 { + fn cycles(self) -> Duration { + Duration { inner: self } + } +} + +/// Implementation of the `Monotonic` trait based on CYCle CouNTer +#[cfg(not(feature = "heterogeneous"))] +pub struct CYCCNT; + +#[cfg(not(feature = "heterogeneous"))] +unsafe impl crate::Monotonic for CYCCNT { + type Instant = Instant; + + fn ratio() -> u32 { + 1 + } + + unsafe fn reset() { + (0xE0001004 as *mut u32).write_volatile(0) + } + + fn now() -> Instant { + Instant::now() + } + + fn zero() -> Instant { + Instant { + inner: 0, + _not_send_or_sync: PhantomData, + } + } +} diff --git a/src/export.rs b/src/export.rs index afed9091..7646e3c5 100644 --- a/src/export.rs +++ b/src/export.rs @@ -1,21 +1,27 @@ -//! IMPLEMENTATION DETAILS. DO NOT USE ANYTHING IN THIS MODULE - -use core::{cell::Cell, u8}; +use core::{ + cell::Cell, + sync::atomic::{AtomicBool, Ordering}, +}; +pub use crate::tq::{NotReady, TimerQueue}; #[cfg(armv7m)] -use cortex_m::register::basepri; +pub use cortex_m::register::basepri; pub use cortex_m::{ - asm::wfi, interrupt, peripheral::scb::SystemHandler, peripheral::syst::SystClkSource, - peripheral::Peripherals, + asm::wfi, + interrupt, + peripheral::{scb::SystemHandler, syst::SystClkSource, DWT}, + Peripherals, }; -use heapless::spsc::SingleCore; -pub use heapless::{consts, i, spsc::Queue}; - -#[cfg(feature = "timer-queue")] -pub use crate::tq::{NotReady, TimerQueue}; +use heapless::spsc::{MultiCore, SingleCore}; +pub use heapless::{consts, i::Queue as iQueue, spsc::Queue}; +pub use heapless::{i::BinaryHeap as iBinaryHeap, BinaryHeap}; +#[cfg(feature = "heterogeneous")] +pub use microamp::shared; -pub type FreeQueue<N> = Queue<u8, N, u8, SingleCore>; -pub type ReadyQueue<T, N> = Queue<(T, u8), N, u8, SingleCore>; +pub type MCFQ<N> = Queue<u8, N, u8, MultiCore>; +pub type MCRQ<T, N> = Queue<(T, u8), N, u8, MultiCore>; +pub type SCFQ<N> = Queue<u8, N, u8, SingleCore>; +pub type SCRQ<T, N> = Queue<(T, u8), N, u8, SingleCore>; #[cfg(armv7m)] #[inline(always)] @@ -43,6 +49,26 @@ where 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) {} + } +} + // Newtype over `Cell` that forbids mutation through a shared reference pub struct Priority { inner: Cell<u8>, @@ -95,7 +121,7 @@ pub unsafe fn lock<T, R>( if current < ceiling { if ceiling == (1 << nvic_prio_bits) { - priority.set(u8::MAX); + priority.set(u8::max_value()); let r = interrupt::free(|_| f(&mut *ptr)); priority.set(current); r @@ -124,7 +150,7 @@ pub unsafe fn lock<T, R>( let current = priority.get(); if current < ceiling { - priority.set(u8::MAX); + priority.set(u8::max_value()); let r = interrupt::free(|_| f(&mut *ptr)); priority.set(current); r @@ -33,68 +33,45 @@ //! //! # Cargo features //! -//! - `timer-queue`. This opt-in feature enables the `schedule` API which can be used to schedule -//! tasks to run in the future. Also see [`Instant`] and [`Duration`]. -//! -//! [`Instant`]: struct.Instant.html -//! [`Duration`]: struct.Duration.html -//! -//! - `nightly`. Enabling this opt-in feature makes RTFM internally use the unstable `const_fn` -//! language feature to reduce static memory usage, runtime overhead and initialization overhead. -//! This feature requires a nightly compiler and may stop working at any time! +//! - `heterogeneous`. This opt-in feature enables the *experimental* heterogeneous multi-core support. #![deny(missing_docs)] +#![deny(rust_2018_compatibility)] +#![deny(rust_2018_idioms)] #![deny(warnings)] #![no_std] -#[cfg(feature = "timer-queue")] -use core::cmp::Ordering; -use core::{fmt, ops}; +use core::ops::Sub; -#[cfg(not(feature = "timer-queue"))] -use cortex_m::peripheral::SYST; use cortex_m::{ interrupt::Nr, peripheral::{CBP, CPUID, DCB, DWT, FPB, FPU, ITM, MPU, NVIC, SCB, TPIU}, }; +#[cfg(not(feature = "heterogeneous"))] +use cortex_m_rt as _; // vector table pub use cortex_m_rtfm_macros::app; +pub use rtfm_core::{Exclusive, Mutex}; +#[cfg(armv7m)] +pub mod cyccnt; #[doc(hidden)] pub mod export; #[doc(hidden)] -#[cfg(feature = "timer-queue")] mod tq; -#[cfg(all(feature = "timer-queue", armv6m))] -compile_error!( - "The `timer-queue` feature is currently not supported on ARMv6-M (`thumbv6m-none-eabi`)" -); - -/// Core peripherals -/// -/// This is `cortex_m::Peripherals` minus the peripherals that the RTFM runtime uses -/// -/// - The `NVIC` field is never present. -/// - When the `timer-queue` feature is enabled the following fields are *not* present: `DWT` and -/// `SYST`. +/// `cortex_m::Peripherals` minus `SYST` #[allow(non_snake_case)] -pub struct Peripherals<'a> { +pub struct Peripherals { /// Cache and branch predictor maintenance operations (not present on Cortex-M0 variants) pub CBP: CBP, /// CPUID pub CPUID: CPUID, - /// Debug Control Block (by value if the `timer-queue` feature is disabled) - #[cfg(feature = "timer-queue")] - pub DCB: &'a mut DCB, - - /// Debug Control Block (borrowed if the `timer-queue` feature is enabled) - #[cfg(not(feature = "timer-queue"))] + /// Debug Control Block pub DCB: DCB, - /// Data Watchpoint and Trace unit (not present if the `timer-queue` feature is enabled) - #[cfg(not(feature = "timer-queue"))] + /// Data Watchpoint and Trace unit pub DWT: DWT, /// Flash Patch and Breakpoint unit (not present on Cortex-M0 variants) @@ -109,245 +86,52 @@ pub struct Peripherals<'a> { /// Memory Protection Unit pub MPU: MPU, - // Nested Vector Interrupt Controller - // pub NVIC: NVIC, - /// System Control Block - pub SCB: &'a mut SCB, + /// Nested Vector Interrupt Controller + pub NVIC: NVIC, - /// SysTick: System Timer (not present if the `timer-queue` is enabled) - #[cfg(not(feature = "timer-queue"))] - pub SYST: SYST, + /// System Control Block + pub SCB: SCB, + // SysTick: System Timer + // pub SYST: SYST, /// Trace Port Interface Unit (not present on Cortex-M0 variants) pub TPIU: TPIU, } -/// A measurement of a monotonically nondecreasing clock. Opaque and useful only with `Duration` -/// -/// This data type is only available when the `timer-queue` feature is enabled -#[derive(Clone, Copy, Debug, Eq, PartialEq)] -#[cfg(feature = "timer-queue")] -pub struct Instant(i32); - -#[cfg(feature = "timer-queue")] -impl Instant { - /// IMPLEMENTATION DETAIL. DO NOT USE - #[doc(hidden)] - pub unsafe fn artificial(timestamp: i32) -> Self { - Instant(timestamp) - } - - /// Returns an instant corresponding to "now" - pub fn now() -> Self { - Instant(DWT::get_cycle_count() as i32) - } - - /// Returns the amount of time elapsed since this instant was created. - pub fn elapsed(&self) -> Duration { - Instant::now() - *self - } - - /// Returns the amount of time elapsed from another instant to this one. - pub fn duration_since(&self, earlier: Instant) -> Duration { - let diff = self.0 - earlier.0; - assert!(diff >= 0, "second instant is later than self"); - Duration(diff as u32) - } -} - -#[cfg(feature = "timer-queue")] -impl ops::AddAssign<Duration> for Instant { - fn add_assign(&mut self, dur: Duration) { - debug_assert!(dur.0 < (1 << 31)); - self.0 = self.0.wrapping_add(dur.0 as i32); +impl From<cortex_m::Peripherals> for Peripherals { + fn from(p: cortex_m::Peripherals) -> Self { + Self { + CBP: p.CBP, + CPUID: p.CPUID, + DCB: p.DCB, + DWT: p.DWT, + FPB: p.FPB, + FPU: p.FPU, + ITM: p.ITM, + MPU: p.MPU, + NVIC: p.NVIC, + SCB: p.SCB, + TPIU: p.TPIU, + } } } -#[cfg(feature = "timer-queue")] -impl ops::Add<Duration> for Instant { - type Output = Self; +/// A monotonic clock / counter +pub unsafe trait Monotonic { + /// A measurement of this clock + type Instant: Copy + Ord + Sub; - fn add(mut self, dur: Duration) -> Self { - self += dur; - self - } -} + /// The ratio between the SysTick (system timer) frequency and this clock frequency + fn ratio() -> u32; -#[cfg(feature = "timer-queue")] -impl ops::SubAssign<Duration> for Instant { - fn sub_assign(&mut self, dur: Duration) { - // XXX should this be a non-debug assertion? - debug_assert!(dur.0 < (1 << 31)); - self.0 = self.0.wrapping_sub(dur.0 as i32); - } -} + /// Returns the current time + fn now() -> Self::Instant; -#[cfg(feature = "timer-queue")] -impl ops::Sub<Duration> for Instant { - type Output = Self; + /// Resets the counter to *zero* + unsafe fn reset(); - fn sub(mut self, dur: Duration) -> Self { - self -= dur; - self - } -} - -#[cfg(feature = "timer-queue")] -impl ops::Sub<Instant> for Instant { - type Output = Duration; - - fn sub(self, other: Instant) -> Duration { - self.duration_since(other) - } -} - -#[cfg(feature = "timer-queue")] -impl Ord for Instant { - fn cmp(&self, rhs: &Self) -> Ordering { - self.0.wrapping_sub(rhs.0).cmp(&0) - } -} - -#[cfg(feature = "timer-queue")] -impl PartialOrd for Instant { - fn partial_cmp(&self, rhs: &Self) -> Option<Ordering> { - Some(self.cmp(rhs)) - } -} - -/// A `Duration` type to represent a span of time. -/// -/// This data type is only available when the `timer-queue` feature is enabled -#[derive(Clone, Copy, Default, Eq, Ord, PartialEq, PartialOrd)] -#[cfg(feature = "timer-queue")] -pub struct Duration(u32); - -#[cfg(feature = "timer-queue")] -impl Duration { - /// Returns the total number of clock cycles contained by this `Duration` - pub fn as_cycles(&self) -> u32 { - self.0 - } -} - -#[cfg(feature = "timer-queue")] -impl ops::AddAssign for Duration { - fn add_assign(&mut self, dur: Duration) { - self.0 += dur.0; - } -} - -#[cfg(feature = "timer-queue")] -impl ops::Add<Duration> for Duration { - type Output = Self; - - fn add(self, other: Self) -> Self { - Duration(self.0 + other.0) - } -} - -#[cfg(feature = "timer-queue")] -impl ops::SubAssign for Duration { - fn sub_assign(&mut self, rhs: Duration) { - self.0 -= rhs.0; - } -} - -#[cfg(feature = "timer-queue")] -impl ops::Sub<Duration> for Duration { - type Output = Self; - - fn sub(self, rhs: Self) -> Self { - Duration(self.0 - rhs.0) - } -} - -/// Adds the `cycles` method to the `u32` type -/// -/// This trait is only available when the `timer-queue` feature is enabled -#[cfg(feature = "timer-queue")] -pub trait U32Ext { - /// Converts the `u32` value into clock cycles - fn cycles(self) -> Duration; -} - -#[cfg(feature = "timer-queue")] -impl U32Ext for u32 { - fn cycles(self) -> Duration { - Duration(self) - } -} - -/// Memory safe access to shared resources -/// -/// In RTFM, locks are implemented as critical sections that prevent other tasks from *starting*. -/// These critical sections are implemented by temporarily increasing the dynamic priority (see -/// [BASEPRI]) of the current context. Entering and leaving these critical sections is always done -/// in constant time (a few instructions). -/// -/// [BASEPRI]: https://developer.arm.com/products/architecture/cpu-architecture/m-profile/docs/100701/latest/special-purpose-mask-registers -pub trait Mutex { - /// Data protected by the mutex - type T; - - /// Creates a critical section and grants temporary access to the protected data - fn lock<R>(&mut self, f: impl FnOnce(&mut Self::T) -> R) -> R; -} - -impl<'a, M> Mutex for &'a mut M -where - M: Mutex, -{ - type T = M::T; - - fn lock<R>(&mut self, f: impl FnOnce(&mut M::T) -> R) -> R { - (**self).lock(f) - } -} - -/// Newtype over `&'a mut T` that implements the `Mutex` trait -/// -/// The `Mutex` implementation for this type is a no-op, no critical section is created -pub struct Exclusive<'a, T>(pub &'a mut T); - -impl<'a, T> Mutex for Exclusive<'a, T> { - type T = T; - - fn lock<R>(&mut self, f: impl FnOnce(&mut T) -> R) -> R { - f(self.0) - } -} - -impl<'a, T> fmt::Debug for Exclusive<'a, T> -where - T: fmt::Debug, -{ - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - (**self).fmt(f) - } -} - -impl<'a, T> fmt::Display for Exclusive<'a, T> -where - T: fmt::Display, -{ - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - (**self).fmt(f) - } -} - -impl<'a, T> ops::Deref for Exclusive<'a, T> { - type Target = T; - - fn deref(&self) -> &T { - self.0 - } -} - -impl<'a, T> ops::DerefMut for Exclusive<'a, T> { - fn deref_mut(&mut self) -> &mut T { - self.0 - } + /// A `Self::Instant` that represents a count of *zero* + fn zero() -> Self::Instant; } /// Sets the given `interrupt` as pending @@ -1,36 +1,34 @@ -use core::cmp::{self, Ordering}; +use core::{ + cmp::{self, Ordering}, + convert::TryInto, + mem, + ops::Sub, +}; use cortex_m::peripheral::{SCB, SYST}; use heapless::{binary_heap::Min, ArrayLength, BinaryHeap}; -use crate::Instant; +use crate::Monotonic; -pub struct TimerQueue<T, N> +pub struct TimerQueue<M, T, N>(pub BinaryHeap<NotReady<M, T>, N, Min>) where - N: ArrayLength<NotReady<T>>, - T: Copy, -{ - pub syst: SYST, - pub queue: BinaryHeap<NotReady<T>, N, Min>, -} + M: Monotonic, + <M::Instant as Sub>::Output: TryInto<u32>, + N: ArrayLength<NotReady<M, T>>, + T: Copy; -impl<T, N> TimerQueue<T, N> +impl<M, T, N> TimerQueue<M, T, N> where - N: ArrayLength<NotReady<T>>, + M: Monotonic, + <M::Instant as Sub>::Output: TryInto<u32>, + N: ArrayLength<NotReady<M, T>>, T: Copy, { - pub fn new(syst: SYST) -> Self { - TimerQueue { - syst, - queue: BinaryHeap::new(), - } - } - #[inline] - pub unsafe fn enqueue_unchecked(&mut self, nr: NotReady<T>) { + pub unsafe fn enqueue_unchecked(&mut self, nr: NotReady<M, T>) { let mut is_empty = true; if self - .queue + .0 .peek() .map(|head| { is_empty = false; @@ -39,77 +37,102 @@ where .unwrap_or(true) { if is_empty { - self.syst.enable_interrupt(); + mem::transmute::<_, SYST>(()).enable_interrupt(); } // set SysTick pending SCB::set_pendst(); } - self.queue.push_unchecked(nr); + self.0.push_unchecked(nr); } #[inline] pub fn dequeue(&mut self) -> Option<(T, u8)> { - if let Some(instant) = self.queue.peek().map(|p| p.instant) { - let diff = instant.0.wrapping_sub(Instant::now().0); - - if diff < 0 { - // task became ready - let nr = unsafe { self.queue.pop_unchecked() }; - - Some((nr.task, nr.index)) + unsafe { + if let Some(instant) = self.0.peek().map(|p| p.instant) { + let now = M::now(); + + if instant < now { + // task became ready + let nr = self.0.pop_unchecked(); + + Some((nr.task, nr.index)) + } else { + // set a new timeout + const MAX: u32 = 0x00ffffff; + + let dur = match (instant - now) + .try_into() + .ok() + .and_then(|x| x.checked_mul(M::ratio())) + { + None => MAX, + Some(x) => cmp::min(MAX, x), + }; + mem::transmute::<_, SYST>(()).set_reload(dur); + + // start counting down from the new reload + mem::transmute::<_, SYST>(()).clear_current(); + + None + } } else { - // set a new timeout - const MAX: u32 = 0x00ffffff; - - self.syst.set_reload(cmp::min(MAX, diff as u32)); - - // start counting down from the new reload - self.syst.clear_current(); + // the queue is empty + mem::transmute::<_, SYST>(()).disable_interrupt(); None } - } else { - // the queue is empty - self.syst.disable_interrupt(); - None } } } -pub struct NotReady<T> +pub struct NotReady<M, T> where T: Copy, + M: Monotonic, + <M::Instant as Sub>::Output: TryInto<u32>, { pub index: u8, - pub instant: Instant, + pub instant: M::Instant, pub task: T, } -impl<T> Eq for NotReady<T> where T: Copy {} +impl<M, T> Eq for NotReady<M, T> +where + T: Copy, + M: Monotonic, + <M::Instant as Sub>::Output: TryInto<u32>, +{ +} -impl<T> Ord for NotReady<T> +impl<M, T> Ord for NotReady<M, T> where T: Copy, + M: Monotonic, + <M::Instant as Sub>::Output: TryInto<u32>, { fn cmp(&self, other: &Self) -> Ordering { self.instant.cmp(&other.instant) } } -impl<T> PartialEq for NotReady<T> +impl<M, T> PartialEq for NotReady<M, T> where T: Copy, + M: Monotonic, + <M::Instant as Sub>::Output: TryInto<u32>, { fn eq(&self, other: &Self) -> bool { self.instant == other.instant } } -impl<T> PartialOrd for NotReady<T> +impl<M, T> PartialOrd for NotReady<M, T> where T: Copy, + M: Monotonic, + <M::Instant as Sub>::Output: TryInto<u32>, { fn partial_cmp(&self, other: &Self) -> Option<Ordering> { Some(self.cmp(&other)) |