lpc55_rtic_macros/lib.rs
1#![deny(warnings)]
2
3extern crate proc_macro;
4
5use proc_macro::TokenStream;
6use std::{fs, path::Path};
7
8use rtic_syntax::Settings;
9
10mod analyze;
11mod check;
12mod codegen;
13#[cfg(test)]
14mod tests;
15
16/// Attribute used to declare a RTIC application
17///
18/// This attribute must be applied to a `const` item of type `()`. The `const` item is effectively
19/// used as a `mod` item: its value must be a block that contains items commonly found in modules,
20/// like functions and `static` variables.
21///
22/// The `app` attribute has one mandatory argument:
23///
24/// - `device = <path>`. The path must point to a device crate generated using [`svd2rust`]
25/// **v0.14.x** or newer.
26///
27/// [`svd2rust`]: https://crates.io/crates/svd2rust
28///
29/// and a few optional arguments:
30///
31/// - `peripherals = <bool>`. Indicates whether the runtime takes the device peripherals and makes
32/// them available to the `init` context.
33///
34/// - `monotonic = <path>`. This is a path to a zero-sized structure (e.g. `struct Foo;`) that
35/// implements the `Monotonic` trait. This argument must be provided to use the `schedule` API.
36///
37/// The items allowed in the block value of the `const` item are specified below:
38///
39/// # 1. `struct Resources`
40///
41/// This structure contains the declaration of all the resources used by the application. Each field
42/// in this structure corresponds to a different resource. Each resource may optionally be given an
43/// initial value using the `#[init(<value>)]` attribute. Resources with no compile-time initial
44/// value as referred to as *late* resources.
45///
46/// # 2. `fn`
47///
48/// Functions must contain *one* of the following attributes: `init`, `idle` or `task`. The
49/// attribute defines the role of the function in the application.
50///
51/// ## a. `#[init]`
52///
53/// This attribute indicates that the function is to be used as the *initialization function*. There
54/// must be exactly one instance of the `init` attribute inside the `app` pseudo-module. The
55/// signature of the `init` function must be `fn (<fn-name>::Context) [-> <fn-name>::LateResources]`
56/// where `<fn-name>` is the name of the function adorned with the `#[init]` attribute.
57///
58/// The `init` function runs after memory (RAM) is initialized and runs with interrupts disabled.
59/// Interrupts are re-enabled after `init` returns.
60///
61/// The `init` attribute accepts the following optional arguments:
62///
63/// - `resources = [resource_a, resource_b, ..]`. This is the list of resources this context has
64/// access to.
65///
66/// - `schedule = [task_a, task_b, ..]`. This is the list of *software* tasks that this context can
67/// schedule to run in the future. *IMPORTANT*: This argument is accepted only if the `monotonic`
68/// argument is passed to the `#[app]` attribute.
69///
70/// - `spawn = [task_a, task_b, ..]`. This is the list of *software* tasks that this context can
71/// immediately spawn.
72///
73/// The first argument of the function, `<fn-name>::Context`, is a structure that contains the
74/// following fields:
75///
76/// - `core`. Exclusive access to core peripherals. The type of this field is [`rtic::Peripherals`]
77/// when the `schedule` API is used and [`cortex_m::Peripherals`] when it's not.
78///
79/// [`rtic::Peripherals`]: ../rtic/struct.Peripherals.html
80/// [`cortex_m::Peripherals`]: https://docs.rs/cortex-m/0.6/cortex_m/peripheral/struct.Peripherals.html
81///
82/// - `device: <device>::Peripherals`. Exclusive access to device-specific peripherals. This
83/// field is only present when the `peripherals` argument of the `#[app]` attribute is set to
84/// `true`. `<device>` is the path to the device crate specified in the top `app` attribute.
85///
86/// - `start: <Instant>`. The `start` time of the system: `<Instant>::zero()`. `<Instant>` is the
87/// `Instant` type associated to the `Monotonic` implementation specified in the top `#[app]`
88/// attribute. **NOTE**: this field is only present when the `schedule` is used.
89///
90/// - `resources: <fn-name>::Resources`. A `struct` that contains all the resources that can be
91/// accessed from this context. Each field is a different resource; each resource may appear as a
92/// reference (`&[mut]-`) or as proxy structure that implements the [`rftm::Mutex`][rtic-mutex] trait.
93///
94/// [rtic-mutex]: ../rtic/trait.Mutex.html
95///
96/// - `schedule: <fn-name>::Schedule`. A `struct` that can be used to schedule *software* tasks.
97///
98/// - `spawn: <fn-name>::Spawn`. A `struct` that can be used to spawn *software* tasks.
99///
100/// The return type `<fn-name>::LateResources` must only be specified when late resources, resources
101/// with no initial value declared at compile time, are used. `<fn-name>::LateResources` is a
102/// structure where each field corresponds to a different late resource. The
103/// `<fn-name>::LateResources` value returned by the `#[init]` function is used to initialize the
104/// late resources before `idle` or any task can start.
105///
106/// Other properties:
107///
108/// - The `static mut` variables declared at the beginning of this function will be transformed into
109/// `&'static mut` references that are safe to access. For example, `static mut FOO: u32 = 0` will
110/// become `FOO: &'static mut u32`.
111///
112/// ## b. `#[idle]`
113///
114/// This attribute indicates that the function is to be used as the *idle task*. There can be at
115/// most once instance of the `idle` attribute inside the `app` pseudo-module. The signature of the
116/// `idle` function must be `fn(<fn-name>::Context) -> !` where `<fn-name>` is the name of the
117/// function adorned with the `#[idle]` attribute.
118///
119/// The `idle` task is a special task that always runs in the background. The `idle` task runs at
120/// the lowest priority of `0`. If the `idle` task is not defined then the runtime sets the
121/// [SLEEPONEXIT] bit after executing `init`.
122///
123/// [SLEEPONEXIT]: https://developer.arm.com/products/architecture/cpu-architecture/m-profile/docs/100737/0100/power-management/sleep-mode/sleep-on-exit-bit
124///
125/// The `idle` attribute accepts the following optional arguments:
126///
127/// - `resources = (..)`. Same meaning / function as [`#[init].resources`](#a-init).
128///
129/// - `schedule = (..)`. Same meaning / function as [`#[init].schedule`](#a-init).
130///
131/// - `spawn = (..)`. Same meaning / function as [`#[init].spawn`](#a-init).
132///
133/// The first argument of the function, `idle::Context`, is a structure that contains the following
134/// fields:
135///
136/// - `resources: _`. Same meaning / function as [`<init>::Context.resources`](#a-init).
137///
138/// - `schedule: idle::Schedule`. Same meaning / function as [`<init>::Context.schedule`](#a-init).
139///
140/// - `spawn: idle::Spawn`. Same meaning / function as [`<init>::Context.spawn`](#a-init).
141///
142/// Other properties:
143///
144/// - The `static mut` variables declared at the beginning of this function will be transformed into
145/// `&'static mut` references that are safe to access. For example, `static mut FOO: u32 = 0` will
146/// become `FOO: &'static mut u32`.
147///
148/// ## c. `#[task]`
149///
150/// This attribute indicates that the function is either a hardware task or a software task. The
151/// signature of hardware tasks must be `fn(<fn-name>::Context)` whereas the signature of software
152/// tasks must be `fn(<fn-name>::Context, <inputs>)`. `<fn-name>` refers to the name of the function
153/// adorned with the `#[task]` attribute.
154///
155/// The `task` attribute accepts the following optional arguments.
156///
157/// - `binds = <interrupt-name>`. Binds this task to a particular interrupt. When this argument is
158/// present the task is treated as a hardware task; when it's omitted the task treated is treated as
159/// a software task.
160///
161/// - `priority = <integer>`. This is the static priority of the exception handler. The value must
162/// be in the range `1..=(1 << <device-path>::NVIC_PRIO_BITS)` where `<device-path>` is the path to
163/// the device crate specified in the top `app` attribute. If this argument is omitted the priority
164/// is assumed to be 1.
165///
166/// - `resources = (..)`. Same meaning / function as [`#[init].resources`](#a-init).
167///
168/// - `schedule = (..)`. Same meaning / function as [`#[init].schedule`](#a-init).
169///
170/// - `spawn = (..)`. Same meaning / function as [`#[init].spawn`](#a-init).
171///
172/// The first argument of the function, `<fn-name>::Context`, is a structure that contains the
173/// following fields:
174///
175/// - `start: <Instant>`. For hardware tasks this is the time at which this handler started
176/// executing. For software tasks this is the time at which the task was scheduled to run. **NOTE**:
177/// only present when the `schedule` API is used.
178///
179/// - `resources: _`. Same meaning / function as [`<init>::Context.resources`](#a-init).
180///
181/// - `schedule: <exception-name>::Schedule`. Same meaning / function as
182/// [`<init>::Context.schedule`](#a-init).
183///
184/// - `spawn: <exception-name>::Spawn`. Same meaning / function as
185/// [`<init>::Context.spawn`](#a-init).
186///
187/// Other properties / constraints:
188///
189/// - The `static mut` variables declared at the beginning of this function will be transformed into
190/// *non*-static `&mut` references that are safe to access. For example, `static mut FOO: u32 = 0`
191/// will become `FOO: &mut u32`.
192///
193/// # 3. `extern` block
194///
195/// This `extern` block contains a list of interrupts which are *not* used by the application as
196/// hardware tasks. These interrupts will be used to dispatch software tasks. Each interrupt will be
197/// used to dispatch *multiple* software tasks *at the same priority level*.
198///
199/// This `extern` block must only contain functions with signature `fn ()`. The names of these
200/// functions must match the names of the target device interrupts.
201///
202/// Attributes can be applied to the functions inside this block. These attributes will be forwarded
203/// to the interrupt handlers generated by the `app` attribute.
204#[proc_macro_attribute]
205pub fn app(args: TokenStream, input: TokenStream) -> TokenStream {
206 let mut settings = Settings::default();
207 settings.optimize_priorities = true;
208 settings.parse_binds = true;
209 settings.parse_cores = cfg!(feature = "heterogeneous") || cfg!(feature = "homogeneous");
210 settings.parse_extern_interrupt = true;
211 settings.parse_schedule = true;
212
213 let (app, analysis) = match rtic_syntax::parse(args, input, settings) {
214 Err(e) => return e.to_compile_error().into(),
215 Ok(x) => x,
216 };
217
218 let extra = match check::app(&app, &analysis) {
219 Err(e) => return e.to_compile_error().into(),
220 Ok(x) => x,
221 };
222
223 let analysis = analyze::app(analysis, &app);
224
225 let ts = codegen::app(&app, &analysis, &extra);
226
227 // Try to write the expanded code to disk
228 if Path::new("target").exists() {
229 fs::write("target/rtic-expansion.rs", ts.to_string()).ok();
230 }
231
232 ts.into()
233}