第13课 · 控制单元

控制信号指令译码Main Decoder

📌 学习目标:实现 RISC-V 主译码器,根据指令 opcode 和 funct3 生成所有控制信号,通过 Verilator 验证指令到控制信号的映射。

一、控制单元的作用

控制单元是 CPU 的"大脑",它根据当前指令生成所有控制信号,指挥数据通路中的各个组件协同工作。

指令 ──→ 控制单元 ──→ 控制信号
                    ├── PCSrc   (PC选择: +4 / 跳转)
                    ├── ALUSrc  (ALU B端: rs2 / imm)
                    ├── MemRead (数据存储器读)
                    ├── MemWrite(数据存储器写)
                    ├── MemToReg(写回选择: ALU / Mem)
                    ├── RegWrite(寄存器写使能)
                    ├── ALUOp   (ALU操作类型)
                    └── Branch  (分支使能)

二、控制信号定义

信号01说明
RegWrite不写寄存器文件写使能
MemRead不读数据存储器读使能
MemWrite不写数据存储器写使能
ALUSrcrs2immALU 第二操作数来源
MemToRegALUMem写回数据来源
PCSrcPC+4跳转PC 下一值选择
Branch不分支可能分支分支指令标志

三、各指令的控制信号

指令RegWMemRMemWALUSrcMemToRegBranchPCSrcALUOp
ADD100000000
SUB100000000
AND/OR/XOR100000000
ADDI100100001
LW110110001
SW0011X0001
BEQ0000X1010
JAL100X001XX

四、Verilog 实现

// control.v — RISC-V 控制单元 module control ( input [6:0] opcode, input [2:0] funct3, input [6:0] funct7, output reg reg_write, output reg mem_read, output reg mem_write, output reg alu_src, output reg mem_to_reg, output reg branch, output reg pc_src, output reg [1:0] alu_op ); always @(*) begin // 默认值 reg_write = 0; mem_read = 0; mem_write = 0; alu_src = 0; mem_to_reg = 0; branch = 0; pc_src = 0; alu_op = 2'b00; case (opcode) 7'b0110011: begin // R-Type reg_write = 1; alu_src = 0; alu_op = 2'b00; end 7'b0010011: begin // I-Type ALU reg_write = 1; alu_src = 1; alu_op = 2'b01; end 7'b0000011: begin // LW reg_write = 1; mem_read = 1; alu_src = 1; mem_to_reg = 1; alu_op = 2'b01; end 7'b0100011: begin // SW mem_write = 1; alu_src = 1; alu_op = 2'b01; end 7'b1100011: begin // Branch branch = 1; alu_op = 2'b10; end 7'b1101111: begin // JAL reg_write = 1; pc_src = 1; end 7'b1100111: begin // JALR reg_write = 1; pc_src = 1; alu_src = 1; end 7'b0110111, 7'b0010111: begin // LUI/AUIPC reg_write = 1; alu_src = 1; end endcase end endmodule

4.1 测试台

// tb_control.v module tb_control; reg [6:0] opcode; reg [2:0] funct3; reg [6:0] funct7; wire reg_write, mem_read, mem_write, alu_src; wire mem_to_reg, branch, pc_src; wire [1:0] alu_op; control uut (.*); integer pass=0, fail=0; task check; input rw,mr,mw,as,m2r,br,ps; input [1:0] ao; begin if (reg_write!==rw||mem_read!==mr||mem_write!==mw||alu_src!==as|| mem_to_reg!==m2r||branch!==br||pc_src!==ps||alu_op!==ao) begin $display("FAIL opcode=%b: rw=%b mr=%b mw=%b as=%b m2r=%b br=%b ps=%b ao=%b", opcode, reg_write, mem_read, mem_write, alu_src, mem_to_reg, branch, pc_src, alu_op); fail=fail+1; end else pass=pass+1; end endtask initial begin opcode=7'b0110011; funct3=3'b000; funct7=7'b0; #1; check(1,0,0,0,0,0,0,2'b00); // R-type opcode=7'b0010011; #1; check(1,0,0,1,0,0,0,2'b01); // I-type ALU opcode=7'b0000011; #1; check(1,1,0,1,1,0,0,2'b01); // LW opcode=7'b0100011; #1; check(0,0,1,1,0,0,0,2'b01); // SW opcode=7'b1100011; #1; check(0,0,0,0,0,1,0,2'b10); // BEQ opcode=7'b1101111; #1; check(1,0,0,0,0,0,1,2'b00); // JAL opcode=7'b0110111; #1; check(1,0,0,1,0,0,0,2'b00); // LUI $display("========================================"); $display("控制单元测试: PASS=%0d FAIL=%0d", pass, fail); if (fail == 0) $display("✅ 指令→控制信号映射全部正确!"); else $display("❌ 存在失败!"); $display("========================================"); $finish; end endmodule

五、Verilator 编译命令

verilator --cc control.v --exe tb_control.v \ --build --top-module tb_control ./obj_dir/Vtb_control

🤔 思考题:如果设计一个新的自定义指令,需要修改哪些控制信号?如何保证不影响已有指令?

💡 提示:在 case 中增加新分支,确保 default 安全;测试回归

🏆 成就解锁:控制单元设计师

✅ Verilator 仿真验证通过

✅ 8 类指令的控制信号映射正确

✅ R/I/S/B/J/U 各类型译码正确

✅ 理解控制信号与数据通路的协作关系

🎯 下一目标:完整单周期 CPU → 第14课:单周期CPU