Exercises — ARM architecture overview
This page assumes only what the ARM architecture overview parent note built: load-store, fixed 32-bit encoding, conditional execution, registers, Thumb, pipelines. Every symbol below is re-explained the instant it appears.
Level 1 — Recognition
L1.1 — Name the access pattern
Recall Solution
ARM is a load-store architecture: only LDR (load register) and STR (store register) touch memory; arithmetic works only on registers.
LDR r0, [r1] ; copy the memory word at address r1 into register r0
ADD r0, r0, #5 ; add 5 — this is register-only, allowed
STR r0, [r1] ; copy the result back to memory at address r1The rule is the load-store principle. You cannot write ADD [r1], #5 on ARM (that IS legal on x86 — see 5.1.10-x86-architecture).
L1.2 — Read the condition suffix
Recall Solution
The suffix GT = "Greater Than". Every ARMv7 instruction carries a 4-bit condition field (bits 31–28) checked against the flags before it runs.
ADDGT r1, r1, #1 means: if the last comparison set flags saying the tested value was greater than the compared value, then compute r1 = r1 + 1; otherwise the instruction becomes a NOP (no-operation, does nothing) and still costs about 1 cycle.
So it only changes r1 when the GT condition is true.
L1.3 — Count the register field bits
Recall Solution
To name one of registers you need bits.
A 32-bit data-processing instruction must pack a condition, opcode, TWO register fields (Rn, Rd), and an operand. Every extra register doubles or grows the field. Choosing makes each field exactly 4 bits — a clean fit. Twenty registers would need 5 bits per field and waste encoding space. See 5.1.07-ARM-instruction-encoding.
Level 2 — Application
L2.1 — Branch-cost formula
Recall Solution
Branch version: Plus the branch instruction itself still costs its normal cycle, but the penalty attributable to prediction is cycles. Conditional-execution version: the branch disappears; the guarded instruction is a fixed 1-cycle operation (it just turns into a NOP if the condition fails). So the average cost is cycle with zero misprediction penalty. Net saving from conditional execution here cycles per avoided branch.
L2.2 — Fetch address arithmetic
Recall Solution
Fixed-width means "next instruction" is always ==== bytes (4 bytes per 32-bit instruction). Addresses are in hexadecimal, where 0x10 = 16 decimal.
The current instruction is at 0x8000; the next three come from repeatedly adding 4:
(0x800C because 0x8008 + 4 = 0x800C; hex digit C = 12.) The starting address 0x8000 is not one of the three, because the question asks for the ones fetched after it.
This trivial "+4 forever" is exactly what variable-length x86 cannot do — it must first decode an instruction to learn its length.
L2.3 — Thumb code-size saving
Recall Solution
A reduction means the new size is of the original. Saved: Smaller code fits better in the instruction cache (6.3.02-cache-memory) → fewer slow fetches from DRAM → lower energy (7.1.05-power-optimization).
Level 3 — Analysis
L3.1 — Why does conditional execution beat branching here?
Recall Solution
First, a mnemonic definition: ==BLE== means "Branch if Less than or Equal" — ARM's branch instruction (B) with the LE condition. (ARM uses B for branches, not the x86-style J for jump.) So BLE skip jumps to skip when r0 <= 0, skipping the ADD.
Semantics we must preserve:
ADD r1, r1, #1happens only whenr0 > 0(the branch fell through).MOV r2, #0happens always — it is after the label, so both paths reach it.
Conditional rewrite (same semantics):
CMP r0, #0 ; set flags from (r0 - 0)
ADDGT r1, r1, #1 ; runs only if r0 > 0, else NOP (matches original ADD)
MOV r2, #0 ; UNCONDITIONAL — matches original always-run MOVThe MOV keeps no condition suffix, because in the original it executes on every path. Guarding it (e.g. MOVLE) would be a bug: it would skip the MOV when r0 > 0 and change the program's meaning.
No branch instruction exists anymore, so there is nothing to mispredict.
Cost of branch version (expected): the BLE costs its own cycle plus an expected penalty of
Cost of conditional version: each guarded (or unconditional) instruction is a fixed 1 cycle regardless of outcome — total predictable cost, penalty .
The conditional path wins by roughly cycles per encounter and, crucially, its timing is deterministic — which is why real-time embedded ARM code loved this feature.
L3.2 — When does conditional execution lose?
Recall Solution
Every guarded instruction still occupies a pipeline slot and still takes a cycle even when it does nothing. If a whole block of guarded instructions runs but the condition is false, you paid cycles for NOPs. A branch that is predicted correctly costs almost nothing and skips those instructions entirely. So the break-even is roughly: When branch predictors got very accurate ( small, e.g. correct so ) the right side shrinks below the left for any block of a few instructions, and predicted branches win. Conditional execution also complicates out-of-order cores because an instruction doesn't know whether it "counts" until late. That trend (great predictors + wide out-of-order machines — see 5.2.03-pipelining) is exactly why ARMv8 removed it.
L3.3 — Banked registers, quantified
Recall Solution
Without banking: on entry you save 5 registers (5 cycles), on exit you restore 5 (5 cycles):
With FIQ banking, the handler already has its own physical r8–r12, so it never touches the interrupted program's copies — nothing to save or restore. Banking removes all 10 cycles.
Why it works: "banked" means the register name r8 maps to a different physical register depending on mode. Switching mode instantly swaps in fresh registers, so context is preserved for free by the hardware instead of by save/restore instructions — the whole point of Fast Interrupt.
Level 4 — Synthesis
L4.1 — Full instruction-field budget
Recall Solution
Register fields: bits each, so Rn = 4, Rd = 4.
Fixed pieces: Cond = 4, format 00 = 2, I = 1, opcode = 4, S = 1.
Sum of everything except the operand:
Operand2 gets the remainder:
Total check: ✓ — matches the parent note's layout (Operand2 occupies bits 11–0, which is 12 bits). Those 12 bits encode either a small immediate or a shifted register (details in 5.1.07-ARM-instruction-encoding).
The figure below is your answer drawn to scale: each coloured box is one field, and its width along the 32-bit ruler is proportional to its bit-count. Read it left-to-right from bit 31 (most-significant, Cond) down to bit 0 (Operand2). Notice how the four fixed lavender/mint/butter/coral boxes on the left consume exactly 20 bits, leaving the single wide mint Operand2 box on the right holding the remaining 12 — this is the "20 + 12 = 32" balance visualised. Any register you add would widen the two Rn/Rd boxes and steal from that right-hand operand box.

L4.2 — Register width vs encoding, the real trade-off
Recall Solution
Bits per field ; two fields cost .
| registers | bits/field | two fields |
|---|---|---|
| 8 | 3 | 6 |
| 16 | 4 | 8 |
| 32 | 5 | 10 |
ARMv7 chose 16. Eight registers spill to memory too often (more slow LDR/STR). Thirty-two would eat 10 of 32 bits just on two register names, starving the operand/immediate space. Sixteen keeps each field a clean 4 bits (fits the byte-aligned layout) while giving enough registers to keep most work off memory — the balance point between (a) and (b).
Level 5 — Mastery
L5.1 — Design a pipeline-depth argument
Recall Solution
Step 1 — count mispredictions (same for both). Branch instructions: . Of those, mispredicted:
Step 2 — Design A cycles. Base one cycle per instruction plus cycles per misprediction: Design A time. Divide cycles by frequency cycles/s:
Step 3 — Design B cycles. Base plus cycles per misprediction: Design B time. Divide by frequency cycles/s:
Step 4 — compare and conclude. Even though B does more cycles (), its higher clock makes each cycle much shorter, so: Design B is about faster. This is exactly why high-performance ARM cores accept deep pipelines and lean on strong branch predictors (5.2.03-pipelining) — the frequency gain outweighs the flush cost as long as prediction stays good. If rose sharply, B's larger flush penalty ( vs ) would erode and eventually reverse that advantage.

L5.2 — Argue the ARMv8 conditional-execution removal end-to-end
Recall Solution
Three linked points:
- The 6-cycle saving assumed a bad predictor (, ). Modern ARM cores predict correctly, so realistic penalty is — a third or less of the old figure.
- Conditional execution is always-paid (L3.2): a block of guarded instructions costs cycles even when skipped, whereas a correctly predicted branch costs almost nothing. With good predictors, for typical blocks, so branching wins on average.
- It fights out-of-order execution (L3.2): an instruction that doesn't know if it "counts" until its condition resolves complicates the wide, deep machines (L5.1) that give ARMv8 its speed. So removal wasn't a regression — it enabled the deeper, faster, out-of-order pipelines whose frequency advantage (L5.1) dwarfs the old conditional-execution trick. The engineer's number is real but obsolete: it belongs to the slow, shallow, poorly-predicting cores of the 1990s.