heapless icon indicating copy to clipboard operation
heapless copied to clipboard

Add `embedded_dma` feature

Open qwerty19106 opened this issue 2 years ago • 9 comments

I added embedded_dma feature to one can send Vec, pool::object::Object<Vec> and pool::boxed::Box<Vec> to embedded DMA as read/write buffers. It allows to use Vec with embedded DMA of HAL crates, for example stm32f4xx_hal::dma::Transfer.

Unfortunately there is a issue that does not allow to implement embedded_dma::WriteBuffer and embedded_dma::ReadBuffer transparently. Thus it requires optional embedded_dma dependency now.

qwerty19106 avatar Mar 16 '23 08:03 qwerty19106

cfail failed on file cfail/ui/not-send.stderr: $DIR replaced on ui. @korken89

qwerty19106 avatar Mar 16 '23 13:03 qwerty19106

CI is fixed: $DIR replaced on ui within cfail/ui/not-send.stderr. It is ready for review.

qwerty19106 avatar Mar 22 '23 08:03 qwerty19106

the impl for Vec is not sound, it doesn't satisfy this safety precondition from here:

As long as no &mut self method is called on the implementing object:

  • read_buffer must always return the same value, if called multiple times.

because you can move the vec without calling any &mut self method, which will cause the address returned by read_buffer to change.

Dirbaio avatar Oct 20 '23 23:10 Dirbaio

ReadBuffer implemented for [T; N], which can be moved without calling any &mut self method also. See ReadTarget for details.

I don't understand ReadBuffer requirements, you can give an example of type, which can implement ReadBuffer safely?

P.S. Rebased.

qwerty19106 avatar Feb 15 '24 08:02 qwerty19106

No, ReadBuffer is implemented for types that deref to [T; N] and additionally implement StableDeref, which means the deref'd-to array doesn't move even if you move the ReadBuffer.

ie it's implemented for things like &'static mut [T; N] and Box<[T; N]>, but not the raw [T; N] because that one can move. heapless::Vec is like the latter.

Dirbaio avatar Feb 15 '24 13:02 Dirbaio

From ReadTarget documentation:

as_read_buffer must adhere to the safety requirements documented for ReadBuffer::read_buffer.

I think that it is not valid, because StableDeref provides some of ReadBuffer requirements already. I suggest:

  1. fix ReadTarget and WriteTarget documentation
  2. Implement ReadTarget and WriteTarget for Vec

qwerty19106 avatar Feb 16 '24 10:02 qwerty19106

I think the embedded-dma docs are incomplete. They say "as long as no &mut self methods are called", but they should also say "and self is not moved". That's the original intent of the traits. The way HALs in the wild use embedded-dma needs it, or they would be unsound.

So it's not sound to implement the traits for heapless::Vec (or for any other container that stores the data inline, not behind a pointer).

Dirbaio avatar Feb 16 '24 10:02 Dirbaio

Box<[T; N]> implements ReadBuffer now, but Box<Vec<T, N>> not implements. It is very oddly.

I think than ReadBuffer should require "and self is not moved", but ReadTarget no, because StableDeref provides it already.

qwerty19106 avatar Feb 16 '24 11:02 qwerty19106

Requirements of ReadBuffer trait:

The implementing type must be safe to use for DMA reads. This means:

It must be a pointer that references the actual buffer.
As long as no &mut self method is called on the implementing object:
    read_buffer must always return the same value, if called multiple times.
    The memory specified by the pointer and size returned by read_buffer must not be freed during the transfer it is used in as long as self is not dropped.

Probably need to add "and buffer is not moved" here.

Additional requirements of ReadBuffer::read_buffer method:

Once this method has been called, it is unsafe to call any &mut self methods on this object as long as the returned value is in use (by DMA).

Taking into account previous requirements it is enough.

The ReadTarget != ReadBuffer, ReadTarget requirements is the same as ReadBuffer::read_buffer method. The ReadBuffer requirements gaurantees by StableDeref trait:

impl<B, T> ReadBuffer for B where
    B: Deref<Target = T> + StableDeref + 'static,
    T: ReadTarget + ?Sized,

Thus the ReadTarget can be implemented for any newtype around [u8; N], [u16; N], et.al. The heapless::Box<Vec<u8, N>> should implement ReadBuffer already, because heapless::Box implement Deref to Vec<u8, N>, and Vec<u8, N> implement Deref to [u8], and [u8] implement ReadTarget. But it not works factically, see embedded-dma#25 for details.

Thus I consider that we should implement ReadTarget for Vec manually, although this will have to be removed when ReadBuffer API will be changed.

qwerty19106 avatar Mar 13 '24 08:03 qwerty19106