Context Switching

When µC/OS-III decides to run a different task (see Scheduling), it saves the current task’s context, which typically consists of the CPU registers, onto the current task’s stack and restores the context of the new task and resumes execution of that task. This process is called a Context Switch.

Context switching adds overhead. The more registers a CPU has, the higher the overhead. The time required to perform a context switch is generally determined by how many registers must be saved and restored by the CPU.

The context switch code is generally part of a processor’s port of µC/OS-III. A port is the code needed to adapt µC/OS-III to the desired processor. This code is placed in special C and assembly language files: os_cpu.h, os_cpu_c.c and os_cpu_a.asm. Porting uC-OS-III provides more details on the steps needed to port µC/OS-III to different CPU architectures.

In this chapter, we will discuss the context switching process in generic terms using a fictitious CPU as shown in Figure 8-1. Our fictitious CPU contains 16 integer registers (R0 to R15), a separate ISR stack pointer, and a separate status register (SR). Every register is 32 bits wide and each of the 16 integer registers can hold either data or an address. The program counter (or instruction pointer) is R15 and there are two separate stack pointers labeled R14 and R14’. R14 represents a task stack pointer (TSP), and R14’ represents an ISR stack pointer (ISP). The CPU automatically switches to the ISR stack when servicing an exception or interrupt. The task stack is accessible from an ISR (i.e., we can push and pop elements onto the task stack when in an ISR), and the interrupt stack is also accessible from a task.

Figure - Fictitious CPU


In µC/OS-III, the stack frame for a ready task is always setup to look as if an interrupt has just occurred and all processor registers were saved onto it. Tasks enter the ready state upon creation and thus their stack frames are pre-initialized by software in a similar manner. Using our fictitious CPU, we’ll assume that a stack frame for a task that is ready to be restored is shown in Figure 8-2.

The task stack pointer points to the last register saved onto the task’s stack. The program counter (PC or R15) and status register (SR) are the first registers saved onto the stack. In fact, these are saved automatically by the CPU when an exception or interrupt occurs (assuming interrupts are enabled) while the other registers are pushed onto the stack by software in the exception handler. The stack pointer (SP or R14) is not actually saved on the stack but instead is saved in the task’s OS_TCB.

The interrupt stack pointer points to the current top-of-stack for the interrupt stack, which is a different memory area. When an ISR executes, the processor uses R14’ as the stack pointer for function calls and local arguments.

Figure - CPU register stacking order of ready task


There are two types of context switches: one performed from a task and another from an ISR. The task level context switch is implemented by the code in OSCtxSw(), which is actually invoked by the macro OS_TASK_SW(). A macro is used as there are many ways to invoke OSCtxSw() such as software interrupts, trap instructions, or simply calling the function.

The ISR context switch is implemented by OSIntCtxSw(). The code for both functions is typically written in assembly language and is found in a file called os_cpu_a.asm.