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

     

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

     

Children
  • Hi Håkon,

    Thanks for the quick response. I actually thought, that I did test it with the errata #132 delay invoked before reading the NRF_CLOCK->LFCLKSTAT register, but it seems that I really didn't. For me, this is a viable workaround for now. Nonetheless, I would be happy if you could share any insights from your internal analysis.

    Regards,

    -mike

  • 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

Related