diff --git a/rtic-common/CHANGELOG.md b/rtic-common/CHANGELOG.md index f90db797c1..1fe270b658 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 1e7f98f71e..cd22f728a7 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 4b1b0f3206..ef3afe85c5 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; diff --git a/rtic-macros/CHANGELOG.md b/rtic-macros/CHANGELOG.md index 3413d8a7dd..6a1fc54049 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 ade6842d5c..e88d1bc927 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 289a63b57e..54db2b3453 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 c328ee0be8..60605f35d1 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 eba2afcaa3..ffa024591c 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 Vec { + vec![] +} + pub fn async_prio_limit(app: &App, analysis: &CodegenAnalysis) -> Vec { 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 Vec { + vec![] +} diff --git a/rtic-macros/src/codegen/bindings/esp32c3.rs b/rtic-macros/src/codegen/bindings/esp32c3.rs new file mode 100644 index 0000000000..a8d58b7342 --- /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(&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 { + vec![] + } + + 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 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 { + 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::>(); + + 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 { + vec![] + } + + pub fn interrupt_exit(_app: &App, _analysis: &CodegenAnalysis) -> Vec { + vec![] + } + + pub fn async_entry( + _app: &App, + _analysis: &CodegenAnalysis, + dispatcher_name: Ident, + ) -> Vec { + 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 { + 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 { + 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 690dfb0387..d929dd8c8e 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 Vec { +pub fn async_entry( + _app: &App, + _analysis: &CodegenAnalysis, + _dispatcher_name: Ident, +) -> Vec { + vec![] +} + +pub fn async_prio_limit(app: &App, _analysis: &CodegenAnalysis) -> Vec { + vec![] +} +pub fn handler_config( + _app: &App, + _analysis: &CodegenAnalysis, + dispatcher_name: Ident, +) -> Vec { vec![] } diff --git a/rtic-macros/src/codegen/hardware_tasks.rs b/rtic-macros/src/codegen/hardware_tasks.rs index 2c5254e967..ee85f59f33 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 2f44edb0bc..430e853e36 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 de67e3de8d..38eed8f3b2 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 6bf0346daf..465f0de549 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 a8f907072e..064b9fa4be 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 deb0a4f8c8..2d66a6772c 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 61ae7e2f91..89a23af990 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 fd8b6c3ad0..ecd3247d5a 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 fb3a35bdd4..db90aa3ecb 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 310c71f194..61ffdf7fab 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 c88175d55a..6a02188558 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 ef545ad750..0fc995eac8 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"))] +mod cortex_basepri; #[cfg(any(feature = "cortex-m-basepri", feature = "rtic-uitestv7"))] pub use cortex_basepri::*; -#[cfg(any(feature = "cortex-m-basepri", feature = "rtic-uitestv7"))] -mod cortex_basepri; +// Cortex-M target with source mask support +#[cfg(any(feature = "cortex-m-source-masking", feature = "rtic-uitestv6"))] +mod cortex_source_mask; #[cfg(any(feature = "cortex-m-source-masking", feature = "rtic-uitestv6"))] pub use cortex_source_mask::*; -#[cfg(any(feature = "cortex-m-source-masking", feature = "rtic-uitestv6"))] -mod cortex_source_mask; +// RISC-V target (any) +#[cfg(feature = "riscv")] +pub use riscv_common::*; -#[cfg(feature = "cortex-m")] -pub use cortex_m::{interrupt::InterruptNumber, peripheral::NVIC}; +#[cfg(feature = "riscv")] +mod 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(interrupt: I) -where - I: InterruptNumber, -{ - NVIC::pend(interrupt); -} - -/// 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() diff --git a/rtic/src/export/cortex_basepri.rs b/rtic/src/export/cortex_basepri.rs index 695236be3a..0eb4eb66c4 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(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 0000000000..112d9c960b --- /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(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 fe95b1b7db..783be483f0 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 0000000000..06c3968659 --- /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 0000000000..f093672cf1 --- /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(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(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); + } +}