From 6cd2e809d7b5deb66e4baca7066a5b445518734d Mon Sep 17 00:00:00 2001 From: Robin Mueller Date: Wed, 3 Jul 2024 11:21:48 +0200 Subject: [PATCH] DMA experimentation --- examples/simple/examples/dma.rs | 10 ++++++-- va416xx-hal/Cargo.toml | 1 + va416xx-hal/src/dma.rs | 45 +++++++++++++++++++++++++-------- 3 files changed, 44 insertions(+), 12 deletions(-) diff --git a/examples/simple/examples/dma.rs b/examples/simple/examples/dma.rs index de4d87f..7e72a44 100644 --- a/examples/simple/examples/dma.rs +++ b/examples/simple/examples/dma.rs @@ -92,7 +92,8 @@ fn transfer_example_8_bit( cortex_m::interrupt::free(|cs| { DMA_ACTIVE_FLAG.borrow(cs).set(false); }); - dma0.prepare_mem_to_mem_transfer_8_bit(src_buf, dest_buf) + let dma_transfer = dma0 + .prepare_mem_to_mem_transfer_8_bit(src_buf, dest_buf) .expect("error preparing transfer"); // Enable all interrupts. // Safety: Not using mask based critical sections. @@ -104,6 +105,7 @@ fn transfer_example_8_bit( dma0.enable(); // We still need to manually trigger the DMA request. dma0.trigger_with_sw_request(); + let dest_buf; // Use polling for completion status. loop { let mut dma_done = false; @@ -118,6 +120,8 @@ fn transfer_example_8_bit( }); if dma_done { rprintln!("8-bit transfer done"); + // Safety: We checked for transfer completion. + dest_buf = unsafe { dma_transfer.release() }; break; } delay_ms.delay_ms(1); @@ -146,8 +150,10 @@ fn transfer_example_16_bit( cortex_m::interrupt::free(|cs| { DMA_ACTIVE_FLAG.borrow(cs).set(false); }); - dma0.prepare_mem_to_mem_transfer_16_bit(src_buf, dest_buf) + let dma_transfer = dma0 + .prepare_mem_to_mem_transfer_16_bit(src_buf, dest_buf) .expect("error preparing transfer"); + dest_buf[5] = 2; // Enable all interrupts. // Safety: Not using mask based critical sections. unsafe { diff --git a/va416xx-hal/Cargo.toml b/va416xx-hal/Cargo.toml index b76fa40..c8c2b04 100644 --- a/va416xx-hal/Cargo.toml +++ b/va416xx-hal/Cargo.toml @@ -17,6 +17,7 @@ paste = "1" embedded-hal-nb = "1" embedded-hal = "1" embedded-io = "0.6" +embedded-dma = "0.2" num_enum = { version = "0.7", default-features = false } typenum = "1" bitflags = "2" diff --git a/va416xx-hal/src/dma.rs b/va416xx-hal/src/dma.rs index 2a295f9..13b4aba 100644 --- a/va416xx-hal/src/dma.rs +++ b/va416xx-hal/src/dma.rs @@ -3,6 +3,8 @@ //! ## Examples //! //! - [Simple DMA example](https://egit.irs.uni-stuttgart.de/rust/va416xx-rs/src/branch/main/examples/simple/examples/dma.rs) +use embedded_dma::WriteBuffer; + use crate::{ clock::{PeripheralClock, PeripheralSelect}, enable_interrupt, pac, @@ -203,6 +205,28 @@ pub struct DmaChannel { pub ch_ctrl_alt: &'static mut DmaChannelControl, } +/// This transfer structure takes ownership of the mutable destination slice. +/// +/// This avoids accidental violation of the ownership rules because the DMA now has mutable +/// access to that slice as well. The mutable slice can be retrieved after DMA transfer completion +/// by using the [Self::release] method. +pub struct DmaTransfer { + buf: W, + //ch: DmaChannel +} + +impl DmaTransfer { + /// Retrieve the mutable destination slice once the DMA transfer has completed. + /// + /// # Safety + /// + /// - The user MUST ensure that the DMA transfer has completed, for example by polling a + /// completion flag set by the DMA_DONE ISR. + pub unsafe fn release(self) -> W { + self.buf + } +} + impl DmaChannel { #[inline(always)] pub fn channel(&self) -> u8 { @@ -281,24 +305,25 @@ impl DmaChannel { /// You can use [Self::enable], [Self::enable_done_interrupt], [Self::enable_active_interrupt] /// to finish the transfer preparation and then use [Self::trigger_with_sw_request] to /// start the DMA transfer. - pub fn prepare_mem_to_mem_transfer_8_bit( + pub fn prepare_mem_to_mem_transfer_8_bit>( &mut self, source: &[u8], - dest: &mut [u8], - ) -> Result<(), DmaTransferInitError> { - let len = Self::common_mem_transfer_checks(source.len(), dest.len())?; + mut dest: W, + ) -> Result, DmaTransferInitError> { + let (write_ptr, len) = unsafe { dest.write_buffer() }; + let len = Self::common_mem_transfer_checks(source.len(), len)?; self.generic_mem_to_mem_transfer_init( len, (source.as_ptr() as u32) .checked_add(len as u32) .ok_or(DmaTransferInitError::AddrOverflow)?, - (dest.as_ptr() as u32) + (write_ptr as u32) .checked_add(len as u32) .ok_or(DmaTransferInitError::AddrOverflow)?, DataSize::Byte, AddrIncrement::Byte, ); - Ok(()) + Ok(DmaTransfer { buf: dest }) } /// Prepares a 16-bit DMA transfer from memory to memory. @@ -310,10 +335,10 @@ impl DmaChannel { /// You can use [Self::enable], [Self::enable_done_interrupt], [Self::enable_active_interrupt] /// to finish the transfer preparation and then use [Self::trigger_with_sw_request] to /// start the DMA transfer. - pub fn prepare_mem_to_mem_transfer_16_bit( + pub fn prepare_mem_to_mem_transfer_16_bit<'dest>( &mut self, source: &[u16], - dest: &mut [u16], + dest: &'dest mut [u16], ) -> Result<(), DmaTransferInitError> { let len = Self::common_mem_transfer_checks(source.len(), dest.len())?; self.generic_mem_to_mem_transfer_init( @@ -339,10 +364,10 @@ impl DmaChannel { /// You can use [Self::enable], [Self::enable_done_interrupt], [Self::enable_active_interrupt] /// to finish the transfer preparation and then use [Self::trigger_with_sw_request] to /// start the DMA transfer. - pub fn prepare_mem_to_mem_transfer_32_bit( + pub fn prepare_mem_to_mem_transfer_32_bit<'dest>( &mut self, source: &[u32], - dest: &mut [u32], + dest: &'dest mut [u32], ) -> Result<(), DmaTransferInitError> { let len = Self::common_mem_transfer_checks(source.len(), dest.len())?; self.generic_mem_to_mem_transfer_init(