Exercises — ARM architecture intro — used in embedded - aerospace
Before we start, we build the one formula every exercise reuses — but this time we derive it, not just quote it.
Level 1 — Recognition
Goal: can you identify the correct ARM concept when you see it?
Exercise 1.1
Which of these is legal ARM and which is illegal, and why?
(a) ADD R0, [R1], [R2]
(b) LDR R0, [R1]
(c) ADD R0, R1, R2Recall Solution 1.1
What we are testing: the load/store rule — the ALU touches registers only.
- (a) ILLEGAL.
ADDis an arithmetic (ALU) instruction, so both operands must be registers or immediates.[R1]and[R2]are memory operands. ARM forbids arithmetic straight from memory. - (b) LEGAL.
LDR(LoaD Register) is exactly the instruction whose job is to bring a memory value into a register.[R1]means "the value at the address stored in R1". - (c) LEGAL. All three operands are registers — that is what the ALU wants.
Answer: only (a) is illegal. See RISC vs CISC for why CISC allows (a) but RISC deliberately does not.
Exercise 1.2
Match each register to its special role: , , .
Recall Solution 1.2
- SP (Stack Pointer) — points at the top of the stack.
- LR (Link Register) — holds the return address when a subroutine is called.
- PC (Program Counter) — holds the address of the instruction being fetched.
See Registers and Register File. The other 13 registers (–) are general-purpose scratch space.
Level 2 — Application
Goal: can you apply the rules to produce or trace code?
Exercise 2.1
Translate d = x - y; into ARM, where the address of x is in R1, the address of y is in R2, and the result d must be written to the address in R3.
Recall Solution 2.1
Follow the load → compute → store shape forced by load/store architecture:
LDR R4, [R1] ; R4 = x (bring x from memory to a register)
LDR R5, [R2] ; R5 = y
SUB R6, R4, R5 ; R6 = R4 - R5 = x - y (ALU on registers only)
STR R6, [R3] ; write R6 back to memory at address in R34 instructions. Two loads, one ALU op, one store — the canonical RISC pattern.
Exercise 2.2
A MOV R0, #7 puts the immediate value 7 into R0. Trace the register values after:
MOV R0, #7
MOV R1, #3
SUB R2, R0, R1
CMP R2, #4What are R0, R1, R2 and the Z flag afterward?
Recall Solution 2.2
- After
MOV R0, #7: . - After
MOV R1, #3: . - After
SUB R2, R0, R1: . CMP R2, #4computes only to set flags (it does not store the result). Result is 0, so the Z (Zero) flag = 1.
Final: . CMP is the flag-setting sibling of SUB; see the flags N Z C V in Registers and Register File.
Level 3 — Analysis
Goal: can you reason about behaviour, timing, and trade-offs?
Exercise 3.1
Run instructions on a stage pipeline with . Compute , , and the speedup .
Recall Solution 3.1
Plug into the boxed formulas.
- .
- .
- .
Reading it: is close to because 200 is large enough that the 5-cycle fill cost is a small fraction. See Instruction Pipelining.
Exercise 3.2
For the same pipeline, how large must be before the speedup reaches 90% of the ideal, i.e. ? Solve for the smallest integer . The figure below plots the whole curve so you can see the answer.
Recall Solution 3.2
Set with : Multiply both sides by (positive, so inequality direction is safe): Smallest integer: . Check: exactly.
Exercise 3.3
A branch misprediction on this pipeline flushes the partly-built instructions and refills. Suppose the flush penalty is cycles. If 1 in every 20 instructions is a mispredicted branch, what is the average cycles per instruction (CPI), and how does conditional execution help?
Recall Solution 3.3
Why base CPI is 1: as derived at the top of this page, an ideal hazard-free pipeline pops out exactly one instruction every cycle in steady state (its initiation rate is 1). So if nothing ever stalled, every instruction would cost 1 cycle on average, i.e. .
A misprediction adds penalty cycles, and this happens with probability :
Conditional execution helps by turning a small if (a branch that can mispredict) into straight-line predicated code (e.g. ADDEQ). Fewer branches ⇒ fewer flushes ⇒ CPI stays near 1 ⇒ more predictable timing, which is exactly what Real-Time Systems and WCET needs.
Exercise 3.4
Now a data hazard. Consider:
LDR R4, [R1] ; load a value into R4
ADD R5, R4, R6 ; immediately uses R4The ADD needs R4 before the LDR has finished bringing it back from memory. Even with forwarding hardware, this costs a 1-cycle load-use stall. If such load-use pairs are 1 in every 10 instructions on the pipeline (branches ignored here), what is the CPI?
Recall Solution 3.4
Why forwarding can't fully hide a load: forwarding pipes a result from one stage's output directly into a later instruction's input, hiding most ALU-to-ALU dependencies with zero cost. But a load's data isn't available until after the memory stage — one stage too late for the very next instruction — so the pipeline must insert 1 bubble (stall) cycle. This is the famous load-use delay. Compilers reduce this by scheduling an unrelated instruction between the load and its use. See Instruction Pipelining and Memory Hierarchy and Cache.
Level 4 — Synthesis
Goal: can you combine several ideas into one design decision?
Exercise 4.1
You must implement if (a == b) c = c + 1; two ways and compare cycle cost on the pipeline (flush penalty ). Assume a,b,c are already in R0, R1, R2.
(A) Branch version:
CMP R0, R1
BNE skip
ADD R2, R2, #1
skip:(B) Predicated version:
CMP R0, R1
ADDEQ R2, R2, #1For the case where a == b (branch not taken but predictor guessed "taken" → mispredict), count cycles for each, ignoring the shared CMP. Assume the predicated instruction issues in a single cycle (the standard case on ARMv7-A, which supports full conditional execution).
Recall Solution 4.1
Count only the instructions after the shared CMP.
- (A) Branch, mispredicted: the
BNEcosts 1 cycle to issue plus a 4-cycle flush penalty when the guess is wrong, then theADDruns (1 cycle). Total cycles. - (B) Predicated: just
ADDEQ, one straight-line instruction, no branch, no flush cycle.
Predication is cheaper here, and — more importantly for aerospace — it always takes the same number of cycles whether or not the condition holds. That constant cost is gold for Real-Time Systems and WCET.
Corner case (footnote): this clean -cycle figure assumes the core executes a predicated instruction in a single cycle — true for classic 32-bit ARM (A32), which predicates almost every instruction. On Thumb/Thumb-2 and on some cores, general predication is replaced by an IT (if-then) block that predicates a short run of following instructions, and on machines with no single-cycle predication a predicated op may still consume its slot even when the condition is false. So predication removes the flush risk but is not always literally free — the win over a misprediction, however, still holds.
Exercise 4.2
You are choosing a core for a battery-powered flight sensor. Core X: stages, clock (so cycle time ). Core Y: stages, clock (so cycle time ). Branches are 15% of instructions with a 30% misprediction rate; flush penalty . (a) Compute each core's average CPI. (b) Compute the average time per instruction , so the clock rates actually enter. (c) Which core is faster on this branchy code, and which do you pick for a real-time job — and why?
Recall Solution 4.2
(a) Average CPI :
- Core X: .
- Core Y: .
So on CPI alone, Y looks better (). But CPI ignores clock speed — a cycle on X is half the length of a cycle on Y, so we must convert to real time.
(b) Average time per instruction :
- Core X: .
- Core Y: .
(c) Verdict on raw speed: despite its worse CPI, Core X is actually faster per instruction (, about quicker) because its 2× clock more than pays for the extra flush penalty. This is the whole point: lower CPI does not mean faster — you must multiply by cycle time.
Which to pick for the real-time flight job? Still Core Y — but not because it is faster on average. Pick it because (1) its shallow pipeline has a smaller, more predictable worst-case flush penalty (4 vs 11 cycles), giving tighter WCET bounds you can certify; and (2) a slower-clock, shallow core generally wins on performance-per-watt, which matters on a battery. Core X's raw-speed advantage is the throughput metric — the right choice for a server, the wrong priority for a deadline-bound, power-limited flight sensor.
Level 5 — Mastery
Goal: can you derive a result and defend a subtle claim end-to-end?
Exercise 5.1
Derive the exact at which pipeline speedup reaches a general fraction of ideal, i.e. (with ), then sanity-check against Exercise 3.2 ().
Recall Solution 5.1
What we do: set the speedup equal to and solve for . The on both sides cancels (divide both sides by , valid since ): Why cancel? It reveals that the fraction of ideal does not depend on directly, only on how compares to . Cross-multiply: Check with : . ✓ Matches Exercise 3.2 exactly.
What it looks like: as the denominator , so — you can never fully reach ideal with finite work; you only approach it, exactly as the flattening curve in the figure shows.
Exercise 5.2
Defend or refute: "For a hard real-time flight controller, a pipeline that averages CPI 1.18 but can spike to CPI 5 on a rare mispredict-heavy path is preferable to one that is always exactly CPI 1.4."
Recall Solution 5.2
Refute. Real-time correctness is governed by worst-case execution time (WCET), not average. A path that spikes to CPI 5 means the worst case is slower than the steady one — if the flight-control loop must finish inside a fixed deadline, that spike can blow the deadline and the average is irrelevant. The always-1.4 core has a tight, provable upper bound, which is exactly what you can certify. This is the deep reason ARM's predictable, conditionally-executed, shallow-pipeline design is loved in aerospace: it trades a little average speed for a small, analysable worst case. See Real-Time Systems and WCET and Power and Performance-per-Watt.
Numeric anchor: worst-case ratio ; if the deadline budget is only the nominal, this core fails certification.
Recall One-paragraph Feynman recap of this whole exercise set
Everything here spins around two truths. First, ARM's ALU only cooks with ingredients on the counter (registers), so real code is always load → compute → store. Second, pipelines make throughput approach , but only when work is large and hazards behave — control hazards (mispredicted branches) cost cycles, and even loads cost a 1-cycle bubble their following user can't dodge. For phones you chase the average; for spaceships you chase the worst case, which is why ARM stays shallow, predictable, and conditionally-executed.