[nrf52840][zephyr] SPI Access in GPIOTE interrupt causes a kernel panic

Hello,

Although I already used the nrf52840 on previous projects (nrfxlib), I'm a newbie using nRF Connect and ZephyrOS (I previously used RTOS).

I'm integrating a driver which uses a GPIO interrupt to trigger read of SPI bus. The driver isn't RTOS ready (no use of semaphores for wait loop).

I got an issue when the driver try to do a SPI access when in the interrupt context of gpio and I don't understand why.

I'm using a zigbee based example app, the driver initialization is done in the main thread (before initialization of the Zboss stack).

I configured the GPIO as (the interrupt is enabled later but calling the same API) :

    err = gpio_pin_configure(gpio1_dev, INT1_ICU_INT1_0_PIN, GPIO_INPUT|GPIO_PULL_UP);
    err |= gpio_pin_interrupt_configure(gpio1_dev, INT1_ICU_INT1_0_PIN, GPIO_INT_DISABLE);
    gpio_init_callback(&icu_int_callback_st, icu_int_callback_handler, BIT(INT1_ICU_INT1_0_PIN));
    err |= gpio_add_callback(gpio1_dev, &icu_int_callback_st);

The SPI is initialized as :

    spi_dev = DEVICE_DT_GET(SPI_ICU_SENSORS);
    if (!device_is_ready(spi_dev)) {
        LOG_ERR("SPI ICU device not ready!");
        err = 1;
    }

The interrupt handler just call the driver API to handle the interrupt API (which basically check sensor status and raise a flag to notify main loop)

static void icu_int_callback_handler(const struct device *port, struct gpio_callback *cb, gpio_port_pins_t pins)

On the driver side the code which fails is the following:

* In the main loop we just wait for the flag to raise

while (interrupt_sensors == 0) {
...
}

* In the GPIO interrupt callback we start a register read of sensor, which finally call spi_transceive :

const struct spi_buf tx_buf = {
        .buf = (void *) data,
        .len = num_bytes
    };
    const struct spi_buf_set tx = {
        .buffers = &tx_buf,
        .count = 1
    };

    if (spi_transceive(spi_dev, &spi_cfg, &tx, NULL)) {
        return 1;
    }

Which cause the error :

ASSERTION FAIL @ WEST_TOPDIR/zephyr/kernel/sem.c:136
E: r0/a1:  0x00000004  r1/a2:  0x00000088  r2/a3:  0x00000000
E: r3/a4:  0x20009755 r12/ip:  0x00000000 r14/lr:  0x00026ad3
E:  xpsr:  0x61000016
E: s[ 0]:  0x20005dcc  s[ 1]:  0x00025a23  s[ 2]:  0x20000448  s[ 3]:  0x2000004c
E: s[ 4]:  0x2000004c  s[ 5]:  0x00000000  s[ 6]:  0x20009820  s[ 7]:  0x000577e7
E: s[ 8]:  0x00000000  s[ 9]:  0x0005d7f4  s[10]:  0x20000448  s[11]:  0x0000c9f9
E: s[12]:  0x2000004c  s[13]:  0x00061e08  s[14]:  0x00000023  s[15]:  0x00000000
E: fpscr:  0x20009866
E: Faulting instruction address (r15/pc): 0x000554ec
E: >>> ZEPHYR FATAL ERROR 4: Kernel panic on CPU 0
E: Fault during interrupt handling

E: Current thread: 0x200023f0 (main)

When I debug the issue I can see that the issue is an assert fail when trying to lock SPI connect in spi_transceive :

1. Do I use the correct API for GPIO and SPI ? I saw that I could use nrfx API

2. What could cause this context lock whereas there is no SPI bus user ? Is there a limitation between GPIOTE interrupt context and SPI access ?

If I move the SPI access to main loop, I fix the issue:

* I add a semaphore  in the GPIO interrupt:

k_sem_give(&int_sem);

* I move the processing done in GPIO interrupt (which does the SPI access) into a dedicated function : chdrv_int_process()

* In the main loop I add a wait of semaphore, then call the process function :

    k_sem_take(&int_sem, K_FOREVER);
    chdrv_int_process(dev_ptr);

It  works but it changes the driver's code which I want to avoid. I don't get why I have the issue (although I'm aware doing processing in the GPIO interrupt isn't the best practice).

Best regards

Gaël

Parents
  • Hi,

    Although I already used the nrf52840 on previous projects (nrfxlib), I'm a newbie using nRF Connect and ZephyrOS (I previously used RTOS).

    I recommend the Nordic Developer Academy as an excellent place to start learning the nRF Connect SDK.

    For the issue:
    Are you working with interrupts from a ISR?
    Instead I recommend using a Workqueue thread? As its docs say:
    ". A workqueue is typically used by an ISR or a high-priority thread to offload non-urgent processing to a lower-priority thread so it does not impact time-sensitive processing."

    Regards,
    Sigurd Hellesvik

  • Hi Sigurd,

    Are you working with interrupts from a ISR?

    This is one of my point. The Zephyr API hides many thing so it's not easy to understand which peripheral and features are really used.

    I defined my spi device as compatible = "nordic,nrf-spim"; in the overlay, so it uses easyDMA, so I suppose it uses interrupts.

    Instead I recommend using a Workqueue thread?

    What do you mean ? You mean use the workqueue thread instead of semaphore ?

    I would like as much as possible to don't change the driver code. So I'd like to understand why I have the lock issue instead of changing the driver's code

    PS : I started to read "nRF Connect SDK Intermediate"

Reply
  • Hi Sigurd,

    Are you working with interrupts from a ISR?

    This is one of my point. The Zephyr API hides many thing so it's not easy to understand which peripheral and features are really used.

    I defined my spi device as compatible = "nordic,nrf-spim"; in the overlay, so it uses easyDMA, so I suppose it uses interrupts.

    Instead I recommend using a Workqueue thread?

    What do you mean ? You mean use the workqueue thread instead of semaphore ?

    I would like as much as possible to don't change the driver code. So I'd like to understand why I have the lock issue instead of changing the driver's code

    PS : I started to read "nRF Connect SDK Intermediate"

Children
No Data
Related