DMA example working

This commit is contained in:
Robin Müller 2024-07-02 23:28:07 +02:00
parent 988d6adcdc
commit d3deb8a467
Signed by: muellerr
GPG Key ID: A649FB78196E3849
3 changed files with 236 additions and 70 deletions

View File

@ -10,7 +10,7 @@ use embedded_hal::delay::DelayNs;
use panic_rtt_target as _; use panic_rtt_target as _;
use rtt_target::{rprintln, rtt_init_print}; use rtt_target::{rprintln, rtt_init_print};
use simple_examples::peb1; 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::pwm::CountdownTimer;
use va416xx_hal::{ use va416xx_hal::{
pac::{self, interrupt}, pac::{self, interrupt},
@ -20,6 +20,7 @@ use va416xx_hal::{
// Place the DMA control block in SRAM1 // Place the DMA control block in SRAM1
const DMA_CTRL_BLOCK_ADDR: u32 = 0x2000_0000; const DMA_CTRL_BLOCK_ADDR: u32 = 0x2000_0000;
static DMA_DONE_FLAG: Mutex<Cell<bool>> = Mutex::new(Cell::new(false)); static DMA_DONE_FLAG: Mutex<Cell<bool>> = Mutex::new(Cell::new(false));
static DMA_ACTIVE_FLAG: Mutex<Cell<bool>> = Mutex::new(Cell::new(false));
#[entry] #[entry]
fn main() -> ! { fn main() -> ! {
@ -40,60 +41,207 @@ fn main() -> ! {
.expect("error creating DMA"); .expect("error creating DMA");
let (mut dma0, _, _, _) = dma.split(); let (mut dma0, _, _, _) = dma.split();
let mut delay_ms = CountdownTimer::new(&mut dp.sysconfig, dp.tim0, &clocks); let mut delay_ms = CountdownTimer::new(&mut dp.sysconfig, dp.tim0, &clocks);
let mut src_buf: [u8; 64] = [0; 64]; let mut src_buf_8_bit: [u8; 65] = [0; 65];
let mut dest_buf: [u8; 64] = [0; 64]; let mut dest_buf_8_bit: [u8; 65] = [0; 65];
let dma_ctrl_block_ptr = DMA_CTRL_BLOCK_ADDR as *const DmaCtrlBlock; let mut src_buf_16_bit: [u16; 33] = [0; 33];
let dma_ctrl_block_ref = unsafe { &*dma_ctrl_block_ptr }; 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 { loop {
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<pac::Tim0>,
) {
(0..64).for_each(|i| { (0..64).for_each(|i| {
src_buf[i] = i as u8; src_buf[i] = i as u8;
}); });
cortex_m::interrupt::free(|cs| { cortex_m::interrupt::free(|cs| {
DMA_DONE_FLAG.borrow(cs).set(false); DMA_DONE_FLAG.borrow(cs).set(false);
}); });
dma0.prepare_mem_to_mem_transfer_8_bit(&src_buf, &mut dest_buf) 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"); .expect("error preparing transfer");
rprintln!("ch0 cfg: {:?}", dma_ctrl_block_ref.pri[0].cfg); // Enable all interrupts.
dma0.select_primary_structure();
// Safety: Not using mask based critical sections. // Safety: Not using mask based critical sections.
unsafe { unsafe {
dma0.enable_done_interrupt(); dma0.enable_done_interrupt();
dma0.enable_active_interrupt(); dma0.enable_active_interrupt();
}; };
// Enable the individual channel.
dma0.enable(); dma0.enable();
//dma0.sw_request(); // We still need to manually trigger the DMA request.
let state = dma0.state_raw(); dma0.trigger_with_sw_request();
rprintln!("dma state: {}", state);
// Use polling for completion status. // Use polling for completion status.
loop { loop {
let mut dma_done = false; let mut dma_done = false;
cortex_m::interrupt::free(|cs| { 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() { if DMA_DONE_FLAG.borrow(cs).get() {
dma_done = true; dma_done = true;
} }
}); });
if dma_done { if dma_done {
rprintln!("DMA transfer done"); rprintln!("8-bit transfer done");
break; break;
} }
let state = dma0.state_raw();
if state != 0 {
rprintln!("dma state: {}", state);
}
delay_ms.delay_ms(1); delay_ms.delay_ms(1);
} }
(0..64).for_each(|i| { (0..64).for_each(|i| {
assert_eq!(dest_buf[i], i as u8); assert_eq!(dest_buf[i], i as u8);
}); });
// Sentinel value, should be 0.
assert_eq!(dest_buf[64], 0);
dest_buf.fill(0); dest_buf.fill(0);
delay_ms.delay_ms(200);
} }
fn transfer_example_16_bit(
src_buf: &mut [u16; 33],
dest_buf: &mut [u16; 33],
dma0: &mut DmaChannel,
delay_ms: &mut CountdownTimer<pac::Tim0>,
) {
// 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<pac::Tim0>,
) {
// 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] #[interrupt]
#[allow(non_snake_case)] #[allow(non_snake_case)]
fn DMA_DONE0() { fn DMA_DONE0() {
rprintln!("dma done interrupt");
// Notify the main loop that the DMA transfer is finished. // Notify the main loop that the DMA transfer is finished.
cortex_m::interrupt::free(|cs| { cortex_m::interrupt::free(|cs| {
DMA_DONE_FLAG.borrow(cs).set(true); DMA_DONE_FLAG.borrow(cs).set(true);
@ -103,5 +251,8 @@ fn DMA_DONE0() {
#[interrupt] #[interrupt]
#[allow(non_snake_case)] #[allow(non_snake_case)]
fn DMA_ACTIVE0() { 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);
});
} }

View File

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

View File

@ -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::{ use crate::{
clock::{PeripheralClock, PeripheralSelect}, clock::{PeripheralClock, PeripheralSelect},
enable_interrupt, pac, 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 /// 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. /// 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> { pub fn new_at_addr(addr: u32) -> Result<*mut DmaCtrlBlock, InvalidCtrlBlockAddr> {
if addr & BASE_PTR_ADDR_MASK > 0 { if addr & BASE_PTR_ADDR_MASK > 0 {
return Err(InvalidCtrlBlockAddr); return Err(InvalidCtrlBlockAddr);
@ -206,7 +212,7 @@ impl DmaChannel {
} }
#[inline(always)] #[inline(always)]
pub fn sw_request(&mut self) { pub fn trigger_with_sw_request(&mut self) {
self.dma self.dma
.chnl_sw_request() .chnl_sw_request()
.write(|w| unsafe { w.bits(1 << self.idx) }); .write(|w| unsafe { w.bits(1 << self.idx) });
@ -249,6 +255,14 @@ impl DmaChannel {
enable_interrupt(self.active_interrupt); 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( pub fn prepare_mem_to_mem_transfer_8_bit(
&mut self, &mut self,
source: &[u8], source: &[u8],
@ -269,10 +283,18 @@ impl DmaChannel {
Ok(()) 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( pub fn prepare_mem_to_mem_transfer_16_bit(
&mut self, &mut self,
source: &[u8], source: &[u16],
dest: &mut [u8], dest: &mut [u16],
) -> Result<(), DmaTransferInitError> { ) -> Result<(), DmaTransferInitError> {
let len = Self::common_mem_transfer_checks(source.len(), dest.len())?; let len = Self::common_mem_transfer_checks(source.len(), dest.len())?;
self.generic_mem_to_mem_transfer_init( self.generic_mem_to_mem_transfer_init(
@ -289,6 +311,14 @@ impl DmaChannel {
Ok(()) 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( pub fn prepare_mem_to_mem_transfer_32_bit(
&mut self, &mut self,
source: &[u32], source: &[u32],
@ -349,8 +379,7 @@ impl DmaChannel {
impl Dma { impl Dma {
/// Create a new DMA instance. /// Create a new DMA instance.
/// ///
/// The user must ensure that the DMA control block is placed statically in some memory /// You can use [DmaCtrlBlock::new_at_addr] to create the DMA control block at a specific address.
/// which can be accessed by the system as well, for example the SRAM1 block.
pub fn new( pub fn new(
syscfg: &mut pac::Sysconfig, syscfg: &mut pac::Sysconfig,
dma: pac::Dma, 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) { 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. // Safety: The DMA channel API only operates on its respective channels.
( (
DmaChannel { DmaChannel {