cancel
Showing results for 
Search instead for 
Did you mean: 

Does anyone realize that the STM32L4 w FreeRtos using CubeMx and enabling Tickless mode is highly flawed.

Kent Swan
Senior

I have been analyzing the flaws in the implementation of the STM32 CUBEMx HAL implementation of FreeRTOS's tickless implementation. In its currently selectable configurations FreeRTOS tickless simply doesn't work as intended -- period.

My test case uses an STM32L476 with CUBEmx 5.1 eclipse plug-in to both the Atollic TrueStudio Ver 9.3 and System Workbench coupled with both STM32Cube FW_L4 V1.13.0 and STM32Cube FW_L4 V1.14.0. Debugging is via an STLink V2 pod. Additionally I use an inexpensive digital logic analyzer to monitoring a number of available gpio ouput pins to be able to monitor wake sleep tick processing in real time in various parts of the code base.

I have determined that:

  1. When FreeRtos is enabled with the cube default set to SysTick management it increments the normal tick cell used by HAL_IncTick() and HAL_GetTick().
  2. When FreeRtos is configured for Tickless mode, it (incorrectly) maintains a second 32 bit tick counter. This it does correctly EXCEPT that now there are two tick counters. The osDelay(ticks) function runs off of the HAL_IncTick() counter BUT that interrupt is suppressed when tickless invokes sleep. When Tickless wakes, it oly updates its own tick counter but does not update the HAL_incTick to match. The result of this is that any osDelay timing is corrupted by the lack of synchronization.
  3. CUBEMx tells you that it want's you to select an alternate timer for SysTick when you are using FreeRtos. This works and the alternate time base will drive the normal timing BUT when you do this, even when you select tickless mode IT WILL NOT WORK. This is because of several fundamental design flaws. The flaw of having two tick counters when you should only have one, The organization of the code bases such that the port.c code is designed for the default SysTick ONLY and has no code to use the alternate time base.

The fundamental code structure for tick management between the CUBEMx generator, the HAL libraries, Free RTOS and the port.c code is flawed because it is not layered correctly. Besides the taboo of having two buried tick accumulation words, it often has data and procedures implemented as static (private to the source module) rather than public. Certain cross layer data access functions are missing, and various default tick management functions which should be marked as __weak so that they can work in the default case or be overridden in a proper layered implentation are not so marked.

Test case experiments to date have shown that this mess is correctable BUT since I need the functionality and I need to use CUBE for project management, I'm going to have to patch the libraries to add the missing cross layer accesses and preserve the CUBE regeration functionality.

I am open to the STM32 CUBE and Library maintanence staff contacting me if they wish conduct a more detailed discussion. If not then <shrug>. Also, while I am open to a knowledgeable discussion on this, I am also sufficiently busy that off topic questions or those asked about things that are obvious when one studies the relevant code will most likely have to go unanswered.

Once I complete my analysis and successful patching I plan to write a definitive paper on the the STM32 FreeRTOS tickless code corrections and publish it here. Even so expect the STM folks to implement any corrections in their own way.

9 REPLIES 9
Jack Peacock_2
Senior III

To start, it is not "incorrect" to use a second timer in tickless mode. The SysTick timer has to be halted if the low power mode (SLEEP or STOP) is used. SysTick has a limited range, far too short when an indeterminate low power time interval can stretch into minutes or hours. Ideally the secondary timer uses some basic period close to the FreeRTOS tick so that it can accurately track tick counts over a much longer period of time compared to SysTick.

When exiting tickless mode the vTaskStepTick() routine in FreeRTOS synchronizes the RTOS tick count to the elapsed time measured by the secondary timer, at which point the secondary timer is halted when SysTick is resumed. If tickless mode is properly implemented there's no need to run two timers simultaneously.

There is some extra work involved in handling partial tick counts, which are cumulative errors if ignored. This condition occurs most often when an IRQ causes the tickless mode to prematurely exit, otherwise the secondary timer forces an exit at a whole tick boundary at the UPDATE event.

In point 3 the "design flaws" you mention are not mistakes. Two timers are used to vary the dynamic range of tick counts under two very different conditions. Yes FreRTOS only uses the SysTick interrupt, but remember the core is halted (SLEEP or STOP) and can't respond to the IRQ. The external hook for tickless mode handles the one case where the alternate timer is used. Scaling tick counters is no different in concept to scaling core frequency to save power. That's why two timers are needed, although the alternate timer is essentially transparent to both FreeRTOS task scheduling and the application since only the tickless API uses it.

The goal in using tickless mode is power reduction when it is known no task will be scheduled for some minimum time period. If low power mode, and halting the core, is not used in tickless mode then there really isn't any benefit. Keeping the SysTick running defeats the purpose of tickless since the interval is reduced to the next SysTick IRQ, milliseconds at most. A secondary time with a slower frequency running in LP mode maintains the tick without waking the core, but still preserves a fast wakeup (in SLEEP mode) to preserve response to realtime events.

Does the HAL implement tickless mode correctly? That's a separate issue from how FreeRTOS works. I don;t use the HAL or Cube so your points on what the HAL does may well be valid, but again it's not a fundamental flaw in the RTOS design. I can say that a tickless implementation not using the HAL, with two timers, works fine. I have it running on L4, L1 and L0 projects (SLEEP low power mode with WFI, fast wakeup required) for the last three years. There is a substantial savings in power on battery systems with this technique.

Jack Peacock

Kent Swan
Senior

Thank you for your detailed response. I concur that FreeRTOS needs to maintain its own timekeeping as it is performing kernel level functions. The issues here are functional collateral tick management dependencies associated with suppressing the time base with FreeRTOS as well as associating the highly suggested alternate time base for FreeRTOS to use and how that timebase interacts with the tickless interrupt suppression function.

  • When using FreeRTOS my preferen is to follow the general rule is that you should invoke FreeRTOS functionality through the platform independent cmcis_os wrapper functions as they are interrupt aware.
  • cmcis_os has no equivalent to the HAL_GetTick function which returns the current tick counter which is use for timing, logging and statistical processing.
  • Both HAL and FreeRTOS's tick cells are static within their source code modules therefore not accessable without an get/set access function.
  • the setter function vTaskStepTick() updates only the FreeRTOS tick counter.
  • HAL does not have an equivalent public setter function to vTaskStepTick
  • When running in tickless mode the normal HAL time base tick counter is thus not updated to be in sync with FreeRTOS so it looses ticks every time sleep mode occurs and HAL_GetTick will fall further and further behind besides compromising any timing function based on knowing the current tick value.

Detailed comparison the past several libraries shows that this is a problem which is being wrestled with. The current L4 14 library attempts to fix the problem by having the SysTick_Handler invoke both  HAL_IncTick() and  osSystickHandler(). This handles keeping the synchronization of the two tick counters only until sleep mode is invoked where upon the SysTick interrupt is suppressed and the two counters fall out of sync by the number of tick slept.

This gets worse when we select an alternative timebase as strongly suggested by Cube. In my test case I selected TIM6 which causes the TIM6_DAC_IRQHandler to be generated with a call to HAL_TIM_IRQHandler(&htim6); This establishes the new system time base which works fine EXCEPT that FreeRTOS no longer works because SysTick is not set up nor is the SysTick_Handler bound to FreeRTOS. Adding a call to  osSystickHandler() will TIM6_DAC_IRQHandler will allow FreeRTOS to work BUT ONLY if you do not enable tickless. The reason for this is that the port.c handler is hard coded to the SysTick cpu periperpheral which has not been initialized and will fail out and the alternate time base is never suppressed nor updated.

Please tell me where my analysis is off base.

Kent Swan
Senior

CORRECTION:

There is a function to return FreeRTOS's current tick value osKernelSysTick(). Changing all application code's uses of HAL_GetTick to osKernelSysTick solves one set of problems BUT it does not solve the basic issue as the HAL libraries are dependant on HAL_GetTick(). The issue remains keeping the HAL tick counter slaved to FreeRTOS's tick counter to maintain tick synchronization.

S.Ma
Principal

Maybe when pulk requests will be functional on github for this code will bring a path to positive and larger contribution pool.

Kent Swan
Senior

A partial solution.

Senario is Cube with FreeRTOS and with tickless selected and using SysTick as the system time base. The SysTick_Handler will be generated in stm32L4xx_it.c. Modify it as shown so that both FreeRTOS tick and HAL tick will maintain sync. Yeah I know it's ugly but it works and it's stable under Cube regen and it keeps the HAL tick values in sync with the FreeRTOS master timing. Still no solution for a non SysTick timebase that cube keeps harping about.

void SysTick_Handler(void)

{

 /* USER CODE BEGIN SysTick_IRQn 0 */

 /* USER CODE END SysTick_IRQn 0 */

 HAL_IncTick(); // this generated code updates the HAL tick value

 osSystickHandler(); // this generated code drives FreeRTOS's tick value as well as the scheduler

 /* USER CODE BEGIN SysTick_IRQn 1 */

extern uint32_t uwTick; // ADD an import a reference to HAL's tick cell

 uwTick = osKernelSysTick();   // ADD a force sync of HAL_GetTick with FreeRTOS internal tick

 /* USER CODE END SysTick_IRQn 1 */

}

turboscrew
Senior III

Also note that FreeRTOS as such has nothing to do with STM. It's just that STM has added support for FreeRTOS in the CubeMX.

Kent Swan
Senior

Re: FreeRTOS and STM32. Well yes it is an independent project BUT its support within the STM32 environment is 'officially' supported at the port.c level AND AS SUCH the commentary about defects is not inappropriate.

CORRECTION:

synchronizing the HAL tick value to the FreeRTOS tick should not be performed until after the scheduler has started. This is shown in the cube SystTic handler snipit below. Again while the following applies to the 10.x implementations of FreeRTOS using SysTick as the time base (see earlier posts here) it is likely applicable to earlier versions.

void SysTick_Handler(void)

{

 /* USER CODE END SysTick_IRQn 0 */

 HAL_IncTick();

 osSystickHandler();

 /* USER CODE BEGIN SysTick_IRQn 1 */

// syn HAL tick to FreeRTOS tick

 if (xTaskGetSchedulerState() != taskSCHEDULER_NOT_STARTED)

     {

    uwTick = osKernelSysTick();

     }

}

Hi Kent, Thanks for your detailed review that made me understand this topic. It would be great if you people can help me to understand it more so that I can fix the things on my side.

With the above fix, your tickless approach worked with out any other issue?

What I am facing is, this update of uwTick  is too late in SysTick_Handler(void) as systick handler will be called when systick expires.

I have another task that is waiting on a thread flag and this thread flag is being set in gpio ISR on gpio interrupt from external peripheral. Upon triggering of this task I use HAL_SPI_Transmit() APIs to send and receive the data from external peripheral. It seems me that this task is triggered before SysTick_Handler() is called and uwTick  is not yet updated. Hence HAL_SPI_Transmit() API blocks as this API depends on Hal timeout function.

If I update uwTick  at very early when vTaskStepTick() is called to compensate the freertos tick. then timing of uwTick remains good. and things work fine.

I know It is bad idea to update uwTick  in task.c as freertos code should be independent but not understanding how to fix this. I am also thinking to use configPOST_SLEEP_PROCESSING() but then all the calculation and code to calculate xTicksToJump will be moved in this function.

Is my understanding correct ? Any suggestion to update uwTick earlier ?

Thanks,

From the following understood requirements:

HAL_tick needs to be high priority so that it can be used in interrupt context

SysTick needs to be low priority as FreeRTOS assumes it can NOT change during interrupt

Tickless sleep needs to turn off the tick so as to be able to actually sleep for long periods of time

I get the following contradiction:

The HAL timer irq can NOT be turned off or an interrupt may busy wait on it, but MUST be turned off in order to sleep for longer than 1ms of the HAL tick.

What resolution do you see happening on this contradiction, My best from research is either A) don;t actually sleep, or B) don't use the HAL.