From c00efd485c0d422188badddde13613ea50668080 Mon Sep 17 00:00:00 2001 From: Patrick Gansterer Date: Mon, 1 Sep 2025 18:07:31 +0200 Subject: [PATCH 01/76] embassy-dfu-usb: Allow `application` and `dfu` feature at the same time Since there is no technical reason to disallow the use of both features at the same time, remove the artifical contraint to give developers more freedom with their implementations. --- embassy-usb-dfu/CHANGELOG.md | 2 ++ embassy-usb-dfu/src/application.rs | 1 + embassy-usb-dfu/src/dfu.rs | 1 + embassy-usb-dfu/src/lib.rs | 14 ++++---------- 4 files changed, 8 insertions(+), 10 deletions(-) diff --git a/embassy-usb-dfu/CHANGELOG.md b/embassy-usb-dfu/CHANGELOG.md index 466eff211..ef842945d 100644 --- a/embassy-usb-dfu/CHANGELOG.md +++ b/embassy-usb-dfu/CHANGELOG.md @@ -8,6 +8,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## Unreleased - ReleaseDate +- Allow enabling the `application` and `dfu` feature at the same time + ## 0.2.0 - 2025-08-27 - First release with changelog. diff --git a/embassy-usb-dfu/src/application.rs b/embassy-usb-dfu/src/application.rs index 4b7b72073..78eb2c083 100644 --- a/embassy-usb-dfu/src/application.rs +++ b/embassy-usb-dfu/src/application.rs @@ -1,3 +1,4 @@ +//! Application part of DFU logic use embassy_boot::BlockingFirmwareState; use embassy_time::{Duration, Instant}; use embassy_usb::control::{InResponse, OutResponse, Recipient, RequestType}; diff --git a/embassy-usb-dfu/src/dfu.rs b/embassy-usb-dfu/src/dfu.rs index 9a2f125fb..be28890bb 100644 --- a/embassy-usb-dfu/src/dfu.rs +++ b/embassy-usb-dfu/src/dfu.rs @@ -1,3 +1,4 @@ +//! DFU bootloader part of DFU logic use embassy_boot::{AlignedBuffer, BlockingFirmwareUpdater, FirmwareUpdaterError}; use embassy_usb::control::{InResponse, OutResponse, Recipient, RequestType}; use embassy_usb::driver::Driver; diff --git a/embassy-usb-dfu/src/lib.rs b/embassy-usb-dfu/src/lib.rs index 54ffa7276..e9f4278b6 100644 --- a/embassy-usb-dfu/src/lib.rs +++ b/embassy-usb-dfu/src/lib.rs @@ -6,21 +6,15 @@ mod fmt; pub mod consts; #[cfg(feature = "dfu")] -mod dfu; -#[cfg(feature = "dfu")] +pub mod dfu; +#[cfg(all(feature = "dfu", not(feature = "application")))] pub use self::dfu::*; #[cfg(feature = "application")] -mod application; -#[cfg(feature = "application")] +pub mod application; +#[cfg(all(feature = "application", not(feature = "dfu")))] pub use self::application::*; -#[cfg(any( - all(feature = "dfu", feature = "application"), - not(any(feature = "dfu", feature = "application")) -))] -compile_error!("usb-dfu must be compiled with exactly one of `dfu`, or `application` features"); - /// Provides a platform-agnostic interface for initiating a system reset. /// /// This crate exposes `ResetImmediate` when compiled with cortex-m or esp32c3 support, which immediately issues a From 224d6b03125dd9c799611883914dd99c5431e749 Mon Sep 17 00:00:00 2001 From: Remmirad Date: Sat, 6 Sep 2025 11:32:23 +0200 Subject: [PATCH 02/76] nrf: 802.15.4 embassy-net-driver --- embassy-net/README.md | 1 + embassy-nrf/CHANGELOG.md | 1 + embassy-nrf/Cargo.toml | 7 ++ embassy-nrf/README.md | 4 + embassy-nrf/src/embassy_net_802154_driver.rs | 96 +++++++++++++++ embassy-nrf/src/lib.rs | 11 ++ examples/nrf52840/Cargo.toml | 4 +- examples/nrf52840/src/bin/sixlowpan.rs | 120 +++++++++++++++++++ 8 files changed, 242 insertions(+), 2 deletions(-) create mode 100644 embassy-nrf/src/embassy_net_802154_driver.rs create mode 100644 examples/nrf52840/src/bin/sixlowpan.rs diff --git a/embassy-net/README.md b/embassy-net/README.md index 1722ffc7b..1c5b30a9c 100644 --- a/embassy-net/README.md +++ b/embassy-net/README.md @@ -25,6 +25,7 @@ unimplemented features of the network protocols. - [`embassy-stm32`](https://github.com/embassy-rs/embassy/tree/main/embassy-stm32) for the builtin Ethernet MAC in all STM32 chips (STM32F1, STM32F2, STM32F4, STM32F7, STM32H7, STM32H5). - [`embassy-net-wiznet`](https://github.com/embassy-rs/embassy/tree/main/embassy-net-wiznet) for Wiznet SPI Ethernet MAC+PHY chips (W5100S, W5500) - [`embassy-net-esp-hosted`](https://github.com/embassy-rs/embassy/tree/main/embassy-net-esp-hosted) for using ESP32 chips with the [`esp-hosted`](https://github.com/espressif/esp-hosted) firmware as WiFi adapters for another non-ESP32 MCU. +- [`embassy-nrf`](https://github.com/embassy-rs/embassy/tree/main/embassy-nrf) for IEEE 802.15.4 support on nrf chips. ## Examples diff --git a/embassy-nrf/CHANGELOG.md b/embassy-nrf/CHANGELOG.md index 5dc941b25..befa34ecf 100644 --- a/embassy-nrf/CHANGELOG.md +++ b/embassy-nrf/CHANGELOG.md @@ -9,6 +9,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## Unreleased - ReleaseDate - changed: nrf54l: Disable glitch detection and enable DC/DC in init. +- changed: Add embassy-net-driver-channel implementation for IEEE 802.15.4 ## 0.7.0 - 2025-08-26 diff --git a/embassy-nrf/Cargo.toml b/embassy-nrf/Cargo.toml index 2ce75cfac..4afd28fbd 100644 --- a/embassy-nrf/Cargo.toml +++ b/embassy-nrf/Cargo.toml @@ -79,6 +79,9 @@ gpiote = [] ## Use RTC1 as the time driver for `embassy-time`, with a tick rate of 32.768khz time-driver-rtc1 = ["_time-driver"] +## Enable embassy-net 802.15.4 driver +net-driver = ["_net-driver"] + ## Allow using the NFC pins as regular GPIO pins (P0_09/P0_10 on nRF52, P0_02/P0_03 on nRF53) nfc-pins-as-gpio = [] @@ -154,6 +157,8 @@ _nrf91 = [] _time-driver = ["dep:embassy-time-driver", "embassy-time-driver?/tick-hz-32_768", "dep:embassy-time-queue-utils", "embassy-embedded-hal/time"] +_net-driver = ["dep:embassy-net-driver-channel","dep:embassy-futures"] + # trustzone state. _s = [] _ns = [] @@ -177,6 +182,8 @@ embassy-sync = { version = "0.7.2", path = "../embassy-sync" } embassy-hal-internal = { version = "0.3.0", path = "../embassy-hal-internal", features = ["cortex-m", "prio-bits-3"] } embassy-embedded-hal = { version = "0.5.0", path = "../embassy-embedded-hal", default-features = false } embassy-usb-driver = { version = "0.2.0", path = "../embassy-usb-driver" } +embassy-net-driver-channel = { version = "0.3.2", path = "../embassy-net-driver-channel", optional = true} +embassy-futures = { version = "0.1.2", path = "../embassy-futures", optional = true} embedded-hal-02 = { package = "embedded-hal", version = "0.2.6", features = ["unproven"] } embedded-hal-1 = { package = "embedded-hal", version = "1.0" } diff --git a/embassy-nrf/README.md b/embassy-nrf/README.md index 3df5f1fa5..26b1fa5ea 100644 --- a/embassy-nrf/README.md +++ b/embassy-nrf/README.md @@ -28,6 +28,10 @@ allows running Rust code without a SPM or TF-M binary, saving flash space and si If the `time-driver-rtc1` feature is enabled, the HAL uses the RTC peripheral as a global time driver for [embassy-time](https://crates.io/crates/embassy-time), with a tick rate of 32768 Hz. +## Embassy-net-driver + +If the board supports IEEE 802.15.4 (see `src/radio/mod.rs`) the corresponding [embassy-net-driver](https://crates.io/crates/embassy-net-driver) implementation can be enabled with the feature `net-driver`. + ## Embedded-hal The `embassy-nrf` HAL implements the traits from [embedded-hal](https://crates.io/crates/embedded-hal) (v0.2 and 1.0) and [embedded-hal-async](https://crates.io/crates/embedded-hal-async), as well as [embedded-io](https://crates.io/crates/embedded-io) and [embedded-io-async](https://crates.io/crates/embedded-io-async). diff --git a/embassy-nrf/src/embassy_net_802154_driver.rs b/embassy-nrf/src/embassy_net_802154_driver.rs new file mode 100644 index 000000000..8662be787 --- /dev/null +++ b/embassy-nrf/src/embassy_net_802154_driver.rs @@ -0,0 +1,96 @@ +//! embassy-net IEEE 802.15.4 driver + +use embassy_futures::select::{select3, Either3}; +use embassy_net_driver_channel::driver::LinkState; +use embassy_net_driver_channel::{self as ch}; +use embassy_time::{Duration, Ticker}; + +use crate::radio::ieee802154::{Packet, Radio}; +use crate::radio::InterruptHandler; +use crate::{self as nrf, interrupt}; + +/// MTU for the nrf radio. +pub const MTU: usize = Packet::CAPACITY as usize; + +/// embassy-net device for the driver. +pub type Device<'d> = embassy_net_driver_channel::Device<'d, MTU>; + +/// Internal state for the embassy-net driver. +pub struct State { + ch_state: ch::State, +} + +impl State { + /// Create a new `State`. + pub const fn new() -> Self { + Self { + ch_state: ch::State::new(), + } + } +} + +/// Background runner for the driver. +/// +/// You must call `.run()` in a background task for the driver to operate. +pub struct Runner<'d, T: nrf::radio::Instance> { + radio: nrf::radio::ieee802154::Radio<'d, T>, + ch: ch::Runner<'d, MTU>, +} + +impl<'d, T: nrf::radio::Instance> Runner<'d, T> { + /// Drives the radio. Needs to run to use the driver. + pub async fn run(mut self) -> ! { + let (state_chan, mut rx_chan, mut tx_chan) = self.ch.split(); + let mut tick = Ticker::every(Duration::from_millis(500)); + let mut packet = Packet::new(); + state_chan.set_link_state(LinkState::Up); + loop { + match select3( + async { + let rx_buf = rx_chan.rx_buf().await; + self.radio.receive(&mut packet).await.ok().map(|_| rx_buf) + }, + tx_chan.tx_buf(), + tick.next(), + ) + .await + { + Either3::First(Some(rx_buf)) => { + let len = rx_buf.len().min(packet.len() as usize); + (&mut rx_buf[..len]).copy_from_slice(&*packet); + rx_chan.rx_done(len); + } + Either3::Second(tx_buf) => { + let len = tx_buf.len().min(Packet::CAPACITY as usize); + packet.copy_from_slice(&tx_buf[..len]); + self.radio.try_send(&mut packet).await.ok().unwrap(); + tx_chan.tx_done(); + } + _ => {} + } + } + } +} + +/// Make sure to use `HfclkSource::ExternalXtal` as the `hfclk_source` +/// to use the radio (nrf52840 product spec v1.11 5.4.1) +/// ``` +/// # use embassy_nrf::config::*; +/// let mut config = Config::default(); +/// config.hfclk_source = HfclkSource::ExternalXtal; +/// ``` +pub async fn new<'a, const N_RX: usize, const N_TX: usize, T: nrf::radio::Instance, Irq>( + mac_addr: [u8; 8], + radio: nrf::Peri<'a, T>, + irq: Irq, + state: &'a mut State, +) -> Result<(Device<'a>, Runner<'a, T>), ()> +where + Irq: interrupt::typelevel::Binding> + 'a, +{ + let radio = Radio::new(radio, irq); + + let (runner, device) = ch::new(&mut state.ch_state, ch::driver::HardwareAddress::Ieee802154(mac_addr)); + + Ok((device, Runner { ch: runner, radio })) +} diff --git a/embassy-nrf/src/lib.rs b/embassy-nrf/src/lib.rs index aa4801897..897e660b8 100644 --- a/embassy-nrf/src/lib.rs +++ b/embassy-nrf/src/lib.rs @@ -137,6 +137,17 @@ pub mod qspi; #[cfg(not(feature = "_nrf54l"))] // TODO #[cfg(not(any(feature = "_nrf91", feature = "_nrf5340-app")))] pub mod radio; + +#[cfg(any( + feature = "nrf52811", + feature = "nrf52820", + feature = "nrf52833", + feature = "nrf52840", + feature = "_nrf5340-net" +))] +#[cfg(feature = "_net-driver")] +pub mod embassy_net_802154_driver; + #[cfg(not(feature = "_nrf54l"))] // TODO #[cfg(feature = "_nrf5340")] pub mod reset; diff --git a/examples/nrf52840/Cargo.toml b/examples/nrf52840/Cargo.toml index a9339bcd3..452e83b7e 100644 --- a/examples/nrf52840/Cargo.toml +++ b/examples/nrf52840/Cargo.toml @@ -10,8 +10,8 @@ embassy-futures = { version = "0.1.2", path = "../../embassy-futures" } embassy-sync = { version = "0.7.2", path = "../../embassy-sync", features = ["defmt"] } embassy-executor = { version = "0.9.0", path = "../../embassy-executor", features = ["arch-cortex-m", "executor-thread", "executor-interrupt", "defmt"] } embassy-time = { version = "0.5.0", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime"] } -embassy-nrf = { version = "0.7.0", path = "../../embassy-nrf", features = ["defmt", "nrf52840", "time-driver-rtc1", "gpiote", "unstable-pac", "time"] } -embassy-net = { version = "0.7.1", path = "../../embassy-net", features = ["defmt", "tcp", "dhcpv4", "medium-ethernet"] } +embassy-nrf = { version = "0.7.0", path = "../../embassy-nrf", features = ["defmt", "nrf52840", "time-driver-rtc1", "gpiote", "unstable-pac", "time", "net-driver"] } +embassy-net = { version = "0.7.1", path = "../../embassy-net", features = ["defmt", "tcp", "dhcpv4", "medium-ethernet","udp", "medium-ieee802154", "proto-ipv6"] } embassy-usb = { version = "0.5.1", path = "../../embassy-usb", features = ["defmt"] } embedded-io = { version = "0.6.0", features = ["defmt-03"] } embedded-io-async = { version = "0.6.1", features = ["defmt-03"] } diff --git a/examples/nrf52840/src/bin/sixlowpan.rs b/examples/nrf52840/src/bin/sixlowpan.rs new file mode 100644 index 000000000..00a597366 --- /dev/null +++ b/examples/nrf52840/src/bin/sixlowpan.rs @@ -0,0 +1,120 @@ +#![no_std] +#![no_main] + +use core::net::Ipv6Addr; + +use defmt::{info, unwrap, warn}; +use embassy_executor::Spawner; +use embassy_net::udp::{PacketMetadata, UdpMetadata, UdpSocket}; +use embassy_net::{IpAddress, IpEndpoint, IpListenEndpoint, Ipv6Cidr, StackResources, StaticConfigV6}; +use embassy_nrf::config::{Config, HfclkSource}; +use embassy_nrf::rng::Rng; +use embassy_nrf::{bind_interrupts, embassy_net_802154_driver as net, peripherals, radio}; +use embassy_time::Delay; +use embedded_hal_async::delay::DelayNs; +use static_cell::StaticCell; +use {defmt_rtt as _, panic_probe as _}; + +bind_interrupts!(struct Irqs { + RADIO => radio::InterruptHandler; + RNG => embassy_nrf::rng::InterruptHandler; +}); + +#[embassy_executor::task] +async fn ieee802154_task(runner: net::Runner<'static, peripherals::RADIO>) -> ! { + runner.run().await +} + +#[embassy_executor::task] +async fn net_task(mut runner: embassy_net::Runner<'static, net::Device<'static>>) -> ! { + runner.run().await +} + +#[embassy_executor::main] +async fn main(spawner: Spawner) { + let mut config = Config::default(); + // Necessary to run the radio nrf52840 v1.11 5.4.1 + config.hfclk_source = HfclkSource::ExternalXtal; + let p = embassy_nrf::init(config); + + let mac_addr: [u8; 8] = [2, 3, 4, 5, 6, 7, 8, 9]; + static NRF802154_STATE: StaticCell> = StaticCell::new(); + let (device, runner) = net::new(mac_addr, p.RADIO, Irqs, NRF802154_STATE.init(net::State::new())) + .await + .unwrap(); + + spawner.spawn(unwrap!(ieee802154_task(runner))); + + // Swap these when flashing a second board + let peer = Ipv6Addr::new(0xfe80, 0, 0, 0, 0xd701, 0xda3f, 0x3955, 0x82a4); + let local = Ipv6Addr::new(0xfe80, 0, 0, 0, 0xd701, 0xda3f, 0x3955, 0x82a5); + + let config = embassy_net::Config::ipv6_static(StaticConfigV6 { + address: Ipv6Cidr::new(local, 64), + gateway: None, + dns_servers: Default::default(), + }); + + // Generate random seed + let mut rng = Rng::new(p.RNG, Irqs); + let mut seed = [0; 8]; + rng.blocking_fill_bytes(&mut seed); + let seed = u64::from_le_bytes(seed); + + // Init network stack + static RESOURCES: StaticCell> = StaticCell::new(); + let (stack, runner) = embassy_net::new(device, config, RESOURCES.init(StackResources::new()), seed); + + spawner.spawn(unwrap!(net_task(runner))); + + let mut rx_buffer = [0; 2096]; + let mut tx_buffer = [0; 2096]; + let mut tx_m_buffer = [PacketMetadata::EMPTY; 5]; + let mut rx_m_buffer = [PacketMetadata::EMPTY; 5]; + + let mut delay = Delay; + loop { + let mut socket = UdpSocket::new( + stack, + &mut tx_m_buffer, + &mut rx_buffer, + &mut rx_m_buffer, + &mut tx_buffer, + ); + socket + .bind(IpListenEndpoint { + addr: Some(IpAddress::Ipv6(local)), + port: 1234, + }) + .unwrap(); + let rep = UdpMetadata { + endpoint: IpEndpoint { + addr: IpAddress::Ipv6(peer), + port: 1234, + }, + local_address: Some(IpAddress::Ipv6(local)), + meta: Default::default(), + }; + + info!("Listening on {:?} UDP:1234...", local); + + let mut recv_buf = [0; 12]; + loop { + delay.delay_ms(2000).await; + if socket.may_recv() { + let n = match socket.recv_from(&mut recv_buf).await { + Ok((0, _)) => panic!(), + Ok((n, _)) => n, + Err(e) => { + warn!("read error: {:?}", e); + break; + } + }; + info!("Received {:02x}", &recv_buf[..n]); + } + + info!("Sending"); + socket.send_to(b"Hello World", rep).await.unwrap(); + } + } +} From c101acbdc3593936f6e966cb33e8ba72698a1a31 Mon Sep 17 00:00:00 2001 From: Carl Kadie Date: Sat, 6 Sep 2025 14:05:45 -0700 Subject: [PATCH 03/76] Update Embassy in the Wild with no_std Raspberry Pi Pico clock demonstrating layered Embassy tasks (I also added a note that newer entries are at the top. If this isn't right, let me know or change.) --- docs/pages/embassy_in_the_wild.adoc | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/docs/pages/embassy_in_the_wild.adoc b/docs/pages/embassy_in_the_wild.adoc index 620794c31..cedbedada 100644 --- a/docs/pages/embassy_in_the_wild.adoc +++ b/docs/pages/embassy_in_the_wild.adoc @@ -2,6 +2,10 @@ Here are known examples of real-world projects which make use of Embassy. Feel free to link:https://github.com/embassy-rs/embassy/blob/main/docs/pages/embassy_in_the_wild.adoc[add more]! +_newer entries at the top_ + +* link:https://github.com/CarlKCarlK/clock[Embassy Clock: Layered, modular bare-metal clock with emulation] +** A `no_std` Raspberry Pi Pico clock demonstrating layered Embassy tasks (Display->Blinker->Clock) for clean separation of multiplexing, blinking, and UI logic. Features single-button HH:MM/MM:SS time-set UI, heapless data structures, and a Renode emulator for hardware-free testing. See link:https://medium.com/@carlmkadie/how-rust-embassy-shine-on-embedded-devices-part-2-aad1adfccf72[this article] for details. * link:https://github.com/1-rafael-1/simple-robot[A simple tracked robot based on Raspberry Pi Pico 2] ** A hobbyist project building a tracked robot with basic autonomous and manual drive mode. * link:https://github.com/1-rafael-1/pi-pico-alarmclock-rust[A Raspberry Pi Pico W Alarmclock] From 86aff0e63e21bd01fa14ad3ad5470b0cf433009d Mon Sep 17 00:00:00 2001 From: Gabriel Smith Date: Tue, 2 Sep 2025 17:00:34 -0400 Subject: [PATCH 04/76] chore: bump embassy-executor to 0.9.1 to match released version --- embassy-executor/Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/embassy-executor/Cargo.toml b/embassy-executor/Cargo.toml index 7763adbe5..41636a26f 100644 --- a/embassy-executor/Cargo.toml +++ b/embassy-executor/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "embassy-executor" -version = "0.9.0" +version = "0.9.1" edition = "2021" license = "MIT OR Apache-2.0" description = "async/await executor designed for embedded usage" From b9023296f66ae8663485c37e8139c9832aae4849 Mon Sep 17 00:00:00 2001 From: Chris Dell Date: Mon, 8 Sep 2025 22:15:16 +0100 Subject: [PATCH 05/76] Add Wiznet W6100 driver --- embassy-net-wiznet/src/chip/mod.rs | 3 + embassy-net-wiznet/src/chip/w5100s.rs | 1 + embassy-net-wiznet/src/chip/w5500.rs | 1 + embassy-net-wiznet/src/chip/w6100.rs | 83 +++++++++++++++++++++++++++ embassy-net-wiznet/src/device.rs | 2 +- 5 files changed, 89 insertions(+), 1 deletion(-) create mode 100644 embassy-net-wiznet/src/chip/w6100.rs diff --git a/embassy-net-wiznet/src/chip/mod.rs b/embassy-net-wiznet/src/chip/mod.rs index 2e7a9ed6c..6e6e5cb78 100644 --- a/embassy-net-wiznet/src/chip/mod.rs +++ b/embassy-net-wiznet/src/chip/mod.rs @@ -4,6 +4,8 @@ pub use w5500::W5500; mod w5100s; use embedded_hal_async::spi::SpiDevice; pub use w5100s::W5100S; +mod w6100; +pub use w6100::W6100; pub(crate) trait SealedChip { type Address; @@ -29,6 +31,7 @@ pub(crate) trait SealedChip { const SOCKET_RX_DATA_READ_PTR: Self::Address; const SOCKET_INTR_MASK: Self::Address; const SOCKET_INTR: Self::Address; + const SOCKET_INTR_CLR: Self::Address; const SOCKET_MODE_VALUE: u8; diff --git a/embassy-net-wiznet/src/chip/w5100s.rs b/embassy-net-wiznet/src/chip/w5100s.rs index 4c4b7ab16..1eef2369e 100644 --- a/embassy-net-wiznet/src/chip/w5100s.rs +++ b/embassy-net-wiznet/src/chip/w5100s.rs @@ -29,6 +29,7 @@ impl super::SealedChip for W5100S { const SOCKET_RX_DATA_READ_PTR: Self::Address = SOCKET_BASE + 0x28; const SOCKET_INTR_MASK: Self::Address = SOCKET_BASE + 0x2C; const SOCKET_INTR: Self::Address = SOCKET_BASE + 0x02; + const SOCKET_INTR_CLR: Self::Address = SOCKET_BASE + 0x02; const SOCKET_MODE_VALUE: u8 = (1 << 2) | (1 << 6); diff --git a/embassy-net-wiznet/src/chip/w5500.rs b/embassy-net-wiznet/src/chip/w5500.rs index 5cfcb94e4..198ba3226 100644 --- a/embassy-net-wiznet/src/chip/w5500.rs +++ b/embassy-net-wiznet/src/chip/w5500.rs @@ -33,6 +33,7 @@ impl super::SealedChip for W5500 { const SOCKET_RX_DATA_READ_PTR: Self::Address = (RegisterBlock::Socket0, 0x28); const SOCKET_INTR_MASK: Self::Address = (RegisterBlock::Socket0, 0x2C); const SOCKET_INTR: Self::Address = (RegisterBlock::Socket0, 0x02); + const SOCKET_INTR_CLR: Self::Address = (RegisterBlock::Socket0, 0x02); const SOCKET_MODE_VALUE: u8 = (1 << 2) | (1 << 7); diff --git a/embassy-net-wiznet/src/chip/w6100.rs b/embassy-net-wiznet/src/chip/w6100.rs new file mode 100644 index 000000000..740b0edaf --- /dev/null +++ b/embassy-net-wiznet/src/chip/w6100.rs @@ -0,0 +1,83 @@ +use embedded_hal_async::spi::{Operation, SpiDevice}; + +#[repr(u8)] +pub enum RegisterBlock { + Common = 0x00, + Socket0 = 0x01, + TxBuf = 0x02, + RxBuf = 0x03, +} + +/// Wiznet W6100 chip. +pub enum W6100 {} + +impl super::Chip for W6100 {} +impl super::SealedChip for W6100 { + type Address = (RegisterBlock, u16); + + const CHIP_VERSION: u8 = 0x46; + + const COMMON_MODE: Self::Address = (RegisterBlock::Common, 0x2004); + const COMMON_MAC: Self::Address = (RegisterBlock::Common, 0x4120); + // SIMR (SOCKET Interrupt Mask Register) + const COMMON_SOCKET_INTR: Self::Address = (RegisterBlock::Common, 0x2114); + const COMMON_PHY_CFG: Self::Address = (RegisterBlock::Common, 0x3000); + const COMMON_VERSION: Self::Address = (RegisterBlock::Common, 0x0002); + + const SOCKET_MODE: Self::Address = (RegisterBlock::Socket0, 0x0000); + const SOCKET_COMMAND: Self::Address = (RegisterBlock::Socket0, 0x0010); + const SOCKET_RXBUF_SIZE: Self::Address = (RegisterBlock::Socket0, 0x0220); + const SOCKET_TXBUF_SIZE: Self::Address = (RegisterBlock::Socket0, 0x0200); + const SOCKET_TX_FREE_SIZE: Self::Address = (RegisterBlock::Socket0, 0x0204); + const SOCKET_TX_DATA_WRITE_PTR: Self::Address = (RegisterBlock::Socket0, 0x020C); + const SOCKET_RECVD_SIZE: Self::Address = (RegisterBlock::Socket0, 0x0224); + const SOCKET_RX_DATA_READ_PTR: Self::Address = (RegisterBlock::Socket0, 0x0228); + // Sn_IMR (SOCKET n Interrupt Mask Register) + const SOCKET_INTR_MASK: Self::Address = (RegisterBlock::Socket0, 0x0024); + // Sn_IR (SOCKET n Interrupt Register) + const SOCKET_INTR: Self::Address = (RegisterBlock::Socket0, 0x0020); + // Sn_IRCLR (Sn_IR Clear Register) + const SOCKET_INTR_CLR: Self::Address = (RegisterBlock::Socket0, 0x0028); + + // MACRAW mode. See Page 57 of https://docs.wiznet.io/img/products/w6100/w6100_ds_v105e.pdf + // Note: Bit 7 is MAC filter. On the W5500 this is normally turned ON however the W6100 will not successfully retrieve an IP address with this enabled. Disabling for now and will have live with the extra noise. + const SOCKET_MODE_VALUE: u8 = 0b0000_0111; + + const BUF_SIZE: u16 = 0x1000; + const AUTO_WRAP: bool = true; + + fn rx_addr(addr: u16) -> Self::Address { + (RegisterBlock::RxBuf, addr) + } + + fn tx_addr(addr: u16) -> Self::Address { + (RegisterBlock::TxBuf, addr) + } + + async fn bus_read( + spi: &mut SPI, + address: Self::Address, + data: &mut [u8], + ) -> Result<(), SPI::Error> { + let address_phase = address.1.to_be_bytes(); + let control_phase = [(address.0 as u8) << 3]; + let operations = &mut [ + Operation::Write(&address_phase), + Operation::Write(&control_phase), + Operation::TransferInPlace(data), + ]; + spi.transaction(operations).await + } + + async fn bus_write(spi: &mut SPI, address: Self::Address, data: &[u8]) -> Result<(), SPI::Error> { + let address_phase = address.1.to_be_bytes(); + let control_phase = [(address.0 as u8) << 3 | 0b0000_0100]; + let data_phase = data; + let operations = &mut [ + Operation::Write(&address_phase[..]), + Operation::Write(&control_phase), + Operation::Write(&data_phase), + ]; + spi.transaction(operations).await + } +} diff --git a/embassy-net-wiznet/src/device.rs b/embassy-net-wiznet/src/device.rs index d2b6bb0c3..8ef92b022 100644 --- a/embassy-net-wiznet/src/device.rs +++ b/embassy-net-wiznet/src/device.rs @@ -125,7 +125,7 @@ impl WiznetDevice { async fn reset_interrupt(&mut self, code: Interrupt) -> Result<(), SPI::Error> { let data = [code as u8]; - self.bus_write(C::SOCKET_INTR, &data).await + self.bus_write(C::SOCKET_INTR_CLR, &data).await } async fn get_tx_write_ptr(&mut self) -> Result { From bed230095d11ee32939ee51f2f298817691bb6aa Mon Sep 17 00:00:00 2001 From: Chris Dell Date: Wed, 10 Sep 2025 11:24:29 +0100 Subject: [PATCH 06/76] Update changelog with new W6100 (unreleased) --- embassy-net-wiznet/CHANGELOG.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/embassy-net-wiznet/CHANGELOG.md b/embassy-net-wiznet/CHANGELOG.md index e464efa69..a74dc3125 100644 --- a/embassy-net-wiznet/CHANGELOG.md +++ b/embassy-net-wiznet/CHANGELOG.md @@ -8,6 +8,9 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## Unreleased - ReleaseDate +- Added experimental W6100 driver with disabled MAC filter (does not currently work with it enabled) +- Introduced `SOCKET_INTR_CLR` register which is needed on W6100 and later models (on W5100/W5500 this is shared with `SOCKET_INTR` and the address is the same) + ## 0.2.1 - 2025-08-26 ## 0.1.1 - 2025-08-14 From 88c4274547db9fe6e6e720c69e61c4912fe03abd Mon Sep 17 00:00:00 2001 From: Birk Tjelmeland Date: Mon, 8 Sep 2025 13:48:48 +0200 Subject: [PATCH 07/76] stm32/usart: fix blocking flush The PR in #2416 fixes buffered usart flushing, but only for the async functions. This commit introduces the same fixes to the blocking functions. --- embassy-stm32/CHANGELOG.md | 1 + embassy-stm32/src/usart/buffered.rs | 4 +++- 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/embassy-stm32/CHANGELOG.md b/embassy-stm32/CHANGELOG.md index ba565f663..4ea11b664 100644 --- a/embassy-stm32/CHANGELOG.md +++ b/embassy-stm32/CHANGELOG.md @@ -15,6 +15,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Add I2S support for STM32F1, STM32C0, STM32F0, STM32F3, STM32F7, STM32G0, STM32WL, STM32H5, STM32H7RS - fix: STM32: Prevent dropped DacChannel from disabling Dac peripheral if another DacChannel is still in scope ([#4577](https://github.com/embassy-rs/embassy/pull/4577)) - feat: Added support for more OctoSPI configurations (e.g. APS6408 RAM) ([#4581](https://github.com/embassy-rs/embassy/pull/4581)) +- fix: stm32/usart: fix bug with blocking flush in buffered uart ([#4648](https://github.com/embassy-rs/embassy/pull/4648)) ## 0.4.0 - 2025-08-26 diff --git a/embassy-stm32/src/usart/buffered.rs b/embassy-stm32/src/usart/buffered.rs index 890c8a80e..c734eed49 100644 --- a/embassy-stm32/src/usart/buffered.rs +++ b/embassy-stm32/src/usart/buffered.rs @@ -692,6 +692,8 @@ impl<'d> BufferedUartTx<'d> { fn blocking_write(&self, buf: &[u8]) -> Result { loop { let state = self.state; + state.tx_done.store(false, Ordering::Release); + let empty = state.tx_buf.is_empty(); let mut tx_writer = unsafe { state.tx_buf.writer() }; @@ -713,7 +715,7 @@ impl<'d> BufferedUartTx<'d> { fn blocking_flush(&self) -> Result<(), Error> { loop { let state = self.state; - if state.tx_buf.is_empty() { + if state.tx_done.load(Ordering::Acquire) { return Ok(()); } } From 535c80e61f17e4ee4605e00623aabeda2181352d Mon Sep 17 00:00:00 2001 From: James Munns Date: Thu, 20 Mar 2025 09:47:56 +0100 Subject: [PATCH 08/76] Add initial DRS scheduler placeholder * Start hacking in cordyceps This adds a third kind of runqueue, for now it should work the same as the current "atomics" runqueue, but uses a cordyceps TransferStack instead of the existing home-rolled linked list. * Clean up, use new cordyceps feature * A bit more cleanup * Update docs to be more clear --- embassy-executor/Cargo.toml | 9 +++ embassy-executor/src/raw/mod.rs | 64 +++++++++++++++++-- .../src/raw/run_queue_drs_atomics.rs | 47 ++++++++++++++ 3 files changed, 116 insertions(+), 4 deletions(-) create mode 100644 embassy-executor/src/raw/run_queue_drs_atomics.rs diff --git a/embassy-executor/Cargo.toml b/embassy-executor/Cargo.toml index 41636a26f..1cd732dfa 100644 --- a/embassy-executor/Cargo.toml +++ b/embassy-executor/Cargo.toml @@ -76,6 +76,12 @@ js-sys = { version = "0.3", optional = true } # arch-avr dependencies avr-device = { version = "0.7.0", optional = true } +[dependencies.cordyceps] +version = "0.3" +git = "https://github.com/hawkw/mycelium" +rev = "aaad19480d175bfc290f1d4dc2d435c6eb3d9fc5" +optional = true + [dev-dependencies] critical-section = { version = "1.1", features = ["std"] } trybuild = "1.0" @@ -125,3 +131,6 @@ trace = ["_any_trace"] ## Enable support for rtos-trace framework rtos-trace = ["_any_trace", "metadata-name", "dep:rtos-trace", "dep:embassy-time-driver"] _any_trace = [] + +## Enable "Deadline Rank Scheduler" +drs-scheduler = ["dep:cordyceps"] diff --git a/embassy-executor/src/raw/mod.rs b/embassy-executor/src/raw/mod.rs index 4280c5750..894a996ec 100644 --- a/embassy-executor/src/raw/mod.rs +++ b/embassy-executor/src/raw/mod.rs @@ -7,7 +7,14 @@ //! Using this module requires respecting subtle safety contracts. If you can, prefer using the safe //! [executor wrappers](crate::Executor) and the [`embassy_executor::task`](embassy_executor_macros::task) macro, which are fully safe. -#[cfg_attr(target_has_atomic = "ptr", path = "run_queue_atomics.rs")] +#[cfg_attr( + all(not(feature = "drs-scheduler"), target_has_atomic = "ptr"), + path = "run_queue_atomics.rs", +)] +#[cfg_attr( + all(feature = "drs-scheduler", target_has_atomic = "ptr"), + path = "run_queue_drs_atomics.rs", +)] #[cfg_attr(not(target_has_atomic = "ptr"), path = "run_queue_critical_section.rs")] mod run_queue; @@ -33,6 +40,8 @@ use core::marker::PhantomData; use core::mem; use core::pin::Pin; use core::ptr::NonNull; +#[cfg(feature = "drs-scheduler")] +use core::ptr::addr_of_mut; #[cfg(not(feature = "arch-avr"))] use core::sync::atomic::AtomicPtr; use core::sync::atomic::Ordering; @@ -42,7 +51,9 @@ use embassy_executor_timer_queue::TimerQueueItem; #[cfg(feature = "arch-avr")] use portable_atomic::AtomicPtr; -use self::run_queue::{RunQueue, RunQueueItem}; +use self::run_queue::RunQueue; +#[cfg(not(feature = "drs-scheduler"))] +use self::run_queue::RunQueueItem; use self::state::State; use self::util::{SyncUnsafeCell, UninitCell}; pub use self::waker::task_from_waker; @@ -54,6 +65,9 @@ extern "Rust" fn __embassy_time_queue_item_from_waker(waker: &Waker) -> &'static unsafe { task_from_waker(waker).timer_queue_item() } } +#[cfg(feature = "drs-scheduler")] +use cordyceps::{stack, Linked}; + /// Raw task header for use in task pointers. /// /// A task can be in one of the following states: @@ -93,9 +107,29 @@ extern "Rust" fn __embassy_time_queue_item_from_waker(waker: &Waker) -> &'static /// - 4: A run-queued task exits - `TaskStorage::poll -> Poll::Ready` /// - 5: Task is dequeued. The task's future is not polled, because exiting the task replaces its `poll_fn`. /// - 6: A task is waken when it is not spawned - `wake_task -> State::run_enqueue` +#[cfg_attr(feature = "drs-scheduler", repr(C))] pub(crate) struct TaskHeader { - pub(crate) state: State, + // TODO(AJM): Make a decision whether we want to support the spicier "pointer recast"/"type punning" + // method of implementing the `cordyceps::Linked` trait or not. + // + // Currently, I do the safer version with `addr_of_mut!`, which doesn't REQUIRE that the first + // element is the `links` field, at the potential cost of a little extra pointer math. + // + // The optimizer *might* (total guess) notice that we are always doing an offset of zero in the + // call to `addr_of_mut` in the `impl Linked for TaskHeader` below, and get the best of both worlds, + // but right now this is maybe a little over cautious. + // + // See https://docs.rs/cordyceps/latest/cordyceps/trait.Linked.html#implementing-linkedlinks for + // more context on the choices here. + #[cfg(feature = "drs-scheduler")] + pub(crate) links: stack::Links, + + // TODO(AJM): We could potentially replace RunQueueItem for other runqueue impls, though + // right now cordyceps doesn't work on non-atomic systems + #[cfg(not(feature = "drs-scheduler"))] pub(crate) run_queue_item: RunQueueItem, + + pub(crate) state: State, pub(crate) executor: AtomicPtr, poll_fn: SyncUnsafeCell>, @@ -108,6 +142,25 @@ pub(crate) struct TaskHeader { all_tasks_next: AtomicPtr, } +#[cfg(feature = "drs-scheduler")] +unsafe impl Linked> for TaskHeader { + type Handle = TaskRef; + + fn into_ptr(r: Self::Handle) -> NonNull { + r.ptr.cast() + } + + unsafe fn from_ptr(ptr: NonNull) -> Self::Handle { + let ptr: NonNull = ptr; + TaskRef { ptr } + } + + unsafe fn links(ptr: NonNull) -> NonNull> { + let ptr: *mut TaskHeader = ptr.as_ptr(); + NonNull::new_unchecked(addr_of_mut!((*ptr).links)) + } +} + /// This is essentially a `&'static TaskStorage` where the type of the future has been erased. #[derive(Debug, Clone, Copy, PartialEq)] pub struct TaskRef { @@ -198,8 +251,11 @@ impl TaskStorage { pub const fn new() -> Self { Self { raw: TaskHeader { - state: State::new(), + #[cfg(not(feature = "drs-scheduler"))] run_queue_item: RunQueueItem::new(), + #[cfg(feature = "drs-scheduler")] + links: stack::Links::new(), + state: State::new(), executor: AtomicPtr::new(core::ptr::null_mut()), // Note: this is lazily initialized so that a static `TaskStorage` will go in `.bss` poll_fn: SyncUnsafeCell::new(None), diff --git a/embassy-executor/src/raw/run_queue_drs_atomics.rs b/embassy-executor/src/raw/run_queue_drs_atomics.rs new file mode 100644 index 000000000..53ada1b14 --- /dev/null +++ b/embassy-executor/src/raw/run_queue_drs_atomics.rs @@ -0,0 +1,47 @@ +use super::{TaskHeader, TaskRef}; +use cordyceps::TransferStack; + + +/// Atomic task queue using a very, very simple lock-free linked-list queue: +/// +/// To enqueue a task, task.next is set to the old head, and head is atomically set to task. +/// +/// Dequeuing is done in batches: the queue is emptied by atomically replacing head with +/// null. Then the batch is iterated following the next pointers until null is reached. +/// +/// Note that batches will be iterated in the reverse order as they were enqueued. This is OK +/// for our purposes: it can't create fairness problems since the next batch won't run until the +/// current batch is completely processed, so even if a task enqueues itself instantly (for example +/// by waking its own waker) can't prevent other tasks from running. +pub(crate) struct RunQueue { + stack: TransferStack, +} + +impl RunQueue { + pub const fn new() -> Self { + Self { + stack: TransferStack::new(), + } + } + + /// Enqueues an item. Returns true if the queue was empty. + /// + /// # Safety + /// + /// `item` must NOT be already enqueued in any queue. + #[inline(always)] + pub(crate) unsafe fn enqueue(&self, task: TaskRef, _: super::state::Token) -> bool { + self.stack.push_was_empty(task) + } + + /// Empty the queue, then call `on_task` for each task that was in the queue. + /// NOTE: It is OK for `on_task` to enqueue more tasks. In this case they're left in the queue + /// and will be processed by the *next* call to `dequeue_all`, *not* the current one. + pub(crate) fn dequeue_all(&self, on_task: impl Fn(TaskRef)) { + let taken = self.stack.take_all(); + for taskref in taken { + taskref.header().state.run_dequeue(); + on_task(taskref); + } + } +} From 1f50e4d496458dbc7fccd9d028217ebfa7735471 Mon Sep 17 00:00:00 2001 From: James Munns Date: Thu, 20 Mar 2025 14:32:14 +0100 Subject: [PATCH 09/76] Implement Deadline Ranked Scheduling This implements a minimal version of Deadline Rank Scheduling, as well as ways to access and set Deadlines. This still needs some UX improvements, but is likely Enough for testing. --- embassy-executor/Cargo.toml | 3 +- embassy-executor/src/raw/mod.rs | 12 ++ .../src/raw/run_queue_drs_atomics.rs | 141 +++++++++++++++++- 3 files changed, 150 insertions(+), 6 deletions(-) diff --git a/embassy-executor/Cargo.toml b/embassy-executor/Cargo.toml index 1cd732dfa..db664a819 100644 --- a/embassy-executor/Cargo.toml +++ b/embassy-executor/Cargo.toml @@ -89,7 +89,6 @@ embassy-sync = { path = "../embassy-sync" } rustversion = "1.0.21" [features] - ## Enable nightly-only features nightly = ["embassy-executor-macros/nightly"] @@ -133,4 +132,4 @@ rtos-trace = ["_any_trace", "metadata-name", "dep:rtos-trace", "dep:embassy-time _any_trace = [] ## Enable "Deadline Rank Scheduler" -drs-scheduler = ["dep:cordyceps"] +drs-scheduler = ["dep:cordyceps", "dep:embassy-time-driver"] diff --git a/embassy-executor/src/raw/mod.rs b/embassy-executor/src/raw/mod.rs index 894a996ec..9b8a4ea8a 100644 --- a/embassy-executor/src/raw/mod.rs +++ b/embassy-executor/src/raw/mod.rs @@ -68,6 +68,9 @@ extern "Rust" fn __embassy_time_queue_item_from_waker(waker: &Waker) -> &'static #[cfg(feature = "drs-scheduler")] use cordyceps::{stack, Linked}; +#[cfg(feature = "drs-scheduler")] +pub use run_queue::Deadline; + /// Raw task header for use in task pointers. /// /// A task can be in one of the following states: @@ -124,6 +127,9 @@ pub(crate) struct TaskHeader { #[cfg(feature = "drs-scheduler")] pub(crate) links: stack::Links, + #[cfg(feature = "drs-scheduler")] + pub(crate) deadline: SyncUnsafeCell, + // TODO(AJM): We could potentially replace RunQueueItem for other runqueue impls, though // right now cordyceps doesn't work on non-atomic systems #[cfg(not(feature = "drs-scheduler"))] @@ -255,6 +261,8 @@ impl TaskStorage { run_queue_item: RunQueueItem::new(), #[cfg(feature = "drs-scheduler")] links: stack::Links::new(), + #[cfg(feature = "drs-scheduler")] + deadline: SyncUnsafeCell::new(0u64), state: State::new(), executor: AtomicPtr::new(core::ptr::null_mut()), // Note: this is lazily initialized so that a static `TaskStorage` will go in `.bss` @@ -352,6 +360,10 @@ impl AvailableTask { self.task.raw.poll_fn.set(Some(TaskStorage::::poll)); self.task.future.write_in_place(future); + // TODO(AJM): Some other way of setting this? Just a placeholder + #[cfg(feature = "drs-scheduler")] + self.task.raw.deadline.set(u64::MAX); + let task = TaskRef::new(self.task); SpawnToken::new(task) diff --git a/embassy-executor/src/raw/run_queue_drs_atomics.rs b/embassy-executor/src/raw/run_queue_drs_atomics.rs index 53ada1b14..69b7b3bf0 100644 --- a/embassy-executor/src/raw/run_queue_drs_atomics.rs +++ b/embassy-executor/src/raw/run_queue_drs_atomics.rs @@ -1,6 +1,7 @@ use super::{TaskHeader, TaskRef}; -use cordyceps::TransferStack; - +use cordyceps::{SortedList, TransferStack}; +use core::future::{Future, poll_fn}; +use core::task::Poll; /// Atomic task queue using a very, very simple lock-free linked-list queue: /// @@ -38,10 +39,142 @@ impl RunQueue { /// NOTE: It is OK for `on_task` to enqueue more tasks. In this case they're left in the queue /// and will be processed by the *next* call to `dequeue_all`, *not* the current one. pub(crate) fn dequeue_all(&self, on_task: impl Fn(TaskRef)) { - let taken = self.stack.take_all(); - for taskref in taken { + let mut sorted = SortedList::::new(|lhs, rhs| unsafe { + // TODO: Do we need any kind of access control here? Not if we say that + // tasks can only set their own priority, which they can't do if we're in + // the scheduler + lhs.deadline.get().cmp(&rhs.deadline.get()) + }); + + loop { + // For each loop, grab any newly pended items + let taken = self.stack.take_all(); + + // Sort these into the list - this is potentially expensive! We do an + // insertion sort of new items, which iterates the linked list. + // + // Something on the order of `O(n * m)`, where `n` is the number + // of new tasks, and `m` is the number of already pending tasks. + sorted.extend(taken); + + // Pop the task with the SOONEST deadline. If there are no tasks + // pending, then we are done. + let Some(taskref) = sorted.pop_front() else { + return; + }; + + // We got one task, mark it as dequeued, and process the task. taskref.header().state.run_dequeue(); on_task(taskref); } } } + +/// A type for interacting with the deadline of the current task +pub struct Deadline { + /// Deadline value in ticks, same time base and ticks as `embassy-time` + pub instant_ticks: u64, +} + +impl Deadline { + /// Set the current task's deadline at exactly `instant_ticks` + /// + /// This method is a future in order to access the currently executing task's + /// header which contains the deadline. + /// + /// Analogous to `Timer::at`. + /// + /// TODO: Should we check/panic if the deadline is in the past? + #[must_use = "Setting deadline must be polled to be effective"] + pub fn set_current_task_deadline(instant_ticks: u64) -> impl Future { + poll_fn(move |cx| { + let task = super::task_from_waker(cx.waker()); + // SAFETY: A task can only modify its own deadline, while the task is being + // polled, meaning that there cannot be concurrent access to the deadline. + unsafe { + task.header().deadline.set(instant_ticks); + } + Poll::Ready(()) + }) + } + + /// Set the current task's deadline `duration_ticks` in the future from when + /// this future is polled. + /// + /// This method is a future in order to access the currently executing task's + /// header which contains the deadline + /// + /// Analogous to `Timer::after` + /// + /// TODO: Do we want to return what the deadline is? + #[must_use = "Setting deadline must be polled to be effective"] + pub fn set_current_task_deadline_after(duration_ticks: u64) -> impl Future { + poll_fn(move |cx| { + let task = super::task_from_waker(cx.waker()); + let now = embassy_time_driver::now(); + + // Since ticks is a u64, saturating add is PROBABLY overly cautious, leave + // it for now, we can probably make this wrapping_add for performance + // reasons later. + let deadline = now.saturating_add(duration_ticks); + + // SAFETY: A task can only modify its own deadline, while the task is being + // polled, meaning that there cannot be concurrent access to the deadline. + unsafe { + task.header().deadline.set(deadline); + } + Poll::Ready(()) + }) + } + + /// Set the current task's deadline `increment_ticks` from the previous deadline. + /// + /// Note that by default (unless otherwise set), tasks start life with the deadline + /// u64::MAX, which means this method will have no effect. + /// + /// This method is a future in order to access the currently executing task's + /// header which contains the deadline + /// + /// Analogous to one increment of `Ticker::every().next()`. + /// + /// TODO: Do we want to return what the deadline is? + #[must_use = "Setting deadline must be polled to be effective"] + pub fn increment_current_task_deadline(increment_ticks: u64) -> impl Future { + poll_fn(move |cx| { + let task = super::task_from_waker(cx.waker()); + + // SAFETY: A task can only modify its own deadline, while the task is being + // polled, meaning that there cannot be concurrent access to the deadline. + unsafe { + // Get the last value + let last = task.header().deadline.get(); + + // Since ticks is a u64, saturating add is PROBABLY overly cautious, leave + // it for now, we can probably make this wrapping_add for performance + // reasons later. + let deadline = last.saturating_add(increment_ticks); + + // Store the new value + task.header().deadline.set(deadline); + } + Poll::Ready(()) + }) + } + + /// Get the current task's deadline as a tick value. + /// + /// This method is a future in order to access the currently executing task's + /// header which contains the deadline + pub fn get_current_task_deadline() -> impl Future { + poll_fn(move |cx| { + let task = super::task_from_waker(cx.waker()); + + // SAFETY: A task can only modify its own deadline, while the task is being + // polled, meaning that there cannot be concurrent access to the deadline. + let deadline = unsafe { + task.header().deadline.get() + }; + Poll::Ready(Self { instant_ticks: deadline }) + }) + } +} From 8c70aafd4be63ff7af895f116444fb81438ae6e0 Mon Sep 17 00:00:00 2001 From: James Munns Date: Tue, 1 Apr 2025 18:35:41 +0200 Subject: [PATCH 10/76] Make some things more consistent --- embassy-executor/Cargo.toml | 4 +- embassy-executor/src/raw/mod.rs | 57 ++----------------- .../src/raw/run_queue_drs_atomics.rs | 31 ++++++++-- 3 files changed, 36 insertions(+), 56 deletions(-) diff --git a/embassy-executor/Cargo.toml b/embassy-executor/Cargo.toml index db664a819..80b5867c9 100644 --- a/embassy-executor/Cargo.toml +++ b/embassy-executor/Cargo.toml @@ -77,9 +77,11 @@ js-sys = { version = "0.3", optional = true } avr-device = { version = "0.7.0", optional = true } [dependencies.cordyceps] +# note: targeting v0.3.3, to be released when +# https://github.com/hawkw/mycelium/pull/520 is merged version = "0.3" git = "https://github.com/hawkw/mycelium" -rev = "aaad19480d175bfc290f1d4dc2d435c6eb3d9fc5" +rev = "9649db0525b9972b95937d83d52d3f51cc486281" optional = true [dev-dependencies] diff --git a/embassy-executor/src/raw/mod.rs b/embassy-executor/src/raw/mod.rs index 9b8a4ea8a..2e5941ef7 100644 --- a/embassy-executor/src/raw/mod.rs +++ b/embassy-executor/src/raw/mod.rs @@ -40,7 +40,6 @@ use core::marker::PhantomData; use core::mem; use core::pin::Pin; use core::ptr::NonNull; -#[cfg(feature = "drs-scheduler")] use core::ptr::addr_of_mut; #[cfg(not(feature = "arch-avr"))] use core::sync::atomic::AtomicPtr; @@ -51,9 +50,7 @@ use embassy_executor_timer_queue::TimerQueueItem; #[cfg(feature = "arch-avr")] use portable_atomic::AtomicPtr; -use self::run_queue::RunQueue; -#[cfg(not(feature = "drs-scheduler"))] -use self::run_queue::RunQueueItem; +use self::run_queue::{RunQueue, RunQueueItem}; use self::state::State; use self::util::{SyncUnsafeCell, UninitCell}; pub use self::waker::task_from_waker; @@ -65,9 +62,6 @@ extern "Rust" fn __embassy_time_queue_item_from_waker(waker: &Waker) -> &'static unsafe { task_from_waker(waker).timer_queue_item() } } -#[cfg(feature = "drs-scheduler")] -use cordyceps::{stack, Linked}; - #[cfg(feature = "drs-scheduler")] pub use run_queue::Deadline; @@ -110,31 +104,14 @@ pub use run_queue::Deadline; /// - 4: A run-queued task exits - `TaskStorage::poll -> Poll::Ready` /// - 5: Task is dequeued. The task's future is not polled, because exiting the task replaces its `poll_fn`. /// - 6: A task is waken when it is not spawned - `wake_task -> State::run_enqueue` -#[cfg_attr(feature = "drs-scheduler", repr(C))] pub(crate) struct TaskHeader { - // TODO(AJM): Make a decision whether we want to support the spicier "pointer recast"/"type punning" - // method of implementing the `cordyceps::Linked` trait or not. - // - // Currently, I do the safer version with `addr_of_mut!`, which doesn't REQUIRE that the first - // element is the `links` field, at the potential cost of a little extra pointer math. - // - // The optimizer *might* (total guess) notice that we are always doing an offset of zero in the - // call to `addr_of_mut` in the `impl Linked for TaskHeader` below, and get the best of both worlds, - // but right now this is maybe a little over cautious. - // - // See https://docs.rs/cordyceps/latest/cordyceps/trait.Linked.html#implementing-linkedlinks for - // more context on the choices here. - #[cfg(feature = "drs-scheduler")] - pub(crate) links: stack::Links, - - #[cfg(feature = "drs-scheduler")] - pub(crate) deadline: SyncUnsafeCell, - - // TODO(AJM): We could potentially replace RunQueueItem for other runqueue impls, though - // right now cordyceps doesn't work on non-atomic systems - #[cfg(not(feature = "drs-scheduler"))] pub(crate) run_queue_item: RunQueueItem, + #[cfg(feature = "drs-scheduler")] + /// Deadline Rank Scheduler Deadline. This field should not be accessed outside the context of + /// the task itself as it being polled by the executor. + pub(crate) deadline: SyncUnsafeCell, + pub(crate) state: State, pub(crate) executor: AtomicPtr, poll_fn: SyncUnsafeCell>, @@ -148,25 +125,6 @@ pub(crate) struct TaskHeader { all_tasks_next: AtomicPtr, } -#[cfg(feature = "drs-scheduler")] -unsafe impl Linked> for TaskHeader { - type Handle = TaskRef; - - fn into_ptr(r: Self::Handle) -> NonNull { - r.ptr.cast() - } - - unsafe fn from_ptr(ptr: NonNull) -> Self::Handle { - let ptr: NonNull = ptr; - TaskRef { ptr } - } - - unsafe fn links(ptr: NonNull) -> NonNull> { - let ptr: *mut TaskHeader = ptr.as_ptr(); - NonNull::new_unchecked(addr_of_mut!((*ptr).links)) - } -} - /// This is essentially a `&'static TaskStorage` where the type of the future has been erased. #[derive(Debug, Clone, Copy, PartialEq)] pub struct TaskRef { @@ -257,11 +215,8 @@ impl TaskStorage { pub const fn new() -> Self { Self { raw: TaskHeader { - #[cfg(not(feature = "drs-scheduler"))] run_queue_item: RunQueueItem::new(), #[cfg(feature = "drs-scheduler")] - links: stack::Links::new(), - #[cfg(feature = "drs-scheduler")] deadline: SyncUnsafeCell::new(0u64), state: State::new(), executor: AtomicPtr::new(core::ptr::null_mut()), diff --git a/embassy-executor/src/raw/run_queue_drs_atomics.rs b/embassy-executor/src/raw/run_queue_drs_atomics.rs index 69b7b3bf0..047265954 100644 --- a/embassy-executor/src/raw/run_queue_drs_atomics.rs +++ b/embassy-executor/src/raw/run_queue_drs_atomics.rs @@ -2,6 +2,29 @@ use super::{TaskHeader, TaskRef}; use cordyceps::{SortedList, TransferStack}; use core::future::{Future, poll_fn}; use core::task::Poll; +use core::ptr::{addr_of_mut, NonNull}; +use cordyceps::sorted_list::Links; +use cordyceps::Linked; + +pub(crate) type RunQueueItem = Links; + +unsafe impl Linked> for super::TaskHeader { + type Handle = TaskRef; + + fn into_ptr(r: Self::Handle) -> NonNull { + r.ptr.cast() + } + + unsafe fn from_ptr(ptr: NonNull) -> Self::Handle { + let ptr: NonNull = ptr; + TaskRef { ptr } + } + + unsafe fn links(ptr: NonNull) -> NonNull> { + let ptr: *mut TaskHeader = ptr.as_ptr(); + NonNull::new_unchecked(addr_of_mut!((*ptr).run_queue_item)) + } +} /// Atomic task queue using a very, very simple lock-free linked-list queue: /// @@ -39,10 +62,10 @@ impl RunQueue { /// NOTE: It is OK for `on_task` to enqueue more tasks. In this case they're left in the queue /// and will be processed by the *next* call to `dequeue_all`, *not* the current one. pub(crate) fn dequeue_all(&self, on_task: impl Fn(TaskRef)) { - let mut sorted = SortedList::::new(|lhs, rhs| unsafe { - // TODO: Do we need any kind of access control here? Not if we say that - // tasks can only set their own priority, which they can't do if we're in - // the scheduler + // SAFETY: `deadline` can only be set through the `Deadline` interface, which + // only allows access to this value while the given task is being polled. + // This acts as mutual exclusion for access. + let mut sorted = SortedList::::new_custom(|lhs, rhs| unsafe { lhs.deadline.get().cmp(&rhs.deadline.get()) }); From ba0426f767bb602750bed4fae87a156b661c0e92 Mon Sep 17 00:00:00 2001 From: James Munns Date: Tue, 1 Apr 2025 18:50:12 +0200 Subject: [PATCH 11/76] Combine DRS and non-DRS atomic scheduler, using cordyceps --- .../{run_queue_drs_atomics.rs => deadline.rs} | 102 +--------------- embassy-executor/src/raw/mod.rs | 18 ++- embassy-executor/src/raw/run_queue_atomics.rs | 110 +++++++++++------- 3 files changed, 80 insertions(+), 150 deletions(-) rename embassy-executor/src/raw/{run_queue_drs_atomics.rs => deadline.rs} (53%) diff --git a/embassy-executor/src/raw/run_queue_drs_atomics.rs b/embassy-executor/src/raw/deadline.rs similarity index 53% rename from embassy-executor/src/raw/run_queue_drs_atomics.rs rename to embassy-executor/src/raw/deadline.rs index 047265954..3f60936cc 100644 --- a/embassy-executor/src/raw/run_queue_drs_atomics.rs +++ b/embassy-executor/src/raw/deadline.rs @@ -1,97 +1,5 @@ -use super::{TaskHeader, TaskRef}; -use cordyceps::{SortedList, TransferStack}; -use core::future::{Future, poll_fn}; +use core::future::{poll_fn, Future}; use core::task::Poll; -use core::ptr::{addr_of_mut, NonNull}; -use cordyceps::sorted_list::Links; -use cordyceps::Linked; - -pub(crate) type RunQueueItem = Links; - -unsafe impl Linked> for super::TaskHeader { - type Handle = TaskRef; - - fn into_ptr(r: Self::Handle) -> NonNull { - r.ptr.cast() - } - - unsafe fn from_ptr(ptr: NonNull) -> Self::Handle { - let ptr: NonNull = ptr; - TaskRef { ptr } - } - - unsafe fn links(ptr: NonNull) -> NonNull> { - let ptr: *mut TaskHeader = ptr.as_ptr(); - NonNull::new_unchecked(addr_of_mut!((*ptr).run_queue_item)) - } -} - -/// Atomic task queue using a very, very simple lock-free linked-list queue: -/// -/// To enqueue a task, task.next is set to the old head, and head is atomically set to task. -/// -/// Dequeuing is done in batches: the queue is emptied by atomically replacing head with -/// null. Then the batch is iterated following the next pointers until null is reached. -/// -/// Note that batches will be iterated in the reverse order as they were enqueued. This is OK -/// for our purposes: it can't create fairness problems since the next batch won't run until the -/// current batch is completely processed, so even if a task enqueues itself instantly (for example -/// by waking its own waker) can't prevent other tasks from running. -pub(crate) struct RunQueue { - stack: TransferStack, -} - -impl RunQueue { - pub const fn new() -> Self { - Self { - stack: TransferStack::new(), - } - } - - /// Enqueues an item. Returns true if the queue was empty. - /// - /// # Safety - /// - /// `item` must NOT be already enqueued in any queue. - #[inline(always)] - pub(crate) unsafe fn enqueue(&self, task: TaskRef, _: super::state::Token) -> bool { - self.stack.push_was_empty(task) - } - - /// Empty the queue, then call `on_task` for each task that was in the queue. - /// NOTE: It is OK for `on_task` to enqueue more tasks. In this case they're left in the queue - /// and will be processed by the *next* call to `dequeue_all`, *not* the current one. - pub(crate) fn dequeue_all(&self, on_task: impl Fn(TaskRef)) { - // SAFETY: `deadline` can only be set through the `Deadline` interface, which - // only allows access to this value while the given task is being polled. - // This acts as mutual exclusion for access. - let mut sorted = SortedList::::new_custom(|lhs, rhs| unsafe { - lhs.deadline.get().cmp(&rhs.deadline.get()) - }); - - loop { - // For each loop, grab any newly pended items - let taken = self.stack.take_all(); - - // Sort these into the list - this is potentially expensive! We do an - // insertion sort of new items, which iterates the linked list. - // - // Something on the order of `O(n * m)`, where `n` is the number - // of new tasks, and `m` is the number of already pending tasks. - sorted.extend(taken); - - // Pop the task with the SOONEST deadline. If there are no tasks - // pending, then we are done. - let Some(taskref) = sorted.pop_front() else { - return; - }; - - // We got one task, mark it as dequeued, and process the task. - taskref.header().state.run_dequeue(); - on_task(taskref); - } - } -} /// A type for interacting with the deadline of the current task pub struct Deadline { @@ -194,10 +102,10 @@ impl Deadline { // SAFETY: A task can only modify its own deadline, while the task is being // polled, meaning that there cannot be concurrent access to the deadline. - let deadline = unsafe { - task.header().deadline.get() - }; - Poll::Ready(Self { instant_ticks: deadline }) + let deadline = unsafe { task.header().deadline.get() }; + Poll::Ready(Self { + instant_ticks: deadline, + }) }) } } diff --git a/embassy-executor/src/raw/mod.rs b/embassy-executor/src/raw/mod.rs index 2e5941ef7..0dd247d30 100644 --- a/embassy-executor/src/raw/mod.rs +++ b/embassy-executor/src/raw/mod.rs @@ -7,14 +7,7 @@ //! Using this module requires respecting subtle safety contracts. If you can, prefer using the safe //! [executor wrappers](crate::Executor) and the [`embassy_executor::task`](embassy_executor_macros::task) macro, which are fully safe. -#[cfg_attr( - all(not(feature = "drs-scheduler"), target_has_atomic = "ptr"), - path = "run_queue_atomics.rs", -)] -#[cfg_attr( - all(feature = "drs-scheduler", target_has_atomic = "ptr"), - path = "run_queue_drs_atomics.rs", -)] +#[cfg_attr(target_has_atomic = "ptr", path = "run_queue_atomics.rs")] #[cfg_attr(not(target_has_atomic = "ptr"), path = "run_queue_critical_section.rs")] mod run_queue; @@ -35,6 +28,9 @@ pub(crate) mod util; #[cfg_attr(feature = "turbowakers", path = "waker_turbo.rs")] mod waker; +#[cfg(feature = "drs-scheduler")] +mod deadline; + use core::future::Future; use core::marker::PhantomData; use core::mem; @@ -50,6 +46,9 @@ use embassy_executor_timer_queue::TimerQueueItem; #[cfg(feature = "arch-avr")] use portable_atomic::AtomicPtr; +#[cfg(feature = "drs-scheduler")] +pub use deadline::Deadline; + use self::run_queue::{RunQueue, RunQueueItem}; use self::state::State; use self::util::{SyncUnsafeCell, UninitCell}; @@ -62,9 +61,6 @@ extern "Rust" fn __embassy_time_queue_item_from_waker(waker: &Waker) -> &'static unsafe { task_from_waker(waker).timer_queue_item() } } -#[cfg(feature = "drs-scheduler")] -pub use run_queue::Deadline; - /// Raw task header for use in task pointers. /// /// A task can be in one of the following states: diff --git a/embassy-executor/src/raw/run_queue_atomics.rs b/embassy-executor/src/raw/run_queue_atomics.rs index ce511d79a..bc5d38250 100644 --- a/embassy-executor/src/raw/run_queue_atomics.rs +++ b/embassy-executor/src/raw/run_queue_atomics.rs @@ -1,19 +1,36 @@ -use core::ptr; -use core::ptr::NonNull; -use core::sync::atomic::{AtomicPtr, Ordering}; +use core::ptr::{addr_of_mut, NonNull}; + +use cordyceps::sorted_list::Links; +use cordyceps::{Linked, SortedList, TransferStack}; use super::{TaskHeader, TaskRef}; -use crate::raw::util::SyncUnsafeCell; -pub(crate) struct RunQueueItem { - next: SyncUnsafeCell>, -} +/// Use `cordyceps::sorted_list::Links` as the singly linked list +/// for RunQueueItems. +pub(crate) type RunQueueItem = Links; -impl RunQueueItem { - pub const fn new() -> Self { - Self { - next: SyncUnsafeCell::new(None), - } +/// Implements the `Linked` trait, allowing for singly linked list usage +/// of any of cordyceps' `TransferStack` (used for the atomic runqueue), +/// `SortedList` (used with the DRS scheduler), or `Stack`, which is +/// popped atomically from the `TransferStack`. +unsafe impl Linked> for TaskHeader { + type Handle = TaskRef; + + // Convert a TaskRef into a TaskHeader ptr + fn into_ptr(r: TaskRef) -> NonNull { + r.ptr + } + + // Convert a TaskHeader into a TaskRef + unsafe fn from_ptr(ptr: NonNull) -> TaskRef { + TaskRef { ptr } + } + + // Given a pointer to a TaskHeader, obtain a pointer to the Links structure, + // which can be used to traverse to other TaskHeader nodes in the linked list + unsafe fn links(ptr: NonNull) -> NonNull> { + let ptr: *mut TaskHeader = ptr.as_ptr(); + NonNull::new_unchecked(addr_of_mut!((*ptr).run_queue_item)) } } @@ -29,13 +46,13 @@ impl RunQueueItem { /// current batch is completely processed, so even if a task enqueues itself instantly (for example /// by waking its own waker) can't prevent other tasks from running. pub(crate) struct RunQueue { - head: AtomicPtr, + stack: TransferStack, } impl RunQueue { pub const fn new() -> Self { Self { - head: AtomicPtr::new(ptr::null_mut()), + stack: TransferStack::new(), } } @@ -46,43 +63,52 @@ impl RunQueue { /// `item` must NOT be already enqueued in any queue. #[inline(always)] pub(crate) unsafe fn enqueue(&self, task: TaskRef, _: super::state::Token) -> bool { - let mut was_empty = false; - - self.head - .fetch_update(Ordering::SeqCst, Ordering::SeqCst, |prev| { - was_empty = prev.is_null(); - unsafe { - // safety: the pointer is either null or valid - let prev = NonNull::new(prev).map(|ptr| TaskRef::from_ptr(ptr.as_ptr())); - // safety: there are no concurrent accesses to `next` - task.header().run_queue_item.next.set(prev); - } - Some(task.as_ptr() as *mut _) - }) - .ok(); - - was_empty + self.stack.push_was_empty(task) } /// Empty the queue, then call `on_task` for each task that was in the queue. /// NOTE: It is OK for `on_task` to enqueue more tasks. In this case they're left in the queue /// and will be processed by the *next* call to `dequeue_all`, *not* the current one. + #[cfg(not(feature = "drs-scheduler"))] pub(crate) fn dequeue_all(&self, on_task: impl Fn(TaskRef)) { - // Atomically empty the queue. - let ptr = self.head.swap(ptr::null_mut(), Ordering::AcqRel); + let taken = self.stack.take_all(); + for taskref in taken { + taskref.header().state.run_dequeue(); + on_task(taskref); + } + } - // safety: the pointer is either null or valid - let mut next = unsafe { NonNull::new(ptr).map(|ptr| TaskRef::from_ptr(ptr.as_ptr())) }; + /// Empty the queue, then call `on_task` for each task that was in the queue. + /// NOTE: It is OK for `on_task` to enqueue more tasks. In this case they're left in the queue + /// and will be processed by the *next* call to `dequeue_all`, *not* the current one. + #[cfg(feature = "drs-scheduler")] + pub(crate) fn dequeue_all(&self, on_task: impl Fn(TaskRef)) { + // SAFETY: `deadline` can only be set through the `Deadline` interface, which + // only allows access to this value while the given task is being polled. + // This acts as mutual exclusion for access. + let mut sorted = + SortedList::::new_custom(|lhs, rhs| unsafe { lhs.deadline.get().cmp(&rhs.deadline.get()) }); - // Iterate the linked list of tasks that were previously in the queue. - while let Some(task) = next { - // If the task re-enqueues itself, the `next` pointer will get overwritten. - // Therefore, first read the next pointer, and only then process the task. - // safety: there are no concurrent accesses to `next` - next = unsafe { task.header().run_queue_item.next.get() }; + loop { + // For each loop, grab any newly pended items + let taken = self.stack.take_all(); - task.header().state.run_dequeue(); - on_task(task); + // Sort these into the list - this is potentially expensive! We do an + // insertion sort of new items, which iterates the linked list. + // + // Something on the order of `O(n * m)`, where `n` is the number + // of new tasks, and `m` is the number of already pending tasks. + sorted.extend(taken); + + // Pop the task with the SOONEST deadline. If there are no tasks + // pending, then we are done. + let Some(taskref) = sorted.pop_front() else { + return; + }; + + // We got one task, mark it as dequeued, and process the task. + taskref.header().state.run_dequeue(); + on_task(taskref); } } } From ed2e51bfa4f92b422233343a0c5b1af98fb36537 Mon Sep 17 00:00:00 2001 From: James Munns Date: Tue, 1 Apr 2025 19:32:12 +0200 Subject: [PATCH 12/76] Dependency enablement trickery --- embassy-executor/Cargo.toml | 18 ++++++++++++------ embassy-executor/src/raw/deadline.rs | 2 ++ embassy-executor/src/raw/mod.rs | 11 +++++++---- embassy-executor/src/raw/run_queue_atomics.rs | 19 ++++++++++++++++--- 4 files changed, 37 insertions(+), 13 deletions(-) diff --git a/embassy-executor/Cargo.toml b/embassy-executor/Cargo.toml index 80b5867c9..06e12ae7e 100644 --- a/embassy-executor/Cargo.toml +++ b/embassy-executor/Cargo.toml @@ -46,7 +46,7 @@ flavors = [ [package.metadata.docs.rs] default-target = "thumbv7em-none-eabi" targets = ["thumbv7em-none-eabi"] -features = ["defmt", "arch-cortex-m", "executor-thread", "executor-interrupt"] +features = ["defmt", "arch-cortex-m", "executor-thread", "executor-interrupt", "drs-scheduler"] [dependencies] defmt = { version = "1.0.1", optional = true } @@ -76,13 +76,17 @@ js-sys = { version = "0.3", optional = true } # arch-avr dependencies avr-device = { version = "0.7.0", optional = true } -[dependencies.cordyceps] -# note: targeting v0.3.3, to be released when +# Note: this is ONLY a dependency when the target has atomics, this is +# used for `run_queue_atomics`. We need to be conditional because +# cordyceps *requires* the use of atomics, so we pull it in when +# `run_queue_atomics` would be enabled, and NOT when `run_queue_critical_section` +# would be enabled. +[target.'cfg(target_has_atomic="ptr")'.dependencies.cordyceps] +# TODO: targeting v0.3.3, to be released when # https://github.com/hawkw/mycelium/pull/520 is merged version = "0.3" git = "https://github.com/hawkw/mycelium" rev = "9649db0525b9972b95937d83d52d3f51cc486281" -optional = true [dev-dependencies] critical-section = { version = "1.1", features = ["std"] } @@ -133,5 +137,7 @@ trace = ["_any_trace"] rtos-trace = ["_any_trace", "metadata-name", "dep:rtos-trace", "dep:embassy-time-driver"] _any_trace = [] -## Enable "Deadline Rank Scheduler" -drs-scheduler = ["dep:cordyceps", "dep:embassy-time-driver"] +## Enable "Deadline Rank Sorted" Scheduler, using soft-realtime "deadlines" to prioritize +## tasks based on the remaining time before their deadline. Adds some overhead. Requires +## hardware atomic support +drs-scheduler = ["dep:embassy-time-driver"] diff --git a/embassy-executor/src/raw/deadline.rs b/embassy-executor/src/raw/deadline.rs index 3f60936cc..c8cc94c52 100644 --- a/embassy-executor/src/raw/deadline.rs +++ b/embassy-executor/src/raw/deadline.rs @@ -2,6 +2,8 @@ use core::future::{poll_fn, Future}; use core::task::Poll; /// A type for interacting with the deadline of the current task +/// +/// Requires the `drs-scheduler` feature pub struct Deadline { /// Deadline value in ticks, same time base and ticks as `embassy-time` pub instant_ticks: u64, diff --git a/embassy-executor/src/raw/mod.rs b/embassy-executor/src/raw/mod.rs index 0dd247d30..f4fbe1bfc 100644 --- a/embassy-executor/src/raw/mod.rs +++ b/embassy-executor/src/raw/mod.rs @@ -101,14 +101,14 @@ extern "Rust" fn __embassy_time_queue_item_from_waker(waker: &Waker) -> &'static /// - 5: Task is dequeued. The task's future is not polled, because exiting the task replaces its `poll_fn`. /// - 6: A task is waken when it is not spawned - `wake_task -> State::run_enqueue` pub(crate) struct TaskHeader { + pub(crate) state: State, pub(crate) run_queue_item: RunQueueItem, - #[cfg(feature = "drs-scheduler")] /// Deadline Rank Scheduler Deadline. This field should not be accessed outside the context of /// the task itself as it being polled by the executor. + #[cfg(feature = "drs-scheduler")] pub(crate) deadline: SyncUnsafeCell, - pub(crate) state: State, pub(crate) executor: AtomicPtr, poll_fn: SyncUnsafeCell>, @@ -211,10 +211,12 @@ impl TaskStorage { pub const fn new() -> Self { Self { raw: TaskHeader { + state: State::new(), run_queue_item: RunQueueItem::new(), + // NOTE: The deadline is set to zero to allow the initializer to reside in `.bss`. This + // will be lazily initalized in `initialize_impl` #[cfg(feature = "drs-scheduler")] deadline: SyncUnsafeCell::new(0u64), - state: State::new(), executor: AtomicPtr::new(core::ptr::null_mut()), // Note: this is lazily initialized so that a static `TaskStorage` will go in `.bss` poll_fn: SyncUnsafeCell::new(None), @@ -311,7 +313,8 @@ impl AvailableTask { self.task.raw.poll_fn.set(Some(TaskStorage::::poll)); self.task.future.write_in_place(future); - // TODO(AJM): Some other way of setting this? Just a placeholder + // By default, deadlines are set to the maximum value, so that any task WITH + // a set deadline will ALWAYS be scheduled BEFORE a task WITHOUT a set deadline #[cfg(feature = "drs-scheduler")] self.task.raw.deadline.set(u64::MAX); diff --git a/embassy-executor/src/raw/run_queue_atomics.rs b/embassy-executor/src/raw/run_queue_atomics.rs index bc5d38250..3715fc658 100644 --- a/embassy-executor/src/raw/run_queue_atomics.rs +++ b/embassy-executor/src/raw/run_queue_atomics.rs @@ -66,6 +66,8 @@ impl RunQueue { self.stack.push_was_empty(task) } + /// # Standard atomic runqueue + /// /// Empty the queue, then call `on_task` for each task that was in the queue. /// NOTE: It is OK for `on_task` to enqueue more tasks. In this case they're left in the queue /// and will be processed by the *next* call to `dequeue_all`, *not* the current one. @@ -78,9 +80,20 @@ impl RunQueue { } } - /// Empty the queue, then call `on_task` for each task that was in the queue. - /// NOTE: It is OK for `on_task` to enqueue more tasks. In this case they're left in the queue - /// and will be processed by the *next* call to `dequeue_all`, *not* the current one. + /// # Deadline Ranked Sorted Scheduler + /// + /// This algorithm will loop until all enqueued tasks are processed. + /// + /// Before polling a task, all currently enqueued tasks will be popped from the + /// runqueue, and will be added to the working `sorted` list, a linked-list that + /// sorts tasks by their deadline, with nearest deadline items in the front, and + /// furthest deadline items in the back. + /// + /// After popping and sorting all pending tasks, the SOONEST task will be popped + /// from the front of the queue, and polled by calling `on_task` on it. + /// + /// This process will repeat until the local `sorted` queue AND the global + /// runqueue are both empty, at which point this function will return. #[cfg(feature = "drs-scheduler")] pub(crate) fn dequeue_all(&self, on_task: impl Fn(TaskRef)) { // SAFETY: `deadline` can only be set through the `Deadline` interface, which From 2a068c528383b3ddc1213b9a5da5445498962bd2 Mon Sep 17 00:00:00 2001 From: James Munns Date: Tue, 1 Apr 2025 19:41:19 +0200 Subject: [PATCH 13/76] Conditional import --- embassy-executor/src/raw/run_queue_atomics.rs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/embassy-executor/src/raw/run_queue_atomics.rs b/embassy-executor/src/raw/run_queue_atomics.rs index 3715fc658..a63f0d116 100644 --- a/embassy-executor/src/raw/run_queue_atomics.rs +++ b/embassy-executor/src/raw/run_queue_atomics.rs @@ -1,7 +1,9 @@ use core::ptr::{addr_of_mut, NonNull}; use cordyceps::sorted_list::Links; -use cordyceps::{Linked, SortedList, TransferStack}; +#[cfg(feature = "drs-scheduler")] +use cordyceps::SortedList; +use cordyceps::{Linked, TransferStack}; use super::{TaskHeader, TaskRef}; From 08a57b1cb0c3c4a40bd03e6e6ea1c97777300cf4 Mon Sep 17 00:00:00 2001 From: James Munns Date: Wed, 2 Apr 2025 23:30:40 +0200 Subject: [PATCH 14/76] Update with changes from the PR --- embassy-executor/Cargo.toml | 5 ++--- embassy-executor/src/raw/run_queue_atomics.rs | 2 +- 2 files changed, 3 insertions(+), 4 deletions(-) diff --git a/embassy-executor/Cargo.toml b/embassy-executor/Cargo.toml index 06e12ae7e..ab95b2939 100644 --- a/embassy-executor/Cargo.toml +++ b/embassy-executor/Cargo.toml @@ -82,11 +82,10 @@ avr-device = { version = "0.7.0", optional = true } # `run_queue_atomics` would be enabled, and NOT when `run_queue_critical_section` # would be enabled. [target.'cfg(target_has_atomic="ptr")'.dependencies.cordyceps] -# TODO: targeting v0.3.3, to be released when -# https://github.com/hawkw/mycelium/pull/520 is merged +# TODO: targeting v0.3.3, to be released soon version = "0.3" git = "https://github.com/hawkw/mycelium" -rev = "9649db0525b9972b95937d83d52d3f51cc486281" +rev = "1dad987b483078b248ac3e2e7a75f1ff2b463cc4" [dev-dependencies] critical-section = { version = "1.1", features = ["std"] } diff --git a/embassy-executor/src/raw/run_queue_atomics.rs b/embassy-executor/src/raw/run_queue_atomics.rs index a63f0d116..08765e06b 100644 --- a/embassy-executor/src/raw/run_queue_atomics.rs +++ b/embassy-executor/src/raw/run_queue_atomics.rs @@ -102,7 +102,7 @@ impl RunQueue { // only allows access to this value while the given task is being polled. // This acts as mutual exclusion for access. let mut sorted = - SortedList::::new_custom(|lhs, rhs| unsafe { lhs.deadline.get().cmp(&rhs.deadline.get()) }); + SortedList::::new_with_cmp(|lhs, rhs| unsafe { lhs.deadline.get().cmp(&rhs.deadline.get()) }); loop { // For each loop, grab any newly pended items From b65a3a301a29c737f336ca344f671d4e9793fda8 Mon Sep 17 00:00:00 2001 From: James Munns Date: Thu, 3 Apr 2025 10:27:26 +0200 Subject: [PATCH 15/76] Clean up some TODOs --- embassy-executor/src/raw/deadline.rs | 62 +++++++++++++++++++++++----- embassy-executor/src/raw/mod.rs | 2 +- 2 files changed, 53 insertions(+), 11 deletions(-) diff --git a/embassy-executor/src/raw/deadline.rs b/embassy-executor/src/raw/deadline.rs index c8cc94c52..0b88ee2d6 100644 --- a/embassy-executor/src/raw/deadline.rs +++ b/embassy-executor/src/raw/deadline.rs @@ -10,6 +10,16 @@ pub struct Deadline { } impl Deadline { + /// Sentinel value representing an "unset" deadline, which has lower priority + /// than any other set deadline value + pub const UNSET_DEADLINE_TICKS: u64 = u64::MAX; + + /// Does the given Deadline represent an "unset" deadline? + #[inline] + pub fn is_unset(&self) -> bool { + self.instant_ticks == Self::UNSET_DEADLINE_TICKS + } + /// Set the current task's deadline at exactly `instant_ticks` /// /// This method is a future in order to access the currently executing task's @@ -17,7 +27,7 @@ impl Deadline { /// /// Analogous to `Timer::at`. /// - /// TODO: Should we check/panic if the deadline is in the past? + /// This method does NOT check whether the deadline has already passed. #[must_use = "Setting deadline must be polled to be effective"] pub fn set_current_task_deadline(instant_ticks: u64) -> impl Future { poll_fn(move |cx| { @@ -32,16 +42,16 @@ impl Deadline { } /// Set the current task's deadline `duration_ticks` in the future from when - /// this future is polled. + /// this future is polled. This deadline is saturated to the max tick value. /// /// This method is a future in order to access the currently executing task's - /// header which contains the deadline + /// header which contains the deadline. /// - /// Analogous to `Timer::after` + /// Analogous to `Timer::after`. /// - /// TODO: Do we want to return what the deadline is? + /// Returns the deadline that was set. #[must_use = "Setting deadline must be polled to be effective"] - pub fn set_current_task_deadline_after(duration_ticks: u64) -> impl Future { + pub fn set_current_task_deadline_after(duration_ticks: u64) -> impl Future { poll_fn(move |cx| { let task = super::task_from_waker(cx.waker()); let now = embassy_time_driver::now(); @@ -56,12 +66,16 @@ impl Deadline { unsafe { task.header().deadline.set(deadline); } - Poll::Ready(()) + Poll::Ready(Deadline { + instant_ticks: deadline, + }) }) } /// Set the current task's deadline `increment_ticks` from the previous deadline. /// + /// This deadline is saturated to the max tick value. + /// /// Note that by default (unless otherwise set), tasks start life with the deadline /// u64::MAX, which means this method will have no effect. /// @@ -70,9 +84,9 @@ impl Deadline { /// /// Analogous to one increment of `Ticker::every().next()`. /// - /// TODO: Do we want to return what the deadline is? + /// Returns the deadline that was set. #[must_use = "Setting deadline must be polled to be effective"] - pub fn increment_current_task_deadline(increment_ticks: u64) -> impl Future { + pub fn increment_current_task_deadline(increment_ticks: u64) -> impl Future { poll_fn(move |cx| { let task = super::task_from_waker(cx.waker()); @@ -89,8 +103,11 @@ impl Deadline { // Store the new value task.header().deadline.set(deadline); + + Poll::Ready(Deadline { + instant_ticks: deadline, + }) } - Poll::Ready(()) }) } @@ -110,4 +127,29 @@ impl Deadline { }) }) } + + /// Clear the current task's deadline, returning the previous value. + /// + /// This sets the deadline to the default value of `u64::MAX`, meaning all + /// tasks with set deadlines will be scheduled BEFORE this task. + pub fn clear_current_task_deadline() -> impl Future { + poll_fn(move |cx| { + let task = super::task_from_waker(cx.waker()); + + // SAFETY: A task can only modify its own deadline, while the task is being + // polled, meaning that there cannot be concurrent access to the deadline. + let deadline = unsafe { + // get the old value + let d = task.header().deadline.get(); + // Store the default value + task.header().deadline.set(Self::UNSET_DEADLINE_TICKS); + // return the old value + d + }; + + Poll::Ready(Self { + instant_ticks: deadline, + }) + }) + } } diff --git a/embassy-executor/src/raw/mod.rs b/embassy-executor/src/raw/mod.rs index f4fbe1bfc..a0890a864 100644 --- a/embassy-executor/src/raw/mod.rs +++ b/embassy-executor/src/raw/mod.rs @@ -316,7 +316,7 @@ impl AvailableTask { // By default, deadlines are set to the maximum value, so that any task WITH // a set deadline will ALWAYS be scheduled BEFORE a task WITHOUT a set deadline #[cfg(feature = "drs-scheduler")] - self.task.raw.deadline.set(u64::MAX); + self.task.raw.deadline.set(deadline::Deadline::UNSET_DEADLINE_TICKS); let task = TaskRef::new(self.task); From b1b2955b604f558d8bd2fcca07b8fd8da3e236fa Mon Sep 17 00:00:00 2001 From: James Munns Date: Tue, 8 Apr 2025 10:05:55 +0200 Subject: [PATCH 16/76] Switch to released version of `cordyceps`, add error if used w/o atomics --- embassy-executor/Cargo.toml | 5 +---- embassy-executor/src/raw/deadline.rs | 3 +++ 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/embassy-executor/Cargo.toml b/embassy-executor/Cargo.toml index ab95b2939..e740f9ccf 100644 --- a/embassy-executor/Cargo.toml +++ b/embassy-executor/Cargo.toml @@ -82,10 +82,7 @@ avr-device = { version = "0.7.0", optional = true } # `run_queue_atomics` would be enabled, and NOT when `run_queue_critical_section` # would be enabled. [target.'cfg(target_has_atomic="ptr")'.dependencies.cordyceps] -# TODO: targeting v0.3.3, to be released soon -version = "0.3" -git = "https://github.com/hawkw/mycelium" -rev = "1dad987b483078b248ac3e2e7a75f1ff2b463cc4" +version = "0.3.3" [dev-dependencies] critical-section = { version = "1.1", features = ["std"] } diff --git a/embassy-executor/src/raw/deadline.rs b/embassy-executor/src/raw/deadline.rs index 0b88ee2d6..da07d1aac 100644 --- a/embassy-executor/src/raw/deadline.rs +++ b/embassy-executor/src/raw/deadline.rs @@ -1,6 +1,9 @@ use core::future::{poll_fn, Future}; use core::task::Poll; +#[cfg(not(target_has_atomic = "ptr"))] +compile_error!("The `drs-scheduler` feature is currently only supported on targets with atomics."); + /// A type for interacting with the deadline of the current task /// /// Requires the `drs-scheduler` feature From 3929142f4c08028ea1982e79fd912e1a44900892 Mon Sep 17 00:00:00 2001 From: James Munns Date: Tue, 8 Apr 2025 10:11:07 +0200 Subject: [PATCH 17/76] One more must_use --- embassy-executor/src/raw/deadline.rs | 1 + embassy-executor/src/raw/mod.rs | 3 ++- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/embassy-executor/src/raw/deadline.rs b/embassy-executor/src/raw/deadline.rs index da07d1aac..ae6394822 100644 --- a/embassy-executor/src/raw/deadline.rs +++ b/embassy-executor/src/raw/deadline.rs @@ -135,6 +135,7 @@ impl Deadline { /// /// This sets the deadline to the default value of `u64::MAX`, meaning all /// tasks with set deadlines will be scheduled BEFORE this task. + #[must_use = "Clearing deadline must be polled to be effective"] pub fn clear_current_task_deadline() -> impl Future { poll_fn(move |cx| { let task = super::task_from_waker(cx.waker()); diff --git a/embassy-executor/src/raw/mod.rs b/embassy-executor/src/raw/mod.rs index a0890a864..21dc67b7e 100644 --- a/embassy-executor/src/raw/mod.rs +++ b/embassy-executor/src/raw/mod.rs @@ -36,7 +36,6 @@ use core::marker::PhantomData; use core::mem; use core::pin::Pin; use core::ptr::NonNull; -use core::ptr::addr_of_mut; #[cfg(not(feature = "arch-avr"))] use core::sync::atomic::AtomicPtr; use core::sync::atomic::Ordering; @@ -48,6 +47,8 @@ use portable_atomic::AtomicPtr; #[cfg(feature = "drs-scheduler")] pub use deadline::Deadline; +#[cfg(feature = "arch-avr")] +use portable_atomic::AtomicPtr; use self::run_queue::{RunQueue, RunQueueItem}; use self::state::State; From 0e28ba1091257111f71b76a664d7038dbfcf9b5e Mon Sep 17 00:00:00 2001 From: James Munns Date: Wed, 16 Apr 2025 15:59:28 +0200 Subject: [PATCH 18/76] "Deadline Rank Sorted Scheduler" -> "Earliest Deadline First Scheduler" --- embassy-executor/Cargo.toml | 7 ++++--- embassy-executor/src/raw/deadline.rs | 4 ++-- embassy-executor/src/raw/mod.rs | 14 +++++++------- embassy-executor/src/raw/run_queue_atomics.rs | 8 ++++---- 4 files changed, 17 insertions(+), 16 deletions(-) diff --git a/embassy-executor/Cargo.toml b/embassy-executor/Cargo.toml index e740f9ccf..17315eaa3 100644 --- a/embassy-executor/Cargo.toml +++ b/embassy-executor/Cargo.toml @@ -46,7 +46,7 @@ flavors = [ [package.metadata.docs.rs] default-target = "thumbv7em-none-eabi" targets = ["thumbv7em-none-eabi"] -features = ["defmt", "arch-cortex-m", "executor-thread", "executor-interrupt", "drs-scheduler"] +features = ["defmt", "arch-cortex-m", "executor-thread", "executor-interrupt", "edf-scheduler"] [dependencies] defmt = { version = "1.0.1", optional = true } @@ -91,6 +91,7 @@ embassy-sync = { path = "../embassy-sync" } rustversion = "1.0.21" [features] + ## Enable nightly-only features nightly = ["embassy-executor-macros/nightly"] @@ -133,7 +134,7 @@ trace = ["_any_trace"] rtos-trace = ["_any_trace", "metadata-name", "dep:rtos-trace", "dep:embassy-time-driver"] _any_trace = [] -## Enable "Deadline Rank Sorted" Scheduler, using soft-realtime "deadlines" to prioritize +## Enable "Earliest Deadline First" Scheduler, using soft-realtime "deadlines" to prioritize ## tasks based on the remaining time before their deadline. Adds some overhead. Requires ## hardware atomic support -drs-scheduler = ["dep:embassy-time-driver"] +edf-scheduler = ["dep:embassy-time-driver"] diff --git a/embassy-executor/src/raw/deadline.rs b/embassy-executor/src/raw/deadline.rs index ae6394822..006c7caf1 100644 --- a/embassy-executor/src/raw/deadline.rs +++ b/embassy-executor/src/raw/deadline.rs @@ -2,11 +2,11 @@ use core::future::{poll_fn, Future}; use core::task::Poll; #[cfg(not(target_has_atomic = "ptr"))] -compile_error!("The `drs-scheduler` feature is currently only supported on targets with atomics."); +compile_error!("The `edf-scheduler` feature is currently only supported on targets with atomics."); /// A type for interacting with the deadline of the current task /// -/// Requires the `drs-scheduler` feature +/// Requires the `edf-scheduler` feature pub struct Deadline { /// Deadline value in ticks, same time base and ticks as `embassy-time` pub instant_ticks: u64, diff --git a/embassy-executor/src/raw/mod.rs b/embassy-executor/src/raw/mod.rs index 21dc67b7e..96e7fda74 100644 --- a/embassy-executor/src/raw/mod.rs +++ b/embassy-executor/src/raw/mod.rs @@ -28,7 +28,7 @@ pub(crate) mod util; #[cfg_attr(feature = "turbowakers", path = "waker_turbo.rs")] mod waker; -#[cfg(feature = "drs-scheduler")] +#[cfg(feature = "edf-scheduler")] mod deadline; use core::future::Future; @@ -45,7 +45,7 @@ use embassy_executor_timer_queue::TimerQueueItem; #[cfg(feature = "arch-avr")] use portable_atomic::AtomicPtr; -#[cfg(feature = "drs-scheduler")] +#[cfg(feature = "edf-scheduler")] pub use deadline::Deadline; #[cfg(feature = "arch-avr")] use portable_atomic::AtomicPtr; @@ -105,9 +105,9 @@ pub(crate) struct TaskHeader { pub(crate) state: State, pub(crate) run_queue_item: RunQueueItem, - /// Deadline Rank Scheduler Deadline. This field should not be accessed outside the context of - /// the task itself as it being polled by the executor. - #[cfg(feature = "drs-scheduler")] + /// Earliest Deadline First scheduler Deadline. This field should not be accessed + /// outside the context of the task itself as it being polled by the executor. + #[cfg(feature = "edf-scheduler")] pub(crate) deadline: SyncUnsafeCell, pub(crate) executor: AtomicPtr, @@ -216,7 +216,7 @@ impl TaskStorage { run_queue_item: RunQueueItem::new(), // NOTE: The deadline is set to zero to allow the initializer to reside in `.bss`. This // will be lazily initalized in `initialize_impl` - #[cfg(feature = "drs-scheduler")] + #[cfg(feature = "edf-scheduler")] deadline: SyncUnsafeCell::new(0u64), executor: AtomicPtr::new(core::ptr::null_mut()), // Note: this is lazily initialized so that a static `TaskStorage` will go in `.bss` @@ -316,7 +316,7 @@ impl AvailableTask { // By default, deadlines are set to the maximum value, so that any task WITH // a set deadline will ALWAYS be scheduled BEFORE a task WITHOUT a set deadline - #[cfg(feature = "drs-scheduler")] + #[cfg(feature = "edf-scheduler")] self.task.raw.deadline.set(deadline::Deadline::UNSET_DEADLINE_TICKS); let task = TaskRef::new(self.task); diff --git a/embassy-executor/src/raw/run_queue_atomics.rs b/embassy-executor/src/raw/run_queue_atomics.rs index 08765e06b..65a9b7859 100644 --- a/embassy-executor/src/raw/run_queue_atomics.rs +++ b/embassy-executor/src/raw/run_queue_atomics.rs @@ -1,7 +1,7 @@ use core::ptr::{addr_of_mut, NonNull}; use cordyceps::sorted_list::Links; -#[cfg(feature = "drs-scheduler")] +#[cfg(feature = "edf-scheduler")] use cordyceps::SortedList; use cordyceps::{Linked, TransferStack}; @@ -73,7 +73,7 @@ impl RunQueue { /// Empty the queue, then call `on_task` for each task that was in the queue. /// NOTE: It is OK for `on_task` to enqueue more tasks. In this case they're left in the queue /// and will be processed by the *next* call to `dequeue_all`, *not* the current one. - #[cfg(not(feature = "drs-scheduler"))] + #[cfg(not(feature = "edf-scheduler"))] pub(crate) fn dequeue_all(&self, on_task: impl Fn(TaskRef)) { let taken = self.stack.take_all(); for taskref in taken { @@ -82,7 +82,7 @@ impl RunQueue { } } - /// # Deadline Ranked Sorted Scheduler + /// # Earliest Deadline First Scheduler /// /// This algorithm will loop until all enqueued tasks are processed. /// @@ -96,7 +96,7 @@ impl RunQueue { /// /// This process will repeat until the local `sorted` queue AND the global /// runqueue are both empty, at which point this function will return. - #[cfg(feature = "drs-scheduler")] + #[cfg(feature = "edf-scheduler")] pub(crate) fn dequeue_all(&self, on_task: impl Fn(TaskRef)) { // SAFETY: `deadline` can only be set through the `Deadline` interface, which // only allows access to this value while the given task is being polled. From 0cb1ffe0258380a3dd25c1037fdfb6ceca3149d9 Mon Sep 17 00:00:00 2001 From: James Munns Date: Wed, 16 Apr 2025 17:58:45 +0200 Subject: [PATCH 19/76] Add EDF example --- examples/nrf52840-edf/.cargo/config.toml | 9 ++ examples/nrf52840-edf/Cargo.toml | 21 +++ examples/nrf52840-edf/build.rs | 35 +++++ examples/nrf52840-edf/memory.x | 12 ++ examples/nrf52840-edf/src/bin/basic.rs | 191 +++++++++++++++++++++++ 5 files changed, 268 insertions(+) create mode 100644 examples/nrf52840-edf/.cargo/config.toml create mode 100644 examples/nrf52840-edf/Cargo.toml create mode 100644 examples/nrf52840-edf/build.rs create mode 100644 examples/nrf52840-edf/memory.x create mode 100644 examples/nrf52840-edf/src/bin/basic.rs diff --git a/examples/nrf52840-edf/.cargo/config.toml b/examples/nrf52840-edf/.cargo/config.toml new file mode 100644 index 000000000..e0b9ce59e --- /dev/null +++ b/examples/nrf52840-edf/.cargo/config.toml @@ -0,0 +1,9 @@ +[target.'cfg(all(target_arch = "arm", target_os = "none"))'] +# replace nRF82840_xxAA with your chip as listed in `probe-rs chip list` +runner = "probe-rs run --chip nRF52840_xxAA" + +[build] +target = "thumbv7em-none-eabi" + +[env] +DEFMT_LOG = "debug" diff --git a/examples/nrf52840-edf/Cargo.toml b/examples/nrf52840-edf/Cargo.toml new file mode 100644 index 000000000..c7147d1af --- /dev/null +++ b/examples/nrf52840-edf/Cargo.toml @@ -0,0 +1,21 @@ +[package] +edition = "2021" +name = "embassy-nrf52840-edf-examples" +version = "0.1.0" +license = "MIT OR Apache-2.0" + +[dependencies] +# NOTE: "edf-scheduler" feature is enabled +embassy-executor = { version = "0.7.0", path = "../../embassy-executor", features = ["arch-cortex-m", "executor-thread", "executor-interrupt", "defmt", "edf-scheduler"] } +embassy-time = { version = "0.4.0", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime"] } +embassy-nrf = { version = "0.3.1", path = "../../embassy-nrf", features = ["defmt", "nrf52840", "time-driver-rtc1", "gpiote", "unstable-pac", "time"] } + +defmt = "0.3" +defmt-rtt = "0.4" + +cortex-m = { version = "0.7.6", features = ["inline-asm", "critical-section-single-core"] } +cortex-m-rt = "0.7.0" +panic-probe = { version = "0.3", features = ["print-defmt"] } + +[profile.release] +debug = 2 diff --git a/examples/nrf52840-edf/build.rs b/examples/nrf52840-edf/build.rs new file mode 100644 index 000000000..30691aa97 --- /dev/null +++ b/examples/nrf52840-edf/build.rs @@ -0,0 +1,35 @@ +//! This build script copies the `memory.x` file from the crate root into +//! a directory where the linker can always find it at build time. +//! For many projects this is optional, as the linker always searches the +//! project root directory -- wherever `Cargo.toml` is. However, if you +//! are using a workspace or have a more complicated build setup, this +//! build script becomes required. Additionally, by requesting that +//! Cargo re-run the build script whenever `memory.x` is changed, +//! updating `memory.x` ensures a rebuild of the application with the +//! new memory settings. + +use std::env; +use std::fs::File; +use std::io::Write; +use std::path::PathBuf; + +fn main() { + // Put `memory.x` in our output directory and ensure it's + // on the linker search path. + let out = &PathBuf::from(env::var_os("OUT_DIR").unwrap()); + File::create(out.join("memory.x")) + .unwrap() + .write_all(include_bytes!("memory.x")) + .unwrap(); + println!("cargo:rustc-link-search={}", out.display()); + + // By default, Cargo will re-run a build script whenever + // any file in the project changes. By specifying `memory.x` + // here, we ensure the build script is only re-run when + // `memory.x` is changed. + println!("cargo:rerun-if-changed=memory.x"); + + println!("cargo:rustc-link-arg-bins=--nmagic"); + println!("cargo:rustc-link-arg-bins=-Tlink.x"); + println!("cargo:rustc-link-arg-bins=-Tdefmt.x"); +} diff --git a/examples/nrf52840-edf/memory.x b/examples/nrf52840-edf/memory.x new file mode 100644 index 000000000..15b492bce --- /dev/null +++ b/examples/nrf52840-edf/memory.x @@ -0,0 +1,12 @@ +MEMORY +{ + /* NOTE 1 K = 1 KiBi = 1024 bytes */ + FLASH : ORIGIN = 0x00000000, LENGTH = 1024K + RAM : ORIGIN = 0x20000000, LENGTH = 256K + + /* These values correspond to the NRF52840 with Softdevices S140 7.3.0 */ + /* + FLASH : ORIGIN = 0x00027000, LENGTH = 868K + RAM : ORIGIN = 0x20020000, LENGTH = 128K + */ +} diff --git a/examples/nrf52840-edf/src/bin/basic.rs b/examples/nrf52840-edf/src/bin/basic.rs new file mode 100644 index 000000000..6a7eb3c4b --- /dev/null +++ b/examples/nrf52840-edf/src/bin/basic.rs @@ -0,0 +1,191 @@ +//! Basic side-by-side example of the Earliest Deadline First scheduler +//! +//! This test spawns a number of background "ambient system load" workers +//! that are constantly working, and runs two sets of trials. +//! +//! The first trial runs with no deadline set, so our trial task is at the +//! same prioritization level as the background worker tasks. +//! +//! The second trial sets a deadline, meaning that it will be given higher +//! scheduling priority than background tasks, that have no deadline set + +#![no_std] +#![no_main] + +use core::sync::atomic::{compiler_fence, Ordering}; +use embassy_executor::{raw::Deadline, Spawner}; +use embassy_time::{Duration, Instant, Timer}; +use {defmt_rtt as _, panic_probe as _}; + +#[embassy_executor::main] +async fn main(spawner: Spawner) { + embassy_nrf::init(Default::default()); + + // Enable flash cache to remove some flash latency jitter + compiler_fence(Ordering::SeqCst); + embassy_nrf::pac::NVMC.icachecnf().write(|w| { + w.set_cacheen(true); + }); + compiler_fence(Ordering::SeqCst); + + // + // Baseline system load tunables + // + + // how many load tasks? More load tasks means more tasks contending + // for the runqueue + let tasks = 32; + // how long should each task work for? The longer the working time, + // the longer the max jitter possible, even when a task is prioritized, + // as EDF is still cooperative and not pre-emptive + // + // 33 ticks ~= 1ms + let work_time_ticks = 33; + // what fraction, 1/denominator, should the system be busy? + // bigger number means **less** busy + // + // 2 => 50% + // 4 => 25% + // 10 => 10% + let denominator = 2; + + // Total time window, so each worker is working 1/denominator + // amount of the total time + let time_window = work_time_ticks * u64::from(tasks) * denominator; + + // Spawn all of our load workers! + for i in 0..tasks { + spawner.must_spawn(load_task(i, work_time_ticks, time_window)); + } + + // Let all the tasks spin up + defmt::println!("Spinning up load tasks..."); + Timer::after_secs(1).await; + + // + // Trial task worker tunables + // + + // How many steps should the workers under test run? + // More steps means more chances to have to wait for other tasks + // in line ahead of us. + let num_steps = 100; + + // How many ticks should the worker take working on each step? + // + // 33 ticks ~= 1ms + let work_ticks = 33; + // How many ticks should the worker wait on each step? + // + // 66 ticks ~= 2ms + let idle_ticks = 66; + + // How many times to repeat each trial? + let trials = 3; + + // The total time a trial would take, in a perfect unloaded system + let theoretical = (num_steps * work_ticks) + (num_steps * idle_ticks); + + defmt::println!(""); + defmt::println!("Starting UNPRIORITIZED worker trials"); + for _ in 0..trials { + // + // UNPRIORITIZED worker + // + defmt::println!(""); + defmt::println!("Starting unprioritized worker"); + let start = Instant::now(); + for _ in 0..num_steps { + let now = Instant::now(); + while now.elapsed().as_ticks() < work_ticks {} + Timer::after_ticks(idle_ticks).await; + } + let elapsed = start.elapsed().as_ticks(); + defmt::println!( + "Trial complete, theoretical ticks: {=u64}, actual ticks: {=u64}", + theoretical, + elapsed + ); + let ratio = ((elapsed as f32) / (theoretical as f32)) * 100.0; + defmt::println!("Took {=f32}% of ideal time", ratio); + Timer::after_millis(500).await; + } + + Timer::after_secs(1).await; + + defmt::println!(""); + defmt::println!("Starting PRIORITIZED worker trials"); + for _ in 0..trials { + // + // PRIORITIZED worker + // + defmt::println!(""); + defmt::println!("Starting prioritized worker"); + let start = Instant::now(); + // Set the deadline to ~2x the theoretical time. In practice, setting any deadline + // here elevates the current task above all other worker tasks. + Deadline::set_current_task_deadline_after(theoretical * 2).await; + + // Perform the trial + for _ in 0..num_steps { + let now = Instant::now(); + while now.elapsed().as_ticks() < work_ticks {} + Timer::after_ticks(idle_ticks).await; + } + + let elapsed = start.elapsed().as_ticks(); + defmt::println!( + "Trial complete, theoretical ticks: {=u64}, actual ticks: {=u64}", + theoretical, + elapsed + ); + let ratio = ((elapsed as f32) / (theoretical as f32)) * 100.0; + defmt::println!("Took {=f32}% of ideal time", ratio); + + // Unset the deadline, deadlines are not automatically cleared, and if our + // deadline is in the past, then we get very high priority! + Deadline::clear_current_task_deadline().await; + + Timer::after_millis(500).await; + } + + defmt::println!(""); + defmt::println!("Trials Complete."); +} + +#[embassy_executor::task(pool_size = 32)] +async fn load_task(id: u32, ticks_on: u64, ttl_ticks: u64) { + let mut last_print = Instant::now(); + let mut last_tick = last_print; + let mut variance = 0; + let mut max_variance = 0; + loop { + let tgt = last_tick + Duration::from_ticks(ttl_ticks); + assert!(tgt > Instant::now(), "fell too behind!"); + + Timer::at(tgt).await; + let now = Instant::now(); + // How late are we from the target? + let var = now.duration_since(tgt).as_ticks(); + max_variance = max_variance.max(var); + variance += var; + + // blocking work + while now.elapsed().as_ticks() < ticks_on {} + + if last_print.elapsed() >= Duration::from_secs(1) { + defmt::trace!( + "Task {=u32} variance ticks (1s): {=u64}, max: {=u64}, act: {=u64}", + id, + variance, + max_variance, + ticks_on, + ); + max_variance = 0; + variance = 0; + last_print = Instant::now(); + } + + last_tick = tgt; + } +} From 67ed473973dae6d60aed8b88b8b854b224660e8d Mon Sep 17 00:00:00 2001 From: James Munns Date: Wed, 16 Apr 2025 18:01:11 +0200 Subject: [PATCH 20/76] rustfmt --- examples/nrf52840-edf/src/bin/basic.rs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/examples/nrf52840-edf/src/bin/basic.rs b/examples/nrf52840-edf/src/bin/basic.rs index 6a7eb3c4b..c270c67b9 100644 --- a/examples/nrf52840-edf/src/bin/basic.rs +++ b/examples/nrf52840-edf/src/bin/basic.rs @@ -13,7 +13,9 @@ #![no_main] use core::sync::atomic::{compiler_fence, Ordering}; -use embassy_executor::{raw::Deadline, Spawner}; + +use embassy_executor::raw::Deadline; +use embassy_executor::Spawner; use embassy_time::{Duration, Instant, Timer}; use {defmt_rtt as _, panic_probe as _}; From 7af8f35a50c631802615044e12cc9c74614f78bb Mon Sep 17 00:00:00 2001 From: James Munns Date: Tue, 3 Jun 2025 16:34:12 +0200 Subject: [PATCH 21/76] There can be only one (run queue) --- embassy-executor/Cargo.toml | 20 ++--- embassy-executor/src/raw/deadline.rs | 3 - embassy-executor/src/raw/mod.rs | 2 - .../{run_queue_atomics.rs => run_queue.rs} | 28 ++++++- .../src/raw/run_queue_critical_section.rs | 74 ------------------- 5 files changed, 36 insertions(+), 91 deletions(-) rename embassy-executor/src/raw/{run_queue_atomics.rs => run_queue.rs} (87%) delete mode 100644 embassy-executor/src/raw/run_queue_critical_section.rs diff --git a/embassy-executor/Cargo.toml b/embassy-executor/Cargo.toml index 17315eaa3..34468e4f9 100644 --- a/embassy-executor/Cargo.toml +++ b/embassy-executor/Cargo.toml @@ -76,13 +76,16 @@ js-sys = { version = "0.3", optional = true } # arch-avr dependencies avr-device = { version = "0.7.0", optional = true } -# Note: this is ONLY a dependency when the target has atomics, this is -# used for `run_queue_atomics`. We need to be conditional because -# cordyceps *requires* the use of atomics, so we pull it in when -# `run_queue_atomics` would be enabled, and NOT when `run_queue_critical_section` -# would be enabled. -[target.'cfg(target_has_atomic="ptr")'.dependencies.cordyceps] -version = "0.3.3" + +[dependencies.cordyceps] +# version = "0.3.3" +# todo: update after https://github.com/hawkw/mycelium/pull/537 is merged +git = "https://github.com/hawkw/mycelium" +rev = "86c428eecfd37ee24dd81f14c4a9f5c8ecefcf17" + +# Note: this is ONLY a dependency when the target does NOT have atomics +[target.'cfg(not(target_has_atomic="ptr"))'.dependencies.mutex] +version = "1.0" [dev-dependencies] critical-section = { version = "1.1", features = ["std"] } @@ -135,6 +138,5 @@ rtos-trace = ["_any_trace", "metadata-name", "dep:rtos-trace", "dep:embassy-time _any_trace = [] ## Enable "Earliest Deadline First" Scheduler, using soft-realtime "deadlines" to prioritize -## tasks based on the remaining time before their deadline. Adds some overhead. Requires -## hardware atomic support +## tasks based on the remaining time before their deadline. Adds some overhead. edf-scheduler = ["dep:embassy-time-driver"] diff --git a/embassy-executor/src/raw/deadline.rs b/embassy-executor/src/raw/deadline.rs index 006c7caf1..0fb22a7ce 100644 --- a/embassy-executor/src/raw/deadline.rs +++ b/embassy-executor/src/raw/deadline.rs @@ -1,9 +1,6 @@ use core::future::{poll_fn, Future}; use core::task::Poll; -#[cfg(not(target_has_atomic = "ptr"))] -compile_error!("The `edf-scheduler` feature is currently only supported on targets with atomics."); - /// A type for interacting with the deadline of the current task /// /// Requires the `edf-scheduler` feature diff --git a/embassy-executor/src/raw/mod.rs b/embassy-executor/src/raw/mod.rs index 96e7fda74..cc43690cb 100644 --- a/embassy-executor/src/raw/mod.rs +++ b/embassy-executor/src/raw/mod.rs @@ -7,8 +7,6 @@ //! Using this module requires respecting subtle safety contracts. If you can, prefer using the safe //! [executor wrappers](crate::Executor) and the [`embassy_executor::task`](embassy_executor_macros::task) macro, which are fully safe. -#[cfg_attr(target_has_atomic = "ptr", path = "run_queue_atomics.rs")] -#[cfg_attr(not(target_has_atomic = "ptr"), path = "run_queue_critical_section.rs")] mod run_queue; #[cfg_attr(all(cortex_m, target_has_atomic = "32"), path = "state_atomics_arm.rs")] diff --git a/embassy-executor/src/raw/run_queue_atomics.rs b/embassy-executor/src/raw/run_queue.rs similarity index 87% rename from embassy-executor/src/raw/run_queue_atomics.rs rename to embassy-executor/src/raw/run_queue.rs index 65a9b7859..f630041e0 100644 --- a/embassy-executor/src/raw/run_queue_atomics.rs +++ b/embassy-executor/src/raw/run_queue.rs @@ -1,9 +1,15 @@ use core::ptr::{addr_of_mut, NonNull}; use cordyceps::sorted_list::Links; +use cordyceps::Linked; #[cfg(feature = "edf-scheduler")] use cordyceps::SortedList; -use cordyceps::{Linked, TransferStack}; + +#[cfg(target_has_atomic = "ptr")] +type TransferStack = cordyceps::TransferStack; + +#[cfg(not(target_has_atomic = "ptr"))] +type TransferStack = cordyceps::TransferStack; use super::{TaskHeader, TaskRef}; @@ -77,7 +83,7 @@ impl RunQueue { pub(crate) fn dequeue_all(&self, on_task: impl Fn(TaskRef)) { let taken = self.stack.take_all(); for taskref in taken { - taskref.header().state.run_dequeue(); + run_dequeue(&taskref); on_task(taskref); } } @@ -122,8 +128,24 @@ impl RunQueue { }; // We got one task, mark it as dequeued, and process the task. - taskref.header().state.run_dequeue(); + run_dequeue(&taskref); on_task(taskref); } } } + +/// atomic state does not require a cs... +#[cfg(target_has_atomic = "ptr")] +#[inline(always)] +fn run_dequeue(taskref: &TaskRef) { + taskref.header().state.run_dequeue(); +} + +/// ...while non-atomic state does +#[cfg(not(target_has_atomic = "ptr"))] +#[inline(always)] +fn run_dequeue(taskref: &TaskRef) { + critical_section::with(|cs| { + taskref.header().state.run_dequeue(cs); + }) +} diff --git a/embassy-executor/src/raw/run_queue_critical_section.rs b/embassy-executor/src/raw/run_queue_critical_section.rs deleted file mode 100644 index 86c4085ed..000000000 --- a/embassy-executor/src/raw/run_queue_critical_section.rs +++ /dev/null @@ -1,74 +0,0 @@ -use core::cell::Cell; - -use critical_section::{CriticalSection, Mutex}; - -use super::TaskRef; - -pub(crate) struct RunQueueItem { - next: Mutex>>, -} - -impl RunQueueItem { - pub const fn new() -> Self { - Self { - next: Mutex::new(Cell::new(None)), - } - } -} - -/// Atomic task queue using a very, very simple lock-free linked-list queue: -/// -/// To enqueue a task, task.next is set to the old head, and head is atomically set to task. -/// -/// Dequeuing is done in batches: the queue is emptied by atomically replacing head with -/// null. Then the batch is iterated following the next pointers until null is reached. -/// -/// Note that batches will be iterated in the reverse order as they were enqueued. This is OK -/// for our purposes: it can't create fairness problems since the next batch won't run until the -/// current batch is completely processed, so even if a task enqueues itself instantly (for example -/// by waking its own waker) can't prevent other tasks from running. -pub(crate) struct RunQueue { - head: Mutex>>, -} - -impl RunQueue { - pub const fn new() -> Self { - Self { - head: Mutex::new(Cell::new(None)), - } - } - - /// Enqueues an item. Returns true if the queue was empty. - /// - /// # Safety - /// - /// `item` must NOT be already enqueued in any queue. - #[inline(always)] - pub(crate) unsafe fn enqueue(&self, task: TaskRef, cs: CriticalSection<'_>) -> bool { - let prev = self.head.borrow(cs).replace(Some(task)); - task.header().run_queue_item.next.borrow(cs).set(prev); - - prev.is_none() - } - - /// Empty the queue, then call `on_task` for each task that was in the queue. - /// NOTE: It is OK for `on_task` to enqueue more tasks. In this case they're left in the queue - /// and will be processed by the *next* call to `dequeue_all`, *not* the current one. - pub(crate) fn dequeue_all(&self, on_task: impl Fn(TaskRef)) { - // Atomically empty the queue. - let mut next = critical_section::with(|cs| self.head.borrow(cs).take()); - - // Iterate the linked list of tasks that were previously in the queue. - while let Some(task) = next { - // If the task re-enqueues itself, the `next` pointer will get overwritten. - // Therefore, first read the next pointer, and only then process the task. - - critical_section::with(|cs| { - next = task.header().run_queue_item.next.borrow(cs).get(); - task.header().state.run_dequeue(cs); - }); - - on_task(task); - } - } -} From d88ea8dd2adefba42489173d5119e888ffa73f07 Mon Sep 17 00:00:00 2001 From: James Munns Date: Wed, 4 Jun 2025 12:22:32 +0200 Subject: [PATCH 22/76] Update with cordyceps changes --- embassy-executor/Cargo.toml | 2 +- embassy-executor/src/raw/run_queue.rs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/embassy-executor/Cargo.toml b/embassy-executor/Cargo.toml index 34468e4f9..0ea18acbc 100644 --- a/embassy-executor/Cargo.toml +++ b/embassy-executor/Cargo.toml @@ -81,7 +81,7 @@ avr-device = { version = "0.7.0", optional = true } # version = "0.3.3" # todo: update after https://github.com/hawkw/mycelium/pull/537 is merged git = "https://github.com/hawkw/mycelium" -rev = "86c428eecfd37ee24dd81f14c4a9f5c8ecefcf17" +rev = "e21f9756e7d787a023f2ef1bc7f2159cc7dd26e0" # Note: this is ONLY a dependency when the target does NOT have atomics [target.'cfg(not(target_has_atomic="ptr"))'.dependencies.mutex] diff --git a/embassy-executor/src/raw/run_queue.rs b/embassy-executor/src/raw/run_queue.rs index f630041e0..c6c7d7109 100644 --- a/embassy-executor/src/raw/run_queue.rs +++ b/embassy-executor/src/raw/run_queue.rs @@ -9,7 +9,7 @@ use cordyceps::SortedList; type TransferStack = cordyceps::TransferStack; #[cfg(not(target_has_atomic = "ptr"))] -type TransferStack = cordyceps::TransferStack; +type TransferStack = cordyceps::MutexTransferStack; use super::{TaskHeader, TaskRef}; From db063945e76a9b62672377ed71e6e833a295a054 Mon Sep 17 00:00:00 2001 From: James Munns Date: Wed, 2 Jul 2025 13:48:32 +0200 Subject: [PATCH 23/76] Inline the "MutexTransferStack" impl as it is unclear whether it will be merged upstream --- embassy-executor/Cargo.toml | 6 ++---- embassy-executor/src/raw/run_queue.rs | 31 ++++++++++++++++++++++++++- 2 files changed, 32 insertions(+), 5 deletions(-) diff --git a/embassy-executor/Cargo.toml b/embassy-executor/Cargo.toml index 0ea18acbc..01c028704 100644 --- a/embassy-executor/Cargo.toml +++ b/embassy-executor/Cargo.toml @@ -78,10 +78,8 @@ avr-device = { version = "0.7.0", optional = true } [dependencies.cordyceps] -# version = "0.3.3" -# todo: update after https://github.com/hawkw/mycelium/pull/537 is merged -git = "https://github.com/hawkw/mycelium" -rev = "e21f9756e7d787a023f2ef1bc7f2159cc7dd26e0" +version = "0.3.4" +features = ["no-cache-pad"] # Note: this is ONLY a dependency when the target does NOT have atomics [target.'cfg(not(target_has_atomic="ptr"))'.dependencies.mutex] diff --git a/embassy-executor/src/raw/run_queue.rs b/embassy-executor/src/raw/run_queue.rs index c6c7d7109..5fd703aad 100644 --- a/embassy-executor/src/raw/run_queue.rs +++ b/embassy-executor/src/raw/run_queue.rs @@ -9,7 +9,7 @@ use cordyceps::SortedList; type TransferStack = cordyceps::TransferStack; #[cfg(not(target_has_atomic = "ptr"))] -type TransferStack = cordyceps::MutexTransferStack; +type TransferStack = MutexTransferStack; use super::{TaskHeader, TaskRef}; @@ -149,3 +149,32 @@ fn run_dequeue(taskref: &TaskRef) { taskref.header().state.run_dequeue(cs); }) } + +/// A wrapper type that acts like TransferStack by wrapping a normal Stack in a CS mutex +#[cfg(not(target_has_atomic="ptr"))] +struct MutexTransferStack>> { + inner: mutex::BlockingMutex>, +} + +#[cfg(not(target_has_atomic="ptr"))] +impl>> MutexTransferStack { + const fn new() -> Self { + Self { + inner: mutex::BlockingMutex::new(cordyceps::Stack::new()), + } + } + + fn push_was_empty(&self, item: T::Handle) -> bool { + self.inner.with_lock(|stack| { + let is_empty = stack.is_empty(); + stack.push(item); + is_empty + }) + } + + fn take_all(&self) -> cordyceps::Stack { + self.inner.with_lock(|stack| { + stack.take_all() + }) + } +} From cf171ad6d9c0a7487400beb9e4a436e5c1b64e19 Mon Sep 17 00:00:00 2001 From: James Munns Date: Thu, 3 Jul 2025 09:11:32 +0200 Subject: [PATCH 24/76] fmt --- embassy-executor/src/raw/run_queue.rs | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/embassy-executor/src/raw/run_queue.rs b/embassy-executor/src/raw/run_queue.rs index 5fd703aad..b4b22819f 100644 --- a/embassy-executor/src/raw/run_queue.rs +++ b/embassy-executor/src/raw/run_queue.rs @@ -151,12 +151,12 @@ fn run_dequeue(taskref: &TaskRef) { } /// A wrapper type that acts like TransferStack by wrapping a normal Stack in a CS mutex -#[cfg(not(target_has_atomic="ptr"))] +#[cfg(not(target_has_atomic = "ptr"))] struct MutexTransferStack>> { inner: mutex::BlockingMutex>, } -#[cfg(not(target_has_atomic="ptr"))] +#[cfg(not(target_has_atomic = "ptr"))] impl>> MutexTransferStack { const fn new() -> Self { Self { @@ -173,8 +173,6 @@ impl>> MutexTransferStack { } fn take_all(&self) -> cordyceps::Stack { - self.inner.with_lock(|stack| { - stack.take_all() - }) + self.inner.with_lock(|stack| stack.take_all()) } } From 20b56b0fe0570f0d1e8c61d23d067627a4dfc165 Mon Sep 17 00:00:00 2001 From: James Munns Date: Tue, 15 Jul 2025 13:33:51 +0200 Subject: [PATCH 25/76] Update to use critical-section::Mutex instead of mutex::BlockingMutex This allows the scheduler to better collaborate with existing critical sections --- embassy-executor/Cargo.toml | 4 ---- embassy-executor/src/raw/run_queue.rs | 28 ++++++++++++++++----------- 2 files changed, 17 insertions(+), 15 deletions(-) diff --git a/embassy-executor/Cargo.toml b/embassy-executor/Cargo.toml index 01c028704..290e67bce 100644 --- a/embassy-executor/Cargo.toml +++ b/embassy-executor/Cargo.toml @@ -81,10 +81,6 @@ avr-device = { version = "0.7.0", optional = true } version = "0.3.4" features = ["no-cache-pad"] -# Note: this is ONLY a dependency when the target does NOT have atomics -[target.'cfg(not(target_has_atomic="ptr"))'.dependencies.mutex] -version = "1.0" - [dev-dependencies] critical-section = { version = "1.1", features = ["std"] } trybuild = "1.0" diff --git a/embassy-executor/src/raw/run_queue.rs b/embassy-executor/src/raw/run_queue.rs index b4b22819f..9acb9dd28 100644 --- a/embassy-executor/src/raw/run_queue.rs +++ b/embassy-executor/src/raw/run_queue.rs @@ -70,8 +70,12 @@ impl RunQueue { /// /// `item` must NOT be already enqueued in any queue. #[inline(always)] - pub(crate) unsafe fn enqueue(&self, task: TaskRef, _: super::state::Token) -> bool { - self.stack.push_was_empty(task) + pub(crate) unsafe fn enqueue(&self, task: TaskRef, _tok: super::state::Token) -> bool { + self.stack.push_was_empty( + task, + #[cfg(not(target_has_atomic = "ptr"))] + _tok, + ) } /// # Standard atomic runqueue @@ -153,26 +157,28 @@ fn run_dequeue(taskref: &TaskRef) { /// A wrapper type that acts like TransferStack by wrapping a normal Stack in a CS mutex #[cfg(not(target_has_atomic = "ptr"))] struct MutexTransferStack>> { - inner: mutex::BlockingMutex>, + inner: critical_section::Mutex>>, } #[cfg(not(target_has_atomic = "ptr"))] impl>> MutexTransferStack { const fn new() -> Self { Self { - inner: mutex::BlockingMutex::new(cordyceps::Stack::new()), + inner: critical_section::Mutex::new(core::cell::RefCell::new(cordyceps::Stack::new())), } } - fn push_was_empty(&self, item: T::Handle) -> bool { - self.inner.with_lock(|stack| { - let is_empty = stack.is_empty(); - stack.push(item); - is_empty - }) + fn push_was_empty(&self, item: T::Handle, token: super::state::Token) -> bool { + let mut guard = self.inner.borrow_ref_mut(token); + let is_empty = guard.is_empty(); + guard.push(item); + is_empty } fn take_all(&self) -> cordyceps::Stack { - self.inner.with_lock(|stack| stack.take_all()) + critical_section::with(|cs| { + let mut guard = self.inner.borrow_ref_mut(cs); + guard.take_all() + }) } } From 38e5e2e9ceb9a34badfdfc57477f0dba23c64ced Mon Sep 17 00:00:00 2001 From: James Munns Date: Tue, 15 Jul 2025 14:51:08 +0200 Subject: [PATCH 26/76] Replace use of RefCell with UnsafeCell --- embassy-executor/src/raw/run_queue.rs | 21 +++++++++++++++++---- 1 file changed, 17 insertions(+), 4 deletions(-) diff --git a/embassy-executor/src/raw/run_queue.rs b/embassy-executor/src/raw/run_queue.rs index 9acb9dd28..97d26a18a 100644 --- a/embassy-executor/src/raw/run_queue.rs +++ b/embassy-executor/src/raw/run_queue.rs @@ -157,19 +157,26 @@ fn run_dequeue(taskref: &TaskRef) { /// A wrapper type that acts like TransferStack by wrapping a normal Stack in a CS mutex #[cfg(not(target_has_atomic = "ptr"))] struct MutexTransferStack>> { - inner: critical_section::Mutex>>, + inner: critical_section::Mutex>>, } #[cfg(not(target_has_atomic = "ptr"))] impl>> MutexTransferStack { const fn new() -> Self { Self { - inner: critical_section::Mutex::new(core::cell::RefCell::new(cordyceps::Stack::new())), + inner: critical_section::Mutex::new(core::cell::UnsafeCell::new(cordyceps::Stack::new())), } } + /// Push an item to the transfer stack, returning whether the stack was previously empty fn push_was_empty(&self, item: T::Handle, token: super::state::Token) -> bool { - let mut guard = self.inner.borrow_ref_mut(token); + /// SAFETY: The critical-section mutex guarantees that there is no *concurrent* access + /// for the lifetime of the token, but does NOT protect against re-entrant access. + /// However, we never *return* the reference, nor do we recurse (or call another method + /// like `take_all`) that could ever allow for re-entrant aliasing. Therefore, the + /// presence of the critical section is sufficient to guarantee exclusive access to + /// the `inner` field for the purposes of this function + let mut guard = unsafe { &mut *self.inner.borrow(token).get() }; let is_empty = guard.is_empty(); guard.push(item); is_empty @@ -177,7 +184,13 @@ impl>> MutexTransferStack { fn take_all(&self) -> cordyceps::Stack { critical_section::with(|cs| { - let mut guard = self.inner.borrow_ref_mut(cs); + /// SAFETY: The critical-section mutex guarantees that there is no *concurrent* access + /// for the lifetime of the token, but does NOT protect against re-entrant access. + /// However, we never *return* the reference, nor do we recurse (or call another method + /// like `push_was_empty`) that could ever allow for re-entrant aliasing. Therefore, the + /// presence of the critical section is sufficient to guarantee exclusive access to + /// the `inner` field for the purposes of this function + let mut guard = unsafe { &mut *self.inner.borrow(cs).get() }; guard.take_all() }) } From 4479f5bbfce9002b965f9e3e189cdd5c61096eff Mon Sep 17 00:00:00 2001 From: James Munns Date: Tue, 15 Jul 2025 14:54:20 +0200 Subject: [PATCH 27/76] Regular comments not doc comments --- embassy-executor/src/raw/run_queue.rs | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/embassy-executor/src/raw/run_queue.rs b/embassy-executor/src/raw/run_queue.rs index 97d26a18a..1eb5775d8 100644 --- a/embassy-executor/src/raw/run_queue.rs +++ b/embassy-executor/src/raw/run_queue.rs @@ -170,12 +170,12 @@ impl>> MutexTransferStack { /// Push an item to the transfer stack, returning whether the stack was previously empty fn push_was_empty(&self, item: T::Handle, token: super::state::Token) -> bool { - /// SAFETY: The critical-section mutex guarantees that there is no *concurrent* access - /// for the lifetime of the token, but does NOT protect against re-entrant access. - /// However, we never *return* the reference, nor do we recurse (or call another method - /// like `take_all`) that could ever allow for re-entrant aliasing. Therefore, the - /// presence of the critical section is sufficient to guarantee exclusive access to - /// the `inner` field for the purposes of this function + // SAFETY: The critical-section mutex guarantees that there is no *concurrent* access + // for the lifetime of the token, but does NOT protect against re-entrant access. + // However, we never *return* the reference, nor do we recurse (or call another method + // like `take_all`) that could ever allow for re-entrant aliasing. Therefore, the + // presence of the critical section is sufficient to guarantee exclusive access to + // the `inner` field for the purposes of this function. let mut guard = unsafe { &mut *self.inner.borrow(token).get() }; let is_empty = guard.is_empty(); guard.push(item); @@ -184,12 +184,12 @@ impl>> MutexTransferStack { fn take_all(&self) -> cordyceps::Stack { critical_section::with(|cs| { - /// SAFETY: The critical-section mutex guarantees that there is no *concurrent* access - /// for the lifetime of the token, but does NOT protect against re-entrant access. - /// However, we never *return* the reference, nor do we recurse (or call another method - /// like `push_was_empty`) that could ever allow for re-entrant aliasing. Therefore, the - /// presence of the critical section is sufficient to guarantee exclusive access to - /// the `inner` field for the purposes of this function + // SAFETY: The critical-section mutex guarantees that there is no *concurrent* access + // for the lifetime of the token, but does NOT protect against re-entrant access. + // However, we never *return* the reference, nor do we recurse (or call another method + // like `push_was_empty`) that could ever allow for re-entrant aliasing. Therefore, the + // presence of the critical section is sufficient to guarantee exclusive access to + // the `inner` field for the purposes of this function. let mut guard = unsafe { &mut *self.inner.borrow(cs).get() }; guard.take_all() }) From b5c9e721009fd4331cdc1ce58a07698eb54f2959 Mon Sep 17 00:00:00 2001 From: James Munns Date: Tue, 15 Jul 2025 14:58:41 +0200 Subject: [PATCH 28/76] Rename, remove excess mut --- embassy-executor/src/raw/run_queue.rs | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/embassy-executor/src/raw/run_queue.rs b/embassy-executor/src/raw/run_queue.rs index 1eb5775d8..97060f4b9 100644 --- a/embassy-executor/src/raw/run_queue.rs +++ b/embassy-executor/src/raw/run_queue.rs @@ -176,9 +176,9 @@ impl>> MutexTransferStack { // like `take_all`) that could ever allow for re-entrant aliasing. Therefore, the // presence of the critical section is sufficient to guarantee exclusive access to // the `inner` field for the purposes of this function. - let mut guard = unsafe { &mut *self.inner.borrow(token).get() }; - let is_empty = guard.is_empty(); - guard.push(item); + let inner = unsafe { &mut *self.inner.borrow(token).get() }; + let is_empty = inner.is_empty(); + inner.push(item); is_empty } @@ -190,8 +190,8 @@ impl>> MutexTransferStack { // like `push_was_empty`) that could ever allow for re-entrant aliasing. Therefore, the // presence of the critical section is sufficient to guarantee exclusive access to // the `inner` field for the purposes of this function. - let mut guard = unsafe { &mut *self.inner.borrow(cs).get() }; - guard.take_all() + let inner = unsafe { &mut *self.inner.borrow(cs).get() }; + inner.take_all() }) } } From 3f606b28f3b32e9e3b9a9f136eeef52828a78512 Mon Sep 17 00:00:00 2001 From: Dion Dokter Date: Tue, 15 Jul 2025 13:40:30 +0200 Subject: [PATCH 29/76] Change deadline to use internal atomics --- embassy-executor/src/raw/deadline.rs | 99 +++++++++++++-------------- embassy-executor/src/raw/mod.rs | 4 +- embassy-executor/src/raw/run_queue.rs | 8 +-- 3 files changed, 54 insertions(+), 57 deletions(-) diff --git a/embassy-executor/src/raw/deadline.rs b/embassy-executor/src/raw/deadline.rs index 0fb22a7ce..a61852612 100644 --- a/embassy-executor/src/raw/deadline.rs +++ b/embassy-executor/src/raw/deadline.rs @@ -1,15 +1,41 @@ use core::future::{poll_fn, Future}; +use core::sync::atomic::{AtomicU32, Ordering}; use core::task::Poll; /// A type for interacting with the deadline of the current task /// /// Requires the `edf-scheduler` feature pub struct Deadline { - /// Deadline value in ticks, same time base and ticks as `embassy-time` - pub instant_ticks: u64, + instant_ticks_hi: AtomicU32, + instant_ticks_lo: AtomicU32, } impl Deadline { + pub(crate) const fn new(instant_ticks: u64) -> Self { + Self { + instant_ticks_hi: AtomicU32::new((instant_ticks >> 32) as u32), + instant_ticks_lo: AtomicU32::new(instant_ticks as u32), + } + } + + pub(crate) const fn new_unset() -> Self { + Self::new(Self::UNSET_DEADLINE_TICKS) + } + + pub(crate) fn set(&self, instant_ticks: u64) { + self.instant_ticks_hi + .store((instant_ticks >> 32) as u32, Ordering::Relaxed); + self.instant_ticks_lo.store(instant_ticks as u32, Ordering::Relaxed); + } + + /// Deadline value in ticks, same time base and ticks as `embassy-time` + pub fn instant_ticks(&self) -> u64 { + let hi = self.instant_ticks_hi.load(Ordering::Relaxed) as u64; + let lo = self.instant_ticks_lo.load(Ordering::Relaxed) as u64; + + (hi << 32) | lo + } + /// Sentinel value representing an "unset" deadline, which has lower priority /// than any other set deadline value pub const UNSET_DEADLINE_TICKS: u64 = u64::MAX; @@ -17,7 +43,7 @@ impl Deadline { /// Does the given Deadline represent an "unset" deadline? #[inline] pub fn is_unset(&self) -> bool { - self.instant_ticks == Self::UNSET_DEADLINE_TICKS + self.instant_ticks() == Self::UNSET_DEADLINE_TICKS } /// Set the current task's deadline at exactly `instant_ticks` @@ -32,11 +58,7 @@ impl Deadline { pub fn set_current_task_deadline(instant_ticks: u64) -> impl Future { poll_fn(move |cx| { let task = super::task_from_waker(cx.waker()); - // SAFETY: A task can only modify its own deadline, while the task is being - // polled, meaning that there cannot be concurrent access to the deadline. - unsafe { - task.header().deadline.set(instant_ticks); - } + task.header().deadline.set(instant_ticks); Poll::Ready(()) }) } @@ -61,14 +83,9 @@ impl Deadline { // reasons later. let deadline = now.saturating_add(duration_ticks); - // SAFETY: A task can only modify its own deadline, while the task is being - // polled, meaning that there cannot be concurrent access to the deadline. - unsafe { - task.header().deadline.set(deadline); - } - Poll::Ready(Deadline { - instant_ticks: deadline, - }) + task.header().deadline.set(deadline); + + Poll::Ready(Deadline::new(deadline)) }) } @@ -90,24 +107,18 @@ impl Deadline { poll_fn(move |cx| { let task = super::task_from_waker(cx.waker()); - // SAFETY: A task can only modify its own deadline, while the task is being - // polled, meaning that there cannot be concurrent access to the deadline. - unsafe { - // Get the last value - let last = task.header().deadline.get(); + // Get the last value + let last = task.header().deadline.instant_ticks(); - // Since ticks is a u64, saturating add is PROBABLY overly cautious, leave - // it for now, we can probably make this wrapping_add for performance - // reasons later. - let deadline = last.saturating_add(increment_ticks); + // Since ticks is a u64, saturating add is PROBABLY overly cautious, leave + // it for now, we can probably make this wrapping_add for performance + // reasons later. + let deadline = last.saturating_add(increment_ticks); - // Store the new value - task.header().deadline.set(deadline); + // Store the new value + task.header().deadline.set(deadline); - Poll::Ready(Deadline { - instant_ticks: deadline, - }) - } + Poll::Ready(Deadline::new(deadline)) }) } @@ -119,12 +130,8 @@ impl Deadline { poll_fn(move |cx| { let task = super::task_from_waker(cx.waker()); - // SAFETY: A task can only modify its own deadline, while the task is being - // polled, meaning that there cannot be concurrent access to the deadline. - let deadline = unsafe { task.header().deadline.get() }; - Poll::Ready(Self { - instant_ticks: deadline, - }) + let deadline = task.header().deadline.instant_ticks(); + Poll::Ready(Self::new(deadline)) }) } @@ -137,20 +144,12 @@ impl Deadline { poll_fn(move |cx| { let task = super::task_from_waker(cx.waker()); - // SAFETY: A task can only modify its own deadline, while the task is being - // polled, meaning that there cannot be concurrent access to the deadline. - let deadline = unsafe { - // get the old value - let d = task.header().deadline.get(); - // Store the default value - task.header().deadline.set(Self::UNSET_DEADLINE_TICKS); - // return the old value - d - }; + // get the old value + let deadline = task.header().deadline.instant_ticks(); + // Store the default value + task.header().deadline.set(Self::UNSET_DEADLINE_TICKS); - Poll::Ready(Self { - instant_ticks: deadline, - }) + Poll::Ready(Self::new(deadline)) }) } } diff --git a/embassy-executor/src/raw/mod.rs b/embassy-executor/src/raw/mod.rs index cc43690cb..be2c5ee28 100644 --- a/embassy-executor/src/raw/mod.rs +++ b/embassy-executor/src/raw/mod.rs @@ -106,7 +106,7 @@ pub(crate) struct TaskHeader { /// Earliest Deadline First scheduler Deadline. This field should not be accessed /// outside the context of the task itself as it being polled by the executor. #[cfg(feature = "edf-scheduler")] - pub(crate) deadline: SyncUnsafeCell, + pub(crate) deadline: Deadline, pub(crate) executor: AtomicPtr, poll_fn: SyncUnsafeCell>, @@ -215,7 +215,7 @@ impl TaskStorage { // NOTE: The deadline is set to zero to allow the initializer to reside in `.bss`. This // will be lazily initalized in `initialize_impl` #[cfg(feature = "edf-scheduler")] - deadline: SyncUnsafeCell::new(0u64), + deadline: Deadline::new_unset(), executor: AtomicPtr::new(core::ptr::null_mut()), // Note: this is lazily initialized so that a static `TaskStorage` will go in `.bss` poll_fn: SyncUnsafeCell::new(None), diff --git a/embassy-executor/src/raw/run_queue.rs b/embassy-executor/src/raw/run_queue.rs index 97060f4b9..e8a046a48 100644 --- a/embassy-executor/src/raw/run_queue.rs +++ b/embassy-executor/src/raw/run_queue.rs @@ -108,11 +108,9 @@ impl RunQueue { /// runqueue are both empty, at which point this function will return. #[cfg(feature = "edf-scheduler")] pub(crate) fn dequeue_all(&self, on_task: impl Fn(TaskRef)) { - // SAFETY: `deadline` can only be set through the `Deadline` interface, which - // only allows access to this value while the given task is being polled. - // This acts as mutual exclusion for access. - let mut sorted = - SortedList::::new_with_cmp(|lhs, rhs| unsafe { lhs.deadline.get().cmp(&rhs.deadline.get()) }); + let mut sorted = SortedList::::new_with_cmp(|lhs, rhs| { + lhs.deadline.instant_ticks().cmp(&rhs.deadline.instant_ticks()) + }); loop { // For each loop, grab any newly pended items From d6d4df1c768f8ae43ad1339b74d351f4cbad0386 Mon Sep 17 00:00:00 2001 From: Dion Dokter Date: Tue, 15 Jul 2025 14:30:02 +0200 Subject: [PATCH 30/76] Add some docs --- embassy-executor/src/raw/deadline.rs | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/embassy-executor/src/raw/deadline.rs b/embassy-executor/src/raw/deadline.rs index a61852612..cbb379b82 100644 --- a/embassy-executor/src/raw/deadline.rs +++ b/embassy-executor/src/raw/deadline.rs @@ -4,7 +4,11 @@ use core::task::Poll; /// A type for interacting with the deadline of the current task /// -/// Requires the `edf-scheduler` feature +/// Requires the `edf-scheduler` feature. +/// +/// Note: Interacting with the deadline should be done locally in a task. +/// In theory you could try to set or read the deadline from another task, +/// but that will result in weird (though not unsound) behavior. pub struct Deadline { instant_ticks_hi: AtomicU32, instant_ticks_lo: AtomicU32, From 52d178560501a464dba67da89a1570ae9a2cf66c Mon Sep 17 00:00:00 2001 From: diondokter Date: Fri, 29 Aug 2025 14:36:17 +0200 Subject: [PATCH 31/76] Introduce metadata-deadline and let the EDF scheduler use it --- embassy-executor/Cargo.toml | 4 +++- embassy-executor/src/metadata.rs | 13 +++++++++++++ embassy-executor/src/raw/deadline.rs | 18 +++++++++--------- embassy-executor/src/raw/mod.rs | 19 +++++++------------ embassy-executor/src/raw/run_queue.rs | 5 ++++- 5 files changed, 36 insertions(+), 23 deletions(-) diff --git a/embassy-executor/Cargo.toml b/embassy-executor/Cargo.toml index 290e67bce..2de36d22d 100644 --- a/embassy-executor/Cargo.toml +++ b/embassy-executor/Cargo.toml @@ -118,6 +118,8 @@ arch-spin = ["_arch"] ## Enable the `name` field in task metadata. metadata-name = ["embassy-executor-macros/metadata-name"] +## Enable the `deadline` field in task metadata. +metadata-deadline = [] #! ### Executor @@ -133,4 +135,4 @@ _any_trace = [] ## Enable "Earliest Deadline First" Scheduler, using soft-realtime "deadlines" to prioritize ## tasks based on the remaining time before their deadline. Adds some overhead. -edf-scheduler = ["dep:embassy-time-driver"] +edf-scheduler = ["dep:embassy-time-driver", "metadata-deadline"] diff --git a/embassy-executor/src/metadata.rs b/embassy-executor/src/metadata.rs index f92c9b37c..fd8095629 100644 --- a/embassy-executor/src/metadata.rs +++ b/embassy-executor/src/metadata.rs @@ -12,6 +12,8 @@ use crate::raw; pub struct Metadata { #[cfg(feature = "metadata-name")] name: Mutex>>, + #[cfg(feature = "metadata-deadline")] + deadline: raw::Deadline, } impl Metadata { @@ -19,6 +21,10 @@ impl Metadata { Self { #[cfg(feature = "metadata-name")] name: Mutex::new(Cell::new(None)), + // NOTE: The deadline is set to zero to allow the initializer to reside in `.bss`. This + // will be lazily initalized in `initialize_impl` + #[cfg(feature = "metadata-deadline")] + deadline: raw::Deadline::new_unset(), } } @@ -52,4 +58,11 @@ impl Metadata { pub fn set_name(&self, name: &'static str) { critical_section::with(|cs| self.name.borrow(cs).set(Some(name))) } + + /// Earliest Deadline First scheduler Deadline. This field should not be accessed + /// outside the context of the task itself as it being polled by the executor. + #[cfg(feature = "metadata-deadline")] + pub fn deadline(&self) -> &raw::Deadline { + &self.deadline + } } diff --git a/embassy-executor/src/raw/deadline.rs b/embassy-executor/src/raw/deadline.rs index cbb379b82..5b585195d 100644 --- a/embassy-executor/src/raw/deadline.rs +++ b/embassy-executor/src/raw/deadline.rs @@ -62,7 +62,7 @@ impl Deadline { pub fn set_current_task_deadline(instant_ticks: u64) -> impl Future { poll_fn(move |cx| { let task = super::task_from_waker(cx.waker()); - task.header().deadline.set(instant_ticks); + task.header().metadata.deadline().set(instant_ticks); Poll::Ready(()) }) } @@ -87,7 +87,7 @@ impl Deadline { // reasons later. let deadline = now.saturating_add(duration_ticks); - task.header().deadline.set(deadline); + task.header().metadata.deadline().set(deadline); Poll::Ready(Deadline::new(deadline)) }) @@ -109,10 +109,10 @@ impl Deadline { #[must_use = "Setting deadline must be polled to be effective"] pub fn increment_current_task_deadline(increment_ticks: u64) -> impl Future { poll_fn(move |cx| { - let task = super::task_from_waker(cx.waker()); + let task_header = super::task_from_waker(cx.waker()).header(); // Get the last value - let last = task.header().deadline.instant_ticks(); + let last = task_header.metadata.deadline().instant_ticks(); // Since ticks is a u64, saturating add is PROBABLY overly cautious, leave // it for now, we can probably make this wrapping_add for performance @@ -120,7 +120,7 @@ impl Deadline { let deadline = last.saturating_add(increment_ticks); // Store the new value - task.header().deadline.set(deadline); + task_header.metadata.deadline().set(deadline); Poll::Ready(Deadline::new(deadline)) }) @@ -134,7 +134,7 @@ impl Deadline { poll_fn(move |cx| { let task = super::task_from_waker(cx.waker()); - let deadline = task.header().deadline.instant_ticks(); + let deadline = task.header().metadata.deadline().instant_ticks(); Poll::Ready(Self::new(deadline)) }) } @@ -146,12 +146,12 @@ impl Deadline { #[must_use = "Clearing deadline must be polled to be effective"] pub fn clear_current_task_deadline() -> impl Future { poll_fn(move |cx| { - let task = super::task_from_waker(cx.waker()); + let task_header = super::task_from_waker(cx.waker()).header(); // get the old value - let deadline = task.header().deadline.instant_ticks(); + let deadline = task_header.metadata.deadline().instant_ticks(); // Store the default value - task.header().deadline.set(Self::UNSET_DEADLINE_TICKS); + task_header.metadata.deadline().set(Self::UNSET_DEADLINE_TICKS); Poll::Ready(Self::new(deadline)) }) diff --git a/embassy-executor/src/raw/mod.rs b/embassy-executor/src/raw/mod.rs index be2c5ee28..f93bfdef9 100644 --- a/embassy-executor/src/raw/mod.rs +++ b/embassy-executor/src/raw/mod.rs @@ -26,7 +26,7 @@ pub(crate) mod util; #[cfg_attr(feature = "turbowakers", path = "waker_turbo.rs")] mod waker; -#[cfg(feature = "edf-scheduler")] +#[cfg(feature = "metadata-deadline")] mod deadline; use core::future::Future; @@ -43,7 +43,7 @@ use embassy_executor_timer_queue::TimerQueueItem; #[cfg(feature = "arch-avr")] use portable_atomic::AtomicPtr; -#[cfg(feature = "edf-scheduler")] +#[cfg(feature = "metadata-deadline")] pub use deadline::Deadline; #[cfg(feature = "arch-avr")] use portable_atomic::AtomicPtr; @@ -103,11 +103,6 @@ pub(crate) struct TaskHeader { pub(crate) state: State, pub(crate) run_queue_item: RunQueueItem, - /// Earliest Deadline First scheduler Deadline. This field should not be accessed - /// outside the context of the task itself as it being polled by the executor. - #[cfg(feature = "edf-scheduler")] - pub(crate) deadline: Deadline, - pub(crate) executor: AtomicPtr, poll_fn: SyncUnsafeCell>, @@ -212,10 +207,6 @@ impl TaskStorage { raw: TaskHeader { state: State::new(), run_queue_item: RunQueueItem::new(), - // NOTE: The deadline is set to zero to allow the initializer to reside in `.bss`. This - // will be lazily initalized in `initialize_impl` - #[cfg(feature = "edf-scheduler")] - deadline: Deadline::new_unset(), executor: AtomicPtr::new(core::ptr::null_mut()), // Note: this is lazily initialized so that a static `TaskStorage` will go in `.bss` poll_fn: SyncUnsafeCell::new(None), @@ -315,7 +306,11 @@ impl AvailableTask { // By default, deadlines are set to the maximum value, so that any task WITH // a set deadline will ALWAYS be scheduled BEFORE a task WITHOUT a set deadline #[cfg(feature = "edf-scheduler")] - self.task.raw.deadline.set(deadline::Deadline::UNSET_DEADLINE_TICKS); + self.task + .raw + .metadata + .deadline() + .set(deadline::Deadline::UNSET_DEADLINE_TICKS); let task = TaskRef::new(self.task); diff --git a/embassy-executor/src/raw/run_queue.rs b/embassy-executor/src/raw/run_queue.rs index e8a046a48..978ca082a 100644 --- a/embassy-executor/src/raw/run_queue.rs +++ b/embassy-executor/src/raw/run_queue.rs @@ -109,7 +109,10 @@ impl RunQueue { #[cfg(feature = "edf-scheduler")] pub(crate) fn dequeue_all(&self, on_task: impl Fn(TaskRef)) { let mut sorted = SortedList::::new_with_cmp(|lhs, rhs| { - lhs.deadline.instant_ticks().cmp(&rhs.deadline.instant_ticks()) + lhs.metadata + .deadline() + .instant_ticks() + .cmp(&rhs.metadata.deadline().instant_ticks()) }); loop { From 99209accb5b37f236691e060474bfc4823d2e123 Mon Sep 17 00:00:00 2001 From: diondokter Date: Fri, 29 Aug 2025 14:44:32 +0200 Subject: [PATCH 32/76] Add edf example to CI and fix the example --- ci.sh | 1 + examples/nrf52840-edf/Cargo.toml | 12 ++++++------ examples/nrf52840-edf/src/bin/basic.rs | 3 ++- 3 files changed, 9 insertions(+), 7 deletions(-) diff --git a/ci.sh b/ci.sh index 50fb3e13d..a25097690 100755 --- a/ci.sh +++ b/ci.sh @@ -239,6 +239,7 @@ cargo batch \ --- build --release --manifest-path docs/examples/layer-by-layer/blinky-async/Cargo.toml --target thumbv7em-none-eabi \ --- build --release --manifest-path examples/nrf52810/Cargo.toml --target thumbv7em-none-eabi --artifact-dir out/examples/nrf52810 \ --- build --release --manifest-path examples/nrf52840/Cargo.toml --target thumbv7em-none-eabi --artifact-dir out/examples/nrf52840 \ + --- build --release --manifest-path examples/nrf52840-edf/Cargo.toml --target thumbv7em-none-eabi --artifact-dir out/examples/nrf52840-edf \ --- build --release --manifest-path examples/nrf5340/Cargo.toml --target thumbv8m.main-none-eabihf --artifact-dir out/examples/nrf5340 \ --- build --release --manifest-path examples/nrf54l15/Cargo.toml --target thumbv8m.main-none-eabihf --artifact-dir out/examples/nrf54l15 \ --- build --release --manifest-path examples/nrf9160/Cargo.toml --target thumbv8m.main-none-eabihf --artifact-dir out/examples/nrf9160 \ diff --git a/examples/nrf52840-edf/Cargo.toml b/examples/nrf52840-edf/Cargo.toml index c7147d1af..9efdff7c4 100644 --- a/examples/nrf52840-edf/Cargo.toml +++ b/examples/nrf52840-edf/Cargo.toml @@ -6,16 +6,16 @@ license = "MIT OR Apache-2.0" [dependencies] # NOTE: "edf-scheduler" feature is enabled -embassy-executor = { version = "0.7.0", path = "../../embassy-executor", features = ["arch-cortex-m", "executor-thread", "executor-interrupt", "defmt", "edf-scheduler"] } -embassy-time = { version = "0.4.0", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime"] } -embassy-nrf = { version = "0.3.1", path = "../../embassy-nrf", features = ["defmt", "nrf52840", "time-driver-rtc1", "gpiote", "unstable-pac", "time"] } +embassy-executor = { version = "0.9.0", path = "../../embassy-executor", features = ["arch-cortex-m", "executor-thread", "executor-interrupt", "defmt", "edf-scheduler"] } +embassy-time = { version = "0.5.0", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime"] } +embassy-nrf = { version = "0.7.0", path = "../../embassy-nrf", features = ["defmt", "nrf52840", "time-driver-rtc1", "gpiote", "unstable-pac", "time"] } -defmt = "0.3" -defmt-rtt = "0.4" +defmt = "1.0.1" +defmt-rtt = "1.0.0" cortex-m = { version = "0.7.6", features = ["inline-asm", "critical-section-single-core"] } cortex-m-rt = "0.7.0" -panic-probe = { version = "0.3", features = ["print-defmt"] } +panic-probe = { version = "1.0.0", features = ["print-defmt"] } [profile.release] debug = 2 diff --git a/examples/nrf52840-edf/src/bin/basic.rs b/examples/nrf52840-edf/src/bin/basic.rs index c270c67b9..8a8e46449 100644 --- a/examples/nrf52840-edf/src/bin/basic.rs +++ b/examples/nrf52840-edf/src/bin/basic.rs @@ -14,6 +14,7 @@ use core::sync::atomic::{compiler_fence, Ordering}; +use defmt::unwrap; use embassy_executor::raw::Deadline; use embassy_executor::Spawner; use embassy_time::{Duration, Instant, Timer}; @@ -57,7 +58,7 @@ async fn main(spawner: Spawner) { // Spawn all of our load workers! for i in 0..tasks { - spawner.must_spawn(load_task(i, work_time_ticks, time_window)); + spawner.spawn(unwrap!(load_task(i, work_time_ticks, time_window))); } // Let all the tasks spin up From a853bbe2a4dbb64c2e691ddcb258b2530d2b5af5 Mon Sep 17 00:00:00 2001 From: Dion Dokter Date: Fri, 29 Aug 2025 15:16:31 +0200 Subject: [PATCH 33/76] Happy CI :) --- embassy-executor/CHANGELOG.md | 1 + embassy-executor/src/raw/mod.rs | 5 +---- 2 files changed, 2 insertions(+), 4 deletions(-) diff --git a/embassy-executor/CHANGELOG.md b/embassy-executor/CHANGELOG.md index dd462608b..03d60208e 100644 --- a/embassy-executor/CHANGELOG.md +++ b/embassy-executor/CHANGELOG.md @@ -11,6 +11,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Added new metadata API for tasks. - Main task automatically gets a name of `main` when the `metadata-name` feature is enabled. - Upgraded rtos-trace +- Added optional "earliest deadline first" EDF scheduling ## 0.9.1 - 2025-08-31 diff --git a/embassy-executor/src/raw/mod.rs b/embassy-executor/src/raw/mod.rs index f93bfdef9..86ee86842 100644 --- a/embassy-executor/src/raw/mod.rs +++ b/embassy-executor/src/raw/mod.rs @@ -39,12 +39,9 @@ use core::sync::atomic::AtomicPtr; use core::sync::atomic::Ordering; use core::task::{Context, Poll, Waker}; -use embassy_executor_timer_queue::TimerQueueItem; -#[cfg(feature = "arch-avr")] -use portable_atomic::AtomicPtr; - #[cfg(feature = "metadata-deadline")] pub use deadline::Deadline; +use embassy_executor_timer_queue::TimerQueueItem; #[cfg(feature = "arch-avr")] use portable_atomic::AtomicPtr; From d04dc2bc35435602bb153d684dd547b2ffc57fbc Mon Sep 17 00:00:00 2001 From: Dion Dokter Date: Fri, 29 Aug 2025 15:37:00 +0200 Subject: [PATCH 34/76] Add cargo metadata --- examples/nrf52840-edf/Cargo.toml | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/examples/nrf52840-edf/Cargo.toml b/examples/nrf52840-edf/Cargo.toml index 9efdff7c4..4c0d910ea 100644 --- a/examples/nrf52840-edf/Cargo.toml +++ b/examples/nrf52840-edf/Cargo.toml @@ -3,6 +3,7 @@ edition = "2021" name = "embassy-nrf52840-edf-examples" version = "0.1.0" license = "MIT OR Apache-2.0" +publish = false [dependencies] # NOTE: "edf-scheduler" feature is enabled @@ -19,3 +20,8 @@ panic-probe = { version = "1.0.0", features = ["print-defmt"] } [profile.release] debug = 2 + +[package.metadata.embassy] +build = [ + { target = "thumbv7em-none-eabi", artifact-dir = "out/examples/nrf52840-edf" } +] From adb0c3e947dc72027a121a74a700df10fc9e2337 Mon Sep 17 00:00:00 2001 From: Dion Dokter Date: Mon, 1 Sep 2025 11:17:14 +0200 Subject: [PATCH 35/76] Add more metadata --- embassy-executor/Cargo.toml | 1 + 1 file changed, 1 insertion(+) diff --git a/embassy-executor/Cargo.toml b/embassy-executor/Cargo.toml index 2de36d22d..e7136466d 100644 --- a/embassy-executor/Cargo.toml +++ b/embassy-executor/Cargo.toml @@ -24,6 +24,7 @@ build = [ {target = "thumbv7em-none-eabi", features = ["arch-cortex-m", "executor-thread"]}, {target = "thumbv7em-none-eabi", features = ["arch-cortex-m", "executor-interrupt"]}, {target = "thumbv7em-none-eabi", features = ["arch-cortex-m", "executor-interrupt", "executor-thread"]}, + {target = "thumbv7em-none-eabi", features = ["arch-cortex-m", "executor-interrupt", "executor-thread", "edf-scheduler"]}, {target = "armv7a-none-eabi", features = ["arch-cortex-ar", "executor-thread"]}, {target = "armv7r-none-eabi", features = ["arch-cortex-ar", "executor-thread"]}, {target = "armv7r-none-eabihf", features = ["arch-cortex-ar", "executor-thread"]}, From 401fac6ea95b6dd16492d784f99f07fb9a1b318b Mon Sep 17 00:00:00 2001 From: Dion Dokter Date: Mon, 8 Sep 2025 11:40:34 +0200 Subject: [PATCH 36/76] Make requested API changes --- embassy-executor/Cargo.toml | 10 ++- embassy-executor/src/metadata.rs | 64 +++++++++++++-- embassy-executor/src/raw/deadline.rs | 109 -------------------------- embassy-executor/src/raw/mod.rs | 6 +- embassy-executor/src/raw/run_queue.rs | 6 +- 5 files changed, 71 insertions(+), 124 deletions(-) diff --git a/embassy-executor/Cargo.toml b/embassy-executor/Cargo.toml index e7136466d..66352a00e 100644 --- a/embassy-executor/Cargo.toml +++ b/embassy-executor/Cargo.toml @@ -119,8 +119,6 @@ arch-spin = ["_arch"] ## Enable the `name` field in task metadata. metadata-name = ["embassy-executor-macros/metadata-name"] -## Enable the `deadline` field in task metadata. -metadata-deadline = [] #! ### Executor @@ -131,9 +129,13 @@ executor-interrupt = [] ## Enable tracing hooks trace = ["_any_trace"] ## Enable support for rtos-trace framework -rtos-trace = ["_any_trace", "metadata-name", "dep:rtos-trace", "dep:embassy-time-driver"] +rtos-trace = ["_any_trace", "metadata-name", "dep:rtos-trace", "embassy-time-driver"] _any_trace = [] ## Enable "Earliest Deadline First" Scheduler, using soft-realtime "deadlines" to prioritize ## tasks based on the remaining time before their deadline. Adds some overhead. -edf-scheduler = ["dep:embassy-time-driver", "metadata-deadline"] +scheduler-deadline = [] + +## Enable the embassy_time_driver dependency. +## This can unlock extra APIs, for example for the `sheduler-deadline` +embassy-time-driver = ["dep:embassy-time-driver"] diff --git a/embassy-executor/src/metadata.rs b/embassy-executor/src/metadata.rs index fd8095629..81c5afafb 100644 --- a/embassy-executor/src/metadata.rs +++ b/embassy-executor/src/metadata.rs @@ -7,12 +7,14 @@ use core::task::Poll; use critical_section::Mutex; use crate::raw; +#[cfg(feature = "scheduler-deadline")] +use crate::raw::Deadline; /// Metadata associated with a task. pub struct Metadata { #[cfg(feature = "metadata-name")] name: Mutex>>, - #[cfg(feature = "metadata-deadline")] + #[cfg(feature = "scheduler-deadline")] deadline: raw::Deadline, } @@ -23,7 +25,7 @@ impl Metadata { name: Mutex::new(Cell::new(None)), // NOTE: The deadline is set to zero to allow the initializer to reside in `.bss`. This // will be lazily initalized in `initialize_impl` - #[cfg(feature = "metadata-deadline")] + #[cfg(feature = "scheduler-deadline")] deadline: raw::Deadline::new_unset(), } } @@ -59,10 +61,62 @@ impl Metadata { critical_section::with(|cs| self.name.borrow(cs).set(Some(name))) } - /// Earliest Deadline First scheduler Deadline. This field should not be accessed - /// outside the context of the task itself as it being polled by the executor. - #[cfg(feature = "metadata-deadline")] + /// Get this task's deadline. + #[cfg(feature = "scheduler-deadline")] pub fn deadline(&self) -> &raw::Deadline { &self.deadline } + + /// Set this task's deadline. + /// + /// This method does NOT check whether the deadline has already passed. + #[cfg(feature = "scheduler-deadline")] + pub fn set_deadline(&self, instant_ticks: u64) { + self.deadline.set(instant_ticks); + } + + /// Remove this task's deadline. + /// This brings it back to the defaul where it's not scheduled ahead of other tasks. + #[cfg(feature = "scheduler-deadline")] + pub fn unset_deadline(&self) { + self.deadline.set(Deadline::UNSET_DEADLINE_TICKS); + } + + /// Set this task's deadline `duration_ticks` in the future from when + /// this future is polled. This deadline is saturated to the max tick value. + /// + /// Analogous to `Timer::after`. + #[cfg(all(feature = "scheduler-deadline", feature = "embassy-time-driver"))] + pub fn set_deadline_after(&self, duration_ticks: u64) { + let now = embassy_time_driver::now(); + + // Since ticks is a u64, saturating add is PROBABLY overly cautious, leave + // it for now, we can probably make this wrapping_add for performance + // reasons later. + let deadline = now.saturating_add(duration_ticks); + + self.set_deadline(deadline); + } + + /// Set the this task's deadline `increment_ticks` from the previous deadline. + /// + /// This deadline is saturated to the max tick value. + /// + /// Note that by default (unless otherwise set), tasks start life with the deadline + /// not set, which means this method will have no effect. + /// + /// Analogous to one increment of `Ticker::every().next()`. + /// + /// Returns the deadline that was set. + #[cfg(feature = "scheduler-deadline")] + pub fn increment_deadline(&self, duration_ticks: u64) { + let last = self.deadline().instant_ticks(); + + // Since ticks is a u64, saturating add is PROBABLY overly cautious, leave + // it for now, we can probably make this wrapping_add for performance + // reasons later. + let deadline = last.saturating_add(duration_ticks); + + self.set_deadline(deadline); + } } diff --git a/embassy-executor/src/raw/deadline.rs b/embassy-executor/src/raw/deadline.rs index 5b585195d..d08dd06ed 100644 --- a/embassy-executor/src/raw/deadline.rs +++ b/embassy-executor/src/raw/deadline.rs @@ -1,6 +1,4 @@ -use core::future::{poll_fn, Future}; use core::sync::atomic::{AtomicU32, Ordering}; -use core::task::Poll; /// A type for interacting with the deadline of the current task /// @@ -49,111 +47,4 @@ impl Deadline { pub fn is_unset(&self) -> bool { self.instant_ticks() == Self::UNSET_DEADLINE_TICKS } - - /// Set the current task's deadline at exactly `instant_ticks` - /// - /// This method is a future in order to access the currently executing task's - /// header which contains the deadline. - /// - /// Analogous to `Timer::at`. - /// - /// This method does NOT check whether the deadline has already passed. - #[must_use = "Setting deadline must be polled to be effective"] - pub fn set_current_task_deadline(instant_ticks: u64) -> impl Future { - poll_fn(move |cx| { - let task = super::task_from_waker(cx.waker()); - task.header().metadata.deadline().set(instant_ticks); - Poll::Ready(()) - }) - } - - /// Set the current task's deadline `duration_ticks` in the future from when - /// this future is polled. This deadline is saturated to the max tick value. - /// - /// This method is a future in order to access the currently executing task's - /// header which contains the deadline. - /// - /// Analogous to `Timer::after`. - /// - /// Returns the deadline that was set. - #[must_use = "Setting deadline must be polled to be effective"] - pub fn set_current_task_deadline_after(duration_ticks: u64) -> impl Future { - poll_fn(move |cx| { - let task = super::task_from_waker(cx.waker()); - let now = embassy_time_driver::now(); - - // Since ticks is a u64, saturating add is PROBABLY overly cautious, leave - // it for now, we can probably make this wrapping_add for performance - // reasons later. - let deadline = now.saturating_add(duration_ticks); - - task.header().metadata.deadline().set(deadline); - - Poll::Ready(Deadline::new(deadline)) - }) - } - - /// Set the current task's deadline `increment_ticks` from the previous deadline. - /// - /// This deadline is saturated to the max tick value. - /// - /// Note that by default (unless otherwise set), tasks start life with the deadline - /// u64::MAX, which means this method will have no effect. - /// - /// This method is a future in order to access the currently executing task's - /// header which contains the deadline - /// - /// Analogous to one increment of `Ticker::every().next()`. - /// - /// Returns the deadline that was set. - #[must_use = "Setting deadline must be polled to be effective"] - pub fn increment_current_task_deadline(increment_ticks: u64) -> impl Future { - poll_fn(move |cx| { - let task_header = super::task_from_waker(cx.waker()).header(); - - // Get the last value - let last = task_header.metadata.deadline().instant_ticks(); - - // Since ticks is a u64, saturating add is PROBABLY overly cautious, leave - // it for now, we can probably make this wrapping_add for performance - // reasons later. - let deadline = last.saturating_add(increment_ticks); - - // Store the new value - task_header.metadata.deadline().set(deadline); - - Poll::Ready(Deadline::new(deadline)) - }) - } - - /// Get the current task's deadline as a tick value. - /// - /// This method is a future in order to access the currently executing task's - /// header which contains the deadline - pub fn get_current_task_deadline() -> impl Future { - poll_fn(move |cx| { - let task = super::task_from_waker(cx.waker()); - - let deadline = task.header().metadata.deadline().instant_ticks(); - Poll::Ready(Self::new(deadline)) - }) - } - - /// Clear the current task's deadline, returning the previous value. - /// - /// This sets the deadline to the default value of `u64::MAX`, meaning all - /// tasks with set deadlines will be scheduled BEFORE this task. - #[must_use = "Clearing deadline must be polled to be effective"] - pub fn clear_current_task_deadline() -> impl Future { - poll_fn(move |cx| { - let task_header = super::task_from_waker(cx.waker()).header(); - - // get the old value - let deadline = task_header.metadata.deadline().instant_ticks(); - // Store the default value - task_header.metadata.deadline().set(Self::UNSET_DEADLINE_TICKS); - - Poll::Ready(Self::new(deadline)) - }) - } } diff --git a/embassy-executor/src/raw/mod.rs b/embassy-executor/src/raw/mod.rs index 86ee86842..6a9dd9749 100644 --- a/embassy-executor/src/raw/mod.rs +++ b/embassy-executor/src/raw/mod.rs @@ -26,7 +26,7 @@ pub(crate) mod util; #[cfg_attr(feature = "turbowakers", path = "waker_turbo.rs")] mod waker; -#[cfg(feature = "metadata-deadline")] +#[cfg(feature = "scheduler-deadline")] mod deadline; use core::future::Future; @@ -39,7 +39,7 @@ use core::sync::atomic::AtomicPtr; use core::sync::atomic::Ordering; use core::task::{Context, Poll, Waker}; -#[cfg(feature = "metadata-deadline")] +#[cfg(feature = "scheduler-deadline")] pub use deadline::Deadline; use embassy_executor_timer_queue::TimerQueueItem; #[cfg(feature = "arch-avr")] @@ -302,7 +302,7 @@ impl AvailableTask { // By default, deadlines are set to the maximum value, so that any task WITH // a set deadline will ALWAYS be scheduled BEFORE a task WITHOUT a set deadline - #[cfg(feature = "edf-scheduler")] + #[cfg(feature = "scheduler-deadline")] self.task .raw .metadata diff --git a/embassy-executor/src/raw/run_queue.rs b/embassy-executor/src/raw/run_queue.rs index 978ca082a..29c977226 100644 --- a/embassy-executor/src/raw/run_queue.rs +++ b/embassy-executor/src/raw/run_queue.rs @@ -2,7 +2,7 @@ use core::ptr::{addr_of_mut, NonNull}; use cordyceps::sorted_list::Links; use cordyceps::Linked; -#[cfg(feature = "edf-scheduler")] +#[cfg(feature = "scheduler-deadline")] use cordyceps::SortedList; #[cfg(target_has_atomic = "ptr")] @@ -83,7 +83,7 @@ impl RunQueue { /// Empty the queue, then call `on_task` for each task that was in the queue. /// NOTE: It is OK for `on_task` to enqueue more tasks. In this case they're left in the queue /// and will be processed by the *next* call to `dequeue_all`, *not* the current one. - #[cfg(not(feature = "edf-scheduler"))] + #[cfg(not(feature = "scheduler-deadline"))] pub(crate) fn dequeue_all(&self, on_task: impl Fn(TaskRef)) { let taken = self.stack.take_all(); for taskref in taken { @@ -106,7 +106,7 @@ impl RunQueue { /// /// This process will repeat until the local `sorted` queue AND the global /// runqueue are both empty, at which point this function will return. - #[cfg(feature = "edf-scheduler")] + #[cfg(feature = "scheduler-deadline")] pub(crate) fn dequeue_all(&self, on_task: impl Fn(TaskRef)) { let mut sorted = SortedList::::new_with_cmp(|lhs, rhs| { lhs.metadata From 09701a339d9085d86a69bf271299d7b59eda9fdc Mon Sep 17 00:00:00 2001 From: Dion Dokter Date: Mon, 8 Sep 2025 12:33:04 +0200 Subject: [PATCH 37/76] Fix example --- embassy-executor/src/raw/deadline.rs | 2 +- examples/nrf52840-edf/Cargo.toml | 4 ++-- examples/nrf52840-edf/src/bin/basic.rs | 6 +++--- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/embassy-executor/src/raw/deadline.rs b/embassy-executor/src/raw/deadline.rs index d08dd06ed..f6d016ae7 100644 --- a/embassy-executor/src/raw/deadline.rs +++ b/embassy-executor/src/raw/deadline.rs @@ -2,7 +2,7 @@ use core::sync::atomic::{AtomicU32, Ordering}; /// A type for interacting with the deadline of the current task /// -/// Requires the `edf-scheduler` feature. +/// Requires the `scheduler-deadline` feature. /// /// Note: Interacting with the deadline should be done locally in a task. /// In theory you could try to set or read the deadline from another task, diff --git a/examples/nrf52840-edf/Cargo.toml b/examples/nrf52840-edf/Cargo.toml index 4c0d910ea..1e8803233 100644 --- a/examples/nrf52840-edf/Cargo.toml +++ b/examples/nrf52840-edf/Cargo.toml @@ -6,8 +6,8 @@ license = "MIT OR Apache-2.0" publish = false [dependencies] -# NOTE: "edf-scheduler" feature is enabled -embassy-executor = { version = "0.9.0", path = "../../embassy-executor", features = ["arch-cortex-m", "executor-thread", "executor-interrupt", "defmt", "edf-scheduler"] } +# NOTE: "scheduler-deadline" and "embassy-time-driver" features are enabled +embassy-executor = { version = "0.9.0", path = "../../embassy-executor", features = ["arch-cortex-m", "executor-thread", "executor-interrupt", "defmt", "scheduler-deadline", "embassy-time-driver"] } embassy-time = { version = "0.5.0", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime"] } embassy-nrf = { version = "0.7.0", path = "../../embassy-nrf", features = ["defmt", "nrf52840", "time-driver-rtc1", "gpiote", "unstable-pac", "time"] } diff --git a/examples/nrf52840-edf/src/bin/basic.rs b/examples/nrf52840-edf/src/bin/basic.rs index 8a8e46449..d888e17d1 100644 --- a/examples/nrf52840-edf/src/bin/basic.rs +++ b/examples/nrf52840-edf/src/bin/basic.rs @@ -15,7 +15,6 @@ use core::sync::atomic::{compiler_fence, Ordering}; use defmt::unwrap; -use embassy_executor::raw::Deadline; use embassy_executor::Spawner; use embassy_time::{Duration, Instant, Timer}; use {defmt_rtt as _, panic_probe as _}; @@ -127,7 +126,8 @@ async fn main(spawner: Spawner) { let start = Instant::now(); // Set the deadline to ~2x the theoretical time. In practice, setting any deadline // here elevates the current task above all other worker tasks. - Deadline::set_current_task_deadline_after(theoretical * 2).await; + let meta = embassy_executor::Metadata::for_current_task().await; + meta.set_deadline_after(theoretical * 2); // Perform the trial for _ in 0..num_steps { @@ -147,7 +147,7 @@ async fn main(spawner: Spawner) { // Unset the deadline, deadlines are not automatically cleared, and if our // deadline is in the past, then we get very high priority! - Deadline::clear_current_task_deadline().await; + meta.unset_deadline(); Timer::after_millis(500).await; } From 2e21dcf2e61440db8c56a421a87c7a6bd22424d0 Mon Sep 17 00:00:00 2001 From: Dion Dokter Date: Mon, 8 Sep 2025 13:20:47 +0200 Subject: [PATCH 38/76] Fix metadata --- embassy-executor/Cargo.toml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/embassy-executor/Cargo.toml b/embassy-executor/Cargo.toml index 66352a00e..fb4c4d579 100644 --- a/embassy-executor/Cargo.toml +++ b/embassy-executor/Cargo.toml @@ -24,7 +24,7 @@ build = [ {target = "thumbv7em-none-eabi", features = ["arch-cortex-m", "executor-thread"]}, {target = "thumbv7em-none-eabi", features = ["arch-cortex-m", "executor-interrupt"]}, {target = "thumbv7em-none-eabi", features = ["arch-cortex-m", "executor-interrupt", "executor-thread"]}, - {target = "thumbv7em-none-eabi", features = ["arch-cortex-m", "executor-interrupt", "executor-thread", "edf-scheduler"]}, + {target = "thumbv7em-none-eabi", features = ["arch-cortex-m", "executor-interrupt", "executor-thread", "scheduler-deadline", "embassy-time-driver"]}, {target = "armv7a-none-eabi", features = ["arch-cortex-ar", "executor-thread"]}, {target = "armv7r-none-eabi", features = ["arch-cortex-ar", "executor-thread"]}, {target = "armv7r-none-eabihf", features = ["arch-cortex-ar", "executor-thread"]}, @@ -47,7 +47,7 @@ flavors = [ [package.metadata.docs.rs] default-target = "thumbv7em-none-eabi" targets = ["thumbv7em-none-eabi"] -features = ["defmt", "arch-cortex-m", "executor-thread", "executor-interrupt", "edf-scheduler"] +features = ["defmt", "arch-cortex-m", "executor-thread", "executor-interrupt", "scheduler-deadline", "embassy-time-driver"] [dependencies] defmt = { version = "1.0.1", optional = true } From e1209c5563576d18c4d033b015c9a5dd6145d581 Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Thu, 11 Sep 2025 15:40:33 +0200 Subject: [PATCH 39/76] executor: make Deadline actually private. --- embassy-executor/Cargo.toml | 2 +- embassy-executor/src/metadata.rs | 8 ++++---- embassy-executor/src/raw/deadline.rs | 14 ++++---------- embassy-executor/src/raw/mod.rs | 8 ++------ embassy-executor/src/raw/run_queue.rs | 8 ++------ 5 files changed, 13 insertions(+), 27 deletions(-) diff --git a/embassy-executor/Cargo.toml b/embassy-executor/Cargo.toml index fb4c4d579..0ac666f80 100644 --- a/embassy-executor/Cargo.toml +++ b/embassy-executor/Cargo.toml @@ -36,7 +36,7 @@ build = [ [package.metadata.embassy_docs] src_base = "https://github.com/embassy-rs/embassy/blob/embassy-executor-v$VERSION/embassy-executor/src/" src_base_git = "https://github.com/embassy-rs/embassy/blob/$COMMIT/embassy-executor/src/" -features = ["defmt"] +features = ["defmt", "scheduler-deadline"] flavors = [ { name = "std", target = "x86_64-unknown-linux-gnu", features = ["arch-std", "executor-thread"] }, { name = "wasm", target = "wasm32-unknown-unknown", features = ["arch-wasm", "executor-thread"] }, diff --git a/embassy-executor/src/metadata.rs b/embassy-executor/src/metadata.rs index 81c5afafb..4220048a6 100644 --- a/embassy-executor/src/metadata.rs +++ b/embassy-executor/src/metadata.rs @@ -63,8 +63,8 @@ impl Metadata { /// Get this task's deadline. #[cfg(feature = "scheduler-deadline")] - pub fn deadline(&self) -> &raw::Deadline { - &self.deadline + pub fn deadline(&self) -> u64 { + self.deadline.instant_ticks() } /// Set this task's deadline. @@ -79,7 +79,7 @@ impl Metadata { /// This brings it back to the defaul where it's not scheduled ahead of other tasks. #[cfg(feature = "scheduler-deadline")] pub fn unset_deadline(&self) { - self.deadline.set(Deadline::UNSET_DEADLINE_TICKS); + self.deadline.set(Deadline::UNSET_TICKS); } /// Set this task's deadline `duration_ticks` in the future from when @@ -110,7 +110,7 @@ impl Metadata { /// Returns the deadline that was set. #[cfg(feature = "scheduler-deadline")] pub fn increment_deadline(&self, duration_ticks: u64) { - let last = self.deadline().instant_ticks(); + let last = self.deadline(); // Since ticks is a u64, saturating add is PROBABLY overly cautious, leave // it for now, we can probably make this wrapping_add for performance diff --git a/embassy-executor/src/raw/deadline.rs b/embassy-executor/src/raw/deadline.rs index f6d016ae7..cc89fadb0 100644 --- a/embassy-executor/src/raw/deadline.rs +++ b/embassy-executor/src/raw/deadline.rs @@ -7,7 +7,7 @@ use core::sync::atomic::{AtomicU32, Ordering}; /// Note: Interacting with the deadline should be done locally in a task. /// In theory you could try to set or read the deadline from another task, /// but that will result in weird (though not unsound) behavior. -pub struct Deadline { +pub(crate) struct Deadline { instant_ticks_hi: AtomicU32, instant_ticks_lo: AtomicU32, } @@ -21,7 +21,7 @@ impl Deadline { } pub(crate) const fn new_unset() -> Self { - Self::new(Self::UNSET_DEADLINE_TICKS) + Self::new(Self::UNSET_TICKS) } pub(crate) fn set(&self, instant_ticks: u64) { @@ -31,7 +31,7 @@ impl Deadline { } /// Deadline value in ticks, same time base and ticks as `embassy-time` - pub fn instant_ticks(&self) -> u64 { + pub(crate) fn instant_ticks(&self) -> u64 { let hi = self.instant_ticks_hi.load(Ordering::Relaxed) as u64; let lo = self.instant_ticks_lo.load(Ordering::Relaxed) as u64; @@ -40,11 +40,5 @@ impl Deadline { /// Sentinel value representing an "unset" deadline, which has lower priority /// than any other set deadline value - pub const UNSET_DEADLINE_TICKS: u64 = u64::MAX; - - /// Does the given Deadline represent an "unset" deadline? - #[inline] - pub fn is_unset(&self) -> bool { - self.instant_ticks() == Self::UNSET_DEADLINE_TICKS - } + pub(crate) const UNSET_TICKS: u64 = u64::MAX; } diff --git a/embassy-executor/src/raw/mod.rs b/embassy-executor/src/raw/mod.rs index 6a9dd9749..51a363385 100644 --- a/embassy-executor/src/raw/mod.rs +++ b/embassy-executor/src/raw/mod.rs @@ -40,7 +40,7 @@ use core::sync::atomic::Ordering; use core::task::{Context, Poll, Waker}; #[cfg(feature = "scheduler-deadline")] -pub use deadline::Deadline; +pub(crate) use deadline::Deadline; use embassy_executor_timer_queue::TimerQueueItem; #[cfg(feature = "arch-avr")] use portable_atomic::AtomicPtr; @@ -303,11 +303,7 @@ impl AvailableTask { // By default, deadlines are set to the maximum value, so that any task WITH // a set deadline will ALWAYS be scheduled BEFORE a task WITHOUT a set deadline #[cfg(feature = "scheduler-deadline")] - self.task - .raw - .metadata - .deadline() - .set(deadline::Deadline::UNSET_DEADLINE_TICKS); + self.task.raw.metadata.unset_deadline(); let task = TaskRef::new(self.task); diff --git a/embassy-executor/src/raw/run_queue.rs b/embassy-executor/src/raw/run_queue.rs index 29c977226..d98c26f73 100644 --- a/embassy-executor/src/raw/run_queue.rs +++ b/embassy-executor/src/raw/run_queue.rs @@ -108,12 +108,8 @@ impl RunQueue { /// runqueue are both empty, at which point this function will return. #[cfg(feature = "scheduler-deadline")] pub(crate) fn dequeue_all(&self, on_task: impl Fn(TaskRef)) { - let mut sorted = SortedList::::new_with_cmp(|lhs, rhs| { - lhs.metadata - .deadline() - .instant_ticks() - .cmp(&rhs.metadata.deadline().instant_ticks()) - }); + let mut sorted = + SortedList::::new_with_cmp(|lhs, rhs| lhs.metadata.deadline().cmp(&rhs.metadata.deadline())); loop { // For each loop, grab any newly pended items From 6ec9bcb1c4dfbe5fc5365d93e75c516bb03bf9fc Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Thu, 11 Sep 2025 16:15:27 +0200 Subject: [PATCH 40/76] executor: add priority scheduler. --- ci.sh | 6 ++++++ embassy-executor/CHANGELOG.md | 1 + embassy-executor/Cargo.toml | 14 ++++++++++--- embassy-executor/src/metadata.rs | 26 ++++++++++++++++++++++++ embassy-executor/src/raw/mod.rs | 5 ----- embassy-executor/src/raw/run_queue.rs | 29 ++++++++++++++++++++++----- 6 files changed, 68 insertions(+), 13 deletions(-) diff --git a/ci.sh b/ci.sh index a25097690..81e665edf 100755 --- a/ci.sh +++ b/ci.sh @@ -38,6 +38,12 @@ cargo batch \ --- build --release --manifest-path embassy-executor/Cargo.toml --target thumbv7em-none-eabi --features arch-cortex-m,executor-thread \ --- build --release --manifest-path embassy-executor/Cargo.toml --target thumbv7em-none-eabi --features arch-cortex-m,executor-interrupt \ --- build --release --manifest-path embassy-executor/Cargo.toml --target thumbv7em-none-eabi --features arch-cortex-m,executor-thread,executor-interrupt \ + --- build --release --manifest-path embassy-executor/Cargo.toml --target thumbv7em-none-eabi --features arch-cortex-m,executor-interrupt,executor-thread,embassy-time-driver \ + --- build --release --manifest-path embassy-executor/Cargo.toml --target thumbv7em-none-eabi --features arch-cortex-m,executor-interrupt,executor-thread,embassy-time-driver,scheduler-priority \ + --- build --release --manifest-path embassy-executor/Cargo.toml --target thumbv7em-none-eabi --features arch-cortex-m,executor-interrupt,executor-thread,embassy-time-driver,scheduler-priority,scheduler-deadline \ + --- build --release --manifest-path embassy-executor/Cargo.toml --target thumbv7em-none-eabi --features arch-cortex-m,executor-interrupt,executor-thread,embassy-time-driver,scheduler-deadline \ + --- build --release --manifest-path embassy-executor/Cargo.toml --target thumbv7em-none-eabi --features arch-cortex-m,executor-interrupt,executor-thread,scheduler-priority,scheduler-deadline \ + --- build --release --manifest-path embassy-executor/Cargo.toml --target thumbv7em-none-eabi --features arch-cortex-m,executor-interrupt,executor-thread,scheduler-deadline \ --- build --release --manifest-path embassy-executor/Cargo.toml --target armv7a-none-eabi --features arch-cortex-ar,executor-thread \ --- build --release --manifest-path embassy-executor/Cargo.toml --target armv7r-none-eabi --features arch-cortex-ar,executor-thread \ --- build --release --manifest-path embassy-executor/Cargo.toml --target armv7r-none-eabihf --features arch-cortex-ar,executor-thread \ diff --git a/embassy-executor/CHANGELOG.md b/embassy-executor/CHANGELOG.md index 03d60208e..6f079a11a 100644 --- a/embassy-executor/CHANGELOG.md +++ b/embassy-executor/CHANGELOG.md @@ -11,6 +11,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Added new metadata API for tasks. - Main task automatically gets a name of `main` when the `metadata-name` feature is enabled. - Upgraded rtos-trace +- Added optional "highest priority" scheduling - Added optional "earliest deadline first" EDF scheduling ## 0.9.1 - 2025-08-31 diff --git a/embassy-executor/Cargo.toml b/embassy-executor/Cargo.toml index 0ac666f80..d4ea0e6ac 100644 --- a/embassy-executor/Cargo.toml +++ b/embassy-executor/Cargo.toml @@ -24,7 +24,12 @@ build = [ {target = "thumbv7em-none-eabi", features = ["arch-cortex-m", "executor-thread"]}, {target = "thumbv7em-none-eabi", features = ["arch-cortex-m", "executor-interrupt"]}, {target = "thumbv7em-none-eabi", features = ["arch-cortex-m", "executor-interrupt", "executor-thread"]}, - {target = "thumbv7em-none-eabi", features = ["arch-cortex-m", "executor-interrupt", "executor-thread", "scheduler-deadline", "embassy-time-driver"]}, + {target = "thumbv7em-none-eabi", features = ["arch-cortex-m", "executor-interrupt", "executor-thread", "embassy-time-driver"]}, + {target = "thumbv7em-none-eabi", features = ["arch-cortex-m", "executor-interrupt", "executor-thread", "embassy-time-driver", "scheduler-priority"]}, + {target = "thumbv7em-none-eabi", features = ["arch-cortex-m", "executor-interrupt", "executor-thread", "embassy-time-driver", "scheduler-priority", "scheduler-deadline"]}, + {target = "thumbv7em-none-eabi", features = ["arch-cortex-m", "executor-interrupt", "executor-thread", "embassy-time-driver", "scheduler-deadline"]}, + {target = "thumbv7em-none-eabi", features = ["arch-cortex-m", "executor-interrupt", "executor-thread", "scheduler-priority", "scheduler-deadline"]}, + {target = "thumbv7em-none-eabi", features = ["arch-cortex-m", "executor-interrupt", "executor-thread", "scheduler-deadline"]}, {target = "armv7a-none-eabi", features = ["arch-cortex-ar", "executor-thread"]}, {target = "armv7r-none-eabi", features = ["arch-cortex-ar", "executor-thread"]}, {target = "armv7r-none-eabihf", features = ["arch-cortex-ar", "executor-thread"]}, @@ -36,7 +41,7 @@ build = [ [package.metadata.embassy_docs] src_base = "https://github.com/embassy-rs/embassy/blob/embassy-executor-v$VERSION/embassy-executor/src/" src_base_git = "https://github.com/embassy-rs/embassy/blob/$COMMIT/embassy-executor/src/" -features = ["defmt", "scheduler-deadline"] +features = ["defmt", "scheduler-deadline", "scheduler-priority"] flavors = [ { name = "std", target = "x86_64-unknown-linux-gnu", features = ["arch-std", "executor-thread"] }, { name = "wasm", target = "wasm32-unknown-unknown", features = ["arch-wasm", "executor-thread"] }, @@ -47,7 +52,7 @@ flavors = [ [package.metadata.docs.rs] default-target = "thumbv7em-none-eabi" targets = ["thumbv7em-none-eabi"] -features = ["defmt", "arch-cortex-m", "executor-thread", "executor-interrupt", "scheduler-deadline", "embassy-time-driver"] +features = ["defmt", "arch-cortex-m", "executor-thread", "executor-interrupt", "scheduler-deadline", "scheduler-priority", "embassy-time-driver"] [dependencies] defmt = { version = "1.0.1", optional = true } @@ -136,6 +141,9 @@ _any_trace = [] ## tasks based on the remaining time before their deadline. Adds some overhead. scheduler-deadline = [] +## Enable "Highest Priority First" Scheduler. Adds some overhead. +scheduler-priority = [] + ## Enable the embassy_time_driver dependency. ## This can unlock extra APIs, for example for the `sheduler-deadline` embassy-time-driver = ["dep:embassy-time-driver"] diff --git a/embassy-executor/src/metadata.rs b/embassy-executor/src/metadata.rs index 4220048a6..bc0df0f83 100644 --- a/embassy-executor/src/metadata.rs +++ b/embassy-executor/src/metadata.rs @@ -1,6 +1,8 @@ #[cfg(feature = "metadata-name")] use core::cell::Cell; use core::future::{poll_fn, Future}; +#[cfg(feature = "scheduler-priority")] +use core::sync::atomic::{AtomicU8, Ordering}; use core::task::Poll; #[cfg(feature = "metadata-name")] @@ -14,6 +16,8 @@ use crate::raw::Deadline; pub struct Metadata { #[cfg(feature = "metadata-name")] name: Mutex>>, + #[cfg(feature = "scheduler-priority")] + priority: AtomicU8, #[cfg(feature = "scheduler-deadline")] deadline: raw::Deadline, } @@ -23,6 +27,8 @@ impl Metadata { Self { #[cfg(feature = "metadata-name")] name: Mutex::new(Cell::new(None)), + #[cfg(feature = "scheduler-priority")] + priority: AtomicU8::new(0), // NOTE: The deadline is set to zero to allow the initializer to reside in `.bss`. This // will be lazily initalized in `initialize_impl` #[cfg(feature = "scheduler-deadline")] @@ -33,6 +39,14 @@ impl Metadata { pub(crate) fn reset(&self) { #[cfg(feature = "metadata-name")] critical_section::with(|cs| self.name.borrow(cs).set(None)); + + #[cfg(feature = "scheduler-priority")] + self.set_priority(0); + + // By default, deadlines are set to the maximum value, so that any task WITH + // a set deadline will ALWAYS be scheduled BEFORE a task WITHOUT a set deadline + #[cfg(feature = "scheduler-deadline")] + self.unset_deadline(); } /// Get the metadata for the current task. @@ -61,6 +75,18 @@ impl Metadata { critical_section::with(|cs| self.name.borrow(cs).set(Some(name))) } + /// Get this task's priority. + #[cfg(feature = "scheduler-priority")] + pub fn priority(&self) -> u8 { + self.priority.load(Ordering::Relaxed) + } + + /// Set this task's priority. + #[cfg(feature = "scheduler-priority")] + pub fn set_priority(&self, priority: u8) { + self.priority.store(priority, Ordering::Relaxed) + } + /// Get this task's deadline. #[cfg(feature = "scheduler-deadline")] pub fn deadline(&self) -> u64 { diff --git a/embassy-executor/src/raw/mod.rs b/embassy-executor/src/raw/mod.rs index 51a363385..9f36c60bc 100644 --- a/embassy-executor/src/raw/mod.rs +++ b/embassy-executor/src/raw/mod.rs @@ -300,11 +300,6 @@ impl AvailableTask { self.task.raw.poll_fn.set(Some(TaskStorage::::poll)); self.task.future.write_in_place(future); - // By default, deadlines are set to the maximum value, so that any task WITH - // a set deadline will ALWAYS be scheduled BEFORE a task WITHOUT a set deadline - #[cfg(feature = "scheduler-deadline")] - self.task.raw.metadata.unset_deadline(); - let task = TaskRef::new(self.task); SpawnToken::new(task) diff --git a/embassy-executor/src/raw/run_queue.rs b/embassy-executor/src/raw/run_queue.rs index d98c26f73..b8b052310 100644 --- a/embassy-executor/src/raw/run_queue.rs +++ b/embassy-executor/src/raw/run_queue.rs @@ -2,7 +2,7 @@ use core::ptr::{addr_of_mut, NonNull}; use cordyceps::sorted_list::Links; use cordyceps::Linked; -#[cfg(feature = "scheduler-deadline")] +#[cfg(any(feature = "scheduler-priority", feature = "scheduler-deadline"))] use cordyceps::SortedList; #[cfg(target_has_atomic = "ptr")] @@ -83,7 +83,7 @@ impl RunQueue { /// Empty the queue, then call `on_task` for each task that was in the queue. /// NOTE: It is OK for `on_task` to enqueue more tasks. In this case they're left in the queue /// and will be processed by the *next* call to `dequeue_all`, *not* the current one. - #[cfg(not(feature = "scheduler-deadline"))] + #[cfg(not(any(feature = "scheduler-priority", feature = "scheduler-deadline")))] pub(crate) fn dequeue_all(&self, on_task: impl Fn(TaskRef)) { let taken = self.stack.take_all(); for taskref in taken { @@ -106,10 +106,29 @@ impl RunQueue { /// /// This process will repeat until the local `sorted` queue AND the global /// runqueue are both empty, at which point this function will return. - #[cfg(feature = "scheduler-deadline")] + #[cfg(any(feature = "scheduler-priority", feature = "scheduler-deadline"))] pub(crate) fn dequeue_all(&self, on_task: impl Fn(TaskRef)) { - let mut sorted = - SortedList::::new_with_cmp(|lhs, rhs| lhs.metadata.deadline().cmp(&rhs.metadata.deadline())); + let mut sorted = SortedList::::new_with_cmp(|lhs, rhs| { + // compare by priority first + #[cfg(feature = "scheduler-priority")] + { + let lp = lhs.metadata.priority(); + let rp = rhs.metadata.priority(); + if lp != rp { + return lp.cmp(&rp).reverse(); + } + } + // compare deadlines in case of tie. + #[cfg(feature = "scheduler-deadline")] + { + let ld = lhs.metadata.deadline(); + let rd = rhs.metadata.deadline(); + if ld != rd { + return ld.cmp(&rd); + } + } + core::cmp::Ordering::Equal + }); loop { // For each loop, grab any newly pended items From 55b3c5c6e8fb5e55a0e507c43db5d9ef32114f64 Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Thu, 11 Sep 2025 21:27:02 +0200 Subject: [PATCH 41/76] ci: use devtool to build. --- .github/ci/build-nightly.sh | 3 + .github/ci/build-xtensa.sh | 11 +- .github/ci/build.sh | 3 + ci-nightly.sh | 17 +- ci-xtensa.sh | 28 +- ci.sh | 359 +---------------------- embassy-executor/Cargo.toml | 25 ++ embassy-net-wiznet/src/chip/mod.rs | 1 + embassy-net/Cargo.toml | 13 + embassy-sync/Cargo.toml | 2 + embassy-time-queue-utils/Cargo.toml | 3 + embassy-time/Cargo.toml | 2 + examples/mspm0c1104/Cargo.toml | 3 +- examples/nrf52840-rtic/src/bin/blinky.rs | 1 - 14 files changed, 68 insertions(+), 403 deletions(-) diff --git a/.github/ci/build-nightly.sh b/.github/ci/build-nightly.sh index 95cb4100c..2d7c4db3f 100755 --- a/.github/ci/build-nightly.sh +++ b/.github/ci/build-nightly.sh @@ -7,6 +7,7 @@ set -euo pipefail export RUSTUP_HOME=/ci/cache/rustup export CARGO_HOME=/ci/cache/cargo export CARGO_TARGET_DIR=/ci/cache/target +export PATH=$CARGO_HOME/bin:$PATH mv rust-toolchain-nightly.toml rust-toolchain.toml # needed for "dumb HTTP" transport support @@ -22,6 +23,8 @@ fi hashtime restore /ci/cache/filetime.json || true hashtime save /ci/cache/filetime.json +cargo install --git https://github.com/embassy-rs/cargo-embassy-devtool --locked --rev 280829ad163f1444999468a57d28fb7c412babbe + ./ci-nightly.sh # Save lockfiles diff --git a/.github/ci/build-xtensa.sh b/.github/ci/build-xtensa.sh index 103575bc9..b6626639d 100755 --- a/.github/ci/build-xtensa.sh +++ b/.github/ci/build-xtensa.sh @@ -7,13 +7,14 @@ set -euo pipefail export RUSTUP_HOME=/ci/cache/rustup export CARGO_HOME=/ci/cache/cargo export CARGO_TARGET_DIR=/ci/cache/target +export PATH=$CARGO_HOME/bin:$PATH # needed for "dumb HTTP" transport support # used when pointing stm32-metapac to a CI-built one. export CARGO_NET_GIT_FETCH_WITH_CLI=true -cargo install espup -/ci/cache/cargo/bin/espup install --toolchain-version 1.84.0.0 +cargo install espup --locked +espup install --toolchain-version 1.88.0.0 # Restore lockfiles if [ -f /ci/cache/lockfiles.tar ]; then @@ -24,11 +25,7 @@ fi hashtime restore /ci/cache/filetime.json || true hashtime save /ci/cache/filetime.json -mkdir .cargo -cat > .cargo/config.toml<< EOF -[unstable] -build-std = ["alloc", "core"] -EOF +cargo install --git https://github.com/embassy-rs/cargo-embassy-devtool --locked --rev 280829ad163f1444999468a57d28fb7c412babbe ./ci-xtensa.sh diff --git a/.github/ci/build.sh b/.github/ci/build.sh index 68a7c0c34..59bcefed6 100755 --- a/.github/ci/build.sh +++ b/.github/ci/build.sh @@ -7,6 +7,7 @@ set -euo pipefail export RUSTUP_HOME=/ci/cache/rustup export CARGO_HOME=/ci/cache/cargo export CARGO_TARGET_DIR=/ci/cache/target +export PATH=$CARGO_HOME/bin:$PATH if [ -f /ci/secrets/teleprobe-token.txt ]; then echo Got teleprobe token! export TELEPROBE_HOST=https://teleprobe.embassy.dev @@ -27,6 +28,8 @@ fi hashtime restore /ci/cache/filetime.json || true hashtime save /ci/cache/filetime.json +cargo install --git https://github.com/embassy-rs/cargo-embassy-devtool --locked --rev 280829ad163f1444999468a57d28fb7c412babbe + ./ci.sh # Save lockfiles diff --git a/ci-nightly.sh b/ci-nightly.sh index d486d442c..afe9f534c 100755 --- a/ci-nightly.sh +++ b/ci-nightly.sh @@ -8,19 +8,4 @@ if [[ -z "${CARGO_TARGET_DIR}" ]]; then export CARGO_TARGET_DIR=target_ci fi -cargo batch \ - --- build --release --manifest-path embassy-executor/Cargo.toml --target thumbv7em-none-eabi --features nightly \ - --- build --release --manifest-path embassy-executor/Cargo.toml --target thumbv7em-none-eabi --features nightly,log \ - --- build --release --manifest-path embassy-executor/Cargo.toml --target thumbv7em-none-eabi --features nightly,defmt \ - --- build --release --manifest-path embassy-executor/Cargo.toml --target thumbv6m-none-eabi --features nightly,defmt \ - --- build --release --manifest-path embassy-executor/Cargo.toml --target thumbv6m-none-eabi --features nightly,defmt,arch-cortex-m,executor-thread,executor-interrupt \ - --- build --release --manifest-path embassy-executor/Cargo.toml --target thumbv7em-none-eabi --features nightly,arch-cortex-m \ - --- build --release --manifest-path embassy-executor/Cargo.toml --target thumbv7em-none-eabi --features nightly,arch-cortex-m,executor-thread \ - --- build --release --manifest-path embassy-executor/Cargo.toml --target thumbv7em-none-eabi --features nightly,arch-cortex-m,executor-interrupt \ - --- build --release --manifest-path embassy-executor/Cargo.toml --target thumbv7em-none-eabi --features nightly,arch-cortex-m,executor-thread,executor-interrupt \ - --- build --release --manifest-path embassy-executor/Cargo.toml --target riscv32imac-unknown-none-elf --features nightly,arch-riscv32 \ - --- build --release --manifest-path embassy-executor/Cargo.toml --target riscv32imac-unknown-none-elf --features nightly,arch-riscv32,executor-thread \ - --- build --release --manifest-path examples/nrf52840-rtic/Cargo.toml --target thumbv7em-none-eabi --artifact-dir out/examples/nrf52840-rtic \ - --- build --release --manifest-path embassy-executor/Cargo.toml --target armv7a-none-eabi --features nightly,arch-cortex-ar,executor-thread \ - -RUSTFLAGS="$RUSTFLAGS -C target-cpu=atmega328p" cargo build --release --manifest-path embassy-executor/Cargo.toml --target avr-none -Z build-std=core,alloc --features nightly,arch-avr,avr-device/atmega328p +cargo embassy-devtool build --group nightly diff --git a/ci-xtensa.sh b/ci-xtensa.sh index 6c9807e98..0dd41a9ce 100755 --- a/ci-xtensa.sh +++ b/ci-xtensa.sh @@ -9,30 +9,4 @@ if [[ -z "${CARGO_TARGET_DIR}" ]]; then export CARGO_TARGET_DIR=target_ci fi -cargo batch \ - --- build --release --manifest-path embassy-executor/Cargo.toml --target xtensa-esp32-none-elf \ - --- build --release --manifest-path embassy-executor/Cargo.toml --target xtensa-esp32-none-elf --features log \ - --- build --release --manifest-path embassy-executor/Cargo.toml --target xtensa-esp32-none-elf --features defmt \ - --- build --release --manifest-path embassy-executor/Cargo.toml --target xtensa-esp32s2-none-elf --features defmt \ - --- build --release --manifest-path embassy-executor/Cargo.toml --target xtensa-esp32-none-elf --features defmt,arch-spin,executor-thread \ - --- build --release --manifest-path embassy-executor/Cargo.toml --target xtensa-esp32s2-none-elf --features defmt,arch-spin,executor-thread \ - --- build --release --manifest-path embassy-executor/Cargo.toml --target xtensa-esp32s3-none-elf --features defmt,arch-spin,executor-thread \ - --- build --release --manifest-path embassy-executor/Cargo.toml --target xtensa-esp32-none-elf --features arch-spin \ - --- build --release --manifest-path embassy-executor/Cargo.toml --target xtensa-esp32-none-elf --features arch-spin,rtos-trace \ - --- build --release --manifest-path embassy-executor/Cargo.toml --target xtensa-esp32-none-elf --features arch-spin,executor-thread \ - --- build --release --manifest-path embassy-sync/Cargo.toml --target xtensa-esp32s2-none-elf --features defmt \ - --- build --release --manifest-path embassy-time/Cargo.toml --target xtensa-esp32s2-none-elf --features defmt,defmt-timestamp-uptime,mock-driver \ - --- build --release --manifest-path embassy-time-queue-utils/Cargo.toml --target xtensa-esp32s2-none-elf \ - --- build --release --manifest-path embassy-time-queue-utils/Cargo.toml --target xtensa-esp32s2-none-elf --features generic-queue-8 \ - --- build --release --manifest-path embassy-net/Cargo.toml --target xtensa-esp32-none-elf --features defmt,tcp,udp,dns,proto-ipv4,medium-ethernet,packet-trace \ - --- build --release --manifest-path embassy-net/Cargo.toml --target xtensa-esp32-none-elf --features defmt,tcp,udp,dns,proto-ipv4,multicast,medium-ethernet \ - --- build --release --manifest-path embassy-net/Cargo.toml --target xtensa-esp32-none-elf --features defmt,tcp,udp,dns,dhcpv4,medium-ethernet \ - --- build --release --manifest-path embassy-net/Cargo.toml --target xtensa-esp32-none-elf --features defmt,tcp,udp,dns,dhcpv4,medium-ethernet,dhcpv4-hostname \ - --- build --release --manifest-path embassy-net/Cargo.toml --target xtensa-esp32-none-elf --features defmt,tcp,udp,dns,proto-ipv6,medium-ethernet \ - --- build --release --manifest-path embassy-net/Cargo.toml --target xtensa-esp32-none-elf --features defmt,tcp,udp,dns,proto-ipv6,medium-ieee802154 \ - --- build --release --manifest-path embassy-net/Cargo.toml --target xtensa-esp32-none-elf --features defmt,tcp,udp,dns,proto-ipv6,medium-ethernet,medium-ieee802154 \ - --- build --release --manifest-path embassy-net/Cargo.toml --target xtensa-esp32-none-elf --features defmt,tcp,udp,dns,proto-ipv6,medium-ethernet \ - --- build --release --manifest-path embassy-net/Cargo.toml --target xtensa-esp32-none-elf --features defmt,tcp,udp,dns,proto-ipv4,proto-ipv6,medium-ethernet \ - --- build --release --manifest-path embassy-net/Cargo.toml --target xtensa-esp32-none-elf --features defmt,tcp,udp,dns,proto-ipv4,proto-ipv6,medium-ip \ - --- build --release --manifest-path embassy-net/Cargo.toml --target xtensa-esp32-none-elf --features defmt,tcp,udp,dns,proto-ipv4,proto-ipv6,medium-ip,medium-ethernet \ - --- build --release --manifest-path embassy-net/Cargo.toml --target xtensa-esp32-none-elf --features defmt,tcp,udp,dns,proto-ipv4,proto-ipv6,medium-ip,medium-ethernet,medium-ieee802154 \ +cargo embassy-devtool build --group xtensa diff --git a/ci.sh b/ci.sh index 81e665edf..6cc2a031d 100755 --- a/ci.sh +++ b/ci.sh @@ -10,362 +10,21 @@ if ! command -v cargo-batch &> /dev/null; then exit 1 fi +if ! command -v cargo-embassy-devtool &> /dev/null; then + echo "cargo-embassy-devtool could not be found. Install it with the following command:" + echo "" + echo " cargo install --git https://github.com/embassy-rs/cargo-embassy-devtool --locked" + echo "" + exit 1 +fi + export RUSTFLAGS=-Dwarnings export DEFMT_LOG=trace,embassy_hal_internal=debug,embassy_net_esp_hosted=debug,cyw43=info,cyw43_pio=info,smoltcp=info if [[ -z "${CARGO_TARGET_DIR}" ]]; then export CARGO_TARGET_DIR=target_ci fi -TARGET=$(rustc -vV | sed -n 's|host: ||p') - -BUILD_EXTRA="" -if [ $TARGET = "x86_64-unknown-linux-gnu" ] || [ $TARGET = "aarch64-unknown-linux-gnu" ]; then - BUILD_EXTRA="--- build --release --manifest-path examples/std/Cargo.toml --target $TARGET --artifact-dir out/examples/std" -fi - -# CI intentionally does not use -eabihf on thumbv7em to minimize dep compile time. -cargo batch \ - --- build --release --manifest-path embassy-executor/Cargo.toml --target thumbv7em-none-eabi \ - --- build --release --manifest-path embassy-executor/Cargo.toml --target thumbv7em-none-eabi --features log \ - --- build --release --manifest-path embassy-executor/Cargo.toml --target thumbv7em-none-eabi --features defmt \ - --- build --release --manifest-path embassy-executor/Cargo.toml --target thumbv6m-none-eabi --features defmt \ - --- build --release --manifest-path embassy-executor/Cargo.toml --target thumbv6m-none-eabi --features defmt,arch-cortex-m,executor-thread,executor-interrupt \ - --- build --release --manifest-path embassy-executor/Cargo.toml --target thumbv7em-none-eabi --features arch-cortex-m \ - --- build --release --manifest-path embassy-executor/Cargo.toml --target thumbv7em-none-eabi --features arch-cortex-m,metadata-name \ - --- build --release --manifest-path embassy-executor/Cargo.toml --target thumbv7em-none-eabi --features arch-cortex-m,trace \ - --- build --release --manifest-path embassy-executor/Cargo.toml --target thumbv7em-none-eabi --features arch-cortex-m,rtos-trace \ - --- build --release --manifest-path embassy-executor/Cargo.toml --target thumbv7em-none-eabi --features arch-cortex-m,trace,rtos-trace \ - --- build --release --manifest-path embassy-executor/Cargo.toml --target thumbv7em-none-eabi --features arch-cortex-m,executor-thread \ - --- build --release --manifest-path embassy-executor/Cargo.toml --target thumbv7em-none-eabi --features arch-cortex-m,executor-interrupt \ - --- build --release --manifest-path embassy-executor/Cargo.toml --target thumbv7em-none-eabi --features arch-cortex-m,executor-thread,executor-interrupt \ - --- build --release --manifest-path embassy-executor/Cargo.toml --target thumbv7em-none-eabi --features arch-cortex-m,executor-interrupt,executor-thread,embassy-time-driver \ - --- build --release --manifest-path embassy-executor/Cargo.toml --target thumbv7em-none-eabi --features arch-cortex-m,executor-interrupt,executor-thread,embassy-time-driver,scheduler-priority \ - --- build --release --manifest-path embassy-executor/Cargo.toml --target thumbv7em-none-eabi --features arch-cortex-m,executor-interrupt,executor-thread,embassy-time-driver,scheduler-priority,scheduler-deadline \ - --- build --release --manifest-path embassy-executor/Cargo.toml --target thumbv7em-none-eabi --features arch-cortex-m,executor-interrupt,executor-thread,embassy-time-driver,scheduler-deadline \ - --- build --release --manifest-path embassy-executor/Cargo.toml --target thumbv7em-none-eabi --features arch-cortex-m,executor-interrupt,executor-thread,scheduler-priority,scheduler-deadline \ - --- build --release --manifest-path embassy-executor/Cargo.toml --target thumbv7em-none-eabi --features arch-cortex-m,executor-interrupt,executor-thread,scheduler-deadline \ - --- build --release --manifest-path embassy-executor/Cargo.toml --target armv7a-none-eabi --features arch-cortex-ar,executor-thread \ - --- build --release --manifest-path embassy-executor/Cargo.toml --target armv7r-none-eabi --features arch-cortex-ar,executor-thread \ - --- build --release --manifest-path embassy-executor/Cargo.toml --target armv7r-none-eabihf --features arch-cortex-ar,executor-thread \ - --- build --release --manifest-path embassy-executor/Cargo.toml --target riscv32imac-unknown-none-elf --features arch-riscv32 \ - --- build --release --manifest-path embassy-executor/Cargo.toml --target riscv32imac-unknown-none-elf --features arch-riscv32,executor-thread \ - --- build --release --manifest-path embassy-embedded-hal/Cargo.toml --target thumbv7em-none-eabi \ - --- build --release --manifest-path embassy-embedded-hal/Cargo.toml --target thumbv7em-none-eabi --features time \ - --- build --release --manifest-path embassy-sync/Cargo.toml --target thumbv6m-none-eabi --features defmt \ - --- build --release --manifest-path embassy-time/Cargo.toml --target thumbv6m-none-eabi --features defmt,defmt-timestamp-uptime,mock-driver \ - --- build --release --manifest-path embassy-time/Cargo.toml --features defmt,std \ - --- build --release --manifest-path embassy-time-queue-utils/Cargo.toml --target thumbv6m-none-eabi \ - --- build --release --manifest-path embassy-time-queue-utils/Cargo.toml --target thumbv6m-none-eabi --features generic-queue-8 \ - --- build --release --manifest-path embassy-net/Cargo.toml --target thumbv7em-none-eabi --features defmt,tcp,udp,dns,proto-ipv4,medium-ethernet,packet-trace \ - --- build --release --manifest-path embassy-net/Cargo.toml --target thumbv7em-none-eabi --features defmt,tcp,udp,dns,proto-ipv4,multicast,medium-ethernet \ - --- build --release --manifest-path embassy-net/Cargo.toml --target thumbv7em-none-eabi --features defmt,tcp,udp,dns,dhcpv4,medium-ethernet \ - --- build --release --manifest-path embassy-net/Cargo.toml --target thumbv7em-none-eabi --features defmt,tcp,udp,dns,dhcpv4,medium-ethernet,dhcpv4-hostname \ - --- build --release --manifest-path embassy-net/Cargo.toml --target thumbv7em-none-eabi --features defmt,tcp,udp,dns,proto-ipv6,medium-ethernet \ - --- build --release --manifest-path embassy-net/Cargo.toml --target thumbv7em-none-eabi --features defmt,tcp,udp,dns,proto-ipv6,medium-ieee802154 \ - --- build --release --manifest-path embassy-net/Cargo.toml --target thumbv7em-none-eabi --features defmt,tcp,udp,dns,proto-ipv6,medium-ethernet,medium-ieee802154 \ - --- build --release --manifest-path embassy-net/Cargo.toml --target thumbv7em-none-eabi --features defmt,tcp,udp,dns,proto-ipv6,medium-ethernet \ - --- build --release --manifest-path embassy-net/Cargo.toml --target thumbv7em-none-eabi --features defmt,tcp,udp,dns,proto-ipv4,proto-ipv6,medium-ethernet \ - --- build --release --manifest-path embassy-net/Cargo.toml --target thumbv7em-none-eabi --features defmt,tcp,udp,dns,proto-ipv4,proto-ipv6,medium-ip \ - --- build --release --manifest-path embassy-net/Cargo.toml --target thumbv7em-none-eabi --features defmt,tcp,udp,dns,proto-ipv4,proto-ipv6,medium-ip,medium-ethernet \ - --- build --release --manifest-path embassy-net/Cargo.toml --target thumbv7em-none-eabi --features defmt,tcp,udp,dns,proto-ipv4,proto-ipv6,medium-ip,medium-ethernet,medium-ieee802154 \ - --- build --release --manifest-path embassy-imxrt/Cargo.toml --target thumbv8m.main-none-eabihf --features mimxrt633s,defmt,unstable-pac,time,time-driver-rtc \ - --- build --release --manifest-path embassy-imxrt/Cargo.toml --target thumbv8m.main-none-eabihf --features mimxrt685s,defmt,unstable-pac,time,time-driver-rtc \ - --- build --release --manifest-path embassy-nrf/Cargo.toml --target thumbv6m-none-eabi --features nrf51,gpiote,time,time-driver-rtc1 \ - --- build --release --manifest-path embassy-nrf/Cargo.toml --target thumbv7em-none-eabi --features nrf52805,gpiote,time,time-driver-rtc1 \ - --- build --release --manifest-path embassy-nrf/Cargo.toml --target thumbv7em-none-eabi --features nrf52810,gpiote,time,time-driver-rtc1 \ - --- build --release --manifest-path embassy-nrf/Cargo.toml --target thumbv7em-none-eabi --features nrf52811,gpiote,time,time-driver-rtc1 \ - --- build --release --manifest-path embassy-nrf/Cargo.toml --target thumbv7em-none-eabi --features nrf52820,gpiote,time,time-driver-rtc1 \ - --- build --release --manifest-path embassy-nrf/Cargo.toml --target thumbv7em-none-eabi --features nrf52832,gpiote,time,time-driver-rtc1,reset-pin-as-gpio \ - --- build --release --manifest-path embassy-nrf/Cargo.toml --target thumbv7em-none-eabi --features nrf52833,gpiote,time,time-driver-rtc1,nfc-pins-as-gpio \ - --- build --release --manifest-path embassy-nrf/Cargo.toml --target thumbv8m.main-none-eabihf --features nrf9160-s,gpiote,time,time-driver-rtc1 \ - --- build --release --manifest-path embassy-nrf/Cargo.toml --target thumbv8m.main-none-eabihf --features nrf9160-ns,gpiote,time,time-driver-rtc1 \ - --- build --release --manifest-path embassy-nrf/Cargo.toml --target thumbv8m.main-none-eabihf --features nrf5340-app-s,gpiote,time,time-driver-rtc1 \ - --- build --release --manifest-path embassy-nrf/Cargo.toml --target thumbv8m.main-none-eabihf --features nrf5340-app-ns,gpiote,time,time-driver-rtc1 \ - --- build --release --manifest-path embassy-nrf/Cargo.toml --target thumbv8m.main-none-eabihf --features nrf5340-net,gpiote,time,time-driver-rtc1 \ - --- build --release --manifest-path embassy-nrf/Cargo.toml --target thumbv8m.main-none-eabihf --features nrf54l15-app-s,gpiote,time,time-driver-rtc1 \ - --- build --release --manifest-path embassy-nrf/Cargo.toml --target thumbv8m.main-none-eabihf --features nrf54l15-app-ns,gpiote,time,time-driver-rtc1 \ - --- build --release --manifest-path embassy-nrf/Cargo.toml --target thumbv7em-none-eabi --features nrf52840,gpiote,time \ - --- build --release --manifest-path embassy-nrf/Cargo.toml --target thumbv7em-none-eabi --features nrf52840,gpiote,time-driver-rtc1 \ - --- build --release --manifest-path embassy-nrf/Cargo.toml --target thumbv7em-none-eabi --features nrf52840,gpiote,time,time-driver-rtc1 \ - --- build --release --manifest-path embassy-nrf/Cargo.toml --target thumbv7em-none-eabi --features nrf52840,gpiote,log,time \ - --- build --release --manifest-path embassy-nrf/Cargo.toml --target thumbv7em-none-eabi --features nrf52840,gpiote,log,time-driver-rtc1 \ - --- build --release --manifest-path embassy-nrf/Cargo.toml --target thumbv7em-none-eabi --features nrf52840,gpiote,log,time,time-driver-rtc1 \ - --- build --release --manifest-path embassy-nrf/Cargo.toml --target thumbv7em-none-eabi --features nrf52840,gpiote,defmt,time \ - --- build --release --manifest-path embassy-nrf/Cargo.toml --target thumbv7em-none-eabi --features nrf52840,gpiote,defmt,time-driver-rtc1 \ - --- build --release --manifest-path embassy-nrf/Cargo.toml --target thumbv7em-none-eabi --features nrf52840,gpiote,defmt,time,time-driver-rtc1 \ - --- build --release --manifest-path embassy-nrf/Cargo.toml --target thumbv6m-none-eabi --features nrf51,defmt,time,time-driver-rtc1 \ - --- build --release --manifest-path embassy-nrf/Cargo.toml --target thumbv6m-none-eabi --features nrf51,defmt,time \ - --- build --release --manifest-path embassy-nrf/Cargo.toml --target thumbv6m-none-eabi --features nrf51,time \ - --- build --release --manifest-path embassy-rp/Cargo.toml --target thumbv6m-none-eabi --features time-driver,defmt,rp2040 \ - --- build --release --manifest-path embassy-rp/Cargo.toml --target thumbv6m-none-eabi --features time-driver,log,rp2040 \ - --- build --release --manifest-path embassy-rp/Cargo.toml --target thumbv6m-none-eabi --features time-driver,intrinsics,rp2040 \ - --- build --release --manifest-path embassy-rp/Cargo.toml --target thumbv6m-none-eabi --features time-driver,qspi-as-gpio,rp2040 \ - --- build --release --manifest-path embassy-rp/Cargo.toml --target thumbv8m.main-none-eabihf --features time-driver,defmt,rp235xa \ - --- build --release --manifest-path embassy-rp/Cargo.toml --target thumbv8m.main-none-eabihf --features time-driver,log,rp235xa \ - --- build --release --manifest-path embassy-rp/Cargo.toml --target thumbv8m.main-none-eabihf --features time-driver,rp235xa,binary-info \ - --- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv8m.main-none-eabihf --features stm32l552ze,dual-bank,defmt,exti,time-driver-any,time \ - --- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv8m.main-none-eabihf --features stm32l552ze,dual-bank,defmt,time-driver-any,time \ - --- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv8m.main-none-eabihf --features stm32l552ze,dual-bank,defmt,exti,time \ - --- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv8m.main-none-eabihf --features stm32l552ze,dual-bank,defmt,time \ - --- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv8m.main-none-eabihf --features stm32l552ze,dual-bank,defmt,exti,time-driver-any,time \ - --- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv8m.main-none-eabihf --features stm32l552ze,dual-bank,defmt,time-driver-any,time \ - --- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv8m.main-none-eabihf --features stm32l552ze,dual-bank,defmt,exti,time \ - --- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv8m.main-none-eabihf --features stm32l552ze,dual-bank,defmt,time \ - --- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv8m.main-none-eabihf --features stm32l552ze,dual-bank,defmt,exti \ - --- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv8m.main-none-eabihf --features stm32l552ze,dual-bank,defmt \ - --- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv8m.main-none-eabihf --features stm32l552ze,single-bank,defmt \ - --- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv6m-none-eabi --features stm32c071rb,defmt,exti,time-driver-any,time \ - --- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv6m-none-eabi --features stm32c051f6,defmt,exti,time-driver-any,time \ - --- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv6m-none-eabi --features stm32c091gb,defmt,exti,time-driver-any,time \ - --- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv6m-none-eabi --features stm32c092rc,defmt,exti,time-driver-any,time \ - --- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv6m-none-eabi --features stm32f038f6,defmt,exti,time-driver-any,time \ - --- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv6m-none-eabi --features stm32f030c6,defmt,exti,time-driver-any,time \ - --- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv6m-none-eabi --features stm32f058t8,defmt,exti,time-driver-any,time \ - --- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv6m-none-eabi --features stm32f030r8,defmt,exti,time-driver-any,time \ - --- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv6m-none-eabi --features stm32f031k6,defmt,exti,time-driver-any,time \ - --- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv6m-none-eabi --features stm32f030rc,defmt,exti,time-driver-any,time \ - --- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv6m-none-eabi --features stm32f070f6,defmt,exti,time-driver-any,time \ - --- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv6m-none-eabi --features stm32f078vb,defmt,exti,time-driver-any,time \ - --- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv6m-none-eabi --features stm32f042g4,defmt,exti,time-driver-any,time \ - --- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv6m-none-eabi --features stm32f072c8,defmt,exti,time-driver-any,time \ - --- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7em-none-eabi --features stm32f401ve,defmt,exti,time-driver-any \ - --- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7em-none-eabi --features stm32f405zg,defmt,exti,time-driver-any \ - --- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7em-none-eabi --features stm32f407zg,defmt,exti,time-driver-any \ - --- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7em-none-eabi --features stm32f401ve,defmt,exti,time-driver-any,time \ - --- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7em-none-eabi --features stm32f405zg,defmt,exti,time-driver-any,time \ - --- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7em-none-eabi --features stm32f407zg,defmt,exti,time-driver-any,time \ - --- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7em-none-eabi --features stm32f410tb,defmt,exti,time-driver-any,time \ - --- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7em-none-eabi --features stm32f411ce,defmt,exti,time-driver-any,time \ - --- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7em-none-eabi --features stm32f412zg,defmt,exti,time-driver-any,time \ - --- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7em-none-eabi --features stm32f413vh,defmt,exti,time-driver-any,time \ - --- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7em-none-eabi --features stm32f415zg,defmt,exti,time-driver-any,time \ - --- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7em-none-eabi --features stm32f417zg,defmt,exti,time-driver-any,time \ - --- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7em-none-eabi --features stm32f423zh,defmt,exti,time-driver-any,time \ - --- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7em-none-eabi --features stm32f427zi,defmt,exti,time-driver-any,time \ - --- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7em-none-eabi --features stm32f429zi,log,exti,time-driver-any,time \ - --- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7em-none-eabi --features stm32f437zi,log,exti,time-driver-any,time \ - --- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7em-none-eabi --features stm32f439zi,defmt,exti,time-driver-any,time \ - --- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7em-none-eabi --features stm32f446ze,defmt,exti,time-driver-any,time \ - --- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7em-none-eabi --features stm32f469zi,defmt,exti,time-driver-any,time \ - --- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7em-none-eabi --features stm32f479zi,defmt,exti,time-driver-any,time \ - --- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7em-none-eabi --features stm32f730i8,defmt,exti,time-driver-any,time \ - --- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7em-none-eabi --features stm32h753zi,defmt,exti,time-driver-any,time \ - --- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7em-none-eabi --features stm32h735zg,defmt,exti,time-driver-any,time \ - --- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7em-none-eabi --features stm32h755zi-cm7,defmt,exti,time-driver-any,time,split-pc2,split-pc3 \ - --- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7em-none-eabi --features stm32h725re,defmt,exti,time-driver-any,time \ - --- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7em-none-eabi --features stm32h7b3ai,defmt,exti,time-driver-any,time \ - --- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7em-none-eabi --features stm32h7b3ai,defmt,exti,time-driver-tim1,time \ - --- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7em-none-eabi --features stm32h7r3z8,defmt,exti,time-driver-tim1,time \ - --- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7em-none-eabi --features stm32h7r7a8,defmt,exti,time-driver-tim1,time \ - --- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7em-none-eabi --features stm32h7s3a8,defmt,exti,time-driver-tim1,time \ - --- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7em-none-eabi --features stm32h7s7z8,defmt,exti,time-driver-tim1,time \ - --- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7em-none-eabi --features stm32l431cb,defmt,exti,time-driver-any,time \ - --- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7em-none-eabi --features stm32l476vg,defmt,exti,time-driver-any,time \ - --- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7em-none-eabi --features stm32l422cb,defmt,exti,time-driver-any,time \ - --- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7em-none-eabi --features stm32wb15cc,defmt,exti,time-driver-any,time \ - --- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv6m-none-eabi --features stm32l072cz,defmt,exti,time-driver-any,time \ - --- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv6m-none-eabi --features stm32l041f6,defmt,exti,time-driver-any,time \ - --- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv6m-none-eabi --features stm32l051k8,defmt,exti,time-driver-any,time \ - --- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv6m-none-eabi --features stm32l073cz,defmt,exti,time-driver-any,low-power,time \ - --- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7m-none-eabi --features stm32l151cb-a,defmt,exti,time-driver-any,time \ - --- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7em-none-eabi --features stm32f303c8,defmt,exti,time-driver-any,time \ - --- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7em-none-eabi --features stm32f398ve,defmt,exti,time-driver-any,time \ - --- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7em-none-eabi --features stm32f378cc,defmt,exti,time-driver-any,time \ - --- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv6m-none-eabi --features stm32g0b0ce,defmt,exti,time-driver-any,time \ - --- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv6m-none-eabi --features stm32g0c1ve,defmt,exti,time-driver-any,time \ - --- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7m-none-eabi --features stm32f217zg,defmt,exti,time-driver-any,time \ - --- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv8m.main-none-eabihf --features stm32l552ze,dual-bank,defmt,exti,time-driver-any,low-power,time \ - --- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv6m-none-eabi --features stm32wl54jc-cm0p,defmt,exti,time-driver-any,time \ - --- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7em-none-eabi --features stm32wle5jb,defmt,exti,time-driver-any,time \ - --- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7em-none-eabi --features stm32g431kb,defmt,exti,time-driver-any,time \ - --- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7em-none-eabi --features stm32g474pe,dual-bank,defmt,exti,time-driver-any,time \ - --- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7m-none-eabi --features stm32f107vc,defmt,exti,time-driver-any,time \ - --- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7m-none-eabi --features stm32f103re,defmt,exti,time-driver-any,time \ - --- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7m-none-eabi --features stm32f100c4,defmt,exti,time-driver-any,time \ - --- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv8m.main-none-eabihf --features stm32h503rb,defmt,exti,time-driver-any,time \ - --- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv8m.main-none-eabihf --features stm32h523cc,defmt,exti,time-driver-any,time \ - --- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv8m.main-none-eabihf --features stm32h562ag,defmt,exti,time-driver-any,time \ - --- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv8m.main-none-eabihf --features stm32wba50ke,defmt,exti,time-driver-any,time \ - --- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv8m.main-none-eabihf --features stm32wba55ug,defmt,exti,time-driver-any,time \ - --- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv8m.main-none-eabihf --features stm32wba62cg,defmt,exti,time-driver-any,time \ - --- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv8m.main-none-eabihf --features stm32wba65ri,defmt,exti,time-driver-any,low-power,time \ - --- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv8m.main-none-eabihf --features stm32u5f9zj,defmt,exti,time-driver-any,time \ - --- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv8m.main-none-eabihf --features stm32u5g9nj,defmt,exti,time-driver-any,time \ - --- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7em-none-eabi --features stm32wb35ce,defmt,exti,time-driver-any,time \ - --- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7em-none-eabi --features stm32wb55rg,defmt,exti,time-driver-any,low-power,time \ - --- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv6m-none-eabi --features stm32u031r8,defmt,exti,time-driver-any,time \ - --- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv6m-none-eabi --features stm32u073mb,defmt,exti,time-driver-any,time \ - --- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv6m-none-eabi --features stm32u083rc,defmt,exti,time-driver-any,time \ - --- build --release --manifest-path embassy-nxp/Cargo.toml --target thumbv8m.main-none-eabihf --features lpc55,defmt \ - --- build --release --manifest-path embassy-nxp/Cargo.toml --target thumbv7em-none-eabihf --features mimxrt1011,rt,defmt,time-driver-pit \ - --- build --release --manifest-path embassy-nxp/Cargo.toml --target thumbv7em-none-eabihf --features mimxrt1062,rt,defmt,time-driver-pit \ - --- build --release --manifest-path embassy-mspm0/Cargo.toml --target thumbv6m-none-eabi --features mspm0c1104dgs20,rt,defmt,time-driver-any \ - --- build --release --manifest-path embassy-mspm0/Cargo.toml --target thumbv6m-none-eabi --features mspm0c1106rgz,rt,defmt,time-driver-any \ - --- build --release --manifest-path embassy-mspm0/Cargo.toml --target thumbv6m-none-eabi --features mspm0g3507pm,rt,defmt,time-driver-any \ - --- build --release --manifest-path embassy-mspm0/Cargo.toml --target thumbv6m-none-eabi --features mspm0g3519pz,rt,defmt,time-driver-any \ - --- build --release --manifest-path embassy-mspm0/Cargo.toml --target thumbv6m-none-eabi --features mspm0l1306rhb,rt,defmt,time-driver-any \ - --- build --release --manifest-path embassy-mspm0/Cargo.toml --target thumbv6m-none-eabi --features mspm0l2228pn,rt,defmt,time-driver-any \ - --- build --release --manifest-path embassy-mspm0/Cargo.toml --target thumbv6m-none-eabi --features mspm0l1345dgs28,rt,defmt,time-driver-any \ - --- build --release --manifest-path embassy-mspm0/Cargo.toml --target thumbv6m-none-eabi --features mspm0l1106dgs28,rt,defmt,time-driver-any \ - --- build --release --manifest-path embassy-mspm0/Cargo.toml --target thumbv6m-none-eabi --features mspm0l1228pm,rt,defmt,time-driver-any \ - --- build --release --manifest-path embassy-mspm0/Cargo.toml --target thumbv6m-none-eabi --features mspm0g1107ycj,rt,defmt,time-driver-any \ - --- build --release --manifest-path embassy-mspm0/Cargo.toml --target thumbv6m-none-eabi --features mspm0g3105rhb,rt,defmt,time-driver-any \ - --- build --release --manifest-path embassy-mspm0/Cargo.toml --target thumbv6m-none-eabi --features mspm0g1505pt,rt,defmt,time-driver-any \ - --- build --release --manifest-path embassy-mspm0/Cargo.toml --target thumbv6m-none-eabi --features mspm0g1519rhb,rt,defmt,time-driver-any \ - --- build --release --manifest-path cyw43/Cargo.toml --target thumbv6m-none-eabi --features ''\ - --- build --release --manifest-path cyw43/Cargo.toml --target thumbv6m-none-eabi --features 'log' \ - --- build --release --manifest-path cyw43/Cargo.toml --target thumbv6m-none-eabi --features 'defmt' \ - --- build --release --manifest-path cyw43/Cargo.toml --target thumbv6m-none-eabi --features 'log,firmware-logs' \ - --- build --release --manifest-path cyw43/Cargo.toml --target thumbv6m-none-eabi --features 'defmt,firmware-logs' \ - --- build --release --manifest-path cyw43/Cargo.toml --target thumbv6m-none-eabi --features 'log,firmware-logs,bluetooth' \ - --- build --release --manifest-path cyw43/Cargo.toml --target thumbv6m-none-eabi --features 'defmt,firmware-logs,bluetooth' \ - --- build --release --manifest-path cyw43-pio/Cargo.toml --target thumbv6m-none-eabi --features 'embassy-rp/rp2040' \ - --- build --release --manifest-path cyw43-pio/Cargo.toml --target thumbv6m-none-eabi --features 'embassy-rp/rp2040' \ - --- build --release --manifest-path embassy-boot-nrf/Cargo.toml --target thumbv7em-none-eabi --features embassy-nrf/nrf52840 \ - --- build --release --manifest-path embassy-boot-nrf/Cargo.toml --target thumbv8m.main-none-eabihf --features embassy-nrf/nrf5340-app-s \ - --- build --release --manifest-path embassy-boot-nrf/Cargo.toml --target thumbv8m.main-none-eabihf --features embassy-nrf/nrf9160-ns \ - --- build --release --manifest-path embassy-boot-nrf/Cargo.toml --target thumbv8m.main-none-eabihf --features embassy-nrf/nrf9120-ns \ - --- build --release --manifest-path embassy-boot-nrf/Cargo.toml --target thumbv8m.main-none-eabihf --features embassy-nrf/nrf9151-ns \ - --- build --release --manifest-path embassy-boot-nrf/Cargo.toml --target thumbv8m.main-none-eabihf --features embassy-nrf/nrf9161-ns \ - --- build --release --manifest-path embassy-boot-rp/Cargo.toml --target thumbv6m-none-eabi --features embassy-rp/rp2040 \ - --- build --release --manifest-path embassy-boot-stm32/Cargo.toml --target thumbv7em-none-eabi --features embassy-stm32/stm32l496zg \ - --- build --release --manifest-path embassy-usb/Cargo.toml --target thumbv6m-none-eabi --no-default-features \ - --- build --release --manifest-path embassy-usb/Cargo.toml --target thumbv6m-none-eabi \ - --- build --release --manifest-path embassy-usb/Cargo.toml --target thumbv6m-none-eabi --features log \ - --- build --release --manifest-path embassy-usb/Cargo.toml --target thumbv6m-none-eabi --features defmt \ - --- build --release --manifest-path embassy-usb/Cargo.toml --target thumbv6m-none-eabi --features usbd-hid \ - --- build --release --manifest-path embassy-usb/Cargo.toml --target thumbv6m-none-eabi --features max-interface-count-1 \ - --- build --release --manifest-path embassy-usb/Cargo.toml --target thumbv6m-none-eabi --features max-interface-count-8 \ - --- build --release --manifest-path embassy-usb/Cargo.toml --target thumbv6m-none-eabi --features max-handler-count-8 \ - --- build --release --manifest-path embassy-usb/Cargo.toml --target thumbv6m-none-eabi --features max-handler-count-8 \ - --- build --release --manifest-path docs/examples/basic/Cargo.toml --target thumbv7em-none-eabi \ - --- build --release --manifest-path docs/examples/layer-by-layer/blinky-pac/Cargo.toml --target thumbv7em-none-eabi \ - --- build --release --manifest-path docs/examples/layer-by-layer/blinky-hal/Cargo.toml --target thumbv7em-none-eabi \ - --- build --release --manifest-path docs/examples/layer-by-layer/blinky-irq/Cargo.toml --target thumbv7em-none-eabi \ - --- build --release --manifest-path docs/examples/layer-by-layer/blinky-async/Cargo.toml --target thumbv7em-none-eabi \ - --- build --release --manifest-path examples/nrf52810/Cargo.toml --target thumbv7em-none-eabi --artifact-dir out/examples/nrf52810 \ - --- build --release --manifest-path examples/nrf52840/Cargo.toml --target thumbv7em-none-eabi --artifact-dir out/examples/nrf52840 \ - --- build --release --manifest-path examples/nrf52840-edf/Cargo.toml --target thumbv7em-none-eabi --artifact-dir out/examples/nrf52840-edf \ - --- build --release --manifest-path examples/nrf5340/Cargo.toml --target thumbv8m.main-none-eabihf --artifact-dir out/examples/nrf5340 \ - --- build --release --manifest-path examples/nrf54l15/Cargo.toml --target thumbv8m.main-none-eabihf --artifact-dir out/examples/nrf54l15 \ - --- build --release --manifest-path examples/nrf9160/Cargo.toml --target thumbv8m.main-none-eabihf --artifact-dir out/examples/nrf9160 \ - --- build --release --manifest-path examples/nrf9151/s/Cargo.toml --target thumbv8m.main-none-eabihf --artifact-dir out/examples/nrf9151/s \ - --- build --release --manifest-path examples/nrf9151/ns/Cargo.toml --target thumbv8m.main-none-eabihf --artifact-dir out/examples/nrf9151/ns \ - --- build --release --manifest-path examples/nrf51/Cargo.toml --target thumbv6m-none-eabi --artifact-dir out/examples/nrf51 \ - --- build --release --manifest-path examples/rp/Cargo.toml --target thumbv6m-none-eabi --artifact-dir out/examples/rp \ - --- build --release --manifest-path examples/rp235x/Cargo.toml --target thumbv8m.main-none-eabihf --artifact-dir out/examples/rp235x \ - --- build --release --manifest-path examples/stm32f0/Cargo.toml --target thumbv6m-none-eabi --artifact-dir out/examples/stm32f0 \ - --- build --release --manifest-path examples/stm32f1/Cargo.toml --target thumbv7m-none-eabi --artifact-dir out/examples/stm32f1 \ - --- build --release --manifest-path examples/stm32f2/Cargo.toml --target thumbv7m-none-eabi --artifact-dir out/examples/stm32f2 \ - --- build --release --manifest-path examples/stm32f3/Cargo.toml --target thumbv7em-none-eabi --artifact-dir out/examples/stm32f3 \ - --- build --release --manifest-path examples/stm32f334/Cargo.toml --target thumbv7em-none-eabi --artifact-dir out/examples/stm32f334 \ - --- build --release --manifest-path examples/stm32f4/Cargo.toml --target thumbv7em-none-eabi --artifact-dir out/examples/stm32f4 \ - --- build --release --manifest-path examples/stm32f469/Cargo.toml --target thumbv7em-none-eabi --artifact-dir out/examples/stm32f469 \ - --- build --release --manifest-path examples/stm32f7/Cargo.toml --target thumbv7em-none-eabi --artifact-dir out/examples/stm32f7 \ - --- build --release --manifest-path examples/stm32c0/Cargo.toml --target thumbv6m-none-eabi --artifact-dir out/examples/stm32c0 \ - --- build --release --manifest-path examples/stm32g0/Cargo.toml --target thumbv6m-none-eabi --artifact-dir out/examples/stm32g0 \ - --- build --release --manifest-path examples/stm32g4/Cargo.toml --target thumbv7em-none-eabi --artifact-dir out/examples/stm32g4 \ - --- build --release --manifest-path examples/stm32h5/Cargo.toml --target thumbv8m.main-none-eabihf --artifact-dir out/examples/stm32h5 \ - --- build --release --manifest-path examples/stm32h7/Cargo.toml --target thumbv7em-none-eabi --artifact-dir out/examples/stm32h7 \ - --- build --release --manifest-path examples/stm32h7b0/Cargo.toml --target thumbv7em-none-eabi --artifact-dir out/examples/stm32h7b0 \ - --- build --release --manifest-path examples/stm32h723/Cargo.toml --target thumbv7em-none-eabi --artifact-dir out/examples/stm32h723 \ - --- build --release --manifest-path examples/stm32h735/Cargo.toml --target thumbv7em-none-eabi --artifact-dir out/examples/stm32h735 \ - --- build --release --manifest-path examples/stm32h755cm4/Cargo.toml --target thumbv7em-none-eabi --artifact-dir out/examples/stm32h755cm4 \ - --- build --release --manifest-path examples/stm32h755cm7/Cargo.toml --target thumbv7em-none-eabi --artifact-dir out/examples/stm32h755cm7 \ - --- build --release --manifest-path examples/stm32h7rs/Cargo.toml --target thumbv7em-none-eabi --artifact-dir out/examples/stm32h7rs \ - --- build --release --manifest-path examples/stm32l0/Cargo.toml --target thumbv6m-none-eabi --artifact-dir out/examples/stm32l0 \ - --- build --release --manifest-path examples/stm32l1/Cargo.toml --target thumbv7m-none-eabi --artifact-dir out/examples/stm32l1 \ - --- build --release --manifest-path examples/stm32l4/Cargo.toml --target thumbv7em-none-eabi --artifact-dir out/examples/stm32l4 \ - --- build --release --manifest-path examples/stm32l432/Cargo.toml --target thumbv7em-none-eabi --artifact-dir out/examples/stm32l432 \ - --- build --release --manifest-path examples/stm32l5/Cargo.toml --target thumbv8m.main-none-eabihf --artifact-dir out/examples/stm32l5 \ - --- build --release --manifest-path examples/stm32u0/Cargo.toml --target thumbv6m-none-eabi --artifact-dir out/examples/stm32u0 \ - --- build --release --manifest-path examples/stm32u5/Cargo.toml --target thumbv8m.main-none-eabihf --artifact-dir out/examples/stm32u5 \ - --- build --release --manifest-path examples/stm32wb/Cargo.toml --target thumbv7em-none-eabi --artifact-dir out/examples/stm32wb \ - --- build --release --manifest-path examples/stm32wba/Cargo.toml --target thumbv8m.main-none-eabihf --artifact-dir out/examples/stm32wba \ - --- build --release --manifest-path examples/stm32wba6/Cargo.toml --target thumbv8m.main-none-eabihf --artifact-dir out/examples/stm32wba6 \ - --- build --release --manifest-path examples/stm32wl/Cargo.toml --target thumbv7em-none-eabi --artifact-dir out/examples/stm32wl \ - --- build --release --manifest-path examples/lpc55s69/Cargo.toml --target thumbv8m.main-none-eabihf --artifact-dir out/examples/lpc55s69 \ - --- build --release --manifest-path examples/mimxrt1011/Cargo.toml --target thumbv7em-none-eabihf --artifact-dir out/examples/mimxrt1011 \ - --- build --release --manifest-path examples/mimxrt1062-evk/Cargo.toml --target thumbv7em-none-eabihf --artifact-dir out/examples/mimxrt1062-evk \ - --- build --release --manifest-path examples/mspm0g3507/Cargo.toml --target thumbv6m-none-eabi --artifact-dir out/examples/mspm0g3507 \ - --- build --release --manifest-path examples/mspm0g3519/Cargo.toml --target thumbv6m-none-eabi --artifact-dir out/examples/mspm0g3519 \ - --- build --release --manifest-path examples/mspm0l1306/Cargo.toml --target thumbv6m-none-eabi --artifact-dir out/examples/mspm0l1306 \ - --- build --release --manifest-path examples/mspm0l2228/Cargo.toml --target thumbv6m-none-eabi --artifact-dir out/examples/mspm0l2228 \ - --- build --release --manifest-path examples/boot/application/nrf/Cargo.toml --target thumbv7em-none-eabi --features embassy-nrf/nrf52840,skip-include --artifact-dir out/examples/boot/nrf52840 \ - --- build --release --manifest-path examples/boot/application/nrf/Cargo.toml --target thumbv8m.main-none-eabihf --features embassy-nrf/nrf9160-ns,skip-include --artifact-dir out/examples/boot/nrf9160 \ - --- build --release --manifest-path examples/boot/application/nrf/Cargo.toml --target thumbv8m.main-none-eabihf --features embassy-nrf/nrf9120-ns,skip-include --artifact-dir out/examples/boot/nrf9120 \ - --- build --release --manifest-path examples/boot/application/nrf/Cargo.toml --target thumbv8m.main-none-eabihf --features embassy-nrf/nrf9151-ns,skip-include --artifact-dir out/examples/boot/nrf9151 \ - --- build --release --manifest-path examples/boot/application/nrf/Cargo.toml --target thumbv8m.main-none-eabihf --features embassy-nrf/nrf9161-ns,skip-include --artifact-dir out/examples/boot/nrf9161 \ - --- build --release --manifest-path examples/boot/application/rp/Cargo.toml --target thumbv6m-none-eabi --features skip-include --artifact-dir out/examples/boot/rp \ - --- build --release --manifest-path examples/boot/application/stm32f3/Cargo.toml --target thumbv7em-none-eabi --features skip-include --artifact-dir out/examples/boot/stm32f3 \ - --- build --release --manifest-path examples/boot/application/stm32f7/Cargo.toml --target thumbv7em-none-eabi --features skip-include --artifact-dir out/examples/boot/stm32f7 \ - --- build --release --manifest-path examples/boot/application/stm32h7/Cargo.toml --target thumbv7em-none-eabi --features skip-include --artifact-dir out/examples/boot/stm32h7 \ - --- build --release --manifest-path examples/boot/application/stm32l0/Cargo.toml --target thumbv6m-none-eabi --features skip-include --artifact-dir out/examples/boot/stm32l0 \ - --- build --release --manifest-path examples/boot/application/stm32l1/Cargo.toml --target thumbv7m-none-eabi --features skip-include --artifact-dir out/examples/boot/stm32l1 \ - --- build --release --manifest-path examples/boot/application/stm32l4/Cargo.toml --target thumbv7em-none-eabi --features skip-include --artifact-dir out/examples/boot/stm32l4 \ - --- build --release --manifest-path examples/boot/application/stm32wl/Cargo.toml --target thumbv7em-none-eabi --features skip-include --artifact-dir out/examples/boot/stm32wl \ - --- build --release --manifest-path examples/boot/application/stm32wb-dfu/Cargo.toml --target thumbv7em-none-eabi --artifact-dir out/examples/boot/stm32wb-dfu \ - --- build --release --manifest-path examples/boot/application/stm32wba-dfu/Cargo.toml --target thumbv8m.main-none-eabihf --artifact-dir out/examples/boot/stm32wba-dfu \ - --- build --release --manifest-path examples/boot/bootloader/nrf/Cargo.toml --target thumbv7em-none-eabi --features embassy-nrf/nrf52840 \ - --- build --release --manifest-path examples/boot/bootloader/nrf/Cargo.toml --target thumbv8m.main-none-eabihf --features embassy-nrf/nrf9160-ns \ - --- build --release --manifest-path examples/boot/bootloader/nrf/Cargo.toml --target thumbv8m.main-none-eabihf --features embassy-nrf/nrf9120-ns \ - --- build --release --manifest-path examples/boot/bootloader/nrf/Cargo.toml --target thumbv8m.main-none-eabihf --features embassy-nrf/nrf9151-ns \ - --- build --release --manifest-path examples/boot/bootloader/nrf/Cargo.toml --target thumbv8m.main-none-eabihf --features embassy-nrf/nrf9161-ns \ - --- build --release --manifest-path examples/boot/bootloader/rp/Cargo.toml --target thumbv6m-none-eabi \ - --- build --release --manifest-path examples/boot/bootloader/stm32/Cargo.toml --target thumbv7em-none-eabi --features embassy-stm32/stm32l496zg \ - --- build --release --manifest-path examples/boot/bootloader/stm32wb-dfu/Cargo.toml --target thumbv7em-none-eabi --features embassy-stm32/stm32wb55rg \ - --- build --release --manifest-path examples/boot/bootloader/stm32wb-dfu/Cargo.toml --target thumbv7em-none-eabi --features embassy-stm32/stm32wb55rg,verify \ - --- build --release --manifest-path examples/boot/bootloader/stm32wba-dfu/Cargo.toml --target thumbv8m.main-none-eabihf --features embassy-stm32/stm32wba65ri,verify \ - --- build --release --manifest-path examples/boot/bootloader/stm32-dual-bank/Cargo.toml --target thumbv7em-none-eabi --features embassy-stm32/stm32h743zi \ - --- build --release --manifest-path examples/wasm/Cargo.toml --target wasm32-unknown-unknown --artifact-dir out/examples/wasm \ - --- build --release --manifest-path tests/stm32/Cargo.toml --target thumbv7m-none-eabi --features stm32f100rd --artifact-dir out/tests/stm32f100rd \ - --- build --release --manifest-path tests/stm32/Cargo.toml --target thumbv7m-none-eabi --features stm32f103c8 --artifact-dir out/tests/stm32f103c8 \ - --- build --release --manifest-path tests/stm32/Cargo.toml --target thumbv7m-none-eabi --features stm32f107vc --artifact-dir out/tests/stm32f107vc \ - --- build --release --manifest-path tests/stm32/Cargo.toml --target thumbv7em-none-eabi --features stm32f429zi --artifact-dir out/tests/stm32f429zi \ - --- build --release --manifest-path tests/stm32/Cargo.toml --target thumbv7em-none-eabi --features stm32f446re --artifact-dir out/tests/stm32f446re \ - --- build --release --manifest-path tests/stm32/Cargo.toml --target thumbv7em-none-eabi --features stm32g491re --artifact-dir out/tests/stm32g491re \ - --- build --release --manifest-path tests/stm32/Cargo.toml --target thumbv6m-none-eabi --features stm32g071rb --artifact-dir out/tests/stm32g071rb \ - --- build --release --manifest-path tests/stm32/Cargo.toml --target thumbv6m-none-eabi --features stm32c031c6 --artifact-dir out/tests/stm32c031c6 \ - --- build --release --manifest-path tests/stm32/Cargo.toml --target thumbv6m-none-eabi --features stm32c071rb --artifact-dir out/tests/stm32c071rb \ - --- build --release --manifest-path tests/stm32/Cargo.toml --target thumbv7em-none-eabi --features stm32h755zi --artifact-dir out/tests/stm32h755zi \ - --- build --release --manifest-path tests/stm32/Cargo.toml --target thumbv7em-none-eabi --features stm32h753zi --artifact-dir out/tests/stm32h753zi \ - --- build --release --manifest-path tests/stm32/Cargo.toml --target thumbv7em-none-eabi --features stm32h7a3zi --artifact-dir out/tests/stm32h7a3zi \ - --- build --release --manifest-path tests/stm32/Cargo.toml --target thumbv7em-none-eabi --features stm32wb55rg --artifact-dir out/tests/stm32wb55rg \ - --- build --release --manifest-path tests/stm32/Cargo.toml --target thumbv8m.main-none-eabihf --features stm32h563zi --artifact-dir out/tests/stm32h563zi \ - --- build --release --manifest-path tests/stm32/Cargo.toml --target thumbv8m.main-none-eabihf --features stm32u585ai --artifact-dir out/tests/stm32u585ai \ - --- build --release --manifest-path tests/stm32/Cargo.toml --target thumbv8m.main-none-eabihf --features stm32u5a5zj --artifact-dir out/tests/stm32u5a5zj \ - --- build --release --manifest-path tests/stm32/Cargo.toml --target thumbv8m.main-none-eabihf --features stm32wba52cg --artifact-dir out/tests/stm32wba52cg \ - --- build --release --manifest-path tests/stm32/Cargo.toml --target thumbv6m-none-eabi --features stm32l073rz --artifact-dir out/tests/stm32l073rz \ - --- build --release --manifest-path tests/stm32/Cargo.toml --target thumbv7m-none-eabi --features stm32l152re --artifact-dir out/tests/stm32l152re \ - --- build --release --manifest-path tests/stm32/Cargo.toml --target thumbv7em-none-eabi --features stm32l4a6zg --artifact-dir out/tests/stm32l4a6zg \ - --- build --release --manifest-path tests/stm32/Cargo.toml --target thumbv7em-none-eabi --features stm32l4r5zi --artifact-dir out/tests/stm32l4r5zi \ - --- build --release --manifest-path tests/stm32/Cargo.toml --target thumbv8m.main-none-eabihf --features stm32l552ze --artifact-dir out/tests/stm32l552ze \ - --- build --release --manifest-path tests/stm32/Cargo.toml --target thumbv7em-none-eabi --features stm32f767zi --artifact-dir out/tests/stm32f767zi \ - --- build --release --manifest-path tests/stm32/Cargo.toml --target thumbv7m-none-eabi --features stm32f207zg --artifact-dir out/tests/stm32f207zg \ - --- build --release --manifest-path tests/stm32/Cargo.toml --target thumbv7em-none-eabi --features stm32f303ze --artifact-dir out/tests/stm32f303ze \ - --- build --release --manifest-path tests/stm32/Cargo.toml --target thumbv7em-none-eabi --features stm32l496zg --artifact-dir out/tests/stm32l496zg \ - --- build --release --manifest-path tests/stm32/Cargo.toml --target thumbv7em-none-eabi --features stm32wl55jc --artifact-dir out/tests/stm32wl55jc \ - --- build --release --manifest-path tests/stm32/Cargo.toml --target thumbv7em-none-eabi --features stm32h7s3l8 --artifact-dir out/tests/stm32h7s3l8 \ - --- build --release --manifest-path tests/stm32/Cargo.toml --target thumbv6m-none-eabi --features stm32f091rc --artifact-dir out/tests/stm32f091rc \ - --- build --release --manifest-path tests/stm32/Cargo.toml --target thumbv8m.main-none-eabihf --features stm32h503rb --artifact-dir out/tests/stm32h503rb \ - --- build --release --manifest-path tests/stm32/Cargo.toml --target thumbv6m-none-eabi --features stm32u083rc --artifact-dir out/tests/stm32u083rc \ - --- build --release --manifest-path tests/rp/Cargo.toml --target thumbv6m-none-eabi --features rp2040 --artifact-dir out/tests/rpi-pico \ - --- build --release --manifest-path tests/rp/Cargo.toml --target thumbv8m.main-none-eabihf --features rp235xb --artifact-dir out/tests/pimoroni-pico-plus-2 \ - --- build --release --manifest-path tests/nrf/Cargo.toml --target thumbv6m-none-eabi --features nrf51422 --artifact-dir out/tests/nrf51422-dk \ - --- build --release --manifest-path tests/nrf/Cargo.toml --target thumbv7em-none-eabi --features nrf52832 --artifact-dir out/tests/nrf52832-dk \ - --- build --release --manifest-path tests/nrf/Cargo.toml --target thumbv7em-none-eabi --features nrf52833 --artifact-dir out/tests/nrf52833-dk \ - --- build --release --manifest-path tests/nrf/Cargo.toml --target thumbv7em-none-eabi --features nrf52840 --artifact-dir out/tests/nrf52840-dk \ - --- build --release --manifest-path tests/nrf/Cargo.toml --target thumbv8m.main-none-eabihf --features nrf5340 --artifact-dir out/tests/nrf5340-dk \ - --- build --release --manifest-path tests/nrf/Cargo.toml --target thumbv8m.main-none-eabihf --features nrf9160 --artifact-dir out/tests/nrf9160-dk \ - --- build --release --manifest-path tests/mspm0/Cargo.toml --target thumbv6m-none-eabi --features mspm0g3507 --artifact-dir out/tests/mspm0g3507 \ - --- build --release --manifest-path tests/riscv32/Cargo.toml --target riscv32imac-unknown-none-elf \ - $BUILD_EXTRA - - -# MSPM0C1104 must be built seperately since cargo batch does not consider env vars set in `.cargo/config.toml`. -# Since the target has 1KB of ram, we need to limit defmt's buffer size. -DEFMT_RTT_BUFFER_SIZE="72" cargo batch \ - --- build --release --manifest-path examples/mspm0c1104/Cargo.toml --target thumbv6m-none-eabi --artifact-dir out/examples/mspm0c1104 \ +cargo embassy-devtool build # temporarily disabled, these boards are dead. rm -rf out/tests/stm32f103c8 diff --git a/embassy-executor/Cargo.toml b/embassy-executor/Cargo.toml index d4ea0e6ac..f6dce5c0e 100644 --- a/embassy-executor/Cargo.toml +++ b/embassy-executor/Cargo.toml @@ -35,6 +35,31 @@ build = [ {target = "armv7r-none-eabihf", features = ["arch-cortex-ar", "executor-thread"]}, {target = "riscv32imac-unknown-none-elf", features = ["arch-riscv32"]}, {target = "riscv32imac-unknown-none-elf", features = ["arch-riscv32", "executor-thread"]}, + # Nightly builds + {group = "nightly", target = "thumbv7em-none-eabi", features = ["nightly"]}, + {group = "nightly", target = "thumbv7em-none-eabi", features = ["nightly", "log"]}, + {group = "nightly", target = "thumbv7em-none-eabi", features = ["nightly", "defmt"]}, + {group = "nightly", target = "thumbv6m-none-eabi", features = ["nightly", "defmt"]}, + {group = "nightly", target = "thumbv6m-none-eabi", features = ["nightly", "defmt", "arch-cortex-m", "executor-thread", "executor-interrupt"]}, + {group = "nightly", target = "thumbv7em-none-eabi", features = ["nightly", "arch-cortex-m"]}, + {group = "nightly", target = "thumbv7em-none-eabi", features = ["nightly", "arch-cortex-m", "executor-thread"]}, + {group = "nightly", target = "thumbv7em-none-eabi", features = ["nightly", "arch-cortex-m", "executor-interrupt"]}, + {group = "nightly", target = "thumbv7em-none-eabi", features = ["nightly", "arch-cortex-m", "executor-thread", "executor-interrupt"]}, + {group = "nightly", target = "riscv32imac-unknown-none-elf", features = ["nightly", "arch-riscv32"]}, + {group = "nightly", target = "riscv32imac-unknown-none-elf", features = ["nightly", "arch-riscv32", "executor-thread"]}, + {group = "nightly", target = "armv7a-none-eabi", features = ["nightly", "arch-cortex-ar", "executor-thread"]}, + {group = "nightly", target = "avr-none", features = ["nightly", "arch-avr", "avr-device/atmega328p"], build-std = ["core", "alloc"], env = { RUSTFLAGS = "-Ctarget-cpu=atmega328p" }}, + # Xtensa builds + {group = "xtensa", build-std = ["core", "alloc"], target = "xtensa-esp32-none-elf", features = []}, + {group = "xtensa", build-std = ["core", "alloc"], target = "xtensa-esp32-none-elf", features = ["log"]}, + {group = "xtensa", build-std = ["core", "alloc"], target = "xtensa-esp32-none-elf", features = ["defmt"]}, + {group = "xtensa", build-std = ["core", "alloc"], target = "xtensa-esp32s2-none-elf", features = ["defmt"]}, + {group = "xtensa", build-std = ["core", "alloc"], target = "xtensa-esp32-none-elf", features = ["defmt", "arch-spin", "executor-thread"]}, + {group = "xtensa", build-std = ["core", "alloc"], target = "xtensa-esp32s2-none-elf", features = ["defmt", "arch-spin", "executor-thread"]}, + {group = "xtensa", build-std = ["core", "alloc"], target = "xtensa-esp32s3-none-elf", features = ["defmt", "arch-spin", "executor-thread"]}, + {group = "xtensa", build-std = ["core", "alloc"], target = "xtensa-esp32-none-elf", features = ["arch-spin"]}, + {group = "xtensa", build-std = ["core", "alloc"], target = "xtensa-esp32-none-elf", features = ["arch-spin", "rtos-trace"]}, + {group = "xtensa", build-std = ["core", "alloc"], target = "xtensa-esp32-none-elf", features = ["arch-spin", "executor-thread"]}, ] diff --git a/embassy-net-wiznet/src/chip/mod.rs b/embassy-net-wiznet/src/chip/mod.rs index 6e6e5cb78..47d7c5dc3 100644 --- a/embassy-net-wiznet/src/chip/mod.rs +++ b/embassy-net-wiznet/src/chip/mod.rs @@ -30,6 +30,7 @@ pub(crate) trait SealedChip { const SOCKET_RECVD_SIZE: Self::Address; const SOCKET_RX_DATA_READ_PTR: Self::Address; const SOCKET_INTR_MASK: Self::Address; + #[allow(dead_code)] const SOCKET_INTR: Self::Address; const SOCKET_INTR_CLR: Self::Address; diff --git a/embassy-net/Cargo.toml b/embassy-net/Cargo.toml index 245626c14..61a2c858a 100644 --- a/embassy-net/Cargo.toml +++ b/embassy-net/Cargo.toml @@ -26,6 +26,19 @@ build = [ {target = "thumbv7em-none-eabi", features = ["defmt", "dns", "medium-ip", "proto-ipv4", "proto-ipv6", "tcp", "udp"]}, {target = "thumbv7em-none-eabi", features = ["defmt", "dns", "medium-ethernet", "medium-ip", "proto-ipv4", "proto-ipv6", "tcp", "udp"]}, {target = "thumbv7em-none-eabi", features = ["defmt", "dns", "medium-ethernet", "medium-ieee802154", "medium-ip", "proto-ipv4", "proto-ipv6", "tcp", "udp"]}, + # Xtensa builds + {group = "xtensa", build-std = ["core", "alloc"], target = "xtensa-esp32-none-elf", features = ["defmt", "tcp", "udp", "dns", "proto-ipv4", "medium-ethernet", "packet-trace"]}, + {group = "xtensa", build-std = ["core", "alloc"], target = "xtensa-esp32-none-elf", features = ["defmt", "tcp", "udp", "dns", "proto-ipv4", "multicast", "medium-ethernet"]}, + {group = "xtensa", build-std = ["core", "alloc"], target = "xtensa-esp32-none-elf", features = ["defmt", "tcp", "udp", "dns", "dhcpv4", "medium-ethernet"]}, + {group = "xtensa", build-std = ["core", "alloc"], target = "xtensa-esp32-none-elf", features = ["defmt", "tcp", "udp", "dns", "dhcpv4", "medium-ethernet", "dhcpv4-hostname"]}, + {group = "xtensa", build-std = ["core", "alloc"], target = "xtensa-esp32-none-elf", features = ["defmt", "tcp", "udp", "dns", "proto-ipv6", "medium-ethernet"]}, + {group = "xtensa", build-std = ["core", "alloc"], target = "xtensa-esp32-none-elf", features = ["defmt", "tcp", "udp", "dns", "proto-ipv6", "medium-ieee802154"]}, + {group = "xtensa", build-std = ["core", "alloc"], target = "xtensa-esp32-none-elf", features = ["defmt", "tcp", "udp", "dns", "proto-ipv6", "medium-ethernet", "medium-ieee802154"]}, + {group = "xtensa", build-std = ["core", "alloc"], target = "xtensa-esp32-none-elf", features = ["defmt", "tcp", "udp", "dns", "proto-ipv6", "medium-ethernet"]}, + {group = "xtensa", build-std = ["core", "alloc"], target = "xtensa-esp32-none-elf", features = ["defmt", "tcp", "udp", "dns", "proto-ipv4", "proto-ipv6", "medium-ethernet"]}, + {group = "xtensa", build-std = ["core", "alloc"], target = "xtensa-esp32-none-elf", features = ["defmt", "tcp", "udp", "dns", "proto-ipv4", "proto-ipv6", "medium-ip"]}, + {group = "xtensa", build-std = ["core", "alloc"], target = "xtensa-esp32-none-elf", features = ["defmt", "tcp", "udp", "dns", "proto-ipv4", "proto-ipv6", "medium-ip", "medium-ethernet"]}, + {group = "xtensa", build-std = ["core", "alloc"], target = "xtensa-esp32-none-elf", features = ["defmt", "tcp", "udp", "dns", "proto-ipv4", "proto-ipv6", "medium-ip", "medium-ethernet", "medium-ieee802154"]}, ] [package.metadata.embassy_docs] diff --git a/embassy-sync/Cargo.toml b/embassy-sync/Cargo.toml index 30a27c13f..6494da727 100644 --- a/embassy-sync/Cargo.toml +++ b/embassy-sync/Cargo.toml @@ -17,6 +17,8 @@ categories = [ [package.metadata.embassy] build = [ {target = "thumbv6m-none-eabi", features = ["defmt"]}, + # Xtensa builds + {group = "xtensa", build-std = ["core", "alloc"], target = "xtensa-esp32s2-none-elf", features = ["defmt"]}, ] [package.metadata.embassy_docs] diff --git a/embassy-time-queue-utils/Cargo.toml b/embassy-time-queue-utils/Cargo.toml index e1abf1cd8..13da62874 100644 --- a/embassy-time-queue-utils/Cargo.toml +++ b/embassy-time-queue-utils/Cargo.toml @@ -56,6 +56,9 @@ _generic-queue = [] build = [ {target = "thumbv6m-none-eabi", features = []}, {target = "thumbv6m-none-eabi", features = ["generic-queue-8"]}, + # Xtensa builds + {group = "xtensa", build-std = ["core", "alloc"], target = "xtensa-esp32s2-none-elf", features = []}, + {group = "xtensa", build-std = ["core", "alloc"], target = "xtensa-esp32s2-none-elf", features = ["generic-queue-8"]}, ] [package.metadata.embassy_docs] diff --git a/embassy-time/Cargo.toml b/embassy-time/Cargo.toml index 6ebf0a468..2d7c3c1fa 100644 --- a/embassy-time/Cargo.toml +++ b/embassy-time/Cargo.toml @@ -17,6 +17,8 @@ categories = [ [package.metadata.embassy] build = [ {target = "thumbv6m-none-eabi", features = ["defmt", "defmt-timestamp-uptime", "mock-driver"]}, + # Xtensa builds + {group = "xtensa", build-std = ["core", "alloc"], target = "xtensa-esp32s2-none-elf", features = ["defmt", "defmt-timestamp-uptime", "mock-driver"]}, ] [package.metadata.embassy_docs] diff --git a/examples/mspm0c1104/Cargo.toml b/examples/mspm0c1104/Cargo.toml index 4daddbbb4..21434106a 100644 --- a/examples/mspm0c1104/Cargo.toml +++ b/examples/mspm0c1104/Cargo.toml @@ -33,7 +33,6 @@ lto = true codegen-units = 1 [package.metadata.embassy] -skip = true # TODO: remove when we find a way to decrease the defmt buffer size in ci. build = [ - { target = "thumbv6m-none-eabi", artifact-dir = "out/examples/mspm0c1104" } + { target = "thumbv6m-none-eabi", artifact-dir = "out/examples/mspm0c1104", env = { DEFMT_RTT_BUFFER_SIZE = "72" }} ] diff --git a/examples/nrf52840-rtic/src/bin/blinky.rs b/examples/nrf52840-rtic/src/bin/blinky.rs index 719e22729..2adac7e0a 100644 --- a/examples/nrf52840-rtic/src/bin/blinky.rs +++ b/examples/nrf52840-rtic/src/bin/blinky.rs @@ -1,6 +1,5 @@ #![no_std] #![no_main] -#![feature(type_alias_impl_trait)] use {defmt_rtt as _, panic_probe as _}; From 19bb3668669710dbd1d17dc5fb0dee6d915f012e Mon Sep 17 00:00:00 2001 From: Roi Bachynskyi Date: Sat, 30 Aug 2025 14:25:25 +0300 Subject: [PATCH 42/76] lpc55: gpio rewritten --- embassy-nxp/Cargo.toml | 6 +- embassy-nxp/src/chips/lpc55.rs | 2 +- embassy-nxp/src/gpio/lpc55.rs | 360 ++++----------------------------- 3 files changed, 44 insertions(+), 324 deletions(-) diff --git a/embassy-nxp/Cargo.toml b/embassy-nxp/Cargo.toml index ab0bfbfd7..c66354ee2 100644 --- a/embassy-nxp/Cargo.toml +++ b/embassy-nxp/Cargo.toml @@ -37,13 +37,13 @@ embedded-io = "0.6.1" embedded-hal-02 = { package = "embedded-hal", version = "0.2.6", features = ["unproven"] } ## Chip dependencies lpc55-pac = { version = "0.5.0", optional = true } -nxp-pac = { version = "0.1.0", optional = true, git = "https://github.com/i509VCB/nxp-pac", rev = "be4dd0936c20d5897364a381b1d95a99514c1e7e" } +nxp-pac = { version = "0.1.0", optional = true, git = "https://github.com/IrinaCh524/nxp-pac", rev = "d452e366105012ddd8ba332a9ab86e1931508235" } imxrt-rt = { version = "0.1.7", optional = true, features = ["device"] } [build-dependencies] cfg_aliases = "0.2.1" -nxp-pac = { version = "0.1.0", git = "https://github.com/i509VCB/nxp-pac", rev = "be4dd0936c20d5897364a381b1d95a99514c1e7e", features = ["metadata"], optional = true } +nxp-pac = { version = "0.1.0", git = "https://github.com/IrinaCh524/nxp-pac", rev = "d452e366105012ddd8ba332a9ab86e1931508235", features = ["metadata"], optional = true } proc-macro2 = "1.0.95" quote = "1.0.15" @@ -79,6 +79,6 @@ _rt1xxx = [] _time_driver = ["dep:embassy-time-driver", "dep:embassy-time-queue-utils"] #! ### Chip selection features -lpc55 = ["dep:lpc55-pac"] +lpc55 = ["nxp-pac/lpc55s69_core0"] mimxrt1011 = ["nxp-pac/mimxrt1011", "_rt1xxx", "dep:imxrt-rt"] mimxrt1062 = ["nxp-pac/mimxrt1062", "_rt1xxx", "dep:imxrt-rt"] diff --git a/embassy-nxp/src/chips/lpc55.rs b/embassy-nxp/src/chips/lpc55.rs index e168ced00..711bff3e7 100644 --- a/embassy-nxp/src/chips/lpc55.rs +++ b/embassy-nxp/src/chips/lpc55.rs @@ -1,4 +1,4 @@ -pub use lpc55_pac as pac; +pub use nxp_pac as pac; embassy_hal_internal::peripherals! { // External pins. These are not only GPIOs, they are multi-purpose pins and can be used by other diff --git a/embassy-nxp/src/gpio/lpc55.rs b/embassy-nxp/src/gpio/lpc55.rs index 8f407bb3a..36ea99d21 100644 --- a/embassy-nxp/src/gpio/lpc55.rs +++ b/embassy-nxp/src/gpio/lpc55.rs @@ -1,12 +1,17 @@ use embassy_hal_internal::{impl_peripheral, PeripheralType}; +use crate::pac::iocon::vals::{PioDigimode, PioMode}; +use crate::pac::{GPIO, IOCON, SYSCON}; use crate::{peripherals, Peri}; pub(crate) fn init() { // Enable clocks for GPIO, PINT, and IOCON - syscon_reg() - .ahbclkctrl0 - .modify(|_, w| w.gpio0().enable().gpio1().enable().mux().enable().iocon().enable()); + SYSCON.ahbclkctrl0().modify(|w| { + w.set_gpio0(true); + w.set_gpio1(true); + w.set_mux(true); + w.set_iocon(true); + }); info!("GPIO initialized"); } @@ -59,21 +64,24 @@ impl<'d> Output<'d> { } pub fn set_high(&mut self) { - gpio_reg().set[self.pin.pin_bank() as usize].write(|w| unsafe { w.bits(self.pin.bit()) }) + GPIO.set(self.pin.pin_bank() as usize) + .write(|w| w.set_setp(self.pin.bit())); } pub fn set_low(&mut self) { - gpio_reg().clr[self.pin.pin_bank() as usize].write(|w| unsafe { w.bits(self.pin.bit()) }) + GPIO.clr(self.pin.pin_bank() as usize) + .write(|w| w.set_clrp(self.pin.bit())); } pub fn toggle(&mut self) { - gpio_reg().not[self.pin.pin_bank() as usize].write(|w| unsafe { w.bits(self.pin.bit()) }) + GPIO.not(self.pin.pin_bank() as usize) + .write(|w| w.set_notp(self.pin.bit())); } /// Get the current output level of the pin. Note that the value returned by this function is /// the voltage level reported by the pin, not the value set by the output driver. pub fn level(&self) -> Level { - let bits = gpio_reg().pin[self.pin.pin_bank() as usize].read().bits(); + let bits = GPIO.pin(self.pin.pin_bank() as usize).read().port(); if bits & self.pin.bit() != 0 { Level::High } else { @@ -101,18 +109,18 @@ impl<'d> Input<'d> { /// Set the pull configuration for the pin. To disable the pull, use [Pull::None]. pub fn set_pull(&mut self, pull: Pull) { - match_iocon!(register, iocon_reg(), self.pin.pin_bank(), self.pin.pin_number(), { - register.modify(|_, w| match pull { - Pull::None => w.mode().inactive(), - Pull::Up => w.mode().pull_up(), - Pull::Down => w.mode().pull_down(), + match_iocon!(register, self.pin.pin_bank(), self.pin.pin_number(), { + register.modify(|w| match pull { + Pull::None => w.set_mode(PioMode::INACTIVE), + Pull::Up => w.set_mode(PioMode::PULL_UP), + Pull::Down => w.set_mode(PioMode::PULL_DOWN), }); }); } /// Get the current input level of the pin. pub fn read(&self) -> Level { - let bits = gpio_reg().pin[self.pin.pin_bank() as usize].read().bits(); + let bits = GPIO.pin(self.pin.pin_bank() as usize).read().port(); if bits & self.pin.bit() != 0 { Level::High } else { @@ -188,8 +196,8 @@ impl<'d> Flex<'d> { /// Set the pin to digital mode. This is required for using a pin as a GPIO pin. The default /// setting for pins is (usually) non-digital. fn set_as_digital(&mut self) { - match_iocon!(register, iocon_reg(), self.pin_bank(), self.pin_number(), { - register.modify(|_, w| w.digimode().digital()); + match_iocon!(register, self.pin_bank(), self.pin_number(), { + register.modify(|w| w.set_digimode(PioDigimode::DIGITAL)); }); } @@ -197,12 +205,14 @@ impl<'d> Flex<'d> { /// function handles itself. pub fn set_as_output(&mut self) { self.set_as_digital(); - gpio_reg().dirset[self.pin.pin_bank() as usize].write(|w| unsafe { w.dirsetp().bits(self.bit()) }) + GPIO.dirset(self.pin.pin_bank() as usize) + .write(|w| w.set_dirsetp(self.bit())) } pub fn set_as_input(&mut self) { self.set_as_digital(); - gpio_reg().dirclr[self.pin.pin_bank() as usize].write(|w| unsafe { w.dirclrp().bits(self.bit()) }) + GPIO.dirclr(self.pin.pin_bank() as usize) + .write(|w| w.set_dirclrp(self.bit())) } } @@ -262,52 +272,6 @@ impl SealedPin for AnyPin { } } -/// Get the GPIO register block. This is used to configure all GPIO pins. -/// -/// # Safety -/// Due to the type system of peripherals, access to the settings of a single pin is possible only -/// by a single thread at a time. Read/Write operations on a single registers are NOT atomic. You -/// must ensure that the GPIO registers are not accessed concurrently by multiple threads. -pub(crate) fn gpio_reg() -> &'static lpc55_pac::gpio::RegisterBlock { - unsafe { &*lpc55_pac::GPIO::ptr() } -} - -/// Get the IOCON register block. -/// -/// # Safety -/// Read/Write operations on a single registers are NOT atomic. You must ensure that the GPIO -/// registers are not accessed concurrently by multiple threads. -pub(crate) fn iocon_reg() -> &'static lpc55_pac::iocon::RegisterBlock { - unsafe { &*lpc55_pac::IOCON::ptr() } -} - -/// Get the INPUTMUX register block. -/// -/// # Safety -/// Read/Write operations on a single registers are NOT atomic. You must ensure that the GPIO -/// registers are not accessed concurrently by multiple threads. -pub(crate) fn inputmux_reg() -> &'static lpc55_pac::inputmux::RegisterBlock { - unsafe { &*lpc55_pac::INPUTMUX::ptr() } -} - -/// Get the SYSCON register block. -/// -/// # Safety -/// Read/Write operations on a single registers are NOT atomic. You must ensure that the GPIO -/// registers are not accessed concurrently by multiple threads. -pub(crate) fn syscon_reg() -> &'static lpc55_pac::syscon::RegisterBlock { - unsafe { &*lpc55_pac::SYSCON::ptr() } -} - -/// Get the PINT register block. -/// -/// # Safety -/// Read/Write operations on a single registers are NOT atomic. You must ensure that the GPIO -/// registers are not accessed concurrently by multiple threads. -pub(crate) fn pint_reg() -> &'static lpc55_pac::pint::RegisterBlock { - unsafe { &*lpc55_pac::PINT::ptr() } -} - /// Match the pin bank and number of a pin to the corresponding IOCON register. /// /// # Example @@ -316,270 +280,26 @@ pub(crate) fn pint_reg() -> &'static lpc55_pac::pint::RegisterBlock { /// use embassy_nxp::pac_utils::{iocon_reg, match_iocon}; /// /// // Make pin PIO1_6 digital and set it to pull-down mode. -/// match_iocon!(register, iocon_reg(), Bank::Bank1, 6, { -/// register.modify(|_, w| w.mode().pull_down().digimode().digital()); +/// match_iocon!(register, Bank::Bank1, 6, { +/// register.modify(|w|{ +/// w.set_mode(PioMode::PULL_DOWN); +/// w.set_digimode(PioDigimode::DIGITAL); +/// +/// } /// }); /// ``` macro_rules! match_iocon { - ($register:ident, $iocon_register:expr, $pin_bank:expr, $pin_number:expr, $action:expr) => { - match ($pin_bank, $pin_number) { - (Bank::Bank0, 0) => { - let $register = &($iocon_register).pio0_0; + ($register:ident, $pin_bank:expr, $pin_number:expr, $action:expr) => { + match $pin_bank { + Bank::Bank0 => { + let $register = IOCON.pio0($pin_number as usize); $action; } - (Bank::Bank0, 1) => { - let $register = &($iocon_register).pio0_1; + + Bank::Bank1 => { + let $register = IOCON.pio1($pin_number as usize); $action; } - (Bank::Bank0, 2) => { - let $register = &($iocon_register).pio0_2; - $action; - } - (Bank::Bank0, 3) => { - let $register = &($iocon_register).pio0_3; - $action; - } - (Bank::Bank0, 4) => { - let $register = &($iocon_register).pio0_4; - $action; - } - (Bank::Bank0, 5) => { - let $register = &($iocon_register).pio0_5; - $action; - } - (Bank::Bank0, 6) => { - let $register = &($iocon_register).pio0_6; - $action; - } - (Bank::Bank0, 7) => { - let $register = &($iocon_register).pio0_7; - $action; - } - (Bank::Bank0, 8) => { - let $register = &($iocon_register).pio0_8; - $action; - } - (Bank::Bank0, 9) => { - let $register = &($iocon_register).pio0_9; - $action; - } - (Bank::Bank0, 10) => { - let $register = &($iocon_register).pio0_10; - $action; - } - (Bank::Bank0, 11) => { - let $register = &($iocon_register).pio0_11; - $action; - } - (Bank::Bank0, 12) => { - let $register = &($iocon_register).pio0_12; - $action; - } - (Bank::Bank0, 13) => { - let $register = &($iocon_register).pio0_13; - $action; - } - (Bank::Bank0, 14) => { - let $register = &($iocon_register).pio0_14; - $action; - } - (Bank::Bank0, 15) => { - let $register = &($iocon_register).pio0_15; - $action; - } - (Bank::Bank0, 16) => { - let $register = &($iocon_register).pio0_16; - $action; - } - (Bank::Bank0, 17) => { - let $register = &($iocon_register).pio0_17; - $action; - } - (Bank::Bank0, 18) => { - let $register = &($iocon_register).pio0_18; - $action; - } - (Bank::Bank0, 19) => { - let $register = &($iocon_register).pio0_19; - $action; - } - (Bank::Bank0, 20) => { - let $register = &($iocon_register).pio0_20; - $action; - } - (Bank::Bank0, 21) => { - let $register = &($iocon_register).pio0_21; - $action; - } - (Bank::Bank0, 22) => { - let $register = &($iocon_register).pio0_22; - $action; - } - (Bank::Bank0, 23) => { - let $register = &($iocon_register).pio0_23; - $action; - } - (Bank::Bank0, 24) => { - let $register = &($iocon_register).pio0_24; - $action; - } - (Bank::Bank0, 25) => { - let $register = &($iocon_register).pio0_25; - $action; - } - (Bank::Bank0, 26) => { - let $register = &($iocon_register).pio0_26; - $action; - } - (Bank::Bank0, 27) => { - let $register = &($iocon_register).pio0_27; - $action; - } - (Bank::Bank0, 28) => { - let $register = &($iocon_register).pio0_28; - $action; - } - (Bank::Bank0, 29) => { - let $register = &($iocon_register).pio0_29; - $action; - } - (Bank::Bank0, 30) => { - let $register = &($iocon_register).pio0_30; - $action; - } - (Bank::Bank0, 31) => { - let $register = &($iocon_register).pio0_31; - $action; - } - (Bank::Bank1, 0) => { - let $register = &($iocon_register).pio1_0; - $action; - } - (Bank::Bank1, 1) => { - let $register = &($iocon_register).pio1_1; - $action; - } - (Bank::Bank1, 2) => { - let $register = &($iocon_register).pio1_2; - $action; - } - (Bank::Bank1, 3) => { - let $register = &($iocon_register).pio1_3; - $action; - } - (Bank::Bank1, 4) => { - let $register = &($iocon_register).pio1_4; - $action; - } - (Bank::Bank1, 5) => { - let $register = &($iocon_register).pio1_5; - $action; - } - (Bank::Bank1, 6) => { - let $register = &($iocon_register).pio1_6; - $action; - } - (Bank::Bank1, 7) => { - let $register = &($iocon_register).pio1_7; - $action; - } - (Bank::Bank1, 8) => { - let $register = &($iocon_register).pio1_8; - $action; - } - (Bank::Bank1, 9) => { - let $register = &($iocon_register).pio1_9; - $action; - } - (Bank::Bank1, 10) => { - let $register = &($iocon_register).pio1_10; - $action; - } - (Bank::Bank1, 11) => { - let $register = &($iocon_register).pio1_11; - $action; - } - (Bank::Bank1, 12) => { - let $register = &($iocon_register).pio1_12; - $action; - } - (Bank::Bank1, 13) => { - let $register = &($iocon_register).pio1_13; - $action; - } - (Bank::Bank1, 14) => { - let $register = &($iocon_register).pio1_14; - $action; - } - (Bank::Bank1, 15) => { - let $register = &($iocon_register).pio1_15; - $action; - } - (Bank::Bank1, 16) => { - let $register = &($iocon_register).pio1_16; - $action; - } - (Bank::Bank1, 17) => { - let $register = &($iocon_register).pio1_17; - $action; - } - (Bank::Bank1, 18) => { - let $register = &($iocon_register).pio1_18; - $action; - } - (Bank::Bank1, 19) => { - let $register = &($iocon_register).pio1_19; - $action; - } - (Bank::Bank1, 20) => { - let $register = &($iocon_register).pio1_20; - $action; - } - (Bank::Bank1, 21) => { - let $register = &($iocon_register).pio1_21; - $action; - } - (Bank::Bank1, 22) => { - let $register = &($iocon_register).pio1_22; - $action; - } - (Bank::Bank1, 23) => { - let $register = &($iocon_register).pio1_23; - $action; - } - (Bank::Bank1, 24) => { - let $register = &($iocon_register).pio1_24; - $action; - } - (Bank::Bank1, 25) => { - let $register = &($iocon_register).pio1_25; - $action; - } - (Bank::Bank1, 26) => { - let $register = &($iocon_register).pio1_26; - $action; - } - (Bank::Bank1, 27) => { - let $register = &($iocon_register).pio1_27; - $action; - } - (Bank::Bank1, 28) => { - let $register = &($iocon_register).pio1_28; - $action; - } - (Bank::Bank1, 29) => { - let $register = &($iocon_register).pio1_29; - $action; - } - (Bank::Bank1, 30) => { - let $register = &($iocon_register).pio1_30; - $action; - } - (Bank::Bank1, 31) => { - let $register = &($iocon_register).pio1_31; - $action; - } - _ => unreachable!(), } }; } From f58d2ceda1abcb44cd8e02c24316433ab4777b2c Mon Sep 17 00:00:00 2001 From: Roi Bachynskyi Date: Sun, 31 Aug 2025 20:08:35 +0300 Subject: [PATCH 43/76] lpc55: pint rewritten --- embassy-nxp/src/pint.rs | 97 ++++++++++++++--------------------------- 1 file changed, 33 insertions(+), 64 deletions(-) diff --git a/embassy-nxp/src/pint.rs b/embassy-nxp/src/pint.rs index ff414b4e6..e594aaa6a 100644 --- a/embassy-nxp/src/pint.rs +++ b/embassy-nxp/src/pint.rs @@ -5,10 +5,11 @@ use core::pin::Pin as FuturePin; use core::task::{Context, Poll}; use critical_section::Mutex; +use embassy_hal_internal::interrupt::InterruptExt; use embassy_sync::waitqueue::AtomicWaker; -use crate::gpio::{self, inputmux_reg, pint_reg, syscon_reg, AnyPin, Level, SealedPin}; -use crate::pac::interrupt; +use crate::gpio::{self, AnyPin, Level, SealedPin}; +use crate::pac::{interrupt, INPUTMUX, PINT, SYSCON}; use crate::Peri; struct PinInterrupt { @@ -88,18 +89,18 @@ enum InterruptOn { } pub(crate) fn init() { - syscon_reg().ahbclkctrl0.modify(|_, w| w.pint().enable()); + SYSCON.ahbclkctrl0().modify(|w| w.set_pint(true)); // Enable interrupts unsafe { - crate::pac::NVIC::unmask(crate::pac::Interrupt::PIN_INT0); - crate::pac::NVIC::unmask(crate::pac::Interrupt::PIN_INT1); - crate::pac::NVIC::unmask(crate::pac::Interrupt::PIN_INT2); - crate::pac::NVIC::unmask(crate::pac::Interrupt::PIN_INT3); - crate::pac::NVIC::unmask(crate::pac::Interrupt::PIN_INT4); - crate::pac::NVIC::unmask(crate::pac::Interrupt::PIN_INT5); - crate::pac::NVIC::unmask(crate::pac::Interrupt::PIN_INT6); - crate::pac::NVIC::unmask(crate::pac::Interrupt::PIN_INT7); + interrupt::PIN_INT0.enable(); + interrupt::PIN_INT1.enable(); + interrupt::PIN_INT2.enable(); + interrupt::PIN_INT3.enable(); + interrupt::PIN_INT4.enable(); + interrupt::PIN_INT5.enable(); + interrupt::PIN_INT6.enable(); + interrupt::PIN_INT7.enable(); }; info!("Pin interrupts initialized"); @@ -119,24 +120,19 @@ impl<'d> InputFuture<'d> { let interrupt_number = next_available_interrupt()?; // Clear interrupt, just in case - pint_reg() - .rise - .write(|w| unsafe { w.rdet().bits(1 << interrupt_number) }); - pint_reg() - .fall - .write(|w| unsafe { w.fdet().bits(1 << interrupt_number) }); + PINT.rise().write(|w| w.set_rdet(1 << interrupt_number)); + PINT.fall().write(|w| w.set_fdet(1 << interrupt_number)); // Enable input multiplexing on pin interrupt register 0 for pin (32*bank + pin_number) - inputmux_reg().pintsel[interrupt_number] - .write(|w| unsafe { w.intpin().bits(32 * pin.pin_bank() as u8 + pin.pin_number()) }); + INPUTMUX + .pintsel(interrupt_number as usize) + .write(|w| w.set_intpin(32 * pin.pin_bank() as u8 + pin.pin_number())); match interrupt_on { InterruptOn::Level(level) => { // Set pin interrupt register to edge sensitive or level sensitive // 0 = edge sensitive, 1 = level sensitive - pint_reg() - .isel - .modify(|r, w| unsafe { w.bits(r.bits() | (1 << interrupt_number)) }); + PINT.isel().modify(|w| w.set_pmode(w.pmode() | (1 << interrupt_number))); // Enable level interrupt. // @@ -144,63 +140,44 @@ impl<'d> InputFuture<'d> { // is activated. // 0 = no-op, 1 = enable - pint_reg() - .sienr - .write(|w| unsafe { w.setenrl().bits(1 << interrupt_number) }); + PINT.sienr().write(|w| w.set_setenrl(1 << interrupt_number)); // Set active level match level { Level::Low => { // 0 = no-op, 1 = select LOW - pint_reg() - .cienf - .write(|w| unsafe { w.cenaf().bits(1 << interrupt_number) }); + PINT.cienf().write(|w| w.set_cenaf(1 << interrupt_number)); } Level::High => { // 0 = no-op, 1 = select HIGH - pint_reg() - .sienf - .write(|w| unsafe { w.setenaf().bits(1 << interrupt_number) }); + PINT.sienf().write(|w| w.set_setenaf(1 << interrupt_number)); } } } InterruptOn::Edge(edge) => { // Set pin interrupt register to edge sensitive or level sensitive // 0 = edge sensitive, 1 = level sensitive - pint_reg() - .isel - .modify(|r, w| unsafe { w.bits(r.bits() & !(1 << interrupt_number)) }); + PINT.isel() + .modify(|w| w.set_pmode(w.pmode() & !(1 << interrupt_number))); // Enable rising/falling edge detection match edge { Edge::Rising => { // 0 = no-op, 1 = enable rising edge - pint_reg() - .sienr - .write(|w| unsafe { w.setenrl().bits(1 << interrupt_number) }); + PINT.sienr().write(|w| w.set_setenrl(1 << interrupt_number)); // 0 = no-op, 1 = disable falling edge - pint_reg() - .cienf - .write(|w| unsafe { w.cenaf().bits(1 << interrupt_number) }); + PINT.cienf().write(|w| w.set_cenaf(1 << interrupt_number)); } Edge::Falling => { // 0 = no-op, 1 = enable falling edge - pint_reg() - .sienf - .write(|w| unsafe { w.setenaf().bits(1 << interrupt_number) }); + PINT.sienf().write(|w| w.set_setenaf(1 << interrupt_number)); // 0 = no-op, 1 = disable rising edge - pint_reg() - .cienr - .write(|w| unsafe { w.cenrl().bits(1 << interrupt_number) }); + PINT.cienr().write(|w| w.set_cenrl(1 << interrupt_number)); } Edge::Both => { // 0 = no-op, 1 = enable - pint_reg() - .sienr - .write(|w| unsafe { w.setenrl().bits(1 << interrupt_number) }); - pint_reg() - .sienf - .write(|w| unsafe { w.setenaf().bits(1 << interrupt_number) }); + PINT.sienr().write(|w| w.set_setenrl(1 << interrupt_number)); + PINT.sienf().write(|w| w.set_setenaf(1 << interrupt_number)); } } } @@ -239,12 +216,8 @@ impl<'d> Drop for InputFuture<'d> { // Disable pin interrupt // 0 = no-op, 1 = disable - pint_reg() - .cienr - .write(|w| unsafe { w.cenrl().bits(1 << interrupt_number) }); - pint_reg() - .cienf - .write(|w| unsafe { w.cenaf().bits(1 << interrupt_number) }); + PINT.cienr().write(|w| w.set_cenrl(1 << interrupt_number)); + PINT.cienf().write(|w| w.set_cenaf(1 << interrupt_number)); critical_section::with(|cs| { let mut pin_interrupts = PIN_INTERRUPTS.borrow(cs).borrow_mut(); @@ -277,12 +250,8 @@ impl<'d> Future for InputFuture<'d> { } fn handle_interrupt(interrupt_number: usize) { - pint_reg() - .rise - .write(|w| unsafe { w.rdet().bits(1 << interrupt_number) }); - pint_reg() - .fall - .write(|w| unsafe { w.fdet().bits(1 << interrupt_number) }); + PINT.rise().write(|w| w.set_rdet(1 << interrupt_number)); + PINT.fall().write(|w| w.set_fdet(1 << interrupt_number)); critical_section::with(|cs| { let mut pin_interrupts = PIN_INTERRUPTS.borrow(cs).borrow_mut(); From e74dbe8c5d625f17c0bcee82aa8a2d45deb33a18 Mon Sep 17 00:00:00 2001 From: Roi Bachynskyi Date: Mon, 1 Sep 2025 23:19:24 +0300 Subject: [PATCH 44/76] lpc55: blocking usart rewritten --- embassy-nxp/src/usart.rs | 1 - embassy-nxp/src/usart/lpc55.rs | 884 +++++++++++++-------------------- 2 files changed, 332 insertions(+), 553 deletions(-) diff --git a/embassy-nxp/src/usart.rs b/embassy-nxp/src/usart.rs index 009c251e2..c426ab96d 100644 --- a/embassy-nxp/src/usart.rs +++ b/embassy-nxp/src/usart.rs @@ -1,5 +1,4 @@ //! Universal Synchronous/Asynchronous Receiver/Transmitter (USART) driver. -#![macro_use] #[cfg_attr(feature = "lpc55", path = "./usart/lpc55.rs")] mod inner; diff --git a/embassy-nxp/src/usart/lpc55.rs b/embassy-nxp/src/usart/lpc55.rs index 3f7456a2e..428b80c4b 100644 --- a/embassy-nxp/src/usart/lpc55.rs +++ b/embassy-nxp/src/usart/lpc55.rs @@ -2,9 +2,12 @@ use core::marker::PhantomData; use embassy_hal_internal::{Peri, PeripheralType}; use embedded_io::{self, ErrorKind}; -pub use sealed::SealedInstance; -use crate::gpio::AnyPin; +use crate::gpio::{match_iocon, AnyPin, Bank, SealedPin}; +use crate::pac::flexcomm::Flexcomm as FlexcommReg; +use crate::pac::iocon::vals::PioFunc; +use crate::pac::usart::Usart as UsartReg; +use crate::pac::*; use crate::{Blocking, Mode}; /// Serial error @@ -47,16 +50,6 @@ pub enum DataBits { DataBits9, } -impl DataBits { - fn bits(&self) -> u8 { - match self { - Self::DataBits7 => 0b00, - Self::DataBits8 => 0b01, - Self::DataBits9 => 0b10, - } - } -} - /// Parity bit. #[derive(Clone, Copy, PartialEq, Eq, Debug)] pub enum Parity { @@ -68,16 +61,6 @@ pub enum Parity { ParityOdd, } -impl Parity { - fn bits(&self) -> u8 { - match self { - Self::ParityNone => 0b00, - Self::ParityEven => 0b10, - Self::ParityOdd => 0b11, - } - } -} - /// Stop bits. #[derive(Clone, Copy, PartialEq, Eq, Debug)] pub enum StopBits { @@ -87,15 +70,6 @@ pub enum StopBits { Stop2, } -impl StopBits { - fn bits(&self) -> bool { - return match self { - Self::Stop1 => false, - Self::Stop2 => true, - }; - } -} - /// UART config. #[non_exhaustive] #[derive(Clone, Debug)] @@ -117,7 +91,7 @@ pub struct Config { impl Default for Config { fn default() -> Self { Self { - baudrate: 9600, + baudrate: 115200, data_bits: DataBits::DataBits8, stop_bits: StopBits::Stop1, parity: Parity::ParityNone, @@ -131,59 +105,72 @@ impl Default for Config { /// 'd: the lifetime marker ensuring correct borrow checking for peripherals used at compile time /// T: the peripheral instance type allowing usage of peripheral specific registers /// M: the operating mode of USART peripheral -pub struct Usart<'d, T: Instance, M: Mode> { - tx: UsartTx<'d, T, M>, - rx: UsartRx<'d, T, M>, +pub struct Usart<'d, M: Mode> { + tx: UsartTx<'d, M>, + rx: UsartRx<'d, M>, } -pub struct UsartTx<'d, T: Instance, M: Mode> { - phantom: PhantomData<(&'d (), T, M)>, +pub struct UsartTx<'d, M: Mode> { + info: &'static Info, + phantom: PhantomData<(&'d (), M)>, } -pub struct UsartRx<'d, T: Instance, M: Mode> { - phantom: PhantomData<(&'d (), T, M)>, +pub struct UsartRx<'d, M: Mode> { + info: &'static Info, + phantom: PhantomData<(&'d (), M)>, } -impl<'d, T: Instance, M: Mode> UsartTx<'d, T, M> { - pub fn new(_usart: Peri<'d, T>, tx: Peri<'d, impl TxPin>, config: Config) -> Self { - Usart::::init(Some(tx.into()), None, config); - Self::new_inner() +impl<'d, M: Mode> UsartTx<'d, M> { + pub fn new(_usart: Peri<'d, T>, tx: Peri<'d, impl TxPin>, config: Config) -> Self { + Usart::::init::(Some(tx.into()), None, config); + Self::new_inner(T::info()) } #[inline] - fn new_inner() -> Self { - Self { phantom: PhantomData } + fn new_inner(info: &'static Info) -> Self { + Self { + info, + phantom: PhantomData, + } } pub fn blocking_write(&mut self, buffer: &[u8]) -> Result<(), Error> { - T::blocking_write(buffer) + for &b in buffer { + while !(self.info.usart_reg.fifostat().read().txnotfull()) {} + self.info.usart_reg.fifowr().write(|w| w.set_txdata(b as u16)); + } + Ok(()) } pub fn blocking_flush(&mut self) -> Result<(), Error> { - T::blocking_flush() + while !(self.info.usart_reg.fifostat().read().txempty()) {} + Ok(()) } pub fn tx_busy(&self) -> bool { - T::tx_busy() + !(self.info.usart_reg.fifostat().read().txempty()) } } -impl<'d, T: Instance> UsartTx<'d, T, Blocking> { - pub fn new_blocking(_usart: Peri<'d, T>, tx: Peri<'d, impl TxPin>, config: Config) -> Self { - Usart::::init(Some(tx.into()), None, config); - Self::new_inner() +impl<'d> UsartTx<'d, Blocking> { + pub fn new_blocking(_usart: Peri<'d, T>, tx: Peri<'d, impl TxPin>, config: Config) -> Self { + Usart::::init::(Some(tx.into()), None, config); + Self::new_inner(T::info()) } } -impl<'d, T: Instance, M: Mode> UsartRx<'d, T, M> { - pub fn new(_usart: Peri<'d, T>, rx: Peri<'d, impl RxPin>, config: Config) -> Self { - Usart::::init(None, Some(rx.into()), config); - Self::new_inner() +impl<'d, M: Mode> UsartRx<'d, M> { + pub fn new(_usart: Peri<'d, T>, rx: Peri<'d, impl RxPin>, config: Config) -> Self { + Usart::::init::(None, Some(rx.into()), config); + Self::new_inner(T::info()) } #[inline] - fn new_inner() -> Self { - Self { phantom: PhantomData } + fn new_inner(info: &'static Info) -> Self { + Self { + info, + phantom: PhantomData, + } } pub fn blocking_read(&mut self, mut buffer: &mut [u8]) -> Result<(), Error> { @@ -201,19 +188,35 @@ impl<'d, T: Instance, M: Mode> UsartRx<'d, T, M> { /// - Ok(n) -> read n bytes /// - Err(n, Error) -> read n-1 bytes, but encountered an error while reading nth byte fn drain_fifo(&mut self, buffer: &mut [u8]) -> Result { - T::drain_fifo(buffer) + for (i, b) in buffer.iter_mut().enumerate() { + while !(self.info.usart_reg.fifostat().read().rxnotempty()) {} + if self.info.usart_reg.fifostat().read().rxerr() { + return Err((i, Error::Overrun)); + } else if self.info.usart_reg.fifordnopop().read().parityerr() { + return Err((i, Error::Parity)); + } else if self.info.usart_reg.fifordnopop().read().framerr() { + return Err((i, Error::Framing)); + } else if self.info.usart_reg.fifordnopop().read().rxnoise() { + return Err((i, Error::Noise)); + } else if self.info.usart_reg.intstat().read().deltarxbrk() { + return Err((i, Error::Break)); + } + let dr = self.info.usart_reg.fiford().read().rxdata() as u8; + *b = dr; + } + Ok(buffer.len()) } } -impl<'d, T: Instance> UsartRx<'d, T, Blocking> { - pub fn new_blocking(_usart: Peri<'d, T>, rx: Peri<'d, impl RxPin>, config: Config) -> Self { - Usart::::init(None, Some(rx.into()), config); - Self::new_inner() +impl<'d> UsartRx<'d, Blocking> { + pub fn new_blocking(_usart: Peri<'d, T>, rx: Peri<'d, impl RxPin>, config: Config) -> Self { + Usart::::init::(None, Some(rx.into()), config); + Self::new_inner(T::info()) } } -impl<'d, T: Instance> Usart<'d, T, Blocking> { - pub fn new_blocking( +impl<'d> Usart<'d, Blocking> { + pub fn new_blocking( usart: Peri<'d, T>, tx: Peri<'d, impl TxPin>, rx: Peri<'d, impl RxPin>, @@ -223,29 +226,70 @@ impl<'d, T: Instance> Usart<'d, T, Blocking> { } } -impl<'d, T: Instance, M: Mode> Usart<'d, T, M> { - fn new_inner(_usart: Peri<'d, T>, mut tx: Peri<'d, AnyPin>, mut rx: Peri<'d, AnyPin>, config: Config) -> Self { - Self::init(Some(tx.reborrow()), Some(rx.reborrow()), config); +impl<'d, M: Mode> Usart<'d, M> { + fn new_inner( + _usart: Peri<'d, T>, + mut tx: Peri<'d, AnyPin>, + mut rx: Peri<'d, AnyPin>, + config: Config, + ) -> Self { + Self::init::(Some(tx.reborrow()), Some(rx.reborrow()), config); Self { - tx: UsartTx::new_inner(), - rx: UsartRx::new_inner(), + tx: UsartTx::new_inner(T::info()), + rx: UsartRx::new_inner(T::info()), } } - fn init(_tx: Option>, _rx: Option>, config: Config) { - T::enable_clock(); - T::reset_flexcomm(); - let source_clock: u32 = T::select_clock(config.baudrate); - T::configure_flexcomm(); - T::tx_pin_config(); - T::rx_pin_config(); - Self::set_baudrate(source_clock, config.baudrate); - T::configure_usart(config); - T::disable_dma(); - T::enable_usart(); + fn init(tx: Option>, rx: Option>, config: Config) { + Self::configure_flexcomm(T::info().fc_reg, T::instance_number()); + Self::configure_clock::(&config); + Self::pin_config::(tx, rx); + Self::configure_usart(T::info(), &config); } - fn set_baudrate(source_clock: u32, baudrate: u32) { + fn configure_clock(config: &Config) { + // Select source clock + + // Adaptive clock choice based on baud rate + // To get the desired baud rate, it is essential to choose the clock bigger than baud rate so that it can be 'chiseled' + // There are two types of dividers: integer divider (baud rate generator register and oversample selection value) + // and fractional divider (fractional rate divider). + + // By default, oversampling rate is 16 which is an industry standard. + // That means 16 clocks are used to deliver the byte to recipient. + // In this way the probability of getting correct bytes instead of noise directly increases as oversampling increases as well. + + // Minimum and maximum values were computed taking these formulas into account: + // For minimum value, MULT = 0, BRGVAL = 0 + // For maximum value, MULT = 255, BRGVAL = 255 + // Flexcomm Interface function clock = (clock selected via FCCLKSEL) / (1 + MULT / DIV) + // By default, OSRVAL = 15 (see above) + // Baud rate = [FCLK / (OSRVAL+1)] / (BRGVAL + 1) + let source_clock = match config.baudrate { + 750_001..=6_000_000 => { + SYSCON + .fcclksel(T::instance_number()) + .modify(|w| w.set_sel(syscon::vals::FcclkselSel::ENUM_0X3)); // 96 MHz + 96_000_000 + } + 1501..=750_000 => { + SYSCON + .fcclksel(T::instance_number()) + .modify(|w| w.set_sel(syscon::vals::FcclkselSel::ENUM_0X2)); // 12 MHz + 12_000_000 + } + 121..=1500 => { + SYSCON + .fcclksel(T::instance_number()) + .modify(|w| w.set_sel(syscon::vals::FcclkselSel::ENUM_0X4)); // 1 MHz + 1_000_000 + } + _ => { + panic!("{} baudrate is not permitted in this mode", config.baudrate); + } + }; + // Calculate MULT and BRG values based on baudrate + // There are two types of dividers: integer divider (baud rate generator register and oversample selection value) // and fractional divider (fractional rate divider). // For oversampling, the default is industry standard 16x oversampling, i.e. OSRVAL = 15 @@ -274,14 +318,167 @@ impl<'d, T: Instance, M: Mode> Usart<'d, T, M> { // Secondly, MULT is calculated to ultimately 'chisel' the clock to get the baud rate. // The deduced formulas are written below. - let brg_value = (source_clock / (16 * baudrate)).min(255); + let brg_value = (source_clock / (16 * config.baudrate)).min(255); let raw_clock = source_clock / (16 * brg_value); - let mult_value = ((raw_clock * 256 / baudrate) - 256).min(255); - T::set_baudrate(mult_value as u8, brg_value as u8); + let mult_value = ((raw_clock * 256 / config.baudrate) - 256).min(255); + + // Write values to the registers + + // FCLK = (clock selected via FCCLKSEL) / (1+ MULT / DIV) + // Remark: To use the fractional baud rate generator, 0xFF must be wirtten to the DIV value + // to yield a denominator vale of 256. All other values are not supported + SYSCON.flexfrgctrl(T::instance_number()).modify(|w| { + w.set_div(0xFF); + w.set_mult(mult_value as u8); + }); + + // Baud rate = [FCLK / (OSRVAL+1)] / (BRGVAL + 1) + // By default, oversampling is 16x, i.e. OSRVAL = 15 + + // Typical industry standard USARTs use a 16x oversample clock to transmit and receive + // asynchronous data. This is the number of BRG clocks used for one data bit. The + // Oversample Select Register (OSR) allows this USART to use a 16x down to a 5x + // oversample clock. There is no oversampling in synchronous modes. + T::info() + .usart_reg + .brg() + .modify(|w| w.set_brgval((brg_value - 1) as u16)); + } + + fn pin_config(tx: Option>, rx: Option>) { + if let Some(tx_pin) = tx { + match_iocon!(register, tx_pin.pin_bank(), tx_pin.pin_number(), { + register.modify(|w| { + w.set_func(T::tx_pin_func()); + w.set_mode(iocon::vals::PioMode::INACTIVE); + w.set_slew(iocon::vals::PioSlew::STANDARD); + w.set_invert(false); + w.set_digimode(iocon::vals::PioDigimode::DIGITAL); + w.set_od(iocon::vals::PioOd::NORMAL); + }); + }) + } + + if let Some(rx_pin) = rx { + match_iocon!(register, rx_pin.pin_bank(), rx_pin.pin_number(), { + register.modify(|w| { + w.set_func(T::rx_pin_func()); + w.set_mode(iocon::vals::PioMode::INACTIVE); + w.set_slew(iocon::vals::PioSlew::STANDARD); + w.set_invert(false); + w.set_digimode(iocon::vals::PioDigimode::DIGITAL); + w.set_od(iocon::vals::PioOd::NORMAL); + }); + }) + }; + } + + fn configure_flexcomm(flexcomm_register: crate::pac::flexcomm::Flexcomm, instance_number: usize) { + critical_section::with(|_cs| { + if !(SYSCON.ahbclkctrl0().read().iocon()) { + SYSCON.ahbclkctrl0().modify(|w| w.set_iocon(true)); + } + }); + critical_section::with(|_cs| { + if !(SYSCON.ahbclkctrl1().read().fc(instance_number)) { + SYSCON.ahbclkctrl1().modify(|w| w.set_fc(instance_number, true)); + } + }); + SYSCON + .presetctrl1() + .modify(|w| w.set_fc_rst(instance_number, syscon::vals::FcRst::ASSERTED)); + SYSCON + .presetctrl1() + .modify(|w| w.set_fc_rst(instance_number, syscon::vals::FcRst::RELEASED)); + flexcomm_register + .pselid() + .modify(|w| w.set_persel(flexcomm::vals::Persel::USART)); + } + + fn configure_usart(info: &'static Info, config: &Config) { + let registers = info.usart_reg; + // See section 34.6.1 + registers.cfg().modify(|w| { + // LIN break mode enable + // Disabled. Break detect and generate is configured for normal operation. + w.set_linmode(false); + //CTS Enable. Determines whether CTS is used for flow control. CTS can be from the + //input pin, or from the USART’s own RTS if loopback mode is enabled. + // No flow control. The transmitter does not receive any automatic flow control signal. + w.set_ctsen(false); + // Selects synchronous or asynchronous operation. + w.set_syncen(usart::vals::Syncen::ASYNCHRONOUS_MODE); + // Selects the clock polarity and sampling edge of received data in synchronous mode. + w.set_clkpol(usart::vals::Clkpol::RISING_EDGE); + // Synchronous mode Master select. + // When synchronous mode is enabled, the USART is a master. + w.set_syncmst(usart::vals::Syncmst::MASTER); + // Selects data loopback mode + w.set_loop_(usart::vals::Loop::NORMAL); + // Output Enable Turnaround time enable for RS-485 operation. + // Disabled. If selected by OESEL, the Output Enable signal deasserted at the end of + // the last stop bit of a transmission. + w.set_oeta(false); + // Output enable select. + // Standard. The RTS signal is used as the standard flow control function. + w.set_oesel(usart::vals::Oesel::STANDARD); + // Automatic address matching enable. + // Disabled. When addressing is enabled by ADDRDET, address matching is done by + // software. This provides the possibility of versatile addressing (e.g. respond to more + // than one address) + w.set_autoaddr(false); + // Output enable polarity. + // Low. If selected by OESEL, the output enable is active low. + w.set_oepol(usart::vals::Oepol::LOW); + }); + + // Configurations based on the config written by a user + registers.cfg().modify(|w| { + w.set_datalen(match config.data_bits { + DataBits::DataBits7 => usart::vals::Datalen::BIT_7, + DataBits::DataBits8 => usart::vals::Datalen::BIT_8, + DataBits::DataBits9 => usart::vals::Datalen::BIT_9, + }); + w.set_paritysel(match config.parity { + Parity::ParityNone => usart::vals::Paritysel::NO_PARITY, + Parity::ParityEven => usart::vals::Paritysel::EVEN_PARITY, + Parity::ParityOdd => usart::vals::Paritysel::ODD_PARITY, + }); + w.set_stoplen(match config.stop_bits { + StopBits::Stop1 => usart::vals::Stoplen::BIT_1, + StopBits::Stop2 => usart::vals::Stoplen::BITS_2, + }); + w.set_rxpol(match config.invert_rx { + false => usart::vals::Rxpol::STANDARD, + true => usart::vals::Rxpol::INVERTED, + }); + w.set_txpol(match config.invert_tx { + false => usart::vals::Txpol::STANDARD, + true => usart::vals::Txpol::INVERTED, + }); + }); + + // DMA-related settings + registers.fifocfg().modify(|w| { + w.set_dmatx(false); + w.set_dmatx(false); + }); + + // Enabling USART + registers.fifocfg().modify(|w| { + w.set_enabletx(true); + w.set_enablerx(true); + }); + registers.cfg().modify(|w| w.set_enable(true)); + + // Drain RX FIFO in case it still has some unrelevant data + while registers.fifostat().read().rxnotempty() { + let _ = registers.fiford().read().0; + } } } -impl<'d, T: Instance, M: Mode> Usart<'d, T, M> { +impl<'d, M: Mode> Usart<'d, M> { /// Transmit the provided buffer blocking execution until done. pub fn blocking_write(&mut self, buffer: &[u8]) -> Result<(), Error> { self.tx.blocking_write(buffer) @@ -304,19 +501,19 @@ impl<'d, T: Instance, M: Mode> Usart<'d, T, M> { /// Split the Usart into a transmitter and receiver, which is particularly /// useful when having two tasks correlating to transmitting and receiving. - pub fn split(self) -> (UsartTx<'d, T, M>, UsartRx<'d, T, M>) { + pub fn split(self) -> (UsartTx<'d, M>, UsartRx<'d, M>) { (self.tx, self.rx) } /// Split the Usart into a transmitter and receiver by mutable reference, /// which is particularly useful when having two tasks correlating to /// transmitting and receiving. - pub fn split_ref(&mut self) -> (&mut UsartTx<'d, T, M>, &mut UsartRx<'d, T, M>) { + pub fn split_ref(&mut self) -> (&mut UsartTx<'d, M>, &mut UsartRx<'d, M>) { (&mut self.tx, &mut self.rx) } } -impl<'d, T: Instance, M: Mode> embedded_hal_02::blocking::serial::Write for UsartTx<'d, T, M> { +impl<'d, M: Mode> embedded_hal_02::blocking::serial::Write for UsartTx<'d, M> { type Error = Error; fn bwrite_all(&mut self, buffer: &[u8]) -> Result<(), Self::Error> { @@ -328,7 +525,7 @@ impl<'d, T: Instance, M: Mode> embedded_hal_02::blocking::serial::Write for } } -impl<'d, T: Instance, M: Mode> embedded_hal_02::blocking::serial::Write for Usart<'d, T, M> { +impl<'d, M: Mode> embedded_hal_02::blocking::serial::Write for Usart<'d, M> { type Error = Error; fn bwrite_all(&mut self, buffer: &[u8]) -> Result<(), Self::Error> { @@ -340,11 +537,11 @@ impl<'d, T: Instance, M: Mode> embedded_hal_02::blocking::serial::Write for } } -impl<'d, T: Instance> embedded_io::ErrorType for UsartTx<'d, T, Blocking> { +impl<'d> embedded_io::ErrorType for UsartTx<'d, Blocking> { type Error = Error; } -impl<'d, T: Instance> embedded_io::Write for UsartTx<'d, T, Blocking> { +impl<'d> embedded_io::Write for UsartTx<'d, Blocking> { fn write(&mut self, buf: &[u8]) -> Result { self.blocking_write(buf).map(|_| buf.len()) } @@ -354,21 +551,21 @@ impl<'d, T: Instance> embedded_io::Write for UsartTx<'d, T, Blocking> { } } -impl<'d, T: Instance> embedded_io::ErrorType for UsartRx<'d, T, Blocking> { +impl<'d> embedded_io::ErrorType for UsartRx<'d, Blocking> { type Error = Error; } -impl<'d, T: Instance> embedded_io::Read for UsartRx<'d, T, Blocking> { +impl<'d> embedded_io::Read for UsartRx<'d, Blocking> { fn read(&mut self, buf: &mut [u8]) -> Result { self.blocking_read(buf).map(|_| buf.len()) } } -impl<'d, T: Instance> embedded_io::ErrorType for Usart<'d, T, Blocking> { +impl<'d> embedded_io::ErrorType for Usart<'d, Blocking> { type Error = Error; } -impl<'d, T: Instance> embedded_io::Write for Usart<'d, T, Blocking> { +impl<'d> embedded_io::Write for Usart<'d, Blocking> { fn write(&mut self, buf: &[u8]) -> Result { self.blocking_write(buf).map(|_| buf.len()) } @@ -378,468 +575,69 @@ impl<'d, T: Instance> embedded_io::Write for Usart<'d, T, Blocking> { } } -impl<'d, T: Instance> embedded_io::Read for Usart<'d, T, Blocking> { +impl<'d> embedded_io::Read for Usart<'d, Blocking> { fn read(&mut self, buf: &mut [u8]) -> Result { self.blocking_read(buf).map(|_| buf.len()) } } -type UsartRegBlock = crate::pac::usart0::RegisterBlock; +struct Info { + usart_reg: UsartReg, + fc_reg: FlexcommReg, +} -mod sealed { - use crate::usart::inner::UsartRegBlock; - use crate::usart::{Config, Error}; - pub trait SealedInstance { - fn usart_reg() -> &'static UsartRegBlock; - fn enable_clock(); - fn select_clock(baudrate: u32) -> u32; - fn configure_flexcomm(); - fn set_baudrate(mult_value: u8, brg_value: u8); - fn reset_flexcomm(); - fn tx_pin_config(); - fn rx_pin_config(); - - fn configure_usart(config: Config) { - // See section 34.6.1 - Self::usart_reg().cfg.modify(|_, w| { - // LIN break mode enable - w.linmode() - // Disabled. Break detect and generate is configured for normal operation. - .disabled() - //CTS Enable. Determines whether CTS is used for flow control. CTS can be from the - //input pin, or from the USART’s own RTS if loopback mode is enabled. - .ctsen() - // No flow control. The transmitter does not receive any automatic flow control signal. - .disabled() - // Selects synchronous or asynchronous operation. - .syncen() - .asynchronous_mode() - // Selects the clock polarity and sampling edge of received data in synchronous mode. - .clkpol() - .rising_edge() - // Synchronous mode Master select. - .syncmst() - // When synchronous mode is enabled, the USART is a master. - .master() - // Selects data loopback mode - .loop_() - // Normal operation - .normal() - // Output Enable Turnaround time enable for RS-485 operation. - .oeta() - // Disabled. If selected by OESEL, the Output Enable signal deasserted at the end of - // the last stop bit of a transmission. - .disabled() - // Output enable select. - .oesel() - // Standard. The RTS signal is used as the standard flow control function. - .standard() - // Automatic address matching enable. - .autoaddr() - // Disabled. When addressing is enabled by ADDRDET, address matching is done by - // software. This provides the possibility of versatile addressing (e.g. respond to more - // than one address) - .disabled() - // Output enable polarity. - .oepol() - // Low. If selected by OESEL, the output enable is active low. - .low() - }); - - Self::usart_reg().cfg.modify(|_, w| unsafe { - w.datalen() - .bits(config.data_bits.bits()) - .paritysel() - .bits(config.parity.bits()) - .stoplen() - .bit(config.stop_bits.bits()) - .rxpol() - .bit(config.invert_rx) - .txpol() - .bit(config.invert_tx) - }); - } - fn disable_dma() { - Self::usart_reg() - .fifocfg - .modify(|_, w| w.dmatx().disabled().dmarx().disabled()); - } - fn enable_usart() { - Self::usart_reg() - .fifocfg - .modify(|_, w| w.enabletx().enabled().enablerx().enabled()); - Self::usart_reg().cfg.modify(|_, w| w.enable().enabled()); - while Self::usart_reg().fifostat.read().rxnotempty().bit_is_set() { - let _ = Self::usart_reg().fiford.read().bits(); - } - } - fn blocking_write(buffer: &[u8]) -> Result<(), Error> { - for &b in buffer { - while Self::usart_reg().fifostat.read().txnotfull().bit_is_clear() {} - Self::usart_reg() - .fifowr - .modify(|_, w| unsafe { w.txdata().bits(b as u16) }); - } - Ok(()) - } - fn blocking_flush() -> Result<(), Error> { - while Self::usart_reg().fifostat.read().txempty().bit_is_clear() {} - Ok(()) - } - fn tx_busy() -> bool { - Self::usart_reg().fifostat.read().txempty().bit_is_clear() - } - fn drain_fifo(buffer: &mut [u8]) -> Result { - for (i, b) in buffer.iter_mut().enumerate() { - while Self::usart_reg().fifostat.read().rxnotempty().bit_is_clear() {} - - if Self::usart_reg().fifostat.read().rxerr().bit_is_set() { - return Err((i, Error::Overrun)); - } else if Self::usart_reg().fifordnopop.read().parityerr().bit_is_set() { - return Err((i, Error::Parity)); - } else if Self::usart_reg().fifordnopop.read().framerr().bit_is_set() { - return Err((i, Error::Framing)); - } else if Self::usart_reg().fifordnopop.read().rxnoise().bit_is_set() { - return Err((i, Error::Noise)); - } else if Self::usart_reg().intstat.read().deltarxbrk().bit_is_set() { - return Err((i, Error::Break)); - } - let dr = Self::usart_reg().fiford.read().bits() as u8; - *b = dr; - } - Ok(buffer.len()) - } - } +trait SealedInstance { + fn info() -> &'static Info; + fn instance_number() -> usize; + fn tx_pin_func() -> PioFunc; + fn rx_pin_func() -> PioFunc; } /// UART instance. #[allow(private_bounds)] -pub trait Instance: sealed::SealedInstance + PeripheralType {} +pub trait Instance: SealedInstance + PeripheralType {} -#[macro_export] macro_rules! impl_instance { - ( - $inst:ident, - usart_peripheral: $USARTX:ident, - usart_crate: $usartX:ident, - - flexcomm: { - field: $FLEXCOMM_FIELD:ident, - clock_field: $FLEXCOMM_CLK_FIELD:ident - }, - - reset: { - bit: $RESET_BIT:ident - }, - - clock: { - sel_field: $CLKSEL_FIELD:ident, - frg_field: $FRG_FIELD:ident - }, - - pins: { - tx: $TX_IOCON:ident => $TX_FUNC:expr, - rx: $RX_IOCON:ident => $RX_FUNC:expr - } - - ) => { - impl $crate::usart::SealedInstance for $crate::peripherals::$inst { - fn usart_reg() -> &'static UsartRegBlock { - unsafe { &*$crate::pac::$USARTX::ptr() } - } - - fn enable_clock() { - critical_section::with(|_cs| { - if syscon_reg().ahbclkctrl0.read().iocon().is_disable() { - syscon_reg().ahbclkctrl0.modify(|_, w| w.iocon().enable()); - } - if syscon_reg().ahbclkctrl1.read().$FLEXCOMM_CLK_FIELD().is_disable() { - syscon_reg() - .ahbclkctrl1 - .modify(|_, w| w.$FLEXCOMM_CLK_FIELD().enable()); - } - }); - } - - fn configure_flexcomm() { - let flexcomm = unsafe { &*$crate::pac::$FLEXCOMM_FIELD::ptr() }; - flexcomm.pselid.modify(|_, w| w.persel().usart()); - } - - fn reset_flexcomm() { - syscon_reg().presetctrl1.modify(|_, w| w.$RESET_BIT().set_bit()); - syscon_reg().presetctrl1.modify(|_, w| w.$RESET_BIT().clear_bit()); - } - - fn select_clock(baudrate: u32) -> u32 { - // Adaptive clock choice based on baud rate - // To get the desired baud rate, it is essential to choose the clock bigger than baud rate so that it can be 'chiseled' - // There are two types of dividers: integer divider (baud rate generator register and oversample selection value) - // and fractional divider (fractional rate divider). - - // By default, oversampling rate is 16 which is an industry standard. - // That means 16 clocks are used to deliver the byte to recipient. - // In this way the probability of getting correct bytes instead of noise directly increases as oversampling increases as well. - - // Minimum and maximum values were computed taking these formulas into account: - // For minimum value, MULT = 0, BRGVAL = 0 - // For maximum value, MULT = 255, BRGVAL = 255 - // Flexcomm Interface function clock = (clock selected via FCCLKSEL) / (1 + MULT / DIV) - // By default, OSRVAL = 15 (see above) - // Baud rate = [FCLK / (OSRVAL+1)] / (BRGVAL + 1) - return match baudrate { - 750_001..=6000000 => { - syscon_reg().$CLKSEL_FIELD().write(|w| w.sel().enum_0x3()); // 96 MHz - 96_000_000 - } - 1501..=750_000 => { - syscon_reg().$CLKSEL_FIELD().write(|w| w.sel().enum_0x2()); // 12 MHz - 12_000_000 - } - 121..=1500 => { - syscon_reg().$CLKSEL_FIELD().write(|w| w.sel().enum_0x4()); // 1 MHz - 1_000_000 - } - _ => { - panic!("{} baudrate is not permitted in this mode", baudrate); - } + ($inst:ident, $fc:ident, $tx_pin:ident, $rx_pin:ident, $fc_num:expr) => { + impl $crate::usart::inner::SealedInstance for $crate::peripherals::$inst { + fn info() -> &'static Info { + static INFO: Info = Info { + usart_reg: crate::pac::$inst, + fc_reg: crate::pac::$fc, }; + &INFO } - - fn set_baudrate(mult_value: u8, brg_value: u8) { - // FCLK = (clock selected via FCCLKSEL) / (1+ MULT / DIV) - // Remark: To use the fractional baud rate generator, 0xFF must be wirtten to the DIV value - // to yield a denominator vale of 256. All other values are not supported - syscon_reg() - .$FRG_FIELD() - .modify(|_, w| unsafe { w.div().bits(0xFF).mult().bits(mult_value as u8) }); - - // Baud rate = [FCLK / (OSRVAL+1)] / (BRGVAL + 1) - // By default, oversampling is 16x, i.e. OSRVAL = 15 - - // Typical industry standard USARTs use a 16x oversample clock to transmit and receive - // asynchronous data. This is the number of BRG clocks used for one data bit. The - // Oversample Select Register (OSR) allows this USART to use a 16x down to a 5x - // oversample clock. There is no oversampling in synchronous modes. - Self::usart_reg() - .brg - .modify(|_, w| unsafe { w.brgval().bits((brg_value - 1) as u16) }); + #[inline] + fn instance_number() -> usize { + $fc_num } - - fn tx_pin_config() { - iocon_reg().$TX_IOCON.modify(|_, w| unsafe { - w.func() - .bits($TX_FUNC) - .digimode() - .digital() - .slew() - .standard() - .mode() - .inactive() - .invert() - .disabled() - .od() - .normal() - }); + #[inline] + fn tx_pin_func() -> PioFunc { + PioFunc::$tx_pin } - - fn rx_pin_config() { - iocon_reg().$RX_IOCON.modify(|_, w| unsafe { - w.func() - .bits($RX_FUNC) - .digimode() - .digital() - .slew() - .standard() - .mode() - .inactive() - .invert() - .disabled() - .od() - .normal() - }); + #[inline] + fn rx_pin_func() -> PioFunc { + PioFunc::$rx_pin } } - impl $crate::usart::Instance for $crate::peripherals::$inst {} }; } -impl_instance!(USART0, usart_peripheral: USART0, usart_crate: usart0, - flexcomm: { - field: FLEXCOMM0, - clock_field: fc0 - }, - - reset: { - bit: fc0_rst - }, - - clock: { - sel_field: fcclksel0, - frg_field: flexfrg0ctrl - }, - - pins: { - tx: pio1_6 => 1, - rx: pio1_5 => 1 - } -); - -impl_instance!(USART1, usart_peripheral: USART1, usart_crate: usart1, - flexcomm: { - field: FLEXCOMM1, - clock_field: fc1 - }, - - reset: { - bit: fc1_rst - }, - - clock: { - sel_field: fcclksel1, - frg_field: flexfrg1ctrl - }, - - pins: { - tx: pio1_11 => 2, - rx: pio1_10 => 2 - } -); - -impl_instance!(USART2, usart_peripheral: USART2, usart_crate: usart2, - flexcomm: { - field: FLEXCOMM2, - clock_field: fc2 - }, - - reset: { - bit: fc2_rst - }, - - clock: { - sel_field: fcclksel2, - frg_field: flexfrg2ctrl - }, - - pins: { - tx: pio0_27 => 1, - rx: pio1_24 => 1 - } -); - -impl_instance!(USART3, usart_peripheral: USART3, usart_crate: usart3, - flexcomm: { - field: FLEXCOMM3, - clock_field: fc3 - }, - - reset: { - bit: fc3_rst - }, - - clock: { - sel_field: fcclksel3, - frg_field: flexfrg3ctrl - }, - - pins: { - tx: pio0_2 => 1, - rx: pio0_3 => 1 - } -); - -impl_instance!(USART4, usart_peripheral: USART4, usart_crate: usart4, - flexcomm: { - field: FLEXCOMM4, - clock_field: fc4 - }, - - reset: { - bit: fc4_rst - }, - - clock: { - sel_field: fcclksel4, - frg_field: flexfrg4ctrl - }, - - pins: { - tx: pio0_16 => 1, - rx: pio0_5 => 2 - } -); - -impl_instance!(USART5, usart_peripheral: USART5, usart_crate: usart5, - flexcomm: { - field: FLEXCOMM5, - clock_field: fc5 - }, - - reset: { - bit: fc5_rst - }, - - clock: { - sel_field: fcclksel5, - frg_field: flexfrg5ctrl - }, - - pins: { - tx: pio0_9 => 3, - rx: pio0_8 => 3 - } -); - -impl_instance!(USART6, usart_peripheral: USART6, usart_crate: usart6, - flexcomm: { - field: FLEXCOMM6, - clock_field: fc6 - }, - - reset: { - bit: fc6_rst - }, - - clock: { - sel_field: fcclksel6, - frg_field: flexfrg6ctrl - }, - - pins: { - tx: pio1_16 => 2, - rx: pio1_13 => 2 - } -); - -impl_instance!(USART7, usart_peripheral: USART7, usart_crate: usart7, - flexcomm: { - field: FLEXCOMM7, - clock_field: fc7 - }, - - reset: { - bit: fc7_rst - }, - - clock: { - sel_field: fcclksel7, - frg_field: flexfrg7ctrl - }, - - pins: { - tx: pio0_19 => 7, - rx: pio0_20 => 7 - } -); +impl_instance!(USART0, FLEXCOMM0, ALT1, ALT1, 0); +impl_instance!(USART1, FLEXCOMM1, ALT2, ALT2, 1); +impl_instance!(USART2, FLEXCOMM2, ALT1, ALT1, 2); +impl_instance!(USART3, FLEXCOMM3, ALT1, ALT1, 3); +impl_instance!(USART4, FLEXCOMM4, ALT1, ALT2, 4); +impl_instance!(USART5, FLEXCOMM5, ALT3, ALT3, 5); +impl_instance!(USART6, FLEXCOMM6, ALT2, ALT2, 6); +impl_instance!(USART7, FLEXCOMM7, ALT7, ALT7, 7); /// Trait for TX pins. pub trait TxPin: crate::gpio::Pin {} /// Trait for RX pins. pub trait RxPin: crate::gpio::Pin {} -// TODO: Add RTS, CTS and CLK pin traits - macro_rules! impl_pin { ($pin:ident, $instance:ident, Tx) => { impl TxPin for crate::peripherals::$pin {} @@ -849,37 +647,19 @@ macro_rules! impl_pin { }; } -impl_pin!(PIO1_5, USART0, Rx); impl_pin!(PIO1_6, USART0, Tx); -impl_pin!(PIO1_10, USART1, Rx); +impl_pin!(PIO1_5, USART0, Rx); impl_pin!(PIO1_11, USART1, Tx); +impl_pin!(PIO1_10, USART1, Rx); impl_pin!(PIO0_27, USART2, Tx); impl_pin!(PIO1_24, USART2, Rx); impl_pin!(PIO0_2, USART3, Tx); impl_pin!(PIO0_3, USART3, Rx); impl_pin!(PIO0_16, USART4, Tx); impl_pin!(PIO0_5, USART4, Rx); -impl_pin!(PIO0_8, USART5, Rx); impl_pin!(PIO0_9, USART5, Tx); +impl_pin!(PIO0_8, USART5, Rx); impl_pin!(PIO1_16, USART6, Tx); impl_pin!(PIO1_13, USART6, Rx); -impl_pin!(PIO0_20, USART7, Rx); impl_pin!(PIO0_19, USART7, Tx); - -/// Get the SYSCON register block. -/// -/// # Safety -/// Read/Write operations on a single registers are NOT atomic. You must ensure that the GPIO -/// registers are not accessed concurrently by multiple threads. -pub(crate) fn syscon_reg() -> &'static crate::pac::syscon::RegisterBlock { - unsafe { &*crate::pac::SYSCON::ptr() } -} - -/// Get the IOCON register block. -/// -/// # Safety -/// Read/Write operations on a single registers are NOT atomic. You must ensure that the GPIO -/// registers are not accessed concurrently by multiple threads. -pub(crate) fn iocon_reg() -> &'static crate::pac::iocon::RegisterBlock { - unsafe { &*crate::pac::IOCON::ptr() } -} +impl_pin!(PIO0_20, USART7, Rx); From d1dc7a3a88fbcc8a47fc8031a0338858c08090e9 Mon Sep 17 00:00:00 2001 From: Irina Chiorean Date: Mon, 1 Sep 2025 12:12:39 +0300 Subject: [PATCH 45/76] lpc55: rtc driver rewritten --- embassy-nxp/src/time_driver/rtc.rs | 76 ++++++++++++++++-------------- 1 file changed, 41 insertions(+), 35 deletions(-) diff --git a/embassy-nxp/src/time_driver/rtc.rs b/embassy-nxp/src/time_driver/rtc.rs index 94272e9c2..fb6de6a5e 100644 --- a/embassy-nxp/src/time_driver/rtc.rs +++ b/embassy-nxp/src/time_driver/rtc.rs @@ -6,7 +6,9 @@ use embassy_hal_internal::interrupt::{InterruptExt, Priority}; use embassy_sync::blocking_mutex::CriticalSectionMutex as Mutex; use embassy_time_driver::{time_driver_impl, Driver}; use embassy_time_queue_utils::Queue; -use lpc55_pac::{interrupt, PMC, RTC, SYSCON}; + +use crate::pac::{interrupt, pmc, rtc, PMC, RTC, SYSCON}; + struct AlarmState { timestamp: Cell, } @@ -32,33 +34,32 @@ time_driver_impl!(static DRIVER: RtcDriver = RtcDriver { }); impl RtcDriver { fn init(&'static self) { - let syscon = unsafe { &*SYSCON::ptr() }; - let pmc = unsafe { &*PMC::ptr() }; - let rtc = unsafe { &*RTC::ptr() }; + let syscon = SYSCON; + let pmc = PMC; + let rtc = RTC; - syscon.ahbclkctrl0.modify(|_, w| w.rtc().enable()); + syscon.ahbclkctrl0().modify(|w| w.set_rtc(true)); // By default the RTC enters software reset. If for some reason it is // not in reset, we enter and them promptly leave.q - rtc.ctrl.modify(|_, w| w.swreset().set_bit()); - rtc.ctrl.modify(|_, w| w.swreset().clear_bit()); + rtc.ctrl().modify(|w| w.set_swreset(true)); + rtc.ctrl().modify(|w| w.set_swreset(false)); // Select clock source - either XTAL or FRO - // pmc.rtcosc32k.write(|w| w.sel().xtal32k()); - pmc.rtcosc32k.write(|w| w.sel().fro32k()); + // pmc.rtcosc32k().write(|w| w.set_sel(pmc::vals::Sel::XTAL32K)); + pmc.rtcosc32k().write(|w| w.set_sel(pmc::vals::Sel::FRO32K)); // Start the RTC peripheral - rtc.ctrl.modify(|_, w| w.rtc_osc_pd().power_up()); - - // rtc.ctrl.modify(|_, w| w.rtc_en().clear_bit()); // EXTRA + rtc.ctrl().modify(|w| w.set_rtc_osc_pd(rtc::vals::RtcOscPd::POWER_UP)); //reset/clear(?) counter - rtc.count.reset(); + rtc.count().modify(|w| w.set_val(0)); //en rtc main counter - rtc.ctrl.modify(|_, w| w.rtc_en().set_bit()); - rtc.ctrl.modify(|_, w| w.rtc1khz_en().set_bit()); + rtc.ctrl().modify(|w| w.set_rtc_en(true)); + rtc.ctrl().modify(|w| w.set_rtc1khz_en(true)); // subsec counter enable - rtc.ctrl.modify(|_, w| w.rtc_subsec_ena().set_bit()); + rtc.ctrl() + .modify(|w| w.set_rtc_subsec_ena(rtc::vals::RtcSubsecEna::POWER_UP)); // enable irq unsafe { @@ -68,7 +69,7 @@ impl RtcDriver { } fn set_alarm(&self, cs: CriticalSection, timestamp: u64) -> bool { - let rtc = unsafe { &*RTC::ptr() }; + let rtc = RTC; let alarm = &self.alarms.borrow(cs); alarm.timestamp.set(timestamp); let now = self.now(); @@ -83,33 +84,38 @@ impl RtcDriver { let sec = (diff / 32768) as u32; let subsec = (diff % 32768) as u32; - let current_sec = rtc.count.read().val().bits(); + let current_sec = rtc.count().read().val(); let target_sec = current_sec.wrapping_add(sec as u32); - rtc.match_.write(|w| unsafe { w.matval().bits(target_sec) }); - rtc.wake.write(|w| unsafe { + rtc.match_().write(|w| w.set_matval(target_sec)); + rtc.wake().write(|w| { let ms = (subsec * 1000) / 32768; - w.val().bits(ms as u16) + w.set_val(ms as u16) }); + if subsec > 0 { let ms = (subsec * 1000) / 32768; - rtc.wake.write(|w| unsafe { w.val().bits(ms as u16) }); + rtc.wake().write(|w| w.set_val(ms as u16)); } - rtc.ctrl.modify(|_, w| w.alarm1hz().clear_bit().wake1khz().clear_bit()); + + rtc.ctrl().modify(|w| { + w.set_alarm1hz(false); + w.set_wake1khz(rtc::vals::Wake1khz::RUN) + }); true } fn on_interrupt(&self) { critical_section::with(|cs| { - let rtc = unsafe { &*RTC::ptr() }; - let flags = rtc.ctrl.read(); - if flags.alarm1hz().bit_is_clear() { - rtc.ctrl.modify(|_, w| w.alarm1hz().set_bit()); + let rtc = RTC; + let flags = rtc.ctrl().read(); + if flags.alarm1hz() == false { + rtc.ctrl().modify(|w| w.set_alarm1hz(true)); self.trigger_alarm(cs); } - if flags.wake1khz().bit_is_clear() { - rtc.ctrl.modify(|_, w| w.wake1khz().set_bit()); + if flags.wake1khz() == rtc::vals::Wake1khz::RUN { + rtc.ctrl().modify(|w| w.set_wake1khz(rtc::vals::Wake1khz::TIMEOUT)); self.trigger_alarm(cs); } }); @@ -135,13 +141,13 @@ impl RtcDriver { impl Driver for RtcDriver { fn now(&self) -> u64 { - let rtc = unsafe { &*RTC::ptr() }; + let rtc = RTC; loop { - let sec1 = rtc.count.read().val().bits() as u64; - let sub1 = rtc.subsec.read().subsec().bits() as u64; - let sec2 = rtc.count.read().val().bits() as u64; - let sub2 = rtc.subsec.read().subsec().bits() as u64; + let sec1 = rtc.count().read().val() as u64; + let sub1 = rtc.subsec().read().subsec() as u64; + let sec2 = rtc.count().read().val() as u64; + let sub2 = rtc.subsec().read().subsec() as u64; if sec1 == sec2 && sub1 == sub2 { return sec1 * 32768 + sub1; @@ -162,7 +168,7 @@ impl Driver for RtcDriver { }) } } -#[cortex_m_rt::interrupt] +#[interrupt] fn RTC() { DRIVER.on_interrupt(); } From 0816a6a4185eac63803bd7155941fa2d73a114a6 Mon Sep 17 00:00:00 2001 From: Roi Bachynskyi Date: Mon, 1 Sep 2025 21:22:14 +0300 Subject: [PATCH 46/76] lpc55: update Cargo.toml and CHANGELOG Co-authored-by: Irina Chiorean --- embassy-nxp/CHANGELOG.md | 2 +- embassy-nxp/Cargo.toml | 9 ++++----- 2 files changed, 5 insertions(+), 6 deletions(-) diff --git a/embassy-nxp/CHANGELOG.md b/embassy-nxp/CHANGELOG.md index 7042ad14c..ab97c4185 100644 --- a/embassy-nxp/CHANGELOG.md +++ b/embassy-nxp/CHANGELOG.md @@ -7,5 +7,5 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## Unreleased - ReleaseDate - +- Moved NXP LPC55S69 from `lpc55-pac` to `nxp-pac` - First release with changelog. diff --git a/embassy-nxp/Cargo.toml b/embassy-nxp/Cargo.toml index c66354ee2..dd132c7ba 100644 --- a/embassy-nxp/Cargo.toml +++ b/embassy-nxp/Cargo.toml @@ -36,21 +36,20 @@ embassy-time-queue-utils = { version = "0.3.0", path = "../embassy-time-queue-ut embedded-io = "0.6.1" embedded-hal-02 = { package = "embedded-hal", version = "0.2.6", features = ["unproven"] } ## Chip dependencies -lpc55-pac = { version = "0.5.0", optional = true } -nxp-pac = { version = "0.1.0", optional = true, git = "https://github.com/IrinaCh524/nxp-pac", rev = "d452e366105012ddd8ba332a9ab86e1931508235" } +nxp-pac = { version = "0.1.0", optional = true, git = "https://github.com/i509VCB/nxp-pac", rev = "b736e3038254d593024aaa1a5a7b1f95a5728538"} imxrt-rt = { version = "0.1.7", optional = true, features = ["device"] } [build-dependencies] cfg_aliases = "0.2.1" -nxp-pac = { version = "0.1.0", git = "https://github.com/IrinaCh524/nxp-pac", rev = "d452e366105012ddd8ba332a9ab86e1931508235", features = ["metadata"], optional = true } +nxp-pac = { version = "0.1.0", git = "https://github.com/i509VCB/nxp-pac", rev = "b736e3038254d593024aaa1a5a7b1f95a5728538", features = ["metadata"], optional = true } proc-macro2 = "1.0.95" quote = "1.0.15" [features] default = ["rt"] # Enable PACs as optional dependencies, since some chip families will use different pac crates (temporarily). -rt = ["lpc55-pac?/rt", "nxp-pac?/rt"] +rt = ["nxp-pac?/rt"] ## Enable [defmt support](https://docs.rs/defmt) and enables `defmt` debug-log messages and formatting in embassy drivers. defmt = ["dep:defmt", "embassy-hal-internal/defmt", "embassy-sync/defmt"] @@ -79,6 +78,6 @@ _rt1xxx = [] _time_driver = ["dep:embassy-time-driver", "dep:embassy-time-queue-utils"] #! ### Chip selection features -lpc55 = ["nxp-pac/lpc55s69_core0"] +lpc55 = ["nxp-pac/lpc55s69_cm33_core0"] mimxrt1011 = ["nxp-pac/mimxrt1011", "_rt1xxx", "dep:imxrt-rt"] mimxrt1062 = ["nxp-pac/mimxrt1062", "_rt1xxx", "dep:imxrt-rt"] From 547a52103b3c30506dc981fa89faa6c12765e97a Mon Sep 17 00:00:00 2001 From: Roi Bachynskyi Date: Wed, 10 Sep 2025 12:21:04 +0300 Subject: [PATCH 47/76] lpc55: added lpc55-core0 feature Co-authored-by: Irina Chiorean --- embassy-nxp/Cargo.toml | 6 +++--- embassy-nxp/src/gpio.rs | 2 +- embassy-nxp/src/lib.rs | 10 +++++----- embassy-nxp/src/usart.rs | 2 +- examples/lpc55s69/Cargo.toml | 2 +- 5 files changed, 11 insertions(+), 11 deletions(-) diff --git a/embassy-nxp/Cargo.toml b/embassy-nxp/Cargo.toml index dd132c7ba..455915f29 100644 --- a/embassy-nxp/Cargo.toml +++ b/embassy-nxp/Cargo.toml @@ -7,7 +7,7 @@ publish = false [package.metadata.embassy] build = [ - {target = "thumbv8m.main-none-eabihf", features = ["defmt", "lpc55"]}, + {target = "thumbv8m.main-none-eabihf", features = ["defmt", "lpc55-core0"]}, {target = "thumbv7em-none-eabihf", features = ["defmt", "mimxrt1011", "rt", "time-driver-pit"]}, {target = "thumbv7em-none-eabihf", features = ["defmt", "mimxrt1062", "rt", "time-driver-pit"]}, ] @@ -18,7 +18,7 @@ src_base_git = "https://github.com/embassy-rs/embassy/blob/$COMMIT/embassy-nxp/s features = ["defmt", "unstable-pac" ] # TODO: Add time-driver-any, as both lpc55 and mimxrt1xxx use different drivers. flavors = [ - { regex_feature = "lpc55", target = "thumbv8m.main-none-eabihf" }, + { regex_feature = "lpc55-core0", target = "thumbv8m.main-none-eabihf" }, { regex_feature = "mimxrt.*", target = "thumbv7em-none-eabihf" }, ] @@ -78,6 +78,6 @@ _rt1xxx = [] _time_driver = ["dep:embassy-time-driver", "dep:embassy-time-queue-utils"] #! ### Chip selection features -lpc55 = ["nxp-pac/lpc55s69_cm33_core0"] +lpc55-core0 = ["nxp-pac/lpc55s69_cm33_core0"] mimxrt1011 = ["nxp-pac/mimxrt1011", "_rt1xxx", "dep:imxrt-rt"] mimxrt1062 = ["nxp-pac/mimxrt1062", "_rt1xxx", "dep:imxrt-rt"] diff --git a/embassy-nxp/src/gpio.rs b/embassy-nxp/src/gpio.rs index 3049cc12d..717b38d96 100644 --- a/embassy-nxp/src/gpio.rs +++ b/embassy-nxp/src/gpio.rs @@ -1,7 +1,7 @@ //! General purpose input/output (GPIO) driver. #![macro_use] -#[cfg_attr(feature = "lpc55", path = "./gpio/lpc55.rs")] +#[cfg_attr(feature = "lpc55-core0", path = "./gpio/lpc55.rs")] #[cfg_attr(rt1xxx, path = "./gpio/rt1xxx.rs")] mod inner; pub use inner::*; diff --git a/embassy-nxp/src/lib.rs b/embassy-nxp/src/lib.rs index 3fcb14b7e..74142a10b 100644 --- a/embassy-nxp/src/lib.rs +++ b/embassy-nxp/src/lib.rs @@ -4,9 +4,9 @@ pub(crate) mod fmt; pub mod gpio; -#[cfg(feature = "lpc55")] +#[cfg(feature = "lpc55-core0")] pub mod pint; -#[cfg(feature = "lpc55")] +#[cfg(feature = "lpc55-core0")] pub mod usart; #[cfg(feature = "_time_driver")] @@ -15,7 +15,7 @@ pub mod usart; mod time_driver; // This mod MUST go last, so that it sees all the `impl_foo!` macros -#[cfg_attr(feature = "lpc55", path = "chips/lpc55.rs")] +#[cfg_attr(feature = "lpc55-core0", path = "chips/lpc55.rs")] #[cfg_attr(feature = "mimxrt1011", path = "chips/mimxrt1011.rs")] #[cfg_attr(feature = "mimxrt1062", path = "chips/mimxrt1062.rs")] mod chip; @@ -83,10 +83,10 @@ pub fn init(_config: config::Config) -> Peripherals { pac::CCM.ccgr6().modify(|v| v.set_cg0(1)); } - #[cfg(any(feature = "lpc55", rt1xxx))] + #[cfg(any(feature = "lpc55-core0", rt1xxx))] gpio::init(); - #[cfg(feature = "lpc55")] + #[cfg(feature = "lpc55-core0")] pint::init(); #[cfg(feature = "_time_driver")] diff --git a/embassy-nxp/src/usart.rs b/embassy-nxp/src/usart.rs index c426ab96d..1d8886f24 100644 --- a/embassy-nxp/src/usart.rs +++ b/embassy-nxp/src/usart.rs @@ -1,5 +1,5 @@ //! Universal Synchronous/Asynchronous Receiver/Transmitter (USART) driver. -#[cfg_attr(feature = "lpc55", path = "./usart/lpc55.rs")] +#[cfg_attr(feature = "lpc55-core0", path = "./usart/lpc55.rs")] mod inner; pub use inner::*; diff --git a/examples/lpc55s69/Cargo.toml b/examples/lpc55s69/Cargo.toml index 79b27f269..579748595 100644 --- a/examples/lpc55s69/Cargo.toml +++ b/examples/lpc55s69/Cargo.toml @@ -7,7 +7,7 @@ license = "MIT OR Apache-2.0" publish = false [dependencies] -embassy-nxp = { version = "0.1.0", path = "../../embassy-nxp", features = ["lpc55", "rt", "defmt", "time-driver-rtc"] } +embassy-nxp = { version = "0.1.0", path = "../../embassy-nxp", features = ["lpc55-core0", "rt", "defmt", "time-driver-rtc"] } embassy-executor = { version = "0.9.0", path = "../../embassy-executor", features = ["arch-cortex-m", "executor-thread", "executor-interrupt"] } embassy-sync = { version = "0.7.2", path = "../../embassy-sync", features = ["defmt"] } embassy-time = { version = "0.5.0", path = "../../embassy-time", features = ["defmt", "tick-hz-32_768"] } From a7e1bf2aff94ed0dd3d56848ccb9afe7a127054e Mon Sep 17 00:00:00 2001 From: Brian Schwind Date: Fri, 12 Sep 2025 20:02:08 +0900 Subject: [PATCH 48/76] Typo fixes --- embassy-stm32/src/ospi/mod.rs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/embassy-stm32/src/ospi/mod.rs b/embassy-stm32/src/ospi/mod.rs index 8384f4fc4..4df3b0042 100644 --- a/embassy-stm32/src/ospi/mod.rs +++ b/embassy-stm32/src/ospi/mod.rs @@ -113,7 +113,7 @@ pub struct TransferConfig { /// Data width (DMODE) pub dwidth: OspiWidth, - /// Data buffer + /// Data Double Transfer rate enable pub ddtr: bool, /// Number of dummy cycles (DCYC) @@ -467,11 +467,11 @@ impl<'d, T: Instance, M: PeriMode> Ospi<'d, T, M> { if let Some(data_length) = data_len { T::REGS.dlr().write(|v| { v.set_dl((data_length - 1) as u32); - }) + }); } else { T::REGS.dlr().write(|v| { v.set_dl((0) as u32); - }) + }); } // Configure instruction/address/data/communication modes @@ -491,7 +491,7 @@ impl<'d, T: Instance, M: PeriMode> Ospi<'d, T, M> { w.set_sioo(command.sioo); }); - // Set informationrequired to initiate transaction + // Set information required to initiate transaction if let Some(instruction) = command.instruction { if let Some(address) = command.address { T::REGS.ir().write(|v| { From 9c4df75940023456e92623700a9bb25fe6600196 Mon Sep 17 00:00:00 2001 From: Brian Schwind Date: Fri, 12 Sep 2025 20:02:33 +0900 Subject: [PATCH 49/76] Set the alternate bytes register to the correct value when configuring an Ospi command --- embassy-stm32/src/ospi/mod.rs | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/embassy-stm32/src/ospi/mod.rs b/embassy-stm32/src/ospi/mod.rs index 4df3b0042..cbd6c8d35 100644 --- a/embassy-stm32/src/ospi/mod.rs +++ b/embassy-stm32/src/ospi/mod.rs @@ -451,11 +451,6 @@ impl<'d, T: Instance, M: PeriMode> Ospi<'d, T, M> { // Configure alternate bytes if let Some(ab) = command.alternate_bytes { T::REGS.abr().write(|v| v.set_alternate(ab)); - T::REGS.ccr().modify(|w| { - w.set_abmode(PhaseMode::from_bits(command.abwidth.into())); - w.set_abdtr(command.abdtr); - w.set_absize(SizeInBits::from_bits(command.absize.into())); - }) } // Configure dummy cycles @@ -474,7 +469,7 @@ impl<'d, T: Instance, M: PeriMode> Ospi<'d, T, M> { }); } - // Configure instruction/address/data/communication modes + // Configure instruction/address/alternate bytes/data/communication modes T::REGS.ccr().modify(|w| { w.set_imode(PhaseMode::from_bits(command.iwidth.into())); w.set_idtr(command.idtr); @@ -484,6 +479,10 @@ impl<'d, T: Instance, M: PeriMode> Ospi<'d, T, M> { w.set_addtr(command.addtr); w.set_adsize(SizeInBits::from_bits(command.adsize.into())); + w.set_abmode(PhaseMode::from_bits(command.abwidth.into())); + w.set_abdtr(command.abdtr); + w.set_absize(SizeInBits::from_bits(command.absize.into())); + w.set_dmode(PhaseMode::from_bits(command.dwidth.into())); w.set_ddtr(command.ddtr); From 4a3e9e38e5fd0f5f2f576691154ecbbdc22eabab Mon Sep 17 00:00:00 2001 From: Brian Schwind Date: Sat, 13 Sep 2025 00:12:14 +0900 Subject: [PATCH 50/76] Apply fixes to HSPI as well --- embassy-stm32/src/hspi/mod.rs | 17 ++++++++--------- 1 file changed, 8 insertions(+), 9 deletions(-) diff --git a/embassy-stm32/src/hspi/mod.rs b/embassy-stm32/src/hspi/mod.rs index 62bc0e979..3b73062a2 100644 --- a/embassy-stm32/src/hspi/mod.rs +++ b/embassy-stm32/src/hspi/mod.rs @@ -116,7 +116,7 @@ pub struct TransferConfig { /// Data width (DMODE) pub dwidth: HspiWidth, - /// Data buffer + /// Data Double Transfer rate enable pub ddtr: bool, /// Number of dummy cycles (DCYC) @@ -395,11 +395,6 @@ impl<'d, T: Instance, M: PeriMode> Hspi<'d, T, M> { // Configure alternate bytes if let Some(ab) = command.alternate_bytes { T::REGS.abr().write(|v| v.set_alternate(ab)); - T::REGS.ccr().modify(|w| { - w.set_abmode(command.abwidth.into()); - w.set_abdtr(command.abdtr); - w.set_absize(command.absize.into()); - }) } // Configure dummy cycles @@ -411,14 +406,14 @@ impl<'d, T: Instance, M: PeriMode> Hspi<'d, T, M> { if let Some(data_length) = data_len { T::REGS.dlr().write(|v| { v.set_dl((data_length - 1) as u32); - }) + }); } else { T::REGS.dlr().write(|v| { v.set_dl((0) as u32); - }) + }); } - // Configure instruction/address/data modes + // Configure instruction/address/alternate bytes/data modes T::REGS.ccr().modify(|w| { w.set_imode(command.iwidth.into()); w.set_idtr(command.idtr); @@ -428,6 +423,10 @@ impl<'d, T: Instance, M: PeriMode> Hspi<'d, T, M> { w.set_addtr(command.addtr); w.set_adsize(command.adsize.into()); + w.set_abmode(command.abwidth.into()); + w.set_abdtr(command.abdtr); + w.set_absize(command.absize.into()); + w.set_dmode(command.dwidth.into()); w.set_ddtr(command.ddtr); }); From 881fee982005b36a73c3b09b69bda48f81603084 Mon Sep 17 00:00:00 2001 From: Brian Schwind Date: Sat, 13 Sep 2025 00:12:36 +0900 Subject: [PATCH 51/76] Apply fixes to XSPI as well --- embassy-stm32/src/xspi/mod.rs | 17 ++++++++--------- 1 file changed, 8 insertions(+), 9 deletions(-) diff --git a/embassy-stm32/src/xspi/mod.rs b/embassy-stm32/src/xspi/mod.rs index 60ccf3c97..5ae074a90 100644 --- a/embassy-stm32/src/xspi/mod.rs +++ b/embassy-stm32/src/xspi/mod.rs @@ -110,7 +110,7 @@ pub struct TransferConfig { /// Data width (DMODE) pub dwidth: XspiWidth, - /// Data buffer + /// Data Double Transfer rate enable pub ddtr: bool, /// Number of dummy cycles (DCYC) @@ -424,11 +424,6 @@ impl<'d, T: Instance, M: PeriMode> Xspi<'d, T, M> { // Configure alternate bytes if let Some(ab) = command.alternate_bytes { T::REGS.abr().write(|v| v.set_alternate(ab)); - T::REGS.ccr().modify(|w| { - w.set_abmode(CcrAbmode::from_bits(command.abwidth.into())); - w.set_abdtr(command.abdtr); - w.set_absize(CcrAbsize::from_bits(command.absize.into())); - }) } else { T::REGS.ccr().modify(|w| { // disable alternate bytes @@ -445,14 +440,14 @@ impl<'d, T: Instance, M: PeriMode> Xspi<'d, T, M> { if let Some(data_length) = data_len { T::REGS.dlr().write(|v| { v.set_dl((data_length - 1) as u32); - }) + }); } else { T::REGS.dlr().write(|v| { v.set_dl((0) as u32); - }) + }); } - // Configure instruction/address/data modes + // Configure instruction/address/alternate bytes/data modes T::REGS.ccr().modify(|w| { w.set_imode(CcrImode::from_bits(command.iwidth.into())); w.set_idtr(command.idtr); @@ -462,6 +457,10 @@ impl<'d, T: Instance, M: PeriMode> Xspi<'d, T, M> { w.set_addtr(command.addtr); w.set_adsize(CcrAdsize::from_bits(command.adsize.into())); + w.set_abmode(CcrAbmode::from_bits(command.abwidth.into())); + w.set_abdtr(command.abdtr); + w.set_absize(CcrAbsize::from_bits(command.absize.into())); + w.set_dmode(CcrDmode::from_bits(command.dwidth.into())); w.set_ddtr(command.ddtr); }); From ffe7f6b0a06bda06ab7bde395f6f74a56be8dbf4 Mon Sep 17 00:00:00 2001 From: Brian Schwind Date: Sat, 13 Sep 2025 00:27:30 +0900 Subject: [PATCH 52/76] Update embassy-stm32/CHANGELOG.md --- embassy-stm32/CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/embassy-stm32/CHANGELOG.md b/embassy-stm32/CHANGELOG.md index 4ea11b664..93a1f4f64 100644 --- a/embassy-stm32/CHANGELOG.md +++ b/embassy-stm32/CHANGELOG.md @@ -16,6 +16,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - fix: STM32: Prevent dropped DacChannel from disabling Dac peripheral if another DacChannel is still in scope ([#4577](https://github.com/embassy-rs/embassy/pull/4577)) - feat: Added support for more OctoSPI configurations (e.g. APS6408 RAM) ([#4581](https://github.com/embassy-rs/embassy/pull/4581)) - fix: stm32/usart: fix bug with blocking flush in buffered uart ([#4648](https://github.com/embassy-rs/embassy/pull/4648)) +- fix: stm32/(ospi/hspi/xspi): Fix the alternate bytes register config sticking around for subsequent writes ## 0.4.0 - 2025-08-26 From 78d5d3f2dde14fcbf4879de19076eb89d9b9ef8b Mon Sep 17 00:00:00 2001 From: Robert Zieba Date: Thu, 11 Sep 2025 14:40:29 -0700 Subject: [PATCH 53/76] Remove `Sized` bound from `MutexGuard::map` Since `MutexGuard` has `T: ?Sized`, `U` does not need to be restricted to `Sized` types. This now allows using `map` to cast from `MutexGuard<'_, M, ImplsTrait>` to `MutexGuard<'_, M, dyn Trait>`. --- embassy-sync/CHANGELOG.md | 1 + embassy-sync/src/mutex.rs | 4 ++-- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/embassy-sync/CHANGELOG.md b/embassy-sync/CHANGELOG.md index a53d5f5b1..242b8b7ab 100644 --- a/embassy-sync/CHANGELOG.md +++ b/embassy-sync/CHANGELOG.md @@ -8,6 +8,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## Unreleased - ReleaseDate - Fix wakers getting dropped by `Signal::reset` +- Remove `Sized` trait bound from `MutexGuard::map` ## 0.7.2 - 2025-08-26 diff --git a/embassy-sync/src/mutex.rs b/embassy-sync/src/mutex.rs index 4ce6dd987..aea682899 100644 --- a/embassy-sync/src/mutex.rs +++ b/embassy-sync/src/mutex.rs @@ -187,7 +187,7 @@ where T: ?Sized, { /// Returns a locked view over a portion of the locked data. - pub fn map(this: Self, fun: impl FnOnce(&mut T) -> &mut U) -> MappedMutexGuard<'a, M, U> { + pub fn map(this: Self, fun: impl FnOnce(&mut T) -> &mut U) -> MappedMutexGuard<'a, M, U> { let mutex = this.mutex; let value = fun(unsafe { &mut *this.mutex.inner.get() }); // Don't run the `drop` method for MutexGuard. The ownership of the underlying @@ -279,7 +279,7 @@ where T: ?Sized, { /// Returns a locked view over a portion of the locked data. - pub fn map(this: Self, fun: impl FnOnce(&mut T) -> &mut U) -> MappedMutexGuard<'a, M, U> { + pub fn map(this: Self, fun: impl FnOnce(&mut T) -> &mut U) -> MappedMutexGuard<'a, M, U> { let state = this.state; let value = fun(unsafe { &mut *this.value }); // Don't run the `drop` method for MutexGuard. The ownership of the underlying From 0ea3478fb5e4fcdcd86e439186794d126ed2eca4 Mon Sep 17 00:00:00 2001 From: Riceman2000 Date: Fri, 12 Sep 2025 12:47:47 -0400 Subject: [PATCH 54/76] Fix typo in PIO SPI examples --- examples/rp/src/bin/pio_spi.rs | 6 +++--- examples/rp/src/bin/pio_spi_async.rs | 6 +++--- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/examples/rp/src/bin/pio_spi.rs b/examples/rp/src/bin/pio_spi.rs index 4218327ec..c45aeac7d 100644 --- a/examples/rp/src/bin/pio_spi.rs +++ b/examples/rp/src/bin/pio_spi.rs @@ -27,9 +27,9 @@ async fn main(_spawner: Spawner) { // These pins are routed to different hardware SPI peripherals, but we can // use them together regardless - let mosi = p.PIN_6; // SPI0 SCLK - let miso = p.PIN_7; // SPI0 MOSI - let clk = p.PIN_8; // SPI1 MISO + let mosi = p.PIN_6; + let miso = p.PIN_7; + let clk = p.PIN_8; let pio::Pio { mut common, sm0, .. } = pio::Pio::new(p.PIO0, Irqs); diff --git a/examples/rp/src/bin/pio_spi_async.rs b/examples/rp/src/bin/pio_spi_async.rs index 18b57d26e..e7d9b0ecc 100644 --- a/examples/rp/src/bin/pio_spi_async.rs +++ b/examples/rp/src/bin/pio_spi_async.rs @@ -27,9 +27,9 @@ async fn main(_spawner: Spawner) { // These pins are routed to different hardware SPI peripherals, but we can // use them together regardless - let mosi = p.PIN_6; // SPI0 SCLK - let miso = p.PIN_7; // SPI0 MOSI - let clk = p.PIN_8; // SPI1 MISO + let mosi = p.PIN_6; + let miso = p.PIN_7; + let clk = p.PIN_8; let pio::Pio { mut common, sm0, .. } = pio::Pio::new(p.PIO0, Irqs); From f829ddd3b236b146701951004b41525de4633c9a Mon Sep 17 00:00:00 2001 From: Riceman2000 Date: Fri, 12 Sep 2025 12:47:55 -0400 Subject: [PATCH 55/76] Example first draft --- .../rp/src/bin/ethernet_w55rp20_tcp_server.rs | 145 ++++++++++++++++++ 1 file changed, 145 insertions(+) create mode 100644 examples/rp/src/bin/ethernet_w55rp20_tcp_server.rs diff --git a/examples/rp/src/bin/ethernet_w55rp20_tcp_server.rs b/examples/rp/src/bin/ethernet_w55rp20_tcp_server.rs new file mode 100644 index 000000000..6f4ba4a70 --- /dev/null +++ b/examples/rp/src/bin/ethernet_w55rp20_tcp_server.rs @@ -0,0 +1,145 @@ +//! This example implements a TCP client that attempts to connect to a host on port 1234 and send it some data once per second. +//! +//! Example written for the [`WIZnet W55RP20-EVB-Pico`](https://docs.wiznet.io/Product/ioNIC/W55RP20/w55rp20-evb-pico) board. +//! Note: the W55RP20 is a single package that contains both a RP2040 and the Wiznet W5500 ethernet +//! controller + +#![no_std] +#![no_main] + +use core::str::FromStr; + +use defmt::*; +use embassy_executor::Spawner; +use embassy_futures::yield_now; +use embassy_net::{Stack, StackResources}; +use embassy_net_wiznet::chip::W5500; +use embassy_net_wiznet::*; +use embassy_rp::clocks::RoscRng; +use embassy_rp::gpio::{Input, Level, Output, Pull}; +use embassy_rp::peripherals::PIO0; +use embassy_rp::pio_programs::spi::Spi; +use embassy_rp::spi::{Async, Config as SpiConfig}; +use embassy_rp::{bind_interrupts, pio}; +use embassy_time::{Delay, Duration, Timer}; +use embedded_hal_bus::spi::ExclusiveDevice; +use embedded_io_async::Write; +use static_cell::StaticCell; +use {defmt_rtt as _, panic_probe as _}; + +bind_interrupts!(struct Irqs { + PIO0_IRQ_0 => pio::InterruptHandler; +}); + +#[embassy_executor::task] +async fn ethernet_task( + runner: Runner< + 'static, + W5500, + ExclusiveDevice, Output<'static>, Delay>, + Input<'static>, + Output<'static>, + >, +) -> ! { + runner.run().await +} + +#[embassy_executor::task] +async fn net_task(mut runner: embassy_net::Runner<'static, Device<'static>>) -> ! { + runner.run().await +} + +#[embassy_executor::main] +async fn main(spawner: Spawner) { + let p = embassy_rp::init(Default::default()); + let mut rng = RoscRng; + let mut led = Output::new(p.PIN_19, Level::Low); + + // The W55RP20 uses a PIO unit for SPI communication, once the SPI bus has been formed using a + // PIO statemachine everything else is generally unchanged from the other examples that use the W5500 + let mosi = p.PIN_23; + let miso = p.PIN_22; + let clk = p.PIN_21; + + let pio::Pio { mut common, sm0, .. } = pio::Pio::new(p.PIO0, Irqs); + + // Construct an SPI driver backed by a PIO state machine + let mut spi_cfg = SpiConfig::default(); + spi_cfg.frequency = 50_000_000; + let spi = Spi::new(&mut common, sm0, clk, mosi, miso, p.DMA_CH0, p.DMA_CH1, spi_cfg); + + // Further control pins + let cs = Output::new(p.PIN_20, Level::High); + let w5500_int = Input::new(p.PIN_24, Pull::Up); + let w5500_reset = Output::new(p.PIN_25, Level::High); + + let mac_addr = [0x02, 0x00, 0x00, 0x00, 0x00, 0x00]; + static STATE: StaticCell> = StaticCell::new(); + let state = STATE.init(State::<8, 8>::new()); + let (device, runner) = embassy_net_wiznet::new( + mac_addr, + state, + ExclusiveDevice::new(spi, cs, Delay), + w5500_int, + w5500_reset, + ) + .await + .unwrap(); + spawner.spawn(unwrap!(ethernet_task(runner))); + + // Generate random seed + let seed = rng.next_u64(); + + // Init network stack + static RESOURCES: StaticCell> = StaticCell::new(); + let (stack, runner) = embassy_net::new( + device, + embassy_net::Config::dhcpv4(Default::default()), + RESOURCES.init(StackResources::new()), + seed, + ); + + // Launch network task + spawner.spawn(unwrap!(net_task(runner))); + + info!("Waiting for DHCP..."); + let cfg = wait_for_config(stack).await; + let local_addr = cfg.address.address(); + info!("IP address: {:?}", local_addr); + + let mut rx_buffer = [0; 4096]; + let mut tx_buffer = [0; 4096]; + loop { + let mut socket = embassy_net::tcp::TcpSocket::new(stack, &mut rx_buffer, &mut tx_buffer); + socket.set_timeout(Some(Duration::from_secs(10))); + + led.set_low(); + info!("Connecting..."); + let host_addr = embassy_net::Ipv4Address::from_str("192.168.1.110").unwrap(); + if let Err(e) = socket.connect((host_addr, 1234)).await { + warn!("connect error: {:?}", e); + continue; + } + info!("Connected to {:?}", socket.remote_endpoint()); + led.set_high(); + + let msg = b"Hello world!\n"; + loop { + if let Err(e) = socket.write_all(msg).await { + warn!("write error: {:?}", e); + break; + } + info!("txd: {}", core::str::from_utf8(msg).unwrap()); + Timer::after_secs(1).await; + } + } +} + +async fn wait_for_config(stack: Stack<'static>) -> embassy_net::StaticConfigV4 { + loop { + if let Some(config) = stack.config_v4() { + return config.clone(); + } + yield_now().await; + } +} From 139ee907755bfa7817001c3ebc4a38eaf31cf243 Mon Sep 17 00:00:00 2001 From: riceman2000 Date: Fri, 12 Sep 2025 17:17:24 -0400 Subject: [PATCH 56/76] Updated example --- .../rp/src/bin/ethernet_w55rp20_tcp_server.rs | 34 +++++++++++++------ 1 file changed, 23 insertions(+), 11 deletions(-) diff --git a/examples/rp/src/bin/ethernet_w55rp20_tcp_server.rs b/examples/rp/src/bin/ethernet_w55rp20_tcp_server.rs index 6f4ba4a70..17dc40aff 100644 --- a/examples/rp/src/bin/ethernet_w55rp20_tcp_server.rs +++ b/examples/rp/src/bin/ethernet_w55rp20_tcp_server.rs @@ -1,4 +1,5 @@ -//! This example implements a TCP client that attempts to connect to a host on port 1234 and send it some data once per second. +//! This example implements a TCP echo server on port 1234 and using DHCP. +//! Send it some data, you should see it echoed back and printed in the console. //! //! Example written for the [`WIZnet W55RP20-EVB-Pico`](https://docs.wiznet.io/Product/ioNIC/W55RP20/w55rp20-evb-pico) board. //! Note: the W55RP20 is a single package that contains both a RP2040 and the Wiznet W5500 ethernet @@ -65,7 +66,8 @@ async fn main(spawner: Spawner) { // Construct an SPI driver backed by a PIO state machine let mut spi_cfg = SpiConfig::default(); - spi_cfg.frequency = 50_000_000; + spi_cfg.frequency = 10_000_000; // The PIO SPI program is much less stable than the actual SPI + // peripheral, use higher speeds at your peril let spi = Spi::new(&mut common, sm0, clk, mosi, miso, p.DMA_CH0, p.DMA_CH1, spi_cfg); // Further control pins @@ -109,28 +111,38 @@ async fn main(spawner: Spawner) { let mut rx_buffer = [0; 4096]; let mut tx_buffer = [0; 4096]; + let mut buf = [0; 4096]; loop { let mut socket = embassy_net::tcp::TcpSocket::new(stack, &mut rx_buffer, &mut tx_buffer); socket.set_timeout(Some(Duration::from_secs(10))); led.set_low(); - info!("Connecting..."); - let host_addr = embassy_net::Ipv4Address::from_str("192.168.1.110").unwrap(); - if let Err(e) = socket.connect((host_addr, 1234)).await { - warn!("connect error: {:?}", e); + info!("Listening on TCP:1234..."); + if let Err(e) = socket.accept(1234).await { + warn!("accept error: {:?}", e); continue; } - info!("Connected to {:?}", socket.remote_endpoint()); + info!("Received connection from {:?}", socket.remote_endpoint()); led.set_high(); - let msg = b"Hello world!\n"; loop { - if let Err(e) = socket.write_all(msg).await { + let n = match socket.read(&mut buf).await { + Ok(0) => { + warn!("read EOF"); + break; + } + Ok(n) => n, + Err(e) => { + warn!("{:?}", e); + break; + } + }; + info!("rxd {}", core::str::from_utf8(&buf[..n]).unwrap()); + + if let Err(e) = socket.write_all(&buf[..n]).await { warn!("write error: {:?}", e); break; } - info!("txd: {}", core::str::from_utf8(msg).unwrap()); - Timer::after_secs(1).await; } } } From 6beb7e35a6bf2ae0e72a389b2dac6bde08e5dcd2 Mon Sep 17 00:00:00 2001 From: riceman2000 Date: Fri, 12 Sep 2025 22:52:53 -0400 Subject: [PATCH 57/76] Remove unused imports --- examples/rp/src/bin/ethernet_w55rp20_tcp_server.rs | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/examples/rp/src/bin/ethernet_w55rp20_tcp_server.rs b/examples/rp/src/bin/ethernet_w55rp20_tcp_server.rs index 17dc40aff..0d69b66c4 100644 --- a/examples/rp/src/bin/ethernet_w55rp20_tcp_server.rs +++ b/examples/rp/src/bin/ethernet_w55rp20_tcp_server.rs @@ -8,8 +8,6 @@ #![no_std] #![no_main] -use core::str::FromStr; - use defmt::*; use embassy_executor::Spawner; use embassy_futures::yield_now; @@ -22,7 +20,7 @@ use embassy_rp::peripherals::PIO0; use embassy_rp::pio_programs::spi::Spi; use embassy_rp::spi::{Async, Config as SpiConfig}; use embassy_rp::{bind_interrupts, pio}; -use embassy_time::{Delay, Duration, Timer}; +use embassy_time::{Delay, Duration}; use embedded_hal_bus::spi::ExclusiveDevice; use embedded_io_async::Write; use static_cell::StaticCell; From 8f10e3638d77cadf058b9083de09fc7189048b0b Mon Sep 17 00:00:00 2001 From: Matt Johnston Date: Sun, 14 Sep 2025 16:30:31 +0800 Subject: [PATCH 58/76] rp/pio: Add onewire strong pullups, parasite power DS18B20 sensors require a strong pullup to be applied for the duration of the temperature conversion, within 10us of the command. The rp2xxx pins have sufficient drive strength to use as the pullup (no external mosfet needed). Add a new write_bytes_pullup() that will apply the pullup after bytes are written. Existing read_bytes()/write_bytes() has no change to onewire timing. A pio_onewire_parasite example reads multiple sensors individually, applying the strong pullup. --- embassy-rp/CHANGELOG.md | 1 + embassy-rp/src/pio_programs/onewire.rs | 45 ++++++++++- examples/rp/src/bin/pio_onewire.rs | 1 + examples/rp/src/bin/pio_onewire_parasite.rs | 89 +++++++++++++++++++++ 4 files changed, 133 insertions(+), 3 deletions(-) create mode 100644 examples/rp/src/bin/pio_onewire_parasite.rs diff --git a/embassy-rp/CHANGELOG.md b/embassy-rp/CHANGELOG.md index b50d41dd1..841c9f068 100644 --- a/embassy-rp/CHANGELOG.md +++ b/embassy-rp/CHANGELOG.md @@ -10,6 +10,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Add PIO SPI - Add PIO I2S input +- Add PIO onewire parasite power strong pullup ## 0.8.0 - 2025-08-26 diff --git a/embassy-rp/src/pio_programs/onewire.rs b/embassy-rp/src/pio_programs/onewire.rs index 287ddab41..980d0fe5f 100644 --- a/embassy-rp/src/pio_programs/onewire.rs +++ b/embassy-rp/src/pio_programs/onewire.rs @@ -52,7 +52,8 @@ impl<'a, PIO: Instance> PioOneWireProgram<'a, PIO> { ; The low pulse was already done, we only need to delay and poll the bit in case we are reading write_1: - nop side 0 [( 6 / CLK) - 1] ; Delay before sampling the input pin + jmp y--, continue_1 side 0 [( 6 / CLK) - 1] ; Delay before sampling input. Always decrement y + continue_1: in pins, 1 side 0 [(48 / CLK) - 1] ; This writes the state of the pin into the ISR ; Fallthrough @@ -61,9 +62,24 @@ impl<'a, PIO: Instance> PioOneWireProgram<'a, PIO> { .wrap_target out x, 1 side 0 [(12 / CLK) - 1] ; Stalls if no data available in TX FIFO and OSR jmp x--, write_1 side 1 [( 6 / CLK) - 1] ; Do the always low part of a bit, jump to write_1 if we want to write a 1 bit - in null, 1 side 1 [(54 / CLK) - 1] ; Do the remainder of the low part of a 0 bit - ; This writes 0 into the ISR so that the shift count stays in sync + jmp y--, continue_0 side 1 [(48 / CLK) - 1] ; Do the remainder of the low part of a 0 bit + jmp pullup side 1 [( 6 / CLK) - 1] ; Remain low while jumping + continue_0: + in null, 1 side 1 [( 6 / CLK) - 1] ; This writes 0 into the ISR so that the shift count stays in sync .wrap + + ; Assume that strong pullup commands always have MSB (the last bit) = 0, + ; since the rising edge can be used to start the operation. + ; That's the case for DS18B20 (44h and 48h). + pullup: + set pins, 1 side 1[( 6 / CLK) - 1] ; Drive pin high output immediately. + ; Strong pullup must be within 10us of rise. + in null, 1 side 1[( 6 / CLK) - 1] ; Keep ISR in sync. Must occur after the y--. + out null, 8 side 1[( 6 / CLK) - 1] ; Wait for write_bytes_pullup() delay to complete. + ; The delay is hundreds of ms, so done externally. + set pins, 0 side 0[( 6 / CLK) - 1] ; Back to open drain, pin low when driven + in null, 8 side 1[( 6 / CLK) - 1] ; Inform write_bytes_pullup() it's ready + jmp next_bit side 0[( 6 / CLK) - 1] ; Continue "# ); @@ -98,6 +114,7 @@ impl<'d, PIO: Instance, const SM: usize> PioOneWire<'d, PIO, SM> { let mut cfg = Config::default(); cfg.use_program(&program.prg, &[&pin]); cfg.set_in_pins(&[&pin]); + cfg.set_set_pins(&[&pin]); let shift_cfg = ShiftConfig { auto_fill: true, @@ -146,6 +163,7 @@ impl<'d, PIO: Instance, const SM: usize> PioOneWire<'d, PIO, SM> { /// Write bytes to the onewire bus pub async fn write_bytes(&mut self, data: &[u8]) { + unsafe { self.sm.set_y(u32::MAX as u32) }; let (rx, tx) = self.sm.rx_tx(); for b in data { tx.wait_push(*b as u32).await; @@ -155,8 +173,29 @@ impl<'d, PIO: Instance, const SM: usize> PioOneWire<'d, PIO, SM> { } } + /// Write bytes to the onewire bus, then apply a strong pullup + pub async fn write_bytes_pullup(&mut self, data: &[u8], pullup_time: embassy_time::Duration) { + unsafe { self.sm.set_y(data.len() as u32 * 8 - 1) }; + let (rx, tx) = self.sm.rx_tx(); + for b in data { + tx.wait_push(*b as u32).await; + + // Empty the buffer that is being filled with every write + let _ = rx.wait_pull().await; + } + + // Perform the delay, usually hundreds of ms. + embassy_time::Timer::after(pullup_time).await; + + // Signal that delay has completed + tx.wait_push(0 as u32).await; + // Wait until it's back at 0 low, open drain + let _ = rx.wait_pull().await; + } + /// Read bytes from the onewire bus pub async fn read_bytes(&mut self, data: &mut [u8]) { + unsafe { self.sm.set_y(u32::MAX as u32) }; let (rx, tx) = self.sm.rx_tx(); for b in data { // Write all 1's so that we can read what the device responds diff --git a/examples/rp/src/bin/pio_onewire.rs b/examples/rp/src/bin/pio_onewire.rs index 379e2b8f9..102f13c45 100644 --- a/examples/rp/src/bin/pio_onewire.rs +++ b/examples/rp/src/bin/pio_onewire.rs @@ -1,4 +1,5 @@ //! This example shows how you can use PIO to read one or more `DS18B20` one-wire temperature sensors. +//! This uses externally powered sensors. For parasite power, see the pio_onewire_parasite.rs example. #![no_std] #![no_main] diff --git a/examples/rp/src/bin/pio_onewire_parasite.rs b/examples/rp/src/bin/pio_onewire_parasite.rs new file mode 100644 index 000000000..fd076dee0 --- /dev/null +++ b/examples/rp/src/bin/pio_onewire_parasite.rs @@ -0,0 +1,89 @@ +//! This example shows how you can use PIO to read one or more `DS18B20` +//! one-wire temperature sensors using parasite power. +//! It applies a strong pullup during conversion, see "Powering the DS18B20" in the datasheet. +//! For externally powered sensors, use the pio_onewire.rs example. + +#![no_std] +#![no_main] +use defmt::*; +use embassy_executor::Spawner; +use embassy_rp::bind_interrupts; +use embassy_rp::peripherals::PIO0; +use embassy_rp::pio::{InterruptHandler, Pio}; +use embassy_rp::pio_programs::onewire::{PioOneWire, PioOneWireProgram, PioOneWireSearch}; +use embassy_time::Duration; +use heapless::Vec; +use {defmt_rtt as _, panic_probe as _}; + +bind_interrupts!(struct Irqs { + PIO0_IRQ_0 => InterruptHandler; +}); + +#[embassy_executor::main] +async fn main(_spawner: Spawner) { + let p = embassy_rp::init(Default::default()); + let mut pio = Pio::new(p.PIO0, Irqs); + + let prg = PioOneWireProgram::new(&mut pio.common); + let mut onewire = PioOneWire::new(&mut pio.common, pio.sm0, p.PIN_2, &prg); + + info!("Starting onewire search"); + + let mut devices = Vec::::new(); + let mut search = PioOneWireSearch::new(); + for _ in 0..10 { + if !search.is_finished() { + if let Some(address) = search.next(&mut onewire).await { + if crc8(&address.to_le_bytes()) == 0 { + info!("Found address: {:x}", address); + let _ = devices.push(address); + } else { + warn!("Found invalid address: {:x}", address); + } + } + } + } + + info!("Search done, found {} devices", devices.len()); + + loop { + // Read all devices one by one + for device in &devices { + onewire.reset().await; + onewire.write_bytes(&[0x55]).await; // Match rom + onewire.write_bytes(&device.to_le_bytes()).await; + // 750 ms delay required for default 12-bit resolution. + onewire.write_bytes_pullup(&[0x44], Duration::from_millis(750)).await; + + onewire.reset().await; + onewire.write_bytes(&[0x55]).await; // Match rom + onewire.write_bytes(&device.to_le_bytes()).await; + onewire.write_bytes(&[0xBE]).await; // Read scratchpad + + let mut data = [0; 9]; + onewire.read_bytes(&mut data).await; + if crc8(&data) == 0 { + let temp = ((data[1] as u32) << 8 | data[0] as u32) as f32 / 16.; + info!("Read device {:x}: {} deg C", device, temp); + } else { + warn!("Reading device {:x} failed. {:02x}", device, data); + } + } + } +} + +fn crc8(data: &[u8]) -> u8 { + let mut crc = 0; + for b in data { + let mut data_byte = *b; + for _ in 0..8 { + let temp = (crc ^ data_byte) & 0x01; + crc >>= 1; + if temp != 0 { + crc ^= 0x8C; + } + data_byte >>= 1; + } + } + crc +} From 7c551b4fdfd7fcf410423355a3a1b3f92d5f65a6 Mon Sep 17 00:00:00 2001 From: Matt Johnston Date: Sun, 14 Sep 2025 16:38:20 +0800 Subject: [PATCH 59/76] rp/pio: Copy onewire examples from rp to rp235x The rp pio_onewire example was updated on cd27a8a06b0160d654ebed7b89ca473041710235 but not rp235x. Copy them to be the same. --- examples/rp235x/src/bin/pio_onewire.rs | 105 +++++++++--------- .../rp235x/src/bin/pio_onewire_parasite.rs | 89 +++++++++++++++ 2 files changed, 144 insertions(+), 50 deletions(-) create mode 100644 examples/rp235x/src/bin/pio_onewire_parasite.rs diff --git a/examples/rp235x/src/bin/pio_onewire.rs b/examples/rp235x/src/bin/pio_onewire.rs index 991510851..102f13c45 100644 --- a/examples/rp235x/src/bin/pio_onewire.rs +++ b/examples/rp235x/src/bin/pio_onewire.rs @@ -1,4 +1,5 @@ -//! This example shows how you can use PIO to read a `DS18B20` one-wire temperature sensor. +//! This example shows how you can use PIO to read one or more `DS18B20` one-wire temperature sensors. +//! This uses externally powered sensors. For parasite power, see the pio_onewire_parasite.rs example. #![no_std] #![no_main] @@ -6,9 +7,10 @@ use defmt::*; use embassy_executor::Spawner; use embassy_rp::bind_interrupts; use embassy_rp::peripherals::PIO0; -use embassy_rp::pio::{self, InterruptHandler, Pio}; -use embassy_rp::pio_programs::onewire::{PioOneWire, PioOneWireProgram}; +use embassy_rp::pio::{InterruptHandler, Pio}; +use embassy_rp::pio_programs::onewire::{PioOneWire, PioOneWireProgram, PioOneWireSearch}; use embassy_time::Timer; +use heapless::Vec; use {defmt_rtt as _, panic_probe as _}; bind_interrupts!(struct Irqs { @@ -21,63 +23,66 @@ async fn main(_spawner: Spawner) { let mut pio = Pio::new(p.PIO0, Irqs); let prg = PioOneWireProgram::new(&mut pio.common); - let onewire = PioOneWire::new(&mut pio.common, pio.sm0, p.PIN_2, &prg); + let mut onewire = PioOneWire::new(&mut pio.common, pio.sm0, p.PIN_2, &prg); - let mut sensor = Ds18b20::new(onewire); + info!("Starting onewire search"); + + let mut devices = Vec::::new(); + let mut search = PioOneWireSearch::new(); + for _ in 0..10 { + if !search.is_finished() { + if let Some(address) = search.next(&mut onewire).await { + if crc8(&address.to_le_bytes()) == 0 { + info!("Found addres: {:x}", address); + let _ = devices.push(address); + } else { + warn!("Found invalid address: {:x}", address); + } + } + } + } + + info!("Search done, found {} devices", devices.len()); loop { - sensor.start().await; // Start a new measurement + onewire.reset().await; + // Skip rom and trigger conversion, we can trigger all devices on the bus immediately + onewire.write_bytes(&[0xCC, 0x44]).await; + Timer::after_secs(1).await; // Allow 1s for the measurement to finish - match sensor.temperature().await { - Ok(temp) => info!("temp = {:?} deg C", temp), - _ => error!("sensor error"), + + // Read all devices one by one + for device in &devices { + onewire.reset().await; + onewire.write_bytes(&[0x55]).await; // Match rom + onewire.write_bytes(&device.to_le_bytes()).await; + onewire.write_bytes(&[0xBE]).await; // Read scratchpad + + let mut data = [0; 9]; + onewire.read_bytes(&mut data).await; + if crc8(&data) == 0 { + let temp = ((data[1] as u32) << 8 | data[0] as u32) as f32 / 16.; + info!("Read device {:x}: {} deg C", device, temp); + } else { + warn!("Reading device {:x} failed", device); + } } Timer::after_secs(1).await; } } -/// DS18B20 temperature sensor driver -pub struct Ds18b20<'d, PIO: pio::Instance, const SM: usize> { - wire: PioOneWire<'d, PIO, SM>, -} - -impl<'d, PIO: pio::Instance, const SM: usize> Ds18b20<'d, PIO, SM> { - pub fn new(wire: PioOneWire<'d, PIO, SM>) -> Self { - Self { wire } - } - - /// Calculate CRC8 of the data - fn crc8(data: &[u8]) -> u8 { - let mut temp; - let mut data_byte; - let mut crc = 0; - for b in data { - data_byte = *b; - for _ in 0..8 { - temp = (crc ^ data_byte) & 0x01; - crc >>= 1; - if temp != 0 { - crc ^= 0x8C; - } - data_byte >>= 1; +fn crc8(data: &[u8]) -> u8 { + let mut crc = 0; + for b in data { + let mut data_byte = *b; + for _ in 0..8 { + let temp = (crc ^ data_byte) & 0x01; + crc >>= 1; + if temp != 0 { + crc ^= 0x8C; } - } - crc - } - - /// Start a new measurement. Allow at least 1000ms before getting `temperature`. - pub async fn start(&mut self) { - self.wire.write_bytes(&[0xCC, 0x44]).await; - } - - /// Read the temperature. Ensure >1000ms has passed since `start` before calling this. - pub async fn temperature(&mut self) -> Result { - self.wire.write_bytes(&[0xCC, 0xBE]).await; - let mut data = [0; 9]; - self.wire.read_bytes(&mut data).await; - match Self::crc8(&data) == 0 { - true => Ok(((data[1] as u32) << 8 | data[0] as u32) as f32 / 16.), - false => Err(()), + data_byte >>= 1; } } + crc } diff --git a/examples/rp235x/src/bin/pio_onewire_parasite.rs b/examples/rp235x/src/bin/pio_onewire_parasite.rs new file mode 100644 index 000000000..fd076dee0 --- /dev/null +++ b/examples/rp235x/src/bin/pio_onewire_parasite.rs @@ -0,0 +1,89 @@ +//! This example shows how you can use PIO to read one or more `DS18B20` +//! one-wire temperature sensors using parasite power. +//! It applies a strong pullup during conversion, see "Powering the DS18B20" in the datasheet. +//! For externally powered sensors, use the pio_onewire.rs example. + +#![no_std] +#![no_main] +use defmt::*; +use embassy_executor::Spawner; +use embassy_rp::bind_interrupts; +use embassy_rp::peripherals::PIO0; +use embassy_rp::pio::{InterruptHandler, Pio}; +use embassy_rp::pio_programs::onewire::{PioOneWire, PioOneWireProgram, PioOneWireSearch}; +use embassy_time::Duration; +use heapless::Vec; +use {defmt_rtt as _, panic_probe as _}; + +bind_interrupts!(struct Irqs { + PIO0_IRQ_0 => InterruptHandler; +}); + +#[embassy_executor::main] +async fn main(_spawner: Spawner) { + let p = embassy_rp::init(Default::default()); + let mut pio = Pio::new(p.PIO0, Irqs); + + let prg = PioOneWireProgram::new(&mut pio.common); + let mut onewire = PioOneWire::new(&mut pio.common, pio.sm0, p.PIN_2, &prg); + + info!("Starting onewire search"); + + let mut devices = Vec::::new(); + let mut search = PioOneWireSearch::new(); + for _ in 0..10 { + if !search.is_finished() { + if let Some(address) = search.next(&mut onewire).await { + if crc8(&address.to_le_bytes()) == 0 { + info!("Found address: {:x}", address); + let _ = devices.push(address); + } else { + warn!("Found invalid address: {:x}", address); + } + } + } + } + + info!("Search done, found {} devices", devices.len()); + + loop { + // Read all devices one by one + for device in &devices { + onewire.reset().await; + onewire.write_bytes(&[0x55]).await; // Match rom + onewire.write_bytes(&device.to_le_bytes()).await; + // 750 ms delay required for default 12-bit resolution. + onewire.write_bytes_pullup(&[0x44], Duration::from_millis(750)).await; + + onewire.reset().await; + onewire.write_bytes(&[0x55]).await; // Match rom + onewire.write_bytes(&device.to_le_bytes()).await; + onewire.write_bytes(&[0xBE]).await; // Read scratchpad + + let mut data = [0; 9]; + onewire.read_bytes(&mut data).await; + if crc8(&data) == 0 { + let temp = ((data[1] as u32) << 8 | data[0] as u32) as f32 / 16.; + info!("Read device {:x}: {} deg C", device, temp); + } else { + warn!("Reading device {:x} failed. {:02x}", device, data); + } + } + } +} + +fn crc8(data: &[u8]) -> u8 { + let mut crc = 0; + for b in data { + let mut data_byte = *b; + for _ in 0..8 { + let temp = (crc ^ data_byte) & 0x01; + crc >>= 1; + if temp != 0 { + crc ^= 0x8C; + } + data_byte >>= 1; + } + } + crc +} From daae1fe5c9357ae97b897defae3d149eeafcc49f Mon Sep 17 00:00:00 2001 From: riceman2000 Date: Sun, 14 Sep 2025 11:30:22 -0400 Subject: [PATCH 60/76] Up SPI freq --- examples/rp/src/bin/ethernet_w55rp20_tcp_server.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/rp/src/bin/ethernet_w55rp20_tcp_server.rs b/examples/rp/src/bin/ethernet_w55rp20_tcp_server.rs index 0d69b66c4..f51df2df9 100644 --- a/examples/rp/src/bin/ethernet_w55rp20_tcp_server.rs +++ b/examples/rp/src/bin/ethernet_w55rp20_tcp_server.rs @@ -64,7 +64,7 @@ async fn main(spawner: Spawner) { // Construct an SPI driver backed by a PIO state machine let mut spi_cfg = SpiConfig::default(); - spi_cfg.frequency = 10_000_000; // The PIO SPI program is much less stable than the actual SPI + spi_cfg.frequency = 12_500_000; // The PIO SPI program is much less stable than the actual SPI // peripheral, use higher speeds at your peril let spi = Spi::new(&mut common, sm0, clk, mosi, miso, p.DMA_CH0, p.DMA_CH1, spi_cfg); From 1c080559fd20eb250e76278ff92d23432b5e0ce8 Mon Sep 17 00:00:00 2001 From: riceman2000 Date: Sun, 14 Sep 2025 14:14:59 -0400 Subject: [PATCH 61/76] Fix removed comments --- examples/rp/src/bin/pio_spi.rs | 6 +++--- examples/rp/src/bin/pio_spi_async.rs | 6 +++--- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/examples/rp/src/bin/pio_spi.rs b/examples/rp/src/bin/pio_spi.rs index c45aeac7d..4218327ec 100644 --- a/examples/rp/src/bin/pio_spi.rs +++ b/examples/rp/src/bin/pio_spi.rs @@ -27,9 +27,9 @@ async fn main(_spawner: Spawner) { // These pins are routed to different hardware SPI peripherals, but we can // use them together regardless - let mosi = p.PIN_6; - let miso = p.PIN_7; - let clk = p.PIN_8; + let mosi = p.PIN_6; // SPI0 SCLK + let miso = p.PIN_7; // SPI0 MOSI + let clk = p.PIN_8; // SPI1 MISO let pio::Pio { mut common, sm0, .. } = pio::Pio::new(p.PIO0, Irqs); diff --git a/examples/rp/src/bin/pio_spi_async.rs b/examples/rp/src/bin/pio_spi_async.rs index e7d9b0ecc..18b57d26e 100644 --- a/examples/rp/src/bin/pio_spi_async.rs +++ b/examples/rp/src/bin/pio_spi_async.rs @@ -27,9 +27,9 @@ async fn main(_spawner: Spawner) { // These pins are routed to different hardware SPI peripherals, but we can // use them together regardless - let mosi = p.PIN_6; - let miso = p.PIN_7; - let clk = p.PIN_8; + let mosi = p.PIN_6; // SPI0 SCLK + let miso = p.PIN_7; // SPI0 MOSI + let clk = p.PIN_8; // SPI1 MISO let pio::Pio { mut common, sm0, .. } = pio::Pio::new(p.PIO0, Irqs); From db1275358cfc33bb1ae3eeca47ddbfb73fe7cf48 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=BCha=20=C3=9Cn=C3=BCvar?= <87157627+phycrax@users.noreply.github.com> Date: Mon, 15 Sep 2025 18:05:30 +0800 Subject: [PATCH 62/76] add gpio speed to qspi config --- embassy-stm32/src/qspi/mod.rs | 51 ++++++++++++++++++----------------- 1 file changed, 27 insertions(+), 24 deletions(-) diff --git a/embassy-stm32/src/qspi/mod.rs b/embassy-stm32/src/qspi/mod.rs index c0cd216f0..0ed661115 100644 --- a/embassy-stm32/src/qspi/mod.rs +++ b/embassy-stm32/src/qspi/mod.rs @@ -62,6 +62,8 @@ pub struct Config { pub cs_high_time: ChipSelectHighTime, /// Shift sampling point of input data (none, or half-cycle) pub sample_shifting: SampleShifting, + /// GPIO Speed + pub gpio_speed: Speed, } impl Default for Config { @@ -73,6 +75,7 @@ impl Default for Config { fifo_threshold: FIFOThresholdLevel::_17Bytes, cs_high_time: ChipSelectHighTime::_5Cycle, sample_shifting: SampleShifting::None, + gpio_speed: Speed::VeryHigh, } } } @@ -286,14 +289,14 @@ impl<'d, T: Instance> Qspi<'d, T, Blocking> { ) -> Self { Self::new_inner( peri, - new_pin!(d0, AfType::output(OutputType::PushPull, Speed::VeryHigh)), - new_pin!(d1, AfType::output(OutputType::PushPull, Speed::VeryHigh)), - new_pin!(d2, AfType::output(OutputType::PushPull, Speed::VeryHigh)), - new_pin!(d3, AfType::output(OutputType::PushPull, Speed::VeryHigh)), - new_pin!(sck, AfType::output(OutputType::PushPull, Speed::VeryHigh)), + new_pin!(d0, AfType::output(OutputType::PushPull, config.gpio_speed)), + new_pin!(d1, AfType::output(OutputType::PushPull, config.gpio_speed)), + new_pin!(d2, AfType::output(OutputType::PushPull, config.gpio_speed)), + new_pin!(d3, AfType::output(OutputType::PushPull, config.gpio_speed)), + new_pin!(sck, AfType::output(OutputType::PushPull, config.gpio_speed)), new_pin!( nss, - AfType::output_pull(OutputType::PushPull, Speed::VeryHigh, Pull::Up) + AfType::output_pull(OutputType::PushPull, config.gpio_speed, Pull::Up) ), None, config, @@ -314,14 +317,14 @@ impl<'d, T: Instance> Qspi<'d, T, Blocking> { ) -> Self { Self::new_inner( peri, - new_pin!(d0, AfType::output(OutputType::PushPull, Speed::VeryHigh)), - new_pin!(d1, AfType::output(OutputType::PushPull, Speed::VeryHigh)), - new_pin!(d2, AfType::output(OutputType::PushPull, Speed::VeryHigh)), - new_pin!(d3, AfType::output(OutputType::PushPull, Speed::VeryHigh)), - new_pin!(sck, AfType::output(OutputType::PushPull, Speed::VeryHigh)), + new_pin!(d0, AfType::output(OutputType::PushPull, config.gpio_speed)), + new_pin!(d1, AfType::output(OutputType::PushPull, config.gpio_speed)), + new_pin!(d2, AfType::output(OutputType::PushPull, config.gpio_speed)), + new_pin!(d3, AfType::output(OutputType::PushPull, config.gpio_speed)), + new_pin!(sck, AfType::output(OutputType::PushPull, config.gpio_speed)), new_pin!( nss, - AfType::output_pull(OutputType::PushPull, Speed::VeryHigh, Pull::Up) + AfType::output_pull(OutputType::PushPull, config.gpio_speed, Pull::Up) ), None, config, @@ -345,14 +348,14 @@ impl<'d, T: Instance> Qspi<'d, T, Async> { ) -> Self { Self::new_inner( peri, - new_pin!(d0, AfType::output(OutputType::PushPull, Speed::VeryHigh)), - new_pin!(d1, AfType::output(OutputType::PushPull, Speed::VeryHigh)), - new_pin!(d2, AfType::output(OutputType::PushPull, Speed::VeryHigh)), - new_pin!(d3, AfType::output(OutputType::PushPull, Speed::VeryHigh)), - new_pin!(sck, AfType::output(OutputType::PushPull, Speed::VeryHigh)), + new_pin!(d0, AfType::output(OutputType::PushPull, config.gpio_speed)), + new_pin!(d1, AfType::output(OutputType::PushPull, config.gpio_speed)), + new_pin!(d2, AfType::output(OutputType::PushPull, config.gpio_speed)), + new_pin!(d3, AfType::output(OutputType::PushPull, config.gpio_speed)), + new_pin!(sck, AfType::output(OutputType::PushPull, config.gpio_speed)), new_pin!( nss, - AfType::output_pull(OutputType::PushPull, Speed::VeryHigh, Pull::Up) + AfType::output_pull(OutputType::PushPull, config.gpio_speed, Pull::Up) ), new_dma!(dma), config, @@ -374,14 +377,14 @@ impl<'d, T: Instance> Qspi<'d, T, Async> { ) -> Self { Self::new_inner( peri, - new_pin!(d0, AfType::output(OutputType::PushPull, Speed::VeryHigh)), - new_pin!(d1, AfType::output(OutputType::PushPull, Speed::VeryHigh)), - new_pin!(d2, AfType::output(OutputType::PushPull, Speed::VeryHigh)), - new_pin!(d3, AfType::output(OutputType::PushPull, Speed::VeryHigh)), - new_pin!(sck, AfType::output(OutputType::PushPull, Speed::VeryHigh)), + new_pin!(d0, AfType::output(OutputType::PushPull, config.gpio_speed)), + new_pin!(d1, AfType::output(OutputType::PushPull, config.gpio_speed)), + new_pin!(d2, AfType::output(OutputType::PushPull, config.gpio_speed)), + new_pin!(d3, AfType::output(OutputType::PushPull, config.gpio_speed)), + new_pin!(sck, AfType::output(OutputType::PushPull, config.gpio_speed)), new_pin!( nss, - AfType::output_pull(OutputType::PushPull, Speed::VeryHigh, Pull::Up) + AfType::output_pull(OutputType::PushPull, config.gpio_speed, Pull::Up) ), new_dma!(dma), config, From 9a4bdec392af3e0e60c190c035875485d2a11433 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=BCha=20=C3=9Cn=C3=BCvar?= <87157627+phycrax@users.noreply.github.com> Date: Mon, 15 Sep 2025 18:07:56 +0800 Subject: [PATCH 63/76] update changelog --- embassy-stm32/CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/embassy-stm32/CHANGELOG.md b/embassy-stm32/CHANGELOG.md index 93a1f4f64..eb48fd6fe 100644 --- a/embassy-stm32/CHANGELOG.md +++ b/embassy-stm32/CHANGELOG.md @@ -17,6 +17,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - feat: Added support for more OctoSPI configurations (e.g. APS6408 RAM) ([#4581](https://github.com/embassy-rs/embassy/pull/4581)) - fix: stm32/usart: fix bug with blocking flush in buffered uart ([#4648](https://github.com/embassy-rs/embassy/pull/4648)) - fix: stm32/(ospi/hspi/xspi): Fix the alternate bytes register config sticking around for subsequent writes +- feat: Configurable gpio speed for QSPI ## 0.4.0 - 2025-08-26 From 219754ff6c82b79a0870faeeb6a79b88e4b6901c Mon Sep 17 00:00:00 2001 From: dimi Date: Tue, 22 Jul 2025 18:37:04 +0200 Subject: [PATCH 64/76] derive Debug and Format for Averaging --- embassy-stm32/src/adc/adc4.rs | 3 ++- embassy-stm32/src/adc/c0.rs | 3 ++- embassy-stm32/src/adc/v3.rs | 2 ++ embassy-stm32/src/adc/v4.rs | 3 ++- 4 files changed, 8 insertions(+), 3 deletions(-) diff --git a/embassy-stm32/src/adc/adc4.rs b/embassy-stm32/src/adc/adc4.rs index 31cbdc0d7..255dc7956 100644 --- a/embassy-stm32/src/adc/adc4.rs +++ b/embassy-stm32/src/adc/adc4.rs @@ -83,7 +83,8 @@ pub enum DacChannel { } /// Number of samples used for averaging. -#[derive(Copy, Clone)] +#[derive(Copy, Clone, Debug)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] pub enum Averaging { Disabled, Samples2, diff --git a/embassy-stm32/src/adc/c0.rs b/embassy-stm32/src/adc/c0.rs index f5870801e..f2837a8f1 100644 --- a/embassy-stm32/src/adc/c0.rs +++ b/embassy-stm32/src/adc/c0.rs @@ -138,7 +138,8 @@ impl<'a> defmt::Format for Prescaler { /// Number of samples used for averaging. /// TODO: Implement hardware averaging setting. #[allow(unused)] -#[derive(Copy, Clone)] +#[derive(Copy, Clone, Debug)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] pub enum Averaging { Disabled, Samples2, diff --git a/embassy-stm32/src/adc/v3.rs b/embassy-stm32/src/adc/v3.rs index 77f24c87f..fdf76fd30 100644 --- a/embassy-stm32/src/adc/v3.rs +++ b/embassy-stm32/src/adc/v3.rs @@ -96,6 +96,8 @@ cfg_if! { } /// Number of samples used for averaging. +#[derive(Copy, Clone, Debug)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] pub enum Averaging { Disabled, Samples2, diff --git a/embassy-stm32/src/adc/v4.rs b/embassy-stm32/src/adc/v4.rs index b0871019a..b66437e6e 100644 --- a/embassy-stm32/src/adc/v4.rs +++ b/embassy-stm32/src/adc/v4.rs @@ -142,7 +142,8 @@ impl Prescaler { } /// Number of samples used for averaging. -#[derive(Copy, Clone)] +#[derive(Copy, Clone, Debug)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] pub enum Averaging { Disabled, Samples2, From 5ee77055a1d0073c3e5f312764acd566b1b92f84 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=BCha=20=C3=9Cn=C3=BCvar?= <87157627+phycrax@users.noreply.github.com> Date: Mon, 15 Sep 2025 18:43:23 +0800 Subject: [PATCH 65/76] fix examples --- examples/stm32f7/src/bin/qspi.rs | 16 ++++++++-------- examples/stm32h742/src/bin/qspi.rs | 16 ++++++++-------- examples/stm32l432/src/bin/qspi_mmap.rs | 16 ++++++++-------- 3 files changed, 24 insertions(+), 24 deletions(-) diff --git a/examples/stm32f7/src/bin/qspi.rs b/examples/stm32f7/src/bin/qspi.rs index ab29ddeff..80652b865 100644 --- a/examples/stm32f7/src/bin/qspi.rs +++ b/examples/stm32f7/src/bin/qspi.rs @@ -273,14 +273,14 @@ async fn main(_spawner: Spawner) -> ! { let p = embassy_stm32::init(config); info!("Embassy initialized"); - let config = QspiCfg { - memory_size: MemorySize::_8MiB, - address_size: AddressSize::_24bit, - prescaler: 16, - cs_high_time: ChipSelectHighTime::_1Cycle, - fifo_threshold: FIFOThresholdLevel::_16Bytes, - sample_shifting: SampleShifting::None, - }; + let mut config = QspiCfg::default(); + config.memory_size = MemorySize::_8MiB; + config.address_size = AddressSize::_24bit; + config.prescaler = 16; + config.cs_high_time = ChipSelectHighTime::_1Cycle; + config.fifo_threshold = FIFOThresholdLevel::_16Bytes; + config.sample_shifting = SampleShifting::None; + let driver = Qspi::new_bank1( p.QUADSPI, p.PF8, p.PF9, p.PE2, p.PF6, p.PF10, p.PB10, p.DMA2_CH7, config, ); diff --git a/examples/stm32h742/src/bin/qspi.rs b/examples/stm32h742/src/bin/qspi.rs index 50e37ec52..9e79d7089 100644 --- a/examples/stm32h742/src/bin/qspi.rs +++ b/examples/stm32h742/src/bin/qspi.rs @@ -266,14 +266,14 @@ async fn main(_spawner: Spawner) -> ! { let p = embassy_stm32::init(config); info!("Embassy initialized"); - let config = QspiCfg { - memory_size: MemorySize::_8MiB, - address_size: AddressSize::_24bit, - prescaler: 16, - cs_high_time: ChipSelectHighTime::_1Cycle, - fifo_threshold: FIFOThresholdLevel::_16Bytes, - sample_shifting: SampleShifting::None, - }; + let mut config = QspiCfg::default(); + config.memory_size = MemorySize::_8MiB; + config.address_size = AddressSize::_24bit; + config.prescaler = 16; + config.cs_high_time = ChipSelectHighTime::_1Cycle; + config.fifo_threshold = FIFOThresholdLevel::_16Bytes; + config.sample_shifting = SampleShifting::None; + let driver = Qspi::new_blocking_bank1(p.QUADSPI, p.PD11, p.PD12, p.PE2, p.PD13, p.PB2, p.PB10, config); let mut flash = FlashMemory::new(driver); let flash_id = flash.read_id(); diff --git a/examples/stm32l432/src/bin/qspi_mmap.rs b/examples/stm32l432/src/bin/qspi_mmap.rs index 075458fe5..feabdd532 100644 --- a/examples/stm32l432/src/bin/qspi_mmap.rs +++ b/examples/stm32l432/src/bin/qspi_mmap.rs @@ -246,14 +246,14 @@ const MEMORY_ADDR: u32 = 0x00000000 as u32; async fn main(_spawner: Spawner) { let p = embassy_stm32::init(Default::default()); - let config = qspi::Config { - memory_size: MemorySize::_16MiB, - address_size: AddressSize::_24bit, - prescaler: 200, - cs_high_time: ChipSelectHighTime::_1Cycle, - fifo_threshold: FIFOThresholdLevel::_16Bytes, - sample_shifting: SampleShifting::None, - }; + let mut config = qspi::Config::default(); + config.memory_size = MemorySize::_16MiB; + config.address_size = AddressSize::_24bit; + config.prescaler = 200; + config.cs_high_time = ChipSelectHighTime::_1Cycle; + config.fifo_threshold = FIFOThresholdLevel::_16Bytes; + config.sample_shifting = SampleShifting::None; + let driver = qspi::Qspi::new_bank1(p.QUADSPI, p.PB1, p.PB0, p.PA7, p.PA6, p.PA3, p.PA2, p.DMA2_CH7, config); let mut flash = FlashMemory::new(driver); let mut wr_buf = [0u8; 256]; From 77d82516131ca37a706e1965e44fca40854931c5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=BCha=20=C3=9Cn=C3=BCvar?= <87157627+phycrax@users.noreply.github.com> Date: Mon, 15 Sep 2025 18:44:52 +0800 Subject: [PATCH 66/76] tag config as non exhaustive --- embassy-stm32/src/qspi/mod.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/embassy-stm32/src/qspi/mod.rs b/embassy-stm32/src/qspi/mod.rs index 0ed661115..0644069a6 100644 --- a/embassy-stm32/src/qspi/mod.rs +++ b/embassy-stm32/src/qspi/mod.rs @@ -48,6 +48,7 @@ impl Default for TransferConfig { /// QSPI driver configuration. #[derive(Clone, Copy)] +#[non_exhaustive] pub struct Config { /// Flash memory size representend as 2^[0-32], as reasonable minimum 1KiB(9) was chosen. /// If you need other value the whose predefined use `Other` variant. From 83209b812c5bc5b5db3ee1624b1eff27c6a45cf6 Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Mon, 15 Sep 2025 14:03:12 +0200 Subject: [PATCH 67/76] Use new docserver bin to build docs. --- .github/ci/doc.sh | 68 +++++++++++++++++++++++------------------------ 1 file changed, 34 insertions(+), 34 deletions(-) diff --git a/.github/ci/doc.sh b/.github/ci/doc.sh index ac96008d8..7d6544e73 100755 --- a/.github/ci/doc.sh +++ b/.github/ci/doc.sh @@ -19,42 +19,42 @@ mv rust-toolchain-nightly.toml rust-toolchain.toml # which makes rustup very sad rustc --version > /dev/null -docserver-builder -i ./embassy-boot -o webroot/crates/embassy-boot/git.zup -docserver-builder -i ./embassy-boot-nrf -o webroot/crates/embassy-boot-nrf/git.zup -docserver-builder -i ./embassy-boot-rp -o webroot/crates/embassy-boot-rp/git.zup -docserver-builder -i ./embassy-boot-stm32 -o webroot/crates/embassy-boot-stm32/git.zup -docserver-builder -i ./embassy-embedded-hal -o webroot/crates/embassy-embedded-hal/git.zup -docserver-builder -i ./embassy-executor -o webroot/crates/embassy-executor/git.zup -docserver-builder -i ./embassy-futures -o webroot/crates/embassy-futures/git.zup -docserver-builder -i ./embassy-nrf -o webroot/crates/embassy-nrf/git.zup -docserver-builder -i ./embassy-rp -o webroot/crates/embassy-rp/git.zup -docserver-builder -i ./embassy-mspm0 -o webroot/crates/embassy-mspm0/git.zup -docserver-builder -i ./embassy-nxp -o webroot/crates/embassy-nxp/git.zup -docserver-builder -i ./embassy-sync -o webroot/crates/embassy-sync/git.zup -docserver-builder -i ./cyw43 -o webroot/crates/cyw43/git.zup -docserver-builder -i ./cyw43-pio -o webroot/crates/cyw43-pio/git.zup -docserver-builder -i ./embassy-stm32-wpan -o webroot/crates/embassy-stm32-wpan/git.zup --output-static webroot/static +docserver build -c -i ./embassy-boot -o webroot/crates/embassy-boot/git.zup +docserver build -c -i ./embassy-boot-nrf -o webroot/crates/embassy-boot-nrf/git.zup +docserver build -c -i ./embassy-boot-rp -o webroot/crates/embassy-boot-rp/git.zup +docserver build -c -i ./embassy-boot-stm32 -o webroot/crates/embassy-boot-stm32/git.zup +docserver build -c -i ./embassy-embedded-hal -o webroot/crates/embassy-embedded-hal/git.zup +docserver build -c -i ./embassy-executor -o webroot/crates/embassy-executor/git.zup +docserver build -c -i ./embassy-futures -o webroot/crates/embassy-futures/git.zup +docserver build -c -i ./embassy-nrf -o webroot/crates/embassy-nrf/git.zup +docserver build -c -i ./embassy-rp -o webroot/crates/embassy-rp/git.zup +docserver build -c -i ./embassy-mspm0 -o webroot/crates/embassy-mspm0/git.zup +docserver build -c -i ./embassy-nxp -o webroot/crates/embassy-nxp/git.zup +docserver build -c -i ./embassy-sync -o webroot/crates/embassy-sync/git.zup +docserver build -c -i ./cyw43 -o webroot/crates/cyw43/git.zup +docserver build -c -i ./cyw43-pio -o webroot/crates/cyw43-pio/git.zup +docserver build -c -i ./embassy-stm32-wpan -o webroot/crates/embassy-stm32-wpan/git.zup --output-static webroot/static -docserver-builder -i ./embassy-time -o webroot/crates/embassy-time/git.zup -docserver-builder -i ./embassy-time-driver -o webroot/crates/embassy-time-driver/git.zup -docserver-builder -i ./embassy-time-queue-utils -o webroot/crates/embassy-time-queue-utils/git.zup +docserver build -c -i ./embassy-time -o webroot/crates/embassy-time/git.zup +docserver build -c -i ./embassy-time-driver -o webroot/crates/embassy-time-driver/git.zup +docserver build -c -i ./embassy-time-queue-utils -o webroot/crates/embassy-time-queue-utils/git.zup -docserver-builder -i ./embassy-usb -o webroot/crates/embassy-usb/git.zup -docserver-builder -i ./embassy-usb-dfu -o webroot/crates/embassy-usb-dfu/git.zup -docserver-builder -i ./embassy-usb-driver -o webroot/crates/embassy-usb-driver/git.zup -docserver-builder -i ./embassy-usb-logger -o webroot/crates/embassy-usb-logger/git.zup -docserver-builder -i ./embassy-usb-synopsys-otg -o webroot/crates/embassy-usb-synopsys-otg/git.zup +docserver build -c -i ./embassy-usb -o webroot/crates/embassy-usb/git.zup +docserver build -c -i ./embassy-usb-dfu -o webroot/crates/embassy-usb-dfu/git.zup +docserver build -c -i ./embassy-usb-driver -o webroot/crates/embassy-usb-driver/git.zup +docserver build -c -i ./embassy-usb-logger -o webroot/crates/embassy-usb-logger/git.zup +docserver build -c -i ./embassy-usb-synopsys-otg -o webroot/crates/embassy-usb-synopsys-otg/git.zup -docserver-builder -i ./embassy-net -o webroot/crates/embassy-net/git.zup -docserver-builder -i ./embassy-net-nrf91 -o webroot/crates/embassy-net-nrf91/git.zup -docserver-builder -i ./embassy-net-driver -o webroot/crates/embassy-net-driver/git.zup -docserver-builder -i ./embassy-net-driver-channel -o webroot/crates/embassy-net-driver-channel/git.zup -docserver-builder -i ./embassy-net-wiznet -o webroot/crates/embassy-net-wiznet/git.zup -docserver-builder -i ./embassy-net-ppp -o webroot/crates/embassy-net-ppp/git.zup -docserver-builder -i ./embassy-net-tuntap -o webroot/crates/embassy-net-tuntap/git.zup -docserver-builder -i ./embassy-net-enc28j60 -o webroot/crates/embassy-net-enc28j60/git.zup -docserver-builder -i ./embassy-net-esp-hosted -o webroot/crates/embassy-net-esp-hosted/git.zup -docserver-builder -i ./embassy-net-adin1110 -o webroot/crates/embassy-net-adin1110/git.zup +docserver build -c -i ./embassy-net -o webroot/crates/embassy-net/git.zup +docserver build -c -i ./embassy-net-nrf91 -o webroot/crates/embassy-net-nrf91/git.zup +docserver build -c -i ./embassy-net-driver -o webroot/crates/embassy-net-driver/git.zup +docserver build -c -i ./embassy-net-driver-channel -o webroot/crates/embassy-net-driver-channel/git.zup +docserver build -c -i ./embassy-net-wiznet -o webroot/crates/embassy-net-wiznet/git.zup +docserver build -c -i ./embassy-net-ppp -o webroot/crates/embassy-net-ppp/git.zup +docserver build -c -i ./embassy-net-tuntap -o webroot/crates/embassy-net-tuntap/git.zup +docserver build -c -i ./embassy-net-enc28j60 -o webroot/crates/embassy-net-enc28j60/git.zup +docserver build -c -i ./embassy-net-esp-hosted -o webroot/crates/embassy-net-esp-hosted/git.zup +docserver build -c -i ./embassy-net-adin1110 -o webroot/crates/embassy-net-adin1110/git.zup export KUBECONFIG=/ci/secrets/kubeconfig.yml POD=$(kubectl -n embassy get po -l app=docserver -o jsonpath={.items[0].metadata.name}) @@ -65,6 +65,6 @@ kubectl cp webroot/static $POD:/data # so that it doesn't prevent other crates from getting docs updates when it breaks. rm -rf webroot -docserver-builder -i ./embassy-stm32 -o webroot/crates/embassy-stm32/git.zup +docserver build -c -i ./embassy-stm32 -o webroot/crates/embassy-stm32/git.zup POD=$(kubectl -n embassy get po -l app=docserver -o jsonpath={.items[0].metadata.name}) kubectl cp webroot/crates $POD:/data From 542ae72ca77f3af95ff0b2a62e1110728e72053d Mon Sep 17 00:00:00 2001 From: goodhoko Date: Tue, 16 Sep 2025 00:23:52 +0200 Subject: [PATCH 68/76] Unify derivation of Clone, Copy and defmt::format for all *SPI configs --- embassy-stm32/src/hspi/mod.rs | 2 ++ embassy-stm32/src/ospi/mod.rs | 3 +++ embassy-stm32/src/qspi/mod.rs | 2 ++ embassy-stm32/src/xspi/mod.rs | 3 +++ 4 files changed, 10 insertions(+) diff --git a/embassy-stm32/src/hspi/mod.rs b/embassy-stm32/src/hspi/mod.rs index 3b73062a2..95d9e5099 100644 --- a/embassy-stm32/src/hspi/mod.rs +++ b/embassy-stm32/src/hspi/mod.rs @@ -86,6 +86,8 @@ impl Default for Config { } /// HSPI transfer configuration. +#[derive(Clone, Copy)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] pub struct TransferConfig { /// Instruction width (IMODE) pub iwidth: HspiWidth, diff --git a/embassy-stm32/src/ospi/mod.rs b/embassy-stm32/src/ospi/mod.rs index cbd6c8d35..eebaf5573 100644 --- a/embassy-stm32/src/ospi/mod.rs +++ b/embassy-stm32/src/ospi/mod.rs @@ -23,6 +23,7 @@ use crate::{peripherals, Peri}; /// OPSI driver config. #[derive(Clone, Copy)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] pub struct Config { /// Fifo threshold used by the peripheral to generate the interrupt indicating data /// or space is available in the FIFO @@ -83,6 +84,8 @@ impl Default for Config { } /// OSPI transfer configuration. +#[derive(Clone, Copy)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] pub struct TransferConfig { /// Instruction width (IMODE) pub iwidth: OspiWidth, diff --git a/embassy-stm32/src/qspi/mod.rs b/embassy-stm32/src/qspi/mod.rs index 0644069a6..b03cd9009 100644 --- a/embassy-stm32/src/qspi/mod.rs +++ b/embassy-stm32/src/qspi/mod.rs @@ -18,6 +18,7 @@ use crate::{peripherals, Peri}; /// QSPI transfer configuration. #[derive(Clone, Copy)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] pub struct TransferConfig { /// Instruction width (IMODE) pub iwidth: QspiWidth, @@ -48,6 +49,7 @@ impl Default for TransferConfig { /// QSPI driver configuration. #[derive(Clone, Copy)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] #[non_exhaustive] pub struct Config { /// Flash memory size representend as 2^[0-32], as reasonable minimum 1KiB(9) was chosen. diff --git a/embassy-stm32/src/xspi/mod.rs b/embassy-stm32/src/xspi/mod.rs index 5ae074a90..901569f64 100644 --- a/embassy-stm32/src/xspi/mod.rs +++ b/embassy-stm32/src/xspi/mod.rs @@ -23,6 +23,7 @@ use crate::{peripherals, Peri}; /// XPSI driver config. #[derive(Clone, Copy)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] pub struct Config { /// Fifo threshold used by the peripheral to generate the interrupt indicating data /// or space is available in the FIFO @@ -80,6 +81,8 @@ impl Default for Config { } /// XSPI transfer configuration. +#[derive(Clone, Copy)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] pub struct TransferConfig { /// Instruction width (IMODE) pub iwidth: XspiWidth, From aec887fe0a08c4d67181a8496a8a0acfd667fef4 Mon Sep 17 00:00:00 2001 From: goodhoko Date: Tue, 16 Sep 2025 00:39:22 +0200 Subject: [PATCH 69/76] Don't forget to update the changelog (thanks CI!) --- embassy-stm32/CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/embassy-stm32/CHANGELOG.md b/embassy-stm32/CHANGELOG.md index eb48fd6fe..624845b3a 100644 --- a/embassy-stm32/CHANGELOG.md +++ b/embassy-stm32/CHANGELOG.md @@ -18,6 +18,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - fix: stm32/usart: fix bug with blocking flush in buffered uart ([#4648](https://github.com/embassy-rs/embassy/pull/4648)) - fix: stm32/(ospi/hspi/xspi): Fix the alternate bytes register config sticking around for subsequent writes - feat: Configurable gpio speed for QSPI +- feat: derive Clone, Copy and defmt::Format for all *SPI-related configs ## 0.4.0 - 2025-08-26 From 759ea1deb96f2070b09b6d812538c4ffd76e8477 Mon Sep 17 00:00:00 2001 From: goodhoko Date: Tue, 16 Sep 2025 00:45:48 +0200 Subject: [PATCH 70/76] It's "derive" all the way down --- embassy-stm32/src/ospi/enums.rs | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/embassy-stm32/src/ospi/enums.rs b/embassy-stm32/src/ospi/enums.rs index 4021f7ce3..4db801752 100644 --- a/embassy-stm32/src/ospi/enums.rs +++ b/embassy-stm32/src/ospi/enums.rs @@ -23,6 +23,7 @@ impl Into for OspiMode { /// Ospi lane width #[allow(dead_code)] #[derive(Copy, Clone)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] pub enum OspiWidth { /// None NONE, @@ -71,6 +72,7 @@ impl Into for FlashSelection { #[allow(dead_code)] #[allow(missing_docs)] #[derive(Copy, Clone)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] pub enum WrapSize { None, _16Bytes, @@ -95,6 +97,7 @@ impl Into for WrapSize { #[allow(missing_docs)] #[allow(dead_code)] #[derive(Copy, Clone)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] pub enum MemoryType { Micron, Macronix, @@ -120,6 +123,7 @@ impl Into for MemoryType { /// Ospi memory size. #[allow(missing_docs)] #[derive(Copy, Clone)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] pub enum MemorySize { _1KiB, _2KiB, @@ -180,6 +184,7 @@ impl Into for MemorySize { /// Ospi Address size #[derive(Copy, Clone)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] pub enum AddressSize { /// 8-bit address _8Bit, @@ -205,6 +210,7 @@ impl Into for AddressSize { /// Time the Chip Select line stays high. #[allow(missing_docs)] #[derive(Copy, Clone)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] pub enum ChipSelectHighTime { _1Cycle, _2Cycle, @@ -234,6 +240,7 @@ impl Into for ChipSelectHighTime { /// FIFO threshold. #[allow(missing_docs)] #[derive(Copy, Clone)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] pub enum FIFOThresholdLevel { _1Bytes, _2Bytes, @@ -311,6 +318,7 @@ impl Into for FIFOThresholdLevel { /// Dummy cycle count #[allow(missing_docs)] #[derive(Copy, Clone)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] pub enum DummyCycles { _0, _1, From 337da33c7a127b083652d635d956f03d79a952bc Mon Sep 17 00:00:00 2001 From: goodhoko Date: Tue, 16 Sep 2025 00:52:59 +0200 Subject: [PATCH 71/76] Also derive for enums that aren't available under my feature-set --- embassy-stm32/src/qspi/enums.rs | 7 +++++++ embassy-stm32/src/xspi/enums.rs | 8 ++++++++ 2 files changed, 15 insertions(+) diff --git a/embassy-stm32/src/qspi/enums.rs b/embassy-stm32/src/qspi/enums.rs index fa5e36d06..8a8420127 100644 --- a/embassy-stm32/src/qspi/enums.rs +++ b/embassy-stm32/src/qspi/enums.rs @@ -23,6 +23,7 @@ impl From for u8 { /// QSPI lane width #[allow(dead_code)] #[derive(Copy, Clone)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] pub enum QspiWidth { /// None NONE, @@ -67,6 +68,7 @@ impl From for bool { /// QSPI memory size. #[allow(missing_docs)] #[derive(Copy, Clone)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] pub enum MemorySize { _1KiB, _2KiB, @@ -127,6 +129,7 @@ impl From for u8 { /// QSPI Address size #[derive(Copy, Clone)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] pub enum AddressSize { /// 8-bit address _8Bit, @@ -152,6 +155,7 @@ impl From for u8 { /// Time the Chip Select line stays high. #[allow(missing_docs)] #[derive(Copy, Clone)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] pub enum ChipSelectHighTime { _1Cycle, _2Cycle, @@ -181,6 +185,7 @@ impl From for u8 { /// FIFO threshold. #[allow(missing_docs)] #[derive(Copy, Clone)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] pub enum FIFOThresholdLevel { _1Bytes, _2Bytes, @@ -258,6 +263,7 @@ impl From for u8 { /// Dummy cycle count #[allow(missing_docs)] #[derive(Copy, Clone)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] pub enum DummyCycles { _0, _1, @@ -334,6 +340,7 @@ impl From for u8 { #[allow(missing_docs)] #[derive(Copy, Clone)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] pub enum SampleShifting { None, HalfCycle, diff --git a/embassy-stm32/src/xspi/enums.rs b/embassy-stm32/src/xspi/enums.rs index c96641180..2e510fada 100644 --- a/embassy-stm32/src/xspi/enums.rs +++ b/embassy-stm32/src/xspi/enums.rs @@ -22,6 +22,7 @@ impl Into for XspiMode { /// Xspi lane width #[derive(Copy, Clone)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] pub enum XspiWidth { /// None NONE, @@ -50,6 +51,7 @@ impl Into for XspiWidth { /// Wrap Size #[allow(missing_docs)] #[derive(Copy, Clone)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] pub enum WrapSize { None, _16Bytes, @@ -73,6 +75,7 @@ impl Into for WrapSize { /// Memory Type #[allow(missing_docs)] #[derive(Copy, Clone)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] pub enum MemoryType { Micron, Macronix, @@ -98,6 +101,7 @@ impl Into for MemoryType { /// Xspi memory size. #[allow(missing_docs)] #[derive(Copy, Clone)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] pub enum MemorySize { _1KiB, _2KiB, @@ -158,6 +162,7 @@ impl Into for MemorySize { /// Xspi Address size #[derive(Copy, Clone)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] pub enum AddressSize { /// 8-bit address _8bit, @@ -183,6 +188,7 @@ impl Into for AddressSize { /// Time the Chip Select line stays high. #[allow(missing_docs)] #[derive(Copy, Clone)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] pub enum ChipSelectHighTime { _1Cycle, _2Cycle, @@ -212,6 +218,7 @@ impl Into for ChipSelectHighTime { /// FIFO threshold. #[allow(missing_docs)] #[derive(Copy, Clone)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] pub enum FIFOThresholdLevel { _1Bytes, _2Bytes, @@ -289,6 +296,7 @@ impl Into for FIFOThresholdLevel { /// Dummy cycle count #[allow(missing_docs)] #[derive(Copy, Clone)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] pub enum DummyCycles { _0, _1, From 8ed1e796bad7b3dbf6e78ea5e5d89b7257571d06 Mon Sep 17 00:00:00 2001 From: goodhoko Date: Mon, 15 Sep 2025 15:34:07 +0200 Subject: [PATCH 72/76] Handle OSPI address errors We were using OSPI in indirect mode via DMA and noticed that the transfer future would never resolve. It was forever busy-looping in `finish_dma()` on the `while !regs.sr().read().tcf() {}` line. After some debugging we noticed that the the `TEF` flag is set. The data sheet says the following about this flag: > The following errors set the TEF flag in OCTOSPI_SR and generates an interrupt if enabled (TEIE = 1 in OCTOSPI_CR): > - in indirect or automatic status-polling mode, when a wrong address has been programmed in OCTOSPI_AR (according to the device size defined by DEVSIZE[4:0]). > - in indirect mode, if the address plus the data length exceed the device size: TEF is set as soon as the access is triggered. Indeed we were configuring our device size to 0 while specifying a non-zero address. Detect this condition and return an error early - as soon as we configure the registers (which, according to the data sheet, should be enough to raise the flag) Also document this behavior on the respective TransferConfig and Config fields. Testing ------- See https://github.com/goodhoko/spi-error-test/blob/main/src/main.rs --- embassy-stm32/src/ospi/mod.rs | 21 ++++++++++++++++++--- 1 file changed, 18 insertions(+), 3 deletions(-) diff --git a/embassy-stm32/src/ospi/mod.rs b/embassy-stm32/src/ospi/mod.rs index eebaf5573..c291a311d 100644 --- a/embassy-stm32/src/ospi/mod.rs +++ b/embassy-stm32/src/ospi/mod.rs @@ -31,7 +31,9 @@ pub struct Config { /// Indicates the type of external device connected pub memory_type: MemoryType, // Need to add an additional enum to provide this public interface /// Defines the size of the external device connected to the OSPI corresponding - /// to the number of address bits required to access the device + /// to the number of address bits required to access the device. + /// When using indirect mode, [`TransferConfig::address`] + the length of the data being read + /// or written must fit within the configured `device_size`, otherwise an error is returned. pub device_size: MemorySize, /// Sets the minimum number of clock cycles that the chip select signal must be held high /// between commands @@ -95,10 +97,11 @@ pub struct TransferConfig { pub isize: AddressSize, /// Instruction Double Transfer rate enable pub idtr: bool, - /// Address width (ADMODE) pub adwidth: OspiWidth, - /// Device memory address + /// Device memory address. + /// In indirect mode, this value + the length of the data being read or written must be within + /// configured [`Config::device_size`], otherwise the transfer returns an error. pub address: Option, /// Number of Address Bytes pub adsize: AddressSize, @@ -528,6 +531,18 @@ impl<'d, T: Instance, M: PeriMode> Ospi<'d, T, M> { } } + // The following errors set the TEF flag in OCTOSPI_SR register: + // - in indirect or automatic status-polling mode, when a wrong address has been programmed + // in OCTOSPI_AR (according to the device size defined by DEVSIZE[4:0]) + // - in indirect mode, if the address plus the data length exceed the device size: TEF is + // set as soon as the access is triggered. + if T::REGS.sr().read().tef() { + // Clear the TEF register to make it ready for the next transfer. + T::REGS.fcr().write(|w| w.set_ctef(true)); + + return Err(OspiError::InvalidCommand); + } + Ok(()) } From 9ae76cbad6fc2bf4d00c731d98df0563241b1de8 Mon Sep 17 00:00:00 2001 From: goodhoko Date: Tue, 16 Sep 2025 09:49:01 +0200 Subject: [PATCH 73/76] Don't forget to update the changelog (again) --- embassy-stm32/CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/embassy-stm32/CHANGELOG.md b/embassy-stm32/CHANGELOG.md index 624845b3a..e3f18ca0d 100644 --- a/embassy-stm32/CHANGELOG.md +++ b/embassy-stm32/CHANGELOG.md @@ -19,6 +19,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - fix: stm32/(ospi/hspi/xspi): Fix the alternate bytes register config sticking around for subsequent writes - feat: Configurable gpio speed for QSPI - feat: derive Clone, Copy and defmt::Format for all *SPI-related configs +- fix: handle address and data-length errors in OSPI ## 0.4.0 - 2025-08-26 From e72e17ded8f5f9f10eb68cc785101e5c7dab73ef Mon Sep 17 00:00:00 2001 From: Brian Schwind Date: Fri, 12 Sep 2025 20:03:00 +0900 Subject: [PATCH 74/76] Write data with the Ospi peripheral in chunks to respect the max DMA transfer size --- embassy-stm32/src/ospi/mod.rs | 38 ++++++++++++++++++++--------------- 1 file changed, 22 insertions(+), 16 deletions(-) diff --git a/embassy-stm32/src/ospi/mod.rs b/embassy-stm32/src/ospi/mod.rs index c291a311d..d93cecb69 100644 --- a/embassy-stm32/src/ospi/mod.rs +++ b/embassy-stm32/src/ospi/mod.rs @@ -1198,16 +1198,19 @@ impl<'d, T: Instance> Ospi<'d, T, Async> { .cr() .modify(|v| v.set_fmode(vals::FunctionalMode::INDIRECT_WRITE)); - let transfer = unsafe { - self.dma - .as_mut() - .unwrap() - .write(buf, T::REGS.dr().as_ptr() as *mut W, Default::default()) - }; + // TODO: implement this using a LinkedList DMA to offload the whole transfer off the CPU. + for chunk in buf.chunks(0xFFFF) { + let transfer = unsafe { + self.dma + .as_mut() + .unwrap() + .write(chunk, T::REGS.dr().as_ptr() as *mut W, Default::default()) + }; - T::REGS.cr().modify(|w| w.set_dmaen(true)); + T::REGS.cr().modify(|w| w.set_dmaen(true)); - transfer.blocking_wait(); + transfer.blocking_wait(); + } finish_dma(T::REGS); @@ -1268,16 +1271,19 @@ impl<'d, T: Instance> Ospi<'d, T, Async> { .cr() .modify(|v| v.set_fmode(vals::FunctionalMode::INDIRECT_WRITE)); - let transfer = unsafe { - self.dma - .as_mut() - .unwrap() - .write(buf, T::REGS.dr().as_ptr() as *mut W, Default::default()) - }; + // TODO: implement this using a LinkedList DMA to offload the whole transfer off the CPU. + for chunk in buf.chunks(0xFFFF) { + let transfer = unsafe { + self.dma + .as_mut() + .unwrap() + .write(chunk, T::REGS.dr().as_ptr() as *mut W, Default::default()) + }; - T::REGS.cr().modify(|w| w.set_dmaen(true)); + T::REGS.cr().modify(|w| w.set_dmaen(true)); - transfer.await; + transfer.await; + } finish_dma(T::REGS); From 97462c07ce3d5797dfcefb15dd4b50383a850638 Mon Sep 17 00:00:00 2001 From: goodhoko Date: Tue, 16 Sep 2025 14:39:45 +0200 Subject: [PATCH 75/76] Aaaaaand the changelog of course --- embassy-stm32/CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/embassy-stm32/CHANGELOG.md b/embassy-stm32/CHANGELOG.md index e3f18ca0d..253b4796d 100644 --- a/embassy-stm32/CHANGELOG.md +++ b/embassy-stm32/CHANGELOG.md @@ -20,6 +20,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - feat: Configurable gpio speed for QSPI - feat: derive Clone, Copy and defmt::Format for all *SPI-related configs - fix: handle address and data-length errors in OSPI +- feat: Allow OSPI DMA writes larger than 64kB using chunking ## 0.4.0 - 2025-08-26 From 017064138003fa38b52f11dba872a43d4fec8b61 Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Tue, 16 Sep 2025 16:01:41 +0200 Subject: [PATCH 76/76] ci: docserver -c flag doesn't exist anymore --- .github/ci/doc.sh | 68 +++++++++++++++++++++++------------------------ 1 file changed, 34 insertions(+), 34 deletions(-) diff --git a/.github/ci/doc.sh b/.github/ci/doc.sh index 7d6544e73..876c261a1 100755 --- a/.github/ci/doc.sh +++ b/.github/ci/doc.sh @@ -19,42 +19,42 @@ mv rust-toolchain-nightly.toml rust-toolchain.toml # which makes rustup very sad rustc --version > /dev/null -docserver build -c -i ./embassy-boot -o webroot/crates/embassy-boot/git.zup -docserver build -c -i ./embassy-boot-nrf -o webroot/crates/embassy-boot-nrf/git.zup -docserver build -c -i ./embassy-boot-rp -o webroot/crates/embassy-boot-rp/git.zup -docserver build -c -i ./embassy-boot-stm32 -o webroot/crates/embassy-boot-stm32/git.zup -docserver build -c -i ./embassy-embedded-hal -o webroot/crates/embassy-embedded-hal/git.zup -docserver build -c -i ./embassy-executor -o webroot/crates/embassy-executor/git.zup -docserver build -c -i ./embassy-futures -o webroot/crates/embassy-futures/git.zup -docserver build -c -i ./embassy-nrf -o webroot/crates/embassy-nrf/git.zup -docserver build -c -i ./embassy-rp -o webroot/crates/embassy-rp/git.zup -docserver build -c -i ./embassy-mspm0 -o webroot/crates/embassy-mspm0/git.zup -docserver build -c -i ./embassy-nxp -o webroot/crates/embassy-nxp/git.zup -docserver build -c -i ./embassy-sync -o webroot/crates/embassy-sync/git.zup -docserver build -c -i ./cyw43 -o webroot/crates/cyw43/git.zup -docserver build -c -i ./cyw43-pio -o webroot/crates/cyw43-pio/git.zup -docserver build -c -i ./embassy-stm32-wpan -o webroot/crates/embassy-stm32-wpan/git.zup --output-static webroot/static +docserver build -i ./embassy-boot -o webroot/crates/embassy-boot/git.zup +docserver build -i ./embassy-boot-nrf -o webroot/crates/embassy-boot-nrf/git.zup +docserver build -i ./embassy-boot-rp -o webroot/crates/embassy-boot-rp/git.zup +docserver build -i ./embassy-boot-stm32 -o webroot/crates/embassy-boot-stm32/git.zup +docserver build -i ./embassy-embedded-hal -o webroot/crates/embassy-embedded-hal/git.zup +docserver build -i ./embassy-executor -o webroot/crates/embassy-executor/git.zup +docserver build -i ./embassy-futures -o webroot/crates/embassy-futures/git.zup +docserver build -i ./embassy-nrf -o webroot/crates/embassy-nrf/git.zup +docserver build -i ./embassy-rp -o webroot/crates/embassy-rp/git.zup +docserver build -i ./embassy-mspm0 -o webroot/crates/embassy-mspm0/git.zup +docserver build -i ./embassy-nxp -o webroot/crates/embassy-nxp/git.zup +docserver build -i ./embassy-sync -o webroot/crates/embassy-sync/git.zup +docserver build -i ./cyw43 -o webroot/crates/cyw43/git.zup +docserver build -i ./cyw43-pio -o webroot/crates/cyw43-pio/git.zup +docserver build -i ./embassy-stm32-wpan -o webroot/crates/embassy-stm32-wpan/git.zup --output-static webroot/static -docserver build -c -i ./embassy-time -o webroot/crates/embassy-time/git.zup -docserver build -c -i ./embassy-time-driver -o webroot/crates/embassy-time-driver/git.zup -docserver build -c -i ./embassy-time-queue-utils -o webroot/crates/embassy-time-queue-utils/git.zup +docserver build -i ./embassy-time -o webroot/crates/embassy-time/git.zup +docserver build -i ./embassy-time-driver -o webroot/crates/embassy-time-driver/git.zup +docserver build -i ./embassy-time-queue-utils -o webroot/crates/embassy-time-queue-utils/git.zup -docserver build -c -i ./embassy-usb -o webroot/crates/embassy-usb/git.zup -docserver build -c -i ./embassy-usb-dfu -o webroot/crates/embassy-usb-dfu/git.zup -docserver build -c -i ./embassy-usb-driver -o webroot/crates/embassy-usb-driver/git.zup -docserver build -c -i ./embassy-usb-logger -o webroot/crates/embassy-usb-logger/git.zup -docserver build -c -i ./embassy-usb-synopsys-otg -o webroot/crates/embassy-usb-synopsys-otg/git.zup +docserver build -i ./embassy-usb -o webroot/crates/embassy-usb/git.zup +docserver build -i ./embassy-usb-dfu -o webroot/crates/embassy-usb-dfu/git.zup +docserver build -i ./embassy-usb-driver -o webroot/crates/embassy-usb-driver/git.zup +docserver build -i ./embassy-usb-logger -o webroot/crates/embassy-usb-logger/git.zup +docserver build -i ./embassy-usb-synopsys-otg -o webroot/crates/embassy-usb-synopsys-otg/git.zup -docserver build -c -i ./embassy-net -o webroot/crates/embassy-net/git.zup -docserver build -c -i ./embassy-net-nrf91 -o webroot/crates/embassy-net-nrf91/git.zup -docserver build -c -i ./embassy-net-driver -o webroot/crates/embassy-net-driver/git.zup -docserver build -c -i ./embassy-net-driver-channel -o webroot/crates/embassy-net-driver-channel/git.zup -docserver build -c -i ./embassy-net-wiznet -o webroot/crates/embassy-net-wiznet/git.zup -docserver build -c -i ./embassy-net-ppp -o webroot/crates/embassy-net-ppp/git.zup -docserver build -c -i ./embassy-net-tuntap -o webroot/crates/embassy-net-tuntap/git.zup -docserver build -c -i ./embassy-net-enc28j60 -o webroot/crates/embassy-net-enc28j60/git.zup -docserver build -c -i ./embassy-net-esp-hosted -o webroot/crates/embassy-net-esp-hosted/git.zup -docserver build -c -i ./embassy-net-adin1110 -o webroot/crates/embassy-net-adin1110/git.zup +docserver build -i ./embassy-net -o webroot/crates/embassy-net/git.zup +docserver build -i ./embassy-net-nrf91 -o webroot/crates/embassy-net-nrf91/git.zup +docserver build -i ./embassy-net-driver -o webroot/crates/embassy-net-driver/git.zup +docserver build -i ./embassy-net-driver-channel -o webroot/crates/embassy-net-driver-channel/git.zup +docserver build -i ./embassy-net-wiznet -o webroot/crates/embassy-net-wiznet/git.zup +docserver build -i ./embassy-net-ppp -o webroot/crates/embassy-net-ppp/git.zup +docserver build -i ./embassy-net-tuntap -o webroot/crates/embassy-net-tuntap/git.zup +docserver build -i ./embassy-net-enc28j60 -o webroot/crates/embassy-net-enc28j60/git.zup +docserver build -i ./embassy-net-esp-hosted -o webroot/crates/embassy-net-esp-hosted/git.zup +docserver build -i ./embassy-net-adin1110 -o webroot/crates/embassy-net-adin1110/git.zup export KUBECONFIG=/ci/secrets/kubeconfig.yml POD=$(kubectl -n embassy get po -l app=docserver -o jsonpath={.items[0].metadata.name}) @@ -65,6 +65,6 @@ kubectl cp webroot/static $POD:/data # so that it doesn't prevent other crates from getting docs updates when it breaks. rm -rf webroot -docserver build -c -i ./embassy-stm32 -o webroot/crates/embassy-stm32/git.zup +docserver build -i ./embassy-stm32 -o webroot/crates/embassy-stm32/git.zup POD=$(kubectl -n embassy get po -l app=docserver -o jsonpath={.items[0].metadata.name}) kubectl cp webroot/crates $POD:/data