aboutsummaryrefslogtreecommitdiff
path: root/src/lib.rs
diff options
context:
space:
mode:
Diffstat (limited to 'src/lib.rs')
-rw-r--r--src/lib.rs288
1 files changed, 156 insertions, 132 deletions
diff --git a/src/lib.rs b/src/lib.rs
index 6ee98d79..3b677380 100644
--- a/src/lib.rs
+++ b/src/lib.rs
@@ -1,3 +1,57 @@
+//! Real Time For the Masses (RTFM), a framework for building concurrent
+//! applications, 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).
+//!
+//! [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
+//!
+//! # Features
+//!
+//! - **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.)
+//!
+//! # Constraints
+//!
+//! - 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` function.
+//!
+//! - Task priorities must remain constant at runtime.
+//!
+//! # Dependencies
+//!
+//! - A device crate generated using [`svd2rust`] v0.11.x. The input SVD file
+//! *must* contain [`<cpu>`] information.
+//! - A `start` lang time: Vanilla `main` must be supported in binary crates.
+//! You can use the [`cortex-m-rt`] crate to fulfill the requirement
+//!
+//! [`svd2rust`]: https://docs.rs/svd2rust/0..0/svd2rust/
+//! [`<cpu>`]: https://www.keil.com/pack/doc/CMSIS/SVD/html/elem_cpu.html
+//! [`cortex-m-rt`]: https://docs.rs/cortex-m-rt/0.3.0/cortex_m_rt/
+//!
+//! # Examples
+//!
+//! In increasing grade of complexity: [examples](./examples/index.html)
+
+#![deny(missing_docs)]
+#![deny(warnings)]
#![feature(asm)]
#![feature(const_fn)]
#![feature(optin_builtin_traits)]
@@ -17,11 +71,73 @@ pub use cortex_m::interrupt::free as atomic;
pub use static_ref::Static;
use cortex_m::interrupt::Nr;
#[cfg(not(armv6m))]
-use cortex_m::register::{basepri_max, basepri};
+use cortex_m::register::{basepri, basepri_max};
+
+pub mod examples;
+
+/// A resource, a means to share data between tasks
+pub trait Resource {
+ /// The data protected by the resource
+ type Data;
+
+ /// Borrows the resource data for the duration of a *global* critical
+ /// section
+ fn borrow<'cs>(
+ &'cs self,
+ cs: &'cs CriticalSection,
+ ) -> &'cs Static<Self::Data>;
+
+ /// Mutable variant of `borrow`
+ fn borrow_mut<'cs>(
+ &'cs mut self,
+ cs: &'cs CriticalSection,
+ ) -> &'cs mut Static<Self::Data>;
+
+ /// Claims the resource data for the span of the closure `f`. For the
+ /// duration of the closure other tasks that may access the resource data
+ /// are prevented from preempting the current task.
+ fn claim<R, F>(&self, t: &mut Threshold, f: F) -> R
+ where
+ F: FnOnce(&Static<Self::Data>, &mut Threshold) -> R;
+
+ /// Mutable variant of `claim`
+ fn claim_mut<R, F>(&mut self, t: &mut Threshold, f: F) -> R
+ where
+ F: FnOnce(&mut Static<Self::Data>, &mut Threshold) -> R;
+}
+
+impl<T> Resource for Static<T> {
+ type Data = T;
+
+ fn borrow<'cs>(&'cs self, _cs: &'cs CriticalSection) -> &'cs Static<T> {
+ self
+ }
+
+ fn borrow_mut<'cs>(
+ &'cs mut self,
+ _cs: &'cs CriticalSection,
+ ) -> &'cs mut Static<T> {
+ self
+ }
-#[inline(always)]
-unsafe fn claim<T, U, R, F, G>(
- data: T,
+ fn claim<R, F>(&self, t: &mut Threshold, f: F) -> R
+ where
+ F: FnOnce(&Static<Self::Data>, &mut Threshold) -> R,
+ {
+ f(self, t)
+ }
+
+ fn claim_mut<R, F>(&mut self, t: &mut Threshold, f: F) -> R
+ where
+ F: FnOnce(&mut Static<Self::Data>, &mut Threshold) -> R,
+ {
+ f(self, t)
+ }
+}
+
+#[doc(hidden)]
+pub unsafe fn claim<T, U, R, F, G>(
+ data: *mut T,
ceiling: u8,
nvic_prio_bits: u8,
t: &mut Threshold,
@@ -30,10 +146,10 @@ unsafe fn claim<T, U, R, F, G>(
) -> R
where
F: FnOnce(U, &mut Threshold) -> R,
- G: FnOnce(T) -> U,
+ G: FnOnce(*mut T) -> U,
{
let max_priority = 1 << nvic_prio_bits;
- if ceiling > t.0 {
+ if ceiling > t.value {
match () {
#[cfg(armv6m)]
() => {
@@ -47,7 +163,7 @@ where
let old = basepri::read();
let hw = (max_priority - ceiling) << (8 - nvic_prio_bits);
basepri_max::write(hw);
- let ret = f(g(data), &mut Threshold(ceiling));
+ let ret = f(g(data), &mut Threshold::new(ceiling));
basepri::write(old);
ret
}
@@ -58,139 +174,44 @@ where
}
}
-pub struct Peripheral<P>
-where
- P: 'static,
-{
- // FIXME(rustc/LLVM bug?) storing the ceiling in the resource de-optimizes
- // claims (the ceiling value gets loaded at runtime rather than inlined)
- // ceiling: u8,
- peripheral: cortex_m::peripheral::Peripheral<P>,
-}
-
-impl<P> Peripheral<P> {
- pub const fn new(peripheral: cortex_m::peripheral::Peripheral<P>) -> Self {
- Peripheral { peripheral }
- }
-
- #[inline(always)]
- pub unsafe fn borrow<'cs>(
- &'static self,
- _cs: &'cs CriticalSection,
- ) -> &'cs P {
- &*self.peripheral.get()
- }
-
- #[inline(always)]
- pub unsafe fn claim<R, F>(
- &'static self,
- ceiling: u8,
- nvic_prio_bits: u8,
- t: &mut Threshold,
- f: F,
- ) -> R
- where
- F: FnOnce(&P, &mut Threshold) -> R,
- {
- claim(
- &self.peripheral,
- ceiling,
- nvic_prio_bits,
- t,
- f,
- |peripheral| &*peripheral.get(),
- )
- }
-
- pub fn get(&self) -> *mut P {
- self.peripheral.get()
- }
-}
-
-unsafe impl<P> Sync for Peripheral<P>
-where
- P: Send,
-{
-}
-
-pub struct Resource<T> {
- // FIXME(rustc/LLVM bug?) storing the ceiling in the resource de-optimizes
- // claims (the ceiling value gets loaded at runtime rather than inlined)
- // ceiling: u8,
+#[doc(hidden)]
+pub struct Cell<T> {
data: UnsafeCell<T>,
}
-impl<T> Resource<T> {
- pub const fn new(value: T) -> Self {
- Resource {
- data: UnsafeCell::new(value),
+#[doc(hidden)]
+impl<T> Cell<T> {
+ pub const fn new(data: T) -> Self {
+ Cell {
+ data: UnsafeCell::new(data),
}
}
- #[inline(always)]
- pub unsafe fn borrow<'cs>(
- &'static self,
- _cs: &'cs CriticalSection,
- ) -> &'cs Static<T> {
- Static::ref_(&*self.data.get())
- }
-
- #[inline(always)]
- pub unsafe fn borrow_mut<'cs>(
- &'static self,
- _cs: &'cs CriticalSection,
- ) -> &'cs mut Static<T> {
- Static::ref_mut(&mut *self.data.get())
- }
-
- #[inline(always)]
- pub unsafe fn claim<R, F>(
- &'static self,
- ceiling: u8,
- nvic_prio_bits: u8,
- t: &mut Threshold,
- f: F,
- ) -> R
- where
- F: FnOnce(&Static<T>, &mut Threshold) -> R,
- {
- claim(&self.data, ceiling, nvic_prio_bits, t, f, |data| {
- Static::ref_(&*data.get())
- })
- }
-
- #[inline(always)]
- pub unsafe fn claim_mut<R, F>(
- &'static self,
- ceiling: u8,
- nvic_prio_bits: u8,
- t: &mut Threshold,
- f: F,
- ) -> R
- where
- F: FnOnce(&mut Static<T>, &mut Threshold) -> R,
- {
- claim(&self.data, ceiling, nvic_prio_bits, t, f, |data| {
- Static::ref_mut(&mut *data.get())
- })
- }
-
pub fn get(&self) -> *mut T {
self.data.get()
}
}
-unsafe impl<T> Sync for Resource<T>
+unsafe impl<T> Sync for Cell<T>
where
T: Send,
{
}
-pub struct Threshold(u8);
+/// Preemption threshold token
+///
+/// The preemption threshold indicates the priority a task must have to preempt
+/// the current context. For example a threshold of 2 indicates that only
+/// interrupts / exceptions with a priority of 3 or greater can preempt the
+/// current context
+pub struct Threshold {
+ value: u8,
+}
impl Threshold {
+ #[doc(hidden)]
pub unsafe fn new(value: u8) -> Self {
- Threshold(value)
+ Threshold { value }
}
}
@@ -206,14 +227,15 @@ where
nvic.set_pending(interrupt);
}
+/// Binds a task `$handler` to the interrupt / exception `$NAME`
#[macro_export]
macro_rules! task {
- ($NAME:ident, $body:path) => {
+ ($NAME:ident, $handler:path) => {
#[allow(non_snake_case)]
#[allow(unsafe_code)]
#[no_mangle]
pub unsafe extern "C" fn $NAME() {
- let f: fn(&mut $crate::Threshold, ::$NAME::Resources) = $body;
+ let f: fn(&mut $crate::Threshold, ::$NAME::Resources) = $handler;
f(
&mut $crate::Threshold::new(::$NAME::$NAME),
@@ -221,11 +243,13 @@ macro_rules! task {
);
}
};
- ($NAME:ident, $body:path, $local:ident {
- $($var:ident: $ty:ty = $expr:expr;)+
+
+ ($NAME:ident, $handler:path, $locals:ident {
+ $(static $var:ident: $ty:ty = $expr:expr;)+
}) => {
- struct $local {
- $($var: $ty,)+
+ #[allow(non_snake_case)]
+ struct $locals {
+ $($var: $crate::Static<$ty>,)+
}
#[allow(non_snake_case)]
@@ -234,17 +258,17 @@ macro_rules! task {
pub unsafe extern "C" fn $NAME() {
let f: fn(
&mut $crate::Threshold,
- &mut $local,
+ &mut $locals,
::$NAME::Resources,
- ) = $body;
+ ) = $handler;
- static mut LOCAL: $local = $local {
- $($var: $expr,)+
+ static mut LOCALS: $locals = $locals {
+ $($var: unsafe { $crate::Static::new($expr) },)+
};
f(
&mut $crate::Threshold::new(::$NAME::$NAME),
- &mut LOCAL,
+ &mut LOCALS,
::$NAME::Resources::new(),
);
}