Worked examples — Hazard detection units
This page is the drill ground for Hazard detection units. We will run one instruction sequence at a time through the pipeline and decide — for every case class — whether the hazard unit must stall, whether the forwarding unit can rescue us, or whether nothing needs doing at all.
Before we start, one tiny reminder so no symbol sneaks in undefined.

The scenario matrix
Every question this topic can ask lands in exactly one of these cells. The rest of the page fills each one.
| # | Case class | Trigger | What the hazard unit does | Example |
|---|---|---|---|---|
| A | No dependency | reads/writes don't overlap | nothing | Ex 1 |
| B | ALU-ALU RAW, 1 apart | prev instr result feeds next | nothing — forwarding fixes it | Ex 2 |
| C | Load-use, 1 apart | lw then instr reads its Rd |
STALL 1 cycle, then forward | Ex 3 |
| D | Load-use, 2 apart | one instr sits between them | nothing — forwarding fixes it | Ex 4 |
| E | Degenerate: Rd = $0 |
destination is register zero | nothing (never stall on $0) |
Ex 5 |
| F | Both Rs and Rt depend | instr reads the loaded reg twice | still one stall | Ex 6 |
| G | Store-use RAW | sw stores a just-produced value |
nothing — forwarding fixes it | Ex 7 |
| H | Control hazard, taken | branch changes PC | FLUSH wrong-path instr | Ex 8 |
| I | Control hazard, not-taken | branch falls through | nothing (guess was right) | Ex 9 |
| J | Unconditional jump (j/jr) |
PC always changes | FLUSH one instr | Ex 10 |
| K | Exam twist: stall + forward chained | lw then two dependents |
one stall + two forwards | Ex 11 |
Recall What is the ONLY data hazard that forces a stall?
A load-use hazard: lw in EX and the very next instruction reads its Rd. ::: Because the loaded value isn't ready until the end of MEM, so there is nothing to forward yet.
Example 1 — Cell A: no dependency at all
Steps
- List what
subreads:Rs = $4,Rt = $5. Why this step? The hazard unit only ever compares the reads of the younger instruction against the writes of older ones. If nothing is read that was just written, there is no dependency. - List what
addwrites:Rd = $3(andadd.RegWrite = 1, so it is a real destination). Why?$3is the only registeraddcould create a hazard on — and only becauseRegWrite = 1. - Compare: is
$3 == $4? No. Is$3 == $5? No. Why? The load-use condition and the forwarding condition both hinge on these equalities. Both fail.
Answer: Do nothing. Full speed, zero bubbles.
Recall Verify
Cycles used = 2 instructions overlapped normally, no extra cycles. Penalty = 0. ✓
Example 2 — Cell B: ALU→ALU RAW, forwarding rescues it
Steps
addfinishes computing$1at the end of its EX stage — the value is sitting in theEX/MEMregister. Sinceadd.RegWrite = 1, this value is a legitimate forwarding source. Why? An ALU result exists the moment EX completes; we don't need to wait for WB. TheRegWritecheck is what lets forwarding trust theRdfield.subreaches EX exactly one cycle later. At that instantadd's value is inEX/MEM. Why this matters: the forwarding unit can routeEX/MEMstraight into the ALU input. The data is already alive when needed.- Hazard unit check: is this a load?
add.MemRead = 0. So the load-use stall condition is false. Why? Stalling is reserved for the one case forwarding cannot cover. This isn't it.
Answer: No stall. Forwarding unit selects EX/MEM → ALU. Penalty = 0.

Recall Verify
The red arrow in the figure hands $1 from EX/MEM into sub's EX. Timing lines up → 0 stall cycles. ✓
Example 3 — Cell C: load-use, the classic stall
Steps
- Evaluate the load-use condition (the producer
lwis inID/EX, the consumeraddis inIF/ID): HereMemRead = 1, andRd = $2 = Rsofadd. Condition TRUE. Why this step? This is the exact combinational test the unit performs.IF/ID.Rs/IF/ID.Rtareadd's read registers;ID/EX.Rdislw's destination. When it fires, forwarding is provably too slow. - Assert the three stall signals defined in the reminder:
PCWrite = 0,IF/ID_Write = 0,Control_Mux = 0. Why?PCWrite = 0andIF/ID_Write = 0freeze the PC and the ID box soaddretries next cycle;Control_Mux = 0zeroes the control bits so the bubble going into EX does nothing. - Next cycle,
lw's data reachesMEM/WB;add(held back one cycle) now enters EX and forwards fromMEM/WB. Why it works: the one-cycle delay is exactly enough for the loaded value to become forwardable.
Answer: 1 stall cycle, then forward. Penalty = 1.

Recall Verify
Without detection add reads stale $2. With detection: 1 bubble → total cycles for the pair rises by exactly 1. Penalty = 1. ✓
Example 4 — Cell D: load-use, but two instructions apart
Steps
- When
addis in ID,lwis no longer in EX — it has advanced to MEM/WB. Why this step? The load-use condition checksID/EX.MemRead. Butlwis inMEM/WBnow, notID/EX. So the stall condition is false. addreaches EX whenlw's data sits inMEM/WB(withlw.RegWrite = 1, that box is a valid forward source). Why it matters: forwarding fromMEM/WB → ALUis legal and the data is ready.- Hazard unit does nothing; forwarding unit does the routing. Why? The gap instruction bought exactly the one cycle a load needs.
Answer: No stall. Penalty = 0. (This is why compilers reorder code to fill load delay slots.)
Recall Verify
Cycles: lw, or, add pipeline with no bubble → 0 extra cycles. ✓
Example 5 — Cell E: degenerate destination $0
Steps
- In MIPS, register
$0is permanently0; writing to it is discarded. Why this step? If a value never actually changes, there is no dependency to protect — reading$0always gives0, load or no load. - So the real condition includes an extra guard:
ID/EX.Rd ≠ 0. Why? Without it, any instruction reading$0after a boguslw $0would stall needlessly — pure waste. - Here
Rd = $0, so the guardID/EX.Rd ≠ 0evaluates to false, forcing the whole AND to FALSE. Why? The stall condition is a big AND; one false term (the$0guard) kills it no matter how many register numbers match. That is exactly what we want — never stall for a value that can't change.
Answer: No stall. Penalty = 0. Always add the Rd ≠ $0 guard.
Example 6 — Cell F: loaded register read twice
Steps
- Load-use condition uses
Rs == RdORRt == Rd. Why this step? It is a single boolean OR — either match is enough to fire it once. - Both disjuncts are true, but OR of true-or-true is still just one true signal. Why? The stall is a decision about the whole instruction, not per-operand. One instruction stalls one cycle regardless of how many operands clash.
- After the single bubble, forwarding delivers
$2to both ALU inputs fromMEM/WB.
Answer: 1 stall cycle (not two). Penalty = 1.
Recall Verify
OR(True, True) = True → one stall assertion → 1 bubble. ✓
Example 7 — Cell G: store-use RAW (a store needs a fresh value)
Steps
- Identify
sw's reads:sw $4, 0($5)reads$5(the address base) and$4(the data to store). Both areRs/Rtfields. Why this step? A store reads registers just like any instruction; the hazard machinery compares those reads against older writes. Hereadd.Rd = $4matchessw's data register. add.RegWrite = 1andadd.MemRead = 0, soaddis an ALU producer, not a load. Why it matters: the load-use stall condition needsMemRead = 1on the producer.addfails it → no stall. The store's ownRegWrite = 0is irrelevant here becauseswis the consumer, not the source.add's result sits inEX/MEM(thenMEM/WB) exactly whenswneeds it. The forwarding unit routes it intosw's pipeline register before the MEM stage writes memory. Why? Store data is only consumed at the MEM stage — even later than an ALU input — so forwarding always has time. Stores never cause a load-use stall.
Answer: No stall. Forward $4 into the store data path. Penalty = 0.
Recall Verify
Producer add: MemRead = 0 → load-use condition false → 0 stall cycles. ✓
Example 8 — Cell H: control hazard, branch taken
Steps
- Resolve the branch early (in ID): compare
$1and$2with a comparator, compute target with an adder. This setsBranch = 1and, since equal,BranchTaken = 1. Why this step? Resolving in ID means we learn "taken" one cycle after fetchingbeq— so only one wrong instruction (add) got fetched. - Flush condition (using the flush signals from the reminder): . Both true → flush. Why? We must cancel the already-fetched wrong-path instruction before it corrupts state.
- Set
IF/ID = NOP(turnaddinto a bubble) and load PC withLabel's address. Why? The bubble does nothing in later stages; the corrected PC fetchessubnext.
Answer: 1 flush (1 penalty cycle) with ID-stage resolution.

Recall Verify
Penalty with ID resolution = 1 cycle (one wrong fetch). If it resolved in EX instead, penalty would be 3. ID resolution → penalty 1. ✓
Example 9 — Cell I: control hazard, branch NOT taken
Steps
- Under the simple predict-not-taken policy, the pipeline keeps fetching sequentially.
Why this step? Guessing "not taken" means we speculatively fetch the fall-through instruction
add. - Branch resolves:
Branch = 1,BranchTaken = 0→ the guess was correct. Why it matters:addwas exactly the right instruction. Nothing to undo. - Flush condition: . No flush.
Answer: 0 penalty. This is why not-taken prediction is free on fall-through. (See 5.2.11-Branch-prediction for smarter guessing.)
Recall Verify
AND(1, 0) = 0 → no flush → penalty 0. ✓
Example 10 — Cell J: unconditional jump (j / jr)
Steps
- Recognise
j(jump to a fixed address) andjr(jump to the address held in a register) as instructions that change the PC unconditionally. Why this step? Unlikebeq, there is nothing to compare — the "taken" outcome is fixed at1. SoFlushfor a jump is justJump = 1(noBranchTakenterm needed). - In the cycle after fetching
j, we have already fetched the fall-throughadd. It is on the wrong path with certainty. Why it matters: since a jump is always taken, the speculatively fetchedaddis always garbage — there is no lucky "not-taken" case. - Flush that one instruction: set
IF/ID = NOP, and load PC withTarget(forj) or with the register value (forjr, which is known after ID reads the register). Why? One wrongly-fetched instruction, one bubble — same 1-cycle penalty as a taken branch when the jump target is computed in ID.
Answer: 1 flush (1 penalty cycle) for a jump resolved in ID. Unlike a branch, this cost is unavoidable — you can't predict-not-taken your way out of an always-taken jump.
Recall Verify
Jump always redirects → always flush 1 fall-through instr → penalty 1 (ID resolution). ✓
Example 11 — Cell K (exam twist): stall then two chained forwards
Steps
lw→addis a load-use hazard (Ex 3 pattern). Stall 1 cycle. Why this step?addreads$2whilelwis in EX → forwarding impossible → one bubble.- After the bubble,
addexecutes and forwards$2fromMEM/WB. Why? By now the loaded value is forwardable — exactly what the stall bought us. subreads$4(produced byadd, forward fromEX/MEM) and$2(fromlw, now in WB — read normally or forward fromMEM/WB). Both are pure ALU/available forwards → no extra stall. Why? Only load-use stalls.addis not a load, sosub's dependency on it needs only forwarding.
Answer: Total penalty = 1 stall cycle, plus forwarding into both add and sub. The exam trap is thinking sub adds a second stall — it does not.
Recall Verify
Load-use stalls fire only when the producer is a load in EX. Only lw→add qualifies → 1 stall total. ✓
Wrap-up: the decision you now run automatically
The flowchart shows how the forwarding unit picks its source: it prefers EX/MEM (the most recent producer) over MEM/WB, and it only forwards after confirming the producer's RegWrite = 1 and Rd ≠ 0.
This ties back to pipeline control signals (the stall/flush lines) and the control unit that hosts this logic. For the Hinglish walkthrough see 5.2.10 Hazard detection units (Hinglish).