Skip to content
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

Experimental embassy changes #2701

Draft
wants to merge 20 commits into
base: main
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all 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
38 changes: 26 additions & 12 deletions esp-hal-embassy/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -12,16 +12,20 @@ default-target = "riscv32imac-unknown-none-elf"
features = ["esp32c6"]

[dependencies]
critical-section = "1.2.0"
defmt = { version = "0.3.8", optional = true }
document-features = "0.2.10"
embassy-executor = { version = "0.6.3", optional = true }
embassy-time-driver = { version = "0.1.0", features = [ "tick-hz-1_000_000" ] }
esp-hal = { version = "0.22.0", path = "../esp-hal" }
log = { version = "0.4.22", optional = true }
macros = { version = "0.15.0", features = ["embassy"], package = "esp-hal-procmacros", path = "../esp-hal-procmacros" }
portable-atomic = "1.9.0"
static_cell = "2.1.0"
critical-section = "1.2.0"
defmt = { version = "0.3.8", optional = true }
document-features = "0.2.10"
embassy-executor = { version = "0.6.3", optional = true }
embassy-sync = { version = "0.6.1" }
embassy-time = { version = "0.3.0" }
embassy-time-driver = { version = "0.1.0", features = [ "tick-hz-1_000_000" ] }
embassy-time-queue-driver = { version = "0.1.0" }
esp-config = { version = "0.2.0", path = "../esp-config" }
esp-hal = { version = "0.22.0", path = "../esp-hal" }
log = { version = "0.4.22", optional = true }
macros = { version = "0.15.0", features = ["embassy"], package = "esp-hal-procmacros", path = "../esp-hal-procmacros" }
portable-atomic = "1.9.0"
static_cell = "2.1.0"

[build-dependencies]
esp-build = { version = "0.1.0", path = "../esp-build" }
Expand All @@ -45,8 +49,18 @@ defmt = ["dep:defmt", "embassy-executor?/defmt", "esp-hal/defmt"]
log = ["dep:log"]
## Provide `Executor` and `InterruptExecutor`
executors = ["dep:embassy-executor", "esp-hal/__esp_hal_embassy"]
## Use the executor-integrated `embassy-time` timer queue.
integrated-timers = ["embassy-executor?/integrated-timers"]
## Use the executor-integrated `embassy-time` timer queue. If not set, the crate provides a generic
## timer queue that can be used with any executor.
integrated-timers = ["embassy-executor?/integrated-timers", "executors"]
## Use a single, global timer queue. This option only needs a single alarm, no matter how many
## executors are used. Ignored if `integrated-timers` is not set.
single-queue = []

[lints.rust]
unexpected_cfgs = "allow"

[patch.crates-io]
embassy-executor = { git = "https://github.com/bugadani/embassy", branch = "refactor" }
embassy-time = { git = "https://github.com/bugadani/embassy", branch = "refactor" }
embassy-time-driver = { git = "https://github.com/bugadani/embassy", branch = "refactor" }
embassy-time-queue-driver = { git = "https://github.com/bugadani/embassy", branch = "refactor" }
22 changes: 21 additions & 1 deletion esp-hal-embassy/build.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
use std::{error::Error, str::FromStr};

use esp_build::assert_unique_used_features;
use esp_config::{generate_config, Value};
use esp_config::{generate_config, Validator, Value};
use esp_metadata::{Chip, Config};

fn main() -> Result<(), Box<dyn Error>> {
Expand Down Expand Up @@ -46,9 +46,29 @@ fn main() -> Result<(), Box<dyn Error>> {
"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.",
Value::Bool(true),
None
),
(
"generic-queue-size",
"The size of the generic queue. Only used if `generic-queue` is enabled.",
Value::Integer(64),
Some(Validator::PositiveInteger),
)],
true,
);

println!("cargo:rustc-check-cfg=cfg(integrated_timers)");
println!("cargo:rustc-check-cfg=cfg(single_queue)");
println!("cargo:rustc-check-cfg=cfg(generic_timers)");

if cfg!(feature = "integrated-timers") {
println!("cargo:rustc-cfg=integrated_timers");
if cfg!(feature = "single-queue") {
println!("cargo:rustc-cfg=single_queue");
}
} else {
println!("cargo:rustc-cfg=generic_timers");
println!("cargo:rustc-cfg=single_queue");
}

Ok(())
}
21 changes: 12 additions & 9 deletions esp-hal-embassy/src/executor/interrupt.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,15 @@

use core::{cell::UnsafeCell, mem::MaybeUninit};

use embassy_executor::{raw, SendSpawner};
use embassy_executor::SendSpawner;
use esp_hal::{
interrupt::{self, software::SoftwareInterrupt, InterruptHandler},
Cpu,
};
use portable_atomic::{AtomicUsize, Ordering};

use super::InnerExecutor;

const COUNT: usize = 3 + cfg!(not(multi_core)) as usize;
static mut EXECUTORS: [CallbackContext; COUNT] = [const { CallbackContext::new() }; COUNT];

Expand All @@ -19,15 +21,15 @@ static mut EXECUTORS: [CallbackContext; COUNT] = [const { CallbackContext::new()
/// software.
pub struct InterruptExecutor<const SWI: u8> {
core: AtomicUsize,
executor: UnsafeCell<MaybeUninit<raw::Executor>>,
executor: UnsafeCell<MaybeUninit<InnerExecutor>>,
interrupt: SoftwareInterrupt<SWI>,
}

unsafe impl<const SWI: u8> Send for InterruptExecutor<SWI> {}
unsafe impl<const SWI: u8> Sync for InterruptExecutor<SWI> {}

struct CallbackContext {
raw_executor: UnsafeCell<*mut raw::Executor>,
raw_executor: UnsafeCell<*mut InnerExecutor>,
}

impl CallbackContext {
Expand All @@ -37,11 +39,11 @@ impl CallbackContext {
}
}

fn get(&self) -> *mut raw::Executor {
fn get(&self) -> *mut InnerExecutor {
unsafe { *self.raw_executor.get() }
}

fn set(&self, executor: *mut raw::Executor) {
fn set(&self, executor: *mut InnerExecutor) {
unsafe { self.raw_executor.get().write(executor) };
}
}
Expand All @@ -52,7 +54,7 @@ extern "C" fn handle_interrupt<const NUM: u8>() {

unsafe {
let executor = unwrap!(EXECUTORS[NUM as usize].get().as_mut());
executor.poll();
executor.inner.poll();
}
}

Expand Down Expand Up @@ -99,7 +101,7 @@ impl<const SWI: u8> InterruptExecutor<SWI> {
unsafe {
(*self.executor.get())
.as_mut_ptr()
.write(raw::Executor::new((SWI as usize) as *mut ()));
.write(InnerExecutor::new(priority, (SWI as usize) as *mut ()));

EXECUTORS[SWI as usize].set((*self.executor.get()).as_mut_ptr());
}
Expand All @@ -117,7 +119,8 @@ impl<const SWI: u8> InterruptExecutor<SWI> {
.set_interrupt_handler(InterruptHandler::new(swi_handler, priority));

let executor = unsafe { (*self.executor.get()).assume_init_ref() };
executor.spawner().make_send()
executor.init();
executor.inner.spawner().make_send()
}

/// Get a SendSpawner for this executor
Expand All @@ -132,6 +135,6 @@ impl<const SWI: u8> InterruptExecutor<SWI> {
panic!("InterruptExecutor::spawner() called on uninitialized executor.");
}
let executor = unsafe { (*self.executor.get()).assume_init_ref() };
executor.spawner().make_send()
executor.inner.spawner().make_send()
}
}
33 changes: 33 additions & 0 deletions esp-hal-embassy/src/executor/mod.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,9 @@
use embassy_executor::raw;
use esp_hal::interrupt::Priority;

pub use self::{interrupt::*, thread::*};
#[cfg(not(single_queue))]
use crate::timer_queue::TimerQueue;

mod interrupt;
mod thread;
Expand All @@ -22,3 +27,31 @@ fn __pender(context: *mut ()) {
_ => unreachable!(),
}
}

#[repr(C)]
pub(crate) struct InnerExecutor {
inner: raw::Executor,
#[cfg(not(single_queue))]
pub(crate) timer_queue: TimerQueue,
}

impl InnerExecutor {
/// Create a new executor.
///
/// When the executor has work to do, it will call the pender function and
/// pass `context` to it.
///
/// See [`Executor`] docs for details on the pender.
pub(crate) fn new(_prio: Priority, context: *mut ()) -> Self {
Self {
inner: raw::Executor::new(context),
#[cfg(not(single_queue))]
timer_queue: TimerQueue::new(_prio),
}
}

pub(crate) fn init(&self) {
#[cfg(not(single_queue))]
self.timer_queue.set_context(self as *const _ as *mut ());
}
}
29 changes: 22 additions & 7 deletions esp-hal-embassy/src/executor/thread.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,15 @@

use core::marker::PhantomData;

use embassy_executor::{raw, Spawner};
use esp_hal::Cpu;
use embassy_executor::Spawner;
#[cfg(multi_core)]
use esp_hal::{interrupt::software::SoftwareInterrupt, macros::handler};
use esp_hal::{interrupt::Priority, Cpu};
#[cfg(low_power_wait)]
use portable_atomic::{AtomicBool, Ordering};

use super::InnerExecutor;

pub(crate) const THREAD_MODE_CONTEXT: usize = 16;

/// global atomic used to keep track of whether there is work to do since sev()
Expand Down Expand Up @@ -55,7 +57,7 @@ pub(crate) fn pend_thread_mode(_core: usize) {
create one instance per core. The executors don't steal tasks from each other."
)]
pub struct Executor {
inner: raw::Executor,
inner: InnerExecutor,
not_send: PhantomData<*mut ()>,
}

Expand All @@ -74,7 +76,10 @@ This will use software-interrupt 3 which isn't available for anything else to wa
}

Self {
inner: raw::Executor::new((THREAD_MODE_CONTEXT + Cpu::current() as usize) as *mut ()),
inner: InnerExecutor::new(
Priority::Priority1,
(THREAD_MODE_CONTEXT + Cpu::current() as usize) as *mut (),
),
not_send: PhantomData,
}
}
Expand All @@ -100,13 +105,15 @@ This will use software-interrupt 3 which isn't available for anything else to wa
///
/// This function never returns.
pub fn run(&'static mut self, init: impl FnOnce(Spawner)) -> ! {
init(self.inner.spawner());
self.inner.init();

init(self.inner.inner.spawner());

#[cfg(low_power_wait)]
let cpu = Cpu::current() as usize;

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

#[cfg(low_power_wait)]
Self::wait_impl(cpu);
Expand Down Expand Up @@ -138,7 +145,15 @@ This will use software-interrupt 3 which isn't available for anything else to wa
// sections in Xtensa are implemented via increasing `PS.INTLEVEL`.
// The critical section ends here. Take care not add code after
// `waiti` if it needs to be inside the CS.
unsafe { core::arch::asm!("waiti 0") };
// Do not lower INTLEVEL below the current value.
match token & 0x0F {
0 => unsafe { core::arch::asm!("waiti 0") },
1 => unsafe { core::arch::asm!("waiti 1") },
2 => unsafe { core::arch::asm!("waiti 2") },
3 => unsafe { core::arch::asm!("waiti 3") },
4 => unsafe { core::arch::asm!("waiti 4") },
_ => unsafe { core::arch::asm!("waiti 5") },
}
}
// If this races and some waker sets the signal, we'll reset it, but still poll.
SIGNAL_WORK_THREAD_MODE[cpu].store(false, Ordering::Relaxed);
Expand Down
3 changes: 2 additions & 1 deletion esp-hal-embassy/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -49,8 +49,9 @@ pub use self::executor::{Executor, InterruptExecutor};
use self::time_driver::{EmbassyTimer, Timer};

#[cfg(feature = "executors")]
mod executor;
pub(crate) mod executor;
mod time_driver;
mod timer_queue;

macro_rules! mk_static {
($t:ty,$val:expr) => {{
Expand Down
Loading
Loading