Broke out async dispatchers into their own place

This commit is contained in:
Emil Fresk 2022-09-21 21:33:31 +02:00
parent b1d499a744
commit 1341cc5bbe
18 changed files with 419 additions and 266 deletions

View file

@ -10,7 +10,7 @@ use panic_semihosting as _;
// task can have a mutable reference stored. // task can have a mutable reference stored.
// - Spawning an async task equates to it being polled once. // - 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 { mod app {
use cortex_m_semihosting::{debug, hprintln}; use cortex_m_semihosting::{debug, hprintln};
use systick_monotonic::*; use systick_monotonic::*;

View file

@ -11,7 +11,8 @@ use syn::Ident;
/// Extend the upstream `Analysis` struct with our field /// Extend the upstream `Analysis` struct with our field
pub struct Analysis { pub struct Analysis {
parent: P<analyze::Analysis>, parent: P<analyze::Analysis>,
pub interrupts: BTreeMap<Priority, (Ident, ExternInterrupt)>, pub interrupts_normal: BTreeMap<Priority, (Ident, ExternInterrupt)>,
pub interrupts_async: BTreeMap<Priority, (Ident, ExternInterrupt)>,
} }
impl ops::Deref for Analysis { impl ops::Deref for Analysis {
@ -24,24 +25,42 @@ impl ops::Deref for Analysis {
// Assign an interrupt to each priority level // Assign an interrupt to each priority level
pub fn app(analysis: P<analyze::Analysis>, app: &App) -> P<Analysis> { pub fn app(analysis: P<analyze::Analysis>, app: &App) -> P<Analysis> {
let mut available_interrupt = app.args.extern_interrupts.clone();
// the set of priorities (each priority only once) // the set of priorities (each priority only once)
let priorities = app let priorities = app
.software_tasks .software_tasks
.values() .values()
.filter(|task| !task.is_async)
.map(|task| task.args.priority)
.collect::<BTreeSet<_>>();
let priorities_async = app
.software_tasks
.values()
.filter(|task| task.is_async)
.map(|task| task.args.priority) .map(|task| task.args.priority)
.collect::<BTreeSet<_>>(); .collect::<BTreeSet<_>>();
// map from priorities to interrupts (holding name and attributes) // map from priorities to interrupts (holding name and attributes)
let interrupts: BTreeMap<Priority, _> = priorities
let interrupts_normal: BTreeMap<Priority, _> = priorities
.iter() .iter()
.copied() .copied()
.rev() .rev()
.zip(&app.args.extern_interrupts) .map(|p| (p, available_interrupt.pop().expect("UNREACHABLE")))
.map(|(p, (id, ext))| (p, (id.clone(), ext.clone()))) .collect();
let interrupts_async: BTreeMap<Priority, _> = priorities_async
.iter()
.copied()
.rev()
.map(|p| (p, available_interrupt.pop().expect("UNREACHABLE")))
.collect(); .collect();
P::new(Analysis { P::new(Analysis {
parent: analysis, parent: analysis,
interrupts, interrupts_normal,
interrupts_async,
}) })
} }

View file

@ -36,28 +36,39 @@ pub fn app(app: &App, _analysis: &Analysis) -> parse::Result<Extra> {
.iter() .iter()
.map(|(name, task)| { .map(|(name, task)| {
first = Some(name); first = Some(name);
task.args.priority (task.args.priority, task.is_async)
}) })
.collect::<HashSet<_>>(); .collect::<HashSet<_>>();
let need = priorities let need_sync = priorities
.iter() .iter()
// Only count if not 0 // Only count if not 0 and not async
.filter(|prio| **prio > 0) .filter(|(prio, is_async)| *prio > 0 && !*is_async)
.count(); .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(); let given = app.args.extern_interrupts.len();
if need > given { if need_sync + need_async > given {
let s = { let s = {
format!( format!(
"not enough interrupts to dispatch \ "not enough interrupts to dispatch all software and async tasks \
all software tasks (need: {}; given: {})", (need: {}; given: {}) - one interrupt is needed per priority and sync/async task",
need, given need_sync + need_async,
given
) )
}; };
// If not enough tasks and first still is None, may cause // If not enough tasks and first still is None, may cause
// "custom attribute panicked" due to unwrap on None // "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 // Check that all exceptions are valid; only exceptions with configurable priorities are

View file

@ -5,6 +5,7 @@ use rtic_syntax::ast::App;
use crate::{analyze::Analysis, check::Extra}; use crate::{analyze::Analysis, check::Extra};
mod assertions; mod assertions;
mod async_dispatchers;
mod dispatchers; mod dispatchers;
mod hardware_tasks; mod hardware_tasks;
mod idle; mod idle;
@ -99,6 +100,7 @@ pub fn app(app: &App, analysis: &Analysis, extra: &Extra) -> TokenStream2 {
let monotonics = monotonic::codegen(app, analysis, extra); let monotonics = monotonic::codegen(app, analysis, extra);
let mod_app_dispatchers = dispatchers::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 mod_app_timer_queue = timer_queue::codegen(app, analysis, extra);
let user_imports = &app.user_imports; let user_imports = &app.user_imports;
let user_code = &app.user_code; 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_dispatchers)*
#(#mod_app_async_dispatchers)*
#(#mod_app_timer_queue)* #(#mod_app_timer_queue)*
#(#mains)* #(#mains)*

View file

@ -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<TokenStream2> {
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::export::executor::AsyncTaskExecutor<#type_name>> =
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<rtic::export::Priority> =
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
}

View file

@ -5,41 +5,28 @@ use rtic_syntax::ast::App;
use crate::{analyze::Analysis, check::Extra, codegen::util}; use crate::{analyze::Analysis, check::Extra, codegen::util};
/// Generates task dispatchers /// 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 mut items = vec![];
let interrupts = &analysis.interrupts; let interrupts = &analysis.interrupts_normal;
// 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::export::executor::AsyncTaskExecutor<#type_name>> =
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<rtic::export::Priority> =
unsafe { rtic::RacyCell::new(rtic::export::Priority::new(0)) };
));
}
}
for (&level, channel) in &analysis.channels { 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 mut stmts = vec![];
let variants = channel let variants = channel
.tasks .tasks
.iter() .iter()
.filter(|name| !app.software_tasks[*name].is_async)
.map(|name| { .map(|name| {
let cfgs = &app.software_tasks[name].cfgs; let cfgs = &app.software_tasks[name].cfgs;
@ -69,6 +56,7 @@ pub fn codegen(app: &App, analysis: &Analysis, extra: &Extra) -> Vec<TokenStream
let n = util::capacity_literal(channel.capacity as usize + 1); let n = util::capacity_literal(channel.capacity as usize + 1);
let rq = util::rq_ident(level); let rq = util::rq_ident(level);
// let (_, _, _, input_ty) = util::regroup_inputs(inputs);
let (rq_ty, rq_expr) = { let (rq_ty, rq_expr) = {
( (
quote!(rtic::export::SCRQ<#t, #n>), quote!(rtic::export::SCRQ<#t, #n>),
@ -88,9 +76,13 @@ pub fn codegen(app: &App, analysis: &Analysis, extra: &Extra) -> Vec<TokenStream
static #rq: rtic::RacyCell<#rq_ty> = rtic::RacyCell::new(#rq_expr); static #rq: rtic::RacyCell<#rq_ty> = rtic::RacyCell::new(#rq_expr);
)); ));
let device = &extra.device; let interrupt = util::suffixed(
let enum_ = util::interrupt_ident(); &interrupts
let interrupt = util::suffixed(&interrupts[&level].0.to_string()); .get(&level)
.expect("RTIC-ICE: Unable to get interrrupt")
.0
.to_string(),
);
let arms = channel let arms = channel
.tasks .tasks
.iter() .iter()
@ -100,36 +92,8 @@ pub fn codegen(app: &App, analysis: &Analysis, extra: &Extra) -> Vec<TokenStream
let fq = util::fq_ident(name); let fq = util::fq_ident(name);
let inputs = util::inputs_ident(name); let inputs = util::inputs_ident(name);
let (_, tupled, pats, _) = util::regroup_inputs(&task.inputs); let (_, tupled, pats, _) = util::regroup_inputs(&task.inputs);
let exec_name = util::internal_task_ident(name, "EXEC");
let prio_name = util::internal_task_ident(name, "PRIORITY");
if task.is_async { if !task.is_async {
let executor_run_ident = util::executor_run_ident(name);
quote!(
#(#cfgs)*
#t::#name => {
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 {
quote!( quote!(
#(#cfgs)* #(#cfgs)*
#t::#name => { #t::#name => {
@ -147,37 +111,12 @@ pub fn codegen(app: &App, analysis: &Analysis, extra: &Extra) -> Vec<TokenStream
) )
} }
) )
} else {
quote!()
} }
}) })
.collect::<Vec<_>>(); .collect::<Vec<_>>();
let n_executors = channel
.tasks
.iter()
.map(|name| {
let task = &app.software_tasks[name];
if task.is_async {
1
} else {
0
}
})
.sum::<usize>()
.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!( stmts.push(quote!(
while let Some((task, index)) = (&mut *#rq.get_mut()).split().1.dequeue() { while let Some((task, index)) = (&mut *#rq.get_mut()).split().1.dequeue() {
match task { match task {
@ -186,37 +125,6 @@ pub fn codegen(app: &App, analysis: &Analysis, extra: &Extra) -> Vec<TokenStream
} }
)); ));
for name in channel
.tasks
.iter()
.filter(|name| app.software_tasks[*name].is_async)
{
let exec_name = util::internal_task_ident(name, "EXEC");
let executor_run_ident = util::executor_run_ident(name);
stmts.push(quote!(
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);
}) && !retry_queue.is_empty() {
// If the retry queue is not empty and the executor finished, restart this
// dispatch to check if the executor should be restarted.
rtic::pend(#device::#enum_::#interrupt);
}
}
));
}
stmts.push(quote!(
while let Some((task, index)) = retry_queue.pop() {
rtic::export::interrupt::free(|_| {
(&mut *#rq.get_mut()).enqueue_unchecked((task, index));
});
}
));
let doc = format!("Interrupt handler to dispatch tasks at priority {}", level); let doc = format!("Interrupt handler to dispatch tasks at priority {}", level);
let attribute = &interrupts[&level].1.attrs; let attribute = &interrupts[&level].1.attrs;
items.push(quote!( items.push(quote!(

View file

@ -13,7 +13,13 @@ pub fn codegen(ctxt: Context, needs_lt: &mut bool, app: &App) -> (TokenStream2,
let resources = match ctxt { let resources = match ctxt {
Context::Init => &app.init.args.local_resources, 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::HardwareTask(name) => &app.hardware_tasks[name].args.local_resources,
Context::SoftwareTask(name) => &app.software_tasks[name].args.local_resources, Context::SoftwareTask(name) => &app.software_tasks[name].args.local_resources,
}; };

View file

@ -206,44 +206,75 @@ pub fn codegen(
let device = &extra.device; let device = &extra.device;
let enum_ = util::interrupt_ident(); let enum_ = util::interrupt_ident();
let interrupt = &analysis let interrupt = if spawnee.is_async {
.interrupts &analysis
.get(&priority) .interrupts_async
.expect("RTIC-ICE: interrupt identifer not found") .get(&priority)
.0; .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"); let internal_spawn_ident = util::internal_task_ident(name, "spawn");
// Spawn caller // Spawn caller
items.push(quote!( if spawnee.is_async {
let rq = util::rq_async_ident(name);
items.push(quote!(
#(#cfgs)* #(#cfgs)*
/// Spawns the task directly /// Spawns the task directly
#[allow(non_snake_case)] #[allow(non_snake_case)]
#[doc(hidden)] #[doc(hidden)]
pub fn #internal_spawn_ident(#(#args,)*) -> Result<(), #ty> { pub fn #internal_spawn_ident(#(#args,)*) -> Result<(), #ty> {
let input = #tupled; let input = #tupled;
unsafe { unsafe {
if let Some(index) = rtic::export::interrupt::free(|_| (&mut *#fq.get_mut()).dequeue()) { let r = rtic::export::interrupt::free(|_| (&mut *#rq.get_mut()).enqueue(input));
(&mut *#inputs
.get_mut())
.get_unchecked_mut(usize::from(index))
.as_mut_ptr()
.write(input);
rtic::export::interrupt::free(|_| { if r.is_ok() {
(&mut *#rq.get_mut()).enqueue_unchecked((#t::#name, index)); rtic::pend(#device::#enum_::#interrupt);
}); }
rtic::pend(#device::#enum_::#interrupt);
Ok(()) r
} else {
Err(input)
} }
} }));
} 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!( module_items.push(quote!(
#(#cfgs)* #(#cfgs)*
@ -252,64 +283,62 @@ pub fn codegen(
)); ));
// Schedule caller // Schedule caller
for (_, monotonic) in &app.monotonics { if !spawnee.is_async {
let instants = util::monotonic_instants_ident(name, &monotonic.ident); for (_, monotonic) in &app.monotonics {
let monotonic_name = monotonic.ident.to_string(); 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 tq = util::tq_ident(&monotonic.ident.to_string());
let t = util::schedule_t_ident(); let t = util::schedule_t_ident();
let m = &monotonic.ident; let m = &monotonic.ident;
let m_ident = util::monotonic_ident(&monotonic_name); let m_ident = util::monotonic_ident(&monotonic_name);
let m_isr = &monotonic.args.binds; let m_isr = &monotonic.args.binds;
let enum_ = util::interrupt_ident(); let enum_ = util::interrupt_ident();
let spawn_handle_string = format!("{}::SpawnHandle", m); let spawn_handle_string = format!("{}::SpawnHandle", m);
let (enable_interrupt, pend) = if &*m_isr.to_string() == "SysTick" { let (enable_interrupt, pend) = if &*m_isr.to_string() == "SysTick" {
( (
quote!(core::mem::transmute::<_, rtic::export::SYST>(()).enable_interrupt()), quote!(core::mem::transmute::<_, rtic::export::SYST>(()).enable_interrupt()),
quote!(rtic::export::SCB::set_pendst()), quote!(rtic::export::SCB::set_pendst()),
) )
} else { } else {
let rt_err = util::rt_err_ident(); let rt_err = util::rt_err_ident();
( (
quote!(rtic::export::NVIC::unmask(#rt_err::#enum_::#m_isr)), quote!(rtic::export::NVIC::unmask(#rt_err::#enum_::#m_isr)),
quote!(rtic::pend(#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 internal_spawn_handle_ident =
// let doc = format!(" RTIC internal: {}:{}", file!(), line!()); util::internal_monotonics_ident(name, m, "SpawnHandle");
// items.push(quote!(#[doc = #doc])); let internal_spawn_at_ident = util::internal_monotonics_ident(name, m, "spawn_at");
let internal_spawn_handle_ident = let internal_spawn_after_ident =
util::internal_monotonics_ident(name, m, "SpawnHandle"); util::internal_monotonics_ident(name, m, "spawn_after");
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 { if monotonic.args.default {
module_items.push(quote!( module_items.push(quote!(
#[doc(inline)] #[doc(inline)]
pub use #m::spawn_after; pub use #m::spawn_after;
#[doc(inline)] #[doc(inline)]
pub use #m::spawn_at; pub use #m::spawn_at;
#[doc(inline)] #[doc(inline)]
pub use #m::SpawnHandle; 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;
} }
)); 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)* #(#cfgs)*
#[allow(non_snake_case)] #[allow(non_snake_case)]
#[allow(non_camel_case_types)] #[allow(non_camel_case_types)]
@ -436,6 +465,7 @@ pub fn codegen(
} }
} }
)); ));
}
} }
} }

View file

@ -15,6 +15,10 @@ pub fn codegen(app: &App, analysis: &Analysis, extra: &Extra) -> Vec<TokenStream
// Populate the FreeQueue // Populate the FreeQueue
for (name, task) in &app.software_tasks { for (name, task) in &app.software_tasks {
if task.is_async {
continue;
}
let cap = task.args.capacity; let cap = task.args.capacity;
let fq_ident = util::fq_ident(name); let fq_ident = util::fq_ident(name);
@ -38,7 +42,11 @@ pub fn codegen(app: &App, analysis: &Analysis, extra: &Extra) -> Vec<TokenStream
stmts.push(quote!(let _ = #rt_err::#interrupt::#name;)); stmts.push(quote!(let _ = #rt_err::#interrupt::#name;));
} }
let interrupt_ids = analysis.interrupts.iter().map(|(p, (id, _))| (p, id)); let interrupt_ids = analysis
.interrupts_normal
.iter()
.map(|(p, (id, _))| (p, id))
.chain(analysis.interrupts_async.iter().map(|(p, (id, _))| (p, id)));
// Unmask interrupts and set their priorities // Unmask interrupts and set their priorities
for (&priority, name) in interrupt_ids.chain(app.hardware_tasks.values().filter_map(|task| { for (&priority, name) in interrupt_ids.chain(app.hardware_tasks.values().filter_map(|task| {

View file

@ -112,7 +112,11 @@ pub fn codegen(
}; };
// Computing mapping of used interrupts to masks // Computing mapping of used interrupts to masks
let interrupt_ids = analysis.interrupts.iter().map(|(p, (id, _))| (p, id)); let interrupt_ids = analysis
.interrupts_normal
.iter()
.map(|(p, (id, _))| (p, id))
.chain(analysis.interrupts_async.iter().map(|(p, (id, _))| (p, id)));
let mut prio_to_masks = HashMap::new(); let mut prio_to_masks = HashMap::new();
let device = &extra.device; let device = &extra.device;
@ -147,7 +151,7 @@ pub fn codegen(
None None
} }
})) { })) {
let v = prio_to_masks.entry(priority - 1).or_insert(Vec::new()); let v: &mut Vec<_> = prio_to_masks.entry(priority - 1).or_default();
v.push(quote!(#device::Interrupt::#name as u32)); v.push(quote!(#device::Interrupt::#name as u32));
mask_ids.push(quote!(#device::Interrupt::#name as u32)); mask_ids.push(quote!(#device::Interrupt::#name as u32));
} }

View file

@ -10,7 +10,13 @@ pub fn codegen(ctxt: Context, needs_lt: &mut bool, app: &App) -> (TokenStream2,
let resources = match ctxt { let resources = match ctxt {
Context::Init => unreachable!("Tried to generate shared resources struct for init"), 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::HardwareTask(name) => &app.hardware_tasks[name].args.shared_resources,
Context::SoftwareTask(name) => &app.software_tasks[name].args.shared_resources, Context::SoftwareTask(name) => &app.software_tasks[name].args.shared_resources,
}; };

View file

@ -36,38 +36,40 @@ pub fn codegen(
let cap_lit = util::capacity_literal(cap as usize); let cap_lit = util::capacity_literal(cap as usize);
let cap_lit_p1 = util::capacity_literal(cap as usize + 1); let cap_lit_p1 = util::capacity_literal(cap as usize + 1);
// Create free queues and inputs / instants buffers if !task.is_async {
let fq = util::fq_ident(name); // Create free queues and inputs / instants buffers
let fq = util::fq_ident(name);
#[allow(clippy::redundant_closure)] #[allow(clippy::redundant_closure)]
let (fq_ty, fq_expr, mk_uninit): (_, _, Box<dyn Fn() -> Option<_>>) = { let (fq_ty, fq_expr, mk_uninit): (_, _, Box<dyn Fn() -> Option<_>>) = {
( (
quote!(rtic::export::SCFQ<#cap_lit_p1>), quote!(rtic::export::SCFQ<#cap_lit_p1>),
quote!(rtic::export::Queue::new()), quote!(rtic::export::Queue::new()),
Box::new(|| Some(util::link_section_uninit())), 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);
));
let elems = &(0..cap)
.map(|_| quote!(core::mem::MaybeUninit::uninit()))
.collect::<Vec<_>>();
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!( 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::<Vec<_>>();
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 #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 = #doc] // #[doc = #doc]
@ -78,20 +80,22 @@ pub fn codegen(
rtic::RacyCell<[core::mem::MaybeUninit<<#mono_type as rtic::Monotonic>::Instant>; #cap_lit]> = rtic::RacyCell<[core::mem::MaybeUninit<<#mono_type as rtic::Monotonic>::Instant>; #cap_lit]> =
rtic::RacyCell::new([#(#elems,)*]); 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 { if task.is_async {
let executor_ident = util::executor_run_ident(name); let executor_ident = util::executor_run_ident(name);
mod_app.push(quote!( mod_app.push(quote!(

View file

@ -26,6 +26,7 @@ pub fn codegen(app: &App, analysis: &Analysis, _extra: &Extra) -> Vec<TokenStrea
let variants = app let variants = app
.software_tasks .software_tasks
.iter() .iter()
.filter(|(_, task)| !task.is_async)
.map(|(name, task)| { .map(|(name, task)| {
let cfgs = &task.cfgs; let cfgs = &task.cfgs;
@ -103,6 +104,7 @@ pub fn codegen(app: &App, analysis: &Analysis, _extra: &Extra) -> Vec<TokenStrea
let arms = app let arms = app
.software_tasks .software_tasks
.iter() .iter()
.filter(|(_, task)| !task.is_async)
.map(|(name, task)| { .map(|(name, task)| {
let cfgs = &task.cfgs; let cfgs = &task.cfgs;
let priority = task.args.priority; let priority = task.args.priority;
@ -110,7 +112,7 @@ pub fn codegen(app: &App, analysis: &Analysis, _extra: &Extra) -> Vec<TokenStrea
let rqt = util::spawn_t_ident(priority); let rqt = util::spawn_t_ident(priority);
// The interrupt that runs the task dispatcher // The interrupt that runs the task dispatcher
let interrupt = &analysis.interrupts.get(&priority).expect("RTIC-ICE: interrupt not found").0; let interrupt = &analysis.interrupts_normal.get(&priority).expect("RTIC-ICE: interrupt not found").0;
let pend = { let pend = {
quote!( quote!(

View file

@ -184,7 +184,12 @@ pub fn regroup_inputs(
pub fn get_task_name(ctxt: Context, app: &App) -> Ident { pub fn get_task_name(ctxt: Context, app: &App) -> Ident {
let s = match ctxt { let s = match ctxt {
Context::Init => app.init.name.to_string(), 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(), 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 { pub fn shared_resources_ident(ctxt: Context, app: &App) -> Ident {
let mut s = match ctxt { let mut s = match ctxt {
Context::Init => app.init.name.to_string(), 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(), 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 { pub fn local_resources_ident(ctxt: Context, app: &App) -> Ident {
let mut s = match ctxt { let mut s = match ctxt {
Context::Init => app.init.name.to_string(), 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(), 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)) 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 /// Generates an identifier for the `enum` of `schedule`-able tasks
pub fn schedule_t_ident() -> Ident { pub fn schedule_t_ident() -> Ident {
mark_internal_name("SCHED_T") mark_internal_name("SCHED_T")

View file

@ -33,8 +33,8 @@ fn analyze() {
.unwrap(); .unwrap();
let analysis = crate::analyze::app(analysis, &app); let analysis = crate::analyze::app(analysis, &app);
let interrupts = &analysis.interrupts; let interrupts = &analysis.interrupts_normal;
assert_eq!(interrupts.len(), 2); assert_eq!(interrupts.len(), 2);
assert_eq!(interrupts[&2].0.to_string(), "B"); assert_eq!(interrupts[&2].0.to_string(), "A");
assert_eq!(interrupts[&1].0.to_string(), "A"); assert_eq!(interrupts[&1].0.to_string(), "B");
} }

View file

@ -145,6 +145,7 @@ pub mod executor {
pub type SCFQ<const N: usize> = Queue<u8, N>; pub type SCFQ<const N: usize> = Queue<u8, N>;
pub type SCRQ<T, const N: usize> = Queue<(T, u8), N>; pub type SCRQ<T, const N: usize> = Queue<(T, u8), N>;
pub type ASYNCRQ<T, const N: usize> = Queue<T, N>;
/// Mask is used to store interrupt masks on systems without a BASEPRI register (M0, M0+, M23). /// 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. /// It needs to be large enough to cover all the relevant interrupts in use.

View file

@ -1,5 +1,5 @@
error: not enough interrupts to dispatch all software tasks (need: 1; given: 0) 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
--> $DIR/extern-interrupt-not-enough.rs:17:8 --> ui/extern-interrupt-not-enough.rs:17:8
| |
17 | fn a(_: a::Context) {} 17 | fn a(_: a::Context) {}
| ^ | ^

View file

@ -12,4 +12,4 @@ error[E0080]: evaluation of constant value failed
3 | #[rtic::app(device = lm3s6965)] 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 | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ 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)