smhk

Handle FreeRTOS tick rollover with 64-bit ticks

In FreeRTOS, with a 32-bit systick running at 1000Hz (i.e. configTICK_RATE_HZ = 1000), the maximum duration it can run before a rollover occurs is ~50 days:1

(2^32-1) / 1000 = 4294967.295 ms
4294967.295 ms / 60 secs / 60 mins / 24 hrs = 49.710269618 days

A rollover (or “overflow”) may require special handling, depending upon what your application does:

  • If your application only need to calculate the difference between two ticks, so long as the difference is not more than ~50 days (assuming 32-bit), then you can rely upon unsigned integer comparison.
  • However, if you need to count the absolute number of ticks beyond the rollover, two possible options are detailed below:

Option A: Increment a 64-bit counter on every tick §

The first option is to implement our own 64-bit tick counter. FreeRTOS provides the callback vApplicationTickHook, which is enabled by setting #define configUSE_TICK_HOOK 1, and allows us to define a callback function that is called on each tick. We can do something like:

static uint64_t systick64 = 0;

void vApplicationTickHook(void)
{
    systick64++;
}

Note that 64-bit data types are generally not atomic, so it will almost certainly be necessary to use a critical section or a mutex, e.g.:

uint64_t systick64_get(void)
{
    uint64_t ret = 0;

    taskENTER_CRITICAL();
    {
        ret = systick64;
    }
    taskEXIT_CRITICAL();

    return ret;
}

Option B: Make use of the private variable xNumOfOverflows§

Though why reinvent the wheel? The FreeRTOS kernel actually already counts the number of overflows. This can be combined with the current 32-bit tick to get a 64-bit tick.

The number of overflows is defined by xNumOfOverflows in tasks.c:

PRIVILEGED_DATA static volatile BaseType_t xNumOfOverflows = ( BaseType_t ) 0;

It is static, so we cannot access it from outside of tasks.c. While vApplicationTickHook is part of the FreeRTOS public API, xNumOfOverflows is not.2 However, by enabling configINCLUDE_FREERTOS_TASK_C_ADDITIONS_H we can add user-defined code into tasks.c.

To do so, first set #define configINCLUDE_FREERTOS_TASK_C_ADDITIONS_H 1 in FreeRTOSConfig.h. Then create two files:

  • The first file must be named freertos_tasks_c_additions.h3, since that is the exact file that FreeRTOS will include in the bottom of tasks.c.4
  • For this example, the second file will be named systick64.h, though the choice is yours. This will be the header file for our new functions.

I created both these files in the same directory as FreeRTOSConfig.h.

Put the declaration for the new function in systick64.h:

systick64.h
#ifndef SYSTICK64_H
#define SYSTICK64_H

#include <stdint.h>

uint64_t xTaskGetTickCount64(void);

#endif  // SYSTICK64_H

Put the definition in freertos_tasks_c_additions.h:

freertos_tasks_c_additions.h
#include "systick64.h"

uint64_t xTaskGetTickCount64(void)
{
    uint32_t overflow = 0;
    uint32_t tick = 0;

    taskENTER_CRITICAL();
    {
        overflow = xNumOfOverflows;
        tick = xTickCount;
    }
    taskEXIT_CRITICAL();

    return (((uint64_t) overflow) << 32) + ((uint64_t) tick);
}

Your application can now do #include "systick64.h" and call the function xTaskGetTickCount64() to get 64-bit ticks!

Conclusion §

Both options are viable, though arguably Option A is better in most cases. While Option B makes use of existing variables, it is relying on undocumented, private features, and is more complex to implement. In comparison, Option A is much simpler, and the overhead of an additional uint64_t counter is unlikely to be prohibitive.


  1. Systicks are either 16-bit or 32-bit, depending upon whether configUSE_16_BIT_TICKS is 1 or 0 respectively. If using a 16-bit systick at 1000Hz, a rollover will occur about once per minute (every 65.535 seconds to be precise). This “feature” can be useful for testing your rollover behaviour. ↩︎

  2. xNumOfOverflows is used internally by the functions vTaskSetTimeoutState and xTaskCheckForTimeOut, so it actually serves a useful purpose, and as such will hopefully remain a part of FreeRTOS in future. ↩︎

  3. Unfortunately there is a mix of plurality, with the macro configINCLUDE_FREERTOS_TASK_C_ADDITIONS_H being TASK singular and the filename freertos_tasks_c_additions.h being tasks plural. Though perhaps this is intentional, since this pattern appears elsewhere, e.g. xTaskGetTickCountFromISR is defined in FreeRTOS/Source/tasks.c plural but is declared in FreeRTOS/Source/include/task.h singular. However, the official FreeRTOS release notes make a mistake by writing freertos_task_c_additions.h singular instead of plural. ↩︎

  4. Although the file extension implies this is a header, personally I think it is better to think of it as a source file, in which case the name freertos_task_c_additions.c would make more sense. Others have made the same observation, but unfortunately that ship has sailed. ↩︎