Override vPortSuppressTicksAndSleep

I’m trying to override vPortSuppressTicksAndSleep function on my STM32WB55 board.
In order to enter and exit low power mode, I override the PRE and POST sleep processing hooks.
When the STM32WB55’s core clock is reduced from 64MHz to 2MHz (also known as Low Power Run mode in STM32WB55) when an early wakeup interrupt occurs, such as from the RTC, the actual sleep time computation is incorrect and ulCompletedSysTickDecrements receives a wrapped result.

This code in vPortSuppressTicksAndSleep:

            /* Something other than the tick interrupt ended the sleep.
             * Work out how long the sleep lasted rounded to complete tick
             * periods (not the ulReload value which accounted for part
             * ticks). */
            ulCompletedSysTickDecrements = ( xExpectedIdleTime * ulTimerCountsForOneTick ) - portNVIC_SYSTICK_CURRENT_VALUE_REG;

            /* How many complete tick periods passed while the processor
             * was waiting? */
            ulCompleteTickPeriods = ulCompletedSysTickDecrements / ulTimerCountsForOneTick;

            /* The reload value is set to whatever fraction of a single tick
             * period remains. */
            portNVIC_SYSTICK_LOAD_REG = ( ( ulCompleteTickPeriods + 1UL ) * ulTimerCountsForOneTick ) - ulCompletedSysTickDecrements;

xExpectedIdleTime = 498
ulTimerCountsForOneTick = 250 (2MHz sys_clock + divider of 8)
ulReloadValue (calculated at low power enter) = 131600

Meaning - I have a wakeup event after 1088 systick’s (ulReloadValue - portNVIC_SYSTICK_CURRENT_VALUE_REG )

As a result, this line reads:
ulCompletedSysTickDecrements = ( xExpectedIdleTime * ulTimerCountsForOneTick ) - portNVIC_SYSTICK_CURRENT_VALUE_REG;
ulCompletedSysTickDecrements = 250*498 - 130512 = 124500 - 130512 = (-)6012…

later in the code, the LOAD_REG is set with this wrong value.

By substituting the problematic line with the following one, I may avoid this problem:
ulCompletedSysTickDecrements = ulReloadValue - portNVIC_SYSTICK_CURRENT_VALUE_REG;

Any idea what is wrong in my setup?

For clarity, are you using the official FreeRTOS version of vPortSuppressTicksAndSleep()? And are you are supplying your own PRE and POST sleep functions via the hooks provided?

Are you changing the core clock in your PRE and POST functions?

Which version of FreeRTOS are you using?

My FreeRTOS Kernel version is V10.4.3
I inserted the following code at the beginning of the overridden vPortSuppressTicksAndSleep() function to check whether the App permitted entering sleep mode. If not, simply go back without running the sleep code:

// Skip sleep mode if actual power mode is RUN MODE
LP_DRIVER_POWER_MODES actualPowerMode = LOW_POWER_DRIVER_CalcLowestSleepMode(xExpectedIdleTime);    
if (actualPowerMode == POWER_MODE_CORE_RUN) // override code

I’m also override the PRE and POST SLEEP_PROCESSING.
During low power enter, I’m decreasing the sys_clk frequency from 64MHz to 2MHz, and therefor the SYS_TICK is also getting slower by X32 (from 8MHz to 250KHz).

The SYS_TICK frequency reduction is being handled by overriding the following code variables: ulStoppedTimerCompensation and ulTimerCountsForOneTick, with computations for the 250KHz sys_tick:

(ulTimerCountsForOneTick_LP * (xExpectedIdleTime - 1UL));   

if (ulReloadValue > ulStoppedTimerCompensation_LP)
    ulReloadValue -= ulStoppedTimerCompensation_LP;

Thanks for the clarification.

The issue is your changing of the SysTick frequency. The design of the official version of vPortSuppressTicksAndSleep() expects that the SysTick frequency will not change during the execution of that function. The ulReloadValue calculated by your version of the function includes the remaining portion of the tick period currently underway, and the remaining portion is measured in higher-frequency SysTick counts, which is a relatively large number of SysTick counts. (32x larger than it should be in your case, when you switch to the low-speed clock.) That means the ulReloadValue is too high, which ultimately leads to your observed behavior.

If you need to change CPU frequency as part of your low-power strategy, you should use a tick timer that has no dependency on the CPU frequency. In the case of STM32W, you could use LPTIM. You can use lptimTick.c if you want to go that way.

I appreciate your quick response!
Now I understand the issue.
I’ll change the timer to LPTIMER per your recommendation.

Question regarding it:
Assuming I’m using the LSE osc as the source for the timer, the timer tick at 32KHz.
Does this mean that the accuracy of the freeRTOS time will be compromised?
As after wakeup from sleep the OS is trying to calculate the time it missed, and each tick time is larger, as the clock is slower, (and the OS calculation is ±tick because the calculation time can be at the middle of a tick time).


The code in lptimTick.c is designed for timing accuracy. It overcomes the weakness of larger timing granularity in the 32kHz reference by design. (It never stops the LPTIM.) So you will not sacrifice timing accuracy.

Thanks for your support :slight_smile:

1 Like