Where to put WFI in FreeRTOS?

marting2015 wrote on Friday, February 13, 2015:

I am using STM32F4 and want to implement STOP mode, which puts the ARM into Wait For Interrupt (WFI) mode. I thought the place to do this was in the Idle hook… Using tickles mode, and the Idle hook calling HAL_PWR_EnterSTOPMode() was the right approach, but I don’t think the result is what I want - I am still debugging this.

However, FreeRTOS docs say,

This makes the idle hook function an ideal place to put the processor into a low power state - providing an automatic power saving whenever there is no processing to be performed. 

AND,

It is paramount that the idle hook function does not call any API functions that could cause it to block.

Putting the ARM in WFI mode is not really a FreeRTOS “API” function, but the WFI is blocking…

Did I miss FreeRTOS example/documentation on where/how ARM WFI mode shouldbe implemented?

xz8987f wrote on Friday, February 13, 2015:

Hello,
putting the WFI in the idle task is fine. It blocks the ARM core/instructions, but it will be waken up by the next timer/tick interrupt. The ‘blocking’ refers to ‘blocking a task/resource/semaphare’, and is really meant about calling a blocking RTOS API call (e.g. vTaskDelay() would count as blocking too).
I hope that makes sense,
Erich

marting2015 wrote on Friday, February 13, 2015:

Thank you for the clarification, it is what I thought, but there was room for my misunderstanding based on the wording of the docs. I will fight thru this a bit and when I learn whats wrong I will update.

My symptom is the target doesn’t sit in WFI mode, except for very briefly, something (an interrupt) is waking it up, but nothing is running. I am pretty sure it is not the timer interrupt…

xz8987f wrote on Friday, February 13, 2015:

ah, yes, any interrupt will wake it up. I was thinking more about the lower power case, where it is more likely that the tick timer will wake it up. The same principle used for tickless idle mode in FreeRTOS, but the control flow is just a little bit different with adjustment of the tick timer period. I use it like this: http://mcuoneclipse.com/2013/07/06/low-power-with-freertos-tickless-idle-mode/, and in ‘normal’ low power mode I use WFI (or better: the low power mode of the microcontroller as appropriate).

Erich

marting2015 wrote on Monday, February 16, 2015:

I was unable to determine why WFI mode doesn’t stop the processor… The IDLE hook has a call to,

HAL_PWR_EnterSTOPMode(PWR_LOWPOWERREGULATOR_ON, PWR_STOPENTRY_WFE); // also tried _WFI

And I am finding that there are always pending interrupts from the “system” (those below 0),

/******  Cortex-M4 Processor Exceptions Numbers ****************************************************************/
  NonMaskableInt_IRQn         = -14,    /*!< 2 Non Maskable Interrupt                                          */
  MemoryManagement_IRQn       = -12,    /*!< 4 Cortex-M4 Memory Management Interrupt                           */
  BusFault_IRQn               = -11,    /*!< 5 Cortex-M4 Bus Fault Interrupt                                   */
  UsageFault_IRQn             = -10,    /*!< 6 Cortex-M4 Usage Fault Interrupt                                 */
  SVCall_IRQn                 = -5,     /*!< 11 Cortex-M4 SV Call Interrupt                                    */
  DebugMonitor_IRQn           = -4,     /*!< 12 Cortex-M4 Debug Monitor Interrupt                              */
  PendSV_IRQn                 = -2,     /*!< 14 Cortex-M4 Pend SV Interrupt                                    */
  SysTick_IRQn                = -1,     /*!< 15 Cortex-M4 System Tick Interrupt                                */

Specifically -1,-2,-4,-5 and -12. I tried clearing these interrupts myself before the call to HAL_PWR_EnterSTOPMode(), but that didn’t work.

marting2015 wrote on Monday, February 16, 2015:

To be clear, I am using tickles mode. I see the IDLE hook being called at a slow rate in tickles mode, ~2 sec rate. If I disable tickles mode I see the IDLE hook called every 1ms.

rtel wrote on Monday, February 16, 2015:

If you are using tickless idle mode, as you suggest in your first post, then FreeRTOS will call WFI for you - you should not use your own call in the idle hook.

If you look at the implementation of vPortSuppressTicksAndSleep() in port.c you will see a sequence similar to this:


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

If you want to do chip specific things before wfi is called then define the configPRE_SLEEP_PROCESSING() macro in FreeRTOSConfig.h to do whatever you want. Likewise anything you want to restore after sleeping can be done in configPOST_SLEEP_PROCESSING(). That is all you should need to do.

The default vPortSuppressTicksAndSleep() function is limited in the time it can spend sleeping and the low power mode that can be entered because it relies on the SysTick timer (which is present on all Cortex-M chips). If you want a lower power mode to be entered you have to provide your own implementation of vPortSuppressTicksAndSleep() - which is why it is a weakly defined symbol. There are examples of doing just that in the FreeRTOS download.

Regards.

marting2015 wrote on Monday, February 16, 2015:

Thank you. That was very useful.

zachmetzinger wrote on Wednesday, April 06, 2016:

If you want a lower power mode to be entered you have to provide your own implementation of vPortSuppressTicksAndSleep() - which is why it is a weakly defined symbol.

We’ve come across an interesting edge case, which may not be covered by the current tickless-idle logic in 8.2.3. Given that we have one, single task blinking an LED every 500ms, and our FreeRTOSConfig.h contains:

#define configSYSTICK_CLOCK_HZ        ((unsigned long)32768)
#define configTICK_RATE_HZ          ((portTickType)128)

If we short-circuit the code in portable/GCC/ARM_CM4F/port.c:

			if (0) //if( xModifiableIdleTime > 0 )
			{
				__asm volatile( "dsb" );
				__asm volatile( "wfi" );
				__asm volatile( "isb" );
			} 

then we get absurd values for xExpectedIdleTime when entering vPortSuppressTicksAndSleep().

So far, we’ve tracked this down to xNextTaskUnblockTime == portMAX_DELAY, almost as if this value was not updated with the next time our LED blink should happen. If we insert large (2^20 loops of 96MHz) delay loops where the above code normally resides, the system begins to operate normally again.

This might be an edge case exposed by our extremely slow clocking of the SysTick timer. We haven’t quite determined root-cause yet, but any suggestions would be helpful.

rtel wrote on Thursday, April 07, 2016:

If I can understand the circumstances under which you get the odd behaviour a little more I can try it here:

If we short-circuit the code in portable/GCC/ARM_CM4F/port.c:

So effectively you have commented out the bit that will place the CPU into a sleep mode. That will cause the code to continue executing as if it had gone to sleep and then woken up again. It should then determine that it woke up for a reason other than a tick interrupt occurring, and try to work out how long it was actually asleep for, and step the tick accordingly (although it is likely the step would be 0 in nearly all cases).

So far, we’ve tracked this down to xNextTaskUnblockTime == portMAX_DELAY,

This is the bit I’m not clear about.

If you are wanting to unblock (toggle your LED) every 500ms when would xNextTaskUnblockTime become portMAX_DELAY? When you add 500ms to the current time, does it co-incidentally become portMAX_DELAY (0xffffffff)? Or is portMAX_DELAY incorrectly set as the next unblock time because of an earlier error?

Note that, if the next unblock time is calculated to be after the tick count has overflowed, then it will be capped to portMAX_DELAY. That way the system should unblock at the same time that the tick count overflows, so the current and normal overflow delay lists can be switched, and the next unblock time then calculated from what was previously the overflow list.

zachmetzinger wrote on Thursday, April 07, 2016:

Thank you for your reply on this, admittedly, very old thread.

So effectively you have commented out the bit that will place the CPU into a sleep mode. That will cause the code to continue executing as if it had gone to sleep and then woken up again.

Correct. We simulate an immediate wake-up from the WFI (having never entered it). We see the code executing the path which calculates the remaining wake-up time.

If you are wanting to unblock (toggle your LED) every 500ms when would xNextTaskUnblockTime become portMAX_DELAY?

This is the root question, I think. For some reason, we are reaching this bit of code in task.c:

   |1977                                            if( listLIST_IS_EMPTY( pxDelayedTaskList ) != pdFALSE )            |
   |1978                                            {                                                                  |
   |1979                                                    /* The delayed list is empty.  Set xNextTaskUnblockTime    |
   |1980                                                    to the maximum possible value so it is extremely           |
   |1981                                                    unlikely that the                                          |
   |1982                                                    if( xTickCount >= xNextTaskUnblockTime ) test will pass    |
   |1983                                                    next time through. */                                      |
B+>|1984                                                    xNextTaskUnblockTime = portMAX_DELAY;                      |
   |1985                                                    break;                                                     |
   |1986                                            }                                                                  |

I don’t understand how the DelayedTaskList would be empty if my singular task contains this call:

    vTaskDelayUntil(&xLastWakeTime, (500 / portTICK_PERIOD_MS));

The scheduler should either be running my tasks or have it on the DelayedTaskList with xNextTaskUnblockTime == xLastWakeTime + (500 / portTICK_PERIOD_MS), correct?

I’m not familiar with the overflowed versus normal lists, so I’ll do some more digging and reading on this.

rtel wrote on Thursday, April 07, 2016:

I don’t have any hardware here to try this with right now, but will give
it a go tomorrow.

zachmetzinger wrote on Thursday, April 07, 2016:

I’m not familiar with the overflowed versus normal lists, so I’ll do some more digging and reading on this.

I now understand the operation of xDelayedTaskList1 and xDelayedTaskList2, but we aren’t getting enough RTOS ticks (<1000) before failure for this to be a concern.

When you add 500ms to the current time, does it co-incidentally become portMAX_DELAY (0xffffffff)?

Not sure what you mean by this, but the before (D) and after ® vTaskDelayUntil’s xLastWaketime looks like this:

Starting scheduler.
D00000000
R00000047
D00000047
R0000008E
D0000008E
R000000D5
D000000D5
R0000011C
D0000011C

The last 32 xExpectedIdleTimes going into vPortSuppressTicksAndSleep() look like this, starting at index 1 and wrapping back to zero in this array:

$8 = {4294966940, 60, 48, 36, 25, 14, 2, 71, 60, 48, 37, 26, 14, 2, 71, 60, 48, 36, 24, 12, 71, 60, 48, 36, 24, 12, 71, 60, 48, 36, 24, 13}

That last xExpectedIdleTime corresponds to portMAX_DELAY - (0x11C + 0x47):

; 0xffffffff - (0x11c + 0x47)
4294966940 /* 0xfffffe9c */

This looks almost like my task wasn’t re-scheduled on the DelayedTaskList?

rtel wrote on Friday, April 08, 2016:

I commented out the WFI instruction in vPortSuppressTicksAndSleep():

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

…and created a single task:

const TickType_t xDelay = pdMS_TO_TICKS( 500 );

	for( ;; )
	{
		vTaskDelay( xDelay );
		BSP_LedClear( mainTASK_LED );
		vTaskDelay( xDelay );
		BSP_LedSet( mainTASK_LED );
	}

…and found the LED toggled at the expected 500ms.

I then recorded the xExpectedIdleTime passed into vPortSuppressTicksAndSleep() for the first 1000 iterations, the first few recorded values are posted below.

So it appears my system is working as expected. I was using FreeRTOS V9.0.0rc2, although can’t see any changes in this area of the code compared to V8.2.3 - although I only checked the vPortSuppressTicksAndSleep() function, not where the expected idle time is calculated.

Have I recreated the same test as you?
Have you modified the code at all, anywhere, even if you think it is irrelevant?

	xExpectedIdleTimes[0]	long unsigned int	500	0x200065d4	
	xExpectedIdleTimes[1]	long unsigned int	500	0x200065d8	
	xExpectedIdleTimes[2]	long unsigned int	500	0x200065dc	
	xExpectedIdleTimes[3]	long unsigned int	500	0x200065e0	
	xExpectedIdleTimes[4]	long unsigned int	500	0x200065e4	
	xExpectedIdleTimes[5]	long unsigned int	500	0x200065e8	
	xExpectedIdleTimes[6]	long unsigned int	500	0x200065ec	
	xExpectedIdleTimes[7]	long unsigned int	500	0x200065f0	
	xExpectedIdleTimes[8]	long unsigned int	500	0x200065f4	
	xExpectedIdleTimes[9]	long unsigned int	500	0x200065f8	
	xExpectedIdleTimes[10]	long unsigned int	500	0x200065fc	
	xExpectedIdleTimes[11]	long unsigned int	500	0x20006600	
	xExpectedIdleTimes[12]	long unsigned int	500	0x20006604	
	xExpectedIdleTimes[13]	long unsigned int	500	0x20006608	
	xExpectedIdleTimes[14]	long unsigned int	500	0x2000660c	
	xExpectedIdleTimes[15]	long unsigned int	500	0x20006610	
	xExpectedIdleTimes[16]	long unsigned int	500	0x20006614	
	xExpectedIdleTimes[17]	long unsigned int	499	0x20006618	
	xExpectedIdleTimes[18]	long unsigned int	499	0x2000661c	
	xExpectedIdleTimes[19]	long unsigned int	499	0x20006620	
	xExpectedIdleTimes[20]	long unsigned int	499	0x20006624	
	xExpectedIdleTimes[21]	long unsigned int	499	0x20006628	
	xExpectedIdleTimes[22]	long unsigned int	499	0x2000662c	
	xExpectedIdleTimes[23]	long unsigned int	499	0x20006630	
	xExpectedIdleTimes[24]	long unsigned int	499	0x20006634	
	xExpectedIdleTimes[25]	long unsigned int	499	0x20006638	
	xExpectedIdleTimes[26]	long unsigned int	499	0x2000663c	
	xExpectedIdleTimes[27]	long unsigned int	499	0x20006640	
	xExpectedIdleTimes[28]	long unsigned int	499	0x20006644	
	xExpectedIdleTimes[29]	long unsigned int	499	0x20006648	
	xExpectedIdleTimes[30]	long unsigned int	499	0x2000664c	
	xExpectedIdleTimes[31]	long unsigned int	499	0x20006650	
	xExpectedIdleTimes[32]	long unsigned int	499	0x20006654	
	xExpectedIdleTimes[33]	long unsigned int	499	0x20006658	
	xExpectedIdleTimes[34]	long unsigned int	499	0x2000665c	
	xExpectedIdleTimes[35]	long unsigned int	499	0x20006660	
	xExpectedIdleTimes[36]	long unsigned int	498	0x20006664	
	xExpectedIdleTimes[37]	long unsigned int	498	0x20006668	
	xExpectedIdleTimes[38]	long unsigned int	498	0x2000666c	
	xExpectedIdleTimes[39]	long unsigned int	498	0x20006670	
	xExpectedIdleTimes[40]	long unsigned int	498	0x20006674	
	xExpectedIdleTimes[41]	long unsigned int	498	0x20006678	
	xExpectedIdleTimes[42]	long unsigned int	498	0x2000667c	
	xExpectedIdleTimes[43]	long unsigned int	498	0x20006680	
	xExpectedIdleTimes[44]	long unsigned int	498	0x20006684	
	xExpectedIdleTimes[45]	long unsigned int	498	0x20006688	
	xExpectedIdleTimes[46]	long unsigned int	498	0x2000668c	
	xExpectedIdleTimes[47]	long unsigned int	498	0x20006690	
	xExpectedIdleTimes[48]	long unsigned int	498	0x20006694	
	xExpectedIdleTimes[49]	long unsigned int	498	0x20006698	
	xExpectedIdleTimes[50]	long unsigned int	498	0x2000669c	
	xExpectedIdleTimes[51]	long unsigned int	498	0x200066a0	
	xExpectedIdleTimes[84]	long unsigned int	496	0x20006724	
	xExpectedIdleTimes[85]	long unsigned int	496	0x20006728	

zachmetzinger wrote on Friday, April 08, 2016:

Have I recreated the same test as you? Have you modified the code at all, anywhere, even if you think it is irrelevant?

[n.b. - We are using the ARM_CM4F port.c from V8.2.3]

Yes, with one exception, which I think is the trigger:

#define configSYSTICK_CLOCK_HZ        ((unsigned long)32768)

Without this defined, SysTick runs at the CPU clock rate. With it defined, the portNVIC_SYSTICK_CLK_BIT bit is set to zero in vPortSetupTimerInterrupt().

I started out with a fresh project, creating one task exactly like yours (above), made a local copy of GCC/ARM_CM4F/port.c as port_cm4f.c, and modified it exactly like yours.

I tested 3 cases:

  1. configUSE_TICKLESS_IDLE == 0
    Result: As expected, no abnormal behavior. LED blinks at 1Hz

  2. configUSE_TICKLESS_IDLE == 1 and configSYSTICK_CLOCK_HZ not defined
    Result: No abnormal behavior, LED blinks at 1Hz.

  3. configUSE_TICKLESS_IDLE == 1 and configSYSTICK_CLOCK_HZ == 32768
    Result: Abnormal LED “mostly ON” … probably PWM, and xExpectedIdleTime near portMAX_DELAY

Here’s the log of the last 20 xExpectedIdleTimes, logged in vPortSuppressTicksAndSleep() before the check against xMaximumPossibleSuppressedTicks:

999: 0xfe1f7e18
998: 0x00000040
997: 0xfe207e58
996: 0x00000040
995: 0xfe217e98
994: 0x00000040
993: 0xfe227ed8
992: 0x00000040
991: 0xfe237f18
990: 0x00000040
989: 0xfe247f58
988: 0x00000040
987: 0xfe257f98
986: 0x00000040
985: 0xfe267fd8
984: 0x00000040
983: 0xfe278018
982: 0x00000040
981: 0xfe288058
980: 0x00000040

And the first 20:

020: 0xfff6fd3d
019: 0x00000040
018: 0xfff7fd7d
017: 0x00000040
016: 0xfff8fdbd
015: 0x00000040
014: 0xfff9fdfd
013: 0x00000040
012: 0x00000040
011: 0xfffafe7e
010: 0x00000040
009: 0xfffbfebe
008: 0x00000040
007: 0xfffcfefe
006: 0x00000040
005: 0xfffdff3e
004: 0x00000040
003: 0xfffeff7e
002: 0x00000040
001: 0xffffffbe
000: 0x00000040

I think the key is that SysTick is running much, much slower than CPU clock. (In our device, the external SysTick source is the 32768 RTC clock)

zachmetzinger wrote on Friday, April 08, 2016:

Followup, case #2 log output:

(same value up to 999)
020: 0x00000040
019: 0x00000040
018: 0x00000040
017: 0x00000040
016: 0x00000040
015: 0x00000040
014: 0x00000040
013: 0x00000040
012: 0x00000040
011: 0x00000040
010: 0x00000040
009: 0x00000040
008: 0x00000040
007: 0x00000040
006: 0x00000040
005: 0x00000040
004: 0x00000040
003: 0x00000040
002: 0x00000040
001: 0x00000040
000: 0x00000040

edwards3 wrote on Friday, April 08, 2016:

Without this defined, SysTick runs at the CPU clock rate. With it defined, the portNVIC_SYSTICK_CLK_BIT bit is set to zero in vPortSetupTimerInterrupt().

configSYSTICK_CLOCK_HZ only describes the frequency of the systick clock, in case it is different to the main clock, it doesnt set the speed. When you set configSYSTICK_CLOCK_HZ to 32768 are you actually feeding the SysTick with 32768? Is that was the portNVIC_SYSTICK_CLK_BIT does?

zachmetzinger wrote on Friday, April 08, 2016:

Correct, SysTick is being fed with 32768 Hz when portNVIC_SYSTICK_CLK_BIT == 0

rtel wrote on Saturday, April 09, 2016:

I’m not sure I can replicate that as I’m not aware of any chips that
have this capability - older chips seem to fix the SysTick at the core
clock speed, and some newer chips allow the speed to be divided. Which
chip are you using? If I can replicate the circumstance I may find the
solution - which could be perhaps be a miscalculation if the slower
clock count does not increment while the chip would normally be asleep
(some of the chip specific low power schemes we have that do use 32K
clocks do take it into account).

zachmetzinger wrote on Monday, April 11, 2016:

If you send me a PM, I’ll arrange to ship an EV Kit to you w/ docs & SDK.