mirror of
https://github.com/rtic-rs/rtic.git
synced 2024-12-24 02:49: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"
|
||||
|
||||
[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
|
||||
|
|
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 util::Ceilings;
|
||||
use std::collections::HashMap;
|
||||
|
||||
pub fn resources(resources: &Statics, ceilings: &Ceilings) {
|
||||
for resource in resources.keys() {
|
||||
assert!(
|
||||
ceilings.get(&resource).is_some(),
|
||||
"resource {} is unused",
|
||||
resource
|
||||
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")
|
||||
}
|
||||
}
|
||||
|
|
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)]
|
||||
#![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;
|
||||
|
||||
mod check;
|
||||
mod syntax;
|
||||
mod trans;
|
||||
mod util;
|
||||
|
||||
use proc_macro::TokenStream;
|
||||
use rtfm_syntax::App;
|
||||
|
||||
use error::*;
|
||||
|
||||
mod analyze;
|
||||
mod check;
|
||||
mod error;
|
||||
mod trans;
|
||||
|
||||
#[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",
|
||||
)?;
|
||||
|
||||
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 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,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();
|
||||
|
||||
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 items = vec![];
|
||||
|
||||
let device = &app.device;
|
||||
let mut lifetime = None;
|
||||
let mut needs_reexport = false;
|
||||
for name in &task.resources {
|
||||
match ceilings[name] {
|
||||
Ceiling::Shared(ceiling) if ceiling > task.priority => {
|
||||
match ownerships[name] {
|
||||
Ownership::Shared { ceiling } if ceiling > task.priority => {
|
||||
fields.push(quote! {
|
||||
pub #name: super::_resource::#name,
|
||||
});
|
||||
|
@ -405,169 +567,5 @@ fn tasks(app: &App, ceilings: &Ceilings, root: &mut Vec<Tokens>) {
|
|||
#(#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 }
|
||||
}
|
||||
|
||||
#[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,
|
||||
|
@ -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)]
|
||||
|
|
Loading…
Reference in a new issue