As the title, I’m wondering the rationality of this scenario on the real world environment. I run the demo program (Demo/CORTEX_M4F_STM32F407ZG-SK) on STM32F429 DISCO, but compile only one application (Common/Minimal/dynamic.c). Note that configUSE_PORT_OPTIMISED_TASK_SELECTION is 0 and there are six tasks in ready lists, “CNT_INC”, “LIM_INC”, “C_CTRL”, “SUSP_TX”, “SUSP_RX”, and “IDLE”. The scenario** begins when “CNT_INC”, “SUSP_RX”, and “IDLE”, having priority 0, remain in the ready list (pxReadyTasksLists[0]
). “SUSP_RX” is the task that I doubt it is executable under three conditions.
The first condition is “CNT_INC” changes its priority value. When “CNT_INC” is moved to higher level of ready lists, such as pxReadyTasksLists[1]
, the vacancy of “CNT_INC” in pxReadyTasksLists[0]
is replaced by the previous task because pxIndex cannot point to NULL. This decreases the opportunity of the replacing task (“IDLE” in this case) to be selected by the scheduler.
If “CNT_INC” does not change its priority value and no task in ready lists is changed, the scheduler will select the next ready tasks in the following sequence.
“CNT_INC” → “SUSP_RX” → “IDLE” → “CNT_INC” → “SUSP_RX” → “IDLE” → …
However, I observe the scheduler selects the next ready task in the following sequence on the demo program through GDB.
“CNT_INC” → “SUSP_RX” → “CNT_INC” → “IDLE” → “CNT_INC” → “SUSP_RX” → “CNT_INC” → “IDLE” → …
The second condition is the tail-chaining mechanism of ARMv7-M architecture. Given that user tasks can invoke taskYield()
to relinquish the processor. If SysTick generates IRQ*** before PendSV returning to Thread mode, PendSV will not return to Thread mode but Handler mode (SysTick in this case) because of the tail-chaining mechanism. Then, SysTick requests PendSV again which flushes out the first selected task. In other words, the first selected task is selected by the scheduler but not executed by the processor.
Consider the second task sequence above, the third condition is SysTick generates IRQ exactly between “CNT_INC” invokes taskYield()
and PendSV returns to “SUSP_RX”. In other words, SysTick separates the processing time, which has a specific pattern, repeatedly. The repeated pattern is illustrated below.
This condition continues until a delayed task, “SUSP_TX” in this case, is awakened. “SUSP_RX” is supposed to react to “SUSP_TX”, however, it is flushed out because of the second condition.
In summary, the first condition can be observed on real environment and the second condition is based on the documentaries. The third condition, however, seems unreasonable. But I’m wondering it can be programmed coincidentally. Consider the processing of “IDLE” task in the repeated pattern, it can be any other task that long enough for SysTick to separate the processing time like the third condition. For example, the maximum frequency of STM32F429 DISCO is 180MHz and the SysTick rate is 1KHz. If the repeated pattern requires exact 180K cycles to operate, then the third condition might be satisfied. And things might go wrong if the task which is flushed out needs to react to others in time.
Please let me know if I miss some information.
** “LIM_INC” is suspended; “C_CTRL” invokes vTaskDelay()
, and “SUSP_TX” is also delayed because it cannot send more data to a full queue.
*** Because SysTick and PendSV have same exception priority, SysTick sets itself to pending state.