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

NRF_CLOCK->LFCLKSTAT register contents are not properly evaluated after a system reset if RTC compare event 1 or 2 are used

I stumbled upon an issue where the LFCLKSTAT register in the NRF_CLOCK peripheral can not be properly evaluated after a softreset if the application that was running before the soft reset was executed uses either the COMPARE1 or COMPARE2 interrupt of an RTC peripheral.

I tried to condense the code leading to this issue to a concise excample.

The LFCLK startup code is shown below. I evaluate the LFCLKSTAT register first, because in a real-life application, the bootloader may have already started the LFCLK.

  if (((NRF_CLOCK->LFCLKSTAT & CLOCK_LFCLKSTAT_STATE_Msk) >> CLOCK_LFCLKSTAT_STATE_Pos) == 0)
  {
    // This block is never reached if RTC compare event 1 or 2 were used.
  
    // select LFCLK
    switch (m_lfclk_config.source)
    {
      case NRF_CLOCK_LF_SRC_RC:
        NRF_CLOCK->LFCLKSRC = CLOCK_LFCLKSRC_SRC_RC;
        break; // NRF_CLOCK_LF_SRC_RC
      
      case NRF_CLOCK_LF_SRC_XTAL:
        NRF_CLOCK->LFCLKSRC = CLOCK_LFCLKSRC_SRC_Xtal;
        break; // NRF_CLOCK_LF_SRC_XTAL
      
      case NRF_CLOCK_LF_SRC_SYNTH:
        NRF_CLOCK->LFCLKSRC = CLOCK_LFCLKSRC_SRC_Synth;
        break; // NRF_CLOCK_LF_SRC_SYNTH
    }
  
    NRF_CLOCK->EVENTS_LFCLKSTARTED = 0;
    // Enable interrupt, so we can call __WFE later.
    NRF_CLOCK->INTENSET = CLOCK_INTENSET_LFCLKSTARTED_Msk;

#if defined(NRF52832_XXAA) || defined(NRF52832_XXAB)
    nrfx_clock_anomaly_132();
#endif // defined(NRF52832_XXAA) || defined(NRF52832_XXAB)
    
    NRF_CLOCK->TASKS_LFCLKSTART = 1;
    while (NRF_CLOCK->EVENTS_LFCLKSTARTED == 0)
    {
      __WFE();
    }
    NRF_CLOCK->INTENCLR = CLOCK_INTENSET_LFCLKSTARTED_Msk;
    NRF_CLOCK->EVENTS_LFCLKSTARTED = 0;
  }

The code inside the above if-block does not get executed after a soft reset if RTC compare event 1 or 2 were used.

The main-function, which calls the above clock initialisation and initializes the RTC is shown below:

int main(void)
{ 
  nrf_gpio_cfg_output(28);
  nrf_gpio_cfg_output(4);
  
  // allow wakeup from sleep on pending interrupt
  SCB->SCR |= SCB_SCR_SEVONPEND_Msk;
  
  nrf_gpio_pin_set(4);
  init_lfclk_f();
  nrf_gpio_pin_clear(4);
  
  // setup and start RTC0
  NRF_RTC0->PRESCALER = (RTC_PRESCALER - 1);
  NRF_RTC0->CC[1] = (NRF_RTC0->COUNTER + RTC_INTERVAL) & RTC_CNT_MAX;
  NRF_RTC0->INTENSET = RTC_INTENSET_COMPARE1_Msk;
  NRF_RTC0->EVTENSET = RTC_EVTEN_COMPARE1_Msk;
  NVIC_SetPriority(RTC0_IRQn, APP_IRQ_PRIORITY_LOW);
  NVIC_EnableIRQ(RTC0_IRQn);
  NRF_RTC0->TASKS_START = 1;
  
  // enable interrupts
  __ASM("CPSIE i");
  
  nrf_gpio_pin_set(28);
  
  while(1)
  {     
    // enter application wait
    __WFE();
  }
  return (0);
}

And the RTC0_IRQHandler...

void RTC0_IRQHandler(void)
{
  uint_fast32_t next_tick;
  static uint8_t num_invoked = 0;
    
  // COMPARE1 event ?
  if (NRF_RTC0->EVENTS_COMPARE[1] != 0)
  {
    // clear event
    NRF_RTC0->EVENTS_COMPARE[1] = 0;
    // force write operation to be done by reading the value back in Cortex M4F devices
    // URL: http://infocenter.nordicsemi.com/topic/com.nordic.infocenter.nrf52.v1.0.0/migration/preface.html
    (void)NRF_RTC0->EVENTS_COMPARE[1];

    // calculate next tick
    next_tick = (NRF_RTC0->CC[1] + RTC_INTERVAL) & RTC_CNT_MAX;

    // set next tick
    NRF_RTC0->CC[1] = next_tick;
    
    if (++num_invoked == 3)
    {
      nrf_gpio_pin_clear(28);
      NVIC_SystemReset();
    }
  }
}

If using the COMPARE0 event instead of COMPARE1 or COMPARE2, the LFCLKSTAT register can be evaluated just fine after a reset. Also, when setting a debugger breakpoint *before* the clock initialisation, the register seems to be evaluated just fine. But an artificial delay in software does not seem to do the trick.

I've also attached the complete example in a zip file:

/cfs-file/__key/support-attachments/beef5d1b77644c448dabff31668f3a47-cc406bf8178b42319c481185b5dab4c1/rtc_2D00_lfclk_2D00_problem.zip

I think, this is actually related to this unresolved issue I had a few months back: https://devzone.nordicsemi.com/f/nordic-q-a/27195/lfclk-related-issues-in-sdk14-dfu

Parents
  • Hi,

     

    I can confirm the same behavior at my end as well, running on a nRF52832 rev 2 (E00) device. Saving the LFCLKSTAT content to a global shows that it is set to 0x10001, but if you read the LFCLKSTAT register some time afterwards, it is 0x00000.

    By adding the errata #132 delay (nrfx_clock_anomaly_132()) before reading the NRF_CLOCK->LFCLKSTAT register, this seems to fix the issue.

    I will report this internally. Thank you very much for reporting this to us, and thank you for providing code that reproduces the issue as well.

     

    Kind regards,

    Håkon

     

  • Hi Håkon,

    Unfortunately moving the nrfx_clock_anomaly_132() function to before reading the NRF_CLOCK->LFCLKSTAT register does not do the trick in my actual application... so I was right in my assumption that I actually tested this, just not in the reduced example I uploaded here.

    What's missing in this example is the use of the Watchdog-peripheral. If I additionally configure and enable the watchdog peripheral, the delay before reading NRF_CLOCK->LFCLKSTAT does not help anymore.

    // After RTC initialisation:
    
      // setup watchdog
      NRF_WDT->CONFIG =   (WDT_CONFIG_SLEEP_Run << WDT_CONFIG_SLEEP_Pos)
                        | (WDT_CONFIG_HALT_Pause << WDT_CONFIG_HALT_Pos);
    
      NRF_WDT->RREN = WDT_RREN_RR0_Enabled << WDT_RREN_RR0_Pos;           // only RR0 enabled for watchdog reset
      NRF_WDT->CRV = (WDT_TIMEOUT * LFCLK_FREQUENCY);                // set watchdog timeout
      NRF_WDT->INTENSET = (WDT_INTENSET_TIMEOUT_Enabled << WDT_INTENSET_TIMEOUT_Pos);
      NVIC_SetPriority(WDT_IRQn, APP_IRQ_PRIORITY_HIGH);
      NVIC_EnableIRQ(WDT_IRQn);
    
      // start watchdog
      NRF_WDT->TASKS_START = 1;

    I've uploaded the modified example which also enables the watchdog and calls nrfx_clock_anomaly_132() before reading LFCLKSTAT.

    It'd be great if you could look into this issue, as it basically prevents me from using the watchdog.

    Regards,

    -mike

    rtc-wdt-lfclk-problem.zip

  • Hi Mike,

     

    Sorry for the late reply. I can confirm the behavior that you see.

    This is unfortunately a feature of the hardware itself, as the WDT will force the LFCLK on, but the system will be cleared when performing a SystemReset(). LFCLK will still run at this point, regardless of what is reflected in the LFCLKSTAT register.

     

    Best regards,

    Håkon

  • Hi Håkon

    So how would I be able to get the RTC running after such a SystemReset()? Just waiting a few seconds until I run into a watchdog reset does not seem to be a viable option to me. What am I missing here?

    Regards,

    -mike

  • Hi Mike

     

    You'll need to start the LFCLK regardless.

    The LFCLKSTAT register looks to be latched for a small period of time when coming out of reset, thus the first if-check will not start the LFCLK.

    My former answer is not 100 % correct. In this specific scenario; the LFCLK will run, but only source the WDT. In order for the RTC to use it, you must start the LFCLK.

     

    Kind regards,

    Håkon

  • Hi Håkon,

    Sorry, I should have been more specific with my previous response or actually should have tested this more thoroughly. This actually resolved the issue.

    I augmented the check for the running LFCLOCK with a check for a reset reason of soft-reset:

    uint32_t reset_reason = NRF_POWER->RESETREAS;
    NRF_POWER->RESETREAS = 0xffffffff;
    
    // start LFCLK prior to starting by the softdevice enable to startup in wait state
    if (    ((reset_reason & POWER_RESETREAS_SREQ_Msk) != 0)
      || ((NRF_CLOCK->LFCLKSTAT & CLOCK_LFCLKSTAT_STATE_Msk) >> CLOCK_LFCLKSTAT_STATE_Pos) == 0)
    {
      // ...
    }

    I still ran into a watchdog reset afterwards. I first assumed that there still was an issue with the LFCLK or the RTC, ignoring the fact that to make sure to not run into a watchdog reset after a soft reset, I should feed the watchdog immediately.

    Thanks for your help.

    Regards,

    -mike

  • Hello All,

    I would like to add my few cents to this discussion, although it's more than year since the last comment.

    I am working with nrf52832 and SDK 14.2.0. I was trying to implement WDT, supported in bootloader and DFU. I came to problem that is reported by many developers - the device is resetting every 5 sec (my WDT interval) by WDT. I went through all articles on WDT, bootloader and DFU. I implemented WDT and its feeding according to all recommendations. Despite that the reset was present.

    I managed to identify that the problem becomes in the bootloader when launching the application and WDT is running. Further down I came to function lfclk_stop(void) in nrf_drv_clock.c that is called just before calling the main application, as part of clean up. There is called while (nrf_clock_lf_is_running()) {} loop and that is never ending for the reason as explained in this thread - LFCLK is not possible to stop because of running WDT.

    How to fix it? By a very simple way - I just added a condition if (!nrf_wdt_started()) before the while loop. Even more, the main application initializes LFCLK and it works ok with this little trick.

    Maybe useful for those who struggle with fixing WDT.

    Best regards,

    peter

Reply
  • Hello All,

    I would like to add my few cents to this discussion, although it's more than year since the last comment.

    I am working with nrf52832 and SDK 14.2.0. I was trying to implement WDT, supported in bootloader and DFU. I came to problem that is reported by many developers - the device is resetting every 5 sec (my WDT interval) by WDT. I went through all articles on WDT, bootloader and DFU. I implemented WDT and its feeding according to all recommendations. Despite that the reset was present.

    I managed to identify that the problem becomes in the bootloader when launching the application and WDT is running. Further down I came to function lfclk_stop(void) in nrf_drv_clock.c that is called just before calling the main application, as part of clean up. There is called while (nrf_clock_lf_is_running()) {} loop and that is never ending for the reason as explained in this thread - LFCLK is not possible to stop because of running WDT.

    How to fix it? By a very simple way - I just added a condition if (!nrf_wdt_started()) before the while loop. Even more, the main application initializes LFCLK and it works ok with this little trick.

    Maybe useful for those who struggle with fixing WDT.

    Best regards,

    peter

Children
No Data
Related