Low Power with FreeRTOS: Tickless Idle Mode

It took me a while to find the time to upgrade to FreeRTOS V7.4.2, but finally it is done :-). What caused me to move from V7.2 to V7.4 is a low power application on the FRDM-KL25Z board. V7.4 comes with two major new features: Queue Sets and Tickles Idle Mode (see this article). The last one if of interest here.

FreeRTOS runs an IDLE task. This one runs when there is no other active task. That task calls an optional Idle task hook which is a perfect place to put the microcontroller into low power mode:

Task T1 Running

Task T1 Running

In above logic analyzer screenshot I have a task T1 running for about 10 ms, then it will be suspended for 1000 ms. So during this 1000 ms the microcontroller could be put to sleep :-).

FreeRTOS IDLE Task Hook

That IDLE task calls the vApplicationIdleHook():

FreeRTOS vApplicationIdleHook

FreeRTOS vApplicationIdleHook

In that hook function I can put my microprocessor into sleep mode, e.g. for the KL25Z this could be done like this:

/*
** ===================================================================
**     Event       :  FRTOS1_vApplicationIdleHook (module Events)
**
**     Component   :  FRTOS1 [FreeRTOS]
**     Description :
**         If enabled, this hook will be called when the RTOS is idle.
**         This might be a good place to go into low power mode.
**     Parameters  : None
**     Returns     : Nothing
** ===================================================================
*/
void FRTOS1_vApplicationIdleHook(void)
{
  /* Called whenever the RTOS is idle (from the IDLE task).
     Here would be a good place to put the CPU into low power mode. */
  Cpu_SetOperationMode(DOM_SLEEP, NULL, NULL); /* next interrupt will wake us up */
}

There are many different modes, but the easiest ones are the ones which use an interrupt as wake up source. So for the above source using DOM_SLEEP, the next interrupt will wake me up.

SysTick Interrupt

If any interrupt is a wake-up source, than this could be a timer interrupt or the the tick interrupt which is used as tick timer for the RTOS. On ARM cores the SysTick timer is typically used as the RTOS tick timer, but on other architectures any periodic interrupt timer can be used. And this is causing a problem: the SysTick interrupt will leave the low power mode. So this means that during that 1000 ms I get out of low power mode and back 100 times if I’m using a 10 ms SysTick period.

To illustrate this, I’m using this task code:

static portTASK_FUNCTION(Task1, pvParameters) {
  for(;;) {
    B8_SetVal(); /* Set I/O to 1 */
    /* do some work for 10 ms */
    B8_ClrVal(); /* Set I/O to 0 */
    FRTOS1_vTaskDelay(1000/portTICK_RATE_MS);
  }
}

Using the B8 pin I can see with a logic analyzer how long the task is active.

To see when a SysTick interrupt fires, I’m toggling an I/O pin in the vApplicationTickHook():

/*
** ===================================================================
**     Event       :  FRTOS1_vApplicationTickHook (module Events)
**
**     Component   :  FRTOS1 [FreeRTOS]
**     Description :
**         If enabled, this hook will be called by the RTOS for every
**         tick increment.
**     Parameters  : None
**     Returns     : Nothing
** ===================================================================
*/
void FRTOS1_vApplicationTickHook(void)
{
  /* Called for every RTOS tick. */
  B9_NegVal(); /* toggle I/O pin to see when interrupt fires */
}

With this, I get this on the logic analyzer:

Task with Tick Interrupts

Task with Tick Interrupts

The task T1 suspends for 1000 ms, and this would be an opportunity to be in low power mode for 1 second. But the SysTick constantly wakes the microcontroller up :-(.

The tick interrupt is needed for the RTOS so it can keep track of the time. However, the RTOS already should know how long the IDLE task will run, so a better solution could be used?

Tickless Idle Mode

The solution to this is the ‘Tickless Idle Mode’, added in FreeRTOS V7.4.0. It calculates the idle time and re-configures the SysTick:

  1. Calculates the new timer value. So such as in my above example, instead of firing the timer every 10 ms until it reaches 1000 ms, it will try to set it up that it only fires once after 1000 ms.
  2. Updates the task/system time.

Below is a picture how this looks in this Tickless Idle Mode: The tick interrupt only fires as needed :-). To illustrate the power mode, I’m using the B10 pin (HIGH if in low power mode, LOW if in run mode):

FreeRTOS Tickless Idle Mode

FreeRTOS Tickless Idle Mode

Implementation Details

There are a few limitations and points to consider:

  1. Depending on the tick timer input clock and prescaler, the largest time span is limited. Typically, on ARM cores the SysTick is using the core/bus clock and has a 24bit counter register. This means that with a 20 MHz clock speed the largest ‘tickless time’ can be only up to 800 ms. The higher the clock, the less ‘tickless’ time is available. A solution would be to use a different tick timer source which might not always be possible. Or to lower the clock frequency.

    20 MHz with 800 ms max Tick Duration

    20 MHz with 800 ms max Tick Duration

  2. The tickless implementation in FreeRTOS needs to stop the tick timer momentarily in the routine vPortSuppressTicksAndSleep(). This means there is a tiny drift of the time maintained by the kernel. To compensate that drift, a compensation value can be provided. More about this below.
  3. vPortSuppressTicksAndSleep() needs to find out if the tick interrupt or any other interrupt was leaving the low power mode. This is either done by reading the timer register, or checking a counter which is modified by the tick interrupt. Below is the same application as above, with a 64 ms timer interrupt:

    Timer Interrupt with Low Power Tickless Mode

    Timer Interrupt with Low Power Tickless Mode

Processor Expert Implementation

FreeRTOS V7.4.x comes with tickless example ports for ARM Cortex M3/M4 (see this version). I have changed it so it works for Processor Expert and additional cores (e.g. S08, S12, ColdFire, Kinetis). While the Kinetis port only supports the SysTick, any other timer sources can be used otherwise. The Processor Expert implementation is available on GitHub.

The FreeRTOS component has new properties for tickless mode support:

FreeRTOS V7.4.2 Component with Tickless Idle Mode Support

FreeRTOS V7.4.2 Component with Tickless Idle Mode Support

Tickless Idle Mode” is disabled by default and can be enabled with the new component. To compensate the timer drift (“Stopped Timer Compensation“, see above), a timer value to compensate can be provided (default 42). By default, the tickless mode is used if the expected idle time is larger than 2 ticks: with “Expected Idle Time” that value can be increased.

Inside port.c, the function vPortSuppressTicksAndSleep() implements the tickless idle mode. It is using macros for different timer implemenation (up/down counting modes).

The original RTOS API is using the macro configPRE_SLEEP_PROCESSING()  to enter low power mode and the macro configPOST_SLEEP_PROCESSING() which is used on exit of the low power mode. I have found that this was not very user-friendly, so I defined Processor Expert events instead which are availble if Tickless Idle Mode is enabled:

Tickless Idle Mode Events

Tickless Idle Mode Events

  • vOnPreSleepProcessing(): called to enter low power mode. An argument tells the duration of the tickless time. Here the application needs to enter low power mode and to wait for an interrupt. Additionally the application can disable peripherals or store values in non-volatile areas.
  • vOnPostSleepProcessing(): this event gets called after wake-up, so the application can restore the RAM or re-enable peripherals.

Below is an example implementation of the user application events:

/*
** ===================================================================
**     Event       :  vOnPreSleepProcessing (module Events)
**
**     Component   :  FRTOS1 [FreeRTOS]
**     Description :
**         Used in tickless idle mode only, but required in this mode.
**         Hook for the application to enter low power mode.
**     Parameters  :
**         NAME            - DESCRIPTION
**         expectedIdleTicks - expected idle
**                           time, in ticks
**     Returns     : Nothing
** ===================================================================
*/
void vOnPreSleepProcessing(portTickType expectedIdleTicks)
{
#if 1 /* example for Kinetis (enable SetOperationMode() in CPU component: */
  B10_SetVal(); /* set I/O pin to indicate low power mode */
  Cpu_SetOperationMode(DOM_SLEEP, NULL, NULL);
#endif
#if 0 /* example for S08 (enable SetWaitMode() in CPU: */
  Cpu_SetWaitMode();
#endif
#if 0 /* example for ColdFire V2: */
  __asm("stop #0x2000"); */
#endif
  /* Write your code here ... */
}

/*
** ===================================================================
**     Event       :  vOnPostSleepProcessing (module Events)
**
**     Component   :  FRTOS1 [FreeRTOS]
**     Description :
**         Event called after the CPU woke up after low power mode.
**         This event is optional.
**     Parameters  :
**         NAME            - DESCRIPTION
**         expectedIdleTicks - expected idle
**                           time, in ticks
**     Returns     : Nothing
** ===================================================================
*/
void vOnPostSleepProcessing(portTickType expectedIdleTicks)
{
  B10_ClrVal(); /* cear I/O pin to indicat end of low power mode */
}

❗ Do *not* use the IDLE hook FRTOS1_vApplicationIdleHook() to enter low power if using tickless idle mode, as otherwise the timing will be screwed up.

Summary

Microcontroller vendors offer many advanced low power modes. Unfortunately they are so complicated and hard to use in most cases. That way I see many applications which doe not use them at all. Sure: if my application is very tiny and does not need an RTOS, then usage of the ‘deep sleep’ modes is can be done, as there are few interconnection points. But as soon as many timers are used or an RTOS is running, things get complicated again. FreeRTOS offers an easy way to tap into low power modes with the IDLE task hooks and with the tickless Idle mode. That way it is really easy to reduce the energy consumption even with using an RTOS.

Problematic is the SysTick implementation by ARM as usually it is tight to the CPU clock: This means that running the CPU in the MHz area greatly limits the available time span offered by the SysTick timer.

I have implemented the tickless Idle mode in the Processor Expert FreeRTOS component available on GitHub. For Kinetis/ARM the SysTick is supported, while for the other architectures (S08, S12, ColdFire) any timer can be used which makes things even more low power. With tickless idle mode, the low power mode is extended and less interrupted. With this, lower current end energy levels are reached while using an RTOS.

Happy Ticklessing 🙂

Advertisements

33 thoughts on “Low Power with FreeRTOS: Tickless Idle Mode

  1. Pingback: Optimized FreeRTOS: Stack Check and SysTick for ARM Cortex Cores | MCU on Eclipse

  2. Pingback: Tutorial: Using the FRDM-KL25Z as Low Power Board | MCU on Eclipse

  3. Pingback: FreeRTOS for the Freescale DSC 56F8400 Core | MCU on Eclipse

  4. Hello Erich,
    How do you determine the value of “Stopped Timer Compensation”?
    For example, if you set that value to ’42’, then would RTOS add 42 tick counts to Systick Counter
    every time it wakes up?

    Thank you,
    GI

    Like

    • Hi Gl,
      Have a look at the generated port.c and see the usage of ulStoppedTimerCompensation. The number of ticks is because temporarily the tick timer gets stopped to assign a new value for the next interrupt. So the number of 42 ticks get subtracted from the new value to compensate for the short time where the tick timer is *not* running. I have not fine tuned that value. If you want to do this, then measure the time (e.g. with toggling a pin and measuring it with a logic analyzer between
      DISABLE_TICK_COUNTER();
      and after
      SET_TICK_DURATION(ulReloadValue); /* Set the new reload value. */
      RESET_TICK_COUNTER_VAL(); /* Reset the counter. */
      ENABLE_TICK_COUNTER(); /* Restart tick timer. */

      Happy New Year!

      Like

  5. Pingback: IoT: FreeRTOS Down to the Micro Amps | MCU on Eclipse

  6. Hello Erich,

    I have tried to test this new feature, but the project does not build. Using the Git refresh from today, I have a compiler error in port.c

    void vPortInitTickTimer(void)

    ulStoppedTimerCompensation = configSTOPPED_TIMER_COMPENSATION/(configCPU_CLOCK_HZ/configSYSTICK_CLOCK_HZ);

    undefined identifier ‘CPU_CORE_CLK_HZ’

    MCU is CFV1 (CN128)

    Best regards,
    Juan A Luna

    Like

    • It seems that many of the other RTOS did not care much about low power. But now it seems that they realized that tickless idle mode is a pretty straight forward and clean way to reduce power consumption, especially for ARM cores using the SysTick.

      Like

  7. Hi Erich,

    thank you for your work: it’s higly appreciated!

    I’m currently working on a Kinetis K11 CPU. I’m using the LPTMR as systick, and I’d like to use
    the tickless mode feature. From what I’m reading, Kinetis port only supports the SysTick.

    Do you think it could be possible to make it compatible with the LPTMR? The logic implemented in the current porting can’t be adapted for the LPTMR due to the fact that after a call to DISABLE_TICK_COUNTER (), the timer counter and compare flag register are reset.

    Am I missing something?

    Again, thank for your work!

    Alessandro

    Like

      • Can I ask a supplementary question (and pass on a tip)?

        I’m using tickless idle on a KL05. A couple of different interrupt signals wake the processor as necessary (more accurately: two external signals drive LLWU pins and go high to wake the processor from Stop mode, and I have programmed the pins as level-sensitive active high interrupts so the processor sees the interrupt as soon as it wakes. I found that if I program the pins as edge-sensitive the processor misses the interrupts if the rising edge occurs while it is in Stop mode. It looks like I could call the interrupt event code from Cpu_OnLLSWakeUpINT(), but it seemed simpler to stick to level-sensitive interrupts). This seems to work as intended, though I have not done much testing.

        My question is this: is there any maximum time that can elapse with the processor in Stop mode? In my application, many days might elapse between the external interrupt events. I have no need for any task to run while waiting for these external events. I see your comment “the largest time span is limited”. I suppose I could get a task to sleep for (say) 800ms in a loop – but I really don’t need to wake, and this seems a waste (of battery). Is there any problem if I sleep for days? (I have the 32kHz crystal running to keep time and date, if that is relevant or useful).

        Like

        • Thanks for the tip!
          The processor can remain in STOP mode as long as possible. The issue is how much into the future the RTOS can delay the tick interrupt. The longer the tick period and the more bits in the tick counter register, the longer. For the LPTMR the tick period is fix at 1 kHz (1ms), and it is a 16 bit register. That way the LPTRM can be hold off for 1 minute max. The other approach is to suspend the RTOS and start it again after you have received your interrupt. For the RTOS the real time (RTC) does not matter: all what the RTOS deals with are the timer ticks.

          Like

        • Hi Erich
          Regarding your post that starts “The processor can remain in STOP mode as long as possible. ” – I am setting a pin in Cpu_OnLLSWakeUpINT() and clearing it in FRTOS1_vOnPreSleepProcessing(), so the pin is high more or less while the processor is running. I see expected activity as my external interrupts wake the processor etc. Then I noticed that, with no other activity, the pin pulses about every 66s (for about 28us). So I guess this is the 1kHz LPTMR driving the 16-bit register which overflows every 66s, wakes the RTOS which sleeps again. Actually, it pulses 10s after my last interrupt-driven activity, then every 66s after that. This is OK with me. But is it (a) expected behaviour and (b) safe? And why is there a pulse 10s after my last interrupt?

          Regards – Charles

          Like

        • Hi Charles,
          yes, that 66 seconds is very likely the overflow interrupt of the LPTMR. I suggest that you completely turn off that timer interrupt, and only enable it again after you have received your external interrupt?

          Erich

          Like

      • Hi Erych,

        thank you for the quickly answer!

        Well, actually you’re right. I was working with wrong beans/freertos versions, and the port.c file was different.
        I’ve updated to the last release and now it “seems” to work properly (I’ll do some more tests to be sure).

        Thank you again for your work.

        Alessandro

        Like

  8. Hi Erych,

    I’ve done some more tests and I’m still getting strange behaviors.

    Actually, I’m working with the following setup:

    – Kinetis K11DN512 as MCU (the LPTMR hardware peripheral logic is the same of the KL25Z);
    – IAR compiler;
    – PE components released on 2014-12-08 with FreeRTOS V8.0.1 (port.c version is the one linked here https://github.com/ErichStyger/McuOnEclipse_PEx/commit/c7a01b200915bf612b1277817186a788b36f7f97);

    IAR COMPILER PROBLEM
    The IAR port uses the vPortTickHandler defined in portasm.s. Compared with the GCC implementation (within port.c), the IAR version doesn’t have the piece of code regarding the tickless mode (#if (configUSE_TICKLESS_IDLE == 1) && configSYSTICK_USE_LOW_POWER_TIMER). The acknowledge of the TICK ISR is missing too (but I had already added that in my code).

    Anyway, in my tests I’ve tried both versions of the vPortTickHandler (the IAR original one within portasm.s with the acknowledge of the TICK ISR added by myself, and the one based on the GCC implementation).

    TICKLESS MODE PROBLEM
    I’m getting strange behaviors using the tickless mode. As long as a low number of interrupt is involved, the tickless seems to work properly. Otherwise, the timing of tickless mode begins to be wrong.

    In order to identify better the problem, I’ve done the following test (it should be replicable with little effort with different boards and compilers):

    – LPTMR configured to use the internal LPO clock (1kHz) (configSYSTICK_LOW_POWER_TIMER_CLOCK_HZ -> 1000);
    – FreeRTOS tick rate set to 1000 Hz (configTICK_RATE_HZ -> 1000);
    – UART set to 115200bps as baudrate (in order to involve TX interrupt and wakeup the MCU from the WFI instruction in tickless mode);
    – single task toggling a gpio and sending out via UART 40 bytes each 10ms;

    Task code:

    void task (void* parameters)
    {
    uint8_t buffer[] = { …40 bytes of data… };

    while (1)
    {
    vTaskDelay ((TickType_t) (10 / portTICK_PERIOD_MS));

    BIT_DEBUG_PE_NegVal (NULL);

    UART_PE_SendBlock (UART_PE_DeviceData, buffer, sizeof (buffer));
    }
    }

    Using the oscilloscope, BIT_DEBUG is toggling at ~250Hz (~4ms period, completely wrong).

    I’ve tried also with the following configuration:

    – UART and task unchanged;
    – LPTMR configured to use the external 32kHz clock (configSYSTICK_LOW_POWER_TIMER_CLOCK_HZ -> 32768). In order to configure it correctly I changed, within port.c, LPTMR_PDD_SelectPrescalerSource(LPTMR0_BASE_PTR, LPTMR_PDD_SOURCE_LPO1KHZ); to LPTMR_PDD_SelectPrescalerSource(LPTMR0_BASE_PTR, LPTMR_PDD_SOURCE_EXT32KHZ);
    – FreeRTOS tick rate set to 32kHz (configTICK_RATE_HZ -> 32768);
    – changed #define portTICK_PERIOD_MS to (1000.0f/configTICK_RATE_HZ) (in order to avoid division by zero);

    Using the oscilloscope, BIT_DEBUG is toggling at ~91.6Hz (~10.91ms period). The error is minor, but I’ve noticed that it increments along with the interrupts number:

    – sending 1 byte each 10ms, BIT_DEBUG is toggling correctly at ~99.6Hz (~10.04ms period);
    – sending 20 byte each 10ms, BIT_DEBUG is toggling ~95.6Hz (~10.46ms period);
    – sending 40byte, as reported before, BIT_DEBUG is toggling at ~91.6Hz (~10.91ms period);

    From what I’ve tested (but I’ve could missed something) tickless port is not implemented correctly OR IAR porting is missing something crucial.

    If you’d like to take a look at my code, I could send to you a zip archive with the processor expert and iar projects.

    Thank you again for your support!

    Alessandro

    Like

    • I need to replicate your setup, but I remember that I had worked with someone in the community to improve the timing in tickless idle mode. One issue for sure is that the timing of the LPTMR tick is not accurate: it is possible to miss one tick (1 ms) for each other interrupt. This might be the problem you are facing? Using a higher resolution timer like the SysTick would not show that behaviour.

      Like

      • Hi Erych,

        unfortunately I’m obliged to use the LPTMR as timer because it’s the only one available in LLS (Low Leakage Stop) power mode. Can you link me to the topic in the community where you talk about the tickless mode?

        It could be that one tick is missed for each other interrupt (which is why I tried to use the 32khz clock as reference in order to minimize the error to ~30us for tick), but I’m not 100% sure; it’s not easy to investigate the problem since the LPTMR operates normally also in debug mode, continuing to count.

        I’ll try to investigate deeper: my guess is that in order to minimize the error, the LPTMR should not be stopped and restarted when WFI exits due to an interrupt.

        Like

  9. A few insights that might be of use to people who are trying to use the RTC alarm interrupt in a KLxx processor with FreeRTOS:

    I wanted an interrupt event to happen some seconds in the future. The RTC PE component mainly operates on times using a structure containing year, month, day, hour, minute, second, so it is easier to do time arithmetic by reading and writing the underlying RTC registers. So to set the alarm 10s in the future:
    alarmTime = RTC_PDD_ReadTimeSecondsReg(RTC_BASE_PTR) + 10;
    RTC_PDD_WriteTimeAlarmReg(RTC_BASE_PTR, alarmTime);

    The RTC1_Alarm() event fired as I expected, until I put FreeRTOS into tickless idle mode. Then the interrupt came in, but after 66 seconds, regardless of the delay I used! The explanation is that for me, in tickless idle mode, with no other interrupts happening, FreeRTOS wakes every 66 seconds (overflow interrupt of the LPTMR). At that point the RTC interrupt is seen and runs.

    The next thing to do is to enable the RTC interrupt as a LLWU source in the processor PE component. For the KL05 this is Internal Module 5 in the LLWU settings.

    Finally, you have to clear the RTC interrupt in Cpu_onLLSWakeupINT():

    wakeupReason = Cpu_GetLLSWakeUpFlags();
    if (wakeupReason & LLWU_INT_MODULE5) { /* RTC */
    RTC_PDD_WriteTimeAlarmReg(RTC_BASE_PTR, 0);
    }

    Why don’t I just use the FreeRTOS timers? They use too much RAM on the tiny KL05.

    Like

  10. Pingback: Regaining Debug Access of NXP i.MX RT1064-EVK executing WFI | MCU on Eclipse

  11. Pingback: FreeRTOS: how to End and Restart the Scheduler | MCU on Eclipse

  12. Hi Erich,

    Is your note to “not use the idle hook to enter low power mode if using tickless idle” still relevant? Processor Expert 3.0.0 now generates a function with this header:

    /*
    ** ===================================================================
    ** Callback : free_rtos_vApplicationIdleHook
    ** Description : This callback occurs if the RTOS is idle. This
    ** might be a good place to go into low power mode.
    ** Parameters : None
    ** Returns : Nothing
    ** ===================================================================
    */
    void free_rtos_vApplicationIdleHook(void)

    which suggests that a call to POWER_SYS_SetMode() would be appropriate there, for instance.

    Otherwise, would tickless idle implement similar behavior as going to STOP mode on a KL device?

    Like

    • Yes, it is still relevant: there should be only one place where you enter low power mode and wait for an interrupt.
      With tickless idle mode, I do this in vOnPreSleepProcessing() only. Entering low power mode from the idle hook would screw up the timing.
      And yes, on a KL device you can go into STOP mode in vOnPreSleepProcessing(). If not using tickless idle, you can do this in the idle hook.

      Like

      • Thanks Erich. In KSDK 1.3.0, I see mention in comments of your vOnPreSleepProcessing() function in `port/gcc/port.c` but no implementation or reference to it. I tried to add the entry as per your Github to my .pe file manually, but am getting an error upon refreshing the project. Do you have a separate guide for adding that functionality to Processor Expert?

        Like

Leave a Reply to Juan A Luna Cancel reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Google photo

You are commenting using your Google account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s

This site uses Akismet to reduce spam. Learn how your comment data is processed.