dma: add Mem2Mem to support memory to memory transfer (#1738)
* dma: add Mem2Mem to support memory to memory transfer * fmt * update CHANGELOG * removed some debugging * use "gdma" as the selector for support * fix empty else * clippy * Mem2Mem::new now accepts the peripheral to use * mark Mem2Mem::new() unsafe * fmt :-/ * add Mem2MemN values for gdma on non-esp32s3 tested on esp32c3,esp32c6 (will have an esp32h2 in a few days) * support the esp32c2 (esp8684) * DmaEligible trait providing dma peripheral value & safe constructor for Mem2Mem dma. * added hil-test for Mem2Mem * fmt dma_mem2mem test * remove `debug!()` * reset the mem2mem bit (mem_trans_en) in in_conf0 on drop
This commit is contained in:
parent
4f9dc960c6
commit
cc7077624c
@ -19,6 +19,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|||||||
- ESP32-S3: Expose optional HSYNC input in LCD_CAM (#1707)
|
- ESP32-S3: Expose optional HSYNC input in LCD_CAM (#1707)
|
||||||
- ESP32-C6: Support lp-core as wake-up source (#1723)
|
- ESP32-C6: Support lp-core as wake-up source (#1723)
|
||||||
- Add support for GPIO wake-up source (#1724)
|
- Add support for GPIO wake-up source (#1724)
|
||||||
|
- dma: add Mem2Mem to support memory to memory transfer (#1738)
|
||||||
- Add `uart` wake source (#1727)
|
- Add `uart` wake source (#1727)
|
||||||
|
|
||||||
### Fixed
|
### Fixed
|
||||||
|
|||||||
@ -88,6 +88,13 @@ impl<const N: u8> RegisterAccess for Channel<N> {
|
|||||||
// nothing special to be done here
|
// nothing special to be done here
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(gdma)]
|
||||||
|
fn set_mem2mem_mode(value: bool) {
|
||||||
|
Self::ch()
|
||||||
|
.in_conf0()
|
||||||
|
.modify(|_, w| w.mem_trans_en().bit(value));
|
||||||
|
}
|
||||||
|
|
||||||
fn set_out_burstmode(burst_mode: bool) {
|
fn set_out_burstmode(burst_mode: bool) {
|
||||||
Self::ch().out_conf0().modify(|_, w| {
|
Self::ch().out_conf0().modify(|_, w| {
|
||||||
w.out_data_burst_en()
|
w.out_data_burst_en()
|
||||||
@ -606,3 +613,151 @@ impl<'d> Dma<'d> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub use m2m::*;
|
||||||
|
mod m2m {
|
||||||
|
use embedded_dma::{ReadBuffer, WriteBuffer};
|
||||||
|
|
||||||
|
use crate::dma::{
|
||||||
|
dma_private::{DmaSupport, DmaSupportRx},
|
||||||
|
Channel,
|
||||||
|
ChannelTypes,
|
||||||
|
DescriptorChain,
|
||||||
|
DmaDescriptor,
|
||||||
|
DmaEligible,
|
||||||
|
DmaError,
|
||||||
|
DmaPeripheral,
|
||||||
|
DmaTransferRx,
|
||||||
|
RxPrivate,
|
||||||
|
TxPrivate,
|
||||||
|
};
|
||||||
|
|
||||||
|
/// DMA Memory to Memory pseudo-Peripheral
|
||||||
|
///
|
||||||
|
/// This is a pseudo-peripheral that allows for memory to memory transfers.
|
||||||
|
/// It is not a real peripheral, but a way to use the DMA engine for memory
|
||||||
|
/// to memory transfers.
|
||||||
|
pub struct Mem2Mem<'d, C, MODE>
|
||||||
|
where
|
||||||
|
C: ChannelTypes,
|
||||||
|
MODE: crate::Mode,
|
||||||
|
{
|
||||||
|
channel: Channel<'d, C, MODE>,
|
||||||
|
tx_chain: DescriptorChain,
|
||||||
|
rx_chain: DescriptorChain,
|
||||||
|
peripheral: DmaPeripheral,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'d, C, MODE> Mem2Mem<'d, C, MODE>
|
||||||
|
where
|
||||||
|
C: ChannelTypes,
|
||||||
|
MODE: crate::Mode,
|
||||||
|
{
|
||||||
|
/// Create a new Mem2Mem instance.
|
||||||
|
pub fn new(
|
||||||
|
mut 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),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Create a new Mem2Mem instance.
|
||||||
|
///
|
||||||
|
/// # Safety
|
||||||
|
///
|
||||||
|
/// You must insure that your not using DMA for the same peripheral and
|
||||||
|
/// that your the only one using the DmaPeripheral.
|
||||||
|
pub unsafe fn new_unsafe(
|
||||||
|
mut channel: Channel<'d, C, MODE>,
|
||||||
|
peripheral: DmaPeripheral,
|
||||||
|
tx_descriptors: &'static mut [DmaDescriptor],
|
||||||
|
rx_descriptors: &'static mut [DmaDescriptor],
|
||||||
|
) -> Self {
|
||||||
|
channel.tx.init_channel();
|
||||||
|
channel.rx.init_channel();
|
||||||
|
Mem2Mem {
|
||||||
|
channel,
|
||||||
|
peripheral,
|
||||||
|
tx_chain: DescriptorChain::new(tx_descriptors),
|
||||||
|
rx_chain: DescriptorChain::new(rx_descriptors),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Start a memory to memory transfer.
|
||||||
|
pub fn start_transfer<'t, TXBUF, RXBUF>(
|
||||||
|
&mut self,
|
||||||
|
tx_buffer: &'t TXBUF,
|
||||||
|
rx_buffer: &'t mut RXBUF,
|
||||||
|
) -> Result<DmaTransferRx<Self>, DmaError>
|
||||||
|
where
|
||||||
|
TXBUF: ReadBuffer<Word = u8>,
|
||||||
|
RXBUF: WriteBuffer<Word = u8>,
|
||||||
|
{
|
||||||
|
let (tx_ptr, tx_len) = unsafe { tx_buffer.read_buffer() };
|
||||||
|
let (rx_ptr, rx_len) = unsafe { rx_buffer.write_buffer() };
|
||||||
|
self.tx_chain.fill_for_tx(false, tx_ptr, tx_len)?;
|
||||||
|
self.rx_chain.fill_for_rx(false, rx_ptr, rx_len)?;
|
||||||
|
unsafe {
|
||||||
|
self.channel
|
||||||
|
.tx
|
||||||
|
.prepare_transfer_without_start(self.peripheral, &self.tx_chain)?;
|
||||||
|
self.channel
|
||||||
|
.rx
|
||||||
|
.prepare_transfer_without_start(self.peripheral, &self.rx_chain)?;
|
||||||
|
self.channel.rx.set_mem2mem_mode(true);
|
||||||
|
}
|
||||||
|
self.channel.tx.start_transfer()?;
|
||||||
|
self.channel.rx.start_transfer()?;
|
||||||
|
Ok(DmaTransferRx::new(self))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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,
|
||||||
|
MODE: crate::Mode,
|
||||||
|
{
|
||||||
|
fn peripheral_wait_dma(&mut self, _is_tx: bool, _is_rx: bool) {
|
||||||
|
while !self.channel.rx.is_done() {}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn peripheral_dma_stop(&mut self) {
|
||||||
|
unreachable!("unsupported")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'d, C, MODE> DmaSupportRx for Mem2Mem<'d, C, MODE>
|
||||||
|
where
|
||||||
|
C: ChannelTypes,
|
||||||
|
MODE: crate::Mode,
|
||||||
|
{
|
||||||
|
type RX = C::Rx<'d>;
|
||||||
|
|
||||||
|
fn rx(&mut self) -> &mut Self::RX {
|
||||||
|
&mut self.channel.rx
|
||||||
|
}
|
||||||
|
|
||||||
|
fn chain(&mut self) -> &mut DescriptorChain {
|
||||||
|
&mut self.tx_chain
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@ -49,7 +49,7 @@
|
|||||||
//! For convenience you can use the [crate::dma_buffers] macro.
|
//! For convenience you can use the [crate::dma_buffers] macro.
|
||||||
#![warn(missing_docs)]
|
#![warn(missing_docs)]
|
||||||
|
|
||||||
use core::{marker::PhantomData, ptr::addr_of_mut, sync::atomic::compiler_fence};
|
use core::{fmt::Debug, marker::PhantomData, ptr::addr_of_mut, sync::atomic::compiler_fence};
|
||||||
|
|
||||||
bitfield::bitfield! {
|
bitfield::bitfield! {
|
||||||
#[doc(hidden)]
|
#[doc(hidden)]
|
||||||
@ -63,8 +63,19 @@ bitfield::bitfield! {
|
|||||||
owner, set_owner: 31;
|
owner, set_owner: 31;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl Debug for DmaDescriptorFlags {
|
||||||
|
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
|
||||||
|
f.debug_struct("DmaDescriptorFlags")
|
||||||
|
.field("size", &self.size())
|
||||||
|
.field("length", &self.length())
|
||||||
|
.field("suc_eof", &self.suc_eof())
|
||||||
|
.field("owner", &self.owner())
|
||||||
|
.finish()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// A DMA transfer descriptor.
|
/// A DMA transfer descriptor.
|
||||||
#[derive(Clone, Copy)]
|
#[derive(Clone, Copy, Debug)]
|
||||||
pub struct DmaDescriptor {
|
pub struct DmaDescriptor {
|
||||||
pub(crate) flags: DmaDescriptorFlags,
|
pub(crate) flags: DmaDescriptorFlags,
|
||||||
pub(crate) buffer: *mut u8,
|
pub(crate) buffer: *mut u8,
|
||||||
@ -373,14 +384,20 @@ pub enum DmaPeripheral {
|
|||||||
Spi2 = 0,
|
Spi2 = 0,
|
||||||
#[cfg(any(pdma, esp32s3))]
|
#[cfg(any(pdma, esp32s3))]
|
||||||
Spi3 = 1,
|
Spi3 = 1,
|
||||||
|
#[cfg(any(esp32c6, esp32h2))]
|
||||||
|
Mem2Mem1 = 1,
|
||||||
#[cfg(any(esp32c3, esp32c6, esp32h2, esp32s3))]
|
#[cfg(any(esp32c3, esp32c6, esp32h2, esp32s3))]
|
||||||
Uhci0 = 2,
|
Uhci0 = 2,
|
||||||
#[cfg(any(esp32, esp32s2, esp32c3, esp32c6, esp32h2, esp32s3))]
|
#[cfg(any(esp32, esp32s2, esp32c3, esp32c6, esp32h2, esp32s3))]
|
||||||
I2s0 = 3,
|
I2s0 = 3,
|
||||||
#[cfg(any(esp32, esp32s3))]
|
#[cfg(any(esp32, esp32s3))]
|
||||||
I2s1 = 4,
|
I2s1 = 4,
|
||||||
|
#[cfg(any(esp32c6, esp32h2))]
|
||||||
|
Mem2Mem4 = 4,
|
||||||
#[cfg(esp32s3)]
|
#[cfg(esp32s3)]
|
||||||
LcdCam = 5,
|
LcdCam = 5,
|
||||||
|
#[cfg(any(esp32c6, esp32h2))]
|
||||||
|
Mem2Mem5 = 5,
|
||||||
#[cfg(not(esp32c2))]
|
#[cfg(not(esp32c2))]
|
||||||
Aes = 6,
|
Aes = 6,
|
||||||
#[cfg(gdma)]
|
#[cfg(gdma)]
|
||||||
@ -391,6 +408,18 @@ pub enum DmaPeripheral {
|
|||||||
Rmt = 9,
|
Rmt = 9,
|
||||||
#[cfg(parl_io)]
|
#[cfg(parl_io)]
|
||||||
ParlIo = 9,
|
ParlIo = 9,
|
||||||
|
#[cfg(any(esp32c6, esp32h2))]
|
||||||
|
Mem2Mem10 = 10,
|
||||||
|
#[cfg(any(esp32c6, esp32h2))]
|
||||||
|
Mem2Mem11 = 11,
|
||||||
|
#[cfg(any(esp32c6, esp32h2))]
|
||||||
|
Mem2Mem12 = 12,
|
||||||
|
#[cfg(any(esp32c6, esp32h2))]
|
||||||
|
Mem2Mem13 = 13,
|
||||||
|
#[cfg(any(esp32c6, esp32h2))]
|
||||||
|
Mem2Mem14 = 14,
|
||||||
|
#[cfg(any(esp32c6, esp32h2))]
|
||||||
|
Mem2Mem15 = 15,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(PartialEq, PartialOrd)]
|
#[derive(PartialEq, PartialOrd)]
|
||||||
@ -408,6 +437,16 @@ impl From<u32> for Owner {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Marks channels as useable for SPI
|
||||||
|
#[doc(hidden)]
|
||||||
|
pub trait DmaEligible {
|
||||||
|
/// The DMA peripheral
|
||||||
|
const DMA_PERIPHERAL: DmaPeripheral;
|
||||||
|
fn dma_peripheral(&self) -> DmaPeripheral {
|
||||||
|
Self::DMA_PERIPHERAL
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// Marks channels as useable for SPI
|
/// Marks channels as useable for SPI
|
||||||
#[doc(hidden)]
|
#[doc(hidden)]
|
||||||
pub trait SpiPeripheral: PeripheralMarker {}
|
pub trait SpiPeripheral: PeripheralMarker {}
|
||||||
@ -458,6 +497,7 @@ pub trait Tx: TxPrivate {}
|
|||||||
pub trait PeripheralMarker {}
|
pub trait PeripheralMarker {}
|
||||||
|
|
||||||
#[doc(hidden)]
|
#[doc(hidden)]
|
||||||
|
#[derive(Debug)]
|
||||||
pub struct DescriptorChain {
|
pub struct DescriptorChain {
|
||||||
pub(crate) descriptors: &'static mut [DmaDescriptor],
|
pub(crate) descriptors: &'static mut [DmaDescriptor],
|
||||||
}
|
}
|
||||||
@ -877,6 +917,9 @@ pub trait RxPrivate: crate::private::Sealed {
|
|||||||
|
|
||||||
fn start_transfer(&mut self) -> Result<(), DmaError>;
|
fn start_transfer(&mut self) -> Result<(), DmaError>;
|
||||||
|
|
||||||
|
#[cfg(gdma)]
|
||||||
|
fn set_mem2mem_mode(&mut self, value: bool);
|
||||||
|
|
||||||
fn listen_ch_in_done(&self);
|
fn listen_ch_in_done(&self);
|
||||||
|
|
||||||
fn clear_ch_in_done(&self);
|
fn clear_ch_in_done(&self);
|
||||||
@ -1042,6 +1085,11 @@ where
|
|||||||
self.rx_impl.start_transfer()
|
self.rx_impl.start_transfer()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(gdma)]
|
||||||
|
fn set_mem2mem_mode(&mut self, value: bool) {
|
||||||
|
R::set_mem2mem_mode(value);
|
||||||
|
}
|
||||||
|
|
||||||
fn listen_ch_in_done(&self) {
|
fn listen_ch_in_done(&self) {
|
||||||
R::listen_ch_in_done();
|
R::listen_ch_in_done();
|
||||||
}
|
}
|
||||||
@ -1411,6 +1459,8 @@ where
|
|||||||
#[doc(hidden)]
|
#[doc(hidden)]
|
||||||
pub trait RegisterAccess: crate::private::Sealed {
|
pub trait RegisterAccess: crate::private::Sealed {
|
||||||
fn init_channel();
|
fn init_channel();
|
||||||
|
#[cfg(gdma)]
|
||||||
|
fn set_mem2mem_mode(value: bool);
|
||||||
fn set_out_burstmode(burst_mode: bool);
|
fn set_out_burstmode(burst_mode: bool);
|
||||||
fn set_out_priority(priority: DmaPriority);
|
fn set_out_priority(priority: DmaPriority);
|
||||||
fn clear_out_interrupts();
|
fn clear_out_interrupts();
|
||||||
|
|||||||
@ -204,6 +204,16 @@ where
|
|||||||
impl<T> crate::private::Sealed for &mut T where T: crate::private::Sealed {}
|
impl<T> crate::private::Sealed for &mut T where T: crate::private::Sealed {}
|
||||||
|
|
||||||
mod peripheral_macros {
|
mod peripheral_macros {
|
||||||
|
#[doc(hidden)]
|
||||||
|
#[macro_export]
|
||||||
|
macro_rules! impl_dma_eligible {
|
||||||
|
($name:ident,$dma:ident) => {
|
||||||
|
impl $crate::dma::DmaEligible for $name {
|
||||||
|
const DMA_PERIPHERAL: $crate::dma::DmaPeripheral = $crate::dma::DmaPeripheral::$dma;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
#[doc(hidden)]
|
#[doc(hidden)]
|
||||||
#[macro_export]
|
#[macro_export]
|
||||||
macro_rules! peripherals {
|
macro_rules! peripherals {
|
||||||
@ -215,6 +225,49 @@ mod peripheral_macros {
|
|||||||
$(
|
$(
|
||||||
$crate::create_peripheral!($(#[$cfg])? $name <= $from_pac);
|
$crate::create_peripheral!($(#[$cfg])? $name <= $from_pac);
|
||||||
)*
|
)*
|
||||||
|
|
||||||
|
|
||||||
|
$crate::impl_dma_eligible!(SPI2,Spi2);
|
||||||
|
#[cfg(any(pdma, esp32s3))]
|
||||||
|
$crate::impl_dma_eligible!(SPI3,Spi3);
|
||||||
|
#[cfg(any(esp32c6, esp32h2))]
|
||||||
|
$crate::impl_dma_eligible!(MEM2MEM1,Mem2Mem1);
|
||||||
|
#[cfg(any(esp32c3, esp32c6, esp32h2, esp32s3))]
|
||||||
|
$crate::impl_dma_eligible!(UHCI0,Uhci0);
|
||||||
|
#[cfg(any(esp32, esp32s2, esp32c3, esp32c6, esp32h2, esp32s3))]
|
||||||
|
$crate::impl_dma_eligible!(I2S0,I2s0);
|
||||||
|
#[cfg(any(esp32, esp32s3))]
|
||||||
|
$crate::impl_dma_eligible!(I2S1,I2s1);
|
||||||
|
#[cfg(any(esp32c6, esp32h2))]
|
||||||
|
$crate::impl_dma_eligible!(MEM2MEM4,Mem2Mem4);
|
||||||
|
#[cfg(esp32s3)]
|
||||||
|
$crate::impl_dma_eligible!(LCD_CAM,LcdCam);
|
||||||
|
#[cfg(any(esp32c6, esp32h2))]
|
||||||
|
$crate::impl_dma_eligible!(MEM2MEM5,Mem2Mem5);
|
||||||
|
#[cfg(not(esp32c2))]
|
||||||
|
$crate::impl_dma_eligible!(AES,Aes);
|
||||||
|
#[cfg(gdma)]
|
||||||
|
$crate::impl_dma_eligible!(SHA,Sha);
|
||||||
|
#[cfg(any(esp32c3, esp32c6, esp32h2, esp32s3))]
|
||||||
|
$crate::impl_dma_eligible!(ADC1,Adc);
|
||||||
|
#[cfg(any(esp32c3, esp32s3))]
|
||||||
|
$crate::impl_dma_eligible!(ADC2,Adc);
|
||||||
|
#[cfg(esp32s3)]
|
||||||
|
$crate::impl_dma_eligible!(RMT,Rmt);
|
||||||
|
#[cfg(parl_io)]
|
||||||
|
$crate::impl_dma_eligible!(PARL_IO,ParlIo);
|
||||||
|
#[cfg(any(esp32c6, esp32h2))]
|
||||||
|
$crate::impl_dma_eligible!(MEM2MEM10,Mem2Mem10);
|
||||||
|
#[cfg(any(esp32c6, esp32h2))]
|
||||||
|
$crate::impl_dma_eligible!(MEM2MEM11,Mem2Mem11);
|
||||||
|
#[cfg(any(esp32c6, esp32h2))]
|
||||||
|
$crate::impl_dma_eligible!(MEM2MEM12,Mem2Mem12);
|
||||||
|
#[cfg(any(esp32c6, esp32h2))]
|
||||||
|
$crate::impl_dma_eligible!(MEM2MEM13,Mem2Mem13);
|
||||||
|
#[cfg(any(esp32c6, esp32h2))]
|
||||||
|
$crate::impl_dma_eligible!(MEM2MEM14,Mem2Mem14);
|
||||||
|
#[cfg(any(esp32c6, esp32h2))]
|
||||||
|
$crate::impl_dma_eligible!(MEM2MEM15,Mem2Mem15);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[allow(non_snake_case)]
|
#[allow(non_snake_case)]
|
||||||
|
|||||||
@ -48,4 +48,11 @@ crate::peripherals! {
|
|||||||
UART1 <= UART1,
|
UART1 <= UART1,
|
||||||
WIFI <= virtual,
|
WIFI <= virtual,
|
||||||
XTS_AES <= XTS_AES,
|
XTS_AES <= XTS_AES,
|
||||||
|
MEM2MEM1 <= virtual,
|
||||||
|
MEM2MEM2 <= virtual,
|
||||||
|
MEM2MEM3 <= virtual,
|
||||||
|
MEM2MEM4 <= virtual,
|
||||||
|
MEM2MEM5 <= virtual,
|
||||||
|
MEM2MEM6 <= virtual,
|
||||||
|
MEM2MEM8 <= virtual,
|
||||||
}
|
}
|
||||||
|
|||||||
@ -87,4 +87,13 @@ crate::peripherals! {
|
|||||||
UHCI0 <= UHCI0,
|
UHCI0 <= UHCI0,
|
||||||
USB_DEVICE <= USB_DEVICE,
|
USB_DEVICE <= USB_DEVICE,
|
||||||
WIFI <= virtual,
|
WIFI <= virtual,
|
||||||
|
MEM2MEM1 <= virtual,
|
||||||
|
MEM2MEM4 <= virtual,
|
||||||
|
MEM2MEM5 <= virtual,
|
||||||
|
MEM2MEM10 <= virtual,
|
||||||
|
MEM2MEM11 <= virtual,
|
||||||
|
MEM2MEM12 <= virtual,
|
||||||
|
MEM2MEM13 <= virtual,
|
||||||
|
MEM2MEM14 <= virtual,
|
||||||
|
MEM2MEM15 <= virtual,
|
||||||
}
|
}
|
||||||
|
|||||||
@ -77,4 +77,13 @@ crate::peripherals! {
|
|||||||
UART1 <= UART1,
|
UART1 <= UART1,
|
||||||
UHCI0 <= UHCI0,
|
UHCI0 <= UHCI0,
|
||||||
USB_DEVICE <= USB_DEVICE,
|
USB_DEVICE <= USB_DEVICE,
|
||||||
|
MEM2MEM1 <= virtual,
|
||||||
|
MEM2MEM4 <= virtual,
|
||||||
|
MEM2MEM5 <= virtual,
|
||||||
|
MEM2MEM10 <= virtual,
|
||||||
|
MEM2MEM11 <= virtual,
|
||||||
|
MEM2MEM12 <= virtual,
|
||||||
|
MEM2MEM13 <= virtual,
|
||||||
|
MEM2MEM14 <= virtual,
|
||||||
|
MEM2MEM15 <= virtual,
|
||||||
}
|
}
|
||||||
|
|||||||
78
examples/src/bin/dma_mem2mem.rs
Normal file
78
examples/src/bin/dma_mem2mem.rs
Normal file
@ -0,0 +1,78 @@
|
|||||||
|
//! Uses DMA to copy memory to memory.
|
||||||
|
|
||||||
|
//% FEATURES: esp-hal/log
|
||||||
|
//% CHIPS: esp32s3 esp32c2 esp32c3 esp32c6 esp32h2
|
||||||
|
|
||||||
|
#![no_std]
|
||||||
|
#![no_main]
|
||||||
|
|
||||||
|
use esp_backtrace as _;
|
||||||
|
use esp_hal::{
|
||||||
|
clock::ClockControl,
|
||||||
|
delay::Delay,
|
||||||
|
dma::{Dma, DmaPriority, Mem2Mem},
|
||||||
|
dma_buffers,
|
||||||
|
peripherals::Peripherals,
|
||||||
|
prelude::*,
|
||||||
|
system::SystemControl,
|
||||||
|
};
|
||||||
|
use log::{error, info};
|
||||||
|
|
||||||
|
const DATA_SIZE: usize = 1024 * 10;
|
||||||
|
|
||||||
|
#[entry]
|
||||||
|
fn main() -> ! {
|
||||||
|
esp_println::logger::init_logger(log::LevelFilter::Info);
|
||||||
|
|
||||||
|
let peripherals = Peripherals::take();
|
||||||
|
let system = SystemControl::new(peripherals.SYSTEM);
|
||||||
|
let clocks = ClockControl::boot_defaults(system.clock_control).freeze();
|
||||||
|
let delay = Delay::new(&clocks);
|
||||||
|
|
||||||
|
let (tx_buffer, tx_descriptors, mut rx_buffer, rx_descriptors) = dma_buffers!(DATA_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(channel, dma_peripheral, tx_descriptors, rx_descriptors);
|
||||||
|
|
||||||
|
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());
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -24,6 +24,10 @@ harness = false
|
|||||||
name = "delay"
|
name = "delay"
|
||||||
harness = false
|
harness = false
|
||||||
|
|
||||||
|
[[test]]
|
||||||
|
name = "dma_mem2mem"
|
||||||
|
harness = false
|
||||||
|
|
||||||
[[test]]
|
[[test]]
|
||||||
name = "ecc"
|
name = "ecc"
|
||||||
harness = false
|
harness = false
|
||||||
|
|||||||
59
hil-test/tests/dma_mem2mem.rs
Normal file
59
hil-test/tests/dma_mem2mem.rs
Normal file
@ -0,0 +1,59 @@
|
|||||||
|
//! DMA Mem2Mem Tests
|
||||||
|
|
||||||
|
//% CHIPS: esp32s3 esp32c2 esp32c3 esp32c6 esp32h2
|
||||||
|
|
||||||
|
#![no_std]
|
||||||
|
#![no_main]
|
||||||
|
|
||||||
|
use defmt_rtt as _;
|
||||||
|
use esp_backtrace as _;
|
||||||
|
use esp_hal::{
|
||||||
|
clock::ClockControl,
|
||||||
|
dma::{Dma, DmaPriority, Mem2Mem},
|
||||||
|
dma_buffers,
|
||||||
|
peripherals::Peripherals,
|
||||||
|
system::SystemControl,
|
||||||
|
};
|
||||||
|
|
||||||
|
const DATA_SIZE: usize = 1024 * 10;
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
#[embedded_test::tests]
|
||||||
|
mod tests {
|
||||||
|
use defmt::assert_eq;
|
||||||
|
|
||||||
|
use super::*;
|
||||||
|
|
||||||
|
#[init]
|
||||||
|
fn init() {}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_internal_mem2mem() {
|
||||||
|
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!(DATA_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(channel, dma_peripheral, tx_descriptors, rx_descriptors);
|
||||||
|
|
||||||
|
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]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
Loading…
Reference in New Issue
Block a user