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,

     

    This makes sense. The Watchdog will force the LFCLK src to run, even if the application shuts off the LFCLK. Note that if you go into the WDT_IRQHandler, the watchdog will perform a reset after a given amount of time (2-3 LFCLK cycles), which is normally enough for the application to boot up again and code.

    As stated here in the PS

    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

    Since a soft-reset will not reset the WDT (as seen in this table), the WDT will be kept on when you boot up again and thus also the LFCLK.

    Somewhere in the main program, it should do a WDT-reset (unless you feed it before the WDT-timeout), then it will start up as normal again and repeat this process.

     

    However; this does not explain the behavior that I saw, that the LFCLKSTAT was not reset properly until approx. 132 us after boot up.

     

    Best regards,

    Håkon

  • Hi Håkon,

    I'm not sure I understand you right. So if I use the WDT, I should not use the SoftReset?

    Since a soft-reset will not reset the WDT (as seen in this table), the WDT will be kept on when you boot up again and thus also the LFCLK.

    What confuses me here is that it still seems that the LFCLKSTAT register is evaluated wrongly or at least the Keil SystemViewer displays wrong register contents? If I understand you right, the WDT that was enabled before the SoftReset forces the LFCLK to not stop. So it should still be running after the soft reset. The check of the LFCLKSTAT register in the code fails - indicating that the LFCLK indeed should be already running. But when inspecting the registers in the SystemViewer, the LFCLKSTAT register shows the LFCLK as not running and the scheduled RTC compare events will not trigger either. Because in my actual application I use one of these RTC compare events to trigger feeding of the Watchdog (indirectly, of course), this will result in a watchdog reset being executed when the watchdog timeout occurs after the soft reset.

    This behaviour can be reproduced in the second demo I uploaded. You will notice that after a system reset was triggered in the RTC_IRQHandler, the same IRQ handler will not be invoked again but the watchdog will trigger.

    (Screenshot showing LFCLKSTAT register in SystemViewer some time after a soft reset)

    Regards,

    -mike

  • 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

Reply Children
  • 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

Related