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

Issue 2059 ringbuffers on gpdma #2302

Open
wants to merge 13 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
87 changes: 62 additions & 25 deletions embassy-stm32/src/dma/bdma.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
//! Basic Direct Memory Acccess (BDMA)

use core::future::Future;
use core::future::{poll_fn, Future};
use core::pin::Pin;
use core::sync::atomic::{fence, AtomicUsize, Ordering};
use core::task::{Context, Poll, Waker};
Expand Down Expand Up @@ -429,6 +429,40 @@ impl<'a, C: Channel> DmaCtrl for DmaCtrlImpl<'a, C> {
}
}

struct RingBuffer {}

impl RingBuffer {
fn is_running(ch: &pac::bdma::Ch) -> bool {
ch.cr().read().en()
}

fn request_stop(ch: &pac::bdma::Ch) {
ch.cr().modify(|w| {
w.set_circ(false);
});
}

async fn stop(ch: &pac::bdma::Ch, set_waker: &mut dyn FnMut(&Waker)) {
use core::sync::atomic::compiler_fence;

Self::request_stop(ch);

//wait until cr.susp reads as true
poll_fn(|cx| {
set_waker(cx.waker());

compiler_fence(Ordering::SeqCst);

if !Self::is_running(ch) {
Poll::Ready(())
} else {
Poll::Pending
}
})
.await
}
}

/// Ringbuffer for reading data using DMA circular mode.
pub struct ReadableRingBuffer<'a, C: Channel, W: Word> {
cr: regs::Cr,
Expand Down Expand Up @@ -506,6 +540,14 @@ impl<'a, C: Channel, W: Word> ReadableRingBuffer<'a, C, W> {
self.ringbuf.clear(&mut DmaCtrlImpl(self.channel.reborrow()));
}

/// This disables the circular DMA causing the DMA transfer to stop when the buffer is full.
pub async fn stop(&mut self) {
RingBuffer::stop(&self.channel.regs().ch(self.channel.num()), &mut |waker| {
self.set_waker(waker)
})
.await
}

/// Read elements from the ring buffer
/// Return a tuple of the length read and the length remaining in the buffer
/// If not all of the elements were read, then there will be some elements in the buffer remaining
Expand Down Expand Up @@ -555,25 +597,15 @@ impl<'a, C: Channel, W: Word> ReadableRingBuffer<'a, C, W> {
///
/// This doesn't immediately stop the transfer, you have to wait until [`is_running`](Self::is_running) returns false.
pub fn request_stop(&mut self) {
let ch = self.channel.regs().ch(self.channel.num());

// Disable the channel. Keep the IEs enabled so the irqs still fire.
// If the channel is enabled and transfer is not completed, we need to perform
// two separate write access to the CR register to disable the channel.
ch.cr().write(|w| {
w.set_teie(true);
w.set_htie(true);
w.set_tcie(true);
});
RingBuffer::request_stop(&self.channel.regs().ch(self.channel.num()));
}

/// Return whether DMA is still running.
///
/// If this returns `false`, it can be because either the transfer finished, or
/// it was requested to stop early with [`request_stop`](Self::request_stop).
pub fn is_running(&mut self) -> bool {
let ch = self.channel.regs().ch(self.channel.num());
ch.cr().read().en()
RingBuffer::is_running(&self.channel.regs().ch(self.channel.num()))
}
}

Expand Down Expand Up @@ -664,6 +696,21 @@ impl<'a, C: Channel, W: Word> WritableRingBuffer<'a, C, W> {
self.ringbuf.clear(&mut DmaCtrlImpl(self.channel.reborrow()));
}

/// Write elements directly to the raw buffer.
/// This can be used to fill the buffer before starting the DMA transfer.
#[allow(dead_code)]
pub fn write_immediate(&mut self, buf: &[W]) -> Result<(usize, usize), OverrunError> {
self.ringbuf.write_immediate(buf)
}

/// This will disable the circular DMA and wait for the current buffer to finish writing to the peripheral.
pub async fn stop(&mut self) {
RingBuffer::stop(&self.channel.regs().ch(self.channel.num()), &mut |waker| {
self.set_waker(waker)
})
.await
}

/// Write elements to the ring buffer
/// Return a tuple of the length written and the length remaining in the buffer
pub fn write(&mut self, buf: &[W]) -> Result<(usize, usize), OverrunError> {
Expand Down Expand Up @@ -700,25 +747,15 @@ impl<'a, C: Channel, W: Word> WritableRingBuffer<'a, C, W> {
///
/// This doesn't immediately stop the transfer, you have to wait until [`is_running`](Self::is_running) returns false.
pub fn request_stop(&mut self) {
let ch = self.channel.regs().ch(self.channel.num());

// Disable the channel. Keep the IEs enabled so the irqs still fire.
// If the channel is enabled and transfer is not completed, we need to perform
// two separate write access to the CR register to disable the channel.
ch.cr().write(|w| {
w.set_teie(true);
w.set_htie(true);
w.set_tcie(true);
});
RingBuffer::request_stop(&self.channel.regs().ch(self.channel.num()));
}

/// Return whether DMA is still running.
///
/// If this returns `false`, it can be because either the transfer finished, or
/// it was requested to stop early with [`request_stop`](Self::request_stop).
pub fn is_running(&mut self) -> bool {
let ch = self.channel.regs().ch(self.channel.num());
ch.cr().read().en()
RingBuffer::is_running(&self.channel.regs().ch(self.channel.num()))
}
}

Expand Down
62 changes: 59 additions & 3 deletions embassy-stm32/src/dma/dma.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
use core::future::Future;
use core::future::{poll_fn, Future};
use core::marker::PhantomData;
use core::pin::Pin;
use core::sync::atomic::{fence, AtomicUsize, Ordering};
Expand Down Expand Up @@ -667,6 +667,39 @@ impl<'a, C: Channel> DmaCtrl for DmaCtrlImpl<'a, C> {
}
}

struct RingBuffer {}

impl RingBuffer {
fn is_running(ch: &pac::dma::St) -> bool {
ch.cr().read().en()
}

fn request_stop(ch: &pac::dma::St) {
ch.cr().modify(|w| {
w.set_circ(false);
});
}

async fn stop(ch: &pac::dma::St, set_waker: &mut dyn FnMut(&Waker)) {
use core::sync::atomic::compiler_fence;

Self::request_stop(ch);

poll_fn(|cx| {
set_waker(cx.waker());

compiler_fence(Ordering::SeqCst);

if !Self::is_running(ch) {
Poll::Ready(())
} else {
Poll::Pending
}
})
.await
}
}

/// Ringbuffer for receiving data using DMA circular mode.
pub struct ReadableRingBuffer<'a, C: Channel, W: Word> {
cr: regs::Cr,
Expand Down Expand Up @@ -705,7 +738,7 @@ impl<'a, C: Channel, W: Word> ReadableRingBuffer<'a, C, W> {
w.set_minc(true);
w.set_pinc(false);
w.set_teie(true);
w.set_htie(options.half_transfer_ir);
w.set_htie(true);
w.set_tcie(true);
w.set_circ(true);
#[cfg(dma_v1)]
Expand Down Expand Up @@ -754,6 +787,14 @@ impl<'a, C: Channel, W: Word> ReadableRingBuffer<'a, C, W> {
ch.cr().write_value(self.cr);
}

/// Disables the circular DMA transfer. The transfer will complete on the next iteration
pub async fn stop(&mut self) {
RingBuffer::stop(&self.channel.regs().st(self.channel.num()), &mut |waker| {
self.set_waker(waker)
})
.await
}

/// Clear all data in the ring buffer.
pub fn clear(&mut self) {
self.ringbuf.clear(&mut DmaCtrlImpl(self.channel.reborrow()));
Expand Down Expand Up @@ -880,7 +921,7 @@ impl<'a, C: Channel, W: Word> WritableRingBuffer<'a, C, W> {
w.set_minc(true);
w.set_pinc(false);
w.set_teie(true);
w.set_htie(options.half_transfer_ir);
w.set_htie(true);
w.set_tcie(true);
w.set_circ(true);
#[cfg(dma_v1)]
Expand Down Expand Up @@ -929,11 +970,26 @@ impl<'a, C: Channel, W: Word> WritableRingBuffer<'a, C, W> {
ch.cr().write_value(self.cr);
}

/// Disables the circular DMA transfer. The transfer will complete on the next iteration
pub async fn stop(&mut self) {
RingBuffer::stop(&self.channel.regs().st(self.channel.num()), &mut |waker| {
self.set_waker(waker)
})
.await
}

/// Clear all data in the ring buffer.
pub fn clear(&mut self) {
self.ringbuf.clear(&mut DmaCtrlImpl(self.channel.reborrow()));
}

/// Write elements directly to the raw buffer.
/// This can be used to fill the buffer before starting the DMA transfer.
#[allow(dead_code)]
pub fn write_immediate(&mut self, buf: &[W]) -> Result<(usize, usize), OverrunError> {
self.ringbuf.write_immediate(buf)
}

/// Write elements from the ring buffer
/// Return a tuple of the length written and the length remaining in the buffer
pub fn write(&mut self, buf: &[W]) -> Result<(usize, usize), OverrunError> {
Expand Down
Loading