cancel
Showing results for 
Search instead for 
Did you mean: 

CubeMX 1.19 cmsis_os2.c SysTick_Handler breaks FreeRTOS if configUSE_TICKLESS_IDLE == 1

sdtbb
Associate II

I am having a "how did this ever work" moment. Maybe I found a (horrible) bug, or maybe I'm overlooking something.

In tickless idle mode FreeRTOS uses the COUNTFLAG in the SysTick SYST_CSR to determine if WFI exited due to the systick, and then calculates how many RTOS ticks have elapsed:

			configPRE_SLEEP_PROCESSING( xModifiableIdleTime );
			if( xModifiableIdleTime > 0 )
			{
				__asm volatile( "dsb" ::: "memory" );
				__asm volatile( "wfi" );
				__asm volatile( "isb" );
			}
			configPOST_SLEEP_PROCESSING( xExpectedIdleTime );

			/* Re-enable interrupts to allow the interrupt that brought the MCU
			out of sleep mode to execute immediately.  see comments above
			__disable_interrupt() call above. */
			__asm volatile( "cpsie i" ::: "memory" );
			__asm volatile( "dsb" );
			__asm volatile( "isb" );

			/* Disable interrupts again because the clock is about to be stopped
			and interrupts that execute while the clock is stopped will increase
			any slippage between the time maintained by the RTOS and calendar
			time. */
			__asm volatile( "cpsid i" ::: "memory" );
			__asm volatile( "dsb" );
			__asm volatile( "isb" );

			/* Disable the SysTick clock without reading the
			portNVIC_SYSTICK_CTRL_REG register to ensure the
			portNVIC_SYSTICK_COUNT_FLAG_BIT is not cleared if it is set.  Again,
			the time the SysTick is stopped for is accounted for as best it can
			be, but using the tickless mode will inevitably result in some tiny
			drift of the time maintained by the kernel with respect to calendar
			time*/
			portNVIC_SYSTICK_CTRL_REG = ( portNVIC_SYSTICK_CLK_BIT | portNVIC_SYSTICK_INT_BIT );

			/* Determine if the SysTick clock has already counted to zero and
			been set back to the current reload value (the reload back being
			correct for the entire expected idle time) or if the SysTick is yet
			to count to zero (in which case an interrupt other than the SysTick
			must have brought the system out of sleep mode). */
			if( ( portNVIC_SYSTICK_CTRL_REG & portNVIC_SYSTICK_COUNT_FLAG_BIT ) != 0 )     <<<<<< Checks the count flag here

 

However, notice that FreeRTOS briefly enables and disables interrupts to allow whatever pended interrupt caused wfi to exit to be serviced.  Usually this will be the systick interrupt.

cmsis_os2.c provides a "helpful" wrapper for the systick interrupt - and the very first thing it does is to clear the systick count flag bit:

#if (USE_CUSTOM_SYSTICK_HANDLER_IMPLEMENTATION == 0)
void SysTick_Handler (void) {
  /* Clear overflow flag */
  SysTick->CTRL;              <<<<<<<<<<<<<<<<< this is not good

  if (xTaskGetSchedulerState() != taskSCHEDULER_NOT_STARTED) {
    /* Call tick handler */
    xPortSysTickHandler();
  }
}
#endif

As far as I can tell this means that the default code generated by CubeMX for the case of configUSE_TICKLESS_IDLE == 1 cannot work because as soon as FreeRTOS finds the conditions where it can use tickless idle then the FreeRTOS tick will stop incrementing.  This is because when the systick wakes the processor from wfi  FreeRTOS will think that a different interrupt woke the processor and if you follow that code path, and realizing that the systick counter has just rolled over (since it caused the wake) FreeRTOS calculates that only a fraction of a tick has elapsed.

Like I said, this is so fundamental that this is causing me a "how can this have ever worked or am I missing something blindingly obvious" moment.

Thanks

 

0 REPLIES 0