Intuition What this page is for
The parent note showed you why ARM is a RISC design: fixed-length instructions, a load/store rule, many registers, a predictable pipeline , and conditional execution. This page stress-tests those ideas against every case class you might meet — small pipelines, huge pipelines, the degenerate "one instruction" case, branch penalties, Thumb code density, and a WCET word problem. Each example tells you which cell of the matrix it covers, asks you to forecast the answer first, then walks every step with a "Why this step?" and a final "Verify:".
New symbols before we start — earned, not assumed:
n = number of instructions we run.
k = number of pipeline stages (Fetch, Decode, Execute, ...). Picture k workstations on an assembly line.
τ (Greek letter "tau") = the time one stage takes, one "tick" of the clock. Units: nanoseconds (ns ).
T = total time to finish all n instructions. We write two flavours: T serial = the time with no overlap (do one instruction fully, then the next), and T pipe = the time with the assembly-line overlap. Same letter T , two subscripts labelling which way we ran the work.
S = speedup = how many times faster the pipeline is than doing it serially, i.e. S = T serial / T pipe . A pure ratio, no units.
Every question this topic can throw at you falls into one of these cells. The examples below are labelled with the cell they hit, and together they fill the whole grid.
Cell
Case class
What makes it tricky
A
Small n , small k
fill cost is not negligible — speedup far below k
B
Large n , fixed k
limiting behaviour: S → k
C
Degenerate: n = 1
one instruction — pipeline gives zero benefit
D
Degenerate: k = 1
one stage — no pipeline at all, S = 1
E
Branch penalty (flush)
a wrong guess wastes k − 1 cycles; effective time rises
F
Conditional execution vs branch
predication removes the flush
G
Load/store instruction count
translating C to ARM, counting memory trips
H
Code density (Thumb, 16-bit)
same program, different instruction size , not count
I
Real-world WCET word problem
picking worst case, not average
J
Exam twist
"more stages = faster?" — find where it breaks
Before the numbers, look at the timing diagram below — every pipeline example on this page is really just counting boxes in a picture like this.
Intuition What the figure shows
Each row is one instruction; each column is one clock cycle of length τ . A coloured box means "this instruction is sitting in this stage during this cycle": blue = F etch, orange = D ecode, green = E xecute. Slide your eye down the diagonal — Instruction 2 starts Fetch the moment Instruction 1 moves to Decode. That overlap is the whole trick.
Now count columns . The picture has n = 4 rows but only 6 columns, not 4 × 3 = 12 . The red dashed line marks the end of the fill : for the first k − 1 = 2 cycles only the top instruction is making progress (the diagonal hasn't "reached the bottom" yet). After the line, in steady state , exactly one green box completes every column. So total columns = fill ( k − 1 ) + one per instruction ( n ) = ( k − 1 ) + n = k + ( n − 1 ) . That is where the formula T pipe = ( k + ( n − 1 )) τ comes from — it is just "count the columns."
Run n = 4 instructions on a k = 3 stage pipeline with τ = 2 ns . How long does it take serially, how long pipelined, and what is the speedup?
Forecast: with only 4 instructions the pipeline barely fills — do you expect the speedup to be close to 3 , or noticeably less? Guess a number before reading on.
Serial time. T serial = n ⋅ k ⋅ τ = 4 ⋅ 3 ⋅ 2 = 24 ns .
Why this step? With no overlap, each of the 4 instructions marches through all 3 stages before the next starts — so we multiply everything together.
Pipelined time. T pipe = ( k + ( n − 1 )) ⋅ τ = ( 3 + 3 ) ⋅ 2 = 12 ns .
Why this step? This is the "count the columns" result from the figure above: k − 1 = 2 fill cycles plus one cycle per instruction. In the picture that is exactly the 6 columns.
Speedup. S = 24/12 = 2.0 .
Why this step? S = T serial / T pipe — it answers "how many times faster?"
Verify: the ideal limit is k = 3 , but we got only 2.0 . That is the point of cell A — in the figure the fill is k − 1 = 2 of the 6 columns, a big fraction when n is tiny. Sanity check: 6 columns × 2 ns = 12 ns . ✓
Same k = 3 , τ = 2 ns , but now n = 100000 . What is the speedup, and what does it approach?
Forecast: will S be closer to 2 (like Example 1) or to 3 ? Why?
Build the speedup formula from the two times. We already have T serial = nk τ (Example 1, step 1) and T pipe = ( k + ( n − 1 )) τ (the "count the columns" result). Divide them:
S = T pipe T serial = ( k + n − 1 ) τ nk τ = k + n − 1 nk .
Why this step? The τ cancels top and bottom, so speedup does not depend on clock speed — only on how many stages and how many instructions. We are deriving the closed form, not just quoting it: it is literally (serial columns) over (pipelined columns).
Plug in the numbers.
S = 3 + 100000 − 1 100000 ⋅ 3 = 100002 300000 ≈ 2.99994.
Why this step? The fill is still only k − 1 = 2 extra cycles — a rounding error next to 100000, so the fraction sits just under 3 .
Take the limit. As n → ∞ , the "k − 1 " in the denominator becomes negligible next to n , so
S = n + ( k − 1 ) nk ⟶ n nk = k = 3.
Why this step? This is why pipelines are worth building — with a steady stream of work you get nearly a full k × speedup. In figure terms: the fill columns are a fixed few, while the steady-state columns grow without bound, so their ratio approaches k .
Verify: 2.99994 < 3 always (you can never beat k from filling), and it is much closer to 3 than Example 1's 2.0 . The only difference between the two examples is n — exactly what cell B isolates. ✓
You run a single instruction (n = 1 ) on the same k = 3 , τ = 2 ns core. Does the pipeline help at all?
Forecast: speedup = 1 , > 1 , or < 1 ?
Serial time. T serial = 1 ⋅ 3 ⋅ 2 = 6 ns .
Why this step? One instruction, three stages, no overlap possible with itself.
Pipelined time. T pipe = ( 3 + ( 1 − 1 )) ⋅ 2 = 3 ⋅ 2 = 6 ns .
Why this step? With n = 1 the "( n − 1 ) " term is zero — there is no follow-on instruction to overlap, so you just pay the full fill. In figure terms: only the top row exists, so total columns equal that one row's k boxes.
Speedup. S = 6/6 = 1 .
Why this step? Equal times mean no benefit.
Verify: a pipeline only helps when instructions stream and overlap. A lone instruction has nothing to overlap with, so S = 1 exactly — the pipeline's fill latency is fully exposed. This is the single-shot degenerate case every WCET analyst must remember. ✓
A CPU with a single stage (k = 1 ): fetch-decode-execute all crammed into one long cycle. Run n = 50 instructions. What speedup does "pipelining" give?
Forecast: if there is only one stage, can there be any overlap?
Serial time. T serial = 50 ⋅ 1 ⋅ τ = 50 τ .
Why this step? With k = 1 stage, each instruction takes exactly one (long) cycle of length τ , and there are 50 of them — so serially the time is n ⋅ k ⋅ τ = 50 ⋅ 1 ⋅ τ .
Pipelined time. T pipe = ( 1 + ( 50 − 1 )) ⋅ τ = 50 τ .
Why this step? With k = 1 there is nothing to split into stations — each instruction occupies the whole "line" alone, so there is no overlap to save time.
Speedup. S = 50 τ /50 τ = 1 .
Why this step? S = 1 + n − 1 n ⋅ 1 = n n = 1 for any n .
Verify: the formula collapses to exactly 1 independent of n — a one-stage "pipeline" is just a plain sequential CPU. This confirms the two degenerate axes (n = 1 and k = 1 ) both give S = 1 for different reasons. ✓
A k = 5 stage core runs n = 1000 instructions, but 20 of them are branches, and each mispredicted branch forces a flush of k − 1 = 4 in-flight instructions (4 wasted cycles). Assume all 20 mispredict. τ = 1 ns . What is the real pipelined time?
Forecast: the ideal time was 1004 ns (parent note). How much do 20 flushes add?
Ideal cycles (no flush). k + ( n − 1 ) = 5 + 999 = 1004 cycles.
Why this step? Start from the clean "count the columns" baseline.
Flush penalty. Each misprediction wastes k − 1 = 4 cycles; 20 of them cost 20 ⋅ 4 = 80 cycles.
Why this step? When a branch guess is wrong, the k − 1 half-built instructions behind it are garbage and must be thrown away — the figure below shows exactly which boxes get discarded (the red hatched ones).
Total cycles and time. 1004 + 80 = 1084 cycles ⇒ T = 1084 ⋅ 1 = 1084 ns .
Why this step? The two costs — the clean streaming baseline and the flush penalty — simply add , because the wasted cycles are extra clock ticks stuffed into the same timeline; multiplying the total cycle count by τ converts cycles into real time.
Effective speedup. vs serial T serial = 1000 ⋅ 5 = 5000 ns , so S = 5000/1084 ≈ 4.613 .
Why this step? Flushes drag the real speedup below the ideal ≈ 4.98 .
Verify: 1084 > 1004 (flushes always add time) and 4.613 < 4.98 (real < ideal). This is exactly why aerospace cores keep pipelines short and lean on conditional execution to avoid branches — see Example 6. ✓
Intuition What the flush figure shows
The blue row is the branch, marching through its 5 stages. The red hatched boxes below it are the k − 1 = 4 instructions the CPU guessed and started fetching down the wrong path. When the branch reaches the stage where its true direction is known (the grey dashed line), those 4 boxes are thrown away — the green row is the correct target restarting from scratch. Count the red boxes: that gap of 4 cycles per mispredict is precisely the "k − 1 " penalty, and it is why deeper pipelines (bigger k ) hurt more when branches misbehave — the theme of Example 10.
Translate if (a == b) x = x + 1; two ways on ARM, with a in R0, b in R1, x in R2. Way 1 uses a branch; way 2 uses predication. On a k = 5 core, a taken/flushed branch costs 4 extra cycles. Compare worst-case cycle counts (count the ADD region only, one pass).
Forecast: which version has the more predictable cycle count?
Way 1 — branch:
CMP R0, R1 ; sets Z=1 if equal
BNE skip ; branch if NOT equal
ADD R2, R2, #1 ; runs only when equal
skip:
Way 2 — predication:
CMP R0, R1 ; sets Z=1 if equal
ADDEQ R2, R2, #1 ; add ONLY if Z=1, no branch
Count Way 1's worst case. CMP (1) + BNE that mispredicts and flushes (1 + 4 penalty) + possible ADD (1) = 1 + 5 + 1 = 7 cycles worst case; best case 6 .
Why this step? The branch's direction depends on data, so timing varies — a WCET nightmare.
Count Way 2. CMP (1) + ADDEQ (1) = 2 cycles, every time , equal or not.
Why this step? ADDEQ is one fixed-length instruction that either writes or does nothing — no jump, no flush.
Compare spread. Way 1: 6–7 cycles (data-dependent). Way 2: exactly 2 cycles.
Verify: Way 2 is both faster and, crucially, has zero timing variance — the property WCET analysis loves. This is why ARM makes almost every instruction predicable. ✓
Translate d = a + b - c; to ARM, where a, b, c, d all live in memory at addresses held in R1, R2, R3, R4. How many instructions, and how many memory trips ?
Forecast: guess the instruction count. Remember: the ALU can never touch memory directly.
Load the three inputs.
LDR R5, [R1] ; a
LDR R6, [R2] ; b
LDR R7, [R3] ; c
Why this step? Load/store rule — arithmetic operands must first sit in registers .
Compute in registers.
ADD R8, R5, R6 ; a + b
SUB R8, R8, R7 ; (a+b) - c
Why this step? Two ALU ops, register-only, each simple and fixed-length.
Store the result.
STR R8, [R4] ; d
Why this step? The result must be written back explicitly — the ALU can't drop it into memory itself.
Count. Instructions: 3 + 2 + 1 = 6 . Memory trips: 3 loads + 1 store = 4 .
Why this step? We tally by category because the load/store discipline forces the split: one load per memory input, one ALU op per operator, one store per memory output — so counting is just adding up these forced groups.
Verify: three inputs ⇒ three loads; one output ⇒ one store; two operators (+ and − ) ⇒ two ALU ops. Total 3 + 1 + 2 = 6 instructions, and memory accesses = 3 + 1 = 4 . Every count is forced by the load/store rule — no way to fold a memory read into the ADD, unlike CISC. ✓
The 6-instruction program from Example 7 is compiled once as 32-bit ARM instructions and once as 16-bit Thumb instructions (assume all 6 have Thumb equivalents). Compare the code size in bytes . Does the instruction count change?
Forecast: does Thumb make the program run fewer instructions, or just take less space ?
ARM size. 6 instructions × 32 bits = 192 bits = 24 bytes .
Why this step? 32 bits = 4 bytes , so 6 × 4 = 24 .
Thumb size. 6 instructions × 16 bits = 96 bits = 12 bytes .
Why this step? Thumb re-encodes common instructions in half the width.
Ratio. 24/12 = 2 — Thumb is 2 × denser here.
Why this step? We divide the two sizes to express the saving as a single factor — the number vendors quote as "code density."
Verify: the instruction count stays 6 in both — Thumb changes each instruction's size , not the program's logic. Half-width ⇒ half the bytes ⇒ smaller flash and better instruction-cache hit rate, boosting performance-per-watt . Beware: some 32-bit operations need two Thumb instructions, so real code isn't always exactly 2 × — this idealised case shows the mechanism. ✓
A flight-control loop must finish within a 500 µs deadline. Its worst path is n = 90000 instructions on a k = 5 pipeline at τ = 5 ns , plus 200 mispredicted branches each flushing 4 cycles. Does it meet the deadline? (Ignore cache misses here.)
Forecast: 500 µs = 500000 ns . Guess whether we're comfortably under, or dangerously close.
Base pipeline cycles. k + ( n − 1 ) = 5 + 89999 = 90004 cycles.
Why this step? Start from the clean streaming cost ("count the columns").
Flush cycles. 200 ⋅ 4 = 800 cycles.
Why this step? WCET must assume every branch mispredicts — worst case, not average.
Total time. ( 90004 + 800 ) ⋅ 5 = 90804 ⋅ 5 = 454020 ns = 454.02 µs .
Why this step? Cycles add (baseline plus penalty), then multiplying by τ = 5 ns converts cycles to real time; dividing by 1000 takes ns → µs .
Compare to deadline. 454.02 µs < 500 µs ⇒ it meets the deadline , with 45.98 µs of margin.
Why this step? A real-time system is only correct if the worst path fits inside the deadline — a positive margin is the pass/fail verdict.
Verify: margin = 500 − 454.02 = 45.98 µs > 0 . Because we used the worst path and assumed all branches flush, any real run is faster — a safe conservative bound, exactly what WCET demands. ✓
Marketing claims a k = 20 super-pipeline crushes a k = 5 core. Both run n = 1000 instructions, τ = 1 ns , with a branch every 5 instructions (200 branches) that each flush k − 1 cycles on mispredict. Which core finishes the program sooner ?
Forecast: deeper pipeline ⇒ higher ideal speedup (20 > 5 ). Does that win in practice here?
k = 5 total cycles. base = k + ( n − 1 ) = 5 + 999 = 1004 ; flush = 200 ⋅ ( k − 1 ) = 200 ⋅ 4 = 800 ; total = 1004 + 800 = 1804 cycles ⇒ 1804 ⋅ 1 = 1804 ns .
Why this step? Same method as Example 5 — clean columns plus one flush penalty of k − 1 per mispredicted branch.
k = 20 total cycles. base = k + ( n − 1 ) = 20 + 999 = 1019 ; flush = 200 ⋅ ( k − 1 ) = 200 ⋅ 19 = 3800 ; total = 1019 + 3800 = 4819 cycles ⇒ 4819 ⋅ 1 = 4819 ns .
Why this step? A deeper pipe means each flush throws away more half-built instructions — the penalty scales with k − 1 , so 200 branches now cost 3800 cycles instead of 800 . Recall the red hatched boxes in the flush figure: a taller pipe has more of them to discard.
Compare. 1804 ns (shallow) vs 4819 ns (deep) — the shorter k = 5 pipeline wins by a wide margin.
Why this step? The ideal S → k promised the deep pipe would be faster, but that limit assumed no flushes. Real branchy code exposes the trade-off the marketing hid.
Verify: 4819 > 1804 , so "more stages" lost once branches are frequent. The ideal S → k (Example 2) assumes zero flushes; here branches every 5 instructions punish the deep pipeline nearly 2.7 × (4819/1804 ≈ 2.67 ). This is precisely why embedded/aerospace ARM cores stay shallow — predictability and low flush cost beat peak throughput . ✓
Recall Quick self-test
Which two degenerate cases both give speedup S = 1 , and why? ::: n = 1 (one instruction, nothing to overlap — you just pay the fill) and k = 1 (one stage, nothing to split into stations).
A branch that mispredicts on a k -stage pipeline wastes how many cycles? ::: k − 1 cycles (the half-built instructions behind the branch get flushed).
Does Thumb reduce the number of instructions or their size? ::: Their size (16-bit vs 32-bit) — the instruction count is unchanged; the program gets denser, not shorter in steps.
In WCET analysis, do you assume branches hit or miss? ::: Miss (worst case) — you must bound the slowest possible path, never the average.
Mnemonic Two knobs, one truth
"Fill for small n , flush for big k ." Small programs are hurt by the fill; deep pipelines are hurt by flushes. Both push aerospace ARM toward short pipelines, streaming work, no branches .