aboutsummaryrefslogtreecommitdiff
path: root/rtic-macros/src/codegen/bindings
diff options
context:
space:
mode:
Diffstat (limited to 'rtic-macros/src/codegen/bindings')
-rw-r--r--rtic-macros/src/codegen/bindings/cortex.rs18
-rw-r--r--rtic-macros/src/codegen/bindings/esp32c3.rs14
-rw-r--r--rtic-macros/src/codegen/bindings/riscv_slic.rs255
-rw-r--r--rtic-macros/src/codegen/bindings/template.rs60
4 files changed, 325 insertions, 22 deletions
diff --git a/rtic-macros/src/codegen/bindings/cortex.rs b/rtic-macros/src/codegen/bindings/cortex.rs
index 69b5ee5..5c56261 100644
--- a/rtic-macros/src/codegen/bindings/cortex.rs
+++ b/rtic-macros/src/codegen/bindings/cortex.rs
@@ -35,6 +35,12 @@ pub fn interrupt_ident() -> Ident {
Ident::new("interrupt", span)
}
+pub fn interrupt_mod(app: &App) -> TokenStream2 {
+ let device = &app.args.device;
+ let interrupt = interrupt_ident();
+ quote!(#device::#interrupt)
+}
+
pub fn check_stack_overflow_before_init(
_app: &App,
_analysis: &CodegenAnalysis,
@@ -199,12 +205,16 @@ mod basepri {
}
}
+pub fn pre_init_preprocessing(_app: &mut App, _analysis: &SyntaxAnalysis) -> parse::Result<()> {
+ Ok(())
+}
+
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 interrupt = interrupt_ident();
let rt_err = util::rt_err_ident();
for name in app.args.dispatchers.keys() {
@@ -217,7 +227,7 @@ pub fn pre_init_checks(app: &App, _: &SyntaxAnalysis) -> Vec<TokenStream2> {
pub fn pre_init_enable_interrupts(app: &App, analysis: &CodegenAnalysis) -> Vec<TokenStream2> {
let mut stmts = vec![];
- let interrupt = util::interrupt_ident();
+ let interrupt = interrupt_ident();
let rt_err = util::rt_err_ident();
let device = &app.args.device;
let nvic_prio_bits = quote!(#device::NVIC_PRIO_BITS);
@@ -381,3 +391,7 @@ pub fn handler_config(
) -> Vec<TokenStream2> {
vec![]
}
+
+pub fn extra_modules(_app: &App, _analysis: &SyntaxAnalysis) -> Vec<TokenStream2> {
+ vec![]
+}
diff --git a/rtic-macros/src/codegen/bindings/esp32c3.rs b/rtic-macros/src/codegen/bindings/esp32c3.rs
index 4b14cae..f8ea22a 100644
--- a/rtic-macros/src/codegen/bindings/esp32c3.rs
+++ b/rtic-macros/src/codegen/bindings/esp32c3.rs
@@ -55,10 +55,20 @@ mod esp32c3 {
Ident::new("Interrupt", span)
}
+ pub fn interrupt_mod(app: &App) -> TokenStream2 {
+ let device = &app.args.device;
+ let interrupt = interrupt_ident();
+ quote!(#device::#interrupt)
+ }
+
pub fn extra_assertions(_: &App, _: &SyntaxAnalysis) -> Vec<TokenStream2> {
vec![]
}
+ pub fn pre_init_preprocessing(_app: &mut App, _analysis: &SyntaxAnalysis) -> parse::Result<()> {
+ Ok(())
+ }
+
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
@@ -232,3 +242,7 @@ mod esp32c3 {
stmts
}
}
+
+pub fn extra_modules(_app: &App, _analysis: &SyntaxAnalysis) -> Vec<TokenStream2> {
+ vec![]
+}
diff --git a/rtic-macros/src/codegen/bindings/riscv_slic.rs b/rtic-macros/src/codegen/bindings/riscv_slic.rs
new file mode 100644
index 0000000..c9bf50a
--- /dev/null
+++ b/rtic-macros/src/codegen/bindings/riscv_slic.rs
@@ -0,0 +1,255 @@
+use crate::{
+ analyze::Analysis as CodegenAnalysis,
+ syntax::{
+ analyze::Analysis as SyntaxAnalysis,
+ ast::{App, Dispatcher},
+ },
+};
+use proc_macro2::{Span, TokenStream as TokenStream2};
+use quote::quote;
+use std::{collections::HashSet, vec};
+use syn::{parse, Attribute, Ident};
+
+/// Utility function to get the SLIC interrupt module.
+pub fn interrupt_ident() -> Ident {
+ let span = Span::call_site();
+ Ident::new("Interrupt", span)
+}
+
+pub fn interrupt_mod(_app: &App) -> TokenStream2 {
+ let interrupt = interrupt_ident();
+ quote!(slic::#interrupt)
+}
+
+/// This macro implements the [`rtic::Mutex`] trait for shared resources using the SLIC.
+#[allow(clippy::too_many_arguments)]
+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)
+ };
+
+ 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 {
+
+ const CEILING: u8 = #ceiling;
+
+ unsafe {
+ rtic::export::lock(#ptr, CEILING, f)
+ }
+ }
+ }
+ )
+}
+
+/// This macro is used to define additional compile-time assertions in case the platform needs it.
+/// The Cortex-M implementations do not use it. Thus, we think we do not need it either.
+pub fn extra_assertions(_app: &App, _analysis: &SyntaxAnalysis) -> Vec<TokenStream2> {
+ vec![]
+}
+
+pub fn pre_init_preprocessing(app: &mut App, _analysis: &SyntaxAnalysis) -> parse::Result<()> {
+ app.args.core = false; // RISC-V SLIC is not compatible with using core peripherals
+ if !app.args.dispatchers.is_empty() {
+ return Err(parse::Error::new(
+ Span::call_site(),
+ "this backend does not support explicit interrupt dispatchers; remove the `dispatchers` argument from `#[app]`",
+ ));
+ }
+
+ // Compute the number of handlers we need to dispatch the software tasks
+ let soft_priorities = app
+ .software_tasks
+ .iter()
+ .map(|(_, task)| task.args.priority)
+ .filter(|prio| *prio > 0)
+ .collect::<HashSet<_>>();
+
+ for i in 0..soft_priorities.len() {
+ let dispatcher_ident = Ident::new(&format!("__RTICDispatcher{}", i), Span::call_site());
+ app.args
+ .dispatchers
+ .insert(dispatcher_ident, Dispatcher { attrs: vec![] });
+ }
+
+ Ok(())
+}
+
+/// This macro is used to check at run-time that all the interruption dispatchers exist.
+pub fn pre_init_checks(app: &App, _analysis: &SyntaxAnalysis) -> Vec<TokenStream2> {
+ let mut stmts: Vec<TokenStream2> = vec![];
+ let int_mod = interrupt_mod(app);
+
+ // check that all dispatchers exists in the `slic::Interrupt` enumeration
+ for name in app.args.dispatchers.keys() {
+ stmts.push(quote!(let _ = #int_mod::#name;));
+ }
+
+ stmts
+}
+
+/// This macro must perform all the required operations to activate the
+/// interrupt sources with their corresponding priority level.
+pub fn pre_init_enable_interrupts(app: &App, analysis: &CodegenAnalysis) -> Vec<TokenStream2> {
+ let mut stmts = vec![];
+
+ // First, we reset and disable all the interrupt controllers
+ stmts.push(quote!(rtic::export::clear_interrupts();));
+
+ // Then, we set the corresponding priorities
+ let interrupt_ids = analysis.interrupts.iter().map(|(p, (id, _))| (p, id));
+ for (&p, name) in interrupt_ids.chain(
+ app.hardware_tasks
+ .values()
+ .map(|task| (&task.args.priority, &task.args.binds)),
+ ) {
+ stmts.push(quote!(
+ rtic::export::set_priority(slic::Interrupt::#name, #p);
+ ));
+ }
+ // Finally, we activate the interrupts
+ stmts.push(quote!(rtic::export::set_interrupts();));
+ stmts
+}
+
+/// Any additional checks that depend on the system architecture.
+pub fn architecture_specific_analysis(app: &App, _analysis: &SyntaxAnalysis) -> parse::Result<()> {
+ // 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})"
+ )
+ };
+
+ return Err(parse::Error::new(first.unwrap().span(), s));
+ }
+
+ if app.args.backend.is_none() {
+ return Err(parse::Error::new(
+ Span::call_site(),
+ "SLIC requires backend-specific configuration",
+ ));
+ }
+
+ Ok(())
+}
+
+/// Macro to add statements to be executed at the beginning of all the interrupt handlers.
+pub fn interrupt_entry(_app: &App, _analysis: &CodegenAnalysis) -> Vec<TokenStream2> {
+ vec![]
+}
+
+/// Macro to add statements to be executed at the end of all the interrupt handlers.
+pub fn interrupt_exit(_app: &App, _analysis: &CodegenAnalysis) -> Vec<TokenStream2> {
+ vec![]
+}
+
+pub fn check_stack_overflow_before_init(
+ _app: &App,
+ _analysis: &CodegenAnalysis,
+) -> Vec<TokenStream2> {
+ vec![quote!(
+ // Check for stack overflow using symbols from `risc-v-rt`.
+ extern "C" {
+ pub static _stack_start: u32;
+ pub static _ebss: u32;
+ }
+
+ let stack_start = &_stack_start as *const _ as u32;
+ let ebss = &_ebss as *const _ as u32;
+
+ if stack_start > ebss {
+ // No flip-link usage, check the SP for overflow.
+ if rtic::export::read_sp() <= ebss {
+ panic!("Stack overflow after allocating executors");
+ }
+ }
+ )]
+}
+
+pub fn async_entry(
+ _app: &App,
+ _analysis: &CodegenAnalysis,
+ _dispatcher_name: Ident,
+) -> Vec<TokenStream2> {
+ vec![]
+}
+
+/// Macro to define a maximum priority level for async tasks.
+pub fn async_prio_limit(_app: &App, analysis: &CodegenAnalysis) -> Vec<TokenStream2> {
+ let max = if let Some(max) = analysis.max_async_prio {
+ quote!(#max)
+ } else {
+ quote!(u8::MAX) // No limit
+ };
+
+ vec![quote!(
+ /// Holds the maximum priority level for use by async HAL drivers.
+ #[no_mangle]
+ static RTIC_ASYNC_MAX_LOGICAL_PRIO: u8 = #max;
+ )]
+}
+
+pub fn handler_config(
+ _app: &App,
+ _analysis: &CodegenAnalysis,
+ _dispatcher_name: Ident,
+) -> Vec<TokenStream2> {
+ vec![]
+}
+
+/// The SLIC requires us to call to the [`riscv_rtic::codegen`] macro to generate
+/// the appropriate SLIC structure, interrupt enumerations, etc.
+pub fn extra_modules(app: &App, _analysis: &SyntaxAnalysis) -> Vec<TokenStream2> {
+ let mut stmts = vec![];
+
+ let hw_slice: Vec<_> = app
+ .hardware_tasks
+ .values()
+ .map(|task| &task.args.binds)
+ .collect();
+ let sw_slice: Vec<_> = app.args.dispatchers.keys().collect();
+
+ let swi_slice: Vec<_> = hw_slice.iter().chain(sw_slice.iter()).collect();
+
+ let device = &app.args.device;
+
+ stmts.push(quote!(
+ use rtic::export::riscv_slic;
+ ));
+ let hart_id = &app.args.backend.as_ref().unwrap().hart_id;
+
+ stmts.push(quote!(rtic::export::codegen!(pac = #device, swi = [#(#swi_slice,)*], backend = [hart_id = #hart_id]);));
+
+ stmts
+}
diff --git a/rtic-macros/src/codegen/bindings/template.rs b/rtic-macros/src/codegen/bindings/template.rs
index b5488b7..ecb46d5 100644
--- a/rtic-macros/src/codegen/bindings/template.rs
+++ b/rtic-macros/src/codegen/bindings/template.rs
@@ -6,40 +6,55 @@ use proc_macro2::TokenStream as TokenStream2;
use quote::quote;
use syn::{parse, Attribute, Ident};
+pub fn interrupt_ident() -> Ident {
+ let span = Span::call_site();
+ Ident::new("interrupt", span)
+}
+
+pub fn interrupt_mod(app: &App) -> TokenStream2 {
+ let device = &app.args.device;
+ let interrupt = interrupt_ident();
+ quote!(#device::#interrupt)
+}
+
pub fn impl_mutex(
- _app: &App,
- _analysis: &CodegenAnalysis,
- _cfgs: &[Attribute],
- _resources_prefix: bool,
- _name: &Ident,
- _ty: &TokenStream2,
- _ceiling: u8,
- _ptr: &TokenStream2,
+ 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<TokenStream2> {
+pub fn extra_assertions(app: &App, analysis: &SyntaxAnalysis) -> Vec<TokenStream2> {
vec![]
}
-pub fn pre_init_checks(_app: &App, _analysis: &SyntaxAnalysis) -> Vec<TokenStream2> {
+pub fn pre_init_preprocessing(app: &mut App, analysis: &SyntaxAnalysis) -> parse::Result<()> {
+ Ok(())
+}
+
+pub fn pre_init_checks(app: &App, analysis: &SyntaxAnalysis) -> Vec<TokenStream2> {
vec![]
}
-pub fn pre_init_enable_interrupts(_app: &App, _analysis: &CodegenAnalysis) -> Vec<TokenStream2> {
+pub fn pre_init_enable_interrupts(app: &App, analysis: &CodegenAnalysis) -> Vec<TokenStream2> {
vec![]
}
-pub fn architecture_specific_analysis(_app: &App, _analysis: &SyntaxAnalysis) -> parse::Result<()> {
+pub fn architecture_specific_analysis(app: &App, analysis: &SyntaxAnalysis) -> parse::Result<()> {
Ok(())
}
-pub fn interrupt_entry(_app: &App, _analysis: &CodegenAnalysis) -> Vec<TokenStream2> {
+pub fn interrupt_entry(app: &App, analysis: &CodegenAnalysis) -> Vec<TokenStream2> {
vec![]
}
-pub fn interrupt_exit(_app: &App, _analysis: &CodegenAnalysis) -> Vec<TokenStream2> {
+pub fn interrupt_exit(app: &App, analysis: &CodegenAnalysis) -> Vec<TokenStream2> {
vec![]
}
@@ -51,20 +66,25 @@ pub fn check_stack_overflow_before_init(
}
pub fn async_entry(
- _app: &App,
- _analysis: &CodegenAnalysis,
- _dispatcher_name: Ident,
+ app: &App,
+ analysis: &CodegenAnalysis,
+ dispatcher_name: Ident,
) -> Vec<TokenStream2> {
vec![]
}
-pub fn async_prio_limit(app: &App, _analysis: &CodegenAnalysis) -> Vec<TokenStream2> {
+pub fn async_prio_limit(app: &App, analysis: &CodegenAnalysis) -> Vec<TokenStream2> {
vec![]
}
+
pub fn handler_config(
- _app: &App,
- _analysis: &CodegenAnalysis,
+ app: &App,
+ analysis: &CodegenAnalysis,
dispatcher_name: Ident,
) -> Vec<TokenStream2> {
vec![]
}
+
+pub fn extra_modules(app: &App, analysis: &SyntaxAnalysis) -> Vec<TokenStream2> {
+ vec![]
+}