From 3cebf49a2feb10b6dbf7e40e4671dbf7a3d8bedf Mon Sep 17 00:00:00 2001 From: Jorge Aparicio Date: Thu, 6 Jul 2017 23:25:29 -0500 Subject: [PATCH] syntax tweaks, relax check, add set_pending(), deal with imported types - allow trailing commas in list of resources - make task.resources optional - add rtfm::set_pending function which can be used to force an interrupt into the pending state. This is a replacement of the old rtfm::request. rtfm::set_pending takes the Interrupt enum provided by the device crate as argument. (The old rtfm::request took a task function as argument) - the user may want to use types they imported into the root of the crate. These types are not available in e.g. `mod idle` so `idle::Resources` *can't* be defined in that module. To workaround this problem `idle::Resources` will be defined in the root, with some other name, and then be re-exported in the `idle` module. - remove the "a resource only used by one task should be local data" check. In some cases you do want a resource owned by a single task instead of local data since `init` can access resources but not a task local data. --- macros/src/check.rs | 21 +++--- macros/src/lib.rs | 1 + macros/src/syntax/mod.rs | 6 +- macros/src/syntax/parse.rs | 16 +++-- macros/src/trans.rs | 132 +++++++++++++++++++++++++++++-------- macros/src/util.rs | 24 +++++-- src/lib.rs | 11 ++++ 7 files changed, 157 insertions(+), 54 deletions(-) diff --git a/macros/src/check.rs b/macros/src/check.rs index ddd9abc429..7c86326ac9 100644 --- a/macros/src/check.rs +++ b/macros/src/check.rs @@ -1,17 +1,12 @@ -use syntax::Resources; -use util::{Ceiling, Ceilings}; +use syntax::Statics; +use util::Ceilings; -pub fn resources(resources: &Resources, ceilings: &Ceilings) { +pub fn resources(resources: &Statics, ceilings: &Ceilings) { for resource in resources.keys() { - if let Some(ceiling) = ceilings.get(&resource) { - assert_ne!( - *ceiling, - Ceiling::Owned, - "{} should be local data", - resource - ); - } else { - panic!("resource {} is unused", resource) - } + assert!( + ceilings.get(&resource).is_some(), + "resource {} is unused", + resource + ); } } diff --git a/macros/src/lib.rs b/macros/src/lib.rs index 05210fbb64..a5fdf96de9 100644 --- a/macros/src/lib.rs +++ b/macros/src/lib.rs @@ -1,3 +1,4 @@ +#![deny(warnings)] #![feature(plugin_registrar)] #![feature(proc_macro_internals)] #![feature(rustc_private)] diff --git a/macros/src/syntax/mod.rs b/macros/src/syntax/mod.rs index 53c17a82d4..757e05ed95 100644 --- a/macros/src/syntax/mod.rs +++ b/macros/src/syntax/mod.rs @@ -10,7 +10,7 @@ pub struct App { pub device: Tokens, pub idle: Idle, pub init: Init, - pub resources: Resources, + pub resources: Statics, pub tasks: Tasks, } @@ -21,7 +21,7 @@ pub struct Init { #[derive(Debug)] pub struct Idle { - pub local: Resources, + pub local: Statics, pub path: Tokens, pub resources: HashSet, } @@ -46,6 +46,6 @@ pub struct Resource { pub ty: Tokens, } -pub type Resources = HashMap; +pub type Statics = HashMap; pub type Tasks = HashMap; diff --git a/macros/src/syntax/parse.rs b/macros/src/syntax/parse.rs index 9cfbd78bdd..056e804a23 100644 --- a/macros/src/syntax/parse.rs +++ b/macros/src/syntax/parse.rs @@ -2,7 +2,7 @@ use std::collections::{HashMap, HashSet}; use syn::{self, DelimToken, Ident, IntTy, Lit, Token, TokenTree}; -use syntax::{App, Idle, Init, Kind, Resource, Resources, Task, Tasks}; +use syntax::{App, Idle, Init, Kind, Resource, Statics, Task, Tasks}; pub fn app(input: &str) -> App { let tts = syn::parse_token_trees(input).unwrap(); @@ -96,7 +96,7 @@ pub fn app(input: &str) -> App { block.delim ); - resources = Some(super::parse::resources(block.tts)); + resources = Some(super::parse::statics(block.tts)); } } "tasks" => { @@ -169,7 +169,7 @@ pub fn idle(tts: Vec) -> Idle { block.delim ); - local = Some(super::parse::resources(block.tts)); + local = Some(super::parse::statics(block.tts)); } else { panic!("expected block, found {:?}", tt); } @@ -273,7 +273,7 @@ pub fn init(tts: Vec) -> Init { fn idents(tts: Vec) -> HashSet { let mut idents = HashSet::new(); - let mut tts = tts.into_iter(); + let mut tts = tts.into_iter().peekable(); while let Some(tt) = tts.next() { if let TokenTree::Token(Token::Ident(id)) = tt { assert!(!idents.contains(&id), "ident {} already listed", id); @@ -281,6 +281,10 @@ fn idents(tts: Vec) -> HashSet { if let Some(tt) = tts.next() { assert_eq!(tt, TokenTree::Token(Token::Comma)); + + if tts.peek().is_none() { + break; + } } else { break; } @@ -292,7 +296,7 @@ fn idents(tts: Vec) -> HashSet { idents } -pub fn resources(tts: Vec) -> Resources { +pub fn statics(tts: Vec) -> Statics { let mut resources = HashMap::new(); let mut tts = tts.into_iter(); @@ -502,7 +506,7 @@ fn task(tts: Vec) -> Task { ); } - let resources = resources.expect("resources field is missing"); + let resources = resources.unwrap_or(HashSet::new()); let priority = priority.expect("priority field is missing"); let kind = if let Some(enabled) = enabled { Kind::Interrupt { enabled } diff --git a/macros/src/trans.rs b/macros/src/trans.rs index 14d24fd81c..b1cf7e796c 100644 --- a/macros/src/trans.rs +++ b/macros/src/trans.rs @@ -39,22 +39,24 @@ fn init(app: &App, main: &mut Vec, root: &mut Vec) { let ty = &resource.ty; fields.push(quote! { - pub #name: &'a mut #ty, + pub #name: &'a mut #krate::Static<#ty>, }); exprs.push(quote! { - #name: &mut *super::#name.get(), + #name: ::#krate::Static::ref_mut(&mut *super::#name.get()), }); } root.push(quote! { + #[allow(non_camel_case_types)] + #[allow(non_snake_case)] + pub struct _initResources<#lifetime> { + #(#fields)* + } + mod init { pub use ::#device::Peripherals; - - #[allow(non_snake_case)] - pub struct Resources<#lifetime> { - #(#fields)* - } + pub use ::_initResources as Resources; impl<#lifetime> Resources<#lifetime> { pub unsafe fn new() -> Self { @@ -184,36 +186,80 @@ fn idle( let device = &app.device; let mut lifetime = None; + let mut needs_reexport = false; + for name in &app.idle.resources { + if ceilings[name].is_owned() { + if app.resources.get(name).is_some() { + needs_reexport = true; + break + } + } + } + + let super_ = if needs_reexport { + None + } else { + Some(Ident::new("super")) + }; let mut rexprs = vec![]; let mut rfields = vec![]; for name in &app.idle.resources { if ceilings[name].is_owned() { lifetime = Some(quote!('a)); + if let Some(resource) = app.resources.get(name) { + let ty = &resource.ty; - rfields.push(quote! { - pub #name: &'a mut ::#device::#name, - }); + rfields.push(quote! { + pub #name: &'a mut ::#krate::Static<#ty>, + }); - rexprs.push(quote! { - #name: &mut *::#device::#name.get(), - }); + rexprs.push(quote! { + #name: ::#krate::Static::ref_mut( + &mut *#super_::#name.get(), + ), + }); + } else { + rfields.push(quote! { + pub #name: &'a mut ::#device::#name, + }); + + rexprs.push(quote! { + #name: &mut *::#device::#name.get(), + }); + } } else { rfields.push(quote! { - pub #name: super::_resource::#name, + pub #name: #super_::_resource::#name, }); rexprs.push(quote! { - #name: super::_resource::#name::new(), + #name: #super_::_resource::#name::new(), }); } } - mod_items.push(quote! { - #[allow(non_snake_case)] - pub struct Resources<#lifetime> { - #(#rfields)* - } + if needs_reexport { + root.push(quote! { + #[allow(non_camel_case_types)] + #[allow(non_snake_case)] + pub struct _idleResources<#lifetime> { + #(#rfields)* + } + }); + mod_items.push(quote! { + pub use ::_idleResources as Resources; + }); + } else { + mod_items.push(quote! { + #[allow(non_snake_case)] + pub struct Resources<#lifetime> { + #(#rfields)* + } + }); + } + + mod_items.push(quote! { impl<#lifetime> Resources<#lifetime> { pub unsafe fn new() -> Self { Resources { @@ -252,6 +298,7 @@ fn tasks(app: &App, ceilings: &Ceilings, root: &mut Vec) { let device = &app.device; let mut lifetime = None; + let mut needs_reexport = false; for name in &task.resources { match ceilings[name] { Ceiling::Shared(ceiling) if ceiling > task.priority => { @@ -268,6 +315,7 @@ fn tasks(app: &App, ceilings: &Ceilings, root: &mut Vec) { _ => { lifetime = Some(quote!('a)); if let Some(resource) = app.resources.get(name) { + needs_reexport = true; let ty = &resource.ty; fields.push(quote! { @@ -292,12 +340,27 @@ fn tasks(app: &App, ceilings: &Ceilings, root: &mut Vec) { } } - items.push(quote! { - #[allow(non_snake_case)] - pub struct Resources<#lifetime> { - #(#fields)* - } - }); + if needs_reexport { + let rname = Ident::new(format!("_{}Resources", name)); + root.push(quote! { + #[allow(non_camel_case_types)] + #[allow(non_snake_case)] + pub struct #rname<#lifetime> { + #(#fields)* + } + }); + + items.push(quote! { + pub use ::#rname as Resources; + }); + } else { + items.push(quote! { + #[allow(non_snake_case)] + pub struct Resources<#lifetime> { + #(#fields)* + } + }); + } items.push(quote! { impl<#lifetime> Resources<#lifetime> { @@ -339,7 +402,22 @@ fn resources(app: &App, ceilings: &Ceilings, root: &mut Vec) { let mut impl_items = vec![]; match *ceiling { - Ceiling::Owned => continue, + Ceiling::Owned(_) => { + if let Some(resource) = app.resources.get(name) { + // For owned resources we don't need claim() or borrow(), + // just get() + let expr = &resource.expr; + let ty = &resource.ty; + + root.push(quote! { + static #name: #krate::Resource<#ty> = + #krate::Resource::new(#expr); + }); + } else { + // Peripheral + continue + } + }, Ceiling::Shared(ceiling) => { if let Some(resource) = app.resources.get(name) { let expr = &resource.expr; diff --git a/macros/src/util.rs b/macros/src/util.rs index 45f1feeff0..4722ca7de6 100644 --- a/macros/src/util.rs +++ b/macros/src/util.rs @@ -1,3 +1,4 @@ +use std::cmp; use std::collections::HashMap; use syn::Ident; @@ -8,13 +9,18 @@ pub type Ceilings = HashMap; #[derive(Clone, Copy, Debug, PartialEq)] pub enum Ceiling { - Owned, + // Owned by one or more tasks that have the same priority + Owned(u8), + // Shared by tasks with different priorities Shared(u8), } impl Ceiling { pub fn is_owned(&self) -> bool { - *self == Ceiling::Owned + match *self { + Ceiling::Owned(_) => true, + _ => false, + } } } @@ -22,14 +28,22 @@ pub fn compute_ceilings(app: &App) -> Ceilings { let mut ceilings = HashMap::new(); for resource in &app.idle.resources { - ceilings.insert(resource.clone(), Ceiling::Owned); + ceilings.insert(resource.clone(), Ceiling::Owned(0)); } for task in app.tasks.values() { for resource in &task.resources { if let Some(ceiling) = ceilings.get_mut(resource) { match *ceiling { - Ceiling::Owned => *ceiling = Ceiling::Shared(task.priority), + Ceiling::Owned(current) => { + if current == task.priority { + *ceiling = Ceiling::Owned(current); + } else { + *ceiling = Ceiling::Shared( + cmp::max(current, task.priority), + ); + } + } Ceiling::Shared(old) => { if task.priority > old { *ceiling = Ceiling::Shared(task.priority); @@ -40,7 +54,7 @@ pub fn compute_ceilings(app: &App) -> Ceilings { continue; } - ceilings.insert(resource.clone(), Ceiling::Owned); + ceilings.insert(resource.clone(), Ceiling::Owned(task.priority)); } } diff --git a/src/lib.rs b/src/lib.rs index f5c8b992b0..5d49af17f2 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -12,6 +12,7 @@ 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; +use cortex_m::interrupt::Nr; #[cfg(not(armv6m))] use cortex_m::register::{basepri_max, basepri}; @@ -175,6 +176,16 @@ impl Threshold { impl !Send for Threshold {} +/// Sets an interrupt as pending +pub fn set_pending(interrupt: I) +where + I: Nr, +{ + // NOTE(safe) atomic write + let nvic = unsafe { &*cortex_m::peripheral::NVIC.get() }; + nvic.set_pending(interrupt); +} + #[macro_export] macro_rules! task { ($NAME:ident, $body:path) => {