diff options
author | 2021-03-04 19:12:35 +0000 | |
---|---|---|
committer | 2021-03-04 19:12:35 +0000 | |
commit | 89a5c8004efaa8f42c86a1aedb609f49ec511333 (patch) | |
tree | 6db5b553e24a540284edc3f3fbf87043c638defc /macros/src/codegen | |
parent | 81a8a591353b1ea0208c68b28ee81286629039cc (diff) | |
parent | 2e4a4ffd87c8a031f27635c060042019511523dc (diff) | |
download | rtic-89a5c8004efaa8f42c86a1aedb609f49ec511333.tar.gz rtic-89a5c8004efaa8f42c86a1aedb609f49ec511333.tar.zst rtic-89a5c8004efaa8f42c86a1aedb609f49ec511333.zip |
Merge #436v0.6.0-alpha.1
436: New monotonic r=AfoHT a=korken89
Design document: https://hackmd.io/vWa9GvssR8qBfUYgMZm0CQ
Closes #433
Closes #432
Closes #427
Closes #426
Closes #403
Closes #332
Closes #312
Closes #309
Closes #299
Closes #292
Closes #247
Closes #219
Co-authored-by: Emil Fresk <emil.fresk@gmail.com>
Diffstat (limited to 'macros/src/codegen')
-rw-r--r-- | macros/src/codegen/assertions.rs | 8 | ||||
-rw-r--r-- | macros/src/codegen/dispatchers.rs | 43 | ||||
-rw-r--r-- | macros/src/codegen/hardware_tasks.rs | 13 | ||||
-rw-r--r-- | macros/src/codegen/init.rs | 35 | ||||
-rw-r--r-- | macros/src/codegen/locals.rs | 1 | ||||
-rw-r--r-- | macros/src/codegen/module.rs | 259 | ||||
-rw-r--r-- | macros/src/codegen/post_init.rs | 18 | ||||
-rw-r--r-- | macros/src/codegen/pre_init.rs | 58 | ||||
-rw-r--r-- | macros/src/codegen/resources.rs | 3 | ||||
-rw-r--r-- | macros/src/codegen/resources_struct.rs | 3 | ||||
-rw-r--r-- | macros/src/codegen/software_tasks.rs | 21 | ||||
-rw-r--r-- | macros/src/codegen/timer_queue.rs | 73 | ||||
-rw-r--r-- | macros/src/codegen/util.rs | 41 |
13 files changed, 346 insertions, 230 deletions
diff --git a/macros/src/codegen/assertions.rs b/macros/src/codegen/assertions.rs index 4d9aae47..a8a4491b 100644 --- a/macros/src/codegen/assertions.rs +++ b/macros/src/codegen/assertions.rs @@ -2,9 +2,10 @@ use proc_macro2::TokenStream as TokenStream2; use quote::quote; use crate::analyze::Analysis; +use rtic_syntax::ast::App; /// Generates compile-time assertions that check that types implement the `Send` / `Sync` traits -pub fn codegen(analysis: &Analysis) -> Vec<TokenStream2> { +pub fn codegen(app: &App, analysis: &Analysis) -> Vec<TokenStream2> { let mut stmts = vec![]; for ty in &analysis.send_types { @@ -15,5 +16,10 @@ pub fn codegen(analysis: &Analysis) -> Vec<TokenStream2> { stmts.push(quote!(rtic::export::assert_sync::<#ty>();)); } + for (_, monotonic) in &app.monotonics { + let ty = &monotonic.ty; + stmts.push(quote!(rtic::export::assert_monotonic::<#ty>();)); + } + stmts } diff --git a/macros/src/codegen/dispatchers.rs b/macros/src/codegen/dispatchers.rs index a6c695f1..dc33b1af 100644 --- a/macros/src/codegen/dispatchers.rs +++ b/macros/src/codegen/dispatchers.rs @@ -5,7 +5,7 @@ use rtic_syntax::ast::App; use crate::{analyze::Analysis, check::Extra, codegen::util}; /// Generates task dispatchers -pub fn codegen(app: &App, analysis: &Analysis, extra: &Extra) -> Vec<TokenStream2> { +pub fn codegen(app: &App, analysis: &Analysis, _extra: &Extra) -> Vec<TokenStream2> { let mut items = vec![]; let interrupts = &analysis.interrupts; @@ -26,15 +26,16 @@ pub fn codegen(app: &App, analysis: &Analysis, extra: &Extra) -> Vec<TokenStream }) .collect::<Vec<_>>(); - let doc = format!( - "Software tasks to be dispatched at priority level {}", - level, - ); + // let doc = format!( + // "Software tasks to be dispatched at priority level {}", + // level, + // ); let t = util::spawn_t_ident(level); items.push(quote!( #[allow(non_camel_case_types)] #[derive(Clone, Copy)] - #[doc = #doc] + // #[doc = #doc] + #[doc(hidden)] pub enum #t { #(#variants,)* } @@ -42,6 +43,7 @@ pub fn codegen(app: &App, analysis: &Analysis, extra: &Extra) -> Vec<TokenStream let n = util::capacity_typenum(channel.capacity, true); let rq = util::rq_ident(level); + let rq = util::mark_internal_ident(&rq); let (rq_ty, rq_expr) = { ( quote!(rtic::export::SCRQ<#t, #n>), @@ -51,12 +53,12 @@ pub fn codegen(app: &App, analysis: &Analysis, extra: &Extra) -> Vec<TokenStream ) }; - let doc = format!( - "Queue of tasks ready to be dispatched at priority level {}", - level - ); + // let doc = format!( + // "Queue of tasks ready to be dispatched at priority level {}", + // level + // ); items.push(quote!( - #[doc = #doc] + #[doc(hidden)] static mut #rq: #rq_ty = #rq_expr; )); @@ -67,23 +69,11 @@ pub fn codegen(app: &App, analysis: &Analysis, extra: &Extra) -> Vec<TokenStream let task = &app.software_tasks[name]; let cfgs = &task.cfgs; let fq = util::fq_ident(name); + let fq = util::mark_internal_ident(&fq); let inputs = util::inputs_ident(name); + let inputs = util::mark_internal_ident(&inputs); let (_, tupled, pats, _) = util::regroup_inputs(&task.inputs); - let (let_instant, instant) = if extra.monotonic.is_some() { - let instants = util::instants_ident(name); - - ( - quote!( - let instant = - #instants.get_unchecked(usize::from(index)).as_ptr().read(); - ), - quote!(, instant), - ) - } else { - (quote!(), quote!()) - }; - let locals_new = if task.locals.is_empty() { quote!() } else { @@ -97,12 +87,11 @@ pub fn codegen(app: &App, analysis: &Analysis, extra: &Extra) -> Vec<TokenStream #t::#name => { let #tupled = #inputs.get_unchecked(usize::from(index)).as_ptr().read(); - #let_instant #fq.split().0.enqueue_unchecked(index); let priority = &rtic::export::Priority::new(PRIORITY); #app_path::#name( #locals_new - #name::Context::new(priority #instant) + #name::Context::new(priority) #(,#pats)* ) } diff --git a/macros/src/codegen/hardware_tasks.rs b/macros/src/codegen/hardware_tasks.rs index 6930d3e0..4a1d7492 100644 --- a/macros/src/codegen/hardware_tasks.rs +++ b/macros/src/codegen/hardware_tasks.rs @@ -29,15 +29,6 @@ pub fn codegen( let mut user_tasks = vec![]; for (name, task) in &app.hardware_tasks { - let (let_instant, instant) = if let Some(ref m) = extra.monotonic { - ( - Some(quote!(let instant = <#m as rtic::Monotonic>::now();)), - Some(quote!(, instant)), - ) - } else { - (None, None) - }; - let locals_new = if task.locals.is_empty() { quote!() } else { @@ -55,12 +46,10 @@ pub fn codegen( unsafe fn #symbol() { const PRIORITY: u8 = #priority; - #let_instant - rtic::export::run(PRIORITY, || { #app_path::#name( #locals_new - #name::Context::new(&rtic::export::Priority::new(PRIORITY) #instant) + #name::Context::new(&rtic::export::Priority::new(PRIORITY)) ) }); } diff --git a/macros/src/codegen/init.rs b/macros/src/codegen/init.rs index 6376ce31..aa9adcb0 100644 --- a/macros/src/codegen/init.rs +++ b/macros/src/codegen/init.rs @@ -5,7 +5,7 @@ use rtic_syntax::{ast::App, Context}; use crate::{ analyze::Analysis, check::Extra, - codegen::{locals, module, resources_struct, util}, + codegen::{locals, module, resources_struct}, }; type CodegenResult = ( @@ -32,32 +32,6 @@ pub fn codegen(app: &App, analysis: &Analysis, extra: &Extra) -> CodegenResult { let mut root_init = vec![]; - let late_fields = analysis - .late_resources - .iter() - .flat_map(|resources| { - resources.iter().map(|name| { - let ty = &app.late_resources[name].ty; - let cfgs = &app.late_resources[name].cfgs; - - quote!( - #(#cfgs)* - pub #name: #ty - ) - }) - }) - .collect::<Vec<_>>(); - - let late_resources = util::late_resources_ident(&name); - - root_init.push(quote!( - /// Resources initialized at runtime - #[allow(non_snake_case)] - pub struct #late_resources { - #(#late_fields),* - } - )); - let mut locals_pat = None; let mut locals_new = None; if !init.locals.is_empty() { @@ -72,10 +46,13 @@ pub fn codegen(app: &App, analysis: &Analysis, extra: &Extra) -> CodegenResult { let attrs = &init.attrs; let stmts = &init.stmts; let locals_pat = locals_pat.iter(); + + let user_init_return = quote! {#name::LateResources, #name::Monotonics}; + let user_init = Some(quote!( #(#attrs)* #[allow(non_snake_case)] - fn #name(#(#locals_pat,)* #context: #name::Context) -> #name::LateResources { + fn #name(#(#locals_pat,)* #context: #name::Context) -> (#user_init_return) { #(#stmts)* } )); @@ -92,7 +69,7 @@ pub fn codegen(app: &App, analysis: &Analysis, extra: &Extra) -> CodegenResult { let app_path = quote! {crate::#app_name}; let locals_new = locals_new.iter(); let call_init = Some( - quote!(let late = #app_path::#name(#(#locals_new,)* #name::Context::new(core.into()));), + quote!(let (late, mut monotonics) = #app_path::#name(#(#locals_new,)* #name::Context::new(core.into()));), ); root_init.push(module::codegen( diff --git a/macros/src/codegen/locals.rs b/macros/src/codegen/locals.rs index 336c0b21..5725a151 100644 --- a/macros/src/codegen/locals.rs +++ b/macros/src/codegen/locals.rs @@ -49,6 +49,7 @@ pub fn codegen( )); items.push(quote!( #(#cfgs)* + #[doc(hidden)] static mut #name: #ty = #expr )); values.push(quote!( diff --git a/macros/src/codegen/module.rs b/macros/src/codegen/module.rs index 2ff4801e..75435b54 100644 --- a/macros/src/codegen/module.rs +++ b/macros/src/codegen/module.rs @@ -18,29 +18,16 @@ pub fn codegen( let mut task_cfgs = vec![]; let name = ctxt.ident(app); + let app_name = &app.name; + let app_path = quote! {crate::#app_name}; - let mut needs_instant = false; let mut lt = None; match ctxt { Context::Init => { - if let Some(m) = &extra.monotonic { - fields.push(quote!( - /// System start time = `Instant(0 /* cycles */)` - pub start: <#m as rtic::Monotonic>::Instant - )); - - values.push(quote!(start: <#m as rtic::Monotonic>::zero())); - - fields.push(quote!( - /// Core (Cortex-M) peripherals minus the SysTick - pub core: rtic::Peripherals - )); - } else { - fields.push(quote!( - /// Core (Cortex-M) peripherals - pub core: rtic::export::Peripherals - )); - } + fields.push(quote!( + /// Core (Cortex-M) peripherals + pub core: rtic::export::Peripherals + )); if extra.peripherals { let device = &extra.device; @@ -66,31 +53,9 @@ pub fn codegen( Context::Idle => {} - Context::HardwareTask(..) => { - if let Some(m) = &extra.monotonic { - fields.push(quote!( - /// Time at which this handler started executing - pub start: <#m as rtic::Monotonic>::Instant - )); + Context::HardwareTask(_) => {} - values.push(quote!(start: instant)); - - needs_instant = true; - } - } - - Context::SoftwareTask(..) => { - if let Some(m) = &extra.monotonic { - fields.push(quote!( - /// The time at which this task was scheduled to run - pub scheduled: <#m as rtic::Monotonic>::Instant - )); - - values.push(quote!(scheduled: instant)); - - needs_instant = true; - } - } + Context::SoftwareTask(_) => {} } if ctxt.has_locals(app) { @@ -103,6 +68,7 @@ pub fn codegen( if ctxt.has_resources(app) { let ident = util::resources_ident(ctxt, app); + let ident = util::mark_internal_ident(&ident); let lt = if resources_tick { lt = Some(quote!('a)); Some(quote!('a)) @@ -129,12 +95,45 @@ pub fn codegen( } if let Context::Init = ctxt { - let init = &app.inits.first().unwrap(); - let late_resources = util::late_resources_ident(&init.name); + let late_fields = analysis + .late_resources + .iter() + .flat_map(|resources| { + resources.iter().map(|name| { + let ty = &app.late_resources[name].ty; + let cfgs = &app.late_resources[name].cfgs; + + quote!( + #(#cfgs)* + pub #name: #ty + ) + }) + }) + .collect::<Vec<_>>(); items.push(quote!( - #[doc(inline)] - pub use super::#late_resources as LateResources; + /// Resources initialized at runtime + #[allow(non_snake_case)] + pub struct LateResources { + #(#late_fields),* + } + )); + + let monotonic_types: Vec<_> = app + .monotonics + .iter() + .map(|(_, monotonic)| { + let mono = &monotonic.ty; + quote! {#mono} + }) + .collect(); + + items.push(quote!( + /// Monotonics used by the system + #[allow(non_snake_case)] + pub struct Monotonics( + #(pub #monotonic_types),* + ); )); } @@ -146,11 +145,7 @@ pub fn codegen( }; let core = if ctxt.is_init() { - if extra.monotonic.is_some() { - Some(quote!(core: rtic::Peripherals,)) - } else { - Some(quote!(core: rtic::export::Peripherals,)) - } + Some(quote!(core: rtic::export::Peripherals,)) } else { None }; @@ -161,14 +156,6 @@ pub fn codegen( Some(quote!(priority: &#lt rtic::export::Priority)) }; - let instant = if needs_instant { - let m = extra.monotonic.clone().expect("RTIC-ICE: UNREACHABLE"); - - Some(quote!(, instant: <#m as rtic::Monotonic>::Instant)) - } else { - None - }; - items.push(quote!( /// Execution context pub struct Context<#lt> { @@ -177,7 +164,7 @@ pub fn codegen( impl<#lt> Context<#lt> { #[inline(always)] - pub unsafe fn new(#core #priority #instant) -> Self { + pub unsafe fn new(#core #priority) -> Self { Context { #(#values,)* } @@ -195,15 +182,15 @@ pub fn codegen( let cfgs = &spawnee.cfgs; // Store a copy of the task cfgs task_cfgs = cfgs.clone(); - let (args, tupled, _untupled, ty) = util::regroup_inputs(&spawnee.inputs); + let (args, tupled, untupled, ty) = util::regroup_inputs(&spawnee.inputs); let args = &args; let tupled = &tupled; let fq = util::fq_ident(name); + let fq = util::mark_internal_ident(&fq); let rq = util::rq_ident(priority); + let rq = util::mark_internal_ident(&rq); let inputs = util::inputs_ident(name); - - let app_name = &app.name; - let app_path = quote! {crate::#app_name}; + let inputs = util::mark_internal_ident(&inputs); let device = &extra.device; let enum_ = util::interrupt_ident(); @@ -216,11 +203,8 @@ pub fn codegen( // Spawn caller items.push(quote!( #(#cfgs)* + /// Spawns the task directly pub fn spawn(#(#args,)*) -> Result<(), #ty> { - // #let_instant // do we need it? - use rtic::Mutex as _; - use rtic::mutex_prelude::*; - let input = #tupled; unsafe { @@ -245,45 +229,114 @@ pub fn codegen( })); // Schedule caller - if let Some(m) = &extra.monotonic { - let instants = util::instants_ident(name); + for (_, monotonic) in &app.monotonics { + let instants = util::monotonic_instants_ident(name, &monotonic.ident); + let instants = util::mark_internal_ident(&instants); + let monotonic_name = monotonic.ident.to_string(); - let tq = util::tq_ident(); + let tq = util::tq_ident(&monotonic.ident.to_string()); + let tq = util::mark_internal_ident(&tq); let t = util::schedule_t_ident(); + let m = &monotonic.ident; + let mono_type = &monotonic.ty; + let m_ident = util::monotonic_ident(&monotonic_name); + let m_ident = util::mark_internal_ident(&m_ident); + let m_isr = &monotonic.args.binds; + let enum_ = util::interrupt_ident(); + + if monotonic.args.default { + items.push(quote!(pub use #m::spawn_after;)); + items.push(quote!(pub use #m::spawn_at;)); + } + + let (enable_interrupt, pend) = if &*m_isr.to_string() == "SysTick" { + ( + quote!(core::mem::transmute::<_, cortex_m::peripheral::SYST>(()) + .enable_interrupt()), + quote!(cortex_m::peripheral::SCB::set_pendst()), + ) + } else { + let rt_err = util::rt_err_ident(); + ( + quote!(rtic::export::NVIC::unmask(#app_path::#rt_err::#enum_::#m_isr)), + quote!(rtic::pend(#app_path::#rt_err::#enum_::#m_isr)), + ) + }; + + let user_imports = &app.user_imports; items.push(quote!( - #(#cfgs)* - pub fn schedule( - instant: <#m as rtic::Monotonic>::Instant - #(,#args)* - ) -> Result<(), #ty> { - unsafe { - use rtic::Mutex as _; - use rtic::mutex_prelude::*; - - let input = #tupled; - if let Some(index) = rtic::export::interrupt::free(|_| #app_path::#fq.dequeue()) { - #app_path::#inputs - .get_unchecked_mut(usize::from(index)) - .as_mut_ptr() - .write(input); - - #app_path::#instants - .get_unchecked_mut(usize::from(index)) - .as_mut_ptr() - .write(instant); - - let nr = rtic::export::NotReady { - instant, - index, - task: #app_path::#t::#name, - }; - - rtic::export::interrupt::free(|_| #app_path::#tq.enqueue_unchecked(nr)); - - Ok(()) + /// Holds methods related to this monotonic + pub mod #m { + #( + #[allow(unused_imports)] + #user_imports + )* + + #(#cfgs)* + /// Spawns the task after a set duration relative to the current time + /// + /// This will use the time `Instant::new(0)` as baseline if called in `#[init]`, + /// so if you use a non-resetable timer use `spawn_at` when in `#[init]` + pub fn spawn_after<D>( + duration: D + #(,#args)* + ) -> Result<(), #ty> + where D: rtic::time::duration::Duration + rtic::time::fixed_point::FixedPoint, + D::T: Into<<#app_path::#mono_type as rtic::time::Clock>::T>, + { + + let instant = if rtic::export::interrupt::free(|_| unsafe { #app_path::#m_ident.is_none() }) { + rtic::time::Instant::new(0) } else { - Err(input) + #app_path::#m::now() + }; + + spawn_at(instant + duration #(,#untupled)*) + } + + #(#cfgs)* + /// Spawns the task at a fixed time instant + pub fn spawn_at( + instant: rtic::time::Instant<#app_path::#mono_type> + #(,#args)* + ) -> Result<(), #ty> { + unsafe { + let input = #tupled; + if let Some(index) = rtic::export::interrupt::free(|_| #app_path::#fq.dequeue()) { + #app_path::#inputs + .get_unchecked_mut(usize::from(index)) + .as_mut_ptr() + .write(input); + + #app_path::#instants + .get_unchecked_mut(usize::from(index)) + .as_mut_ptr() + .write(instant); + + let nr = rtic::export::NotReady { + instant, + index, + task: #app_path::#t::#name, + }; + + rtic::export::interrupt::free(|_| + if let Some(mono) = #app_path::#m_ident.as_mut() { + #app_path::#tq.enqueue_unchecked( + nr, + || #enable_interrupt, + || #pend, + mono) + } else { + // We can only use the timer queue if `init` has returned, and it + // writes the `Some(monotonic)` we are accessing here. + core::hint::unreachable_unchecked() + }); + + Ok(()) + } else { + Err(input) + } } } })); diff --git a/macros/src/codegen/post_init.rs b/macros/src/codegen/post_init.rs index 5545944d..96c5df80 100644 --- a/macros/src/codegen/post_init.rs +++ b/macros/src/codegen/post_init.rs @@ -1,6 +1,7 @@ -use proc_macro2::TokenStream as TokenStream2; +use proc_macro2::{Span, TokenStream as TokenStream2}; use quote::quote; use rtic_syntax::ast::App; +use syn::Index; use crate::{analyze::Analysis, codegen::util}; @@ -12,7 +13,7 @@ pub fn codegen(app: &App, analysis: &Analysis) -> Vec<TokenStream2> { if !analysis.late_resources.is_empty() { // BTreeSet wrapped in a vector for name in analysis.late_resources.first().unwrap() { - let mangled_name = util::mangle_ident(&name); + let mangled_name = util::mark_internal_ident(&name); // If it's live let cfgs = app.late_resources[name].cfgs.clone(); if analysis.locations.get(name).is_some() { @@ -25,6 +26,19 @@ pub fn codegen(app: &App, analysis: &Analysis) -> Vec<TokenStream2> { } } + for (i, (monotonic, _)) in app.monotonics.iter().enumerate() { + let idx = Index { + index: i as u32, + span: Span::call_site(), + }; + stmts.push(quote!(monotonics.#idx.reset();)); + + // Store the monotonic + let name = util::monotonic_ident(&monotonic.to_string()); + let name = util::mark_internal_ident(&name); + stmts.push(quote!(#name = Some(monotonics.#idx);)); + } + // Enable the interrupts -- this completes the `init`-ialization phase stmts.push(quote!(rtic::export::interrupt::enable();)); diff --git a/macros/src/codegen/pre_init.rs b/macros/src/codegen/pre_init.rs index 969de84a..d5105445 100644 --- a/macros/src/codegen/pre_init.rs +++ b/macros/src/codegen/pre_init.rs @@ -8,6 +8,8 @@ use crate::{analyze::Analysis, check::Extra, codegen::util}; pub fn codegen(app: &App, analysis: &Analysis, extra: &Extra) -> Vec<TokenStream2> { let mut stmts = vec![]; + let rt_err = util::rt_err_ident(); + // Disable interrupts -- `init` must run with interrupts disabled stmts.push(quote!(rtic::export::interrupt::disable();)); @@ -15,6 +17,7 @@ pub fn codegen(app: &App, analysis: &Analysis, extra: &Extra) -> Vec<TokenStream for (name, task) in &app.software_tasks { let cap = task.args.capacity; let fq_ident = util::fq_ident(name); + let fq_ident = util::mark_internal_ident(&fq_ident); stmts.push(quote!( (0..#cap).for_each(|i| #fq_ident.enqueue_unchecked(i)); @@ -47,14 +50,14 @@ pub fn codegen(app: &App, analysis: &Analysis, extra: &Extra) -> Vec<TokenStream let interrupt = util::interrupt_ident(); stmts.push(quote!( core.NVIC.set_priority( - you_must_enable_the_rt_feature_for_the_pac_in_your_cargo_toml::#interrupt::#name, + #rt_err::#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(you_must_enable_the_rt_feature_for_the_pac_in_your_cargo_toml::#interrupt::#name);)); + stmts.push(quote!(rtic::export::NVIC::unmask(#rt_err::#interrupt::#name);)); } // Set exception priorities @@ -74,23 +77,48 @@ pub fn codegen(app: &App, analysis: &Analysis, extra: &Extra) -> Vec<TokenStream );)); } - // Initialize the SysTick if there exist a TimerQueue - if extra.monotonic.is_some() { - let priority = analysis.channels.keys().max().unwrap(); + // Initialize monotonic's interrupts + for (_, monotonic) in app.monotonics.iter() + //.map(|(ident, monotonic)| (ident, &monotonic.args.priority, &monotonic.args.binds)) + { + let priority = &monotonic.args.priority; + let binds = &monotonic.args.binds; // 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(); - )); + let app_name = &app.name; + let app_path = quote! {crate::#app_name}; + let mono_type = &monotonic.ty; + + if &*binds.to_string() == "SysTick" { + stmts.push(quote!( + core.SCB.set_priority( + rtic::export::SystemHandler::SysTick, + rtic::export::logical2hw(#priority, #nvic_prio_bits), + ); + + // Always enable monotonic interrupts if they should never be off + if !<#mono_type as rtic::Monotonic>::DISABLE_INTERRUPT_ON_EMPTY_QUEUE { + core::mem::transmute::<_, cortex_m::peripheral::SYST>(()) + .enable_interrupt(); + } + )); + } else { + // NOTE this also checks that the interrupt exists in the `Interrupt` enumeration + let interrupt = util::interrupt_ident(); + stmts.push(quote!( + core.NVIC.set_priority( + #rt_err::#interrupt::#binds, + rtic::export::logical2hw(#priority, #nvic_prio_bits), + ); + + // Always enable monotonic interrupts if they should never be off + if !<#mono_type as rtic::Monotonic>::DISABLE_INTERRUPT_ON_EMPTY_QUEUE { + rtic::export::NVIC::unmask(#app_path::#rt_err::#interrupt::#binds); + } + )); + } } // If there's no user `#[idle]` then optimize returning from interrupt handlers diff --git a/macros/src/codegen/resources.rs b/macros/src/codegen/resources.rs index 76871e59..fa52b86d 100644 --- a/macros/src/codegen/resources.rs +++ b/macros/src/codegen/resources.rs @@ -21,7 +21,7 @@ pub fn codegen( for (name, res, expr, _) in app.resources(analysis) { let cfgs = &res.cfgs; let ty = &res.ty; - let mangled_name = util::mangle_ident(&name); + let mangled_name = util::mark_internal_ident(&name); { let section = if expr.is_none() { @@ -42,6 +42,7 @@ pub fn codegen( let attrs = &res.attrs; mod_app.push(quote!( #[allow(non_upper_case_globals)] + #[doc(hidden)] #(#attrs)* #(#cfgs)* #section diff --git a/macros/src/codegen/resources_struct.rs b/macros/src/codegen/resources_struct.rs index bffe9431..8ed8a291 100644 --- a/macros/src/codegen/resources_struct.rs +++ b/macros/src/codegen/resources_struct.rs @@ -31,7 +31,7 @@ pub fn codegen(ctxt: Context, needs_lt: &mut bool, app: &App) -> (TokenStream2, None }; let ty = &res.ty; - let mangled_name = util::mangle_ident(&name); + let mangled_name = util::mark_internal_ident(&name); // let ownership = &analysis.ownerships[name]; let r_prop = &res.properties; @@ -112,6 +112,7 @@ pub fn codegen(ctxt: Context, needs_lt: &mut bool, app: &App) -> (TokenStream2, let doc = format!("Resources `{}` has access to", ctxt.ident(app)); let ident = util::resources_ident(ctxt, app); + let ident = util::mark_internal_ident(&ident); let item = quote!( #[allow(non_snake_case)] #[doc = #doc] diff --git a/macros/src/codegen/software_tasks.rs b/macros/src/codegen/software_tasks.rs index 9cb5f7a9..a39fe4cc 100644 --- a/macros/src/codegen/software_tasks.rs +++ b/macros/src/codegen/software_tasks.rs @@ -37,6 +37,7 @@ pub fn codegen( // Create free queues and inputs / instants buffers let fq = util::fq_ident(name); + let fq = util::mark_internal_ident(&fq); let (fq_ty, fq_expr, mk_uninit): (_, _, Box<dyn Fn() -> Option<_>>) = { ( @@ -48,8 +49,9 @@ pub fn codegen( ) }; mod_app.push(quote!( - /// Queue version of a free-list that keeps track of empty slots in - /// the following buffers + // /// Queue version of a free-list that keeps track of empty slots in + // /// the following buffers + #[doc(hidden)] static mut #fq: #fq_ty = #fq_expr; )); @@ -57,24 +59,29 @@ pub fn codegen( .map(|_| quote!(core::mem::MaybeUninit::uninit())) .collect::<Vec<_>>(); - if let Some(m) = &extra.monotonic { - let instants = util::instants_ident(name); + for (_, monotonic) in &app.monotonics { + let instants = util::monotonic_instants_ident(name, &monotonic.ident); + let instants = util::mark_internal_ident(&instants); + let mono_type = &monotonic.ty; let uninit = mk_uninit(); mod_app.push(quote!( #uninit - /// Buffer that holds the instants associated to the inputs of a task + // /// Buffer that holds the instants associated to the inputs of a task + #[doc(hidden)] static mut #instants: - [core::mem::MaybeUninit<<#m as rtic::Monotonic>::Instant>; #cap_lit] = + [core::mem::MaybeUninit<rtic::time::Instant<#mono_type>>; #cap_lit] = [#(#elems,)*]; )); } let uninit = mk_uninit(); let inputs_ident = util::inputs_ident(name); + let inputs_ident = util::mark_internal_ident(&inputs_ident); mod_app.push(quote!( #uninit - /// Buffer that holds the inputs of a task + // /// Buffer that holds the inputs of a task + #[doc(hidden)] static mut #inputs_ident: [core::mem::MaybeUninit<#input_ty>; #cap_lit] = [#(#elems,)*]; )); diff --git a/macros/src/codegen/timer_queue.rs b/macros/src/codegen/timer_queue.rs index fa2c7b36..82d0ac98 100644 --- a/macros/src/codegen/timer_queue.rs +++ b/macros/src/codegen/timer_queue.rs @@ -5,10 +5,10 @@ use rtic_syntax::ast::App; use crate::{analyze::Analysis, check::Extra, codegen::util}; /// Generates timer queues and timer queue handlers -pub fn codegen(app: &App, analysis: &Analysis, extra: &Extra) -> Vec<TokenStream2> { +pub fn codegen(app: &App, analysis: &Analysis, _extra: &Extra) -> Vec<TokenStream2> { let mut items = vec![]; - if let Some(m) = &extra.monotonic { + if !app.monotonics.is_empty() { let t = util::schedule_t_ident(); // Enumeration of `schedule`-able tasks @@ -26,9 +26,10 @@ pub fn codegen(app: &App, analysis: &Analysis, extra: &Extra) -> Vec<TokenStream }) .collect::<Vec<_>>(); - let doc = "Tasks that can be scheduled".to_string(); + // let doc = "Tasks that can be scheduled".to_string(); items.push(quote!( - #[doc = #doc] + // #[doc = #doc] + #[doc(hidden)] #[allow(non_camel_case_types)] #[derive(Clone, Copy)] enum #t { @@ -36,32 +37,54 @@ pub fn codegen(app: &App, analysis: &Analysis, extra: &Extra) -> Vec<TokenStream } )); } + } - let tq = util::tq_ident(); + for (_, monotonic) in &app.monotonics { + let monotonic_name = monotonic.ident.to_string(); + let tq = util::tq_ident(&monotonic_name); + let tq = util::mark_internal_ident(&tq); + let t = util::schedule_t_ident(); + let mono_type = &monotonic.ty; + let m_ident = util::monotonic_ident(&monotonic_name); + let m_ident = util::mark_internal_ident(&m_ident); + let app_name = &app.name; + let app_path = quote! {crate::#app_name}; - // Static variable and resource proxy + // Static variables and resource proxy { - let doc = "Timer queue".to_string(); + // let doc = &format!("Timer queue for {}", monotonic_name); let cap = app .software_tasks .iter() .map(|(_name, task)| task.args.capacity) .sum(); let n = util::capacity_typenum(cap, false); - let tq_ty = quote!(rtic::export::TimerQueue<#m, #t, #n>); + let tq_ty = quote!(rtic::export::TimerQueue<#mono_type, #t, #n>); items.push(quote!( - #[doc = #doc] + #[doc(hidden)] static mut #tq: #tq_ty = rtic::export::TimerQueue( rtic::export::BinaryHeap( rtic::export::iBinaryHeap::new() ) ); )); + + let mono = util::monotonic_ident(&monotonic_name); + let mono = util::mark_internal_ident(&mono); + // let doc = &format!("Storage for {}", monotonic_name); + + items.push(quote!( + #[doc(hidden)] + static mut #mono: Option<#mono_type> = None; + )); } // Timer queue handler { + let enum_ = util::interrupt_ident(); + let rt_err = util::rt_err_ident(); + let arms = app .software_tasks .iter() @@ -69,13 +92,15 @@ pub fn codegen(app: &App, analysis: &Analysis, extra: &Extra) -> Vec<TokenStream let cfgs = &task.cfgs; let priority = task.args.priority; let rq = util::rq_ident(priority); + let rq = util::mark_internal_ident(&rq); let rqt = util::spawn_t_ident(priority); - let enum_ = util::interrupt_ident(); + + // The interrupt that runs the task dispatcher let interrupt = &analysis.interrupts.get(&priority).expect("RTIC-ICE: interrupt not found").0; let pend = { quote!( - rtic::pend(you_must_enable_the_rt_feature_for_the_pac_in_your_cargo_toml::#enum_::#interrupt); + rtic::pend(#rt_err::#enum_::#interrupt); ) }; @@ -90,21 +115,39 @@ pub fn codegen(app: &App, analysis: &Analysis, extra: &Extra) -> Vec<TokenStream }) .collect::<Vec<_>>(); - let sys_tick = util::suffixed("SysTick"); + let bound_interrupt = &monotonic.args.binds; + let disable_isr = if &*bound_interrupt.to_string() == "SysTick" { + quote!(core::mem::transmute::<_, cortex_m::peripheral::SYST>(()).disable_interrupt()) + } else { + quote!(rtic::export::NVIC::mask(#rt_err::#enum_::#bound_interrupt)) + }; + items.push(quote!( #[no_mangle] - unsafe fn #sys_tick() { - use rtic::Mutex as _; + #[allow(non_snake_case)] + unsafe fn #bound_interrupt() { - while let Some((task, index)) = rtic::export::interrupt::free(|_| #tq.dequeue()) + while let Some((task, index)) = rtic::export::interrupt::free(|_| + if let Some(mono) = #app_path::#m_ident.as_mut() { + #tq.dequeue(|| #disable_isr, mono) + } else { + // We can only use the timer queue if `init` has returned, and it + // writes the `Some(monotonic)` we are accessing here. + core::hint::unreachable_unchecked() + }) { match task { #(#arms)* } } + + rtic::export::interrupt::free(|_| if let Some(mono) = #app_path::#m_ident.as_mut() { + mono.on_interrupt(); + }); } )); } } + items } diff --git a/macros/src/codegen/util.rs b/macros/src/codegen/util.rs index fb8f1a84..6589f62d 100644 --- a/macros/src/codegen/util.rs +++ b/macros/src/codegen/util.rs @@ -77,8 +77,11 @@ pub fn inputs_ident(task: &Ident) -> Ident { } /// Generates an identifier for the `INSTANTS` buffer (`schedule` API) -pub fn instants_ident(task: &Ident) -> Ident { - Ident::new(&format!("{}_INSTANTS", task), Span::call_site()) +pub fn monotonic_instants_ident(task: &Ident, monotonic: &Ident) -> Ident { + Ident::new( + &format!("{}_{}_INSTANTS", task, monotonic), + Span::call_site(), + ) } pub fn interrupt_ident() -> Ident { @@ -103,16 +106,8 @@ pub fn is_exception(name: &Ident) -> bool { ) } -/// Generates a pre-reexport identifier for the "late resources" struct -pub fn late_resources_ident(init: &Ident) -> Ident { - Ident::new( - &format!("{}LateResources", init.to_string()), - Span::call_site(), - ) -} - -/// Mangle an ident -pub fn mangle_ident(ident: &Ident) -> Ident { +/// Mark an ident as internal +pub fn mark_internal_ident(ident: &Ident) -> Ident { Ident::new( &format!("__rtic_internal_{}", ident.to_string()), Span::call_site(), @@ -222,7 +217,7 @@ pub fn rq_ident(priority: u8) -> Ident { /// Generates an identifier for the `enum` of `schedule`-able tasks pub fn schedule_t_ident() -> Ident { - Ident::new(&"SCHED_T".to_string(), Span::call_site()) + Ident::new(&"SCHED_T", Span::call_site()) } /// Generates an identifier for the `enum` of `spawn`-able tasks @@ -233,14 +228,26 @@ pub fn spawn_t_ident(priority: u8) -> Ident { Ident::new(&format!("P{}_T", priority), Span::call_site()) } +/// Suffixed identifier pub fn suffixed(name: &str) -> Ident { let span = Span::call_site(); Ident::new(name, span) } /// Generates an identifier for a timer queue -/// -/// At most there is one timer queue -pub fn tq_ident() -> Ident { - Ident::new(&"TQ".to_string(), Span::call_site()) +pub fn tq_ident(name: &str) -> Ident { + Ident::new(&format!("TQ_{}", name), Span::call_site()) +} + +/// Generates an identifier for monotonic timer storage +pub fn monotonic_ident(name: &str) -> Ident { + Ident::new(&format!("MONOTONIC_STORAGE_{}", name), Span::call_site()) +} + +/// The name to get better RT flag errors +pub fn rt_err_ident() -> Ident { + Ident::new( + &"you_must_enable_the_rt_feature_for_the_pac_in_your_cargo_toml", + Span::call_site(), + ) } |