Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fast double-buffered DMA #226

Merged
merged 3 commits into from
Jun 7, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
9 changes: 8 additions & 1 deletion src/dma/bdma.rs
Original file line number Diff line number Diff line change
Expand Up @@ -288,11 +288,18 @@ macro_rules! bdma_stream {
}

#[inline(always)]
fn clear_transfer_complete_interrupt(&mut self) {
fn clear_transfer_complete_flag(&mut self) {
//NOTE(unsafe) Atomic write with no side-effects and we only access the bits
// that belongs to the StreamX
let dma = unsafe { &*I::ptr() };
dma.$ifcr.write(|w| w.$tcif().set_bit());
}

#[inline(always)]
fn clear_transfer_complete_interrupt(&mut self) {
self.clear_transfer_complete_flag();
//NOTE(unsafe) Atomic read with no side-effects.
let dma = unsafe { &*I::ptr() };
let _ = dma.$isr.read();
let _ = dma.$isr.read(); // Delay 2 peripheral clocks
}
Expand Down
9 changes: 8 additions & 1 deletion src/dma/dma.rs
Original file line number Diff line number Diff line change
Expand Up @@ -369,11 +369,18 @@ macro_rules! dma_stream {
}

#[inline(always)]
fn clear_transfer_complete_interrupt(&mut self) {
fn clear_transfer_complete_flag(&mut self) {
//NOTE(unsafe) Atomic write with no side-effects and we only access the bits
// that belongs to the StreamX
let dma = unsafe { &*I::ptr() };
dma.$ifcr.write(|w| w.$tcif().set_bit());
}

#[inline(always)]
fn clear_transfer_complete_interrupt(&mut self) {
self.clear_transfer_complete_flag();
//NOTE(unsafe) Atomic read with no side-effects.
let dma = unsafe { &*I::ptr() };
let _ = dma.$isr.read();
let _ = dma.$isr.read(); // Delay 2 peripheral clocks
}
Expand Down
44 changes: 44 additions & 0 deletions src/dma/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,8 @@ pub enum DMAError {
NotReady,
/// The user provided a buffer that is not big enough while double buffering.
SmallBuffer,
/// DMA started transfer on the inactive buffer while the user was processing it.
Overflow,
}

/// Possible DMA's directions.
Expand Down Expand Up @@ -602,6 +604,48 @@ macro_rules! db_transfer_def {
Ok((buf, current, last_remaining))
}

/// Wait for the transfer of the currently active buffer to complete,
/// then call a function on the now inactive buffer and acknowledge the
/// transfer complete flag.
///
/// NOTE(panic): This will panic then used in single buffer mode (not DBM).
///
/// NOTE(unsafe): Memory safety is not guaranteed.
/// The user must ensure that the user function called on the inactive
/// buffer completes before the running DMA transfer of the active buffer
/// completes. If the DMA wins the race to the inactive buffer
/// a `DMAError::Overflow` is returned but processing continues.
///
/// NOTE(fence): The user function must ensure buffer access ordering
/// against the flag accesses. Call
/// `core::sync::atomic::fence(core::sync::atomic::Ordering::SeqCst)`
/// before and after accessing the buffer.
pub unsafe fn next_dbm_transfer_with<F, T>(
&mut self,
func: F,
) -> Result<T, DMAError>
where
F: FnOnce(&mut BUF, CurrentBuffer) -> T,
{
while !STREAM::get_transfer_complete_flag() { }
self.stream.clear_transfer_complete_flag();

// NOTE(panic): Panic if stream not configured in double buffer mode.
let inactive = STREAM::get_inactive_buffer().unwrap();

// This buffer is inactive now and can be accessed.
// NOTE(panic): We always hold ownership in lieu of the DMA peripheral.
let buf = self.buf[inactive as usize].as_mut().unwrap();

let result = func(buf, inactive);

if STREAM::get_transfer_complete_flag() {
Err(DMAError::Overflow)
} else {
Ok(result)
}
}

/// Clear half transfer interrupt (htif) for the DMA stream.
#[inline(always)]
pub fn clear_half_transfer_interrupt(&mut self) {
Expand Down
8 changes: 7 additions & 1 deletion src/dma/traits.rs
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,13 @@ pub trait Stream: Sealed {
/// Clear all interrupts for the DMA stream.
fn clear_interrupts(&mut self);

/// Clear transfer complete interrupt (tcif) for the DMA stream.
/// Clear transfer complete interrupt flag (tcif) for the DMA stream
/// but do not insert artificial delays.
fn clear_transfer_complete_flag(&mut self);

/// Clear transfer complete interrupt (tcif) for the DMA stream and delay
/// to ensure the change has settled through the bridge, peripheral, and
/// synchronizers.
fn clear_transfer_complete_interrupt(&mut self);

/// Clear transfer error interrupt (teif) for the DMA stream.
Expand Down