mirror of
https://github.com/rtic-rs/rtic.git
synced 2024-12-25 03:19:34 +01:00
split macro parser into its own crate and improve error handling / reporting
This commit is contained in:
parent
59afbf02aa
commit
98596554b3
10 changed files with 511 additions and 961 deletions
|
@ -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
70
macros/src/analyze.rs
Normal 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
|
||||||
|
}
|
|
@ -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
1
macros/src/error.rs
Normal file
|
@ -0,0 +1 @@
|
||||||
|
error_chain!();
|
|
@ -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")?)
|
||||||
}
|
}
|
||||||
|
|
|
@ -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>;
|
|
|
@ -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,
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -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)*
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
|
@ -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
|
|
||||||
}
|
|
28
src/lib.rs
28
src/lib.rs
|
@ -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)]
|
||||||
|
|
Loading…
Reference in a new issue