rename rtfm::critical to rtfm::atomic, touch up the documentation

This commit is contained in:
Jorge Aparicio 2017-04-25 21:51:07 -05:00
parent 840c32060b
commit 404dde6f2a

View file

@ -1,12 +1,13 @@
//! RTFM: Real Time For the Masses (ARM Cortex-M edition) //! RTFM: Real Time For the Masses (ARM Cortex-M edition)
//! //!
//! RTFM is a framework for building event-driven applications for ARM Cortex-M //! `cortex-m-rtfm` is a framework for building concurrent applications for ARM
//! microcontrollers. //! Cortex-M microcontrollers.
//! //!
//! This crate is based on the RTFM framework created by [prof. Per //! This crate is based on [the RTFM framework] created by [prof. Per
//! Lindgren][per] and uses a simplified version of the Stack Resource Policy as //! Lindgren][per] and uses a simplified version of the Stack Resource Policy as
//! scheduling policy. Check the [references] for details. //! scheduling policy (check the [references] for details).
//! //!
//! [the RTFM framework]: http://www.rtfm-lang.org/
//! [per]: https://www.ltu.se/staff/p/pln-1.11258?l=en //! [per]: https://www.ltu.se/staff/p/pln-1.11258?l=en
//! [references]: ./index.html#references //! [references]: ./index.html#references
//! //!
@ -14,13 +15,16 @@
//! //!
//! - **Event triggered tasks** as the unit of concurrency. //! - **Event triggered tasks** as the unit of concurrency.
//! - Supports prioritization of tasks and, thus, **preemptive multitasking**. //! - Supports prioritization of tasks and, thus, **preemptive multitasking**.
//! - **Data race free memory sharing** through fine grained *non global* //! - **Efficient data race free memory sharing** through fine grained *non
//! critical sections. //! global* critical sections.
//! - **Deadlock free execution** guaranteed at compile time. //! - **Deadlock free execution** guaranteed at compile time.
//! - **Minimal overhead** as the scheduler has no software component / runtime; //! - **Minimal scheduling overhead** as the scheduler has no "software
//! the hardware does all the scheduling. //! component"; the hardware does all the scheduling.
//! - Full support for all Cortex M3, M4 and M7 devices. M0(+) is also supported //! - **Highly efficient memory usage**. All the tasks share the call stack and
//! but the whole API is not available (due to missing hardware features). //! there's no hard dependency on a dynamic allocator.
//! - **All Cortex M3, M4 and M7 devices are fully supported**. M0(+) is
//! partially supported as the whole API is not available (due to missing
//! hardware features).
//! - The number of task priority levels is configurable at compile time through //! - The number of task priority levels is configurable at compile time through
//! the `P2` (4 levels), `P3` (8 levels), etc. Cargo features. The number of //! the `P2` (4 levels), `P3` (8 levels), etc. Cargo features. The number of
//! priority levels supported by the hardware is device specific but this //! priority levels supported by the hardware is device specific but this
@ -55,6 +59,7 @@
//! ## Zero tasks //! ## Zero tasks
//! //!
//! ``` ignore //! ``` ignore
//! #![feature(used)]
//! #![no_std] //! #![no_std]
//! //!
//! #[macro_use] // for the `hprintln!` macro //! #[macro_use] // for the `hprintln!` macro
@ -101,7 +106,7 @@
//! structure into your program: //! structure into your program:
//! //!
//! - `init`, the initialization phase, runs first. This function is executed //! - `init`, the initialization phase, runs first. This function is executed
//! inside a *global* critical section and can't be preempted. //! "atomically", in the sense that no task / interrupt can preempt it.
//! //!
//! - `idle`, a never ending function that runs after `init`. //! - `idle`, a never ending function that runs after `init`.
//! //!
@ -110,6 +115,8 @@
//! # One task //! # One task
//! //!
//! ``` ignore //! ``` ignore
//! #![feature(const_fn)]
//! #![feature(used)]
//! #![no_std] //! #![no_std]
//! //!
//! extern crate cortex_m_rt; //! extern crate cortex_m_rt;
@ -129,7 +136,9 @@
//! // IDLE LOOP //! // IDLE LOOP
//! fn idle(_priority: P0) -> ! { //! fn idle(_priority: P0) -> ! {
//! // Sleep //! // Sleep
//! loop { rtfm::wfi() } //! loop {
//! rtfm::wfi();
//! }
//! } //! }
//! //!
//! // TASKS //! // TASKS
@ -173,6 +182,8 @@
//! # Two "serial" tasks //! # Two "serial" tasks
//! //!
//! ``` ignore //! ``` ignore
//! #![feature(const_fn)]
//! #![feature(used)]
//! #![no_std] //! #![no_std]
//! //!
//! extern crate cortex_m_rt; //! extern crate cortex_m_rt;
@ -203,6 +214,17 @@
//! // Data shared between tasks `t1` and `t2` //! // Data shared between tasks `t1` and `t2`
//! static COUNTER: Resource<Cell<u32>, C1> = Resource::new(Cell::new(0)); //! static COUNTER: Resource<Cell<u32>, C1> = Resource::new(Cell::new(0));
//! //!
//! fn init(priority: P0, ceiling: &C16) {
//! // ..
//! }
//!
//! fn idle(priority: P0) -> ! {
//! // Sleep
//! loop {
//! rtfm::wfi();
//! }
//! }
//!
//! fn t1(_task: Tim6Dacunder, priority: P1) { //! fn t1(_task: Tim6Dacunder, priority: P1) {
//! let ceiling = priority.as_ceiling(); //! let ceiling = priority.as_ceiling();
//! //!
@ -223,13 +245,13 @@
//! Here we declare two tasks, `t1` and `t2`; both with a priority of 1 (`P1`). //! Here we declare two tasks, `t1` and `t2`; both with a priority of 1 (`P1`).
//! As both tasks have the same priority, we say that they are *serial* tasks in //! As both tasks have the same priority, we say that they are *serial* tasks in
//! the sense that `t1` can only run *after* `t2` is done and vice versa; i.e. //! the sense that `t1` can only run *after* `t2` is done and vice versa; i.e.
//! there's no preemption. //! no preemption between them is possible.
//! //!
//! To share data between these two tasks, we use the //! To share data between these two tasks, we use the
//! [`Resource`](./struct.Resource.html) abstraction. As the tasks can't preempt //! [`Resource`](./struct.Resource.html) abstraction. As the tasks can't preempt
//! each other, they can access the `COUNTER` resource using the zero cost //! each other, they can access the `COUNTER` resource using the zero cost
//! [`access`](./struct.Resource.html#method.access) method -- no //! [`access`](./struct.Resource.html#method.access) method -- no
//! synchronization needed. //! synchronization is required.
//! //!
//! `COUNTER` has an extra type parameter: `C1`. This is the *ceiling* of the //! `COUNTER` has an extra type parameter: `C1`. This is the *ceiling* of the
//! resource. For now suffices to say that the ceiling must be the maximum of //! resource. For now suffices to say that the ceiling must be the maximum of
@ -240,6 +262,8 @@
//! # Preemptive multitasking //! # Preemptive multitasking
//! //!
//! ``` ignore //! ``` ignore
//! #![feature(const_fn)]
//! #![feature(used)]
//! #![no_std] //! #![no_std]
//! //!
//! extern crate cortex_m_rt; //! extern crate cortex_m_rt;
@ -250,9 +274,7 @@
//! use core::cell::Cell; //! use core::cell::Cell;
//! //!
//! use stm32f30x::interrupt::{Tim6Dacunder, Tim7}; //! use stm32f30x::interrupt::{Tim6Dacunder, Tim7};
//! use rtfm::{C2, C16, P0, P1, P2, Resource}; //! use rtfm::{C1, C16, C2, P0, P1, P2, Resource};
//!
//! // omitted: `idle`, `init`
//! //!
//! tasks!(stm32f30x, { //! tasks!(stm32f30x, {
//! t1: Task { //! t1: Task {
@ -269,16 +291,29 @@
//! //!
//! static COUNTER: Resource<Cell<u32>, C2> = Resource::new(Cell::new(0)); //! static COUNTER: Resource<Cell<u32>, C2> = Resource::new(Cell::new(0));
//! //!
//! fn init(priority: P0, ceiling: &C16) {
//! // ..
//! }
//!
//! fn idle(priority: P0) -> ! {
//! // Sleep
//! loop {
//! rtfm::wfi();
//! }
//! }
//!
//! fn t1(_task: Tim6Dacunder, priority: P1) { //! fn t1(_task: Tim6Dacunder, priority: P1) {
//! // .. //! // ..
//! //!
//! let ceiling: &C1 = priority.as_ceiling(); //! let ceiling: &C1 = priority.as_ceiling();
//! //!
//! ceiling.raise(&COUNTER, |ceiling: &C2| { //! ceiling.raise(
//! &COUNTER, |ceiling: &C2| {
//! let counter = COUNTER.access(&priority, ceiling); //! let counter = COUNTER.access(&priority, ceiling);
//! //!
//! counter.set(counter.get() + 1); //! counter.set(counter.get() + 1);
//! }); //! }
//! );
//! //!
//! // .. //! // ..
//! } //! }
@ -300,39 +335,48 @@
//! To avoid data races, `t1` must modify `COUNTER` in an atomic way; i.e. `t2` //! To avoid data races, `t1` must modify `COUNTER` in an atomic way; i.e. `t2`
//! most not preempt `t1` while `COUNTER` is being modified. This is //! most not preempt `t1` while `COUNTER` is being modified. This is
//! accomplished by [`raise`](./struct.C.html#method.raise)-ing the `ceiling`. //! accomplished by [`raise`](./struct.C.html#method.raise)-ing the `ceiling`.
//! This creates a critical section, denoted by a closure, for whose span //! This creates a critical section, denoted by a closure; for whose execution,
//! `COUNTER` is accessible but `t2` is blocked from preempting `t1`. //! `COUNTER` is accessible but `t2` is blocked from preempting `t1`.
//! //!
//! How `t2` accesses `COUNTER` remains unchanged. Since `t1` can't preempt `t2` //! How `t2` accesses `COUNTER` remains unchanged. Since `t1` can't preempt `t2`
//! due to the differences in priority; no critical section is needed in `t2`. //! due to the differences in priority; no critical section is needed in `t2`.
//! //!
//! Note that the ceiling of `COUNTER` had to change to `C2`. This is required //! Note that the ceiling of `COUNTER` had to be changed to `C2`. This is
//! because the ceiling must be the maximum between `P1` and `P2`. //! required because the ceiling must be the maximum between `P1` and `P2`.
//! //!
//! Finally, it should be noted that the critical section in `t1` will only //! Finally, it should be noted that the critical section in `t1` will only
//! block tasks with a priority of 2 or lower. This is exactly what the ceiling //! block tasks with a priority of 2 or lower. This is exactly what the ceiling
//! represents: it's the "bar" that a task priority must pass in order to be //! represents: it's the "bar" that a task priority must pass in order to be
//! able to preempt the current task / critical section. //! able to preempt the current task / critical section. Note that a task with
//! e.g. a priority of 3 (`P3`) effectively imposes a ceiling of 3 (`C3`)
//! because only other task with a priority of 4 or greater can preempt it.
//! //!
//! # Peripherals as resources //! # Peripherals as resources
//! //!
//! ``` ignore //! ``` ignore
//! #![feature(const_fn)]
//! #![feature(used)]
//! #![no_std] //! #![no_std]
//! //!
//! extern crate cortex_m_rt; //! extern crate cortex_m_rt;
//! #[macro_use] //! #[macro_use]
//! extern crate cortex_m_rtfm as rtfm; //! extern crate cortex_m_rtfm as rtfm;
//! extern crate stm32f100xx; //! extern crate stm32f30x;
//! //!
//! use rtfm::{C0, C16, P0, Peripheral}; //! use rtfm::{C0, C16, P0, Peripheral};
//! //!
//! static GPIOA: Peripheral<stm32f100xx::Gpioa, C0> = //! peripherals!(stm32f30x, {
//! unsafe { Peripheral::new(stm32f100xx::GPIOA) }; //! GPIOA: Peripheral {
//! register_block: Gpioa,
//! ceiling: C0,
//! },
//! RCC: Peripheral {
//! register_block: Rcc,
//! ceiling: C0,
//! },
//! });
//! //!
//! static RCC: Peripheral<stm32f100xx::Rcc, C0> = //! tasks!(stm32f30x, {});
//! unsafe { Peripheral::new(stm32f100xx::RCC) };
//!
//! tasks!(stm32f100xx, {});
//! //!
//! fn init(priority: P0, ceiling: &C16) { //! fn init(priority: P0, ceiling: &C16) {
//! let gpioa = GPIOA.access(&priority, &ceiling); //! let gpioa = GPIOA.access(&priority, &ceiling);
@ -343,7 +387,9 @@
//! //!
//! fn idle(_priority: P0) -> ! { //! fn idle(_priority: P0) -> ! {
//! // Sleep //! // Sleep
//! loop { rtfm::wfi() } //! loop {
//! rtfm::wfi();
//! }
//! } //! }
//! ``` //! ```
//! //!
@ -352,8 +398,8 @@
//! [`Peripheral`](./struct.Peripheral.html) abstraction. //! [`Peripheral`](./struct.Peripheral.html) abstraction.
//! //!
//! `Peripheral` and `Resource` has pretty much the same API except that //! `Peripheral` and `Resource` has pretty much the same API except that
//! `Peripheral.new` is `unsafe`. Care must be taken to NOT alias peripherals; //! `Peripheral` instances must be declared using the
//! i.e. don't create two `Peripheral`s that point to the same register block. //! [`peripherals!`](./macro.peripherals.html) macro.
//! //!
//! # References //! # References
//! //!
@ -537,10 +583,10 @@ impl<Periph, RC> Peripheral<Periph, C<RC>> {
unsafe impl<T, C> Sync for Peripheral<T, C> {} unsafe impl<T, C> Sync for Peripheral<T, C> {}
/// Runs the closure `f` in a *global* critical section /// Runs the closure `f` "atomically"
/// ///
/// No task can preempt this critical section /// No task can preempt the execution of the closure
pub fn critical<R, F>(f: F) -> R pub fn atomic<R, F>(f: F) -> R
where where
F: FnOnce(&CMAX) -> R, F: FnOnce(&CMAX) -> R,
{ {
@ -558,7 +604,7 @@ where
r r
} }
/// Disables the `task` /// Disables a `task`
/// ///
/// The task won't run even if the underlying interrupt is raised /// The task won't run even if the underlying interrupt is raised
pub fn disable<T, TP>(_task: fn(T, P<TP>)) pub fn disable<T, TP>(_task: fn(T, P<TP>))
@ -572,7 +618,7 @@ where
unsafe { (*_NVIC.get()).disable(_task) } unsafe { (*_NVIC.get()).disable(_task) }
} }
/// Enables the `task` /// Enables a `task`
pub fn enable<T, TP>(_task: fn(T, P<TP>)) pub fn enable<T, TP>(_task: fn(T, P<TP>))
where where
T: Context + Nr, T: Context + Nr,
@ -791,7 +837,9 @@ macro_rules! peripherals {
/// ///
/// fn idle(priority: P0) -> ! { /// fn idle(priority: P0) -> ! {
/// // Sleep /// // Sleep
/// loop { rtfm::wfi() } /// loop {
/// rtfm::wfi();
/// }
/// } /// }
/// ///
/// // NOTE signature must match the tasks! declaration /// // NOTE signature must match the tasks! declaration
@ -813,7 +861,7 @@ macro_rules! tasks {
},)* },)*
}) => { }) => {
fn main() { fn main() {
$crate::critical(|cmax| { $crate::atomic(|cmax| {
fn validate_signature(_: fn($crate::P0, &$crate::CMAX)) {} fn validate_signature(_: fn($crate::P0, &$crate::CMAX)) {}
validate_signature(init); validate_signature(init);