From bae31ebce7f5cf30a24e0af2157f962feb74f72c Mon Sep 17 00:00:00 2001 From: xoviat Date: Sat, 29 Jul 2023 19:25:18 -0500 Subject: [PATCH 01/11] stm32/dma: rename ringbuf --- embassy-stm32/src/dma/bdma.rs | 16 ++++++++-------- embassy-stm32/src/dma/dma.rs | 12 ++++++------ embassy-stm32/src/dma/ringbuffer.rs | 18 +++++++++--------- embassy-stm32/src/usart/ringbuffered.rs | 6 +++--- 4 files changed, 26 insertions(+), 26 deletions(-) diff --git a/embassy-stm32/src/dma/bdma.rs b/embassy-stm32/src/dma/bdma.rs index d956047d..1dad364b 100644 --- a/embassy-stm32/src/dma/bdma.rs +++ b/embassy-stm32/src/dma/bdma.rs @@ -9,7 +9,7 @@ use atomic_polyfill::AtomicUsize; use embassy_hal_internal::{into_ref, Peripheral, PeripheralRef}; use embassy_sync::waitqueue::AtomicWaker; -use super::ringbuffer::{DmaCtrl, DmaRingBuffer, OverrunError}; +use super::ringbuffer::{DmaCtrl, OverrunError, ReadableDmaRingBuffer}; use super::word::{Word, WordSize}; use super::Dir; use crate::_generated::BDMA_CHANNEL_COUNT; @@ -395,13 +395,13 @@ impl<'a, C: Channel> DmaCtrl for DmaCtrlImpl<'a, C> { } } -pub struct RingBuffer<'a, C: Channel, W: Word> { +pub struct ReadableRingBuffer<'a, C: Channel, W: Word> { cr: regs::Cr, channel: PeripheralRef<'a, C>, - ringbuf: DmaRingBuffer<'a, W>, + ringbuf: ReadableDmaRingBuffer<'a, W>, } -impl<'a, C: Channel, W: Word> RingBuffer<'a, C, W> { +impl<'a, C: Channel, W: Word> ReadableRingBuffer<'a, C, W> { pub unsafe fn new_read( channel: impl Peripheral

+ 'a, _request: Request, @@ -442,7 +442,7 @@ impl<'a, C: Channel, W: Word> RingBuffer<'a, C, W> { let mut this = Self { channel, cr: w, - ringbuf: DmaRingBuffer::new(buffer), + ringbuf: ReadableDmaRingBuffer::new(buffer), }; this.clear_irqs(); @@ -475,7 +475,7 @@ impl<'a, C: Channel, W: Word> RingBuffer<'a, C, W> { self.ringbuf.read(DmaCtrlImpl(self.channel.reborrow()), buf) } - /// Read an exact number of elements from the ringbuffer. + /// Read an exact number of elements from the ReadableRingBuffer. /// /// 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. @@ -513,7 +513,7 @@ impl<'a, C: Channel, W: Word> RingBuffer<'a, C, W> { .await } - /// The capacity of the ringbuffer + /// The capacity of the ReadableRingBuffer pub fn cap(&self) -> usize { self.ringbuf.cap() } @@ -550,7 +550,7 @@ impl<'a, C: Channel, W: Word> RingBuffer<'a, C, W> { } } -impl<'a, C: Channel, W: Word> Drop for RingBuffer<'a, C, W> { +impl<'a, C: Channel, W: Word> Drop for ReadableRingBuffer<'a, C, W> { fn drop(&mut self) { self.request_stop(); while self.is_running() {} diff --git a/embassy-stm32/src/dma/dma.rs b/embassy-stm32/src/dma/dma.rs index 219ef2eb..9157f72d 100644 --- a/embassy-stm32/src/dma/dma.rs +++ b/embassy-stm32/src/dma/dma.rs @@ -7,7 +7,7 @@ use core::task::{Context, Poll, Waker}; use embassy_hal_internal::{into_ref, Peripheral, PeripheralRef}; use embassy_sync::waitqueue::AtomicWaker; -use super::ringbuffer::{DmaCtrl, DmaRingBuffer, OverrunError}; +use super::ringbuffer::{DmaCtrl, OverrunError, ReadableDmaRingBuffer}; use super::word::{Word, WordSize}; use super::Dir; use crate::_generated::DMA_CHANNEL_COUNT; @@ -625,13 +625,13 @@ impl<'a, C: Channel> DmaCtrl for DmaCtrlImpl<'a, C> { } } -pub struct RingBuffer<'a, C: Channel, W: Word> { +pub struct ReadableRingBuffer<'a, C: Channel, W: Word> { cr: regs::Cr, channel: PeripheralRef<'a, C>, - ringbuf: DmaRingBuffer<'a, W>, + ringbuf: ReadableDmaRingBuffer<'a, W>, } -impl<'a, C: Channel, W: Word> RingBuffer<'a, C, W> { +impl<'a, C: Channel, W: Word> ReadableRingBuffer<'a, C, W> { pub unsafe fn new_read( channel: impl Peripheral

+ 'a, _request: Request, @@ -677,7 +677,7 @@ impl<'a, C: Channel, W: Word> RingBuffer<'a, C, W> { let mut this = Self { channel, cr: w, - ringbuf: DmaRingBuffer::new(buffer), + ringbuf: ReadableDmaRingBuffer::new(buffer), }; this.clear_irqs(); @@ -797,7 +797,7 @@ impl<'a, C: Channel, W: Word> RingBuffer<'a, C, W> { } } -impl<'a, C: Channel, W: Word> Drop for RingBuffer<'a, C, W> { +impl<'a, C: Channel, W: Word> Drop for ReadableRingBuffer<'a, C, W> { fn drop(&mut self) { self.request_stop(); while self.is_running() {} diff --git a/embassy-stm32/src/dma/ringbuffer.rs b/embassy-stm32/src/dma/ringbuffer.rs index 19079397..92be3334 100644 --- a/embassy-stm32/src/dma/ringbuffer.rs +++ b/embassy-stm32/src/dma/ringbuffer.rs @@ -29,7 +29,7 @@ use super::word::Word; /// | | | | /// +- end --------------------+ +- start ----------------+ /// ``` -pub struct DmaRingBuffer<'a, W: Word> { +pub struct ReadableDmaRingBuffer<'a, W: Word> { pub(crate) dma_buf: &'a mut [W], start: usize, } @@ -51,7 +51,7 @@ pub trait DmaCtrl { fn reset_complete_count(&mut self) -> usize; } -impl<'a, W: Word> DmaRingBuffer<'a, W> { +impl<'a, W: Word> ReadableDmaRingBuffer<'a, W> { pub fn new(dma_buf: &'a mut [W]) -> Self { Self { dma_buf, start: 0 } } @@ -263,7 +263,7 @@ mod tests { #[test] fn empty_and_read_not_started() { let mut dma_buf = [0u8; 16]; - let ringbuf = DmaRingBuffer::new(&mut dma_buf); + let ringbuf = ReadableDmaRingBuffer::new(&mut dma_buf); assert_eq!(0, ringbuf.start); } @@ -273,7 +273,7 @@ mod tests { let mut dma = TestCircularTransfer::new(16); let mut dma_buf: [u8; 16] = array::from_fn(|idx| idx as u8); // 0, 1, ..., 15 - let mut ringbuf = DmaRingBuffer::new(&mut dma_buf); + let mut ringbuf = ReadableDmaRingBuffer::new(&mut dma_buf); assert_eq!(0, ringbuf.start); assert_eq!(16, ringbuf.cap()); @@ -314,7 +314,7 @@ mod tests { let mut dma = TestCircularTransfer::new(16); let mut dma_buf: [u8; 16] = array::from_fn(|idx| idx as u8); // 0, 1, ..., 15 - let mut ringbuf = DmaRingBuffer::new(&mut dma_buf); + let mut ringbuf = ReadableDmaRingBuffer::new(&mut dma_buf); assert_eq!(0, ringbuf.start); assert_eq!(16, ringbuf.cap()); @@ -349,7 +349,7 @@ mod tests { let mut dma = TestCircularTransfer::new(16); let mut dma_buf: [u8; 16] = array::from_fn(|idx| idx as u8); // 0, 1, ..., 15 - let mut ringbuf = DmaRingBuffer::new(&mut dma_buf); + let mut ringbuf = ReadableDmaRingBuffer::new(&mut dma_buf); assert_eq!(0, ringbuf.start); assert_eq!(16, ringbuf.cap()); @@ -384,7 +384,7 @@ mod tests { let mut dma = TestCircularTransfer::new(16); let mut dma_buf: [u8; 16] = array::from_fn(|idx| idx as u8); // 0, 1, ..., 15 - let mut ringbuf = DmaRingBuffer::new(&mut dma_buf); + let mut ringbuf = ReadableDmaRingBuffer::new(&mut dma_buf); assert_eq!(0, ringbuf.start); assert_eq!(16, ringbuf.cap()); @@ -420,7 +420,7 @@ mod tests { let mut dma = TestCircularTransfer::new(16); let mut dma_buf: [u8; 16] = array::from_fn(|idx| idx as u8); // 0, 1, ..., 15 - let mut ringbuf = DmaRingBuffer::new(&mut dma_buf); + let mut ringbuf = ReadableDmaRingBuffer::new(&mut dma_buf); assert_eq!(0, ringbuf.start); assert_eq!(16, ringbuf.cap()); @@ -454,7 +454,7 @@ mod tests { let mut dma = TestCircularTransfer::new(16); let mut dma_buf: [u8; 16] = array::from_fn(|idx| idx as u8); // 0, 1, ..., 15 - let mut ringbuf = DmaRingBuffer::new(&mut dma_buf); + let mut ringbuf = ReadableDmaRingBuffer::new(&mut dma_buf); assert_eq!(0, ringbuf.start); assert_eq!(16, ringbuf.cap()); diff --git a/embassy-stm32/src/usart/ringbuffered.rs b/embassy-stm32/src/usart/ringbuffered.rs index 80261d04..71077c07 100644 --- a/embassy-stm32/src/usart/ringbuffered.rs +++ b/embassy-stm32/src/usart/ringbuffered.rs @@ -6,12 +6,12 @@ use embassy_hal_internal::PeripheralRef; use futures::future::{select, Either}; use super::{clear_interrupt_flags, rdr, sr, BasicInstance, Error, UartRx}; -use crate::dma::RingBuffer; +use crate::dma::ReadableRingBuffer; use crate::usart::{Regs, Sr}; pub struct RingBufferedUartRx<'d, T: BasicInstance, RxDma: super::RxDma> { _peri: PeripheralRef<'d, T>, - ring_buf: RingBuffer<'d, RxDma, u8>, + ring_buf: ReadableRingBuffer<'d, RxDma, u8>, } impl<'d, T: BasicInstance, RxDma: super::RxDma> UartRx<'d, T, RxDma> { @@ -24,7 +24,7 @@ impl<'d, T: BasicInstance, RxDma: super::RxDma> UartRx<'d, T, RxDma> { let request = self.rx_dma.request(); let opts = Default::default(); - let ring_buf = unsafe { RingBuffer::new_read(self.rx_dma, request, rdr(T::regs()), dma_buf, opts) }; + let ring_buf = unsafe { ReadableRingBuffer::new_read(self.rx_dma, request, rdr(T::regs()), dma_buf, opts) }; RingBufferedUartRx { _peri: self._peri, From 6256a6c57c82b775241228636b6f344ca1722fa3 Mon Sep 17 00:00:00 2001 From: xoviat Date: Sat, 29 Jul 2023 19:27:16 -0500 Subject: [PATCH 02/11] fix comments --- embassy-stm32/src/dma/bdma.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/embassy-stm32/src/dma/bdma.rs b/embassy-stm32/src/dma/bdma.rs index 1dad364b..7b5008f0 100644 --- a/embassy-stm32/src/dma/bdma.rs +++ b/embassy-stm32/src/dma/bdma.rs @@ -475,7 +475,7 @@ impl<'a, C: Channel, W: Word> ReadableRingBuffer<'a, C, W> { self.ringbuf.read(DmaCtrlImpl(self.channel.reborrow()), buf) } - /// Read an exact number of elements from the ReadableRingBuffer. + /// 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. @@ -513,7 +513,7 @@ impl<'a, C: Channel, W: Word> ReadableRingBuffer<'a, C, W> { .await } - /// The capacity of the ReadableRingBuffer + /// The capacity of the ringbuffer. pub fn cap(&self) -> usize { self.ringbuf.cap() } From 8064f4bfe07c407884d412ce4820153e607c68b4 Mon Sep 17 00:00:00 2001 From: xoviat Date: Sat, 29 Jul 2023 20:10:29 -0500 Subject: [PATCH 03/11] stm32/dma: add draft writable dma buf --- embassy-stm32/src/dma/ringbuffer.rs | 93 +++++++++++++++++++++++++++++ 1 file changed, 93 insertions(+) diff --git a/embassy-stm32/src/dma/ringbuffer.rs b/embassy-stm32/src/dma/ringbuffer.rs index 92be3334..db367298 100644 --- a/embassy-stm32/src/dma/ringbuffer.rs +++ b/embassy-stm32/src/dma/ringbuffer.rs @@ -197,6 +197,99 @@ impl<'a, W: Word> ReadableDmaRingBuffer<'a, W> { length } } + +pub struct WritableDmaRingBuffer<'a, W: Word> { + pub(crate) dma_buf: &'a mut [W], + end: usize, +} + +impl<'a, W: Word> WritableDmaRingBuffer<'a, W> { + pub fn new(dma_buf: &'a mut [W]) -> Self { + Self { dma_buf, end: 0 } + } + + /// Reset the ring buffer to its initial state + pub fn clear(&mut self, mut dma: impl DmaCtrl) { + self.end = 0; + dma.reset_complete_count(); + } + + /// The capacity of the ringbuffer + pub const fn cap(&self) -> usize { + self.dma_buf.len() + } + + /// The current position of the ringbuffer + fn pos(&self, remaining_transfers: usize) -> usize { + self.cap() - remaining_transfers + } + + /// Write elements from the ring 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> { + let start = self.pos(dma.get_remaining_transfers()); + if start < self.end && self.end + buf.len() < self.cap() { + // The available, unwritten portion in the ring buffer DOES NOT wrap + // and copying elements into the buffer will not cause it to + + // Copy into the dma buffer + let len = self.copy_from(buf, self.end..self.cap()); + + compiler_fence(Ordering::SeqCst); + + // Confirm that the DMA is not inside data we could have written + let pos = self.pos(dma.get_remaining_transfers()); + if pos > self.end || pos <= start || dma.get_complete_count() > 1 { + Err(OverrunError) + } else { + self.end = (self.end + len) % self.cap(); + + Ok((len, self.cap() - (self.end - start))) + } + } else if self.end > start { + // The available, unwritten portion in the ring buffer DOES wrap + let len = self.copy_from(buf, self.end..start); + + compiler_fence(Ordering::SeqCst); + + dma.get_complete_count(); + + todo!() + } else if start < self.end && self.end + buf.len() >= self.cap() { + // The available, unwritten portion in the ring buffer DOES NOT wrap + // and copying elements into the buffer will cause it to + + let tail = self.copy_from(buf, self.end..self.cap()); + let head = self.copy_from(&buf[tail..], 0..start); + + compiler_fence(Ordering::SeqCst); + + dma.reset_complete_count(); + + todo!() + } else { + todo!() + } + } + /// Copy into the dma buffer at `data_range` from `buf` + fn copy_from(&mut self, buf: &[W], data_range: Range) -> usize { + // Limit the number of elements that can be copied + let length = usize::min(data_range.len(), buf.len()); + + // Copy into dma buffer from read buffer + // We need to do it like this instead of a simple copy_from_slice() because + // reading from a part of memory that may be simultaneously written to is unsafe + unsafe { + let dma_buf = self.dma_buf.as_mut_ptr(); + + for i in 0..length { + core::ptr::write_volatile(dma_buf.offset((data_range.start + i) as isize), buf[i]); + } + } + + length + } +} #[cfg(test)] mod tests { use core::array; From 603c4cb4fa5f3dc2d95c5e47f13149beaa227bf5 Mon Sep 17 00:00:00 2001 From: xoviat Date: Sun, 30 Jul 2023 09:18:33 -0500 Subject: [PATCH 04/11] stm32/dma: complete initial ringbuf impl. --- embassy-stm32/src/dma/dma.rs | 171 +++++++++++++++++++++++++++- embassy-stm32/src/dma/ringbuffer.rs | 50 ++++---- 2 files changed, 200 insertions(+), 21 deletions(-) diff --git a/embassy-stm32/src/dma/dma.rs b/embassy-stm32/src/dma/dma.rs index 9157f72d..3c5c79fd 100644 --- a/embassy-stm32/src/dma/dma.rs +++ b/embassy-stm32/src/dma/dma.rs @@ -7,7 +7,7 @@ use core::task::{Context, Poll, Waker}; use embassy_hal_internal::{into_ref, Peripheral, PeripheralRef}; use embassy_sync::waitqueue::AtomicWaker; -use super::ringbuffer::{DmaCtrl, OverrunError, ReadableDmaRingBuffer}; +use super::ringbuffer::{DmaCtrl, OverrunError, ReadableDmaRingBuffer, WritableDmaRingBuffer}; use super::word::{Word, WordSize}; use super::Dir; use crate::_generated::DMA_CHANNEL_COUNT; @@ -806,3 +806,172 @@ impl<'a, C: Channel, W: Word> Drop for ReadableRingBuffer<'a, C, W> { fence(Ordering::SeqCst); } } + +pub struct WritableRingBuffer<'a, C: Channel, W: Word> { + cr: regs::Cr, + channel: PeripheralRef<'a, C>, + ringbuf: WritableDmaRingBuffer<'a, W>, +} + +impl<'a, C: Channel, W: Word> WritableRingBuffer<'a, C, W> { + pub unsafe fn new_read( + channel: impl Peripheral

+ 'a, + _request: Request, + peri_addr: *mut W, + buffer: &'a mut [W], + options: TransferOptions, + ) -> Self { + into_ref!(channel); + + let len = buffer.len(); + assert!(len > 0 && len <= 0xFFFF); + + let dir = Dir::MemoryToPeripheral; + let data_size = W::size(); + + let channel_number = channel.num(); + let dma = channel.regs(); + + // "Preceding reads and writes cannot be moved past subsequent writes." + fence(Ordering::SeqCst); + + let mut w = regs::Cr(0); + w.set_dir(dir.into()); + w.set_msize(data_size.into()); + w.set_psize(data_size.into()); + w.set_pl(vals::Pl::VERYHIGH); + w.set_minc(vals::Inc::INCREMENTED); + w.set_pinc(vals::Inc::FIXED); + w.set_teie(true); + w.set_htie(options.half_transfer_ir); + w.set_tcie(true); + w.set_circ(vals::Circ::ENABLED); + #[cfg(dma_v1)] + w.set_trbuff(true); + #[cfg(dma_v2)] + w.set_chsel(_request); + w.set_pburst(options.pburst.into()); + w.set_mburst(options.mburst.into()); + w.set_pfctrl(options.flow_ctrl.into()); + w.set_en(true); + + let buffer_ptr = buffer.as_mut_ptr(); + let mut this = Self { + channel, + cr: w, + ringbuf: WritableDmaRingBuffer::new(buffer), + }; + this.clear_irqs(); + + #[cfg(dmamux)] + super::dmamux::configure_dmamux(&mut *this.channel, _request); + + let ch = dma.st(channel_number); + ch.par().write_value(peri_addr as u32); + ch.m0ar().write_value(buffer_ptr as u32); + ch.ndtr().write_value(regs::Ndtr(len as _)); + ch.fcr().write(|w| { + if let Some(fth) = options.fifo_threshold { + // FIFO mode + w.set_dmdis(vals::Dmdis::DISABLED); + w.set_fth(fth.into()); + } else { + // Direct mode + w.set_dmdis(vals::Dmdis::ENABLED); + } + }); + + this + } + + pub fn start(&mut self) { + let ch = self.channel.regs().st(self.channel.num()); + ch.cr().write_value(self.cr); + } + + pub fn clear(&mut self) { + self.ringbuf.clear(DmaCtrlImpl(self.channel.reborrow())); + } + + /// 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> { + self.ringbuf.write(DmaCtrlImpl(self.channel.reborrow()), buf) + } + + /// Write an exact number of elements from the ringbuffer. + pub async fn write_exact(&mut self, buffer: &[W]) -> Result { + use core::future::poll_fn; + use core::sync::atomic::compiler_fence; + + 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 + pub fn cap(&self) -> usize { + self.ringbuf.cap() + } + + pub fn set_waker(&mut self, waker: &Waker) { + STATE.ch_wakers[self.channel.index()].register(waker); + } + + fn clear_irqs(&mut self) { + let channel_number = self.channel.num(); + let dma = self.channel.regs(); + let isrn = channel_number / 4; + let isrbit = channel_number % 4; + + dma.ifcr(isrn).write(|w| { + w.set_htif(isrbit, true); + w.set_tcif(isrbit, true); + w.set_teif(isrbit, true); + }); + } + + pub fn request_stop(&mut self) { + let ch = self.channel.regs().st(self.channel.num()); + + // Disable the channel. Keep the IEs enabled so the irqs still fire. + ch.cr().write(|w| { + w.set_teie(true); + w.set_htie(true); + w.set_tcie(true); + }); + } + + pub fn is_running(&mut self) -> bool { + let ch = self.channel.regs().st(self.channel.num()); + ch.cr().read().en() + } +} + +impl<'a, C: Channel, W: Word> Drop for WritableRingBuffer<'a, C, W> { + fn drop(&mut self) { + self.request_stop(); + while self.is_running() {} + + // "Subsequent reads and writes cannot be moved ahead of preceding reads." + fence(Ordering::SeqCst); + } +} diff --git a/embassy-stm32/src/dma/ringbuffer.rs b/embassy-stm32/src/dma/ringbuffer.rs index db367298..e9d33021 100644 --- a/embassy-stm32/src/dma/ringbuffer.rs +++ b/embassy-stm32/src/dma/ringbuffer.rs @@ -228,9 +228,24 @@ impl<'a, W: Word> WritableDmaRingBuffer<'a, W> { /// 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> { let start = self.pos(dma.get_remaining_transfers()); - if start < self.end && self.end + buf.len() < self.cap() { - // The available, unwritten portion in the ring buffer DOES NOT wrap - // and copying elements into the buffer will not cause it to + if start > self.end { + // The occupied portion in the ring buffer DOES wrap + let len = self.copy_from(buf, self.end..start); + + compiler_fence(Ordering::SeqCst); + + // Confirm that the DMA is not inside data we could have written + let pos = self.pos(dma.get_remaining_transfers()); + if (pos > self.end && pos <= start) || dma.get_complete_count() > 1 { + Err(OverrunError) + } else { + self.end = (self.end + len) % self.cap(); + + Ok((len, self.cap() - (start - self.end))) + } + } else if start <= self.end && self.end + buf.len() < self.cap() { + // The occupied portion in the ring buffer DOES NOT wrap + // and copying elements into the buffer WILL NOT cause it to // Copy into the dma buffer let len = self.copy_from(buf, self.end..self.cap()); @@ -239,36 +254,31 @@ impl<'a, W: Word> WritableDmaRingBuffer<'a, W> { // Confirm that the DMA is not inside data we could have written let pos = self.pos(dma.get_remaining_transfers()); - if pos > self.end || pos <= start || dma.get_complete_count() > 1 { + if pos > self.end || pos < start || dma.get_complete_count() > 1 { Err(OverrunError) } else { self.end = (self.end + len) % self.cap(); Ok((len, self.cap() - (self.end - start))) } - } else if self.end > start { - // The available, unwritten portion in the ring buffer DOES wrap - let len = self.copy_from(buf, self.end..start); - - compiler_fence(Ordering::SeqCst); - - dma.get_complete_count(); - - todo!() - } else if start < self.end && self.end + buf.len() >= self.cap() { - // The available, unwritten portion in the ring buffer DOES NOT wrap - // and copying elements into the buffer will cause it to + } else { + // The occupied portion in the ring buffer DOES NOT wrap + // and copying elements into the buffer WILL cause it to let tail = self.copy_from(buf, self.end..self.cap()); let head = self.copy_from(&buf[tail..], 0..start); compiler_fence(Ordering::SeqCst); - dma.reset_complete_count(); + // Confirm that the DMA is not inside data we could have written + let pos = self.pos(dma.get_remaining_transfers()); + if pos > self.end || pos < start || dma.reset_complete_count() > 1 { + Err(OverrunError) + } else { + self.end = head; - todo!() - } else { - todo!() + Ok((tail + head, self.cap() - (start - self.end))) + } } } /// Copy into the dma buffer at `data_range` from `buf` From fd9b6487e12dff80bf9e23cba474af5d8773c8a7 Mon Sep 17 00:00:00 2001 From: xoviat Date: Sun, 30 Jul 2023 09:25:58 -0500 Subject: [PATCH 05/11] stm32/dma: impl. wringbuf for bdma --- embassy-stm32/src/dma/bdma.rs | 154 +++++++++++++++++++++++++++++++++- embassy-stm32/src/dma/dma.rs | 4 +- 2 files changed, 155 insertions(+), 3 deletions(-) diff --git a/embassy-stm32/src/dma/bdma.rs b/embassy-stm32/src/dma/bdma.rs index 7b5008f0..2905338d 100644 --- a/embassy-stm32/src/dma/bdma.rs +++ b/embassy-stm32/src/dma/bdma.rs @@ -9,7 +9,7 @@ use atomic_polyfill::AtomicUsize; use embassy_hal_internal::{into_ref, Peripheral, PeripheralRef}; use embassy_sync::waitqueue::AtomicWaker; -use super::ringbuffer::{DmaCtrl, OverrunError, ReadableDmaRingBuffer}; +use super::ringbuffer::{DmaCtrl, OverrunError, ReadableDmaRingBuffer, WritableDmaRingBuffer}; use super::word::{Word, WordSize}; use super::Dir; use crate::_generated::BDMA_CHANNEL_COUNT; @@ -559,3 +559,155 @@ impl<'a, C: Channel, W: Word> Drop for ReadableRingBuffer<'a, C, W> { fence(Ordering::SeqCst); } } + +pub struct WritableRingBuffer<'a, C: Channel, W: Word> { + cr: regs::Cr, + channel: PeripheralRef<'a, C>, + ringbuf: WritableDmaRingBuffer<'a, W>, +} + +impl<'a, C: Channel, W: Word> WritableRingBuffer<'a, C, W> { + pub unsafe fn new_write( + channel: impl Peripheral

+ 'a, + _request: Request, + peri_addr: *mut W, + buffer: &'a mut [W], + _options: TransferOptions, + ) -> Self { + into_ref!(channel); + + let len = buffer.len(); + assert!(len > 0 && len <= 0xFFFF); + + let dir = Dir::MemoryToPeripheral; + let data_size = W::size(); + + let channel_number = channel.num(); + let dma = channel.regs(); + + // "Preceding reads and writes cannot be moved past subsequent writes." + fence(Ordering::SeqCst); + + #[cfg(bdma_v2)] + critical_section::with(|_| channel.regs().cselr().modify(|w| w.set_cs(channel.num(), _request))); + + let mut w = regs::Cr(0); + w.set_psize(data_size.into()); + w.set_msize(data_size.into()); + w.set_minc(vals::Inc::ENABLED); + w.set_dir(dir.into()); + w.set_teie(true); + w.set_htie(true); + w.set_tcie(true); + w.set_circ(vals::Circ::ENABLED); + w.set_pl(vals::Pl::VERYHIGH); + w.set_en(true); + + let buffer_ptr = buffer.as_mut_ptr(); + let mut this = Self { + channel, + cr: w, + ringbuf: WritableDmaRingBuffer::new(buffer), + }; + this.clear_irqs(); + + #[cfg(dmamux)] + super::dmamux::configure_dmamux(&mut *this.channel, _request); + + let ch = dma.ch(channel_number); + ch.par().write_value(peri_addr as u32); + ch.mar().write_value(buffer_ptr as u32); + ch.ndtr().write(|w| w.set_ndt(len as u16)); + + this + } + + pub fn start(&mut self) { + let ch = self.channel.regs().ch(self.channel.num()); + ch.cr().write_value(self.cr) + } + + pub fn clear(&mut self) { + self.ringbuf.clear(DmaCtrlImpl(self.channel.reborrow())); + } + + /// Write elements from the ring buffer + /// Return a tuple of the length written and the length remaining in the buffer + pub fn read(&mut self, buf: &[W]) -> Result<(usize, usize), OverrunError> { + self.ringbuf.write(DmaCtrlImpl(self.channel.reborrow()), buf) + } + + /// Write an exact number of elements to the ringbuffer. + pub async fn write_exact(&mut self, buffer: &[W]) -> Result { + use core::future::poll_fn; + use core::sync::atomic::compiler_fence; + + let mut written_data = 0; + let buffer_len = buffer.len(); + + poll_fn(|cx| { + self.set_waker(cx.waker()); + + compiler_fence(Ordering::SeqCst); + + match self.read(&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. + pub fn cap(&self) -> usize { + self.ringbuf.cap() + } + + pub fn set_waker(&mut self, waker: &Waker) { + STATE.ch_wakers[self.channel.index()].register(waker); + } + + fn clear_irqs(&mut self) { + let dma = self.channel.regs(); + dma.ifcr().write(|w| { + w.set_htif(self.channel.num(), true); + w.set_tcif(self.channel.num(), true); + w.set_teif(self.channel.num(), true); + }); + } + + 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); + }); + } + + pub fn is_running(&mut self) -> bool { + let ch = self.channel.regs().ch(self.channel.num()); + ch.cr().read().en() + } +} + +impl<'a, C: Channel, W: Word> Drop for WritableRingBuffer<'a, C, W> { + fn drop(&mut self) { + self.request_stop(); + while self.is_running() {} + + // "Subsequent reads and writes cannot be moved ahead of preceding reads." + fence(Ordering::SeqCst); + } +} diff --git a/embassy-stm32/src/dma/dma.rs b/embassy-stm32/src/dma/dma.rs index 3c5c79fd..9cd7aa8d 100644 --- a/embassy-stm32/src/dma/dma.rs +++ b/embassy-stm32/src/dma/dma.rs @@ -814,7 +814,7 @@ pub struct WritableRingBuffer<'a, C: Channel, W: Word> { } impl<'a, C: Channel, W: Word> WritableRingBuffer<'a, C, W> { - pub unsafe fn new_read( + pub unsafe fn new_write( channel: impl Peripheral

+ 'a, _request: Request, peri_addr: *mut W, @@ -899,7 +899,7 @@ impl<'a, C: Channel, W: Word> WritableRingBuffer<'a, C, W> { self.ringbuf.write(DmaCtrlImpl(self.channel.reborrow()), buf) } - /// Write an exact number of elements from the ringbuffer. + /// Write an exact number of elements to the ringbuffer. pub async fn write_exact(&mut self, buffer: &[W]) -> Result { use core::future::poll_fn; use core::sync::atomic::compiler_fence; From 087e649bc297676c483f1f2a94a5abf101dea9a2 Mon Sep 17 00:00:00 2001 From: xoviat Date: Sun, 30 Jul 2023 09:28:02 -0500 Subject: [PATCH 06/11] stm32/dma: fix typos --- embassy-stm32/src/dma/bdma.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/embassy-stm32/src/dma/bdma.rs b/embassy-stm32/src/dma/bdma.rs index 2905338d..60f4fbd0 100644 --- a/embassy-stm32/src/dma/bdma.rs +++ b/embassy-stm32/src/dma/bdma.rs @@ -631,9 +631,9 @@ impl<'a, C: Channel, W: Word> WritableRingBuffer<'a, C, W> { self.ringbuf.clear(DmaCtrlImpl(self.channel.reborrow())); } - /// Write elements from the ring buffer + /// Write elements to the ring buffer /// Return a tuple of the length written and the length remaining in the buffer - pub fn read(&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) } @@ -650,7 +650,7 @@ impl<'a, C: Channel, W: Word> WritableRingBuffer<'a, C, W> { compiler_fence(Ordering::SeqCst); - match self.read(&buffer[written_data..buffer_len]) { + match self.write(&buffer[written_data..buffer_len]) { Ok((len, remaining)) => { written_data += len; if written_data == buffer_len { From 6f30e92c7aecc80d9fdf23e462a5128c07446f2f Mon Sep 17 00:00:00 2001 From: xoviat Date: Sun, 30 Jul 2023 10:57:17 -0500 Subject: [PATCH 07/11] stm32/dma: don't write to full ringbuf --- embassy-stm32/src/dma/ringbuffer.rs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/embassy-stm32/src/dma/ringbuffer.rs b/embassy-stm32/src/dma/ringbuffer.rs index e9d33021..800f1906 100644 --- a/embassy-stm32/src/dma/ringbuffer.rs +++ b/embassy-stm32/src/dma/ringbuffer.rs @@ -243,6 +243,8 @@ impl<'a, W: Word> WritableDmaRingBuffer<'a, W> { Ok((len, self.cap() - (start - self.end))) } + } else if start == self.end && dma.get_complete_count() == 0 { + Ok((0, 0)) } else if start <= self.end && self.end + buf.len() < self.cap() { // The occupied portion in the ring buffer DOES NOT wrap // and copying elements into the buffer WILL NOT cause it to From 538cf2bc24c6c9b299b01a63f775fa37d66c635b Mon Sep 17 00:00:00 2001 From: xoviat Date: Sun, 30 Jul 2023 14:02:41 -0500 Subject: [PATCH 08/11] stm32/dma: fix condition check --- embassy-stm32/src/dma/ringbuffer.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/embassy-stm32/src/dma/ringbuffer.rs b/embassy-stm32/src/dma/ringbuffer.rs index 800f1906..1235e532 100644 --- a/embassy-stm32/src/dma/ringbuffer.rs +++ b/embassy-stm32/src/dma/ringbuffer.rs @@ -236,7 +236,7 @@ impl<'a, W: Word> WritableDmaRingBuffer<'a, W> { // Confirm that the DMA is not inside data we could have written let pos = self.pos(dma.get_remaining_transfers()); - if (pos > self.end && pos <= start) || dma.get_complete_count() > 1 { + if (pos > self.end && pos <= start) || dma.get_complete_count() > 0 { Err(OverrunError) } else { self.end = (self.end + len) % self.cap(); From c38c85ef1fef86a5fc73d1329616df17afb3d385 Mon Sep 17 00:00:00 2001 From: xoviat Date: Sun, 30 Jul 2023 19:39:17 -0500 Subject: [PATCH 09/11] stm32/dma: add traces --- embassy-stm32/src/dma/ringbuffer.rs | 28 ++++++++++++++++++++++++++++ 1 file changed, 28 insertions(+) diff --git a/embassy-stm32/src/dma/ringbuffer.rs b/embassy-stm32/src/dma/ringbuffer.rs index 1235e532..8056a7c3 100644 --- a/embassy-stm32/src/dma/ringbuffer.rs +++ b/embassy-stm32/src/dma/ringbuffer.rs @@ -229,6 +229,13 @@ impl<'a, W: Word> WritableDmaRingBuffer<'a, W> { pub fn write(&mut self, mut dma: impl DmaCtrl, buf: &[W]) -> Result<(usize, usize), OverrunError> { let start = self.pos(dma.get_remaining_transfers()); if start > self.end { + trace!( + "[1]: start, end, complete_count: {}, {}, {}", + start, + self.end, + dma.get_complete_count() + ); + // The occupied portion in the ring buffer DOES wrap let len = self.copy_from(buf, self.end..start); @@ -244,8 +251,22 @@ impl<'a, W: Word> WritableDmaRingBuffer<'a, W> { Ok((len, self.cap() - (start - self.end))) } } else if start == self.end && dma.get_complete_count() == 0 { + trace!( + "[2]: start, end, complete_count: {}, {}, {}", + start, + self.end, + dma.get_complete_count() + ); + Ok((0, 0)) } else if start <= self.end && self.end + buf.len() < self.cap() { + trace!( + "[3]: start, end, complete_count: {}, {}, {}", + start, + self.end, + dma.get_complete_count() + ); + // The occupied portion in the ring buffer DOES NOT wrap // and copying elements into the buffer WILL NOT cause it to @@ -264,6 +285,13 @@ impl<'a, W: Word> WritableDmaRingBuffer<'a, W> { Ok((len, self.cap() - (self.end - start))) } } else { + trace!( + "[4]: start, end, complete_count: {}, {}, {}", + start, + self.end, + dma.get_complete_count() + ); + // The occupied portion in the ring buffer DOES NOT wrap // and copying elements into the buffer WILL cause it to From ffa0c08140be6c90bde9f11e797eda95e4b8331b Mon Sep 17 00:00:00 2001 From: xoviat Date: Sun, 30 Jul 2023 20:22:14 -0500 Subject: [PATCH 10/11] stm32/dma: fix condition check --- embassy-stm32/src/dma/ringbuffer.rs | 17 +++++++++++------ 1 file changed, 11 insertions(+), 6 deletions(-) diff --git a/embassy-stm32/src/dma/ringbuffer.rs b/embassy-stm32/src/dma/ringbuffer.rs index 8056a7c3..c3e4f20c 100644 --- a/embassy-stm32/src/dma/ringbuffer.rs +++ b/embassy-stm32/src/dma/ringbuffer.rs @@ -230,9 +230,10 @@ impl<'a, W: Word> WritableDmaRingBuffer<'a, W> { let start = self.pos(dma.get_remaining_transfers()); if start > self.end { trace!( - "[1]: start, end, complete_count: {}, {}, {}", + "[1]: start, end, len, complete_count: {}, {}, {}, {}", start, self.end, + buf.len(), dma.get_complete_count() ); @@ -242,8 +243,9 @@ impl<'a, W: Word> WritableDmaRingBuffer<'a, W> { compiler_fence(Ordering::SeqCst); // Confirm that the DMA is not inside data we could have written - let pos = self.pos(dma.get_remaining_transfers()); - if (pos > self.end && pos <= start) || dma.get_complete_count() > 0 { + let (pos, 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 { Err(OverrunError) } else { self.end = (self.end + len) % self.cap(); @@ -252,18 +254,20 @@ impl<'a, W: Word> WritableDmaRingBuffer<'a, W> { } } else if start == self.end && dma.get_complete_count() == 0 { trace!( - "[2]: start, end, complete_count: {}, {}, {}", + "[2]: start, end, len, complete_count: {}, {}, {}, {}", start, self.end, + buf.len(), dma.get_complete_count() ); Ok((0, 0)) } else if start <= self.end && self.end + buf.len() < self.cap() { trace!( - "[3]: start, end, complete_count: {}, {}, {}", + "[3]: start, end, len, complete_count: {}, {}, {}, {}", start, self.end, + buf.len(), dma.get_complete_count() ); @@ -286,9 +290,10 @@ impl<'a, W: Word> WritableDmaRingBuffer<'a, W> { } } else { trace!( - "[4]: start, end, complete_count: {}, {}, {}", + "[4]: start, end, len, complete_count: {}, {}, {}, {}", start, self.end, + buf.len(), dma.get_complete_count() ); From bbc8424a5b502187d62cb0ff607e1ac0a719c1c4 Mon Sep 17 00:00:00 2001 From: xoviat Date: Mon, 31 Jul 2023 17:55:25 -0500 Subject: [PATCH 11/11] stm32/dma: remove trace --- embassy-stm32/src/dma/ringbuffer.rs | 32 ----------------------------- 1 file changed, 32 deletions(-) diff --git a/embassy-stm32/src/dma/ringbuffer.rs b/embassy-stm32/src/dma/ringbuffer.rs index c3e4f20c..945c7508 100644 --- a/embassy-stm32/src/dma/ringbuffer.rs +++ b/embassy-stm32/src/dma/ringbuffer.rs @@ -229,14 +229,6 @@ impl<'a, W: Word> WritableDmaRingBuffer<'a, W> { pub fn write(&mut self, mut dma: impl DmaCtrl, buf: &[W]) -> Result<(usize, usize), OverrunError> { let start = self.pos(dma.get_remaining_transfers()); if start > self.end { - trace!( - "[1]: start, end, len, complete_count: {}, {}, {}, {}", - start, - self.end, - buf.len(), - dma.get_complete_count() - ); - // The occupied portion in the ring buffer DOES wrap let len = self.copy_from(buf, self.end..start); @@ -253,24 +245,8 @@ impl<'a, W: Word> WritableDmaRingBuffer<'a, W> { Ok((len, self.cap() - (start - self.end))) } } else if start == self.end && dma.get_complete_count() == 0 { - trace!( - "[2]: start, end, len, complete_count: {}, {}, {}, {}", - start, - self.end, - buf.len(), - dma.get_complete_count() - ); - Ok((0, 0)) } else if start <= self.end && self.end + buf.len() < self.cap() { - trace!( - "[3]: start, end, len, complete_count: {}, {}, {}, {}", - start, - self.end, - buf.len(), - dma.get_complete_count() - ); - // The occupied portion in the ring buffer DOES NOT wrap // and copying elements into the buffer WILL NOT cause it to @@ -289,14 +265,6 @@ impl<'a, W: Word> WritableDmaRingBuffer<'a, W> { Ok((len, self.cap() - (self.end - start))) } } else { - trace!( - "[4]: start, end, len, complete_count: {}, {}, {}, {}", - start, - self.end, - buf.len(), - dma.get_complete_count() - ); - // The occupied portion in the ring buffer DOES NOT wrap // and copying elements into the buffer WILL cause it to