Merge pull request #1743 from xoviat/dma-2

stm32/dma: consolidate ringbuf
This commit is contained in:
Dario Nieuwenhuis 2023-08-10 15:21:31 +00:00 committed by GitHub
commit 95262ad559
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 120 additions and 131 deletions

View File

@ -393,6 +393,10 @@ impl<'a, C: Channel> DmaCtrl for DmaCtrlImpl<'a, C> {
fn reset_complete_count(&mut self) -> usize { fn reset_complete_count(&mut self) -> usize {
STATE.complete_count[self.0.index()].swap(0, Ordering::AcqRel) STATE.complete_count[self.0.index()].swap(0, Ordering::AcqRel)
} }
fn set_waker(&mut self, waker: &Waker) {
STATE.ch_wakers[self.0.index()].register(waker);
}
} }
pub struct ReadableRingBuffer<'a, C: Channel, W: Word> { pub struct ReadableRingBuffer<'a, C: Channel, W: Word> {
@ -463,7 +467,7 @@ impl<'a, C: Channel, W: Word> ReadableRingBuffer<'a, C, W> {
} }
pub fn clear(&mut self) { pub fn clear(&mut self) {
self.ringbuf.clear(DmaCtrlImpl(self.channel.reborrow())); self.ringbuf.clear(&mut DmaCtrlImpl(self.channel.reborrow()));
} }
/// Read elements from the ring buffer /// Read elements from the ring buffer
@ -472,7 +476,7 @@ impl<'a, C: Channel, W: Word> ReadableRingBuffer<'a, C, W> {
/// The length remaining is the capacity, ring_buf.len(), less the elements remaining after the read /// The length remaining is the capacity, ring_buf.len(), less the elements remaining after the read
/// OverrunError is returned if the portion to be read was overwritten by the DMA controller. /// OverrunError is returned if the portion to be read was overwritten by the DMA controller.
pub fn read(&mut self, buf: &mut [W]) -> Result<(usize, usize), OverrunError> { pub fn read(&mut self, buf: &mut [W]) -> Result<(usize, usize), OverrunError> {
self.ringbuf.read(DmaCtrlImpl(self.channel.reborrow()), buf) self.ringbuf.read(&mut DmaCtrlImpl(self.channel.reborrow()), buf)
} }
/// Read an exact number of elements from the ringbuffer. /// Read an exact number of elements from the ringbuffer.
@ -487,39 +491,18 @@ impl<'a, C: Channel, W: Word> ReadableRingBuffer<'a, C, W> {
/// - If M equals N/2 or N/2 divides evenly into M, this function will return every N/2 elements read on the DMA source. /// - If M equals N/2 or N/2 divides evenly into M, this function will return every N/2 elements read on the DMA source.
/// - Otherwise, this function may need up to N/2 extra elements to arrive before returning. /// - Otherwise, this function may need up to N/2 extra elements to arrive before returning.
pub async fn read_exact(&mut self, buffer: &mut [W]) -> Result<usize, OverrunError> { pub async fn read_exact(&mut self, buffer: &mut [W]) -> Result<usize, OverrunError> {
use core::future::poll_fn; self.ringbuf
use core::sync::atomic::compiler_fence; .read_exact(&mut DmaCtrlImpl(self.channel.reborrow()), buffer)
.await
let mut read_data = 0;
let buffer_len = buffer.len();
poll_fn(|cx| {
self.set_waker(cx.waker());
compiler_fence(Ordering::SeqCst);
match self.read(&mut buffer[read_data..buffer_len]) {
Ok((len, remaining)) => {
read_data += len;
if read_data == buffer_len {
Poll::Ready(Ok(remaining))
} else {
Poll::Pending
}
}
Err(e) => Poll::Ready(Err(e)),
}
})
.await
} }
/// The capacity of the ringbuffer. /// The capacity of the ringbuffer.
pub fn cap(&self) -> usize { pub const fn cap(&self) -> usize {
self.ringbuf.cap() self.ringbuf.cap()
} }
pub fn set_waker(&mut self, waker: &Waker) { pub fn set_waker(&mut self, waker: &Waker) {
STATE.ch_wakers[self.channel.index()].register(waker); DmaCtrlImpl(self.channel.reborrow()).set_waker(waker);
} }
fn clear_irqs(&mut self) { fn clear_irqs(&mut self) {
@ -628,50 +611,29 @@ impl<'a, C: Channel, W: Word> WritableRingBuffer<'a, C, W> {
} }
pub fn clear(&mut self) { pub fn clear(&mut self) {
self.ringbuf.clear(DmaCtrlImpl(self.channel.reborrow())); self.ringbuf.clear(&mut DmaCtrlImpl(self.channel.reborrow()));
} }
/// Write elements to the ring buffer /// Write elements to the ring buffer
/// Return a tuple of the length written and the length remaining in the 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> { pub fn write(&mut self, buf: &[W]) -> Result<(usize, usize), OverrunError> {
self.ringbuf.write(DmaCtrlImpl(self.channel.reborrow()), buf) self.ringbuf.write(&mut DmaCtrlImpl(self.channel.reborrow()), buf)
} }
/// Write an exact number of elements to the ringbuffer. /// Write an exact number of elements to the ringbuffer.
pub async fn write_exact(&mut self, buffer: &[W]) -> Result<usize, OverrunError> { pub async fn write_exact(&mut self, buffer: &[W]) -> Result<usize, OverrunError> {
use core::future::poll_fn; self.ringbuf
use core::sync::atomic::compiler_fence; .write_exact(&mut DmaCtrlImpl(self.channel.reborrow()), buffer)
.await
let mut written_data = 0;
let buffer_len = buffer.len();
poll_fn(|cx| {
self.set_waker(cx.waker());
compiler_fence(Ordering::SeqCst);
match self.write(&buffer[written_data..buffer_len]) {
Ok((len, remaining)) => {
written_data += len;
if written_data == buffer_len {
Poll::Ready(Ok(remaining))
} else {
Poll::Pending
}
}
Err(e) => Poll::Ready(Err(e)),
}
})
.await
} }
/// The capacity of the ringbuffer. /// The capacity of the ringbuffer.
pub fn cap(&self) -> usize { pub const fn cap(&self) -> usize {
self.ringbuf.cap() self.ringbuf.cap()
} }
pub fn set_waker(&mut self, waker: &Waker) { pub fn set_waker(&mut self, waker: &Waker) {
STATE.ch_wakers[self.channel.index()].register(waker); DmaCtrlImpl(self.channel.reborrow()).set_waker(waker);
} }
fn clear_irqs(&mut self) { fn clear_irqs(&mut self) {

View File

@ -623,6 +623,10 @@ impl<'a, C: Channel> DmaCtrl for DmaCtrlImpl<'a, C> {
fn reset_complete_count(&mut self) -> usize { fn reset_complete_count(&mut self) -> usize {
STATE.complete_count[self.0.index()].swap(0, Ordering::AcqRel) STATE.complete_count[self.0.index()].swap(0, Ordering::AcqRel)
} }
fn set_waker(&mut self, waker: &Waker) {
STATE.ch_wakers[self.0.index()].register(waker);
}
} }
pub struct ReadableRingBuffer<'a, C: Channel, W: Word> { pub struct ReadableRingBuffer<'a, C: Channel, W: Word> {
@ -708,7 +712,7 @@ impl<'a, C: Channel, W: Word> ReadableRingBuffer<'a, C, W> {
} }
pub fn clear(&mut self) { pub fn clear(&mut self) {
self.ringbuf.clear(DmaCtrlImpl(self.channel.reborrow())); self.ringbuf.clear(&mut DmaCtrlImpl(self.channel.reborrow()));
} }
/// Read elements from the ring buffer /// Read elements from the ring buffer
@ -717,7 +721,7 @@ impl<'a, C: Channel, W: Word> ReadableRingBuffer<'a, C, W> {
/// The length remaining is the capacity, ring_buf.len(), less the elements remaining after the read /// The length remaining is the capacity, ring_buf.len(), less the elements remaining after the read
/// OverrunError is returned if the portion to be read was overwritten by the DMA controller. /// OverrunError is returned if the portion to be read was overwritten by the DMA controller.
pub fn read(&mut self, buf: &mut [W]) -> Result<(usize, usize), OverrunError> { pub fn read(&mut self, buf: &mut [W]) -> Result<(usize, usize), OverrunError> {
self.ringbuf.read(DmaCtrlImpl(self.channel.reborrow()), buf) self.ringbuf.read(&mut DmaCtrlImpl(self.channel.reborrow()), buf)
} }
/// Read an exact number of elements from the ringbuffer. /// Read an exact number of elements from the ringbuffer.
@ -732,39 +736,18 @@ impl<'a, C: Channel, W: Word> ReadableRingBuffer<'a, C, W> {
/// - If M equals N/2 or N/2 divides evenly into M, this function will return every N/2 elements read on the DMA source. /// - If M equals N/2 or N/2 divides evenly into M, this function will return every N/2 elements read on the DMA source.
/// - Otherwise, this function may need up to N/2 extra elements to arrive before returning. /// - Otherwise, this function may need up to N/2 extra elements to arrive before returning.
pub async fn read_exact(&mut self, buffer: &mut [W]) -> Result<usize, OverrunError> { pub async fn read_exact(&mut self, buffer: &mut [W]) -> Result<usize, OverrunError> {
use core::future::poll_fn; self.ringbuf
use core::sync::atomic::compiler_fence; .read_exact(&mut DmaCtrlImpl(self.channel.reborrow()), buffer)
.await
let mut read_data = 0;
let buffer_len = buffer.len();
poll_fn(|cx| {
self.set_waker(cx.waker());
compiler_fence(Ordering::SeqCst);
match self.read(&mut buffer[read_data..buffer_len]) {
Ok((len, remaining)) => {
read_data += len;
if read_data == buffer_len {
Poll::Ready(Ok(remaining))
} else {
Poll::Pending
}
}
Err(e) => Poll::Ready(Err(e)),
}
})
.await
} }
// The capacity of the ringbuffer // The capacity of the ringbuffer
pub fn cap(&self) -> usize { pub const fn cap(&self) -> usize {
self.ringbuf.cap() self.ringbuf.cap()
} }
pub fn set_waker(&mut self, waker: &Waker) { pub fn set_waker(&mut self, waker: &Waker) {
STATE.ch_wakers[self.channel.index()].register(waker); DmaCtrlImpl(self.channel.reborrow()).set_waker(waker);
} }
fn clear_irqs(&mut self) { fn clear_irqs(&mut self) {
@ -890,50 +873,29 @@ impl<'a, C: Channel, W: Word> WritableRingBuffer<'a, C, W> {
} }
pub fn clear(&mut self) { pub fn clear(&mut self) {
self.ringbuf.clear(DmaCtrlImpl(self.channel.reborrow())); self.ringbuf.clear(&mut DmaCtrlImpl(self.channel.reborrow()));
} }
/// Write elements from the ring buffer /// Write elements from the ring buffer
/// Return a tuple of the length written and the length remaining in the 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> { pub fn write(&mut self, buf: &[W]) -> Result<(usize, usize), OverrunError> {
self.ringbuf.write(DmaCtrlImpl(self.channel.reborrow()), buf) self.ringbuf.write(&mut DmaCtrlImpl(self.channel.reborrow()), buf)
} }
/// Write an exact number of elements to the ringbuffer. /// Write an exact number of elements to the ringbuffer.
pub async fn write_exact(&mut self, buffer: &[W]) -> Result<usize, OverrunError> { pub async fn write_exact(&mut self, buffer: &[W]) -> Result<usize, OverrunError> {
use core::future::poll_fn; self.ringbuf
use core::sync::atomic::compiler_fence; .write_exact(&mut DmaCtrlImpl(self.channel.reborrow()), buffer)
.await
let mut written_data = 0;
let buffer_len = buffer.len();
poll_fn(|cx| {
self.set_waker(cx.waker());
compiler_fence(Ordering::SeqCst);
match self.write(&buffer[written_data..buffer_len]) {
Ok((len, remaining)) => {
written_data += len;
if written_data == buffer_len {
Poll::Ready(Ok(remaining))
} else {
Poll::Pending
}
}
Err(e) => Poll::Ready(Err(e)),
}
})
.await
} }
// The capacity of the ringbuffer // The capacity of the ringbuffer
pub fn cap(&self) -> usize { pub const fn cap(&self) -> usize {
self.ringbuf.cap() self.ringbuf.cap()
} }
pub fn set_waker(&mut self, waker: &Waker) { pub fn set_waker(&mut self, waker: &Waker) {
STATE.ch_wakers[self.channel.index()].register(waker); DmaCtrlImpl(self.channel.reborrow()).set_waker(waker);
} }
fn clear_irqs(&mut self) { fn clear_irqs(&mut self) {

View File

@ -1,7 +1,9 @@
#![cfg_attr(gpdma, allow(unused))] #![cfg_attr(gpdma, allow(unused))]
use core::future::poll_fn;
use core::ops::Range; use core::ops::Range;
use core::sync::atomic::{compiler_fence, Ordering}; use core::sync::atomic::{compiler_fence, Ordering};
use core::task::{Poll, Waker};
use super::word::Word; use super::word::Word;
@ -49,6 +51,9 @@ pub trait DmaCtrl {
/// Reset the transfer completed counter to 0 and return the value just prior to the reset. /// Reset the transfer completed counter to 0 and return the value just prior to the reset.
fn reset_complete_count(&mut self) -> usize; fn reset_complete_count(&mut self) -> usize;
/// Set the waker for a running poll_fn
fn set_waker(&mut self, waker: &Waker);
} }
impl<'a, W: Word> ReadableDmaRingBuffer<'a, W> { impl<'a, W: Word> ReadableDmaRingBuffer<'a, W> {
@ -57,7 +62,7 @@ impl<'a, W: Word> ReadableDmaRingBuffer<'a, W> {
} }
/// Reset the ring buffer to its initial state /// Reset the ring buffer to its initial state
pub fn clear(&mut self, mut dma: impl DmaCtrl) { pub fn clear(&mut self, dma: &mut impl DmaCtrl) {
self.start = 0; self.start = 0;
dma.reset_complete_count(); dma.reset_complete_count();
} }
@ -68,8 +73,43 @@ impl<'a, W: Word> ReadableDmaRingBuffer<'a, W> {
} }
/// The current position of the ringbuffer /// The current position of the ringbuffer
fn pos(&self, remaining_transfers: usize) -> usize { fn pos(&self, dma: &mut impl DmaCtrl) -> usize {
self.cap() - remaining_transfers self.cap() - dma.get_remaining_transfers()
}
/// Read an exact number of elements from the ringbuffer.
///
/// Returns the remaining number of elements available for immediate reading.
/// OverrunError is returned if the portion to be read was overwritten by the DMA controller.
///
/// Async/Wake Behavior:
/// The underlying DMA peripheral only can wake us when its buffer pointer has reached the halfway point,
/// and when it wraps around. This means that when called with a buffer of length 'M', when this
/// ring buffer was created with a buffer of size 'N':
/// - If M equals N/2 or N/2 divides evenly into M, this function will return every N/2 elements read on the DMA source.
/// - Otherwise, this function may need up to N/2 extra elements to arrive before returning.
pub async fn read_exact(&mut self, dma: &mut impl DmaCtrl, buffer: &mut [W]) -> Result<usize, OverrunError> {
let mut read_data = 0;
let buffer_len = buffer.len();
poll_fn(|cx| {
dma.set_waker(cx.waker());
compiler_fence(Ordering::SeqCst);
match self.read(dma, &mut buffer[read_data..buffer_len]) {
Ok((len, remaining)) => {
read_data += len;
if read_data == buffer_len {
Poll::Ready(Ok(remaining))
} else {
Poll::Pending
}
}
Err(e) => Poll::Ready(Err(e)),
}
})
.await
} }
/// Read elements from the ring buffer /// Read elements from the ring buffer
@ -77,7 +117,7 @@ impl<'a, W: Word> ReadableDmaRingBuffer<'a, W> {
/// If not all of the elements were read, then there will be some elements in the buffer remaining /// If not all of the elements were read, then there will be some elements in the buffer remaining
/// The length remaining is the capacity, ring_buf.len(), less the elements remaining after the read /// The length remaining is the capacity, ring_buf.len(), less the elements remaining after the read
/// OverrunError is returned if the portion to be read was overwritten by the DMA controller. /// OverrunError is returned if the portion to be read was overwritten by the DMA controller.
pub fn read(&mut self, mut dma: impl DmaCtrl, buf: &mut [W]) -> Result<(usize, usize), OverrunError> { pub fn read(&mut self, dma: &mut impl DmaCtrl, buf: &mut [W]) -> Result<(usize, usize), OverrunError> {
/* /*
This algorithm is optimistic: we assume we haven't overrun more than a full buffer and then check This algorithm is optimistic: we assume we haven't overrun more than a full buffer and then check
after we've done our work to see we have. This is because on stm32, an interrupt is not guaranteed after we've done our work to see we have. This is because on stm32, an interrupt is not guaranteed
@ -93,7 +133,7 @@ impl<'a, W: Word> ReadableDmaRingBuffer<'a, W> {
rather than the data we actually copied because it costs nothing and confirms an error condition rather than the data we actually copied because it costs nothing and confirms an error condition
earlier. earlier.
*/ */
let end = self.pos(dma.get_remaining_transfers()); let end = self.pos(dma);
if self.start == end && dma.get_complete_count() == 0 { if self.start == end && dma.get_complete_count() == 0 {
// No elements are available in the buffer // No elements are available in the buffer
Ok((0, self.cap())) Ok((0, self.cap()))
@ -114,8 +154,7 @@ impl<'a, W: Word> ReadableDmaRingBuffer<'a, W> {
then, get the current position of of the dma write and check then, get the current position of of the dma write and check
if it's inside data we could have copied if it's inside data we could have copied
*/ */
let (pos, complete_count) = let (pos, complete_count) = critical_section::with(|_| (self.pos(dma), dma.get_complete_count()));
critical_section::with(|_| (self.pos(dma.get_remaining_transfers()), dma.get_complete_count()));
if (pos >= self.start && pos < end) || (complete_count > 0 && pos >= end) || complete_count > 1 { if (pos >= self.start && pos < end) || (complete_count > 0 && pos >= end) || complete_count > 1 {
Err(OverrunError) Err(OverrunError)
} else { } else {
@ -141,7 +180,7 @@ impl<'a, W: Word> ReadableDmaRingBuffer<'a, W> {
then, get the current position of of the dma write and check then, get the current position of of the dma write and check
if it's inside data we could have copied if it's inside data we could have copied
*/ */
let pos = self.pos(dma.get_remaining_transfers()); let pos = self.pos(dma);
if pos > self.start || pos < end || dma.get_complete_count() > 1 { if pos > self.start || pos < end || dma.get_complete_count() > 1 {
Err(OverrunError) Err(OverrunError)
} else { } else {
@ -169,7 +208,7 @@ impl<'a, W: Word> ReadableDmaRingBuffer<'a, W> {
then, get the current position of of the dma write and check then, get the current position of of the dma write and check
if it's inside data we could have copied if it's inside data we could have copied
*/ */
let pos = self.pos(dma.get_remaining_transfers()); let pos = self.pos(dma);
if pos > self.start || pos < end || dma.reset_complete_count() > 1 { if pos > self.start || pos < end || dma.reset_complete_count() > 1 {
Err(OverrunError) Err(OverrunError)
} else { } else {
@ -209,7 +248,7 @@ impl<'a, W: Word> WritableDmaRingBuffer<'a, W> {
} }
/// Reset the ring buffer to its initial state /// Reset the ring buffer to its initial state
pub fn clear(&mut self, mut dma: impl DmaCtrl) { pub fn clear(&mut self, dma: &mut impl DmaCtrl) {
self.end = 0; self.end = 0;
dma.reset_complete_count(); dma.reset_complete_count();
} }
@ -220,14 +259,39 @@ impl<'a, W: Word> WritableDmaRingBuffer<'a, W> {
} }
/// The current position of the ringbuffer /// The current position of the ringbuffer
fn pos(&self, remaining_transfers: usize) -> usize { fn pos(&self, dma: &mut impl DmaCtrl) -> usize {
self.cap() - remaining_transfers self.cap() - dma.get_remaining_transfers()
}
/// Write an exact number of elements to the ringbuffer.
pub async fn write_exact(&mut self, dma: &mut impl DmaCtrl, buffer: &[W]) -> Result<usize, OverrunError> {
let mut written_data = 0;
let buffer_len = buffer.len();
poll_fn(|cx| {
dma.set_waker(cx.waker());
compiler_fence(Ordering::SeqCst);
match self.write(dma, &buffer[written_data..buffer_len]) {
Ok((len, remaining)) => {
written_data += len;
if written_data == buffer_len {
Poll::Ready(Ok(remaining))
} else {
Poll::Pending
}
}
Err(e) => Poll::Ready(Err(e)),
}
})
.await
} }
/// Write elements from the ring buffer /// Write elements from the ring buffer
/// Return a tuple of the length written and the capacity remaining to be written in the buffer /// Return a tuple of the length written and the capacity remaining to be written in the buffer
pub fn write(&mut self, mut dma: impl DmaCtrl, buf: &[W]) -> Result<(usize, usize), OverrunError> { pub fn write(&mut self, dma: &mut impl DmaCtrl, buf: &[W]) -> Result<(usize, usize), OverrunError> {
let start = self.pos(dma.get_remaining_transfers()); let start = self.pos(dma);
if start > self.end { if start > self.end {
// The occupied portion in the ring buffer DOES wrap // The occupied portion in the ring buffer DOES wrap
let len = self.copy_from(buf, self.end..start); let len = self.copy_from(buf, self.end..start);
@ -235,8 +299,7 @@ impl<'a, W: Word> WritableDmaRingBuffer<'a, W> {
compiler_fence(Ordering::SeqCst); compiler_fence(Ordering::SeqCst);
// Confirm that the DMA is not inside data we could have written // Confirm that the DMA is not inside data we could have written
let (pos, complete_count) = let (pos, complete_count) = critical_section::with(|_| (self.pos(dma), dma.get_complete_count()));
critical_section::with(|_| (self.pos(dma.get_remaining_transfers()), dma.get_complete_count()));
if (pos >= self.end && pos < start) || (complete_count > 0 && pos >= start) || complete_count > 1 { if (pos >= self.end && pos < start) || (complete_count > 0 && pos >= start) || complete_count > 1 {
Err(OverrunError) Err(OverrunError)
} else { } else {
@ -256,7 +319,7 @@ impl<'a, W: Word> WritableDmaRingBuffer<'a, W> {
compiler_fence(Ordering::SeqCst); compiler_fence(Ordering::SeqCst);
// Confirm that the DMA is not inside data we could have written // Confirm that the DMA is not inside data we could have written
let pos = self.pos(dma.get_remaining_transfers()); let pos = self.pos(dma);
if pos > self.end || pos < start || dma.get_complete_count() > 1 { if pos > self.end || pos < start || dma.get_complete_count() > 1 {
Err(OverrunError) Err(OverrunError)
} else { } else {
@ -274,7 +337,7 @@ impl<'a, W: Word> WritableDmaRingBuffer<'a, W> {
compiler_fence(Ordering::SeqCst); compiler_fence(Ordering::SeqCst);
// Confirm that the DMA is not inside data we could have written // Confirm that the DMA is not inside data we could have written
let pos = self.pos(dma.get_remaining_transfers()); let pos = self.pos(dma);
if pos > self.end || pos < start || dma.reset_complete_count() > 1 { if pos > self.end || pos < start || dma.reset_complete_count() > 1 {
Err(OverrunError) Err(OverrunError)
} else { } else {
@ -323,7 +386,7 @@ mod tests {
requests: cell::RefCell<vec::Vec<TestCircularTransferRequest>>, requests: cell::RefCell<vec::Vec<TestCircularTransferRequest>>,
} }
impl DmaCtrl for &mut TestCircularTransfer { impl DmaCtrl for TestCircularTransfer {
fn get_remaining_transfers(&self) -> usize { fn get_remaining_transfers(&self) -> usize {
match self.requests.borrow_mut().pop().unwrap() { match self.requests.borrow_mut().pop().unwrap() {
TestCircularTransferRequest::PositionRequest(pos) => { TestCircularTransferRequest::PositionRequest(pos) => {
@ -350,6 +413,8 @@ mod tests {
_ => unreachable!(), _ => unreachable!(),
} }
} }
fn set_waker(&mut self, waker: &Waker) {}
} }
impl TestCircularTransfer { impl TestCircularTransfer {