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 pub core: rtic::export::Peripherals
28 ));
29
30 if extra.peripherals {
31 let device = &extra.device;
32
33 fields.push(quote!(
34 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 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_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 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 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 #[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 }
156 Context::SoftwareTask(t) => {
157 &app.software_tasks[t].cfgs
158 }
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 #(#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 items.push(quote!(
228
229 #(#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 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 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 let msg = (&*#inputs
346 .get())
347 .get_unchecked(usize::from(index))
348 .as_ptr()
349 .read();
350 (&mut *#fq.get_mut()).split().0.enqueue_unchecked(index);
352
353 Ok(msg)
354 } else {
355 Err(())
356 }
357 })
358 }
359
360 #[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 #(#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 #(#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 #[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}