From f07e25c97049888f61e63517bc497307ac5c2edb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?D=C3=A1niel=20Buga?= Date: Wed, 30 Oct 2024 09:12:15 +0100 Subject: [PATCH] Add option to disable waiti (#2329) --- esp-hal-embassy/CHANGELOG.md | 2 + esp-hal-embassy/Cargo.toml | 1 + esp-hal-embassy/build.rs | 12 +++++ esp-hal-embassy/src/executor/thread.rs | 64 +++++++++++++------------- hil-test/Cargo.toml | 14 +++--- 5 files changed, 55 insertions(+), 38 deletions(-) diff --git a/esp-hal-embassy/CHANGELOG.md b/esp-hal-embassy/CHANGELOG.md index c3cad8639..95d69b3dd 100644 --- a/esp-hal-embassy/CHANGELOG.md +++ b/esp-hal-embassy/CHANGELOG.md @@ -9,6 +9,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Added +- `ESP_HAL_EMBASSY_LOW_POWER_WAIT` configuration option. (#2329) + ### Changed - Reduce memory footprint by 4 bytes on multi-core MCUs. diff --git a/esp-hal-embassy/Cargo.toml b/esp-hal-embassy/Cargo.toml index ffac44458..d4827afca 100644 --- a/esp-hal-embassy/Cargo.toml +++ b/esp-hal-embassy/Cargo.toml @@ -25,6 +25,7 @@ static_cell = "2.1.0" [build-dependencies] esp-build = { version = "0.1.0", path = "../esp-build" } +esp-config = { version = "0.1.0", path = "../esp-config", features = ["build"] } esp-metadata = { version = "0.4.0", path = "../esp-metadata" } [features] diff --git a/esp-hal-embassy/build.rs b/esp-hal-embassy/build.rs index 1c7ecfc2f..ff1606cbe 100644 --- a/esp-hal-embassy/build.rs +++ b/esp-hal-embassy/build.rs @@ -1,6 +1,7 @@ use std::{error::Error, str::FromStr}; use esp_build::assert_unique_used_features; +use esp_config::{generate_config, Value}; use esp_metadata::{Chip, Config}; fn main() -> Result<(), Box> { @@ -37,5 +38,16 @@ fn main() -> Result<(), Box> { // Define all necessary configuration symbols for the configured device: config.define_symbols(); + // emit config + generate_config( + "esp_hal_embassy", + &[( + "low-power-wait", + Value::Bool(true), + "Enables the lower-power wait if no tasks are ready to run on the thread-mode executor. This allows the MCU to use less power if the workload allows. Recommended for battery-powered systems. May impact analog performance.", + )], + true, + ); + Ok(()) } diff --git a/esp-hal-embassy/src/executor/thread.rs b/esp-hal-embassy/src/executor/thread.rs index 4af62ba3e..b279c6d41 100644 --- a/esp-hal-embassy/src/executor/thread.rs +++ b/esp-hal-embassy/src/executor/thread.rs @@ -6,6 +6,7 @@ use embassy_executor::{raw, Spawner}; use esp_hal::{get_core, Cpu}; #[cfg(multi_core)] use esp_hal::{interrupt::software::SoftwareInterrupt, macros::handler}; +#[cfg(low_power_wait)] use portable_atomic::{AtomicBool, Ordering}; pub(crate) const THREAD_MODE_CONTEXT: usize = 16; @@ -15,7 +16,7 @@ pub(crate) const THREAD_MODE_CONTEXT: usize = 16; static SIGNAL_WORK_THREAD_MODE: [AtomicBool; Cpu::COUNT] = [const { AtomicBool::new(false) }; Cpu::COUNT]; -#[cfg(multi_core)] +#[cfg(all(multi_core, low_power_wait))] #[handler] fn software3_interrupt() { // This interrupt is fired when the thread-mode executor's core needs to be @@ -25,30 +26,33 @@ fn software3_interrupt() { unsafe { SoftwareInterrupt::<3>::steal().reset() }; } -pub(crate) fn pend_thread_mode(core: usize) { - // Signal that there is work to be done. - SIGNAL_WORK_THREAD_MODE[core].store(true, Ordering::SeqCst); +pub(crate) fn pend_thread_mode(_core: usize) { + #[cfg(low_power_wait)] + { + // Signal that there is work to be done. + SIGNAL_WORK_THREAD_MODE[_core].store(true, Ordering::SeqCst); - // If we are pending a task on the current core, we're done. Otherwise, we - // need to make sure the other core wakes up. - #[cfg(multi_core)] - if core != get_core() as usize { - // We need to clear the interrupt from software. We don't actually - // need it to trigger and run the interrupt handler, we just need to - // kick waiti to return. - unsafe { SoftwareInterrupt::<3>::steal().raise() }; + // If we are pending a task on the current core, we're done. Otherwise, we + // need to make sure the other core wakes up. + #[cfg(multi_core)] + if _core != get_core() as usize { + // We need to clear the interrupt from software. We don't actually + // need it to trigger and run the interrupt handler, we just need to + // kick waiti to return. + unsafe { SoftwareInterrupt::<3>::steal().raise() }; + } } } -/// A thread aware Executor +/// Thread mode executor. +/// +/// This is the simplest and most common kind of executor. It runs on thread +/// mode (at the lowest priority level). +#[cfg_attr(multi_core, doc = "")] #[cfg_attr( multi_core, - doc = r#" -This executor is capable of waking an -executor running on another core if work -needs to be completed there for a task to -progress on this core. -"# + doc = "This executor is safe to use on multiple cores. You need to +create one instance per core. The executors don't steal tasks from each other." )] pub struct Executor { inner: raw::Executor, @@ -64,7 +68,7 @@ impl Executor { This will use software-interrupt 3 which isn't available for anything else to wake the other core(s)."# )] pub fn new() -> Self { - #[cfg(multi_core)] + #[cfg(all(multi_core, low_power_wait))] unsafe { SoftwareInterrupt::<3>::steal().set_interrupt_handler(software3_interrupt); } @@ -90,7 +94,7 @@ This will use software-interrupt 3 which isn't available for anything else to wa /// you mutable access. There's a few ways to do this: /// /// - a [StaticCell](https://docs.rs/static_cell/latest/static_cell/) (safe) - /// - a `static mut` (unsafe) + /// - a `static mut` (unsafe, not recommended) /// - a local variable in a function you know never returns (like `fn main() /// -> !`), upgrading its lifetime with `transmute`. (unsafe) /// @@ -98,20 +102,19 @@ This will use software-interrupt 3 which isn't available for anything else to wa pub fn run(&'static mut self, init: impl FnOnce(Spawner)) -> ! { init(self.inner.spawner()); + #[cfg(low_power_wait)] let cpu = get_core() as usize; loop { - unsafe { - self.inner.poll(); + unsafe { self.inner.poll() }; - Self::wait_impl(cpu); - } + #[cfg(low_power_wait)] + Self::wait_impl(cpu); } } - #[doc(hidden)] - #[cfg(xtensa)] - pub fn wait_impl(cpu: usize) { + #[cfg(all(xtensa, low_power_wait))] + fn wait_impl(cpu: usize) { // Manual critical section implementation that only masks interrupts handlers. // We must not acquire the cross-core on dual-core systems because that would // prevent the other core from doing useful work while this core is sleeping. @@ -140,9 +143,8 @@ This will use software-interrupt 3 which isn't available for anything else to wa } } - #[doc(hidden)] - #[cfg(riscv)] - pub fn wait_impl(cpu: usize) { + #[cfg(all(riscv, low_power_wait))] + fn wait_impl(cpu: usize) { // we do not care about race conditions between the load and store operations, // interrupts will only set this value to true. critical_section::with(|_| { diff --git a/hil-test/Cargo.toml b/hil-test/Cargo.toml index 300190c17..980d53dce 100644 --- a/hil-test/Cargo.toml +++ b/hil-test/Cargo.toml @@ -235,33 +235,33 @@ esp32 = [ "embedded-test/xtensa-semihosting", "esp-backtrace/esp32", "esp-hal/esp32", - "esp-hal-embassy/esp32", + "esp-hal-embassy?/esp32", "esp-wifi?/esp32", ] esp32c2 = [ "esp-backtrace/esp32c2", "esp-hal/esp32c2", - "esp-hal-embassy/esp32c2", + "esp-hal-embassy?/esp32c2", "esp-wifi?/esp32c2", ] esp32c3 = [ "esp-backtrace/esp32c3", "esp-hal/esp32c3", - "esp-hal-embassy/esp32c3", + "esp-hal-embassy?/esp32c3", "esp-wifi?/esp32c3", "esp-wifi?/phy-enable-usb", ] esp32c6 = [ "esp-backtrace/esp32c6", "esp-hal/esp32c6", - "esp-hal-embassy/esp32c6", + "esp-hal-embassy?/esp32c6", "esp-wifi?/esp32c6", "esp-wifi?/phy-enable-usb", ] esp32h2 = [ "esp-backtrace/esp32h2", "esp-hal/esp32h2", - "esp-hal-embassy/esp32h2", + "esp-hal-embassy?/esp32h2", "esp-wifi?/esp32h2", "esp-wifi?/phy-enable-usb", ] @@ -269,14 +269,14 @@ esp32s2 = [ "embedded-test/xtensa-semihosting", "esp-backtrace/esp32s2", "esp-hal/esp32s2", - "esp-hal-embassy/esp32s2", + "esp-hal-embassy?/esp32s2", "esp-wifi?/esp32s2", ] esp32s3 = [ "embedded-test/xtensa-semihosting", "esp-backtrace/esp32s3", "esp-hal/esp32s3", - "esp-hal-embassy/esp32s3", + "esp-hal-embassy?/esp32s3", "esp-wifi?/esp32s3", "esp-wifi?/phy-enable-usb", ]