5.2.10 · D5Processor Datapath & Pipelining

Question bank — Hazard detection units

2,789 words13 min readBack to topic

Before the questions, this page builds every piece of vocabulary it uses from scratch. Read this setup once; the whole bank rests on it.

The five lanes (pipeline stages)

Picture the pipeline as five lanes, and an instruction sliding one lane to the right every clock tick:

  • IFinstruction fetch: grab the next instruction from memory.
  • IDinstruction decode: figure out what it is and read its source registers.
  • EXexecute: the ALU does the arithmetic.
  • MEMmemory: touch data memory (only loads and stores do real work here).
  • WBwrite-back: write the result into the register file.

Pipeline registers (the ID/EX-style names)

Between each pair of lanes sits a pipeline register — a latch that carries an instruction's data from one stage into the next at every clock edge. They are named by the two stages they sit between:

A field inside such a register is written with a dot: ID/EX.MemRead means "the MemRead control bit of whatever instruction is currently in EX".

Register fields Rs, Rt, Rd

Every MIPS instruction names up to three registers by number:

The control signals used in this bank

Where a bubble comes from — the control mux

Forwarding vs. stalling (the two cures)

  • Forwarding = a shortcut wire that carries a freshly-computed value backward to an instruction that needs it now, instead of waiting for it to reach the register file. Handled by the forwarding unit out of EX/MEM or MEM/WB.
  • Stall = freezing the front of the pipeline for one tick and injecting a bubble so a not-yet-ready value has time to appear.

The load-use detection condition, spelled out

Every stall in this bank comes from one Boolean expression. Here it is fully written:

Why exactly one bubble is enough

Trace the load lw and its consumer add, tick by tick, with a bubble inserted:

Cycle IF ID EX MEM WB
1 lw
2 add lw
3 add bubble lw
4 bubble lw
5 add bubble lw

In cycle 3 the hazard unit detects lw in EX and add in ID, so it freezes add (holds it in ID) and injects a bubble. By cycle 5 the load's value has reached MEM/WB, which is exactly a stage the forwarding unit can bypass from into add's EX. One bubble is the minimum delay that lifts the load into a forwardable position — hence exactly one suffices.

The hazard detection unit only ever fires when forwarding cannot save you. That single sentence is the key that unlocks most of these traps.


True or false — justify

The forwarding unit and the hazard detection unit are the same block of logic
False. They are separate combinational blocks: forwarding chooses which wire feeds the ALU (data routing), while hazard detection decides whether to stall/flush (control flow). They coordinate but have different jobs and different outputs.
Every data hazard forces a pipeline stall
False. Most RAW (read-after-write) hazards between arithmetic instructions are cured for free by forwarding — the result exists at the end of EX and can be bypassed. Only the load-use case, where the value literally doesn't exist yet, needs a stall.
The hazard detection unit lives in the EX stage where the ALU is
False. It lives in the ID stage, because ID is where an instruction's source registers (Rs, Rt) are decoded. Detecting the dependency in ID lets us stall before EX ever consumes a stale value.
The hazard detection unit must be combinational, not clocked
True. The stall/flush decision has to be ready within the same cycle the offending instruction is in ID — before the clock edge latches wrong values. A clocked (sequential) unit would decide one cycle too late.
A load-use stall of one bubble is always enough for the dependent instruction to get correct data
True (for a classic 5-stage MIPS with forwarding). One bubble pushes the load into MEM/WB, and from there the forwarding unit can bypass the loaded value into the dependent instruction's EX. Without forwarding you'd need more stalling.
Flushing an instruction and stalling an instruction are the same operation
False. A stall holds an instruction in place and re-tries it next cycle (it's still on the correct path). A flush discards an instruction because it was on the wrong path (e.g. after a taken branch) — it can never be useful, so we kill it permanently.
Writing to register 0 in MIPS can create a false load-use hazard
True — and that's exactly why real detection logic adds an Rd != 0 check. Register 0 is hardwired to zero; a "dependency" on it is meaningless, so treating it as a hazard would insert pointless bubbles.
WAR and WAW hazards are common in the simple 5-stage in-order pipeline
False. In an in-order pipeline instructions read and write in program order, so WAR (write-after-read) and WAW (write-after-write) can't arise. They only matter in out-of-order execution, which reorders reads and writes.
Structural hazards can be detected by the same load-use comparison logic
False. Structural hazards come from a resource conflict (e.g. one memory port serving both IF and MEM in the same cycle), not a register-number match. They're avoided by duplicating resources (separate I- and D-memory), a different mechanism entirely.
Resolving branches in the ID stage instead of EX reduces the number of instructions we must flush
True. If the branch outcome is known in ID, only the single instruction fetched behind it (in IF/ID) is wrong. Resolving later in EX means two or three wrong instructions have already entered the pipe and all must be flushed.

Spot the error

Claim: "We forward the load's result straight from the ID/EX register into the next instruction's ALU, so no stall is ever needed." — what's wrong?
The load hasn't read memory yet when it's in EX; its result only exists at the end of MEM. There is nothing in ID/EX to forward. That timing gap is the whole reason a load-use stall exists.
Claim: "On a stall we set PCWrite = 1 so the PC keeps moving." — what's wrong?
A stall needs PCWrite = 0 (the freeze setting from the definitions above). If the PC advanced during the stall we'd fetch and then lose the next instruction. Freezing the PC and IF/ID is what makes the front of the pipe wait.
Claim: "To insert a bubble we freeze the ID/EX control signals so they hold their old value." — what's wrong?
A bubble is made by zeroing the ID/EX control signals — switching the control mux to its all-zero input — turning that slot into a NOP. Holding the old control values would let a real operation slip through and corrupt state.
Claim: "During a load-use stall we hold the load instruction in ID." — what's wrong?
We hold the dependent instruction (the consumer) in ID; the load itself moves forward normally into EX/MEM so its data can become available. Holding the load would defeat the purpose — it needs to advance to produce the value.
Claim: "A flush after a taken branch just requires setting Stall = 1." — what's wrong?
Stalling would keep the wrong-path instruction and re-run it — but it's on a path we're abandoning. We need a Flush: convert IF/ID to a NOP so the wrong instruction is discarded, then fetch the branch target.
Claim: "The detection condition only checks Rs, since that's the first operand." — what's wrong?
The condition ANDs MemRead with an OR over both Rs and Rt, because the dependent instruction could read the load's destination through either source operand. Missing Rt would let a real hazard slip past on second-operand dependencies.
Claim: "Since forwarding handles EX/MEM and MEM/WB hazards, the hazard unit can check those too and skip the load case." — what's wrong?
Backwards. Forwarding already resolves the EX/MEM and MEM/WB cases, so the hazard unit needn't stall for them. The one case forwarding can't fix — load result not yet produced — is precisely the case the hazard unit must catch.

Why questions

Why does the hazard detection unit specifically watch ID/EX.MemRead rather than the opcode?
MemRead = 1 is the clean, decoded flag that identifies a load — the only instruction whose result isn't ready by end of EX. Watching one control bit is cheaper and faster than re-decoding the opcode inside the hazard unit.
Why must the stall decision finish inside a single clock cycle?
Because at the next rising edge, the pipeline registers latch new values. If the Stall signal isn't asserted in time, the stale operand rides into EX and the wrong result is committed — unrecoverable in a simple pipeline.
Why is forwarding preferred over stalling whenever it's possible?
Forwarding costs zero extra cycles — it just reroutes an existing value through a mux. Stalling wastes a full cycle (a bubble) of throughput. So we stall only when the needed data physically doesn't exist yet.
Why does moving branch resolution earlier (into ID) need extra hardware, and is it worth it?
You add a comparator and a target-address adder in ID. It's usually worth it: the branch penalty drops from ~3 flushed instructions to 1, saving cycles on every taken branch, which happen often in loops.
Why can't the hazard detection unit alone eliminate all pipeline stalls?
It can only detect and react; it can't invent data that doesn't exist. For a load-use dependency the value simply isn't available yet, so some delay is unavoidable — the unit just minimizes it to one bubble instead of many.
Why does the control-signal path need a mux that can force all signals to zero?
Because that zeroing mux is the physical mechanism for inserting a bubble. Selecting the all-zero input turns the ID/EX slot into a NOP so a stalled or flushed instruction cannot alter registers or memory.
Why is branch prediction (5.2.11-Branch-prediction) a complement to, not a replacement for, control-hazard flush logic?
Prediction guesses the path so we can fetch speculatively, but every wrong guess still needs the Flush machinery to discard the mis-fetched instructions. Prediction reduces how often we flush; it doesn't remove the need to flush.

Edge cases

If the load's destination register equals the dependent instruction's Rs and Rt (both operands), how many bubbles are inserted?
Still exactly one. The condition is a single OR — it fires once regardless of whether one or both operands match — and one bubble is enough to let the load reach a forwardable stage (MEM/WB by cycle 5, as the timing table showed).
What happens if the instruction after a load reads a different register than the load's Rd?
No hazard — the Rd == Rs / Rd == Rt comparison fails, so Stall = 0 and the pipeline runs at full speed. The mere presence of a load nearby is harmless without a matching register.
A load is followed by a store that uses the loaded value as its data-to-store — is it still a load-use hazard?
Yes, if the store reads the load's Rd through Rt (the store-data register). The consumer being a store doesn't change the timing: the value still isn't ready, so a stall is required just the same.
What if a taken branch is immediately followed by another branch on the wrong path?
The wrong-path branch is flushed like any other wrong-path instruction before it can affect the PC. Only after fetching the correct target do we begin evaluating the next real branch.
A branch is resolved in ID and turns out not taken — what does the hazard unit do?
Nothing special. The sequentially-fetched next instruction (PC+4) was already the correct one, so Flush = Branch AND BranchTaken evaluates false and no bubble is inserted.
Two independent loads back-to-back, where the second instruction after the first load uses the value — is a stall needed?
No. By the time the consumer reaches ID, the first load is already in MEM/WB, so forwarding can supply the value. The load-use stall only triggers when the consumer is directly behind the load (one cycle apart).
If ID/EX.MemRead = 1 but the loaded value is never used by any following instruction, does the unit stall?
No. With no register match on Rs or Rt, the detection condition is false. A load with no dependent consumer flows through with zero penalty — the unit reacts to dependencies, not to loads themselves.
Recall One-line summary to lock it in

The hazard detection unit stalls only for the load-use case (value not yet produced), flushes only for wrong-path branch instructions, and stays silent whenever the forwarding unit can already reroute the data.