Skip to content
2 changes: 1 addition & 1 deletion esp-hal-embassy/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
## [Unreleased]

### Added

- Embassy executor CPU stats (#3728)

### Changed

Expand Down
5 changes: 5 additions & 0 deletions esp-hal-embassy/esp_config.yml
Original file line number Diff line number Diff line change
Expand Up @@ -45,3 +45,8 @@ options:
constraints:
- type:
validator: positive_integer

- name: low-power-wait-stats
description: Enables statistics for the low-power wait feature. Needs the `low-power-wait` config option to be enabled
default:
- value: true
67 changes: 67 additions & 0 deletions esp-hal-embassy/src/executor/thread.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,15 +3,24 @@
use core::marker::PhantomData;

use embassy_executor::Spawner;
#[cfg(all(low_power_wait, low_power_wait_stats))]
use embassy_time::Instant;
#[cfg(all(low_power_wait, multi_core))]
use esp_hal::interrupt::software::SoftwareInterrupt;
use esp_hal::{interrupt::Priority, system::Cpu};
#[cfg(all(low_power_wait, low_power_wait_stats))]
use portable_atomic::AtomicU64;
#[cfg(low_power_wait)]
use portable_atomic::{AtomicBool, Ordering};

use super::InnerExecutor;

pub(crate) const THREAD_MODE_CONTEXT: usize = 16;

// Count the ticks the CPU was in wait_impl
#[cfg(all(low_power_wait, low_power_wait_stats))]
static SLEEP_TICKS: AtomicU64 = AtomicU64::new(0);

/// global atomic used to keep track of whether there is work to do since sev()
/// is not available on either Xtensa or RISC-V
static SIGNAL_WORK_THREAD_MODE: [AtomicBool; Cpu::COUNT] =
Expand Down Expand Up @@ -172,6 +181,15 @@ This will use software-interrupt 3 which isn't available for anything else to wa

unsafe { self.inner.inner.poll() };

#[cfg(all(low_power_wait, low_power_wait_stats))]
let before = Instant::now().as_ticks();
#[cfg(low_power_wait)]
Self::wait_impl(self.cpu as usize);
#[cfg(all(low_power_wait, low_power_wait_stats))]
{
let after = Instant::now().as_ticks();
SLEEP_TICKS.fetch_add(after - before, Ordering::Relaxed);
}
hooks.on_idle();

#[cfg(low_power_wait)]
Expand Down Expand Up @@ -239,3 +257,52 @@ impl Default for Executor {
Self::new()
}
}

// Based on https://github.com/embassy-rs/embassy/pull/3920
#[cfg(all(low_power_wait, low_power_wait_stats))]
pub mod thread_low_power_wait_stats {
use embassy_time::Instant;
use portable_atomic::Ordering;

/// Statistics for the thread low power wait usage.
pub struct ThreadLowPowerWaitStats {
previous_tick: u64,
previous_sleep_tick: u64,
}

impl ThreadLowPowerWaitStats {
/// Create a new instance of `ThreadLowPowerWaitStats`.
/// This initializes the previous tick and sleep tick values
/// to the current time and the current sleep tick count.
/// Run get_usage() to get the current usage percentage periodically.
pub fn new() -> Self {
Self {
previous_tick: Instant::now().as_ticks(),
previous_sleep_tick: super::SLEEP_TICKS.load(Ordering::Relaxed),
}
}

/// Get the current usage percentage of the thread low power wait.
pub fn get_usage(&mut self) -> f32 {
let current_tick = Instant::now().as_ticks();
let current_sleep_tick = super::SLEEP_TICKS.load(Ordering::Relaxed);

// Calculate the ratio of time spent sleeping to total time since last report,
// the inverse of which is the time spent busy
let sleep_tick_difference = (current_sleep_tick - self.previous_sleep_tick) as f32;
let tick_difference = (current_tick - self.previous_tick) as f32;
let usage = 1f32 - sleep_tick_difference / tick_difference;

self.previous_tick = current_tick;
self.previous_sleep_tick = current_sleep_tick;

usage * 100.0
}
}

impl Default for ThreadLowPowerWaitStats {
fn default() -> Self {
Self::new()
}
}
}
2 changes: 2 additions & 0 deletions esp-hal-embassy/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,8 @@ mod fmt;
use esp_hal::timer::{AnyTimer, timg::Timer as TimgTimer};
pub use macros::embassy_main as main;

#[cfg(all(low_power_wait, low_power_wait_stats, feature = "executors"))]
pub use self::executor::thread_low_power_wait_stats::ThreadLowPowerWaitStats;
#[cfg(feature = "executors")]
pub use self::executor::{Callbacks, Executor, InterruptExecutor};
use self::time_driver::{EmbassyTimer, Timer};
Expand Down
Loading