From 98596554b3d88a7619bdbc3ac7462a95b7263e96 Mon Sep 17 00:00:00 2001 From: Jorge Aparicio Date: Fri, 14 Jul 2017 18:54:54 -0500 Subject: [PATCH] split macro parser into its own crate and improve error handling / reporting --- macros/Cargo.toml | 5 + macros/src/analyze.rs | 70 +++++ macros/src/check.rs | 83 ++++- macros/src/error.rs | 1 + macros/src/lib.rs | 38 ++- macros/src/syntax/mod.rs | 51 ---- macros/src/syntax/parse.rs | 522 ------------------------------- macros/src/trans.rs | 612 ++++++++++++++++++------------------- macros/src/util.rs | 62 ---- src/lib.rs | 28 +- 10 files changed, 511 insertions(+), 961 deletions(-) create mode 100644 macros/src/analyze.rs create mode 100644 macros/src/error.rs delete mode 100644 macros/src/syntax/mod.rs delete mode 100644 macros/src/syntax/parse.rs delete mode 100644 macros/src/util.rs diff --git a/macros/Cargo.toml b/macros/Cargo.toml index 3aece07954..4237e21e8a 100644 --- a/macros/Cargo.toml +++ b/macros/Cargo.toml @@ -4,8 +4,13 @@ name = "cortex-m-rtfm-macros" version = "0.1.0" [dependencies] +error-chain = "0.10.0" quote = "0.3.15" syn = "0.11.11" +[dependencies.rtfm-syntax] +git = "https://github.com/japaric/rtfm-syntax" +optional = false + [lib] proc-macro = true diff --git a/macros/src/analyze.rs b/macros/src/analyze.rs new file mode 100644 index 0000000000..0fc125daba --- /dev/null +++ b/macros/src/analyze.rs @@ -0,0 +1,70 @@ +use std::cmp; +use std::collections::HashMap; + +use syn::Ident; + +use check::App; + +pub type Ownerships = HashMap; + +pub enum Ownership { + /// Owned or co-owned by tasks that run at the same priority + Owned { priority: u8 }, + /// Shared by tasks that run at different priorities. + /// + /// `ceiling` is the maximum value across all the task priorities + Shared { ceiling: u8 }, +} + +impl Ownership { + pub fn is_owned(&self) -> bool { + match *self { + Ownership::Owned { .. } => true, + _ => false, + } + } +} + +pub fn app(app: &App) -> Ownerships { + let mut ownerships = HashMap::new(); + + for resource in &app.idle.resources { + ownerships.insert(resource.clone(), Ownership::Owned { priority: 0 }); + } + + for task in app.tasks.values() { + for resource in &task.resources { + if let Some(ownership) = ownerships.get_mut(resource) { + match *ownership { + Ownership::Owned { priority } => { + if priority == task.priority { + *ownership = Ownership::Owned { priority }; + } else { + *ownership = Ownership::Shared { + ceiling: cmp::max(priority, task.priority), + }; + } + } + Ownership::Shared { ceiling } => { + if task.priority > ceiling { + *ownership = Ownership::Shared { + ceiling: task.priority, + }; + } + } + } + + continue; + } + + ownerships.insert( + resource.clone(), + Ownership::Owned { + priority: task.priority, + }, + ); + } + } + + ownerships +} diff --git a/macros/src/check.rs b/macros/src/check.rs index 7c86326ac9..a459ab297e 100644 --- a/macros/src/check.rs +++ b/macros/src/check.rs @@ -1,12 +1,79 @@ -use syntax::Statics; -use util::Ceilings; +use std::collections::HashMap; -pub fn resources(resources: &Statics, ceilings: &Ceilings) { - for resource in resources.keys() { - assert!( - ceilings.get(&resource).is_some(), - "resource {} is unused", - resource +use quote::Tokens; +use rtfm_syntax::{Idents, Idle, Init, Statics}; +use syn::Ident; + +use error::*; + +pub struct App { + pub device: Tokens, + pub idle: Idle, + pub init: Init, + pub resources: Statics, + pub tasks: Tasks, +} + +pub type Tasks = HashMap; + +pub struct Task { + pub enabled: Option, + pub priority: u8, + pub resources: Idents, +} + +pub fn app(app: ::rtfm_syntax::App) -> Result { + let mut tasks = HashMap::new(); + + for (k, v) in app.tasks { + let name = k.clone(); + tasks.insert( + k, + ::check::task(v) + .chain_err(|| format!("checking task `{}`", name))?, ); } + + let app = App { + device: app.device, + idle: app.idle, + init: app.init, + resources: app.resources, + tasks, + }; + + ::check::resources(&app)?; + + Ok(app) +} + +fn resources(app: &App) -> Result<()> { + for resource in app.resources.keys() { + if app.idle.resources.contains(resource) { + continue; + } + + if app.tasks + .values() + .any(|task| task.resources.contains(resource)) + { + continue; + } + + bail!("resource `{}` is unused", resource); + } + + Ok(()) +} + +fn task(task: ::rtfm_syntax::Task) -> Result { + if let Some(priority) = task.priority { + Ok(Task { + enabled: task.enabled, + priority, + resources: task.resources, + }) + } else { + bail!("should contain a `priority` field") + } } diff --git a/macros/src/error.rs b/macros/src/error.rs new file mode 100644 index 0000000000..c044473728 --- /dev/null +++ b/macros/src/error.rs @@ -0,0 +1 @@ +error_chain!(); diff --git a/macros/src/lib.rs b/macros/src/lib.rs index 10839eebce..467cbb9360 100644 --- a/macros/src/lib.rs +++ b/macros/src/lib.rs @@ -1,26 +1,44 @@ -#![deny(warnings)] #![feature(proc_macro)] #![recursion_limit = "128"] +#[macro_use] +extern crate error_chain; extern crate proc_macro; #[macro_use] extern crate quote; +extern crate rtfm_syntax; extern crate syn; -mod check; -mod syntax; -mod trans; -mod util; - use proc_macro::TokenStream; +use rtfm_syntax::App; + +use error::*; + +mod analyze; +mod check; +mod error; +mod trans; #[proc_macro] pub fn rtfm(ts: TokenStream) -> TokenStream { + match run(ts) { + Err(e) => panic!("{}", error_chain::ChainedError::display(&e)), + Ok(ts) => ts, + } +} + +fn run(ts: TokenStream) -> Result { let input = format!("{}", ts); - let app = syntax::parse::app(&input); - let ceilings = util::compute_ceilings(&app); - check::resources(&app.resources, &ceilings); + let app = check::app(App::parse(&input) + .chain_err(|| "parsing the `rtfm!` macro")?).chain_err( + || "checking the application specification", + )?; - format!("{}", trans::app(&app, &ceilings)).parse().unwrap() + let ownerships = analyze::app(&app); + let tokens = trans::app(&app, &ownerships); + + Ok(format!("{}", tokens) + .parse() + .map_err(|_| "BUG: error parsing the generated code")?) } diff --git a/macros/src/syntax/mod.rs b/macros/src/syntax/mod.rs deleted file mode 100644 index 757e05ed95..0000000000 --- a/macros/src/syntax/mod.rs +++ /dev/null @@ -1,51 +0,0 @@ -use std::collections::{HashMap, HashSet}; - -use syn::Ident; -use quote::Tokens; - -pub mod parse; - -#[derive(Debug)] -pub struct App { - pub device: Tokens, - pub idle: Idle, - pub init: Init, - pub resources: Statics, - pub tasks: Tasks, -} - -#[derive(Debug)] -pub struct Init { - pub path: Tokens, -} - -#[derive(Debug)] -pub struct Idle { - pub local: Statics, - pub path: Tokens, - pub resources: HashSet, -} - -#[derive(Debug)] -pub struct Task { - pub kind: Kind, - pub priority: u8, - pub resources: HashSet, -} - -#[derive(Debug)] -pub enum Kind { - Exception, - Interrupt { enabled: bool }, -} - -// $ident: $ty = $expr; -#[derive(Debug)] -pub struct Resource { - pub expr: Tokens, - pub ty: Tokens, -} - -pub type Statics = HashMap; - -pub type Tasks = HashMap; diff --git a/macros/src/syntax/parse.rs b/macros/src/syntax/parse.rs deleted file mode 100644 index 19b2511163..0000000000 --- a/macros/src/syntax/parse.rs +++ /dev/null @@ -1,522 +0,0 @@ -use std::collections::{HashMap, HashSet}; - -use syn::{self, DelimToken, Ident, IntTy, Lit, Token, TokenTree}; - -use syntax::{App, Idle, Init, Kind, Resource, Statics, Task, Tasks}; - -pub fn app(input: &str) -> App { - let tts = syn::parse_token_trees(input).unwrap(); - - let mut device = None; - let mut init = None; - let mut idle = None; - let mut resources = None; - let mut tasks = None; - - let mut tts = tts.into_iter(); - while let Some(tt) = tts.next() { - let id = if let TokenTree::Token(Token::Ident(id)) = tt { - id - } else { - panic!("expected ident, found {:?}", tt); - }; - - let tt = tts.next(); - assert_eq!( - tt, - Some(TokenTree::Token(Token::Colon)), - "expected colon, found {:?}", - tt - ); - - match id.as_ref() { - "device" => { - assert!(device.is_none(), "duplicated device field"); - - let mut pieces = vec![]; - - loop { - if let Some(tt) = tts.next() { - if tt == TokenTree::Token(Token::Comma) { - break; - } else { - pieces.push(tt); - } - } else { - panic!("expected path, found EOM"); - } - } - - device = Some(quote!(#(#pieces)*)); - continue; - } - "idle" => { - assert!(idle.is_none(), "duplicated idle field"); - - let tt = tts.next(); - if let Some(TokenTree::Delimited(block)) = tt { - assert_eq!( - block.delim, - DelimToken::Brace, - "expected brace, found {:?}", - block.delim - ); - - idle = Some(super::parse::idle(block.tts)); - } else { - panic!("expected block, found {:?}", tt); - } - } - "init" => { - assert!(init.is_none(), "duplicated init field"); - - let tt = tts.next(); - if let Some(TokenTree::Delimited(block)) = tt { - assert_eq!( - block.delim, - DelimToken::Brace, - "expected brace, found {:?}", - block.delim - ); - - init = Some(super::parse::init(block.tts)); - } else { - panic!("expected block, found {:?}", tt); - } - } - "resources" => { - assert!(resources.is_none(), "duplicated resources field"); - - let tt = tts.next(); - if let Some(TokenTree::Delimited(block)) = tt { - assert_eq!( - block.delim, - DelimToken::Brace, - "expected brace, found {:?}", - block.delim - ); - - resources = Some(super::parse::statics(block.tts)); - } - } - "tasks" => { - assert!(tasks.is_none(), "duplicated tasks field"); - - let tt = tts.next(); - if let Some(TokenTree::Delimited(block)) = tt { - assert_eq!( - block.delim, - DelimToken::Brace, - "expected brace, found {:?}", - block.delim - ); - - tasks = Some(super::parse::tasks(block.tts)); - } - } - id => panic!("unexpected field {}", id), - } - - let tt = tts.next(); - assert_eq!( - tt, - Some(TokenTree::Token(Token::Comma)), - "expected comma, found {:?}", - tt - ); - } - - App { - device: device.expect("device field is missing"), - idle: idle.expect("idle field is missing"), - init: init.expect("init field is missing"), - resources: resources.unwrap_or(HashMap::new()), - tasks: tasks.unwrap_or(HashMap::new()), - } -} - -pub fn idle(tts: Vec) -> Idle { - let mut tts = tts.into_iter(); - - let mut local = None; - let mut path = None; - let mut resources = None; - while let Some(tt) = tts.next() { - let id = if let TokenTree::Token(Token::Ident(id)) = tt { - id - } else { - panic!("expected ident, found {:?}", tt); - }; - - let tt = tts.next(); - assert_eq!( - tt, - Some(TokenTree::Token(Token::Colon)), - "expected colon, found {:?}", - tt - ); - - match id.as_ref() { - "local" => { - assert!(local.is_none(), "duplicated local field"); - - let tt = tts.next(); - if let Some(TokenTree::Delimited(block)) = tt { - assert_eq!( - block.delim, - DelimToken::Brace, - "expected brace, found {:?}", - block.delim - ); - - local = Some(super::parse::statics(block.tts)); - } else { - panic!("expected block, found {:?}", tt); - } - } - "path" => { - assert!(path.is_none(), "duplicated path field"); - - let mut pieces = vec![]; - loop { - let tt = tts.next() - .expect("expected comma, found end of macro"); - - if tt == TokenTree::Token(Token::Comma) { - path = Some(quote!(#(#pieces)*)); - break; - } else { - pieces.push(tt); - } - } - - continue; - } - "resources" => { - assert!(resources.is_none(), "duplicated resources field"); - - let tt = tts.next(); - if let Some(TokenTree::Delimited(array)) = tt { - assert_eq!( - array.delim, - DelimToken::Bracket, - "expected bracket, found {:?}", - array.delim - ); - - resources = Some(super::parse::idents(array.tts)); - - } else { - panic!("expected array, found {:?}", tt); - } - } - id => panic!("unexpected field {}", id), - } - - let tt = tts.next(); - assert_eq!( - tt, - Some(TokenTree::Token(Token::Comma)), - "expected comma, found {:?}", - tt - ); - } - - Idle { - local: local.unwrap_or(HashMap::new()), - path: path.expect("path field is missing"), - resources: resources.unwrap_or(HashSet::new()), - } -} - -pub fn init(tts: Vec) -> Init { - let mut tts = tts.into_iter(); - - let mut path = None; - while let Some(tt) = tts.next() { - let id = if let TokenTree::Token(Token::Ident(id)) = tt { - id - } else { - panic!("expected ident, found {:?}", tt); - }; - - let tt = tts.next(); - assert_eq!( - tt, - Some(TokenTree::Token(Token::Colon)), - "expected colon, found {:?}", - tt - ); - - match id.as_ref() { - "path" => { - let mut pieces = vec![]; - loop { - let tt = tts.next() - .expect("expected comma, found end of macro"); - - if tt == TokenTree::Token(Token::Comma) { - path = Some(quote!(#(#pieces)*)); - break; - } else { - pieces.push(tt); - } - } - } - id => panic!("unexpected field {}", id), - } - } - - Init { path: path.expect("path field is missing") } -} - -fn idents(tts: Vec) -> HashSet { - let mut idents = HashSet::new(); - - 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); - idents.insert(id); - - if let Some(tt) = tts.next() { - assert_eq!(tt, TokenTree::Token(Token::Comma)); - - if tts.peek().is_none() { - break; - } - } else { - break; - } - } else { - panic!("expected ident, found {:?}", tt); - }; - } - - idents -} - -pub fn statics(tts: Vec) -> Statics { - let mut resources = HashMap::new(); - - let mut tts = tts.into_iter(); - while let Some(tt) = tts.next() { - let name = if let TokenTree::Token(Token::Ident(ident)) = tt { - ident - } else { - panic!("expected ident, found {:?}", tt); - }; - - assert!( - !resources.contains_key(&name), - "resource {} already listed", - name - ); - - let tt = tts.next(); - assert_eq!( - tt, - Some(TokenTree::Token(Token::Colon)), - "expected comma, found {:?}", - tt - ); - - let mut pieces = vec![]; - loop { - if let Some(tt) = tts.next() { - if tt == TokenTree::Token(Token::Eq) { - break; - } else { - pieces.push(tt); - } - } else { - panic!("expected type, found EOM"); - } - } - - let ty = quote!(#(#pieces)*); - - let mut pieces = vec![]; - loop { - if let Some(tt) = tts.next() { - if tt == TokenTree::Token(Token::Semi) { - break; - } else { - pieces.push(tt); - } - } else { - panic!("expected expression, found EOM"); - } - } - - let expr = quote!(#(#pieces)*); - - let resource = Resource { expr, ty }; - resources.insert(name, resource); - } - - resources -} - -pub fn tasks(tts: Vec) -> Tasks { - let mut tasks = HashMap::new(); - - let mut tts = tts.into_iter(); - while let Some(tt) = tts.next() { - let name = if let TokenTree::Token(Token::Ident(ident)) = tt { - ident - } else { - panic!("expected ident, found {:?}", tt); - }; - - let tt = tts.next(); - assert_eq!( - tt, - Some(TokenTree::Token(Token::Colon)), - "expected colon, found {:?}", - tt - ); - - let tt = tts.next(); - if let Some(TokenTree::Delimited(block)) = tt { - assert_eq!( - block.delim, - DelimToken::Brace, - "expected brace, found {:?}", - block.delim - ); - - assert!(!tasks.contains_key(&name), "task {} already listed", name); - tasks.insert(name, super::parse::task(block.tts)); - } else { - panic!("expected block, found {:?}", tt); - } - - let tt = tts.next(); - assert_eq!( - tt, - Some(TokenTree::Token(Token::Comma)), - "expected comma, found {:?}", - tt - ); - } - - tasks -} - -/// Parses the body of a task -/// -/// ``` -/// enabled: true, -/// priority: 1, -/// resources: [R1, TIM2], -/// ``` -/// -/// the `enabled` field is optional and distinguishes interrupts from -/// exceptions. Interrupts have an `enabled` field, whereas exceptions don't. -fn task(tts: Vec) -> Task { - let mut enabled = None; - let mut priority = None; - let mut resources = None; - - let mut tts = tts.into_iter(); - while let Some(tt) = tts.next() { - let ident = if let TokenTree::Token(Token::Ident(ident)) = tt { - ident - } else { - panic!("expected ident, found {:?}", tt); - }; - - let tt = tts.next(); - assert_eq!( - tt, - Some(TokenTree::Token(Token::Colon)), - "expected colon, found {:?}", - tt - ); - - match ident.as_ref() { - "enabled" => { - assert!(enabled.is_none(), "duplicated enabled field"); - - let tt = tts.next(); - - if let Some(TokenTree::Token(Token::Literal(lit))) = tt { - if let Lit::Bool(b) = lit { - enabled = Some(b); - } else { - panic!("`enabled` value must be a boolean"); - } - } else { - panic!("expected literal, found {:?}", tt); - } - } - "priority" => { - assert!(priority.is_none(), "duplicated priority field"); - - let tt = tts.next(); - - if let Some(TokenTree::Token(Token::Literal(lit))) = tt { - if let Lit::Int(val, ty) = lit { - assert_eq!( - ty, - IntTy::Unsuffixed, - "`priority` value must be an unsuffixed value" - ); - - assert!( - val < 256, - "`priority` value must be less than 256" - ); - - priority = Some(val as u8); - } else { - panic!("enabled value must be a boolean"); - } - } else { - panic!("expected literal, found {:?}", tt); - } - } - "resources" => { - assert!(resources.is_none(), "duplicated resources field"); - - let tt = tts.next(); - if let Some(TokenTree::Delimited(block)) = tt { - assert_eq!( - block.delim, - DelimToken::Bracket, - "expected bracket, found {:?}", - block.delim - ); - - resources = Some(super::parse::idents(block.tts)); - } else { - panic!("expected block, found {:?}", tt); - } - } - id => panic!("unexpected field {}", id), - } - - let tt = tts.next(); - assert_eq!( - tt, - Some(TokenTree::Token(Token::Comma)), - "expected comma, found {:?}", - tt - ); - } - - 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 } - } else { - Kind::Exception - }; - - Task { - kind, - priority, - resources, - } -} diff --git a/macros/src/trans.rs b/macros/src/trans.rs index 13dc51d186..7f5b2787f9 100644 --- a/macros/src/trans.rs +++ b/macros/src/trans.rs @@ -1,21 +1,21 @@ -use quote::Tokens; -use syn::Ident; +use quote::{Ident, Tokens}; -use syntax::{App, Kind}; -use util::{Ceiling, Ceilings}; +use analyze::{Ownership, Ownerships}; +use check::App; fn krate() -> Ident { - Ident::new("rtfm") + Ident::from("rtfm") } -pub fn app(app: &App, ceilings: &Ceilings) -> Tokens { - let mut main = vec![]; +pub fn app(app: &App, ownerships: &Ownerships) -> Tokens { let mut root = vec![]; + let mut main = vec![]; - super::trans::init(app, &mut main, &mut root); - super::trans::idle(app, ceilings, &mut main, &mut root); - super::trans::resources(app, ceilings, &mut root); - super::trans::tasks(app, ceilings, &mut root); + // ::trans::check(app, &mut main); + ::trans::init(app, &mut main, &mut root); + ::trans::idle(app, ownerships, &mut main, &mut root); + ::trans::resources(app, ownerships, &mut root); + ::trans::tasks(app, ownerships, &mut root); root.push(quote! { fn main() { @@ -26,127 +26,15 @@ pub fn app(app: &App, ceilings: &Ceilings) -> Tokens { quote!(#(#root)*) } -fn init(app: &App, main: &mut Vec, root: &mut Vec) { - let device = &app.device; - let krate = krate(); - - let mut tys = vec![quote!(init::Peripherals)]; - let mut exprs = vec![quote!(init::Peripherals::all())]; - let mut mod_items = vec![]; - - if !app.resources.is_empty() { - let mut fields = vec![]; - let mut lifetime = None; - let mut rexprs = vec![]; - - for (name, resource) in &app.resources { - lifetime = Some(quote!('a)); - - let ty = &resource.ty; - - fields.push(quote! { - pub #name: &'a mut #krate::Static<#ty>, - }); - - rexprs.push(quote! { - #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_items.push(quote! { - pub use ::_initResources as Resources; - - impl<#lifetime> Resources<#lifetime> { - pub unsafe fn new() -> Self { - Resources { - #(#rexprs)* - } - } - } - }); - - tys.push(quote!(init::Resources)); - exprs.push(quote!(init::Resources::new())); - } - - root.push(quote! { - mod init { - pub use ::#device::Peripherals; - - #(#mod_items)* - } - }); - - let mut exceptions = vec![]; - let mut interrupts = vec![]; - for (name, task) in &app.tasks { - match task.kind { - Kind::Exception => { - if exceptions.is_empty() { - exceptions.push(quote! { - let scb = #device::SCB.borrow(_cs); - }); - } - - let priority = task.priority; - exceptions.push(quote! { - let prio_bits = #device::NVIC_PRIO_BITS; - let hw = ((1 << prio_bits) - #priority) << (8 - prio_bits); - scb.shpr[#krate::Exception::#name.nr() - 4].write(hw); - }); - } - Kind::Interrupt { enabled } => { - if interrupts.is_empty() { - interrupts.push(quote! { - let nvic = #device::NVIC.borrow(_cs); - }); - } - - let priority = task.priority; - interrupts.push(quote! { - let prio_bits = #device::NVIC_PRIO_BITS; - let hw = ((1 << prio_bits) - #priority) << (8 - prio_bits); - nvic.set_priority(#device::Interrupt::#name, hw); - }); - - if enabled { - interrupts.push(quote! { - nvic.enable(#device::Interrupt::#name); - }); - } else { - interrupts.push(quote! { - nvic.disable(#device::Interrupt::#name); - }); - } - } - } - } - - let init = &app.init.path; - main.push(quote! { - // type check - let init: fn(#(#tys,)*) = #init; - - #krate::atomic(|_cs| unsafe { - init(#(#exprs,)*); - - #(#exceptions)* - #(#interrupts)* - }); - }); -} +// Check that the exceptions / interrupts are valid +// Sadly we can't do this test at expansion time. Instead we'll generate some +// code that won't compile if the interrupt name is invalid. +// fn check(app: &App, main: &mut Vec) { +// } fn idle( app: &App, - ceilings: &Ceilings, + ownerships: &Ownerships, main: &mut Vec, root: &mut Vec, ) { @@ -160,17 +48,17 @@ fn idle( !app.idle .resources .iter() - .all(|resource| ceilings[resource].is_owned()) + .all(|resource| ownerships[resource].is_owned()) { tys.push(quote!(#krate::Threshold)); exprs.push(quote!(unsafe { #krate::Threshold::new(0) })); } - if !app.idle.local.is_empty() { + if !app.idle.locals.is_empty() { let mut lexprs = vec![]; let mut lfields = vec![]; - for (name, resource) in &app.idle.local { + for (name, resource) in &app.idle.locals { let expr = &resource.expr; let ty = &resource.ty; @@ -184,16 +72,16 @@ fn idle( } mod_items.push(quote! { - pub struct Local { + pub struct Locals { #(#lfields)* } }); - tys.push(quote!(&'static mut idle::Local)); - exprs.push(quote!(unsafe { &mut LOCAL })); + tys.push(quote!(&'static mut idle::Locals)); + exprs.push(quote!(unsafe { &mut LOCALS })); main.push(quote! { - static mut LOCAL: idle::Local = idle::Local { + static mut LOCALS: idle::Locals = idle::Locals { #(#lexprs)* }; }); @@ -205,10 +93,10 @@ fn idle( let mut needs_reexport = false; for name in &app.idle.resources { - if ceilings[name].is_owned() { + if ownerships[name].is_owned() { if app.resources.get(name).is_some() { needs_reexport = true; - break + break; } } } @@ -221,7 +109,7 @@ fn idle( let mut rexprs = vec![]; let mut rfields = vec![]; for name in &app.idle.resources { - if ceilings[name].is_owned() { + if ownerships[name].is_owned() { lifetime = Some(quote!('a)); if let Some(resource) = app.resources.get(name) { let ty = &resource.ty; @@ -305,7 +193,282 @@ fn idle( }); } -fn tasks(app: &App, ceilings: &Ceilings, root: &mut Vec) { +fn init(app: &App, main: &mut Vec, root: &mut Vec) { + let device = &app.device; + let krate = krate(); + + let mut tys = vec![quote!(#device::Peripherals)]; + let mut exprs = vec![quote!(#device::Peripherals::all())]; + let mut mod_items = vec![]; + + if !app.resources.is_empty() { + let mut fields = vec![]; + let mut lifetime = None; + let mut rexprs = vec![]; + + for (name, resource) in &app.resources { + lifetime = Some(quote!('a)); + + let ty = &resource.ty; + + fields.push(quote! { + pub #name: &'a mut #krate::Static<#ty>, + }); + + rexprs.push(quote! { + #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_items.push(quote! { + pub use ::_initResources as Resources; + + impl<#lifetime> Resources<#lifetime> { + pub unsafe fn new() -> Self { + Resources { + #(#rexprs)* + } + } + } + }); + + tys.push(quote!(init::Resources)); + exprs.push(quote!(init::Resources::new())); + } + + root.push(quote! { + mod init { + pub use ::#device::Peripherals; + + #(#mod_items)* + } + }); + + let mut exceptions = vec![]; + let mut interrupts = vec![]; + for (name, task) in &app.tasks { + if let Some(enabled) = task.enabled { + // Interrupt. These can be enabled / disabled through the NVIC + if interrupts.is_empty() { + interrupts.push(quote! { + let nvic = #device::NVIC.borrow(_cs); + }); + } + + let priority = task.priority; + interrupts.push(quote! { + let prio_bits = #device::NVIC_PRIO_BITS; + let hw = ((1 << prio_bits) - #priority) << (8 - prio_bits); + nvic.set_priority(#device::Interrupt::#name, hw); + }); + + if enabled { + interrupts.push(quote! { + nvic.enable(#device::Interrupt::#name); + }); + } else { + interrupts.push(quote! { + nvic.disable(#device::Interrupt::#name); + }); + } + } else { + // Exception + if exceptions.is_empty() { + exceptions.push(quote! { + let scb = #device::SCB.borrow(_cs); + }); + } + + let priority = task.priority; + exceptions.push(quote! { + let prio_bits = #device::NVIC_PRIO_BITS; + let hw = ((1 << prio_bits) - #priority) << (8 - prio_bits); + scb.shpr[#krate::Exception::#name.nr() - 4].write(hw); + }); + } + } + + let init = &app.init.path; + main.push(quote! { + // type check + let init: fn(#(#tys,)*) = #init; + + #krate::atomic(|_cs| unsafe { + init(#(#exprs,)*); + + #(#exceptions)* + #(#interrupts)* + }); + }); +} + +fn resources(app: &App, ownerships: &Ownerships, root: &mut Vec) { + let krate = krate(); + let device = &app.device; + + let mut items = vec![]; + let mut impls = vec![]; + for (name, ownership) in ownerships { + let mut impl_items = vec![]; + + match *ownership { + Ownership::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; + } + } + Ownership::Shared { ceiling } => { + if let Some(resource) = app.resources.get(name) { + let expr = &resource.expr; + let ty = &resource.ty; + + root.push(quote! { + static #name: #krate::Resource<#ty> = + #krate::Resource::new(#expr); + }); + + impl_items.push(quote! { + pub fn borrow<'cs>( + &'cs self, + cs: &'cs #krate::CriticalSection, + ) -> &'cs #krate::Static<#ty> { + unsafe { #name.borrow(cs) } + } + + pub fn borrow_mut<'cs>( + &'cs mut self, + cs: &'cs #krate::CriticalSection, + ) -> &'cs mut #krate::Static<#ty> { + unsafe { #name.borrow_mut(cs) } + } + + pub fn claim( + &self, + t: &mut #krate::Threshold, + f: F, + ) -> R + where + F: FnOnce( + &#krate::Static<#ty>, + &mut #krate::Threshold) -> R + { + unsafe { + #name.claim( + #ceiling, + #device::NVIC_PRIO_BITS, + t, + f, + ) + } + } + + pub fn claim_mut( + &mut self, + t: &mut #krate::Threshold, + f: F, + ) -> R + where + F: FnOnce( + &mut #krate::Static<#ty>, + &mut #krate::Threshold) -> R + { + unsafe { + #name.claim_mut( + #ceiling, + #device::NVIC_PRIO_BITS, + t, + f, + ) + } + } + }); + } else { + root.push(quote! { + static #name: #krate::Peripheral<#device::#name> = + #krate::Peripheral::new(#device::#name); + }); + + impl_items.push(quote! { + pub fn borrow<'cs>( + &'cs self, + cs: &'cs #krate::CriticalSection, + ) -> &'cs #device::#name { + unsafe { #name.borrow(cs) } + } + + pub fn claim( + &self, + t: &mut #krate::Threshold, + f: F, + ) -> R + where + F: FnOnce( + &#device::#name, + &mut #krate::Threshold) -> R + { + unsafe { + #name.claim( + #ceiling, + #device::NVIC_PRIO_BITS, + t, + f, + ) + } + } + }); + } + + impls.push(quote! { + #[allow(dead_code)] + impl _resource::#name { + #(#impl_items)* + } + }); + + items.push(quote! { + #[allow(non_camel_case_types)] + pub struct #name { _0: () } + + impl #name { + pub unsafe fn new() -> Self { + #name { _0: () } + } + } + }); + } + } + } + + root.push(quote! { + mod _resource { + #(#items)* + } + + #(#impls)* + }); +} + +fn tasks(app: &App, ownerships: &Ownerships, root: &mut Vec) { + let device = &app.device; let krate = krate(); for (name, task) in &app.tasks { @@ -313,12 +476,11 @@ fn tasks(app: &App, ceilings: &Ceilings, root: &mut Vec) { let mut fields = vec![]; let mut items = 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 => { + match ownerships[name] { + Ownership::Shared { ceiling } if ceiling > task.priority => { fields.push(quote! { pub #name: super::_resource::#name, }); @@ -405,169 +567,5 @@ fn tasks(app: &App, ceilings: &Ceilings, root: &mut Vec) { #(#items)* } }); - } } - -fn resources(app: &App, ceilings: &Ceilings, root: &mut Vec) { - let krate = krate(); - let device = &app.device; - - let mut items = vec![]; - let mut impls = vec![]; - for (name, ceiling) in ceilings { - let mut impl_items = vec![]; - - match *ceiling { - 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; - let ty = &resource.ty; - - root.push(quote! { - static #name: #krate::Resource<#ty> = - #krate::Resource::new(#expr); - }); - - impl_items.push(quote! { - pub fn borrow<'cs>( - &'cs self, - _cs: &'cs #krate::CriticalSection, - ) -> &'cs #krate::Static<#ty> { - unsafe { - #krate::Static::ref_(&*#name.get()) - } - } - - pub fn borrow_mut<'cs>( - &'cs mut self, - _cs: &'cs #krate::CriticalSection, - ) -> &'cs mut #krate::Static<#ty> { - unsafe { - #krate::Static::ref_mut(&mut *#name.get()) - } - } - - pub fn claim( - &self, - t: &mut #krate::Threshold, - f: F, - ) -> R - where - F: FnOnce( - &#krate::Static<#ty>, - &mut #krate::Threshold) -> R - { - unsafe { - #name.claim( - #ceiling, - #device::NVIC_PRIO_BITS, - t, - f, - ) - } - } - - pub fn claim_mut( - &mut self, - t: &mut #krate::Threshold, - f: F, - ) -> R - where - F: FnOnce( - &mut #krate::Static<#ty>, - &mut #krate::Threshold) -> R - { - unsafe { - #name.claim_mut( - #ceiling, - #device::NVIC_PRIO_BITS, - t, - f, - ) - } - } - }); - } else { - root.push(quote! { - static #name: #krate::Peripheral<#device::#name> = - #krate::Peripheral::new(#device::#name); - }); - - impl_items.push(quote! { - pub fn borrow<'cs>( - &'cs self, - _cs: &'cs #krate::CriticalSection, - ) -> &'cs #device::#name { - unsafe { - &*#name.get() - } - } - - pub fn claim( - &self, - t: &mut #krate::Threshold, - f: F, - ) -> R - where - F: FnOnce( - &#device::#name, - &mut #krate::Threshold) -> R - { - unsafe { - #name.claim( - #ceiling, - #device::NVIC_PRIO_BITS, - t, - f, - ) - } - } - }); - } - - impls.push(quote! { - #[allow(dead_code)] - impl _resource::#name { - #(#impl_items)* - } - }); - - items.push(quote! { - #[allow(non_camel_case_types)] - pub struct #name { _0: () } - - impl #name { - pub unsafe fn new() -> Self { - #name { _0: () } - } - } - }); - } - } - } - - root.push(quote! { - mod _resource { - #(#items)* - } - - #(#impls)* - }); -} diff --git a/macros/src/util.rs b/macros/src/util.rs deleted file mode 100644 index 4722ca7de6..0000000000 --- a/macros/src/util.rs +++ /dev/null @@ -1,62 +0,0 @@ -use std::cmp; -use std::collections::HashMap; - -use syn::Ident; - -use syntax::App; - -pub type Ceilings = HashMap; - -#[derive(Clone, Copy, Debug, PartialEq)] -pub enum Ceiling { - // 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 { - match *self { - Ceiling::Owned(_) => true, - _ => false, - } - } -} - -pub fn compute_ceilings(app: &App) -> Ceilings { - let mut ceilings = HashMap::new(); - - for resource in &app.idle.resources { - 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(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); - } - } - } - - continue; - } - - ceilings.insert(resource.clone(), Ceiling::Owned(task.priority)); - } - } - - ceilings -} diff --git a/src/lib.rs b/src/lib.rs index 23f3abd1c5..daca2d0d8e 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -82,6 +82,14 @@ impl

Peripheral

{ Peripheral { peripheral } } + #[inline(always)] + pub unsafe fn borrow<'cs>( + &'static self, + _cs: &'cs CriticalSection, + ) -> &'cs P { + &*self.peripheral.get() + } + #[inline(always)] pub unsafe fn claim( &'static self, @@ -123,7 +131,25 @@ pub struct Resource { impl Resource { pub const fn new(value: T) -> Self { - Resource { data: UnsafeCell::new(value) } + Resource { + data: UnsafeCell::new(value), + } + } + + #[inline(always)] + pub unsafe fn borrow<'cs>( + &'static self, + _cs: &'cs CriticalSection, + ) -> &'cs Static { + Static::ref_(&*self.data.get()) + } + + #[inline(always)] + pub unsafe fn borrow_mut<'cs>( + &'static self, + _cs: &'cs CriticalSection, + ) -> &'cs mut Static { + Static::ref_mut(&mut *self.data.get()) } #[inline(always)]