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
- `ESP_HAL_EMBASSY_LOW_POWER_WAIT` configuration option. (#2329)
### Changed
- Reduce memory footprint by 4 bytes on multi-core MCUs.

View File

@ -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]

View File

@ -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<dyn Error>> {
@ -37,5 +38,16 @@ fn main() -> Result<(), Box<dyn Error>> {
// 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(())
}

View File

@ -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(|_| {

View File

@ -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",
]