adafruit / circuitpython

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

PDMIn.record() infinite loop on SAMD21 #5458

Closed dhalbert closed 2 years ago

dhalbert commented 3 years ago

CircuitPython version

Adafruit CircuitPython 7.0.0 on 2021-09-20; Adafruit CircuitPlayground Express with samd21g18

Code/REPL

# This is a pruning of https://learn.adafruit.com/perk-up-ears/code 
# test.py

import array
import board
import audiobusio

NUM_SAMPLES = 90

mic = audiobusio.PDMIn(board.MICROPHONE_CLOCK, board.MICROPHONE_DATA,
                       sample_rate=16000, bit_depth=16)

samples = array.array('H', [0] * NUM_SAMPLES)
mic.record(samples, len(samples))

Behavior

Adafruit CircuitPython 7.0.0 on 2021-09-20; Adafruit CircuitPlayground Express with samd21g18
>>> import test
[infinite loop - CIRCUITPY disappears]

Description

Also fails as code.py, but easier to control as test.py. Does not necessarily fail if this is not the first thing (e.g. does not crash after a ctrl-D when a previous version had the mic.record() commented out).

EDIT: determined this is an infinite loop, not a crash per se.

Additional information

Not tested on any other boards yet. Found by @ Meg in discord, 10:30pm and following (ET) 2021-10-11. Does not fail in 6.3.0.

dhalbert commented 3 years ago

I did a bisect on this, and it turns out to be

When PDMIn.record() happens, it now loops forever inside common_hal_audiobusio_pdmin_record_to_buffer().

@tannewt @DavePutz common_hal_audiobusio_pdmin_record_to_buffer() uses the event system for handling incoming DMA I2S data. I do not know yet why #5100 broke this, but one or the other is making some assumption about the event system setup that is not shared by the other. If you have an idea, I'd be very interested.

dhalbert commented 3 years ago

I am on the track of something. Commenting out the !event_interrupt_active(event_channel) check loop below prevents the infinite loop. That test is probably being misled by the new use of the event system.

        // Wait for the next buffer to fill
        //uint32_t wait_counts = 0;
        #ifdef SAMD21
          #define MAX_WAIT_COUNTS 1000
        #endif
        #ifdef SAM_D5X_E5X
          #define MAX_WAIT_COUNTS 6000
        #endif
        // If wait_counts exceeds the max count, buffer has probably stopped filling;
        // DMA may have missed an I2S trigger event.
        // while (!event_interrupt_active(event_channel) && ++wait_counts < MAX_WAIT_COUNTS) {
        //     RUN_BACKGROUND_TASKS;
        // }