Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Adding support for the ESP32C3 #733

Merged
merged 2 commits into from
Sep 27, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions rtic-common/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,4 +13,8 @@ For each category, *Added*, *Changed*, *Fixed* add new entries at the top!

### Fixed

## [v1.0.1]

- `portable-atomic` used as a drop in replacement for `core::sync::atomic` in code and macros. `portable-atomic` imported with `default-features = false`, as we do not require CAS.

## [v1.0.0] - 2023-05-31
3 changes: 2 additions & 1 deletion rtic-common/Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[package]
name = "rtic-common"
version = "1.0.0"
version = "1.0.1"

edition = "2021"
authors = [
Expand All @@ -18,6 +18,7 @@ license = "MIT OR Apache-2.0"

[dependencies]
critical-section = "1"
portable-atomic = { version = "1", default-features = false }

[features]
default = []
Expand Down
2 changes: 1 addition & 1 deletion rtic-common/src/wait_queue.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,9 @@
use core::marker::PhantomPinned;
use core::pin::Pin;
use core::ptr::null_mut;
use core::sync::atomic::{AtomicBool, AtomicPtr, Ordering};
use core::task::Waker;
use critical_section as cs;
use portable_atomic::{AtomicBool, AtomicPtr, Ordering};

/// A helper definition of a wait queue.
pub type WaitQueue = DoublyLinkedList<Waker>;
Expand Down
4 changes: 4 additions & 0 deletions rtic-macros/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
2 changes: 2 additions & 0 deletions rtic-macros/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -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 = []

Expand Down
7 changes: 5 additions & 2 deletions rtic-macros/src/codegen/async_dispatchers.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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,
},
};
Expand Down Expand Up @@ -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;
Expand Down
14 changes: 14 additions & 0 deletions rtic-macros/src/codegen/bindings.rs
Original file line number Diff line number Diff line change
@@ -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::*;

Expand All @@ -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;
21 changes: 20 additions & 1 deletion rtic-macros/src/codegen/bindings/cortex.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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};
Expand All @@ -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 {
Expand Down Expand Up @@ -323,6 +327,14 @@ pub fn interrupt_exit(_app: &App, _analysis: &CodegenAnalysis) -> Vec<TokenStrea
vec![]
}

pub fn async_entry(
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I guess this new hook is required by ESP32C3 for un-pending the dispatchers' interrupt, right?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yep

_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)
Expand All @@ -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 changes: 213 additions & 0 deletions rtic-macros/src/codegen/bindings/esp32c3.rs
Original file line number Diff line number Diff line change
@@ -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
}
}
17 changes: 16 additions & 1 deletion rtic-macros/src/codegen/bindings/template.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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![]
}
Loading