use crate::syntax::{ast::App, Context}; use crate::{analyze::Analysis, codegen::util}; use proc_macro2::TokenStream as TokenStream2; use quote::quote; #[allow(clippy::too_many_lines)] pub fn codegen( ctxt: Context, shared_resources_tick: bool, local_resources_tick: bool, app: &App, analysis: &Analysis, ) -> TokenStream2 { let mut items = vec![]; let mut module_items = vec![]; let mut fields = vec![]; let mut values = vec![]; // Used to copy task cfgs to the whole module let mut task_cfgs = vec![]; let name = ctxt.ident(app); let mut lt = None; match ctxt { Context::Init => { fields.push(quote!( /// Core (Cortex-M) peripherals pub core: rtic::export::Peripherals )); if app.args.peripherals { let device = &app.args.device; fields.push(quote!( /// Device peripherals pub device: #device::Peripherals )); values.push(quote!(device: #device::Peripherals::steal())); } lt = Some(quote!('a)); fields.push(quote!( /// Critical section token for init pub cs: rtic::export::CriticalSection<#lt> )); values.push(quote!(cs: rtic::export::CriticalSection::new())); values.push(quote!(core)); } Context::Idle | Context::HardwareTask(_) | Context::SoftwareTask(_) => {} } if ctxt.has_local_resources(app) { let ident = util::local_resources_ident(ctxt, app); let lt = if local_resources_tick { lt = Some(quote!('a)); Some(quote!('a)) } else { None }; module_items.push(quote!( #[doc(inline)] pub use super::#ident as LocalResources; )); fields.push(quote!( /// Local Resources this task has access to pub local: #name::LocalResources<#lt> )); values.push(quote!(local: #name::LocalResources::new())); } if ctxt.has_shared_resources(app) { let ident = util::shared_resources_ident(ctxt, app); let lt = if shared_resources_tick { lt = Some(quote!('a)); Some(quote!('a)) } else { None }; module_items.push(quote!( #[doc(inline)] pub use super::#ident as SharedResources; )); fields.push(quote!( /// Shared Resources this task has access to pub shared: #name::SharedResources<#lt> )); let priority = if ctxt.is_init() { None } else { Some(quote!(priority)) }; values.push(quote!(shared: #name::SharedResources::new(#priority))); } let doc = match ctxt { Context::Idle => "Idle loop", Context::Init => "Initialization function", Context::HardwareTask(_) => "Hardware task", Context::SoftwareTask(_) => "Software task", }; let v = Vec::new(); let cfgs = match ctxt { Context::HardwareTask(t) => &app.hardware_tasks[t].cfgs, Context::SoftwareTask(t) => &app.software_tasks[t].cfgs, _ => &v, }; let core = if ctxt.is_init() { Some(quote!(core: rtic::export::Peripherals,)) } else { None }; let priority = if ctxt.is_init() { None } else { Some(quote!(priority: &#lt rtic::export::Priority)) }; let internal_context_name = util::internal_task_ident(name, "Context"); items.push(quote!( #(#cfgs)* /// Execution context #[allow(non_snake_case)] #[allow(non_camel_case_types)] pub struct #internal_context_name<#lt> { #(#fields,)* } #(#cfgs)* impl<#lt> #internal_context_name<#lt> { #[inline(always)] pub unsafe fn new(#core #priority) -> Self { #internal_context_name { #(#values,)* } } } )); module_items.push(quote!( #(#cfgs)* #[doc(inline)] pub use super::#internal_context_name as Context; )); if let Context::SoftwareTask(..) = ctxt { let spawnee = &app.software_tasks[name]; let priority = spawnee.args.priority; let cfgs = &spawnee.cfgs; // Store a copy of the task cfgs task_cfgs = cfgs.clone(); let device = &app.args.device; let enum_ = util::interrupt_ident(); let interrupt = &analysis .interrupts .get(&priority) .expect("RTIC-ICE: interrupt identifer not found") .0; let internal_spawn_ident = util::internal_task_ident(name, "spawn"); // Spawn caller let rq = util::rq_async_ident(name); items.push(quote!( #(#cfgs)* /// Spawns the task directly #[allow(non_snake_case)] #[doc(hidden)] pub fn #internal_spawn_ident() -> Result<(), ()> { unsafe { let r = rtic::export::interrupt::free(|_| (&mut *#rq.get_mut()).enqueue(())); if r.is_ok() { rtic::pend(#device::#enum_::#interrupt); } r } })); module_items.push(quote!( #(#cfgs)* #[doc(inline)] pub use super::#internal_spawn_ident as spawn; )); } if items.is_empty() { quote!() } else { quote!( #(#items)* #[allow(non_snake_case)] #(#task_cfgs)* #[doc = #doc] pub mod #name { #(#module_items)* } ) } }