第05课 · ALU

算术逻辑单元RISC-VVerilator

📌 学习目标:设计 RISC-V 风格的 ALU,支持加、减、与、或、异或、移位、比较等操作,并通过 Verilator 全面验证。

一、ALU 是什么?

ALU(Arithmetic Logic Unit,算术逻辑单元)是 CPU 的执行核心,负责所有算术和逻辑运算。RISC-V 的 ALU 需要支持以下操作:

操作码操作功能RISC-V 指令
000ADDA + BADD/ADDI/LW/SW
001SUBA - BSUB
010ANDA & BAND/ANDI
011ORA | BOR/ORI
100XORA ^ BXOR/XORI
101SRLA >> B(逻辑右移)SRL/SRLI
110SLLA << B(逻辑左移)SLL/SLLI
111SLTA < B ? 1 : 0(有符号比较)SLT/SLTI

二、ALU 设计要点

2.1 减法实现

A - B = A + (~B) + 1,利用补码运算,只需在加法器输入端加一个 MUX 和取反器。

2.2 移位实现

逻辑左移(SLL):低位补 0
逻辑右移(SRL):高位补 0
算术右移(SRA):高位补符号位

2.3 比较实现

SLT(Set on Less Than):如果 A < B(有符号),输出 1,否则输出 0。
通过减法结果的符号位来判断,但需要处理溢出情况。

三、Verilog 实现

// alu.v — RISC-V 风格 32位 ALU module alu ( input [31:0] a, input [31:0] b, input [2:0] op, output [31:0] result, output zero ); reg [31:0] result_r; assign result = result_r; assign zero = (result_r == 32'b0); always @(*) begin case (op) 3'b000: result_r = a + b; // ADD 3'b001: result_r = a - b; // SUB 3'b010: result_r = a & b; // AND 3'b011: result_r = a | b; // OR 3'b100: result_r = a ^ b; // XOR 3'b101: result_r = a >> b[4:0]; // SRL 3'b110: result_r = a << b[4:0]; // SLL 3'b111: begin // SLT if ($signed(a) < $signed(b)) result_r = 32'd1; else result_r = 32'd0; end default: result_r = 32'b0; endcase end endmodule

3.1 测试台

// tb_alu.v — ALU 全面测试 module tb_alu; reg [31:0] a, b; reg [2:0] op; wire [31:0] result; wire zero; alu uut (.a(a), .b(b), .op(op), .result(result), .zero(zero)); integer pass = 0, fail = 0; task check; input [31:0] expected; begin if (result !== expected) begin $display("FAIL: a=%0d b=%0d op=%b got=%0d exp=%0d", a, b, op, result, expected); fail = fail + 1; end else pass = pass + 1; end endtask initial begin // ADD 测试 a = 32'd10; b = 32'd20; op = 3'b000; #10; check(32'd30); a = 32'hFFFFFFFF; b = 32'd1; op = 3'b000; #10; check(32'd0); // SUB 测试 a = 32'd50; b = 32'd20; op = 3'b001; #10; check(32'd30); a = 32'd5; b = 32'd10; op = 3'b001; #10; check(32'hFFFFFFFB); // -5 补码 // AND 测试 a = 32'hFF00FF00; b = 32'h0F0F0F0F; op = 3'b010; #10; check(32'h0F000F00); // OR 测试 a = 32'hFF00FF00; b = 32'h0F0F0F0F; op = 3'b011; #10; check(32'hFF0FFF0F); // XOR 测试 a = 32'hFF00FF00; b = 32'h0F0F0F0F; op = 3'b100; #10; check(32'hF00FF00F); // SRL 测试 a = 32'h80000004; b = 32'd2; op = 3'b101; #10; check(32'h20000001); // SLL 测试 a = 32'd1; b = 32'd4; op = 3'b110; #10; check(32'd16); // SLT 测试 a = 32'hFFFFFFFE; b = 32'd1; op = 3'b111; #10; check(32'd1); // -2 < 1 → 1 a = 32'd5; b = 32'd3; op = 3'b111; #10; check(32'd0); // 5 > 3 → 0 // Zero 标志位测试 a = 32'd10; b = 32'd10; op = 3'b001; #10; if (zero !== 1'b1) begin $display("FAIL: zero flag"); fail = fail + 1; end else pass = pass + 1; $display("========================================"); $display("ALU 测试: PASS=%0d FAIL=%0d", pass, fail); if (fail == 0) $display("✅ 加减与或移位比较全通过!"); else $display("❌ 存在失败!"); $display("========================================"); $finish; end endmodule

四、Verilator 编译命令

verilator --cc alu.v --exe tb_alu.v \ --build --top-module tb_alu ./obj_dir/Vtb_alu

五、ALU 在 RISC-V 中的角色

🤔 思考题:为什么 RISC-V 的 ALU 只需要一个 zero 标志位,而 x86 需要多个标志位(CF/ZF/SF/OF)?

💡 提示:RISC 的精简设计哲学 — 比较和分支用专用指令(SLT+BEQ),而不是依赖标志位

🏆 成就解锁:ALU 设计师

✅ Verilator 仿真验证通过

✅ ADD/SUB/AND/OR/XOR/SRL/SLL/SLT 全部验证正确

✅ 补码减法验证正确

✅ 有符号比较 SLT 验证正确

✅ Zero 标志位验证正确

🎯 下一目标:时序逻辑入门 → 第06课:触发器