From 4992db78777c408a545a787e43450d4947144550 Mon Sep 17 00:00:00 2001 From: Jorge Aparicio Date: Fri, 21 Apr 2017 00:24:54 -0500 Subject: [PATCH] more docs, remove Ceiling / Priority / Level traits --- Cargo.toml | 11 +- README.md | 6 +- build.rs | 38 +- src/lib.rs | 605 +++++++++++++++++++++++----- tests/cfail/borrow.rs | 4 +- tests/cfail/ceiling.rs | 2 +- tests/cfail/lock.rs | 4 +- tests/cfail/race-1.rs | 8 +- tests/cfail/race-2.rs | 6 +- tests/cfail/tasks-p0.rs | 12 +- tests/cfail/tasks-same-handler.rs | 10 +- tests/cfail/tasks-wrong-idle.rs | 12 +- tests/cfail/tasks-wrong-init.rs | 12 +- tests/cfail/tasks-wrong-priority.rs | 8 +- tests/cfail/tasks-wrong-task.rs | 10 +- 15 files changed, 578 insertions(+), 170 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 3304796921..ff82901490 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,7 +1,10 @@ [package] -authors = ["Jorge Aparicio "] +authors = [ + "Jorge Aparicio ", + "Per Lindgren ", +] build = "build.rs" -name = "cortex-m-srp" +name = "cortex-m-rtfm" version = "0.1.0" [build-dependencies] @@ -9,9 +12,9 @@ quote = "0.3.15" syn = "0.11.10" [dependencies] -cortex-m = "0.2.2" +cortex-m = "0.2.4" +static-ref = "0.1.0" typenum = "1.7.0" -static-ref = { git = "https://github.com/japaric/static-ref" } [dev-dependencies] compiletest_rs = "0.2.5" diff --git a/README.md b/README.md index 687d88f46e..53fb22ad04 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,8 @@ -# `cortex-m-srp` +# `cortex-m-rtfm` + +> Real Time For the Masses (Cortex-M edition) + +# [Manual](https://docs.rs/cortex-m-rtfm) # License diff --git a/build.rs b/build.rs index 3fcb6954a1..0de38e02ea 100644 --- a/build.rs +++ b/build.rs @@ -44,25 +44,37 @@ fn main() { let c = Ident::new(format!("C{}", i)); let u = Ident::new(format!("U{}", i)); + let doc = format!("A ceiling of {}", i); tokens.push( quote! { - /// Ceiling + #[doc = #doc] pub type #c = C<::typenum::#u>; - - unsafe impl Ceiling for #c {} }, ); } // Priorities - for i in 1..(1 << bits) + 1 { + for i in 0..(1 << bits) + 1 { let c = Ident::new(format!("C{}", i)); let p = Ident::new(format!("P{}", i)); let u = Ident::new(format!("U{}", i)); + let doc = if i == 0 { + format!("A priority of 0, the lowest priority") + } else { + format!( + "A priority of {}{}", + i, + if i == (1 << bits) { + ", the highest priority" + } else { + "" + } + ) + }; tokens.push( quote! { - /// Priority + #[doc = #doc] pub type #p = P<::typenum::#u>; impl #p { @@ -73,19 +85,11 @@ fn main() { } } } - - unsafe impl Priority for #p {} - - unsafe impl Level for ::typenum::#u { - fn hw() -> u8 { - logical2hw(::typenum::#u::to_u8()) - } - } }, ); } - // GreaterThanOrEqual + // GreaterThanOrEqual & LessThanOrEqual for i in 0..(1 << bits) + 1 { for j in 0..(i + 1) { let i = Ident::new(format!("U{}", i)); @@ -95,16 +99,20 @@ fn main() { quote! { unsafe impl GreaterThanOrEqual<::typenum::#j> for ::typenum::#i {} + + unsafe impl LessThanOrEqual<::typenum::#i> for + ::typenum::#j {} }, ); } } let u = Ident::new(format!("U{}", (1 << bits))); + let c = Ident::new(format!("C{}", (1 << bits))); tokens.push( quote! { /// Maximum ceiling - pub type CMAX = C<::typenum::#u>; + pub type CMAX = #c; /// Maximum priority level pub type UMAX = ::typenum::#u; diff --git a/src/lib.rs b/src/lib.rs index 8e276e6988..ad79850f0d 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,4 +1,352 @@ -//! Stack Resource Policy +//! RTFM: Real Time For the Masses (ARM Cortex-M edition) +//! +//! RTFM is a framework for building event driven / real time applications. +//! +//! 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 +//! scheduling policy. (Check the [references] for details) +//! +//! [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. +//! - Supports prioritizing tasks and, thus, preemptive multitasking. +//! - Data sharing through fine grained, *partial* critical sections. +//! - Deadlock free execution guaranteed at compile time. +//! - Minimal overhead as the scheduler has no software component / runtime; the +//! hardware does all the scheduling. +//! - Full support for all Cortex M3, M4 and M7 devices. M0(+) is partially +//! supported at this time. +//! - The number of priority levels is configurable at compile time through the +//! `P2` (4 levels), `P3` (8 levels), etc. Cargo features. The number of +//! priority levels supported by the hardware is device specific but this +//! crate defaults to 16 as that's the most common scenario. +//! - This task model is amenable to known WCET (Worst Case Execution Time) +//! analysis and scheduling analysis techniques. (Though we don't have any +//! tooling for that ATM.) +//! +//! # Limitations +//! +//! - Task priorities must be fixed at runtime. +//! +//! # Dependencies +//! +//! - A device crate generated using [`svd2rust`] v0.6.x +//! - 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.6.1/svd2rust/ +//! [`cortex-m-rt`]: https://docs.rs/cortex-m-rt/0.1.1/cortex_m_rt/ +//! +//! # Examples +//! +//! Ordered in increasing level of complexity: +//! +//! - [Zero tasks](./index.html#zero-tasks) +//! - [One task](./index.html#one-task) +//! - [Two "serial" tasks](./index.html#two-serial-tasks) +//! - [Preemptive multitasking](./index.html#preemptive-multitasking) +//! - [Peripherals as resources](./index.html#peripherals-as-resources) +//! +//! ## Zero tasks +//! +//! ``` ignore +//! #![no_std] +//! +//! #[macro_use] // for the `hprintln!` macro +//! extern crate cortex_m; +//! +//! // before main initialization + `start` lang item +//! extern crate cortex_m_rt; +//! +//! #[macro_use] // for the `tasks!` macro +//! extern crate cortex_m_rtfm as rtfm; +//! +//! // device crate generated using svd2rust +//! extern crate stm32f100xx; +//! +//! use rtfm::{C16, P0}; +//! +//! // Declare tasks. None in this example +//! tasks!(stm32f100xx, {}); +//! +//! // INITIALIZATION PHASE +//! fn init(_priority: P0, _ceiling: &C16) { +//! hprintln!("INIT"); +//! } +//! +//! // IDLE LOOP +//! fn idle(_priority: P0) -> ! { +//! hprintln!("IDLE"); +//! +//! // Sleep +//! loop { +//! rtfm::wfi(); +//! } +//! } +//! ``` +//! +//! Expected output: +//! +//! ``` text +//! INIT +//! IDLE +//! ``` +//! +//! The `tasks!` macro forces the following structure into your program: +//! +//! - `init`, the initialization phase, is run first. This function is executed +//! in a *global* critical section and can't be preempted. +//! +//! - `idle`, a never ending function that runs after `init`. +//! +//! Note that both `init` and `idle` have priority 0, the lowest priority. +//! +//! # One task +//! +//! ``` ignore +//! #![no_std] +//! +//! extern crate cortex_m_rt; +//! #[macro_use] +//! extern crate cortex_m_rtfm as rtfm; +//! extern crate stm32f100xx; +//! +//! use core::cell::Cell; +//! +//! use stm32f100xx::interrupt::Tim7Irq; +//! use rtfm::{C16, Local, P0, P1}; +//! +//! // INITIALIZATION PHASE +//! fn init(_priority: P0, _ceiling: &C16) { +//! // Configure TIM7 for periodic interrupts +//! // Configure GPIO for LED driving +//! } +//! +//! // IDLE LOOP +//! fn idle(_priority: P0) -> ! { +//! // Sleep +//! loop { rtfm::wfi() } +//! } +//! +//! // TASKS +//! tasks!(stm32f100xx, { +//! periodic: (Tim7Irq, P1), +//! }); +//! +//! fn periodic(task: Tim7Irq, _priority: P1) { +//! // Task local data +//! static STATE: Local, Tim7Irq> = Local::new(Cell::new(false)); +//! +//! let state = STATE.borrow(&task); +//! +//! // Toggle state +//! state.set(!state.get()); +//! +//! // Blink an LED +//! if state.get() { +//! LED.on(); +//! } else { +//! LED.off(); +//! } +//! } +//! ``` +//! +//! Here we define a task named `periodic` and bind it to the `Tim7Irq` +//! interrupt handler. Every time the `Tim7Irq` interrupt is triggered, the +//! `periodic` runs. We assign to this task a priority of 1, `P1`; this is the +//! lowest priority that a task can have. +//! +//! We use the [`Local`](./struct.Local.html) abstraction to add state to the +//! task; this task local data will be preserved across runs of the `periodic` +//! task. Note that `STATE` is owned by the `periodic` task, in the sense that +//! no other task can access it; this is reflected in its type signature (the +//! `Tim7Irq` type parameter). +//! +//! # Two "serial" tasks +//! +//! ``` ignore +//! #![no_std] +//! +//! extern crate cortex_m_rt; +//! #[macro_use] +//! extern crate cortex_m_rtfm as rtfm; +//! extern crate stm32f100xx; +//! +//! use core::cell::Cell; +//! +//! use stm32f100xx::interrupt::{Tim6DacIrq, Tim7Irq}; +//! use rtfm::{C1, C16, P0, P1, Resource}; +//! +//! // omitted: `idle`, `init` +//! +//! tasks!(stm32f100xx, { +//! t1: (Tim6DacIrq, P1), +//! t2: (Tim7Irq, P1), +//! }); +//! +//! // Data shared between tasks `t1` and `t2` +//! static COUNTER: Resource, C1> = Resource::new(Cell::new(0)); +//! +//! fn t1(_task: Tim6DacIrq, priority: P1) { +//! let ceiling = priority.as_ceiling(); +//! +//! let counter = COUNTER.borrow(&priority, &ceiling); +//! +//! counter.set(counter.get() + 1); +//! } +//! +//! fn t2(_task: Tim7Irq, priority: P1) { +//! let ceiling = priority.as_ceiling(); +//! +//! let counter = COUNTER.borrow(&priority, &ceiling); +//! +//! counter.set(counter.get() + 2); +//! } +//! ``` +//! +//! 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 +//! the sense that `t1` can only run *after* `t2` is done and vice versa; i.e. +//! there's no preemption. +//! +//! To share data between these two tasks, we use the +//! [`Resource`](./struct.Resource.html) abstraction. As the tasks can't preempt +//! each other, they can access the `COUNTER` resource using the zero cost +//! [`borrow`](./struct.Resource.html#method.borrow) method -- no +//! synchronization needed. +//! +//! `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 +//! the priorities of all the tasks that access the resource -- in this case, +//! `C1 == max(P1, P1)`. If you try a smaller value like `C0`, you'll find out +//! that your program doesn't compile. +//! +//! # Preemptive multitasking +//! +//! ``` ignore +//! #![no_std] +//! +//! extern crate cortex_m_rt; +//! #[macro_use] +//! extern crate cortex_m_rtfm as rtfm; +//! extern crate stm32f100xx; +//! +//! use core::cell::Cell; +//! +//! use stm32f100xx::interrupt::{Tim6DacIrq, Tim7Irq}; +//! use rtfm::{C2, C16, P0, P1, P2, Resource}; +//! +//! // omitted: `idle`, `init` +//! +//! tasks!(stm32f100xx, { +//! t1: (Tim6DacIrq, P1), +//! t2: (Tim7Irq, P2), +//! }); +//! +//! static COUNTER: Resource, C2> = Resource::new(Cell::new(0)); +//! +//! fn t1(_task: Tim6DacIrq, priority: P1) { +//! // .. +//! +//! COUNTER.lock(&priority, |r1, _| { +//! r1.set(r1.get() + 1); +//! }); +//! +//! // .. +//! } +//! +//! fn t2(_task: Tim7Irq, priority: P2) { +//! let ceiling = priority.as_ceiling(); +//! +//! let counter = COUNTER.borrow(&priority, &ceiling); +//! +//! counter.set(counter.get() + 2); +//! } +//! ``` +//! +//! Now we have a variation of the previous example. Like before, `t1` has a +//! priority of 1 (`P1`) but `t2` now has a priority of 2 (`P2`). This means +//! that `t2` can preempt `t1` if a `Tim7Irq` interrupt occurs while `t1` is +//! being executed. +//! +//! 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 +//! accomplished using the [`lock`](./struct.Resource.html#method.lock) method. +//! This method creates a critical section, denoted by a closure, for whose span +//! `COUNTER` is accessible but `t2` is blocked from preempting `t1`. +//! +//! 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`. +//! +//! Note that the ceiling of `COUNTER` also changed to `C2`. This is required +//! because the ceiling must be the maximum between `P1` and `P2`. +//! +//! Finally, it should be noted that `COUNTER.lock` will only 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 able to preempt the +//! current task / critical section. +//! +//! # Peripherals as resources +//! +//! ``` ignore +//! #![no_std] +//! +//! extern crate cortex_m_rt; +//! #[macro_use] +//! extern crate cortex_m_rtfm as rtfm; +//! extern crate stm32f100xx; +//! +//! use rtfm::{C0, C16, P0, Peripheral}; +//! +//! static GPIOA: Peripheral = +//! unsafe { Peripheral::new(stm32f100xx::GPIOA) }; +//! +//! static RCC: Peripheral = +//! unsafe { Peripheral::new(stm32f100xx::RCC) }; +//! +//! tasks!(stm32f100xx, {}); +//! +//! fn init(priority: P0, ceiling: &C16) { +//! let gpioa = GPIOA.borrow(&priority, &ceiling); +//! let rcc = RCC.borrow(&priority, &ceiling); +//! +//! // .. +//! } +//! +//! fn idle(_priority: P0) -> ! { +//! // Sleep +//! loop { rtfm::wfi() } +//! } +//! ``` +//! +//! Peripherals are global resources too and as such they can be protected in +//! the same way as `Resource`s using the +//! [`Peripheral`](./struct.Peripheral.html) abstraction. +//! +//! `Peripheral` and `Resource` has pretty much the same API except that +//! `Peripheral.new` is `unsafe`. Care must be taken to NOT alias peripherals; +//! i.e. create two `Peripheral`s that point to the same register block address. +//! +//! # References +//! +//! - Baker, T. P. (1991). Stack-based scheduling of realtime processes. +//! *Real-Time Systems*, 3(1), 67-99. +//! +//! > The seminal Stack Resource Policy paper. [PDF]. +//! +//! [PDF]: 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] +//! +//! [PDF]: http://www.diva-portal.org/smash/get/diva2:1005680/FULLTEXT01.pdf #![deny(missing_docs)] #![deny(warnings)] @@ -18,16 +366,16 @@ use cortex_m::interrupt::Nr; #[cfg(not(thumbv6m))] use cortex_m::register::{basepri, basepri_max}; use static_ref::Ref; -use typenum::Unsigned; +use typenum::{U0, Unsigned}; #[cfg(not(thumbv6m))] use typenum::{Cmp, Greater, Less}; -pub use cortex_m::ctxt::Local; pub use cortex_m::asm::{bkpt, wfi}; #[doc(hidden)] -pub use cortex_m::peripheral::NVIC; +pub use cortex_m::peripheral::NVIC as _NVIC; +/// Compiler barrier macro_rules! barrier { () => { asm!("" @@ -38,18 +386,46 @@ macro_rules! barrier { } } -/// A resource -pub struct Resource { - _ceiling: PhantomData, +/// Task local data +/// +/// This data can only be accessed by the task `T` +pub struct Local { + _task: PhantomData, + data: UnsafeCell, +} + +impl Local { + /// Creates a task local variable with some initial `value` + pub const fn new(value: T) -> Self { + Local { + _task: PhantomData, + data: UnsafeCell::new(value), + } + } + + /// Borrows the task local data for the duration of the task + pub fn borrow<'task>(&'static self, _task: &'task TASK) -> &'task T { + unsafe { &*self.data.get() } + } +} + +unsafe impl Sync for Local {} + +/// A resource with ceiling `C` +/// +/// Only tasks with priority equal to or smaller than `C` can access this resource +pub struct Resource { + _ceiling: PhantomData, data: UnsafeCell, } -impl Resource { - /// Creates a new resource with ceiling `C` - pub const fn new(data: T) -> Self - where - C: Ceiling, - { +impl Resource> +where + CEILING: GreaterThanOrEqual, + CEILING: LessThanOrEqual, +{ + /// Creates a new resource with the specified `CEILING` + pub const fn new(data: T) -> Self { Resource { _ceiling: PhantomData, data: UnsafeCell::new(data), @@ -58,17 +434,19 @@ impl Resource { } impl Resource> { - /// Borrows the resource for the duration of another resource's critical - /// section + /// Borrows the resource for the duration of a critical section /// - /// This operation is zero cost and doesn't impose any additional blocking - pub fn borrow<'cs, PRIORITY, SCEILING>( + /// This operation is zero cost and doesn't impose any additional blocking. + /// + /// **NOTE** Only tasks with a priority equal to or smaller than the + /// resource ceiling can access the resource. + pub fn borrow<'cs, PRIORITY, CCEILING>( &'static self, _priority: &P, - _system_ceiling: &'cs C, + _current_ceiling: &'cs C, ) -> Ref<'cs, T> where - SCEILING: GreaterThanOrEqual, + CCEILING: GreaterThanOrEqual, CEILING: GreaterThanOrEqual, { unsafe { Ref::new(&*self.data.get()) } @@ -81,16 +459,26 @@ impl Resource> { /// preempting the current task. /// /// Within this critical section, resources with ceiling equal to or smaller - /// than `CEILING` can be borrowed at zero cost. See - /// [Resource.borrow](struct.Resource.html#method.borrow). + /// than `CEILING` can be borrowed at zero cost using the + /// [Resource.borrow](struct.Resource.html#method.borrow) method. + /// + /// **NOTE** Only tasks with a priority equal to or smaller than the + /// resource ceiling can access the resource. #[cfg(not(thumbv6m))] - pub fn lock(&'static self, _priority: &P, f: F) -> R - where F: FnOnce(Ref, &C) -> R, - CEILING: Cmp + Cmp + Level + pub fn lock( + &'static self, + _priority: &P, + f: F, + ) -> R + where + F: FnOnce(Ref, &C) -> R, + CEILING: Cmp, + CEILING: Cmp, + CEILING: Unsigned, { unsafe { let old_basepri = basepri::read(); - basepri_max::write(::hw()); + basepri_max::write(logical2hw(CEILING::to_u8())); barrier!(); let ret = f(Ref::new(&*self.data.get()), &C { _marker: PhantomData }); @@ -101,11 +489,7 @@ impl Resource> { } } -unsafe impl Sync for Resource -where - C: Ceiling, -{ -} +unsafe impl Sync for Resource {} /// A hardware peripheral as a resource pub struct Peripheral @@ -116,9 +500,10 @@ where _ceiling: PhantomData, } -impl Peripheral +impl Peripheral> where - C: Ceiling, + CEILING: GreaterThanOrEqual, + CEILING: LessThanOrEqual, { /// Assigns a ceiling `C` to the `peripheral` /// @@ -139,7 +524,7 @@ impl Peripheral> { pub fn borrow<'cs, PRIORITY, SCEILING>( &'static self, _priority: &P, - _system_ceiling: &'cs C, + _current_ceiling: &'cs C, ) -> Ref<'cs, Periph> where SCEILING: GreaterThanOrEqual, @@ -150,13 +535,19 @@ impl Peripheral> { /// See [Resource.lock](./struct.Resource.html#method.lock) #[cfg(not(thumbv6m))] - pub fn lock(&'static self, _priority: &P, f: F) -> R - where F: FnOnce(Ref, &C) -> R, - CEILING: Cmp + Cmp + Level + pub fn lock( + &'static self, + _priority: &P, + f: F, + ) -> R + where + F: FnOnce(Ref, &C) -> R, + CEILING: Cmp + Cmp, + CEILING: Unsigned, { unsafe { let old_basepri = basepri::read(); - basepri_max::write(::hw()); + basepri_max::write(logical2hw(CEILING::to_u8())); barrier!(); let ret = f( Ref::new(&*self.peripheral.get()), @@ -169,13 +560,9 @@ impl Peripheral> { } } -unsafe impl Sync for Peripheral -where - C: Ceiling, -{ -} +unsafe impl Sync for Peripheral {} -/// A global critical section +/// Runs closure `f` in a *global* critical section /// /// No task can preempt this critical section pub fn critical(f: F) -> R @@ -197,12 +584,11 @@ where } /// Requests the execution of a `task` -pub fn request(_task: fn(T, P)) +pub fn request(_task: fn(T, P)) where T: Context + Nr, - P: Priority, { - let nvic = unsafe { &*NVIC.get() }; + let nvic = unsafe { &*_NVIC.get() }; match () { #[cfg(debug_assertions)] @@ -219,10 +605,18 @@ where // NOTE(safe) zero sized type let task = unsafe { core::ptr::read(0x0 as *const T) }; + // NOTE(safe) atomic write nvic.set_pending(task); } +#[doc(hidden)] +pub fn _validate_priority(_: &P) +where + PRIORITY: Cmp + LessThanOrEqual, +{ +} + /// A type-level ceiling pub struct C { _marker: PhantomData, @@ -235,93 +629,88 @@ pub struct P { impl P where - T: Level, + T: Unsigned, { #[doc(hidden)] - pub fn hw() -> u8 { - T::hw() + pub fn _hw() -> u8 { + logical2hw(T::to_u8()) } } -/// A valid resource ceiling -/// -/// DO NOT IMPLEMENT THIS TRAIT YOURSELF -pub unsafe trait Ceiling {} - /// Type-level `>=` operator /// -/// DO NOT IMPLEMENT THIS TRAIT YOURSELF +/// Do not implement this trait yourself. This is an implementation detail. pub unsafe trait GreaterThanOrEqual {} -/// Interrupt hardware level +/// Type-level `<=` operator /// -/// DO NOT IMPLEMENT THIS TRAIT YOURSELF -pub unsafe trait Level { - /// Interrupt hardware level - fn hw() -> u8; -} +/// Do not implement this trait yourself. This is an implementation detail. +pub unsafe trait LessThanOrEqual {} -/// A valid task priority +/// Converts a logical priority into a shifted hardware priority, as used by the +/// NVIC and the BASEPRI register /// -/// DO NOT IMPLEMENT THIS TRAIT YOURSELF -pub unsafe trait Priority {} - - -/// Convert a logical priority to a shifted hardware prio -/// as used by the NVIC and basepri registers -/// Notice, wrapping causes a panic due to u8 +/// # Panics +/// +/// This function panics if `logical` is outside the closed range +/// `[1, 1 << PRIORITY_BITS]`. Where `PRIORITY_BITS` is the number of priority +/// bits used by the device specific NVIC implementation. pub fn logical2hw(logical: u8) -> u8 { + assert!(logical >= 1 && logical <= (1 << PRIORITY_BITS)); + ((1 << PRIORITY_BITS) - logical) << (8 - PRIORITY_BITS) } -/// Convert a shifted hardware prio to a logical priority -/// as used by the NVIC and basepri registers -/// Notice, wrapping causes a panic due to u8 +/// Converts a shifted hardware priority into a logical priority pub fn hw2logical(hw: u8) -> u8 { (1 << PRIORITY_BITS) - (hw >> (8 - PRIORITY_BITS)) } - -/// Priority 0, the lowest priority -pub type P0 = P<::typenum::U0>; - -/// Declares tasks +/// A macro to declare tasks +/// +/// Each `$task` is bound to an `$Interrupt` handler and has a priority `$P`. +/// +/// The `$Interrupt` handlers are defined in the `$device` crate. +/// +/// **NOTE** This macro will expand to a `main` function. +/// +/// Apart from defining the listed `$tasks`, the `init` and `idle` functions +/// must be defined as well. `init` has type signature `fn(P0, &C16)`, and +/// `idle` has signature `fn(P0) -> !`. #[macro_export] macro_rules! tasks { - ($krate:ident, { + ($device:ident, { $($task:ident: ($Interrupt:ident, $P:ident),)* }) => { fn main() { $crate::critical(|cmax| { - fn signature(_: fn($crate::P0, &$crate::CMAX)) {} + fn validate_signature(_: fn($crate::P0, &$crate::CMAX)) {} - signature(init); - let p0 = unsafe { ::core::ptr::read(0x0 as *const _) }; + validate_signature(init); + let p0 = unsafe { ::core::mem::transmute::<_, P0>(()) }; init(p0, cmax); set_priorities(); enable_tasks(); }); - fn signature(_: fn($crate::P0) -> !) {} + fn validate_signature(_: fn($crate::P0) -> !) {} - signature(idle); - let p0 = unsafe { ::core::ptr::read(0x0 as *const _) }; + validate_signature(idle); + let p0 = unsafe { ::core::mem::transmute::<_, P0>(()) }; idle(p0); fn set_priorities() { // NOTE(safe) this function runs in an interrupt free context - let _nvic = unsafe { &*$crate::NVIC.get() }; + let _nvic = unsafe { &*$crate::_NVIC.get() }; $( { - let hw = $crate::$P::hw(); - if hw != 0 { - unsafe { - _nvic.set_priority - (::$krate::interrupt::Interrupt::$Interrupt, - hw, - ); - } + let hw = $crate::$P::_hw(); + unsafe { + _nvic.set_priority( + ::$device::interrupt::Interrupt::$Interrupt, + hw, + ); } } )* @@ -331,42 +720,34 @@ macro_rules! tasks { fn enable_tasks() { // NOTE(safe) this function runs in an interrupt free context - let _nvic = unsafe { &*$crate::NVIC.get() }; + let _nvic = unsafe { &*$crate::_NVIC.get() }; $( - _nvic.enable(::$krate::interrupt::Interrupt::$Interrupt); + _nvic.enable(::$device::interrupt::Interrupt::$Interrupt); )* } - #[allow(dead_code)] - fn is_priority

() - where - P: $crate::Priority, - { - } - #[allow(dead_code)] #[link_section = ".rodata.interrupts"] #[used] - static INTERRUPTS: ::$krate::interrupt::Handlers = - ::$krate::interrupt::Handlers { + static INTERRUPTS: ::$device::interrupt::Handlers = + ::$device::interrupt::Handlers { $( $Interrupt: { extern "C" fn $task( - task: ::$krate::interrupt::$Interrupt + task: ::$device::interrupt::$Interrupt ) { - is_priority::<$crate::$P>(); - ::$task( - task, unsafe { - ::core::ptr::read(0x0 as *const $crate::$P) - } - ) + let p = unsafe { + ::core::mem::transmute::<_, $crate::$P>(()) + }; + $crate::_validate_priority(&p); + ::$task(task, p) } $task }, )* - ..::$krate::interrupt::DEFAULT_HANDLERS + ..::$device::interrupt::DEFAULT_HANDLERS }; } } diff --git a/tests/cfail/borrow.rs b/tests/cfail/borrow.rs index 6d8ab2aa4d..ca246e7f79 100644 --- a/tests/cfail/borrow.rs +++ b/tests/cfail/borrow.rs @@ -1,6 +1,6 @@ -extern crate cortex_m_srp; +extern crate cortex_m_rtfm as rtfm; -use cortex_m_srp::{C1, C2, C3, C4, C5, P2, Resource}; +use rtfm::{C1, C2, C3, C4, C5, P2, Resource}; static R1: Resource = Resource::new(0); static R2: Resource = Resource::new(0); diff --git a/tests/cfail/ceiling.rs b/tests/cfail/ceiling.rs index 8ca8413915..a10aee9d78 100644 --- a/tests/cfail/ceiling.rs +++ b/tests/cfail/ceiling.rs @@ -1,4 +1,4 @@ -extern crate cortex_m_srp as rtfm; +extern crate cortex_m_rtfm as rtfm; use rtfm::{C3, P0, P2, Resource}; diff --git a/tests/cfail/lock.rs b/tests/cfail/lock.rs index 83f3ec8703..c69c0df5fe 100644 --- a/tests/cfail/lock.rs +++ b/tests/cfail/lock.rs @@ -1,6 +1,6 @@ -extern crate cortex_m_srp; +extern crate cortex_m_rtfm as rtfm; -use cortex_m_srp::{C16, C2, P1, P16, P2, P3, Resource}; +use rtfm::{C16, C2, P1, P16, P2, P3, Resource}; static R1: Resource = Resource::new(0); diff --git a/tests/cfail/race-1.rs b/tests/cfail/race-1.rs index cc31ef7612..35a476dfc0 100644 --- a/tests/cfail/race-1.rs +++ b/tests/cfail/race-1.rs @@ -1,18 +1,18 @@ -extern crate cortex_m_srp as srp; +extern crate cortex_m_rtfm as rtfm; -use srp::{C2, C4, P1, P3, Resource}; +use rtfm::{C2, C4, P1, P3, Resource}; static R1: Resource = Resource::new(0); fn j1(prio: P1) { R1.lock(&prio, |r1, _| { // Would preempt this critical section - // srp::request(j2); + // rtfm::request(j2); }); } fn j2(prio: P3) { - srp::critical(|ceil| { + rtfm::critical(|ceil| { let r1 = R1.borrow(&prio, &ceil); //~^ error }); diff --git a/tests/cfail/race-2.rs b/tests/cfail/race-2.rs index c2f564eaf4..23d7dd4a3d 100644 --- a/tests/cfail/race-2.rs +++ b/tests/cfail/race-2.rs @@ -1,6 +1,6 @@ -extern crate cortex_m_srp as srp; +extern crate cortex_m_rtfm as rtfm; -use srp::{C2, C4, P1, P3, Resource}; +use rtfm::{C2, C4, P1, P3, Resource}; static R1: Resource = Resource::new(0); static R2: Resource = Resource::new(0); @@ -8,7 +8,7 @@ static R2: Resource = Resource::new(0); fn j1(prio: P1) { R1.lock(&prio, |r1, _| { // Would preempt this critical section - // srp::request(j2); + // rtfm::request(j2); }); } diff --git a/tests/cfail/tasks-p0.rs b/tests/cfail/tasks-p0.rs index 3813963c3f..f1e83a378d 100644 --- a/tests/cfail/tasks-p0.rs +++ b/tests/cfail/tasks-p0.rs @@ -1,13 +1,13 @@ -// error-pattern: no associated item named `hw` +// error-pattern: type mismatch #![feature(used)] extern crate core; extern crate cortex_m; #[macro_use] -extern crate cortex_m_srp; +extern crate cortex_m_rtfm as rtfm; -use cortex_m_srp::{C16, P0, P1}; +use rtfm::{C16, P0, P1}; use device::interrupt::Exti0; /// Tasks can't have priority 0. Only idle has priority 0 @@ -15,9 +15,11 @@ tasks!(device, { j1: (Exti0, P0), }); -fn init(_: C16) {} +fn init(_: P0, _: &C16) {} -fn idle(_: P0) {} +fn idle(_: P0) -> ! { + loop {} +} fn j1(_task: Exti0, _prio: P1) {} diff --git a/tests/cfail/tasks-same-handler.rs b/tests/cfail/tasks-same-handler.rs index 345e65cd3b..662a67db4a 100644 --- a/tests/cfail/tasks-same-handler.rs +++ b/tests/cfail/tasks-same-handler.rs @@ -5,9 +5,9 @@ extern crate core; extern crate cortex_m; #[macro_use] -extern crate cortex_m_srp; +extern crate cortex_m_rtfm as rtfm; -use cortex_m_srp::{C16, P0, P1, P2}; +use rtfm::{C16, P0, P1, P2}; use device::interrupt::Exti0; // WRONG: Two tasks mapped to the same interrupt handler @@ -16,9 +16,11 @@ tasks!(device, { j2: (Exti0, P2), }); -fn init(_: C16) {} +fn init(_: P0, _: &C16) {} -fn idle(_: P0) {} +fn idle(_: P0) -> ! { + loop {} +} fn j1(_task: Exti0, _prio: P1) {} diff --git a/tests/cfail/tasks-wrong-idle.rs b/tests/cfail/tasks-wrong-idle.rs index 8d6ef4beff..77afef6b3f 100644 --- a/tests/cfail/tasks-wrong-idle.rs +++ b/tests/cfail/tasks-wrong-idle.rs @@ -5,20 +5,22 @@ extern crate core; extern crate cortex_m; #[macro_use] -extern crate cortex_m_srp; +extern crate cortex_m_rtfm as rtfm; -use cortex_m_srp::{C16, P0, P1}; use device::interrupt::Exti0; +use rtfm::{C16, P0, P1}; /// Tasks can't have priority 0. Only idle has priority 0 tasks!(device, { j1: (Exti0, P1), }); -fn init(_: C16) {} +fn init(_: P0, _: &C16) {} -// WRONG. `idle` must have signature `fn(P1)` -fn idle(_: P1) {} +// WRONG. `idle` must have signature `fn(P0) -> !` +fn idle(_: P1) -> ! { + loop {} +} fn j1(_task: Exti0, _prio: P1) {} diff --git a/tests/cfail/tasks-wrong-init.rs b/tests/cfail/tasks-wrong-init.rs index 368772fd17..8b8359c40a 100644 --- a/tests/cfail/tasks-wrong-init.rs +++ b/tests/cfail/tasks-wrong-init.rs @@ -5,19 +5,21 @@ extern crate core; extern crate cortex_m; #[macro_use] -extern crate cortex_m_srp; +extern crate cortex_m_rtfm as rtfm; -use cortex_m_srp::{C1, P0, P1}; +use rtfm::{C1, P0, P1}; use device::interrupt::Exti0; tasks!(device, { j1: (Exti0, P1), }); -// WRONG. `init` must have signature `fn(P0, C16)` -fn init(_: P0, _: C1) {} +// WRONG. `init` must have signature `fn(P0, &C16)` +fn init(_: P0, _: &C1) {} -fn idle(_: P0) {} +fn idle(_: P0) -> ! { + loop {} +} fn j1(_task: Exti0, _prio: P1) {} diff --git a/tests/cfail/tasks-wrong-priority.rs b/tests/cfail/tasks-wrong-priority.rs index 741a96ad01..08deef32e9 100644 --- a/tests/cfail/tasks-wrong-priority.rs +++ b/tests/cfail/tasks-wrong-priority.rs @@ -5,7 +5,7 @@ extern crate core; extern crate cortex_m; #[macro_use] -extern crate cortex_m_srp; +extern crate cortex_m_rtfm as rtfm; use cortex_m_srp::{C16, P0, P1, P2}; use device::interrupt::Exti1; @@ -14,9 +14,11 @@ tasks!(device, { j1: (Exti0, P1), }); -fn init(_: C16) {} +fn init(_: P0, _: &C16) {} -fn idle(_: P0) {} +fn idle(_: P0) -> ! { + loop {} +} // Wrong priority token. Declared P1, got P2 fn j1(_task: Exti1, _prio: P1) {} diff --git a/tests/cfail/tasks-wrong-task.rs b/tests/cfail/tasks-wrong-task.rs index ee69a8b876..15bd7764cd 100644 --- a/tests/cfail/tasks-wrong-task.rs +++ b/tests/cfail/tasks-wrong-task.rs @@ -5,18 +5,20 @@ extern crate core; extern crate cortex_m; #[macro_use] -extern crate cortex_m_srp; +extern crate cortex_m_rtfm as rtfm; -use cortex_m_srp::{C16, P0, P1}; +use cortex_mrtfm::{C16, P0, P1}; use device::interrupt::Exti1; tasks!(device, { j1: (Exti0, P1), }); -fn init(_: C16) {} +fn init(_: P0, _: &C16) {} -fn idle(_: P0) {} +fn idle(_: P0) -> ! { + loop {} +} // Wrong task token. Declared Exti0, got Exti1 fn j1(_task: Exti1, _prio: P1) {}