diff options
author | 2023-01-23 20:05:47 +0100 | |
---|---|---|
committer | 2023-03-01 00:33:31 +0100 | |
commit | 306aa47170fd59369b7a184924e287dc3706d64d (patch) | |
tree | 75a331a63a4021f078e330bf2ce4edb1228e2ecf /macros/src/codegen | |
parent | b8b881f446a226d6f3c4a7db7c9174590b47dbf6 (diff) | |
download | rtic-306aa47170fd59369b7a184924e287dc3706d64d.tar.gz rtic-306aa47170fd59369b7a184924e287dc3706d64d.tar.zst rtic-306aa47170fd59369b7a184924e287dc3706d64d.zip |
Add rtic-timer (timerqueue + monotonic) and rtic-monotonics (systick-monotonic)
Diffstat (limited to 'macros/src/codegen')
-rw-r--r-- | macros/src/codegen/assertions.rs | 53 | ||||
-rw-r--r-- | macros/src/codegen/async_dispatchers.rs | 89 | ||||
-rw-r--r-- | macros/src/codegen/hardware_tasks.rs | 87 | ||||
-rw-r--r-- | macros/src/codegen/idle.rs | 58 | ||||
-rw-r--r-- | macros/src/codegen/init.rs | 95 | ||||
-rw-r--r-- | macros/src/codegen/local_resources.rs | 65 | ||||
-rw-r--r-- | macros/src/codegen/local_resources_struct.rs | 102 | ||||
-rw-r--r-- | macros/src/codegen/main.rs | 52 | ||||
-rw-r--r-- | macros/src/codegen/module.rs | 194 | ||||
-rw-r--r-- | macros/src/codegen/post_init.rs | 47 | ||||
-rw-r--r-- | macros/src/codegen/pre_init.rs | 85 | ||||
-rw-r--r-- | macros/src/codegen/shared_resources.rs | 183 | ||||
-rw-r--r-- | macros/src/codegen/shared_resources_struct.rs | 119 | ||||
-rw-r--r-- | macros/src/codegen/software_tasks.rs | 64 | ||||
-rw-r--r-- | macros/src/codegen/util.rs | 238 |
15 files changed, 0 insertions, 1531 deletions
diff --git a/macros/src/codegen/assertions.rs b/macros/src/codegen/assertions.rs deleted file mode 100644 index dd94aa6d..00000000 --- a/macros/src/codegen/assertions.rs +++ /dev/null @@ -1,53 +0,0 @@ -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/macros/src/codegen/async_dispatchers.rs b/macros/src/codegen/async_dispatchers.rs deleted file mode 100644 index a12ad325..00000000 --- a/macros/src/codegen/async_dispatchers.rs +++ /dev/null @@ -1,89 +0,0 @@ -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/macros/src/codegen/hardware_tasks.rs b/macros/src/codegen/hardware_tasks.rs deleted file mode 100644 index 8a5a8f6c..00000000 --- a/macros/src/codegen/hardware_tasks.rs +++ /dev/null @@ -1,87 +0,0 @@ -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/macros/src/codegen/idle.rs b/macros/src/codegen/idle.rs deleted file mode 100644 index 0c833ef3..00000000 --- a/macros/src/codegen/idle.rs +++ /dev/null @@ -1,58 +0,0 @@ -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/macros/src/codegen/init.rs b/macros/src/codegen/init.rs deleted file mode 100644 index 6e1059f7..00000000 --- a/macros/src/codegen/init.rs +++ /dev/null @@ -1,95 +0,0 @@ -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/macros/src/codegen/local_resources.rs b/macros/src/codegen/local_resources.rs deleted file mode 100644 index e6d15533..00000000 --- a/macros/src/codegen/local_resources.rs +++ /dev/null @@ -1,65 +0,0 @@ -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/macros/src/codegen/local_resources_struct.rs b/macros/src/codegen/local_resources_struct.rs deleted file mode 100644 index 100c3eb5..00000000 --- a/macros/src/codegen/local_resources_struct.rs +++ /dev/null @@ -1,102 +0,0 @@ -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/macros/src/codegen/main.rs b/macros/src/codegen/main.rs deleted file mode 100644 index 2775d259..00000000 --- a/macros/src/codegen/main.rs +++ /dev/null @@ -1,52 +0,0 @@ -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/macros/src/codegen/module.rs b/macros/src/codegen/module.rs deleted file mode 100644 index 4725b9a9..00000000 --- a/macros/src/codegen/module.rs +++ /dev/null @@ -1,194 +0,0 @@ -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> { - - if #exec_name.spawn(|| #name(unsafe { #name::Context::new() } #(,#input_untupled)*) ) { - - #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/macros/src/codegen/post_init.rs b/macros/src/codegen/post_init.rs deleted file mode 100644 index c4e53837..00000000 --- a/macros/src/codegen/post_init.rs +++ /dev/null @@ -1,47 +0,0 @@ -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/macros/src/codegen/pre_init.rs b/macros/src/codegen/pre_init.rs deleted file mode 100644 index 28ba29c0..00000000 --- a/macros/src/codegen/pre_init.rs +++ /dev/null @@ -1,85 +0,0 @@ -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/macros/src/codegen/shared_resources.rs b/macros/src/codegen/shared_resources.rs deleted file mode 100644 index 19fd13fe..00000000 --- a/macros/src/codegen/shared_resources.rs +++ /dev/null @@ -1,183 +0,0 @@ -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, - "e!(#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/macros/src/codegen/shared_resources_struct.rs b/macros/src/codegen/shared_resources_struct.rs deleted file mode 100644 index fa6f0fcb..00000000 --- a/macros/src/codegen/shared_resources_struct.rs +++ /dev/null @@ -1,119 +0,0 @@ -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/macros/src/codegen/software_tasks.rs b/macros/src/codegen/software_tasks.rs deleted file mode 100644 index 34fc851a..00000000 --- a/macros/src/codegen/software_tasks.rs +++ /dev/null @@ -1,64 +0,0 @@ -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/macros/src/codegen/util.rs b/macros/src/codegen/util.rs deleted file mode 100644 index e121487c..00000000 --- a/macros/src/codegen/util.rs +++ /dev/null @@ -1,238 +0,0 @@ -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(), - ) -} |