diff --git a/examples/async-task-multiple-prios.rs b/examples/async-task-multiple-prios.rs index d47e87b1bc..3e197987a2 100644 --- a/examples/async-task-multiple-prios.rs +++ b/examples/async-task-multiple-prios.rs @@ -10,7 +10,7 @@ use panic_semihosting as _; // task can have a mutable reference stored. // - Spawning an async task equates to it being polled once. -#[rtic::app(device = lm3s6965, dispatchers = [SSI0, UART0], peripherals = true)] +#[rtic::app(device = lm3s6965, dispatchers = [SSI0, QEI0, UART0, UART1], peripherals = true)] mod app { use cortex_m_semihosting::{debug, hprintln}; use systick_monotonic::*; diff --git a/macros/src/analyze.rs b/macros/src/analyze.rs index d255b7f5bc..b33c99815a 100644 --- a/macros/src/analyze.rs +++ b/macros/src/analyze.rs @@ -11,7 +11,8 @@ use syn::Ident; /// Extend the upstream `Analysis` struct with our field pub struct Analysis { parent: P, - pub interrupts: BTreeMap, + pub interrupts_normal: BTreeMap, + pub interrupts_async: BTreeMap, } impl ops::Deref for Analysis { @@ -24,24 +25,42 @@ impl ops::Deref for Analysis { // Assign an interrupt to each priority level pub fn app(analysis: P, app: &App) -> P { + let mut available_interrupt = app.args.extern_interrupts.clone(); + // the set of priorities (each priority only once) let priorities = app .software_tasks .values() + .filter(|task| !task.is_async) + .map(|task| task.args.priority) + .collect::>(); + + let priorities_async = app + .software_tasks + .values() + .filter(|task| task.is_async) .map(|task| task.args.priority) .collect::>(); // map from priorities to interrupts (holding name and attributes) - let interrupts: BTreeMap = priorities + + let interrupts_normal: BTreeMap = priorities .iter() .copied() .rev() - .zip(&app.args.extern_interrupts) - .map(|(p, (id, ext))| (p, (id.clone(), ext.clone()))) + .map(|p| (p, available_interrupt.pop().expect("UNREACHABLE"))) + .collect(); + + let interrupts_async: BTreeMap = priorities_async + .iter() + .copied() + .rev() + .map(|p| (p, available_interrupt.pop().expect("UNREACHABLE"))) .collect(); P::new(Analysis { parent: analysis, - interrupts, + interrupts_normal, + interrupts_async, }) } diff --git a/macros/src/check.rs b/macros/src/check.rs index 74df688c58..7bc0df350a 100644 --- a/macros/src/check.rs +++ b/macros/src/check.rs @@ -36,28 +36,39 @@ pub fn app(app: &App, _analysis: &Analysis) -> parse::Result { .iter() .map(|(name, task)| { first = Some(name); - task.args.priority + (task.args.priority, task.is_async) }) .collect::>(); - let need = priorities + let need_sync = priorities .iter() - // Only count if not 0 - .filter(|prio| **prio > 0) + // Only count if not 0 and not async + .filter(|(prio, is_async)| *prio > 0 && !*is_async) .count(); + + let need_async = priorities + .iter() + // Only count if not 0 and async + .filter(|(prio, is_async)| *prio > 0 && *is_async) + .count(); + let given = app.args.extern_interrupts.len(); - if need > given { + if need_sync + need_async > given { let s = { format!( - "not enough interrupts to dispatch \ - all software tasks (need: {}; given: {})", - need, given + "not enough interrupts to dispatch all software and async tasks \ + (need: {}; given: {}) - one interrupt is needed per priority and sync/async task", + need_sync + need_async, + given ) }; // If not enough tasks and first still is None, may cause // "custom attribute panicked" due to unwrap on None - return Err(parse::Error::new(first.unwrap().span(), &s)); + return Err(parse::Error::new( + first.expect("RTIC-ICE: needed async + needed sync").span(), + &s, + )); } // Check that all exceptions are valid; only exceptions with configurable priorities are diff --git a/macros/src/codegen.rs b/macros/src/codegen.rs index d7711b638b..60ae3dac1e 100644 --- a/macros/src/codegen.rs +++ b/macros/src/codegen.rs @@ -5,6 +5,7 @@ use rtic_syntax::ast::App; use crate::{analyze::Analysis, check::Extra}; mod assertions; +mod async_dispatchers; mod dispatchers; mod hardware_tasks; mod idle; @@ -99,6 +100,7 @@ pub fn app(app: &App, analysis: &Analysis, extra: &Extra) -> TokenStream2 { let monotonics = monotonic::codegen(app, analysis, extra); let mod_app_dispatchers = dispatchers::codegen(app, analysis, extra); + let mod_app_async_dispatchers = async_dispatchers::codegen(app, analysis, extra); let mod_app_timer_queue = timer_queue::codegen(app, analysis, extra); let user_imports = &app.user_imports; let user_code = &app.user_code; @@ -150,6 +152,8 @@ pub fn app(app: &App, analysis: &Analysis, extra: &Extra) -> TokenStream2 { #(#mod_app_dispatchers)* + #(#mod_app_async_dispatchers)* + #(#mod_app_timer_queue)* #(#mains)* diff --git a/macros/src/codegen/async_dispatchers.rs b/macros/src/codegen/async_dispatchers.rs new file mode 100644 index 0000000000..b2ee5bd33d --- /dev/null +++ b/macros/src/codegen/async_dispatchers.rs @@ -0,0 +1,130 @@ +use proc_macro2::TokenStream as TokenStream2; +use quote::quote; +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 { + let mut items = vec![]; + + let interrupts = &analysis.interrupts_async; + + // Generate executor definition and priority in global scope + for (name, task) in app.software_tasks.iter() { + if task.is_async { + let type_name = util::internal_task_ident(name, "F"); + let exec_name = util::internal_task_ident(name, "EXEC"); + let prio_name = util::internal_task_ident(name, "PRIORITY"); + + items.push(quote!( + #[allow(non_camel_case_types)] + type #type_name = impl core::future::Future + 'static; + #[allow(non_upper_case_globals)] + static #exec_name: + rtic::RacyCell> = + rtic::RacyCell::new(rtic::export::executor::AsyncTaskExecutor::new()); + + // The executors priority, this can be any value - we will overwrite it when we + // start a task + #[allow(non_upper_case_globals)] + static #prio_name: rtic::RacyCell = + unsafe { rtic::RacyCell::new(rtic::export::Priority::new(0)) }; + )); + } + } + + for (&level, channel) in &analysis.channels { + if channel + .tasks + .iter() + .map(|task_name| !app.software_tasks[task_name].is_async) + .all(|is_not_async| is_not_async) + { + // check if all tasks are not async, if so don't generate this. + continue; + } + + let mut stmts = vec![]; + let device = &extra.device; + let enum_ = util::interrupt_ident(); + let interrupt = util::suffixed(&interrupts[&level].0.to_string()); + + for name in channel + .tasks + .iter() + .filter(|name| app.software_tasks[*name].is_async) + { + let exec_name = util::internal_task_ident(name, "EXEC"); + let prio_name = util::internal_task_ident(name, "PRIORITY"); + let task = &app.software_tasks[name]; + // let cfgs = &task.cfgs; + let (_, tupled, pats, input_types) = util::regroup_inputs(&task.inputs); + let executor_run_ident = util::executor_run_ident(name); + + let n = util::capacity_literal(channel.capacity as usize + 1); + let rq = util::rq_async_ident(name); + let (rq_ty, rq_expr) = { + ( + quote!(rtic::export::ASYNCRQ<#input_types, #n>), + quote!(rtic::export::Queue::new()), + ) + }; + + items.push(quote!( + #[doc(hidden)] + #[allow(non_camel_case_types)] + #[allow(non_upper_case_globals)] + static #rq: rtic::RacyCell<#rq_ty> = rtic::RacyCell::new(#rq_expr); + )); + + stmts.push(quote!( + if !(&mut *#exec_name.get_mut()).is_running() { + if let Some(#tupled) = rtic::export::interrupt::free(|_| (&mut *#rq.get_mut()).dequeue()) { + + // The async executor needs a static priority + #prio_name.get_mut().write(rtic::export::Priority::new(PRIORITY)); + let priority: &'static _ = &*#prio_name.get(); + + (&mut *#exec_name.get_mut()).spawn(#name(#name::Context::new(priority) #(,#pats)*)); + #executor_run_ident.store(true, core::sync::atomic::Ordering::Relaxed); + } + } + + if #executor_run_ident.load(core::sync::atomic::Ordering::Relaxed) { + #executor_run_ident.store(false, core::sync::atomic::Ordering::Relaxed); + if (&mut *#exec_name.get_mut()).poll(|| { + #executor_run_ident.store(true, core::sync::atomic::Ordering::Release); + rtic::pend(#device::#enum_::#interrupt); + }) && !rtic::export::interrupt::free(|_| (&*#rq.get_mut()).is_empty()) { + // If the ready queue is not empty and the executor finished, restart this + // dispatch to check if the executor should be restarted. + rtic::pend(#device::#enum_::#interrupt); + } + } + )); + } + + let doc = format!( + "Interrupt handler to dispatch async tasks at priority {}", + level + ); + let attribute = &interrupts[&level].1.attrs; + items.push(quote!( + #[allow(non_snake_case)] + #[doc = #doc] + #[no_mangle] + #(#attribute)* + unsafe fn #interrupt() { + /// The priority of this interrupt handler + const PRIORITY: u8 = #level; + + rtic::export::run(PRIORITY, || { + #(#stmts)* + }); + } + )); + } + + items +} diff --git a/macros/src/codegen/dispatchers.rs b/macros/src/codegen/dispatchers.rs index f5f36c49f0..7a9fa0a3c2 100644 --- a/macros/src/codegen/dispatchers.rs +++ b/macros/src/codegen/dispatchers.rs @@ -5,41 +5,28 @@ 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 { +pub fn codegen(app: &App, analysis: &Analysis, _extra: &Extra) -> Vec { let mut items = vec![]; - let interrupts = &analysis.interrupts; - - // Generate executor definition and priority in global scope - for (name, task) in app.software_tasks.iter() { - if task.is_async { - let type_name = util::internal_task_ident(name, "F"); - let exec_name = util::internal_task_ident(name, "EXEC"); - let prio_name = util::internal_task_ident(name, "PRIORITY"); - - items.push(quote!( - #[allow(non_camel_case_types)] - type #type_name = impl core::future::Future + 'static; - #[allow(non_upper_case_globals)] - static #exec_name: - rtic::RacyCell> = - rtic::RacyCell::new(rtic::export::executor::AsyncTaskExecutor::new()); - - // The executors priority, this can be any value - we will overwrite it when we - // start a task - #[allow(non_upper_case_globals)] - static #prio_name: rtic::RacyCell = - unsafe { rtic::RacyCell::new(rtic::export::Priority::new(0)) }; - )); - } - } + let interrupts = &analysis.interrupts_normal; for (&level, channel) in &analysis.channels { + if channel + .tasks + .iter() + .map(|task_name| app.software_tasks[task_name].is_async) + .all(|is_async| is_async) + { + // check if all tasks are async, if so don't generate this. + continue; + } + let mut stmts = vec![]; let variants = channel .tasks .iter() + .filter(|name| !app.software_tasks[*name].is_async) .map(|name| { let cfgs = &app.software_tasks[name].cfgs; @@ -69,6 +56,7 @@ pub fn codegen(app: &App, analysis: &Analysis, extra: &Extra) -> Vec), @@ -88,9 +76,13 @@ pub fn codegen(app: &App, analysis: &Analysis, extra: &Extra) -> Vec = rtic::RacyCell::new(#rq_expr); )); - let device = &extra.device; - let enum_ = util::interrupt_ident(); - let interrupt = util::suffixed(&interrupts[&level].0.to_string()); + let interrupt = util::suffixed( + &interrupts + .get(&level) + .expect("RTIC-ICE: Unable to get interrrupt") + .0 + .to_string(), + ); let arms = channel .tasks .iter() @@ -100,36 +92,8 @@ pub fn codegen(app: &App, analysis: &Analysis, extra: &Extra) -> Vec { - if !(&mut *#exec_name.get_mut()).is_running() { - let #tupled = - (&*#inputs - .get()) - .get_unchecked(usize::from(index)) - .as_ptr() - .read(); - (&mut *#fq.get_mut()).split().0.enqueue_unchecked(index); - - // The async executor needs a static priority - #prio_name.get_mut().write(rtic::export::Priority::new(PRIORITY)); - let priority: &'static _ = &*#prio_name.get(); - - (&mut *#exec_name.get_mut()).spawn(#name(#name::Context::new(priority) #(,#pats)*)); - #executor_run_ident.store(true, core::sync::atomic::Ordering::Relaxed); - } else { - retry_queue.push_unchecked((#t::#name, index)); - } - } - ) - } else { + if !task.is_async { quote!( #(#cfgs)* #t::#name => { @@ -147,37 +111,12 @@ pub fn codegen(app: &App, analysis: &Analysis, extra: &Extra) -> Vec>(); - let n_executors = channel - .tasks - .iter() - .map(|name| { - let task = &app.software_tasks[name]; - if task.is_async { - 1 - } else { - 0 - } - }) - .sum::() - .max(1); - - // TODO: This `retry_queue` comes from the current design of the dispatcher queue handling. - // To remove this we would need to redesign how the dispatcher handles queues, and this can - // be done as an optimization later. - // - // The core issue is that we should only dequeue the ready queue if the exexutor associated - // to the task is not running. As it is today this queue is blindly dequeued, see the - // `while let Some(...) = (&mut *#rq.get_mut())...` a few lines down. The current "hack" is - // to just requeue the executor run if it should not have been dequeued. This needs however - // to be done after the ready queue has been exhausted. - stmts.push(quote!( - let mut retry_queue: rtic::export::Vec<_, #n_executors> = rtic::export::Vec::new(); - )); - stmts.push(quote!( while let Some((task, index)) = (&mut *#rq.get_mut()).split().1.dequeue() { match task { @@ -186,37 +125,6 @@ pub fn codegen(app: &App, analysis: &Analysis, extra: &Extra) -> Vec (TokenStream2, let resources = match ctxt { Context::Init => &app.init.args.local_resources, - Context::Idle => &app.idle.as_ref().unwrap().args.local_resources, + Context::Idle => { + &app.idle + .as_ref() + .expect("RTIC-ICE: unable to get idle name") + .args + .local_resources + } Context::HardwareTask(name) => &app.hardware_tasks[name].args.local_resources, Context::SoftwareTask(name) => &app.software_tasks[name].args.local_resources, }; diff --git a/macros/src/codegen/module.rs b/macros/src/codegen/module.rs index 29f276625e..913588a2ee 100644 --- a/macros/src/codegen/module.rs +++ b/macros/src/codegen/module.rs @@ -206,44 +206,75 @@ pub fn codegen( let device = &extra.device; let enum_ = util::interrupt_ident(); - let interrupt = &analysis - .interrupts - .get(&priority) - .expect("RTIC-ICE: interrupt identifer not found") - .0; + let interrupt = if spawnee.is_async { + &analysis + .interrupts_async + .get(&priority) + .expect("RTIC-ICE: interrupt identifer not found") + .0 + } else { + &analysis + .interrupts_normal + .get(&priority) + .expect("RTIC-ICE: interrupt identifer not found") + .0 + }; let internal_spawn_ident = util::internal_task_ident(name, "spawn"); // Spawn caller - items.push(quote!( + if spawnee.is_async { + 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(#(#args,)*) -> Result<(), #ty> { - let input = #tupled; + #(#cfgs)* + /// Spawns the task directly + #[allow(non_snake_case)] + #[doc(hidden)] + pub fn #internal_spawn_ident(#(#args,)*) -> Result<(), #ty> { + let input = #tupled; - unsafe { - if let Some(index) = rtic::export::interrupt::free(|_| (&mut *#fq.get_mut()).dequeue()) { - (&mut *#inputs - .get_mut()) - .get_unchecked_mut(usize::from(index)) - .as_mut_ptr() - .write(input); + unsafe { + let r = rtic::export::interrupt::free(|_| (&mut *#rq.get_mut()).enqueue(input)); - rtic::export::interrupt::free(|_| { - (&mut *#rq.get_mut()).enqueue_unchecked((#t::#name, index)); - }); - rtic::pend(#device::#enum_::#interrupt); + if r.is_ok() { + rtic::pend(#device::#enum_::#interrupt); + } - Ok(()) - } else { - Err(input) + r } - } + })); + } else { + items.push(quote!( - })); + #(#cfgs)* + /// Spawns the task directly + #[allow(non_snake_case)] + #[doc(hidden)] + pub fn #internal_spawn_ident(#(#args,)*) -> Result<(), #ty> { + let input = #tupled; + + unsafe { + if let Some(index) = rtic::export::interrupt::free(|_| (&mut *#fq.get_mut()).dequeue()) { + (&mut *#inputs + .get_mut()) + .get_unchecked_mut(usize::from(index)) + .as_mut_ptr() + .write(input); + + rtic::export::interrupt::free(|_| { + (&mut *#rq.get_mut()).enqueue_unchecked((#t::#name, index)); + }); + rtic::pend(#device::#enum_::#interrupt); + + Ok(()) + } else { + Err(input) + } + } + + })); + } module_items.push(quote!( #(#cfgs)* @@ -252,64 +283,62 @@ pub fn codegen( )); // Schedule caller - for (_, monotonic) in &app.monotonics { - let instants = util::monotonic_instants_ident(name, &monotonic.ident); - let monotonic_name = monotonic.ident.to_string(); + if !spawnee.is_async { + for (_, monotonic) in &app.monotonics { + let instants = util::monotonic_instants_ident(name, &monotonic.ident); + let monotonic_name = monotonic.ident.to_string(); - let tq = util::tq_ident(&monotonic.ident.to_string()); - let t = util::schedule_t_ident(); - let m = &monotonic.ident; - let m_ident = util::monotonic_ident(&monotonic_name); - let m_isr = &monotonic.args.binds; - let enum_ = util::interrupt_ident(); - let spawn_handle_string = format!("{}::SpawnHandle", m); + let tq = util::tq_ident(&monotonic.ident.to_string()); + let t = util::schedule_t_ident(); + let m = &monotonic.ident; + let m_ident = util::monotonic_ident(&monotonic_name); + let m_isr = &monotonic.args.binds; + let enum_ = util::interrupt_ident(); + let spawn_handle_string = format!("{}::SpawnHandle", m); - let (enable_interrupt, pend) = if &*m_isr.to_string() == "SysTick" { - ( - quote!(core::mem::transmute::<_, rtic::export::SYST>(()).enable_interrupt()), - quote!(rtic::export::SCB::set_pendst()), - ) - } else { - let rt_err = util::rt_err_ident(); - ( - quote!(rtic::export::NVIC::unmask(#rt_err::#enum_::#m_isr)), - quote!(rtic::pend(#rt_err::#enum_::#m_isr)), - ) - }; + let (enable_interrupt, pend) = if &*m_isr.to_string() == "SysTick" { + ( + quote!(core::mem::transmute::<_, rtic::export::SYST>(()).enable_interrupt()), + quote!(rtic::export::SCB::set_pendst()), + ) + } else { + let rt_err = util::rt_err_ident(); + ( + quote!(rtic::export::NVIC::unmask(#rt_err::#enum_::#m_isr)), + quote!(rtic::pend(#rt_err::#enum_::#m_isr)), + ) + }; - let tq_marker = &util::timer_queue_marker_ident(); + let tq_marker = &util::timer_queue_marker_ident(); - // For future use - // let doc = format!(" RTIC internal: {}:{}", file!(), line!()); - // items.push(quote!(#[doc = #doc])); - let internal_spawn_handle_ident = - util::internal_monotonics_ident(name, m, "SpawnHandle"); - let internal_spawn_at_ident = util::internal_monotonics_ident(name, m, "spawn_at"); - let internal_spawn_after_ident = - util::internal_monotonics_ident(name, m, "spawn_after"); + let internal_spawn_handle_ident = + util::internal_monotonics_ident(name, m, "SpawnHandle"); + let internal_spawn_at_ident = util::internal_monotonics_ident(name, m, "spawn_at"); + let internal_spawn_after_ident = + util::internal_monotonics_ident(name, m, "spawn_after"); - if monotonic.args.default { - module_items.push(quote!( - #[doc(inline)] - pub use #m::spawn_after; - #[doc(inline)] - pub use #m::spawn_at; - #[doc(inline)] - pub use #m::SpawnHandle; - )); - } - module_items.push(quote!( - pub mod #m { - #[doc(inline)] - pub use super::super::#internal_spawn_after_ident as spawn_after; - #[doc(inline)] - pub use super::super::#internal_spawn_at_ident as spawn_at; - #[doc(inline)] - pub use super::super::#internal_spawn_handle_ident as SpawnHandle; + if monotonic.args.default { + module_items.push(quote!( + #[doc(inline)] + pub use #m::spawn_after; + #[doc(inline)] + pub use #m::spawn_at; + #[doc(inline)] + pub use #m::SpawnHandle; + )); } - )); + module_items.push(quote!( + pub mod #m { + #[doc(inline)] + pub use super::super::#internal_spawn_after_ident as spawn_after; + #[doc(inline)] + pub use super::super::#internal_spawn_at_ident as spawn_at; + #[doc(inline)] + pub use super::super::#internal_spawn_handle_ident as SpawnHandle; + } + )); - items.push(quote!( + items.push(quote!( #(#cfgs)* #[allow(non_snake_case)] #[allow(non_camel_case_types)] @@ -436,6 +465,7 @@ pub fn codegen( } } )); + } } } diff --git a/macros/src/codegen/pre_init.rs b/macros/src/codegen/pre_init.rs index 3d541a4749..5708c0f1a5 100644 --- a/macros/src/codegen/pre_init.rs +++ b/macros/src/codegen/pre_init.rs @@ -15,6 +15,10 @@ pub fn codegen(app: &App, analysis: &Analysis, extra: &Extra) -> Vec Vec = prio_to_masks.entry(priority - 1).or_default(); v.push(quote!(#device::Interrupt::#name as u32)); mask_ids.push(quote!(#device::Interrupt::#name as u32)); } diff --git a/macros/src/codegen/shared_resources_struct.rs b/macros/src/codegen/shared_resources_struct.rs index 90cbc1beef..9aade29332 100644 --- a/macros/src/codegen/shared_resources_struct.rs +++ b/macros/src/codegen/shared_resources_struct.rs @@ -10,7 +10,13 @@ pub fn codegen(ctxt: Context, needs_lt: &mut bool, app: &App) -> (TokenStream2, let resources = match ctxt { Context::Init => unreachable!("Tried to generate shared resources struct for init"), - Context::Idle => &app.idle.as_ref().unwrap().args.shared_resources, + Context::Idle => { + &app.idle + .as_ref() + .expect("RTIC-ICE: unable to get idle name") + .args + .shared_resources + } Context::HardwareTask(name) => &app.hardware_tasks[name].args.shared_resources, Context::SoftwareTask(name) => &app.software_tasks[name].args.shared_resources, }; diff --git a/macros/src/codegen/software_tasks.rs b/macros/src/codegen/software_tasks.rs index 71869b6e61..08f2102fcf 100644 --- a/macros/src/codegen/software_tasks.rs +++ b/macros/src/codegen/software_tasks.rs @@ -36,38 +36,40 @@ pub fn codegen( let cap_lit = util::capacity_literal(cap as usize); let cap_lit_p1 = util::capacity_literal(cap as usize + 1); - // Create free queues and inputs / instants buffers - let fq = util::fq_ident(name); + if !task.is_async { + // Create free queues and inputs / instants buffers + let fq = util::fq_ident(name); - #[allow(clippy::redundant_closure)] - let (fq_ty, fq_expr, mk_uninit): (_, _, Box Option<_>>) = { - ( - quote!(rtic::export::SCFQ<#cap_lit_p1>), - quote!(rtic::export::Queue::new()), - Box::new(|| Some(util::link_section_uninit())), - ) - }; - mod_app.push(quote!( - // /// Queue version of a free-list that keeps track of empty slots in - // /// the following buffers - #[allow(non_camel_case_types)] - #[allow(non_upper_case_globals)] - #[doc(hidden)] - static #fq: rtic::RacyCell<#fq_ty> = rtic::RacyCell::new(#fq_expr); - )); + #[allow(clippy::redundant_closure)] + let (fq_ty, fq_expr, mk_uninit): (_, _, Box Option<_>>) = { + ( + quote!(rtic::export::SCFQ<#cap_lit_p1>), + quote!(rtic::export::Queue::new()), + Box::new(|| Some(util::link_section_uninit())), + ) + }; - let elems = &(0..cap) - .map(|_| quote!(core::mem::MaybeUninit::uninit())) - .collect::>(); - - for (_, monotonic) in &app.monotonics { - let instants = util::monotonic_instants_ident(name, &monotonic.ident); - let mono_type = &monotonic.ty; - - let uninit = mk_uninit(); - // For future use - // let doc = format!(" RTIC internal: {}:{}", file!(), line!()); mod_app.push(quote!( + // /// Queue version of a free-list that keeps track of empty slots in + // /// the following buffers + #[allow(non_camel_case_types)] + #[allow(non_upper_case_globals)] + #[doc(hidden)] + static #fq: rtic::RacyCell<#fq_ty> = rtic::RacyCell::new(#fq_expr); + )); + + let elems = &(0..cap) + .map(|_| quote!(core::mem::MaybeUninit::uninit())) + .collect::>(); + + for (_, monotonic) in &app.monotonics { + let instants = util::monotonic_instants_ident(name, &monotonic.ident); + let mono_type = &monotonic.ty; + + let uninit = mk_uninit(); + // For future use + // let doc = format!(" RTIC internal: {}:{}", file!(), line!()); + mod_app.push(quote!( #uninit // /// Buffer that holds the instants associated to the inputs of a task // #[doc = #doc] @@ -78,20 +80,22 @@ pub fn codegen( rtic::RacyCell<[core::mem::MaybeUninit<<#mono_type as rtic::Monotonic>::Instant>; #cap_lit]> = rtic::RacyCell::new([#(#elems,)*]); )); + } + + let uninit = mk_uninit(); + let inputs_ident = util::inputs_ident(name); + + // Buffer that holds the inputs of a task + mod_app.push(quote!( + #uninit + #[allow(non_camel_case_types)] + #[allow(non_upper_case_globals)] + #[doc(hidden)] + static #inputs_ident: rtic::RacyCell<[core::mem::MaybeUninit<#input_ty>; #cap_lit]> = + rtic::RacyCell::new([#(#elems,)*]); + )); } - let uninit = mk_uninit(); - let inputs_ident = util::inputs_ident(name); - - mod_app.push(quote!( - #uninit - // /// Buffer that holds the inputs of a task - #[allow(non_camel_case_types)] - #[allow(non_upper_case_globals)] - #[doc(hidden)] - static #inputs_ident: rtic::RacyCell<[core::mem::MaybeUninit<#input_ty>; #cap_lit]> = - rtic::RacyCell::new([#(#elems,)*]); - )); if task.is_async { let executor_ident = util::executor_run_ident(name); mod_app.push(quote!( diff --git a/macros/src/codegen/timer_queue.rs b/macros/src/codegen/timer_queue.rs index db6a9e3d28..092095f509 100644 --- a/macros/src/codegen/timer_queue.rs +++ b/macros/src/codegen/timer_queue.rs @@ -26,6 +26,7 @@ pub fn codegen(app: &App, analysis: &Analysis, _extra: &Extra) -> Vec Vec Vec Ident { let s = match ctxt { Context::Init => app.init.name.to_string(), - Context::Idle => app.idle.as_ref().unwrap().name.to_string(), + Context::Idle => app + .idle + .as_ref() + .expect("RTIC-ICE: unable to find idle name") + .name + .to_string(), Context::HardwareTask(ident) | Context::SoftwareTask(ident) => ident.to_string(), }; @@ -195,7 +200,12 @@ pub fn get_task_name(ctxt: Context, app: &App) -> Ident { pub fn shared_resources_ident(ctxt: Context, app: &App) -> Ident { let mut s = match ctxt { Context::Init => app.init.name.to_string(), - Context::Idle => app.idle.as_ref().unwrap().name.to_string(), + Context::Idle => app + .idle + .as_ref() + .expect("RTIC-ICE: unable to find idle name") + .name + .to_string(), Context::HardwareTask(ident) | Context::SoftwareTask(ident) => ident.to_string(), }; @@ -208,7 +218,12 @@ pub fn shared_resources_ident(ctxt: Context, app: &App) -> Ident { pub fn local_resources_ident(ctxt: Context, app: &App) -> Ident { let mut s = match ctxt { Context::Init => app.init.name.to_string(), - Context::Idle => app.idle.as_ref().unwrap().name.to_string(), + Context::Idle => app + .idle + .as_ref() + .expect("RTIC-ICE: unable to find idle name") + .name + .to_string(), Context::HardwareTask(ident) | Context::SoftwareTask(ident) => ident.to_string(), }; @@ -225,6 +240,11 @@ pub fn rq_ident(priority: u8) -> Ident { mark_internal_name(&format!("P{}_RQ", priority)) } +/// Generates an identifier for a ready queue, async task version +pub fn rq_async_ident(async_task_name: &Ident) -> Ident { + mark_internal_name(&format!("ASYNC_TACK_{}_RQ", async_task_name)) +} + /// Generates an identifier for the `enum` of `schedule`-able tasks pub fn schedule_t_ident() -> Ident { mark_internal_name("SCHED_T") diff --git a/macros/src/tests/single.rs b/macros/src/tests/single.rs index f20c9ccbb3..7f39a21401 100644 --- a/macros/src/tests/single.rs +++ b/macros/src/tests/single.rs @@ -33,8 +33,8 @@ fn analyze() { .unwrap(); let analysis = crate::analyze::app(analysis, &app); - let interrupts = &analysis.interrupts; + let interrupts = &analysis.interrupts_normal; assert_eq!(interrupts.len(), 2); - assert_eq!(interrupts[&2].0.to_string(), "B"); - assert_eq!(interrupts[&1].0.to_string(), "A"); + assert_eq!(interrupts[&2].0.to_string(), "A"); + assert_eq!(interrupts[&1].0.to_string(), "B"); } diff --git a/src/export.rs b/src/export.rs index ec18c9cf41..da4a6917b4 100644 --- a/src/export.rs +++ b/src/export.rs @@ -145,6 +145,7 @@ pub mod executor { pub type SCFQ = Queue; pub type SCRQ = Queue<(T, u8), N>; +pub type ASYNCRQ = Queue; /// Mask is used to store interrupt masks on systems without a BASEPRI register (M0, M0+, M23). /// It needs to be large enough to cover all the relevant interrupts in use. diff --git a/ui/extern-interrupt-not-enough.stderr b/ui/extern-interrupt-not-enough.stderr index a667c58824..d8c01b9a1a 100644 --- a/ui/extern-interrupt-not-enough.stderr +++ b/ui/extern-interrupt-not-enough.stderr @@ -1,5 +1,5 @@ -error: not enough interrupts to dispatch all software tasks (need: 1; given: 0) - --> $DIR/extern-interrupt-not-enough.rs:17:8 +error: not enough interrupts to dispatch all software and async tasks (need: 1; given: 0) - one interrupt is needed per priority and sync/async task + --> ui/extern-interrupt-not-enough.rs:17:8 | 17 | fn a(_: a::Context) {} | ^ diff --git a/ui/task-priority-too-high.stderr b/ui/task-priority-too-high.stderr index 8b905c4039..a7a15ebfe5 100644 --- a/ui/task-priority-too-high.stderr +++ b/ui/task-priority-too-high.stderr @@ -12,4 +12,4 @@ error[E0080]: evaluation of constant value failed 3 | #[rtic::app(device = lm3s6965)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ the evaluated program panicked at 'Maximum priority used by interrupt vector 'I2C0' is more than supported by hardware', $DIR/ui/task-priority-too-high.rs:3:1 | - = note: this error originates in the macro `$crate::panic::panic_2021` (in Nightly builds, run with -Z macro-backtrace for more info) + = note: this error originates in the macro `$crate::panic::panic_2021` which comes from the expansion of the macro `::core::panic` (in Nightly builds, run with -Z macro-backtrace for more info)