aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authoronsdagens <pawdzi-7@student.ltu.se>2023-09-27 21:39:35 +0200
committerEmil Fresk <emil.fresk@gmail.com>2023-09-27 19:50:13 +0000
commit2b2208e217a96086696bd6f36cff2a6cd4c4ac9f (patch)
treec17d993fd70954663c0f0ffb6b6afed5228f9714
parent3b8d787a917a7a39b28bea85ba2b3a86539e0852 (diff)
esp32c3 support
-rw-r--r--rtic-common/CHANGELOG.md4
-rw-r--r--rtic-common/Cargo.toml3
-rw-r--r--rtic-common/src/wait_queue.rs2
-rw-r--r--rtic-macros/CHANGELOG.md4
-rw-r--r--rtic-macros/Cargo.toml2
-rw-r--r--rtic-macros/src/codegen/async_dispatchers.rs7
-rw-r--r--rtic-macros/src/codegen/bindings.rs14
-rw-r--r--rtic-macros/src/codegen/bindings/cortex.rs21
-rw-r--r--rtic-macros/src/codegen/bindings/esp32c3.rs213
-rw-r--r--rtic-macros/src/codegen/bindings/template.rs17
-rw-r--r--rtic-macros/src/codegen/hardware_tasks.rs4
-rw-r--r--rtic-macros/src/codegen/util.rs6
-rw-r--r--rtic-macros/src/lib.rs6
-rw-r--r--rtic-sync/CHANGELOG.md8
-rw-r--r--rtic-sync/Cargo.toml3
-rw-r--r--rtic-sync/src/arbiter.rs2
-rw-r--r--rtic-sync/src/channel.rs2
-rw-r--r--rtic-sync/src/lib.rs1
-rw-r--r--rtic/CHANGELOG.md1
-rw-r--r--rtic/Cargo.toml5
-rw-r--r--rtic/build.rs6
-rw-r--r--rtic/src/export.rs58
-rw-r--r--rtic/src/export/cortex_basepri.rs5
-rw-r--r--rtic/src/export/cortex_common.rs18
-rw-r--r--rtic/src/export/cortex_source_mask.rs5
-rw-r--r--rtic/src/export/riscv_common.rs2
-rw-r--r--rtic/src/export/riscv_esp32c3.rs164
27 files changed, 524 insertions, 59 deletions
diff --git a/rtic-common/CHANGELOG.md b/rtic-common/CHANGELOG.md
index f90db79..1fe270b 100644
--- a/rtic-common/CHANGELOG.md
+++ b/rtic-common/CHANGELOG.md
@@ -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
diff --git a/rtic-common/Cargo.toml b/rtic-common/Cargo.toml
index 1e7f98f..cd22f72 100644
--- a/rtic-common/Cargo.toml
+++ b/rtic-common/Cargo.toml
@@ -1,6 +1,6 @@
[package]
name = "rtic-common"
-version = "1.0.0"
+version = "1.0.1"
edition = "2021"
authors = [
@@ -18,6 +18,7 @@ license = "MIT OR Apache-2.0"
[dependencies]
critical-section = "1"
+portable-atomic = { version = "1", default-features = false }
[features]
default = []
diff --git a/rtic-common/src/wait_queue.rs b/rtic-common/src/wait_queue.rs
index 4b1b0f3..ef3afe8 100644
--- a/rtic-common/src/wait_queue.rs
+++ b/rtic-common/src/wait_queue.rs
@@ -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>;
diff --git a/rtic-macros/CHANGELOG.md b/rtic-macros/CHANGELOG.md
index 3413d8a..6a1fc54 100644
--- a/rtic-macros/CHANGELOG.md
+++ b/rtic-macros/CHANGELOG.md
@@ -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
diff --git a/rtic-macros/Cargo.toml b/rtic-macros/Cargo.toml
index ade6842..e88d1bc 100644
--- a/rtic-macros/Cargo.toml
+++ b/rtic-macros/Cargo.toml
@@ -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 = []
diff --git a/rtic-macros/src/codegen/async_dispatchers.rs b/rtic-macros/src/codegen/async_dispatchers.rs
index 289a63b..54db2b3 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::{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;
diff --git a/rtic-macros/src/codegen/bindings.rs b/rtic-macros/src/codegen/bindings.rs
index c328ee0..60605f3 100644
--- a/rtic-macros/src/codegen/bindings.rs
+++ b/rtic-macros/src/codegen/bindings.rs
@@ -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; \ No newline at end of file
diff --git a/rtic-macros/src/codegen/bindings/cortex.rs b/rtic-macros/src/codegen/bindings/cortex.rs
index eba2afc..ffa0245 100644
--- a/rtic-macros/src/codegen/bindings/cortex.rs
+++ b/rtic-macros/src/codegen/bindings/cortex.rs
@@ -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![]
+}
diff --git a/rtic-macros/src/codegen/bindings/esp32c3.rs b/rtic-macros/src/codegen/bindings/esp32c3.rs
new file mode 100644
index 0000000..a8d58b7
--- /dev/null
+++ b/rtic-macros/src/codegen/bindings/esp32c3.rs
@@ -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
+ }
+}
diff --git a/rtic-macros/src/codegen/bindings/template.rs b/rtic-macros/src/codegen/bindings/template.rs
index 690dfb0..d929dd8 100644
--- a/rtic-macros/src/codegen/bindings/template.rs
+++ b/rtic-macros/src/codegen/bindings/template.rs
@@ -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![]
}
diff --git a/rtic-macros/src/codegen/hardware_tasks.rs b/rtic-macros/src/codegen/hardware_tasks.rs
index 2c5254e..ee85f59 100644
--- a/rtic-macros/src/codegen/hardware_tasks.rs
+++ b/rtic-macros/src/codegen/hardware_tasks.rs
@@ -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)*
diff --git a/rtic-macros/src/codegen/util.rs b/rtic-macros/src/codegen/util.rs
index 2f44edb..430e853 100644
--- a/rtic-macros/src/codegen/util.rs
+++ b/rtic-macros/src/codegen/util.rs
@@ -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 {
diff --git a/rtic-macros/src/lib.rs b/rtic-macros/src/lib.rs
index de67e3d..38eed8f 100644
--- a/rtic-macros/src/lib.rs
+++ b/rtic-macros/src/lib.rs
@@ -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.");
diff --git a/rtic-sync/CHANGELOG.md b/rtic-sync/CHANGELOG.md
index 6bf0346..465f0de 100644
--- a/rtic-sync/CHANGELOG.md
+++ b/rtic-sync/CHANGELOG.md
@@ -3,7 +3,7 @@
All notable changes to this project will be documented in this file.
This project adheres to [Semantic Versioning](http://semver.org/).
-For each category, *Added*, *Changed*, *Fixed* add new entries at the top!
+For each category, _Added_, _Changed_, _Fixed_ add new entries at the top!
## [Unreleased]
@@ -13,6 +13,10 @@ For each category, *Added*, *Changed*, *Fixed* add new entries at the top!
### Fixed
+## [v1.0.3]
+
+- `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.2] - 2023-08-29
### Fixed
@@ -25,6 +29,6 @@ For each category, *Added*, *Changed*, *Fixed* add new entries at the top!
- `make_channel` could be UB
-## [v1.0.0] - 2023-05-31 - yanked
+## [v1.0.0] - 2023-05-31 - yanked
- Initial release
diff --git a/rtic-sync/Cargo.toml b/rtic-sync/Cargo.toml
index a8f9070..064b9fa 100644
--- a/rtic-sync/Cargo.toml
+++ b/rtic-sync/Cargo.toml
@@ -1,6 +1,6 @@
[package]
name = "rtic-sync"
-version = "1.0.2"
+version = "1.0.3"
edition = "2021"
authors = [
@@ -20,6 +20,7 @@ license = "MIT OR Apache-2.0"
heapless = "0.7"
critical-section = "1"
rtic-common = { version = "1.0.0", path = "../rtic-common" }
+portable-atomic = { version = "1", default-features = false }
[dev-dependencies]
tokio = { version = "1", features = ["rt", "macros", "time"] }
diff --git a/rtic-sync/src/arbiter.rs b/rtic-sync/src/arbiter.rs
index deb0a4f..2d66a67 100644
--- a/rtic-sync/src/arbiter.rs
+++ b/rtic-sync/src/arbiter.rs
@@ -27,8 +27,8 @@ use core::cell::UnsafeCell;
use core::future::poll_fn;
use core::ops::{Deref, DerefMut};
use core::pin::Pin;
-use core::sync::atomic::{fence, AtomicBool, Ordering};
use core::task::{Poll, Waker};
+use portable_atomic::{fence, AtomicBool, Ordering};
use rtic_common::dropper::OnDrop;
use rtic_common::wait_queue::{Link, WaitQueue};
diff --git a/rtic-sync/src/channel.rs b/rtic-sync/src/channel.rs
index 61ae7e2..89a23af 100644
--- a/rtic-sync/src/channel.rs
+++ b/rtic-sync/src/channel.rs
@@ -108,7 +108,7 @@ macro_rules! make_channel {
static mut CHANNEL: $crate::channel::Channel<$type, $size> =
$crate::channel::Channel::new();
- static CHECK: ::core::sync::atomic::AtomicU8 = ::core::sync::atomic::AtomicU8::new(0);
+ static CHECK: $crate::portable_atomic::AtomicU8 = $crate::portable_atomic::AtomicU8::new(0);
$crate::channel::critical_section::with(|_| {
if CHECK.load(::core::sync::atomic::Ordering::Relaxed) != 0 {
diff --git a/rtic-sync/src/lib.rs b/rtic-sync/src/lib.rs
index fd8b6c3..ecd3247 100644
--- a/rtic-sync/src/lib.rs
+++ b/rtic-sync/src/lib.rs
@@ -5,6 +5,7 @@
pub mod arbiter;
pub mod channel;
+pub use portable_atomic;
#[cfg(test)]
#[macro_use]
diff --git a/rtic/CHANGELOG.md b/rtic/CHANGELOG.md
index fb3a35b..db90aa3 100644
--- a/rtic/CHANGELOG.md
+++ b/rtic/CHANGELOG.md
@@ -8,6 +8,7 @@ For each category, *Added*, *Changed*, *Fixed* add new entries at the top!
## [Unreleased]
### Added
+- Unstable support for ESP32-C3
### Fixed
diff --git a/rtic/Cargo.toml b/rtic/Cargo.toml
index 310c71f..61ffdf7 100644
--- a/rtic/Cargo.toml
+++ b/rtic/Cargo.toml
@@ -31,6 +31,8 @@ features = ["rtic-macros/test-template"]
name = "rtic"
[dependencies]
+esp32c3 = { version = "0.17.0", optional = true}
+riscv = {version = "0.10.1", optional = true}
cortex-m = { version = "0.7.0", optional = true }
bare-metal = "1.0.0"
#portable-atomic = { version = "0.3.19" }
@@ -62,14 +64,13 @@ trybuild = "1"
[features]
default = []
-
thumbv6-backend = ["cortex-m", "rtic-macros/cortex-m-source-masking"]
thumbv7-backend = ["cortex-m", "rtic-macros/cortex-m-basepri"]
thumbv8base-backend = ["cortex-m", "rtic-macros/cortex-m-source-masking"]
thumbv8main-backend = ["cortex-m", "rtic-macros/cortex-m-basepri"]
# riscv-clic-backend = ["rtic-macros/riscv-clic"]
# riscv-ch32-backend = ["rtic-macros/riscv-ch32"]
-# riscv-esp32c3-backend = ["rtic-macros/riscv-esp32c3"]
+riscv-esp32c3-backend = ["esp32c3", "riscv", "rtic-macros/riscv-esp32c3"]
# needed for testing
rtic-uitestv7 = ["thumbv7-backend"]
diff --git a/rtic/build.rs b/rtic/build.rs
index c88175d..6a02188 100644
--- a/rtic/build.rs
+++ b/rtic/build.rs
@@ -11,8 +11,12 @@ fn main() {
println!("cargo:rustc-cfg=feature=\"cortex-m-basepri\"");
} else if target.starts_with("thumbv6m") | target.starts_with("thumbv8m.base") {
println!("cargo:rustc-cfg=feature=\"cortex-m-source-masking\"");
+ //this should not be this general
+ //riscv processors differ in interrupt implementation
+ //even within the same target
+ //need some other way to discern
} else if target.starts_with("riscv32i") {
- panic!("No RISC-V support yet.");
+ println!("cargo:rustc-cfg=feature=\"riscv-esp32c3\"");
// TODO: Add feature here for risc-v targets
// println!("cargo:rustc-cfg=feature=\"riscv\"");
diff --git a/rtic/src/export.rs b/rtic/src/export.rs
index ef545ad..0fc995e 100644
--- a/rtic/src/export.rs
+++ b/rtic/src/export.rs
@@ -4,56 +4,38 @@ pub use atomic_polyfill as atomic;
pub mod executor;
-#[cfg(all(
- feature = "cortex-m-basepri",
- not(any(feature = "thumbv7-backend", feature = "thumbv8main-backend"))
-))]
-compile_error!(
- "Building for Cortex-M with basepri, but 'thumbv7-backend' or 'thumbv8main-backend' backend not selected"
-);
+// Cortex-M target (any)
+#[cfg(feature = "cortex-m")]
+pub use cortex_common::*;
-#[cfg(all(
- feature = "cortex-m-source-masking",
- not(any(feature = "thumbv6-backend", feature = "thumbv8base-backend"))
-))]
-compile_error!(
- "Building for Cortex-M with source masking, but 'thumbv6-backend' or 'thumbv8base-backend' backend not selected"
-);
+#[cfg(feature = "cortex-m")]
+mod cortex_common;
+// Cortex-M target with basepri support
#[cfg(any(feature = "cortex-m-basepri", feature = "rtic-uitestv7"))]
-pub use cortex_basepri::*;
+mod cortex_basepri;
#[cfg(any(feature = "cortex-m-basepri", feature = "rtic-uitestv7"))]
-mod cortex_basepri;
+pub use cortex_basepri::*;
+// Cortex-M target with source mask support
#[cfg(any(feature = "cortex-m-source-masking", feature = "rtic-uitestv6"))]
-pub use cortex_source_mask::*;
+mod cortex_source_mask;
#[cfg(any(feature = "cortex-m-source-masking", feature = "rtic-uitestv6"))]
-mod cortex_source_mask;
+pub use cortex_source_mask::*;
-#[cfg(feature = "cortex-m")]
-pub use cortex_m::{interrupt::InterruptNumber, peripheral::NVIC};
+// RISC-V target (any)
+#[cfg(feature = "riscv")]
+pub use riscv_common::*;
-/// Sets the given `interrupt` as pending
-///
-/// This is a convenience function around
-/// [`NVIC::pend`](../cortex_m/peripheral/struct.NVIC.html#method.pend)
-#[cfg(feature = "cortex-m")]
-pub fn pend<I>(interrupt: I)
-where
- I: InterruptNumber,
-{
- NVIC::pend(interrupt);
-}
+#[cfg(feature = "riscv")]
+mod riscv_common;
-/// Priority conversion, takes logical priorities 1..=N and converts it to NVIC priority.
-#[cfg(feature = "cortex-m")]
-#[inline]
-#[must_use]
-pub const fn cortex_logical2hw(logical: u8, nvic_prio_bits: u8) -> u8 {
- ((1 << nvic_prio_bits) - logical) << (8 - nvic_prio_bits)
-}
+#[cfg(feature = "riscv-esp32c3")]
+mod riscv_esp32c3;
+#[cfg(feature = "riscv-esp32c3")]
+pub use riscv_esp32c3::*;
#[inline(always)]
pub fn assert_send<T>()
diff --git a/rtic/src/export/cortex_basepri.rs b/rtic/src/export/cortex_basepri.rs
index 695236b..0eb4eb6 100644
--- a/rtic/src/export/cortex_basepri.rs
+++ b/rtic/src/export/cortex_basepri.rs
@@ -8,6 +8,11 @@ pub use cortex_m::{
Peripherals,
};
+#[cfg(not(any(feature = "thumbv7-backend", feature = "thumbv8main-backend")))]
+compile_error!(
+ "Building for Cortex-M with basepri, but 'thumbv7-backend' or 'thumbv8main-backend' backend not selected"
+);
+
#[inline(always)]
pub fn run<F>(priority: u8, f: F)
where
diff --git a/rtic/src/export/cortex_common.rs b/rtic/src/export/cortex_common.rs
new file mode 100644
index 0000000..112d9c9
--- /dev/null
+++ b/rtic/src/export/cortex_common.rs
@@ -0,0 +1,18 @@
+pub use cortex_m::{interrupt::InterruptNumber, peripheral::NVIC};
+
+#[inline]
+#[must_use]
+pub const fn cortex_logical2hw(logical: u8, nvic_prio_bits: u8) -> u8 {
+ ((1 << nvic_prio_bits) - logical) << (8 - nvic_prio_bits)
+}
+
+/// Sets the given `interrupt` as pending
+///
+/// This is a convenience function around
+/// [`NVIC::pend`](../cortex_m/peripheral/struct.NVIC.html#method.pend)
+pub fn pend<I>(interrupt: I)
+where
+ I: InterruptNumber,
+{
+ NVIC::pend(interrupt);
+}
diff --git a/rtic/src/export/cortex_source_mask.rs b/rtic/src/export/cortex_source_mask.rs
index fe95b1b..783be48 100644
--- a/rtic/src/export/cortex_source_mask.rs
+++ b/rtic/src/export/cortex_source_mask.rs
@@ -6,6 +6,11 @@ pub use cortex_m::{
Peripherals,
};
+#[cfg(not(any(feature = "thumbv6-backend", feature = "thumbv8base-backend")))]
+compile_error!(
+ "Building for Cortex-M with source masking, but 'thumbv6-backend' or 'thumbv8base-backend' backend not selected"
+);
+
/// Mask is used to store interrupt masks on systems without a BASEPRI register (M0, M0+, M23).
/// It needs to be large enough to cover all the relevant interrupts in use.
/// For M0/M0+ there are only 32 interrupts so we only need one u32 value.
diff --git a/rtic/src/export/riscv_common.rs b/rtic/src/export/riscv_common.rs
new file mode 100644
index 0000000..06c3968
--- /dev/null
+++ b/rtic/src/export/riscv_common.rs
@@ -0,0 +1,2 @@
+/// GENERIC RE-EXPORTS: needed for all RTIC backends
+pub use riscv::interrupt;
diff --git a/rtic/src/export/riscv_esp32c3.rs b/rtic/src/export/riscv_esp32c3.rs
new file mode 100644
index 0000000..f093672
--- /dev/null
+++ b/rtic/src/export/riscv_esp32c3.rs
@@ -0,0 +1,164 @@
+use esp32c3::INTERRUPT_CORE0; //priority threshold control
+pub use esp32c3::{Interrupt, Peripherals};
+pub use riscv::{interrupt, register::mcause}; //low level interrupt enable/disable
+
+#[cfg(all(feature = "riscv-esp32c3", not(feature = "riscv-esp32c3-backend")))]
+compile_error!("Building for the esp32c3, but 'riscv-esp32c3-backend not selected'");
+
+#[inline(always)]
+pub fn run<F>(priority: u8, f: F)
+where
+ F: FnOnce(),
+{
+ if priority == 1 {
+ //if priority is 1, priority thresh should be 1
+ f();
+ unsafe {
+ (*INTERRUPT_CORE0::ptr())
+ .cpu_int_thresh
+ .write(|w| w.cpu_int_thresh().bits(1));
+ }
+ } else {
+ //read current thresh
+ let initial = unsafe {
+ (*INTERRUPT_CORE0::ptr())
+ .cpu_int_thresh
+ .read()
+ .cpu_int_thresh()
+ .bits()
+ };
+ f();
+ //write back old thresh
+ unsafe {
+ (*INTERRUPT_CORE0::ptr())
+ .cpu_int_thresh
+ .write(|w| w.cpu_int_thresh().bits(initial));
+ }
+ }
+}
+
+/// Lock implementation using threshold and global Critical Section (CS)
+///
+/// # Safety
+///
+/// The system ceiling is raised from current to ceiling
+/// by either
+/// - raising the threshold to the ceiling value, or
+/// - disable all interrupts in case we want to
+/// mask interrupts with maximum priority
+///
+/// Dereferencing a raw pointer inside CS
+///
+/// The priority.set/priority.get can safely be outside the CS
+/// as being a context local cell (not affected by preemptions).
+/// It is merely used in order to omit masking in case current
+/// priority is current priority >= ceiling.
+#[inline(always)]
+pub unsafe fn lock<T, R>(ptr: *mut T, ceiling: u8, f: impl FnOnce(&mut T) -> R) -> R {
+ if ceiling == (15) {
+ //turn off interrupts completely, were at max prio
+ let r = critical_section::with(|_| f(&mut *ptr));
+ r
+ } else {
+ let current = unsafe {
+ (*INTERRUPT_CORE0::ptr())
+ .cpu_int_thresh
+ .read()
+ .cpu_int_thresh()
+ .bits()
+ };
+
+ unsafe {
+ (*INTERRUPT_CORE0::ptr())
+ .cpu_int_thresh
+ .write(|w| w.cpu_int_thresh().bits(ceiling + 1))
+ } //esp32c3 lets interrupts with prio equal to threshold through so we up it by one
+ let r = f(&mut *ptr);
+ unsafe {
+ (*INTERRUPT_CORE0::ptr())
+ .cpu_int_thresh
+ .write(|w| w.cpu_int_thresh().bits(current))
+ }
+ r
+ }
+}
+
+/// Sets the given software interrupt as pending
+#[inline(always)]
+pub fn pend(int: Interrupt) {
+ unsafe {
+ let peripherals = Peripherals::steal();
+ match int {
+ Interrupt::FROM_CPU_INTR0 => peripherals
+ .SYSTEM
+ .cpu_intr_from_cpu_0
+ .write(|w| w.cpu_intr_from_cpu_0().bit(true)),
+ Interrupt::FROM_CPU_INTR1 => peripherals
+ .SYSTEM
+ .cpu_intr_from_cpu_1
+ .write(|w| w.cpu_intr_from_cpu_1().bit(true)),
+ Interrupt::FROM_CPU_INTR2 => peripherals
+ .SYSTEM
+ .cpu_intr_from_cpu_2
+ .write(|w| w.cpu_intr_from_cpu_2().bit(true)),
+ Interrupt::FROM_CPU_INTR3 => peripherals
+ .SYSTEM
+ .cpu_intr_from_cpu_3
+ .write(|w| w.cpu_intr_from_cpu_3().bit(true)),
+ _ => panic!("Unsupported software interrupt"), //should never happen, checked at compile time
+ }
+ }
+}
+
+// Sets the given software interrupt as not pending
+pub fn unpend(int: Interrupt) {
+ unsafe {
+ let peripherals = Peripherals::steal();
+ match int {
+ Interrupt::FROM_CPU_INTR0 => peripherals
+ .SYSTEM
+ .cpu_intr_from_cpu_0
+ .write(|w| w.cpu_intr_from_cpu_0().bit(false)),
+ Interrupt::FROM_CPU_INTR1 => peripherals
+ .SYSTEM
+ .cpu_intr_from_cpu_1
+ .write(|w| w.cpu_intr_from_cpu_1().bit(false)),
+ Interrupt::FROM_CPU_INTR2 => peripherals
+ .SYSTEM
+ .cpu_intr_from_cpu_2
+ .write(|w| w.cpu_intr_from_cpu_2().bit(false)),
+ Interrupt::FROM_CPU_INTR3 => peripherals
+ .SYSTEM
+ .cpu_intr_from_cpu_3
+ .write(|w| w.cpu_intr_from_cpu_3().bit(false)),
+ _ => panic!("Unsupported software interrupt"),
+ }
+ }
+}
+
+pub fn enable(int: Interrupt, prio: u8, cpu_int_id: u8) {
+ const INTERRUPT_MAP_BASE: u32 = 0x600c2000; //this isn't exposed properly in the PAC,
+ //should maybe figure out a workaround that
+ //doesnt involve raw pointers.
+ //Again, this is how they do it in the HAL
+ //but i'm really not a fan.
+ let interrupt_number = int as isize;
+ let cpu_interrupt_number = cpu_int_id as isize;
+
+ unsafe {
+ let intr_map_base = INTERRUPT_MAP_BASE as *mut u32;
+ intr_map_base
+ .offset(interrupt_number)
+ .write_volatile(cpu_interrupt_number as u32);
+ //map peripheral interrupt to CPU interrupt
+ (*INTERRUPT_CORE0::ptr())
+ .cpu_int_enable
+ .modify(|r, w| w.bits((1 << cpu_interrupt_number) | r.bits())); //enable the CPU interupt.
+ let intr = INTERRUPT_CORE0::ptr();
+ let intr_prio_base = (*intr).cpu_int_pri_0.as_ptr();
+
+ intr_prio_base
+ .offset(cpu_interrupt_number)
+ .write_volatile(prio as u32);
+ }
+}