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

验证方法与通过条件:

  1. 观察pc信号:每个时钟上升沿后pc增加4,表明IF阶段PC正确传递。
  2. 观察alu_result信号:在ID/EX寄存器捕获后一个周期,ALU输出应为rs1_data + rs2_data(因寄存器堆初始化为regfile[i]=i,第一条指令add x1,x2,x3应输出5)。
  3. 观察reg_write_en信号:在MEM/WB阶段后拉高,且寄存器堆中x1在写回周期后更新为5(可在testbench中通过$display检查uut.regfile[1]的值)。
  4. 通过条件:仿真无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初值不确定。