aboutsummaryrefslogtreecommitdiff
path: root/macros/src
diff options
context:
space:
mode:
Diffstat (limited to 'macros/src')
-rw-r--r--macros/src/analyze.rs29
-rw-r--r--macros/src/check.rs29
-rw-r--r--macros/src/codegen.rs4
-rw-r--r--macros/src/codegen/async_dispatchers.rs130
-rw-r--r--macros/src/codegen/dispatchers.rs140
-rw-r--r--macros/src/codegen/local_resources_struct.rs8
-rw-r--r--macros/src/codegen/module.rs202
-rw-r--r--macros/src/codegen/pre_init.rs10
-rw-r--r--macros/src/codegen/shared_resources.rs8
-rw-r--r--macros/src/codegen/shared_resources_struct.rs8
-rw-r--r--macros/src/codegen/software_tasks.rs86
-rw-r--r--macros/src/codegen/timer_queue.rs4
-rw-r--r--macros/src/codegen/util.rs26
-rw-r--r--macros/src/tests/single.rs6
14 files changed, 421 insertions, 269 deletions
diff --git a/macros/src/analyze.rs b/macros/src/analyze.rs
index d255b7f5..b33c9981 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<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 {
@@ -24,24 +25,42 @@ impl ops::Deref for Analysis {
// Assign an interrupt to each priority level
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)
let priorities = app
.software_tasks
.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)
.collect::<BTreeSet<_>>();
// map from priorities to interrupts (holding name and attributes)
- let interrupts: BTreeMap<Priority, _> = priorities
+
+ let interrupts_normal: BTreeMap<Priority, _> = priorities
+ .iter()
+ .copied()
+ .rev()
+ .map(|p| (p, available_interrupt.pop().expect("UNREACHABLE")))
+ .collect();
+
+ let interrupts_async: BTreeMap<Priority, _> = priorities_async
.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();
P::new(Analysis {
parent: analysis,
- interrupts,
+ interrupts_normal,
+ interrupts_async,
})
}
diff --git a/macros/src/check.rs b/macros/src/check.rs
index 74df688c..7bc0df35 100644
--- a/macros/src/check.rs
+++ b/macros/src/check.rs
@@ -36,28 +36,39 @@ pub fn app(app: &App, _analysis: &Analysis) -> parse::Result<Extra> {
.iter()
.map(|(name, task)| {
first = Some(name);
- task.args.priority
+ (task.args.priority, task.is_async)
})
.collect::<HashSet<_>>();
- 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 d7711b63..60ae3dac 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 00000000..b2ee5bd3
--- /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<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
+}
diff --git a/macros/src/codegen/dispatchers.rs b/macros/src/codegen/dispatchers.rs
index f5f36c49..7a9fa0a3 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<TokenStream2> {
+pub fn codegen(app: &App, analysis: &Analysis, _extra: &Extra) -> Vec<TokenStream2> {
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::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)) };
- ));
- }
- }
+ 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<TokenStream
let n = util::capacity_literal(channel.capacity as usize + 1);
let rq = util::rq_ident(level);
+ // let (_, _, _, input_ty) = util::regroup_inputs(inputs);
let (rq_ty, rq_expr) = {
(
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);
));
- 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<TokenStream
let fq = util::fq_ident(name);
let inputs = util::inputs_ident(name);
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 {
- 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 {
+ if !task.is_async {
quote!(
#(#cfgs)*
#t::#name => {
@@ -147,36 +111,11 @@ pub fn codegen(app: &App, analysis: &Analysis, extra: &Extra) -> Vec<TokenStream
)
}
)
- }
- })
- .collect::<Vec<_>>();
-
- let n_executors = channel
- .tasks
- .iter()
- .map(|name| {
- let task = &app.software_tasks[name];
- if task.is_async {
- 1
} else {
- 0
+ quote!()
}
})
- .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();
- ));
+ .collect::<Vec<_>>();
stmts.push(quote!(
while let Some((task, index)) = (&mut *#rq.get_mut()).split().1.dequeue() {
@@ -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 attribute = &interrupts[&level].1.attrs;
items.push(quote!(
diff --git a/macros/src/codegen/local_resources_struct.rs b/macros/src/codegen/local_resources_struct.rs
index b7eae3f9..ed5577c9 100644
--- a/macros/src/codegen/local_resources_struct.rs
+++ b/macros/src/codegen/local_resources_struct.rs
@@ -13,7 +13,13 @@ pub fn codegen(ctxt: Context, needs_lt: &mut bool, app: &App) -> (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 29f27662..913588a2 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;
-
- 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)
+ #(#cfgs)*
+ /// Spawns the task directly
+ #[allow(non_snake_case)]
+ #[doc(hidden)]
+ pub fn #internal_spawn_ident(#(#args,)*) -> Result<(), #ty> {
+ let input = #tupled;
+
+ unsafe {
+ let r = rtic::export::interrupt::free(|_| (&mut *#rq.get_mut()).enqueue(input));
+
+ if r.is_ok() {
+ rtic::pend(#device::#enum_::#interrupt);
+ }
+
+ 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();
-
- 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 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");
-
- if monotonic.args.default {
+ 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 (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 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!(
- #[doc(inline)]
- pub use #m::spawn_after;
- #[doc(inline)]
- pub use #m::spawn_at;
- #[doc(inline)]
- pub use #m::SpawnHandle;
+ 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)*
#[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 3d541a47..5708c0f1 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<TokenStream
// Populate the FreeQueue
for (name, task) in &app.software_tasks {
+ if task.is_async {
+ continue;
+ }
+
let cap = task.args.capacity;
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;));
}
- 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
for (&priority, name) in interrupt_ids.chain(app.hardware_tasks.values().filter_map(|task| {
diff --git a/macros/src/codegen/shared_resources.rs b/macros/src/codegen/shared_resources.rs
index 4a750070..f964f467 100644
--- a/macros/src/codegen/shared_resources.rs
+++ b/macros/src/codegen/shared_resources.rs
@@ -112,7 +112,11 @@ pub fn codegen(
};
// 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 device = &extra.device;
@@ -147,7 +151,7 @@ pub fn codegen(
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));
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 90cbc1be..9aade293 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 71869b6e..08f2102f 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);
-
- #[allow(clippy::redundant_closure)]
- let (fq_ty, fq_expr, mk_uninit): (_, _, Box<dyn Fn() -> 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);
- ));
-
- let elems = &(0..cap)
- .map(|_| quote!(core::mem::MaybeUninit::uninit()))
- .collect::<Vec<_>>();
+ if !task.is_async {
+ // Create free queues and inputs / instants buffers
+ let fq = util::fq_ident(name);
- for (_, monotonic) in &app.monotonics {
- let instants = util::monotonic_instants_ident(name, &monotonic.ident);
- let mono_type = &monotonic.ty;
+ #[allow(clippy::redundant_closure)]
+ let (fq_ty, fq_expr, mk_uninit): (_, _, Box<dyn Fn() -> Option<_>>) = {
+ (
+ quote!(rtic::export::SCFQ<#cap_lit_p1>),
+ quote!(rtic::export::Queue::new()),
+ Box::new(|| Some(util::link_section_uninit())),
+ )
+ };
- 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::<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
// /// 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 db6a9e3d..092095f5 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<TokenStrea
let variants = app
.software_tasks
.iter()
+ .filter(|(_, task)| !task.is_async)
.map(|(name, task)| {
let cfgs = &task.cfgs;
@@ -103,6 +104,7 @@ pub fn codegen(app: &App, analysis: &Analysis, _extra: &Extra) -> Vec<TokenStrea
let arms = app
.software_tasks
.iter()
+ .filter(|(_, task)| !task.is_async)
.map(|(name, task)| {
let cfgs = &task.cfgs;
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);
// 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 = {
quote!(
diff --git a/macros/src/codegen/util.rs b/macros/src/codegen/util.rs
index a0a59090..8d9d1ca9 100644
--- a/macros/src/codegen/util.rs
+++ b/macros/src/codegen/util.rs
@@ -184,7 +184,12 @@ pub fn regroup_inputs(
pub fn get_task_name(ctxt: Context, app: &App) -> 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 f20c9ccb..7f39a214 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");
}