The Cortex-M3 is an ARM processor core designed for microcontroller applications. One of its key features is support for multitasking through preemptive context switching. This allows multiple tasks or threads to share the M3 core by rapidly switching between their execution contexts.
What is Context Switching?
Context switching refers to the saving and restoring of state as execution switches between threads or tasks. Each thread has its own execution context which includes the values of CPU registers like the program counter, stack pointer, and general purpose registers. When a context switch occurs, the context (register values) of the current thread is saved to memory and the context of the next thread is loaded so execution can resume seamlessly.
Context switching allows multitasking even on a single-core system like the Cortex-M3. The M3 can rapidly switch between threads, executing each for a slice of time before swapping to another. This gives the illusion of parallel execution. In reality, only one thread runs at a time while the others are suspended, but the switching happens so fast it is not noticeable.
Context Switching on Cortex-M3
The Cortex-M3 implements preemptive multitasking with configurable priority levels. This allows high priority threads to preempt lower priority ones. When a thread is preempted, a context switch saves its state so it can resume later.
The M3 has a simplified register file with only 13 general purpose registers. This small context makes context switching very fast. Saving the context only requires saving the general purpose registers and the program counter. This takes just a few CPU cycles.
Context switching on the Cortex-M3 is handled by the NVIC (Nested Vectored Interrupt Controller). When a higher priority thread becomes ready to run, the NVIC triggers an exception which performs the context switch. The exception handler saves the current context and restores the new one before returning to the preempting thread.
The switch between thread contexts is performed by the PendSV (pend supervisor call) exception handler. PendSV has the lowest priority of all exceptions, so a context switch only occurs when no other exception needs handling.
Setting the PENDSVSET bit in the NVIC’s INTCTRL register triggers a pending PendSV exception. This causes the context switch to occur once interrupts are enabled and no higher priority exceptions are pending.
The PendSV handler stores the context of the current thread to its stack, loads the stored context of the next thread, and returns to the preempting thread. This transition is transparent aside from the context switch time.
Context Save and Restore
The context being saved and restored during a context switch consists of the general purpose registers R0-R12, the program counter, LR (link register), and stack pointer (PSP). The PSP is banked for thread mode or handler mode.
The program counter and stack pointer must be preserved to resume execution in the correct thread at the point where it was preempted. The other registers contain the working data for that thread.
The current thread’s context is automatically pushed to the stack by hardware during the exception entry. The PendSV handler then writes it to the thread’s own stack frame for later retrieval. The handler loads the target thread’s previously stored context before returning.
Configuring Context Switching
Using context switching on a Cortex-M3 microcontroller requires configuring the NVIC and PendSV handler in software.
The NVIC needs each thread configured with a priority level. Higher priority threads can preempt lower priority ones. Equal priority threads cannot preempt each other.
Priority levels range from 0 (highest) to 255 (lowest). Level 0 is reserved for exceptions like the SysTick timer. Threads typically start at level 1.
The NVIC’s SHPR registers configure priority grouping and PendSV priority level. PendSV should be lowest priority.
The PendSV handler performs the actual context switching. It needs to be written to save and restore the thread contexts as described earlier. The handler stores context, switches stacks, restores context, and returns to the new thread.
Triggering the PendSV exception at the appropriate times requires integration with the RTOS or thread management code.
Each thread needs its own stack for context storage and handling interrupts. Stacks should be sized appropriately for each thread’s needs.
The stack pointer (PSP) automatically switches when changing threads. This allows transparent handling of interrupts for the active thread.
Benefits of Context Switching
Context switching provides several benefits:
- Allows multithreading on single core systems like Cortex-M3
- Lightweight and fast context switches
- Premption of lower priority threads
- Simple scheduling based on priorities
- Transparent interrupt handling per thread
With only 13 registers to save/restore, context switches take just a few cycles. This enables responsive multitasking even for basic microcontrollers.
Preemption ensures high priority threads aren’t blocked. Simple priority-based scheduling avoids overhead of complex schemes.
Context switching enables efficient multitasking and real-time responsiveness with minimal memory and performance overhead.
Context switching allows the Cortex-M3 microcontroller to handle multithreading efficiently. By rapidly switching contexts, the M3 can provide real-time responsiveness and preemptive scheduling for multiple threads sharing a single core. The small register set of the M3 makes this very fast and lightweight.
With the NVIC and PendSV handler configured correctly, context switching can enable responsive multitasking in deeply embedded applications. The Cortex-M3 architecture is designed to make this process simple and efficient.