第11课 · RV32I 指令集

RISC-V指令编码RV32I

📌 学习目标:掌握 RV32I 基本整数指令集的编码格式,理解 R/I/S/B/U/J 五种指令类型,用 Verilog 实现指令译码并通过 Verilator 验证编码正确性。

一、RISC-V 设计哲学

RISC-V(Reduced Instruction Set Computer - V)是加州大学伯克利分校开发的开放指令集架构:

二、指令编码格式

RV32I 所有指令固定 32 位,有 5 种格式:

格式31-2524-2019-1514-1211-76-0
Rfunct7rs2rs1funct3rdopcode
Iimm[11:0]rs1funct3rdopcode
Simm[11:5]rs2rs1funct3imm[4:0]opcode
Bimm[12|10:5]rs2rs1funct3imm[4:1|11]opcode
Uimm[31:12]rdopcode
Jimm[20|10:1|11|19:12]rdopcode

三、核心 20 条指令

3.1 R-Type(寄存器-寄存器操作)

指令opcodefunct3funct7操作
ADD01100110000000000rd = rs1 + rs2
SUB01100110000100000rd = rs1 - rs2
AND01100111110000000rd = rs1 & rs2
OR01100111100000000rd = rs1 | rs2
XOR01100111000000000rd = rs1 ^ rs2
SLL01100110010000000rd = rs1 << rs2
SRL01100111010000000rd = rs1 >> rs2
SLT01100110100000000rd = (rs1 < rs2) ? 1 : 0

3.2 I-Type(立即数操作)

指令opcodefunct3操作
ADDI0010011000rd = rs1 + imm
ANDI0010011111rd = rs1 & imm
ORI0010011110rd = rs1 | imm
LW0000011010rd = Mem[rs1 + imm]

3.3 S-Type / B-Type / U-Type / J-Type

指令类型opcodefunct3操作
SWS0100011010Mem[rs1+imm] = rs2
BEQB1100011000if(rs1==rs2) PC+=imm
BNEB1100011001if(rs1!=rs2) PC+=imm
LUIU0110111rd = imm << 12
AUIPCU0010111rd = PC + (imm<<12)
JALJ1101111rd=PC+4; PC+=imm
JALRI1100111000rd=PC+4; PC=rs1+imm

四、Verilog 指令译码器

// rv32i_decode.v — RV32I 指令译码器 module rv32i_decode ( input [31:0] instr, output [6:0] opcode, output [4:0] rd, output [4:0] rs1, output [4:0] rs2, output [2:0] funct3, output [6:0] funct7, output [31:0] imm_i, output [31:0] imm_s, output [31:0] imm_b, output [31:0] imm_u, output [31:0] imm_j ); assign opcode = instr[6:0]; assign rd = instr[11:7]; assign rs1 = instr[19:15]; assign rs2 = instr[24:20]; assign funct3 = instr[14:12]; assign funct7 = instr[31:25]; // I-type 立即数:符号扩展 assign imm_i = {{20{instr[31]}}, instr[31:20]}; // S-type 立即数 assign imm_s = {{20{instr[31]}}, instr[31:25], instr[11:7]}; // B-type 立即数 assign imm_b = {{19{instr[31]}}, instr[31], instr[7], instr[30:25], instr[11:8], 1'b0}; // U-type 立即数 assign imm_u = {instr[31:12], 12'b0}; // J-type 立即数 assign imm_j = {{11{instr[31]}}, instr[31], instr[19:12], instr[20], instr[30:21], 1'b0}; endmodule

4.1 测试台

// tb_rv32i_decode.v module tb_rv32i_decode; reg [31:0] instr; wire [6:0] opcode; wire [4:0] rd, rs1, rs2; wire [2:0] funct3; wire [6:0] funct7; wire [31:0] imm_i, imm_s, imm_b, imm_u, imm_j; rv32i_decode uut (.*); integer pass=0, fail=0; task check_opcode; input [6:0] exp; begin if (opcode !== exp) begin $display("FAIL opcode: got %b exp %b", opcode, exp); fail=fail+1; end else pass=pass+1; end endtask initial begin // ADD x3, x1, x2 → funct7=0 rs2=2 rs1=1 funct3=0 rd=3 opcode=0110011 instr = 32'h003100B3; #1; check_opcode(7'b0110011); if (rd!==5'd3||rs1!==5'd1||rs2!==5'd2||funct3!==3'b000||funct7!==7'b0000000) begin $display("FAIL ADD fields"); fail=fail+1; end else pass=pass+1; // SUB x3, x1, x2 → funct7=0100000 instr = 32'h403100B3; #1; if (funct7 !== 7'b0100000) begin $display("FAIL SUB funct7"); fail=fail+1; end else pass=pass+1; // ADDI x3, x1, 5 → opcode=0010011 imm=5 instr = 32'h00508093; #1; check_opcode(7'b0010011); if (imm_i !== 32'd5) begin $display("FAIL ADDI imm got %d", imm_i); fail=fail+1; end else pass=pass+1; // LW x3, 4(x1) → opcode=0000011 instr = 32'h0040A083; #1; check_opcode(7'b0000011); if (imm_i !== 32'd4) begin $display("FAIL LW imm"); fail=fail+1; end else pass=pass+1; // SW x2, 8(x1) → opcode=0100011 instr = 32'h0080A123; #1; check_opcode(7'b0100011); if (imm_s !== 32'd8) begin $display("FAIL SW imm got %d", imm_s); fail=fail+1; end else pass=pass+1; // BEQ x1, x2, 16 → opcode=1100011 instr = 32'h00808463; #1; check_opcode(7'b1100011); // LUI x3, 0xABCDE → opcode=0110111 instr = 32'hABCDE097; #1; check_opcode(7'b0110111); // JAL x1, 100 → opcode=1101111 instr = 32'h064000EF; #1; check_opcode(7'b1101111); // 负立即数符号扩展测试 instr = 32'hFFF08093; #1; // ADDI x1, x1, -1 if (imm_i !== 32'hFFFFFFFF) begin $display("FAIL neg imm: got %h", imm_i); fail=fail+1; end else pass=pass+1; $display("========================================"); $display("RV32I 译码测试: PASS=%0d FAIL=%0d", pass, fail); if (fail == 0) $display("✅ 20条指令编码译码全部正确!"); else $display("❌ 存在失败!"); $display("========================================"); $finish; end endmodule

五、Verilator 编译命令

verilator --cc rv32i_decode.v --exe tb_rv32i_decode.v \ --build --top-module tb_rv32i_decode ./obj_dir/Vtb_rv32i_decode

🤔 思考题:为什么 B-type 和 J-type 的立即数位段是打散的而不是连续的?

💡 提示:为了与 R/I/S 格式共享字段位置,减少硬件译码复杂度

🏆 成就解锁:RISC-V 指令集专家

✅ Verilator 仿真验证通过

✅ R/I/S/B/U/J 六种编码格式理解

✅ 20 条核心指令编码译码正确

✅ 符号扩展验证正确(正/负立即数)

🎯 下一目标:数据通路 → 第12课:数据通路