1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
#![deny(warnings)]
#![recursion_limit = "128"]

extern crate proc_macro;

use proc_macro::TokenStream;
use std::{fs, path::Path};

use syn::parse_macro_input;

mod analyze;
mod check;
mod codegen;
mod syntax;

/// Attribute used to declare a RTFM 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,
/// 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**.
///
/// [`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
///
/// `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.
///
/// [`Send`]: https://doc.rust-lang.org/core/marker/trait.Send.html
/// [`Sync`]: https://doc.rust-lang.org/core/marker/trait.Sync.html
///
/// 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 `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.
///
/// [`Mutex`]: ../rtfm/trait.Mutex.html
///
/// # 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.
///
/// ## 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 ()`.
///
/// 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
/// 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.
///
/// - `spawn = [task_a, task_b, ..]`. This is the list of *software* tasks that this function can
/// immediately spawn.
///
/// The `app` attribute will injected a *context* into this function that comprises the following
/// variables:
///
/// - `core: rtfm::Peripherals`. Exclusive access to core peripherals. See [`rtfm::Peripherals`] for
/// more details.
///
/// [`rtfm::Peripherals`]: ../rtfm/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.
///
/// - `start: rtfm::Instant`. The `start` time of the system: `Instant(0 /* cycles */)`. **NOTE**:
/// only present if the `timer-queue` feature is enabled.
///
/// - `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`).
///
/// - `schedule: init::Schedule`. A `struct` that can be used to schedule *software* tasks.
/// **NOTE**: only present if the `timer-queue` feature is enabled.
///
/// - `spawn: init::Spawn`. A `struct` that can be used to spawn *software* tasks.
///
/// Other properties / constraints:
///
/// - The `init` function can **not** be called from software.
///
/// - 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() -> !`.
///
/// 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`.
///
/// [SLEEPONEXIT]: https://developer.arm.com/products/architecture/cpu-architecture/m-profile/docs/100737/0100/power-management/sleep-mode/sleep-on-exit-bit
///
/// The `idle` attribute accepts the following optional arguments:
///
/// - `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:
///
/// - `resources: _`. Same meaning / function as [`init.resources`](#a-init).
///
/// - `schedule: idle::Schedule`. Same meaning / function as [`init.schedule`](#a-init).
///
/// - `spawn: idle::Spawn`. Same meaning / function as [`init.spawn`](#a-init).
///
/// Other properties / constraints:
///
/// - The `idle` function can **not** be called from software.
///
/// - 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 input = parse_macro_input!(input as syntax::Input);

    let app = match syntax::App::parse(input.items, args) {
        Err(e) => return e.to_compile_error().into(),
        Ok(app) => app,
    };

    // Check the specification
    if let Err(e) = check::app(&app) {
        return e.to_compile_error().into();
    }

    // Ceiling analysis
    let analysis = analyze::app(&app);

    // Code generation
    let ts = codegen::app(&input.ident, &app, &analysis);

    // Try to write the expanded code to disk
    if Path::new("target").exists() {
        fs::write("target/rtfm-expansion.rs", ts.to_string()).ok();
    }

    ts.into()
}