1. 先用一段亲身经历说明为什么我非要把ModelSim接进高云工程先交代一下背景。我在一个项目里用高云GW1NR-9做视频采集端的控制逻辑开发环境用的是Gowin SoftwareRTL代码写了一版功能仿真跑下来也没发现什么大问题但一旦把PLL、RAM这些IP核加进去Gowin自带的仿真器就开始让我头疼波形查看的交互逻辑不顺批量跑回归测试的时候脚本能力又弱项目组其他人习惯用ModelSim发过来的测试用例我用自带仿真器经常打不开反过来我发给他们的波形文件他们也看不了。这就逼着我做了一个决定把高云FPGA的开发流程和ModelSim的仿真流程打通以后所有功能仿真、时序仿真和回归测试统一在ModelSim里完成。这个环境搭建过程前前后后踩了不少坑网上资料又零散写这篇文章就是想把这些经验和完整的操作链路整理出来让后面接手的人少走弯路。这篇文章适合谁看正在用高云Gowin FPGA做开发、觉得自带仿真器不够趁手的工程师以及学校实验室里做国产FPGA方向、需要做较复杂仿真验证的学生。不需要你有非常深的ModelSim基础我尽量从原理讲到实操每一步都说明白为什么这么做。2. 开工前先理清高云IDE、ModelSim与仿真库之间的关系2.1 两者的分工逻辑很多人第一次接触联合仿真时会懵仿真不是IDE自带功能吗为什么还要单独接一个ModelSim其实这里面的逻辑不复杂你要理解FPGA仿真的几个阶段。在FPGA设计流程里功能仿真也叫行为仿真验证的是RTL代码的逻辑行为这个阶段不关心门延迟和布线延迟只看波形和逻辑功能对不对。时序仿真是综合之后、布局布线之后做的仿真模型里加入了器件的实际延迟信息用来检查时序是否满足设计要求。高云的Gowin Software自带仿真器能跑这两种仿真但它的波形查看和分析能力比较基础遇到复杂设计、大文件波形、长时间仿真时体验会下降不少。ModelSim作为业界使用最广泛的HDL仿真器之一优势在于编译速度快、仿真性能稳定、debug工具成熟还支持Tcl脚本做批量仿真和自动化回归。把这些优势接入高云FPGA开发流程里等于给设计验证环节换了一套更顺手的工具链。2.2 高云工程中哪些文件是给ModelSim用的在Gowin IDE里当你的工程综合、布局布线完成后IDE会生成一系列仿真相关文件。你需要从里面找到这几类它们是联合仿真的核心仿真网表文件由综合/布局布线后的设计导出通常是.vo后缀Verilog Output里面就是门级或原语级的网表描述。功能仿真的话也可以用RTL源文件直接编译。测试激励文件你自己写的testbench.v或.sv后缀。高云器件仿真库文件包括GowinSim.v、GowinRTL.v等这些是高云FPGA原语的仿真模型ModelSim不认识高云器件自带的各种原语比如PLL、OSC、DDR3的硬核控制器必须把高云的仿真库加进去ModelSim才知道这些原语的行为是什么。IP核仿真模型工程里用了高云的IP核比如PLL、RAM、FIFO之类的Gowin IDE会在生成IP时同时生成对应的仿真模型。这部分文件也必须在ModelSim里编译。打个比方高云IDE相当于生产了一套“零件”和“图纸”而ModelSim是加工车间它需要先把零件清单仿真库和图纸网表文件、Testbench全部读进去才能开始运转。缺了任何一样出来的结果必然有问题。2.3 版本匹配是第一个隐形坑版本问题我放在最前面说因为它最容易让人心态崩溃。ModelSim和Gowin IDE各自的版本更新速度不同但绝大多数情况下ModelSim SE 10.x系列比如10.6、10.7和Gowin Software 1.9.x系列搭配是没问题的我目前主力用的就是ModelSim SE 2020.4配Gowin 1.9.9。比较常见的坑是Gowin IDE版本过旧生成的原语列表和最新ModelSim的编译语法有冲突或者是ModelSim版本太旧很多教程还在用SE 10.1对SystemVerilog的新特性支持不全高云IP核生成的仿真模型编译不过去。建议安装前先确认Gowin IDE版本对应的Release Note里面会标明支持的第三方EDA工具版本范围。如果你用的恰好是两者都比较新的版本并且遇到编译报错优先考虑换成官方文档推荐的那一档ModelSim版本别在编译报错上死磕性价比很低。3. 在高云IDE里把设计“打包”给ModelSim3.1 建立工程阶段的建议好的开始是成功的一半。在高云IDE里新建工程时我建议提前想清楚哪些代码要参与仿真。不需要参与仿真的模块比如只用于上板测试的下载逻辑可以放到单独的目录里避免后面全部一股脑编译进ModelSim。工程建好后编写RTL代码时也要养成一个习惯把时序逻辑的复位信号统一起来。后面ModelSim仿真出现大面积未知态X态时大部分原因就是复位信号没有处理好这个点我在后面专门讲。3.2 生成仿真文件的具体操作当RTL代码写完、综合通过之后在Gowin IDE里执行以下操作来导出仿真文件在菜单栏找到“Tools”点击下拉菜单中的“Compile Simulation Files”。在弹出的对话框里选择仿真工具。如果列表里有ModelSim相关选项就直接选没有就选“Others”。点击确定后IDE会在工程目录下生成一个sim文件夹里面包含了编译好的仿真库文件、仿真网表文件和一组示例脚本。打开sim目录你会看到.v格式的库文件例如GowinSim.v以及由当前设计导出得到的网表文件。有一点要特别注意如果你在工程里用了IP核确认一下sim目录里是否包含了对应IP的仿真模型。有些版本的IDE不会自动导出所有IP的模型文件需要你到IP生成目录下手动拷贝。漏掉这一点到ModelSim那边编译就会报Module not found的错误而报错信息又不直接提示是IP模型缺失排查起来很费劲。3.3 关于测试激励的编写习惯Testbench是联合仿真的入口写的好坏直接决定调试效率。我的习惯是每个testbench文件头部固定写清楚四个信息——被测模块名、时钟频率、复位信号极性、本用例验证的功能点。高云FPGA的IP核像PLL、OSC对复位和时钟稳定时间有严格要求。比如高云PLL的lock信号在仿真模型里通常需要几十微秒才能拉高如果你在testbench里只等了几个时钟周期就检测lock信号仿真波形看起来就会很奇怪逻辑明明没问题输出却一直不对。这种情况下testbench里应该等足够的仿真时间再开始喂数据或者在复位释放后、操作真正开始前等待PLL lock信号稳定。4. ModelSim侧配置仿真库映射与do脚本4.1 第一次打开ModelSim需要做的事打开ModelSim先在你的工程目录下建一个专门放仿真文件的文件夹比如msim_work。然后在ModelSim的Transcript窗口里先把当前工作目录切换过去cd 你的工程仿真目录接下来要创建一个库用来存放编译后的设计文件。ModelSim里的work库是默认库但如果多个工程复用同一个ModelSim安装环境建议每个工程单独建库避免冲突。指令很简单vlib work vmap work workvmap命令的作用是把逻辑库名和物理路径映射起来这一步相当于给ModelSim指路让它在编译源文件时知道把结果放到哪里去。4.2 编译高云仿真库和设计文件然后就是编译高云仿真库。把高云IDE生成的GowinSim.v和GowinRTL.v编译进ModelSim指令是vlog -work work 高云IDE安装目录/sim/GowinSim.v vlog -work work 高云IDE安装目录/sim/GowinRTL.v接着编译你自己的设计文件。这里有个关键点编译顺序一定要正确。如果一个模块A调用了模块B必须先把B编译进来再编译A。Verilog里虽然不严格要求先声明后使用但ModelSim在增量编译时对顺序很敏感一旦顺序反了就会报Instantiation of module B failed。编译顺序比较稳妥的安排是先编译库文件再编译IP仿真模型最后编译自己的RTL文件和testbench。如果你用了高云的IP那IP的仿真模型要放在你的RTL之前编译。再来说vlog命令的参数。一般我建议加上-sv表示允许SystemVerilog语法因为高云有些IP核的仿真模型是用SystemVerilog写的。如果你在编译过程中报了一堆看不懂的语法错误先检查一下是不是没加-sv参数。4.3 用do脚本跑仿真而不是手动点鼠标ModelSim支持命令行操作高手基本都靠do脚本而不是界面上点鼠标因为do脚本可复用、可自动化还能放进版本管理里。下面是一个简洁的do脚本模板vlib work vmap work work vlog -work work -sv D:/project/gowin_sim/GowinSim.v vlog -work work -sv D:/project/gowin_sim/GowinRTL.v vlog -work work -sv D:/project/rtl/top.v vlog -work work -sv D:/project/rtl/pll_ip.v vlog -work work -sv D:/project/tb/tb_top.v vsim -t 1ps -L work work.tb_top add wave -position end sim:/tb_top/* add wave -position end sim:/tb_top/uut/* run -all简单说下每行各干了什么。vsim -t 1ps是设置仿真时间精度为1皮秒这个精度对高云FPGA的时序仿真基本够用。-L work是把work库作为设计库加载进来这个参数很关键不加的话ModelSim可能找不到高云原语的定义。add wave不加的话仿真完了之后查看波形面板一片空白。我一般会把顶层testbench的信号和被测模块uut内部的信号都加进去这样既能看整体交互也能看内部状态机跳转。如果内部信号层级比较深在波形窗口里操作不方便直接在do脚本里手动指定路径是最高效的方式。run -all表示一直跑到有$finish或$stop为止。如果你的testbench里没写这两个系统任务那就得在do脚本里用run -us 100这种指定仿真时长的写法不然仿真会一直挂在那里跑不完。4.4 初始化脚本路径要注意的事高云IDE生成的库文件路径里常常包含空格比如默认安装目录C:\Program Files\Gowin\Software_1.9.9在ModelSim的Tcl环境里直接使用带有空格和反斜杠的路径会很麻烦。建议一在do脚本里统一用正斜杠/代替反斜杠ModelSim解析起来更稳。建议二把高云安装目录下的仿真库文件拷贝到工程目录下再引用或者用vmap建立虚拟映射不要每次都写一长串绝对路径。我是直接把GowinSim.v、GowinRTL.v拷到一个sim_lib目录里这样脚本拿到任何一台电脑上都能用不会因为安装路径不一致而报错。5. 波形一片红仿真不跑顺着这条链路自查5.1 红线和X态先看时钟和复位如果你打开波形看到大片红色或者高阻态Z线别急着怀疑代码逻辑先查两个东西时钟有没有产生、复位有没有正常释放。时钟问题高云FPGA内部的时钟源OSC原语和PLL在仿真模型里不是一通电就立刻稳定输出的。尤其是PLL从输入时钟稳定到lock信号拉高中间有锁定时间。在功能仿真里很多testbench直接把PLL的lock信号忽略掉直接给被测模块喂时钟这样做在简单逻辑里能跑通但在带复位状态机的设计里容易出现模块在复位尚未释放时收到了跳变的时钟边沿导致状态机进入非法状态。我的建议是testbench里仿真开始后先等一段时间比如10us再释放复位之后再过几个周期才开始真实激励。这里有个小知识点ModelSim波形显示中红色通常表示X态未知态蓝色或绿色表示0/1正常电平高阻态Z则显示为一条水平线具体颜色可在设置里调。看到X态优先怀疑信号没有被初始化。在Verilog里reg类型的信号仿真初值是X如果你在复位逻辑里只处理置位不复位那这个信号就会一直保持X态进而污染后续所有逻辑。5.2 编译报错从第一条错误开始看ModelSim编译报错时Transcript窗口会给出错误行号和错误类型。很多人习惯从头开始一条一条修这个方向其实不对。我的习惯是先看第一条报错然后回到源文件对应的那一行把上下文看一遍。往往第一条报错是根因后面的报错全是“连锁反应”。常见的高云联合仿真编译报错有几种Module not found某个模块没有被编译进来优先检查是否漏了高云IP仿真模型。Port width mismatch端口位宽不匹配。高云IP核有的端口是可变位宽你在例化时位宽写错了编译时就会报这个错。Unknown identifier变量未定义检查是不是漏了声明或者把reg和wire用混了。Cant find module Gowin_PLL典型的高云原语库没编译进去。报错信息的处理策略很简单不要沉浸在报错堆里而是回到“库文件、IP模型、RTL、testbench”这条编译链路上按顺序排除。5.3 testbench顶层没选对仿真跑了但波形空这个坑特别隐蔽ModelSim的vsim仿真指令里指定的是testbench模块名但如果你用了run -all时仿真立即结束、波形窗口里什么信号都看不到那有可能是你的testbench里自己加了$finish或者仿真时间单位设置得太大导致一眨眼就跑完了。还有一种情况testbench里例化了被测模块但例化名称写得和RTL中不一致或者在testbench里把被测模块接到了错误端口上导致内部信号完全没有翻转。检查方法很简单——在波形窗口里添加被测模块内部的信号如果内部信号没有任何翻转那就不是仿真环境的问题而是testbench的连接问题。5.4 高云PLL仿真的lock信号处理关于PLL的仿真我再多写几句。高云的PLL模型在ModelSim中需要额外的编译选项某些版本需要-L gowin参数来指定库路径否则PLL仿真模型里的内部逻辑会无法解析。具体表现是PLL输入时钟正常但输出时钟一直是X态或Z态lock信号一直拉不高。解决办法是在vsim指令中显式加载高云库vsim -t 1ps -L work -L gw1n9 work.tb_top等号后面的gw1n9是具体器件系列的库名不同器件的库名不一样比如GW2A系列可能是gw2a。具体库名可以到高云IDE安装目录下的sim文件夹里查看.v文件名和库目录名来确认。6. 工程化提速回归测试、批处理与团队协作6.1 把do脚本推广到全项目组单机环境搭好之后如果项目组有多个人要做仿真验证建议把do脚本、文件清单和目录结构统一下来。我们的做法是每个模块的设计目录下都放一个sim子目录里面固定放三个文件——compile.do负责编译、simulate.do负责启动仿真并加载波形、run_all.batWindows批处理一键依次执行前两个脚本。run_all.bat的内容也不复杂echo off vsim -c -do compile.do -do simulate.do这里的-c是让ModelSim以命令行模式运行不弹图形界面速度更快适合批量回归。6.2 用批处理跑回归测试功能改动频繁时手动在ModelSim里加载一个testbench跑一遍点一下效率太低了。我通常会把所有测试用例写成一个个独立的do脚本再用一个主控脚本依次调用do test_case1.do do test_case2.do do test_case3.do每个test_case的do脚本里在run -all之后加一行检查语句if {[runStatus] eq break} { echo Test FAILED } else { echo Test PASSED }runStatus可以获取当前仿真的状态如果仿真中途遇到了断言失败或者$stop状态就是break此时脚本自动打印FAILED。如果正常跑到了$finish状态是finish就会打印PASSED。这样批量回归下来哪个用例挂了、挂在哪一看输出日志就知道。6.3 覆盖率收集与跨工具协作ModelSim还有一个很好用的功能是覆盖率收集。在高云FPGA项目里覆盖率分析可以帮你判断testbench写的是否充分。在vsim指令里加上vsim -t 1ps -c -coverage -L work work.tb_top仿真结束后用coverage report命令导出覆盖率报告。不过我得提醒一句覆盖率只是衡量验证充分性的一个指标不是越高越好重点关注的应该是状态机分支覆盖和关键信号翻转覆盖别把时间浪费在追求不切实际的100%行覆盖率上。团队协作方面高云IDE工程文件.gws和ModelSim的do脚本建议一并纳入版本管理。这样任何人拉下代码后先跑一下run_all.bat就能在十几分钟内确认当前RTL版本能不能通过最基本的仿真验证。这个习惯对多人并行开发、频繁合并代码的场景价值非常大。6.4 混用Verilog和SystemVerilog时的处理如果你的高云工程里既有.v文件又有.sv文件ModelSim编译时建议统一加-sv参数。不要觉得-sv只对.sv文件有意义在实际操作中某些.v文件里如果包含了SystemVerilog的语法片段比如带类型的interface不加-sv编译也会报错。全工程统一用vlog -sv编译是最省心的做法。7. 我在实际项目中沉淀的几个使用习惯环境搭好只是第一步真正让这套联合仿真流程发挥价值的是日常使用的习惯。我分享几个自己实践下来比较管用的细节。第一模型库文件一定要固定版本。高云IDE升级之后GowinSim.v可能会有变动但如果你旧工程还依赖旧版本的库不要急着把旧库文件删掉。我们的做法是每个工程目录下放一个sim_lib文件夹里面记录当前工程所用的库文件版本号。升级IDE后新建工程时会主动拷贝新库文件进来而旧工程维持旧库不变。这样既保证了新工程的特性支持又不会让旧工程莫名其妙跑不通。第二命名规范对仿真调试效率的影响很大。时钟信号统一用clk做前缀、复位统一用rst_n或rst做后缀同时要有注释说明极性状态机信号统一叫state_*。这样在ModelSim波形窗口里加信号时能通过前缀快速筛选不用在长长的信号列表里翻半天。第三仿真波形存档。跑完一个重要验证用例后把波形文件.wlf格式一并提交到共享目录或者连同testbench一起提交到版本管理。不要觉得波形文件占空间浪费等出了问题回头排查时你会感谢当时存下来的波形。亲眼见到过好几个同事重构完代码后拍胸脯说逻辑没变结果对比波形才发现细微的时序差异没有历史波形这个结论得花好几天才能查出来。第四遇到ModelSim界面卡顿的情况优先想到是不是波形数据量太大。ModelSim加载几百MB的wlf波形时界面会明显卡顿。这种情况下用add wave只添加关注的关键信号而不是把所有信号一股脑全加进去。仿真时长也可以适当缩短分段dump波形比一次性跑超长仿真再回头翻找信号要高效得多。这套高云FPGA加ModelSim的联合仿真环境前前后后帮我在多个项目里省下了大量验证时间。尤其是高频改动代码、需要反复回归验证的阶段用脚本批量跑一遍比在GUI里手动操作快出好几倍。如果你是刚接触高云FPGA不久建议先把环境搭起来并用一个最简单的流水灯工程整个流程跑通一遍不急着在复杂设计上直接切换等熟悉了各个库文件和脚本的作用再逐步搬到正式项目里用。