RISC-V Pipeline Hazards
These are like traffic jams. The flow cannot be moved into next stages due to some issues. There are 3 Main types of hazards
- Structural Hazards
- Data hazards
- Control hazards
1. Structural Hazards (The Physical Block)
two different instructions try to use the same physical piece of hardware at the exact same time.
How it happens: Think of a single-port memory chip that handles both your code (instructions) and your data (RAM variables).
- Stage 1 (Fetch): Instruction 4 is trying to read a new instruction from memory.
- Stage 5 (Memory): Instruction 1 (a
LWinstruction) is simultaneously trying to read data out of that exact same memory chip.
They collide. The memory hardware can only answer one request per cycle.
The Fix: Harvard Architecture
Modern chips solve this by physically separating the memory into two distinct blocks:
- Instruction Memory (for fetching code)
- Data Memory (for loading/storing variables). By duplicating the hardware, the collision is permanently avoided.
2. Data Hazards
this happens when two instructions in the pipeline close to another. the second instruction needs data in a register, but the first instruction must finish the job and put it there first, and its not happen yet. so the second instruction must wait till the first one is done even though they are few steps apart in the timeline.
ADD x1, x2, x3(Calculates a value forx1)SUB x4, x1, x5(Needs to readx1to do its math)
Clock Cycle: 1 2 3 4 5
-----------------------------------------
ADD x1... [IF] [ID] [EX] [MEM][WB] <-- x1 is written to RAM/Registers here!
SUB ...,x1 ---- [IF] [ID] [EX] [MEM][WB] <-- SUB needs to READ x1 here!The fixes:
1. Naive way (Stalling)
after detecting this conflict, the CPU will freeze SUB instruction until the ADD is done. This slows things down, so the 1 instruction per clock cycle throughput cannot be delivered.
2. Genius Approach (Data forwarding/Bypassing)
Note that the instructions are one cycle apart. so on Pipeline stage 3 of the ADD instruction, the ALU already has the answer required by the SUB later. So engineers made a bypass wiring so the next instruction can directly get the value of ALU without need to wait for ADD to be done.
There are two primary forwarding paths built into the hardware:
-
EX-to-EX Forwarding (0-Cycle Delay):
- The Scenario:
ADD x1, x2, x3is immediately followed bySUB x4, x1, x5. - How it works: At the end of the clock cycle, the
ADDinstruction finishes its math in the ALU. The result drops into the temporary pipeline register between the Execute and Memory stages (EX/MEM). On the very next clock cycle, a bypass wire routes that exact value right back into the input of the ALU for theSUBinstruction. TheSUBinstruction gets its data instantly, losing zero clock cycles.
- The Scenario:
-
MEM-to-EX Forwarding (0-Cycle Delay):
- The Scenario:
ADD x1, x2, x3is followed by another random instruction, and then followed bySUB x4, x1, x5. - How it works: By the time
SUBhits the Execute stage, theADDinstruction has moved up to Stage 4 (Memory). A bypass wire grabs the data from the temporary register between Memory and Writeback (MEM/WB) and feeds it backward into the ALU input. Again, throughput stays at a perfect 1 cycle per instruction.
- The Scenario:
3. Control Hazards (Branching blindspots)
happen with branch instructions (if/else) and jumps.
Imagine when the CPU get this instruction, BEQ (Branch if Equal), if x1 == x2 it's supposed to skip the next 10 lines.
Clock Cycle: 1 2 3 4 5 6 7
---------------------------------------------------
BEQ x1,x2,TRGT [IF] [ID] [EX] [MEM][WB]
ADD ... ---- [IF] [ID] [EX] [MEM][WB] <-- Mistakenly fetched!
SUB ... ---- ---- [IF] [ID] [EX] [MEM][WB] <-- Mistakenly fetched!CPU does not know which branch to pick until, clock cycle 3. So the instructions to be executed next are completely wrong if the branch instruction return true.
Fixes:
1. Naive way (Stalling)
CPU can flush the invalid but already fetched data by converting them to NOP (No operation) instructions. This can be done since the branching instruction is at Execution stage while the next are getting decoded & fetched.
The caveat is CPU now wasted 2 operation cycles doing nothing.
2. Intelligent Guessing using historic behavior
Modern CPUs use a pattern recognition mechanism to determine guesses about a Branching instruction based on its historic usage. for example, "90% of the time, this loop repeats." It uses this knowledge to confidently fetch the target instruction — not the next one. If the guess is correct in this case, 0 CPU Cycle delay since the target instruction is correct. If wrong, it uses the NOP method in the above fix.
This basically remove the stalling in 'Most' cases, which is a performance gain.