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
#![deny(warnings)]

extern crate proc_macro;

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

use rtic_syntax::Settings;

mod analyze;
mod check;
mod codegen;
#[cfg(test)]
mod tests;

/// 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,
/// 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** or newer.
///
/// [`svd2rust`]: https://crates.io/crates/svd2rust
///
/// and a few optional arguments:
///
/// - `peripherals = <bool>`. Indicates whether the runtime takes the device peripherals and makes
/// them available to the `init` context.
///
/// - `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.
///
/// The items allowed in the block value of the `const` item are specified below:
///
/// # 1. `struct Resources`
///
/// 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` 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 `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 context has
/// access to.
///
/// - `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 context can
/// immediately spawn.
///
/// The first argument of the function, `<fn-name>::Context`, is a structure that contains the
/// following fields:
///
/// - `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.
///
/// [`rtic::Peripherals`]: ../rtic/struct.Peripherals.html
/// [`cortex_m::Peripherals`]: https://docs.rs/cortex-m/0.6/cortex_m/peripheral/struct.Peripherals.html
///
/// - `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: <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: <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.
///
/// [rtic-mutex]: ../rtic/trait.Mutex.html
///
/// - `schedule: <fn-name>::Schedule`. A `struct` that can be used to schedule *software* tasks.
///
/// - `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.
///
/// 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`.
///
/// ## 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(<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
/// [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 first argument of the function, `idle::Context`, is a structure that contains the following
/// fields:
///
/// - `resources: _`. Same meaning / function as [`<init>::Context.resources`](#a-init).
///
/// - `schedule: idle::Schedule`. Same meaning / function as [`<init>::Context.schedule`](#a-init).
///
/// - `spawn: idle::Spawn`. Same meaning / function as [`<init>::Context.spawn`](#a-init).
///
/// 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. `#[task]`
///
/// 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.
///
/// The `task` 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 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).
///
/// - `schedule = (..)`. Same meaning / function as [`#[init].schedule`](#a-init).
///
/// - `spawn = (..)`. Same meaning / function as [`#[init].spawn`](#a-init).
///
/// The first argument of the function, `<fn-name>::Context`, is a structure that contains the
/// following fields:
///
/// - `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>::Context.resources`](#a-init).
///
/// - `schedule: <exception-name>::Schedule`. Same meaning / function as
/// [`<init>::Context.schedule`](#a-init).
///
/// - `spawn: <exception-name>::Spawn`.  Same meaning / function as
/// [`<init>::Context.spawn`](#a-init).
///
/// Other properties / constraints:
///
/// - The `static mut` variables declared at the beginning of this function will be transformed into
/// *non*-static `&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.
///
/// 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 {
    let mut settings = Settings::default();
    settings.optimize_priorities = true;
    settings.parse_binds = true;
    settings.parse_cores = cfg!(feature = "heterogeneous") || cfg!(feature = "homogeneous");
    settings.parse_extern_interrupt = true;
    settings.parse_schedule = true;

    let (app, analysis) = match rtic_syntax::parse(args, input, settings) {
        Err(e) => return e.to_compile_error().into(),
        Ok(x) => x,
    };

    let extra = match check::app(&app, &analysis) {
        Err(e) => return e.to_compile_error().into(),
        Ok(x) => x,
    };

    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();
    }

    ts.into()
}