cortex_m_rtic_macros/codegen/
module.rs

1use crate::{analyze::Analysis, check::Extra, codegen::util};
2use proc_macro2::TokenStream as TokenStream2;
3use quote::quote;
4use rtic_syntax::{ast::App, Context};
5
6#[allow(clippy::too_many_lines)]
7pub fn codegen(
8    ctxt: Context,
9    shared_resources_tick: bool,
10    local_resources_tick: bool,
11    app: &App,
12    analysis: &Analysis,
13    extra: &Extra,
14) -> TokenStream2 {
15    let mut items = vec![];
16    let mut module_items = vec![];
17    let mut fields = vec![];
18    let mut values = vec![];
19
20    let name = ctxt.ident(app);
21
22    let mut lt = None;
23    match ctxt {
24        Context::Init => {
25            fields.push(quote!(
26                /// Core (Cortex-M) peripherals
27                pub core: rtic::export::Peripherals
28            ));
29
30            if extra.peripherals {
31                let device = &extra.device;
32
33                fields.push(quote!(
34                    /// Device peripherals
35                    pub device: #device::Peripherals
36                ));
37
38                values.push(quote!(device: #device::Peripherals::steal()));
39            }
40
41            lt = Some(quote!('a));
42            fields.push(quote!(
43                /// Critical section token for init
44                pub cs: rtic::export::CriticalSection<#lt>
45            ));
46
47            values.push(quote!(cs: rtic::export::CriticalSection::new()));
48
49            values.push(quote!(core));
50        }
51
52        Context::Idle | Context::HardwareTask(_) | Context::SoftwareTask(_) => {}
53    }
54
55    // if ctxt.has_locals(app) {
56    //     let ident = util::locals_ident(ctxt, app);
57    //     module_items.push(quote!(
58    //         #[doc(inline)]
59    //         pub use super::#ident as Locals;
60    //     ));
61    // }
62
63    if ctxt.has_local_resources(app) {
64        let ident = util::local_resources_ident(ctxt, app);
65        let lt = if local_resources_tick {
66            lt = Some(quote!('a));
67            Some(quote!('a))
68        } else {
69            None
70        };
71
72        module_items.push(quote!(
73            #[doc(inline)]
74            pub use super::#ident as LocalResources;
75        ));
76
77        fields.push(quote!(
78            /// Local Resources this task has access to
79            pub local: #name::LocalResources<#lt>
80        ));
81
82        values.push(quote!(local: #name::LocalResources::new()));
83    }
84
85    if ctxt.has_shared_resources(app) {
86        let ident = util::shared_resources_ident(ctxt, app);
87        let lt = if shared_resources_tick {
88            lt = Some(quote!('a));
89            Some(quote!('a))
90        } else {
91            None
92        };
93
94        module_items.push(quote!(
95            #[doc(inline)]
96            pub use super::#ident as SharedResources;
97        ));
98
99        fields.push(quote!(
100            /// Shared Resources this task has access to
101            pub shared: #name::SharedResources<#lt>
102        ));
103
104        let priority = if ctxt.is_init() {
105            None
106        } else {
107            Some(quote!(priority))
108        };
109        values.push(quote!(shared: #name::SharedResources::new(#priority)));
110    }
111
112    if let Context::Init = ctxt {
113        let monotonic_types: Vec<_> = app
114            .monotonics
115            .iter()
116            .map(|(_, monotonic)| {
117                let cfgs = &monotonic.cfgs;
118                let mono = &monotonic.ty;
119                quote! {
120                    #(#cfgs)*
121                    pub #mono
122                }
123            })
124            .collect();
125
126        let internal_monotonics_ident = util::mark_internal_name("Monotonics");
127
128        items.push(quote!(
129            /// Monotonics used by the system
130            #[allow(non_snake_case)]
131            #[allow(non_camel_case_types)]
132            pub struct #internal_monotonics_ident(
133                #(#monotonic_types),*
134            );
135        ));
136
137        module_items.push(quote!(
138            #[doc(inline)]
139            pub use super::#internal_monotonics_ident as Monotonics;
140        ));
141    }
142
143    let doc = match ctxt {
144        Context::Idle => " Idle loop",
145        Context::Init => " Initialization function",
146        Context::HardwareTask(_) => " Hardware task",
147        Context::SoftwareTask(_) => " Software task",
148    };
149
150    let v = Vec::new();
151    let cfgs = match ctxt {
152        Context::HardwareTask(t) => {
153            &app.hardware_tasks[t].cfgs
154            // ...
155        }
156        Context::SoftwareTask(t) => {
157            &app.software_tasks[t].cfgs
158            // ...
159        }
160        _ => &v,
161    };
162
163    let core = if ctxt.is_init() {
164        Some(quote!(core: rtic::export::Peripherals,))
165    } else {
166        None
167    };
168
169    let priority = if ctxt.is_init() {
170        None
171    } else {
172        Some(quote!(priority: &#lt rtic::export::Priority))
173    };
174
175    let internal_context_name = util::internal_task_ident(name, "Context");
176
177    items.push(quote!(
178        /// Execution context
179        #(#cfgs)*
180        #[allow(non_snake_case)]
181        #[allow(non_camel_case_types)]
182        pub struct #internal_context_name<#lt> {
183            #(#fields,)*
184        }
185
186        #(#cfgs)*
187        impl<#lt> #internal_context_name<#lt> {
188            #[doc(hidden)]
189            #[inline(always)]
190            pub unsafe fn new(#core #priority) -> Self {
191                #internal_context_name {
192                    #(#values,)*
193                }
194            }
195        }
196    ));
197
198    module_items.push(quote!(
199        #[doc(inline)]
200        #(#cfgs)*
201        pub use super::#internal_context_name as Context;
202    ));
203
204    if let Context::SoftwareTask(..) = ctxt {
205        let spawnee = &app.software_tasks[name];
206        let priority = spawnee.args.priority;
207        let t = util::spawn_t_ident(priority);
208        let cfgs = &spawnee.cfgs;
209        let (args, tupled, untupled, ty) = util::regroup_inputs(&spawnee.inputs);
210        let args = &args;
211        let tupled = &tupled;
212        let fq = util::fq_ident(name);
213        let rq = util::rq_ident(priority);
214        let inputs = util::inputs_ident(name);
215
216        let device = &extra.device;
217        let enum_ = util::interrupt_ident();
218        let interrupt = &analysis
219            .interrupts
220            .get(&priority)
221            .expect("RTIC-ICE: interrupt identifer not found")
222            .0;
223
224        let internal_spawn_ident = util::internal_task_ident(name, "spawn");
225
226        // Spawn caller
227        items.push(quote!(
228
229        /// Spawns the task directly
230        #(#cfgs)*
231        pub fn #internal_spawn_ident(#(#args,)*) -> Result<(), #ty> {
232            let input = #tupled;
233
234            unsafe {
235                if let Some(index) = rtic::export::interrupt::free(|_| (&mut *#fq.get_mut()).dequeue()) {
236                    (&mut *#inputs
237                        .get_mut())
238                        .get_unchecked_mut(usize::from(index))
239                        .as_mut_ptr()
240                        .write(input);
241
242                    rtic::export::interrupt::free(|_| {
243                        (&mut *#rq.get_mut()).enqueue_unchecked((#t::#name, index));
244                    });
245
246                    rtic::pend(#device::#enum_::#interrupt);
247
248                    Ok(())
249                } else {
250                    Err(input)
251                }
252            }
253
254        }));
255
256        module_items.push(quote!(
257            #[doc(inline)]
258            #(#cfgs)*
259            pub use super::#internal_spawn_ident as spawn;
260        ));
261
262        // Schedule caller
263        for (_, monotonic) in &app.monotonics {
264            let instants = util::monotonic_instants_ident(name, &monotonic.ident);
265            let monotonic_name = monotonic.ident.to_string();
266
267            let tq = util::tq_ident(&monotonic.ident.to_string());
268            let t = util::schedule_t_ident();
269            let m = &monotonic.ident;
270            let cfgs = &monotonic.cfgs;
271            let m_ident = util::monotonic_ident(&monotonic_name);
272            let m_isr = &monotonic.args.binds;
273            let enum_ = util::interrupt_ident();
274            let spawn_handle_string = format!("{}::SpawnHandle", m);
275
276            let (enable_interrupt, pend) = if &*m_isr.to_string() == "SysTick" {
277                (
278                    quote!(core::mem::transmute::<_, rtic::export::SYST>(()).enable_interrupt()),
279                    quote!(rtic::export::SCB::set_pendst()),
280                )
281            } else {
282                let rt_err = util::rt_err_ident();
283                (
284                    quote!(rtic::export::NVIC::unmask(#rt_err::#enum_::#m_isr)),
285                    quote!(rtic::pend(#rt_err::#enum_::#m_isr)),
286                )
287            };
288
289            let tq_marker = &util::timer_queue_marker_ident();
290
291            // For future use
292            // let doc = format!(" RTIC internal: {}:{}", file!(), line!());
293            // items.push(quote!(#[doc = #doc]));
294            let internal_spawn_handle_ident =
295                util::internal_monotonics_ident(name, m, "SpawnHandle");
296            let internal_spawn_at_ident = util::internal_monotonics_ident(name, m, "spawn_at");
297            let internal_spawn_after_ident =
298                util::internal_monotonics_ident(name, m, "spawn_after");
299
300            if monotonic.args.default {
301                module_items.push(quote!(
302                    #(#cfgs)*
303                    pub use #m::spawn_after;
304                    #(#cfgs)*
305                    pub use #m::spawn_at;
306                    #(#cfgs)*
307                    pub use #m::SpawnHandle;
308                ));
309            }
310            module_items.push(quote!(
311                #[doc(hidden)]
312                #(#cfgs)*
313                pub mod #m {
314                    pub use super::super::#internal_spawn_after_ident as spawn_after;
315                    pub use super::super::#internal_spawn_at_ident as spawn_at;
316                    pub use super::super::#internal_spawn_handle_ident as SpawnHandle;
317                }
318            ));
319
320            items.push(quote!(
321                #[doc(hidden)]
322                #(#cfgs)*
323                #[allow(non_snake_case)]
324                #[allow(non_camel_case_types)]
325                pub struct #internal_spawn_handle_ident {
326                    #[doc(hidden)]
327                    marker: u32,
328                }
329
330                #(#cfgs)*
331                impl core::fmt::Debug for #internal_spawn_handle_ident {
332                    #[doc(hidden)]
333                    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
334                        f.debug_struct(#spawn_handle_string).finish()
335                    }
336                }
337
338                #(#cfgs)*
339                impl #internal_spawn_handle_ident {
340                    pub fn cancel(self) -> Result<#ty, ()> {
341                        rtic::export::interrupt::free(|_| unsafe {
342                            let tq = &mut *#tq.get_mut();
343                            if let Some((_task, index)) = tq.cancel_marker(self.marker) {
344                                // Get the message
345                                let msg = (&*#inputs
346                                    .get())
347                                    .get_unchecked(usize::from(index))
348                                    .as_ptr()
349                                    .read();
350                                // Return the index to the free queue
351                                (&mut *#fq.get_mut()).split().0.enqueue_unchecked(index);
352
353                                Ok(msg)
354                            } else {
355                                Err(())
356                            }
357                        })
358                    }
359
360                    /// Reschedule after 
361                    #[inline]
362                    #(#cfgs)*
363                    pub fn reschedule_after(
364                        self,
365                        duration: <#m as rtic::Monotonic>::Duration
366                    ) -> Result<Self, ()> {
367                        self.reschedule_at(monotonics::#m::now() + duration)
368                    }
369
370                    /// Reschedule at 
371                    #(#cfgs)*
372                    pub fn reschedule_at(
373                        self,
374                        instant: <#m as rtic::Monotonic>::Instant
375                    ) -> Result<Self, ()> {
376                        rtic::export::interrupt::free(|_| unsafe {
377                            let marker = #tq_marker.get().read();
378                            #tq_marker.get_mut().write(marker.wrapping_add(1));
379
380                            let tq = (&mut *#tq.get_mut());
381
382                            tq.update_marker(self.marker, marker, instant, || #pend).map(|_| #name::#m::SpawnHandle { marker })
383                        })
384                    }
385                }
386
387                /// Spawns the task after a set duration relative to the current time
388                ///
389                /// This will use the time `Instant::new(0)` as baseline if called in `#[init]`,
390                /// so if you use a non-resetable timer use `spawn_at` when in `#[init]`
391                #(#cfgs)*
392                #[allow(non_snake_case)]
393                pub fn #internal_spawn_after_ident(
394                    duration: <#m as rtic::Monotonic>::Duration
395                    #(,#args)*
396                ) -> Result<#name::#m::SpawnHandle, #ty>
397                {
398                    let instant = monotonics::#m::now();
399
400                    #internal_spawn_at_ident(instant + duration #(,#untupled)*)
401                }
402
403                #(#cfgs)*
404                /// Spawns the task at a fixed time instant
405                #[allow(non_snake_case)]
406                pub fn #internal_spawn_at_ident(
407                    instant: <#m as rtic::Monotonic>::Instant
408                    #(,#args)*
409                ) -> Result<#name::#m::SpawnHandle, #ty> {
410                    unsafe {
411                        let input = #tupled;
412                        if let Some(index) = rtic::export::interrupt::free(|_| (&mut *#fq.get_mut()).dequeue()) {
413                            (&mut *#inputs
414                                .get_mut())
415                                .get_unchecked_mut(usize::from(index))
416                                .as_mut_ptr()
417                                .write(input);
418
419                            (&mut *#instants
420                                .get_mut())
421                                .get_unchecked_mut(usize::from(index))
422                                .as_mut_ptr()
423                                .write(instant);
424
425                            rtic::export::interrupt::free(|_| {
426                                let marker = #tq_marker.get().read();
427                                let nr = rtic::export::NotReady {
428                                    instant,
429                                    index,
430                                    task: #t::#name,
431                                    marker,
432                                };
433
434                                #tq_marker.get_mut().write(#tq_marker.get().read().wrapping_add(1));
435
436                                let tq = &mut *#tq.get_mut();
437
438                                tq.enqueue_unchecked(
439                                    nr,
440                                    || #enable_interrupt,
441                                    || #pend,
442                                    (&mut *#m_ident.get_mut()).as_mut());
443
444                                Ok(#name::#m::SpawnHandle { marker })
445                            })
446                        } else {
447                            Err(input)
448                        }
449                    }
450                }
451            ));
452        }
453    }
454
455    if items.is_empty() {
456        quote!()
457    } else {
458        quote!(
459            #(#items)*
460            #[allow(non_snake_case)]
461            #(#cfgs)*
462            #[doc = #doc]
463            pub mod #name {
464                #(#module_items)*
465            }
466        )
467    }
468}