diff options
-rw-r--r-- | macros/Cargo.toml | 5 | ||||
-rw-r--r-- | macros/src/analyze.rs | 70 | ||||
-rw-r--r-- | macros/src/check.rs | 85 | ||||
-rw-r--r-- | macros/src/error.rs | 1 | ||||
-rw-r--r-- | macros/src/lib.rs | 36 | ||||
-rw-r--r-- | macros/src/syntax/mod.rs | 51 | ||||
-rw-r--r-- | macros/src/syntax/parse.rs | 522 | ||||
-rw-r--r-- | macros/src/trans.rs | 470 | ||||
-rw-r--r-- | macros/src/util.rs | 62 | ||||
-rw-r--r-- | src/lib.rs | 28 |
10 files changed, 440 insertions, 890 deletions
diff --git a/macros/Cargo.toml b/macros/Cargo.toml index 3aece079..4237e21e 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 00000000..0fc125da --- /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<Ident, Ownership>; + +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 7c86326a..a459ab29 100644 --- a/macros/src/check.rs +++ b/macros/src/check.rs @@ -1,12 +1,79 @@ -use syntax::Statics; -use util::Ceilings; - -pub fn resources(resources: &Statics, ceilings: &Ceilings) { - for resource in resources.keys() { - assert!( - ceilings.get(&resource).is_some(), - "resource {} is unused", - resource +use std::collections::HashMap; + +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<Ident, Task>; + +pub struct Task { + pub enabled: Option<bool>, + pub priority: u8, + pub resources: Idents, +} + +pub fn app(app: ::rtfm_syntax::App) -> Result<App> { + 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<Task> { + 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 00000000..c0444737 --- /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 10839eeb..467cbb93 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; +use proc_macro::TokenStream; +use rtfm_syntax::App; + +use error::*; + +mod analyze; mod check; -mod syntax; +mod error; mod trans; -mod util; - -use proc_macro::TokenStream; #[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<TokenStream> { 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", + )?; + + let ownerships = analyze::app(&app); + let tokens = trans::app(&app, &ownerships); - format!("{}", trans::app(&app, &ceilings)).parse().unwrap() + 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 757e05ed..00000000 --- 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<Ident>, -} - -#[derive(Debug)] -pub struct Task { - pub kind: Kind, - pub priority: u8, - pub resources: HashSet<Ident>, -} - -#[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<Ident, Resource>; - -pub type Tasks = HashMap<Ident, Task>; diff --git a/macros/src/syntax/parse.rs b/macros/src/syntax/parse.rs deleted file mode 100644 index 19b25111..00000000 --- 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<TokenTree>) -> 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<TokenTree>) -> 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<TokenTree>) -> HashSet<Ident> { - 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<TokenTree>) -> 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<TokenTree>) -> 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<TokenTree>) -> 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 13dc51d1..7f5b2787 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<Tokens>, root: &mut Vec<Tokens>) { - 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<Tokens>) { +// } fn idle( app: &App, - ceilings: &Ceilings, + ownerships: &Ownerships, main: &mut Vec<Tokens>, root: &mut Vec<Tokens>, ) { @@ -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,121 +193,134 @@ fn idle( }); } -fn tasks(app: &App, ceilings: &Ceilings, root: &mut Vec<Tokens>) { +fn init(app: &App, main: &mut Vec<Tokens>, root: &mut Vec<Tokens>) { + let device = &app.device; let krate = krate(); - for (name, task) in &app.tasks { - let mut exprs = vec![]; - let mut fields = vec![]; - let mut items = vec![]; + let mut tys = vec![quote!(#device::Peripherals)]; + let mut exprs = vec![quote!(#device::Peripherals::all())]; + let mut mod_items = vec![]; - let device = &app.device; + if !app.resources.is_empty() { + let mut fields = vec![]; let mut lifetime = None; - let mut needs_reexport = false; - for name in &task.resources { - match ceilings[name] { - Ceiling::Shared(ceiling) if ceiling > task.priority => { - fields.push(quote! { - pub #name: super::_resource::#name, - }); - - exprs.push(quote! { - #name: { - super::_resource::#name::new() - }, - }); - } - _ => { - lifetime = Some(quote!('a)); - if let Some(resource) = app.resources.get(name) { - needs_reexport = true; - let ty = &resource.ty; - - fields.push(quote! { - pub #name: &'a mut ::#krate::Static<#ty>, - }); + let mut rexprs = vec![]; - exprs.push(quote! { - #name: ::#krate::Static::ref_mut( - &mut *super::#name.get(), - ), - }); - } else { - fields.push(quote! { - pub #name: &'a mut ::#device::#name, - }); + for (name, resource) in &app.resources { + lifetime = Some(quote!('a)); - exprs.push(quote! { - #name: &mut *::#device::#name.get(), - }); - } - } - } - } + let ty = &resource.ty; - 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)* - } + fields.push(quote! { + pub #name: &'a mut #krate::Static<#ty>, }); - items.push(quote! { - pub use ::#rname as Resources; - }); - } else { - items.push(quote! { - #[allow(non_snake_case)] - pub struct Resources<#lifetime> { - #(#fields)* - } + rexprs.push(quote! { + #name: ::#krate::Static::ref_mut(&mut *super::#name.get()), }); } - items.push(quote! { + 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 { - #(#exprs)* + #(#rexprs)* } } } }); - let priority = task.priority; - root.push(quote!{ - #[allow(dead_code)] - #[allow(non_snake_case)] - mod #name { - #[deny(dead_code)] - pub const #name: u8 = #priority; - #[deny(const_err)] - const CHECK_PRIORITY: (u8, u8) = ( - #priority - 1, - (1 << ::#device::NVIC_PRIO_BITS) - #priority, - ); + tys.push(quote!(init::Resources)); + exprs.push(quote!(init::Resources::new())); + } - #(#items)* + 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, ceilings: &Ceilings, root: &mut Vec<Tokens>) { +fn resources(app: &App, ownerships: &Ownerships, root: &mut Vec<Tokens>) { let krate = krate(); let device = &app.device; let mut items = vec![]; let mut impls = vec![]; - for (name, ceiling) in ceilings { + for (name, ownership) in ownerships { let mut impl_items = vec![]; - match *ceiling { - Ceiling::Owned(_) => { + match *ownership { + Ownership::Owned { .. } => { if let Some(resource) = app.resources.get(name) { // For owned resources we don't need claim() or borrow(), // just get() @@ -432,10 +333,10 @@ fn resources(app: &App, ceilings: &Ceilings, root: &mut Vec<Tokens>) { }); } else { // Peripheral - continue + continue; } - }, - Ceiling::Shared(ceiling) => { + } + Ownership::Shared { ceiling } => { if let Some(resource) = app.resources.get(name) { let expr = &resource.expr; let ty = &resource.ty; @@ -448,20 +349,16 @@ fn resources(app: &App, ceilings: &Ceilings, root: &mut Vec<Tokens>) { impl_items.push(quote! { pub fn borrow<'cs>( &'cs self, - _cs: &'cs #krate::CriticalSection, + cs: &'cs #krate::CriticalSection, ) -> &'cs #krate::Static<#ty> { - unsafe { - #krate::Static::ref_(&*#name.get()) - } + unsafe { #name.borrow(cs) } } pub fn borrow_mut<'cs>( &'cs mut self, - _cs: &'cs #krate::CriticalSection, + cs: &'cs #krate::CriticalSection, ) -> &'cs mut #krate::Static<#ty> { - unsafe { - #krate::Static::ref_mut(&mut *#name.get()) - } + unsafe { #name.borrow_mut(cs) } } pub fn claim<R, F>( @@ -513,11 +410,9 @@ fn resources(app: &App, ceilings: &Ceilings, root: &mut Vec<Tokens>) { impl_items.push(quote! { pub fn borrow<'cs>( &'cs self, - _cs: &'cs #krate::CriticalSection, + cs: &'cs #krate::CriticalSection, ) -> &'cs #device::#name { - unsafe { - &*#name.get() - } + unsafe { #name.borrow(cs) } } pub fn claim<R, F>( @@ -571,3 +466,106 @@ fn resources(app: &App, ceilings: &Ceilings, root: &mut Vec<Tokens>) { #(#impls)* }); } + +fn tasks(app: &App, ownerships: &Ownerships, root: &mut Vec<Tokens>) { + let device = &app.device; + let krate = krate(); + + for (name, task) in &app.tasks { + let mut exprs = vec![]; + let mut fields = vec![]; + let mut items = vec![]; + + let mut lifetime = None; + let mut needs_reexport = false; + for name in &task.resources { + match ownerships[name] { + Ownership::Shared { ceiling } if ceiling > task.priority => { + fields.push(quote! { + pub #name: super::_resource::#name, + }); + + exprs.push(quote! { + #name: { + super::_resource::#name::new() + }, + }); + } + _ => { + lifetime = Some(quote!('a)); + if let Some(resource) = app.resources.get(name) { + needs_reexport = true; + let ty = &resource.ty; + + fields.push(quote! { + pub #name: &'a mut ::#krate::Static<#ty>, + }); + + exprs.push(quote! { + #name: ::#krate::Static::ref_mut( + &mut *super::#name.get(), + ), + }); + } else { + fields.push(quote! { + pub #name: &'a mut ::#device::#name, + }); + + exprs.push(quote! { + #name: &mut *::#device::#name.get(), + }); + } + } + } + } + + 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> { + pub unsafe fn new() -> Self { + Resources { + #(#exprs)* + } + } + } + }); + + let priority = task.priority; + root.push(quote!{ + #[allow(dead_code)] + #[allow(non_snake_case)] + mod #name { + #[deny(dead_code)] + pub const #name: u8 = #priority; + #[deny(const_err)] + const CHECK_PRIORITY: (u8, u8) = ( + #priority - 1, + (1 << ::#device::NVIC_PRIO_BITS) - #priority, + ); + + #(#items)* + } + }); + } +} diff --git a/macros/src/util.rs b/macros/src/util.rs deleted file mode 100644 index 4722ca7d..00000000 --- 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<Ident, Ceiling>; - -#[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 -} @@ -83,6 +83,14 @@ impl<P> Peripheral<P> { } #[inline(always)] + pub unsafe fn borrow<'cs>( + &'static self, + _cs: &'cs CriticalSection, + ) -> &'cs P { + &*self.peripheral.get() + } + + #[inline(always)] pub unsafe fn claim<R, F>( &'static self, ceiling: u8, @@ -123,7 +131,25 @@ pub struct Resource<T> { impl<T> Resource<T> { 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<T> { + Static::ref_(&*self.data.get()) + } + + #[inline(always)] + pub unsafe fn borrow_mut<'cs>( + &'static self, + _cs: &'cs CriticalSection, + ) -> &'cs mut Static<T> { + Static::ref_mut(&mut *self.data.get()) } #[inline(always)] |