From 81275bfa4f41e2066770087f3a33cad4227eab41 Mon Sep 17 00:00:00 2001 From: Jorge Aparicio Date: Thu, 13 Jun 2019 23:56:59 +0200 Subject: rtfm-syntax refactor + heterogeneous multi-core support --- src/cyccnt.rs | 205 ++++++++++++++++++++++++++++++++++++++ src/export.rs | 56 ++++++++--- src/lib.rs | 308 +++++++++------------------------------------------------- src/tq.rs | 117 +++++++++++++--------- 4 files changed, 362 insertions(+), 324 deletions(-) create mode 100644 src/cyccnt.rs (limited to 'src') diff --git a/src/cyccnt.rs b/src/cyccnt.rs new file mode 100644 index 0000000..a2b216c --- /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 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 for Instant { + type Output = Self; + + fn add(mut self, dur: Duration) -> Self { + self += dur; + self + } +} + +impl ops::SubAssign 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 for Instant { + type Output = Self; + + fn sub(mut self, dur: Duration) -> Self { + self -= dur; + self + } +} + +impl ops::Sub 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 { + 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 for Duration { + type Error = Infallible; + + fn try_into(self) -> Result { + Ok(self.as_cycles()) + } +} + +impl ops::AddAssign for Duration { + fn add_assign(&mut self, dur: Duration) { + self.inner += dur.inner; + } +} + +impl ops::Add 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 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 afed909..7646e3c 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 = Queue; -pub type ReadyQueue = Queue<(T, u8), N, u8, SingleCore>; +pub type MCFQ = Queue; +pub type MCRQ = Queue<(T, u8), N, u8, MultiCore>; +pub type SCFQ = Queue; +pub type SCRQ = 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, @@ -95,7 +121,7 @@ pub unsafe fn lock( 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( 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 diff --git a/src/lib.rs b/src/lib.rs index 1fe88c4..73e6e20 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -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 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 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 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 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 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 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 { - 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 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 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(&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(&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(&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 diff --git a/src/tq.rs b/src/tq.rs index 8ca1bd3..4f9b6e7 100644 --- a/src/tq.rs +++ b/src/tq.rs @@ -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 +pub struct TimerQueue(pub BinaryHeap, N, Min>) where - N: ArrayLength>, - T: Copy, -{ - pub syst: SYST, - pub queue: BinaryHeap, N, Min>, -} + M: Monotonic, + ::Output: TryInto, + N: ArrayLength>, + T: Copy; -impl TimerQueue +impl TimerQueue where - N: ArrayLength>, + M: Monotonic, + ::Output: TryInto, + N: ArrayLength>, T: Copy, { - pub fn new(syst: SYST) -> Self { - TimerQueue { - syst, - queue: BinaryHeap::new(), - } - } - #[inline] - pub unsafe fn enqueue_unchecked(&mut self, nr: NotReady) { + pub unsafe fn enqueue_unchecked(&mut self, nr: NotReady) { 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 +pub struct NotReady where T: Copy, + M: Monotonic, + ::Output: TryInto, { pub index: u8, - pub instant: Instant, + pub instant: M::Instant, pub task: T, } -impl Eq for NotReady where T: Copy {} +impl Eq for NotReady +where + T: Copy, + M: Monotonic, + ::Output: TryInto, +{ +} -impl Ord for NotReady +impl Ord for NotReady where T: Copy, + M: Monotonic, + ::Output: TryInto, { fn cmp(&self, other: &Self) -> Ordering { self.instant.cmp(&other.instant) } } -impl PartialEq for NotReady +impl PartialEq for NotReady where T: Copy, + M: Monotonic, + ::Output: TryInto, { fn eq(&self, other: &Self) -> bool { self.instant == other.instant } } -impl PartialOrd for NotReady +impl PartialOrd for NotReady where T: Copy, + M: Monotonic, + ::Output: TryInto, { fn partial_cmp(&self, other: &Self) -> Option { Some(self.cmp(&other)) -- cgit v1.2.3 From 9897728709528a02545523bea72576abce89dc4c Mon Sep 17 00:00:00 2001 From: Jorge Aparicio Date: Tue, 18 Jun 2019 10:31:31 +0200 Subject: add homogeneous multi-core support --- Cargo.toml | 4 +- ci/script.sh | 4 +- heterogeneous/Cargo.toml | 18 +++++++ heterogeneous/README.md | 1 + heterogeneous/examples/smallest.rs | 7 +++ heterogeneous/examples/x-init-2.rs | 39 ++++++++++++++ heterogeneous/examples/x-init.rs | 26 ++++++++++ heterogeneous/examples/x-schedule.rs | 36 +++++++++++++ heterogeneous/examples/x-spawn.rs | 20 ++++++++ heterogeneous/src/lib.rs | 94 ++++++++++++++++++++++++++++++++++ homogeneous/Cargo.toml | 17 +++++++ homogeneous/README.md | 1 + homogeneous/examples/smallest.rs | 7 +++ homogeneous/examples/x-init-2.rs | 39 ++++++++++++++ homogeneous/examples/x-init.rs | 26 ++++++++++ homogeneous/examples/x-schedule.rs | 36 +++++++++++++ homogeneous/examples/x-spawn.rs | 20 ++++++++ homogeneous/src/lib.rs | 94 ++++++++++++++++++++++++++++++++++ macros/Cargo.toml | 1 + macros/src/check.rs | 22 ++++++++ macros/src/codegen.rs | 3 +- macros/src/codegen/dispatchers.rs | 10 +++- macros/src/codegen/hardware_tasks.rs | 6 ++- macros/src/codegen/post_init.rs | 18 ++++++- macros/src/codegen/pre_init.rs | 17 +++++-- macros/src/codegen/resources.rs | 8 ++- macros/src/codegen/software_tasks.rs | 8 ++- macros/src/codegen/spawn_body.rs | 5 +- macros/src/codegen/timer_queue.rs | 8 +-- macros/src/codegen/util.rs | 23 ++++++++- macros/src/lib.rs | 2 +- mc/Cargo.toml | 18 ------- mc/README.md | 1 - mc/examples/smallest.rs | 7 --- mc/examples/x-init-2.rs | 39 -------------- mc/examples/x-init.rs | 26 ---------- mc/examples/x-schedule.rs | 36 ------------- mc/examples/x-spawn.rs | 20 -------- mc/src/lib.rs | 99 ------------------------------------ src/lib.rs | 2 +- 40 files changed, 600 insertions(+), 268 deletions(-) create mode 100644 heterogeneous/Cargo.toml create mode 100644 heterogeneous/README.md create mode 100644 heterogeneous/examples/smallest.rs create mode 100644 heterogeneous/examples/x-init-2.rs create mode 100644 heterogeneous/examples/x-init.rs create mode 100644 heterogeneous/examples/x-schedule.rs create mode 100644 heterogeneous/examples/x-spawn.rs create mode 100644 heterogeneous/src/lib.rs create mode 100644 homogeneous/Cargo.toml create mode 100644 homogeneous/README.md create mode 100644 homogeneous/examples/smallest.rs create mode 100644 homogeneous/examples/x-init-2.rs create mode 100644 homogeneous/examples/x-init.rs create mode 100644 homogeneous/examples/x-schedule.rs create mode 100644 homogeneous/examples/x-spawn.rs create mode 100644 homogeneous/src/lib.rs delete mode 100644 mc/Cargo.toml delete mode 100644 mc/README.md delete mode 100644 mc/examples/smallest.rs delete mode 100644 mc/examples/x-init-2.rs delete mode 100644 mc/examples/x-init.rs delete mode 100644 mc/examples/x-schedule.rs delete mode 100644 mc/examples/x-spawn.rs delete mode 100644 mc/src/lib.rs (limited to 'src') diff --git a/Cargo.toml b/Cargo.toml index 81ca256..ef45be8 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -74,6 +74,7 @@ compiletest_rs = "0.3.22" [features] heterogeneous = ["cortex-m-rtfm-macros/heterogeneous", "microamp"] +homogeneous = ["cortex-m-rtfm-macros/homogeneous", "microamp"] # used for testing this crate; do not use in applications __v7 =[] @@ -83,6 +84,7 @@ lto = true [workspace] members = [ + "heterogeneous", + "homogeneous", "macros", - "mc", ] diff --git a/ci/script.sh b/ci/script.sh index a6485cf..1b3d561 100644 --- a/ci/script.sh +++ b/ci/script.sh @@ -43,7 +43,7 @@ main() { cargo test --test multi --features heterogeneous --target $T # multi-core compile-pass tests - pushd mc + pushd heterogeneous local exs=( smallest x-init-2 @@ -91,6 +91,8 @@ main() { cargo check --target $T --examples --features __v7 fi + cargo check -p homogeneous --target $T --examples + # run-pass tests case $T in thumbv6m-none-eabi | thumbv7m-none-eabi) diff --git a/heterogeneous/Cargo.toml b/heterogeneous/Cargo.toml new file mode 100644 index 0000000..fd05d07 --- /dev/null +++ b/heterogeneous/Cargo.toml @@ -0,0 +1,18 @@ +[package] +authors = ["Jorge Aparicio "] +edition = "2018" +name = "heterogeneous" +# this crate is only used for testing +publish = false +version = "0.0.0-alpha.0" + +[dependencies] +bare-metal = "0.2.4" + +[dependencies.cortex-m-rtfm] +path = ".." +features = ["heterogeneous"] + +[dev-dependencies] +panic-halt = "0.2.0" +microamp = "0.1.0-alpha.1" diff --git a/heterogeneous/README.md b/heterogeneous/README.md new file mode 100644 index 0000000..8e49ff8 --- /dev/null +++ b/heterogeneous/README.md @@ -0,0 +1 @@ +This directory contains *heterogeneous* multi-core compile pass tests. diff --git a/heterogeneous/examples/smallest.rs b/heterogeneous/examples/smallest.rs new file mode 100644 index 0000000..9b6bb82 --- /dev/null +++ b/heterogeneous/examples/smallest.rs @@ -0,0 +1,7 @@ +#![no_main] +#![no_std] + +use panic_halt as _; + +#[rtfm::app(cores = 2, device = heterogeneous)] +const APP: () = {}; diff --git a/heterogeneous/examples/x-init-2.rs b/heterogeneous/examples/x-init-2.rs new file mode 100644 index 0000000..b9c3919 --- /dev/null +++ b/heterogeneous/examples/x-init-2.rs @@ -0,0 +1,39 @@ +//! [compile-pass] Cross initialization of late resources + +#![deny(unsafe_code)] +#![deny(warnings)] +#![no_main] +#![no_std] + +use panic_halt as _; + +#[rtfm::app(cores = 2, device = heterogeneous)] +const APP: () = { + extern "C" { + // owned by core #1 but initialized by core #0 + static mut X: u32; + + // owned by core #0 but initialized by core #1 + static mut Y: u32; + } + + #[init(core = 0, late = [X])] + fn a(_: a::Context) -> a::LateResources { + a::LateResources { X: 0 } + } + + #[idle(core = 0, resources = [Y])] + fn b(_: b::Context) -> ! { + loop {} + } + + #[init(core = 1)] + fn c(_: c::Context) -> c::LateResources { + c::LateResources { Y: 0 } + } + + #[idle(core = 1, resources = [X])] + fn d(_: d::Context) -> ! { + loop {} + } +}; diff --git a/heterogeneous/examples/x-init.rs b/heterogeneous/examples/x-init.rs new file mode 100644 index 0000000..53e7380 --- /dev/null +++ b/heterogeneous/examples/x-init.rs @@ -0,0 +1,26 @@ +//! [compile-pass] Split initialization of late resources + +#![deny(unsafe_code)] +#![deny(warnings)] +#![no_main] +#![no_std] + +use panic_halt as _; + +#[rtfm::app(cores = 2, device = heterogeneous)] +const APP: () = { + extern "C" { + static mut X: u32; + static mut Y: u32; + } + + #[init(core = 0, late = [X])] + fn a(_: a::Context) -> a::LateResources { + a::LateResources { X: 0 } + } + + #[init(core = 1)] + fn b(_: b::Context) -> b::LateResources { + b::LateResources { Y: 0 } + } +}; diff --git a/heterogeneous/examples/x-schedule.rs b/heterogeneous/examples/x-schedule.rs new file mode 100644 index 0000000..cbfc01f --- /dev/null +++ b/heterogeneous/examples/x-schedule.rs @@ -0,0 +1,36 @@ +#![no_main] +#![no_std] + +use panic_halt as _; + +#[rtfm::app(cores = 2, device = heterogeneous, monotonic = heterogeneous::MT)] +const APP: () = { + #[init(core = 0, spawn = [ping])] + fn init(c: init::Context) { + c.spawn.ping().ok(); + } + + #[task(core = 0, schedule = [ping])] + fn pong(c: pong::Context) { + c.schedule.ping(c.scheduled + 1_000_000).ok(); + } + + #[task(core = 1, schedule = [pong])] + fn ping(c: ping::Context) { + c.schedule.pong(c.scheduled + 1_000_000).ok(); + } + + extern "C" { + #[core = 0] + fn I0(); + + #[core = 0] + fn I1(); + + #[core = 1] + fn I0(); + + #[core = 1] + fn I1(); + } +}; diff --git a/heterogeneous/examples/x-spawn.rs b/heterogeneous/examples/x-spawn.rs new file mode 100644 index 0000000..3fc64f6 --- /dev/null +++ b/heterogeneous/examples/x-spawn.rs @@ -0,0 +1,20 @@ +#![no_main] +#![no_std] + +use panic_halt as _; + +#[rtfm::app(cores = 2, device = heterogeneous)] +const APP: () = { + #[init(core = 0, spawn = [foo])] + fn init(c: init::Context) { + c.spawn.foo().ok(); + } + + #[task(core = 1)] + fn foo(_: foo::Context) {} + + extern "C" { + #[core = 1] + fn I0(); + } +}; diff --git a/heterogeneous/src/lib.rs b/heterogeneous/src/lib.rs new file mode 100644 index 0000000..a4f0ec5 --- /dev/null +++ b/heterogeneous/src/lib.rs @@ -0,0 +1,94 @@ +//! Fake multi-core PAC + +#![no_std] + +use core::{ + cmp::Ordering, + ops::{Add, Sub}, +}; + +use bare_metal::Nr; +use rtfm::Monotonic; + +// both cores have the exact same interrupts +pub use Interrupt_0 as Interrupt_1; + +// Fake priority bits +pub const NVIC_PRIO_BITS: u8 = 3; + +pub fn xpend(_core: u8, _interrupt: impl Nr) {} + +/// Fake monotonic timer +pub struct MT; + +unsafe impl Monotonic for MT { + type Instant = Instant; + + fn ratio() -> u32 { + 1 + } + + unsafe fn reset() { + (0xE0001004 as *mut u32).write_volatile(0) + } + + fn now() -> Instant { + unsafe { Instant((0xE0001004 as *const u32).read_volatile() as i32) } + } + + fn zero() -> Instant { + Instant(0) + } +} + +#[derive(Clone, Copy, Eq, PartialEq)] +pub struct Instant(i32); + +impl Add for Instant { + type Output = Instant; + + fn add(self, rhs: u32) -> Self { + Instant(self.0.wrapping_add(rhs as i32)) + } +} + +impl Sub for Instant { + type Output = u32; + + fn sub(self, rhs: Self) -> u32 { + self.0.checked_sub(rhs.0).unwrap() as u32 + } +} + +impl Ord for Instant { + fn cmp(&self, rhs: &Self) -> Ordering { + self.0.wrapping_sub(rhs.0).cmp(&0) + } +} + +impl PartialOrd for Instant { + fn partial_cmp(&self, rhs: &Self) -> Option { + Some(self.cmp(rhs)) + } +} + +// Fake interrupts +#[allow(non_camel_case_types)] +#[derive(Clone, Copy)] +#[repr(u8)] +pub enum Interrupt_0 { + I0 = 0, + I1 = 1, + I2 = 2, + I3 = 3, + I4 = 4, + I5 = 5, + I6 = 6, + I7 = 7, +} + +unsafe impl Nr for Interrupt_0 { + fn nr(&self) -> u8 { + *self as u8 + } +} diff --git a/homogeneous/Cargo.toml b/homogeneous/Cargo.toml new file mode 100644 index 0000000..210ee2e --- /dev/null +++ b/homogeneous/Cargo.toml @@ -0,0 +1,17 @@ +[package] +authors = ["Jorge Aparicio "] +edition = "2018" +name = "homogeneous" +# this crate is only used for testing +publish = false +version = "0.0.0-alpha.0" + +[dependencies] +bare-metal = "0.2.4" + +[dependencies.cortex-m-rtfm] +path = ".." +features = ["homogeneous"] + +[dev-dependencies] +panic-halt = "0.2.0" diff --git a/homogeneous/README.md b/homogeneous/README.md new file mode 100644 index 0000000..17e9c6e --- /dev/null +++ b/homogeneous/README.md @@ -0,0 +1 @@ +This directory contains *homogeneous* multi-core compile pass tests. diff --git a/homogeneous/examples/smallest.rs b/homogeneous/examples/smallest.rs new file mode 100644 index 0000000..b99476c --- /dev/null +++ b/homogeneous/examples/smallest.rs @@ -0,0 +1,7 @@ +#![no_main] +#![no_std] + +use panic_halt as _; + +#[rtfm::app(cores = 2, device = homogeneous)] +const APP: () = {}; diff --git a/homogeneous/examples/x-init-2.rs b/homogeneous/examples/x-init-2.rs new file mode 100644 index 0000000..f51e2f6 --- /dev/null +++ b/homogeneous/examples/x-init-2.rs @@ -0,0 +1,39 @@ +//! [compile-pass] Cross initialization of late resources + +#![deny(unsafe_code)] +#![deny(warnings)] +#![no_main] +#![no_std] + +use panic_halt as _; + +#[rtfm::app(cores = 2, device = homogeneous)] +const APP: () = { + extern "C" { + // owned by core #1 but initialized by core #0 + static mut X: u32; + + // owned by core #0 but initialized by core #1 + static mut Y: u32; + } + + #[init(core = 0, late = [X])] + fn a(_: a::Context) -> a::LateResources { + a::LateResources { X: 0 } + } + + #[idle(core = 0, resources = [Y])] + fn b(_: b::Context) -> ! { + loop {} + } + + #[init(core = 1)] + fn c(_: c::Context) -> c::LateResources { + c::LateResources { Y: 0 } + } + + #[idle(core = 1, resources = [X])] + fn d(_: d::Context) -> ! { + loop {} + } +}; diff --git a/homogeneous/examples/x-init.rs b/homogeneous/examples/x-init.rs new file mode 100644 index 0000000..5089e38 --- /dev/null +++ b/homogeneous/examples/x-init.rs @@ -0,0 +1,26 @@ +//! [compile-pass] Split initialization of late resources + +#![deny(unsafe_code)] +#![deny(warnings)] +#![no_main] +#![no_std] + +use panic_halt as _; + +#[rtfm::app(cores = 2, device = homogeneous)] +const APP: () = { + extern "C" { + static mut X: u32; + static mut Y: u32; + } + + #[init(core = 0, late = [X])] + fn a(_: a::Context) -> a::LateResources { + a::LateResources { X: 0 } + } + + #[init(core = 1)] + fn b(_: b::Context) -> b::LateResources { + b::LateResources { Y: 0 } + } +}; diff --git a/homogeneous/examples/x-schedule.rs b/homogeneous/examples/x-schedule.rs new file mode 100644 index 0000000..12b5cb8 --- /dev/null +++ b/homogeneous/examples/x-schedule.rs @@ -0,0 +1,36 @@ +#![no_main] +#![no_std] + +use panic_halt as _; + +#[rtfm::app(cores = 2, device = homogeneous, monotonic = homogeneous::MT)] +const APP: () = { + #[init(core = 0, spawn = [ping])] + fn init(c: init::Context) { + c.spawn.ping().ok(); + } + + #[task(core = 0, schedule = [ping])] + fn pong(c: pong::Context) { + c.schedule.ping(c.scheduled + 1_000_000).ok(); + } + + #[task(core = 1, schedule = [pong])] + fn ping(c: ping::Context) { + c.schedule.pong(c.scheduled + 1_000_000).ok(); + } + + extern "C" { + #[core = 0] + fn I0(); + + #[core = 0] + fn I1(); + + #[core = 1] + fn I0(); + + #[core = 1] + fn I1(); + } +}; diff --git a/homogeneous/examples/x-spawn.rs b/homogeneous/examples/x-spawn.rs new file mode 100644 index 0000000..a76ac61 --- /dev/null +++ b/homogeneous/examples/x-spawn.rs @@ -0,0 +1,20 @@ +#![no_main] +#![no_std] + +use panic_halt as _; + +#[rtfm::app(cores = 2, device = homogeneous)] +const APP: () = { + #[init(core = 0, spawn = [foo])] + fn init(c: init::Context) { + c.spawn.foo().ok(); + } + + #[task(core = 1)] + fn foo(_: foo::Context) {} + + extern "C" { + #[core = 1] + fn I0(); + } +}; diff --git a/homogeneous/src/lib.rs b/homogeneous/src/lib.rs new file mode 100644 index 0000000..a4f0ec5 --- /dev/null +++ b/homogeneous/src/lib.rs @@ -0,0 +1,94 @@ +//! Fake multi-core PAC + +#![no_std] + +use core::{ + cmp::Ordering, + ops::{Add, Sub}, +}; + +use bare_metal::Nr; +use rtfm::Monotonic; + +// both cores have the exact same interrupts +pub use Interrupt_0 as Interrupt_1; + +// Fake priority bits +pub const NVIC_PRIO_BITS: u8 = 3; + +pub fn xpend(_core: u8, _interrupt: impl Nr) {} + +/// Fake monotonic timer +pub struct MT; + +unsafe impl Monotonic for MT { + type Instant = Instant; + + fn ratio() -> u32 { + 1 + } + + unsafe fn reset() { + (0xE0001004 as *mut u32).write_volatile(0) + } + + fn now() -> Instant { + unsafe { Instant((0xE0001004 as *const u32).read_volatile() as i32) } + } + + fn zero() -> Instant { + Instant(0) + } +} + +#[derive(Clone, Copy, Eq, PartialEq)] +pub struct Instant(i32); + +impl Add for Instant { + type Output = Instant; + + fn add(self, rhs: u32) -> Self { + Instant(self.0.wrapping_add(rhs as i32)) + } +} + +impl Sub for Instant { + type Output = u32; + + fn sub(self, rhs: Self) -> u32 { + self.0.checked_sub(rhs.0).unwrap() as u32 + } +} + +impl Ord for Instant { + fn cmp(&self, rhs: &Self) -> Ordering { + self.0.wrapping_sub(rhs.0).cmp(&0) + } +} + +impl PartialOrd for Instant { + fn partial_cmp(&self, rhs: &Self) -> Option { + Some(self.cmp(rhs)) + } +} + +// Fake interrupts +#[allow(non_camel_case_types)] +#[derive(Clone, Copy)] +#[repr(u8)] +pub enum Interrupt_0 { + I0 = 0, + I1 = 1, + I2 = 2, + I3 = 3, + I4 = 4, + I5 = 5, + I6 = 6, + I7 = 7, +} + +unsafe impl Nr for Interrupt_0 { + fn nr(&self) -> u8 { + *self as u8 + } +} diff --git a/macros/Cargo.toml b/macros/Cargo.toml index 2854dad..c4e897f 100644 --- a/macros/Cargo.toml +++ b/macros/Cargo.toml @@ -24,3 +24,4 @@ git = "https://github.com/japaric/rtfm-syntax" [features] heterogeneous = [] +homogeneous = [] diff --git a/macros/src/check.rs b/macros/src/check.rs index c22a0f1..619ec8f 100644 --- a/macros/src/check.rs +++ b/macros/src/check.rs @@ -20,6 +20,28 @@ impl<'a> Extra<'a> { } pub fn app<'a>(app: &'a App, analysis: &Analysis) -> parse::Result> { + if cfg!(feature = "homogeneous") { + // this RTFM mode uses the same namespace for all cores so we need to check that the + // identifiers used for each core `#[init]` and `#[idle]` functions don't collide + let mut seen = HashSet::new(); + + for name in app + .inits + .values() + .map(|init| &init.name) + .chain(app.idles.values().map(|idle| &idle.name)) + { + if seen.contains(name) { + return Err(parse::Error::new( + name.span(), + "this identifier is already being used by another core", + )); + } else { + seen.insert(name); + } + } + } + // check that all exceptions are valid; only exceptions with configurable priorities are // accepted for (name, task) in app diff --git a/macros/src/codegen.rs b/macros/src/codegen.rs index 86b4a67..9276626 100644 --- a/macros/src/codegen.rs +++ b/macros/src/codegen.rs @@ -67,10 +67,11 @@ pub fn app(app: &App, analysis: &Analysis, extra: &Extra) -> TokenStream2 { )); let cfg_core = util::cfg_core(core, app.args.cores); + let main = util::suffixed("main", core); mains.push(quote!( #[no_mangle] #cfg_core - unsafe fn main() -> ! { + unsafe extern "C" fn #main() -> ! { #(#assertion_stmts)* #(#pre_init_stmts)* diff --git a/macros/src/codegen/dispatchers.rs b/macros/src/codegen/dispatchers.rs index 65d25c7..988e3c8 100644 --- a/macros/src/codegen/dispatchers.rs +++ b/macros/src/codegen/dispatchers.rs @@ -55,8 +55,14 @@ pub fn codegen(app: &App, analysis: &Analysis, extra: &Extra) -> Vec), quote!(rtfm::export::Queue(rtfm::export::iQueue::u8())), ) @@ -156,7 +162,7 @@ pub fn codegen(app: &App, analysis: &Analysis, extra: &Extra) -> Vec util::cfg_core(*core, app.args.cores), // shared `static`s and cross-initialized resources need to be in `.shared` memory - _ => Some(quote!(#[rtfm::export::shared])), + _ => { + if cfg!(feature = "heterogeneous") { + Some(quote!(#[rtfm::export::shared])) + } else { + None + } + } }; let (ty, expr) = if let Some(expr) = expr { diff --git a/macros/src/codegen/software_tasks.rs b/macros/src/codegen/software_tasks.rs index 8b2c0cd..383a5d8 100644 --- a/macros/src/codegen/software_tasks.rs +++ b/macros/src/codegen/software_tasks.rs @@ -52,8 +52,14 @@ pub fn codegen( })), ) } else { + let shared = if cfg!(feature = "heterogeneous") { + Some(quote!(#[rtfm::export::shared])) + } else { + None + }; + ( - Some(quote!(#[rtfm::export::shared])), + shared, quote!(rtfm::export::MCFQ<#cap_ty>), quote!(rtfm::export::Queue(rtfm::export::iQueue::u8())), ) diff --git a/macros/src/codegen/spawn_body.rs b/macros/src/codegen/spawn_body.rs index 83cb5c0..98bce07 100644 --- a/macros/src/codegen/spawn_body.rs +++ b/macros/src/codegen/spawn_body.rs @@ -45,14 +45,15 @@ pub fn codegen( }; let device = extra.device; + let enum_ = util::interrupt_ident(receiver, app.args.cores); let interrupt = &analysis.interrupts[&receiver][&priority]; let pend = if sender != receiver { quote!( - #device::xpend(#receiver, #device::Interrupt::#interrupt); + #device::xpend(#receiver, #device::#enum_::#interrupt); ) } else { quote!( - rtfm::pend(#device::Interrupt::#interrupt); + rtfm::pend(#device::#enum_::#interrupt); ) }; diff --git a/macros/src/codegen/timer_queue.rs b/macros/src/codegen/timer_queue.rs index cb84577..d306ed5 100644 --- a/macros/src/codegen/timer_queue.rs +++ b/macros/src/codegen/timer_queue.rs @@ -89,15 +89,16 @@ pub fn codegen(app: &App, analysis: &Analysis, extra: &Extra) -> Vec Vec>(); let priority = timer_queue.priority; + let sys_tick = util::suffixed("SysTick", sender); items.push(quote!( #cfg_sender #[no_mangle] - unsafe fn SysTick() { + unsafe fn #sys_tick() { use rtfm::Mutex as _; /// The priority of this handler diff --git a/macros/src/codegen/util.rs b/macros/src/codegen/util.rs index 203fcee..8c43b35 100644 --- a/macros/src/codegen/util.rs +++ b/macros/src/codegen/util.rs @@ -27,9 +27,11 @@ pub fn capacity_typenum(capacity: u8, round_up_to_power_of_two: bool) -> TokenSt pub fn cfg_core(core: Core, cores: u8) -> Option { if cores == 1 { None - } else { + } else if cfg!(feature = "heterogeneous") { let core = core.to_string(); Some(quote!(#[cfg(core = #core)])) + } else { + None } } @@ -102,6 +104,15 @@ pub fn instants_ident(task: &Ident, sender: Core) -> Ident { Ident::new(&format!("{}_S{}_INSTANTS", task, sender), Span::call_site()) } +pub fn interrupt_ident(core: Core, cores: u8) -> Ident { + let span = Span::call_site(); + if cores == 1 { + Ident::new("Interrupt", span) + } else { + Ident::new(&format!("Interrupt_{}", core), span) + } +} + /// Generates a pre-reexport identifier for the "late resources" struct pub fn late_resources_ident(init: &Ident) -> Ident { Ident::new( @@ -245,6 +256,16 @@ pub fn spawn_t_ident(receiver: Core, priority: u8, sender: Core) -> Ident { ) } +pub fn suffixed(name: &str, core: u8) -> Ident { + let span = Span::call_site(); + + if cfg!(feature = "homogeneous") { + Ident::new(&format!("{}_{}", name, core), span) + } else { + Ident::new(name, span) + } +} + /// Generates an identifier for a timer queue /// /// At most there's one timer queue per core diff --git a/macros/src/lib.rs b/macros/src/lib.rs index 6e1a797..6502d9c 100644 --- a/macros/src/lib.rs +++ b/macros/src/lib.rs @@ -20,7 +20,7 @@ pub fn app(args: TokenStream, input: TokenStream) -> TokenStream { args, input, Settings { - parse_cores: cfg!(feature = "heterogeneous"), + parse_cores: cfg!(feature = "heterogeneous") || cfg!(feature = "homogeneous"), parse_exception: true, parse_extern_interrupt: true, parse_interrupt: true, diff --git a/mc/Cargo.toml b/mc/Cargo.toml deleted file mode 100644 index 7c75335..0000000 --- a/mc/Cargo.toml +++ /dev/null @@ -1,18 +0,0 @@ -[package] -authors = ["Jorge Aparicio "] -edition = "2018" -name = "mc" -# this crate is only used for testing -publish = false -version = "0.0.0-alpha.0" - -[dependencies] -cortex-m = "0.6.0" - -[dependencies.cortex-m-rtfm] -path = ".." -features = ["heterogeneous"] - -[dev-dependencies] -panic-halt = "0.2.0" -microamp = "0.1.0-alpha.1" diff --git a/mc/README.md b/mc/README.md deleted file mode 100644 index e1335bb..0000000 --- a/mc/README.md +++ /dev/null @@ -1 +0,0 @@ -This directory contains multi-core compile pass tests. diff --git a/mc/examples/smallest.rs b/mc/examples/smallest.rs deleted file mode 100644 index 792935a..0000000 --- a/mc/examples/smallest.rs +++ /dev/null @@ -1,7 +0,0 @@ -#![no_main] -#![no_std] - -use panic_halt as _; - -#[rtfm::app(cores = 2, device = mc)] -const APP: () = {}; diff --git a/mc/examples/x-init-2.rs b/mc/examples/x-init-2.rs deleted file mode 100644 index ff48b11..0000000 --- a/mc/examples/x-init-2.rs +++ /dev/null @@ -1,39 +0,0 @@ -//! [compile-pass] Cross initialization of late resources - -#![deny(unsafe_code)] -#![deny(warnings)] -#![no_main] -#![no_std] - -use panic_halt as _; - -#[rtfm::app(cores = 2, device = mc)] -const APP: () = { - extern "C" { - // owned by core #1 but initialized by core #0 - static mut X: u32; - - // owned by core #0 but initialized by core #1 - static mut Y: u32; - } - - #[init(core = 0, late = [X])] - fn a(_: a::Context) -> a::LateResources { - a::LateResources { X: 0 } - } - - #[idle(core = 0, resources = [Y])] - fn b(_: b::Context) -> ! { - loop {} - } - - #[init(core = 1)] - fn c(_: c::Context) -> c::LateResources { - c::LateResources { Y: 0 } - } - - #[idle(core = 1, resources = [X])] - fn d(_: d::Context) -> ! { - loop {} - } -}; diff --git a/mc/examples/x-init.rs b/mc/examples/x-init.rs deleted file mode 100644 index 3f26c5c..0000000 --- a/mc/examples/x-init.rs +++ /dev/null @@ -1,26 +0,0 @@ -//! [compile-pass] Split initialization of late resources - -#![deny(unsafe_code)] -#![deny(warnings)] -#![no_main] -#![no_std] - -use panic_halt as _; - -#[rtfm::app(cores = 2, device = mc)] -const APP: () = { - extern "C" { - static mut X: u32; - static mut Y: u32; - } - - #[init(core = 0, late = [X])] - fn a(_: a::Context) -> a::LateResources { - a::LateResources { X: 0 } - } - - #[init(core = 1)] - fn b(_: b::Context) -> b::LateResources { - b::LateResources { Y: 0 } - } -}; diff --git a/mc/examples/x-schedule.rs b/mc/examples/x-schedule.rs deleted file mode 100644 index 76e70ac..0000000 --- a/mc/examples/x-schedule.rs +++ /dev/null @@ -1,36 +0,0 @@ -#![no_main] -#![no_std] - -use panic_halt as _; - -#[rtfm::app(cores = 2, device = mc, monotonic = mc::MT)] -const APP: () = { - #[init(core = 0, spawn = [ping])] - fn init(c: init::Context) { - c.spawn.ping().ok(); - } - - #[task(core = 0, schedule = [ping])] - fn pong(c: pong::Context) { - c.schedule.ping(c.scheduled + 1_000_000).ok(); - } - - #[task(core = 1, schedule = [pong])] - fn ping(c: ping::Context) { - c.schedule.pong(c.scheduled + 1_000_000).ok(); - } - - extern "C" { - #[core = 0] - fn I0(); - - #[core = 0] - fn I1(); - - #[core = 1] - fn I0(); - - #[core = 1] - fn I1(); - } -}; diff --git a/mc/examples/x-spawn.rs b/mc/examples/x-spawn.rs deleted file mode 100644 index 749918f..0000000 --- a/mc/examples/x-spawn.rs +++ /dev/null @@ -1,20 +0,0 @@ -#![no_main] -#![no_std] - -use panic_halt as _; - -#[rtfm::app(cores = 2, device = mc)] -const APP: () = { - #[init(core = 0, spawn = [foo])] - fn init(c: init::Context) { - c.spawn.foo().ok(); - } - - #[task(core = 1)] - fn foo(_: foo::Context) {} - - extern "C" { - #[core = 1] - fn I0(); - } -}; diff --git a/mc/src/lib.rs b/mc/src/lib.rs deleted file mode 100644 index d86c0e8..0000000 --- a/mc/src/lib.rs +++ /dev/null @@ -1,99 +0,0 @@ -//! Fake multi-core PAC - -#![no_std] - -use core::{ - cmp::Ordering, - ops::{Add, Sub}, -}; - -use cortex_m::interrupt::Nr; -use rtfm::Monotonic; - -// Fake priority bits -pub const NVIC_PRIO_BITS: u8 = 3; - -pub struct CrossPend; - -pub fn xpend(_core: u8, _interrupt: impl Nr) {} - -/// Fake monotonic timer -pub struct MT; - -unsafe impl Monotonic for MT { - type Instant = Instant; - - fn ratio() -> u32 { - 1 - } - - unsafe fn reset() { - (0xE0001004 as *mut u32).write_volatile(0) - } - - fn now() -> Instant { - unsafe { Instant((0xE0001004 as *const u32).read_volatile() as i32) } - } - - fn zero() -> Instant { - Instant(0) - } -} - -#[derive(Clone, Copy, Eq, PartialEq)] -pub struct Instant(i32); - -impl Add for Instant { - type Output = Instant; - - fn add(self, rhs: u32) -> Self { - Instant(self.0.wrapping_add(rhs as i32)) - } -} - -impl Sub for Instant { - type Output = u32; - - fn sub(self, rhs: Self) -> u32 { - self.0.checked_sub(rhs.0).unwrap() as u32 - } -} - -impl Ord for Instant { - fn cmp(&self, rhs: &Self) -> Ordering { - self.0.wrapping_sub(rhs.0).cmp(&0) - } -} - -impl PartialOrd for Instant { - fn partial_cmp(&self, rhs: &Self) -> Option { - Some(self.cmp(rhs)) - } -} - -// Fake interrupts -pub enum Interrupt { - I0, - I1, - I2, - I3, - I4, - I5, - I6, - I7, -} - -unsafe impl Nr for Interrupt { - fn nr(&self) -> u8 { - match self { - Interrupt::I0 => 0, - Interrupt::I1 => 1, - Interrupt::I2 => 2, - Interrupt::I3 => 3, - Interrupt::I4 => 4, - Interrupt::I5 => 5, - Interrupt::I6 => 6, - Interrupt::I7 => 7, - } - } -} diff --git a/src/lib.rs b/src/lib.rs index 73e6e20..acb3a63 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -47,7 +47,7 @@ use cortex_m::{ interrupt::Nr, peripheral::{CBP, CPUID, DCB, DWT, FPB, FPU, ITM, MPU, NVIC, SCB, TPIU}, }; -#[cfg(not(feature = "heterogeneous"))] +#[cfg(all(not(feature = "heterogeneous"), not(feature = "homogeneous")))] use cortex_m_rt as _; // vector table pub use cortex_m_rtfm_macros::app; pub use rtfm_core::{Exclusive, Mutex}; -- cgit v1.2.3 From 596cf585ea8dc278d88e0652dffbacbc75de04c6 Mon Sep 17 00:00:00 2001 From: Jorge Aparicio Date: Mon, 24 Jun 2019 14:09:12 +0200 Subject: Monotonic trait is safe; add MultiCore trait --- heterogeneous/src/lib.rs | 6 ++++-- homogeneous/src/lib.rs | 6 ++++-- macros/src/codegen.rs | 2 +- macros/src/codegen/assertions.rs | 11 +++++++++-- src/cyccnt.rs | 7 ++++++- src/export.rs | 7 +++++++ src/lib.rs | 5 ++++- 7 files changed, 35 insertions(+), 9 deletions(-) (limited to 'src') diff --git a/heterogeneous/src/lib.rs b/heterogeneous/src/lib.rs index a4f0ec5..3288bfe 100644 --- a/heterogeneous/src/lib.rs +++ b/heterogeneous/src/lib.rs @@ -8,7 +8,7 @@ use core::{ }; use bare_metal::Nr; -use rtfm::Monotonic; +use rtfm::{Monotonic, MultiCore}; // both cores have the exact same interrupts pub use Interrupt_0 as Interrupt_1; @@ -21,7 +21,7 @@ pub fn xpend(_core: u8, _interrupt: impl Nr) {} /// Fake monotonic timer pub struct MT; -unsafe impl Monotonic for MT { +impl Monotonic for MT { type Instant = Instant; fn ratio() -> u32 { @@ -41,6 +41,8 @@ unsafe impl Monotonic for MT { } } +impl MultiCore for MT {} + #[derive(Clone, Copy, Eq, PartialEq)] pub struct Instant(i32); diff --git a/homogeneous/src/lib.rs b/homogeneous/src/lib.rs index a4f0ec5..3288bfe 100644 --- a/homogeneous/src/lib.rs +++ b/homogeneous/src/lib.rs @@ -8,7 +8,7 @@ use core::{ }; use bare_metal::Nr; -use rtfm::Monotonic; +use rtfm::{Monotonic, MultiCore}; // both cores have the exact same interrupts pub use Interrupt_0 as Interrupt_1; @@ -21,7 +21,7 @@ pub fn xpend(_core: u8, _interrupt: impl Nr) {} /// Fake monotonic timer pub struct MT; -unsafe impl Monotonic for MT { +impl Monotonic for MT { type Instant = Instant; fn ratio() -> u32 { @@ -41,6 +41,8 @@ unsafe impl Monotonic for MT { } } +impl MultiCore for MT {} + #[derive(Clone, Copy, Eq, PartialEq)] pub struct Instant(i32); diff --git a/macros/src/codegen.rs b/macros/src/codegen.rs index 9276626..a351599 100644 --- a/macros/src/codegen.rs +++ b/macros/src/codegen.rs @@ -32,7 +32,7 @@ pub fn app(app: &App, analysis: &Analysis, extra: &Extra) -> TokenStream2 { // generate a `main` function for each core for core in 0..app.args.cores { - let assertion_stmts = assertions::codegen(core, analysis); + let assertion_stmts = assertions::codegen(core, analysis, extra); let (const_app_pre_init, pre_init_stmts) = pre_init::codegen(core, &app, analysis, extra); diff --git a/macros/src/codegen/assertions.rs b/macros/src/codegen/assertions.rs index 95268a2..4a77352 100644 --- a/macros/src/codegen/assertions.rs +++ b/macros/src/codegen/assertions.rs @@ -1,10 +1,10 @@ use proc_macro2::TokenStream as TokenStream2; use quote::quote; -use crate::analyze::Analysis; +use crate::{analyze::Analysis, check::Extra}; /// Generates compile-time assertions that check that types implement the `Send` / `Sync` traits -pub fn codegen(core: u8, analysis: &Analysis) -> Vec { +pub fn codegen(core: u8, analysis: &Analysis, extra: &Extra) -> Vec { let mut stmts = vec![]; // we don't generate *all* assertions on all cores because the user could conditionally import a @@ -22,5 +22,12 @@ pub fn codegen(core: u8, analysis: &Analysis) -> Vec { } } + // if the `schedule` API is used in more than one core then we need to check that the + // `monotonic` timer can be used in multi-core context + if analysis.timer_queues.len() > 1 && analysis.timer_queues.contains_key(&core) { + let monotonic = extra.monotonic(); + stmts.push(quote!(rtfm::export::assert_multicore::<#monotonic>();)); + } + stmts } diff --git a/src/cyccnt.rs b/src/cyccnt.rs index a2b216c..468aa71 100644 --- a/src/cyccnt.rs +++ b/src/cyccnt.rs @@ -116,6 +116,11 @@ pub struct Duration { } 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 @@ -181,7 +186,7 @@ impl U32Ext for u32 { pub struct CYCCNT; #[cfg(not(feature = "heterogeneous"))] -unsafe impl crate::Monotonic for CYCCNT { +impl crate::Monotonic for CYCCNT { type Instant = Instant; fn ratio() -> u32 { diff --git a/src/export.rs b/src/export.rs index 7646e3c..572068c 100644 --- a/src/export.rs +++ b/src/export.rs @@ -108,6 +108,13 @@ where { } +#[inline(always)] +pub fn assert_multicore() +where + T: super::MultiCore, +{ +} + #[cfg(armv7m)] #[inline(always)] pub unsafe fn lock( diff --git a/src/lib.rs b/src/lib.rs index acb3a63..decd2da 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -117,7 +117,7 @@ impl From for Peripherals { } /// A monotonic clock / counter -pub unsafe trait Monotonic { +pub trait Monotonic { /// A measurement of this clock type Instant: Copy + Ord + Sub; @@ -134,6 +134,9 @@ pub unsafe trait Monotonic { fn zero() -> Self::Instant; } +/// A marker trait that indicates that it is correct to use this type in multi-core context +pub trait MultiCore {} + /// Sets the given `interrupt` as pending /// /// This is a convenience function around -- cgit v1.2.3 From a87cb2486f488666450636c9cb68f79681f5f358 Mon Sep 17 00:00:00 2001 From: Jorge Aparicio Date: Thu, 11 Jul 2019 13:28:25 +0200 Subject: change Monotonic::ratio return type to Fraction --- heterogeneous/src/lib.rs | 9 ++++++--- homogeneous/src/lib.rs | 9 ++++++--- src/cyccnt.rs | 16 +++++++++++----- src/lib.rs | 14 +++++++++++++- src/tq.rs | 10 +++++----- 5 files changed, 41 insertions(+), 17 deletions(-) (limited to 'src') diff --git a/heterogeneous/src/lib.rs b/heterogeneous/src/lib.rs index 3288bfe..95ff184 100644 --- a/heterogeneous/src/lib.rs +++ b/heterogeneous/src/lib.rs @@ -8,7 +8,7 @@ use core::{ }; use bare_metal::Nr; -use rtfm::{Monotonic, MultiCore}; +use rtfm::{Fraction, Monotonic, MultiCore}; // both cores have the exact same interrupts pub use Interrupt_0 as Interrupt_1; @@ -24,8 +24,11 @@ pub struct MT; impl Monotonic for MT { type Instant = Instant; - fn ratio() -> u32 { - 1 + fn ratio() -> Fraction { + Fraction { + numerator: 1, + denominator: 1, + } } unsafe fn reset() { diff --git a/homogeneous/src/lib.rs b/homogeneous/src/lib.rs index 3288bfe..95ff184 100644 --- a/homogeneous/src/lib.rs +++ b/homogeneous/src/lib.rs @@ -8,7 +8,7 @@ use core::{ }; use bare_metal::Nr; -use rtfm::{Monotonic, MultiCore}; +use rtfm::{Fraction, Monotonic, MultiCore}; // both cores have the exact same interrupts pub use Interrupt_0 as Interrupt_1; @@ -24,8 +24,11 @@ pub struct MT; impl Monotonic for MT { type Instant = Instant; - fn ratio() -> u32 { - 1 + fn ratio() -> Fraction { + Fraction { + numerator: 1, + denominator: 1, + } } unsafe fn reset() { diff --git a/src/cyccnt.rs b/src/cyccnt.rs index 468aa71..c8a1b7e 100644 --- a/src/cyccnt.rs +++ b/src/cyccnt.rs @@ -10,9 +10,15 @@ use core::{ 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 +/// +/// Note that this value is tied to the CYCCNT of one core and that sending it a different core +/// makes it lose its meaning -- each Cortex-M core has its own CYCCNT counter and these are usually +/// unsynchronized and they may even be running at different frequencies. #[derive(Clone, Copy, Eq, PartialEq)] pub struct Instant { inner: i32, @@ -21,7 +27,6 @@ pub struct Instant { unsafe impl Sync for Instant {} -#[cfg(not(feature = "heterogeneous"))] unsafe impl Send for Instant {} impl Instant { @@ -182,15 +187,16 @@ impl U32Ext for u32 { } /// Implementation of the `Monotonic` trait based on CYCle CouNTer -#[cfg(not(feature = "heterogeneous"))] pub struct CYCCNT; -#[cfg(not(feature = "heterogeneous"))] impl crate::Monotonic for CYCCNT { type Instant = Instant; - fn ratio() -> u32 { - 1 + fn ratio() -> Fraction { + Fraction { + numerator: 1, + denominator: 1, + } } unsafe fn reset() { diff --git a/src/lib.rs b/src/lib.rs index decd2da..22eff5a 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -116,13 +116,25 @@ impl From for Peripherals { } } +/// 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 type Instant: Copy + Ord + Sub; /// The ratio between the SysTick (system timer) frequency and this clock frequency - fn ratio() -> u32; + /// + /// 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 fn now() -> Self::Instant; diff --git a/src/tq.rs b/src/tq.rs index 4f9b6e7..4edb40a 100644 --- a/src/tq.rs +++ b/src/tq.rs @@ -62,11 +62,11 @@ where // set a new timeout const MAX: u32 = 0x00ffffff; - let dur = match (instant - now) - .try_into() - .ok() - .and_then(|x| x.checked_mul(M::ratio())) - { + 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, Some(x) => cmp::min(MAX, x), }; -- cgit v1.2.3 From 45f9faae9c51c8d84c939a9e00985388fc1d2cdf Mon Sep 17 00:00:00 2001 From: Jorge Aparicio Date: Wed, 21 Aug 2019 12:19:38 +0200 Subject: document #[app] --- Cargo.toml | 2 +- macros/src/lib.rs | 188 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ src/lib.rs | 8 +-- 3 files changed, 192 insertions(+), 6 deletions(-) (limited to 'src') diff --git a/Cargo.toml b/Cargo.toml index 5541717..1449d6e 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -12,7 +12,7 @@ license = "MIT OR Apache-2.0" name = "cortex-m-rtfm" readme = "README.md" repository = "https://github.com/japaric/cortex-m-rtfm" -version = "0.5.0-alpha.1" +version = "0.5.0-beta.1" [lib] name = "rtfm" diff --git a/macros/src/lib.rs b/macros/src/lib.rs index 7a436e7..6675765 100644 --- a/macros/src/lib.rs +++ b/macros/src/lib.rs @@ -13,6 +13,194 @@ mod codegen; #[cfg(test)] mod tests; +/// Attribute used to declare a RTFM application +/// +/// This attribute must be applied to a `const` item of type `()`. The `const` item is effectively +/// used as a `mod` item: its value must be a block that contains items commonly found in modules, +/// like functions and `static` variables. +/// +/// The `app` attribute has one mandatory argument: +/// +/// - `device = `. The path must point to a device crate generated using [`svd2rust`] +/// **v0.14.x**. +/// +/// [`svd2rust`]: https://crates.io/crates/svd2rust +/// +/// and several optional arguments: +/// +/// - `peripherals = `. Indicates whether the runtime takes the device peripherals and makes +/// them available to the `init` context. +/// +/// - `monotonic = `. This is a path to a zero-sized structure (e.g. `struct Foo;`) that +/// implements the `Monotonic` trait. This argument must be provided to use the `schedule` API. +/// +/// The items allowed in the block value of the `const` item are specified below: +/// +/// # 1. `struct Resources` +/// +/// This structure contains the declaration of all the resources used by the application. Each field +/// in this structure corresponds to a different resource. Each resource may optionally be given an +/// initial value using the `#[init()]` attribute. Resources with no compile-time initial +/// value as referred to as *late* resources. +/// +/// # 2. `fn` +/// +/// Functions must contain *one* of the following attributes: `init`, `idle` or `task`. The +/// attribute defines the role of the function in the application. +/// +/// ## a. `#[init]` +/// +/// This attribute indicates that the function is to be used as the *initialization function*. There +/// must be exactly one instance of the `init` attribute inside the `app` pseudo-module. The +/// signature of the `init` function must be `fn (::Context) [-> ::LateResources]` +/// where `` is the name of the function adorned with the `#[init]` attribute. +/// +/// The `init` function runs after memory (RAM) is initialized and runs with interrupts disabled. +/// Interrupts are re-enabled after `init` returns. +/// +/// The `init` attribute accepts the following optional arguments: +/// +/// - `resources = [resource_a, resource_b, ..]`. This is the list of resources this context has +/// access to. +/// +/// - `schedule = [task_a, task_b, ..]`. This is the list of *software* tasks that this context can +/// schedule to run in the future. *IMPORTANT*: This argument is accepted only if the `monotonic` +/// argument is passed to the `#[app]` attribute. +/// +/// - `spawn = [task_a, task_b, ..]`. This is the list of *software* tasks that this context can +/// immediately spawn. +/// +/// The first argument of the function, `::Context`, is a structure that contains the +/// following fields: +/// +/// - `core`. Exclusive access to core peripherals. The type of this field is [`rtfm::Peripherals`] +/// when the `schedule` API is used and [`cortex_m::Peripherals`] when it's not. +/// +/// [`rtfm::Peripherals`]: ../rtfm/struct.Peripherals.html +/// [`cortex_m::Peripherals`]: https://docs.rs/cortex-m/0.6/cortex_m/peripheral/struct.Peripherals.html +/// +/// - `device: ::Peripherals`. Exclusive access to device-specific peripherals. This +/// field is only present when the `peripherals` argument of the `#[app]` attribute is set to +/// `true`. `` is the path to the device crate specified in the top `app` attribute. +/// +/// - `start: `. The `start` time of the system: `::zero()`. `` is the +/// `Instant` type associated to the `Monotonic` implementation specified in the top `#[app]` +/// attribute. **NOTE**: this field is only present when the `schedule` is used. +/// +/// - `resources: ::Resources`. A `struct` that contains all the resources that can be +/// accessed from this context. Each field is a different resource; each resource may appear as a +/// reference (`&[mut]-`) or as proxy structure that implements the [`rftm::Mutex`] trait. +/// +/// [`rtfm::Mutex`]: ../rtfm/trait.Mutex.html +/// +/// - `schedule: ::Schedule`. A `struct` that can be used to schedule *software* tasks. +/// +/// - `spawn: ::Spawn`. A `struct` that can be used to spawn *software* tasks. +/// +/// The return type `::LateResources` must only be specified when late resources, resources +/// with no initial value declared at compile time, are used. `::LateResources` is a +/// structure where each field corresponds to a different late resource. The +/// `::LateResources` value returned by the `#[init]` function is used to initialize the +/// late resources before `idle` or any task can start. +/// +/// Other properties: +/// +/// - The `static mut` variables declared at the beginning of this function will be transformed into +/// `&'static mut` references that are safe to access. For example, `static mut FOO: u32 = 0` will +/// become `FOO: &'static mut u32`. +/// +/// ## b. `#[idle]` +/// +/// This attribute indicates that the function is to be used as the *idle task*. There can be at +/// most once instance of the `idle` attribute inside the `app` pseudo-module. The signature of the +/// `idle` function must be `fn(::Context) -> !` where `` is the name of the +/// function adorned with the `#[idle]` attribute. +/// +/// The `idle` task is a special task that always runs in the background. The `idle` task runs at +/// the lowest priority of `0`. If the `idle` task is not defined then the runtime sets the +/// [SLEEPONEXIT] bit after executing `init`. +/// +/// [SLEEPONEXIT]: https://developer.arm.com/products/architecture/cpu-architecture/m-profile/docs/100737/0100/power-management/sleep-mode/sleep-on-exit-bit +/// +/// The `idle` attribute accepts the following optional arguments: +/// +/// - `resources = (..)`. Same meaning / function as [`#[init].resources`](#a-init). +/// +/// - `schedule = (..)`. Same meaning / function as [`#[init].schedule`](#a-init). +/// +/// - `spawn = (..)`. Same meaning / function as [`#[init].spawn`](#a-init). +/// +/// The first argument of the function, `idle::Context`, is a structure that contains the following +/// fields: +/// +/// - `resources: _`. Same meaning / function as [`::Context.resources`](#a-init). +/// +/// - `schedule: idle::Schedule`. Same meaning / function as [`::Context.schedule`](#a-init). +/// +/// - `spawn: idle::Spawn`. Same meaning / function as [`::Context.spawn`](#a-init). +/// +/// Other properties: +/// +/// - The `static mut` variables declared at the beginning of this function will be transformed into +/// `&'static mut` references that are safe to access. For example, `static mut FOO: u32 = 0` will +/// become `FOO: &'static mut u32`. +/// +/// ## c. `#[task]` +/// +/// This attribute indicates that the function is either a hardware task or a software task. The +/// signature of hardware tasks must be `fn(::Context)` whereas the signature of software +/// tasks must be `fn(::Context, )`. `` refers to the name of the function +/// adorned with the `#[task]` attribute. +/// +/// The `task` attribute accepts the following optional arguments. +/// +/// - `binds = `. Binds this task to a particular interrupt. When this argument is +/// present the task is treated as a hardware task; when it's omitted the task treated is treated as +/// a software task. +/// +/// - `priority = `. This is the static priority of the exception handler. The value must +/// be in the range `1..=(1 << ::NVIC_PRIO_BITS)` where `` is the path to +/// the device crate specified in the top `app` attribute. If this argument is omitted the priority +/// is assumed to be 1. +/// +/// - `resources = (..)`. Same meaning / function as [`#[init].resources`](#a-init). +/// +/// - `schedule = (..)`. Same meaning / function as [`#[init].schedule`](#a-init). +/// +/// - `spawn = (..)`. Same meaning / function as [`#[init].spawn`](#a-init). +/// +/// The first argument of the function, `::Context`, is a structure that contains the +/// following fields: +/// +/// - `start: `. For hardware tasks this is the time at which this handler started +/// executing. For software tasks this is the time at which the task was scheduled to run. **NOTE**: +/// only present when the `schedule` API is used. +/// +/// - `resources: _`. Same meaning / function as [`::Context.resources`](#a-init). +/// +/// - `schedule: ::Schedule`. Same meaning / function as +/// [`::Context.schedule`](#a-init). +/// +/// - `spawn: ::Spawn`. Same meaning / function as +/// [`::Context.spawn`](#a-init). +/// +/// Other properties / constraints: +/// +/// - The `static mut` variables declared at the beginning of this function will be transformed into +/// *non*-static `&mut` references that are safe to access. For example, `static mut FOO: u32 = 0` +/// will become `FOO: &mut u32`. +/// +/// # 3. `extern` block +/// +/// This `extern` block contains a list of interrupts which are *not* used by the application as +/// hardware tasks. These interrupts will be used to dispatch software tasks. Each interrupt will be +/// used to dispatch *multiple* software tasks *at the same priority level*. +/// +/// This `extern` block must only contain functions with signature `fn ()`. The names of these +/// functions must match the names of the target device interrupts. +/// +/// Attributes can be applied to the functions inside this block. These attributes will be forwarded +/// to the interrupt handlers generated by the `app` attribute. #[proc_macro_attribute] pub fn app(args: TokenStream, input: TokenStream) -> TokenStream { let mut settings = Settings::default(); diff --git a/src/lib.rs b/src/lib.rs index 22eff5a..d78a1e1 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,6 +1,6 @@ //! Real Time For the Masses (RTFM) framework for ARM Cortex-M microcontrollers //! -//! **HEADS UP** This is an **alpha** pre-release; there may be breaking changes in the API and +//! **HEADS UP** This is an **beta** pre-release; there may be breaking changes in the API and //! semantics before a proper release is made. //! //! **IMPORTANT**: This crate is published as [`cortex-m-rtfm`] on crates.io but the name of the @@ -12,10 +12,8 @@ //! //! [here]: https://japaric.github.io/rtfm5/book/en/ //! -//! Don't forget to check the documentation of the [`#[app]`] attribute, which is the main component -//! of the framework. -//! -//! [`#[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) //! -- cgit v1.2.3 From 996bdf8f0c18fe86f2649dd43f7019c4f6ad1bb2 Mon Sep 17 00:00:00 2001 From: Jorge Aparicio Date: Wed, 21 Aug 2019 12:33:04 +0200 Subject: doc tweaks --- book/en/src/by-example/app.md | 4 ++-- macros/src/lib.rs | 4 ++-- src/lib.rs | 5 ++++- 3 files changed, 8 insertions(+), 5 deletions(-) (limited to 'src') diff --git a/book/en/src/by-example/app.md b/book/en/src/by-example/app.md index 02c49b1..ebb71f1 100644 --- a/book/en/src/by-example/app.md +++ b/book/en/src/by-example/app.md @@ -10,8 +10,8 @@ All RTFM applications use the [`app`] attribute (`#[app(..)]`). This attribute must be applied to a `const` item that contains items. The `app` attribute has a mandatory `device` argument that takes a *path* as a value. This path must point to a *peripheral access crate* (PAC) generated using [`svd2rust`] -**v0.14.x**. The `app` attribute will expand into a suitable entry point so it's -not required to use the [`cortex_m_rt::entry`] attribute. +**v0.14.x** or newer. The `app` attribute will expand into a suitable entry +point so it's not required to use the [`cortex_m_rt::entry`] attribute. [`app`]: ../../api/cortex_m_rtfm_macros/attr.app.html [`svd2rust`]: https://crates.io/crates/svd2rust diff --git a/macros/src/lib.rs b/macros/src/lib.rs index 6675765..fdde5c6 100644 --- a/macros/src/lib.rs +++ b/macros/src/lib.rs @@ -22,11 +22,11 @@ mod tests; /// The `app` attribute has one mandatory argument: /// /// - `device = `. The path must point to a device crate generated using [`svd2rust`] -/// **v0.14.x**. +/// **v0.14.x** or newer. /// /// [`svd2rust`]: https://crates.io/crates/svd2rust /// -/// and several optional arguments: +/// and a few optional arguments: /// /// - `peripherals = `. Indicates whether the runtime takes the device peripherals and makes /// them available to the `init` context. diff --git a/src/lib.rs b/src/lib.rs index d78a1e1..502cbc6 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -31,7 +31,10 @@ //! //! # Cargo features //! -//! - `heterogeneous`. This opt-in feature enables the *experimental* heterogeneous multi-core support. +//! - `heterogeneous`. This opt-in feature enables the *experimental* heterogeneous multi-core +//! support. This feature depends on unstable feature and requires the use of the nightly channel. +//! +//! - `homogeneous`. This opt-in feature enables the *experimental* homogeneous multi-core support. #![deny(missing_docs)] #![deny(rust_2018_compatibility)] -- cgit v1.2.3 From 7aa270cb92180abfc9102a69efdde378c3396b5e Mon Sep 17 00:00:00 2001 From: Jorge Aparicio Date: Sun, 15 Sep 2019 18:36:00 +0200 Subject: don't use deprecated API --- macros/src/codegen/pre_init.rs | 2 +- src/export.rs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) (limited to 'src') diff --git a/macros/src/codegen/pre_init.rs b/macros/src/codegen/pre_init.rs index 948dae5..605171b 100644 --- a/macros/src/codegen/pre_init.rs +++ b/macros/src/codegen/pre_init.rs @@ -75,7 +75,7 @@ pub fn codegen( // NOTE unmask the interrupt *after* setting its priority: changing the priority of a pended // interrupt is implementation defined - stmts.push(quote!(core.NVIC.enable(#device::#interrupt::#name);)); + stmts.push(quote!(rtfm::export::NVIC::unmask(#device::#interrupt::#name);)); } // cross-spawn barriers: now that priorities have been set and the interrupts have been unmasked diff --git a/src/export.rs b/src/export.rs index 572068c..96c444b 100644 --- a/src/export.rs +++ b/src/export.rs @@ -9,7 +9,7 @@ pub use cortex_m::register::basepri; pub use cortex_m::{ asm::wfi, interrupt, - peripheral::{scb::SystemHandler, syst::SystClkSource, DWT}, + peripheral::{scb::SystemHandler, syst::SystClkSource, DWT, NVIC}, Peripherals, }; use heapless::spsc::{MultiCore, SingleCore}; -- cgit v1.2.3