Exercises — Hazard detection units
Before we start, study the reference diagram — every later solution points back to it, so keep it in view.

- The hazard detection unit (HDU) lives in ID — in the figure it is the box labelled "HDU (ID)" drawn in cyan with a dashed border. It reads the register numbers of the instruction being decoded and compares them against the instruction now in EX.
- The forwarding unit lives near EX — the box labelled "FWD (EX)" drawn in amber with a solid double border. It steers already-computed results straight into the ALU inputs, skipping the register file. The two amber arrows are text-labelled "EX/MEM → ALU" and "MEM/WB → ALU" — these are the forwarding paths that carry every later "forwarding handles it" claim.
- The stall is the arrow text-labelled "STALL: PCWrite=0, IF/ID_Write=0" (drawn white, dotted) running from the HDU back toward the PC and IF/ID register.
Level 1 — Recognition
Exercise 1.1
Classify each situation as a data, control, or structural hazard.
- (a)
beq $1,$2,Lfollowed by the next sequential instruction being fetched before the branch resolves. - (b)
add $1,$2,$3immediately followed bysub $4,$1,$5. - (c) A single-port memory that must serve an instruction fetch (IF) and a
lwmemory access (MEM) in the same cycle.
Recall Solution 1.1
- (a) Control hazard — the pipeline fetched an instruction before it knew whether the branch is taken.
- (b) Data hazard (RAW:
subreads$1, whichaddwrites). In the reference figure, this is exactly what the arrow labelled "EX/MEM → ALU" resolves without any stall. - (c) Structural hazard — two instructions compete for one hardware resource (the memory port) in the same cycle.
Exercise 1.2
The HDU is described as combinational logic. Which one property does that guarantee?
- It stores state across cycles.
- Its outputs settle within the same clock cycle, before the next clock edge.
- It runs on its own private clock.
Recall Solution 1.2
Answer: 2. Combinational logic has no memory; given the inputs (the register fields already latched in the pipeline registers) it produces stall/flush outputs within the cycle, so the correct control decision is ready before the next clock edge commits any value.
Level 2 — Application
Exercise 2.1
Apply the load-use detection formula. Given:
ID/EX.MemRead = 1ID/EX.RegisterRd = 2IF/ID.RegisterRs = 5IF/ID.RegisterRt = 2
Does the HDU assert a stall? Show which clause fires.
Recall Solution 2.1
The detection condition is
Substituting: (true). Then , → false; , → true. The OR is true, the AND is true.
Result: STALL asserted. The RegisterRt clause fired. In the reference figure this is the cyan arrow (ID/EX → HDU) reading MemRead, triggering the white "STALL" arrow.
Exercise 2.2
For the same instruction the HDU asserts a stall. List the exact values it drives on the three control signals, and say in one line what each freezes.
Recall Solution 2.2
PCWrite = 0— freezes the PC, so no new instruction is fetched.IF/ID_Write = 0— freezes the IF/ID register, so the dependent instruction stays in ID to retry.Control_Mux = 0— as defined above, selects the all-zeros input into ID/EX, inserting a NOP bubble so the frozen instruction doesn't corrupt EX.
Exercise 2.3
Same as 2.1 but now ID/EX.RegisterRd = 0. A real MIPS HDU also checks RegisterRd ≠ 0. Does it stall?
Recall Solution 2.3
Register $0 is hardwired to zero — a "load into $0" produces no usable value and nobody truly depends on it. So the guard RegisterRd ≠ 0 is added. Here , so the guard is false, the whole condition is false, and no stall is asserted. Proceed normally.
Level 3 — Analysis
Exercise 3.1
Walk the load-use sequence cycle-by-cycle and count total cycles.
lw $2, 20($1) ; I1
add $4, $2, $5 ; I2
The pipeline is 5 stages; a stall inserts exactly one bubble. Assume forwarding from MEM/WB is available. How many cycles until add finishes WB, and how many bubbles were inserted?
Recall Solution 3.1
With the stall, the schedule is (matching the parent note). Only these two instructions exist in the program, so from cycle 4 onward nothing new is fetched — the IF column is empty simply because the program has ended, not because of a stall:
| Cycle | IF | ID | EX | MEM | WB |
|---|---|---|---|---|---|
| 1 | lw | ||||
| 2 | add | lw | |||
| 3 | add | NOP | lw | ||
| 4 | (none) | add | NOP | lw | |
| 5 | (none) | add | NOP | lw | |
| 6 | (none) | add | NOP | ||
| 7 | (none) | add |
Why does IF show add again in cycle 3 rather than going empty? During the stall the HDU sets PCWrite = 0, so the PC does not advance — it still holds the address of add. The IF stage has no memory of its own; every cycle it simply fetches whatever address the PC currently points at. With the PC frozen on add's address, IF re-fetches the same add in cycle 3. Meanwhile IF/ID_Write = 0 freezes the IF/ID register, so the copy of add already in ID is held for its retry. The re-fetched add is harmless: it lands in IF/ID again next cycle and simply overwrites the identical value. So a frozen PC produces a repeated fetch, not an empty slot.
add reaches WB in cycle 7. One bubble was inserted (cycle 3's NOP). In cycle 5, lw's data is in MEM/WB and the forwarding unit feeds it into EX for add — this is the arrow labelled "MEM/WB → ALU" in the reference figure doing its job.
Exercise 3.2
Follow the combined decision tree for an instruction in ID reading RegisterRs = 3. State:
- (a)
EX/MEM.RegisterRd = 3,EX/MEM.RegWrite = 1,ID/EX.MemRead = 0. - (b)
EX/MEM.RegisterRd = 7,MEM/WB.RegisterRd = 3,MEM/WB.RegWrite = 1,ID/EX.MemRead = 0. - (c)
ID/EX.RegisterRd = 3,ID/EX.MemRead = 1.
For each, say whether forwarding or the HDU (stall) resolves it.
Recall Solution 3.2
- (a) EX-hazard match with RegWrite → forward from EX/MEM (the arrow labelled "EX/MEM → ALU" in the reference figure). No stall.
- (b) No EX match, but MEM-hazard match with RegWrite → forward from MEM/WB (the arrow labelled "MEM/WB → ALU"). No stall.
- (c) Source of a load still in EX → data not ready → HDU stalls one cycle (the "STALL" arrow), then forwarding takes over.
Exercise 3.3
A naive 5-stage design resolves branches in EX. A taken branch must flush every instruction fetched since the branch. How many wrong-path instructions get flushed (the branch penalty)? Then state the penalty if the branch is resolved in ID instead.
Recall Solution 3.3
Counting convention used on this page: the branch penalty is the number of wrong-path instructions that were fetched and must be discarded. The count depends on which stage first knows the branch outcome, because the pipeline keeps fetching one new instruction per cycle until then.
- Resolve in EX: the branch travels IF→ID→EX, and it only learns its outcome at the end of EX. By that point two younger instructions have been fetched (one during the branch's ID cycle, one during its EX cycle), both wrong → penalty = 2. (You will sometimes see "3" quoted — that convention also counts the cycle in which the correct target is finally fetched as lost, i.e. it measures lost cycles rather than discarded instructions. Same pipeline, different thing being counted. This page counts discarded instructions, so we use 2.)
- Resolve in ID: with a comparator and a target adder placed in ID, the outcome is known one stage earlier, so only one younger instruction (the PC+4 fetch during the branch's ID cycle) has been fetched → penalty = 1.
This is the "penalty 2→1" (or "3→1" under the lost-cycle convention) improvement the parent note describes. See 5.2.11-Branch-prediction for how prediction pushes this toward 0.
Level 4 — Synthesis
Exercise 4.1
Design the complete output interface of a minimal HDU. Inputs: ID/EX.MemRead, and the register-number fields ID/EX.RegisterRd, IF/ID.RegisterRs, IF/ID.RegisterRt, with the RegisterRd ≠ 0 guard included. Give the boolean for the internal Stall signal and the three control outputs the HDU drives when a stall is needed.
Recall Solution 4.1
First the internal detection signal: Then the HDU drives the full output interface from that one signal:
- — so
PCWrite = 0exactly whenStall = 1(freeze the PC). - — so
IF/ID_Write = 0exactly whenStall = 1(hold the dependent instruction in ID). - — so
Control_Mux = 0exactly whenStall = 1(inject the NOP bubble into ID/EX).
When Stall = 0 all three are 1 and the pipeline advances normally. This mirrors the Verilog sketch in the parent note, with the RegisterRd != 0 guard added and the two write-enables plus the bubble mux spelled out. It is purely combinational: no clocked storage, outputs valid within the cycle.
Exercise 4.2
Consider back-to-back loads:
lw $2, 0($1) ; I1
lw $3, 0($2) ; I2 (address uses $2!)
add $4, $3, $2 ; I3
Identify every stall the HDU inserts and justify each.
Recall Solution 4.2
- I1→I2: I2 computes its address from
$2, which I1 loads. When I1 is in EX (MemRead=1,RegisterRd=2) and I2 is in ID readingRegisterRs=2→ stall 1 cycle. Address computation cannot use un-arrived load data, and forwarding to the address ALU can't precede MEM. - I2→I3: I3 reads
$3(loaded by I2) and$2. When I2 is in EX withMemRead=1,RegisterRd=3, and I3 is in ID reading$3→ stall 1 cycle. After the bubble,$3forwards from MEM/WB;$2(from I1, long done) forwards or reads normally. - Total: 2 stalls (2 bubbles).
Level 5 — Mastery
Exercise 5.1
A program runs on a branch-resolved-in-ID pipeline. Out of 1000 instructions:
- 200 are loads, and half of them are immediately followed by a dependent use (each such case = 1 stall).
- 150 are branches; 40% are taken (each taken branch = 1 flush = 1 lost cycle).
- Base CPI (cycles per instruction) with no hazards = 1.
Compute the effective CPI and the total cycle count. (CPI = total cycles ÷ instructions.)
Recall Solution 5.1
- Load-use stalls: stall cycles.
- Branch flushes: flush cycles.
- Total penalty cycles .
- Total cycles .
- Effective CPI .
Interpretation: hazards added 16% overhead. Moving branch resolution earlier (or adding prediction) attacks the 60-cycle branch term; scheduling an independent instruction after each load attacks the 100-cycle stall term.
Exercise 5.2
Reorder the code below (a legal instruction schedule) so the HDU inserts zero stalls, without changing the program's result. $6,$7,$8 are independent of the load.
lw $2, 0($1) ; I1
add $4, $2, $5 ; I2 (uses $2 -> load-use stall)
sub $6, $7, $8 ; I3 (independent)
Recall Solution 5.2
Move the independent sub between the load and its use:
lw $2, 0($1) ; I1
sub $6, $7, $8 ; I3 (independent - fills the delay slot)
add $4, $2, $5 ; I2 ($2's data is now ready via forwarding)
When add reaches ID, the lw is already in MEM (not EX), so MemRead in ID/EX is 0 for the compare — no stall condition — and $2 forwards from MEM/WB (the arrow labelled "MEM/WB → ALU" in the reference figure). Stalls: 0. This is compiler scheduling: the same job the HDU would otherwise do with a bubble, done for free by reordering.
Recall Self-test recap
Load-use is the only data hazard that forces a stall ::: because a load's data doesn't exist until after MEM, so no earlier stage can forward it in time for the very next instruction. Stall vs flush ::: a stall holds a still-valid instruction to retry later while nothing is discarded; a flush cancels a wrong-path instruction that must never execute. Why the HDU sits in ID ::: registers are read in ID, so source dependencies are known before EX would use stale data. The three stall signals ::: PCWrite=0 freezes the PC, IF/ID_Write=0 holds the instruction in ID, Control_Mux=0 injects a NOP bubble into ID/EX. Why a frozen PC causes IF to re-fetch the same instruction ::: IF has no memory of its own and simply fetches whatever address the PC holds, so a frozen PC yields a repeated fetch rather than an empty slot.
See also: 5.2.9-Forwarding-unit, 5.2.7-Pipeline-control-signals, 5.1.3-Control-unit, 5.2.11-Branch-prediction.