This new DMA API corrects possible cache coherency issues on chips with D-Cache, when working with buffers at arbitrary memory locations (i.e. supplied by Python code). The API is used by SPI to fix an issue with corrupt data when reading from SPI using DMA in certain cases. A regression test is included (it depends on external hardware connection). Explanation: 1) It's necessary to invalidate D-Cache after a DMA RX operation completes in case the CPU reads (or speculatively reads) from the DMA RX region during the operation. This seems to have been the root cause of issue #13471 (only when src==dest for this case). 2) More generally, it is also necessary to temporarily mark the first and last cache lines of a DMA RX operation as "uncached", in case the DMA buffer shares this cache line with unrelated data. The CPU could otherwise write the other data at any time during the DMA operation (for example from an interrupt handler), creating a dirty cache line that's inconsistent with the DMA result. Fixes issue #13471. This work was funded through GitHub Sponsors. Signed-off-by: Angus Gratton <angus@redyak.com.au>
44 lines
1.4 KiB
Python
44 lines
1.4 KiB
Python
from machine import SPI
|
|
# Regression test for DMA for DCache coherency bugs with cache line
|
|
# written originally for https://github.com/micropython/micropython/issues/13471
|
|
|
|
# IMPORTANT: This test requires SPI2 MISO (pin Y8 on Pyboard D) to be connected to GND
|
|
|
|
SPI_NUM = 2
|
|
|
|
spi = SPI(SPI_NUM, baudrate=5_000_000)
|
|
buf = bytearray(1024)
|
|
ok = True
|
|
|
|
for offs in range(0, len(buf)):
|
|
v = memoryview(buf)[offs : offs + 128]
|
|
spi.readinto(v, 0xFF)
|
|
if not all(b == 0x00 for b in v):
|
|
print(offs, v.hex())
|
|
ok = False
|
|
|
|
print("Variable offset fixed length " + ("OK" if ok else "FAIL"))
|
|
|
|
# this takes around 30s to run, so skipped if already failing
|
|
if ok:
|
|
for op_len in range(1, 66):
|
|
wr = b"\xFF" * op_len
|
|
for offs in range(1, len(buf) - op_len - 1):
|
|
# Place some "sentinel" values before and after the DMA buffer
|
|
before = offs & 0xFF
|
|
after = (~offs) & 0xFF
|
|
buf[offs - 1] = before
|
|
buf[offs + op_len] = after
|
|
v = memoryview(buf)[offs : offs + op_len]
|
|
spi.write_readinto(wr, v)
|
|
if (
|
|
not all(b == 0x00 for b in v)
|
|
or buf[offs - 1] != before
|
|
or buf[offs + op_len] != after
|
|
):
|
|
print(v.hex())
|
|
print(hex(op_len), hex(offs), hex(buf[offs - 1]), hex(buf[offs + op_len]))
|
|
ok = False
|
|
|
|
print("Variable offset and lengths " + ("OK" if ok else "FAIL"))
|