diff options
Diffstat (limited to 'macros/src')
-rw-r--r-- | macros/src/check.rs | 17 | ||||
-rw-r--r-- | macros/src/lib.rs | 63 | ||||
-rw-r--r-- | macros/src/syntax/mod.rs | 52 | ||||
-rw-r--r-- | macros/src/syntax/parse.rs | 496 | ||||
-rw-r--r-- | macros/src/trans.rs | 486 | ||||
-rw-r--r-- | macros/src/util.rs | 48 |
6 files changed, 1162 insertions, 0 deletions
diff --git a/macros/src/check.rs b/macros/src/check.rs new file mode 100644 index 00000000..ddd9abc4 --- /dev/null +++ b/macros/src/check.rs @@ -0,0 +1,17 @@ +use syntax::Resources; +use util::{Ceiling, Ceilings}; + +pub fn resources(resources: &Resources, 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) + } + } +} diff --git a/macros/src/lib.rs b/macros/src/lib.rs new file mode 100644 index 00000000..05210fbb --- /dev/null +++ b/macros/src/lib.rs @@ -0,0 +1,63 @@ +#![feature(plugin_registrar)] +#![feature(proc_macro_internals)] +#![feature(rustc_private)] +#![recursion_limit = "128"] + +extern crate proc_macro; +#[macro_use] +extern crate quote; +extern crate rustc_errors; +extern crate rustc_plugin; +extern crate syn; +extern crate syntax as rustc_syntax; + +use proc_macro::TokenStream; +use rustc_errors::Handler; +use rustc_errors::emitter::ColorConfig; +use rustc_plugin::Registry; +use rustc_syntax::codemap::{CodeMap, FilePathMapping}; +use rustc_syntax::ext::base::SyntaxExtension; +use rustc_syntax::parse::ParseSess; +use rustc_syntax::symbol::Symbol; +use rustc_syntax::tokenstream::TokenStream as TokenStream_; +use std::rc::Rc; +use std::str::FromStr; + +mod check; +mod syntax; +mod trans; +mod util; + +fn expand_rtfm(ts: TokenStream_) -> TokenStream_ { + let input = format!("{}", ts); + + let app = syntax::parse::app(&input); + let ceilings = util::compute_ceilings(&app); + check::resources(&app.resources, &ceilings); + + let output = format!("{}", trans::app(&app, &ceilings)); + + let mapping = FilePathMapping::empty(); + let codemap = Rc::new(CodeMap::new(mapping)); + + let tty_handler = Handler::with_tty_emitter( + ColorConfig::Auto, + true, + false, + Some(codemap.clone()), + ); + + let sess = ParseSess::with_span_handler(tty_handler, codemap.clone()); + proc_macro::__internal::set_parse_sess(&sess, || { + let ts = TokenStream::from_str(&output).unwrap(); + proc_macro::__internal::token_stream_inner(ts) + }) +} + +#[plugin_registrar] +pub fn plugin_registrar(reg: &mut Registry) { + reg.register_syntax_extension( + Symbol::intern("rtfm"), + SyntaxExtension::ProcMacro(Box::new(expand_rtfm)), + ); +} diff --git a/macros/src/syntax/mod.rs b/macros/src/syntax/mod.rs new file mode 100644 index 00000000..c856617e --- /dev/null +++ b/macros/src/syntax/mod.rs @@ -0,0 +1,52 @@ +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: Resources, + pub tasks: Tasks, +} + +#[derive(Debug)] +pub struct Init { + pub path: Tokens, + pub resources: HashSet<Ident>, +} + +#[derive(Debug)] +pub struct Idle { + pub local: Resources, + 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 Resources = HashMap<Ident, Resource>; + +pub type Tasks = HashMap<Ident, Task>; diff --git a/macros/src/syntax/parse.rs b/macros/src/syntax/parse.rs new file mode 100644 index 00000000..e6d3f461 --- /dev/null +++ b/macros/src/syntax/parse.rs @@ -0,0 +1,496 @@ +use std::collections::{HashMap, HashSet}; + +use syn::{self, DelimToken, Ident, IntTy, Lit, Token, TokenTree}; +use quote::Tokens; + +use syntax::{App, Idle, Init, Kind, Resource, Resources, 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::resources(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.expect("resources field is missing"), + tasks: tasks.expect("tasks field is missing"), + } +} + +fn idle_init( + tts: Vec<TokenTree>, + allows_locals: bool, +) -> (Option<Resources>, Tokens, HashSet<Ident>) { + 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" if allows_locals => { + 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::resources(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 EOM"); + + 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 + ); + } + + ( + local, + path.expect("path field is missing"), + resources.unwrap_or(HashSet::new()), + ) +} + +pub fn idle(tts: Vec<TokenTree>) -> Idle { + let (locals, path, resources) = idle_init(tts, true); + + Idle { + local: locals.expect("local field is missing"), + path, + resources, + } +} + +pub fn init(tts: Vec<TokenTree>) -> Init { + let (_, path, resources) = idle_init(tts, false); + + Init { path, resources } +} + +fn idents(tts: Vec<TokenTree>) -> HashSet<Ident> { + let mut idents = HashSet::new(); + + let mut tts = tts.into_iter(); + 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)); + } else { + break; + } + } else { + panic!("expected ident, found {:?}", tt); + }; + } + + idents +} + +pub fn resources(tts: Vec<TokenTree>) -> Resources { + 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.expect("resources field is missing"); + 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 new file mode 100644 index 00000000..6cf3a517 --- /dev/null +++ b/macros/src/trans.rs @@ -0,0 +1,486 @@ +use quote::Tokens; +use syn::Ident; + +use syntax::{App, Kind}; +use util::{Ceiling, Ceilings}; + +fn krate() -> Ident { + Ident::new("rtfm") +} + +pub fn app(app: &App, ceilings: &Ceilings) -> Tokens { + let mut main = vec![]; + let mut root = 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); + + root.push(quote! { + fn main() { + #(#main)* + } + }); + + quote!(#(#root)*) +} + +fn init(app: &App, main: &mut Vec<Tokens>, root: &mut Vec<Tokens>) { + let device = &app.device; + let krate = krate(); + + let mut fields = vec![]; + let mut exprs = vec![]; + let mut lifetime = None; + for name in &app.init.resources { + lifetime = Some(quote!('a)); + + if let Some(resource) = app.resources.get(name) { + let ty = &resource.ty; + + fields.push(quote! { + pub #name: &'a mut #ty, + }); + + exprs.push(quote! { + #name: &mut *super::#name.get(), + }); + } else { + fields.push(quote! { + pub #name: &'a mut ::#device::#name, + }); + + exprs.push(quote! { + #name: &mut *::#device::#name.get(), + }); + } + } + + root.push(quote! { + mod init { + #[allow(non_snake_case)] + pub struct Resources<#lifetime> { + #(#fields)* + } + + impl<#lifetime> Resources<#lifetime> { + pub unsafe fn new() -> Self { + Resources { + #(#exprs)* + } + } + } + } + }); + + 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[rtfm::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(init::Resources) = #init; + + #krate::atomic(|cs| unsafe { + init(init::Resources::new()); + + #(#exceptions)* + #(#interrupts)* + }); + }); +} + +fn idle( + app: &App, + ceilings: &Ceilings, + main: &mut Vec<Tokens>, + root: &mut Vec<Tokens>, +) { + let krate = krate(); + + let mut mod_items = vec![]; + let mut tys = vec![]; + let mut exprs = vec![]; + + if !app.idle.resources.is_empty() && + !app.idle + .resources + .iter() + .all(|resource| ceilings[resource].is_owned()) + { + tys.push(quote!(#krate::Threshold)); + exprs.push(quote!(unsafe { #krate::Threshold::new(0) })); + } + + if !app.idle.local.is_empty() { + let mut lexprs = vec![]; + let mut lfields = vec![]; + + for (name, resource) in &app.idle.local { + let expr = &resource.expr; + let ty = &resource.ty; + + lfields.push(quote! { + pub #name: #ty, + }); + + lexprs.push(quote! { + #name: #expr, + }); + } + + mod_items.push(quote! { + pub struct Local { + #(#lfields)* + } + }); + + tys.push(quote!(&'static mut idle::Local)); + exprs.push(quote!(unsafe { &mut LOCAL })); + + main.push(quote! { + static mut LOCAL: idle::Local = idle::Local { + #(#lexprs)* + }; + }); + } + + if !app.idle.resources.is_empty() { + let device = &app.device; + let mut lifetime = None; + + let mut rexprs = vec![]; + let mut rfields = vec![]; + for name in &app.idle.resources { + if ceilings[name].is_owned() { + lifetime = Some(quote!('a)); + + 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, + }); + + rexprs.push(quote! { + #name: super::_resource::#name::new(), + }); + } + } + + mod_items.push(quote! { + #[allow(non_snake_case)] + pub struct Resources<#lifetime> { + #(#rfields)* + } + + impl<#lifetime> Resources<#lifetime> { + pub unsafe fn new() -> Self { + Resources { + #(#rexprs)* + } + } + } + }); + + tys.push(quote!(idle::Resources)); + exprs.push(quote!(unsafe { idle::Resources::new() })); + } + + root.push(quote! { + mod idle { + #(#mod_items)* + } + }); + + let idle = &app.idle.path; + main.push(quote! { + // type check + let idle: fn(#(#tys),*) -> ! = #idle; + + idle(#(#exprs),*); + }); +} + +fn tasks(app: &App, ceilings: &Ceilings, root: &mut Vec<Tokens>) { + let krate = krate(); + + for (name, task) in &app.tasks { + let mut exprs = vec![]; + let mut fields = vec![]; + let mut items = vec![]; + + let device = &app.device; + let mut lifetime = None; + 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) { + 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(), + }); + } + } + } + } + + 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)* + } + }); + + } +} + +fn resources(app: &App, ceilings: &Ceilings, root: &mut Vec<Tokens>) { + 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 => 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<R, F>( + &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<R, F>( + &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<R, F>( + &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 new file mode 100644 index 00000000..45f1feef --- /dev/null +++ b/macros/src/util.rs @@ -0,0 +1,48 @@ +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, + Shared(u8), +} + +impl Ceiling { + pub fn is_owned(&self) -> bool { + *self == Ceiling::Owned + } +} + +pub fn compute_ceilings(app: &App) -> Ceilings { + let mut ceilings = HashMap::new(); + + for resource in &app.idle.resources { + ceilings.insert(resource.clone(), Ceiling::Owned); + } + + 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::Shared(old) => { + if task.priority > old { + *ceiling = Ceiling::Shared(task.priority); + } + } + } + + continue; + } + + ceilings.insert(resource.clone(), Ceiling::Owned); + } + } + + ceilings +} |