|
NOTE:This is a read only archive of threads posted to the FreeRTOS support forum. Use these archive pages to search previous posts. New forum support threads can be started at the FreeRTOS forums.
FreeRTOS Support Archive
The FreeRTOS support forum can be used for active support both from Amazon Web Services and the community. In return for using our software for free, we request you play fair and do your bit to help others! Sign up for an account and receive notifications of new support topics then help where you can.
This is a read only archive of threads posted to the FreeRTOS support forum. Use these archive pages to search previous posts. New forum support threads can be started at the FreeRTOS forums.
[FreeRTOS Home]
[Live FreeRTOS Forum]
[FAQ]
[Archive Top]
[February 2015 Threads]
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 HAL PWREnterSTOPMode() 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 ****************************************************************/
NonMaskableIntIRQn = -14, /*!< 2 Non Maskable Interrupt */
MemoryManagementIRQn = -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 configPRE SLEEPPROCESSING() macro in FreeRTOSConfig.h to do whatever you want. Likewise anything you want to restore after sleeping can be done in configPOST SLEEPPROCESSING(). 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 portMAX DELAY? 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 / portTICK PERIODMS), 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;
configPRE SLEEPPROCESSING( xModifiableIdleTime );
if( xModifiableIdleTime > 0 )
{
// __asm volatile( “dsb” );
// __asm volatile( “wfi” );
// __asm volatile( “isb” );
}
configPOST SLEEPPROCESSING( xExpectedIdleTime );
~~~~
…and created a single task:
~~~~
const TickType t 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 portNVIC SYSTICKCLK_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 portcm4f.c, and modified it exactly like yours.
I tested 3 cases:
1) configUSE TICKLESSIDLE == 0
Result: As expected, no abnormal behavior. LED blinks at 1Hz
2) configUSE TICKLESSIDLE == 1 and configSYSTICK CLOCKHZ not defined
Result: No abnormal behavior, LED blinks at 1Hz.
3) configUSE TICKLESSIDLE == 1 and configSYSTICK CLOCKHZ == 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().
configSYSTICK CLOCKHZ 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 CLOCKHZ to 32768 are you actually feeding the SysTick with 32768? Is that was the portNVIC SYSTICKCLK_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:
- pxDelayedTaskList has one task on it
- The current thread of execution is in portTASK_FUNCTION (the idle task)
- vTaskSwitchContext is called
- The scheduler is not suspended, so the else case is taken, and this function exits
- xTaskIncrementTick fires
- The following code executes
/* It is time to remove the item from the Blocked state. */
( void ) uxListRemove( &( pxTCB->xGenericListItem ) );
- Next loop around this for ( ;; ) the pxDelayedTaskList is empty
- xNextTaskUnblockTime = portMAX_DELAY;
- xTaskIncrementTick exits
- Thread execution resumes within the idle task about here:
TickType_t xExpectedIdleTime;
/* It is not desirable to suspend then resume the scheduler on */
- Execution enters vPortSuppressTicksAndSleep (ARMCM4 implementation)
a. Note that xExpectedIdleTime is portMAXDELAY at this point
- eTaskConfirmSleepModeStatus() is called for a possible eAbortSleep, but returns eReturn
- 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:
- pxDelayedTaskList has one task on it
Ok.
- The current thread of execution is in portTASK_FUNCTION (the idle task)
Ok.
- vTaskSwitchContext is called
By what? Is this the idle task calling taskYIELD()?
- 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?
- xTaskIncrementTick fires
Presumably this is the tick interrupt executing and calling this function.
- The following code executes
/* It is time to remove the item from the Blocked state. */
( void ) uxListRemove( &( pxTCB->xGenericListItem ) );
Ok.
- Next loop around this for ( ;; ) the pxDelayedTaskList is empty
So now there are no tasks in the Blocked state.
- 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?
- xTaskIncrementTick exits
Ok.
- 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?
- 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( listCURRENT LISTLENGTH( &( 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 configUSE PORTOPTIMISED TASKSELECTION and configEXPECTED IDLETIME BEFORESLEEP 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[tskIDLE PRIORITY+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.
Copyright (C) Amazon Web Services, Inc. or its affiliates. All rights reserved.
|
|