From 4060c3def88f82d4e4f48de7137ce365167ef265 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rom=C3=A1n=20C=C3=A1rdenas=20Rodr=C3=ADguez?= Date: Wed, 20 Mar 2024 21:06:47 +0100 Subject: RISC-V support over CLINT (#815) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * 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 * 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 --- rtic-macros/CHANGELOG.md | 1 + rtic-macros/Cargo.toml | 4 +- rtic-macros/src/codegen.rs | 1 + rtic-macros/src/codegen/async_dispatchers.rs | 7 +- rtic-macros/src/codegen/bindings.rs | 11 +- rtic-macros/src/codegen/bindings/cortex.rs | 18 +- rtic-macros/src/codegen/bindings/esp32c3.rs | 14 ++ rtic-macros/src/codegen/bindings/riscv_slic.rs | 255 +++++++++++++++++++++++++ rtic-macros/src/codegen/bindings/template.rs | 60 ++++-- rtic-macros/src/codegen/extra_mods.rs | 9 + rtic-macros/src/codegen/main.rs | 16 +- rtic-macros/src/codegen/module.rs | 29 +-- rtic-macros/src/codegen/pre_init.rs | 10 +- rtic-macros/src/codegen/util.rs | 2 - rtic-macros/src/lib.rs | 17 +- rtic-macros/src/preprocess.rs | 7 + rtic-macros/src/syntax.rs | 1 + rtic-macros/src/syntax/ast.rs | 10 +- rtic-macros/src/syntax/backend.rs | 32 ++++ rtic-macros/src/syntax/backend/cortex.rs | 16 ++ rtic-macros/src/syntax/backend/esp32c3.rs | 16 ++ rtic-macros/src/syntax/backend/riscv_slic.rs | 16 ++ rtic-macros/src/syntax/backend/template.rs | 15 ++ rtic-macros/src/syntax/parse/app.rs | 28 +++ 24 files changed, 539 insertions(+), 56 deletions(-) create mode 100644 rtic-macros/src/codegen/bindings/riscv_slic.rs create mode 100644 rtic-macros/src/codegen/extra_mods.rs create mode 100644 rtic-macros/src/preprocess.rs create mode 100644 rtic-macros/src/syntax/backend.rs create mode 100644 rtic-macros/src/syntax/backend/cortex.rs create mode 100644 rtic-macros/src/syntax/backend/esp32c3.rs create mode 100644 rtic-macros/src/syntax/backend/riscv_slic.rs create mode 100644 rtic-macros/src/syntax/backend/template.rs (limited to 'rtic-macros') 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 { 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 { pub fn pre_init_enable_interrupts(app: &App, analysis: &CodegenAnalysis) -> Vec { 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 { vec![] } + +pub fn extra_modules(_app: &App, _analysis: &SyntaxAnalysis) -> Vec { + 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 { vec![] } + pub fn pre_init_preprocessing(_app: &mut App, _analysis: &SyntaxAnalysis) -> parse::Result<()> { + Ok(()) + } + 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 @@ -232,3 +242,7 @@ mod esp32c3 { stmts } } + +pub fn extra_modules(_app: &App, _analysis: &SyntaxAnalysis) -> Vec { + 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(&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 { + 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::>(); + + 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 { + let mut stmts: Vec = 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 { + 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::>(); + + 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 { + 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 { + vec![] +} + +pub fn check_stack_overflow_before_init( + _app: &App, + _analysis: &CodegenAnalysis, +) -> Vec { + 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 { + vec![] +} + +/// Macro to define a maximum priority level for async tasks. +pub fn async_prio_limit(_app: &App, analysis: &CodegenAnalysis) -> Vec { + 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 { + 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 { + 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 { +pub fn extra_assertions(app: &App, analysis: &SyntaxAnalysis) -> Vec { vec![] } -pub fn pre_init_checks(_app: &App, _analysis: &SyntaxAnalysis) -> Vec { +pub fn pre_init_preprocessing(app: &mut App, analysis: &SyntaxAnalysis) -> parse::Result<()> { + Ok(()) +} + +pub fn pre_init_checks(app: &App, analysis: &SyntaxAnalysis) -> Vec { vec![] } -pub fn pre_init_enable_interrupts(_app: &App, _analysis: &CodegenAnalysis) -> Vec { +pub fn pre_init_enable_interrupts(app: &App, analysis: &CodegenAnalysis) -> Vec { 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 { +pub fn interrupt_entry(app: &App, analysis: &CodegenAnalysis) -> Vec { vec![] } -pub fn interrupt_exit(_app: &App, _analysis: &CodegenAnalysis) -> Vec { +pub fn interrupt_exit(app: &App, analysis: &CodegenAnalysis) -> Vec { 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 { vec![] } -pub fn async_prio_limit(app: &App, _analysis: &CodegenAnalysis) -> Vec { +pub fn async_prio_limit(app: &App, analysis: &CodegenAnalysis) -> Vec { vec![] } + pub fn handler_config( - _app: &App, - _analysis: &CodegenAnalysis, + app: &App, + analysis: &CodegenAnalysis, dispatcher_name: Ident, ) -> Vec { vec![] } + +pub fn extra_modules(app: &App, analysis: &SyntaxAnalysis) -> Vec { + 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 { + 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; @@ -16,16 +16,20 @@ 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 { // 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, } /// 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 { + 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 { + 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 { + 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 { + 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 { 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::() { + 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::() { peripherals = p.value; @@ -113,6 +127,18 @@ impl AppArgs { )); } } + + "backend" => { + if let Ok(p) = input.parse::() { + 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) -- cgit v1.2.3