aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--ci/expected/esp32c3/monotonic.run34
-rw-r--r--examples/esp32c3/.cargo/config.toml5
-rw-r--r--examples/esp32c3/Cargo.lock78
-rw-r--r--examples/esp32c3/Cargo.toml3
-rw-r--r--examples/esp32c3/README.md22
-rw-r--r--examples/esp32c3/examples/monotonic.rs51
-rw-r--r--examples/esp32c3/examples/sw_and_hw.rs1
-rw-r--r--examples/esp32c3/rust-toolchain.toml2
-rw-r--r--rtic-monotonics/CHANGELOG.md3
-rw-r--r--rtic-monotonics/Cargo.toml9
-rw-r--r--rtic-monotonics/src/esp32c3.rs193
-rw-r--r--rtic-monotonics/src/lib.rs3
12 files changed, 395 insertions, 9 deletions
diff --git a/ci/expected/esp32c3/monotonic.run b/ci/expected/esp32c3/monotonic.run
new file mode 100644
index 0000000..9bf82bd
--- /dev/null
+++ b/ci/expected/esp32c3/monotonic.run
@@ -0,0 +1,34 @@
+QEMU 8.2.0 monitor - type 'help' for more information
+(qemu) q
+ESP-ROM:esp32c3-api1-20210207
+Build:Feb 7 2021
+rst:0x1 (POWERON),boot:0x8 (SPI_FAST_FLASH_BOOT)
+SPIWP:0xee
+mode:DIO, clock div:2
+load:0x3fcd5820,len:0x1714
+load:0x403cc710,len:0x968
+load:0x403ce710,len:0x2f9c
+entry 0x403cc710
+I (0) boot: ESP-IDF v5.1.2-342-gbcf1645e44 2nd stage bootloader
+I (0) boot: compile time Dec 12 2023 10:50:58
+I (0) boot: chip revision: v0.3
+I (0) boot.esp32c3: SPI Speed : 40MHz
+I (0) boot.esp32c3: SPI Mode : SLOW READ
+I (0) boot.esp32c3: SPI Flash Size : 4MB
+I (0) boot: Enabling RNG early entropy source...
+I (1) boot: Partition Table:
+I (1) boot: ## Label Usage Type ST Offset Length
+I (1) boot: 0 nvs WiFi data 01 02 00009000 00006000
+I (1) boot: 1 phy_init RF data 01 01 0000f000 00001000
+I (1) boot: 2 factory factory app 00 00 00010000 003f0000
+I (1) boot: End of partition table
+I (1) esp_image: REDACTED
+I (3) esp_image: REDACTED
+I (3) esp_image: REDACTED
+I (8) esp_image: REDACTED
+I (11) boot: Loaded app from partition at offset 0x10000
+I (11) boot: Disabling RNG early entropy source...
+init
+hello from bar
+hello from baz
+hello from foo
diff --git a/examples/esp32c3/.cargo/config.toml b/examples/esp32c3/.cargo/config.toml
index 9ea4ecb..05f0a29 100644
--- a/examples/esp32c3/.cargo/config.toml
+++ b/examples/esp32c3/.cargo/config.toml
@@ -1,6 +1,6 @@
[target.riscv32imc-unknown-none-elf]
# Real hardware
-#runner = "espflash flash --monitor"
+# runner = "espflash flash --monitor"
# QEMU emulator
runner = "./runner.sh"
@@ -14,6 +14,3 @@ rustflags = [
]
target = "riscv32imc-unknown-none-elf"
-
-[unstable]
-build-std = ["core"]
diff --git a/examples/esp32c3/Cargo.lock b/examples/esp32c3/Cargo.lock
index 35d467e..5aaf0e1 100644
--- a/examples/esp32c3/Cargo.lock
+++ b/examples/esp32c3/Cargo.lock
@@ -145,6 +145,15 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "361a90feb7004eca4019fb28352a9465666b24f840f5c3cddf0ff13920590b89"
[[package]]
+name = "embedded-hal-async"
+version = "1.0.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0c4c685bbef7fe13c3c6dd4da26841ed3980ef33e841cddfa15ce8a8fb3f1884"
+dependencies = [
+ "embedded-hal 1.0.0",
+]
+
+[[package]]
name = "enum-as-inner"
version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -285,6 +294,7 @@ dependencies = [
"esp-println",
"esp32c3 0.22.0",
"rtic",
+ "rtic-monotonics",
]
[[package]]
@@ -385,6 +395,30 @@ dependencies = [
]
[[package]]
+name = "futures-core"
+version = "0.3.30"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "dfc6580bb841c5a68e9ef15c77ccc837b40a7504914d52e47b8b0e9bbda25a1d"
+
+[[package]]
+name = "futures-task"
+version = "0.3.30"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "38d84fa142264698cdce1a9f9172cf383a0c82de1bddcf3092901442c4097004"
+
+[[package]]
+name = "futures-util"
+version = "0.3.30"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "3d6401deb83407ab3da39eba7e33987a73c3df0c82b4bb5813ee871c19c41d48"
+dependencies = [
+ "futures-core",
+ "futures-task",
+ "pin-project-lite",
+ "pin-utils",
+]
+
+[[package]]
name = "gcd"
version = "2.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -480,6 +514,18 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "de3145af08024dea9fa9914f381a17b8fc6034dfb00f3a84013f7ff43f29ed4c"
[[package]]
+name = "pin-project-lite"
+version = "0.2.14"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "bda66fc9667c18cb2758a2ac84d1167245054bcf85d5d1aaa6923f45801bdd02"
+
+[[package]]
+name = "pin-utils"
+version = "0.1.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184"
+
+[[package]]
name = "portable-atomic"
version = "1.6.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -612,6 +658,14 @@ dependencies = [
]
[[package]]
+name = "rtic-common"
+version = "1.0.1"
+dependencies = [
+ "critical-section",
+ "portable-atomic",
+]
+
+[[package]]
name = "rtic-core"
version = "1.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -629,6 +683,30 @@ dependencies = [
]
[[package]]
+name = "rtic-monotonics"
+version = "2.0.2"
+dependencies = [
+ "cfg-if",
+ "esp32c3 0.22.0",
+ "fugit",
+ "portable-atomic",
+ "riscv",
+ "rtic-time",
+]
+
+[[package]]
+name = "rtic-time"
+version = "2.0.0"
+dependencies = [
+ "critical-section",
+ "embedded-hal 1.0.0",
+ "embedded-hal-async",
+ "fugit",
+ "futures-util",
+ "rtic-common",
+]
+
+[[package]]
name = "rustversion"
version = "1.0.14"
source = "registry+https://github.com/rust-lang/crates.io-index"
diff --git a/examples/esp32c3/Cargo.toml b/examples/esp32c3/Cargo.toml
index fa2a4e0..adb0ece 100644
--- a/examples/esp32c3/Cargo.toml
+++ b/examples/esp32c3/Cargo.toml
@@ -8,6 +8,7 @@ license = "MIT OR Apache-2.0"
[dependencies]
rtic = {path = "../../rtic/"}
+rtic-monotonics = {path = "../../rtic-monotonics/"}
esp-hal = { version = "0.16.1", features = ["esp32c3", "direct-vectoring", "interrupt-preemption"] }
esp-backtrace = { version = "0.11.0", features = [
"esp32c3",
@@ -21,4 +22,4 @@ esp-println = { version = "0.9.0", features = ["esp32c3", "uart"] }
[features]
test-critical-section = []
-riscv-esp32c3-backend = ["rtic/riscv-esp32c3-backend"]
+riscv-esp32c3-backend = ["rtic/riscv-esp32c3-backend", "rtic-monotonics/esp32c3-systimer"]
diff --git a/examples/esp32c3/README.md b/examples/esp32c3/README.md
index 37d0167..fa382c6 100644
--- a/examples/esp32c3/README.md
+++ b/examples/esp32c3/README.md
@@ -15,12 +15,24 @@ This crate uses the most convenient option in ``cargo-espflash`` and ``espflash`
## Running the crate
+Uncomment the
+
+```runner = "espflash flash --monitor"```
+
+line in ``.cargo/config.toml``
+
+and comment out (or remove)
+
+```runner = "./runner.sh"```
+
+Now, running
+
```cargo run --example sw_and_hw --features=riscv-esp32c3-backend (--release)```
-should do the trick.
+in the root of this crate should do the trick.
# Expected behavior
-The program
+The example ``sw_and_hw``
- Prints ``init``
- Enters a high prio task
- During the execution of the high prio task, the button should be non-functional
@@ -31,3 +43,9 @@ The program
- Exits the low prio task
- Prints ``idle``
+The example ``monotonic``
+- Prints ``init``
+- Spawns the ``foo``, ``bar``, ``baz`` tasks (because of hardware interrupt latency dispatch, the order here may vary).
+- Each task prints ``hello from $TASK`` on entry
+- The tasks wait for 1, 2, 3 seconds respectively
+- Once the wait period is over, each task exits printing ``bye from $TASK`` (now in the proper order).
diff --git a/examples/esp32c3/examples/monotonic.rs b/examples/esp32c3/examples/monotonic.rs
new file mode 100644
index 0000000..1c0bd77
--- /dev/null
+++ b/examples/esp32c3/examples/monotonic.rs
@@ -0,0 +1,51 @@
+#![no_main]
+#![no_std]
+use esp_backtrace as _;
+#[rtic::app(device = esp32c3, dispatchers = [])]
+mod app {
+ use rtic_monotonics::esp32c3::prelude::*;
+ esp32c3_systimer_monotonic!(Mono);
+ use esp_hal as _;
+ use esp_println::println;
+
+ #[shared]
+ struct Shared {}
+
+ #[local]
+ struct Local {}
+
+ #[init]
+ fn init(cx: init::Context) -> (Shared, Local) {
+ println!("init");
+ let timer = cx.device.SYSTIMER;
+
+ Mono::start(timer);
+
+ foo::spawn().unwrap();
+ bar::spawn().unwrap();
+ baz::spawn().unwrap();
+
+ (Shared {}, Local {})
+ }
+
+ #[task]
+ async fn foo(_cx: foo::Context) {
+ println!("hello from foo");
+ Mono::delay(2_u64.secs()).await;
+ println!("bye from foo");
+ }
+
+ #[task]
+ async fn bar(_cx: bar::Context) {
+ println!("hello from bar");
+ Mono::delay(3_u64.secs()).await;
+ println!("bye from bar");
+ }
+
+ #[task]
+ async fn baz(_cx: baz::Context) {
+ println!("hello from baz");
+ Mono::delay(4_u64.secs()).await;
+ println!("bye from baz");
+ }
+}
diff --git a/examples/esp32c3/examples/sw_and_hw.rs b/examples/esp32c3/examples/sw_and_hw.rs
index 413df92..42dd0ec 100644
--- a/examples/esp32c3/examples/sw_and_hw.rs
+++ b/examples/esp32c3/examples/sw_and_hw.rs
@@ -1,6 +1,5 @@
#![no_main]
#![no_std]
-
#[rtic::app(device = esp32c3, dispatchers=[FROM_CPU_INTR0, FROM_CPU_INTR1])]
mod app {
use esp_backtrace as _;
diff --git a/examples/esp32c3/rust-toolchain.toml b/examples/esp32c3/rust-toolchain.toml
index 446d6fe..e6c6255 100644
--- a/examples/esp32c3/rust-toolchain.toml
+++ b/examples/esp32c3/rust-toolchain.toml
@@ -1,4 +1,4 @@
[toolchain]
-channel = "nightly-2023-11-14"
+channel = "stable"
components = ["rust-src"]
targets = ["riscv32imc-unknown-none-elf"]
diff --git a/rtic-monotonics/CHANGELOG.md b/rtic-monotonics/CHANGELOG.md
index b13a3a9..fa185d6 100644
--- a/rtic-monotonics/CHANGELOG.md
+++ b/rtic-monotonics/CHANGELOG.md
@@ -13,6 +13,9 @@ For each category, *Added*, *Changed*, *Fixed* add new entries at the top!
## v2.0.2 - 2024-07-05
+### Added
+- `SYSTIMER` based monotonic for the ESP32-C3
+
### Fixed
- Fix `stm32` monotonic for timer peripherals with only two clock compare modules
diff --git a/rtic-monotonics/Cargo.toml b/rtic-monotonics/Cargo.toml
index 8895fcb..953c658 100644
--- a/rtic-monotonics/Cargo.toml
+++ b/rtic-monotonics/Cargo.toml
@@ -31,6 +31,7 @@ features = [
"stm32_tim4",
"stm32_tim5",
"stm32_tim15",
+ "esp32c3-systimer",
]
rustdoc-flags = ["--cfg", "docsrs"]
@@ -65,6 +66,11 @@ stm32-metapac = { version = "15.0.0", optional = true }
# i.MX RT
imxrt-ral = { version = "0.5.3", optional = true }
+
+esp32c3 = {version = "0.22.0", optional = true }
+riscv = {version = "0.11.1", optional = true }
+
+
[build-dependencies]
proc-macro2 = { version = "1.0.36", optional = true }
quote = { version = "1.0.15", optional = true }
@@ -104,6 +110,9 @@ imxrt = ["dep:cortex-m", "dep:imxrt-ral"]
imxrt_gpt1 = ["imxrt"]
imxrt_gpt2 = ["imxrt"]
+# ESP32-C3 Timer
+esp32c3-systimer = ["dep:esp32c3", "dep:riscv"]
+
# STM32 timers
# Use as `features = ["stm32g081kb", "stm32_tim15"]`
stm32_tim2 = []
diff --git a/rtic-monotonics/src/esp32c3.rs b/rtic-monotonics/src/esp32c3.rs
new file mode 100644
index 0000000..61e044e
--- /dev/null
+++ b/rtic-monotonics/src/esp32c3.rs
@@ -0,0 +1,193 @@
+//! [`Monotonic`](rtic_time::Monotonic) implementation for ESP32C3's SYSTIMER.
+//!
+//! Always runs at a fixed rate of 16 MHz.
+//!
+//! # Example
+//!
+//! ```
+//! use rtic_monotonics::esp32c3::prelude::*;
+//!
+//! esp32c3_systimer_monotonic!(Mono);
+//!
+//! fn init() {
+//! # // This is normally provided by the selected PAC
+//! # let timer = unsafe { esp32c3::Peripherals::steal() }.SYSTIMER;
+//! #
+//! // Start the monotonic
+//! Mono::start(timer);
+//! }
+//!
+//! async fn usage() {
+//! loop {
+//! // Use the monotonic
+//! let timestamp = Mono::now();
+//! Mono::delay(100.millis()).await;
+//! }
+//! }
+//! ```
+
+/// Common definitions and traits for using the RP2040 timer monotonic
+pub mod prelude {
+ pub use crate::esp32c3_systimer_monotonic;
+
+ pub use crate::Monotonic;
+
+ pub use fugit::{self, ExtU64, ExtU64Ceil};
+}
+use crate::TimerQueueBackend;
+use esp32c3::{INTERRUPT_CORE0, SYSTIMER};
+use rtic_time::timer_queue::TimerQueue;
+
+/// Timer implementing [`TimerQueueBackend`].
+pub struct TimerBackend;
+
+impl TimerBackend {
+ /// Starts the monotonic timer.
+ ///
+ /// **Do not use this function directly.**
+ ///
+ /// Use the prelude macros instead.
+ pub fn _start(timer: SYSTIMER) {
+ const INTERRUPT_MAP_BASE: u32 = 0x600c2000;
+ let interrupt_number = 37 as isize;
+ let cpu_interrupt_number = 31 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(15 as u32);
+ }
+ timer.conf().write(|w| w.timer_unit0_work_en().set_bit());
+ timer
+ .conf()
+ .write(|w| w.timer_unit1_core0_stall_en().clear_bit());
+ TIMER_QUEUE.initialize(Self {})
+ }
+}
+
+static TIMER_QUEUE: TimerQueue<TimerBackend> = TimerQueue::new();
+use esp32c3;
+impl TimerQueueBackend for TimerBackend {
+ type Ticks = u64;
+ fn now() -> Self::Ticks {
+ let peripherals = unsafe { esp32c3::Peripherals::steal() };
+ peripherals
+ .SYSTIMER
+ .unit0_op()
+ .write(|w| w.timer_unit0_update().set_bit());
+ // this must be polled until value is valid
+ while {
+ peripherals
+ .SYSTIMER
+ .unit0_op()
+ .read()
+ .timer_unit0_value_valid()
+ == false
+ } {}
+ let instant: u64 = (peripherals.SYSTIMER.unit0_value_lo().read().bits() as u64)
+ | ((peripherals.SYSTIMER.unit0_value_hi().read().bits() as u64) << 32);
+ instant
+ }
+
+ fn set_compare(instant: Self::Ticks) {
+ let systimer = unsafe { esp32c3::Peripherals::steal() }.SYSTIMER;
+ systimer
+ .target0_conf()
+ .write(|w| w.target0_timer_unit_sel().set_bit());
+ systimer
+ .target0_conf()
+ .write(|w| w.target0_period_mode().clear_bit());
+ systimer
+ .target0_lo()
+ .write(|w| unsafe { w.bits((instant & 0xFFFFFFFF).try_into().unwrap()) });
+ systimer
+ .target0_hi()
+ .write(|w| unsafe { w.bits((instant >> 32).try_into().unwrap()) });
+ systimer
+ .comp0_load()
+ .write(|w| w.timer_comp0_load().set_bit()); //sync period to comp register
+ systimer.conf().write(|w| w.target0_work_en().set_bit());
+ systimer.int_ena().write(|w| w.target0().set_bit());
+ }
+
+ fn clear_compare_flag() {
+ unsafe { esp32c3::Peripherals::steal() }
+ .SYSTIMER
+ .int_clr()
+ .write(|w| w.target0().bit(true));
+ }
+
+ fn pend_interrupt() {
+ extern "C" {
+ fn cpu_int_31_handler();
+ }
+ //run the timer interrupt handler in a critical section to emulate a max priority
+ //interrupt.
+ //since there is no hardware support for pending a timer interrupt.
+ riscv::interrupt::disable();
+ unsafe { cpu_int_31_handler() };
+ unsafe { riscv::interrupt::enable() };
+ }
+
+ fn timer_queue() -> &'static TimerQueue<Self> {
+ &TIMER_QUEUE
+ }
+}
+
+/// Create an ESP32-C3 SysTimer based monotonic and register the necessary interrupt for it.
+///
+/// See [`crate::esp32c3`] for more details.
+///
+/// # Arguments
+///
+/// * `name` - The name that the monotonic type will have.
+#[macro_export]
+macro_rules! esp32c3_systimer_monotonic {
+ ($name:ident) => {
+ /// A `Monotonic` based on the ESP32-C3 SysTimer peripheral.
+ pub struct $name;
+
+ impl $name {
+ /// Starts the `Monotonic`.
+ ///
+ /// This method must be called only once.
+ pub fn start(timer: esp32c3::SYSTIMER) {
+ #[export_name = "cpu_int_31_handler"]
+ #[allow(non_snake_case)]
+ unsafe extern "C" fn Systimer() {
+ use $crate::TimerQueueBackend;
+ $crate::esp32c3::TimerBackend::timer_queue().on_monotonic_interrupt();
+ }
+
+ $crate::esp32c3::TimerBackend::_start(timer);
+ }
+ }
+
+ impl $crate::TimerQueueBasedMonotonic for $name {
+ type Backend = $crate::esp32c3::TimerBackend;
+ type Instant = $crate::fugit::Instant<
+ <Self::Backend as $crate::TimerQueueBackend>::Ticks,
+ 1,
+ 16_000_000,
+ >;
+ type Duration = $crate::fugit::Duration<
+ <Self::Backend as $crate::TimerQueueBackend>::Ticks,
+ 1,
+ 16_000_000,
+ >;
+ }
+
+ $crate::rtic_time::impl_embedded_hal_delay_fugit!($name);
+ $crate::rtic_time::impl_embedded_hal_async_delay_fugit!($name);
+ };
+}
diff --git a/rtic-monotonics/src/lib.rs b/rtic-monotonics/src/lib.rs
index 757e901..9fe0bbf 100644
--- a/rtic-monotonics/src/lib.rs
+++ b/rtic-monotonics/src/lib.rs
@@ -39,6 +39,9 @@ pub use rtic_time::{
TimeoutError,
};
+#[cfg(feature = "esp32c3-systimer")]
+pub mod esp32c3;
+
#[cfg(feature = "cortex-m-systick")]
pub mod systick;