rtic_macros/codegen/bindings/
cortex.rs
1use crate::{
2 analyze::Analysis as CodegenAnalysis,
3 codegen::util,
4 syntax::{analyze::Analysis as SyntaxAnalysis, ast::App},
5};
6use proc_macro2::{Span, TokenStream as TokenStream2};
7use quote::quote;
8use std::collections::HashSet;
9use syn::{parse, Attribute, Ident};
10
11#[cfg(feature = "cortex-m-basepri")]
12pub use basepri::*;
13#[cfg(feature = "cortex-m-source-masking")]
14pub use source_masking::*;
15
16fn is_exception(name: &Ident) -> bool {
18 let s = name.to_string();
19
20 matches!(
21 &*s,
22 "MemoryManagement"
23 | "BusFault"
24 | "UsageFault"
25 | "SecureFault"
26 | "SVCall"
27 | "DebugMonitor"
28 | "PendSV"
29 | "SysTick"
30 )
31}
32
33pub fn interrupt_ident() -> Ident {
34 let span = Span::call_site();
35 Ident::new("interrupt", span)
36}
37
38pub fn interrupt_mod(app: &App) -> TokenStream2 {
39 let device = &app.args.device;
40 let interrupt = interrupt_ident();
41 quote!(#device::#interrupt)
42}
43
44pub fn check_stack_overflow_before_init(
45 _app: &App,
46 _analysis: &CodegenAnalysis,
47) -> Vec<TokenStream2> {
48 vec![quote!(
49 extern "C" {
51 pub static _stack_start: u32;
52 pub static __ebss: u32;
53 }
54
55 let stack_start = &_stack_start as *const _ as u32;
56 let ebss = &__ebss as *const _ as u32;
57
58 if stack_start > ebss {
59 if rtic::export::msp::read() <= ebss {
61 panic!("Stack overflow after allocating executors");
62 }
63 }
64 )]
65}
66
67#[cfg(feature = "cortex-m-source-masking")]
68mod source_masking {
69 use super::*;
70 use std::collections::HashMap;
71
72 #[allow(clippy::too_many_arguments)]
74 pub fn impl_mutex(
75 app: &App,
76 analysis: &CodegenAnalysis,
77 cfgs: &[Attribute],
78 resources_prefix: bool,
79 name: &Ident,
80 ty: &TokenStream2,
81 ceiling: u8,
82 ptr: &TokenStream2,
83 ) -> TokenStream2 {
84 let path = if resources_prefix {
85 quote!(shared_resources::#name)
86 } else {
87 quote!(#name)
88 };
89
90 let interrupt_ids = analysis.interrupts.iter().map(|(p, (id, _))| (p, id));
92
93 let mut prio_to_masks = HashMap::new();
94 let device = &app.args.device;
95 let mut mask_ids = Vec::new();
98
99 for (&priority, name) in interrupt_ids.chain(app.hardware_tasks.values().flat_map(|task| {
100 if !is_exception(&task.args.binds) {
101 Some((&task.args.priority, &task.args.binds))
102 } else {
103 None
104 }
105 })) {
106 let v: &mut Vec<_> = prio_to_masks.entry(priority - 1).or_default();
107 v.push(quote!(#device::Interrupt::#name as u32));
108 mask_ids.push(quote!(#device::Interrupt::#name as u32));
109 }
110
111 let mut mask_arr = Vec::new();
114 for i in 0..3 {
116 let v = if let Some(v) = prio_to_masks.get(&i) {
117 v.clone()
118 } else {
119 Vec::new()
120 };
121
122 mask_arr.push(quote!(
123 rtic::export::create_mask([#(#v),*])
124 ));
125 }
126
127 quote!(
128 #(#cfgs)*
129 impl<'a> rtic::Mutex for #path<'a> {
130 type T = #ty;
131
132 #[inline(always)]
133 fn lock<RTIC_INTERNAL_R>(&mut self, f: impl FnOnce(&mut #ty) -> RTIC_INTERNAL_R) -> RTIC_INTERNAL_R {
134 const CEILING: u8 = #ceiling;
136 const N_CHUNKS: usize = rtic::export::compute_mask_chunks([#(#mask_ids),*]);
137 const MASKS: [rtic::export::Mask<N_CHUNKS>; 3] = [#(#mask_arr),*];
138
139 unsafe {
140 rtic::export::lock(
141 #ptr,
142 CEILING,
143 &MASKS,
144 f,
145 )
146 }
147 }
148 }
149 )
150 }
151
152 pub fn extra_assertions(_: &App, _: &SyntaxAnalysis) -> Vec<TokenStream2> {
153 vec![]
154 }
155}
156
157#[cfg(feature = "cortex-m-basepri")]
158mod basepri {
159 use super::*;
160
161 #[allow(clippy::too_many_arguments)]
163 pub fn impl_mutex(
164 app: &App,
165 _analysis: &CodegenAnalysis,
166 cfgs: &[Attribute],
167 resources_prefix: bool,
168 name: &Ident,
169 ty: &TokenStream2,
170 ceiling: u8,
171 ptr: &TokenStream2,
172 ) -> TokenStream2 {
173 let path = if resources_prefix {
174 quote!(shared_resources::#name)
175 } else {
176 quote!(#name)
177 };
178
179 let device = &app.args.device;
180 quote!(
181 #(#cfgs)*
182 impl<'a> rtic::Mutex for #path<'a> {
183 type T = #ty;
184
185 #[inline(always)]
186 fn lock<RTIC_INTERNAL_R>(&mut self, f: impl FnOnce(&mut #ty) -> RTIC_INTERNAL_R) -> RTIC_INTERNAL_R {
187 const CEILING: u8 = #ceiling;
189
190 unsafe {
191 rtic::export::lock(
192 #ptr,
193 CEILING,
194 #device::NVIC_PRIO_BITS,
195 f,
196 )
197 }
198 }
199 }
200 )
201 }
202
203 pub fn extra_assertions(_: &App, _: &SyntaxAnalysis) -> Vec<TokenStream2> {
204 vec![]
205 }
206}
207
208pub fn pre_init_preprocessing(_app: &mut App, _analysis: &SyntaxAnalysis) -> parse::Result<()> {
209 Ok(())
210}
211
212pub fn pre_init_checks(app: &App, _: &SyntaxAnalysis) -> Vec<TokenStream2> {
213 let mut stmts = vec![];
214
215 let interrupt = interrupt_ident();
218 let rt_err = util::rt_err_ident();
219
220 for name in app.args.dispatchers.keys() {
221 stmts.push(quote!(let _ = #rt_err::#interrupt::#name;));
222 }
223
224 stmts
225}
226
227pub fn pre_init_enable_interrupts(app: &App, analysis: &CodegenAnalysis) -> Vec<TokenStream2> {
228 let mut stmts = vec![];
229
230 let interrupt = interrupt_ident();
231 let rt_err = util::rt_err_ident();
232 let device = &app.args.device;
233 let nvic_prio_bits = quote!(#device::NVIC_PRIO_BITS);
234 let interrupt_ids = analysis.interrupts.iter().map(|(p, (id, _))| (p, id));
235
236 for (&priority, name) in interrupt_ids.chain(app.hardware_tasks.values().filter_map(|task| {
238 if is_exception(&task.args.binds) {
239 None
241 } else {
242 Some((&task.args.priority, &task.args.binds))
243 }
244 })) {
245 let es = format!(
246 "Maximum priority used by interrupt vector '{name}' is more than supported by hardware"
247 );
248 stmts.push(quote!(
250 const _: () = if (1 << #nvic_prio_bits) < #priority as usize { ::core::panic!(#es); };
251 ));
252
253 stmts.push(quote!(
254 core.NVIC.set_priority(
255 #rt_err::#interrupt::#name,
256 rtic::export::cortex_logical2hw(#priority, #nvic_prio_bits),
257 );
258 ));
259
260 stmts.push(quote!(rtic::export::NVIC::unmask(#rt_err::#interrupt::#name);));
263 }
264
265 for (name, priority) in app.hardware_tasks.values().filter_map(|task| {
267 if is_exception(&task.args.binds) {
268 Some((&task.args.binds, task.args.priority))
269 } else {
270 None
271 }
272 }) {
273 let es = format!(
274 "Maximum priority used by interrupt vector '{name}' is more than supported by hardware"
275 );
276 stmts.push(quote!(
278 const _: () = if (1 << #nvic_prio_bits) < #priority as usize { ::core::panic!(#es); };
279 ));
280
281 stmts.push(quote!(core.SCB.set_priority(
282 rtic::export::SystemHandler::#name,
283 rtic::export::cortex_logical2hw(#priority, #nvic_prio_bits),
284 );));
285 }
286
287 stmts
288}
289
290pub fn architecture_specific_analysis(app: &App, _: &SyntaxAnalysis) -> parse::Result<()> {
291 for name in app.args.dispatchers.keys() {
294 let name_s = name.to_string();
295
296 match &*name_s {
297 "NonMaskableInt" | "HardFault" | "MemoryManagement" | "BusFault" | "UsageFault"
298 | "SecureFault" | "SVCall" | "DebugMonitor" | "PendSV" | "SysTick" => {
299 return Err(parse::Error::new(
300 name.span(),
301 "Cortex-M exceptions can't be used as `extern` interrupts",
302 ));
303 }
304
305 _ => {}
306 }
307 }
308
309 let mut first = None;
312 let priorities = app
313 .software_tasks
314 .iter()
315 .map(|(name, task)| {
316 first = Some(name);
317 task.args.priority
318 })
319 .filter(|prio| *prio > 0)
320 .collect::<HashSet<_>>();
321
322 let need = priorities.len();
323 let given = app.args.dispatchers.len();
324 if need > given {
325 let s = {
326 format!(
327 "not enough interrupts to dispatch \
328 all software tasks (need: {need}; given: {given})"
329 )
330 };
331
332 return Err(parse::Error::new(first.unwrap().span(), s));
335 }
336
337 for (name, task) in &app.hardware_tasks {
340 let name_s = task.args.binds.to_string();
341 match &*name_s {
342 "NonMaskableInt" | "HardFault" => {
343 return Err(parse::Error::new(
344 name.span(),
345 "only exceptions with configurable priority can be used as hardware tasks",
346 ));
347 }
348
349 _ => {}
350 }
351 }
352
353 Ok(())
354}
355
356pub fn interrupt_entry(_app: &App, _analysis: &CodegenAnalysis) -> Vec<TokenStream2> {
357 vec![]
358}
359
360pub fn interrupt_exit(_app: &App, _analysis: &CodegenAnalysis) -> Vec<TokenStream2> {
361 vec![]
362}
363
364pub fn async_entry(
365 _app: &App,
366 _analysis: &CodegenAnalysis,
367 _dispatcher_name: Ident,
368) -> Vec<TokenStream2> {
369 vec![]
370}
371
372pub fn async_prio_limit(app: &App, analysis: &CodegenAnalysis) -> Vec<TokenStream2> {
373 let max = if let Some(max) = analysis.max_async_prio {
374 quote!(#max)
375 } else {
376 let device = &app.args.device;
378 quote!(1 << #device::NVIC_PRIO_BITS)
379 };
380
381 vec![quote!(
382 #[no_mangle]
384 static RTIC_ASYNC_MAX_LOGICAL_PRIO: u8 = #max;
385 )]
386}
387pub fn handler_config(
388 _app: &App,
389 _analysis: &CodegenAnalysis,
390 _dispatcher_name: Ident,
391) -> Vec<TokenStream2> {
392 vec![]
393}
394
395pub fn extra_modules(_app: &App, _analysis: &SyntaxAnalysis) -> Vec<TokenStream2> {
396 vec![]
397}