1. 为什么要在ModelSim里仿易灵思工程
搞过FPGA的人都知道,仿真验证是开发流程里最花时间、也最容易翻车的一环。易灵思(Efinix)这几年在国产FPGA里势头很猛,Ti60F225这种片子接口资源丰富、逻辑密度也够用,很多人拿它做TDC直方图采集、图像预处理、MIPI收发这类项目,但不少朋友在把Efinity工程和ModelSim联起来的时候卡了壳。有人是不确定用哪个仿真库,有人是编译网表报一堆错,还有人干脆不知道Efinity导出哪些文件才能给ModelSim用。
先说结论:易灵思官方IDE叫Efinity,工具本身是自带在线逻辑分析仪(ILA)的,但那个东西只能在板子上跑起来之后抓信号,适合看真实硬件波形。你要在代码写对之前就验证RTL行为、做时序走查,还是得靠外部仿真器。ModelSim作为最普及的通用仿真工具,对易灵思网表的支持是够用的,关键是把“编译库、网表、SDF反标”这三件事的顺序和路径搞清楚。
这篇文章不聊空理论,直接按我实际踩过的坑来讲:从软件版本怎么搭配、路径避开哪些雷,到Efinity导出网表和SDF、ModelSim编译易灵思库、写do脚本跑联合仿真,再到波形全是红线和时序反标警告的排查方法。整套流程我自己在Ti60F225的项目里跑通过,你按这个顺序走,基本半小时内能出波形。
2. 环境准备:版本、路径和库文件
很多联仿失败根本不是代码问题,而是环境没配对。易灵思的Efinity和ModelSim之间的配合,对版本和路径比官方文档写的还要敏感。
2.1 软件版本怎么选
Efinity目前主流的稳定版本是2022.x和2023.x系列,ModelSim这边建议用Intel FPGA Starter Edition,也就是大家常说的ModelSim-Intel。为什么要强调这个版本?因为易灵思的仿真库是基于Verilog-2001和SystemVerilog写的,老版本的ModelSim SE在某些语法解析上会直接报错,尤其是接口数组和带参数化的模块,新版本对SystemVerilog的支持明显更稳。
我自己测试过ModelSim 10.5b、10.7和ModelSim-Intel 10.5b,都能和Efinity 2022.2配合使用。注意一点:别用太新的ModelSim版本去配老Efinity,有时反而会出现时间精度单位不兼容的问题。
提示:如果你的电脑上装了多个仿真器,记得在系统PATH里把ModelSim的bin目录放在一个不会被其他版本干扰的位置,联仿时ModelSim是从PATH里找vsim/vlib/vlog这些命令的。
2.2 安装路径的两个雷
第一个雷是安装路径里不能有中文和空格。Efinity如果装在C:\Program Files\Efinity这种带空格的路径下,生成do文件或调用仿真器时路径字符串经常被截断,报一个莫名其妙的"file not found"。我习惯装在D:\eda\efinity,ModelSim装在D:\eda\modelsim,全路径纯英文,一次跑通。
第二个雷是两个软件的安装目录最好在国际盘符下。有人图省事装在系统默认目录,仿真跑起来产生的临时文件越来越大,C盘满了之后ModelSim直接罢工,波形出不来还以为是库有问题。
2.3 库文件清单:ModelSim需要认识易灵思的什么
易灵思的网表仿真不像Xilinx的uni9000那么复杂,核心库就三个来源,你一定要在联仿前确认它们的存在:
- Efinity安装目录下的
lib/verilog文件夹,里面有易灵思原语库,比如Efx_prim.v、Efx_seu.v这类文件。 - 工程导出仿真文件时生成的顶层网表文件,通常是
xxx.vm或xxx.v。 - 布局布线后导出的SDF时序文件,后缀是
.sdf。
有些教程会建议直接把易灵思的库文件夹全部vlog编译进ModelSim,我实测不需要全部,只要把当前工程用到的原语库编译进去就够了。但你如果图省事,全部编译也没问题,最多多花几十秒。
3. 实操:Efinity侧网表和SDF的生成
联仿的源头在Efinity工程里,如果Efinity侧没把仿真产物生成对,ModelSim那边再折腾也白搭。我见过太多人卡在这一步:代码综合没问题,但不知道在哪里导出布局布线后的仿真文件。
3.1 创建工程与约束检查
先把Efinity工程建好,管脚约束和时序约束最好在综合之前就写清楚,尤其是时钟约束。联仿时的SDF反标依赖时序约束,约束写得不准,仿真波形出来但时序检查会报一堆hold violation,看起来像设计问题,实际是约束问题。
我有个习惯:工程里单独建sim目录,和src、constr分开。导出仿真文件时输出路径直接指到sim目录,ModelSim工程也建在这个目录下,两边共享路径,避免跨盘符带来的权限问题。
3.2 布局布线后导出仿真产物
Efinity完成布局布线之后,在菜单里找File > Export > Simulation或Tools > Generate Simulation Netlist,不同版本菜单位置略有差异,但核心选项一致:
- 输出格式选Verilog网表。
- 勾选SDF输出选项,生成时序反标文件。
- 选择输出目录,建议用
sim文件夹。
这一步做完,你会在sim目录下看到:
top_module.vm:布局布线后的网表,模块名通常是顶层名加后缀。top_module.sdf:时序反标文件,包含单元延迟和互连延迟。- 可能还有一些约束文件的格式化副本。
导出时有一个容易忽略的选项是Use gated clock,如果你的设计里有门控时钟,这个选项务必按Efinity提示勾选,否则后续时序仿真时门控逻辑会变成不定态,波形上一片红色。
3.3 导出目录里到底有什么
我把自己项目里的仿真目录列个参考:
sim/ ├── top_module.vm ├── top_module.sdf ├── testbench_ti60.v ├── run_ti60.do └── modelsim.initestbench_ti60.v是手写的测试激励,run_ti60.do是ModelSim批处理脚本,modelsim.ini是ModelSim库映射文件。modelsim.ini这个文件你不一定要手动建,ModelSim运行vlib和vmap时自动生成,但如果你要固定库映射,提前放一份也是好的。
4. 实操:ModelSim侧库编译与仿真脚本
到了ModelSim这一侧,所有动作都可以在一个do文件里完成。我建议别在GUI里手动点,一方面慢,另一方面一错就乱了。脚本化是联仿效率的关键。
4.1 手动vlib/vlog的完整流程
打开ModelSim,先切到sim目录,然后按顺序执行下面几条命令。第一条是建库:
vlib work vmap work work接着编译易灵思原语库。这里路径根据你自己的Efinity安装位置改:
vlog -work work D:/eda/efinity/lib/verilog/Efx_prim.v vlog -work work D:/eda/efinity/lib/verilog/efx_technology.v再看你的设计是否用到易灵思的硬核IP,比如MIPI、DDR、RISC-V软核。如果有,还要编译对应IP库。硬核IP的仿真模型一般在Efinity安装目录的ip子目录或工程生成的IP目录下,文件名一般带_sim_model后缀。
然后编译顶层网表:
vlog -work work sim/top_module.vm最后编译测试激励:
vlog -work work sim/testbench_ti60.v编译顺序不能乱。先原语库,再网表,最后testbench,因为testbench里会例化顶层模块,如果顶层模块还没编译到work库里,vlog会报找不到模块名。
4.2 写一个do文件跑通仿真
完整的do文件参考如下:
vlib work vmap work work vlog -work work D:/eda/efinity/lib/verilog/Efx_prim.v vlog -work work D:/eda/efinity/lib/verilog/efx_technology.v vlog -work work sim/top_module.vm vlog -work work sim/testbench_ti60.v vsim -L work -t 1ps work.testbench add wave -position end sim:/testbench/* run -all这段脚本里有两个地方值得解释。第一是-t 1ps,这是时间精度设置。易灵思SDF文件里的延迟精度通常是皮秒级别,时间精度设成1ps能保证反标不丢小数位。第二是-L work,这个选项告诉vsim在work库里搜索用户自定义的模块,缺少它时经常报Load of top failed。
跑起来之后,如果你在wave窗口看到信号全都是红色或全X,先别怀疑代码,看下面第5部分的排查。
4.3 SDF反标与时序检查
上面的do脚本能跑功能仿真,但严格来说还没把时序检查带进去。要做带时序的仿真,需要把SDF文件反标到网表模块上。
在vsim命令后面加一行:
vsim -L work -t 1ps work.testbench -sdftyp /testbench/dut_instance=sim/top_module.sdf这里的dut_instance是testbench里例化顶层模块的实例名,路径必须和testbench中的层次一致。如果反标成功,ModelSim的Transcript窗口会打印一行SDF反标的信息,包括反标文件路径和模块名。如果没打印,说明路径写错了,时序检查不会报错但时序数据完全没生效。
反标之后,你可以通过打开Simulate > Runtime Options里的Enable timing checks选项来开启时序检查。或者直接在vsim命令行加:
+notimingchecks注意这个选项是关闭时序检查用的,只有在你只关心功能、不关心时序时才加。调试SDF问题时要避免误加。
5. 常见问题与排查技巧实录
联仿设置本身不难,难的是报错信息经常不直白。我把这几个月被问得最多的问题统一整理成速查表,再补充几个我亲身踩过的坑。
5.1 编译报错排查
| 报错关键词 | 可能原因 | 解决方案 |
|---|---|---|
Library "work" not found | 没有先执行vlib/vmap | 确认do脚本里先建库再编译 |
Cannot find module | testbench例化名和网表顶层名不一致 | 打开top_module.vm文件确认顶层module名 |
vlog-13069 | 原语库编译语法不兼容 | 换ModelSim-Intel版本,或升级Efinity |
Near ",": syntax error | 原语库版本和ModelSim不匹配 | 尝试只编译用到的原语文件,而非全部 |
编译问题暴露最多的场景是:全部编译易灵思库时报语法错。因为有些原语文件依赖特定的仿真器宏定义,ModelSim不认识。解决思路是只编译当前设计用到的模块,配合+define+做宏定义。比如编译原语库时加:
vlog -work work +define+SIMULATION D:/eda/efinity/lib/verilog/Efx_prim.v很多宏定义在官方文档里有说明,仿真目录下通常也有一个编译脚本模板,优先参考那个模板。
5.2 SDF警告与反标失败
SDF反标时报$sdf_annotate警告,最常见原因是实例路径写错。ModelSim里的SDF反标路径取决于你的网表层级,不一定等于testbench里的例化路径。我的经验是先在testbench里加一行:
initial $sdf_annotate("sim/top_module.sdf", dut_instance);这个系统任务的好处是路径写法和-sdftyp一致,而且如果反标失败,仿真器会给出更明确的报错层级。用$sdf_annotate定位问题后,再改正vsim命令行的-sdftyp参数。
还有一种情况是反标成功但波形里没有延迟,检查一下vsim启动时的log里有没有这一行:
# Loading SDF file: sim/top_module.sdf如果没有,说明SDF没被加载,看看文件是否存在、路径是否相对ModelSim当前工作目录。
5.3 波形全是红线
红线就是不定态,出现原因通常集中在三种可能。第一,时钟或复位没有正确初始化。testbench里的initial块只给寄存器赋值,没有驱动复位信号和时钟。第二,SDF反标成功但时序约束没写,导致仿真器认为所有路径都在hold violation,输出全X。第三,编译库时缺少易灵思原始未驱动信号的定义,比如某些硬核复位引脚悬空。
我建议先跑一遍纯功能仿真,不带SDF,看波形是不是正常。如果功能仿真是好的,加上SDF之后才变红,就排查时序约束;如果功能仿真就全红,那就是testbench激励的问题。
5.4 回归与效率提升
联仿设置好之后,还有一个使用习惯问题值得提。项目迭代过程中,RTL每改一次,就要重跑一遍综合和布局布线,网表变了SDF也要重新导出。我习惯在do脚本里把编译网表和SDF反标的过程拆成两段,先编RTL快速跑功能仿真,确认逻辑无误后再编网表跑时序仿真。
这样能省不少时间。尤其在做TDC直方图这类对延迟敏感的项目时,关心的是延迟链的进位逻辑和时钟偏斜,功能仿真看不出问题,必须靠时序仿真验证。但每次只为了看一眼布局布线效果就去跑整个流程,效率太低,分开跑是更务实的方案。
说到易灵思库精度和延迟的一个细节:ModelSim默认时间单位为1ns,如果你不设置-t 1ps,SDF里0.01ns级别的延迟会被舍入成0,仿真波形看起来像没有延迟,但某些触发沿却因为实际有延迟而采样出错。所以这个参数不要省。
6. 一些值得养成的习惯
最后分享一个我在多项目联仿配置里沉淀下来的习惯。把所有易灵思库文件放到ModelSim工作目录下的efx_lib子目录,用vmap efx_lib ./efx_lib映射成逻辑库名,而不是全部堆在work库。网表和原语库分离后,回归测试时只需要重编网表和testbench,原语库不用动,编译时间明显缩短。
另外,testbench尽量写成自检形式,不要纯看波形。比如TDC直方图这种设计,输出一个bin值后,testbench里用$display或assert自动比对预期值,跑完看Transcript窗口的结果即可,不用肉眼看几十路波形。联仿不仅是让波形出来,更要让验证结果可自动判定,这样才能在迭代中真正省心。
我个人在实际操作中的最大体会是:联仿配置这一关,难的不是ModelSim命令,而是对工具链“谁生成什么、谁消费什么”的整体理解。Efinity负责把设计变成可仿真的网表和时序文件,ModelSim负责把这些文件变成可观察的波形和时序报告。当路径、版本、库映射这三样都顺了之后,后续所有FPGA项目都能复用同一套流程,一次搭好,长期受益。