NCS nRF91: using PRS after mcuboot used SPI

Hi,

we are using an nRF9160 on a custom board with NCS 1.8.0. I have been able to reproduce the following behaviour with a nRF9160 DK 1.0 and NCS 1.9.1

For our application, we need mcuboot to have its secondary on an external SPI flash. We use SPI3 for that and of course configure it for mcuboot in its device tree.

In our main application, we need 3 UARTs (we use UART0,1,2) and on the "3" block, we need to use SPI and I2C "simultaneously" using PRS with the nrfx drivers. Of course that SPI3 and I2C3 are not running at the same time, but they get initialized, used and deinitialized consecutively. just one time it's a spim3 and one time it's a twim3.

But some strange problems start to occur, when using the function block 3 slightly different. 

If you init the spim/twim as "blocking", i.e. without a callback function for events, and initialize, transfer and deinitialize consecutively, everything seems to work fine. 

But when I want to use a callback function, I get a fault at runtime. 

void main(void)
{
  printk("Hello World ! %s\n", CONFIG_BOARD);

  eErr = nrfx_spim_init(&sSpimInstance, &sDevCfgSpim, NULL, NULL);
  eErr = nrfx_spim_xfer(&sSpimInstance, &sSpiRxTxData, 0); /* xfer necessary to get crash */
  nrfx_spim_uninit(&sSpimInstance);

  eErr = nrfx_twim_init(&sTwimInstance, &sDevCfg, Main_EvtHandler, &sTwimInstance); /* crash */
  nrfx_twim_enable(&sTwimInstance);  
  nrfx_twim_uninit(&sTwimInstance);
}

I always seem to crash at this function it looks like: __STATIC_INLINE void __NVIC_EnableIRQ(IRQn_Type IRQn),that wants to enable the interrupt for that function block for the callback.

I only seem to get these crashes when I use SPI3 also for the mcuboot flash access. For the MCUBOOT I obviously have the SPI3 "activated" in the device tree, while in my main application, i have it disabled in the device tree. My guess is, that MCUBOOT marks something somewhere, that the regular application cannot unmark or something, when just using nrfx drivers and not devicetree-zephyr-stuff.

When the Function block is used to do one thing, the other seems not to work anymore. Likewise I get a crash when using certain functions "between" the xfer and the deinit like so:

void main(void)
{
  printk("Hello World ! %s\n", CONFIG_BOARD);

  eErr = nrfx_spim_init(&sSpimInstance, &sDevCfgSpim, Main_TestEvtHandlerSpi, NULL);
  eErr = nrfx_spim_xfer(&sSpimInstance, &sSpiRxTxData, 0);
  k_sem_take(&sMain_Sem, K_MSEC(500)); /* crash */
  nrfx_spim_uninit(&sSpimInstance);
}

This does not seem to be related to the SPI3 being used by MCUBOOT, but it's frustrating nonetheless.

I have attached a project for the DK that reproduces the fault. 

It would be interesting to see, if there's a way to make this work, since ti is crucial to our project.


1033.hello_world_twim.zip

Parents
  • Hi Matthias,

    I2C3 and SPI3 share the same base memory address as you can see in the nRF9160 memory instantiation. Have you tried using different peripheral instances for I2C and SPI?

    Best regards,
    Dejan

  • Hi Dejan,

    I know. As I said, we use the other function blocks for uart0, uart1 and uart2. The last function block then is used via PRS
    developer.nordicsemi.com/.../README.html

  • This reply was deleted.
  • Try the following main.c:

    /*
     * Copyright (c) 2012-2014 Wind River Systems, Inc.
     *
     * SPDX-License-Identifier: Apache-2.0
     */
    
    #include <zephyr.h>
    #include <sys/printk.h>
    #include <string.h>
    #include <nrfx_twim.h>
    #include <nrfx_spim.h>
    #include <drivers/src/prs/nrfx_prs.h>
    
    nrfx_twim_t sTwimInstance = NRFX_TWIM_INSTANCE(3);
    nrfx_spim_t sSpimInstance = NRFX_SPIM_INSTANCE(3);
    volatile nrfx_err_t eErr;
    
    nrfx_twim_config_t sDevCfg = 
    {
      .scl = 6,                
      .sda = 5,                
      .frequency = NRF_TWIM_FREQ_400K,          
      .interrupt_priority = 7, 
      .hold_bus_uninit = false 
    };
    
    nrfx_spim_config_t sDevCfgSpim = 
    {
      .sck_pin        = 7,
      .mosi_pin       = 8,
      .miso_pin       = 9,
      .ss_pin         = NRFX_SPIM_PIN_NOT_USED,
      .ss_active_high = false,
      .irq_priority   = 7,
      .orc            = 0xFF,
      .frequency      = NRF_SPIM_FREQ_8M,
      .mode           = NRF_SPIM_MODE_0,
      .bit_order      = NRF_SPIM_BIT_ORDER_MSB_FIRST,
      .miso_pull      = NRF_GPIO_PIN_PULLUP,
    };
    
    uint8_t au8Data[16];
    
    nrfx_spim_xfer_desc_t sSpiRxTxData = 
    {
      .p_tx_buffer = au8Data,
      .tx_length   = 4,
      .p_rx_buffer = au8Data,
      .rx_length   = 4
    };
    
    nrfx_twim_evt_t sTestEvent;
    nrfx_spim_evt_t sTestEventSpi;
    
    K_SEM_DEFINE(sMain_Sem, 0, 1);
    
    static void Main_EvtHandler(const nrfx_twim_evt_t* psEvent, void* pContext)
    {
      memcpy(&sTestEvent, psEvent, sizeof(nrfx_twim_evt_t));
      k_sem_give(&sMain_Sem);
    }
    
    static void Main_TestEvtHandlerSpi(const nrfx_spim_evt_t* psEvent, void* pContext)
    {
      memcpy(&sTestEventSpi, psEvent, sizeof(nrfx_spim_evt_t));
      k_sem_give(&sMain_Sem);
    }
    
    static void spim_handler(const nrfx_spim_evt_t *p_event, void *p_context)
    {
    	printk("spim_handler running\n");
    }
    
    
    void main(void)
    {
    	printk("hello world twim ! %s\n", CONFIG_BOARD);
    
    #if 1 /* error case 1 */
      IRQ_CONNECT(DT_IRQN(DT_NODELABEL(i2c3)),
    		    DT_IRQ(DT_NODELABEL(i2c3), priority),
    		    nrfx_isr, nrfx_prs_box_3_irq_handler, 0);
    
      eErr = nrfx_spim_init(&sSpimInstance, &sDevCfgSpim, spim_handler, NULL);
      eErr = nrfx_spim_xfer(&sSpimInstance, &sSpiRxTxData, 0); /* xfer necessary to get crash */
      nrfx_spim_uninit(&sSpimInstance);
    
      eErr = nrfx_twim_init(&sTwimInstance, &sDevCfg, Main_EvtHandler, &sTwimInstance); /* crash */
      nrfx_twim_enable(&sTwimInstance);  
      nrfx_twim_uninit(&sTwimInstance);
      printk("Goes here");
    #else /* error case 2 */
    
      eErr = nrfx_spim_init(&sSpimInstance, &sDevCfgSpim, Main_TestEvtHandlerSpi, NULL);
      eErr = nrfx_spim_xfer(&sSpimInstance, &sSpiRxTxData, 0);
      k_sem_take(&sMain_Sem, K_MSEC(500)); /* crash */
      nrfx_spim_uninit(&sSpimInstance);
    
    #endif
    }
    

    With that I was able to go past nrfx_twim_init() without the program crashing.

  • Hi,
    at first sight this seems like it's working at first glance, but I have noticed some strange behaviour still.

    I want to use the SPIM in a blocking way. When I do, then I get what seems infinite twim event handler after a twim xfer.

    I have my new main.c here:

    #include <zephyr.h>
    #include <sys/printk.h>
    #include <string.h>
    #include <nrfx_twim.h>
    #include <nrfx_spim.h>
    #include <drivers/src/prs/nrfx_prs.h>
    
    nrfx_twim_t sTwimInstance = NRFX_TWIM_INSTANCE(3);
    nrfx_spim_t sSpimInstance = NRFX_SPIM_INSTANCE(3);
    volatile nrfx_err_t eErr;
    
    nrfx_twim_config_t sDevCfg = 
    {
      .scl = 6,                
      .sda = 5,                
      .frequency = NRF_TWIM_FREQ_400K,          
      .interrupt_priority = 7, 
      .hold_bus_uninit = false 
    };
    
    nrfx_spim_config_t sDevCfgSpim = 
    {
      .sck_pin        = 7,
      .mosi_pin       = 8,
      .miso_pin       = 9,
      .ss_pin         = NRFX_SPIM_PIN_NOT_USED,
      .ss_active_high = false,
      .irq_priority   = 7,
      .orc            = 0xFF,
      .frequency      = NRF_SPIM_FREQ_8M,
      .mode           = NRF_SPIM_MODE_0,
      .bit_order      = NRF_SPIM_BIT_ORDER_MSB_FIRST,
      .miso_pull      = NRF_GPIO_PIN_PULLUP,
    };
    
    uint8_t au8Data[16];
    
    nrfx_spim_xfer_desc_t sSpiRxTxData = 
    {
      .p_tx_buffer = au8Data,
      .tx_length   = 4,
      .p_rx_buffer = au8Data,
      .rx_length   = 4
    };
    
    nrfx_twim_xfer_desc_t sTwiRxTxData = 
    {
    
        .type = NRFX_TWIM_XFER_RX, ///< Type of transfer.
        .address = 123,            ///< Slave address.
        .primary_length = 1,       ///< Number of bytes transferred.
        .secondary_length = 0, ///< Number of bytes transferred.
        .p_primary_buf = au8Data,    ///< Pointer to transferred data.
        .p_secondary_buf = 0,  ///< Pointer to transferred data.
    };
    
    nrfx_twim_evt_t sTestEvent;
    nrfx_spim_evt_t sTestEventSpi;
    
    K_SEM_DEFINE(sMain_Sem, 0, 1);
    
    static void Main_EvtHandler(const nrfx_twim_evt_t* psEvent, void* pContext)
    {
      /* Handler gets called a LOT! (infinitely?) */
      memcpy(&sTestEvent, psEvent, sizeof(nrfx_twim_evt_t));
      k_sem_give(&sMain_Sem);
    }
    
    static void Main_TestEvtHandlerSpi(const nrfx_spim_evt_t* psEvent, void* pContext)
    {
      memcpy(&sTestEventSpi, psEvent, sizeof(nrfx_spim_evt_t));
      k_sem_give(&sMain_Sem);
    }
    
    void main(void)
    {
      volatile static uint8_t u8Test123 = 0;
    	printk("Hello World ! %s\n", CONFIG_BOARD);
    
      memset(&sTestEvent, 0xA5, sizeof(sTestEvent));
      memset(&sTestEventSpi, 0xA5, sizeof(sTestEventSpi));
    
    #if 1 /* error case 1 */
      IRQ_CONNECT(DT_IRQN(DT_NODELABEL(i2c3)),
    		    DT_IRQ(DT_NODELABEL(i2c3), priority),
    		    nrfx_isr, nrfx_prs_box_3_irq_handler, 0);
    
      eErr = nrfx_spim_init(&sSpimInstance, &sDevCfgSpim, NULL, NULL);
      eErr = nrfx_spim_xfer(&sSpimInstance, &sSpiRxTxData, 0); /* xfer necessary to get crash */
      nrfx_spim_uninit(&sSpimInstance);
    
      k_sleep(K_MSEC(1000));
    
      eErr = nrfx_twim_init(&sTwimInstance, &sDevCfg, Main_EvtHandler, &sTwimInstance); /* crash */
      nrfx_twim_enable(&sTwimInstance);  
      nrfx_twim_xfer(&sTwimInstance, &sTwiRxTxData, 0);
      k_sem_take(&sMain_Sem, K_MSEC(10000));
      nrfx_twim_uninit(&sTwimInstance);
    
    #else /* error case 2 */
    
      eErr = nrfx_spim_init(&sSpimInstance, &sDevCfgSpim, Main_TestEvtHandlerSpi, NULL);
      eErr = nrfx_spim_xfer(&sSpimInstance, &sSpiRxTxData, 0);
      k_sem_take(&sMain_Sem, K_MSEC(500)); /* crash */
      nrfx_spim_uninit(&sSpimInstance);
    
    #endif
    
      /* dummy var for setting breakpoints */
      u8Test123++;
      u8Test123++;
      u8Test123++;
      u8Test123++;
      k_sleep(K_FOREVER);
    }

    If spim is used in a non-blocking way, as you have it in your latest snippet, then the Spi handler never gets called (which might make sense, given the IRQ_CONNECT() just connects the i2c, but still).
    I need a way to wait for the spi transfer to be finished as well. Either with it being blocking or with it calling a handler which gives a semaphore (like I am doing with twim in my sample here), but for that I need either one to work.

    I tried something like this, but then I get a build error, since an irq can apparently only be connected one time.

      IRQ_CONNECT(DT_IRQN(DT_NODELABEL(spi3)),
    		    DT_IRQ(DT_NODELABEL(spi3), priority),
    		    nrfx_isr, nrfx_prs_box_3_irq_handler, 0);
    
      eErr = nrfx_spim_init(&sSpimInstance, &sDevCfgSpim, Main_TestEvtHandlerSpi, NULL);
      eErr = nrfx_spim_xfer(&sSpimInstance, &sSpiRxTxData, 0); /* xfer necessary to get crash */
      nrfx_spim_uninit(&sSpimInstance);
    
      k_sleep(K_MSEC(1000));
      
      IRQ_CONNECT(DT_IRQN(DT_NODELABEL(i2c3)),
    		    DT_IRQ(DT_NODELABEL(i2c3), priority),
    		    nrfx_isr, nrfx_prs_box_3_irq_handler, 0);
    
      eErr = nrfx_twim_init(&sTwimInstance, &sDevCfg, Main_EvtHandler, &sTwimInstance); /* crash */
      nrfx_twim_enable(&sTwimInstance);  
      nrfx_twim_xfer(&sTwimInstance, &sTwiRxTxData, 0);
      k_sem_take(&sMain_Sem, K_MSEC(10000));
      nrfx_twim_uninit(&sTwimInstance);

  • Hello,

    Sorry for the delay on this. Have you made any progress lately?

    I will be gone until Monday, but will look into your issue then

    Best regards,

    Simon

  • Hi,

    No worries. Since I also have a lot of other work to do, I also put this on the backburner, since I am not as well acquainted with the inner workings of the SDK/the nRF91 as you and would certainly waste more time than anything.

Reply Children
No Data
Related