-
Notifications
You must be signed in to change notification settings - Fork 105
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
DMA buffer swap logic #171
Conversation
I haven't looked at all the PR yet, but one thing you could change is to use |
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 And btw, this is the exactly use case of |
There was a problem hiding this 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
There was a problem hiding this 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
Ack. It felt to me like it has been there for much longer. Changed to |
There was a problem hiding this 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, |
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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()
.
There was a problem hiding this comment.
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
There was a problem hiding this comment.
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). |
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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.
There was a problem hiding this 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.
I have tested this on hardware and verified that it functions as intended. |
This PR (into the
dma
branch) rewrites thenext_transfer()
andnext_transfer_with()
implementations.CurrentBuffer
enums renamed to be more accurate and peripheral-matching.usize
for the addresses instead ofu32
.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.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 callsnext_transfer_with()
with triple buffering (one active, one inactive, and one user).fence()
added.Pending:
BDMA
is not done.@thalesfragoso @richardeoin @ryan-summers comments appreciated.