3756 views|13 replies

5213

Posts

239

Resources
The OP
 

[FreeRTOS check-in station 3 is open] Task status and switching, closing time is August 20 [Copy link]

 

Activity Overview: Click here to view (including activity encouragement and activity learning content)

Check-in start and end time for this site: August 18th - August 20th (3 days)

Check-in tasks:

1. Read Cruelfox's third article: FreeRTOS Learning Notes (3) Task Status and Switching

2. Reply to this thread: In FreeRTOS task programs, the xTaskDelay() function is often used to achieve the purpose of delayed execution. If xTaskDelay(20) is called in a task, and you want to wait for 20 time units, but the delay operation exceeds 25 time units, please analyze the possible reasons?

FreeRTOS Learning Notes (3) Task Status and Switching

For your convenience, I copied cruelfox's " FreeRTOS Learning Notes (3) Task Status and Switching " here~

FreeRTOS tasks have the following states:

run Running
Ready Ready
block Blocked
Suspend Suspended


  The states other than the running state are collectively referred to as the non-running state. Because FreeRTOS is designed for a single CPU system, at any time, only one task can be in the running state, even if it seems that there are multiple tasks running at the same time - this is just the effect of multiple tasks switching constantly. When a task switches from the running state to the non-running state, the execution scene - the CPU registers are saved in the task's private stack; when it returns to the running state, the previously saved registers are restored from the stack. This is the most basic function of task scheduling.



  Ready task

  The ready state of a FreeRTOS task means that the task is not currently being executed, but can be executed at any time. When the next task switching opportunity comes, FreeRTOS will select the task with the highest priority from the list of ready tasks and switch it to the running state.
  Task priority is a property of a task. FreeRTOS uses an integer to represent the priority, with the lowest priority being 0 and the highest being the value defined by the configMAX_PRIORITIES macro minus 1. A priority needs to be specified when creating a task, and it can also be changed after creation through the vTaskPrioritySet() function. The meaning of priority is that when a task is in the ready state, as long as there is a task with a higher priority than it in the ready state, it will not get the opportunity to execute.
  When there are two or more tasks of the same priority in the ready state, they will take turns to get the opportunity to execute (but the execution time is not guaranteed to be evenly distributed), and the system will not favor any one of them.

  Timing of task switching

  FreeRTOS will inevitably switch tasks in the following situations:
(A) The running task calls vTaskSuspend() to suspend itself.
(B) The running task calls the delay function and requires waiting for a period of time before executing.
(C) The running task needs to wait for a synchronization event that has not occurred.
  Because the current task actively suspends execution, FreeRTOS needs to switch it to the suspended or blocked state, and then select the highest priority task from the list of ready tasks to execute. If there are no other tasks that need to be executed, the system's own Idle task with the lowest priority will also be executed. There is also a special case that
(D) the running task calls taskYIELD() to actively request to switch tasks.
  At this time, if there is a task with the same or higher priority in the ready task list, a task switch will occur, otherwise the current task will be returned to continue execution.

  If there is configUSE_PREEMPITON=1 in the FreeRTOS configuration, preemptive task scheduling will be performed. The meaning of preemption is that when the newly added tasks in the task ready list (including new tasks, tasks resumed from the suspended state, and tasks exited from the blocked state) and the tasks whose priorities have been modified have a higher priority than the currently running task, they will be immediately switched to the higher priority task for execution. The previously running task does not know when its execution is suspended, so it is called "preempted".
  Preemptive scheduling ensures that the highest priority task will immediately obtain processor resources once the running conditions are ripe. However, if there is more than one task with the highest priority, they will not preempt each other by default, unless configUSE_TIME_SLICING=1 is configured, and FreeRTOS performs time slice management. After enabling time slice management, ready tasks of the same priority are executed in turn, and each task executes at most one fixed cycle each time, basically evenly distributing processor resources. To summarize, under preemptive scheduling, the timing of task switching is:
(E) The running task is preempted by a higher priority task.
(F) The running task is preempted by a task of the same priority at the end of a time slice.

  Let's borrow an example from the manual. When there is no preemptive scheduling, it is like the following figure: Task3 is not blocked and needs to actively hand over control to switch to the ready Task1 and Task2.

  While preemptive scheduling is as follows: Always immediately schedule to the ready task with a higher priority.

  The preemptive scheduling of the same priority task in turn with time slices is enabled: Task2 and Idle task are both of the lowest priority, and they are executed in turn, and they must switch at the end of the time slice.


  Implementation of task switching

  Analyze the details of the implementation and pick a simple one to start with: vTaskSuspend() function, which is used to adjust a task state to the suspended state.

  1. void vTaskSuspend( TaskHandle_t xTaskToSuspend)
  2. {
  3. TCB_t *pxTCB;
  4. taskENTER_CRITICAL();
  5. pxTCB = prvGetTCBFromHandle( xTaskToSuspend );
  6. if( uxListRemove( &( pxTCB->xStateListItem ) ) == ( UBaseType_t ) 0 )
  7. {
  8. taskRESET_READY_PRIORITY( pxTCB->uxPriority );
  9. }
  10. if( listLIST_ITEM_CONTAINER( &( pxTCB->xEventListItem ) ) != NULL )
  11. {
  12. ( void ) uxListRemove( &( pxTCB->xEventListItem ) );
  13. }
  14. vListInsertEnd( &xSuspendedTaskList, &( pxTCB->xStateListItem ) );
  15. taskEXIT_CRITICAL();
  16. if( xSchedulerRunning != pdFALSE )
  17. {
  18. taskENTER_CRITICAL();
  19. prvResetNextTaskUnblockTime();
  20. taskEXIT_CRITICAL();
  21. }
  22. if( pxTCB == pxCurrentTCB )
  23. {
  24. if( xSchedulerRunning != pdFALSE )
  25. {
  26. portYIELD_WITHIN_API();
  27. }
  28. else
  29. {
  30. if( listCURRENT_LIST_LENGTH( &xSuspendedTaskList ) == uxCurrentNumberOfTasks )
  31. {
  32. pxCurrentTCB = NULL;
  33. }
  34. else
  35. {
  36. vTaskSwitchContext();
  37. }
  38. }
  39. }
  40. }
Copy code


  This function has three parts: the first is to delete the xStateListItem member in the task TCB structure from the state list, and then insert it into the suspended task list; and delete the xEventListItem member from the list. The second part is to execute prvResetNextTaskUnblockTime() if the scheduler is working. The third part is to perform task scheduling if the current task is suspended: portYIELD_WITHIN_API() or vTaskSwitchContext() switches to the unsuspended task when the scheduler is not working.
  Ignore the operations related to the two critical sections taskENTER_CRITICAL() and taskEXIT_CRITICAL(). It is a bit strange that the uxListRemove() function only requires one parameter. You need to check list.c and list.h to understand that the FreeRTOS list is implemented using a doubly linked list data structure to understand: from the xStateListItem list member, you can index the entire list. When the state list to which the task belongs is empty, do you need to execute taskRESET_READY_PRIORITY() to reset the ready state of this priority level? It's hard to understand, I have to trace the code... It turns out that each priority has a list of ready tasks, not just one list for all ready tasks as I thought. The following lists are globally defined in tasks.c:

  1. /* Lists for ready and blocked tasks. ------------------*/
  2. PRIVILEGED_DATA static List_t pxReadyTasksLists[ configMAX_PRIORITIES ];
  3. PRIVILEGED_DATA static List_t xDelayedTaskList1;
  4. PRIVILEGED_DATA static List_t xDelayedTaskList2;
  5. PRIVILEGED_DATA static List_t * volatile pxDelayedTaskList;
  6. PRIVILEGED_DATA static List_t * volatile pxOverflowDelayedTaskList;
  7. PRIVILEGED_DATA static List_t xPendingReadyList;
Copy code


  It can be guessed that FreeRTOS tasks must be in a certain list (running tasks are in the ready list); when task scheduling occurs, the list must be adjusted. The
  second step of vTastSuspend() is to reset the global variable xNextTaskUnblockTime, because the suspended task may be in a blocked state waiting for a delay, and its influence needs to be eliminated. The
  third step is task scheduling, which is implemented by the scheduler using portYIELD() (otherwise the scheduler is not running...this will be analyzed later). And portYIELD() is implemented in portmacro.h for the ARM Cortex-m3 platform as follows:

  1. #define portYIELD() \
  2. { \
  3. portNVIC_INT_CTRL_REG = portNVIC_PENDSVSET_BIT; \
  4. __asm volatile( "dsb" ); \
  5. __asm volatile( "isb" ); \
  6. }
Copy code



  What this macro does is to set the PendSV status bit, which will cause the PendSV exception, and then implement the task switch in the PendSV ISR handler. Now we can boldly guess that FreeRTOS uses the PendSV exception handler to perform task switching. Continue to analyze this handler, which is an assembly implementation function in port.c:

  1. void xPortPendSVHandler(void)
  2. {
  3. __asm volatile (
  4. " mrs r0, psp \n"
  5. "isb\n"
  6. " ldr r3, pxCurrentTCBConst \n"
  7. " ldr r2, [r3] \n"
  8. " stmdb r0!, {r4-r11} \n"
  9. " str r0, [r2] \n"
  10. " stmdb sp!, {r3, r14} \n"
  11. " mov r0, %0 \n"
  12. " msr basepri, r0 \n"
  13. " bl vTaskSwitchContext \n"
  14. " mov r0, #0 \n"
  15. " msr basepri, r0 \n"
  16. " ldmia sp!, {r3, r14} \n"
  17. " ldr r1, [r3] \n"
  18. " ldr r0, [r1] \n"
  19. " ldmia r0!, {r4-r11} \n"
  20. " msr psp, r0 \n"
  21. "isb\n"
  22. " bx r14 \n"
  23. " .align 4 \n"
  24. "pxCurrentTCBConst: .word pxCurrentTCB \n"
  25. ::"i"(configMAX_SYSCALL_INTERRUPT_PRIORITY) );
  26. }
Copy code


  Because before entering the interrupt, 8 registers are saved in the PSP stack: r0, r1, r2, r3, r12, LR, PC, xPSR, r0 to r3 can be used without any worries. First read the current task TCB address to r2, save the registers from r4 to r11 to the task stack, and then save the new task stack top (here is the r0 register) in the TCB (the first member of the TCB is the stack top). Next, call the vTaskSwitchContext() function to switch the context, then get the current task TCB address to r1, read the task stack top from the TCB, pop the register from the stack, set the PSP stack register, and finally exit the exception handling.
  Because the PSP register has been changed, the stack before entering the exception and the stack after exiting the exception belong to different tasks. From the perspective of a task, it seems that nothing has changed after calling the portYIELD() function. If the vTaskSwitchContext() function does nothing, the above exception handling will not do any substantial operations and will still return to the current task.
  Let's see how context switching is implemented: In fact, the key is
   taskSELECT_HIGHEST_PRIORITY_TASK();
  This is another macro, defined as

  1. {
  2. UBaseType_t uxTopPriority = uxTopReadyPriority;
  3. while( listLIST_IS_EMPTY( &( pxReadyTasksLists[ uxTopPriority ] ) ) )
  4. {
  5. --uxTopPriority;
  6. }
  7. listGET_OWNER_OF_NEXT_ENTRY( pxCurrentTCB, &( pxReadyTasksLists[ uxTopPriority ] ) );
  8. uxTopReadyPriority = uxTopPriority;
  9. }
Copy code


  It is not difficult to understand. It searches downward from the highest priority until it finds a ready task list that is not empty. It selects a task from it, sets the current TCB address to its TCB address, and then saves the priority of the global ready state. So the main thing vTaskSwitchContext() does is to select the task to be executed and set the current pxCurrentTCB global variable.

  How does the time slice work?

  Since the time slice is needed, there must be hardware timer interrupt support. FreeRTOS calls this interrupt a tick interrupt, and what timer is used to implement it depends on the platform. After checking the source code, I confirmed that for ARM Cortex-m0/m3, Systick Timer is used, which is a timer provided by the ARM core instead of a timer device on the APB bus. This makes full use of the hardware and is compatible with MCUs from different manufacturers.
  The timer interrupt function is relatively simple

  1. void xPortSysTickHandler(void)
  2. {
  3. portDISABLE_INTERRUPTS();
  4. {
  5. if( xTaskIncrementTick() != pdFALSE )
  6. {
  7. portNVIC_INT_CTRL_REG = portNVIC_PENDSVSET_BIT;
  8. }
  9. }
  10. portENABLE_INTERRUPTS();
  11. }
Copy code


  The main thing is to call xTaskIncrementTick() to count and determine whether task switching is needed. In addition to the rotation of ready tasks with the same priority, there are also cases where switching is required, such as using xTaskDelay() and other functions to apply for delay expiration, and waiting for the event timeout to expire. The latter two are in the DelayedTaskList list, and I will not list the implementation details here.

The questions on this site seem to be simpler than the previous one. Come on!
After reading, you are welcome to reply to the questions on this site: In FreeRTOS task programs, the xTaskDelay() function is often used to achieve the purpose of delayed execution. If xTaskDelay(20) is called in a task, and you want to wait for 20 time units, but the delay operation exceeds 25 time units, please analyze the possible reasons?

This post is from MCU
Add and join groups EEWorld service account EEWorld subscription account Automotive development circle

Latest reply

Since the timer uses the SysTick Timer instead of the hardware timer resource, it is still a software-implemented timer. When a task with a higher priority than the timer is being executed, the delay time will be longer than the set time.   Details Published on 2020-8-20 22:41
 

1w

Posts

16

Resources
2
 
This post was last edited by ddllxxrr on 2020-8-19 11:10

In FreeRTOS task programs, the xTaskDelay() function is often used to achieve the purpose of delayed execution. If xTaskDelay(20) is called in a task, and you want to wait for 20 time units, but the delay operation exceeds 25 time units, please analyze the possible reasons?

A: I think it is waiting for a semaphore, and this semaphore is executed by a lower-level task, which has been executed for more than 25 time units before giving this semaphore. Or there is a higher priority task that preempted the execution of several units in these 20 time units, or an interrupt affected its time.

This post is from MCU
 
Personal signaturehttp://shop34182318.taobao.com/
https://shop436095304.taobao.com/?spm=a230r.7195193.1997079397.37.69fe60dfT705yr
 

122

Posts

0

Resources
3
 

Question: In FreeRTOS task programs, the xTaskDelay() function is often used to achieve the purpose of delayed execution. If xTaskDelay(20) is called in a task, and you want to wait for 20 time units, but the delay operation exceeds 25 time units, please analyze the possible reasons?

Answer: First of all, the 20 time units of xTaskDelay(20) are definitely consumed, no doubt about it. The main thing is to analyze who consumes the extra 5 time units.

1. Preempted by a higher priority task, that is, when the current task waits for 19 time units, a higher priority task preempts the resource and runs for 6 time units before releasing the resource

2. Preempted by the same priority level. When the current task has to wait for 19 time units, there is a task with the same priority level that is in the ready state. It can definitely take over the resources and run for 6 time units before releasing the resources.

3. Being interrupted, whether it is naked or not, the rtos hardware interrupt always has the highest priority. During normal programming, we will not do a lot of things in the interrupt, but it does not rule out that some people write application code in the interrupt, or even write delay functions

This post is from MCU
 
 
 

6

Posts

0

Resources
4
 

vTaskDelay is a relative delay function. When a function calls vTaskDelay(), it will enter a blocking state. When it enters a blocking state, other tasks will be executed.
1. In the case of blocking, the high-priority task waiting for a semaphore or message queue must reach the specified timeout before it can be preempted.
2. When a task is executed, if an interruption occurs or it is interrupted by a high-priority task, the execution time of the task will be extended. When vTaskDelay() is executed, the relative time will not change, so the total time becomes longer.

This post is from MCU
 
 
 

73

Posts

0

Resources
5
 

The extra 5 delay times may be because when the delay expires and the system enters the waiting state, it finds that a higher priority task has preempted the CPU. The CPU will be used only after the higher priority task exits.

This post is from MCU
 
 
 

5

Posts

0

Resources
6
 

When using xTaskDelay(), a running task will switch tasks. The original task becomes a blocked task, and the system will switch to the highest priority in the ready task for execution. I think the reasons for the delay are as follows:

  • When there is no preemptive scheduling, it may be because the hardware interrupt processing time is too long. Preemptive scheduling may be preempted by a higher priority task, causing the CPU to be occupied and the delay to time out.
  • On the other hand, due to the use of time slice rotation scheduling, it is preempted by the same priority, but it needs to be switched at the end of the time slice. Of course, it may also be due to interruption

This post is from MCU
 
 
 

144

Posts

0

Resources
7
 

vTaskDelay() is a relative delay function. Each time a task is delayed, it is calculated from the moment when the delay function vTaskDelay() is called. The delay is relative to this moment. If an interrupt occurs during the execution of task A, the task execution cycle will be longer.
(1) High-level task preemption

(2) When Task A and Task B, or even more tasks, are executed simultaneously, the delay function of Task A will also be delayed.

(3) The time lost during scheduling of the task itself, such as blocking or suspension until execution, can also cause delays.

This post is from MCU
 
 
 

33

Posts

0

Resources
8
 

1. This delay is a relative time, not an absolute time; 2. The task is ready, and other high-priority tasks have run for 5 time units;

This post is from MCU
 
 
 

282

Posts

2

Resources
9
 

The extra 5 time units are the code running for 5 time units before calling vTaskDelay(), because the delay calculation of vTaskDelay() is relative to the starting position of the call.

This post is from MCU
 
 
 

27

Posts

0

Resources
10
 

Because executing xTaskDelay will trigger task scheduling. If it is preemptive, it may be preempted by a high-priority task. If it is time slice rotation, the delay time may exceed the remaining time slice time occupied by this task, and it may be rotated by the same-level task. It may also trigger task scheduling and be scheduled to other tasks (or low-priority tasks). It is also possible that interrupts and other events occupy 5 units of time.

This post is from MCU
 
 
 

280

Posts

0

Resources
11
 

During the delay, another high-priority task was executed, taking up 6 more time units.

This post is from MCU
 
 
 

419

Posts

1

Resources
12
 

1. There is a task with a higher priority than the original task, which is ready to start after the original task is ready to start, and the execution time is 5 time units;

2. There is a ready task with the same priority as the original task, and the different tasks are interspersed with 5 time units;

3. Hardware problems lead to inaccurate timing

This post is from MCU
 
Personal signature君应有语,渺万里层云,千山暮雪,知向谁边?
 
 

493

Posts

1

Resources
13
 

Since the timer uses the SysTick Timer instead of the hardware timer resource, it is still a software-implemented timer. When a task with a higher priority than the timer is being executed, the delay time will be longer than the set time.

This post is from MCU
 
 
 

5213

Posts

239

Resources
14
 

Related interpretation by cruelfox: https://en.eeworld.com/bbs/thread-1141050-1-1.html

This post is from MCU
Add and join groups EEWorld service account EEWorld subscription account Automotive development circle
 
 
 

Just looking around
Find a datasheet?

EEWorld Datasheet Technical Support

Copyright © 2005-2024 EEWORLD.com.cn, Inc. All rights reserved 京B2-20211791 京ICP备10001474号-1 电信业务审批[2006]字第258号函 京公网安备 11010802033920号
快速回复 返回顶部 Return list