aboutsummaryrefslogtreecommitdiff
path: root/macros/src
diff options
context:
space:
mode:
Diffstat (limited to 'macros/src')
-rw-r--r--macros/src/check.rs17
-rw-r--r--macros/src/lib.rs63
-rw-r--r--macros/src/syntax/mod.rs52
-rw-r--r--macros/src/syntax/parse.rs496
-rw-r--r--macros/src/trans.rs486
-rw-r--r--macros/src/util.rs48
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
+}