SPI semaphore trips wdt when light sleep enabled

dalbert
Posts: 7
Joined: Wed Nov 16, 2016 3:45 am

SPI semaphore trips wdt when light sleep enabled

Postby dalbert » Sat Nov 04, 2023 9:44 pm

I have an application that interacts with a radio transceiver using the SPI bus. Typically, when the radio receives a message, it raises a status line which is configured as an interrupt source on the ESP32. The ESP32 ISR then reads data from the radio.

The code makes use of the functions:

Code: Select all

uint8_t IRAM_ATTR xcvr_read(uint8_t reg) {
    uint8_t value;
    SPI.beginTransaction(SPISettings(SPI_RATE, SPI_MSBFIRST, SPI_MODE0));
    digitalWrite(NSS, 0);
    SPI.transfer(reg);
    value = SPI.transfer(0);
    digitalWrite(NSS, 1);
    SPI.endTransaction();
    return value;
}

void IRAM_ATTR xcvr_write(uint8_t reg, uint8_t value) {
    SPI.beginTransaction(SPISettings(SPI_RATE, SPI_MSBFIRST, SPI_MODE0));
    digitalWrite(NSS, 0);
    SPI.transfer(0x80 | reg); // set write bit
    SPI.transfer(value);
    digitalWrite(NSS, 1);
    SPI.endTransaction();
}
These are the only functions that call SPI.beginTransaction() and SPI.endTransaction(). This works well until the main loop enables light sleep (which is configured to wake on interrupt):

Code: Select all

  
  esp_sleep_enable_ext1_wakeup((1ULL<<GPIO_NUM_25) |  // radio packet preamble
                               (1ULL<<GPIO_NUM_26) |  // radio packet received
                               (1ULL<<GPIO_NUM_34),   // power coprocessor
                               ESP_EXT1_WAKEUP_ANY_HIGH);

  // Enter low power sleep mode
  esp_light_sleep_start();
  
  // restore normal operation of GPIOs used for external wakeup
  rtc_gpio_deinit(GPIO_NUM_25);
  rtc_gpio_deinit(GPIO_NUM_26);
  rtc_gpio_deinit(GPIO_NUM_34);
 
When light sleep is enabled, I frequently get an exception:

Code: Select all

Guru Meditation Error: Core  1 panic'ed (Interrupt wdt timeout on CPU1).

Core  1 register dump:
PC      : 0x40092577  PS      : 0x00060835  A0      : 0x80091be7  A1      : 0x3ffbfc0c
A2      : 0x3ffbfde8  A3      : 0xb33fffff  A4      : 0x0000cdcd  A5      : 0x00060823  
A6      : 0x00060823  A7      : 0x0000abab  A8      : 0x0000cdcd  A9      : 0xffffffff  
A10     : 0x00000015  A11     : 0x3ffc50a8  A12     : 0x80008000  A13     : 0x3ffcd0e0
A14     : 0x007bfde8  A15     : 0x003fffff  SAR     : 0x0000001b  EXCCAUSE: 0x00000006  
EXCVADDR: 0x00000000  LBEG    : 0x40085bbd  LEND    : 0x40085bc5  LCOUNT  : 0x00000027
Core  1 was running in ISR context:
EPC1    : 0x400f35a7  EPC2    : 0x00000000  EPC3    : 0x00000000  EPC4    : 0x00000000
...

  #0  0x400923df:0x3ffbb5e0 in vListInsert at /Users/ficeto/Desktop/ESP32/ESP32S2/esp-idf-public/components/freertos/list.c:183
  #1  0x4009105b:0x3ffbb600 in vTaskPlaceOnEventList at /Users/ficeto/Desktop/ESP32/ESP32S2/esp-idf-public/components/freertos/tasks.c:3566
  #2  0x400903e6:0x3ffbb620 in xQueueSemaphoreTake at /Users/ficeto/Desktop/ESP32/ESP32S2/esp-idf-public/components/freertos/queue.c:1679
  #3  0x400de7e4:0x3ffbb660 in SPIClass::beginTransaction(SPISettings) at C:/Users/david/.platformio/packages/framework-arduinoespressif32/libraries/SPI/src/SPI.cpp:179
 
The watchdog is in SPI.cpp:179 which is:

Code: Select all

SPI_PARAM_LOCK();

which is:

#define SPI_PARAM_LOCK()    do {} while (xSemaphoreTake(paramLock, portMAX_DELAY) != pdPASS)
When light sleep is disabled, this never happens.
It's not clear to me how the deadlock could be taking place, but I'm obviously doing something wrong and any suggestions would be appreciated!

dalbert
Posts: 7
Joined: Wed Nov 16, 2016 3:45 am

Re: SPI semaphore trips wdt when light sleep enabled

Postby dalbert » Tue Nov 07, 2023 5:39 pm

For anyone else who encounters this, it turned out to be pilot error: accessing the SPI bus from both interrupt and user contexts.

The light sleep being enabled was a red herring, it simply affected timing. I use the SPI bus to manage a radio transceiver extensively in interrupt context and occasionally in user context. Any call to SPI.beginTransaction() locks the SPI bus until SPI.endTransaction(), so if a user context SPI transaction is interrupted by an ISR that also tries to use the SPI bus, deadlock ensues and the WDT trips.

The solution in my case was to more carefully manage those accesses to ensure no user context SPI access could take place while an interrupt that could trigger interrupt context SPI access was possible (either by disabling those interrupts during user-context access or ensuring that the user context access couldn't happen when an interrupt was possible e.g. when the transceiver was not in receive mode). It may be possible to disable interrupts entirely around the user-context SPI transactions, but I suspect the SPI code needs interrupts enabled - I did not check this.

Who is online

Users browsing this forum: Majestic-12 [Bot] and 143 guests