SysTick as an exception
The SysTick, or SYSTICK, is a built-in 24-bit count down system timer presented in every Cortex-M microcontroller. As a consequence, it is also available in every STM32 microcontroller with the same architecture.
The SysTick also acts as an exception, in parallel with, for example, an Interrupt Request (IRQ). The exception, when triggered, is then managed by the Nested Vectored Interrupt Controller (NVIC) and dispatched according to its priority.
The importance of SysTick calibration
This system timer happens to be extremely important due to its goal — to be used by an RTOS or as a portable basic timer. According to the datasheet of the STM32F030R8:
The SysTick calibration value is set to 6000, which gives a reference time base of 1 ms with the SysTick clock set to 6 MHz (max fHCLK / 8).
A reference time of 1ms is a perfect match for RTOS context switching!
Note
The calibration value differs between different Cortex-M, and so does fHCLK.
By default, the STM32F030R8 has its HCLK or HSI (High Speed Internal) clock, set as 8MHz.
Reading from the left to the right, the HSI is then divided by 8 to output the Cortex system timer, the SysTick. This flow results in a timer of 1MHz, or 1us. However, the frequency at which the SysTick runs, can change by:
- Selecting (SW) the PLLCLK or HSE.
- Setting a different HPRE (a prescaler) for SYSCLK.
In a case where SYSCLK is 64MHz, the frequency of the SysTick clock is 8MHz (assuming the prescaler doesn’t change).
HAL_InitTick
Let’s take a first look at a simple HAL implementation and its use of SysTick.
The function HAL_Init
is the first function to run to initialize the ST HAL. Interesting enough, even before the peripherals are initialized, the SysTick is set:
Let’s then take a deeper look inside HAL_InitTick
:
The HAL introduces a bit of bloated code, but essentially, the HAL_InitTick
sets the number of ticks necessary to result in a 1ms SysTick interrupt. Inside HAL_SYSTICK_Config
the priority of the SysTick_IRQn, the SysTick Interrupt Request, is set to the lowest priority possible (in the case of STM32F030R8 is 3, where 0 is the highest priority).
However, the priority is then changed from lowest to highest priority in the next step of HAL_Init
, since TickPriority is 0:
This is quite important! This change of priority means that the SysTick will suspend any Interrupt Service Routine (ISR) from running as long as SysTick is being executed.
Note
The frequency of the system clock might change after the SysTick is first set. For example, the HAL of STM32F030R8 sets first the SysTick at a frequency of 8MHz and follows by changing it to 48MHz later. The priority remains.
Finally, anytime SysTick is triggered, SysTick_Handler
is called:
And HAL_IncTick
increments a milisecond:
Priorities
The priority of the SysTick IRQ is key here. The ST HAL uses the SysTick for its own delays and timeouts, hence the importance of a correctly set prio.
The languague is odd, but the TICK_INT_PRIORITY
is actually set to the highest priority (numerically the lowest). Having a high priority for the SysTick is effective for maintaining precise time tracking, but it can result in frequent interruptions of lower-priority tasks.
In case precise time tracking is a requirement, the setup of another timer could be a solution.
Priorities with freeRTOS
The popular freeRTOS uses SysTick as its tick interrupt and it is recommended to keep the priority low.
This might result in some timing jitter but will ensure the remaining tasks work as expected.
The Cortex-M interrupts priorities always need to be explicitly defined when using freeRTOS API functions:
Cortex-M interrupts default to having a priority value of zero. Zero is the highest possible priority value. Therefore, never leave the priority of an interrupt that uses the interrupt safe RTOS API at its default value.
Another very important aspect of priorities with freeRTOS, is which priority to provide to freeRTOS to create a critical section:
The RTOS interrupt nesting scheme splits the available interrupt priorities into two groups - those that will get masked by RTOS critical sections, and those that are never masked by RTOS critical sections and are therefore always enabled.
The RTOS kernel creates a critical section by writing the configMAX_SYSCALL_INTERRUPT_PRIORITY value into the ARM Cortex-M BASEPRI register. As priority 0 interrupts (the highest priority possible) cannot be masked using BASEPRI, configMAX_SYSCALL_INTERRUPT_PRIORITY must not be set to 0.
In short, configMAX_SYSCALL_INTERRUPT_PRIORITY
cannot be set to zero and should be set to a priority in which any priority below is expected be masked (suspended).
As an example, in a case where there are 4 priority degrees (0, 1, 2 and 3) and configMAX_SYSCALL_INTERRUPT_PRIORITY
is equal to 1, it means any interrupt below priority 1 (2 and 3) will be masked.
In some cases, when porting freeRTOS, a new SysTick_Handler
is given. For example, for a Cortex-M33:
This means the SysTick is only used for freeRTOS and not for the ST HAL anymore!
The function HAL_InitTick
needs to be defined again and must use another timer.
That being said, a common scenario is:
- freeRTOS uses SysTick with a/the low/lowest priority.
- HAL uses another basic timer to manage delays and timeouts
- Another timer might be necessary to achieve precise timing tracking