mirror of
https://github.com/rtic-rs/rtic.git
synced 2025-12-18 22:05:37 +01:00
WIP
This commit is contained in:
parent
754f041ae0
commit
8723c6d45b
41 changed files with 1974 additions and 2851 deletions
|
|
@ -13,8 +13,10 @@ version = "0.3.1"
|
|||
failure = "0.1.1"
|
||||
proc-macro2 = "0.3.6"
|
||||
quote = "0.5.1"
|
||||
rtfm-syntax = "0.3.0"
|
||||
# rtfm-syntax = "0.3.0"
|
||||
rtfm-syntax = { git = "https://github.com/japaric/rtfm-syntax", branch = "tq" }
|
||||
syn = "0.13.1"
|
||||
either = "1.5.0"
|
||||
|
||||
[lib]
|
||||
proc-macro = true
|
||||
|
|
|
|||
|
|
@ -1,77 +1,260 @@
|
|||
use std::cmp;
|
||||
use std::collections::HashMap;
|
||||
use std::collections::hash_map::Entry;
|
||||
use std::collections::{HashMap, HashSet};
|
||||
|
||||
use syn::Ident;
|
||||
use either::Either;
|
||||
use syn::{Ident, Type};
|
||||
use syntax::check::App;
|
||||
|
||||
use check::App;
|
||||
pub fn app(app: &App) -> Context {
|
||||
let mut async = HashSet::new();
|
||||
let mut async_after = HashSet::new();
|
||||
let mut dispatchers = HashMap::new();
|
||||
let mut triggers = HashMap::new();
|
||||
let mut tq = TimerQueue::new();
|
||||
let mut free_interrupts = app.free_interrupts.iter().cloned().collect::<Vec<_>>();
|
||||
|
||||
pub type Ownerships = HashMap<Ident, Ownership>;
|
||||
async.extend(&app.init.async);
|
||||
async_after.extend(&app.init.async_after);
|
||||
|
||||
pub enum Ownership {
|
||||
/// Owned or co-owned by tasks that run at the same priority
|
||||
Owned { priority: u8 },
|
||||
/// Shared by tasks that run at different priorities.
|
||||
///
|
||||
/// `ceiling` is the maximum value across all the task priorities
|
||||
Shared { ceiling: u8 },
|
||||
}
|
||||
|
||||
impl Ownership {
|
||||
pub fn ceiling(&self) -> u8 {
|
||||
match *self {
|
||||
Ownership::Owned { priority } => priority,
|
||||
Ownership::Shared { ceiling } => ceiling,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn is_owned(&self) -> bool {
|
||||
match *self {
|
||||
Ownership::Owned { .. } => true,
|
||||
_ => false,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn app(app: &App) -> Ownerships {
|
||||
let mut ownerships = HashMap::new();
|
||||
|
||||
for resource in &app.idle.resources {
|
||||
ownerships.insert(resource.clone(), Ownership::Owned { priority: 0 });
|
||||
}
|
||||
|
||||
for task in app.tasks.values() {
|
||||
for resource in task.resources.iter() {
|
||||
if let Some(ownership) = ownerships.get_mut(resource) {
|
||||
match *ownership {
|
||||
Ownership::Owned { priority } => {
|
||||
if priority == task.priority {
|
||||
*ownership = Ownership::Owned { priority };
|
||||
} else {
|
||||
*ownership = Ownership::Shared {
|
||||
ceiling: cmp::max(priority, task.priority),
|
||||
};
|
||||
}
|
||||
}
|
||||
Ownership::Shared { ceiling } => {
|
||||
if task.priority > ceiling {
|
||||
*ownership = Ownership::Shared {
|
||||
ceiling: task.priority,
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
continue;
|
||||
// compute dispatchers
|
||||
for (name, task) in &app.tasks {
|
||||
match task.interrupt_or_capacity {
|
||||
Either::Left(interrupt) => {
|
||||
triggers.insert(interrupt, (*name, task.priority));
|
||||
}
|
||||
Either::Right(capacity) => {
|
||||
let dispatcher = dispatchers.entry(task.priority).or_insert(Dispatcher::new(
|
||||
free_interrupts.pop().expect("not enough free interrupts"),
|
||||
));
|
||||
dispatcher.tasks.push(*name);
|
||||
dispatcher.capacity += capacity;
|
||||
}
|
||||
}
|
||||
|
||||
ownerships.insert(
|
||||
resource.clone(),
|
||||
Ownership::Owned {
|
||||
priority: task.priority,
|
||||
},
|
||||
);
|
||||
for task in &task.async {
|
||||
async.insert(*task);
|
||||
}
|
||||
|
||||
for task in &task.async_after {
|
||||
async_after.insert(*task);
|
||||
|
||||
// Timer queue
|
||||
if let Entry::Vacant(entry) = tq.tasks.entry(*task) {
|
||||
tq.capacity += app.tasks[task].interrupt_or_capacity.right().unwrap();
|
||||
entry.insert(app.tasks[task].priority);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ownerships
|
||||
// The SysTick exception runs at the highest dispatcher priority
|
||||
let sys_tick = dispatchers.keys().cloned().max().unwrap_or(1);
|
||||
|
||||
// compute ceilings
|
||||
let mut ceilings = Ceilings::new(sys_tick);
|
||||
|
||||
// the SysTick interrupt contends for the timer queue (this has been accounted for in the
|
||||
// `Ceilings` constructor) and for the producer end of all the dispatcher queues (__#N::Q)
|
||||
for dispatch_priority in dispatchers.keys() {
|
||||
ceilings
|
||||
.dispatch_queues
|
||||
.insert(*dispatch_priority, sys_tick);
|
||||
}
|
||||
|
||||
// resources
|
||||
for (priority, resource) in app.idle.resources.iter().map(|res| (0, res)).chain(
|
||||
app.tasks
|
||||
.iter()
|
||||
.flat_map(|(name, task)| task.resources.iter().map(move |res| (task.priority, res))),
|
||||
) {
|
||||
let ceiling = ceilings
|
||||
.resources
|
||||
.entry(*resource)
|
||||
.or_insert(Ceiling::Owned(priority));
|
||||
if priority > (*ceiling).into() {
|
||||
*ceiling = Ceiling::Shared(priority);
|
||||
} else if priority < (*ceiling).into() && ceiling.is_owned() {
|
||||
*ceiling = Ceiling::Shared((*ceiling).into());
|
||||
}
|
||||
}
|
||||
|
||||
// async
|
||||
for (caller_priority, task) in app.tasks
|
||||
.values()
|
||||
.flat_map(|caller| caller.async.iter().map(move |task| (caller.priority, task)))
|
||||
{
|
||||
// async callers contend for the consumer end of the task slot queue (#task::SQ) and ...
|
||||
let ceiling = ceilings.slot_queues.entry(*task).or_insert(caller_priority);
|
||||
|
||||
if caller_priority > *ceiling {
|
||||
*ceiling = caller_priority;
|
||||
}
|
||||
|
||||
// .. for the producer end of the dispatcher queue (__#dispatch_priority::Q)
|
||||
let dispatch_priority = app.tasks[task].priority;
|
||||
let ceiling = ceilings
|
||||
.dispatch_queues
|
||||
.entry(dispatch_priority)
|
||||
.or_insert(dispatch_priority);
|
||||
|
||||
if caller_priority > *ceiling {
|
||||
*ceiling = caller_priority;
|
||||
}
|
||||
}
|
||||
|
||||
// async_after
|
||||
for (caller_priority, task) in app.tasks.values().flat_map(|caller| {
|
||||
caller
|
||||
.async_after
|
||||
.iter()
|
||||
.map(move |task| (caller.priority, task))
|
||||
}) {
|
||||
// async_after callers contend for the consumer end of the task slot queue (#task::SQ) and
|
||||
// ...
|
||||
let ceiling = ceilings.slot_queues.entry(*task).or_insert(caller_priority);
|
||||
|
||||
if caller_priority > *ceiling {
|
||||
*ceiling = caller_priority;
|
||||
}
|
||||
|
||||
// .. for the timer queue
|
||||
if caller_priority > ceilings.timer_queue {
|
||||
ceilings.timer_queue = caller_priority;
|
||||
}
|
||||
}
|
||||
|
||||
Context {
|
||||
async,
|
||||
async_after,
|
||||
ceilings,
|
||||
dispatchers,
|
||||
sys_tick,
|
||||
triggers,
|
||||
timer_queue: tq,
|
||||
}
|
||||
}
|
||||
|
||||
pub struct Context {
|
||||
// set of `async` tasks
|
||||
pub async: HashSet<Ident>,
|
||||
// set of `async_after` tasks
|
||||
pub async_after: HashSet<Ident>,
|
||||
pub ceilings: Ceilings,
|
||||
// Priority:u8 -> Dispatcher
|
||||
pub dispatchers: HashMap<u8, Dispatcher>,
|
||||
// Interrupt:Ident -> Task:Ident
|
||||
pub triggers: HashMap<Ident, (Ident, u8)>,
|
||||
pub timer_queue: TimerQueue,
|
||||
// priority of the SysTick exception
|
||||
pub sys_tick: u8,
|
||||
}
|
||||
|
||||
pub struct TimerQueue {
|
||||
// Task:Ident -> Priority:u8
|
||||
tasks: HashMap<Ident, u8>,
|
||||
capacity: u8,
|
||||
}
|
||||
|
||||
impl TimerQueue {
|
||||
fn new() -> Self {
|
||||
TimerQueue {
|
||||
tasks: HashMap::new(),
|
||||
capacity: 0,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn capacity(&self) -> u8 {
|
||||
self.capacity
|
||||
}
|
||||
|
||||
pub fn tasks(&self) -> &HashMap<Ident, u8> {
|
||||
&self.tasks
|
||||
}
|
||||
}
|
||||
|
||||
pub struct Dispatcher {
|
||||
capacity: u8,
|
||||
interrupt: Ident,
|
||||
tasks: Vec<Ident>,
|
||||
}
|
||||
|
||||
impl Dispatcher {
|
||||
fn new(interrupt: Ident) -> Self {
|
||||
Dispatcher {
|
||||
capacity: 0,
|
||||
interrupt,
|
||||
tasks: vec![],
|
||||
}
|
||||
}
|
||||
|
||||
pub fn capacity(&self) -> u8 {
|
||||
self.capacity
|
||||
}
|
||||
|
||||
pub fn interrupt(&self) -> Ident {
|
||||
self.interrupt
|
||||
}
|
||||
|
||||
pub fn tasks(&self) -> &[Ident] {
|
||||
&self.tasks
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct Ceilings {
|
||||
dispatch_queues: HashMap<u8, u8>,
|
||||
resources: HashMap<Ident, Ceiling>,
|
||||
slot_queues: HashMap<Ident, u8>,
|
||||
timer_queue: u8,
|
||||
}
|
||||
|
||||
impl Ceilings {
|
||||
fn new(sys_tick_priority: u8) -> Self {
|
||||
Ceilings {
|
||||
dispatch_queues: HashMap::new(),
|
||||
resources: HashMap::new(),
|
||||
slot_queues: HashMap::new(),
|
||||
timer_queue: sys_tick_priority,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn dispatch_queues(&self) -> &HashMap<u8, u8> {
|
||||
&self.dispatch_queues
|
||||
}
|
||||
|
||||
pub fn resources(&self) -> &HashMap<Ident, Ceiling> {
|
||||
&self.resources
|
||||
}
|
||||
|
||||
pub fn slot_queues(&self) -> &HashMap<Ident, u8> {
|
||||
&self.slot_queues
|
||||
}
|
||||
|
||||
pub fn timer_queue(&self) -> u8 {
|
||||
self.timer_queue
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, Debug)]
|
||||
pub enum Ceiling {
|
||||
Owned(u8),
|
||||
Shared(u8),
|
||||
}
|
||||
|
||||
impl Ceiling {
|
||||
pub fn is_owned(&self) -> bool {
|
||||
if let Ceiling::Owned(..) = *self {
|
||||
true
|
||||
} else {
|
||||
false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<Ceiling> for u8 {
|
||||
fn from(ceiling: Ceiling) -> u8 {
|
||||
match ceiling {
|
||||
Ceiling::Owned(prio) => prio,
|
||||
Ceiling::Shared(ceil) => ceil,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,95 +1,7 @@
|
|||
use std::collections::HashMap;
|
||||
use syntax::check::App;
|
||||
use syntax::Result;
|
||||
|
||||
use syn::{Ident, Path};
|
||||
use syntax::check::{self, Idents, Idle, Init, Statics};
|
||||
use syntax::{self, Result};
|
||||
|
||||
pub struct App {
|
||||
pub device: Path,
|
||||
pub idle: Idle,
|
||||
pub init: Init,
|
||||
pub resources: Statics,
|
||||
pub tasks: Tasks,
|
||||
}
|
||||
|
||||
pub type Tasks = HashMap<Ident, Task>;
|
||||
|
||||
#[allow(non_camel_case_types)]
|
||||
pub enum Exception {
|
||||
PENDSV,
|
||||
SVCALL,
|
||||
SYS_TICK,
|
||||
}
|
||||
|
||||
impl Exception {
|
||||
pub fn from(s: &str) -> Option<Self> {
|
||||
Some(match s {
|
||||
"PENDSV" => Exception::PENDSV,
|
||||
"SVCALL" => Exception::SVCALL,
|
||||
"SYS_TICK" => Exception::SYS_TICK,
|
||||
_ => return None,
|
||||
})
|
||||
}
|
||||
|
||||
pub fn nr(&self) -> usize {
|
||||
match *self {
|
||||
Exception::PENDSV => 14,
|
||||
Exception::SVCALL => 11,
|
||||
Exception::SYS_TICK => 15,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub enum Kind {
|
||||
Exception(Exception),
|
||||
Interrupt { enabled: bool },
|
||||
}
|
||||
|
||||
pub struct Task {
|
||||
pub kind: Kind,
|
||||
pub path: Path,
|
||||
pub priority: u8,
|
||||
pub resources: Idents,
|
||||
}
|
||||
|
||||
pub fn app(app: check::App) -> Result<App> {
|
||||
let app = App {
|
||||
device: app.device,
|
||||
idle: app.idle,
|
||||
init: app.init,
|
||||
resources: app.resources,
|
||||
tasks: app.tasks
|
||||
.into_iter()
|
||||
.map(|(k, v)| {
|
||||
let v = ::check::task(k.as_ref(), v)?;
|
||||
|
||||
Ok((k, v))
|
||||
})
|
||||
.collect::<Result<_>>()?,
|
||||
};
|
||||
|
||||
Ok(app)
|
||||
}
|
||||
|
||||
fn task(name: &str, task: syntax::check::Task) -> Result<Task> {
|
||||
let kind = match Exception::from(name) {
|
||||
Some(e) => {
|
||||
ensure!(
|
||||
task.enabled.is_none(),
|
||||
"`enabled` field is not valid for exceptions"
|
||||
);
|
||||
|
||||
Kind::Exception(e)
|
||||
}
|
||||
None => Kind::Interrupt {
|
||||
enabled: task.enabled.unwrap_or(true),
|
||||
},
|
||||
};
|
||||
|
||||
Ok(Task {
|
||||
kind,
|
||||
path: task.path,
|
||||
priority: task.priority.unwrap_or(1),
|
||||
resources: task.resources,
|
||||
})
|
||||
pub fn app(_app: &App) -> Result<()> {
|
||||
// TODO ???
|
||||
Ok(())
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
//! Procedural macros of the `cortex-m-rtfm` crate
|
||||
// #![deny(warnings)]
|
||||
#![allow(warnings)]
|
||||
#![feature(proc_macro)]
|
||||
#![recursion_limit = "128"]
|
||||
#![recursion_limit = "256"]
|
||||
|
||||
#[macro_use]
|
||||
extern crate failure;
|
||||
|
|
@ -10,6 +10,7 @@ extern crate proc_macro2;
|
|||
extern crate syn;
|
||||
#[macro_use]
|
||||
extern crate quote;
|
||||
extern crate either;
|
||||
extern crate rtfm_syntax as syntax;
|
||||
|
||||
use proc_macro::TokenStream;
|
||||
|
|
@ -19,154 +20,6 @@ mod analyze;
|
|||
mod check;
|
||||
mod trans;
|
||||
|
||||
/// The `app!` macro, a macro used to specify the tasks and resources of a RTFM application.
|
||||
///
|
||||
/// The contents of this macro uses a `key: value` syntax. All the possible keys are shown below:
|
||||
///
|
||||
/// ``` text
|
||||
/// app! {
|
||||
/// device: ..,
|
||||
///
|
||||
/// resources: { .. },
|
||||
///
|
||||
/// init: { .. },
|
||||
///
|
||||
/// idle: { .. },
|
||||
///
|
||||
/// tasks: { .. },
|
||||
/// }
|
||||
/// ```
|
||||
///
|
||||
/// # `device`
|
||||
///
|
||||
/// The value of this key is a Rust path, like `foo::bar::baz`, that must point to a *device crate*,
|
||||
/// a crate generated using `svd2rust`.
|
||||
///
|
||||
/// # `resources`
|
||||
///
|
||||
/// This key is optional. Its value is a list of `static` variables. These variables are the data
|
||||
/// that can be safely accessed, modified and shared by tasks.
|
||||
///
|
||||
/// ``` text
|
||||
/// resources: {
|
||||
/// static A: bool = false;
|
||||
/// static B: i32 = 0;
|
||||
/// static C: [u8; 16] = [0; 16];
|
||||
/// static D: Thing = Thing::new(..);
|
||||
/// static E: Thing;
|
||||
/// }
|
||||
/// ```
|
||||
///
|
||||
/// The initial value of a resource can be omitted. This means that the resource will be runtime
|
||||
/// initialized; these runtime initialized resources are also known as *late resources*.
|
||||
///
|
||||
/// If this key is omitted its value defaults to an empty list.
|
||||
///
|
||||
/// # `init`
|
||||
///
|
||||
/// This key is optional. Its value is a set of key values. All the possible keys are shown below:
|
||||
///
|
||||
/// ``` text
|
||||
/// init: {
|
||||
/// path: ..,
|
||||
/// }
|
||||
/// ```
|
||||
///
|
||||
/// ## `init.path`
|
||||
///
|
||||
/// This key is optional. Its value is a Rust path, like `foo::bar::baz`, that points to the
|
||||
/// initialization function.
|
||||
///
|
||||
/// If the key is omitted its value defaults to `init`.
|
||||
///
|
||||
/// ## `init.resources`
|
||||
///
|
||||
/// This key is optional. Its value is a set of resources the `init` function *owns*. The resources
|
||||
/// in this list must be a subset of the resources listed in the top `resources` key. Note that some
|
||||
/// restrictions apply:
|
||||
///
|
||||
/// - The resources in this list can't be late resources.
|
||||
/// - The resources that appear in this list can't appear in other list like `idle.resources` or
|
||||
/// `tasks.$TASK.resources`
|
||||
///
|
||||
/// If this key is omitted its value is assumed to be an empty list.
|
||||
///
|
||||
/// # `idle`
|
||||
///
|
||||
/// This key is optional. Its value is a set of key values. All the possible keys are shown below:
|
||||
///
|
||||
/// ``` text
|
||||
/// idle: {
|
||||
/// path: ..,
|
||||
/// resources: [..],
|
||||
/// }
|
||||
/// ```
|
||||
///
|
||||
/// ## `idle.path`
|
||||
///
|
||||
/// This key is optional. Its value is a Rust path, like `foo::bar::baz`, that points to the idle
|
||||
/// loop function.
|
||||
///
|
||||
/// If the key is omitted its value defaults to `idle`.
|
||||
///
|
||||
/// ## `idle.resources`
|
||||
///
|
||||
/// This key is optional. Its value is a list of resources the `idle` loop has access to. The
|
||||
/// resources in this list must be a subset of the resources listed in the top `resources` key.
|
||||
///
|
||||
/// If omitted its value defaults to an empty list.
|
||||
///
|
||||
/// # `tasks`
|
||||
///
|
||||
/// This key is optional. Its value is a list of tasks. Each task itself is a set of key value pair.
|
||||
/// The full syntax is shown below:
|
||||
///
|
||||
/// ``` text
|
||||
/// tasks: {
|
||||
/// $TASK: {
|
||||
/// enabled: ..,
|
||||
/// path: ..,
|
||||
/// priority: ..,
|
||||
/// resources: [..],
|
||||
/// },
|
||||
/// }
|
||||
/// ```
|
||||
///
|
||||
/// If this key is omitted its value is assumed to be an empty list.
|
||||
///
|
||||
/// ## `tasks.$TASK`
|
||||
///
|
||||
/// The key must be either a Cortex-M exception or a device specific interrupt. `PENDSV`, `SVCALL`,
|
||||
/// `SYS_TICK` are considered as exceptions. All other names are assumed to be interrupts.
|
||||
///
|
||||
/// ## `tasks.$TASK.enabled`
|
||||
///
|
||||
/// This key is optional for interrupts and forbidden for exceptions. Its value must be a boolean
|
||||
/// and indicates whether the interrupt will be enabled (`true`) or disabled (`false`) after `init`
|
||||
/// ends and before `idle` starts.
|
||||
///
|
||||
/// If this key is omitted its value defaults to `true`.
|
||||
///
|
||||
/// ## `tasks.$TASK.path`
|
||||
///
|
||||
/// The value of this key is a Rust path, like `foo::bar::baz`, that points to the handler of this
|
||||
/// task.
|
||||
///
|
||||
/// ## `tasks.$TASK.priority`
|
||||
///
|
||||
/// This key is optional. Its value is an integer with type `u8` that specifies the priority of this
|
||||
/// task. The minimum valid priority is 1. The maximum valid priority depends on the number of the
|
||||
/// NVIC priority bits the device has; if the device has 4 priority bits the maximum allowed value
|
||||
/// would be 16.
|
||||
///
|
||||
/// If this key is omitted its value defaults to `1`.
|
||||
///
|
||||
/// ## `tasks.$TASK.resources`
|
||||
///
|
||||
/// This key is optional. Its value is a list of resources this task has access to. The resources in
|
||||
/// this list must be a subset of the resources listed in the top `resources` key.
|
||||
///
|
||||
/// If omitted its value defaults to an empty list.
|
||||
#[proc_macro]
|
||||
pub fn app(ts: TokenStream) -> TokenStream {
|
||||
match run(ts) {
|
||||
|
|
@ -177,10 +30,10 @@ pub fn app(ts: TokenStream) -> TokenStream {
|
|||
|
||||
fn run(ts: TokenStream) -> Result<TokenStream> {
|
||||
let app = App::parse(ts)?.check()?;
|
||||
let app = check::app(app)?;
|
||||
check::app(&app)?;
|
||||
|
||||
let ownerships = analyze::app(&app);
|
||||
let tokens = trans::app(&app, &ownerships);
|
||||
let ctxt = analyze::app(&app);
|
||||
let tokens = trans::app(&ctxt, &app);
|
||||
|
||||
Ok(tokens.into())
|
||||
}
|
||||
|
|
|
|||
1311
macros/src/trans.rs
1311
macros/src/trans.rs
File diff suppressed because it is too large
Load diff
Loading…
Add table
Add a link
Reference in a new issue