mirror of
https://github.com/esp-rs/esp-hal.git
synced 2025-09-28 12:50:53 +00:00
Add FlashSafeDma
wrapper for eh traits which ensure correct DMA transfers from flash (#678)
This commit is contained in:
parent
4ba618c875
commit
47b987fb66
@ -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
|
||||
|
||||
|
@ -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,
|
||||
|
@ -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
|
||||
}
|
||||
}
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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
|
||||
}
|
||||
}
|
||||
|
@ -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()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user