
1. 为什么我建议用安路TD ModelSim做联合仿真安路TD是安路科技官方的FPGA开发套件它覆盖了从代码编写、综合、布局布线到比特流生成、下载调试的完整流程。TD配套了自带的仿真环境但在处理大规模设计、复杂时序验证、长时间回归测试时自带的仿真器在波形查看效率、调试手段、脚本化批量跑仿真方面确实不如ModelSim顺手。如果你是从Xilinx Vivado或Intel Quartus转过来的对ModelSim或QuestaSim这套仿真流程应该不陌生。安路TD本身是支持导出仿真工程和仿真模型给第三方EDA工具用的官方推荐对接的就是ModelSim SE或DE版本。TD负责“设计”ModelSim负责“验证”各干各擅长的活这是这套方案最核心的思路。这篇文章适合谁正在用安路TD做FPGA开发想用ModelSim做功能仿真前仿真的工程师刚接触安路国产FPGA平台被“TD里怎么调用ModelSim”“仿真库里到底编译哪个文件”这类问题卡住的新手想把仿真流程脚本化、自动化减少重复点鼠标操作的效率派。我会把从IP核配置、仿真模型导出、ModelSim工程搭建、testbench编写、波形分析到常见报错排障的完整链路过一遍。很多细节是官方文档没写全、但实际工程中一定会踩到的我尽量把这些坑都摊开讲。2. 环境选型与版本匹配2.1 安路TD版本怎么选安路TD目前主流版本有TD 4.x、TD 5.x、TD 6.x等不同版本对芯片系列的支持范围不一样比如早期版本的TD只支持ELF系列后来逐步扩展到EF2、EG4、EG5等。我的建议是用开发板厂商或者安路FAE提供的最新稳定版不要追最新版也不要停在太老的版本新版本TD对ModelSim接口的适配更好仿真模型导出脚本也更完善。安装TD时注意安装路径不要带中文和空格ModelSim也一样。Windows下建议装到D:\Tools\TD、D:\Tools\modelSim这样的路径避免后面编译库、仿真路径解析时出现奇怪报错。我自己第一次装的时候图省事直接装在桌面结果路径里带中文导致ModelSim脚本里读取IP模型时一直报文件找不到排查了一下午才反应过来是路径编码问题。2.2 ModelSim版本怎么选TD对ModelSim的对接方式并不复杂本质就是通过编译脚本和仿真模型文件实现所以ModelSim SE、DE、PE版本都可以用。工程里我更推荐ModelSim SE-64如果你的license支持的话。ModelSim SE-64 2020.4是一个比较稳的版本对SystemVerilog支持、波形窗口性能、运行速度都够用。ModelSim DE 2022.2属于桌面级增强版界面更现代化对大工程的波形抓取和分析更友好。选哪个版本主要看你现有的license支持哪个版本是否需要同时兼容其他FPGA厂商的仿真。如果你既要弄安路又要弄Xilinx或者Intel的工程选一个统一的SE版本更省事。2.3 为什么不在TD里直接仿真很多新人会问安路TD不是有自带的仿真器吗为什么非要折腾ModelSim我给你说几个实际场景TD自带仿真器在波形查看功能上比较基础做简单的组合逻辑、小规模状态机还行一旦波形长、信号多缩放、查找、分组标记这些操作都不够快TD自带仿真器对SystemVerilog testbench的支持有限很多验证常用的随机约束、覆盖率、断言功能都没有团队协作时验证环境如果跑在ModelSim/QuestaSim上FPGA工程师用TD导出仿真工程给验证工程师大家才能在同一套工具链下工作。所以联合仿真的本质是TD负责“设计输入和IP配置”ModelSim负责“行为级验证”两边结合才能把国产FPGA的开发效率拉满。3. IP核配置与仿真模型导出3.1 在TD里配置IP核安路TD的IP核配置界面和Xilinx的IP Catalog、Intel的Platform Designer不太一样但思路一致。以最常见的PLL锁相环为例操作流程是在TD的工程管理器中选择“Tools”下的“IP Generator”不同版本的菜单名可能不同也有叫“Manage IP”的在目标芯片型号下选择IP类型比如PLL、RAM、FIFO、乘法器、DDS等填写参数输入时钟频率、输出时钟频率、倍频/分频系数、输出相位、占空比等。注意参考芯片数据手册确保输出频率在允许范围内点击生成TD会在工程目录下生成IP核的例化模板、约束模板和仿真模型。这里有个关键点IP核在联合仿真时需要“仿真模型”。TD生成的IP核仿真模型通常是经过封装的模型文件作用是让仿真器可以在功能层面模拟这个IP的行为而不需要真实物理实现。比如PLL的仿真模型会模拟锁定过程、时钟输出行为、失锁标志等这些行为对功能验证非常关键。3.2 导出仿真模型和编译库TD安装目录下一般带有针对ModelSim的仿真库编译脚本或预编译好的库文件。在TD中你可以通过工程菜单里的“Simulation”相关选项把当前设计用到的IP核、原语、simulation model一起整理到一个仿真目录。以我常用的TD工程目录为例典型结构是这样的ProjectA/ ├── src/ # RTL源码 ├── ip/ # IP核文件含仿真模型 ├── tb/ # testbench目录 ├── con/ # 约束文件.fdc/.sdc └── sim/ # 仿真脚本和输出目录联合仿真前需要找到TD分发包里针对ModelSim的库编译脚本通常是Tcl脚本或者批处理文件一般位于TD安装目录的sim_lib或template目录下。把安路基础的仿真库包含逻辑单元仿真库、IO仿真库、PLL等硬核模型在ModelSim里编译一次生成ModelSim认识的标准库。如果你在ModelSim里直接编译自己的RTL代码报错报“module not found”或者“unknown identifier”十有八九是仿真库没有编译或者仿真库文件选错了。3.3 测试平台testbench怎么搭Testbench是整个联合仿真的入口。建议单独放在tb目录不跟RTL混在一起。一个最低限度的testbench结构包含以下部分module tb顶层例化你要验证的设计模块DUT时钟产生用always #10 clk ~clk;注意时钟周期和IP核输入频率对应复位产生上电复位保持若干周期后释放激励产生使能、请求、数据输入等监测输出用$display、$monitor打印关键信息结束控制用$finish结束仿真。这里我特别想说一个新手很容易踩的坑安路TD生成的IP核例化模板里的端口未必和ModelSim的testbench直接兼容。尤其是带参数parameter的接口或者带有仿真专用端口的IP比如PLL的locked输出、FIFO的almost full等在testbench例化时端口名、位宽必须与IP核例化模板完全一致。请在TD生成的例化模板文件里复制端口列表不要手敲。4. ModelSim工程搭建与联合仿真实操流程4.1 建工程还是用脚本在ModelSim里做联合仿真有图形界面和脚本两种方式图形界面方式File - New - Project把源码加进去编译仿真。适合小工程和临时调试脚本方式写.do脚本用vlib、vlog、vsim、add wave、run命令完成全部动作。适合反复回归、多人协作、自动化验证。我强烈建议从一开始就用脚本方式哪怕工程小也学着用。理由有三点脚本可以重复执行改一次上电时序全流程重跑不用重新点几十次鼠标脚本可以放到版本管理里团队共享避免“我这边仿真能过你那边仿不过”的尴尬后续做自动化批量仿真、覆盖率跑批都是基于脚本扩展。4.2 标准仿真脚本流程以下是我在实际工程中常用的一套ModelSim脚本骨架你可以直接参考。编译阶段脚本# compile_sim.tcl # 清理旧库 if {[file exists work]} { vdel -all -lib work } # 建立新库 vlib work vmap work work # 编译安路仿真库根据实际路径调整 vlog -work work incdir../bsp_lib ../bsp_lib/glbl.v vlog -work work incdir../bsp_lib ../bsp_lib/*.v # 编译IP核仿真模型 vlog -work work incdir../ip/pll ../ip/pll/pll_sim.v vlog -work work incdir../ip/ram ../ip/ram/ram_sim.v # 编译RTL源码 vlog -work work incdir../src ../src/top.v vlog -work work incdir../src ../src/*.v # 编译testbench vlog -work work incdir../tb ../tb/tb_top.v仿真阶段脚本# run_sim.tcl vsim -voptargsacc work.tb_top # 添加波形 add wave -hex /tb_top/clk add wave -hex /tb_top/rst_n add wave -hex /tb_top/dut/* # 运行仿真 run 100us注意几个细节vsim -voptargsacc是为了禁止ModelSim在优化时把层次结构打平。不加这个的话add wave的时候经常看不到内部信号add wave -hex表示以十六进制显示也可以用-radix unsigned、-radix decimal等add wave /tb_top/dut/*是把DUT下面的所有信号一次性加进去层级结构比较深的时候很好用run 100us表示跑100微秒仿真时间。也可以用run -all但run -all如果testbench里没有$finishModelSim会一直跑建议在tb里用$finish控制结束或配合超时保护。4.3 时间单位和仿真精度设置ModelSim默认的时间单位和testbench里的timescale有关。建议在testbench文件开头写清楚timescale 1ns/1ps这样一个10MHz时钟对应周期100nsalways #50 clk ~clk;就是10MHz。如果不做timescale设置ModelSim可能会以默认的1ns/1ns或者更奇怪的方式解释语句导致波形时间轴和预期对不上。这个坑虽然低级但我见过不少人卡在这里仿真跑出来的时钟频率跟预期差几个数量级还找不到原因。5. 波形分析与调试实践5.1 波形分析的三个层次仿真跑完ModelSim的wave窗口里会显示添加的信号。实际调试中我一般按照从整体到局部的方式推进第一层看全局信号。时钟clk、复位rst_n、使能en、关键状态机状态信号。如果复位释放后状态机没有进入预期状态先检查复位释放时间和时钟是否正常第二层看总线级别的数据。比如AXI总线的读写通道握手信号、FIFO的读写计数、RAM的地址和数据。这里建议把位宽比较大的信号group成一个bus以十六进制显示阅读效率高很多第三层看IP核的输出。比如PLL锁定信号、FIFO的水位指示、串口发送的串行波形等。对照IP核数据手册里的时序图确认行为是否一致。5.2 常用波形操作技巧以下几个操作我几乎每次仿真都会用到光标测量点击wave窗口工具栏的光标按钮可以添加一个或者多个光标直接看两个事件的间隔。测量时钟周期、信号延迟、FIFO间隔等非常方便波形搜索CtrlF可以在波形里搜索指定数值或跳变沿比如搜索某个信号从0变1的时刻不用肉眼慢慢找信号重命名双击信号名改成有意义的名字比如clk_pll_out方便截图和讨论多bit信号切换显示进制在wave窗口右键信号选择Radix改成Unsigned或Hexadecimal。总线、数据信号强烈建议改成十六进制显示波形缩放Zoom Fit的快捷键是CtrlZ一键让所有波形都显示在窗口里。波形很长的时候要靠它快速总览。5.3 从波形倒推问题波形分析的本质是“对照设计预期找差异”。我的实操习惯是先确定一个“基准时间点”比如系统复位释放那个时钟沿。所有问题的讨论都以这个点为基准从输出端往下追。比如串口没有正常发出数据先看发送模块的tx信号有没有波形。如果tx一直是高说明根本没有开始发送再往前看发送触发信号有没有拉高再看FIFO有没有数据读到再看状态机卡在哪一步用$display在关键节点打印信息跟波形互相印证。波形告诉你“发生了什么”$display告诉你“代码内部执行到了哪里”两者对照定位速度翻倍。6. 常见报错与解决方案实录6.1 报错速查表我把这些年安路TD ModelSim联合仿真过程中常遇到的报错整理成了表格基本覆盖了90%的入门问题报错现象可能原因解决方案ModelSim无法启动提示license问题license配置错误或过期检查环境变量LM_LICENSE_FILE、license.dat路径确认license主机ID匹配vlog编译时报“Cannot find design unit”仿真库未编译或库路径错误先在ModelSim里编译安路仿真库再用vmap指定库名vsim时报“Unknown identifier”或“No such object”testbench例化端口名与DUT端口不一致从TD生成的例化模板复制端口列表逐位核对仿真波形全是红X或高阻Z信号未驱动、复位一直有效、IP核模型未加载检查testbench激励、复位释放时序、IP仿真模型是否编译成功波形有clk但所有信号都是X初值未初始化或复位时序不对testbench里给寄存器提供初始值确保复位释放时间足够编译IP核仿真模型报语法错误部分仿真模型需要SystemVerilog编译用vlog -sv编译对应文件仿真时间无穷无尽run -all不结束testbench没有$finish在合适位置加$finish或超时保护add wave看不到内部信号vsim没有加acc层次被优化vsim命令加-voptargsacc波形里带转义字符的信号名查询不到转义信号名问题用{/tb_top/dut/bus_name[7:0]}花括号包裹ModelSim闪退或崩溃工程路径过长、带中文、库损坏换短路径重新编译库避免中文目录6.2 高频问题详细排查第一类编译阶段的“Cannot find design unit”。这个报错的含义是ModelSim在编译当前文件时找不到它依赖的某个模块或单元。比如你的顶层例化了RAM IP但编译RAM IP的仿真模型文件排在后面或者根本没被编译进来。排查思路确认当前ModelSim工程里已经添加了所有需要的源文件。检查IP核目录下是否有仿真模型文件一般叫xxx_sim.v、xxx_bb.v或xxx_model.v确认编译顺序。Verilog对声明顺序要求不严格但依赖模块最好先编译。最简单的方式是先编译库文件和IP仿真模型再编译RTL最后编译testbench如果IP模型内部用了include文件记得把include路径指向IP目录或TD的include目录。第二类波形全红X或全高阻Z。红X在ModelSim里代表未知值高阻Z代表没有被驱动。出现这个现象通常不是“模型坏了”而是设计里信号根本没被赋初值或者复位信号一直保持有效导致所有时序逻辑没有跳变。排查步骤先看复位信号rst_n释放了没有。很多人写testbench时复位拉低后忘了拉高导致所有寄存器永远停留在复位状态看时钟信号有没有。没有时钟波形所有时序逻辑全都动不了看IP核的仿真模型有没有使能端口。比如PLL在没有locked之前输出时钟可能保持为0或X需要等locked信号拉高如果某一组信号是Z说明这组信号有多个驱动源互相竞争或者根本没连驱动。检查testbench里有没有对三态总线做上拉或下拉。第三类路径问题导致编译不到文件。Windows下TD生成的路径可能是相对路径也可能带反斜杠和空格。ModelSim脚本里建议统一使用正斜杠“/”并且对路径用花括号包起来set ip_dir {D:/Works/ProjectA/ip/pll} vlog -work work ${ip_dir}/pll_sim.v遇到“cant open file”之类的报错先检查路径是否存在、文件是否存在别急着怀疑工具坏了。6.3 我踩过的一些比较耗时的坑分享几个不算高频、但一旦遇到很费时间的坑。第一个坑TD生成的PLL仿真模型和ModelSim版本兼容性问题。早期某个TD版本生成的PLL模型里用了较新的SystemVerilog语法用老版本ModelSim编译直接报错。后来排查发现是模型内部用了接口相关语法解决办法是用更新的ModelSim版本或者在编译时打开SystemVerilog模式。第二个坑testbench里用过程赋值做延时注入但信号是wire类型仿真波形上看不到预期的毛刺。原因是wire类型在被连续赋值时如果用assign #5 a b;这个延时是对的但如果用过程赋值always (*) #5 a b;每次事件变化都会触发新的延时波形行为会退化。类似这种问题只能靠波形辅助判断单看代码很难发现。第三个坑FIFO IP核的仿真模型里带超时保护机制。某些FIFO核模型为了模拟硬件行为会检测空满边界如果testbench在FIFO满的时候还一直写入模型可能会打印错误信息或者直接挂起。这不是你的代码有bug是IP模型的保护机制生效了需要检查写使能和满标志的时序关系。7. 效率提升建议把仿真脚本沉淀成模板写过几次完整的联合仿真流程之后我建议你把自己的仿真脚本整理成一个“项目模板”目录包含compile_sim.tcl编译库和源码的脚本run_sim.tcl启动仿真、添加波形、控制run时间的脚本tb_template.vtestbench骨架时钟复位激励预设好README记录TD和ModelSim的版本对应关系、安装路径、注意事项。每次新开FPGA工程直接复制模板改一改顶层文件名和IP核路径就能省掉大量重复劳动。脚本化仿真还有个隐藏好处换电脑、换同事、换版本时只要脚本描述的是同一套流程仿真结果稳定复现不会因为“某人环境没配好”而互相甩锅。如果你的工程规模比较大可以考虑用ModelSim的batch mode跑仿真vsim -c -do compile_sim.tcl -do run_sim.tcl -do quit-c表示命令行模式不打开图形界面跑完后自动退出适合服务器上批量回归。波形文件可以用log命令保存到wlf文件里之后随时用ModelSim打开回看。最后再说一个我在工作中一直沿用的习惯每次仿真跑通之后把脚本和测试平台提交到版本管理并且顺手在README里记录一下“这个工程用的TD版本、ModelSim版本、IP核版本”。不要小看这个动作半年后再拾起一个老项目版本对应关系全靠这些记录才能快速恢复环境。好了以上就是我从IP核配置到ModelSim波形分析的全流程记录尤其把几个容易卡住人的地方拆开讲了。如果你用的TD版本或芯片系列跟我的不完全一样配置菜单和生成的文件名可能有些差异但整体流程和排查思路是通用的。下次做什么联合仿真卡住的时候回来翻翻这篇大概率能省下半天查文档的时间。