第一个仿真工程:从零开始跑通你的第一个波形

说实话,很多初学者卡在第一步——不是看不懂代码,而是不知道怎么让代码跑起来。我记得自己刚学Verilog那会儿,对着Modelsim界面愣了半天,连怎么添加文件都不知道。今天咱们就把这层窗户纸捅破。

1. 编写你的第一段Verilog代码

先别想太复杂。咱们写一个最简单的计数器,就8位宽,每个时钟上升沿加1。这种电路我几乎每个项目都会用到,算是基本功中的基本功。

module counter (
    input        clk,
    input        rst_n,
    output reg [7:0] cnt
);

always @(posedge clk or negedge rst_n) begin
    if (!rst_n)
        cnt <= 8'd0;
    else
        cnt <= cnt + 1'b1;
end

endmodule

这段代码什么意思?rst_n低电平时计数器清零,否则每个时钟加1。嗯,这里要注意:rst_n是异步复位,敏感列表里同时写了posedge clknegedge rst_n,这是标准写法。

我的习惯:文件名和模块名保持一致。比如这个模块叫counter,文件就叫counter.v。别问我为什么,你试试文件名乱起,后期找代码找到崩溃就知道了。

2. 编译操作——别让红字吓到你

代码写好了,下一步就是编译。在Modelsim里,编译就是把.v文件变成库文件(.lib)。操作很简单:

  1. 打开Modelsim,在Transcript窗口下方输入vlib work,创建一个叫work的库
  2. 输入vlog counter.v,编译你的设计文件
  3. 如果看到# ** Note: (vlog-2286) ...这样的信息,别慌,那是正常提示

我曾经遇到一个新手,看到Transcript窗口里出现黄色警告就以为出错了,其实Modelsim的警告大部分可以忽略。真正要警惕的是** Error开头的红字。如果编译报错,先检查:

  • 分号有没有漏掉(Verilog里分号是语句结束符)
  • begin/end是否成对
  • 模块名和文件名是否一致
避坑指南:我曾经因为文件名大小写问题折腾了半小时。Windows下文件名不区分大小写,但Modelsim内部是区分的。建议统一用小写字母命名。

3. 仿真启动——让代码动起来

编译通过后,还不能直接看波形。你得先写一个testbench,也就是测试平台。说白了,就是给被测试模块提供激励信号。

`timescale 1ns/1ps

module tb_counter;

reg        clk;
reg        rst_n;
wire [7:0] cnt;

counter u_counter (
    .clk   (clk),
    .rst_n (rst_n),
    .cnt   (cnt)
);

initial begin
    clk = 0;
    rst_n = 0;
    #20 rst_n = 1;
    #500 $stop;
end

always #5 clk = ~clk;

endmodule

这个testbench做了三件事:

  • 生成一个周期为10ns的时钟(#5翻转一次)
  • 在0时刻复位,20ns后释放复位
  • 运行500ns后自动停止

在Modelsim里跑仿真:

  1. 编译testbench:vlog tb_counter.v
  2. 启动仿真:vsim work.tb_counter
  3. 在出现的wave窗口里,右键选择要观察的信号,或者直接输入add wave *添加所有信号
  4. 点击Run按钮,或者输入run 500ns

核心要点:仿真不是看代码,是看波形。波形才是验证的最终依据。我见过太多人盯着代码看半天找bug,其实波形上一眼就能看出来。

4. 查看波形——学会读懂信号的变化

波形出来了,怎么看?咱们这个计数器很简单:

  • rst_n为低时,cnt保持0
  • rst_n拉高后,每个时钟上升沿cnt加1
  • cnt从0x00一直加到0xFF,然后溢出回到0x00

你可以在波形上放大某个区域,用鼠标拖拽选中一段,然后按Ctrl+滚轮缩放。Modelsim的波形查看器有几个实用功能:

操作 快捷键 作用
放大 Ctrl + 滚轮向上 查看细节
缩小 Ctrl + 滚轮向下 查看整体
测量时间 左键拖拽 显示两个光标间的时间差
添加光标 右键 → Add Cursor 标记关键时间点

我个人习惯在波形上添加几个光标,分别标记复位释放点、第一次计数溢出点、以及任何看起来异常的地方。这样回头分析问题时,能快速定位。

5. 本章知识体系

下面这张图总结了咱们今天走通的整个流程:

第一个仿真工程流程 1. 编写Verilog代码 counter.v 2. 编译(Compile) vlog counter.v 3. 编写TB tb_counter.v 4. 启动仿真(Simulate) vsim work.tb_counter 5. 查看波形 & 分析结果 add wave * → run 500ns 每个步骤都对应一个Modelsim命令或操作

你看,整个流程其实就五步。写代码、编译、写testbench、启动仿真、看波形。我当年第一次跑通这个流程时,看到波形里计数器乖乖地从0加到255,那种成就感至今难忘。

小技巧:如果你觉得每次输入命令太麻烦,可以把所有命令写到一个.do文件里。比如创建一个run.do,内容就是上面那些命令,然后输入do run.do一键执行。这个习惯我一直用到现在。

好了,现在你已经跑通了第一个仿真工程。波形里那个阶梯状的cnt信号,就是数字电路最直观的语言。下次遇到复杂设计,别忘了回到这个最基础的流程——写代码、编译、仿真、看波形,万变不离其宗。


公众号:蓝海资料掘金营,微信deep3321