Skip to content

Conversation

@sfe-SparkFro
Copy link
Contributor

Summary

Fixes #18471

Testing

Before this change, the reproduction code in #18471 freezes within ~100 loop iterations. After this change, the reproduction code does not freeze (at least, not within 10k loop iterations).

Trade-offs and Alternatives

I don't know the original motivation for using DMA channels for large SPI transfers, but one downside of this change is that long interrupts could hold up SPI transfers. I don't know how problematic that really is, but users should be writing their interrupts to be efficient anyways.

If a user really want to use DMA channels for large SPI transfers, they can easily do this with the existing MicroPython API. Something like this should work (I've not tested it myself):

src_data = bytearray(1024)
DATA_REQUEST_INDEX = 24

spi = machine.SPI(0)

dma = rp2.DMA()
ctrl = dma.pack_ctrl(size=0, inc_write=False, treq_sel=DATA_REQUEST_INDEX)

dma.configure(
    read=src_data,
    write=spi,
    count=len(src_data),
    ctrl=ctrl,
    trigger=True
)

An alternative solution could be to enable to the user to select whether they want to use the DMA for large transfers, either when the SPI constructor is called, or when the read/write functions are called.

@github-actions
Copy link

github-actions bot commented Dec 16, 2025

Code size report:

Reference:  esp32/mpconfigport: Enable Zcmp opcodes for ESP32P4. [6341258]
Comparison: rp2/machine_spi: Don't use DMA for SPI transfers if PSRAM is enabled. [merge of 403b109]
  mpy-cross:    +0 +0.000% 
   bare-arm:    +0 +0.000% 
minimal x86:    +0 +0.000% 
   unix x64:    +0 +0.000% standard
      stm32:    +0 +0.000% PYBV10
     mimxrt:    +0 +0.000% TEENSY40
        rp2:    +0 +0.000% RPI_PICO_W
       samd:    +0 +0.000% ADAFRUIT_ITSYBITSY_M4_EXPRESS
  qemu rv32:    +0 +0.000% VIRT_RV32

@peterhinch
Copy link
Contributor

Is this a bug or a known limitation? If the bus arbitration for PSRAM can fail irretrievably in the face of concurrent access by two DMA channels, the fault will eventually occur in user code. In this case disabling DMA in the SPI driver is a workround rather than a fix. If, rather than a bug, there is an inherent limit on DMA data rates it should be documented.

Fixes bug where SPI can freeze if another DMA channel is
tranferring to/from PSRAM.

Signed-off-by: Dryw Wade <dryw.wade@sparkfun.com>
@sfe-SparkFro
Copy link
Contributor Author

Force pushed 403b109, which only disables DMA transfers if PSRAM is enabled. @dpgeorge Please have a look. Haven't tested yet, don't have hardware with me right now.

Is this a bug or a known limitation?

The fact that DMA transfers to/from PSRAM can stall other DMA channels is a known limitation. Section 4.4.3 of the RP2350 datasheet states this pretty clearly in the second paragraph:

...QMI serial transfers force lengthy bus stalls on the DMA... stalling the DMA prevents any other active DMA channels from making progress during this time, slowing overall DMA throughput.

However, the current MicroPython SPI implementation results in a bug where the processor can get stuck in an infinite loop, because it does nothing to consider the above limitation. IMO, it should at least do something to guarantee that it can't get stuck in an infinite loop.

In this case disabling DMA in the SPI driver is a workround rather than a fix.

Yes, outright disabling DMA transfers is certainly a workaround. Perhaps an alternative fix could be something like the following?

         dma_start_channel_mask((1u << chan_rx) | (1u << chan_tx));
-        dma_channel_wait_for_finish_blocking(chan_rx);
         dma_channel_wait_for_finish_blocking(chan_tx);

+        // Wait for SPI to finish, and all data to be received by RX DMA
+        while (spi_is_busy(self->spi_inst) || spi_is_readable(self->spi_inst)) {
+        }
+
+        // If RX DMA still has transfers pending, raise an error
+        if (dma_channel_hw_addr(chan_rx)->transfer_count) {
+            mp_raise_OSError(MP_EIO);
+        }
    }

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

rp2: Max baudrate SPI can freeze if another DMA channel transfers to/from PSRAM

3 participants