3. 实现基础ALU

实现基础ALU 学习卡片

任务目标

在本案例中,你将设计一个支持算术运算(加、减)、逻辑运算(与、或、异或)以及移位操作(逻辑左移、逻辑右移)的基础ALU模块,并使用Verilog编写测试平台进行功能验证。该ALU面向RISC-V整数指令集的核心运算需求,为后续处理器流水线设计提供计算单元。

环境依赖

本案例需要Verilog仿真工具,推荐使用Icarus Verilog(iverilog)配合GTKWave查看波形,或使用商业工具如Vivado/VCS。以下实现基于标准Verilog-2001,不依赖特定厂商原语。

ALU模块实现

ALU采用组合逻辑设计,通过4位控制信号alu_op选择运算类型。输入为两个32位操作数a和b,输出32位结果result。移位操作使用b[4:0]作为移位量(支持最大31位移位)。减法通过补码加法实现,即a + (~b + 1)。

module alu #(parameter WIDTH = 32) (
    input  wire [WIDTH-1:0] a,
    input  wire [WIDTH-1:0] b,
    input  wire [3:0]       alu_op,
    output reg  [WIDTH-1:0] result
);

    // alu_op 编码定义
    localparam OP_ADD  = 4'b0000;  // 加法
    localparam OP_SUB  = 4'b0001;  // 减法
    localparam OP_AND  = 4'b0010;  // 按位与
    localparam OP_OR   = 4'b0011;  // 按位或
    localparam OP_XOR  = 4'b0100;  // 按位异或
    localparam OP_SLL  = 4'b0101;  // 逻辑左移
    localparam OP_SRL  = 4'b0110;  // 逻辑右移

    always @(*) begin
        case (alu_op)
            OP_ADD:  result = a + b;
            OP_SUB:  result = a - b;
            OP_AND:  result = a & b;
            OP_OR:   result = a | b;
            OP_XOR:  result = a ^ b;
            OP_SLL:  result = a << b[4:0];
            OP_SRL:  result = a >> b[4:0];
            default: result = {WIDTH{1'b0}};
        endcase
    end

endmodule

测试平台与验证

测试平台通过初始化操作数并遍历所有操作码,检查每个运算结果是否与预期值一致。测试覆盖边界值(全0、全1)和典型随机值,确保移位操作使用b[4:0]作为移位量。

`timescale 1ns/1ps
module alu_tb;

    reg  [31:0] a, b;
    reg  [3:0]  alu_op;
    wire [31:0] result;

    // 预期结果寄存器
    reg  [31:0] expected;
    integer errors = 0;

    alu #(.WIDTH(32)) dut (
        .a(a), .b(b), .alu_op(alu_op), .result(result)
    );

    // 测试向量生成
    initial begin
        $dumpfile("alu_tb.vcd");
        $dumpvars(0, alu_tb);

        // 测试用例 1: 加法
        a = 32'h0000000F; b = 32'h00000001; alu_op = 4'b0000; expected = 32'h00000010;
        #10 check_result;

        // 测试用例 2: 减法 (结果为负)
        a = 32'h00000001; b = 32'h0000000F; alu_op = 4'b0001; expected = 32'hFFFFFFF2;
        #10 check_result;

        // 测试用例 3: 与运算
        a = 32'hFF00FF00; b = 32'h0F0F0F0F; alu_op = 4'b0010; expected = 32'h0F000F00;
        #10 check_result;

        // 测试用例 4: 或运算
        a = 32'hFF00FF00; b = 32'h0F0F0F0F; alu_op = 4'b0011; expected = 32'hFF0FFF0F;
        #10 check_result;

        // 测试用例 5: 异或运算
        a = 32'hFF00FF00; b = 32'h0F0F0F0F; alu_op = 4'b0100; expected = 32'hF00FF00F;
        #10 check_result;

        // 测试用例 6: 逻辑左移 (移位量 = 4)
        a = 32'h00000001; b = 32'h00000004; alu_op = 4'b0101; expected = 32'h00000010;
        #10 check_result;

        // 测试用例 7: 逻辑右移 (移位量 = 8)
        a = 32'h80000000; b = 32'h00000008; alu_op = 4'b0110; expected = 32'h00800000;
        #10 check_result;

        // 测试用例 8: 边界 - 全1加法
        a = 32'hFFFFFFFF; b = 32'h00000001; alu_op = 4'b0000; expected = 32'h00000000;
        #10 check_result;

        // 测试用例 9: 边界 - 全0减法
        a = 32'h00000000; b = 32'h00000001; alu_op = 4'b0001; expected = 32'hFFFFFFFF;
        #10 check_result;

        // 测试用例 10: 移位量超过31 (取b[4:0] = 0)
        a = 32'h00000001; b = 32'h00000020; alu_op = 4'b0101; expected = 32'h00000001;
        #10 check_result;

        // 汇总报告
        if (errors == 0)
            $display("TEST PASSED: All %0d checks passed.", 10);
        else
            $display("TEST FAILED: %0d errors found.", errors);

        $finish;
    end

    // 自动比较任务
    task check_result;
        begin
            if (result !== expected) begin
                errors = errors + 1;
                $display("ERROR: op=%b a=%h b=%h => result=%h, expected=%h",
                         alu_op, a, b, result, expected);
            end
        end
    endtask

endmodule

运行与验证方法

使用Icarus Verilog运行仿真:

iverilog -o alu_tb alu.v alu_tb.v
vvp alu_tb

若终端输出TEST PASSED: All 10 checks passed.,则说明所有测试用例通过,ALU功能正确。可通过GTKWave查看波形:

gtkwave alu_tb.vcd

验证判定标准

测试平台包含10个定向测试用例,覆盖全部7种运算类型及边界条件。当所有用例的result与expected完全一致时,判定ALU实现正确。若出现错误,终端会打印具体操作码、输入和期望值,便于定位问题。