aboutsummaryrefslogtreecommitdiff
path: root/rtic-macros
diff options
context:
space:
mode:
authorRomán Cárdenas Rodríguez <rcardenas.rod@gmail.com>2024-03-20 21:06:47 +0100
committerGitHub <noreply@github.com>2024-03-20 20:06:47 +0000
commit4060c3def88f82d4e4f48de7137ce365167ef265 (patch)
treef326f0687092cab2b772952b579d63d12d3d34aa /rtic-macros
parent22ac33a826dedacde5d3d5c0964ff072555a9b32 (diff)
RISC-V support over CLINT (#815)
* Rebase to master * using interrupt_mod * bug fixes * fix other backends * Add changelog * forgot about rtic-macros * backend-specific configuration * core peripherals optional over macro argument * pre_init_preprocessing binding * CI for RISC-V (WIP) * separation of concerns * add targets for RISC-V examples * remove qemu feature * prepare examples folder * move examples all together * move ci out of examples * minor changes * add cortex-m * new xtask: proof of concept * fix build.yml * feature typo * clean rtic examples * reproduce weird issue * remove unsafe code in user app * update dependencies * allow builds on riscv32imc * let's fix QEMU * Update .github/workflows/build.yml Co-authored-by: Henrik Tjäder <henrik@tjaders.com> * New build.rs * removing test features * adapt ui test to new version of clippy * add more examples to RISC-V backend * proper configuration of heapless for riscv32imc * opt-out examples for riscv32imc * point to new version of riscv-slic * adapt new macro bindings * adapt examples and CI to stable * fix cortex-m CI * Review --------- Co-authored-by: Henrik Tjäder <henrik@tjaders.com>
Diffstat (limited to 'rtic-macros')
-rw-r--r--rtic-macros/CHANGELOG.md1
-rw-r--r--rtic-macros/Cargo.toml4
-rw-r--r--rtic-macros/src/codegen.rs1
-rw-r--r--rtic-macros/src/codegen/async_dispatchers.rs7
-rw-r--r--rtic-macros/src/codegen/bindings.rs11
-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
-rw-r--r--rtic-macros/src/codegen/extra_mods.rs9
-rw-r--r--rtic-macros/src/codegen/main.rs16
-rw-r--r--rtic-macros/src/codegen/module.rs29
-rw-r--r--rtic-macros/src/codegen/pre_init.rs10
-rw-r--r--rtic-macros/src/codegen/util.rs2
-rw-r--r--rtic-macros/src/lib.rs17
-rw-r--r--rtic-macros/src/preprocess.rs7
-rw-r--r--rtic-macros/src/syntax.rs1
-rw-r--r--rtic-macros/src/syntax/ast.rs10
-rw-r--r--rtic-macros/src/syntax/backend.rs32
-rw-r--r--rtic-macros/src/syntax/backend/cortex.rs16
-rw-r--r--rtic-macros/src/syntax/backend/esp32c3.rs16
-rw-r--r--rtic-macros/src/syntax/backend/riscv_slic.rs16
-rw-r--r--rtic-macros/src/syntax/backend/template.rs15
-rw-r--r--rtic-macros/src/syntax/parse/app.rs28
24 files changed, 539 insertions, 56 deletions
diff --git a/rtic-macros/CHANGELOG.md b/rtic-macros/CHANGELOG.md
index 66bd9bc..a0d761b 100644
--- a/rtic-macros/CHANGELOG.md
+++ b/rtic-macros/CHANGELOG.md
@@ -11,6 +11,7 @@ For each category, *Added*, *Changed*, *Fixed* add new entries at the top!
### Added
+- Unstable support for RISC-V targets compatible with `riscv-slic`
- RTIC v2 now works on stable.
- Unstable ESP32-C3 support.
diff --git a/rtic-macros/Cargo.toml b/rtic-macros/Cargo.toml
index 423fe31..443ed0d 100644
--- a/rtic-macros/Cargo.toml
+++ b/rtic-macros/Cargo.toml
@@ -36,8 +36,8 @@ cortex-m-basepri = []
riscv-esp32c3 = []
# riscv-clic = []
# riscv-ch32 = []
-
-
+riscv-slic = []
+
# backend API test
test-template = []
diff --git a/rtic-macros/src/codegen.rs b/rtic-macros/src/codegen.rs
index c04f213..060db6d 100644
--- a/rtic-macros/src/codegen.rs
+++ b/rtic-macros/src/codegen.rs
@@ -8,6 +8,7 @@ pub mod bindings;
mod assertions;
mod async_dispatchers;
+mod extra_mods;
mod hardware_tasks;
mod idle;
mod init;
diff --git a/rtic-macros/src/codegen/async_dispatchers.rs b/rtic-macros/src/codegen/async_dispatchers.rs
index 9144b2a..3d166ca 100644
--- a/rtic-macros/src/codegen/async_dispatchers.rs
+++ b/rtic-macros/src/codegen/async_dispatchers.rs
@@ -2,7 +2,7 @@ use crate::syntax::ast::App;
use crate::{
analyze::Analysis,
codegen::{
- bindings::{async_entry, handler_config, interrupt_entry, interrupt_exit},
+ bindings::{async_entry, handler_config, interrupt_entry, interrupt_exit, interrupt_mod},
util,
},
};
@@ -36,10 +36,9 @@ pub fn codegen(app: &App, analysis: &Analysis) -> TokenStream2 {
};
let pend_interrupt = if level > 0 {
- let device = &app.args.device;
- let enum_ = util::interrupt_ident();
+ let int_mod = interrupt_mod(app);
- quote!(rtic::export::pend(#device::#enum_::#dispatcher_name);)
+ quote!(rtic::export::pend(#int_mod::#dispatcher_name);)
} else {
// For 0 priority tasks we don't need to pend anything
quote!()
diff --git a/rtic-macros/src/codegen/bindings.rs b/rtic-macros/src/codegen/bindings.rs
index 60605f3..501ccdf 100644
--- a/rtic-macros/src/codegen/bindings.rs
+++ b/rtic-macros/src/codegen/bindings.rs
@@ -2,7 +2,8 @@
feature = "cortex-m-source-masking",
feature = "cortex-m-basepri",
feature = "test-template",
- feature = "riscv-esp32c3"
+ feature = "riscv-esp32c3",
+ feature = "riscv-slic",
)))]
compile_error!("No backend selected");
@@ -22,4 +23,10 @@ mod template;
pub use esp32c3::*;
#[cfg(feature = "riscv-esp32c3")]
-mod esp32c3; \ No newline at end of file
+mod esp32c3;
+
+#[cfg(feature = "riscv-slic")]
+pub use riscv_slic::*;
+
+#[cfg(feature = "riscv-slic")]
+mod riscv_slic;
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![]
+}
diff --git a/rtic-macros/src/codegen/extra_mods.rs b/rtic-macros/src/codegen/extra_mods.rs
new file mode 100644
index 0000000..b92dda6
--- /dev/null
+++ b/rtic-macros/src/codegen/extra_mods.rs
@@ -0,0 +1,9 @@
+use super::bindings::extra_modules;
+use crate::analyze::Analysis;
+use crate::syntax::ast::App;
+use proc_macro2::TokenStream as TokenStream2;
+
+/// Generates code that runs before `#[init]`
+pub fn codegen(app: &App, analysis: &Analysis) -> Vec<TokenStream2> {
+ extra_modules(app, analysis)
+}
diff --git a/rtic-macros/src/codegen/main.rs b/rtic-macros/src/codegen/main.rs
index 5612796..80f2cf6 100644
--- a/rtic-macros/src/codegen/main.rs
+++ b/rtic-macros/src/codegen/main.rs
@@ -1,4 +1,3 @@
-use super::{assertions, post_init, pre_init};
use crate::{
analyze::Analysis,
codegen::{bindings, util},
@@ -7,8 +6,12 @@ use crate::{
use proc_macro2::TokenStream as TokenStream2;
use quote::quote;
+use super::{assertions, extra_mods, post_init, pre_init};
+
/// Generates code for `fn main`
pub fn codegen(app: &App, analysis: &Analysis) -> TokenStream2 {
+ let extra_mods_stmts = extra_mods::codegen(app, analysis);
+
let assertion_stmts = assertions::codegen(app, analysis);
let pre_init_stmts = pre_init::codegen(app, analysis);
@@ -40,9 +43,18 @@ pub fn codegen(app: &App, analysis: &Analysis) -> TokenStream2 {
let main = util::suffixed("main");
let init_name = &app.init.name;
+
+ let init_args = if app.args.core {
+ quote!(core.into(), executors_size)
+ } else {
+ quote!(executors_size)
+ };
+
let msp_check = bindings::check_stack_overflow_before_init(app, analysis);
quote!(
+ #(#extra_mods_stmts)*
+
#[doc(hidden)]
#[no_mangle]
unsafe extern "C" fn #main() -> ! {
@@ -63,7 +75,7 @@ pub fn codegen(app: &App, analysis: &Analysis) -> TokenStream2 {
// 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(), executors_size));
+ let (shared_resources, local_resources) = #init_name(#init_name::Context::new(#init_args));
#(#post_init_stmts)*
});
diff --git a/rtic-macros/src/codegen/module.rs b/rtic-macros/src/codegen/module.rs
index c8afe07..17c8ce7 100644
--- a/rtic-macros/src/codegen/module.rs
+++ b/rtic-macros/src/codegen/module.rs
@@ -1,5 +1,5 @@
use crate::syntax::{ast::App, Context};
-use crate::{analyze::Analysis, codegen::util};
+use crate::{analyze::Analysis, codegen::bindings::interrupt_mod, codegen::util};
use proc_macro2::TokenStream as TokenStream2;
use quote::quote;
@@ -17,15 +17,19 @@ pub fn codegen(ctxt: Context, app: &App, analysis: &Analysis) -> TokenStream2 {
match ctxt {
Context::Init => {
fields.push(quote!(
- /// Core peripherals
- pub core: rtic::export::Peripherals
- ));
-
- fields.push(quote!(
/// The space used to allocate async executors in bytes.
pub executors_size: usize
));
+ if app.args.core {
+ fields.push(quote!(
+ /// Core peripherals
+ pub core: rtic::export::Peripherals
+ ));
+
+ values.push(quote!(core: core));
+ }
+
if app.args.peripherals {
let device = &app.args.device;
@@ -43,8 +47,6 @@ pub fn codegen(ctxt: Context, app: &App, analysis: &Analysis) -> TokenStream2 {
));
values.push(quote!(cs: rtic::export::CriticalSection::new()));
-
- values.push(quote!(core));
values.push(quote!(executors_size));
}
@@ -98,7 +100,11 @@ pub fn codegen(ctxt: Context, app: &App, analysis: &Analysis) -> TokenStream2 {
};
let core = if ctxt.is_init() {
- Some(quote!(core: rtic::export::Peripherals, executors_size: usize))
+ if app.args.core {
+ Some(quote!(core: rtic::export::Peripherals, executors_size: usize))
+ } else {
+ Some(quote!(executors_size: usize))
+ }
} else {
None
};
@@ -144,10 +150,9 @@ pub fn codegen(ctxt: Context, app: &App, analysis: &Analysis) -> TokenStream2 {
task_cfgs = cfgs.clone();
let pend_interrupt = if priority > 0 {
- let device = &app.args.device;
- let enum_ = util::interrupt_ident();
+ let int_mod = interrupt_mod(app);
let interrupt = &analysis.interrupts.get(&priority).expect("UREACHABLE").0;
- quote!(rtic::export::pend(#device::#enum_::#interrupt);)
+ quote!(rtic::export::pend(#int_mod::#interrupt);)
} else {
quote!()
};
diff --git a/rtic-macros/src/codegen/pre_init.rs b/rtic-macros/src/codegen/pre_init.rs
index a2d0e8c..8de75ff 100644
--- a/rtic-macros/src/codegen/pre_init.rs
+++ b/rtic-macros/src/codegen/pre_init.rs
@@ -11,10 +11,12 @@ pub fn codegen(app: &App, analysis: &Analysis) -> Vec<TokenStream2> {
// 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();
- ));
+ if app.args.core {
+ 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();
+ ));
+ }
stmts.append(&mut pre_init_checks(app, analysis));
diff --git a/rtic-macros/src/codegen/util.rs b/rtic-macros/src/codegen/util.rs
index b4682ee..dda7e29 100644
--- a/rtic-macros/src/codegen/util.rs
+++ b/rtic-macros/src/codegen/util.rs
@@ -3,8 +3,6 @@ use core::sync::atomic::{AtomicUsize, Ordering};
use proc_macro2::{Span, TokenStream as TokenStream2};
use quote::quote;
use syn::{Ident, PatType};
-//hook the target specific interrupt_ident function
-pub use super::bindings::interrupt_ident;
const RTIC_INTERNAL: &str = "__rtic_internal";
diff --git a/rtic-macros/src/lib.rs b/rtic-macros/src/lib.rs
index 38eed8f..c464ab0 100644
--- a/rtic-macros/src/lib.rs
+++ b/rtic-macros/src/lib.rs
@@ -14,13 +14,14 @@ macro_rules! with_backend {
feature = "cortex-m-source-masking",
feature = "cortex-m-basepri",
feature = "test-template",
- feature = "riscv-esp32c3"
+ feature = "riscv-esp32c3",
+ feature = "riscv-slic",
))]
$($tokens)*
};
}
-with_backend! { mod: [analyze, check, codegen, syntax] }
+with_backend! { mod: [analyze, check, codegen, preprocess, syntax] }
with_backend! { use std::{fs, env, path::Path}; }
with_backend! { use proc_macro::TokenStream; }
@@ -47,11 +48,18 @@ with_backend! {
/// Should never panic, cargo feeds a path which is later converted to a string
#[proc_macro_attribute]
pub fn app(_args: TokenStream, _input: TokenStream) -> TokenStream {
- let (app, analysis) = match syntax::parse(_args, _input) {
+ let (mut app, analysis) = match syntax::parse(_args, _input) {
Err(e) => return e.to_compile_error().into(),
Ok(x) => x,
};
+ // Modify app based on backend before continuing
+ if let Err(e) = preprocess::app(&mut app, &analysis) {
+ return e.to_compile_error().into();
+ }
+ let app = app;
+ // App is not mutable after this point
+
if let Err(e) = check::app(&app, &analysis) {
return e.to_compile_error().into();
}
@@ -109,6 +117,7 @@ with_backend! {
feature = "cortex-m-source-masking",
feature = "cortex-m-basepri",
feature = "test-template",
- feature = "riscv-esp32c3"
+ feature = "riscv-esp32c3",
+ feature = "riscv-slic",
)))]
compile_error!("Cannot compile. No backend feature selected.");
diff --git a/rtic-macros/src/preprocess.rs b/rtic-macros/src/preprocess.rs
new file mode 100644
index 0000000..2fcd10b
--- /dev/null
+++ b/rtic-macros/src/preprocess.rs
@@ -0,0 +1,7 @@
+use crate::codegen::bindings::pre_init_preprocessing;
+use crate::syntax::{analyze::Analysis, ast::App};
+use syn::parse;
+
+pub fn app(app: &mut App, analysis: &Analysis) -> parse::Result<()> {
+ pre_init_preprocessing(app, analysis)
+}
diff --git a/rtic-macros/src/syntax.rs b/rtic-macros/src/syntax.rs
index d6f5a47..a44b1ec 100644
--- a/rtic-macros/src/syntax.rs
+++ b/rtic-macros/src/syntax.rs
@@ -12,6 +12,7 @@ use crate::syntax::ast::App;
mod accessors;
pub mod analyze;
pub mod ast;
+mod backend;
mod check;
mod parse;
diff --git a/rtic-macros/src/syntax/ast.rs b/rtic-macros/src/syntax/ast.rs
index f0067b8..06feb1f 100644
--- a/rtic-macros/src/syntax/ast.rs
+++ b/rtic-macros/src/syntax/ast.rs
@@ -2,7 +2,7 @@
use syn::{Attribute, Expr, Ident, Item, ItemUse, Pat, PatType, Path, Stmt, Type};
-use crate::syntax::Map;
+use crate::syntax::{backend::BackendArgs, Map};
/// The `#[app]` attribute
#[derive(Debug)]
@@ -60,11 +60,17 @@ pub struct AppArgs {
/// Device
pub device: Path,
- /// Peripherals
+ /// Core peripherals
+ pub core: bool,
+
+ /// Device peripherals
pub peripherals: bool,
/// Interrupts used to dispatch software tasks
pub dispatchers: Dispatchers,
+
+ /// Backend-specific arguments
+ pub backend: Option<BackendArgs>,
}
/// The `init`-ialization function
diff --git a/rtic-macros/src/syntax/backend.rs b/rtic-macros/src/syntax/backend.rs
new file mode 100644
index 0000000..460ef56
--- /dev/null
+++ b/rtic-macros/src/syntax/backend.rs
@@ -0,0 +1,32 @@
+#[cfg(not(any(
+ feature = "cortex-m-source-masking",
+ feature = "cortex-m-basepri",
+ feature = "test-template",
+ feature = "riscv-esp32c3",
+ feature = "riscv-slic",
+)))]
+compile_error!("No backend selected");
+
+#[cfg(any(feature = "cortex-m-source-masking", feature = "cortex-m-basepri"))]
+pub use cortex::*;
+
+#[cfg(feature = "test-template")]
+pub use template::*;
+
+#[cfg(feature = "riscv-esp32c3")]
+pub use esp32c3::*;
+
+#[cfg(feature = "riscv-slic")]
+pub use riscv_slic::*;
+
+#[cfg(any(feature = "cortex-m-source-masking", feature = "cortex-m-basepri"))]
+mod cortex;
+
+#[cfg(feature = "test-template")]
+mod template;
+
+#[cfg(feature = "riscv-esp32c3")]
+mod esp32c3;
+
+#[cfg(feature = "riscv-slic")]
+mod riscv_slic;
diff --git a/rtic-macros/src/syntax/backend/cortex.rs b/rtic-macros/src/syntax/backend/cortex.rs
new file mode 100644
index 0000000..b53e927
--- /dev/null
+++ b/rtic-macros/src/syntax/backend/cortex.rs
@@ -0,0 +1,16 @@
+use syn::{
+ parse::{Parse, ParseStream},
+ Error, Result,
+};
+
+#[derive(Debug)]
+pub struct BackendArgs();
+
+impl Parse for BackendArgs {
+ fn parse(input: ParseStream) -> Result<Self> {
+ Err(Error::new(
+ input.span(),
+ "cortex backend does not accept any arguments",
+ ))
+ }
+}
diff --git a/rtic-macros/src/syntax/backend/esp32c3.rs b/rtic-macros/src/syntax/backend/esp32c3.rs
new file mode 100644
index 0000000..33143f4
--- /dev/null
+++ b/rtic-macros/src/syntax/backend/esp32c3.rs
@@ -0,0 +1,16 @@
+use syn::{
+ parse::{Parse, ParseStream},
+ Error, Result,
+};
+
+#[derive(Debug)]
+pub struct BackendArgs();
+
+impl Parse for BackendArgs {
+ fn parse(input: ParseStream) -> Result<Self> {
+ Err(Error::new(
+ input.span(),
+ "esp32c3 backend does not accept any arguments",
+ ))
+ }
+}
diff --git a/rtic-macros/src/syntax/backend/riscv_slic.rs b/rtic-macros/src/syntax/backend/riscv_slic.rs
new file mode 100644
index 0000000..2ed8e77
--- /dev/null
+++ b/rtic-macros/src/syntax/backend/riscv_slic.rs
@@ -0,0 +1,16 @@
+use syn::{
+ parse::{Parse, ParseStream},
+ Ident, Result,
+};
+
+#[derive(Debug)]
+pub struct BackendArgs {
+ pub hart_id: Ident,
+}
+
+impl Parse for BackendArgs {
+ fn parse(input: ParseStream) -> Result<Self> {
+ let hart_id = input.parse()?;
+ Ok(BackendArgs { hart_id })
+ }
+}
diff --git a/rtic-macros/src/syntax/backend/template.rs b/rtic-macros/src/syntax/backend/template.rs
new file mode 100644
index 0000000..6dad114
--- /dev/null
+++ b/rtic-macros/src/syntax/backend/template.rs
@@ -0,0 +1,15 @@
+use syn::{
+ parse::{Parse, ParseStream},
+ Result,
+};
+
+#[derive(Debug)]
+pub struct BackendArgs {
+ // Define your backend-specific input here
+}
+
+impl Parse for BackendArgs {
+ fn parse(input: ParseStream) -> Result<Self> {
+ todo!("define how to parse your backend-specific arguments")
+ }
+}
diff --git a/rtic-macros/src/syntax/parse/app.rs b/rtic-macros/src/syntax/parse/app.rs
index efcafbe..469bcb8 100644
--- a/rtic-macros/src/syntax/parse/app.rs
+++ b/rtic-macros/src/syntax/parse/app.rs
@@ -13,6 +13,7 @@ use crate::syntax::{
App, AppArgs, Dispatcher, Dispatchers, HardwareTask, Idle, IdleArgs, Init, InitArgs,
LocalResource, SharedResource, SoftwareTask,
},
+ backend::BackendArgs,
parse::{self as syntax_parse, util},
Either, Map, Set,
};
@@ -24,8 +25,10 @@ impl AppArgs {
(|input: ParseStream<'_>| -> parse::Result<Self> {
let mut custom = Set::new();
let mut device = None;
+ let mut core = true;
let mut peripherals = true;
let mut dispatchers = Dispatchers::new();
+ let mut backend = None;
loop {
if input.is_empty() {
@@ -59,6 +62,17 @@ impl AppArgs {
}
}
+ "core" => {
+ if let Ok(p) = input.parse::<LitBool>() {
+ core = p.value;
+ } else {
+ return Err(parse::Error::new(
+ ident.span(),
+ "unexpected argument value; this should be a boolean",
+ ));
+ }
+ }
+
"peripherals" => {
if let Ok(p) = input.parse::<LitBool>() {
peripherals = p.value;
@@ -113,6 +127,18 @@ impl AppArgs {
));
}
}
+
+ "backend" => {
+ if let Ok(p) = input.parse::<BackendArgs>() {
+ backend = Some(p);
+ } else {
+ return Err(parse::Error::new(
+ ident.span(),
+ "unable to parse backend configuration",
+ ));
+ }
+ }
+
_ => {
return Err(parse::Error::new(ident.span(), "unexpected argument"));
}
@@ -134,8 +160,10 @@ impl AppArgs {
Ok(AppArgs {
device,
+ core,
peripherals,
dispatchers,
+ backend,
})
})
.parse2(tokens)