cortex_m_rtic_macros/codegen/
timer_queue.rs

1use proc_macro2::TokenStream as TokenStream2;
2use quote::quote;
3use rtic_syntax::ast::App;
4
5use crate::{analyze::Analysis, check::Extra, codegen::util};
6
7/// Generates timer queues and timer queue handlers
8#[allow(clippy::too_many_lines)]
9pub fn codegen(app: &App, analysis: &Analysis, _extra: &Extra) -> Vec<TokenStream2> {
10    let mut items = vec![];
11
12    if !app.monotonics.is_empty() {
13        // Generate the marker counter used to track for `cancel` and `reschedule`
14        let tq_marker = util::timer_queue_marker_ident();
15        items.push(quote!(
16            #[doc(hidden)]
17            #[allow(non_camel_case_types)]
18            #[allow(non_upper_case_globals)]
19            static #tq_marker: rtic::RacyCell<u32> = rtic::RacyCell::new(0);
20        ));
21
22        let t = util::schedule_t_ident();
23
24        // Enumeration of `schedule`-able tasks
25        {
26            let variants = app
27                .software_tasks
28                .iter()
29                .map(|(name, task)| {
30                    let cfgs = &task.cfgs;
31
32                    quote!(
33                        #(#cfgs)*
34                        #name
35                    )
36                })
37                .collect::<Vec<_>>();
38
39            // For future use
40            // let doc = "Tasks that can be scheduled".to_string();
41            items.push(quote!(
42                // #[doc = #doc]
43                #[doc(hidden)]
44                #[allow(non_camel_case_types)]
45                #[derive(Clone, Copy)]
46                pub enum #t {
47                    #(#variants,)*
48                }
49            ));
50        }
51    }
52
53    for (_, monotonic) in &app.monotonics {
54        let monotonic_name = monotonic.ident.to_string();
55        let tq = util::tq_ident(&monotonic_name);
56        let t = util::schedule_t_ident();
57        let mono_type = &monotonic.ty;
58        let cfgs = &monotonic.cfgs;
59        let m_ident = util::monotonic_ident(&monotonic_name);
60
61        // Static variables and resource proxy
62        {
63            // For future use
64            // let doc = &format!("Timer queue for {}", monotonic_name);
65            let cap: usize = app
66                .software_tasks
67                .iter()
68                .map(|(_name, task)| task.args.capacity as usize)
69                .sum();
70            let n = util::capacity_literal(cap);
71            let tq_ty = quote!(rtic::export::TimerQueue<#mono_type, #t, #n>);
72
73            // For future use
74            // let doc = format!(" RTIC internal: {}:{}", file!(), line!());
75            items.push(quote!(
76                #[doc(hidden)]
77                #[allow(non_camel_case_types)]
78                #[allow(non_upper_case_globals)]
79                #(#cfgs)*
80                static #tq: rtic::RacyCell<#tq_ty> =
81                    rtic::RacyCell::new(rtic::export::TimerQueue(rtic::export::SortedLinkedList::new_u16()));
82            ));
83
84            let mono = util::monotonic_ident(&monotonic_name);
85            // For future use
86            // let doc = &format!("Storage for {}", monotonic_name);
87
88            items.push(quote!(
89                #[doc(hidden)]
90                #[allow(non_camel_case_types)]
91                #[allow(non_upper_case_globals)]
92                #(#cfgs)*
93                static #mono: rtic::RacyCell<Option<#mono_type>> = rtic::RacyCell::new(None);
94            ));
95        }
96
97        // Timer queue handler
98        {
99            let enum_ = util::interrupt_ident();
100            let rt_err = util::rt_err_ident();
101
102            let arms = app
103                .software_tasks
104                .iter()
105                .map(|(name, task)| {
106                    let cfgs = &task.cfgs;
107                    let priority = task.args.priority;
108                    let rq = util::rq_ident(priority);
109                    let rqt = util::spawn_t_ident(priority);
110
111                    // The interrupt that runs the task dispatcher
112                    let interrupt = &analysis.interrupts.get(&priority).expect("RTIC-ICE: interrupt not found").0;
113
114                    let pend = {
115                        quote!(
116                            rtic::pend(#rt_err::#enum_::#interrupt);
117                        )
118                    };
119
120                    quote!(
121                        #(#cfgs)*
122                        #t::#name => {
123                            rtic::export::interrupt::free(|_| (&mut *#rq.get_mut()).split().0.enqueue_unchecked((#rqt::#name, index)));
124
125                            #pend
126                        }
127                    )
128                })
129                .collect::<Vec<_>>();
130
131            let cfgs = &monotonic.cfgs;
132            let bound_interrupt = &monotonic.args.binds;
133            let disable_isr = if &*bound_interrupt.to_string() == "SysTick" {
134                quote!(core::mem::transmute::<_, rtic::export::SYST>(()).disable_interrupt())
135            } else {
136                quote!(rtic::export::NVIC::mask(#rt_err::#enum_::#bound_interrupt))
137            };
138
139            items.push(quote!(
140                #[no_mangle]
141                #[allow(non_snake_case)]
142                #(#cfgs)*
143                unsafe fn #bound_interrupt() {
144                    while let Some((task, index)) = rtic::export::interrupt::free(|_|
145                        if let Some(mono) = (&mut *#m_ident.get_mut()).as_mut() {
146                            (&mut *#tq.get_mut()).dequeue(|| #disable_isr, mono)
147                        } else {
148                            // We can only use the timer queue if `init` has returned, and it
149                            // writes the `Some(monotonic)` we are accessing here.
150                            core::hint::unreachable_unchecked()
151                        })
152                    {
153                        match task {
154                            #(#arms)*
155                        }
156                    }
157
158                    rtic::export::interrupt::free(|_| if let Some(mono) = (&mut *#m_ident.get_mut()).as_mut() {
159                        mono.on_interrupt();
160                    });
161                }
162            ));
163        }
164    }
165
166    items
167}