Pipeline Hazards & Their Fixes
A hazard is any situation that stops the next instruction from entering the pipeline on the next clock cycle. There are exactly three kinds — the exam loves “explain and exemplify a hazard, then give its solution.”
| Hazard | Cause | Primary fix |
|---|---|---|
| Structural | two instructions want the same hardware in the same cycle | duplicate the resource |
| Data | an instruction needs a result not yet produced by an earlier one | forwarding, else stall |
| Control | the next address depends on a branch still in the pipe | stall / predict / delayed branch |
1. Structural hazard
Occurs when two instructions at different stages need the same hardware resource at the same time. The classic case: a machine with a single memory for both instructions and data. In one cycle instruction 4 wants to be fetched (IF) while instruction 1 wants to access data (MEM) — both hit the one memory.
2. Data hazard
Occurs when an instruction needs a value that an earlier, still-executing instruction hasn't written back yet. Example — sub reads $s0, which add is still computing:
add $s0, $t0, $t1 # produces $s0 (ready only after its WB) sub $t2, $s0, $t3 # needs $s0 in its EX stage
When sub reaches EX, add is only in MEM — it hasn't written $s0 to the register file yet. Reading the register now gives the old value.
Fix A — Forwarding (bypassing) · the preferred fix
The result actually exists the moment add finishes EX — it's sitting in the EX/MEM pipeline register. So route it directly from there into sub's ALU input, without waiting for WB. No stall needed.
Fix B — Stall (bubble) · when forwarding can't help
A lw produces its value only after MEM, one stage later than an ALU op. If the very next instruction needs it, even forwarding is one cycle too late — so the pipeline must stall for one cycle (insert a “bubble”), then forward.
lw $s0, 0($t1) # value ready only after MEM sub $t2, $s0, $t3 # needs $s0 in EX — one cycle too early
3. Control (branch) hazard
Occurs when the pipeline must fetch the next instruction before it knows whether a branch is taken. beq resolves its condition only in a later stage, but IF wants a new instruction on the very next cycle — which address should it fetch?
beq $t0, $t1, LABEL # taken? not known yet ??? # what do we fetch next cycle?
- Stall — freeze IF until the branch resolves (simple, wastes cycles).
- Branch prediction — guess (e.g. “not taken”), keep fetching; if wrong, flush the wrongly-fetched instructions and restart. Cheap when the guess is usually right.
- Delayed branch — always execute the instruction right after the branch (the “branch delay slot”); the compiler puts something useful there.
- Define each hazard in one line and give a concrete example.
- Structural → duplicate the resource (split instruction/data memory).
- Data → forward from a pipeline register; stall only for load-use.
- Control → stall, predict, or delayed branch.