ARM Cortex-M processors utilize branch instructions to alter the flow of a program by jumping to a new location in code. Branching allows for non-linear program execution, which is essential for implementing loops, conditional statements, and function calls.
Types of Branch Instructions
There are several types of branch instructions available in the ARM Thumb and Thumb-2 instruction sets used by Cortex-M CPUs:
- B – Branch causes an unconditional branch to a target address
- BL – Branch with Link calls a subroutine at a target address and stores the return address in the Link Register (LR)
- BX – Branch and Exchange branches to an address specified in a register
- BLX – Branch with Link and Exchange calls a subroutine indicated by a register and stores the return address in LR
Conditional branching can be achieved using the conditional instructions Bcond, BLcond, BXcond, and BLXcond, where cond is a condition code like EQ (equal) or NE (not equal). These branches only occur if the condition flags in the Application Program Status Register (APSR) match the cond code.
Branches specify the target address in one of two ways:
- PC-relative: The signed offset from the current Program Counter (PC) is encoded in the instruction. This allows position-independent code.
- Absolute: The instruction contains the full target address. This requires updating the instruction if code moves in memory.
PC-relative branches have a limited range of ±1 MB from the PC due to the instruction encoding size. Absolute branches can target the full 4 GB address space of Cortex-M.
Branch Link Instructions
BL and BLX store the address of the next instruction (return address) in the Link Register (LR) before branching. This allows subroutines to easily return after completing.
The LR is part of the general purpose register bank in Cortex-M. It can be explicitly stored to and loaded from like any other register. The compiler handles storing return addresses in LR for function calls.
Conditional branches use the APSR condition flags to determine if a branch should occur. The flags include:
- Negative (N) – Set if result is negative
- Zero (Z) – Set if result is zero
- Carry (C) – Set if unsigned overflow from addition, cleared if borrow from subtraction
- Overflow (V) – Set if signed overflow from addition/subtraction
Common conditional branch types based on these flags:
- BEQ/BNE – Branch if Equal/Not Equal (compares Z flag)
- BCS/BCC – Branch if Carry Set/Cleared (unsigned compare using C flag)
- BMI/BPL – Branch if Minus/Plus (tests sign bit in N flag)
- BVS/BVC – Branch if Overflow/No Overflow (checks V flag)
These allow constructs like branching when a comparison is true/false or detecting overflow from an arithmetic operation.
Branch Instructions in Loops
Branching is most commonly used for implementing conditional and looping constructs in code. For example:
loop: ... SUB R1, R1, #1 ; decrement loop counter CMP R1, #0 ; compare to 0 BNE loop ; branch to loop label if R1 != 0
This simple loop continues until R1 reaches 0. The BNE instruction branches back to the loop label until the comparison sets the Z flag. When R1 equals 0, the BEQ condition will fail and execution will fall through to the next instruction after the loop.
The limited offset range of PC-relative branches poses a problem when branching to distant code locations. Cortex-M provides two options to resolve this:
- Use an absolute branch – Targets full address space but less position independent
- Add branch stubs – Extra branches placed within ±1 MB that can daisy-chain
Branch stubs work by branching to a stub area near the target, which then branches to the final location. By chaining these stubs, long distance branches are achievable with PC-relative offset sizes.
Like other modern CPU architectures, Cortex-M processors employ branch prediction to avoid pipeline stalls from branching. The CPU guesses which way a conditional branch will go based on past behavior.
If correctly predicted, the next instructions are fetched without delay. A mispredict incurs a penalty while the pipeline is flushed and execution is redirected. Predictability depends on branch history and structure of code.
Branch Instruction Uses
Beyond loops and conditionals, branches are also useful for:
- Switch/case statements – Branch table of addresses for each case
- State machines – Branch between different state code blocks
- Function returns – Return to the LR stored address
- Function calls – Indirect branch using function pointer in register
- ISRs – Branch to handler code based on interrupt source
Efficient use of branching is key to optimized code and performance on Cortex-M processors.
Branch Instruction Summary
Key points about branch instructions on ARM Cortex-M:
- Alter sequential program flow to implement loops, decisions, calls
- Multiple types for conditional/unconditional, subroutines
- PC-relative offsets or absolute addressing for targets
- Link register used to store return addresses
- Condition flags allow branching based on results
- Prediction avoids stall from branch decision
- Used widely for control flow, functions, state machines
Overall, branches are an integral part of any meaningful Cortex-M program, enabling nonlinear code execution.