Quality RTOS & Embedded Software

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




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.


Where to put WFI in FreeRTOS?

Posted by zachmetzinger on May 3, 2016

I think we've identified the issue, but it merits a closer look. It appears the problem occurs during the following sequence of events:

  1. pxDelayedTaskList has one task on it
  2. The current thread of execution is in portTASK_FUNCTION (the idle task)
  3. vTaskSwitchContext is called
  4. The scheduler is not suspended, so the else case is taken, and this function exits
  5. xTaskIncrementTick fires
  6. The following code executes /* It is time to remove the item from the Blocked state. */ ( void ) uxListRemove( &( pxTCB->xGenericListItem ) );
  7. Next loop around this for ( ;; ) the pxDelayedTaskList is empty
  8. xNextTaskUnblockTime = portMAX_DELAY;
  9. xTaskIncrementTick exits
  10. Thread execution resumes within the idle task about here: TickType_t xExpectedIdleTime; /* It is not desirable to suspend then resume the scheduler on */
  11. Execution enters vPortSuppressTicksAndSleep (ARMCM4 implementation)
    a. Note that xExpectedIdleTime is portMAXDELAY at this point
  12. eTaskConfirmSleepModeStatus() is called for a possible eAbortSleep, but returns eReturn
  13. Device attempts to sleep for xMaximumPossibleSuppressedTicks <- INCORRECT

So, we think the reason that the failure occurs is that eTaskConfirmSleepModeStatus() doesn't check pxReadyTasksLists. Does this seem reasonable?


Where to put WFI in FreeRTOS?

Posted by zachmetzinger on May 3, 2016

This addition to eTaskConfirmSleepModeStatus appears to fix the issue we were tracking down.

	/* First, check that there are no tasks on the pxReadyTasksLists higher than tskIDLE_PRIORITY */
	for( uxPriority = ( UBaseType_t ) (tskIDLE_PRIORITY+1); uxPriority < ( UBaseType_t ) configMAX_PRIORITIES; uxPriority++ )
	{
	  if (listLIST_IS_EMPTY(&( pxReadyTasksLists[ uxPriority ] )) != pdTRUE) {
	    eReturn = eAbortSleep;
	    break;
	  }
	}
 

Where to put WFI in FreeRTOS?

Posted by rtel on May 4, 2016

I would need to follow through your scenario in more detail, with the code in front of me, as I'm not following it exactly just reading it in isolation - but some initial comments.

eTaskConfirmSleepModeStatus() is only checking that no tasks have been made ready, and no interrupts have executed, since the scheduler was suspended. If one of these events did occur since the scheduler was suspended then the task could not have been placed into a ready list (because the scheduler was suspended) so the additional test you propose should not be needed.

If there is an obscure race condition you have found somewhere, and the test is needed, then inspecting the highest priority ready state variable (which might be a priority value or a bit mask, depending on the setting of configUSEPORTOPTIMISEDTASKSELECTION) would be a faster way of finding the information rather than looping through all the ready lists - which would not be desirable due to the time it would take to execute.


Where to put WFI in FreeRTOS?

Posted by rtel on May 4, 2016

More detail:

  1. pxDelayedTaskList has one task on it

Ok.

  1. The current thread of execution is in portTASK_FUNCTION (the idle task)

Ok.

  1. vTaskSwitchContext is called

By what? Is this the idle task calling taskYIELD()?

  1. The scheduler is not suspended, so the else case is taken, and this function exits

....so presumably when it exits it returns to the Idle task, so no context switch was performed. Is that correct?

  1. xTaskIncrementTick fires

Presumably this is the tick interrupt executing and calling this function.

  1. The following code executes /* It is time to remove the item from the Blocked state. */ ( void ) uxListRemove( &( pxTCB->xGenericListItem ) );

Ok.

  1. Next loop around this for ( ;; ) the pxDelayedTaskList is empty

So now there are no tasks in the Blocked state.

  1. xNextTaskUnblockTime = portMAX_DELAY;

Presumably because there are no tasks in the Blocked state. At this point do you just have the Idle task and the task that was unblocked in the tick interrupt executing?

  1. xTaskIncrementTick exits

Ok.

  1. Thread execution resumes within the idle task about here: TickType_t xExpectedIdleTime; /* It is not desirable to suspend then resume the scheduler on */

If the Idle task is still running is it right that the task that was unblocked by the tick interrupt is also running at the idle priority?

  1. Execution enters vPortSuppressTicksAndSleep (ARMCM4 implementation)
    a. Note that xExpectedIdleTime is portMAXDELAY at this point

From your description so far, vPortSuppressTicksAndSleep() should never even be called because prvGetExpectedIdleTime() will return 0 if the the other task is running at the idle priority, see the line....

else if( listCURRENTLISTLENGTH( &( pxReadyTasksLists[ tskIDLE_PRIORITY ] ) ) > 1 )

....or if the task that unblocked has a priority above the idle priority (in which case the idle task should not even be executing):

So it seems this is the part where the problem is.

What do you have the configUSEPORTOPTIMISEDTASKSELECTION and configEXPECTEDIDLETIMEBEFORESLEEP set to?


Where to put WFI in FreeRTOS?

Posted by zachmetzinger on May 4, 2016

vTaskSwitchContext is called

By what? Is this the idle task calling taskYIELD()?

Yes, in the (configUSE_PREEMPTION == 0) block, taskYIELD() is being called.

The scheduler is not suspended, so the else case is taken, and this function exits

....so presumably when it exits it returns to the Idle task, so no context switch was performed. Is that correct?

Correct. According to my trace, the next bit of code that runs after vTaskSwitchContext is xTaskIncrementTick, which then resumes execution within the idle task before TickType_t xExpectedIdleTime.

  xNextTaskUnblockTime = portMAX_DELAY;

Presumably because there are no tasks in the Blocked state. At this point do you just have the Idle task and the task that was unblocked in the tick interrupt executing?

Yes, only the idle task and the task just unblocked.

If the Idle task is still running is it right that the task that was unblocked by the tick interrupt is also running at the idle priority?

The "other" task, which is not the idle task, is called 'Task1'. It has tskIDLE_PRIORITY+1.

...or if the task that unblocked has a priority above the idle priority (in which case the idle task should not even be executing):

Task1 was suspended for 500ms, which expired, and it was put on the pxReadyTasksLists[tskIDLEPRIORITY+1] list. However, the scheduler did not switch to it, so the idle task carried along and tried to sleep. This only happens when configSYSTICKCLOCK_HZ is defined -- it has the value 32768. If this is not defined and the CPU clock is also SYSTICK clock, this condition does not happen. I believe it to be a race condition exacerbated by the slow SysTick clock.

What do you have the configUSEPORTOPTIMISEDTASKSELECTION and configEXPECTEDIDLETIMEBEFORESLEEP set to?

I'm using the default GCC/ARM_CM4F/portmacro.h defines.

/* Architecture specific optimisations. */
#ifndef configUSE_PORT_OPTIMISED_TASK_SELECTION
	#define configUSE_PORT_OPTIMISED_TASK_SELECTION 1
#endif
#ifndef configEXPECTED_IDLE_TIME_BEFORE_SLEEP
	#define configEXPECTED_IDLE_TIME_BEFORE_SLEEP 2
#endif

Where to put WFI in FreeRTOS?

Posted by rtel on May 5, 2016

Task1 was suspended for 500ms, which expired, and it was put on the pxReadyTasksLists[tskIDLE_PRIORITY+1] list. However, the scheduler did not switch to it, so the idle task carried along and tried to sleep

Why did the scheduler not switch to it? If the description is accurate then that would seem to be the cause of your problem - and the code afterwards is only doing the wrong thing because the idle task should not be running anyway.


Where to put WFI in FreeRTOS?

Posted by zachmetzinger on May 5, 2016

Why did the scheduler not switch to it?

Unfortunately, I am not an expert in the internals of FreeRTOS.

I think we have a work-around which we can provide to our customers, and it seems harmless enough if it is a "should never occur" case. We'll take a deeper look at it when more time is available.


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




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

Latest News

FreeRTOS kernel V10 is available for immediate download. Now MIT licensed.


FreeRTOS Partners

ARM Connected RTOS partner for all ARM microcontroller cores

IAR Partner

Microchip Premier RTOS Partner

RTOS partner of NXP for all NXP ARM microcontrollers

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