Home | Projects | Notes > Real-Time Operating Systems (RTOS) > Context Switching
See Scheduling & Context Switching for processor level details.
When a task executes on the processor it utilizes
Processor core registers (among them PSP is for user tasks, MSP is for kernel)
Task's own stack memory (if a task wants to do any push/pop operations during function call)
State of a task = [Processor core registers] + [Task's stack memory]
Two categories of stack memory utilized during the run-time of a FreeRTOS based application:
Task's private stack (Process stack) - Used when a task does push/pop
Push/pop to/from this stack space is tracked by the PSP register of the ARM Cortex-M processor.
Kernel stack (Main stack) - Used when an ISR (e.g., SysTick handler, PendSV handler) does push/pop
Push/pop to/from this stack space is tracked by the MSP register of the ARM Cortex-M processor.
Context switching is a process of switching out of one task and switching in another taask on the CPU to execute.
In RTOS context switching is taken care of by the scheduler.
In FreeRTOS context switching is taken care of by the PendSV handler defined in port.c
.
Whether context switch should happen or note depends upon the scheduling policy of the scheduler.
Priority-based preemptive scheduler - For every RTOS tick interrupt, the scheduler will compare the priority of the running task with the priorities of the tasks in the Ready list. If there is any Ready task whose priority is higher than the running task then the context switch will occur.
On FreeRTOS you can also trigger context switch manually using the macro taskYIELD()
.
xxxxxxxxxx
21/* task.h */
2
xxxxxxxxxx
81/* portmacro.h */
2/* Scheduler utilities. */
3
4
5 /* Set a PendSV to request a context switch. */ \
6 portNVIC_INT_CTRL_REG = portNVIC_PENDSVSET_BIT; \
7 ...
8}
L6: What it does is identical to what the SysTick does. It pends the PendSV interrupt which means triggering the context switch manually.
Context switch also happens immediately whenever new task unblocks and if its priority is higher than the currently running task.
Before a running task is switched out, the following things have to be taken care of.
Processor core registers R0, R1, R2, R3, R12, LR, PC, xPSR (i.e., stack frame) are saved onto the task's private stack automatically by the processor SysTick interrupt entry sequence.
If context switching is required, the SysTick timer will pend the PendSV exception and PendSV handler will run.
Processor core registers R4-R11 that are not saved by (1) have to be saved manually on the task's private stack memory. (Saving the context) Also, push R14 one more time since it will be necessary to recover the R14 value that will have been corrupted by bl vTaskSwitchContext
subroutine call inside the xPortPendSVHandler()
when the current task is switched back in later.
Q: Doens't R14(LR) get pushed automatically by the processor as an exception exit sequence when the xPortPendSVHandler()
? xPortPendSVHandler()
there's a branch instruction bx r14
which means that we need to make sure that R14 register retains the correct value. Note that the branch instruction bx r14
gets called before the exception exit sequence of PendSV handler.
Save the new top of stack value (PSP) into the first member (pxTopOfStack
) of TCB. This is so when this task is switched back in later, it can restore its context from its stack.
Select the next potential task to execute on the CPU. This is done by vTaskSwitchContext()
implmented in tasks.c
.
xxxxxxxxxx
81/* task.c */
2void vTaskSwitchContext( void )
3{
4 ...
5 /* Select a new task to run using either the generic C or port optimised asm code. */
6 taskSELECT_HIGHEST_PRIORITY_TASK();
7 ...
8}
The macro
taskSELECT_HIGHEST_PRIORITY_TASK()
fetches the pointer to the TCB of the next task to run.
At this time, pxCurrentTCB
will contain the TCB of the next task to run.
First, get the address of the TOS of the task to be switched in. Copy the value of pxTopOfStack
into the PSP register. (PSP initialization for the new task.)
Pop the registers R4-R11, R14(LR) (from the stack to the processor core registers). (Restoring the context)
Note that the R14(LR) gets popped here again! Check out the corresponding part in the Task Switching-Out Procedure section above!
Exception exit - Now PSP is pointing to the start address of the stack frame (R0-R3, R12, LR, PC, xPSR) which will be popped (from the stack to the processor core registers) automatically by the processor during the exception exit sequence.
Context switching code analysis
xxxxxxxxxx
571/* port.c */
2
3void xPortPendSVHandler( void )
4{
5 /* This is a naked function. */
6
7 __asm volatile
8 (
9 " mrs r0, psp \n"
10 " isb \n"
11 " \n"
12 " ldr r3, pxCurrentTCBConst \n"/* Get the location of the current TCB. */
13 " ldr r2, [r3] \n"
14 " \n"
15 " tst r14, #0x10 \n"/* Is the task using the FPU context? If so, push high vfp registers. */
16 " it eq \n"
17 " vstmdbeq r0!, {s16-s31} \n"
18 " \n"
19 " stmdb r0!, {r4-r11, r14} \n"/* Save the core registers. */
20 " str r0, [r2] \n"/* Save the new top of stack into the first member of the TCB. */
21 " \n"
22 " stmdb sp!, {r0, r3} \n"
23 " mov r0, %0 \n"
24 " msr basepri, r0 \n"
25 " dsb \n"
26 " isb \n"
27 " bl vTaskSwitchContext \n"
28 " mov r0, #0 \n"
29 " msr basepri, r0 \n"
30 " ldmia sp!, {r0, r3} \n"
31 " \n"
32 " ldr r1, [r3] \n"/* The first item in pxCurrentTCB is the task top of stack. */
33 " ldr r0, [r1] \n"
34 " \n"
35 " ldmia r0!, {r4-r11, r14} \n"/* Pop the core registers. */
36 " \n"
37 " tst r14, #0x10 \n"/* Is the task using the FPU context? If so, pop the high vfp registers too. */
38 " it eq \n"
39 " vldmiaeq r0!, {s16-s31} \n"
40 " \n"
41 " msr psp, r0 \n"
42 " isb \n"
43 " \n"
44 /* XMC4000 specific errata workaround. */
45
46 " push { r14 } \n"
47 " pop { pc } \n"
48
49
50 " \n"
51 " bx r14 \n"
52 " \n"
53 " .align 4 \n"
54 "pxCurrentTCBConst: .word pxCurrentTCB \n"
55 ::"i" ( configMAX_SYSCALL_INTERRUPT_PRIORITY )
56 );
57}
L9: Copy the PSP value intoa general purpose register r0. (Used in L19)
L19:
stmdb
stands for "Store Multiple Decrement Before (Full Descending)". TOS gets decremented first, and then a value is stored onto stack (this is done for multiple values). Saves the core registers that were not automatically taken care of by the processor during the SysTick exception entry sequence.L20: Stores the PSP in the first element in the TCB. (r2 contains the address of the current TCB.)
L27: Context switch takes place by
vTaskSwitchContext
.xxxxxxxxxx
81/* task.c */
2void vTaskSwitchContext( void )
3{
4...
5/* Select a new task to run using either the generic C or port optimised asm code. */
6taskSELECT_HIGHEST_PRIORITY_TASK();
7...
8}
xxxxxxxxxx
171/* task.c */
234
5
6/* Find the highest priority queue that contains ready tasks. */ \
7while( listLIST_IS_EMPTY( &( pxReadyTasksLists[ uxTopPriority ] ) ) ) \
8{ \
9configASSERT( uxTopPriority ); \
10--uxTopPriority; \
11} \
12\
13/* listGET_OWNER_OF_NEXT_ENTRY indexes through the list, so the tasks of \
14* the same priority get an equal share of the processor time. */ \
15listGET_OWNER_OF_NEXT_ENTRY( pxCurrentTCB, &( pxReadyTasksLists[ uxTopPriority ] ) ); \
16uxTopReadyPriority = uxTopPriority; \
17}
L7: Previous task is moved to the Ready list.
L15:
pxCurrentTCB
gets updated.L32: Start of the new task switching in
L54:
pxCurrentTCB
is a global variable that points to the currently running task's TCB. To check which task is curretnly running, copy this variable's name and add it to the "Expressions" window in the CubeIDE.
What does the "time slice" mean in FreeRTOS?
configTICK_RATE_HZ
)
Should every FreeRTOS project have its own FreeRTOsconfig.h
file?
Is FreeRTOsconfig.h
file dependent on MCU?
If configTICK_RATE_HZ = 125Hz
, what’s the time slice?
If i don't want to use SysTick timer as the kernel tick timer, can i use some other timer peripheral of the MCU?
In FreeRTOS, who triggers the context switching?
When running FreeRTOS on ARM Cortex M based MCUs, Which handler will carry out the context switching operations?
Is SysTick timer a peripheral outside the ARM Cortex-M processor?
If configTICK_RATE_HZ = 1000
Hz, what's the value fed in to the SysTick counter ?
Assume : MCU System Clock is 16Mhz.
Nayak, K. (2022). Mastering RTOS: Hands on FreeRTOS and STM32Fx with Debugging [Video file]. Retrieved from https://www.udemy.com/course/mastering-rtos-hands-on-with-freertos-arduino-and-stm32fx/