diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/cyccnt.rs | 221 | ||||
-rw-r--r-- | src/export.rs | 178 | ||||
-rw-r--r-- | src/lib.rs | 382 | ||||
-rw-r--r-- | src/tq.rs | 164 |
4 files changed, 457 insertions, 488 deletions
diff --git a/src/cyccnt.rs b/src/cyccnt.rs new file mode 100644 index 00000000..8e07b001 --- /dev/null +++ b/src/cyccnt.rs @@ -0,0 +1,221 @@ +//! Data Watchpoint Trace (DWT) unit's CYCle CouNTer (CYCCNT) + +use core::{ + cmp::Ordering, + convert::{Infallible, TryInto}, + fmt, ops, +}; + +use cortex_m::peripheral::DWT; + +use crate::Fraction; + +/// A measurement of the CYCCNT. Opaque and useful only with `Duration` +/// +/// This data type is only available on ARMv7-M +/// +/// # Correctness +/// +/// Adding or subtracting a `Duration` of more than `(1 << 31)` cycles to an `Instant` effectively +/// makes it "wrap around" and creates an incorrect value. This is also true if the operation is +/// done in steps, e.g. `(instant + dur) + dur` where `dur` is `(1 << 30)` ticks. +#[derive(Clone, Copy, Eq, PartialEq)] +pub struct Instant { + inner: i32, +} + +impl Instant { + /// Returns an instant corresponding to "now" + /// + /// *HEADS UP* this function can, and will, return nonsensical values if called within `init`. + /// Only use it in `idle` and tasks. In `init`, use the `init::Context.start` field, or the + /// `CYCCNT::zero` function, instead of this function + pub fn now() -> Self { + Instant { + inner: DWT::get_cycle_count() as i32, + } + } + + /// Returns the amount of time elapsed since this instant was created. + pub fn elapsed(&self) -> Duration { + let diff = Instant::now().inner.wrapping_sub(self.inner); + assert!(diff >= 0, "instant now is earlier than self"); + Duration { inner: diff as u32 } + } + + /// Returns the amount of time elapsed from another instant to this one. + pub fn duration_since(&self, earlier: Instant) -> Duration { + let diff = self.inner.wrapping_sub(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) { + // NOTE this is a debug assertion because there's no foolproof way to detect a wrap around; + // the user may write `(instant + dur) + dur` where `dur` is `(1<<31)-1` ticks. + 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) { + // NOTE see the NOTE in `<Instant as AddAssign<Duration>>::add_assign` + 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 +/// +/// # Correctness +/// +/// This type is *not* appropriate for representing time spans in the order of, or larger than, +/// seconds because it can hold a maximum of `(1 << 31)` "ticks" where each tick is the inverse of +/// the CPU frequency, which usually is dozens of MHz. +#[derive(Clone, Copy, Default, Eq, Ord, PartialEq, PartialOrd)] +pub struct Duration { + inner: u32, +} + +impl Duration { + /// Creates a new `Duration` from the specified number of clock cycles + pub fn from_cycles(cycles: u32) -> Self { + Duration { inner: cycles } + } + + /// 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 +pub struct CYCCNT; + +impl crate::Monotonic for CYCCNT { + type Instant = Instant; + + fn ratio() -> Fraction { + Fraction { + numerator: 1, + denominator: 1, + } + } + + unsafe fn reset() { + (0xE0001004 as *mut u32).write_volatile(0) + } + + fn now() -> Instant { + Instant::now() + } + + fn zero() -> Instant { + Instant { inner: 0 } + } +} diff --git a/src/export.rs b/src/export.rs index cf7293b6..27f7f5fb 100644 --- a/src/export.rs +++ b/src/export.rs @@ -1,44 +1,71 @@ -//! IMPLEMENTATION DETAILS. DO NOT USE ANYTHING IN THIS MODULE - -#[cfg(not(feature = "nightly"))] -use core::ptr; -use core::{cell::Cell, u8}; +use core::{ + cell::Cell, + sync::atomic::{AtomicBool, Ordering}, +}; +pub use crate::tq::{NotReady, TimerQueue}; +pub use bare_metal::CriticalSection; #[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, NVIC}, + Peripherals, }; -pub use heapless::consts; -use heapless::spsc::{Queue, SingleCore}; +use heapless::spsc::SingleCore; +pub use heapless::{consts, i::Queue as iQueue, spsc::Queue}; +pub use heapless::{i::BinaryHeap as iBinaryHeap, BinaryHeap}; -#[cfg(feature = "timer-queue")] -pub use crate::tq::{isr as sys_tick, NotReady, TimerQueue}; - -pub type FreeQueue<N> = Queue<u8, N, usize, SingleCore>; -pub type ReadyQueue<T, N> = Queue<(T, u8), N, usize, SingleCore>; +pub type SCFQ<N> = Queue<u8, N, u8, SingleCore>; +pub type SCRQ<T, N> = Queue<(T, u8), N, u8, SingleCore>; #[cfg(armv7m)] #[inline(always)] -pub fn run<F>(f: F) +pub fn run<F>(priority: u8, f: F) where F: FnOnce(), { - let initial = basepri::read(); - f(); - unsafe { basepri::write(initial) } + 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(armv7m))] #[inline(always)] -pub fn run<F>(f: F) +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) {} + } +} + // Newtype over `Cell` that forbids mutation through a shared reference pub struct Priority { inner: Cell<u8>, @@ -52,7 +79,7 @@ impl Priority { } } - // these two methods are used by claim (see below) but can't be used from the RTFM application + // 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) @@ -64,86 +91,6 @@ impl Priority { } } -#[cfg(feature = "nightly")] -pub struct MaybeUninit<T> { - // we newtype so the end-user doesn't need `#![feature(maybe_uninit)]` in their code - inner: core::mem::MaybeUninit<T>, -} - -#[cfg(feature = "nightly")] -impl<T> MaybeUninit<T> { - pub const fn uninit() -> Self { - MaybeUninit { - inner: core::mem::MaybeUninit::uninit(), - } - } - - pub fn as_ptr(&self) -> *const T { - self.inner.as_ptr() - } - - pub fn as_mut_ptr(&mut self) -> *mut T { - self.inner.as_mut_ptr() - } - - pub fn write(&mut self, value: T) -> &mut T { - self.inner.write(value) - } -} - -#[cfg(not(feature = "nightly"))] -pub struct MaybeUninit<T> { - value: Option<T>, -} - -#[cfg(not(feature = "nightly"))] -const MSG: &str = - "you have hit a bug (UB) in RTFM implementation; try enabling this crate 'nightly' feature"; - -#[cfg(not(feature = "nightly"))] -impl<T> MaybeUninit<T> { - pub const fn uninit() -> Self { - MaybeUninit { value: None } - } - - pub fn as_ptr(&self) -> *const T { - if let Some(x) = self.value.as_ref() { - x - } else { - unreachable!(MSG) - } - } - - pub fn as_mut_ptr(&mut self) -> *mut T { - if let Some(x) = self.value.as_mut() { - x - } else { - unreachable!(MSG) - } - } - - pub unsafe fn get_ref(&self) -> &T { - if let Some(x) = self.value.as_ref() { - x - } else { - unreachable!(MSG) - } - } - - pub unsafe fn get_mut(&mut self) -> &mut T { - if let Some(x) = self.value.as_mut() { - x - } else { - unreachable!(MSG) - } - } - - pub fn write(&mut self, val: T) { - // NOTE(volatile) we have observed UB when this uses a plain `ptr::write` - unsafe { ptr::write_volatile(&mut self.value, Some(val)) } - } -} - #[inline(always)] pub fn assert_send<T>() where @@ -160,21 +107,18 @@ where #[cfg(armv7m)] #[inline(always)] -pub unsafe fn claim<T, R, F>( +pub unsafe fn lock<T, R>( ptr: *mut T, priority: &Priority, ceiling: u8, nvic_prio_bits: u8, - f: F, -) -> R -where - F: FnOnce(&mut T) -> R, -{ + f: impl FnOnce(&mut T) -> R, +) -> R { let current = priority.get(); - if priority.get() < ceiling { + 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 @@ -193,20 +137,17 @@ where #[cfg(not(armv7m))] #[inline(always)] -pub unsafe fn claim<T, R, F>( +pub unsafe fn lock<T, R>( ptr: *mut T, priority: &Priority, ceiling: u8, _nvic_prio_bits: u8, - f: F, -) -> R -where - F: FnOnce(&mut T) -> R, -{ + f: impl FnOnce(&mut T) -> R, +) -> R { let current = priority.get(); - if priority.get() < ceiling { - priority.set(u8::MAX); + if current < ceiling { + priority.set(u8::max_value()); let r = interrupt::free(|_| f(&mut *ptr)); priority.set(current); r @@ -215,8 +156,7 @@ where } } -#[cfg(armv7m)] #[inline] -fn logical2hw(logical: u8, nvic_prio_bits: u8) -> u8 { +pub fn logical2hw(logical: u8, nvic_prio_bits: u8) -> u8 { ((1 << nvic_prio_bits) - logical) << (8 - nvic_prio_bits) } @@ -1,22 +1,23 @@ -//! Real Time For the Masses (RTFM) framework for ARM Cortex-M microcontrollers +//! Real-Time Interrupt-driven Concurrency (RTIC) framework for ARM Cortex-M microcontrollers //! -//! **IMPORTANT**: This crate is published as [`cortex-m-rtfm`] on crates.io but the name of the -//! library is `rtfm`. +//! **HEADS UP** This is an **beta** pre-release; there may be breaking changes in the API and +//! semantics before a proper release is made. //! -//! [`cortex-m-rtfm`]: https://crates.io/crates/cortex-m-rtfm +//! **IMPORTANT**: This crate is published as [`cortex-m-rtic`] on crates.io but the name of the +//! library is `rtic`. //! -//! The user level documentation can be found [here]. +//! [`cortex-m-rtic`]: https://crates.io/crates/cortex-m-rtic //! -//! [here]: https://japaric.github.io/cortex-m-rtfm/book/en/ +//! The user level documentation can be found [here]. //! -//! Don't forget to check the documentation of the [`#[app]`] attribute, which is the main component -//! of the framework. +//! [here]: https://rtic.rs //! -//! [`#[app]`]: ../cortex_m_rtfm_macros/attr.app.html +//! 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 guaranteed to compile on stable Rust 1.31 (2018 edition) and up. It *might* +//! This crate is guaranteed to compile on stable Rust 1.36 (2018 edition) and up. It *might* //! compile on older versions but that may change in any new patch release. //! //! # Semantic Versioning @@ -27,73 +28,43 @@ //! release. //! //! [SemVer]: https://semver.org/spec/v2.0.0.html -//! -//! # 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 -//! `core::mem::MaybeUninit` API and 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! -#![cfg_attr(feature = "nightly", feature(maybe_uninit))] #![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}, }; -pub use cortex_m_rtfm_macros::app; +use cortex_m_rt as _; // vector table +pub use cortex_m_rtic_macros::app; +pub use rtic_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) @@ -108,253 +79,78 @@ 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 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); - } -} - -#[cfg(feature = "timer-queue")] -impl ops::Add<Duration> for Instant { - type Output = Self; - - fn add(mut self, dur: Duration) -> Self { - self += dur; - self - } -} - -#[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); - } -} - -#[cfg(feature = "timer-queue")] -impl ops::Sub<Duration> for Instant { - type Output = Self; - - 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, F>(&mut self, f: F) -> R - where - F: FnOnce(&mut Self::T) -> R; -} - -impl<'a, M> Mutex for &'a mut M -where - M: Mutex, -{ - type T = M::T; - - fn lock<R, F>(&mut self, f: F) -> R - where - F: FnOnce(&mut Self::T) -> 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, F>(&mut self, f: F) -> R - where - F: FnOnce(&mut Self::T) -> 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 - } +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, + } + } +} + +/// A fraction +pub struct Fraction { + /// The numerator + pub numerator: u32, + + /// The denominator + pub denominator: u32, +} + +/// A monotonic clock / counter +pub trait Monotonic { + /// A measurement of this clock, use `CYCCNT` as a reference implementation for `Instant`. + /// Note that the Instant must be a signed value such as `i32`. + type Instant: Copy + Ord + Sub; + + /// The ratio between the system timer (SysTick) frequency and this clock frequency, i.e. + /// `Monotonic clock * Fraction = System clock` + /// + /// The ratio must be expressed in *reduced* `Fraction` form to prevent overflows. That is + /// `2 / 3` instead of `4 / 6` + fn ratio() -> Fraction; + + /// Returns the current time + /// + /// # Correctness + /// + /// This function is *allowed* to return nonsensical values if called before `reset` is invoked + /// by the runtime. Therefore application authors should *not* call this function during the + /// `#[init]` phase. + fn now() -> Self::Instant; + + /// Resets the counter to *zero* + /// + /// # Safety + /// + /// This function will be called *exactly once* by the RTIC runtime after `#[init]` returns and + /// before tasks can start; this is also the case in multi-core applications. User code must + /// *never* call this function. + unsafe fn reset(); + + /// 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, Mutex}; +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,97 +37,111 @@ where .unwrap_or(true) { if is_empty { - self.syst.enable_interrupt(); + mem::transmute::<_, SYST>(()).enable_interrupt(); } - // set SysTick pending - (*SCB::ptr()).icsr.write(1 << 26); + // 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)> { + 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 ratio = M::ratio(); + let dur = match (instant - now).try_into().ok().and_then(|x| { + x.checked_mul(ratio.numerator) + .map(|x| x / ratio.denominator) + }) { + None => MAX, + + // ARM Architecture Reference Manual says: + // "Setting SYST_RVR to zero has the effect of + // disabling the SysTick counter independently + // of the counter enable bit." + Some(0) => 1, + + 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 { + // The queue is empty + mem::transmute::<_, 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)) } } - -#[inline(always)] -pub fn isr<TQ, T, N, F>(mut tq: TQ, mut f: F) -where - TQ: Mutex<T = TimerQueue<T, N>>, - T: Copy + Send, - N: ArrayLength<NotReady<T>>, - F: FnMut(T, u8), -{ - loop { - // XXX does `#[inline(always)]` improve performance or not? - let next = tq.lock(#[inline(always)] - |tq| { - if let Some(instant) = tq.queue.peek().map(|p| p.instant) { - let diff = instant.0.wrapping_sub(Instant::now().0); - - if diff < 0 { - // task became ready - let m = unsafe { tq.queue.pop_unchecked() }; - - Some((m.task, m.index)) - } else { - // set a new timeout - const MAX: u32 = 0x00ffffff; - - tq.syst.set_reload(cmp::min(MAX, diff as u32)); - - // start counting down from the new reload - tq.syst.clear_current(); - - None - } - } else { - // the queue is empty - tq.syst.disable_interrupt(); - None - } - }); - - if let Some((task, index)) = next { - f(task, index) - } else { - return; - } - } -} |