8000 dma_channel_abort does not wait for CHAN_ABORT to go zero · Issue #923 · raspberrypi/pico-sdk · GitHub
[go: up one dir, main page]

Skip to content

dma_channel_abort does not wait for CHAN_ABORT to go zero #923

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

Open
alastairpatrick opened this issue Jul 17, 2022 · 6 comments
Open

dma_channel_abort does not wait for CHAN_ABORT to go zero #923

alastairpatrick opened this issue Jul 17, 2022 · 6 comments
Assignees
Labels
documentation Improvements or additions to documentation hardware_dma
Milestone

Comments

@alastairpatrick
Copy link
Contributor

The source for dma_channel_abort from SDK 1.4.0:

static inline void dma_channel_abort(uint channel) {
    check_dma_channel_param(channel);
    dma_hw->abort = 1u << channel;
    // Bit will go 0 once channel has reached safe state
    // (i.e. any in-flight transfers have retired)
    while (dma_hw->ch[channel].ctrl_trig & DMA_CH0_CTRL_TRIG_BUSY_BITS) tight_loop_contents();
}

Note that while it waits for the channel's BUSY flag to go zero, it does not wait for CHAN_ABORT to go zero.

From the datasheet, emphasis mine:

DMA: CHAN_ABORT Register
Offset: 0x444
Description
Abort an in-progress transfer sequence on one or more channels

Each bit corresponds to a channel. Writing a 1 aborts whatever transfer sequence is in progress on that channel. The bit will remain high until any inflight transfers have been flushed through the address and data FIFOs.

After writing, this register must be polled until it returns all-zero. Until this point, it is unsafe to restart the channel.

Also from the datasheet:

At the time an abort is triggered, a channel may have bus transfers currently in flight between the read and write master,
and these transfers cannot be revoked. The CTRL.BUSY flag stays high until these transfers complete, and the channel
reaches a safe state, which generally takes only a few cycles. The channel must not be restarted until its CTRL.BUSY flag
deasserts.
Starting a new sequence of transfers whilst transfers from an old sequence are still in flight can lead to
unpredictable behaviour.

The datasheet does not indicate whether it is necessary to wait for both BUSY to go zero and CHAN_ABORT to go zero or if waiting for either condition is sufficient. It also does not indicate in which order code should wait for them to go zero. As mentioned, dma_channel_abort only waits for BUSY to go zero.

I suggest there is either a bug in dma_channel_abort (if it ought to be waiting for both) or the datasheet would benefit from clarification.

@kilograham kilograham added the documentation Improvements or additions to documentation label Jul 17, 2022
@lurch
Copy link
Contributor
lurch commented Jul 17, 2022

See also Errata RP2040-E13 in the RP2040 datasheet, if you haven't already.

@alastairpatrick
Copy link
Contributor Author

Given RP2040-E13, I agree that this is documentation.

The workaround recommends "Before aborting a channel, clear its interrupt enable. After aborting a channel, poll the CTRL.BUSY bit to wait for completion (not the ABORT bit), clear the spurious IRQ, and restore the interrupt enable."

I suggest this could be improved too. CHAN_ABORT can be used to abort multiple channels with a single write to the register. In fact this seems like the best way to abort multiple channels that trigger each other, with other methods likely to suffer from race conditions.

The workaround recommendation assumes that only a single channel is being aborted. It would be good to confirm that, even when multiple channels are aborted, it is adequate and necessary to check only the CTRL.BUSY bits for each channel in turn and not to check CHAN_ABORT. Without that clarification, it is not clear that checking only CTRL.BUSY is free of race conditions when aborting multiple channels.

@kilograham kilograham added this to the 1.5.1 milestone May 26, 2023
@kilograham
Copy link
Contributor

moving to 1.5.1 so it doesn't get forgotten (even if the docs aren't fixed there)

@tommie
Copy link
tommie commented Apr 29, 2024

In fact this seems like the best way to abort multiple channels that trigger each other, with other methods likely to suffer from race conditions.

Related, and for posterity, I have a DMA control+data setup, with the data channel re-starting control at the end, rather than issuing a null transfer, emulating a ring. This required me to do two CHAN_ABORT on both channels to ensure it stops. I'm thinking the DMA trigger register may already be in the DMA FIFO, making this racy even with CHAN_ABORT. To be safe, I have it in a loop:

while (dma_channel_is_busy(dma_ctrl) || dma_channel_is_busy(dma_data)) {
    dma_channel_abort_mask((1u << dma_ctrl) | (1u << dma_data));
}

@kilograham kilograham modified the milestones: 1.6.0, 1.7.0 Aug 3, 2024
@glopesdev
Copy link
glopesdev commented May 28, 2025

@tommie I have the exact same problem and would like to try your approach, but coming up against dma_channel_abort_mask is undefined. A quick search turned out nothing, so I was wondering if this was your own local definition, or whether the API actually exists anywhere?

P.S.: For future reference, replacing this call with two single-channel dma_channel_abort calls also works, so leaving it for now.

@tommie
Copy link
tommie commented May 28, 2025

@glopesdev Yepp, you're right.

// Aborts several DMA channels at once, and waits for their
// completion. If one channel is triggering another, it may be
// necessary to call this multiple times to resolve the race condition
// of a trigger register being loaded into the DMA FIFO already.
static inline void dma_channel_abort_mask(uint mask) {
  hw_set_bits(&dma_hw->abort, mask);

  while (dma_hw->abort & mask) {
    tight_loop_contents();
  }
}

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

No branches or pull requests

6 participants
0