哈喽各位大佬萌新们!

最近在搞数字逻辑实验,做到实验二 ALU 设计这块儿了。感觉 Verilog 对新手还是有点门槛的,特别是 ALU 和 Testbench 怎么写、怎么理解。我把学习过程和代码理解整理了一下,希望能帮到同样在入门的同学!

这个实验主要是让我们设计一个简单的算术逻辑单元 (ALU),就是 CPU 里面负责算数的那个核心部件,然后再写个测试平台 (Testbench) 来模拟运行,看看我们设计的 ALU 对不对。

下面就开扒 alu.vtb_alu.v 这两个文件!

一、ALU 模块 (alu.v) 剖析:CPU 的计算核心

alu.v 这个文件就是用来描述 ALU 这个硬件具体要做什么事情的。

// --- ALU 模块代码 (带注释) ---
// 为 ALU 功能码定义符号名称,提高可读性
`define MovBH   3'b000  // 对应 MOV 操作 (输出 B)
`define AddBH   3'b001  // 对应 ADD 操作 (A + B)
`define SubBH   3'b010  // 对应 SUB 操作 (A - B)
`define AndBH   3'b011  // 对应 AND 操作 (A & B)
`define OrBH    3'b100  // 对应 OR 操作  (A | B)

// ALU 模块定义
module alu (
    clk,      // 时钟输入
    rst,      // 复位输入 (低电平有效)
    en_in,    // 操作使能输入
    alu_a,    // 16位输入操作数 A
    alu_b,    // 16位输入操作数 B
    alu_func, // 3位功能码,选择执行的操作
    en_out,   // 输出使能信号,指示操作完成
    alu_out   // 16位输出结果
);

// 端口声明
input               clk;
input               rst;
input               en_in;
input      [15:0]   alu_a;
input      [15:0]   alu_b;
input      [2:0]    alu_func;
output reg [15:0]   alu_out; // 输出结果是寄存器类型
output reg          en_out;  // 输出使能也是寄存器类型

// ALU 的行为描述 (核心逻辑)
always @(posedge clk or negedge rst) // 时钟上升沿或复位下降沿触发
begin
    if (rst == 1'b0) // 检查是否复位
    begin
        // 复位时,输出清零,使能无效
        alu_out <= 16'b0;
        en_out  <= 1'b0;
    end
    else if (en_in == 1'b1) // 没复位,且使能信号有效时
    begin
        en_out <= 1'b1; // 输出使能有效

        // 根据功能码 (alu_func) 选择操作
        case (alu_func)
            `MovBH: alu_out <= alu_b;         // 传递 B
            `AddBH: alu_out <= alu_a + alu_b; // A + B
            `SubBH: alu_out <= alu_a - alu_b; // A - B
            `AndBH: alu_out <= alu_a & alu_b; // A & B (按位与)
            `OrBH:  alu_out <= alu_a | alu_b; // A | B (按位或)
            default: alu_out <= 16'b0;        // 其他情况输出 0
        endcase
    end
    else // 没复位,也没使能时
    begin
        en_out <= 1'b0; // 输出使能无效
        // alu_out 保持不变
    end
end

endmodule

代码要点解读:

  1. define: 用有意义的名字 (MovBH, AddBH...) 代替二进制码 (3'b000, 3'b001...),代码更好看懂。

  2. module alu (...): 定义了一个叫 alu 的硬件模块。括号里是它的“插口”(端口)。

  3. input/output reg: 声明输入输出端口。

    • input: 信号从外面进来。

    • output reg: 信号往外面送,并且这个信号的值是由模块内部逻辑(always 块)在特定时刻(时钟沿)更新并保持的,所以用 reg(寄存器)。

    • [15:0]:表示这是 16 根线(位宽为 16)。

  4. always @(posedge clk or negedge rst): 这是时序逻辑的核心!

    • posedge clk: 时钟信号从 0 跳到 1 的那个瞬间。

    • negedge rst: 复位信号从 1 跳到 0 的那个瞬间。

    • or: 两个条件满足任意一个,里面的代码就执行一次。

    • 这表示:要么复位了,要么时钟来了,这个 always 块里的逻辑就要动一下。

  5. if (rst == 1'b0): 判断是不是按下了复位键(低电平有效)。复位了就把输出清零。

  6. else if (en_in == 1'b1): 如果没复位,再看使能信号 en_in 是不是有效。有效才干活。

  7. <= (非阻塞赋值): 在 always @(posedge clk...) 这种时序逻辑里,用 <= 来赋值。可以理解为“计划”在时钟沿那个点把右边的值赋给左边,更符合硬件实际工作方式。

  8. case (alu_func): 根据 alu_func 这个 3 位信号的值,决定具体执行哪个运算(加、减、与、或等)。

  9. default: 如果 alu_func 传进来一个我们没定义的值,就执行 default 后面的操作(这里是输出 0)。

二、ALU Testbench (tb_alu.v) 剖析:给 ALU 当“陪练”

光有 ALU 设计还不行,得测试啊!Testbench (tb_alu.v) 就是用来给 ALU 模块当“陪练”的,它模拟外部环境,给 ALU 输入各种信号,然后我们可以观察 ALU 的输出对不对。

// --- ALU Testbench 代码 (带注释) ---
`timescale 1ns / 1ps // 定义仿真时间和精度

module tb_alu(); // Testbench 模块,通常没端口

// 内部信号:用 reg 驱动 ALU 输入,用 wire 接收 ALU 输出
reg clk;
reg rst;
reg en_in;
reg [15:0] alu_a;
reg [15:0] alu_b;
reg [2:0]  alu_func;

wire en_out;
wire [15:0] alu_out;

// 把 ALU 模块“请”进来测试 (实例化)
alu test_alu(
    .clk      (clk),      // 把 tb 的 clk 连到 ALU 的 clk
    .rst      (rst),      // 把 tb 的 rst 连到 ALU 的 rst
    .en_in    (en_in),    // ...以此类推
    .alu_a    (alu_a),
    .alu_b    (alu_b),
    .alu_func (alu_func),
    .en_out   (en_out),   // 把 ALU 的输出 en_out 连到 tb 的 en_out
    .alu_out  (alu_out)   // 把 ALU 的输出 alu_out 连到 tb 的 alu_out
);

parameter Tclk = 10; // 定义时钟周期为 10ns

// 生成时钟信号 (一直跳变)
initial begin
    clk = 0;
    forever #(Tclk/2) clk = ~clk; // 每 5ns 翻转一次
end

// 生成复位信号 (只在开头有效一小段时间)
initial begin
    rst = 1'b0;        // 开始时复位有效
    #(Tclk*4) rst = 1'b1; // 40ns 后复位结束
end

// 生成 alu_func 变化的激励信号
initial begin
    alu_func = 3'b000; // 先测 Mov
    #(Tclk*8) alu_func = 3'b001; // 80ns 后测 Add
    #(Tclk*8) alu_func = 3'b010; // 再过 80ns 测 Sub
    #(Tclk*8) alu_func = 3'b011; // ...测 And
    #(Tclk*8) alu_func = 3'b100; // ...测 Or
end

// 设置操作数 a 和 b 的初始值
initial begin
    alu_a = 16'h0101; // 十六进制的 0101
    alu_b = 16'h0011; // 十六进制的 0011
end

// --- 这部分用计数器生成 en_in 信号 ---
reg [4:0] cnt1; // 计数器

// 计数器逻辑:时钟来就加 1,复位就清零
always @ (posedge clk or negedge rst) begin
    if(!rst)
        cnt1 <= 5'd0;
    else
        cnt1 <= cnt1 + 1;
end

// 使能信号生成逻辑:复位结束后第一个周期给个使能脉冲
always @ (posedge clk or negedge rst) begin
    if(!rst)
        en_in <= 1'b0;
    else if (cnt1 == 5'd1) // 复位后第一个周期
        en_in <= 1'b1;      // 给 ALU 一个使能信号
    else
        en_in <= 1'b0;      // 其他时候不使能
end
// --- en_in 生成结束 ---

// 仿真结束控制
initial begin
    #(Tclk*50) $stop; // 跑 500ns 后停止仿真
end

endmodule

Testbench 要点解读:

  1. timescale: 告诉仿真器时间的单位。

  2. module tb_alu(): Testbench 模块,名字可以随便取,一般以 tb_ 开头。

  3. reg/wire:

    • reg: 用来定义 Testbench 内部需要主动改变值的信号,这些信号会连接到 ALU 的输入端。

    • wire: 用来接收 ALU 的输出信号,Testbench 只观察这些信号的值。

  4. alu test_alu(...): 把我们设计的 alu 模块拿到 Testbench 里用,这叫实例化.clk(clk) 这种写法是把 Testbench 里的 clk 连接到 alu 模块实例 test_aluclk 端口上。

  5. parameter: 定义一个常量 Tclk,方便后面统一修改时钟周期。

  6. initial: 里面的代码只执行一次。

    • 时钟生成: 用 forever#(延迟) 制造一个不停跳变的 clk 信号。

    • 复位生成: 在仿真开始时给一个短暂的低电平 rst 信号。

    • 激励生成: 在不同的仿真时间点,改变 alu_funcalu_aalu_b 的值,模拟各种输入情况。

    • 仿真停止: 用 $stop 在一定时间后结束仿真。

  7. always: 这里用 always 块配合计数器 cnt1 来精确地生成 en_in 信号,让它在复位结束后的第一个周期有效一下。

三、关键 Verilog 概念快速回顾

  • 模块 (module): 设计的基本块。

  • 端口 (input, output): 模块的接口。

  • 数据类型 (reg, wire): reg 存值,wire 连接。

  • always: 描述硬件行为。

    • @(posedge clk or negedge rst): 时序逻辑(带异步复位)。

    • @(*): 组合逻辑(本例没用,但常用)。

  • 赋值: <= (非阻塞,用于时序), = (阻塞,用于组合或 initial)。

  • case 语句: 多选一。

  • parameter: 定义参数(常量)。

  • define: 定义宏。

  • timescale: 仿真时间设置。

  • $stop: 停止仿真。

  • forever: 无限循环。

  • #: 仿真延迟。

  • 运算符: +, -, &, |, ~, ==, != 等。

  • 数字表示: 3'b001 (二进制), 16'h0101 (十六进制), 32'd5 (十进制)。

总结

好啦,以上就是我对实验二 ALU 和 Testbench 代码的理解和分析。核心就是用 Verilog 把 ALU 的功能描述出来 (alu.v),再写一个 Testbench (tb_alu.v) 去模拟各种输入来测试它。

希望这个帖子对大家有帮助!

Logo

中国智能体开发者社区,聚焦智能体与大模型开发,提供前沿资讯、实用工具链、开源项目及行业案例。通过技术沙龙、开发者大赛等活动,促进经验交流与协作,助力开发者快速构建创新智能应用。

更多推荐