Skip to content

Add embassy executor cpu stats #3728

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Draft
wants to merge 11 commits into
base: main
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from 8 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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
2 changes: 1 addition & 1 deletion esp-hal-embassy/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ test = false
[dependencies]
cfg-if = "1.0.0"
critical-section = "1.2.0"
esp-hal = { version = "1.0.0-beta.1", path = "../esp-hal" }
esp-hal = { version = "1.0.0-beta.1", path = "../esp-hal", features = ["unstable"] }
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
esp-hal = { version = "1.0.0-beta.1", path = "../esp-hal", features = ["unstable"] }
esp-hal = { version = "1.0.0-beta.1", path = "../esp-hal" }

We intentionally don't enable the feature.

Copy link
Contributor Author

@Szybet Szybet Jul 1, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Without it it doesn't compile standalone. If that is intentional I will disable it

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You need to enable unstable in your own project, not like this.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

done

portable-atomic = "1.11.0"
static_cell = "2.1.0"

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
64 changes: 64 additions & 0 deletions esp-hal-embassy/src/executor/thread.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,16 +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
#[cfg(low_power_wait)]
Expand Down Expand Up @@ -110,8 +118,15 @@ This will use software-interrupt 3 which isn't available for anything else to wa
loop {
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(cpu);
#[cfg(all(low_power_wait, low_power_wait_stats))]
{
let after = Instant::now().as_ticks();
SLEEP_TICKS.fetch_add(after - before, Ordering::Relaxed);
}
}
}

Expand Down Expand Up @@ -180,3 +195,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::{Executor, InterruptExecutor};
use self::time_driver::{EmbassyTimer, Timer};
Expand Down
Loading