aboutsummaryrefslogtreecommitdiff
path: root/src/lib.rs
diff options
context:
space:
mode:
Diffstat (limited to 'src/lib.rs')
-rw-r--r--src/lib.rs436
1 files changed, 304 insertions, 132 deletions
diff --git a/src/lib.rs b/src/lib.rs
index 9d55887..74cf96a 100644
--- a/src/lib.rs
+++ b/src/lib.rs
@@ -1,170 +1,342 @@
//! Real Time For the Masses (RTFM) framework for ARM Cortex-M microcontrollers
//!
-//! This crate is based on [the RTFM framework] created by the Embedded Systems
-//! group at [Luleå University of Technology][ltu], led by Prof. Per Lindgren,
-//! and uses a simplified version of the Stack Resource Policy as scheduling
-//! policy (check the [references] for details).
+//! **IMPORTANT**: This crate is published as [`cortex-m-rtfm`] on crates.io but the name of the
+//! library is `rtfm`.
//!
-//! [the RTFM framework]: http://www.rtfm-lang.org/
-//! [ltu]: https://www.ltu.se/?l=en
-//! [per]: https://www.ltu.se/staff/p/pln-1.11258?l=en
-//! [references]: ./index.html#references
+//! [`cortex-m-rtfm`]: https://crates.io/crates/cortex-m-rtfm
//!
-//! # Features
+//! The user level documentation can be found [here].
//!
-//! - **Event triggered tasks** as the unit of concurrency.
-//! - Support for prioritization of tasks and, thus, **preemptive
-//! multitasking**.
-//! - **Efficient and data race free memory sharing** through fine grained *non
-//! global* critical sections.
-//! - **Deadlock free execution** guaranteed at compile time.
-//! - **Minimal scheduling overhead** as the scheduler has no "software
-//! component": the hardware does all the scheduling.
-//! - **Highly efficient memory usage**: All the tasks share a single call stack
-//! and there's no hard dependency on a dynamic memory allocator.
-//! - **All Cortex M devices are fully supported**.
-//! - This task model is amenable to known WCET (Worst Case Execution Time)
-//! analysis and scheduling analysis techniques. (Though we haven't yet
-//! developed Rust friendly tooling for that.)
+//! [here]: ../../book/index.html
//!
-//! # Constraints
+//! Don't forget to check the documentation of the [`#[app]`] attribute, which is the main component
+//! of the framework.
//!
-//! - Tasks must run to completion. That's it, tasks can't contain endless
-//! loops. However, you can run an endless event loop in the `idle` *loop*.
+//! [`#[app]`]: ../cortex_m_rtfm_macros/attr.app.html
//!
-//! - Task priorities must remain constant at runtime.
+//! # Cargo features
//!
-//! # Dependencies
+//! - `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`].
//!
-//! The application crate must depend on a device crate generated using
-//! [`svd2rust`] v0.12.x and the "rt" feature of that crate must be enabled. The
-//! SVD file used to generate the device crate *must* contain [`<cpu>`]
-//! information.
-//!
-//! [`svd2rust`]: https://docs.rs/svd2rust/0.12.0/svd2rust/
-//! [`<cpu>`]: https://www.keil.com/pack/doc/CMSIS/SVD/html/elem_cpu.html
-//!
-//! # `app!`
-//!
-//! The `app!` macro is documented [here].
-//!
-//! [here]: https://docs.rs/cortex-m-rtfm-macros/0.3.0/cortex_m_rtfm_macros/fn.app.html
-//!
-//! # Important: Cortex-M7 devices
-//!
-//! If targeting a Cortex-M7 device with revision r0p1 then you MUST enable the `cm7-r0p1` Cargo
-//! feature of this crate or the `Resource.claim` and `Resource.claim_mut` methods WILL misbehave.
-//!
-//! # Examples
-//!
-//! In increasing grade of complexity. See the [examples](./examples/index.html)
-//! module.
-//!
-//! # References
-//!
-//! - Baker, T. P. (1991). Stack-based scheduling of realtime processes.
-//! *Real-Time Systems*, 3(1), 67-99.
-//!
-//! > The original Stack Resource Policy paper. [PDF][srp].
-//!
-//! [srp]: http://www.cs.fsu.edu/~baker/papers/mstacks3.pdf
-//!
-//! - Eriksson, J., Häggström, F., Aittamaa, S., Kruglyak, A., & Lindgren, P.
-//! (2013, June). Real-time for the masses, step 1: Programming API and static
-//! priority SRP kernel primitives. In Industrial Embedded Systems (SIES),
-//! 2013 8th IEEE International Symposium on (pp. 110-113). IEEE.
-//!
-//! > A description of the RTFM task and resource model. [PDF][rtfm]
-//!
-//! [rtfm]: http://www.diva-portal.org/smash/get/diva2:1005680/FULLTEXT01.pdf
+//! [`Instant`]: struct.Instant.html
+//! [`Duration`]: struct.Duration.html
+
#![deny(missing_docs)]
#![deny(warnings)]
#![no_std]
-extern crate cortex_m;
-extern crate cortex_m_rtfm_macros;
-extern crate rtfm_core;
-extern crate untagged_option;
+use core::{cell::Cell, u8};
+#[cfg(feature = "timer-queue")]
+use core::{cmp::Ordering, ops};
-use core::{mem, u8};
-
-pub use cortex_m::asm::{bkpt, wfi};
+#[cfg(not(feature = "timer-queue"))]
+use cortex_m::peripheral::SYST;
+#[cfg(armv7m)]
+use cortex_m::register::basepri;
+use cortex_m::{
+ interrupt::{self, Nr},
+ peripheral::{CBP, CPUID, DCB, DWT, FPB, FPU, ITM, MPU, NVIC, SCB, TPIU},
+};
pub use cortex_m_rtfm_macros::app;
-pub use rtfm_core::{Resource, Threshold};
+
+#[doc(hidden)]
+pub mod export;
#[doc(hidden)]
-pub use untagged_option::UntaggedOption;
+#[cfg(feature = "timer-queue")]
+mod tq;
-use cortex_m::interrupt::{self, Nr};
-use cortex_m::peripheral::NVIC;
-#[cfg(not(armv6m))]
-use cortex_m::register::basepri;
+/// 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`.
+#[allow(non_snake_case)]
+pub struct Peripherals<'a> {
+ /// 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"))]
+ pub DCB: DCB,
+
+ /// Data Watchpoint and Trace unit (not present if the `timer-queue` feature is enabled)
+ #[cfg(not(feature = "timer-queue"))]
+ pub DWT: DWT,
-pub mod examples;
+ /// Flash Patch and Breakpoint unit (not present on Cortex-M0 variants)
+ pub FPB: FPB,
-/// Executes the closure `f` in a preemption free context
+ /// Floating Point Unit (only present on `thumbv7em-none-eabihf`)
+ pub FPU: FPU,
+
+ /// Instrumentation Trace Macrocell (not present on Cortex-M0 variants)
+ pub ITM: ITM,
+
+ /// Memory Protection Unit
+ pub MPU: MPU,
+
+ // Nested Vector Interrupt Controller
+ // pub NVIC: NVIC,
+ /// System Control Block
+ pub SCB: &'a mut SCB,
+
+ /// SysTick: System Timer (not present if the `timer-queue` is enabled)
+ #[cfg(not(feature = "timer-queue"))]
+ 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`
///
-/// During the execution of the closure no task can preempt the current task.
-pub fn atomic<R, F>(t: &mut Threshold, f: F) -> R
-where
- F: FnOnce(&mut Threshold) -> R,
-{
- if t.value() == u8::MAX {
- f(t)
- } else {
- interrupt::disable();
- let r = f(&mut unsafe { Threshold::max() });
- unsafe { interrupt::enable() };
- r
+/// 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)
}
}
-#[inline]
-#[doc(hidden)]
-pub unsafe fn claim<T, R, F>(
- data: T,
- ceiling: u8,
- _nvic_prio_bits: u8,
- t: &mut Threshold,
- f: F,
-) -> R
-where
- F: FnOnce(T, &mut Threshold) -> R,
-{
- if ceiling > t.value() {
- match () {
- #[cfg(armv6m)]
- () => atomic(t, |t| f(data, t)),
+#[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;
- #[cfg(not(armv6m))]
- () => {
- let max_priority = 1 << _nvic_prio_bits;
+ fn add(mut self, dur: Duration) -> Self {
+ self += dur;
+ self
+ }
+}
- if ceiling == max_priority {
- atomic(t, |t| f(data, t))
+#[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, Eq, Ord, PartialEq, PartialOrd)]
+#[cfg(feature = "timer-queue")]
+pub struct Duration(u32);
+
+#[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.
+///
+/// [BASEPRI]: https://developer.arm.com/products/architecture/cpu-architecture/m-profile/docs/100701/latest/special-purpose-mask-registers
+pub unsafe trait Mutex {
+ /// IMPLEMENTATION DETAIL. DO NOT USE THIS CONSTANT
+ #[doc(hidden)]
+ const CEILING: u8;
+
+ /// IMPLEMENTATION DETAIL. DO NOT USE THIS CONSTANT
+ #[doc(hidden)]
+ const NVIC_PRIO_BITS: u8;
+
+ /// Data protected by the mutex
+ type Data: Send;
+
+ /// IMPLEMENTATION DETAIL. DO NOT USE THIS METHOD
+ #[doc(hidden)]
+ unsafe fn priority(&self) -> &Cell<u8>;
+
+ /// IMPLEMENTATION DETAIL. DO NOT USE THIS METHOD
+ #[doc(hidden)]
+ fn ptr(&self) -> *mut Self::Data;
+
+ /// Creates a critical section and grants temporary access to the protected data
+ #[inline(always)]
+ #[cfg(armv7m)]
+ fn lock<R, F>(&mut self, f: F) -> R
+ where
+ F: FnOnce(&mut Self::Data) -> R,
+ {
+ unsafe {
+ let current = self.priority().get();
+
+ if self.priority().get() < Self::CEILING {
+ if Self::CEILING == (1 << Self::NVIC_PRIO_BITS) {
+ self.priority().set(u8::MAX);
+ let r = interrupt::free(|_| f(&mut *self.ptr()));
+ self.priority().set(current);
+ r
} else {
- let old = basepri::read();
- let hw = (max_priority - ceiling) << (8 - _nvic_prio_bits);
- basepri::write(hw);
- let ret = f(data, &mut Threshold::new(ceiling));
- basepri::write(old);
- ret
+ self.priority().set(Self::CEILING);
+ basepri::write(logical2hw(Self::CEILING, Self::NVIC_PRIO_BITS));
+ let r = f(&mut *self.ptr());
+ basepri::write(logical2hw(current, Self::NVIC_PRIO_BITS));
+ self.priority().set(current);
+ r
}
+ } else {
+ f(&mut *self.ptr())
}
}
- } else {
- f(data, t)
}
+
+ /// Creates a critical section and grants temporary access to the protected data
+ #[cfg(not(armv7m))]
+ fn lock<R, F>(&mut self, f: F) -> R
+ where
+ F: FnOnce(&mut Self::Data) -> R,
+ {
+ unsafe {
+ let current = self.priority().get();
+
+ if self.priority().get() < Self::CEILING {
+ self.priority().set(u8::MAX);
+ let r = interrupt::free(|_| f(&mut *self.ptr()));
+ self.priority().set(current);
+ r
+ } else {
+ f(&mut *self.ptr())
+ }
+ }
+ }
+}
+
+#[cfg(armv7m)]
+#[inline]
+fn logical2hw(logical: u8, nvic_prio_bits: u8) -> u8 {
+ ((1 << nvic_prio_bits) - logical) << (8 - nvic_prio_bits)
}
-/// Sets an interrupt, that is a task, as pending
+/// Sets the given `interrupt` as pending
///
-/// If the task priority is high enough the task will be serviced immediately,
-/// otherwise it will be serviced at some point after the current task ends.
-pub fn set_pending<I>(interrupt: I)
+/// This is a convenience function around
+/// [`NVIC::pend`](../cortex_m/peripheral/struct.NVIC.html#method.pend)
+pub fn pend<I>(interrupt: I)
where
I: Nr,
{
- // NOTE(safe) atomic write
- let mut nvic: NVIC = unsafe { mem::transmute(()) };
- nvic.set_pending(interrupt);
+ NVIC::pend(interrupt)
}