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