From 2d80f3631bc3bf382ae24c25dbf8ff33af2ad430 Mon Sep 17 00:00:00 2001 From: Jorge Aparicio Date: Sat, 29 Jul 2017 00:34:00 -0500 Subject: [PATCH] update examples --- .gdbinit | 6 ++ .gitignore | 1 + Cargo.toml | 4 +- README.md | 5 +- examples/full-syntax.rs | 1 - examples/nested.rs | 14 ++-- examples/one-task.rs | 19 ++--- examples/preemption.rs | 8 +- examples/two-tasks.rs | 4 - macros/Cargo.toml | 5 +- macros/src/check.rs | 4 +- macros/src/lib.rs | 144 ++++++++++++++++++++++++++++++++- src/examples/_1_one_task.rs | 19 ++--- src/examples/_2_two_tasks.rs | 4 - src/examples/_3_preemption.rs | 8 +- src/examples/_4_nested.rs | 14 ++-- src/examples/_6_full_syntax.rs | 1 - src/lib.rs | 32 ++++---- 18 files changed, 206 insertions(+), 87 deletions(-) create mode 100644 .gdbinit diff --git a/.gdbinit b/.gdbinit new file mode 100644 index 0000000000..7c72d4fb1f --- /dev/null +++ b/.gdbinit @@ -0,0 +1,6 @@ +target remote :3333 + +monitor arm semihosting enable + +load +step diff --git a/.gitignore b/.gitignore index 7a8e51cd0f..8bc31d43a2 100644 --- a/.gitignore +++ b/.gitignore @@ -1,4 +1,5 @@ **/*.rs.bk *.org +.gdb_history Cargo.lock target/ diff --git a/Cargo.toml b/Cargo.toml index 8ce7bdb23d..f7fe92abfc 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -4,7 +4,7 @@ authors = [ "Per Lindgren ", ] categories = ["concurrency", "embedded", "no-std"] -description = "Real Time For the Masses (RTFM), a framework for building concurrent applications, for ARM Cortex-M microcontrollers" +description = "Real Time For the Masses (RTFM) framework for ARM Cortex-M microcontrollers" documentation = "https://docs.rs/cortex-m-rtfm" keywords = ["arm", "cortex-m"] license = "MIT OR Apache-2.0" @@ -14,8 +14,8 @@ version = "0.2.0" [dependencies] cortex-m = "0.3.1" +rtfm-core = "0.1.0" static-ref = "0.2.1" -rtfm-core = { git = "https://github.com/japaric/rtfm-core" } [dependencies.cortex-m-rtfm-macros] path = "macros" diff --git a/README.md b/README.md index eb9555f686..240e2f8fa7 100644 --- a/README.md +++ b/README.md @@ -3,10 +3,9 @@ # `cortex-m-rtfm` -> Real Time For the Masses (RTFM), a framework for building concurrent -> applications, for ARM Cortex-M MCUs +> Real Time For the Masses (RTFM) framework for ARM Cortex-M microcontrollers -# [Manual](https://docs.rs/cortex-m-rtfm) +# [Documentation](https://docs.rs/cortex-m-rtfm) # License diff --git a/examples/full-syntax.rs b/examples/full-syntax.rs index 20e8dfbce7..9b6b394e5e 100644 --- a/examples/full-syntax.rs +++ b/examples/full-syntax.rs @@ -1,6 +1,5 @@ //! A showcase of the `app!` macro syntax #![deny(unsafe_code)] -#![feature(const_fn)] #![feature(proc_macro)] #![no_std] diff --git a/examples/nested.rs b/examples/nested.rs index 7013170910..1c164f86b0 100644 --- a/examples/nested.rs +++ b/examples/nested.rs @@ -3,7 +3,6 @@ //! 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)] -#![feature(const_fn)] #![feature(proc_macro)] #![no_std] @@ -59,13 +58,14 @@ fn idle() -> ! { } } -fn exti0(t: &mut Threshold, r: EXTI0::Resources) { +#[allow(non_snake_case)] +fn exti0( + t: &mut Threshold, + EXTI0::Resources { mut LOW, mut HIGH }: EXTI0::Resources, +) { // Because this task has a priority of 1 the preemption threshold `t` also // starts at 1 - let mut low = r.LOW; - let mut high = r.HIGH; - // B rtfm::bkpt(); @@ -73,7 +73,7 @@ fn exti0(t: &mut Threshold, r: EXTI0::Resources) { rtfm::set_pending(Interrupt::EXTI1); // ~> exti1 // A claim creates a critical section - low.claim_mut(t, |_low, t| { + 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 @@ -94,7 +94,7 @@ fn exti0(t: &mut Threshold, r: EXTI0::Resources) { rtfm::bkpt(); // Claims can be nested - high.claim_mut(t, |_high, _| { + HIGH.claim_mut(t, |_high, _| { // This claim increases the preemption threshold to 3 // Now `exti2` can't preempt this task diff --git a/examples/one-task.rs b/examples/one-task.rs index 556177d0f1..e58d9fcd90 100644 --- a/examples/one-task.rs +++ b/examples/one-task.rs @@ -1,6 +1,5 @@ //! An application with one task #![deny(unsafe_code)] -#![feature(const_fn)] #![feature(proc_macro)] #![no_std] @@ -34,17 +33,6 @@ app! { // Path to the task handler path: sys_tick, - // This is the priority of the task. - // - // 1 is the lowest priority a task can have, and the maximum - // priority is determined by the number of priority bits the device - // has. `stm32f103xx` has 4 priority bits so 16 is the maximum valid - // value. - // - // You can omit this field. If you do the priority is assumed to be - // 1. - priority: 1, - // These are the resources this task has access to. // // A resource can be a peripheral like `GPIOC` or a static variable @@ -54,7 +42,10 @@ app! { } } -fn init(p: init::Peripherals, _r: init::Resources) { +fn init(p: init::Peripherals, r: init::Resources) { + // `init` can modify all the `resources` declared in `app!` + r.ON; + // power on GPIOC p.RCC.apb2enr.modify(|_, w| w.iopcen().enabled()); @@ -81,7 +72,7 @@ fn idle() -> ! { // // `_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. `TIMER0_A1::Resources` +// `r` is the set of resources this task has access to. `SYS_TICK::Resources` // has one field per resource declared in `app!`. fn sys_tick(_t: &mut Threshold, r: SYS_TICK::Resources) { // toggle state diff --git a/examples/preemption.rs b/examples/preemption.rs index 256b9bddfd..5fda37d57e 100644 --- a/examples/preemption.rs +++ b/examples/preemption.rs @@ -1,6 +1,5 @@ //! Two tasks running at *different* priorities with access to the same resource #![deny(unsafe_code)] -#![feature(const_fn)] #![feature(proc_macro)] #![no_std] @@ -58,8 +57,11 @@ 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| { **counter += 1; }); + // lead to undefined behavior. + r.COUNTER.claim_mut(t, |counter, _t| { + // `claim_mut` creates a critical section + **counter += 1; + }); // .. } diff --git a/examples/two-tasks.rs b/examples/two-tasks.rs index ea059a0248..2200e5baa0 100644 --- a/examples/two-tasks.rs +++ b/examples/two-tasks.rs @@ -1,7 +1,5 @@ //! Two tasks running at the *same* priority with access to the same resource - #![deny(unsafe_code)] -#![feature(const_fn)] #![feature(proc_macro)] #![no_std] @@ -31,8 +29,6 @@ app! { }, } -// When data resources are declared in the top `resources` field, `init` will -// have full access to them fn init(_p: init::Peripherals, _r: init::Resources) { // .. } diff --git a/macros/Cargo.toml b/macros/Cargo.toml index 4237e21e8a..a62d20892b 100644 --- a/macros/Cargo.toml +++ b/macros/Cargo.toml @@ -6,11 +6,8 @@ version = "0.1.0" [dependencies] error-chain = "0.10.0" quote = "0.3.15" +rtfm-syntax = "0.1.0" syn = "0.11.11" -[dependencies.rtfm-syntax] -git = "https://github.com/japaric/rtfm-syntax" -optional = false - [lib] proc-macro = true diff --git a/macros/src/check.rs b/macros/src/check.rs index 42dd3c9eca..3cd112acfd 100644 --- a/macros/src/check.rs +++ b/macros/src/check.rs @@ -2,7 +2,7 @@ use std::collections::HashMap; use syn::{Ident, Path}; use syntax::check::{self, Idle, Init}; -use syntax::{self, Idents, Statics}; +use syntax::{self, Resources, Statics}; use syntax::error::*; @@ -51,7 +51,7 @@ pub struct Task { pub kind: Kind, pub path: Path, pub priority: u8, - pub resources: Idents, + pub resources: Resources, } pub fn app(app: check::App) -> Result { diff --git a/macros/src/lib.rs b/macros/src/lib.rs index 8ea87fa076..2a1f72e418 100644 --- a/macros/src/lib.rs +++ b/macros/src/lib.rs @@ -1,5 +1,4 @@ //! Procedural macros for the RTFM framework - #![deny(warnings)] #![feature(proc_macro)] #![recursion_limit = "128"] @@ -14,7 +13,6 @@ extern crate syn; use proc_macro::TokenStream; use syntax::App; - use syntax::error::*; mod analyze; @@ -23,6 +21,148 @@ mod trans; /// The `app!` macro, a macro used to specify the tasks and resources of a /// RTFM application. +/// +/// The contents of this macro uses a `key: value` syntax. All the possible keys +/// are shown below: +/// +/// ``` text +/// app! { +/// device: .., +/// +/// resources: { .. }, +/// +/// init: { .. }, +/// +/// idle: { .. }, +/// +/// tasks: { .. }, +/// } +/// ``` +/// +/// # `device` +/// +/// The value of this key is a Rust path, like `foo::bar::baz`, that must point to +/// a *device crate*, a crate generated using `svd2rust`. +/// +/// # `resources` +/// +/// This key is optional. Its value is a list of `static` variables. These +/// variables are the data that can be safely accessed, modified and shared by +/// tasks. +/// +/// ``` text +/// resources: { +/// static A: bool = false; +/// static B: i32 = 0; +/// static C: [u8; 16] = [0; 16]; +/// static D: Thing = Thing::new(..); +/// } +/// ``` +/// +/// If this key is omitted its value defaults to an empty list. +/// +/// # `init` +/// +/// This key is optional. Its value is a set of key values. All the possible +/// keys are shown below: +/// +/// ``` text +/// init: { +/// path: .., +/// } +/// ``` +/// +/// ## `init.path` +/// +/// This key is optional. Its value is a Rust path, like `foo::bar::baz`, that +/// points to the initialization function. +/// +/// If the key is omitted its value defaults to `init`. +/// +/// # `idle` +/// +/// This key is optional. Its value is a set of key values. All the possible +/// keys are shown below: +/// +/// ``` text +/// idle: { +/// path: .., +/// resources: [..], +/// } +/// ``` +/// +/// ## `idle.path` +/// +/// This key is optional. Its value is a Rust path, like `foo::bar::baz`, that +/// points to the idle loop function. +/// +/// If the key is omitted its value defaults to `idle`. +/// +/// ## `idle.resources` +/// +/// This key is optional. Its value is a list of resources the `idle` loop has +/// access to. The resources in this list can refer to the resources listed in +/// the top `resources` key. If the name doesn't match one of the resources +/// listed in the top `resources` key the resource is assumed to be a +/// peripheral. +/// +/// If omitted its value defaults to an empty list. +/// +/// # `tasks` +/// +/// This key is optional. Its value is a list of tasks. Each task itself is a +/// set of key value pair. The full syntax is shown below: +/// +/// ``` text +/// tasks: { +/// $TASK: { +/// enabled: .., +/// path: .., +/// priority: .., +/// resources: [..], +/// }, +/// } +/// ``` +/// +/// If this key is omitted its value is assumed to be an empty list. +/// +/// ## `tasks.$TASK` +/// +/// The key must be either a Cortex-M exception or a device specific interrupt. +/// `PENDSV`, `SVCALL`, `SYS_TICK` are considered as exceptions. All other names +/// are assumed to be interrupts. +/// +/// ## `tasks.$TASK.enabled` +/// +/// This key is optional for interrupts and forbidden for exceptions. Its value +/// must be a boolean and indicates whether the interrupt will be enabled +/// (`true`) or disabled (`false`) after `init` ends and before `idle` starts. +/// +/// If this key is omitted its value defaults to `true`. +/// +/// ## `tasks.$TASK.path` +/// +/// The value of this key is a Rust path, like `foo::bar::baz`, that points to +/// the handler of this task. +/// +/// ## `tasks.$TASK.priority` +/// +/// This key is optional. Its value is an integer with type `u8` that specifies +/// the priority of this task. The minimum valid priority is 1. The maximum +/// valid priority depends on the number of the NVIC priority bits the device +/// has; if the device has 4 priority bits the maximum allowed value would be +/// 16. +/// +/// If this key is omitted its value defaults to `1`. +/// +/// ## `tasks.$TASK.resources` +/// +/// This key is optional. Its value is a list of resources this task has access +/// to. The resources in this list can refer to the resources listed in the top +/// `resources` key. If the name doesn't match one of the resources listed in +/// the top `resources` key the resource is assumed to be a peripheral. +/// +/// If omitted its value defaults to an empty list. #[proc_macro] pub fn app(ts: TokenStream) -> TokenStream { match run(ts) { diff --git a/src/examples/_1_one_task.rs b/src/examples/_1_one_task.rs index 1bccc2199e..614db2aa8d 100644 --- a/src/examples/_1_one_task.rs +++ b/src/examples/_1_one_task.rs @@ -2,7 +2,6 @@ //! //! ``` //! #![deny(unsafe_code)] -//! #![feature(const_fn)] //! #![feature(proc_macro)] //! #![no_std] //! @@ -36,17 +35,6 @@ //! // Path to the task handler //! path: sys_tick, //! -//! // This is the priority of the task. -//! // -//! // 1 is the lowest priority a task can have, and the maximum -//! // priority is determined by the number of priority bits the device -//! // has. `stm32f103xx` has 4 priority bits so 16 is the maximum valid -//! // value. -//! // -//! // You can omit this field. If you do the priority is assumed to be -//! // 1. -//! priority: 1, -//! //! // These are the resources this task has access to. //! // //! // A resource can be a peripheral like `GPIOC` or a static variable @@ -56,7 +44,10 @@ //! } //! } //! -//! fn init(p: init::Peripherals, _r: init::Resources) { +//! fn init(p: init::Peripherals, r: init::Resources) { +//! // `init` can modify all the `resources` declared in `app!` +//! r.ON; +//! //! // power on GPIOC //! p.RCC.apb2enr.modify(|_, w| w.iopcen().enabled()); //! @@ -83,7 +74,7 @@ //! // //! // `_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. `TIMER0_A1::Resources` +//! // `r` is the set of resources this task has access to. `SYS_TICK::Resources` //! // has one field per resource declared in `app!`. //! fn sys_tick(_t: &mut Threshold, r: SYS_TICK::Resources) { //! // toggle state diff --git a/src/examples/_2_two_tasks.rs b/src/examples/_2_two_tasks.rs index 451293cfe8..5db991b0e2 100644 --- a/src/examples/_2_two_tasks.rs +++ b/src/examples/_2_two_tasks.rs @@ -1,9 +1,7 @@ //! Two tasks running at the *same* priority with access to the same resource //! //! ``` -//! //! #![deny(unsafe_code)] -//! #![feature(const_fn)] //! #![feature(proc_macro)] //! #![no_std] //! @@ -33,8 +31,6 @@ //! }, //! } //! -//! // When data resources are declared in the top `resources` field, `init` will -//! // have full access to them //! fn init(_p: init::Peripherals, _r: init::Resources) { //! // .. //! } diff --git a/src/examples/_3_preemption.rs b/src/examples/_3_preemption.rs index 1f6b244bcb..9dc8983b5a 100644 --- a/src/examples/_3_preemption.rs +++ b/src/examples/_3_preemption.rs @@ -2,7 +2,6 @@ //! //! ``` //! #![deny(unsafe_code)] -//! #![feature(const_fn)] //! #![feature(proc_macro)] //! #![no_std] //! @@ -60,8 +59,11 @@ //! // 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| { **counter += 1; }); +//! // lead to undefined behavior. +//! r.COUNTER.claim_mut(t, |counter, _t| { +//! // `claim_mut` creates a critical section +//! **counter += 1; +//! }); //! //! // .. //! } diff --git a/src/examples/_4_nested.rs b/src/examples/_4_nested.rs index d0306210d8..94af0bee6d 100644 --- a/src/examples/_4_nested.rs +++ b/src/examples/_4_nested.rs @@ -5,7 +5,6 @@ //! //! ``` //! #![deny(unsafe_code)] -//! #![feature(const_fn)] //! #![feature(proc_macro)] //! #![no_std] //! @@ -61,13 +60,14 @@ //! } //! } //! -//! fn exti0(t: &mut Threshold, r: EXTI0::Resources) { +//! #[allow(non_snake_case)] +//! fn exti0( +//! t: &mut Threshold, +//! EXTI0::Resources { mut LOW, mut HIGH }: EXTI0::Resources, +//! ) { //! // Because this task has a priority of 1 the preemption threshold `t` also //! // starts at 1 //! -//! let mut low = r.LOW; -//! let mut high = r.HIGH; -//! //! // B //! rtfm::bkpt(); //! @@ -75,7 +75,7 @@ //! rtfm::set_pending(Interrupt::EXTI1); // ~> exti1 //! //! // A claim creates a critical section -//! low.claim_mut(t, |_low, t| { +//! 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 @@ -96,7 +96,7 @@ //! rtfm::bkpt(); //! //! // Claims can be nested -//! high.claim_mut(t, |_high, _| { +//! HIGH.claim_mut(t, |_high, _| { //! // This claim increases the preemption threshold to 3 //! //! // Now `exti2` can't preempt this task diff --git a/src/examples/_6_full_syntax.rs b/src/examples/_6_full_syntax.rs index 449bee6cdd..805206578d 100644 --- a/src/examples/_6_full_syntax.rs +++ b/src/examples/_6_full_syntax.rs @@ -2,7 +2,6 @@ //! //! ``` //! #![deny(unsafe_code)] -//! #![feature(const_fn)] //! #![feature(proc_macro)] //! #![no_std] //! diff --git a/src/lib.rs b/src/lib.rs index dc856596df..6188ed3174 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,5 +1,4 @@ -//! Real Time For the Masses (RTFM), a framework for building concurrent -//! applications, for ARM Cortex-M microcontrollers +//! 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, @@ -37,24 +36,24 @@ //! //! # 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 +//! The application crate must depend on a device crate generated using +//! [`svd2rust`] v0.11.x and the "rt" feature of that crate must be enabled. The +//! SVD file used to generate the device crate *must* contain [``] +//! information. //! //! [`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/ +//! +//! # More documentation +//! +//! The `app!` macro is documented [here](../cortex_m_rtfm_macros/fn.app.html). //! //! # Examples //! -//! In increasing grade of complexity, see the [examples](./examples/index.html) +//! 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] @@ -74,7 +73,9 @@ use cortex_m::register::basepri; pub mod examples; -/// Executes the closure `f` in an interrupt free context +/// Executes the closure `f` in a preemption free context +/// +/// During the execution of the closure no task can preempt the current task. pub fn atomic(t: &mut Threshold, f: F) -> R where F: FnOnce(&mut Threshold) -> R, @@ -127,11 +128,10 @@ where } } -/// Sets an interrupt as pending +/// Sets an interrupt, that is a task, 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. +/// 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(interrupt: I) where I: Nr,