Verilog十日谈 Day 2:数字电路的筋骨:详解wire、reg与模块层次化设计

告别“变量”思维!深入硬件描述核心,搞懂“线”与“寄存器”的本质区别。

各位未来的芯片设计师,欢迎回到《Verilog十日谈》。

在Day 1,我们完成了从软件思维到硬件思维的革命性转变,并用一个简单的与门,跑通了设计->仿真的完整工业流程。现在,你已经知道你的代码不是在写程序,而是在绘制一张电路蓝图

今天,我们要深入这张蓝图的“筋骨”所在。如果说Day 1我们搭建了骨架,那么今天就是要搞清连接骨架的“肌腱”与“神经”。

你是否曾对以下问题感到困惑?

  • wire 和 reg 到底有什么区别?为什么Testbench里驱动信号用 reg,而模块输出又定义成 wire
  • 我的代码明明语法没错,为什么仿真就是不出波形?
     信号显示为红色'X'或高阻'Z'
  • 如何像搭积木一样,构建复杂的数字系统?

如果你的答案是“是”,那么今天的课程将为你拨开迷雾。理解 wire 和 reg,是写出正确、可综合Verilog代码的**第二道基石**。

一、 思维破壁:你的“变量”不是变量,是硬件连接!

请再次忘记C语言中的intfloat!在Verilog中,我们描述的是**物理连接**和**数据存储**。

  • wire(线):*它代表的是一条**物理连接线**,用于连接器件的端口。它的值由**驱动源**决定,本身**不能存储任何值**。

    • 核心特质:
       它需要被持续驱动。如果没有驱动,它就是高阻态 z。如果被多个源驱动,它的值由驱动强度决定(通常会导致未知 x)。
    • 一个比喻:
       wire 就像一根电线,它本身不带电,电灯泡(接收端)亮不亮,完全取决于电线另一头的开关和电池(驱动源)的状态。
  • reg(寄存器):它的名字具有极大的误导性!它**不一定**会生成实际的硬件寄存器。它代表的是一个**数据存储单元**,能够在某个时刻被赋值,并保持这个值直到下一次赋值。

    • 核心特质:
       它可以在 always 和 initial 块中被赋值。
    • 一个比喻:
       reg 更像一个带锁的盒子,你可以往里面放东西(赋值),它会一直保存着,直到你再次打开锁改变它。

黄金法则:

  • assign 语句的左侧必须是 wire 类型。
  • always 和 initial 块内被赋值的信号,必须定义为 reg 类型。
  • 模块的输入端口只能是 wire
  • 模块的输出端口可以是 wire 或 reg,但取决于其驱动方式。

二、 实战一:用“结构描述法”构建一个异或门

在Day 1,我们用了行为描述 (assign y = a & b;) 。今天,我们用更底层的**结构描述**方式来构建一个异或门,这将完美展示 wire 的作用。

我们知道: y = a ^ b = (~a & b) | (a & ~b)

  1. 创建新项目


新建 xor_gate.v 文件,输入以下代码:
    // 使用基本的与、或、非门运算符来构建一个异或门module xor_gate (    input   a,    input   b,    output  y);    // 定义内部连接线    wire    a_not, b_not;    wire    and_out1, and_out2;    // 使用运算符实现逻辑门功能    // 非门:产生 a 的非    assign a_not = ~a;    // 非门:产生 b 的非      assign b_not = ~b;    // 与门:计算 ~a & b    assign and_out1 = a_not & b;    // 与门:计算 a & ~b    assign and_out2 = a & b_not;    // 或门:将两个与门的输出合并,得到最终结果 y = (~a & b) | (a & ~b)    assign y = and_out1 | and_out2;endmodule



    1. 点击编译,并查看RTL图。
      你会看到一个清晰的门级电路结构,而那些 wire 类型的信号 (a_notb_not 等),正是连接这些门的**线**!这就是 wire 的本质。




    三、 实战二:Testbench进阶与 reg 的深入理解

    现在,为我们的异或门搭建测试平台。我们将通过这个Testbench,彻底理解 reg 在测试中的用法。

    新建 tb_xor_gate.v 文件,输入以下代码:


      `timescale 1ns/1nsmodule tb_xor_gate();    // 1. 定义激励信号    // 注意:它们要驱动DUT的输入,所以在always/initial块中赋值,必须定义为 reg!    reg     a, b;    // 注意:它们要连接DUT的输出,所以定义为 wire    wire    y;    // 2. 实例化被测设计    xor_gate u_xor_gate (        .a  (a),        .b  (b),        .y  (y)    );    // 3. 产生激励    initial begin        // 初始化        a = 1'b0;        b = 1'b0;        #20;        // 生成所有4种输入组合,观察输出        a = 1'b0; b = 1'b1; #20;        a = 1'b1; b = 1'b0; #20;        a = 1'b1; b = 1'b1; #20;        // 额外加一个case,确保波形完整        a = 1'b0; b = 1'b0; #20;        $stop;    endendmodule



      关键解释:

      • 为什么 a 和 b 是 reg?因为在这个Testbench模块内部,是 initial 块在给 a 和 b 赋值。它们是被Testbench**驱动**的,需要一个“数据存储”的抽象来描述它们值的变化。
      • 为什么 y 是 wire?因为它仅仅是将被测模块 u_xor_gate 的输出端口连接到了这里,它是一条**连接线**。

      四、 联合仿真:在波形中验证逻辑

      1. 按照Day 1的流程,设置好仿真,启动ModelSim。
      2. 运行仿真,查看波形。



      请仔细观察:

      • 当 a 和 b 相同时,y 为 0
      • 当 a 和 b 不同时,y 为 1
      • 同时,你还可以在波形图中看到内部连接线 and_out1 和 and_out2 的变化,这能帮助你更深层次地理解电路的运作流程。**这就是仿真的强大之处——让你能窥探芯片内部的任何一根“线”!

      五、 模块的层次化设计:工程师的必备技能

      现在,我们已经有两个“芯片”了:and_gate(Day1设计) 和 xor_gate。如何将它们组合成一个更复杂的系统?答案就是:模块实例化

      让我们设计一个 logic_unit,它包含一个与门和一个异或门。

      1. 新建 logic_unit.v 文件:


        module logic_unit (    input   [1:0]   sel,    // 2位选择信号    input           x,    input           y,    output          out);    wire    and_out, xor_out; // 内部连接线    // 实例化我们已经设计好的与门模块    and_gate u_and (        .a  (x),        .b  (y),        .y  (and_out)  // 输出连接到内部线 and_out    );    // 实例化我们刚刚设计的异或门模块    xor_gate u_xor (        .a  (x),        .b  (y),        .y  (xor_out) // 输出连接到内部线 xor_out    );    // 根据选择信号 sel,选择输出哪一个结果    // 这是一个2选1选择器,我们用条件运算符实现    assign out = (sel == 2'b00) ? and_out :                 (sel == 2'b01) ? xor_out :                 1'b0; // 默认输出0endmodule



        1. 为 logic_unit 编写Testbench并进行仿真。这将是一个绝佳的练习,让你真正体验如何像搭积木一样构建数字系统。

        【今日源码下载】本文的完整源码(xor_gate.vtb_xor_gate.vlogic_unit.v)已准备好。点击文末原文链接下载 

        【明日预告】

        今天,我们彻底剖析了Verilog的“筋骨”——wirereg,并迈入了模块化设计的大门。你已经具备了构建复杂系统的基本能力。

        但是,组合逻辑的世界里暗藏玄机!一个巨大的陷阱正等着90%的初学者。

        **Day 3:组合逻辑的双刃剑:assignalways@(*)与Latch陷阱**

        我们将:

        • 深入讲解描述组合逻辑的另一种强大方式:always@(*) 块。
        • 揭示一个由ifcase语句不完整而引发的**锁存器**大坑!
        • 总结出安全编写组合逻辑的“军规”。

        互动话题: 在今天的实验中,你成功看到内部信号的波形了吗?对于wirereg,你是否还有疑问?或者你最想在Day 3中解决关于组合逻辑的什么问题?欢迎在评论区留言讨论!


        《Verilog十日谈》系列精华目录(更新):

        • Day 1: 从软件思维到硬件思维:我的第一行Verilog代码与ModelSim仿真
        • Day 2: 数字电路的筋骨:详解wire、reg与模块层次化设计 (当前)
        • Day 3: 组合逻辑的双刃剑:assign与always@(*)与Latch陷阱
        • Day 4: 时序逻辑的基石:时钟、复位与非阻塞赋值(<=)
        • Day 5: 价值千金的法则:阻塞(=)与非阻塞(<=)的终极对决
        • Day 6: 实战:用有限状态机实现一个序列检测器
        • Day 7: Testbench进阶:自动化验证、任务与系统函数
        • Day 8: 高效设计之道:PLL、RAM等IP核的调用与仿真
        • Day 9: 番外篇:常见问题答疑与调试技巧精讲
        • Day 10: 总结与展望:你的FPGA学习路线图

        从思维到工具,从语法到项目,我们已经为你铺平了道路。点击关注,用十天,我们深入Verilog的“筋骨”所在,不见不散!

        源码下载

        lesson2.rar


        1、代码文件需要付费后才可见。
        2、支付问题请联系微信公众号客服。
        3、优质Verilog/VHDL代码资源,所见即所得。
        Verilog/VHDL资源下载 » Verilog十日谈 Day 2:数字电路的筋骨:详解wire、reg与模块层次化设计

        注册为本站会员,充值100得150,详情咨询客服

        目前为止共有 *** 位优秀的会员加入! 立刻加入会员