[][src]Struct imxrt_hal::dma::Circular

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 the Buffer. 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() or drain() 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]

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]

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]

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]

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>

Notable traits for Drain<'a, E>

impl<'a, E: Element> Iterator for Drain<'a, E> type Item = 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

impl<E: Debug> Debug for Circular<E>[src]

impl<E: Element> Send for Circular<E>[src]

Auto Trait Implementations

impl<E> !Sync for Circular<E>

impl<E> Unpin for Circular<E>

Blanket Implementations

impl<T> Any for T where
    T: 'static + ?Sized
[src]

impl<T> Borrow<T> for T where
    T: ?Sized
[src]

impl<T> BorrowMut<T> for T where
    T: ?Sized
[src]

impl<T> From<T> for T[src]

impl<T, U> Into<U> for T where
    U: From<T>, 
[src]

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]

type Error = Infallible

The type returned in the event of a conversion error.

impl<T, U> TryInto<U> for T where
    U: TryFrom<T>, 
[src]

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.