stm32: fix ringbugger overrun errors due to bad dma wrap-around behavior

This commit is contained in:
Alexandros Liarokapis 2024-10-01 17:59:04 +03:00
parent c991ddb766
commit f0d2ebdc7e
5 changed files with 72 additions and 117 deletions

View File

@ -6,7 +6,7 @@ use core::task::{Context, Poll, Waker};
use embassy_hal_internal::{into_ref, Peripheral, PeripheralRef}; use embassy_hal_internal::{into_ref, Peripheral, PeripheralRef};
use embassy_sync::waitqueue::AtomicWaker; use embassy_sync::waitqueue::AtomicWaker;
use super::ringbuffer::{DmaCtrl, OverrunError, ReadableDmaRingBuffer, WritableDmaRingBuffer}; use super::ringbuffer::{DmaCtrl, Error, ReadableDmaRingBuffer, WritableDmaRingBuffer};
use super::word::{Word, WordSize}; use super::word::{Word, WordSize};
use super::{AnyChannel, Channel, Dir, Request, STATE}; use super::{AnyChannel, Channel, Dir, Request, STATE};
use crate::interrupt::typelevel::Interrupt; use crate::interrupt::typelevel::Interrupt;
@ -299,7 +299,6 @@ impl AnyChannel {
} else { } else {
return; return;
} }
state.waker.wake(); state.waker.wake();
} }
#[cfg(bdma)] #[cfg(bdma)]
@ -828,7 +827,8 @@ impl<'a, W: Word> ReadableRingBuffer<'a, W> {
/// ///
/// You must call this after creating it for it to work. /// You must call this after creating it for it to work.
pub fn start(&mut self) { pub fn start(&mut self) {
self.channel.start() self.channel.start();
self.clear();
} }
/// Clear all data in the ring buffer. /// Clear all data in the ring buffer.
@ -840,15 +840,15 @@ impl<'a, W: Word> ReadableRingBuffer<'a, W> {
/// Return a tuple of the length read and the length remaining in the 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 /// 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. /// Error 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), Error> {
self.ringbuf.read(&mut 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.
/// ///
/// Returns the remaining number of elements available for immediate reading. /// 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. /// Error is returned if the portion to be read was overwritten by the DMA controller.
/// ///
/// Async/Wake Behavior: /// Async/Wake Behavior:
/// The underlying DMA peripheral only can wake us when its buffer pointer has reached the halfway point, /// The underlying DMA peripheral only can wake us when its buffer pointer has reached the halfway point,
@ -856,12 +856,17 @@ impl<'a, W: Word> ReadableRingBuffer<'a, W> {
/// ring buffer was created with a buffer of size 'N': /// 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. /// - 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, Error> {
self.ringbuf self.ringbuf
.read_exact(&mut DmaCtrlImpl(self.channel.reborrow()), buffer) .read_exact(&mut DmaCtrlImpl(self.channel.reborrow()), buffer)
.await .await
} }
/// The current length of the ringbuffer
pub fn len(&mut self) -> Result<usize, Error> {
Ok(self.ringbuf.len(&mut DmaCtrlImpl(self.channel.reborrow()))?)
}
/// The capacity of the ringbuffer /// The capacity of the ringbuffer
pub const fn capacity(&self) -> usize { pub const fn capacity(&self) -> usize {
self.ringbuf.cap() self.ringbuf.cap()
@ -986,23 +991,28 @@ impl<'a, W: Word> WritableRingBuffer<'a, W> {
/// Write elements directly to the raw buffer. /// Write elements directly to the raw buffer.
/// This can be used to fill the buffer before starting the DMA transfer. /// This can be used to fill the buffer before starting the DMA transfer.
#[allow(dead_code)] #[allow(dead_code)]
pub fn write_immediate(&mut self, buf: &[W]) -> Result<(usize, usize), OverrunError> { pub fn write_immediate(&mut self, buf: &[W]) -> Result<(usize, usize), Error> {
self.ringbuf.write_immediate(buf) self.ringbuf.write_immediate(buf)
} }
/// 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), Error> {
self.ringbuf.write(&mut 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, Error> {
self.ringbuf self.ringbuf
.write_exact(&mut DmaCtrlImpl(self.channel.reborrow()), buffer) .write_exact(&mut DmaCtrlImpl(self.channel.reborrow()), buffer)
.await .await
} }
/// The current length of the ringbuffer
pub fn len(&mut self) -> Result<usize, Error> {
Ok(self.ringbuf.len(&mut DmaCtrlImpl(self.channel.reborrow()))?)
}
/// The capacity of the ringbuffer /// The capacity of the ringbuffer
pub const fn capacity(&self) -> usize { pub const fn capacity(&self) -> usize {
self.ringbuf.cap() self.ringbuf.cap()

View File

@ -19,9 +19,13 @@ pub trait DmaCtrl {
#[derive(Debug, PartialEq)] #[derive(Debug, PartialEq)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))] #[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub struct OverrunError; pub enum Error {
Overrun,
DmaUnsynced,
}
#[derive(Debug, Clone, Copy, Default)] #[derive(Debug, Clone, Copy, Default)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
struct DmaIndex { struct DmaIndex {
complete_count: usize, complete_count: usize,
pos: usize, pos: usize,
@ -38,15 +42,19 @@ impl DmaIndex {
} }
fn dma_sync(&mut self, cap: usize, dma: &mut impl DmaCtrl) { fn dma_sync(&mut self, cap: usize, dma: &mut impl DmaCtrl) {
let first_pos = cap - dma.get_remaining_transfers(); // Important!
self.complete_count += dma.reset_complete_count(); // The ordering of the first two lines matters!
self.pos = cap - dma.get_remaining_transfers(); // If changed, the code will detect a wrong +capacity
// jump at wrap-around.
let count_diff = dma.reset_complete_count();
let pos = cap - dma.get_remaining_transfers();
self.pos = if pos < self.pos && count_diff == 0 {
cap - 1
} else {
pos
};
// If the latter call to get_remaining_transfers() returned a smaller value than the first, the dma self.complete_count += count_diff;
// has wrapped around between calls and we must check if the complete count also incremented.
if self.pos < first_pos {
self.complete_count += dma.reset_complete_count();
}
} }
fn advance(&mut self, cap: usize, steps: usize) { fn advance(&mut self, cap: usize, steps: usize) {
@ -96,14 +104,18 @@ impl<'a, W: Word> ReadableDmaRingBuffer<'a, W> {
} }
/// Get the available readable dma samples. /// Get the available readable dma samples.
pub fn len(&mut self, dma: &mut impl DmaCtrl) -> Result<usize, OverrunError> { pub fn len(&mut self, dma: &mut impl DmaCtrl) -> Result<usize, Error> {
self.write_index.dma_sync(self.cap(), dma); self.write_index.dma_sync(self.cap(), dma);
DmaIndex::normalize(&mut self.write_index, &mut self.read_index); DmaIndex::normalize(&mut self.write_index, &mut self.read_index);
let diff = self.write_index.diff(self.cap(), &self.read_index); let diff = self.write_index.diff(self.cap(), &self.read_index);
if diff < 0 || diff > self.cap() as isize { if diff < 0 {
Err(OverrunError) return Err(Error::DmaUnsynced);
}
if diff > self.cap() as isize {
Err(Error::Overrun)
} else { } else {
Ok(diff as usize) Ok(diff as usize)
} }
@ -113,9 +125,9 @@ impl<'a, W: Word> ReadableDmaRingBuffer<'a, W> {
/// Return a tuple of the length read and the length remaining in the 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 /// 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, /// Error is returned if the portion to be read was overwritten by the DMA controller,
/// in which case the rinbuffer will automatically reset itself. /// in which case the rinbuffer will automatically reset itself.
pub fn read(&mut self, dma: &mut impl DmaCtrl, buf: &mut [W]) -> Result<(usize, usize), OverrunError> { pub fn read(&mut self, dma: &mut impl DmaCtrl, buf: &mut [W]) -> Result<(usize, usize), Error> {
self.read_raw(dma, buf).inspect_err(|_e| { self.read_raw(dma, buf).inspect_err(|_e| {
self.reset(dma); self.reset(dma);
}) })
@ -124,7 +136,7 @@ impl<'a, W: Word> ReadableDmaRingBuffer<'a, W> {
/// Read an exact number of elements from the ringbuffer. /// Read an exact number of elements from the ringbuffer.
/// ///
/// Returns the remaining number of elements available for immediate reading. /// 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. /// Error is returned if the portion to be read was overwritten by the DMA controller.
/// ///
/// Async/Wake Behavior: /// Async/Wake Behavior:
/// The underlying DMA peripheral only can wake us when its buffer pointer has reached the halfway point, /// The underlying DMA peripheral only can wake us when its buffer pointer has reached the halfway point,
@ -132,7 +144,7 @@ impl<'a, W: Word> ReadableDmaRingBuffer<'a, W> {
/// ring buffer was created with a buffer of size 'N': /// 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. /// - 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, dma: &mut impl DmaCtrl, buffer: &mut [W]) -> Result<usize, OverrunError> { pub async fn read_exact(&mut self, dma: &mut impl DmaCtrl, buffer: &mut [W]) -> Result<usize, Error> {
let mut read_data = 0; let mut read_data = 0;
let buffer_len = buffer.len(); let buffer_len = buffer.len();
@ -154,7 +166,7 @@ impl<'a, W: Word> ReadableDmaRingBuffer<'a, W> {
.await .await
} }
fn read_raw(&mut self, dma: &mut impl DmaCtrl, buf: &mut [W]) -> Result<(usize, usize), OverrunError> { fn read_raw(&mut self, dma: &mut impl DmaCtrl, buf: &mut [W]) -> Result<(usize, usize), Error> {
let readable = self.len(dma)?.min(buf.len()); let readable = self.len(dma)?.min(buf.len());
for i in 0..readable { for i in 0..readable {
buf[i] = self.read_buf(i); buf[i] = self.read_buf(i);
@ -205,14 +217,17 @@ impl<'a, W: Word> WritableDmaRingBuffer<'a, W> {
} }
/// Get the remaining writable dma samples. /// Get the remaining writable dma samples.
pub fn len(&mut self, dma: &mut impl DmaCtrl) -> Result<usize, OverrunError> { pub fn len(&mut self, dma: &mut impl DmaCtrl) -> Result<usize, Error> {
self.read_index.dma_sync(self.cap(), dma); self.read_index.dma_sync(self.cap(), dma);
DmaIndex::normalize(&mut self.read_index, &mut self.write_index); DmaIndex::normalize(&mut self.read_index, &mut self.write_index);
let diff = self.write_index.diff(self.cap(), &self.read_index); let diff = self.write_index.diff(self.cap(), &self.read_index);
if diff < 0 || diff > self.cap() as isize { if diff > self.cap() as isize {
Err(OverrunError) return Err(Error::DmaUnsynced);
}
if diff < 0 {
Err(Error::Overrun)
} else { } else {
Ok(self.cap().saturating_sub(diff as usize)) Ok(self.cap().saturating_sub(diff as usize))
} }
@ -225,17 +240,17 @@ impl<'a, W: Word> WritableDmaRingBuffer<'a, W> {
/// Append data to the ring buffer. /// Append data to the ring buffer.
/// Returns a tuple of the data written and the remaining write capacity in the 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. /// Error 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 /// In this case, the ringbuffer will automatically reset itself, giving a full buffer worth of
/// leeway between the write index and the DMA. /// leeway between the write index and the DMA.
pub fn write(&mut self, dma: &mut impl DmaCtrl, buf: &[W]) -> Result<(usize, usize), OverrunError> { pub fn write(&mut self, dma: &mut impl DmaCtrl, buf: &[W]) -> Result<(usize, usize), Error> {
self.write_raw(dma, buf).inspect_err(|_e| { self.write_raw(dma, buf).inspect_err(|_e| {
self.reset(dma); self.reset(dma);
}) })
} }
/// Write elements directly to the buffer. /// Write elements directly to the buffer.
pub fn write_immediate(&mut self, buf: &[W]) -> Result<(usize, usize), OverrunError> { pub fn write_immediate(&mut self, buf: &[W]) -> Result<(usize, usize), Error> {
for (i, data) in buf.iter().enumerate() { for (i, data) in buf.iter().enumerate() {
self.write_buf(i, *data) self.write_buf(i, *data)
} }
@ -244,7 +259,7 @@ impl<'a, W: Word> WritableDmaRingBuffer<'a, W> {
} }
/// 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, dma: &mut impl DmaCtrl, buffer: &[W]) -> Result<usize, OverrunError> { pub async fn write_exact(&mut self, dma: &mut impl DmaCtrl, buffer: &[W]) -> Result<usize, Error> {
let mut written_data = 0; let mut written_data = 0;
let buffer_len = buffer.len(); let buffer_len = buffer.len();
@ -266,7 +281,7 @@ impl<'a, W: Word> WritableDmaRingBuffer<'a, W> {
.await .await
} }
pub fn write_raw(&mut self, dma: &mut impl DmaCtrl, buf: &[W]) -> Result<(usize, usize), OverrunError> { fn write_raw(&mut self, dma: &mut impl DmaCtrl, buf: &[W]) -> Result<(usize, usize), Error> {
let writable = self.len(dma)?.min(buf.len()); let writable = self.len(dma)?.min(buf.len());
for i in 0..writable { for i in 0..writable {
self.write_buf(i, buf[i]); self.write_buf(i, buf[i]);

View File

@ -2,13 +2,14 @@ use std::{cell, vec};
use super::*; use super::*;
#[allow(dead_code)] #[allow(unused)]
#[derive(PartialEq, Debug)] #[derive(PartialEq, Debug)]
enum TestCircularTransferRequest { enum TestCircularTransferRequest {
ResetCompleteCount(usize), ResetCompleteCount(usize),
PositionRequest(usize), PositionRequest(usize),
} }
#[allow(unused)]
struct TestCircularTransfer { struct TestCircularTransfer {
len: usize, len: usize,
requests: cell::RefCell<vec::Vec<TestCircularTransferRequest>>, requests: cell::RefCell<vec::Vec<TestCircularTransferRequest>>,
@ -39,6 +40,7 @@ impl DmaCtrl for TestCircularTransfer {
} }
impl TestCircularTransfer { impl TestCircularTransfer {
#[allow(unused)]
pub fn new(len: usize) -> Self { pub fn new(len: usize) -> Self {
Self { Self {
requests: cell::RefCell::new(vec![]), requests: cell::RefCell::new(vec![]),
@ -46,6 +48,7 @@ impl TestCircularTransfer {
} }
} }
#[allow(unused)]
pub fn setup(&self, mut requests: vec::Vec<TestCircularTransferRequest>) { pub fn setup(&self, mut requests: vec::Vec<TestCircularTransferRequest>) {
requests.reverse(); requests.reverse();
self.requests.replace(requests); self.requests.replace(requests);
@ -54,84 +57,6 @@ impl TestCircularTransfer {
const CAP: usize = 16; const CAP: usize = 16;
#[test]
fn dma_index_dma_sync_syncs_position_to_last_read_if_sync_takes_place_on_same_dma_cycle() {
let mut dma = TestCircularTransfer::new(CAP);
dma.setup(vec![
TestCircularTransferRequest::PositionRequest(4),
TestCircularTransferRequest::ResetCompleteCount(0),
TestCircularTransferRequest::PositionRequest(7),
]);
let mut index = DmaIndex::default();
index.dma_sync(CAP, &mut dma);
assert_eq!(index.complete_count, 0);
assert_eq!(index.pos, 7);
}
#[test]
fn dma_index_dma_sync_updates_complete_count_properly_if_sync_takes_place_on_same_dma_cycle() {
let mut dma = TestCircularTransfer::new(CAP);
dma.setup(vec![
TestCircularTransferRequest::PositionRequest(4),
TestCircularTransferRequest::ResetCompleteCount(2),
TestCircularTransferRequest::PositionRequest(7),
]);
let mut index = DmaIndex::default();
index.complete_count = 1;
index.dma_sync(CAP, &mut dma);
assert_eq!(index.complete_count, 3);
assert_eq!(index.pos, 7);
}
#[test]
fn dma_index_dma_sync_syncs_to_last_position_if_reads_occur_on_different_dma_cycles() {
let mut dma = TestCircularTransfer::new(CAP);
dma.setup(vec![
TestCircularTransferRequest::PositionRequest(10),
TestCircularTransferRequest::ResetCompleteCount(1),
TestCircularTransferRequest::PositionRequest(5),
TestCircularTransferRequest::ResetCompleteCount(0),
]);
let mut index = DmaIndex::default();
index.dma_sync(CAP, &mut dma);
assert_eq!(index.complete_count, 1);
assert_eq!(index.pos, 5);
}
#[test]
fn dma_index_dma_sync_detects_new_cycle_if_later_position_is_less_than_first_and_first_complete_count_occurs_on_first_cycle(
) {
let mut dma = TestCircularTransfer::new(CAP);
dma.setup(vec![
TestCircularTransferRequest::PositionRequest(10),
TestCircularTransferRequest::ResetCompleteCount(1),
TestCircularTransferRequest::PositionRequest(5),
TestCircularTransferRequest::ResetCompleteCount(1),
]);
let mut index = DmaIndex::default();
index.complete_count = 1;
index.dma_sync(CAP, &mut dma);
assert_eq!(index.complete_count, 3);
assert_eq!(index.pos, 5);
}
#[test]
fn dma_index_dma_sync_detects_new_cycle_if_later_position_is_less_than_first_and_first_complete_count_occurs_on_later_cycle(
) {
let mut dma = TestCircularTransfer::new(CAP);
dma.setup(vec![
TestCircularTransferRequest::PositionRequest(10),
TestCircularTransferRequest::ResetCompleteCount(2),
TestCircularTransferRequest::PositionRequest(5),
TestCircularTransferRequest::ResetCompleteCount(0),
]);
let mut index = DmaIndex::default();
index.complete_count = 1;
index.dma_sync(CAP, &mut dma);
assert_eq!(index.complete_count, 3);
assert_eq!(index.pos, 5);
}
#[test] #[test]
fn dma_index_as_index_returns_index_mod_cap_by_default() { fn dma_index_as_index_returns_index_mod_cap_by_default() {
let index = DmaIndex::default(); let index = DmaIndex::default();

View File

@ -27,8 +27,14 @@ pub enum Error {
} }
#[cfg(not(gpdma))] #[cfg(not(gpdma))]
impl From<ringbuffer::OverrunError> for Error { impl From<ringbuffer::Error> for Error {
fn from(_: ringbuffer::OverrunError) -> Self { fn from(#[allow(unused)] err: ringbuffer::Error) -> Self {
#[cfg(feature = "defmt")]
{
if err == ringbuffer::Error::DmaUnsynced {
defmt::error!("Ringbuffer broken invariants detected!");
}
}
Self::Overrun Self::Overrun
} }
} }

View File

@ -83,7 +83,6 @@ impl<'d> RingBufferedUartRx<'d> {
// Clear the buffer so that it is ready to receive data // Clear the buffer so that it is ready to receive data
compiler_fence(Ordering::SeqCst); compiler_fence(Ordering::SeqCst);
self.ring_buf.start(); self.ring_buf.start();
self.ring_buf.clear();
let r = self.info.regs; let r = self.info.regs;
// clear all interrupts and DMA Rx Request // clear all interrupts and DMA Rx Request