nRF52832 high current consumption after reset

Hi folks

We are facing an issue with occasionally ~240 uA added current consumption on our nRF52832 based custom board when it comes out of reset. We only see the problem on maybe 1-2% of the devices and even devices having this erratic behavior does not aways exhibit this issue. To narrow down the root cause of the problem we have tried various things, but so far only below "solutions" seems to solve the problem:

1. Replace the softreset with a watchdog reset seems to solve the problem.
2. Removing the secure bootloader from the solution and just let the device startup in our application from reset seems to solve the problem. We can softreset the device without having the extra current consumption after reset.

We have verified that just before softresetting the device the current consumption is normal (not the added ~230 uA), but just after reset the current is added an so far only if the secure bootloader is part of the solution.

Judging from the amount of added current consumption, I suspect it is a resource in the chip that has requested for the HF oscillator and forget to turn it off again. Is there any known issues regarding this with the secure bootloader? Any other clue that could cause this issue? 

We are using Softdevice S132 v5.0.0 / SDK 14.0.0 and its accompanying secure_bootloader_ble

Parents
  • Hi,

    Is the device using the LFXO or LFRC as low frequency clock soruce? The reason I ask is that there is a known issue in S132 version 5 where the HFXO may be left running after LFRC calibration. Thisis fixed in version 6.0.0.

  • Both the bootloader and the application are configured with "CLOCK_CONFIG_LF_SRC 1" in the sdk_config.h file, which means 32 kHz XTAL, we are not using the LFRC oscillator.

  • Now tried without the bootloader involved at all (debug build but with same build switches) and optimization level as in the release build). In test scenario 1 it now works as expected with only ~2 uA standby current after each soft reset. However Test scenario 2 is the same, every retriggering of the watchdog causes the standby current to jump between 2 uA and 250 uA. 

  • I wrote some bare-metal code which doesn't exhibit the issue; how different is the code you have issues with? Sleep has a bunch of issues regarding power, so possibly it's some weird combination of sleep and WDT

    // Watchdog WDT
    // RR[0] register request reload access key value
    #define WATCHDOG_RELOAD_VALUE 0x6E524635
    // Set 60 second watchdog timeout
    #define WDT_TIMWOUT ((60UL * 32768UL) - 1UL)
    #define TYPICAL_INTERRUPT_PRIORITY  6
    // RTC ticks per second using 8 Hz
    #define RTC_TICKS_PER_SECOND 8UL
    // Retrigger watchdog, maybe every 10 seconds
    #define TRIGGER_COUNT_IN_TICKS (10UL*RTC_TICKS_PER_SECOND)
    // Throw in a soft reset every now and then, maybe every 90 seconds
    #define SOFT_RESET_COUNT_IN_TICKS (90UL*RTC_TICKS_PER_SECOND)
    // Throw in a watchdog reset every now and then, maybe every 900 seconds
    #define WATCHDOG_RESET_COUNT_IN_TICKS (900UL*RTC_TICKS_PER_SECOND)
    
    static NRF_RTC_Type *pSpiRTC  = NRF_RTC2;
    IRQn_Type SpiRTC_IRQn         = RTC2_IRQn;
    volatile bool mRTC_Tick = true;
    // Counter for how often the watchdog should be triggered, cleared on every reset
    uint32_t WDT_TriggerPeriodCounter = 0UL;
    // Counter for how often the soft reset should be issued, cleared on every reset
    uint32_t SystemResetCounter = 0UL;
    // Place following data in section .noinit (IAR) so it doesn't get wiped on a reset (.non_init SES & .noinit GCC)
    #pragma default_variable_attributes = @ ".noinit"
    static uint32_t SystemWatchdogTimer __attribute__((section(".noinit")));
    #pragma default_variable_attributes = // ends ".noinit"
    
    void RTC2_IRQHandler(void)
    {
        if (pSpiRTC->EVENTS_TICK == 1)
        {
            pSpiRTC->EVENTS_TICK = 0;
            mRTC_Tick = true;
        }
        // Clear any pending hardware register bus operations
        __DSB();
    }
    
    // Run this code from startup before SystemInit()
    static void TestWatchdog(void)
    {
       // Unless cleared, the RESETREAS register will be cumulative. A field is cleared by writing '1' to it. If none of
       // the reset sources are flagged, this indicates that the chip was reset from the on-chip reset generator, which
       // will indicate a power-on-reset or a brownout reset
       // Workaround for Errata 136: System: Bits in RESETREAS are set when they should not be
       if (1) //(errata_136())
       {
          if (NRF_POWER->RESETREAS & POWER_RESETREAS_RESETPIN_Msk)
          {
             NRF_POWER->RESETREAS = ~POWER_RESETREAS_RESETPIN_Msk;
          }
       }
       // Check reset reasons - Watchdog
       if (NRF_POWER->RESETREAS & POWER_RESETREAS_DOG_Msk)
       {
       }
       // Check reset reasons - soft reset
       if (NRF_POWER->RESETREAS & POWER_RESETREAS_SREQ_Msk)
       {
       }
       // Watchdog and timer should be running if this is a repeated soft reset; also restart on watchdog reset
       if ((NRF_WDT->RUNSTATUS == 0) || ((NRF_POWER->RESETREAS & POWER_RESETREAS_SREQ_Msk) == 0) || (NRF_POWER->RESETREAS & POWER_RESETREAS_DOG_Msk))
       {
          // Tested if RTC safely starts a non-running LFCLK - it does not
          // Watchdog not running - assume power-on reset so init LFCLK and WDT
          SystemWatchdogTimer = 0UL;
          // Select 32kHz clock source
          //NRF_CLOCK->LFCLKSRC = CLOCK_LFCLKSRC_SRC_Xtal << CLOCK_LFCLKSRC_SRC_Pos;
          NRF_CLOCK->LFCLKSRC = CLOCK_LFCLKSRC_SRC_RC << CLOCK_LFCLKSRC_SRC_Pos;
          //NRF_CLOCK->LFCLKSRC = CLOCK_LFCLKSRC_SRC_Synth << CLOCK_LFCLKSRC_SRC_Pos;
          // Start 32kHz clock
          NRF_CLOCK->EVENTS_LFCLKSTARTED = 0;
          __DSB();
          NRF_CLOCK->TASKS_LFCLKSTART = 1;
          __DSB();
          while (NRF_CLOCK->EVENTS_LFCLKSTARTED == 0) ;
          // Clear started event
          NRF_CLOCK->EVENTS_LFCLKSTARTED = 0;
          // Start RTC, enable TICK event
          // Select tick: 12 bit prescaler for COUNTER frequency (32768/(PRESCALER+1)). Must be written when RTC is stopped
          pSpiRTC->PRESCALER = 4095; // max prescaler 0xFFF -> 8 Hz "Tick"
          pSpiRTC->EVTENSET = RTC_EVTENSET_TICK_Msk;
          // Set interrupt priority higher than SWI/EGU priority and enable interrupt
          NVIC_SetPriority(SpiRTC_IRQn, TYPICAL_INTERRUPT_PRIORITY);
          NVIC_ClearPendingIRQ(SpiRTC_IRQn);
          NVIC_EnableIRQ(SpiRTC_IRQn);
          // Enable tick interrupt
          pSpiRTC->INTENSET = 0x00001;
          // The update of COUNTER relies on a stable LFCLK, TASKS_START while LFCLK is not running will start
          // LFCLK, but the update will be delayed by up to ~250 us
          pSpiRTC->TASKS_START = 1;
          // Set watchdog WDT mode
          NRF_WDT->CONFIG = 1UL;
          // Set timeout
          NRF_WDT->CRV = (uint32_t)WDT_TIMWOUT;
          // Enable channel 0
          NRF_WDT->RREN = 1UL;
          // Enable
          NRF_WDT->TASKS_START = 1UL;
       }
       // Clear all reset reasons
       NRF_POWER->RESETREAS = 0xFFFFFFFFUL;
       // Ensure reset timer is cleared
       SystemResetCounter = 0UL;
       while (1) {
          // Wakeup RTC_TICKS_PER_SECOND (8) times per second
          while(!mRTC_Tick)
          {
             //nrf_gpio_pin_set(X14_PIN);
             // Errata 220: CPU: RAM is not ready when written - Disable IRQ while using WFE
             // Enable SEVONPEND to disable interrupts so the internal events that generate the interrupt cause wakeuup in __WFE context and not in interrupt context
             // Before: ENABLE_WAKEUP_SOURCE -> __WFE -> WAKEUP_SOURCE_ISR -> CONTINUE_FROM_ISR  next line of __WFE
             // After:  ENABLE_WAKEUP_SOURCE -> SEVONPEND -> DISABLE_INTERRUPTS -> __WFE -> WAKEUP inside __WFE -> ENABLE_interrupts -> WAKEUP_SOURCE_ISR
             //
             // Errata 75: MWU: Increased current consumption
             // This has to be handled by turning off MWU but it is used in SoftDevice
             // see https://infocenter.nordicsemi.com/index.jsp?topic=%2Ferrata_nRF52832_EngB%2FERR%2FnRF52832%2FEngineeringB%2Flatest%2Fanomaly_832_75.html
             //
             // Errata 220: Enable SEVONPEND
             SCB->SCR |= SCB_SCR_SEVONPEND_Msk;
             __disable_irq();
             // Clear the internal event register and wait for event - note an interrupt is required
             __WFE(); __SEV(); __WFE(); __NOP(); __NOP(); __NOP(); __NOP();
             __enable_irq();
          }
          // Get here RTC_TICKS_PER_SECOND (8) times per second
          mRTC_Tick = false;
          // System ticks wake up frequently, retrigger watchdog every TRIGGER_COUNT_IN__TICKS seconds
          // Retrigger, technically only retrigger if trigger not already pending; hold off if waiting for watchdog reset
          WDT_TriggerPeriodCounter++;
          // Force a periodic watchdog reset
          SystemWatchdogTimer++;
          if ((NRF_WDT->REQSTATUS == 1UL) && (SystemWatchdogTimer < WATCHDOG_RESET_COUNT_IN_TICKS) &&  (WDT_TriggerPeriodCounter >= TRIGGER_COUNT_IN_TICKS))
          {
             NRF_WDT->RR[0] = WATCHDOG_RELOAD_VALUE;
             WDT_TriggerPeriodCounter = 0UL;
          }
          // Barrier to ensure register is set before sleep etc
          __DSB();
          // Throw in a soft reset every now and then, maybe every 90 seconds
          if (++SystemResetCounter > SOFT_RESET_COUNT_IN_TICKS)
          {
             // Clear all reset reasons
             NRF_POWER->RESETREAS = 0xFFFFFFFFUL;
             // Reset on next tick to ensure tick interrupt doesn't occur during a reset
             while(!mRTC_Tick) ;
             mRTC_Tick = false;
             NVIC_SystemReset();
          }
       }
    }

    Edits: Added periodic soft reset. Added only init watchdog and LFCLK if watchdog not running

    Maybe try running this code on a failing device; the code can run before errata in SystemInit().

  • Thanks for suggestions hmolesworth, however I now managed to pinpoint exactly where for some strange reason things goes wrong in Test Scenario 1 (which we use).

    In the bootloader code the nrf_drv_clock.c there is a function to start the LF CLK. In the SDK we use it looks like this:

    static void lfclk_start(void)
    {
      nrf_clock_event_clear(NRF_CLOCK_EVENT_LFCLKSTARTED);
      nrf_clock_int_enable(NRF_CLOCK_INT_LF_STARTED_MASK);
      nrf_clock_task_trigger(NRF_CLOCK_TASK_LFCLKSTART);
    }

    This is called during startup in the bootloader code.

    Running this code we experience the OK OK BAD OK OK BAD .... sequence when ever we soft reset the device.

    Now I discovered inserting a wait just before starting the LF CLK the current consumption is ~2 uA on each and every soft reset. So now I have modified the code to look like this:

    static void lfclk_start(void)
    {
      nrf_clock_event_clear(NRF_CLOCK_EVENT_LFCLKSTARTED);
      nrf_clock_int_enable(NRF_CLOCK_INT_LF_STARTED_MASK);
      nrf_delay_us(21);
      nrf_clock_task_trigger(NRF_CLOCK_TASK_LFCLKSTART);
    }

    Inserting 21 us delay or more before starting the LF CLK all is good. Below 21 us doesn't work.

    Can anyone explain why this is necessary and what is going on here?

    I will try test on more devices to see if this is consistent across nRF52832 chips.

    Above "solution" doesn't change the behavior for Test Scenario 2, it still jumps synchronous with the WDOG retriggering, so maybe we are facing two different problems. 

    /Br Kim

  • The bootloader is not handling the differences in clock domain well, so your solution is a good idea. Even  using a __DSB() instruction after each step is probably insufficient.

    "Jitter or delay in the RTC is due to the peripheral clock being a low frequency clock (LFCLK) which is not synchronous to the faster PCLK16M.
    Registers in the peripheral interface, part of the PCLK16M domain, have a set of mirrored registers in the LFCLK domain. For example, the COUNTER value accessible from the CPU is in the PCLK16M domain and is latched on read from an internal register called COUNTER in the LFCLK domain. COUNTER is the register which is actually modified each time the RTC ticks. These registers must be synchronised between clock domains (PCLK16M and LFCLK)."

    In passing, SDK17 applies a delay thus:

    void nrfx_clock_lfclk_start(void)
    {
        NRFX_ASSERT(m_clock_cb.module_initialized);
        nrf_clock_event_clear(NRF_CLOCK_EVENT_LFCLKSTARTED);
        nrf_clock_int_enable(NRF_CLOCK_INT_LF_STARTED_MASK);
    
    #if NRFX_CHECK(USE_WORKAROUND_FOR_ANOMALY_132)
        nrfx_clock_anomaly_132();
    #endif
    
        nrf_clock_task_trigger(NRF_CLOCK_TASK_LFCLKSTART);
    }
    

    #if NRFX_CHECK(USE_WORKAROUND_FOR_ANOMALY_132)
    /**
     * @brief Function for applying delay of 138us before starting LFCLK.
     */
    static void nrfx_clock_anomaly_132(void)
    {
        uint32_t cyccnt_inital;
        uint32_t core_debug;
        uint32_t dwt_ctrl;
    
        // Preserve DEMCR register to do not influence into its configuration. Enable the trace and
        // debug blocks. It is required to read and write data to DWT block.
        core_debug = CoreDebug->DEMCR;
        CoreDebug->DEMCR = core_debug | CoreDebug_DEMCR_TRCENA_Msk;
    
        // Preserve CTRL register in DWT block to do not influence into its configuration. Make sure
        // that cycle counter is enabled.
        dwt_ctrl = DWT->CTRL;
        DWT->CTRL = dwt_ctrl | DWT_CTRL_CYCCNTENA_Msk;
    
        // Store start value of cycle counter.
        cyccnt_inital = DWT->CYCCNT;
    
        // Delay required time.
        while ((DWT->CYCCNT - cyccnt_inital) < ANOMALY_132_DELAY_CYCLES)
        {}
    
        // Restore preserved registers.
        DWT->CTRL = dwt_ctrl;
        CoreDebug->DEMCR = core_debug;
    }
    #endif // NRFX_CHECK(USE_WORKAROUND_FOR_ANOMALY_132)
    

  • Hi,

    That was a good find. I did not think about it, but it match perfectly with errata 132. I do not recall comming across this before,  probaly because a workaroudn is included around in newer SDK versions but also that you need the "right" timing to trigger it.

    Your workaround seems good in principle, though in order to be 100% safe you should increase the delay to 138 us. Using a nrf_delay_us() call as you do is fine, but you can also copy-paste use the nrfx_clock_anomaly_132() implementation from SDK 17.1.0 if you like.

Reply
  • Hi,

    That was a good find. I did not think about it, but it match perfectly with errata 132. I do not recall comming across this before,  probaly because a workaroudn is included around in newer SDK versions but also that you need the "right" timing to trigger it.

    Your workaround seems good in principle, though in order to be 100% safe you should increase the delay to 138 us. Using a nrf_delay_us() call as you do is fine, but you can also copy-paste use the nrfx_clock_anomaly_132() implementation from SDK 17.1.0 if you like.

Children
Related