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/bindings/cortex.rs | 346 +++++++++++++++++++++++++++++ 1 file changed, 346 insertions(+) create mode 100644 rtic-macros/src/codegen/bindings/cortex.rs (limited to 'rtic-macros/src/codegen/bindings/cortex.rs') 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(()) +} -- 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/cortex.rs | 50 ++++++------------------------ 1 file changed, 9 insertions(+), 41 deletions(-) (limited to 'rtic-macros/src/codegen/bindings/cortex.rs') 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), );)); } -- 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/bindings/cortex.rs | 8 ++++++++ 1 file changed, 8 insertions(+) (limited to 'rtic-macros/src/codegen/bindings/cortex.rs') 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![] +} -- 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/cortex.rs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) (limited to 'rtic-macros/src/codegen/bindings/cortex.rs') 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 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/bindings/cortex.rs') 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/bindings/cortex.rs') 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