aboutsummaryrefslogtreecommitdiff
path: root/rtic-macros/src/codegen
diff options
context:
space:
mode:
Diffstat (limited to 'rtic-macros/src/codegen')
-rw-r--r--rtic-macros/src/codegen/assertions.rs53
-rw-r--r--rtic-macros/src/codegen/async_dispatchers.rs89
-rw-r--r--rtic-macros/src/codegen/hardware_tasks.rs87
-rw-r--r--rtic-macros/src/codegen/idle.rs58
-rw-r--r--rtic-macros/src/codegen/init.rs95
-rw-r--r--rtic-macros/src/codegen/local_resources.rs65
-rw-r--r--rtic-macros/src/codegen/local_resources_struct.rs102
-rw-r--r--rtic-macros/src/codegen/main.rs52
-rw-r--r--rtic-macros/src/codegen/module.rs197
-rw-r--r--rtic-macros/src/codegen/post_init.rs47
-rw-r--r--rtic-macros/src/codegen/pre_init.rs85
-rw-r--r--rtic-macros/src/codegen/shared_resources.rs183
-rw-r--r--rtic-macros/src/codegen/shared_resources_struct.rs119
-rw-r--r--rtic-macros/src/codegen/software_tasks.rs64
-rw-r--r--rtic-macros/src/codegen/util.rs238
15 files changed, 1534 insertions, 0 deletions
diff --git a/rtic-macros/src/codegen/assertions.rs b/rtic-macros/src/codegen/assertions.rs
new file mode 100644
index 00000000..dd94aa6d
--- /dev/null
+++ b/rtic-macros/src/codegen/assertions.rs
@@ -0,0 +1,53 @@
+use proc_macro2::TokenStream as TokenStream2;
+use quote::quote;
+
+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) -> Vec<TokenStream2> {
+ let mut stmts = vec![];
+
+ for ty in &analysis.send_types {
+ stmts.push(quote!(rtic::export::assert_send::<#ty>();));
+ }
+
+ for ty in &analysis.sync_types {
+ stmts.push(quote!(rtic::export::assert_sync::<#ty>();));
+ }
+
+ let device = &app.args.device;
+ let chunks_name = util::priority_mask_chunks_ident();
+ let no_basepri_checks: Vec<_> = app
+ .hardware_tasks
+ .iter()
+ .filter_map(|(_, task)| {
+ if !util::is_exception(&task.args.binds) {
+ let interrupt_name = &task.args.binds;
+ Some(quote!(
+ 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");
+ }
+ ))
+ } else {
+ None
+ }
+ })
+ .collect();
+
+ let const_check = quote! {
+ const _CONST_CHECK: () = {
+ if !rtic::export::have_basepri() {
+ #(#no_basepri_checks)*
+ } else {
+ // TODO: Add armv7 checks here
+ }
+ };
+
+ let _ = _CONST_CHECK;
+ };
+
+ stmts.push(const_check);
+
+ stmts
+}
diff --git a/rtic-macros/src/codegen/async_dispatchers.rs b/rtic-macros/src/codegen/async_dispatchers.rs
new file mode 100644
index 00000000..a12ad325
--- /dev/null
+++ b/rtic-macros/src/codegen/async_dispatchers.rs
@@ -0,0 +1,89 @@
+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) -> TokenStream2 {
+ let mut items = vec![];
+
+ let interrupts = &analysis.interrupts;
+
+ // Generate executor definition and priority in global scope
+ for (name, _) in app.software_tasks.iter() {
+ let type_name = util::internal_task_ident(name, "F");
+ let exec_name = util::internal_task_ident(name, "EXEC");
+
+ items.push(quote!(
+ #[allow(non_camel_case_types)]
+ type #type_name = impl core::future::Future;
+ #[allow(non_upper_case_globals)]
+ static #exec_name: rtic::export::executor::AsyncTaskExecutor<#type_name> =
+ rtic::export::executor::AsyncTaskExecutor::new();
+ ));
+ }
+
+ for (&level, channel) in &analysis.channels {
+ let mut stmts = vec![];
+
+ let dispatcher_name = if level > 0 {
+ util::suffixed(&interrupts.get(&level).expect("UNREACHABLE").0.to_string())
+ } else {
+ util::zero_prio_dispatcher_ident()
+ };
+
+ let pend_interrupt = if level > 0 {
+ let device = &app.args.device;
+ let enum_ = util::interrupt_ident();
+
+ quote!(rtic::pend(#device::#enum_::#dispatcher_name);)
+ } else {
+ // For 0 priority tasks we don't need to pend anything
+ quote!()
+ };
+
+ for name in channel.tasks.iter() {
+ let exec_name = util::internal_task_ident(name, "EXEC");
+ // TODO: Fix cfg
+ // let task = &app.software_tasks[name];
+ // let cfgs = &task.cfgs;
+
+ stmts.push(quote!(
+ #exec_name.poll(|| {
+ #exec_name.set_pending();
+ #pend_interrupt
+ });
+ ));
+ }
+
+ if level > 0 {
+ let doc = format!("Interrupt handler to dispatch async tasks at priority {level}");
+ let attribute = &interrupts.get(&level).expect("UNREACHABLE").1.attrs;
+ items.push(quote!(
+ #[allow(non_snake_case)]
+ #[doc = #doc]
+ #[no_mangle]
+ #(#attribute)*
+ unsafe fn #dispatcher_name() {
+ /// The priority of this interrupt handler
+ const PRIORITY: u8 = #level;
+
+ rtic::export::run(PRIORITY, || {
+ #(#stmts)*
+ });
+ }
+ ));
+ } else {
+ items.push(quote!(
+ #[allow(non_snake_case)]
+ unsafe fn #dispatcher_name() -> ! {
+ loop {
+ #(#stmts)*
+ }
+ }
+ ));
+ }
+ }
+
+ quote!(#(#items)*)
+}
diff --git a/rtic-macros/src/codegen/hardware_tasks.rs b/rtic-macros/src/codegen/hardware_tasks.rs
new file mode 100644
index 00000000..8a5a8f6c
--- /dev/null
+++ b/rtic-macros/src/codegen/hardware_tasks.rs
@@ -0,0 +1,87 @@
+use crate::syntax::{ast::App, Context};
+use crate::{
+ analyze::Analysis,
+ 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) -> TokenStream2 {
+ let mut mod_app = vec![];
+ let mut root = vec![];
+ let mut user_tasks = vec![];
+
+ for (name, task) in &app.hardware_tasks {
+ let symbol = task.args.binds.clone();
+ let priority = task.args.priority;
+ let cfgs = &task.cfgs;
+ let attrs = &task.attrs;
+
+ mod_app.push(quote!(
+ #[allow(non_snake_case)]
+ #[no_mangle]
+ #(#attrs)*
+ #(#cfgs)*
+ unsafe fn #symbol() {
+ const PRIORITY: u8 = #priority;
+
+ rtic::export::run(PRIORITY, || {
+ #name(
+ #name::Context::new()
+ )
+ });
+ }
+ ));
+
+ // `${task}Locals`
+ if !task.args.local_resources.is_empty() {
+ let (item, constructor) =
+ local_resources_struct::codegen(Context::HardwareTask(name), app);
+
+ root.push(item);
+
+ mod_app.push(constructor);
+ }
+
+ // `${task}Resources`
+ if !task.args.shared_resources.is_empty() {
+ let (item, constructor) =
+ shared_resources_struct::codegen(Context::HardwareTask(name), app);
+
+ root.push(item);
+
+ mod_app.push(constructor);
+ }
+
+ // Module generation...
+
+ root.push(module::codegen(Context::HardwareTask(name), app, analysis));
+
+ // End module generation
+
+ if !task.is_extern {
+ let attrs = &task.attrs;
+ let context = &task.context;
+ let stmts = &task.stmts;
+ user_tasks.push(quote!(
+ #(#attrs)*
+ #[allow(non_snake_case)]
+ fn #name(#context: #name::Context) {
+ use rtic::Mutex as _;
+ use rtic::mutex::prelude::*;
+
+ #(#stmts)*
+ }
+ ));
+ }
+ }
+
+ quote!(
+ #(#mod_app)*
+
+ #(#root)*
+
+ #(#user_tasks)*
+ )
+}
diff --git a/rtic-macros/src/codegen/idle.rs b/rtic-macros/src/codegen/idle.rs
new file mode 100644
index 00000000..0c833ef3
--- /dev/null
+++ b/rtic-macros/src/codegen/idle.rs
@@ -0,0 +1,58 @@
+use crate::syntax::{ast::App, Context};
+use crate::{
+ analyze::Analysis,
+ 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) -> TokenStream2 {
+ if let Some(idle) = &app.idle {
+ let mut mod_app = vec![];
+ let mut root_idle = vec![];
+
+ let name = &idle.name;
+
+ if !idle.args.shared_resources.is_empty() {
+ let (item, constructor) = shared_resources_struct::codegen(Context::Idle, app);
+
+ root_idle.push(item);
+ mod_app.push(constructor);
+ }
+
+ if !idle.args.local_resources.is_empty() {
+ let (item, constructor) = local_resources_struct::codegen(Context::Idle, app);
+
+ root_idle.push(item);
+
+ mod_app.push(constructor);
+ }
+
+ root_idle.push(module::codegen(Context::Idle, app, analysis));
+
+ let attrs = &idle.attrs;
+ let context = &idle.context;
+ let stmts = &idle.stmts;
+ let user_idle = Some(quote!(
+ #(#attrs)*
+ #[allow(non_snake_case)]
+ fn #name(#context: #name::Context) -> ! {
+ use rtic::Mutex as _;
+ use rtic::mutex::prelude::*;
+
+ #(#stmts)*
+ }
+ ));
+
+ quote!(
+ #(#mod_app)*
+
+ #(#root_idle)*
+
+ #user_idle
+ )
+ } else {
+ quote!()
+ }
+}
diff --git a/rtic-macros/src/codegen/init.rs b/rtic-macros/src/codegen/init.rs
new file mode 100644
index 00000000..6e1059f7
--- /dev/null
+++ b/rtic-macros/src/codegen/init.rs
@@ -0,0 +1,95 @@
+use proc_macro2::TokenStream as TokenStream2;
+use quote::quote;
+
+use crate::{
+ analyze::Analysis,
+ codegen::{local_resources_struct, module},
+ syntax::{ast::App, Context},
+};
+
+/// Generates support code for `#[init]` functions
+pub fn codegen(app: &App, analysis: &Analysis) -> TokenStream2 {
+ let init = &app.init;
+ let name = &init.name;
+
+ let mut root_init = vec![];
+
+ let context = &init.context;
+ let attrs = &init.attrs;
+ let stmts = &init.stmts;
+ let shared = &init.user_shared_struct;
+ let local = &init.user_local_struct;
+
+ let shared_resources: Vec<_> = app
+ .shared_resources
+ .iter()
+ .map(|(k, v)| {
+ let ty = &v.ty;
+ let cfgs = &v.cfgs;
+ let docs = &v.docs;
+ quote!(
+ #(#cfgs)*
+ #(#docs)*
+ #k: #ty,
+ )
+ })
+ .collect();
+ let local_resources: Vec<_> = app
+ .local_resources
+ .iter()
+ .map(|(k, v)| {
+ let ty = &v.ty;
+ let cfgs = &v.cfgs;
+ let docs = &v.docs;
+ quote!(
+ #(#cfgs)*
+ #(#docs)*
+ #k: #ty,
+ )
+ })
+ .collect();
+
+ root_init.push(quote! {
+ struct #shared {
+ #(#shared_resources)*
+ }
+
+ struct #local {
+ #(#local_resources)*
+ }
+ });
+
+ // let locals_pat = locals_pat.iter();
+
+ let user_init_return = quote! {#shared, #local};
+
+ let user_init = quote!(
+ #(#attrs)*
+ #[inline(always)]
+ #[allow(non_snake_case)]
+ fn #name(#context: #name::Context) -> (#user_init_return) {
+ #(#stmts)*
+ }
+ );
+
+ let mut mod_app = None;
+
+ // `${task}Locals`
+ if !init.args.local_resources.is_empty() {
+ let (item, constructor) = local_resources_struct::codegen(Context::Init, app);
+
+ root_init.push(item);
+
+ mod_app = Some(constructor);
+ }
+
+ root_init.push(module::codegen(Context::Init, app, analysis));
+
+ quote!(
+ #mod_app
+
+ #(#root_init)*
+
+ #user_init
+ )
+}
diff --git a/rtic-macros/src/codegen/local_resources.rs b/rtic-macros/src/codegen/local_resources.rs
new file mode 100644
index 00000000..e6d15533
--- /dev/null
+++ b/rtic-macros/src/codegen/local_resources.rs
@@ -0,0 +1,65 @@
+use crate::syntax::ast::App;
+use crate::{analyze::Analysis, codegen::util};
+use proc_macro2::TokenStream as TokenStream2;
+use quote::quote;
+
+/// Generates `local` variables and local resource proxies
+///
+/// I.e. the `static` variables and theirs proxies.
+pub fn codegen(app: &App, _analysis: &Analysis) -> TokenStream2 {
+ let mut mod_app = vec![];
+
+ // All local resources declared in the `#[local]' struct
+ for (name, res) in &app.local_resources {
+ let cfgs = &res.cfgs;
+ let ty = &res.ty;
+ let mangled_name = util::static_local_resource_ident(name);
+
+ let attrs = &res.attrs;
+
+ // late resources in `util::link_section_uninit`
+ // unless user specifies custom link section
+ let section = if attrs.iter().any(|attr| attr.path.is_ident("link_section")) {
+ None
+ } else {
+ Some(util::link_section_uninit())
+ };
+
+ // For future use
+ // let doc = format!(" RTIC internal: {}:{}", file!(), line!());
+ mod_app.push(quote!(
+ #[allow(non_camel_case_types)]
+ #[allow(non_upper_case_globals)]
+ // #[doc = #doc]
+ #[doc(hidden)]
+ #(#attrs)*
+ #(#cfgs)*
+ #section
+ static #mangled_name: rtic::RacyCell<core::mem::MaybeUninit<#ty>> = rtic::RacyCell::new(core::mem::MaybeUninit::uninit());
+ ));
+ }
+
+ // All declared `local = [NAME: TY = EXPR]` local resources
+ for (task_name, resource_name, task_local) in app.declared_local_resources() {
+ let cfgs = &task_local.cfgs;
+ let ty = &task_local.ty;
+ let expr = &task_local.expr;
+ let attrs = &task_local.attrs;
+
+ let mangled_name = util::declared_static_local_resource_ident(resource_name, task_name);
+
+ // For future use
+ // let doc = format!(" RTIC internal: {}:{}", file!(), line!());
+ mod_app.push(quote!(
+ #[allow(non_camel_case_types)]
+ #[allow(non_upper_case_globals)]
+ // #[doc = #doc]
+ #[doc(hidden)]
+ #(#attrs)*
+ #(#cfgs)*
+ static #mangled_name: rtic::RacyCell<#ty> = rtic::RacyCell::new(#expr);
+ ));
+ }
+
+ quote!(#(#mod_app)*)
+}
diff --git a/rtic-macros/src/codegen/local_resources_struct.rs b/rtic-macros/src/codegen/local_resources_struct.rs
new file mode 100644
index 00000000..100c3eb5
--- /dev/null
+++ b/rtic-macros/src/codegen/local_resources_struct.rs
@@ -0,0 +1,102 @@
+use crate::syntax::{
+ ast::{App, TaskLocal},
+ Context,
+};
+use proc_macro2::TokenStream as TokenStream2;
+use quote::quote;
+
+use crate::codegen::util;
+
+/// Generates local resources structs
+pub fn codegen(ctxt: Context, app: &App) -> (TokenStream2, TokenStream2) {
+ let resources = match ctxt {
+ Context::Init => &app.init.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,
+ };
+
+ let task_name = util::get_task_name(ctxt, app);
+
+ let mut fields = vec![];
+ let mut values = vec![];
+
+ for (name, task_local) in resources {
+ let (cfgs, ty, is_declared) = match task_local {
+ TaskLocal::External => {
+ let r = app.local_resources.get(name).expect("UNREACHABLE");
+ (&r.cfgs, &r.ty, false)
+ }
+ TaskLocal::Declared(r) => (&r.cfgs, &r.ty, true),
+ };
+
+ let lt = if ctxt.runs_once() {
+ quote!('static)
+ } else {
+ quote!('a)
+ };
+
+ let mangled_name = if matches!(task_local, TaskLocal::External) {
+ util::static_local_resource_ident(name)
+ } else {
+ util::declared_static_local_resource_ident(name, &task_name)
+ };
+
+ fields.push(quote!(
+ #(#cfgs)*
+ #[allow(missing_docs)]
+ pub #name: &#lt mut #ty
+ ));
+
+ let expr = if is_declared {
+ // If the local resources is already initialized, we only need to access its value and
+ // not go through an `MaybeUninit`
+ quote!(&mut *#mangled_name.get_mut())
+ } else {
+ quote!(&mut *(&mut *#mangled_name.get_mut()).as_mut_ptr())
+ };
+
+ values.push(quote!(
+ #(#cfgs)*
+ #name: #expr
+ ));
+ }
+
+ fields.push(quote!(
+ #[doc(hidden)]
+ pub __rtic_internal_marker: ::core::marker::PhantomData<&'a ()>
+ ));
+
+ values.push(quote!(__rtic_internal_marker: ::core::marker::PhantomData));
+
+ 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)]
+ #[allow(non_camel_case_types)]
+ #[doc = #doc]
+ pub struct #ident<'a> {
+ #(#fields,)*
+ }
+ );
+
+ let constructor = quote!(
+ impl<'a> #ident<'a> {
+ #[inline(always)]
+ #[allow(missing_docs)]
+ pub unsafe fn new() -> Self {
+ #ident {
+ #(#values,)*
+ }
+ }
+ }
+ );
+
+ (item, constructor)
+}
diff --git a/rtic-macros/src/codegen/main.rs b/rtic-macros/src/codegen/main.rs
new file mode 100644
index 00000000..2775d259
--- /dev/null
+++ b/rtic-macros/src/codegen/main.rs
@@ -0,0 +1,52 @@
+use crate::{analyze::Analysis, codegen::util, syntax::ast::App};
+use proc_macro2::TokenStream as TokenStream2;
+use quote::quote;
+
+use super::{assertions, post_init, pre_init};
+
+/// Generates code for `fn main`
+pub fn codegen(app: &App, analysis: &Analysis) -> TokenStream2 {
+ let assertion_stmts = assertions::codegen(app, analysis);
+
+ let pre_init_stmts = pre_init::codegen(app, analysis);
+
+ let post_init_stmts = post_init::codegen(app, analysis);
+
+ let call_idle = if let Some(idle) = &app.idle {
+ let name = &idle.name;
+ quote!(#name(#name::Context::new()))
+ } else if analysis.channels.get(&0).is_some() {
+ let dispatcher = util::zero_prio_dispatcher_ident();
+ quote!(#dispatcher();)
+ } else {
+ quote!(loop {
+ rtic::export::nop()
+ })
+ };
+
+ let main = util::suffixed("main");
+ let init_name = &app.init.name;
+ quote!(
+ #[doc(hidden)]
+ #[no_mangle]
+ unsafe extern "C" fn #main() -> ! {
+ #(#assertion_stmts)*
+
+ #(#pre_init_stmts)*
+
+ #[inline(never)]
+ fn __rtic_init_resources<F>(f: F) where F: FnOnce() {
+ f();
+ }
+
+ // Wrap late_init_stmts in a function to ensure that stack space is reclaimed.
+ __rtic_init_resources(||{
+ let (shared_resources, local_resources) = #init_name(#init_name::Context::new(core.into()));
+
+ #(#post_init_stmts)*
+ });
+
+ #call_idle
+ }
+ )
+}
diff --git a/rtic-macros/src/codegen/module.rs b/rtic-macros/src/codegen/module.rs
new file mode 100644
index 00000000..8b3fca23
--- /dev/null
+++ b/rtic-macros/src/codegen/module.rs
@@ -0,0 +1,197 @@
+use crate::syntax::{ast::App, Context};
+use crate::{analyze::Analysis, codegen::util};
+use proc_macro2::TokenStream as TokenStream2;
+use quote::quote;
+
+#[allow(clippy::too_many_lines)]
+pub fn codegen(ctxt: Context, app: &App, analysis: &Analysis) -> 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);
+
+ match ctxt {
+ Context::Init => {
+ fields.push(quote!(
+ /// Core (Cortex-M) peripherals
+ pub core: rtic::export::Peripherals
+ ));
+
+ if app.args.peripherals {
+ let device = &app.args.device;
+
+ fields.push(quote!(
+ /// Device peripherals
+ pub device: #device::Peripherals
+ ));
+
+ values.push(quote!(device: #device::Peripherals::steal()));
+ }
+
+ fields.push(quote!(
+ /// Critical section token for init
+ pub cs: rtic::export::CriticalSection<'a>
+ ));
+
+ values.push(quote!(cs: rtic::export::CriticalSection::new()));
+
+ values.push(quote!(core));
+ }
+
+ Context::Idle | Context::HardwareTask(_) | Context::SoftwareTask(_) => {}
+ }
+
+ if ctxt.has_local_resources(app) {
+ let ident = util::local_resources_ident(ctxt, app);
+
+ module_items.push(quote!(
+ #[doc(inline)]
+ pub use super::#ident as LocalResources;
+ ));
+
+ fields.push(quote!(
+ /// Local Resources this task has access to
+ pub local: #name::LocalResources<'a>
+ ));
+
+ values.push(quote!(local: #name::LocalResources::new()));
+ }
+
+ if ctxt.has_shared_resources(app) {
+ let ident = util::shared_resources_ident(ctxt, app);
+
+ module_items.push(quote!(
+ #[doc(inline)]
+ pub use super::#ident as SharedResources;
+ ));
+
+ fields.push(quote!(
+ /// Shared Resources this task has access to
+ pub shared: #name::SharedResources<'a>
+ ));
+
+ values.push(quote!(shared: #name::SharedResources::new()));
+ }
+
+ let doc = match ctxt {
+ Context::Idle => "Idle loop",
+ Context::Init => "Initialization function",
+ Context::HardwareTask(_) => "Hardware task",
+ Context::SoftwareTask(_) => "Software task",
+ };
+
+ let v = Vec::new();
+ let cfgs = match ctxt {
+ Context::HardwareTask(t) => &app.hardware_tasks[t].cfgs,
+ Context::SoftwareTask(t) => &app.software_tasks[t].cfgs,
+ _ => &v,
+ };
+
+ let core = if ctxt.is_init() {
+ Some(quote!(core: rtic::export::Peripherals,))
+ } else {
+ None
+ };
+
+ let internal_context_name = util::internal_task_ident(name, "Context");
+ let exec_name = util::internal_task_ident(name, "EXEC");
+
+ items.push(quote!(
+ #(#cfgs)*
+ /// Execution context
+ #[allow(non_snake_case)]
+ #[allow(non_camel_case_types)]
+ pub struct #internal_context_name<'a> {
+ #[doc(hidden)]
+ __rtic_internal_p: ::core::marker::PhantomData<&'a ()>,
+ #(#fields,)*
+ }
+
+ #(#cfgs)*
+ impl<'a> #internal_context_name<'a> {
+ #[inline(always)]
+ #[allow(missing_docs)]
+ pub unsafe fn new(#core) -> Self {
+ #internal_context_name {
+ __rtic_internal_p: ::core::marker::PhantomData,
+ #(#values,)*
+ }
+ }
+ }
+ ));
+
+ module_items.push(quote!(
+ #(#cfgs)*
+ #[doc(inline)]
+ pub use super::#internal_context_name as Context;
+ ));
+
+ if let Context::SoftwareTask(..) = ctxt {
+ let spawnee = &app.software_tasks[name];
+ let priority = spawnee.args.priority;
+ let cfgs = &spawnee.cfgs;
+ // Store a copy of the task cfgs
+ task_cfgs = cfgs.clone();
+
+ let pend_interrupt = if priority > 0 {
+ let device = &app.args.device;
+ let enum_ = util::interrupt_ident();
+ let interrupt = &analysis.interrupts.get(&priority).expect("UREACHABLE").0;
+ quote!(rtic::pend(#device::#enum_::#interrupt);)
+ } else {
+ quote!()
+ };
+
+ let internal_spawn_ident = util::internal_task_ident(name, "spawn");
+ let (input_args, input_tupled, input_untupled, input_ty) =
+ util::regroup_inputs(&spawnee.inputs);
+
+ // Spawn caller
+ items.push(quote!(
+ #(#cfgs)*
+ /// Spawns the task directly
+ #[allow(non_snake_case)]
+ #[doc(hidden)]
+ pub fn #internal_spawn_ident(#(#input_args,)*) -> Result<(), #input_ty> {
+ // SAFETY: If `try_allocate` suceeds one must call `spawn`, which we do.
+ unsafe {
+ if #exec_name.try_allocate() {
+ let f = #name(unsafe { #name::Context::new() } #(,#input_untupled)*);
+ #exec_name.spawn(f);
+ #pend_interrupt
+
+ Ok(())
+ } else {
+ Err(#input_tupled)
+ }
+ }
+
+ }
+ ));
+
+ module_items.push(quote!(
+ #(#cfgs)*
+ #[doc(inline)]
+ pub use super::#internal_spawn_ident as spawn;
+ ));
+ }
+
+ if items.is_empty() {
+ quote!()
+ } else {
+ quote!(
+ #(#items)*
+
+ #[allow(non_snake_case)]
+ #(#task_cfgs)*
+ #[doc = #doc]
+ pub mod #name {
+ #(#module_items)*
+ }
+ )
+ }
+}
diff --git a/rtic-macros/src/codegen/post_init.rs b/rtic-macros/src/codegen/post_init.rs
new file mode 100644
index 00000000..c4e53837
--- /dev/null
+++ b/rtic-macros/src/codegen/post_init.rs
@@ -0,0 +1,47 @@
+use crate::{analyze::Analysis, codegen::util, syntax::ast::App};
+use proc_macro2::TokenStream as TokenStream2;
+use quote::quote;
+
+/// Generates code that runs after `#[init]` returns
+pub fn codegen(app: &App, analysis: &Analysis) -> Vec<TokenStream2> {
+ let mut stmts = vec![];
+
+ // Initialize shared resources
+ for (name, res) in &app.shared_resources {
+ let mangled_name = util::static_shared_resource_ident(name);
+ // If it's live
+ let cfgs = res.cfgs.clone();
+ if analysis.shared_resources.get(name).is_some() {
+ stmts.push(quote!(
+ // We include the cfgs
+ #(#cfgs)*
+ // Resource is a RacyCell<MaybeUninit<T>>
+ // - `get_mut` to obtain a raw pointer to `MaybeUninit<T>`
+ // - `write` the defined value for the late resource T
+ #mangled_name.get_mut().write(core::mem::MaybeUninit::new(shared_resources.#name));
+ ));
+ }
+ }
+
+ // Initialize local resources
+ for (name, res) in &app.local_resources {
+ let mangled_name = util::static_local_resource_ident(name);
+ // If it's live
+ let cfgs = res.cfgs.clone();
+ if analysis.local_resources.get(name).is_some() {
+ stmts.push(quote!(
+ // We include the cfgs
+ #(#cfgs)*
+ // Resource is a RacyCell<MaybeUninit<T>>
+ // - `get_mut` to obtain a raw pointer to `MaybeUninit<T>`
+ // - `write` the defined value for the late resource T
+ #mangled_name.get_mut().write(core::mem::MaybeUninit::new(local_resources.#name));
+ ));
+ }
+ }
+
+ // Enable the interrupts -- this completes the `init`-ialization phase
+ stmts.push(quote!(rtic::export::interrupt::enable();));
+
+ stmts
+}
diff --git a/rtic-macros/src/codegen/pre_init.rs b/rtic-macros/src/codegen/pre_init.rs
new file mode 100644
index 00000000..28ba29c0
--- /dev/null
+++ b/rtic-macros/src/codegen/pre_init.rs
@@ -0,0 +1,85 @@
+use crate::syntax::ast::App;
+use proc_macro2::TokenStream as TokenStream2;
+use quote::quote;
+
+use crate::{analyze::Analysis, codegen::util};
+
+/// Generates code that runs before `#[init]`
+pub fn codegen(app: &App, analysis: &Analysis) -> Vec<TokenStream2> {
+ let mut stmts = vec![];
+
+ let rt_err = util::rt_err_ident();
+
+ // Disable interrupts -- `init` must run with interrupts disabled
+ stmts.push(quote!(rtic::export::interrupt::disable();));
+
+ stmts.push(quote!(
+ // To set the variable in cortex_m so the peripherals cannot be taken multiple times
+ let mut core: rtic::export::Peripherals = rtic::export::Peripherals::steal().into();
+ ));
+
+ 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.dispatchers.keys() {
+ stmts.push(quote!(let _ = #rt_err::#interrupt::#name;));
+ }
+
+ let interrupt_ids = analysis.interrupts.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| {
+ if util::is_exception(&task.args.binds) {
+ // We do exceptions in another pass
+ None
+ } else {
+ Some((&task.args.priority, &task.args.binds))
+ }
+ })) {
+ let es = format!(
+ "Maximum priority used by interrupt vector '{name}' is more than supported by hardware"
+ );
+ // Compile time assert that this priority is supported by the device
+ stmts.push(quote!(
+ const _: () = if (1 << #nvic_prio_bits) < #priority as usize { ::core::panic!(#es); };
+ ));
+
+ stmts.push(quote!(
+ core.NVIC.set_priority(
+ #rt_err::#interrupt::#name,
+ rtic::export::logical2hw(#priority, #nvic_prio_bits),
+ );
+ ));
+
+ // NOTE unmask the interrupt *after* setting its priority: changing the priority of a pended
+ // interrupt is implementation defined
+ stmts.push(quote!(rtic::export::NVIC::unmask(#rt_err::#interrupt::#name);));
+ }
+
+ // Set exception priorities
+ for (name, priority) in app.hardware_tasks.values().filter_map(|task| {
+ if util::is_exception(&task.args.binds) {
+ Some((&task.args.binds, task.args.priority))
+ } else {
+ None
+ }
+ }) {
+ let es = format!(
+ "Maximum priority used by interrupt vector '{name}' is more than supported by hardware"
+ );
+ // Compile time assert that this priority is supported by the device
+ stmts.push(quote!(
+ const _: () = if (1 << #nvic_prio_bits) < #priority as usize { ::core::panic!(#es); };
+ ));
+
+ stmts.push(quote!(core.SCB.set_priority(
+ rtic::export::SystemHandler::#name,
+ rtic::export::logical2hw(#priority, #nvic_prio_bits),
+ );));
+ }
+
+ stmts
+}
diff --git a/rtic-macros/src/codegen/shared_resources.rs b/rtic-macros/src/codegen/shared_resources.rs
new file mode 100644
index 00000000..19fd13fe
--- /dev/null
+++ b/rtic-macros/src/codegen/shared_resources.rs
@@ -0,0 +1,183 @@
+use crate::syntax::{analyze::Ownership, ast::App};
+use crate::{analyze::Analysis, codegen::util};
+use proc_macro2::TokenStream as TokenStream2;
+use quote::quote;
+use std::collections::HashMap;
+
+/// Generates `static` variables and shared resource proxies
+pub fn codegen(app: &App, analysis: &Analysis) -> TokenStream2 {
+ let mut mod_app = vec![];
+ let mut mod_resources = vec![];
+
+ for (name, res) in &app.shared_resources {
+ let cfgs = &res.cfgs;
+ let ty = &res.ty;
+ let mangled_name = &util::static_shared_resource_ident(name);
+
+ let attrs = &res.attrs;
+
+ // late resources in `util::link_section_uninit`
+ // unless user specifies custom link section
+ let section = if attrs.iter().any(|attr| attr.path.is_ident("link_section")) {
+ None
+ } else {
+ Some(util::link_section_uninit())
+ };
+
+ // For future use
+ // let doc = format!(" RTIC internal: {}:{}", file!(), line!());
+ mod_app.push(quote!(
+ #[allow(non_camel_case_types)]
+ #[allow(non_upper_case_globals)]
+ // #[doc = #doc]
+ #[doc(hidden)]
+ #(#attrs)*
+ #(#cfgs)*
+ #section
+ static #mangled_name: rtic::RacyCell<core::mem::MaybeUninit<#ty>> = rtic::RacyCell::new(core::mem::MaybeUninit::uninit());
+ ));
+
+ // For future use
+ // let doc = format!(" RTIC internal: {}:{}", file!(), line!());
+
+ let shared_name = util::need_to_lock_ident(name);
+
+ if !res.properties.lock_free {
+ mod_resources.push(quote!(
+ // #[doc = #doc]
+ #[doc(hidden)]
+ #[allow(non_camel_case_types)]
+ #(#cfgs)*
+ pub struct #shared_name<'a> {
+ __rtic_internal_p: ::core::marker::PhantomData<&'a ()>,
+ }
+
+ #(#cfgs)*
+ impl<'a> #shared_name<'a> {
+ #[inline(always)]
+ pub unsafe fn new() -> Self {
+ #shared_name { __rtic_internal_p: ::core::marker::PhantomData }
+ }
+ }
+ ));
+
+ let ptr = quote!(
+ #(#cfgs)*
+ #mangled_name.get_mut() as *mut _
+ );
+
+ let ceiling = match analysis.ownerships.get(name) {
+ Some(Ownership::Owned { priority } | Ownership::CoOwned { priority }) => *priority,
+ Some(Ownership::Contended { ceiling }) => *ceiling,
+ None => 0,
+ };
+
+ // For future use
+ // let doc = format!(" RTIC internal ({} resource): {}:{}", doc, file!(), line!());
+
+ mod_app.push(util::impl_mutex(
+ app,
+ cfgs,
+ true,
+ &shared_name,
+ &quote!(#ty),
+ ceiling,
+ &ptr,
+ ));
+ }
+ }
+
+ let mod_resources = if mod_resources.is_empty() {
+ quote!()
+ } else {
+ quote!(mod shared_resources {
+ #(#mod_resources)*
+ })
+ };
+
+ // Computing mapping of used interrupts to masks
+ let interrupt_ids = analysis.interrupts.iter().map(|(p, (id, _))| (p, id));
+
+ let mut prio_to_masks = HashMap::new();
+ let device = &app.args.device;
+ let mut uses_exceptions_with_resources = false;
+
+ let mut mask_ids = Vec::new();
+
+ for (&priority, name) in interrupt_ids.chain(app.hardware_tasks.values().flat_map(|task| {
+ if !util::is_exception(&task.args.binds) {
+ Some((&task.args.priority, &task.args.binds))
+ } else {
+ // If any resource to the exception uses non-lock-free or non-local resources this is
+ // not allwed on thumbv6.
+ uses_exceptions_with_resources = uses_exceptions_with_resources
+ || task
+ .args
+ .shared_resources
+ .iter()
+ .map(|(ident, access)| {
+ if access.is_exclusive() {
+ if let Some(r) = app.shared_resources.get(ident) {
+ !r.properties.lock_free
+ } else {
+ false
+ }
+ } else {
+ false
+ }
+ })
+ .any(|v| v);
+
+ None
+ }
+ })) {
+ 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));
+ }
+
+ // Call rtic::export::create_mask([Mask; N]), where the array is the list of shifts
+
+ let mut mask_arr = Vec::new();
+ // NOTE: 0..3 assumes max 4 priority levels according to M0, M23 spec
+ for i in 0..3 {
+ let v = if let Some(v) = prio_to_masks.get(&i) {
+ v.clone()
+ } else {
+ Vec::new()
+ };
+
+ mask_arr.push(quote!(
+ rtic::export::create_mask([#(#v),*])
+ ));
+ }
+
+ // Generate a constant for the number of chunks needed by Mask.
+ let chunks_name = util::priority_mask_chunks_ident();
+ mod_app.push(quote!(
+ #[doc(hidden)]
+ #[allow(non_upper_case_globals)]
+ const #chunks_name: usize = rtic::export::compute_mask_chunks([#(#mask_ids),*]);
+ ));
+
+ let masks_name = util::priority_masks_ident();
+ mod_app.push(quote!(
+ #[doc(hidden)]
+ #[allow(non_upper_case_globals)]
+ const #masks_name: [rtic::export::Mask<#chunks_name>; 3] = [#(#mask_arr),*];
+ ));
+
+ if uses_exceptions_with_resources {
+ mod_app.push(quote!(
+ #[doc(hidden)]
+ #[allow(non_upper_case_globals)]
+ const __rtic_internal_V6_ERROR: () = rtic::export::no_basepri_panic();
+ ));
+ }
+
+ quote!(
+ #(#mod_app)*
+
+ #mod_resources
+ )
+}
diff --git a/rtic-macros/src/codegen/shared_resources_struct.rs b/rtic-macros/src/codegen/shared_resources_struct.rs
new file mode 100644
index 00000000..fa6f0fcb
--- /dev/null
+++ b/rtic-macros/src/codegen/shared_resources_struct.rs
@@ -0,0 +1,119 @@
+use crate::syntax::{ast::App, Context};
+use proc_macro2::TokenStream as TokenStream2;
+use quote::quote;
+
+use crate::codegen::util;
+
+/// Generate shared resources structs
+pub fn codegen(ctxt: Context, app: &App) -> (TokenStream2, TokenStream2) {
+ let resources = match ctxt {
+ Context::Init => unreachable!("Tried to generate shared resources struct for init"),
+ 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 mut fields = vec![];
+ let mut values = vec![];
+
+ for (name, access) in resources {
+ let res = app.shared_resources.get(name).expect("UNREACHABLE");
+
+ let cfgs = &res.cfgs;
+
+ // access hold if the resource is [x] (exclusive) or [&x] (shared)
+ let mut_ = if access.is_exclusive() {
+ Some(quote!(mut))
+ } else {
+ None
+ };
+ let ty = &res.ty;
+ let mangled_name = util::static_shared_resource_ident(name);
+ let shared_name = util::need_to_lock_ident(name);
+
+ if res.properties.lock_free {
+ // Lock free resources of `idle` and `init` get 'static lifetime
+ let lt = if ctxt.runs_once() {
+ quote!('static)
+ } else {
+ quote!('a)
+ };
+
+ fields.push(quote!(
+ #(#cfgs)*
+ #[allow(missing_docs)]
+ pub #name: &#lt #mut_ #ty
+ ));
+ } else if access.is_shared() {
+ fields.push(quote!(
+ #(#cfgs)*
+ #[allow(missing_docs)]
+ pub #name: &'a #ty
+ ));
+ } else {
+ fields.push(quote!(
+ #(#cfgs)*
+ #[allow(missing_docs)]
+ pub #name: shared_resources::#shared_name<'a>
+ ));
+
+ values.push(quote!(
+ #(#cfgs)*
+ #name: shared_resources::#shared_name::new()
+
+ ));
+
+ // continue as the value has been filled,
+ continue;
+ }
+
+ let expr = if access.is_exclusive() {
+ quote!(&mut *(&mut *#mangled_name.get_mut()).as_mut_ptr())
+ } else {
+ quote!(&*(&*#mangled_name.get()).as_ptr())
+ };
+
+ values.push(quote!(
+ #(#cfgs)*
+ #name: #expr
+ ));
+ }
+
+ fields.push(quote!(
+ #[doc(hidden)]
+ pub __rtic_internal_marker: core::marker::PhantomData<&'a ()>
+ ));
+
+ values.push(quote!(__rtic_internal_marker: core::marker::PhantomData));
+
+ 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]
+ pub struct #ident<'a> {
+ #(#fields,)*
+ }
+ );
+
+ let constructor = quote!(
+ impl<'a> #ident<'a> {
+ #[inline(always)]
+ #[allow(missing_docs)]
+ pub unsafe fn new() -> Self {
+ #ident {
+ #(#values,)*
+ }
+ }
+ }
+ );
+
+ (item, constructor)
+}
diff --git a/rtic-macros/src/codegen/software_tasks.rs b/rtic-macros/src/codegen/software_tasks.rs
new file mode 100644
index 00000000..34fc851a
--- /dev/null
+++ b/rtic-macros/src/codegen/software_tasks.rs
@@ -0,0 +1,64 @@
+use crate::syntax::{ast::App, Context};
+use crate::{
+ analyze::Analysis,
+ codegen::{local_resources_struct, module, shared_resources_struct},
+};
+use proc_macro2::TokenStream as TokenStream2;
+use quote::quote;
+
+pub fn codegen(app: &App, analysis: &Analysis) -> TokenStream2 {
+ let mut mod_app = vec![];
+ let mut root = vec![];
+ let mut user_tasks = vec![];
+
+ // Any task
+ for (name, task) in app.software_tasks.iter() {
+ if !task.args.local_resources.is_empty() {
+ let (item, constructor) =
+ local_resources_struct::codegen(Context::SoftwareTask(name), app);
+
+ root.push(item);
+
+ mod_app.push(constructor);
+ }
+
+ if !task.args.shared_resources.is_empty() {
+ let (item, constructor) =
+ shared_resources_struct::codegen(Context::SoftwareTask(name), app);
+
+ root.push(item);
+
+ mod_app.push(constructor);
+ }
+
+ if !&task.is_extern {
+ let context = &task.context;
+ let attrs = &task.attrs;
+ let cfgs = &task.cfgs;
+ let stmts = &task.stmts;
+ let inputs = &task.inputs;
+
+ user_tasks.push(quote!(
+ #(#attrs)*
+ #(#cfgs)*
+ #[allow(non_snake_case)]
+ async fn #name<'a>(#context: #name::Context<'a> #(,#inputs)*) {
+ use rtic::Mutex as _;
+ use rtic::mutex::prelude::*;
+
+ #(#stmts)*
+ }
+ ));
+ }
+
+ root.push(module::codegen(Context::SoftwareTask(name), app, analysis));
+ }
+
+ quote!(
+ #(#mod_app)*
+
+ #(#root)*
+
+ #(#user_tasks)*
+ )
+}
diff --git a/rtic-macros/src/codegen/util.rs b/rtic-macros/src/codegen/util.rs
new file mode 100644
index 00000000..d0c8cc0e
--- /dev/null
+++ b/rtic-macros/src/codegen/util.rs
@@ -0,0 +1,238 @@
+use crate::syntax::{ast::App, Context};
+use core::sync::atomic::{AtomicUsize, Ordering};
+use proc_macro2::{Span, TokenStream as TokenStream2};
+use quote::quote;
+use syn::{Attribute, Ident, PatType};
+
+const RTIC_INTERNAL: &str = "__rtic_internal";
+
+/// Generates a `Mutex` implementation
+pub fn impl_mutex(
+ app: &App,
+ cfgs: &[Attribute],
+ resources_prefix: bool,
+ name: &Ident,
+ ty: &TokenStream2,
+ ceiling: u8,
+ ptr: &TokenStream2,
+) -> TokenStream2 {
+ let path = if resources_prefix {
+ quote!(shared_resources::#name)
+ } else {
+ quote!(#name)
+ };
+
+ let device = &app.args.device;
+ let masks_name = priority_masks_ident();
+ quote!(
+ #(#cfgs)*
+ impl<'a> rtic::Mutex for #path<'a> {
+ type T = #ty;
+
+ #[inline(always)]
+ fn lock<RTIC_INTERNAL_R>(&mut self, f: impl FnOnce(&mut #ty) -> RTIC_INTERNAL_R) -> RTIC_INTERNAL_R {
+ /// Priority ceiling
+ const CEILING: u8 = #ceiling;
+
+ unsafe {
+ rtic::export::lock(
+ #ptr,
+ CEILING,
+ #device::NVIC_PRIO_BITS,
+ &#masks_name,
+ f,
+ )
+ }
+ }
+ }
+ )
+}
+
+pub fn interrupt_ident() -> Ident {
+ let span = Span::call_site();
+ Ident::new("interrupt", span)
+}
+
+/// Whether `name` is an exception with configurable priority
+pub fn is_exception(name: &Ident) -> bool {
+ let s = name.to_string();
+
+ matches!(
+ &*s,
+ "MemoryManagement"
+ | "BusFault"
+ | "UsageFault"
+ | "SecureFault"
+ | "SVCall"
+ | "DebugMonitor"
+ | "PendSV"
+ | "SysTick"
+ )
+}
+
+/// Mark a name as internal
+pub fn mark_internal_name(name: &str) -> Ident {
+ Ident::new(&format!("{RTIC_INTERNAL}_{name}"), Span::call_site())
+}
+
+/// Generate an internal identifier for tasks
+pub fn internal_task_ident(task: &Ident, ident_name: &str) -> Ident {
+ mark_internal_name(&format!("{task}_{ident_name}"))
+}
+
+fn link_section_index() -> usize {
+ static INDEX: AtomicUsize = AtomicUsize::new(0);
+
+ INDEX.fetch_add(1, Ordering::Relaxed)
+}
+
+/// Add `link_section` attribute
+pub fn link_section_uninit() -> TokenStream2 {
+ let section = format!(".uninit.rtic{}", link_section_index());
+
+ quote!(#[link_section = #section])
+}
+
+/// Regroups the inputs of a task
+///
+/// `inputs` could be &[`input: Foo`] OR &[`mut x: i32`, `ref y: i64`]
+pub fn regroup_inputs(
+ inputs: &[PatType],
+) -> (
+ // args e.g. &[`_0`], &[`_0: i32`, `_1: i64`]
+ Vec<TokenStream2>,
+ // tupled e.g. `_0`, `(_0, _1)`
+ TokenStream2,
+ // untupled e.g. &[`_0`], &[`_0`, `_1`]
+ Vec<TokenStream2>,
+ // ty e.g. `Foo`, `(i32, i64)`
+ TokenStream2,
+) {
+ if inputs.len() == 1 {
+ let ty = &inputs[0].ty;
+
+ (
+ vec![quote!(_0: #ty)],
+ quote!(_0),
+ vec![quote!(_0)],
+ quote!(#ty),
+ )
+ } else {
+ let mut args = vec![];
+ let mut pats = vec![];
+ let mut tys = vec![];
+
+ for (i, input) in inputs.iter().enumerate() {
+ let i = Ident::new(&format!("_{i}"), Span::call_site());
+ let ty = &input.ty;
+
+ args.push(quote!(#i: #ty));
+
+ pats.push(quote!(#i));
+
+ tys.push(quote!(#ty));
+ }
+
+ let tupled = {
+ let pats = pats.clone();
+ quote!((#(#pats,)*))
+ };
+ let ty = quote!((#(#tys,)*));
+ (args, tupled, pats, ty)
+ }
+}
+
+/// Get the ident for the name of the task
+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()
+ .expect("RTIC-ICE: unable to find idle name")
+ .name
+ .to_string(),
+ Context::HardwareTask(ident) | Context::SoftwareTask(ident) => ident.to_string(),
+ };
+
+ Ident::new(&s, Span::call_site())
+}
+
+/// Generates a pre-reexport identifier for the "shared resources" struct
+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()
+ .expect("RTIC-ICE: unable to find idle name")
+ .name
+ .to_string(),
+ Context::HardwareTask(ident) | Context::SoftwareTask(ident) => ident.to_string(),
+ };
+
+ s.push_str("SharedResources");
+
+ mark_internal_name(&s)
+}
+
+/// Generates a pre-reexport identifier for the "local resources" struct
+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()
+ .expect("RTIC-ICE: unable to find idle name")
+ .name
+ .to_string(),
+ Context::HardwareTask(ident) | Context::SoftwareTask(ident) => ident.to_string(),
+ };
+
+ s.push_str("LocalResources");
+
+ mark_internal_name(&s)
+}
+
+/// Suffixed identifier
+pub fn suffixed(name: &str) -> Ident {
+ let span = Span::call_site();
+ Ident::new(name, span)
+}
+
+pub fn static_shared_resource_ident(name: &Ident) -> Ident {
+ mark_internal_name(&format!("shared_resource_{name}"))
+}
+
+/// Generates an Ident for the number of 32 bit chunks used for Mask storage.
+pub fn priority_mask_chunks_ident() -> Ident {
+ mark_internal_name("MASK_CHUNKS")
+}
+
+pub fn priority_masks_ident() -> Ident {
+ mark_internal_name("MASKS")
+}
+
+pub fn static_local_resource_ident(name: &Ident) -> Ident {
+ mark_internal_name(&format!("local_resource_{name}"))
+}
+
+pub fn declared_static_local_resource_ident(name: &Ident, task_name: &Ident) -> Ident {
+ mark_internal_name(&format!("local_{task_name}_{name}"))
+}
+
+pub fn need_to_lock_ident(name: &Ident) -> Ident {
+ Ident::new(&format!("{name}_that_needs_to_be_locked"), name.span())
+}
+
+pub fn zero_prio_dispatcher_ident() -> Ident {
+ Ident::new("__rtic_internal_async_0_prio_dispatcher", Span::call_site())
+}
+
+/// The name to get better RT flag errors
+pub fn rt_err_ident() -> Ident {
+ Ident::new(
+ "you_must_enable_the_rt_feature_for_the_pac_in_your_cargo_toml",
+ Span::call_site(),
+ )
+}