From 77d355e0ecb3f5eab4a563981552fdb69175c8c5 Mon Sep 17 00:00:00 2001 From: Alex Moon Date: Fri, 18 Apr 2025 15:08:18 -0400 Subject: [PATCH 01/97] Make the nrf Twim RAM buffer a instance variable instead of stack allocated --- embassy-nrf/src/twim.rs | 165 +++------------------ examples/nrf52840/src/bin/twim.rs | 4 +- examples/nrf52840/src/bin/twim_lowpower.rs | 2 + 3 files changed, 28 insertions(+), 143 deletions(-) diff --git a/embassy-nrf/src/twim.rs b/embassy-nrf/src/twim.rs index 083b54b99..95b24c616 100644 --- a/embassy-nrf/src/twim.rs +++ b/embassy-nrf/src/twim.rs @@ -4,7 +4,6 @@ use core::future::{poll_fn, Future}; use core::marker::PhantomData; -use core::mem::MaybeUninit; use core::sync::atomic::compiler_fence; use core::sync::atomic::Ordering::SeqCst; use core::task::Poll; @@ -17,7 +16,7 @@ use embassy_time::{Duration, Instant}; use embedded_hal_1::i2c::Operation; pub use pac::twim::vals::Frequency; -use crate::chip::{EASY_DMA_SIZE, FORCE_COPY_BUFFER_SIZE}; +use crate::chip::EASY_DMA_SIZE; use crate::gpio::Pin as GpioPin; use crate::interrupt::typelevel::Interrupt; use crate::pac::gpio::vals as gpiovals; @@ -75,8 +74,8 @@ pub enum Error { Transmit, /// Data reception failed. Receive, - /// The buffer is not in data RAM. It's most likely in flash, and nRF's DMA cannot access flash. - BufferNotInRAM, + /// The buffer is not in data RAM and is larger than the RAM buffer. It's most likely in flash, and nRF's DMA cannot access flash. + RAMBufferTooSmall, /// Didn't receive an ACK bit after the address byte. Address might be wrong, or the i2c device chip might not be connected properly. AddressNack, /// Didn't receive an ACK bit after a data byte. @@ -115,6 +114,7 @@ impl interrupt::typelevel::Handler for InterruptHandl /// TWI driver. pub struct Twim<'d, T: Instance> { _p: Peri<'d, T>, + tx_ram_buffer: &'d mut [u8], } impl<'d, T: Instance> Twim<'d, T> { @@ -125,6 +125,7 @@ impl<'d, T: Instance> Twim<'d, T> { sda: Peri<'d, impl GpioPin>, scl: Peri<'d, impl GpioPin>, config: Config, + tx_ram_buffer: &'d mut [u8], ) -> Self { let r = T::regs(); @@ -159,7 +160,10 @@ impl<'d, T: Instance> Twim<'d, T> { // Enable TWIM instance. r.enable().write(|w| w.set_enable(vals::Enable::ENABLED)); - let mut twim = Self { _p: twim }; + let mut twim = Self { + _p: twim, + tx_ram_buffer, + }; // Apply runtime peripheral configuration Self::set_config(&mut twim, &config).unwrap(); @@ -174,21 +178,17 @@ impl<'d, T: Instance> Twim<'d, T> { } /// Set TX buffer, checking that it is in RAM and has suitable length. - unsafe fn set_tx_buffer( - &mut self, - buffer: &[u8], - ram_buffer: Option<&mut [MaybeUninit; FORCE_COPY_BUFFER_SIZE]>, - ) -> Result<(), Error> { + unsafe fn set_tx_buffer(&mut self, buffer: &[u8]) -> Result<(), Error> { let buffer = if slice_in_ram(buffer) { buffer } else { - let ram_buffer = ram_buffer.ok_or(Error::BufferNotInRAM)?; + if buffer.len() > self.tx_ram_buffer.len() { + return Err(Error::RAMBufferTooSmall); + } trace!("Copying TWIM tx buffer into RAM for DMA"); - let ram_buffer = &mut ram_buffer[..buffer.len()]; - // Inline implementation of the nightly API MaybeUninit::copy_from_slice(ram_buffer, buffer) - let uninit_src: &[MaybeUninit] = unsafe { core::mem::transmute(buffer) }; - ram_buffer.copy_from_slice(uninit_src); - unsafe { &*(ram_buffer as *const [MaybeUninit] as *const [u8]) } + let ram_buffer = &mut self.tx_ram_buffer[..buffer.len()]; + ram_buffer.copy_from_slice(buffer); + &*ram_buffer }; if buffer.len() > EASY_DMA_SIZE { @@ -358,7 +358,6 @@ impl<'d, T: Instance> Twim<'d, T> { &mut self, address: u8, operations: &mut [Operation<'_>], - tx_ram_buffer: Option<&mut [MaybeUninit; FORCE_COPY_BUFFER_SIZE]>, last_op: Option<&Operation<'_>>, inten: bool, ) -> Result { @@ -397,7 +396,7 @@ impl<'d, T: Instance> Twim<'d, T> { // Set up DMA buffers. unsafe { - self.set_tx_buffer(wr_buffer, tx_ram_buffer)?; + self.set_tx_buffer(wr_buffer)?; self.set_rx_buffer(rd_buffer)?; } @@ -450,7 +449,7 @@ impl<'d, T: Instance> Twim<'d, T> { { // Set up DMA buffers. unsafe { - self.set_tx_buffer(wr_buffer, tx_ram_buffer)?; + self.set_tx_buffer(wr_buffer)?; self.set_rx_buffer(rd_buffer)?; } @@ -472,7 +471,7 @@ impl<'d, T: Instance> Twim<'d, T> { // Set up DMA buffers. unsafe { - self.set_tx_buffer(buffer, tx_ram_buffer)?; + self.set_tx_buffer(buffer)?; } // Start write operation. @@ -539,28 +538,9 @@ impl<'d, T: Instance> Twim<'d, T> { /// An `Operation::Write` following an `Operation::Read` must have a /// non-empty buffer. pub fn blocking_transaction(&mut self, address: u8, mut operations: &mut [Operation<'_>]) -> Result<(), Error> { - let mut tx_ram_buffer = [MaybeUninit::uninit(); FORCE_COPY_BUFFER_SIZE]; let mut last_op = None; while !operations.is_empty() { - let ops = self.setup_operations(address, operations, Some(&mut tx_ram_buffer), last_op, false)?; - let (in_progress, rest) = operations.split_at_mut(ops); - self.blocking_wait(); - self.check_operations(in_progress)?; - last_op = in_progress.last(); - operations = rest; - } - Ok(()) - } - - /// Same as [`blocking_transaction`](Twim::blocking_transaction) but will fail instead of copying data into RAM. Consult the module level documentation to learn more. - pub fn blocking_transaction_from_ram( - &mut self, - address: u8, - mut operations: &mut [Operation<'_>], - ) -> Result<(), Error> { - let mut last_op = None; - while !operations.is_empty() { - let ops = self.setup_operations(address, operations, None, last_op, false)?; + let ops = self.setup_operations(address, operations, last_op, false)?; let (in_progress, rest) = operations.split_at_mut(ops); self.blocking_wait(); self.check_operations(in_progress)?; @@ -580,30 +560,9 @@ impl<'d, T: Instance> Twim<'d, T> { mut operations: &mut [Operation<'_>], timeout: Duration, ) -> Result<(), Error> { - let mut tx_ram_buffer = [MaybeUninit::uninit(); FORCE_COPY_BUFFER_SIZE]; let mut last_op = None; while !operations.is_empty() { - let ops = self.setup_operations(address, operations, Some(&mut tx_ram_buffer), last_op, false)?; - let (in_progress, rest) = operations.split_at_mut(ops); - self.blocking_wait_timeout(timeout)?; - self.check_operations(in_progress)?; - last_op = in_progress.last(); - operations = rest; - } - Ok(()) - } - - /// Same as [`blocking_transaction_timeout`](Twim::blocking_transaction_timeout) but will fail instead of copying data into RAM. Consult the module level documentation to learn more. - #[cfg(feature = "time")] - pub fn blocking_transaction_from_ram_timeout( - &mut self, - address: u8, - mut operations: &mut [Operation<'_>], - timeout: Duration, - ) -> Result<(), Error> { - let mut last_op = None; - while !operations.is_empty() { - let ops = self.setup_operations(address, operations, None, last_op, false)?; + let ops = self.setup_operations(address, operations, last_op, false)?; let (in_progress, rest) = operations.split_at_mut(ops); self.blocking_wait_timeout(timeout)?; self.check_operations(in_progress)?; @@ -624,28 +583,9 @@ impl<'d, T: Instance> Twim<'d, T> { /// An `Operation::Write` following an `Operation::Read` must have a /// non-empty buffer. pub async fn transaction(&mut self, address: u8, mut operations: &mut [Operation<'_>]) -> Result<(), Error> { - let mut tx_ram_buffer = [MaybeUninit::uninit(); FORCE_COPY_BUFFER_SIZE]; let mut last_op = None; while !operations.is_empty() { - let ops = self.setup_operations(address, operations, Some(&mut tx_ram_buffer), last_op, true)?; - let (in_progress, rest) = operations.split_at_mut(ops); - self.async_wait().await?; - self.check_operations(in_progress)?; - last_op = in_progress.last(); - operations = rest; - } - Ok(()) - } - - /// Same as [`transaction`](Twim::transaction) but will fail instead of copying data into RAM. Consult the module level documentation to learn more. - pub async fn transaction_from_ram( - &mut self, - address: u8, - mut operations: &mut [Operation<'_>], - ) -> Result<(), Error> { - let mut last_op = None; - while !operations.is_empty() { - let ops = self.setup_operations(address, operations, None, last_op, true)?; + let ops = self.setup_operations(address, operations, last_op, true)?; let (in_progress, rest) = operations.split_at_mut(ops); self.async_wait().await?; self.check_operations(in_progress)?; @@ -665,11 +605,6 @@ impl<'d, T: Instance> Twim<'d, T> { self.blocking_transaction(address, &mut [Operation::Write(buffer)]) } - /// Same as [`blocking_write`](Twim::blocking_write) but will fail instead of copying data into RAM. Consult the module level documentation to learn more. - pub fn blocking_write_from_ram(&mut self, address: u8, buffer: &[u8]) -> Result<(), Error> { - self.blocking_transaction_from_ram(address, &mut [Operation::Write(buffer)]) - } - /// Read from an I2C slave. /// /// The buffer must have a length of at most 255 bytes on the nRF52832 @@ -687,16 +622,6 @@ impl<'d, T: Instance> Twim<'d, T> { self.blocking_transaction(address, &mut [Operation::Write(wr_buffer), Operation::Read(rd_buffer)]) } - /// Same as [`blocking_write_read`](Twim::blocking_write_read) but will fail instead of copying data into RAM. Consult the module level documentation to learn more. - pub fn blocking_write_read_from_ram( - &mut self, - address: u8, - wr_buffer: &[u8], - rd_buffer: &mut [u8], - ) -> Result<(), Error> { - self.blocking_transaction_from_ram(address, &mut [Operation::Write(wr_buffer), Operation::Read(rd_buffer)]) - } - // =========================================== /// Write to an I2C slave with timeout. @@ -707,17 +632,6 @@ impl<'d, T: Instance> Twim<'d, T> { self.blocking_transaction_timeout(address, &mut [Operation::Write(buffer)], timeout) } - /// Same as [`blocking_write`](Twim::blocking_write) but will fail instead of copying data into RAM. Consult the module level documentation to learn more. - #[cfg(feature = "time")] - pub fn blocking_write_from_ram_timeout( - &mut self, - address: u8, - buffer: &[u8], - timeout: Duration, - ) -> Result<(), Error> { - self.blocking_transaction_from_ram_timeout(address, &mut [Operation::Write(buffer)], timeout) - } - /// Read from an I2C slave. /// /// The buffer must have a length of at most 255 bytes on the nRF52832 @@ -747,22 +661,6 @@ impl<'d, T: Instance> Twim<'d, T> { ) } - /// Same as [`blocking_write_read`](Twim::blocking_write_read) but will fail instead of copying data into RAM. Consult the module level documentation to learn more. - #[cfg(feature = "time")] - pub fn blocking_write_read_from_ram_timeout( - &mut self, - address: u8, - wr_buffer: &[u8], - rd_buffer: &mut [u8], - timeout: Duration, - ) -> Result<(), Error> { - self.blocking_transaction_from_ram_timeout( - address, - &mut [Operation::Write(wr_buffer), Operation::Read(rd_buffer)], - timeout, - ) - } - // =========================================== /// Read from an I2C slave. @@ -781,12 +679,6 @@ impl<'d, T: Instance> Twim<'d, T> { self.transaction(address, &mut [Operation::Write(buffer)]).await } - /// Same as [`write`](Twim::write) but will fail instead of copying data into RAM. Consult the module level documentation to learn more. - pub async fn write_from_ram(&mut self, address: u8, buffer: &[u8]) -> Result<(), Error> { - self.transaction_from_ram(address, &mut [Operation::Write(buffer)]) - .await - } - /// Write data to an I2C slave, then read data from the slave without /// triggering a stop condition between the two. /// @@ -796,17 +688,6 @@ impl<'d, T: Instance> Twim<'d, T> { self.transaction(address, &mut [Operation::Write(wr_buffer), Operation::Read(rd_buffer)]) .await } - - /// Same as [`write_read`](Twim::write_read) but will fail instead of copying data into RAM. Consult the module level documentation to learn more. - pub async fn write_read_from_ram( - &mut self, - address: u8, - wr_buffer: &[u8], - rd_buffer: &mut [u8], - ) -> Result<(), Error> { - self.transaction_from_ram(address, &mut [Operation::Write(wr_buffer), Operation::Read(rd_buffer)]) - .await - } } impl<'a, T: Instance> Drop for Twim<'a, T> { @@ -904,7 +785,7 @@ impl embedded_hal_1::i2c::Error for Error { Self::RxBufferTooLong => embedded_hal_1::i2c::ErrorKind::Other, Self::Transmit => embedded_hal_1::i2c::ErrorKind::Other, Self::Receive => embedded_hal_1::i2c::ErrorKind::Other, - Self::BufferNotInRAM => embedded_hal_1::i2c::ErrorKind::Other, + Self::RAMBufferTooSmall => embedded_hal_1::i2c::ErrorKind::Other, Self::AddressNack => { embedded_hal_1::i2c::ErrorKind::NoAcknowledge(embedded_hal_1::i2c::NoAcknowledgeSource::Address) } diff --git a/examples/nrf52840/src/bin/twim.rs b/examples/nrf52840/src/bin/twim.rs index ceaafd784..e30a3855d 100644 --- a/examples/nrf52840/src/bin/twim.rs +++ b/examples/nrf52840/src/bin/twim.rs @@ -9,6 +9,7 @@ use defmt::*; use embassy_executor::Spawner; use embassy_nrf::twim::{self, Twim}; use embassy_nrf::{bind_interrupts, peripherals}; +use static_cell::ConstStaticCell; use {defmt_rtt as _, panic_probe as _}; const ADDRESS: u8 = 0x50; @@ -22,7 +23,8 @@ async fn main(_spawner: Spawner) { let p = embassy_nrf::init(Default::default()); info!("Initializing TWI..."); let config = twim::Config::default(); - let mut twi = Twim::new(p.TWISPI0, Irqs, p.P0_03, p.P0_04, config); + static RAM_BUFFER: ConstStaticCell<[u8; 16]> = ConstStaticCell::new([0; 16]); + let mut twi = Twim::new(p.TWISPI0, Irqs, p.P0_03, p.P0_04, config, RAM_BUFFER.take()); info!("Reading..."); diff --git a/examples/nrf52840/src/bin/twim_lowpower.rs b/examples/nrf52840/src/bin/twim_lowpower.rs index 8a6f958eb..f7380e20d 100644 --- a/examples/nrf52840/src/bin/twim_lowpower.rs +++ b/examples/nrf52840/src/bin/twim_lowpower.rs @@ -30,6 +30,7 @@ async fn main(_p: Spawner) { loop { info!("Initializing TWI..."); let config = twim::Config::default(); + let mut ram_buffer = [0u8; 16]; // Create the TWIM instance with borrowed singletons, so they're not consumed. let mut twi = Twim::new( @@ -38,6 +39,7 @@ async fn main(_p: Spawner) { p.P0_03.reborrow(), p.P0_04.reborrow(), config, + &mut ram_buffer, ); info!("Reading..."); From 3c9661cebc0f093d8d9a194adca2d1c815ebfd2e Mon Sep 17 00:00:00 2001 From: Gerhard de Clercq <11624490+Gerharddc@users.noreply.github.com> Date: Tue, 22 Apr 2025 12:20:21 +0200 Subject: [PATCH 02/97] [embassy-usb-dfu] Add generic DFU marking interface This commit adds an interface that allows users to customise how the bootloader is informed that DFU mode is needed on the next boot. --- embassy-usb-dfu/src/application.rs | 36 +++++++++++++++++++++--------- 1 file changed, 25 insertions(+), 11 deletions(-) diff --git a/embassy-usb-dfu/src/application.rs b/embassy-usb-dfu/src/application.rs index f0d7626f6..e93c241ad 100644 --- a/embassy-usb-dfu/src/application.rs +++ b/embassy-usb-dfu/src/application.rs @@ -13,9 +13,25 @@ use crate::consts::{ }; use crate::Reset; +/// Generic interface for a system that can signal to the bootloader that USB DFU mode is needed on the next boot. +/// +/// By default this trait is implemented for `BlockingFirmwareState<'d, STATE>` but you could also implement this generic +/// interface yourself instead in more complex situations. This could for instance be when you cannot hand ownership of a +/// `BlockingFirmwareState` instance over directly to the DFU `Control` instance and need to use a more complex mechanism. +pub trait DfuMarker { + /// Signal to the bootloader that DFU mode should be used on the next boot. + fn mark_dfu(&mut self); +} + +impl<'d, STATE: NorFlash> DfuMarker for BlockingFirmwareState<'d, STATE> { + fn mark_dfu(&mut self) { + self.mark_dfu().expect("Failed to mark DFU mode in bootloader") + } +} + /// Internal state for the DFU class -pub struct Control<'d, STATE: NorFlash, RST: Reset> { - firmware_state: BlockingFirmwareState<'d, STATE>, +pub struct Control { + dfu_marker: MARK, attrs: DfuAttributes, state: State, timeout: Option, @@ -23,11 +39,11 @@ pub struct Control<'d, STATE: NorFlash, RST: Reset> { _rst: PhantomData, } -impl<'d, STATE: NorFlash, RST: Reset> Control<'d, STATE, RST> { +impl Control { /// Create a new DFU instance to expose a DFU interface. - pub fn new(firmware_state: BlockingFirmwareState<'d, STATE>, attrs: DfuAttributes) -> Self { + pub fn new(dfu_marker: MARK, attrs: DfuAttributes) -> Self { Control { - firmware_state, + dfu_marker, attrs, state: State::AppIdle, detach_start: None, @@ -37,7 +53,7 @@ impl<'d, STATE: NorFlash, RST: Reset> Control<'d, STATE, RST> { } } -impl<'d, STATE: NorFlash, RST: Reset> Handler for Control<'d, STATE, RST> { +impl Handler for Control { fn reset(&mut self) { if let Some(start) = self.detach_start { let delta = Instant::now() - start; @@ -48,9 +64,7 @@ impl<'d, STATE: NorFlash, RST: Reset> Handler for Control<'d, STATE, RST> { timeout.as_millis() ); if delta < timeout { - self.firmware_state - .mark_dfu() - .expect("Failed to mark DFU mode in bootloader"); + self.dfu_marker.mark_dfu(); RST::sys_reset() } } @@ -109,9 +123,9 @@ impl<'d, STATE: NorFlash, RST: Reset> Handler for Control<'d, STATE, RST> { /// it should expose a DFU device, and a software reset will be issued. /// /// To apply USB DFU updates, the bootloader must be capable of recognizing the DFU magic and exposing a device to handle the full DFU transaction with the host. -pub fn usb_dfu<'d, D: Driver<'d>, STATE: NorFlash, RST: Reset>( +pub fn usb_dfu<'d, D: Driver<'d>, MARK: DfuMarker, RST: Reset>( builder: &mut Builder<'d, D>, - handler: &'d mut Control<'d, STATE, RST>, + handler: &'d mut Control, timeout: Duration, ) { let mut func = builder.function(0x00, 0x00, 0x00); From 8cf8fb324ce9063890d8912cccd02bc79fbffd1d Mon Sep 17 00:00:00 2001 From: Michael Medin Date: Thu, 24 Apr 2025 07:15:11 +0200 Subject: [PATCH 03/97] Add function to allow re-init rcc config for stm32 --- embassy-stm32/src/lib.rs | 28 ++++++++++++++++++++++++++++ 1 file changed, 28 insertions(+) diff --git a/embassy-stm32/src/lib.rs b/embassy-stm32/src/lib.rs index 226293a9d..0cc6886d9 100644 --- a/embassy-stm32/src/lib.rs +++ b/embassy-stm32/src/lib.rs @@ -616,3 +616,31 @@ fn init_hw(config: Config) -> Peripherals { p }) } + + +/// Re-initialize the `embassy-stm32` clock configuration with the provided configuration. +/// +/// This is useful when you need to alter the CPU clock after configuring peripherals. +/// For instance, configure an external clock via spi or i2c. +/// +/// Please not this only re-configures the rcc and the time driver (not GPIO, EXTI, etc). +/// +/// This should only be called after `init`. +#[cfg(not(feature = "_dual-core"))] +pub fn reinitialize_rcc(config: Config) { + critical_section::with(|cs| { + unsafe { + rcc::init(config.rcc); + + // must be after rcc init + #[cfg(feature = "_time-driver")] + time_driver::init(cs); + + #[cfg(feature = "low-power")] + { + crate::rcc::REFCOUNT_STOP2 = 0; + crate::rcc::REFCOUNT_STOP1 = 0; + } + } + }) +} From f67f11534f9617279b9ff83a693aad5f7ceb4d4f Mon Sep 17 00:00:00 2001 From: Michael Medin Date: Thu, 24 Apr 2025 07:25:21 +0200 Subject: [PATCH 04/97] Fixed formatting --- embassy-stm32/src/lib.rs | 1 - 1 file changed, 1 deletion(-) diff --git a/embassy-stm32/src/lib.rs b/embassy-stm32/src/lib.rs index 0cc6886d9..cccffd4fe 100644 --- a/embassy-stm32/src/lib.rs +++ b/embassy-stm32/src/lib.rs @@ -617,7 +617,6 @@ fn init_hw(config: Config) -> Peripherals { }) } - /// Re-initialize the `embassy-stm32` clock configuration with the provided configuration. /// /// This is useful when you need to alter the CPU clock after configuring peripherals. From 6842ced7cb858f7735bc9376db9d39386dadec58 Mon Sep 17 00:00:00 2001 From: Michael Medin Date: Thu, 24 Apr 2025 10:17:04 +0200 Subject: [PATCH 05/97] Fixed for cs not always used --- embassy-stm32/src/lib.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/embassy-stm32/src/lib.rs b/embassy-stm32/src/lib.rs index cccffd4fe..1ad30d522 100644 --- a/embassy-stm32/src/lib.rs +++ b/embassy-stm32/src/lib.rs @@ -627,13 +627,13 @@ fn init_hw(config: Config) -> Peripherals { /// This should only be called after `init`. #[cfg(not(feature = "_dual-core"))] pub fn reinitialize_rcc(config: Config) { - critical_section::with(|cs| { + critical_section::with(|_cs| { unsafe { rcc::init(config.rcc); // must be after rcc init #[cfg(feature = "_time-driver")] - time_driver::init(cs); + time_driver::init(_cs); #[cfg(feature = "low-power")] { From b2c32a947ea724facb75f81978dfb5d3ca331ddb Mon Sep 17 00:00:00 2001 From: Michael Medin Date: Thu, 24 Apr 2025 10:54:35 +0200 Subject: [PATCH 06/97] Updated based on feedback --- embassy-stm32/src/lib.rs | 48 ++++++++++++++++++---------------------- 1 file changed, 21 insertions(+), 27 deletions(-) diff --git a/embassy-stm32/src/lib.rs b/embassy-stm32/src/lib.rs index 1ad30d522..af7ef9fe0 100644 --- a/embassy-stm32/src/lib.rs +++ b/embassy-stm32/src/lib.rs @@ -222,6 +222,7 @@ pub(crate) use stm32_metapac as pac; use crate::interrupt::Priority; #[cfg(feature = "rt")] pub use crate::pac::NVIC_PRIO_BITS; +use critical_section::CriticalSection; /// `embassy-stm32` global configuration. #[non_exhaustive] @@ -600,17 +601,7 @@ fn init_hw(config: Config) -> Peripherals { #[cfg(feature = "exti")] exti::init(cs); - rcc::init(config.rcc); - - // must be after rcc init - #[cfg(feature = "_time-driver")] - time_driver::init(cs); - - #[cfg(feature = "low-power")] - { - crate::rcc::REFCOUNT_STOP2 = 0; - crate::rcc::REFCOUNT_STOP1 = 0; - } + init_rcc(cs, config.rcc); } p @@ -626,20 +617,23 @@ fn init_hw(config: Config) -> Peripherals { /// /// This should only be called after `init`. #[cfg(not(feature = "_dual-core"))] -pub fn reinitialize_rcc(config: Config) { - critical_section::with(|_cs| { - unsafe { - rcc::init(config.rcc); - - // must be after rcc init - #[cfg(feature = "_time-driver")] - time_driver::init(_cs); - - #[cfg(feature = "low-power")] - { - crate::rcc::REFCOUNT_STOP2 = 0; - crate::rcc::REFCOUNT_STOP1 = 0; - } - } - }) +pub fn reinit(config: rcc::Config) { + critical_section::with(|cs| init_rcc(cs, config)) +} + +#[cfg(not(feature = "_dual-core"))] +fn init_rcc(_cs: CriticalSection, config: rcc::Config) { + unsafe { + rcc::init(config); + + // must be after rcc init + #[cfg(feature = "_time-driver")] + time_driver::init(_cs); + + #[cfg(feature = "low-power")] + { + crate::rcc::REFCOUNT_STOP2 = 0; + crate::rcc::REFCOUNT_STOP1 = 0; + } + } } From 8661b019e609f50bd78067cc49c65a4babfead00 Mon Sep 17 00:00:00 2001 From: Michael Medin Date: Thu, 24 Apr 2025 10:57:02 +0200 Subject: [PATCH 07/97] Fixed formatting --- embassy-stm32/src/lib.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/embassy-stm32/src/lib.rs b/embassy-stm32/src/lib.rs index af7ef9fe0..b0bc7ffbb 100644 --- a/embassy-stm32/src/lib.rs +++ b/embassy-stm32/src/lib.rs @@ -213,6 +213,7 @@ macro_rules! bind_interrupts { // Reexports pub use _generated::{peripherals, Peripherals}; +use critical_section::CriticalSection; pub use embassy_hal_internal::{Peri, PeripheralType}; #[cfg(feature = "unstable-pac")] pub use stm32_metapac as pac; @@ -222,7 +223,6 @@ pub(crate) use stm32_metapac as pac; use crate::interrupt::Priority; #[cfg(feature = "rt")] pub use crate::pac::NVIC_PRIO_BITS; -use critical_section::CriticalSection; /// `embassy-stm32` global configuration. #[non_exhaustive] From b0519d11fb0842267e0cdd36da9b84cdb0ebb23a Mon Sep 17 00:00:00 2001 From: Michael Medin Date: Thu, 24 Apr 2025 11:01:46 +0200 Subject: [PATCH 08/97] Possible fix for unused CS and feature selections --- embassy-stm32/src/lib.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/embassy-stm32/src/lib.rs b/embassy-stm32/src/lib.rs index b0bc7ffbb..466634edf 100644 --- a/embassy-stm32/src/lib.rs +++ b/embassy-stm32/src/lib.rs @@ -213,6 +213,7 @@ macro_rules! bind_interrupts { // Reexports pub use _generated::{peripherals, Peripherals}; +#[cfg(not(feature = "_dual-core"))] use critical_section::CriticalSection; pub use embassy_hal_internal::{Peri, PeripheralType}; #[cfg(feature = "unstable-pac")] From 584066e209141ce92d882ceb6e7525c980833689 Mon Sep 17 00:00:00 2001 From: Michael Medin Date: Thu, 24 Apr 2025 11:07:15 +0200 Subject: [PATCH 09/97] updated cs gates for dual core --- embassy-stm32/src/lib.rs | 2 -- 1 file changed, 2 deletions(-) diff --git a/embassy-stm32/src/lib.rs b/embassy-stm32/src/lib.rs index 466634edf..444d14f28 100644 --- a/embassy-stm32/src/lib.rs +++ b/embassy-stm32/src/lib.rs @@ -213,7 +213,6 @@ macro_rules! bind_interrupts { // Reexports pub use _generated::{peripherals, Peripherals}; -#[cfg(not(feature = "_dual-core"))] use critical_section::CriticalSection; pub use embassy_hal_internal::{Peri, PeripheralType}; #[cfg(feature = "unstable-pac")] @@ -622,7 +621,6 @@ pub fn reinit(config: rcc::Config) { critical_section::with(|cs| init_rcc(cs, config)) } -#[cfg(not(feature = "_dual-core"))] fn init_rcc(_cs: CriticalSection, config: rcc::Config) { unsafe { rcc::init(config); From 7259f20fe211f2833ffb4aa1b44e6ddd60145853 Mon Sep 17 00:00:00 2001 From: Dion Dokter Date: Thu, 24 Apr 2025 11:23:11 +0200 Subject: [PATCH 10/97] Add configurable bank support in build script --- embassy-stm32/Cargo.toml | 9 ++++-- embassy-stm32/build.rs | 67 ++++++++++++++++++++++++++++++---------- 2 files changed, 58 insertions(+), 18 deletions(-) diff --git a/embassy-stm32/Cargo.toml b/embassy-stm32/Cargo.toml index 82bc76883..afef5d72f 100644 --- a/embassy-stm32/Cargo.toml +++ b/embassy-stm32/Cargo.toml @@ -73,7 +73,8 @@ rand_core = "0.6.3" sdio-host = "0.9.0" critical-section = "1.1" #stm32-metapac = { version = "16" } -stm32-metapac = { git = "https://github.com/embassy-rs/stm32-data-generated", tag = "stm32-data-380f03cb71f43a242adc45e83607a380ffe0447b" } +# stm32-metapac = { git = "https://github.com/embassy-rs/stm32-data-generated", tag = "stm32-data-380f03cb71f43a242adc45e83607a380ffe0447b" } +stm32-metapac = { path = "R:/stm32-data/build/stm32-metapac" } vcell = "0.1.3" nb = "1.0.0" @@ -102,7 +103,8 @@ proc-macro2 = "1.0.36" quote = "1.0.15" #stm32-metapac = { version = "16", default-features = false, features = ["metadata"]} -stm32-metapac = { git = "https://github.com/embassy-rs/stm32-data-generated", tag = "stm32-data-380f03cb71f43a242adc45e83607a380ffe0447b", default-features = false, features = ["metadata"] } +# stm32-metapac = { git = "https://github.com/embassy-rs/stm32-data-generated", tag = "stm32-data-380f03cb71f43a242adc45e83607a380ffe0447b", default-features = false, features = ["metadata"] } +stm32-metapac = { path = "R:/stm32-data/build/stm32-metapac", default-features = false, features = ["metadata"] } [features] default = ["rt"] @@ -197,6 +199,9 @@ split-pc2 = ["_split-pins-enabled"] ## Split PC3 split-pc3 = ["_split-pins-enabled"] +dual-bank = [] +single-bank = [] + ## internal use only _split-pins-enabled = [] diff --git a/embassy-stm32/build.rs b/embassy-stm32/build.rs index f9f03c51b..2a3213c0c 100644 --- a/embassy-stm32/build.rs +++ b/embassy-stm32/build.rs @@ -49,6 +49,48 @@ fn main() { } } + // ======== + // Select the memory variant to use + let memory = { + let single_bank_selected = env::var("CARGO_FEATURE_SINGLE_BANK").is_ok(); + let dual_bank_selected = env::var("CARGO_FEATURE_DUAL_BANK").is_ok(); + + let single_bank_memory = METADATA.memory.iter().find(|mem| { + mem.iter() + .filter(|region| region.kind == MemoryRegionKind::Flash) + .count() + == 1 + }); + + let dual_bank_memory = METADATA.memory.iter().find(|mem| { + mem.iter() + .filter(|region| region.kind == MemoryRegionKind::Flash) + .count() + == 2 + }); + + cfgs.set( + "bank_setup_configurable", + single_bank_memory.is_some() && dual_bank_memory.is_some(), + ); + + match (single_bank_selected, dual_bank_selected) { + (true, true) => panic!("Both 'single-bank' and 'dual-bank' features enabled"), + (true, false) => { + single_bank_memory.expect("The 'single-bank' feature is not supported on this dual bank chip") + } + (false, true) => { + dual_bank_memory.expect("The 'dual-bank' feature is not supported on this single bank chip") + } + (false, false) => { + if METADATA.memory.len() != 1 { + panic!("Chip supports single and dual bank configuration. No Cargo feature to select one is enabled. Use the 'single-bank' or 'dual-bank' feature to make your selection") + } + METADATA.memory[0] + } + } + }; + // ======== // Generate singletons @@ -290,8 +332,7 @@ fn main() { // ======== // Generate FLASH regions let mut flash_regions = TokenStream::new(); - let flash_memory_regions: Vec<_> = METADATA - .memory + let flash_memory_regions: Vec<_> = memory .iter() .filter(|x| x.kind == MemoryRegionKind::Flash && x.settings.is_some()) .collect(); @@ -1616,8 +1657,7 @@ fn main() { let mut pins_table: Vec> = Vec::new(); let mut adc_table: Vec> = Vec::new(); - for m in METADATA - .memory + for m in memory .iter() .filter(|m| m.kind == MemoryRegionKind::Flash && m.settings.is_some()) { @@ -1855,8 +1895,7 @@ fn main() { // ======== // Generate flash constants - let flash_regions: Vec<&MemoryRegion> = METADATA - .memory + let flash_regions: Vec<&MemoryRegion> = memory .iter() .filter(|x| x.kind == MemoryRegionKind::Flash && x.name.starts_with("BANK_")) .collect(); @@ -1981,7 +2020,7 @@ fn main() { println!("cargo:rerun-if-changed=build.rs"); if cfg!(feature = "memory-x") { - gen_memory_x(out_dir); + gen_memory_x(memory, out_dir); println!("cargo:rustc-link-search={}", out_dir.display()); } } @@ -2070,11 +2109,11 @@ fn rustfmt(path: impl AsRef) { } } -fn gen_memory_x(out_dir: &Path) { +fn gen_memory_x(memory: &[MemoryRegion], out_dir: &Path) { let mut memory_x = String::new(); - let flash = get_memory_range(MemoryRegionKind::Flash); - let ram = get_memory_range(MemoryRegionKind::Ram); + let flash = get_memory_range(memory, MemoryRegionKind::Flash); + let ram = get_memory_range(memory, MemoryRegionKind::Ram); write!(memory_x, "MEMORY\n{{\n").unwrap(); writeln!( @@ -2098,12 +2137,8 @@ fn gen_memory_x(out_dir: &Path) { std::fs::write(out_dir.join("memory.x"), memory_x.as_bytes()).unwrap(); } -fn get_memory_range(kind: MemoryRegionKind) -> (u32, u32, String) { - let mut mems: Vec<_> = METADATA - .memory - .iter() - .filter(|m| m.kind == kind && m.size != 0) - .collect(); +fn get_memory_range(memory: &[MemoryRegion], kind: MemoryRegionKind) -> (u32, u32, String) { + let mut mems: Vec<_> = memory.iter().filter(|m| m.kind == kind && m.size != 0).collect(); mems.sort_by_key(|m| m.address); let mut start = u32::MAX; From 19351c374ad42eb720194f8921375773a9b9c0a9 Mon Sep 17 00:00:00 2001 From: Dion Dokter Date: Thu, 24 Apr 2025 11:40:15 +0200 Subject: [PATCH 11/97] Check the bank setup when required --- embassy-stm32/src/flash/common.rs | 4 ++++ embassy-stm32/src/flash/g.rs | 10 ++++++++++ 2 files changed, 14 insertions(+) diff --git a/embassy-stm32/src/flash/common.rs b/embassy-stm32/src/flash/common.rs index 1376ca4b4..93d734b20 100644 --- a/embassy-stm32/src/flash/common.rs +++ b/embassy-stm32/src/flash/common.rs @@ -20,6 +20,10 @@ pub struct Flash<'d, MODE = Async> { impl<'d> Flash<'d, Blocking> { /// Create a new flash driver, usable in blocking mode. pub fn new_blocking(p: Peri<'d, FLASH>) -> Self { + #[cfg(bank_setup_configurable)] + // Check if the configuration matches the embassy setup + super::check_bank_setup(); + Self { inner: p, _mode: PhantomData, diff --git a/embassy-stm32/src/flash/g.rs b/embassy-stm32/src/flash/g.rs index 83663743c..f55c5e6a7 100644 --- a/embassy-stm32/src/flash/g.rs +++ b/embassy-stm32/src/flash/g.rs @@ -109,3 +109,13 @@ fn wait_busy() { fn wait_busy() { while pac::FLASH.sr().read().bsy() {} } + +#[cfg(bank_setup_configurable)] +pub(crate) fn check_bank_setup() { + if cfg!(feature = "single-bank") && pac::FLASH.optr().read().dbank() { + panic!("Embassy is configured as single-bank, but the hardware is running in dual-bank mode. Change the hardware by changing the dbank value in the user option bytes or configure embassy to use dual-bank config"); + } + if cfg!(feature = "dual-bank") && !pac::FLASH.optr().read().dbank() { + panic!("Embassy is configured as dual-bank, but the hardware is running in single-bank mode. Change the hardware by changing the dbank value in the user option bytes or configure embassy to use single-bank config"); + } +} From a41c83d1824a42bd68bb4d2f01500d06d25b0118 Mon Sep 17 00:00:00 2001 From: Dion Dokter Date: Thu, 24 Apr 2025 11:56:17 +0200 Subject: [PATCH 12/97] Add to changelog --- embassy-stm32/CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/embassy-stm32/CHANGELOG.md b/embassy-stm32/CHANGELOG.md index c50ab5294..7b7d559e2 100644 --- a/embassy-stm32/CHANGELOG.md +++ b/embassy-stm32/CHANGELOG.md @@ -7,6 +7,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## Unreleased - Modify BufferedUart initialization to take pins before interrupts ([#3983](https://github.com/embassy-rs/embassy/pull/3983)) +- Added a 'single-bank' and a 'dual-bank' feature so chips with configurable flash bank setups can be supported in embassy. The G4 series now supports this. ([#4125](https://github.com/embassy-rs/embassy/pull/4125)) ## 0.2.0 - 2025-01-10 From 4ce3bdb3703e5120c7936b5e3762744ae4461e75 Mon Sep 17 00:00:00 2001 From: 1-rafael-1 Date: Sat, 26 Apr 2025 21:54:40 +0200 Subject: [PATCH 13/97] Add core voltage scaling options and PLL parameter finder for RP2040 --- embassy-rp/src/clocks.rs | 242 ++++++++++++++++++++++++++++++++++++--- 1 file changed, 228 insertions(+), 14 deletions(-) diff --git a/embassy-rp/src/clocks.rs b/embassy-rp/src/clocks.rs index 67aa5e540..ba7b139a6 100644 --- a/embassy-rp/src/clocks.rs +++ b/embassy-rp/src/clocks.rs @@ -69,6 +69,48 @@ pub enum PeriClkSrc { // Gpin1 = ClkPeriCtrlAuxsrc::CLKSRC_GPIN1 as _ , } +/// Core voltage scaling options for RP2040. +/// See RP2040 Datasheet, Table 18. +#[cfg(feature = "rp2040")] +#[derive(Clone, Copy, Debug, PartialEq, Eq)] +#[repr(u8)] +pub enum VoltageScale { + /// 0.85V + V0_85 = 0b1000, + /// 0.90V + V0_90 = 0b1001, + /// 0.95V + V0_95 = 0b1010, + /// 1.00V + V1_00 = 0b1011, + /// 1.05V + V1_05 = 0b1100, + /// 1.10V (Default) + V1_10 = 0b1101, + /// 1.15V + V1_15 = 0b1110, + /// 1.20V + V1_20 = 0b1111, +} + +#[cfg(feature = "rp2040")] +impl VoltageScale { + /// Get the recommended Brown-Out Detection (BOD) setting for this voltage. + /// See RP2040 Datasheet, Table 19. + fn recommended_bod(self) -> u8 { + match self { + VoltageScale::V0_85 => 0b1000, // BOD recommends VSEL + 1 + VoltageScale::V0_90 => 0b1001, + VoltageScale::V0_95 => 0b1010, + VoltageScale::V1_00 => 0b1011, + VoltageScale::V1_05 => 0b1100, + VoltageScale::V1_10 => 0b1101, // Default + VoltageScale::V1_15 => 0b1110, + VoltageScale::V1_20 => 0b1111, + } + } +} + /// CLock configuration. #[non_exhaustive] pub struct ClockConfig { @@ -89,6 +131,9 @@ pub struct ClockConfig { /// RTC clock configuration. #[cfg(feature = "rp2040")] pub rtc_clk: Option, + /// Core voltage scaling (RP2040 only). Defaults to 1.10V if None. + #[cfg(feature = "rp2040")] + pub voltage_scale: Option, // gpin0: Option<(u32, Gpin<'static, AnyPin>)>, // gpin1: Option<(u32, Gpin<'static, AnyPin>)>, } @@ -152,6 +197,93 @@ impl ClockConfig { div_frac: 0, phase: 0, }), + #[cfg(feature = "rp2040")] + voltage_scale: None, // Use hardware default (1.10V) + // gpin0: None, + // gpin1: None, + } + } + + /// Clock configuration derived from external crystal, targeting a specific SYS clock frequency for RP2040. + /// + /// This function calculates the required PLL settings and core voltage based on the target frequency. + /// + /// # Arguments + /// + /// * `crystal_hz`: The frequency of the external crystal (e.g., 12_000_000 for 12MHz). + /// * `sys_freq_hz`: The target system clock frequency. + /// + /// # Panics + /// + /// Panics if the requested frequency is impossible to achieve with the given crystal, + /// or if the required voltage for the frequency is not supported or known. + /// + /// # Safety Notes (RP2040) + /// + /// * Frequencies > 133MHz require increased core voltage. + /// * This function automatically selects `VoltageScale::V1_15` for frequencies > 133MHz and <= 200MHz. + /// * Frequencies > 200MHz might require `VoltageScale::V1_20` or higher and are considered overclocking beyond datasheet recommendations. + /// This function will select `VoltageScale::V1_20` for frequencies > 200MHz, use with caution. + /// * Ensure your hardware supports the selected voltage and frequency. + #[cfg(feature = "rp2040")] + pub fn crystal_freq(crystal_hz: u32, sys_freq_hz: u32) -> Self { + // Determine required voltage based on target frequency + let voltage_scale = match sys_freq_hz { + 0..=133_000_000 => VoltageScale::V1_10, // Default voltage is sufficient + 133_000_001..=200_000_000 => VoltageScale::V1_15, // Requires 1.15V + _ => VoltageScale::V1_20, // Frequencies > 200MHz require at least 1.20V (Overclocking) + }; + + // Find suitable PLL parameters + let pll_params = find_pll_params(crystal_hz, sys_freq_hz) + .expect("Could not find valid PLL parameters for the requested frequency"); + + Self { + rosc: Some(RoscConfig { + hz: 6_500_000, + range: RoscRange::Medium, + drive_strength: [0; 8], + div: 16, + }), + xosc: Some(XoscConfig { + hz: crystal_hz, + sys_pll: Some(pll_params), + // Keep USB PLL at 48MHz for compatibility + usb_pll: Some(PllConfig { + refdiv: 1, + fbdiv: 120, + post_div1: 6, + post_div2: 5, + }), + delay_multiplier: 128, + }), + ref_clk: RefClkConfig { + src: RefClkSrc::Xosc, + div: 1, + }, + sys_clk: SysClkConfig { + src: SysClkSrc::PllSys, + div_int: 1, + div_frac: 0, + }, + peri_clk_src: Some(PeriClkSrc::Sys), + usb_clk: Some(UsbClkConfig { + src: UsbClkSrc::PllUsb, + div: 1, + phase: 0, + }), + adc_clk: Some(AdcClkConfig { + src: AdcClkSrc::PllUsb, + div: 1, + phase: 0, + }), + rtc_clk: Some(RtcClkConfig { + src: RtcClkSrc::PllUsb, + div_int: 1024, + div_frac: 0, + phase: 0, + }), + voltage_scale: Some(voltage_scale), // gpin0: None, // gpin1: None, } @@ -192,8 +324,10 @@ impl ClockConfig { div_frac: 171, phase: 0, }), - // gpin0: None, - // gpin1: None, + #[cfg(feature = "rp2040")] + voltage_scale: None, // Use hardware default (1.10V) + // gpin0: None, + // gpin1: None, } } @@ -405,6 +539,72 @@ pub struct RtcClkConfig { pub phase: u8, } +/// Find valid PLL parameters (refdiv, fbdiv, post_div1, post_div2) for a target output frequency +/// based on the input frequency. +/// +/// See RP2040 Datasheet section 2.16.3. Reference Clock (ref) and 2.18.3. PLL +#[cfg(feature = "rp2040")] +fn find_pll_params(input_hz: u32, target_hz: u32) -> Option { + // Constraints from datasheet: + // REFDIV: 1..=63 + // FBDIV: 16..=320 + // POSTDIV1: 1..=7 + // POSTDIV2: 1..=7 (must be <= POSTDIV1) + // VCO frequency (input_hz / refdiv * fbdiv): 400MHz ..= 1600MHz + + for refdiv in 1..=63 { + let ref_clk = input_hz / refdiv; + // Reference clock must be >= 5MHz (implied by VCO min / FBDIV max) + if ref_clk < 5_000_000 { + continue; + } + + for fbdiv in (16..=320).rev() { + // Iterate high fbdiv first for better VCO stability + let vco_freq = ref_clk * fbdiv; + if !(400_000_000..=1_600_000_000).contains(&vco_freq) { + continue; + } + + // We want vco_freq / (post_div1 * post_div2) = target_hz + // So, post_div1 * post_div2 = vco_freq / target_hz + let target_post_div_product = vco_freq as f64 / target_hz as f64; + if target_post_div_product < 1.0 || target_post_div_product > 49.0 { + // 7*7 = 49 + continue; + } + // Manual rounding: floor(x + 0.5) + let target_post_div_product_int = (target_post_div_product + 0.5) as u32; + if target_post_div_product_int == 0 { + continue; + } + + // Check if the rounded product gives the target frequency + if vco_freq / target_post_div_product_int != target_hz { + continue; + } + + for post_div1 in (1..=7).rev() { + // Iterate high post_div1 first + if target_post_div_product_int % post_div1 == 0 { + let post_div2 = target_post_div_product_int / post_div1; + if (1..=7).contains(&post_div2) && post_div2 <= post_div1 { + // Found a valid combination + return Some(PllConfig { + refdiv: refdiv as u8, // Cast u32 to u8 (safe: 1..=63) + fbdiv: fbdiv as u16, // Cast u32 to u16 (safe: 16..=320) + post_div1: post_div1 as u8, + post_div2: post_div2 as u8, + }); + } + } + } + } + } + + None // No valid parameters found +} + /// safety: must be called exactly once at bootup pub(crate) unsafe fn init(config: ClockConfig) { // Reset everything except: @@ -447,23 +647,14 @@ pub(crate) unsafe fn init(config: ClockConfig) { reset::reset(peris); reset::unreset_wait(peris); - // let gpin0_freq = config.gpin0.map_or(0, |p| { - // core::mem::forget(p.1); - // p.0 - // }); - // CLOCKS.gpin0.store(gpin0_freq, Ordering::Relaxed); - // let gpin1_freq = config.gpin1.map_or(0, |p| { - // core::mem::forget(p.1); - // p.0 - // }); - // CLOCKS.gpin1.store(gpin1_freq, Ordering::Relaxed); - + // Configure ROSC first if present let rosc_freq = match config.rosc { Some(config) => configure_rosc(config), None => 0, }; CLOCKS.rosc.store(rosc_freq, Ordering::Relaxed); + // Configure XOSC and PLLs if present let (xosc_freq, pll_sys_freq, pll_usb_freq) = match config.xosc { Some(config) => { // start XOSC @@ -488,6 +679,7 @@ pub(crate) unsafe fn init(config: ClockConfig) { CLOCKS.pll_sys.store(pll_sys_freq, Ordering::Relaxed); CLOCKS.pll_usb.store(pll_usb_freq, Ordering::Relaxed); + // Configure REF clock source and divider let (ref_src, ref_aux, clk_ref_freq) = { use {ClkRefCtrlAuxsrc as Aux, ClkRefCtrlSrc as Src}; let div = config.ref_clk.div as u32; @@ -514,7 +706,7 @@ pub(crate) unsafe fn init(config: ClockConfig) { w.set_int(config.ref_clk.div); }); - // Configure tick generation on the 2040. + // Configure tick generation using REF clock #[cfg(feature = "rp2040")] pac::WATCHDOG.tick().write(|w| { w.set_cycles((clk_ref_freq / 1_000_000) as u16); @@ -532,6 +724,28 @@ pub(crate) unsafe fn init(config: ClockConfig) { pac::TICKS.watchdog_ctrl().write(|w| w.set_enable(true)); } + // Set Core Voltage (RP2040 only) BEFORE switching SYS clock to high speed PLL + #[cfg(feature = "rp2040")] + if let Some(voltage) = config.voltage_scale { + let vreg = pac::VREG_AND_CHIP_RESET; + let current_vsel = vreg.vreg().read().vsel(); + let target_vsel = voltage as u8; + + if target_vsel != current_vsel { + // Set voltage and recommended BOD level + vreg.bod().write(|w| w.set_vsel(voltage.recommended_bod())); + vreg.vreg().write(|w| w.set_vsel(target_vsel)); + + // Wait 10us for regulator to settle. Delay calculation uses REF clock + // as it's guaranteed to be stable here, before SYS potentially switches. + // 10 us = 1/100_000 s. cycles = freq * time. + let delay_cycles = clk_ref_freq / 100_000; + // delay(N) waits N+1 cycles. + cortex_m::asm::delay(delay_cycles.saturating_sub(1)); + } + } + + // Configure SYS clock source and divider let (sys_src, sys_aux, clk_sys_freq) = { use {ClkSysCtrlAuxsrc as Aux, ClkSysCtrlSrc as Src}; let (src, aux, freq) = match config.sys_clk.src { From 713d6291d569cf44ce3a53bc93ddd7d569fb93ed Mon Sep 17 00:00:00 2001 From: 1-rafael-1 Date: Sat, 26 Apr 2025 21:54:48 +0200 Subject: [PATCH 14/97] Scale clock dividers in HD44780, rotary encoder, and stepper driver based on system clock frequency --- embassy-rp/src/pio_programs/hd44780.rs | 15 +++++++++++++-- embassy-rp/src/pio_programs/rotary_encoder.rs | 8 +++++++- embassy-rp/src/pio_programs/stepper.rs | 5 +++-- 3 files changed, 23 insertions(+), 5 deletions(-) diff --git a/embassy-rp/src/pio_programs/hd44780.rs b/embassy-rp/src/pio_programs/hd44780.rs index 5846a8027..3aa54495f 100644 --- a/embassy-rp/src/pio_programs/hd44780.rs +++ b/embassy-rp/src/pio_programs/hd44780.rs @@ -1,5 +1,6 @@ //! [HD44780 display driver](https://www.sparkfun.com/datasheets/LCD/HD44780.pdf) +use crate::clocks::clk_sys_freq; use crate::dma::{AnyChannel, Channel}; use crate::pio::{ Common, Config, Direction, FifoJoin, Instance, Irq, LoadedProgram, PioPin, ShiftConfig, ShiftDirection, @@ -134,7 +135,12 @@ impl<'l, P: Instance, const S: usize> PioHD44780<'l, P, S> { let mut cfg = Config::default(); cfg.use_program(&word_prg.prg, &[&e]); - cfg.clock_divider = 125u8.into(); + + // Scale the divider based on system clock frequency + // Original: 125 at 125 MHz (1 MHz PIO clock) + let word_divider = (clk_sys_freq() / 1_000_000) as u8; // Target 1 MHz PIO clock + cfg.clock_divider = word_divider.into(); + cfg.set_out_pins(&[&db4, &db5, &db6, &db7]); cfg.shift_out = ShiftConfig { auto_fill: true, @@ -160,7 +166,12 @@ impl<'l, P: Instance, const S: usize> PioHD44780<'l, P, S> { let mut cfg = Config::default(); cfg.use_program(&seq_prg.prg, &[&e]); - cfg.clock_divider = 8u8.into(); // ~64ns/insn + + // Original: 8 at 125 MHz (~15.6 MHz PIO clock) + // Comment says ~64ns/insn which is 1/(15.6 MHz) = ~64ns + let seq_divider = (clk_sys_freq() / 15_600_000) as u8; // Target ~15.6 MHz PIO clock (~64ns/insn) + cfg.clock_divider = seq_divider.into(); + cfg.set_jmp_pin(&db7); cfg.set_set_pins(&[&rs, &rw]); cfg.set_out_pins(&[&db4, &db5, &db6, &db7]); diff --git a/embassy-rp/src/pio_programs/rotary_encoder.rs b/embassy-rp/src/pio_programs/rotary_encoder.rs index e520da8a3..7bd463bb8 100644 --- a/embassy-rp/src/pio_programs/rotary_encoder.rs +++ b/embassy-rp/src/pio_programs/rotary_encoder.rs @@ -2,6 +2,7 @@ use fixed::traits::ToFixed; +use crate::clocks::clk_sys_freq; use crate::gpio::Pull; use crate::pio::{ Common, Config, Direction as PioDirection, FifoJoin, Instance, LoadedProgram, PioPin, ShiftDirection, StateMachine, @@ -48,7 +49,12 @@ impl<'d, T: Instance, const SM: usize> PioEncoder<'d, T, SM> { cfg.set_in_pins(&[&pin_a, &pin_b]); cfg.fifo_join = FifoJoin::RxOnly; cfg.shift_in.direction = ShiftDirection::Left; - cfg.clock_divider = 10_000.to_fixed(); + + // Original: 10_000 at 125 MHz (12.5 KHz PIO clock) + // Scale divider to maintain same PIO clock frequency at different system clocks + let divider = (clk_sys_freq() as f32 / 12_500.0).to_fixed(); + cfg.clock_divider = divider; + cfg.use_program(&program.prg, &[]); sm.set_config(&cfg); sm.set_enable(true); diff --git a/embassy-rp/src/pio_programs/stepper.rs b/embassy-rp/src/pio_programs/stepper.rs index 495191659..6878c32f5 100644 --- a/embassy-rp/src/pio_programs/stepper.rs +++ b/embassy-rp/src/pio_programs/stepper.rs @@ -6,6 +6,7 @@ use fixed::traits::ToFixed; use fixed::types::extra::U8; use fixed::FixedU32; +use crate::clocks::clk_sys_freq; use crate::pio::{Common, Config, Direction, Instance, Irq, LoadedProgram, PioPin, StateMachine}; use crate::Peri; @@ -64,7 +65,7 @@ impl<'d, T: Instance, const SM: usize> PioStepper<'d, T, SM> { sm.set_pin_dirs(Direction::Out, &[&pin0, &pin1, &pin2, &pin3]); let mut cfg = Config::default(); cfg.set_out_pins(&[&pin0, &pin1, &pin2, &pin3]); - cfg.clock_divider = (125_000_000 / (100 * 136)).to_fixed(); + cfg.clock_divider = (clk_sys_freq() / (100 * 136)).to_fixed(); cfg.use_program(&program.prg, &[]); sm.set_config(&cfg); sm.set_enable(true); @@ -73,7 +74,7 @@ impl<'d, T: Instance, const SM: usize> PioStepper<'d, T, SM> { /// Set pulse frequency pub fn set_frequency(&mut self, freq: u32) { - let clock_divider: FixedU32 = (125_000_000 / (freq * 136)).to_fixed(); + let clock_divider: FixedU32 = (clk_sys_freq() / (freq * 136)).to_fixed(); assert!(clock_divider <= 65536, "clkdiv must be <= 65536"); assert!(clock_divider >= 1, "clkdiv must be >= 1"); self.sm.set_clock_divider(clock_divider); From 45b7127d614ddc65181249e70d94422500865ecd Mon Sep 17 00:00:00 2001 From: 1-rafael-1 Date: Sat, 26 Apr 2025 21:55:16 +0200 Subject: [PATCH 15/97] fmt --- embassy-rp/src/pio_programs/rotary_encoder.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/embassy-rp/src/pio_programs/rotary_encoder.rs b/embassy-rp/src/pio_programs/rotary_encoder.rs index 7bd463bb8..71567a602 100644 --- a/embassy-rp/src/pio_programs/rotary_encoder.rs +++ b/embassy-rp/src/pio_programs/rotary_encoder.rs @@ -49,12 +49,12 @@ impl<'d, T: Instance, const SM: usize> PioEncoder<'d, T, SM> { cfg.set_in_pins(&[&pin_a, &pin_b]); cfg.fifo_join = FifoJoin::RxOnly; cfg.shift_in.direction = ShiftDirection::Left; - + // Original: 10_000 at 125 MHz (12.5 KHz PIO clock) // Scale divider to maintain same PIO clock frequency at different system clocks let divider = (clk_sys_freq() as f32 / 12_500.0).to_fixed(); cfg.clock_divider = divider; - + cfg.use_program(&program.prg, &[]); sm.set_config(&cfg); sm.set_enable(true); From b0594d16f238f803a0192810833ae2b0c3941ec3 Mon Sep 17 00:00:00 2001 From: 1-rafael-1 Date: Sat, 26 Apr 2025 22:55:24 +0200 Subject: [PATCH 16/97] Add overclock example for RP2040 with 200 MHz clock configuration --- examples/rp/src/bin/overclock.rs | 60 ++++++++++++++++++++++++++++++++ 1 file changed, 60 insertions(+) create mode 100644 examples/rp/src/bin/overclock.rs diff --git a/examples/rp/src/bin/overclock.rs b/examples/rp/src/bin/overclock.rs new file mode 100644 index 000000000..429fff1ac --- /dev/null +++ b/examples/rp/src/bin/overclock.rs @@ -0,0 +1,60 @@ +#![no_std] +#![no_main] + +use defmt::*; +use embassy_executor::Spawner; +use embassy_rp::clocks::{clk_sys_freq, ClockConfig}; +use embassy_rp::config::Config; +use embassy_rp::gpio::{Level, Output}; +use embassy_time::{Duration, Instant, Timer}; +use {defmt_rtt as _, panic_probe as _}; + +const COUNT_TO: i32 = 1_000_000; + +#[embassy_executor::main] +async fn main(_spawner: Spawner) -> ! { + // Set up for clock frequency of 200 MHz + // We will need a clock config in the HAL config that supports this frequency + // The RP2040 can run at 200 MHz with a 12 MHz crystal + let config = Config::new(ClockConfig::crystal_freq(12_000_000, 200_000_000)); + + // Initialize the peripherals + let p = embassy_rp::init(config); + + // Show CPU frequency for verification + let sys_freq = clk_sys_freq(); + info!("System clock frequency: {} Hz", sys_freq); + + // LED to indicate the system is running + let mut led = Output::new(p.PIN_25, Level::Low); + + loop { + // Reset the counter at the start of measurement period + let mut counter = 0; + + // Turn LED on while counting + led.set_high(); + + let start = Instant::now(); + + // Count to COUNT_TO + // This is a busy loop that will take some time to complete + while counter < COUNT_TO { + counter += 1; + } + + let elapsed = start - Instant::now(); + + // Report the elapsed time + led.set_low(); + info!( + "At {}Mhz: Elapsed time to count to {}: {}ms", + sys_freq / 1_000_000, + COUNT_TO, + elapsed.as_millis() + ); + + // Wait 2 seconds before starting the next measurement + Timer::after(Duration::from_secs(2)).await; + } +} From 74cb84eb4e4be75859deb6fa4896efae5345eacb Mon Sep 17 00:00:00 2001 From: Michael Medin Date: Mon, 28 Apr 2025 09:14:56 +0200 Subject: [PATCH 17/97] Moved functions to rcc module (this is a bit awkward as we now have two init functions in rcc: `rcc::init`and `rcc::init_rcc`) --- embassy-stm32/src/lib.rs | 32 +------------------------------- embassy-stm32/src/rcc/mod.rs | 30 ++++++++++++++++++++++++++++++ 2 files changed, 31 insertions(+), 31 deletions(-) diff --git a/embassy-stm32/src/lib.rs b/embassy-stm32/src/lib.rs index 444d14f28..3e84d3386 100644 --- a/embassy-stm32/src/lib.rs +++ b/embassy-stm32/src/lib.rs @@ -213,7 +213,6 @@ macro_rules! bind_interrupts { // Reexports pub use _generated::{peripherals, Peripherals}; -use critical_section::CriticalSection; pub use embassy_hal_internal::{Peri, PeripheralType}; #[cfg(feature = "unstable-pac")] pub use stm32_metapac as pac; @@ -601,38 +600,9 @@ fn init_hw(config: Config) -> Peripherals { #[cfg(feature = "exti")] exti::init(cs); - init_rcc(cs, config.rcc); + rcc::init_rcc(cs, config.rcc); } p }) } - -/// Re-initialize the `embassy-stm32` clock configuration with the provided configuration. -/// -/// This is useful when you need to alter the CPU clock after configuring peripherals. -/// For instance, configure an external clock via spi or i2c. -/// -/// Please not this only re-configures the rcc and the time driver (not GPIO, EXTI, etc). -/// -/// This should only be called after `init`. -#[cfg(not(feature = "_dual-core"))] -pub fn reinit(config: rcc::Config) { - critical_section::with(|cs| init_rcc(cs, config)) -} - -fn init_rcc(_cs: CriticalSection, config: rcc::Config) { - unsafe { - rcc::init(config); - - // must be after rcc init - #[cfg(feature = "_time-driver")] - time_driver::init(_cs); - - #[cfg(feature = "low-power")] - { - crate::rcc::REFCOUNT_STOP2 = 0; - crate::rcc::REFCOUNT_STOP1 = 0; - } - } -} diff --git a/embassy-stm32/src/rcc/mod.rs b/embassy-stm32/src/rcc/mod.rs index 4f43d3748..cf88cfad6 100644 --- a/embassy-stm32/src/rcc/mod.rs +++ b/embassy-stm32/src/rcc/mod.rs @@ -34,6 +34,7 @@ pub use _version::*; use stm32_metapac::RCC; pub use crate::_generated::{mux, Clocks}; +use crate::rcc; use crate::time::Hertz; #[cfg(feature = "low-power")] @@ -369,3 +370,32 @@ pub fn enable_and_reset() { pub fn disable() { T::RCC_INFO.disable(); } + +/// Re-initialize the `embassy-stm32` clock configuration with the provided configuration. +/// +/// This is useful when you need to alter the CPU clock after configuring peripherals. +/// For instance, configure an external clock via spi or i2c. +/// +/// Please not this only re-configures the rcc and the time driver (not GPIO, EXTI, etc). +/// +/// This should only be called after `init`. +#[cfg(not(feature = "_dual-core"))] +pub fn reinit(config: Config) { + critical_section::with(|cs| init_rcc(cs, config)) +} + +fn init_rcc(_cs: CriticalSection, config: Config) { + unsafe { + init(config); + + // must be after rcc init + #[cfg(feature = "_time-driver")] + crate::time_driver::init(_cs); + + #[cfg(feature = "low-power")] + { + REFCOUNT_STOP2 = 0; + REFCOUNT_STOP1 = 0; + } + } +} From 1d578f5a7e1fe2b676f36b1960a93af899418c91 Mon Sep 17 00:00:00 2001 From: Michael Medin Date: Mon, 28 Apr 2025 09:21:21 +0200 Subject: [PATCH 18/97] function needs to be pub(crate) --- embassy-stm32/src/rcc/mod.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/embassy-stm32/src/rcc/mod.rs b/embassy-stm32/src/rcc/mod.rs index cf88cfad6..22bec6d1a 100644 --- a/embassy-stm32/src/rcc/mod.rs +++ b/embassy-stm32/src/rcc/mod.rs @@ -384,7 +384,7 @@ pub fn reinit(config: Config) { critical_section::with(|cs| init_rcc(cs, config)) } -fn init_rcc(_cs: CriticalSection, config: Config) { +pub(crate) fn init_rcc(_cs: CriticalSection, config: Config) { unsafe { init(config); From a94cc79b9b5e01f7b207947a1dfa30432e508a9c Mon Sep 17 00:00:00 2001 From: Michael Medin Date: Mon, 28 Apr 2025 18:52:03 +0200 Subject: [PATCH 19/97] removed unused import --- embassy-stm32/src/rcc/mod.rs | 1 - 1 file changed, 1 deletion(-) diff --git a/embassy-stm32/src/rcc/mod.rs b/embassy-stm32/src/rcc/mod.rs index 22bec6d1a..3c00d5dfb 100644 --- a/embassy-stm32/src/rcc/mod.rs +++ b/embassy-stm32/src/rcc/mod.rs @@ -34,7 +34,6 @@ pub use _version::*; use stm32_metapac::RCC; pub use crate::_generated::{mux, Clocks}; -use crate::rcc; use crate::time::Hertz; #[cfg(feature = "low-power")] From 3a6dc910ffc66d4a30b89f299432b383271a719f Mon Sep 17 00:00:00 2001 From: 1-rafael-1 Date: Mon, 28 Apr 2025 22:54:15 +0200 Subject: [PATCH 20/97] first working draft --- embassy-rp/src/clocks.rs | 666 +++++++++++++++++++++++-------- examples/rp/src/bin/overclock.rs | 26 +- 2 files changed, 509 insertions(+), 183 deletions(-) diff --git a/embassy-rp/src/clocks.rs b/embassy-rp/src/clocks.rs index ba7b139a6..b74e90f5e 100644 --- a/embassy-rp/src/clocks.rs +++ b/embassy-rp/src/clocks.rs @@ -70,43 +70,51 @@ pub enum PeriClkSrc { } /// Core voltage scaling options for RP2040. -/// See RP2040 Datasheet, Table 18. +/// See RP2040 Datasheet, Table 189, VREG Register. #[cfg(feature = "rp2040")] #[derive(Clone, Copy, Debug, PartialEq, Eq)] #[repr(u8)] pub enum VoltageScale { /// 0.85V - V0_85 = 0b1000, + V0_85 = 0b0110, /// 0.90V - V0_90 = 0b1001, + V0_90 = 0b0111, /// 0.95V - V0_95 = 0b1010, + V0_95 = 0b1000, /// 1.00V - V1_00 = 0b1011, + V1_00 = 0b1001, /// 1.05V - V1_05 = 0b1100, + V1_05 = 0b1010, /// 1.10V (Default) - V1_10 = 0b1101, + V1_10 = 0b1011, /// 1.15V - V1_15 = 0b1110, + V1_15 = 0b1100, /// 1.20V - V1_20 = 0b1111, + V1_20 = 0b1101, + /// 1.25V + V1_25 = 0b1110, + /// 1.30V + V1_30 = 0b1111, } #[cfg(feature = "rp2040")] impl VoltageScale { /// Get the recommended Brown-Out Detection (BOD) setting for this voltage. - /// See RP2040 Datasheet, Table 19. + /// Sets the BOD threshold to approximately 90% of the core voltage. + /// See RP2040 Datasheet, Table 190, BOD Register fn recommended_bod(self) -> u8 { match self { - VoltageScale::V0_85 => 0b1000, // BOD recommends VSEL + 1 - VoltageScale::V0_90 => 0b1001, - VoltageScale::V0_95 => 0b1010, - VoltageScale::V1_00 => 0b1011, - VoltageScale::V1_05 => 0b1100, - VoltageScale::V1_10 => 0b1101, // Default - VoltageScale::V1_15 => 0b1110, - VoltageScale::V1_20 => 0b1111, + // ~90% of voltage setting based on Table 190 values + VoltageScale::V0_85 => 0b0111, // 0.774V (~91% of 0.85V) + VoltageScale::V0_90 => 0b1000, // 0.817V (~91% of 0.90V) + VoltageScale::V0_95 => 0b1001, // 0.860V (~91% of 0.95V) + VoltageScale::V1_00 => 0b1010, // 0.903V (~90% of 1.00V) + VoltageScale::V1_05 => 0b1011, // 0.946V (~90% of 1.05V) + VoltageScale::V1_10 => 0b1100, // 0.989V (~90% of 1.10V) + VoltageScale::V1_15 => 0b1101, // 1.032V (~90% of 1.15V) + VoltageScale::V1_20 => 0b1110, // 1.075V (~90% of 1.20V) + VoltageScale::V1_25 => 0b1111, // 1.118V (~89% of 1.25V) + VoltageScale::V1_30 => 0b1111, // 1.118V (~86% of 1.30V) - using max available threshold } } } @@ -134,6 +142,9 @@ pub struct ClockConfig { /// Core voltage scaling (RP2040 only). Defaults to 1.10V if None. #[cfg(feature = "rp2040")] pub voltage_scale: Option, + /// Voltage stabilization delay in microseconds. + #[cfg(feature = "rp2040")] + pub voltage_stabilization_delay_us: Option, // gpin0: Option<(u32, Gpin<'static, AnyPin>)>, // gpin1: Option<(u32, Gpin<'static, AnyPin>)>, } @@ -199,8 +210,10 @@ impl ClockConfig { }), #[cfg(feature = "rp2040")] voltage_scale: None, // Use hardware default (1.10V) - // gpin0: None, - // gpin1: None, + #[cfg(feature = "rp2040")] + voltage_stabilization_delay_us: None, + // gpin0: None, + // gpin1: None, } } @@ -235,8 +248,16 @@ impl ClockConfig { }; // Find suitable PLL parameters - let pll_params = find_pll_params(crystal_hz, sys_freq_hz) - .expect("Could not find valid PLL parameters for the requested frequency"); + let pll_params = match find_pll_params(crystal_hz, sys_freq_hz) { + Some(params) => params, + None => { + // If we can't find valid parameters for the requested frequency, + // fall back to safe defaults (125 MHz for RP2040) + let safe_freq = 125_000_000; // Safe default frequency + find_pll_params(crystal_hz, safe_freq) + .expect("Could not find valid PLL parameters even for safe default frequency") + } + }; Self { rosc: Some(RoscConfig { @@ -284,6 +305,8 @@ impl ClockConfig { phase: 0, }), voltage_scale: Some(voltage_scale), + #[cfg(feature = "rp2040")] + voltage_stabilization_delay_us: None, // gpin0: None, // gpin1: None, } @@ -326,11 +349,128 @@ impl ClockConfig { }), #[cfg(feature = "rp2040")] voltage_scale: None, // Use hardware default (1.10V) - // gpin0: None, - // gpin1: None, + #[cfg(feature = "rp2040")] + voltage_stabilization_delay_us: None, + // gpin0: None, + // gpin1: None, } } + /// Configure the system clock to a specific frequency in MHz. + /// + /// This is a more user-friendly way to configure the system clock, similar to + /// the Pico SDK's approach. It automatically handles voltage scaling based on the + /// requested frequency and uses the standard 12MHz crystal found on most RP2040 boards. + /// + /// # Arguments + /// + /// * `sys_freq_mhz` - The target system clock frequency in MHz + /// + /// # Safety Notes + /// + /// * Frequencies > 133MHz require increased core voltage and are considered overclocking. + /// * Frequencies > 250MHz are extreme overclocking and may not be stable on all chips. + /// + /// # Example + /// + /// ``` + /// // Configure for standard 125MHz clock + /// let config = ClockConfig::with_speed_mhz(125); + /// + /// // Overclock to 200MHz (requires higher voltage) + /// let config = ClockConfig::with_speed_mhz(200); + /// ``` + #[cfg(feature = "rp2040")] + pub fn with_speed_mhz(sys_freq_mhz: u32) -> Self { + // For 125MHz, use exactly the same config as the default to avoid any differences + if sys_freq_mhz == 125 { + return Self::crystal(12_000_000); + } + + // For other frequencies, provide appropriate voltage scaling and PLL configuration + // Standard crystal on Raspberry Pi Pico boards is 12MHz + const DEFAULT_CRYSTAL_HZ: u32 = 12_000_000; + + let sys_freq_hz = sys_freq_mhz * 1_000_000; + let mut config = Self::crystal_freq(DEFAULT_CRYSTAL_HZ, sys_freq_hz); + + // For frequencies above 200MHz, ensure we're using the highest voltage + if sys_freq_mhz > 200 { + config.voltage_scale = Some(VoltageScale::V1_20); + } + + config + } + + /// Configure the system clock to a specific frequency in Hz, using a custom crystal frequency. + /// + /// This more flexible version allows specifying both the crystal frequency and target + /// system frequency for boards with non-standard crystals. + /// + /// # Arguments + /// + /// * `crystal_hz` - The frequency of the external crystal in Hz + /// * `sys_freq_hz` - The target system clock frequency in Hz + /// + /// # Safety Notes + /// + /// * Frequencies > 133MHz require increased core voltage and are considered overclocking. + /// * Frequencies > 250MHz are extreme overclocking and may not be stable on all chips. + /// + /// # Example + /// + /// ``` + /// // Use a non-standard 16MHz crystal to achieve 250MHz + /// let config = ClockConfig::with_custom_crystal(16_000_000, 250_000_000); + /// ``` + #[cfg(feature = "rp2040")] + pub fn with_custom_crystal(crystal_hz: u32, sys_freq_hz: u32) -> Self { + Self::crystal_freq(crystal_hz, sys_freq_hz) + } + + #[cfg(feature = "rp2040")] + pub fn with_speed_mhz_test_voltage(sys_freq_mhz: u32, voltage: Option) -> Self { + // Create a config with the requested frequency + let sys_freq_hz = sys_freq_mhz * 1_000_000; + let mut config = Self::crystal_freq(12_000_000, sys_freq_hz); + + // Override the voltage setting + config.voltage_scale = voltage; + + // For debugging + // println!("Debug: Setting freq to {}MHz with voltage {:?}", sys_freq_mhz, voltage); + + config + } + + /// Similar to `with_speed_mhz_test_voltage` but with an extended voltage stabilization delay. + /// + /// This function is useful for testing voltage stability issues where the default delay + /// may not be sufficient for the voltage regulator to fully stabilize. + /// + /// # Arguments + /// + /// * `sys_freq_mhz` - The target system clock frequency in MHz + /// * `voltage` - The desired voltage scale setting + /// * `stabilization_delay_us` - Voltage stabilization delay in microseconds (default: 500μs) + #[cfg(feature = "rp2040")] + pub fn with_speed_mhz_test_voltage_extended_delay( + sys_freq_mhz: u32, + voltage: Option, + stabilization_delay_us: Option, + ) -> Self { + // Create a config with the requested frequency + let sys_freq_hz = sys_freq_mhz * 1_000_000; + let mut config = Self::crystal_freq(12_000_000, sys_freq_hz); + + // Override the voltage setting + config.voltage_scale = voltage; + + // Add a custom voltage stabilization delay + config.voltage_stabilization_delay_us = stabilization_delay_us; + + config + } // pub fn bind_gpin(&mut self, gpin: Gpin<'static, P>, hz: u32) { // match P::NR { // 0 => self.gpin0 = Some((hz, gpin.into())), @@ -385,6 +525,7 @@ pub struct XoscConfig { } /// PLL configuration. +#[derive(Clone, Copy, Debug)] pub struct PllConfig { /// Reference divisor. pub refdiv: u8, @@ -542,67 +683,124 @@ pub struct RtcClkConfig { /// Find valid PLL parameters (refdiv, fbdiv, post_div1, post_div2) for a target output frequency /// based on the input frequency. /// +/// Similar to the Pico SDK's parameter selection approach, prioritizing stability. /// See RP2040 Datasheet section 2.16.3. Reference Clock (ref) and 2.18.3. PLL #[cfg(feature = "rp2040")] fn find_pll_params(input_hz: u32, target_hz: u32) -> Option { + // Fixed reference divider for system PLL + const PLL_SYS_REFDIV: u8 = 1; + // Constraints from datasheet: // REFDIV: 1..=63 // FBDIV: 16..=320 // POSTDIV1: 1..=7 // POSTDIV2: 1..=7 (must be <= POSTDIV1) - // VCO frequency (input_hz / refdiv * fbdiv): 400MHz ..= 1600MHz + // VCO frequency (input_hz / refdiv * fbdiv): 750MHz ..= 1800MHz - for refdiv in 1..=63 { - let ref_clk = input_hz / refdiv; - // Reference clock must be >= 5MHz (implied by VCO min / FBDIV max) - if ref_clk < 5_000_000 { + // Calculate reference frequency + let reference_freq = input_hz / PLL_SYS_REFDIV as u32; + + // Start from highest fbdiv for better stability (like SDK does) + for fbdiv in (16..=320).rev() { + let vco_freq = reference_freq * fbdiv; + + // Check VCO frequency is within valid range + if vco_freq < 750_000_000 || vco_freq > 1_800_000_000 { continue; } - for fbdiv in (16..=320).rev() { - // Iterate high fbdiv first for better VCO stability - let vco_freq = ref_clk * fbdiv; - if !(400_000_000..=1_600_000_000).contains(&vco_freq) { - continue; - } + // Try all possible postdiv combinations starting from larger values + // (more conservative/stable approach) + for post_div1 in (1..=7).rev() { + for post_div2 in (1..=post_div1).rev() { + let out_freq = vco_freq / (post_div1 * post_div2) as u32; - // We want vco_freq / (post_div1 * post_div2) = target_hz - // So, post_div1 * post_div2 = vco_freq / target_hz - let target_post_div_product = vco_freq as f64 / target_hz as f64; - if target_post_div_product < 1.0 || target_post_div_product > 49.0 { - // 7*7 = 49 - continue; - } - // Manual rounding: floor(x + 0.5) - let target_post_div_product_int = (target_post_div_product + 0.5) as u32; - if target_post_div_product_int == 0 { - continue; - } - - // Check if the rounded product gives the target frequency - if vco_freq / target_post_div_product_int != target_hz { - continue; - } - - for post_div1 in (1..=7).rev() { - // Iterate high post_div1 first - if target_post_div_product_int % post_div1 == 0 { - let post_div2 = target_post_div_product_int / post_div1; - if (1..=7).contains(&post_div2) && post_div2 <= post_div1 { - // Found a valid combination - return Some(PllConfig { - refdiv: refdiv as u8, // Cast u32 to u8 (safe: 1..=63) - fbdiv: fbdiv as u16, // Cast u32 to u16 (safe: 16..=320) - post_div1: post_div1 as u8, - post_div2: post_div2 as u8, - }); - } + // Check if we get the exact target frequency without remainder + if out_freq == target_hz && (vco_freq % (post_div1 * post_div2) as u32 == 0) { + return Some(PllConfig { + refdiv: PLL_SYS_REFDIV, + fbdiv: fbdiv as u16, + post_div1: post_div1 as u8, + post_div2: post_div2 as u8, + }); } } } } - None // No valid parameters found + // If we couldn't find an exact match, find the closest match + let mut best_config = None; + let mut min_diff = u32::MAX; + + for fbdiv in (16..=320).rev() { + let vco_freq = reference_freq * fbdiv; + + if vco_freq < 750_000_000 || vco_freq > 1_800_000_000 { + continue; + } + + for post_div1 in (1..=7).rev() { + for post_div2 in (1..=post_div1).rev() { + let out_freq = vco_freq / (post_div1 * post_div2) as u32; + let diff = if out_freq > target_hz { + out_freq - target_hz + } else { + target_hz - out_freq + }; + + // If this is closer to the target, save it + if diff < min_diff { + min_diff = diff; + best_config = Some(PllConfig { + refdiv: PLL_SYS_REFDIV, + fbdiv: fbdiv as u16, + post_div1: post_div1 as u8, + post_div2: post_div2 as u8, + }); + } + } + } + } + + // Return the closest match if we found one + best_config +} + +#[cfg(feature = "rp2040")] +pub fn compare_pll_params() { + // Parameters from default configuration + let default_params = PllConfig { + refdiv: 1, + fbdiv: 125, + post_div1: 6, + post_div2: 2, + }; + + // Calculate parameters using our find_pll_params function + let crystal_hz = 12_000_000; + let target_hz = 125_000_000; + let calculated_params = find_pll_params(crystal_hz, target_hz).expect("Failed to find PLL parameters"); + + // Check if they're identical + let params_match = default_params.refdiv == calculated_params.refdiv + && default_params.fbdiv == calculated_params.fbdiv + && default_params.post_div1 == calculated_params.post_div1 + && default_params.post_div2 == calculated_params.post_div2; + + // Here we'd normally print results, but without a console we'll just + // use this for debugging in our IDE + let _default_output_freq = crystal_hz / default_params.refdiv as u32 * default_params.fbdiv as u32 + / (default_params.post_div1 * default_params.post_div2) as u32; + + let _calculated_output_freq = crystal_hz / calculated_params.refdiv as u32 * calculated_params.fbdiv as u32 + / (calculated_params.post_div1 * calculated_params.post_div2) as u32; + + // Parameters: default vs calculated + // refdiv: 1 vs {calculated_params.refdiv} + // fbdiv: 125 vs {calculated_params.fbdiv} + // post_div1: 6 vs {calculated_params.post_div1} + // post_div2: 2 vs {calculated_params.post_div2} + // params_match: {params_match} } /// safety: must be called exactly once at bootup @@ -640,12 +838,42 @@ pub(crate) unsafe fn init(config: ClockConfig) { #[cfg(feature = "_rp235x")] while c.clk_ref_selected().read() != pac::clocks::regs::ClkRefSelected(1) {} - // Reset the PLLs - let mut peris = reset::Peripherals(0); - peris.set_pll_sys(true); - peris.set_pll_usb(true); - reset::reset(peris); - reset::unreset_wait(peris); + // Set Core Voltage (RP2040 only) BEFORE doing anything with PLLs + // This is critical for overclocking - must be done before PLL setup + #[cfg(feature = "rp2040")] + if let Some(voltage) = config.voltage_scale { + let vreg = pac::VREG_AND_CHIP_RESET; + let current_vsel = vreg.vreg().read().vsel(); + let target_vsel = voltage as u8; + + if target_vsel != current_vsel { + // IMPORTANT: Use modify() instead of write() to preserve the HIZ and EN bits + // This is critical - otherwise we might disable the regulator when changing voltage + vreg.vreg().modify(|w| w.set_vsel(target_vsel)); + + // For higher voltage settings (overclocking), we need a longer stabilization time + // Default to 1000 µs (1ms) like the SDK, but allow user override + let settling_time_us = config.voltage_stabilization_delay_us.unwrap_or_else(|| { + match voltage { + VoltageScale::V1_15 => 1000, // 1ms for 1.15V (matches SDK default) + VoltageScale::V1_20 | VoltageScale::V1_25 | VoltageScale::V1_30 => 2000, // 2ms for higher voltages + _ => 500, // 500 µs for standard voltages + } + }); + + // We need a clock that's guaranteed to be running at this point + // Use ROSC which should be configured by now + let rosc_freq_rough = 6_000_000; // Rough ROSC frequency estimate + let cycles_per_us = rosc_freq_rough / 1_000_000; + let delay_cycles = settling_time_us * cycles_per_us; + + // Wait for voltage to stabilize + cortex_m::asm::delay(delay_cycles); + + // Only NOW set the BOD level after voltage has stabilized + vreg.bod().write(|w| w.set_vsel(voltage.recommended_bod())); + } + } // Configure ROSC first if present let rosc_freq = match config.rosc { @@ -654,59 +882,91 @@ pub(crate) unsafe fn init(config: ClockConfig) { }; CLOCKS.rosc.store(rosc_freq, Ordering::Relaxed); - // Configure XOSC and PLLs if present - let (xosc_freq, pll_sys_freq, pll_usb_freq) = match config.xosc { + // Configure XOSC - we'll need this for our temporary stable clock + let xosc_freq = match &config.xosc { Some(config) => { - // start XOSC - // datasheet mentions support for clock inputs into XIN, but doesn't go into - // how this is achieved. pico-sdk doesn't support this at all. start_xosc(config.hz, config.delay_multiplier); - - let pll_sys_freq = match config.sys_pll { - Some(sys_pll_config) => configure_pll(pac::PLL_SYS, config.hz, sys_pll_config), - None => 0, - }; - let pll_usb_freq = match config.usb_pll { - Some(usb_pll_config) => configure_pll(pac::PLL_USB, config.hz, usb_pll_config), - None => 0, - }; - - (config.hz, pll_sys_freq, pll_usb_freq) + config.hz } - None => (0, 0, 0), + None => 0, }; CLOCKS.xosc.store(xosc_freq, Ordering::Relaxed); - CLOCKS.pll_sys.store(pll_sys_freq, Ordering::Relaxed); - CLOCKS.pll_usb.store(pll_usb_freq, Ordering::Relaxed); - // Configure REF clock source and divider - let (ref_src, ref_aux, clk_ref_freq) = { - use {ClkRefCtrlAuxsrc as Aux, ClkRefCtrlSrc as Src}; - let div = config.ref_clk.div as u32; - assert!(div >= 1 && div <= 4); - match config.ref_clk.src { - RefClkSrc::Xosc => (Src::XOSC_CLKSRC, Aux::CLKSRC_PLL_USB, xosc_freq / div), - RefClkSrc::Rosc => (Src::ROSC_CLKSRC_PH, Aux::CLKSRC_PLL_USB, rosc_freq / div), - RefClkSrc::PllUsb => (Src::CLKSRC_CLK_REF_AUX, Aux::CLKSRC_PLL_USB, pll_usb_freq / div), - // RefClkSrc::Gpin0 => (Src::CLKSRC_CLK_REF_AUX, Aux::CLKSRC_GPIN0, gpin0_freq / div), - // RefClkSrc::Gpin1 => (Src::CLKSRC_CLK_REF_AUX, Aux::CLKSRC_GPIN1, gpin1_freq / div), - } + // SETUP TEMPORARY STABLE CLOCKS FIRST + // Configure USB PLL for our stable temporary clock + // This follows the SDK's approach of using USB PLL as a stable intermediate clock + let usb_pll_freq = match &config.xosc { + Some(config) => match &config.usb_pll { + Some(usb_pll_config) => { + // Reset USB PLL + let mut peris = reset::Peripherals(0); + peris.set_pll_usb(true); + reset::reset(peris); + reset::unreset_wait(peris); + + // Configure the USB PLL - this should give us 48MHz + let usb_pll_freq = configure_pll(pac::PLL_USB, xosc_freq, *usb_pll_config); + CLOCKS.pll_usb.store(usb_pll_freq, Ordering::Relaxed); + usb_pll_freq + } + None => 0, + }, + None => 0, }; - assert!(clk_ref_freq != 0); - CLOCKS.reference.store(clk_ref_freq, Ordering::Relaxed); + + // Configure REF clock to use XOSC c.clk_ref_ctrl().write(|w| { - w.set_src(ref_src); - w.set_auxsrc(ref_aux); + w.set_src(ClkRefCtrlSrc::XOSC_CLKSRC); }); #[cfg(feature = "rp2040")] - while c.clk_ref_selected().read() != (1 << ref_src as u32) {} + while c.clk_ref_selected().read() != (1 << ClkRefCtrlSrc::XOSC_CLKSRC as u32) {} #[cfg(feature = "_rp235x")] - while c.clk_ref_selected().read() != pac::clocks::regs::ClkRefSelected(1 << ref_src as u32) {} - c.clk_ref_div().write(|w| { - w.set_int(config.ref_clk.div); + while c.clk_ref_selected().read() != pac::clocks::regs::ClkRefSelected(1 << ClkRefCtrlSrc::XOSC_CLKSRC as u32) {} + + // First switch the system clock to a stable source (USB PLL at 48MHz) + // This follows the Pico SDK's approach to ensure stability during reconfiguration + c.clk_sys_ctrl().write(|w| { + w.set_auxsrc(ClkSysCtrlAuxsrc::CLKSRC_PLL_USB); + w.set_src(ClkSysCtrlSrc::CLKSRC_CLK_SYS_AUX); }); + #[cfg(feature = "rp2040")] + while c.clk_sys_selected().read() != (1 << ClkSysCtrlSrc::CLKSRC_CLK_SYS_AUX as u32) {} + #[cfg(feature = "_rp235x")] + while c.clk_sys_selected().read() + != pac::clocks::regs::ClkSysSelected(1 << ClkSysCtrlSrc::CLKSRC_CLK_SYS_AUX as u32) + {} + + // Short delay after switching to USB PLL to ensure stability + cortex_m::asm::delay(100); + + // NOW CONFIGURE THE SYSTEM PLL (safely, since we're running from the USB PLL) + let pll_sys_freq = match &config.xosc { + Some(config) => match &config.sys_pll { + Some(sys_pll_config) => { + // Reset SYS PLL + let mut peris = reset::Peripherals(0); + peris.set_pll_sys(true); + reset::reset(peris); + reset::unreset_wait(peris); + + // Configure the SYS PLL + let pll_sys_freq = configure_pll(pac::PLL_SYS, xosc_freq, *sys_pll_config); + + // Ensure PLL is locked and stable + cortex_m::asm::delay(100); + + CLOCKS.pll_sys.store(pll_sys_freq, Ordering::Relaxed); + pll_sys_freq + } + None => 0, + }, + None => 0, + }; + // Configure tick generation using REF clock + let clk_ref_freq = xosc_freq; // REF clock is now XOSC + CLOCKS.reference.store(clk_ref_freq, Ordering::Relaxed); #[cfg(feature = "rp2040")] pac::WATCHDOG.tick().write(|w| { w.set_cycles((clk_ref_freq / 1_000_000) as u16); @@ -724,34 +984,14 @@ pub(crate) unsafe fn init(config: ClockConfig) { pac::TICKS.watchdog_ctrl().write(|w| w.set_enable(true)); } - // Set Core Voltage (RP2040 only) BEFORE switching SYS clock to high speed PLL - #[cfg(feature = "rp2040")] - if let Some(voltage) = config.voltage_scale { - let vreg = pac::VREG_AND_CHIP_RESET; - let current_vsel = vreg.vreg().read().vsel(); - let target_vsel = voltage as u8; - - if target_vsel != current_vsel { - // Set voltage and recommended BOD level - vreg.bod().write(|w| w.set_vsel(voltage.recommended_bod())); - vreg.vreg().write(|w| w.set_vsel(target_vsel)); - - // Wait 10us for regulator to settle. Delay calculation uses REF clock - // as it's guaranteed to be stable here, before SYS potentially switches. - // 10 us = 1/100_000 s. cycles = freq * time. - let delay_cycles = clk_ref_freq / 100_000; - // delay(N) waits N+1 cycles. - cortex_m::asm::delay(delay_cycles.saturating_sub(1)); - } - } - - // Configure SYS clock source and divider + // NOW SWITCH THE SYSTEM CLOCK TO THE CONFIGURED SOURCE + // The SYS PLL is now stable and we can safely switch to it let (sys_src, sys_aux, clk_sys_freq) = { use {ClkSysCtrlAuxsrc as Aux, ClkSysCtrlSrc as Src}; let (src, aux, freq) = match config.sys_clk.src { SysClkSrc::Ref => (Src::CLK_REF, Aux::CLKSRC_PLL_SYS, clk_ref_freq), SysClkSrc::PllSys => (Src::CLKSRC_CLK_SYS_AUX, Aux::CLKSRC_PLL_SYS, pll_sys_freq), - SysClkSrc::PllUsb => (Src::CLKSRC_CLK_SYS_AUX, Aux::CLKSRC_PLL_USB, pll_usb_freq), + SysClkSrc::PllUsb => (Src::CLKSRC_CLK_SYS_AUX, Aux::CLKSRC_PLL_USB, usb_pll_freq), SysClkSrc::Rosc => (Src::CLKSRC_CLK_SYS_AUX, Aux::ROSC_CLKSRC, rosc_freq), SysClkSrc::Xosc => (Src::CLKSRC_CLK_SYS_AUX, Aux::XOSC_CLKSRC, xosc_freq), // SysClkSrc::Gpin0 => (Src::CLKSRC_CLK_SYS_AUX, Aux::CLKSRC_GPIN0, gpin0_freq), @@ -762,28 +1002,48 @@ pub(crate) unsafe fn init(config: ClockConfig) { }; assert!(clk_sys_freq != 0); CLOCKS.sys.store(clk_sys_freq, Ordering::Relaxed); - if sys_src != ClkSysCtrlSrc::CLK_REF { - c.clk_sys_ctrl().write(|w| w.set_src(ClkSysCtrlSrc::CLK_REF)); - #[cfg(feature = "rp2040")] - while c.clk_sys_selected().read() != (1 << ClkSysCtrlSrc::CLK_REF as u32) {} - #[cfg(feature = "_rp235x")] - while c.clk_sys_selected().read() != pac::clocks::regs::ClkSysSelected(1 << ClkSysCtrlSrc::CLK_REF as u32) {} + + // Set the divider before changing the source if it's increasing + if config.sys_clk.div_int > 1 || config.sys_clk.div_frac > 0 { + c.clk_sys_div().write(|w| { + w.set_int(config.sys_clk.div_int); + w.set_frac(config.sys_clk.div_frac); + }); } + + // Configure aux source first if needed + if sys_src == ClkSysCtrlSrc::CLKSRC_CLK_SYS_AUX { + c.clk_sys_ctrl().modify(|w| { + w.set_auxsrc(sys_aux); + }); + } + + // Now set the source c.clk_sys_ctrl().write(|w| { - w.set_auxsrc(sys_aux); + if sys_src == ClkSysCtrlSrc::CLKSRC_CLK_SYS_AUX { + w.set_auxsrc(sys_aux); + } w.set_src(sys_src); }); + // Wait for the clock to be selected #[cfg(feature = "rp2040")] while c.clk_sys_selected().read() != (1 << sys_src as u32) {} #[cfg(feature = "_rp235x")] while c.clk_sys_selected().read() != pac::clocks::regs::ClkSysSelected(1 << sys_src as u32) {} - c.clk_sys_div().write(|w| { - w.set_int(config.sys_clk.div_int); - w.set_frac(config.sys_clk.div_frac); - }); + // Short delay after final clock switch to ensure stability + cortex_m::asm::delay(100); + // Set the divider after changing the source if it's decreasing + if config.sys_clk.div_int == 1 && config.sys_clk.div_frac == 0 { + c.clk_sys_div().write(|w| { + w.set_int(config.sys_clk.div_int); + w.set_frac(config.sys_clk.div_frac); + }); + } + + // CONFIGURE PERIPHERAL CLOCK let mut peris = reset::ALL_PERIPHERALS; if let Some(src) = config.peri_clk_src { @@ -794,7 +1054,7 @@ pub(crate) unsafe fn init(config: ClockConfig) { let peri_freq = match src { PeriClkSrc::Sys => clk_sys_freq, PeriClkSrc::PllSys => pll_sys_freq, - PeriClkSrc::PllUsb => pll_usb_freq, + PeriClkSrc::PllUsb => usb_pll_freq, PeriClkSrc::Rosc => rosc_freq, PeriClkSrc::Xosc => xosc_freq, // PeriClkSrc::Gpin0 => gpin0_freq, @@ -810,6 +1070,7 @@ pub(crate) unsafe fn init(config: ClockConfig) { CLOCKS.peri.store(0, Ordering::Relaxed); } + // CONFIGURE USB CLOCK if let Some(conf) = config.usb_clk { c.clk_usb_div().write(|w| w.set_int(conf.div)); c.clk_usb_ctrl().write(|w| { @@ -818,7 +1079,7 @@ pub(crate) unsafe fn init(config: ClockConfig) { w.set_auxsrc(ClkUsbCtrlAuxsrc::from_bits(conf.src as _)); }); let usb_freq = match conf.src { - UsbClkSrc::PllUsb => pll_usb_freq, + UsbClkSrc::PllUsb => usb_pll_freq, UsbClkSrc::PllSys => pll_sys_freq, UsbClkSrc::Rosc => rosc_freq, UsbClkSrc::Xosc => xosc_freq, @@ -833,6 +1094,7 @@ pub(crate) unsafe fn init(config: ClockConfig) { CLOCKS.usb.store(0, Ordering::Relaxed); } + // CONFIGURE ADC CLOCK if let Some(conf) = config.adc_clk { c.clk_adc_div().write(|w| w.set_int(conf.div)); c.clk_adc_ctrl().write(|w| { @@ -841,7 +1103,7 @@ pub(crate) unsafe fn init(config: ClockConfig) { w.set_auxsrc(ClkAdcCtrlAuxsrc::from_bits(conf.src as _)); }); let adc_in_freq = match conf.src { - AdcClkSrc::PllUsb => pll_usb_freq, + AdcClkSrc::PllUsb => usb_pll_freq, AdcClkSrc::PllSys => pll_sys_freq, AdcClkSrc::Rosc => rosc_freq, AdcClkSrc::Xosc => xosc_freq, @@ -856,7 +1118,7 @@ pub(crate) unsafe fn init(config: ClockConfig) { CLOCKS.adc.store(0, Ordering::Relaxed); } - // rp2040 specific clocks + // CONFIGURE RTC CLOCK #[cfg(feature = "rp2040")] if let Some(conf) = config.rtc_clk { c.clk_rtc_div().write(|w| { @@ -869,7 +1131,7 @@ pub(crate) unsafe fn init(config: ClockConfig) { w.set_auxsrc(ClkRtcCtrlAuxsrc::from_bits(conf.src as _)); }); let rtc_in_freq = match conf.src { - RtcClkSrc::PllUsb => pll_usb_freq, + RtcClkSrc::PllUsb => usb_pll_freq, RtcClkSrc::PllSys => pll_sys_freq, RtcClkSrc::Rosc => rosc_freq, RtcClkSrc::Xosc => xosc_freq, @@ -999,43 +1261,101 @@ fn start_xosc(crystal_hz: u32, delay_multiplier: u32) { #[inline(always)] fn configure_pll(p: pac::pll::Pll, input_freq: u32, config: PllConfig) -> u32 { + // Calculate reference frequency let ref_freq = input_freq / config.refdiv as u32; - assert!(config.fbdiv >= 16 && config.fbdiv <= 320); - assert!(config.post_div1 >= 1 && config.post_div1 <= 7); - assert!(config.post_div2 >= 1 && config.post_div2 <= 7); - assert!(config.refdiv >= 1 && config.refdiv <= 63); - assert!(ref_freq >= 5_000_000 && ref_freq <= 800_000_000); + + // Validate PLL parameters + assert!( + config.fbdiv >= 16 && config.fbdiv <= 320, + "fbdiv must be between 16 and 320" + ); + assert!( + config.post_div1 >= 1 && config.post_div1 <= 7, + "post_div1 must be between 1 and 7" + ); + assert!( + config.post_div2 >= 1 && config.post_div2 <= 7, + "post_div2 must be between 1 and 7" + ); + assert!(config.post_div2 <= config.post_div1, "post_div2 must be <= post_div1"); + assert!( + config.refdiv >= 1 && config.refdiv <= 63, + "refdiv must be between 1 and 63" + ); + assert!( + ref_freq >= 5_000_000 && ref_freq <= 800_000_000, + "ref_freq must be between 5MHz and 800MHz" + ); + + // Calculate VCO frequency let vco_freq = ref_freq.saturating_mul(config.fbdiv as u32); - assert!(vco_freq >= 750_000_000 && vco_freq <= 1_800_000_000); + assert!( + vco_freq >= 750_000_000 && vco_freq <= 1_800_000_000, + "VCO frequency must be between 750MHz and 1800MHz" + ); - // Load VCO-related dividers before starting VCO - p.cs().write(|w| w.set_refdiv(config.refdiv as _)); - p.fbdiv_int().write(|w| w.set_fbdiv_int(config.fbdiv)); + // We follow the SDK's approach to PLL configuration which is: + // 1. Power down PLL + // 2. Configure the reference divider + // 3. Configure the feedback divider + // 4. Power up PLL and VCO + // 5. Wait for PLL to lock + // 6. Configure post-dividers + // 7. Enable post-divider output - // Turn on PLL - let pwr = p.pwr().write(|w| { - w.set_dsmpd(true); // "nothing is achieved by setting this low" - w.set_pd(false); - w.set_vcopd(false); - w.set_postdivpd(true); + // 1. Power down PLL before configuration + p.pwr().write(|w| { + w.set_pd(true); // Power down the PLL + w.set_vcopd(true); // Power down the VCO + w.set_postdivpd(true); // Power down the post divider + w.set_dsmpd(true); // Disable fractional mode *w }); - // Wait for PLL to lock - while !p.cs().read().lock() {} + // Short delay after powering down + cortex_m::asm::delay(10); - // Set post-dividers + // 2. Configure reference divider first + p.cs().write(|w| w.set_refdiv(config.refdiv as _)); + + // 3. Configure feedback divider + p.fbdiv_int().write(|w| w.set_fbdiv_int(config.fbdiv)); + + // 4. Power up PLL and VCO, but keep post divider powered down during initial lock + p.pwr().write(|w| { + w.set_pd(false); // Power up the PLL + w.set_vcopd(false); // Power up the VCO + w.set_postdivpd(true); // Keep post divider powered down during initial lock + w.set_dsmpd(true); // Disable fractional mode (simpler configuration) + *w + }); + + // 5. Wait for PLL to lock with a timeout + let mut timeout = 1_000_000; // Reasonable timeout value + while !p.cs().read().lock() { + timeout -= 1; + if timeout == 0 { + // PLL failed to lock, return 0 to indicate failure + return 0; + } + } + + // 6. Configure post dividers after PLL is locked p.prim().write(|w| { w.set_postdiv1(config.post_div1); w.set_postdiv2(config.post_div2); }); - // Turn on post divider - p.pwr().write(|w| { - *w = pwr; - w.set_postdivpd(false); + // 7. Enable the post divider output + p.pwr().modify(|w| { + w.set_postdivpd(false); // Power up post divider + *w }); + // Final delay to ensure everything is stable + cortex_m::asm::delay(100); + + // Calculate and return actual output frequency vco_freq / ((config.post_div1 * config.post_div2) as u32) } diff --git a/examples/rp/src/bin/overclock.rs b/examples/rp/src/bin/overclock.rs index 429fff1ac..db6c8f448 100644 --- a/examples/rp/src/bin/overclock.rs +++ b/examples/rp/src/bin/overclock.rs @@ -3,22 +3,18 @@ use defmt::*; use embassy_executor::Spawner; -use embassy_rp::clocks::{clk_sys_freq, ClockConfig}; +use embassy_rp::clocks::{clk_sys_freq, ClockConfig, VoltageScale}; use embassy_rp::config::Config; use embassy_rp::gpio::{Level, Output}; use embassy_time::{Duration, Instant, Timer}; use {defmt_rtt as _, panic_probe as _}; -const COUNT_TO: i32 = 1_000_000; +const COUNT_TO: i64 = 10_000_000; #[embassy_executor::main] async fn main(_spawner: Spawner) -> ! { // Set up for clock frequency of 200 MHz - // We will need a clock config in the HAL config that supports this frequency - // The RP2040 can run at 200 MHz with a 12 MHz crystal - let config = Config::new(ClockConfig::crystal_freq(12_000_000, 200_000_000)); - - // Initialize the peripherals + let config = Config::new(ClockConfig::with_speed_mhz(200)); let p = embassy_rp::init(config); // Show CPU frequency for verification @@ -37,20 +33,19 @@ async fn main(_spawner: Spawner) -> ! { let start = Instant::now(); - // Count to COUNT_TO // This is a busy loop that will take some time to complete while counter < COUNT_TO { counter += 1; } - let elapsed = start - Instant::now(); + let elapsed = Instant::now() - start; // Report the elapsed time led.set_low(); info!( "At {}Mhz: Elapsed time to count to {}: {}ms", sys_freq / 1_000_000, - COUNT_TO, + counter, elapsed.as_millis() ); @@ -58,3 +53,14 @@ async fn main(_spawner: Spawner) -> ! { Timer::after(Duration::from_secs(2)).await; } } + +// let config = Config::new(ClockConfig::with_speed_mhz_test_voltage(125, Some(VoltageScale::V1_10))); +// let config = Config::default(); +// let config = Config::new(ClockConfig::with_speed_mhz_test_voltage_extended_delay( +// 200, // Standard 125MHz clock +// Some(VoltageScale::V1_15), // 1.15V voltage +// Some(1000), // 1000μs (1ms) stabilization delay - significantly longer than default +// )); +// Initialize the peripherals + +// let p = embassy_rp::init(Default::default()); //testing the bog standard From b967aaf7cc90e1e8810f24968ba137141b0c35ec Mon Sep 17 00:00:00 2001 From: Dion Dokter Date: Tue, 29 Apr 2025 16:34:12 +0200 Subject: [PATCH 21/97] Add support for g0 --- embassy-stm32/src/flash/g.rs | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/embassy-stm32/src/flash/g.rs b/embassy-stm32/src/flash/g.rs index f55c5e6a7..70e472dcf 100644 --- a/embassy-stm32/src/flash/g.rs +++ b/embassy-stm32/src/flash/g.rs @@ -110,7 +110,7 @@ fn wait_busy() { while pac::FLASH.sr().read().bsy() {} } -#[cfg(bank_setup_configurable)] +#[cfg(all(bank_setup_configurable, any(flash_g4c2, flash_g4c3, flash_g4c4)))] pub(crate) fn check_bank_setup() { if cfg!(feature = "single-bank") && pac::FLASH.optr().read().dbank() { panic!("Embassy is configured as single-bank, but the hardware is running in dual-bank mode. Change the hardware by changing the dbank value in the user option bytes or configure embassy to use dual-bank config"); @@ -119,3 +119,13 @@ pub(crate) fn check_bank_setup() { panic!("Embassy is configured as dual-bank, but the hardware is running in single-bank mode. Change the hardware by changing the dbank value in the user option bytes or configure embassy to use single-bank config"); } } + +#[cfg(all(bank_setup_configurable, flash_g0x1))] +pub(crate) fn check_bank_setup() { + if cfg!(feature = "single-bank") && pac::FLASH.optr().read().dual_bank() { + panic!("Embassy is configured as single-bank, but the hardware is running in dual-bank mode. Change the hardware by changing the dual_bank value in the user option bytes or configure embassy to use dual-bank config"); + } + if cfg!(feature = "dual-bank") && !pac::FLASH.optr().read().dual_bank() { + panic!("Embassy is configured as dual-bank, but the hardware is running in single-bank mode. Change the hardware by changing the dual_bank value in the user option bytes or configure embassy to use single-bank config"); + } +} From 1c2208718863dc0ed23449393c0213c8f9194519 Mon Sep 17 00:00:00 2001 From: Dion Dokter Date: Tue, 29 Apr 2025 16:37:27 +0200 Subject: [PATCH 22/97] Add support for L5 --- embassy-stm32/src/flash/l.rs | 10 ++++++++++ examples/stm32l5/Cargo.toml | 2 +- 2 files changed, 11 insertions(+), 1 deletion(-) diff --git a/embassy-stm32/src/flash/l.rs b/embassy-stm32/src/flash/l.rs index ea00bf499..bc6f8c873 100644 --- a/embassy-stm32/src/flash/l.rs +++ b/embassy-stm32/src/flash/l.rs @@ -238,3 +238,13 @@ unsafe fn wait_ready_blocking() -> Result<(), Error> { } } } + +#[cfg(all(bank_setup_configurable, flash_l5))] +pub(crate) fn check_bank_setup() { + if cfg!(feature = "single-bank") && pac::FLASH.optr().read().dbank() { + panic!("Embassy is configured as single-bank, but the hardware is running in dual-bank mode. Change the hardware by changing the dbank value in the user option bytes or configure embassy to use dual-bank config"); + } + if cfg!(feature = "dual-bank") && !pac::FLASH.optr().read().dbank() { + panic!("Embassy is configured as dual-bank, but the hardware is running in single-bank mode. Change the hardware by changing the dbank value in the user option bytes or configure embassy to use single-bank config"); + } +} diff --git a/examples/stm32l5/Cargo.toml b/examples/stm32l5/Cargo.toml index fbf68c890..4c372a554 100644 --- a/examples/stm32l5/Cargo.toml +++ b/examples/stm32l5/Cargo.toml @@ -6,7 +6,7 @@ license = "MIT OR Apache-2.0" [dependencies] # Change stm32l552ze to your chip name, if necessary. -embassy-stm32 = { version = "0.2.0", path = "../../embassy-stm32", features = [ "defmt", "unstable-pac", "stm32l552ze", "time-driver-any", "exti", "memory-x", "low-power"] } +embassy-stm32 = { version = "0.2.0", path = "../../embassy-stm32", features = [ "defmt", "unstable-pac", "stm32l552ze", "time-driver-any", "exti", "memory-x", "low-power", "dual-bank"] } embassy-sync = { version = "0.6.2", path = "../../embassy-sync", features = ["defmt"] } embassy-executor = { version = "0.7.0", path = "../../embassy-executor", features = ["arch-cortex-m", "executor-thread", "defmt"] } embassy-time = { version = "0.4.0", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime", "tick-hz-32_768"] } From 5691c61ef9a128c84f5f5962ed736a3a757371c1 Mon Sep 17 00:00:00 2001 From: Dion Dokter Date: Tue, 29 Apr 2025 16:55:11 +0200 Subject: [PATCH 23/97] Add F4 support --- embassy-stm32/build.rs | 12 ++++-------- embassy-stm32/src/flash/f4.rs | 10 ++++++++++ 2 files changed, 14 insertions(+), 8 deletions(-) diff --git a/embassy-stm32/build.rs b/embassy-stm32/build.rs index 2a3213c0c..946aa0399 100644 --- a/embassy-stm32/build.rs +++ b/embassy-stm32/build.rs @@ -56,17 +56,13 @@ fn main() { let dual_bank_selected = env::var("CARGO_FEATURE_DUAL_BANK").is_ok(); let single_bank_memory = METADATA.memory.iter().find(|mem| { - mem.iter() - .filter(|region| region.kind == MemoryRegionKind::Flash) - .count() - == 1 + mem.iter().any(|region| region.name.contains("BANK_1")) + && !mem.iter().any(|region| region.name.contains("BANK_2")) }); let dual_bank_memory = METADATA.memory.iter().find(|mem| { - mem.iter() - .filter(|region| region.kind == MemoryRegionKind::Flash) - .count() - == 2 + mem.iter().any(|region| region.name.contains("BANK_1")) + && mem.iter().any(|region| region.name.contains("BANK_2")) }); cfgs.set( diff --git a/embassy-stm32/src/flash/f4.rs b/embassy-stm32/src/flash/f4.rs index 687eabaeb..ba3067556 100644 --- a/embassy-stm32/src/flash/f4.rs +++ b/embassy-stm32/src/flash/f4.rs @@ -555,3 +555,13 @@ mod tests { assert_sector(0x17, FlashBank::Bank2, 7, 0x080E_0000, LARGE_SECTOR_SIZE, 0x080F_FFFF); } } + +#[cfg(all(bank_setup_configurable))] +pub(crate) fn check_bank_setup() { + if cfg!(feature = "single-bank") && pac::FLASH.optcr().read().db1m() { + panic!("Embassy is configured as single-bank, but the hardware is running in dual-bank mode. Change the hardware by changing the db1m value in the user option bytes or configure embassy to use dual-bank config"); + } + if cfg!(feature = "dual-bank") && !pac::FLASH.optcr().read().db1m() { + panic!("Embassy is configured as dual-bank, but the hardware is running in single-bank mode. Change the hardware by changing the db1m value in the user option bytes or configure embassy to use single-bank config"); + } +} From a1081f29bfa0bc0940b55d7d249d9cb76c2d2e23 Mon Sep 17 00:00:00 2001 From: Dion Dokter Date: Tue, 29 Apr 2025 16:59:04 +0200 Subject: [PATCH 24/97] Add f7 support --- embassy-stm32/src/flash/f7.rs | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/embassy-stm32/src/flash/f7.rs b/embassy-stm32/src/flash/f7.rs index 09ebe9db9..043382590 100644 --- a/embassy-stm32/src/flash/f7.rs +++ b/embassy-stm32/src/flash/f7.rs @@ -169,3 +169,13 @@ mod tests { assert_sector(7, 0x080C_0000, LARGE_SECTOR_SIZE, 0x080F_FFFF); } } + +#[cfg(all(bank_setup_configurable))] +pub(crate) fn check_bank_setup() { + if cfg!(feature = "single-bank") && !pac::FLASH.optcr().read().n_dbank() { + panic!("Embassy is configured as single-bank, but the hardware is running in dual-bank mode. Change the hardware by changing the ndbank value in the user option bytes or configure embassy to use dual-bank config"); + } + if cfg!(feature = "dual-bank") && pac::FLASH.optcr().read().n_dbank() { + panic!("Embassy is configured as dual-bank, but the hardware is running in single-bank mode. Change the hardware by changing the ndbank value in the user option bytes or configure embassy to use single-bank config"); + } +} From 34a4dddfe79e55057c2ce87ee60b1c6b93c5ac76 Mon Sep 17 00:00:00 2001 From: Dion Dokter Date: Tue, 29 Apr 2025 17:05:07 +0200 Subject: [PATCH 25/97] Add L4+ support --- embassy-stm32/src/flash/l.rs | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/embassy-stm32/src/flash/l.rs b/embassy-stm32/src/flash/l.rs index bc6f8c873..3b62fa2ee 100644 --- a/embassy-stm32/src/flash/l.rs +++ b/embassy-stm32/src/flash/l.rs @@ -248,3 +248,13 @@ pub(crate) fn check_bank_setup() { panic!("Embassy is configured as dual-bank, but the hardware is running in single-bank mode. Change the hardware by changing the dbank value in the user option bytes or configure embassy to use single-bank config"); } } + +#[cfg(all(bank_setup_configurable, flash_l4))] +pub(crate) fn check_bank_setup() { + if cfg!(feature = "single-bank") && pac::FLASH.optr().read().dualbank() { + panic!("Embassy is configured as single-bank, but the hardware is running in dual-bank mode. Change the hardware by changing the dualbank value in the user option bytes or configure embassy to use dual-bank config"); + } + if cfg!(feature = "dual-bank") && !pac::FLASH.optr().read().dualbank() { + panic!("Embassy is configured as dual-bank, but the hardware is running in single-bank mode. Change the hardware by changing the dualbank value in the user option bytes or configure embassy to use single-bank config"); + } +} From c84d8e36323ccc7b12782ae2f7644e49b1074978 Mon Sep 17 00:00:00 2001 From: Dion Dokter Date: Tue, 29 Apr 2025 17:07:02 +0200 Subject: [PATCH 26/97] Update changelog --- embassy-stm32/CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/embassy-stm32/CHANGELOG.md b/embassy-stm32/CHANGELOG.md index 7b7d559e2..b6781905e 100644 --- a/embassy-stm32/CHANGELOG.md +++ b/embassy-stm32/CHANGELOG.md @@ -7,7 +7,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## Unreleased - Modify BufferedUart initialization to take pins before interrupts ([#3983](https://github.com/embassy-rs/embassy/pull/3983)) -- Added a 'single-bank' and a 'dual-bank' feature so chips with configurable flash bank setups can be supported in embassy. The G4 series now supports this. ([#4125](https://github.com/embassy-rs/embassy/pull/4125)) +- Added a 'single-bank' and a 'dual-bank' feature so chips with configurable flash bank setups are be supported in embassy ([#4125](https://github.com/embassy-rs/embassy/pull/4125)) ## 0.2.0 - 2025-01-10 From fb7504c2930a3f177a33f1dcc736da0d548d6e97 Mon Sep 17 00:00:00 2001 From: Alex Moon Date: Tue, 29 Apr 2025 11:07:58 -0400 Subject: [PATCH 27/97] Add docs --- embassy-nrf/src/twim.rs | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/embassy-nrf/src/twim.rs b/embassy-nrf/src/twim.rs index 95b24c616..3d5e841d1 100644 --- a/embassy-nrf/src/twim.rs +++ b/embassy-nrf/src/twim.rs @@ -119,6 +119,12 @@ pub struct Twim<'d, T: Instance> { impl<'d, T: Instance> Twim<'d, T> { /// Create a new TWI driver. + /// + /// `tx_ram_buffer` is required if any write operations will be performed with data that is not in RAM. + /// Usually this is static data that the compiler locates in flash instead of RAM. The `tx_ram_buffer` + /// needs to be at least as large as the largest write operation that will be executed with a buffer + /// that is not in RAM. If all write operations will be performed from RAM, an empty buffer (`&[]`) may + /// be used. pub fn new( twim: Peri<'d, T>, _irq: impl interrupt::typelevel::Binding> + 'd, From ae59d0acf4f7c7be028b1246aaa8033015985154 Mon Sep 17 00:00:00 2001 From: Dion Dokter Date: Tue, 29 Apr 2025 17:13:43 +0200 Subject: [PATCH 28/97] Use generated metapac instead of local one --- embassy-stm32/Cargo.toml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/embassy-stm32/Cargo.toml b/embassy-stm32/Cargo.toml index afef5d72f..5ed5c3519 100644 --- a/embassy-stm32/Cargo.toml +++ b/embassy-stm32/Cargo.toml @@ -74,7 +74,7 @@ sdio-host = "0.9.0" critical-section = "1.1" #stm32-metapac = { version = "16" } # stm32-metapac = { git = "https://github.com/embassy-rs/stm32-data-generated", tag = "stm32-data-380f03cb71f43a242adc45e83607a380ffe0447b" } -stm32-metapac = { path = "R:/stm32-data/build/stm32-metapac" } +stm32-metapac = { git="https://ci.embassy.dev/jobs/039aa6808ef1/artifacts/generated.git" } vcell = "0.1.3" nb = "1.0.0" @@ -104,7 +104,7 @@ quote = "1.0.15" #stm32-metapac = { version = "16", default-features = false, features = ["metadata"]} # stm32-metapac = { git = "https://github.com/embassy-rs/stm32-data-generated", tag = "stm32-data-380f03cb71f43a242adc45e83607a380ffe0447b", default-features = false, features = ["metadata"] } -stm32-metapac = { path = "R:/stm32-data/build/stm32-metapac", default-features = false, features = ["metadata"] } +stm32-metapac = { git="https://ci.embassy.dev/jobs/039aa6808ef1/artifacts/generated.git", default-features = false, features = ["metadata"] } [features] default = ["rt"] From 77e8bc9b28d6988b2703029679f290b351fc54a0 Mon Sep 17 00:00:00 2001 From: 1-rafael-1 Date: Tue, 29 Apr 2025 22:49:05 +0200 Subject: [PATCH 29/97] refactoring to have higher and lower level api --- embassy-rp/sdk examples | 1 + embassy-rp/src/clocks.rs | 546 +++++++++++++++++++------------ examples/rp/src/bin/overclock.rs | 13 +- 3 files changed, 352 insertions(+), 208 deletions(-) create mode 160000 embassy-rp/sdk examples diff --git a/embassy-rp/sdk examples b/embassy-rp/sdk examples new file mode 160000 index 000000000..ee68c78d0 --- /dev/null +++ b/embassy-rp/sdk examples @@ -0,0 +1 @@ +Subproject commit ee68c78d0afae2b69c03ae1a72bf5cc267a2d94c diff --git a/embassy-rp/src/clocks.rs b/embassy-rp/src/clocks.rs index b74e90f5e..1f5c27df1 100644 --- a/embassy-rp/src/clocks.rs +++ b/embassy-rp/src/clocks.rs @@ -1,4 +1,78 @@ -//! Clock configuration for the RP2040 +//! # Clock configuration for the RP2040 and RP235x microcontrollers. +//! +//! # Clock Configuration API +//! +//! This module provides both high-level convenience functions and low-level manual +//! configuration options for the RP2040 clock system. +//! +//! ## High-Level Convenience Functions +//! +//! For most users, these functions provide an easy way to configure clocks: +//! +//! - `ClockConfig::crystal(12_000_000)` - Default configuration with 12MHz crystal giving 125MHz system clock +//! - `ClockConfig::at_sys_frequency_mhz(200)` - Set system clock to a specific frequency with automatic voltage scaling +//! - `ClockConfig::with_external_crystal(16_000_000)` - Configure with a non-standard crystal frequency +//! +//! ## Manual Configuration +//! +//! For advanced users who need precise control: +//! +//! ```rust,ignore +//! // Start with default configuration and customize it +//! let mut config = ClockConfig::default(); +//! +//! // Set custom PLL parameters +//! config.xosc = Some(XoscConfig { +//! hz: 12_000_000, +//! sys_pll: Some(PllConfig { +//! refdiv: 1, +//! fbdiv: 200, +//! post_div1: 6, +//! post_div2: 2, +//! }), +//! // ... other fields +//! }); +//! +//! // Set voltage for overclocking +//! config.voltage_scale = Some(VoltageScale::V1_15); +//! ``` +//! +//! ## Voltage Scaling for Overclocking (RP2040 only) +//! +//! When overclocking beyond 133MHz, higher core voltages are needed: +//! +//! - Up to 133MHz: `VoltageScale::V1_10` (default) +//! - 133-200MHz: `VoltageScale::V1_15` +//! - Above 200MHz: `VoltageScale::V1_20` or higher +//! +//! The `at_sys_frequency_mhz()` function automatically sets appropriate voltages. +//! +//! ## Examples +//! +//! ### Standard 125MHz configuration +//! ```rust,ignore +//! let config = ClockConfig::crystal(12_000_000); +//! ``` +//! +//! Or using the default configuration: +//! ```rust,ignore +//! let config = ClockConfig::default(); +//! ``` +//! +//! ### Overclock to 200MHz +//! ```rust,ignore +//! let config = ClockConfig::at_sys_frequency_mhz(200); +//! ``` +//! +//! ### Manual configuration for advanced scenarios +//! ```rust,ignore +//! use embassy_rp::clocks::{ClockConfig, XoscConfig, PllConfig, VoltageScale}; +//! +//! // Start with defaults and customize +//! let mut config = ClockConfig::default(); +//! config.voltage_scale = Some(VoltageScale::V1_15); +//! // Set other parameters as needed... +//! ``` #[cfg(feature = "rp2040")] use core::arch::asm; @@ -18,6 +92,7 @@ use crate::{pac, reset, Peri}; // gpin is not usually safe to use during the boot init() call, so it won't // be very useful until we have runtime clock reconfiguration. once this // happens we can resurrect the commented-out gpin bits. + struct Clocks { xosc: AtomicU32, sys: AtomicU32, @@ -70,30 +145,58 @@ pub enum PeriClkSrc { } /// Core voltage scaling options for RP2040. -/// See RP2040 Datasheet, Table 189, VREG Register. +/// +/// The RP2040 voltage regulator can be configured for different output voltages. +/// Higher voltages allow for higher clock frequencies but increase power consumption. +/// +/// # Typical Use Cases +/// +/// - `V0_85` to `V1_05`: Power saving for lower frequencies (below 100MHz) +/// - `V1_10`: Default voltage, safe for standard 125MHz operation +/// - `V1_15`: Required for frequencies above 133MHz (e.g., 200MHz overclocking) +/// - `V1_20`: For more extreme overclocking (200MHz+) +/// - `V1_25` and `V1_30`: Highest voltage settings, use with caution +/// +/// # Overclocking Notes +/// +/// When overclocking: +/// - Frequencies up to 133MHz are typically stable at default voltage (`V1_10`) +/// - Frequencies from 133MHz to 200MHz generally require `V1_15` +/// - Frequencies above 200MHz typically require `V1_20` or higher +/// +/// # Power Consumption +/// +/// Higher voltages increase power consumption and heat generation. In battery-powered +/// applications, consider using lower voltages when maximum performance is not required. +/// +/// # Safety +/// +/// Increased voltage can reduce the lifespan of the chip if used for extended periods, +/// especially at `V1_25` and `V1_30`. These higher voltages should be used with +/// consideration of thermal management. #[cfg(feature = "rp2040")] #[derive(Clone, Copy, Debug, PartialEq, Eq)] #[repr(u8)] pub enum VoltageScale { - /// 0.85V + /// 0.85V - Lowest power consumption, suitable for low frequencies V0_85 = 0b0110, - /// 0.90V + /// 0.90V - Low power consumption V0_90 = 0b0111, - /// 0.95V + /// 0.95V - Low power consumption V0_95 = 0b1000, - /// 1.00V + /// 1.00V - Medium power consumption V1_00 = 0b1001, - /// 1.05V + /// 1.05V - Medium power consumption V1_05 = 0b1010, - /// 1.10V (Default) + /// 1.10V (Default) - Standard voltage for 125MHz operation V1_10 = 0b1011, - /// 1.15V + /// 1.15V - Required for frequencies above 133MHz V1_15 = 0b1100, - /// 1.20V + /// 1.20V - For higher overclocking (200MHz+) V1_20 = 0b1101, - /// 1.25V + /// 1.25V - High voltage, use with caution V1_25 = 0b1110, - /// 1.30V + /// 1.30V - Maximum voltage, use with extreme caution V1_30 = 0b1111, } @@ -143,14 +246,57 @@ pub struct ClockConfig { #[cfg(feature = "rp2040")] pub voltage_scale: Option, /// Voltage stabilization delay in microseconds. + /// If not set, appropriate defaults will be used based on voltage level. #[cfg(feature = "rp2040")] pub voltage_stabilization_delay_us: Option, // gpin0: Option<(u32, Gpin<'static, AnyPin>)>, // gpin1: Option<(u32, Gpin<'static, AnyPin>)>, } +impl Default for ClockConfig { + /// Creates a minimal default configuration with safe values. + /// + /// This configuration uses the ring oscillator (ROSC) as the clock source + /// and sets minimal defaults that guarantee a working system. It's intended + /// as a starting point for manual configuration. + /// + /// Most users should use one of the more specific configuration functions: + /// - `ClockConfig::crystal()` - Standard configuration with external crystal + /// - `ClockConfig::rosc()` - Configuration using only the internal oscillator + /// - `ClockConfig::at_sys_frequency_mhz()` - Configuration for a specific system frequency + fn default() -> Self { + Self { + rosc: None, + xosc: None, + ref_clk: RefClkConfig { + src: RefClkSrc::Rosc, + div: 1, + }, + sys_clk: SysClkConfig { + src: SysClkSrc::Rosc, + div_int: 1, + div_frac: 0, + }, + peri_clk_src: None, + usb_clk: None, + adc_clk: None, + #[cfg(feature = "rp2040")] + rtc_clk: None, + #[cfg(feature = "rp2040")] + voltage_scale: None, + #[cfg(feature = "rp2040")] + voltage_stabilization_delay_us: None, + // gpin0: None, + // gpin1: None, + } + } +} + impl ClockConfig { /// Clock configuration derived from external crystal. + /// + /// This uses default settings for most parameters, suitable for typical use cases. + /// For manual control of PLL parameters, use `new_manual()` or modify the struct fields directly. pub fn crystal(crystal_hz: u32) -> Self { Self { rosc: Some(RoscConfig { @@ -217,101 +363,6 @@ impl ClockConfig { } } - /// Clock configuration derived from external crystal, targeting a specific SYS clock frequency for RP2040. - /// - /// This function calculates the required PLL settings and core voltage based on the target frequency. - /// - /// # Arguments - /// - /// * `crystal_hz`: The frequency of the external crystal (e.g., 12_000_000 for 12MHz). - /// * `sys_freq_hz`: The target system clock frequency. - /// - /// # Panics - /// - /// Panics if the requested frequency is impossible to achieve with the given crystal, - /// or if the required voltage for the frequency is not supported or known. - /// - /// # Safety Notes (RP2040) - /// - /// * Frequencies > 133MHz require increased core voltage. - /// * This function automatically selects `VoltageScale::V1_15` for frequencies > 133MHz and <= 200MHz. - /// * Frequencies > 200MHz might require `VoltageScale::V1_20` or higher and are considered overclocking beyond datasheet recommendations. - /// This function will select `VoltageScale::V1_20` for frequencies > 200MHz, use with caution. - /// * Ensure your hardware supports the selected voltage and frequency. - #[cfg(feature = "rp2040")] - pub fn crystal_freq(crystal_hz: u32, sys_freq_hz: u32) -> Self { - // Determine required voltage based on target frequency - let voltage_scale = match sys_freq_hz { - 0..=133_000_000 => VoltageScale::V1_10, // Default voltage is sufficient - 133_000_001..=200_000_000 => VoltageScale::V1_15, // Requires 1.15V - _ => VoltageScale::V1_20, // Frequencies > 200MHz require at least 1.20V (Overclocking) - }; - - // Find suitable PLL parameters - let pll_params = match find_pll_params(crystal_hz, sys_freq_hz) { - Some(params) => params, - None => { - // If we can't find valid parameters for the requested frequency, - // fall back to safe defaults (125 MHz for RP2040) - let safe_freq = 125_000_000; // Safe default frequency - find_pll_params(crystal_hz, safe_freq) - .expect("Could not find valid PLL parameters even for safe default frequency") - } - }; - - Self { - rosc: Some(RoscConfig { - hz: 6_500_000, - range: RoscRange::Medium, - drive_strength: [0; 8], - div: 16, - }), - xosc: Some(XoscConfig { - hz: crystal_hz, - sys_pll: Some(pll_params), - // Keep USB PLL at 48MHz for compatibility - usb_pll: Some(PllConfig { - refdiv: 1, - fbdiv: 120, - post_div1: 6, - post_div2: 5, - }), - delay_multiplier: 128, - }), - ref_clk: RefClkConfig { - src: RefClkSrc::Xosc, - div: 1, - }, - sys_clk: SysClkConfig { - src: SysClkSrc::PllSys, - div_int: 1, - div_frac: 0, - }, - peri_clk_src: Some(PeriClkSrc::Sys), - usb_clk: Some(UsbClkConfig { - src: UsbClkSrc::PllUsb, - div: 1, - phase: 0, - }), - adc_clk: Some(AdcClkConfig { - src: AdcClkSrc::PllUsb, - div: 1, - phase: 0, - }), - rtc_clk: Some(RtcClkConfig { - src: RtcClkSrc::PllUsb, - div_int: 1024, - div_frac: 0, - phase: 0, - }), - voltage_scale: Some(voltage_scale), - #[cfg(feature = "rp2040")] - voltage_stabilization_delay_us: None, - // gpin0: None, - // gpin1: None, - } - } - /// Clock configuration from internal oscillator. pub fn rosc() -> Self { Self { @@ -366,22 +417,17 @@ impl ClockConfig { /// /// * `sys_freq_mhz` - The target system clock frequency in MHz /// - /// # Safety Notes - /// - /// * Frequencies > 133MHz require increased core voltage and are considered overclocking. - /// * Frequencies > 250MHz are extreme overclocking and may not be stable on all chips. - /// /// # Example /// /// ``` /// // Configure for standard 125MHz clock - /// let config = ClockConfig::with_speed_mhz(125); + /// let config = ClockConfig::at_sys_frequency_mhz(125); /// - /// // Overclock to 200MHz (requires higher voltage) - /// let config = ClockConfig::with_speed_mhz(200); + /// // Overclock to 200MHz + /// let config = ClockConfig::at_sys_frequency_mhz(200); /// ``` #[cfg(feature = "rp2040")] - pub fn with_speed_mhz(sys_freq_mhz: u32) -> Self { + pub fn at_sys_frequency_mhz(sys_freq_mhz: u32) -> Self { // For 125MHz, use exactly the same config as the default to avoid any differences if sys_freq_mhz == 125 { return Self::crystal(12_000_000); @@ -392,12 +438,7 @@ impl ClockConfig { const DEFAULT_CRYSTAL_HZ: u32 = 12_000_000; let sys_freq_hz = sys_freq_mhz * 1_000_000; - let mut config = Self::crystal_freq(DEFAULT_CRYSTAL_HZ, sys_freq_hz); - - // For frequencies above 200MHz, ensure we're using the highest voltage - if sys_freq_mhz > 200 { - config.voltage_scale = Some(VoltageScale::V1_20); - } + let config = Self::crystal_freq(DEFAULT_CRYSTAL_HZ, sys_freq_hz); config } @@ -412,11 +453,6 @@ impl ClockConfig { /// * `crystal_hz` - The frequency of the external crystal in Hz /// * `sys_freq_hz` - The target system clock frequency in Hz /// - /// # Safety Notes - /// - /// * Frequencies > 133MHz require increased core voltage and are considered overclocking. - /// * Frequencies > 250MHz are extreme overclocking and may not be stable on all chips. - /// /// # Example /// /// ``` @@ -428,58 +464,93 @@ impl ClockConfig { Self::crystal_freq(crystal_hz, sys_freq_hz) } - #[cfg(feature = "rp2040")] - pub fn with_speed_mhz_test_voltage(sys_freq_mhz: u32, voltage: Option) -> Self { - // Create a config with the requested frequency - let sys_freq_hz = sys_freq_mhz * 1_000_000; - let mut config = Self::crystal_freq(12_000_000, sys_freq_hz); - - // Override the voltage setting - config.voltage_scale = voltage; - - // For debugging - // println!("Debug: Setting freq to {}MHz with voltage {:?}", sys_freq_mhz, voltage); - - config - } - - /// Similar to `with_speed_mhz_test_voltage` but with an extended voltage stabilization delay. + /// Configure clocks derived from an external crystal with specific system frequency. /// - /// This function is useful for testing voltage stability issues where the default delay - /// may not be sufficient for the voltage regulator to fully stabilize. + /// This function calculates optimal PLL parameters to achieve the requested system + /// frequency from the given crystal frequency. It's used internally by higher-level + /// configuration functions. /// /// # Arguments /// - /// * `sys_freq_mhz` - The target system clock frequency in MHz - /// * `voltage` - The desired voltage scale setting - /// * `stabilization_delay_us` - Voltage stabilization delay in microseconds (default: 500μs) + /// * `crystal_hz` - The frequency of the external crystal in Hz + /// * `sys_freq_hz` - The desired system clock frequency in Hz + /// + /// # Returns + /// + /// A ClockConfig configured to achieve the requested system frequency using the + /// specified crystal, or panic if no valid parameters can be found. #[cfg(feature = "rp2040")] - pub fn with_speed_mhz_test_voltage_extended_delay( - sys_freq_mhz: u32, - voltage: Option, - stabilization_delay_us: Option, - ) -> Self { - // Create a config with the requested frequency - let sys_freq_hz = sys_freq_mhz * 1_000_000; - let mut config = Self::crystal_freq(12_000_000, sys_freq_hz); + fn crystal_freq(crystal_hz: u32, sys_freq_hz: u32) -> Self { + // Find optimal PLL parameters for the requested frequency + let sys_pll_params = find_pll_params(crystal_hz, sys_freq_hz) + .unwrap_or_else(|| panic!("Could not find valid PLL parameters for system clock")); - // Override the voltage setting - config.voltage_scale = voltage; + // Set the voltage scale based on the target frequency + // Higher frequencies require higher voltage + let voltage_scale = match sys_freq_hz { + freq if freq > 200_000_000 => Some(VoltageScale::V1_20), + freq if freq > 133_000_000 => Some(VoltageScale::V1_15), + _ => None, // Use default voltage (V1_10) + }; - // Add a custom voltage stabilization delay - config.voltage_stabilization_delay_us = stabilization_delay_us; + // For USB PLL, we always want 48MHz for USB + let usb_pll_params = if crystal_hz == 12_000_000 { + // For standard 12MHz crystal, use the default parameters + PllConfig { + refdiv: 1, + fbdiv: 120, + post_div1: 6, + post_div2: 5, + } + } else { + // For other crystals, calculate parameters to get 48MHz + find_pll_params(crystal_hz, 48_000_000) + .unwrap_or_else(|| panic!("Could not find valid PLL parameters for USB clock")) + }; - config + Self { + rosc: Some(RoscConfig { + hz: 6_500_000, + range: RoscRange::Medium, + drive_strength: [0; 8], + div: 16, + }), + xosc: Some(XoscConfig { + hz: crystal_hz, + sys_pll: Some(sys_pll_params), + usb_pll: Some(usb_pll_params), + delay_multiplier: 128, + }), + ref_clk: RefClkConfig { + src: RefClkSrc::Xosc, + div: 1, + }, + sys_clk: SysClkConfig { + src: SysClkSrc::PllSys, + div_int: 1, + div_frac: 0, + }, + peri_clk_src: Some(PeriClkSrc::Sys), + usb_clk: Some(UsbClkConfig { + src: UsbClkSrc::PllUsb, + div: 1, + phase: 0, + }), + adc_clk: Some(AdcClkConfig { + src: AdcClkSrc::PllUsb, + div: 1, + phase: 0, + }), + rtc_clk: Some(RtcClkConfig { + src: RtcClkSrc::PllUsb, + div_int: 1024, + div_frac: 0, + phase: 0, + }), + voltage_scale, + voltage_stabilization_delay_us: None, + } } - // pub fn bind_gpin(&mut self, gpin: Gpin<'static, P>, hz: u32) { - // match P::NR { - // 0 => self.gpin0 = Some((hz, gpin.into())), - // 1 => self.gpin1 = Some((hz, gpin.into())), - // _ => unreachable!(), - // } - // // pin is now provisionally bound. if the config is applied it must be forgotten, - // // or Gpin::drop will deconfigure the clock input. - // } } /// ROSC freq range. @@ -525,6 +596,30 @@ pub struct XoscConfig { } /// PLL configuration. +/// +/// This struct defines the parameters used to configure the Phase-Locked Loop (PLL) +/// in the RP2040. The parameters follow the definitions from the RP2040 datasheet, +/// section 2.18.3. +/// +/// # Parameters +/// +/// * `refdiv` - Reference divider (1-63) +/// * `fbdiv` - VCO feedback divider (16-320) +/// * `post_div1` - First post divider (1-7) +/// * `post_div2` - Second post divider (1-7) - must be less than or equal to post_div1 +/// +/// # Constraints +/// +/// * VCO frequency (input_hz / refdiv * fbdiv) must be between 750MHz and 1800MHz +/// * post_div2 must be less than or equal to post_div1 +/// +/// # Calculation +/// +/// The output frequency of the PLL is calculated as: +/// +/// `output_hz = (input_hz / refdiv * fbdiv) / (post_div1 * post_div2)` +/// +/// Where input_hz is typically the crystal frequency (e.g., 12MHz). #[derive(Clone, Copy, Debug)] pub struct PllConfig { /// Reference divisor. @@ -537,6 +632,50 @@ pub struct PllConfig { pub post_div2: u8, } +impl PllConfig { + /// Calculate the output frequency for this PLL configuration + /// given an input frequency. + pub fn output_frequency(&self, input_hz: u32) -> u32 { + let ref_freq = input_hz / self.refdiv as u32; + let vco_freq = ref_freq * self.fbdiv as u32; + vco_freq / ((self.post_div1 * self.post_div2) as u32) + } + + /// Check if this PLL configuration is valid for the given input frequency. + pub fn is_valid(&self, input_hz: u32) -> bool { + // Check divisor constraints + if self.refdiv < 1 || self.refdiv > 63 { + return false; + } + if self.fbdiv < 16 || self.fbdiv > 320 { + return false; + } + if self.post_div1 < 1 || self.post_div1 > 7 { + return false; + } + if self.post_div2 < 1 || self.post_div2 > 7 { + return false; + } + if self.post_div2 > self.post_div1 { + return false; + } + + // Calculate reference frequency + let ref_freq = input_hz / self.refdiv as u32; + + // Check reference frequency range + if ref_freq < 5_000_000 || ref_freq > 800_000_000 { + return false; + } + + // Calculate VCO frequency + let vco_freq = ref_freq * self.fbdiv as u32; + + // Check VCO frequency range + vco_freq >= 750_000_000 && vco_freq <= 1_800_000_000 + } +} + /// Reference clock config. pub struct RefClkConfig { /// Reference clock source. @@ -683,6 +822,35 @@ pub struct RtcClkConfig { /// Find valid PLL parameters (refdiv, fbdiv, post_div1, post_div2) for a target output frequency /// based on the input frequency. /// +/// This function searches for the best PLL configuration to achieve the requested target frequency +/// while staying within the VCO frequency range of 750MHz to 1800MHz. It prioritizes stability +/// over exact frequency matching by using larger divisors where possible. +/// +/// # Parameters +/// +/// * `input_hz`: The input frequency in Hz (typically the crystal frequency, e.g. 12MHz) +/// * `target_hz`: The desired output frequency in Hz (e.g. 125MHz for standard RP2040 operation) +/// +/// # Returns +/// +/// * `Some(PllConfig)` if valid parameters were found +/// * `None` if no valid parameters could be found for the requested combination +/// +/// # Algorithm +/// +/// 1. Set reference divider to 1 (fixed for simplicity) +/// 2. Try different feedback divisors (fbdiv) starting from highest to lowest +/// 3. For each fbdiv value, check if the resulting VCO frequency is valid (750-1800MHz) +/// 4. Find post-divider combinations that give the exact requested frequency +/// 5. If no exact match, return the closest approximation +/// +/// # Example +/// +/// ``` +/// // Find parameters for 133MHz system clock from 12MHz crystal +/// let pll_params = find_pll_params(12_000_000, 133_000_000).unwrap(); +/// ``` +/// /// Similar to the Pico SDK's parameter selection approach, prioritizing stability. /// See RP2040 Datasheet section 2.16.3. Reference Clock (ref) and 2.18.3. PLL #[cfg(feature = "rp2040")] @@ -766,43 +934,6 @@ fn find_pll_params(input_hz: u32, target_hz: u32) -> Option { best_config } -#[cfg(feature = "rp2040")] -pub fn compare_pll_params() { - // Parameters from default configuration - let default_params = PllConfig { - refdiv: 1, - fbdiv: 125, - post_div1: 6, - post_div2: 2, - }; - - // Calculate parameters using our find_pll_params function - let crystal_hz = 12_000_000; - let target_hz = 125_000_000; - let calculated_params = find_pll_params(crystal_hz, target_hz).expect("Failed to find PLL parameters"); - - // Check if they're identical - let params_match = default_params.refdiv == calculated_params.refdiv - && default_params.fbdiv == calculated_params.fbdiv - && default_params.post_div1 == calculated_params.post_div1 - && default_params.post_div2 == calculated_params.post_div2; - - // Here we'd normally print results, but without a console we'll just - // use this for debugging in our IDE - let _default_output_freq = crystal_hz / default_params.refdiv as u32 * default_params.fbdiv as u32 - / (default_params.post_div1 * default_params.post_div2) as u32; - - let _calculated_output_freq = crystal_hz / calculated_params.refdiv as u32 * calculated_params.fbdiv as u32 - / (calculated_params.post_div1 * calculated_params.post_div2) as u32; - - // Parameters: default vs calculated - // refdiv: 1 vs {calculated_params.refdiv} - // fbdiv: 125 vs {calculated_params.fbdiv} - // post_div1: 6 vs {calculated_params.post_div1} - // post_div2: 2 vs {calculated_params.post_div2} - // params_match: {params_match} -} - /// safety: must be called exactly once at bootup pub(crate) unsafe fn init(config: ClockConfig) { // Reset everything except: @@ -1603,6 +1734,7 @@ impl rand_core::RngCore for RoscRng { dest.fill_with(Self::next_u8) } } + /// Enter the `DORMANT` sleep state. This will stop *all* internal clocks /// and can only be exited through resets, dormant-wake GPIO interrupts, /// and RTC interrupts. If RTC is clocked from an internal clock source diff --git a/examples/rp/src/bin/overclock.rs b/examples/rp/src/bin/overclock.rs index db6c8f448..9027f1516 100644 --- a/examples/rp/src/bin/overclock.rs +++ b/examples/rp/src/bin/overclock.rs @@ -14,7 +14,18 @@ const COUNT_TO: i64 = 10_000_000; #[embassy_executor::main] async fn main(_spawner: Spawner) -> ! { // Set up for clock frequency of 200 MHz - let config = Config::new(ClockConfig::with_speed_mhz(200)); + // This will set all the necessary defaults including slightly raised voltage + // See embassy_rp::clocks::ClockConfig for more options, including full manual control + let config = Config::new(ClockConfig::at_sys_frequency_mhz(200)); + + // Show the voltage scale and brownout-detection for verification + info!("System core voltage: {}", Debug2Format(&config.clocks.voltage_scale)); + // info!( + // "Brownout detection: {}", + // Debug2Format(&config.clocks.brownout_detection) + // ); + + // Initialize the peripherals let p = embassy_rp::init(config); // Show CPU frequency for verification From d44b94523576d3f8c1f586811f600eb3ba223606 Mon Sep 17 00:00:00 2001 From: 1-rafael-1 Date: Tue, 29 Apr 2025 22:49:53 +0200 Subject: [PATCH 30/97] Remove unused embassy-rp/sdk examples subproject --- embassy-rp/sdk examples | 1 - 1 file changed, 1 deletion(-) delete mode 160000 embassy-rp/sdk examples diff --git a/embassy-rp/sdk examples b/embassy-rp/sdk examples deleted file mode 160000 index ee68c78d0..000000000 --- a/embassy-rp/sdk examples +++ /dev/null @@ -1 +0,0 @@ -Subproject commit ee68c78d0afae2b69c03ae1a72bf5cc267a2d94c From 34b6a518f87a528856f1891d32ac1a48d6239b18 Mon Sep 17 00:00:00 2001 From: Curly Date: Wed, 30 Apr 2025 07:50:13 -0700 Subject: [PATCH 31/97] add 133MHz tick rate to support PR2040 @ 133MHz when `TIMERx`'s `SOURCE` is set to `SYSCLK` --- embassy-time-driver/Cargo.toml | 2 ++ embassy-time-driver/gen_tick.py | 1 + embassy-time-driver/src/tick.rs | 3 +++ embassy-time/Cargo.toml | 2 ++ 4 files changed, 8 insertions(+) diff --git a/embassy-time-driver/Cargo.toml b/embassy-time-driver/Cargo.toml index b709cafc1..16213cb75 100644 --- a/embassy-time-driver/Cargo.toml +++ b/embassy-time-driver/Cargo.toml @@ -226,6 +226,8 @@ tick-hz-128_000_000 = [] tick-hz-130_000_000 = [] ## 131.072MHz Tick Rate tick-hz-131_072_000 = [] +## 133.0MHz Tick Rate +tick-hz-133_000_000 = [] ## 140.0MHz Tick Rate tick-hz-140_000_000 = [] ## 144.0MHz Tick Rate diff --git a/embassy-time-driver/gen_tick.py b/embassy-time-driver/gen_tick.py index af194c31f..080434457 100644 --- a/embassy-time-driver/gen_tick.py +++ b/embassy-time-driver/gen_tick.py @@ -22,6 +22,7 @@ for i in range(1, 30): ticks.append(10 * i * 1_000_000) for i in range(15, 50): ticks.append(20 * i * 1_000_000) +ticks.append(133 * 1_000_000) seen = set() ticks = sorted([x for x in ticks if not (x in seen or seen.add(x))]) diff --git a/embassy-time-driver/src/tick.rs b/embassy-time-driver/src/tick.rs index 916ae9498..5059e1628 100644 --- a/embassy-time-driver/src/tick.rs +++ b/embassy-time-driver/src/tick.rs @@ -182,6 +182,8 @@ pub const TICK_HZ: u64 = 128_000_000; pub const TICK_HZ: u64 = 130_000_000; #[cfg(feature = "tick-hz-131_072_000")] pub const TICK_HZ: u64 = 131_072_000; +#[cfg(feature = "tick-hz-133_000_000")] +pub const TICK_HZ: u64 = 133_000_000; #[cfg(feature = "tick-hz-140_000_000")] pub const TICK_HZ: u64 = 140_000_000; #[cfg(feature = "tick-hz-144_000_000")] @@ -410,6 +412,7 @@ pub const TICK_HZ: u64 = 5_242_880_000; feature = "tick-hz-128_000_000", feature = "tick-hz-130_000_000", feature = "tick-hz-131_072_000", + feature = "tick-hz-133_000_000", feature = "tick-hz-140_000_000", feature = "tick-hz-144_000_000", feature = "tick-hz-150_000_000", diff --git a/embassy-time/Cargo.toml b/embassy-time/Cargo.toml index 80a39dbf5..dc144ec3c 100644 --- a/embassy-time/Cargo.toml +++ b/embassy-time/Cargo.toml @@ -274,6 +274,8 @@ tick-hz-128_000_000 = ["embassy-time-driver/tick-hz-128_000_000"] tick-hz-130_000_000 = ["embassy-time-driver/tick-hz-130_000_000"] ## 131.072MHz Tick Rate tick-hz-131_072_000 = ["embassy-time-driver/tick-hz-131_072_000"] +## 133.0MHz Tick Rate +tick-hz-133_000_000 = ["embassy-time-driver/tick-hz-133_000_000"] ## 140.0MHz Tick Rate tick-hz-140_000_000 = ["embassy-time-driver/tick-hz-140_000_000"] ## 144.0MHz Tick Rate From 0591d60a7931255fd1161608c69c4935d6a16676 Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Wed, 30 Apr 2025 18:00:17 +0200 Subject: [PATCH 32/97] stm32/otg: calculate TRDT using AHB freq instead of kernel freq. --- embassy-stm32/build.rs | 5 +++++ embassy-stm32/src/rcc/mod.rs | 2 ++ embassy-stm32/src/usb/otg.rs | 2 +- 3 files changed, 8 insertions(+), 1 deletion(-) diff --git a/embassy-stm32/build.rs b/embassy-stm32/build.rs index f9f03c51b..3fefa9717 100644 --- a/embassy-stm32/build.rs +++ b/embassy-stm32/build.rs @@ -647,6 +647,8 @@ fn main() { PeripheralRccKernelClock::Clock(clock) => clock_gen.gen_clock(p.name, clock), }; + let bus_clock_frequency = clock_gen.gen_clock(p.name, &rcc.bus_clock); + // A refcount leak can result if the same field is shared by peripherals with different stop modes // This condition should be checked in stm32-data let stop_mode = match rcc.stop_mode { @@ -660,6 +662,9 @@ fn main() { fn frequency() -> crate::time::Hertz { #clock_frequency } + fn bus_frequency() -> crate::time::Hertz { + #bus_clock_frequency + } const RCC_INFO: crate::rcc::RccInfo = unsafe { crate::rcc::RccInfo::new( diff --git a/embassy-stm32/src/rcc/mod.rs b/embassy-stm32/src/rcc/mod.rs index 4f43d3748..150daa4a7 100644 --- a/embassy-stm32/src/rcc/mod.rs +++ b/embassy-stm32/src/rcc/mod.rs @@ -97,6 +97,8 @@ pub(crate) unsafe fn get_freqs() -> &'static Clocks { pub(crate) trait SealedRccPeripheral { fn frequency() -> Hertz; + #[allow(dead_code)] + fn bus_frequency() -> Hertz; const RCC_INFO: RccInfo; } diff --git a/embassy-stm32/src/usb/otg.rs b/embassy-stm32/src/usb/otg.rs index 2aca3d929..590d1a427 100644 --- a/embassy-stm32/src/usb/otg.rs +++ b/embassy-stm32/src/usb/otg.rs @@ -549,7 +549,7 @@ foreach_interrupt!( ); fn calculate_trdt(speed: Dspd) -> u8 { - let ahb_freq = T::frequency().0; + let ahb_freq = T::bus_frequency().0; match speed { Dspd::HIGH_SPEED => { // From RM0431 (F72xx), RM0090 (F429), RM0390 (F446) From 22b5f73811a7cc0dbca920e02b5d001d252d344c Mon Sep 17 00:00:00 2001 From: 1-rafael-1 Date: Thu, 1 May 2025 00:11:56 +0200 Subject: [PATCH 33/97] add manual overclock example, finalize API, cleanup --- embassy-rp/src/clocks.rs | 268 +++++++++++++----------- examples/rp/src/bin/overclock.rs | 24 +-- examples/rp/src/bin/overclock_manual.rs | 81 +++++++ 3 files changed, 227 insertions(+), 146 deletions(-) create mode 100644 examples/rp/src/bin/overclock_manual.rs diff --git a/embassy-rp/src/clocks.rs b/embassy-rp/src/clocks.rs index 1f5c27df1..86c172879 100644 --- a/embassy-rp/src/clocks.rs +++ b/embassy-rp/src/clocks.rs @@ -147,56 +147,30 @@ pub enum PeriClkSrc { /// Core voltage scaling options for RP2040. /// /// The RP2040 voltage regulator can be configured for different output voltages. -/// Higher voltages allow for higher clock frequencies but increase power consumption. -/// -/// # Typical Use Cases -/// -/// - `V0_85` to `V1_05`: Power saving for lower frequencies (below 100MHz) -/// - `V1_10`: Default voltage, safe for standard 125MHz operation -/// - `V1_15`: Required for frequencies above 133MHz (e.g., 200MHz overclocking) -/// - `V1_20`: For more extreme overclocking (200MHz+) -/// - `V1_25` and `V1_30`: Highest voltage settings, use with caution -/// -/// # Overclocking Notes -/// -/// When overclocking: -/// - Frequencies up to 133MHz are typically stable at default voltage (`V1_10`) -/// - Frequencies from 133MHz to 200MHz generally require `V1_15` -/// - Frequencies above 200MHz typically require `V1_20` or higher -/// -/// # Power Consumption -/// -/// Higher voltages increase power consumption and heat generation. In battery-powered -/// applications, consider using lower voltages when maximum performance is not required. -/// -/// # Safety -/// -/// Increased voltage can reduce the lifespan of the chip if used for extended periods, -/// especially at `V1_25` and `V1_30`. These higher voltages should be used with -/// consideration of thermal management. +/// Higher voltages allow for higher clock frequencies but increase power consumption and heat. #[cfg(feature = "rp2040")] #[derive(Clone, Copy, Debug, PartialEq, Eq)] #[repr(u8)] pub enum VoltageScale { - /// 0.85V - Lowest power consumption, suitable for low frequencies + /// 0.85V V0_85 = 0b0110, - /// 0.90V - Low power consumption + /// 0.90V V0_90 = 0b0111, - /// 0.95V - Low power consumption + /// 0.95V V0_95 = 0b1000, - /// 1.00V - Medium power consumption + /// 1.00V V1_00 = 0b1001, - /// 1.05V - Medium power consumption + /// 1.05V V1_05 = 0b1010, - /// 1.10V (Default) - Standard voltage for 125MHz operation + /// 1.10V V1_10 = 0b1011, - /// 1.15V - Required for frequencies above 133MHz + /// 1.15V V1_15 = 0b1100, - /// 1.20V - For higher overclocking (200MHz+) + /// 1.20V V1_20 = 0b1101, - /// 1.25V - High voltage, use with caution + /// 1.25V V1_25 = 0b1110, - /// 1.30V - Maximum voltage, use with extreme caution + /// 1.30V V1_30 = 0b1111, } @@ -204,10 +178,8 @@ pub enum VoltageScale { impl VoltageScale { /// Get the recommended Brown-Out Detection (BOD) setting for this voltage. /// Sets the BOD threshold to approximately 90% of the core voltage. - /// See RP2040 Datasheet, Table 190, BOD Register fn recommended_bod(self) -> u8 { match self { - // ~90% of voltage setting based on Table 190 values VoltageScale::V0_85 => 0b0111, // 0.774V (~91% of 0.85V) VoltageScale::V0_90 => 0b1000, // 0.817V (~91% of 0.90V) VoltageScale::V0_95 => 0b1001, // 0.860V (~91% of 0.95V) @@ -246,7 +218,7 @@ pub struct ClockConfig { #[cfg(feature = "rp2040")] pub voltage_scale: Option, /// Voltage stabilization delay in microseconds. - /// If not set, appropriate defaults will be used based on voltage level. + /// If not set, defaults will be used based on voltage level. #[cfg(feature = "rp2040")] pub voltage_stabilization_delay_us: Option, // gpin0: Option<(u32, Gpin<'static, AnyPin>)>, @@ -409,7 +381,7 @@ impl ClockConfig { /// Configure the system clock to a specific frequency in MHz. /// - /// This is a more user-friendly way to configure the system clock, similar to + /// This is a user-friendly way to configure the system clock, similar to /// the Pico SDK's approach. It automatically handles voltage scaling based on the /// requested frequency and uses the standard 12MHz crystal found on most RP2040 boards. /// @@ -420,9 +392,6 @@ impl ClockConfig { /// # Example /// /// ``` - /// // Configure for standard 125MHz clock - /// let config = ClockConfig::at_sys_frequency_mhz(125); - /// /// // Overclock to 200MHz /// let config = ClockConfig::at_sys_frequency_mhz(200); /// ``` @@ -551,6 +520,98 @@ impl ClockConfig { voltage_stabilization_delay_us: None, } } + + /// Configure with manual PLL settings for full control over system clock + /// + /// This method provides a simple way to configure the system with custom PLL parameters + /// without needing to understand the full nested configuration structure. + /// + /// # Arguments + /// + /// * `xosc_hz` - The frequency of the external crystal in Hz + /// * `pll_config` - The PLL configuration parameters to achieve desired frequency + /// * `voltage_scale` - Optional voltage scaling for overclocking (required for >133MHz) + /// + /// # Returns + /// + /// A ClockConfig configured with the specified PLL parameters + /// + /// # Example + /// + /// ```rust,ignore + /// // Configure for 200MHz operation + /// let config = Config::default(); + /// config.clocks = ClockConfig::manual_pll( + /// 12_000_000, + /// PllConfig { + /// refdiv: 1, // Reference divider (12 MHz / 1 = 12 MHz) + /// fbdiv: 100, // Feedback divider (12 MHz * 100 = 1200 MHz VCO) + /// post_div1: 3, // First post divider (1200 MHz / 3 = 400 MHz) + /// post_div2: 2, // Second post divider (400 MHz / 2 = 200 MHz) + /// }, + /// Some(VoltageScale::V1_15) + /// ); + /// ``` + #[cfg(feature = "rp2040")] + pub fn manual_pll(xosc_hz: u32, pll_config: PllConfig, voltage_scale: Option) -> Self { + // Calculate the actual output frequency for documentation + // let ref_freq = xosc_hz / pll_config.refdiv as u32; + // let vco_freq = ref_freq * pll_config.fbdiv as u32; + // let sys_freq = vco_freq / ((pll_config.post_div1 * pll_config.post_div2) as u32); + + // Validate PLL parameters + assert!(pll_config.is_valid(xosc_hz), "Invalid PLL parameters"); + + let mut config = Self::default(); + + config.xosc = Some(XoscConfig { + hz: xosc_hz, + sys_pll: Some(pll_config), + usb_pll: Some(PllConfig { + refdiv: 1, + fbdiv: 120, + post_div1: 6, + post_div2: 5, + }), + delay_multiplier: 128, + }); + + config.ref_clk = RefClkConfig { + src: RefClkSrc::Xosc, + div: 1, + }; + + config.sys_clk = SysClkConfig { + src: SysClkSrc::PllSys, + div_int: 1, + div_frac: 0, + }; + + config.voltage_scale = voltage_scale; + config.peri_clk_src = Some(PeriClkSrc::Sys); + + // Set reasonable defaults for other clocks + config.usb_clk = Some(UsbClkConfig { + src: UsbClkSrc::PllUsb, + div: 1, + phase: 0, + }); + + config.adc_clk = Some(AdcClkConfig { + src: AdcClkSrc::PllUsb, + div: 1, + phase: 0, + }); + + config.rtc_clk = Some(RtcClkConfig { + src: RtcClkSrc::PllUsb, + div_int: 1024, + div_frac: 0, + phase: 0, + }); + + config + } } /// ROSC freq range. @@ -596,30 +657,6 @@ pub struct XoscConfig { } /// PLL configuration. -/// -/// This struct defines the parameters used to configure the Phase-Locked Loop (PLL) -/// in the RP2040. The parameters follow the definitions from the RP2040 datasheet, -/// section 2.18.3. -/// -/// # Parameters -/// -/// * `refdiv` - Reference divider (1-63) -/// * `fbdiv` - VCO feedback divider (16-320) -/// * `post_div1` - First post divider (1-7) -/// * `post_div2` - Second post divider (1-7) - must be less than or equal to post_div1 -/// -/// # Constraints -/// -/// * VCO frequency (input_hz / refdiv * fbdiv) must be between 750MHz and 1800MHz -/// * post_div2 must be less than or equal to post_div1 -/// -/// # Calculation -/// -/// The output frequency of the PLL is calculated as: -/// -/// `output_hz = (input_hz / refdiv * fbdiv) / (post_div1 * post_div2)` -/// -/// Where input_hz is typically the crystal frequency (e.g., 12MHz). #[derive(Clone, Copy, Debug)] pub struct PllConfig { /// Reference divisor. @@ -836,35 +873,17 @@ pub struct RtcClkConfig { /// * `Some(PllConfig)` if valid parameters were found /// * `None` if no valid parameters could be found for the requested combination /// -/// # Algorithm -/// -/// 1. Set reference divider to 1 (fixed for simplicity) -/// 2. Try different feedback divisors (fbdiv) starting from highest to lowest -/// 3. For each fbdiv value, check if the resulting VCO frequency is valid (750-1800MHz) -/// 4. Find post-divider combinations that give the exact requested frequency -/// 5. If no exact match, return the closest approximation -/// /// # Example /// /// ``` /// // Find parameters for 133MHz system clock from 12MHz crystal /// let pll_params = find_pll_params(12_000_000, 133_000_000).unwrap(); /// ``` -/// -/// Similar to the Pico SDK's parameter selection approach, prioritizing stability. -/// See RP2040 Datasheet section 2.16.3. Reference Clock (ref) and 2.18.3. PLL #[cfg(feature = "rp2040")] fn find_pll_params(input_hz: u32, target_hz: u32) -> Option { // Fixed reference divider for system PLL const PLL_SYS_REFDIV: u8 = 1; - // Constraints from datasheet: - // REFDIV: 1..=63 - // FBDIV: 16..=320 - // POSTDIV1: 1..=7 - // POSTDIV2: 1..=7 (must be <= POSTDIV1) - // VCO frequency (input_hz / refdiv * fbdiv): 750MHz ..= 1800MHz - // Calculate reference frequency let reference_freq = input_hz / PLL_SYS_REFDIV as u32; @@ -969,8 +988,7 @@ pub(crate) unsafe fn init(config: ClockConfig) { #[cfg(feature = "_rp235x")] while c.clk_ref_selected().read() != pac::clocks::regs::ClkRefSelected(1) {} - // Set Core Voltage (RP2040 only) BEFORE doing anything with PLLs - // This is critical for overclocking - must be done before PLL setup + // Set Core Voltage (RP2040 only), if we have config for it and we're not using the default #[cfg(feature = "rp2040")] if let Some(voltage) = config.voltage_scale { let vreg = pac::VREG_AND_CHIP_RESET; @@ -978,17 +996,15 @@ pub(crate) unsafe fn init(config: ClockConfig) { let target_vsel = voltage as u8; if target_vsel != current_vsel { - // IMPORTANT: Use modify() instead of write() to preserve the HIZ and EN bits - // This is critical - otherwise we might disable the regulator when changing voltage + // Use modify() instead of write() to preserve the HIZ and EN bits - otherwise we will disable the regulator when changing voltage vreg.vreg().modify(|w| w.set_vsel(target_vsel)); - // For higher voltage settings (overclocking), we need a longer stabilization time - // Default to 1000 µs (1ms) like the SDK, but allow user override + // Wait for the voltage to stabilize. Use the provided delay or default based on voltage let settling_time_us = config.voltage_stabilization_delay_us.unwrap_or_else(|| { match voltage { - VoltageScale::V1_15 => 1000, // 1ms for 1.15V (matches SDK default) + VoltageScale::V1_15 => 1000, // 1ms for 1.15V VoltageScale::V1_20 | VoltageScale::V1_25 | VoltageScale::V1_30 => 2000, // 2ms for higher voltages - _ => 500, // 500 µs for standard voltages + _ => 0, // no delay for all others } }); @@ -1001,7 +1017,7 @@ pub(crate) unsafe fn init(config: ClockConfig) { // Wait for voltage to stabilize cortex_m::asm::delay(delay_cycles); - // Only NOW set the BOD level after voltage has stabilized + // Only now set the BOD level after voltage has stabilized vreg.bod().write(|w| w.set_vsel(voltage.recommended_bod())); } } @@ -1026,9 +1042,9 @@ pub(crate) unsafe fn init(config: ClockConfig) { // SETUP TEMPORARY STABLE CLOCKS FIRST // Configure USB PLL for our stable temporary clock // This follows the SDK's approach of using USB PLL as a stable intermediate clock - let usb_pll_freq = match &config.xosc { + let pll_usb_freq = match &config.xosc { Some(config) => match &config.usb_pll { - Some(usb_pll_config) => { + Some(pll_usb_config) => { // Reset USB PLL let mut peris = reset::Peripherals(0); peris.set_pll_usb(true); @@ -1036,7 +1052,7 @@ pub(crate) unsafe fn init(config: ClockConfig) { reset::unreset_wait(peris); // Configure the USB PLL - this should give us 48MHz - let usb_pll_freq = configure_pll(pac::PLL_USB, xosc_freq, *usb_pll_config); + let usb_pll_freq = configure_pll(pac::PLL_USB, xosc_freq, *pll_usb_config); CLOCKS.pll_usb.store(usb_pll_freq, Ordering::Relaxed); usb_pll_freq } @@ -1122,7 +1138,7 @@ pub(crate) unsafe fn init(config: ClockConfig) { let (src, aux, freq) = match config.sys_clk.src { SysClkSrc::Ref => (Src::CLK_REF, Aux::CLKSRC_PLL_SYS, clk_ref_freq), SysClkSrc::PllSys => (Src::CLKSRC_CLK_SYS_AUX, Aux::CLKSRC_PLL_SYS, pll_sys_freq), - SysClkSrc::PllUsb => (Src::CLKSRC_CLK_SYS_AUX, Aux::CLKSRC_PLL_USB, usb_pll_freq), + SysClkSrc::PllUsb => (Src::CLKSRC_CLK_SYS_AUX, Aux::CLKSRC_PLL_USB, pll_usb_freq), SysClkSrc::Rosc => (Src::CLKSRC_CLK_SYS_AUX, Aux::ROSC_CLKSRC, rosc_freq), SysClkSrc::Xosc => (Src::CLKSRC_CLK_SYS_AUX, Aux::XOSC_CLKSRC, xosc_freq), // SysClkSrc::Gpin0 => (Src::CLKSRC_CLK_SYS_AUX, Aux::CLKSRC_GPIN0, gpin0_freq), @@ -1185,7 +1201,7 @@ pub(crate) unsafe fn init(config: ClockConfig) { let peri_freq = match src { PeriClkSrc::Sys => clk_sys_freq, PeriClkSrc::PllSys => pll_sys_freq, - PeriClkSrc::PllUsb => usb_pll_freq, + PeriClkSrc::PllUsb => pll_usb_freq, PeriClkSrc::Rosc => rosc_freq, PeriClkSrc::Xosc => xosc_freq, // PeriClkSrc::Gpin0 => gpin0_freq, @@ -1210,7 +1226,7 @@ pub(crate) unsafe fn init(config: ClockConfig) { w.set_auxsrc(ClkUsbCtrlAuxsrc::from_bits(conf.src as _)); }); let usb_freq = match conf.src { - UsbClkSrc::PllUsb => usb_pll_freq, + UsbClkSrc::PllUsb => pll_usb_freq, UsbClkSrc::PllSys => pll_sys_freq, UsbClkSrc::Rosc => rosc_freq, UsbClkSrc::Xosc => xosc_freq, @@ -1234,7 +1250,7 @@ pub(crate) unsafe fn init(config: ClockConfig) { w.set_auxsrc(ClkAdcCtrlAuxsrc::from_bits(conf.src as _)); }); let adc_in_freq = match conf.src { - AdcClkSrc::PllUsb => usb_pll_freq, + AdcClkSrc::PllUsb => pll_usb_freq, AdcClkSrc::PllSys => pll_sys_freq, AdcClkSrc::Rosc => rosc_freq, AdcClkSrc::Xosc => xosc_freq, @@ -1262,7 +1278,7 @@ pub(crate) unsafe fn init(config: ClockConfig) { w.set_auxsrc(ClkRtcCtrlAuxsrc::from_bits(conf.src as _)); }); let rtc_in_freq = match conf.src { - RtcClkSrc::PllUsb => usb_pll_freq, + RtcClkSrc::PllUsb => pll_usb_freq, RtcClkSrc::PllSys => pll_sys_freq, RtcClkSrc::Rosc => rosc_freq, RtcClkSrc::Xosc => xosc_freq, @@ -1390,40 +1406,36 @@ fn start_xosc(crystal_hz: u32, delay_multiplier: u32) { while !pac::XOSC.status().read().stable() {} } +/// PLL (Phase-Locked Loop) configuration #[inline(always)] fn configure_pll(p: pac::pll::Pll, input_freq: u32, config: PllConfig) -> u32 { // Calculate reference frequency let ref_freq = input_freq / config.refdiv as u32; // Validate PLL parameters - assert!( - config.fbdiv >= 16 && config.fbdiv <= 320, - "fbdiv must be between 16 and 320" - ); - assert!( - config.post_div1 >= 1 && config.post_div1 <= 7, - "post_div1 must be between 1 and 7" - ); - assert!( - config.post_div2 >= 1 && config.post_div2 <= 7, - "post_div2 must be between 1 and 7" - ); - assert!(config.post_div2 <= config.post_div1, "post_div2 must be <= post_div1"); - assert!( - config.refdiv >= 1 && config.refdiv <= 63, - "refdiv must be between 1 and 63" - ); - assert!( - ref_freq >= 5_000_000 && ref_freq <= 800_000_000, - "ref_freq must be between 5MHz and 800MHz" - ); + // Feedback divider (FBDIV) must be between 16 and 320 + assert!(config.fbdiv >= 16 && config.fbdiv <= 320); + + // Post divider 1 (POSTDIV1) must be between 1 and 7 + assert!(config.post_div1 >= 1 && config.post_div1 <= 7); + + // Post divider 2 (POSTDIV2) must be between 1 and 7 + assert!(config.post_div2 >= 1 && config.post_div2 <= 7); + + // Post divider 2 (POSTDIV2) must be less than or equal to post divider 1 (POSTDIV1) + assert!(config.post_div2 <= config.post_div1); + + // Reference divider (REFDIV) must be between 1 and 63 + assert!(config.refdiv >= 1 && config.refdiv <= 63); + + // Reference frequency (REF_FREQ) must be between 5MHz and 800MHz + assert!(ref_freq >= 5_000_000 && ref_freq <= 800_000_000); // Calculate VCO frequency let vco_freq = ref_freq.saturating_mul(config.fbdiv as u32); - assert!( - vco_freq >= 750_000_000 && vco_freq <= 1_800_000_000, - "VCO frequency must be between 750MHz and 1800MHz" - ); + + // VCO (Voltage Controlled Oscillator) frequency must be between 750MHz and 1800MHz + assert!(vco_freq >= 750_000_000 && vco_freq <= 1_800_000_000); // We follow the SDK's approach to PLL configuration which is: // 1. Power down PLL diff --git a/examples/rp/src/bin/overclock.rs b/examples/rp/src/bin/overclock.rs index 9027f1516..e3ac77340 100644 --- a/examples/rp/src/bin/overclock.rs +++ b/examples/rp/src/bin/overclock.rs @@ -1,9 +1,13 @@ +//! # Overclocking the RP2040 to 200 MHz +//! +//! This example demonstrates how to configure the RP2040 to run at 200 MHz using a higher level API. + #![no_std] #![no_main] use defmt::*; use embassy_executor::Spawner; -use embassy_rp::clocks::{clk_sys_freq, ClockConfig, VoltageScale}; +use embassy_rp::clocks::{clk_sys_freq, ClockConfig}; use embassy_rp::config::Config; use embassy_rp::gpio::{Level, Output}; use embassy_time::{Duration, Instant, Timer}; @@ -15,15 +19,10 @@ const COUNT_TO: i64 = 10_000_000; async fn main(_spawner: Spawner) -> ! { // Set up for clock frequency of 200 MHz // This will set all the necessary defaults including slightly raised voltage - // See embassy_rp::clocks::ClockConfig for more options, including full manual control let config = Config::new(ClockConfig::at_sys_frequency_mhz(200)); - // Show the voltage scale and brownout-detection for verification + // Show the voltage scale for verification info!("System core voltage: {}", Debug2Format(&config.clocks.voltage_scale)); - // info!( - // "Brownout detection: {}", - // Debug2Format(&config.clocks.brownout_detection) - // ); // Initialize the peripherals let p = embassy_rp::init(config); @@ -64,14 +63,3 @@ async fn main(_spawner: Spawner) -> ! { Timer::after(Duration::from_secs(2)).await; } } - -// let config = Config::new(ClockConfig::with_speed_mhz_test_voltage(125, Some(VoltageScale::V1_10))); -// let config = Config::default(); -// let config = Config::new(ClockConfig::with_speed_mhz_test_voltage_extended_delay( -// 200, // Standard 125MHz clock -// Some(VoltageScale::V1_15), // 1.15V voltage -// Some(1000), // 1000μs (1ms) stabilization delay - significantly longer than default -// )); -// Initialize the peripherals - -// let p = embassy_rp::init(Default::default()); //testing the bog standard diff --git a/examples/rp/src/bin/overclock_manual.rs b/examples/rp/src/bin/overclock_manual.rs new file mode 100644 index 000000000..ad6abf0e7 --- /dev/null +++ b/examples/rp/src/bin/overclock_manual.rs @@ -0,0 +1,81 @@ +//! # Overclocking the RP2040 to 200 MHz manually +//! +//! This example demonstrates how to manually configure the RP2040 to run at 200 MHz. + +#![no_std] +#![no_main] + +use defmt::*; +use embassy_executor::Spawner; +use embassy_rp::clocks; +use embassy_rp::clocks::{ClockConfig, PllConfig, VoltageScale}; +use embassy_rp::config::Config; +use embassy_rp::gpio::{Level, Output}; +use embassy_time::{Duration, Instant, Timer}; +use {defmt_rtt as _, panic_probe as _}; + +const COUNT_TO: i64 = 10_000_000; + +/// Configure the RP2040 for 200 MHz operation by manually specifying +/// all the required parameters instead of using higher-level APIs. +fn configure_manual_overclock() -> Config { + // Set the PLL configuration manually, starting from default values + let mut config = Config::default(); + + // Set the system clock to 200 MHz using a PLL with a reference frequency of 12 MHz + config.clocks = ClockConfig::manual_pll( + 12_000_000, + PllConfig { + refdiv: 1, + fbdiv: 100, + post_div1: 3, + post_div2: 2, + }, + // For 200 MHz, we need a voltage scale of 1.15V + Some(VoltageScale::V1_15), + ); + + config +} + +#[embassy_executor::main] +async fn main(_spawner: Spawner) -> ! { + // Initialize with our manual overclock configuration + let p = embassy_rp::init(configure_manual_overclock()); + + // Verify the actual system clock frequency + let sys_freq = clocks::clk_sys_freq(); + info!("System clock frequency: {} MHz", sys_freq / 1_000_000); + + // LED to indicate the system is running + let mut led = Output::new(p.PIN_25, Level::Low); + + loop { + // Reset the counter at the start of measurement period + let mut counter = 0; + + // Turn LED on while counting + led.set_high(); + + let start = Instant::now(); + + // This is a busy loop that will take some time to complete + while counter < COUNT_TO { + counter += 1; + } + + let elapsed = Instant::now() - start; + + // Report the elapsed time + led.set_low(); + info!( + "At {}Mhz: Elapsed time to count to {}: {}ms", + sys_freq / 1_000_000, + counter, + elapsed.as_millis() + ); + + // Wait 2 seconds before starting the next measurement + Timer::after(Duration::from_secs(2)).await; + } +} From ace20f40ad1b2168c9d2b78155c0cf29244bc456 Mon Sep 17 00:00:00 2001 From: Dion Dokter Date: Thu, 1 May 2025 10:35:10 +0200 Subject: [PATCH 34/97] Maybe fixed all CI --- .github/ci/test.sh | 4 ++-- embassy-stm32/Cargo.toml | 4 ++-- examples/stm32l4/Cargo.toml | 2 +- tests/stm32/Cargo.toml | 8 +++++--- 4 files changed, 10 insertions(+), 8 deletions(-) diff --git a/.github/ci/test.sh b/.github/ci/test.sh index c78865e54..88962a533 100755 --- a/.github/ci/test.sh +++ b/.github/ci/test.sh @@ -29,8 +29,8 @@ cargo test --manifest-path ./embassy-nrf/Cargo.toml --no-default-features --feat cargo test --manifest-path ./embassy-rp/Cargo.toml --no-default-features --features time-driver,rp2040,_test cargo test --manifest-path ./embassy-rp/Cargo.toml --no-default-features --features time-driver,rp235xa,_test -cargo test --manifest-path ./embassy-stm32/Cargo.toml --no-default-features --features stm32f429vg,exti,time-driver-any,exti +cargo test --manifest-path ./embassy-stm32/Cargo.toml --no-default-features --features stm32f429vg,exti,time-driver-any,exti,dual-bank cargo test --manifest-path ./embassy-stm32/Cargo.toml --no-default-features --features stm32f732ze,exti,time-driver-any,exti -cargo test --manifest-path ./embassy-stm32/Cargo.toml --no-default-features --features stm32f769ni,exti,time-driver-any,exti +cargo test --manifest-path ./embassy-stm32/Cargo.toml --no-default-features --features stm32f769ni,exti,time-driver-any,exti,single-bank cargo test --manifest-path ./embassy-net-adin1110/Cargo.toml diff --git a/embassy-stm32/Cargo.toml b/embassy-stm32/Cargo.toml index 5ed5c3519..ec22325f9 100644 --- a/embassy-stm32/Cargo.toml +++ b/embassy-stm32/Cargo.toml @@ -74,7 +74,7 @@ sdio-host = "0.9.0" critical-section = "1.1" #stm32-metapac = { version = "16" } # stm32-metapac = { git = "https://github.com/embassy-rs/stm32-data-generated", tag = "stm32-data-380f03cb71f43a242adc45e83607a380ffe0447b" } -stm32-metapac = { git="https://ci.embassy.dev/jobs/039aa6808ef1/artifacts/generated.git" } +stm32-metapac = { git="https://ci.embassy.dev/jobs/3de13111608a/artifacts/generated.git" } vcell = "0.1.3" nb = "1.0.0" @@ -104,7 +104,7 @@ quote = "1.0.15" #stm32-metapac = { version = "16", default-features = false, features = ["metadata"]} # stm32-metapac = { git = "https://github.com/embassy-rs/stm32-data-generated", tag = "stm32-data-380f03cb71f43a242adc45e83607a380ffe0447b", default-features = false, features = ["metadata"] } -stm32-metapac = { git="https://ci.embassy.dev/jobs/039aa6808ef1/artifacts/generated.git", default-features = false, features = ["metadata"] } +stm32-metapac = { git="https://ci.embassy.dev/jobs/3de13111608a/artifacts/generated.git", default-features = false, features = ["metadata"] } [features] default = ["rt"] diff --git a/examples/stm32l4/Cargo.toml b/examples/stm32l4/Cargo.toml index b609110af..239bfcd79 100644 --- a/examples/stm32l4/Cargo.toml +++ b/examples/stm32l4/Cargo.toml @@ -6,7 +6,7 @@ license = "MIT OR Apache-2.0" [dependencies] # Change stm32l4s5vi to your chip name, if necessary. -embassy-stm32 = { version = "0.2.0", path = "../../embassy-stm32", features = [ "defmt", "unstable-pac", "stm32l4r5zi", "memory-x", "time-driver-any", "exti", "chrono"] } +embassy-stm32 = { version = "0.2.0", path = "../../embassy-stm32", features = [ "defmt", "unstable-pac", "stm32l4r5zi", "memory-x", "time-driver-any", "exti", "chrono", "dual-bank"] } embassy-sync = { version = "0.6.2", path = "../../embassy-sync", features = ["defmt"] } embassy-executor = { version = "0.7.0", path = "../../embassy-executor", features = ["arch-cortex-m", "executor-thread", "defmt"] } embassy-time = { version = "0.4.0", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime", "tick-hz-32_768", ] } diff --git a/tests/stm32/Cargo.toml b/tests/stm32/Cargo.toml index a676aee53..3a347e279 100644 --- a/tests/stm32/Cargo.toml +++ b/tests/stm32/Cargo.toml @@ -12,7 +12,7 @@ stm32f207zg = ["embassy-stm32/stm32f207zg", "spi-v1", "chrono", "not-gpdma", "et stm32f303ze = ["embassy-stm32/stm32f303ze", "chrono", "not-gpdma"] stm32f429zi = ["embassy-stm32/stm32f429zi", "spi-v1", "chrono", "eth", "stop", "can", "not-gpdma", "dac", "rng"] stm32f446re = ["embassy-stm32/stm32f446re", "spi-v1", "chrono", "stop", "can", "not-gpdma", "dac", "sdmmc"] -stm32f767zi = ["embassy-stm32/stm32f767zi", "chrono", "not-gpdma", "eth", "rng"] +stm32f767zi = ["embassy-stm32/stm32f767zi", "chrono", "not-gpdma", "eth", "rng", "single-bank"] stm32g071rb = ["embassy-stm32/stm32g071rb", "cm0", "not-gpdma", "dac", "ucpd"] stm32g491re = ["embassy-stm32/stm32g491re", "chrono", "stop", "not-gpdma", "rng", "fdcan", "cordic"] stm32h563zi = ["embassy-stm32/stm32h563zi", "spi-v345", "chrono", "eth", "rng", "fdcan", "hash", "cordic", "stop"] @@ -23,8 +23,8 @@ stm32l073rz = ["embassy-stm32/stm32l073rz", "cm0", "not-gpdma", "rng"] stm32l152re = ["embassy-stm32/stm32l152re", "spi-v1", "chrono", "not-gpdma"] stm32l496zg = ["embassy-stm32/stm32l496zg", "not-gpdma", "rng"] stm32l4a6zg = ["embassy-stm32/stm32l4a6zg", "chrono", "not-gpdma", "rng", "hash"] -stm32l4r5zi = ["embassy-stm32/stm32l4r5zi", "chrono", "not-gpdma", "rng"] -stm32l552ze = ["embassy-stm32/stm32l552ze", "not-gpdma", "rng", "hash"] +stm32l4r5zi = ["embassy-stm32/stm32l4r5zi", "chrono", "not-gpdma", "rng", "dual-bank"] +stm32l552ze = ["embassy-stm32/stm32l552ze", "not-gpdma", "rng", "hash", "dual-bank"] stm32u585ai = ["embassy-stm32/stm32u585ai", "spi-v345", "chrono", "rng", "hash", "cordic"] stm32u5a5zj = ["embassy-stm32/stm32u5a5zj", "spi-v345", "chrono", "rng", "hash"] # FIXME: cordic test cause it crash stm32wb55rg = ["embassy-stm32/stm32wb55rg", "chrono", "not-gpdma", "ble", "mac" , "rng"] @@ -53,6 +53,8 @@ not-gpdma = [] dac = [] ucpd = [] cordic = ["dep:num-traits"] +dual-bank = ["embassy-stm32/dual-bank"] +single-bank = ["embassy-stm32/single-bank"] cm0 = ["portable-atomic/unsafe-assume-single-core"] From a83726b77e1f40293b6c0701c7d203160bc150fb Mon Sep 17 00:00:00 2001 From: Dion Dokter Date: Thu, 1 May 2025 11:15:14 +0200 Subject: [PATCH 35/97] Fix the f4 flash tests by throwing away alt mode --- embassy-stm32/src/flash/f4.rs | 299 +++++++--------------------------- 1 file changed, 59 insertions(+), 240 deletions(-) diff --git a/embassy-stm32/src/flash/f4.rs b/embassy-stm32/src/flash/f4.rs index ba3067556..7d789a6bb 100644 --- a/embassy-stm32/src/flash/f4.rs +++ b/embassy-stm32/src/flash/f4.rs @@ -8,173 +8,6 @@ use super::{FlashBank, FlashRegion, FlashSector, FLASH_REGIONS, WRITE_SIZE}; use crate::_generated::FLASH_SIZE; use crate::flash::Error; use crate::pac; -#[allow(missing_docs)] // TODO -#[cfg(any(stm32f427, stm32f429, stm32f437, stm32f439, stm32f469, stm32f479))] -mod alt_regions { - use core::marker::PhantomData; - - use crate::Peri; - use crate::_generated::flash_regions::{OTPRegion, BANK1_REGION1, BANK1_REGION2, BANK1_REGION3, OTP_REGION}; - use crate::_generated::FLASH_SIZE; - use crate::flash::{asynch, Async, Bank1Region1, Bank1Region2, Blocking, Error, Flash, FlashBank, FlashRegion}; - use crate::peripherals::FLASH; - - pub const ALT_BANK1_REGION3: FlashRegion = FlashRegion { - size: 3 * BANK1_REGION3.erase_size, - ..BANK1_REGION3 - }; - pub const ALT_BANK2_REGION1: FlashRegion = FlashRegion { - bank: FlashBank::Bank2, - base: BANK1_REGION1.base + FLASH_SIZE as u32 / 2, - ..BANK1_REGION1 - }; - pub const ALT_BANK2_REGION2: FlashRegion = FlashRegion { - bank: FlashBank::Bank2, - base: BANK1_REGION2.base + FLASH_SIZE as u32 / 2, - ..BANK1_REGION2 - }; - pub const ALT_BANK2_REGION3: FlashRegion = FlashRegion { - bank: FlashBank::Bank2, - base: BANK1_REGION3.base + FLASH_SIZE as u32 / 2, - size: 3 * BANK1_REGION3.erase_size, - ..BANK1_REGION3 - }; - - pub const ALT_FLASH_REGIONS: [&FlashRegion; 6] = [ - &BANK1_REGION1, - &BANK1_REGION2, - &ALT_BANK1_REGION3, - &ALT_BANK2_REGION1, - &ALT_BANK2_REGION2, - &ALT_BANK2_REGION3, - ]; - - pub struct AltBank1Region3<'d, MODE = Async>(pub &'static FlashRegion, Peri<'d, FLASH>, PhantomData); - pub struct AltBank2Region1<'d, MODE = Async>(pub &'static FlashRegion, Peri<'d, FLASH>, PhantomData); - pub struct AltBank2Region2<'d, MODE = Async>(pub &'static FlashRegion, Peri<'d, FLASH>, PhantomData); - pub struct AltBank2Region3<'d, MODE = Async>(pub &'static FlashRegion, Peri<'d, FLASH>, PhantomData); - - pub struct AltFlashLayout<'d, MODE = Async> { - pub bank1_region1: Bank1Region1<'d, MODE>, - pub bank1_region2: Bank1Region2<'d, MODE>, - pub bank1_region3: AltBank1Region3<'d, MODE>, - pub bank2_region1: AltBank2Region1<'d, MODE>, - pub bank2_region2: AltBank2Region2<'d, MODE>, - pub bank2_region3: AltBank2Region3<'d, MODE>, - pub otp_region: OTPRegion<'d, MODE>, - } - - impl<'d> Flash<'d> { - pub fn into_alt_regions(self) -> AltFlashLayout<'d, Async> { - assert!(!super::is_default_layout()); - - // SAFETY: We never expose the cloned peripheral references, and their instance is not public. - // Also, all async flash region operations are protected with a mutex. - let p = self.inner; - AltFlashLayout { - bank1_region1: Bank1Region1(&BANK1_REGION1, unsafe { p.clone_unchecked() }, PhantomData), - bank1_region2: Bank1Region2(&BANK1_REGION2, unsafe { p.clone_unchecked() }, PhantomData), - bank1_region3: AltBank1Region3(&ALT_BANK1_REGION3, unsafe { p.clone_unchecked() }, PhantomData), - bank2_region1: AltBank2Region1(&ALT_BANK2_REGION1, unsafe { p.clone_unchecked() }, PhantomData), - bank2_region2: AltBank2Region2(&ALT_BANK2_REGION2, unsafe { p.clone_unchecked() }, PhantomData), - bank2_region3: AltBank2Region3(&ALT_BANK2_REGION3, unsafe { p.clone_unchecked() }, PhantomData), - otp_region: OTPRegion(&OTP_REGION, unsafe { p.clone_unchecked() }, PhantomData), - } - } - - pub fn into_alt_blocking_regions(self) -> AltFlashLayout<'d, Blocking> { - assert!(!super::is_default_layout()); - - // SAFETY: We never expose the cloned peripheral references, and their instance is not public. - // Also, all blocking flash region operations are protected with a cs. - let p = self.inner; - AltFlashLayout { - bank1_region1: Bank1Region1(&BANK1_REGION1, unsafe { p.clone_unchecked() }, PhantomData), - bank1_region2: Bank1Region2(&BANK1_REGION2, unsafe { p.clone_unchecked() }, PhantomData), - bank1_region3: AltBank1Region3(&ALT_BANK1_REGION3, unsafe { p.clone_unchecked() }, PhantomData), - bank2_region1: AltBank2Region1(&ALT_BANK2_REGION1, unsafe { p.clone_unchecked() }, PhantomData), - bank2_region2: AltBank2Region2(&ALT_BANK2_REGION2, unsafe { p.clone_unchecked() }, PhantomData), - bank2_region3: AltBank2Region3(&ALT_BANK2_REGION3, unsafe { p.clone_unchecked() }, PhantomData), - otp_region: OTPRegion(&OTP_REGION, unsafe { p.clone_unchecked() }, PhantomData), - } - } - } - - macro_rules! foreach_altflash_region { - ($type_name:ident, $region:ident) => { - impl $type_name<'_, MODE> { - pub fn blocking_read(&mut self, offset: u32, bytes: &mut [u8]) -> Result<(), Error> { - crate::flash::common::blocking_read(self.0.base, self.0.size, offset, bytes) - } - } - - impl $type_name<'_, Async> { - pub async fn read(&mut self, offset: u32, bytes: &mut [u8]) -> Result<(), Error> { - self.blocking_read(offset, bytes) - } - - pub async fn write(&mut self, offset: u32, bytes: &[u8]) -> Result<(), Error> { - let _guard = asynch::REGION_ACCESS.lock().await; - unsafe { asynch::write_chunked(self.0.base, self.0.size, offset, bytes).await } - } - - pub async fn erase(&mut self, from: u32, to: u32) -> Result<(), Error> { - let _guard = asynch::REGION_ACCESS.lock().await; - unsafe { asynch::erase_sectored(self.0.base, from, to).await } - } - } - - impl embedded_storage::nor_flash::ErrorType for $type_name<'_, MODE> { - type Error = Error; - } - - impl embedded_storage::nor_flash::ReadNorFlash for $type_name<'_, MODE> { - const READ_SIZE: usize = crate::flash::READ_SIZE; - - fn read(&mut self, offset: u32, bytes: &mut [u8]) -> Result<(), Self::Error> { - self.blocking_read(offset, bytes) - } - - fn capacity(&self) -> usize { - self.0.size as usize - } - } - - impl embedded_storage_async::nor_flash::ReadNorFlash for $type_name<'_, Async> { - const READ_SIZE: usize = crate::flash::READ_SIZE; - - async fn read(&mut self, offset: u32, bytes: &mut [u8]) -> Result<(), Self::Error> { - self.read(offset, bytes).await - } - - fn capacity(&self) -> usize { - self.0.size as usize - } - } - - impl embedded_storage_async::nor_flash::NorFlash for $type_name<'_, Async> { - const WRITE_SIZE: usize = $region.write_size as usize; - const ERASE_SIZE: usize = $region.erase_size as usize; - - async fn write(&mut self, offset: u32, bytes: &[u8]) -> Result<(), Self::Error> { - self.write(offset, bytes).await - } - - async fn erase(&mut self, from: u32, to: u32) -> Result<(), Self::Error> { - self.erase(from, to).await - } - } - }; - } - - foreach_altflash_region!(AltBank1Region3, ALT_BANK1_REGION3); - foreach_altflash_region!(AltBank2Region1, ALT_BANK2_REGION1); - foreach_altflash_region!(AltBank2Region2, ALT_BANK2_REGION2); - foreach_altflash_region!(AltBank2Region3, ALT_BANK2_REGION3); -} - -#[cfg(any(stm32f427, stm32f429, stm32f437, stm32f439, stm32f469, stm32f479))] -pub use alt_regions::*; static WAKER: AtomicWaker = AtomicWaker::new(); static DATA_CACHE_WAS_ENABLED: AtomicBool = AtomicBool::new(false); @@ -185,26 +18,10 @@ impl FlashSector { } } -#[cfg(any(stm32f427, stm32f429, stm32f437, stm32f439, stm32f469, stm32f479))] -pub(crate) fn is_default_layout() -> bool { - !pac::FLASH.optcr().read().db1m() -} - -#[cfg(not(any(stm32f427, stm32f429, stm32f437, stm32f439, stm32f469, stm32f479)))] pub(crate) const fn is_default_layout() -> bool { true } -#[cfg(any(stm32f427, stm32f429, stm32f437, stm32f439, stm32f469, stm32f479))] -pub fn get_flash_regions() -> &'static [&'static FlashRegion] { - if is_default_layout() { - &FLASH_REGIONS - } else { - &ALT_FLASH_REGIONS - } -} - -#[cfg(not(any(stm32f427, stm32f429, stm32f437, stm32f439, stm32f469, stm32f479)))] pub const fn get_flash_regions() -> &'static [&'static FlashRegion] { &FLASH_REGIONS } @@ -487,72 +304,74 @@ mod tests { const MEDIUM_SECTOR_SIZE: u32 = 64 * 1024; const LARGE_SECTOR_SIZE: u32 = 128 * 1024; - let assert_sector = |snb: u8, index_in_bank: u8, start: u32, size: u32, address: u32| { - let sector = get_sector(address, &FLASH_REGIONS); - assert_eq!(snb, sector.snb()); - assert_eq!( - FlashSector { - bank: FlashBank::Bank1, - index_in_bank, - start, - size - }, - sector - ); - }; + if !cfg!(feature = "dual-bank") { + let assert_sector = |snb: u8, index_in_bank: u8, start: u32, size: u32, address: u32| { + let sector = get_sector(address, &FLASH_REGIONS); + assert_eq!(snb, sector.snb()); + assert_eq!( + FlashSector { + bank: sector.bank, + index_in_bank, + start, + size + }, + sector + ); + }; - assert_sector(0x00, 0, 0x0800_0000, SMALL_SECTOR_SIZE, 0x0800_0000); - assert_sector(0x00, 0, 0x0800_0000, SMALL_SECTOR_SIZE, 0x0800_3FFF); - assert_sector(0x03, 3, 0x0800_C000, SMALL_SECTOR_SIZE, 0x0800_C000); - assert_sector(0x03, 3, 0x0800_C000, SMALL_SECTOR_SIZE, 0x0800_FFFF); + assert_sector(0x00, 0, 0x0800_0000, SMALL_SECTOR_SIZE, 0x0800_0000); + assert_sector(0x00, 0, 0x0800_0000, SMALL_SECTOR_SIZE, 0x0800_3FFF); + assert_sector(0x03, 3, 0x0800_C000, SMALL_SECTOR_SIZE, 0x0800_C000); + assert_sector(0x03, 3, 0x0800_C000, SMALL_SECTOR_SIZE, 0x0800_FFFF); - assert_sector(0x04, 4, 0x0801_0000, MEDIUM_SECTOR_SIZE, 0x0801_0000); - assert_sector(0x04, 4, 0x0801_0000, MEDIUM_SECTOR_SIZE, 0x0801_FFFF); + assert_sector(0x04, 4, 0x0801_0000, MEDIUM_SECTOR_SIZE, 0x0801_0000); + assert_sector(0x04, 4, 0x0801_0000, MEDIUM_SECTOR_SIZE, 0x0801_FFFF); - assert_sector(0x05, 5, 0x0802_0000, LARGE_SECTOR_SIZE, 0x0802_0000); - assert_sector(0x05, 5, 0x0802_0000, LARGE_SECTOR_SIZE, 0x0803_FFFF); - assert_sector(0x0B, 11, 0x080E_0000, LARGE_SECTOR_SIZE, 0x080E_0000); - assert_sector(0x0B, 11, 0x080E_0000, LARGE_SECTOR_SIZE, 0x080F_FFFF); + assert_sector(0x05, 5, 0x0802_0000, LARGE_SECTOR_SIZE, 0x0802_0000); + assert_sector(0x05, 5, 0x0802_0000, LARGE_SECTOR_SIZE, 0x0803_FFFF); + assert_sector(0x0B, 11, 0x080E_0000, LARGE_SECTOR_SIZE, 0x080E_0000); + assert_sector(0x0B, 11, 0x080E_0000, LARGE_SECTOR_SIZE, 0x080F_FFFF); + } else { + let assert_sector = |snb: u8, bank: FlashBank, index_in_bank: u8, start: u32, size: u32, address: u32| { + let sector = get_sector(address, &FLASH_REGIONS); + assert_eq!(snb, sector.snb()); + assert_eq!( + FlashSector { + bank, + index_in_bank, + start, + size + }, + sector + ) + }; - let assert_sector = |snb: u8, bank: FlashBank, index_in_bank: u8, start: u32, size: u32, address: u32| { - let sector = get_sector(address, &ALT_FLASH_REGIONS); - assert_eq!(snb, sector.snb()); - assert_eq!( - FlashSector { - bank, - index_in_bank, - start, - size - }, - sector - ) - }; + assert_sector(0x00, FlashBank::Bank1, 0, 0x0800_0000, SMALL_SECTOR_SIZE, 0x0800_0000); + assert_sector(0x00, FlashBank::Bank1, 0, 0x0800_0000, SMALL_SECTOR_SIZE, 0x0800_3FFF); + assert_sector(0x03, FlashBank::Bank1, 3, 0x0800_C000, SMALL_SECTOR_SIZE, 0x0800_C000); + assert_sector(0x03, FlashBank::Bank1, 3, 0x0800_C000, SMALL_SECTOR_SIZE, 0x0800_FFFF); - assert_sector(0x00, FlashBank::Bank1, 0, 0x0800_0000, SMALL_SECTOR_SIZE, 0x0800_0000); - assert_sector(0x00, FlashBank::Bank1, 0, 0x0800_0000, SMALL_SECTOR_SIZE, 0x0800_3FFF); - assert_sector(0x03, FlashBank::Bank1, 3, 0x0800_C000, SMALL_SECTOR_SIZE, 0x0800_C000); - assert_sector(0x03, FlashBank::Bank1, 3, 0x0800_C000, SMALL_SECTOR_SIZE, 0x0800_FFFF); + assert_sector(0x04, FlashBank::Bank1, 4, 0x0801_0000, MEDIUM_SECTOR_SIZE, 0x0801_0000); + assert_sector(0x04, FlashBank::Bank1, 4, 0x0801_0000, MEDIUM_SECTOR_SIZE, 0x0801_FFFF); - assert_sector(0x04, FlashBank::Bank1, 4, 0x0801_0000, MEDIUM_SECTOR_SIZE, 0x0801_0000); - assert_sector(0x04, FlashBank::Bank1, 4, 0x0801_0000, MEDIUM_SECTOR_SIZE, 0x0801_FFFF); + assert_sector(0x05, FlashBank::Bank1, 5, 0x0802_0000, LARGE_SECTOR_SIZE, 0x0802_0000); + assert_sector(0x05, FlashBank::Bank1, 5, 0x0802_0000, LARGE_SECTOR_SIZE, 0x0803_FFFF); + assert_sector(0x07, FlashBank::Bank1, 7, 0x0806_0000, LARGE_SECTOR_SIZE, 0x0806_0000); + assert_sector(0x07, FlashBank::Bank1, 7, 0x0806_0000, LARGE_SECTOR_SIZE, 0x0807_FFFF); - assert_sector(0x05, FlashBank::Bank1, 5, 0x0802_0000, LARGE_SECTOR_SIZE, 0x0802_0000); - assert_sector(0x05, FlashBank::Bank1, 5, 0x0802_0000, LARGE_SECTOR_SIZE, 0x0803_FFFF); - assert_sector(0x07, FlashBank::Bank1, 7, 0x0806_0000, LARGE_SECTOR_SIZE, 0x0806_0000); - assert_sector(0x07, FlashBank::Bank1, 7, 0x0806_0000, LARGE_SECTOR_SIZE, 0x0807_FFFF); + assert_sector(0x10, FlashBank::Bank2, 0, 0x0808_0000, SMALL_SECTOR_SIZE, 0x0808_0000); + assert_sector(0x10, FlashBank::Bank2, 0, 0x0808_0000, SMALL_SECTOR_SIZE, 0x0808_3FFF); + assert_sector(0x13, FlashBank::Bank2, 3, 0x0808_C000, SMALL_SECTOR_SIZE, 0x0808_C000); + assert_sector(0x13, FlashBank::Bank2, 3, 0x0808_C000, SMALL_SECTOR_SIZE, 0x0808_FFFF); - assert_sector(0x10, FlashBank::Bank2, 0, 0x0808_0000, SMALL_SECTOR_SIZE, 0x0808_0000); - assert_sector(0x10, FlashBank::Bank2, 0, 0x0808_0000, SMALL_SECTOR_SIZE, 0x0808_3FFF); - assert_sector(0x13, FlashBank::Bank2, 3, 0x0808_C000, SMALL_SECTOR_SIZE, 0x0808_C000); - assert_sector(0x13, FlashBank::Bank2, 3, 0x0808_C000, SMALL_SECTOR_SIZE, 0x0808_FFFF); + assert_sector(0x14, FlashBank::Bank2, 4, 0x0809_0000, MEDIUM_SECTOR_SIZE, 0x0809_0000); + assert_sector(0x14, FlashBank::Bank2, 4, 0x0809_0000, MEDIUM_SECTOR_SIZE, 0x0809_FFFF); - assert_sector(0x14, FlashBank::Bank2, 4, 0x0809_0000, MEDIUM_SECTOR_SIZE, 0x0809_0000); - assert_sector(0x14, FlashBank::Bank2, 4, 0x0809_0000, MEDIUM_SECTOR_SIZE, 0x0809_FFFF); - - assert_sector(0x15, FlashBank::Bank2, 5, 0x080A_0000, LARGE_SECTOR_SIZE, 0x080A_0000); - assert_sector(0x15, FlashBank::Bank2, 5, 0x080A_0000, LARGE_SECTOR_SIZE, 0x080B_FFFF); - assert_sector(0x17, FlashBank::Bank2, 7, 0x080E_0000, LARGE_SECTOR_SIZE, 0x080E_0000); - assert_sector(0x17, FlashBank::Bank2, 7, 0x080E_0000, LARGE_SECTOR_SIZE, 0x080F_FFFF); + assert_sector(0x15, FlashBank::Bank2, 5, 0x080A_0000, LARGE_SECTOR_SIZE, 0x080A_0000); + assert_sector(0x15, FlashBank::Bank2, 5, 0x080A_0000, LARGE_SECTOR_SIZE, 0x080B_FFFF); + assert_sector(0x17, FlashBank::Bank2, 7, 0x080E_0000, LARGE_SECTOR_SIZE, 0x080E_0000); + assert_sector(0x17, FlashBank::Bank2, 7, 0x080E_0000, LARGE_SECTOR_SIZE, 0x080F_FFFF); + } } } From 28a98ed0df5a6536190e267e46bc6822c532dba6 Mon Sep 17 00:00:00 2001 From: Dion Dokter Date: Thu, 1 May 2025 11:20:38 +0200 Subject: [PATCH 36/97] Fix f7 example --- examples/stm32f7/Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/stm32f7/Cargo.toml b/examples/stm32f7/Cargo.toml index 9fbe2efc3..1a46931d9 100644 --- a/examples/stm32f7/Cargo.toml +++ b/examples/stm32f7/Cargo.toml @@ -6,7 +6,7 @@ license = "MIT OR Apache-2.0" [dependencies] # Change stm32f777zi to your chip name, if necessary. -embassy-stm32 = { version = "0.2.0", path = "../../embassy-stm32", features = ["defmt", "stm32f777zi", "memory-x", "unstable-pac", "time-driver-any", "exti"] } +embassy-stm32 = { version = "0.2.0", path = "../../embassy-stm32", features = ["defmt", "stm32f777zi", "memory-x", "unstable-pac", "time-driver-any", "exti", "single-bank"] } embassy-sync = { version = "0.6.2", path = "../../embassy-sync", features = ["defmt"] } embassy-executor = { version = "0.7.0", path = "../../embassy-executor", features = ["arch-cortex-m", "executor-thread", "defmt"] } embassy-time = { version = "0.4.0", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime", "tick-hz-32_768"] } From 66b8fdc818cd9faba6919b0c57fd1938546cca9e Mon Sep 17 00:00:00 2001 From: Dion Dokter Date: Thu, 1 May 2025 11:28:57 +0200 Subject: [PATCH 37/97] Fix f7 boot example --- examples/boot/application/stm32f7/Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/boot/application/stm32f7/Cargo.toml b/examples/boot/application/stm32f7/Cargo.toml index 6a5a500de..d62c67742 100644 --- a/examples/boot/application/stm32f7/Cargo.toml +++ b/examples/boot/application/stm32f7/Cargo.toml @@ -8,7 +8,7 @@ license = "MIT OR Apache-2.0" embassy-sync = { version = "0.6.2", path = "../../../../embassy-sync" } embassy-executor = { version = "0.7.0", path = "../../../../embassy-executor", features = ["arch-cortex-m", "executor-thread"] } embassy-time = { version = "0.4.0", path = "../../../../embassy-time", features = [ "tick-hz-32_768"] } -embassy-stm32 = { version = "0.2.0", path = "../../../../embassy-stm32", features = ["stm32f767zi", "time-driver-any", "exti"] } +embassy-stm32 = { version = "0.2.0", path = "../../../../embassy-stm32", features = ["stm32f767zi", "time-driver-any", "exti", "single-bank"] } embassy-boot-stm32 = { version = "0.2.0", path = "../../../../embassy-boot-stm32", features = [] } embassy-embedded-hal = { version = "0.3.0", path = "../../../../embassy-embedded-hal" } From cff6eb9a81a34f7e7437f30470c0914177ca3a27 Mon Sep 17 00:00:00 2001 From: Dion Dokter Date: Thu, 1 May 2025 11:52:22 +0200 Subject: [PATCH 38/97] Fix more CI --- ci.sh | 25 +++++++++++++------------ 1 file changed, 13 insertions(+), 12 deletions(-) diff --git a/ci.sh b/ci.sh index 5a438f0b1..6e320e4d1 100755 --- a/ci.sh +++ b/ci.sh @@ -88,16 +88,17 @@ cargo batch \ --- 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,defmt,exti,time-driver-any,time \ - --- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv8m.main-none-eabihf --features stm32l552ze,defmt,time-driver-any,time \ - --- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv8m.main-none-eabihf --features stm32l552ze,defmt,exti,time \ - --- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv8m.main-none-eabihf --features stm32l552ze,defmt,time \ - --- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv8m.main-none-eabihf --features stm32l552ze,defmt,exti,time-driver-any,time \ - --- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv8m.main-none-eabihf --features stm32l552ze,defmt,time-driver-any,time \ - --- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv8m.main-none-eabihf --features stm32l552ze,defmt,exti,time \ - --- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv8m.main-none-eabihf --features stm32l552ze,defmt,time \ - --- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv8m.main-none-eabihf --features stm32l552ze,defmt,exti \ - --- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv8m.main-none-eabihf --features stm32l552ze,defmt \ + --- 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 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 \ @@ -153,10 +154,10 @@ cargo batch \ --- 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 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,defmt,exti,time-driver-any,low-power,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 stm32g474pe,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 \ From 59d839a2b8a488bf1e9056f46ba1dff61148642b Mon Sep 17 00:00:00 2001 From: Dion Dokter Date: Thu, 1 May 2025 13:37:12 +0200 Subject: [PATCH 39/97] Fix dual bank support for F7 --- .github/ci/test.sh | 8 +++-- embassy-stm32/src/flash/f7.rs | 57 +++++++++++++++++++++++++++++++++-- 2 files changed, 60 insertions(+), 5 deletions(-) diff --git a/.github/ci/test.sh b/.github/ci/test.sh index 88962a533..c9b332cf8 100755 --- a/.github/ci/test.sh +++ b/.github/ci/test.sh @@ -29,8 +29,10 @@ cargo test --manifest-path ./embassy-nrf/Cargo.toml --no-default-features --feat cargo test --manifest-path ./embassy-rp/Cargo.toml --no-default-features --features time-driver,rp2040,_test cargo test --manifest-path ./embassy-rp/Cargo.toml --no-default-features --features time-driver,rp235xa,_test -cargo test --manifest-path ./embassy-stm32/Cargo.toml --no-default-features --features stm32f429vg,exti,time-driver-any,exti,dual-bank -cargo test --manifest-path ./embassy-stm32/Cargo.toml --no-default-features --features stm32f732ze,exti,time-driver-any,exti -cargo test --manifest-path ./embassy-stm32/Cargo.toml --no-default-features --features stm32f769ni,exti,time-driver-any,exti,single-bank +cargo test --manifest-path ./embassy-stm32/Cargo.toml --no-default-features --features stm32f429vg,time-driver-any,exti,single-bank +cargo test --manifest-path ./embassy-stm32/Cargo.toml --no-default-features --features stm32f429vg,time-driver-any,exti,dual-bank +cargo test --manifest-path ./embassy-stm32/Cargo.toml --no-default-features --features stm32f732ze,time-driver-any,exti +cargo test --manifest-path ./embassy-stm32/Cargo.toml --no-default-features --features stm32f769ni,time-driver-any,exti,single-bank +cargo test --manifest-path ./embassy-stm32/Cargo.toml --no-default-features --features stm32f769ni,time-driver-any,exti,dual-bank cargo test --manifest-path ./embassy-net-adin1110/Cargo.toml diff --git a/embassy-stm32/src/flash/f7.rs b/embassy-stm32/src/flash/f7.rs index 043382590..27a7afadf 100644 --- a/embassy-stm32/src/flash/f7.rs +++ b/embassy-stm32/src/flash/f7.rs @@ -5,6 +5,12 @@ use super::{FlashRegion, FlashSector, FLASH_REGIONS, WRITE_SIZE}; use crate::flash::Error; use crate::pac; +impl FlashSector { + const fn snb(&self) -> u8 { + ((self.bank as u8) << 4) + self.index_in_bank + } +} + pub(crate) const fn is_default_layout() -> bool { true } @@ -53,7 +59,7 @@ pub(crate) unsafe fn blocking_write(start_address: u32, buf: &[u8; WRITE_SIZE]) pub(crate) unsafe fn blocking_erase_sector(sector: &FlashSector) -> Result<(), Error> { pac::FLASH.cr().modify(|w| { w.set_ser(true); - w.set_snb(sector.index_in_bank) + w.set_snb(sector.snb()) }); pac::FLASH.cr().modify(|w| { @@ -137,7 +143,7 @@ mod tests { } #[test] - #[cfg(stm32f769)] + #[cfg(all(stm32f769, feature = "single-bank"))] fn can_get_sector() { const SMALL_SECTOR_SIZE: u32 = 32 * 1024; const MEDIUM_SECTOR_SIZE: u32 = 128 * 1024; @@ -168,6 +174,53 @@ mod tests { assert_sector(7, 0x080C_0000, LARGE_SECTOR_SIZE, 0x080C_0000); assert_sector(7, 0x080C_0000, LARGE_SECTOR_SIZE, 0x080F_FFFF); } + + #[test] + #[cfg(all(stm32f769, feature = "dual-bank"))] + fn can_get_sector() { + const SMALL_SECTOR_SIZE: u32 = 16 * 1024; + const MEDIUM_SECTOR_SIZE: u32 = 64 * 1024; + const LARGE_SECTOR_SIZE: u32 = 128 * 1024; + + let assert_sector = |index_in_bank: u8, start: u32, size: u32, address: u32, snb: u8, bank: FlashBank| { + assert_eq!( + FlashSector { + bank: bank, + index_in_bank, + start, + size + }, + get_sector(address, &FLASH_REGIONS) + ); + assert_eq!(get_sector(address, &FLASH_REGIONS).snb(), snb); + }; + + assert_sector(0, 0x0800_0000, SMALL_SECTOR_SIZE, 0x0800_0000, 0x00, FlashBank::Bank1); + assert_sector(0, 0x0800_0000, SMALL_SECTOR_SIZE, 0x0800_3FFF, 0x00, FlashBank::Bank1); + assert_sector(3, 0x0800_C000, SMALL_SECTOR_SIZE, 0x0800_C000, 0x03, FlashBank::Bank1); + assert_sector(3, 0x0800_C000, SMALL_SECTOR_SIZE, 0x0800_FFFF, 0x03, FlashBank::Bank1); + + assert_sector(4, 0x0801_0000, MEDIUM_SECTOR_SIZE, 0x0801_0000, 0x04, FlashBank::Bank1); + assert_sector(4, 0x0801_0000, MEDIUM_SECTOR_SIZE, 0x0801_FFFF, 0x04, FlashBank::Bank1); + + assert_sector(5, 0x0802_0000, LARGE_SECTOR_SIZE, 0x0802_0000, 0x05, FlashBank::Bank1); + assert_sector(5, 0x0802_0000, LARGE_SECTOR_SIZE, 0x0803_FFFF, 0x05, FlashBank::Bank1); + assert_sector(10, 0x080C_0000, LARGE_SECTOR_SIZE, 0x080C_0000, 0x0A, FlashBank::Bank1); + assert_sector(10, 0x080C_0000, LARGE_SECTOR_SIZE, 0x080D_FFFF, 0x0A, FlashBank::Bank1); + + assert_sector(0, 0x0810_0000, SMALL_SECTOR_SIZE, 0x0810_0000, 0x10, FlashBank::Bank2); + assert_sector(0, 0x0810_0000, SMALL_SECTOR_SIZE, 0x0810_3FFF, 0x10, FlashBank::Bank2); + assert_sector(3, 0x0810_C000, SMALL_SECTOR_SIZE, 0x0810_C000, 0x13, FlashBank::Bank2); + assert_sector(3, 0x0810_C000, SMALL_SECTOR_SIZE, 0x0810_FFFF, 0x13, FlashBank::Bank2); + + assert_sector(4, 0x0811_0000, MEDIUM_SECTOR_SIZE, 0x0811_0000, 0x14, FlashBank::Bank2); + assert_sector(4, 0x0811_0000, MEDIUM_SECTOR_SIZE, 0x0811_FFFF, 0x14, FlashBank::Bank2); + + assert_sector(5, 0x0812_0000, LARGE_SECTOR_SIZE, 0x0812_0000, 0x15, FlashBank::Bank2); + assert_sector(5, 0x0812_0000, LARGE_SECTOR_SIZE, 0x0813_FFFF, 0x15, FlashBank::Bank2); + assert_sector(10, 0x081C_0000, LARGE_SECTOR_SIZE, 0x081C_0000, 0x1A, FlashBank::Bank2); + assert_sector(10, 0x081C_0000, LARGE_SECTOR_SIZE, 0x081D_FFFF, 0x1A, FlashBank::Bank2); + } } #[cfg(all(bank_setup_configurable))] From e478bdf1df1c41cee0fd0aad29a66db4cf6f93f5 Mon Sep 17 00:00:00 2001 From: Dion Dokter Date: Thu, 1 May 2025 13:49:29 +0200 Subject: [PATCH 40/97] Remove all notion of 'default' and 'alt' flash layouts. Now there's just the one layout. --- embassy-stm32/src/flash/asynch.rs | 7 +++---- embassy-stm32/src/flash/common.rs | 7 +++---- embassy-stm32/src/flash/f0.rs | 10 +--------- embassy-stm32/src/flash/f1f3.rs | 10 +--------- embassy-stm32/src/flash/f2.rs | 10 +--------- embassy-stm32/src/flash/f4.rs | 14 +++----------- embassy-stm32/src/flash/f7.rs | 18 +++++------------- embassy-stm32/src/flash/g.rs | 10 +--------- embassy-stm32/src/flash/h5.rs | 14 +------------- embassy-stm32/src/flash/h50.rs | 10 +--------- embassy-stm32/src/flash/h7.rs | 10 +--------- embassy-stm32/src/flash/l.rs | 10 +--------- embassy-stm32/src/flash/mod.rs | 10 +--------- embassy-stm32/src/flash/other.rs | 10 +--------- embassy-stm32/src/flash/u0.rs | 10 +--------- embassy-stm32/src/flash/u5.rs | 10 +--------- 16 files changed, 26 insertions(+), 144 deletions(-) diff --git a/embassy-stm32/src/flash/asynch.rs b/embassy-stm32/src/flash/asynch.rs index 599b7bb4e..006dcddeb 100644 --- a/embassy-stm32/src/flash/asynch.rs +++ b/embassy-stm32/src/flash/asynch.rs @@ -6,8 +6,8 @@ use embassy_sync::blocking_mutex::raw::CriticalSectionRawMutex; use embassy_sync::mutex::Mutex; use super::{ - blocking_read, ensure_sector_aligned, family, get_sector, Async, Error, Flash, FlashLayout, FLASH_BASE, FLASH_SIZE, - WRITE_SIZE, + blocking_read, ensure_sector_aligned, family, get_flash_regions, get_sector, Async, Error, Flash, FlashLayout, + FLASH_BASE, FLASH_SIZE, WRITE_SIZE, }; use crate::interrupt::InterruptExt; use crate::peripherals::FLASH; @@ -34,7 +34,6 @@ impl<'d> Flash<'d, Async> { /// /// See module-level documentation for details on how memory regions work. pub fn into_regions(self) -> FlashLayout<'d, Async> { - assert!(family::is_default_layout()); FlashLayout::new(self.inner) } @@ -123,7 +122,7 @@ pub(super) async unsafe fn write_chunked(base: u32, size: u32, offset: u32, byte pub(super) async unsafe fn erase_sectored(base: u32, from: u32, to: u32) -> Result<(), Error> { let start_address = base + from; let end_address = base + to; - let regions = family::get_flash_regions(); + let regions = get_flash_regions(); ensure_sector_aligned(start_address, end_address, regions)?; diff --git a/embassy-stm32/src/flash/common.rs b/embassy-stm32/src/flash/common.rs index 93d734b20..10023e637 100644 --- a/embassy-stm32/src/flash/common.rs +++ b/embassy-stm32/src/flash/common.rs @@ -4,8 +4,8 @@ use core::sync::atomic::{fence, Ordering}; use embassy_hal_internal::drop::OnDrop; use super::{ - family, Async, Blocking, Error, FlashBank, FlashLayout, FlashRegion, FlashSector, FLASH_SIZE, MAX_ERASE_SIZE, - READ_SIZE, WRITE_SIZE, + family, get_flash_regions, Async, Blocking, Error, FlashBank, FlashLayout, FlashRegion, FlashSector, FLASH_SIZE, + MAX_ERASE_SIZE, READ_SIZE, WRITE_SIZE, }; use crate::Peri; use crate::_generated::FLASH_BASE; @@ -36,7 +36,6 @@ impl<'d, MODE> Flash<'d, MODE> { /// /// See module-level documentation for details on how memory regions work. pub fn into_blocking_regions(self) -> FlashLayout<'d, Blocking> { - assert!(family::is_default_layout()); FlashLayout::new(self.inner) } @@ -141,7 +140,7 @@ pub(super) unsafe fn blocking_erase( ) -> Result<(), Error> { let start_address = base + from; let end_address = base + to; - let regions = family::get_flash_regions(); + let regions = get_flash_regions(); ensure_sector_aligned(start_address, end_address, regions)?; diff --git a/embassy-stm32/src/flash/f0.rs b/embassy-stm32/src/flash/f0.rs index 402312f68..3f9dbe945 100644 --- a/embassy-stm32/src/flash/f0.rs +++ b/embassy-stm32/src/flash/f0.rs @@ -1,18 +1,10 @@ use core::ptr::write_volatile; use core::sync::atomic::{fence, Ordering}; -use super::{FlashRegion, FlashSector, FLASH_REGIONS, WRITE_SIZE}; +use super::{FlashSector, WRITE_SIZE}; use crate::flash::Error; use crate::pac; -pub(crate) const fn is_default_layout() -> bool { - true -} - -pub(crate) const fn get_flash_regions() -> &'static [&'static FlashRegion] { - &FLASH_REGIONS -} - pub(crate) unsafe fn lock() { pac::FLASH.cr().modify(|w| w.set_lock(true)); } diff --git a/embassy-stm32/src/flash/f1f3.rs b/embassy-stm32/src/flash/f1f3.rs index ec237b9ff..bf9ad2893 100644 --- a/embassy-stm32/src/flash/f1f3.rs +++ b/embassy-stm32/src/flash/f1f3.rs @@ -1,18 +1,10 @@ use core::ptr::write_volatile; use core::sync::atomic::{fence, Ordering}; -use super::{FlashRegion, FlashSector, FLASH_REGIONS, WRITE_SIZE}; +use super::{FlashSector, WRITE_SIZE}; use crate::flash::Error; use crate::pac; -pub(crate) const fn is_default_layout() -> bool { - true -} - -pub(crate) const fn get_flash_regions() -> &'static [&'static FlashRegion] { - &FLASH_REGIONS -} - pub(crate) unsafe fn lock() { pac::FLASH.cr().modify(|w| w.set_lock(true)); } diff --git a/embassy-stm32/src/flash/f2.rs b/embassy-stm32/src/flash/f2.rs index cdab1fd2d..67e380619 100644 --- a/embassy-stm32/src/flash/f2.rs +++ b/embassy-stm32/src/flash/f2.rs @@ -3,7 +3,7 @@ use core::sync::atomic::{fence, AtomicBool, Ordering}; use pac::flash::regs::Sr; -use super::{FlashBank, FlashRegion, FlashSector, FLASH_REGIONS, WRITE_SIZE}; +use super::{get_flash_regions, FlashBank, FlashSector, WRITE_SIZE}; use crate::flash::Error; use crate::pac; @@ -15,14 +15,6 @@ impl FlashSector { } } -pub(crate) const fn is_default_layout() -> bool { - true -} - -pub(crate) const fn get_flash_regions() -> &'static [&'static FlashRegion] { - &FLASH_REGIONS -} - pub(crate) unsafe fn lock() { pac::FLASH.cr().modify(|w| w.set_lock(true)); } diff --git a/embassy-stm32/src/flash/f4.rs b/embassy-stm32/src/flash/f4.rs index 7d789a6bb..62e0492b5 100644 --- a/embassy-stm32/src/flash/f4.rs +++ b/embassy-stm32/src/flash/f4.rs @@ -4,7 +4,7 @@ use core::sync::atomic::{fence, AtomicBool, Ordering}; use embassy_sync::waitqueue::AtomicWaker; use pac::flash::regs::Sr; -use super::{FlashBank, FlashRegion, FlashSector, FLASH_REGIONS, WRITE_SIZE}; +use super::{get_flash_regions, FlashBank, FlashSector, WRITE_SIZE}; use crate::_generated::FLASH_SIZE; use crate::flash::Error; use crate::pac; @@ -18,14 +18,6 @@ impl FlashSector { } } -pub(crate) const fn is_default_layout() -> bool { - true -} - -pub const fn get_flash_regions() -> &'static [&'static FlashRegion] { - &FLASH_REGIONS -} - pub(crate) unsafe fn on_interrupt() { // Clear IRQ flags pac::FLASH.sr().write(|w| { @@ -306,7 +298,7 @@ mod tests { if !cfg!(feature = "dual-bank") { let assert_sector = |snb: u8, index_in_bank: u8, start: u32, size: u32, address: u32| { - let sector = get_sector(address, &FLASH_REGIONS); + let sector = get_sector(address, crate::flash::get_flash_regions()); assert_eq!(snb, sector.snb()); assert_eq!( FlashSector { @@ -333,7 +325,7 @@ mod tests { assert_sector(0x0B, 11, 0x080E_0000, LARGE_SECTOR_SIZE, 0x080F_FFFF); } else { let assert_sector = |snb: u8, bank: FlashBank, index_in_bank: u8, start: u32, size: u32, address: u32| { - let sector = get_sector(address, &FLASH_REGIONS); + let sector = get_sector(address, crate::flash::get_flash_regions()); assert_eq!(snb, sector.snb()); assert_eq!( FlashSector { diff --git a/embassy-stm32/src/flash/f7.rs b/embassy-stm32/src/flash/f7.rs index 27a7afadf..0547c747a 100644 --- a/embassy-stm32/src/flash/f7.rs +++ b/embassy-stm32/src/flash/f7.rs @@ -1,7 +1,7 @@ use core::ptr::write_volatile; use core::sync::atomic::{fence, Ordering}; -use super::{FlashRegion, FlashSector, FLASH_REGIONS, WRITE_SIZE}; +use super::{FlashSector, WRITE_SIZE}; use crate::flash::Error; use crate::pac; @@ -11,14 +11,6 @@ impl FlashSector { } } -pub(crate) const fn is_default_layout() -> bool { - true -} - -pub(crate) const fn get_flash_regions() -> &'static [&'static FlashRegion] { - &FLASH_REGIONS -} - pub(crate) unsafe fn lock() { pac::FLASH.cr().modify(|w| w.set_lock(true)); } @@ -124,7 +116,7 @@ mod tests { start, size }, - get_sector(address, &FLASH_REGIONS) + get_sector(address, crate::flash::get_flash_regions()) ) }; @@ -157,7 +149,7 @@ mod tests { start, size }, - get_sector(address, &FLASH_REGIONS) + get_sector(address, crate::flash::get_flash_regions()) ) }; @@ -190,9 +182,9 @@ mod tests { start, size }, - get_sector(address, &FLASH_REGIONS) + get_sector(address, crate::flash::get_flash_regions()) ); - assert_eq!(get_sector(address, &FLASH_REGIONS).snb(), snb); + assert_eq!(get_sector(address, crate::flash::get_flash_regions()).snb(), snb); }; assert_sector(0, 0x0800_0000, SMALL_SECTOR_SIZE, 0x0800_0000, 0x00, FlashBank::Bank1); diff --git a/embassy-stm32/src/flash/g.rs b/embassy-stm32/src/flash/g.rs index 70e472dcf..bc1fd360c 100644 --- a/embassy-stm32/src/flash/g.rs +++ b/embassy-stm32/src/flash/g.rs @@ -3,18 +3,10 @@ use core::sync::atomic::{fence, Ordering}; use cortex_m::interrupt; -use super::{FlashRegion, FlashSector, FLASH_REGIONS, WRITE_SIZE}; +use super::{FlashSector, WRITE_SIZE}; use crate::flash::Error; use crate::pac; -pub(crate) const fn is_default_layout() -> bool { - true -} - -pub(crate) const fn get_flash_regions() -> &'static [&'static FlashRegion] { - &FLASH_REGIONS -} - pub(crate) unsafe fn lock() { pac::FLASH.cr().modify(|w| w.set_lock(true)); } diff --git a/embassy-stm32/src/flash/h5.rs b/embassy-stm32/src/flash/h5.rs index d95de2e38..fd9bfcc75 100644 --- a/embassy-stm32/src/flash/h5.rs +++ b/embassy-stm32/src/flash/h5.rs @@ -1,22 +1,10 @@ use core::ptr::write_volatile; use core::sync::atomic::{fence, Ordering}; -use super::{FlashRegion, FlashSector, FLASH_REGIONS, WRITE_SIZE}; +use super::{FlashSector, WRITE_SIZE}; use crate::flash::Error; use crate::pac; -pub(crate) const fn is_default_layout() -> bool { - true -} - -// const fn is_dual_bank() -> bool { -// FLASH_REGIONS.len() >= 2 -// } - -pub(crate) fn get_flash_regions() -> &'static [&'static FlashRegion] { - &FLASH_REGIONS -} - pub(crate) unsafe fn lock() { if !pac::FLASH.nscr().read().lock() { pac::FLASH.nscr().modify(|r| { diff --git a/embassy-stm32/src/flash/h50.rs b/embassy-stm32/src/flash/h50.rs index 74cd6cc03..f8e210556 100644 --- a/embassy-stm32/src/flash/h50.rs +++ b/embassy-stm32/src/flash/h50.rs @@ -8,17 +8,9 @@ use cortex_m::interrupt; use pac::flash::regs::Nssr; use pac::flash::vals::Bksel; -use super::{Error, FlashBank, FlashRegion, FlashSector, FLASH_REGIONS, WRITE_SIZE}; +use super::{Error, FlashBank, FlashSector, WRITE_SIZE}; use crate::pac; -pub(crate) const fn is_default_layout() -> bool { - true -} - -pub(crate) const fn get_flash_regions() -> &'static [&'static FlashRegion] { - &FLASH_REGIONS -} - pub(crate) unsafe fn lock() { pac::FLASH.nscr().modify(|w| w.set_lock(true)); } diff --git a/embassy-stm32/src/flash/h7.rs b/embassy-stm32/src/flash/h7.rs index 254915381..f1d84101c 100644 --- a/embassy-stm32/src/flash/h7.rs +++ b/embassy-stm32/src/flash/h7.rs @@ -1,22 +1,14 @@ use core::ptr::write_volatile; use core::sync::atomic::{fence, Ordering}; -use super::{FlashRegion, FlashSector, BANK1_REGION, FLASH_REGIONS, WRITE_SIZE}; +use super::{FlashSector, BANK1_REGION, FLASH_REGIONS, WRITE_SIZE}; use crate::flash::Error; use crate::pac; -pub(crate) const fn is_default_layout() -> bool { - true -} - const fn is_dual_bank() -> bool { FLASH_REGIONS.len() >= 2 } -pub(crate) fn get_flash_regions() -> &'static [&'static FlashRegion] { - &FLASH_REGIONS -} - pub(crate) unsafe fn lock() { pac::FLASH.bank(0).cr().modify(|w| w.set_lock(true)); if is_dual_bank() { diff --git a/embassy-stm32/src/flash/l.rs b/embassy-stm32/src/flash/l.rs index 3b62fa2ee..65cea005c 100644 --- a/embassy-stm32/src/flash/l.rs +++ b/embassy-stm32/src/flash/l.rs @@ -1,18 +1,10 @@ use core::ptr::write_volatile; use core::sync::atomic::{fence, Ordering}; -use super::{FlashRegion, FlashSector, FLASH_REGIONS, WRITE_SIZE}; +use super::{FlashSector, WRITE_SIZE}; use crate::flash::Error; use crate::pac; -pub(crate) const fn is_default_layout() -> bool { - true -} - -pub(crate) const fn get_flash_regions() -> &'static [&'static FlashRegion] { - &FLASH_REGIONS -} - pub(crate) unsafe fn lock() { #[cfg(any(flash_wl, flash_wb, flash_l4))] pac::FLASH.cr().modify(|w| w.set_lock(true)); diff --git a/embassy-stm32/src/flash/mod.rs b/embassy-stm32/src/flash/mod.rs index c7488c8ef..adc45db9c 100644 --- a/embassy-stm32/src/flash/mod.rs +++ b/embassy-stm32/src/flash/mod.rs @@ -14,17 +14,9 @@ pub use common::*; pub use crate::_generated::flash_regions::*; pub use crate::_generated::{FLASH_BASE, FLASH_SIZE, MAX_ERASE_SIZE, WRITE_SIZE}; -/// Get whether the default flash layout is being used. -/// -/// In some chips, dual-bank is not default. This will then return `false` -/// when dual-bank is enabled. -pub fn is_default_layout() -> bool { - family::is_default_layout() -} - /// Get all flash regions. pub fn get_flash_regions() -> &'static [&'static FlashRegion] { - family::get_flash_regions() + &FLASH_REGIONS } /// Read size (always 1) diff --git a/embassy-stm32/src/flash/other.rs b/embassy-stm32/src/flash/other.rs index 20f84a72f..293a79be3 100644 --- a/embassy-stm32/src/flash/other.rs +++ b/embassy-stm32/src/flash/other.rs @@ -1,14 +1,6 @@ #![allow(unused)] -use super::{Error, FlashRegion, FlashSector, FLASH_REGIONS, WRITE_SIZE}; - -pub(crate) const fn is_default_layout() -> bool { - true -} - -pub(crate) const fn get_flash_regions() -> &'static [&'static FlashRegion] { - &FLASH_REGIONS -} +use super::{Error, FlashSector, WRITE_SIZE}; pub(crate) unsafe fn lock() { unimplemented!(); diff --git a/embassy-stm32/src/flash/u0.rs b/embassy-stm32/src/flash/u0.rs index bfdbd15a5..68d847eca 100644 --- a/embassy-stm32/src/flash/u0.rs +++ b/embassy-stm32/src/flash/u0.rs @@ -3,18 +3,10 @@ use core::sync::atomic::{fence, Ordering}; use cortex_m::interrupt; -use super::{FlashRegion, FlashSector, FLASH_REGIONS, WRITE_SIZE}; +use super::{FlashSector, WRITE_SIZE}; use crate::flash::Error; use crate::pac; -pub(crate) const fn is_default_layout() -> bool { - true -} - -pub(crate) const fn get_flash_regions() -> &'static [&'static FlashRegion] { - &FLASH_REGIONS -} - pub(crate) unsafe fn lock() { pac::FLASH.cr().modify(|w| w.set_lock(true)); } diff --git a/embassy-stm32/src/flash/u5.rs b/embassy-stm32/src/flash/u5.rs index dad698316..131caa195 100644 --- a/embassy-stm32/src/flash/u5.rs +++ b/embassy-stm32/src/flash/u5.rs @@ -1,18 +1,10 @@ use core::ptr::write_volatile; use core::sync::atomic::{fence, Ordering}; -use super::{FlashBank, FlashRegion, FlashSector, FLASH_REGIONS, WRITE_SIZE}; +use super::{FlashBank, FlashSector, WRITE_SIZE}; use crate::flash::Error; use crate::pac; -pub(crate) const fn is_default_layout() -> bool { - true -} - -pub(crate) const fn get_flash_regions() -> &'static [&'static FlashRegion] { - &FLASH_REGIONS -} - pub(crate) unsafe fn lock() { #[cfg(feature = "trustzone-secure")] pac::FLASH.seccr().modify(|w| w.set_lock(true)); From df84dfec7a55bed44ad5609511e4a2f596a156af Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Thu, 1 May 2025 14:35:46 +0200 Subject: [PATCH 41/97] Remove embassy_nrf::radio::ble. Fixes #4144 --- embassy-nrf/src/radio/ble.rs | 394 ---------------------------- embassy-nrf/src/radio/ieee802154.rs | 7 +- embassy-nrf/src/radio/mod.rs | 8 +- 3 files changed, 5 insertions(+), 404 deletions(-) delete mode 100644 embassy-nrf/src/radio/ble.rs diff --git a/embassy-nrf/src/radio/ble.rs b/embassy-nrf/src/radio/ble.rs deleted file mode 100644 index d42bbe5f6..000000000 --- a/embassy-nrf/src/radio/ble.rs +++ /dev/null @@ -1,394 +0,0 @@ -//! Radio driver implementation focused on Bluetooth Low-Energy transmission. - -use core::future::poll_fn; -use core::sync::atomic::{compiler_fence, Ordering}; -use core::task::Poll; - -use embassy_hal_internal::drop::OnDrop; -pub use pac::radio::vals::Mode; -#[cfg(not(feature = "_nrf51"))] -use pac::radio::vals::Plen as PreambleLength; - -use crate::interrupt::typelevel::Interrupt; -use crate::pac::radio::vals; -use crate::radio::*; -pub use crate::radio::{Error, TxPower}; -use crate::util::slice_in_ram_or; -use crate::Peri; - -/// Radio driver. -pub struct Radio<'d, T: Instance> { - _p: Peri<'d, T>, -} - -impl<'d, T: Instance> Radio<'d, T> { - /// Create a new radio driver. - pub fn new( - radio: Peri<'d, T>, - _irq: impl interrupt::typelevel::Binding> + 'd, - ) -> Self { - let r = T::regs(); - - r.pcnf1().write(|w| { - // It is 0 bytes long in a standard BLE packet - w.set_statlen(0); - // MaxLen configures the maximum packet payload plus add-on size in - // number of bytes that can be transmitted or received by the RADIO. This feature can be used to ensure - // that the RADIO does not overwrite, or read beyond, the RAM assigned to the packet payload. This means - // that if the packet payload length defined by PCNF1.STATLEN and the LENGTH field in the packet specifies a - // packet larger than MAXLEN, the payload will be truncated at MAXLEN - // - // To simplify the implementation, It is setted as the maximum value - // and the length of the packet is controlled only by the LENGTH field in the packet - w.set_maxlen(255); - // Configure the length of the address field in the packet - // The prefix after the address fields is always appended, so is always 1 byte less than the size of the address - // The base address is truncated from the least significant byte if the BALEN is less than 4 - // - // BLE address is always 4 bytes long - w.set_balen(3); // 3 bytes base address (+ 1 prefix); - // Configure the endianess - // For BLE is always little endian (LSB first) - w.set_endian(vals::Endian::LITTLE); - // Data whitening is used to avoid long sequences of zeros or - // ones, e.g., 0b0000000 or 0b1111111, in the data bit stream. - // The whitener and de-whitener are defined the same way, - // using a 7-bit linear feedback shift register with the - // polynomial x7 + x4 + 1. - // - // In BLE Whitening shall be applied on the PDU and CRC of all - // Link Layer packets and is performed after the CRC generation - // in the transmitter. No other parts of the packets are whitened. - // De-whitening is performed before the CRC checking in the receiver - // Before whitening or de-whitening, the shift register should be - // initialized based on the channel index. - w.set_whiteen(true); - }); - - // Configure CRC - r.crccnf().write(|w| { - // In BLE the CRC shall be calculated on the PDU of all Link Layer - // packets (even if the packet is encrypted). - // It skips the address field - w.set_skipaddr(vals::Skipaddr::SKIP); - // In BLE 24-bit CRC = 3 bytes - w.set_len(vals::Len::THREE); - }); - - // Ch map between 2400 MHZ .. 2500 MHz - // All modes use this range - #[cfg(not(feature = "_nrf51"))] - r.frequency().write(|w| w.set_map(vals::Map::DEFAULT)); - - // Configure shortcuts to simplify and speed up sending and receiving packets. - r.shorts().write(|w| { - // start transmission/recv immediately after ramp-up - // disable radio when transmission/recv is done - w.set_ready_start(true); - w.set_end_disable(true); - }); - - // Enable NVIC interrupt - T::Interrupt::unpend(); - unsafe { T::Interrupt::enable() }; - - Self { _p: radio } - } - - fn state(&self) -> RadioState { - super::state(T::regs()) - } - - /// Set the radio mode - /// - /// The radio must be disabled before calling this function - pub fn set_mode(&mut self, mode: Mode) { - assert!(self.state() == RadioState::DISABLED); - - let r = T::regs(); - r.mode().write(|w| w.set_mode(mode)); - - #[cfg(not(feature = "_nrf51"))] - r.pcnf0().write(|w| { - w.set_plen(match mode { - Mode::BLE_1MBIT => PreambleLength::_8BIT, - Mode::BLE_2MBIT => PreambleLength::_16BIT, - #[cfg(any( - feature = "nrf52811", - feature = "nrf52820", - feature = "nrf52833", - feature = "nrf52840", - feature = "_nrf5340-net" - ))] - Mode::BLE_LR125KBIT | Mode::BLE_LR500KBIT => PreambleLength::LONG_RANGE, - _ => unimplemented!(), - }) - }); - } - - /// Set the header size changing the S1's len field - /// - /// The radio must be disabled before calling this function - pub fn set_header_expansion(&mut self, use_s1_field: bool) { - assert!(self.state() == RadioState::DISABLED); - - let r = T::regs(); - - // s1 len in bits - let s1len: u8 = match use_s1_field { - false => 0, - true => 8, - }; - - r.pcnf0().write(|w| { - // Configure S0 to 1 byte length, this will represent the Data/Adv header flags - w.set_s0len(true); - // Configure the length (in bits) field to 1 byte length, this will represent the length of the payload - // and also be used to know how many bytes to read/write from/to the buffer - w.set_lflen(0); - // Configure the lengh (in bits) of bits in the S1 field. It could be used to represent the CTEInfo for data packages in BLE. - w.set_s1len(s1len); - }); - } - - /// Set initial data whitening value - /// Data whitening is used to avoid long sequences of zeros or ones, e.g., 0b0000000 or 0b1111111, in the data bit stream - /// On BLE the initial value is the channel index | 0x40 - /// - /// The radio must be disabled before calling this function - pub fn set_whitening_init(&mut self, whitening_init: u8) { - assert!(self.state() == RadioState::DISABLED); - - let r = T::regs(); - - r.datawhiteiv().write(|w| w.set_datawhiteiv(whitening_init)); - } - - /// Set the central frequency to be used - /// It should be in the range 2400..2500 - /// - /// [The radio must be disabled before calling this function](https://devzone.nordicsemi.com/f/nordic-q-a/15829/radio-frequency-change) - pub fn set_frequency(&mut self, frequency: u32) { - assert!(self.state() == RadioState::DISABLED); - assert!((2400..=2500).contains(&frequency)); - - let r = T::regs(); - - r.frequency().write(|w| w.set_frequency((frequency - 2400) as u8)); - } - - /// Set the acess address - /// This address is always constants for advertising - /// And a random value generate on each connection - /// It is used to filter the packages - /// - /// The radio must be disabled before calling this function - pub fn set_access_address(&mut self, access_address: u32) { - assert!(self.state() == RadioState::DISABLED); - - let r = T::regs(); - - // Configure logical address - // The byte ordering on air is always least significant byte first for the address - // So for the address 0xAA_BB_CC_DD, the address on air will be DD CC BB AA - // The package order is BASE, PREFIX so BASE=0xBB_CC_DD and PREFIX=0xAA - r.prefix0().write(|w| w.set_ap0((access_address >> 24) as u8)); - - // The base address is truncated from the least significant byte (because the BALEN is less than 4) - // So it shifts the address to the right - r.base0().write_value(access_address << 8); - - // Don't match tx address - r.txaddress().write(|w| w.set_txaddress(0)); - - // Match on logical address - // This config only filter the packets by the address, - // so only packages send to the previous address - // will finish the reception (TODO: check the explanation) - r.rxaddresses().write(|w| { - w.set_addr0(true); - w.set_addr1(true); - w.set_addr2(true); - w.set_addr3(true); - w.set_addr4(true); - }); - } - - /// Set the CRC polynomial - /// It only uses the 24 least significant bits - /// - /// The radio must be disabled before calling this function - pub fn set_crc_poly(&mut self, crc_poly: u32) { - assert!(self.state() == RadioState::DISABLED); - - let r = T::regs(); - - r.crcpoly().write(|w| { - // Configure the CRC polynomial - // Each term in the CRC polynomial is mapped to a bit in this - // register which index corresponds to the term's exponent. - // The least significant term/bit is hard-wired internally to - // 1, and bit number 0 of the register content is ignored by - // the hardware. The following example is for an 8 bit CRC - // polynomial: x8 + x7 + x3 + x2 + 1 = 1 1000 1101 . - w.set_crcpoly(crc_poly & 0xFFFFFF) - }); - } - - /// Set the CRC init value - /// It only uses the 24 least significant bits - /// The CRC initial value varies depending of the PDU type - /// - /// The radio must be disabled before calling this function - pub fn set_crc_init(&mut self, crc_init: u32) { - assert!(self.state() == RadioState::DISABLED); - - let r = T::regs(); - - r.crcinit().write(|w| w.set_crcinit(crc_init & 0xFFFFFF)); - } - - /// Set the radio tx power - /// - /// The radio must be disabled before calling this function - pub fn set_tx_power(&mut self, tx_power: TxPower) { - assert!(self.state() == RadioState::DISABLED); - - let r = T::regs(); - - r.txpower().write(|w| w.set_txpower(tx_power)); - } - - /// Set buffer to read/write - /// - /// This method is unsound. You should guarantee that the buffer will live - /// for the life time of the transmission or if the buffer will be modified. - /// Also if the buffer is smaller than the packet length, the radio will - /// read/write memory out of the buffer bounds. - fn set_buffer(&mut self, buffer: &[u8]) -> Result<(), Error> { - slice_in_ram_or(buffer, Error::BufferNotInRAM)?; - - let r = T::regs(); - - // Here it consider that the length of the packet is - // correctly set in the buffer, otherwise it will send - // unowned regions of memory - let ptr = buffer.as_ptr(); - - // Configure the payload - r.packetptr().write_value(ptr as u32); - - Ok(()) - } - - /// Send packet - /// If the length byte in the package is greater than the buffer length - /// the radio will read memory out of the buffer bounds - pub async fn transmit(&mut self, buffer: &[u8]) -> Result<(), Error> { - self.set_buffer(buffer)?; - - let r = T::regs(); - self.trigger_and_wait_end(move || { - // Initialize the transmission - // trace!("txen"); - - r.tasks_txen().write_value(1); - }) - .await; - - Ok(()) - } - - /// Receive packet - /// If the length byte in the received package is greater than the buffer length - /// the radio will write memory out of the buffer bounds - pub async fn receive(&mut self, buffer: &mut [u8]) -> Result<(), Error> { - self.set_buffer(buffer)?; - - let r = T::regs(); - self.trigger_and_wait_end(move || { - // Initialize the transmission - // trace!("rxen"); - r.tasks_rxen().write_value(1); - }) - .await; - - Ok(()) - } - - async fn trigger_and_wait_end(&mut self, trigger: impl FnOnce()) { - let r = T::regs(); - let s = T::state(); - - // If the Future is dropped before the end of the transmission - // it disable the interrupt and stop the transmission - // to keep the state consistent - let drop = OnDrop::new(|| { - trace!("radio drop: stopping"); - - r.intenclr().write(|w| w.set_end(true)); - - r.tasks_stop().write_value(1); - - r.events_end().write_value(0); - - trace!("radio drop: stopped"); - }); - - // trace!("radio:enable interrupt"); - // Clear some remnant side-effects (TODO: check if this is necessary) - r.events_end().write_value(0); - - // Enable interrupt - r.intenset().write(|w| w.set_end(true)); - - compiler_fence(Ordering::SeqCst); - - // Trigger the transmission - trigger(); - - // On poll check if interrupt happen - poll_fn(|cx| { - s.event_waker.register(cx.waker()); - if r.events_end().read() == 1 { - // trace!("radio:end"); - return core::task::Poll::Ready(()); - } - Poll::Pending - }) - .await; - - compiler_fence(Ordering::SeqCst); - r.events_end().write_value(0); // ACK - - // Everthing ends fine, so it disable the drop - drop.defuse(); - } - - /// Disable the radio - fn disable(&mut self) { - let r = T::regs(); - - compiler_fence(Ordering::SeqCst); - // If it is already disabled, do nothing - if self.state() != RadioState::DISABLED { - trace!("radio:disable"); - // Trigger the disable task - r.tasks_disable().write_value(1); - - // Wait until the radio is disabled - while r.events_disabled().read() == 0 {} - - compiler_fence(Ordering::SeqCst); - - // Acknowledge it - r.events_disabled().write_value(0); - } - } -} - -impl<'d, T: Instance> Drop for Radio<'d, T> { - fn drop(&mut self) { - self.disable(); - } -} diff --git a/embassy-nrf/src/radio/ieee802154.rs b/embassy-nrf/src/radio/ieee802154.rs index 2f0bcbe04..7f4f8f462 100644 --- a/embassy-nrf/src/radio/ieee802154.rs +++ b/embassy-nrf/src/radio/ieee802154.rs @@ -5,10 +5,11 @@ use core::task::Poll; use embassy_hal_internal::drop::OnDrop; -use super::{state, Error, Instance, InterruptHandler, RadioState, TxPower}; +use super::{Error, Instance, InterruptHandler, TxPower}; use crate::interrupt::typelevel::Interrupt; use crate::interrupt::{self}; use crate::pac::radio::vals; +pub use crate::pac::radio::vals::State as RadioState; use crate::Peri; /// Default (IEEE compliant) Start of Frame Delimiter @@ -200,7 +201,7 @@ impl<'d, T: Instance> Radio<'d, T> { /// Get the current radio state fn state(&self) -> RadioState { - state(T::regs()) + T::regs().state().read().state() } /// Moves the radio from any state to the DISABLED state @@ -293,7 +294,7 @@ impl<'d, T: Instance> Radio<'d, T> { r.shorts().write(|_| {}); r.tasks_stop().write_value(1); loop { - match state(r) { + match r.state().read().state() { RadioState::DISABLED | RadioState::RX_IDLE => break, _ => (), } diff --git a/embassy-nrf/src/radio/mod.rs b/embassy-nrf/src/radio/mod.rs index 982436266..608ef9024 100644 --- a/embassy-nrf/src/radio/mod.rs +++ b/embassy-nrf/src/radio/mod.rs @@ -6,7 +6,6 @@ #![macro_use] /// Bluetooth Low Energy Radio driver. -pub mod ble; #[cfg(any( feature = "nrf52811", feature = "nrf52820", @@ -21,7 +20,6 @@ use core::marker::PhantomData; use embassy_hal_internal::PeripheralType; use embassy_sync::waitqueue::AtomicWaker; -use pac::radio::vals::State as RadioState; pub use pac::radio::vals::Txpower as TxPower; use crate::{interrupt, pac}; @@ -82,6 +80,7 @@ macro_rules! impl_radio { pac::$pac_type } + #[allow(unused)] fn state() -> &'static crate::radio::State { static STATE: crate::radio::State = crate::radio::State::new(); &STATE @@ -99,8 +98,3 @@ pub trait Instance: SealedInstance + PeripheralType + 'static + Send { /// Interrupt for this peripheral. type Interrupt: interrupt::typelevel::Interrupt; } - -/// Get the state of the radio -pub(crate) fn state(radio: pac::radio::Radio) -> RadioState { - radio.state().read().state() -} From c01776a3d779caf5f43fda03e5506f569f1bf9ac Mon Sep 17 00:00:00 2001 From: 1-rafael-1 Date: Thu, 1 May 2025 17:07:48 +0200 Subject: [PATCH 42/97] - two more doc examples test ignored - added tests for the new calculations and fixed an overflow issue these tests surfaced. - Activate brownout detection. --- embassy-rp/src/clocks.rs | 222 +++++++++++++++++++++++++++++++++++++-- 1 file changed, 214 insertions(+), 8 deletions(-) diff --git a/embassy-rp/src/clocks.rs b/embassy-rp/src/clocks.rs index 86c172879..005564b8b 100644 --- a/embassy-rp/src/clocks.rs +++ b/embassy-rp/src/clocks.rs @@ -391,7 +391,7 @@ impl ClockConfig { /// /// # Example /// - /// ``` + /// ```rust,ignore /// // Overclock to 200MHz /// let config = ClockConfig::at_sys_frequency_mhz(200); /// ``` @@ -424,7 +424,7 @@ impl ClockConfig { /// /// # Example /// - /// ``` + /// ```rust,ignore /// // Use a non-standard 16MHz crystal to achieve 250MHz /// let config = ClockConfig::with_custom_crystal(16_000_000, 250_000_000); /// ``` @@ -875,7 +875,7 @@ pub struct RtcClkConfig { /// /// # Example /// -/// ``` +/// ```rust,ignore /// // Find parameters for 133MHz system clock from 12MHz crystal /// let pll_params = find_pll_params(12_000_000, 133_000_000).unwrap(); /// ``` @@ -885,7 +885,7 @@ fn find_pll_params(input_hz: u32, target_hz: u32) -> Option { const PLL_SYS_REFDIV: u8 = 1; // Calculate reference frequency - let reference_freq = input_hz / PLL_SYS_REFDIV as u32; + let reference_freq = input_hz as u64 / PLL_SYS_REFDIV as u64; // Start from highest fbdiv for better stability (like SDK does) for fbdiv in (16..=320).rev() { @@ -900,10 +900,10 @@ fn find_pll_params(input_hz: u32, target_hz: u32) -> Option { // (more conservative/stable approach) for post_div1 in (1..=7).rev() { for post_div2 in (1..=post_div1).rev() { - let out_freq = vco_freq / (post_div1 * post_div2) as u32; + let out_freq = vco_freq / (post_div1 * post_div2); // Check if we get the exact target frequency without remainder - if out_freq == target_hz && (vco_freq % (post_div1 * post_div2) as u32 == 0) { + if out_freq == target_hz as u64 && (vco_freq % (post_div1 * post_div2) == 0) { return Some(PllConfig { refdiv: PLL_SYS_REFDIV, fbdiv: fbdiv as u16, @@ -928,7 +928,7 @@ fn find_pll_params(input_hz: u32, target_hz: u32) -> Option { for post_div1 in (1..=7).rev() { for post_div2 in (1..=post_div1).rev() { - let out_freq = vco_freq / (post_div1 * post_div2) as u32; + let out_freq = (vco_freq / (post_div1 * post_div2) as u64) as u32; let diff = if out_freq > target_hz { out_freq - target_hz } else { @@ -1018,7 +1018,10 @@ pub(crate) unsafe fn init(config: ClockConfig) { cortex_m::asm::delay(delay_cycles); // Only now set the BOD level after voltage has stabilized - vreg.bod().write(|w| w.set_vsel(voltage.recommended_bod())); + vreg.bod().write(|w| { + w.set_vsel(voltage.recommended_bod()); + w.set_en(true); // Enable brownout detection + }); } } @@ -1875,3 +1878,206 @@ pub fn dormant_sleep() { } } } + +#[cfg(test)] +mod tests { + use super::*; + + #[cfg(feature = "rp2040")] + #[test] + fn test_voltage_scale_bod_values() { + // Test that each voltage level maps to the correct BOD threshold (approx. 90% of VDD) + // This verifies our BOD settings match our documentation + { + assert_eq!(VoltageScale::V0_85.recommended_bod(), 0b0111); // ~0.774V (91% of 0.85V) + assert_eq!(VoltageScale::V0_90.recommended_bod(), 0b1000); // ~0.817V (91% of 0.90V) + assert_eq!(VoltageScale::V0_95.recommended_bod(), 0b1001); // ~0.860V (91% of 0.95V) + assert_eq!(VoltageScale::V1_00.recommended_bod(), 0b1010); // ~0.903V (90% of 1.00V) + assert_eq!(VoltageScale::V1_05.recommended_bod(), 0b1011); // ~0.946V (90% of 1.05V) + assert_eq!(VoltageScale::V1_10.recommended_bod(), 0b1100); // ~0.989V (90% of 1.10V) + assert_eq!(VoltageScale::V1_15.recommended_bod(), 0b1101); // ~1.032V (90% of 1.15V) + assert_eq!(VoltageScale::V1_20.recommended_bod(), 0b1110); // ~1.075V (90% of 1.20V) + assert_eq!(VoltageScale::V1_25.recommended_bod(), 0b1111); // ~1.118V (89% of 1.25V) + assert_eq!(VoltageScale::V1_30.recommended_bod(), 0b1111); // ~1.118V (86% of 1.30V) - using max available + } + } + + #[cfg(feature = "rp2040")] + #[test] + fn test_find_pll_params() { + #[cfg(feature = "rp2040")] + { + // Test standard 125 MHz configuration with 12 MHz crystal + let params = find_pll_params(12_000_000, 125_000_000).unwrap(); + assert_eq!(params.refdiv, 1); + assert_eq!(params.fbdiv, 125); + + // Test USB PLL configuration for 48MHz + // The algorithm may find different valid parameters than the SDK defaults + // We'll check that it generates a valid configuration that produces 48MHz + let params = find_pll_params(12_000_000, 48_000_000).unwrap(); + assert_eq!(params.refdiv, 1); + + // Calculate the actual output frequency + let ref_freq = 12_000_000 / params.refdiv as u32; + let vco_freq = ref_freq as u64 * params.fbdiv as u64; + let output_freq = (vco_freq / ((params.post_div1 * params.post_div2) as u64)) as u32; + + // Verify the output frequency is correct + assert_eq!(output_freq, 48_000_000); + + // Verify VCO frequency is in valid range + assert!(vco_freq >= 750_000_000 && vco_freq <= 1_800_000_000); + + // Test overclocked configuration for 200 MHz + let params = find_pll_params(12_000_000, 200_000_000).unwrap(); + assert_eq!(params.refdiv, 1); + let vco_freq = 12_000_000 as u64 * params.fbdiv as u64; + let output_freq = (vco_freq / ((params.post_div1 * params.post_div2) as u64)) as u32; + assert_eq!(output_freq, 200_000_000); + assert!(vco_freq >= 750_000_000 && vco_freq <= 1_800_000_000); // VCO in valid range + + // Test non-standard crystal with 16 MHz + let params = find_pll_params(16_000_000, 125_000_000).unwrap(); + let vco_freq = (16_000_000 / params.refdiv as u32) as u64 * params.fbdiv as u64; + let output_freq = (vco_freq / ((params.post_div1 * params.post_div2) as u64)) as u32; + + // With a 16 MHz crystal, we might not get exactly 125 MHz + // Check that it's close enough (within 0.2% margin) + let freq_diff = if output_freq > 125_000_000 { + output_freq - 125_000_000 + } else { + 125_000_000 - output_freq + }; + let error_percentage = (freq_diff as f64 / 125_000_000.0) * 100.0; + assert!( + error_percentage < 0.2, + "Output frequency {} is not close enough to target 125 MHz. Error: {:.2}%", + output_freq, + error_percentage + ); + + assert!(vco_freq >= 750_000_000 && vco_freq <= 1_800_000_000); + } + } + + #[cfg(feature = "rp2040")] + #[test] + fn test_pll_config_validation() { + // Test PLL configuration validation logic + let valid_config = PllConfig { + refdiv: 1, + fbdiv: 125, + post_div1: 6, + post_div2: 2, + }; + + // Valid configuration should pass validation + assert!(valid_config.is_valid(12_000_000)); + + // Test fbdiv constraints + let mut invalid_config = valid_config; + invalid_config.fbdiv = 15; // Below minimum of 16 + assert!(!invalid_config.is_valid(12_000_000)); + + invalid_config.fbdiv = 321; // Above maximum of 320 + assert!(!invalid_config.is_valid(12_000_000)); + + // Test post_div constraints + invalid_config = valid_config; + invalid_config.post_div1 = 0; // Below minimum of 1 + assert!(!invalid_config.is_valid(12_000_000)); + + invalid_config = valid_config; + invalid_config.post_div1 = 8; // Above maximum of 7 + assert!(!invalid_config.is_valid(12_000_000)); + + // Test post_div2 must be <= post_div1 + invalid_config = valid_config; + invalid_config.post_div2 = 7; + invalid_config.post_div1 = 3; + assert!(!invalid_config.is_valid(12_000_000)); + + // Test reference frequency constraints + invalid_config = valid_config; + assert!(!invalid_config.is_valid(4_000_000)); // Below minimum of 5 MHz + assert!(!invalid_config.is_valid(900_000_000)); // Above maximum of 800 MHz + + // Test VCO frequency constraints + invalid_config = valid_config; + invalid_config.fbdiv = 16; + assert!(!invalid_config.is_valid(12_000_000)); // VCO too low: 12MHz * 16 = 192MHz + + // Test VCO frequency constraints - too high + invalid_config = valid_config; + invalid_config.fbdiv = 200; + invalid_config.refdiv = 1; + // This should be INVALID: 12MHz * 200 = 2400MHz exceeds max VCO of 1800MHz + assert!(!invalid_config.is_valid(12_000_000)); + + // Test a valid high VCO configuration + invalid_config.fbdiv = 150; // 12MHz * 150 = 1800MHz, exactly at the limit + assert!(invalid_config.is_valid(12_000_000)); + } + + #[cfg(feature = "rp2040")] + #[test] + fn test_manual_pll_helper() { + { + // Test the new manual_pll helper method + let config = ClockConfig::manual_pll( + 12_000_000, + PllConfig { + refdiv: 1, + fbdiv: 100, + post_div1: 3, + post_div2: 2, + }, + Some(VoltageScale::V1_15), + ); + + // Check voltage scale was set correctly + assert_eq!(config.voltage_scale, Some(VoltageScale::V1_15)); + + // Check PLL config was set correctly + assert_eq!(config.xosc.as_ref().unwrap().sys_pll.as_ref().unwrap().refdiv, 1); + assert_eq!(config.xosc.as_ref().unwrap().sys_pll.as_ref().unwrap().fbdiv, 100); + assert_eq!(config.xosc.as_ref().unwrap().sys_pll.as_ref().unwrap().post_div1, 3); + assert_eq!(config.xosc.as_ref().unwrap().sys_pll.as_ref().unwrap().post_div2, 2); + + // Check we get the expected frequency + assert_eq!( + config + .xosc + .as_ref() + .unwrap() + .sys_pll + .as_ref() + .unwrap() + .output_frequency(12_000_000), + 200_000_000 + ); + } + } + + #[cfg(feature = "rp2040")] + #[test] + fn test_auto_voltage_scaling() { + { + // Test automatic voltage scaling based on frequency + // Under 133 MHz should use default voltage (None) + let config = ClockConfig::at_sys_frequency_mhz(125); + assert_eq!(config.voltage_scale, None); + + // 133-200 MHz should use V1_15 + let config = ClockConfig::at_sys_frequency_mhz(150); + assert_eq!(config.voltage_scale, Some(VoltageScale::V1_15)); + let config = ClockConfig::at_sys_frequency_mhz(200); + assert_eq!(config.voltage_scale, Some(VoltageScale::V1_15)); + + // Above 200 MHz should use V1_20 + let config = ClockConfig::at_sys_frequency_mhz(250); + assert_eq!(config.voltage_scale, Some(VoltageScale::V1_20)); + } + } +} From 3c559378a5f3662134cb7db2d902302d7cb8f937 Mon Sep 17 00:00:00 2001 From: 1-rafael-1 Date: Thu, 1 May 2025 18:29:13 +0200 Subject: [PATCH 43/97] Add overclocking test for RP2040 with timer and PWM tests at 200Mhz --- tests/rp/src/bin/overclock.rs | 62 +++++++++++++++++++++++++++++++++++ 1 file changed, 62 insertions(+) create mode 100644 tests/rp/src/bin/overclock.rs diff --git a/tests/rp/src/bin/overclock.rs b/tests/rp/src/bin/overclock.rs new file mode 100644 index 000000000..c7b9180a0 --- /dev/null +++ b/tests/rp/src/bin/overclock.rs @@ -0,0 +1,62 @@ +#![no_std] +#![no_main] +#[cfg(feature = "rp2040")] +teleprobe_meta::target!(b"rpi-pico"); + +use defmt::{assert, assert_eq, info}; +use embassy_executor::Spawner; +use embassy_rp::config::Config; +use embassy_rp::gpio::{Input, Pull}; +use embassy_rp::pwm::{Config as PwmConfig, Pwm}; +use embassy_time::{Instant, Timer}; +use {defmt_rtt as _, panic_probe as _}; + +#[cfg(feature = "rp2040")] +#[embassy_executor::main] +async fn main(_spawner: Spawner) { + // Initialize with 200MHz clock configuration for RP2040 + let mut config = Config::default(); + config.clocks = embassy_rp::clocks::ClockConfig::at_sys_frequency_mhz(200); + + let p = embassy_rp::init(config); + + info!("RP2040 overclocked to 200MHz!"); + info!("System clock frequency: {} Hz", embassy_rp::clocks::clk_sys_freq()); + + // Test 1: Timer accuracy at 200MHz + info!("Testing timer accuracy at 200MHz..."); + let start = Instant::now(); + Timer::after_millis(100).await; + let end = Instant::now(); + let ms = (end - start).as_millis(); + info!("slept for {} ms", ms); + assert!(ms >= 99); + assert!(ms < 110); + info!("Timer test passed!"); + + // Test 2: PWM functionality at 200MHz + info!("Testing PWM functionality at 200MHz..."); + let pwm_cfg = { + let mut c = PwmConfig::default(); + c.divider = ((embassy_rp::clocks::clk_sys_freq() / 1_000_000) as u8).into(); + c.top = 10000; + c.compare_a = 5000; + c.compare_b = 5000; + c + }; + + // Test PWM output + let pin1 = Input::new(p.PIN_9, Pull::None); + let _pwm = Pwm::new_output_a(p.PWM_SLICE3, p.PIN_6, pwm_cfg); + Timer::after_millis(1).await; + let initial_state = pin1.is_low(); + Timer::after_millis(5).await; + assert_eq!(pin1.is_high(), initial_state); + Timer::after_millis(5).await; + assert_eq!(pin1.is_low(), initial_state); + info!("PWM test passed!"); + + info!("All tests passed at 200MHz!"); + info!("Overclock test successful"); + cortex_m::asm::bkpt(); +} From 7fa59a6b31c7c9744e5cdef3826bc4e726736606 Mon Sep 17 00:00:00 2001 From: 1-rafael-1 Date: Thu, 1 May 2025 18:38:03 +0200 Subject: [PATCH 44/97] Refactor overclock test for RP2040: add unused imports conditionally and ensure placeholder main function for non-RP2040 targets --- tests/rp/src/bin/overclock.rs | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/tests/rp/src/bin/overclock.rs b/tests/rp/src/bin/overclock.rs index c7b9180a0..c4393dde4 100644 --- a/tests/rp/src/bin/overclock.rs +++ b/tests/rp/src/bin/overclock.rs @@ -1,14 +1,23 @@ #![no_std] #![no_main] +#![cfg_attr(not(feature = "rp2040"), allow(unused_imports))] + #[cfg(feature = "rp2040")] teleprobe_meta::target!(b"rpi-pico"); +#[cfg(feature = "rp2040")] use defmt::{assert, assert_eq, info}; +#[cfg(feature = "rp2040")] use embassy_executor::Spawner; +#[cfg(feature = "rp2040")] use embassy_rp::config::Config; +#[cfg(feature = "rp2040")] use embassy_rp::gpio::{Input, Pull}; +#[cfg(feature = "rp2040")] use embassy_rp::pwm::{Config as PwmConfig, Pwm}; +#[cfg(feature = "rp2040")] use embassy_time::{Instant, Timer}; +#[cfg(feature = "rp2040")] use {defmt_rtt as _, panic_probe as _}; #[cfg(feature = "rp2040")] @@ -60,3 +69,11 @@ async fn main(_spawner: Spawner) { info!("Overclock test successful"); cortex_m::asm::bkpt(); } + +#[cfg(not(feature = "rp2040"))] +#[embassy_executor::main] +async fn main(_spawner: embassy_executor::Spawner) { + // This is an empty placeholder main function for non-RP2040 targets + // It should never be called since the test only runs on RP2040 + cortex_m::asm::bkpt(); +} From 38fd357536e0332c3295d760eed4f481f4d01ce2 Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Thu, 1 May 2025 18:38:37 +0200 Subject: [PATCH 45/97] Update stm32-metapac --- embassy-stm32/Cargo.toml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/embassy-stm32/Cargo.toml b/embassy-stm32/Cargo.toml index 54badc8f2..e9f236881 100644 --- a/embassy-stm32/Cargo.toml +++ b/embassy-stm32/Cargo.toml @@ -73,7 +73,7 @@ rand_core = "0.6.3" sdio-host = "0.9.0" critical-section = "1.1" #stm32-metapac = { version = "16" } -stm32-metapac = { git = "https://github.com/embassy-rs/stm32-data-generated", tag = "stm32-data-a821bf5dd8d283c1e8de88fc7699235777a07e78" } +stm32-metapac = { git = "https://github.com/embassy-rs/stm32-data-generated", tag = "stm32-data-169e38298f9421dddbabc0eb81d0c30fb1eec0a7" } vcell = "0.1.3" nb = "1.0.0" @@ -102,7 +102,7 @@ proc-macro2 = "1.0.36" quote = "1.0.15" #stm32-metapac = { version = "16", default-features = false, features = ["metadata"]} -stm32-metapac = { git = "https://github.com/embassy-rs/stm32-data-generated", tag = "stm32-data-a821bf5dd8d283c1e8de88fc7699235777a07e78", default-features = false, features = ["metadata"] } +stm32-metapac = { git = "https://github.com/embassy-rs/stm32-data-generated", tag = "stm32-data-169e38298f9421dddbabc0eb81d0c30fb1eec0a7", default-features = false, features = ["metadata"] } [features] default = ["rt"] From 561356f68ab2d3d6fbe1f85245eb320dbc3e59f0 Mon Sep 17 00:00:00 2001 From: 1-rafael-1 Date: Thu, 1 May 2025 22:17:25 +0200 Subject: [PATCH 46/97] overclock test, should now run on all rp chips --- tests/rp/src/bin/overclock.rs | 96 +++++++++++++++-------------------- 1 file changed, 41 insertions(+), 55 deletions(-) diff --git a/tests/rp/src/bin/overclock.rs b/tests/rp/src/bin/overclock.rs index c4393dde4..d6d6062f2 100644 --- a/tests/rp/src/bin/overclock.rs +++ b/tests/rp/src/bin/overclock.rs @@ -1,79 +1,65 @@ #![no_std] #![no_main] -#![cfg_attr(not(feature = "rp2040"), allow(unused_imports))] #[cfg(feature = "rp2040")] teleprobe_meta::target!(b"rpi-pico"); +#[cfg(feature = "rp235xb")] +teleprobe_meta::target!(b"pimoroni-pico-plus-2"); -#[cfg(feature = "rp2040")] use defmt::{assert, assert_eq, info}; -#[cfg(feature = "rp2040")] use embassy_executor::Spawner; +use embassy_rp::clocks; #[cfg(feature = "rp2040")] +use embassy_rp::clocks::ClockConfig; +#[cfg(feature = "rp2040")] +use embassy_rp::clocks::VoltageScale; use embassy_rp::config::Config; -#[cfg(feature = "rp2040")] -use embassy_rp::gpio::{Input, Pull}; -#[cfg(feature = "rp2040")] -use embassy_rp::pwm::{Config as PwmConfig, Pwm}; -#[cfg(feature = "rp2040")] -use embassy_time::{Instant, Timer}; -#[cfg(feature = "rp2040")] +use embassy_time::Instant; use {defmt_rtt as _, panic_probe as _}; -#[cfg(feature = "rp2040")] +const COUNT_TO: i64 = 10_000_000; + #[embassy_executor::main] async fn main(_spawner: Spawner) { - // Initialize with 200MHz clock configuration for RP2040 let mut config = Config::default(); - config.clocks = embassy_rp::clocks::ClockConfig::at_sys_frequency_mhz(200); - let p = embassy_rp::init(config); + // Initialize with 200MHz clock configuration for RP2040, other chips will use default clock + #[cfg(feature = "rp2040")] + { + config.clocks = ClockConfig::at_sys_frequency_mhz(200); + let voltage = config.clocks.voltage_scale.unwrap(); + assert!(matches!(voltage, VoltageScale::V1_15), "Expected voltage scale V1_15"); + } - info!("RP2040 overclocked to 200MHz!"); - info!("System clock frequency: {} Hz", embassy_rp::clocks::clk_sys_freq()); + let _p = embassy_rp::init(config); - // Test 1: Timer accuracy at 200MHz - info!("Testing timer accuracy at 200MHz..."); - let start = Instant::now(); - Timer::after_millis(100).await; - let end = Instant::now(); - let ms = (end - start).as_millis(); - info!("slept for {} ms", ms); - assert!(ms >= 99); - assert!(ms < 110); - info!("Timer test passed!"); + let (time_elapsed, clk_sys_freq) = { + // Test the system speed + let mut counter = 0; + let start = Instant::now(); + while counter < COUNT_TO { + counter += 1; + } + let elapsed = Instant::now() - start; - // Test 2: PWM functionality at 200MHz - info!("Testing PWM functionality at 200MHz..."); - let pwm_cfg = { - let mut c = PwmConfig::default(); - c.divider = ((embassy_rp::clocks::clk_sys_freq() / 1_000_000) as u8).into(); - c.top = 10000; - c.compare_a = 5000; - c.compare_b = 5000; - c + (elapsed.as_millis(), clocks::clk_sys_freq()) }; - // Test PWM output - let pin1 = Input::new(p.PIN_9, Pull::None); - let _pwm = Pwm::new_output_a(p.PWM_SLICE3, p.PIN_6, pwm_cfg); - Timer::after_millis(1).await; - let initial_state = pin1.is_low(); - Timer::after_millis(5).await; - assert_eq!(pin1.is_high(), initial_state); - Timer::after_millis(5).await; - assert_eq!(pin1.is_low(), initial_state); - info!("PWM test passed!"); + // Report the elapsed time, so that the compiler doesn't optimize it away for chips other than RP2040 + info!( + "At {}Mhz: Elapsed time to count to {}: {}ms", + clk_sys_freq / 1_000_000, + COUNT_TO, + time_elapsed + ); + + #[cfg(feature = "rp2040")] + { + // we should be at 200MHz + assert_eq!(clk_sys_freq, 200_000_000, "System clock frequency is not 200MHz"); + // At 200MHz, the time to count to 10_000_000 should be at 600ms, testing with 1% margin + assert!(time_elapsed <= 606, "Elapsed time is too long"); + } - info!("All tests passed at 200MHz!"); - info!("Overclock test successful"); - cortex_m::asm::bkpt(); -} - -#[cfg(not(feature = "rp2040"))] -#[embassy_executor::main] -async fn main(_spawner: embassy_executor::Spawner) { - // This is an empty placeholder main function for non-RP2040 targets - // It should never be called since the test only runs on RP2040 cortex_m::asm::bkpt(); } From a33e7172f6bf7dc9590432dd62c2b71d0215d99d Mon Sep 17 00:00:00 2001 From: 1-rafael-1 Date: Thu, 1 May 2025 22:23:14 +0200 Subject: [PATCH 47/97] make ci happy --- tests/rp/src/bin/overclock.rs | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/tests/rp/src/bin/overclock.rs b/tests/rp/src/bin/overclock.rs index d6d6062f2..e4845a55f 100644 --- a/tests/rp/src/bin/overclock.rs +++ b/tests/rp/src/bin/overclock.rs @@ -6,7 +6,9 @@ teleprobe_meta::target!(b"rpi-pico"); #[cfg(feature = "rp235xb")] teleprobe_meta::target!(b"pimoroni-pico-plus-2"); -use defmt::{assert, assert_eq, info}; +use defmt::info; +#[cfg(feature = "rp2040")] +use defmt::{assert, assert_eq}; use embassy_executor::Spawner; use embassy_rp::clocks; #[cfg(feature = "rp2040")] @@ -21,7 +23,10 @@ const COUNT_TO: i64 = 10_000_000; #[embassy_executor::main] async fn main(_spawner: Spawner) { + #[cfg(feature = "rp2040")] let mut config = Config::default(); + #[cfg(not(feature = "rp2040"))] + let config = Config::default(); // Initialize with 200MHz clock configuration for RP2040, other chips will use default clock #[cfg(feature = "rp2040")] From 2fd803f7c336dd6aa042c34e11e213e6e4eb13ad Mon Sep 17 00:00:00 2001 From: Marc <35759328+marcemmers@users.noreply.github.com> Date: Fri, 2 May 2025 12:17:35 +0200 Subject: [PATCH 48/97] Removed instance from uart types --- embassy-rp/src/uart/buffered.rs | 306 +++++++++++++++++--------------- embassy-rp/src/uart/mod.rs | 291 +++++++++++++++++------------- 2 files changed, 324 insertions(+), 273 deletions(-) diff --git a/embassy-rp/src/uart/buffered.rs b/embassy-rp/src/uart/buffered.rs index da18138b5..44b6ee469 100644 --- a/embassy-rp/src/uart/buffered.rs +++ b/embassy-rp/src/uart/buffered.rs @@ -34,28 +34,31 @@ impl State { } /// Buffered UART driver. -pub struct BufferedUart<'d, T: Instance> { - pub(crate) rx: BufferedUartRx<'d, T>, - pub(crate) tx: BufferedUartTx<'d, T>, +pub struct BufferedUart { + pub(super) rx: BufferedUartRx, + pub(super) tx: BufferedUartTx, } /// Buffered UART RX handle. -pub struct BufferedUartRx<'d, T: Instance> { - pub(crate) phantom: PhantomData<&'d mut T>, +pub struct BufferedUartRx { + pub(super) info: &'static Info, + pub(super) state: &'static State, + // pub(crate) phantom: PhantomData<&'d mut T>, } /// Buffered UART TX handle. -pub struct BufferedUartTx<'d, T: Instance> { - pub(crate) phantom: PhantomData<&'d mut T>, +pub struct BufferedUartTx { + pub(super) info: &'static Info, + pub(super) state: &'static State, + // pub(crate) phantom: PhantomData<&'d mut T>, } -pub(crate) fn init_buffers<'d, T: Instance + 'd>( - _irq: impl Binding>, +pub(super) fn init_buffers<'d>( + info: &Info, + state: &State, tx_buffer: Option<&'d mut [u8]>, rx_buffer: Option<&'d mut [u8]>, ) { - let state = T::buffered_state(); - if let Some(tx_buffer) = tx_buffer { let len = tx_buffer.len(); unsafe { state.tx_buf.init(tx_buffer.as_mut_ptr(), len) }; @@ -76,61 +79,73 @@ pub(crate) fn init_buffers<'d, T: Instance + 'd>( // This means we can leave the interrupt enabled the whole time as long as // we clear it after it happens. The downside is that the we manually have // to pend the ISR when we want data transmission to start. - let regs = T::regs(); - regs.uartimsc().write(|w| { + info.regs.uartimsc().write(|w| { w.set_rxim(true); w.set_rtim(true); w.set_txim(true); }); - T::Interrupt::unpend(); - unsafe { T::Interrupt::enable() }; + info.interrupt.unpend(); + unsafe { info.interrupt.enable() }; } -impl<'d, T: Instance> BufferedUart<'d, T> { +impl BufferedUart { /// Create a buffered UART instance. - pub fn new( + pub fn new<'d, T: Instance>( _uart: Peri<'d, T>, tx: Peri<'d, impl TxPin>, rx: Peri<'d, impl RxPin>, - irq: impl Binding>, + _irq: impl Binding>, tx_buffer: &'d mut [u8], rx_buffer: &'d mut [u8], config: Config, ) -> Self { - super::Uart::<'d, T, Async>::init(Some(tx.into()), Some(rx.into()), None, None, config); - init_buffers::(irq, Some(tx_buffer), Some(rx_buffer)); + super::Uart::<'d, Async>::init(T::info(), Some(tx.into()), Some(rx.into()), None, None, config); + init_buffers(T::info(), T::buffered_state(), Some(tx_buffer), Some(rx_buffer)); Self { - rx: BufferedUartRx { phantom: PhantomData }, - tx: BufferedUartTx { phantom: PhantomData }, + rx: BufferedUartRx { + info: T::info(), + state: T::buffered_state(), + }, + tx: BufferedUartTx { + info: T::info(), + state: T::buffered_state(), + }, } } /// Create a buffered UART instance with flow control. - pub fn new_with_rtscts( + pub fn new_with_rtscts<'d, T: Instance>( _uart: Peri<'d, T>, tx: Peri<'d, impl TxPin>, rx: Peri<'d, impl RxPin>, rts: Peri<'d, impl RtsPin>, cts: Peri<'d, impl CtsPin>, - irq: impl Binding>, + _irq: impl Binding>, tx_buffer: &'d mut [u8], rx_buffer: &'d mut [u8], config: Config, ) -> Self { - super::Uart::<'d, T, Async>::init( + super::Uart::<'d, Async>::init( + T::info(), Some(tx.into()), Some(rx.into()), Some(rts.into()), Some(cts.into()), config, ); - init_buffers::(irq, Some(tx_buffer), Some(rx_buffer)); + init_buffers(T::info(), T::buffered_state(), Some(tx_buffer), Some(rx_buffer)); Self { - rx: BufferedUartRx { phantom: PhantomData }, - tx: BufferedUartTx { phantom: PhantomData }, + rx: BufferedUartRx { + info: T::info(), + state: T::buffered_state(), + }, + tx: BufferedUartTx { + info: T::info(), + state: T::buffered_state(), + }, } } @@ -160,68 +175,75 @@ impl<'d, T: Instance> BufferedUart<'d, T> { } /// sets baudrate on runtime - pub fn set_baudrate(&mut self, baudrate: u32) { - super::Uart::<'d, T, Async>::set_baudrate_inner(baudrate); + pub fn set_baudrate<'d>(&mut self, baudrate: u32) { + super::Uart::<'d, Async>::set_baudrate_inner(self.rx.info, baudrate); } /// Split into separate RX and TX handles. - pub fn split(self) -> (BufferedUartTx<'d, T>, BufferedUartRx<'d, T>) { + pub fn split(self) -> (BufferedUartTx, BufferedUartRx) { (self.tx, self.rx) } /// Split the Uart 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 BufferedUartTx<'d, T>, &mut BufferedUartRx<'d, T>) { + pub fn split_ref(&mut self) -> (&mut BufferedUartTx, &mut BufferedUartRx) { (&mut self.tx, &mut self.rx) } } -impl<'d, T: Instance> BufferedUartRx<'d, T> { +impl BufferedUartRx { /// Create a new buffered UART RX. - pub fn new( + pub fn new<'d, T: Instance>( _uart: Peri<'d, T>, - irq: impl Binding>, + _irq: impl Binding>, rx: Peri<'d, impl RxPin>, rx_buffer: &'d mut [u8], config: Config, ) -> Self { - super::Uart::<'d, T, Async>::init(None, Some(rx.into()), None, None, config); - init_buffers::(irq, None, Some(rx_buffer)); + super::Uart::<'d, Async>::init(T::info(), None, Some(rx.into()), None, None, config); + init_buffers(T::info(), T::buffered_state(), None, Some(rx_buffer)); - Self { phantom: PhantomData } + Self { + info: T::info(), + state: T::buffered_state(), + } } /// Create a new buffered UART RX with flow control. - pub fn new_with_rts( + pub fn new_with_rts<'d, T: Instance>( _uart: Peri<'d, T>, - irq: impl Binding>, + _irq: impl Binding>, rx: Peri<'d, impl RxPin>, rts: Peri<'d, impl RtsPin>, rx_buffer: &'d mut [u8], config: Config, ) -> Self { - super::Uart::<'d, T, Async>::init(None, Some(rx.into()), Some(rts.into()), None, config); - init_buffers::(irq, None, Some(rx_buffer)); + super::Uart::<'d, Async>::init(T::info(), None, Some(rx.into()), Some(rts.into()), None, config); + init_buffers(T::info(), T::buffered_state(), None, Some(rx_buffer)); - Self { phantom: PhantomData } + Self { + info: T::info(), + state: T::buffered_state(), + } } - fn read<'a>(buf: &'a mut [u8]) -> impl Future> + 'a - where - T: 'd, - { + fn read<'a>( + info: &'static Info, + state: &'static State, + buf: &'a mut [u8], + ) -> impl Future> + 'a { poll_fn(move |cx| { - if let Poll::Ready(r) = Self::try_read(buf) { + if let Poll::Ready(r) = Self::try_read(info, state, buf) { return Poll::Ready(r); } - T::buffered_state().rx_waker.register(cx.waker()); + state.rx_waker.register(cx.waker()); Poll::Pending }) } - fn get_rx_error() -> Option { - let errs = T::buffered_state().rx_error.swap(0, Ordering::Relaxed); + fn get_rx_error(state: &State) -> Option { + let errs = state.rx_error.swap(0, Ordering::Relaxed); if errs & RXE_OVERRUN != 0 { Some(Error::Overrun) } else if errs & RXE_BREAK != 0 { @@ -235,15 +257,11 @@ impl<'d, T: Instance> BufferedUartRx<'d, T> { } } - fn try_read(buf: &mut [u8]) -> Poll> - where - T: 'd, - { + fn try_read(info: &Info, state: &State, buf: &mut [u8]) -> Poll> { if buf.is_empty() { return Poll::Ready(Ok(0)); } - let state = T::buffered_state(); let mut rx_reader = unsafe { state.rx_buf.reader() }; let n = rx_reader.pop(|data| { let n = data.len().min(buf.len()); @@ -252,7 +270,7 @@ impl<'d, T: Instance> BufferedUartRx<'d, T> { }); let result = if n == 0 { - match Self::get_rx_error() { + match Self::get_rx_error(state) { None => return Poll::Pending, Some(e) => Err(e), } @@ -262,8 +280,7 @@ impl<'d, T: Instance> BufferedUartRx<'d, T> { // (Re-)Enable the interrupt to receive more data in case it was // disabled because the buffer was full or errors were detected. - let regs = T::regs(); - regs.uartimsc().write_set(|w| { + info.regs.uartimsc().write_set(|w| { w.set_rxim(true); w.set_rtim(true); }); @@ -274,23 +291,19 @@ impl<'d, T: Instance> BufferedUartRx<'d, T> { /// Read from UART RX buffer blocking execution until done. pub fn blocking_read(&mut self, buf: &mut [u8]) -> Result { loop { - match Self::try_read(buf) { + match Self::try_read(self.info, self.state, buf) { Poll::Ready(res) => return res, Poll::Pending => continue, } } } - fn fill_buf<'a>() -> impl Future> - where - T: 'd, - { + fn fill_buf<'a>(state: &'static State) -> impl Future> { poll_fn(move |cx| { - let state = T::buffered_state(); let mut rx_reader = unsafe { state.rx_buf.reader() }; let (p, n) = rx_reader.pop_buf(); let result = if n == 0 { - match Self::get_rx_error() { + match Self::get_rx_error(state) { None => { state.rx_waker.register(cx.waker()); return Poll::Pending; @@ -306,64 +319,70 @@ impl<'d, T: Instance> BufferedUartRx<'d, T> { }) } - fn consume(amt: usize) { - let state = T::buffered_state(); + fn consume(info: &Info, state: &State, amt: usize) { let mut rx_reader = unsafe { state.rx_buf.reader() }; rx_reader.pop_done(amt); // (Re-)Enable the interrupt to receive more data in case it was // disabled because the buffer was full or errors were detected. - let regs = T::regs(); - regs.uartimsc().write_set(|w| { + info.regs.uartimsc().write_set(|w| { w.set_rxim(true); w.set_rtim(true); }); } /// we are ready to read if there is data in the buffer - fn read_ready() -> Result { - let state = T::buffered_state(); + fn read_ready(state: &State) -> Result { Ok(!state.rx_buf.is_empty()) } } -impl<'d, T: Instance> BufferedUartTx<'d, T> { +impl BufferedUartTx { /// Create a new buffered UART TX. - pub fn new( + pub fn new<'d, T: Instance>( _uart: Peri<'d, T>, - irq: impl Binding>, + _irq: impl Binding>, tx: Peri<'d, impl TxPin>, tx_buffer: &'d mut [u8], config: Config, ) -> Self { - super::Uart::<'d, T, Async>::init(Some(tx.into()), None, None, None, config); - init_buffers::(irq, Some(tx_buffer), None); + super::Uart::<'d, Async>::init(T::info(), Some(tx.into()), None, None, None, config); + init_buffers(T::info(), T::buffered_state(), Some(tx_buffer), None); - Self { phantom: PhantomData } + Self { + info: T::info(), + state: T::buffered_state(), + } } /// Create a new buffered UART TX with flow control. - pub fn new_with_cts( + pub fn new_with_cts<'d, T: Instance>( _uart: Peri<'d, T>, - irq: impl Binding>, + _irq: impl Binding>, tx: Peri<'d, impl TxPin>, cts: Peri<'d, impl CtsPin>, tx_buffer: &'d mut [u8], config: Config, ) -> Self { - super::Uart::<'d, T, Async>::init(Some(tx.into()), None, None, Some(cts.into()), config); - init_buffers::(irq, Some(tx_buffer), None); + super::Uart::<'d, Async>::init(T::info(), Some(tx.into()), None, None, Some(cts.into()), config); + init_buffers(T::info(), T::buffered_state(), Some(tx_buffer), None); - Self { phantom: PhantomData } + Self { + info: T::info(), + state: T::buffered_state(), + } } - fn write(buf: &[u8]) -> impl Future> + '_ { + fn write<'d>( + info: &'static Info, + state: &'static State, + buf: &'d [u8], + ) -> impl Future> + 'd { poll_fn(move |cx| { if buf.is_empty() { return Poll::Ready(Ok(0)); } - let state = T::buffered_state(); let mut tx_writer = unsafe { state.tx_buf.writer() }; let n = tx_writer.push(|data| { let n = data.len().min(buf.len()); @@ -379,14 +398,13 @@ impl<'d, T: Instance> BufferedUartTx<'d, T> { // FIFO and the number of bytes drops below a threshold. When the // FIFO was empty we have to manually pend the interrupt to shovel // TX data from the buffer into the FIFO. - T::Interrupt::pend(); + info.interrupt.pend(); Poll::Ready(Ok(n)) }) } - fn flush() -> impl Future> { + fn flush(state: &'static State) -> impl Future> { poll_fn(move |cx| { - let state = T::buffered_state(); if !state.tx_buf.is_empty() { state.tx_waker.register(cx.waker()); return Poll::Pending; @@ -403,8 +421,7 @@ impl<'d, T: Instance> BufferedUartTx<'d, T> { } loop { - let state = T::buffered_state(); - let mut tx_writer = unsafe { state.tx_buf.writer() }; + let mut tx_writer = unsafe { self.state.tx_buf.writer() }; let n = tx_writer.push(|data| { let n = data.len().min(buf.len()); data[..n].copy_from_slice(&buf[..n]); @@ -416,7 +433,7 @@ impl<'d, T: Instance> BufferedUartTx<'d, T> { // FIFO and the number of bytes drops below a threshold. When the // FIFO was empty we have to manually pend the interrupt to shovel // TX data from the buffer into the FIFO. - T::Interrupt::pend(); + self.info.interrupt.pend(); return Ok(n); } } @@ -425,8 +442,7 @@ impl<'d, T: Instance> BufferedUartTx<'d, T> { /// Flush UART TX blocking execution until done. pub fn blocking_flush(&mut self) -> Result<(), Error> { loop { - let state = T::buffered_state(); - if state.tx_buf.is_empty() { + if self.state.tx_buf.is_empty() { return Ok(()); } } @@ -434,7 +450,7 @@ impl<'d, T: Instance> BufferedUartTx<'d, T> { /// Check if UART is busy. pub fn busy(&self) -> bool { - T::regs().uartfr().read().busy() + self.info.regs.uartfr().read().busy() } /// Assert a break condition after waiting for the transmit buffers to empty, @@ -445,7 +461,7 @@ impl<'d, T: Instance> BufferedUartTx<'d, T> { /// This method may block for a long amount of time since it has to wait /// for the transmit fifo to empty, which may take a while on slow links. pub async fn send_break(&mut self, bits: u32) { - let regs = T::regs(); + let regs = self.info.regs; let bits = bits.max({ let lcr = regs.uartlcr_h().read(); let width = lcr.wlen() as u32 + 5; @@ -458,7 +474,7 @@ impl<'d, T: Instance> BufferedUartTx<'d, T> { let div_clk = clk_peri_freq() as u64 * 64; let wait_usecs = (1_000_000 * bits as u64 * divx64 * 16 + div_clk - 1) / div_clk; - Self::flush().await.unwrap(); + Self::flush(self.state).await.unwrap(); while self.busy() {} regs.uartlcr_h().write_set(|w| w.set_brk(true)); Timer::after_micros(wait_usecs).await; @@ -466,28 +482,26 @@ impl<'d, T: Instance> BufferedUartTx<'d, T> { } } -impl<'d, T: Instance> Drop for BufferedUartRx<'d, T> { +impl Drop for BufferedUartRx { fn drop(&mut self) { - let state = T::buffered_state(); - unsafe { state.rx_buf.deinit() } + unsafe { self.state.rx_buf.deinit() } // TX is inactive if the buffer is not available. // We can now unregister the interrupt handler - if !state.tx_buf.is_available() { - T::Interrupt::disable(); + if !self.state.tx_buf.is_available() { + self.info.interrupt.disable(); } } } -impl<'d, T: Instance> Drop for BufferedUartTx<'d, T> { +impl Drop for BufferedUartTx { fn drop(&mut self) { - let state = T::buffered_state(); - unsafe { state.tx_buf.deinit() } + unsafe { self.state.tx_buf.deinit() } // RX is inactive if the buffer is not available. // We can now unregister the interrupt handler - if !state.rx_buf.is_available() { - T::Interrupt::disable(); + if !self.state.rx_buf.is_available() { + self.info.interrupt.disable(); } } } @@ -499,7 +513,7 @@ pub struct BufferedInterruptHandler { impl interrupt::typelevel::Handler for BufferedInterruptHandler { unsafe fn on_interrupt() { - let r = T::regs(); + let r = T::info().regs; if r.uartdmacr().read().rxdmae() { return; } @@ -603,95 +617,95 @@ impl embedded_io::Error for Error { } } -impl<'d, T: Instance> embedded_io_async::ErrorType for BufferedUart<'d, T> { +impl embedded_io_async::ErrorType for BufferedUart { type Error = Error; } -impl<'d, T: Instance> embedded_io_async::ErrorType for BufferedUartRx<'d, T> { +impl embedded_io_async::ErrorType for BufferedUartRx { type Error = Error; } -impl<'d, T: Instance> embedded_io_async::ErrorType for BufferedUartTx<'d, T> { +impl embedded_io_async::ErrorType for BufferedUartTx { type Error = Error; } -impl<'d, T: Instance + 'd> embedded_io_async::Read for BufferedUart<'d, T> { +impl embedded_io_async::Read for BufferedUart { async fn read(&mut self, buf: &mut [u8]) -> Result { - BufferedUartRx::<'d, T>::read(buf).await + BufferedUartRx::read(self.rx.info, self.rx.state, buf).await } } -impl<'d, T: Instance + 'd> embedded_io_async::Read for BufferedUartRx<'d, T> { +impl embedded_io_async::Read for BufferedUartRx { async fn read(&mut self, buf: &mut [u8]) -> Result { - Self::read(buf).await + Self::read(self.info, self.state, buf).await } } -impl<'d, T: Instance + 'd> embedded_io_async::ReadReady for BufferedUart<'d, T> { +impl embedded_io_async::ReadReady for BufferedUart { fn read_ready(&mut self) -> Result { - BufferedUartRx::<'d, T>::read_ready() + BufferedUartRx::read_ready(self.rx.state) } } -impl<'d, T: Instance + 'd> embedded_io_async::ReadReady for BufferedUartRx<'d, T> { +impl embedded_io_async::ReadReady for BufferedUartRx { fn read_ready(&mut self) -> Result { - Self::read_ready() + Self::read_ready(self.state) } } -impl<'d, T: Instance + 'd> embedded_io_async::BufRead for BufferedUart<'d, T> { +impl embedded_io_async::BufRead for BufferedUart { async fn fill_buf(&mut self) -> Result<&[u8], Self::Error> { - BufferedUartRx::<'d, T>::fill_buf().await + BufferedUartRx::fill_buf(self.rx.state).await } fn consume(&mut self, amt: usize) { - BufferedUartRx::<'d, T>::consume(amt) + BufferedUartRx::consume(self.rx.info, self.rx.state, amt) } } -impl<'d, T: Instance + 'd> embedded_io_async::BufRead for BufferedUartRx<'d, T> { +impl embedded_io_async::BufRead for BufferedUartRx { async fn fill_buf(&mut self) -> Result<&[u8], Self::Error> { - Self::fill_buf().await + Self::fill_buf(self.state).await } fn consume(&mut self, amt: usize) { - Self::consume(amt) + Self::consume(self.info, self.state, amt) } } -impl<'d, T: Instance + 'd> embedded_io_async::Write for BufferedUart<'d, T> { +impl embedded_io_async::Write for BufferedUart { async fn write(&mut self, buf: &[u8]) -> Result { - BufferedUartTx::<'d, T>::write(buf).await + BufferedUartTx::write(self.tx.info, self.tx.state, buf).await } async fn flush(&mut self) -> Result<(), Self::Error> { - BufferedUartTx::<'d, T>::flush().await + BufferedUartTx::flush(self.tx.state).await } } -impl<'d, T: Instance + 'd> embedded_io_async::Write for BufferedUartTx<'d, T> { +impl embedded_io_async::Write for BufferedUartTx { async fn write(&mut self, buf: &[u8]) -> Result { - Self::write(buf).await + Self::write(self.info, self.state, buf).await } async fn flush(&mut self) -> Result<(), Self::Error> { - Self::flush().await + Self::flush(self.state).await } } -impl<'d, T: Instance + 'd> embedded_io::Read for BufferedUart<'d, T> { +impl embedded_io::Read for BufferedUart { fn read(&mut self, buf: &mut [u8]) -> Result { self.rx.blocking_read(buf) } } -impl<'d, T: Instance + 'd> embedded_io::Read for BufferedUartRx<'d, T> { +impl embedded_io::Read for BufferedUartRx { fn read(&mut self, buf: &mut [u8]) -> Result { self.blocking_read(buf) } } -impl<'d, T: Instance + 'd> embedded_io::Write for BufferedUart<'d, T> { +impl embedded_io::Write for BufferedUart { fn write(&mut self, buf: &[u8]) -> Result { self.tx.blocking_write(buf) } @@ -701,7 +715,7 @@ impl<'d, T: Instance + 'd> embedded_io::Write for BufferedUart<'d, T> { } } -impl<'d, T: Instance + 'd> embedded_io::Write for BufferedUartTx<'d, T> { +impl embedded_io::Write for BufferedUartTx { fn write(&mut self, buf: &[u8]) -> Result { self.blocking_write(buf) } @@ -711,11 +725,11 @@ impl<'d, T: Instance + 'd> embedded_io::Write for BufferedUartTx<'d, T> { } } -impl<'d, T: Instance> embedded_hal_02::serial::Read for BufferedUartRx<'d, T> { +impl embedded_hal_02::serial::Read for BufferedUartRx { type Error = Error; fn read(&mut self) -> Result> { - let r = T::regs(); + let r = self.info.regs; if r.uartfr().read().rxfe() { return Err(nb::Error::WouldBlock); } @@ -736,7 +750,7 @@ impl<'d, T: Instance> embedded_hal_02::serial::Read for BufferedUartRx<'d, T } } -impl<'d, T: Instance> embedded_hal_02::blocking::serial::Write for BufferedUartTx<'d, T> { +impl embedded_hal_02::blocking::serial::Write for BufferedUartTx { type Error = Error; fn bwrite_all(&mut self, mut buffer: &[u8]) -> Result<(), Self::Error> { @@ -755,7 +769,7 @@ impl<'d, T: Instance> embedded_hal_02::blocking::serial::Write for BufferedU } } -impl<'d, T: Instance> embedded_hal_02::serial::Read for BufferedUart<'d, T> { +impl embedded_hal_02::serial::Read for BufferedUart { type Error = Error; fn read(&mut self) -> Result> { @@ -763,7 +777,7 @@ impl<'d, T: Instance> embedded_hal_02::serial::Read for BufferedUart<'d, T> } } -impl<'d, T: Instance> embedded_hal_02::blocking::serial::Write for BufferedUart<'d, T> { +impl embedded_hal_02::blocking::serial::Write for BufferedUart { type Error = Error; fn bwrite_all(&mut self, mut buffer: &[u8]) -> Result<(), Self::Error> { @@ -782,25 +796,25 @@ impl<'d, T: Instance> embedded_hal_02::blocking::serial::Write for BufferedU } } -impl<'d, T: Instance> embedded_hal_nb::serial::ErrorType for BufferedUartRx<'d, T> { +impl embedded_hal_nb::serial::ErrorType for BufferedUartRx { type Error = Error; } -impl<'d, T: Instance> embedded_hal_nb::serial::ErrorType for BufferedUartTx<'d, T> { +impl embedded_hal_nb::serial::ErrorType for BufferedUartTx { type Error = Error; } -impl<'d, T: Instance> embedded_hal_nb::serial::ErrorType for BufferedUart<'d, T> { +impl embedded_hal_nb::serial::ErrorType for BufferedUart { type Error = Error; } -impl<'d, T: Instance> embedded_hal_nb::serial::Read for BufferedUartRx<'d, T> { +impl embedded_hal_nb::serial::Read for BufferedUartRx { fn read(&mut self) -> nb::Result { embedded_hal_02::serial::Read::read(self) } } -impl<'d, T: Instance> embedded_hal_nb::serial::Write for BufferedUartTx<'d, T> { +impl embedded_hal_nb::serial::Write for BufferedUartTx { fn write(&mut self, char: u8) -> nb::Result<(), Self::Error> { self.blocking_write(&[char]).map(drop).map_err(nb::Error::Other) } @@ -810,13 +824,13 @@ impl<'d, T: Instance> embedded_hal_nb::serial::Write for BufferedUartTx<'d, T> { } } -impl<'d, T: Instance> embedded_hal_nb::serial::Read for BufferedUart<'d, T> { +impl embedded_hal_nb::serial::Read for BufferedUart { fn read(&mut self) -> Result> { embedded_hal_02::serial::Read::read(&mut self.rx) } } -impl<'d, T: Instance> embedded_hal_nb::serial::Write for BufferedUart<'d, T> { +impl embedded_hal_nb::serial::Write for BufferedUart { fn write(&mut self, char: u8) -> nb::Result<(), Self::Error> { self.blocking_write(&[char]).map(drop).map_err(nb::Error::Other) } diff --git a/embassy-rp/src/uart/mod.rs b/embassy-rp/src/uart/mod.rs index 7ce074a3f..d36884109 100644 --- a/embassy-rp/src/uart/mod.rs +++ b/embassy-rp/src/uart/mod.rs @@ -13,7 +13,8 @@ use pac::uart::regs::Uartris; use crate::clocks::clk_peri_freq; use crate::dma::{AnyChannel, Channel}; use crate::gpio::{AnyPin, SealedPin}; -use crate::interrupt::typelevel::{Binding, Interrupt}; +use crate::interrupt::typelevel::{Binding, Interrupt as _}; +use crate::interrupt::{Interrupt, InterruptExt}; use crate::pac::io::vals::{Inover, Outover}; use crate::{interrupt, pac, peripherals, RegExt}; @@ -135,37 +136,41 @@ pub struct DmaState { } /// UART driver. -pub struct Uart<'d, T: Instance, M: Mode> { - tx: UartTx<'d, T, M>, - rx: UartRx<'d, T, M>, +pub struct Uart<'d, M: Mode> { + tx: UartTx<'d, M>, + rx: UartRx<'d, M>, } /// UART TX driver. -pub struct UartTx<'d, T: Instance, M: Mode> { +pub struct UartTx<'d, M: Mode> { + info: &'static Info, tx_dma: Option>, - phantom: PhantomData<(&'d mut T, M)>, + phantom: PhantomData, } /// UART RX driver. -pub struct UartRx<'d, T: Instance, M: Mode> { +pub struct UartRx<'d, M: Mode> { + info: &'static Info, + dma_state: &'static DmaState, rx_dma: Option>, - phantom: PhantomData<(&'d mut T, M)>, + phantom: PhantomData, } -impl<'d, T: Instance, M: Mode> UartTx<'d, T, M> { +impl<'d, M: Mode> UartTx<'d, M> { /// Create a new DMA-enabled UART which can only send data - pub fn new( + pub fn new( _uart: Peri<'d, T>, tx: Peri<'d, impl TxPin>, tx_dma: Peri<'d, impl Channel>, config: Config, ) -> Self { - Uart::::init(Some(tx.into()), None, None, None, config); - Self::new_inner(Some(tx_dma.into())) + Uart::::init(T::info(), Some(tx.into()), None, None, None, config); + Self::new_inner::(Some(tx_dma.into())) } - fn new_inner(tx_dma: Option>) -> Self { + fn new_inner(tx_dma: Option>) -> Self { Self { + info: T::info(), tx_dma, phantom: PhantomData, } @@ -173,7 +178,7 @@ impl<'d, T: Instance, M: Mode> UartTx<'d, T, M> { /// Transmit the provided buffer blocking execution until done. pub fn blocking_write(&mut self, buffer: &[u8]) -> Result<(), Error> { - let r = T::regs(); + let r = self.info.regs; for &b in buffer { while r.uartfr().read().txff() {} r.uartdr().write(|w| w.set_data(b)); @@ -183,14 +188,13 @@ impl<'d, T: Instance, M: Mode> UartTx<'d, T, M> { /// Flush UART TX blocking execution until done. pub fn blocking_flush(&mut self) -> Result<(), Error> { - let r = T::regs(); - while !r.uartfr().read().txfe() {} + while !self.info.regs.uartfr().read().txfe() {} Ok(()) } /// Check if UART is busy transmitting. pub fn busy(&self) -> bool { - T::regs().uartfr().read().busy() + self.info.regs.uartfr().read().busy() } /// Assert a break condition after waiting for the transmit buffers to empty, @@ -201,7 +205,7 @@ impl<'d, T: Instance, M: Mode> UartTx<'d, T, M> { /// This method may block for a long amount of time since it has to wait /// for the transmit fifo to empty, which may take a while on slow links. pub async fn send_break(&mut self, bits: u32) { - let regs = T::regs(); + let regs = self.info.regs; let bits = bits.max({ let lcr = regs.uartlcr_h().read(); let width = lcr.wlen() as u32 + 5; @@ -222,65 +226,75 @@ impl<'d, T: Instance, M: Mode> UartTx<'d, T, M> { } } -impl<'d, T: Instance> UartTx<'d, T, Blocking> { +impl<'d> UartTx<'d, Blocking> { /// Create a new UART TX instance for blocking mode operations. - pub fn new_blocking(_uart: Peri<'d, T>, tx: Peri<'d, impl TxPin>, config: Config) -> Self { - Uart::::init(Some(tx.into()), None, None, None, config); - Self::new_inner(None) + pub fn new_blocking(_uart: Peri<'d, T>, tx: Peri<'d, impl TxPin>, config: Config) -> Self { + Uart::::init(T::info(), Some(tx.into()), None, None, None, config); + Self::new_inner::(None) } /// Convert this uart TX instance into a buffered uart using the provided /// irq and transmit buffer. - pub fn into_buffered( + pub fn into_buffered( self, - irq: impl Binding>, + _irq: impl Binding>, tx_buffer: &'d mut [u8], - ) -> BufferedUartTx<'d, T> { - buffered::init_buffers::(irq, Some(tx_buffer), None); + ) -> BufferedUartTx { + buffered::init_buffers(T::info(), T::buffered_state(), Some(tx_buffer), None); - BufferedUartTx { phantom: PhantomData } + BufferedUartTx { + info: T::info(), + state: T::buffered_state(), + } } } -impl<'d, T: Instance> UartTx<'d, T, Async> { +impl<'d> UartTx<'d, Async> { /// Write to UART TX from the provided buffer using DMA. pub async fn write(&mut self, buffer: &[u8]) -> Result<(), Error> { let ch = self.tx_dma.as_mut().unwrap().reborrow(); let transfer = unsafe { - T::regs().uartdmacr().write_set(|reg| { + self.info.regs.uartdmacr().write_set(|reg| { reg.set_txdmae(true); }); // If we don't assign future to a variable, the data register pointer // is held across an await and makes the future non-Send. - crate::dma::write(ch, buffer, T::regs().uartdr().as_ptr() as *mut _, T::TX_DREQ.into()) + crate::dma::write( + ch, + buffer, + self.info.regs.uartdr().as_ptr() as *mut _, + self.info.tx_dreq.into(), + ) }; transfer.await; Ok(()) } } -impl<'d, T: Instance, M: Mode> UartRx<'d, T, M> { +impl<'d, M: Mode> UartRx<'d, M> { /// Create a new DMA-enabled UART which can only receive data - pub fn new( + pub fn new( _uart: Peri<'d, T>, rx: Peri<'d, impl RxPin>, _irq: impl Binding>, rx_dma: Peri<'d, impl Channel>, config: Config, ) -> Self { - Uart::::init(None, Some(rx.into()), None, None, config); - Self::new_inner(true, Some(rx_dma.into())) + Uart::::init(T::info(), None, Some(rx.into()), None, None, config); + Self::new_inner::(true, Some(rx_dma.into())) } - fn new_inner(has_irq: bool, rx_dma: Option>) -> Self { + fn new_inner(has_irq: bool, rx_dma: Option>) -> Self { debug_assert_eq!(has_irq, rx_dma.is_some()); if has_irq { // disable all error interrupts initially - T::regs().uartimsc().write(|w| w.0 = 0); + T::info().regs.uartimsc().write(|w| w.0 = 0); T::Interrupt::unpend(); unsafe { T::Interrupt::enable() }; } Self { + info: T::info(), + dma_state: T::dma_state(), rx_dma, phantom: PhantomData, } @@ -299,7 +313,7 @@ impl<'d, T: Instance, M: Mode> UartRx<'d, T, M> { /// encountered. in both cases, `len` is the number of *good* bytes copied into /// `buffer`. fn drain_fifo(&mut self, buffer: &mut [u8]) -> Result { - let r = T::regs(); + let r = self.info.regs; for (i, b) in buffer.iter_mut().enumerate() { if r.uartfr().read().rxfe() { return Ok(i); @@ -323,12 +337,12 @@ impl<'d, T: Instance, M: Mode> UartRx<'d, T, M> { } } -impl<'d, T: Instance, M: Mode> Drop for UartRx<'d, T, M> { +impl<'d, M: Mode> Drop for UartRx<'d, M> { fn drop(&mut self) { if self.rx_dma.is_some() { - T::Interrupt::disable(); + self.info.interrupt.disable(); // clear dma flags. irq handlers use these to disambiguate among themselves. - T::regs().uartdmacr().write_clear(|reg| { + self.info.regs.uartdmacr().write_clear(|reg| { reg.set_rxdmae(true); reg.set_txdmae(true); reg.set_dmaonerr(true); @@ -337,23 +351,26 @@ impl<'d, T: Instance, M: Mode> Drop for UartRx<'d, T, M> { } } -impl<'d, T: Instance> UartRx<'d, T, Blocking> { +impl<'d> UartRx<'d, Blocking> { /// Create a new UART RX instance for blocking mode operations. - pub fn new_blocking(_uart: Peri<'d, T>, rx: Peri<'d, impl RxPin>, config: Config) -> Self { - Uart::::init(None, Some(rx.into()), None, None, config); - Self::new_inner(false, None) + pub fn new_blocking(_uart: Peri<'d, T>, rx: Peri<'d, impl RxPin>, config: Config) -> Self { + Uart::::init(T::info(), None, Some(rx.into()), None, None, config); + Self::new_inner::(false, None) } /// Convert this uart RX instance into a buffered uart using the provided /// irq and receive buffer. - pub fn into_buffered( + pub fn into_buffered( self, - irq: impl Binding>, + _irq: impl Binding>, rx_buffer: &'d mut [u8], - ) -> BufferedUartRx<'d, T> { - buffered::init_buffers::(irq, None, Some(rx_buffer)); + ) -> BufferedUartRx { + buffered::init_buffers(T::info(), T::buffered_state(), None, Some(rx_buffer)); - BufferedUartRx { phantom: PhantomData } + BufferedUartRx { + info: T::info(), + state: T::buffered_state(), + } } } @@ -364,7 +381,7 @@ pub struct InterruptHandler { impl interrupt::typelevel::Handler for InterruptHandler { unsafe fn on_interrupt() { - let uart = T::regs(); + let uart = T::info().regs; if !uart.uartdmacr().read().rxdmae() { return; } @@ -380,13 +397,13 @@ impl interrupt::typelevel::Handler for InterruptHandl } } -impl<'d, T: Instance> UartRx<'d, T, Async> { +impl<'d> UartRx<'d, Async> { /// Read from UART RX into the provided buffer. pub async fn read(&mut self, buffer: &mut [u8]) -> Result<(), Error> { // clear error flags before we drain the fifo. errors that have accumulated // in the flags will also be present in the fifo. - T::dma_state().rx_errs.store(0, Ordering::Relaxed); - T::regs().uarticr().write(|w| { + self.dma_state.rx_errs.store(0, Ordering::Relaxed); + self.info.regs.uarticr().write(|w| { w.set_oeic(true); w.set_beic(true); w.set_peic(true); @@ -408,28 +425,33 @@ impl<'d, T: Instance> UartRx<'d, T, Async> { // interrupt flags will have been raised, and those will be picked up immediately // by the interrupt handler. let ch = self.rx_dma.as_mut().unwrap().reborrow(); - T::regs().uartimsc().write_set(|w| { + self.info.regs.uartimsc().write_set(|w| { w.set_oeim(true); w.set_beim(true); w.set_peim(true); w.set_feim(true); }); - T::regs().uartdmacr().write_set(|reg| { + self.info.regs.uartdmacr().write_set(|reg| { reg.set_rxdmae(true); reg.set_dmaonerr(true); }); let transfer = unsafe { // If we don't assign future to a variable, the data register pointer // is held across an await and makes the future non-Send. - crate::dma::read(ch, T::regs().uartdr().as_ptr() as *const _, buffer, T::RX_DREQ.into()) + crate::dma::read( + ch, + self.info.regs.uartdr().as_ptr() as *const _, + buffer, + self.info.rx_dreq.into(), + ) }; // wait for either the transfer to complete or an error to happen. let transfer_result = select( transfer, poll_fn(|cx| { - T::dma_state().rx_err_waker.register(cx.waker()); - match T::dma_state().rx_errs.swap(0, Ordering::Relaxed) { + self.dma_state.rx_err_waker.register(cx.waker()); + match self.dma_state.rx_errs.swap(0, Ordering::Relaxed) { 0 => Poll::Pending, e => Poll::Ready(Uartris(e as u32)), } @@ -441,7 +463,7 @@ impl<'d, T: Instance> UartRx<'d, T, Async> { Either::First(()) => { // We're here because the DMA finished, BUT if an error occurred on the LAST // byte, then we may still need to grab the error state! - Uartris(T::dma_state().rx_errs.swap(0, Ordering::Relaxed) as u32) + Uartris(self.dma_state.rx_errs.swap(0, Ordering::Relaxed) as u32) } Either::Second(e) => { // We're here because we errored, which means this is the error that @@ -521,8 +543,8 @@ impl<'d, T: Instance> UartRx<'d, T, Async> { ) -> Result { // clear error flags before we drain the fifo. errors that have accumulated // in the flags will also be present in the fifo. - T::dma_state().rx_errs.store(0, Ordering::Relaxed); - T::regs().uarticr().write(|w| { + self.dma_state.rx_errs.store(0, Ordering::Relaxed); + self.info.regs.uarticr().write(|w| { w.set_oeic(true); w.set_beic(true); w.set_peic(true); @@ -555,13 +577,13 @@ impl<'d, T: Instance> UartRx<'d, T, Async> { // interrupt flags will have been raised, and those will be picked up immediately // by the interrupt handler. let ch = self.rx_dma.as_mut().unwrap(); - T::regs().uartimsc().write_set(|w| { + self.info.regs.uartimsc().write_set(|w| { w.set_oeim(true); w.set_beim(true); w.set_peim(true); w.set_feim(true); }); - T::regs().uartdmacr().write_set(|reg| { + self.info.regs.uartdmacr().write_set(|reg| { reg.set_rxdmae(true); reg.set_dmaonerr(true); }); @@ -572,9 +594,9 @@ impl<'d, T: Instance> UartRx<'d, T, Async> { // is held across an await and makes the future non-Send. crate::dma::read( ch.reborrow(), - T::regs().uartdr().as_ptr() as *const _, + self.info.regs.uartdr().as_ptr() as *const _, sbuffer, - T::RX_DREQ.into(), + self.info.rx_dreq.into(), ) }; @@ -582,8 +604,8 @@ impl<'d, T: Instance> UartRx<'d, T, Async> { let transfer_result = select( transfer, poll_fn(|cx| { - T::dma_state().rx_err_waker.register(cx.waker()); - match T::dma_state().rx_errs.swap(0, Ordering::Relaxed) { + self.dma_state.rx_err_waker.register(cx.waker()); + match self.dma_state.rx_errs.swap(0, Ordering::Relaxed) { 0 => Poll::Pending, e => Poll::Ready(Uartris(e as u32)), } @@ -596,7 +618,7 @@ impl<'d, T: Instance> UartRx<'d, T, Async> { Either::First(()) => { // We're here because the DMA finished, BUT if an error occurred on the LAST // byte, then we may still need to grab the error state! - Uartris(T::dma_state().rx_errs.swap(0, Ordering::Relaxed) as u32) + Uartris(self.dma_state.rx_errs.swap(0, Ordering::Relaxed) as u32) } Either::Second(e) => { // We're here because we errored, which means this is the error that @@ -635,7 +657,7 @@ impl<'d, T: Instance> UartRx<'d, T, Async> { continue; } - let regs = T::regs(); + let regs = self.info.regs; let all_full = next_addr == eval; // NOTE: This is off label usage of RSR! See the issue below for @@ -685,9 +707,9 @@ impl<'d, T: Instance> UartRx<'d, T, Async> { } } -impl<'d, T: Instance> Uart<'d, T, Blocking> { +impl<'d> Uart<'d, Blocking> { /// Create a new UART without hardware flow control - pub fn new_blocking( + pub fn new_blocking( uart: Peri<'d, T>, tx: Peri<'d, impl TxPin>, rx: Peri<'d, impl RxPin>, @@ -697,7 +719,7 @@ impl<'d, T: Instance> Uart<'d, T, Blocking> { } /// Create a new UART with hardware flow control (RTS/CTS) - pub fn new_with_rtscts_blocking( + pub fn new_with_rtscts_blocking( uart: Peri<'d, T>, tx: Peri<'d, impl TxPin>, rx: Peri<'d, impl RxPin>, @@ -720,24 +742,30 @@ impl<'d, T: Instance> Uart<'d, T, Blocking> { /// Convert this uart instance into a buffered uart using the provided /// irq, transmit and receive buffers. - pub fn into_buffered( + pub fn into_buffered( self, - irq: impl Binding>, + _irq: impl Binding>, tx_buffer: &'d mut [u8], rx_buffer: &'d mut [u8], - ) -> BufferedUart<'d, T> { - buffered::init_buffers::(irq, Some(tx_buffer), Some(rx_buffer)); + ) -> BufferedUart { + buffered::init_buffers(T::info(), T::buffered_state(), Some(tx_buffer), Some(rx_buffer)); BufferedUart { - rx: BufferedUartRx { phantom: PhantomData }, - tx: BufferedUartTx { phantom: PhantomData }, + rx: BufferedUartRx { + info: T::info(), + state: T::buffered_state(), + }, + tx: BufferedUartTx { + info: T::info(), + state: T::buffered_state(), + }, } } } -impl<'d, T: Instance> Uart<'d, T, Async> { +impl<'d> Uart<'d, Async> { /// Create a new DMA enabled UART without hardware flow control - pub fn new( + pub fn new( uart: Peri<'d, T>, tx: Peri<'d, impl TxPin>, rx: Peri<'d, impl RxPin>, @@ -760,7 +788,7 @@ impl<'d, T: Instance> Uart<'d, T, Async> { } /// Create a new DMA enabled UART with hardware flow control (RTS/CTS) - pub fn new_with_rtscts( + pub fn new_with_rtscts( uart: Peri<'d, T>, tx: Peri<'d, impl TxPin>, rx: Peri<'d, impl RxPin>, @@ -785,8 +813,8 @@ impl<'d, T: Instance> Uart<'d, T, Async> { } } -impl<'d, T: Instance + 'd, M: Mode> Uart<'d, T, M> { - fn new_inner( +impl<'d, M: Mode> Uart<'d, M> { + fn new_inner( _uart: Peri<'d, T>, mut tx: Peri<'d, AnyPin>, mut rx: Peri<'d, AnyPin>, @@ -798,6 +826,7 @@ impl<'d, T: Instance + 'd, M: Mode> Uart<'d, T, M> { config: Config, ) -> Self { Self::init( + T::info(), Some(tx.reborrow()), Some(rx.reborrow()), rts.as_mut().map(|x| x.reborrow()), @@ -806,19 +835,20 @@ impl<'d, T: Instance + 'd, M: Mode> Uart<'d, T, M> { ); Self { - tx: UartTx::new_inner(tx_dma), - rx: UartRx::new_inner(has_irq, rx_dma), + tx: UartTx::new_inner::(tx_dma), + rx: UartRx::new_inner::(has_irq, rx_dma), } } fn init( + info: &Info, tx: Option>, rx: Option>, rts: Option>, cts: Option>, config: Config, ) { - let r = T::regs(); + let r = info.regs; if let Some(pin) = &tx { let funcsel = { let pin_number = ((pin.gpio().as_ptr() as u32) & 0x1FF) / 8; @@ -896,7 +926,7 @@ impl<'d, T: Instance + 'd, M: Mode> Uart<'d, T, M> { }); } - Self::set_baudrate_inner(config.baudrate); + Self::set_baudrate_inner(info, config.baudrate); let (pen, eps) = match config.parity { Parity::ParityNone => (false, false), @@ -926,8 +956,8 @@ impl<'d, T: Instance + 'd, M: Mode> Uart<'d, T, M> { }); } - fn lcr_modify(f: impl FnOnce(&mut crate::pac::uart::regs::UartlcrH) -> R) -> R { - let r = T::regs(); + fn lcr_modify(info: &Info, f: impl FnOnce(&mut crate::pac::uart::regs::UartlcrH) -> R) -> R { + let r = info.regs; // Notes from PL011 reference manual: // @@ -978,11 +1008,11 @@ impl<'d, T: Instance + 'd, M: Mode> Uart<'d, T, M> { /// sets baudrate on runtime pub fn set_baudrate(&mut self, baudrate: u32) { - Self::set_baudrate_inner(baudrate); + Self::set_baudrate_inner(self.tx.info, baudrate); } - fn set_baudrate_inner(baudrate: u32) { - let r = T::regs(); + fn set_baudrate_inner(info: &Info, baudrate: u32) { + let r = info.regs; let clk_base = crate::clocks::clk_peri_freq(); @@ -1002,11 +1032,11 @@ impl<'d, T: Instance + 'd, M: Mode> Uart<'d, T, M> { r.uartibrd().write_value(pac::uart::regs::Uartibrd(baud_ibrd)); r.uartfbrd().write_value(pac::uart::regs::Uartfbrd(baud_fbrd)); - Self::lcr_modify(|_| {}); + Self::lcr_modify(info, |_| {}); } } -impl<'d, T: Instance, M: Mode> Uart<'d, T, M> { +impl<'d, M: Mode> Uart<'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) @@ -1034,19 +1064,19 @@ impl<'d, T: Instance, M: Mode> Uart<'d, T, M> { /// Split the Uart into a transmitter and receiver, which is particularly /// useful when having two tasks correlating to transmitting and receiving. - pub fn split(self) -> (UartTx<'d, T, M>, UartRx<'d, T, M>) { + pub fn split(self) -> (UartTx<'d, M>, UartRx<'d, M>) { (self.tx, self.rx) } /// Split the Uart 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 UartTx<'d, T, M>, &mut UartRx<'d, T, M>) { + pub fn split_ref(&mut self) -> (&mut UartTx<'d, M>, &mut UartRx<'d, M>) { (&mut self.tx, &mut self.rx) } } -impl<'d, T: Instance> Uart<'d, T, Async> { +impl<'d> Uart<'d, Async> { /// Write to UART TX from the provided buffer. pub async fn write(&mut self, buffer: &[u8]) -> Result<(), Error> { self.tx.write(buffer).await @@ -1076,10 +1106,10 @@ impl<'d, T: Instance> Uart<'d, T, Async> { } } -impl<'d, T: Instance, M: Mode> embedded_hal_02::serial::Read for UartRx<'d, T, M> { +impl<'d, M: Mode> embedded_hal_02::serial::Read for UartRx<'d, M> { type Error = Error; fn read(&mut self) -> Result> { - let r = T::regs(); + let r = self.info.regs; if r.uartfr().read().rxfe() { return Err(nb::Error::WouldBlock); } @@ -1100,11 +1130,11 @@ impl<'d, T: Instance, M: Mode> embedded_hal_02::serial::Read for UartRx<'d, } } -impl<'d, T: Instance, M: Mode> embedded_hal_02::serial::Write for UartTx<'d, T, M> { +impl<'d, M: Mode> embedded_hal_02::serial::Write for UartTx<'d, M> { type Error = Error; fn write(&mut self, word: u8) -> Result<(), nb::Error> { - let r = T::regs(); + let r = self.info.regs; if r.uartfr().read().txff() { return Err(nb::Error::WouldBlock); } @@ -1114,7 +1144,7 @@ impl<'d, T: Instance, M: Mode> embedded_hal_02::serial::Write for UartTx<'d, } fn flush(&mut self) -> Result<(), nb::Error> { - let r = T::regs(); + let r = self.info.regs; if !r.uartfr().read().txfe() { return Err(nb::Error::WouldBlock); } @@ -1122,7 +1152,7 @@ impl<'d, T: Instance, M: Mode> embedded_hal_02::serial::Write for UartTx<'d, } } -impl<'d, T: Instance, M: Mode> embedded_hal_02::blocking::serial::Write for UartTx<'d, T, M> { +impl<'d, M: Mode> embedded_hal_02::blocking::serial::Write for UartTx<'d, M> { type Error = Error; fn bwrite_all(&mut self, buffer: &[u8]) -> Result<(), Self::Error> { @@ -1134,7 +1164,7 @@ impl<'d, T: Instance, M: Mode> embedded_hal_02::blocking::serial::Write for } } -impl<'d, T: Instance, M: Mode> embedded_hal_02::serial::Read for Uart<'d, T, M> { +impl<'d, M: Mode> embedded_hal_02::serial::Read for Uart<'d, M> { type Error = Error; fn read(&mut self) -> Result> { @@ -1142,7 +1172,7 @@ impl<'d, T: Instance, M: Mode> embedded_hal_02::serial::Read for Uart<'d, T, } } -impl<'d, T: Instance, M: Mode> embedded_hal_02::serial::Write for Uart<'d, T, M> { +impl<'d, M: Mode> embedded_hal_02::serial::Write for Uart<'d, M> { type Error = Error; fn write(&mut self, word: u8) -> Result<(), nb::Error> { @@ -1154,7 +1184,7 @@ impl<'d, T: Instance, M: Mode> embedded_hal_02::serial::Write for Uart<'d, T } } -impl<'d, T: Instance, M: Mode> embedded_hal_02::blocking::serial::Write for Uart<'d, T, M> { +impl<'d, M: Mode> embedded_hal_02::blocking::serial::Write for Uart<'d, M> { type Error = Error; fn bwrite_all(&mut self, buffer: &[u8]) -> Result<(), Self::Error> { @@ -1177,21 +1207,21 @@ impl embedded_hal_nb::serial::Error for Error { } } -impl<'d, T: Instance, M: Mode> embedded_hal_nb::serial::ErrorType for UartRx<'d, T, M> { +impl<'d, M: Mode> embedded_hal_nb::serial::ErrorType for UartRx<'d, M> { type Error = Error; } -impl<'d, T: Instance, M: Mode> embedded_hal_nb::serial::ErrorType for UartTx<'d, T, M> { +impl<'d, M: Mode> embedded_hal_nb::serial::ErrorType for UartTx<'d, M> { type Error = Error; } -impl<'d, T: Instance, M: Mode> embedded_hal_nb::serial::ErrorType for Uart<'d, T, M> { +impl<'d, M: Mode> embedded_hal_nb::serial::ErrorType for Uart<'d, M> { type Error = Error; } -impl<'d, T: Instance, M: Mode> embedded_hal_nb::serial::Read for UartRx<'d, T, M> { +impl<'d, M: Mode> embedded_hal_nb::serial::Read for UartRx<'d, M> { fn read(&mut self) -> nb::Result { - let r = T::regs(); + let r = self.info.regs; if r.uartfr().read().rxfe() { return Err(nb::Error::WouldBlock); } @@ -1212,7 +1242,7 @@ impl<'d, T: Instance, M: Mode> embedded_hal_nb::serial::Read for UartRx<'d, T, M } } -impl<'d, T: Instance, M: Mode> embedded_hal_nb::serial::Write for UartTx<'d, T, M> { +impl<'d, M: Mode> embedded_hal_nb::serial::Write for UartTx<'d, M> { fn write(&mut self, char: u8) -> nb::Result<(), Self::Error> { self.blocking_write(&[char]).map_err(nb::Error::Other) } @@ -1222,11 +1252,11 @@ impl<'d, T: Instance, M: Mode> embedded_hal_nb::serial::Write for UartTx<'d, T, } } -impl<'d, T: Instance> embedded_io::ErrorType for UartTx<'d, T, Blocking> { +impl<'d> embedded_io::ErrorType for UartTx<'d, Blocking> { type Error = Error; } -impl<'d, T: Instance> embedded_io::Write for UartTx<'d, T, Blocking> { +impl<'d> embedded_io::Write for UartTx<'d, Blocking> { fn write(&mut self, buf: &[u8]) -> Result { self.blocking_write(buf).map(|_| buf.len()) } @@ -1236,13 +1266,13 @@ impl<'d, T: Instance> embedded_io::Write for UartTx<'d, T, Blocking> { } } -impl<'d, T: Instance, M: Mode> embedded_hal_nb::serial::Read for Uart<'d, T, M> { +impl<'d, M: Mode> embedded_hal_nb::serial::Read for Uart<'d, M> { fn read(&mut self) -> Result> { embedded_hal_02::serial::Read::read(&mut self.rx) } } -impl<'d, T: Instance, M: Mode> embedded_hal_nb::serial::Write for Uart<'d, T, M> { +impl<'d, M: Mode> embedded_hal_nb::serial::Write for Uart<'d, M> { fn write(&mut self, char: u8) -> nb::Result<(), Self::Error> { self.blocking_write(&[char]).map_err(nb::Error::Other) } @@ -1252,11 +1282,11 @@ impl<'d, T: Instance, M: Mode> embedded_hal_nb::serial::Write for Uart<'d, T, M> } } -impl<'d, T: Instance> embedded_io::ErrorType for Uart<'d, T, Blocking> { +impl<'d> embedded_io::ErrorType for Uart<'d, Blocking> { type Error = Error; } -impl<'d, T: Instance> embedded_io::Write for Uart<'d, T, Blocking> { +impl<'d> embedded_io::Write for Uart<'d, Blocking> { fn write(&mut self, buf: &[u8]) -> Result { self.blocking_write(buf).map(|_| buf.len()) } @@ -1266,13 +1296,17 @@ impl<'d, T: Instance> embedded_io::Write for Uart<'d, T, Blocking> { } } +struct Info { + regs: pac::uart::Uart, + tx_dreq: pac::dma::vals::TreqSel, + rx_dreq: pac::dma::vals::TreqSel, + interrupt: Interrupt, +} + trait SealedMode {} trait SealedInstance { - const TX_DREQ: pac::dma::vals::TreqSel; - const RX_DREQ: pac::dma::vals::TreqSel; - - fn regs() -> pac::uart::Uart; + fn info() -> &'static Info; fn buffered_state() -> &'static buffered::State; @@ -1308,11 +1342,14 @@ pub trait Instance: SealedInstance + PeripheralType { macro_rules! impl_instance { ($inst:ident, $irq:ident, $tx_dreq:expr, $rx_dreq:expr) => { impl SealedInstance for peripherals::$inst { - const TX_DREQ: pac::dma::vals::TreqSel = $tx_dreq; - const RX_DREQ: pac::dma::vals::TreqSel = $rx_dreq; - - fn regs() -> pac::uart::Uart { - pac::$inst + fn info() -> &'static Info { + static INFO: Info = Info { + regs: pac::$inst, + tx_dreq: $tx_dreq, + rx_dreq: $rx_dreq, + interrupt: crate::interrupt::typelevel::$irq::IRQ, + }; + &INFO } fn buffered_state() -> &'static buffered::State { From 1efe59ec475aa38948d1fc95feb34860f08c8dcd Mon Sep 17 00:00:00 2001 From: Marc <35759328+marcemmers@users.noreply.github.com> Date: Fri, 2 May 2025 12:52:04 +0200 Subject: [PATCH 49/97] Update examples and tests --- examples/rp/src/bin/uart_buffered_split.rs | 2 +- examples/rp/src/bin/uart_unidir.rs | 2 +- tests/rp/src/bin/uart.rs | 6 +++--- tests/rp/src/bin/uart_buffered.rs | 6 +++--- tests/rp/src/bin/uart_dma.rs | 6 +++--- 5 files changed, 11 insertions(+), 11 deletions(-) diff --git a/examples/rp/src/bin/uart_buffered_split.rs b/examples/rp/src/bin/uart_buffered_split.rs index da7e94139..3adbc18ab 100644 --- a/examples/rp/src/bin/uart_buffered_split.rs +++ b/examples/rp/src/bin/uart_buffered_split.rs @@ -48,7 +48,7 @@ async fn main(spawner: Spawner) { } #[embassy_executor::task] -async fn reader(mut rx: BufferedUartRx<'static, UART0>) { +async fn reader(mut rx: BufferedUartRx) { info!("Reading..."); loop { let mut buf = [0; 31]; diff --git a/examples/rp/src/bin/uart_unidir.rs b/examples/rp/src/bin/uart_unidir.rs index a45f40756..c2c8dfad8 100644 --- a/examples/rp/src/bin/uart_unidir.rs +++ b/examples/rp/src/bin/uart_unidir.rs @@ -39,7 +39,7 @@ async fn main(spawner: Spawner) { } #[embassy_executor::task] -async fn reader(mut rx: UartRx<'static, UART1, Async>) { +async fn reader(mut rx: UartRx<'static, Async>) { info!("Reading..."); loop { // read a total of 4 transmissions (32 / 8) and then print the result diff --git a/tests/rp/src/bin/uart.rs b/tests/rp/src/bin/uart.rs index 84744ab77..80230f3fe 100644 --- a/tests/rp/src/bin/uart.rs +++ b/tests/rp/src/bin/uart.rs @@ -8,17 +8,17 @@ teleprobe_meta::target!(b"pimoroni-pico-plus-2"); use defmt::{assert_eq, *}; use embassy_executor::Spawner; use embassy_rp::gpio::{Level, Output}; -use embassy_rp::uart::{Blocking, Config, Error, Instance, Parity, Uart, UartRx}; +use embassy_rp::uart::{Blocking, Config, Error, Parity, Uart, UartRx}; use embassy_time::Timer; use {defmt_rtt as _, panic_probe as _}; -fn read(uart: &mut Uart<'_, impl Instance, Blocking>) -> Result<[u8; N], Error> { +fn read(uart: &mut Uart<'_, Blocking>) -> Result<[u8; N], Error> { let mut buf = [255; N]; uart.blocking_read(&mut buf)?; Ok(buf) } -fn read1(uart: &mut UartRx<'_, impl Instance, Blocking>) -> Result<[u8; N], Error> { +fn read1(uart: &mut UartRx<'_, Blocking>) -> Result<[u8; N], Error> { let mut buf = [255; N]; uart.blocking_read(&mut buf)?; Ok(buf) diff --git a/tests/rp/src/bin/uart_buffered.rs b/tests/rp/src/bin/uart_buffered.rs index d5f655e9b..cb78fc142 100644 --- a/tests/rp/src/bin/uart_buffered.rs +++ b/tests/rp/src/bin/uart_buffered.rs @@ -10,7 +10,7 @@ use embassy_executor::Spawner; use embassy_rp::bind_interrupts; use embassy_rp::gpio::{Level, Output}; use embassy_rp::peripherals::UART0; -use embassy_rp::uart::{BufferedInterruptHandler, BufferedUart, BufferedUartRx, Config, Error, Instance, Parity}; +use embassy_rp::uart::{BufferedInterruptHandler, BufferedUart, BufferedUartRx, Config, Error, Parity}; use embassy_time::Timer; use embedded_io_async::{Read, ReadExactError, Write}; use {defmt_rtt as _, panic_probe as _}; @@ -19,7 +19,7 @@ bind_interrupts!(struct Irqs { UART0_IRQ => BufferedInterruptHandler; }); -async fn read(uart: &mut BufferedUart<'_, impl Instance>) -> Result<[u8; N], Error> { +async fn read(uart: &mut BufferedUart) -> Result<[u8; N], Error> { let mut buf = [255; N]; match uart.read_exact(&mut buf).await { Ok(()) => Ok(buf), @@ -29,7 +29,7 @@ async fn read(uart: &mut BufferedUart<'_, impl Instance>) -> Res } } -async fn read1(uart: &mut BufferedUartRx<'_, impl Instance>) -> Result<[u8; N], Error> { +async fn read1(uart: &mut BufferedUartRx) -> Result<[u8; N], Error> { let mut buf = [255; N]; match uart.read_exact(&mut buf).await { Ok(()) => Ok(buf), diff --git a/tests/rp/src/bin/uart_dma.rs b/tests/rp/src/bin/uart_dma.rs index a09101223..a7af81f5f 100644 --- a/tests/rp/src/bin/uart_dma.rs +++ b/tests/rp/src/bin/uart_dma.rs @@ -10,7 +10,7 @@ use embassy_executor::Spawner; use embassy_rp::bind_interrupts; use embassy_rp::gpio::{Level, Output}; use embassy_rp::peripherals::UART0; -use embassy_rp::uart::{Async, Config, Error, Instance, InterruptHandler, Parity, Uart, UartRx}; +use embassy_rp::uart::{Async, Config, Error, InterruptHandler, Parity, Uart, UartRx}; use embassy_time::Timer; use {defmt_rtt as _, panic_probe as _}; @@ -18,13 +18,13 @@ bind_interrupts!(struct Irqs { UART0_IRQ => InterruptHandler; }); -async fn read(uart: &mut Uart<'_, impl Instance, Async>) -> Result<[u8; N], Error> { +async fn read(uart: &mut Uart<'_, Async>) -> Result<[u8; N], Error> { let mut buf = [255; N]; uart.read(&mut buf).await?; Ok(buf) } -async fn read1(uart: &mut UartRx<'_, impl Instance, Async>) -> Result<[u8; N], Error> { +async fn read1(uart: &mut UartRx<'_, Async>) -> Result<[u8; N], Error> { let mut buf = [255; N]; uart.read(&mut buf).await?; Ok(buf) From 407540c8fe5061dfd245d605775fea36c2dd00ce Mon Sep 17 00:00:00 2001 From: Marc <35759328+marcemmers@users.noreply.github.com> Date: Fri, 2 May 2025 13:32:09 +0200 Subject: [PATCH 50/97] Remove some forgotten commented out code --- embassy-rp/src/uart/buffered.rs | 2 -- 1 file changed, 2 deletions(-) diff --git a/embassy-rp/src/uart/buffered.rs b/embassy-rp/src/uart/buffered.rs index 44b6ee469..02649ad81 100644 --- a/embassy-rp/src/uart/buffered.rs +++ b/embassy-rp/src/uart/buffered.rs @@ -43,14 +43,12 @@ pub struct BufferedUart { pub struct BufferedUartRx { pub(super) info: &'static Info, pub(super) state: &'static State, - // pub(crate) phantom: PhantomData<&'d mut T>, } /// Buffered UART TX handle. pub struct BufferedUartTx { pub(super) info: &'static Info, pub(super) state: &'static State, - // pub(crate) phantom: PhantomData<&'d mut T>, } pub(super) fn init_buffers<'d>( From d799af9dd82cda9cef3e5de91ae3e8555ad1d2d2 Mon Sep 17 00:00:00 2001 From: Marc <35759328+marcemmers@users.noreply.github.com> Date: Fri, 2 May 2025 13:40:50 +0200 Subject: [PATCH 51/97] Replace generic inner with arguments --- embassy-rp/src/uart/mod.rs | 33 +++++++++++++++++++-------------- 1 file changed, 19 insertions(+), 14 deletions(-) diff --git a/embassy-rp/src/uart/mod.rs b/embassy-rp/src/uart/mod.rs index d36884109..c3a15fda5 100644 --- a/embassy-rp/src/uart/mod.rs +++ b/embassy-rp/src/uart/mod.rs @@ -165,12 +165,12 @@ impl<'d, M: Mode> UartTx<'d, M> { config: Config, ) -> Self { Uart::::init(T::info(), Some(tx.into()), None, None, None, config); - Self::new_inner::(Some(tx_dma.into())) + Self::new_inner(T::info(), Some(tx_dma.into())) } - fn new_inner(tx_dma: Option>) -> Self { + fn new_inner(info: &'static Info, tx_dma: Option>) -> Self { Self { - info: T::info(), + info, tx_dma, phantom: PhantomData, } @@ -230,7 +230,7 @@ impl<'d> UartTx<'d, Blocking> { /// Create a new UART TX instance for blocking mode operations. pub fn new_blocking(_uart: Peri<'d, T>, tx: Peri<'d, impl TxPin>, config: Config) -> Self { Uart::::init(T::info(), Some(tx.into()), None, None, None, config); - Self::new_inner::(None) + Self::new_inner(T::info(), None) } /// Convert this uart TX instance into a buffered uart using the provided @@ -281,20 +281,25 @@ impl<'d, M: Mode> UartRx<'d, M> { config: Config, ) -> Self { Uart::::init(T::info(), None, Some(rx.into()), None, None, config); - Self::new_inner::(true, Some(rx_dma.into())) + Self::new_inner(T::info(), T::dma_state(), true, Some(rx_dma.into())) } - fn new_inner(has_irq: bool, rx_dma: Option>) -> Self { + fn new_inner( + info: &'static Info, + dma_state: &'static DmaState, + has_irq: bool, + rx_dma: Option>, + ) -> Self { debug_assert_eq!(has_irq, rx_dma.is_some()); if has_irq { // disable all error interrupts initially - T::info().regs.uartimsc().write(|w| w.0 = 0); - T::Interrupt::unpend(); - unsafe { T::Interrupt::enable() }; + info.regs.uartimsc().write(|w| w.0 = 0); + info.interrupt.unpend(); + unsafe { info.interrupt.enable() }; } Self { - info: T::info(), - dma_state: T::dma_state(), + info, + dma_state, rx_dma, phantom: PhantomData, } @@ -355,7 +360,7 @@ impl<'d> UartRx<'d, Blocking> { /// Create a new UART RX instance for blocking mode operations. pub fn new_blocking(_uart: Peri<'d, T>, rx: Peri<'d, impl RxPin>, config: Config) -> Self { Uart::::init(T::info(), None, Some(rx.into()), None, None, config); - Self::new_inner::(false, None) + Self::new_inner(T::info(), T::dma_state(), false, None) } /// Convert this uart RX instance into a buffered uart using the provided @@ -835,8 +840,8 @@ impl<'d, M: Mode> Uart<'d, M> { ); Self { - tx: UartTx::new_inner::(tx_dma), - rx: UartRx::new_inner::(has_irq, rx_dma), + tx: UartTx::new_inner(T::info(), tx_dma), + rx: UartRx::new_inner(T::info(), T::dma_state(), has_irq, rx_dma), } } From 371373886bb6cfa2a082835d87786a2916c74562 Mon Sep 17 00:00:00 2001 From: Marc <35759328+marcemmers@users.noreply.github.com> Date: Fri, 2 May 2025 14:23:58 +0200 Subject: [PATCH 52/97] Fix examples for rp235x --- examples/rp235x/src/bin/sharing.rs | 2 +- examples/rp235x/src/bin/uart_buffered_split.rs | 2 +- examples/rp235x/src/bin/uart_unidir.rs | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/examples/rp235x/src/bin/sharing.rs b/examples/rp235x/src/bin/sharing.rs index 5416e20ce..497c4f845 100644 --- a/examples/rp235x/src/bin/sharing.rs +++ b/examples/rp235x/src/bin/sharing.rs @@ -31,7 +31,7 @@ use rand::RngCore; use static_cell::{ConstStaticCell, StaticCell}; use {defmt_rtt as _, panic_probe as _}; -type UartAsyncMutex = mutex::Mutex>; +type UartAsyncMutex = mutex::Mutex>; struct MyType { inner: u32, diff --git a/examples/rp235x/src/bin/uart_buffered_split.rs b/examples/rp235x/src/bin/uart_buffered_split.rs index f707c4b5e..7cad09f9b 100644 --- a/examples/rp235x/src/bin/uart_buffered_split.rs +++ b/examples/rp235x/src/bin/uart_buffered_split.rs @@ -48,7 +48,7 @@ async fn main(spawner: Spawner) { } #[embassy_executor::task] -async fn reader(mut rx: BufferedUartRx<'static, UART0>) { +async fn reader(mut rx: BufferedUartRx) { info!("Reading..."); loop { let mut buf = [0; 31]; diff --git a/examples/rp235x/src/bin/uart_unidir.rs b/examples/rp235x/src/bin/uart_unidir.rs index 4e98f9e1e..45c9c8407 100644 --- a/examples/rp235x/src/bin/uart_unidir.rs +++ b/examples/rp235x/src/bin/uart_unidir.rs @@ -39,7 +39,7 @@ async fn main(spawner: Spawner) { } #[embassy_executor::task] -async fn reader(mut rx: UartRx<'static, UART1, Async>) { +async fn reader(mut rx: UartRx<'static, Async>) { info!("Reading..."); loop { // read a total of 4 transmissions (32 / 8) and then print the result From 8f3b6643b311a5a16fe6a64227066627990452b9 Mon Sep 17 00:00:00 2001 From: Marc <35759328+marcemmers@users.noreply.github.com> Date: Fri, 2 May 2025 14:36:08 +0200 Subject: [PATCH 53/97] And another fix, should be the last failing example --- examples/rp/src/bin/sharing.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/rp/src/bin/sharing.rs b/examples/rp/src/bin/sharing.rs index 5416e20ce..497c4f845 100644 --- a/examples/rp/src/bin/sharing.rs +++ b/examples/rp/src/bin/sharing.rs @@ -31,7 +31,7 @@ use rand::RngCore; use static_cell::{ConstStaticCell, StaticCell}; use {defmt_rtt as _, panic_probe as _}; -type UartAsyncMutex = mutex::Mutex>; +type UartAsyncMutex = mutex::Mutex>; struct MyType { inner: u32, From 3441e805070c7efb7cad20a84d1986e215b4de3d Mon Sep 17 00:00:00 2001 From: 1-rafael-1 Date: Fri, 2 May 2025 23:51:28 +0200 Subject: [PATCH 54/97] first batch of changes after review --- embassy-rp/src/clocks.rs | 322 +++++++++--------------- examples/rp/src/bin/overclock.rs | 11 +- examples/rp/src/bin/overclock_manual.rs | 20 +- tests/rp/src/bin/overclock.rs | 10 +- 4 files changed, 133 insertions(+), 230 deletions(-) diff --git a/embassy-rp/src/clocks.rs b/embassy-rp/src/clocks.rs index 005564b8b..107e499b7 100644 --- a/embassy-rp/src/clocks.rs +++ b/embassy-rp/src/clocks.rs @@ -9,9 +9,8 @@ //! //! For most users, these functions provide an easy way to configure clocks: //! -//! - `ClockConfig::crystal(12_000_000)` - Default configuration with 12MHz crystal giving 125MHz system clock //! - `ClockConfig::at_sys_frequency_mhz(200)` - Set system clock to a specific frequency with automatic voltage scaling -//! - `ClockConfig::with_external_crystal(16_000_000)` - Configure with a non-standard crystal frequency +//! - `ClockConfig::crystal(12_000_000)` - Default configuration with 12MHz crystal giving 125MHz system clock //! //! ## Manual Configuration //! @@ -34,19 +33,9 @@ //! }); //! //! // Set voltage for overclocking -//! config.voltage_scale = Some(VoltageScale::V1_15); +//! config.core_voltage = CoreVoltage::V1_15; //! ``` //! -//! ## Voltage Scaling for Overclocking (RP2040 only) -//! -//! When overclocking beyond 133MHz, higher core voltages are needed: -//! -//! - Up to 133MHz: `VoltageScale::V1_10` (default) -//! - 133-200MHz: `VoltageScale::V1_15` -//! - Above 200MHz: `VoltageScale::V1_20` or higher -//! -//! The `at_sys_frequency_mhz()` function automatically sets appropriate voltages. -//! //! ## Examples //! //! ### Standard 125MHz configuration @@ -61,16 +50,16 @@ //! //! ### Overclock to 200MHz //! ```rust,ignore -//! let config = ClockConfig::at_sys_frequency_mhz(200); +//! let config = ClockConfig::crystal_freq(200_000_000); //! ``` //! //! ### Manual configuration for advanced scenarios //! ```rust,ignore -//! use embassy_rp::clocks::{ClockConfig, XoscConfig, PllConfig, VoltageScale}; +//! use embassy_rp::clocks::{ClockConfig, XoscConfig, PllConfig, CoreVoltage}; //! //! // Start with defaults and customize //! let mut config = ClockConfig::default(); -//! config.voltage_scale = Some(VoltageScale::V1_15); +//! config.core_voltage = CoreVoltage::V1_15; //! // Set other parameters as needed... //! ``` @@ -144,14 +133,16 @@ pub enum PeriClkSrc { // Gpin1 = ClkPeriCtrlAuxsrc::CLKSRC_GPIN1 as _ , } -/// Core voltage scaling options for RP2040. +/// Core voltage regulator settings for RP2040. /// /// The RP2040 voltage regulator can be configured for different output voltages. /// Higher voltages allow for higher clock frequencies but increase power consumption and heat. #[cfg(feature = "rp2040")] #[derive(Clone, Copy, Debug, PartialEq, Eq)] #[repr(u8)] -pub enum VoltageScale { +pub enum CoreVoltage { + /// 0.80V - Suitable for lower frequencies + V0_80 = 0b0000, /// 0.85V V0_85 = 0b0110, /// 0.90V @@ -162,11 +153,11 @@ pub enum VoltageScale { V1_00 = 0b1001, /// 1.05V V1_05 = 0b1010, - /// 1.10V + /// 1.10V - Default voltage level V1_10 = 0b1011, - /// 1.15V + /// 1.15V - Required for overclocking to 133-200MHz V1_15 = 0b1100, - /// 1.20V + /// 1.20V - Required for overclocking above 200MHz V1_20 = 0b1101, /// 1.25V V1_25 = 0b1110, @@ -175,21 +166,22 @@ pub enum VoltageScale { } #[cfg(feature = "rp2040")] -impl VoltageScale { +impl CoreVoltage { /// Get the recommended Brown-Out Detection (BOD) setting for this voltage. /// Sets the BOD threshold to approximately 90% of the core voltage. fn recommended_bod(self) -> u8 { match self { - VoltageScale::V0_85 => 0b0111, // 0.774V (~91% of 0.85V) - VoltageScale::V0_90 => 0b1000, // 0.817V (~91% of 0.90V) - VoltageScale::V0_95 => 0b1001, // 0.860V (~91% of 0.95V) - VoltageScale::V1_00 => 0b1010, // 0.903V (~90% of 1.00V) - VoltageScale::V1_05 => 0b1011, // 0.946V (~90% of 1.05V) - VoltageScale::V1_10 => 0b1100, // 0.989V (~90% of 1.10V) - VoltageScale::V1_15 => 0b1101, // 1.032V (~90% of 1.15V) - VoltageScale::V1_20 => 0b1110, // 1.075V (~90% of 1.20V) - VoltageScale::V1_25 => 0b1111, // 1.118V (~89% of 1.25V) - VoltageScale::V1_30 => 0b1111, // 1.118V (~86% of 1.30V) - using max available threshold + CoreVoltage::V0_80 => 0b0110, // 0.720V (~90% of 0.80V) + CoreVoltage::V0_85 => 0b0111, // 0.774V (~91% of 0.85V) + CoreVoltage::V0_90 => 0b1000, // 0.817V (~91% of 0.90V) + CoreVoltage::V0_95 => 0b1001, // 0.860V (~91% of 0.95V) + CoreVoltage::V1_00 => 0b1010, // 0.903V (~90% of 1.00V) + CoreVoltage::V1_05 => 0b1011, // 0.946V (~90% of 1.05V) + CoreVoltage::V1_10 => 0b1100, // 0.989V (~90% of 1.10V) + CoreVoltage::V1_15 => 0b1101, // 1.032V (~90% of 1.15V) + CoreVoltage::V1_20 => 0b1110, // 1.075V (~90% of 1.20V) + CoreVoltage::V1_25 => 0b1111, // 1.118V (~89% of 1.25V) + CoreVoltage::V1_30 => 0b1111, // 1.118V (~86% of 1.30V) - using max available threshold } } } @@ -214,9 +206,9 @@ pub struct ClockConfig { /// RTC clock configuration. #[cfg(feature = "rp2040")] pub rtc_clk: Option, - /// Core voltage scaling (RP2040 only). Defaults to 1.10V if None. + /// Core voltage scaling (RP2040 only). Defaults to 1.10V. #[cfg(feature = "rp2040")] - pub voltage_scale: Option, + pub core_voltage: CoreVoltage, /// Voltage stabilization delay in microseconds. /// If not set, defaults will be used based on voltage level. #[cfg(feature = "rp2040")] @@ -255,7 +247,7 @@ impl Default for ClockConfig { #[cfg(feature = "rp2040")] rtc_clk: None, #[cfg(feature = "rp2040")] - voltage_scale: None, + core_voltage: CoreVoltage::V1_10, #[cfg(feature = "rp2040")] voltage_stabilization_delay_us: None, // gpin0: None, @@ -327,7 +319,7 @@ impl ClockConfig { phase: 0, }), #[cfg(feature = "rp2040")] - voltage_scale: None, // Use hardware default (1.10V) + core_voltage: CoreVoltage::V1_10, // Use hardware default (1.10V) #[cfg(feature = "rp2040")] voltage_stabilization_delay_us: None, // gpin0: None, @@ -371,7 +363,7 @@ impl ClockConfig { phase: 0, }), #[cfg(feature = "rp2040")] - voltage_scale: None, // Use hardware default (1.10V) + core_voltage: CoreVoltage::V1_10, // Use hardware default (1.10V) #[cfg(feature = "rp2040")] voltage_stabilization_delay_us: None, // gpin0: None, @@ -379,146 +371,59 @@ impl ClockConfig { } } - /// Configure the system clock to a specific frequency in MHz. - /// - /// This is a user-friendly way to configure the system clock, similar to - /// the Pico SDK's approach. It automatically handles voltage scaling based on the - /// requested frequency and uses the standard 12MHz crystal found on most RP2040 boards. - /// - /// # Arguments - /// - /// * `sys_freq_mhz` - The target system clock frequency in MHz - /// - /// # Example - /// - /// ```rust,ignore - /// // Overclock to 200MHz - /// let config = ClockConfig::at_sys_frequency_mhz(200); - /// ``` - #[cfg(feature = "rp2040")] - pub fn at_sys_frequency_mhz(sys_freq_mhz: u32) -> Self { - // For 125MHz, use exactly the same config as the default to avoid any differences - if sys_freq_mhz == 125 { - return Self::crystal(12_000_000); - } - - // For other frequencies, provide appropriate voltage scaling and PLL configuration - // Standard crystal on Raspberry Pi Pico boards is 12MHz - const DEFAULT_CRYSTAL_HZ: u32 = 12_000_000; - - let sys_freq_hz = sys_freq_mhz * 1_000_000; - let config = Self::crystal_freq(DEFAULT_CRYSTAL_HZ, sys_freq_hz); - - config - } - - /// Configure the system clock to a specific frequency in Hz, using a custom crystal frequency. - /// - /// This more flexible version allows specifying both the crystal frequency and target - /// system frequency for boards with non-standard crystals. - /// - /// # Arguments - /// - /// * `crystal_hz` - The frequency of the external crystal in Hz - /// * `sys_freq_hz` - The target system clock frequency in Hz - /// - /// # Example - /// - /// ```rust,ignore - /// // Use a non-standard 16MHz crystal to achieve 250MHz - /// let config = ClockConfig::with_custom_crystal(16_000_000, 250_000_000); - /// ``` - #[cfg(feature = "rp2040")] - pub fn with_custom_crystal(crystal_hz: u32, sys_freq_hz: u32) -> Self { - Self::crystal_freq(crystal_hz, sys_freq_hz) - } - /// Configure clocks derived from an external crystal with specific system frequency. /// /// This function calculates optimal PLL parameters to achieve the requested system - /// frequency from the given crystal frequency. It's used internally by higher-level - /// configuration functions. + /// frequency. This only works for the usual 12MHz crystal. In case a different crystal is used, + /// You will have to set the PLL parameters manually. /// /// # Arguments /// - /// * `crystal_hz` - The frequency of the external crystal in Hz /// * `sys_freq_hz` - The desired system clock frequency in Hz /// /// # Returns /// /// A ClockConfig configured to achieve the requested system frequency using the - /// specified crystal, or panic if no valid parameters can be found. + /// the usual 12Mhz crystal, or panic if no valid parameters can be found. + /// + /// # Note on core voltage: + /// To date the only officially documented core voltages (see Datasheet section 2.15.3.1. Instances) are: + /// - Up to 133MHz: V1_10 (default) + /// - Above 133MHz: V1_15, but in the context of the datasheet covering reaching up to 200Mhz + /// That way all other frequencies below 133MHz or above 200MHz are not explicitly documented and not covered here. + /// In case You want to go below 133MHz or above 200MHz and want a different voltage, You will have to set that manually and with caution. #[cfg(feature = "rp2040")] - fn crystal_freq(crystal_hz: u32, sys_freq_hz: u32) -> Self { + pub fn crystal_freq(sys_freq_hz: u32) -> Self { + // Start with the standard configuration from crystal() + const DEFAULT_CRYSTAL_HZ: u32 = 12_000_000; + let mut config = Self::crystal(DEFAULT_CRYSTAL_HZ); + + // No need to modify anything if target frequency is already 125MHz + // (which is what crystal() configures by default) + if sys_freq_hz == 125_000_000 { + return config; + } + // Find optimal PLL parameters for the requested frequency - let sys_pll_params = find_pll_params(crystal_hz, sys_freq_hz) + let sys_pll_params = find_pll_params(DEFAULT_CRYSTAL_HZ, sys_freq_hz) .unwrap_or_else(|| panic!("Could not find valid PLL parameters for system clock")); + // Replace the sys_pll configuration with our custom parameters + if let Some(xosc) = &mut config.xosc { + xosc.sys_pll = Some(sys_pll_params); + } + // Set the voltage scale based on the target frequency // Higher frequencies require higher voltage - let voltage_scale = match sys_freq_hz { - freq if freq > 200_000_000 => Some(VoltageScale::V1_20), - freq if freq > 133_000_000 => Some(VoltageScale::V1_15), - _ => None, // Use default voltage (V1_10) - }; - - // For USB PLL, we always want 48MHz for USB - let usb_pll_params = if crystal_hz == 12_000_000 { - // For standard 12MHz crystal, use the default parameters - PllConfig { - refdiv: 1, - fbdiv: 120, - post_div1: 6, - post_div2: 5, - } - } else { - // For other crystals, calculate parameters to get 48MHz - find_pll_params(crystal_hz, 48_000_000) - .unwrap_or_else(|| panic!("Could not find valid PLL parameters for USB clock")) - }; - - Self { - rosc: Some(RoscConfig { - hz: 6_500_000, - range: RoscRange::Medium, - drive_strength: [0; 8], - div: 16, - }), - xosc: Some(XoscConfig { - hz: crystal_hz, - sys_pll: Some(sys_pll_params), - usb_pll: Some(usb_pll_params), - delay_multiplier: 128, - }), - ref_clk: RefClkConfig { - src: RefClkSrc::Xosc, - div: 1, - }, - sys_clk: SysClkConfig { - src: SysClkSrc::PllSys, - div_int: 1, - div_frac: 0, - }, - peri_clk_src: Some(PeriClkSrc::Sys), - usb_clk: Some(UsbClkConfig { - src: UsbClkSrc::PllUsb, - div: 1, - phase: 0, - }), - adc_clk: Some(AdcClkConfig { - src: AdcClkSrc::PllUsb, - div: 1, - phase: 0, - }), - rtc_clk: Some(RtcClkConfig { - src: RtcClkSrc::PllUsb, - div_int: 1024, - div_frac: 0, - phase: 0, - }), - voltage_scale, - voltage_stabilization_delay_us: None, + #[cfg(feature = "rp2040")] + { + config.core_voltage = match sys_freq_hz { + freq if freq > 133_000_000 => CoreVoltage::V1_15, + _ => CoreVoltage::V1_10, // Use default voltage (V1_10) + }; } + + config } /// Configure with manual PLL settings for full control over system clock @@ -530,7 +435,7 @@ impl ClockConfig { /// /// * `xosc_hz` - The frequency of the external crystal in Hz /// * `pll_config` - The PLL configuration parameters to achieve desired frequency - /// * `voltage_scale` - Optional voltage scaling for overclocking (required for >133MHz) + /// * `core_voltage` - Voltage scaling for overclocking (required for >133MHz) /// /// # Returns /// @@ -549,11 +454,11 @@ impl ClockConfig { /// post_div1: 3, // First post divider (1200 MHz / 3 = 400 MHz) /// post_div2: 2, // Second post divider (400 MHz / 2 = 200 MHz) /// }, - /// Some(VoltageScale::V1_15) + /// CoreVoltage::V1_15 /// ); /// ``` #[cfg(feature = "rp2040")] - pub fn manual_pll(xosc_hz: u32, pll_config: PllConfig, voltage_scale: Option) -> Self { + pub fn manual_pll(xosc_hz: u32, pll_config: PllConfig, core_voltage: CoreVoltage) -> Self { // Calculate the actual output frequency for documentation // let ref_freq = xosc_hz / pll_config.refdiv as u32; // let vco_freq = ref_freq * pll_config.fbdiv as u32; @@ -587,7 +492,7 @@ impl ClockConfig { div_frac: 0, }; - config.voltage_scale = voltage_scale; + config.core_voltage = core_voltage; config.peri_clk_src = Some(PeriClkSrc::Sys); // Set reasonable defaults for other clocks @@ -865,7 +770,7 @@ pub struct RtcClkConfig { /// /// # Parameters /// -/// * `input_hz`: The input frequency in Hz (typically the crystal frequency, e.g. 12MHz) +/// * `input_hz`: The input frequency in Hz (typically the crystal frequency, e.g. 12MHz for th most common one used on rp2040 boards) /// * `target_hz`: The desired output frequency in Hz (e.g. 125MHz for standard RP2040 operation) /// /// # Returns @@ -990,7 +895,8 @@ pub(crate) unsafe fn init(config: ClockConfig) { // Set Core Voltage (RP2040 only), if we have config for it and we're not using the default #[cfg(feature = "rp2040")] - if let Some(voltage) = config.voltage_scale { + { + let voltage = config.core_voltage; let vreg = pac::VREG_AND_CHIP_RESET; let current_vsel = vreg.vreg().read().vsel(); let target_vsel = voltage as u8; @@ -1002,9 +908,9 @@ pub(crate) unsafe fn init(config: ClockConfig) { // Wait for the voltage to stabilize. Use the provided delay or default based on voltage let settling_time_us = config.voltage_stabilization_delay_us.unwrap_or_else(|| { match voltage { - VoltageScale::V1_15 => 1000, // 1ms for 1.15V - VoltageScale::V1_20 | VoltageScale::V1_25 | VoltageScale::V1_30 => 2000, // 2ms for higher voltages - _ => 0, // no delay for all others + CoreVoltage::V1_15 => 1000, // 1ms for 1.15V + CoreVoltage::V1_20 | CoreVoltage::V1_25 | CoreVoltage::V1_30 => 2000, // 2ms for higher voltages + _ => 0, // no delay for all others } }); @@ -1042,9 +948,8 @@ pub(crate) unsafe fn init(config: ClockConfig) { }; CLOCKS.xosc.store(xosc_freq, Ordering::Relaxed); - // SETUP TEMPORARY STABLE CLOCKS FIRST + // Setup temporary stable clocks first // Configure USB PLL for our stable temporary clock - // This follows the SDK's approach of using USB PLL as a stable intermediate clock let pll_usb_freq = match &config.xosc { Some(config) => match &config.usb_pll { Some(pll_usb_config) => { @@ -1055,7 +960,13 @@ pub(crate) unsafe fn init(config: ClockConfig) { reset::unreset_wait(peris); // Configure the USB PLL - this should give us 48MHz - let usb_pll_freq = configure_pll(pac::PLL_USB, xosc_freq, *pll_usb_config); + let usb_pll_freq = match configure_pll(pac::PLL_USB, xosc_freq, *pll_usb_config) { + Ok(freq) => freq, + Err(_) => { + panic!("Failed to configure USB PLL"); + } + }; + CLOCKS.pll_usb.store(usb_pll_freq, Ordering::Relaxed); usb_pll_freq } @@ -1074,7 +985,7 @@ pub(crate) unsafe fn init(config: ClockConfig) { while c.clk_ref_selected().read() != pac::clocks::regs::ClkRefSelected(1 << ClkRefCtrlSrc::XOSC_CLKSRC as u32) {} // First switch the system clock to a stable source (USB PLL at 48MHz) - // This follows the Pico SDK's approach to ensure stability during reconfiguration + // This follows the official Pico SDK's approach to ensure stability during reconfiguration c.clk_sys_ctrl().write(|w| { w.set_auxsrc(ClkSysCtrlAuxsrc::CLKSRC_PLL_USB); w.set_src(ClkSysCtrlSrc::CLKSRC_CLK_SYS_AUX); @@ -1101,7 +1012,12 @@ pub(crate) unsafe fn init(config: ClockConfig) { reset::unreset_wait(peris); // Configure the SYS PLL - let pll_sys_freq = configure_pll(pac::PLL_SYS, xosc_freq, *sys_pll_config); + let pll_sys_freq = match configure_pll(pac::PLL_SYS, xosc_freq, *sys_pll_config) { + Ok(freq) => freq, + Err(_) => { + panic!("Failed to configure system PLL"); + } + }; // Ensure PLL is locked and stable cortex_m::asm::delay(100); @@ -1411,7 +1327,7 @@ fn start_xosc(crystal_hz: u32, delay_multiplier: u32) { /// PLL (Phase-Locked Loop) configuration #[inline(always)] -fn configure_pll(p: pac::pll::Pll, input_freq: u32, config: PllConfig) -> u32 { +fn configure_pll(p: pac::pll::Pll, input_freq: u32, config: PllConfig) -> Result { // Calculate reference frequency let ref_freq = input_freq / config.refdiv as u32; @@ -1482,7 +1398,7 @@ fn configure_pll(p: pac::pll::Pll, input_freq: u32, config: PllConfig) -> u32 { timeout -= 1; if timeout == 0 { // PLL failed to lock, return 0 to indicate failure - return 0; + return Err("PLL failed to lock"); } } @@ -1502,7 +1418,7 @@ fn configure_pll(p: pac::pll::Pll, input_freq: u32, config: PllConfig) -> u32 { cortex_m::asm::delay(100); // Calculate and return actual output frequency - vco_freq / ((config.post_div1 * config.post_div2) as u32) + Ok(vco_freq / ((config.post_div1 * config.post_div2) as u32)) } /// General purpose input clock pin. @@ -1883,25 +1799,6 @@ pub fn dormant_sleep() { mod tests { use super::*; - #[cfg(feature = "rp2040")] - #[test] - fn test_voltage_scale_bod_values() { - // Test that each voltage level maps to the correct BOD threshold (approx. 90% of VDD) - // This verifies our BOD settings match our documentation - { - assert_eq!(VoltageScale::V0_85.recommended_bod(), 0b0111); // ~0.774V (91% of 0.85V) - assert_eq!(VoltageScale::V0_90.recommended_bod(), 0b1000); // ~0.817V (91% of 0.90V) - assert_eq!(VoltageScale::V0_95.recommended_bod(), 0b1001); // ~0.860V (91% of 0.95V) - assert_eq!(VoltageScale::V1_00.recommended_bod(), 0b1010); // ~0.903V (90% of 1.00V) - assert_eq!(VoltageScale::V1_05.recommended_bod(), 0b1011); // ~0.946V (90% of 1.05V) - assert_eq!(VoltageScale::V1_10.recommended_bod(), 0b1100); // ~0.989V (90% of 1.10V) - assert_eq!(VoltageScale::V1_15.recommended_bod(), 0b1101); // ~1.032V (90% of 1.15V) - assert_eq!(VoltageScale::V1_20.recommended_bod(), 0b1110); // ~1.075V (90% of 1.20V) - assert_eq!(VoltageScale::V1_25.recommended_bod(), 0b1111); // ~1.118V (89% of 1.25V) - assert_eq!(VoltageScale::V1_30.recommended_bod(), 0b1111); // ~1.118V (86% of 1.30V) - using max available - } - } - #[cfg(feature = "rp2040")] #[test] fn test_find_pll_params() { @@ -1942,7 +1839,12 @@ mod tests { let vco_freq = (16_000_000 / params.refdiv as u32) as u64 * params.fbdiv as u64; let output_freq = (vco_freq / ((params.post_div1 * params.post_div2) as u64)) as u32; - // With a 16 MHz crystal, we might not get exactly 125 MHz + // Test non-standard crystal with 15 MHz + let params = find_pll_params(15_000_000, 125_000_000).unwrap(); + let vco_freq = (15_000_000 / params.refdiv as u32) as u64 * params.fbdiv as u64; + let output_freq = (vco_freq / ((params.post_div1 * params.post_div2) as u64)) as u32; + + // With a 15 MHz crystal, we might not get exactly 125 MHz // Check that it's close enough (within 0.2% margin) let freq_diff = if output_freq > 125_000_000 { output_freq - 125_000_000 @@ -2033,11 +1935,11 @@ mod tests { post_div1: 3, post_div2: 2, }, - Some(VoltageScale::V1_15), + CoreVoltage::V1_15, ); // Check voltage scale was set correctly - assert_eq!(config.voltage_scale, Some(VoltageScale::V1_15)); + assert_eq!(config.core_voltage, CoreVoltage::V1_15); // Check PLL config was set correctly assert_eq!(config.xosc.as_ref().unwrap().sys_pll.as_ref().unwrap().refdiv, 1); @@ -2065,19 +1967,23 @@ mod tests { fn test_auto_voltage_scaling() { { // Test automatic voltage scaling based on frequency - // Under 133 MHz should use default voltage (None) - let config = ClockConfig::at_sys_frequency_mhz(125); - assert_eq!(config.voltage_scale, None); + // Under 133 MHz should use default voltage (V1_10) + let config = ClockConfig::crystal_freq(125_000_000); + assert_eq!(config.core_voltage, CoreVoltage::V1_10); // 133-200 MHz should use V1_15 - let config = ClockConfig::at_sys_frequency_mhz(150); - assert_eq!(config.voltage_scale, Some(VoltageScale::V1_15)); - let config = ClockConfig::at_sys_frequency_mhz(200); - assert_eq!(config.voltage_scale, Some(VoltageScale::V1_15)); + let config = ClockConfig::crystal_freq(150_000_000); + assert_eq!(config.core_voltage, CoreVoltage::V1_15); + let config = ClockConfig::crystal_freq(200_000_000); + assert_eq!(config.core_voltage, CoreVoltage::V1_15); - // Above 200 MHz should use V1_20 - let config = ClockConfig::at_sys_frequency_mhz(250); - assert_eq!(config.voltage_scale, Some(VoltageScale::V1_20)); + // Above 200 MHz should use V1_25 + let config = ClockConfig::crystal_freq(250_000_000); + assert_eq!(config.core_voltage, CoreVoltage::V1_15); + + // Below 125 MHz should use V1_10 + let config = ClockConfig::crystal_freq(100_000_000); + assert_eq!(config.core_voltage, CoreVoltage::V1_10); } } } diff --git a/examples/rp/src/bin/overclock.rs b/examples/rp/src/bin/overclock.rs index e3ac77340..f9a8c94d0 100644 --- a/examples/rp/src/bin/overclock.rs +++ b/examples/rp/src/bin/overclock.rs @@ -1,6 +1,6 @@ //! # Overclocking the RP2040 to 200 MHz //! -//! This example demonstrates how to configure the RP2040 to run at 200 MHz using a higher level API. +//! This example demonstrates how to configure the RP2040 to run at 200 MHz. #![no_std] #![no_main] @@ -17,19 +17,18 @@ const COUNT_TO: i64 = 10_000_000; #[embassy_executor::main] async fn main(_spawner: Spawner) -> ! { - // Set up for clock frequency of 200 MHz - // This will set all the necessary defaults including slightly raised voltage - let config = Config::new(ClockConfig::at_sys_frequency_mhz(200)); + // Set up for clock frequency of 200 MHz, setting all necessary defaults. + let config = Config::new(ClockConfig::crystal_freq(200_000_000)); // Show the voltage scale for verification - info!("System core voltage: {}", Debug2Format(&config.clocks.voltage_scale)); + info!("System core voltage: {}", Debug2Format(&config.clocks.core_voltage)); // Initialize the peripherals let p = embassy_rp::init(config); // Show CPU frequency for verification let sys_freq = clk_sys_freq(); - info!("System clock frequency: {} Hz", sys_freq); + info!("System clock frequency: {} MHz", sys_freq / 1_000_000); // LED to indicate the system is running let mut led = Output::new(p.PIN_25, Level::Low); diff --git a/examples/rp/src/bin/overclock_manual.rs b/examples/rp/src/bin/overclock_manual.rs index ad6abf0e7..35160b250 100644 --- a/examples/rp/src/bin/overclock_manual.rs +++ b/examples/rp/src/bin/overclock_manual.rs @@ -8,7 +8,7 @@ use defmt::*; use embassy_executor::Spawner; use embassy_rp::clocks; -use embassy_rp::clocks::{ClockConfig, PllConfig, VoltageScale}; +use embassy_rp::clocks::{ClockConfig, CoreVoltage, PllConfig}; use embassy_rp::config::Config; use embassy_rp::gpio::{Level, Output}; use embassy_time::{Duration, Instant, Timer}; @@ -16,23 +16,21 @@ use {defmt_rtt as _, panic_probe as _}; const COUNT_TO: i64 = 10_000_000; -/// Configure the RP2040 for 200 MHz operation by manually specifying -/// all the required parameters instead of using higher-level APIs. +/// Configure the RP2040 for 200 MHz operation by manually specifying the PLL settings. fn configure_manual_overclock() -> Config { // Set the PLL configuration manually, starting from default values let mut config = Config::default(); - // Set the system clock to 200 MHz using a PLL with a reference frequency of 12 MHz + // Set the system clock to 200 MHz config.clocks = ClockConfig::manual_pll( - 12_000_000, + 12_000_000, // Crystal frequency, 12 MHz is common. If using custom, set to your value. PllConfig { - refdiv: 1, - fbdiv: 100, - post_div1: 3, - post_div2: 2, + refdiv: 1, // Reference divider + fbdiv: 100, // Feedback divider + post_div1: 3, // Post divider 1 + post_div2: 2, // Post divider 2 }, - // For 200 MHz, we need a voltage scale of 1.15V - Some(VoltageScale::V1_15), + CoreVoltage::V1_15, // Core voltage, should be set to V1_15 for 200 MHz ); config diff --git a/tests/rp/src/bin/overclock.rs b/tests/rp/src/bin/overclock.rs index e4845a55f..6c58a6b90 100644 --- a/tests/rp/src/bin/overclock.rs +++ b/tests/rp/src/bin/overclock.rs @@ -14,7 +14,7 @@ use embassy_rp::clocks; #[cfg(feature = "rp2040")] use embassy_rp::clocks::ClockConfig; #[cfg(feature = "rp2040")] -use embassy_rp::clocks::VoltageScale; +use embassy_rp::clocks::CoreVoltage; use embassy_rp::config::Config; use embassy_time::Instant; use {defmt_rtt as _, panic_probe as _}; @@ -31,15 +31,15 @@ async fn main(_spawner: Spawner) { // Initialize with 200MHz clock configuration for RP2040, other chips will use default clock #[cfg(feature = "rp2040")] { - config.clocks = ClockConfig::at_sys_frequency_mhz(200); - let voltage = config.clocks.voltage_scale.unwrap(); - assert!(matches!(voltage, VoltageScale::V1_15), "Expected voltage scale V1_15"); + config.clocks = ClockConfig::crystal_freq(200_000_000); + let voltage = config.clocks.core_voltage; + assert!(matches!(voltage, CoreVoltage::V1_15), "Expected voltage scale V1_15"); } let _p = embassy_rp::init(config); + // Test the system speed let (time_elapsed, clk_sys_freq) = { - // Test the system speed let mut counter = 0; let start = Instant::now(); while counter < COUNT_TO { From e5c03e1e791bf3460fe6e0af65a02f2259763eaa Mon Sep 17 00:00:00 2001 From: Yuri Astrakhan Date: Fri, 2 May 2025 17:56:32 -0400 Subject: [PATCH 55/97] chore: use `probe-rs` instead of `probe-run` I found a few remaining deprecated `probe-run` cases. --- docs/examples/basic/.cargo/config.toml | 4 ++-- docs/examples/layer-by-layer/.cargo/config.toml | 3 ++- docs/pages/getting_started.adoc | 2 +- 3 files changed, 5 insertions(+), 4 deletions(-) diff --git a/docs/examples/basic/.cargo/config.toml b/docs/examples/basic/.cargo/config.toml index 8ca28df39..17616a054 100644 --- a/docs/examples/basic/.cargo/config.toml +++ b/docs/examples/basic/.cargo/config.toml @@ -1,6 +1,6 @@ [target.'cfg(all(target_arch = "arm", target_os = "none"))'] -# replace nRF82840_xxAA with your chip as listed in `probe-run --list-chips` -runner = "probe-run --chip nRF52840_xxAA" +# 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" diff --git a/docs/examples/layer-by-layer/.cargo/config.toml b/docs/examples/layer-by-layer/.cargo/config.toml index 3012f05dc..f30d9e446 100644 --- a/docs/examples/layer-by-layer/.cargo/config.toml +++ b/docs/examples/layer-by-layer/.cargo/config.toml @@ -1,5 +1,6 @@ [target.'cfg(all(target_arch = "arm", target_os = "none"))'] -runner = "probe-run --chip STM32L475VG" +# replace your chip as listed in `probe-rs chip list` +runner = "probe-rs run --chip STM32L475VG" rustflags = [ "-C", "link-arg=--nmagic", diff --git a/docs/pages/getting_started.adoc b/docs/pages/getting_started.adoc index 954f3fd28..d1f65a885 100644 --- a/docs/pages/getting_started.adoc +++ b/docs/pages/getting_started.adoc @@ -66,7 +66,7 @@ If everything worked correctly, you should see a blinking LED on your board, and [source] ---- Finished dev [unoptimized + debuginfo] target(s) in 1m 56s - Running `probe-run --chip STM32F407VGTx target/thumbv7em-none-eabi/debug/blinky` + Running `probe-rs run --chip STM32F407VGTx target/thumbv7em-none-eabi/debug/blinky` (HOST) INFO flashing program (71.36 KiB) (HOST) INFO success! ──────────────────────────────────────────────────────────────────────────────── From 7f259aaff26bb060e49f91f894ff00e257e1a744 Mon Sep 17 00:00:00 2001 From: Yuri Astrakhan Date: Fri, 2 May 2025 21:54:21 -0400 Subject: [PATCH 56/97] chore: minor readme cleanup use simpler markdown linking, minor grammar cleanup --- README.md | 85 +++++++++++++++++++++++++++++-------------------------- 1 file changed, 45 insertions(+), 40 deletions(-) diff --git a/README.md b/README.md index 383fb6671..669fa469b 100644 --- a/README.md +++ b/README.md @@ -1,52 +1,55 @@ # Embassy -Embassy is the next-generation framework for embedded applications. Write safe, correct and energy-efficient embedded code faster, using the Rust programming language, its async facilities, and the Embassy libraries. +Embassy is the next-generation framework for embedded applications. Write safe, correct, and energy-efficient embedded code faster, using the Rust programming language, its async facilities, and the Embassy libraries. + +## [Documentation](https://embassy.dev/book/index.html) - [API reference](https://docs.embassy.dev/) - [Website](https://embassy.dev/) - [Chat](https://matrix.to/#/#embassy-rs:matrix.org) -## Documentation - API reference - Website - Chat ## Rust + async ❤️ embedded -The Rust programming language is blazingly fast and memory-efficient, with no runtime, garbage collector or OS. It catches a wide variety of bugs at compile time, thanks to its full memory- and thread-safety, and expressive type system. +The Rust programming language is blazingly fast and memory-efficient, with no runtime, garbage collector, or OS. It catches a wide variety of bugs at compile time, thanks to its full memory- and thread-safety, and expressive type system. -Rust's async/await allows for unprecedentedly easy and efficient multitasking in embedded systems. Tasks get transformed at compile time into state machines that get run cooperatively. It requires no dynamic memory allocation, and runs on a single stack, so no per-task stack size tuning is required. It obsoletes the need for a traditional RTOS with kernel context switching, and is faster and smaller than one! +Rust's [async/await](https://rust-lang.github.io/async-book/) allows for unprecedentedly easy and efficient multitasking in embedded systems. Tasks get transformed at compile time into state machines that get run cooperatively. It requires no dynamic memory allocation and runs on a single stack, so no per-task stack size tuning is required. It obsoletes the need for a traditional RTOS with kernel context switching, and is [faster and smaller than one!](https://tweedegolf.nl/en/blog/65/async-rust-vs-rtos-showdown) ## Batteries included -- **Hardware Abstraction Layers** - HALs implement safe, idiomatic Rust APIs to use the hardware capabilities, so raw register manipulation is not needed. The Embassy project maintains HALs for select hardware, but you can still use HALs from other projects with Embassy. - - embassy-stm32, for all STM32 microcontroller families. - - embassy-nrf, for the Nordic Semiconductor nRF52, nRF53, nRF54 and nRF91 series. - - embassy-rp, for the Raspberry Pi RP2040 and RP23xx microcontrollers. - - embassy-mspm0, for the Texas Instruments MSPM0 microcontrollers. - - esp-rs, for the Espressif Systems ESP32 series of chips. - - Embassy HAL support for Espressif chips, as well as Async WiFi, Bluetooth and ESP-NOW, is being developed in the [esp-rs/esp-hal](https://github.com/esp-rs/esp-hal) repository. - - ch32-hal, for the WCH 32-bit RISC-V(CH32V) series of chips. - - mpfs-hal, for the Microchip PolarFire SoC. - - py32-hal, for the Puya Semiconductor PY32 series of microcontrollers. +- **Hardware Abstraction Layers + ** - HALs implement safe, idiomatic Rust APIs to use the hardware capabilities, so raw register manipulation is not needed. The Embassy project maintains HALs for select hardware, but you can still use HALs from other projects with Embassy. + - [embassy-stm32](https://docs.embassy.dev/embassy-stm32/), for all STM32 microcontroller families. + - [embassy-nrf](https://docs.embassy.dev/embassy-nrf/), for the Nordic Semiconductor nRF52, nRF53, nRF54 and nRF91 series. + - [embassy-rp](https://docs.embassy.dev/embassy-rp/), for the Raspberry Pi RP2040 and RP23xx microcontrollers. + - [embassy-mspm0](https://docs.embassy.dev/embassy-mspm0/), for the Texas Instruments MSPM0 microcontrollers. + - [esp-rs](https://github.com/esp-rs), for the Espressif Systems ESP32 series of chips. + - Embassy HAL support for Espressif chips, as well as Async Wi-Fi, Bluetooth, and ESP-NOW, is being developed in the [esp-rs/esp-hal](https://github.com/esp-rs/esp-hal) repository. + - [ch32-hal](https://github.com/ch32-rs/ch32-hal), for the WCH 32-bit RISC-V(CH32V) series of chips. + - [mpfs-hal](https://github.com/AlexCharlton/mpfs-hal), for the Microchip PolarFire SoC. + - [py32-hal](https://github.com/py32-rs/py32-hal), for the Puya Semiconductor PY32 series of microcontrollers. -- **Time that Just Works** - -No more messing with hardware timers. embassy_time provides Instant, Duration and Timer types that are globally available and never overflow. +- **Time that Just Works** - + No more messing with hardware timers. [embassy_time](https://docs.embassy.dev/embassy-time) provides Instant, Duration, and Timer types that are globally available and never overflow. -- **Real-time ready** - -Tasks on the same async executor run cooperatively, but you can create multiple executors with different priorities, so that higher priority tasks preempt lower priority ones. See the example. +- **Real-time ready** - + Tasks on the same async executor run cooperatively, but you can create multiple executors with different priorities so that higher priority tasks preempt lower priority ones. See the [example](https://github.com/embassy-rs/embassy/blob/master/examples/nrf52840/src/bin/multiprio.rs). -- **Low-power ready** - -Easily build devices with years of battery life. The async executor automatically puts the core to sleep when there's no work to do. Tasks are woken by interrupts, there is no busy-loop polling while waiting. - -- **Networking** - -The embassy-net network stack implements extensive networking functionality, including Ethernet, IP, TCP, UDP, ICMP and DHCP. Async drastically simplifies managing timeouts and serving multiple connections concurrently. +- **Low-power ready** - + Easily build devices with years of battery life. The async executor automatically puts the core to sleep when there's no work to do. Tasks are woken by interrupts, there is no busy-loop polling while waiting. + +- **Networking** - + The [embassy-net](https://docs.embassy.dev/embassy-net/) network stack implements extensive networking functionality, including Ethernet, IP, TCP, UDP, ICMP, and DHCP. Async drastically simplifies managing timeouts and serving multiple connections concurrently. - **Bluetooth** - - The trouble crate provides a Bluetooth Low Energy 4.x and 5.x Host that runs on any microcontroller implementing the bt-hci traits (currently `nRF52`, `rp2040`, `rp23xx` and `esp32` and `serial` controllers are supported). - - The nrf-softdevice crate provides Bluetooth Low Energy 4.x and 5.x support for nRF52 microcontrollers. - - The embassy-stm32-wpan crate provides Bluetooth Low Energy 5.x support for stm32wb microcontrollers. + - The [trouble](https://github.com/embassy-rs/trouble) crate provides a Bluetooth Low Energy 4.x and 5.x Host that runs on any microcontroller implementing the [bt-hci](https://github.com/embassy-rs/bt-hci) traits (currently + `nRF52`, `rp2040`, `rp23xx` and `esp32` and `serial` controllers are supported). + - The [nrf-softdevice](https://github.com/embassy-rs/nrf-softdevice) crate provides Bluetooth Low Energy 4.x and 5.x support for nRF52 microcontrollers. + - The [embassy-stm32-wpan](https://github.com/embassy-rs/embassy/tree/main/embassy-stm32-wpan) crate provides Bluetooth Low Energy 5.x support for stm32wb microcontrollers. -- **LoRa** - The lora-rs project provides an async LoRa and LoRaWAN stack that works well on Embassy. +- **LoRa** - + The [lora-rs](https://github.com/lora-rs/lora-rs) project provides an async LoRa and LoRaWAN stack that works well on Embassy. -- **USB** - -embassy-usb implements a device-side USB stack. Implementations for common classes such as USB serial (CDC ACM) and USB HID are available, and a rich builder API allows building your own. - -- **Bootloader and DFU** - -embassy-boot is a lightweight bootloader supporting firmware application upgrades in a power-fail-safe way, with trial boots and rollbacks. +- **USB** - + [embassy-usb](https://docs.embassy.dev/embassy-usb/) implements a device-side USB stack. Implementations for common classes such as USB serial (CDC ACM) and USB HID are available, and a rich builder API allows building your own. +- **Bootloader and DFU** - + [embassy-boot](https://github.com/embassy-rs/embassy/tree/master/embassy-boot) is a lightweight bootloader supporting firmware application upgrades in a power-fail-safe way, with trial boots and rollbacks. ## Sneak peek @@ -93,13 +96,15 @@ async fn main(spawner: Spawner) { ## Examples -Examples are found in the `examples/` folder separated by the chip manufacturer they are designed to run on. For example: +Examples are found in the +`examples/` folder separated by the chip manufacturer they are designed to run on. For example: -* `examples/nrf52840` run on the `nrf52840-dk` board (PCA10056) but should be easily adaptable to other nRF52 chips and boards. -* `examples/nrf5340` run on the `nrf5340-dk` board (PCA10095). -* `examples/stm32xx` for the various STM32 families. -* `examples/rp` are for the RP2040 chip. -* `examples/std` are designed to run locally on your PC. +* `examples/nrf52840` run on the + `nrf52840-dk` board (PCA10056) but should be easily adaptable to other nRF52 chips and boards. +* `examples/nrf5340` run on the `nrf5340-dk` board (PCA10095). +* `examples/stm32xx` for the various STM32 families. +* `examples/rp` are for the RP2040 chip. +* `examples/std` are designed to run locally on your PC. ### Running examples @@ -126,7 +131,7 @@ cargo run --release --bin blinky For more help getting started, see [Getting Started][1] and [Running the Examples][2]. -## Developing Embassy with Rust Analyzer based editors +## Developing Embassy with Rust Analyzer-based editors The [Rust Analyzer](https://rust-analyzer.github.io/) is used by [Visual Studio Code](https://code.visualstudio.com/) and others. Given the multiple targets that Embassy serves, there is no Cargo workspace file. Instead, the Rust Analyzer @@ -136,7 +141,7 @@ please refer to the `.vscode/settings.json` file's `rust-analyzer.linkedProjects ## Minimum supported Rust version (MSRV) Embassy is guaranteed to compile on stable Rust 1.75 and up. It *might* -compile with older versions but that may change in any new patch release. +compile with older versions, but that may change in any new patch release. ## Why the name? From 3d9cac361ed6bd0cd00cab7bf924608c89de9108 Mon Sep 17 00:00:00 2001 From: 1-rafael-1 Date: Sat, 3 May 2025 14:46:30 +0200 Subject: [PATCH 57/97] Add PIO clock divider utility and refactor related modules --- embassy-rp/src/pio_programs/clock_divider.rs | 25 +++++++++++++++++++ embassy-rp/src/pio_programs/hd44780.rs | 14 ++++------- embassy-rp/src/pio_programs/mod.rs | 1 + embassy-rp/src/pio_programs/rotary_encoder.rs | 10 +++----- embassy-rp/src/pio_programs/stepper.rs | 18 ++++++------- 5 files changed, 43 insertions(+), 25 deletions(-) create mode 100644 embassy-rp/src/pio_programs/clock_divider.rs diff --git a/embassy-rp/src/pio_programs/clock_divider.rs b/embassy-rp/src/pio_programs/clock_divider.rs new file mode 100644 index 000000000..02e353f53 --- /dev/null +++ b/embassy-rp/src/pio_programs/clock_divider.rs @@ -0,0 +1,25 @@ +//! Helper functions for calculating PIO clock dividers + +use fixed::traits::ToFixed; +use fixed::types::extra::U8; + +use crate::clocks::clk_sys_freq; + +/// Calculate a PIO clock divider value based on the desired target frequency. +/// +/// # Arguments +/// +/// * `target_hz` - The desired PIO clock frequency in Hz +/// +/// # Returns +/// +/// A fixed-point divider value suitable for use in a PIO state machine configuration +#[inline] +pub fn calculate_pio_clock_divider(target_hz: u32) -> fixed::FixedU32 { + // Requires a non-zero frequency + assert!(target_hz > 0, "PIO clock frequency cannot be zero"); + + // Calculate the divider + let divider = (clk_sys_freq() + target_hz / 2) / target_hz; + divider.to_fixed() +} diff --git a/embassy-rp/src/pio_programs/hd44780.rs b/embassy-rp/src/pio_programs/hd44780.rs index 3aa54495f..546c85a89 100644 --- a/embassy-rp/src/pio_programs/hd44780.rs +++ b/embassy-rp/src/pio_programs/hd44780.rs @@ -1,11 +1,11 @@ //! [HD44780 display driver](https://www.sparkfun.com/datasheets/LCD/HD44780.pdf) -use crate::clocks::clk_sys_freq; use crate::dma::{AnyChannel, Channel}; use crate::pio::{ Common, Config, Direction, FifoJoin, Instance, Irq, LoadedProgram, PioPin, ShiftConfig, ShiftDirection, StateMachine, }; +use crate::pio_programs::clock_divider::calculate_pio_clock_divider; use crate::Peri; /// This struct represents a HD44780 program that takes command words ( <0:4>) @@ -136,10 +136,8 @@ impl<'l, P: Instance, const S: usize> PioHD44780<'l, P, S> { let mut cfg = Config::default(); cfg.use_program(&word_prg.prg, &[&e]); - // Scale the divider based on system clock frequency - // Original: 125 at 125 MHz (1 MHz PIO clock) - let word_divider = (clk_sys_freq() / 1_000_000) as u8; // Target 1 MHz PIO clock - cfg.clock_divider = word_divider.into(); + // Target 1 MHz PIO clock (each cycle is 1µs) + cfg.clock_divider = calculate_pio_clock_divider(1_000_000); cfg.set_out_pins(&[&db4, &db5, &db6, &db7]); cfg.shift_out = ShiftConfig { @@ -167,10 +165,8 @@ impl<'l, P: Instance, const S: usize> PioHD44780<'l, P, S> { let mut cfg = Config::default(); cfg.use_program(&seq_prg.prg, &[&e]); - // Original: 8 at 125 MHz (~15.6 MHz PIO clock) - // Comment says ~64ns/insn which is 1/(15.6 MHz) = ~64ns - let seq_divider = (clk_sys_freq() / 15_600_000) as u8; // Target ~15.6 MHz PIO clock (~64ns/insn) - cfg.clock_divider = seq_divider.into(); + // Target ~15.6 MHz PIO clock (~64ns/insn) + cfg.clock_divider = calculate_pio_clock_divider(15_600_000); cfg.set_jmp_pin(&db7); cfg.set_set_pins(&[&rs, &rw]); diff --git a/embassy-rp/src/pio_programs/mod.rs b/embassy-rp/src/pio_programs/mod.rs index 74537825b..8eac328b3 100644 --- a/embassy-rp/src/pio_programs/mod.rs +++ b/embassy-rp/src/pio_programs/mod.rs @@ -1,5 +1,6 @@ //! Pre-built pio programs for common interfaces +pub mod clock_divider; pub mod hd44780; pub mod i2s; pub mod onewire; diff --git a/embassy-rp/src/pio_programs/rotary_encoder.rs b/embassy-rp/src/pio_programs/rotary_encoder.rs index 71567a602..70b3795e9 100644 --- a/embassy-rp/src/pio_programs/rotary_encoder.rs +++ b/embassy-rp/src/pio_programs/rotary_encoder.rs @@ -1,12 +1,10 @@ //! PIO backed quadrature encoder -use fixed::traits::ToFixed; - -use crate::clocks::clk_sys_freq; use crate::gpio::Pull; use crate::pio::{ Common, Config, Direction as PioDirection, FifoJoin, Instance, LoadedProgram, PioPin, ShiftDirection, StateMachine, }; +use crate::pio_programs::clock_divider::calculate_pio_clock_divider; use crate::Peri; /// This struct represents an Encoder program loaded into pio instruction memory. @@ -50,10 +48,8 @@ impl<'d, T: Instance, const SM: usize> PioEncoder<'d, T, SM> { cfg.fifo_join = FifoJoin::RxOnly; cfg.shift_in.direction = ShiftDirection::Left; - // Original: 10_000 at 125 MHz (12.5 KHz PIO clock) - // Scale divider to maintain same PIO clock frequency at different system clocks - let divider = (clk_sys_freq() as f32 / 12_500.0).to_fixed(); - cfg.clock_divider = divider; + // Target 12.5 KHz PIO clock + cfg.clock_divider = calculate_pio_clock_divider(12_500); cfg.use_program(&program.prg, &[]); sm.set_config(&cfg); diff --git a/embassy-rp/src/pio_programs/stepper.rs b/embassy-rp/src/pio_programs/stepper.rs index 6878c32f5..0e9a8daf9 100644 --- a/embassy-rp/src/pio_programs/stepper.rs +++ b/embassy-rp/src/pio_programs/stepper.rs @@ -2,12 +2,8 @@ use core::mem::{self, MaybeUninit}; -use fixed::traits::ToFixed; -use fixed::types::extra::U8; -use fixed::FixedU32; - -use crate::clocks::clk_sys_freq; use crate::pio::{Common, Config, Direction, Instance, Irq, LoadedProgram, PioPin, StateMachine}; +use crate::pio_programs::clock_divider::calculate_pio_clock_divider; use crate::Peri; /// This struct represents a Stepper driver program loaded into pio instruction memory. @@ -65,7 +61,9 @@ impl<'d, T: Instance, const SM: usize> PioStepper<'d, T, SM> { sm.set_pin_dirs(Direction::Out, &[&pin0, &pin1, &pin2, &pin3]); let mut cfg = Config::default(); cfg.set_out_pins(&[&pin0, &pin1, &pin2, &pin3]); - cfg.clock_divider = (clk_sys_freq() / (100 * 136)).to_fixed(); + + cfg.clock_divider = calculate_pio_clock_divider(100 * 136); + cfg.use_program(&program.prg, &[]); sm.set_config(&cfg); sm.set_enable(true); @@ -74,9 +72,11 @@ impl<'d, T: Instance, const SM: usize> PioStepper<'d, T, SM> { /// Set pulse frequency pub fn set_frequency(&mut self, freq: u32) { - let clock_divider: FixedU32 = (clk_sys_freq() / (freq * 136)).to_fixed(); - assert!(clock_divider <= 65536, "clkdiv must be <= 65536"); - assert!(clock_divider >= 1, "clkdiv must be >= 1"); + let clock_divider = calculate_pio_clock_divider(freq * 136); + let divider_f32 = clock_divider.to_num::(); + assert!(divider_f32 <= 65536.0, "clkdiv must be <= 65536"); + assert!(divider_f32 >= 1.0, "clkdiv must be >= 1"); + self.sm.set_clock_divider(clock_divider); self.sm.clkdiv_restart(); } From 0406b83c526f03567863d80a603db77c514df27f Mon Sep 17 00:00:00 2001 From: Hiroaki Yutani Date: Sun, 4 May 2025 09:39:35 +0900 Subject: [PATCH 58/97] Tweak the example `rust-toolchain.toml` in doc --- docs/pages/project_structure.adoc | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/pages/project_structure.adoc b/docs/pages/project_structure.adoc index 722ec8d9d..227508b97 100644 --- a/docs/pages/project_structure.adoc +++ b/docs/pages/project_structure.adoc @@ -85,9 +85,9 @@ A minimal example: [source,toml] ---- [toolchain] -channel = "nightly-2023-08-19" # <- as of writing, this is the exact rust version embassy uses +channel = "1.85" # <- as of writing, this is the exact rust version embassy uses components = [ "rust-src", "rustfmt" ] # <- optionally add "llvm-tools-preview" for some extra features like "cargo size" targets = [ - "thumbv6m-none-eabi" # <-change for your platform + "thumbv6m-none-eabi" # <- change for your platform ] ---- From a42fa0a67aef90453b239b62b81df8db20664c87 Mon Sep 17 00:00:00 2001 From: Hiroaki Yutani Date: Sun, 4 May 2025 09:54:37 +0900 Subject: [PATCH 59/97] Tweak --- docs/pages/new_project.adoc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/pages/new_project.adoc b/docs/pages/new_project.adoc index af1cb75c5..cd943b4f6 100644 --- a/docs/pages/new_project.adoc +++ b/docs/pages/new_project.adoc @@ -150,7 +150,7 @@ stm32g474-example # Before upgrading check that everything is available on all tier1 targets here: # https://rust-lang.github.io/rustup-components-history [toolchain] -channel = "nightly-2023-11-01" +channel = "1.85" components = [ "rust-src", "rustfmt", "llvm-tools", "miri" ] targets = ["thumbv7em-none-eabi"] ---- From 723ebfcb4f17e48f2a614f0e1e124e8da9c75b70 Mon Sep 17 00:00:00 2001 From: JuliDi <20155974+JuliDi@users.noreply.github.com> Date: Sun, 4 May 2025 16:31:26 +0200 Subject: [PATCH 60/97] clarify docs for signal and watch --- embassy-sync/src/signal.rs | 5 +++-- embassy-sync/src/watch.rs | 4 ++-- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/embassy-sync/src/signal.rs b/embassy-sync/src/signal.rs index a0f4b5a74..e7095401e 100644 --- a/embassy-sync/src/signal.rs +++ b/embassy-sync/src/signal.rs @@ -6,7 +6,7 @@ use core::task::{Context, Poll, Waker}; use crate::blocking_mutex::raw::RawMutex; use crate::blocking_mutex::Mutex; -/// Single-slot signaling primitive. +/// Single-slot signaling primitive for a _single_ consumer. /// /// This is similar to a [`Channel`](crate::channel::Channel) with a buffer size of 1, except /// "sending" to it (calling [`Signal::signal`]) when full will overwrite the previous value instead @@ -17,6 +17,7 @@ use crate::blocking_mutex::Mutex; /// updates. /// /// For more advanced use cases, you might want to use [`Channel`](crate::channel::Channel) instead. +/// For multiple consumers, use [`Watch`](crate::watch::Watch) instead. /// /// Signals are generally declared as `static`s and then borrowed as required. /// @@ -106,7 +107,7 @@ where }) } - /// Future that completes when this Signal has been signaled. + /// Future that completes when this Signal has been signaled, taking the value out of the signal. pub fn wait(&self) -> impl Future + '_ { poll_fn(move |cx| self.poll_wait(cx)) } diff --git a/embassy-sync/src/watch.rs b/embassy-sync/src/watch.rs index e76646c0b..08d6a833d 100644 --- a/embassy-sync/src/watch.rs +++ b/embassy-sync/src/watch.rs @@ -10,7 +10,7 @@ use crate::blocking_mutex::raw::RawMutex; use crate::blocking_mutex::Mutex; use crate::waitqueue::MultiWakerRegistration; -/// The `Watch` is a single-slot signaling primitive that allows multiple receivers to concurrently await +/// The `Watch` is a single-slot signaling primitive that allows _multiple_ (`N`) receivers to concurrently await /// changes to the value. Unlike a [`Signal`](crate::signal::Signal), `Watch` supports multiple receivers, /// and unlike a [`PubSubChannel`](crate::pubsub::PubSubChannel), `Watch` immediately overwrites the previous /// value when a new one is sent, without waiting for all receivers to read the previous value. @@ -298,7 +298,7 @@ impl WatchBehavior for Watch } impl Watch { - /// Create a new `Watch` channel. + /// Create a new `Watch` channel for `N` receivers. pub const fn new() -> Self { Self { mutex: Mutex::new(RefCell::new(WatchState { From a97be9c7400cb6608ff94ef3c887ac7cc816e4ec Mon Sep 17 00:00:00 2001 From: Hiroaki Yutani Date: Mon, 5 May 2025 12:41:20 +0900 Subject: [PATCH 61/97] Handle negative value --- examples/rp235x/src/bin/pio_rotary_encoder_rxf.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/examples/rp235x/src/bin/pio_rotary_encoder_rxf.rs b/examples/rp235x/src/bin/pio_rotary_encoder_rxf.rs index ccc601661..33499babb 100644 --- a/examples/rp235x/src/bin/pio_rotary_encoder_rxf.rs +++ b/examples/rp235x/src/bin/pio_rotary_encoder_rxf.rs @@ -88,8 +88,8 @@ impl<'d, T: Instance, const SM: usize> PioEncoder<'d, T, SM> { Self { sm } } - pub async fn read(&mut self) -> u32 { - self.sm.get_rxf_entry(0) + pub async fn read(&mut self) -> i32 { + self.sm.get_rxf_entry(0) as i32 } } From 0d03aa0bec01fb0289915861d997bf7f2cf8d232 Mon Sep 17 00:00:00 2001 From: 1-rafael-1 Date: Mon, 5 May 2025 22:55:09 +0200 Subject: [PATCH 62/97] rework init() --- embassy-rp/src/clocks.rs | 266 ++++++++++++++++----------------------- 1 file changed, 108 insertions(+), 158 deletions(-) diff --git a/embassy-rp/src/clocks.rs b/embassy-rp/src/clocks.rs index 107e499b7..a4cc129ab 100644 --- a/embassy-rp/src/clocks.rs +++ b/embassy-rp/src/clocks.rs @@ -168,20 +168,20 @@ pub enum CoreVoltage { #[cfg(feature = "rp2040")] impl CoreVoltage { /// Get the recommended Brown-Out Detection (BOD) setting for this voltage. - /// Sets the BOD threshold to approximately 90% of the core voltage. + /// Sets the BOD threshold to approximately 80% of the core voltage. fn recommended_bod(self) -> u8 { match self { - CoreVoltage::V0_80 => 0b0110, // 0.720V (~90% of 0.80V) - CoreVoltage::V0_85 => 0b0111, // 0.774V (~91% of 0.85V) - CoreVoltage::V0_90 => 0b1000, // 0.817V (~91% of 0.90V) - CoreVoltage::V0_95 => 0b1001, // 0.860V (~91% of 0.95V) - CoreVoltage::V1_00 => 0b1010, // 0.903V (~90% of 1.00V) - CoreVoltage::V1_05 => 0b1011, // 0.946V (~90% of 1.05V) - CoreVoltage::V1_10 => 0b1100, // 0.989V (~90% of 1.10V) - CoreVoltage::V1_15 => 0b1101, // 1.032V (~90% of 1.15V) - CoreVoltage::V1_20 => 0b1110, // 1.075V (~90% of 1.20V) - CoreVoltage::V1_25 => 0b1111, // 1.118V (~89% of 1.25V) - CoreVoltage::V1_30 => 0b1111, // 1.118V (~86% of 1.30V) - using max available threshold + CoreVoltage::V0_80 => 0b0100, // 0.645V (~81% of 0.80V) + CoreVoltage::V0_85 => 0b0101, // 0.688V (~81% of 0.85V) + CoreVoltage::V0_90 => 0b0110, // 0.731V (~81% of 0.90V) + CoreVoltage::V0_95 => 0b0111, // 0.774V (~81% of 0.95V) + CoreVoltage::V1_00 => 0b1000, // 0.817V (~82% of 1.00V) + CoreVoltage::V1_05 => 0b1000, // 0.817V (~78% of 1.05V) + CoreVoltage::V1_10 => 0b1001, // 0.860V (~78% of 1.10V) + CoreVoltage::V1_15 => 0b1010, // 0.903V (~79% of 1.15V) + CoreVoltage::V1_20 => 0b1011, // 0.946V (~79% of 1.20V) + CoreVoltage::V1_25 => 0b1100, // 0.989V (~79% of 1.25V) + CoreVoltage::V1_30 => 0b1101, // 1.032V (~79% of 1.30V) } } } @@ -459,11 +459,6 @@ impl ClockConfig { /// ``` #[cfg(feature = "rp2040")] pub fn manual_pll(xosc_hz: u32, pll_config: PllConfig, core_voltage: CoreVoltage) -> Self { - // Calculate the actual output frequency for documentation - // let ref_freq = xosc_hz / pll_config.refdiv as u32; - // let vco_freq = ref_freq * pll_config.fbdiv as u32; - // let sys_freq = vco_freq / ((pll_config.post_div1 * pll_config.post_div2) as u32); - // Validate PLL parameters assert!(pll_config.is_valid(xosc_hz), "Invalid PLL parameters"); @@ -893,6 +888,30 @@ pub(crate) unsafe fn init(config: ClockConfig) { #[cfg(feature = "_rp235x")] while c.clk_ref_selected().read() != pac::clocks::regs::ClkRefSelected(1) {} + // Reset the PLLs + let mut peris = reset::Peripherals(0); + peris.set_pll_sys(true); + peris.set_pll_usb(true); + reset::reset(peris); + reset::unreset_wait(peris); + + // let gpin0_freq = config.gpin0.map_or(0, |p| { + // core::mem::forget(p.1); + // p.0 + // }); + // CLOCKS.gpin0.store(gpin0_freq, Ordering::Relaxed); + // let gpin1_freq = config.gpin1.map_or(0, |p| { + // core::mem::forget(p.1); + // p.0 + // }); + // CLOCKS.gpin1.store(gpin1_freq, Ordering::Relaxed); + + let rosc_freq = match config.rosc { + Some(config) => configure_rosc(config), + None => 0, + }; + CLOCKS.rosc.store(rosc_freq, Ordering::Relaxed); + // Set Core Voltage (RP2040 only), if we have config for it and we're not using the default #[cfg(feature = "rp2040")] { @@ -901,8 +920,9 @@ pub(crate) unsafe fn init(config: ClockConfig) { let current_vsel = vreg.vreg().read().vsel(); let target_vsel = voltage as u8; + // If the target voltage is different from the current one, we need to change it if target_vsel != current_vsel { - // Use modify() instead of write() to preserve the HIZ and EN bits - otherwise we will disable the regulator when changing voltage + // Use modify() to preserve the HIZ and EN bits - otherwise we will disable the regulator when changing voltage vreg.vreg().modify(|w| w.set_vsel(target_vsel)); // Wait for the voltage to stabilize. Use the provided delay or default based on voltage @@ -914,16 +934,14 @@ pub(crate) unsafe fn init(config: ClockConfig) { } }); - // We need a clock that's guaranteed to be running at this point - // Use ROSC which should be configured by now - let rosc_freq_rough = 6_000_000; // Rough ROSC frequency estimate - let cycles_per_us = rosc_freq_rough / 1_000_000; - let delay_cycles = settling_time_us * cycles_per_us; + if settling_time_us != 0 { + // Delay in microseconds, using the ROSC frequency to calculate cycles + let cycles_per_us = rosc_freq / 1_000_000; + let delay_cycles = settling_time_us * cycles_per_us; + cortex_m::asm::delay(delay_cycles); + } - // Wait for voltage to stabilize - cortex_m::asm::delay(delay_cycles); - - // Only now set the BOD level after voltage has stabilized + // Only now set the BOD level. At htis point the voltage is considered stable. vreg.bod().write(|w| { w.set_vsel(voltage.recommended_bod()); w.set_en(true); // Enable brownout detection @@ -931,108 +949,64 @@ pub(crate) unsafe fn init(config: ClockConfig) { } } - // Configure ROSC first if present - let rosc_freq = match config.rosc { - Some(config) => configure_rosc(config), - None => 0, - }; - CLOCKS.rosc.store(rosc_freq, Ordering::Relaxed); - - // Configure XOSC - we'll need this for our temporary stable clock - let xosc_freq = match &config.xosc { + let (xosc_freq, pll_sys_freq, pll_usb_freq) = match config.xosc { Some(config) => { + // start XOSC + // datasheet mentions support for clock inputs into XIN, but doesn't go into + // how this is achieved. pico-sdk doesn't support this at all. start_xosc(config.hz, config.delay_multiplier); - config.hz + + let pll_sys_freq = match config.sys_pll { + Some(sys_pll_config) => match configure_pll(pac::PLL_SYS, config.hz, sys_pll_config) { + Ok(freq) => freq, + Err(e) => panic!("Failed to configure PLL_SYS: {}", e), + }, + None => 0, + }; + let pll_usb_freq = match config.usb_pll { + Some(usb_pll_config) => match configure_pll(pac::PLL_USB, config.hz, usb_pll_config) { + Ok(freq) => freq, + Err(e) => panic!("Failed to configure PLL_USB: {}", e), + }, + None => 0, + }; + + (config.hz, pll_sys_freq, pll_usb_freq) } - None => 0, + None => (0, 0, 0), }; + CLOCKS.xosc.store(xosc_freq, Ordering::Relaxed); + CLOCKS.pll_sys.store(pll_sys_freq, Ordering::Relaxed); + CLOCKS.pll_usb.store(pll_usb_freq, Ordering::Relaxed); - // Setup temporary stable clocks first - // Configure USB PLL for our stable temporary clock - let pll_usb_freq = match &config.xosc { - Some(config) => match &config.usb_pll { - Some(pll_usb_config) => { - // Reset USB PLL - let mut peris = reset::Peripherals(0); - peris.set_pll_usb(true); - reset::reset(peris); - reset::unreset_wait(peris); - - // Configure the USB PLL - this should give us 48MHz - let usb_pll_freq = match configure_pll(pac::PLL_USB, xosc_freq, *pll_usb_config) { - Ok(freq) => freq, - Err(_) => { - panic!("Failed to configure USB PLL"); - } - }; - - CLOCKS.pll_usb.store(usb_pll_freq, Ordering::Relaxed); - usb_pll_freq - } - None => 0, - }, - None => 0, + let (ref_src, ref_aux, clk_ref_freq) = { + use {ClkRefCtrlAuxsrc as Aux, ClkRefCtrlSrc as Src}; + let div = config.ref_clk.div as u32; + assert!(div >= 1 && div <= 4); + match config.ref_clk.src { + RefClkSrc::Xosc => (Src::XOSC_CLKSRC, Aux::CLKSRC_PLL_USB, xosc_freq / div), + RefClkSrc::Rosc => (Src::ROSC_CLKSRC_PH, Aux::CLKSRC_PLL_USB, rosc_freq / div), + RefClkSrc::PllUsb => (Src::CLKSRC_CLK_REF_AUX, Aux::CLKSRC_PLL_USB, pll_usb_freq / div), + // RefClkSrc::Gpin0 => (Src::CLKSRC_CLK_REF_AUX, Aux::CLKSRC_GPIN0, gpin0_freq / div), + // RefClkSrc::Gpin1 => (Src::CLKSRC_CLK_REF_AUX, Aux::CLKSRC_GPIN1, gpin1_freq / div), + } }; - - // Configure REF clock to use XOSC - c.clk_ref_ctrl().write(|w| { - w.set_src(ClkRefCtrlSrc::XOSC_CLKSRC); - }); - #[cfg(feature = "rp2040")] - while c.clk_ref_selected().read() != (1 << ClkRefCtrlSrc::XOSC_CLKSRC as u32) {} - #[cfg(feature = "_rp235x")] - while c.clk_ref_selected().read() != pac::clocks::regs::ClkRefSelected(1 << ClkRefCtrlSrc::XOSC_CLKSRC as u32) {} - - // First switch the system clock to a stable source (USB PLL at 48MHz) - // This follows the official Pico SDK's approach to ensure stability during reconfiguration - c.clk_sys_ctrl().write(|w| { - w.set_auxsrc(ClkSysCtrlAuxsrc::CLKSRC_PLL_USB); - w.set_src(ClkSysCtrlSrc::CLKSRC_CLK_SYS_AUX); - }); - - #[cfg(feature = "rp2040")] - while c.clk_sys_selected().read() != (1 << ClkSysCtrlSrc::CLKSRC_CLK_SYS_AUX as u32) {} - #[cfg(feature = "_rp235x")] - while c.clk_sys_selected().read() - != pac::clocks::regs::ClkSysSelected(1 << ClkSysCtrlSrc::CLKSRC_CLK_SYS_AUX as u32) - {} - - // Short delay after switching to USB PLL to ensure stability - cortex_m::asm::delay(100); - - // NOW CONFIGURE THE SYSTEM PLL (safely, since we're running from the USB PLL) - let pll_sys_freq = match &config.xosc { - Some(config) => match &config.sys_pll { - Some(sys_pll_config) => { - // Reset SYS PLL - let mut peris = reset::Peripherals(0); - peris.set_pll_sys(true); - reset::reset(peris); - reset::unreset_wait(peris); - - // Configure the SYS PLL - let pll_sys_freq = match configure_pll(pac::PLL_SYS, xosc_freq, *sys_pll_config) { - Ok(freq) => freq, - Err(_) => { - panic!("Failed to configure system PLL"); - } - }; - - // Ensure PLL is locked and stable - cortex_m::asm::delay(100); - - CLOCKS.pll_sys.store(pll_sys_freq, Ordering::Relaxed); - pll_sys_freq - } - None => 0, - }, - None => 0, - }; - - // Configure tick generation using REF clock - let clk_ref_freq = xosc_freq; // REF clock is now XOSC + assert!(clk_ref_freq != 0); CLOCKS.reference.store(clk_ref_freq, Ordering::Relaxed); + c.clk_ref_ctrl().write(|w| { + w.set_src(ref_src); + w.set_auxsrc(ref_aux); + }); + #[cfg(feature = "rp2040")] + while c.clk_ref_selected().read() != (1 << ref_src as u32) {} + #[cfg(feature = "_rp235x")] + while c.clk_ref_selected().read() != pac::clocks::regs::ClkRefSelected(1 << ref_src as u32) {} + c.clk_ref_div().write(|w| { + w.set_int(config.ref_clk.div); + }); + + // Configure tick generation on the 2040. #[cfg(feature = "rp2040")] pac::WATCHDOG.tick().write(|w| { w.set_cycles((clk_ref_freq / 1_000_000) as u16); @@ -1050,8 +1024,6 @@ pub(crate) unsafe fn init(config: ClockConfig) { pac::TICKS.watchdog_ctrl().write(|w| w.set_enable(true)); } - // NOW SWITCH THE SYSTEM CLOCK TO THE CONFIGURED SOURCE - // The SYS PLL is now stable and we can safely switch to it let (sys_src, sys_aux, clk_sys_freq) = { use {ClkSysCtrlAuxsrc as Aux, ClkSysCtrlSrc as Src}; let (src, aux, freq) = match config.sys_clk.src { @@ -1068,48 +1040,28 @@ pub(crate) unsafe fn init(config: ClockConfig) { }; assert!(clk_sys_freq != 0); CLOCKS.sys.store(clk_sys_freq, Ordering::Relaxed); - - // Set the divider before changing the source if it's increasing - if config.sys_clk.div_int > 1 || config.sys_clk.div_frac > 0 { - c.clk_sys_div().write(|w| { - w.set_int(config.sys_clk.div_int); - w.set_frac(config.sys_clk.div_frac); - }); + if sys_src != ClkSysCtrlSrc::CLK_REF { + c.clk_sys_ctrl().write(|w| w.set_src(ClkSysCtrlSrc::CLK_REF)); + #[cfg(feature = "rp2040")] + while c.clk_sys_selected().read() != (1 << ClkSysCtrlSrc::CLK_REF as u32) {} + #[cfg(feature = "_rp235x")] + while c.clk_sys_selected().read() != pac::clocks::regs::ClkSysSelected(1 << ClkSysCtrlSrc::CLK_REF as u32) {} } - - // Configure aux source first if needed - if sys_src == ClkSysCtrlSrc::CLKSRC_CLK_SYS_AUX { - c.clk_sys_ctrl().modify(|w| { - w.set_auxsrc(sys_aux); - }); - } - - // Now set the source c.clk_sys_ctrl().write(|w| { - if sys_src == ClkSysCtrlSrc::CLKSRC_CLK_SYS_AUX { - w.set_auxsrc(sys_aux); - } + w.set_auxsrc(sys_aux); w.set_src(sys_src); }); - // Wait for the clock to be selected #[cfg(feature = "rp2040")] while c.clk_sys_selected().read() != (1 << sys_src as u32) {} #[cfg(feature = "_rp235x")] while c.clk_sys_selected().read() != pac::clocks::regs::ClkSysSelected(1 << sys_src as u32) {} - // Short delay after final clock switch to ensure stability - cortex_m::asm::delay(100); + c.clk_sys_div().write(|w| { + w.set_int(config.sys_clk.div_int); + w.set_frac(config.sys_clk.div_frac); + }); - // Set the divider after changing the source if it's decreasing - if config.sys_clk.div_int == 1 && config.sys_clk.div_frac == 0 { - c.clk_sys_div().write(|w| { - w.set_int(config.sys_clk.div_int); - w.set_frac(config.sys_clk.div_frac); - }); - } - - // CONFIGURE PERIPHERAL CLOCK let mut peris = reset::ALL_PERIPHERALS; if let Some(src) = config.peri_clk_src { @@ -1136,7 +1088,6 @@ pub(crate) unsafe fn init(config: ClockConfig) { CLOCKS.peri.store(0, Ordering::Relaxed); } - // CONFIGURE USB CLOCK if let Some(conf) = config.usb_clk { c.clk_usb_div().write(|w| w.set_int(conf.div)); c.clk_usb_ctrl().write(|w| { @@ -1160,7 +1111,6 @@ pub(crate) unsafe fn init(config: ClockConfig) { CLOCKS.usb.store(0, Ordering::Relaxed); } - // CONFIGURE ADC CLOCK if let Some(conf) = config.adc_clk { c.clk_adc_div().write(|w| w.set_int(conf.div)); c.clk_adc_ctrl().write(|w| { @@ -1184,7 +1134,7 @@ pub(crate) unsafe fn init(config: ClockConfig) { CLOCKS.adc.store(0, Ordering::Relaxed); } - // CONFIGURE RTC CLOCK + // rp2040 specific clocks #[cfg(feature = "rp2040")] if let Some(conf) = config.rtc_clk { c.clk_rtc_div().write(|w| { @@ -1393,7 +1343,7 @@ fn configure_pll(p: pac::pll::Pll, input_freq: u32, config: PllConfig) -> Result }); // 5. Wait for PLL to lock with a timeout - let mut timeout = 1_000_000; // Reasonable timeout value + let mut timeout = 1_000_000; while !p.cs().read().lock() { timeout -= 1; if timeout == 0 { From 57938d1174a2c8f29448b4fffaea33dba06ae653 Mon Sep 17 00:00:00 2001 From: vinsynth <1.5vhunt@gmail.com> Date: Mon, 5 May 2025 17:32:38 -0400 Subject: [PATCH 63/97] fix h7 sai example start sequence --- examples/stm32h7/src/bin/sai.rs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/examples/stm32h7/src/bin/sai.rs b/examples/stm32h7/src/bin/sai.rs index 95ffe257a..79a856b3b 100644 --- a/examples/stm32h7/src/bin/sai.rs +++ b/examples/stm32h7/src/bin/sai.rs @@ -112,8 +112,10 @@ async fn main(_spawner: Spawner) { let mut buf = [0u32; HALF_DMA_BUFFER_LENGTH]; loop { - sai_receiver.read(&mut buf).await.unwrap(); + // write() must be called before read() to start the master (transmitter) + // clock used by the receiver sai_transmitter.write(&buf).await.unwrap(); + sai_receiver.read(&mut buf).await.unwrap(); } } From 0460a924ac06a7dd33b4e50396948ba9bcb5374e Mon Sep 17 00:00:00 2001 From: Hiroaki Yutani Date: Tue, 6 May 2025 09:09:22 +0900 Subject: [PATCH 64/97] chore: Wrap link_section attribute with unsafe --- docs/pages/faq.adoc | 2 +- embassy-rp/Cargo.toml | 4 +- embassy-rp/src/bootsel.rs | 2 +- embassy-rp/src/flash.rs | 6 +-- embassy-rp/src/lib.rs | 8 +-- embassy-rp/src/multicore.rs | 4 +- embassy-stm32-wpan/src/tables.rs | 50 +++++++++---------- embassy-stm32/src/lib.rs | 2 +- .../boot/application/stm32wl/src/bin/a.rs | 2 +- .../boot/application/stm32wl/src/bin/b.rs | 2 +- examples/mimxrt6/build.rs | 2 +- examples/mimxrt6/src/lib.rs | 6 +-- examples/rp235x/src/bin/blinky.rs | 2 +- examples/rp235x/src/bin/blinky_wifi.rs | 2 +- .../rp235x/src/bin/blinky_wifi_pico_plus_2.rs | 2 +- .../rp235x/src/bin/pio_rotary_encoder_rxf.rs | 2 +- examples/stm32h7/src/bin/adc_dma.rs | 2 +- examples/stm32h7/src/bin/sai.rs | 4 +- examples/stm32h7/src/bin/spi_bdma.rs | 2 +- examples/stm32h723/src/bin/spdifrx.rs | 4 +- examples/stm32h755cm4/src/bin/blinky.rs | 2 +- examples/stm32h755cm7/src/bin/blinky.rs | 2 +- examples/stm32wl/src/bin/blinky.rs | 2 +- examples/stm32wl/src/bin/button.rs | 2 +- examples/stm32wl/src/bin/button_exti.rs | 2 +- examples/stm32wl/src/bin/flash.rs | 2 +- examples/stm32wl/src/bin/random.rs | 2 +- examples/stm32wl/src/bin/rtc.rs | 2 +- examples/stm32wl/src/bin/uart_async.rs | 2 +- 29 files changed, 64 insertions(+), 64 deletions(-) diff --git a/docs/pages/faq.adoc b/docs/pages/faq.adoc index a535e89f8..b21be9a30 100644 --- a/docs/pages/faq.adoc +++ b/docs/pages/faq.adoc @@ -268,7 +268,7 @@ General steps: 1. Find out which memory region BDMA has access to. You can get this information from the bus matrix and the memory mapping table in the STM32 datasheet. 2. Add the memory region to `memory.x`, you can modify the generated one from https://github.com/embassy-rs/stm32-data-generated/tree/main/data/chips. 3. You might need to modify `build.rs` to make cargo pick up the modified `memory.x`. -4. In your code, access the defined memory region using `#[link_section = ".xxx"]` +4. In your code, access the defined memory region using `#[unsafe(link_section = ".xxx")]` 5. Copy data to that region before using BDMA. See link:https://github.com/embassy-rs/embassy/blob/main/examples/stm32h7/src/bin/spi_bdma.rs[SMT32H7 SPI BDMA example] for more details. diff --git a/embassy-rp/Cargo.toml b/embassy-rp/Cargo.toml index b440591cf..a073c8b9e 100644 --- a/embassy-rp/Cargo.toml +++ b/embassy-rp/Cargo.toml @@ -86,7 +86,7 @@ boot2-w25x10cl = [] ## Have embassy-rp not provide the boot2 so you can use your own. ## Place your own in the ".boot2" section like: ## ``` -## #[link_section = ".boot2"] +## #[unsafe(link_section = ".boot2")] ## #[used] ## static BOOT2: [u8; 256] = [0; 256]; // Provide your own with e.g. include_bytes! ## ``` @@ -109,7 +109,7 @@ imagedef-nonsecure-exe = [] ## ```ignore ## use embassy_rp::block::ImageDef; ## -## #[link_section = ".start_block"] +## #[unsafe(link_section = ".start_block")] ## #[used] ## static IMAGE_DEF: ImageDef = ImageDef::secure_exe(); // Update this with your own implementation. ## ``` diff --git a/embassy-rp/src/bootsel.rs b/embassy-rp/src/bootsel.rs index 14f9e46aa..5f0bac248 100644 --- a/embassy-rp/src/bootsel.rs +++ b/embassy-rp/src/bootsel.rs @@ -36,7 +36,7 @@ mod ram_helpers { /// This function must live in ram. It uses inline asm to avoid any /// potential calls to ABI functions that might be in flash. #[inline(never)] - #[link_section = ".data.ram_func"] + #[unsafe(link_section = ".data.ram_func")] #[cfg(target_arch = "arm")] pub unsafe fn read_cs_status() -> GpioStatus { let result: u32; diff --git a/embassy-rp/src/flash.rs b/embassy-rp/src/flash.rs index ef1cd9212..1ac15a677 100644 --- a/embassy-rp/src/flash.rs +++ b/embassy-rp/src/flash.rs @@ -623,7 +623,7 @@ mod ram_helpers { /// Length of data must be a multiple of 4096 /// addr must be aligned to 4096 #[inline(never)] - #[link_section = ".data.ram_func"] + #[unsafe(link_section = ".data.ram_func")] #[cfg(feature = "rp2040")] unsafe fn write_flash_inner(addr: u32, len: u32, data: Option<&[u8]>, ptrs: *const FlashFunctionPointers) { #[cfg(target_arch = "arm")] @@ -688,7 +688,7 @@ mod ram_helpers { /// Length of data must be a multiple of 4096 /// addr must be aligned to 4096 #[inline(never)] - #[link_section = ".data.ram_func"] + #[unsafe(link_section = ".data.ram_func")] #[cfg(feature = "_rp235x")] unsafe fn write_flash_inner(addr: u32, len: u32, data: Option<&[u8]>, ptrs: *const FlashFunctionPointers) { let data = data.map(|d| d.as_ptr()).unwrap_or(core::ptr::null()); @@ -807,7 +807,7 @@ mod ram_helpers { /// /// Credit: taken from `rp2040-flash` (also licensed Apache+MIT) #[inline(never)] - #[link_section = ".data.ram_func"] + #[unsafe(link_section = ".data.ram_func")] #[cfg(feature = "rp2040")] unsafe fn read_flash_inner(cmd: FlashCommand, ptrs: *const FlashFunctionPointers) { #[cfg(target_arch = "arm")] diff --git a/embassy-rp/src/lib.rs b/embassy-rp/src/lib.rs index f549446bc..9b7c2b6ea 100644 --- a/embassy-rp/src/lib.rs +++ b/embassy-rp/src/lib.rs @@ -434,13 +434,13 @@ macro_rules! select_bootloader { ( $( $feature:literal => $loader:ident, )+ default => $default:ident ) => { $( #[cfg(feature = $feature)] - #[link_section = ".boot2"] + #[unsafe(link_section = ".boot2")] #[used] static BOOT2: [u8; 256] = rp2040_boot2::$loader; )* #[cfg(not(any( $( feature = $feature),* )))] - #[link_section = ".boot2"] + #[unsafe(link_section = ".boot2")] #[used] static BOOT2: [u8; 256] = rp2040_boot2::$default; } @@ -463,13 +463,13 @@ macro_rules! select_imagedef { ( $( $feature:literal => $imagedef:ident, )+ default => $default:ident ) => { $( #[cfg(feature = $feature)] - #[link_section = ".start_block"] + #[unsafe(link_section = ".start_block")] #[used] static IMAGE_DEF: crate::block::ImageDef = crate::block::ImageDef::$imagedef(); )* #[cfg(not(any( $( feature = $feature),* )))] - #[link_section = ".start_block"] + #[unsafe(link_section = ".start_block")] #[used] static IMAGE_DEF: crate::block::ImageDef = crate::block::ImageDef::$default(); } diff --git a/embassy-rp/src/multicore.rs b/embassy-rp/src/multicore.rs index d10b6837c..ec05bfdf5 100644 --- a/embassy-rp/src/multicore.rs +++ b/embassy-rp/src/multicore.rs @@ -90,7 +90,7 @@ impl Stack { #[cfg(all(feature = "rt", feature = "rp2040"))] #[interrupt] -#[link_section = ".data.ram_func"] +#[unsafe(link_section = ".data.ram_func")] unsafe fn SIO_IRQ_PROC1() { let sio = pac::SIO; // Clear IRQ @@ -115,7 +115,7 @@ unsafe fn SIO_IRQ_PROC1() { #[cfg(all(feature = "rt", feature = "_rp235x"))] #[interrupt] -#[link_section = ".data.ram_func"] +#[unsafe(link_section = ".data.ram_func")] unsafe fn SIO_IRQ_FIFO() { let sio = pac::SIO; // Clear IRQ diff --git a/embassy-stm32-wpan/src/tables.rs b/embassy-stm32-wpan/src/tables.rs index fe6fc47a3..204790e6d 100644 --- a/embassy-stm32-wpan/src/tables.rs +++ b/embassy-stm32-wpan/src/tables.rs @@ -190,94 +190,94 @@ pub struct RefTable { } // --------------------- ref table --------------------- -#[link_section = "TL_REF_TABLE"] +#[unsafe(link_section = "TL_REF_TABLE")] pub static mut TL_REF_TABLE: MaybeUninit = MaybeUninit::uninit(); -#[link_section = "MB_MEM1"] +#[unsafe(link_section = "MB_MEM1")] pub static mut TL_DEVICE_INFO_TABLE: Aligned> = Aligned(MaybeUninit::uninit()); -#[link_section = "MB_MEM1"] +#[unsafe(link_section = "MB_MEM1")] pub static mut TL_BLE_TABLE: Aligned> = Aligned(MaybeUninit::uninit()); -#[link_section = "MB_MEM1"] +#[unsafe(link_section = "MB_MEM1")] pub static mut TL_THREAD_TABLE: Aligned> = Aligned(MaybeUninit::uninit()); -#[link_section = "MB_MEM1"] +#[unsafe(link_section = "MB_MEM1")] pub static mut TL_LLD_TESTS_TABLE: Aligned> = Aligned(MaybeUninit::uninit()); -#[link_section = "MB_MEM1"] +#[unsafe(link_section = "MB_MEM1")] pub static mut TL_BLE_LLD_TABLE: Aligned> = Aligned(MaybeUninit::uninit()); -#[link_section = "MB_MEM1"] +#[unsafe(link_section = "MB_MEM1")] pub static mut TL_SYS_TABLE: Aligned> = Aligned(MaybeUninit::uninit()); -#[link_section = "MB_MEM1"] +#[unsafe(link_section = "MB_MEM1")] pub static mut TL_MEM_MANAGER_TABLE: Aligned> = Aligned(MaybeUninit::uninit()); -#[link_section = "MB_MEM1"] +#[unsafe(link_section = "MB_MEM1")] pub static mut TL_TRACES_TABLE: Aligned> = Aligned(MaybeUninit::uninit()); -#[link_section = "MB_MEM1"] +#[unsafe(link_section = "MB_MEM1")] pub static mut TL_MAC_802_15_4_TABLE: Aligned> = Aligned(MaybeUninit::uninit()); -#[link_section = "MB_MEM1"] +#[unsafe(link_section = "MB_MEM1")] pub static mut TL_ZIGBEE_TABLE: Aligned> = Aligned(MaybeUninit::uninit()); // --------------------- tables --------------------- -#[link_section = "MB_MEM1"] +#[unsafe(link_section = "MB_MEM1")] pub static mut FREE_BUF_QUEUE: Aligned> = Aligned(MaybeUninit::uninit()); #[allow(dead_code)] -#[link_section = "MB_MEM1"] +#[unsafe(link_section = "MB_MEM1")] pub static mut TRACES_EVT_QUEUE: Aligned> = Aligned(MaybeUninit::uninit()); -#[link_section = "MB_MEM2"] +#[unsafe(link_section = "MB_MEM2")] pub static mut CS_BUFFER: Aligned> = Aligned(MaybeUninit::uninit()); -#[link_section = "MB_MEM2"] +#[unsafe(link_section = "MB_MEM2")] pub static mut EVT_QUEUE: Aligned> = Aligned(MaybeUninit::uninit()); -#[link_section = "MB_MEM2"] +#[unsafe(link_section = "MB_MEM2")] pub static mut SYSTEM_EVT_QUEUE: Aligned> = Aligned(MaybeUninit::uninit()); // --------------------- app tables --------------------- #[cfg(feature = "mac")] -#[link_section = "MB_MEM2"] +#[unsafe(link_section = "MB_MEM2")] pub static mut MAC_802_15_4_CMD_BUFFER: Aligned> = Aligned(MaybeUninit::uninit()); #[cfg(feature = "mac")] -#[link_section = "MB_MEM2"] +#[unsafe(link_section = "MB_MEM2")] pub static mut MAC_802_15_4_NOTIF_RSP_EVT_BUFFER: MaybeUninit< Aligned, > = MaybeUninit::uninit(); -#[link_section = "MB_MEM2"] +#[unsafe(link_section = "MB_MEM2")] pub static mut EVT_POOL: Aligned> = Aligned(MaybeUninit::uninit()); -#[link_section = "MB_MEM2"] +#[unsafe(link_section = "MB_MEM2")] pub static mut SYS_CMD_BUF: Aligned> = Aligned(MaybeUninit::uninit()); -#[link_section = "MB_MEM2"] +#[unsafe(link_section = "MB_MEM2")] pub static mut SYS_SPARE_EVT_BUF: Aligned> = Aligned(MaybeUninit::uninit()); #[cfg(feature = "mac")] -#[link_section = "MB_MEM2"] +#[unsafe(link_section = "MB_MEM2")] pub static mut MAC_802_15_4_CNFINDNOT: Aligned> = Aligned(MaybeUninit::uninit()); #[cfg(feature = "ble")] -#[link_section = "MB_MEM1"] +#[unsafe(link_section = "MB_MEM1")] pub static mut BLE_CMD_BUFFER: Aligned> = Aligned(MaybeUninit::uninit()); #[cfg(feature = "ble")] -#[link_section = "MB_MEM2"] +#[unsafe(link_section = "MB_MEM2")] pub static mut BLE_SPARE_EVT_BUF: Aligned> = Aligned(MaybeUninit::uninit()); #[cfg(feature = "ble")] -#[link_section = "MB_MEM2"] +#[unsafe(link_section = "MB_MEM2")] // fuck these "magic" numbers from ST ---v---v pub static mut HCI_ACL_DATA_BUFFER: Aligned> = Aligned(MaybeUninit::uninit()); diff --git a/embassy-stm32/src/lib.rs b/embassy-stm32/src/lib.rs index 226293a9d..8ba604830 100644 --- a/embassy-stm32/src/lib.rs +++ b/embassy-stm32/src/lib.rs @@ -331,7 +331,7 @@ mod dual_core { /// use core::mem::MaybeUninit; /// use embassy_stm32::{init_secondary, SharedData}; /// - /// #[link_section = ".ram_d3"] + /// #[unsafe(link_section = ".ram_d3")] /// static SHARED_DATA: MaybeUninit = MaybeUninit::uninit(); /// /// init_secondary(&SHARED_DATA); diff --git a/examples/boot/application/stm32wl/src/bin/a.rs b/examples/boot/application/stm32wl/src/bin/a.rs index 127de0237..e4526927f 100644 --- a/examples/boot/application/stm32wl/src/bin/a.rs +++ b/examples/boot/application/stm32wl/src/bin/a.rs @@ -20,7 +20,7 @@ static APP_B: &[u8] = &[0, 1, 2, 3]; #[cfg(not(feature = "skip-include"))] static APP_B: &[u8] = include_bytes!("../../b.bin"); -#[link_section = ".shared_data"] +#[unsafe(link_section = ".shared_data")] static SHARED_DATA: MaybeUninit = MaybeUninit::uninit(); #[embassy_executor::main] diff --git a/examples/boot/application/stm32wl/src/bin/b.rs b/examples/boot/application/stm32wl/src/bin/b.rs index 768dadf8b..6016a9555 100644 --- a/examples/boot/application/stm32wl/src/bin/b.rs +++ b/examples/boot/application/stm32wl/src/bin/b.rs @@ -11,7 +11,7 @@ use embassy_stm32::SharedData; use embassy_time::Timer; use panic_reset as _; -#[link_section = ".shared_data"] +#[unsafe(link_section = ".shared_data")] static SHARED_DATA: MaybeUninit = MaybeUninit::uninit(); #[embassy_executor::main] diff --git a/examples/mimxrt6/build.rs b/examples/mimxrt6/build.rs index 9c0ed3213..56010dfd6 100644 --- a/examples/mimxrt6/build.rs +++ b/examples/mimxrt6/build.rs @@ -25,7 +25,7 @@ fn main() { .write_all( format!( r##" -#[link_section = ".biv"] +#[unsafe(link_section = ".biv")] #[used] static BOOT_IMAGE_VERSION: u32 = 0x{:02x}{:02x}{:02x}00; "##, diff --git a/examples/mimxrt6/src/lib.rs b/examples/mimxrt6/src/lib.rs index da6e14427..3c3ea1981 100644 --- a/examples/mimxrt6/src/lib.rs +++ b/examples/mimxrt6/src/lib.rs @@ -6,15 +6,15 @@ use {defmt_rtt as _, panic_probe as _}; // auto-generated version information from Cargo.toml include!(concat!(env!("OUT_DIR"), "/biv.rs")); -#[link_section = ".otfad"] +#[unsafe(link_section = ".otfad")] #[used] static OTFAD: [u8; 256] = [0; 256]; #[rustfmt::skip] -#[link_section = ".fcb"] +#[unsafe(link_section = ".fcb")] #[used] static FCB: FlexSPIFlashConfigurationBlock = FlexSPIFlashConfigurationBlock::build(); -#[link_section = ".keystore"] +#[unsafe(link_section = ".keystore")] #[used] static KEYSTORE: [u8; 2048] = [0; 2048]; diff --git a/examples/rp235x/src/bin/blinky.rs b/examples/rp235x/src/bin/blinky.rs index a36029f92..8a2464fbb 100644 --- a/examples/rp235x/src/bin/blinky.rs +++ b/examples/rp235x/src/bin/blinky.rs @@ -14,7 +14,7 @@ use {defmt_rtt as _, panic_probe as _}; // Program metadata for `picotool info`. // This isn't needed, but it's recomended to have these minimal entries. -#[link_section = ".bi_entries"] +#[unsafe(link_section = ".bi_entries")] #[used] pub static PICOTOOL_ENTRIES: [embassy_rp::binary_info::EntryAddr; 4] = [ embassy_rp::binary_info::rp_program_name!(c"Blinky Example"), diff --git a/examples/rp235x/src/bin/blinky_wifi.rs b/examples/rp235x/src/bin/blinky_wifi.rs index ef029867a..8c352ebc4 100644 --- a/examples/rp235x/src/bin/blinky_wifi.rs +++ b/examples/rp235x/src/bin/blinky_wifi.rs @@ -18,7 +18,7 @@ use {defmt_rtt as _, panic_probe as _}; // Program metadata for `picotool info`. // This isn't needed, but it's recommended to have these minimal entries. -#[link_section = ".bi_entries"] +#[unsafe(link_section = ".bi_entries")] #[used] pub static PICOTOOL_ENTRIES: [embassy_rp::binary_info::EntryAddr; 4] = [ embassy_rp::binary_info::rp_program_name!(c"Blinky Example"), diff --git a/examples/rp235x/src/bin/blinky_wifi_pico_plus_2.rs b/examples/rp235x/src/bin/blinky_wifi_pico_plus_2.rs index 2a919a1ea..0a5bccfb3 100644 --- a/examples/rp235x/src/bin/blinky_wifi_pico_plus_2.rs +++ b/examples/rp235x/src/bin/blinky_wifi_pico_plus_2.rs @@ -18,7 +18,7 @@ use {defmt_rtt as _, panic_probe as _}; // Program metadata for `picotool info`. // This isn't needed, but it's recomended to have these minimal entries. -#[link_section = ".bi_entries"] +#[unsafe(link_section = ".bi_entries")] #[used] pub static PICOTOOL_ENTRIES: [embassy_rp::binary_info::EntryAddr; 4] = [ embassy_rp::binary_info::rp_program_name!(c"Blinky Example"), diff --git a/examples/rp235x/src/bin/pio_rotary_encoder_rxf.rs b/examples/rp235x/src/bin/pio_rotary_encoder_rxf.rs index ccc601661..0e3b4e5f9 100644 --- a/examples/rp235x/src/bin/pio_rotary_encoder_rxf.rs +++ b/examples/rp235x/src/bin/pio_rotary_encoder_rxf.rs @@ -16,7 +16,7 @@ use pio::{Common, Config, FifoJoin, Instance, InterruptHandler, Pio, PioPin, Shi use {defmt_rtt as _, panic_probe as _}; // Program metadata for `picotool info` -#[link_section = ".bi_entries"] +#[unsafe(link_section = ".bi_entries")] #[used] pub static PICOTOOL_ENTRIES: [embassy_rp::binary_info::EntryAddr; 4] = [ embassy_rp::binary_info::rp_program_name!(c"example_pio_rotary_encoder_rxf"), diff --git a/examples/stm32h7/src/bin/adc_dma.rs b/examples/stm32h7/src/bin/adc_dma.rs index dc775f18a..f06b5d06e 100644 --- a/examples/stm32h7/src/bin/adc_dma.rs +++ b/examples/stm32h7/src/bin/adc_dma.rs @@ -8,7 +8,7 @@ use embassy_stm32::Config; use embassy_time::Timer; use {defmt_rtt as _, panic_probe as _}; -#[link_section = ".ram_d3"] +#[unsafe(link_section = ".ram_d3")] static mut DMA_BUF: [u16; 2] = [0; 2]; #[embassy_executor::main] diff --git a/examples/stm32h7/src/bin/sai.rs b/examples/stm32h7/src/bin/sai.rs index 95ffe257a..c2bf7d6f2 100644 --- a/examples/stm32h7/src/bin/sai.rs +++ b/examples/stm32h7/src/bin/sai.rs @@ -16,9 +16,9 @@ const DMA_BUFFER_LENGTH: usize = HALF_DMA_BUFFER_LENGTH * 2; // 2 half-blocks const SAMPLE_RATE: u32 = 48000; //DMA buffer must be in special region. Refer https://embassy.dev/book/#_stm32_bdma_only_working_out_of_some_ram_regions -#[link_section = ".sram1_bss"] +#[unsafe(link_section = ".sram1_bss")] static mut TX_BUFFER: GroundedArrayCell = GroundedArrayCell::uninit(); -#[link_section = ".sram1_bss"] +#[unsafe(link_section = ".sram1_bss")] static mut RX_BUFFER: GroundedArrayCell = GroundedArrayCell::uninit(); #[embassy_executor::main] diff --git a/examples/stm32h7/src/bin/spi_bdma.rs b/examples/stm32h7/src/bin/spi_bdma.rs index 9166fe9b6..5a7dff572 100644 --- a/examples/stm32h7/src/bin/spi_bdma.rs +++ b/examples/stm32h7/src/bin/spi_bdma.rs @@ -16,7 +16,7 @@ use static_cell::StaticCell; use {defmt_rtt as _, panic_probe as _}; // Defined in memory.x -#[link_section = ".ram_d3"] +#[unsafe(link_section = ".ram_d3")] static mut RAM_D3: GroundedArrayCell = GroundedArrayCell::uninit(); #[embassy_executor::task] diff --git a/examples/stm32h723/src/bin/spdifrx.rs b/examples/stm32h723/src/bin/spdifrx.rs index bc8249ced..a04d7cb34 100644 --- a/examples/stm32h723/src/bin/spdifrx.rs +++ b/examples/stm32h723/src/bin/spdifrx.rs @@ -24,10 +24,10 @@ const HALF_DMA_BUFFER_LENGTH: usize = BLOCK_LENGTH * CHANNEL_COUNT; const DMA_BUFFER_LENGTH: usize = HALF_DMA_BUFFER_LENGTH * 2; // 2 half-blocks // DMA buffers must be in special regions. Refer https://embassy.dev/book/#_stm32_bdma_only_working_out_of_some_ram_regions -#[link_section = ".sram1"] +#[unsafe(link_section = ".sram1")] static mut SPDIFRX_BUFFER: GroundedArrayCell = GroundedArrayCell::uninit(); -#[link_section = ".sram4"] +#[unsafe(link_section = ".sram4")] static mut SAI_BUFFER: GroundedArrayCell = GroundedArrayCell::uninit(); #[embassy_executor::main] diff --git a/examples/stm32h755cm4/src/bin/blinky.rs b/examples/stm32h755cm4/src/bin/blinky.rs index b5c547839..39112c1f5 100644 --- a/examples/stm32h755cm4/src/bin/blinky.rs +++ b/examples/stm32h755cm4/src/bin/blinky.rs @@ -10,7 +10,7 @@ use embassy_stm32::SharedData; use embassy_time::Timer; use {defmt_rtt as _, panic_probe as _}; -#[link_section = ".ram_d3.shared_data"] +#[unsafe(link_section = ".ram_d3.shared_data")] static SHARED_DATA: MaybeUninit = MaybeUninit::uninit(); #[embassy_executor::main] diff --git a/examples/stm32h755cm7/src/bin/blinky.rs b/examples/stm32h755cm7/src/bin/blinky.rs index 94d2226c0..b30bf4de8 100644 --- a/examples/stm32h755cm7/src/bin/blinky.rs +++ b/examples/stm32h755cm7/src/bin/blinky.rs @@ -10,7 +10,7 @@ use embassy_stm32::SharedData; use embassy_time::Timer; use {defmt_rtt as _, panic_probe as _}; -#[link_section = ".ram_d3.shared_data"] +#[unsafe(link_section = ".ram_d3.shared_data")] static SHARED_DATA: MaybeUninit = MaybeUninit::uninit(); #[embassy_executor::main] diff --git a/examples/stm32wl/src/bin/blinky.rs b/examples/stm32wl/src/bin/blinky.rs index ce7d0ec58..a2a90871d 100644 --- a/examples/stm32wl/src/bin/blinky.rs +++ b/examples/stm32wl/src/bin/blinky.rs @@ -10,7 +10,7 @@ use embassy_stm32::SharedData; use embassy_time::Timer; use {defmt_rtt as _, panic_probe as _}; -#[link_section = ".shared_data"] +#[unsafe(link_section = ".shared_data")] static SHARED_DATA: MaybeUninit = MaybeUninit::uninit(); #[embassy_executor::main] diff --git a/examples/stm32wl/src/bin/button.rs b/examples/stm32wl/src/bin/button.rs index 8b5204479..21bcd2ac6 100644 --- a/examples/stm32wl/src/bin/button.rs +++ b/examples/stm32wl/src/bin/button.rs @@ -9,7 +9,7 @@ use embassy_stm32::gpio::{Input, Level, Output, Pull, Speed}; use embassy_stm32::SharedData; use {defmt_rtt as _, panic_probe as _}; -#[link_section = ".shared_data"] +#[unsafe(link_section = ".shared_data")] static SHARED_DATA: MaybeUninit = MaybeUninit::uninit(); #[entry] diff --git a/examples/stm32wl/src/bin/button_exti.rs b/examples/stm32wl/src/bin/button_exti.rs index 8dd1a6a5e..0a8aece34 100644 --- a/examples/stm32wl/src/bin/button_exti.rs +++ b/examples/stm32wl/src/bin/button_exti.rs @@ -10,7 +10,7 @@ use embassy_stm32::gpio::Pull; use embassy_stm32::SharedData; use {defmt_rtt as _, panic_probe as _}; -#[link_section = ".shared_data"] +#[unsafe(link_section = ".shared_data")] static SHARED_DATA: MaybeUninit = MaybeUninit::uninit(); #[embassy_executor::main] diff --git a/examples/stm32wl/src/bin/flash.rs b/examples/stm32wl/src/bin/flash.rs index 147f5d293..320a9723a 100644 --- a/examples/stm32wl/src/bin/flash.rs +++ b/examples/stm32wl/src/bin/flash.rs @@ -9,7 +9,7 @@ use embassy_stm32::flash::Flash; use embassy_stm32::SharedData; use {defmt_rtt as _, panic_probe as _}; -#[link_section = ".shared_data"] +#[unsafe(link_section = ".shared_data")] static SHARED_DATA: MaybeUninit = MaybeUninit::uninit(); #[embassy_executor::main] diff --git a/examples/stm32wl/src/bin/random.rs b/examples/stm32wl/src/bin/random.rs index df2ed0054..68b9d7d00 100644 --- a/examples/stm32wl/src/bin/random.rs +++ b/examples/stm32wl/src/bin/random.rs @@ -14,7 +14,7 @@ bind_interrupts!(struct Irqs{ RNG => rng::InterruptHandler; }); -#[link_section = ".shared_data"] +#[unsafe(link_section = ".shared_data")] static SHARED_DATA: MaybeUninit = MaybeUninit::uninit(); #[embassy_executor::main] diff --git a/examples/stm32wl/src/bin/rtc.rs b/examples/stm32wl/src/bin/rtc.rs index 69a9ddc4c..d3709120f 100644 --- a/examples/stm32wl/src/bin/rtc.rs +++ b/examples/stm32wl/src/bin/rtc.rs @@ -12,7 +12,7 @@ use embassy_stm32::{Config, SharedData}; use embassy_time::Timer; use {defmt_rtt as _, panic_probe as _}; -#[link_section = ".shared_data"] +#[unsafe(link_section = ".shared_data")] static SHARED_DATA: MaybeUninit = MaybeUninit::uninit(); #[embassy_executor::main] diff --git a/examples/stm32wl/src/bin/uart_async.rs b/examples/stm32wl/src/bin/uart_async.rs index ece9b9201..505a85f47 100644 --- a/examples/stm32wl/src/bin/uart_async.rs +++ b/examples/stm32wl/src/bin/uart_async.rs @@ -14,7 +14,7 @@ bind_interrupts!(struct Irqs{ LPUART1 => InterruptHandler; }); -#[link_section = ".shared_data"] +#[unsafe(link_section = ".shared_data")] static SHARED_DATA: MaybeUninit = MaybeUninit::uninit(); /* From 5105442f1f0587c0899ea29b52c5202ed5c81e6c Mon Sep 17 00:00:00 2001 From: Hiroaki Yutani Date: Tue, 6 May 2025 09:44:00 +0900 Subject: [PATCH 65/97] Fix clippy::bad_bit_mask --- embassy-stm32/src/spdifrx/mod.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/embassy-stm32/src/spdifrx/mod.rs b/embassy-stm32/src/spdifrx/mod.rs index 08dba04fe..9c42217f0 100644 --- a/embassy-stm32/src/spdifrx/mod.rs +++ b/embassy-stm32/src/spdifrx/mod.rs @@ -223,7 +223,7 @@ impl<'d, T: Instance> Spdifrx<'d, T> { }; for sample in data.as_mut() { - if (*sample & (0x0002_u32)) == 0x0001 { + if (*sample & (0x0002_u32)) != 0 { // Discard invalid samples, setting them to mute level. *sample = 0; } else { From 103cda49912af6cc524324d28f57e91c05da6741 Mon Sep 17 00:00:00 2001 From: Hiroaki Yutani Date: Tue, 6 May 2025 12:11:03 +0900 Subject: [PATCH 66/97] Add a temporary tweak --- examples/rp/Cargo.toml | 3 +++ examples/rp235x/Cargo.toml | 3 +++ 2 files changed, 6 insertions(+) diff --git a/examples/rp/Cargo.toml b/examples/rp/Cargo.toml index 45ca30e4c..97b14a327 100644 --- a/examples/rp/Cargo.toml +++ b/examples/rp/Cargo.toml @@ -67,3 +67,6 @@ opt-level = 'z' debug = 2 lto = true opt-level = "z" + +[patch.crates-io] +cortex-m-rt = { git = "https://github.com/yutannihilation/cortex-m", branch = "fix/allow-unsafe-wrap-rust2024" } diff --git a/examples/rp235x/Cargo.toml b/examples/rp235x/Cargo.toml index 345a915af..99091ee90 100644 --- a/examples/rp235x/Cargo.toml +++ b/examples/rp235x/Cargo.toml @@ -64,3 +64,6 @@ debug = 2 [profile.dev] lto = true opt-level = "z" + +[patch.crates-io] +cortex-m-rt = { git = "https://github.com/yutannihilation/cortex-m", branch = "fix/allow-unsafe-wrap-rust2024" } From 326c88c9662c1351eb5b8f5337268a8a22f114e8 Mon Sep 17 00:00:00 2001 From: Hiroaki Yutani Date: Tue, 6 May 2025 12:13:53 +0900 Subject: [PATCH 67/97] One more tweak --- embassy-rp/Cargo.toml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/embassy-rp/Cargo.toml b/embassy-rp/Cargo.toml index a073c8b9e..e8091a2a5 100644 --- a/embassy-rp/Cargo.toml +++ b/embassy-rp/Cargo.toml @@ -174,3 +174,6 @@ smart-leds = "0.4.0" [dev-dependencies] embassy-executor = { version = "0.7.0", path = "../embassy-executor", features = ["arch-std", "executor-thread"] } static_cell = { version = "2" } + +[patch.crates-io] +cortex-m-rt = { git = "https://github.com/yutannihilation/cortex-m", branch = "fix/allow-unsafe-wrap-rust2024" } From b1d37cbf22cedc926eb9f61a39ec02715530f63e Mon Sep 17 00:00:00 2001 From: Hiroaki Yutani Date: Tue, 6 May 2025 12:17:46 +0900 Subject: [PATCH 68/97] Try another tweak --- tests/rp/Cargo.toml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/tests/rp/Cargo.toml b/tests/rp/Cargo.toml index 1335aa84b..ed3a880a0 100644 --- a/tests/rp/Cargo.toml +++ b/tests/rp/Cargo.toml @@ -93,3 +93,6 @@ debug = false debug-assertions = false opt-level = 0 overflow-checks = false + +[patch.crates-io] +cortex-m-rt = { git = "https://github.com/yutannihilation/cortex-m", branch = "fix/allow-unsafe-wrap-rust2024" } From 254b203e1a88f7524911d520bc8738447f5c813c Mon Sep 17 00:00:00 2001 From: Ulf Lilleengen Date: Tue, 6 May 2025 12:46:52 +0200 Subject: [PATCH 69/97] fix: update embassy-net changelog --- embassy-net/CHANGELOG.md | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/embassy-net/CHANGELOG.md b/embassy-net/CHANGELOG.md index cfee5f3cf..8773772ce 100644 --- a/embassy-net/CHANGELOG.md +++ b/embassy-net/CHANGELOG.md @@ -9,10 +9,12 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 No unreleased changes yet... Quick, go send a PR! -## 0.7 - 2025-02-14 +## 0.7 - 2025-05-06 - don't infinite loop if udp::send methods receive a buffer too large to ever be sent - add ICMP sockets and a ping utility +- configurable rate_limit for the ping utility +- Feature match udp sockets ## 0.6 - 2025-01-05 From 73f11d238a96a2186e93f786bd29b75149097e02 Mon Sep 17 00:00:00 2001 From: Dion Dokter Date: Tue, 6 May 2025 13:15:26 +0200 Subject: [PATCH 70/97] Updated metapac tag --- embassy-stm32/Cargo.toml | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/embassy-stm32/Cargo.toml b/embassy-stm32/Cargo.toml index ec22325f9..972307bec 100644 --- a/embassy-stm32/Cargo.toml +++ b/embassy-stm32/Cargo.toml @@ -73,8 +73,7 @@ rand_core = "0.6.3" sdio-host = "0.9.0" critical-section = "1.1" #stm32-metapac = { version = "16" } -# stm32-metapac = { git = "https://github.com/embassy-rs/stm32-data-generated", tag = "stm32-data-380f03cb71f43a242adc45e83607a380ffe0447b" } -stm32-metapac = { git="https://ci.embassy.dev/jobs/3de13111608a/artifacts/generated.git" } +stm32-metapac = { git = "https://github.com/embassy-rs/stm32-data-generated", tag = "stm32-data-7251801e3273011ce28a89e8f2e45eec2e419e26" } vcell = "0.1.3" nb = "1.0.0" @@ -103,8 +102,7 @@ proc-macro2 = "1.0.36" quote = "1.0.15" #stm32-metapac = { version = "16", default-features = false, features = ["metadata"]} -# stm32-metapac = { git = "https://github.com/embassy-rs/stm32-data-generated", tag = "stm32-data-380f03cb71f43a242adc45e83607a380ffe0447b", default-features = false, features = ["metadata"] } -stm32-metapac = { git="https://ci.embassy.dev/jobs/3de13111608a/artifacts/generated.git", default-features = false, features = ["metadata"] } +stm32-metapac = { git = "https://github.com/embassy-rs/stm32-data-generated", tag = "stm32-data-7251801e3273011ce28a89e8f2e45eec2e419e26", default-features = false, features = ["metadata"] } [features] default = ["rt"] From bd3b3b45266c3dc3bf0d443a2e727814968cb245 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: Wed, 7 May 2025 17:14:28 +0800 Subject: [PATCH 71/97] derive debug copy clone defmt for pwmpinconfig --- embassy-stm32/src/timer/simple_pwm.rs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/embassy-stm32/src/timer/simple_pwm.rs b/embassy-stm32/src/timer/simple_pwm.rs index 54ab7d0d5..8fd7e8df4 100644 --- a/embassy-stm32/src/timer/simple_pwm.rs +++ b/embassy-stm32/src/timer/simple_pwm.rs @@ -31,6 +31,8 @@ pub struct PwmPin<'d, T, C> { /// PWM pin config /// /// This configures the pwm pin settings +#[derive(Debug, Copy, Clone)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] pub struct PwmPinConfig { /// PWM Pin output type pub output_type: OutputType, From 9d9ae7c5d5bf3371a7bec2fa69a9c27415277fc0 Mon Sep 17 00:00:00 2001 From: Hiroaki Yutani Date: Wed, 7 May 2025 22:59:41 +0900 Subject: [PATCH 72/97] Revert --- embassy-rp/Cargo.toml | 7 ++--- embassy-rp/src/bootsel.rs | 2 +- embassy-rp/src/flash.rs | 6 ++-- embassy-rp/src/lib.rs | 8 ++--- embassy-rp/src/multicore.rs | 4 +-- embassy-stm32-wpan/src/tables.rs | 50 ++++++++++++++++---------------- embassy-stm32/src/lib.rs | 2 +- tests/rp/Cargo.toml | 3 -- 8 files changed, 38 insertions(+), 44 deletions(-) diff --git a/embassy-rp/Cargo.toml b/embassy-rp/Cargo.toml index e8091a2a5..b440591cf 100644 --- a/embassy-rp/Cargo.toml +++ b/embassy-rp/Cargo.toml @@ -86,7 +86,7 @@ boot2-w25x10cl = [] ## Have embassy-rp not provide the boot2 so you can use your own. ## Place your own in the ".boot2" section like: ## ``` -## #[unsafe(link_section = ".boot2")] +## #[link_section = ".boot2"] ## #[used] ## static BOOT2: [u8; 256] = [0; 256]; // Provide your own with e.g. include_bytes! ## ``` @@ -109,7 +109,7 @@ imagedef-nonsecure-exe = [] ## ```ignore ## use embassy_rp::block::ImageDef; ## -## #[unsafe(link_section = ".start_block")] +## #[link_section = ".start_block"] ## #[used] ## static IMAGE_DEF: ImageDef = ImageDef::secure_exe(); // Update this with your own implementation. ## ``` @@ -174,6 +174,3 @@ smart-leds = "0.4.0" [dev-dependencies] embassy-executor = { version = "0.7.0", path = "../embassy-executor", features = ["arch-std", "executor-thread"] } static_cell = { version = "2" } - -[patch.crates-io] -cortex-m-rt = { git = "https://github.com/yutannihilation/cortex-m", branch = "fix/allow-unsafe-wrap-rust2024" } diff --git a/embassy-rp/src/bootsel.rs b/embassy-rp/src/bootsel.rs index 5f0bac248..14f9e46aa 100644 --- a/embassy-rp/src/bootsel.rs +++ b/embassy-rp/src/bootsel.rs @@ -36,7 +36,7 @@ mod ram_helpers { /// This function must live in ram. It uses inline asm to avoid any /// potential calls to ABI functions that might be in flash. #[inline(never)] - #[unsafe(link_section = ".data.ram_func")] + #[link_section = ".data.ram_func"] #[cfg(target_arch = "arm")] pub unsafe fn read_cs_status() -> GpioStatus { let result: u32; diff --git a/embassy-rp/src/flash.rs b/embassy-rp/src/flash.rs index 1ac15a677..ef1cd9212 100644 --- a/embassy-rp/src/flash.rs +++ b/embassy-rp/src/flash.rs @@ -623,7 +623,7 @@ mod ram_helpers { /// Length of data must be a multiple of 4096 /// addr must be aligned to 4096 #[inline(never)] - #[unsafe(link_section = ".data.ram_func")] + #[link_section = ".data.ram_func"] #[cfg(feature = "rp2040")] unsafe fn write_flash_inner(addr: u32, len: u32, data: Option<&[u8]>, ptrs: *const FlashFunctionPointers) { #[cfg(target_arch = "arm")] @@ -688,7 +688,7 @@ mod ram_helpers { /// Length of data must be a multiple of 4096 /// addr must be aligned to 4096 #[inline(never)] - #[unsafe(link_section = ".data.ram_func")] + #[link_section = ".data.ram_func"] #[cfg(feature = "_rp235x")] unsafe fn write_flash_inner(addr: u32, len: u32, data: Option<&[u8]>, ptrs: *const FlashFunctionPointers) { let data = data.map(|d| d.as_ptr()).unwrap_or(core::ptr::null()); @@ -807,7 +807,7 @@ mod ram_helpers { /// /// Credit: taken from `rp2040-flash` (also licensed Apache+MIT) #[inline(never)] - #[unsafe(link_section = ".data.ram_func")] + #[link_section = ".data.ram_func"] #[cfg(feature = "rp2040")] unsafe fn read_flash_inner(cmd: FlashCommand, ptrs: *const FlashFunctionPointers) { #[cfg(target_arch = "arm")] diff --git a/embassy-rp/src/lib.rs b/embassy-rp/src/lib.rs index 9b7c2b6ea..f549446bc 100644 --- a/embassy-rp/src/lib.rs +++ b/embassy-rp/src/lib.rs @@ -434,13 +434,13 @@ macro_rules! select_bootloader { ( $( $feature:literal => $loader:ident, )+ default => $default:ident ) => { $( #[cfg(feature = $feature)] - #[unsafe(link_section = ".boot2")] + #[link_section = ".boot2"] #[used] static BOOT2: [u8; 256] = rp2040_boot2::$loader; )* #[cfg(not(any( $( feature = $feature),* )))] - #[unsafe(link_section = ".boot2")] + #[link_section = ".boot2"] #[used] static BOOT2: [u8; 256] = rp2040_boot2::$default; } @@ -463,13 +463,13 @@ macro_rules! select_imagedef { ( $( $feature:literal => $imagedef:ident, )+ default => $default:ident ) => { $( #[cfg(feature = $feature)] - #[unsafe(link_section = ".start_block")] + #[link_section = ".start_block"] #[used] static IMAGE_DEF: crate::block::ImageDef = crate::block::ImageDef::$imagedef(); )* #[cfg(not(any( $( feature = $feature),* )))] - #[unsafe(link_section = ".start_block")] + #[link_section = ".start_block"] #[used] static IMAGE_DEF: crate::block::ImageDef = crate::block::ImageDef::$default(); } diff --git a/embassy-rp/src/multicore.rs b/embassy-rp/src/multicore.rs index ec05bfdf5..d10b6837c 100644 --- a/embassy-rp/src/multicore.rs +++ b/embassy-rp/src/multicore.rs @@ -90,7 +90,7 @@ impl Stack { #[cfg(all(feature = "rt", feature = "rp2040"))] #[interrupt] -#[unsafe(link_section = ".data.ram_func")] +#[link_section = ".data.ram_func"] unsafe fn SIO_IRQ_PROC1() { let sio = pac::SIO; // Clear IRQ @@ -115,7 +115,7 @@ unsafe fn SIO_IRQ_PROC1() { #[cfg(all(feature = "rt", feature = "_rp235x"))] #[interrupt] -#[unsafe(link_section = ".data.ram_func")] +#[link_section = ".data.ram_func"] unsafe fn SIO_IRQ_FIFO() { let sio = pac::SIO; // Clear IRQ diff --git a/embassy-stm32-wpan/src/tables.rs b/embassy-stm32-wpan/src/tables.rs index 204790e6d..fe6fc47a3 100644 --- a/embassy-stm32-wpan/src/tables.rs +++ b/embassy-stm32-wpan/src/tables.rs @@ -190,94 +190,94 @@ pub struct RefTable { } // --------------------- ref table --------------------- -#[unsafe(link_section = "TL_REF_TABLE")] +#[link_section = "TL_REF_TABLE"] pub static mut TL_REF_TABLE: MaybeUninit = MaybeUninit::uninit(); -#[unsafe(link_section = "MB_MEM1")] +#[link_section = "MB_MEM1"] pub static mut TL_DEVICE_INFO_TABLE: Aligned> = Aligned(MaybeUninit::uninit()); -#[unsafe(link_section = "MB_MEM1")] +#[link_section = "MB_MEM1"] pub static mut TL_BLE_TABLE: Aligned> = Aligned(MaybeUninit::uninit()); -#[unsafe(link_section = "MB_MEM1")] +#[link_section = "MB_MEM1"] pub static mut TL_THREAD_TABLE: Aligned> = Aligned(MaybeUninit::uninit()); -#[unsafe(link_section = "MB_MEM1")] +#[link_section = "MB_MEM1"] pub static mut TL_LLD_TESTS_TABLE: Aligned> = Aligned(MaybeUninit::uninit()); -#[unsafe(link_section = "MB_MEM1")] +#[link_section = "MB_MEM1"] pub static mut TL_BLE_LLD_TABLE: Aligned> = Aligned(MaybeUninit::uninit()); -#[unsafe(link_section = "MB_MEM1")] +#[link_section = "MB_MEM1"] pub static mut TL_SYS_TABLE: Aligned> = Aligned(MaybeUninit::uninit()); -#[unsafe(link_section = "MB_MEM1")] +#[link_section = "MB_MEM1"] pub static mut TL_MEM_MANAGER_TABLE: Aligned> = Aligned(MaybeUninit::uninit()); -#[unsafe(link_section = "MB_MEM1")] +#[link_section = "MB_MEM1"] pub static mut TL_TRACES_TABLE: Aligned> = Aligned(MaybeUninit::uninit()); -#[unsafe(link_section = "MB_MEM1")] +#[link_section = "MB_MEM1"] pub static mut TL_MAC_802_15_4_TABLE: Aligned> = Aligned(MaybeUninit::uninit()); -#[unsafe(link_section = "MB_MEM1")] +#[link_section = "MB_MEM1"] pub static mut TL_ZIGBEE_TABLE: Aligned> = Aligned(MaybeUninit::uninit()); // --------------------- tables --------------------- -#[unsafe(link_section = "MB_MEM1")] +#[link_section = "MB_MEM1"] pub static mut FREE_BUF_QUEUE: Aligned> = Aligned(MaybeUninit::uninit()); #[allow(dead_code)] -#[unsafe(link_section = "MB_MEM1")] +#[link_section = "MB_MEM1"] pub static mut TRACES_EVT_QUEUE: Aligned> = Aligned(MaybeUninit::uninit()); -#[unsafe(link_section = "MB_MEM2")] +#[link_section = "MB_MEM2"] pub static mut CS_BUFFER: Aligned> = Aligned(MaybeUninit::uninit()); -#[unsafe(link_section = "MB_MEM2")] +#[link_section = "MB_MEM2"] pub static mut EVT_QUEUE: Aligned> = Aligned(MaybeUninit::uninit()); -#[unsafe(link_section = "MB_MEM2")] +#[link_section = "MB_MEM2"] pub static mut SYSTEM_EVT_QUEUE: Aligned> = Aligned(MaybeUninit::uninit()); // --------------------- app tables --------------------- #[cfg(feature = "mac")] -#[unsafe(link_section = "MB_MEM2")] +#[link_section = "MB_MEM2"] pub static mut MAC_802_15_4_CMD_BUFFER: Aligned> = Aligned(MaybeUninit::uninit()); #[cfg(feature = "mac")] -#[unsafe(link_section = "MB_MEM2")] +#[link_section = "MB_MEM2"] pub static mut MAC_802_15_4_NOTIF_RSP_EVT_BUFFER: MaybeUninit< Aligned, > = MaybeUninit::uninit(); -#[unsafe(link_section = "MB_MEM2")] +#[link_section = "MB_MEM2"] pub static mut EVT_POOL: Aligned> = Aligned(MaybeUninit::uninit()); -#[unsafe(link_section = "MB_MEM2")] +#[link_section = "MB_MEM2"] pub static mut SYS_CMD_BUF: Aligned> = Aligned(MaybeUninit::uninit()); -#[unsafe(link_section = "MB_MEM2")] +#[link_section = "MB_MEM2"] pub static mut SYS_SPARE_EVT_BUF: Aligned> = Aligned(MaybeUninit::uninit()); #[cfg(feature = "mac")] -#[unsafe(link_section = "MB_MEM2")] +#[link_section = "MB_MEM2"] pub static mut MAC_802_15_4_CNFINDNOT: Aligned> = Aligned(MaybeUninit::uninit()); #[cfg(feature = "ble")] -#[unsafe(link_section = "MB_MEM1")] +#[link_section = "MB_MEM1"] pub static mut BLE_CMD_BUFFER: Aligned> = Aligned(MaybeUninit::uninit()); #[cfg(feature = "ble")] -#[unsafe(link_section = "MB_MEM2")] +#[link_section = "MB_MEM2"] pub static mut BLE_SPARE_EVT_BUF: Aligned> = Aligned(MaybeUninit::uninit()); #[cfg(feature = "ble")] -#[unsafe(link_section = "MB_MEM2")] +#[link_section = "MB_MEM2"] // fuck these "magic" numbers from ST ---v---v pub static mut HCI_ACL_DATA_BUFFER: Aligned> = Aligned(MaybeUninit::uninit()); diff --git a/embassy-stm32/src/lib.rs b/embassy-stm32/src/lib.rs index 8ba604830..226293a9d 100644 --- a/embassy-stm32/src/lib.rs +++ b/embassy-stm32/src/lib.rs @@ -331,7 +331,7 @@ mod dual_core { /// use core::mem::MaybeUninit; /// use embassy_stm32::{init_secondary, SharedData}; /// - /// #[unsafe(link_section = ".ram_d3")] + /// #[link_section = ".ram_d3"] /// static SHARED_DATA: MaybeUninit = MaybeUninit::uninit(); /// /// init_secondary(&SHARED_DATA); diff --git a/tests/rp/Cargo.toml b/tests/rp/Cargo.toml index ed3a880a0..1335aa84b 100644 --- a/tests/rp/Cargo.toml +++ b/tests/rp/Cargo.toml @@ -93,6 +93,3 @@ debug = false debug-assertions = false opt-level = 0 overflow-checks = false - -[patch.crates-io] -cortex-m-rt = { git = "https://github.com/yutannihilation/cortex-m", branch = "fix/allow-unsafe-wrap-rust2024" } From f5ddb9358f841d1a1e3a996eb30d7d9fd5d44d38 Mon Sep 17 00:00:00 2001 From: Hiroaki Yutani Date: Wed, 7 May 2025 23:00:39 +0900 Subject: [PATCH 73/97] Remove tweak --- examples/rp/Cargo.toml | 3 --- examples/rp235x/Cargo.toml | 3 --- 2 files changed, 6 deletions(-) diff --git a/examples/rp/Cargo.toml b/examples/rp/Cargo.toml index 97b14a327..45ca30e4c 100644 --- a/examples/rp/Cargo.toml +++ b/examples/rp/Cargo.toml @@ -67,6 +67,3 @@ opt-level = 'z' debug = 2 lto = true opt-level = "z" - -[patch.crates-io] -cortex-m-rt = { git = "https://github.com/yutannihilation/cortex-m", branch = "fix/allow-unsafe-wrap-rust2024" } diff --git a/examples/rp235x/Cargo.toml b/examples/rp235x/Cargo.toml index 99091ee90..345a915af 100644 --- a/examples/rp235x/Cargo.toml +++ b/examples/rp235x/Cargo.toml @@ -64,6 +64,3 @@ debug = 2 [profile.dev] lto = true opt-level = "z" - -[patch.crates-io] -cortex-m-rt = { git = "https://github.com/yutannihilation/cortex-m", branch = "fix/allow-unsafe-wrap-rust2024" } From 70e2c052056e31f06ac67d69fda503f07cae46d0 Mon Sep 17 00:00:00 2001 From: Felipe Balbi Date: Wed, 7 May 2025 08:33:03 -0700 Subject: [PATCH 74/97] Rename rtc.rs to time_driver.rs We will add another time driver for user selection. --- embassy-imxrt/src/lib.rs | 4 ++-- embassy-imxrt/src/{rtc.rs => time_driver.rs} | 0 2 files changed, 2 insertions(+), 2 deletions(-) rename embassy-imxrt/src/{rtc.rs => time_driver.rs} (100%) diff --git a/embassy-imxrt/src/lib.rs b/embassy-imxrt/src/lib.rs index 5fbf3244b..fdf07b433 100644 --- a/embassy-imxrt/src/lib.rs +++ b/embassy-imxrt/src/lib.rs @@ -22,7 +22,7 @@ pub mod gpio; pub mod iopctl; #[cfg(feature = "_time-driver")] -pub mod rtc; +pub mod time_driver; // This mod MUST go last, so that it sees all the `impl_foo!' macros #[cfg_attr(feature = "mimxrt633s", path = "chips/mimxrt633s.rs")] @@ -137,7 +137,7 @@ pub fn init(config: config::Config) -> Peripherals { // init RTC time driver #[cfg(feature = "_time-driver")] - rtc::init(config.time_interrupt_priority); + time_driver::init(config.time_interrupt_priority); peripherals } diff --git a/embassy-imxrt/src/rtc.rs b/embassy-imxrt/src/time_driver.rs similarity index 100% rename from embassy-imxrt/src/rtc.rs rename to embassy-imxrt/src/time_driver.rs From 64ce271af526a9311b0c1c251b19b5ce79032551 Mon Sep 17 00:00:00 2001 From: Felipe Balbi Date: Wed, 7 May 2025 10:39:25 -0700 Subject: [PATCH 75/97] clocks: split clock and reset operations Some peripherals need to control clock without touching the reset. Signed-off-by: Felipe Balbi --- embassy-imxrt/src/clocks.rs | 30 ++++++++++++++++++++++-------- 1 file changed, 22 insertions(+), 8 deletions(-) diff --git a/embassy-imxrt/src/clocks.rs b/embassy-imxrt/src/clocks.rs index 1d36fb142..5be5f3925 100644 --- a/embassy-imxrt/src/clocks.rs +++ b/embassy-imxrt/src/clocks.rs @@ -1561,7 +1561,8 @@ pub(crate) unsafe fn init(config: ClockConfig) -> Result<(), ClockError> { ///Trait to expose perph clocks trait SealedSysconPeripheral { - fn enable_and_reset_perph_clock(); + fn enable_perph_clock(); + fn reset_perph(); fn disable_perph_clock(); } @@ -1574,7 +1575,18 @@ pub trait SysconPeripheral: SealedSysconPeripheral + 'static {} /// /// Peripheral must not be in use. pub fn enable_and_reset() { - T::enable_and_reset_perph_clock(); + T::enable_perph_clock(); + T::reset_perph(); +} + +/// Enables peripheral `T`. +pub fn enable() { + T::enable_perph_clock(); +} + +/// Reset peripheral `T`. +pub fn reset() { + T::reset_perph(); } /// Disables peripheral `T`. @@ -1588,15 +1600,21 @@ pub fn disable() { macro_rules! impl_perph_clk { ($peripheral:ident, $clkctl:ident, $clkreg:ident, $rstctl:ident, $rstreg:ident, $bit:expr) => { impl SealedSysconPeripheral for crate::peripherals::$peripheral { - fn enable_and_reset_perph_clock() { + fn enable_perph_clock() { // SAFETY: unsafe needed to take pointers to Rstctl1 and Clkctl1 let cc1 = unsafe { pac::$clkctl::steal() }; - let rc1 = unsafe { pac::$rstctl::steal() }; paste! { // SAFETY: unsafe due to the use of bits() cc1.[<$clkreg _set>]().write(|w| unsafe { w.bits(1 << $bit) }); + } + } + fn reset_perph() { + // SAFETY: unsafe needed to take pointers to Rstctl1 and Clkctl1 + let rc1 = unsafe { pac::$rstctl::steal() }; + + paste! { // SAFETY: unsafe due to the use of bits() rc1.[<$rstreg _clr>]().write(|w| unsafe { w.bits(1 << $bit) }); } @@ -1605,12 +1623,8 @@ macro_rules! impl_perph_clk { fn disable_perph_clock() { // SAFETY: unsafe needed to take pointers to Rstctl1 and Clkctl1 let cc1 = unsafe { pac::$clkctl::steal() }; - let rc1 = unsafe { pac::$rstctl::steal() }; paste! { - // SAFETY: unsafe due to the use of bits() - rc1.[<$rstreg _set>]().write(|w| unsafe { w.bits(1 << $bit) }); - // SAFETY: unsafe due to the use of bits() cc1.[<$clkreg _clr>]().write(|w| unsafe { w.bits(1 << $bit) }); } From a254daf4fffe74c65d1846f620dd674fa4e14aac Mon Sep 17 00:00:00 2001 From: 1-rafael-1 Date: Wed, 7 May 2025 21:19:09 +0200 Subject: [PATCH 76/97] Changes after review --- embassy-rp/src/clocks.rs | 51 ++++++++++++++++++++++---------- examples/rp/src/bin/overclock.rs | 2 +- tests/rp/src/bin/overclock.rs | 2 +- 3 files changed, 37 insertions(+), 18 deletions(-) diff --git a/embassy-rp/src/clocks.rs b/embassy-rp/src/clocks.rs index a4cc129ab..0c3988aac 100644 --- a/embassy-rp/src/clocks.rs +++ b/embassy-rp/src/clocks.rs @@ -9,7 +9,7 @@ //! //! For most users, these functions provide an easy way to configure clocks: //! -//! - `ClockConfig::at_sys_frequency_mhz(200)` - Set system clock to a specific frequency with automatic voltage scaling +//! - `ClockConfig::system_freq(125_000_000)` - Set system clock to a specific frequency with automatic voltage scaling //! - `ClockConfig::crystal(12_000_000)` - Default configuration with 12MHz crystal giving 125MHz system clock //! //! ## Manual Configuration @@ -50,7 +50,7 @@ //! //! ### Overclock to 200MHz //! ```rust,ignore -//! let config = ClockConfig::crystal_freq(200_000_000); +//! let config = ClockConfig::system_freq(200_000_000); //! ``` //! //! ### Manual configuration for advanced scenarios @@ -90,6 +90,7 @@ struct Clocks { pll_usb: AtomicU32, usb: AtomicU32, adc: AtomicU32, + // See above re gpin handling being commented out // gpin0: AtomicU32, // gpin1: AtomicU32, rosc: AtomicU32, @@ -106,6 +107,7 @@ static CLOCKS: Clocks = Clocks { pll_usb: AtomicU32::new(0), usb: AtomicU32::new(0), adc: AtomicU32::new(0), + // See above re gpin handling being commented out // gpin0: AtomicU32::new(0), // gpin1: AtomicU32::new(0), rosc: AtomicU32::new(0), @@ -129,6 +131,7 @@ pub enum PeriClkSrc { Rosc = ClkPeriCtrlAuxsrc::ROSC_CLKSRC_PH as _, /// XOSC. Xosc = ClkPeriCtrlAuxsrc::XOSC_CLKSRC as _, + // See above re gpin handling being commented out // Gpin0 = ClkPeriCtrlAuxsrc::CLKSRC_GPIN0 as _ , // Gpin1 = ClkPeriCtrlAuxsrc::CLKSRC_GPIN1 as _ , } @@ -213,6 +216,7 @@ pub struct ClockConfig { /// If not set, defaults will be used based on voltage level. #[cfg(feature = "rp2040")] pub voltage_stabilization_delay_us: Option, + // See above re gpin handling being commented out // gpin0: Option<(u32, Gpin<'static, AnyPin>)>, // gpin1: Option<(u32, Gpin<'static, AnyPin>)>, } @@ -227,7 +231,7 @@ impl Default for ClockConfig { /// Most users should use one of the more specific configuration functions: /// - `ClockConfig::crystal()` - Standard configuration with external crystal /// - `ClockConfig::rosc()` - Configuration using only the internal oscillator - /// - `ClockConfig::at_sys_frequency_mhz()` - Configuration for a specific system frequency + /// - `ClockConfig::system_freq()` - Configuration for a specific system frequency fn default() -> Self { Self { rosc: None, @@ -250,6 +254,7 @@ impl Default for ClockConfig { core_voltage: CoreVoltage::V1_10, #[cfg(feature = "rp2040")] voltage_stabilization_delay_us: None, + // See above re gpin handling being commented out // gpin0: None, // gpin1: None, } @@ -322,6 +327,7 @@ impl ClockConfig { core_voltage: CoreVoltage::V1_10, // Use hardware default (1.10V) #[cfg(feature = "rp2040")] voltage_stabilization_delay_us: None, + // See above re gpin handling being commented out // gpin0: None, // gpin1: None, } @@ -366,6 +372,7 @@ impl ClockConfig { core_voltage: CoreVoltage::V1_10, // Use hardware default (1.10V) #[cfg(feature = "rp2040")] voltage_stabilization_delay_us: None, + // See above re gpin handling being commented out // gpin0: None, // gpin1: None, } @@ -393,19 +400,19 @@ impl ClockConfig { /// That way all other frequencies below 133MHz or above 200MHz are not explicitly documented and not covered here. /// In case You want to go below 133MHz or above 200MHz and want a different voltage, You will have to set that manually and with caution. #[cfg(feature = "rp2040")] - pub fn crystal_freq(sys_freq_hz: u32) -> Self { + pub fn system_freq(hz: u32) -> Self { // Start with the standard configuration from crystal() const DEFAULT_CRYSTAL_HZ: u32 = 12_000_000; let mut config = Self::crystal(DEFAULT_CRYSTAL_HZ); // No need to modify anything if target frequency is already 125MHz // (which is what crystal() configures by default) - if sys_freq_hz == 125_000_000 { + if hz == 125_000_000 { return config; } // Find optimal PLL parameters for the requested frequency - let sys_pll_params = find_pll_params(DEFAULT_CRYSTAL_HZ, sys_freq_hz) + let sys_pll_params = find_pll_params(DEFAULT_CRYSTAL_HZ, hz) .unwrap_or_else(|| panic!("Could not find valid PLL parameters for system clock")); // Replace the sys_pll configuration with our custom parameters @@ -417,7 +424,7 @@ impl ClockConfig { // Higher frequencies require higher voltage #[cfg(feature = "rp2040")] { - config.core_voltage = match sys_freq_hz { + config.core_voltage = match hz { freq if freq > 133_000_000 => CoreVoltage::V1_15, _ => CoreVoltage::V1_10, // Use default voltage (V1_10) }; @@ -631,6 +638,7 @@ pub enum RefClkSrc { Rosc, /// PLL USB. PllUsb, + // See above re gpin handling being commented out // Gpin0, // Gpin1, } @@ -649,6 +657,7 @@ pub enum SysClkSrc { Rosc, /// XOSC. Xosc, + // See above re gpin handling being commented out // Gpin0, // Gpin1, } @@ -684,6 +693,7 @@ pub enum UsbClkSrc { Rosc = ClkUsbCtrlAuxsrc::ROSC_CLKSRC_PH as _, /// XOSC. Xosc = ClkUsbCtrlAuxsrc::XOSC_CLKSRC as _, + // See above re gpin handling being commented out // Gpin0 = ClkUsbCtrlAuxsrc::CLKSRC_GPIN0 as _ , // Gpin1 = ClkUsbCtrlAuxsrc::CLKSRC_GPIN1 as _ , } @@ -711,6 +721,7 @@ pub enum AdcClkSrc { Rosc = ClkAdcCtrlAuxsrc::ROSC_CLKSRC_PH as _, /// XOSC. Xosc = ClkAdcCtrlAuxsrc::XOSC_CLKSRC as _, + // See above re gpin handling being commented out // Gpin0 = ClkAdcCtrlAuxsrc::CLKSRC_GPIN0 as _ , // Gpin1 = ClkAdcCtrlAuxsrc::CLKSRC_GPIN1 as _ , } @@ -739,6 +750,7 @@ pub enum RtcClkSrc { Rosc = ClkRtcCtrlAuxsrc::ROSC_CLKSRC_PH as _, /// XOSC. Xosc = ClkRtcCtrlAuxsrc::XOSC_CLKSRC as _, + // See above re gpin handling being commented out // Gpin0 = ClkRtcCtrlAuxsrc::CLKSRC_GPIN0 as _ , // Gpin1 = ClkRtcCtrlAuxsrc::CLKSRC_GPIN1 as _ , } @@ -895,6 +907,7 @@ pub(crate) unsafe fn init(config: ClockConfig) { reset::reset(peris); reset::unreset_wait(peris); + // See above re gpin handling being commented out // let gpin0_freq = config.gpin0.map_or(0, |p| { // core::mem::forget(p.1); // p.0 @@ -941,7 +954,7 @@ pub(crate) unsafe fn init(config: ClockConfig) { cortex_m::asm::delay(delay_cycles); } - // Only now set the BOD level. At htis point the voltage is considered stable. + // Only now set the BOD level. At this point the voltage is considered stable. vreg.bod().write(|w| { w.set_vsel(voltage.recommended_bod()); w.set_en(true); // Enable brownout detection @@ -952,8 +965,6 @@ pub(crate) unsafe fn init(config: ClockConfig) { let (xosc_freq, pll_sys_freq, pll_usb_freq) = match config.xosc { Some(config) => { // start XOSC - // datasheet mentions support for clock inputs into XIN, but doesn't go into - // how this is achieved. pico-sdk doesn't support this at all. start_xosc(config.hz, config.delay_multiplier); let pll_sys_freq = match config.sys_pll { @@ -988,6 +999,7 @@ pub(crate) unsafe fn init(config: ClockConfig) { RefClkSrc::Xosc => (Src::XOSC_CLKSRC, Aux::CLKSRC_PLL_USB, xosc_freq / div), RefClkSrc::Rosc => (Src::ROSC_CLKSRC_PH, Aux::CLKSRC_PLL_USB, rosc_freq / div), RefClkSrc::PllUsb => (Src::CLKSRC_CLK_REF_AUX, Aux::CLKSRC_PLL_USB, pll_usb_freq / div), + // See above re gpin handling being commented out // RefClkSrc::Gpin0 => (Src::CLKSRC_CLK_REF_AUX, Aux::CLKSRC_GPIN0, gpin0_freq / div), // RefClkSrc::Gpin1 => (Src::CLKSRC_CLK_REF_AUX, Aux::CLKSRC_GPIN1, gpin1_freq / div), } @@ -1032,6 +1044,7 @@ pub(crate) unsafe fn init(config: ClockConfig) { SysClkSrc::PllUsb => (Src::CLKSRC_CLK_SYS_AUX, Aux::CLKSRC_PLL_USB, pll_usb_freq), SysClkSrc::Rosc => (Src::CLKSRC_CLK_SYS_AUX, Aux::ROSC_CLKSRC, rosc_freq), SysClkSrc::Xosc => (Src::CLKSRC_CLK_SYS_AUX, Aux::XOSC_CLKSRC, xosc_freq), + // See above re gpin handling being commented out // SysClkSrc::Gpin0 => (Src::CLKSRC_CLK_SYS_AUX, Aux::CLKSRC_GPIN0, gpin0_freq), // SysClkSrc::Gpin1 => (Src::CLKSRC_CLK_SYS_AUX, Aux::CLKSRC_GPIN1, gpin1_freq), }; @@ -1075,6 +1088,7 @@ pub(crate) unsafe fn init(config: ClockConfig) { PeriClkSrc::PllUsb => pll_usb_freq, PeriClkSrc::Rosc => rosc_freq, PeriClkSrc::Xosc => xosc_freq, + // See above re gpin handling being commented out // PeriClkSrc::Gpin0 => gpin0_freq, // PeriClkSrc::Gpin1 => gpin1_freq, }; @@ -1100,6 +1114,7 @@ pub(crate) unsafe fn init(config: ClockConfig) { UsbClkSrc::PllSys => pll_sys_freq, UsbClkSrc::Rosc => rosc_freq, UsbClkSrc::Xosc => xosc_freq, + // See above re gpin handling being commented out // UsbClkSrc::Gpin0 => gpin0_freq, // UsbClkSrc::Gpin1 => gpin1_freq, }; @@ -1123,6 +1138,7 @@ pub(crate) unsafe fn init(config: ClockConfig) { AdcClkSrc::PllSys => pll_sys_freq, AdcClkSrc::Rosc => rosc_freq, AdcClkSrc::Xosc => xosc_freq, + // See above re gpin handling being commented out // AdcClkSrc::Gpin0 => gpin0_freq, // AdcClkSrc::Gpin1 => gpin1_freq, }; @@ -1151,6 +1167,7 @@ pub(crate) unsafe fn init(config: ClockConfig) { RtcClkSrc::PllSys => pll_sys_freq, RtcClkSrc::Rosc => rosc_freq, RtcClkSrc::Xosc => xosc_freq, + // See above re gpin handling being commented out // RtcClkSrc::Gpin0 => gpin0_freq, // RtcClkSrc::Gpin1 => gpin1_freq, }; @@ -1217,6 +1234,7 @@ pub fn xosc_freq() -> u32 { CLOCKS.xosc.load(Ordering::Relaxed) } +// See above re gpin handling being commented out // pub fn gpin0_freq() -> u32 { // CLOCKS.gpin0.load(Ordering::Relaxed) // } @@ -1452,6 +1470,7 @@ impl_gpoutpin!(PIN_25, 3); pub enum GpoutSrc { /// Sys PLL. PllSys = ClkGpoutCtrlAuxsrc::CLKSRC_PLL_SYS as _, + // See above re gpin handling being commented out // Gpin0 = ClkGpoutCtrlAuxsrc::CLKSRC_GPIN0 as _ , // Gpin1 = ClkGpoutCtrlAuxsrc::CLKSRC_GPIN1 as _ , /// USB PLL. @@ -1547,6 +1566,7 @@ impl<'d, T: GpoutPin> Gpout<'d, T> { let base = match src { ClkGpoutCtrlAuxsrc::CLKSRC_PLL_SYS => pll_sys_freq(), + // See above re gpin handling being commented out // ClkGpoutCtrlAuxsrc::CLKSRC_GPIN0 => gpin0_freq(), // ClkGpoutCtrlAuxsrc::CLKSRC_GPIN1 => gpin1_freq(), ClkGpoutCtrlAuxsrc::CLKSRC_PLL_USB => pll_usb_freq(), @@ -1555,7 +1575,6 @@ impl<'d, T: GpoutPin> Gpout<'d, T> { ClkGpoutCtrlAuxsrc::CLK_SYS => clk_sys_freq(), ClkGpoutCtrlAuxsrc::CLK_USB => clk_usb_freq(), ClkGpoutCtrlAuxsrc::CLK_ADC => clk_adc_freq(), - //ClkGpoutCtrlAuxsrc::CLK_RTC => clk_rtc_freq() as _, ClkGpoutCtrlAuxsrc::CLK_REF => clk_ref_freq(), _ => unreachable!(), }; @@ -1918,21 +1937,21 @@ mod tests { { // Test automatic voltage scaling based on frequency // Under 133 MHz should use default voltage (V1_10) - let config = ClockConfig::crystal_freq(125_000_000); + let config = ClockConfig::system_freq(125_000_000); assert_eq!(config.core_voltage, CoreVoltage::V1_10); // 133-200 MHz should use V1_15 - let config = ClockConfig::crystal_freq(150_000_000); + let config = ClockConfig::system_freq(150_000_000); assert_eq!(config.core_voltage, CoreVoltage::V1_15); - let config = ClockConfig::crystal_freq(200_000_000); + let config = ClockConfig::system_freq(200_000_000); assert_eq!(config.core_voltage, CoreVoltage::V1_15); // Above 200 MHz should use V1_25 - let config = ClockConfig::crystal_freq(250_000_000); + let config = ClockConfig::system_freq(250_000_000); assert_eq!(config.core_voltage, CoreVoltage::V1_15); // Below 125 MHz should use V1_10 - let config = ClockConfig::crystal_freq(100_000_000); + let config = ClockConfig::system_freq(100_000_000); assert_eq!(config.core_voltage, CoreVoltage::V1_10); } } diff --git a/examples/rp/src/bin/overclock.rs b/examples/rp/src/bin/overclock.rs index f9a8c94d0..9c78e0c9d 100644 --- a/examples/rp/src/bin/overclock.rs +++ b/examples/rp/src/bin/overclock.rs @@ -18,7 +18,7 @@ const COUNT_TO: i64 = 10_000_000; #[embassy_executor::main] async fn main(_spawner: Spawner) -> ! { // Set up for clock frequency of 200 MHz, setting all necessary defaults. - let config = Config::new(ClockConfig::crystal_freq(200_000_000)); + let config = Config::new(ClockConfig::system_freq(200_000_000)); // Show the voltage scale for verification info!("System core voltage: {}", Debug2Format(&config.clocks.core_voltage)); diff --git a/tests/rp/src/bin/overclock.rs b/tests/rp/src/bin/overclock.rs index 6c58a6b90..be8e85a3f 100644 --- a/tests/rp/src/bin/overclock.rs +++ b/tests/rp/src/bin/overclock.rs @@ -31,7 +31,7 @@ async fn main(_spawner: Spawner) { // Initialize with 200MHz clock configuration for RP2040, other chips will use default clock #[cfg(feature = "rp2040")] { - config.clocks = ClockConfig::crystal_freq(200_000_000); + config.clocks = ClockConfig::system_freq(200_000_000); let voltage = config.clocks.core_voltage; assert!(matches!(voltage, CoreVoltage::V1_15), "Expected voltage scale V1_15"); } From 297ff3d03229bedb2582c171be23b488ecc4e520 Mon Sep 17 00:00:00 2001 From: Felipe Balbi Date: Wed, 7 May 2025 12:56:58 -0700 Subject: [PATCH 77/97] clocks: remove defmt messages Whenever any of the defmt-timestamp-uptime* features is enabled, defmt will insert code that reads the timestamp in order to embed it into the format string. This means that we *must* have a functional time driver by the time the very first defmt message is printed. Because clocks.rs is the part of the code setting up clocks that may, indeed, be required by the chosen clock driver, it cannot contain any defmt messages, otherwise it will trigger a read to a function that does not yet exist. Signed-off-by: Felipe Balbi --- embassy-imxrt/src/clocks.rs | 42 +------------------------------------ 1 file changed, 1 insertion(+), 41 deletions(-) diff --git a/embassy-imxrt/src/clocks.rs b/embassy-imxrt/src/clocks.rs index 5be5f3925..39c3e6238 100644 --- a/embassy-imxrt/src/clocks.rs +++ b/embassy-imxrt/src/clocks.rs @@ -1,8 +1,6 @@ //! Clock configuration for the `RT6xx` use core::sync::atomic::{AtomicU32, AtomicU8, Ordering}; -#[cfg(feature = "defmt")] -use defmt; use paste::paste; use crate::pac; @@ -503,7 +501,6 @@ impl ConfigurableClock for LposcConfig { } } } else { - error!("failed to convert desired clock rate, {:#}, to LPOSC Freq", freq); Err(ClockError::InvalidFrequency) } } @@ -549,7 +546,6 @@ impl ConfigurableClock for FfroConfig { Ok(()) } fn get_clock_rate(&self) -> Result { - trace!("getting ffro clock rate"); Ok(self.freq.load(Ordering::Relaxed)) } fn set_clock_rate(&mut self, _div: u8, _mult: u8, freq: u32) -> Result<(), ClockError> { @@ -616,7 +612,6 @@ impl ConfigurableClock for SfroConfig { fn set_clock_rate(&mut self, _div: u8, _mult: u8, freq: u32) -> Result<(), ClockError> { if self.state == State::Enabled { if freq == SFRO_FREQ { - trace!("Sfro frequency is already set at 16MHz"); Ok(()) } else { Err(ClockError::InvalidFrequency) @@ -677,7 +672,6 @@ impl MultiSourceClock for MainPllClkConfig { } MainPllClkSrc::SFRO => { if !clock_src_config.is_enabled() { - error!("Can't set SFRO as source for MainPll as it's not enabled"); return Err(ClockError::ClockNotEnabled); } // check if desired frequency is a valid multiple of 16m SFRO clock @@ -703,7 +697,6 @@ impl ConfigurableClock for MainPllClkConfig { } fn disable(&self) -> Result<(), ClockError> { if self.is_enabled() { - error!("Attempting to reset the Main Pll Clock, should be resetting its source"); Err(ClockError::ClockNotSupported) } else { Err(ClockError::ClockNotEnabled) @@ -719,7 +712,6 @@ impl ConfigurableClock for MainPllClkConfig { } fn set_clock_rate(&mut self, div: u8, mult: u8, freq: u32) -> Result<(), ClockError> { if self.is_enabled() { - trace!("attempting to set main pll clock rate"); // SAFETY: unsafe needed to take pointers to Sysctl0 and Clkctl0 let clkctl0 = unsafe { crate::pac::Clkctl0::steal() }; let sysctl0 = unsafe { crate::pac::Sysctl0::steal() }; @@ -741,15 +733,12 @@ impl ConfigurableClock for MainPllClkConfig { base_rate = r; } MainPllClkSrc::FFRO => { - trace!("found FFRO as source, wait a bit"); delay_loop_clocks(1000, desired_freq); match clkctl0.ffroctl0().read().trim_range().is_ffro_48mhz() { true => base_rate = Into::into(FfroFreq::Ffro48m), false => base_rate = Into::into(FfroFreq::Ffro60m), } - trace!("found ffro rate to be: {:#}", base_rate); if div == 2 { - trace!("dividing FFRO rate by 2"); clkctl0.syspll0clksel().write(|w| w.sel().ffro_div_2()); delay_loop_clocks(150, desired_freq); base_rate /= 2; @@ -763,10 +752,8 @@ impl ConfigurableClock for MainPllClkConfig { } }; base_rate *= u32::from(mult); - trace!("calculated base rate at: {:#}", base_rate); if base_rate != freq { // make sure to power syspll back up before returning the error - error!("invalid frequency found, powering syspll back up before returning error. Check div and mult"); // Clear System PLL reset clkctl0.syspll0ctl0().write(|w| w.reset().normal()); // Power up SYSPLL @@ -775,13 +762,11 @@ impl ConfigurableClock for MainPllClkConfig { .write(|w| w.syspllana_pd().clr_pdruncfg0().syspllldo_pd().clr_pdruncfg0()); return Err(ClockError::InvalidFrequency); } - trace!("setting default num and denom"); // SAFETY: unsafe needed to write the bits for the num and demon fields clkctl0.syspll0num().write(|w| unsafe { w.num().bits(0b0) }); clkctl0.syspll0denom().write(|w| unsafe { w.denom().bits(0b1) }); delay_loop_clocks(30, desired_freq); self.mult.store(mult, Ordering::Relaxed); - trace!("setting self.mult as: {:#}", mult); match mult { 16 => { clkctl0.syspll0ctl0().modify(|_r, w| w.mult().div_16()); @@ -803,7 +788,6 @@ impl ConfigurableClock for MainPllClkConfig { } _ => return Err(ClockError::InvalidMult), } - trace!("clear syspll reset"); // Clear System PLL reset clkctl0.syspll0ctl0().modify(|_r, w| w.reset().normal()); // Power up SYSPLL @@ -819,7 +803,6 @@ impl ConfigurableClock for MainPllClkConfig { clkctl0.syspll0ctl0().modify(|_, w| w.holdringoff_ena().dsiable()); delay_loop_clocks(15, desired_freq); - trace!("setting new PFD0 bits"); // gate the output and clear bits. // SAFETY: unsafe needed to write the bits for pfd0 clkctl0 @@ -833,7 +816,6 @@ impl ConfigurableClock for MainPllClkConfig { .modify(|_r, w| unsafe { w.pfd0_clkgate().not_gated().pfd0().bits(0x12) }); // wait for ready bit to be set delay_loop_clocks(50, desired_freq); - trace!("waiting for mainpll clock to be ready"); while clkctl0.syspll0pfd().read().pfd0_clkrdy().bit_is_clear() {} // clear by writing a 1 clkctl0.syspll0pfd().modify(|_, w| w.pfd0_clkrdy().set_bit()); @@ -854,11 +836,9 @@ impl ConfigurableClock for MainPllClkConfig { impl MainPllClkConfig { /// Calculate the mult value of a desired frequency, return error if invalid pub(self) fn calc_mult(rate: u32, base_freq: u32) -> Result { - trace!("calculating mult for {:#} / {:#}", rate, base_freq); const VALIDMULTS: [u8; 6] = [16, 17, 20, 22, 27, 33]; if rate > base_freq && rate % base_freq == 0 { let mult = (rate / base_freq) as u8; - trace!("verifying that calculated mult {:#} is a valid one", mult); if VALIDMULTS.into_iter().any(|i| i == mult) { Ok(mult) } else { @@ -1112,7 +1092,6 @@ impl ConfigurableClock for MainClkConfig { Ok(()) } fn disable(&self) -> Result<(), ClockError> { - error!("Attempting to reset the main clock, should NOT happen during runtime"); Err(ClockError::ClockNotSupported) } fn get_clock_rate(&self) -> Result { @@ -1120,7 +1099,6 @@ impl ConfigurableClock for MainClkConfig { Ok(rate) } fn set_clock_rate(&mut self, _div: u8, _mult: u8, _freq: u32) -> Result<(), ClockError> { - error!("The multi-source set_clock_rate_and_source method should be used instead of set_clock_rate"); Err(ClockError::ClockNotSupported) } fn is_enabled(&self) -> bool { @@ -1145,7 +1123,6 @@ impl ConfigurableClock for ClkInConfig { } } fn set_clock_rate(&mut self, _div: u8, _mult: u8, freq: u32) -> Result<(), ClockError> { - trace!("Setting value of clk in config, this won't change the clock itself"); self.freq.as_ref().unwrap().store(freq, Ordering::Relaxed); Ok(()) } @@ -1188,7 +1165,6 @@ impl ConfigurableClock for RtcClkConfig { Ok(()) } fn disable(&self) -> Result<(), ClockError> { - error!("Resetting the RTC clock, this should NOT happen during runtime"); Err(ClockError::ClockNotSupported) } fn set_clock_rate(&mut self, _div: u8, _mult: u8, freq: u32) -> Result<(), ClockError> { @@ -1199,7 +1175,6 @@ impl ConfigurableClock for RtcClkConfig { match r { RtcFreq::Default1Hz => { if rtc.ctrl().read().rtc_en().is_enable() { - trace!("Attempting to enable an already enabled clock, RTC 1Hz"); } else { rtc.ctrl().modify(|_r, w| w.rtc_en().enable()); } @@ -1207,7 +1182,6 @@ impl ConfigurableClock for RtcClkConfig { } RtcFreq::HighResolution1khz => { if rtc.ctrl().read().rtc1khz_en().is_enable() { - trace!("Attempting to enable an already enabled clock, RTC 1Hz"); } else { rtc.ctrl().modify(|_r, w| w.rtc1khz_en().enable()); } @@ -1215,7 +1189,6 @@ impl ConfigurableClock for RtcClkConfig { } RtcFreq::SubSecond32kHz => { if rtc.ctrl().read().rtc_subsec_ena().is_enable() { - trace!("Attempting to enable an already enabled clock, RTC 1Hz"); } else { rtc.ctrl().modify(|_r, w| w.rtc_subsec_ena().enable()); } @@ -1245,18 +1218,12 @@ impl ConfigurableClock for RtcClkConfig { impl SysClkConfig { /// Updates the system core clock frequency, SW concept used for systick - fn update_sys_core_clock(&self) { - trace!( - "System core clock has been updated to {:?}, this involves no HW reg writes", - self.sysclkfreq.load(Ordering::Relaxed) - ); - } + fn update_sys_core_clock(&self) {} } impl ConfigurableClock for SysOscConfig { fn enable_and_reset(&self) -> Result<(), ClockError> { if self.state == State::Enabled { - trace!("SysOsc was already enabled"); return Ok(()); } @@ -1498,32 +1465,26 @@ impl ClockOutConfig { /// Using the config, enables all desired clocks to desired clock rates fn init_clock_hw(config: ClockConfig) -> Result<(), ClockError> { if let Err(e) = config.rtc.enable_and_reset() { - error!("couldn't Power on OSC for RTC, result: {:?}", e); return Err(e); } if let Err(e) = config.lposc.enable_and_reset() { - error!("couldn't Power on LPOSC, result: {:?}", e); return Err(e); } if let Err(e) = config.ffro.enable_and_reset() { - error!("couldn't Power on FFRO, result: {:?}", e); return Err(e); } if let Err(e) = config.sfro.enable_and_reset() { - error!("couldn't Power on SFRO, result: {:?}", e); return Err(e); } if let Err(e) = config.sys_osc.enable_and_reset() { - error!("Couldn't enable sys oscillator {:?}", e); return Err(e); } if let Err(e) = config.main_pll_clk.enable_and_reset() { - error!("Couldn't enable main pll clock {:?}", e); return Err(e); } @@ -1542,7 +1503,6 @@ fn init_clock_hw(config: ClockConfig) -> Result<(), ClockError> { init_syscpuahb_clk(); if let Err(e) = config.main_clk.enable_and_reset() { - error!("Couldn't enable main clock {:?}", e); return Err(e); } From 4621c8aa7a1ee1b55f2f0bf80fc48eddf76af320 Mon Sep 17 00:00:00 2001 From: 1-rafael-1 Date: Wed, 7 May 2025 22:16:29 +0200 Subject: [PATCH 78/97] Clarify comment for CoreVoltage enum regarding V1_20 --- embassy-rp/src/clocks.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/embassy-rp/src/clocks.rs b/embassy-rp/src/clocks.rs index 0c3988aac..6694aab66 100644 --- a/embassy-rp/src/clocks.rs +++ b/embassy-rp/src/clocks.rs @@ -160,7 +160,7 @@ pub enum CoreVoltage { V1_10 = 0b1011, /// 1.15V - Required for overclocking to 133-200MHz V1_15 = 0b1100, - /// 1.20V - Required for overclocking above 200MHz + /// 1.20V V1_20 = 0b1101, /// 1.25V V1_25 = 0b1110, From d46d3119cea14f6cd14c2fae7bf712faff4e6cfe Mon Sep 17 00:00:00 2001 From: Matt Rodgers Date: Wed, 7 May 2025 21:15:54 +0100 Subject: [PATCH 79/97] Implement embedded-io-async traits for USB CDC ACM --- embassy-usb-driver/Cargo.toml | 1 + embassy-usb-driver/src/lib.rs | 9 +++ embassy-usb/Cargo.toml | 1 + embassy-usb/src/class/cdc_acm.rs | 99 ++++++++++++++++++++++++++++++++ 4 files changed, 110 insertions(+) diff --git a/embassy-usb-driver/Cargo.toml b/embassy-usb-driver/Cargo.toml index 41493f00d..edb6551b0 100644 --- a/embassy-usb-driver/Cargo.toml +++ b/embassy-usb-driver/Cargo.toml @@ -20,3 +20,4 @@ features = ["defmt"] [dependencies] defmt = { version = "0.3", optional = true } +embedded-io-async = "0.6.1" diff --git a/embassy-usb-driver/src/lib.rs b/embassy-usb-driver/src/lib.rs index 3b705c8c4..d204e4d85 100644 --- a/embassy-usb-driver/src/lib.rs +++ b/embassy-usb-driver/src/lib.rs @@ -395,3 +395,12 @@ pub enum EndpointError { /// The endpoint is disabled. Disabled, } + +impl embedded_io_async::Error for EndpointError { + fn kind(&self) -> embedded_io_async::ErrorKind { + match self { + Self::BufferOverflow => embedded_io_async::ErrorKind::OutOfMemory, + Self::Disabled => embedded_io_async::ErrorKind::NotConnected, + } + } +} diff --git a/embassy-usb/Cargo.toml b/embassy-usb/Cargo.toml index 771190c89..4950fbe2a 100644 --- a/embassy-usb/Cargo.toml +++ b/embassy-usb/Cargo.toml @@ -52,6 +52,7 @@ embassy-sync = { version = "0.6.2", path = "../embassy-sync" } embassy-net-driver-channel = { version = "0.3.0", path = "../embassy-net-driver-channel" } defmt = { version = "0.3", optional = true } +embedded-io-async = "0.6.1" log = { version = "0.4.14", optional = true } heapless = "0.8" diff --git a/embassy-usb/src/class/cdc_acm.rs b/embassy-usb/src/class/cdc_acm.rs index ea9d9fb7b..732a433f8 100644 --- a/embassy-usb/src/class/cdc_acm.rs +++ b/embassy-usb/src/class/cdc_acm.rs @@ -410,6 +410,18 @@ impl<'d, D: Driver<'d>> Sender<'d, D> { } } +impl<'d, D: Driver<'d>> embedded_io_async::ErrorType for Sender<'d, D> { + type Error = EndpointError; +} + +impl<'d, D: Driver<'d>> embedded_io_async::Write for Sender<'d, D> { + async fn write(&mut self, buf: &[u8]) -> Result { + let len = core::cmp::min(buf.len(), self.max_packet_size() as usize); + self.write_packet(&buf[..len]).await?; + Ok(len) + } +} + /// CDC ACM class packet receiver. /// /// You can obtain a `Receiver` with [`CdcAcmClass::split`] @@ -451,6 +463,93 @@ impl<'d, D: Driver<'d>> Receiver<'d, D> { pub async fn wait_connection(&mut self) { self.read_ep.wait_enabled().await; } + + /// Turn the `Receiver` into a [`BufferedReceiver`]. + /// + /// The supplied buffer must be large enough to hold max_packet_size bytes. + pub fn into_buffered(self, buf: &'d mut [u8]) -> BufferedReceiver<'d, D> { + BufferedReceiver { + receiver: self, + buffer: buf, + start: 0, + end: 0, + } + } +} + +/// CDC ACM class buffered receiver. +/// +/// It is a requirement of the [`embedded_io_async::Read`] trait that arbitrarily small lengths of +/// data can be read from the stream. The [`Receiver`] can only read full packets at a time. The +/// `BufferedReceiver` instead buffers a single packet if the caller does not read all of the data, +/// so that the remaining data can be returned in subsequent calls. +/// +/// If you have no requirement to use the [`embedded_io_async::Read`] trait or to read a data length +/// less than the packet length, then it is more efficient to use the [`Receiver`] directly. +/// +/// You can obtain a `BufferedReceiver` with [`Receiver::into_buffered`]. +/// +/// [`embedded_io_async::Read`]: https://docs.rs/embedded-io-async/latest/embedded_io_async/trait.Read.html +pub struct BufferedReceiver<'d, D: Driver<'d>> { + receiver: Receiver<'d, D>, + buffer: &'d mut [u8], + start: usize, + end: usize, +} + +impl<'d, D: Driver<'d>> BufferedReceiver<'d, D> { + fn read_from_buffer(&mut self, buf: &mut [u8]) -> usize { + let available = &self.buffer[self.start..self.end]; + let len = core::cmp::min(available.len(), buf.len()); + buf[..len].copy_from_slice(&self.buffer[..len]); + self.start += len; + len + } + + /// Gets the current line coding. The line coding contains information that's mainly relevant + /// for USB to UART serial port emulators, and can be ignored if not relevant. + pub fn line_coding(&self) -> LineCoding { + self.receiver.line_coding() + } + + /// Gets the DTR (data terminal ready) state + pub fn dtr(&self) -> bool { + self.receiver.dtr() + } + + /// Gets the RTS (request to send) state + pub fn rts(&self) -> bool { + self.receiver.rts() + } + + /// Waits for the USB host to enable this interface + pub async fn wait_connection(&mut self) { + self.receiver.wait_connection().await; + } +} + +impl<'d, D: Driver<'d>> embedded_io_async::ErrorType for BufferedReceiver<'d, D> { + type Error = EndpointError; +} + +impl<'d, D: Driver<'d>> embedded_io_async::Read for BufferedReceiver<'d, D> { + async fn read(&mut self, buf: &mut [u8]) -> Result { + // If there is a buffered packet, return data from that first + if self.start != self.end { + return Ok(self.read_from_buffer(buf)); + } + + // If the caller's buffer is large enough to contain an entire packet, read directly into + // that instead of buffering the packet internally. + if buf.len() > self.receiver.max_packet_size() as usize { + return self.receiver.read_packet(buf).await; + } + + // Otherwise read a packet into the internal buffer, and return some of it to the caller + self.start = 0; + self.end = self.receiver.read_packet(&mut self.buffer).await?; + return Ok(self.read_from_buffer(buf)); + } } /// Number of stop bits for LineCoding From 42c62ba8999df08ad34d566f30f0a7199dbae083 Mon Sep 17 00:00:00 2001 From: Felipe Balbi Date: Wed, 7 May 2025 10:40:43 -0700 Subject: [PATCH 80/97] Add OS Event timer support Allow for the use of the OS Event timer as a time source. Signed-off-by: Felipe Balbi --- embassy-imxrt/Cargo.toml | 9 +- embassy-imxrt/src/lib.rs | 4 +- embassy-imxrt/src/time_driver.rs | 206 +++++++++++++++++++++++++++---- examples/mimxrt6/Cargo.toml | 4 +- 4 files changed, 189 insertions(+), 34 deletions(-) diff --git a/embassy-imxrt/Cargo.toml b/embassy-imxrt/Cargo.toml index d58de6353..f16002a8d 100644 --- a/embassy-imxrt/Cargo.toml +++ b/embassy-imxrt/Cargo.toml @@ -12,7 +12,7 @@ documentation = "https://docs.embassy.dev/embassy-imxrt" [package.metadata.embassy_docs] src_base = "https://github.com/embassy-rs/embassy/blob/embassy-imxrt-v$VERSION/embassy-imxrt/src/" src_base_git = "https://github.com/embassy-rs/embassy/blob/$COMMIT/embassy-imxrt/src/" -features = ["defmt", "unstable-pac", "time", "time-driver"] +features = ["defmt", "unstable-pac", "time", "time-driver-os-timer"] flavors = [ { regex_feature = "mimxrt6.*", target = "thumbv8m.main-none-eabihf" } ] @@ -37,9 +37,12 @@ defmt = ["dep:defmt", "embassy-hal-internal/defmt", "embassy-sync/defmt", "mimxr time = ["dep:embassy-time", "embassy-embedded-hal/time"] ## Enable custom embassy time-driver implementation, using 32KHz RTC -time-driver-rtc = ["_time-driver"] +time-driver-rtc = ["_time-driver", "embassy-time-driver?/tick-hz-1_000"] -_time-driver = ["dep:embassy-time-driver", "embassy-time-driver?/tick-hz-1_000", "dep:embassy-time-queue-utils", "embassy-embedded-hal/time"] +## Enable custom embassy time-driver implementation, using 1MHz OS Timer +time-driver-os-timer = ["_time-driver", "embassy-time-driver?/tick-hz-1_000_000"] + +_time-driver = ["dep:embassy-time-driver", "dep:embassy-time-queue-utils", "embassy-embedded-hal/time"] ## Reexport the PAC for the currently enabled chip at `embassy_imxrt::pac` (unstable) unstable-pac = [] diff --git a/embassy-imxrt/src/lib.rs b/embassy-imxrt/src/lib.rs index fdf07b433..ad9f81e88 100644 --- a/embassy-imxrt/src/lib.rs +++ b/embassy-imxrt/src/lib.rs @@ -132,12 +132,10 @@ pub fn init(config: config::Config) -> Peripherals { error!("unable to initialize Clocks for reason: {:?}", e); // Panic here? } - gpio::init(); } - - // init RTC time driver #[cfg(feature = "_time-driver")] time_driver::init(config.time_interrupt_priority); + gpio::init(); peripherals } diff --git a/embassy-imxrt/src/time_driver.rs b/embassy-imxrt/src/time_driver.rs index 56a8f7397..c68679d3e 100644 --- a/embassy-imxrt/src/time_driver.rs +++ b/embassy-imxrt/src/time_driver.rs @@ -1,5 +1,6 @@ -//! RTC Time Driver. +//! Time Driver. use core::cell::{Cell, RefCell}; +#[cfg(feature = "time-driver-rtc")] use core::sync::atomic::{compiler_fence, AtomicU32, Ordering}; use critical_section::CriticalSection; @@ -8,27 +9,11 @@ use embassy_sync::blocking_mutex::Mutex; use embassy_time_driver::Driver; use embassy_time_queue_utils::Queue; +#[cfg(feature = "time-driver-os-timer")] +use crate::clocks::enable; use crate::interrupt::InterruptExt; use crate::{interrupt, pac}; -fn rtc() -> &'static pac::rtc::RegisterBlock { - unsafe { &*pac::Rtc::ptr() } -} - -/// Calculate the timestamp from the period count and the tick count. -/// -/// To get `now()`, `period` is read first, then `counter` is read. If the counter value matches -/// the expected range for the `period` parity, we're done. If it doesn't, this means that -/// a new period start has raced us between reading `period` and `counter`, so we assume the `counter` value -/// corresponds to the next period. -/// -/// the 1kHz RTC counter is 16 bits and RTC doesn't have separate compare channels, -/// so using a 32 bit GPREG0-2 as counter, compare, and int_en -/// `period` is a 32bit integer, gpreg 'counter' is 31 bits plus the parity bit for overflow detection -fn calc_now(period: u32, counter: u32) -> u64 { - ((period as u64) << 31) + ((counter ^ ((period & 1) << 31)) as u64) -} - struct AlarmState { timestamp: Cell, } @@ -43,6 +28,34 @@ impl AlarmState { } } +#[cfg(feature = "time-driver-rtc")] +fn rtc() -> &'static pac::rtc::RegisterBlock { + unsafe { &*pac::Rtc::ptr() } +} + +/// Calculate the timestamp from the period count and the tick count. +/// +/// To get `now()`, `period` is read first, then `counter` is read. If the counter value matches +/// the expected range for the `period` parity, we're done. If it doesn't, this means that +/// a new period start has raced us between reading `period` and `counter`, so we assume the `counter` value +/// corresponds to the next period. +/// +/// the 1kHz RTC counter is 16 bits and RTC doesn't have separate compare channels, +/// so using a 32 bit GPREG0-2 as counter, compare, and int_en +/// `period` is a 32bit integer, gpreg 'counter' is 31 bits plus the parity bit for overflow detection +#[cfg(feature = "time-driver-rtc")] +fn calc_now(period: u32, counter: u32) -> u64 { + ((period as u64) << 31) + ((counter ^ ((period & 1) << 31)) as u64) +} + +#[cfg(feature = "time-driver-rtc")] +embassy_time_driver::time_driver_impl!(static DRIVER: Rtc = Rtc { + period: AtomicU32::new(0), + alarms: Mutex::const_new(CriticalSectionRawMutex::new(), AlarmState::new()), + queue: Mutex::new(RefCell::new(Queue::new())), +}); + +#[cfg(feature = "time-driver-rtc")] struct Rtc { /// Number of 2^31 periods elapsed since boot. period: AtomicU32, @@ -51,12 +64,7 @@ struct Rtc { queue: Mutex>, } -embassy_time_driver::time_driver_impl!(static DRIVER: Rtc = Rtc { - period: AtomicU32::new(0), - alarms: Mutex::const_new(CriticalSectionRawMutex::new(), AlarmState::new()), - queue: Mutex::new(RefCell::new(Queue::new())), -}); - +#[cfg(feature = "time-driver-rtc")] impl Rtc { /// Access the GPREG0 register to use it as a 31-bit counter. #[inline] @@ -219,6 +227,7 @@ impl Rtc { } } +#[cfg(feature = "time-driver-rtc")] impl Driver for Rtc { fn now(&self) -> u64 { // `period` MUST be read before `counter`, see comment at the top for details. @@ -242,13 +251,158 @@ impl Driver for Rtc { } } -#[cfg(feature = "rt")] +#[cfg(all(feature = "rt", feature = "time-driver-rtc"))] #[allow(non_snake_case)] #[interrupt] fn RTC() { DRIVER.on_interrupt() } +#[cfg(feature = "time-driver-os-timer")] +fn os() -> &'static pac::ostimer0::RegisterBlock { + unsafe { &*pac::Ostimer0::ptr() } +} + +/// Convert gray to decimal +/// +/// Os Event provides a 64-bit timestamp gray-encoded. All we have to +/// do here is read both 32-bit halves of the register and convert +/// from gray to regular binary. +#[cfg(feature = "time-driver-os-timer")] +fn gray_to_dec(gray: u64) -> u64 { + let mut dec = gray; + + dec ^= dec >> 1; + dec ^= dec >> 2; + dec ^= dec >> 4; + dec ^= dec >> 8; + dec ^= dec >> 16; + dec ^= dec >> 32; + + dec +} + +/// Convert decimal to gray +/// +/// Before writing match value to the target register, we must convert +/// it back into gray code. +#[cfg(feature = "time-driver-os-timer")] +fn dec_to_gray(dec: u64) -> u64 { + let gray = dec; + gray ^ (gray >> 1) +} + +#[cfg(feature = "time-driver-os-timer")] +embassy_time_driver::time_driver_impl!(static DRIVER: OsTimer = OsTimer { + alarms: Mutex::const_new(CriticalSectionRawMutex::new(), AlarmState::new()), + queue: Mutex::new(RefCell::new(Queue::new())), +}); + +#[cfg(feature = "time-driver-os-timer")] +struct OsTimer { + /// Timestamp at which to fire alarm. u64::MAX if no alarm is scheduled. + alarms: Mutex, + queue: Mutex>, +} + +#[cfg(feature = "time-driver-os-timer")] +impl OsTimer { + fn init(&'static self, irq_prio: crate::interrupt::Priority) { + // init alarms + critical_section::with(|cs| { + let alarm = DRIVER.alarms.borrow(cs); + alarm.timestamp.set(u64::MAX); + }); + + // Enable clocks. Documentation advises AGAINST resetting this + // peripheral. + enable::(); + + interrupt::OS_EVENT.disable(); + + // Make sure interrupt is masked + os().osevent_ctrl().modify(|_, w| w.ostimer_intena().clear_bit()); + + // Default to the end of time + os().match_l().write(|w| unsafe { w.bits(0xffff_ffff) }); + os().match_h().write(|w| unsafe { w.bits(0xffff_ffff) }); + + interrupt::OS_EVENT.unpend(); + interrupt::OS_EVENT.set_priority(irq_prio); + unsafe { interrupt::OS_EVENT.enable() }; + } + + fn set_alarm(&self, cs: CriticalSection, timestamp: u64) -> bool { + let alarm = self.alarms.borrow(cs); + alarm.timestamp.set(timestamp); + + let t = self.now(); + if timestamp <= t { + os().osevent_ctrl().modify(|_, w| w.ostimer_intena().clear_bit()); + alarm.timestamp.set(u64::MAX); + return false; + } + + let gray_timestamp = dec_to_gray(timestamp); + + os().match_l() + .write(|w| unsafe { w.bits(gray_timestamp as u32 & 0xffff_ffff) }); + os().match_h() + .write(|w| unsafe { w.bits((gray_timestamp >> 32) as u32) }); + os().osevent_ctrl().modify(|_, w| w.ostimer_intena().set_bit()); + + true + } + + #[cfg(feature = "rt")] + fn trigger_alarm(&self, cs: CriticalSection) { + let mut next = self.queue.borrow(cs).borrow_mut().next_expiration(self.now()); + while !self.set_alarm(cs, next) { + next = self.queue.borrow(cs).borrow_mut().next_expiration(self.now()); + } + } + + #[cfg(feature = "rt")] + fn on_interrupt(&self) { + critical_section::with(|cs| { + if os().osevent_ctrl().read().ostimer_intrflag().bit_is_set() { + os().osevent_ctrl().modify(|_, w| w.ostimer_intena().clear_bit()); + self.trigger_alarm(cs); + } + }); + } +} + +#[cfg(feature = "time-driver-os-timer")] +impl Driver for OsTimer { + fn now(&self) -> u64 { + let mut t = os().evtimerh().read().bits() as u64; + t <<= 32; + t |= os().evtimerl().read().bits() as u64; + gray_to_dec(t) + } + + fn schedule_wake(&self, at: u64, waker: &core::task::Waker) { + critical_section::with(|cs| { + let mut queue = self.queue.borrow(cs).borrow_mut(); + + if queue.schedule_wake(at, waker) { + let mut next = queue.next_expiration(self.now()); + while !self.set_alarm(cs, next) { + next = queue.next_expiration(self.now()); + } + } + }) + } +} + +#[cfg(all(feature = "rt", feature = "time-driver-os-timer"))] +#[allow(non_snake_case)] +#[interrupt] +fn OS_EVENT() { + DRIVER.on_interrupt() +} + pub(crate) fn init(irq_prio: crate::interrupt::Priority) { DRIVER.init(irq_prio) } diff --git a/examples/mimxrt6/Cargo.toml b/examples/mimxrt6/Cargo.toml index 8fc510c47..b0c56f003 100644 --- a/examples/mimxrt6/Cargo.toml +++ b/examples/mimxrt6/Cargo.toml @@ -12,8 +12,8 @@ defmt-rtt = "1.0" embassy-executor = { version = "0.7.0", path = "../../embassy-executor", features = ["arch-cortex-m", "executor-thread", "executor-interrupt", "defmt"] } embassy-futures = { version = "0.1.1", path = "../../embassy-futures" } -embassy-imxrt = { version = "0.1.0", path = "../../embassy-imxrt", features = ["defmt", "mimxrt685s", "unstable-pac", "time", "time-driver-rtc"] } -embassy-time = { version = "0.4", path = "../../embassy-time" } +embassy-imxrt = { version = "0.1.0", path = "../../embassy-imxrt", features = ["defmt", "mimxrt685s", "unstable-pac", "time", "time-driver-os-timer"] } +embassy-time = { version = "0.4", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime"] } embassy-sync = { version = "0.6.2", path = "../../embassy-sync", features = ["defmt"] } embedded-hal-1 = { package = "embedded-hal", version = "1.0" } embedded-hal-async = "1.0.0" From d35df5cfbadb0142d4c8fd44b5dcbfa81ab7ac15 Mon Sep 17 00:00:00 2001 From: Matthew Tran <0e4ef622@gmail.com> Date: Thu, 8 May 2025 00:09:21 -0500 Subject: [PATCH 81/97] embassy-usb-dfu: Change return of reset to () Also adds &self to the Reset trait, which makes it easier to implement cleanup/delays before actually resetting. --- embassy-usb-dfu/src/application.rs | 10 ++++------ embassy-usb-dfu/src/dfu.rs | 14 ++++++-------- embassy-usb-dfu/src/lib.rs | 8 ++++---- examples/boot/application/stm32wb-dfu/src/main.rs | 4 ++-- examples/boot/bootloader/stm32wb-dfu/src/main.rs | 4 ++-- 5 files changed, 18 insertions(+), 22 deletions(-) diff --git a/embassy-usb-dfu/src/application.rs b/embassy-usb-dfu/src/application.rs index e93c241ad..3c1e8b2cc 100644 --- a/embassy-usb-dfu/src/application.rs +++ b/embassy-usb-dfu/src/application.rs @@ -1,5 +1,3 @@ -use core::marker::PhantomData; - use embassy_boot::BlockingFirmwareState; use embassy_time::{Duration, Instant}; use embassy_usb::control::{InResponse, OutResponse, Recipient, RequestType}; @@ -36,19 +34,19 @@ pub struct Control { state: State, timeout: Option, detach_start: Option, - _rst: PhantomData, + reset: RST, } impl Control { /// Create a new DFU instance to expose a DFU interface. - pub fn new(dfu_marker: MARK, attrs: DfuAttributes) -> Self { + pub fn new(dfu_marker: MARK, attrs: DfuAttributes, reset: RST) -> Self { Control { dfu_marker, attrs, state: State::AppIdle, detach_start: None, timeout: None, - _rst: PhantomData, + reset, } } } @@ -65,7 +63,7 @@ impl Handler for Control { ); if delta < timeout { self.dfu_marker.mark_dfu(); - RST::sys_reset() + self.reset.sys_reset() } } } diff --git a/embassy-usb-dfu/src/dfu.rs b/embassy-usb-dfu/src/dfu.rs index c23286cf5..a98d6ab40 100644 --- a/embassy-usb-dfu/src/dfu.rs +++ b/embassy-usb-dfu/src/dfu.rs @@ -1,5 +1,3 @@ -use core::marker::PhantomData; - use embassy_boot::{AlignedBuffer, BlockingFirmwareUpdater, FirmwareUpdaterError}; use embassy_usb::control::{InResponse, OutResponse, Recipient, RequestType}; use embassy_usb::driver::Driver; @@ -20,12 +18,12 @@ pub struct Control<'d, DFU: NorFlash, STATE: NorFlash, RST: Reset, const BLOCK_S status: Status, offset: usize, buf: AlignedBuffer, - _rst: PhantomData, + reset: RST, } impl<'d, DFU: NorFlash, STATE: NorFlash, RST: Reset, const BLOCK_SIZE: usize> Control<'d, DFU, STATE, RST, BLOCK_SIZE> { /// Create a new DFU instance to handle DFU transfers. - pub fn new(updater: BlockingFirmwareUpdater<'d, DFU, STATE>, attrs: DfuAttributes) -> Self { + pub fn new(updater: BlockingFirmwareUpdater<'d, DFU, STATE>, attrs: DfuAttributes, reset: RST) -> Self { Self { updater, attrs, @@ -33,7 +31,7 @@ impl<'d, DFU: NorFlash, STATE: NorFlash, RST: Reset, const BLOCK_SIZE: usize> Co status: Status::Ok, offset: 0, buf: AlignedBuffer([0; BLOCK_SIZE]), - _rst: PhantomData, + reset, } } @@ -155,14 +153,14 @@ impl<'d, DFU: NorFlash, STATE: NorFlash, RST: Reset, const BLOCK_SIZE: usize> Ha } match Request::try_from(req.request) { Ok(Request::GetStatus) => { - //TODO: Configurable poll timeout, ability to add string for Vendor error - buf[0..6].copy_from_slice(&[self.status as u8, 0x32, 0x00, 0x00, self.state as u8, 0x00]); match self.state { State::DlSync => self.state = State::Download, - State::ManifestSync => RST::sys_reset(), + State::ManifestSync => self.reset.sys_reset(), _ => {} } + //TODO: Configurable poll timeout, ability to add string for Vendor error + buf[0..6].copy_from_slice(&[self.status as u8, 0x32, 0x00, 0x00, self.state as u8, 0x00]); Some(InResponse::Accepted(&buf[0..6])) } Ok(Request::GetState) => { diff --git a/embassy-usb-dfu/src/lib.rs b/embassy-usb-dfu/src/lib.rs index eaa4b6e33..54ffa7276 100644 --- a/embassy-usb-dfu/src/lib.rs +++ b/embassy-usb-dfu/src/lib.rs @@ -26,10 +26,10 @@ compile_error!("usb-dfu must be compiled with exactly one of `dfu`, or `applicat /// This crate exposes `ResetImmediate` when compiled with cortex-m or esp32c3 support, which immediately issues a /// reset request without interfacing with any other peripherals. /// -/// If alternate behaviour is desired, a custom implementation of Reset can be provided as a type argument to the usb_dfu function. +/// If alternate behaviour is desired, a custom implementation of Reset can be provided as an argument to the usb_dfu function. pub trait Reset { /// Reset the device. - fn sys_reset() -> !; + fn sys_reset(&self); } /// Reset immediately. @@ -38,7 +38,7 @@ pub struct ResetImmediate; #[cfg(feature = "esp32c3-hal")] impl Reset for ResetImmediate { - fn sys_reset() -> ! { + fn sys_reset(&self) { esp32c3_hal::reset::software_reset(); loop {} } @@ -50,7 +50,7 @@ pub struct ResetImmediate; #[cfg(feature = "cortex-m")] impl Reset for ResetImmediate { - fn sys_reset() -> ! { + fn sys_reset(&self) { cortex_m::peripheral::SCB::sys_reset() } } diff --git a/examples/boot/application/stm32wb-dfu/src/main.rs b/examples/boot/application/stm32wb-dfu/src/main.rs index 0ab99ff90..dda2b795b 100644 --- a/examples/boot/application/stm32wb-dfu/src/main.rs +++ b/examples/boot/application/stm32wb-dfu/src/main.rs @@ -44,7 +44,7 @@ async fn main(_spawner: Spawner) { let mut config_descriptor = [0; 256]; let mut bos_descriptor = [0; 256]; let mut control_buf = [0; 64]; - let mut state = Control::new(firmware_state, DfuAttributes::CAN_DOWNLOAD); + let mut state = Control::new(firmware_state, DfuAttributes::CAN_DOWNLOAD, ResetImmediate); let mut builder = Builder::new( driver, config, @@ -54,7 +54,7 @@ async fn main(_spawner: Spawner) { &mut control_buf, ); - usb_dfu::<_, _, ResetImmediate>(&mut builder, &mut state, Duration::from_millis(2500)); + usb_dfu(&mut builder, &mut state, Duration::from_millis(2500)); let mut dev = builder.build(); dev.run().await diff --git a/examples/boot/bootloader/stm32wb-dfu/src/main.rs b/examples/boot/bootloader/stm32wb-dfu/src/main.rs index b09d53cf0..28216806e 100644 --- a/examples/boot/bootloader/stm32wb-dfu/src/main.rs +++ b/examples/boot/bootloader/stm32wb-dfu/src/main.rs @@ -55,7 +55,7 @@ fn main() -> ! { let mut config_descriptor = [0; 256]; let mut bos_descriptor = [0; 256]; let mut control_buf = [0; 4096]; - let mut state = Control::new(updater, DfuAttributes::CAN_DOWNLOAD); + let mut state = Control::new(updater, DfuAttributes::CAN_DOWNLOAD, ResetImmediate); let mut builder = Builder::new( driver, config, @@ -77,7 +77,7 @@ fn main() -> ! { msos::PropertyData::RegMultiSz(DEVICE_INTERFACE_GUIDS), )); - usb_dfu::<_, _, _, ResetImmediate, 4096>(&mut builder, &mut state); + usb_dfu::<_, _, _, _, 4096>(&mut builder, &mut state); let mut dev = builder.build(); embassy_futures::block_on(dev.run()); From 5c0a63a0ddbc8c8afc97dc2857c2d78093c48768 Mon Sep 17 00:00:00 2001 From: Matthew Tran <0e4ef622@gmail.com> Date: Thu, 8 May 2025 00:12:00 -0500 Subject: [PATCH 82/97] embassy-usb-dfu: Reset immediately if WILL_DETACH is set This is necessary to support the windows WinUSB driver which is not capable of generating the USB reset. --- embassy-usb-dfu/src/application.rs | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/embassy-usb-dfu/src/application.rs b/embassy-usb-dfu/src/application.rs index 3c1e8b2cc..6ad07a78c 100644 --- a/embassy-usb-dfu/src/application.rs +++ b/embassy-usb-dfu/src/application.rs @@ -81,10 +81,15 @@ impl Handler for Control { match Request::try_from(req.request) { Ok(Request::Detach) => { - trace!("Received DETACH, awaiting USB reset"); self.detach_start = Some(Instant::now()); self.timeout = Some(Duration::from_millis(req.value as u64)); self.state = State::AppDetach; + if self.attrs.contains(DfuAttributes::WILL_DETACH) { + trace!("Received DETACH, performing reset"); + self.reset(); + } else { + trace!("Received DETACH, awaiting USB reset"); + } Some(OutResponse::Accepted) } _ => None, From 65bd86f19f8b2338c4593f35ac8e44a677a2b801 Mon Sep 17 00:00:00 2001 From: Dion Dokter Date: Thu, 8 May 2025 13:40:21 +0200 Subject: [PATCH 83/97] Stm32: Fix opamp copy pasta mistake --- embassy-stm32/src/opamp.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/embassy-stm32/src/opamp.rs b/embassy-stm32/src/opamp.rs index b368df6c3..2eb2e61c1 100644 --- a/embassy-stm32/src/opamp.rs +++ b/embassy-stm32/src/opamp.rs @@ -239,7 +239,7 @@ impl<'d, T: Instance> OpAmp<'d, T> { T::regs().csr().modify(|w| { w.set_vp_sel(VpSel::from_bits(pin.channel())); - w.set_vm_sel(VmSel::OUTPUT); + w.set_vm_sel(VmSel::PGA); w.set_pga_gain(pga_gain); w.set_opaintoen(true); w.set_opampen(true); From d4c378e059443dbaaaece02a0f5148db62bd4484 Mon Sep 17 00:00:00 2001 From: Felipe Balbi Date: Fri, 11 Apr 2025 14:28:59 -0700 Subject: [PATCH 84/97] Add embassy-imxrt CRC driver --- docs/pages/imxrt.adoc | 2 +- embassy-imxrt/src/crc.rs | 190 ++++++++++++++++++++++++++++++++ embassy-imxrt/src/lib.rs | 1 + examples/mimxrt6/src/bin/crc.rs | 175 +++++++++++++++++++++++++++++ 4 files changed, 367 insertions(+), 1 deletion(-) create mode 100644 embassy-imxrt/src/crc.rs create mode 100644 examples/mimxrt6/src/bin/crc.rs diff --git a/docs/pages/imxrt.adoc b/docs/pages/imxrt.adoc index adf5218e8..a84fbae03 100644 --- a/docs/pages/imxrt.adoc +++ b/docs/pages/imxrt.adoc @@ -9,5 +9,5 @@ The link: link:https://github.com/embassy-rs/embassy/tree/main/embassy-imxrt[Emb The following peripherals have a HAL implementation at present +* CRC * GPIO - diff --git a/embassy-imxrt/src/crc.rs b/embassy-imxrt/src/crc.rs new file mode 100644 index 000000000..24d6ba5bd --- /dev/null +++ b/embassy-imxrt/src/crc.rs @@ -0,0 +1,190 @@ +//! Cyclic Redundancy Check (CRC) + +use core::marker::PhantomData; + +use crate::clocks::{enable_and_reset, SysconPeripheral}; +pub use crate::pac::crc_engine::mode::CrcPolynomial as Polynomial; +use crate::{peripherals, Peri, PeripheralType}; + +/// CRC driver. +pub struct Crc<'d> { + info: Info, + _config: Config, + _lifetime: PhantomData<&'d ()>, +} + +/// CRC configuration +pub struct Config { + /// Polynomial to be used + pub polynomial: Polynomial, + + /// Reverse bit order of input? + pub reverse_in: bool, + + /// 1's complement input? + pub complement_in: bool, + + /// Reverse CRC bit order? + pub reverse_out: bool, + + /// 1's complement CRC? + pub complement_out: bool, + + /// CRC Seed + pub seed: u32, +} + +impl Config { + /// Create a new CRC config. + #[must_use] + pub fn new( + polynomial: Polynomial, + reverse_in: bool, + complement_in: bool, + reverse_out: bool, + complement_out: bool, + seed: u32, + ) -> Self { + Config { + polynomial, + reverse_in, + complement_in, + reverse_out, + complement_out, + seed, + } + } +} + +impl Default for Config { + fn default() -> Self { + Self { + polynomial: Polynomial::CrcCcitt, + reverse_in: false, + complement_in: false, + reverse_out: false, + complement_out: false, + seed: 0xffff, + } + } +} + +impl<'d> Crc<'d> { + /// Instantiates new CRC peripheral and initializes to default values. + pub fn new(_peripheral: Peri<'d, T>, config: Config) -> Self { + // enable CRC clock + enable_and_reset::(); + + let mut instance = Self { + info: T::info(), + _config: config, + _lifetime: PhantomData, + }; + + instance.reconfigure(); + instance + } + + /// Reconfigured the CRC peripheral. + fn reconfigure(&mut self) { + self.info.regs.mode().write(|w| { + w.crc_poly() + .variant(self._config.polynomial) + .bit_rvs_wr() + .variant(self._config.reverse_in) + .cmpl_wr() + .variant(self._config.complement_in) + .bit_rvs_sum() + .variant(self._config.reverse_out) + .cmpl_sum() + .variant(self._config.complement_out) + }); + + // Init CRC value + self.info + .regs + .seed() + .write(|w| unsafe { w.crc_seed().bits(self._config.seed) }); + } + + /// Feeds a byte into the CRC peripheral. Returns the computed checksum. + pub fn feed_byte(&mut self, byte: u8) -> u32 { + self.info.regs.wr_data8().write(|w| unsafe { w.bits(byte) }); + + self.info.regs.sum().read().bits() + } + + /// Feeds an slice of bytes into the CRC peripheral. Returns the computed checksum. + pub fn feed_bytes(&mut self, bytes: &[u8]) -> u32 { + let (prefix, data, suffix) = unsafe { bytes.align_to::() }; + + for b in prefix { + self.info.regs.wr_data8().write(|w| unsafe { w.bits(*b) }); + } + + for d in data { + self.info.regs.wr_data32().write(|w| unsafe { w.bits(*d) }); + } + + for b in suffix { + self.info.regs.wr_data8().write(|w| unsafe { w.bits(*b) }); + } + + self.info.regs.sum().read().bits() + } + + /// Feeds a halfword into the CRC peripheral. Returns the computed checksum. + pub fn feed_halfword(&mut self, halfword: u16) -> u32 { + self.info.regs.wr_data16().write(|w| unsafe { w.bits(halfword) }); + + self.info.regs.sum().read().bits() + } + + /// Feeds an slice of halfwords into the CRC peripheral. Returns the computed checksum. + pub fn feed_halfwords(&mut self, halfwords: &[u16]) -> u32 { + for halfword in halfwords { + self.info.regs.wr_data16().write(|w| unsafe { w.bits(*halfword) }); + } + + self.info.regs.sum().read().bits() + } + + /// Feeds a words into the CRC peripheral. Returns the computed checksum. + pub fn feed_word(&mut self, word: u32) -> u32 { + self.info.regs.wr_data32().write(|w| unsafe { w.bits(word) }); + + self.info.regs.sum().read().bits() + } + + /// Feeds an slice of words into the CRC peripheral. Returns the computed checksum. + pub fn feed_words(&mut self, words: &[u32]) -> u32 { + for word in words { + self.info.regs.wr_data32().write(|w| unsafe { w.bits(*word) }); + } + + self.info.regs.sum().read().bits() + } +} + +struct Info { + regs: crate::pac::CrcEngine, +} + +trait SealedInstance { + fn info() -> Info; +} + +/// CRC instance trait. +#[allow(private_bounds)] +pub trait Instance: SealedInstance + PeripheralType + SysconPeripheral + 'static + Send {} + +impl Instance for peripherals::CRC {} + +impl SealedInstance for peripherals::CRC { + fn info() -> Info { + // SAFETY: safe from single executor + Info { + regs: unsafe { crate::pac::CrcEngine::steal() }, + } + } +} diff --git a/embassy-imxrt/src/lib.rs b/embassy-imxrt/src/lib.rs index ad9f81e88..d8ce97f76 100644 --- a/embassy-imxrt/src/lib.rs +++ b/embassy-imxrt/src/lib.rs @@ -18,6 +18,7 @@ compile_error!( pub(crate) mod fmt; pub mod clocks; +pub mod crc; pub mod gpio; pub mod iopctl; diff --git a/examples/mimxrt6/src/bin/crc.rs b/examples/mimxrt6/src/bin/crc.rs new file mode 100644 index 000000000..005a250e5 --- /dev/null +++ b/examples/mimxrt6/src/bin/crc.rs @@ -0,0 +1,175 @@ +#![no_std] +#![no_main] + +extern crate embassy_imxrt_examples; + +use defmt::*; +use embassy_executor::Spawner; +use embassy_imxrt::crc::{Config, Crc, Polynomial}; +use {defmt_rtt as _, panic_probe as _}; + +#[embassy_executor::main] +async fn main(_spawner: Spawner) { + let mut p = embassy_imxrt::init(Default::default()); + let data = b"123456789"; + + info!("Initializing CRC"); + + // CRC-CCITT + let mut crc = Crc::new(p.CRC.reborrow(), Default::default()); + let output = crc.feed_bytes(data); + defmt::assert_eq!(output, 0x29b1); + + // CRC16-ARC + let mut crc = Crc::new( + p.CRC.reborrow(), + Config { + polynomial: Polynomial::Crc16, + reverse_in: true, + reverse_out: true, + complement_out: false, + seed: 0, + ..Default::default() + }, + ); + let output = crc.feed_bytes(data); + defmt::assert_eq!(output, 0xbb3d); + + // CRC16-CMS + let mut crc = Crc::new( + p.CRC.reborrow(), + Config { + polynomial: Polynomial::Crc16, + reverse_in: false, + reverse_out: false, + complement_out: false, + seed: 0xffff, + ..Default::default() + }, + ); + let output = crc.feed_bytes(data); + defmt::assert_eq!(output, 0xaee7); + + // CRC16-DDS-110 + let mut crc = Crc::new( + p.CRC.reborrow(), + Config { + polynomial: Polynomial::Crc16, + reverse_in: false, + reverse_out: false, + complement_out: false, + seed: 0x800d, + ..Default::default() + }, + ); + let output = crc.feed_bytes(data); + defmt::assert_eq!(output, 0x9ecf); + + // CRC16-MAXIM-DOW + let mut crc = Crc::new( + p.CRC.reborrow(), + Config { + polynomial: Polynomial::Crc16, + reverse_in: true, + reverse_out: true, + complement_out: true, + seed: 0, + ..Default::default() + }, + ); + let output = crc.feed_bytes(data); + defmt::assert_eq!(output, 0x44c2); + + // CRC16-MODBUS + let mut crc = Crc::new( + p.CRC.reborrow(), + Config { + polynomial: Polynomial::Crc16, + reverse_in: true, + reverse_out: true, + complement_out: false, + seed: 0xffff, + ..Default::default() + }, + ); + let output = crc.feed_bytes(data); + defmt::assert_eq!(output, 0x4b37); + + // CRC32-BZIP2 + let mut crc = Crc::new( + p.CRC.reborrow(), + Config { + polynomial: Polynomial::Crc32, + reverse_in: false, + reverse_out: false, + complement_out: true, + seed: 0xffff_ffff, + ..Default::default() + }, + ); + let output = crc.feed_bytes(data); + defmt::assert_eq!(output, 0xfc89_1918); + + // CRC32-CKSUM + let mut crc = Crc::new( + p.CRC.reborrow(), + Config { + polynomial: Polynomial::Crc32, + reverse_in: false, + reverse_out: false, + complement_out: true, + seed: 0, + ..Default::default() + }, + ); + let output = crc.feed_bytes(data); + defmt::assert_eq!(output, 0x765e_7680); + + // CRC32-ISO-HDLC + let mut crc = Crc::new( + p.CRC.reborrow(), + Config { + polynomial: Polynomial::Crc32, + reverse_in: true, + reverse_out: true, + complement_out: true, + seed: 0xffff_ffff, + ..Default::default() + }, + ); + let output = crc.feed_bytes(data); + defmt::assert_eq!(output, 0xcbf4_3926); + + // CRC32-JAMCRC + let mut crc = Crc::new( + p.CRC.reborrow(), + Config { + polynomial: Polynomial::Crc32, + reverse_in: true, + reverse_out: true, + complement_out: false, + seed: 0xffff_ffff, + ..Default::default() + }, + ); + let output = crc.feed_bytes(data); + defmt::assert_eq!(output, 0x340b_c6d9); + + // CRC32-MPEG-2 + let mut crc = Crc::new( + p.CRC.reborrow(), + Config { + polynomial: Polynomial::Crc32, + reverse_in: false, + reverse_out: false, + complement_out: false, + seed: 0xffff_ffff, + ..Default::default() + }, + ); + let output = crc.feed_bytes(data); + defmt::assert_eq!(output, 0x0376_e6e7); + + info!("end program"); + cortex_m::asm::bkpt(); +} From 8e7e4332b40707e8d36338ad8ec486320bb3538f Mon Sep 17 00:00:00 2001 From: Felipe Balbi Date: Fri, 11 Apr 2025 15:03:53 -0700 Subject: [PATCH 85/97] Add embassy-imxrt RNG driver --- docs/pages/imxrt.adoc | 2 +- embassy-imxrt/src/lib.rs | 1 + embassy-imxrt/src/rng.rs | 257 ++++++++++++++++++++++++++++++++ examples/mimxrt6/src/bin/rng.rs | 40 +++++ 4 files changed, 299 insertions(+), 1 deletion(-) create mode 100644 embassy-imxrt/src/rng.rs create mode 100644 examples/mimxrt6/src/bin/rng.rs diff --git a/docs/pages/imxrt.adoc b/docs/pages/imxrt.adoc index adf5218e8..9fcb6725c 100644 --- a/docs/pages/imxrt.adoc +++ b/docs/pages/imxrt.adoc @@ -10,4 +10,4 @@ The link: link:https://github.com/embassy-rs/embassy/tree/main/embassy-imxrt[Emb The following peripherals have a HAL implementation at present * GPIO - +* RNG diff --git a/embassy-imxrt/src/lib.rs b/embassy-imxrt/src/lib.rs index ad9f81e88..f728ba060 100644 --- a/embassy-imxrt/src/lib.rs +++ b/embassy-imxrt/src/lib.rs @@ -20,6 +20,7 @@ pub(crate) mod fmt; pub mod clocks; pub mod gpio; pub mod iopctl; +pub mod rng; #[cfg(feature = "_time-driver")] pub mod time_driver; diff --git a/embassy-imxrt/src/rng.rs b/embassy-imxrt/src/rng.rs new file mode 100644 index 000000000..67e7ab65d --- /dev/null +++ b/embassy-imxrt/src/rng.rs @@ -0,0 +1,257 @@ +//! True Random Number Generator (TRNG) + +use core::future::poll_fn; +use core::marker::PhantomData; +use core::task::Poll; + +use embassy_futures::block_on; +use embassy_sync::waitqueue::AtomicWaker; +use rand_core::{CryptoRng, RngCore}; + +use crate::clocks::{enable_and_reset, SysconPeripheral}; +use crate::interrupt::typelevel::Interrupt; +use crate::{interrupt, peripherals, Peri, PeripheralType}; + +static RNG_WAKER: AtomicWaker = AtomicWaker::new(); + +/// RNG ;error +#[derive(Debug, PartialEq, Eq, Clone, Copy)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] +pub enum Error { + /// Seed error. + SeedError, + + /// HW Error. + HwError, + + /// Frequency Count Fail + FreqCountFail, +} + +/// RNG interrupt handler. +pub struct InterruptHandler { + _phantom: PhantomData, +} + +impl interrupt::typelevel::Handler for InterruptHandler { + unsafe fn on_interrupt() { + let regs = T::info().regs; + let int_status = regs.int_status().read(); + + if int_status.ent_val().bit_is_set() + || int_status.hw_err().bit_is_set() + || int_status.frq_ct_fail().bit_is_set() + { + regs.int_ctrl().modify(|_, w| { + w.ent_val() + .ent_val_0() + .hw_err() + .hw_err_0() + .frq_ct_fail() + .frq_ct_fail_0() + }); + RNG_WAKER.wake(); + } + } +} + +/// RNG driver. +pub struct Rng<'d> { + info: Info, + _lifetime: PhantomData<&'d ()>, +} + +impl<'d> Rng<'d> { + /// Create a new RNG driver. + pub fn new( + _inner: Peri<'d, T>, + _irq: impl interrupt::typelevel::Binding> + 'd, + ) -> Self { + enable_and_reset::(); + + let mut random = Self { + info: T::info(), + _lifetime: PhantomData, + }; + random.init(); + + T::Interrupt::unpend(); + unsafe { T::Interrupt::enable() }; + + random + } + + /// Reset the RNG. + pub fn reset(&mut self) { + self.info.regs.mctl().write(|w| w.rst_def().set_bit().prgm().set_bit()); + } + + /// Fill the given slice with random values. + pub async fn async_fill_bytes(&mut self, dest: &mut [u8]) -> Result<(), Error> { + // We have a total of 16 words (512 bits) of entropy at our + // disposal. The idea here is to read all bits and copy the + // necessary bytes to the slice. + for chunk in dest.chunks_mut(64) { + self.async_fill_chunk(chunk).await?; + } + + Ok(()) + } + + async fn async_fill_chunk(&mut self, chunk: &mut [u8]) -> Result<(), Error> { + // wait for interrupt + let res = poll_fn(|cx| { + // Check if already ready. + // TODO: Is this necessary? Could we just check once after + // the waker has been registered? + if self.info.regs.int_status().read().ent_val().bit_is_set() { + return Poll::Ready(Ok(())); + } + + RNG_WAKER.register(cx.waker()); + + self.unmask_interrupts(); + + let mctl = self.info.regs.mctl().read(); + + // Check again if interrupt fired + if mctl.ent_val().bit_is_set() { + Poll::Ready(Ok(())) + } else if mctl.err().bit_is_set() { + Poll::Ready(Err(Error::HwError)) + } else if mctl.fct_fail().bit_is_set() { + Poll::Ready(Err(Error::FreqCountFail)) + } else { + Poll::Pending + } + }) + .await; + + let bits = self.info.regs.mctl().read(); + + if bits.ent_val().bit_is_set() { + let mut entropy = [0; 16]; + + for (i, item) in entropy.iter_mut().enumerate() { + *item = self.info.regs.ent(i).read().bits(); + } + + // Read MCTL after reading ENT15 + let _ = self.info.regs.mctl().read(); + + if entropy.iter().any(|e| *e == 0) { + return Err(Error::SeedError); + } + + // SAFETY: entropy is the same for input and output types in + // native endianness. + let entropy: [u8; 64] = unsafe { core::mem::transmute(entropy) }; + + // write bytes to chunk + chunk.copy_from_slice(&entropy[..chunk.len()]); + } + + res + } + + fn mask_interrupts(&mut self) { + self.info.regs.int_mask().write(|w| { + w.ent_val() + .ent_val_0() + .hw_err() + .hw_err_0() + .frq_ct_fail() + .frq_ct_fail_0() + }); + } + + fn unmask_interrupts(&mut self) { + self.info.regs.int_mask().modify(|_, w| { + w.ent_val() + .ent_val_1() + .hw_err() + .hw_err_1() + .frq_ct_fail() + .frq_ct_fail_1() + }); + } + + fn enable_interrupts(&mut self) { + self.info.regs.int_ctrl().write(|w| { + w.ent_val() + .ent_val_1() + .hw_err() + .hw_err_1() + .frq_ct_fail() + .frq_ct_fail_1() + }); + } + + fn init(&mut self) { + self.mask_interrupts(); + + // Switch TRNG to programming mode + self.info.regs.mctl().modify(|_, w| w.prgm().set_bit()); + + self.enable_interrupts(); + + // Switch TRNG to Run Mode + self.info + .regs + .mctl() + .modify(|_, w| w.trng_acc().set_bit().prgm().clear_bit()); + } +} + +impl RngCore for Rng<'_> { + fn next_u32(&mut self) -> u32 { + let mut bytes = [0u8; 4]; + block_on(self.async_fill_bytes(&mut bytes)).unwrap(); + u32::from_ne_bytes(bytes) + } + + fn next_u64(&mut self) -> u64 { + let mut bytes = [0u8; 8]; + block_on(self.async_fill_bytes(&mut bytes)).unwrap(); + u64::from_ne_bytes(bytes) + } + + fn fill_bytes(&mut self, dest: &mut [u8]) { + block_on(self.async_fill_bytes(dest)).unwrap(); + } + + fn try_fill_bytes(&mut self, dest: &mut [u8]) -> Result<(), rand_core::Error> { + self.fill_bytes(dest); + Ok(()) + } +} + +impl CryptoRng for Rng<'_> {} + +struct Info { + regs: crate::pac::Trng, +} + +trait SealedInstance { + fn info() -> Info; +} + +/// RNG instance trait. +#[allow(private_bounds)] +pub trait Instance: SealedInstance + PeripheralType + SysconPeripheral + 'static + Send { + /// Interrupt for this RNG instance. + type Interrupt: interrupt::typelevel::Interrupt; +} + +impl Instance for peripherals::RNG { + type Interrupt = crate::interrupt::typelevel::RNG; +} + +impl SealedInstance for peripherals::RNG { + fn info() -> Info { + // SAFETY: safe from single executor + Info { + regs: unsafe { crate::pac::Trng::steal() }, + } + } +} diff --git a/examples/mimxrt6/src/bin/rng.rs b/examples/mimxrt6/src/bin/rng.rs new file mode 100644 index 000000000..5f64cb96a --- /dev/null +++ b/examples/mimxrt6/src/bin/rng.rs @@ -0,0 +1,40 @@ +#![no_std] +#![no_main] + +extern crate embassy_imxrt_examples; + +use defmt::*; +use embassy_executor::Spawner; +use embassy_imxrt::rng::Rng; +use embassy_imxrt::{bind_interrupts, peripherals, rng}; +use rand::RngCore; +use {defmt_rtt as _, panic_probe as _}; + +bind_interrupts!(struct Irqs { + RNG => rng::InterruptHandler; +}); + +#[embassy_executor::main] +async fn main(_spawner: Spawner) { + let p = embassy_imxrt::init(Default::default()); + + info!("Initializing RNG"); + let mut rng = Rng::new(p.RNG, Irqs); + let mut buf = [0u8; 65]; + + // Async interface + unwrap!(rng.async_fill_bytes(&mut buf).await); + info!("random bytes: {:02x}", buf); + + // RngCore interface + let mut random_bytes = [0; 16]; + + let random_u32 = rng.next_u32(); + let random_u64 = rng.next_u64(); + + rng.fill_bytes(&mut random_bytes); + + info!("random_u32 {}", random_u32); + info!("random_u64 {}", random_u64); + info!("random_bytes {}", random_bytes); +} From ee71dda6317ef8de66b09612a020416466aeb2d3 Mon Sep 17 00:00:00 2001 From: 9names <60134748+9names@users.noreply.github.com> Date: Sat, 10 May 2025 19:58:03 +1000 Subject: [PATCH 86/97] Clarify embassy-rp rt feature purpose --- embassy-rp/Cargo.toml | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/embassy-rp/Cargo.toml b/embassy-rp/Cargo.toml index b440591cf..8fb8a50fd 100644 --- a/embassy-rp/Cargo.toml +++ b/embassy-rp/Cargo.toml @@ -26,7 +26,10 @@ features = ["defmt", "unstable-pac", "time-driver", "rp2040"] [features] default = [ "rt" ] -## Enable the rt feature of [`rp-pac`](https://docs.rs/rp-pac). This brings in the [`cortex-m-rt`](https://docs.rs/cortex-m-rt) crate, which adds startup code and minimal runtime initialization. + +## Enable the `rt` feature of [`rp-pac`](https://docs.rs/rp-pac). +## With `rt` enabled the PAC provides interrupt vectors instead of letting [`cortex-m-rt`](https://docs.rs/cortex-m-rt) do that. +## See for more info. rt = [ "rp-pac/rt" ] ## Enable [defmt support](https://docs.rs/defmt) and enables `defmt` debug-log messages and formatting in embassy drivers. From 967a98fd4448a8e00c8cbfb509c1cc32e2bdcd29 Mon Sep 17 00:00:00 2001 From: Alessandro Gasbarroni Date: Tue, 17 Dec 2024 13:25:58 +0100 Subject: [PATCH 87/97] nrf: Add IPC peripheral for nRF5340 --- embassy-nrf/src/chips/nrf5340_app.rs | 5 + embassy-nrf/src/chips/nrf5340_net.rs | 5 + embassy-nrf/src/ipc.rs | 360 +++++++++++++++++++++++++++ embassy-nrf/src/lib.rs | 2 + 4 files changed, 372 insertions(+) create mode 100644 embassy-nrf/src/ipc.rs diff --git a/embassy-nrf/src/chips/nrf5340_app.rs b/embassy-nrf/src/chips/nrf5340_app.rs index 0103fa7ae..99cf29487 100644 --- a/embassy-nrf/src/chips/nrf5340_app.rs +++ b/embassy-nrf/src/chips/nrf5340_app.rs @@ -262,6 +262,9 @@ embassy_hal_internal::peripherals! { PPI_GROUP4, PPI_GROUP5, + // IPC + IPC, + // GPIO port 0 #[cfg(feature = "lfxo-pins-as-gpio")] P0_00, @@ -327,6 +330,8 @@ embassy_hal_internal::peripherals! { EGU5, } +impl_ipc!(IPC, IPC, IPC); + impl_usb!(USBD, USBD, USBD); impl_uarte!(SERIAL0, UARTE0, SERIAL0); diff --git a/embassy-nrf/src/chips/nrf5340_net.rs b/embassy-nrf/src/chips/nrf5340_net.rs index 22d33d080..c2932be31 100644 --- a/embassy-nrf/src/chips/nrf5340_net.rs +++ b/embassy-nrf/src/chips/nrf5340_net.rs @@ -141,6 +141,9 @@ embassy_hal_internal::peripherals! { PPI_GROUP4, PPI_GROUP5, + // IPC + IPC, + // GPIO port 0 P0_00, P0_01, @@ -200,6 +203,8 @@ embassy_hal_internal::peripherals! { EGU0, } +impl_ipc!(IPC, IPC, IPC); + impl_uarte!(SERIAL0, UARTE0, SERIAL0); impl_spim!(SERIAL0, SPIM0, SERIAL0); impl_spis!(SERIAL0, SPIS0, SERIAL0); diff --git a/embassy-nrf/src/ipc.rs b/embassy-nrf/src/ipc.rs new file mode 100644 index 000000000..410783ef4 --- /dev/null +++ b/embassy-nrf/src/ipc.rs @@ -0,0 +1,360 @@ +//! InterProcessor Communication (IPC) + +#![macro_use] + +use core::future::poll_fn; +use core::sync::atomic::{compiler_fence, Ordering}; +use core::task::Poll; + +use embassy_hal_internal::{into_ref, Peripheral, PeripheralRef}; +use embassy_sync::waitqueue::AtomicWaker; + +use crate::peripherals::IPC; +use crate::{interrupt, pac}; + +/// IPC Event +#[derive(Debug, Clone, Copy)] +pub enum IpcEvent { + /// IPC Event 0 + Event0 = 0, + /// IPC Event 1 + Event1 = 1, + /// IPC Event 2 + Event2 = 2, + /// IPC Event 3 + Event3 = 3, + /// IPC Event 4 + Event4 = 4, + /// IPC Event 5 + Event5 = 5, + /// IPC Event 6 + Event6 = 6, + /// IPC Event 7 + Event7 = 7, + /// IPC Event 8 + Event8 = 8, + /// IPC Event 9 + Event9 = 9, + /// IPC Event 10 + Event10 = 10, + /// IPC Event 11 + Event11 = 11, + /// IPC Event 12 + Event12 = 12, + /// IPC Event 13 + Event13 = 13, + /// IPC Event 14 + Event14 = 14, + /// IPC Event 15 + Event15 = 15, +} + +const EVENTS: [IpcEvent; 16] = [ + IpcEvent::Event0, + IpcEvent::Event1, + IpcEvent::Event2, + IpcEvent::Event3, + IpcEvent::Event4, + IpcEvent::Event5, + IpcEvent::Event6, + IpcEvent::Event7, + IpcEvent::Event8, + IpcEvent::Event9, + IpcEvent::Event10, + IpcEvent::Event11, + IpcEvent::Event12, + IpcEvent::Event13, + IpcEvent::Event14, + IpcEvent::Event15, +]; + +/// IPC Channel +#[derive(Debug, Clone, Copy)] +pub enum IpcChannel { + /// IPC Channel 0 + Channel0, + /// IPC Channel 1 + Channel1, + /// IPC Channel 2 + Channel2, + /// IPC Channel 3 + Channel3, + /// IPC Channel 4 + Channel4, + /// IPC Channel 5 + Channel5, + /// IPC Channel 6 + Channel6, + /// IPC Channel 7 + Channel7, + /// IPC Channel 8 + Channel8, + /// IPC Channel 9 + Channel9, + /// IPC Channel 10 + Channel10, + /// IPC Channel 11 + Channel11, + /// IPC Channel 12 + Channel12, + /// IPC Channel 13 + Channel13, + /// IPC Channel 14 + Channel14, + /// IPC Channel 15 + Channel15, +} + +/// Interrupt Handler +pub struct InterruptHandler {} + +impl interrupt::typelevel::Handler for InterruptHandler { + unsafe fn on_interrupt() { + let regs = IPC::regs(); + + // Check if an event was generated, and if it was, trigger the corresponding waker + for event in EVENTS { + if regs.events_receive(event as usize).read() & 0x01 == 0x01 { + // Event is set. Reset and wake waker + regs.events_receive(event as usize).write_value(0); + IPC::state().waker_for(event); + } + + // Ensure the state is actually cleared + // Ref: nRF5340 PS v1.5 7.1.9.1 p.153 + compiler_fence(Ordering::SeqCst); + while regs.events_receive(event as usize).read() & 0x01 != 0x00 {} + } + } +} + +/// IPC driver +pub struct Ipc<'d, T: Instance> { + _peri: PeripheralRef<'d, T>, +} + +impl<'d, T: Instance> From> for Ipc<'d, T> { + fn from(value: PeripheralRef<'d, T>) -> Self { + Self { _peri: value } + } +} + +impl<'d, T: Instance> Ipc<'d, T> { + /// Create IPC driver + pub fn new(ipc: impl Peripheral

+ 'd) -> Self { + into_ref!(ipc); + + Self { _peri: ipc } + } + + /// Duplicates the peripheral singleton + /// + /// # Safety + /// + /// Ensure manually that only one peripheral is in use at one time + pub unsafe fn clone_unchecked(&self) -> Self { + Self { + _peri: self._peri.clone_unchecked(), + } + } + + /// Configures the sending of events + /// + /// Events can be configured to broadcast on one or multiple IPC channels. + pub fn configure_send_event>(&self, ev: IpcEvent, channels: I) { + let regs = T::regs(); + + regs.send_cnf(ev as usize).write(|w| { + for channel in channels { + match channel { + IpcChannel::Channel0 => w.set_chen0(true), + IpcChannel::Channel1 => w.set_chen1(true), + IpcChannel::Channel2 => w.set_chen2(true), + IpcChannel::Channel3 => w.set_chen3(true), + IpcChannel::Channel4 => w.set_chen4(true), + IpcChannel::Channel5 => w.set_chen5(true), + IpcChannel::Channel6 => w.set_chen6(true), + IpcChannel::Channel7 => w.set_chen7(true), + IpcChannel::Channel8 => w.set_chen8(true), + IpcChannel::Channel9 => w.set_chen9(true), + IpcChannel::Channel10 => w.set_chen10(true), + IpcChannel::Channel11 => w.set_chen11(true), + IpcChannel::Channel12 => w.set_chen12(true), + IpcChannel::Channel13 => w.set_chen13(true), + IpcChannel::Channel14 => w.set_chen14(true), + IpcChannel::Channel15 => w.set_chen15(true), + } + } + }) + } + + /// Configures the receiving of events + /// + /// Events can be configured to be received by one or multiple IPC channels. + pub fn configure_receive_event>(&self, ev: IpcEvent, channels: I) { + let regs = T::regs(); + + regs.receive_cnf(ev as usize).write(|w| { + for channel in channels { + match channel { + IpcChannel::Channel0 => w.set_chen0(true), + IpcChannel::Channel1 => w.set_chen1(true), + IpcChannel::Channel2 => w.set_chen2(true), + IpcChannel::Channel3 => w.set_chen3(true), + IpcChannel::Channel4 => w.set_chen4(true), + IpcChannel::Channel5 => w.set_chen5(true), + IpcChannel::Channel6 => w.set_chen6(true), + IpcChannel::Channel7 => w.set_chen7(true), + IpcChannel::Channel8 => w.set_chen8(true), + IpcChannel::Channel9 => w.set_chen9(true), + IpcChannel::Channel10 => w.set_chen10(true), + IpcChannel::Channel11 => w.set_chen11(true), + IpcChannel::Channel12 => w.set_chen12(true), + IpcChannel::Channel13 => w.set_chen13(true), + IpcChannel::Channel14 => w.set_chen14(true), + IpcChannel::Channel15 => w.set_chen15(true), + } + } + }); + } + + /// Triggers an event + pub fn trigger_event(&self, ev: IpcEvent) { + let regs = T::regs(); + + regs.tasks_send(ev as usize).write_value(0x01); + } + + /// Wait for event to be triggered + pub async fn wait_for_event(&self, ev: IpcEvent) { + let regs = T::regs(); + + // Enable interrupt + match ev { + IpcEvent::Event0 => { + regs.inten().modify(|m| m.set_receive0(true)); + } + IpcEvent::Event1 => { + regs.inten().modify(|m| m.set_receive1(true)); + } + IpcEvent::Event2 => { + regs.inten().modify(|m| m.set_receive2(true)); + } + IpcEvent::Event3 => { + regs.inten().modify(|m| m.set_receive3(true)); + } + IpcEvent::Event4 => { + regs.inten().modify(|m| m.set_receive4(true)); + } + IpcEvent::Event5 => { + regs.inten().modify(|m| m.set_receive5(true)); + } + IpcEvent::Event6 => { + regs.inten().modify(|m| m.set_receive6(true)); + } + IpcEvent::Event7 => { + regs.inten().modify(|m| m.set_receive7(true)); + } + IpcEvent::Event8 => { + regs.inten().modify(|m| m.set_receive8(true)); + } + IpcEvent::Event9 => { + regs.inten().modify(|m| m.set_receive9(true)); + } + IpcEvent::Event10 => { + regs.inten().modify(|m| m.set_receive10(true)); + } + IpcEvent::Event11 => { + regs.inten().modify(|m| m.set_receive11(true)); + } + IpcEvent::Event12 => { + regs.inten().modify(|m| m.set_receive12(true)); + } + IpcEvent::Event13 => { + regs.inten().modify(|m| m.set_receive13(true)); + } + IpcEvent::Event14 => { + regs.inten().modify(|m| m.set_receive14(true)); + } + IpcEvent::Event15 => { + regs.inten().modify(|m| m.set_receive15(true)); + } + }; + + poll_fn(|cx| { + IPC::state().waker_for(ev).register(cx.waker()); + + if regs.events_receive(ev as usize).read() & 0x01 == 0x01 { + regs.events_receive(ev as usize).write_value(0x00); + + Poll::Ready(()) + } else { + Poll::Pending + } + }) + .await; + } +} + +pub(crate) struct State { + wakers: [AtomicWaker; 16], +} + +impl State { + pub(crate) const fn new() -> Self { + const WAKER: AtomicWaker = AtomicWaker::new(); + + Self { wakers: [WAKER; 16] } + } + + const fn waker_for(&self, ev: IpcEvent) -> &AtomicWaker { + match ev { + IpcEvent::Event0 => &self.wakers[0], + IpcEvent::Event1 => &self.wakers[1], + IpcEvent::Event2 => &self.wakers[2], + IpcEvent::Event3 => &self.wakers[3], + IpcEvent::Event4 => &self.wakers[4], + IpcEvent::Event5 => &self.wakers[5], + IpcEvent::Event6 => &self.wakers[6], + IpcEvent::Event7 => &self.wakers[7], + IpcEvent::Event8 => &self.wakers[8], + IpcEvent::Event9 => &self.wakers[9], + IpcEvent::Event10 => &self.wakers[10], + IpcEvent::Event11 => &self.wakers[11], + IpcEvent::Event12 => &self.wakers[12], + IpcEvent::Event13 => &self.wakers[13], + IpcEvent::Event14 => &self.wakers[14], + IpcEvent::Event15 => &self.wakers[15], + } + } +} + +pub(crate) trait SealedInstance { + fn regs() -> pac::ipc::Ipc; + fn state() -> &'static State; +} + +/// IPC peripheral instance. +#[allow(private_bounds)] +pub trait Instance: Peripheral

+ SealedInstance + 'static + Send { + /// Interrupt for this peripheral. + type Interrupt: interrupt::typelevel::Interrupt; +} + +macro_rules! impl_ipc { + ($type:ident, $pac_type:ident, $irq:ident) => { + impl crate::ipc::SealedInstance for peripherals::$type { + fn regs() -> pac::ipc::Ipc { + pac::$pac_type + } + + fn state() -> &'static crate::ipc::State { + static STATE: crate::ipc::State = crate::ipc::State::new(); + &STATE + } + } + impl crate::ipc::Instance for peripherals::$type { + type Interrupt = crate::interrupt::typelevel::$irq; + } + }; +} diff --git a/embassy-nrf/src/lib.rs b/embassy-nrf/src/lib.rs index 07ba2f6d4..5bce65a98 100644 --- a/embassy-nrf/src/lib.rs +++ b/embassy-nrf/src/lib.rs @@ -88,6 +88,8 @@ pub mod gpiote; #[cfg(not(feature = "_nrf54l"))] // TODO #[cfg(any(feature = "nrf52832", feature = "nrf52833", feature = "nrf52840"))] pub mod i2s; +#[cfg(feature = "_nrf5340")] +pub mod ipc; #[cfg(not(feature = "_nrf54l"))] // TODO #[cfg(any( feature = "nrf52832", From 4567beda7b7773c8cb11f19f0f4f146c1243508d Mon Sep 17 00:00:00 2001 From: 1-rafael-1 Date: Sun, 11 May 2025 17:26:36 +0200 Subject: [PATCH 88/97] rp235x overclocking --- embassy-rp/src/clocks.rs | 329 +++++++++++++++++++++--- examples/rp/src/bin/overclock.rs | 8 +- examples/rp/src/bin/overclock_manual.rs | 10 +- examples/rp235x/src/bin/overclock.rs | 74 ++++++ tests/rp/src/bin/overclock.rs | 49 ++-- 5 files changed, 405 insertions(+), 65 deletions(-) create mode 100644 examples/rp235x/src/bin/overclock.rs diff --git a/embassy-rp/src/clocks.rs b/embassy-rp/src/clocks.rs index 6694aab66..ea5e9362b 100644 --- a/embassy-rp/src/clocks.rs +++ b/embassy-rp/src/clocks.rs @@ -38,7 +38,7 @@ //! //! ## Examples //! -//! ### Standard 125MHz configuration +//! ### Standard 125MHz (rp2040) or 150Mhz (rp235x) configuration //! ```rust,ignore //! let config = ClockConfig::crystal(12_000_000); //! ``` @@ -136,43 +136,152 @@ pub enum PeriClkSrc { // Gpin1 = ClkPeriCtrlAuxsrc::CLKSRC_GPIN1 as _ , } -/// Core voltage regulator settings for RP2040. +/// Core voltage regulator settings. /// -/// The RP2040 voltage regulator can be configured for different output voltages. +/// The voltage regulator can be configured for different output voltages. /// Higher voltages allow for higher clock frequencies but increase power consumption and heat. -#[cfg(feature = "rp2040")] #[derive(Clone, Copy, Debug, PartialEq, Eq)] #[repr(u8)] pub enum CoreVoltage { - /// 0.80V - Suitable for lower frequencies + // RP2040 voltage levels + #[cfg(feature = "rp2040")] + /// RP2040: 0.80V V0_80 = 0b0000, - /// 0.85V + #[cfg(feature = "rp2040")] + /// RP2040: 0.85V V0_85 = 0b0110, - /// 0.90V + #[cfg(feature = "rp2040")] + /// RP2040: 0.90V V0_90 = 0b0111, - /// 0.95V + #[cfg(feature = "rp2040")] + /// RP2040: 0.95V V0_95 = 0b1000, - /// 1.00V + #[cfg(feature = "rp2040")] + /// RP2040: 1.00V V1_00 = 0b1001, - /// 1.05V + #[cfg(feature = "rp2040")] + /// RP2040: 1.05V V1_05 = 0b1010, - /// 1.10V - Default voltage level + #[cfg(feature = "rp2040")] + /// RP2040: 1.10V - Default voltage level V1_10 = 0b1011, - /// 1.15V - Required for overclocking to 133-200MHz + #[cfg(feature = "rp2040")] + /// RP2040: 1.15V - Required for overclocking to 133-200MHz V1_15 = 0b1100, - /// 1.20V + #[cfg(feature = "rp2040")] + /// RP2040: 1.20V V1_20 = 0b1101, - /// 1.25V + #[cfg(feature = "rp2040")] + /// RP2040: 1.25V V1_25 = 0b1110, - /// 1.30V + #[cfg(feature = "rp2040")] + /// RP2040: 1.30V V1_30 = 0b1111, + + // RP235x voltage levels + #[cfg(feature = "_rp235x")] + /// RP235x: 0.55V + V0_55 = 0b00000, + #[cfg(feature = "_rp235x")] + /// RP235x: 0.60V + V0_60 = 0b00001, + #[cfg(feature = "_rp235x")] + /// RP235x: 0.65V + V0_65 = 0b00010, + #[cfg(feature = "_rp235x")] + /// RP235x: 0.70V + V0_70 = 0b00011, + #[cfg(feature = "_rp235x")] + /// RP235x: 0.75V + V0_75 = 0b00100, + #[cfg(feature = "_rp235x")] + /// RP235x: 0.80V + V0_80 = 0b00101, + #[cfg(feature = "_rp235x")] + /// RP235x: 0.85V + V0_85 = 0b00110, + #[cfg(feature = "_rp235x")] + /// RP235x: 0.90V + V0_90 = 0b00111, + #[cfg(feature = "_rp235x")] + /// RP235x: 0.95V + V0_95 = 0b01000, + #[cfg(feature = "_rp235x")] + /// RP235x: 1.00V + V1_00 = 0b01001, + #[cfg(feature = "_rp235x")] + /// RP235x: 1.05V + V1_05 = 0b01010, + #[cfg(feature = "_rp235x")] + /// RP235x: 1.10V - Default voltage level + V1_10 = 0b01011, + #[cfg(feature = "_rp235x")] + /// RP235x: 1.15V + V1_15 = 0b01100, + #[cfg(feature = "_rp235x")] + /// RP235x: 1.20V + V1_20 = 0b01101, + #[cfg(feature = "_rp235x")] + /// RP235x: 1.25V + V1_25 = 0b01110, + #[cfg(feature = "_rp235x")] + /// RP235x: 1.30V + V1_30 = 0b01111, + #[cfg(feature = "_rp235x")] + /// RP235x: 1.35V + V1_35 = 0b10000, + #[cfg(feature = "_rp235x")] + /// RP235x: 1.40V + V1_40 = 0b10001, + #[cfg(feature = "_rp235x")] + /// RP235x: 1.50V + V1_50 = 0b10010, + #[cfg(feature = "_rp235x")] + /// RP235x: 1.60V + V1_60 = 0b10011, + #[cfg(feature = "_rp235x")] + /// RP235x: 1.65V + V1_65 = 0b10100, + #[cfg(feature = "_rp235x")] + /// RP235x: 1.70V + V1_70 = 0b10101, + #[cfg(feature = "_rp235x")] + /// RP235x: 1.80V + V1_80 = 0b10110, + #[cfg(feature = "_rp235x")] + /// RP235x: 1.90V + V1_90 = 0b10111, + #[cfg(feature = "_rp235x")] + /// RP235x: 2.00V + V2_00 = 0b11000, + #[cfg(feature = "_rp235x")] + /// RP235x: 2.35V + V2_35 = 0b11001, + #[cfg(feature = "_rp235x")] + /// RP235x: 2.50V + V2_50 = 0b11010, + #[cfg(feature = "_rp235x")] + /// RP235x: 2.65V + V2_65 = 0b11011, + #[cfg(feature = "_rp235x")] + /// RP235x: 2.80V + V2_80 = 0b11100, + #[cfg(feature = "_rp235x")] + /// RP235x: 3.00V + V3_00 = 0b11101, + #[cfg(feature = "_rp235x")] + /// RP235x: 3.15V + V3_15 = 0b11110, + #[cfg(feature = "_rp235x")] + /// RP235x: 3.30V + V3_30 = 0b11111, } -#[cfg(feature = "rp2040")] impl CoreVoltage { /// Get the recommended Brown-Out Detection (BOD) setting for this voltage. /// Sets the BOD threshold to approximately 80% of the core voltage. fn recommended_bod(self) -> u8 { + #[cfg(feature = "rp2040")] match self { CoreVoltage::V0_80 => 0b0100, // 0.645V (~81% of 0.80V) CoreVoltage::V0_85 => 0b0101, // 0.688V (~81% of 0.85V) @@ -180,12 +289,38 @@ impl CoreVoltage { CoreVoltage::V0_95 => 0b0111, // 0.774V (~81% of 0.95V) CoreVoltage::V1_00 => 0b1000, // 0.817V (~82% of 1.00V) CoreVoltage::V1_05 => 0b1000, // 0.817V (~78% of 1.05V) - CoreVoltage::V1_10 => 0b1001, // 0.860V (~78% of 1.10V) + CoreVoltage::V1_10 => 0b1001, // 0.860V (~78% of 1.10V), the default CoreVoltage::V1_15 => 0b1010, // 0.903V (~79% of 1.15V) CoreVoltage::V1_20 => 0b1011, // 0.946V (~79% of 1.20V) CoreVoltage::V1_25 => 0b1100, // 0.989V (~79% of 1.25V) CoreVoltage::V1_30 => 0b1101, // 1.032V (~79% of 1.30V) } + #[cfg(feature = "_rp235x")] + match self { + CoreVoltage::V0_55 => 0b00001, // 0.516V (~94% of 0.55V) + CoreVoltage::V0_60 => 0b00010, // 0.559V (~93% of 0.60V) + CoreVoltage::V0_65 => 0b00011, // 0.602V (~93% of 0.65V) + CoreVoltage::V0_70 => 0b00011, // 0.602V (~86% of 0.70V) + CoreVoltage::V0_75 => 0b00100, // 0.645V (~86% of 0.75V) + CoreVoltage::V0_80 => 0b00101, // 0.688V (~86% of 0.80V) + CoreVoltage::V0_85 => 0b00110, // 0.731V (~86% of 0.85V) + CoreVoltage::V0_90 => 0b00110, // 0.731V (~81% of 0.90V) + CoreVoltage::V0_95 => 0b00111, // 0.774V (~81% of 0.95V) + CoreVoltage::V1_00 => 0b01000, // 0.817V (~82% of 1.00V) + CoreVoltage::V1_05 => 0b01000, // 0.817V (~78% of 1.05V) + CoreVoltage::V1_10 => 0b01001, // 0.860V (~78% of 1.10V), the default + CoreVoltage::V1_15 => 0b01001, // 0.860V (~75% of 1.15V) + CoreVoltage::V1_20 => 0b01010, // 0.903V (~75% of 1.20V) + CoreVoltage::V1_25 => 0b01010, // 0.903V (~72% of 1.25V) + CoreVoltage::V1_30 => 0b01011, // 0.946V (~73% of 1.30V) + CoreVoltage::V1_35 => 0b01011, // 0.946V (~70% of 1.35V) + CoreVoltage::V1_40 => 0b01100, // 0.989V (~71% of 1.40V) + CoreVoltage::V1_50 => 0b01101, // 1.032V (~69% of 1.50V) + CoreVoltage::V1_60 => 0b01110, // 1.075V (~67% of 1.60V) + CoreVoltage::V1_65 => 0b01110, // 1.075V (~65% of 1.65V) + CoreVoltage::V1_70 => 0b01111, // 1.118V (~66% of 1.70V) + _ => 0b10000, // the rp2350 datasheet repeats this value for all other core voltages + } } } @@ -209,12 +344,10 @@ pub struct ClockConfig { /// RTC clock configuration. #[cfg(feature = "rp2040")] pub rtc_clk: Option, - /// Core voltage scaling (RP2040 only). Defaults to 1.10V. - #[cfg(feature = "rp2040")] + /// Core voltage scaling. Defaults to 1.10V. pub core_voltage: CoreVoltage, /// Voltage stabilization delay in microseconds. /// If not set, defaults will be used based on voltage level. - #[cfg(feature = "rp2040")] pub voltage_stabilization_delay_us: Option, // See above re gpin handling being commented out // gpin0: Option<(u32, Gpin<'static, AnyPin>)>, @@ -250,9 +383,7 @@ impl Default for ClockConfig { adc_clk: None, #[cfg(feature = "rp2040")] rtc_clk: None, - #[cfg(feature = "rp2040")] core_voltage: CoreVoltage::V1_10, - #[cfg(feature = "rp2040")] voltage_stabilization_delay_us: None, // See above re gpin handling being commented out // gpin0: None, @@ -323,9 +454,7 @@ impl ClockConfig { div_frac: 0, phase: 0, }), - #[cfg(feature = "rp2040")] core_voltage: CoreVoltage::V1_10, // Use hardware default (1.10V) - #[cfg(feature = "rp2040")] voltage_stabilization_delay_us: None, // See above re gpin handling being commented out // gpin0: None, @@ -368,9 +497,7 @@ impl ClockConfig { div_frac: 171, phase: 0, }), - #[cfg(feature = "rp2040")] core_voltage: CoreVoltage::V1_10, // Use hardware default (1.10V) - #[cfg(feature = "rp2040")] voltage_stabilization_delay_us: None, // See above re gpin handling being commented out // gpin0: None, @@ -394,12 +521,17 @@ impl ClockConfig { /// the usual 12Mhz crystal, or panic if no valid parameters can be found. /// /// # Note on core voltage: + /// + /// **For RP2040**: /// To date the only officially documented core voltages (see Datasheet section 2.15.3.1. Instances) are: /// - Up to 133MHz: V1_10 (default) /// - Above 133MHz: V1_15, but in the context of the datasheet covering reaching up to 200Mhz /// That way all other frequencies below 133MHz or above 200MHz are not explicitly documented and not covered here. /// In case You want to go below 133MHz or above 200MHz and want a different voltage, You will have to set that manually and with caution. - #[cfg(feature = "rp2040")] + /// + /// **For RP235x**: + /// At this point in time there is no official manufacturer endorsement for running the chip on other core voltages and/or other clock speeds than the defaults. + /// Using this function is experimental and may not work as expected or even damage the chip. pub fn system_freq(hz: u32) -> Self { // Start with the standard configuration from crystal() const DEFAULT_CRYSTAL_HZ: u32 = 12_000_000; @@ -407,9 +539,14 @@ impl ClockConfig { // No need to modify anything if target frequency is already 125MHz // (which is what crystal() configures by default) + #[cfg(feature = "rp2040")] if hz == 125_000_000 { return config; } + #[cfg(feature = "_rp235x")] + if hz == 150_000_000 { + return config; + } // Find optimal PLL parameters for the requested frequency let sys_pll_params = find_pll_params(DEFAULT_CRYSTAL_HZ, hz) @@ -429,6 +566,14 @@ impl ClockConfig { _ => CoreVoltage::V1_10, // Use default voltage (V1_10) }; } + #[cfg(feature = "_rp235x")] + { + config.core_voltage = match hz { + // There is no official support for running the chip on other core voltages and/or other clock speeds than the defaults. + // So for now we have not way of knowing what the voltage should be. Change this if the manufacturer provides more information. + _ => CoreVoltage::V1_10, // Use default voltage (V1_10) + }; + } config } @@ -791,7 +936,6 @@ pub struct RtcClkConfig { /// // Find parameters for 133MHz system clock from 12MHz crystal /// let pll_params = find_pll_params(12_000_000, 133_000_000).unwrap(); /// ``` -#[cfg(feature = "rp2040")] fn find_pll_params(input_hz: u32, target_hz: u32) -> Option { // Fixed reference divider for system PLL const PLL_SYS_REFDIV: u8 = 1; @@ -925,18 +1069,59 @@ pub(crate) unsafe fn init(config: ClockConfig) { }; CLOCKS.rosc.store(rosc_freq, Ordering::Relaxed); - // Set Core Voltage (RP2040 only), if we have config for it and we're not using the default - #[cfg(feature = "rp2040")] + // Set Core Voltage, if we have config for it and we're not using the default { let voltage = config.core_voltage; + + #[cfg(feature = "rp2040")] let vreg = pac::VREG_AND_CHIP_RESET; + #[cfg(feature = "_rp235x")] + let vreg = pac::POWMAN; + let current_vsel = vreg.vreg().read().vsel(); let target_vsel = voltage as u8; // If the target voltage is different from the current one, we need to change it if target_vsel != current_vsel { - // Use modify() to preserve the HIZ and EN bits - otherwise we will disable the regulator when changing voltage - vreg.vreg().modify(|w| w.set_vsel(target_vsel)); + #[cfg(feature = "rp2040")] + { + // Use modify() to preserve the HIZ and EN bits - otherwise we will disable the regulator when changing voltage + vreg.vreg().modify(|w| w.set_vsel(target_vsel)); + } + #[cfg(feature = "_rp235x")] + { + // The rp235x has a different way of controlling the voltage regulator + // Changes to the voltage regulator are protected by a password, see datasheet section 6.4 + // The password is "5AFE" (0x5AFE), it must be set in the top 16 bits of the register + + // The rp235x by default locks the voltage regulator control, so we need to unlock it first + // See datasheet section 6.3.2. Software Control + vreg.vreg_ctrl().modify(|w| { + // Add password to top 16 bits, preserving the rest, repeat below for other registers + w.0 = (w.0 & 0x0000FFFF) | (0x5AFE << 16); + w.set_unlock(true); + *w + }); + + // Set the voltage + vreg.vreg().modify(|w| { + w.0 = (w.0 & 0x0000FFFF) | (0x5AFE << 16); + w.set_vsel(target_vsel); + *w + }); + + // The rp235x has two more registers to set the voltage for low power mode + vreg.vreg_lp_entry().modify(|w| { + w.0 = (w.0 & 0x0000FFFF) | (0x5AFE << 16); + w.set_vsel(target_vsel); + *w + }); + vreg.vreg_lp_exit().modify(|w| { + w.0 = (w.0 & 0x0000FFFF) | (0x5AFE << 16); + w.set_vsel(target_vsel); + *w + }); + } // Wait for the voltage to stabilize. Use the provided delay or default based on voltage let settling_time_us = config.voltage_stabilization_delay_us.unwrap_or_else(|| { @@ -959,6 +1144,19 @@ pub(crate) unsafe fn init(config: ClockConfig) { w.set_vsel(voltage.recommended_bod()); w.set_en(true); // Enable brownout detection }); + + #[cfg(feature = "_rp235x")] + { + // The rp235x has a separate register for the BOD level in low power mode + vreg.bod_lp_entry().write(|w| { + w.set_vsel(voltage.recommended_bod()); + w.set_en(true); // Enable brownout detection + }); + vreg.bod_lp_exit().write(|w| { + w.set_vsel(voltage.recommended_bod()); + w.set_en(true); // Enable brownout detection + }); + } } } @@ -1283,6 +1481,73 @@ pub fn clk_rtc_freq() -> u16 { CLOCKS.rtc.load(Ordering::Relaxed) } +/// The core voltage of the chip. +/// +/// Returns the current core voltage or an error if the voltage register +/// contains an unknown value. +pub fn core_voltage() -> Result { + #[cfg(feature = "rp2040")] + { + let vreg = pac::VREG_AND_CHIP_RESET; + let vsel = vreg.vreg().read().vsel(); + match vsel { + 0b0000 => Ok(CoreVoltage::V0_80), + 0b0110 => Ok(CoreVoltage::V0_85), + 0b0111 => Ok(CoreVoltage::V0_90), + 0b1000 => Ok(CoreVoltage::V0_95), + 0b1001 => Ok(CoreVoltage::V1_00), + 0b1010 => Ok(CoreVoltage::V1_05), + 0b1011 => Ok(CoreVoltage::V1_10), + 0b1100 => Ok(CoreVoltage::V1_15), + 0b1101 => Ok(CoreVoltage::V1_20), + 0b1110 => Ok(CoreVoltage::V1_25), + 0b1111 => Ok(CoreVoltage::V1_30), + _ => Err("Unexpected value in register"), + } + } + + #[cfg(feature = "_rp235x")] + { + let vreg = pac::POWMAN; + let vsel = vreg.vreg().read().vsel(); + match vsel { + 0b00000 => Ok(CoreVoltage::V0_55), + 0b00001 => Ok(CoreVoltage::V0_60), + 0b00010 => Ok(CoreVoltage::V0_65), + 0b00011 => Ok(CoreVoltage::V0_70), + 0b00100 => Ok(CoreVoltage::V0_75), + 0b00101 => Ok(CoreVoltage::V0_80), + 0b00110 => Ok(CoreVoltage::V0_85), + 0b00111 => Ok(CoreVoltage::V0_90), + 0b01000 => Ok(CoreVoltage::V0_95), + 0b01001 => Ok(CoreVoltage::V1_00), + 0b01010 => Ok(CoreVoltage::V1_05), + 0b01011 => Ok(CoreVoltage::V1_10), + 0b01100 => Ok(CoreVoltage::V1_15), + 0b01101 => Ok(CoreVoltage::V1_20), + 0b01110 => Ok(CoreVoltage::V1_25), + 0b01111 => Ok(CoreVoltage::V1_30), + 0b10000 => Ok(CoreVoltage::V1_35), + 0b10001 => Ok(CoreVoltage::V1_40), + 0b10010 => Ok(CoreVoltage::V1_50), + 0b10011 => Ok(CoreVoltage::V1_60), + 0b10100 => Ok(CoreVoltage::V1_65), + 0b10101 => Ok(CoreVoltage::V1_70), + 0b10110 => Ok(CoreVoltage::V1_80), + 0b10111 => Ok(CoreVoltage::V1_90), + 0b11000 => Ok(CoreVoltage::V2_00), + 0b11001 => Ok(CoreVoltage::V2_35), + 0b11010 => Ok(CoreVoltage::V2_50), + 0b11011 => Ok(CoreVoltage::V2_65), + 0b11100 => Ok(CoreVoltage::V2_80), + 0b11101 => Ok(CoreVoltage::V3_00), + 0b11110 => Ok(CoreVoltage::V3_15), + 0b11111 => Ok(CoreVoltage::V3_30), + _ => Err("Unexpected value in register"), + } + } +} + fn start_xosc(crystal_hz: u32, delay_multiplier: u32) { let startup_delay = (((crystal_hz / 1000) * delay_multiplier) + 128) / 256; pac::XOSC.startup().write(|w| w.set_delay(startup_delay as u16)); diff --git a/examples/rp/src/bin/overclock.rs b/examples/rp/src/bin/overclock.rs index 9c78e0c9d..89147ba42 100644 --- a/examples/rp/src/bin/overclock.rs +++ b/examples/rp/src/bin/overclock.rs @@ -7,7 +7,7 @@ use defmt::*; use embassy_executor::Spawner; -use embassy_rp::clocks::{clk_sys_freq, ClockConfig}; +use embassy_rp::clocks::{clk_sys_freq, core_voltage, ClockConfig}; use embassy_rp::config::Config; use embassy_rp::gpio::{Level, Output}; use embassy_time::{Duration, Instant, Timer}; @@ -20,15 +20,15 @@ async fn main(_spawner: Spawner) -> ! { // Set up for clock frequency of 200 MHz, setting all necessary defaults. let config = Config::new(ClockConfig::system_freq(200_000_000)); - // Show the voltage scale for verification - info!("System core voltage: {}", Debug2Format(&config.clocks.core_voltage)); - // Initialize the peripherals let p = embassy_rp::init(config); // Show CPU frequency for verification let sys_freq = clk_sys_freq(); info!("System clock frequency: {} MHz", sys_freq / 1_000_000); + // Show core voltage for verification + let core_voltage = core_voltage().unwrap(); + info!("Core voltage: {}", Debug2Format(&core_voltage)); // LED to indicate the system is running let mut led = Output::new(p.PIN_25, Level::Low); diff --git a/examples/rp/src/bin/overclock_manual.rs b/examples/rp/src/bin/overclock_manual.rs index 35160b250..88ef26a7a 100644 --- a/examples/rp/src/bin/overclock_manual.rs +++ b/examples/rp/src/bin/overclock_manual.rs @@ -7,8 +7,7 @@ use defmt::*; use embassy_executor::Spawner; -use embassy_rp::clocks; -use embassy_rp::clocks::{ClockConfig, CoreVoltage, PllConfig}; +use embassy_rp::clocks::{clk_sys_freq, core_voltage, ClockConfig, CoreVoltage, PllConfig}; use embassy_rp::config::Config; use embassy_rp::gpio::{Level, Output}; use embassy_time::{Duration, Instant, Timer}; @@ -41,9 +40,12 @@ async fn main(_spawner: Spawner) -> ! { // Initialize with our manual overclock configuration let p = embassy_rp::init(configure_manual_overclock()); - // Verify the actual system clock frequency - let sys_freq = clocks::clk_sys_freq(); + // Show CPU frequency for verification + let sys_freq = clk_sys_freq(); info!("System clock frequency: {} MHz", sys_freq / 1_000_000); + // Show core voltage for verification + let core_voltage = core_voltage().unwrap(); + info!("Core voltage: {}", Debug2Format(&core_voltage)); // LED to indicate the system is running let mut led = Output::new(p.PIN_25, Level::Low); diff --git a/examples/rp235x/src/bin/overclock.rs b/examples/rp235x/src/bin/overclock.rs new file mode 100644 index 000000000..8713df688 --- /dev/null +++ b/examples/rp235x/src/bin/overclock.rs @@ -0,0 +1,74 @@ +//! # Overclocking the RP2350 to 200 MHz +//! +//! This example demonstrates how to configure the RP2350 to run at 200 MHz instead of the default 150 MHz. +//! +//! ## Note +//! +//! As of yet there is no official support for running the RP235x at higher clock frequencies and/or other core voltages than the default. +//! Doing so may cause unexpected behavior and/or damage the chip. + +#![no_std] +#![no_main] + +use defmt::*; +use embassy_executor::Spawner; +use embassy_rp::clocks::{clk_sys_freq, core_voltage, ClockConfig, CoreVoltage}; +use embassy_rp::config::Config; +use embassy_rp::gpio::{Level, Output}; +use embassy_time::{Duration, Instant, Timer}; +use {defmt_rtt as _, panic_probe as _}; + +const COUNT_TO: i64 = 10_000_000; + +#[embassy_executor::main] +async fn main(_spawner: Spawner) -> ! { + // Set up for clock frequency of 200 MHz, setting all necessary defaults. + let mut config = Config::new(ClockConfig::system_freq(200_000_000)); + + // since for the rp235x there is no official support for higher clock frequencies, `system_freq()` will not set a voltage for us. + // We need to guess the core voltage, that is needed for the higher clock frequency. Going with a small increase from the default 1.1V here, based on + // what we know about the RP2040. This is not guaranteed to be correct. + config.clocks.core_voltage = CoreVoltage::V1_15; + + // Initialize the peripherals + let p = embassy_rp::init(config); + + // Show CPU frequency for verification + let sys_freq = clk_sys_freq(); + info!("System clock frequency: {} MHz", sys_freq / 1_000_000); + // Show core voltage for verification + let core_voltage = core_voltage().unwrap(); + info!("Core voltage: {}", Debug2Format(&core_voltage)); + + // LED to indicate the system is running + let mut led = Output::new(p.PIN_25, Level::Low); + + loop { + // Reset the counter at the start of measurement period + let mut counter = 0; + + // Turn LED on while counting + led.set_high(); + + let start = Instant::now(); + + // This is a busy loop that will take some time to complete + while counter < COUNT_TO { + counter += 1; + } + + let elapsed = Instant::now() - start; + + // Report the elapsed time + led.set_low(); + info!( + "At {}Mhz: Elapsed time to count to {}: {}ms", + sys_freq / 1_000_000, + counter, + elapsed.as_millis() + ); + + // Wait 2 seconds before starting the next measurement + Timer::after(Duration::from_secs(2)).await; + } +} diff --git a/tests/rp/src/bin/overclock.rs b/tests/rp/src/bin/overclock.rs index be8e85a3f..a568d7fed 100644 --- a/tests/rp/src/bin/overclock.rs +++ b/tests/rp/src/bin/overclock.rs @@ -7,14 +7,8 @@ teleprobe_meta::target!(b"rpi-pico"); teleprobe_meta::target!(b"pimoroni-pico-plus-2"); use defmt::info; -#[cfg(feature = "rp2040")] -use defmt::{assert, assert_eq}; use embassy_executor::Spawner; -use embassy_rp::clocks; -#[cfg(feature = "rp2040")] -use embassy_rp::clocks::ClockConfig; -#[cfg(feature = "rp2040")] -use embassy_rp::clocks::CoreVoltage; +use embassy_rp::clocks::{clk_sys_freq, core_voltage, ClockConfig, CoreVoltage}; use embassy_rp::config::Config; use embassy_time::Instant; use {defmt_rtt as _, panic_probe as _}; @@ -23,23 +17,26 @@ const COUNT_TO: i64 = 10_000_000; #[embassy_executor::main] async fn main(_spawner: Spawner) { - #[cfg(feature = "rp2040")] let mut config = Config::default(); - #[cfg(not(feature = "rp2040"))] - let config = Config::default(); - // Initialize with 200MHz clock configuration for RP2040, other chips will use default clock - #[cfg(feature = "rp2040")] + // Initialize with 200MHz clock configuration + config.clocks = ClockConfig::system_freq(200_000_000); + + // if we are rp235x, we need to manually set the core voltage. rp2040 should do this automatically + #[cfg(feature = "rp235xb")] { - config.clocks = ClockConfig::system_freq(200_000_000); - let voltage = config.clocks.core_voltage; - assert!(matches!(voltage, CoreVoltage::V1_15), "Expected voltage scale V1_15"); + config.clocks.core_voltage = CoreVoltage::V1_15; } let _p = embassy_rp::init(config); + // We should be at core voltage of 1.15V + assert_eq!(core_voltage().unwrap(), CoreVoltage::V1_15, "Core voltage is not 1.15V"); + // We should be at 200MHz + assert_eq!(clk_sys_freq(), 200_000_000, "System clock frequency is not 200MHz"); + // Test the system speed - let (time_elapsed, clk_sys_freq) = { + let time_elapsed = { let mut counter = 0; let start = Instant::now(); while counter < COUNT_TO { @@ -47,24 +44,26 @@ async fn main(_spawner: Spawner) { } let elapsed = Instant::now() - start; - (elapsed.as_millis(), clocks::clk_sys_freq()) + elapsed.as_millis() }; - // Report the elapsed time, so that the compiler doesn't optimize it away for chips other than RP2040 + // Tests will fail if unused variables are detected: + // Report the elapsed time, so that the compiler doesn't optimize it away for the chip not on test info!( "At {}Mhz: Elapsed time to count to {}: {}ms", - clk_sys_freq / 1_000_000, + clk_sys_freq() / 1_000_000, COUNT_TO, time_elapsed ); + // Check if the elapsed time is within expected limits + // for rp2040 we expect about 600ms #[cfg(feature = "rp2040")] - { - // we should be at 200MHz - assert_eq!(clk_sys_freq, 200_000_000, "System clock frequency is not 200MHz"); - // At 200MHz, the time to count to 10_000_000 should be at 600ms, testing with 1% margin - assert!(time_elapsed <= 606, "Elapsed time is too long"); - } + // allow 1% error + assert!(time_elapsed < 606, "Elapsed time is too long"); + // for rp235x we expect about 450ms + #[cfg(feature = "rp235xb")] + assert!(time_elapsed < 455, "Elapsed time is too long"); cortex_m::asm::bkpt(); } From a0a339d01ade57aa6e66835a27b1e6ea54333b54 Mon Sep 17 00:00:00 2001 From: Matthew Tran <0e4ef622@gmail.com> Date: Thu, 8 May 2025 23:26:10 -0500 Subject: [PATCH 89/97] nrf: Rework IPC module --- embassy-nrf/src/ipc.rs | 423 +++++++++++++++++++++-------------------- 1 file changed, 213 insertions(+), 210 deletions(-) diff --git a/embassy-nrf/src/ipc.rs b/embassy-nrf/src/ipc.rs index 410783ef4..a8a08c911 100644 --- a/embassy-nrf/src/ipc.rs +++ b/embassy-nrf/src/ipc.rs @@ -3,18 +3,20 @@ #![macro_use] use core::future::poll_fn; -use core::sync::atomic::{compiler_fence, Ordering}; +use core::marker::PhantomData; use core::task::Poll; -use embassy_hal_internal::{into_ref, Peripheral, PeripheralRef}; +use embassy_hal_internal::{Peri, PeripheralType}; use embassy_sync::waitqueue::AtomicWaker; -use crate::peripherals::IPC; -use crate::{interrupt, pac}; +use crate::interrupt::typelevel::Interrupt; +use crate::{interrupt, pac, ppi}; + +const EVENT_COUNT: usize = 16; /// IPC Event -#[derive(Debug, Clone, Copy)] -pub enum IpcEvent { +#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)] +pub enum EventNumber { /// IPC Event 0 Event0 = 0, /// IPC Event 1 @@ -49,27 +51,27 @@ pub enum IpcEvent { Event15 = 15, } -const EVENTS: [IpcEvent; 16] = [ - IpcEvent::Event0, - IpcEvent::Event1, - IpcEvent::Event2, - IpcEvent::Event3, - IpcEvent::Event4, - IpcEvent::Event5, - IpcEvent::Event6, - IpcEvent::Event7, - IpcEvent::Event8, - IpcEvent::Event9, - IpcEvent::Event10, - IpcEvent::Event11, - IpcEvent::Event12, - IpcEvent::Event13, - IpcEvent::Event14, - IpcEvent::Event15, +const EVENTS: [EventNumber; EVENT_COUNT] = [ + EventNumber::Event0, + EventNumber::Event1, + EventNumber::Event2, + EventNumber::Event3, + EventNumber::Event4, + EventNumber::Event5, + EventNumber::Event6, + EventNumber::Event7, + EventNumber::Event8, + EventNumber::Event9, + EventNumber::Event10, + EventNumber::Event11, + EventNumber::Event12, + EventNumber::Event13, + EventNumber::Event14, + EventNumber::Event15, ]; /// IPC Channel -#[derive(Debug, Clone, Copy)] +#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)] pub enum IpcChannel { /// IPC Channel 0 Channel0, @@ -105,188 +107,124 @@ pub enum IpcChannel { Channel15, } -/// Interrupt Handler -pub struct InterruptHandler {} +impl IpcChannel { + fn mask(self) -> u32 { + 1 << (self as u32) + } +} -impl interrupt::typelevel::Handler for InterruptHandler { +/// Interrupt Handler +pub struct InterruptHandler { + _phantom: PhantomData, +} + +impl interrupt::typelevel::Handler for InterruptHandler { unsafe fn on_interrupt() { - let regs = IPC::regs(); + let regs = T::regs(); // Check if an event was generated, and if it was, trigger the corresponding waker for event in EVENTS { if regs.events_receive(event as usize).read() & 0x01 == 0x01 { - // Event is set. Reset and wake waker - regs.events_receive(event as usize).write_value(0); - IPC::state().waker_for(event); + regs.intenclr().write(|w| w.0 = 0x01 << event as u32); + T::state().wakers[event as usize].wake(); } - - // Ensure the state is actually cleared - // Ref: nRF5340 PS v1.5 7.1.9.1 p.153 - compiler_fence(Ordering::SeqCst); - while regs.events_receive(event as usize).read() & 0x01 != 0x00 {} } } } /// IPC driver +#[non_exhaustive] pub struct Ipc<'d, T: Instance> { - _peri: PeripheralRef<'d, T>, -} - -impl<'d, T: Instance> From> for Ipc<'d, T> { - fn from(value: PeripheralRef<'d, T>) -> Self { - Self { _peri: value } - } + /// Event 0 + pub event0: Event<'d, T>, + /// Event 1 + pub event1: Event<'d, T>, + /// Event 2 + pub event2: Event<'d, T>, + /// Event 3 + pub event3: Event<'d, T>, + /// Event 4 + pub event4: Event<'d, T>, + /// Event 5 + pub event5: Event<'d, T>, + /// Event 6 + pub event6: Event<'d, T>, + /// Event 7 + pub event7: Event<'d, T>, + /// Event 8 + pub event8: Event<'d, T>, + /// Event 9 + pub event9: Event<'d, T>, + /// Event 10 + pub event10: Event<'d, T>, + /// Event 11 + pub event11: Event<'d, T>, + /// Event 12 + pub event12: Event<'d, T>, + /// Event 13 + pub event13: Event<'d, T>, + /// Event 14 + pub event14: Event<'d, T>, + /// Event 15 + pub event15: Event<'d, T>, } impl<'d, T: Instance> Ipc<'d, T> { - /// Create IPC driver - pub fn new(ipc: impl Peripheral

+ 'd) -> Self { - into_ref!(ipc); + /// Create a new IPC driver. + pub fn new( + _p: Peri<'d, T>, + _irq: impl interrupt::typelevel::Binding> + 'd, + ) -> Self { + T::Interrupt::unpend(); + unsafe { T::Interrupt::enable() }; - Self { _peri: ipc } - } - - /// Duplicates the peripheral singleton - /// - /// # Safety - /// - /// Ensure manually that only one peripheral is in use at one time - pub unsafe fn clone_unchecked(&self) -> Self { - Self { - _peri: self._peri.clone_unchecked(), - } - } - - /// Configures the sending of events - /// - /// Events can be configured to broadcast on one or multiple IPC channels. - pub fn configure_send_event>(&self, ev: IpcEvent, channels: I) { - let regs = T::regs(); - - regs.send_cnf(ev as usize).write(|w| { - for channel in channels { - match channel { - IpcChannel::Channel0 => w.set_chen0(true), - IpcChannel::Channel1 => w.set_chen1(true), - IpcChannel::Channel2 => w.set_chen2(true), - IpcChannel::Channel3 => w.set_chen3(true), - IpcChannel::Channel4 => w.set_chen4(true), - IpcChannel::Channel5 => w.set_chen5(true), - IpcChannel::Channel6 => w.set_chen6(true), - IpcChannel::Channel7 => w.set_chen7(true), - IpcChannel::Channel8 => w.set_chen8(true), - IpcChannel::Channel9 => w.set_chen9(true), - IpcChannel::Channel10 => w.set_chen10(true), - IpcChannel::Channel11 => w.set_chen11(true), - IpcChannel::Channel12 => w.set_chen12(true), - IpcChannel::Channel13 => w.set_chen13(true), - IpcChannel::Channel14 => w.set_chen14(true), - IpcChannel::Channel15 => w.set_chen15(true), - } - } - }) - } - - /// Configures the receiving of events - /// - /// Events can be configured to be received by one or multiple IPC channels. - pub fn configure_receive_event>(&self, ev: IpcEvent, channels: I) { - let regs = T::regs(); - - regs.receive_cnf(ev as usize).write(|w| { - for channel in channels { - match channel { - IpcChannel::Channel0 => w.set_chen0(true), - IpcChannel::Channel1 => w.set_chen1(true), - IpcChannel::Channel2 => w.set_chen2(true), - IpcChannel::Channel3 => w.set_chen3(true), - IpcChannel::Channel4 => w.set_chen4(true), - IpcChannel::Channel5 => w.set_chen5(true), - IpcChannel::Channel6 => w.set_chen6(true), - IpcChannel::Channel7 => w.set_chen7(true), - IpcChannel::Channel8 => w.set_chen8(true), - IpcChannel::Channel9 => w.set_chen9(true), - IpcChannel::Channel10 => w.set_chen10(true), - IpcChannel::Channel11 => w.set_chen11(true), - IpcChannel::Channel12 => w.set_chen12(true), - IpcChannel::Channel13 => w.set_chen13(true), - IpcChannel::Channel14 => w.set_chen14(true), - IpcChannel::Channel15 => w.set_chen15(true), - } - } - }); - } - - /// Triggers an event - pub fn trigger_event(&self, ev: IpcEvent) { - let regs = T::regs(); - - regs.tasks_send(ev as usize).write_value(0x01); - } - - /// Wait for event to be triggered - pub async fn wait_for_event(&self, ev: IpcEvent) { - let regs = T::regs(); - - // Enable interrupt - match ev { - IpcEvent::Event0 => { - regs.inten().modify(|m| m.set_receive0(true)); - } - IpcEvent::Event1 => { - regs.inten().modify(|m| m.set_receive1(true)); - } - IpcEvent::Event2 => { - regs.inten().modify(|m| m.set_receive2(true)); - } - IpcEvent::Event3 => { - regs.inten().modify(|m| m.set_receive3(true)); - } - IpcEvent::Event4 => { - regs.inten().modify(|m| m.set_receive4(true)); - } - IpcEvent::Event5 => { - regs.inten().modify(|m| m.set_receive5(true)); - } - IpcEvent::Event6 => { - regs.inten().modify(|m| m.set_receive6(true)); - } - IpcEvent::Event7 => { - regs.inten().modify(|m| m.set_receive7(true)); - } - IpcEvent::Event8 => { - regs.inten().modify(|m| m.set_receive8(true)); - } - IpcEvent::Event9 => { - regs.inten().modify(|m| m.set_receive9(true)); - } - IpcEvent::Event10 => { - regs.inten().modify(|m| m.set_receive10(true)); - } - IpcEvent::Event11 => { - regs.inten().modify(|m| m.set_receive11(true)); - } - IpcEvent::Event12 => { - regs.inten().modify(|m| m.set_receive12(true)); - } - IpcEvent::Event13 => { - regs.inten().modify(|m| m.set_receive13(true)); - } - IpcEvent::Event14 => { - regs.inten().modify(|m| m.set_receive14(true)); - } - IpcEvent::Event15 => { - regs.inten().modify(|m| m.set_receive15(true)); - } + let _phantom = PhantomData; + #[rustfmt::skip] + let r = Self { // attributes on expressions are experimental + event0: Event { number: EventNumber::Event0, _phantom }, + event1: Event { number: EventNumber::Event1, _phantom }, + event2: Event { number: EventNumber::Event2, _phantom }, + event3: Event { number: EventNumber::Event3, _phantom }, + event4: Event { number: EventNumber::Event4, _phantom }, + event5: Event { number: EventNumber::Event5, _phantom }, + event6: Event { number: EventNumber::Event6, _phantom }, + event7: Event { number: EventNumber::Event7, _phantom }, + event8: Event { number: EventNumber::Event8, _phantom }, + event9: Event { number: EventNumber::Event9, _phantom }, + event10: Event { number: EventNumber::Event10, _phantom }, + event11: Event { number: EventNumber::Event11, _phantom }, + event12: Event { number: EventNumber::Event12, _phantom }, + event13: Event { number: EventNumber::Event13, _phantom }, + event14: Event { number: EventNumber::Event14, _phantom }, + event15: Event { number: EventNumber::Event15, _phantom }, }; + r + } +} +/// IPC event +pub struct Event<'d, T: Instance> { + number: EventNumber, + _phantom: PhantomData<&'d T>, +} + +impl<'d, T: Instance> Event<'d, T> { + /// Trigger the event. + pub fn trigger(&self) { + let nr = self.number; + T::regs().tasks_send(nr as usize).write_value(1); + } + + /// Wait for the event to be triggered. + pub async fn wait(&mut self) { + let regs = T::regs(); + let nr = self.number as usize; + regs.intenset().write(|w| w.0 = 1 << nr); poll_fn(|cx| { - IPC::state().waker_for(ev).register(cx.waker()); - - if regs.events_receive(ev as usize).read() & 0x01 == 0x01 { - regs.events_receive(ev as usize).write_value(0x00); + T::state().wakers[nr].register(cx.waker()); + if regs.events_receive(nr).read() == 1 { + regs.events_receive(nr).write_value(0x00); Poll::Ready(()) } else { Poll::Pending @@ -294,37 +232,102 @@ impl<'d, T: Instance> Ipc<'d, T> { }) .await; } + + /// Returns the [`EventNumber`] of the event. + pub fn number(&self) -> EventNumber { + self.number + } + + /// Create a handle that can trigger the event. + pub fn trigger_handle(&self) -> EventTrigger<'d, T> { + EventTrigger { + number: self.number, + _phantom: PhantomData, + } + } + + /// Configure the channels the event will broadcast to + pub fn configure_trigger>(&mut self, channels: I) { + T::regs().send_cnf(self.number as usize).write(|w| { + for channel in channels { + w.0 |= channel.mask(); + } + }) + } + + /// Configure the channels the event will listen on + pub fn configure_wait>(&mut self, channels: I) { + T::regs().receive_cnf(self.number as usize).write(|w| { + for channel in channels { + w.0 |= channel.mask(); + } + }); + } + + /// Get the task for the IPC event to use with PPI. + pub fn task(&self) -> ppi::Task<'d> { + let nr = self.number as usize; + let regs = T::regs(); + ppi::Task::from_reg(regs.tasks_send(nr)) + } + + /// Get the event for the IPC event to use with PPI. + pub fn event(&self) -> ppi::Event<'d> { + let nr = self.number as usize; + let regs = T::regs(); + ppi::Event::from_reg(regs.events_receive(nr)) + } + + /// Reborrow into a "child" Event. + /// + /// `self` will stay borrowed until the child Event is dropped. + pub fn reborrow(&mut self) -> Event<'_, T> { + Self { ..*self } + } + + /// Steal an IPC event by number. + /// + /// # Safety + /// + /// The event number must not be in use by another [`Event`]. + pub unsafe fn steal(number: EventNumber) -> Self { + Self { + number, + _phantom: PhantomData, + } + } +} + +/// A handle that can trigger an IPC event. +/// +/// This `struct` is returned by [`Event::trigger_handle`]. +#[derive(Debug, Copy, Clone)] +pub struct EventTrigger<'d, T: Instance> { + number: EventNumber, + _phantom: PhantomData<&'d T>, +} + +impl EventTrigger<'_, T> { + /// Trigger the event. + pub fn trigger(&self) { + let nr = self.number; + T::regs().tasks_send(nr as usize).write_value(1); + } + + /// Returns the [`EventNumber`] of the event. + pub fn number(&self) -> EventNumber { + self.number + } } pub(crate) struct State { - wakers: [AtomicWaker; 16], + wakers: [AtomicWaker; EVENT_COUNT], } impl State { pub(crate) const fn new() -> Self { - const WAKER: AtomicWaker = AtomicWaker::new(); - - Self { wakers: [WAKER; 16] } - } - - const fn waker_for(&self, ev: IpcEvent) -> &AtomicWaker { - match ev { - IpcEvent::Event0 => &self.wakers[0], - IpcEvent::Event1 => &self.wakers[1], - IpcEvent::Event2 => &self.wakers[2], - IpcEvent::Event3 => &self.wakers[3], - IpcEvent::Event4 => &self.wakers[4], - IpcEvent::Event5 => &self.wakers[5], - IpcEvent::Event6 => &self.wakers[6], - IpcEvent::Event7 => &self.wakers[7], - IpcEvent::Event8 => &self.wakers[8], - IpcEvent::Event9 => &self.wakers[9], - IpcEvent::Event10 => &self.wakers[10], - IpcEvent::Event11 => &self.wakers[11], - IpcEvent::Event12 => &self.wakers[12], - IpcEvent::Event13 => &self.wakers[13], - IpcEvent::Event14 => &self.wakers[14], - IpcEvent::Event15 => &self.wakers[15], + Self { + wakers: [const { AtomicWaker::new() }; EVENT_COUNT], } } } @@ -336,7 +339,7 @@ pub(crate) trait SealedInstance { /// IPC peripheral instance. #[allow(private_bounds)] -pub trait Instance: Peripheral

+ SealedInstance + 'static + Send { +pub trait Instance: PeripheralType + SealedInstance + 'static + Send { /// Interrupt for this peripheral. type Interrupt: interrupt::typelevel::Interrupt; } From 133500167ca53cfbc5e9268356753bc0e3f8c209 Mon Sep 17 00:00:00 2001 From: 1-rafael-1 Date: Mon, 12 May 2025 09:45:05 +0200 Subject: [PATCH 90/97] limit CoreVoltage eum to values up to 1.30V, because we do not support unlocking higher voltages --- embassy-rp/src/clocks.rs | 141 ++++++--------------------------------- 1 file changed, 21 insertions(+), 120 deletions(-) diff --git a/embassy-rp/src/clocks.rs b/embassy-rp/src/clocks.rs index ea5e9362b..bcd08c204 100644 --- a/embassy-rp/src/clocks.rs +++ b/embassy-rp/src/clocks.rs @@ -140,6 +140,10 @@ pub enum PeriClkSrc { /// /// The voltage regulator can be configured for different output voltages. /// Higher voltages allow for higher clock frequencies but increase power consumption and heat. +/// +/// **Note**: For RP235x the maximum voltage is 1.30V, unless unlocked by setting unless the voltage limit +/// is disabled using the disable_voltage_limit field in the vreg_ctrl register. For lack of practical use at this +/// point in time, this is not implemented here. So the maximum voltage in this enum is 1.30V for now. #[derive(Clone, Copy, Debug, PartialEq, Eq)] #[repr(u8)] pub enum CoreVoltage { @@ -227,54 +231,6 @@ pub enum CoreVoltage { #[cfg(feature = "_rp235x")] /// RP235x: 1.30V V1_30 = 0b01111, - #[cfg(feature = "_rp235x")] - /// RP235x: 1.35V - V1_35 = 0b10000, - #[cfg(feature = "_rp235x")] - /// RP235x: 1.40V - V1_40 = 0b10001, - #[cfg(feature = "_rp235x")] - /// RP235x: 1.50V - V1_50 = 0b10010, - #[cfg(feature = "_rp235x")] - /// RP235x: 1.60V - V1_60 = 0b10011, - #[cfg(feature = "_rp235x")] - /// RP235x: 1.65V - V1_65 = 0b10100, - #[cfg(feature = "_rp235x")] - /// RP235x: 1.70V - V1_70 = 0b10101, - #[cfg(feature = "_rp235x")] - /// RP235x: 1.80V - V1_80 = 0b10110, - #[cfg(feature = "_rp235x")] - /// RP235x: 1.90V - V1_90 = 0b10111, - #[cfg(feature = "_rp235x")] - /// RP235x: 2.00V - V2_00 = 0b11000, - #[cfg(feature = "_rp235x")] - /// RP235x: 2.35V - V2_35 = 0b11001, - #[cfg(feature = "_rp235x")] - /// RP235x: 2.50V - V2_50 = 0b11010, - #[cfg(feature = "_rp235x")] - /// RP235x: 2.65V - V2_65 = 0b11011, - #[cfg(feature = "_rp235x")] - /// RP235x: 2.80V - V2_80 = 0b11100, - #[cfg(feature = "_rp235x")] - /// RP235x: 3.00V - V3_00 = 0b11101, - #[cfg(feature = "_rp235x")] - /// RP235x: 3.15V - V3_15 = 0b11110, - #[cfg(feature = "_rp235x")] - /// RP235x: 3.30V - V3_30 = 0b11111, } impl CoreVoltage { @@ -313,13 +269,7 @@ impl CoreVoltage { CoreVoltage::V1_20 => 0b01010, // 0.903V (~75% of 1.20V) CoreVoltage::V1_25 => 0b01010, // 0.903V (~72% of 1.25V) CoreVoltage::V1_30 => 0b01011, // 0.946V (~73% of 1.30V) - CoreVoltage::V1_35 => 0b01011, // 0.946V (~70% of 1.35V) - CoreVoltage::V1_40 => 0b01100, // 0.989V (~71% of 1.40V) - CoreVoltage::V1_50 => 0b01101, // 1.032V (~69% of 1.50V) - CoreVoltage::V1_60 => 0b01110, // 1.075V (~67% of 1.60V) - CoreVoltage::V1_65 => 0b01110, // 1.075V (~65% of 1.65V) - CoreVoltage::V1_70 => 0b01111, // 1.118V (~66% of 1.70V) - _ => 0b10000, // the rp2350 datasheet repeats this value for all other core voltages + // all others: 0.946V (see CoreVoltage: we do not support setting Voltages higher than 1.30V at this point) } } } @@ -1083,45 +1033,17 @@ pub(crate) unsafe fn init(config: ClockConfig) { // If the target voltage is different from the current one, we need to change it if target_vsel != current_vsel { + // Set the voltage regulator to the target voltage #[cfg(feature = "rp2040")] - { - // Use modify() to preserve the HIZ and EN bits - otherwise we will disable the regulator when changing voltage - vreg.vreg().modify(|w| w.set_vsel(target_vsel)); - } + vreg.vreg().modify(|w| w.set_vsel(target_vsel)); #[cfg(feature = "_rp235x")] - { - // The rp235x has a different way of controlling the voltage regulator - // Changes to the voltage regulator are protected by a password, see datasheet section 6.4 - // The password is "5AFE" (0x5AFE), it must be set in the top 16 bits of the register - - // The rp235x by default locks the voltage regulator control, so we need to unlock it first - // See datasheet section 6.3.2. Software Control - vreg.vreg_ctrl().modify(|w| { - // Add password to top 16 bits, preserving the rest, repeat below for other registers - w.0 = (w.0 & 0x0000FFFF) | (0x5AFE << 16); - w.set_unlock(true); - *w - }); - - // Set the voltage - vreg.vreg().modify(|w| { - w.0 = (w.0 & 0x0000FFFF) | (0x5AFE << 16); - w.set_vsel(target_vsel); - *w - }); - - // The rp235x has two more registers to set the voltage for low power mode - vreg.vreg_lp_entry().modify(|w| { - w.0 = (w.0 & 0x0000FFFF) | (0x5AFE << 16); - w.set_vsel(target_vsel); - *w - }); - vreg.vreg_lp_exit().modify(|w| { - w.0 = (w.0 & 0x0000FFFF) | (0x5AFE << 16); - w.set_vsel(target_vsel); - *w - }); - } + // For rp235x changes to the voltage regulator are protected by a password, see datasheet section 6.4 Power Management (POWMAN) Registers + // The password is "5AFE" (0x5AFE), it must be set in the top 16 bits of the register + vreg.vreg().modify(|w| { + w.0 = (w.0 & 0x0000FFFF) | (0x5AFE << 16); // Set the password + w.set_vsel(target_vsel); + *w + }); // Wait for the voltage to stabilize. Use the provided delay or default based on voltage let settling_time_us = config.voltage_stabilization_delay_us.unwrap_or_else(|| { @@ -1140,23 +1062,17 @@ pub(crate) unsafe fn init(config: ClockConfig) { } // Only now set the BOD level. At this point the voltage is considered stable. + #[cfg(feature = "rp2040")] vreg.bod().write(|w| { w.set_vsel(voltage.recommended_bod()); w.set_en(true); // Enable brownout detection }); - #[cfg(feature = "_rp235x")] - { - // The rp235x has a separate register for the BOD level in low power mode - vreg.bod_lp_entry().write(|w| { - w.set_vsel(voltage.recommended_bod()); - w.set_en(true); // Enable brownout detection - }); - vreg.bod_lp_exit().write(|w| { - w.set_vsel(voltage.recommended_bod()); - w.set_en(true); // Enable brownout detection - }); - } + vreg.bod().write(|w| { + w.0 = (w.0 & 0x0000FFFF) | (0x5AFE << 16); // Set the password + w.set_vsel(voltage.recommended_bod()); + w.set_en(true); // Enable brownout detection + }); } } @@ -1527,23 +1443,8 @@ pub fn core_voltage() -> Result { 0b01101 => Ok(CoreVoltage::V1_20), 0b01110 => Ok(CoreVoltage::V1_25), 0b01111 => Ok(CoreVoltage::V1_30), - 0b10000 => Ok(CoreVoltage::V1_35), - 0b10001 => Ok(CoreVoltage::V1_40), - 0b10010 => Ok(CoreVoltage::V1_50), - 0b10011 => Ok(CoreVoltage::V1_60), - 0b10100 => Ok(CoreVoltage::V1_65), - 0b10101 => Ok(CoreVoltage::V1_70), - 0b10110 => Ok(CoreVoltage::V1_80), - 0b10111 => Ok(CoreVoltage::V1_90), - 0b11000 => Ok(CoreVoltage::V2_00), - 0b11001 => Ok(CoreVoltage::V2_35), - 0b11010 => Ok(CoreVoltage::V2_50), - 0b11011 => Ok(CoreVoltage::V2_65), - 0b11100 => Ok(CoreVoltage::V2_80), - 0b11101 => Ok(CoreVoltage::V3_00), - 0b11110 => Ok(CoreVoltage::V3_15), - 0b11111 => Ok(CoreVoltage::V3_30), _ => Err("Unexpected value in register"), + // see CoreVoltage: we do not support setting Voltages higher than 1.30V at this point } } } From 79e452922a6b467f2e8547a6b28698ed5f409705 Mon Sep 17 00:00:00 2001 From: 1-rafael-1 Date: Mon, 12 May 2025 21:33:47 +0200 Subject: [PATCH 91/97] Add ClockError enum and update system_freq to return Result for error handling --- embassy-rp/src/clocks.rs | 66 ++++++++++++++++++++-------- examples/rp/src/bin/overclock.rs | 2 +- examples/rp235x/src/bin/overclock.rs | 2 +- tests/rp/src/bin/overclock.rs | 2 +- 4 files changed, 50 insertions(+), 22 deletions(-) diff --git a/embassy-rp/src/clocks.rs b/embassy-rp/src/clocks.rs index bcd08c204..5872ef789 100644 --- a/embassy-rp/src/clocks.rs +++ b/embassy-rp/src/clocks.rs @@ -82,6 +82,18 @@ use crate::{pac, reset, Peri}; // be very useful until we have runtime clock reconfiguration. once this // happens we can resurrect the commented-out gpin bits. +/// Clock error types. +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] +pub enum ClockError { + /// PLL failed to lock within the timeout period. + PllLockTimedOut, + /// Could not find valid PLL parameters for system clock. + InvalidPllParameters, + /// Reading the core voltage failed due to an unexpected value in the register. + UnexpectedCoreVoltageRead, +} + struct Clocks { xosc: AtomicU32, sys: AtomicU32, @@ -144,8 +156,9 @@ pub enum PeriClkSrc { /// **Note**: For RP235x the maximum voltage is 1.30V, unless unlocked by setting unless the voltage limit /// is disabled using the disable_voltage_limit field in the vreg_ctrl register. For lack of practical use at this /// point in time, this is not implemented here. So the maximum voltage in this enum is 1.30V for now. -#[derive(Clone, Copy, Debug, PartialEq, Eq)] #[repr(u8)] +#[derive(Clone, Copy, Debug, PartialEq, Eq)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] pub enum CoreVoltage { // RP2040 voltage levels #[cfg(feature = "rp2040")] @@ -468,7 +481,7 @@ impl ClockConfig { /// # Returns /// /// A ClockConfig configured to achieve the requested system frequency using the - /// the usual 12Mhz crystal, or panic if no valid parameters can be found. + /// the usual 12Mhz crystal, or an error if no valid parameters can be found. /// /// # Note on core voltage: /// @@ -482,7 +495,11 @@ impl ClockConfig { /// **For RP235x**: /// At this point in time there is no official manufacturer endorsement for running the chip on other core voltages and/or other clock speeds than the defaults. /// Using this function is experimental and may not work as expected or even damage the chip. - pub fn system_freq(hz: u32) -> Self { + /// + /// # Returns + /// + /// A Result containing either the configured ClockConfig or a ClockError. + pub fn system_freq(hz: u32) -> Result { // Start with the standard configuration from crystal() const DEFAULT_CRYSTAL_HZ: u32 = 12_000_000; let mut config = Self::crystal(DEFAULT_CRYSTAL_HZ); @@ -491,16 +508,15 @@ impl ClockConfig { // (which is what crystal() configures by default) #[cfg(feature = "rp2040")] if hz == 125_000_000 { - return config; + return Ok(config); } #[cfg(feature = "_rp235x")] if hz == 150_000_000 { - return config; + return Ok(config); } // Find optimal PLL parameters for the requested frequency - let sys_pll_params = find_pll_params(DEFAULT_CRYSTAL_HZ, hz) - .unwrap_or_else(|| panic!("Could not find valid PLL parameters for system clock")); + let sys_pll_params = find_pll_params(DEFAULT_CRYSTAL_HZ, hz).ok_or(ClockError::InvalidPllParameters)?; // Replace the sys_pll configuration with our custom parameters if let Some(xosc) = &mut config.xosc { @@ -525,7 +541,7 @@ impl ClockConfig { }; } - config + Ok(config) } /// Configure with manual PLL settings for full control over system clock @@ -620,6 +636,7 @@ impl ClockConfig { #[repr(u16)] #[non_exhaustive] #[derive(Clone, Copy, Debug, PartialEq, Eq)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] pub enum RoscRange { /// Low range. Low = pac::rosc::vals::FreqRange::LOW.0, @@ -726,6 +743,7 @@ pub struct RefClkConfig { /// Reference clock source. #[non_exhaustive] #[derive(Clone, Copy, Debug, PartialEq, Eq)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] pub enum RefClkSrc { /// XOSC. Xosc, @@ -741,6 +759,7 @@ pub enum RefClkSrc { /// SYS clock source. #[non_exhaustive] #[derive(Clone, Copy, Debug, PartialEq, Eq)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] pub enum SysClkSrc { /// REF. Ref, @@ -779,6 +798,7 @@ pub struct SysClkConfig { #[repr(u8)] #[non_exhaustive] #[derive(Clone, Copy, Debug, PartialEq, Eq)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] pub enum UsbClkSrc { /// PLL USB. PllUsb = ClkUsbCtrlAuxsrc::CLKSRC_PLL_USB as _, @@ -807,6 +827,7 @@ pub struct UsbClkConfig { #[repr(u8)] #[non_exhaustive] #[derive(Clone, Copy, Debug, PartialEq, Eq)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] pub enum AdcClkSrc { /// PLL USB. PllUsb = ClkAdcCtrlAuxsrc::CLKSRC_PLL_USB as _, @@ -835,6 +856,7 @@ pub struct AdcClkConfig { #[repr(u8)] #[non_exhaustive] #[derive(Clone, Copy, Debug, PartialEq, Eq)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] #[cfg(feature = "rp2040")] pub enum RtcClkSrc { /// PLL USB. @@ -1084,14 +1106,20 @@ pub(crate) unsafe fn init(config: ClockConfig) { let pll_sys_freq = match config.sys_pll { Some(sys_pll_config) => match configure_pll(pac::PLL_SYS, config.hz, sys_pll_config) { Ok(freq) => freq, + #[cfg(feature = "defmt")] Err(e) => panic!("Failed to configure PLL_SYS: {}", e), + #[cfg(not(feature = "defmt"))] + Err(_e) => panic!("Failed to configure PLL_SYS"), }, None => 0, }; let pll_usb_freq = match config.usb_pll { Some(usb_pll_config) => match configure_pll(pac::PLL_USB, config.hz, usb_pll_config) { Ok(freq) => freq, + #[cfg(feature = "defmt")] Err(e) => panic!("Failed to configure PLL_USB: {}", e), + #[cfg(not(feature = "defmt"))] + Err(_e) => panic!("Failed to configure PLL_USB"), }, None => 0, }; @@ -1401,7 +1429,7 @@ pub fn clk_rtc_freq() -> u16 { /// /// Returns the current core voltage or an error if the voltage register /// contains an unknown value. -pub fn core_voltage() -> Result { +pub fn core_voltage() -> Result { #[cfg(feature = "rp2040")] { let vreg = pac::VREG_AND_CHIP_RESET; @@ -1418,7 +1446,7 @@ pub fn core_voltage() -> Result { 0b1101 => Ok(CoreVoltage::V1_20), 0b1110 => Ok(CoreVoltage::V1_25), 0b1111 => Ok(CoreVoltage::V1_30), - _ => Err("Unexpected value in register"), + _ => Err(ClockError::UnexpectedCoreVoltageRead), } } @@ -1443,7 +1471,7 @@ pub fn core_voltage() -> Result { 0b01101 => Ok(CoreVoltage::V1_20), 0b01110 => Ok(CoreVoltage::V1_25), 0b01111 => Ok(CoreVoltage::V1_30), - _ => Err("Unexpected value in register"), + _ => Err(ClockError::UnexpectedCoreVoltageRead), // see CoreVoltage: we do not support setting Voltages higher than 1.30V at this point } } @@ -1461,7 +1489,7 @@ fn start_xosc(crystal_hz: u32, delay_multiplier: u32) { /// PLL (Phase-Locked Loop) configuration #[inline(always)] -fn configure_pll(p: pac::pll::Pll, input_freq: u32, config: PllConfig) -> Result { +fn configure_pll(p: pac::pll::Pll, input_freq: u32, config: PllConfig) -> Result { // Calculate reference frequency let ref_freq = input_freq / config.refdiv as u32; @@ -1532,7 +1560,7 @@ fn configure_pll(p: pac::pll::Pll, input_freq: u32, config: PllConfig) -> Result timeout -= 1; if timeout == 0 { // PLL failed to lock, return 0 to indicate failure - return Err("PLL failed to lock"); + return Err(ClockError::PllLockTimedOut); } } @@ -2103,21 +2131,21 @@ mod tests { { // Test automatic voltage scaling based on frequency // Under 133 MHz should use default voltage (V1_10) - let config = ClockConfig::system_freq(125_000_000); + let config = ClockConfig::system_freq(125_000_000).unwrap(); assert_eq!(config.core_voltage, CoreVoltage::V1_10); // 133-200 MHz should use V1_15 - let config = ClockConfig::system_freq(150_000_000); + let config = ClockConfig::system_freq(150_000_000).unwrap(); assert_eq!(config.core_voltage, CoreVoltage::V1_15); - let config = ClockConfig::system_freq(200_000_000); + let config = ClockConfig::system_freq(200_000_000).unwrap(); assert_eq!(config.core_voltage, CoreVoltage::V1_15); - // Above 200 MHz should use V1_25 - let config = ClockConfig::system_freq(250_000_000); + // Above 200 MHz should use V1_15 + let config = ClockConfig::system_freq(250_000_000).unwrap(); assert_eq!(config.core_voltage, CoreVoltage::V1_15); // Below 125 MHz should use V1_10 - let config = ClockConfig::system_freq(100_000_000); + let config = ClockConfig::system_freq(100_000_000).unwrap(); assert_eq!(config.core_voltage, CoreVoltage::V1_10); } } diff --git a/examples/rp/src/bin/overclock.rs b/examples/rp/src/bin/overclock.rs index 89147ba42..2706399af 100644 --- a/examples/rp/src/bin/overclock.rs +++ b/examples/rp/src/bin/overclock.rs @@ -18,7 +18,7 @@ const COUNT_TO: i64 = 10_000_000; #[embassy_executor::main] async fn main(_spawner: Spawner) -> ! { // Set up for clock frequency of 200 MHz, setting all necessary defaults. - let config = Config::new(ClockConfig::system_freq(200_000_000)); + let config = Config::new(ClockConfig::system_freq(200_000_000).unwrap()); // Initialize the peripherals let p = embassy_rp::init(config); diff --git a/examples/rp235x/src/bin/overclock.rs b/examples/rp235x/src/bin/overclock.rs index 8713df688..178fd62ca 100644 --- a/examples/rp235x/src/bin/overclock.rs +++ b/examples/rp235x/src/bin/overclock.rs @@ -23,7 +23,7 @@ const COUNT_TO: i64 = 10_000_000; #[embassy_executor::main] async fn main(_spawner: Spawner) -> ! { // Set up for clock frequency of 200 MHz, setting all necessary defaults. - let mut config = Config::new(ClockConfig::system_freq(200_000_000)); + let mut config = Config::new(ClockConfig::system_freq(200_000_000).unwrap()); // since for the rp235x there is no official support for higher clock frequencies, `system_freq()` will not set a voltage for us. // We need to guess the core voltage, that is needed for the higher clock frequency. Going with a small increase from the default 1.1V here, based on diff --git a/tests/rp/src/bin/overclock.rs b/tests/rp/src/bin/overclock.rs index a568d7fed..167a26eb2 100644 --- a/tests/rp/src/bin/overclock.rs +++ b/tests/rp/src/bin/overclock.rs @@ -20,7 +20,7 @@ async fn main(_spawner: Spawner) { let mut config = Config::default(); // Initialize with 200MHz clock configuration - config.clocks = ClockConfig::system_freq(200_000_000); + config.clocks = ClockConfig::system_freq(200_000_000).unwrap(); // if we are rp235x, we need to manually set the core voltage. rp2040 should do this automatically #[cfg(feature = "rp235xb")] From be1b679d48b5d781f888fb97c1fed7479235019b Mon Sep 17 00:00:00 2001 From: 1-rafael-1 Date: Mon, 12 May 2025 21:42:03 +0200 Subject: [PATCH 92/97] Refactor CoreVoltage enum, separate for rp2040 and rp235x --- embassy-rp/src/clocks.rs | 98 +++++++++++++++++----------------------- 1 file changed, 42 insertions(+), 56 deletions(-) diff --git a/embassy-rp/src/clocks.rs b/embassy-rp/src/clocks.rs index 5872ef789..3975e7e79 100644 --- a/embassy-rp/src/clocks.rs +++ b/embassy-rp/src/clocks.rs @@ -156,93 +156,79 @@ pub enum PeriClkSrc { /// **Note**: For RP235x the maximum voltage is 1.30V, unless unlocked by setting unless the voltage limit /// is disabled using the disable_voltage_limit field in the vreg_ctrl register. For lack of practical use at this /// point in time, this is not implemented here. So the maximum voltage in this enum is 1.30V for now. +#[cfg(feature = "rp2040")] #[repr(u8)] #[derive(Clone, Copy, Debug, PartialEq, Eq)] #[cfg_attr(feature = "defmt", derive(defmt::Format))] pub enum CoreVoltage { - // RP2040 voltage levels - #[cfg(feature = "rp2040")] - /// RP2040: 0.80V + /// 0.80V V0_80 = 0b0000, - #[cfg(feature = "rp2040")] - /// RP2040: 0.85V + /// 0.85V V0_85 = 0b0110, - #[cfg(feature = "rp2040")] - /// RP2040: 0.90V + /// 0.90V V0_90 = 0b0111, - #[cfg(feature = "rp2040")] - /// RP2040: 0.95V + /// 0.95V V0_95 = 0b1000, - #[cfg(feature = "rp2040")] - /// RP2040: 1.00V + /// 1.00V V1_00 = 0b1001, - #[cfg(feature = "rp2040")] - /// RP2040: 1.05V + /// 1.05V V1_05 = 0b1010, - #[cfg(feature = "rp2040")] - /// RP2040: 1.10V - Default voltage level + /// 1.10V - Default voltage level V1_10 = 0b1011, - #[cfg(feature = "rp2040")] - /// RP2040: 1.15V - Required for overclocking to 133-200MHz + /// 1.15V - Required for overclocking to 133-200MHz V1_15 = 0b1100, - #[cfg(feature = "rp2040")] - /// RP2040: 1.20V + /// 1.20V V1_20 = 0b1101, - #[cfg(feature = "rp2040")] - /// RP2040: 1.25V + /// 1.25V V1_25 = 0b1110, - #[cfg(feature = "rp2040")] - /// RP2040: 1.30V + /// 1.30V V1_30 = 0b1111, +} - // RP235x voltage levels - #[cfg(feature = "_rp235x")] - /// RP235x: 0.55V +/// Core voltage regulator settings. +/// +/// The voltage regulator can be configured for different output voltages. +/// Higher voltages allow for higher clock frequencies but increase power consumption and heat. +/// +/// **Note**: For RP235x the maximum voltage is 1.30V, unless unlocked by setting unless the voltage limit +/// is disabled using the disable_voltage_limit field in the vreg_ctrl register. For lack of practical use at this +/// point in time, this is not implemented here. So the maximum voltage in this enum is 1.30V for now. +#[cfg(feature = "_rp235x")] +#[repr(u8)] +#[derive(Clone, Copy, Debug, PartialEq, Eq)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] +pub enum CoreVoltage { + /// 0.55V V0_55 = 0b00000, - #[cfg(feature = "_rp235x")] - /// RP235x: 0.60V + /// 0.60V V0_60 = 0b00001, - #[cfg(feature = "_rp235x")] - /// RP235x: 0.65V + /// 0.65V V0_65 = 0b00010, - #[cfg(feature = "_rp235x")] - /// RP235x: 0.70V + /// 0.70V V0_70 = 0b00011, - #[cfg(feature = "_rp235x")] - /// RP235x: 0.75V + /// 0.75V V0_75 = 0b00100, - #[cfg(feature = "_rp235x")] - /// RP235x: 0.80V + /// 0.80V V0_80 = 0b00101, - #[cfg(feature = "_rp235x")] - /// RP235x: 0.85V + /// 0.85V V0_85 = 0b00110, - #[cfg(feature = "_rp235x")] - /// RP235x: 0.90V + /// 0.90V V0_90 = 0b00111, - #[cfg(feature = "_rp235x")] - /// RP235x: 0.95V + /// 0.95V V0_95 = 0b01000, - #[cfg(feature = "_rp235x")] - /// RP235x: 1.00V + /// 1.00V V1_00 = 0b01001, - #[cfg(feature = "_rp235x")] - /// RP235x: 1.05V + /// 1.05V V1_05 = 0b01010, - #[cfg(feature = "_rp235x")] - /// RP235x: 1.10V - Default voltage level + /// 1.10V - Default voltage level V1_10 = 0b01011, - #[cfg(feature = "_rp235x")] - /// RP235x: 1.15V + /// 1.15V V1_15 = 0b01100, - #[cfg(feature = "_rp235x")] - /// RP235x: 1.20V + /// 1.20V V1_20 = 0b01101, - #[cfg(feature = "_rp235x")] - /// RP235x: 1.25V + /// 1.25V V1_25 = 0b01110, - #[cfg(feature = "_rp235x")] - /// RP235x: 1.30V + /// 1.30V V1_30 = 0b01111, } From abafbed0d5fba70ab5d0096b9d381577d2f880c8 Mon Sep 17 00:00:00 2001 From: 1-rafael-1 Date: Mon, 12 May 2025 21:43:17 +0200 Subject: [PATCH 93/97] remove Debug2Fmt from examples --- examples/rp/src/bin/overclock.rs | 2 +- examples/rp/src/bin/overclock_manual.rs | 2 +- examples/rp235x/src/bin/overclock.rs | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/examples/rp/src/bin/overclock.rs b/examples/rp/src/bin/overclock.rs index 2706399af..83b17308b 100644 --- a/examples/rp/src/bin/overclock.rs +++ b/examples/rp/src/bin/overclock.rs @@ -28,7 +28,7 @@ async fn main(_spawner: Spawner) -> ! { info!("System clock frequency: {} MHz", sys_freq / 1_000_000); // Show core voltage for verification let core_voltage = core_voltage().unwrap(); - info!("Core voltage: {}", Debug2Format(&core_voltage)); + info!("Core voltage: {}", core_voltage); // LED to indicate the system is running let mut led = Output::new(p.PIN_25, Level::Low); diff --git a/examples/rp/src/bin/overclock_manual.rs b/examples/rp/src/bin/overclock_manual.rs index 88ef26a7a..dea5cfb3c 100644 --- a/examples/rp/src/bin/overclock_manual.rs +++ b/examples/rp/src/bin/overclock_manual.rs @@ -45,7 +45,7 @@ async fn main(_spawner: Spawner) -> ! { info!("System clock frequency: {} MHz", sys_freq / 1_000_000); // Show core voltage for verification let core_voltage = core_voltage().unwrap(); - info!("Core voltage: {}", Debug2Format(&core_voltage)); + info!("Core voltage: {}", core_voltage); // LED to indicate the system is running let mut led = Output::new(p.PIN_25, Level::Low); diff --git a/examples/rp235x/src/bin/overclock.rs b/examples/rp235x/src/bin/overclock.rs index 178fd62ca..5fd97ef97 100644 --- a/examples/rp235x/src/bin/overclock.rs +++ b/examples/rp235x/src/bin/overclock.rs @@ -38,7 +38,7 @@ async fn main(_spawner: Spawner) -> ! { info!("System clock frequency: {} MHz", sys_freq / 1_000_000); // Show core voltage for verification let core_voltage = core_voltage().unwrap(); - info!("Core voltage: {}", Debug2Format(&core_voltage)); + info!("Core voltage: {}", core_voltage); // LED to indicate the system is running let mut led = Output::new(p.PIN_25, Level::Low); From 1314808b3a39d856d33655504db00d799914c440 Mon Sep 17 00:00:00 2001 From: 1-rafael-1 Date: Tue, 13 May 2025 10:49:23 +0200 Subject: [PATCH 94/97] Changes after review: copypasted doc comment fixed and no cfg gates to panic on failing pll config in init() --- embassy-rp/src/clocks.rs | 17 +++-------------- 1 file changed, 3 insertions(+), 14 deletions(-) diff --git a/embassy-rp/src/clocks.rs b/embassy-rp/src/clocks.rs index 3975e7e79..47e71c448 100644 --- a/embassy-rp/src/clocks.rs +++ b/embassy-rp/src/clocks.rs @@ -152,11 +152,6 @@ pub enum PeriClkSrc { /// /// The voltage regulator can be configured for different output voltages. /// Higher voltages allow for higher clock frequencies but increase power consumption and heat. -/// -/// **Note**: For RP235x the maximum voltage is 1.30V, unless unlocked by setting unless the voltage limit -/// is disabled using the disable_voltage_limit field in the vreg_ctrl register. For lack of practical use at this -/// point in time, this is not implemented here. So the maximum voltage in this enum is 1.30V for now. -#[cfg(feature = "rp2040")] #[repr(u8)] #[derive(Clone, Copy, Debug, PartialEq, Eq)] #[cfg_attr(feature = "defmt", derive(defmt::Format))] @@ -190,7 +185,7 @@ pub enum CoreVoltage { /// The voltage regulator can be configured for different output voltages. /// Higher voltages allow for higher clock frequencies but increase power consumption and heat. /// -/// **Note**: For RP235x the maximum voltage is 1.30V, unless unlocked by setting unless the voltage limit +/// **Note**: The maximum voltage is 1.30V, unless unlocked by setting unless the voltage limit /// is disabled using the disable_voltage_limit field in the vreg_ctrl register. For lack of practical use at this /// point in time, this is not implemented here. So the maximum voltage in this enum is 1.30V for now. #[cfg(feature = "_rp235x")] @@ -1092,20 +1087,14 @@ pub(crate) unsafe fn init(config: ClockConfig) { let pll_sys_freq = match config.sys_pll { Some(sys_pll_config) => match configure_pll(pac::PLL_SYS, config.hz, sys_pll_config) { Ok(freq) => freq, - #[cfg(feature = "defmt")] - Err(e) => panic!("Failed to configure PLL_SYS: {}", e), - #[cfg(not(feature = "defmt"))] - Err(_e) => panic!("Failed to configure PLL_SYS"), + Err(e) => panic!("Failed to configure PLL_SYS: {:?}", e), }, None => 0, }; let pll_usb_freq = match config.usb_pll { Some(usb_pll_config) => match configure_pll(pac::PLL_USB, config.hz, usb_pll_config) { Ok(freq) => freq, - #[cfg(feature = "defmt")] - Err(e) => panic!("Failed to configure PLL_USB: {}", e), - #[cfg(not(feature = "defmt"))] - Err(_e) => panic!("Failed to configure PLL_USB"), + Err(e) => panic!("Failed to configure PLL_USB: {:?}", e), }, None => 0, }; From 981ef20f83ec88601818d8c55f69a1037d57b0cb Mon Sep 17 00:00:00 2001 From: 1-rafael-1 Date: Tue, 13 May 2025 10:59:11 +0200 Subject: [PATCH 95/97] removed one line too many --- embassy-rp/src/clocks.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/embassy-rp/src/clocks.rs b/embassy-rp/src/clocks.rs index 47e71c448..857877680 100644 --- a/embassy-rp/src/clocks.rs +++ b/embassy-rp/src/clocks.rs @@ -152,6 +152,7 @@ pub enum PeriClkSrc { /// /// The voltage regulator can be configured for different output voltages. /// Higher voltages allow for higher clock frequencies but increase power consumption and heat. +#[cfg(feature = "rp2040")] #[repr(u8)] #[derive(Clone, Copy, Debug, PartialEq, Eq)] #[cfg_attr(feature = "defmt", derive(defmt::Format))] From 8bb25a551abbf3677a440d6f73401b88cf4de57f Mon Sep 17 00:00:00 2001 From: i509VCB Date: Tue, 13 May 2025 22:53:16 -0500 Subject: [PATCH 96/97] ci: build std examples on aarch64-unknown-linux-gnu --- ci.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ci.sh b/ci.sh index 6e320e4d1..f2d461c1a 100755 --- a/ci.sh +++ b/ci.sh @@ -19,7 +19,7 @@ fi TARGET=$(rustc -vV | sed -n 's|host: ||p') BUILD_EXTRA="" -if [ $TARGET = "x86_64-unknown-linux-gnu" ]; then +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 From be20c708fa7b099507c2027a8c5666b54f1e5723 Mon Sep 17 00:00:00 2001 From: jake-taf <149392739+jake-taf@users.noreply.github.com> Date: Wed, 14 May 2025 09:45:20 -0400 Subject: [PATCH 97/97] Interrupt Doc Comments Support adding doc comments to interrupts --- embassy-stm32/src/lib.rs | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/embassy-stm32/src/lib.rs b/embassy-stm32/src/lib.rs index 3e84d3386..f8d09413d 100644 --- a/embassy-stm32/src/lib.rs +++ b/embassy-stm32/src/lib.rs @@ -173,8 +173,9 @@ pub use crate::_generated::interrupt; // developer note: this macro can't be in `embassy-hal-internal` due to the use of `$crate`. #[macro_export] macro_rules! bind_interrupts { - ($vis:vis struct $name:ident { + ($(#[$outer:meta])* $vis:vis struct $name:ident { $( + $(#[$inner:meta])* $(#[cfg($cond_irq:meta)])? $irq:ident => $( $(#[cfg($cond_handler:meta)])? @@ -183,12 +184,14 @@ macro_rules! bind_interrupts { )* }) => { #[derive(Copy, Clone)] + $(#[$outer])* $vis struct $name; $( #[allow(non_snake_case)] #[no_mangle] $(#[cfg($cond_irq)])? + $(#[$inner])* unsafe extern "C" fn $irq() { $( $(#[cfg($cond_handler)])?