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.rs288
1 files changed, 106 insertions, 182 deletions
diff --git a/macros/src/lib.rs b/macros/src/lib.rs
index c8d9fee1..e659559e 100644
--- a/macros/src/lib.rs
+++ b/macros/src/lib.rs
@@ -1,123 +1,119 @@
#![deny(warnings)]
-#![recursion_limit = "128"]
extern crate proc_macro;
use proc_macro::TokenStream;
-use syn::parse_macro_input;
+use std::{fs, path::Path};
+
+use rtic_syntax::Settings;
mod analyze;
mod check;
mod codegen;
-mod syntax;
+#[cfg(test)]
+mod tests;
-/// Attribute used to declare a RTFM application
+/// Attribute used to declare a RTIC application
///
-/// 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,
+/// This attribute must be applied to a module block that contains items commonly found in modules,
/// like functions and `static` variables.
///
/// The `app` attribute has one mandatory argument:
///
/// - `device = <path>`. The path must point to a device crate generated using [`svd2rust`]
-/// **v0.14.x**.
+/// **v0.14.x** or newer.
///
/// [`svd2rust`]: https://crates.io/crates/svd2rust
///
-/// The items allowed in the block value of the `const` item are specified below:
-///
-/// # 1. `static [mut]` variables
-///
-/// 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.
-///
-/// [`lock`]: ../rtfm/trait.Mutex.html#method.lock
+/// and a few optional arguments:
///
-/// `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.
+/// - `peripherals = <bool>`. Indicates whether the runtime takes the device peripherals and makes
+/// them available to the `init` context.
///
-/// [`Send`]: https://doc.rust-lang.org/core/marker/trait.Send.html
-/// [`Sync`]: https://doc.rust-lang.org/core/marker/trait.Sync.html
+/// - `monotonic = <path>`. This is a path to a zero-sized structure (e.g. `struct Foo;`) that
+/// implements the `Monotonic` trait. This argument must be provided to use the `schedule` API.
///
-/// 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 items allowed in the module block are specified below:
///
-/// 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.
+/// # 1. `#[resources] struct <resource-name>`
///
-/// [`Mutex`]: ../rtfm/trait.Mutex.html
+/// This structure contains the declaration of all the resources used by the application. Each field
+/// in this structure corresponds to a different resource. Each resource may optionally be given an
+/// initial value using the `#[init(<value>)]` attribute. Resources with no compile-time initial
+/// value as referred to as *late* resources.
///
/// # 2. `fn`
///
-/// 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.
+/// Functions must contain *one* of the following attributes: `init`, `idle` or `task`. The
+/// attribute defines the role of the function in the application.
///
/// ## a. `#[init]`
///
/// 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 ()`.
+/// signature of the `init` function must be `fn (<fn-name>::Context) [-> <fn-name>::LateResources]`
+/// where `<fn-name>` is the name of the function adorned with the `#[init]` attribute.
///
/// The `init` function runs after memory (RAM) is initialized and runs with interrupts disabled.
/// Interrupts are re-enabled after `init` returns.
///
/// The `init` attribute accepts the following optional arguments:
///
-/// - `resources = [RESOURCE_A, RESOURCE_B, ..]`. This is the list of resources this function has
+/// - `resources = [resource_a, resource_b, ..]`. This is the list of resources this context has
/// access to.
///
-/// - `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.
+/// - `schedule = [task_a, task_b, ..]`. This is the list of *software* tasks that this context can
+/// schedule to run in the future. *IMPORTANT*: This argument is accepted only if the `monotonic`
+/// argument is passed to the `#[app]` attribute.
///
-/// - `spawn = [task_a, task_b, ..]`. This is the list of *software* tasks that this function can
+/// - `spawn = [task_a, task_b, ..]`. This is the list of *software* tasks that this context can
/// immediately spawn.
///
-/// The `app` attribute will injected a *context* into this function that comprises the following
-/// variables:
+/// The first argument of the function, `<fn-name>::Context`, is a structure that contains the
+/// following fields:
///
-/// - `core: rtfm::Peripherals`. Exclusive access to core peripherals. See [`rtfm::Peripherals`] for
-/// more details.
+/// - `core`. Exclusive access to core peripherals. The type of this field is [`rtic::Peripherals`]
+/// when the `schedule` API is used and [`cortex_m::Peripherals`] when it's not.
///
-/// [`rtfm::Peripherals`]: ../rtfm/struct.Peripherals.html
+/// [`rtic::Peripherals`]: ../rtic/struct.Peripherals.html
+/// [`cortex_m::Peripherals`]: https://docs.rs/cortex-m/0.6/cortex_m/peripheral/struct.Peripherals.html
///
-/// - `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.
+/// - `device: <device>::Peripherals`. Exclusive access to device-specific peripherals. This
+/// field is only present when the `peripherals` argument of the `#[app]` attribute is set to
+/// `true`. `<device>` is the path to the device crate specified in the top `app` attribute.
///
-/// - `start: rtfm::Instant`. The `start` time of the system: `Instant(0 /* cycles */)`. **NOTE**:
-/// only present if the `timer-queue` feature is enabled.
+/// - `start: <Instant>`. The `start` time of the system: `<Instant>::zero()`. `<Instant>` is the
+/// `Instant` type associated to the `Monotonic` implementation specified in the top `#[app]`
+/// attribute. **NOTE**: this field is only present when the `schedule` is used.
///
-/// - `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`).
+/// - `resources: <fn-name>::Resources`. A `struct` that contains all the resources that can be
+/// accessed from this context. Each field is a different resource; each resource may appear as a
+/// reference (`&[mut]-`) or as proxy structure that implements the [`rftm::Mutex`][rtic-mutex] trait.
///
-/// - `schedule: init::Schedule`. A `struct` that can be used to schedule *software* tasks.
-/// **NOTE**: only present if the `timer-queue` feature is enabled.
+/// [rtic-mutex]: ../rtic/trait.Mutex.html
///
-/// - `spawn: init::Spawn`. A `struct` that can be used to spawn *software* tasks.
+/// - `schedule: <fn-name>::Schedule`. A `struct` that can be used to schedule *software* tasks.
///
-/// Other properties / constraints:
+/// - `spawn: <fn-name>::Spawn`. A `struct` that can be used to spawn *software* tasks.
+///
+/// The return type `<fn-name>::LateResources` must only be specified when late resources, resources
+/// with no initial value declared at compile time, are used. `<fn-name>::LateResources` is a
+/// structure where each field corresponds to a different late resource. The
+/// `<fn-name>::LateResources` value returned by the `#[init]` function is used to initialize the
+/// late resources before `idle` or any task can start.
///
-/// - The `init` function can **not** be called from software.
+/// Other properties:
///
/// - 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`.
///
-/// - Assignments (e.g. `FOO = 0`) at the end of this function can be used to initialize *late*
-/// resources.
-///
/// ## b. `#[idle]`
///
/// 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() -> !`.
+/// `idle` function must be `fn(<fn-name>::Context) -> !` where `<fn-name>` is the name of the
+/// function adorned with the `#[idle]` attribute.
///
/// 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
@@ -133,38 +129,37 @@ mod syntax;
///
/// - `spawn = (..)`. Same meaning / function as [`#[init].spawn`](#a-init).
///
-/// The `app` attribute will injected a *context* into this function that comprises the following
-/// variables:
-///
-/// - `resources: _`. Same meaning / function as [`init.resources`](#a-init).
+/// The first argument of the function, `idle::Context`, is a structure that contains the following
+/// fields:
///
-/// - `schedule: idle::Schedule`. Same meaning / function as [`init.schedule`](#a-init).
+/// - `resources: _`. Same meaning / function as [`<init>::Context.resources`](#a-init).
///
-/// - `spawn: idle::Spawn`. Same meaning / function as [`init.spawn`](#a-init).
+/// - `schedule: idle::Schedule`. Same meaning / function as [`<init>::Context.schedule`](#a-init).
///
-/// Other properties / constraints:
+/// - `spawn: idle::Spawn`. Same meaning / function as [`<init>::Context.spawn`](#a-init).
///
-/// - The `idle` function can **not** be called from software.
+/// Other properties:
///
/// - 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()`.
+/// ## c. `#[task]`
///
-/// The name of the function must match one of the Cortex-M exceptions that has [configurable
-/// priority][system-handler].
+/// This attribute indicates that the function is either a hardware task or a software task. The
+/// signature of hardware tasks must be `fn(<fn-name>::Context)` whereas the signature of software
+/// tasks must be `fn(<fn-name>::Context, <inputs>)`. `<fn-name>` refers to the name of the function
+/// adorned with the `#[task]` attribute.
///
-/// [system-handler]: ../cortex_m/peripheral/scb/enum.SystemHandler.html
+/// The `task` attribute accepts the following optional arguments.
///
-/// The `exception` attribute accepts the following optional arguments.
+/// - `binds = <interrupt-name>`. Binds this task to a particular interrupt. When this argument is
+/// present the task is treated as a hardware task; when it's omitted the task treated is treated as
+/// a software task.
///
/// - `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
+/// the device crate specified 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).
@@ -173,105 +168,26 @@ mod syntax;
///
/// - `spawn = (..)`. Same meaning / function as [`#[init].spawn`](#a-init).
///
-/// The `app` attribute will injected a *context* into this function that comprises the following
-/// variables:
+/// The first argument of the function, `<fn-name>::Context`, is a structure that contains the
+/// following fields:
///
-/// - `start: rtfm::Instant`. The time at which this handler started executing. **NOTE**: only
-/// present if the `timer-queue` feature is enabled.
+/// - `start: <Instant>`. For hardware tasks this is the time at which this handler started
+/// executing. For software tasks this is the time at which the task was scheduled to run. **NOTE**:
+/// only present when the `schedule` API is used.
///
-/// - `resources: _`. Same meaning / function as [`init.resources`](#a-init).
+/// - `resources: _`. Same meaning / function as [`<init>::Context.resources`](#a-init).
///
-/// - `schedule: <exception-name>::Schedule`. Same meaning / function as [`init.schedule`](#a-init).
+/// - `schedule: <exception-name>::Schedule`. Same meaning / function as
+/// [`<init>::Context.schedule`](#a-init).
///
-/// - `spawn: <exception-name>::Spawn`. Same meaning / function as [`init.spawn`](#a-init).
+/// - `spawn: <exception-name>::Spawn`. Same meaning / function as
+/// [`<init>::Context.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`.
+/// *non*-static `&mut` references that are safe to access. For example, `static mut FOO: u32 = 0`
+/// will become `FOO: &mut u32`.
///
/// # 3. `extern` block
///
@@ -282,27 +198,35 @@ mod syntax;
/// 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.
+/// 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 mut settings = Settings::default();
+ settings.optimize_priorities = true;
+ settings.parse_binds = true;
+ settings.parse_extern_interrupt = true;
+ settings.parse_schedule = true;
- let app = match syntax::App::parse(items, args) {
+ let (app, analysis) = match rtic_syntax::parse(args, input, settings) {
Err(e) => return e.to_compile_error().into(),
- Ok(app) => app,
+ Ok(x) => x,
};
- // Check the specification
- if let Err(e) = check::app(&app) {
- return e.to_compile_error().into();
- }
+ let extra = match check::app(&app, &analysis) {
+ Err(e) => return e.to_compile_error().into(),
+ Ok(x) => x,
+ };
- // Ceiling analysis
- let analysis = analyze::app(&app);
+ let analysis = analyze::app(analysis, &app);
+
+ let ts = codegen::app(&app, &analysis, &extra);
+
+ // Try to write the expanded code to disk
+ if Path::new("target").exists() {
+ fs::write("target/rtic-expansion.rs", ts.to_string()).ok();
+ }
- // Code generation
- codegen::app(&app, &analysis).into()
+ ts.into()
}