aboutsummaryrefslogtreecommitdiff
path: root/macros/src/lib.rs
diff options
context:
space:
mode:
Diffstat (limited to 'macros/src/lib.rs')
-rw-r--r--macros/src/lib.rs367
1 files changed, 247 insertions, 120 deletions
diff --git a/macros/src/lib.rs b/macros/src/lib.rs
index 65d5ad89..e382b410 100644
--- a/macros/src/lib.rs
+++ b/macros/src/lib.rs
@@ -1,185 +1,312 @@
-//! Procedural macros of the `cortex-m-rtfm` crate
// #![deny(warnings)]
#![recursion_limit = "128"]
-#[macro_use]
-extern crate failure;
extern crate proc_macro;
extern crate proc_macro2;
-extern crate syn;
-#[macro_use]
extern crate quote;
-extern crate rtfm_syntax as syntax;
+extern crate rand;
+extern crate syn;
use proc_macro::TokenStream;
-use syntax::{App, Result};
+use syn::parse_macro_input;
mod analyze;
mod check;
-mod trans;
+mod codegen;
+mod syntax;
-/// The `app!` macro, a macro used to specify the tasks and resources of a RTFM application.
+/// Attribute used to declare a RTFM application
///
-/// The contents of this macro uses a `key: value` syntax. All the possible keys are shown below:
+/// This attribute must be applied to a `const` item of type `()`. The `const` item is effectively
+/// used as a `mod` item: its value must be a block that contains items commonly found in modules,
+/// like functions and `static` variables.
///
-/// ``` text
-/// app! {
-/// device: ..,
+/// The `app` attribute has one mandatory argument:
///
-/// resources: { .. },
+/// - `device = <path>`. The path must point to a device crate generated using [`svd2rust`]
+/// **v0.14.x**.
///
-/// init: { .. },
+/// [`svd2rust`]: https://crates.io/crates/svd2rust
///
-/// idle: { .. },
+/// The items allowed in the block value of the `const` item are specified below:
///
-/// tasks: { .. },
-/// }
-/// ```
+/// # 1. `static [mut]` variables
///
-/// # `device`
+/// These variables are used as *resources*. Resources can be owned by tasks or shared between them.
+/// Tasks can get `&mut` (exclusives) references to `static mut` resources, but only `&` (shared)
+/// references to `static` resources. Lower priority tasks will need a [`lock`] to get a `&mut`
+/// reference to a `static mut` resource shared with higher priority tasks.
///
-/// 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`.
+/// [`lock`]: ../rtfm/trait.Mutex.html#method.lock
///
-/// # `resources`
+/// `static mut` resources that are shared by tasks that run at *different* priorities need to
+/// implement the [`Send`] trait. Similarly, `static` resources that are shared by tasks that run at
+/// *different* priorities need to implement the [`Sync`] trait.
///
-/// 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.
+/// [`Send`]: https://doc.rust-lang.org/core/marker/trait.Send.html
+/// [`Sync`]: https://doc.rust-lang.org/core/marker/trait.Sync.html
///
-/// ``` text
-/// resources: {
-/// static A: bool = false;
-/// static B: i32 = 0;
-/// static C: [u8; 16] = [0; 16];
-/// static D: Thing = Thing::new(..);
-/// static E: Thing;
-/// }
-/// ```
+/// Resources can be initialized at runtime by assigning them `()` (the unit value) as their initial
+/// value in their declaration. These "late" resources need to be initialized an the end of the
+/// `init` function.
///
-/// 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*.
+/// The `app` attribute will inject a `resources` module in the root of the crate. This module
+/// contains proxy `struct`s that implement the [`Mutex`] trait. The `struct` are named after the
+/// `static mut` resources. For example, `static mut FOO: u32 = 0` will map to a `resources::FOO`
+/// `struct` that implements the `Mutex<Data = u32>` trait.
///
-/// If this key is omitted its value defaults to an empty list.
+/// [`Mutex`]: ../rtfm/trait.Mutex.html
///
-/// # `init`
+/// # 2. `fn`
///
-/// This key is optional. Its value is a set of key values. All the possible keys are shown below:
+/// Functions must contain *one* of the following attributes: `init`, `idle`, `interrupt`,
+/// `exception` or `task`. The attribute defines the role of the function in the application.
///
-/// ``` text
-/// init: {
-/// path: ..,
-/// }
-/// ```
+/// ## a. `#[init]`
///
-/// ## `init.path`
+/// This attribute indicates that the function is to be used as the *initialization function*. There
+/// must be exactly one instance of the `init` attribute inside the `app` pseudo-module. The
+/// signature of the `init` function must be `[unsafe] fn ()`.
///
-/// This key is optional. Its value is a Rust path, like `foo::bar::baz`, that points to the
-/// initialization function.
+/// The `init` function runs after memory (RAM) is initialized and runs with interrupts disabled.
+/// Interrupts are re-enabled after `init` returns.
///
-/// If the key is omitted its value defaults to `init`.
+/// The `init` attribute accepts the following optional arguments:
///
-/// ## `init.resources`
+/// - `resources = [RESOURCE_A, RESOURCE_B, ..]`. This is the list of resources this function has
+/// access to.
///
-/// 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:
+/// - `schedule = [task_a, task_b, ..]`. This is the list of *software* tasks that this function can
+/// schedule to run in the future. *IMPORTANT*: This argument is accepted only if the `timer-queue`
+/// feature has been enabled.
///
-/// - 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`
+/// - `spawn = [task_a, task_b, ..]`. This is the list of *software* tasks that this function can
+/// immediately spawn.
///
-/// If this key is omitted its value is assumed to be an empty list.
+/// The `app` attribute will injected a *context* into this function that comprises the following
+/// variables:
///
-/// # `idle`
+/// - `core: rtfm::Peripherals`. Exclusive access to core peripherals. See [`rtfm::Peripherals`] for
+/// more details.
///
-/// This key is optional. Its value is a set of key values. All the possible keys are shown below:
+/// [`rtfm::Peripherals`]: ../rtfm/struct.Peripherals.html
///
-/// ``` text
-/// idle: {
-/// path: ..,
-/// resources: [..],
-/// }
-/// ```
+/// - `device: <device-path>::Peripherals`. Exclusive access to device-specific peripherals.
+/// `<device-path>` is the path to the device crate declared in the top `app` attribute.
///
-/// ## `idle.path`
+/// - `start: rtfm::Instant`. The `start` time of the system: `Instant(0 /* cycles */)`. **NOTE**:
+/// only present if the `timer-queue` feature is enabled.
///
-/// This key is optional. Its value is a Rust path, like `foo::bar::baz`, that points to the idle
-/// loop function.
+/// - `resources: _`. An opaque `struct` that contains all the resources assigned to this function.
+/// The resource maybe appear by value (`impl Singleton`), by references (`&[mut]`) or by proxy
+/// (`impl Mutex`).
///
-/// If the key is omitted its value defaults to `idle`.
+/// - `schedule: init::Schedule`. A `struct` that can be used to schedule *software* tasks.
+/// **NOTE**: only present if the `timer-queue` feature is enabled.
///
-/// ## `idle.resources`
+/// - `spawn: init::Spawn`. A `struct` that can be used to spawn *software* tasks.
///
-/// 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.
+/// Other properties / constraints:
///
-/// If omitted its value defaults to an empty list.
+/// - The `init` function can **not** be called from software.
///
-/// # `tasks`
+/// - The `static mut` variables declared at the beginning of this function will be transformed into
+/// `&'static mut` references that are safe to access. For example, `static mut FOO: u32 = 0` will
+/// become `FOO: &'static mut u32`.
///
-/// 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:
+/// - Assignments (e.g. `FOO = 0`) at the end of this function can be used to initialize *late*
+/// resources.
///
-/// ``` text
-/// tasks: {
-/// $TASK: {
-/// enabled: ..,
-/// path: ..,
-/// priority: ..,
-/// resources: [..],
-/// },
-/// }
-/// ```
+/// ## b. `#[idle]`
///
-/// If this key is omitted its value is assumed to be an empty list.
+/// This attribute indicates that the function is to be used as the *idle task*. There can be at
+/// most once instance of the `idle` attribute inside the `app` pseudo-module. The signature of the
+/// `idle` function must be `fn() -> !`.
///
-/// ## `tasks.$TASK`
+/// The `idle` task is a special task that always runs in the background. The `idle` task runs at
+/// the lowest priority of `0`. If the `idle` task is not defined then the runtime sets the
+/// [SLEEPONEXIT] bit after executing `init`.
///
-/// 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.
+/// [SLEEPONEXIT]: https://developer.arm.com/products/architecture/cpu-architecture/m-profile/docs/100737/0100/power-management/sleep-mode/sleep-on-exit-bit
///
-/// ## `tasks.$TASK.enabled`
+/// The `idle` attribute accepts the following optional arguments:
///
-/// 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.
+/// - `resources = (..)`. Same meaning / function as [`#[init].resources`](#a-init).
///
-/// If this key is omitted its value defaults to `true`.
+/// - `schedule = (..)`. Same meaning / function as [`#[init].schedule`](#a-init).
///
-/// ## `tasks.$TASK.path`
+/// - `spawn = (..)`. Same meaning / function as [`#[init].spawn`](#a-init).
///
-/// The value of this key is a Rust path, like `foo::bar::baz`, that points to the handler of this
-/// task.
+/// The `app` attribute will injected a *context* into this function that comprises the following
+/// variables:
///
-/// ## `tasks.$TASK.priority`
+/// - `resources: _`. Same meaning / function as [`init.resources`](#a-init).
///
-/// 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.
+/// - `schedule: idle::Schedule`. Same meaning / function as [`init.schedule`](#a-init).
///
-/// If this key is omitted its value defaults to `1`.
+/// - `spawn: idle::Spawn`. Same meaning / function as [`init.spawn`](#a-init).
///
-/// ## `tasks.$TASK.resources`
+/// Other properties / constraints:
///
-/// 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.
+/// - The `idle` function can **not** be called from software.
///
-/// If omitted its value defaults to an empty list.
-#[proc_macro]
-pub fn app(ts: TokenStream) -> TokenStream {
- match run(ts) {
- Err(e) => panic!("error: {}", e),
- Ok(ts) => ts,
- }
-}
+/// - The `static mut` variables declared at the beginning of this function will be transformed into
+/// `&'static mut` references that are safe to access. For example, `static mut FOO: u32 = 0` will
+/// become `FOO: &'static mut u32`.
+///
+/// ## c. `#[exception]`
+///
+/// This attribute indicates that the function is to be used as an *exception handler*, a type of
+/// hardware task. The signature of `exception` handlers must be `[unsafe] fn()`.
+///
+/// The name of the function must match one of the Cortex-M exceptions that has [configurable
+/// priority][system-handler].
+///
+/// [system-handler]: ../cortex_m/peripheral/scb/enum.SystemHandler.html
+///
+/// The `exception` attribute accepts the following optional arguments.
+///
+/// - `priority = <integer>`. This is the static priority of the exception handler. The value must
+/// be in the range `1..=(1 << <device-path>::NVIC_PRIO_BITS)` where `<device-path>` is the path to
+/// the device crate declared in the top `app` attribute. If this argument is omitted the priority
+/// is assumed to be 1.
+///
+/// - `resources = (..)`. Same meaning / function as [`#[init].resources`](#a-init).
+///
+/// - `schedule = (..)`. Same meaning / function as [`#[init].schedule`](#a-init).
+///
+/// - `spawn = (..)`. Same meaning / function as [`#[init].spawn`](#a-init).
+///
+/// The `app` attribute will injected a *context* into this function that comprises the following
+/// variables:
+///
+/// - `start: rtfm::Instant`. The time at which this handler started executing. **NOTE**: only
+/// present if the `timer-queue` feature is enabled.
+///
+/// - `resources: _`. Same meaning / function as [`init.resources`](#a-init).
+///
+/// - `schedule: <exception-name>::Schedule`. Same meaning / function as [`init.schedule`](#a-init).
+///
+/// - `spawn: <exception-name>::Spawn`. Same meaning / function as [`init.spawn`](#a-init).
+///
+/// Other properties / constraints:
+///
+/// - `exception` handlers can **not** be called from software.
+///
+/// - The `static mut` variables declared at the beginning of this function will be transformed into
+/// `&mut` references that are safe to access. For example, `static mut FOO: u32 = 0` will
+/// become `FOO: &mut u32`.
+///
+/// ## d. `#[interrupt]`
+///
+/// This attribute indicates that the function is to be used as an *interrupt handler*, a type of
+/// hardware task. The signature of `interrupt` handlers must be `[unsafe] fn()`.
+///
+/// The name of the function must match one of the device specific interrupts. See your device crate
+/// documentation (`Interrupt` enum) for more details.
+///
+/// The `interrupt` attribute accepts the following optional arguments.
+///
+/// - `priority = (..)`. Same meaning / function as [`#[exception].priority`](#b-exception).
+///
+/// - `resources = (..)`. Same meaning / function as [`#[init].resources`](#a-init).
+///
+/// - `schedule = (..)`. Same meaning / function as [`#[init].schedule`](#a-init).
+///
+/// - `spawn = (..)`. Same meaning / function as [`#[init].spawn`](#a-init).
+///
+/// The `app` attribute will injected a *context* into this function that comprises the following
+/// variables:
+///
+/// - `start: rtfm::Instant`. Same meaning / function as [`exception.start`](#b-exception).
+///
+/// - `resources: _`. Same meaning / function as [`init.resources`](#a-init).
+///
+/// - `schedule: <interrupt-name>::Schedule`. Same meaning / function as [`init.schedule`](#a-init).
+///
+/// - `spawn: <interrupt-name>::Spawn`. Same meaning / function as [`init.spawn`](#a-init).
+///
+/// Other properties / constraints:
+///
+/// - `interrupt` handlers can **not** be called from software, but they can be [`pend`]-ed by the
+/// software from any context.
+///
+/// [`pend`]: ../rtfm/fn.pend.html
+///
+/// - The `static mut` variables declared at the beginning of this function will be transformed into
+/// `&mut` references that are safe to access. For example, `static mut FOO: u32 = 0` will
+/// become `FOO: &mut u32`.
+///
+/// ## e. `#[task]`
+///
+/// This attribute indicates that the function is to be used as a *software task*. The signature of
+/// software `task`s must be `[unsafe] fn(<inputs>)`.
+///
+/// The `task` attribute accepts the following optional arguments.
+///
+/// - `capacity = <integer>`. The maximum number of instances of this task that can be queued onto
+/// the task scheduler for execution. The value must be in the range `1..=255`. If the `capacity`
+/// argument is omitted then the capacity will be inferred.
+///
+/// - `priority = <integer>`. Same meaning / function as [`#[exception].priority`](#b-exception).
+///
+/// - `resources = (..)`. Same meaning / function as [`#[init].resources`](#a-init).
+///
+/// - `schedule = (..)`. Same meaning / function as [`#[init].schedule`](#a-init).
+///
+/// - `spawn = (..)`. Same meaning / function as [`#[init].spawn`](#a-init).
+///
+/// The `app` attribute will injected a *context* into this function that comprises the following
+/// variables:
+///
+/// - `scheduled: rtfm::Instant`. The time at which this task was scheduled to run. **NOTE**: Only
+/// present if `timer-queue` is enabled.
+///
+/// - `resources: _`. Same meaning / function as [`init.resources`](#a-init).
+///
+/// - `schedule: <interrupt-name>::Schedule`. Same meaning / function as [`init.schedule`](#a-init).
+///
+/// - `spawn: <interrupt-name>::Spawn`. Same meaning / function as [`init.spawn`](#a-init).
+///
+/// Other properties / constraints:
+///
+/// - Software `task`s can **not** be called from software, but they can be `spawn`-ed and
+/// `schedule`-d by the software from any context.
+///
+/// - The `static mut` variables declared at the beginning of this function will be transformed into
+/// `&mut` references that are safe to access. For example, `static mut FOO: u32 = 0` will
+/// become `FOO: &mut u32`.
+///
+/// # 3. `extern` block
+///
+/// This `extern` block contains a list of interrupts which are *not* used by the application as
+/// hardware tasks. These interrupts will be used to dispatch software tasks. Each interrupt will be
+/// used to dispatch *multiple* software tasks *at the same priority level*.
+///
+/// This `extern` block must only contain functions with signature `fn ()`. The names of these
+/// functions must match the names of the target device interrupts.
+///
+/// Importantly, attributes can be applied to the functions inside this block. These attributes will
+/// be forwarded to the interrupt handlers generated by the `app` attribute.
+#[proc_macro_attribute]
+pub fn app(args: TokenStream, input: TokenStream) -> TokenStream {
+ // Parse
+ let args = parse_macro_input!(args as syntax::AppArgs);
+ let items = parse_macro_input!(input as syntax::Input).items;
+
+ let app = match syntax::App::parse(items, args) {
+ Err(e) => return e.to_compile_error().into(),
+ Ok(app) => app,
+ };
-fn run(ts: TokenStream) -> Result<TokenStream> {
- let app = App::parse(ts)?.check()?;
- let app = check::app(app)?;
+ // Check the specification
+ if let Err(e) = check::app(&app) {
+ return e.to_compile_error().into();
+ }
- let ownerships = analyze::app(&app);
- let tokens = trans::app(&app, &ownerships);
+ // Ceiling analysis
+ let analysis = analyze::app(&app);
- Ok(tokens.into())
+ // Code generation
+ codegen::app(&app, &analysis)
}