use core::ops::Range; use core::sync::atomic::{compiler_fence, Ordering}; use super::word::Word; /// A "read-only" ring-buffer to be used together with the DMA controller which /// writes in a circular way, "uncontrolled" to the buffer. /// /// A snapshot of the ring buffer state can be attained by setting the `ndtr` field /// to the current register value. `ndtr` describes the current position of the DMA /// write. /// /// # Safety /// /// The ring buffer controls the TCIF (transfer completed interrupt flag) to /// detect buffer overruns, hence this interrupt must be disabled. /// The buffer can detect overruns up to one period, that is, for a X byte buffer, /// overruns can be detected if they happen from byte X+1 up to 2X. After this /// point, overrunds may or may not be detected. /// /// # Buffer layout /// /// ```text /// Without wraparound: With wraparound: /// /// + buf +--- NDTR ---+ + buf +---------- NDTR ----------+ /// | | | | | | /// v v v v v v /// +-----------------------------------------+ +-----------------------------------------+ /// |oooooooooooXXXXXXXXXXXXXXXXoooooooooooooo| |XXXXXXXXXXXXXooooooooooooXXXXXXXXXXXXXXXX| /// +-----------------------------------------+ +-----------------------------------------+ /// ^ ^ ^ ^ ^ ^ /// | | | | | | /// +- first --+ | +- end ------+ | /// | | | | /// +- end --------------------+ +- first ----------------+ /// ``` pub struct DmaRingBuffer<'a, W: Word> { pub(crate) dma_buf: &'a mut [W], first: usize, pub ndtr: usize, expect_next_read_to_wrap: bool, } #[derive(Debug, PartialEq)] pub struct OverrunError; pub trait DmaCtrl { /// Get the NDTR register value, i.e. the space left in the underlying /// buffer until the dma writer wraps. fn ndtr(&self) -> usize; /// Read the transfer completed interrupt flag /// This flag is set by the dma controller when NDTR is reloaded, /// i.e. when the writing wraps. fn tcif(&self) -> bool; /// Clear the transfer completed interrupt flag fn clear_tcif(&mut self); } impl<'a, W: Word> DmaRingBuffer<'a, W> { pub fn new(dma_buf: &'a mut [W]) -> Self { let ndtr = dma_buf.len(); Self { dma_buf, first: 0, ndtr, expect_next_read_to_wrap: false, } } /// Reset the ring buffer to its initial state pub fn clear(&mut self) { self.first = 0; self.ndtr = self.dma_buf.len(); self.expect_next_read_to_wrap = false; } /// The buffer end position fn end(&self) -> usize { self.dma_buf.len() - self.ndtr } /// Returns whether the buffer is empty #[allow(dead_code)] pub fn is_empty(&self) -> bool { self.first == self.end() } /// The current number of bytes in the buffer /// This may change at any time if dma is currently active #[allow(dead_code)] pub fn len(&self) -> usize { // Read out a stable end (the dma periheral can change it at anytime) let end = self.end(); if self.first <= end { // No wrap end - self.first } else { self.dma_buf.len() - self.first + end } } /// Read bytes from the ring buffer /// OverrunError is returned if the portion to be read was overwritten by the DMA controller. pub fn read(&mut self, dma: &mut impl DmaCtrl, buf: &mut [W]) -> Result { let end = self.end(); compiler_fence(Ordering::SeqCst); if self.first == end { // The buffer is currently empty if dma.tcif() { // The dma controller has written such that the ring buffer now wraps // This is the special case where exactly n*dma_buf.len(), n = 1,2,..., bytes was written, // but where additional bytes are now written causing the ring buffer to wrap. // This is only an error if the writing has passed the current unread region. self.ndtr = dma.ndtr(); if self.end() > self.first { dma.clear_tcif(); return Err(OverrunError); } } self.expect_next_read_to_wrap = false; Ok(0) } else if self.first < end { // The available, unread portion in the ring buffer DOES NOT wrap if self.expect_next_read_to_wrap { // The read was expected to wrap but it did not dma.clear_tcif(); return Err(OverrunError); } // Copy out the bytes from the dma buffer let len = self.copy_to(buf, self.first..end); compiler_fence(Ordering::SeqCst); if dma.tcif() { // The dma controller has written such that the ring buffer now wraps self.ndtr = dma.ndtr(); if self.end() > self.first { // The bytes that we have copied out have overflowed // as the writer has now both wrapped and is currently writing // within the region that we have just copied out // Clear transfer completed interrupt flag dma.clear_tcif(); return Err(OverrunError); } } self.first = (self.first + len) % self.dma_buf.len(); self.expect_next_read_to_wrap = false; Ok(len) } else { // The available, unread portion in the ring buffer DOES wrap // The dma controller has wrapped since we last read and is currently // writing (or the next byte added will be) in the beginning of the ring buffer. // If the unread portion wraps then the writer must also have wrapped, // or it has wrapped and we already cleared the TCIF flag assert!(dma.tcif() || self.expect_next_read_to_wrap); // Clear transfer completed interrupt flag dma.clear_tcif(); if self.first + buf.len() < self.dma_buf.len() { // The provided read buffer is not large enough to include all bytes from the tail of the dma buffer. // Copy out from the dma buffer let len = self.copy_to(buf, self.first..self.dma_buf.len()); compiler_fence(Ordering::SeqCst); // We have now copied out the data from dma_buf // Make sure that the just read part was not overwritten during the copy self.ndtr = dma.ndtr(); if self.end() > self.first || dma.tcif() { // The writer has entered the data that we have just read since we read out `end` in the beginning and until now. return Err(OverrunError); } self.first = (self.first + len) % self.dma_buf.len(); self.expect_next_read_to_wrap = true; Ok(len) } else { // The provided read buffer is large enough to include all bytes from the tail of the dma buffer, // so the next read will not have any unread tail bytes in the ring buffer. // Copy out from the dma buffer let tail = self.copy_to(buf, self.first..self.dma_buf.len()); let head = self.copy_to(&mut buf[tail..], 0..end); compiler_fence(Ordering::SeqCst); // We have now copied out the data from dma_buf // Make sure that the just read part was not overwritten during the copy self.ndtr = dma.ndtr(); if self.end() > self.first || dma.tcif() { return Err(OverrunError); } self.first = head; self.expect_next_read_to_wrap = false; Ok(tail + head) } } } /// Copy from the dma buffer at `data_range` into `buf` fn copy_to(&mut self, buf: &mut [W], data_range: Range) -> usize { // Limit the number of bytes that can be copied let length = usize::min(data_range.len(), buf.len()); // Copy from dma buffer into 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_ptr(); for i in 0..length { buf[i] = core::ptr::read_volatile(dma_buf.offset((data_range.start + i) as isize)); } } length } } #[cfg(test)] mod tests { use core::array; use core::cell::RefCell; use super::*; struct TestCtrl { next_ndtr: RefCell>, tcif: bool, } impl TestCtrl { pub const fn new() -> Self { Self { next_ndtr: RefCell::new(None), tcif: false, } } pub fn set_next_ndtr(&mut self, ndtr: usize) { self.next_ndtr.borrow_mut().replace(ndtr); } } impl DmaCtrl for TestCtrl { fn ndtr(&self) -> usize { self.next_ndtr.borrow_mut().unwrap() } fn tcif(&self) -> bool { self.tcif } fn clear_tcif(&mut self) { self.tcif = false; } } #[test] fn empty() { let mut dma_buf = [0u8; 16]; let ringbuf = DmaRingBuffer::new(&mut dma_buf); assert!(ringbuf.is_empty()); assert_eq!(0, ringbuf.len()); } #[test] fn can_read() { let mut dma_buf: [u8; 16] = array::from_fn(|idx| idx as u8); // 0, 1, ..., 15 let mut ctrl = TestCtrl::new(); let mut ringbuf = DmaRingBuffer::new(&mut dma_buf); ringbuf.ndtr = 6; assert!(!ringbuf.is_empty()); assert_eq!(10, ringbuf.len()); let mut buf = [0; 2]; assert_eq!(2, ringbuf.read(&mut ctrl, &mut buf).unwrap()); assert_eq!([0, 1], buf); assert_eq!(8, ringbuf.len()); let mut buf = [0; 2]; assert_eq!(2, ringbuf.read(&mut ctrl, &mut buf).unwrap()); assert_eq!([2, 3], buf); assert_eq!(6, ringbuf.len()); let mut buf = [0; 8]; assert_eq!(6, ringbuf.read(&mut ctrl, &mut buf).unwrap()); assert_eq!([4, 5, 6, 7, 8, 9], buf[..6]); assert_eq!(0, ringbuf.len()); let mut buf = [0; 2]; assert_eq!(0, ringbuf.read(&mut ctrl, &mut buf).unwrap()); } #[test] fn can_read_with_wrap() { let mut dma_buf: [u8; 16] = array::from_fn(|idx| idx as u8); // 0, 1, ..., 15 let mut ctrl = TestCtrl::new(); let mut ringbuf = DmaRingBuffer::new(&mut dma_buf); ringbuf.first = 12; ringbuf.ndtr = 10; // The dma controller has written 4 + 6 bytes and has reloaded NDTR ctrl.tcif = true; ctrl.set_next_ndtr(10); assert!(!ringbuf.is_empty()); assert_eq!(6 + 4, ringbuf.len()); let mut buf = [0; 2]; assert_eq!(2, ringbuf.read(&mut ctrl, &mut buf).unwrap()); assert_eq!([12, 13], buf); assert_eq!(6 + 2, ringbuf.len()); let mut buf = [0; 4]; assert_eq!(4, ringbuf.read(&mut ctrl, &mut buf).unwrap()); assert_eq!([14, 15, 0, 1], buf); assert_eq!(4, ringbuf.len()); } #[test] fn can_read_when_dma_writer_is_wrapped_and_read_does_not_wrap() { let mut dma_buf: [u8; 16] = array::from_fn(|idx| idx as u8); // 0, 1, ..., 15 let mut ctrl = TestCtrl::new(); let mut ringbuf = DmaRingBuffer::new(&mut dma_buf); ringbuf.first = 2; ringbuf.ndtr = 6; // The dma controller has written 6 + 2 bytes and has reloaded NDTR ctrl.tcif = true; ctrl.set_next_ndtr(14); let mut buf = [0; 2]; assert_eq!(2, ringbuf.read(&mut ctrl, &mut buf).unwrap()); assert_eq!([2, 3], buf); assert_eq!(true, ctrl.tcif); // The interrupt flag IS NOT cleared } #[test] fn can_read_when_dma_writer_is_wrapped_and_read_wraps() { let mut dma_buf: [u8; 16] = array::from_fn(|idx| idx as u8); // 0, 1, ..., 15 let mut ctrl = TestCtrl::new(); let mut ringbuf = DmaRingBuffer::new(&mut dma_buf); ringbuf.first = 12; ringbuf.ndtr = 10; // The dma controller has written 6 + 2 bytes and has reloaded NDTR ctrl.tcif = true; ctrl.set_next_ndtr(14); let mut buf = [0; 10]; assert_eq!(10, ringbuf.read(&mut ctrl, &mut buf).unwrap()); assert_eq!([12, 13, 14, 15, 0, 1, 2, 3, 4, 5], buf); assert_eq!(false, ctrl.tcif); // The interrupt flag IS cleared } #[test] fn cannot_read_when_dma_writer_wraps_with_same_ndtr() { let mut dma_buf = [0u8; 16]; let mut ctrl = TestCtrl::new(); let mut ringbuf = DmaRingBuffer::new(&mut dma_buf); ringbuf.first = 6; ringbuf.ndtr = 10; ctrl.set_next_ndtr(9); assert!(ringbuf.is_empty()); // The ring buffer thinks that it is empty // The dma controller has written exactly 16 bytes ctrl.tcif = true; let mut buf = [0; 2]; assert_eq!(Err(OverrunError), ringbuf.read(&mut ctrl, &mut buf)); assert_eq!(false, ctrl.tcif); // The interrupt flag IS cleared } #[test] fn cannot_read_when_dma_writer_overwrites_during_not_wrapping_read() { let mut dma_buf: [u8; 16] = array::from_fn(|idx| idx as u8); // 0, 1, ..., 15 let mut ctrl = TestCtrl::new(); let mut ringbuf = DmaRingBuffer::new(&mut dma_buf); ringbuf.first = 2; ringbuf.ndtr = 6; // The dma controller has written 6 + 3 bytes and has reloaded NDTR ctrl.tcif = true; ctrl.set_next_ndtr(13); let mut buf = [0; 2]; assert_eq!(Err(OverrunError), ringbuf.read(&mut ctrl, &mut buf)); assert_eq!(false, ctrl.tcif); // The interrupt flag IS cleared } #[test] fn cannot_read_when_dma_writer_overwrites_during_wrapping_read() { let mut dma_buf: [u8; 16] = array::from_fn(|idx| idx as u8); // 0, 1, ..., 15 let mut ctrl = TestCtrl::new(); let mut ringbuf = DmaRingBuffer::new(&mut dma_buf); ringbuf.first = 12; ringbuf.ndtr = 10; // The dma controller has written 6 + 13 bytes and has reloaded NDTR ctrl.tcif = true; ctrl.set_next_ndtr(3); let mut buf = [0; 2]; assert_eq!(Err(OverrunError), ringbuf.read(&mut ctrl, &mut buf)); assert_eq!(false, ctrl.tcif); // The interrupt flag IS cleared } }