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

DMA buffer swap logic #171

Merged
merged 10 commits into from
Jan 5, 2021

Conversation

jordens
Copy link
Member

@jordens jordens commented Dec 1, 2020

This PR (into the dma branch) rewrites the next_transfer() and next_transfer_with() implementations.

  • CurrentBuffer enums renamed to be more accurate and peripheral-matching.
  • Added a few stream methods to expose the dbm/ct bits in joint semantics (also faster).
  • Using usize for the addresses instead of u32.
  • The non-double buffer and the double buffer code paths are unified.
  • It poisons the inactive buffer address to convert silent corruption into DMA/bus errors (@adamgreig 's idea).
  • With that, I'd consider next_transfer_with() safe. It's not DMA overrun safe in general but it is safe from any concurrent buffer access by user and DMA since the buffer the user has access to is always out of reach of the DMA (by poisoning or implicit disable). Certainly safe in the sense that there is nothing that the user can do to make it safer or less safe.
  • Overrun errors due to due to DMA hitting the poison or writing to the active address will not go unnoticed (user needs to clear, handle, and restart).
  • Errors due to next_transfer{,_with} being invoked late (DMA wrap around) can still go unnoticed. I don't think there is anything we can do about it since the peripheral will happily wrap around unattended (AFAWK). But they now are guaranteed to not lead to corruption within a buffer.
  • next_transfer() just calls next_transfer_with() with triple buffering (one active, one inactive, and one user).
  • fence() added.

Pending:

  • This is untested on hardware.
  • BDMA is not done.
  • Returning a/the buffer on error might make sense.

@thalesfragoso @richardeoin @ryan-summers comments appreciated.

@thalesfragoso
Copy link
Member

I haven't looked at all the PR yet, but one thing you could change is to use fence instead of compiler_fence + dmb, that way you avoid a function call (cortex_m::asm::dmb is never inlined on stable).

@jordens
Copy link
Member Author

jordens commented Dec 1, 2020

The cortex-m assembly is being inlined even on stable for a while now.

@thalesfragoso
Copy link
Member

The cortex-m assembly is being inlined even on stable for a while now.

I don't think so, as far as I know, inlined assembly calls in stable was only made possible recently in cortex-m 0.7.0 and only when using linker-plugin-lto. As of today, I don't think there is a single PAC/HAL using cortex-m 0.7, which means that people can't really use 0.7 together with those crates, also, the use of linker-plugin-lto isn't widespread. (See rust-embedded/cortex-m#259 and rust-embedded/cortex-m#139)

And btw, this is the exactly use case of fence, it will insert a DMB instruction.

Copy link
Member

@ryan-summers ryan-summers left a comment

Choose a reason for hiding this comment

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

In general, I think these are nice improvements and increase maintainability. There's still a race condition that I would like to resolve though. Comments inline

src/dma/mod.rs Outdated Show resolved Hide resolved
src/dma/mod.rs Show resolved Hide resolved
src/dma/mod.rs Outdated Show resolved Hide resolved
src/dma/mod.rs Show resolved Hide resolved
Copy link
Member

@ryan-summers ryan-summers left a comment

Choose a reason for hiding this comment

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

Inadvertently approved when I requested changes

@jordens
Copy link
Member Author

jordens commented Dec 2, 2020

I don't think so, as far as I know, inlined assembly calls in stable was only made possible recently in cortex-m 0.7.0 and only when using linker-plugin-lto.

Ack. It felt to me like it has been there for much longer. Changed to fence().

Copy link
Member

@ryan-summers ryan-summers left a comment

Choose a reason for hiding this comment

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

I think one addition to an nb::Result would be appropriate - other than that, we should likely test this on hardware, and pending that, it looks good to me otherwise.

/// DMA not ready to change buffers.
NotReady(T),
NotReady,
Copy link
Member

Choose a reason for hiding this comment

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

Instead of adding a NotReady, can we use an nb::Result::WouldBlock? I think it's essentially being used in the same way - a user needs to wait for the transfer to finish first.

Copy link
Member Author

Choose a reason for hiding this comment

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

I think that's useful. But this error should also carry the unused buffer (otherwise that's lost). The alternative would be to have another closure that gets the unused buffer in case of error, making transfer_next_with() somewhat like Result::map_or_else().

Copy link
Member

Choose a reason for hiding this comment

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

That's a good point, but this PR doesn't support returning the unused buffer on failure anyways

Copy link
Member Author

Choose a reason for hiding this comment

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

That regression is on the TODO list above.

// verification/poisoning and subsequent (old completed) buffer content
// access.
// Cortex-M7: Also protect the corresponding data access sequence.
// NOTE: The data cache also needs to be flushed (if enabled).
Copy link
Member

Choose a reason for hiding this comment

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

It seems to me like this is something we should be doing for all users, since the DMA memory is guaranteed to be in a cache-able memory location. We should do an MVA invalidate for all addresses in the buffer if the d-cache is enabled, as the comment here indicates.

Note that there seems to be some issue here because cortex-m requires ownership of the SCB for invalidate_dcache_by_address() and cache lines must be 32-byte aligned, which means that DMA buffers must be cache-aligned when the d-cache is enabled as well.

Now that I've written all of this, it may be worth breaking out into a separate PR to add support for d-cache-enabled implementations.

https://docs.rs/cortex-m/0.7.0/cortex_m/peripheral/struct.SCB.html#method.invalidate_dcache_by_address

Copy link
Member

Choose a reason for hiding this comment

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

It looks like that would also restrict us to using buffers that are a length that is a multiple of the cache size.

Copy link
Member

Choose a reason for hiding this comment

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

Note that there's a lot of other cache operations that we aren't supporting (such as cleaning the active buffer to commit it to memory before starting a DMA write), so this isn't necessarily the only place where cache operation support needs to be added. Let's leave this for the future though and add a disclaimer that DMA does not support the D-cache being enabled.

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.

Looks good to me, with the caveat that I haven't tried it on hardware either. Thanks for the improvements u32 -> usize and fence.

I'd say this can be merged into the dma branch as soon as you're happy with it, since I see the whole dma branch as experimental/unstable.

src/dma/traits.rs Outdated Show resolved Hide resolved
src/dma/traits.rs Outdated Show resolved Hide resolved
@ryan-summers
Copy link
Member

I have tested this on hardware and verified that it functions as intended.

@richardeoin
Copy link
Member

richardeoin commented Jan 4, 2021

I've also successfully tested this on hardware. @jordens could you mark this as "ready for review" / let me know merging it into #153 is OK. We can continue any discussion there.

@jordens jordens marked this pull request as ready for review January 4, 2021 20:27
@richardeoin richardeoin merged commit 7b62474 into stm32-rs:dma Jan 5, 2021
@jordens jordens deleted the feature/dma-buffer-swap-logic branch January 5, 2021 22:02
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.

4 participants