From 0b5afce771cb9e5cc42c4fd4c5e18f020bf1ecad Mon Sep 17 00:00:00 2001 From: Jorge Aparicio Date: Thu, 27 Jul 2017 11:37:58 -0500 Subject: [PATCH] refactor Resource / Threshold into its own crate, drop task!, tweak rtfm::atomic task! can be re-added in a backward compatible fashion and I'd like to not have two ways to assign a task handler to an interrupt / exception in the first release. rtfm::atomic now uses the `Threshold` token instead of the `CriticalSection` token. This reduces overhead by dropping the "are interrupts enabled?" check. --- Cargo.toml | 1 + macros/src/trans.rs | 32 ++++++--- src/lib.rs | 168 ++++++-------------------------------------- 3 files changed, 47 insertions(+), 154 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 392538b452..8ce7bdb23d 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -15,6 +15,7 @@ version = "0.2.0" [dependencies] cortex-m = "0.3.1" static-ref = "0.2.1" +rtfm-core = { git = "https://github.com/japaric/rtfm-core" } [dependencies.cortex-m-rtfm-macros] path = "macros" diff --git a/macros/src/trans.rs b/macros/src/trans.rs index a137e0ec7c..4f389d3028 100644 --- a/macros/src/trans.rs +++ b/macros/src/trans.rs @@ -238,7 +238,7 @@ fn init(app: &App, main: &mut Vec, root: &mut Vec) { // Interrupt. These can be enabled / disabled through the NVIC if interrupts.is_empty() { interrupts.push(quote! { - let nvic = #device::NVIC.borrow(_cs); + let nvic = &*#device::NVIC.get(); }); } @@ -262,7 +262,7 @@ fn init(app: &App, main: &mut Vec, root: &mut Vec) { // Exception if exceptions.is_empty() { exceptions.push(quote! { - let scb = #device::SCB.borrow(_cs); + let scb = &*#device::SCB.get(); }); } @@ -280,7 +280,7 @@ fn init(app: &App, main: &mut Vec, root: &mut Vec) { // type check let init: fn(#(#tys,)*) = #init; - #krate::atomic(|_cs| unsafe { + #krate::atomic(unsafe { &mut #krate::Threshold::new(0) }, |_t| unsafe { init(#(#exprs,)*); #(#exceptions)* @@ -328,15 +328,19 @@ fn resources(app: &App, ownerships: &Ownerships, root: &mut Vec) { fn borrow<'cs>( &'cs self, - _cs: &'cs #krate::CriticalSection, + t: &'cs #krate::Threshold, ) -> &'cs #krate::Static<#ty> { + assert!(t.value() >= #ceiling); + unsafe { #krate::Static::ref_(&#_name) } } fn borrow_mut<'cs>( &'cs mut self, - _cs: &'cs #krate::CriticalSection, + t: &'cs #krate::Threshold, ) -> &'cs mut #krate::Static<#ty> { + assert!(t.value() >= #ceiling); + unsafe { #krate::Static::ref_mut(&mut #_name) } @@ -390,8 +394,10 @@ fn resources(app: &App, ownerships: &Ownerships, root: &mut Vec) { fn borrow<'cs>( &'cs self, - _cs: &'cs #krate::CriticalSection, + t: &'cs #krate::Threshold, ) -> &'cs #krate::Static<#device::#name> { + assert!(t.value() >= #ceiling); + unsafe { #krate::Static::ref_(&*#device::#name.get()) } @@ -399,8 +405,10 @@ fn resources(app: &App, ownerships: &Ownerships, root: &mut Vec) { fn borrow_mut<'cs>( &'cs mut self, - _cs: &'cs #krate::CriticalSection, + t: &'cs #krate::Threshold, ) -> &'cs mut #krate::Static<#device::#name> { + assert!(t.value() >= #ceiling); + unsafe { #krate::Static::ref_mut( &mut *#device::#name.get(), @@ -458,7 +466,7 @@ fn resources(app: &App, ownerships: &Ownerships, root: &mut Vec) { impls.push(quote! { #[allow(unsafe_code)] - impl #krate::Resource for _resource::#name { + unsafe impl #krate::Resource for _resource::#name { #(#impl_items)* } }); @@ -594,7 +602,13 @@ fn tasks(app: &App, ownerships: &Ownerships, root: &mut Vec) { let priority = task.priority; if needs_threshold { tys.push(quote!(&mut #krate::Threshold)); - exprs.push(quote!(&mut #krate::Threshold::new(#priority))); + exprs.push(quote! { + &mut if #priority == 1 << #device::NVIC_PRIO_BITS { + #krate::Threshold::new(::core::u8::MAX) + } else { + #krate::Threshold::new(#priority) + } + }); } if has_resources { diff --git a/src/lib.rs b/src/lib.rs index 43079c1757..ba96762341 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -50,7 +50,6 @@ //! //! In increasing grade of complexity, see the [examples](./examples/index.html) //! module. - #![deny(missing_docs)] #![deny(warnings)] #![feature(asm)] @@ -61,76 +60,32 @@ extern crate cortex_m; extern crate cortex_m_rtfm_macros; +extern crate rtfm_core; extern crate static_ref; +use core::u8; + +pub use rtfm_core::{Resource, Static, Threshold}; pub use cortex_m::asm::{bkpt, wfi}; -pub use cortex_m::interrupt::CriticalSection; -pub use cortex_m::interrupt::free as atomic; pub use cortex_m_rtfm_macros::app; -pub use static_ref::Static; -use cortex_m::interrupt::Nr; +use cortex_m::interrupt::{self, Nr}; #[cfg(not(armv6m))] -use cortex_m::register::{basepri, basepri_max}; +use cortex_m::register::basepri; pub mod examples; -/// A resource, a means to share data between tasks -pub trait Resource { - /// The data protected by the resource - type Data; - - /// Borrows the resource data for the duration of a *global* critical - /// section - fn borrow<'cs>( - &'cs self, - cs: &'cs CriticalSection, - ) -> &'cs Static; - - /// Mutable variant of `borrow` - fn borrow_mut<'cs>( - &'cs mut self, - cs: &'cs CriticalSection, - ) -> &'cs mut Static; - - /// Claims the resource data for the span of the closure `f`. For the - /// duration of the closure other tasks that may access the resource data - /// are prevented from preempting the current task. - fn claim(&self, t: &mut Threshold, f: F) -> R - where - F: FnOnce(&Static, &mut Threshold) -> R; - - /// Mutable variant of `claim` - fn claim_mut(&mut self, t: &mut Threshold, f: F) -> R - where - F: FnOnce(&mut Static, &mut Threshold) -> R; -} - -impl Resource for Static { - type Data = T; - - fn borrow<'cs>(&'cs self, _cs: &'cs CriticalSection) -> &'cs Static { - self - } - - fn borrow_mut<'cs>( - &'cs mut self, - _cs: &'cs CriticalSection, - ) -> &'cs mut Static { - self - } - - fn claim(&self, t: &mut Threshold, f: F) -> R - where - F: FnOnce(&Static, &mut Threshold) -> R, - { - f(self, t) - } - - fn claim_mut(&mut self, t: &mut Threshold, f: F) -> R - where - F: FnOnce(&mut Static, &mut Threshold) -> R, - { - f(self, t) +/// Executes the closure `f` in an interrupt free context +pub fn atomic(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 } } @@ -147,20 +102,19 @@ where F: FnOnce(T, &mut Threshold) -> R, { let max_priority = 1 << nvic_prio_bits; - if ceiling > t.value { + if ceiling > t.value() { match () { #[cfg(armv6m)] - () => { - atomic(|_| f(data, &mut Threshold::new(max_priority))) - } + () => atomic(t, |t| f(data, t)), + #[cfg(not(armv6m))] () => { if ceiling == max_priority { - atomic(|_| f(data, &mut Threshold::new(max_priority))) + atomic(t, |t| f(data, t)) } else { let old = basepri::read(); let hw = (max_priority - ceiling) << (8 - nvic_prio_bits); - basepri_max::write(hw); + basepri::write(hw); let ret = f(data, &mut Threshold::new(ceiling)); basepri::write(old); ret @@ -172,25 +126,6 @@ where } } -/// Preemption threshold token -/// -/// The preemption threshold indicates the priority a task must have to preempt -/// the current context. For example a threshold of 2 indicates that only -/// interrupts / exceptions with a priority of 3 or greater can preempt the -/// current context -pub struct Threshold { - value: u8, -} - -impl Threshold { - #[doc(hidden)] - pub unsafe fn new(value: u8) -> Self { - Threshold { value } - } -} - -impl !Send for Threshold {} - /// Sets an interrupt as pending /// /// If the interrupt priority is high enough the interrupt will be serviced @@ -205,63 +140,6 @@ where nvic.set_pending(interrupt); } -/// Binds a task `$handler` to the interrupt / exception `$NAME` -/// -/// This macro takes two arguments: the name of an exception / interrupt and the -/// path to the function that will be used as the task handler. That function -/// must have signature `fn(&mut Threshold, $NAME::Resources)`. -/// -/// Optionally, a third argument may be used to declare task local data. -/// The handler will have exclusive access to these *local* variables on each -/// invocation. If the third argument is used then the signature of the handler -/// function must be `fn(&mut Threshold, &mut $locals, $NAME::Resources)`. -#[macro_export] -macro_rules! task { - ($NAME:ident, $handler:path) => { - #[allow(non_snake_case)] - #[allow(unsafe_code)] - #[no_mangle] - pub unsafe extern "C" fn $NAME() { - let f: fn(&mut $crate::Threshold, ::$NAME::Resources) = $handler; - - f( - &mut $crate::Threshold::new(::$NAME::$NAME), - ::$NAME::Resources::new(), - ); - } - }; - - ($NAME:ident, $handler:path, $locals:ident { - $(static $var:ident: $ty:ty = $expr:expr;)+ - }) => { - #[allow(non_snake_case)] - struct $locals { - $($var: $crate::Static<$ty>,)+ - } - - #[allow(non_snake_case)] - #[allow(unsafe_code)] - #[no_mangle] - pub unsafe extern "C" fn $NAME() { - let f: fn( - &mut $crate::Threshold, - &mut $locals, - ::$NAME::Resources, - ) = $handler; - - static mut LOCALS: $locals = $locals { - $($var: unsafe { $crate::Static::new($expr) },)+ - }; - - f( - &mut $crate::Threshold::new(::$NAME::$NAME), - &mut LOCALS, - ::$NAME::Resources::new(), - ); - } - }; -} - #[allow(non_camel_case_types)] #[doc(hidden)] pub enum Exception {