3. 实现基础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实现正确。若出现错误,终端会打印具体操作码、输入和期望值,便于定位问题。