diff --git a/CHANGELOG.md b/CHANGELOG.md index 32ee7036a..e0917c07f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -21,6 +21,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Add `ClockControl::max` helper for all chips (#701) - Added module-level documentation for all peripherals - Added module-level documentation for all peripherals (#680) +- Implement sleep with some wakeup methods for `esp32-s3` (#660) +- Add `FlashSafeDma` wrapper for eh traits which ensure correct DMA transfer from source data in flash (ROM) (#678) ### Changed diff --git a/esp-hal-common/src/dma/mod.rs b/esp-hal-common/src/dma/mod.rs index f24924937..20b42ab3c 100644 --- a/esp-hal-common/src/dma/mod.rs +++ b/esp-hal-common/src/dma/mod.rs @@ -83,6 +83,10 @@ pub enum DmaError { InvalidAlignment, OutOfDescriptors, InvalidDescriptorSize, + /// DescriptorError the DMA rejected the descriptor configuration. This + /// could be because the source address of the data is not in RAM. Ensure + /// your source data is in a valid address space, or try using + /// [`crate::FlashSafeDma`] wrapper. DescriptorError, Overflow, Exhausted, diff --git a/esp-hal-common/src/lib.rs b/esp-hal-common/src/lib.rs index 53cafba92..46aad4981 100644 --- a/esp-hal-common/src/lib.rs +++ b/esp-hal-common/src/lib.rs @@ -302,3 +302,53 @@ mod critical_section_impl { } } } + +/// FlashSafeDma +/// +/// The embedded-hal traits make no guarentees about +/// where the buffers are placed. The DMA implementation in Espressif chips has +/// a limitation in that it can only access the RAM address space, meaning data +/// to be transmitted from the flash address space must be copied into RAM +/// first. +/// +/// This wrapper struct should be used when a peripheral using the DMA engine +/// needs to transmit data from flash (ROM) via the embedded-hal traits. This is +/// often a `const` variable. +/// +/// Example usage using [`spi::dma::SpiDma`] +/// ```no_run +/// const ARRAY_IN_FLASH = [0xAA; 128] +/// +/// let spi = SpiDma::new(/* */); +/// +/// spi.write(&ARRAY_IN_FLASH[..]).unwrap(); // error when transmission starts +/// +/// let spi = FlashSafeDma::new(spi); +/// +/// spi.write(&ARRAY_IN_FLASH[..]).unwrap(); // success +/// ``` +pub struct FlashSafeDma { + inner: T, + buffer: [u8; SIZE], +} + +impl FlashSafeDma { + pub fn new(inner: T) -> Self { + Self { + inner, + buffer: [0u8; SIZE], + } + } + + pub fn inner_mut(&mut self) -> &mut T { + &mut self.inner + } + + pub fn inner(&self) -> &T { + &self.inner + } + + pub fn free(self) -> T { + self.inner + } +} diff --git a/esp-hal-common/src/soc/esp32/mod.rs b/esp-hal-common/src/soc/esp32/mod.rs index 6a7aa2a13..9d5c1078d 100644 --- a/esp-hal-common/src/soc/esp32/mod.rs +++ b/esp-hal-common/src/soc/esp32/mod.rs @@ -19,4 +19,7 @@ pub(crate) mod constants { pub const RMT_RAM_START: usize = 0x3ff56800; pub const RMT_CHANNEL_RAM_SIZE: usize = 64; + + pub const SOC_DRAM_LOW: u32 = 0x3FFA_E000; + pub const SOC_DRAM_HIGH: u32 = 0x4000_0000; } diff --git a/esp-hal-common/src/soc/esp32c2/mod.rs b/esp-hal-common/src/soc/esp32c2/mod.rs index 2eb7733d0..bec36080a 100644 --- a/esp-hal-common/src/soc/esp32c2/mod.rs +++ b/esp-hal-common/src/soc/esp32c2/mod.rs @@ -13,3 +13,8 @@ pub mod radio_clocks; pub(crate) mod registers { pub const INTERRUPT_MAP_BASE: u32 = 0x600c2000; } + +pub(crate) mod constants { + pub const SOC_DRAM_LOW: u32 = 0x3FCA_0000; + pub const SOC_DRAM_HIGH: u32 = 0x3FCE_0000; +} diff --git a/esp-hal-common/src/soc/esp32c3/mod.rs b/esp-hal-common/src/soc/esp32c3/mod.rs index 271658b61..89da41c7f 100644 --- a/esp-hal-common/src/soc/esp32c3/mod.rs +++ b/esp-hal-common/src/soc/esp32c3/mod.rs @@ -26,4 +26,7 @@ pub(crate) mod constants { pub const RMT_CHANNEL_RAM_SIZE: usize = 48; pub const RMT_CLOCK_SRC: u8 = 1; pub const RMT_CLOCK_SRC_FREQ: fugit::HertzU32 = fugit::HertzU32::MHz(80); + + pub const SOC_DRAM_LOW: u32 = 0x3FC8_0000; + pub const SOC_DRAM_HIGH: u32 = 0x3FCE_0000; } diff --git a/esp-hal-common/src/soc/esp32c6/mod.rs b/esp-hal-common/src/soc/esp32c6/mod.rs index 48aa712d5..94e08b82b 100644 --- a/esp-hal-common/src/soc/esp32c6/mod.rs +++ b/esp-hal-common/src/soc/esp32c6/mod.rs @@ -30,4 +30,7 @@ pub(crate) mod constants { pub const RMT_CHANNEL_RAM_SIZE: usize = 48; pub const RMT_CLOCK_SRC: u8 = 1; pub const RMT_CLOCK_SRC_FREQ: fugit::HertzU32 = fugit::HertzU32::MHz(80); + + pub const SOC_DRAM_LOW: u32 = 0x4080_0000; + pub const SOC_DRAM_HIGH: u32 = 0x4088_0000; } diff --git a/esp-hal-common/src/soc/esp32h2/mod.rs b/esp-hal-common/src/soc/esp32h2/mod.rs index 4213bd1c7..f832d29c1 100644 --- a/esp-hal-common/src/soc/esp32h2/mod.rs +++ b/esp-hal-common/src/soc/esp32h2/mod.rs @@ -29,4 +29,7 @@ pub(crate) mod constants { pub const RMT_CHANNEL_RAM_SIZE: usize = 48; pub const RMT_CLOCK_SRC: bool = false; pub const RMT_CLOCK_SRC_FREQ: fugit::HertzU32 = fugit::HertzU32::MHz(32); + + pub const SOC_DRAM_LOW: u32 = 0x4080_0000; + pub const SOC_DRAM_HIGH: u32 = 0x4085_0000; } diff --git a/esp-hal-common/src/soc/esp32s2/mod.rs b/esp-hal-common/src/soc/esp32s2/mod.rs index 2dd40a360..2534fb63a 100644 --- a/esp-hal-common/src/soc/esp32s2/mod.rs +++ b/esp-hal-common/src/soc/esp32s2/mod.rs @@ -22,4 +22,7 @@ pub(crate) mod constants { pub const RMT_RAM_START: usize = 0x3f416400; pub const RMT_CHANNEL_RAM_SIZE: usize = 64; + + pub const SOC_DRAM_LOW: u32 = 0x3FFB_0000; + pub const SOC_DRAM_HIGH: u32 = 0x4000_0000; } diff --git a/esp-hal-common/src/soc/esp32s3/mod.rs b/esp-hal-common/src/soc/esp32s3/mod.rs index a9cf92c25..30c09ed46 100644 --- a/esp-hal-common/src/soc/esp32s3/mod.rs +++ b/esp-hal-common/src/soc/esp32s3/mod.rs @@ -25,4 +25,7 @@ pub(crate) mod constants { pub const RMT_CHANNEL_RAM_SIZE: usize = 48; pub const RMT_CLOCK_SRC: u8 = 1; pub const RMT_CLOCK_SRC_FREQ: fugit::HertzU32 = fugit::HertzU32::MHz(80); + + pub const SOC_DRAM_LOW: u32 = 0x3FC8_8000; + pub const SOC_DRAM_HIGH: u32 = 0x3FD0_0000; } diff --git a/esp-hal-common/src/soc/mod.rs b/esp-hal-common/src/soc/mod.rs index 09ed682e3..0b4346a42 100644 --- a/esp-hal-common/src/soc/mod.rs +++ b/esp-hal-common/src/soc/mod.rs @@ -10,3 +10,11 @@ pub use self::soc::*; mod soc; mod efuse_field; + +pub fn is_valid_ram_address(address: u32) -> bool { + if (soc::constants::SOC_DRAM_LOW..=soc::constants::SOC_DRAM_HIGH).contains(&address) { + true + } else { + false + } +} diff --git a/esp-hal-common/src/spi.rs b/esp-hal-common/src/spi.rs index 4ac1a0a81..a96626bb6 100644 --- a/esp-hal-common/src/spi.rs +++ b/esp-hal-common/src/spi.rs @@ -766,6 +766,7 @@ pub mod dma { TxPrivate, }, peripheral::PeripheralRef, + FlashSafeDma, }; pub trait WithDmaSpi2<'d, T, C, M> @@ -1281,6 +1282,57 @@ pub mod dma { } } + impl, const SIZE: usize> + embedded_hal::blocking::spi::Transfer for FlashSafeDma + { + type Error = T::Error; + + fn transfer<'w>(&mut self, words: &'w mut [u8]) -> Result<&'w [u8], Self::Error> { + self.inner.transfer(words) + } + } + + impl, const SIZE: usize> + embedded_hal::blocking::spi::Write for FlashSafeDma + { + type Error = T::Error; + + fn write(&mut self, words: &[u8]) -> Result<(), Self::Error> { + Ok( + if !crate::soc::is_valid_ram_address(&words[0] as *const _ as u32) { + for chunk in words.chunks(SIZE) { + self.buffer[..chunk.len()].copy_from_slice(chunk); + self.inner.write(&self.buffer[..chunk.len()])?; + } + } else { + self.inner.write(words)?; + }, + ) + } + } + + impl, const SIZE: usize> embedded_hal::spi::FullDuplex + for FlashSafeDma + where + Self: embedded_hal::blocking::spi::Transfer, + Self: embedded_hal::blocking::spi::Write, + { + type Error = T::Error; + + fn read(&mut self) -> nb::Result { + use embedded_hal::blocking::spi::Transfer; + let mut buf = [0; 1]; + self.transfer(&mut buf)?; + Ok(buf[0]) + } + + fn send(&mut self, word: u8) -> nb::Result<(), Self::Error> { + use embedded_hal::blocking::spi::Write; + self.write(&[word])?; + Ok(()) + } + } + #[cfg(feature = "async")] mod asynch { use super::*; @@ -1389,6 +1441,50 @@ pub mod dma { self.spi.flush() } } + + impl embedded_hal_async::spi::SpiBus + for FlashSafeDma + { + async fn read(&mut self, words: &mut [u8]) -> Result<(), Self::Error> { + self.inner.read(words).await + } + + async fn write(&mut self, words: &[u8]) -> Result<(), Self::Error> { + Ok( + if !crate::soc::is_valid_ram_address(&words[0] as *const _ as u32) { + for chunk in words.chunks(SIZE) { + self.buffer[..chunk.len()].copy_from_slice(chunk); + self.inner.write(&self.buffer[..chunk.len()]).await?; + } + } else { + self.inner.write(words).await?; + }, + ) + } + + async fn flush(&mut self) -> Result<(), Self::Error> { + self.inner.flush().await + } + + async fn transfer_in_place(&mut self, words: &mut [u8]) -> Result<(), Self::Error> { + self.inner.transfer_in_place(words).await + } + + async fn transfer(&mut self, read: &mut [u8], write: &[u8]) -> Result<(), Self::Error> { + Ok( + if !crate::soc::is_valid_ram_address(&write[0] as *const _ as u32) { + for (read, write) in read.chunks_mut(SIZE).zip(write.chunks(SIZE)) { + self.buffer[..write.len()].copy_from_slice(write); + self.inner + .transfer(read, &self.buffer[..write.len()]) + .await?; + } + } else { + self.inner.transfer(read, write).await?; + }, + ) + } + } } #[cfg(feature = "eh1")] @@ -1396,7 +1492,7 @@ pub mod dma { use embedded_hal_1::spi::SpiBus; use super::{super::InstanceDma, SpiDma, SpiPeripheral}; - use crate::{dma::ChannelTypes, spi::IsFullDuplex}; + use crate::{dma::ChannelTypes, spi::IsFullDuplex, FlashSafeDma}; impl<'d, T, C, M> embedded_hal_1::spi::ErrorType for SpiDma<'d, T, C, M> where @@ -1451,6 +1547,54 @@ pub mod dma { self.spi.flush() } } + + impl embedded_hal_1::spi::ErrorType + for FlashSafeDma + { + type Error = T::Error; + } + + impl embedded_hal_1::spi::SpiBus + for FlashSafeDma + { + fn read(&mut self, words: &mut [u8]) -> Result<(), Self::Error> { + self.inner.read(words) + } + + fn write(&mut self, words: &[u8]) -> Result<(), Self::Error> { + Ok( + if !crate::soc::is_valid_ram_address(&words[0] as *const _ as u32) { + for chunk in words.chunks(SIZE) { + self.buffer[..chunk.len()].copy_from_slice(chunk); + self.inner.write(&self.buffer[..chunk.len()])?; + } + } else { + self.inner.write(words)?; + }, + ) + } + + fn transfer(&mut self, read: &mut [u8], write: &[u8]) -> Result<(), Self::Error> { + Ok( + if !crate::soc::is_valid_ram_address(&write[0] as *const _ as u32) { + for (read, write) in read.chunks_mut(SIZE).zip(write.chunks(SIZE)) { + self.buffer[..write.len()].copy_from_slice(write); + self.inner.transfer(read, &self.buffer[..write.len()])?; + } + } else { + self.inner.transfer(read, write)?; + }, + ) + } + + fn transfer_in_place(&mut self, words: &mut [u8]) -> Result<(), Self::Error> { + self.inner.transfer_in_place(words) + } + + fn flush(&mut self) -> Result<(), Self::Error> { + self.inner.flush() + } + } } }