adafruit / circuitpython

CircuitPython - a Python implementation for teaching coding with microcontrollers
https://circuitpython.org
Other
4.09k stars 1.22k forks source link

Incorrect PIO background write on RP2040 #9678

Closed jepler closed 3 weeks ago

jepler commented 3 weeks ago

CircuitPython version

Adafruit CircuitPython 9.1.4 on 2024-09-17; Adafruit Metro RP2040 with rp2040
Board ID:adafruit_metro_rp2040
UID:DF6260785F3B3E2B

Code/REPL

# SPDX-FileCopyrightText: 2022 Jeff Epler, written for Adafruit Industries
#
# SPDX-License-Identifier: MIT

import array
import board
import supervisor
from rp2pio import StateMachine
from adafruit_pioasm import Program

# Pixel stream is very similar to NeoPixel WS2812B, but inverted.
#
# Pixel time is 1.25us (800kHz)
#
# Datasheet low times for a "0" bit are 310 (min) 360 (typ) 410 (max) ns
# Datasheet high times for a "1" bit are 650 (min) 720 (typ) 1000 (max) ns
#
# Operating PIO at 14x the bit clock lets us achieve nominal 357ns and 714ns
DEBUG = 1 # Set to 1 to output OPPOSITE signal on the next higher pin
_program = Program(
    f"""
.side_set 2
    nop side 2 [7]
.wrap_target
    pull ifempty        side 0
    out pins 1          side 1
.wrap
        """
)

data = array.array('L', range(64))

sm = StateMachine(
    _program.assembled,
    auto_pull=False,
    first_sideset_pin=board.A1,
    first_out_pin=board.A0,
    out_shift_right=False,
    pull_threshold=32,
    frequency=800_000 * 14,
    **_program.pio_kwargs,
)

sm.background_write(loop=data)
while True:
    pass

Behavior

This program should write 32-bit SPI data 0x0 ... 0x3f, forever.

It seems like this is what it does on RP2350 (with recent-ish main branch build; though I could test more closely)

On RP2040, the first repetition of the data is truncated.

This causes problems with PIO programs like the "background writing neopixel" because when the data organization is wrong, the wrong parts of the DMA data stream can be used for a bit count or a delay count.

Description

No response

Additional information

No response

jepler commented 3 weeks ago

Doing a single non-looping background write first may be a workaround for the problem:

sm.background_write(data)
time.sleep(.01)
sm.background_write(loop=data)
jepler commented 3 weeks ago

@ladyada for the TM1814 program you can also try with this workaround

    def _transmit(self, buf):
        self._buf = buf
        ## WORKAROUND FOR https://github.com/adafruit/circuitpython/issues/9678
        self._sm.background_write(memoryview(buf).cast("L"), swap=True)
        time.sleep(.01)
        ## END WORKAROUND
        self._sm.background_write(loop=memoryview(buf).cast("L"), swap=True)