RAW冒险零停顿解决:将结果直接旁路到需要它的指令
流水线中,后续指令需要前序指令的结果,但前序指令还没写回寄存器堆——这就是RAW(Read After Write)冒险。
朴素方案:插入停顿(Stall/NOP),等写回完成。但每个RAW冒险浪费1-2个周期,性能损失巨大。
前递方案:结果计算完成后,直接从流水线寄存器旁路到需要它的阶段,不等写回。
| 前递来源 | 符号 | 延迟减少 | 优先级 |
|---|---|---|---|
| 寄存器堆(无前递) | RF | 0 | 最低 |
| MEM阶段结果 | MEM→ID | 1周期 | 中 |
| EX阶段结果 | EX→ID | 2周期 | 最高 |
前递单元(Forwarding Unit)需要检测三种情况:
RISC-V规定x0始终为0,即使指令目标寄存器是x0也不改变其值。因此:
// Lesson 33: Data Forwarding (Bypass) for RAW hazards
module forwarding_unit #(
parameter DATA_W = 32
)(
input wire [4:0] id_rs1_i, id_rs2_i,
input wire [4:0] ex_rd_i,
input wire ex_regwrite_i,
input wire [31:0] ex_result_i,
input wire [4:0] mem_rd_i,
input wire mem_regwrite_i,
input wire [31:0] mem_result_i,
input wire [31:0] rf_rs1_i, rf_rs2_i,
output reg [31:0] fwd_rs1_o, fwd_rs2_o,
output reg [1:0] fwd_sel_rs1_o, fwd_sel_rs2_o
);
// EX前递优先级高于MEM
always @(*) begin
// RS1前递
if (ex_regwrite_i && ex_rd_i != 5'd0 && ex_rd_i == id_rs1_i) begin
fwd_rs1_o = ex_result_i;
fwd_sel_rs1_o = 2'b01; // EX
end else if (mem_regwrite_i && mem_rd_i != 5'd0
&& mem_rd_i == id_rs1_i) begin
fwd_rs1_o = mem_result_i;
fwd_sel_rs1_o = 2'b10; // MEM
end else begin
fwd_rs1_o = rf_rs1_i;
fwd_sel_rs1_o = 2'b00; // RF
end
// RS2前递(结构相同)
if (ex_regwrite_i && ex_rd_i != 5'd0 && ex_rd_i == id_rs2_i) begin
fwd_rs2_o = ex_result_i;
fwd_sel_rs2_o = 2'b01;
end else if (mem_regwrite_i && mem_rd_i != 5'd0
&& mem_rd_i == id_rs2_i) begin
fwd_rs2_o = mem_result_i;
fwd_sel_rs2_o = 2'b10;
end else begin
fwd_rs2_o = rf_rs2_i;
fwd_sel_rs2_o = 2'b00;
end
end
endmodule
| 场景 | 无前递 | 有前递 | 节省 |
|---|---|---|---|
| EX→EX (1条间隔) | 2 stall | 0 stall | 2周期 |
| MEM→EX (2条间隔) | 1 stall | 0 stall | 1周期 |
| Load→EX | 2 stall | 1 stall | 1周期 |
| 整体IPC提升 | ~0.5-0.7 | ~0.8-1.0 | ~50%+ |