aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--rtic-macros/Cargo.toml1
-rw-r--r--rtic-macros/src/bindings.rs1
-rw-r--r--rtic-macros/src/check.rs71
-rw-r--r--rtic-macros/src/codegen.rs2
-rw-r--r--rtic-macros/src/codegen/assertions.rs36
-rw-r--r--rtic-macros/src/codegen/bindings.rs5
-rw-r--r--rtic-macros/src/codegen/bindings/cortex.rs346
-rw-r--r--rtic-macros/src/codegen/module.rs4
-rw-r--r--rtic-macros/src/codegen/pre_init.rs69
-rw-r--r--rtic-macros/src/codegen/shared_resources.rs86
-rw-r--r--rtic-macros/src/codegen/util.rs68
-rw-r--r--rtic-macros/src/lib.rs3
-rw-r--r--rtic/Cargo.toml2
-rw-r--r--rtic/src/export.rs310
-rw-r--r--rtic/src/export/cortex_basepri.rs78
-rw-r--r--rtic/src/export/cortex_source_mask.rs194
16 files changed, 654 insertions, 622 deletions
diff --git a/rtic-macros/Cargo.toml b/rtic-macros/Cargo.toml
index 970f6662..12cd0d95 100644
--- a/rtic-macros/Cargo.toml
+++ b/rtic-macros/Cargo.toml
@@ -24,6 +24,7 @@ proc-macro = true
[features]
default = []
debugprint = []
+
# list of supported codegen backends
thumbv6 = []
thumbv7 = []
diff --git a/rtic-macros/src/bindings.rs b/rtic-macros/src/bindings.rs
deleted file mode 100644
index 8b137891..00000000
--- a/rtic-macros/src/bindings.rs
+++ /dev/null
@@ -1 +0,0 @@
-
diff --git a/rtic-macros/src/check.rs b/rtic-macros/src/check.rs
index a05c82e8..957a9b36 100644
--- a/rtic-macros/src/check.rs
+++ b/rtic-macros/src/check.rs
@@ -1,70 +1,7 @@
-use std::collections::HashSet;
-
-use crate::syntax::ast::App;
+use crate::codegen::bindings::architecture_specific_analysis;
+use crate::syntax::{analyze::Analysis, ast::App};
use syn::parse;
-pub fn app(app: &App) -> 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::<HashSet<_>>();
-
- 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(())
+pub fn app(app: &App, analysis: &Analysis) -> parse::Result<()> {
+ architecture_specific_analysis(app, analysis)
}
diff --git a/rtic-macros/src/codegen.rs b/rtic-macros/src/codegen.rs
index 24e98ce9..48ee5e33 100644
--- a/rtic-macros/src/codegen.rs
+++ b/rtic-macros/src/codegen.rs
@@ -4,6 +4,8 @@ use quote::quote;
use crate::analyze::Analysis;
use crate::syntax::ast::App;
+pub mod bindings;
+
mod assertions;
mod async_dispatchers;
mod hardware_tasks;
diff --git a/rtic-macros/src/codegen/assertions.rs b/rtic-macros/src/codegen/assertions.rs
index dd94aa6d..2f5dd523 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<TokenStream2> {
@@ -16,38 +17,7 @@ pub fn codegen(app: &App, analysis: &Analysis) -> Vec<TokenStream2> {
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 00000000..1efe0ce0
--- /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 00000000..15976a10
--- /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<RTIC_INTERNAL_R>(&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<N_CHUNKS>; 3] = [#(#mask_arr),*];
+
+ unsafe {
+ rtic::export::lock(
+ #ptr,
+ CEILING,
+ &MASKS,
+ f,
+ )
+ }
+ }
+ }
+ )
+ }
+
+ pub fn extra_assertions(_: &App, _: &SyntaxAnalysis) -> Vec<TokenStream2> {
+ // 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<RTIC_INTERNAL_R>(&mut self, f: impl FnOnce(&mut #ty) -> RTIC_INTERNAL_R) -> RTIC_INTERNAL_R {
+ /// Priority ceiling
+ const CEILING: u8 = #ceiling;
+
+ unsafe {
+ rtic::export::lock(
+ #ptr,
+ CEILING,
+ #device::NVIC_PRIO_BITS,
+ f,
+ )
+ }
+ }
+ }
+ )
+ }
+
+ pub fn extra_assertions(_: &App, _: &SyntaxAnalysis) -> Vec<TokenStream2> {
+ vec![]
+ }
+}
+
+pub fn pre_init_checks(app: &App, _: &SyntaxAnalysis) -> Vec<TokenStream2> {
+ 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<TokenStream2> {
+ 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::<HashSet<_>>();
+
+ 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 8b3fca23..af4e0346 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 28ba29c0..a2d0e8c1 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<TokenStream2> {
let mut stmts = vec![];
- let rt_err = util::rt_err_ident();
-
// Disable interrupts -- `init` must run with interrupts disabled
stmts.push(quote!(rtic::export::interrupt::disable();));
@@ -18,68 +16,9 @@ pub fn codegen(app: &App, analysis: &Analysis) -> Vec<TokenStream2> {
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 19fd13fe..686c2809 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 d0c8cc0e..27c8a2a1 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<RTIC_INTERNAL_R>(&mut self, f: impl FnOnce(&mut #ty) -> RTIC_INTERNAL_R) -> RTIC_INTERNAL_R {
- /// Priority ceiling
- const CEILING: u8 = #ceiling;
-
- unsafe {
- rtic::export::lock(
- #ptr,
- CEILING,
- #device::NVIC_PRIO_BITS,
- &#masks_name,
- f,
- )
- }
- }
- }
- )
-}
-
pub fn interrupt_ident() -> Ident {
let span = Span::call_site();
Ident::new("interrupt", span)
}
-/// Whether `name` is an exception with configurable priority
-pub fn is_exception(name: &Ident) -> bool {
- let s = name.to_string();
-
- matches!(
- &*s,
- "MemoryManagement"
- | "BusFault"
- | "UsageFault"
- | "SecureFault"
- | "SVCall"
- | "DebugMonitor"
- | "PendSV"
- | "SysTick"
- )
-}
-
/// Mark a name as internal
pub fn mark_internal_name(name: &str) -> Ident {
Ident::new(&format!("{RTIC_INTERNAL}_{name}"), Span::call_site())
@@ -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}"))
}
diff --git a/rtic-macros/src/lib.rs b/rtic-macros/src/lib.rs
index 3ac27017..cd2a9245 100644
--- a/rtic-macros/src/lib.rs
+++ b/rtic-macros/src/lib.rs
@@ -8,7 +8,6 @@ use proc_macro::TokenStream;
use std::{env, fs, path::Path};
mod analyze;
-mod bindings;
mod check;
mod codegen;
mod syntax;
@@ -38,7 +37,7 @@ pub fn app(args: TokenStream, input: TokenStream) -> TokenStream {
Ok(x) => x,
};
- if let Err(e) = check::app(&app) {
+ if let Err(e) = check::app(&app, &analysis) {
return e.to_compile_error().into();
}
diff --git a/rtic/Cargo.toml b/rtic/Cargo.toml
index 9fa9f9d5..24b7fd17 100644
--- a/rtic/Cargo.toml
+++ b/rtic/Cargo.toml
@@ -51,7 +51,7 @@ lm3s6965 = "0.1.3"
cortex-m-semihosting = "0.5.0"
rtic-time = { path = "../rtic-time" }
rtic-channel = { path = "../rtic-channel" }
-rtic-monotonics = { path = "../rtic-monotonics" }
+rtic-monotonics = { path = "../rtic-monotonics", features = ["cortex_m_systick"] }
[dev-dependencies.futures]
version = "0.3.26"
diff --git a/rtic/src/export.rs b/rtic/src/export.rs
index cdca9727..91812fa0 100644
--- a/rtic/src/export.rs
+++ b/rtic/src/export.rs
@@ -1,89 +1,20 @@
pub use bare_metal::CriticalSection;
-pub use cortex_m::{
- asm::nop,
- asm::wfi,
- interrupt,
- peripheral::{scb::SystemHandler, DWT, NVIC, SCB, SYST},
- Peripherals,
-};
//pub use portable_atomic as atomic;
pub use atomic_polyfill as atomic;
pub mod executor;
-/// Mask is used to store interrupt masks on systems without a BASEPRI register (M0, M0+, M23).
-/// It needs to be large enough to cover all the relevant interrupts in use.
-/// For M0/M0+ there are only 32 interrupts so we only need one u32 value.
-/// For M23 there can be as many as 480 interrupts.
-/// Rather than providing space for all possible interrupts, we just detect the highest interrupt in
-/// use at compile time and allocate enough u32 chunks to cover them.
-#[derive(Copy, Clone)]
-pub struct Mask<const M: usize>([u32; M]);
+// #[cfg(have_basepri)]
+pub mod cortex_basepri;
-impl<const M: usize> core::ops::BitOrAssign for Mask<M> {
- fn bitor_assign(&mut self, rhs: Self) {
- for i in 0..M {
- self.0[i] |= rhs.0[i];
- }
- }
-}
-
-#[cfg(not(have_basepri))]
-impl<const M: usize> Mask<M> {
- /// Set a bit inside a Mask.
- const fn set_bit(mut self, bit: u32) -> Self {
- let block = bit / 32;
-
- if block as usize >= M {
- panic!("Generating masks for thumbv6/thumbv8m.base failed! Are you compiling for thumbv6 on an thumbv7 MCU or using an unsupported thumbv8m.base MCU?");
- }
-
- let offset = bit - (block * 32);
- self.0[block as usize] |= 1 << offset;
- self
- }
-}
-
-#[cfg(have_basepri)]
-use cortex_m::register::basepri;
-
-#[cfg(have_basepri)]
-#[inline(always)]
-pub fn run<F>(priority: u8, f: F)
-where
- F: FnOnce(),
-{
- if priority == 1 {
- // If the priority of this interrupt is `1` then BASEPRI can only be `0`
- f();
- unsafe { basepri::write(0) }
- } else {
- let initial = basepri::read();
- f();
- unsafe { basepri::write(initial) }
- }
-}
-
-#[cfg(not(have_basepri))]
-#[inline(always)]
-pub fn run<F>(_priority: u8, f: F)
-where
- F: FnOnce(),
-{
- f();
-}
-
-/// Const helper to check architecture
-pub const fn have_basepri() -> bool {
- #[cfg(have_basepri)]
- {
- true
- }
+// #[cfg(not(have_basepri))]
+pub mod cortex_source_mask;
- #[cfg(not(have_basepri))]
- {
- false
- }
+/// Priority conversion, takes logical priorities 1..=N and converts it to NVIC priority.
+#[inline]
+#[must_use]
+pub const fn cortex_logical2hw(logical: u8, nvic_prio_bits: u8) -> u8 {
+ ((1 << nvic_prio_bits) - logical) << (8 - nvic_prio_bits)
}
#[inline(always)]
@@ -99,226 +30,3 @@ where
T: Sync,
{
}
-
-/// Lock implementation using BASEPRI and global Critical Section (CS)
-///
-/// # Safety
-///
-/// The system ceiling is raised from current to ceiling
-/// by either
-/// - raising the BASEPRI to the ceiling value, or
-/// - disable all interrupts in case we want to
-/// mask interrupts with maximum priority
-///
-/// Dereferencing a raw pointer inside CS
-///
-/// The priority.set/priority.get can safely be outside the CS
-/// as being a context local cell (not affected by preemptions).
-/// It is merely used in order to omit masking in case current
-/// priority is current priority >= ceiling.
-///
-/// Lock Efficiency:
-/// Experiments validate (sub)-zero cost for CS implementation
-/// (Sub)-zero as:
-/// - Either zero OH (lock optimized out), or
-/// - Amounting to an optimal assembly implementation
-/// - The BASEPRI value is folded to a constant at compile time
-/// - CS entry, single assembly instruction to write BASEPRI
-/// - CS exit, single assembly instruction to write BASEPRI
-/// - priority.set/get optimized out (their effect not)
-/// - On par or better than any handwritten implementation of SRP
-///
-/// Limitations:
-/// The current implementation reads/writes BASEPRI once
-/// even in some edge cases where this may be omitted.
-/// Total OH of per task is max 2 clock cycles, negligible in practice
-/// but can in theory be fixed.
-///
-#[cfg(have_basepri)]
-#[inline(always)]
-pub unsafe fn lock<T, R, const M: usize>(
- ptr: *mut T,
- ceiling: u8,
- nvic_prio_bits: u8,
- _mask: &[Mask<M>; 3],
- f: impl FnOnce(&mut T) -> R,
-) -> R {
- if ceiling == (1 << nvic_prio_bits) {
- let r = interrupt::free(|_| f(&mut *ptr));
- r
- } else {
- let current = basepri::read();
- basepri::write(logical2hw(ceiling, nvic_prio_bits));
- let r = f(&mut *ptr);
- basepri::write(current);
- r
- }
-}
-
-/// Lock implementation using interrupt masking
-///
-/// # Safety
-///
-/// The system ceiling is raised from current to ceiling
-/// by computing a 32 bit `mask` (1 bit per interrupt)
-/// 1: ceiling >= priority > current
-/// 0: else
-///
-/// On CS entry, `clear_enable_mask(mask)` disables interrupts
-/// On CS exit, `set_enable_mask(mask)` re-enables interrupts
-///
-/// The priority.set/priority.get can safely be outside the CS
-/// as being a context local cell (not affected by preemptions).
-/// It is merely used in order to omit masking in case
-/// current priority >= ceiling.
-///
-/// Dereferencing a raw pointer is done safely inside the CS
-///
-/// Lock Efficiency:
-/// Early experiments validate (sub)-zero cost for CS implementation
-/// (Sub)-zero as:
-/// - Either zero OH (lock optimized out), or
-/// - Amounting to an optimal assembly implementation
-/// - if ceiling == (1 << nvic_prio_bits)
-/// - we execute the closure in a global critical section (interrupt free)
-/// - CS entry cost, single write to core register
-/// - CS exit cost, single write to core register
-/// else
-/// - The `mask` value is folded to a constant at compile time
-/// - CS entry, single write of the 32 bit `mask` to the `icer` register
-/// - CS exit, single write of the 32 bit `mask` to the `iser` register
-/// - priority.set/get optimized out (their effect not)
-/// - On par or better than any hand written implementation of SRP
-///
-/// Limitations:
-/// Current implementation does not allow for tasks with shared resources
-/// to be bound to exception handlers, as these cannot be masked in HW.
-///
-/// Possible solutions:
-/// - Mask exceptions by global critical sections (interrupt::free)
-/// - Temporary lower exception priority
-///
-/// These possible solutions are set goals for future work
-#[cfg(not(have_basepri))]
-#[inline(always)]
-pub unsafe fn lock<T, R, const M: usize>(
- ptr: *mut T,
- ceiling: u8,
- _nvic_prio_bits: u8,
- masks: &[Mask<M>; 3],
- f: impl FnOnce(&mut T) -> R,
-) -> R {
- if ceiling >= 4 {
- // safe to manipulate outside critical section
- // execute closure under protection of raised system ceiling
-
- // safe to manipulate outside critical section
- interrupt::free(|_| f(&mut *ptr))
- } else {
- // safe to manipulate outside critical section
- let mask = compute_mask(0, ceiling, masks);
- clear_enable_mask(mask);
-
- // execute closure under protection of raised system ceiling
- let r = f(&mut *ptr);
-
- set_enable_mask(mask);
-
- // safe to manipulate outside critical section
- r
- }
-}
-
-#[cfg(not(have_basepri))]
-#[inline(always)]
-fn compute_mask<const M: usize>(from_prio: u8, to_prio: u8, masks: &[Mask<M>; 3]) -> Mask<M> {
- let mut res = Mask([0; M]);
- masks[from_prio as usize..to_prio as usize]
- .iter()
- .for_each(|m| res |= *m);
- res
-}
-
-// enables interrupts
-#[cfg(not(have_basepri))]
-#[inline(always)]
-unsafe fn set_enable_mask<const M: usize>(mask: Mask<M>) {
- for i in 0..M {
- // This check should involve compile time constants and be optimized out.
- if mask.0[i] != 0 {
- (*NVIC::PTR).iser[i].write(mask.0[i]);
- }
- }
-}
-
-// disables interrupts
-#[cfg(not(have_basepri))]
-#[inline(always)]
-unsafe fn clear_enable_mask<const M: usize>(mask: Mask<M>) {
- for i in 0..M {
- // This check should involve compile time constants and be optimized out.
- if mask.0[i] != 0 {
- (*NVIC::PTR).icer[i].write(mask.0[i]);
- }
- }
-}
-
-#[inline]
-#[must_use]
-pub fn logical2hw(logical: u8, nvic_prio_bits: u8) -> u8 {
- ((1 << nvic_prio_bits) - logical) << (8 - nvic_prio_bits)
-}
-
-#[cfg(have_basepri)]
-pub const fn create_mask<const N: usize, const M: usize>(_: [u32; N]) -> Mask<M> {
- Mask([0; M])
-}
-
-#[cfg(not(have_basepri))]
-pub const fn create_mask<const N: usize, const M: usize>(list_of_shifts: [u32; N]) -> Mask<M> {
- let mut mask = Mask([0; M]);
- let mut i = 0;
-
- while i < N {
- let shift = list_of_shifts[i];
- i += 1;
- mask = mask.set_bit(shift);
- }
-
- mask
-}
-
-#[cfg(have_basepri)]
-pub const fn compute_mask_chunks<const L: usize>(_: [u32; L]) -> usize {
- 0
-}
-
-/// Compute the number of u32 chunks needed to store the Mask value.
-/// On M0, M0+ this should always end up being 1.
-/// On M23 we will pick a number that allows us to store the highest index used by the code.
-/// This means the amount of overhead will vary based on the actually interrupts used by the code.
-#[cfg(not(have_basepri))]
-pub const fn compute_mask_chunks<const L: usize>(ids: [u32; L]) -> usize {
- let mut max: usize = 0;
- let mut i = 0;
-
- while i < L {
- let id = ids[i] as usize;
- i += 1;
-
- if id > max {
- max = id;
- }
- }
- (max + 32) / 32
-}
-
-#[cfg(have_basepri)]
-pub const fn no_basepri_panic() {
- // For non-v6 all is fine
-}
-
-#[cfg(not(have_basepri))]
-pub const fn no_basepri_panic() {
- panic!("Exceptions with shared resources are not allowed when compiling for thumbv6 or thumbv8m.base. Use local resources or `#[lock_free]` shared resources");
-}
diff --git a/rtic/src/export/cortex_basepri.rs b/rtic/src/export/cortex_basepri.rs
new file mode 100644
index 00000000..04f8ae90
--- /dev/null
+++ b/rtic/src/export/cortex_basepri.rs
@@ -0,0 +1,78 @@
+use super::cortex_logical2hw;
+use cortex_m::register::basepri;
+pub use cortex_m::{
+ asm::nop,
+ asm::wfi,
+ interrupt,
+ peripheral::{scb::SystemHandler, DWT, NVIC, SCB, SYST},
+ Peripherals,
+};
+
+#[inline(always)]
+pub fn run<F>(priority: u8, f: F)
+where
+ F: FnOnce(),
+{
+ if priority == 1 {
+ // If the priority of this interrupt is `1` then BASEPRI can only be `0`
+ f();
+ unsafe { basepri::write(0) }
+ } else {
+ let initial = basepri::read();
+ f();
+ unsafe { basepri::write(initial) }
+ }
+}
+
+/// Lock implementation using BASEPRI and global Critical Section (CS)
+///
+/// # Safety
+///
+/// The system ceiling is raised from current to ceiling
+/// by either
+/// - raising the BASEPRI to the ceiling value, or
+/// - disable all interrupts in case we want to
+/// mask interrupts with maximum priority
+///
+/// Dereferencing a raw pointer inside CS
+///
+/// The priority.set/priority.get can safely be outside the CS
+/// as being a context local cell (not affected by preemptions).
+/// It is merely used in order to omit masking in case current
+/// priority is current priority >= ceiling.
+///
+/// Lock Efficiency:
+/// Experiments validate (sub)-zero cost for CS implementation
+/// (Sub)-zero as:
+/// - Either zero OH (lock optimized out), or
+/// - Amounting to an optimal assembly implementation
+/// - The BASEPRI value is folded to a constant at compile time
+/// - CS entry, single assembly instruction to write BASEPRI
+/// - CS exit, single assembly instruction to write BASEPRI
+/// - priority.set/get optimized out (their effect not)
+/// - On par or better than any handwritten implementation of SRP
+///
+/// Limitations:
+/// The current implementation reads/writes BASEPRI once
+/// even in some edge cases where this may be omitted.
+/// Total OH of per task is max 2 clock cycles, negligible in practice
+/// but can in theory be fixed.
+///
+#[inline(always)]
+pub unsafe fn lock<T, R, const M: usize>(
+ ptr: *mut T,
+ ceiling: u8,
+ nvic_prio_bits: u8,
+ f: impl FnOnce(&mut T) -> R,
+) -> R {
+ if ceiling == (1 << nvic_prio_bits) {
+ let r = interrupt::free(|_| f(&mut *ptr));
+ r
+ } else {
+ let current = basepri::read();
+ basepri::write(cortex_logical2hw(ceiling, nvic_prio_bits));
+ let r = f(&mut *ptr);
+ basepri::write(current);
+ r
+ }
+}
diff --git a/rtic/src/export/cortex_source_mask.rs b/rtic/src/export/cortex_source_mask.rs
new file mode 100644
index 00000000..db5c5f71
--- /dev/null
+++ b/rtic/src/export/cortex_source_mask.rs
@@ -0,0 +1,194 @@
+pub use cortex_m::{
+ asm::nop,
+ asm::wfi,
+ interrupt,
+ peripheral::{scb::SystemHandler, DWT, NVIC, SCB, SYST},
+ Peripherals,
+};
+
+/// Mask is used to store interrupt masks on systems without a BASEPRI register (M0, M0+, M23).
+/// It needs to be large enough to cover all the relevant interrupts in use.
+/// For M0/M0+ there are only 32 interrupts so we only need one u32 value.
+/// For M23 there can be as many as 480 interrupts.
+/// Rather than providing space for all possible interrupts, we just detect the highest interrupt in
+/// use at compile time and allocate enough u32 chunks to cover them.
+#[derive(Copy, Clone)]
+pub struct Mask<const M: usize>([u32; M]);
+
+pub const fn create_mask<const N: usize, const M: usize>(list_of_shifts: [u32; N]) -> Mask<M> {
+ let mut mask = Mask([0; M]);
+ let mut i = 0;
+
+ while i < N {
+ let shift = list_of_shifts[i];
+ i += 1;
+ mask = mask.set_bit(shift);
+ }
+
+ mask
+}
+
+/// Compute the number of u32 chunks needed to store the Mask value.
+/// On M0, M0+ this should always end up being 1.
+/// On M23 we will pick a number that allows us to store the highest index used by the code.
+/// This means the amount of overhead will vary based on the actually interrupts used by the code.
+pub const fn compute_mask_chunks<const L: usize>(ids: [u32; L]) -> usize {
+ let mut max: usize = 0;
+ let mut i = 0;
+
+ while i < L {
+ let id = ids[i] as usize;
+ i += 1;
+
+ if id > max {
+ max = id;
+ }
+ }
+ (max + 32) / 32
+}
+
+impl<const M: usize> Mask<M> {
+ /// Set a bit inside a Mask.
+ const fn set_bit(mut self, bit: u32) -> Self {
+ let block = bit / 32;
+
+ if block as usize >= M {
+ panic!("Generating masks for thumbv6/thumbv8m.base failed! Are you compiling for thumbv6 on an thumbv7 MCU or using an unsupported thumbv8m.base MCU?");
+ }
+
+ let offset = bit - (block * 32);
+ self.0[block as usize] |= 1 << offset;
+ self
+ }
+}
+
+#[inline(always)]
+pub fn run<F>(_priority: u8, f: F)
+where
+ F: FnOnce(),
+{
+ f();
+}
+
+/// Lock implementation using interrupt masking
+///
+/// # Safety
+///
+/// The system ceiling is raised from current to ceiling
+/// by computing a 32 bit `mask` (1 bit per interrupt)
+/// 1: ceiling >= priority > current
+/// 0: else
+///
+/// On CS entry, `clear_enable_mask(mask)` disables interrupts
+/// On CS exit, `set_enable_mask(mask)` re-enables interrupts
+///
+/// The priority.set/priority.get can safely be outside the CS
+/// as being a context local cell (not affected by preemptions).
+/// It is merely used in order to omit masking in case
+/// current priority >= ceiling.
+///
+/// Dereferencing a raw pointer is done safely inside the CS
+///
+/// Lock Efficiency:
+/// Early experiments validate (sub)-zero cost for CS implementation
+/// (Sub)-zero as:
+/// - Either zero OH (lock optimized out), or
+/// - Amounting to an optimal assembly implementation
+/// - if ceiling == (1 << nvic_prio_bits)
+/// - we execute the closure in a global critical section (interrupt free)
+/// - CS entry cost, single write to core register
+/// - CS exit cost, single write to core register
+/// else
+/// - The `mask` value is folded to a constant at compile time
+/// - CS entry, single write of the 32 bit `mask` to the `icer` register
+/// - CS exit, single write of the 32 bit `mask` to the `iser` register
+/// - priority.set/get optimized out (their effect not)
+/// - On par or better than any hand written implementation of SRP
+///
+/// Limitations:
+/// Current implementation does not allow for tasks with shared resources
+/// to be bound to exception handlers, as these cannot be masked in HW.
+///
+/// Possible solutions:
+/// - Mask exceptions by global critical sections (interrupt::free)
+/// - Temporary lower exception priority
+///
+/// These possible solutions are set goals for future work
+#[inline(always)]
+pub unsafe fn lock<T, R, const M: usize>(
+ ptr: *mut T,
+ ceiling: u8,
+ masks: &[Mask<M>; 3],
+ f: impl FnOnce(&mut T) -> R,
+) -> R {
+ if ceiling >= 4 {
+ // safe to manipulate outside critical section
+ // execute closure under protection of raised system ceiling
+
+ // safe to manipulate outside critical section
+ interrupt::free(|_| f(&mut *ptr))
+ } else {
+ // safe to manipulate outside critical section
+ let mask = compute_mask(0, ceiling, masks);
+ clear_enable_mask(mask);
+
+ // execute closure under protection of raised system ceiling
+ let r = f(&mut *ptr);
+
+ set_enable_mask(mask);
+
+ // safe to manipulate outside critical section
+ r
+ }
+}
+
+#[inline(always)]
+pub const fn compute_mask<const M: usize>(
+ from_prio: u8,
+ to_prio: u8,
+ masks: &[Mask<M>; 3],
+) -> Mask<M> {
+ let mut res = Mask([0; M]);
+
+ let mut idx = from_prio as usize;
+
+ while idx < to_prio as usize {
+ let mut i = 0;
+
+ while i < M {
+ //self.0[i] |= rhs.0[i];
+ res.0[i] |= masks[idx].0[i];
+ i += 1;
+ }
+
+ idx += 1;
+ }
+
+ // masks[from_prio as usize..to_prio as usize]
+ // .iter()
+ // .for_each(|m| res |= *m);
+
+ res
+}
+
+// enables interrupts
+#[inline(always)]
+unsafe fn set_enable_mask<const M: usize>(mask: Mask<M>) {
+ for i in 0..M {
+ // This check should involve compile time constants and be optimized out.
+ if mask.0[i] != 0 {
+ (*NVIC::PTR).iser[i].write(mask.0[i]);
+ }
+ }
+}
+
+// disables interrupts
+#[inline(always)]
+unsafe fn clear_enable_mask<const M: usize>(mask: Mask<M>) {
+ for i in 0..M {
+ // This check should involve compile time constants and be optimized out.
+ if mask.0[i] != 0 {
+ (*NVIC::PTR).icer[i].write(mask.0[i]);
+ }
+ }
+}