Struct imxrt_hal::dma::Circular [−][src]
pub struct Circular<E> { /* fields omitted */ }
A circular DMA buffer
Circular
provides a push()
and pop()
interface to manipulate the backing memory. Unlike a Linear
adapter, a Circular
may be accessed
while a transfer is in progress. For example, you may continue to push()
elements into a Circular
while a DMA transfer
is reading from the previously-pushed values. The DMA controller will interpret the memory as a circular buffer, and it will
wrap around when reading / writing elements to the memory, just as push()
and pop()
wrap around the buffer.
Circular
has two requirements:
- the size of the backing
Buffer
is a power of two - the alignment of the
Buffer
is a multiple of the size of theBuffer
. The size includes the element type and the buffer length.
If you don’t hold these two requirements, you will fail to construct a Circular
. We enforce the requirements even through
the unsafe
interface.
To enforce the alignment requirement, create a newtype struct that specifies an alignment. See the example for more guidance.
The capacity of a Circular
buffer is one less than the size of the backing memory. For example, if a Circular
is backed
by a 512-element buffer, the capacity is 511.
Using Circular
as a DMA source
push()
your elements into the buffer (A)- hand-off your
Circular
instance to another DMA object - (optional) during the transfer, continue to
push()
elements into the buffer - when the transfer completes, you will be able to overwrite the elements supplied during (A)
Using Circular
as a DMA destination
reserve()
a number of elements to hold the incoming elements- hand-off your
Circular
instance to another DMA object - when the transfer completes, you will be able to
pop()
ordrain()
the received elements
The Circular
adapter will “own” the Buffer
provided on construction. However, when
it’s dropped, Circular
will not release ownership. Either keep the object alive, or use the
new_unchecked()
method to construct a new Circular
adapter over
the same buffer.
Example
use imxrt_hal::dma; // A newtype to enforce the required alignment #[repr(align(1024))] // 512 * 2 for size of u16 struct Align(dma::Buffer<[u16; 512]>); static BUFFER: Align = Align(dma::Buffer::new([0; 512])); let mut circular = dma::Circular::new(&BUFFER.0).unwrap(); // BUFFER is taken and cannot be used again assert_eq!(dma::Circular::new(&BUFFER.0).unwrap_err(), dma::CircularError::BufferTaken); // The maximum number of elements is one less than the size of the backing // memory. assert_eq!(circular.capacity(), 511); circular.push(1); circular.push(2); circular.push(3); assert_eq!(circular.len(), 3); assert_eq!(circular.pop(), Some(1)); assert_eq!(circular.pop(), Some(2)); assert!(!circular.is_empty()); assert_eq!(circular.pop(), Some(3)); assert_eq!(circular.pop(), None); assert!(circular.is_empty());
If the underlying buffer size is not a power of two, we cannot create a circular DMA queue:
#[repr(align(64))] struct Align(dma::Buffer<[u16; 30]>); static BUFFER: Align = Align(dma::Buffer::new([0; 30])); let err = dma::Circular::new(&BUFFER.0).expect_err("30 is not a power of two"); assert_eq!(err, dma::CircularError::NotPowerOfTwo);
If the alignment is not a multiple of the size, we cannot create a circular DMA queue:
#[repr(align(256))] // Should be 1024 to account for u32 size struct Align(dma::Buffer<[u32; 256]>); static BUFFER: Align = Align(dma::Buffer::new([0; 256])); let err = dma::Circular::new(&BUFFER.0).expect_err("incorrect alignment"); assert_eq!(err, dma::CircularError::IncorrectAlignment);
Notes on runtime alignment checks
The implementation might miss a circular memory buffer that has an incorrect alignment specification
but was, by chance, put in a memory location that supports the alignment requirements. For example,
a buffer that should be 64-byte aligned, but is incorrectly labeled #[repr(32)]
, may be placed at
a 64-byte boundary. The implementation cannot detect these “you got lucky” situations, and you’re program
will work. But, you may see an “incorrect alignment” error on a different software build.
If you start to notice “incorrect alignment” errors across different builds of your software, ensure that your circular buffers are meeting the alignment requirements described above.
Implementations
impl<E: Element> Circular<E>
[src]
pub fn new<B>(buffer: &'static Buffer<B>) -> Result<Self, CircularError> where
B: AsMutSlice<Element = E>,
[src]
B: AsMutSlice<Element = E>,
Creates a new circular DMA buffer using the memory supplied by buffer
pub unsafe fn new_unchecked<B>(
buffer: &'static Buffer<B>
) -> Result<Self, CircularError> where
B: AsMutSlice<Element = E>,
[src]
buffer: &'static Buffer<B>
) -> Result<Self, CircularError> where
B: AsMutSlice<Element = E>,
Creates a new circular DMA buffer using the memory supplied by buffer
, but do not
check for buffer ownership
Safety
Caller must ensure that the buffer
is not in use anywhere else. Otherwise, there will be
more than one owner of mutable memory.
pub unsafe fn from_raw<B>(raw: &mut B) -> Result<Self, CircularError> where
B: AsMutSlice<Element = E>,
[src]
B: AsMutSlice<Element = E>,
Creates a new circular DMA buffer from arbitrary memory
Safety
Caller must ensure that the lifetime of raw
is greater than all the DMA transfers that use
the memory. Caller must ensure that there are no other mutable references to this memory.
use imxrt_hal::dma; #[repr(align(64))] struct Align([u8; 64]); let mut my_memory = Align([0; 64]); // my_memory is stack-allocated, so we need to ensure that the `Circular` doesn't // outlive my_memory. let mut circular = unsafe { dma::Circular::from_raw(&mut my_memory.0).unwrap() };
pub fn len(&self) -> usize
[src]
Returns the number of readable elements in the queue
pub fn is_empty(&self) -> bool
[src]
Returns true
if the queue has no readable elements
pub fn clear(&mut self)
[src]
Clears the readable contents from the queue
let mut circular = dma::Circular::new(&BUFFER.0).unwrap(); circular.insert(0..30); assert_eq!(circular.len(), 30); circular.clear(); assert!(circular.is_empty());
pub fn capacity(&self) -> usize
[src]
Returns the number of elements the circular queue can hold
The capacity is always one less than the number of elements in the backing buffer.
pub fn push(&mut self, element: E) -> bool
[src]
Pushes an element into the circular queue
Returns true
if the element was enqueued, or false
if there wasn’t enough space
pub fn insert<I>(&mut self, iter: I) -> usize where
I: IntoIterator<Item = E>,
[src]
I: IntoIterator<Item = E>,
Inserts elements from iter
into the circular buffer, returning the number
elements inserted into the buffer
If inserting an element would overwrite an unread element,
insert
may not insert all the elements into the buffer.
use imxrt_hal::dma; #[repr(align(64))] struct Align(dma::Buffer<[u16; 32]>); static BUFFER: Align = Align(dma::Buffer::new([0; 32])); let mut circular = dma::Circular::new(&BUFFER.0).unwrap(); assert_eq!(circular.insert(0..30), 30); assert_eq!(circular.insert(31..60), 1);
pub fn peek(&self) -> Option<E>
[src]
Peeks at the next element in the queue
Returns None
if there are no elements to read.
pub fn pop(&mut self) -> Option<E>
[src]
Remove the next element from the queue
pub fn drain(&mut self) -> Drain<'_, E>ⓘ
[src]
Returns an iterator that can drain the readable contents from the circular queue
Each iteration calls pop()
, returning the next readable element
in the queue, until the readable elements are exhausted. If the
Drain
iterator is dropped before it drains the elements, those
elements remain in the queue.
let mut circular = dma::Circular::new(&BUFFER.0).unwrap(); circular.insert(0..30); let elems = circular.drain().take(15); for (elem, actual) in elems.zip(0..15) { assert_eq!(elem, actual); } // The `drain()` didn't completely exhaust the circular buffer, // so we can `pop()` the next element. assert_eq!(circular.pop().unwrap(), 15);
pub fn reserve(&mut self, reservation: usize)
[src]
Reserves reservation
number of elements to be used as a DMA transfer destination
Use reserve()
when you want to receive data into the circular buffer. Once the transfer
completes, the reservation
number of elements will be readable.
reservation
is capped at the capacity of the circular buffer.
Trait Implementations
Auto Trait Implementations
Blanket Implementations
impl<T> Any for T where
T: 'static + ?Sized,
[src]
T: 'static + ?Sized,
impl<T> Borrow<T> for T where
T: ?Sized,
[src]
T: ?Sized,
impl<T> BorrowMut<T> for T where
T: ?Sized,
[src]
T: ?Sized,
pub fn borrow_mut(&mut self) -> &mut T
[src]
impl<T> From<T> for T
[src]
impl<T, U> Into<U> for T where
U: From<T>,
[src]
U: From<T>,
impl<T> Same<T> for T
type Output = T
Should always be Self
impl<T, U> TryFrom<U> for T where
U: Into<T>,
[src]
U: Into<T>,
type Error = Infallible
The type returned in the event of a conversion error.
pub fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>
[src]
impl<T, U> TryInto<U> for T where
U: TryFrom<T>,
[src]
U: TryFrom<T>,