4.1.20 · D5Computer Architecture (Deep)
Question bank — Hazard mitigation — stalling, forwarding - bypassing, branch prediction
True or false — justify
Forwarding can eliminate every RAW data hazard in the classic 5-stage pipeline.
False. It kills every ALU-producer RAW, but a
lw result is only born at the end of MEM while the very next instruction needs it in EX — one cycle earlier in real time. You cannot forward into the past, so load-use still costs ≥1 stall.A structural hazard and a data hazard can be caused by the exact same instruction pair.
True in principle — one pair could contend for the register file (structural) and have a RAW dependency (data). They are independent failure modes; classic MIPS avoids the register-file structural clash by writing in the first half of the cycle and reading in the second.
A stall (bubble) always produces the wrong answer if we removed it.
False. Removing a necessary stall gives a wrong answer, but if forwarding already supplies the value, the stall is redundant and removing it is both correct and faster. The stall is only mandatory when no wire can deliver the value in time.
Predicting "not taken" is correct exactly half the time on real code.
False. "~50%" is a rough average over random branches; real code has loop back-edges (mostly taken) and error checks (mostly not-taken), so a static not-taken predictor's accuracy is workload-dependent and often worse than 50% on loop-heavy code.
A 2-bit saturating predictor can never mispredict twice in a row.
False. It requires two wrong-in-a-row before it flips its prediction — so it absolutely can mispredict twice consecutively (e.g. a branch that suddenly reverses its long-run behavior). The hysteresis just prevents a single anomaly from flipping it.
The branch penalty depends only on the pipeline depth, not on where the branch resolves.
False. The penalty equals the number of stages between fetch and the point of resolution. Resolving a branch earlier (e.g. in ID instead of EX) shrinks the penalty even with the same total depth.
If misprediction rate , the effective CPI equals the ideal CPI regardless of branch frequency.
True. In , setting zeroes the penalty term for any and . Perfect prediction hides branches entirely — at least in this simple model.
Spot the error
"We forward the MEM/WB value whenever MEM/WB.rd == ID/EX.rs, no other checks needed."
Missing two guards: you must also check
MEM/WB.RegWrite (the instruction actually writes a register) and rd ≠ 0 (register $zero is never a real destination). Forwarding a non-writing or $zero result corrupts the ALU input."Two earlier instructions write $t0; forward from MEM/WB because it's been in flight longer, so it's more settled."
Backwards. Program order says the most recent write before you is the correct one, and that value sits in the closer EX/MEM latch. The newest-wins rule gives EX/MEM priority; MEM/WB only forwards when the EX-hazard condition is false.
"A load-use stall lasts as long as it takes the value to reach WB, so it's 3 bubbles."
Wrong count. With forwarding, we only wait until the value exists (end of MEM), then bypass it into EX. The gap between "available end of MEM" and "needed start of EX" is a single cycle → exactly 1 bubble, not 3.
"To stall, we just freeze the PC and everything upstream sorts itself out."
Incomplete. Stalling needs three coordinated actions: disable the PC write, disable the IF/ID write (freeze the decoded instruction), and inject a bubble by zeroing the ID/EX control signals. Freeze without a bubble lets the stale instruction advance and do damage.
"Predict-not-taken means we do nothing until the branch resolves."
No — that describes stalling. Predict-not-taken keeps actively fetching the fall-through path speculatively; it only flushes those instructions if the branch turns out taken. Doing nothing wastes cycles even when the guess would have been right.
"A delayed branch slot is a wasted cycle we can't avoid."
The delay slot is always executed, but the compiler can fill it with a genuinely useful independent instruction (see Compiler instruction scheduling), turning the would-be penalty into productive work. It's only wasted if no such instruction exists (fill with NOP).
Why questions
Why can forwarding close the ALU-producer gap but not the load-use gap?
The ALU result exists at the end of EX — the same cycle the consumer sits in EX, so a wire delivers it in time. The load result exists only at the end of MEM, which is one cycle after the consumer's EX, and no wire can move data backward in time.
Why does the register file get read in ID but written in WB, creating so many hazards?
Decode must read operands early to feed EX, and the result isn't ready to write until the very end (after MEM for loads). This read-early/write-late split is what makes an earlier instruction's write lag a later instruction's read — the structural root of every RAW hazard.
Why does a 2-bit predictor beat a 1-bit one on loop branches specifically?
A loop back-edge is taken every iteration except the last. A 1-bit predictor flips on that single not-taken exit and then mispredicts again on re-entry — two misses per loop. The 2-bit counter's hysteresis absorbs the lone exit, keeping its long-run "taken" guess intact.
Why does the CPI formula multiply , , and rather than add them?
They are conditional stages of one event: a cycle is lost only if the instruction is a branch (fraction ) and it is mispredicted (probability ), and then it costs cycles. Independent probabilities compound multiplicatively; the product is added to the ideal CPI of 1.
Why do more history bits eventually stop helping a saturating counter?
Past 2 bits, a simple saturating counter captures little extra pattern while its larger table increases aliasing (different branches mapping to the same counter). The added state costs area and hurts accuracy through interference — diminishing returns beyond the hysteresis 2 bits already provide.
Why is stalling described as "brute-force" if it's always correct?
Correctness is cheap; throughput is the goal. Stalling guarantees the right answer by simply waiting, but every bubble is a lost instruction slot — it sacrifices the whole point of pipelining (1 instruction/cycle) to avoid needing clever hardware.
Edge cases
If a producer's destination is $zero ($0), should we ever forward it?
Never.
$zero is hard-wired to 0 and is never a meaningful result, so all forwarding conditions explicitly require rd ≠ 0. Forwarding it would overwrite a correct zero with garbage in the ALU input mux.What happens if two consecutive lw instructions target the same register — is it one stall or two?
The consumer of the second
lw (not the first lw itself) is what stalls; the second lw doesn't read the first's value in EX, it just writes its own. A load-use stall is triggered by the use in the following instruction's EX, so count stalls per use, not per load.Does a branch that is correctly predicted still cost any penalty?
In this simple model, no — a correct prediction fetches the right path continuously with zero flush, so its penalty term is 0. (Real machines may still pay a small BTB/target-fetch cost, but the classic CPI model treats a correct prediction as free.)
What is the effective CPI when every instruction is a branch and prediction is perfect?
With and , . Perfect prediction makes even an all-branch program run at ideal throughput; branch frequency only matters when predictions are wrong.
If forwarding hardware exists but a stall is still inserted for a load-use case, is the pipeline buggy?
No — that stall is mandatory, not redundant. Forwarding handles the value once it exists (end of MEM), but the one-cycle bubble is still required to align that availability with the consumer's EX. Correct designs do both together.
Can a control hazard and a data hazard occur on the very same cycle?
Yes. A branch instruction can depend on a register value still in flight (data hazard resolving its condition) while the pipeline simultaneously doesn't know the next PC (control hazard). They stack, and the total penalty is the combination of the stall/flush each demands.
Recall One-line self-test before you close this page
Name the single reason forwarding cannot save the load-use case. ::: The load's value is born at end of MEM, one cycle after the consumer's EX — you cannot forward data backward in time.
See also: Out-of-order execution and Tomasulo (how real cores dodge these stalls by reordering), Amdahl's Law and CPI analysis (where the CPI feeds into speedup), and Caches and memory hierarchy (a separate stall source layered on top of these).