mirror of
https://github.com/esp-rs/esp-hal.git
synced 2025-09-29 05:10:55 +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)
|
- Add `ClockControl::max` helper for all chips (#701)
|
||||||
- Added module-level documentation for all peripherals
|
- Added module-level documentation for all peripherals
|
||||||
- Added module-level documentation for all peripherals (#680)
|
- 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
|
### Changed
|
||||||
|
|
||||||
|
@ -83,6 +83,10 @@ pub enum DmaError {
|
|||||||
InvalidAlignment,
|
InvalidAlignment,
|
||||||
OutOfDescriptors,
|
OutOfDescriptors,
|
||||||
InvalidDescriptorSize,
|
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,
|
DescriptorError,
|
||||||
Overflow,
|
Overflow,
|
||||||
Exhausted,
|
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_RAM_START: usize = 0x3ff56800;
|
||||||
pub const RMT_CHANNEL_RAM_SIZE: usize = 64;
|
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(crate) mod registers {
|
||||||
pub const INTERRUPT_MAP_BASE: u32 = 0x600c2000;
|
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_CHANNEL_RAM_SIZE: usize = 48;
|
||||||
pub const RMT_CLOCK_SRC: u8 = 1;
|
pub const RMT_CLOCK_SRC: u8 = 1;
|
||||||
pub const RMT_CLOCK_SRC_FREQ: fugit::HertzU32 = fugit::HertzU32::MHz(80);
|
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_CHANNEL_RAM_SIZE: usize = 48;
|
||||||
pub const RMT_CLOCK_SRC: u8 = 1;
|
pub const RMT_CLOCK_SRC: u8 = 1;
|
||||||
pub const RMT_CLOCK_SRC_FREQ: fugit::HertzU32 = fugit::HertzU32::MHz(80);
|
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_CHANNEL_RAM_SIZE: usize = 48;
|
||||||
pub const RMT_CLOCK_SRC: bool = false;
|
pub const RMT_CLOCK_SRC: bool = false;
|
||||||
pub const RMT_CLOCK_SRC_FREQ: fugit::HertzU32 = fugit::HertzU32::MHz(32);
|
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_RAM_START: usize = 0x3f416400;
|
||||||
pub const RMT_CHANNEL_RAM_SIZE: usize = 64;
|
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_CHANNEL_RAM_SIZE: usize = 48;
|
||||||
pub const RMT_CLOCK_SRC: u8 = 1;
|
pub const RMT_CLOCK_SRC: u8 = 1;
|
||||||
pub const RMT_CLOCK_SRC_FREQ: fugit::HertzU32 = fugit::HertzU32::MHz(80);
|
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 soc;
|
||||||
|
|
||||||
mod efuse_field;
|
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,
|
TxPrivate,
|
||||||
},
|
},
|
||||||
peripheral::PeripheralRef,
|
peripheral::PeripheralRef,
|
||||||
|
FlashSafeDma,
|
||||||
};
|
};
|
||||||
|
|
||||||
pub trait WithDmaSpi2<'d, T, C, M>
|
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")]
|
#[cfg(feature = "async")]
|
||||||
mod asynch {
|
mod asynch {
|
||||||
use super::*;
|
use super::*;
|
||||||
@ -1389,6 +1441,50 @@ pub mod dma {
|
|||||||
self.spi.flush()
|
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")]
|
#[cfg(feature = "eh1")]
|
||||||
@ -1396,7 +1492,7 @@ pub mod dma {
|
|||||||
use embedded_hal_1::spi::SpiBus;
|
use embedded_hal_1::spi::SpiBus;
|
||||||
|
|
||||||
use super::{super::InstanceDma, SpiDma, SpiPeripheral};
|
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>
|
impl<'d, T, C, M> embedded_hal_1::spi::ErrorType for SpiDma<'d, T, C, M>
|
||||||
where
|
where
|
||||||
@ -1451,6 +1547,54 @@ pub mod dma {
|
|||||||
self.spi.flush()
|
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