Skip to content

CircuitPython 4.x hang on PyGamer after about 10 minutes of while loop playing stereo audio #2005

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

Closed
kevinjwalters opened this issue Jul 22, 2019 · 32 comments

Comments

@kevinjwalters
Copy link

kevinjwalters commented Jul 22, 2019

I've got a CircuitPython hang that I've seen on 4.1.0 rc0 and rc1 if I leave some code running. Takes about ten minutes on a PyGamer to hang, it's not predictable but will do it on about one in four executions. The serial connection (Windows 8.1 desktop) becomes disconnected but probably with no close (tapping a key in terminal window makes it realise the PyGamer's gone away) and the CIRCUITPY becomes inaccessible. I left the PyGamer screen illuminated and that's almost same printed line as I see on serial out. I say almost because I get one extra line on the USB serial rather than screen which surprises me a little. If I reset it doesn't do that safe mode thing. I tried 4.0.2 and it didn't seem to do it but because this occurs infrequently I can't say for sure whether 4.0.2 is ok or not.

The output to DAC stops changing too.

I've got GND/A0/A1 from Feather style connector on the back connected to a 'scope. I also have the standard small speaker attached but it's not enabled by the code. I wouldn't expect that to affect things.

I have not seen any MemoryError exceptions during development - seems unlikely memory is being exceeded and certainly no exception seen on output.

I don't have a JLink debugger setup so am a bit limited on gathering more data on this.

Will supply code later.

@kevinjwalters
Copy link
Author

kevinjwalters commented Jul 22, 2019

Powered on PyGamer today, left same code running for 2 mins, then copied a file off and that triggered a restart. That hung within about 10 mins too. Same serial output, the last line is only on the serial over USB not the PyGamer screen. My printed line of text per loop is a touch longer than the PyGamer screen width so each line wraps onto a new screen line.

@kevinjwalters
Copy link
Author

kevinjwalters commented Jul 22, 2019

For completeness, I did one without anything attached to the feather connector. Second run hung after 20 mins.

I tend to leave it running on my desk and glance at PyGamer or tty window occasionally to see if it has stopped scrolling.

@kevinjwalters
Copy link
Author

BTW, is there any kind of watchdog reset in CP?

@dhalbert
Copy link
Collaborator

BTW, is there any kind of watchdog reset in CP?

Nope, nothing that would cause that. There has been an issue of microcontroller fuses being set by accident that enable the watchdog timer, but that has a max of 32 seconds.

@kevinjwalters
Copy link
Author

I was thinking more of something that might save the situation and give a form of auto recovery.

@tannewt
Copy link
Member

tannewt commented Jul 22, 2019

@kevinjwalters Do you have a jlink? We could get you one. Just email me.

@dhalbert
Copy link
Collaborator

dhalbert commented Jul 24, 2019

I got this to hang twice, but only after running for hours. Getting permission to post code here.
@tannewt note where it's hanging.

Backtrace (4.1.0-rc.1):

EDIT: Notice the bad pointer for pixels in frame 3, which is not an address in RAM. pixels is buffer in refresh_area(), and is on the stack. But that may be a red herring, because the DMA descriptors look better. It might be a gdb confusion or a junk value due to how the compiler handles the stack frame.

Program received signal SIGTRAP, Trace/breakpoint trap.
shared_dma_transfer (peripheral=0x43000000, buffer_out=<optimized out>, dest=<optimized out>, src=src@entry=0x0, 
    buffer_in=<optimized out>, buffer_in@entry=0x0, length=<optimized out>, tx=<optimized out>, tx@entry=0 '\000')
    at peripherals/samd/dma.c:192
192	        while ((dma_transfer_status(SHARED_TX_CHANNEL) & 0x3) == 0) {}
(gdb) bt
#0  shared_dma_transfer (peripheral=0x43000000, buffer_out=<optimized out>, dest=<optimized out>, src=src@entry=0x0, 
    buffer_in=<optimized out>, buffer_in@entry=0x0, length=<optimized out>, tx=<optimized out>, tx@entry=0 '\000')
    at peripherals/samd/dma.c:192
#1  0x0000a0c4 in sercom_dma_write (length=<optimized out>, buffer=<optimized out>, sercom=<optimized out>)
    at peripherals/samd/dma.c:225
#2  common_hal_busio_spi_write.part.1 (self=<optimized out>, data=<optimized out>, len=<optimized out>)
    at common-hal/busio/SPI.c:316
#3  0x000151d4 in displayio_display_send_pixels (length=<optimized out>, pixels=0xa "", 
    self=0x20001458 <displays+80>) at ../../shared-module/displayio/Display.c:325
#4  refresh_area (area=0x2000036c <supervisor_terminal_text_grid+68>, display=0x20001458 <displays+80>)
    at ../../shared-module/displayio/__init__.c:87
#5  displayio_refresh_displays () at ../../shared-module/displayio/__init__.c:138
#6  run_background_tasks.part.0 () at background.c:63
#7  0x0003196c in run_background_tasks () at background.c:53
#8  mp_execute_bytecode (code_state=code_state@entry=0x2002fe60, inject_exc=<optimized out>, inject_exc@entry=0x0)
    at ../../py/vm.c:1295
#9  0x00032224 in fun_bc_call (self_in=0x20028670, n_args=<optimized out>, n_kw=1, args=0x0) at ../../py/objfun.c:284
#10 0x0002e8e4 in parse_compile_execute (source=source@entry=0x3deaf, 
    input_kind=input_kind@entry=MP_PARSE_FILE_INPUT, exec_flags=exec_flags@entry=32, result=result@entry=0x2002ffb8)
    at ../../lib/utils/pyexec.c:114
#11 0x0002ea08 in pyexec_file (result=0x2002ffb8, filename=0x3deaf "code.py") at ../../lib/utils/pyexec.c:529
#12 maybe_run_list (filenames=filenames@entry=0x20000384 <supported_filenames>, 
    exec_result=exec_result@entry=0x2002ffb8) at ../../main.c:175
#13 0x0002f374 in run_code_py (safe_mode=<optimized out>) at ../../main.c:231
#14 main () at ../../main.c:440

EDIT: Inside dma.c, tx_descriptor is &dma_descriptors[SHARED_TX_CHANNEL], which looks like [3]

(gdb) p/x dma_descriptors
$17 = {{BTCTRL = {bit = {VALID = 0x1, EVOSEL = 0x1, BLOCKACT = 0x0, BEATSIZE = 0x1, SRCINC = 0x1, DSTINC = 0x0, 
        STEPSEL = 0x1, STEPSIZE = 0x1}, reg = 0x3503}, BTCNT = {bit = {BTCNT = 0x1fc}, reg = 0x1fc}, SRCADDR = {
      bit = {SRCADDR = 0x200186c0}, reg = 0x200186c0}, DSTADDR = {CRC = {CHKINIT = 0x43002414}, bit = {
        DSTADDR = 0x43002414}, reg = 0x43002414}, DESCADDR = {bit = {DESCADDR = 0x20001650}, reg = 0x20001650}}, {
    BTCTRL = {bit = {VALID = 0x1, EVOSEL = 0x1, BLOCKACT = 0x0, BEATSIZE = 0x1, SRCINC = 0x1, DSTINC = 0x0, 
        STEPSEL = 0x1, STEPSIZE = 0x1}, reg = 0x3503}, BTCNT = {bit = {BTCNT = 0x1fc}, reg = 0x1fc}, SRCADDR = {
      bit = {SRCADDR = 0x200186c2}, reg = 0x200186c2}, DSTADDR = {CRC = {CHKINIT = 0x43002416}, bit = {
        DSTADDR = 0x43002416}, reg = 0x43002416}, DESCADDR = {bit = {DESCADDR = 0x20001660}, reg = 0x20001660}}, {
    BTCTRL = {bit = {VALID = 0x0, EVOSEL = 0x0, BLOCKACT = 0x0, BEATSIZE = 0x0, SRCINC = 0x0, DSTINC = 0x0, 
        STEPSEL = 0x0, STEPSIZE = 0x0}, reg = 0x0}, BTCNT = {bit = {BTCNT = 0x0}, reg = 0x0}, SRCADDR = {bit = {
        SRCADDR = 0x0}, reg = 0x0}, DSTADDR = {CRC = {CHKINIT = 0x0}, bit = {DSTADDR = 0x0}, reg = 0x0}, DESCADDR = {
      bit = {DESCADDR = 0x0}, reg = 0x0}}, {BTCTRL = {bit = {VALID = 0x1, EVOSEL = 0x0, BLOCKACT = 0x0, 
        BEATSIZE = 0x0, SRCINC = 0x1, DSTINC = 0x0, STEPSEL = 0x0, STEPSIZE = 0x0}, reg = 0x401}, BTCNT = {bit = {
        BTCNT = 0x400}, reg = 0x400}, SRCADDR = {bit = {SRCADDR = 0x2002fd40}, reg = 0x2002fd40}, DSTADDR = {CRC = {
        CHKINIT = 0x43000028}, bit = {DSTADDR = 0x43000028}, reg = 0x43000028}, DESCADDR = {bit = {DESCADDR = 0x0}, 
      reg = 0x0}}, {BTCTRL = {bit = {VALID = 0x0, EVOSEL = 0x0, BLOCKACT = 0x0, BEATSIZE = 0x0, SRCINC = 0x0, 
        DSTINC = 0x0, STEPSEL = 0x0, STEPSIZE = 0x0}, reg = 0x0}, BTCNT = {bit = {BTCNT = 0x0}, reg = 0x0}, 
    SRCADDR = {bit = {SRCADDR = 0x0}, reg = 0x0}, DSTADDR = {CRC = {CHKINIT = 0x0}, bit = {DSTADDR = 0x0}, 
      reg = 0x0}, DESCADDR = {bit = {DESCADDR = 0x0}, reg = 0x0}}}
(gdb) p/x dma_descriptors[3]
$18 = {BTCTRL = {bit = {VALID = 0x1, EVOSEL = 0x0, BLOCKACT = 0x0, BEATSIZE = 0x0, SRCINC = 0x1, DSTINC = 0x0, 
      STEPSEL = 0x0, STEPSIZE = 0x0}, reg = 0x401}, BTCNT = {bit = {BTCNT = 0x400}, reg = 0x400}, SRCADDR = {bit = {
      SRCADDR = 0x2002fd40}, reg = 0x2002fd40}, DSTADDR = {CRC = {CHKINIT = 0x43000028}, bit = {
      DSTADDR = 0x43000028}, reg = 0x43000028}, DESCADDR = {bit = {DESCADDR = 0x0}, reg = 0x0}}

@tannewt
Copy link
Member

tannewt commented Jul 24, 2019

Another interesting tidbit would be the sercom registers which should be clocking the dma.

@dhalbert
Copy link
Collaborator

dhalbert commented Jul 24, 2019

(gdb) p peripheral
$19 = (void *) 0x43000000
(gdb) p/x *(Sercom*) peripheral
$21 = {I2CM = {CTRLA = {bit = {SWRST = 0x0, ENABLE = 0x1, MODE = 0x3, RUNSTDBY = 0x0, PINOUT = 0x0, SDAHOLD = 0x0, 
        MEXTTOEN = 0x0, SEXTTOEN = 0x0, SPEED = 0x0, SCLSM = 0x0, INACTOUT = 0x0, LOWTOUTEN = 0x0}, reg = 0x2000e}, 
    CTRLB = {bit = {SMEN = 0x0, QCEN = 0x0, CMD = 0x2, ACKACT = 0x0}, reg = 0x20000}, CTRLC = {bit = {
        DATA32B = 0x0}, reg = 0x0}, BAUD = {bit = {BAUD = 0x0, BAUDLOW = 0x0, HSBAUD = 0x0, HSBAUDLOW = 0x0}, 
      reg = 0x0}, Reserved1 = {0x0, 0x0, 0x0, 0x0}, INTENCLR = {bit = {MB = 0x0, SB = 0x0, ERROR = 0x0}, reg = 0x0}, 
    Reserved2 = {0x0}, INTENSET = {bit = {MB = 0x0, SB = 0x0, ERROR = 0x0}, reg = 0x0}, Reserved3 = {0x0}, 
    INTFLAG = {bit = {MB = 0x1, SB = 0x0, ERROR = 0x0}, reg = 0x1}, Reserved4 = {0x0}, STATUS = {bit = {
        BUSERR = 0x0, ARBLOST = 0x0, RXNACK = 0x0, BUSSTATE = 0x0, LOWTOUT = 0x0, CLKHOLD = 0x0, MEXTTOUT = 0x0, 
        SEXTTOUT = 0x0, LENERR = 0x0}, reg = 0x0}, SYNCBUSY = {bit = {SWRST = 0x0, ENABLE = 0x0, SYSOP = 0x0, 
        LENGTH = 0x0}, reg = 0x0}, Reserved5 = {0x0, 0x0, 0x0, 0x0}, ADDR = {bit = {ADDR = 0x0, LENEN = 0x0, 
        HS = 0x0, TENBITEN = 0x0, LEN = 0x0}, reg = 0x0}, DATA = {bit = {DATA = 0x0}, reg = 0x0}, Reserved6 = {0x0, 
      0x0, 0x0, 0x0}, DBGCTRL = {bit = {DBGSTOP = 0x0}, reg = 0x0}}, I2CS = {CTRLA = {bit = {SWRST = 0x0, 
        ENABLE = 0x1, MODE = 0x3, RUNSTDBY = 0x0, PINOUT = 0x0, SDAHOLD = 0x0, SEXTTOEN = 0x0, SPEED = 0x0, 
        SCLSM = 0x0, LOWTOUTEN = 0x0}, reg = 0x2000e}, CTRLB = {bit = {SMEN = 0x0, GCMD = 0x0, AACKEN = 0x0, 
        AMODE = 0x0, CMD = 0x2, ACKACT = 0x0}, reg = 0x20000}, CTRLC = {bit = {SDASETUP = 0x0, DATA32B = 0x0}, 
      reg = 0x0}, Reserved1 = {0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0}, INTENCLR = {bit = {PREC = 0x0, AMATCH = 0x0, 
        DRDY = 0x0, ERROR = 0x0}, reg = 0x0}, Reserved2 = {0x0}, INTENSET = {bit = {PREC = 0x0, AMATCH = 0x0, 
        DRDY = 0x0, ERROR = 0x0}, reg = 0x0}, Reserved3 = {0x0}, INTFLAG = {bit = {PREC = 0x1, AMATCH = 0x0, 
        DRDY = 0x0, ERROR = 0x0}, reg = 0x1}, Reserved4 = {0x0}, STATUS = {bit = {BUSERR = 0x0, COLL = 0x0, 
        RXNACK = 0x0, DIR = 0x0, SR = 0x0, LOWTOUT = 0x0, CLKHOLD = 0x0, SEXTTOUT = 0x0, HS = 0x0, LENERR = 0x0}, 
      reg = 0x0}, SYNCBUSY = {bit = {SWRST = 0x0, ENABLE = 0x0, LENGTH = 0x0}, reg = 0x0}, Reserved5 = {0x0, 0x0}, 
    LENGTH = {bit = {LEN = 0x0, LENEN = 0x0}, reg = 0x0}, ADDR = {bit = {GENCEN = 0x0, ADDR = 0x0, TENBITEN = 0x0, 
        ADDRMASK = 0x0}, reg = 0x0}, DATA = {bit = {DATA = 0x0}, reg = 0x0}}, SPI = {CTRLA = {bit = {SWRST = 0x0, 
        ENABLE = 0x1, MODE = 0x3, RUNSTDBY = 0x0, IBON = 0x0, DOPO = 0x2, DIPO = 0x0, FORM = 0x0, CPHA = 0x0, 
        CPOL = 0x0, DORD = 0x0}, reg = 0x2000e}, CTRLB = {bit = {CHSIZE = 0x0, PLOADEN = 0x0, SSDE = 0x0, 
        MSSEN = 0x0, AMODE = 0x0, RXEN = 0x1}, reg = 0x20000}, CTRLC = {bit = {ICSPACE = 0x0, DATA32B = 0x0}, 
      reg = 0x0}, BAUD = {bit = {BAUD = 0x0}, reg = 0x0}, Reserved1 = {0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0}, 
    INTENCLR = {bit = {DRE = 0x0, TXC = 0x0, RXC = 0x0, SSL = 0x0, ERROR = 0x0}, reg = 0x0}, Reserved2 = {0x0}, 
    INTENSET = {bit = {DRE = 0x0, TXC = 0x0, RXC = 0x0, SSL = 0x0, ERROR = 0x0}, reg = 0x0}, Reserved3 = {0x0}, 
    INTFLAG = {bit = {DRE = 0x1, TXC = 0x0, RXC = 0x0, SSL = 0x0, ERROR = 0x0}, reg = 0x1}, Reserved4 = {0x0}, 
    STATUS = {bit = {BUFOVF = 0x0, LENERR = 0x0}, reg = 0x0}, SYNCBUSY = {bit = {SWRST = 0x0, ENABLE = 0x0, 
        CTRLB = 0x0, LENGTH = 0x0}, reg = 0x0}, Reserved5 = {0x0, 0x0}, LENGTH = {bit = {LEN = 0x0, LENEN = 0x0}, 
      reg = 0x0}, ADDR = {bit = {ADDR = 0x0, ADDRMASK = 0x0}, reg = 0x0}, DATA = {bit = {DATA = 0x0}, reg = 0x0}, 
    Reserved6 = {0x0, 0x0, 0x0, 0x0}, DBGCTRL = {bit = {DBGSTOP = 0x0}, reg = 0x0}}, USART = {CTRLA = {bit = {
        SWRST = 0x0, ENABLE = 0x1, MODE = 0x3, RUNSTDBY = 0x0, IBON = 0x0, TXINV = 0x0, RXINV = 0x0, SAMPR = 0x0, 
        TXPO = 0x2, RXPO = 0x0, SAMPA = 0x0, FORM = 0x0, CMODE = 0x0, CPOL = 0x0, DORD = 0x0}, reg = 0x2000e}, 
    CTRLB = {bit = {CHSIZE = 0x0, SBMODE = 0x0, COLDEN = 0x0, SFDE = 0x0, ENC = 0x0, PMODE = 0x0, TXEN = 0x0, 
        RXEN = 0x1, LINCMD = 0x0}, reg = 0x20000}, CTRLC = {bit = {GTIME = 0x0, BRKLEN = 0x0, HDRDLY = 0x0, 
        INACK = 0x0, DSNACK = 0x0, MAXITER = 0x0, DATA32B = 0x0}, reg = 0x0}, BAUD = {bit = {BAUD = 0x0}, FRAC = {
        BAUD = 0x0, FP = 0x0}, FRACFP = {BAUD = 0x0, FP = 0x0}, USARTFP = {BAUD = 0x0}, reg = 0x0}, RXPL = {bit = {
        RXPL = 0x0}, reg = 0x0}, Reserved1 = {0x0, 0x0, 0x0, 0x0, 0x0}, INTENCLR = {bit = {DRE = 0x0, TXC = 0x0, 
        RXC = 0x0, RXS = 0x0, CTSIC = 0x0, RXBRK = 0x0, ERROR = 0x0}, reg = 0x0}, Reserved2 = {0x0}, INTENSET = {
      bit = {DRE = 0x0, TXC = 0x0, RXC = 0x0, RXS = 0x0, CTSIC = 0x0, RXBRK = 0x0, ERROR = 0x0}, reg = 0x0}, 
    Reserved3 = {0x0}, INTFLAG = {bit = {DRE = 0x1, TXC = 0x0, RXC = 0x0, RXS = 0x0, CTSIC = 0x0, RXBRK = 0x0, 
        ERROR = 0x0}, reg = 0x1}, Reserved4 = {0x0}, STATUS = {bit = {PERR = 0x0, FERR = 0x0, BUFOVF = 0x0, 
        CTS = 0x0, ISF = 0x0, COLL = 0x0, TXE = 0x0, ITER = 0x0}, reg = 0x0}, SYNCBUSY = {bit = {SWRST = 0x0, 
        ENABLE = 0x0, CTRLB = 0x0, RXERRCNT = 0x0, LENGTH = 0x0}, reg = 0x0}, RXERRCNT = {reg = 0x0}, Reserved5 = {
      0x0}, LENGTH = {bit = {LEN = 0x0, LENEN = 0x0}, reg = 0x0}, Reserved6 = {0x0, 0x0, 0x0, 0x0}, DATA = {bit = {
        DATA = 0x0}, reg = 0x0}, Reserved7 = {0x0, 0x0, 0x0, 0x0}, DBGCTRL = {bit = {DBGSTOP = 0x0}, reg = 0x0}}}

@kevinjwalters
Copy link
Author

The values "optimized out" will be presumably sitting in registers? What's the info register output when it's stuck in shared_dma_transfer?

@dhalbert
Copy link
Collaborator

The values might have been in registers or sometimes on the stack, but at the point in the code where the values are no longer needed, the compiler will reuse those locations, so they are not preserved and would get overwritten for other uses. I'd have to look at the machine code to see if they're accidentally still available. But I can usually go back in the stack and see what was passed down.

@kevinjwalters
Copy link
Author

length should still be around in a register at https://github.com/adafruit/samd-peripherals/blob/83a4759d186574d8034435cd2303def85e4ed793/samd/dma.c#L192 as it's required later as a return value https://github.com/adafruit/samd-peripherals/blob/83a4759d186574d8034435cd2303def85e4ed793/samd/dma.c#L213 ? I was curious about this just because, as you noted, pixels has as a strange value.

@kevinjwalters
Copy link
Author

kevinjwalters commented Jul 24, 2019

length is probably fine, 0x400 appears in BTCNT.reg and sounds plausible.

I'm wondering if #1992 could be related to this in some way? I'd imagine the circuitpython interpreter has little interest in the 16bit values and yet they have a profound effect on triggering that bug. Stranger still, I believe when it happens a "fix" is to append a single extra 16bit value. It's very visual on x-y oscilloscope output as the looping image starts perfect and then distorts as x-y go out of sync. They then periodically come back into sync for a moment and this repeats.

@dhalbert
Copy link
Collaborator

I compiled with -Og to get better debugging, but I then ran for 24 hours with no failure. I changed back to -Os and got an identical backtrace to the one above, sometime during overnight testing (I set it going and went to sleep).

@kevinjwalters
Copy link
Author

kevinjwalters commented Jul 26, 2019

I'm going to watche/listen to the CircuitPython Deep Dive but where is the stack for the CP interpreter? Is there any guarantee that it can't clash with other users of the SRAM or detect if it does? I ask because the data buffer + length ends up in SRCADDR and that's 0x2002fd40 which is fairly close (704 bytes) to end of (M4 192k of) SRAM at 0x2002ffff.

@dhalbert
Copy link
Collaborator

Yes, we have stack checking: https://github.com/adafruit/circuitpython/blob/master/py/stackctrl.c#L55. The VM interpreter does a check at opportune times, and there's a 1kB safety area. Most routines don't allocate large chunks of stuff on the stack.

@kevinjwalters
Copy link
Author

kevinjwalters commented Jul 27, 2019

There's two variable length arrays (presumably on the stack?) at the end of this highlighted piece of code in refresh_area(): https://github.com/adafruit/circuitpython/blob/master/shared-module/displayio/__init__.c#L24-L63

I've not yet figured out exactly what the code is doing but buffer_size has potential to be 128 which means a 4*128 = 512 allocation on stack for buffer[] plus whatever mask[] size is.

Is it worth checking some MP_MAX_STACK_USAGE_SENTINEL_BYTE (0xEE) are still present where expected? Oh, maybe this isn't turned on due to MICROPY_MAX_STACK_USAGE not being true? I think this also means import ustack doesn't work but I see there's documentation for ustack built-in module: https://circuitpython.readthedocs.io/en/4.x/shared-bindings/ustack/__init__.html?highlight=ustack

I see there's a STACK_CANARY_VALUE of 0x017829ef at bottom of stack.

I think I'm also getting confused between the python stack and the C stack, I was more interested in latter.

https://github.com/adafruit/circuitpython/blob/master/ports/atmel-samd/mpconfigport.h#L63-L64 suggests the C stack is at least 24k which sounds substantial for what it's used for and far larger than the 4096 SAMD21 equivalent which seems to work without problem.

BTW, what's the volatile for on https://github.com/adafruit/circuitpython/blob/master/shared-module/displayio/__init__.c#L62 - is this silencing a some sort of bogus compiler warning?

@kevinjwalters
Copy link
Author

kevinjwalters commented Jul 27, 2019

One other observation, displayio_display_begin_transaction() is called three times in https://github.com/adafruit/circuitpython/blob/master/shared-module/displayio/__init__.c#L78 but the one highlighted is the only one that doesn't check return code to verify success.

@dhalbert
Copy link
Collaborator

Thank you for auditing this code!

@tannewt Take a look at these issues:

I've not yet figured out exactly what the code is doing but buffer_size has potential to be 128 which means a 4*128 = 512 allocation on stack for buffer[] plus whatever mask[] size is.

mask[] can actually be quite large. It could be pretty much the size of the whole display. So suppose it's 320*480, that makes mask length 4800 + 1. @tannewt Note how large this can get.

I set a breakpoint in the section of the code and am running your test program. There appears clipping going on, so it never actually hits this code.

Is it worth checking some MP_MAX_STACK_USAGE_SENTINEL_BYTE (0xEE) are still present where expected?

I think we could just call mp_stack_check().

I think I'm also getting confused between the python stack and the C stack, I was more interested in latter.

They are on the same stack, though there are compile options to use a separate stack or to use the heap (STACKLESS).

BTW, what's the volatile for on https://github.com/adafruit/circuitpython/blob/master/shared-module/displayio/__init__.c#L62 - is this silencing a some sort of bogus compiler warning?

volatile just ensures there's a memory location for the variable, and that it's always written to when set. It's necessary for a number of reasons (like writing to peripheral registers), but in this case I think it may be a debugging leftover: it makes it easier to check the value of a variable in the debugger, avoiding <optimized-out>.

One other observation, displayio_display_begin_transaction() is called three times in https://github.com/adafruit/circuitpython/blob/master/shared-module/displayio/__init__.c#L78 but the one highlighted is the only one that doesn't check return code to verify success.

That is a good point, and should be fixed. Again, in this particular case, it appears that code is not reached when your test program is running.

@dhalbert
Copy link
Collaborator

Posting the test code:
code-v0.9.1-githubissue2005.py.zip

@tannewt
Copy link
Member

tannewt commented Jul 29, 2019

So, yes it looks like we should check we have enough stack left.

mask's size should be limited by buffer_size because it holds one bit per pixel of the current area (clipped.)

Yup, the volatile is debugging leftovers.

I'll likely rework the begin_transaction stuff with the e-paper work I'm starting. As it is now, the first check causes a chip select blip that we don't need.

@dhalbert
Copy link
Collaborator

@tannewt did you see any issues with the DMA descriptors? The above issues are interesting, but I don't think that code gets called anyway in this case, where it hangs waiting for the DMA to finish.

@tannewt
Copy link
Member

tannewt commented Jul 29, 2019

I doubt the dma_descriptors are corrupted by the stack because they are on the other side of the heap. However, it's worth looking to see what is placed next to it in memory.

It is weird that DRE is high on the Sercom since it should trigger a DMA burst to fill it. This could happen if the DMA wasn't ready when DRE was triggered I think.

I wonder if it's related to the DMA issues @ladyada and @PaintYourDragon saw with the nintendo emulator and eyeball code.

@ladyada
Copy link
Member

ladyada commented Jul 30, 2019

we think it only happens when you have two running DMA tasks. it was extremely sporadic and hard to repro so i patch-fixed it by detecting the 'DMA lockup' and kicking

https://github.com/adafruit/nofrendo_arcada/blob/master/nofrendo_arcada.ino#L136

@dhalbert
Copy link
Collaborator

@ladyada @tannewt This is in the errata: is this relevant?

image

@ladyada
Copy link
Member

ladyada commented Jul 30, 2019

could be related, but i was seeing 0x3 in chstatus which would be BUSY + PEND
image

@kevinjwalters
Copy link
Author

#1908 has also gone down the path of DMA investigation.

@jepler
Copy link

jepler commented Dec 17, 2019

Also getting this, when I have sound + display.

shared_dma_transfer (peripheral=0x43000000, buffer_out=<optimized out>, 
    dest=<optimized out>, src=src@entry=0x0, buffer_in=<optimized out>, 
    buffer_in@entry=0x0, length=<optimized out>, tx=<optimized out>, 
    tx@entry=0 '\000') at peripherals/samd/dma.c:192
192	        while ((dma_transfer_status(SHARED_TX_CHANNEL) & 0x3) == 0) {}
(gdb) where
#0  shared_dma_transfer (peripheral=0x43000000, buffer_out=<optimized out>, 
    dest=<optimized out>, src=src@entry=0x0, buffer_in=<optimized out>, 
    buffer_in@entry=0x0, length=<optimized out>, tx=<optimized out>, 
    tx@entry=0 '\000') at peripherals/samd/dma.c:192
#1  0x0000cef0 in sercom_dma_write (length=<optimized out>, 
    buffer=<optimized out>, sercom=<optimized out>)
    at peripherals/samd/dma.c:225
#2  common_hal_busio_spi_write.part.1 (self=<optimized out>, 
    data=<optimized out>, len=<optimized out>) at common-hal/busio/SPI.c:316
#3  0x0001c39c in _send_pixels (length=512, pixels=<optimized out>, 
    self=0x20001710 <displays+80>)
    at ../../shared-module/displayio/Display.c:210
#4  _refresh_area (area=0x200038cc, self=0x20001710 <displays+80>)
    at ../../shared-module/displayio/Display.c:287
#5  _refresh_display (self=self@entry=0x20001710 <displays+80>)
    at ../../shared-module/displayio/Display.c:305
#6  0x0001ddac in displayio_display_background (self=0x20001710 <displays+80>)
    at ../../shared-module/displayio/Display.c:368
#7  displayio_background () at ../../shared-module/displayio/__init__.c:46
#8  run_background_tasks () at background.c:84
#9  supervisor_run_background_tasks_if_tick ()
    at ../../supervisor/shared/tick.c:88
#10 0x0003ab3e in mp_execute_bytecode (code_state=code_state@entry=0x2002fa00, 
    inject_exc=<optimized out>, inject_exc@entry=0x0) at ../../py/vm.c:1295
#11 0x0003b400 in fun_bc_call (self_in=0x20029790, n_args=<optimized out>, 
    n_kw=782, args=0x2002fa84) at ../../py/objfun.c:284
#12 0x0002c2a0 in mp_obj_instance_store_attr (value=0xbf9ef4da, attr=975, 
    self_in=0x200031d0) at ../../py/objtype.c:744
#13 mp_obj_instance_attr (self_in=0x200031d0, attr=975, dest=0x2002fac0)
    at ../../py/objtype.c:822
#14 0x00027e8c in mp_store_attr (base=0x200031d0, attr=975, value=0xbf9ef4da)
    at ../../py/runtime.c:1133
#15 0x0003a1dc in mp_execute_bytecode (code_state=code_state@entry=0x20003130, 
    inject_exc=<optimized out>, inject_exc@entry=0x0) at ../../py/vm.c:423
#16 0x0003b400 in fun_bc_call (self_in=0x20002e80, n_args=<optimized out>, 
    n_kw=0, args=0x0) at ../../py/objfun.c:284
#17 0x0002e6c0 in do_execute_raw_code (module_obj=module_obj@entry=0x20029970, 
    raw_code=0x20003120, filename=<optimized out>)
    at ../../py/builtinimport.c:177
#18 0x00037432 in do_load (module_obj=module_obj@entry=0x20029970, 
    file=file@entry=0x2002fcb8) at ../../py/builtinimport.c:236
#19 0x00037762 in mp_builtin___import__ (n_args=n_args@entry=5, 
    args=args@entry=0x2002fdec) at ../../py/builtinimport.c:470
#20 0x00039eb4 in mp_import_name (level=<optimized out>, 
    fromlist=<optimized out>, name=<optimized out>) at ../../py/runtime.c:1390
#21 mp_execute_bytecode (code_state=code_state@entry=0x2002fe70, 
    inject_exc=<optimized out>, inject_exc@entry=0x0) at ../../py/vm.c:1217
#22 0x0003b400 in fun_bc_call (self_in=0x20002e60, n_args=<optimized out>, 
    n_kw=0, args=0x0) at ../../py/objfun.c:284
#23 0x00037a2c in parse_compile_execute (source=source@entry=0x2002ffb8, 
    input_kind=<optimized out>, exec_flags=exec_flags@entry=22, 
    result=result@entry=0x0) at ../../lib/utils/pyexec.c:114
#24 0x000382a2 in pyexec_friendly_repl () at ../../lib/utils/pyexec.c:518
#25 run_repl () at ../../main.c:399
#26 main () at ../../main.c:455
(gdb) info local
beat_size = <optimized out>
sercom = <optimized out>
tx_active = <optimized out>
rx_active = <optimized out>
(gdb) p/x *(Sercom*) peripheral
$2 = {I2CM = {CTRLA = {bit = {SWRST = 0x0, ENABLE = 0x1, MODE = 0x3, 
        RUNSTDBY = 0x0, PINOUT = 0x0, SDAHOLD = 0x0, MEXTTOEN = 0x0, 
        SEXTTOEN = 0x0, SPEED = 0x0, SCLSM = 0x0, INACTOUT = 0x0, 
        LOWTOUTEN = 0x0}, reg = 0x2000e}, CTRLB = {bit = {SMEN = 0x0, 
        QCEN = 0x0, CMD = 0x2, ACKACT = 0x0}, reg = 0x20000}, CTRLC = {bit = {
        DATA32B = 0x0}, reg = 0x0}, BAUD = {bit = {BAUD = 0x0, BAUDLOW = 0x0, 
        HSBAUD = 0x0, HSBAUDLOW = 0x0}, reg = 0x0}, Reserved1 = {0x0, 0x0, 
      0x0, 0x0}, INTENCLR = {bit = {MB = 0x0, SB = 0x0, ERROR = 0x0}, 
      reg = 0x0}, Reserved2 = {0x0}, INTENSET = {bit = {MB = 0x0, SB = 0x0, 
        ERROR = 0x0}, reg = 0x0}, Reserved3 = {0x0}, INTFLAG = {bit = {
        MB = 0x1, SB = 0x0, ERROR = 0x0}, reg = 0x1}, Reserved4 = {0x0}, 
    STATUS = {bit = {BUSERR = 0x0, ARBLOST = 0x0, RXNACK = 0x0, 
        BUSSTATE = 0x0, LOWTOUT = 0x0, CLKHOLD = 0x0, MEXTTOUT = 0x0, 
        SEXTTOUT = 0x0, LENERR = 0x0}, reg = 0x0}, SYNCBUSY = {bit = {
        SWRST = 0x0, ENABLE = 0x0, SYSOP = 0x0, LENGTH = 0x0}, reg = 0x0}, 
    Reserved5 = {0x0, 0x0, 0x0, 0x0}, ADDR = {bit = {ADDR = 0x0, LENEN = 0x0, 
        HS = 0x0, TENBITEN = 0x0, LEN = 0x0}, reg = 0x0}, DATA = {bit = {
        DATA = 0x0}, reg = 0x0}, Reserved6 = {0x0, 0x0, 0x0, 0x0}, DBGCTRL = {
      bit = {DBGSTOP = 0x0}, reg = 0x0}}, I2CS = {CTRLA = {bit = {SWRST = 0x0, 
        ENABLE = 0x1, MODE = 0x3, RUNSTDBY = 0x0, PINOUT = 0x0, SDAHOLD = 0x0, 
        SEXTTOEN = 0x0, SPEED = 0x0, SCLSM = 0x0, LOWTOUTEN = 0x0}, 
      reg = 0x2000e}, CTRLB = {bit = {SMEN = 0x0, GCMD = 0x0, AACKEN = 0x0, 
        AMODE = 0x0, CMD = 0x2, ACKACT = 0x0}, reg = 0x20000}, CTRLC = {bit = {
        SDASETUP = 0x0, DATA32B = 0x0}, reg = 0x0}, Reserved1 = {0x0, 0x0, 
      0x0, 0x0, 0x0, 0x0, 0x0, 0x0}, INTENCLR = {bit = {PREC = 0x0, 
        AMATCH = 0x0, DRDY = 0x0, ERROR = 0x0}, reg = 0x0}, Reserved2 = {0x0}, 
    INTENSET = {bit = {PREC = 0x0, AMATCH = 0x0, DRDY = 0x0, ERROR = 0x0}, 
      reg = 0x0}, Reserved3 = {0x0}, INTFLAG = {bit = {PREC = 0x1, 
        AMATCH = 0x0, DRDY = 0x0, ERROR = 0x0}, reg = 0x1}, Reserved4 = {0x0}, 
    STATUS = {bit = {BUSERR = 0x0, COLL = 0x0, RXNACK = 0x0, DIR = 0x0, 
        SR = 0x0, LOWTOUT = 0x0, CLKHOLD = 0x0, SEXTTOUT = 0x0, HS = 0x0, 
        LENERR = 0x0}, reg = 0x0}, SYNCBUSY = {bit = {SWRST = 0x0, 
        ENABLE = 0x0, LENGTH = 0x0}, reg = 0x0}, Reserved5 = {0x0, 0x0}, 
    LENGTH = {bit = {LEN = 0x0, LENEN = 0x0}, reg = 0x0}, ADDR = {bit = {
        GENCEN = 0x0, ADDR = 0x0, TENBITEN = 0x0, ADDRMASK = 0x0}, reg = 0x0}, 
    DATA = {bit = {DATA = 0x0}, reg = 0x0}}, SPI = {CTRLA = {bit = {
        SWRST = 0x0, ENABLE = 0x1, MODE = 0x3, RUNSTDBY = 0x0, IBON = 0x0, 
        DOPO = 0x2, DIPO = 0x0, FORM = 0x0, CPHA = 0x0, CPOL = 0x0, 
        DORD = 0x0}, reg = 0x2000e}, CTRLB = {bit = {CHSIZE = 0x0, 
        PLOADEN = 0x0, SSDE = 0x0, MSSEN = 0x0, AMODE = 0x0, RXEN = 0x1}, 
      reg = 0x20000}, CTRLC = {bit = {ICSPACE = 0x0, DATA32B = 0x0}, 
      reg = 0x0}, BAUD = {bit = {BAUD = 0x0}, reg = 0x0}, Reserved1 = {0x0, 
      0x0, 0x0, 0x0, 0x0, 0x0, 0x0}, INTENCLR = {bit = {DRE = 0x0, TXC = 0x0, 
        RXC = 0x0, SSL = 0x0, ERROR = 0x0}, reg = 0x0}, Reserved2 = {0x0}, 
    INTENSET = {bit = {DRE = 0x0, TXC = 0x0, RXC = 0x0, SSL = 0x0, 
        ERROR = 0x0}, reg = 0x0}, Reserved3 = {0x0}, INTFLAG = {bit = {
        DRE = 0x1, TXC = 0x0, RXC = 0x0, SSL = 0x0, ERROR = 0x0}, reg = 0x1}, 
    Reserved4 = {0x0}, STATUS = {bit = {BUFOVF = 0x0, LENERR = 0x0}, 
      reg = 0x0}, SYNCBUSY = {bit = {SWRST = 0x0, ENABLE = 0x0, CTRLB = 0x0, 
        LENGTH = 0x0}, reg = 0x0}, Reserved5 = {0x0, 0x0}, LENGTH = {bit = {
        LEN = 0x0, LENEN = 0x0}, reg = 0x0}, ADDR = {bit = {ADDR = 0x0, 
        ADDRMASK = 0x0}, reg = 0x0}, DATA = {bit = {DATA = 0x0}, reg = 0x0}, 
    Reserved6 = {0x0, 0x0, 0x0, 0x0}, DBGCTRL = {bit = {DBGSTOP = 0x0}, 
      reg = 0x0}}, USART = {CTRLA = {bit = {SWRST = 0x0, ENABLE = 0x1, 
        MODE = 0x3, RUNSTDBY = 0x0, IBON = 0x0, TXINV = 0x0, RXINV = 0x0, 
        SAMPR = 0x0, TXPO = 0x2, RXPO = 0x0, SAMPA = 0x0, FORM = 0x0, 
        CMODE = 0x0, CPOL = 0x0, DORD = 0x0}, reg = 0x2000e}, CTRLB = {bit = {
        CHSIZE = 0x0, SBMODE = 0x0, COLDEN = 0x0, SFDE = 0x0, ENC = 0x0, 
        PMODE = 0x0, TXEN = 0x0, RXEN = 0x1, LINCMD = 0x0}, reg = 0x20000}, 
    CTRLC = {bit = {GTIME = 0x0, BRKLEN = 0x0, HDRDLY = 0x0, INACK = 0x0, 
        DSNACK = 0x0, MAXITER = 0x0, DATA32B = 0x0}, reg = 0x0}, BAUD = {
      bit = {BAUD = 0x0}, FRAC = {BAUD = 0x0, FP = 0x0}, FRACFP = {BAUD = 0x0, 
        FP = 0x0}, USARTFP = {BAUD = 0x0}, reg = 0x0}, RXPL = {bit = {
        RXPL = 0x0}, reg = 0x0}, Reserved1 = {0x0, 0x0, 0x0, 0x0, 0x0}, 
    INTENCLR = {bit = {DRE = 0x0, TXC = 0x0, RXC = 0x0, RXS = 0x0, 
        CTSIC = 0x0, RXBRK = 0x0, ERROR = 0x0}, reg = 0x0}, Reserved2 = {0x0}, 
    INTENSET = {bit = {DRE = 0x0, TXC = 0x0, RXC = 0x0, RXS = 0x0, 
        CTSIC = 0x0, RXBRK = 0x0, ERROR = 0x0}, reg = 0x0}, Reserved3 = {0x0}, 
    INTFLAG = {bit = {DRE = 0x1, TXC = 0x0, RXC = 0x0, RXS = 0x0, CTSIC = 0x0, 
        RXBRK = 0x0, ERROR = 0x0}, reg = 0x1}, Reserved4 = {0x0}, STATUS = {
      bit = {PERR = 0x0, FERR = 0x0, BUFOVF = 0x0, CTS = 0x0, ISF = 0x0, 
        COLL = 0x0, TXE = 0x0, ITER = 0x0}, reg = 0x0}, SYNCBUSY = {bit = {
        SWRST = 0x0, ENABLE = 0x0, CTRLB = 0x0, RXERRCNT = 0x0, LENGTH = 0x0}, 
      reg = 0x0}, RXERRCNT = {reg = 0x0}, Reserved5 = {0x0}, LENGTH = {bit = {
        LEN = 0x0, LENEN = 0x0}, reg = 0x0}, Reserved6 = {0x0, 0x0, 0x0, 0x0}, 
    DATA = {bit = {DATA = 0x0}, reg = 0x0}, Reserved7 = {0x0, 0x0, 0x0, 0x0}, 
    DBGCTRL = {bit = {DBGSTOP = 0x0}, reg = 0x0}}}
(gdb) p/x dma_descriptors 
$3 = {{BTCTRL = {bit = {VALID = 0x1, EVOSEL = 0x1, BLOCKACT = 0x0, 
        BEATSIZE = 0x0, SRCINC = 0x1, DSTINC = 0x0, STEPSEL = 0x1, 
        STEPSIZE = 0x0}, reg = 0x1403}, BTCNT = {bit = {BTCNT = 0x40}, 
      reg = 0x40}, SRCADDR = {bit = {SRCADDR = 0x20003640}, reg = 0x20003640}, 
    DSTADDR = {CRC = {CHKINIT = 0x43002415}, bit = {DSTADDR = 0x43002415}, 
      reg = 0x43002415}, DESCADDR = {bit = {DESCADDR = 0x200017b0}, 
      reg = 0x200017b0}}, {BTCTRL = {bit = {VALID = 0x1, EVOSEL = 0x1, 
        BLOCKACT = 0x0, BEATSIZE = 0x0, SRCINC = 0x1, DSTINC = 0x0, 
        STEPSEL = 0x1, STEPSIZE = 0x0}, reg = 0x1403}, BTCNT = {bit = {
        BTCNT = 0x40}, reg = 0x40}, SRCADDR = {bit = {SRCADDR = 0x20003640}, 
      reg = 0x20003640}, DSTADDR = {CRC = {CHKINIT = 0x43002417}, bit = {
        DSTADDR = 0x43002417}, reg = 0x43002417}, DESCADDR = {bit = {
        DESCADDR = 0x200017c0}, reg = 0x200017c0}}, {BTCTRL = {bit = {
        VALID = 0x0, EVOSEL = 0x0, BLOCKACT = 0x0, BEATSIZE = 0x0, 
        SRCINC = 0x0, DSTINC = 0x0, STEPSEL = 0x0, STEPSIZE = 0x0}, 
      reg = 0x0}, BTCNT = {bit = {BTCNT = 0x0}, reg = 0x0}, SRCADDR = {bit = {
        SRCADDR = 0x0}, reg = 0x0}, DSTADDR = {CRC = {CHKINIT = 0x0}, bit = {
        DSTADDR = 0x0}, reg = 0x0}, DESCADDR = {bit = {DESCADDR = 0x0}, 
      reg = 0x0}}, {BTCTRL = {bit = {VALID = 0x1, EVOSEL = 0x0, 
        BLOCKACT = 0x0, BEATSIZE = 0x0, SRCINC = 0x1, DSTINC = 0x0, 
        STEPSEL = 0x0, STEPSIZE = 0x0}, reg = 0x401}, BTCNT = {bit = {
        BTCNT = 0x200}, reg = 0x200}, SRCADDR = {bit = {SRCADDR = 0x2002f8c0}, 
      reg = 0x2002f8c0}, DSTADDR = {CRC = {CHKINIT = 0x43000028}, bit = {
        DSTADDR = 0x43000028}, reg = 0x43000028}, DESCADDR = {bit = {
        DESCADDR = 0x0}, reg = 0x0}}, {BTCTRL = {bit = {VALID = 0x0, 
        EVOSEL = 0x0, BLOCKACT = 0x0, BEATSIZE = 0x0, SRCINC = 0x0, 
        DSTINC = 0x0, STEPSEL = 0x0, STEPSIZE = 0x0}, reg = 0x0}, BTCNT = {
      bit = {BTCNT = 0x0}, reg = 0x0}, SRCADDR = {bit = {SRCADDR = 0x0}, 
      reg = 0x0}, DSTADDR = {CRC = {CHKINIT = 0x0}, bit = {DSTADDR = 0x0}, 
      reg = 0x0}, DESCADDR = {bit = {DESCADDR = 0x0}, reg = 0x0}}}

@jepler
Copy link

jepler commented Dec 17, 2019

(gdb) stepi
dma_transfer_status (channel_number=<optimized out>)
    at peripherals/samd/samd51/dma.c:88
88	    return channel->CHINTFLAG.reg;
(gdb) x/i $pc
=> 0xcdb6 <shared_dma_transfer+198>:	ldrb	r0, [r6, #14]
(gdb) p/x $r6
$10 = 0x4100a070
(gdb) p &((Dmac     *)0x4100A000UL)->Channel[3]
$11 = (DmacChannel *) 0x4100a070
(gdb) p *$11
$12 = {CHCTRLA = {bit = {SWRST = 0, ENABLE = 1, RUNSTDBY = 0, TRIGSRC = 13, 
      TRIGACT = 2, BURSTLEN = 0, THRESHOLD = 0}, reg = 2100482}, CHCTRLB = {
    bit = {CMD = 0 '\000'}, reg = 0 '\000'}, CHPRILVL = {bit = {
      PRILVL = 0 '\000'}, reg = 0 '\000'}, CHEVCTRL = {bit = {
      EVACT = 0 '\000', EVOMODE = 0 '\000', EVIE = 0 '\000', EVOE = 0 '\000'}, 
    reg = 0 '\000'}, Reserved1 = "\000\000\000\000", CHINTENCLR = {bit = {
      TERR = 0 '\000', TCMPL = 0 '\000', SUSP = 0 '\000'}, reg = 0 '\000'}, 
  CHINTENSET = {bit = {TERR = 0 '\000', TCMPL = 0 '\000', SUSP = 0 '\000'}, 
    reg = 0 '\000'}, CHINTFLAG = {bit = {TERR = 0 '\000', TCMPL = 0 '\000', 
      SUSP = 0 '\000'}, reg = 0 '\000'}, CHSTATUS = {bit = {PEND = 1 '\001', 
      BUSY = 0 '\000', FERR = 0 '\000', CRCERR = 0 '\000'}, reg = 1 '\001'}}

@ladyada
Copy link
Member

ladyada commented Dec 17, 2019

this could be related to the DMA bugs that @PaintYourDragon have bumped into

@PaintYourDragon
Copy link

Might be related to a couple things I’ve encountered with the Monster M4sk eyes in Arduino.

If it’s the “multiple active DMA channels, one or more with linked descriptors” issue mentioned in the errata above: I don’t have a workaround for this other than change the design of the code if possible to avoid linked descriptors. Had to do this in the eyes, which un-did a small optimization there but oh well, at least it's solid now.

A different problem I’ve encountered involves a DMA-transfer-complete callback occasionally and randomly not being invoked as it should. Workaround for this was knowing (very roughly) how long the DMA operation should take…and then if the code stalls for significantly longer than this in a particular spot, disabling and re-enabling the DMA channel. This CAN cause frames to drop but it gets back on track pretty quickly.

If the latter sounds, familiar, code for the M4_Eyes project is here:
https://github.com/adafruit/Adafruit_Learning_System_Guides/tree/master/M4_Eyes
Specifically, here’s where the DMA timeout is checked:
https://github.com/adafruit/Adafruit_Learning_System_Guides/blob/master/M4_Eyes/M4_Eyes.ino#L836
dmaStartTime is set immediately after the job is started:
https://github.com/adafruit/Adafruit_Learning_System_Guides/blob/master/M4_Eyes/M4_Eyes.ino#L943
DMA_TIMEOUT value is estimated here, based on knowing the size of a scanline in this particular application:
https://github.com/adafruit/Adafruit_Learning_System_Guides/blob/master/M4_Eyes/M4_Eyes.ino#L97
And the channel restart function is here:
https://github.com/adafruit/Adafruit_Learning_System_Guides/blob/master/M4_Eyes/DMAbuddy.h

@jepler
Copy link

jepler commented May 1, 2020

I believe this was fixed by adafruit/samd-peripherals#29. Please feel free to re-open if it reproduces on current CircuitPython. (The fix was probably first in CircuitPython 5.0.0, btw)

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

No branches or pull requests

6 participants