Add option to disable waiti (#2329)

This commit is contained in:
Dániel Buga 2024-10-30 09:12:15 +01:00 committed by GitHub
parent b7224ef4c9
commit f07e25c970
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
5 changed files with 55 additions and 38 deletions

View File

@ -9,6 +9,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
### Added ### Added
- `ESP_HAL_EMBASSY_LOW_POWER_WAIT` configuration option. (#2329)
### Changed ### Changed
- Reduce memory footprint by 4 bytes on multi-core MCUs. - Reduce memory footprint by 4 bytes on multi-core MCUs.

View File

@ -25,6 +25,7 @@ static_cell = "2.1.0"
[build-dependencies] [build-dependencies]
esp-build = { version = "0.1.0", path = "../esp-build" } 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" } esp-metadata = { version = "0.4.0", path = "../esp-metadata" }
[features] [features]

View File

@ -1,6 +1,7 @@
use std::{error::Error, str::FromStr}; use std::{error::Error, str::FromStr};
use esp_build::assert_unique_used_features; use esp_build::assert_unique_used_features;
use esp_config::{generate_config, Value};
use esp_metadata::{Chip, Config}; use esp_metadata::{Chip, Config};
fn main() -> Result<(), Box<dyn Error>> { fn main() -> Result<(), Box<dyn Error>> {
@ -37,5 +38,16 @@ fn main() -> Result<(), Box<dyn Error>> {
// Define all necessary configuration symbols for the configured device: // Define all necessary configuration symbols for the configured device:
config.define_symbols(); 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(()) Ok(())
} }

View File

@ -6,6 +6,7 @@ use embassy_executor::{raw, Spawner};
use esp_hal::{get_core, Cpu}; use esp_hal::{get_core, Cpu};
#[cfg(multi_core)] #[cfg(multi_core)]
use esp_hal::{interrupt::software::SoftwareInterrupt, macros::handler}; use esp_hal::{interrupt::software::SoftwareInterrupt, macros::handler};
#[cfg(low_power_wait)]
use portable_atomic::{AtomicBool, Ordering}; use portable_atomic::{AtomicBool, Ordering};
pub(crate) const THREAD_MODE_CONTEXT: usize = 16; 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] = static SIGNAL_WORK_THREAD_MODE: [AtomicBool; Cpu::COUNT] =
[const { AtomicBool::new(false) }; Cpu::COUNT]; [const { AtomicBool::new(false) }; Cpu::COUNT];
#[cfg(multi_core)] #[cfg(all(multi_core, low_power_wait))]
#[handler] #[handler]
fn software3_interrupt() { fn software3_interrupt() {
// This interrupt is fired when the thread-mode executor's core needs to be // 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() }; unsafe { SoftwareInterrupt::<3>::steal().reset() };
} }
pub(crate) fn pend_thread_mode(core: usize) { pub(crate) fn pend_thread_mode(_core: usize) {
// Signal that there is work to be done. #[cfg(low_power_wait)]
SIGNAL_WORK_THREAD_MODE[core].store(true, Ordering::SeqCst); {
// 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 // If we are pending a task on the current core, we're done. Otherwise, we
// need to make sure the other core wakes up. // need to make sure the other core wakes up.
#[cfg(multi_core)] #[cfg(multi_core)]
if core != get_core() as usize { if _core != get_core() as usize {
// We need to clear the interrupt from software. We don't actually // 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 // need it to trigger and run the interrupt handler, we just need to
// kick waiti to return. // kick waiti to return.
unsafe { SoftwareInterrupt::<3>::steal().raise() }; 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( #[cfg_attr(
multi_core, multi_core,
doc = r#" doc = "This executor is safe to use on multiple cores. You need to
This executor is capable of waking an create one instance per core. The executors don't steal tasks from each other."
executor running on another core if work
needs to be completed there for a task to
progress on this core.
"#
)] )]
pub struct Executor { pub struct Executor {
inner: raw::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)."# This will use software-interrupt 3 which isn't available for anything else to wake the other core(s)."#
)] )]
pub fn new() -> Self { pub fn new() -> Self {
#[cfg(multi_core)] #[cfg(all(multi_core, low_power_wait))]
unsafe { unsafe {
SoftwareInterrupt::<3>::steal().set_interrupt_handler(software3_interrupt); 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: /// you mutable access. There's a few ways to do this:
/// ///
/// - a [StaticCell](https://docs.rs/static_cell/latest/static_cell/) (safe) /// - 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() /// - a local variable in a function you know never returns (like `fn main()
/// -> !`), upgrading its lifetime with `transmute`. (unsafe) /// -> !`), 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)) -> ! { pub fn run(&'static mut self, init: impl FnOnce(Spawner)) -> ! {
init(self.inner.spawner()); init(self.inner.spawner());
#[cfg(low_power_wait)]
let cpu = get_core() as usize; let cpu = get_core() as usize;
loop { loop {
unsafe { unsafe { self.inner.poll() };
self.inner.poll();
Self::wait_impl(cpu); #[cfg(low_power_wait)]
} Self::wait_impl(cpu);
} }
} }
#[doc(hidden)] #[cfg(all(xtensa, low_power_wait))]
#[cfg(xtensa)] fn wait_impl(cpu: usize) {
pub fn wait_impl(cpu: usize) {
// Manual critical section implementation that only masks interrupts handlers. // Manual critical section implementation that only masks interrupts handlers.
// We must not acquire the cross-core on dual-core systems because that would // 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. // 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(all(riscv, low_power_wait))]
#[cfg(riscv)] fn wait_impl(cpu: usize) {
pub fn wait_impl(cpu: usize) {
// we do not care about race conditions between the load and store operations, // we do not care about race conditions between the load and store operations,
// interrupts will only set this value to true. // interrupts will only set this value to true.
critical_section::with(|_| { critical_section::with(|_| {

View File

@ -235,33 +235,33 @@ esp32 = [
"embedded-test/xtensa-semihosting", "embedded-test/xtensa-semihosting",
"esp-backtrace/esp32", "esp-backtrace/esp32",
"esp-hal/esp32", "esp-hal/esp32",
"esp-hal-embassy/esp32", "esp-hal-embassy?/esp32",
"esp-wifi?/esp32", "esp-wifi?/esp32",
] ]
esp32c2 = [ esp32c2 = [
"esp-backtrace/esp32c2", "esp-backtrace/esp32c2",
"esp-hal/esp32c2", "esp-hal/esp32c2",
"esp-hal-embassy/esp32c2", "esp-hal-embassy?/esp32c2",
"esp-wifi?/esp32c2", "esp-wifi?/esp32c2",
] ]
esp32c3 = [ esp32c3 = [
"esp-backtrace/esp32c3", "esp-backtrace/esp32c3",
"esp-hal/esp32c3", "esp-hal/esp32c3",
"esp-hal-embassy/esp32c3", "esp-hal-embassy?/esp32c3",
"esp-wifi?/esp32c3", "esp-wifi?/esp32c3",
"esp-wifi?/phy-enable-usb", "esp-wifi?/phy-enable-usb",
] ]
esp32c6 = [ esp32c6 = [
"esp-backtrace/esp32c6", "esp-backtrace/esp32c6",
"esp-hal/esp32c6", "esp-hal/esp32c6",
"esp-hal-embassy/esp32c6", "esp-hal-embassy?/esp32c6",
"esp-wifi?/esp32c6", "esp-wifi?/esp32c6",
"esp-wifi?/phy-enable-usb", "esp-wifi?/phy-enable-usb",
] ]
esp32h2 = [ esp32h2 = [
"esp-backtrace/esp32h2", "esp-backtrace/esp32h2",
"esp-hal/esp32h2", "esp-hal/esp32h2",
"esp-hal-embassy/esp32h2", "esp-hal-embassy?/esp32h2",
"esp-wifi?/esp32h2", "esp-wifi?/esp32h2",
"esp-wifi?/phy-enable-usb", "esp-wifi?/phy-enable-usb",
] ]
@ -269,14 +269,14 @@ esp32s2 = [
"embedded-test/xtensa-semihosting", "embedded-test/xtensa-semihosting",
"esp-backtrace/esp32s2", "esp-backtrace/esp32s2",
"esp-hal/esp32s2", "esp-hal/esp32s2",
"esp-hal-embassy/esp32s2", "esp-hal-embassy?/esp32s2",
"esp-wifi?/esp32s2", "esp-wifi?/esp32s2",
] ]
esp32s3 = [ esp32s3 = [
"embedded-test/xtensa-semihosting", "embedded-test/xtensa-semihosting",
"esp-backtrace/esp32s3", "esp-backtrace/esp32s3",
"esp-hal/esp32s3", "esp-hal/esp32s3",
"esp-hal-embassy/esp32s3", "esp-hal-embassy?/esp32s3",
"esp-wifi?/esp32s3", "esp-wifi?/esp32s3",
"esp-wifi?/phy-enable-usb", "esp-wifi?/phy-enable-usb",
] ]