aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--macros/Cargo.toml5
-rw-r--r--macros/src/analyze.rs70
-rw-r--r--macros/src/check.rs85
-rw-r--r--macros/src/error.rs1
-rw-r--r--macros/src/lib.rs36
-rw-r--r--macros/src/syntax/mod.rs51
-rw-r--r--macros/src/syntax/parse.rs522
-rw-r--r--macros/src/trans.rs470
-rw-r--r--macros/src/util.rs62
-rw-r--r--src/lib.rs28
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
-}
diff --git a/src/lib.rs b/src/lib.rs
index 23f3abd1..daca2d0d 100644
--- a/src/lib.rs
+++ b/src/lib.rs
@@ -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)]