Add FlashSafeDma wrapper for eh traits which ensure correct DMA transfers from flash (#678)

This commit is contained in:
Scott Mabin 2023-08-09 13:07:20 -07:00 committed by GitHub
parent 4ba618c875
commit 47b987fb66
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
12 changed files with 232 additions and 1 deletions

View File

@ -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

View File

@ -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,

View File

@ -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<T, const SIZE: usize> {
inner: T,
buffer: [u8; SIZE],
}
impl<T, const SIZE: usize> FlashSafeDma<T, SIZE> {
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
}
}

View File

@ -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;
}

View File

@ -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;
}

View File

@ -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;
}

View File

@ -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;
}

View File

@ -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;
}

View File

@ -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;
}

View File

@ -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;
}

View File

@ -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
}
}

View File

@ -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<T: embedded_hal::blocking::spi::Transfer<u8>, const SIZE: usize>
embedded_hal::blocking::spi::Transfer<u8> for FlashSafeDma<T, SIZE>
{
type Error = T::Error;
fn transfer<'w>(&mut self, words: &'w mut [u8]) -> Result<&'w [u8], Self::Error> {
self.inner.transfer(words)
}
}
impl<T: embedded_hal::blocking::spi::Write<u8>, const SIZE: usize>
embedded_hal::blocking::spi::Write<u8> for FlashSafeDma<T, SIZE>
{
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<T: embedded_hal::spi::FullDuplex<u8>, const SIZE: usize> embedded_hal::spi::FullDuplex<u8>
for FlashSafeDma<T, SIZE>
where
Self: embedded_hal::blocking::spi::Transfer<u8, Error = T::Error>,
Self: embedded_hal::blocking::spi::Write<u8, Error = T::Error>,
{
type Error = T::Error;
fn read(&mut self) -> nb::Result<u8, Self::Error> {
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<T: embedded_hal_async::spi::SpiBus, const SIZE: usize> embedded_hal_async::spi::SpiBus
for FlashSafeDma<T, SIZE>
{
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<T: embedded_hal_1::spi::ErrorType, const SIZE: usize> embedded_hal_1::spi::ErrorType
for FlashSafeDma<T, SIZE>
{
type Error = T::Error;
}
impl<T: embedded_hal_1::spi::SpiBus, const SIZE: usize> embedded_hal_1::spi::SpiBus
for FlashSafeDma<T, SIZE>
{
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()
}
}
}
}