//! 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 [``] 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/ //! [``]: 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, see the [examples](./examples/index.html) //! module. #![deny(missing_docs)] #![deny(warnings)] #![feature(asm)] #![feature(const_fn)] #![feature(optin_builtin_traits)] #![feature(proc_macro)] #![no_std] extern crate cortex_m; extern crate cortex_m_rtfm_macros; extern crate static_ref; pub use cortex_m::asm::{bkpt, wfi}; pub use cortex_m::interrupt::CriticalSection; pub use cortex_m::interrupt::free as atomic; pub use cortex_m_rtfm_macros::app; pub use static_ref::Static; use cortex_m::interrupt::Nr; #[cfg(not(armv6m))] 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; /// Mutable variant of `borrow` fn borrow_mut<'cs>( &'cs mut self, cs: &'cs CriticalSection, ) -> &'cs mut Static; /// 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(&self, t: &mut Threshold, f: F) -> R where F: FnOnce(&Static, &mut Threshold) -> R; /// Mutable variant of `claim` fn claim_mut(&mut self, t: &mut Threshold, f: F) -> R where F: FnOnce(&mut Static, &mut Threshold) -> R; } impl Resource for Static { type Data = T; fn borrow<'cs>(&'cs self, _cs: &'cs CriticalSection) -> &'cs Static { self } fn borrow_mut<'cs>( &'cs mut self, _cs: &'cs CriticalSection, ) -> &'cs mut Static { self } fn claim(&self, t: &mut Threshold, f: F) -> R where F: FnOnce(&Static, &mut Threshold) -> R, { f(self, t) } fn claim_mut(&mut self, t: &mut Threshold, f: F) -> R where F: FnOnce(&mut Static, &mut Threshold) -> R, { f(self, t) } } #[doc(hidden)] pub unsafe fn claim( data: T, ceiling: u8, nvic_prio_bits: u8, t: &mut Threshold, f: F, ) -> R where F: FnOnce(T, &mut Threshold) -> R, { let max_priority = 1 << nvic_prio_bits; if ceiling > t.value { match () { #[cfg(armv6m)] () => { atomic(|_| f(data, &mut Threshold::new(max_priority))) } #[cfg(not(armv6m))] () => { if ceiling == max_priority { atomic(|_| f(data, &mut Threshold::new(max_priority))) } else { let old = basepri::read(); let hw = (max_priority - ceiling) << (8 - nvic_prio_bits); basepri_max::write(hw); let ret = f(data, &mut Threshold::new(ceiling)); basepri::write(old); ret } } } } else { f(data, t) } } /// 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 } } } impl !Send for Threshold {} /// Sets an interrupt as pending /// /// If the interrupt priority is high enough the interrupt will be serviced /// immediately, otherwise it will be serviced at some point after the current /// task ends. pub fn set_pending(interrupt: I) where I: Nr, { // NOTE(safe) atomic write let nvic = unsafe { &*cortex_m::peripheral::NVIC.get() }; nvic.set_pending(interrupt); } /// Binds a task `$handler` to the interrupt / exception `$NAME` /// /// This macro takes two arguments: the name of an exception / interrupt and the /// path to the function that will be used as the task handler. That function /// must have signature `fn(&mut Threshold, $NAME::Resources)`. /// /// Optionally, a third argument may be used to declare task local data. /// The handler will have exclusive access to these *local* variables on each /// invocation. If the third argument is used then the signature of the handler /// function must be `fn(&mut Threshold, &mut $locals, $NAME::Resources)`. #[macro_export] macro_rules! task { ($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) = $handler; f( &mut $crate::Threshold::new(::$NAME::$NAME), ::$NAME::Resources::new(), ); } }; ($NAME:ident, $handler:path, $locals:ident { $(static $var:ident: $ty:ty = $expr:expr;)+ }) => { #[allow(non_snake_case)] struct $locals { $($var: $crate::Static<$ty>,)+ } #[allow(non_snake_case)] #[allow(unsafe_code)] #[no_mangle] pub unsafe extern "C" fn $NAME() { let f: fn( &mut $crate::Threshold, &mut $locals, ::$NAME::Resources, ) = $handler; static mut LOCALS: $locals = $locals { $($var: unsafe { $crate::Static::new($expr) },)+ }; f( &mut $crate::Threshold::new(::$NAME::$NAME), &mut LOCALS, ::$NAME::Resources::new(), ); } }; } #[allow(non_camel_case_types)] #[doc(hidden)] pub enum Exception { /// System service call via SWI instruction SVCALL, /// Pendable request for system service PENDSV, /// System tick timer SYS_TICK, } impl Exception { #[doc(hidden)] pub fn nr(&self) -> usize { match *self { Exception::SVCALL => 11, Exception::PENDSV => 14, Exception::SYS_TICK => 15, } } }