mirror of
https://github.com/rtic-rs/rtic.git
synced 2025-12-16 21:05:35 +01:00
esp32c3 support
This commit is contained in:
parent
3b8d787a91
commit
2b2208e217
27 changed files with 525 additions and 60 deletions
|
|
@ -7,6 +7,10 @@ For each category, *Added*, *Changed*, *Fixed* add new entries at the top!
|
|||
|
||||
## [Unreleased]
|
||||
|
||||
### Added
|
||||
|
||||
- Unstable ESP32-C3 support.
|
||||
|
||||
## [v2.0.1] - 2023-07-25
|
||||
|
||||
### Added
|
||||
|
|
|
|||
|
|
@ -33,9 +33,11 @@ default = []
|
|||
# list of supported codegen backends
|
||||
cortex-m-source-masking = []
|
||||
cortex-m-basepri = []
|
||||
riscv-esp32c3 = []
|
||||
# riscv-clic = []
|
||||
# riscv-ch32 = []
|
||||
|
||||
|
||||
# backend API test
|
||||
test-template = []
|
||||
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@ use crate::syntax::ast::App;
|
|||
use crate::{
|
||||
analyze::Analysis,
|
||||
codegen::{
|
||||
bindings::{interrupt_entry, interrupt_exit},
|
||||
bindings::{async_entry, interrupt_entry, interrupt_exit, handler_config},
|
||||
util,
|
||||
},
|
||||
};
|
||||
|
|
@ -67,14 +67,17 @@ pub fn codegen(app: &App, analysis: &Analysis) -> TokenStream2 {
|
|||
let attribute = &interrupts.get(&level).expect("UNREACHABLE").1.attrs;
|
||||
let entry_stmts = interrupt_entry(app, analysis);
|
||||
let exit_stmts = interrupt_exit(app, analysis);
|
||||
|
||||
let async_entry_stmts = async_entry(app, analysis, dispatcher_name.clone());
|
||||
let config = handler_config(app,analysis,dispatcher_name.clone());
|
||||
items.push(quote!(
|
||||
#[allow(non_snake_case)]
|
||||
#[doc = #doc]
|
||||
#[no_mangle]
|
||||
#(#attribute)*
|
||||
#(#config)*
|
||||
unsafe fn #dispatcher_name() {
|
||||
#(#entry_stmts)*
|
||||
#(#async_entry_stmts)*
|
||||
|
||||
/// The priority of this interrupt handler
|
||||
const PRIORITY: u8 = #level;
|
||||
|
|
|
|||
|
|
@ -1,3 +1,11 @@
|
|||
#[cfg(not(any(
|
||||
feature = "cortex-m-source-masking",
|
||||
feature = "cortex-m-basepri",
|
||||
feature = "test-template",
|
||||
feature = "riscv-esp32c3"
|
||||
)))]
|
||||
compile_error!("No backend selected");
|
||||
|
||||
#[cfg(any(feature = "cortex-m-source-masking", feature = "cortex-m-basepri"))]
|
||||
pub use cortex::*;
|
||||
|
||||
|
|
@ -9,3 +17,9 @@ mod cortex;
|
|||
|
||||
#[cfg(feature = "test-template")]
|
||||
mod template;
|
||||
|
||||
#[cfg(feature = "riscv-esp32c3")]
|
||||
pub use esp32c3::*;
|
||||
|
||||
#[cfg(feature = "riscv-esp32c3")]
|
||||
mod esp32c3;
|
||||
|
|
@ -3,7 +3,7 @@ use crate::{
|
|||
codegen::util,
|
||||
syntax::{analyze::Analysis as SyntaxAnalysis, ast::App},
|
||||
};
|
||||
use proc_macro2::TokenStream as TokenStream2;
|
||||
use proc_macro2::{Span, TokenStream as TokenStream2};
|
||||
use quote::quote;
|
||||
use std::collections::HashSet;
|
||||
use syn::{parse, Attribute, Ident};
|
||||
|
|
@ -29,6 +29,10 @@ fn is_exception(name: &Ident) -> bool {
|
|||
| "SysTick"
|
||||
)
|
||||
}
|
||||
pub fn interrupt_ident() -> Ident {
|
||||
let span = Span::call_site();
|
||||
Ident::new("interrupt", span)
|
||||
}
|
||||
|
||||
#[cfg(feature = "cortex-m-source-masking")]
|
||||
mod source_masking {
|
||||
|
|
@ -323,6 +327,14 @@ pub fn interrupt_exit(_app: &App, _analysis: &CodegenAnalysis) -> Vec<TokenStrea
|
|||
vec![]
|
||||
}
|
||||
|
||||
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> {
|
||||
let max = if let Some(max) = analysis.max_async_prio {
|
||||
quote!(#max)
|
||||
|
|
@ -338,3 +350,10 @@ pub fn async_prio_limit(app: &App, analysis: &CodegenAnalysis) -> Vec<TokenStrea
|
|||
static RTIC_ASYNC_MAX_LOGICAL_PRIO: u8 = #max;
|
||||
)]
|
||||
}
|
||||
pub fn handler_config(
|
||||
_app: &App,
|
||||
_analysis: &CodegenAnalysis,
|
||||
_dispatcher_name: Ident,
|
||||
) -> Vec<TokenStream2> {
|
||||
vec![]
|
||||
}
|
||||
|
|
|
|||
213
rtic-macros/src/codegen/bindings/esp32c3.rs
Normal file
213
rtic-macros/src/codegen/bindings/esp32c3.rs
Normal file
|
|
@ -0,0 +1,213 @@
|
|||
#[cfg(feature = "riscv-esp32c3")]
|
||||
pub use esp32c3::*;
|
||||
|
||||
#[cfg(feature = "riscv-esp32c3")]
|
||||
mod esp32c3 {
|
||||
use crate::{
|
||||
analyze::Analysis as CodegenAnalysis,
|
||||
codegen::util,
|
||||
syntax::{analyze::Analysis as SyntaxAnalysis, ast::App},
|
||||
};
|
||||
use proc_macro2::{Span, TokenStream as TokenStream2};
|
||||
use quote::quote;
|
||||
use std::collections::HashSet;
|
||||
use syn::{parse, Attribute, Ident};
|
||||
|
||||
#[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 {
|
||||
/// Priority ceiling
|
||||
const CEILING: u8 = #ceiling;
|
||||
unsafe {
|
||||
rtic::export::lock(
|
||||
#ptr,
|
||||
CEILING,
|
||||
f,
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
pub fn interrupt_ident() -> Ident {
|
||||
let span = Span::call_site();
|
||||
Ident::new("Interrupt", span)
|
||||
}
|
||||
|
||||
pub fn extra_assertions(_: &App, _: &SyntaxAnalysis) -> Vec<TokenStream2> {
|
||||
vec![]
|
||||
}
|
||||
|
||||
pub fn pre_init_checks(app: &App, _: &SyntaxAnalysis) -> Vec<TokenStream2> {
|
||||
let mut stmts = vec![];
|
||||
// check that all dispatchers exists in the `Interrupt` enumeration regardless of whether
|
||||
// they are used or not
|
||||
let rt_err = util::rt_err_ident();
|
||||
|
||||
for name in app.args.dispatchers.keys() {
|
||||
stmts.push(quote!(let _ = #rt_err::Interrupt::#name;));
|
||||
}
|
||||
stmts
|
||||
}
|
||||
pub fn pre_init_enable_interrupts(app: &App, analysis: &CodegenAnalysis) -> Vec<TokenStream2> {
|
||||
let mut stmts = vec![];
|
||||
let mut curr_cpu_id:u8 = 1; //cpu interrupt id 0 is reserved
|
||||
let rt_err = util::rt_err_ident();
|
||||
let max_prio: usize = 15; //unfortunately this is not part of pac, but we know that max prio is 15.
|
||||
let interrupt_ids = analysis.interrupts.iter().map(|(p, (id, _))| (p, id));
|
||||
// Unmask interrupts and set their priorities
|
||||
for (&priority, name) in interrupt_ids.chain(
|
||||
app.hardware_tasks
|
||||
.values()
|
||||
.filter_map(|task| Some((&task.args.priority, &task.args.binds))),
|
||||
) {
|
||||
let es = format!(
|
||||
"Maximum priority used by interrupt vector '{name}' is more than supported by hardware"
|
||||
);
|
||||
// Compile time assert that this priority is supported by the device
|
||||
stmts.push(quote!(
|
||||
const _: () = if (#max_prio) <= #priority as usize { ::core::panic!(#es); };
|
||||
));
|
||||
stmts.push(quote!(
|
||||
rtic::export::enable(
|
||||
#rt_err::Interrupt::#name,
|
||||
#priority,
|
||||
#curr_cpu_id,
|
||||
);
|
||||
));
|
||||
curr_cpu_id += 1;
|
||||
}
|
||||
stmts
|
||||
}
|
||||
|
||||
pub fn architecture_specific_analysis(
|
||||
app: &App,
|
||||
_analysis: &SyntaxAnalysis,
|
||||
) -> parse::Result<()> {
|
||||
//check if the dispatchers are supported
|
||||
for name in app.args.dispatchers.keys() {
|
||||
let name_s = name.to_string();
|
||||
match &*name_s {
|
||||
"FROM_CPU_INTR0" | "FROM_CPU_INTR1" | "FROM_CPU_INTR2" | "FROM_CPU_INTR3" => {}
|
||||
|
||||
_ => {
|
||||
return Err(parse::Error::new(
|
||||
name.span(),
|
||||
"Only FROM_CPU_INTRX are supported as dispatchers",
|
||||
));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Check that there are enough external interrupts to dispatch the software tasks and the timer
|
||||
// queue handler
|
||||
let mut first = None;
|
||||
let priorities = app
|
||||
.software_tasks
|
||||
.iter()
|
||||
.map(|(name, task)| {
|
||||
first = Some(name);
|
||||
task.args.priority
|
||||
})
|
||||
.filter(|prio| *prio > 0)
|
||||
.collect::<HashSet<_>>();
|
||||
|
||||
let need = priorities.len();
|
||||
let given = app.args.dispatchers.len();
|
||||
if need > given {
|
||||
let s = {
|
||||
format!(
|
||||
"not enough interrupts to dispatch \
|
||||
all software tasks (need: {need}; given: {given})"
|
||||
)
|
||||
};
|
||||
|
||||
// If not enough tasks and first still is None, may cause
|
||||
// "custom attribute panicked" due to unwrap on None
|
||||
return Err(parse::Error::new(first.unwrap().span(), s));
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn interrupt_entry(_app: &App, _analysis: &CodegenAnalysis) -> Vec<TokenStream2> {
|
||||
vec![]
|
||||
}
|
||||
|
||||
pub fn interrupt_exit(_app: &App, _analysis: &CodegenAnalysis) -> Vec<TokenStream2> {
|
||||
vec![]
|
||||
}
|
||||
|
||||
pub fn async_entry(
|
||||
_app: &App,
|
||||
_analysis: &CodegenAnalysis,
|
||||
dispatcher_name: Ident,
|
||||
) -> Vec<TokenStream2> {
|
||||
let mut stmts = vec![];
|
||||
stmts.push(quote!(
|
||||
rtic::export::unpend(rtic::export::Interrupt::#dispatcher_name); //simulate cortex-m behavior by unpending the interrupt on entry.
|
||||
));
|
||||
stmts
|
||||
}
|
||||
|
||||
pub fn async_prio_limit(app: &App, analysis: &CodegenAnalysis) -> Vec<TokenStream2> {
|
||||
let max = if let Some(max) = analysis.max_async_prio {
|
||||
quote!(#max)
|
||||
} else {
|
||||
// No limit
|
||||
let device = &app.args.device;
|
||||
quote!(1 << #device::NVIC_PRIO_BITS)
|
||||
};
|
||||
|
||||
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> {
|
||||
let mut stmts = vec![];
|
||||
let mut curr_cpu_id = 1;
|
||||
//let mut ret = "";
|
||||
let interrupt_ids = analysis.interrupts.iter().map(|(p, (id, _))| (p, id));
|
||||
for (_, name) in interrupt_ids.chain(
|
||||
app.hardware_tasks
|
||||
.values()
|
||||
.filter_map(|task| Some((&task.args.priority, &task.args.binds))),
|
||||
) {
|
||||
if *name == dispatcher_name{
|
||||
let ret = &("cpu_int_".to_owned()+&curr_cpu_id.to_string()+"_handler");
|
||||
stmts.push(
|
||||
quote!(#[export_name = #ret])
|
||||
);
|
||||
}
|
||||
curr_cpu_id += 1;
|
||||
}
|
||||
|
||||
stmts
|
||||
}
|
||||
}
|
||||
|
|
@ -43,6 +43,21 @@ pub fn interrupt_exit(_app: &App, _analysis: &CodegenAnalysis) -> Vec<TokenStrea
|
|||
vec![]
|
||||
}
|
||||
|
||||
pub fn async_prio_limit(_app: &App, _analysis: &CodegenAnalysis) -> Vec<TokenStream2> {
|
||||
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,
|
||||
dispatcher_name: Ident,
|
||||
) -> Vec<TokenStream2> {
|
||||
vec![]
|
||||
}
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@ use crate::syntax::{ast::App, Context};
|
|||
use crate::{
|
||||
analyze::Analysis,
|
||||
codegen::{
|
||||
bindings::{interrupt_entry, interrupt_exit},
|
||||
bindings::{interrupt_entry, interrupt_exit, handler_config},
|
||||
local_resources_struct, module, shared_resources_struct,
|
||||
},
|
||||
};
|
||||
|
|
@ -22,12 +22,14 @@ pub fn codegen(app: &App, analysis: &Analysis) -> TokenStream2 {
|
|||
let attrs = &task.attrs;
|
||||
let entry_stmts = interrupt_entry(app, analysis);
|
||||
let exit_stmts = interrupt_exit(app, analysis);
|
||||
let config = handler_config(app, analysis, symbol.clone());
|
||||
|
||||
mod_app.push(quote!(
|
||||
#[allow(non_snake_case)]
|
||||
#[no_mangle]
|
||||
#(#attrs)*
|
||||
#(#cfgs)*
|
||||
#(#config)*
|
||||
unsafe fn #symbol() {
|
||||
#(#entry_stmts)*
|
||||
|
||||
|
|
|
|||
|
|
@ -3,13 +3,11 @@ 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";
|
||||
|
||||
pub fn interrupt_ident() -> Ident {
|
||||
let span = Span::call_site();
|
||||
Ident::new("interrupt", span)
|
||||
}
|
||||
|
||||
/// Mark a name as internal
|
||||
pub fn mark_internal_name(name: &str) -> Ident {
|
||||
|
|
|
|||
|
|
@ -13,7 +13,8 @@ macro_rules! with_backend {
|
|||
#[cfg(any(
|
||||
feature = "cortex-m-source-masking",
|
||||
feature = "cortex-m-basepri",
|
||||
feature = "test-template"
|
||||
feature = "test-template",
|
||||
feature = "riscv-esp32c3"
|
||||
))]
|
||||
$($tokens)*
|
||||
};
|
||||
|
|
@ -107,6 +108,7 @@ with_backend! {
|
|||
#[cfg(not(any(
|
||||
feature = "cortex-m-source-masking",
|
||||
feature = "cortex-m-basepri",
|
||||
feature = "test-template"
|
||||
feature = "test-template",
|
||||
feature = "riscv-esp32c3"
|
||||
)))]
|
||||
compile_error!("Cannot compile. No backend feature selected.");
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue