This post is older than 2 years and might not be relevant anymore
More Info: Consider searching for newer posts

Disabling the softdevice hangs when using LFRC with an active watchdog

Using LFRC as the softdevice LFCLK source, enabling and running the WDT causes the softdevice to hang until the watchdog reset when calling nrf_sdh_disable_request. This is with SDK v15.3.0 on both S112 and S140.

Configuring the WDT to pause on debugging, I was able to trace the call to following:

1. nrf_sdh_disable_request
2. sdh_state_observer_notify
3. handler(evt, p_observer->p_context) (which is sd_state_evt_handler in the first iteration of my loop)
4. nrfx_clock_enable
5. nrf_drv_clock_lfclk_release
6. lfclk_stop
7. nrfx_clock_lfclk_stop
8. while(nrf_clock_lf_is_running()). <--- Hangs here (nrfx_clock.c:245)
The PS states "When started, the watchdog will automatically force the 32.768 kHz RC oscillator on as long as no other 32.768 kHz clock source is running and generating the 32.768 kHz system clock".
Therefore, I presume that the hang is caused from the softdevice entering a blocking while loop waiting for an LFCLK src to stop that never will because of the WDT.

Given the functions below, I don't see a way for the softdevice to successfully disable while using an LFRC that is also in use by the WDT, without registering an m_clock_cb.lfclk_requests, which the nrfx_wdt drivers do not appear to do:
/**
 * @brief SoftDevice enable/disable state handler.
 *
 * @param[in] state     State.
 * @param[in] p_context Context.
 */
static void sd_state_evt_handler(nrf_sdh_state_evt_t state, void * p_context)
{
    switch (state)
    {
        case NRF_SDH_EVT_STATE_ENABLE_PREPARE:
            NVIC_DisableIRQ(POWER_CLOCK_IRQn);
            break;

        case NRF_SDH_EVT_STATE_ENABLED:
            CRITICAL_REGION_ENTER();
            /* Make sure that nrf_drv_clock module is initialized */
            if (!m_clock_cb.module_initialized)
            {
                (void)nrf_drv_clock_init();
            }
            /* SD is one of the LFCLK requesters, but it will enable it by itself. */
            ++(m_clock_cb.lfclk_requests);
            m_clock_cb.lfclk_on = true;
            CRITICAL_REGION_EXIT();
            break;

        case NRF_SDH_EVT_STATE_DISABLED:
            /* Reinit interrupts */
            ASSERT(m_clock_cb.module_initialized);
            nrfx_clock_enable();

            /* SD leaves LFCLK enabled - disable it if it is no longer required. */
            nrf_drv_clock_lfclk_release();
            break;

        default:
            break;
    }
}
void nrf_drv_clock_lfclk_release(void)
{
    ASSERT(m_clock_cb.module_initialized);
    ASSERT(m_clock_cb.lfclk_requests > 0);

    CRITICAL_REGION_ENTER();
    --(m_clock_cb.lfclk_requests);
    if (m_clock_cb.lfclk_requests == 0)
    {
        lfclk_stop();
    }
    CRITICAL_REGION_EXIT();
}

nrfx_err_t nrfx_wdt_init(nrfx_wdt_config_t const * p_config,
                         nrfx_wdt_event_handler_t  wdt_event_handler)
{
    NRFX_ASSERT(p_config);
    nrfx_err_t err_code;

#if !NRFX_CHECK(NRFX_WDT_CONFIG_NO_IRQ)
    NRFX_ASSERT(wdt_event_handler != NULL);
    m_wdt_event_handler = wdt_event_handler;
#else
    NRFX_ASSERT(wdt_event_handler == NULL);
    (void)wdt_event_handler;
#endif
    if (m_state == NRFX_DRV_STATE_UNINITIALIZED)
    {
        m_state = NRFX_DRV_STATE_INITIALIZED;
    }
    else
    {
        err_code = NRFX_ERROR_INVALID_STATE;
        NRFX_LOG_WARNING("Function: %s, error code: %s.",
                         __func__,
                         NRFX_LOG_ERROR_STRING_GET(err_code));
        return err_code;
    }

    nrf_wdt_behaviour_set(p_config->behaviour);

    nrf_wdt_reload_value_set((p_config->reload_value * 32768) / 1000);

#if !NRFX_CHECK(NRFX_WDT_CONFIG_NO_IRQ)
    NRFX_IRQ_PRIORITY_SET(WDT_IRQn, p_config->interrupt_priority);
    NRFX_IRQ_ENABLE(WDT_IRQn);
#endif

    err_code = NRFX_SUCCESS;
    NRFX_LOG_INFO("Function: %s, error code: %s.", __func__, NRFX_LOG_ERROR_STRING_GET(err_code));
    return err_code;
}

Am I missing a softdevice call or step that enables it to proceed while sharing the LFCLK source with WDT?

Here are my clock settings for the softdevice:


// <o> NRF_SDH_CLOCK_LF_SRC  - SoftDevice clock source.

// <0=> NRF_CLOCK_LF_SRC_RC
// <1=> NRF_CLOCK_LF_SRC_XTAL
// <2=> NRF_CLOCK_LF_SRC_SYNTH

#ifndef NRF_SDH_CLOCK_LF_SRC
#define NRF_SDH_CLOCK_LF_SRC 1
#endif

// <o> NRF_SDH_CLOCK_LF_RC_CTIV - SoftDevice calibration timer interval.
#ifndef NRF_SDH_CLOCK_LF_RC_CTIV
#define NRF_SDH_CLOCK_LF_RC_CTIV 16
#endif

// <o> NRF_SDH_CLOCK_LF_RC_TEMP_CTIV - SoftDevice calibration timer interval under constant temperature.
// <i> How often (in number of calibration intervals) the RC oscillator shall be calibrated
// <i>  if the temperature has not changed.

#ifndef NRF_SDH_CLOCK_LF_RC_TEMP_CTIV
#define NRF_SDH_CLOCK_LF_RC_TEMP_CTIV 2
#endif

Commenting out the call to nrf_drv_clock_lfclk_release or lfclk_stop prevents the hang.

Also, if I switch NRF_SDH_CLOCK_LF_SRC to LFSYNT, the softdevice disables successfully without any hanging.  However, the softdevice documentation states that there are only two sources usable for the softdevice: the internal RC and XTAL.

Is LFSYNT not recommended for the softdevice? Or is this a valid alternative as an "internal RC" if the additional current consumption is acceptable?

Related