Skip to content

Conversation

ritikmishra
Copy link
Contributor

Summary

I was running into an issue with using multiple SPI+DMA transactions in a very tight control loop: sometimes the spi.transfer would hang indefinitely because the RX DMA channel was enabled, but somehow the TX DMA channel was disabled.

I managed to figure out that this was happening because the DMAC interrupt handler does not wait for the channel to finish disabling itself, meaning that sometimes the disable doesn't finish until after my loop's next SPI enable is dispatched.

The fix is for the DMAC interrupt handler to wait for the channel to finish disabling before waking the future up again.

--

Note that TransferFuture is not a public item, so it should be OK for its return type to change.

Checklist

  • All new or modified code is well documented, especially public items
  • No new warnings or clippy suggestions have been introduced - CI will deny clippy warnings by default! You may #[allow] certain lints where reasonable, but ideally justify those with a short comment.

});

while dmac.channel(channel).chctrla().read().enable().bit_is_set() {
core::hint::spin_loop();
Copy link
Contributor

Choose a reason for hiding this comment

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

Is spin_loop necessary? I thought that bug had been fixed. Don't we use an empty while body in tons of other places?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

there was a bug where spin_loop was necessary? it seems like sometimes there's an empty body, sometimes spin_loop is used, and sometimes cortex_m::asm::nop() is used

Copy link
Contributor

Choose a reason for hiding this comment

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

Indeed maybe we should standardize at some point, but it's not a source of bugs or anything so I'm not super concerned about it for now

// Prevent the compiler from re-ordering read/write
// operations beyond this fence.
// (see https://docs.rust-embedded.org/embedonomicon/dma.html#compiler-misoptimizations)
atomic::fence(atomic::Ordering::Acquire); // ▼
Copy link
Contributor

Choose a reason for hiding this comment

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

Is there another atomic::fence with Release ordering somewhere else? I thought fences like this were only meaningful when paired across two concurrent functions? I think it's already guaranteed by the language that the enable bit will read true before wake() is called. The reads are volatile, so they can't be removed, and the read is observed, so you can't reorder wake() before it.

Copy link
Contributor

Choose a reason for hiding this comment

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

Yeah, this seems more or less unnecessary. I think the intention was to protect reordering reads/writes to the memory chunk the DMA is accessing, although we already have fences for that at a lower level.

That being said, we had a discussion a while ago about how atomic::fence isn't even enough to guarantee no reordering will take place... IIRC the verdict was that we should use an empty asm! black box instead.

Copy link
Contributor Author

@ritikmishra ritikmishra Sep 18, 2025

Choose a reason for hiding this comment

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

Channel::_enable_private has the corresponding Release fence + is called in the TransferFuture::poll implementation that this ISR interacts with

@jbeaurivage
Copy link
Contributor

Nice, thank you! One small comment, I think this fix should also extend to D11/D21 targets. This should also take care of the clippy warning by the same occasion.

@ritikmishra
Copy link
Contributor Author

oh good catch @jbeaurivage , added in 2d466f1

i clearly don't use the d11/d21 device i own >.<
@jbeaurivage jbeaurivage merged commit ece2aff into atsamd-rs:master Sep 22, 2025
108 of 109 checks passed
@github-actions github-actions bot mentioned this pull request Sep 22, 2025
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