diff options
Diffstat (limited to 'macros/src/codegen/module.rs')
-rw-r--r-- | macros/src/codegen/module.rs | 328 |
1 files changed, 328 insertions, 0 deletions
diff --git a/macros/src/codegen/module.rs b/macros/src/codegen/module.rs new file mode 100644 index 00000000..5f077a22 --- /dev/null +++ b/macros/src/codegen/module.rs @@ -0,0 +1,328 @@ +use proc_macro2::TokenStream as TokenStream2; +use quote::quote; +use rtfm_syntax::{ast::App, Context}; + +use crate::{check::Extra, codegen::util}; + +pub fn codegen(ctxt: Context, resources_tick: bool, app: &App, extra: &Extra) -> TokenStream2 { + let mut items = vec![]; + let mut fields = vec![]; + let mut values = vec![]; + + let name = ctxt.ident(app); + + let core = ctxt.core(app); + let mut needs_instant = false; + let mut lt = None; + match ctxt { + Context::Init(core) => { + if app.uses_schedule(core) { + let m = extra.monotonic(); + + fields.push(quote!( + /// System start time = `Instant(0 /* cycles */)` + pub start: <#m as rtfm::Monotonic>::Instant + )); + + values.push(quote!(start: <#m as rtfm::Monotonic>::zero())); + + fields.push(quote!( + /// Core (Cortex-M) peripherals minus the SysTick + pub core: rtfm::Peripherals + )); + } else { + fields.push(quote!( + /// Core (Cortex-M) peripherals + pub core: rtfm::export::Peripherals + )); + } + + if extra.peripherals == Some(core) { + let device = extra.device; + + fields.push(quote!( + /// Device peripherals + pub device: #device::Peripherals + )); + + values.push(quote!(device: #device::Peripherals::steal())); + } + + values.push(quote!(core)); + } + + Context::Idle(..) => {} + + Context::HardwareTask(..) => { + if app.uses_schedule(core) { + let m = extra.monotonic(); + + fields.push(quote!( + /// Time at which this handler started executing + pub start: <#m as rtfm::Monotonic>::Instant + )); + + values.push(quote!(start: instant)); + + needs_instant = true; + } + } + + Context::SoftwareTask(..) => { + if app.uses_schedule(core) { + let m = extra.monotonic(); + + fields.push(quote!( + /// The time at which this task was scheduled to run + pub scheduled: <#m as rtfm::Monotonic>::Instant + )); + + values.push(quote!(scheduled: instant)); + + needs_instant = true; + } + } + } + + if ctxt.has_locals(app) { + let ident = util::locals_ident(ctxt, app); + items.push(quote!( + #[doc(inline)] + pub use super::#ident as Locals; + )); + } + + if ctxt.has_resources(app) { + let ident = util::resources_ident(ctxt, app); + let lt = if resources_tick { + lt = Some(quote!('a)); + Some(quote!('a)) + } else { + None + }; + + items.push(quote!( + #[doc(inline)] + pub use super::#ident as Resources; + )); + + fields.push(quote!( + /// Resources this task has access to + pub resources: Resources<#lt> + )); + + let priority = if ctxt.is_init() { + None + } else { + Some(quote!(priority)) + }; + values.push(quote!(resources: Resources::new(#priority))); + } + + if ctxt.uses_schedule(app) { + let doc = "Tasks that can be `schedule`-d from this context"; + if ctxt.is_init() { + items.push(quote!( + #[doc = #doc] + #[derive(Clone, Copy)] + pub struct Schedule { + _not_send: core::marker::PhantomData<*mut ()>, + } + )); + + fields.push(quote!( + #[doc = #doc] + pub schedule: Schedule + )); + + values.push(quote!( + schedule: Schedule { _not_send: core::marker::PhantomData } + )); + } else { + lt = Some(quote!('a)); + + items.push(quote!( + #[doc = #doc] + #[derive(Clone, Copy)] + pub struct Schedule<'a> { + priority: &'a rtfm::export::Priority, + } + + impl<'a> Schedule<'a> { + #[doc(hidden)] + #[inline(always)] + pub unsafe fn priority(&self) -> &rtfm::export::Priority { + &self.priority + } + } + )); + + fields.push(quote!( + #[doc = #doc] + pub schedule: Schedule<'a> + )); + + values.push(quote!( + schedule: Schedule { priority } + )); + } + } + + if ctxt.uses_spawn(app) { + let doc = "Tasks that can be `spawn`-ed from this context"; + if ctxt.is_init() { + fields.push(quote!( + #[doc = #doc] + pub spawn: Spawn + )); + + items.push(quote!( + #[doc = #doc] + #[derive(Clone, Copy)] + pub struct Spawn { + _not_send: core::marker::PhantomData<*mut ()>, + } + )); + + values.push(quote!(spawn: Spawn { _not_send: core::marker::PhantomData })); + } else { + lt = Some(quote!('a)); + + fields.push(quote!( + #[doc = #doc] + pub spawn: Spawn<'a> + )); + + let mut instant_method = None; + if ctxt.is_idle() { + items.push(quote!( + #[doc = #doc] + #[derive(Clone, Copy)] + pub struct Spawn<'a> { + priority: &'a rtfm::export::Priority, + } + )); + + values.push(quote!(spawn: Spawn { priority })); + } else { + let instant_field = if app.uses_schedule(core) { + let m = extra.monotonic(); + + needs_instant = true; + instant_method = Some(quote!( + pub unsafe fn instant(&self) -> <#m as rtfm::Monotonic>::Instant { + self.instant + } + )); + Some(quote!(instant: <#m as rtfm::Monotonic>::Instant,)) + } else { + None + }; + + items.push(quote!( + /// Tasks that can be spawned from this context + #[derive(Clone, Copy)] + pub struct Spawn<'a> { + #instant_field + priority: &'a rtfm::export::Priority, + } + )); + + let _instant = if needs_instant { + Some(quote!(, instant)) + } else { + None + }; + values.push(quote!( + spawn: Spawn { priority #_instant } + )); + } + + items.push(quote!( + impl<'a> Spawn<'a> { + #[doc(hidden)] + #[inline(always)] + pub unsafe fn priority(&self) -> &rtfm::export::Priority { + self.priority + } + + #instant_method + } + )); + } + } + + if let Context::Init(core) = ctxt { + let init = &app.inits[&core]; + if init.returns_late_resources { + let late_resources = util::late_resources_ident(&init.name); + + items.push(quote!( + #[doc(inline)] + pub use super::#late_resources as LateResources; + )); + } + } + + let doc = match ctxt { + Context::Idle(_) => "Idle loop", + Context::Init(_) => "Initialization function", + Context::HardwareTask(_) => "Hardware task", + Context::SoftwareTask(_) => "Software task", + }; + + let core = if ctxt.is_init() { + if app.uses_schedule(core) { + Some(quote!(core: rtfm::Peripherals,)) + } else { + Some(quote!(core: rtfm::export::Peripherals,)) + } + } else { + None + }; + + let priority = if ctxt.is_init() { + None + } else { + Some(quote!(priority: &#lt rtfm::export::Priority)) + }; + + let instant = if needs_instant { + let m = extra.monotonic(); + + Some(quote!(, instant: <#m as rtfm::Monotonic>::Instant)) + } else { + None + }; + + items.push(quote!( + /// Execution context + pub struct Context<#lt> { + #(#fields,)* + } + + impl<#lt> Context<#lt> { + #[inline(always)] + pub unsafe fn new(#core #priority #instant) -> Self { + Context { + #(#values,)* + } + } + } + )); + + if !items.is_empty() { + let cfg_core = util::cfg_core(ctxt.core(app), app.args.cores); + + quote!( + #[allow(non_snake_case)] + #[doc = #doc] + #cfg_core + pub mod #name { + #(#items)* + } + ) + } else { + quote!() + } +} |