embedded-dma icon indicating copy to clipboard operation
embedded-dma copied to clipboard

Add buffer slices

Open andrewgazelka opened this issue 3 years ago • 2 comments

Depends on #19

Add a means for taking a slice of a buffer. I think the reasoning for this is best explained by the documentation I have written.


A [BufferSlice] is a slice which wraps either a [ReadBuffer] or a [WriteBuffer]

  • When it wraps a [ReadBuffer], it implements [ReadBuffer]
  • When it wraps a [WriteBuffer], it implements [WriteBuffer]
  • To prevent panics and to enforce safety, the given range is coerced between (0, len], where len is the length of the original buffer

Use Case Many HALs use the length of a {Read,Write}Buffer to configure DMA Transfers. However, changing the length of the buffer can be complicated. For instance, consider the case where we want to change the length of a slice for a DMA transfer:

use embedded_dma::{BufferExt, WriteBuffer};
struct DmaTransfer<Buf> {
    buf: Buf,
}
                                                                                         
impl<Buf: WriteBuffer> DmaTransfer<Buf> {
    fn start(buf: Buf) -> Self {
        // DMA logic
        Self { buf }
    }
                                                                                         
    async fn wait_until_done(&self) {
        // to implement
    }
                                                                                         
    fn free(self) -> Buf {
        // busy loop which waits until DMA is done to ensure safety
        self.buf
    }
}
                                                                                         
/// This function is bad because we cannot obtain the original slice—just a subset of it.
async fn dma_transfer_bad1(buffer: &'static mut [u8], length: usize) -> &'static [u8] {
    let sub_slice = &mut buffer[..length];
    let transfer = DmaTransfer::start(sub_slice);
    transfer.wait_until_done().await;
    transfer.free()
}
                                                                                         
/// This function is bad because we cannot unsplit the slice.
async fn dma_transfer_bad2(buffer: &'static mut [u8], length: usize) -> &'static [u8] {
    let (slice_a, slice_b) = buffer.split_at_mut(length);
    let transfer = DmaTransfer::start(slice_a);
    transfer.wait_until_done().await;
    let slice_a = transfer.free();
    // can't unsplit!!!
    slice_a
}
                                                                                         
/// This function is good—we can get the whole slice back!
fn dma_transfer(buffer: &'static mut [u8], length: usize) -> &'static [u8] {
    let buffer_slice = buffer.into_buffer_slice(..length);
    let transfer = DmaTransfer::start(buffer_slice);
    // we are commenting this out so we can actually test it without an async runtime
    // transfer.wait_until_done().await;
    let buffer_slice = transfer.free();
    buffer_slice.inner()
}
                                                                                         
const SIZE: usize = 1024;
                                                                                         
let buffer = {
    static mut BUFFER: [u8; 1024] = [0_u8; SIZE];
    unsafe { &mut BUFFER }
};
                                                                                         
assert_eq!(buffer.len(), SIZE);
                                                                                         
let returned_buffer = dma_transfer(buffer, 123);
                                                                                         
assert_eq!(returned_buffer.len(), SIZE);

Considerations

  • [ ] Should we include extension method into_buffer_slice or not?
    • An example where it might not look as aesthetically pleasing is let buf_slice = (&BUF).into_buffer_slice(123..); since we have to add ()'s around &BUF
    • [ ] If we do include this, should we only have into_buffer_slice implemented for Read and Write Buffers, not for all sized structs? We could instead have methods into_{write, read}_buffer_slice.

andrewgazelka avatar Nov 17 '21 22:11 andrewgazelka

@eldruin @thalesfragoso any thoughts?

andrewgazelka avatar Jan 21 '22 18:01 andrewgazelka

Sorry, I haven't had the time to review this yet. However, I would like to say that I agree with the underlying reasoning and I know that tokio-uring has something similar, so maybe it's worth checking it, even if it's just to compare to.

thalesfragoso avatar Feb 01 '22 18:02 thalesfragoso