diff options
Diffstat (limited to 'macros/src/codegen/pre_init.rs')
-rw-r--r-- | macros/src/codegen/pre_init.rs | 109 |
1 files changed, 109 insertions, 0 deletions
diff --git a/macros/src/codegen/pre_init.rs b/macros/src/codegen/pre_init.rs new file mode 100644 index 00000000..9c5f35ec --- /dev/null +++ b/macros/src/codegen/pre_init.rs @@ -0,0 +1,109 @@ +use proc_macro2::TokenStream as TokenStream2; +use quote::quote; +use rtic_syntax::ast::App; + +use crate::{analyze::Analysis, check::Extra, codegen::util}; + +/// Generates code that runs before `#[init]` +pub fn codegen(app: &App, analysis: &Analysis, extra: &Extra) -> Vec<TokenStream2> { + let mut stmts = vec![]; + + // Disable interrupts -- `init` must run with interrupts disabled + stmts.push(quote!(rtic::export::interrupt::disable();)); + + // Populate the FreeQueue + for fq in &analysis.free_queues { + // Get the task name + let name = fq.0; + let task = &app.software_tasks[name]; + let cap = task.args.capacity; + + let fq_ident = util::fq_ident(name); + + stmts.push(quote!( + (0..#cap).for_each(|i| #fq_ident.enqueue_unchecked(i)); + )); + } + + stmts.push(quote!( + // To set the variable in cortex_m so the peripherals cannot be taken multiple times + let mut core: rtic::export::Peripherals = rtic::export::Peripherals::steal().into(); + )); + + let device = extra.device; + let nvic_prio_bits = quote!(#device::NVIC_PRIO_BITS); + + // Unmask interrupts and set their priorities + for (&priority, name) in analysis + .interrupts + .iter() + .chain(app.hardware_tasks.values().flat_map(|task| { + if !util::is_exception(&task.args.binds) { + Some((&task.args.priority, &task.args.binds)) + } else { + // We do exceptions in another pass + None + } + })) + { + // Compile time assert that this priority is supported by the device + stmts.push(quote!(let _ = [(); ((1 << #nvic_prio_bits) - #priority as usize)];)); + + // NOTE this also checks that the interrupt exists in the `Interrupt` enumeration + let interrupt = util::interrupt_ident(); + stmts.push(quote!( + core.NVIC.set_priority( + #device::#interrupt::#name, + rtic::export::logical2hw(#priority, #nvic_prio_bits), + ); + )); + + // NOTE unmask the interrupt *after* setting its priority: changing the priority of a pended + // interrupt is implementation defined + stmts.push(quote!(rtic::export::NVIC::unmask(#device::#interrupt::#name);)); + } + + // Set exception priorities + for (name, priority) in app.hardware_tasks.values().filter_map(|task| { + if util::is_exception(&task.args.binds) { + Some((&task.args.binds, task.args.priority)) + } else { + None + } + }) { + // Compile time assert that this priority is supported by the device + stmts.push(quote!(let _ = [(); ((1 << #nvic_prio_bits) - #priority as usize)];)); + + stmts.push(quote!(core.SCB.set_priority( + rtic::export::SystemHandler::#name, + rtic::export::logical2hw(#priority, #nvic_prio_bits), + );)); + } + + // Initialize the SysTick if there exist a TimerQueue + if let Some(tq) = analysis.timer_queues.first() { + let priority = tq.priority; + + // Compile time assert that this priority is supported by the device + stmts.push(quote!(let _ = [(); ((1 << #nvic_prio_bits) - #priority as usize)];)); + + stmts.push(quote!(core.SCB.set_priority( + rtic::export::SystemHandler::SysTick, + rtic::export::logical2hw(#priority, #nvic_prio_bits), + );)); + + stmts.push(quote!( + core.SYST.set_clock_source(rtic::export::SystClkSource::Core); + core.SYST.enable_counter(); + core.DCB.enable_trace(); + )); + } + + // If there's no user `#[idle]` then optimize returning from interrupt handlers + if app.idles.is_empty() { + // Set SLEEPONEXIT bit to enter sleep mode when returning from ISR + stmts.push(quote!(core.SCB.scr.modify(|r| r | 1 << 1);)); + } + + stmts +} |