mirror of
https://github.com/rtic-rs/rtic.git
synced 2024-12-01 16:04:33 +01:00
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:
parent
dee2fcde71
commit
0b5afce771
3 changed files with 47 additions and 154 deletions
|
@ -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"
|
||||||
|
|
|
@ -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 {
|
||||||
|
|
168
src/lib.rs
168
src/lib.rs
|
@ -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
|
where
|
||||||
type Data;
|
F: FnOnce(&mut Threshold) -> R,
|
||||||
|
{
|
||||||
/// Borrows the resource data for the duration of a *global* critical
|
if t.value() == u8::MAX {
|
||||||
/// section
|
f(t)
|
||||||
fn borrow<'cs>(
|
} else {
|
||||||
&'cs self,
|
interrupt::disable();
|
||||||
cs: &'cs CriticalSection,
|
let r = f(&mut unsafe { Threshold::max() });
|
||||||
) -> &'cs Static<Self::Data>;
|
unsafe { interrupt::enable() };
|
||||||
|
r
|
||||||
/// 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
|
|
||||||
F: FnOnce(&Static<Self::Data>, &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)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn claim_mut<R, F>(&mut self, t: &mut Threshold, f: F) -> R
|
|
||||||
where
|
|
||||||
F: FnOnce(&mut Static<Self::Data>, &mut Threshold) -> 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 {
|
||||||
|
|
Loading…
Reference in a new issue