rtfm! macro take 2

This commit is contained in:
Jorge Aparicio 2017-07-04 11:26:11 -05:00
parent 2bf5401439
commit 86a360a396
27 changed files with 1367 additions and 2237 deletions

View file

@ -10,24 +10,8 @@ keywords = ["arm", "cortex-m"]
license = "MIT OR Apache-2.0" license = "MIT OR Apache-2.0"
name = "cortex-m-rtfm" name = "cortex-m-rtfm"
repository = "https://github.com/japaric/cortex-m-rtfm" repository = "https://github.com/japaric/cortex-m-rtfm"
version = "0.1.1" version = "0.2.0"
[build-dependencies]
quote = "0.3.15"
syn = "0.11.10"
[dependencies] [dependencies]
cortex-m = "0.2.6" cortex-m = "0.3.0"
static-ref = "0.1.0" static-ref = "0.2.0"
typenum = "1.7.0"
[dev-dependencies]
compiletest_rs = "0.2.5"
[features]
# Number of priority bits
P2 = []
P3 = []
P4 = []
P5 = []
default = ["P4"]

130
build.rs
View file

@ -1,137 +1,11 @@
#[macro_use]
extern crate quote;
extern crate syn;
use std::env; use std::env;
use std::fs::File;
use std::io::Write;
use std::path::PathBuf;
use syn::{Ident, IntTy, Lit};
fn main() { fn main() {
let target = env::var("TARGET").unwrap(); let target = env::var("TARGET").unwrap();
if target.starts_with("thumbv6m") { if target.starts_with("thumbv6m-") {
println!("cargo:rustc-cfg=thumbv6m"); println!("cargo:rustc-cfg=armv6m");
} }
let bits = if env::var_os("CARGO_FEATURE_P2").is_some() {
2
} else if env::var_os("CARGO_FEATURE_P3").is_some() {
3
} else if env::var_os("CARGO_FEATURE_P4").is_some() {
4
} else if env::var_os("CARGO_FEATURE_P5").is_some() {
5
} else {
panic!(
"Specify the number of priority bits through one of these Cargo \
features: P2, P3, P4 or P5"
);
};
let n = Lit::Int(bits, IntTy::Unsuffixed);
let mut tokens = vec![];
tokens.push(
quote! {
const PRIORITY_BITS: u8 = #n;
},
);
// Ceilings and thresholds
for i in 0..(1 << bits) + 1 {
let c = Ident::new(format!("C{}", i));
let t = Ident::new(format!("T{}", i));
let u = Ident::new(format!("U{}", i));
let doc = format!("A ceiling of {}", i);
tokens.push(
quote! {
#[doc = #doc]
pub type #c = ::typenum::#u;
},
);
let doc = format!("A preemption threshold of {}", i);
tokens.push(
quote! {
#[doc = #doc]
pub type #t = Threshold<::typenum::#u>;
},
);
}
// Priorities
for i in 0..(1 << bits) + 1 {
let p = Ident::new(format!("P{}", i));
let u = Ident::new(format!("U{}", i));
let doc = format!(
"A priority of {}{}",
i,
if i == 0 {
", the lowest priority"
} else if i == (1 << bits) {
", the highest priority"
} else {
""
}
);
tokens.push(
quote! {
#[doc = #doc]
pub type #p = Priority<::typenum::#u>;
},
);
}
// GreaterThanOrEqual & LessThanOrEqual
for i in 0..(1 << bits) + 1 {
for j in 0..(i + 1) {
let i = Ident::new(format!("U{}", i));
let j = Ident::new(format!("U{}", j));
tokens.push(
quote! {
unsafe impl GreaterThanOrEqual<::typenum::#j> for
::typenum::#i {}
unsafe impl LessThanOrEqual<::typenum::#i> for
::typenum::#j {}
},
);
}
}
let u = Ident::new(format!("U{}", (1 << bits)));
let c = Ident::new(format!("C{}", (1 << bits)));
let p = Ident::new(format!("P{}", (1 << bits)));
let t = Ident::new(format!("T{}", (1 << bits)));
tokens.push(
quote! {
/// Maximum ceiling
pub type CMax = #c;
/// Maximum priority
pub type PMax = #p;
/// Maximum preemption threshold
pub type TMax = #t;
/// Maximum priority level
pub type UMax = ::typenum::#u;
},
);
let tokens = quote! {
#(#tokens)*
};
let out_dir = env::var("OUT_DIR").unwrap();
let mut out = File::create(PathBuf::from(out_dir).join("prio.rs")).unwrap();
out.write_all(tokens.as_str().as_bytes()).unwrap();
println!("cargo:rerun-if-changed=build.rs"); println!("cargo:rerun-if-changed=build.rs");
} }

11
macros/Cargo.toml Normal file
View file

@ -0,0 +1,11 @@
[package]
authors = ["Jorge Aparicio <jorge@japaric.io>"]
name = "rtfm-macros"
version = "0.1.0"
[dependencies]
quote = "0.3.15"
syn = "0.11.11"
[lib]
plugin = true

17
macros/src/check.rs Normal file
View file

@ -0,0 +1,17 @@
use syntax::Resources;
use util::{Ceiling, Ceilings};
pub fn resources(resources: &Resources, ceilings: &Ceilings) {
for resource in resources.keys() {
if let Some(ceiling) = ceilings.get(&resource) {
assert_ne!(
*ceiling,
Ceiling::Owned,
"{} should be local data",
resource
);
} else {
panic!("resource {} is unused", resource)
}
}
}

63
macros/src/lib.rs Normal file
View file

@ -0,0 +1,63 @@
#![feature(plugin_registrar)]
#![feature(proc_macro_internals)]
#![feature(rustc_private)]
#![recursion_limit = "128"]
extern crate proc_macro;
#[macro_use]
extern crate quote;
extern crate rustc_errors;
extern crate rustc_plugin;
extern crate syn;
extern crate syntax as rustc_syntax;
use proc_macro::TokenStream;
use rustc_errors::Handler;
use rustc_errors::emitter::ColorConfig;
use rustc_plugin::Registry;
use rustc_syntax::codemap::{CodeMap, FilePathMapping};
use rustc_syntax::ext::base::SyntaxExtension;
use rustc_syntax::parse::ParseSess;
use rustc_syntax::symbol::Symbol;
use rustc_syntax::tokenstream::TokenStream as TokenStream_;
use std::rc::Rc;
use std::str::FromStr;
mod check;
mod syntax;
mod trans;
mod util;
fn expand_rtfm(ts: TokenStream_) -> TokenStream_ {
let input = format!("{}", ts);
let app = syntax::parse::app(&input);
let ceilings = util::compute_ceilings(&app);
check::resources(&app.resources, &ceilings);
let output = format!("{}", trans::app(&app, &ceilings));
let mapping = FilePathMapping::empty();
let codemap = Rc::new(CodeMap::new(mapping));
let tty_handler = Handler::with_tty_emitter(
ColorConfig::Auto,
true,
false,
Some(codemap.clone()),
);
let sess = ParseSess::with_span_handler(tty_handler, codemap.clone());
proc_macro::__internal::set_parse_sess(&sess, || {
let ts = TokenStream::from_str(&output).unwrap();
proc_macro::__internal::token_stream_inner(ts)
})
}
#[plugin_registrar]
pub fn plugin_registrar(reg: &mut Registry) {
reg.register_syntax_extension(
Symbol::intern("rtfm"),
SyntaxExtension::ProcMacro(Box::new(expand_rtfm)),
);
}

52
macros/src/syntax/mod.rs Normal file
View file

@ -0,0 +1,52 @@
use std::collections::{HashMap, HashSet};
use syn::Ident;
use quote::Tokens;
pub mod parse;
#[derive(Debug)]
pub struct App {
pub device: Tokens,
pub idle: Idle,
pub init: Init,
pub resources: Resources,
pub tasks: Tasks,
}
#[derive(Debug)]
pub struct Init {
pub path: Tokens,
pub resources: HashSet<Ident>,
}
#[derive(Debug)]
pub struct Idle {
pub local: Resources,
pub path: Tokens,
pub resources: HashSet<Ident>,
}
#[derive(Debug)]
pub struct Task {
pub kind: Kind,
pub priority: u8,
pub resources: HashSet<Ident>,
}
#[derive(Debug)]
pub enum Kind {
Exception,
Interrupt { enabled: bool },
}
// $ident: $ty = $expr;
#[derive(Debug)]
pub struct Resource {
pub expr: Tokens,
pub ty: Tokens,
}
pub type Resources = HashMap<Ident, Resource>;
pub type Tasks = HashMap<Ident, Task>;

496
macros/src/syntax/parse.rs Normal file
View file

@ -0,0 +1,496 @@
use std::collections::{HashMap, HashSet};
use syn::{self, DelimToken, Ident, IntTy, Lit, Token, TokenTree};
use quote::Tokens;
use syntax::{App, Idle, Init, Kind, Resource, Resources, Task, Tasks};
pub fn app(input: &str) -> App {
let tts = syn::parse_token_trees(input).unwrap();
let mut device = None;
let mut init = None;
let mut idle = None;
let mut resources = None;
let mut tasks = None;
let mut tts = tts.into_iter();
while let Some(tt) = tts.next() {
let id = if let TokenTree::Token(Token::Ident(id)) = tt {
id
} else {
panic!("expected ident, found {:?}", tt);
};
let tt = tts.next();
assert_eq!(
tt,
Some(TokenTree::Token(Token::Colon)),
"expected colon, found {:?}",
tt
);
match id.as_ref() {
"device" => {
assert!(device.is_none(), "duplicated device field");
let mut pieces = vec![];
loop {
if let Some(tt) = tts.next() {
if tt == TokenTree::Token(Token::Comma) {
break;
} else {
pieces.push(tt);
}
} else {
panic!("expected path, found EOM");
}
}
device = Some(quote!(#(#pieces)*));
continue;
}
"idle" => {
assert!(idle.is_none(), "duplicated idle field");
let tt = tts.next();
if let Some(TokenTree::Delimited(block)) = tt {
assert_eq!(
block.delim,
DelimToken::Brace,
"expected brace, found {:?}",
block.delim
);
idle = Some(super::parse::idle(block.tts));
} else {
panic!("expected block, found {:?}", tt);
}
}
"init" => {
assert!(init.is_none(), "duplicated init field");
let tt = tts.next();
if let Some(TokenTree::Delimited(block)) = tt {
assert_eq!(
block.delim,
DelimToken::Brace,
"expected brace, found {:?}",
block.delim
);
init = Some(super::parse::init(block.tts));
} else {
panic!("expected block, found {:?}", tt);
}
}
"resources" => {
assert!(resources.is_none(), "duplicated resources field");
let tt = tts.next();
if let Some(TokenTree::Delimited(block)) = tt {
assert_eq!(
block.delim,
DelimToken::Brace,
"expected brace, found {:?}",
block.delim
);
resources = Some(super::parse::resources(block.tts));
}
}
"tasks" => {
assert!(tasks.is_none(), "duplicated tasks field");
let tt = tts.next();
if let Some(TokenTree::Delimited(block)) = tt {
assert_eq!(
block.delim,
DelimToken::Brace,
"expected brace, found {:?}",
block.delim
);
tasks = Some(super::parse::tasks(block.tts));
}
}
id => panic!("unexpected field {}", id),
}
let tt = tts.next();
assert_eq!(
tt,
Some(TokenTree::Token(Token::Comma)),
"expected comma, found {:?}",
tt
);
}
App {
device: device.expect("device field is missing"),
idle: idle.expect("idle field is missing"),
init: init.expect("init field is missing"),
resources: resources.expect("resources field is missing"),
tasks: tasks.expect("tasks field is missing"),
}
}
fn idle_init(
tts: Vec<TokenTree>,
allows_locals: bool,
) -> (Option<Resources>, Tokens, HashSet<Ident>) {
let mut tts = tts.into_iter();
let mut local = None;
let mut path = None;
let mut resources = None;
while let Some(tt) = tts.next() {
let id = if let TokenTree::Token(Token::Ident(id)) = tt {
id
} else {
panic!("expected ident, found {:?}", tt);
};
let tt = tts.next();
assert_eq!(
tt,
Some(TokenTree::Token(Token::Colon)),
"expected colon, found {:?}",
tt
);
match id.as_ref() {
"local" if allows_locals => {
assert!(local.is_none(), "duplicated local field");
let tt = tts.next();
if let Some(TokenTree::Delimited(block)) = tt {
assert_eq!(
block.delim,
DelimToken::Brace,
"expected brace, found {:?}",
block.delim
);
local = Some(super::parse::resources(block.tts));
} else {
panic!("expected block, found {:?}", tt);
}
}
"path" => {
assert!(path.is_none(), "duplicated path field");
let mut pieces = vec![];
loop {
let tt = tts.next().expect("expected comma, found EOM");
if tt == TokenTree::Token(Token::Comma) {
path = Some(quote!(#(#pieces)*));
break;
} else {
pieces.push(tt);
}
}
continue;
}
"resources" => {
assert!(resources.is_none(), "duplicated resources field");
let tt = tts.next();
if let Some(TokenTree::Delimited(array)) = tt {
assert_eq!(
array.delim,
DelimToken::Bracket,
"expected bracket, found {:?}",
array.delim
);
resources = Some(super::parse::idents(array.tts));
} else {
panic!("expected array, found {:?}", tt);
}
}
id => panic!("unexpected field {}", id),
}
let tt = tts.next();
assert_eq!(
tt,
Some(TokenTree::Token(Token::Comma)),
"expected comma, found {:?}",
tt
);
}
(
local,
path.expect("path field is missing"),
resources.unwrap_or(HashSet::new()),
)
}
pub fn idle(tts: Vec<TokenTree>) -> Idle {
let (locals, path, resources) = idle_init(tts, true);
Idle {
local: locals.expect("local field is missing"),
path,
resources,
}
}
pub fn init(tts: Vec<TokenTree>) -> Init {
let (_, path, resources) = idle_init(tts, false);
Init { path, resources }
}
fn idents(tts: Vec<TokenTree>) -> HashSet<Ident> {
let mut idents = HashSet::new();
let mut tts = tts.into_iter();
while let Some(tt) = tts.next() {
if let TokenTree::Token(Token::Ident(id)) = tt {
assert!(!idents.contains(&id), "ident {} already listed", id);
idents.insert(id);
if let Some(tt) = tts.next() {
assert_eq!(tt, TokenTree::Token(Token::Comma));
} else {
break;
}
} else {
panic!("expected ident, found {:?}", tt);
};
}
idents
}
pub fn resources(tts: Vec<TokenTree>) -> Resources {
let mut resources = HashMap::new();
let mut tts = tts.into_iter();
while let Some(tt) = tts.next() {
let name = if let TokenTree::Token(Token::Ident(ident)) = tt {
ident
} else {
panic!("expected ident, found {:?}", tt);
};
assert!(
!resources.contains_key(&name),
"resource {} already listed",
name
);
let tt = tts.next();
assert_eq!(
tt,
Some(TokenTree::Token(Token::Colon)),
"expected comma, found {:?}",
tt
);
let mut pieces = vec![];
loop {
if let Some(tt) = tts.next() {
if tt == TokenTree::Token(Token::Eq) {
break;
} else {
pieces.push(tt);
}
} else {
panic!("expected type, found EOM");
}
}
let ty = quote!(#(#pieces)*);
let mut pieces = vec![];
loop {
if let Some(tt) = tts.next() {
if tt == TokenTree::Token(Token::Semi) {
break;
} else {
pieces.push(tt);
}
} else {
panic!("expected expression, found EOM");
}
}
let expr = quote!(#(#pieces)*);
let resource = Resource { expr, ty };
resources.insert(name, resource);
}
resources
}
pub fn tasks(tts: Vec<TokenTree>) -> Tasks {
let mut tasks = HashMap::new();
let mut tts = tts.into_iter();
while let Some(tt) = tts.next() {
let name = if let TokenTree::Token(Token::Ident(ident)) = tt {
ident
} else {
panic!("expected ident, found {:?}", tt);
};
let tt = tts.next();
assert_eq!(
tt,
Some(TokenTree::Token(Token::Colon)),
"expected colon, found {:?}",
tt
);
let tt = tts.next();
if let Some(TokenTree::Delimited(block)) = tt {
assert_eq!(
block.delim,
DelimToken::Brace,
"expected brace, found {:?}",
block.delim
);
assert!(!tasks.contains_key(&name), "task {} already listed", name);
tasks.insert(name, super::parse::task(block.tts));
} else {
panic!("expected block, found {:?}", tt);
}
let tt = tts.next();
assert_eq!(
tt,
Some(TokenTree::Token(Token::Comma)),
"expected comma, found {:?}",
tt
);
}
tasks
}
/// Parses the body of a task
///
/// ```
/// enabled: true,
/// priority: 1,
/// resources: [R1, TIM2],
/// ```
///
/// the `enabled` field is optional and distinguishes interrupts from
/// exceptions. Interrupts have an `enabled` field, whereas exceptions don't.
fn task(tts: Vec<TokenTree>) -> Task {
let mut enabled = None;
let mut priority = None;
let mut resources = None;
let mut tts = tts.into_iter();
while let Some(tt) = tts.next() {
let ident = if let TokenTree::Token(Token::Ident(ident)) = tt {
ident
} else {
panic!("expected ident, found {:?}", tt);
};
let tt = tts.next();
assert_eq!(
tt,
Some(TokenTree::Token(Token::Colon)),
"expected colon, found {:?}",
tt
);
match ident.as_ref() {
"enabled" => {
assert!(enabled.is_none(), "duplicated enabled field");
let tt = tts.next();
if let Some(TokenTree::Token(Token::Literal(lit))) = tt {
if let Lit::Bool(b) = lit {
enabled = Some(b);
} else {
panic!("`enabled` value must be a boolean");
}
} else {
panic!("expected literal, found {:?}", tt);
}
}
"priority" => {
assert!(priority.is_none(), "duplicated priority field");
let tt = tts.next();
if let Some(TokenTree::Token(Token::Literal(lit))) = tt {
if let Lit::Int(val, ty) = lit {
assert_eq!(
ty,
IntTy::Unsuffixed,
"`priority` value must be an unsuffixed value"
);
assert!(
val < 256,
"`priority` value must be less than 256"
);
priority = Some(val as u8);
} else {
panic!("enabled value must be a boolean");
}
} else {
panic!("expected literal, found {:?}", tt);
}
}
"resources" => {
assert!(resources.is_none(), "duplicated resources field");
let tt = tts.next();
if let Some(TokenTree::Delimited(block)) = tt {
assert_eq!(
block.delim,
DelimToken::Bracket,
"expected bracket, found {:?}",
block.delim
);
resources = Some(super::parse::idents(block.tts));
} else {
panic!("expected block, found {:?}", tt);
}
}
id => panic!("unexpected field {}", id),
}
let tt = tts.next();
assert_eq!(
tt,
Some(TokenTree::Token(Token::Comma)),
"expected comma, found {:?}",
tt
);
}
let resources = resources.expect("resources field is missing");
let priority = priority.expect("priority field is missing");
let kind = if let Some(enabled) = enabled {
Kind::Interrupt { enabled }
} else {
Kind::Exception
};
Task {
kind,
priority,
resources,
}
}

486
macros/src/trans.rs Normal file
View file

@ -0,0 +1,486 @@
use quote::Tokens;
use syn::Ident;
use syntax::{App, Kind};
use util::{Ceiling, Ceilings};
fn krate() -> Ident {
Ident::new("rtfm")
}
pub fn app(app: &App, ceilings: &Ceilings) -> Tokens {
let mut main = vec![];
let mut root = vec![];
super::trans::init(app, &mut main, &mut root);
super::trans::idle(app, ceilings, &mut main, &mut root);
super::trans::resources(app, ceilings, &mut root);
super::trans::tasks(app, ceilings, &mut root);
root.push(quote! {
fn main() {
#(#main)*
}
});
quote!(#(#root)*)
}
fn init(app: &App, main: &mut Vec<Tokens>, root: &mut Vec<Tokens>) {
let device = &app.device;
let krate = krate();
let mut fields = vec![];
let mut exprs = vec![];
let mut lifetime = None;
for name in &app.init.resources {
lifetime = Some(quote!('a));
if let Some(resource) = app.resources.get(name) {
let ty = &resource.ty;
fields.push(quote! {
pub #name: &'a mut #ty,
});
exprs.push(quote! {
#name: &mut *super::#name.get(),
});
} else {
fields.push(quote! {
pub #name: &'a mut ::#device::#name,
});
exprs.push(quote! {
#name: &mut *::#device::#name.get(),
});
}
}
root.push(quote! {
mod init {
#[allow(non_snake_case)]
pub struct Resources<#lifetime> {
#(#fields)*
}
impl<#lifetime> Resources<#lifetime> {
pub unsafe fn new() -> Self {
Resources {
#(#exprs)*
}
}
}
}
});
let mut exceptions = vec![];
let mut interrupts = vec![];
for (name, task) in &app.tasks {
match task.kind {
Kind::Exception => {
if exceptions.is_empty() {
exceptions.push(quote! {
let scb = #device::SCB.borrow(cs);
});
}
let priority = task.priority;
exceptions.push(quote! {
let prio_bits = #device::NVIC_PRIO_BITS;
let hw = ((1 << prio_bits) - #priority) << (8 - prio_bits);
scb.shpr[rtfm::Exception::#name.nr() - 4].write(hw);
});
}
Kind::Interrupt { enabled } => {
if interrupts.is_empty() {
interrupts.push(quote! {
let nvic = #device::NVIC.borrow(cs);
});
}
let priority = task.priority;
interrupts.push(quote! {
let prio_bits = #device::NVIC_PRIO_BITS;
let hw = ((1 << prio_bits) - #priority) << (8 - prio_bits);
nvic.set_priority(#device::Interrupt::#name, hw);
});
if enabled {
interrupts.push(quote! {
nvic.enable(#device::Interrupt::#name);
});
} else {
interrupts.push(quote! {
nvic.disable(#device::Interrupt::#name);
});
}
}
}
}
let init = &app.init.path;
main.push(quote! {
// type check
let init: fn(init::Resources) = #init;
#krate::atomic(|cs| unsafe {
init(init::Resources::new());
#(#exceptions)*
#(#interrupts)*
});
});
}
fn idle(
app: &App,
ceilings: &Ceilings,
main: &mut Vec<Tokens>,
root: &mut Vec<Tokens>,
) {
let krate = krate();
let mut mod_items = vec![];
let mut tys = vec![];
let mut exprs = vec![];
if !app.idle.resources.is_empty() &&
!app.idle
.resources
.iter()
.all(|resource| ceilings[resource].is_owned())
{
tys.push(quote!(#krate::Threshold));
exprs.push(quote!(unsafe { #krate::Threshold::new(0) }));
}
if !app.idle.local.is_empty() {
let mut lexprs = vec![];
let mut lfields = vec![];
for (name, resource) in &app.idle.local {
let expr = &resource.expr;
let ty = &resource.ty;
lfields.push(quote! {
pub #name: #ty,
});
lexprs.push(quote! {
#name: #expr,
});
}
mod_items.push(quote! {
pub struct Local {
#(#lfields)*
}
});
tys.push(quote!(&'static mut idle::Local));
exprs.push(quote!(unsafe { &mut LOCAL }));
main.push(quote! {
static mut LOCAL: idle::Local = idle::Local {
#(#lexprs)*
};
});
}
if !app.idle.resources.is_empty() {
let device = &app.device;
let mut lifetime = None;
let mut rexprs = vec![];
let mut rfields = vec![];
for name in &app.idle.resources {
if ceilings[name].is_owned() {
lifetime = Some(quote!('a));
rfields.push(quote! {
pub #name: &'a mut ::#device::#name,
});
rexprs.push(quote! {
#name: &mut *::#device::#name.get(),
});
} else {
rfields.push(quote! {
pub #name: super::_resource::#name,
});
rexprs.push(quote! {
#name: super::_resource::#name::new(),
});
}
}
mod_items.push(quote! {
#[allow(non_snake_case)]
pub struct Resources<#lifetime> {
#(#rfields)*
}
impl<#lifetime> Resources<#lifetime> {
pub unsafe fn new() -> Self {
Resources {
#(#rexprs)*
}
}
}
});
tys.push(quote!(idle::Resources));
exprs.push(quote!(unsafe { idle::Resources::new() }));
}
root.push(quote! {
mod idle {
#(#mod_items)*
}
});
let idle = &app.idle.path;
main.push(quote! {
// type check
let idle: fn(#(#tys),*) -> ! = #idle;
idle(#(#exprs),*);
});
}
fn tasks(app: &App, ceilings: &Ceilings, root: &mut Vec<Tokens>) {
let krate = krate();
for (name, task) in &app.tasks {
let mut exprs = vec![];
let mut fields = vec![];
let mut items = vec![];
let device = &app.device;
let mut lifetime = None;
for name in &task.resources {
match ceilings[name] {
Ceiling::Shared(ceiling) if ceiling > task.priority => {
fields.push(quote! {
pub #name: super::_resource::#name,
});
exprs.push(quote! {
#name: {
super::_resource::#name::new()
},
});
}
_ => {
lifetime = Some(quote!('a));
if let Some(resource) = app.resources.get(name) {
let ty = &resource.ty;
fields.push(quote! {
pub #name: &'a mut ::#krate::Static<#ty>,
});
exprs.push(quote! {
#name: ::#krate::Static::ref_mut(
&mut *super::#name.get(),
),
});
} else {
fields.push(quote! {
pub #name: &'a mut ::#device::#name,
});
exprs.push(quote! {
#name: &mut *::#device::#name.get(),
});
}
}
}
}
items.push(quote! {
#[allow(non_snake_case)]
pub struct Resources<#lifetime> {
#(#fields)*
}
});
items.push(quote! {
impl<#lifetime> Resources<#lifetime> {
pub unsafe fn new() -> Self {
Resources {
#(#exprs)*
}
}
}
});
let priority = task.priority;
root.push(quote!{
#[allow(dead_code)]
#[allow(non_snake_case)]
mod #name {
#[deny(dead_code)]
pub const #name: u8 = #priority;
#[deny(const_err)]
const CHECK_PRIORITY: (u8, u8) = (
#priority - 1,
(1 << ::#device::NVIC_PRIO_BITS) - #priority,
);
#(#items)*
}
});
}
}
fn resources(app: &App, ceilings: &Ceilings, root: &mut Vec<Tokens>) {
let krate = krate();
let device = &app.device;
let mut items = vec![];
let mut impls = vec![];
for (name, ceiling) in ceilings {
let mut impl_items = vec![];
match *ceiling {
Ceiling::Owned => continue,
Ceiling::Shared(ceiling) => {
if let Some(resource) = app.resources.get(name) {
let expr = &resource.expr;
let ty = &resource.ty;
root.push(quote! {
static #name: #krate::Resource<#ty> =
#krate::Resource::new(#expr);
});
impl_items.push(quote! {
pub fn borrow<'cs>(
&'cs self,
_cs: &'cs #krate::CriticalSection,
) -> &'cs #krate::Static<#ty> {
unsafe {
#krate::Static::ref_(&*#name.get())
}
}
pub fn borrow_mut<'cs>(
&'cs mut self,
_cs: &'cs #krate::CriticalSection,
) -> &'cs mut #krate::Static<#ty> {
unsafe {
#krate::Static::ref_mut(&mut *#name.get())
}
}
pub fn claim<R, F>(
&self,
t: &mut #krate::Threshold,
f: F,
) -> R
where
F: FnOnce(
&#krate::Static<#ty>,
&mut #krate::Threshold) -> R
{
unsafe {
#name.claim(
#ceiling,
#device::NVIC_PRIO_BITS,
t,
f,
)
}
}
pub fn claim_mut<R, F>(
&mut self,
t: &mut #krate::Threshold,
f: F,
) -> R
where
F: FnOnce(
&mut #krate::Static<#ty>,
&mut #krate::Threshold) -> R
{
unsafe {
#name.claim_mut(
#ceiling,
#device::NVIC_PRIO_BITS,
t,
f,
)
}
}
});
} else {
root.push(quote! {
static #name: #krate::Peripheral<#device::#name> =
#krate::Peripheral::new(#device::#name);
});
impl_items.push(quote! {
pub fn borrow<'cs>(
&'cs self,
_cs: &'cs #krate::CriticalSection,
) -> &'cs #device::#name {
unsafe {
&*#name.get()
}
}
pub fn claim<R, F>(
&self,
t: &mut #krate::Threshold,
f: F,
) -> R
where
F: FnOnce(
&#device::#name,
&mut #krate::Threshold) -> R
{
unsafe {
#name.claim(
#ceiling,
#device::NVIC_PRIO_BITS,
t,
f,
)
}
}
});
}
impls.push(quote! {
#[allow(dead_code)]
impl _resource::#name {
#(#impl_items)*
}
});
items.push(quote! {
#[allow(non_camel_case_types)]
pub struct #name { _0: () }
impl #name {
pub unsafe fn new() -> Self {
#name { _0: () }
}
}
});
}
}
}
root.push(quote! {
mod _resource {
#(#items)*
}
#(#impls)*
});
}

48
macros/src/util.rs Normal file
View file

@ -0,0 +1,48 @@
use std::collections::HashMap;
use syn::Ident;
use syntax::App;
pub type Ceilings = HashMap<Ident, Ceiling>;
#[derive(Clone, Copy, Debug, PartialEq)]
pub enum Ceiling {
Owned,
Shared(u8),
}
impl Ceiling {
pub fn is_owned(&self) -> bool {
*self == Ceiling::Owned
}
}
pub fn compute_ceilings(app: &App) -> Ceilings {
let mut ceilings = HashMap::new();
for resource in &app.idle.resources {
ceilings.insert(resource.clone(), Ceiling::Owned);
}
for task in app.tasks.values() {
for resource in &task.resources {
if let Some(ceiling) = ceilings.get_mut(resource) {
match *ceiling {
Ceiling::Owned => *ceiling = Ceiling::Shared(task.priority),
Ceiling::Shared(old) => {
if task.priority > old {
*ceiling = Ceiling::Shared(task.priority);
}
}
}
continue;
}
ceilings.insert(resource.clone(), Ceiling::Owned);
}
}
ceilings
}

1096
src/lib.rs

File diff suppressed because it is too large Load diff

View file

@ -1,16 +0,0 @@
extern crate compiletest_rs as compiletest;
use std::path::PathBuf;
use compiletest::common::Mode;
#[test]
fn cfail() {
let mut config = compiletest::default_config();
config.mode = Mode::CompileFail;
config.src_base = PathBuf::from(format!("tests/cfail"));
config.target_rustcflags =
Some("-L target/debug -L target/debug/deps ".to_string());
compiletest::run_tests(&config);
}

View file

@ -1,35 +0,0 @@
extern crate cortex_m_rtfm as rtfm;
use rtfm::{C1, C2, C3, C4, C5, P2, Resource, T2};
static R1: Resource<i32, C4> = Resource::new(0);
static R2: Resource<i32, C3> = Resource::new(0);
static R3: Resource<i32, C4> = Resource::new(0);
static R4: Resource<i32, C5> = Resource::new(0);
static R5: Resource<i32, C1> = Resource::new(0);
static R6: Resource<i32, C2> = Resource::new(0);
fn j1(prio: P2, thr: T2) {
thr.raise(
&R1, |thr| {
// NOTE PT = Preemption Threshold, TP = Task Priority
// CAN access a resource with ceiling RC when PT > RC
let r2 = R2.access(&prio, thr);
// CAN access a resource with ceiling RC when PT == RC
let r3 = R3.access(&prio, thr);
// CAN'T access a resource with ceiling RC when PT < RC
let r4 = R4.access(&prio, thr);
//~^ error
// CAN'T access a resource with ceiling RC when RC < TP
let r5 = R5.access(&prio, thr);
//~^ error
// CAN access a resource with ceiling RC when RC == tP
let r6 = R6.access(&prio, thr);
}
);
}

View file

@ -1,29 +0,0 @@
extern crate cortex_m_rtfm as rtfm;
use rtfm::{C2, C3, P0, P2, Resource, T2};
static R1: Resource<(), C3> = Resource::new(());
fn j1(prio: P2, thr: T2) {
let t3 = thr.raise(
&R1, |thr| {
// forbidden: ceiling token can't outlive the critical section
thr //~ error
}
);
// Would be bad: lockless access to a resource with ceiling = 3
let r2 = R1.access(&prio, t3);
}
fn j2(prio: P0) {
let c16 = rtfm::atomic(
|c16| {
// forbidden: ceiling token can't outlive the critical section
c16 //~ error
},
);
// Would be bad: lockless access to a resource with ceiling = 16
let r1 = R1.access(&prio, c16);
}

View file

@ -1,36 +0,0 @@
extern crate cortex_m_rtfm as rtfm;
use rtfm::{CMax, C2, P1, P2, P3, PMax, Resource, T1, T2, T3, TMax};
static R1: Resource<i32, C2> = Resource::new(0);
// You don't need to raise the ceiling to access a resource with ceiling equal
// to the task priority.
fn j1(prio: P2, thr: T2) {
thr.raise(&R1, |_| {});
//~^ error
// OK
let r1 = R1.access(&prio, &thr);
}
// You CAN access a resource with ceiling C from a task with priority P if C > P
// if you raise the preemption threshold first
fn j2(prio: P1, thr: T1) {
// OK
thr.raise(&R1, |thr| { let r1 = R1.access(&prio, thr); })
}
static R2: Resource<i32, CMax> = Resource::new(0);
// Tasks with priority less than P16 can't access a resource with ceiling CMax
fn j4(prio: P1, thr: T1) {
thr.raise(&R2, |thr| {});
//~^ error
}
// Only tasks with priority P16 can directly access a resource with ceiling CMax
fn j5(prio: PMax, thr: TMax) {
// OK
let r2 = R2.access(&prio, &thr);
}

View file

@ -1,68 +0,0 @@
// error-pattern: has already been defined
#![feature(used)]
#[macro_use]
extern crate cortex_m_rtfm as rtfm;
use rtfm::{P0, P1, T0, TMax};
use device::interrupt::Exti0;
peripherals!(device, {
GPIOA: Peripheral {
register_block: Gpioa,
ceiling: C1,
},
// WRONG: peripheral alias
GPIOA: Peripheral {
register_block: Gpioa,
ceiling: C2,
},
});
tasks!(device, {});
fn init(_: P0, _: &TMax) {}
fn idle(_: P0, _: T0) -> ! {
loop {}
}
fn j1(_task: Exti0, _prio: P1) {}
// fake device crate
extern crate core;
extern crate cortex_m;
mod device {
use cortex_m::peripheral::Peripheral;
pub const GPIOA: Peripheral<Gpioa> = unsafe { Peripheral::new(0x0) };
pub struct Gpioa;
pub mod interrupt {
use cortex_m::interrupt::Nr;
extern "C" fn default_handler<T>(_: T) {}
pub struct Handlers {
pub Exti0: extern "C" fn(Exti0),
}
pub struct Exti0;
pub enum Interrupt {
Exti0,
}
unsafe impl Nr for Interrupt {
fn nr(&self) -> u8 {
0
}
}
pub const DEFAULT_HANDLERS: Handlers =
Handlers { Exti0: default_handler };
}
}

View file

@ -1,74 +0,0 @@
// error-pattern: symbol `GPIOA` is already defined
#![feature(const_fn)]
#![feature(used)]
#[macro_use]
extern crate cortex_m_rtfm as rtfm;
use rtfm::{P0, P1, T0, TMax};
use device::interrupt::Exti0;
peripherals!(device, {
GPIOA: Peripheral {
register_block: Gpioa,
ceiling: C1,
},
});
mod foo {
// WRONG: peripheral alias
peripherals!(device, {
GPIOA: Peripheral {
register_block: Gpioa,
ceiling: C2,
},
});
}
tasks!(device, {});
fn init(_: P0, _: &TMax) {}
fn idle(_: P0, _: T0) -> ! {
loop {}
}
fn j1(_task: Exti0, _prio: P1) {}
// fake device crate
extern crate core;
extern crate cortex_m;
mod device {
use cortex_m::peripheral::Peripheral;
pub const GPIOA: Peripheral<Gpioa> = unsafe { Peripheral::new(0x0) };
pub struct Gpioa;
pub mod interrupt {
use cortex_m::interrupt::Nr;
extern "C" fn default_handler<T>(_: T) {}
pub struct Handlers {
pub Exti0: extern "C" fn(Exti0),
}
pub struct Exti0;
pub enum Interrupt {
Exti0,
}
unsafe impl Nr for Interrupt {
fn nr(&self) -> u8 {
0
}
}
pub const DEFAULT_HANDLERS: Handlers =
Handlers { Exti0: default_handler };
}
}

View file

@ -1,42 +0,0 @@
extern crate cortex_m_rtfm as rtfm;
use rtfm::{C2, P1, P3, Resource, T1, T3};
static R1: Resource<i32, C2> = Resource::new(0);
fn j1(prio: P1, thr: T1) {
thr.raise(
&R1, |thr| {
let r1 = R1.access(&prio, thr);
// `j2` preempts this critical section
rtfm::request(j2);
}
);
}
fn j2(_task: Task, prio: P3, thr: T3) {
rtfm::atomic(
|thr| {
// OK C2 (R1's ceiling) <= T16 (preemption threshold)
// BAD C2 (R1's ceiling) < P3 (j2's priority)
let r1 = R1.access(&prio, &thr);
//~^ error
},
);
}
// glue
extern crate cortex_m;
use cortex_m::ctxt::Context;
use cortex_m::interrupt::Nr;
struct Task;
unsafe impl Context for Task {}
unsafe impl Nr for Task {
fn nr(&self) -> u8 {
0
}
}

View file

@ -1,43 +0,0 @@
extern crate cortex_m_rtfm as rtfm;
use rtfm::{C2, C4, P1, P3, Resource, T1, T3};
static R1: Resource<i32, C2> = Resource::new(0);
static R2: Resource<i32, C4> = Resource::new(0);
fn j1(prio: P1, thr: T1) {
thr.raise(
&R1, |thr| {
let r1 = R1.access(&prio, thr);
// `j2` preempts this critical section
rtfm::request(j2);
}
);
}
fn j2(_task: Task, prio: P3, thr: T3) {
thr.raise(
&R2, |thr| {
// OK C2 (R1's ceiling) <= T4 (preemption threshold)
// BAD C2 (R1's ceiling) < P3 (j2's priority)
let r1 = R1.access(&prio, thr);
//~^ error
}
);
}
// glue
extern crate cortex_m;
use cortex_m::ctxt::Context;
use cortex_m::interrupt::Nr;
struct Task;
unsafe impl Context for Task {}
unsafe impl Nr for Task {
fn nr(&self) -> u8 {
0
}
}

View file

@ -1,24 +0,0 @@
extern crate cortex_m_rtfm as rtfm;
use rtfm::{C2, CMax, P1, P3, Resource, T1, T3};
static R1: Resource<i32, C2> = Resource::new(0);
// You CAN'T use `raise` to lower the preemption level
fn j1(prio: P3, thr: T3) {
thr.raise(&R1, |thr| {});
//~^ error
}
static R2: Resource<i32, CMax> = Resource::new(0);
// You CAN'T `raise` the preemption level to the maximum
fn j2(prio: P1, thr: T1) {
thr.raise(&R2, |thr| {});
//~^ error
// Instead use `rtfm::atomic` to access a resource with ceiling C16
rtfm::atomic(|thr| {
let r2 = R2.access(&prio, thr);
});
}

View file

@ -1,92 +0,0 @@
// error-pattern: expected struct `typenum::Equal`, found struct `typenum::Greater`
#![feature(used)]
#[macro_use]
extern crate cortex_m_rtfm as rtfm;
use rtfm::{P0, P1, T0, T1, TMax};
use device::interrupt::Exti0;
// WRONG: Tasks can't have a priority of 0.
// Only idle and init can have a priority of 0.
tasks!(device, {
j1: Task {
interrupt: Exti0,
priority: P0,
enabled: true,
},
});
fn init(_: P0, _: &TMax) {}
fn idle(_: P0, _: T0) -> ! {
loop {}
}
fn j1(_task: Exti0, _prio: P1, _thr: T1) {}
// fake device crate
extern crate core;
extern crate cortex_m;
mod device {
pub mod interrupt {
use cortex_m::ctxt::Context;
use cortex_m::interrupt::Nr;
extern "C" fn default_handler<T>(_: T) {}
pub struct Handlers {
pub Exti0: extern "C" fn(Exti0),
pub Exti1: extern "C" fn(Exti1),
pub Exti2: extern "C" fn(Exti2),
}
pub struct Exti0;
pub struct Exti1;
pub struct Exti2;
pub enum Interrupt {
Exti0,
Exti1,
Exti2,
}
unsafe impl Nr for Interrupt {
fn nr(&self) -> u8 {
0
}
}
unsafe impl Context for Exti0 {}
unsafe impl Nr for Exti0 {
fn nr(&self) -> u8 {
0
}
}
unsafe impl Context for Exti1 {}
unsafe impl Nr for Exti1 {
fn nr(&self) -> u8 {
0
}
}
unsafe impl Context for Exti2 {}
unsafe impl Nr for Exti2 {
fn nr(&self) -> u8 {
0
}
}
pub const DEFAULT_HANDLERS: Handlers = Handlers {
Exti0: default_handler,
Exti1: default_handler,
Exti2: default_handler,
};
}
}

View file

@ -1,98 +0,0 @@
// error-pattern: field `Exti0` specified more than once
#![feature(used)]
#[macro_use]
extern crate cortex_m_rtfm as rtfm;
use rtfm::{P0, P1, P2, T0, T1, T2, TMax};
use device::interrupt::{Exti0, Exti1};
// WRONG: Two tasks mapped to the same interrupt handler
tasks!(device, {
j1: Task {
interrupt: Exti0,
priority: P1,
enabled: true,
},
j2: Task {
interrupt: Exti0,
priority: P2,
enabled: true,
},
});
fn init(_: P0, _: &TMax) {}
fn idle(_: P0, _: T0) -> ! {
loop {}
}
fn j1(_task: Exti0, _prio: P1, _thr: T1) {}
fn j2(_task: Exti0, _prio: P2, _thr: T2) {}
// fake device crate
extern crate core;
extern crate cortex_m;
mod device {
pub mod interrupt {
use cortex_m::ctxt::Context;
use cortex_m::interrupt::Nr;
extern "C" fn default_handler<T>(_: T) {}
pub struct Handlers {
pub Exti0: extern "C" fn(Exti0),
pub Exti1: extern "C" fn(Exti1),
pub Exti2: extern "C" fn(Exti2),
}
pub struct Exti0;
pub struct Exti1;
pub struct Exti2;
pub enum Interrupt {
Exti0,
Exti1,
Exti2,
}
unsafe impl Nr for Interrupt {
fn nr(&self) -> u8 {
0
}
}
unsafe impl Context for Exti0 {}
unsafe impl Nr for Exti0 {
fn nr(&self) -> u8 {
0
}
}
unsafe impl Context for Exti1 {}
unsafe impl Nr for Exti1 {
fn nr(&self) -> u8 {
0
}
}
unsafe impl Context for Exti2 {}
unsafe impl Nr for Exti2 {
fn nr(&self) -> u8 {
0
}
}
pub const DEFAULT_HANDLERS: Handlers = Handlers {
Exti0: default_handler,
Exti1: default_handler,
Exti2: default_handler,
};
}
}

View file

@ -1,89 +0,0 @@
// error-pattern: mismatched types
#![feature(used)]
#[macro_use]
extern crate cortex_m_rtfm as rtfm;
use device::interrupt::Exti0;
use rtfm::{P0, P1, T0, T1, TMax};
tasks!(device, {
j1: Task {
interrupt: Exti0,
priority: P1,
enabled: true,
},
});
fn init(_: P0, _: &TMax) {}
// WRONG. `idle` must have signature `fn(P0, C0) -> !`
fn idle(_: P0, _: T0) {}
fn j1(_task: Exti0, _prio: P1, _thr: T1) {}
// fake device crate
extern crate core;
extern crate cortex_m;
mod device {
pub mod interrupt {
use cortex_m::ctxt::Context;
use cortex_m::interrupt::Nr;
extern "C" fn default_handler<T>(_: T) {}
pub struct Handlers {
pub Exti0: extern "C" fn(Exti0),
pub Exti1: extern "C" fn(Exti1),
pub Exti2: extern "C" fn(Exti2),
}
pub struct Exti0;
pub struct Exti1;
pub struct Exti2;
pub enum Interrupt {
Exti0,
Exti1,
Exti2,
}
unsafe impl Nr for Interrupt {
fn nr(&self) -> u8 {
0
}
}
unsafe impl Context for Exti0 {}
unsafe impl Nr for Exti0 {
fn nr(&self) -> u8 {
0
}
}
unsafe impl Context for Exti1 {}
unsafe impl Nr for Exti1 {
fn nr(&self) -> u8 {
0
}
}
unsafe impl Context for Exti2 {}
unsafe impl Nr for Exti2 {
fn nr(&self) -> u8 {
0
}
}
pub const DEFAULT_HANDLERS: Handlers = Handlers {
Exti0: default_handler,
Exti1: default_handler,
Exti2: default_handler,
};
}
}

View file

@ -1,91 +0,0 @@
// error-pattern: mismatched types
#![feature(used)]
#[macro_use]
extern crate cortex_m_rtfm as rtfm;
use rtfm::{P0, P1, T0, T1, TMax};
use device::interrupt::Exti0;
tasks!(device, {
j1: Task {
interrupt: Exti0,
priority: P1,
enabled: true,
},
});
// WRONG. `init` must have signature `fn(P0, &TMax)`
fn init(_: P0, _: &T1) {}
fn idle(_: P0, _: T0) -> ! {
loop {}
}
fn j1(_task: Exti0, _prio: P1, _thr: T1) {}
// fake device crate
extern crate core;
extern crate cortex_m;
mod device {
pub mod interrupt {
use cortex_m::ctxt::Context;
use cortex_m::interrupt::Nr;
extern "C" fn default_handler<T>(_: T) {}
pub struct Handlers {
pub Exti0: extern "C" fn(Exti0),
pub Exti1: extern "C" fn(Exti1),
pub Exti2: extern "C" fn(Exti2),
}
pub struct Exti0;
pub struct Exti1;
pub struct Exti2;
pub enum Interrupt {
Exti0,
Exti1,
Exti2,
}
unsafe impl Nr for Interrupt {
fn nr(&self) -> u8 {
0
}
}
unsafe impl Context for Exti0 {}
unsafe impl Nr for Exti0 {
fn nr(&self) -> u8 {
0
}
}
unsafe impl Context for Exti1 {}
unsafe impl Nr for Exti1 {
fn nr(&self) -> u8 {
0
}
}
unsafe impl Context for Exti2 {}
unsafe impl Nr for Exti2 {
fn nr(&self) -> u8 {
0
}
}
pub const DEFAULT_HANDLERS: Handlers = Handlers {
Exti0: default_handler,
Exti1: default_handler,
Exti2: default_handler,
};
}
}

View file

@ -1,91 +0,0 @@
// error-pattern: mismatched types
#![feature(used)]
#[macro_use]
extern crate cortex_m_rtfm as rtfm;
use device::interrupt::Exti0;
use rtfm::{P0, P1, P2, T0, T1, T2, TMax};
tasks!(device, {
j1: Task {
interrupt: Exti0,
priority: P1,
enabled: true,
},
});
fn init(_: P0, _: &TMax) {}
fn idle(_: P0, _: T0) -> ! {
loop {}
}
// Wrong priority token. Declared P1, got P2
fn j1(_task: Exti0, _prio: P2, _thr: T2) {}
// fake device crate
extern crate core;
extern crate cortex_m;
mod device {
pub mod interrupt {
use cortex_m::ctxt::Context;
use cortex_m::interrupt::Nr;
extern "C" fn default_handler<T>(_: T) {}
pub struct Handlers {
pub Exti0: extern "C" fn(Exti0),
pub Exti1: extern "C" fn(Exti1),
pub Exti2: extern "C" fn(Exti2),
}
pub struct Exti0;
pub struct Exti1;
pub struct Exti2;
pub enum Interrupt {
Exti0,
Exti1,
Exti2,
}
unsafe impl Nr for Interrupt {
fn nr(&self) -> u8 {
0
}
}
unsafe impl Context for Exti0 {}
unsafe impl Nr for Exti0 {
fn nr(&self) -> u8 {
0
}
}
unsafe impl Context for Exti1 {}
unsafe impl Nr for Exti1 {
fn nr(&self) -> u8 {
0
}
}
unsafe impl Context for Exti2 {}
unsafe impl Nr for Exti2 {
fn nr(&self) -> u8 {
0
}
}
pub const DEFAULT_HANDLERS: Handlers = Handlers {
Exti0: default_handler,
Exti1: default_handler,
Exti2: default_handler,
};
}
}

View file

@ -1,91 +0,0 @@
// error-pattern: mismatched types
#![feature(used)]
#[macro_use]
extern crate cortex_m_rtfm as rtfm;
use device::interrupt::{Exti0, Exti1};
use rtfm::{P0, P1, T0, T1, TMax};
tasks!(device, {
j1: Task {
interrupt: Exti0,
priority: P1,
enabled: true,
},
});
fn init(_: P0, _: &TMax) {}
fn idle(_: P0, _: T0) -> ! {
loop {}
}
// Wrong task token. Declared Exti0, got Exti1
fn j1(_task: Exti1, _prio: P1, _thr: T1) {}
// fake device crate
extern crate core;
extern crate cortex_m;
mod device {
pub mod interrupt {
use cortex_m::ctxt::Context;
use cortex_m::interrupt::Nr;
extern "C" fn default_handler<T>(_: T) {}
pub struct Handlers {
pub Exti0: extern "C" fn(Exti0),
pub Exti1: extern "C" fn(Exti1),
pub Exti2: extern "C" fn(Exti2),
}
pub struct Exti0;
pub struct Exti1;
pub struct Exti2;
pub enum Interrupt {
Exti0,
Exti1,
Exti2,
}
unsafe impl Nr for Interrupt {
fn nr(&self) -> u8 {
0
}
}
unsafe impl Context for Exti0 {}
unsafe impl Nr for Exti0 {
fn nr(&self) -> u8 {
0
}
}
unsafe impl Context for Exti1 {}
unsafe impl Nr for Exti1 {
fn nr(&self) -> u8 {
0
}
}
unsafe impl Context for Exti2 {}
unsafe impl Nr for Exti2 {
fn nr(&self) -> u8 {
0
}
}
pub const DEFAULT_HANDLERS: Handlers = Handlers {
Exti0: default_handler,
Exti1: default_handler,
Exti2: default_handler,
};
}
}

View file

@ -1,91 +0,0 @@
// error-pattern: mismatched types
#![feature(used)]
#[macro_use]
extern crate cortex_m_rtfm as rtfm;
use rtfm::{C2, P0, P1, T0, T2, TMax};
use device::interrupt::Exti0;
tasks!(device, {
j1: Task {
interrupt: Exti0,
priority: P1,
enabled: true,
},
});
fn init(_: P0, _: &TMax) {}
fn idle(_: P0, _: T0) -> ! {
loop {}
}
// Wrong ceiling token. `prio` and `thr` must match in levels
fn j1(_task: Exti0, _prio: P1, _thr: T2) {}
// fake device crate
extern crate core;
extern crate cortex_m;
mod device {
pub mod interrupt {
use cortex_m::ctxt::Context;
use cortex_m::interrupt::Nr;
extern "C" fn default_handler<T>(_: T) {}
pub struct Handlers {
pub Exti0: extern "C" fn(Exti0),
pub Exti1: extern "C" fn(Exti1),
pub Exti2: extern "C" fn(Exti2),
}
pub struct Exti0;
pub struct Exti1;
pub struct Exti2;
pub enum Interrupt {
Exti0,
Exti1,
Exti2,
}
unsafe impl Nr for Interrupt {
fn nr(&self) -> u8 {
0
}
}
unsafe impl Context for Exti0 {}
unsafe impl Nr for Exti0 {
fn nr(&self) -> u8 {
0
}
}
unsafe impl Context for Exti1 {}
unsafe impl Nr for Exti1 {
fn nr(&self) -> u8 {
0
}
}
unsafe impl Context for Exti2 {}
unsafe impl Nr for Exti2 {
fn nr(&self) -> u8 {
0
}
}
pub const DEFAULT_HANDLERS: Handlers = Handlers {
Exti0: default_handler,
Exti1: default_handler,
Exti2: default_handler,
};
}
}

View file

@ -1,159 +0,0 @@
#![feature(const_fn)]
#![feature(optin_builtin_traits)]
#![feature(used)]
#[macro_use]
extern crate cortex_m_rtfm as rtfm;
use core::cell::RefCell;
use rtfm::{C2, Local, P0, P1, P2, Resource, T0, T1, T2, TMax};
use device::interrupt::{Exti0, Exti1};
tasks!(device, {
t1: Task {
interrupt: Exti0,
priority: P1,
enabled: true,
},
t2: Task {
interrupt: Exti1,
priority: P2,
enabled: true,
},
});
fn init(_: P0, _: &TMax) {}
fn idle(_: P0, _: T0) -> ! {
rtfm::request(t1);
rtfm::request(t1);
loop {}
}
static CHANNEL: Resource<RefCell<Option<Exti0>>, C2> = {
//~^ error: Send
Resource::new(RefCell::new(None))
};
static LOCAL: Local<i32, Exti0> = Local::new(0);
fn t1(mut task: Exti0, ref priority: P1, ref threshold: T1) {
// First run
static FIRST: Local<bool, Exti0> = Local::new(true);
let first = *FIRST.borrow(&task);
if first {
// toggle
*FIRST.borrow_mut(&mut task) = false;
}
if first {
threshold.raise(
&CHANNEL, move |threshold| {
let channel = CHANNEL.access(priority, threshold);
// BAD: give up task token
*channel.borrow_mut() = Some(task);
}
);
return;
}
let _local = LOCAL.borrow_mut(&mut task);
// ..
// `t2` will preempt `t1`
rtfm::request(t2);
// ..
// `LOCAL` mutably borrowed up to this point
}
fn t2(_task: Exti1, ref priority: P2, ref threshold: T2) {
let channel = CHANNEL.access(priority, threshold);
let mut channel = channel.borrow_mut();
if let Some(mut other_task) = channel.take() {
// BAD: `t2` has access to `t1`'s task token
// so it can now mutably access local while `t1` is also using it
let _local = LOCAL.borrow_mut(&mut other_task);
}
}
// fake device crate
extern crate core;
extern crate cortex_m;
mod device {
pub mod interrupt {
use cortex_m::ctxt::Context;
use cortex_m::interrupt::Nr;
extern "C" fn default_handler<T>(_: T) {}
pub struct Handlers {
pub Exti0: extern "C" fn(Exti0),
pub Exti1: extern "C" fn(Exti1),
pub Exti2: extern "C" fn(Exti2),
}
pub struct Exti0;
pub struct Exti1;
pub struct Exti2;
pub enum Interrupt {
Exti0,
Exti1,
Exti2,
}
unsafe impl Nr for Interrupt {
fn nr(&self) -> u8 {
0
}
}
unsafe impl Context for Exti0 {}
unsafe impl Nr for Exti0 {
fn nr(&self) -> u8 {
0
}
}
impl !Send for Exti0 {}
unsafe impl Context for Exti1 {}
unsafe impl Nr for Exti1 {
fn nr(&self) -> u8 {
0
}
}
impl !Send for Exti1 {}
unsafe impl Context for Exti2 {}
unsafe impl Nr for Exti2 {
fn nr(&self) -> u8 {
0
}
}
impl !Send for Exti2 {}
pub const DEFAULT_HANDLERS: Handlers = Handlers {
Exti0: default_handler,
Exti1: default_handler,
Exti2: default_handler,
};
}
}