4. 实现控制信号生成

任务说明

本案例在经典5级流水线(IF→ID→EX→MEM→WB)中实现ID阶段的控制信号生成逻辑。输入为32位指令,输出为流水线各阶段所需的控制信号:RegWrite、MemRead、MemWrite、Branch、Jump、ALUSrc、ALUOp、MemToReg。控制信号随指令在流水线中逐级传递,确保每个阶段只使用当前指令对应的控制信号。

实现方案

采用RISC-V RV32I基础指令集,按指令类型(R型、I型、S型、B型、JAL、JALR、LUI、AUIPC)生成控制信号。控制信号生成器为纯组合逻辑,输出在ID阶段锁存后随流水线寄存器逐级传递。本案例聚焦控制信号生成与传递机制,不包含数据冒险检测(该内容在后续案例中实现)。

核心代码

// control_unit.v
`timescale 1ns/1ps

module control_unit (
    input  wire [6:0] opcode,      // 指令[6:0]
    input  wire [2:0] funct3,      // 指令[14:12]
    input  wire [6:0] funct7,      // 指令[31:25]
    output reg        reg_write,   // 写寄存器堆
    output reg        mem_read,    // 读数据存储器
    output reg        mem_write,   // 写数据存储器
    output reg        branch,      // 分支指令
    output reg        jump,        // 跳转指令
    output reg        alu_src,     // ALU第二操作数选择(0=寄存器,1=立即数)
    output reg [1:0]  alu_op,      // ALU操作编码
    output reg        mem_to_reg   // 写回数据选择(0=ALU结果,1=存储器数据)
);

    // ALUOp编码:00=仅用于lw/sw,01=B型分支,10=R型,11=I型算术
    localparam [1:0] ALUOP_LS   = 2'b00;
    localparam [1:0] ALUOP_BR   = 2'b01;
    localparam [1:0] ALUOP_R    = 2'b10;
    localparam [1:0] ALUOP_I    = 2'b11;

    // RISC-V opcodes
    localparam [6:0] OP_RTYPE   = 7'b0110011;  // R型
    localparam [6:0] OP_ITYPE   = 7'b0010011;  // I型算术
    localparam [6:0] OP_LOAD    = 7'b0000011;  // 加载
    localparam [6:0] OP_STORE   = 7'b0100011;  // 存储
    localparam [6:0] OP_BRANCH  = 7'b1100011;  // 分支
    localparam [6:0] OP_JAL     = 7'b1101111;  // JAL
    localparam [6:0] OP_JALR    = 7'b1100111;  // JALR
    localparam [6:0] OP_LUI     = 7'b0110111;  // LUI
    localparam [6:0] OP_AUIPC   = 7'b0010111;  // AUIPC

    always @(*) begin
        // 默认值:所有控制信号置零
        reg_write  = 1'b0;
        mem_read   = 1'b0;
        mem_write  = 1'b0;
        branch     = 1'b0;
        jump       = 1'b0;
        alu_src    = 1'b0;
        alu_op     = 2'b00;
        mem_to_reg = 1'b0;

        case (opcode)
            OP_RTYPE: begin
                reg_write = 1'b1;
                alu_op    = ALUOP_R;
            end
            OP_ITYPE: begin
                reg_write = 1'b1;
                alu_src   = 1'b1;
                alu_op    = ALUOP_I;
            end
            OP_LOAD: begin
                reg_write  = 1'b1;
                mem_read   = 1'b1;
                alu_src    = 1'b1;
                alu_op     = ALUOP_LS;
                mem_to_reg = 1'b1;
            end
            OP_STORE: begin
                mem_write = 1'b1;
                alu_src   = 1'b1;
                alu_op    = ALUOP_LS;
            end
            OP_BRANCH: begin
                branch = 1'b1;
                alu_op = ALUOP_BR;
            end
            OP_JAL: begin
                reg_write = 1'b1;
                jump      = 1'b1;
                alu_op    = ALUOP_I;  // 使用I型ALU操作计算PC+4
            end
            OP_JALR: begin
                reg_write = 1'b1;
                jump      = 1'b1;
                alu_src   = 1'b1;
                alu_op    = ALUOP_I;
            end
            OP_LUI: begin
                reg_write = 1'b1;
                alu_src   = 1'b1;
                alu_op    = ALUOP_I;
            end
            OP_AUIPC: begin
                reg_write = 1'b1;
                alu_src   = 1'b1;
                alu_op    = ALUOP_I;
            end
            default: begin
                // 未定义指令:所有信号保持默认零值
            end
        endcase
    end

endmodule

流水线传递结构

// pipeline_control_reg.v - 控制信号在EX/MEM/WB阶段的传递
module pipeline_control_reg (
    input  wire        clk,
    input  wire        rst_n,
    // ID/EX 流水线寄存器
    input  wire        id_ex_reg_write,
    input  wire        id_ex_mem_read,
    input  wire        id_ex_mem_write,
    input  wire        id_ex_branch,
    input  wire        id_ex_jump,
    input  wire        id_ex_alu_src,
    input  wire [1:0]  id_ex_alu_op,
    input  wire        id_ex_mem_to_reg,
    output reg         ex_reg_write,
    output reg         ex_mem_read,
    output reg         ex_mem_write,
    output reg         ex_branch,
    output reg         ex_jump,
    output reg         ex_alu_src,
    output reg  [1:0]  ex_alu_op,
    output reg         ex_mem_to_reg,
    // EX/MEM 流水线寄存器
    input  wire        ex_mem_reg_write_in,
    input  wire        ex_mem_mem_read_in,
    input  wire        ex_mem_mem_write_in,
    input  wire        ex_mem_branch_in,
    input  wire        ex_mem_jump_in,
    input  wire        ex_mem_mem_to_reg_in,
    output reg         mem_reg_write,
    output reg         mem_mem_read,
    output reg         mem_mem_write,
    output reg         mem_branch,
    output reg         mem_jump,
    output reg         mem_mem_to_reg,
    // MEM/WB 流水线寄存器
    input  wire        mem_wb_reg_write_in,
    input  wire        mem_wb_mem_to_reg_in,
    output reg         wb_reg_write,
    output reg         wb_mem_to_reg
);

    // ID/EX 段
    always @(posedge clk or negedge rst_n) begin
        if (!rst_n) begin
            ex_reg_write  <= 1'b0;
            ex_mem_read   <= 1'b0;
            ex_mem_write  <= 1'b0;
            ex_branch     <= 1'b0;
            ex_jump       <= 1'b0;
            ex_alu_src    <= 1'b0;
            ex_alu_op     <= 2'b00;
            ex_mem_to_reg <= 1'b0;
        end else begin
            ex_reg_write  <= id_ex_reg_write;
            ex_mem_read   <= id_ex_mem_read;
            ex_mem_write  <= id_ex_mem_write;
            ex_branch     <= id_ex_branch;
            ex_jump       <= id_ex_jump;
            ex_alu_src    <= id_ex_alu_src;
            ex_alu_op     <= id_ex_alu_op;
            ex_mem_to_reg <= id_ex_mem_to_reg;
        end
    end

    // EX/MEM 段
    always @(posedge clk or negedge rst_n) begin
        if (!rst_n) begin
            mem_reg_write  <= 1'b0;
            mem_mem_read   <= 1'b0;
            mem_mem_write  <= 1'b0;
            mem_branch     <= 1'b0;
            mem_jump       <= 1'b0;
            mem_mem_to_reg <= 1'b0;
        end else begin
            mem_reg_write  <= ex_mem_reg_write_in;
            mem_mem_read   <= ex_mem_mem_read_in;
            mem_mem_write  <= ex_mem_mem_write_in;
            mem_branch     <= ex_mem_branch_in;
            mem_jump       <= ex_mem_jump_in;
            mem_mem_to_reg <= ex_mem_mem_to_reg_in;
        end
    end

    // MEM/WB 段
    always @(posedge clk or negedge rst_n) begin
        if (!rst_n) begin
            wb_reg_write  <= 1'b0;
            wb_mem_to_reg <= 1'b0;
        end else begin
            wb_reg_write  <= mem_wb_reg_write_in;
            wb_mem_to_reg <= mem_wb_mem_to_reg_in;
        end
    end

endmodule

测试平台与验证

// control_unit_tb.v
`timescale 1ns/1ps
module control_unit_tb;
    reg  [6:0] opcode, funct7;
    reg  [2:0] funct3;
    wire       reg_write, mem_read, mem_write, branch, jump, alu_src, mem_to_reg;
    wire [1:0] alu_op;

    control_unit dut (
        .opcode(opcode), .funct3(funct3), .funct7(funct7),
        .reg_write(reg_write), .mem_read(mem_read), .mem_write(mem_write),
        .branch(branch), .jump(jump), .alu_src(alu_src),
        .alu_op(alu_op), .mem_to_reg(mem_to_reg)
    );

    initial begin
        // R型:add x1, x2, x3 (0x002081B3)
        opcode = 7'b0110011; funct3 = 3'b000; funct7 = 7'b0000000;
        #10;
        if (reg_write !== 1'b1 || alu_op !== 2'b10 || alu_src !== 1'b0)
            $display("FAIL: R-type");

        // I型:addi x1, x2, 5 (0x00508193)
        opcode = 7'b0010011; funct3 = 3'b000; funct7 = 7'b0000000;
        #10;
        if (reg_write !== 1'b1 || alu_op !== 2'b11 || alu_src !== 1'b1)
            $display("FAIL: I-type");

        // 加载:lw x1, 0(x2) (0x00012083)
        opcode = 7'b0000011; funct3 = 3'b010; funct7 = 7'b0000000;
        #10;
        if (reg_write !== 1'b1 || mem_read !== 1'b1 || mem_to_reg !== 1'b1 || alu_src !== 1'b1)
            $display("FAIL: Load");

        // 存储:sw x1, 0(x2) (0x00112023)
        opcode = 7'b0100011; funct3 = 3'b010; funct7 = 7'b0000000;
        #10;
        if (mem_write !== 1'b1 || reg_write !== 1'b0 || alu_src !== 1'b1)
            $display("FAIL: Store");

        // 分支:beq x1, x2, label (0x00208063)
        opcode = 7'b1100011; funct3 = 3'b000; funct7 = 7'b0000000;
        #10;
        if (branch !== 1'b1 || alu_op !== 2'b01 || reg_write !== 1'b0)
            $display("FAIL: Branch");

        // JAL
        opcode = 7'b1101111; funct3 = 3'b000; funct7 = 7'b0000000;
        #10;
        if (jump !== 1'b1 || reg_write !== 1'b1)
            $display("FAIL: JAL");

        // JALR
        opcode = 7'b1100111; funct3 = 3'b000; funct7 = 7'b0000000;
        #10;
        if (jump !== 1'b1 || reg_write !== 1'b1 || alu_src !== 1'b1)
            $display("FAIL: JALR");

        // LUI
        opcode = 7'b0110111; funct3 = 3'b000; funct7 = 7'b0000000;
        #10;
        if (reg_write !== 1'b1 || alu_src !== 1'b1)
            $display("FAIL: LUI");

        // AUIPC
        opcode = 7'b0010111; funct3 = 3'b000; funct7 = 7'b0000000;
        #10;
        if (reg_write !== 1'b1 || alu_src !== 1'b1)
            $display("FAIL: AUIPC");

        $display("Test completed");
        $finish;
    end
endmodule

运行与验证

  1. 仿真运行:使用Icarus Verilog运行测试平台:
    iverilog -o sim control_unit.v control_unit_tb.v && vvp sim
  2. 通过条件:仿真输出不包含任何FAIL行,且最后一行显示Test completed。
  3. 预期输出:由于所有测试向量均按正确控制信号设计,仿真将直接输出Test completed。

关键说明

  • 控制信号生成器仅依赖opcode字段区分指令类型,funct3和funct7在RV32I基础指令中不影响控制信号(除后续扩展指令外),因此当前实现未使用这两个输入。
  • 流水线传递寄存器在每个时钟上升沿采样前级信号,复位时全部清零,防止未定义指令产生未知控制信号。
  • 本案例未包含分支比较逻辑(ALU中实现)和跳转地址计算(ID阶段实现),这些属于后续案例内容。