3. 添加流水线寄存器
任务目标
在单周期RISC-V处理器基础上,于IF/ID、ID/EX、EX/MEM、MEM/WB四个阶段边界插入流水线寄存器,使指令数据在时钟上升沿逐级传递,形成经典5级流水线数据通路。本案例聚焦寄存器结构与数据传递机制,不包含冒险处理单元。
必要环境/依赖
Verilog-2001兼容仿真器(如Icarus Verilog 10.x / Vivado / VCS)。无需额外IP核,所有代码均为纯RTL实现。
核心实现
以下代码定义流水线寄存器模块及5级流水线CPU顶层。每个寄存器组在时钟上升沿捕获前级输出,并稳定保持至下一时钟沿。控制信号与数据通路同步打拍传递。
// pipeline_regs.v - 5级流水线寄存器与CPU顶层
module pipe_reg #(parameter WIDTH = 32) (
input wire clk,
input wire rst_n,
input wire [WIDTH-1:0] d,
output reg [WIDTH-1:0] q
);
always @(posedge clk or negedge rst_n) begin
if (!rst_n) q <= {WIDTH{1'b0}};
else q <= d;
end
endmodule
// IF/ID寄存器:传递PC+4与取指指令
module IF_ID (
input wire clk, rst_n,
input wire [31:0] pc_plus4_in, instr_in,
output reg [31:0] pc_plus4_out, instr_out
);
always @(posedge clk or negedge rst_n) begin
if (!rst_n) begin
pc_plus4_out <= 32'b0;
instr_out <= 32'b0;
end else begin
pc_plus4_out <= pc_plus4_in;
instr_out <= instr_in;
end
end
endmodule
// ID/EX寄存器:传递控制信号、寄存器数据、立即数、PC+4
`define CTRL_W 8 // {RegWrite, MemtoReg, Branch, MemRead, MemWrite, ALUSrc, RegDst, ALUOp1:0} 简化为8位
module ID_EX (
input wire clk, rst_n,
input wire [`CTRL_W-1:0] ctrl_in,
input wire [31:0] rs1_data_in, rs2_data_in, imm_in, pc_plus4_in,
input wire [4:0] rs1_addr_in, rs2_addr_in, rd_addr_in,
output reg [`CTRL_W-1:0] ctrl_out,
output reg [31:0] rs1_data_out, rs2_data_out, imm_out, pc_plus4_out,
output reg [4:0] rs1_addr_out, rs2_addr_out, rd_addr_out
);
always @(posedge clk or negedge rst_n) begin
if (!rst_n) begin
ctrl_out <= 0;
rs1_data_out <= 0; rs2_data_out <= 0; imm_out <= 0; pc_plus4_out <= 0;
rs1_addr_out <= 0; rs2_addr_out <= 0; rd_addr_out <= 0;
end else begin
ctrl_out <= ctrl_in;
rs1_data_out <= rs1_data_in; rs2_data_out <= rs2_data_in;
imm_out <= imm_in; pc_plus4_out <= pc_plus4_in;
rs1_addr_out <= rs1_addr_in; rs2_addr_out <= rs2_addr_in;
rd_addr_out <= rd_addr_in;
end
end
endmodule
// EX/MEM寄存器:传递ALU结果、写数据、控制信号、目标寄存器地址
module EX_MEM (
input wire clk, rst_n,
input wire [3:0] ctrl_in, // {RegWrite, MemtoReg, MemRead, MemWrite}
input wire [31:0] alu_result_in, write_data_in,
input wire [4:0] rd_addr_in,
output reg [3:0] ctrl_out,
output reg [31:0] alu_result_out, write_data_out,
output reg [4:0] rd_addr_out
);
always @(posedge clk or negedge rst_n) begin
if (!rst_n) begin
ctrl_out <= 0;
alu_result_out <= 0; write_data_out <= 0;
rd_addr_out <= 0;
end else begin
ctrl_out <= ctrl_in;
alu_result_out <= alu_result_in;
write_data_out <= write_data_in;
rd_addr_out <= rd_addr_in;
end
end
endmodule
// MEM/WB寄存器:传递读数据、ALU结果、控制信号、目标寄存器地址
module MEM_WB (
input wire clk, rst_n,
input wire [1:0] ctrl_in, // {RegWrite, MemtoReg}
input wire [31:0] mem_data_in, alu_result_in,
input wire [4:0] rd_addr_in,
output reg [1:0] ctrl_out,
output reg [31:0] mem_data_out, alu_result_out,
output reg [4:0] rd_addr_out
);
always @(posedge clk or negedge rst_n) begin
if (!rst_n) begin
ctrl_out <= 0;
mem_data_out <= 0; alu_result_out <= 0;
rd_addr_out <= 0;
end else begin
ctrl_out <= ctrl_in;
mem_data_out <= mem_data_in;
alu_result_out <= alu_result_in;
rd_addr_out <= rd_addr_in;
end
end
endmodule
// 5级流水线CPU顶层(不含冒险单元,数据通路直通)
module pipeline_cpu (
input wire clk, rst_n,
input wire [31:0] instr, // 来自指令存储器
input wire [31:0] read_data, // 来自数据存储器
output wire [31:0] pc, // 当前PC(IF阶段)
output wire [31:0] alu_result, // EX阶段结果(观察用)
output wire reg_write_en // WB阶段写使能(观察用)
);
// IF阶段
reg [31:0] pc_reg;
wire [31:0] pc_plus4 = pc_reg + 4;
always @(posedge clk or negedge rst_n) begin
if (!rst_n) pc_reg <= 32'b0;
else pc_reg <= pc_plus4; // 无分支,顺序执行
end
assign pc = pc_reg;
// IF/ID寄存器实例
wire [31:0] ifid_pc4, ifid_instr;
IF_ID u_ifid (.clk(clk), .rst_n(rst_n),
.pc_plus4_in(pc_plus4), .instr_in(instr),
.pc_plus4_out(ifid_pc4), .instr_out(ifid_instr));
// ID阶段:简单译码(仅提取字段,不执行控制逻辑)
wire [4:0] rs1 = ifid_instr[19:15];
wire [4:0] rs2 = ifid_instr[24:20];
wire [4:0] rd = ifid_instr[11:7];
wire [31:0] imm = {{20{ifid_instr[31]}}, ifid_instr[31:20]}; // I型立即数
// 寄存器堆(简化:32x32,异步读)
reg [31:0] regfile [0:31];
integer i;
initial for (i=0; i<32; i=i+1) regfile[i] = i; // 初始化:寄存器值=编号
wire [31:0] rs1_data = regfile[rs1];
wire [31:0] rs2_data = regfile[rs2];
// 控制信号(固定示例:R型指令 RegWrite=1, ALUSrc=0, MemtoReg=0, Branch=0, MemRead=0, MemWrite=0)
wire [`CTRL_W-1:0] id_ctrl = 8'b1000_0000; // 仅RegWrite有效
// ID/EX寄存器实例
wire [`CTRL_W-1:0] idex_ctrl;
wire [31:0] idex_rs1, idex_rs2, idex_imm, idex_pc4;
wire [4:0] idex_rs1a, idex_rs2a, idex_rda;
ID_EX u_idex (.clk(clk), .rst_n(rst_n),
.ctrl_in(id_ctrl),
.rs1_data_in(rs1_data), .rs2_data_in(rs2_data),
.imm_in(imm), .pc_plus4_in(ifid_pc4),
.rs1_addr_in(rs1), .rs2_addr_in(rs2), .rd_addr_in(rd),
.ctrl_out(idex_ctrl),
.rs1_data_out(idex_rs1), .rs2_data_out(idex_rs2),
.imm_out(idex_imm), .pc_plus4_out(idex_pc4),
.rs1_addr_out(idex_rs1a), .rs2_addr_out(idex_rs2a),
.rd_addr_out(idex_rda));
// EX阶段:ALU(仅支持加法)
wire [31:0] alu_in2 = idex_ctrl[1] ? idex_imm : idex_rs2; // ALUSrc
wire [31:0] alu_result_w = idex_rs1 + alu_in2;
assign alu_result = alu_result_w;
// EX/MEM寄存器实例
wire [3:0] exmem_ctrl;
wire [31:0] exmem_alu, exmem_wdata;
wire [4:0] exmem_rd;
EX_MEM u_exmem (.clk(clk), .rst_n(rst_n),
.ctrl_in({idex_ctrl[7], idex_ctrl[6], 2'b00}), // {RegWrite, MemtoReg, 0, 0}
.alu_result_in(alu_result_w), .write_data_in(idex_rs2),
.rd_addr_in(idex_rda),
.ctrl_out(exmem_ctrl),
.alu_result_out(exmem_alu), .write_data_out(exmem_wdata),
.rd_addr_out(exmem_rd));
// MEM阶段:无存储器访问(示例中MemRead/MemWrite均为0)
// MEM/WB寄存器实例
wire [1:0] memwb_ctrl;
wire [31:0] memwb_memdata, memwb_alu;
wire [4:0] memwb_rd;
MEM_WB u_memwb (.clk(clk), .rst_n(rst_n),
.ctrl_in(exmem_ctrl[3:2]), // {RegWrite, MemtoReg}
.mem_data_in(read_data), .alu_result_in(exmem_alu),
.rd_addr_in(exmem_rd),
.ctrl_out(memwb_ctrl),
.mem_data_out(memwb_memdata), .alu_result_out(memwb_alu),
.rd_addr_out(memwb_rd));
// WB阶段:写回
wire [31:0] wb_data = memwb_ctrl[0] ? memwb_memdata : memwb_alu;
always @(posedge clk) begin
if (memwb_ctrl[1]) regfile[memwb_rd] <= wb_data;
end
assign reg_write_en = memwb_ctrl[1];
endmodule
运行与验证
仿真测试平台(testbench)要点:
// testbench 关键片段
module tb;
reg clk = 0, rst_n = 0;
reg [31:0] instr, read_data;
wire [31:0] pc, alu_result;
wire reg_write_en;
pipeline_cpu uut (.clk(clk), .rst_n(rst_n), .instr(instr),
.read_data(read_data), .pc(pc),
.alu_result(alu_result), .reg_write_en(reg_write_en));
always #5 clk = ~clk;
initial begin
// 复位
#10 rst_n = 1;
// 连续输入两条R型指令:add x1, x2, x3 (0x003100b3) 和 add x4, x5, x6 (0x00628233)
instr = 32'h003100b3; read_data = 0;
#10 instr = 32'h00628233;
#10 instr = 32'h00000000; // NOP
#60;
$finish;
end
endmodule
编译与仿真命令(Icarus Verilog):
iverilog -o sim pipeline_regs.v tb.v
vvp sim
验证方法与通过条件:
- 观察
pc信号:每个时钟上升沿后pc增加4,表明IF阶段PC正确传递。 - 观察
alu_result信号:在ID/EX寄存器捕获后一个周期,ALU输出应为rs1_data + rs2_data(因寄存器堆初始化为regfile[i]=i,第一条指令add x1,x2,x3应输出5)。 - 观察
reg_write_en信号:在MEM/WB阶段后拉高,且寄存器堆中x1在写回周期后更新为5(可在testbench中通过$display检查uut.regfile[1]的值)。 - 通过条件:仿真无X态传播,且
alu_result在预期周期输出正确值5,写回后regfile[1]==5。
预期输出(确定性推导):第一条指令add x1,x2,x3在EX阶段输出alu_result=2+3=5;在WB阶段后regfile[1]更新为5。第二条指令add x4,x5,x6在EX阶段输出alu_result=5+6=11。
常见问题
- 数据前递缺失:本案例未包含冒险处理,若连续指令存在数据依赖,结果会错误。这是后续案例(转发与停顿)的内容。
- 控制信号位宽不匹配:各阶段寄存器控制信号位宽不同,修改控制逻辑时需同步调整连接。
- 复位后PC为0:确保
rst_n至少保持一个完整时钟周期低电平,否则PC初值不确定。