cortex_m_rtic_macros/
check.rs

1use std::collections::HashSet;
2
3use proc_macro2::Span;
4use rtic_syntax::{analyze::Analysis, ast::App};
5use syn::{parse, Path};
6
7pub struct Extra {
8    pub device: Path,
9    pub peripherals: bool,
10}
11
12pub fn app(app: &App, _analysis: &Analysis) -> parse::Result<Extra> {
13    // Check that external (device-specific) interrupts are not named after known (Cortex-M)
14    // exceptions
15    for name in app.args.extern_interrupts.keys() {
16        let name_s = name.to_string();
17
18        match &*name_s {
19            "NonMaskableInt" | "HardFault" | "MemoryManagement" | "BusFault" | "UsageFault"
20            | "SecureFault" | "SVCall" | "DebugMonitor" | "PendSV" | "SysTick" => {
21                return Err(parse::Error::new(
22                    name.span(),
23                    "Cortex-M exceptions can't be used as `extern` interrupts",
24                ));
25            }
26
27            _ => {}
28        }
29    }
30
31    // Check that there are enough external interrupts to dispatch the software tasks and the timer
32    // queue handler
33    let mut first = None;
34    let priorities = app
35        .software_tasks
36        .iter()
37        .map(|(name, task)| {
38            first = Some(name);
39            task.args.priority
40        })
41        .collect::<HashSet<_>>();
42
43    let need = priorities.len();
44    let given = app.args.extern_interrupts.len();
45    if need > given {
46        let s = {
47            format!(
48                "not enough interrupts to dispatch \
49                    all software tasks (need: {}; given: {})",
50                need, given
51            )
52        };
53
54        // If not enough tasks and first still is None, may cause
55        // "custom attribute panicked" due to unwrap on None
56        return Err(parse::Error::new(first.unwrap().span(), s));
57    }
58
59    // Check that all exceptions are valid; only exceptions with configurable priorities are
60    // accepted
61    for (name, task) in &app.hardware_tasks {
62        let name_s = task.args.binds.to_string();
63        match &*name_s {
64            "NonMaskableInt" | "HardFault" => {
65                return Err(parse::Error::new(
66                    name.span(),
67                    "only exceptions with configurable priority can be used as hardware tasks",
68                ));
69            }
70
71            _ => {}
72        }
73    }
74
75    if let Some(device) = app.args.device.clone() {
76        Ok(Extra {
77            device,
78            peripherals: app.args.peripherals,
79        })
80    } else {
81        Err(parse::Error::new(
82            Span::call_site(),
83            "a `device` argument must be specified in `#[rtic::app]`",
84        ))
85    }
86}