SystemVerilog FPGA 入门 – 第 2 节 – 设计:4 位 2:1 多路选择器(MUX)

设计:4 位 2:1 多路选择器(MUX)

本节尝试向页面添加了互动组件与 SVG,如有显示异常,可在评论反馈或直接联系作者。

认识 4 位 2:1 多路选择器

多路选择器(Multiplexer,MUX)的作用是:从多个输入中选择一个,传递到输出。

a[3:0] b[3:0] y[3:0] sel 0 1 MUX

其接口抽象为:

信号 位宽 作用
a 4 位 输入 A
b 4 位 输入 B
sel 1 位 选择信号
y 4 位 输出

当 sel=0 时,输出应选择 a;当 sel=1 时,输出应选择 b。

1. 如果 a = 4’b1010,b = 4’b0111,sel = 0,那么 y 应该是多少?




2. 如果 sel 从 0 变成 1,但 a 和 b 不变,输出应该怎样变化?




3. 这个电路需要 clk 时钟输入吗?为什么?






需要注意的是,MUX 不需要时钟激发。这种组合逻辑电路没有时钟输入也能工作。

类型 特征 示例 工作方式
组合逻辑 当前输入决定输出 多路选择器、加法器、译码器 输入变化后,输出随之变化,不需要时钟沿触发。
时序逻辑 保存状态 D 触发器、计数器、状态机 通常在时钟沿更新寄存器保存的状态。

真实硬件中的组合逻辑并非零延迟,输出需要经过传播时间才稳定。但它不需要等待特定的时钟沿。

用 SystemVerilog 实现

接下来亲自实现一下端口列表。创建 src/mux2.sv。

注意:与 Verilog 不同,SystemVerilog 使用 logic 类型代替 wire 和 reg。详见上节对比。
logic 可以用于组合逻辑输出,也可以用于时序逻辑中的寄存器变量,具体取决于驱动方式。

module mux2 (
    input logic [3:0] a,
    input logic [3:0] b,
    input logic sel,
    output logic [3:0] y
);

endmodule

现在开始实现输出逻辑,可以选择使用三目条件或者组合 always_comb 块。初学时此处使用三目条件(sel ? b : a)实现:

assign y = sel ? b : a;

SystemVerilog 使用 assign 语句为组合逻辑赋值。sel ? b : a 表示如果 sel 为真(1),则输出 b,否则输出 a。

assign 是连续赋值。只要 sel、a 或 b 发生变化,y 就会根据表达式重新计算,不需要时钟。

为了更加符合工程项目结构,建立 tests/tb_mux2.sv 测试文件以编写 testbench。

注意:此处的测试与之前所配置的 Python 测试不同,SystemVerilog 的 testbench 用于入门、时序逻辑、基础模块,Python 测试则适用于大量随机测试、复杂验证、自动化。现在建立新的测试项目时需要修改 justfile,添加新的测试目标。

test-sv FILE TEST:
    mkdir -p build
    verilator --binary --timing --top-module {{TEST}} -Mdir build/vobj src/{{FILE}}.sv tests/{{TEST}}.sv
    ./build/vobj/V{{TEST}}

先回答一个设计问题

假设我们测试:

a   = 4'b1010;
b   = 4'b0111;
sel = 1'b1;
你认为 testbench 应该如何判断 y 是否正确?






在 testbench 中写测试与其它语言写集成测试类似。我们可以使用 if 判断输出是否符合预期,并在不符合时打印错误信息。

对于 if (y !== 4'b0111) 需要理解,!== 是 SystemVerilog 中的不等于(严格)运算符,它不仅比较数值,还比较位的有效性(0、1、X、Z)。在测试中使用 !== 可以确保输出与预期完全匹配,包括未定义或高阻态的情况。上述示例中 0b1010 与 0b0111 在 sel = 1 时,输出应为 0b0111,如果输出不符合预期,测试将打印错误信息。

现在开始编写。

module tb_mux2;
    logic [3:0] a;
    logic [3:0] b;
    logic sel;
    logic [3:0] y;

    mux2 dut (
        .a  (a),
        .b  (b),
        .sel(sel),
        .y  (y)
    );
    initial begin
        // Setting input.
        a   = 4'b1010;
        b   = 4'b0111;
        sel = 1'b1;

        // Waiting for stability.
        #1;

        if (y !== b) $fatal(1, "Test failed!");

        $display("Test passed!");
        $finish;
    end
endmodule

创建测试模块需要注意:

  • module tb_mux2; 定义测试模块不需要端口,因为它是顶层测试模块。
  • mux2 dut (...); 实例化被测试的模块 mux2,并将信号连接到测试模块的逻辑变量。
  • initial begin ... end 块在仿真开始时执行一次,设置输入信号、等待传播延迟,并检查输出是否符合预期。
  • #1; 表示等待 1 个时间单位(仿真时间),确保组合逻辑有足够时间稳定输出。
  • $fatal(1, "Test failed!"); 在测试失败时终止仿真并打印错误信息。1 是错误代码,表示仿真以错误状态退出。
  • $display("Test passed!"); 在测试通过时打印成功信息。
  • $finish; 结束仿真。

然后运行测试:

just test-sv mux2 tb_mux2

返回 Test passed! 表示测试通过。

试着改变输入 sel 为 0,并修改预期输出为 a,再次运行测试,确保测试覆盖了所有情况。

module tb_mux2;
    logic [3:0] a;
    logic [3:0] b;
    logic sel;
    logic [3:0] y;

    mux2 dut (
        .a  (a),
        .b  (b),
        .sel(sel),
        .y  (y)
    );
    initial begin
        // Setting input.
        a   = 4'b1010;
        b   = 4'b0111;
        sel = 1'b1;

        // Waiting for stability.
        #1;

        if (y !== b) $fatal(1, "Test failed! sel = 1.");

        sel = 1'b0;
        #1;

        if (y !== a) $fatal(1, "Test failed! sel = 0.");

        $display("Test passed!");         

        $finish;
    end
endmodule

运行测试,应当能够通过。

现在,回到刚刚的测试。为什么在 sel 发生变化后 y 也会立即发生变化?

因为 MUX 不需要等待时钟沿,输入变化后,输出就会随组合逻辑的传播而更新。

导出波形

接下来,试试再添加一组测试在刚刚的 tb_mux2.sv 中:

  • 将 a 设为 4'b0000
  • 将 b 设为 4'b1111
  • 设置 sel = 0,等待 #1,检查输出
  • 再设置 sel = 1,等待 #1,检查输出
// Setting input
a = 4'b0000;
b = 4'b1111;
sel = 1'b0;

#1;
// Checking output.
if (y !== a) $fatal(1, "Test failed! sel = 0.");

sel = 1'b1;

#1;
if (y !== b) $fatal(1, "Test failed! sel = 1.");

运行测试:

just test-sv mux2 tb_mux2

测试通过。

接下来在 initial begin 后面添加:

$dumpfile("build/mux2.vcd");
$dumpvars(0, tb_mux2);

这两个系统任务分别用于:

  • $dumpfile:指定波形文件的路径。
  • $dumpvars:记录测试平台及其内部的信号变化。

然后再编辑 justfile,给 verilator 添加 --trace 参数:

test-sv FILE TEST:
    mkdir -p build
    verilator --binary --trace --timing --top-module {{TEST}} -Mdir build/vobj src/{{FILE}}.sv tests/{{TEST}}.sv
    ./build/vobj/V{{TEST}}

再次运行测试,然后得到 build/mux2.vcd 文件。使用波形查看器 GTKWave打开该文件。

打开这些数据:

GTKWave

这里能够看到第一帧(0ps)的 sel 为 1,输出 y 为 7 = b。

GTKWave

第二帧(1ps)sel 为 0,输出为 a = A。

GTKWave

第三帧(2ps)sel 为 0,输出为 a = 0。

GTKWave

第四帧(3ps)sel 为 1,输出为 b = F。

至此,4 位 2:1 多路选择器的设计、实现、测试和波形导出都完成了。

暂无评论

发送评论 编辑评论


				
|´・ω・)ノ
ヾ(≧∇≦*)ゝ
(☆ω☆)
(╯‵□′)╯︵┴─┴
 ̄﹃ ̄
(/ω\)
∠( ᐛ 」∠)_
(๑•̀ㅁ•́ฅ)
→_→
୧(๑•̀⌄•́๑)૭
٩(ˊᗜˋ*)و
(ノ°ο°)ノ
(´இ皿இ`)
⌇●﹏●⌇
(ฅ´ω`ฅ)
(╯°A°)╯︵○○○
φ( ̄∇ ̄o)
ヾ(´・ ・`。)ノ"
( ง ᵒ̌皿ᵒ̌)ง⁼³₌₃
(ó﹏ò。)
Σ(っ °Д °;)っ
( ,,´・ω・)ノ"(´っω・`。)
╮(╯▽╰)╭
o(*////▽////*)q
>﹏<
( ๑´•ω•) "(ㆆᴗㆆ)
😂
😀
😅
😊
🙂
🙃
😌
😍
😘
😜
😝
😏
😒
🙄
😳
😡
😔
😫
😱
😭
💩
👻
🙌
🖕
👍
👫
👬
👭
🌚
🌝
🙈
💊
😶
🙏
🍦
🍉
😣
Source: github.com/k4yt3x/flowerhd
颜文字
Emoji
小恐龙
花!
上一篇
下一篇