When the line jams: hazards and forwarding

After this lesson you will take any short straight-line fragment, name its hazards, count the stall cycles with and without forwarding, and fill the cycle-by-cycle table the way an exercise expects — including the one stall that no wire can remove.

In 1982 a Stanford team called their processor MIPS: "Microprocessor without Interlocked Pipe Stages". The name was a design decision: the hardware had no stall logic at all. If an instruction needed a result the previous one had not finished, nothing stopped it from reading garbage — unless the compiler had put enough empty instructions in between.

The notes play compiler on page 25: sub x2, x1, x3, one nop, then and x12, x2, x5, "Now x2 is ready". It is not. Two questions for the next forty-five minutes: how many empty slots does that pair really need — and why does one extra wire bring the answer down to zero, for everything except a load?

Three ways a pipeline can jam

Lesson 1 ran the five-stage pipeline under two silent assumptions, spelled out by the notes on page 20: every instruction is independent of the others, and there are no branches or jumps. Drop either and the overlap stops being free. The word for what goes wrong is hazard.

Hazard — a situation in which overlapped execution would give a different result from executing the instructions one at a time, so the pipeline must either produce a wrong value or delay an instruction. The delay is a stall, also called a bubble: a cycle in which a stage does no useful work.

Each stall is a cycle in which no instruction completes — the reason the real CPI of a pipeline sits above 1 (lesson 3). Think of the assembly line as a laundry: washer, dryer, folding table, ironing board, closet, one load in each. Three things can jam it (notes, pages 20–21):

Data hazards are this lesson. Here is the notes' example (page 21), written as the 2026 exam paper writes code — capital mnemonics, registers R1, R2 … and offset(Rbase) addressing (the 2024 and 2025 papers put a dollar sign in front of every register, $R1, $F2: same thing); the notes spell it lw x2, 0(x1) / add x3, x2, x4, and x2 is R2, register number 2.

I0   LD   R2, 0(R1)      # R2 = memory[R1 + 0]
I1   ADD  R3, R2, R4     # needs R2
C1 C2 C3 C4 C5 C6 I0 LD R2 IF ID EX ME WB I1 ADD R3,R2 IF ID EX ME WB R2 read too early
Fig. 1. Born in cycle 4, needed in cycle 3. R2 exists only at the end of the load's ME stage and reaches the register file in WB; the add reads it in ID. The orange arrow is the RAW dependence pointing backwards in time. (ME = MEM, shortened to fit the cells.)

Three orders of access, one of them real

Two instructions that touch the same register can collide in three orders, named after the order the accesses must keep; the "dependency analysis" table of Exercise 1 has a second name for each:

A fact the notes leave out and examiners like to ask: in the five-stage in-order pipeline only RAW can happen. Every instruction reads in ID and writes in WB, three stages later, and none overtakes another (Fig. 2).

C1 C2 C3 C4 C5 C6 I0 IF ID EX ME WB I1 IF ID EX ME WB R5 read first, always

WAR: ADD R4, R5, R6 reads R5 in C2, SUB R5, R7, R8 writes it in C6

C1 C2 C3 C4 C5 C6 I0 IF ID EX ME WB I1 IF ID EX ME WB R2 writes in order

WAW: SUB R2, R1, R3 and ADD R2, R7, R8 both write R2, in C5 and C6

Fig. 2. Why WAR and WAW are impossible here. Left: reads happen in ID and writes in WB, so the older read is four cycles ahead of the younger write. Right: all writes go through the one write port of WB in program order, so the later value lands last. Both return in lesson 7, with multi-cycle units and out-of-order completion. The dependences still exist in the code — Exercise 1 asks you to list them; whether they become hazards depends on the machine.

Structural hazards are absent for a different reason: they were designed away. The datapath of lesson 1 has separate instruction and data memories, a register file with two read ports and one write port, and its own adder for PC + 4, so no two stages compete for a resource. The notes turn this into a slogan that is too strong (red pen below). The papers never make the slogan; what they state, as a machine assumption of every VLIW exercise of 2024–2026, is one of those design decisions: "in the Register File, it is possible to read and write at the same address at the same clock cycle" — the split-cycle register file of the next section.

One more thing an examiner likes to ask: which classes can hardware remove for good? Structural hazards, yes — duplicate the resource and they are gone. Data hazards it can only reduce: forwarding takes most of the stalls, and this lesson ends with the one it cannot take. Control hazards it can only guess around: prediction, lessons 4–6.

In the five-stage in-order pipeline only two things can go wrong: a RAW dependence and a branch. Everything else was designed away.

The distance rule: what a RAW costs without forwarding

Start with the plain machine: no forwarding, but one cheap trick in the register file that every exam paper assumes. The notes describe it on page 22: the register file is written in the first half of the clock cycle and read in the second half, so an instruction in WB and one in ID exchange a value within the same cycle. This is the split-cycle register file — the "read and write at the same address at the same clock cycle" of the exam texts.

C1 C2 C3 C4 C5 C6 C7 I1 IF ID EX ME WB I2 IF ID EX ME WB I3 IF ID EX ME WB I4 IF ID EX ME C5 write read I1 WB I4 ID same cycle, no stall
Fig. 3. The notes' Figure 5 (p. 22) redrawn. In cycle 5, I1 writes in the first half of the cycle and I4 reads in the second half (dashed arrow): the consumer three instructions after the producer needs no stall and no extra hardware.

Example 2 of the notes, cycle by cycle

Now put the consumer right behind the producer — Example 2 of the notes (page 23), the pair of every exercise of this kind:

I0   SUB  R2, R1, R3     # R2 = R1 - R3
I1   AND  R12, R2, R5    # needs R2 at once

Walk it. Cycle 3: SUB is in EX computing R2; AND is in ID and wants R2, but the register file still holds the old value. Cycle 4: SUB is in MEM, R2 sits in the EX/MEM pipeline register; AND is held in ID. Cycle 5: SUB writes R2 in the first half of the cycle, AND reads it in the second half and moves to EX in cycle 6. Two cycles lost. The table is the form of every such exercise: a row per instruction, a column per clock cycle.

Example 2 without forwarding: two stalls, 8 clock cycles (as in the notes, p. 23). "stall": held in ID while the bubble travels on.
12345678
I0 SUB R2, R1, R3IFIDEXMEMWB
I1 AND R12, R2, R5IFIDstallstallEXMEMWB

The example generalises into a rule the notes never state but all their tables obey. Call \(d\) the distance from producer to consumer: \(d = 1\) for the very next instruction, \(d = 2\) with one instruction between them, and so on.

  1. Clock the producer. Fetched in cycle \(t\), it goes through IF, ID, EX, MEM, WB in cycles \(t, t+1, t+2, t+3, t+4\); its result enters the register file in WB:

    \[ \text{write} = t + 4 . \]
  2. The consumer is \(d\) instructions later: fetched in cycle \(t + d\), it reads its registers in ID in cycle \(t + d + 1\) if nothing holds it.

  3. Hold it \(s\) cycles in ID; the read happens in cycle \(t + d + 1 + s\). With the split-cycle register file a read in the same cycle as the write sees the new value, so

    \[ t + d + 1 + s \ge t + 4 . \]
  4. Solve for \(s\); a stall count cannot be negative:

    \[ s = \max(0,\ 3 - d) . \]

    \(d = 1\): two stalls (Example 2). \(d = 2\): one. \(d = 3\): none — the I1 to I4 pair of Fig. 3.

  5. Without the split-cycle file the read must come strictly after the write, \(t + d + 1 + s \ge t + 5\), so \(s = \max(0,\ 4 - d)\): one more stall at distances 1 to 3. That is what the papers' assumption line saves you.

\[ \text{stalls} = \max(0,\ 3 - d) \]
\(d\)
distance in instructions from the producer to the consumer (1 = the next instruction)
\(3\)
the stages between the read in ID and the write in WB

A load obeys the same rule without forwarding: its value also reaches the register file in WB. Loads and ALU instructions part ways only once forwarding exists.

A stall freezes everything behind it

The two-row table hides one thing: when an instruction is held in ID, the instruction behind it cannot enter ID and is held in IF, and everything further back waits too. The notes' Figure 9 (page 26) has five instructions that all read R2 after SUB writes it. Recomputed:

I0   SUB  R2, R1, R3
I1   AND  R12, R2, R5    # d = 1: 2 stalls
I2   OR   R13, R6, R2    # d = 2, held in IF behind I1: 0
I3   ADD  R14, R2, R2    # d = 3: 0
I4   SD   R15, 100(R2)   # d = 4: 0
The notes' Figure 9, p. 26, without forwarding: 11 cycles, 2 stalls. A repeated IF is an instruction held in fetch while the one ahead stalls in ID.
1234567891011
I0 SUB R2, R1, R3IFIDEXMEMWB
I1 AND R12, R2, R5IFIDstallstallEXMEMWB
I2 OR R13, R6, R2IFIFIFIDEXMEMWB
I3 ADD R14, R2, R2IFIDEXMEMWB
I4 SD R15, 100(R2)IFIDEXMEMWB

Only I1 pays: by the time I2 reaches ID in cycle 6 the write is done, and I3 and I4 are later still. By hand, work row by row with one rule — an instruction enters a stage only in the cycle after the one ahead of it has left that stage — and I1's stalls push I2 along by themselves. The total is the cycle of the last WB: 11 = 5 instructions + 4 cycles of fill + 2 stalls, the formula sheet's \(\text{cycles} = IC + 4 + \text{stalls}\); lesson 3 turns it into CPI and seconds.

Forwarding: one wire and one comparison

Look at Example 2 again. R2 is computed at the end of cycle 3 and sits, finished, in the EX/MEM pipeline register during cycle 4 — exactly the cycle in which AND would like to execute. The two stalls carry an existing value through MEM, WB and back out of the register file. Forwarding (the notes also say bypassing) adds a wire from the pipeline register straight to the ALU input, and a multiplexer that chooses between the register-file value and the wire.

before: no forwarding C1 C2 C3 C4 C5 C6 C7 C8 SUB IF ID EX ME WB AND IF ID EX ME WB 8 cycles after: EX/EX path C1 C2 C3 C4 C5 C6 SUB IF ID EX ME WB AND IF ID EX ME WB 6 cycles
Fig. 4. Example 2 before and after. Above: two bubbles, then the value read from the register file in cycle 5 (dashed arrow). Below: the magenta arrow is the EX/EX forwarding path — the ALU result of SUB enters the ALU of AND one cycle later, and the stalls are gone.

The three paths, in the names the course uses

The notes name each path by the stage the value comes from and the stage it goes to (pages 23–24). All three take the value from a pipeline register, never from the register file:

Store data from an ALU producer, and the base register of a load or store, go through the same ALU-input multiplexers, so EX/EX and MEM/EX cover them too. With forwarding, Example 2 shrinks to the notes' second table:

Example 2 with forwarding: 0 stalls, 6 clock cycles. In cycle 4 the ALU of AND takes R2 from EX/MEM (EX/EX path).
123456
I0 SUB R2, R1, R3IFIDEXMEMWB
I1 AND R12, R2, R5IFIDEXMEMWB
The notes' Figure 7, p. 24, with forwarding: a store right after the load of the same register costs nothing. In cycle 5 the data memory takes R2 from MEM/WB (MEM/MEM path).
123456
I0 LD R2, 0(R1)IFIDEXMEMWB
I1 SD R2, 8(R1)IFIDEXMEMWB

What the forwarding unit actually does

Figure 8 of the notes (page 24) is Patterson and Hennessy's forwarding datapath, and it gives away the secret: forwarding is a comparison of register numbers. When an instruction was decoded in ID, its source numbers rs1 and rs2 were copied into ID/EX; the two instructions ahead of it carry their destination number rd in EX/MEM and MEM/WB. The forwarding unit compares them while the instruction is in EX and sets the multiplexers. In the textbook's words (background from the book, not in the notes), for the first ALU input:

EX hazard:
  if EX/MEM.RegWrite and EX/MEM.rd != R0
     and EX/MEM.rd == ID/EX.rs1
  then ForwardA = EX/MEM        # EX/EX path

MEM hazard:
  if MEM/WB.RegWrite and MEM/WB.rd != R0
     and MEM/WB.rd == ID/EX.rs1
     and not (EX hazard on rs1)
  then ForwardA = MEM/WB        # MEM/EX path

Two details matter. The clause "not EX hazard" gives priority to the newest result: in ADD R1, R1, R2 / ADD R1, R1, R3 / ADD R1, R1, R4 both older instructions match R1, and the third add must take the second one's value, in EX/MEM. And R0 is excluded because it is hard-wired to zero: an instruction that "writes" R0 forwards nothing.

Forwarding paths: which comparison lights which wire

The datapath of the notes' Figure 8, simplified: the three pipeline registers, the two ALU-input multiplexers, the data memory with its own multiplexer, and the forwarding unit at the bottom with the register numbers it compares. Pick a producer/consumer pair; the wire that carries the value lights up, and the readouts show the comparison that selected it.

  1. Before choosing the second case (SUB then OR two instructions later), predict which pipeline register holds R2 when OR is in EX, and therefore which path serves it. Then look.
    What you should see

    MEM/WB: the SUB is in WB by then, so its result has moved on from EX/MEM. The MEM/EX wire lights, selected by MEM/WB.rd = R2 = ID/EX.rs2. The same wire serves a load's value one cycle after the bubble — look at the third case.

  2. The double hazard (three ADD R1 in a row): both EX/MEM.rd and MEM/WB.rd equal R1. Which value must the third add take, and what would go wrong with the other one?
    What you should see

    The EX/EX wire: EX/MEM holds the second add's result, the newest one. MEM/WB holds the first add's result, which the second add has already consumed and overwritten; taking it would skip one addition. That is why the MEM-hazard condition carries the clause "and not EX hazard".

  3. In the sixth case the numbers match (rd = 0 = rs1) and yet no wire lights. Why is this correct, and what does SUB read instead?
    What you should see

    R0 is hard-wired to zero; an instruction with destination R0 produces no value. The comparison is masked by rd != R0, and SUB reads 0 from the register file, which is the right value. Without that clause the ALU result of the previous instruction would leak into R0.

  4. The last case is the distance-3 pair. No wire lights — so where did the value come from, and what did the split-cycle register file have to do with it?
    What you should see

    From the register file, through the WB path drawn at the top: the producer wrote R2 in the first half of the cycle in which the consumer was in ID and read it in the second half. Without the split-cycle file the read would miss the write by half a cycle, and the hardware would need one stall — the toggle of the next widget shows that case.

The table collects what forwarding buys, read as the exam reads it: producer, consumer, distance.

Stall cycles of a RAW pair in the five-stage pipeline with the split-cycle register file ("fwd." = with the three forwarding paths).
Producer → use\(d = 1\) no fwd.\(d = 1\) fwd.\(d = 2\) fwd.\(d \ge 3\)
ALU → operand, address, store data20 (EX/EX)0 (MEM/EX)0
Load → operand, address21 (bubble, then MEM/EX)0 (MEM/EX)0
Load → store data20 (MEM/MEM)00

The one stall no wire can remove: load-use

Fig. 5 puts the two producers side by side. An ALU result is born at the end of EX, one cycle before the next instruction's EX: the EX/EX wire delivers it just in time. A load's value is born at the end of MEM — the memory has to answer first — while the next instruction's EX runs in that same cycle: there is nothing to forward yet. The consumer waits one cycle, then MEM/EX hands it the value. This is the load-use hazard: one stall even with full forwarding, the fact of the chapter most worth remembering.

C1 C2 C3 C4 C5 C6 I0 IF ID EX ME WB I1 IF ID EX ME WB 0 stalls

SUB R2, R1, R3 then AND R12, R2, R5: EX/EX path

C1 C2 C3 C4 C5 C6 C7 I0 IF ID EX ME WB I1 IF ID EX ME WB 1 stall, still

LD R2, 4(R1) then ADD R3, R2, R4: one bubble, then MEM/EX

Fig. 5. Same wires, different birthplace. Left: the ALU result exists after cycle 3 and the next EX (cycle 4) gets it. Right: the loaded value exists only after cycle 4, so the consumer's EX is pushed to cycle 5 — the notes' Figure 6 (p. 24) with the bubble made explicit.
Load-use with forwarding as a table: 1 stall, 7 clock cycles.
1234567
I0 LD R2, 4(R1)IFIDEXMEMWB
I1 ADD R3, R2, R4IFIDstallEXMEMWB

Who inserts the bubble

The forwarding unit works in EX; it can route a value but cannot hold an instruction back. The load-use case needs a second circuit one stage earlier, the hazard detection unit of Patterson and Hennessy (background from the book: the notes draw the bubble but never say who makes it). It sits in ID and asks: is the instruction in EX a load whose destination is one of my sources?

if ID/EX.MemRead
   and (ID/EX.rd == IF/ID.rs1 or ID/EX.rd == IF/ID.rs2)
then stall one cycle

One refinement the course's third wire forces on the book's rule: when the instruction in ID is a store and the match is on its data register, there is no stall — the data is needed only in MEM, where the MEM/MEM path delivers it (the Figure 7 table above). Only the base register of a store goes through the test.

Stalling is three actions in one cycle (Fig. 6): freeze the PC, so the same instruction is fetched again; freeze the IF/ID register, so the instruction in ID stays and reads its registers again next cycle; and zero the control signals entering ID/EX, so the stage downstream executes a nop. That zeroed set of control signals is the bubble; it travels through EX, MEM and WB doing nothing.

Hazard detection PC IF/ID ID 0 ID/EX EX hold select 0 a bubble
Fig. 6. How a bubble is made. The unit (violet) reads the source numbers in IF/ID and the destination and MemRead in ID/EX; on a load-use match it holds the PC and IF/ID (orange, dashed) and selects zero for the controls entering ID/EX.

"Forwarding reads the register file faster." No: forwarding never touches the register file. The value comes from the EX/MEM or MEM/WB pipeline register, where it waits anyway; the register file is still written in WB. The split-cycle register file is a separate trick inside the register file.

Pipeline table builder

One row per instruction, one column per clock cycle. Pick a code fragment or type your own (LD, SD, ADD, SUB, AND, OR, XOR, ADDI, ADDUI, SUBI and NOP; registers R0 to R31), switch forwarding and the split-cycle register file on or off, and reveal the cycles one at a time. Orange dashed cells are stall bubbles, a repeated IF is an instruction held in fetch, magenta arrows are forwarding paths (read the path's name off its two ends), the dashed ochre arrow is a register-file read in the same cycle as the write. The counters only count what you have revealed.

  1. Keep the default fragment and both switches as they are (no forwarding). Before pressing anything, write down the stalls you expect for I1, I2, I3 and I4 from the distance rule; then reveal all cycles.
    What you should see

    2, 2, 2 and 0: three RAW pairs at distance 1 (R2, R3, R5) and I4 whose producers wrote long ago. 15 cycles = 5 + 4 + 6, CPI 3.00. Notice how I2 shows three IF cells: it was held in fetch while I1 stalled.

  2. Now switch forwarding on. Which single stall survives, and which arrow serves each of the other three pairs?
    What you should see

    Only I1's: a load-use bubble, then the MEM/EX arrow from the load's MEM cell. I2 and I3 are served by EX/EX, and I4's R5 by MEM/EX. 10 cycles = 5 + 4 + 1, CPI 2.00 — the insight the exam is built around: forwarding removes every ALU-to-ALU stall and leaves exactly one per load-use pair.

  3. Choose the notes' Example 3 (one NOP between SUB and AND) with forwarding off. Does the NOP make AND ready, as the notes claim? Then add a second NOP in the box and press "Use my code".
    What you should see

    No: one stall remains (8 cycles for 3 instructions). With two NOPs the stalls vanish, but the table is still 8 cycles long, now for 4 instructions: the NOPs cost exactly the cycles the hardware would have stalled, and they inflate the instruction count on top. CPI looks better (2.00 instead of 2.67) because it counts the NOPs as work.

  4. Choose the Figure 9 fragment, switch forwarding on and the split-cycle register file off. Predict which instruction stalls now, then reveal.
    What you should see

    I3 (ADD R14, R2, R2), the consumer at distance 3: its EX falls one cycle after the producer's WB, when the value is neither in a pipeline register any more nor yet readable from the register file. The split-cycle file closes that gap — which is why every exam paper grants it in the machine assumptions.

Who fixes it: the compiler or the hardware

Everything so far was hardware. The notes (page 25) sort the answers to a RAW hazard by who acts: static solutions are applied by the compiler before the program runs, dynamic solutions by the hardware while it runs. Keep the two words; they return for scheduling (VLIW against Tomasulo) and branch prediction.

The four solutions to RAW hazards of the notes, p. 25, with the cost the notes leave out.
SolutionWhoWhat it doesWhat it costs
nop insertioncompiler (static)puts empty instructions between producer and consumerthe same lost cycles as a stall, plus a larger program and a larger instruction count; written for one pipeline depth
instruction schedulingcompiler (static)moves independent instructions into the gapfree when independent instructions exist nearby; impossible otherwise
stalling (interlock)hardware (dynamic)holds the consumer in ID and sends a bubble downlost cycles; a small detection circuit; no code growth
forwardinghardware (dynamic)takes the value from a pipeline registermultiplexers and comparators; removes every data-hazard stall except load-use

The notes' Examples 3 and 4, corrected

Now the warm-up puzzle. The notes insert one nop (Example 3, page 25) or one independent instruction (Example 4, page 26) between SUB R2 and AND R12, R2, R5 and declare R2 ready. Run the distance rule: without forwarding the pair needs two stalls, so one filler turns \(d = 1\) into \(d = 2\) and removes one stall. The hardware still inserts the other — or, without interlocks, AND reads the old R2.

# no forwarding: the consumer must be the third instruction after
I0   SUB  R2, R1, R3
I1   NOP                 # or an independent instruction
I2   NOP                 # or a second one
I3   AND  R12, R2, R5    # reads R2 in cycle 5 = SUB's WB: ready

# with forwarding: nothing between an ALU producer and its consumer
I0   SUB  R2, R1, R3
I1   AND  R12, R2, R5    # EX/EX path

# with forwarding, a load producer: exactly one slot
I0   LD   R2, 0(R1)
I1   NOP                 # fill it with useful work
I2   ADD  R3, R2, R4     # MEM/EX path, 0 stalls

Why does a hardware stall beat the compiler's nop if both lose a cycle? Because the nop is a real instruction — fetched, counted in IC, and the program grows — and the compiler had to know the pipeline depth of the machine the code would run on; an interlock costs a comparator and nothing else. And why does the compiler still matter when forwarding exists? Because of the one slot forwarding cannot fill: after every load something useful should go into the load-use slot. That is the beginning of compiler scheduling, the main tool of lesson 15 and the VLIW lessons.

Stanford's MIPS was announced in a 1982 paper as "a fast pipelined engine without pipeline interlocks", with "software solutions to several traditional hardware problems, such as providing pipeline interlocks": the compiler inserted the empty instructions you just counted. The commercial R2000 of 1985 was a five-stage pipeline much like this lesson's. Interlocks and forwarding came back everywhere: a compiler cannot know how deep tomorrow's pipeline will be.

So the warm-up has its answer: two empty slots without forwarding, none with it — and the one-slot case the notes were reaching for exists: the slot after a load.

Forwarding is a comparison of register numbers in the pipeline registers, and it removes every data-hazard stall but one: the slot after a load.

Exam-style questions

Explain the main concepts and benefits of each one of the following techniques used in a pipelined RISC-V processor:

  1. The split-cycle register file (write in the first half of the clock cycle, read in the second half).
  2. The forwarding paths EX/EX, MEM/EX and MEM/MEM and the forwarding unit.
  3. The load-use hazard and the hazard detection unit.
  4. Static versus dynamic solutions to RAW data hazards.
Show model answer

1. The register file is written in the first half of a clock cycle and read in the second half, so an instruction in WB and an instruction in ID exchange a value within the same cycle. Benefit: a consumer three instructions after its producer needs no stall and no extra datapath; without it a consumer at distance 1, 2 or 3 would pay one more stall. It is the "read and write at the same address in the same cycle" assumption of the exercises.

2. Forwarding (bypassing) feeds a result to a dependent instruction from the pipeline register where it already sits, instead of waiting for WB and a register-file read. EX/EX takes the ALU result from EX/MEM into the ALU input (ALU producer at distance 1); MEM/EX takes a value from MEM/WB into the ALU input (ALU producer at distance 2, or a loaded value); MEM/MEM takes a loaded value from MEM/WB into the data-memory write input (load followed by a store). The forwarding unit compares the destination register numbers in EX/MEM and MEM/WB with the source numbers in ID/EX, gives priority to the newest result and ignores R0. Benefit: all ALU-to-ALU stalls disappear; cost: multiplexers and comparators.

3. A load's value exists only at the end of its MEM stage, so an instruction that uses it immediately would need it in the same cycle: one stall is unavoidable even with forwarding, after which the MEM/EX path delivers the value. The hazard detection unit, in ID, detects the case (the instruction in EX is a load whose destination equals a source the instruction in ID needs in EX; the data register of a store is exempt, since the MEM/MEM path serves it in MEM) and inserts the bubble: it holds the PC and the IF/ID register and zeroes the control signals entering ID/EX. Benefit: correctness with the minimum delay; the compiler can fill the slot with an independent instruction.

4. Static solutions are applied by the compiler: nop insertion (simple, wastes the same cycles as a stall and inflates code and instruction count) and instruction scheduling (fills the gap with independent instructions, free when they exist). Dynamic solutions are applied by the hardware at run time: stalling through interlocks (bubbles, no code growth) and forwarding (the preferred one). Benefit of dynamic solutions: the code is independent of the pipeline depth; benefit of static ones: no detection hardware and the ability to fill slots forwarding cannot remove.

This is the shape of the 8-point question of the June 2026 paper: a few lines per sub-point, each with the concept, the mechanism and the benefit. The examiner is checking the vocabulary (pipeline registers, ID/EX, EX/MEM, MEM/WB, bypass, interlock, bubble), the cost next to the benefit, and the two facts that separate a good answer from a vague one: the forwarding unit compares register numbers, and load-use keeps one stall.

Consider the 5-stage RISC-V pipeline (IF, ID, EX, MEM, WB) with a register file that is written in the first half of the clock cycle and read in the second half. Consider the following code:

I0   LD   R2, 0(R1)
I1   ADD  R3, R2, R4
I2   SUB  R5, R3, R6
I3   SD   R5, 8(R1)
I4   OR   R7, R2, R5

(a) Complete the cycle-by-cycle table without forwarding. (b) Complete it with the EX/EX, MEM/EX and MEM/MEM forwarding paths. For each case give the stall cycles, the total clock cycles (IC + 4 + stalls) and the CPI. (c) Compute the speed-up of (b) over (a).

Show model answer

(a) Without forwarding. RAW on R2 (I0 to I1), R3 (I1 to I2) and R5 (I2 to I3), all at distance 1: 2 stalls each. I4 reads R2 and R5 in cycle 12, both already written: 0 stalls.

123456789101112131415
I0 LD R2, 0(R1)IFIDEXMEMWB
I1 ADD R3, R2, R4IFIDstallstallEXMEMWB
I2 SUB R5, R3, R6IFIFIFIDstallstallEXMEMWB
I3 SD R5, 8(R1)IFIFIFIDstallstallEXMEMWB
I4 OR R7, R2, R5IFIFIFIDEXMEMWB

Stalls = 6; clock cycles = 5 + 4 + 6 = 15 (the cycle of the last WB); CPI = 15 / 5 = 3.0.

(b) With forwarding. I0 to I1 is a load-use pair: 1 stall, then R2 through the MEM/EX path in cycle 5. I1 to I2: EX/EX path in cycle 6, 0 stalls. I2 to I3: R5 forwarded to the store through the EX/EX path in cycle 7, 0 stalls. I4: R5 through the MEM/EX path in cycle 8, R2 from the register file, 0 stalls.

12345678910
I0 LD R2, 0(R1)IFIDEXMEMWB
I1 ADD R3, R2, R4IFIDstallEXMEMWB
I2 SUB R5, R3, R6IFIFIDEXMEMWB
I3 SD R5, 8(R1)IFIDEXMEMWB
I4 OR R7, R2, R5IFIDEXMEMWB

Stalls = 1; clock cycles = 5 + 4 + 1 = 10; CPI = 10 / 5 = 2.0.

(c) Speed-up = 15 / 10 = 1.5 (the CPI drops from 3.0 to 2.0).

Not one of the official papers' exercises: the papers of 2024–2026 test this material through the dependence table of Exercise 1, the CPI line of every table and the quizzes. The form is that of the notes' Example 2 and Figure 9 and of the textbook. What is checked: the stalls per pair from the distance rule, the frozen IF cells of the instruction behind a stall, the one surviving load-use stall, and a total that equals the cycle of the last WB. Writing "S" (or the notes' "ID-Stallo" / "IF-Stallo") for a held cell gives the same cycle count.

The code SUB R2, R1, R3 / AND R12, R2, R5 / OR R13, R6, R2 / ADD R14, R2, R2 / SD R15, 100(R2) runs on the 5-stage pipeline without forwarding, with the split-cycle register file. How many clock cycles does it take, counting the pipeline fill?

Show model answer

Only AND stalls (distance 1 from SUB: 2 stalls); OR is held in IF behind it and reads R2 in cycle 6, after the write in cycle 5; ADD and SD are later still. Clock cycles = IC + 4 + stalls = 5 + 4 + 2 = 11.

The trap is to charge OR for its own distance-2 dependence (one stall by the rule) — but the rule assumes the consumer was not already delayed. The stall of AND has pushed OR past the write. Count on the table, not on the rule alone.

On a 5-stage pipeline with full forwarding (EX/EX, MEM/EX, MEM/MEM) and a split-cycle register file, how many stall cycles does LD R5, 0(R9) immediately followed by ADD R6, R5, R5 cause? (Single answer.)

  • 0 Forwarding cannot deliver a value that does not exist yet: the load's data appears at the end of its MEM stage, in the same cycle as the add's EX would run.
  • 1 One bubble, then the MEM/EX path hands the loaded value from MEM/WB to the ALU.
  • 2 Two stalls is the cost without forwarding; the MEM/EX path saves one of them.
  • 3 Three stalls would mean waiting for the register file without the split-cycle trick; both mechanisms exist here.
Show model answer

Answer 2: 1 stall. The loaded value exists only at the end of the load's MEM stage; the add's EX is delayed one cycle and then takes the value through the MEM/EX path.

A single-answer quiz in the form of the papers' quiz block (none of the 2024–2026 papers asks this exact pair, but it is the one fact of the chapter a quiz can test in one line). The distractor 0 is the notes' "forwarding eliminates stalls".

Which hazards can occur in the basic in-order 5-stage RISC-V pipeline with separate instruction and data memories? (Multiple answers.)

  • RAW data hazards A consumer can reach ID before its producer has reached WB.
  • WAR data hazards Every read happens in ID, three stages before any younger instruction's WB.
  • WAW data hazards All writes go through the single WB stage in program order.
  • Control hazards The next PC is not known when the instruction after a branch should be fetched.
  • Structural hazards on memory Separate instruction and data memories let IF and MEM of two instructions proceed in the same cycle.
Show model answer

Answers 1 and 4. Reads in ID precede any later write in WB and all writes are in order through one WB stage, so WAR and WAW are impossible; split memories remove the IF/MEM conflict; RAW and control hazards remain.

A multiple-answer quiz in the form of the June 2026 paper. The notes' "WAR … rare in RISC" is the distractor to resist.

A compiler targets a 5-stage pipeline with no forwarding and no interlocks, but with the split-cycle register file. How many NOP instructions must it insert between SUB R2, R1, R3 and AND R12, R2, R5 for the AND to read the new R2? Motivate your answer. (Single answer.)

  • 0 Without forwarding the AND would read R2 in cycle 3 while SUB computes it; zero slots is the forwarding answer.
  • 1 The notes' Example 3; with one slot the AND reads in cycle 4 and SUB writes in cycle 5: stale value.
  • 2 With two slots the AND is the third instruction after SUB and reads R2 in cycle 5, the cycle of SUB's WB; the split-cycle file makes that read see the new value.
  • 3 Three slots would be needed only without the split-cycle register file, when the read must come the cycle after the write.
Show model answer

Answer 3: 2 NOPs. Motivation: SUB writes R2 in WB in cycle 5. The consumer at distance \(d\) reads in cycle \(d + 2\); the read must be in cycle 5 or later, so \(d = 3\): two instructions in between. Stalls = max(0, 3 − d) = 0.

The "motivate your answer" line is where the second point is: write the two cycle numbers (write in 5, read in d + 2), not only the count.

Check yourself

Why can a WAR hazard never occur in the in-order 5-stage pipeline?

  • Because the compiler renames the registers No compiler is involved; the guarantee comes from the pipeline's timing.
  • Because every instruction reads in ID and writes in WB, and the instructions stay in order, so the older read always precedes the younger write Fig. 2: the read is four cycles ahead of the write, and nothing can overtake.
  • Because the register file is written in the first half of the cycle The split-cycle file is about a write and a read in the same cycle; it does not order different instructions.
  • Because the forwarding unit detects it The forwarding unit only handles RAW pairs; it never holds anything back.

No forwarding, split-cycle register file. ADD R4, R1, R2 is followed by one independent instruction and then by SUB R6, R4, R5. How many stalls does the SUB pay?

  • 0 Zero needs distance 3; here the SUB is at distance 2.
  • 1 max(0, 3 − 2) = 1: the SUB reads in cycle 4, the write is in cycle 5, one cycle short.
  • 2 Two is the cost at distance 1; the independent instruction already removed one.
  • 3 Three would be the distance-1 cost without the split-cycle register file.

Which statements about forwarding are true?

  • The value comes from a pipeline register, not from the register file EX/MEM for the EX/EX path, MEM/WB for MEM/EX and MEM/MEM.
  • The register file is still written in WB as before Forwarding adds a shortcut; it does not change where results are finally stored.
  • It removes every data-hazard stall The load-use pair keeps one stall: the value does not exist before the end of MEM.
  • The forwarding unit decides by comparing register numbers held in the pipeline registers EX/MEM.rd and MEM/WB.rd against ID/EX.rs1 and rs2, with RegWrite and the R0 exclusion.
  • It requires the compiler to mark the dependent instructions It is a dynamic solution: the hardware finds the dependence by itself, from the register numbers.

Three true, two false — the false ones are the two most common misunderstandings of the chapter.

With full forwarding, LD R2, 0(R1) is immediately followed by SD R2, 8(R3). Which path serves the store, and how many stalls?

  • MEM/EX, 1 stall That would be the case if the store needed R2 in its EX stage; the store data is needed only in MEM.
  • MEM/MEM, 0 stalls The store's MEM stage runs in the cycle in which the loaded value sits in MEM/WB; the wire goes straight to the data-memory input.
  • EX/EX, 0 stalls A load has no ALU result to offer in EX/MEM; its value is born in MEM.
  • None, 2 stalls Two stalls is the cost without forwarding.

ADD R1, R1, R2 / ADD R1, R1, R3 / ADD R1, R1, R4 with full forwarding. When the third add is in EX, both EX/MEM.rd and MEM/WB.rd equal R1. Which value must it use?

  • The one in EX/MEM, the result of the second add The newest result wins: the MEM-hazard condition is masked when an EX hazard matches the same source.
  • The one in MEM/WB, the result of the first add That value has already been consumed and overwritten by the second add; using it would skip one addition.
  • The register file The register file still holds the value from before the first add.
  • Either, since both are R1 They are different values of R1 at different times; only the newest is correct.

An instruction is held in ID for two stall cycles. What happens to the instruction right behind it during those cycles?

  • It overtakes and enters ID first The pipeline is in order: no instruction passes another.
  • It is held in IF, and its IF cell repeats in the table The PC and the IF/ID register are frozen; the same instruction is fetched again until ID frees up.
  • It is thrown away and fetched later Nothing is flushed on a data hazard; flushing belongs to branches (lesson 4).
  • It enters EX directly as a bubble The bubble is a set of zeroed control signals, not the next instruction.

Without forwarding, a compiler places two NOPs between SUB R2 and AND R12, R2, R5 instead of letting the hardware stall twice. Compared with the stalled version, the program now …

  • runs two cycles faster The NOPs occupy exactly the cycles the stalls would have taken: 8 cycles either way.
  • takes the same number of cycles but executes two more instructions, so its CPI looks lower 8 cycles for 4 instructions (CPI 2.0) instead of 8 cycles for 2 (CPI 4.0): CPI counts the NOPs as work. Same speed, bigger program.
  • takes two cycles longer Each NOP replaces one stall cycle; nothing is added on top.
  • needs the hazard detection unit to recognise the NOPs A NOP is an ordinary instruction with no dependences; no detection is involved.

Key take-aways

What to watch and read