Without forwarding, a consumer waits until the producer writes the register file. That costs three bubbles per ALU pair in a five-stage pipe.
Forwarding routes the EX/MEM and MEM/WB results back to the ALU inputs. An ALU chain then runs with zero bubbles.
Consumer needs rs | EX/MEM (younger) | MEM/WB (older) | Select |
|---|---|---|---|
No producer matches | No match | No match | Register file |
ALU result ready | rd matches, write set | Do not care | EX/MEM |
Older result only | No match | rd matches, write set | MEM/WB |
Both match | rd matches, write set | rd matches, write set | EX/MEM, younger wins |
Match on x0 or write clear | Ignored | Ignored | Register file |
Both bypass sources can match at once when two producers target the same register. The younger EX/MEM value is the correct one, so it takes priority.
A match on register zero never forwards. Register zero reads zero by definition, and a cleared write enable means the producer wrote nothing.
A load value is ready only at the end of MEM, one cycle after the next instruction starts EX. No bypass fixes that, so the hazard unit freezes the PC and injects one bubble.
Assume 25% of instructions are ALU consumers after an ALU producer, and 8% are load-use pairs
no forwarding: 0.25 x 3 + 0.08 x 3 = 0.75 + 0.24 = 0.99 stalls per instruction
with forwarding: 0.25 x 0 + 0.08 x 1 = 0.08 stalls per instruction
saved by forwarding: 0.99 - 0.08 = 0.91 stalls per instructionThe hazard rule is simple: stall on load-use, bypass otherwise. The next-lesson detail is the detector truth table, which this lesson states in words.
Zero. The EX/MEM bypass delivers the result in time for the next EX.
One. The loaded word is ready only at the end of MEM, so the consumer EX waits one cycle.
A forwarding-select unit with youngest-wins priority, plus a testbench that checks 8 directed producer-consumer cases.
module fwd_sel (
input logic [4:0] rs,
input logic [4:0] rd_ex,
input logic regwrite_ex,
input logic [4:0] rd_mem,
input logic regwrite_mem,
output logic [1:0] fwd
);
always_comb begin
fwd = 2'd0;
if (regwrite_mem && (rd_mem != 5'd0) && (rd_mem == rs))
fwd = 2'd1;
if (regwrite_ex && (rd_ex != 5'd0) && (rd_ex == rs))
fwd = 2'd2;
end
endmodule