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.h
3, since that is the exact file that FreeRTOS will include in the bottom oftasks.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.
Systicks are either 16-bit or 32-bit, depending upon whether
configUSE_16_BIT_TICKS
is1
or0
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. ↩︎xNumOfOverflows
is used internally by the functionsvTaskSetTimeoutState
andxTaskCheckForTimeOut
, so it actually serves a useful purpose, and as such will hopefully remain a part of FreeRTOS in future. ↩︎Unfortunately there is a mix of plurality, with the macro
configINCLUDE_FREERTOS_TASK_C_ADDITIONS_H
beingTASK
singular and the filenamefreertos_tasks_c_additions.h
beingtasks
plural. Though perhaps this is intentional, since this pattern appears elsewhere, e.g.xTaskGetTickCountFromISR
is defined inFreeRTOS/Source/tasks.c
plural but is declared inFreeRTOS/Source/include/task.h
singular. However, the official FreeRTOS release notes make a mistake by writingfreertos_task_c_additions.h
singular instead of plural. ↩︎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. ↩︎