1. 为什么安路TD与Modelsim联合仿真值得花时间折腾
安路科技的TD(Tang Dynasty)系列FPGA开发工具链在国内中小规模逻辑设计圈子里用得越来越多,尤其是Anlogic EG4、AL3等系列芯片,性价比确实能打。但只要你用过TD自带的仿真工具,大概率会有一种"能用但不好用"的感觉——波形加载慢、信号层级不直观、调试手段有限。所以很多人第一反应就是把TD和Modelsim搭起来做联合仿真。
这个思路本身没问题,Modelsim在仿真波形查看、断点调试、覆盖率分析这些方面的成熟度,确实是TD自带仿真器短期内追不上的。但问题在于,TD和Modelsim的联合流程不像Vivado+Modelsim那样有官方手把手的文档,很多环节需要自己踩坑。我自己在安路EG4系列上做过几个项目,从IP核编译到波形调试,前后踩了不少坑,有些问题卡了一整天都找不到原因。这篇文章就把整个流程拆开讲清楚,包括IP核怎么编译、库怎么映射、波形为什么是红线、覆盖率文件怎么合并这些高频问题。
不管你是刚接触安路TD的新手,还是已经用过一段时间但联合仿真总是出问题的老手,下面这些内容应该都能帮你省下不少时间。我尽量把每个步骤的"为什么"也讲清楚,这样你遇到变体情况时能自己判断怎么处理。
2. 联合仿真的整体思路与工具链选型
2.1 为什么不用TD自带仿真器而选Modelsim
TD自带的仿真工具底层其实也是基于Model Technology的仿真内核做了一定封装,但封装之后暴露给用户的控制粒度就粗了很多。举个很实际的例子:TD自带仿真器在查看跨模块信号时,经常需要你手动展开很多层,而且波形窗口的缩放和标记操作响应比较慢。当你的设计里有多个时钟域、几十个状态机的时候,这种体验会让人很烦躁。
Modelsim的优势在于:第一,波形窗口的操作效率高,支持通配符添加信号、支持波形分组保存;第二,断点调试和单步执行更灵活,可以在信号变化时触发断点;第三,覆盖率分析功能完善,能直接生成HTML报告。这些在复杂设计的验证阶段非常关键。
但选Modelsim也有代价。你需要自己管理仿真库的编译和映射,TD生成的IP核需要单独编译到Modelsim的库中,仿真脚本需要自己写。这些就是本文要重点解决的问题。
2.2 工具版本搭配的坑
版本搭配这件事,我踩过最大的坑就是Modelsim版本和TD版本不匹配导致IP核仿真模型编译失败。安路TD每个大版本更新时,IP核的仿真模型可能会有细微变化,比如使用了新的SystemVerilog语法特性,老版本Modelsim就不支持。
我实测下来比较稳的搭配是:
| TD版本 | Modelsim版本 | 备注 |
|---|---|---|
| TD 5.6.x | Modelsim SE 2020.4 | 最稳定组合 |
| TD 5.5.x | Modelsim SE 2019.2 | 需要手动改仿真模型中的部分语法 |
| TD 5.7.x | Modelsim SE 2022.2 | 支持较好,推荐 |
如果你用的是Modelsim DE版本,注意DE版本对混合语言仿真的支持需要额外配置,SE版本在这方面省心很多。另外,Linux环境下Modelsim的安装和激活流程和Windows有差异,后面会单独说。
2.3 联合仿真的两种模式
TD和Modelsim联合仿真有两种模式:一种是手动模式,你自己写do脚本、自己编译库、自己启动仿真;另一种是半自动模式,利用TD生成仿真脚本框架,你再手动调整。我推荐新手从半自动模式入手,等熟悉了整个流程再转手动模式。
半自动模式的核心思路是:TD在综合完成后可以生成一个仿真用的文件列表和顶层testbench模板,你把这个模板拿到Modelsim里,配合自己编译的库,就能跑起来。手动模式则完全脱离TD,所有文件列表和编译顺序都自己控制,适合需要精细控制仿真环境的场景。
3. IP核编译与仿真库映射的核心细节
3.1 安路IP核的仿真模型在哪里
安路TD安装目录下,IP核的仿真模型通常放在$TD_INSTALL_PATH/ip/simulation或者类似路径下。不同IP核的仿真模型组织方式不一样,有的是已经编译好的Verilog源文件,有的是加密的.vp文件,还有的是需要你手动编译的库文件。
以常用的PLL、FIFO、RAM这些IP为例,它们的仿真模型一般在ip/<ip_name>/sim/目录下。你需要做的是把这些仿真模型文件加入Modelsim的编译列表。但注意,不是所有IP核的仿真模型都能直接编译通过,有些需要依赖安路的基础仿真库。
3.2 编译安路仿真库的完整步骤
安路的基础仿真库通常叫anlogic_sim_lib或者类似名字,里面包含了安路原语(primitive)的仿真行为模型。这个库必须最先编译,因为所有IP核的仿真模型都依赖它。
具体操作步骤:
- 找到安路仿真库源文件目录,一般在
$TD_INSTALL_PATH/sim/lib/下。 - 在Modelsim中创建一个新的库,命名为
anlogic_lib。 - 将库源文件按依赖顺序编译进去。注意,安路的仿真库文件之间有依赖关系,不能随便打乱顺序。通常的顺序是:基础原语 → IO模型 → 存储器模型 → 专用IP模型。
- 编译完成后,检查是否有编译错误。如果有关于
include文件找不到的错误,需要手动指定include路径。
我自己的做法是写一个compile_anlogic_lib.do脚本,把编译命令固化下来,这样每次换项目只需要改路径就行。脚本大概长这样:
# compile_anlogic_lib.do vlib anlogic_lib vmap anlogic_lib anlogic_lib vlog -work anlogic_lib +incdir+$env(TD_INSTALL_PATH)/sim/include \ $env(TD_INSTALL_PATH)/sim/lib/anlogic_prim.v vlog -work anlogic_lib +incdir+$env(TD_INSTALL_PATH)/sim/include \ $env(TD_INSTALL_PATH)/sim/lib/anlogic_io.v vlog -work anlogic_lib +incdir+$env(TD_INSTALL_PATH)/sim/include \ $env(TD_INSTALL_PATH)/sim/lib/anlogic_mem.v注意:
$env(TD_INSTALL_PATH)这个环境变量需要提前设置好,或者在脚本里直接写绝对路径。Windows下路径中的反斜杠要改成正斜杠,否则Modelsim的Tcl解释器会报错。
3.3 IP核仿真模型的编译顺序与依赖处理
编译完基础库之后,接下来编译IP核的仿真模型。这里有个关键点:不同IP核之间可能有依赖关系。比如你用了PLL,PLL的仿真模型可能依赖基础库中的时钟管理原语;你用了DDR控制器,它的仿真模型可能依赖多个基础IP的仿真模型。
我的建议是:先编译所有IP核共用的基础仿真模型,再编译各个IP核自己的仿真模型。如果编译过程中报"module not found"的错误,说明有依赖没有提前编译,需要找到对应的源文件先编译。
另外,安路有些IP核的仿真模型是加密的,文件名后缀可能是.vp。这种文件需要用Modelsim的vlog命令配合-protect选项来编译,或者直接使用安路提供的预编译库。如果你在编译时看到"encrypted source file"之类的提示,就属于这种情况。
3.4 库映射的常见错误与排查
库映射(vmap)这一步看起来简单,但出错率很高。最常见的错误是映射路径写错,导致Modelsim找不到库。另一个常见错误是映射了错误的库版本,比如把不同TD版本编译的库混用。
排查库映射问题的方法:在Modelsim的Transcript窗口输入vmap命令,查看当前所有库的映射关系。确认anlogic_lib指向的路径是正确的,并且该路径下确实有_info文件。如果没有_info文件,说明库没有编译成功。
还有一个坑是:Windows下路径中包含空格或中文字符,Modelsim可能无法正确识别。所以TD的安装路径最好全英文、无空格。
4. 从TD工程到Modelsim仿真的完整实操流程
4.1 TD工程侧的准备工作
在TD中完成综合之后,不要急着直接点仿真。先做几件事:
第一,确认你的顶层模块名和testbench模块名不冲突。TD生成的仿真文件列表中,顶层模块名默认是top,如果你的testbench也叫top,会冲突。
第二,检查TD生成的仿真文件列表。在TD的Project菜单下找到Generate Simulation File或者类似选项,生成一个.f文件列表。这个列表包含了你的设计源文件、IP核仿真模型、以及TD自动生成的仿真顶层。
第三,确认IP核的仿真模型路径已经包含在文件列表中。有时候TD不会自动把IP核仿真模型加入列表,需要你手动添加。
4.2 Modelsim工程的建立与文件组织
我习惯在Modelsim中建立一个独立的工程目录,结构如下:
sim_project/ ├── lib/ # 编译好的库 ├── src/ # 设计源文件(从TD工程复制或链接) ├── tb/ # testbench文件 ├── ip_sim/ # IP核仿真模型 ├── scripts/ # do脚本 └── wave/ # 波形保存目录建立工程时,先编译库,再编译设计源文件,最后编译testbench。编译顺序很重要:基础库 → IP核仿真模型 → 设计源文件 → testbench。
4.3 仿真脚本的编写要点
一个完整的仿真do脚本应该包含以下步骤:
# run_sim.do # 1. 清理之前的仿真 quit -sim # 2. 映射库 vmap anlogic_lib ./lib/anlogic_lib vmap work ./lib/work # 3. 编译设计文件 vlog -work work +incdir+./src ./src/*.v vlog -work work +incdir+./tb ./tb/tb_top.v # 4. 加载仿真 vsim -t 1ps -L anlogic_lib -L work work.tb_top # 5. 添加波形 add wave -r /* # 6. 运行仿真 run 100us这个脚本里,-t 1ps指定了仿真时间精度,-L指定了库搜索路径。add wave -r /*是递归添加所有信号,方便调试但会拖慢仿真速度,正式跑的时候建议只添加关键信号。
4.4 波形调试的实操技巧
波形调试阶段,我常用的几个技巧:
第一,用add wave -divider给波形分组,把不同时钟域的信号分开显示,看起来清爽很多。
第二,用virtual signal把总线信号拆分成多个单bit信号,方便观察每一位的变化。
第三,用when命令设置条件断点。比如when {/tb_top/state == 3'b101} {echo "State reached"},当状态机进入特定状态时自动暂停。
第四,波形保存。用write wave命令把当前波形保存为.wlf文件,下次可以直接打开,不用重新跑仿真。
5. 高频问题排查与避坑经验实录
5.1 波形全是红线怎么办
这是被问得最多的问题。波形显示红线,通常意味着信号的值是X(不定态)。造成不定态的原因有很多,按出现频率排序:
| 现象 | 可能原因 | 排查方法 |
|---|---|---|
| 所有信号都是红线 | 时钟或复位没有正确初始化 | 检查testbench中时钟是否翻转、复位是否释放 |
| 部分信号红线 | 该信号驱动源未初始化 | 追溯该信号的驱动逻辑,检查寄存器初值 |
| IP核输出红线 | IP核仿真模型未正确编译或未加载 | 检查库映射和IP核仿真模型是否编译成功 |
| 仿真开始后一段时间才变红 | 跨时钟域信号未同步 | 检查CDC路径,添加同步器 |
我遇到过一次特别隐蔽的情况:PLL的锁定信号一直是红线,查了半天发现是PLL仿真模型中的参考时钟频率参数和testbench中给的时钟频率不匹配。PLL仿真模型在频率不匹配时会输出不定态。所以,IP核的参数配置一定要和testbench中的激励一致。
5.2 IP核编译报错的典型场景
IP核编译报错最常见的有三类:
第一类,找不到include文件。安路IP核仿真模型中经常include一些公共定义文件,这些文件在TD安装目录的sim/include下。解决方法是在vlog命令中加+incdir+指定include路径。
第二类,语法错误。有些IP核仿真模型使用了SystemVerilog语法,如果你的Modelsim版本不支持或者没有开启SV支持,就会报语法错误。解决方法是在vlog命令中加-sv选项,或者升级Modelsim版本。
第三类,模块重复定义。如果你同时编译了多个IP核的仿真模型,而它们都定义了同一个模块名,就会冲突。解决方法是只编译你实际用到的IP核仿真模型,或者用-L指定库搜索顺序。
5.3 覆盖率文件合并的操作方法
Modelsim的覆盖率文件默认是每个仿真过程生成一个.ucdb文件。如果你跑了多次仿真,想合并覆盖率结果,可以用vcover merge命令:
vcover merge merged.ucdb run1.ucdb run2.ucdb run3.ucdb合并之后,用vcover report生成文本报告,或者用vcover html生成HTML报告。注意,合并覆盖率文件时,要求所有.ucdb文件来自同一个设计版本,否则合并结果没有意义。
5.4 Linux环境下Modelsim的安装与激活注意事项
Linux下Modelsim的安装和Windows差别不大,但有几个点需要注意:
第一,安装包要选对。Linux版本的Modelsim安装包通常是.run文件,需要先赋予执行权限再运行。
第二,激活方式。Linux下通常使用LM_LICENSE_FILE环境变量指定license文件路径。如果license文件路径中有空格,需要用引号括起来。
第三,库编译时的路径问题。Linux下路径分隔符是正斜杠,和Windows一致,但环境变量的引用方式不同。在do脚本中引用环境变量要用$env(VAR_NAME)。
第四,权限问题。Linux下如果Modelsim安装在系统目录,普通用户可能没有写权限,导致库编译失败。建议把库编译到用户目录下。
6. 联合仿真效率提升的进阶技巧
6.1 用脚本自动化整个仿真流程
手动敲命令效率太低,我习惯把整个流程写成一个Makefile或者Shell脚本。比如:
#!/bin/bash # run_sim.sh export TD_INSTALL_PATH=/opt/anlogic/td vsim -c -do "do scripts/compile_lib.do; do scripts/run_sim.do; quit -f"这样每次只需要执行一个命令就能完成编译和仿真。如果配合CI工具,还能实现自动化回归测试。
6.2 波形对比与回归测试
Modelsim支持波形对比功能。你可以把一次"黄金仿真"的波形保存下来,后续每次修改设计后,把新波形和黄金波形对比,快速发现异常。具体操作是:用write wave保存参考波形,然后用compare wave命令进行对比。
回归测试的思路是:写一个脚本,自动跑多个testcase,每个testcase跑完后检查覆盖率是否达标、是否有断言失败。如果都通过,就自动进入下一个testcase。
6.3 仿真性能优化的几个手段
仿真速度慢是常态,尤其是门级仿真。几个优化手段:
第一,减少波形记录信号数量。只记录关键信号,不要add wave -r /*。
第二,使用-novopt选项关闭优化,虽然会降低仿真速度,但能保证信号可见性。如果不需要看内部信号,可以开启优化提升速度。
第三,把仿真时间精度从1ps改成1ns,减少仿真事件数量。
第四,对于长时间仿真,使用run -all配合when条件断点,避免手动干预。
6.4 与Vivado联合仿真的差异对比
如果你同时用过Vivado+Modelsim和安路TD+Modelsim,会发现几个明显差异:
| 对比项 | Vivado+Modelsim | 安路TD+Modelsim |
|---|---|---|
| 库编译 | Vivado自动完成 | 需要手动编译 |
| IP核仿真模型 | 自动加入 | 需要手动加入 |
| 仿真脚本 | 自动生成 | 需要手动编写或调整 |
| 波形调试 | 集成度较高 | 需要手动配置 |
| 文档支持 | 完善 | 相对较少 |
这些差异意味着安路TD+Modelsim的联合仿真需要你投入更多前期配置时间,但一旦配置好,后续使用体验和Vivado+Modelsim差别不大。
7. 几个容易被忽略的细节
7.1 testbench中的时钟和复位处理
安路IP核的仿真模型对时钟和复位有特定要求。比如PLL仿真模型要求参考时钟在复位释放前就必须稳定翻转,否则PLL可能无法锁定。FIFO仿真模型要求复位信号至少保持一定周期,否则内部状态机可能不初始化。
我的做法是在testbench中统一处理时钟和复位:先给时钟,等100ns,再释放复位,再等100ns,然后开始激励。这样能避免大部分初始化问题。
7.2 仿真模型与综合模型的差异
IP核的仿真模型和综合模型行为可能不完全一致。仿真模型为了加快仿真速度,可能会简化某些行为。比如某些IP核的仿真模型不模拟真实的建立保持时间,导致仿真通过但实际硬件不工作。
所以,仿真通过只是第一步,最终还是要上板验证。仿真阶段重点关注功能正确性,时序问题留给静态时序分析工具。
7.3 文件路径中的空格和特殊字符
这个问题看似小,但坑了很多人。TD安装路径、工程路径、库路径中如果有空格或中文字符,Modelsim可能无法正确解析。我见过最离谱的情况是路径中有一个空格,导致vlog命令把路径拆成了两个参数,编译直接失败。
所以,从TD安装开始,所有路径都用全英文、无空格、无特殊字符。这是最省心的做法。
7.4 仿真日志的保存与分析
Modelsim的Transcript窗口内容默认不保存,仿真结束后就没了。如果仿真过程中有警告或错误,你需要在仿真结束后回头查看,就很麻烦。
我的做法是在do脚本开头加transcript file sim_log.txt,把Transcript内容保存到文件。仿真结束后,用grep搜索"Error"、"Warning"、"X"等关键词,快速定位问题。
8. 我个人在实际操作中的体会
安路TD+Modelsim联合仿真这件事,说难不难,说简单也不简单。核心难点不在技术本身,而在于信息分散、文档不全,很多问题需要自己摸索。我刚开始用的时候,光是让第一个仿真跑起来就花了两天时间,大部分时间都耗在库编译和路径配置上。
后来我总结出一个原则:把所有配置固化成脚本,把所有路径写成变量。这样换项目的时候,只需要改变量值,不用重新踩一遍坑。另外,遇到问题先看Transcript窗口的报错信息,Modelsim的报错通常很具体,顺着报错找原因,比盲目搜索效率高得多。
还有一个经验是:不要等到设计全部写完才开始搭仿真环境。在项目初期就把仿真流程跑通,哪怕只是一个简单的计数器,这样后面加IP核、加复杂逻辑的时候,仿真环境已经就绪,能省很多时间。
最后分享一个小技巧:如果你在Modelsim中调试时发现某个信号一直看不到,先检查这个信号是否被优化掉了。在vsim命令中加-novopt选项可以关闭优化,保证所有信号可见。虽然仿真速度会慢一些,但调试阶段这个代价是值得的。