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 let mut task_cfgs = vec![];
14
15 let name = ctxt.ident(app);
16
17 match ctxt {
18 Context::Init => {
19 fields.push(quote!(
20 pub executors_size: usize
22 ));
23
24 if app.args.core {
25 fields.push(quote!(
26 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 pub device: #device::Peripherals
39 ));
40
41 values.push(quote!(device: #device::Peripherals::steal()));
42 }
43
44 fields.push(quote!(
45 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 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 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 #[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 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 items.push(quote!(
167 #(#cfgs)*
168 #[allow(non_snake_case)]
170 #[doc(hidden)]
171 pub fn #internal_spawn_ident(#(#input_args,)*) -> ::core::result::Result<(), #input_ty> {
172 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}