split macro parser into its own crate and improve error handling / reporting

This commit is contained in:
Jorge Aparicio 2017-07-14 18:54:54 -05:00
parent 59afbf02aa
commit 98596554b3
10 changed files with 511 additions and 961 deletions

View file

@ -4,8 +4,13 @@ name = "cortex-m-rtfm-macros"
version = "0.1.0" version = "0.1.0"
[dependencies] [dependencies]
error-chain = "0.10.0"
quote = "0.3.15" quote = "0.3.15"
syn = "0.11.11" syn = "0.11.11"
[dependencies.rtfm-syntax]
git = "https://github.com/japaric/rtfm-syntax"
optional = false
[lib] [lib]
proc-macro = true proc-macro = true

70
macros/src/analyze.rs Normal file
View file

@ -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
}

View file

@ -1,12 +1,79 @@
use syntax::Statics; use std::collections::HashMap;
use util::Ceilings;
pub fn resources(resources: &Statics, ceilings: &Ceilings) { use quote::Tokens;
for resource in resources.keys() { use rtfm_syntax::{Idents, Idle, Init, Statics};
assert!( use syn::Ident;
ceilings.get(&resource).is_some(),
"resource {} is unused", use error::*;
resource
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")
}
} }

1
macros/src/error.rs Normal file
View file

@ -0,0 +1 @@
error_chain!();

View file

@ -1,26 +1,44 @@
#![deny(warnings)]
#![feature(proc_macro)] #![feature(proc_macro)]
#![recursion_limit = "128"] #![recursion_limit = "128"]
#[macro_use]
extern crate error_chain;
extern crate proc_macro; extern crate proc_macro;
#[macro_use] #[macro_use]
extern crate quote; extern crate quote;
extern crate rtfm_syntax;
extern crate syn; extern crate syn;
mod check;
mod syntax;
mod trans;
mod util;
use proc_macro::TokenStream; use proc_macro::TokenStream;
use rtfm_syntax::App;
use error::*;
mod analyze;
mod check;
mod error;
mod trans;
#[proc_macro] #[proc_macro]
pub fn rtfm(ts: TokenStream) -> TokenStream { 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 input = format!("{}", ts);
let app = syntax::parse::app(&input); let app = check::app(App::parse(&input)
let ceilings = util::compute_ceilings(&app); .chain_err(|| "parsing the `rtfm!` macro")?).chain_err(
check::resources(&app.resources, &ceilings); || "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")?)
} }

View file

@ -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>;

View file

@ -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,
}
}

View file

@ -1,21 +1,21 @@
use quote::Tokens; use quote::{Ident, Tokens};
use syn::Ident;
use syntax::{App, Kind}; use analyze::{Ownership, Ownerships};
use util::{Ceiling, Ceilings}; use check::App;
fn krate() -> Ident { fn krate() -> Ident {
Ident::new("rtfm") Ident::from("rtfm")
} }
pub fn app(app: &App, ceilings: &Ceilings) -> Tokens { pub fn app(app: &App, ownerships: &Ownerships) -> Tokens {
let mut main = vec![];
let mut root = vec![]; let mut root = vec![];
let mut main = vec![];
super::trans::init(app, &mut main, &mut root); // ::trans::check(app, &mut main);
super::trans::idle(app, ceilings, &mut main, &mut root); ::trans::init(app, &mut main, &mut root);
super::trans::resources(app, ceilings, &mut root); ::trans::idle(app, ownerships, &mut main, &mut root);
super::trans::tasks(app, ceilings, &mut root); ::trans::resources(app, ownerships, &mut root);
::trans::tasks(app, ownerships, &mut root);
root.push(quote! { root.push(quote! {
fn main() { fn main() {
@ -26,127 +26,15 @@ pub fn app(app: &App, ceilings: &Ceilings) -> Tokens {
quote!(#(#root)*) quote!(#(#root)*)
} }
fn init(app: &App, main: &mut Vec<Tokens>, root: &mut Vec<Tokens>) { // Check that the exceptions / interrupts are valid
let device = &app.device; // Sadly we can't do this test at expansion time. Instead we'll generate some
let krate = krate(); // code that won't compile if the interrupt name is invalid.
// fn check(app: &App, main: &mut Vec<Tokens>) {
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)*
});
});
}
fn idle( fn idle(
app: &App, app: &App,
ceilings: &Ceilings, ownerships: &Ownerships,
main: &mut Vec<Tokens>, main: &mut Vec<Tokens>,
root: &mut Vec<Tokens>, root: &mut Vec<Tokens>,
) { ) {
@ -160,17 +48,17 @@ fn idle(
!app.idle !app.idle
.resources .resources
.iter() .iter()
.all(|resource| ceilings[resource].is_owned()) .all(|resource| ownerships[resource].is_owned())
{ {
tys.push(quote!(#krate::Threshold)); tys.push(quote!(#krate::Threshold));
exprs.push(quote!(unsafe { #krate::Threshold::new(0) })); 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 lexprs = vec![];
let mut lfields = vec![]; let mut lfields = vec![];
for (name, resource) in &app.idle.local { for (name, resource) in &app.idle.locals {
let expr = &resource.expr; let expr = &resource.expr;
let ty = &resource.ty; let ty = &resource.ty;
@ -184,16 +72,16 @@ fn idle(
} }
mod_items.push(quote! { mod_items.push(quote! {
pub struct Local { pub struct Locals {
#(#lfields)* #(#lfields)*
} }
}); });
tys.push(quote!(&'static mut idle::Local)); tys.push(quote!(&'static mut idle::Locals));
exprs.push(quote!(unsafe { &mut LOCAL })); exprs.push(quote!(unsafe { &mut LOCALS }));
main.push(quote! { main.push(quote! {
static mut LOCAL: idle::Local = idle::Local { static mut LOCALS: idle::Locals = idle::Locals {
#(#lexprs)* #(#lexprs)*
}; };
}); });
@ -205,10 +93,10 @@ fn idle(
let mut needs_reexport = false; let mut needs_reexport = false;
for name in &app.idle.resources { for name in &app.idle.resources {
if ceilings[name].is_owned() { if ownerships[name].is_owned() {
if app.resources.get(name).is_some() { if app.resources.get(name).is_some() {
needs_reexport = true; needs_reexport = true;
break break;
} }
} }
} }
@ -221,7 +109,7 @@ fn idle(
let mut rexprs = vec![]; let mut rexprs = vec![];
let mut rfields = vec![]; let mut rfields = vec![];
for name in &app.idle.resources { for name in &app.idle.resources {
if ceilings[name].is_owned() { if ownerships[name].is_owned() {
lifetime = Some(quote!('a)); lifetime = Some(quote!('a));
if let Some(resource) = app.resources.get(name) { if let Some(resource) = app.resources.get(name) {
let ty = &resource.ty; let ty = &resource.ty;
@ -305,7 +193,282 @@ 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();
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<Tokens>) {
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<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.borrow(cs) }
}
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)*
});
}
fn tasks(app: &App, ownerships: &Ownerships, root: &mut Vec<Tokens>) {
let device = &app.device;
let krate = krate(); let krate = krate();
for (name, task) in &app.tasks { for (name, task) in &app.tasks {
@ -313,12 +476,11 @@ fn tasks(app: &App, ceilings: &Ceilings, root: &mut Vec<Tokens>) {
let mut fields = vec![]; let mut fields = vec![];
let mut items = vec![]; let mut items = vec![];
let device = &app.device;
let mut lifetime = None; let mut lifetime = None;
let mut needs_reexport = false; let mut needs_reexport = false;
for name in &task.resources { for name in &task.resources {
match ceilings[name] { match ownerships[name] {
Ceiling::Shared(ceiling) if ceiling > task.priority => { Ownership::Shared { ceiling } if ceiling > task.priority => {
fields.push(quote! { fields.push(quote! {
pub #name: super::_resource::#name, pub #name: super::_resource::#name,
}); });
@ -405,169 +567,5 @@ fn tasks(app: &App, ceilings: &Ceilings, root: &mut Vec<Tokens>) {
#(#items)* #(#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(_) => {
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<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)*
});
}

View file

@ -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
}

View file

@ -82,6 +82,14 @@ impl<P> Peripheral<P> {
Peripheral { peripheral } Peripheral { peripheral }
} }
#[inline(always)]
pub unsafe fn borrow<'cs>(
&'static self,
_cs: &'cs CriticalSection,
) -> &'cs P {
&*self.peripheral.get()
}
#[inline(always)] #[inline(always)]
pub unsafe fn claim<R, F>( pub unsafe fn claim<R, F>(
&'static self, &'static self,
@ -123,7 +131,25 @@ pub struct Resource<T> {
impl<T> Resource<T> { impl<T> Resource<T> {
pub const fn new(value: T) -> Self { 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)] #[inline(always)]