ESB + BLE integration error

Hi All, 
I encountered an issue while trying to use both Bluetooth and ESB in the same application.
To clarify upfront: I am not using these protocols in parallel. Also, I’m working with NCS version 3.0.1.
In my application, I primarily use ESB as the main protocol, and only occasionally switch to BLE—mainly for FOTA over BLE.

I found an example on DevZone demonstrating how to integrate both protocols. However, after adapting it to my application, I continuously receive assertion faults from the nrfx_timer module.

Eventually, I discovered that the part of the code responsible for disabling the ESB module doesn't work as I expected.
The function used to disable ESB is:

void esb_disable(void)
{
	esb_ppi_disable_all();
	esb_fem_reset();

	sys_timer_deinit();
	esb_ppi_deinit();

	/* Radio ramp-up time to default mode */
	nrf_radio_fast_ramp_up_enable_set(NRF_RADIO, false);

	esb_state = ESB_STATE_IDLE;
	errata216_off();
	esb_initialized = false;

	reset_fifos();

	memset(rx_pipe_info, 0, sizeof(rx_pipe_info));
	memset(pids, 0, sizeof(pids));

	esb_irq_disable();
}

I believe the esb_irq_disable() function should be called first within this function. Additionally, it seems reasonable to add a folowwing line:
on_radio_disabled = NULL;

If the radio interrupt occurs, while esb_disable is being processed, the timer might be already deinitialized, but the esb module might still attempt to reconfigure it, which leads to an assertion fail. This is exactly what happens in my application.
void esb_disable(void)
{
    on_radio_disabled = NULL;
    esb_irq_disable();
	esb_ppi_disable_all();
	esb_fem_reset();

	sys_timer_deinit();
	esb_ppi_deinit();

	/* Radio ramp-up time to default mode */
	nrf_radio_fast_ramp_up_enable_set(NRF_RADIO, false);

	esb_state = ESB_STATE_IDLE;
	errata216_off();
	esb_initialized = false;

	reset_fifos();

	memset(rx_pipe_info, 0, sizeof(rx_pipe_info));
	memset(pids, 0, sizeof(pids));
}

What do you think? Are there any other reasons why this function has irq disable at the end?

Parents
  • Hi,

     

    I found an example on DevZone demonstrating how to integrate both protocols. However, after adapting it to my application, I continuously receive assertion faults from the nrfx_timer module.

    Can you share the log here?

     

    Which device are you testing on?

     

    Kind regards,

    Håkon

  • I'm testing on the nRF52840, and here is a fragment of the log file:

    : HW Variant: nRF52x (0x0002)
    I: Firmware: Standard Bluetooth controller (0x00) Version 137.20634 Build 2617349514
    I: Identity: F3:DD:ED:AF:F3:59 (random)
    I: HCI: version 6.0 (0x0e) revision 0x10f3, manufacturer 0x0059
    I: LMP: version 6.0 (0x0e) subver 0x10f3
    I: Bluetooth initialized
    
    Timer inst 2, state 0, chan 0
    ASSERTION FAIL @ WEST_TOPDIR/modules/hal/nordic/nrfx/drivers/src/nrfx_timer.c:316
    E: r0/a1:  0x00000004  r1/a2:  0x0000013c  r2/a3:  0x2000fff0
    E: r3/a4:  0x00000004 r12/ip:  0x00000000 r14/lr:  0x0003db89
    E:  xpsr:  0x21000011
    E: Faulting instruction address (r15/pc): 0x00046de0
    E: >>> ZEPHYR FATAL ERROR 4: Kernel panic on CPU 0
    E: Fault during interrupt handling

  • Can you confirm which SDK version you're using?

    That assert line does not match against NCS v3.0.2, unless you have done changes to the nrfx_timer.c file. If so, please share those.

     

    Kind regards,

    Håkon

  • Oh, my mistake — I forgot to mention that I modified the nrfx_timer code to add logs that help me debug this issue.
    I'm using NCS v3.0.1.
    The assertion that throws the error is on line 15 of the following function:

    void nrfx_timer_compare(nrfx_timer_t const *   p_instance,
                            nrf_timer_cc_channel_t cc_channel,
                            uint32_t               cc_value,
                            bool                   enable_int)
    {
    
        if (m_cb[p_instance->instance_id].state == NRFX_DRV_STATE_UNINITIALIZED)
        {
            printk("Timer inst %d, state %d, chan %d\n",
                p_instance->instance_id,
                m_cb[p_instance->instance_id].state,
                cc_channel);
        }
    
        NRFX_ASSERT(m_cb[p_instance->instance_id].state != NRFX_DRV_STATE_UNINITIALIZED);
    
        nrf_timer_int_mask_t timer_int = nrfy_timer_compare_int_get(cc_channel);
    
        if (enable_int)
        {
            nrfy_timer_event_clear(p_instance->p_reg, nrfy_timer_compare_event_get(cc_channel));
            nrfy_timer_int_enable(p_instance->p_reg, timer_int);
        }
        else
        {
            nrfy_timer_int_disable(p_instance->p_reg, timer_int);
        }
    
        nrfy_timer_cc_set(p_instance->p_reg, cc_channel, cc_value);
        NRFX_LOG_INFO("Timer id: %d, capture value set: %lu, channel: %d.",
                      p_instance->instance_id,
                      (unsigned long)cc_value,
                      cc_channel);
    }

    As you can see, the error is thrown by Timer 2, which is used by the ESB module. This timer has a state of NRFX_DRV_STATE_UNINITIALIZED, which is not allowed and triggers the assertion.

    This state suggests that the timer was disabled by sys_timer_deinit, but before esb_irq_disable was called. The ESB interrupt handler then tries to use the timer, leading to the failure.
    Debugging this issue confirmed my suspicion.
    And below is diff of changes I made to nrfx_timer:
    diff --git a/nrfx/drivers/src/nrfx_timer.c b/nrfx/drivers/src/nrfx_timer.c
    index 4e07f4b..d58afc8 100644
    --- a/nrfx/drivers/src/nrfx_timer.c
    +++ b/nrfx/drivers/src/nrfx_timer.c
    @@ -67,6 +67,7 @@
     
     #define NRFX_LOG_MODULE TIMER
     #include <nrfx_log.h>
    +#include <zephyr/sys/printk.h>
     
     /** @brief Timer control block. */
     typedef struct
    @@ -157,7 +158,11 @@ nrfx_err_t nrfx_timer_init(nrfx_timer_t const *        p_instance,
         p_cb->state = err_code == NRFX_SUCCESS ?
                     NRFX_DRV_STATE_INITIALIZED : NRFX_DRV_STATE_UNINITIALIZED;
     
    -    NRFX_LOG_INFO("Function: %s, error code: %s.", __func__, NRFX_LOG_ERROR_STRING_GET(err_code));
    +    if (err_code != NRFX_SUCCESS) 
    +    {
    +        printk("Function: %s, error code: %s.", __func__, NRFX_LOG_ERROR_STRING_GET(err_code));
    +    }
    +
         return err_code;
     }
     
    @@ -185,6 +190,10 @@ nrfx_err_t nrfx_timer_reconfigure(nrfx_timer_t const *        p_instance,
     
     void nrfx_timer_uninit(nrfx_timer_t const * p_instance)
     {
    +    if ( m_cb[p_instance->instance_id].state == NRFX_DRV_STATE_UNINITIALIZED )
    +    {
    +        printk("Instance %d is already uninitialized.\n", p_instance->instance_id);
    +    }
         NRFX_ASSERT(m_cb[p_instance->instance_id].state != NRFX_DRV_STATE_UNINITIALIZED);
     
         nrfy_timer_int_uninit(p_instance->p_reg);
    @@ -295,6 +304,15 @@ void nrfx_timer_compare(nrfx_timer_t const *   p_instance,
                             uint32_t               cc_value,
                             bool                   enable_int)
     {
    +
    +    if (m_cb[p_instance->instance_id].state == NRFX_DRV_STATE_UNINITIALIZED)
    +    {
    +        printk("Timer inst %d, state %d, chan %d\n",
    +            p_instance->instance_id,
    +            m_cb[p_instance->instance_id].state,
    +            cc_channel);
    +    }
    +
         NRFX_ASSERT(m_cb[p_instance->instance_id].state != NRFX_DRV_STATE_UNINITIALIZED);
     
         nrf_timer_int_mask_t timer_int = nrfy_timer_compare_int_get(cc_channel);


  • Hi,

     

    I believe you might be on the right track to why it occurs, and this is something that I will report internally.

    Just for testing/debug purposes, could you manually stop the radio before running the esb_disable() routine and see if this fixes the issue?

    nrf_radio_task_trigger(NRF_RADIO, NRF_RADIO_TASK_STOP);
     
    Kind regards,
    Håkon
     
Reply Children
  • Yes, it helps. When the radio is disabled, the esb deinitialization procedure works correctly.

  • Hi,

     

    Thank you for confirming and testing. This is highly appreciated. I believe you have found a bug in our library, which is exactly as you previously described:

    Rote said:
    As you can see, the error is thrown by Timer 2, which is used by the ESB module. This timer has a state of NRFX_DRV_STATE_UNINITIALIZED, which is not allowed and triggers the assertion.

    This state suggests that the timer was disabled by sys_timer_deinit, but before esb_irq_disable was called. The ESB interrupt handler then tries to use the timer, leading to the failure.
    Debugging this issue confirmed my suspicion.

    I will ofcourse follow up on this internally and ensure that the issue is handled for future versions of the SDK. Thank you so much for your cooperation and patience in this matter.

     

    Kind regards,

    Håkon

Related