rtic_macros/codegen/
module.rs

1use crate::syntax::{ast::App, Context};
2use crate::{analyze::Analysis, codegen::bindings::interrupt_mod, codegen::util};
3use proc_macro2::TokenStream as TokenStream2;
4use quote::quote;
5
6#[allow(clippy::too_many_lines)]
7pub fn codegen(ctxt: Context, app: &App, analysis: &Analysis) -> TokenStream2 {
8    let mut items = vec![];
9    let mut module_items = vec![];
10    let mut fields = vec![];
11    let mut values = vec![];
12    // Used to copy task cfgs to the whole module
13    let mut task_cfgs = vec![];
14
15    let name = ctxt.ident(app);
16
17    match ctxt {
18        Context::Init => {
19            fields.push(quote!(
20                /// The space used to allocate async executors in bytes.
21                pub executors_size: usize
22            ));
23
24            if app.args.core {
25                fields.push(quote!(
26                    /// Core peripherals
27                    pub core: rtic::export::Peripherals
28                ));
29
30                values.push(quote!(core: core));
31            }
32
33            if app.args.peripherals {
34                let device = &app.args.device;
35
36                fields.push(quote!(
37                    /// Device peripherals (PAC)
38                    pub device: #device::Peripherals
39                ));
40
41                values.push(quote!(device: #device::Peripherals::steal()));
42            }
43
44            fields.push(quote!(
45                /// Critical section token for init
46                pub cs: rtic::export::CriticalSection<'a>
47            ));
48
49            values.push(quote!(cs: rtic::export::CriticalSection::new()));
50            values.push(quote!(executors_size));
51        }
52
53        Context::Idle | Context::HardwareTask(_) | Context::SoftwareTask(_) => {}
54    }
55
56    if ctxt.has_local_resources(app) {
57        let ident = util::local_resources_ident(ctxt, app);
58
59        module_items.push(quote!(
60            #[doc(inline)]
61            pub use super::#ident as LocalResources;
62        ));
63
64        fields.push(quote!(
65            /// Local Resources this task has access to
66            pub local: #name::LocalResources<'a>
67        ));
68
69        values.push(quote!(local: #name::LocalResources::new()));
70    }
71
72    if ctxt.has_shared_resources(app) {
73        let ident = util::shared_resources_ident(ctxt, app);
74
75        module_items.push(quote!(
76            #[doc(inline)]
77            pub use super::#ident as SharedResources;
78        ));
79
80        fields.push(quote!(
81            /// Shared Resources this task has access to
82            pub shared: #name::SharedResources<'a>
83        ));
84
85        values.push(quote!(shared: #name::SharedResources::new()));
86    }
87
88    let doc = match ctxt {
89        Context::Idle => "Idle loop",
90        Context::Init => "Initialization function",
91        Context::HardwareTask(_) => "Hardware task",
92        Context::SoftwareTask(_) => "Software task",
93    };
94
95    let v = Vec::new();
96    let cfgs = match ctxt {
97        Context::HardwareTask(t) => &app.hardware_tasks[t].cfgs,
98        Context::SoftwareTask(t) => &app.software_tasks[t].cfgs,
99        _ => &v,
100    };
101
102    let core = if ctxt.is_init() {
103        if app.args.core {
104            Some(quote!(core: rtic::export::Peripherals, executors_size: usize))
105        } else {
106            Some(quote!(executors_size: usize))
107        }
108    } else {
109        None
110    };
111
112    let internal_context_name = util::internal_task_ident(name, "Context");
113    let exec_name = util::internal_task_ident(name, "EXEC");
114
115    items.push(quote!(
116        #(#cfgs)*
117        /// Execution context
118        #[allow(non_snake_case)]
119        #[allow(non_camel_case_types)]
120        pub struct #internal_context_name<'a> {
121            #[doc(hidden)]
122            __rtic_internal_p: ::core::marker::PhantomData<&'a ()>,
123            #(#fields,)*
124        }
125
126        #(#cfgs)*
127        impl<'a> #internal_context_name<'a> {
128            #[inline(always)]
129            #[allow(missing_docs)]
130            pub unsafe fn new(#core) -> Self {
131                #internal_context_name {
132                    __rtic_internal_p: ::core::marker::PhantomData,
133                    #(#values,)*
134                }
135            }
136        }
137    ));
138
139    module_items.push(quote!(
140        #(#cfgs)*
141        #[doc(inline)]
142        pub use super::#internal_context_name as Context;
143    ));
144
145    if let Context::SoftwareTask(..) = ctxt {
146        let spawnee = &app.software_tasks[name];
147        let priority = spawnee.args.priority;
148        let cfgs = &spawnee.cfgs;
149        // Store a copy of the task cfgs
150        task_cfgs.clone_from(cfgs);
151
152        let pend_interrupt = if priority > 0 {
153            let int_mod = interrupt_mod(app);
154            let interrupt = &analysis.interrupts.get(&priority).expect("UREACHABLE").0;
155            quote!(rtic::export::pend(#int_mod::#interrupt);)
156        } else {
157            quote!()
158        };
159
160        let internal_spawn_ident = util::internal_task_ident(name, "spawn");
161        let from_ptr_n_args = util::from_ptr_n_args_ident(spawnee.inputs.len());
162        let (input_args, input_tupled, input_untupled, input_ty) =
163            util::regroup_inputs(&spawnee.inputs);
164
165        // Spawn caller
166        items.push(quote!(
167            #(#cfgs)*
168            /// Spawns the task directly
169            #[allow(non_snake_case)]
170            #[doc(hidden)]
171            pub fn #internal_spawn_ident(#(#input_args,)*) -> ::core::result::Result<(), #input_ty> {
172                // SAFETY: If `try_allocate` succeeds one must call `spawn`, which we do.
173                unsafe {
174                    let exec = rtic::export::executor::AsyncTaskExecutor::#from_ptr_n_args(#name, &#exec_name);
175                    if exec.try_allocate() {
176                        exec.spawn(#name(unsafe { #name::Context::new() } #(,#input_untupled)*));
177                        #pend_interrupt
178
179                        Ok(())
180                    } else {
181                        Err(#input_tupled)
182                    }
183                }
184            }
185        ));
186
187        module_items.push(quote!(
188            #(#cfgs)*
189            #[doc(inline)]
190            pub use super::#internal_spawn_ident as spawn;
191        ));
192    }
193
194    if items.is_empty() {
195        quote!()
196    } else {
197        quote!(
198            #(#items)*
199
200            #[allow(non_snake_case)]
201            #(#task_cfgs)*
202            #[doc = #doc]
203            pub mod #name {
204                #(#module_items)*
205            }
206        )
207    }
208}