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.
This commit is contained in:
Jorge Aparicio 2017-07-27 11:37:58 -05:00
parent dee2fcde71
commit 0b5afce771
3 changed files with 47 additions and 154 deletions

View file

@ -15,6 +15,7 @@ version = "0.2.0"
[dependencies] [dependencies]
cortex-m = "0.3.1" cortex-m = "0.3.1"
static-ref = "0.2.1" static-ref = "0.2.1"
rtfm-core = { git = "https://github.com/japaric/rtfm-core" }
[dependencies.cortex-m-rtfm-macros] [dependencies.cortex-m-rtfm-macros]
path = "macros" path = "macros"

View file

@ -238,7 +238,7 @@ fn init(app: &App, main: &mut Vec<Tokens>, root: &mut Vec<Tokens>) {
// Interrupt. These can be enabled / disabled through the NVIC // Interrupt. These can be enabled / disabled through the NVIC
if interrupts.is_empty() { if interrupts.is_empty() {
interrupts.push(quote! { 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<Tokens>, root: &mut Vec<Tokens>) {
// Exception // Exception
if exceptions.is_empty() { if exceptions.is_empty() {
exceptions.push(quote! { 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<Tokens>, root: &mut Vec<Tokens>) {
// type check // type check
let init: fn(#(#tys,)*) = #init; let init: fn(#(#tys,)*) = #init;
#krate::atomic(|_cs| unsafe { #krate::atomic(unsafe { &mut #krate::Threshold::new(0) }, |_t| unsafe {
init(#(#exprs,)*); init(#(#exprs,)*);
#(#exceptions)* #(#exceptions)*
@ -328,15 +328,19 @@ fn resources(app: &App, ownerships: &Ownerships, root: &mut Vec<Tokens>) {
fn borrow<'cs>( fn borrow<'cs>(
&'cs self, &'cs self,
_cs: &'cs #krate::CriticalSection, t: &'cs #krate::Threshold,
) -> &'cs #krate::Static<#ty> { ) -> &'cs #krate::Static<#ty> {
assert!(t.value() >= #ceiling);
unsafe { #krate::Static::ref_(&#_name) } unsafe { #krate::Static::ref_(&#_name) }
} }
fn borrow_mut<'cs>( fn borrow_mut<'cs>(
&'cs mut self, &'cs mut self,
_cs: &'cs #krate::CriticalSection, t: &'cs #krate::Threshold,
) -> &'cs mut #krate::Static<#ty> { ) -> &'cs mut #krate::Static<#ty> {
assert!(t.value() >= #ceiling);
unsafe { unsafe {
#krate::Static::ref_mut(&mut #_name) #krate::Static::ref_mut(&mut #_name)
} }
@ -390,8 +394,10 @@ fn resources(app: &App, ownerships: &Ownerships, root: &mut Vec<Tokens>) {
fn borrow<'cs>( fn borrow<'cs>(
&'cs self, &'cs self,
_cs: &'cs #krate::CriticalSection, t: &'cs #krate::Threshold,
) -> &'cs #krate::Static<#device::#name> { ) -> &'cs #krate::Static<#device::#name> {
assert!(t.value() >= #ceiling);
unsafe { unsafe {
#krate::Static::ref_(&*#device::#name.get()) #krate::Static::ref_(&*#device::#name.get())
} }
@ -399,8 +405,10 @@ fn resources(app: &App, ownerships: &Ownerships, root: &mut Vec<Tokens>) {
fn borrow_mut<'cs>( fn borrow_mut<'cs>(
&'cs mut self, &'cs mut self,
_cs: &'cs #krate::CriticalSection, t: &'cs #krate::Threshold,
) -> &'cs mut #krate::Static<#device::#name> { ) -> &'cs mut #krate::Static<#device::#name> {
assert!(t.value() >= #ceiling);
unsafe { unsafe {
#krate::Static::ref_mut( #krate::Static::ref_mut(
&mut *#device::#name.get(), &mut *#device::#name.get(),
@ -458,7 +466,7 @@ fn resources(app: &App, ownerships: &Ownerships, root: &mut Vec<Tokens>) {
impls.push(quote! { impls.push(quote! {
#[allow(unsafe_code)] #[allow(unsafe_code)]
impl #krate::Resource for _resource::#name { unsafe impl #krate::Resource for _resource::#name {
#(#impl_items)* #(#impl_items)*
} }
}); });
@ -594,7 +602,13 @@ fn tasks(app: &App, ownerships: &Ownerships, root: &mut Vec<Tokens>) {
let priority = task.priority; let priority = task.priority;
if needs_threshold { if needs_threshold {
tys.push(quote!(&mut #krate::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 { if has_resources {

View file

@ -50,7 +50,6 @@
//! //!
//! In increasing grade of complexity, see the [examples](./examples/index.html) //! In increasing grade of complexity, see the [examples](./examples/index.html)
//! module. //! module.
#![deny(missing_docs)] #![deny(missing_docs)]
#![deny(warnings)] #![deny(warnings)]
#![feature(asm)] #![feature(asm)]
@ -61,76 +60,32 @@
extern crate cortex_m; extern crate cortex_m;
extern crate cortex_m_rtfm_macros; extern crate cortex_m_rtfm_macros;
extern crate rtfm_core;
extern crate static_ref; 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::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 cortex_m_rtfm_macros::app;
pub use static_ref::Static; use cortex_m::interrupt::{self, Nr};
use cortex_m::interrupt::Nr;
#[cfg(not(armv6m))] #[cfg(not(armv6m))]
use cortex_m::register::{basepri, basepri_max}; use cortex_m::register::basepri;
pub mod examples; pub mod examples;
/// A resource, a means to share data between tasks /// Executes the closure `f` in an interrupt free context
pub trait Resource { pub fn atomic<R, F>(t: &mut Threshold, f: F) -> R
/// 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<Self::Data>;
/// Mutable variant of `borrow`
fn borrow_mut<'cs>(
&'cs mut self,
cs: &'cs CriticalSection,
) -> &'cs mut Static<Self::Data>;
/// 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<R, F>(&self, t: &mut Threshold, f: F) -> R
where where
F: FnOnce(&Static<Self::Data>, &mut Threshold) -> R; F: FnOnce(&mut Threshold) -> R,
/// Mutable variant of `claim`
fn claim_mut<R, F>(&mut self, t: &mut Threshold, f: F) -> R
where
F: FnOnce(&mut Static<Self::Data>, &mut Threshold) -> R;
}
impl<T> Resource for Static<T> {
type Data = T;
fn borrow<'cs>(&'cs self, _cs: &'cs CriticalSection) -> &'cs Static<T> {
self
}
fn borrow_mut<'cs>(
&'cs mut self,
_cs: &'cs CriticalSection,
) -> &'cs mut Static<T> {
self
}
fn claim<R, F>(&self, t: &mut Threshold, f: F) -> R
where
F: FnOnce(&Static<Self::Data>, &mut Threshold) -> R,
{ {
f(self, t) if t.value() == u8::MAX {
} f(t)
} else {
fn claim_mut<R, F>(&mut self, t: &mut Threshold, f: F) -> R interrupt::disable();
where let r = f(&mut unsafe { Threshold::max() });
F: FnOnce(&mut Static<Self::Data>, &mut Threshold) -> R, unsafe { interrupt::enable() };
{ r
f(self, t)
} }
} }
@ -147,20 +102,19 @@ where
F: FnOnce(T, &mut Threshold) -> R, F: FnOnce(T, &mut Threshold) -> R,
{ {
let max_priority = 1 << nvic_prio_bits; let max_priority = 1 << nvic_prio_bits;
if ceiling > t.value { if ceiling > t.value() {
match () { match () {
#[cfg(armv6m)] #[cfg(armv6m)]
() => { () => atomic(t, |t| f(data, t)),
atomic(|_| f(data, &mut Threshold::new(max_priority)))
}
#[cfg(not(armv6m))] #[cfg(not(armv6m))]
() => { () => {
if ceiling == max_priority { if ceiling == max_priority {
atomic(|_| f(data, &mut Threshold::new(max_priority))) atomic(t, |t| f(data, t))
} else { } else {
let old = basepri::read(); let old = basepri::read();
let hw = (max_priority - ceiling) << (8 - nvic_prio_bits); let hw = (max_priority - ceiling) << (8 - nvic_prio_bits);
basepri_max::write(hw); basepri::write(hw);
let ret = f(data, &mut Threshold::new(ceiling)); let ret = f(data, &mut Threshold::new(ceiling));
basepri::write(old); basepri::write(old);
ret 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 /// Sets an interrupt as pending
/// ///
/// If the interrupt priority is high enough the interrupt will be serviced /// If the interrupt priority is high enough the interrupt will be serviced
@ -205,63 +140,6 @@ where
nvic.set_pending(interrupt); 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)] #[allow(non_camel_case_types)]
#[doc(hidden)] #[doc(hidden)]
pub enum Exception { pub enum Exception {