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

nRF52840, SDK 14.0.0 and tickless idle FreeRTOS - some suggestions

This post is more about some suggestions for review rather than a question.

I've been looking at implementing a project using the nRF52840, SDK 14.0.0 and FreeRTOS in a low power application. The release note for SDK 14.0.0. indicates that FreeRTOS is not supported on the nRF52840, however I see no reason why FreeRTOS shouldn't work on this platform.

I ported the example ble_app_hrs_freertos app for ARM GCC to pca10056 and found that current consumption was very high even with tickless idle enabled. Upon profiling the idle code, I found that sd_app_event_wait() was being called and then exiting immediately on every iteration of the idle loop.

There were what seemed to be a number of improvements that could be made. Please see the attached port_cmsis_systick.c files for my suggested changes.

They include:

  • Don't turn off app interrupts when going into idle. Instead only turn off RTC timing interrupts. This should improve interrupt latency while sleeping since interrupt handlers will run directly after return from WFE events. This also allows interrupt handlers to make FreeRTOS API calls which could potentially set tasks to running.
  • Stay as long as possible in the inner tickless idle loop if there is no reason to exit. That is, the only reason to exit idle is if a task has been scheduled to run, or the maximum time to wait has elapsed.
  • Don't rely solely on RTC capture compare interrupt to wake from WFE. Instead, use a counter comparison check to determine if wait time has elapsed.
  • Clear pending CPU event using SEV/WFE before testing for idle exit and going to sleep. This prevents any event prior to going to idle from waking the CPU immediately on a call to sd_app_event_wait().

There are likely some improvements to make to this code, however I have found that these changes result in a significant reduction in consumed power.

The attached port_cmsis_systick_with_profiling.c contains a set of counters which are ticked at various points of the idle loop to try and gain an understanding of how often events occur. eg We can determine how often the code requests idle, how many times we wake CPU from idle but the conditions for FreeRTOS are not met to exit idle (eg as a result of a BLE interrupt).

I'd be interested in feedback on this approach and to know if there are any holes in my reasoning with the proposed changes.

I've attached the ble_app_hrs_freertos app ported to pca10056. This also includes an FPU_IRQHandler to address issues with CPU not sleeping with FPU interrupt pending.

ble_app_hrs_freertos_pca10056.tgz

port_cmsis_systick_with_profiling.c

port_cmsis_systick.c

UPDATED 16th Oct 2018:

See the updated port_cmsis_systick.c which includes important changes including the following:

  • Fixes a bug which could result in infinite sleep in idle.  Ensure that pending RTC wake interrupt is cleared before entry to sleep.
  • Ensures pending FPU interrupts are cleared before entry to sleep.
  • Support GPIO profiling of time spent asleep.  Use critical section around WFE instruction when profiling to ensure that GPIO pins are set to show CPU asleep/awake state prior to interrupt handlers running.

Parents
  • Hi Austin!

    Great work! With your code current consumption on my nRF52832 custom board dropped from 1 mA to 0,1 mA.

  • Thanks for posting your current consumption statistics, that's a great result.

  • Sorry for nesting this under another comment., but due to the age of this ticket, I'm forced to reply to a comment.

    For any future consumers of 's solution, which I do find to be quite nice, there are a few things to watch out for.

    1. In ISRs that wake the cpu from sleep, the tick count will be stale, meaning any FreeRTOS call that relies on the tick value will be inaccurate. I found out the hard way that my software timer started from an ISR was inaccurate because the RTOS tick count isn't updated until after the ISR completes. Austin hints at this in the description, but I thought it would be useful to explicitly call out.
    2. While it is common practice to minimize time in ISRs, there is a race condition with the RTC tick and ISR completion when waking from sleep. If sleep is exited due to the RTC timeout and then an interrupt occurs prior to reading the RTC counter for performing the vTaskStepTick(diff) operation, if the interrupt's ISR runs long enough for another RTC tick to occur, then the tick correction's diff value will exceed 
      xExpectedIdleTime. If configUSE_TICKLESS_IDLE_SIMPLE_DEBUG is defined, a tick will be lost. If not defined, vTaskStepTick will assert if enabled.
Reply
  • Sorry for nesting this under another comment., but due to the age of this ticket, I'm forced to reply to a comment.

    For any future consumers of 's solution, which I do find to be quite nice, there are a few things to watch out for.

    1. In ISRs that wake the cpu from sleep, the tick count will be stale, meaning any FreeRTOS call that relies on the tick value will be inaccurate. I found out the hard way that my software timer started from an ISR was inaccurate because the RTOS tick count isn't updated until after the ISR completes. Austin hints at this in the description, but I thought it would be useful to explicitly call out.
    2. While it is common practice to minimize time in ISRs, there is a race condition with the RTC tick and ISR completion when waking from sleep. If sleep is exited due to the RTC timeout and then an interrupt occurs prior to reading the RTC counter for performing the vTaskStepTick(diff) operation, if the interrupt's ISR runs long enough for another RTC tick to occur, then the tick correction's diff value will exceed 
      xExpectedIdleTime. If configUSE_TICKLESS_IDLE_SIMPLE_DEBUG is defined, a tick will be lost. If not defined, vTaskStepTick will assert if enabled.
Children
No Data