diff --git a/Cargo.toml b/Cargo.toml index 112e624a8b..5e9d548338 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -12,6 +12,35 @@ name = "cortex-m-rtfm" repository = "https://github.com/japaric/cortex-m-rtfm" version = "0.3.2" +[[example]] +name = "async-after" +required-features = ["timer-queue"] + +[[example]] +name = "async" + +[[example]] +name = "empty" + +[[example]] +name = "interrupt" + +[[example]] +name = "periodic-payload" +required-features = ["timer-queue"] + +[[example]] +name = "periodic-preemption-payload" +required-features = ["timer-queue"] + +[[example]] +name = "periodic-preemption" +required-features = ["timer-queue"] + +[[example]] +name = "periodic" +required-features = ["timer-queue"] + [dependencies] cortex-m = "0.4.0" cortex-m-rtfm-macros = { path = "macros", version = "0.3.1" } @@ -32,6 +61,7 @@ version = "0.9.0" [features] cm7-r0p1 = ["cortex-m/cm7-r0p1"] +timer-queue = ["cortex-m-rtfm-macros/timer-queue"] [profile.release] lto = true diff --git a/macros/Cargo.toml b/macros/Cargo.toml index fd38f32966..4161ed3448 100644 --- a/macros/Cargo.toml +++ b/macros/Cargo.toml @@ -20,3 +20,6 @@ either = "1.5.0" [lib] proc-macro = true + +[features] +timer-queue = [] \ No newline at end of file diff --git a/macros/src/check.rs b/macros/src/check.rs index 424c3e8bc5..300eac82c6 100644 --- a/macros/src/check.rs +++ b/macros/src/check.rs @@ -1,7 +1,15 @@ use syntax::check::App; use syntax::Result; -pub fn app(_app: &App) -> Result<()> { - // TODO ??? +pub fn app(app: &App) -> Result<()> { + if !cfg!(feature = "timer-queue") { + if !app.init.async_after.is_empty() + || app.tasks.values().any(|task| !task.async_after.is_empty()) + { + return Err(format_err!( + "async_after is not supported. Enable the 'timer-queue' feature to use it" + )); + } + } Ok(()) } diff --git a/macros/src/trans.rs b/macros/src/trans.rs index 612d9e5876..ec36d10f8b 100644 --- a/macros/src/trans.rs +++ b/macros/src/trans.rs @@ -108,28 +108,51 @@ pub fn app(ctxt: &Context, app: &App) -> Tokens { // primitive type and there's no way to import that type into a module (we don't know its // full path). So instead we just assume that `#input` has been imported in the root; this // forces us to put anything that refers to `#input` in the root. - root.push(quote! { - pub struct #__context<#lifetime> { - pub async: #name::Async, - pub baseline: u32, - pub input: #input, - pub resources: #name::Resources<#lifetime>, - pub threshold: #hidden::#krate::Threshold<#name::Priority>, - } + if cfg!(feature = "timer-queue") { + root.push(quote! { + pub struct #__context<#lifetime> { + pub async: #name::Async, + pub baseline: u32, + pub input: #input, + pub resources: #name::Resources<#lifetime>, + pub threshold: #hidden::#krate::Threshold<#name::Priority>, + } - #[allow(unsafe_code)] - impl<#lifetime> #__context<#lifetime> { - pub unsafe fn new(bl: #hidden::#krate::Instant, payload: #input) -> Self { - #__context { - async: #name::Async::new(bl), - baseline: bl.into(), - input: payload, - resources: #name::Resources::new(), - threshold: #hidden::#krate::Threshold::new(), + #[allow(unsafe_code)] + impl<#lifetime> #__context<#lifetime> { + pub unsafe fn new(bl: #hidden::#krate::Instant, payload: #input) -> Self { + #__context { + async: #name::Async::new(bl), + baseline: bl.into(), + input: payload, + resources: #name::Resources::new(), + threshold: #hidden::#krate::Threshold::new(), + } } } - } - }); + }); + } else { + root.push(quote! { + pub struct #__context<#lifetime> { + pub async: #name::Async, + pub input: #input, + pub resources: #name::Resources<#lifetime>, + pub threshold: #hidden::#krate::Threshold<#name::Priority>, + } + + #[allow(unsafe_code)] + impl<#lifetime> #__context<#lifetime> { + pub unsafe fn new(payload: #input) -> Self { + #__context { + async: #name::Async::new(), + input: payload, + resources: #name::Resources::new(), + threshold: #hidden::#krate::Threshold::new(), + } + } + } + }); + } let res_fields = task.resources .iter() @@ -163,7 +186,13 @@ pub fn app(ctxt: &Context, app: &App) -> Tokens { let async_exprs = task.async .iter() - .map(|task| quote!(#task: ::__async::#task::new(_bl))) + .map(|task| { + if cfg!(feature = "timer-queue") { + quote!(#task: ::__async::#task::new(_bl)) + } else { + quote!(#task: ::__async::#task::new()) + } + }) .chain( task.async_after .iter() @@ -189,15 +218,6 @@ pub fn app(ctxt: &Context, app: &App) -> Tokens { #(#async_fields,)* } - #[allow(unsafe_code)] - impl Async { - pub unsafe fn new(_bl: #krate::Instant) -> Self { - Async { - #(#async_exprs,)* - } - } - } - #[allow(non_snake_case)] pub struct Resources<#lifetime> { #(#res_fields,)* @@ -213,18 +233,49 @@ pub fn app(ctxt: &Context, app: &App) -> Tokens { } }); + if cfg!(feature = "timer-queue") { + mod_.push(quote! { + #[allow(unsafe_code)] + impl Async { + pub unsafe fn new(_bl: #krate::Instant) -> Self { + Async { + #(#async_exprs,)* + } + } + } + + }); + } else { + mod_.push(quote! { + #[allow(unsafe_code)] + impl Async { + pub unsafe fn new() -> Self { + Async { + #(#async_exprs,)* + } + } + } + + }); + } + match task.interrupt_or_capacity { Either::Left(interrupt) => { let export_name = interrupt.as_ref(); let fn_name = Ident::from(format!("__{}", interrupt)); + let bl = if cfg!(feature = "timer-queue") { + Some(quote!(#hidden::#krate::Instant::now(),)) + } else { + None + }; root.push(quote! { #[allow(non_snake_case)] #[allow(unsafe_code)] #[export_name = #export_name] pub unsafe extern "C" fn #fn_name() { let _ = #device::Interrupt::#interrupt; // verify that the interrupt exists - #name::HANDLER(#name::Context::new(#hidden::#krate::Instant::now(), ())) + #name::HANDLER(#name::Context::new(#bl ())) } }); } @@ -293,44 +344,90 @@ pub fn app(ctxt: &Context, app: &App) -> Tokens { )); let qc = Ident::from(format!("U{}", ctxt.ceilings.dispatch_queues()[&priority])); - quote! { - #[allow(non_camel_case_types)] - pub struct #name { baseline: #krate::Instant } + if cfg!(feature = "timer-queue") { + quote! { + #[allow(non_camel_case_types)] + pub struct #name { baseline: #krate::Instant } - #[allow(unsafe_code)] - impl #name { - pub unsafe fn new(bl: #krate::Instant) -> Self { - #name { baseline: bl } + #[allow(unsafe_code)] + impl #name { + pub unsafe fn new(bl: #krate::Instant) -> Self { + #name { baseline: bl } + } + + // XXX or take `self`? + #[inline] + pub fn post

( + &self, + t: &mut #krate::Threshold

, + payload: #ty, + ) -> Result<(), #ty> + where + P: #krate::Unsigned + + #krate::Max<#krate::#sqc> + + #krate::Max<#krate::#qc>, + #krate::Maximum: #krate::Unsigned, + #krate::Maximum: #krate::Unsigned, + { + unsafe { + if let Some(slot) = + ::#name::SQ::new().claim_mut(t, |sq, _| sq.dequeue()) { + let tp = slot + .write(self.baseline, payload) + .tag(::#__priority::Task::#name); + + ::#__priority::Q::new().claim_mut(t, |q, _| { + q.split().0.enqueue_unchecked(tp); + }); + + Ok(()) + } else { + Err(payload) + } + } + } } + } + } else { + quote! { + #[allow(non_camel_case_types)] + pub struct #name {} - // XXX or take `self`? - #[inline] - pub fn post

( - &self, - t: &mut #krate::Threshold

, - payload: #ty, - ) -> Result<(), #ty> - where - P: #krate::Unsigned + - #krate::Max<#krate::#sqc> + - #krate::Max<#krate::#qc>, - #krate::Maximum: #krate::Unsigned, - #krate::Maximum: #krate::Unsigned, - { - unsafe { - if let Some(slot) = - ::#name::SQ::new().claim_mut(t, |sq, _| sq.dequeue()) { - let tp = slot - .write(self.baseline, payload) - .tag(::#__priority::Task::#name); + #[allow(unsafe_code)] + impl #name { + pub unsafe fn new() -> Self { + #name {} + } - ::#__priority::Q::new().claim_mut(t, |q, _| { - q.split().0.enqueue_unchecked(tp); - }); + // XXX or take `self`? + #[inline] + pub fn post

( + &self, + t: &mut #krate::Threshold

, + payload: #ty, + ) -> Result<(), #ty> + where + P: #krate::Unsigned + + #krate::Max<#krate::#sqc> + + #krate::Max<#krate::#qc>, + #krate::Maximum: #krate::Unsigned, + #krate::Maximum: #krate::Unsigned, + { + unsafe { + if let Some(slot) = + ::#name::SQ::new().claim_mut(t, |sq, _| sq.dequeue()) { + let tp = slot + .write(payload) + .tag(::#__priority::Task::#name); - Ok(()) - } else { - Err(payload) + ::#__priority::Q::new().claim_mut(t, |q, _| { + q.split().0.enqueue_unchecked(tp); + }); + + Ok(()) + } else { + Err(payload) + } } } } @@ -537,14 +634,27 @@ pub fn app(ctxt: &Context, app: &App) -> Tokens { .tasks() .iter() .map(|name| { - quote! { + // NOTE(get) this is the only `Slot` producer because a task can only be + // dispatched at one priority + if cfg!(feature = "timer-queue") { + quote! { #__priority::Task::#name => { let (bl, payload, slot) = payload.coerce().read(); - // NOTE(get) only `Slot` producer because a task can only be dispatched at one // priority #name::SQ::get().split().0.enqueue_unchecked(slot); #name::HANDLER(#name::Context::new(bl, payload)); } + + } + } else { + quote! { + #__priority::Task::#name => { + let (payload, slot) = payload.coerce().read(); + // priority + #name::SQ::get().split().0.enqueue_unchecked(slot); + #name::HANDLER(#name::Context::new(payload)); + } + } } }) .collect::>(); @@ -640,7 +750,11 @@ pub fn app(ctxt: &Context, app: &App) -> Tokens { let async_exprs = app.init .async .iter() - .map(|task| quote!(#task: ::__async::#task::new(_bl))) + .map(|task| if cfg!(feature = "timer-queue") { + quote!(#task: ::__async::#task::new(_bl)) + } else { + quote!(#task: ::__async::#task::new()) + }) .chain( app.init .async_after @@ -661,6 +775,21 @@ pub fn app(ctxt: &Context, app: &App) -> Tokens { }) .collect::>(); + let bl = if cfg!(feature = "timer-queue") { + Some(quote!(let _bl = #krate::Instant::new(0);)) + } else { + None + }; + let baseline_field = if cfg!(feature = "timer-queue") { + Some(quote!(pub baseline: u32,)) + } else { + None + }; + let baseline_expr = if cfg!(feature = "timer-queue") { + Some(quote!(baseline: 0,)) + } else { + None + }; root.push(quote! { #[allow(non_snake_case)] pub struct _ZN4init13LateResourcesE { @@ -678,7 +807,7 @@ pub fn app(ctxt: &Context, app: &App) -> Tokens { pub struct Context { pub async: Async, - pub baseline: u32, + #baseline_field pub core: #krate::Core, pub device: Device, pub resources: Resources, @@ -690,7 +819,7 @@ pub fn app(ctxt: &Context, app: &App) -> Tokens { pub unsafe fn new(core: #krate::Core) -> Self { Context { async: Async::new(), - baseline: 0, + #baseline_expr core, device: Device::steal(), resources: Resources::new(), @@ -706,7 +835,7 @@ pub fn app(ctxt: &Context, app: &App) -> Tokens { #[allow(unsafe_code)] impl Async { unsafe fn new() -> Self { - let _bl = #krate::Instant::new(0); + #bl Async { #(#async_exprs,)* diff --git a/src/lib.rs b/src/lib.rs index d6fd0b4c00..e60c915d21 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -15,6 +15,7 @@ extern crate typenum; mod instant; mod node; mod resource; +#[cfg(feature = "timer-queue")] mod tq; use core::mem; @@ -38,6 +39,7 @@ pub use instant::Instant; pub use node::Node; use node::{Slot, TaggedPayload}; pub use resource::{Resource, Threshold}; +#[cfg(feature = "timer-queue")] pub use tq::{dispatch, TimerQueue}; pub type PayloadQueue = RingBuffer, N, u8>; diff --git a/src/node.rs b/src/node.rs index 9fe1bff283..42f336e76d 100644 --- a/src/node.rs +++ b/src/node.rs @@ -9,24 +9,29 @@ pub struct Node where T: 'static, { + #[cfg(feature = "timer-queue")] baseline: Instant, payload: T, } +#[cfg(feature = "timer-queue")] impl Eq for Node {} +#[cfg(feature = "timer-queue")] impl Ord for Node { fn cmp(&self, rhs: &Self) -> Ordering { self.baseline.cmp(&rhs.baseline) } } +#[cfg(feature = "timer-queue")] impl PartialEq for Node { fn eq(&self, rhs: &Self) -> bool { self.baseline.eq(&rhs.baseline) } } +#[cfg(feature = "timer-queue")] impl PartialOrd for Node { fn partial_cmp(&self, rhs: &Self) -> Option { Some(self.cmp(rhs)) @@ -42,11 +47,18 @@ where } impl Slot { + #[cfg(feature = "timer-queue")] pub fn write(self, bl: Instant, data: T) -> Payload { self.node.baseline = bl; unsafe { ptr::write(&mut self.node.payload, data) } Payload { node: self.node } } + + #[cfg(not(feature = "timer-queue"))] + pub fn write(self, data: T) -> Payload { + unsafe { ptr::write(&mut self.node.payload, data) } + Payload { node: self.node } + } } impl Into> for &'static mut Node { @@ -64,11 +76,18 @@ where } impl Payload { + #[cfg(feature = "timer-queue")] pub fn read(self) -> (Instant, T, Slot) { let data = unsafe { ptr::read(&self.node.payload) }; (self.node.baseline, data, Slot { node: self.node }) } + #[cfg(not(feature = "timer-queue"))] + pub fn read(self) -> (T, Slot) { + let data = unsafe { ptr::read(&self.node.payload) }; + (data, Slot { node: self.node }) + } + pub fn tag(self, tag: A) -> TaggedPayload where A: Copy, @@ -97,6 +116,7 @@ where mem::transmute(self.payload) } + #[cfg(feature = "timer-queue")] pub fn baseline(&self) -> Instant { self.payload.node.baseline } @@ -116,12 +136,14 @@ where } } +#[cfg(feature = "timer-queue")] impl Eq for TaggedPayload where T: Copy, { } +#[cfg(feature = "timer-queue")] impl Ord for TaggedPayload where T: Copy, @@ -131,6 +153,7 @@ where } } +#[cfg(feature = "timer-queue")] impl PartialEq for TaggedPayload where T: Copy, @@ -140,6 +163,7 @@ where } } +#[cfg(feature = "timer-queue")] impl PartialOrd for TaggedPayload where T: Copy,