Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Change DMA to have a non-static requirement #271

Closed
wants to merge 2 commits into from

Conversation

andrewgazelka
Copy link
Contributor

Background

Even though it is mentioned in the comments of some of the examples, the memory accessed by DMA does not necessarily need to be on the stack. If we defined memory.x to have our stack somewhere where DMA can access, we can pass in a stack buffer just fine.

Changes

  • Update comments referencing stack requirement
  • Change Static{Read,Write}Buffer -> Static{Read,Write}Buffer

@andrewgazelka andrewgazelka changed the title Change DMA to be non-static requirement Change DMA to have a non-static requirement Oct 31, 2021
@@ -743,8 +743,8 @@ macro_rules! db_transfer_def {
};
}

db_transfer_def!(DBTransfer, init, StaticWriteBuffer, write_buffer, mut;);
db_transfer_def!(ConstDBTransfer, init_const, StaticReadBuffer, read_buffer;
Copy link
Contributor

@mattico mattico Nov 2, 2021

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can you explain this change more?

My understanding is that 'static is required because the hardware DMA transfer can continue regardless if the Transfer is forgotten. For example:

    let spi = spi.disable();
    let mut short_buffer = [0u8; 1024];
    let streams = StreamsTuple::new(dp.DMA1, ccdr.peripheral.DMA1);
    let config = DmaConfig::default().memory_increment(true);
    let mut transfer: Transfer<_, _, PeripheralToMemory, _, _> =
        Transfer::init(streams.0, spi, &mut short_buffer[..], None, config);
    
    transfer.start(|spi| {
        spi.enable_dma_tx();
        spi.inner_mut()
            .cr1
            .write(|w| w.ssi().slave_not_selected().spe().enabled());
        spi.inner_mut().cr1.modify(|_, w| w.cstart().started());
    });

    mem::forget(transfer); // <<<<<<<<

    loop {
        // &ref to buffer while DMA transfer is still writing to buffer
        info!("short_buffer = {:?}", short_buffer);
    }

This is undefined behavior because the compiler can assume that the memory doesn't change between subsequent reads of &-references. From a quick search I can't find anything documenting this explicitly, but it's apparently an open question if it's even valid to use volatile reads on memory altered by external "threads" - which wouldn't be a question if it were valid for &-references.

Perhaps there could be an unsafe constructor for non-'static buffers which has the invariant that the buffer must live as long as the hardware DMA transfer?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I forgot about this. I think an unsafe constructor is a good solution, I can't think of a safe way to do this.

@andrewgazelka andrewgazelka force-pushed the dma-non-static branch 4 times, most recently from 77f2b9f to a83cb46 Compare November 7, 2021 20:50
@andrewgazelka
Copy link
Contributor Author

I added an unsafe variant: unsafe fn init_unsafe(...)

Copy link
Member

@richardeoin richardeoin left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I see the usefulness of this, but I'm not entirely convinced this is "correct" to have in the HAL either. As noted in the embedded-dma docs, it's already possible to "escape" the 'static requirement by unsafe impling StaticWriteBuffer for a your own types. I think this method essentially does the same thing, but less verbose (no separate unsafe impl).

There's also a PR open rust-embedded/embedded-dma#10 that I think I support, which would remove the current WriteBuffer. If that PR was merged, then we'd actually have to do some more work to try to implement init_unsafe again (and I don't think we want to do that).

DIR: Direction,
PERIPHERAL: TargetAddress<DIR>,
BUF: StaticWriteBuffer<Word = <PERIPHERAL as TargetAddress<DIR>>::MemSize>,
BUF: WriteBuffer<Word = <PERIPHERAL as TargetAddress<DIR>>::MemSize>,
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The WriteBuffer bound is redundant here, so it should be removed

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I thought so as well, but for some reason, the code doesn't compile without it.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ah, it is because StaticWriteBuffer and WriteBuffer both have separate associated type Words. Weird...

@andrewgazelka
Copy link
Contributor Author

I see the usefulness of this, but I'm not entirely convinced this is "correct" to have in the HAL either. As noted in the embedded-dma docs, it's already possible to "escape" the 'static requirement by unsafe impling StaticWriteBuffer for a your own types. I think this method essentially does the same thing, but less verbose (no separate unsafe impl).

There's also a PR open rust-embedded/embedded-dma#10 that I think I support, which would remove the current WriteBuffer. If that PR was merged, then we'd actually have to do some more work to try to implement init_unsafe again (and I don't think we want to do that).

Ah. I wasn't aware of the StaticWriteBuffer pattern. I agree then that is probably that way to go then.

@andrewgazelka
Copy link
Contributor Author

andrewgazelka commented Nov 17, 2021

@richardeoin Correct me if I am wrong, but this is a valid concern. We could also wait to for this to get fixed in embedded-dma.

@andrewgazelka
Copy link
Contributor Author

closing in favor of #313

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants