mirror of
https://github.com/rtic-rs/rtic.git
synced 2024-12-25 19:39:32 +01:00
syntax tweaks, relax check, add set_pending(), deal with imported types
- allow trailing commas in list of resources - make task.resources optional - add rtfm::set_pending function which can be used to force an interrupt into the pending state. This is a replacement of the old rtfm::request. rtfm::set_pending takes the Interrupt enum provided by the device crate as argument. (The old rtfm::request took a task function as argument) - the user may want to use types they imported into the root of the crate. These types are not available in e.g. `mod idle` so `idle::Resources` *can't* be defined in that module. To workaround this problem `idle::Resources` will be defined in the root, with some other name, and then be re-exported in the `idle` module. - remove the "a resource only used by one task should be local data" check. In some cases you do want a resource owned by a single task instead of local data since `init` can access resources but not a task local data.
This commit is contained in:
parent
4b0c3bff87
commit
3cebf49a2f
7 changed files with 157 additions and 54 deletions
|
@ -1,17 +1,12 @@
|
||||||
use syntax::Resources;
|
use syntax::Statics;
|
||||||
use util::{Ceiling, Ceilings};
|
use util::Ceilings;
|
||||||
|
|
||||||
pub fn resources(resources: &Resources, ceilings: &Ceilings) {
|
pub fn resources(resources: &Statics, ceilings: &Ceilings) {
|
||||||
for resource in resources.keys() {
|
for resource in resources.keys() {
|
||||||
if let Some(ceiling) = ceilings.get(&resource) {
|
assert!(
|
||||||
assert_ne!(
|
ceilings.get(&resource).is_some(),
|
||||||
*ceiling,
|
"resource {} is unused",
|
||||||
Ceiling::Owned,
|
resource
|
||||||
"{} should be local data",
|
);
|
||||||
resource
|
|
||||||
);
|
|
||||||
} else {
|
|
||||||
panic!("resource {} is unused", resource)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,3 +1,4 @@
|
||||||
|
#![deny(warnings)]
|
||||||
#![feature(plugin_registrar)]
|
#![feature(plugin_registrar)]
|
||||||
#![feature(proc_macro_internals)]
|
#![feature(proc_macro_internals)]
|
||||||
#![feature(rustc_private)]
|
#![feature(rustc_private)]
|
||||||
|
|
|
@ -10,7 +10,7 @@ pub struct App {
|
||||||
pub device: Tokens,
|
pub device: Tokens,
|
||||||
pub idle: Idle,
|
pub idle: Idle,
|
||||||
pub init: Init,
|
pub init: Init,
|
||||||
pub resources: Resources,
|
pub resources: Statics,
|
||||||
pub tasks: Tasks,
|
pub tasks: Tasks,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -21,7 +21,7 @@ pub struct Init {
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub struct Idle {
|
pub struct Idle {
|
||||||
pub local: Resources,
|
pub local: Statics,
|
||||||
pub path: Tokens,
|
pub path: Tokens,
|
||||||
pub resources: HashSet<Ident>,
|
pub resources: HashSet<Ident>,
|
||||||
}
|
}
|
||||||
|
@ -46,6 +46,6 @@ pub struct Resource {
|
||||||
pub ty: Tokens,
|
pub ty: Tokens,
|
||||||
}
|
}
|
||||||
|
|
||||||
pub type Resources = HashMap<Ident, Resource>;
|
pub type Statics = HashMap<Ident, Resource>;
|
||||||
|
|
||||||
pub type Tasks = HashMap<Ident, Task>;
|
pub type Tasks = HashMap<Ident, Task>;
|
||||||
|
|
|
@ -2,7 +2,7 @@ use std::collections::{HashMap, HashSet};
|
||||||
|
|
||||||
use syn::{self, DelimToken, Ident, IntTy, Lit, Token, TokenTree};
|
use syn::{self, DelimToken, Ident, IntTy, Lit, Token, TokenTree};
|
||||||
|
|
||||||
use syntax::{App, Idle, Init, Kind, Resource, Resources, Task, Tasks};
|
use syntax::{App, Idle, Init, Kind, Resource, Statics, Task, Tasks};
|
||||||
|
|
||||||
pub fn app(input: &str) -> App {
|
pub fn app(input: &str) -> App {
|
||||||
let tts = syn::parse_token_trees(input).unwrap();
|
let tts = syn::parse_token_trees(input).unwrap();
|
||||||
|
@ -96,7 +96,7 @@ pub fn app(input: &str) -> App {
|
||||||
block.delim
|
block.delim
|
||||||
);
|
);
|
||||||
|
|
||||||
resources = Some(super::parse::resources(block.tts));
|
resources = Some(super::parse::statics(block.tts));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
"tasks" => {
|
"tasks" => {
|
||||||
|
@ -169,7 +169,7 @@ pub fn idle(tts: Vec<TokenTree>) -> Idle {
|
||||||
block.delim
|
block.delim
|
||||||
);
|
);
|
||||||
|
|
||||||
local = Some(super::parse::resources(block.tts));
|
local = Some(super::parse::statics(block.tts));
|
||||||
} else {
|
} else {
|
||||||
panic!("expected block, found {:?}", tt);
|
panic!("expected block, found {:?}", tt);
|
||||||
}
|
}
|
||||||
|
@ -273,7 +273,7 @@ pub fn init(tts: Vec<TokenTree>) -> Init {
|
||||||
fn idents(tts: Vec<TokenTree>) -> HashSet<Ident> {
|
fn idents(tts: Vec<TokenTree>) -> HashSet<Ident> {
|
||||||
let mut idents = HashSet::new();
|
let mut idents = HashSet::new();
|
||||||
|
|
||||||
let mut tts = tts.into_iter();
|
let mut tts = tts.into_iter().peekable();
|
||||||
while let Some(tt) = tts.next() {
|
while let Some(tt) = tts.next() {
|
||||||
if let TokenTree::Token(Token::Ident(id)) = tt {
|
if let TokenTree::Token(Token::Ident(id)) = tt {
|
||||||
assert!(!idents.contains(&id), "ident {} already listed", id);
|
assert!(!idents.contains(&id), "ident {} already listed", id);
|
||||||
|
@ -281,6 +281,10 @@ fn idents(tts: Vec<TokenTree>) -> HashSet<Ident> {
|
||||||
|
|
||||||
if let Some(tt) = tts.next() {
|
if let Some(tt) = tts.next() {
|
||||||
assert_eq!(tt, TokenTree::Token(Token::Comma));
|
assert_eq!(tt, TokenTree::Token(Token::Comma));
|
||||||
|
|
||||||
|
if tts.peek().is_none() {
|
||||||
|
break;
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
@ -292,7 +296,7 @@ fn idents(tts: Vec<TokenTree>) -> HashSet<Ident> {
|
||||||
idents
|
idents
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn resources(tts: Vec<TokenTree>) -> Resources {
|
pub fn statics(tts: Vec<TokenTree>) -> Statics {
|
||||||
let mut resources = HashMap::new();
|
let mut resources = HashMap::new();
|
||||||
|
|
||||||
let mut tts = tts.into_iter();
|
let mut tts = tts.into_iter();
|
||||||
|
@ -502,7 +506,7 @@ fn task(tts: Vec<TokenTree>) -> Task {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
let resources = resources.expect("resources field is missing");
|
let resources = resources.unwrap_or(HashSet::new());
|
||||||
let priority = priority.expect("priority field is missing");
|
let priority = priority.expect("priority field is missing");
|
||||||
let kind = if let Some(enabled) = enabled {
|
let kind = if let Some(enabled) = enabled {
|
||||||
Kind::Interrupt { enabled }
|
Kind::Interrupt { enabled }
|
||||||
|
|
|
@ -39,22 +39,24 @@ fn init(app: &App, main: &mut Vec<Tokens>, root: &mut Vec<Tokens>) {
|
||||||
let ty = &resource.ty;
|
let ty = &resource.ty;
|
||||||
|
|
||||||
fields.push(quote! {
|
fields.push(quote! {
|
||||||
pub #name: &'a mut #ty,
|
pub #name: &'a mut #krate::Static<#ty>,
|
||||||
});
|
});
|
||||||
|
|
||||||
exprs.push(quote! {
|
exprs.push(quote! {
|
||||||
#name: &mut *super::#name.get(),
|
#name: ::#krate::Static::ref_mut(&mut *super::#name.get()),
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
root.push(quote! {
|
root.push(quote! {
|
||||||
|
#[allow(non_camel_case_types)]
|
||||||
|
#[allow(non_snake_case)]
|
||||||
|
pub struct _initResources<#lifetime> {
|
||||||
|
#(#fields)*
|
||||||
|
}
|
||||||
|
|
||||||
mod init {
|
mod init {
|
||||||
pub use ::#device::Peripherals;
|
pub use ::#device::Peripherals;
|
||||||
|
pub use ::_initResources as Resources;
|
||||||
#[allow(non_snake_case)]
|
|
||||||
pub struct Resources<#lifetime> {
|
|
||||||
#(#fields)*
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<#lifetime> Resources<#lifetime> {
|
impl<#lifetime> Resources<#lifetime> {
|
||||||
pub unsafe fn new() -> Self {
|
pub unsafe fn new() -> Self {
|
||||||
|
@ -184,36 +186,80 @@ fn idle(
|
||||||
let device = &app.device;
|
let device = &app.device;
|
||||||
let mut lifetime = None;
|
let mut lifetime = None;
|
||||||
|
|
||||||
|
let mut needs_reexport = false;
|
||||||
|
for name in &app.idle.resources {
|
||||||
|
if ceilings[name].is_owned() {
|
||||||
|
if app.resources.get(name).is_some() {
|
||||||
|
needs_reexport = true;
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let super_ = if needs_reexport {
|
||||||
|
None
|
||||||
|
} else {
|
||||||
|
Some(Ident::new("super"))
|
||||||
|
};
|
||||||
let mut rexprs = vec![];
|
let mut rexprs = vec![];
|
||||||
let mut rfields = vec![];
|
let mut rfields = vec![];
|
||||||
for name in &app.idle.resources {
|
for name in &app.idle.resources {
|
||||||
if ceilings[name].is_owned() {
|
if ceilings[name].is_owned() {
|
||||||
lifetime = Some(quote!('a));
|
lifetime = Some(quote!('a));
|
||||||
|
if let Some(resource) = app.resources.get(name) {
|
||||||
|
let ty = &resource.ty;
|
||||||
|
|
||||||
rfields.push(quote! {
|
rfields.push(quote! {
|
||||||
pub #name: &'a mut ::#device::#name,
|
pub #name: &'a mut ::#krate::Static<#ty>,
|
||||||
});
|
});
|
||||||
|
|
||||||
rexprs.push(quote! {
|
rexprs.push(quote! {
|
||||||
#name: &mut *::#device::#name.get(),
|
#name: ::#krate::Static::ref_mut(
|
||||||
});
|
&mut *#super_::#name.get(),
|
||||||
|
),
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
rfields.push(quote! {
|
||||||
|
pub #name: &'a mut ::#device::#name,
|
||||||
|
});
|
||||||
|
|
||||||
|
rexprs.push(quote! {
|
||||||
|
#name: &mut *::#device::#name.get(),
|
||||||
|
});
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
rfields.push(quote! {
|
rfields.push(quote! {
|
||||||
pub #name: super::_resource::#name,
|
pub #name: #super_::_resource::#name,
|
||||||
});
|
});
|
||||||
|
|
||||||
rexprs.push(quote! {
|
rexprs.push(quote! {
|
||||||
#name: super::_resource::#name::new(),
|
#name: #super_::_resource::#name::new(),
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
mod_items.push(quote! {
|
if needs_reexport {
|
||||||
#[allow(non_snake_case)]
|
root.push(quote! {
|
||||||
pub struct Resources<#lifetime> {
|
#[allow(non_camel_case_types)]
|
||||||
#(#rfields)*
|
#[allow(non_snake_case)]
|
||||||
}
|
pub struct _idleResources<#lifetime> {
|
||||||
|
#(#rfields)*
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
mod_items.push(quote! {
|
||||||
|
pub use ::_idleResources as Resources;
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
mod_items.push(quote! {
|
||||||
|
#[allow(non_snake_case)]
|
||||||
|
pub struct Resources<#lifetime> {
|
||||||
|
#(#rfields)*
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
mod_items.push(quote! {
|
||||||
impl<#lifetime> Resources<#lifetime> {
|
impl<#lifetime> Resources<#lifetime> {
|
||||||
pub unsafe fn new() -> Self {
|
pub unsafe fn new() -> Self {
|
||||||
Resources {
|
Resources {
|
||||||
|
@ -252,6 +298,7 @@ fn tasks(app: &App, ceilings: &Ceilings, root: &mut Vec<Tokens>) {
|
||||||
|
|
||||||
let device = &app.device;
|
let device = &app.device;
|
||||||
let mut lifetime = None;
|
let mut lifetime = None;
|
||||||
|
let mut needs_reexport = false;
|
||||||
for name in &task.resources {
|
for name in &task.resources {
|
||||||
match ceilings[name] {
|
match ceilings[name] {
|
||||||
Ceiling::Shared(ceiling) if ceiling > task.priority => {
|
Ceiling::Shared(ceiling) if ceiling > task.priority => {
|
||||||
|
@ -268,6 +315,7 @@ fn tasks(app: &App, ceilings: &Ceilings, root: &mut Vec<Tokens>) {
|
||||||
_ => {
|
_ => {
|
||||||
lifetime = Some(quote!('a));
|
lifetime = Some(quote!('a));
|
||||||
if let Some(resource) = app.resources.get(name) {
|
if let Some(resource) = app.resources.get(name) {
|
||||||
|
needs_reexport = true;
|
||||||
let ty = &resource.ty;
|
let ty = &resource.ty;
|
||||||
|
|
||||||
fields.push(quote! {
|
fields.push(quote! {
|
||||||
|
@ -292,12 +340,27 @@ fn tasks(app: &App, ceilings: &Ceilings, root: &mut Vec<Tokens>) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
items.push(quote! {
|
if needs_reexport {
|
||||||
#[allow(non_snake_case)]
|
let rname = Ident::new(format!("_{}Resources", name));
|
||||||
pub struct Resources<#lifetime> {
|
root.push(quote! {
|
||||||
#(#fields)*
|
#[allow(non_camel_case_types)]
|
||||||
}
|
#[allow(non_snake_case)]
|
||||||
});
|
pub struct #rname<#lifetime> {
|
||||||
|
#(#fields)*
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
items.push(quote! {
|
||||||
|
pub use ::#rname as Resources;
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
items.push(quote! {
|
||||||
|
#[allow(non_snake_case)]
|
||||||
|
pub struct Resources<#lifetime> {
|
||||||
|
#(#fields)*
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
items.push(quote! {
|
items.push(quote! {
|
||||||
impl<#lifetime> Resources<#lifetime> {
|
impl<#lifetime> Resources<#lifetime> {
|
||||||
|
@ -339,7 +402,22 @@ fn resources(app: &App, ceilings: &Ceilings, root: &mut Vec<Tokens>) {
|
||||||
let mut impl_items = vec![];
|
let mut impl_items = vec![];
|
||||||
|
|
||||||
match *ceiling {
|
match *ceiling {
|
||||||
Ceiling::Owned => continue,
|
Ceiling::Owned(_) => {
|
||||||
|
if let Some(resource) = app.resources.get(name) {
|
||||||
|
// For owned resources we don't need claim() or borrow(),
|
||||||
|
// just get()
|
||||||
|
let expr = &resource.expr;
|
||||||
|
let ty = &resource.ty;
|
||||||
|
|
||||||
|
root.push(quote! {
|
||||||
|
static #name: #krate::Resource<#ty> =
|
||||||
|
#krate::Resource::new(#expr);
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
// Peripheral
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
},
|
||||||
Ceiling::Shared(ceiling) => {
|
Ceiling::Shared(ceiling) => {
|
||||||
if let Some(resource) = app.resources.get(name) {
|
if let Some(resource) = app.resources.get(name) {
|
||||||
let expr = &resource.expr;
|
let expr = &resource.expr;
|
||||||
|
|
|
@ -1,3 +1,4 @@
|
||||||
|
use std::cmp;
|
||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
|
|
||||||
use syn::Ident;
|
use syn::Ident;
|
||||||
|
@ -8,13 +9,18 @@ pub type Ceilings = HashMap<Ident, Ceiling>;
|
||||||
|
|
||||||
#[derive(Clone, Copy, Debug, PartialEq)]
|
#[derive(Clone, Copy, Debug, PartialEq)]
|
||||||
pub enum Ceiling {
|
pub enum Ceiling {
|
||||||
Owned,
|
// Owned by one or more tasks that have the same priority
|
||||||
|
Owned(u8),
|
||||||
|
// Shared by tasks with different priorities
|
||||||
Shared(u8),
|
Shared(u8),
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Ceiling {
|
impl Ceiling {
|
||||||
pub fn is_owned(&self) -> bool {
|
pub fn is_owned(&self) -> bool {
|
||||||
*self == Ceiling::Owned
|
match *self {
|
||||||
|
Ceiling::Owned(_) => true,
|
||||||
|
_ => false,
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -22,14 +28,22 @@ pub fn compute_ceilings(app: &App) -> Ceilings {
|
||||||
let mut ceilings = HashMap::new();
|
let mut ceilings = HashMap::new();
|
||||||
|
|
||||||
for resource in &app.idle.resources {
|
for resource in &app.idle.resources {
|
||||||
ceilings.insert(resource.clone(), Ceiling::Owned);
|
ceilings.insert(resource.clone(), Ceiling::Owned(0));
|
||||||
}
|
}
|
||||||
|
|
||||||
for task in app.tasks.values() {
|
for task in app.tasks.values() {
|
||||||
for resource in &task.resources {
|
for resource in &task.resources {
|
||||||
if let Some(ceiling) = ceilings.get_mut(resource) {
|
if let Some(ceiling) = ceilings.get_mut(resource) {
|
||||||
match *ceiling {
|
match *ceiling {
|
||||||
Ceiling::Owned => *ceiling = Ceiling::Shared(task.priority),
|
Ceiling::Owned(current) => {
|
||||||
|
if current == task.priority {
|
||||||
|
*ceiling = Ceiling::Owned(current);
|
||||||
|
} else {
|
||||||
|
*ceiling = Ceiling::Shared(
|
||||||
|
cmp::max(current, task.priority),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
Ceiling::Shared(old) => {
|
Ceiling::Shared(old) => {
|
||||||
if task.priority > old {
|
if task.priority > old {
|
||||||
*ceiling = Ceiling::Shared(task.priority);
|
*ceiling = Ceiling::Shared(task.priority);
|
||||||
|
@ -40,7 +54,7 @@ pub fn compute_ceilings(app: &App) -> Ceilings {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
ceilings.insert(resource.clone(), Ceiling::Owned);
|
ceilings.insert(resource.clone(), Ceiling::Owned(task.priority));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
11
src/lib.rs
11
src/lib.rs
|
@ -12,6 +12,7 @@ pub use cortex_m::asm::{bkpt, wfi};
|
||||||
pub use cortex_m::interrupt::CriticalSection;
|
pub use cortex_m::interrupt::CriticalSection;
|
||||||
pub use cortex_m::interrupt::free as atomic;
|
pub use cortex_m::interrupt::free as atomic;
|
||||||
pub use static_ref::Static;
|
pub use static_ref::Static;
|
||||||
|
use cortex_m::interrupt::Nr;
|
||||||
#[cfg(not(armv6m))]
|
#[cfg(not(armv6m))]
|
||||||
use cortex_m::register::{basepri_max, basepri};
|
use cortex_m::register::{basepri_max, basepri};
|
||||||
|
|
||||||
|
@ -175,6 +176,16 @@ impl Threshold {
|
||||||
|
|
||||||
impl !Send for Threshold {}
|
impl !Send for Threshold {}
|
||||||
|
|
||||||
|
/// Sets an interrupt as pending
|
||||||
|
pub fn set_pending<I>(interrupt: I)
|
||||||
|
where
|
||||||
|
I: Nr,
|
||||||
|
{
|
||||||
|
// NOTE(safe) atomic write
|
||||||
|
let nvic = unsafe { &*cortex_m::peripheral::NVIC.get() };
|
||||||
|
nvic.set_pending(interrupt);
|
||||||
|
}
|
||||||
|
|
||||||
#[macro_export]
|
#[macro_export]
|
||||||
macro_rules! task {
|
macro_rules! task {
|
||||||
($NAME:ident, $body:path) => {
|
($NAME:ident, $body:path) => {
|
||||||
|
|
Loading…
Reference in a new issue