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
This commit is contained in:
liebman 2024-07-15 06:00:33 -06:00 committed by GitHub
parent 7e981a5376
commit 1631f22083
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
7 changed files with 571 additions and 157 deletions

View File

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

View File

@ -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<Self, DmaError> {
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<Self, DmaError> {
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<Self, DmaError> {
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,

View File

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

View File

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

View File

@ -24,6 +24,10 @@ harness = false
name = "delay"
harness = false
[[test]]
name = "dma_macros"
harness = false
[[test]]
name = "dma_mem2mem"
harness = false

View File

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

View File

@ -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"),
}
}
}