cortex_m_rtic_macros/codegen/
util.rs

1use core::sync::atomic::{AtomicUsize, Ordering};
2
3use proc_macro2::{Span, TokenStream as TokenStream2};
4use quote::quote;
5use rtic_syntax::{ast::App, Context};
6use syn::{Attribute, Ident, LitInt, PatType};
7
8use crate::check::Extra;
9
10const RTIC_INTERNAL: &str = "__rtic_internal";
11
12/// Turns `capacity` into an unsuffixed integer literal
13pub fn capacity_literal(capacity: usize) -> LitInt {
14    LitInt::new(&capacity.to_string(), Span::call_site())
15}
16
17/// Identifier for the free queue
18pub fn fq_ident(task: &Ident) -> Ident {
19    mark_internal_name(&format!("{}_FQ", task))
20}
21
22/// Generates a `Mutex` implementation
23pub fn impl_mutex(
24    extra: &Extra,
25    cfgs: &[Attribute],
26    resources_prefix: bool,
27    name: &Ident,
28    ty: &TokenStream2,
29    ceiling: u8,
30    ptr: &TokenStream2,
31) -> TokenStream2 {
32    let (path, priority) = if resources_prefix {
33        (quote!(shared_resources::#name), quote!(self.priority()))
34    } else {
35        (quote!(#name), quote!(self.priority))
36    };
37
38    let device = &extra.device;
39    let masks_name = priority_masks_ident();
40    quote!(
41        #(#cfgs)*
42        impl<'a> rtic::Mutex for #path<'a> {
43            type T = #ty;
44
45            #[inline(always)]
46            fn lock<RTIC_INTERNAL_R>(&mut self, f: impl FnOnce(&mut #ty) -> RTIC_INTERNAL_R) -> RTIC_INTERNAL_R {
47                /// Priority ceiling
48                const CEILING: u8 = #ceiling;
49
50                unsafe {
51                    rtic::export::lock(
52                        #ptr,
53                        #priority,
54                        CEILING,
55                        #device::NVIC_PRIO_BITS,
56                        &#masks_name,
57                        f,
58                    )
59                }
60            }
61        }
62    )
63}
64
65/// Generates an identifier for the `INPUTS` buffer (`spawn` & `schedule` API)
66pub fn inputs_ident(task: &Ident) -> Ident {
67    mark_internal_name(&format!("{}_INPUTS", task))
68}
69
70/// Generates an identifier for the `INSTANTS` buffer (`schedule` API)
71pub fn monotonic_instants_ident(task: &Ident, monotonic: &Ident) -> Ident {
72    mark_internal_name(&format!("{}_{}_INSTANTS", task, monotonic))
73}
74
75pub fn interrupt_ident() -> Ident {
76    let span = Span::call_site();
77    Ident::new("interrupt", span)
78}
79
80pub fn timer_queue_marker_ident() -> Ident {
81    mark_internal_name("TIMER_QUEUE_MARKER")
82}
83
84/// Whether `name` is an exception with configurable priority
85pub fn is_exception(name: &Ident) -> bool {
86    let s = name.to_string();
87
88    matches!(
89        &*s,
90        "MemoryManagement"
91            | "BusFault"
92            | "UsageFault"
93            | "SecureFault"
94            | "SVCall"
95            | "DebugMonitor"
96            | "PendSV"
97            | "SysTick"
98    )
99}
100
101/// Mark a name as internal
102pub fn mark_internal_name(name: &str) -> Ident {
103    Ident::new(&format!("{}_{}", RTIC_INTERNAL, name), Span::call_site())
104}
105
106/// Generate an internal identifier for monotonics
107pub fn internal_monotonics_ident(task: &Ident, monotonic: &Ident, ident_name: &str) -> Ident {
108    mark_internal_name(&format!("{}_{}_{}", task, monotonic, ident_name,))
109}
110
111/// Generate an internal identifier for tasks
112pub fn internal_task_ident(task: &Ident, ident_name: &str) -> Ident {
113    mark_internal_name(&format!("{}_{}", task, ident_name))
114}
115
116fn link_section_index() -> usize {
117    static INDEX: AtomicUsize = AtomicUsize::new(0);
118
119    INDEX.fetch_add(1, Ordering::Relaxed)
120}
121
122/// Add `link_section` attribute
123pub fn link_section_uninit() -> TokenStream2 {
124    let section = format!(".uninit.rtic{}", link_section_index());
125
126    quote!(#[link_section = #section])
127}
128
129// Regroups the inputs of a task
130//
131// `inputs` could be &[`input: Foo`] OR &[`mut x: i32`, `ref y: i64`]
132pub fn regroup_inputs(
133    inputs: &[PatType],
134) -> (
135    // args e.g. &[`_0`],  &[`_0: i32`, `_1: i64`]
136    Vec<TokenStream2>,
137    // tupled e.g. `_0`, `(_0, _1)`
138    TokenStream2,
139    // untupled e.g. &[`_0`], &[`_0`, `_1`]
140    Vec<TokenStream2>,
141    // ty e.g. `Foo`, `(i32, i64)`
142    TokenStream2,
143) {
144    if inputs.len() == 1 {
145        let ty = &inputs[0].ty;
146
147        (
148            vec![quote!(_0: #ty)],
149            quote!(_0),
150            vec![quote!(_0)],
151            quote!(#ty),
152        )
153    } else {
154        let mut args = vec![];
155        let mut pats = vec![];
156        let mut tys = vec![];
157
158        for (i, input) in inputs.iter().enumerate() {
159            let i = Ident::new(&format!("_{}", i), Span::call_site());
160            let ty = &input.ty;
161
162            args.push(quote!(#i: #ty));
163
164            pats.push(quote!(#i));
165
166            tys.push(quote!(#ty));
167        }
168
169        let tupled = {
170            let pats = pats.clone();
171            quote!((#(#pats,)*))
172        };
173        let ty = quote!((#(#tys,)*));
174        (args, tupled, pats, ty)
175    }
176}
177
178/// Get the ident for the name of the task
179pub fn get_task_name(ctxt: Context, app: &App) -> Ident {
180    let s = match ctxt {
181        Context::Init => app.init.name.to_string(),
182        Context::Idle => app.idle.as_ref().unwrap().name.to_string(),
183        Context::HardwareTask(ident) | Context::SoftwareTask(ident) => ident.to_string(),
184    };
185
186    Ident::new(&s, Span::call_site())
187}
188
189/// Generates a pre-reexport identifier for the "shared resources" struct
190pub fn shared_resources_ident(ctxt: Context, app: &App) -> Ident {
191    let mut s = match ctxt {
192        Context::Init => app.init.name.to_string(),
193        Context::Idle => app.idle.as_ref().unwrap().name.to_string(),
194        Context::HardwareTask(ident) | Context::SoftwareTask(ident) => ident.to_string(),
195    };
196
197    s.push_str("SharedResources");
198
199    mark_internal_name(&s)
200}
201
202/// Generates a pre-reexport identifier for the "local resources" struct
203pub fn local_resources_ident(ctxt: Context, app: &App) -> Ident {
204    let mut s = match ctxt {
205        Context::Init => app.init.name.to_string(),
206        Context::Idle => app.idle.as_ref().unwrap().name.to_string(),
207        Context::HardwareTask(ident) | Context::SoftwareTask(ident) => ident.to_string(),
208    };
209
210    s.push_str("LocalResources");
211
212    mark_internal_name(&s)
213}
214
215/// Generates an identifier for a ready queue
216///
217/// There may be several task dispatchers, one for each priority level.
218/// The ready queues are SPSC queues
219pub fn rq_ident(priority: u8) -> Ident {
220    mark_internal_name(&format!("P{}_RQ", priority))
221}
222
223/// Generates an identifier for the `enum` of `schedule`-able tasks
224pub fn schedule_t_ident() -> Ident {
225    Ident::new("SCHED_T", Span::call_site())
226}
227
228/// Generates an identifier for the `enum` of `spawn`-able tasks
229///
230/// This identifier needs the same structure as the `RQ` identifier because there's one ready queue
231/// for each of these `T` enums
232pub fn spawn_t_ident(priority: u8) -> Ident {
233    Ident::new(&format!("P{}_T", priority), Span::call_site())
234}
235
236/// Suffixed identifier
237pub fn suffixed(name: &str) -> Ident {
238    let span = Span::call_site();
239    Ident::new(name, span)
240}
241
242/// Generates an identifier for a timer queue
243pub fn tq_ident(name: &str) -> Ident {
244    mark_internal_name(&format!("TQ_{}", name))
245}
246
247/// Generates an identifier for monotonic timer storage
248pub fn monotonic_ident(name: &str) -> Ident {
249    mark_internal_name(&format!("MONOTONIC_STORAGE_{}", name))
250}
251
252pub fn static_shared_resource_ident(name: &Ident) -> Ident {
253    mark_internal_name(&format!("shared_resource_{}", name))
254}
255
256/// Generates an Ident for the number of 32 bit chunks used for Mask storage.
257pub fn priority_mask_chunks_ident() -> Ident {
258    mark_internal_name("MASK_CHUNKS")
259}
260
261pub fn priority_masks_ident() -> Ident {
262    mark_internal_name("MASKS")
263}
264
265pub fn static_local_resource_ident(name: &Ident) -> Ident {
266    mark_internal_name(&format!("local_resource_{}", name))
267}
268
269pub fn declared_static_local_resource_ident(name: &Ident, task_name: &Ident) -> Ident {
270    mark_internal_name(&format!("local_{}_{}", task_name, name))
271}
272
273pub fn need_to_lock_ident(name: &Ident) -> Ident {
274    Ident::new(&format!("{}_that_needs_to_be_locked", name), name.span())
275}
276
277/// The name to get better RT flag errors
278pub fn rt_err_ident() -> Ident {
279    Ident::new(
280        "you_must_enable_the_rt_feature_for_the_pac_in_your_cargo_toml",
281        Span::call_site(),
282    )
283}