The link register in the ARM Cortex-M3 processor is a special register that contains the return address when a function call is made. It allows the processor to return to the instruction after the function call once the function has completed execution. The link register plays a crucial role in handling subroutines and function calls efficiently.
Function Calls and the Link Register
When a function call is made on the Cortex-M3 processor, the address of the instruction immediately after the call is stored in the link register. This allows the function to execute its code and then return control back to the calling routine once complete. Here is a simple example: Main: … BL function … function: … BX LR
When the BL (branch with link) instruction is executed, the address of the next instruction (represented by the ellipsis) is stored in the link register (LR). The processor then branches to the function label and executes that code. When the function finishes, it returns using the BX (branch and exchange) instruction with the LR as the operand. This branches back to the calling routine where execution can continue after the original function call.
Registers in ARM Cortex-M3
The Cortex-M3 processor contains 13 general purpose registers (R0-R12), the stack pointer (SP), link register (LR), and program counter (PC). The link register is register R14. It is a standard 32-bit ARM register that can be read from, written to, and manipulated just like the other registers.
Unlike other registers, the processor automatically stores return addresses in LR for function call branches. It does not need to be explicitly loaded or stored for correct program operation. Some key points about the link register:
- Used to hold the return address after a branch instruction
- Updated automatically by the processor on function calls
- Can be explicitly read or written like a general purpose register
- Stores an address pointing to the instruction after a BL or BLX call
Typical Uses of the Link Register
Here are some common ways the link register is used in ARM Cortex-M3 programming:
Returning from Functions
As shown earlier, the primary use of LR is to return from a function call. The BX instruction can return to the calling code by specifying LR as the operand. This jumps back to the instruction that was stored in the link register. BX LR
Nested Function Calls
The link register enables nested function calls to work properly. If one function calls another, the LR will contain the return address back to the first function. This chain can extend as deep as necessary for nested calls. FuncA: … BL FuncB … FuncB: … BX LR
When FuncB returns, it will jump back into FuncA using the LR register.
Branch Table Dispatch
The link register can be used as a general purpose register to hold a branch table index. The table can then be used to jump to code blocks based on the index value. LDR R5, =jumptable MOV LR, #3 // branch table index LDR PC, [R5, LR, LSL #2] // load offset and branch jumptable: .word block0 .word block1 .word block2
This loads the LR with an index, looks up the corresponding offset in the table, and performs an indirect branch to the block of code.
Position Independent Code
LR can be used to call code that is positioned independently in memory. Rather than branching directly to fixed addresses, the code can be relocated and the LR used to indirect call between different portions. ADR LR, code_block // get relative address BLX LR // branch to position independent code code_block: …
LR in ARM / Thumb Interworking
The Cortex-M3 supports both ARM and Thumb instruction sets. The link register handling changes slightly when transitioning between the two in interworking mode:
- BL branches save LR with the full 32-bit ARM address
- BLX branches save LR with the bit 0 set to indicate ARM or Thumb state
- On return, the processor inspects bit 0 to jump to the correct instruction set
This interworking technique relies on the link register to keep track of the current instruction set and branch to the proper one.
Saving / Restoring the Link Register
While the processor handles saving LR on branches, it may also need to be explicitly stored and restored at times. For example: PUSH {LR} // save LR on stack … POP {LR} // restore original LR
This allows a function to preserve the original return address even across other operations that may modify LR. The SP register points to the last pushed value, so a POP will restore the previous LR state.
Modifying the Link Register
Being a general purpose register, the LR can also be directly modified like other registers. This allows you to:
- Set up a custom return address
- Use it as a temporary register in code
- Branch to an alternate location instead of returning
However, directly changing LR can lead to unexpected behavior if not done properly. The compiler expects it to contain valid return addresses, so caution should be exercised when manipulating LR manually.
Accessing LR from C Code
The LR is commonly accessed from assembly code in the Cortex-M3. In C programs, the register is not directly available. However, gcc provides some intrinsic functions to access it: uint32_t lr_val; __asm volatile(“MOV %0, LR” : “=r” (lr_val)); // read LR __asm volatile(“MOV LR, %0” :: “r” (new_val)); // write LR
This inline assembly allows reading or writing the link register value from C code when needed.
Exceptions and the Link Register
On exception entries, the link register is automatically pushed to the stack as part of the exception stack frame. This saves the return address and allows exceptions to nest properly. On the exception return, the LR is restored so that program execution can resume where it left off.
Link Register and Debugging
The link register presents some unique challenges during debugging:
- Single stepping through code may skip over instructions from automated LR handling
- Breakpoints on return instructions may land inside called functions instead
- Stack traces require understanding LR behavior
Debuggers have to take special care to properly save and restore the link register value so that single stepping, breakpoints, and call stack walks operate correctly.
The link register is a key component in ARM Cortex-M3 platforms for handling subroutines, exceptions, and debug operation. Understanding its purpose provides great insight into the inner workings of the processor.
This covers the fundamentals of the link register, its importance in branch handling, and how it can be utilized in your Cortex-M3 code.