aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/examples/_0_zero_tasks.rs47
-rw-r--r--src/examples/_1_one_task.rs100
-rw-r--r--src/examples/_2_two_tasks.rs62
-rw-r--r--src/examples/_3_preemption.rs71
-rw-r--r--src/examples/_4_nested.rs132
-rw-r--r--src/examples/_5_late_resources.rs90
-rw-r--r--src/examples/_6_safe_static_mut_ref.rs35
-rw-r--r--src/examples/_7_generics.rs77
-rw-r--r--src/examples/_8_full_syntax.rs87
-rw-r--r--src/examples/mod.rs11
-rw-r--r--src/export.rs84
-rw-r--r--src/lib.rs436
-rw-r--r--src/tq.rs135
13 files changed, 523 insertions, 844 deletions
diff --git a/src/examples/_0_zero_tasks.rs b/src/examples/_0_zero_tasks.rs
deleted file mode 100644
index 0484bb9d..00000000
--- a/src/examples/_0_zero_tasks.rs
+++ /dev/null
@@ -1,47 +0,0 @@
-//! Minimal example with zero tasks
-//!
-//! ```
-//! #![deny(unsafe_code)]
-//! #![deny(warnings)]
-//! #![no_std]
-//!
-//! extern crate cortex_m_rtfm as rtfm; // IMPORTANT always do this rename
-//! extern crate stm32f103xx; // the device crate
-//!
-//! // import the procedural macro
-//! use rtfm::app;
-//!
-//! // This macro call indicates that this is a RTFM application
-//! //
-//! // This macro will expand to a `main` function so you don't need to supply
-//! // `main` yourself.
-//! app! {
-//! // this is the path to the device crate
-//! device: stm32f103xx,
-//! }
-//!
-//! // The initialization phase.
-//! //
-//! // This runs first and within a *global* critical section. Nothing can preempt
-//! // this function.
-//! fn init(p: init::Peripherals) {
-//! // This function has access to all the peripherals of the device
-//! p.core.SYST;
-//! p.device.GPIOA;
-//! p.device.RCC;
-//! // ..
-//! }
-//!
-//! // The idle loop.
-//! //
-//! // This runs after `init` and has a priority of 0. All tasks can preempt this
-//! // function. This function can never return so it must contain some sort of
-//! // endless loop.
-//! fn idle() -> ! {
-//! loop {
-//! // This puts the processor to sleep until there's a task to service
-//! rtfm::wfi();
-//! }
-//! }
-//! ```
-// Auto-generated. Do not modify.
diff --git a/src/examples/_1_one_task.rs b/src/examples/_1_one_task.rs
deleted file mode 100644
index b9075a59..00000000
--- a/src/examples/_1_one_task.rs
+++ /dev/null
@@ -1,100 +0,0 @@
-//! An application with one task
-//!
-//! ```
-//! #![deny(unsafe_code)]
-//! #![deny(warnings)]
-//! #![no_std]
-//!
-//! extern crate cortex_m;
-//! extern crate cortex_m_rtfm as rtfm;
-//! extern crate stm32f103xx;
-//!
-//! use cortex_m::peripheral::syst::SystClkSource;
-//! use rtfm::{app, Threshold};
-//! use stm32f103xx::GPIOC;
-//!
-//! app! {
-//! device: stm32f103xx,
-//!
-//! // Here data resources are declared
-//! //
-//! // Data resources are static variables that are safe to share across tasks
-//! resources: {
-//! // Declaration of resources looks exactly like declaration of static
-//! // variables
-//! static ON: bool = false;
-//! },
-//!
-//! // Here tasks are declared
-//! //
-//! // Each task corresponds to an interrupt or an exception. Every time the
-//! // interrupt or exception becomes *pending* the corresponding task handler
-//! // will be executed.
-//! tasks: {
-//! // Here we declare that we'll use the SYS_TICK exception as a task
-//! SYS_TICK: {
-//! // Path to the task handler
-//! path: sys_tick,
-//!
-//! // These are the resources this task has access to.
-//! //
-//! // The resources listed here must also appear in `app.resources`
-//! resources: [ON],
-//! },
-//! }
-//! }
-//!
-//! fn init(mut p: init::Peripherals, r: init::Resources) {
-//! // `init` can modify all the `resources` declared in `app!`
-//! r.ON;
-//!
-//! // power on GPIOC
-//! p.device.RCC.apb2enr.modify(|_, w| w.iopcen().enabled());
-//!
-//! // configure PC13 as output
-//! p.device.GPIOC.bsrr.write(|w| w.bs13().set());
-//! p.device
-//! .GPIOC
-//! .crh
-//! .modify(|_, w| w.mode13().output().cnf13().push());
-//!
-//! // configure the system timer to generate one interrupt every second
-//! p.core.SYST.set_clock_source(SystClkSource::Core);
-//! p.core.SYST.set_reload(8_000_000); // 1s
-//! p.core.SYST.enable_interrupt();
-//! p.core.SYST.enable_counter();
-//! }
-//!
-//! fn idle() -> ! {
-//! loop {
-//! rtfm::wfi();
-//! }
-//! }
-//!
-//! // This is the task handler of the SYS_TICK exception
-//! //
-//! // `_t` is the preemption threshold token. We won't use it in this program.
-//! //
-//! // `r` is the set of resources this task has access to. `SYS_TICK::Resources`
-//! // has one field per resource declared in `app!`.
-//! #[allow(unsafe_code)]
-//! fn sys_tick(_t: &mut Threshold, mut r: SYS_TICK::Resources) {
-//! // toggle state
-//! *r.ON = !*r.ON;
-//!
-//! if *r.ON {
-//! // set the pin PC13 high
-//! // NOTE(unsafe) atomic write to a stateless register
-//! unsafe {
-//! (*GPIOC::ptr()).bsrr.write(|w| w.bs13().set());
-//! }
-//! } else {
-//! // set the pin PC13 low
-//! // NOTE(unsafe) atomic write to a stateless register
-//! unsafe {
-//! (*GPIOC::ptr()).bsrr.write(|w| w.br13().reset());
-//! }
-//! }
-//! }
-//! ```
-// Auto-generated. Do not modify.
diff --git a/src/examples/_2_two_tasks.rs b/src/examples/_2_two_tasks.rs
deleted file mode 100644
index 516ff0c9..00000000
--- a/src/examples/_2_two_tasks.rs
+++ /dev/null
@@ -1,62 +0,0 @@
-//! Two tasks running at the *same* priority with access to the same resource
-//!
-//! ```
-//! #![deny(unsafe_code)]
-//! #![deny(warnings)]
-//! #![no_std]
-//!
-//! extern crate cortex_m_rtfm as rtfm;
-//! extern crate stm32f103xx;
-//!
-//! use rtfm::{app, Threshold};
-//!
-//! app! {
-//! device: stm32f103xx,
-//!
-//! resources: {
-//! static COUNTER: u64 = 0;
-//! },
-//!
-//! // Both SYS_TICK and TIM2 have access to the `COUNTER` data
-//! tasks: {
-//! SYS_TICK: {
-//! path: sys_tick,
-//! resources: [COUNTER],
-//! },
-//!
-//! TIM2: {
-//! path: tim2,
-//! resources: [COUNTER],
-//! },
-//! },
-//! }
-//!
-//! fn init(_p: init::Peripherals, _r: init::Resources) {
-//! // ..
-//! }
-//!
-//! fn idle() -> ! {
-//! loop {
-//! rtfm::wfi();
-//! }
-//! }
-//!
-//! // As both tasks are running at the same priority one can't preempt the other.
-//! // Thus both tasks have direct access to the resource
-//! fn sys_tick(_t: &mut Threshold, mut r: SYS_TICK::Resources) {
-//! // ..
-//!
-//! *r.COUNTER += 1;
-//!
-//! // ..
-//! }
-//!
-//! fn tim2(_t: &mut Threshold, mut r: TIM2::Resources) {
-//! // ..
-//!
-//! *r.COUNTER += 1;
-//!
-//! // ..
-//! }
-//! ```
-// Auto-generated. Do not modify.
diff --git a/src/examples/_3_preemption.rs b/src/examples/_3_preemption.rs
deleted file mode 100644
index 14c9d925..00000000
--- a/src/examples/_3_preemption.rs
+++ /dev/null
@@ -1,71 +0,0 @@
-//! Two tasks running at *different* priorities with access to the same resource
-//!
-//! ```
-//! #![deny(unsafe_code)]
-//! #![deny(warnings)]
-//! #![no_std]
-//!
-//! extern crate cortex_m_rtfm as rtfm;
-//! extern crate stm32f103xx;
-//!
-//! use rtfm::{app, Resource, Threshold};
-//!
-//! app! {
-//! device: stm32f103xx,
-//!
-//! resources: {
-//! static COUNTER: u64 = 0;
-//! },
-//!
-//! tasks: {
-//! // The `SYS_TICK` task has higher priority than `TIM2`
-//! SYS_TICK: {
-//! path: sys_tick,
-//! priority: 2,
-//! resources: [COUNTER],
-//! },
-//!
-//! TIM2: {
-//! path: tim2,
-//! priority: 1,
-//! resources: [COUNTER],
-//! },
-//! },
-//! }
-//!
-//! fn init(_p: init::Peripherals, _r: init::Resources) {
-//! // ..
-//! }
-//!
-//! fn idle() -> ! {
-//! loop {
-//! rtfm::wfi();
-//! }
-//! }
-//!
-//! fn sys_tick(_t: &mut Threshold, mut r: SYS_TICK::Resources) {
-//! // ..
-//!
-//! // This task can't be preempted by `tim2` so it has direct access to the
-//! // resource data
-//! *r.COUNTER += 1;
-//!
-//! // ..
-//! }
-//!
-//! fn tim2(t: &mut Threshold, mut r: TIM2::Resources) {
-//! // ..
-//!
-//! // As this task runs at lower priority it needs a critical section to
-//! // prevent `sys_tick` from preempting it while it modifies this resource
-//! // data. The critical section is required to prevent data races which can
-//! // lead to undefined behavior.
-//! r.COUNTER.claim_mut(t, |counter, _t| {
-//! // `claim_mut` creates a critical section
-//! *counter += 1;
-//! });
-//!
-//! // ..
-//! }
-//! ```
-// Auto-generated. Do not modify.
diff --git a/src/examples/_4_nested.rs b/src/examples/_4_nested.rs
deleted file mode 100644
index 26f8fd84..00000000
--- a/src/examples/_4_nested.rs
+++ /dev/null
@@ -1,132 +0,0 @@
-//! Nesting claims and how the preemption threshold works
-//!
-//! If you run this program you'll hit the breakpoints as indicated by the
-//! letters in the comments: A, then B, then C, etc.
-//!
-//! ```
-//! #![deny(unsafe_code)]
-//! #![deny(warnings)]
-//! #![no_std]
-//!
-//! extern crate cortex_m_rtfm as rtfm;
-//! extern crate stm32f103xx;
-//!
-//! use rtfm::{app, Resource, Threshold};
-//! use stm32f103xx::Interrupt;
-//!
-//! app! {
-//! device: stm32f103xx,
-//!
-//! resources: {
-//! static LOW: u64 = 0;
-//! static HIGH: u64 = 0;
-//! },
-//!
-//! tasks: {
-//! EXTI0: {
-//! path: exti0,
-//! priority: 1,
-//! resources: [LOW, HIGH],
-//! },
-//!
-//! EXTI1: {
-//! path: exti1,
-//! priority: 2,
-//! resources: [LOW],
-//! },
-//!
-//! EXTI2: {
-//! path: exti2,
-//! priority: 3,
-//! resources: [HIGH],
-//! },
-//! },
-//! }
-//!
-//! fn init(_p: init::Peripherals, _r: init::Resources) {}
-//!
-//! fn idle() -> ! {
-//! // A
-//! rtfm::bkpt();
-//!
-//! // Sets task `exti0` as pending
-//! //
-//! // Because `exti0` has higher priority than `idle` it will be executed
-//! // immediately
-//! rtfm::set_pending(Interrupt::EXTI0); // ~> exti0
-//!
-//! loop {
-//! rtfm::wfi();
-//! }
-//! }
-//!
-//! #[allow(non_snake_case)]
-//! fn exti0(
-//! t: &mut Threshold,
-//! EXTI0::Resources {
-//! LOW: mut low,
-//! HIGH: mut high,
-//! }: EXTI0::Resources,
-//! ) {
-//! // Because this task has a priority of 1 the preemption threshold `t` also
-//! // starts at 1
-//!
-//! // B
-//! rtfm::bkpt();
-//!
-//! // Because `exti1` has higher priority than `exti0` it can preempt it
-//! rtfm::set_pending(Interrupt::EXTI1); // ~> exti1
-//!
-//! // A claim creates a critical section
-//! low.claim_mut(t, |_low, t| {
-//! // This claim increases the preemption threshold to 2
-//! //
-//! // 2 is just high enough to not race with task `exti1` for access to the
-//! // `LOW` resource
-//!
-//! // D
-//! rtfm::bkpt();
-//!
-//! // Now `exti1` can't preempt this task because its priority is equal to
-//! // the current preemption threshold
-//! rtfm::set_pending(Interrupt::EXTI1);
-//!
-//! // But `exti2` can, because its priority is higher than the current
-//! // preemption threshold
-//! rtfm::set_pending(Interrupt::EXTI2); // ~> exti2
-//!
-//! // F
-//! rtfm::bkpt();
-//!
-//! // Claims can be nested
-//! high.claim_mut(t, |_high, _| {
-//! // This claim increases the preemption threshold to 3
-//!
-//! // Now `exti2` can't preempt this task
-//! rtfm::set_pending(Interrupt::EXTI2);
-//!
-//! // G
-//! rtfm::bkpt();
-//! });
-//!
-//! // Upon leaving the critical section the preemption threshold drops back
-//! // to 2 and `exti2` immediately preempts this task
-//! // ~> exti2
-//! });
-//!
-//! // Once again the preemption threshold drops but this time to 1. Now the
-//! // pending `exti1` task can preempt this task
-//! // ~> exti1
-//! }
-//!
-//! fn exti1(_t: &mut Threshold, _r: EXTI1::Resources) {
-//! // C, I
-//! rtfm::bkpt();
-//! }
-//!
-//! fn exti2(_t: &mut Threshold, _r: EXTI2::Resources) {
-//! // E, H
-//! rtfm::bkpt();
-//! }
-//! ```
-// Auto-generated. Do not modify.
diff --git a/src/examples/_5_late_resources.rs b/src/examples/_5_late_resources.rs
deleted file mode 100644
index 7ab90a4e..00000000
--- a/src/examples/_5_late_resources.rs
+++ /dev/null
@@ -1,90 +0,0 @@
-//! Demonstrates initialization of resources in `init`.
-//!
-//! ```
-//! #![deny(unsafe_code)]
-//! #![deny(warnings)]
-//! #![no_std]
-//!
-//! extern crate cortex_m_rtfm as rtfm;
-//! extern crate stm32f103xx;
-//!
-//! use rtfm::{app, Threshold};
-//!
-//! app! {
-//! device: stm32f103xx,
-//!
-//! resources: {
-//! // Usually, resources are initialized with a constant initializer:
-//! static ON: bool = false;
-//!
-//! // However, there are cases where this is not possible or not desired.
-//! // For example, there may not be a sensible value to use, or the type may
-//! // not be constructible in a constant (like `Vec`).
-//! //
-//! // While it is possible to use an `Option` in some cases, that requires
-//! // you to properly initialize it and `.unwrap()` it at every use. It
-//! // also consumes more memory.
-//! //
-//! // To solve this, it is possible to defer initialization of resources to
-//! // `init` by omitting the initializer. Doing that will require `init` to
-//! // return the values of all "late" resources.
-//! static IP_ADDRESS: u32;
-//!
-//! // PORT is used by 2 tasks, making it a shared resource. This just tests
-//! // another internal code path and is not important for the example.
-//! static PORT: u16;
-//! },
-//!
-//! idle: {
-//! // Test that late resources can be used in idle
-//! resources: [IP_ADDRESS],
-//! },
-//!
-//! tasks: {
-//! SYS_TICK: {
-//! priority: 1,
-//! path: sys_tick,
-//! resources: [IP_ADDRESS, PORT, ON],
-//! },
-//!
-//! EXTI0: {
-//! priority: 2,
-//! path: exti0,
-//! resources: [PORT],
-//! }
-//! }
-//! }
-//!
-//! // The signature of `init` is now required to have a specific return type.
-//! fn init(_p: init::Peripherals, _r: init::Resources) -> init::LateResources {
-//! // `init::Resources` does not contain `IP_ADDRESS`, since it is not yet
-//! // initialized.
-//! //_r.IP_ADDRESS; // doesn't compile
-//!
-//! // ...obtain value for IP_ADDRESS from EEPROM/DHCP...
-//! let ip_address = 0x7f000001;
-//!
-//! init::LateResources {
-//! // This struct will contain fields for all resources with omitted
-//! // initializers.
-//! IP_ADDRESS: ip_address,
-//! PORT: 0,
-//! }
-//! }
-//!
-//! fn sys_tick(_t: &mut Threshold, r: SYS_TICK::Resources) {
-//! // Other tasks can access late resources like any other, since they are
-//! // guaranteed to be initialized when tasks are run.
-//!
-//! r.IP_ADDRESS;
-//! }
-//!
-//! fn exti0(_t: &mut Threshold, _r: EXTI0::Resources) {}
-//!
-//! fn idle(_t: &mut Threshold, _r: idle::Resources) -> ! {
-//! loop {
-//! rtfm::wfi();
-//! }
-//! }
-//! ```
-// Auto-generated. Do not modify.
diff --git a/src/examples/_6_safe_static_mut_ref.rs b/src/examples/_6_safe_static_mut_ref.rs
deleted file mode 100644
index 8f7267f5..00000000
--- a/src/examples/_6_safe_static_mut_ref.rs
+++ /dev/null
@@ -1,35 +0,0 @@
-//! Safe creation of `&'static mut` references
-//!
-//! ```
-//! #![deny(unsafe_code)]
-//! #![deny(warnings)]
-//! #![no_std]
-//!
-//! extern crate cortex_m_rtfm as rtfm;
-//! extern crate stm32f103xx;
-//!
-//! use rtfm::app;
-//!
-//! app! {
-//! device: stm32f103xx,
-//!
-//! resources: {
-//! static BUFFER: [u8; 16] = [0; 16];
-//! },
-//!
-//! init: {
-//! resources: [BUFFER],
-//! },
-//! }
-//!
-//! fn init(_p: init::Peripherals, r: init::Resources) {
-//! let _buf: &'static mut [u8; 16] = r.BUFFER;
-//! }
-//!
-//! fn idle() -> ! {
-//! loop {
-//! rtfm::wfi();
-//! }
-//! }
-//! ```
-// Auto-generated. Do not modify.
diff --git a/src/examples/_7_generics.rs b/src/examples/_7_generics.rs
deleted file mode 100644
index 5dafdbf2..00000000
--- a/src/examples/_7_generics.rs
+++ /dev/null
@@ -1,77 +0,0 @@
-//! Working with resources in a generic fashion
-//!
-//! ```
-//! #![deny(unsafe_code)]
-//! #![deny(warnings)]
-//! #![no_std]
-//!
-//! extern crate cortex_m_rtfm as rtfm;
-//! extern crate stm32f103xx;
-//!
-//! use rtfm::{app, Resource, Threshold};
-//! use stm32f103xx::{GPIOA, SPI1};
-//!
-//! app! {
-//! device: stm32f103xx,
-//!
-//! resources: {
-//! static GPIOA: GPIOA;
-//! static SPI1: SPI1;
-//! },
-//!
-//! tasks: {
-//! EXTI0: {
-//! path: exti0,
-//! priority: 1,
-//! resources: [GPIOA, SPI1],
-//! },
-//!
-//! EXTI1: {
-//! path: exti1,
-//! priority: 2,
-//! resources: [GPIOA, SPI1],
-//! },
-//! },
-//! }
-//!
-//! fn init(p: init::Peripherals) -> init::LateResources {
-//! init::LateResources {
-//! GPIOA: p.device.GPIOA,
-//! SPI1: p.device.SPI1,
-//! }
-//! }
-//!
-//! fn idle() -> ! {
-//! loop {
-//! rtfm::wfi();
-//! }
-//! }
-//!
-//! // A generic function that uses some resources
-//! fn work<G, S>(t: &mut Threshold, gpioa: &G, spi1: &S)
-//! where
-//! G: Resource<Data = GPIOA>,
-//! S: Resource<Data = SPI1>,
-//! {
-//! gpioa.claim(t, |_gpioa, t| {
-//! // drive NSS low
-//!
-//! spi1.claim(t, |_spi1, _| {
-//! // transfer data
-//! });
-//!
-//! // drive NSS high
-//! });
-//! }
-//!
-//! // This task needs critical sections to access the resources
-//! fn exti0(t: &mut Threshold, r: EXTI0::Resources) {
-//! work(t, &r.GPIOA, &r.SPI1);
-//! }
-//!
-//! // This task has direct access to the resources
-//! fn exti1(t: &mut Threshold, r: EXTI1::Resources) {
-//! work(t, &r.GPIOA, &r.SPI1);
-//! }
-//! ```
-// Auto-generated. Do not modify.
diff --git a/src/examples/_8_full_syntax.rs b/src/examples/_8_full_syntax.rs
deleted file mode 100644
index cc7fbc22..00000000
--- a/src/examples/_8_full_syntax.rs
+++ /dev/null
@@ -1,87 +0,0 @@
-//! A showcase of the `app!` macro syntax
-//!
-//! ```
-//! #![deny(unsafe_code)]
-//! #![deny(warnings)]
-//! #![no_std]
-//!
-//! extern crate cortex_m_rtfm as rtfm;
-//! extern crate stm32f103xx;
-//!
-//! use rtfm::{app, Threshold};
-//!
-//! app! {
-//! device: stm32f103xx,
-//!
-//! resources: {
-//! static CO_OWNED: u32 = 0;
-//! static ON: bool = false;
-//! static OWNED: bool = false;
-//! static SHARED: bool = false;
-//! },
-//!
-//! init: {
-//! // This is the path to the `init` function
-//! //
-//! // `init` doesn't necessarily has to be in the root of the crate
-//! path: main::init,
-//! },
-//!
-//! idle: {
-//! // This is a path to the `idle` function
-//! //
-//! // `idle` doesn't necessarily has to be in the root of the crate
-//! path: main::idle,
-//! resources: [OWNED, SHARED],
-//! },
-//!
-//! tasks: {
-//! SYS_TICK: {
-//! path: sys_tick,
-//! // If omitted priority is assumed to be 1
-//! // priority: 1,
-//! resources: [CO_OWNED, ON, SHARED],
-//! },
-//!
-//! TIM2: {
-//! // Tasks are enabled, between `init` and `idle`, by default but they
-//! // can start disabled if `false` is specified here
-//! enabled: false,
-//! path: tim2,
-//! priority: 1,
-//! resources: [CO_OWNED],
-//! },
-//! },
-//! }
-//!
-//! mod main {
-//! use rtfm::{self, Resource, Threshold};
-//!
-//! pub fn init(_p: ::init::Peripherals, _r: ::init::Resources) {}
-//!
-//! pub fn idle(t: &mut Threshold, mut r: ::idle::Resources) -> ! {
-//! loop {
-//! *r.OWNED = !*r.OWNED;
-//!
-//! if *r.OWNED {
-//! if r.SHARED.claim(t, |shared, _| *shared) {
-//! rtfm::wfi();
-//! }
-//! } else {
-//! r.SHARED.claim_mut(t, |shared, _| *shared = !*shared);
-//! }
-//! }
-//! }
-//! }
-//!
-//! fn sys_tick(_t: &mut Threshold, mut r: SYS_TICK::Resources) {
-//! *r.ON = !*r.ON;
-//!
-//! *r.CO_OWNED += 1;
-//! }
-//!
-//! fn tim2(_t: &mut Threshold, mut r: TIM2::Resources) {
-//! *r.CO_OWNED += 1;
-//! }
-//! ```
-// Auto-generated. Do not modify.
diff --git a/src/examples/mod.rs b/src/examples/mod.rs
deleted file mode 100644
index 64d1e2ec..00000000
--- a/src/examples/mod.rs
+++ /dev/null
@@ -1,11 +0,0 @@
-//! Examples
-// Auto-generated. Do not modify.
-pub mod _0_zero_tasks;
-pub mod _1_one_task;
-pub mod _2_two_tasks;
-pub mod _3_preemption;
-pub mod _4_nested;
-pub mod _5_late_resources;
-pub mod _6_safe_static_mut_ref;
-pub mod _7_generics;
-pub mod _8_full_syntax;
diff --git a/src/export.rs b/src/export.rs
new file mode 100644
index 00000000..cb63e0ce
--- /dev/null
+++ b/src/export.rs
@@ -0,0 +1,84 @@
+/// IMPLEMENTATION DETAILS. DO NOT USE ANYTHING IN THIS MODULE
+use core::{hint, ptr};
+
+#[cfg(armv7m)]
+use cortex_m::register::basepri;
+pub use cortex_m::{
+ asm::wfi, interrupt, peripheral::scb::SystemHandler, peripheral::syst::SystClkSource,
+ peripheral::Peripherals,
+};
+pub use cortex_m_rt::{entry, exception};
+pub use heapless::consts;
+use heapless::spsc::Queue;
+
+#[cfg(feature = "timer-queue")]
+pub use crate::tq::{isr as sys_tick, NotReady, TimerQueue};
+
+pub type FreeQueue<N> = Queue<u8, N>;
+pub type ReadyQueue<T, N> = Queue<(T, u8), N>;
+
+#[cfg(armv7m)]
+#[inline(always)]
+pub fn run<F>(f: F)
+where
+ F: FnOnce(),
+{
+ let initial = basepri::read();
+ f();
+ unsafe { basepri::write(initial) }
+}
+
+#[cfg(not(armv7m))]
+#[inline(always)]
+pub fn run<F>(f: F)
+where
+ F: FnOnce(),
+{
+ f();
+}
+
+// TODO(MaybeUninit) Until core::mem::MaybeUninit is stabilized we use our own (inefficient)
+// implementation
+pub struct MaybeUninit<T> {
+ value: Option<T>,
+}
+
+impl<T> MaybeUninit<T> {
+ pub const fn uninitialized() -> Self {
+ MaybeUninit { value: None }
+ }
+
+ pub unsafe fn get_ref(&self) -> &T {
+ if let Some(x) = self.value.as_ref() {
+ x
+ } else {
+ hint::unreachable_unchecked()
+ }
+ }
+
+ pub unsafe fn get_mut(&mut self) -> &mut T {
+ if let Some(x) = self.value.as_mut() {
+ x
+ } else {
+ hint::unreachable_unchecked()
+ }
+ }
+
+ pub fn set(&mut self, val: T) {
+ unsafe { ptr::write(&mut self.value, Some(val)) }
+ }
+}
+
+#[inline(always)]
+pub fn assert_send<T>()
+where
+ T: Send,
+{
+}
+
+#[inline(always)]
+pub fn assert_sync<T>()
+where
+ T: Sync,
+{
+}
diff --git a/src/lib.rs b/src/lib.rs
index 9d558875..74cf96ac 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)
}
diff --git a/src/tq.rs b/src/tq.rs
new file mode 100644
index 00000000..88db2f84
--- /dev/null
+++ b/src/tq.rs
@@ -0,0 +1,135 @@
+use core::cmp::{self, Ordering};
+
+use cortex_m::peripheral::{SCB, SYST};
+use heapless::{binary_heap::Min, ArrayLength, BinaryHeap};
+
+use crate::{Instant, Mutex};
+
+pub struct TimerQueue<T, N>
+where
+ N: ArrayLength<NotReady<T>>,
+ T: Copy,
+{
+ pub syst: SYST,
+ pub queue: BinaryHeap<NotReady<T>, N, Min>,
+}
+
+impl<T, N> TimerQueue<T, N>
+where
+ N: ArrayLength<NotReady<T>>,
+ T: Copy,
+{
+ pub fn new(syst: SYST) -> Self {
+ TimerQueue {
+ syst,
+ queue: BinaryHeap::new(),
+ }
+ }
+
+ #[inline]
+ pub unsafe fn enqueue_unchecked(&mut self, nr: NotReady<T>) {
+ let mut is_empty = true;
+ if self
+ .queue
+ .peek()
+ .map(|head| {
+ is_empty = false;
+ nr.instant < head.instant
+ })
+ .unwrap_or(true)
+ {
+ if is_empty {
+ self.syst.enable_interrupt();
+ }
+
+ // set SysTick pending
+ (*SCB::ptr()).icsr.write(1 << 26);
+ }
+
+ self.queue.push_unchecked(nr);
+ }
+}
+
+pub struct NotReady<T>
+where
+ T: Copy,
+{
+ pub index: u8,
+ pub instant: Instant,
+ pub task: T,
+}
+
+impl<T> Eq for NotReady<T> where T: Copy {}
+
+impl<T> Ord for NotReady<T>
+where
+ T: Copy,
+{
+ fn cmp(&self, other: &Self) -> Ordering {
+ self.instant.cmp(&other.instant)
+ }
+}
+
+impl<T> PartialEq for NotReady<T>
+where
+ T: Copy,
+{
+ fn eq(&self, other: &Self) -> bool {
+ self.instant == other.instant
+ }
+}
+
+impl<T> PartialOrd for NotReady<T>
+where
+ T: Copy,
+{
+ fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
+ Some(self.cmp(&other))
+ }
+}
+
+#[inline(always)]
+pub fn isr<TQ, T, N, F>(mut tq: TQ, mut f: F)
+where
+ TQ: Mutex<Data = TimerQueue<T, N>>,
+ T: Copy + Send,
+ N: ArrayLength<NotReady<T>>,
+ F: FnMut(T, u8),
+{
+ loop {
+ // XXX does `#[inline(always)]` improve performance or not?
+ let next = tq.lock(#[inline(always)]
+ |tq| {
+ if let Some(instant) = tq.queue.peek().map(|p| p.instant) {
+ let diff = instant.0.wrapping_sub(Instant::now().0);
+
+ if diff < 0 {
+ // task became ready
+ let m = unsafe { tq.queue.pop_unchecked() };
+
+ Some((m.task, m.index))
+ } else {
+ // set a new timeout
+ const MAX: u32 = 0x00ffffff;
+
+ tq.syst.set_reload(cmp::min(MAX, diff as u32));
+
+ // start counting down from the new reload
+ tq.syst.clear_current();
+
+ None
+ }
+ } else {
+ // the queue is empty
+ tq.syst.disable_interrupt();
+ None
+ }
+ });
+
+ if let Some((task, index)) = next {
+ f(task, index)
+ } else {
+ return;
+ }
+ }
+}