From 85fb890b0025db386459f8b6a573c29f00bf3ed1 Mon Sep 17 00:00:00 2001 From: Alexandros Liarokapis Date: Mon, 23 Sep 2024 02:49:05 +0300 Subject: [PATCH] add auto-clear functionality to ringbuffer --- embassy-stm32/src/dma/ringbuffer/mod.rs | 56 ++++++++++++------- .../dma/ringbuffer/tests/prop_test/reader.rs | 3 +- .../dma/ringbuffer/tests/prop_test/writer.rs | 3 +- 3 files changed, 40 insertions(+), 22 deletions(-) diff --git a/embassy-stm32/src/dma/ringbuffer/mod.rs b/embassy-stm32/src/dma/ringbuffer/mod.rs index d4d119a69..abc01e3cc 100644 --- a/embassy-stm32/src/dma/ringbuffer/mod.rs +++ b/embassy-stm32/src/dma/ringbuffer/mod.rs @@ -85,7 +85,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, dma: &mut impl DmaCtrl) { dma.reset_complete_count(); self.write_index.reset(); @@ -113,17 +113,12 @@ impl<'a, W: Word> ReadableDmaRingBuffer<'a, W> { /// 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 /// 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, + /// in which case the rinbuffer will automatically clear itself. pub fn read(&mut self, dma: &mut impl DmaCtrl, buf: &mut [W]) -> Result<(usize, usize), OverrunError> { - self.sync_write_index(dma); - let readable = self.len()?.min(buf.len()); - for i in 0..readable { - buf[i] = self.read_buf(i); - } - self.sync_write_index(dma); - let available = self.len()?; - self.read_index.advance(self.cap(), readable); - Ok((readable, available - readable)) + self.read_raw(dma, buf).inspect_err(|_e| { + self.clear(dma); + }) } /// Read an exact number of elements from the ringbuffer. @@ -159,6 +154,18 @@ impl<'a, W: Word> ReadableDmaRingBuffer<'a, W> { .await } + fn read_raw(&mut self, dma: &mut impl DmaCtrl, buf: &mut [W]) -> Result<(usize, usize), OverrunError> { + self.sync_write_index(dma); + let readable = self.len()?.min(buf.len()); + for i in 0..readable { + buf[i] = self.read_buf(i); + } + self.sync_write_index(dma); + let available = self.len()?; + self.read_index.advance(self.cap(), readable); + Ok((readable, available - readable)) + } + fn read_buf(&self, offset: usize) -> W { unsafe { core::ptr::read_volatile( @@ -222,16 +229,13 @@ impl<'a, W: Word> WritableDmaRingBuffer<'a, W> { /// Append data to the ring buffer. /// Returns a tuple of the data written and the remaining write capacity in the buffer. + /// OverrunError is returned if the portion to be written was previously read by the DMA controller. + /// In this case, the ringbuffer will automatically reset itself, giving a full buffer worth of + /// leeway between the write index and the DMA. pub fn write(&mut self, dma: &mut impl DmaCtrl, buf: &[W]) -> Result<(usize, usize), OverrunError> { - self.sync_read_index(dma); - let writable = self.len()?.min(buf.len()); - for i in 0..writable { - self.write_buf(i, buf[i]); - } - self.sync_read_index(dma); - let available = self.len()?; - self.write_index.advance(self.cap(), writable); - Ok((writable, available - writable)) + self.write_raw(dma, buf).inspect_err(|_e| { + self.clear(dma); + }) } /// Write elements directly to the buffer. @@ -266,6 +270,18 @@ impl<'a, W: Word> WritableDmaRingBuffer<'a, W> { .await } + pub fn write_raw(&mut self, dma: &mut impl DmaCtrl, buf: &[W]) -> Result<(usize, usize), OverrunError> { + self.sync_read_index(dma); + let writable = self.len()?.min(buf.len()); + for i in 0..writable { + self.write_buf(i, buf[i]); + } + self.sync_read_index(dma); + let available = self.len()?; + self.write_index.advance(self.cap(), writable); + Ok((writable, available - writable)) + } + fn write_buf(&mut self, offset: usize, value: W) { unsafe { core::ptr::write_volatile( diff --git a/embassy-stm32/src/dma/ringbuffer/tests/prop_test/reader.rs b/embassy-stm32/src/dma/ringbuffer/tests/prop_test/reader.rs index 6555ebfb0..6e640a813 100644 --- a/embassy-stm32/src/dma/ringbuffer/tests/prop_test/reader.rs +++ b/embassy-stm32/src/dma/ringbuffer/tests/prop_test/reader.rs @@ -38,8 +38,9 @@ impl ReferenceStateMachine for ReaderSM { Status::Available(x + y) } } + (Status::Failed, ReaderTransition::Write(_)) => Status::Failed, (Status::Available(x), ReaderTransition::ReadUpTo(y)) => Status::Available(x.saturating_sub(*y)), - (Status::Failed, _) => Status::Failed, + (Status::Failed, ReaderTransition::ReadUpTo(_)) => Status::Available(0), } } } diff --git a/embassy-stm32/src/dma/ringbuffer/tests/prop_test/writer.rs b/embassy-stm32/src/dma/ringbuffer/tests/prop_test/writer.rs index 15f54c672..c1b3859b2 100644 --- a/embassy-stm32/src/dma/ringbuffer/tests/prop_test/writer.rs +++ b/embassy-stm32/src/dma/ringbuffer/tests/prop_test/writer.rs @@ -38,8 +38,9 @@ impl ReferenceStateMachine for WriterSM { Status::Available(x - y) } } + (Status::Failed, WriterTransition::Read(_)) => Status::Failed, (Status::Available(x), WriterTransition::WriteUpTo(y)) => Status::Available((x + *y).min(CAP)), - (Status::Failed, _) => Status::Failed, + (Status::Failed, WriterTransition::WriteUpTo(_)) => Status::Available(CAP), } } }