ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

ModelSim 2020.4仿真全流程指南:从Testbench编写到波形排查

ModelSim 2020.4仿真全流程指南:从Testbench编写到波形排查 上周帮一位做通信基带的同事排查一个奇怪的仿真现象ModelSim 2020.4编译一遍通过vsim也启动了但波形窗口里全是红色的线数据总线一个有效周期都没有。当时我扫了一眼Testbench就明白了——顶层实例化少接了一个时钟端口信号在仿真器眼里就是悬空的所以全部飘红。这种问题在刚接触FPGA的人手里几乎每周都会出现而ModelSim又是绝大多数人入坑数字逻辑仿真用的第一款工具。与其让新手一个个踩坑不如把ModelSim 2020.4从安装、编译、仿真到波形排查的完整流程写清楚。这篇文章适合正在学Verilog的在校生、刚转FPGA开发的工程师以及任何需要在本地跑RTL仿真但还没玩明白ModelSim的同学。按照我下面这套思路走一遍你不仅能跑通第一个仿真还能掌握排查问题的方法论。1. 先搞清楚ModelSim 2020.4在验证流程里是什么角色1.1 功能仿真与极早期验证为什么省不下很多初学者有个误区代码能综合、能上板为什么还要在ModelSim里多此一举实际上ModelSim这类仿真器承担的是功能验证它的工作是在综合和布局布线之前把RTL代码按事件驱动的方式跑一遍用波形验证电路行为是否符合预期。这一步如果省了后面所有问题都要到板子上才发现一个逻辑错误可能花掉半天到一周的调试时间。ModelSim的仿真对象是纯粹的HDL代码它不关心你的设计最终映射到哪个型号的FPGA也不管时序约束满不满足。正因为它“只讲逻辑”所以速度快、上手门槛低。工程师先在ModelSim里把功能玩明白了再进入Quartus或Vivado做综合实现这样分层的验证思路是工业界普遍采用的方式不是学校里为了交作业才这么做。1.2 你拿到的ModelSim是哪个版本决定了很多事标题里的“2020.4”需要先厘清一个概念。市面上叫做ModelSim的软件主要分为几类它们的授权方式和功能边界差得很多版本类型来源授权方式典型特点ModelSim SESiemens EDA原Mentor商业付费完整功能支持SystemVerilog、覆盖率、脚本化回归ModelSim DESiemens EDA商业付费介于SE与入门版之间ModelSim Intel FPGA EditionIntel FPGA官网免费随Quartus Prime提供支持Intel器件库仿真ModelSim PE Student学校授权教育免费用于教学规模受限你说“2020.4版本”在实际工程里最常见的是ModelSim Intel FPGA Edition因为它随Quartus Prime 20.1等版本一起发布版本号经常和Quartus对应。还有一种是你从Siemens官网或培训渠道拿到的SE/D E 2020.4功能更全。这两者的操作流程99%相同区别在器件库支持和授权文件。所以后面讲的步骤无论你是哪种授权渠道都可以直接照搬。这里插一句关于授权的提醒搜索“ModelSim破解”相关的资料在社区里相当多但我强烈不建议碰。一方面破解程序被植入风险的案例不少另一方面一旦参与商业项目侵权风险会直接落在个人头上。Intel官方提供的ModelSim Intel FPGA Edition是免费授权注册一个署账号就能下载Siemens官方也会提供评估授权。普通学习和验证用这些足够了没必要冒安全风险。2. 环境准备安装、授权与库文件一次性配齐2.1 安装路径和组件选择里藏着三个坑我见过太多人ModelSim装好了却跑不起来重新看安装过程问题基本出在三个地方。第一个坑是路径带中文字符或空格。ModelSim对路径中的中文支持极差D:\新建文件夹\modeltech64_2020.4这种目录编译的时候常常报一些莫名其妙的错。装到纯英文路径比如D:\eda\modelsim_2020.4能避开很多问题。第二个坑是安装时没选对组件。ModelSim在安装向导里会列出一堆选项比如Win32/Win64、Simulation Libraries。如果你是64位系统务必选64位版本器件库组件能勾就勾虽然会占用几个GB空间但后面仿真Intel特定IP核的时候能省去手工编译库的麻烦。第三个坑是环境变量。安装完成后ModelSim正常情况下会自动把vsim.exe所在目录加入PATH但有时候杀毒软件会拦掉这一步。验证方法是打开命令提示符输入vsim -version如果能输出版本信息就说明环境变量没问题。如果提示不是内部或外部命令手动把D:\eda\modelsim_2020.4\win64加到系统PATH里。2.2 授权配置合法渠道的一点心得ModelSim启动时如果找不到授权文件会弹出License Error对话框。商业版的授权方式是通过环境变量LM_LICENSE_FILE指向授权服务器或license文件。环境变量配好之后在命令行执行vsim第一次启动可能会弹窗让你设置license路径指向你获得的license文件即可。如果是ModelSim Intel FPGA Edition它走的是Intel FPGA license机制。安装Quartus Prime时一并配置好在Quartus中设置ModelSim路径它就能自动识别到授权。需要注意Intel FPGA Edition在启动时也会检查环境变量LM_LICENSE_FILE如果你之前装过其他EDA工具设置了冲突的环境变量先确认它指向的是Intel授权的dat文件。判断授权是否正常最直接的办法还是vsim -version。如果它不但输出版本号还输出Chip Corp等信息说明核心引擎起来了。如果只输出版本号但后面跟着Error信息基本就是授权问题。2.3 把仿真库目录预先建好ModelSim的库不是凭空来的。一个仿真工程至少需要一个工作库默认名称是work。规范的流程是vlib work这条命令会在当前目录下生成一个work文件夹所有编译出来的中间文件都在里面。如果省略这步直接vlog编译ModelSim会提示找不到work库。很多人会问为什么每次打开ModelSim都要重新设置库路径因为work库是跟工程目录绑定的ModelSim默认的当前目录只在启动瞬间读取一次。所以正确做法是启动ModelSim后先cd到你的工程目录再执行vlib work。我通常会在工程目录里放一个setup.tcl脚本把库初始化和器件库路径都写好每次打开软件直接source setup.tcl省得重复劳动。3. 手写一个Testbench跑通首个完整仿真3.1 一个最朴素的计数器设计纸上谈兵没意思直接上代码。我以一个4位计数器为例它的功能非常简单异步复位时钟上升沿计数计数到15自动归零。代码长这样module counter #( parameter WIDTH 4 )( input wire clk, input wire rst_n, output reg [WIDTH-1:0] cnt ); always (posedge clk or negedge rst_n) begin if (!rst_n) begin cnt {WIDTH{1b0}}; end else begin cnt cnt 1b1; end end endmodule这段代码没有任何高级技巧但它包含了数字逻辑最核心的三要素时钟、复位、时序逻辑赋值。仿真它你能看到所有基础操作的完整链路。3.2 Testbench的结构与激励构建写Testbench不是写设计代码它的职责是给DUT提供激励并检查输出。最关键的是时钟和复位怎么产生。看下面的例子timescale 1ns/1ps module tb_counter(); reg clk; reg rst_n; wire [3:0] cnt; counter #( .WIDTH(4) ) u_dut ( .clk (clk), .rst_n (rst_n), .cnt (cnt) ); // 100MHz时钟周期10ns initial begin clk 1b0; forever #5 clk ~clk; end // 复位信号前20ns拉低然后释放 initial begin rst_n 1b0; #20; rst_n 1b1; end // 运行200ns后结束仿真 initial begin #200; $finish; end endmodule这里有几个细节值得展开。时钟生成用了forever #5 clk ~clk;含义是每5ns翻转一次所以时钟周期10ns。为什么用forever因为它必须一直运行到仿真结束不能只翻转一次就停。复位逻辑里前20ns拉低复位然后拉高。#20是延迟控制表示等待20ns。这模拟了上电复位的过程上电时电路未稳定复位信号拉低等稳定后释放复位。仿真结束用$finish它告诉仿真器可以停下来了。没有这行ModelSim会一直跑下去你手动停止也行但自动化流程里必须靠它收尾。3.3 编译、仿真、加波形的标准三步代码准备好了打开ModelSim命令行按顺序执行# 第一步创建库 vlib work # 第二步编译RTL和Testbench vlog counter.v tb_counter.v # 第三步启动仿真 vsim work.tb_counter这里要解释一个重要概念为什么vsim指定的是work.tb_counter而不是counter因为仿真入口是Testbench它包含了对DUT的实例化仿真器从tb_counter这个顶层模块开始执行。counter只是被实例化的子模块单独仿真它没有激励源。仿真窗口打开后添加波形信号add wave -r /tb_counter/* run -alladd wave -r里的-r表示递归会把tb_counter下所有层次的所有信号都加进来包括子模块内部的信号。run -all的意思是运行到仿真结束碰到$finish也可以在run -r后面加时间比如run 1us跑1微秒。如果在波形窗口看到计数器从0递增到15然后清零恭喜你第一个仿真跑通了。4. 波形是红线、高阻Z、时序错乱一套系统的排查链路4.1 看清波形颜色背后的状态含义回到开头的问题波形为什么是红的在ModelSim默认配色里红色表示信号是高阻态或未连接通常对应Verilog中的Z态。还有一种情况是信号显示为红色和蓝色交替的条纹状那通常表示X态——未知态。三态和未知的区别很关键颜色逻辑状态含义常见原因蓝色0低电平正常工作绿色1高电平正常工作红色Z高阻端口未连接、tri-state未驱动橙色/红色条纹X未知寄存器未初始化、多驱动冲突如果你看到整个总线的波形都是红色的等高线先不要急着改代码。把鼠标停在相应信号上ModelSim状态栏会显示它的当前逻辑状态。如果显示Z优先检查实例化的端口连接如果显示X优先检查复位和寄存器初始化。4.2 排查顺序比直接改代码更重要遇到异常波形十有八九是以下四个原因之一按顺序排查效率最高。第一层顶层端口连接完整性。这是最容易被忽略的。对比设计模块和Testbench实例化的端口列表数一遍每个信号是否都被连接。我用过的一个土方法把实例化代码里的端口名对齐肉眼扫一遍谁的宽度不匹配、谁漏了一根线一目了然。第二层复位信号是否有效。如果你的设计是有复位逻辑的而Testbench里rst_n从头到尾没拉低过仿真开始后寄存器里全是遗留的X波形自然就红了。正确做法是仿真开始后给一个明确的复位脉冲然后再释放。有些设计还依赖复位信号的长度比如至少保持一个时钟周期这些细节都要在Testbench里体现。第三层激励是否真正生效。时钟和复位都对了但输出还是不动检查Testbench里数据或控制信号有没有initial或always块驱动。有时候你写了激励但被一个#100跳过了或者forever里面漏了分号信号就一直是初始值。第四层仿真时间够不够长。这也是一个高频坑。如果设计里有分频器、计数器或者状态机初始仿真时间只跑了几十纳秒可能连第一个有效状态都没到。我通常先跑一个较大的时间范围比如run -all拉满再用光标卡具体时间点分析。4.3 delta cycle为什么仿真时间没走信号却变了还有一个进阶概念许多人第一次遇到时很困惑——如果信号在0时刻就出现异常或者在一个always块里出现竞争很可能和delta cycle相关。ModelSim是基于事件驱动的仿真器它在同一个时间戳内会处理多个事件这些事件按先后顺序排队执行。每处理一个事件就是一个delta cycle。比如一个组合逻辑assign y a b;当a在10ns时刻变化y的更新并不会发生在10ns而是发生在10ns1delta。这个delta在波形上看起来没有时间流逝但对仿真行为有决定性影响。如果你发现组合逻辑的输出总是滞后一拍或者$display打印出来的值和自己推导的不一致多半是没考虑到同一时间戳内事件的先后顺序。解决方法是把敏感列表写全避免和混用还有就是不依赖没有延时的组合逻辑反馈。5. 用.do脚本把仿真流程固化下来5.1 一个方便复用和修改的sim.do模板GUI里点来点去只是第一步实际工程中每次打开ModelSim重新打命令太浪费时间。ModelSim支持Tcl脚本把固定的操作流程写成.do文件以后双击就能跑。下面这个模板是我自己一直在用的适配绝大多数单模块仿真# sim.do # Usage: vsim -do sim.do quit -sim vlib work vlog counter.v vlog tb_counter.v vsim -voptargsacc work.tb_counter # 添加全部信号 add wave -r /tb_counter/* # 运行并保存波形 run -all wave zoom full在ModelSim命令行执行vsim -do sim.do它会自动完成从清空仿真库到跑完波形的全过程。这里-voptargsacc这个参数值得说一句它指示仿真器做优化编译但保留全部信号可访问性方便对内部信号加波形。如果某些信号在优化过程被裁掉了add wave会提示找不到加上这个参数就能避免。5.2 批量回归与日志抓取仿真不只是跑一次就完事真实项目中代码改动后要反复回归。ModelSim的batch模式可以不需要启动GUI在命令行直接跑vsim -c -do sim.do -wlf result.wlf sim.log 21-c表示命令行模式不弹出图形界面适合服务器上跑或批量执行。-wlf result.wlf会把波形数据保存到wlf文件后面用vsim -view result.wlf随时能打开查看。 sim.log 21把输出重定向到日志文件。日志抓取对定位问题太有用了。Testbench里加上$display打印关键信息回归完后用编辑器搜索Error、Warning关键字几秒钟就能筛掉一批问题。如果波形文件特别大注意加log -r /tb_counter/*来限制记录范围否则wlf文件会膨胀得很快。6. 与Principal 工具联动的实操细节6.1 在Quartus Prime中配置ModelSim路径现在主流的FPGA开发流程是把Quartus和ModelSim组合使用。Quartus里配置ModelSim很简单打开Tools - Options - EDA Tool Options在ModelSim一栏填上vsim.exe的完整路径。配置好后Quartus编译完工程可以直接通过Tools - Run Simulation Tool - RTL Simulation一键启动ModelSim。这里要提醒一点从Quartus启动ModelSim时它会自动生成一个simulation/modelsim目录里面包含所有IP核的仿真库和编译脚本。这不是废话很多新手不知道这个目录是自动生成的以为需要自己手动把每个IP的库搬过去。6.2 IP核仿真时的库路径设置一旦设计里用了PLL、RAM、FIFO这些IP核直接vlog自己的代码再vsim大概率会报找不到altera_mf、altera_lnsim这类库。原因是IP核仿真需要厂家提供的仿真模型。解决办法是在vsim之前先把对应的库映射上vlib work vmap altera_mf D:/eda/modelsim_2020.4/altera_lib/altera_mf vmap lpm D:/eda/modelsim_2020.4/altera_lib/lpm vmap altera_lnsim D:/eda/modelsim_2020.4/altera_lib/altera_lnsim路径请根据你实际安装位置修改。如果你在安装ModelSim时勾选了Simulation Libraries这些目录通常已经存在。vmap的作用是把逻辑库名映射到物理路径和Linux里的软链接类似。IP核仿真文件里通过altera_mf这种逻辑名引用库所以必须做好映射。6.3 新手最常见的三个仿真坑我的实测经验第一用initial块在多个地方驱动同一个信号。有些同学在Testbench里既想给data_in赋初值又想在另一个initial里发激励结果两个initial同时对同一个信号赋值ModelSim会报Multiple drivers警告波形出现意外的红X。解决办法是保证一个信号只在一处驱动或者用force/release在特定时间段覆盖。第二忘记加timescale。timescale 1ns/1ps告诉仿真器时间单位和精度。如果省略ModelSim会按默认值处理而默认值可能和你期望的时钟频率差几个数量级。你的Testbench明明写的是#5延迟结果波形上看到的却是等了5秒是的仿真器真有可能这么干。所以每个Testbench文件第一行都要写。第三$finish放错了位置。最典型的情形是时钟生成任务的forever循环还在跑但主激励提前执行了$finish导致后面大量逻辑没被覆盖。更隐蔽的问题是$finish放在initial块的中间后面的语句永远不会执行。我在做覆盖率统计时就栽过这个跟头——漏了一大块分支没跑到还以为是代码写少了。7. 建议的调试环境和搜错策略7.1 ModelSim结合其他文本工具的多窗口调试体验很多工程师习惯只开ModelSim和编辑器两个窗口。坦白说ModelSim自带的波形窗口足够用但代码编辑体验一般。我现在的习惯是用VS Code或UltraEdit写代码ModelSim只负责编译和仿真。改完代码切到ModelSim敲vlog重新编译出错信息会指出文件和行号再切回编辑器修改。比在ModelSim内置编辑器里改代码舒服得多。如果你发现某段代码怎么编译都过不去先复制报错信息到搜索引擎里搜这一招能解决至少70%的问题因为FPGA社区的报错基本上都被人问过。搜的时候保留原版英文错误信息和版本号比搜中文能得到更精确的答案。7.2 用Wave窗口的正确姿势来分析时序波形窗口不只是看一眼信号高低电平那么简单。几个高频操作必须熟练鼠标中键拖动可以平移波形滚轮缩放是基本操作。在波形窗口插入光标Insert Cursor可以测量两个时刻之间的时间差。状态栏直接显示Delta。信号值可以在波形上直接看十六进制还是二进制默认可能显示二进制右键信号选择Radix - Hexadecimal改成十六进制。时序分析时我会同时叠加多个相关信号时钟、复位、使能、有效数据。你看一条总线是否在时钟沿之后稳定变化用光标量一下数据变化点和时钟上升沿之间的偏移。如果数据变化点覆盖了时钟沿这个设计在真实硬件上很容易出亚稳态问题功能仿真看不出来但时序仿真会暴露出来。8. 回归与验证的边界8.1 功能仿真通过不等于板上也能过写到最后必须泼一盆冷水ModelSim功能仿真跑通了只能说明逻辑行为正确。它不验证物理器件上的建立保持时间、不验证布线延迟、不验证驱动强度。那些要交给Quartus/Vivado自带的时序仿真引擎去跑或者上板实测。我在开发一个串口模块时深有体会功能仿真里波特率发生器的计数器变化非常漂亮输出波形无懈可击但上板后和PC通信偶尔丢一个字节最后查出来是硬件引脚约束没配好导致某个信号在板上有毛刺。这个问题的根因ModelSim完全检测不到。所以正确的态度是ModelSim帮你兜住逻辑层面的底但不要指望它兜住物理世界的所有问题。8.2 一个实用的纪律先仿真再综合我现在给自己定了一条纪律任何模块不管多简单先写Testbench先跑通ModelSim仿真再进综合工具。哪怕是20行的译码器也一样走完流程。这个习惯养成之后代码质量有明显提升因为你在写激励的过程中会逼自己把模块的输入输出想清楚很多接口设计缺陷在这一步就暴露了。如果你看到这里还觉得有点晕试着动手把第三节的计数器代码原封不动敲一遍然后跑通它。那个过程会让你把所有概念串起来。第一次跑通波形的那种感觉和第一次点亮LED一样上瘾。
返回列表