The predictor must answer inside the fetch stage. Larger tables and deeper history cost access time, so fetch budgets cap predictor size.
One common fix splits prediction into a fast small table plus a slower accurate overrider. The fast answer keeps fetch moving, and the overrider corrects it a cycle later when they disagree.
Choice | Effect on fetch | Cost |
|---|---|---|
Bigger bimodal table | Less aliasing, same cycle | More area and slower access |
Fast plus override | Fetch never waits | Extra muxing and a late-redirect path |
Deeper history | Better correlation | Longer warm-up after phase change |
Resolution compares the prediction against the real outcome. A mismatch squashes wrong-path instructions, restores the rename checkpoint, and redirects fetch to the correct PC.
Penalty equals the stages between fetch and resolution plus refill. A branch resolved 3 stages deep discards about 3 fetch slots of wrong-path work.
A second case improved mispredict rate from 6.4 to 5.1 percent, but resolution had moved deeper, so average penalty rose from 11.4 to 13.0 cycles. MPKI still fell 7.68 to 6.12, yet the CPI saving was only about 0.008, and observed IPC moved just 1.27 to 1.28: the rate win was spent on deeper recovery.
Check fetch utilization and the recovery histogram before celebrating accuracy. If redirect stalls already dominate, the next gain must come from bandwidth or refill, not prediction.
Front-end stall share | Before | After tuning |
|---|---|---|
Redirect and refill | 42% | 34% |
I-cache misses | 33% | 38% |
Decode backpressure | 25% | 28% |
Total | 100% | 100% |
The model below steps a fetch pipeline at depths 2, 3 and 4. A shift register tracks live fetch slots, one mispredict squashes them, and the check counts the squashed slots against pipeline depth.
class fetch_pipe_c;
int unsigned depth;
int unsigned fetched;
int unsigned flushed;
int unsigned redirect_pc;
bit live[8];
function new(int unsigned d);
depth = d;
fetched = 0;
flushed = 0;
redirect_pc = 0;
for (int i = 0; i < 8; i++) live[i] = 0;
endfunction
function void run_fetch(int unsigned n, int unsigned bad_at);
for (int c = 0; c < n; c++) begin
for (int s = depth - 1; s > 0; s--) live[s] = live[s-1];
live[0] = 1;
fetched++;
if (c == bad_at) begin
flushed = 0;
for (int s = 0; s < depth; s++) flushed = flushed + (live[s] ? 1 : 0);
for (int s = 0; s < 8; s++) live[s] = 0;
redirect_pc = 32'h8000;
end
end
endfunction
endclass
module tb_fetch_recovery;
int errors = 0;
initial begin
for (int d = 2; d <= 4; d++) begin
fetch_pipe_c p;
p = new(d);
p.run_fetch(12, 4);
if (p.flushed != d) errors++;
if (p.redirect_pc != 32'h8000) errors++;
if (p.fetched != 12) errors++;
end
if (errors == 0) $display("PASS: one mispredict flushed exactly depth slots at depths 2 3 and 4");
else $display("FAIL: %0d checks wrong", errors);
$finish;
end
endmoduleAbout 4, plus refill cycles to resteady fetch. The model above checks this equality at depths 2, 3 and 4.
The bottleneck moved. Prediction stopped being the limiter, so the next gain must come from the memory side of fetch.