diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/lib.rs | 1072 |
1 files changed, 170 insertions, 902 deletions
@@ -1,422 +1,3 @@ -//! Real Time For the Masses (RTFM), a framework for building concurrent -//! applications, for ARM Cortex-M microcontrollers -//! -//! This crate is based on [the RTFM framework] created by the Embedded Systems -//! group at [Luleå University of Technology][ltu], led by Prof. Per Lindgren, -//! and uses a simplified version of the Stack Resource Policy as scheduling -//! policy (check the [references] for details). -//! -//! [the RTFM framework]: http://www.rtfm-lang.org/ -//! [ltu]: https://www.ltu.se/?l=en -//! [per]: https://www.ltu.se/staff/p/pln-1.11258?l=en -//! [references]: ./index.html#references -//! -//! # Features -//! -//! - **Event triggered tasks** as the unit of concurrency. -//! - Support for prioritization of tasks and, thus, **preemptive -//! multitasking**. -//! - **Efficient and data race free memory sharing** through fine grained *non -//! global* critical sections. -//! - **Deadlock free execution** guaranteed at compile time. -//! - **Minimal scheduling overhead** as the scheduler has no "software -//! component": the hardware does all the scheduling. -//! - **Highly efficient memory usage**: All the tasks share a single call stack -//! and there's no hard dependency on a dynamic memory allocator. -//! - **All Cortex 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 `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 haven't yet -//! developed Rust friendly tooling for that.) -//! -//! # Requirements -//! -//! - Tasks must run to completion. That's it, tasks can't contain endless -//! loops. -//! - Task priorities must remain constant at runtime. -//! -//! # Dependencies -//! -//! - A device crate generated using [`svd2rust`] v0.7.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.7.0/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 -//! #![feature(used)] -//! #![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 stm32f30x; -//! -//! use rtfm::{P0, T0, TMax}; -//! -//! // TASKS (None in this example) -//! tasks!(stm32f30x, {}); -//! -//! // INITIALIZATION PHASE -//! fn init(_priority: P0, _threshold: &TMax) { -//! hprintln!("INIT"); -//! } -//! -//! // IDLE LOOP -//! fn idle(_priority: P0, _threshold: T0) -> ! { -//! hprintln!("IDLE"); -//! -//! // Sleep -//! loop { -//! rtfm::wfi(); -//! } -//! } -//! ``` -//! -//! Expected output: -//! -//! ``` text -//! INIT -//! IDLE -//! ``` -//! -//! The `tasks!` macro overrides the `main` function and imposes the following -//! structure into your program: -//! -//! - `init`, the initialization phase, runs first. This function is executed -//! "atomically", in the sense that no task / interrupt can preempt it. -//! -//! - `idle`, a never ending function that runs after `init`. -//! -//! Both `init` and `idle` have a priority of 0, the lowest priority. In RTFM, -//! a higher priority value means more urgent. -//! -//! # One task -//! -//! ``` ignore -//! #![feature(const_fn)] -//! #![feature(used)] -//! #![no_std] -//! -//! extern crate cortex_m_rt; -//! #[macro_use] -//! extern crate cortex_m_rtfm as rtfm; -//! extern crate stm32f30x; -//! -//! use stm32f30x::interrupt::Tim7; -//! use rtfm::{Local, P0, P1, T0, T1, TMax}; -//! -//! // INITIALIZATION PHASE -//! fn init(_priority: P0, _threshold: &TMax) { -//! // Configure TIM7 for periodic interrupts -//! // Configure GPIO for LED driving -//! } -//! -//! // IDLE LOOP -//! fn idle(_priority: P0, _threshold: T0) -> ! { -//! // Sleep -//! loop { -//! rtfm::wfi(); -//! } -//! } -//! -//! // TASKS -//! tasks!(stm32f30x, { -//! periodic: Task { -//! interrupt: Tim7, -//! priority: P1, -//! enabled: true, -//! }, -//! }); -//! -//! fn periodic(mut task: Tim7, _priority: P1, _threshold: T1) { -//! // Task local data -//! static STATE: Local<bool, Tim7> = Local::new(false); -//! -//! let state = STATE.borrow_mut(&mut task); -//! -//! // Toggle state -//! *state = !*state; -//! -//! // Blink an LED -//! if *state { -//! LED.on(); -//! } else { -//! LED.off(); -//! } -//! } -//! ``` -//! -//! Here we define a task named `periodic` and bind it to the `Tim7` -//! interrupt. The `periodic` task will run every time the `Tim7` interrupt -//! is triggered. 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 -//! `Tim7` type parameter). -//! -//! # Two "serial" tasks -//! -//! ``` ignore -//! #![feature(const_fn)] -//! #![feature(used)] -//! #![no_std] -//! -//! extern crate cortex_m_rt; -//! #[macro_use] -//! extern crate cortex_m_rtfm as rtfm; -//! extern crate stm32f30x; -//! -//! use core::cell::Cell; -//! -//! use stm32f30x::interrupt::{Tim6Dacunder, Tim7}; -//! use rtfm::{C1, P0, P1, Resource, T0, T1, TMax}; -//! -//! tasks!(stm32f30x, { -//! t1: Task { -//! interrupt: Tim6Dacunder, -//! priority: P1, -//! enabled: true, -//! }, -//! t2: Task { -//! interrupt: Tim7, -//! priority: P1, -//! enabled: true, -//! }, -//! }); -//! -//! // Data shared between tasks `t1` and `t2` -//! static COUNTER: Resource<Cell<u32>, C1> = Resource::new(Cell::new(0)); -//! -//! fn init(priority: P0, threshold: &TMax) { -//! // .. -//! } -//! -//! fn idle(priority: P0, threshold: T0) -> ! { -//! // Sleep -//! loop { -//! rtfm::wfi(); -//! } -//! } -//! -//! fn t1(_task: Tim6Dacunder, priority: P1, threshold: T1) { -//! let counter = COUNTER.access(&priority, &threshold); -//! -//! counter.set(counter.get() + 1); -//! } -//! -//! fn t2(_task: Tim7, priority: P1, threshold: T1) { -//! let counter = COUNTER.access(&priority, &threshold); -//! -//! 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. -//! no preemption between them is possible. -//! -//! 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 -//! [`access`](./struct.Resource.html#method.access) method -- no -//! synchronization is required. -//! -//! `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 -//! #![feature(const_fn)] -//! #![feature(used)] -//! #![no_std] -//! -//! extern crate cortex_m_rt; -//! #[macro_use] -//! extern crate cortex_m_rtfm as rtfm; -//! extern crate stm32f30x; -//! -//! use core::cell::Cell; -//! -//! use stm32f30x::interrupt::{Tim6Dacunder, Tim7}; -//! use rtfm::{C2, P0, P1, P2, Resource, T0, T1, T2, TMax}; -//! -//! tasks!(stm32f30x, { -//! t1: Task { -//! interrupt: Tim6Dacunder, -//! priority: P1, -//! enabled: true, -//! }, -//! t2: Task { -//! interrupt: Tim7, -//! priority: P2, -//! enabled: true, -//! }, -//! }); -//! -//! static COUNTER: Resource<Cell<u32>, C2> = Resource::new(Cell::new(0)); -//! -//! fn init(priority: P0, threshold: &TMax) { -//! // .. -//! } -//! -//! fn idle(priority: P0, threshold: T0) -> ! { -//! // Sleep -//! loop { -//! rtfm::wfi(); -//! } -//! } -//! -//! fn t1(_task: Tim6Dacunder, priority: P1, threshold: T1) { -//! // .. -//! -//! threshold.raise( -//! &COUNTER, |threshold: &T2| { -//! let counter = COUNTER.access(&priority, threshold); -//! -//! counter.set(counter.get() + 1); -//! } -//! ); -//! -//! // .. -//! } -//! -//! fn t2(_task: Tim7, priority: P2, threshold: T2) { -//! let counter = COUNTER.access(&priority, &threshold); -//! -//! 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 `Tim7` 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 by [`raise`](./struct.C.html#method.raise)-ing the preemption -//! `threshold`. This creates a critical section, denoted by a closure; for -//! whose execution, `COUNTER` is accessible while `t2` is prevented 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` had to be changed to `C2`. This is -//! 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 -//! block tasks with a priority of 2 or lower. This is exactly what the -//! preemption threshold represents: it's the "bar" that a task priority must -//! pass in order to be able to preempt the current task / critical section. -//! Note that a task with a priority of e.g. 3 (`P3`) effectively imposes a -//! threshold of 3 (`C3`) because only a task with a priority of 4 or greater -//! can preempt it. -//! -//! # Peripherals as resources -//! -//! ``` ignore -//! #![feature(const_fn)] -//! #![feature(used)] -//! #![no_std] -//! -//! extern crate cortex_m_rt; -//! #[macro_use] -//! extern crate cortex_m_rtfm as rtfm; -//! extern crate stm32f30x; -//! -//! use rtfm::{P0, Peripheral, T0, TMax}; -//! -//! peripherals!(stm32f30x, { -//! GPIOA: Peripheral { -//! register_block: Gpioa, -//! ceiling: C0, -//! }, -//! RCC: Peripheral { -//! register_block: Rcc, -//! ceiling: C0, -//! }, -//! }); -//! -//! tasks!(stm32f30x, {}); -//! -//! fn init(priority: P0, threshold: &TMax) { -//! let gpioa = GPIOA.access(&priority, threshold); -//! let rcc = RCC.access(&priority, threshold); -//! -//! // .. -//! } -//! -//! fn idle(_priority: P0, _threshold: T0) -> ! { -//! // 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` instances must be declared using the -//! [`peripherals!`](./macro.peripherals.html) macro. -//! -//! # 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 - -#![deny(missing_docs)] -#![deny(warnings)] #![feature(asm)] #![feature(const_fn)] #![feature(optin_builtin_traits)] @@ -424,550 +5,237 @@ extern crate cortex_m; extern crate static_ref; -extern crate typenum; use core::cell::UnsafeCell; -use core::marker::PhantomData; -use core::ptr; - -use cortex_m::ctxt::Context; -use cortex_m::interrupt::Nr; -#[cfg(not(thumbv6m))] -use cortex_m::register::{basepri, basepri_max}; -use static_ref::Ref; -use typenum::{Cmp, Greater, U0, Unsigned}; -#[cfg(not(thumbv6m))] -use typenum::Less; pub use cortex_m::asm::{bkpt, wfi}; +pub use cortex_m::interrupt::CriticalSection; +pub use cortex_m::interrupt::free as atomic; +pub use static_ref::Static; +#[cfg(not(armv6m))] +use cortex_m::register::{basepri_max, basepri}; -#[doc(hidden)] -pub use cortex_m::peripheral::NVIC as _NVIC; - -/// Compiler barrier -#[cfg(not(thumbv6m))] +#[cfg(not(armv6m))] macro_rules! barrier { () => { - asm!("" - : - : - : "memory" - : "volatile"); - } -} - -/// Task local data -/// -/// This data can only be accessed by the task `T` -pub struct Local<D, T> { - _task: PhantomData<T>, - data: UnsafeCell<D>, -} - -impl<T, TASK> Local<T, TASK> { - /// 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() } - } - - /// Mutably borrows the task local data for the duration of the task - pub fn borrow_mut<'task>( - &'static self, - _task: &'task mut TASK, - ) -> &'task mut T { - unsafe { &mut *self.data.get() } + asm!("" ::: "memory" : "volatile"); } } -unsafe impl<T, TASK> Sync for Local<T, TASK> {} - -/// A resource with ceiling `C` -/// -/// A resource is used to share memory between two or more tasks -pub struct Resource<T, C> { - _ceiling: PhantomData<C>, - data: UnsafeCell<T>, -} - -impl<T, RC> Resource<T, RC> +#[inline(always)] +unsafe fn claim<T, U, R, F, G>( + data: T, + ceiling: u8, + nvic_prio_bits: u8, + t: &mut Threshold, + f: F, + g: G, +) -> R where - RC: GreaterThanOrEqual<U0>, - RC: LessThanOrEqual<UMax>, + F: FnOnce(U, &mut Threshold) -> R, + G: FnOnce(T) -> U, { - /// Creates a new resource - pub const fn new(data: T) -> Self { - Resource { - _ceiling: PhantomData, - data: UnsafeCell::new(data), + let max_priority = 1 << nvic_prio_bits; + if ceiling > t.0 { + match () { + #[cfg(armv6m)] + () => { + atomic(|_| f(g(data), &mut Threshold::new(max_priority))) + } + #[cfg(not(armv6m))] + () => { + if ceiling == max_priority { + atomic(|_| f(g(data), &mut Threshold::new(max_priority))) + } else { + let old = basepri::read(); + let hw = (max_priority - ceiling) << (8 - nvic_prio_bits); + basepri_max::write(hw); + barrier!(); + let ret = f(g(data), &mut Threshold(ceiling)); + barrier!(); + basepri::write(old); + ret + } + } } + } else { + f(g(data), t) } } -impl<T, RC> Resource<T, RC> { - /// Grants data race free and deadlock free access to the resource data - /// - /// This operation is zero cost and doesn't impose any additional blocking. - /// - /// # Requirements - /// - /// To access the resource data these conditions must be met: - /// - /// - The resource ceiling must be greater than or equal to the task - /// priority - /// - The preemption threshold must be greater than or equal to the resource - /// ceiling - pub fn access<'cs, TP, PT>( - &'static self, - _task_priority: &Priority<TP>, - _preemption_threshold: &'cs Threshold<PT>, - ) -> Ref<'cs, T> - where - RC: GreaterThanOrEqual<TP>, - PT: GreaterThanOrEqual<RC>, - { - unsafe { Ref::new(&*self.data.get()) } - } -} - -unsafe impl<T, C> Sync for Resource<T, C> -where - T: Send, -{ -} - -/// A hardware peripheral as a resource -/// -/// To assign a ceiling to a peripheral, use the -/// [`peripherals!`](./macro.peripherals.html) macro -pub struct Peripheral<P, PC> +pub struct Peripheral<P> where P: 'static, { + // FIXME(rustc/LLVM bug?) storing the ceiling in the resource de-optimizes + // claims (the ceiling value gets loaded at runtime rather than inlined) + // ceiling: u8, peripheral: cortex_m::peripheral::Peripheral<P>, - _ceiling: PhantomData<PC>, } -impl<P, PC> Peripheral<P, PC> -where - PC: GreaterThanOrEqual<U0>, - PC: LessThanOrEqual<UMax>, -{ - #[doc(hidden)] - pub const unsafe fn _new(peripheral: cortex_m::peripheral::Peripheral<P>,) - -> Self { - Peripheral { - _ceiling: PhantomData, - peripheral: peripheral, - } +impl<P> Peripheral<P> { + pub const fn new(peripheral: cortex_m::peripheral::Peripheral<P>) -> Self { + Peripheral { peripheral } } -} -impl<Periph, PC> Peripheral<Periph, PC> { - /// See [Resource.access](./struct.Resource.html#method.access) - pub fn access<'cs, TP, PT>( + #[inline(always)] + pub unsafe fn claim<R, F>( &'static self, - _task_priority: &Priority<TP>, - _preemption_threshold: &'cs Threshold<PT>, - ) -> Ref<'cs, Periph> + ceiling: u8, + nvic_prio_bits: u8, + t: &mut Threshold, + f: F, + ) -> R where - PC: GreaterThanOrEqual<TP>, - PT: GreaterThanOrEqual<PC>, + F: FnOnce(&P, &mut Threshold) -> R, { - unsafe { Ref::new(&*self.peripheral.get()) } + claim( + &self.peripheral, + ceiling, + nvic_prio_bits, + t, + f, + |peripheral| &*peripheral.get(), + ) } -} - -unsafe impl<T, C> Sync for Peripheral<T, C> {} - -/// Runs the closure `f` "atomically" -/// -/// No task can preempt the execution of the closure -pub fn atomic<R, F>(f: F) -> R -where - F: FnOnce(&TMax) -> R, -{ - let primask = ::cortex_m::register::primask::read(); - ::cortex_m::interrupt::disable(); - - let r = f(&Threshold { _marker: PhantomData }); - // If the interrupts were active before our `disable` call, then re-enable - // them. Otherwise, keep them disabled - if primask.is_active() { - unsafe { ::cortex_m::interrupt::enable() } + pub fn get(&self) -> *mut P { + self.peripheral.get() } - - r -} - -/// Disables a `task` -/// -/// The task won't run even if the underlying interrupt is raised -pub fn disable<T, N>(_task: fn(T, Priority<N>, Threshold<N>)) -where - T: Context + Nr, -{ - // NOTE(safe) zero sized type - let _task = unsafe { ptr::read(0x0 as *const T) }; - - // NOTE(safe) atomic write - unsafe { (*_NVIC.get()).disable(_task) } } -/// Enables a `task` -pub fn enable<T, N>(_task: fn(T, Priority<N>, Threshold<N>)) +unsafe impl<P> Sync for Peripheral<P> where - T: Context + Nr, + P: Send, { - // NOTE(safe) zero sized type - let _task = unsafe { ptr::read(0x0 as *const T) }; - - // NOTE(safe) atomic write - unsafe { (*_NVIC.get()).enable(_task) } -} - -/// Converts a shifted hardware priority into a logical priority -pub fn hw2logical(hw: u8) -> u8 { - (1 << PRIORITY_BITS) - (hw >> (8 - PRIORITY_BITS)) } -/// Converts a logical priority into a shifted hardware priority, as used by the -/// NVIC and the BASEPRI register -/// -/// # 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) +pub struct Resource<T> { + // FIXME(rustc/LLVM bug?) storing the ceiling in the resource de-optimizes + // claims (the ceiling value gets loaded at runtime rather than inlined) + // ceiling: u8, + data: UnsafeCell<T>, } -/// Requests the execution of a `task` -pub fn request<T, N>(_task: fn(T, Priority<N>, Threshold<N>)) -where - T: Context + Nr, -{ - let nvic = unsafe { &*_NVIC.get() }; - - match () { - #[cfg(debug_assertions)] - () => { - // NOTE(safe) zero sized type - let task = unsafe { core::ptr::read(0x0 as *const T) }; - // NOTE(safe) atomic read - assert!(!nvic.is_pending(task), - "Task is already in the pending state"); - } - #[cfg(not(debug_assertions))] - () => {} +impl<T> Resource<T> { + pub const fn new(value: T) -> Self { + Resource { data: UnsafeCell::new(value) } } - // 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<TP>(_: &Priority<TP>) -where - TP: Cmp<U0, Output = Greater> + LessThanOrEqual<UMax>, -{ -} - -/// Preemption threshold -pub struct Threshold<T> { - _marker: PhantomData<T>, -} - -impl<PT> Threshold<PT> { - /// Raises the preemption threshold to match the `resource` ceiling - #[cfg(not(thumbv6m))] - pub fn raise<RC, RES, R, F>(&self, _resource: &'static RES, f: F) -> R + #[inline(always)] + pub unsafe fn claim<R, F>( + &'static self, + ceiling: u8, + nvic_prio_bits: u8, + t: &mut Threshold, + f: F, + ) -> R where - RES: ResourceLike<Ceiling = RC>, - RC: Cmp<PT, Output = Greater> + Cmp<UMax, Output = Less> + Unsigned, - F: FnOnce(&Threshold<RC>) -> R, + F: FnOnce(&Static<T>, &mut Threshold) -> R, { - unsafe { - let old_basepri = basepri::read(); - basepri_max::write(logical2hw(RC::to_u8())); - barrier!(); - let ret = f(&Threshold { _marker: PhantomData }); - barrier!(); - basepri::write(old_basepri); - ret - } + claim(&self.data, ceiling, nvic_prio_bits, t, f, |data| { + Static::ref_(&*data.get()) + }) } -} -impl<N> !Send for Threshold<N> {} + #[inline(always)] + pub unsafe fn claim_mut<R, F>( + &'static self, + ceiling: u8, + nvic_prio_bits: u8, + t: &mut Threshold, + f: F, + ) -> R + where + F: FnOnce(&mut Static<T>, &mut Threshold) -> R, + { + claim(&self.data, ceiling, nvic_prio_bits, t, f, |data| { + Static::ref_mut(&mut *data.get()) + }) + } -/// Priority -pub struct Priority<N> { - _marker: PhantomData<N>, + pub fn get(&self) -> *mut T { + self.data.get() + } } -impl<T> Priority<T> +unsafe impl<T> Sync for Resource<T> where - T: Unsigned, + T: Send, { - #[doc(hidden)] - pub fn _hw() -> u8 { - logical2hw(T::to_u8()) - } } -impl<N> !Send for Priority<N> {} +pub struct Threshold(u8); -/// Maps a `Resource` / `Peripheral` to its ceiling -/// -/// Do not implement this trait yourself. This is an implementation detail. -pub unsafe trait ResourceLike { - /// The ceiling of the resource - type Ceiling; -} - -unsafe impl<P, PC> ResourceLike for Peripheral<P, PC> { - type Ceiling = PC; -} - -unsafe impl<T, RC> ResourceLike for Resource<T, RC> { - type Ceiling = RC; +impl Threshold { + pub unsafe fn new(value: u8) -> Self { + Threshold(value) + } } -/// Type-level `>=` operator -/// -/// Do not implement this trait yourself. This is an implementation detail. -pub unsafe trait GreaterThanOrEqual<RHS> {} +impl !Send for Threshold {} -/// Type-level `<=` operator -/// -/// Do not implement this trait yourself. This is an implementation detail. -pub unsafe trait LessThanOrEqual<RHS> {} - -/// A macro to assign ceilings to peripherals -/// -/// **NOTE** A peripheral instance, like RCC, can only be bound to a *single* -/// ceiling. Trying to use this macro to bind the same peripheral to several -/// ceiling will result in a compiler error. -/// -/// # Example -/// -/// NOTE With device crates generated using svd2rust 0.8+ you can omit the -/// register_block field. -/// -/// ``` ignore -/// #[macro_use] -/// extern crate cortex_m_rtfm; -/// // device crate generated using `svd2rust` -/// extern crate stm32f30x; -/// -/// peripherals!(stm32f30x, { -/// GPIOA: Peripheral { -/// register_block: Gpioa, -/// ceiling: C1, -/// }, -/// RCC: Peripheral { -/// register_block: Rcc, -/// ceiling: C0, -/// }, -/// }); -/// ``` #[macro_export] -macro_rules! peripherals { - ($device:ident, { - $($PERIPHERAL:ident: Peripheral { - register_block: $RegisterBlock:ident, - ceiling: $C:ident, - },)+ - }) => { - $( - #[allow(private_no_mangle_statics)] - #[no_mangle] - static $PERIPHERAL: - $crate::Peripheral<::$device::$RegisterBlock, $crate::$C> = - unsafe { $crate::Peripheral::_new(::$device::$PERIPHERAL) }; - )+ +macro_rules! task { + ($NAME:ident, $body:path) => { + #[allow(non_snake_case)] + #[no_mangle] + pub unsafe extern "C" fn $NAME() { + let f: fn($crate::Threshold, ::$NAME::Resources) = $body; + + f( + $crate::Threshold::new(::$NAME::$NAME), + ::$NAME::Resources::new(), + ); + } }; - ($device:ident, { - $($PERIPHERAL:ident: Peripheral { - ceiling: $C:ident, - },)+ - }) => { - $( - #[allow(private_no_mangle_statics)] - #[no_mangle] - static $PERIPHERAL: - $crate::Peripheral<::$device::$PERIPHERAL, $crate::$C> = - unsafe { $crate::Peripheral::_new(::$device::$PERIPHERAL) }; - )+ - } -} - -/// A macro to declare tasks -/// -/// **NOTE** This macro will expand to a `main` function. -/// -/// Each `$task` is bound to an `$Interrupt` handler and has a priority `$P`. -/// The minimum priority of a task is `P1`. `$enabled` indicates whether the -/// task will be enabled before `idle` runs. -/// -/// The `$Interrupt` handlers are defined in the `$device` crate. -/// -/// Apart from defining the listed `$tasks`, the `init` and `idle` functions -/// must be defined as well. `init` has signature `fn(P0, &TMax)`, and `idle` -/// has signature `fn(P0) -> !`. -/// -/// # Example -/// -/// ``` ignore -/// #[feature(used)] -/// #[no_std] -/// -/// extern crate cortex_m_rt; -/// #[macro_use] -/// extern crate cortex_m_rtfm as rtfm; -/// // device crate generated using `svd2rust` -/// extern crate stm32f30x; -/// -/// use rtfm::{P0, P1, P2, T0, T1, T2, TMax}; -/// use stm32f30x::interrupt::{Exti0, Tim7}; -/// -/// tasks!(stm32f30x, { -/// periodic: Task { -/// interrupt: Tim7, -/// priority: P1, -/// enabled: true, -/// }, -/// button: Task { -/// interrupt: Exti0, -/// priority: P2, -/// enabled: true, -/// }, -/// }); -/// -/// fn init(priority: P0, threshold: &TMax) { -/// // .. -/// } -/// -/// fn idle(priority: P0, threshold: T0) -> ! { -/// // Sleep -/// loop { -/// rtfm::wfi(); -/// } -/// } -/// -/// // NOTE signature must match the tasks! declaration -/// fn periodic(task: Tim7, priority: P1, threshold: T1) { -/// // .. -/// } -/// -/// fn button(task: Exti0, priority: P2, threshold: T2) { -/// // .. -/// } -/// ``` -#[macro_export] -macro_rules! tasks { - ($device:ident, { - $($task:ident: Task { - interrupt:$Interrupt:ident, - priority: $P:ident, - enabled: $enabled:expr, - },)* + ($NAME:ident, $body:path, $local:ident { + $($var:ident: $ty:ty = $expr:expr;)+ }) => { - fn main() { - $crate::atomic(|t_max| { - fn validate_signature(_: fn($crate::P0, &$crate::TMax)) {} - - validate_signature(init); - let p0 = unsafe { ::core::mem::transmute::<_, P0>(()) }; - init(p0, t_max); - set_priorities(); - enable_tasks(); - }); - - fn validate_signature(_: fn($crate::P0, $crate::T0) -> !) {} - - validate_signature(idle); - let p0 = unsafe { ::core::mem::transmute::<_, P0>(()) }; - let t0 = unsafe { ::core::mem::transmute::<_, T0>(()) }; - idle(p0, t0); - - fn set_priorities() { - // NOTE(safe) this function runs in an interrupt free context - let _nvic = unsafe { &*$crate::_NVIC.get() }; - - $( - { - let hw = $crate::$P::_hw(); - unsafe { - _nvic.set_priority( - ::$device::interrupt::Interrupt::$Interrupt, - hw, - ); - } - } - )* - - // TODO freeze the NVIC.IPR register using the MPU, if available - } - - fn enable_tasks() { - // NOTE(safe) this function runs in an interrupt free context - let _nvic = unsafe { &*$crate::_NVIC.get() }; + struct $local { + $($var: $ty,)+ + } - $( - if $enabled { - $crate::enable(::$task); - } - )* - } + #[allow(non_snake_case)] + #[no_mangle] + pub unsafe extern "C" fn $NAME() { + let f: fn( + $crate::Threshold, + &mut $local, + ::$NAME::Resources, + ) = $body; + + static mut LOCAL: $local = $local { + $($var: $expr,)+ + }; + + f( + $crate::Threshold::new(::$NAME::$NAME), + &mut LOCAL, + ::$NAME::Resources::new(), + ); + } + }; +} - #[allow(dead_code)] - #[link_section = ".rodata.interrupts"] - #[used] - static INTERRUPTS: ::$device::interrupt::Handlers = - ::$device::interrupt::Handlers { - $( - $Interrupt: { - extern "C" fn $task( - task: ::$device::interrupt::$Interrupt - ) { - fn validate_signature<N>( - _: fn(::$device::interrupt::$Interrupt, - $crate::Priority<N>, - $crate::Threshold<N>)) {} - validate_signature(::$task); - let p = unsafe { - ::core::mem::transmute::<_, $crate::$P>(()) - }; - let t = unsafe { - ::core::mem::transmute(()) - }; - $crate::_validate_priority(&p); - ::$task(task, p, t) - } +#[allow(non_camel_case_types)] +#[doc(hidden)] +pub enum Exception { + /// System service call via SWI instruction + SVCALL, + /// Pendable request for system service + PENDSV, + /// System tick timer + SYS_TICK, +} - $task - }, - )* - ..::$device::interrupt::DEFAULT_HANDLERS - }; +impl Exception { + #[doc(hidden)] + pub fn nr(&self) -> usize { + match *self { + Exception::SVCALL => 11, + Exception::PENDSV => 14, + Exception::SYS_TICK => 15, } } } - -include!(concat!(env!("OUT_DIR"), "/prio.rs")); |