Context Concepts
Modern software architecture relies heavily on modular design, encapsulation, and abstraction principles. During execution, programs continuously invoke subroutines and return from them, forming a tree-like call structure.
In single-task systems, the CPU must preserve register values and system state before entering a subroutine, then restore them upon return. In multi-task systems, each task maintains its own execution context, and task switching involves migrating the entire execution environment from one process to another—this is the fundamental mechanism enabling multitasking.
When interrupts or exceptions occur, the current program's execution context must be preserved—this is called context saving or protecting the现场 (protecting the scene).
From a broader perspective, all of these scenarios fall under context switching. While this mechanism remains transparent to application developers and end users, it represents critical knowledge for system-level software engineers and OS developers.
Consider a basic function call chain:
void FunctionX();
void FunctionY();
void FunctionZ();
void FunctionX()
{
// ...
FunctionY();
// ...
}
void FunctionY()
{
// ...
FunctionZ();
// ...
}
void FunctionZ()
{
// ...
}
When execution reaches the point where FunctionY calls FunctionZ (with PC at line 18), FunctionY's return address (line 15) currently resides in the return address register. However, invoking FunctionZ will overwrite this register with FunctionZ's return address (line 21). Therefore, the CPU must save the return address before the call.
Additionally, the general-purpose registers contain data used by FunctionY that will be needed after FunctionZ completes. Since FunctionZ also requires registers for computation, the current register values must be preserved so FunctionZ can use them freely.
FunctionZ may also allocate local variables, causing stack pointer modifications—this value must also be saved and restored after FunctionZ completes.
TriCore Context Switching and CSA Architecture
Context Contents
In embedded architectures like ARM, context data typically resides on the stack. During function calls, general-purpose registers and link registers are pushed on to the stack, then popped during returns.
TriCore employs a different approach: contexts are stored in dedicated memory regions called CSA (Context Save Areas). Although CSA resides in RAM like stacks, its separate management from stack memory combined with hardware-based automatic switching provides superior memory efficiency and real-time performance.
TriCore implements two distinct context types: Upper Context and Lower Context. Upper context comprises registers A[10] through A[15], D[8] through D[15], and the Program Status Word (PSW). Lower context includes registers A[2] through A[7], D[0] through D[7], and A[11]. The return address register A[11] appears in both categories. Registers A[0], A[1], A[8], and A[9] function as global address registers and are never stored in context areas.
This division exists because subroutines don't always require full register preservation. In TriCore architecture, Upper Context gets automatically saved by hardware during interrupt or trap entry, after which the subroutine can freely use those registers. When upper context registers prove insufficient, software must manually save Lower Context for the subroutine or trap handler. The compiler handles these decisions during compilation—application code doesn't manage register allocation directly.
Each context segment includes a PCXI (Previous Context Information), also called the Link Word. Since CSA operates as a linked list structure, PCXI functions as a pointer connecting multiple context frames, effectively representing the program's call stack.
Both upper and lower contexts occupy 64 bytes of RAM storage. Memory addresses must be user-allocated with strict 64-byte alignment, and this allocated region becomes the Context Save Area (CSA).
PCXI Register Structure
PCXI serves as the first 4 bytes of each context and acts as a link word, functioning as a linked list pointer to connect nested call frames and display the complete call hierarchy.
Each CPU core maintains a PCXI register pointing to the current task list head—specifically, the most recently saved 64-byte context frame, representing the call stack's top. This context's PCXI field points to the preceding context, establishing the task linked list that encodes function call relationships. Debuggers reconstruct call stacks by parsing these PCXI linkages.
Consider main calling FunctionA (saving context to CSA1), wich then calls FunctionB (saving context to CSA2). The CPU's PCXI points to CSA2, while CSA2's PCXI points to CSA1. Upon FunctionB returning, the system restores CSA2's state to resume FunctionA, and PCXI shifts to CSA1. When FunctionA returns, CSA1 restores main's execution context, completing the unwinding sequence.
The PCXI register structure contains:
- RES: Reserved bits
- PCPN: Previous CPU Priority Number—during interrupt handling, the current CPU interrupt priority (ICR.CCPN) gets stored here for environment preservation and later restoration
- PIE: Previous Interrupt Enable—similarly stores the CPU interrupt enable state (ICR.IE) for context preservation
- UL: Upper/Lower Context Tag—1 indicates upper context, 0 indicates lower context
- PCXO, PCXS: PCXI memory address offsets, combined with the PCXI Segment number to form the linked list pointer, referencing the last-saved context segment (call stack top)
The address calculation follows: ((PCXS << 12) | (PCXO << 6))
Beyond PCXI, each CPU includes FCX (Free CSA List Head Pointer) and LCX (Free CSA List Limit Pointer) registers. FCX points to the head of the available CSA linked list—the first usable CSA segment. When the system depletes available CSA regions, an exception occurs. LCX marks the CSA allocation boundary.
For single-task systems, PCXI, FCX, and the in-memory PCXI fields collectively maintain two linked lists: an available list and the task list containing saved contexts. When context preservation is required, the system extracts one CSA segment from the available list's head and appends it to the task list's head. During context restoration, the task list's head CSA segment returns to the available list's head.
Multi-task systems require each independent thread to maintain its own task list pointer—this falls under OS responsibility during task switches. The available list remains singular, with CSA link words maintaining the list structure.
Context Save Area (CSA)
CSA regions occupy dedicated RAM sections, visible in linker configuration files. Each CSA segment spans 64 bytes with the lower 6 address bits forced to zero, mandating strict 64-byte alignment.
System initialization configures PCXI, FCX, and LCX registers while establishing the CSA linked list structure. The following initialization routine demonstrates the setup process:
IFX_SSW_INLINE void Ifx_Ssw_initCSA(uint32 *csaStart, uint32 *csaEnd)
{
uint32 idx;
uint32 nextCxiValue = 0U;
uint32 *prevCsa = NULL;
uint32 *currCsa = csaStart;
uint32 csaCount = ((uint32)csaEnd - (uint32)csaStart) / 64U;
for (idx = 0U; idx < csaCount; idx++)
{
nextCxiValue = ((uint32)((uint32)currCsa & ((uint32)0xFU << 28U)) >> 12U) | \
((uint32)((uint32)currCsa & ((uint32)0xFFFFU << 6U)) >> 6U);
if (idx == 0U)
{
Ifx_Ssw_MTCR(CPU_FCX, nextCxiValue);
}
else
{
*prevCsa = nextCxiValue;
}
if (idx == (csaCount - 3U))
{
Ifx_Ssw_MTCR(CPU_LCX, nextCxiValue);
}
prevCsa = (uint32 *)currCsa;
currCsa += IFX_SSW_CSA_SIZE;
}
*prevCsa = 0U;
Ifx_Ssw_DSYNC();
}
Debug inspection of compiled binaries reveals CSA allocation spanning 0x70039c00 to 0x7003bc00—8KB total. Post-initialization, FCX points to 0x70039C00 (the first CSA segment), and LCX indicates 0x7003BB40, leaving a 192-byte safety margin of 3 reserved CSA segments. PCXI remains zero until the first context save occurs.
Memory inspection shows the FCX register referencing the first CSA segment, with each segment's PCXI pointing to the next available segment, forming the available linked list.
Context Switching Operations
Context saving and restoration activate through events (interrupts, exceptions, or CALL instructions) or explicit operations. The following table summarizes triggering scenarios:
Interrupt, Exception, Function Call: Upper context gets automatically saved by hardware. For interrupts and exceptions, compilers insert RFE (Return from Exception) instructions to restore upper context, release the CSA segment, and load the return address into PC. Function calls similarly insert RET instructions performing equivalent operations.
BISR, SVLCX Instructions: These save lower context through compiler analysis of register usage or manual inline assembly invocation. Lower context preservation occurs when upper context registers prove insufficient—typical for lengthy interrupt handlers or nested subroutine calls. Restoration uses RSLCX instructions to reload register values.
STLCX, STUCX Instructions: These operations save general-purpose registers to memory (non-CSA regions) using CSA format, with LDLCX and LDUCX restoring values to registers. System registers like PCXI and FCX remain unaffected. These instructions perform temporary register preservation without state switching.
Summary
TriCore context frames occupy 64-byte storage segments containing general-purpose registers and PSW contents, divided into upper and lower categories.
Context segments reside in RAM regions called CSA, organized as linked lists. Two lists exist: the available list and the task list (which also represents the system's call stack). When interrupts, exceptions, or function calls occur, the system extracts a segment from the available list to prepend to the task list; returns dequeue the task list head and return it to the available list.
CPU handles these lists through PCXI, FCX, and LCX registers. PCXI references the most recently saved context segment (task list head); FCX points to the next available CSA segment (available list head); LCX establishes the usage boundary, triggering exceptions when FCX reaches LCX.
Context switching operations include hardware-automated upper context preservation during interrupts, exceptions, and calls, with compiler or user-directed lower context operations available as needed.
CSA memory allocation requires careful consideration. Embedded systems face memory constraints, so CSA size must balance availability against resource limits. Each function call level consumes one 64-byte CSA segment—maximum call depth analysis plus safety margins guide allocation. Multi-task OS environments compound this since each thread maintains independent call stacks; all tasks' CSA consumption accumulates.
Practical Examples
Context Switching in Single-Task Systems
Single-task systems operate as single-threaded environments where call stacks remain linear except during interrupts. The following example traces CSA usage through a function call sequence.
Initialization Phase
Before any function invocation, no context preservation has occurred. PCXI points to null, and FCX references the first CSA segment (CSA1). The initial segment at 0x70039C00 serves as the available list head, pointing to the subsequent CSA segment.
When no context preservation has occurred, the CSA state appears as follows:
First-Level Call
Executing a CALL instruction to EcuM_Init triggers automatic upper context preservation alongside the branch.
After this CALL instruction, register state changes:
Converting the PCXI value reveals it now points to CSA1, while FCX references CSA2. This makes CSA2 the available list head. The CPU's PCXI contains UL=1, indicating the referenced context is upper context (CALL instructions automatically save upper context).
Memory inspection shows CSA1's PCXI points to null (the task list contains only one element), while CSA2 still references CSA3. The CSA layout becomes:
Second-Level Call
Continuing execution so EcuM_Init calls Mcu_Init reveals:
PCXI has advanced one CSA segment, now pointing to CSA2, while FCX references CSA3.
Memory examination shows the task list head (CSA2) now points to CSA1, while CSA3 continues pointing to CSA4. The updated layout:
At this stage, the context linked list structure becomes evident—each nested call extends the task list. Within CSA2, the saved A11 return address register (0x800268FE) represents EcuM_Init's return location, while Mcu_Init's return address resides in the current A11 register. Other general-purpose registers maintain corresponding mappings.
Second-Level Call Return
Placing a breakpoint at Mcu_Init's final line reveals the RET instruction the compiler inserted, paired with the earlier CALL. This instruction restores upper context and returns to the previous call site.
After executing RET, PCXI reverts one CSA segment to CSA1, and FCX shifts back to CSA2.
Memory shows CSA2's PCXI (70E72) pointing to CSA3, with CSA1 pointing to null:
This state mirrors the pre-second-level-call configuration—the CSA segment consumed by the second-level call gets returned to the available list.
First-Level Call Return
Similarly, executing the RET instruction at EcuM_Init's conclusion restores context and returns to the caller. The state reverts to the initial configuration.
This completes the context switching flow for single-task system calls. Multi-task systems follow identical principles, differing only in that the OS must switch the three system registers (PCXI, FCX, LCX) to each Task's saved state during context switches.
The internal hardware logic during CSA switching involves implementation details transparent to software developers.
Context Switching in Interrupt Service Routines
Entering Interrupt Handlers
Since interrupts preempt normal task execution, placing a breakpoint at the interrupt antry point (using ADC3 here as an example) reveals CPU executing a nested subroutine call before the interrupt occurred.
Observing CSA-related registers shows PCXI pointing to CSA2 and FCX referencing CSA3. The combined layout shows main's nested call consuming CSA1, followed by hardware saving CSA2 upon interrupt entry:
Saving Lower Context
Lengthy interrupt service routines require the compiler to execute SVLCX to preserve lower context.
After this instruction, both PCXI and FCX have advanced. The PCXI PCPN field equals 120, reflecting the current interrupt priority captured during context preservation. Since SVLCX saves lower context, the PCXI UL field equals 0:
The subsequent CALL instruction entering the interrupt service routine follows normal program flow.
Restoring Lower Context
After completing the interrupt service routine, the compiler inserts the RSLCX instruction (paired with SVLCX) for lower context restoration.
Execution restores CSA to its pre-SVLCX state.
Exiting Interrupt Handlers
Upon routine completion, the RFE instruction exits the interrupt, restoring hardware-saved upper context, returning to the pre-interrupt address, and updating system state accordingly. The interrupt handling completes, and normal program execution resumes.
References
- Infineon-AURIX_TC3xx_Architecture_vol1-UserManual-v01_00-EN.pdf