From 9e445b3583c15c7701f3167eaa8dfe4afd541691 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20Tj=C3=A4der?= Date: Sat, 4 Feb 2023 16:47:17 +0100 Subject: Move rtic macros to repo root, tune xtask --- rtic-macros/src/codegen/assertions.rs | 53 +++++ rtic-macros/src/codegen/async_dispatchers.rs | 89 ++++++++ rtic-macros/src/codegen/hardware_tasks.rs | 87 ++++++++ rtic-macros/src/codegen/idle.rs | 58 +++++ rtic-macros/src/codegen/init.rs | 95 ++++++++ rtic-macros/src/codegen/local_resources.rs | 65 ++++++ rtic-macros/src/codegen/local_resources_struct.rs | 102 +++++++++ rtic-macros/src/codegen/main.rs | 52 +++++ rtic-macros/src/codegen/module.rs | 197 +++++++++++++++++ rtic-macros/src/codegen/post_init.rs | 47 ++++ rtic-macros/src/codegen/pre_init.rs | 85 ++++++++ rtic-macros/src/codegen/shared_resources.rs | 183 ++++++++++++++++ rtic-macros/src/codegen/shared_resources_struct.rs | 119 +++++++++++ rtic-macros/src/codegen/software_tasks.rs | 64 ++++++ rtic-macros/src/codegen/util.rs | 238 +++++++++++++++++++++ 15 files changed, 1534 insertions(+) create mode 100644 rtic-macros/src/codegen/assertions.rs create mode 100644 rtic-macros/src/codegen/async_dispatchers.rs create mode 100644 rtic-macros/src/codegen/hardware_tasks.rs create mode 100644 rtic-macros/src/codegen/idle.rs create mode 100644 rtic-macros/src/codegen/init.rs create mode 100644 rtic-macros/src/codegen/local_resources.rs create mode 100644 rtic-macros/src/codegen/local_resources_struct.rs create mode 100644 rtic-macros/src/codegen/main.rs create mode 100644 rtic-macros/src/codegen/module.rs create mode 100644 rtic-macros/src/codegen/post_init.rs create mode 100644 rtic-macros/src/codegen/pre_init.rs create mode 100644 rtic-macros/src/codegen/shared_resources.rs create mode 100644 rtic-macros/src/codegen/shared_resources_struct.rs create mode 100644 rtic-macros/src/codegen/software_tasks.rs create mode 100644 rtic-macros/src/codegen/util.rs (limited to 'rtic-macros/src/codegen') diff --git a/rtic-macros/src/codegen/assertions.rs b/rtic-macros/src/codegen/assertions.rs new file mode 100644 index 0000000..dd94aa6 --- /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 { + 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 0000000..a12ad32 --- /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 0000000..8a5a8f6 --- /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 0000000..0c833ef --- /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 0000000..6e1059f --- /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 0000000..e6d1553 --- /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> = 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 0000000..100c3eb --- /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 0000000..2775d25 --- /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) 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 0000000..8b3fca2 --- /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 0000000..c4e5383 --- /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 { + 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> + // - `get_mut` to obtain a raw pointer to `MaybeUninit` + // - `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> + // - `get_mut` to obtain a raw pointer to `MaybeUninit` + // - `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 0000000..28ba29c --- /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 { + 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 0000000..19fd13f --- /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> = 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/rtic-macros/src/codegen/shared_resources_struct.rs b/rtic-macros/src/codegen/shared_resources_struct.rs new file mode 100644 index 0000000..fa6f0fc --- /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 0000000..34fc851 --- /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 0000000..d0c8cc0 --- /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(&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, + // tupled e.g. `_0`, `(_0, _1)` + TokenStream2, + // untupled e.g. &[`_0`], &[`_0`, `_1`] + Vec, + // 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(), + ) +} -- cgit v1.2.3 From 60f0342b697cdddbab9c0e8c6d772bc7aab9de38 Mon Sep 17 00:00:00 2001 From: Emil Fresk Date: Sat, 11 Feb 2023 08:55:19 +0100 Subject: Break out core specific codegen to bindings --- rtic-macros/src/codegen/assertions.rs | 36 +-- rtic-macros/src/codegen/bindings.rs | 5 + rtic-macros/src/codegen/bindings/cortex.rs | 346 ++++++++++++++++++++++++++++ rtic-macros/src/codegen/module.rs | 4 +- rtic-macros/src/codegen/pre_init.rs | 69 +----- rtic-macros/src/codegen/shared_resources.rs | 86 +------ rtic-macros/src/codegen/util.rs | 68 ------ 7 files changed, 364 insertions(+), 250 deletions(-) create mode 100644 rtic-macros/src/codegen/bindings.rs create mode 100644 rtic-macros/src/codegen/bindings/cortex.rs (limited to 'rtic-macros/src/codegen') diff --git a/rtic-macros/src/codegen/assertions.rs b/rtic-macros/src/codegen/assertions.rs index dd94aa6..2f5dd52 100644 --- a/rtic-macros/src/codegen/assertions.rs +++ b/rtic-macros/src/codegen/assertions.rs @@ -1,8 +1,9 @@ use proc_macro2::TokenStream as TokenStream2; use quote::quote; +use super::bindings::extra_assertions; +use crate::analyze::Analysis; 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 { @@ -16,38 +17,7 @@ pub fn codegen(app: &App, analysis: &Analysis) -> Vec { 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.append(&mut extra_assertions(app, analysis)); stmts } diff --git a/rtic-macros/src/codegen/bindings.rs b/rtic-macros/src/codegen/bindings.rs new file mode 100644 index 0000000..1efe0ce --- /dev/null +++ b/rtic-macros/src/codegen/bindings.rs @@ -0,0 +1,5 @@ +// TODO: Feature gate +mod cortex; + +// TODO: Feature gate +pub use cortex::*; diff --git a/rtic-macros/src/codegen/bindings/cortex.rs b/rtic-macros/src/codegen/bindings/cortex.rs new file mode 100644 index 0000000..15976a1 --- /dev/null +++ b/rtic-macros/src/codegen/bindings/cortex.rs @@ -0,0 +1,346 @@ +use crate::{ + analyze::Analysis as CodegenAnalysis, + codegen::util, + syntax::{analyze::Analysis as SyntaxAnalysis, ast::App}, +}; +use proc_macro2::TokenStream as TokenStream2; +use quote::quote; +use std::collections::HashSet; +use syn::{parse, Attribute, Ident}; + +// TODO: This should be feature gated +// pub use basepri::*; +pub use source_masking::*; + +/// Whether `name` is an exception with configurable priority +fn is_exception(name: &Ident) -> bool { + let s = name.to_string(); + + matches!( + &*s, + "MemoryManagement" + | "BusFault" + | "UsageFault" + | "SecureFault" + | "SVCall" + | "DebugMonitor" + | "PendSV" + | "SysTick" + ) +} + +pub mod source_masking { + use super::*; + use std::collections::HashMap; + + /// Generates a `Mutex` implementation + pub fn impl_mutex( + app: &App, + analysis: &CodegenAnalysis, + 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) + }; + + // 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 !is_exception(&task.args.binds) { + Some((&task.args.priority, &task.args.binds)) + } else { + 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),*]) + )); + } + + // 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!( + #(#cfgs)* + impl<'a> rtic::Mutex for #path<'a> { + type T = #ty; + + #[inline(always)] + fn lock(&mut self, f: impl FnOnce(&mut #ty) -> RTIC_INTERNAL_R) -> RTIC_INTERNAL_R { + /// Priority ceiling + const CEILING: u8 = #ceiling; + const N_CHUNKS: usize = rtic::export::compute_mask_chunks([#(#mask_ids),*]); + const MASKS: [rtic::export::Mask; 3] = [#(#mask_arr),*]; + + unsafe { + rtic::export::lock( + #ptr, + CEILING, + &MASKS, + f, + ) + } + } + } + ) + } + + pub fn extra_assertions(_: &App, _: &SyntaxAnalysis) -> Vec { + // let device = &app.args.device; + // let no_basepri_checks: Vec<_> = app + // .hardware_tasks + // .iter() + // .filter_map(|(_, task)| { + // if !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: () = { + // #(#no_basepri_checks)* + // }; + + // let _ = _CONST_CHECK; + // }; + + // vec![const_check] + vec![] + } +} + +pub mod basepri { + use super::*; + + /// Generates a `Mutex` implementation + pub fn impl_mutex( + app: &App, + _analysis: &CodegenAnalysis, + 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; + quote!( + #(#cfgs)* + impl<'a> rtic::Mutex for #path<'a> { + type T = #ty; + + #[inline(always)] + fn lock(&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, + f, + ) + } + } + } + ) + } + + pub fn extra_assertions(_: &App, _: &SyntaxAnalysis) -> Vec { + vec![] + } +} + +pub fn pre_init_checks(app: &App, _: &SyntaxAnalysis) -> Vec { + let mut stmts = vec![]; + + // check that all dispatchers exists in the `Interrupt` enumeration regardless of whether + // they are used or not + let interrupt = util::interrupt_ident(); + let rt_err = util::rt_err_ident(); + + for name in app.args.dispatchers.keys() { + stmts.push(quote!(let _ = #rt_err::#interrupt::#name;)); + } + + stmts +} + +pub fn pre_init_enable_interrupts(app: &App, analysis: &CodegenAnalysis) -> Vec { + let mut stmts = vec![]; + + let interrupt = util::interrupt_ident(); + let rt_err = util::rt_err_ident(); + let device = &app.args.device; + let nvic_prio_bits = quote!(#device::NVIC_PRIO_BITS); + 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 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 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 +} + +pub fn architecture_specific_analysis(app: &App, _: &SyntaxAnalysis) -> parse::Result<()> { + // Check that external (device-specific) interrupts are not named after known (Cortex-M) + // exceptions + for name in app.args.dispatchers.keys() { + let name_s = name.to_string(); + + match &*name_s { + "NonMaskableInt" | "HardFault" | "MemoryManagement" | "BusFault" | "UsageFault" + | "SecureFault" | "SVCall" | "DebugMonitor" | "PendSV" | "SysTick" => { + return Err(parse::Error::new( + name.span(), + "Cortex-M exceptions can't be used as `extern` interrupts", + )); + } + + _ => {} + } + } + + // Check that there are enough external interrupts to dispatch the software tasks and the timer + // queue handler + let mut first = None; + let priorities = app + .software_tasks + .iter() + .map(|(name, task)| { + first = Some(name); + task.args.priority + }) + .filter(|prio| *prio > 0) + .collect::>(); + + let need = priorities.len(); + let given = app.args.dispatchers.len(); + if need > given { + let s = { + format!( + "not enough interrupts to dispatch \ + all software tasks (need: {need}; given: {given})" + ) + }; + + // If not enough tasks and first still is None, may cause + // "custom attribute panicked" due to unwrap on None + return Err(parse::Error::new(first.unwrap().span(), s)); + } + + // Check that all exceptions are valid; only exceptions with configurable priorities are + // accepted + for (name, task) in &app.hardware_tasks { + let name_s = task.args.binds.to_string(); + match &*name_s { + "NonMaskableInt" | "HardFault" => { + return Err(parse::Error::new( + name.span(), + "only exceptions with configurable priority can be used as hardware tasks", + )); + } + + _ => {} + } + } + + Ok(()) +} diff --git a/rtic-macros/src/codegen/module.rs b/rtic-macros/src/codegen/module.rs index 8b3fca2..af4e034 100644 --- a/rtic-macros/src/codegen/module.rs +++ b/rtic-macros/src/codegen/module.rs @@ -17,7 +17,7 @@ pub fn codegen(ctxt: Context, app: &App, analysis: &Analysis) -> TokenStream2 { match ctxt { Context::Init => { fields.push(quote!( - /// Core (Cortex-M) peripherals + /// Core peripherals pub core: rtic::export::Peripherals )); @@ -25,7 +25,7 @@ pub fn codegen(ctxt: Context, app: &App, analysis: &Analysis) -> TokenStream2 { let device = &app.args.device; fields.push(quote!( - /// Device peripherals + /// Device peripherals (PAC) pub device: #device::Peripherals )); diff --git a/rtic-macros/src/codegen/pre_init.rs b/rtic-macros/src/codegen/pre_init.rs index 28ba29c..a2d0e8c 100644 --- a/rtic-macros/src/codegen/pre_init.rs +++ b/rtic-macros/src/codegen/pre_init.rs @@ -1,15 +1,13 @@ +use super::bindings::{pre_init_checks, pre_init_enable_interrupts}; +use crate::analyze::Analysis; 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 { 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();)); @@ -18,68 +16,9 @@ pub fn codegen(app: &App, analysis: &Analysis) -> Vec { 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.append(&mut pre_init_checks(app, analysis)); - stmts.push(quote!(core.SCB.set_priority( - rtic::export::SystemHandler::#name, - rtic::export::logical2hw(#priority, #nvic_prio_bits), - );)); - } + stmts.append(&mut pre_init_enable_interrupts(app, analysis)); stmts } diff --git a/rtic-macros/src/codegen/shared_resources.rs b/rtic-macros/src/codegen/shared_resources.rs index 19fd13f..686c280 100644 --- a/rtic-macros/src/codegen/shared_resources.rs +++ b/rtic-macros/src/codegen/shared_resources.rs @@ -2,7 +2,8 @@ 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; + +use super::bindings::impl_mutex; /// Generates `static` variables and shared resource proxies pub fn codegen(app: &App, analysis: &Analysis) -> TokenStream2 { @@ -75,8 +76,9 @@ pub fn codegen(app: &App, analysis: &Analysis) -> TokenStream2 { // For future use // let doc = format!(" RTIC internal ({} resource): {}:{}", doc, file!(), line!()); - mod_app.push(util::impl_mutex( + mod_app.push(impl_mutex( app, + analysis, cfgs, true, &shared_name, @@ -95,86 +97,6 @@ pub fn codegen(app: &App, analysis: &Analysis) -> TokenStream2 { }) }; - // 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)* diff --git a/rtic-macros/src/codegen/util.rs b/rtic-macros/src/codegen/util.rs index d0c8cc0..27c8a2a 100644 --- a/rtic-macros/src/codegen/util.rs +++ b/rtic-macros/src/codegen/util.rs @@ -6,70 +6,11 @@ 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(&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()) @@ -204,15 +145,6 @@ 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}")) } -- cgit v1.2.3 From b9e0f36aff96ec4e39cf4f728777cbc808df2c78 Mon Sep 17 00:00:00 2001 From: Emil Fresk Date: Sun, 19 Feb 2023 14:30:49 +0100 Subject: Add feature flags --- rtic-macros/src/codegen/bindings.rs | 19 ++++++++++-- rtic-macros/src/codegen/bindings/cortex.rs | 50 ++++++------------------------ rtic-macros/src/codegen/util.rs | 2 +- 3 files changed, 26 insertions(+), 45 deletions(-) (limited to 'rtic-macros/src/codegen') diff --git a/rtic-macros/src/codegen/bindings.rs b/rtic-macros/src/codegen/bindings.rs index 1efe0ce..a187820 100644 --- a/rtic-macros/src/codegen/bindings.rs +++ b/rtic-macros/src/codegen/bindings.rs @@ -1,5 +1,18 @@ -// TODO: Feature gate -mod cortex; +#[cfg(not(any( + feature = "cortex_m_source_masking", + feature = "cortex_m_basepri", + feaute = "test_template" +)))] +compile_error!("No backend selected"); + +#[cfg(any(feature = "cortex_m_source_masking", feature = "cortex_m_basepri"))] +pub use cortex::*; -// TODO: Feature gate +#[cfg(feature = "test_template")] pub use cortex::*; + +#[cfg(any(feature = "cortex_m_source_masking", feature = "cortex_m_basepri"))] +mod cortex; + +#[cfg(feature = "test_template")] +mod template; diff --git a/rtic-macros/src/codegen/bindings/cortex.rs b/rtic-macros/src/codegen/bindings/cortex.rs index 15976a1..f028cee 100644 --- a/rtic-macros/src/codegen/bindings/cortex.rs +++ b/rtic-macros/src/codegen/bindings/cortex.rs @@ -8,8 +8,9 @@ use quote::quote; use std::collections::HashSet; use syn::{parse, Attribute, Ident}; -// TODO: This should be feature gated -// pub use basepri::*; +#[cfg(feature = "cortex_m_basepri")] +pub use basepri::*; +#[cfg(feature = "cortex_m_source_masking")] pub use source_masking::*; /// Whether `name` is an exception with configurable priority @@ -29,7 +30,8 @@ fn is_exception(name: &Ident) -> bool { ) } -pub mod source_masking { +#[cfg(feature = "cortex_m_source_masking")] +mod source_masking { use super::*; use std::collections::HashMap; @@ -87,14 +89,6 @@ pub mod source_masking { )); } - // 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!( #(#cfgs)* impl<'a> rtic::Mutex for #path<'a> { @@ -121,38 +115,12 @@ pub mod source_masking { } pub fn extra_assertions(_: &App, _: &SyntaxAnalysis) -> Vec { - // let device = &app.args.device; - // let no_basepri_checks: Vec<_> = app - // .hardware_tasks - // .iter() - // .filter_map(|(_, task)| { - // if !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: () = { - // #(#no_basepri_checks)* - // }; - - // let _ = _CONST_CHECK; - // }; - - // vec![const_check] vec![] } } -pub mod basepri { +#[cfg(feature = "cortex_m_basepri")] +mod basepri { use super::*; /// Generates a `Mutex` implementation @@ -245,7 +213,7 @@ pub fn pre_init_enable_interrupts(app: &App, analysis: &CodegenAnalysis) -> Vec< stmts.push(quote!( core.NVIC.set_priority( #rt_err::#interrupt::#name, - rtic::export::logical2hw(#priority, #nvic_prio_bits), + rtic::export::cortex_logical2hw(#priority, #nvic_prio_bits), ); )); @@ -272,7 +240,7 @@ pub fn pre_init_enable_interrupts(app: &App, analysis: &CodegenAnalysis) -> Vec< stmts.push(quote!(core.SCB.set_priority( rtic::export::SystemHandler::#name, - rtic::export::logical2hw(#priority, #nvic_prio_bits), + rtic::export::cortex_logical2hw(#priority, #nvic_prio_bits), );)); } diff --git a/rtic-macros/src/codegen/util.rs b/rtic-macros/src/codegen/util.rs index 27c8a2a..2f44edb 100644 --- a/rtic-macros/src/codegen/util.rs +++ b/rtic-macros/src/codegen/util.rs @@ -2,7 +2,7 @@ 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}; +use syn::{Ident, PatType}; const RTIC_INTERNAL: &str = "__rtic_internal"; -- cgit v1.2.3 From e8cebff69e3fd78ade613829c10dd2328495fa44 Mon Sep 17 00:00:00 2001 From: Emil Fresk Date: Mon, 20 Feb 2023 10:49:09 +0100 Subject: Added support for adding codegen to intrrupt entry and exit (needed for RISC-V) --- rtic-macros/src/codegen/async_dispatchers.rs | 15 ++++++++++++++- rtic-macros/src/codegen/bindings/cortex.rs | 8 ++++++++ rtic-macros/src/codegen/hardware_tasks.rs | 11 ++++++++++- 3 files changed, 32 insertions(+), 2 deletions(-) (limited to 'rtic-macros/src/codegen') diff --git a/rtic-macros/src/codegen/async_dispatchers.rs b/rtic-macros/src/codegen/async_dispatchers.rs index a12ad32..bdbfeaa 100644 --- a/rtic-macros/src/codegen/async_dispatchers.rs +++ b/rtic-macros/src/codegen/async_dispatchers.rs @@ -1,5 +1,11 @@ use crate::syntax::ast::App; -use crate::{analyze::Analysis, codegen::util}; +use crate::{ + analyze::Analysis, + codegen::{ + bindings::{interrupt_entry, interrupt_exit}, + util, + }, +}; use proc_macro2::TokenStream as TokenStream2; use quote::quote; @@ -59,18 +65,25 @@ pub fn codegen(app: &App, analysis: &Analysis) -> TokenStream2 { if level > 0 { let doc = format!("Interrupt handler to dispatch async tasks at priority {level}"); let attribute = &interrupts.get(&level).expect("UNREACHABLE").1.attrs; + let entry_stmts = interrupt_entry(app, analysis); + let exit_stmts = interrupt_exit(app, analysis); + items.push(quote!( #[allow(non_snake_case)] #[doc = #doc] #[no_mangle] #(#attribute)* unsafe fn #dispatcher_name() { + #(#entry_stmts)* + /// The priority of this interrupt handler const PRIORITY: u8 = #level; rtic::export::run(PRIORITY, || { #(#stmts)* }); + + #(#exit_stmts)* } )); } else { diff --git a/rtic-macros/src/codegen/bindings/cortex.rs b/rtic-macros/src/codegen/bindings/cortex.rs index f028cee..d5eb12e 100644 --- a/rtic-macros/src/codegen/bindings/cortex.rs +++ b/rtic-macros/src/codegen/bindings/cortex.rs @@ -312,3 +312,11 @@ pub fn architecture_specific_analysis(app: &App, _: &SyntaxAnalysis) -> parse::R Ok(()) } + +pub fn interrupt_entry(_app: &App, _analysis: &CodegenAnalysis) -> Vec { + vec![] +} + +pub fn interrupt_exit(_app: &App, _analysis: &CodegenAnalysis) -> Vec { + vec![] +} diff --git a/rtic-macros/src/codegen/hardware_tasks.rs b/rtic-macros/src/codegen/hardware_tasks.rs index 8a5a8f6..2c5254e 100644 --- a/rtic-macros/src/codegen/hardware_tasks.rs +++ b/rtic-macros/src/codegen/hardware_tasks.rs @@ -1,7 +1,10 @@ use crate::syntax::{ast::App, Context}; use crate::{ analyze::Analysis, - codegen::{local_resources_struct, module, shared_resources_struct}, + codegen::{ + bindings::{interrupt_entry, interrupt_exit}, + local_resources_struct, module, shared_resources_struct, + }, }; use proc_macro2::TokenStream as TokenStream2; use quote::quote; @@ -17,6 +20,8 @@ pub fn codegen(app: &App, analysis: &Analysis) -> TokenStream2 { let priority = task.args.priority; let cfgs = &task.cfgs; let attrs = &task.attrs; + let entry_stmts = interrupt_entry(app, analysis); + let exit_stmts = interrupt_exit(app, analysis); mod_app.push(quote!( #[allow(non_snake_case)] @@ -24,6 +29,8 @@ pub fn codegen(app: &App, analysis: &Analysis) -> TokenStream2 { #(#attrs)* #(#cfgs)* unsafe fn #symbol() { + #(#entry_stmts)* + const PRIORITY: u8 = #priority; rtic::export::run(PRIORITY, || { @@ -31,6 +38,8 @@ pub fn codegen(app: &App, analysis: &Analysis) -> TokenStream2 { #name::Context::new() ) }); + + #(#exit_stmts)* } )); -- cgit v1.2.3 From 4442c4692634c90ba60653d42d72f9f259ae1a16 Mon Sep 17 00:00:00 2001 From: Emil Fresk Date: Mon, 20 Feb 2023 20:56:18 +0100 Subject: Update backend features to be more clear --- rtic-macros/src/codegen/bindings.rs | 14 +++++++------- rtic-macros/src/codegen/bindings/cortex.rs | 8 ++++---- 2 files changed, 11 insertions(+), 11 deletions(-) (limited to 'rtic-macros/src/codegen') diff --git a/rtic-macros/src/codegen/bindings.rs b/rtic-macros/src/codegen/bindings.rs index a187820..68378b5 100644 --- a/rtic-macros/src/codegen/bindings.rs +++ b/rtic-macros/src/codegen/bindings.rs @@ -1,18 +1,18 @@ #[cfg(not(any( - feature = "cortex_m_source_masking", - feature = "cortex_m_basepri", - feaute = "test_template" + feature = "cortex-m-source-masking", + feature = "cortex-m-basepri", + feaute = "test-template" )))] compile_error!("No backend selected"); -#[cfg(any(feature = "cortex_m_source_masking", feature = "cortex_m_basepri"))] +#[cfg(any(feature = "cortex-m-source-masking", feature = "cortex-m-basepri"))] pub use cortex::*; -#[cfg(feature = "test_template")] +#[cfg(feature = "test-template")] pub use cortex::*; -#[cfg(any(feature = "cortex_m_source_masking", feature = "cortex_m_basepri"))] +#[cfg(any(feature = "cortex-m-source-masking", feature = "cortex-m-basepri"))] mod cortex; -#[cfg(feature = "test_template")] +#[cfg(feature = "test-template")] mod template; diff --git a/rtic-macros/src/codegen/bindings/cortex.rs b/rtic-macros/src/codegen/bindings/cortex.rs index d5eb12e..3f0584c 100644 --- a/rtic-macros/src/codegen/bindings/cortex.rs +++ b/rtic-macros/src/codegen/bindings/cortex.rs @@ -8,9 +8,9 @@ use quote::quote; use std::collections::HashSet; use syn::{parse, Attribute, Ident}; -#[cfg(feature = "cortex_m_basepri")] +#[cfg(feature = "cortex-m-basepri")] pub use basepri::*; -#[cfg(feature = "cortex_m_source_masking")] +#[cfg(feature = "cortex-m-source-masking")] pub use source_masking::*; /// Whether `name` is an exception with configurable priority @@ -30,7 +30,7 @@ fn is_exception(name: &Ident) -> bool { ) } -#[cfg(feature = "cortex_m_source_masking")] +#[cfg(feature = "cortex-m-source-masking")] mod source_masking { use super::*; use std::collections::HashMap; @@ -119,7 +119,7 @@ mod source_masking { } } -#[cfg(feature = "cortex_m_basepri")] +#[cfg(feature = "cortex-m-basepri")] mod basepri { use super::*; -- cgit v1.2.3 From 1c26eb722f7911737b7973982563a12bc3fb4164 Mon Sep 17 00:00:00 2001 From: Emil Fresk Date: Wed, 22 Feb 2023 19:59:50 +0100 Subject: Add template for bindings --- rtic-macros/src/codegen/bindings.rs | 4 +-- rtic-macros/src/codegen/bindings/template.rs | 44 ++++++++++++++++++++++++++++ 2 files changed, 46 insertions(+), 2 deletions(-) create mode 100644 rtic-macros/src/codegen/bindings/template.rs (limited to 'rtic-macros/src/codegen') diff --git a/rtic-macros/src/codegen/bindings.rs b/rtic-macros/src/codegen/bindings.rs index 68378b5..6c214cd 100644 --- a/rtic-macros/src/codegen/bindings.rs +++ b/rtic-macros/src/codegen/bindings.rs @@ -1,7 +1,7 @@ #[cfg(not(any( feature = "cortex-m-source-masking", feature = "cortex-m-basepri", - feaute = "test-template" + feature = "test-template" )))] compile_error!("No backend selected"); @@ -9,7 +9,7 @@ compile_error!("No backend selected"); pub use cortex::*; #[cfg(feature = "test-template")] -pub use cortex::*; +pub use template::*; #[cfg(any(feature = "cortex-m-source-masking", feature = "cortex-m-basepri"))] mod cortex; diff --git a/rtic-macros/src/codegen/bindings/template.rs b/rtic-macros/src/codegen/bindings/template.rs new file mode 100644 index 0000000..18f88fe --- /dev/null +++ b/rtic-macros/src/codegen/bindings/template.rs @@ -0,0 +1,44 @@ +use crate::{ + analyze::Analysis as CodegenAnalysis, + syntax::{analyze::Analysis as SyntaxAnalysis, ast::App}, +}; +use proc_macro2::TokenStream as TokenStream2; +use quote::quote; +use syn::{parse, Attribute, Ident}; + +pub fn impl_mutex( + _app: &App, + _analysis: &CodegenAnalysis, + _cfgs: &[Attribute], + _resources_prefix: bool, + _name: &Ident, + _ty: &TokenStream2, + _ceiling: u8, + _ptr: &TokenStream2, +) -> TokenStream2 { + quote!() +} + +pub fn extra_assertions(_app: &App, _analysis: &SyntaxAnalysis) -> Vec { + vec![] +} + +pub fn pre_init_checks(_app: &App, _analysis: &SyntaxAnalysis) -> Vec { + vec![] +} + +pub fn pre_init_enable_interrupts(_app: &App, _analysis: &CodegenAnalysis) -> Vec { + vec![] +} + +pub fn architecture_specific_analysis(_app: &App, _analysis: &SyntaxAnalysis) -> parse::Result<()> { + Ok(()) +} + +pub fn interrupt_entry(_app: &App, _analysis: &CodegenAnalysis) -> Vec { + vec![] +} + +pub fn interrupt_exit(_app: &App, _analysis: &CodegenAnalysis) -> Vec { + vec![] +} -- cgit v1.2.3 From bdfad77938f66d65db87ac19286fd5fc421985c4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20Tj=C3=A4der?= Date: Sat, 4 Mar 2023 20:52:39 +0100 Subject: rtic-macros: clippy fixes --- rtic-macros/src/codegen/bindings/cortex.rs | 1 + 1 file changed, 1 insertion(+) (limited to 'rtic-macros/src/codegen') diff --git a/rtic-macros/src/codegen/bindings/cortex.rs b/rtic-macros/src/codegen/bindings/cortex.rs index 3f0584c..9eab5cd 100644 --- a/rtic-macros/src/codegen/bindings/cortex.rs +++ b/rtic-macros/src/codegen/bindings/cortex.rs @@ -124,6 +124,7 @@ mod basepri { use super::*; /// Generates a `Mutex` implementation + #[allow(clippy::too_many_arguments)] pub fn impl_mutex( app: &App, _analysis: &CodegenAnalysis, -- cgit v1.2.3 From 25491291afe70350071041bd94473931ecdf87b0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20Tj=C3=A4der?= Date: Sat, 4 Mar 2023 21:09:22 +0100 Subject: rtic-macros: clippy fixes --- rtic-macros/src/codegen/bindings/cortex.rs | 1 + 1 file changed, 1 insertion(+) (limited to 'rtic-macros/src/codegen') diff --git a/rtic-macros/src/codegen/bindings/cortex.rs b/rtic-macros/src/codegen/bindings/cortex.rs index 9eab5cd..767befa 100644 --- a/rtic-macros/src/codegen/bindings/cortex.rs +++ b/rtic-macros/src/codegen/bindings/cortex.rs @@ -36,6 +36,7 @@ mod source_masking { use std::collections::HashMap; /// Generates a `Mutex` implementation + #[allow(clippy::too_many_arguments)] pub fn impl_mutex( app: &App, analysis: &CodegenAnalysis, -- cgit v1.2.3