mirror of
https://github.com/rtic-rs/rtic.git
synced 2024-11-25 21:19:35 +01:00
rtfm! macro take 2
This commit is contained in:
parent
2bf5401439
commit
86a360a396
27 changed files with 1367 additions and 2237 deletions
22
Cargo.toml
22
Cargo.toml
|
@ -10,24 +10,8 @@ keywords = ["arm", "cortex-m"]
|
|||
license = "MIT OR Apache-2.0"
|
||||
name = "cortex-m-rtfm"
|
||||
repository = "https://github.com/japaric/cortex-m-rtfm"
|
||||
version = "0.1.1"
|
||||
|
||||
[build-dependencies]
|
||||
quote = "0.3.15"
|
||||
syn = "0.11.10"
|
||||
version = "0.2.0"
|
||||
|
||||
[dependencies]
|
||||
cortex-m = "0.2.6"
|
||||
static-ref = "0.1.0"
|
||||
typenum = "1.7.0"
|
||||
|
||||
[dev-dependencies]
|
||||
compiletest_rs = "0.2.5"
|
||||
|
||||
[features]
|
||||
# Number of priority bits
|
||||
P2 = []
|
||||
P3 = []
|
||||
P4 = []
|
||||
P5 = []
|
||||
default = ["P4"]
|
||||
cortex-m = "0.3.0"
|
||||
static-ref = "0.2.0"
|
130
build.rs
130
build.rs
|
@ -1,137 +1,11 @@
|
|||
#[macro_use]
|
||||
extern crate quote;
|
||||
extern crate syn;
|
||||
|
||||
use std::env;
|
||||
use std::fs::File;
|
||||
use std::io::Write;
|
||||
use std::path::PathBuf;
|
||||
|
||||
use syn::{Ident, IntTy, Lit};
|
||||
|
||||
fn main() {
|
||||
let target = env::var("TARGET").unwrap();
|
||||
|
||||
if target.starts_with("thumbv6m") {
|
||||
println!("cargo:rustc-cfg=thumbv6m");
|
||||
if target.starts_with("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");
|
||||
}
|
||||
|
|
11
macros/Cargo.toml
Normal file
11
macros/Cargo.toml
Normal 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
17
macros/src/check.rs
Normal 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
63
macros/src/lib.rs
Normal 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
52
macros/src/syntax/mod.rs
Normal 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
496
macros/src/syntax/parse.rs
Normal 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
486
macros/src/trans.rs
Normal 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
48
macros/src/util.rs
Normal 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
|
||||
}
|
1110
src/lib.rs
1110
src/lib.rs
File diff suppressed because it is too large
Load diff
|
@ -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);
|
||||
}
|
|
@ -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);
|
||||
}
|
||||
);
|
||||
}
|
|
@ -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);
|
||||
}
|
|
@ -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);
|
||||
}
|
|
@ -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 };
|
||||
}
|
||||
}
|
|
@ -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 };
|
||||
}
|
||||
}
|
|
@ -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
|
||||
}
|
||||
}
|
|
@ -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
|
||||
}
|
||||
}
|
|
@ -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);
|
||||
});
|
||||
}
|
|
@ -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,
|
||||
};
|
||||
}
|
||||
}
|
|
@ -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,
|
||||
};
|
||||
}
|
||||
}
|
|
@ -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,
|
||||
};
|
||||
}
|
||||
}
|
|
@ -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,
|
||||
};
|
||||
}
|
||||
}
|
|
@ -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,
|
||||
};
|
||||
}
|
||||
}
|
|
@ -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,
|
||||
};
|
||||
}
|
||||
}
|
|
@ -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,
|
||||
};
|
||||
}
|
||||
}
|
|
@ -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,
|
||||
};
|
||||
}
|
||||
}
|
Loading…
Reference in a new issue