From 1631f22083ef4162ee63f7cb1b0d15b14ea499ff Mon Sep 17 00:00:00 2001 From: liebman Date: Mon, 15 Jul 2024 06:00:33 -0600 Subject: [PATCH] support dma chunk sizes other than 4092 (#1758) * support dma chunk sizes other than 4092 * fmt * update CHANGELOG * fix 0 size static assert * review changes: - `.div_ceil()` - return errors for bad chunk size and buffer sizes in Mem2Mem constructors - correct 0 chunk size check in descripter macros * dma: clear the mem2mem bit when channel is configured instead of in Drop --- esp-hal/CHANGELOG.md | 1 + esp-hal/src/dma/gdma.rs | 65 ++++--- esp-hal/src/dma/mod.rs | 296 ++++++++++++++++++-------------- examples/src/bin/dma_mem2mem.rs | 3 +- hil-test/Cargo.toml | 4 + hil-test/tests/dma_macros.rs | 205 ++++++++++++++++++++++ hil-test/tests/dma_mem2mem.rs | 154 ++++++++++++++++- 7 files changed, 571 insertions(+), 157 deletions(-) create mode 100644 hil-test/tests/dma_macros.rs diff --git a/esp-hal/CHANGELOG.md b/esp-hal/CHANGELOG.md index e66b779c0..9fc7b5b4e 100644 --- a/esp-hal/CHANGELOG.md +++ b/esp-hal/CHANGELOG.md @@ -44,6 +44,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Improved interrupt latency on RISC-V based chips (#1679) - `esp_wifi::initialize` no longer requires running maximum CPU clock, instead check it runs above 80MHz. (#1688) - Move DMA descriptors from DMA Channel to each individual peripheral driver. (#1719) +- Support DMA chunk sizes other than the default 4092 (#1758) - Improved interrupt latency on Xtensa based chips (#1735) - Improve PCNT api (#1765) diff --git a/esp-hal/src/dma/gdma.rs b/esp-hal/src/dma/gdma.rs index e0fbac28a..bf3dcae1a 100644 --- a/esp-hal/src/dma/gdma.rs +++ b/esp-hal/src/dma/gdma.rs @@ -653,18 +653,38 @@ mod m2m { { /// Create a new Mem2Mem instance. pub fn new( - mut channel: Channel<'d, C, MODE>, + channel: Channel<'d, C, MODE>, peripheral: impl DmaEligible, tx_descriptors: &'static mut [DmaDescriptor], rx_descriptors: &'static mut [DmaDescriptor], - ) -> Self { - channel.tx.init_channel(); - channel.rx.init_channel(); - Mem2Mem { - channel, - peripheral: peripheral.dma_peripheral(), - tx_chain: DescriptorChain::new(tx_descriptors), - rx_chain: DescriptorChain::new(rx_descriptors), + ) -> Result { + unsafe { + Self::new_unsafe( + channel, + peripheral.dma_peripheral(), + tx_descriptors, + rx_descriptors, + crate::dma::CHUNK_SIZE, + ) + } + } + + /// Create a new Mem2Mem instance with specific chunk size. + pub fn new_with_chunk_size( + channel: Channel<'d, C, MODE>, + peripheral: impl DmaEligible, + tx_descriptors: &'static mut [DmaDescriptor], + rx_descriptors: &'static mut [DmaDescriptor], + chunk_size: usize, + ) -> Result { + unsafe { + Self::new_unsafe( + channel, + peripheral.dma_peripheral(), + tx_descriptors, + rx_descriptors, + chunk_size, + ) } } @@ -679,15 +699,22 @@ mod m2m { peripheral: DmaPeripheral, tx_descriptors: &'static mut [DmaDescriptor], rx_descriptors: &'static mut [DmaDescriptor], - ) -> Self { + chunk_size: usize, + ) -> Result { + if !(1..=4092).contains(&chunk_size) { + return Err(DmaError::InvalidChunkSize); + } + if tx_descriptors.is_empty() || rx_descriptors.is_empty() { + return Err(DmaError::OutOfDescriptors); + } channel.tx.init_channel(); channel.rx.init_channel(); - Mem2Mem { + Ok(Mem2Mem { channel, peripheral, - tx_chain: DescriptorChain::new(tx_descriptors), - rx_chain: DescriptorChain::new(rx_descriptors), - } + tx_chain: DescriptorChain::new_with_chunk_size(tx_descriptors, chunk_size), + rx_chain: DescriptorChain::new_with_chunk_size(rx_descriptors, chunk_size), + }) } /// Start a memory to memory transfer. @@ -719,16 +746,6 @@ mod m2m { } } - impl<'d, C, MODE> Drop for Mem2Mem<'d, C, MODE> - where - C: ChannelTypes, - MODE: crate::Mode, - { - fn drop(&mut self) { - self.channel.rx.set_mem2mem_mode(false); - } - } - impl<'d, C, MODE> DmaSupport for Mem2Mem<'d, C, MODE> where C: ChannelTypes, diff --git a/esp-hal/src/dma/mod.rs b/esp-hal/src/dma/mod.rs index 06ba10869..ffc17fbaa 100644 --- a/esp-hal/src/dma/mod.rs +++ b/esp-hal/src/dma/mod.rs @@ -40,8 +40,8 @@ //! # } //! ``` //! -//! ⚠️ Note: Descriptors should be sized as `(CHUNK_SIZE + 4091) / 4092`. -//! I.e., to transfer buffers of size `1..=4092`, you need 1 descriptor. +//! ⚠️ Note: Descriptors should be sized as `(max_transfer_size + CHUNK_SIZE - 1) / CHUNK_SIZE`. +//! I.e., to transfer buffers of size `1..=CHUNK_SIZE`, you need 1 descriptor. //! //! For convenience you can use the [crate::dma_buffers] macro. #![warn(missing_docs)] @@ -142,7 +142,8 @@ pub enum DmaInterrupt { RxDone, } -const CHUNK_SIZE: usize = 4092; +/// The default CHUNK_SIZE used for DMA transfers +pub const CHUNK_SIZE: usize = 4092; /// Convenience macro to create DMA buffers and descriptors /// @@ -153,39 +154,12 @@ const CHUNK_SIZE: usize = 4092; /// ``` #[macro_export] macro_rules! dma_buffers { - ($tx_size:expr, $rx_size:expr) => {{ - static mut TX_BUFFER: [u8; $tx_size] = [0u8; $tx_size]; - static mut RX_BUFFER: [u8; $rx_size] = [0u8; $rx_size]; - static mut TX_DESCRIPTORS: [$crate::dma::DmaDescriptor; ($tx_size + 4091) / 4092] = - [$crate::dma::DmaDescriptor::EMPTY; ($tx_size + 4091) / 4092]; - static mut RX_DESCRIPTORS: [$crate::dma::DmaDescriptor; ($rx_size + 4091) / 4092] = - [$crate::dma::DmaDescriptor::EMPTY; ($rx_size + 4091) / 4092]; - unsafe { - ( - &mut TX_BUFFER, - &mut TX_DESCRIPTORS, - &mut RX_BUFFER, - &mut RX_DESCRIPTORS, - ) - } - }}; - - ($size:expr) => {{ - static mut TX_BUFFER: [u8; $size] = [0u8; $size]; - static mut RX_BUFFER: [u8; $size] = [0u8; $size]; - static mut TX_DESCRIPTORS: [$crate::dma::DmaDescriptor; ($size + 4091) / 4092] = - [$crate::dma::DmaDescriptor::EMPTY; ($size + 4091) / 4092]; - static mut RX_DESCRIPTORS: [$crate::dma::DmaDescriptor; ($size + 4091) / 4092] = - [$crate::dma::DmaDescriptor::EMPTY; ($size + 4091) / 4092]; - unsafe { - ( - &mut TX_BUFFER, - &mut TX_DESCRIPTORS, - &mut RX_BUFFER, - &mut RX_DESCRIPTORS, - ) - } - }}; + ($tx_size:expr, $rx_size:expr) => { + $crate::dma_buffers_chunk_size!($tx_size, $rx_size, $crate::dma::CHUNK_SIZE) + }; + ($size:expr) => { + $crate::dma_buffers_chunk_size!($size, $crate::dma::CHUNK_SIZE) + }; } /// Convenience macro to create circular DMA buffers and descriptors @@ -198,59 +172,13 @@ macro_rules! dma_buffers { /// ``` #[macro_export] macro_rules! dma_circular_buffers { - ($tx_size:expr, $rx_size:expr) => {{ - static mut TX_BUFFER: [u8; $tx_size] = [0u8; $tx_size]; - static mut RX_BUFFER: [u8; $rx_size] = [0u8; $rx_size]; + ($tx_size:expr, $rx_size:expr) => { + $crate::dma_circular_buffers_chunk_size!($tx_size, $rx_size, $crate::dma::CHUNK_SIZE) + }; - const tx_descriptor_len: usize = if $tx_size > 4092 * 2 { - ($tx_size + 4091) / 4092 - } else { - 3 - }; - - const rx_descriptor_len: usize = if $rx_size > 4092 * 2 { - ($rx_size + 4091) / 4092 - } else { - 3 - }; - - static mut TX_DESCRIPTORS: [$crate::dma::DmaDescriptor; tx_descriptor_len] = - [$crate::dma::DmaDescriptor::EMPTY; tx_descriptor_len]; - static mut RX_DESCRIPTORS: [$crate::dma::DmaDescriptor; rx_descriptor_len] = - [$crate::dma::DmaDescriptor::EMPTY; rx_descriptor_len]; - unsafe { - ( - &mut TX_BUFFER, - &mut TX_DESCRIPTORS, - &mut RX_BUFFER, - &mut RX_DESCRIPTORS, - ) - } - }}; - - ($size:expr) => {{ - static mut TX_BUFFER: [u8; $size] = [0u8; $size]; - static mut RX_BUFFER: [u8; $size] = [0u8; $size]; - - const descriptor_len: usize = if $size > 4092 * 2 { - ($size + 4091) / 4092 - } else { - 3 - }; - - static mut TX_DESCRIPTORS: [$crate::dma::DmaDescriptor; descriptor_len] = - [$crate::dma::DmaDescriptor::EMPTY; descriptor_len]; - static mut RX_DESCRIPTORS: [$crate::dma::DmaDescriptor; descriptor_len] = - [$crate::dma::DmaDescriptor::EMPTY; descriptor_len]; - unsafe { - ( - &mut TX_BUFFER, - &mut TX_DESCRIPTORS, - &mut RX_BUFFER, - &mut RX_DESCRIPTORS, - ) - } - }}; + ($size:expr) => { + $crate::dma_circular_buffers_chunk_size!($size, $size, $crate::dma::CHUNK_SIZE) + }; } /// Convenience macro to create DMA descriptors @@ -262,21 +190,13 @@ macro_rules! dma_circular_buffers { /// ``` #[macro_export] macro_rules! dma_descriptors { - ($tx_size:expr, $rx_size:expr) => {{ - static mut TX_DESCRIPTORS: [$crate::dma::DmaDescriptor; ($tx_size + 4091) / 4092] = - [$crate::dma::DmaDescriptor::EMPTY; ($tx_size + 4091) / 4092]; - static mut RX_DESCRIPTORS: [$crate::dma::DmaDescriptor; ($rx_size + 4091) / 4092] = - [$crate::dma::DmaDescriptor::EMPTY; ($rx_size + 4091) / 4092]; - unsafe { (&mut TX_DESCRIPTORS, &mut RX_DESCRIPTORS) } - }}; + ($tx_size:expr, $rx_size:expr) => { + $crate::dma_descriptors_chunk_size!($tx_size, $rx_size, $crate::dma::CHUNK_SIZE) + }; - ($size:expr) => {{ - static mut TX_DESCRIPTORS: [$crate::dma::DmaDescriptor; ($size + 4091) / 4092] = - [$crate::dma::DmaDescriptor::EMPTY; ($size + 4091) / 4092]; - static mut RX_DESCRIPTORS: [$crate::dma::DmaDescriptor; ($size + 4091) / 4092] = - [$crate::dma::DmaDescriptor::EMPTY; ($size + 4091) / 4092]; - unsafe { (&mut TX_DESCRIPTORS, &mut RX_DESCRIPTORS) } - }}; + ($size:expr) => { + $crate::dma_descriptors_chunk_size!($size, $size, $crate::dma::CHUNK_SIZE) + }; } /// Convenience macro to create circular DMA descriptors @@ -288,15 +208,127 @@ macro_rules! dma_descriptors { /// ``` #[macro_export] macro_rules! dma_circular_descriptors { - ($tx_size:expr, $rx_size:expr) => {{ - const tx_descriptor_len: usize = if $tx_size > 4092 * 2 { - ($tx_size + 4091) / 4092 + ($tx_size:expr, $rx_size:expr) => { + $crate::dma_circular_descriptors_chunk_size!($tx_size, $rx_size, $crate::dma::CHUNK_SIZE) + }; + + ($size:expr) => { + $crate::dma_circular_descriptors_chunk_size!($size, $size, $crate::dma::CHUNK_SIZE) + }; +} + +/// Convenience macro to create DMA buffers and descriptors with specific chunk +/// size +/// +/// ## Usage +/// ```rust,ignore +/// // TX and RX buffers are 32000 bytes - passing only one parameter makes TX and RX the same size +/// let (tx_buffer, tx_descriptors, rx_buffer, rx_descriptors) = dma_buffers!(32000, 32000, 4032); +/// ``` +#[macro_export] +macro_rules! dma_buffers_chunk_size { + ($tx_size:expr, $rx_size:expr, $chunk_size:expr) => {{ + static mut TX_BUFFER: [u8; $tx_size] = [0u8; $tx_size]; + static mut RX_BUFFER: [u8; $rx_size] = [0u8; $rx_size]; + let (mut tx_descriptors, mut rx_descriptors) = + $crate::dma_descriptors_chunk_size!($tx_size, $rx_size, $chunk_size); + unsafe { + ( + &mut TX_BUFFER, + tx_descriptors, + &mut RX_BUFFER, + rx_descriptors, + ) + } + }}; + + ($size:expr, $chunk_size:expr) => { + $crate::dma_buffers_chunk_size!($size, $size, $chunk_size) + }; +} + +/// Convenience macro to create circular DMA buffers and descriptors with +/// specific chunk size +/// +/// ## Usage +/// ```rust,ignore +/// // TX and RX buffers are 32000 bytes - passing only one parameter makes TX and RX the same size +/// let (tx_buffer, tx_descriptors, rx_buffer, rx_descriptors) = +/// dma_circular_buffers!(32000, 32000, 4032); +/// ``` +#[macro_export] +macro_rules! dma_circular_buffers_chunk_size { + ($tx_size:expr, $rx_size:expr, $chunk_size:expr) => {{ + static mut TX_BUFFER: [u8; $tx_size] = [0u8; $tx_size]; + static mut RX_BUFFER: [u8; $rx_size] = [0u8; $rx_size]; + let (mut tx_descriptors, mut rx_descriptors) = + $crate::dma_circular_descriptors_chunk_size!($tx_size, $rx_size, $chunk_size); + unsafe { + ( + &mut TX_BUFFER, + tx_descriptors, + &mut RX_BUFFER, + rx_descriptors, + ) + } + }}; + + ($size:expr, $chunk_size:expr) => {{ + $crate::dma_circular_buffers_chunk_size!($size, $size, $chunk_size) + }}; +} + +/// Convenience macro to create DMA descriptors with specific chunk size +/// +/// ## Usage +/// ```rust,ignore +/// // Create TX and RX descriptors for transactions up to 32000 bytes - passing only one parameter assumes TX and RX are the same size +/// let (tx_descriptors, rx_descriptors) = dma_descriptors_chunk_size!(32000, 32000, 4032); +/// ``` +#[macro_export] +macro_rules! dma_descriptors_chunk_size { + ($tx_size:expr, $rx_size:expr, $chunk_size:expr) => {{ + // these will check for size at compile time + const _: () = assert!($chunk_size <= 4092, "chunk size must be <= 4092"); + const _: () = assert!($chunk_size > 0, "chunk size must be > 0"); + + static mut TX_DESCRIPTORS: [$crate::dma::DmaDescriptor; + ($tx_size + $chunk_size - 1) / $chunk_size] = + [$crate::dma::DmaDescriptor::EMPTY; ($tx_size + $chunk_size - 1) / $chunk_size]; + static mut RX_DESCRIPTORS: [$crate::dma::DmaDescriptor; + ($rx_size + $chunk_size - 1) / $chunk_size] = + [$crate::dma::DmaDescriptor::EMPTY; ($rx_size + $chunk_size - 1) / $chunk_size]; + unsafe { (&mut TX_DESCRIPTORS, &mut RX_DESCRIPTORS) } + }}; + + ($size:expr, $chunk_size:expr) => { + $crate::dma_descriptors_chunk_size!($size, $size, $chunk_size) + }; +} + +/// Convenience macro to create circular DMA descriptors with specific chunk +/// size +/// +/// ## Usage +/// ```rust,ignore +/// // Create TX and RX descriptors for transactions up to 32000 bytes - passing only one parameter assumes TX and RX are the same size +/// let (tx_descriptors, rx_descriptors) = dma_circular_descriptors!(32000, 32000, 4032); +/// ``` +#[macro_export] +macro_rules! dma_circular_descriptors_chunk_size { + ($tx_size:expr, $rx_size:expr, $chunk_size:expr) => {{ + // these will check for size at compile time + const _: () = assert!($chunk_size <= 4092, "chunk size must be <= 4092"); + const _: () = assert!($chunk_size > 0, "chunk size must be > 0"); + + const tx_descriptor_len: usize = if $tx_size > $chunk_size * 2 { + ($tx_size + $chunk_size - 1) / $chunk_size } else { 3 }; - const rx_descriptor_len: usize = if $rx_size > 4092 * 2 { - ($rx_size + 4091) / 4092 + const rx_descriptor_len: usize = if $rx_size > $chunk_size * 2 { + ($rx_size + $chunk_size - 1) / $chunk_size } else { 3 }; @@ -308,19 +340,9 @@ macro_rules! dma_circular_descriptors { unsafe { (&mut TX_DESCRIPTORS, &mut RX_DESCRIPTORS) } }}; - ($size:expr) => {{ - const descriptor_len: usize = if $size > 4092 * 2 { - ($size + 4091) / 4092 - } else { - 3 - }; - - static mut TX_DESCRIPTORS: [$crate::dma::DmaDescriptor; descriptor_len] = - [$crate::dma::DmaDescriptor::EMPTY; descriptor_len]; - static mut RX_DESCRIPTORS: [$crate::dma::DmaDescriptor; descriptor_len] = - [$crate::dma::DmaDescriptor::EMPTY; descriptor_len]; - unsafe { (&mut TX_DESCRIPTORS, &mut RX_DESCRIPTORS) } - }}; + ($size:expr, $chunk_size:expr) => { + $crate::dma_circular_descriptors_chunk_size!($size, $size, $chunk_size) + }; } /// DMA Errors @@ -342,6 +364,8 @@ pub enum DmaError { BufferTooSmall, /// Descriptors or buffers are not located in a supported memory region UnsupportedMemoryRegion, + /// Invalid DMA chunk size + InvalidChunkSize, } /// DMA Priorities @@ -497,11 +521,25 @@ pub trait PeripheralMarker {} #[derive(Debug)] pub struct DescriptorChain { pub(crate) descriptors: &'static mut [DmaDescriptor], + chunk_size: usize, } impl DescriptorChain { pub fn new(descriptors: &'static mut [DmaDescriptor]) -> Self { - Self { descriptors } + Self { + descriptors, + chunk_size: CHUNK_SIZE, + } + } + + pub fn new_with_chunk_size( + descriptors: &'static mut [DmaDescriptor], + chunk_size: usize, + ) -> Self { + Self { + descriptors, + chunk_size, + } } pub fn first_mut(&mut self) -> *mut DmaDescriptor { @@ -535,7 +573,7 @@ impl DescriptorChain { return Err(DmaError::UnsupportedMemoryRegion); } - if self.descriptors.len() < len.div_ceil(CHUNK_SIZE) { + if self.descriptors.len() < len.div_ceil(self.chunk_size) { return Err(DmaError::OutOfDescriptors); } @@ -545,8 +583,8 @@ impl DescriptorChain { self.descriptors.fill(DmaDescriptor::EMPTY); - let max_chunk_size = if !circular || len > CHUNK_SIZE * 2 { - CHUNK_SIZE + let max_chunk_size = if !circular || len > self.chunk_size * 2 { + self.chunk_size } else { len / 3 + len % 3 }; @@ -611,14 +649,14 @@ impl DescriptorChain { return Err(DmaError::BufferTooSmall); } - if self.descriptors.len() < len.div_ceil(CHUNK_SIZE) { + if self.descriptors.len() < len.div_ceil(self.chunk_size) { return Err(DmaError::OutOfDescriptors); } self.descriptors.fill(DmaDescriptor::EMPTY); - let max_chunk_size = if !circular || len > CHUNK_SIZE * 2 { - CHUNK_SIZE + let max_chunk_size = if !circular || len > self.chunk_size * 2 { + self.chunk_size } else { len / 3 + len % 3 }; @@ -976,6 +1014,10 @@ where fn init(&mut self, burst_mode: bool, priority: DmaPriority) { R::set_in_burstmode(burst_mode); R::set_in_priority(priority); + // clear the mem2mem mode to avoid failed DMA if this + // channel was previously used for a mem2mem transfer. + #[cfg(gdma)] + R::set_mem2mem_mode(false); } unsafe fn prepare_transfer_without_start( diff --git a/examples/src/bin/dma_mem2mem.rs b/examples/src/bin/dma_mem2mem.rs index a345cceec..6374d03c7 100644 --- a/examples/src/bin/dma_mem2mem.rs +++ b/examples/src/bin/dma_mem2mem.rs @@ -38,7 +38,8 @@ fn main() -> ! { #[cfg(not(any(feature = "esp32c2", feature = "esp32c3", feature = "esp32s3")))] let dma_peripheral = peripherals.MEM2MEM1; - let mut mem2mem = Mem2Mem::new(channel, dma_peripheral, tx_descriptors, rx_descriptors); + let mut mem2mem = + Mem2Mem::new(channel, dma_peripheral, tx_descriptors, rx_descriptors).unwrap(); for i in 0..core::mem::size_of_val(tx_buffer) { tx_buffer[i] = (i % 256) as u8; diff --git a/hil-test/Cargo.toml b/hil-test/Cargo.toml index e85189392..ba9ee469f 100644 --- a/hil-test/Cargo.toml +++ b/hil-test/Cargo.toml @@ -24,6 +24,10 @@ harness = false name = "delay" harness = false +[[test]] +name = "dma_macros" +harness = false + [[test]] name = "dma_mem2mem" harness = false diff --git a/hil-test/tests/dma_macros.rs b/hil-test/tests/dma_macros.rs new file mode 100644 index 000000000..d5193e406 --- /dev/null +++ b/hil-test/tests/dma_macros.rs @@ -0,0 +1,205 @@ +//! DMA Mem2Mem Tests + +//% CHIPS: esp32s3 esp32c2 esp32c3 esp32c6 esp32h2 + +#![no_std] +#![no_main] + +use defmt_rtt as _; +use esp_backtrace as _; + +const DATA_SIZE: usize = 1024 * 10; + +pub(crate) const fn compute_size(size: usize, chunk_size: usize) -> usize { + size.div_ceil(chunk_size) +} + +pub(crate) const fn compute_circular_size(size: usize, chunk_size: usize) -> usize { + if size > chunk_size * 2 { + size.div_ceil(chunk_size) + } else { + 3 + } +} + +#[cfg(test)] +#[embedded_test::tests] +mod tests { + use defmt::assert_eq; + + use super::*; + + #[init] + fn init() {} + + #[test] + fn test_dma_descriptors_same_size() { + use esp_hal::dma::CHUNK_SIZE; + let (tx_descriptors, rx_descriptors) = esp_hal::dma_descriptors!(DATA_SIZE); + assert_eq!(tx_descriptors.len(), rx_descriptors.len()); + assert_eq!(tx_descriptors.len(), compute_size(DATA_SIZE, CHUNK_SIZE)); + assert_eq!(rx_descriptors.len(), compute_size(DATA_SIZE, CHUNK_SIZE)); + } + + #[test] + fn test_dma_descriptors_different_size() { + use esp_hal::dma::CHUNK_SIZE; + const TX_SIZE: usize = DATA_SIZE; + const RX_SIZE: usize = DATA_SIZE / 2; + let (tx_descriptors, rx_descriptors) = esp_hal::dma_descriptors!(TX_SIZE, RX_SIZE); + assert_eq!(tx_descriptors.len(), compute_size(TX_SIZE, CHUNK_SIZE)); + assert_eq!(rx_descriptors.len(), compute_size(RX_SIZE, CHUNK_SIZE)); + } + + #[test] + fn test_dma_circular_descriptors_same_size() { + use esp_hal::dma::CHUNK_SIZE; + let (tx_descriptors, rx_descriptors) = esp_hal::dma_circular_descriptors!(DATA_SIZE); + assert_eq!(tx_descriptors.len(), rx_descriptors.len()); + assert_eq!( + tx_descriptors.len(), + compute_circular_size(DATA_SIZE, CHUNK_SIZE) + ); + assert_eq!( + rx_descriptors.len(), + compute_circular_size(DATA_SIZE, CHUNK_SIZE) + ); + } + + #[test] + fn test_dma_circular_descriptors_different_size() { + use esp_hal::dma::CHUNK_SIZE; + const TX_SIZE: usize = CHUNK_SIZE * 2; + const RX_SIZE: usize = DATA_SIZE / 2; + let (tx_descriptors, rx_descriptors) = esp_hal::dma_circular_descriptors!(TX_SIZE, RX_SIZE); + assert_eq!( + tx_descriptors.len(), + compute_circular_size(TX_SIZE, CHUNK_SIZE) + ); + assert_eq!( + rx_descriptors.len(), + compute_circular_size(RX_SIZE, CHUNK_SIZE) + ); + } + + #[test] + fn test_dma_buffers_same_size() { + use esp_hal::dma::CHUNK_SIZE; + let (tx_buffer, tx_descriptors, rx_buffer, rx_descriptors) = + esp_hal::dma_buffers!(DATA_SIZE); + assert_eq!(tx_buffer.len(), DATA_SIZE); + assert_eq!(rx_buffer.len(), DATA_SIZE); + assert_eq!(tx_descriptors.len(), rx_descriptors.len()); + assert_eq!(tx_descriptors.len(), compute_size(DATA_SIZE, CHUNK_SIZE)); + assert_eq!(rx_descriptors.len(), compute_size(DATA_SIZE, CHUNK_SIZE)); + } + + #[test] + fn test_dma_buffers_different_size() { + use esp_hal::dma::CHUNK_SIZE; + const TX_SIZE: usize = DATA_SIZE; + const RX_SIZE: usize = DATA_SIZE / 2; + + let (tx_buffer, tx_descriptors, rx_buffer, rx_descriptors) = + esp_hal::dma_buffers!(TX_SIZE, RX_SIZE); + assert_eq!(tx_buffer.len(), TX_SIZE); + assert_eq!(rx_buffer.len(), RX_SIZE); + assert_eq!(tx_descriptors.len(), compute_size(TX_SIZE, CHUNK_SIZE)); + assert_eq!(rx_descriptors.len(), compute_size(RX_SIZE, CHUNK_SIZE)); + } + + #[test] + fn test_dma_circular_buffers_same_size() { + use esp_hal::dma::CHUNK_SIZE; + let (tx_buffer, tx_descriptors, rx_buffer, rx_descriptors) = + esp_hal::dma_circular_buffers!(DATA_SIZE); + assert_eq!(tx_buffer.len(), DATA_SIZE); + assert_eq!(rx_buffer.len(), DATA_SIZE); + assert_eq!(tx_descriptors.len(), rx_descriptors.len()); + assert_eq!( + tx_descriptors.len(), + compute_circular_size(DATA_SIZE, CHUNK_SIZE) + ); + assert_eq!( + rx_descriptors.len(), + compute_circular_size(DATA_SIZE, CHUNK_SIZE) + ); + } + + #[test] + fn test_dma_circular_buffers_different_size() { + use esp_hal::dma::CHUNK_SIZE; + const TX_SIZE: usize = CHUNK_SIZE * 4; + const RX_SIZE: usize = CHUNK_SIZE * 2; + let (tx_buffer, tx_descriptors, rx_buffer, rx_descriptors) = + esp_hal::dma_circular_buffers!(TX_SIZE, RX_SIZE); + assert_eq!(tx_buffer.len(), TX_SIZE); + assert_eq!(rx_buffer.len(), RX_SIZE); + assert_eq!( + tx_descriptors.len(), + compute_circular_size(TX_SIZE, CHUNK_SIZE) + ); + assert_eq!( + rx_descriptors.len(), + compute_circular_size(RX_SIZE, CHUNK_SIZE) + ); + } + + #[test] + fn test_dma_descriptors_chunk_size_same_size() { + const CHUNK_SIZE: usize = 2048; + let (tx_descriptors, rx_descriptors) = + esp_hal::dma_descriptors_chunk_size!(DATA_SIZE, CHUNK_SIZE); + assert_eq!(tx_descriptors.len(), rx_descriptors.len()); + assert_eq!(tx_descriptors.len(), compute_size(DATA_SIZE, CHUNK_SIZE)); + assert_eq!(rx_descriptors.len(), compute_size(DATA_SIZE, CHUNK_SIZE)); + } + + #[test] + fn test_dma_descriptors_chunk_size_different_size() { + const CHUNK_SIZE: usize = 2048; + const TX_SIZE: usize = DATA_SIZE; + const RX_SIZE: usize = DATA_SIZE / 2; + let (tx_descriptors, rx_descriptors) = + esp_hal::dma_descriptors_chunk_size!(TX_SIZE, RX_SIZE, CHUNK_SIZE); + assert_eq!(tx_descriptors.len(), compute_size(TX_SIZE, CHUNK_SIZE)); + assert_eq!(rx_descriptors.len(), compute_size(RX_SIZE, CHUNK_SIZE)); + } + + #[test] + fn test_dma_circular_buffers_chunk_size_same_size() { + const CHUNK_SIZE: usize = 2048; + let (tx_buffer, tx_descriptors, rx_buffer, rx_descriptors) = + esp_hal::dma_circular_buffers_chunk_size!(DATA_SIZE, CHUNK_SIZE); + assert_eq!(tx_buffer.len(), DATA_SIZE); + assert_eq!(rx_buffer.len(), DATA_SIZE); + assert_eq!(tx_descriptors.len(), rx_descriptors.len()); + assert_eq!( + tx_descriptors.len(), + compute_circular_size(DATA_SIZE, CHUNK_SIZE) + ); + assert_eq!( + rx_descriptors.len(), + compute_circular_size(DATA_SIZE, CHUNK_SIZE) + ); + } + + #[test] + fn test_dma_circular_buffers_chunk_size_different_size() { + const CHUNK_SIZE: usize = 2048; + const TX_SIZE: usize = DATA_SIZE; + const RX_SIZE: usize = DATA_SIZE / 2; + let (tx_buffer, tx_descriptors, rx_buffer, rx_descriptors) = + esp_hal::dma_circular_buffers_chunk_size!(TX_SIZE, RX_SIZE, CHUNK_SIZE); + assert_eq!(tx_buffer.len(), TX_SIZE); + assert_eq!(rx_buffer.len(), RX_SIZE); + assert_eq!( + tx_descriptors.len(), + compute_circular_size(TX_SIZE, CHUNK_SIZE) + ); + assert_eq!( + rx_descriptors.len(), + compute_circular_size(RX_SIZE, CHUNK_SIZE) + ); + } +} diff --git a/hil-test/tests/dma_mem2mem.rs b/hil-test/tests/dma_mem2mem.rs index 3869f4a9e..4f31c75d1 100644 --- a/hil-test/tests/dma_mem2mem.rs +++ b/hil-test/tests/dma_mem2mem.rs @@ -9,8 +9,10 @@ use defmt_rtt as _; use esp_backtrace as _; use esp_hal::{ clock::ClockControl, - dma::{Dma, DmaPriority, Mem2Mem}, + dma::{Dma, DmaError, DmaPriority, Mem2Mem}, dma_buffers, + dma_buffers_chunk_size, + dma_descriptors, peripherals::Peripherals, system::SystemControl, }; @@ -42,18 +44,160 @@ mod tests { #[cfg(not(any(feature = "esp32c2", feature = "esp32c3", feature = "esp32s3")))] let dma_peripheral = peripherals.MEM2MEM1; - let mut mem2mem = Mem2Mem::new(channel, dma_peripheral, tx_descriptors, rx_descriptors); + let mut mem2mem = + Mem2Mem::new(channel, dma_peripheral, tx_descriptors, rx_descriptors).unwrap(); for i in 0..core::mem::size_of_val(tx_buffer) { tx_buffer[i] = (i % 256) as u8; } let dma_wait = mem2mem.start_transfer(&tx_buffer, &mut rx_buffer).unwrap(); dma_wait.wait().unwrap(); - // explicitly drop to insure the mem2mem bit is not left set as this causes - // subsequent dma tests to fail. - drop(mem2mem); for i in 0..core::mem::size_of_val(tx_buffer) { assert_eq!(rx_buffer[i], tx_buffer[i]); } } + + #[test] + fn test_internal_mem2mem_chunk_size() { + const CHUNK_SIZE: usize = 2048; + let peripherals = Peripherals::take(); + let system = SystemControl::new(peripherals.SYSTEM); + let _clocks = ClockControl::boot_defaults(system.clock_control).freeze(); + + let (tx_buffer, tx_descriptors, mut rx_buffer, rx_descriptors) = + dma_buffers_chunk_size!(DATA_SIZE, CHUNK_SIZE); + + let dma = Dma::new(peripherals.DMA); + let channel = dma.channel0.configure(false, DmaPriority::Priority0); + #[cfg(any(feature = "esp32c2", feature = "esp32c3", feature = "esp32s3"))] + let dma_peripheral = peripherals.SPI2; + #[cfg(not(any(feature = "esp32c2", feature = "esp32c3", feature = "esp32s3")))] + let dma_peripheral = peripherals.MEM2MEM1; + + 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; + } + let dma_wait = mem2mem.start_transfer(&tx_buffer, &mut rx_buffer).unwrap(); + dma_wait.wait().unwrap(); + for i in 0..core::mem::size_of_val(tx_buffer) { + assert_eq!(rx_buffer[i], tx_buffer[i]); + } + } + + #[test] + fn test_mem2mem_errors_zero_tx() { + use esp_hal::dma::CHUNK_SIZE; + + let peripherals = Peripherals::take(); + let system = SystemControl::new(peripherals.SYSTEM); + let _clocks = ClockControl::boot_defaults(system.clock_control).freeze(); + + let dma = Dma::new(peripherals.DMA); + let channel = dma.channel0.configure(false, DmaPriority::Priority0); + #[cfg(any(feature = "esp32c2", feature = "esp32c3", feature = "esp32s3"))] + let dma_peripheral = peripherals.SPI2; + #[cfg(not(any(feature = "esp32c2", feature = "esp32c3", feature = "esp32s3")))] + let dma_peripheral = peripherals.MEM2MEM1; + + let (tx_descriptors, rx_descriptors) = dma_descriptors!(0, 1024); + match Mem2Mem::new_with_chunk_size( + channel, + dma_peripheral, + tx_descriptors, + rx_descriptors, + CHUNK_SIZE, + ) { + Err(DmaError::OutOfDescriptors) => (), + _ => panic!("Expected OutOfDescriptors"), + } + } + + #[test] + fn test_mem2mem_errors_zero_rx() { + use esp_hal::dma::CHUNK_SIZE; + + let peripherals = Peripherals::take(); + let system = SystemControl::new(peripherals.SYSTEM); + let _clocks = ClockControl::boot_defaults(system.clock_control).freeze(); + + let dma = Dma::new(peripherals.DMA); + let channel = dma.channel0.configure(false, DmaPriority::Priority0); + #[cfg(any(feature = "esp32c2", feature = "esp32c3", feature = "esp32s3"))] + let dma_peripheral = peripherals.SPI2; + #[cfg(not(any(feature = "esp32c2", feature = "esp32c3", feature = "esp32s3")))] + let dma_peripheral = peripherals.MEM2MEM1; + + let (tx_descriptors, rx_descriptors) = dma_descriptors!(1024, 0); + match Mem2Mem::new_with_chunk_size( + channel, + dma_peripheral, + tx_descriptors, + rx_descriptors, + CHUNK_SIZE, + ) { + Err(DmaError::OutOfDescriptors) => (), + _ => panic!("Expected OutOfDescriptors"), + } + } + + #[test] + fn test_mem2mem_errors_chunk_size_too_small() { + let peripherals = Peripherals::take(); + let system = SystemControl::new(peripherals.SYSTEM); + let _clocks = ClockControl::boot_defaults(system.clock_control).freeze(); + + let dma = Dma::new(peripherals.DMA); + let channel = dma.channel0.configure(false, DmaPriority::Priority0); + #[cfg(any(feature = "esp32c2", feature = "esp32c3", feature = "esp32s3"))] + let dma_peripheral = peripherals.SPI2; + #[cfg(not(any(feature = "esp32c2", feature = "esp32c3", feature = "esp32s3")))] + let dma_peripheral = peripherals.MEM2MEM1; + + let (tx_descriptors, rx_descriptors) = dma_descriptors!(1024, 1024); + match Mem2Mem::new_with_chunk_size( + channel, + dma_peripheral, + tx_descriptors, + rx_descriptors, + 0, + ) { + Err(DmaError::InvalidChunkSize) => (), + _ => panic!("Expected InvalidChunkSize"), + } + } + + #[test] + fn test_mem2mem_errors_chunk_size_too_big() { + let peripherals = Peripherals::take(); + let system = SystemControl::new(peripherals.SYSTEM); + let _clocks = ClockControl::boot_defaults(system.clock_control).freeze(); + + let dma = Dma::new(peripherals.DMA); + let channel = dma.channel0.configure(false, DmaPriority::Priority0); + #[cfg(any(feature = "esp32c2", feature = "esp32c3", feature = "esp32s3"))] + let dma_peripheral = peripherals.SPI2; + #[cfg(not(any(feature = "esp32c2", feature = "esp32c3", feature = "esp32s3")))] + let dma_peripheral = peripherals.MEM2MEM1; + + let (tx_descriptors, rx_descriptors) = dma_descriptors!(1024, 1024); + match Mem2Mem::new_with_chunk_size( + channel, + dma_peripheral, + tx_descriptors, + rx_descriptors, + 4093, + ) { + Err(DmaError::InvalidChunkSize) => (), + _ => panic!("Expected InvalidChunkSize"), + } + } }