Quality RTOS & Embedded Software

 Real time embedded FreeRTOS RSS feed 
Quick Start Supported MCUs PDF Books Trace Tools Ecosystem


Loading

Where to put WFI in FreeRTOS?

Posted by marting2015 on 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 HALPWREnterSTOPMode() 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?


Where to put WFI in FreeRTOS?

Posted by xz8987f on 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


Where to put WFI in FreeRTOS?

Posted by marting2015 on 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...


Where to put WFI in FreeRTOS?

Posted by xz8987f on 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


Where to put WFI in FreeRTOS?

Posted by marting2015 on February 16, 2015

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

HALPWREnterSTOPMode(PWRLOWPOWERREGULATORON, PWRSTOPENTRYWFE); // 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 HALPWREnterSTOPMode(), but that didn't work.


Where to put WFI in FreeRTOS?

Posted by marting2015 on 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.


Where to put WFI in FreeRTOS?

Posted by rtel on 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 configPRESLEEPPROCESSING() macro in FreeRTOSConfig.h to do whatever you want. Likewise anything you want to restore after sleeping can be done in configPOSTSLEEPPROCESSING(). 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.


Where to put WFI in FreeRTOS?

Posted by marting2015 on February 16, 2015

Thank you. That was very useful.


Where to put WFI in FreeRTOS?

Posted by zachmetzinger on April 6, 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.


Where to put WFI in FreeRTOS?

Posted by rtel on April 7, 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 portMAXDELAY? When you add 500ms to the current time, does it co-incidentally become portMAXDELAY (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.


Where to put WFI in FreeRTOS?

Posted by zachmetzinger on April 7, 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 / portTICKPERIODMS), correct?

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


Where to put WFI in FreeRTOS?

Posted by rtel on April 7, 2016

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


Where to put WFI in FreeRTOS?

Posted by zachmetzinger on April 7, 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 (R) 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?


Where to put WFI in FreeRTOS?

Posted by rtel on April 8, 2016

I commented out the WFI instruction in vPortSuppressTicksAndSleep():

~~~~ xModifiableIdleTime = xExpectedIdleTime; configPRESLEEPPROCESSING( xModifiableIdleTime ); if( xModifiableIdleTime > 0 ) { // _asm volatile( "dsb" ); // __asm volatile( "wfi" ); // __asm volatile( "isb" ); } configPOSTSLEEP_PROCESSING( xExpectedIdleTime ); ~~~~

...and created a single task:

~~~~ const TickTypet xDelay = pdMSTO_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 ~~~~


Where to put WFI in FreeRTOS?

Posted by zachmetzinger on April 8, 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 portNVICSYSTICKCLK_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/ARMCM4F/port.c as portcm4f.c, and modified it exactly like yours.

I tested 3 cases:

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

2) configUSETICKLESSIDLE == 1 and configSYSTICKCLOCKHZ not defined Result: No abnormal behavior, LED blinks at 1Hz.

3) configUSETICKLESSIDLE == 1 and configSYSTICKCLOCKHZ == 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)


Where to put WFI in FreeRTOS?

Posted by zachmetzinger on April 8, 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

Where to put WFI in FreeRTOS?

Posted by edwards3 on April 8, 2016

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

configSYSTICKCLOCKHZ 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 configSYSTICKCLOCKHZ to 32768 are you actually feeding the SysTick with 32768? Is that was the portNVICSYSTICKCLK_BIT does?


Where to put WFI in FreeRTOS?

Posted by zachmetzinger on April 8, 2016

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


Where to put WFI in FreeRTOS?

Posted by rtel on April 9, 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).


Where to put WFI in FreeRTOS?

Posted by zachmetzinger on April 11, 2016

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


Where to put WFI in FreeRTOS?

Posted by zachmetzinger on April 11, 2016

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).

I also suspected that it was due to the SysTick not incrementing in vPortSuppressTicksAndSleep(). The instructions in there execute far faster, and you'd have to execute 96e6/32768 or ~2930 more instructions before the SysTick current value would be anything other than zero upon exit from that function.


Where to put WFI in FreeRTOS?

Posted by zachmetzinger on April 13, 2016

The device is related to the MAX32620, so that EV Kit should work for duplicating the issue, should you be so inclined.


Where to put WFI in FreeRTOS?

Posted by rtel on April 13, 2016

This file contains an implementation of vPortSupressTicksAndSleep() that works with a 32K clock. Compare its implementation to the default implementation in port.c and see if anything special was done to ensure operation at the slow input clock speed:

https://sourceforge.net/p/freertos/code/HEAD/tree/trunk/FreeRTOS/Demo/CORTEXM4FCEC1302KeilGCC/mainlowpower/lowpowertick_config.c


Where to put WFI in FreeRTOS?

Posted by rtel on April 13, 2016

Just to add to that, specifically I point out this file as it is a 32K DOWN counter, as SysTick is also a down counter.


[ Back to the top ]    [ About FreeRTOS ]    [ Privacy ]    [ Sitemap ]    [ ]


Copyright (C) Amazon Web Services, Inc. or its affiliates. All rights reserved.

Latest News

NXP tweet showing LPC5500 (ARMv8-M Cortex-M33) running FreeRTOS.

Meet Richard Barry and learn about running FreeRTOS on RISC-V at FOSDEM 2019

Version 10.1.1 of the FreeRTOS kernel is available for immediate download. MIT licensed.

View a recording of the "OTA Update Security and Reliability" webinar, presented by TI and AWS.


Careers

FreeRTOS and other embedded software careers at AWS.



FreeRTOS Partners

ARM Connected RTOS partner for all ARM microcontroller cores

Espressif ESP32

IAR Partner

Microchip Premier RTOS Partner

RTOS partner of NXP for all NXP ARM microcontrollers

Renesas

STMicro RTOS partner supporting ARM7, ARM Cortex-M3, ARM Cortex-M4 and ARM Cortex-M0

Texas Instruments MCU Developer Network RTOS partner for ARM and MSP430 microcontrollers

OpenRTOS and SafeRTOS

Xilinx Microblaze and Zynq partner