Potential xNumOfOverflows bug when tickless idle is enabled

Hello!

I am currently working with both FreeRTOS 9 and 10, and have uncovered something that I suspect is a bug concerning the global variable “xNumOfOverflows” defined and managed by tasks.c.

During tick increments in:

BaseType_t xTaskIncrementTick( void )

I notice that there is very explicit handling for overflow detection.

if( xConstTickCount == ( TickType_t ) 0U ) /*lint !e774 'if' does not always evaluate to false as it is looking for an overflow. */
{
    taskSWITCH_DELAYED_LISTS();
}

This macro appears to be the only location that increments the overflow counter; however, it’s not the only place where the overflow counter is used.

#define taskSWITCH_DELAYED_LISTS()																	\
{																									\
	List_t *pxTemp;																					\
																									\
	/* The delayed tasks list should be empty when the lists are switched. */						\
	configASSERT( ( listLIST_IS_EMPTY( pxDelayedTaskList ) ) );										\
																									\
	pxTemp = pxDelayedTaskList;																		\
	pxDelayedTaskList = pxOverflowDelayedTaskList;													\
	pxOverflowDelayedTaskList = pxTemp;																\
	xNumOfOverflows++;																				\
	prvResetNextTaskUnblockTime();																	\
}

I cannot find evidence that tickless idle is taking the same care to detect and increment xNumOfOverflows.

void vTaskStepTick( const TickType_t xTicksToJump )
{
    /* Correct the tick count value after a period during which the tick
    was suppressed. Note this does *not* call the tick hook function for
    each stepped tick. */
    configASSERT( ( xTickCount + xTicksToJump ) <= xNextTaskUnblockTime );
    xTickCount += xTicksToJump;
    traceINCREASE_TICK_COUNT( xTicksToJump );
}

It looks like xTickCount is just naively incrementing by ticks.

Other than what may be going on in the kernel due to xNumOfOverflows potentially being inaccurate, it also calls in the question of whether the following function is safe to use.

void vTaskInternalSetTimeOutState( TimeOut_t * const pxTimeOut )
{
   /* For internal use only as it does not use a critical section. */
   pxTimeOut->xOverflowCount = xNumOfOverflows;
   pxTimeOut->xTimeOnEntering = xTickCount;
}

Have I uncovered a situation where overflow is incorrectly being handled when in tickless idle? If not, can some explanation be provided to explain what I may be missing?

I’ve not double checked the code so tell me if you think this is wrong.

There is a function called something like prvGetExpectedIdleTime() that is used by tickless idle to determine the time at which the processor knows it must next perform some processing. For example, if three tasks are in the blocked state, the first will unblock in 100ms, the second in 1000ms and the third in 10000ms, then prvGetExpectedIdleTime() will return 100ms as that is the closest time it knows it has something to do - and the processor will be configured to wake from sleep mode in 100ms. This is the worst case time - an interrupt can always bring the processor out of sleep mode at an earlier time. However, the “next known unblock time” is already capped to the time at which the tick count overflows, so prvGetExpectedIdleTime() can never return a value that would cause vTaskStepTick() to overflow the tick count. It would be interesting to see if all corner cases were tested here - but use of configEXPECTED_IDLE_TIME_BEFORE_SLEEP - which is the minimum sleep period allowed - should prevent vTaskStepTick() ending up exactly on the overflow value.

Thank you for interesting advice. It is very helpful as we also troubled whether tick overflow should be considered.

I understand that users need to implement to exit tickless mode once by next timing of unbloked or overflow ticks, which can be obtained by the prvGetExpectedIdleTime(), in porting or application layer.

BTW, could you please tell me whether the above specifications (shown in bold) are explicitly mentioned in the official manuals on this site? If it exists, I would like to provide it to my related persons.
Honestly, unlike unblocked timing, I could not easily imagine and not find out mentions about overflow.

You do not need to do that - the kernel does that. Would you please elaborate the problem you are facing?

Yes, I understand that’s the intent of the kernel.
However, we found we needed to be careful in certain situations dependent on a microcontroller.
Let me explain the case of using RX130 (*1) provided by Renesas.

RX130 uses CMT (*2) for system timer of kernel. And more, CMT is stopped in Software Standby Mode which is one of the low power consumption mode (*3).
It means RX130 can not know when tick overflows occur in Software standby Mode if no measures are taken. RX130 may keep in sleeping even when overflows occur. It is reason we should consider something about tick overflows in porting or application layer in tickless idle mode.

The root problem of above situation is that system timer stops in tickless mode because of depending on microcontroller (peripheral features of low power and timers). So I understand that is none of kernel’s business and users should solve that.
About a concrete solution in case of RX130, I think users should implement the follows:

  1. In the Idle task, get remained ticks until unblocked or overflow by the prvGetExpectedIdleTime().
  2. With using configPRE_SLEEP_PROCESSING, occur the alarm interrupt with binary count mode of RTC (*4) when remained ticks elapse. Then exit sleeping by triggered the alarm interrupt.

Above situation was my facing problem.
In my conclusion, I think the followings are important to understand how to implement tickless mode in FreeRTOS:

  • The prvGetExpectedIdleTime() obtains remained ticks until unblocked or overflow.
  • Users need to exit tick less mode once by somthing ways until this remained ticks elapse.
  • So the vTaskStepTick() doesn’t need to consider tick overflows because of above two specifications.

If there are descriptions about that in FreeRTOS official manual, it will be helpful for implementation of such as RX130’s low power support.


*1: Porting codes (with CC-RX compiler) are here: Kernel V11.1.0 porting for RX130
and device’s manual is here: RX130 Hardware manual
*2: See Chapter 23 in the manual.
*3: See Table 11.2 in Chapter 11 in the manual. CMT is classified as “Peripheral modules” in this table.
*4: See Chapter 24 in the manual. RTC is the only timer which can be used in Software standby Mode (*3).

If the macro portSUPPRESS_TICKS_AND_SLEEP( xExpectedIdleTime ) is implemented correctly to only enter the sleep mode for xExpectedIdleTime amount of time, it should never happen because the FreeRTOS-Kernel takes care of overflow calculation. In other words, if the next overflow will happen 60 ticks later, xExpectedIdleTime passed to portSUPPRESS_TICKS_AND_SLEEP will never be more than 60.

prvGetExpectedIdleTime is an internal function but we can still update its description here to say that time may be that of a next tick overflow.

OK. I understand the kernel increases overflow counter when the just overflowed timing by the (generally) periodic process of tick increment; the taskSWITCH_DELAYED_LISTS() which is called when tick counter equals to 0 in the xTaskIncrementTick().

That’s good! Thank you for your consideration.

This PR addresses it - Update documentation of prvGetExpectedIdleTime by aggarg · Pull Request #1061 · FreeRTOS/FreeRTOS-Kernel · GitHub.

2 Likes