diff --git a/examples/simple/examples/dma.rs b/examples/simple/examples/dma.rs index 4f44797..f437f08 100644 --- a/examples/simple/examples/dma.rs +++ b/examples/simple/examples/dma.rs @@ -10,7 +10,7 @@ use embedded_hal::delay::DelayNs; use panic_rtt_target as _; use rtt_target::{rprintln, rtt_init_print}; use simple_examples::peb1; -use va416xx_hal::dma::{Dma, DmaCfg, DmaCtrlBlock}; +use va416xx_hal::dma::{Dma, DmaCfg, DmaChannel, DmaCtrlBlock}; use va416xx_hal::pwm::CountdownTimer; use va416xx_hal::{ pac::{self, interrupt}, @@ -20,6 +20,7 @@ use va416xx_hal::{ // Place the DMA control block in SRAM1 const DMA_CTRL_BLOCK_ADDR: u32 = 0x2000_0000; static DMA_DONE_FLAG: Mutex> = Mutex::new(Cell::new(false)); +static DMA_ACTIVE_FLAG: Mutex> = Mutex::new(Cell::new(false)); #[entry] fn main() -> ! { @@ -40,60 +41,207 @@ fn main() -> ! { .expect("error creating DMA"); let (mut dma0, _, _, _) = dma.split(); let mut delay_ms = CountdownTimer::new(&mut dp.sysconfig, dp.tim0, &clocks); - let mut src_buf: [u8; 64] = [0; 64]; - let mut dest_buf: [u8; 64] = [0; 64]; - let dma_ctrl_block_ptr = DMA_CTRL_BLOCK_ADDR as *const DmaCtrlBlock; - let dma_ctrl_block_ref = unsafe { &*dma_ctrl_block_ptr }; + let mut src_buf_8_bit: [u8; 65] = [0; 65]; + let mut dest_buf_8_bit: [u8; 65] = [0; 65]; + let mut src_buf_16_bit: [u16; 33] = [0; 33]; + let mut dest_buf_16_bit: [u16; 33] = [0; 33]; + let mut src_buf_32_bit: [u32; 17] = [0; 17]; + let mut dest_buf_32_bit: [u32; 17] = [0; 17]; loop { - (0..64).for_each(|i| { - src_buf[i] = i as u8; - }); - cortex_m::interrupt::free(|cs| { - DMA_DONE_FLAG.borrow(cs).set(false); - }); - dma0.prepare_mem_to_mem_transfer_8_bit(&src_buf, &mut dest_buf) - .expect("error preparing transfer"); - rprintln!("ch0 cfg: {:?}", dma_ctrl_block_ref.pri[0].cfg); - dma0.select_primary_structure(); - // Safety: Not using mask based critical sections. - unsafe { - dma0.enable_done_interrupt(); - dma0.enable_active_interrupt(); - }; - dma0.enable(); - //dma0.sw_request(); - let state = dma0.state_raw(); - rprintln!("dma state: {}", state); - // Use polling for completion status. - loop { - let mut dma_done = false; - cortex_m::interrupt::free(|cs| { - if DMA_DONE_FLAG.borrow(cs).get() { - dma_done = true; - } - }); - if dma_done { - rprintln!("DMA transfer done"); - break; - } - let state = dma0.state_raw(); - if state != 0 { - rprintln!("dma state: {}", state); - } - delay_ms.delay_ms(1); - } - (0..64).for_each(|i| { - assert_eq!(dest_buf[i], i as u8); - }); - dest_buf.fill(0); - delay_ms.delay_ms(200); + transfer_example_8_bit( + &mut src_buf_8_bit, + &mut dest_buf_8_bit, + &mut dma0, + &mut delay_ms, + ); + delay_ms.delay_ms(500); + transfer_example_16_bit( + &mut src_buf_16_bit, + &mut dest_buf_16_bit, + &mut dma0, + &mut delay_ms, + ); + delay_ms.delay_ms(500); + transfer_example_32_bit( + &mut src_buf_32_bit, + &mut dest_buf_32_bit, + &mut dma0, + &mut delay_ms, + ); + delay_ms.delay_ms(500); } } +fn transfer_example_8_bit( + src_buf: &mut [u8; 65], + dest_buf: &mut [u8; 65], + dma0: &mut DmaChannel, + delay_ms: &mut CountdownTimer, +) { + (0..64).for_each(|i| { + src_buf[i] = i as u8; + }); + cortex_m::interrupt::free(|cs| { + DMA_DONE_FLAG.borrow(cs).set(false); + }); + cortex_m::interrupt::free(|cs| { + DMA_ACTIVE_FLAG.borrow(cs).set(false); + }); + 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. + unsafe { + dma0.enable_done_interrupt(); + dma0.enable_active_interrupt(); + }; + // Enable the individual channel. + dma0.enable(); + // We still need to manually trigger the DMA request. + dma0.trigger_with_sw_request(); + // Use polling for completion status. + loop { + let mut dma_done = false; + cortex_m::interrupt::free(|cs| { + if DMA_ACTIVE_FLAG.borrow(cs).get() { + rprintln!("DMA0 is active with 8 bit transfer"); + DMA_ACTIVE_FLAG.borrow(cs).set(false); + } + if DMA_DONE_FLAG.borrow(cs).get() { + dma_done = true; + } + }); + if dma_done { + rprintln!("8-bit transfer done"); + break; + } + delay_ms.delay_ms(1); + } + (0..64).for_each(|i| { + assert_eq!(dest_buf[i], i as u8); + }); + // Sentinel value, should be 0. + assert_eq!(dest_buf[64], 0); + dest_buf.fill(0); +} + +fn transfer_example_16_bit( + src_buf: &mut [u16; 33], + dest_buf: &mut [u16; 33], + dma0: &mut DmaChannel, + delay_ms: &mut CountdownTimer, +) { + // Set values scaled from 0 to 65535 to verify this is really a 16-bit transfer. + (0..32).for_each(|i| { + src_buf[i] = (i as u32 * u16::MAX as u32 / (src_buf.len() - 1) as u32) as u16; + }); + cortex_m::interrupt::free(|cs| { + DMA_DONE_FLAG.borrow(cs).set(false); + }); + cortex_m::interrupt::free(|cs| { + DMA_ACTIVE_FLAG.borrow(cs).set(false); + }); + dma0.prepare_mem_to_mem_transfer_16_bit(src_buf, dest_buf) + .expect("error preparing transfer"); + // Enable all interrupts. + // Safety: Not using mask based critical sections. + unsafe { + dma0.enable_done_interrupt(); + dma0.enable_active_interrupt(); + }; + // Enable the individual channel. + dma0.enable(); + // We still need to manually trigger the DMA request. + dma0.trigger_with_sw_request(); + // Use polling for completion status. + loop { + let mut dma_done = false; + cortex_m::interrupt::free(|cs| { + if DMA_ACTIVE_FLAG.borrow(cs).get() { + rprintln!("DMA0 is active with 16-bit transfer"); + DMA_ACTIVE_FLAG.borrow(cs).set(false); + } + if DMA_DONE_FLAG.borrow(cs).get() { + dma_done = true; + } + }); + if dma_done { + rprintln!("16-bit transfer done"); + break; + } + delay_ms.delay_ms(1); + } + (0..32).for_each(|i| { + assert_eq!( + dest_buf[i], + (i as u32 * u16::MAX as u32 / (src_buf.len() - 1) as u32) as u16 + ); + }); + // Sentinel value, should be 0. + assert_eq!(dest_buf[32], 0); + dest_buf.fill(0); +} + +fn transfer_example_32_bit( + src_buf: &mut [u32; 17], + dest_buf: &mut [u32; 17], + dma0: &mut DmaChannel, + delay_ms: &mut CountdownTimer, +) { + // Set values scaled from 0 to 65535 to verify this is really a 16-bit transfer. + (0..16).for_each(|i| { + src_buf[i] = (i as u64 * u32::MAX as u64 / (src_buf.len() - 1) as u64) as u32; + }); + cortex_m::interrupt::free(|cs| { + DMA_DONE_FLAG.borrow(cs).set(false); + }); + cortex_m::interrupt::free(|cs| { + DMA_ACTIVE_FLAG.borrow(cs).set(false); + }); + dma0.prepare_mem_to_mem_transfer_32_bit(src_buf, dest_buf) + .expect("error preparing transfer"); + // Enable all interrupts. + // Safety: Not using mask based critical sections. + unsafe { + dma0.enable_done_interrupt(); + dma0.enable_active_interrupt(); + }; + // Enable the individual channel. + dma0.enable(); + // We still need to manually trigger the DMA request. + dma0.trigger_with_sw_request(); + // Use polling for completion status. + loop { + let mut dma_done = false; + cortex_m::interrupt::free(|cs| { + if DMA_ACTIVE_FLAG.borrow(cs).get() { + rprintln!("DMA0 is active with 32-bit transfer"); + DMA_ACTIVE_FLAG.borrow(cs).set(false); + } + if DMA_DONE_FLAG.borrow(cs).get() { + dma_done = true; + } + }); + if dma_done { + rprintln!("32-bit transfer done"); + break; + } + delay_ms.delay_ms(1); + } + (0..16).for_each(|i| { + assert_eq!( + dest_buf[i], + (i as u64 * u32::MAX as u64 / (src_buf.len() - 1) as u64) as u32 + ); + }); + // Sentinel value, should be 0. + assert_eq!(dest_buf[16], 0); + dest_buf.fill(0); +} + #[interrupt] #[allow(non_snake_case)] fn DMA_DONE0() { - rprintln!("dma done interrupt"); // Notify the main loop that the DMA transfer is finished. cortex_m::interrupt::free(|cs| { DMA_DONE_FLAG.borrow(cs).set(true); @@ -103,5 +251,8 @@ fn DMA_DONE0() { #[interrupt] #[allow(non_snake_case)] fn DMA_ACTIVE0() { - rprintln!("dma active interrupt"); + // Notify the main loop that the DMA 0 is active now. + cortex_m::interrupt::free(|cs| { + DMA_ACTIVE_FLAG.borrow(cs).set(true); + }); } diff --git a/va416xx-hal/memory.x b/va416xx-hal/memory.x deleted file mode 100644 index cf01aa3..0000000 --- a/va416xx-hal/memory.x +++ /dev/null @@ -1,14 +0,0 @@ -MEMORY -{ - FLASH : ORIGIN = 0x00000000, LENGTH = 256K - /* RAM is a mandatory region. This RAM refers to the SRAM_0 */ - RAM : ORIGIN = 0x1FFF8000, LENGTH = 32K - SRAM_1 : ORIGIN = 0x20000000, LENGTH = 32K -} - -/* This is where the call stack will be allocated. */ -/* The stack is of the full descending type. */ -/* NOTE Do NOT modify `_stack_start` unless you know what you are doing */ -/* SRAM_0 can be used for all busses: Instruction, Data and System */ -/* SRAM_1 only supports the system bus */ -_stack_start = ORIGIN(RAM) + LENGTH(RAM) - 4; diff --git a/va416xx-hal/src/dma.rs b/va416xx-hal/src/dma.rs index 839f643..1c36fe9 100644 --- a/va416xx-hal/src/dma.rs +++ b/va416xx-hal/src/dma.rs @@ -1,3 +1,8 @@ +//! API for the DMA peripheral +//! +//! ## Examples +//! +//! - [Simple DMA example](https://egit.irs.uni-stuttgart.de/rust/va416xx-rs/src/branch/main/examples/simple/examples/dma.rs) use crate::{ clock::{PeripheralClock, PeripheralSelect}, enable_interrupt, pac, @@ -134,6 +139,7 @@ impl DmaCtrlBlock { /// /// The passed address must be 128-byte aligned. The user must also take care of specifying /// a valid memory address for the DMA control block which is accessible by the system as well. + /// For example, the control block can be placed in the SRAM1. pub fn new_at_addr(addr: u32) -> Result<*mut DmaCtrlBlock, InvalidCtrlBlockAddr> { if addr & BASE_PTR_ADDR_MASK > 0 { return Err(InvalidCtrlBlockAddr); @@ -206,7 +212,7 @@ impl DmaChannel { } #[inline(always)] - pub fn sw_request(&mut self) { + pub fn trigger_with_sw_request(&mut self) { self.dma .chnl_sw_request() .write(|w| unsafe { w.bits(1 << self.idx) }); @@ -249,6 +255,14 @@ impl DmaChannel { enable_interrupt(self.active_interrupt); } + /// Prepares a 8-bit DMA transfer from memory to memory. + /// + /// This function does not enable the DMA channel and interrupts and only prepares + /// the DMA control block parameters for the transfer. + /// + /// 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( &mut self, source: &[u8], @@ -269,10 +283,18 @@ impl DmaChannel { Ok(()) } + /// Prepares a 16-bit DMA transfer from memory to memory. + /// + /// This function does not enable the DMA channel and interrupts and only prepares + /// the DMA control block parameters for the transfer. + /// + /// 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( &mut self, - source: &[u8], - dest: &mut [u8], + source: &[u16], + dest: &mut [u16], ) -> Result<(), DmaTransferInitError> { let len = Self::common_mem_transfer_checks(source.len(), dest.len())?; self.generic_mem_to_mem_transfer_init( @@ -289,6 +311,14 @@ impl DmaChannel { Ok(()) } + /// Prepares a 32-bit DMA transfer from memory to memory. + /// + /// This function does not enable the DMA channel and interrupts and only prepares + /// the DMA control block parameters for the transfer. + /// + /// 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( &mut self, source: &[u32], @@ -349,8 +379,7 @@ impl DmaChannel { impl Dma { /// Create a new DMA instance. /// - /// The user must ensure that the DMA control block is placed statically in some memory - /// which can be accessed by the system as well, for example the SRAM1 block. + /// You can use [DmaCtrlBlock::new_at_addr] to create the DMA control block at a specific address. pub fn new( syscfg: &mut pac::Sysconfig, dma: pac::Dma, @@ -392,9 +421,9 @@ impl Dma { }); } - /// Split the DMA instance into four DMA channels which can be used individually. + /// Split the DMA instance into four DMA channels which can be used individually. This allows + /// using the inidividual DMA channels in separate tasks. pub fn split(self) -> (DmaChannel, DmaChannel, DmaChannel, DmaChannel) { - //let (pri, alt) = self.ctrl_block.split(); // Safety: The DMA channel API only operates on its respective channels. ( DmaChannel {