aboutsummaryrefslogtreecommitdiff
path: root/macros/src/codegen
diff options
context:
space:
mode:
Diffstat (limited to 'macros/src/codegen')
-rw-r--r--macros/src/codegen/assertions.rs10
-rw-r--r--macros/src/codegen/async_dispatchers.rs129
-rw-r--r--macros/src/codegen/dispatchers.rs67
-rw-r--r--macros/src/codegen/hardware_tasks.rs16
-rw-r--r--macros/src/codegen/idle.rs15
-rw-r--r--macros/src/codegen/init.rs16
-rw-r--r--macros/src/codegen/local_resources.rs6
-rw-r--r--macros/src/codegen/local_resources_struct.rs19
-rw-r--r--macros/src/codegen/module.rs263
-rw-r--r--macros/src/codegen/monotonic.rs280
-rw-r--r--macros/src/codegen/post_init.rs17
-rw-r--r--macros/src/codegen/pre_init.rs22
-rw-r--r--macros/src/codegen/shared_resources.rs18
-rw-r--r--macros/src/codegen/shared_resources_struct.rs40
-rw-r--r--macros/src/codegen/software_tasks.rs132
-rw-r--r--macros/src/codegen/timer_queue.rs33
-rw-r--r--macros/src/codegen/util.rs43
17 files changed, 789 insertions, 337 deletions
diff --git a/macros/src/codegen/assertions.rs b/macros/src/codegen/assertions.rs
index 3e0ad61c..0f8326c7 100644
--- a/macros/src/codegen/assertions.rs
+++ b/macros/src/codegen/assertions.rs
@@ -1,11 +1,11 @@
use proc_macro2::TokenStream as TokenStream2;
use quote::quote;
-use crate::{analyze::Analysis, check::Extra, codegen::util};
-use rtic_syntax::ast::App;
+use crate::syntax::ast::App;
+use crate::{analyze::Analysis, codegen::util};
/// Generates compile-time assertions that check that types implement the `Send` / `Sync` traits
-pub fn codegen(app: &App, analysis: &Analysis, extra: &Extra) -> Vec<TokenStream2> {
+pub fn codegen(app: &App, analysis: &Analysis) -> Vec<TokenStream2> {
let mut stmts = vec![];
for ty in &analysis.send_types {
@@ -21,7 +21,7 @@ pub fn codegen(app: &App, analysis: &Analysis, extra: &Extra) -> Vec<TokenStream
stmts.push(quote!(rtic::export::assert_monotonic::<#ty>();));
}
- let device = &extra.device;
+ let device = &app.args.device;
let chunks_name = util::priority_mask_chunks_ident();
let no_basepri_checks: Vec<_> = app
.hardware_tasks
@@ -29,9 +29,7 @@ pub fn codegen(app: &App, analysis: &Analysis, extra: &Extra) -> Vec<TokenStream
.filter_map(|(_, task)| {
if !util::is_exception(&task.args.binds) {
let interrupt_name = &task.args.binds;
- let cfgs = &task.cfgs;
Some(quote!(
- #(#cfgs)*
if (#device::Interrupt::#interrupt_name as usize) >= (#chunks_name * 32) {
::core::panic!("An interrupt out of range is used while in armv6 or armv8m.base");
}
diff --git a/macros/src/codegen/async_dispatchers.rs b/macros/src/codegen/async_dispatchers.rs
new file mode 100644
index 00000000..8b0e928b
--- /dev/null
+++ b/macros/src/codegen/async_dispatchers.rs
@@ -0,0 +1,129 @@
+use crate::syntax::ast::App;
+use crate::{analyze::Analysis, codegen::util};
+use proc_macro2::TokenStream as TokenStream2;
+use quote::quote;
+
+/// Generates task dispatchers
+pub fn codegen(app: &App, analysis: &Analysis) -> 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 = &app.args.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 !(&*#exec_name.get()).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 a90a97c7..1a8b4042 100644
--- a/macros/src/codegen/dispatchers.rs
+++ b/macros/src/codegen/dispatchers.rs
@@ -1,21 +1,31 @@
+use crate::syntax::ast::App;
+use crate::{analyze::Analysis, codegen::util};
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> {
+pub fn codegen(app: &App, analysis: &Analysis) -> Vec<TokenStream2> {
let mut items = vec![];
- let interrupts = &analysis.interrupts;
+ 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;
@@ -45,6 +55,7 @@ pub fn codegen(app: &App, analysis: &Analysis, _extra: &Extra) -> Vec<TokenStrea
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>),
@@ -64,6 +75,13 @@ pub fn codegen(app: &App, analysis: &Analysis, _extra: &Extra) -> Vec<TokenStrea
static #rq: rtic::RacyCell<#rq_ty> = rtic::RacyCell::new(#rq_expr);
));
+ let interrupt = util::suffixed(
+ &interrupts
+ .get(&level)
+ .expect("RTIC-ICE: Unable to get interrrupt")
+ .0
+ .to_string(),
+ );
let arms = channel
.tasks
.iter()
@@ -74,23 +92,27 @@ pub fn codegen(app: &App, analysis: &Analysis, _extra: &Extra) -> Vec<TokenStrea
let inputs = util::inputs_ident(name);
let (_, tupled, pats, _) = util::regroup_inputs(&task.inputs);
- quote!(
- #(#cfgs)*
- #t::#name => {
- let #tupled =
- (&*#inputs
- .get())
- .get_unchecked(usize::from(index))
- .as_ptr()
- .read();
- (&mut *#fq.get_mut()).split().0.enqueue_unchecked(index);
- let priority = &rtic::export::Priority::new(PRIORITY);
- #name(
- #name::Context::new(priority)
- #(,#pats)*
- )
- }
- )
+ if !task.is_async {
+ quote!(
+ #(#cfgs)*
+ #t::#name => {
+ let #tupled =
+ (&*#inputs
+ .get())
+ .get_unchecked(usize::from(index))
+ .as_ptr()
+ .read();
+ (&mut *#fq.get_mut()).split().0.enqueue_unchecked(index);
+ let priority = &rtic::export::Priority::new(PRIORITY);
+ #name(
+ #name::Context::new(priority)
+ #(,#pats)*
+ )
+ }
+ )
+ } else {
+ quote!()
+ }
})
.collect::<Vec<_>>();
@@ -103,7 +125,6 @@ pub fn codegen(app: &App, analysis: &Analysis, _extra: &Extra) -> Vec<TokenStrea
));
let doc = format!("Interrupt handler to dispatch tasks at priority {}", level);
- let interrupt = util::suffixed(&interrupts[&level].0.to_string());
let attribute = &interrupts[&level].1.attrs;
items.push(quote!(
#[allow(non_snake_case)]
diff --git a/macros/src/codegen/hardware_tasks.rs b/macros/src/codegen/hardware_tasks.rs
index b3f05d2a..2a81d9a0 100644
--- a/macros/src/codegen/hardware_tasks.rs
+++ b/macros/src/codegen/hardware_tasks.rs
@@ -1,18 +1,15 @@
-use proc_macro2::TokenStream as TokenStream2;
-use quote::quote;
-use rtic_syntax::{ast::App, Context};
-
+use crate::syntax::{ast::App, Context};
use crate::{
analyze::Analysis,
- check::Extra,
codegen::{local_resources_struct, module, shared_resources_struct},
};
+use proc_macro2::TokenStream as TokenStream2;
+use quote::quote;
/// Generate support code for hardware tasks (`#[exception]`s and `#[interrupt]`s)
pub fn codegen(
app: &App,
analysis: &Analysis,
- extra: &Extra,
) -> (
// mod_app_hardware_tasks -- interrupt handlers and `${task}Resources` constructors
Vec<TokenStream2>,
@@ -33,12 +30,10 @@ pub fn codegen(
let priority = task.args.priority;
let cfgs = &task.cfgs;
let attrs = &task.attrs;
- let user_hardware_task_isr_doc = &format!(" User HW task ISR trampoline for {name}");
mod_app.push(quote!(
#[allow(non_snake_case)]
#[no_mangle]
- #[doc = #user_hardware_task_isr_doc]
#(#attrs)*
#(#cfgs)*
unsafe fn #symbol() {
@@ -87,19 +82,14 @@ pub fn codegen(
local_needs_lt,
app,
analysis,
- extra,
));
- let user_hardware_task_doc = &format!(" User HW task: {name}");
if !task.is_extern {
let attrs = &task.attrs;
- let cfgs = &task.cfgs;
let context = &task.context;
let stmts = &task.stmts;
user_tasks.push(quote!(
- #[doc = #user_hardware_task_doc]
#(#attrs)*
- #(#cfgs)*
#[allow(non_snake_case)]
fn #name(#context: #name::Context) {
use rtic::Mutex as _;
diff --git a/macros/src/codegen/idle.rs b/macros/src/codegen/idle.rs
index 77a7f9fe..98679399 100644
--- a/macros/src/codegen/idle.rs
+++ b/macros/src/codegen/idle.rs
@@ -1,18 +1,15 @@
-use proc_macro2::TokenStream as TokenStream2;
-use quote::quote;
-use rtic_syntax::{ast::App, Context};
-
+use crate::syntax::{ast::App, Context};
use crate::{
analyze::Analysis,
- check::Extra,
codegen::{local_resources_struct, module, shared_resources_struct},
};
+use proc_macro2::TokenStream as TokenStream2;
+use quote::quote;
/// Generates support code for `#[idle]` functions
pub fn codegen(
app: &App,
analysis: &Analysis,
- extra: &Extra,
) -> (
// mod_app_idle -- the `${idle}Resources` constructor
Vec<TokenStream2>,
@@ -57,16 +54,13 @@ pub fn codegen(
local_needs_lt,
app,
analysis,
- extra,
));
- let idle_doc = " User provided idle function".to_string();
let attrs = &idle.attrs;
let context = &idle.context;
let stmts = &idle.stmts;
let user_idle = Some(quote!(
#(#attrs)*
- #[doc = #idle_doc]
#[allow(non_snake_case)]
fn #name(#context: #name::Context) -> ! {
use rtic::Mutex as _;
@@ -82,6 +76,9 @@ pub fn codegen(
(mod_app, root_idle, user_idle, call_idle)
} else {
+ // TODO: No idle defined, check for 0-priority tasks and generate an executor if needed
+
+ //
(
vec![],
vec![],
diff --git a/macros/src/codegen/init.rs b/macros/src/codegen/init.rs
index 34f86f27..9a6fe2d5 100644
--- a/macros/src/codegen/init.rs
+++ b/macros/src/codegen/init.rs
@@ -1,11 +1,10 @@
use proc_macro2::TokenStream as TokenStream2;
use quote::quote;
-use rtic_syntax::{ast::App, Context};
use crate::{
analyze::Analysis,
- check::Extra,
codegen::{local_resources_struct, module},
+ syntax::{ast::App, Context},
};
type CodegenResult = (
@@ -24,7 +23,7 @@ type CodegenResult = (
);
/// Generates support code for `#[init]` functions
-pub fn codegen(app: &App, analysis: &Analysis, extra: &Extra) -> CodegenResult {
+pub fn codegen(app: &App, analysis: &Analysis) -> CodegenResult {
let init = &app.init;
let mut local_needs_lt = false;
let name = &init.name;
@@ -65,27 +64,22 @@ pub fn codegen(app: &App, analysis: &Analysis, extra: &Extra) -> CodegenResult {
)
})
.collect();
-
- let shared_resources_doc = " RTIC shared resource struct".to_string();
- let local_resources_doc = " RTIC local resource struct".to_string();
root_init.push(quote! {
- #[doc = #shared_resources_doc]
struct #shared {
#(#shared_resources)*
}
- #[doc = #local_resources_doc]
struct #local {
#(#local_resources)*
}
});
+ // let locals_pat = locals_pat.iter();
+
let user_init_return = quote! {#shared, #local, #name::Monotonics};
- let user_init_doc = " User provided init function".to_string();
let user_init = quote!(
#(#attrs)*
- #[doc = #user_init_doc]
#[inline(always)]
#[allow(non_snake_case)]
fn #name(#context: #name::Context) -> (#user_init_return) {
@@ -105,6 +99,7 @@ pub fn codegen(app: &App, analysis: &Analysis, extra: &Extra) -> CodegenResult {
mod_app = Some(constructor);
}
+ // let locals_new = locals_new.iter();
let call_init = quote! {
let (shared_resources, local_resources, mut monotonics) = #name(#name::Context::new(core.into()));
};
@@ -115,7 +110,6 @@ pub fn codegen(app: &App, analysis: &Analysis, extra: &Extra) -> CodegenResult {
local_needs_lt,
app,
analysis,
- extra,
));
(mod_app, root_init, user_init, call_init)
diff --git a/macros/src/codegen/local_resources.rs b/macros/src/codegen/local_resources.rs
index 6e7c1daa..6fc63cd9 100644
--- a/macros/src/codegen/local_resources.rs
+++ b/macros/src/codegen/local_resources.rs
@@ -1,8 +1,7 @@
+use crate::syntax::ast::App;
+use crate::{analyze::Analysis, codegen::util};
use proc_macro2::TokenStream as TokenStream2;
use quote::quote;
-use rtic_syntax::ast::App;
-
-use crate::{analyze::Analysis, check::Extra, codegen::util};
/// Generates `local` variables and local resource proxies
///
@@ -10,7 +9,6 @@ use crate::{analyze::Analysis, check::Extra, codegen::util};
pub fn codegen(
app: &App,
_analysis: &Analysis,
- _extra: &Extra,
) -> (
// mod_app -- the `static` variables behind the proxies
Vec<TokenStream2>,
diff --git a/macros/src/codegen/local_resources_struct.rs b/macros/src/codegen/local_resources_struct.rs
index 74bdbf8b..309fd8d2 100644
--- a/macros/src/codegen/local_resources_struct.rs
+++ b/macros/src/codegen/local_resources_struct.rs
@@ -1,9 +1,9 @@
-use proc_macro2::TokenStream as TokenStream2;
-use quote::quote;
-use rtic_syntax::{
+use crate::syntax::{
ast::{App, TaskLocal},
Context,
};
+use proc_macro2::TokenStream as TokenStream2;
+use quote::quote;
use crate::codegen::util;
@@ -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,
};
@@ -49,9 +55,7 @@ pub fn codegen(ctxt: Context, needs_lt: &mut bool, app: &App) -> (TokenStream2,
util::declared_static_local_resource_ident(name, &task_name)
};
- let local_resource_doc = format!(" Local resource `{name}`");
fields.push(quote!(
- #[doc = #local_resource_doc]
#(#cfgs)*
pub #name: &#lt mut #ty
));
@@ -84,7 +88,7 @@ pub fn codegen(ctxt: Context, needs_lt: &mut bool, app: &App) -> (TokenStream2,
}
}
- let doc = format!(" Local resources `{}` has access to", ctxt.ident(app));
+ let doc = format!("Local resources `{}` has access to", ctxt.ident(app));
let ident = util::local_resources_ident(ctxt, app);
let item = quote!(
#[allow(non_snake_case)]
@@ -98,7 +102,6 @@ pub fn codegen(ctxt: Context, needs_lt: &mut bool, app: &App) -> (TokenStream2,
let constructor = quote!(
impl<#lt> #ident<#lt> {
#[inline(always)]
- #[doc(hidden)]
pub unsafe fn new() -> Self {
#ident {
#(#values,)*
diff --git a/macros/src/codegen/module.rs b/macros/src/codegen/module.rs
index 8dcdbcf3..7ac06c5c 100644
--- a/macros/src/codegen/module.rs
+++ b/macros/src/codegen/module.rs
@@ -1,7 +1,7 @@
-use crate::{analyze::Analysis, check::Extra, codegen::util};
+use crate::syntax::{ast::App, Context};
+use crate::{analyze::Analysis, codegen::util};
use proc_macro2::TokenStream as TokenStream2;
use quote::quote;
-use rtic_syntax::{ast::App, Context};
#[allow(clippy::too_many_lines)]
pub fn codegen(
@@ -10,12 +10,13 @@ pub fn codegen(
local_resources_tick: bool,
app: &App,
analysis: &Analysis,
- extra: &Extra,
) -> TokenStream2 {
let mut items = vec![];
let mut module_items = vec![];
let mut fields = vec![];
let mut values = vec![];
+ // Used to copy task cfgs to the whole module
+ let mut task_cfgs = vec![];
let name = ctxt.ident(app);
@@ -27,8 +28,8 @@ pub fn codegen(
pub core: rtic::export::Peripherals
));
- if extra.peripherals {
- let device = &extra.device;
+ if app.args.peripherals {
+ let device = &app.args.device;
fields.push(quote!(
/// Device peripherals
@@ -52,14 +53,6 @@ pub fn codegen(
Context::Idle | Context::HardwareTask(_) | Context::SoftwareTask(_) => {}
}
- // if ctxt.has_locals(app) {
- // let ident = util::locals_ident(ctxt, app);
- // module_items.push(quote!(
- // #[doc(inline)]
- // pub use super::#ident as Locals;
- // ));
- // }
-
if ctxt.has_local_resources(app) {
let ident = util::local_resources_ident(ctxt, app);
let lt = if local_resources_tick {
@@ -114,12 +107,8 @@ pub fn codegen(
.monotonics
.iter()
.map(|(_, monotonic)| {
- let cfgs = &monotonic.cfgs;
let mono = &monotonic.ty;
- quote! {
- #(#cfgs)*
- pub #mono
- }
+ quote! {#mono}
})
.collect();
@@ -130,7 +119,7 @@ pub fn codegen(
#[allow(non_snake_case)]
#[allow(non_camel_case_types)]
pub struct #internal_monotonics_ident(
- #(#monotonic_types),*
+ #(pub #monotonic_types),*
);
));
@@ -141,10 +130,10 @@ pub fn codegen(
}
let doc = match ctxt {
- Context::Idle => " Idle loop",
- Context::Init => " Initialization function",
- Context::HardwareTask(_) => " Hardware task",
- Context::SoftwareTask(_) => " Software task",
+ Context::Idle => "Idle loop",
+ Context::Init => "Initialization function",
+ Context::HardwareTask(_) => "Hardware task",
+ Context::SoftwareTask(_) => "Software task",
};
let v = Vec::new();
@@ -175,8 +164,8 @@ pub fn codegen(
let internal_context_name = util::internal_task_ident(name, "Context");
items.push(quote!(
- /// Execution context
#(#cfgs)*
+ /// Execution context
#[allow(non_snake_case)]
#[allow(non_camel_case_types)]
pub struct #internal_context_name<#lt> {
@@ -185,7 +174,6 @@ pub fn codegen(
#(#cfgs)*
impl<#lt> #internal_context_name<#lt> {
- #[doc(hidden)]
#[inline(always)]
pub unsafe fn new(#core #priority) -> Self {
#internal_context_name {
@@ -196,8 +184,8 @@ pub fn codegen(
));
module_items.push(quote!(
- #[doc(inline)]
#(#cfgs)*
+ #[doc(inline)]
pub use super::#internal_context_name as Context;
));
@@ -206,6 +194,8 @@ pub fn codegen(
let priority = spawnee.args.priority;
let t = util::spawn_t_ident(priority);
let cfgs = &spawnee.cfgs;
+ // Store a copy of the task cfgs
+ task_cfgs = cfgs.clone();
let (args, tupled, untupled, ty) = util::regroup_inputs(&spawnee.inputs);
let args = &args;
let tupled = &tupled;
@@ -213,112 +203,141 @@ pub fn codegen(
let rq = util::rq_ident(priority);
let inputs = util::inputs_ident(name);
- let device = &extra.device;
+ let device = &app.args.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!(
- /// Spawns the task directly
- #(#cfgs)*
- 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));
- });
+ if r.is_ok() {
+ rtic::pend(#device::#enum_::#interrupt);
+ }
- rtic::pend(#device::#enum_::#interrupt);
+ r
+ }
+ }));
+ } else {
+ items.push(quote!(
- 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 {
+ 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!(
- #[doc(inline)]
#(#cfgs)*
+ #[doc(inline)]
pub use super::#internal_spawn_ident as spawn;
));
// 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 cfgs = &monotonic.cfgs;
- 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!(
- #(#cfgs)*
- pub use #m::spawn_after;
- #(#cfgs)*
- pub use #m::spawn_at;
- #(#cfgs)*
- 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!(
- #[doc(hidden)]
- #(#cfgs)*
- pub mod #m {
- pub use super::super::#internal_spawn_after_ident as spawn_after;
- pub use super::super::#internal_spawn_at_ident as spawn_at;
- pub use super::super::#internal_spawn_handle_ident as SpawnHandle;
- }
- ));
- items.push(quote!(
- #[doc(hidden)]
+ items.push(quote!(
#(#cfgs)*
#[allow(non_snake_case)]
#[allow(non_camel_case_types)]
@@ -329,7 +348,6 @@ pub fn codegen(
#(#cfgs)*
impl core::fmt::Debug for #internal_spawn_handle_ident {
- #[doc(hidden)]
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
f.debug_struct(#spawn_handle_string).finish()
}
@@ -340,7 +358,7 @@ pub fn codegen(
pub fn cancel(self) -> Result<#ty, ()> {
rtic::export::interrupt::free(|_| unsafe {
let tq = &mut *#tq.get_mut();
- if let Some((_task, index)) = tq.cancel_marker(self.marker) {
+ if let Some((_task, index)) = tq.cancel_task_marker(self.marker) {
// Get the message
let msg = (&*#inputs
.get())
@@ -357,9 +375,7 @@ pub fn codegen(
})
}
- /// Reschedule after
#[inline]
- #(#cfgs)*
pub fn reschedule_after(
self,
duration: <#m as rtic::Monotonic>::Duration
@@ -367,8 +383,6 @@ pub fn codegen(
self.reschedule_at(monotonics::#m::now() + duration)
}
- /// Reschedule at
- #(#cfgs)*
pub fn reschedule_at(
self,
instant: <#m as rtic::Monotonic>::Instant
@@ -379,16 +393,17 @@ pub fn codegen(
let tq = (&mut *#tq.get_mut());
- tq.update_marker(self.marker, marker, instant, || #pend).map(|_| #name::#m::SpawnHandle { marker })
+ tq.update_task_marker(self.marker, marker, instant, || #pend).map(|_| #name::#m::SpawnHandle { marker })
})
}
}
+
+ #(#cfgs)*
/// Spawns the task after a set duration relative to the current time
///
/// This will use the time `Instant::new(0)` as baseline if called in `#[init]`,
/// so if you use a non-resetable timer use `spawn_at` when in `#[init]`
- #(#cfgs)*
#[allow(non_snake_case)]
pub fn #internal_spawn_after_ident(
duration: <#m as rtic::Monotonic>::Duration
@@ -424,10 +439,10 @@ pub fn codegen(
rtic::export::interrupt::free(|_| {
let marker = #tq_marker.get().read();
- let nr = rtic::export::NotReady {
- instant,
- index,
+ let nr = rtic::export::TaskNotReady {
task: #t::#name,
+ index,
+ instant,
marker,
};
@@ -435,7 +450,7 @@ pub fn codegen(
let tq = &mut *#tq.get_mut();
- tq.enqueue_unchecked(
+ tq.enqueue_task_unchecked(
nr,
|| #enable_interrupt,
|| #pend,
@@ -449,6 +464,7 @@ pub fn codegen(
}
}
));
+ }
}
}
@@ -457,8 +473,9 @@ pub fn codegen(
} else {
quote!(
#(#items)*
+
#[allow(non_snake_case)]
- #(#cfgs)*
+ #(#task_cfgs)*
#[doc = #doc]
pub mod #name {
#(#module_items)*
diff --git a/macros/src/codegen/monotonic.rs b/macros/src/codegen/monotonic.rs
new file mode 100644
index 00000000..417a1d6a
--- /dev/null
+++ b/macros/src/codegen/monotonic.rs
@@ -0,0 +1,280 @@
+use crate::syntax::ast::App;
+use proc_macro2::TokenStream as TokenStream2;
+use quote::quote;
+
+use crate::{analyze::Analysis, codegen::util};
+
+/// Generates monotonic module dispatchers
+pub fn codegen(app: &App, _analysis: &Analysis) -> TokenStream2 {
+ let mut monotonic_parts: Vec<_> = Vec::new();
+
+ let tq_marker = util::timer_queue_marker_ident();
+
+ 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_name);
+ let m = &monotonic.ident;
+ let m_ident = util::monotonic_ident(&monotonic_name);
+ let m_isr = &monotonic.args.binds;
+ let enum_ = util::interrupt_ident();
+ let name_str = &m.to_string();
+ let ident = util::monotonic_ident(name_str);
+ let doc = &format!(
+ "This module holds the static implementation for `{}::now()`",
+ name_str
+ );
+
+ 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(super::super::#rt_err::#enum_::#m_isr)),
+ quote!(rtic::pend(super::super::#rt_err::#enum_::#m_isr)),
+ )
+ };
+
+ let default_monotonic = if monotonic.args.default {
+ quote!(
+ #[doc(inline)]
+ pub use #m::now;
+ #[doc(inline)]
+ pub use #m::delay;
+ #[doc(inline)]
+ pub use #m::delay_until;
+ #[doc(inline)]
+ pub use #m::timeout_at;
+ #[doc(inline)]
+ pub use #m::timeout_after;
+ )
+ } else {
+ quote!()
+ };
+
+ monotonic_parts.push(quote! {
+ #default_monotonic
+
+ #[doc = #doc]
+ #[allow(non_snake_case)]
+ pub mod #m {
+ /// Read the current time from this monotonic
+ pub fn now() -> <super::super::#m as rtic::Monotonic>::Instant {
+ rtic::export::interrupt::free(|_| {
+ use rtic::Monotonic as _;
+ if let Some(m) = unsafe{ &mut *super::super::#ident.get_mut() } {
+ m.now()
+ } else {
+ <super::super::#m as rtic::Monotonic>::zero()
+ }
+ })
+ }
+
+ /// Delay
+ #[inline(always)]
+ #[allow(non_snake_case)]
+ pub fn delay(duration: <super::super::#m as rtic::Monotonic>::Duration)
+ -> DelayFuture {
+ let until = now() + duration;
+ DelayFuture { until, waker_storage: None }
+ }
+
+ /// Delay until a specific time
+ #[inline(always)]
+ #[allow(non_snake_case)]
+ pub fn delay_until(instant: <super::super::#m as rtic::Monotonic>::Instant)
+ -> DelayFuture {
+ let until = instant;
+ DelayFuture { until, waker_storage: None }
+ }
+
+ /// Delay future.
+ #[allow(non_snake_case)]
+ #[allow(non_camel_case_types)]
+ pub struct DelayFuture {
+ until: <super::super::#m as rtic::Monotonic>::Instant,
+ waker_storage: Option<rtic::export::IntrusiveNode<rtic::export::WakerNotReady<super::super::#m>>>,
+ }
+
+ impl Drop for DelayFuture {
+ fn drop(&mut self) {
+ if let Some(waker_storage) = &mut self.waker_storage {
+ rtic::export::interrupt::free(|_| unsafe {
+ let tq = &mut *super::super::#tq.get_mut();
+ tq.cancel_waker_marker(waker_storage.val.marker);
+ });
+ }
+ }
+ }
+
+ impl core::future::Future for DelayFuture {
+ type Output = ();
+
+ fn poll(
+ mut self: core::pin::Pin<&mut Self>,
+ cx: &mut core::task::Context<'_>
+ ) -> core::task::Poll<Self::Output> {
+ let mut s = self.as_mut();
+ let now = now();
+ let until = s.until;
+ let is_ws_none = s.waker_storage.is_none();
+
+ if now >= until {
+ return core::task::Poll::Ready(());
+ } else if is_ws_none {
+ rtic::export::interrupt::free(|_| unsafe {
+ let marker = super::super::#tq_marker.get().read();
+ super::super::#tq_marker.get_mut().write(marker.wrapping_add(1));
+
+ let nr = s.waker_storage.insert(rtic::export::IntrusiveNode::new(rtic::export::WakerNotReady {
+ waker: cx.waker().clone(),
+ instant: until,
+ marker,
+ }));
+
+ let tq = &mut *super::super::#tq.get_mut();
+
+ tq.enqueue_waker(
+ core::mem::transmute(nr), // Transmute the reference to static
+ || #enable_interrupt,
+ || #pend,
+ (&mut *super::super::#m_ident.get_mut()).as_mut());
+ });
+ }
+
+ core::task::Poll::Pending
+ }
+ }
+
+ /// Timeout future.
+ #[allow(non_snake_case)]
+ #[allow(non_camel_case_types)]
+ pub struct TimeoutFuture<F: core::future::Future> {
+ future: F,
+ until: <super::super::#m as rtic::Monotonic>::Instant,
+ waker_storage: Option<rtic::export::IntrusiveNode<rtic::export::WakerNotReady<super::super::#m>>>,
+ }
+
+ impl<F: core::future::Future> Drop for TimeoutFuture<F> {
+ fn drop(&mut self) {
+ if let Some(waker_storage) = &mut self.waker_storage {
+ rtic::export::interrupt::free(|_| unsafe {
+ let tq = &mut *super::super::#tq.get_mut();
+ tq.cancel_waker_marker(waker_storage.val.marker);
+ });
+ }
+ }
+ }
+
+ /// Timeout after
+ #[allow(non_snake_case)]
+ #[inline(always)]
+ pub fn timeout_after<F: core::future::Future>(
+ future: F,
+ duration: <super::super::#m as rtic::Monotonic>::Duration
+ ) -> TimeoutFuture<F> {
+ let until = now() + duration;
+ TimeoutFuture {
+ future,
+ until,
+ waker_storage: None,
+ }
+ }
+
+ /// Timeout at
+ #[allow(non_snake_case)]
+ #[inline(always)]
+ pub fn timeout_at<F: core::future::Future>(
+ future: F,
+ instant: <super::super::#m as rtic::Monotonic>::Instant
+ ) -> TimeoutFuture<F> {
+ TimeoutFuture {
+ future,
+ until: instant,
+ waker_storage: None,
+ }
+ }
+
+ impl<F> core::future::Future for TimeoutFuture<F>
+ where
+ F: core::future::Future,
+ {
+ type Output = Result<F::Output, super::TimeoutError>;
+
+ fn poll(
+ self: core::pin::Pin<&mut Self>,
+ cx: &mut core::task::Context<'_>
+ ) -> core::task::Poll<Self::Output> {
+ // SAFETY: We don't move the underlying pinned value.
+ let mut s = unsafe { self.get_unchecked_mut() };
+ let future = unsafe { core::pin::Pin::new_unchecked(&mut s.future) };
+ let now = now();
+ let until = s.until;
+ let is_ws_none = s.waker_storage.is_none();
+
+ match future.poll(cx) {
+ core::task::Poll::Ready(r) => {
+ if let Some(waker_storage) = &mut s.waker_storage {
+ rtic::export::interrupt::free(|_| unsafe {
+ let tq = &mut *super::super::#tq.get_mut();
+ tq.cancel_waker_marker(waker_storage.val.marker);
+ });
+ }
+
+ return core::task::Poll::Ready(Ok(r));
+ }
+ core::task::Poll::Pending => {
+ if now >= until {
+ // Timeout
+ return core::task::Poll::Ready(Err(super::TimeoutError));
+ } else if is_ws_none {
+ rtic::export::interrupt::free(|_| unsafe {
+ let marker = super::super::#tq_marker.get().read();
+ super::super::#tq_marker.get_mut().write(marker.wrapping_add(1));
+
+ let nr = s.waker_storage.insert(rtic::export::IntrusiveNode::new(rtic::export::WakerNotReady {
+ waker: cx.waker().clone(),
+ instant: until,
+ marker,
+ }));
+
+ let tq = &mut *super::super::#tq.get_mut();
+
+ tq.enqueue_waker(
+ core::mem::transmute(nr), // Transmute the reference to static
+ || #enable_interrupt,
+ || #pend,
+ (&mut *super::super::#m_ident.get_mut()).as_mut());
+ });
+ }
+ }
+ }
+
+ core::task::Poll::Pending
+ }
+ }
+ }
+ });
+ }
+
+ if monotonic_parts.is_empty() {
+ quote!()
+ } else {
+ quote!(
+ pub use rtic::Monotonic as _;
+
+ /// Holds static methods for each monotonic.
+ pub mod monotonics {
+ /// A timeout error.
+ #[derive(Debug)]
+ pub struct TimeoutError;
+
+ #(#monotonic_parts)*
+ }
+ )
+ }
+}
diff --git a/macros/src/codegen/post_init.rs b/macros/src/codegen/post_init.rs
index 460b4e21..df5daa1e 100644
--- a/macros/src/codegen/post_init.rs
+++ b/macros/src/codegen/post_init.rs
@@ -1,6 +1,6 @@
+use crate::syntax::ast::App;
use proc_macro2::{Span, TokenStream as TokenStream2};
use quote::quote;
-use rtic_syntax::ast::App;
use syn::Index;
use crate::{analyze::Analysis, codegen::util};
@@ -43,28 +43,21 @@ pub fn codegen(app: &App, analysis: &Analysis) -> Vec<TokenStream2> {
}
}
- for (i, (monotonic_ident, monotonic)) in app.monotonics.iter().enumerate() {
+ for (i, (monotonic, _)) in app.monotonics.iter().enumerate() {
// For future use
// let doc = format!(" RTIC internal: {}:{}", file!(), line!());
// stmts.push(quote!(#[doc = #doc]));
- let cfgs = &monotonic.cfgs;
#[allow(clippy::cast_possible_truncation)]
let idx = Index {
index: i as u32,
span: Span::call_site(),
};
- stmts.push(quote!(
- #(#cfgs)*
- monotonics.#idx.reset();
- ));
+ stmts.push(quote!(monotonics.#idx.reset();));
// Store the monotonic
- let name = util::monotonic_ident(&monotonic_ident.to_string());
- stmts.push(quote!(
- #(#cfgs)*
- #name.get_mut().write(Some(monotonics.#idx));
- ));
+ let name = util::monotonic_ident(&monotonic.to_string());
+ stmts.push(quote!(#name.get_mut().write(Some(monotonics.#idx));));
}
// Enable the interrupts -- this completes the `init`-ialization phase
diff --git a/macros/src/codegen/pre_init.rs b/macros/src/codegen/pre_init.rs
index 2362cb74..ef3acba7 100644
--- a/macros/src/codegen/pre_init.rs
+++ b/macros/src/codegen/pre_init.rs
@@ -1,11 +1,11 @@
+use crate::syntax::ast::App;
use proc_macro2::TokenStream as TokenStream2;
use quote::quote;
-use rtic_syntax::ast::App;
-use crate::{analyze::Analysis, check::Extra, codegen::util};
+use crate::{analyze::Analysis, codegen::util};
/// Generates code that runs before `#[init]`
-pub fn codegen(app: &App, analysis: &Analysis, extra: &Extra) -> Vec<TokenStream2> {
+pub fn codegen(app: &App, analysis: &Analysis) -> Vec<TokenStream2> {
let mut stmts = vec![];
let rt_err = util::rt_err_ident();
@@ -15,12 +15,14 @@ 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 cfgs = &task.cfgs;
let fq_ident = util::fq_ident(name);
stmts.push(quote!(
- #(#cfgs)*
(0..#cap).for_each(|i| (&mut *#fq_ident.get_mut()).enqueue_unchecked(i));
));
}
@@ -30,17 +32,21 @@ pub fn codegen(app: &App, analysis: &Analysis, extra: &Extra) -> Vec<TokenStream
let mut core: rtic::export::Peripherals = rtic::export::Peripherals::steal().into();
));
- let device = &extra.device;
+ let device = &app.args.device;
let nvic_prio_bits = quote!(#device::NVIC_PRIO_BITS);
// check that all dispatchers exists in the `Interrupt` enumeration regardless of whether
// they are used or not
let interrupt = util::interrupt_ident();
- for name in app.args.extern_interrupts.keys() {
+ for name in app.args.dispatchers.keys() {
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 b5dff09d..66f38002 100644
--- a/macros/src/codegen/shared_resources.rs
+++ b/macros/src/codegen/shared_resources.rs
@@ -1,14 +1,13 @@
-use crate::{analyze::Analysis, check::Extra, codegen::util};
+use crate::syntax::{analyze::Ownership, ast::App};
+use crate::{analyze::Analysis, codegen::util};
use proc_macro2::TokenStream as TokenStream2;
use quote::quote;
-use rtic_syntax::{analyze::Ownership, ast::App};
use std::collections::HashMap;
/// Generates `static` variables and shared resource proxies
pub fn codegen(
app: &App,
analysis: &Analysis,
- extra: &Extra,
) -> (
// mod_app -- the `static` variables behind the proxies
Vec<TokenStream2>,
@@ -90,7 +89,7 @@ pub fn codegen(
// let doc = format!(" RTIC internal ({} resource): {}:{}", doc, file!(), line!());
mod_app.push(util::impl_mutex(
- extra,
+ app,
cfgs,
true,
&shared_name,
@@ -112,10 +111,14 @@ 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;
+ let device = &app.args.device;
let mut uses_exceptions_with_resources = false;
let mut mask_ids = Vec::new();
@@ -147,8 +150,7 @@ pub fn codegen(
None
}
})) {
- #[allow(clippy::or_fun_call)]
- 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 df362719..1d46aa4e 100644
--- a/macros/src/codegen/shared_resources_struct.rs
+++ b/macros/src/codegen/shared_resources_struct.rs
@@ -1,6 +1,6 @@
+use crate::syntax::{ast::App, Context};
use proc_macro2::TokenStream as TokenStream2;
use quote::quote;
-use rtic_syntax::{ast::App, Context};
use crate::codegen::util;
@@ -10,24 +10,17 @@ 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,
};
- let v = Vec::new();
- let task_cfgs = match ctxt {
- Context::HardwareTask(t) => {
- &app.hardware_tasks[t].cfgs
- // ...
- }
- Context::SoftwareTask(t) => {
- &app.software_tasks[t].cfgs
- // ...
- }
- _ => &v,
- };
-
let mut fields = vec![];
let mut values = vec![];
let mut has_cfgs = false;
@@ -57,18 +50,14 @@ pub fn codegen(ctxt: Context, needs_lt: &mut bool, app: &App) -> (TokenStream2,
quote!('a)
};
- let lock_free_resource_doc = format!(" Lock free resource `{name}`");
fields.push(quote!(
- #[doc = #lock_free_resource_doc]
#(#cfgs)*
pub #name: &#lt #mut_ #ty
));
} else if access.is_shared() {
lt = Some(quote!('a));
- let shared_resource_doc = format!(" Shared resource `{name}`");
fields.push(quote!(
- #[doc = #shared_resource_doc]
#(#cfgs)*
pub #name: &'a #ty
));
@@ -76,16 +65,12 @@ pub fn codegen(ctxt: Context, needs_lt: &mut bool, app: &App) -> (TokenStream2,
// Resource proxy
lt = Some(quote!('a));
- let resource_doc =
- format!(" Resource proxy resource `{name}`. Use method `.lock()` to gain access");
fields.push(quote!(
- #[doc = #resource_doc]
#(#cfgs)*
pub #name: shared_resources::#shared_name<'a>
));
values.push(quote!(
- #[doc(hidden)]
#(#cfgs)*
#name: shared_resources::#shared_name::new(priority)
@@ -95,17 +80,13 @@ pub fn codegen(ctxt: Context, needs_lt: &mut bool, app: &App) -> (TokenStream2,
continue;
}
- let resource_doc;
let expr = if access.is_exclusive() {
- resource_doc = format!(" Exclusive access resource `{name}`");
quote!(&mut *(&mut *#mangled_name.get_mut()).as_mut_ptr())
} else {
- resource_doc = format!(" Non-exclusive access resource `{name}`");
quote!(&*(&*#mangled_name.get()).as_ptr())
};
values.push(quote!(
- #[doc = #resource_doc]
#(#cfgs)*
#name: #expr
));
@@ -125,13 +106,12 @@ pub fn codegen(ctxt: Context, needs_lt: &mut bool, app: &App) -> (TokenStream2,
}
}
- let doc = format!(" Shared resources `{}` has access to", ctxt.ident(app));
+ let doc = format!("Shared resources `{}` has access to", ctxt.ident(app));
let ident = util::shared_resources_ident(ctxt, app);
let item = quote!(
#[allow(non_snake_case)]
#[allow(non_camel_case_types)]
#[doc = #doc]
- #(#task_cfgs)*
pub struct #ident<#lt> {
#(#fields,)*
}
@@ -143,9 +123,7 @@ pub fn codegen(ctxt: Context, needs_lt: &mut bool, app: &App) -> (TokenStream2,
Some(quote!(priority: &#lt rtic::export::Priority))
};
let constructor = quote!(
- #(#task_cfgs)*
impl<#lt> #ident<#lt> {
- #[doc(hidden)]
#[inline(always)]
pub unsafe fn new(#arg) -> Self {
#ident {
diff --git a/macros/src/codegen/software_tasks.rs b/macros/src/codegen/software_tasks.rs
index 226121dd..f9247dae 100644
--- a/macros/src/codegen/software_tasks.rs
+++ b/macros/src/codegen/software_tasks.rs
@@ -1,17 +1,14 @@
-use proc_macro2::TokenStream as TokenStream2;
-use quote::quote;
-use rtic_syntax::{ast::App, Context};
-
+use crate::syntax::{ast::App, Context};
use crate::{
analyze::Analysis,
- check::Extra,
codegen::{local_resources_struct, module, shared_resources_struct, util},
};
+use proc_macro2::TokenStream as TokenStream2;
+use quote::quote;
pub fn codegen(
app: &App,
analysis: &Analysis,
- extra: &Extra,
) -> (
// mod_app_software_tasks -- free queues, buffers and `${task}Resources` constructors
Vec<TokenStream2>,
@@ -27,74 +24,87 @@ pub fn codegen(
let mut root = vec![];
let mut user_tasks = vec![];
- for (name, task) in &app.software_tasks {
+ // Any task
+ for (name, task) in app.software_tasks.iter() {
let inputs = &task.inputs;
- let cfgs = &task.cfgs;
let (_, _, _, input_ty) = util::regroup_inputs(inputs);
let cap = task.args.capacity;
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
- #(#cfgs)*
- #[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;
- let cfgs = &monotonic.cfgs;
+ #[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]
#[allow(non_camel_case_types)]
#[allow(non_upper_case_globals)]
#[doc(hidden)]
- #(#cfgs)*
static #instants:
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)]
- #(#cfgs)*
- 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!(
+ #[allow(non_camel_case_types)]
+ #[allow(non_upper_case_globals)]
+ #[doc(hidden)]
+ static #executor_ident: core::sync::atomic::AtomicBool =
+ core::sync::atomic::AtomicBool::new(false);
+ ));
+ }
+
+ let inputs = &task.inputs;
// `${task}Resources`
let mut shared_needs_lt = false;
@@ -130,13 +140,24 @@ pub fn codegen(
let attrs = &task.attrs;
let cfgs = &task.cfgs;
let stmts = &task.stmts;
- let user_task_doc = format!(" User SW task {name}");
+ let (async_marker, context_lifetime) = if task.is_async {
+ (
+ quote!(async),
+ if shared_needs_lt || local_needs_lt {
+ quote!(<'static>)
+ } else {
+ quote!()
+ },
+ )
+ } else {
+ (quote!(), quote!())
+ };
+
user_tasks.push(quote!(
- #[doc = #user_task_doc]
#(#attrs)*
#(#cfgs)*
#[allow(non_snake_case)]
- fn #name(#context: #name::Context #(,#inputs)*) {
+ #async_marker fn #name(#context: #name::Context #context_lifetime #(,#inputs)*) {
use rtic::Mutex as _;
use rtic::mutex::prelude::*;
@@ -151,7 +172,6 @@ pub fn codegen(
local_needs_lt,
app,
analysis,
- extra,
));
}
diff --git a/macros/src/codegen/timer_queue.rs b/macros/src/codegen/timer_queue.rs
index f5867dc4..281148d9 100644
--- a/macros/src/codegen/timer_queue.rs
+++ b/macros/src/codegen/timer_queue.rs
@@ -1,18 +1,18 @@
+use crate::syntax::ast::App;
+use crate::{analyze::Analysis, codegen::util};
use proc_macro2::TokenStream as TokenStream2;
use quote::quote;
-use rtic_syntax::ast::App;
-
-use crate::{analyze::Analysis, check::Extra, codegen::util};
/// Generates timer queues and timer queue handlers
#[allow(clippy::too_many_lines)]
-pub fn codegen(app: &App, analysis: &Analysis, _extra: &Extra) -> Vec<TokenStream2> {
+pub fn codegen(app: &App, analysis: &Analysis) -> Vec<TokenStream2> {
let mut items = vec![];
if !app.monotonics.is_empty() {
// Generate the marker counter used to track for `cancel` and `reschedule`
let tq_marker = util::timer_queue_marker_ident();
items.push(quote!(
+ // #[doc = #doc]
#[doc(hidden)]
#[allow(non_camel_case_types)]
#[allow(non_upper_case_globals)]
@@ -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;
@@ -55,7 +56,6 @@ pub fn codegen(app: &App, analysis: &Analysis, _extra: &Extra) -> Vec<TokenStrea
let tq = util::tq_ident(&monotonic_name);
let t = util::schedule_t_ident();
let mono_type = &monotonic.ty;
- let cfgs = &monotonic.cfgs;
let m_ident = util::monotonic_ident(&monotonic_name);
// Static variables and resource proxy
@@ -67,8 +67,8 @@ pub fn codegen(app: &App, analysis: &Analysis, _extra: &Extra) -> Vec<TokenStrea
.iter()
.map(|(_name, task)| task.args.capacity as usize)
.sum();
- let n = util::capacity_literal(cap);
- let tq_ty = quote!(rtic::export::TimerQueue<#mono_type, #t, #n>);
+ let n_task = util::capacity_literal(cap);
+ let tq_ty = quote!(rtic::export::TimerQueue<#mono_type, #t, #n_task>);
// For future use
// let doc = format!(" RTIC internal: {}:{}", file!(), line!());
@@ -76,9 +76,12 @@ pub fn codegen(app: &App, analysis: &Analysis, _extra: &Extra) -> Vec<TokenStrea
#[doc(hidden)]
#[allow(non_camel_case_types)]
#[allow(non_upper_case_globals)]
- #(#cfgs)*
- static #tq: rtic::RacyCell<#tq_ty> =
- rtic::RacyCell::new(rtic::export::TimerQueue(rtic::export::SortedLinkedList::new_u16()));
+ static #tq: rtic::RacyCell<#tq_ty> = rtic::RacyCell::new(
+ rtic::export::TimerQueue {
+ task_queue: rtic::export::SortedLinkedList::new_u16(),
+ waker_queue: rtic::export::IntrusiveSortedLinkedList::new(),
+ }
+ );
));
let mono = util::monotonic_ident(&monotonic_name);
@@ -89,7 +92,6 @@ pub fn codegen(app: &App, analysis: &Analysis, _extra: &Extra) -> Vec<TokenStrea
#[doc(hidden)]
#[allow(non_camel_case_types)]
#[allow(non_upper_case_globals)]
- #(#cfgs)*
static #mono: rtic::RacyCell<Option<#mono_type>> = rtic::RacyCell::new(None);
));
}
@@ -102,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;
@@ -109,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!(
@@ -120,7 +123,9 @@ pub fn codegen(app: &App, analysis: &Analysis, _extra: &Extra) -> Vec<TokenStrea
quote!(
#(#cfgs)*
#t::#name => {
- rtic::export::interrupt::free(|_| (&mut *#rq.get_mut()).split().0.enqueue_unchecked((#rqt::#name, index)));
+ rtic::export::interrupt::free(|_|
+ (&mut *#rq.get_mut()).split().0.enqueue_unchecked((#rqt::#name, index))
+ );
#pend
}
@@ -128,7 +133,6 @@ pub fn codegen(app: &App, analysis: &Analysis, _extra: &Extra) -> Vec<TokenStrea
})
.collect::<Vec<_>>();
- let cfgs = &monotonic.cfgs;
let bound_interrupt = &monotonic.args.binds;
let disable_isr = if &*bound_interrupt.to_string() == "SysTick" {
quote!(core::mem::transmute::<_, rtic::export::SYST>(()).disable_interrupt())
@@ -139,7 +143,6 @@ pub fn codegen(app: &App, analysis: &Analysis, _extra: &Extra) -> Vec<TokenStrea
items.push(quote!(
#[no_mangle]
#[allow(non_snake_case)]
- #(#cfgs)*
unsafe fn #bound_interrupt() {
while let Some((task, index)) = rtic::export::interrupt::free(|_|
if let Some(mono) = (&mut *#m_ident.get_mut()).as_mut() {
diff --git a/macros/src/codegen/util.rs b/macros/src/codegen/util.rs
index 0a3edc20..151906da 100644
--- a/macros/src/codegen/util.rs
+++ b/macros/src/codegen/util.rs
@@ -1,12 +1,10 @@
use core::sync::atomic::{AtomicUsize, Ordering};
+use crate::syntax::{ast::App, Context};
use proc_macro2::{Span, TokenStream as TokenStream2};
use quote::quote;
-use rtic_syntax::{ast::App, Context};
use syn::{Attribute, Ident, LitInt, PatType};
-use crate::check::Extra;
-
const RTIC_INTERNAL: &str = "__rtic_internal";
/// Turns `capacity` into an unsuffixed integer literal
@@ -21,7 +19,7 @@ pub fn fq_ident(task: &Ident) -> Ident {
/// Generates a `Mutex` implementation
pub fn impl_mutex(
- extra: &Extra,
+ app: &App,
cfgs: &[Attribute],
resources_prefix: bool,
name: &Ident,
@@ -35,7 +33,7 @@ pub fn impl_mutex(
(quote!(#name), quote!(self.priority))
};
- let device = &extra.device;
+ let device = &app.args.device;
let masks_name = priority_masks_ident();
quote!(
#(#cfgs)*
@@ -67,6 +65,11 @@ pub fn inputs_ident(task: &Ident) -> Ident {
mark_internal_name(&format!("{}_INPUTS", task))
}
+/// Generates an identifier for the `EXECUTOR_RUN` atomics (`async` API)
+pub fn executor_run_ident(task: &Ident) -> Ident {
+ mark_internal_name(&format!("{}_EXECUTOR_RUN", task))
+}
+
/// Generates an identifier for the `INSTANTS` buffer (`schedule` API)
pub fn monotonic_instants_ident(task: &Ident, monotonic: &Ident) -> Ident {
mark_internal_name(&format!("{}_{}_INSTANTS", task, monotonic))
@@ -179,7 +182,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(),
};
@@ -190,7 +198,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(),
};
@@ -203,7 +216,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(),
};
@@ -220,9 +238,14 @@ 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 {
- Ident::new("SCHED_T", Span::call_site())
+ mark_internal_name("SCHED_T")
}
/// Generates an identifier for the `enum` of `spawn`-able tasks
@@ -230,7 +253,7 @@ pub fn schedule_t_ident() -> Ident {
/// This identifier needs the same structure as the `RQ` identifier because there's one ready queue
/// for each of these `T` enums
pub fn spawn_t_ident(priority: u8) -> Ident {
- Ident::new(&format!("P{}_T", priority), Span::call_site())
+ mark_internal_name(&format!("P{}_T", priority))
}
/// Suffixed identifier