Skip to content

Clustering DMA channels into an array has lost CTRL reset values #56

@cbiffle

Description

@cbiffle

I had some code that resembled this:

        for smnum in 0..STRAND_COUNT {
            // irrelevant stuff omitted
            p.DMA.ch[smnum].ch_al1_ctrl.write(|w| unsafe {
                w.read_error().set_bit() // to clear
                    .write_error().set_bit() // to clear
                    .bswap().set_bit()
                    .treq_sel().bits(0 + smnum as u8) // DREQ_PIO0_TX0..3 are contiguous
                    .incr_write().clear_bit()
                    .incr_read().set_bit()
                    .data_size().size_word()
                    .high_priority().set_bit()
                    .en().set_bit()
            });
        }

It causes rather elaborate misbehavior on DMA channel 0.

Can you see why?

Unfortunately, you can't see why, as the critical piece of information does not actually appear in the code. svd2rust defaults any register bitfields that are not explicitly set to its idea of the reset value (a design flaw, imo, but hey). The rp2040 SVD from the RaspPi folx have all the DMA channel registers modeled separately, which is good, because they have different reset values.

Why? Because for the CHAIN_TO field to be a no-op, it has to be set to the ID of the channel being configured. So for channel 0 it's 0, for channel 1, it's 1, etc.

The PAC loses this information from the SVD by throwing away the types of channels 1-7 and using the channel 0 type for everything. Of course, you have to do that to make an array. (svd2rust also makes types distinct just so they can have different reset values, preventing them from being in an array, which, well, you can guess my opinion on that.)

The net effect of this is that every channel other than 0 implicitly triggers channel 0 when it completes. Every time.

I'm not actually sure how to fix this. Moving away from the array would be terribly unfortunate and cause me to basically hack an array back in so that I can loop over channels (god forbid you wanted to allocate channels dynamically, with them all becoming different types!). However, the current situation is a rather elaborate footgun for DMA. I don't believe svd2rust has any notion of a field that must be set explicitly when writing a register, which otherwise would be a reasonable thing to reach for here.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions