mirror of
https://github.com/rtic-rs/rtic.git
synced 2025-12-16 21:05:35 +01:00
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>
This commit is contained in:
parent
22ac33a826
commit
4060c3def8
166 changed files with 2322 additions and 315 deletions
|
|
@ -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.
|
||||
|
||||
|
|
|
|||
|
|
@ -36,8 +36,8 @@ cortex-m-basepri = []
|
|||
riscv-esp32c3 = []
|
||||
# riscv-clic = []
|
||||
# riscv-ch32 = []
|
||||
|
||||
|
||||
riscv-slic = []
|
||||
|
||||
# backend API test
|
||||
test-template = []
|
||||
|
||||
|
|
|
|||
|
|
@ -8,6 +8,7 @@ pub mod bindings;
|
|||
|
||||
mod assertions;
|
||||
mod async_dispatchers;
|
||||
mod extra_mods;
|
||||
mod hardware_tasks;
|
||||
mod idle;
|
||||
mod init;
|
||||
|
|
|
|||
|
|
@ -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!()
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
mod esp32c3;
|
||||
|
||||
#[cfg(feature = "riscv-slic")]
|
||||
pub use riscv_slic::*;
|
||||
|
||||
#[cfg(feature = "riscv-slic")]
|
||||
mod riscv_slic;
|
||||
|
|
|
|||
|
|
@ -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![]
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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![]
|
||||
}
|
||||
|
|
|
|||
255
rtic-macros/src/codegen/bindings/riscv_slic.rs
Normal file
255
rtic-macros/src/codegen/bindings/riscv_slic.rs
Normal file
|
|
@ -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
|
||||
}
|
||||
|
|
@ -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> {
|
||||
vec![]
|
||||
}
|
||||
|
||||
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 pre_init_preprocessing(app: &mut App, analysis: &SyntaxAnalysis) -> parse::Result<()> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn interrupt_entry(_app: &App, _analysis: &CodegenAnalysis) -> Vec<TokenStream2> {
|
||||
pub fn pre_init_checks(app: &App, analysis: &SyntaxAnalysis) -> Vec<TokenStream2> {
|
||||
vec![]
|
||||
}
|
||||
|
||||
pub fn interrupt_exit(_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<()> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn interrupt_entry(app: &App, analysis: &CodegenAnalysis) -> Vec<TokenStream2> {
|
||||
vec![]
|
||||
}
|
||||
|
||||
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,
|
||||
) -> Vec<TokenStream2> {
|
||||
vec![]
|
||||
}
|
||||
|
||||
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 async_prio_limit(app: &App, analysis: &CodegenAnalysis) -> Vec<TokenStream2> {
|
||||
vec![]
|
||||
}
|
||||
|
||||
pub fn handler_config(
|
||||
app: &App,
|
||||
analysis: &CodegenAnalysis,
|
||||
dispatcher_name: Ident,
|
||||
) -> Vec<TokenStream2> {
|
||||
vec![]
|
||||
}
|
||||
|
||||
pub fn extra_modules(app: &App, analysis: &SyntaxAnalysis) -> Vec<TokenStream2> {
|
||||
vec![]
|
||||
}
|
||||
|
|
|
|||
9
rtic-macros/src/codegen/extra_mods.rs
Normal file
9
rtic-macros/src/codegen/extra_mods.rs
Normal file
|
|
@ -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)
|
||||
}
|
||||
|
|
@ -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)*
|
||||
});
|
||||
|
|
|
|||
|
|
@ -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!()
|
||||
};
|
||||
|
|
|
|||
|
|
@ -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));
|
||||
|
||||
|
|
|
|||
|
|
@ -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";
|
||||
|
||||
|
|
|
|||
|
|
@ -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.");
|
||||
|
|
|
|||
7
rtic-macros/src/preprocess.rs
Normal file
7
rtic-macros/src/preprocess.rs
Normal file
|
|
@ -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)
|
||||
}
|
||||
|
|
@ -12,6 +12,7 @@ use crate::syntax::ast::App;
|
|||
mod accessors;
|
||||
pub mod analyze;
|
||||
pub mod ast;
|
||||
mod backend;
|
||||
mod check;
|
||||
mod parse;
|
||||
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
32
rtic-macros/src/syntax/backend.rs
Normal file
32
rtic-macros/src/syntax/backend.rs
Normal file
|
|
@ -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;
|
||||
16
rtic-macros/src/syntax/backend/cortex.rs
Normal file
16
rtic-macros/src/syntax/backend/cortex.rs
Normal file
|
|
@ -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",
|
||||
))
|
||||
}
|
||||
}
|
||||
16
rtic-macros/src/syntax/backend/esp32c3.rs
Normal file
16
rtic-macros/src/syntax/backend/esp32c3.rs
Normal file
|
|
@ -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",
|
||||
))
|
||||
}
|
||||
}
|
||||
16
rtic-macros/src/syntax/backend/riscv_slic.rs
Normal file
16
rtic-macros/src/syntax/backend/riscv_slic.rs
Normal file
|
|
@ -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 })
|
||||
}
|
||||
}
|
||||
15
rtic-macros/src/syntax/backend/template.rs
Normal file
15
rtic-macros/src/syntax/backend/template.rs
Normal file
|
|
@ -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")
|
||||
}
|
||||
}
|
||||
|
|
@ -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)
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue