cortex_m_rtic_macros/codegen/
pre_init.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 code that runs before `#[init]`
8pub fn codegen(app: &App, analysis: &Analysis, extra: &Extra) -> Vec<TokenStream2> {
9    let mut stmts = vec![];
10
11    let rt_err = util::rt_err_ident();
12
13    // Disable interrupts -- `init` must run with interrupts disabled
14    stmts.push(quote!(rtic::export::interrupt::disable();));
15
16    // Populate the FreeQueue
17    for (name, task) in &app.software_tasks {
18        let cap = task.args.capacity;
19        let cfgs = &task.cfgs;
20        let fq_ident = util::fq_ident(name);
21
22        stmts.push(quote!(
23            #(#cfgs)*
24            (0..#cap).for_each(|i| (&mut *#fq_ident.get_mut()).enqueue_unchecked(i));
25        ));
26    }
27
28    stmts.push(quote!(
29        // To set the variable in cortex_m so the peripherals cannot be taken multiple times
30        let mut core: rtic::export::Peripherals = rtic::export::Peripherals::steal().into();
31    ));
32
33    let device = &extra.device;
34    let nvic_prio_bits = quote!(#device::NVIC_PRIO_BITS);
35
36    // check that all dispatchers exists in the `Interrupt` enumeration regardless of whether
37    // they are used or not
38    let interrupt = util::interrupt_ident();
39    for name in app.args.extern_interrupts.keys() {
40        stmts.push(quote!(let _ = #rt_err::#interrupt::#name;));
41    }
42
43    let interrupt_ids = analysis.interrupts.iter().map(|(p, (id, _))| (p, id));
44
45    // Unmask interrupts and set their priorities
46    for (&priority, name) in interrupt_ids.chain(app.hardware_tasks.values().filter_map(|task| {
47        if util::is_exception(&task.args.binds) {
48            // We do exceptions in another pass
49            None
50        } else {
51            Some((&task.args.priority, &task.args.binds))
52        }
53    })) {
54        let es = format!(
55            "Maximum priority used by interrupt vector '{}' is more than supported by hardware",
56            name
57        );
58        // Compile time assert that this priority is supported by the device
59        stmts.push(quote!(
60            const _: () =  if (1 << #nvic_prio_bits) < #priority as usize { ::core::panic!(#es); };
61        ));
62
63        stmts.push(quote!(
64            core.NVIC.set_priority(
65                #rt_err::#interrupt::#name,
66                rtic::export::logical2hw(#priority, #nvic_prio_bits),
67            );
68        ));
69
70        // NOTE unmask the interrupt *after* setting its priority: changing the priority of a pended
71        // interrupt is implementation defined
72        stmts.push(quote!(rtic::export::NVIC::unmask(#rt_err::#interrupt::#name);));
73    }
74
75    // Set exception priorities
76    for (name, priority) in app.hardware_tasks.values().filter_map(|task| {
77        if util::is_exception(&task.args.binds) {
78            Some((&task.args.binds, task.args.priority))
79        } else {
80            None
81        }
82    }) {
83        let es = format!(
84            "Maximum priority used by interrupt vector '{}' is more than supported by hardware",
85            name
86        );
87        // Compile time assert that this priority is supported by the device
88        stmts.push(quote!(
89            const _: () =  if (1 << #nvic_prio_bits) < #priority as usize { ::core::panic!(#es); };
90        ));
91
92        stmts.push(quote!(core.SCB.set_priority(
93            rtic::export::SystemHandler::#name,
94            rtic::export::logical2hw(#priority, #nvic_prio_bits),
95        );));
96    }
97
98    // Initialize monotonic's interrupts
99    for (_, monotonic) in &app.monotonics {
100        let priority = if let Some(prio) = monotonic.args.priority {
101            quote! { #prio }
102        } else {
103            quote! { (1 << #nvic_prio_bits) }
104        };
105        let binds = &monotonic.args.binds;
106
107        let name = &monotonic.ident;
108        let es = format!(
109            "Maximum priority used by monotonic '{}' is more than supported by hardware",
110            name
111        );
112        // Compile time assert that this priority is supported by the device
113        stmts.push(quote!(
114            const _: () =  if (1 << #nvic_prio_bits) < #priority as usize { ::core::panic!(#es); };
115        ));
116
117        let mono_type = &monotonic.ty;
118
119        if &*binds.to_string() == "SysTick" {
120            stmts.push(quote!(
121                core.SCB.set_priority(
122                    rtic::export::SystemHandler::SysTick,
123                    rtic::export::logical2hw(#priority, #nvic_prio_bits),
124                );
125
126                // Always enable monotonic interrupts if they should never be off
127                if !<#mono_type as rtic::Monotonic>::DISABLE_INTERRUPT_ON_EMPTY_QUEUE {
128                    core::mem::transmute::<_, rtic::export::SYST>(())
129                        .enable_interrupt();
130                }
131            ));
132        } else {
133            stmts.push(quote!(
134                core.NVIC.set_priority(
135                    #rt_err::#interrupt::#binds,
136                    rtic::export::logical2hw(#priority, #nvic_prio_bits),
137                );
138
139                // Always enable monotonic interrupts if they should never be off
140                if !<#mono_type as rtic::Monotonic>::DISABLE_INTERRUPT_ON_EMPTY_QUEUE {
141                    rtic::export::NVIC::unmask(#rt_err::#interrupt::#binds);
142                }
143            ));
144        }
145    }
146    stmts
147}