mirror of
https://github.com/esp-rs/esp-hal.git
synced 2025-10-02 14:44:42 +00:00
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:
parent
7e981a5376
commit
1631f22083
@ -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)
|
||||
|
||||
|
@ -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,
|
||||
|
@ -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(
|
||||
|
@ -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;
|
||||
|
@ -24,6 +24,10 @@ harness = false
|
||||
name = "delay"
|
||||
harness = false
|
||||
|
||||
[[test]]
|
||||
name = "dma_macros"
|
||||
harness = false
|
||||
|
||||
[[test]]
|
||||
name = "dma_mem2mem"
|
||||
harness = false
|
||||
|
205
hil-test/tests/dma_macros.rs
Normal file
205
hil-test/tests/dma_macros.rs
Normal 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)
|
||||
);
|
||||
}
|
||||
}
|
@ -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"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user