diff --git a/esp-hal/src/dma/gdma.rs b/esp-hal/src/dma/gdma.rs index c8b49563ca6..da6724e89c2 100644 --- a/esp-hal/src/dma/gdma.rs +++ b/esp-hal/src/dma/gdma.rs @@ -93,6 +93,13 @@ impl RegisterAccess for Channel { .modify(|_, w| w.mem_trans_en().bit(value)); } + #[cfg(esp32s3)] + fn set_out_ext_mem_block_size(size: DmaExtMemBKSize) { + Self::ch() + .out_conf1() + .modify(|_, w| unsafe { w.out_ext_mem_bk_size().bits(size as u8) }); + } + fn set_out_burstmode(burst_mode: bool) { Self::ch().out_conf0().modify(|_, w| { w.out_data_burst_en() @@ -206,6 +213,13 @@ impl RegisterAccess for Channel { .write(|w| w.out_eof().clear_bit_by_one()); } + #[cfg(esp32s3)] + fn set_in_ext_mem_block_size(size: DmaExtMemBKSize) { + Self::ch() + .in_conf1() + .modify(|_, w| unsafe { w.in_ext_mem_bk_size().bits(size as u8) }); + } + fn set_in_burstmode(burst_mode: bool) { Self::ch().in_conf0().modify(|_, w| { w.in_data_burst_en() @@ -626,6 +640,8 @@ pub use m2m::*; mod m2m { use embedded_dma::{ReadBuffer, WriteBuffer}; + #[cfg(esp32s3)] + use crate::dma::DmaExtMemBKSize; use crate::dma::{ dma_private::{DmaSupport, DmaSupportRx}, Channel, @@ -751,6 +767,18 @@ mod m2m { .prepare_transfer_without_start(self.peripheral, &self.rx_chain)?; self.channel.rx.set_mem2mem_mode(true); } + #[cfg(esp32s3)] + if crate::soc::is_valid_psram_address(tx_ptr as u32) { + self.channel + .tx + .set_ext_mem_block_size(DmaExtMemBKSize::Size32); + } + #[cfg(esp32s3)] + if crate::soc::is_valid_psram_address(rx_ptr as u32) { + self.channel + .rx + .set_ext_mem_block_size(DmaExtMemBKSize::Size32); + } self.channel.tx.start_transfer()?; self.channel.rx.start_transfer()?; Ok(DmaTransferRx::new(self)) diff --git a/esp-hal/src/dma/mod.rs b/esp-hal/src/dma/mod.rs index a40786b8092..61714cd81a1 100644 --- a/esp-hal/src/dma/mod.rs +++ b/esp-hal/src/dma/mod.rs @@ -66,7 +66,7 @@ impl Debug for DmaDescriptorFlags { .field("size", &self.size()) .field("length", &self.length()) .field("suc_eof", &self.suc_eof()) - .field("owner", &self.owner()) + .field("owner", &(if self.owner() { "DMA" } else { "CPU" })) .finish() } } @@ -567,8 +567,8 @@ impl DescriptorChain { ) -> Result<(), DmaError> { if !crate::soc::is_valid_ram_address(self.first() as u32) || !crate::soc::is_valid_ram_address(self.last() as u32) - || !crate::soc::is_valid_ram_address(data as u32) - || !crate::soc::is_valid_ram_address(unsafe { data.add(len) } as u32) + || !crate::soc::is_valid_memory_address(data as u32) + || !crate::soc::is_valid_memory_address(unsafe { data.add(len) } as u32) { return Err(DmaError::UnsupportedMemoryRegion); } @@ -639,8 +639,8 @@ impl DescriptorChain { ) -> Result<(), DmaError> { if !crate::soc::is_valid_ram_address(self.first() as u32) || !crate::soc::is_valid_ram_address(self.last() as u32) - || !crate::soc::is_valid_ram_address(data as u32) - || !crate::soc::is_valid_ram_address(unsafe { data.add(len) } as u32) + || !crate::soc::is_valid_memory_address(data as u32) + || !crate::soc::is_valid_memory_address(unsafe { data.add(len) } as u32) { return Err(DmaError::UnsupportedMemoryRegion); } @@ -707,6 +707,15 @@ impl DescriptorChain { } } +/// Block size for transfers to/from psram +#[derive(Copy, Clone, Debug, PartialEq)] +#[allow(missing_docs)] +pub enum DmaExtMemBKSize { + Size16 = 0, + Size32 = 1, + Size64 = 2, +} + pub(crate) struct TxCircularState { write_offset: usize, write_descr_ptr: *mut DmaDescriptor, @@ -967,6 +976,9 @@ pub trait RxPrivate: crate::private::Sealed { fn start_transfer(&mut self) -> Result<(), DmaError>; + #[cfg(esp32s3)] + fn set_ext_mem_block_size(&self, size: DmaExtMemBKSize); + #[cfg(gdma)] fn set_mem2mem_mode(&mut self, value: bool); @@ -1126,6 +1138,11 @@ where self.rx_impl.start_transfer() } + #[cfg(esp32s3)] + fn set_ext_mem_block_size(&self, size: DmaExtMemBKSize) { + CH::Channel::set_in_ext_mem_block_size(size); + } + #[cfg(gdma)] fn set_mem2mem_mode(&mut self, value: bool) { CH::Channel::set_mem2mem_mode(value); @@ -1244,6 +1261,9 @@ pub trait TxPrivate: crate::private::Sealed { fn start_transfer(&mut self) -> Result<(), DmaError>; + #[cfg(esp32s3)] + fn set_ext_mem_block_size(&self, size: DmaExtMemBKSize); + fn clear_ch_out_done(&self); fn is_ch_out_done_set(&self) -> bool; @@ -1410,6 +1430,11 @@ where self.tx_impl.start_transfer() } + #[cfg(esp32s3)] + fn set_ext_mem_block_size(&self, size: DmaExtMemBKSize) { + CH::Channel::set_out_ext_mem_block_size(size); + } + fn clear_ch_out_done(&self) { self.tx_impl.clear_ch_out_done(); } @@ -1489,6 +1514,8 @@ pub trait RegisterAccess: crate::private::Sealed { fn init_channel(); #[cfg(gdma)] fn set_mem2mem_mode(value: bool); + #[cfg(esp32s3)] + fn set_out_ext_mem_block_size(size: DmaExtMemBKSize); fn set_out_burstmode(burst_mode: bool); fn set_out_priority(priority: DmaPriority); fn clear_out_interrupts(); @@ -1507,6 +1534,8 @@ pub trait RegisterAccess: crate::private::Sealed { fn reset_out_eof_interrupt(); fn last_out_dscr_address() -> usize; + #[cfg(esp32s3)] + fn set_in_ext_mem_block_size(size: DmaExtMemBKSize); fn set_in_burstmode(burst_mode: bool); fn set_in_priority(priority: DmaPriority); fn clear_in_interrupts(); diff --git a/esp-hal/src/soc/mod.rs b/esp-hal/src/soc/mod.rs index 47090437149..1e7c93b41a2 100644 --- a/esp-hal/src/soc/mod.rs +++ b/esp-hal/src/soc/mod.rs @@ -71,3 +71,20 @@ impl self::efuse::Efuse { pub(crate) fn is_valid_ram_address(address: u32) -> bool { (self::constants::SOC_DRAM_LOW..=self::constants::SOC_DRAM_HIGH).contains(&address) } + +#[allow(unused)] +pub(crate) fn is_valid_psram_address(address: u32) -> bool { + #[cfg(psram)] + { + let start = crate::psram::psram_vaddr_start() as u32; + let end = start + crate::psram::PSRAM_BYTES as u32; + (start..=end).contains(&address) + } + #[cfg(not(psram))] + false +} + +#[allow(unused)] +pub(crate) fn is_valid_memory_address(address: u32) -> bool { + is_valid_ram_address(address) || is_valid_psram_address(address) +} diff --git a/examples/Cargo.toml b/examples/Cargo.toml index d373b7196cb..e3bb27e0060 100644 --- a/examples/Cargo.toml +++ b/examples/Cargo.toml @@ -7,6 +7,7 @@ publish = false [dependencies] aes = "0.8.4" +aligned = { version = "0.4.2", optional = true } bleps = { git = "https://github.com/bjoernQ/bleps", package = "bleps", rev = "a5148d8ae679e021b78f53fd33afb8bb35d0b62e", features = [ "macros", "async"] } cfg-if = "1.0.0" critical-section = "1.1.2" diff --git a/examples/src/bin/dma_extmem2mem.rs b/examples/src/bin/dma_extmem2mem.rs new file mode 100644 index 00000000000..bb934d0fece --- /dev/null +++ b/examples/src/bin/dma_extmem2mem.rs @@ -0,0 +1,126 @@ +//! Uses DMA to copy psram to internal memory. + +//% FEATURES: esp-hal/log opsram-2m aligned +//% CHIPS: esp32s3 + +#![no_std] +#![no_main] + +use aligned::{Aligned, A64}; +use esp_backtrace as _; +use esp_hal::{ + clock::ClockControl, + delay::Delay, + dma::{Dma, DmaPriority, Mem2Mem}, + dma_descriptors_chunk_size, + peripherals::Peripherals, + prelude::*, + system::SystemControl, +}; +use log::{error, info}; +extern crate alloc; + +const DATA_SIZE: usize = 1024 * 10; +const CHUNK_SIZE: usize = 4032; // size is aligned to 64 bytes + +macro_rules! dma_buffer_aligned { + ($size:expr, $align:ty) => {{ + static mut BUFFER: Aligned<$align, [u8; $size]> = Aligned([0; $size]); + unsafe { &mut *BUFFER } + }}; +} + +macro_rules! dma_alloc_buffer { + ($size:expr, $align:expr) => {{ + let layout = core::alloc::Layout::from_size_align($size, $align).unwrap(); + unsafe { + let ptr = alloc::alloc::alloc(layout); + if ptr.is_null() { + error!("make_buffers: alloc failed"); + alloc::alloc::handle_alloc_error(layout); + } + core::slice::from_raw_parts_mut(ptr, $size) + } + }}; +} + +#[global_allocator] +static ALLOCATOR: esp_alloc::EspHeap = esp_alloc::EspHeap::empty(); + +fn init_heap(psram: impl esp_hal::peripheral::Peripheral

) { + esp_hal::psram::init_psram(psram); + info!( + "init_heap: start: 0x{:0x}", + esp_hal::psram::psram_vaddr_start() + ); + unsafe { + ALLOCATOR.init( + esp_hal::psram::psram_vaddr_start() as *mut u8, + esp_hal::psram::PSRAM_BYTES, + ); + } +} + +#[entry] +fn main() -> ! { + esp_println::logger::init_logger(log::LevelFilter::Info); + + let peripherals = Peripherals::take(); + init_heap(peripherals.PSRAM); + let system = SystemControl::new(peripherals.SYSTEM); + let clocks = ClockControl::boot_defaults(system.clock_control).freeze(); + let delay = Delay::new(&clocks); + + let mut rx_buffer: &mut [u8] = dma_alloc_buffer!(DATA_SIZE, 64); + let tx_buffer = dma_buffer_aligned!(DATA_SIZE, A64); + let (tx_descriptors, rx_descriptors) = dma_descriptors_chunk_size!(DATA_SIZE, CHUNK_SIZE); + + let dma = Dma::new(peripherals.DMA); + let channel = dma.channel0.configure(false, DmaPriority::Priority0); + let dma_peripheral = peripherals.SPI2; + + let mut mem2mem = Mem2Mem::new_with_chunk_size( + channel, + dma_peripheral, + tx_descriptors, + rx_descriptors, + CHUNK_SIZE, + ) + .unwrap(); + + for i in 0..core::mem::size_of_val(tx_buffer) { + tx_buffer[i] = (i % 256) as u8; + } + + info!("Starting transfer of {} bytes", DATA_SIZE); + let result = mem2mem.start_transfer(&tx_buffer, &mut rx_buffer); + match result { + Ok(dma_wait) => { + info!("Transfer started"); + dma_wait.wait().unwrap(); + info!("Transfer completed, comparing buffer"); + let mut error = false; + for i in 0..core::mem::size_of_val(tx_buffer) { + if rx_buffer[i] != tx_buffer[i] { + error!( + "Error: tx_buffer[{}] = {}, rx_buffer[{}] = {}", + i, tx_buffer[i], i, rx_buffer[i] + ); + error = true; + break; + } + } + if !error { + info!("Buffers are equal"); + } + info!("Done"); + } + Err(e) => { + error!("start_transfer: Error: {:?}", e); + } + } + + loop { + delay.delay(2.secs()); + } +}