aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Cargo.toml22
-rw-r--r--build.rs130
-rw-r--r--macros/Cargo.toml11
-rw-r--r--macros/src/check.rs17
-rw-r--r--macros/src/lib.rs63
-rw-r--r--macros/src/syntax/mod.rs52
-rw-r--r--macros/src/syntax/parse.rs496
-rw-r--r--macros/src/trans.rs486
-rw-r--r--macros/src/util.rs48
-rw-r--r--src/lib.rs1072
-rw-r--r--tests/cfail.rs16
-rw-r--r--tests/cfail/access.rs35
-rw-r--r--tests/cfail/ceiling.rs29
-rw-r--r--tests/cfail/lock.rs36
-rw-r--r--tests/cfail/peripherals-alias-1.rs68
-rw-r--r--tests/cfail/peripherals-alias-2.rs74
-rw-r--r--tests/cfail/race-1.rs42
-rw-r--r--tests/cfail/race-2.rs43
-rw-r--r--tests/cfail/raise.rs24
-rw-r--r--tests/cfail/tasks-p0.rs92
-rw-r--r--tests/cfail/tasks-same-handler.rs98
-rw-r--r--tests/cfail/tasks-wrong-idle.rs89
-rw-r--r--tests/cfail/tasks-wrong-init.rs91
-rw-r--r--tests/cfail/tasks-wrong-priority.rs91
-rw-r--r--tests/cfail/tasks-wrong-task.rs91
-rw-r--r--tests/cfail/tasks-wrong-threshold.rs91
-rw-r--r--tests/cfail/token-transfer.rs159
27 files changed, 1348 insertions, 2218 deletions
diff --git a/Cargo.toml b/Cargo.toml
index e2cd37f3..f37449d7 100644
--- a/Cargo.toml
+++ b/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" \ No newline at end of file
diff --git a/build.rs b/build.rs
index 19b12170..ebfee06c 100644
--- a/build.rs
+++ b/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");
- }
-
- 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>;
- },
- );
+ if target.starts_with("thumbv6m-") {
+ println!("cargo:rustc-cfg=armv6m");
}
- // 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");
}
diff --git a/macros/Cargo.toml b/macros/Cargo.toml
new file mode 100644
index 00000000..0e255e38
--- /dev/null
+++ b/macros/Cargo.toml
@@ -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
diff --git a/macros/src/check.rs b/macros/src/check.rs
new file mode 100644
index 00000000..ddd9abc4
--- /dev/null
+++ b/macros/src/check.rs
@@ -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)
+ }
+ }
+}
diff --git a/macros/src/lib.rs b/macros/src/lib.rs
new file mode 100644
index 00000000..05210fbb
--- /dev/null
+++ b/macros/src/lib.rs
@@ -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)),
+ );
+}
diff --git a/macros/src/syntax/mod.rs b/macros/src/syntax/mod.rs
new file mode 100644
index 00000000..c856617e
--- /dev/null
+++ b/macros/src/syntax/mod.rs
@@ -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>;
diff --git a/macros/src/syntax/parse.rs b/macros/src/syntax/parse.rs
new file mode 100644
index 00000000..e6d3f461
--- /dev/null
+++ b/macros/src/syntax/parse.rs
@@ -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,
+ }
+}
diff --git a/macros/src/trans.rs b/macros/src/trans.rs
new file mode 100644
index 00000000..6cf3a517
--- /dev/null
+++ b/macros/src/trans.rs
@@ -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)*
+ });
+}
diff --git a/macros/src/util.rs b/macros/src/util.rs
new file mode 100644
index 00000000..45f1feef
--- /dev/null
+++ b/macros/src/util.rs
@@ -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
+}
diff --git a/src/lib.rs b/src/lib.rs
index e325d27f..f5c8b992 100644
--- a/src/lib.rs
+++ b/src/lib.rs
@@ -1,422 +1,3 @@
-//! Real Time For the Masses (RTFM), a framework for building concurrent
-//! applications, for ARM Cortex-M microcontrollers
-//!
-//! This crate is based on [the RTFM framework] created by the Embedded Systems
-//! group at [Luleå University of Technology][ltu], led by Prof. Per Lindgren,
-//! and uses a simplified version of the Stack Resource Policy as scheduling
-//! policy (check the [references] for details).
-//!
-//! [the RTFM framework]: http://www.rtfm-lang.org/
-//! [ltu]: https://www.ltu.se/?l=en
-//! [per]: https://www.ltu.se/staff/p/pln-1.11258?l=en
-//! [references]: ./index.html#references
-//!
-//! # Features
-//!
-//! - **Event triggered tasks** as the unit of concurrency.
-//! - Support for prioritization of tasks and, thus, **preemptive
-//! multitasking**.
-//! - **Efficient and data race free memory sharing** through fine grained *non
-//! global* critical sections.
-//! - **Deadlock free execution** guaranteed at compile time.
-//! - **Minimal scheduling overhead** as the scheduler has no "software
-//! component": the hardware does all the scheduling.
-//! - **Highly efficient memory usage**: All the tasks share a single call stack
-//! and there's no hard dependency on a dynamic memory allocator.
-//! - **All Cortex M3, M4 and M7 devices are fully supported**. M0(+) is
-//! partially supported as the whole API is not available due to missing
-//! hardware features.
-//! - The number of task priority levels is configurable at compile time through
-//! the `P2` (4 levels), `P3` (8 levels), etc. Cargo features. The number of
-//! priority levels supported by the hardware is device specific but this
-//! crate defaults to 16 as that's the most common scenario.
-//! - This task model is amenable to known WCET (Worst Case Execution Time)
-//! analysis and scheduling analysis techniques. (Though we haven't yet
-//! developed Rust friendly tooling for that.)
-//!
-//! # Requirements
-//!
-//! - Tasks must run to completion. That's it, tasks can't contain endless
-//! loops.
-//! - Task priorities must remain constant at runtime.
-//!
-//! # Dependencies
-//!
-//! - A device crate generated using [`svd2rust`] v0.7.x
-//! - A `start` lang time: Vanilla `main` must be supported in binary crates.
-//! You can use the [`cortex-m-rt`] crate to fulfill the requirement
-//!
-//! [`svd2rust`]: https://docs.rs/svd2rust/0.7.0/svd2rust/
-//! [`cortex-m-rt`]: https://docs.rs/cortex-m-rt/0.1.1/cortex_m_rt/
-//!
-//! # Examples
-//!
-//! Ordered in increasing level of complexity:
-//!
-//! - [Zero tasks](./index.html#zero-tasks)
-//! - [One task](./index.html#one-task)
-//! - [Two "serial" tasks](./index.html#two-serial-tasks)
-//! - [Preemptive multitasking](./index.html#preemptive-multitasking)
-//! - [Peripherals as resources](./index.html#peripherals-as-resources)
-//!
-//! ## Zero tasks
-//!
-//! ``` ignore
-//! #![feature(used)]
-//! #![no_std]
-//!
-//! #[macro_use] // for the `hprintln!` macro
-//! extern crate cortex_m;
-//!
-//! // before main initialization + `start` lang item
-//! extern crate cortex_m_rt;
-//!
-//! #[macro_use] // for the `tasks!` macro
-//! extern crate cortex_m_rtfm as rtfm;
-//!
-//! // device crate generated using svd2rust
-//! extern crate stm32f30x;
-//!
-//! use rtfm::{P0, T0, TMax};
-//!
-//! // TASKS (None in this example)
-//! tasks!(stm32f30x, {});
-//!
-//! // INITIALIZATION PHASE
-//! fn init(_priority: P0, _threshold: &TMax) {
-//! hprintln!("INIT");
-//! }
-//!
-//! // IDLE LOOP
-//! fn idle(_priority: P0, _threshold: T0) -> ! {
-//! hprintln!("IDLE");
-//!
-//! // Sleep
-//! loop {
-//! rtfm::wfi();
-//! }
-//! }
-//! ```
-//!
-//! Expected output:
-//!
-//! ``` text
-//! INIT
-//! IDLE
-//! ```
-//!
-//! The `tasks!` macro overrides the `main` function and imposes the following
-//! structure into your program:
-//!
-//! - `init`, the initialization phase, runs first. This function is executed
-//! "atomically", in the sense that no task / interrupt can preempt it.
-//!
-//! - `idle`, a never ending function that runs after `init`.
-//!
-//! Both `init` and `idle` have a priority of 0, the lowest priority. In RTFM,
-//! a higher priority value means more urgent.
-//!
-//! # One task
-//!
-//! ``` ignore
-//! #![feature(const_fn)]
-//! #![feature(used)]
-//! #![no_std]
-//!
-//! extern crate cortex_m_rt;
-//! #[macro_use]
-//! extern crate cortex_m_rtfm as rtfm;
-//! extern crate stm32f30x;
-//!
-//! use stm32f30x::interrupt::Tim7;
-//! use rtfm::{Local, P0, P1, T0, T1, TMax};
-//!
-//! // INITIALIZATION PHASE
-//! fn init(_priority: P0, _threshold: &TMax) {
-//! // Configure TIM7 for periodic interrupts
-//! // Configure GPIO for LED driving
-//! }
-//!
-//! // IDLE LOOP
-//! fn idle(_priority: P0, _threshold: T0) -> ! {
-//! // Sleep
-//! loop {
-//! rtfm::wfi();
-//! }
-//! }
-//!
-//! // TASKS
-//! tasks!(stm32f30x, {
-//! periodic: Task {
-//! interrupt: Tim7,
-//! priority: P1,
-//! enabled: true,
-//! },
-//! });
-//!
-//! fn periodic(mut task: Tim7, _priority: P1, _threshold: T1) {
-//! // Task local data
-//! static STATE: Local<bool, Tim7> = Local::new(false);
-//!
-//! let state = STATE.borrow_mut(&mut task);
-//!
-//! // Toggle state
-//! *state = !*state;
-//!
-//! // Blink an LED
-//! if *state {
-//! LED.on();
-//! } else {
-//! LED.off();
-//! }
-//! }
-//! ```
-//!
-//! Here we define a task named `periodic` and bind it to the `Tim7`
-//! interrupt. The `periodic` task will run every time the `Tim7` interrupt
-//! is triggered. We assign to this task a priority of 1 (`P1`); this is the
-//! lowest priority that a task can have.
-//!
-//! We use the [`Local`](./struct.Local.html) abstraction to add state to the
-//! task; this task local data will be preserved across runs of the `periodic`
-//! task. Note that `STATE` is owned by the `periodic` task, in the sense that
-//! no other task can access it; this is reflected in its type signature (the
-//! `Tim7` type parameter).
-//!
-//! # Two "serial" tasks
-//!
-//! ``` ignore
-//! #![feature(const_fn)]
-//! #![feature(used)]
-//! #![no_std]
-//!
-//! extern crate cortex_m_rt;
-//! #[macro_use]
-//! extern crate cortex_m_rtfm as rtfm;
-//! extern crate stm32f30x;
-//!
-//! use core::cell::Cell;
-//!
-//! use stm32f30x::interrupt::{Tim6Dacunder, Tim7};
-//! use rtfm::{C1, P0, P1, Resource, T0, T1, TMax};
-//!
-//! tasks!(stm32f30x, {
-//! t1: Task {
-//! interrupt: Tim6Dacunder,
-//! priority: P1,
-//! enabled: true,
-//! },
-//! t2: Task {
-//! interrupt: Tim7,
-//! priority: P1,
-//! enabled: true,
-//! },
-//! });
-//!
-//! // Data shared between tasks `t1` and `t2`
-//! static COUNTER: Resource<Cell<u32>, C1> = Resource::new(Cell::new(0));
-//!
-//! fn init(priority: P0, threshold: &TMax) {
-//! // ..
-//! }
-//!
-//! fn idle(priority: P0, threshold: T0) -> ! {
-//! // Sleep
-//! loop {
-//! rtfm::wfi();
-//! }
-//! }
-//!
-//! fn t1(_task: Tim6Dacunder, priority: P1, threshold: T1) {
-//! let counter = COUNTER.access(&priority, &threshold);
-//!
-//! counter.set(counter.get() + 1);
-//! }
-//!
-//! fn t2(_task: Tim7, priority: P1, threshold: T1) {
-//! let counter = COUNTER.access(&priority, &threshold);
-//!
-//! counter.set(counter.get() + 2);
-//! }
-//! ```
-//!
-//! Here we declare two tasks, `t1` and `t2`; both with a priority of 1 (`P1`).
-//! As both tasks have the same priority, we say that they are *serial* tasks in
-//! the sense that `t1` can only run *after* `t2` is done and vice versa; i.e.
-//! no preemption between them is possible.
-//!
-//! To share data between these two tasks, we use the
-//! [`Resource`](./struct.Resource.html) abstraction. As the tasks can't preempt
-//! each other, they can access the `COUNTER` resource using the zero cost
-//! [`access`](./struct.Resource.html#method.access) method -- no
-//! synchronization is required.
-//!
-//! `COUNTER` has an extra type parameter: `C1`. This is the *ceiling* of the
-//! resource. For now suffices to say that the ceiling must be the maximum of
-//! the priorities of all the tasks that access the resource -- in this case,
-//! `C1 == max(P1, P1)`. If you try a smaller value like `C0`, you'll find out
-//! that your program doesn't compile.
-//!
-//! # Preemptive multitasking
-//!
-//! ``` ignore
-//! #![feature(const_fn)]
-//! #![feature(used)]
-//! #![no_std]
-//!
-//! extern crate cortex_m_rt;
-//! #[macro_use]
-//! extern crate cortex_m_rtfm as rtfm;
-//! extern crate stm32f30x;
-//!
-//! use core::cell::Cell;
-//!
-//! use stm32f30x::interrupt::{Tim6Dacunder, Tim7};
-//! use rtfm::{C2, P0, P1, P2, Resource, T0, T1, T2, TMax};
-//!
-//! tasks!(stm32f30x, {
-//! t1: Task {
-//! interrupt: Tim6Dacunder,
-//! priority: P1,
-//! enabled: true,
-//! },
-//! t2: Task {
-//! interrupt: Tim7,
-//! priority: P2,
-//! enabled: true,
-//! },
-//! });
-//!
-//! static COUNTER: Resource<Cell<u32>, C2> = Resource::new(Cell::new(0));
-//!
-//! fn init(priority: P0, threshold: &TMax) {
-//! // ..
-//! }
-//!
-//! fn idle(priority: P0, threshold: T0) -> ! {
-//! // Sleep
-//! loop {
-//! rtfm::wfi();
-//! }
-//! }
-//!
-//! fn t1(_task: Tim6Dacunder, priority: P1, threshold: T1) {
-//! // ..
-//!
-//! threshold.raise(
-//! &COUNTER, |threshold: &T2| {
-//! let counter = COUNTER.access(&priority, threshold);
-//!
-//! counter.set(counter.get() + 1);
-//! }
-//! );
-//!
-//! // ..
-//! }
-//!
-//! fn t2(_task: Tim7, priority: P2, threshold: T2) {
-//! let counter = COUNTER.access(&priority, &threshold);
-//!
-//! counter.set(counter.get() + 2);
-//! }
-//! ```
-//!
-//! Now we have a variation of the previous example. Like before, `t1` has a
-//! priority of 1 (`P1`) but `t2` now has a priority of 2 (`P2`). This means
-//! that `t2` can preempt `t1` if a `Tim7` interrupt occurs while `t1` is
-//! being executed.
-//!
-//! To avoid data races, `t1` must modify `COUNTER` in an atomic way; i.e. `t2`
-//! most not preempt `t1` while `COUNTER` is being modified. This is
-//! accomplished by [`raise`](./struct.C.html#method.raise)-ing the preemption
-//! `threshold`. This creates a critical section, denoted by a closure; for
-//! whose execution, `COUNTER` is accessible while `t2` is prevented from
-//! preempting `t1`.
-//!
-//! How `t2` accesses `COUNTER` remains unchanged. Since `t1` can't preempt `t2`
-//! due to the differences in priority; no critical section is needed in `t2`.
-//!
-//! Note that the ceiling of `COUNTER` had to be changed to `C2`. This is
-//! required because the ceiling must be the maximum between `P1` and `P2`.
-//!
-//! Finally, it should be noted that the critical section in `t1` will only
-//! block tasks with a priority of 2 or lower. This is exactly what the
-//! preemption threshold represents: it's the "bar" that a task priority must
-//! pass in order to be able to preempt the current task / critical section.
-//! Note that a task with a priority of e.g. 3 (`P3`) effectively imposes a
-//! threshold of 3 (`C3`) because only a task with a priority of 4 or greater
-//! can preempt it.
-//!
-//! # Peripherals as resources
-//!
-//! ``` ignore
-//! #![feature(const_fn)]
-//! #![feature(used)]
-//! #![no_std]
-//!
-//! extern crate cortex_m_rt;
-//! #[macro_use]
-//! extern crate cortex_m_rtfm as rtfm;
-//! extern crate stm32f30x;
-//!
-//! use rtfm::{P0, Peripheral, T0, TMax};
-//!
-//! peripherals!(stm32f30x, {
-//! GPIOA: Peripheral {
-//! register_block: Gpioa,
-//! ceiling: C0,
-//! },
-//! RCC: Peripheral {
-//! register_block: Rcc,
-//! ceiling: C0,
-//! },
-//! });
-//!
-//! tasks!(stm32f30x, {});
-//!
-//! fn init(priority: P0, threshold: &TMax) {
-//! let gpioa = GPIOA.access(&priority, threshold);
-//! let rcc = RCC.access(&priority, threshold);
-//!
-//! // ..
-//! }
-//!
-//! fn idle(_priority: P0, _threshold: T0) -> ! {
-//! // Sleep
-//! loop {
-//! rtfm::wfi();
-//! }
-//! }
-//! ```
-//!
-//! Peripherals are global resources too and as such they can be protected in
-//! the same way as `Resource`s using the
-//! [`Peripheral`](./struct.Peripheral.html) abstraction.
-//!
-//! `Peripheral` and `Resource` has pretty much the same API except that
-//! `Peripheral` instances must be declared using the
-//! [`peripherals!`](./macro.peripherals.html) macro.
-//!
-//! # References
-//!
-//! - Baker, T. P. (1991). Stack-based scheduling of realtime processes.
-//! *Real-Time Systems*, 3(1), 67-99.
-//!
-//! > The original Stack Resource Policy paper. [PDF][srp].
-//!
-//! [srp]: http://www.cs.fsu.edu/~baker/papers/mstacks3.pdf
-//!
-//! - Eriksson, J., Häggström, F., Aittamaa, S., Kruglyak, A., & Lindgren, P.
-//! (2013, June). Real-time for the masses, step 1: Programming API and static
-//! priority SRP kernel primitives. In Industrial Embedded Systems (SIES),
-//! 2013 8th IEEE International Symposium on (pp. 110-113). IEEE.
-//!
-//! > A description of the RTFM task and resource model. [PDF][rtfm]
-//!
-//! [rtfm]: http://www.diva-portal.org/smash/get/diva2:1005680/FULLTEXT01.pdf
-
-#![deny(missing_docs)]
-#![deny(warnings)]
#![feature(asm)]
#![feature(const_fn)]
#![feature(optin_builtin_traits)]
@@ -424,550 +5,237 @@
extern crate cortex_m;
extern crate static_ref;
-extern crate typenum;
use core::cell::UnsafeCell;
-use core::marker::PhantomData;
-use core::ptr;
-
-use cortex_m::ctxt::Context;
-use cortex_m::interrupt::Nr;
-#[cfg(not(thumbv6m))]
-use cortex_m::register::{basepri, basepri_max};
-use static_ref::Ref;
-use typenum::{Cmp, Greater, U0, Unsigned};
-#[cfg(not(thumbv6m))]
-use typenum::Less;
pub use cortex_m::asm::{bkpt, wfi};
+pub use cortex_m::interrupt::CriticalSection;
+pub use cortex_m::interrupt::free as atomic;
+pub use static_ref::Static;
+#[cfg(not(armv6m))]
+use cortex_m::register::{basepri_max, basepri};
-#[doc(hidden)]
-pub use cortex_m::peripheral::NVIC as _NVIC;
-
-/// Compiler barrier
-#[cfg(not(thumbv6m))]
+#[cfg(not(armv6m))]
macro_rules! barrier {
() => {
- asm!(""
- :
- :
- : "memory"
- : "volatile");
- }
-}
-
-/// Task local data
-///
-/// This data can only be accessed by the task `T`
-pub struct Local<D, T> {
- _task: PhantomData<T>,
- data: UnsafeCell<D>,
-}
-
-impl<T, TASK> Local<T, TASK> {
- /// Creates a task local variable with some initial `value`
- pub const fn new(value: T) -> Self {
- Local {
- _task: PhantomData,
- data: UnsafeCell::new(value),
- }
- }
-
- /// Borrows the task local data for the duration of the task
- pub fn borrow<'task>(&'static self, _task: &'task TASK) -> &'task T {
- unsafe { &*self.data.get() }
- }
-
- /// Mutably borrows the task local data for the duration of the task
- pub fn borrow_mut<'task>(
- &'static self,
- _task: &'task mut TASK,
- ) -> &'task mut T {
- unsafe { &mut *self.data.get() }
+ asm!("" ::: "memory" : "volatile");
}
}
-unsafe impl<T, TASK> Sync for Local<T, TASK> {}
-
-/// A resource with ceiling `C`
-///
-/// A resource is used to share memory between two or more tasks
-pub struct Resource<T, C> {
- _ceiling: PhantomData<C>,
- data: UnsafeCell<T>,
-}
-
-impl<T, RC> Resource<T, RC>
+#[inline(always)]
+unsafe fn claim<T, U, R, F, G>(
+ data: T,
+ ceiling: u8,
+ nvic_prio_bits: u8,
+ t: &mut Threshold,
+ f: F,
+ g: G,
+) -> R
where
- RC: GreaterThanOrEqual<U0>,
- RC: LessThanOrEqual<UMax>,
+ F: FnOnce(U, &mut Threshold) -> R,
+ G: FnOnce(T) -> U,
{
- /// Creates a new resource
- pub const fn new(data: T) -> Self {
- Resource {
- _ceiling: PhantomData,
- data: UnsafeCell::new(data),
+ let max_priority = 1 << nvic_prio_bits;
+ if ceiling > t.0 {
+ match () {
+ #[cfg(armv6m)]
+ () => {
+ atomic(|_| f(g(data), &mut Threshold::new(max_priority)))
+ }
+ #[cfg(not(armv6m))]
+ () => {
+ if ceiling == max_priority {
+ atomic(|_| f(g(data), &mut Threshold::new(max_priority)))
+ } else {
+ let old = basepri::read();
+ let hw = (max_priority - ceiling) << (8 - nvic_prio_bits);
+ basepri_max::write(hw);
+ barrier!();
+ let ret = f(g(data), &mut Threshold(ceiling));
+ barrier!();
+ basepri::write(old);
+ ret
+ }
+ }
}
+ } else {
+ f(g(data), t)
}
}
-impl<T, RC> Resource<T, RC> {
- /// Grants data race free and deadlock free access to the resource data
- ///
- /// This operation is zero cost and doesn't impose any additional blocking.
- ///
- /// # Requirements
- ///
- /// To access the resource data these conditions must be met:
- ///
- /// - The resource ceiling must be greater than or equal to the task
- /// priority
- /// - The preemption threshold must be greater than or equal to the resource
- /// ceiling
- pub fn access<'cs, TP, PT>(
- &'static self,
- _task_priority: &Priority<TP>,
- _preemption_threshold: &'cs Threshold<PT>,
- ) -> Ref<'cs, T>
- where
- RC: GreaterThanOrEqual<TP>,
- PT: GreaterThanOrEqual<RC>,
- {
- unsafe { Ref::new(&*self.data.get()) }
- }
-}
-
-unsafe impl<T, C> Sync for Resource<T, C>
-where
- T: Send,
-{
-}
-
-/// A hardware peripheral as a resource
-///
-/// To assign a ceiling to a peripheral, use the
-/// [`peripherals!`](./macro.peripherals.html) macro
-pub struct Peripheral<P, PC>
+pub struct Peripheral<P>
where
P: 'static,
{
+ // FIXME(rustc/LLVM bug?) storing the ceiling in the resource de-optimizes
+ // claims (the ceiling value gets loaded at runtime rather than inlined)
+ // ceiling: u8,
peripheral: cortex_m::peripheral::Peripheral<P>,
- _ceiling: PhantomData<PC>,
}
-impl<P, PC> Peripheral<P, PC>
-where
- PC: GreaterThanOrEqual<U0>,
- PC: LessThanOrEqual<UMax>,
-{
- #[doc(hidden)]
- pub const unsafe fn _new(peripheral: cortex_m::peripheral::Peripheral<P>,)
- -> Self {
- Peripheral {
- _ceiling: PhantomData,
- peripheral: peripheral,
- }
+impl<P> Peripheral<P> {
+ pub const fn new(peripheral: cortex_m::peripheral::Peripheral<P>) -> Self {
+ Peripheral { peripheral }
}
-}
-impl<Periph, PC> Peripheral<Periph, PC> {
- /// See [Resource.access](./struct.Resource.html#method.access)
- pub fn access<'cs, TP, PT>(
+ #[inline(always)]
+ pub unsafe fn claim<R, F>(
&'static self,
- _task_priority: &Priority<TP>,
- _preemption_threshold: &'cs Threshold<PT>,
- ) -> Ref<'cs, Periph>
+ ceiling: u8,
+ nvic_prio_bits: u8,
+ t: &mut Threshold,
+ f: F,
+ ) -> R
where
- PC: GreaterThanOrEqual<TP>,
- PT: GreaterThanOrEqual<PC>,
+ F: FnOnce(&P, &mut Threshold) -> R,
{
- unsafe { Ref::new(&*self.peripheral.get()) }
+ claim(
+ &self.peripheral,
+ ceiling,
+ nvic_prio_bits,
+ t,
+ f,
+ |peripheral| &*peripheral.get(),
+ )
}
-}
-
-unsafe impl<T, C> Sync for Peripheral<T, C> {}
-
-/// Runs the closure `f` "atomically"
-///
-/// No task can preempt the execution of the closure
-pub fn atomic<R, F>(f: F) -> R
-where
- F: FnOnce(&TMax) -> R,
-{
- let primask = ::cortex_m::register::primask::read();
- ::cortex_m::interrupt::disable();
-
- let r = f(&Threshold { _marker: PhantomData });
- // If the interrupts were active before our `disable` call, then re-enable
- // them. Otherwise, keep them disabled
- if primask.is_active() {
- unsafe { ::cortex_m::interrupt::enable() }
+ pub fn get(&self) -> *mut P {
+ self.peripheral.get()
}
-
- r
-}
-
-/// Disables a `task`
-///
-/// The task won't run even if the underlying interrupt is raised
-pub fn disable<T, N>(_task: fn(T, Priority<N>, Threshold<N>))
-where
- T: Context + Nr,
-{
- // NOTE(safe) zero sized type
- let _task = unsafe { ptr::read(0x0 as *const T) };
-
- // NOTE(safe) atomic write
- unsafe { (*_NVIC.get()).disable(_task) }
}
-/// Enables a `task`
-pub fn enable<T, N>(_task: fn(T, Priority<N>, Threshold<N>))
+unsafe impl<P> Sync for Peripheral<P>
where
- T: Context + Nr,
+ P: Send,
{
- // NOTE(safe) zero sized type
- let _task = unsafe { ptr::read(0x0 as *const T) };
-
- // NOTE(safe) atomic write
- unsafe { (*_NVIC.get()).enable(_task) }
-}
-
-/// Converts a shifted hardware priority into a logical priority
-pub fn hw2logical(hw: u8) -> u8 {
- (1 << PRIORITY_BITS) - (hw >> (8 - PRIORITY_BITS))
}
-/// Converts a logical priority into a shifted hardware priority, as used by the
-/// NVIC and the BASEPRI register
-///
-/// # Panics
-///
-/// This function panics if `logical` is outside the closed range
-/// `[1, 1 << PRIORITY_BITS]`. Where `PRIORITY_BITS` is the number of priority
-/// bits used by the device specific NVIC implementation.
-pub fn logical2hw(logical: u8) -> u8 {
- assert!(logical >= 1 && logical <= (1 << PRIORITY_BITS));
-
- ((1 << PRIORITY_BITS) - logical) << (8 - PRIORITY_BITS)
+pub struct Resource<T> {
+ // FIXME(rustc/LLVM bug?) storing the ceiling in the resource de-optimizes
+ // claims (the ceiling value gets loaded at runtime rather than inlined)
+ // ceiling: u8,
+ data: UnsafeCell<T>,
}
-/// Requests the execution of a `task`
-pub fn request<T, N>(_task: fn(T, Priority<N>, Threshold<N>))
-where
- T: Context + Nr,
-{
- let nvic = unsafe { &*_NVIC.get() };
-
- match () {
- #[cfg(debug_assertions)]
- () => {
- // NOTE(safe) zero sized type
- let task = unsafe { core::ptr::read(0x0 as *const T) };
- // NOTE(safe) atomic read
- assert!(!nvic.is_pending(task),
- "Task is already in the pending state");
- }
- #[cfg(not(debug_assertions))]
- () => {}
+impl<T> Resource<T> {
+ pub const fn new(value: T) -> Self {
+ Resource { data: UnsafeCell::new(value) }
}
- // NOTE(safe) zero sized type
- let task = unsafe { core::ptr::read(0x0 as *const T) };
-
- // NOTE(safe) atomic write
- nvic.set_pending(task);
-}
-
-#[doc(hidden)]
-pub fn _validate_priority<TP>(_: &Priority<TP>)
-where
- TP: Cmp<U0, Output = Greater> + LessThanOrEqual<UMax>,
-{
-}
-
-/// Preemption threshold
-pub struct Threshold<T> {
- _marker: PhantomData<T>,
-}
-
-impl<PT> Threshold<PT> {
- /// Raises the preemption threshold to match the `resource` ceiling
- #[cfg(not(thumbv6m))]
- pub fn raise<RC, RES, R, F>(&self, _resource: &'static RES, f: F) -> R
+ #[inline(always)]
+ pub unsafe fn claim<R, F>(
+ &'static self,
+ ceiling: u8,
+ nvic_prio_bits: u8,
+ t: &mut Threshold,
+ f: F,
+ ) -> R
where
- RES: ResourceLike<Ceiling = RC>,
- RC: Cmp<PT, Output = Greater> + Cmp<UMax, Output = Less> + Unsigned,
- F: FnOnce(&Threshold<RC>) -> R,
+ F: FnOnce(&Static<T>, &mut Threshold) -> R,
{
- unsafe {
- let old_basepri = basepri::read();
- basepri_max::write(logical2hw(RC::to_u8()));
- barrier!();
- let ret = f(&Threshold { _marker: PhantomData });
- barrier!();
- basepri::write(old_basepri);
- ret
- }
+ claim(&self.data, ceiling, nvic_prio_bits, t, f, |data| {
+ Static::ref_(&*data.get())
+ })
}
-}
-impl<N> !Send for Threshold<N> {}
+ #[inline(always)]
+ pub unsafe fn claim_mut<R, F>(
+ &'static self,
+ ceiling: u8,
+ nvic_prio_bits: u8,
+ t: &mut Threshold,
+ f: F,
+ ) -> R
+ where
+ F: FnOnce(&mut Static<T>, &mut Threshold) -> R,
+ {
+ claim(&self.data, ceiling, nvic_prio_bits, t, f, |data| {
+ Static::ref_mut(&mut *data.get())
+ })
+ }
-/// Priority
-pub struct Priority<N> {
- _marker: PhantomData<N>,
+ pub fn get(&self) -> *mut T {
+ self.data.get()
+ }
}
-impl<T> Priority<T>
+unsafe impl<T> Sync for Resource<T>
where
- T: Unsigned,
+ T: Send,
{
- #[doc(hidden)]
- pub fn _hw() -> u8 {
- logical2hw(T::to_u8())
- }
}
-impl<N> !Send for Priority<N> {}
+pub struct Threshold(u8);
-/// Maps a `Resource` / `Peripheral` to its ceiling
-///
-/// Do not implement this trait yourself. This is an implementation detail.
-pub unsafe trait ResourceLike {
- /// The ceiling of the resource
- type Ceiling;
-}
-
-unsafe impl<P, PC> ResourceLike for Peripheral<P, PC> {
- type Ceiling = PC;
-}
-
-unsafe impl<T, RC> ResourceLike for Resource<T, RC> {
- type Ceiling = RC;
+impl Threshold {
+ pub unsafe fn new(value: u8) -> Self {
+ Threshold(value)
+ }
}
-/// Type-level `>=` operator
-///
-/// Do not implement this trait yourself. This is an implementation detail.
-pub unsafe trait GreaterThanOrEqual<RHS> {}
+impl !Send for Threshold {}
-/// Type-level `<=` operator
-///
-/// Do not implement this trait yourself. This is an implementation detail.
-pub unsafe trait LessThanOrEqual<RHS> {}
-
-/// A macro to assign ceilings to peripherals
-///
-/// **NOTE** A peripheral instance, like RCC, can only be bound to a *single*
-/// ceiling. Trying to use this macro to bind the same peripheral to several
-/// ceiling will result in a compiler error.
-///
-/// # Example
-///
-/// NOTE With device crates generated using svd2rust 0.8+ you can omit the
-/// register_block field.
-///
-/// ``` ignore
-/// #[macro_use]
-/// extern crate cortex_m_rtfm;
-/// // device crate generated using `svd2rust`
-/// extern crate stm32f30x;
-///
-/// peripherals!(stm32f30x, {
-/// GPIOA: Peripheral {
-/// register_block: Gpioa,
-/// ceiling: C1,
-/// },
-/// RCC: Peripheral {
-/// register_block: Rcc,
-/// ceiling: C0,
-/// },
-/// });
-/// ```
#[macro_export]
-macro_rules! peripherals {
- ($device:ident, {
- $($PERIPHERAL:ident: Peripheral {
- register_block: $RegisterBlock:ident,
- ceiling: $C:ident,
- },)+
- }) => {
- $(
- #[allow(private_no_mangle_statics)]
- #[no_mangle]
- static $PERIPHERAL:
- $crate::Peripheral<::$device::$RegisterBlock, $crate::$C> =
- unsafe { $crate::Peripheral::_new(::$device::$PERIPHERAL) };
- )+
+macro_rules! task {
+ ($NAME:ident, $body:path) => {
+ #[allow(non_snake_case)]
+ #[no_mangle]
+ pub unsafe extern "C" fn $NAME() {
+ let f: fn($crate::Threshold, ::$NAME::Resources) = $body;
+
+ f(
+ $crate::Threshold::new(::$NAME::$NAME),
+ ::$NAME::Resources::new(),
+ );
+ }
};
- ($device:ident, {
- $($PERIPHERAL:ident: Peripheral {
- ceiling: $C:ident,
- },)+
- }) => {
- $(
- #[allow(private_no_mangle_statics)]
- #[no_mangle]
- static $PERIPHERAL:
- $crate::Peripheral<::$device::$PERIPHERAL, $crate::$C> =
- unsafe { $crate::Peripheral::_new(::$device::$PERIPHERAL) };
- )+
- }
-}
-
-/// A macro to declare tasks
-///
-/// **NOTE** This macro will expand to a `main` function.
-///
-/// Each `$task` is bound to an `$Interrupt` handler and has a priority `$P`.
-/// The minimum priority of a task is `P1`. `$enabled` indicates whether the
-/// task will be enabled before `idle` runs.
-///
-/// The `$Interrupt` handlers are defined in the `$device` crate.
-///
-/// Apart from defining the listed `$tasks`, the `init` and `idle` functions
-/// must be defined as well. `init` has signature `fn(P0, &TMax)`, and `idle`
-/// has signature `fn(P0) -> !`.
-///
-/// # Example
-///
-/// ``` ignore
-/// #[feature(used)]
-/// #[no_std]
-///
-/// extern crate cortex_m_rt;
-/// #[macro_use]
-/// extern crate cortex_m_rtfm as rtfm;
-/// // device crate generated using `svd2rust`
-/// extern crate stm32f30x;
-///
-/// use rtfm::{P0, P1, P2, T0, T1, T2, TMax};
-/// use stm32f30x::interrupt::{Exti0, Tim7};
-///
-/// tasks!(stm32f30x, {
-/// periodic: Task {
-/// interrupt: Tim7,
-/// priority: P1,
-/// enabled: true,
-/// },
-/// button: Task {
-/// interrupt: Exti0,
-/// priority: P2,
-/// enabled: true,
-/// },
-/// });
-///
-/// fn init(priority: P0, threshold: &TMax) {
-/// // ..
-/// }
-///
-/// fn idle(priority: P0, threshold: T0) -> ! {
-/// // Sleep
-/// loop {
-/// rtfm::wfi();
-/// }
-/// }
-///
-/// // NOTE signature must match the tasks! declaration
-/// fn periodic(task: Tim7, priority: P1, threshold: T1) {
-/// // ..
-/// }
-///
-/// fn button(task: Exti0, priority: P2, threshold: T2) {
-/// // ..
-/// }
-/// ```
-#[macro_export]
-macro_rules! tasks {
- ($device:ident, {
- $($task:ident: Task {
- interrupt:$Interrupt:ident,
- priority: $P:ident,
- enabled: $enabled:expr,
- },)*
+ ($NAME:ident, $body:path, $local:ident {
+ $($var:ident: $ty:ty = $expr:expr;)+
}) => {
- fn main() {
- $crate::atomic(|t_max| {
- fn validate_signature(_: fn($crate::P0, &$crate::TMax)) {}
-
- validate_signature(init);
- let p0 = unsafe { ::core::mem::transmute::<_, P0>(()) };
- init(p0, t_max);
- set_priorities();
- enable_tasks();
- });
-
- fn validate_signature(_: fn($crate::P0, $crate::T0) -> !) {}
-
- validate_signature(idle);
- let p0 = unsafe { ::core::mem::transmute::<_, P0>(()) };
- let t0 = unsafe { ::core::mem::transmute::<_, T0>(()) };
- idle(p0, t0);
-
- fn set_priorities() {
- // NOTE(safe) this function runs in an interrupt free context
- let _nvic = unsafe { &*$crate::_NVIC.get() };
-
- $(
- {
- let hw = $crate::$P::_hw();
- unsafe {
- _nvic.set_priority(
- ::$device::interrupt::Interrupt::$Interrupt,
- hw,
- );
- }
- }
- )*
-
- // TODO freeze the NVIC.IPR register using the MPU, if available
- }
-
- fn enable_tasks() {
- // NOTE(safe) this function runs in an interrupt free context
- let _nvic = unsafe { &*$crate::_NVIC.get() };
+ struct $local {
+ $($var: $ty,)+
+ }
- $(
- if $enabled {
- $crate::enable(::$task);
- }
- )*
- }
+ #[allow(non_snake_case)]
+ #[no_mangle]
+ pub unsafe extern "C" fn $NAME() {
+ let f: fn(
+ $crate::Threshold,
+ &mut $local,
+ ::$NAME::Resources,
+ ) = $body;
+
+ static mut LOCAL: $local = $local {
+ $($var: $expr,)+
+ };
+
+ f(
+ $crate::Threshold::new(::$NAME::$NAME),
+ &mut LOCAL,
+ ::$NAME::Resources::new(),
+ );
+ }
+ };
+}
- #[allow(dead_code)]
- #[link_section = ".rodata.interrupts"]
- #[used]
- static INTERRUPTS: ::$device::interrupt::Handlers =
- ::$device::interrupt::Handlers {
- $(
- $Interrupt: {
- extern "C" fn $task(
- task: ::$device::interrupt::$Interrupt
- ) {
- fn validate_signature<N>(
- _: fn(::$device::interrupt::$Interrupt,
- $crate::Priority<N>,
- $crate::Threshold<N>)) {}
- validate_signature(::$task);
- let p = unsafe {
- ::core::mem::transmute::<_, $crate::$P>(())
- };
- let t = unsafe {
- ::core::mem::transmute(())
- };
- $crate::_validate_priority(&p);
- ::$task(task, p, t)
- }
+#[allow(non_camel_case_types)]
+#[doc(hidden)]
+pub enum Exception {
+ /// System service call via SWI instruction
+ SVCALL,
+ /// Pendable request for system service
+ PENDSV,
+ /// System tick timer
+ SYS_TICK,
+}
- $task
- },
- )*
- ..::$device::interrupt::DEFAULT_HANDLERS
- };
+impl Exception {
+ #[doc(hidden)]
+ pub fn nr(&self) -> usize {
+ match *self {
+ Exception::SVCALL => 11,
+ Exception::PENDSV => 14,
+ Exception::SYS_TICK => 15,
}
}
}
-
-include!(concat!(env!("OUT_DIR"), "/prio.rs"));
diff --git a/tests/cfail.rs b/tests/cfail.rs
deleted file mode 100644
index 44c982ce..00000000
--- a/tests/cfail.rs
+++ /dev/null
@@ -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);
-}
diff --git a/tests/cfail/access.rs b/tests/cfail/access.rs
deleted file mode 100644
index b35fb371..00000000
--- a/tests/cfail/access.rs
+++ /dev/null
@@ -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);
- }
- );
-}
diff --git a/tests/cfail/ceiling.rs b/tests/cfail/ceiling.rs
deleted file mode 100644
index 118dbcf6..00000000
--- a/tests/cfail/ceiling.rs
+++ /dev/null
@@ -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);
-}
diff --git a/tests/cfail/lock.rs b/tests/cfail/lock.rs
deleted file mode 100644
index 64753ac1..00000000
--- a/tests/cfail/lock.rs
+++ /dev/null
@@ -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);
-}
diff --git a/tests/cfail/peripherals-alias-1.rs b/tests/cfail/peripherals-alias-1.rs
deleted file mode 100644
index 124f3510..00000000
--- a/tests/cfail/peripherals-alias-1.rs
+++ /dev/null
@@ -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 };
- }
-}
diff --git a/tests/cfail/peripherals-alias-2.rs b/tests/cfail/peripherals-alias-2.rs
deleted file mode 100644
index b50931ee..00000000
--- a/tests/cfail/peripherals-alias-2.rs
+++ /dev/null
@@ -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 };
- }
-}
diff --git a/tests/cfail/race-1.rs b/tests/cfail/race-1.rs
deleted file mode 100644
index 8d32c42e..00000000
--- a/tests/cfail/race-1.rs
+++ /dev/null
@@ -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
- }
-}
diff --git a/tests/cfail/race-2.rs b/tests/cfail/race-2.rs
deleted file mode 100644
index f44856e0..00000000
--- a/tests/cfail/race-2.rs
+++ /dev/null
@@ -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
- }
-}
diff --git a/tests/cfail/raise.rs b/tests/cfail/raise.rs
deleted file mode 100644
index 3d7e5647..00000000
--- a/tests/cfail/raise.rs
+++ /dev/null
@@ -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);
- });
-}
diff --git a/tests/cfail/tasks-p0.rs b/tests/cfail/tasks-p0.rs
deleted file mode 100644
index 588ac5d0..00000000
--- a/tests/cfail/tasks-p0.rs
+++ /dev/null
@@ -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,
- };
- }
-}
diff --git a/tests/cfail/tasks-same-handler.rs b/tests/cfail/tasks-same-handler.rs
deleted file mode 100644
index fdd8b063..00000000
--- a/tests/cfail/tasks-same-handler.rs
+++ /dev/null
@@ -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,
- };
- }
-}
diff --git a/tests/cfail/tasks-wrong-idle.rs b/tests/cfail/tasks-wrong-idle.rs
deleted file mode 100644
index e6ff7790..00000000
--- a/tests/cfail/tasks-wrong-idle.rs
+++ /dev/null
@@ -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,
- };
- }
-}
diff --git a/tests/cfail/tasks-wrong-init.rs b/tests/cfail/tasks-wrong-init.rs
deleted file mode 100644
index d12b427d..00000000
--- a/tests/cfail/tasks-wrong-init.rs
+++ /dev/null
@@ -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,
- };
- }
-}
diff --git a/tests/cfail/tasks-wrong-priority.rs b/tests/cfail/tasks-wrong-priority.rs
deleted file mode 100644
index 4d05d6b0..00000000
--- a/tests/cfail/tasks-wrong-priority.rs
+++ /dev/null
@@ -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,
- };
- }
-}
diff --git a/tests/cfail/tasks-wrong-task.rs b/tests/cfail/tasks-wrong-task.rs
deleted file mode 100644
index 026290a7..00000000
--- a/tests/cfail/tasks-wrong-task.rs
+++ /dev/null
@@ -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,
- };
- }
-}
diff --git a/tests/cfail/tasks-wrong-threshold.rs b/tests/cfail/tasks-wrong-threshold.rs
deleted file mode 100644
index 4fca734e..00000000
--- a/tests/cfail/tasks-wrong-threshold.rs
+++ /dev/null
@@ -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,
- };
- }
-}
diff --git a/tests/cfail/token-transfer.rs b/tests/cfail/token-transfer.rs
deleted file mode 100644
index 1d81e8f7..00000000
--- a/tests/cfail/token-transfer.rs
+++ /dev/null
@@ -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,
- };
- }
-}