
算术/逻辑运算指令 (R-Type)这类指令操作两个寄存器结果写入第三个寄存器。所有R-Type指令的Opcode都是0110011。注在此解释一下OpcodeOpcode是操作码指定操作类型。指令功能描述Funct3Funct7ADD加法rd rs1 rs20000000000SUB减法rd rs1 - rs20000100000AND按位与rd rs1 rs21110000000OR按位或rd rs1 | rs21100000000XOR按位异或rd rs1 ^ rs21000000000SLT有符号数比较并置位若rs1 rs2则rd1否则rd00100000000SLTU无符号数比较并置位若rs1 rs2则rd1否则rd00110000000SLL逻辑左移rd rs1 rs0010000000SRL逻辑右移rd rs1 rs2 (高位补0)1010000000SRA算术右移rd rs1 rs2 (高位补符号位)1010100000想必大家看完此表之后会对R型指令有一个大体的了解但对于Funct3与Funct7可能存在困惑接下来我会对于这两个进行一个解释1. Funct33位功能码长度3个比特位取值范围 0~7。位置位于指令的第14~12位。核心职责区分指令的“变种”或“条件类型”。典型应用场景分支指令BEQ、BNE、BLT等它们共享同一个Opcode。Funct3的3位编码用来告诉CPU使用哪种比较条件。000 BEQ相等则跳转001 BNE不相等则跳转100 BLT小于则跳转加载/存储指令LB、LH、LW等Funct3用来指定数据的位宽单位字节和是否带符号扩展。000 LB加载字节8位001 LH加载半字16位010 LW加载字32位2. Funct77位功能码长度7个比特位取值范围 0~127。位置位于指令的第31~25位即最高位段。核心职责区分同类别下的“不同运算操作”特别是扩展算术运算。典型应用场景最经典的案例在RISC-V中ADD加法 和 SUB减法 拥有完全相同的Opcode、完全相同的Funct3000并且都需要两个源寄存器和目标寄存器。那么CPU怎么区分它们全靠 Funct7 来定乾坤Funct7 0000000 Funct3 000 → 代表 ADD加法Funct7 0100000 Funct3 000 → 代表 SUB减法这7个比特位允许CPU支持更复杂的操作比如乘除扩展M拓展或位操作B拓展。3. 为什么要把指令拆得这么碎设计哲学为什么不把Opcode直接做长一点比如20位来包含所有指令节省指令位宽高效利用寄存器Opcode越长留给寄存器编号rd/rs1/rs2的位宽就越短只有32位固定长度。RISC-V用极短的7位Opcode仅128种基础大类把细分的压力转交给Funct3和Funct7从而腾出更多的5位空间共32个来命名通用寄存器极大提高了寄存器的利用效率。简化译码器硬件这种固定字段的分层译码方式让CPU的译码电路不需要复杂的查表直接通过硬连线Hardwired Control就能快速识别有利于提高主频和降低功耗。4.举例①模块的外壳输入输出端口Verilog用 module 和 endmodule 包裹。这就像芯片的引脚。module alu (// 输入信号input wire [31:0] rs1, // 源操作数1 (对应你的rs1)input wire [31:0] rs2, // 源操作数2 (对应你的rs2)input wire [2:0] funct3, // 对应你的3位Funct3input wire [6:0] funct7, // 对应你的7位Funct7// 输出信号output reg [31:0] rd // 结果 (对应你的rd));②核心逻辑case分支电路里区分指令靠的是 case 语句类似C语言的 switch。// 内部逻辑只要输入信号rs1/rs2/funct3/funct7一变化立即重新计算always (*) begincase ({funct7, funct3}) // 拼接字段作为选择条件// ---------- 算术运算 ----------10b0000000_000: rd rs1 rs2; // ADD (funct70000000, funct3000)10b0100000_000: rd rs1 - rs2; // SUB (funct70100000, funct3000)// ---------- 位运算 ----------10b0000000_111: rd rs1 rs2; // AND (funct70000000, funct3111)10b0000000_110: rd rs1 | rs2; // OR (funct70000000, funct3110)10b0000000_100: rd rs1 ^ rs2; // XOR (funct70000000, funct3100)// ---------- 比较置位 ----------10b0000000_010: rd ($signed(rs1) $signed(rs2)) ? 32d1 : 32d0; // SLT (有符号)10b0000000_011: rd (rs1 rs2) ? 32d1 : 32d0; // SLTU (无符号)// ---------- 移位运算 ----------10b0000000_001: rd rs1 rs2[4:0]; // SLL (逻辑左移)10b0000000_101: rd rs1 rs2[4:0]; // SRL (逻辑右移高位补0)10b0100000_101: rd rs1 rs2[4:0];// SRA (算术右移高位补符号位, 注意Verilog用)default: rd 32b0; // 默认输出0endcaseend③时序逻辑带时钟的写法上面的代码是组合逻辑一通电立刻出结果有延迟但没节拍。在实际CPU流水线中计算完结果通常要在下一个时钟上升沿锁存。代码长这样// 带时钟和复位信号的写法always (posedge clk or negedge rst_n) beginif (!rst_n) beginrd 32b0; // 复位时结果清零end else begincase ({funct7, funct3})10b0000000_000: rd rs1 rs2; // 注意这里用的是 (非阻塞赋值)// ... (其余case和上面一样)endcaseendend④整体的一个大概框架module riscv_alu_top (input wire clk, // 时钟input wire rst_n, // 复位低有效input wire [31:0] inst, // 输入的32位机器码input wire [31:0] reg1, reg2,output reg [31:0] result);wire [6:0] opcode inst[6:0]; // 提取Opcodewire [2:0] f3 inst[14:12]; // 提取Funct3wire [6:0] f7 inst[31:25]; // 提取Funct7// 调用上面的ALU模块 (实例化)alu u_alu (.rs1(reg1),.rs2(reg2),.funct3(f3),.funct7(f7),.rd(result));endmodule最后关于RISC-V的其余内容主包正在努力更新中宝子们可以关注等待后续