这一篇在干嘛?
依据安路官方《TangDynasty® 工程仿真指南》(SWUG107,TD 6.0)撰写。很多人写完 Verilog 直接综合、下板、看 LED 灯闪不闪——灯不闪就完全抓瞎。仿真是在软件环境里模拟真实设计行为的过程:施加激励、观察输出,在代码写错的几分钟内就发现它,而不是上板后浪费一晚上。这篇讲清 TD 里四个仿真阶段各验证什么、怎么编译仿真库、testbench 怎么写、功能仿真和时序仿真分别怎么跑。本系列目录见设计仿真指南首页。
为什么要仿真,四个阶段各管什么
先说结论:在较大的工程中通常需要进行反复的仿真测试,以保证功能和时序满足要求。TD 在 FPGA Flow 的每个阶段后都留了一个仿真窗口,越往后的阶段验证的内容越”接近真实硬件”。官方给出的全景图如下——横轴是 FPGA Flow 的推进方向,每个节点下挂一种仿真:

四个阶段逐一拆解:
① Behavioral Simulation(RTL 行为级仿真)——在 FPGA Flow 运行完 Read Design 并执行 open run 操作后进行。这是综合前的源码级仿真,检查代码的语法错误以及代码行为的正确性。它最便宜、跑得最快,能在设计早期发现逻辑 bug,节约整个设计周期。
行为级代码本身也有四个好处,这解释了为什么前期都写 RTL 而不是直接画电路:
- 行为级代码更具备可读性;
- 行为级代码具备复用性,可在其他工程设计中使用相同的 RTL 代码;
- 行为级代码具备可移植性,可移植到其他器件系列使用;
- 行为级代码的仿真更便捷。
② Post RTL Simulation(后 RTL 级仿真)——Flow 运行完 Optimize RTL 并 open run 后进行。仿真的是 TD 优化后形成的门级电路网表,验证工程功能的正确性。换句话说,它检查”TD 把你的代码优化改写之后,功能有没有被改坏”。
③ Post Gate Simulation(后 Gate 级仿真)——Flow 运行完 Optimize Gate 并 open run 后进行。仿真的是门级电路转换成安路器件库底层物理电路的网表,验证综合后设计功能的正确性。到这里,你的设计已经映射到了 EG4S20 真实存在的 LUT、寄存器等物理单元上。
④ Post Route Simulation(后 Route 级仿真)——Flow 运行完 Optimize Placement 和 Optimize Routing 两步并 open run 后进行。仿真的是布局布线优化后的电路网表,它又分功能仿真和时序仿真两种:时序仿真需要包含 SDF 文件(由 TD 自动生成),能保证布局布线后的设计满足功能和时序的要求。
常见坑:跳过 Behavioral 直接跑时序仿真
有些同学觉得”仿真一遍就够”,直接跑到布局布线后做时序仿真。问题在于:行为级仿真 5 秒能暴露的逻辑 bug,拖到时序仿真阶段可能要编译十几分钟才能发现,而且此时报错信息被网表、SDF 包裹,定位困难得多。正确节奏是每个阶段都仿一遍,让每一层”翻译”(综合优化 → 门级映射 → 布局布线)都经过验证。
TD 支持哪些仿真器
TD 各器件库支持的第三方仿真工具及版本如下表。注意 EG 系列(EG4S20 所在系列)支持 Modelsim、Questa 和 VCS 三种:
| 器件库 | 第三方仿真工具 | 版本 |
|---|---|---|
| EF2 / EF3 / EG / PH1 / PH2 / SF1 | Mentor Graphics Modelsim Simulator | 2020 |
| EF2 / EF3 / EG / PH1 / PH2 / SF1 | Mentor Graphics Questa Advanced Simulation | 2020 |
| EF2 / EF3 / EG / PH1 / PH2 / SF1 | Synopsys Verilog Compiler Simulator | 2020 2018 |
但有一个重要区别:Modelsim 和 Questa 已关联到 TD 设计环境中,可以在 TD 软件里点一下按钮就联合仿真;Synopsys VCS 暂未关联 TD,只能在 Linux 系统下搭建外部仿真环境(见后文第 8 节)。
按操作系统看支持的组合:
| 仿真工具 | Red Hat 64-bit Linux | Windows 10 64-bit |
|---|---|---|
| Mentor Graphics Modelsim Simulator | 是 | 是 |
| Mentor Graphics Questa Advanced Simulation | 是 | 是 |
| Synopsys Verilog Compiler Simulator (VCS) | 不支持,可使用外部仿真环境 | 不支持 |
对大学生来说,Windows + Modelsim 是最省事的组合:全程不用离开 TD 界面,也不用手写脚本。
TD 的两个联合仿真工具
TD 软件集成了两个联合仿真工具,各司其职:
- Simulation Libraries Compiler:编译 TD 器件库,让第三方仿真工具能够调用安路的器件模型。相当于把”安路硬件长什么样”翻译成你的仿真器听得懂的语言。
- Project Simulation:在 TD 各个 flow 阶段自动生成对应的仿真文件并实现一键自动仿真。它可以自动化生成 testbench 文件、仿真脚本以及网表文件,用户只需编写 testbench 的激励部分代码即可。
Simulation Libraries Compiler 从菜单 Tools → Simulation Libraries Compiler 打开。注意一个贴心细节:启动 TD 就可以使用该功能,无需打开工程——也就是说你可以在建工程之前就先把库编译好。
Project Simulation 从菜单 Tools → Project Simulation 打开。使用前提是:必须打开并编译工程。它覆盖 Behavioral、Post RTL、Post Gate、Post Route 全部四个阶段的仿真。
记住分工:Simulation Libraries Compiler 管”库”(一劳永逸),Project Simulation 管”每次仿真”(日常使用)。
仿真前的三件准备工作
官方清单——仿真之前需要完成:
- 进行仿真相关的设置:选择第三方仿真工具及其可执行文件目录等(VCS 仿真时必须勾选 flow_sim_filelist 选项,不勾选默认按 Modelsim 的仿真设置);
- 编译器件库;
- 创建 testbench 文件以及仿真脚本;
- 生成网表(Post RTL / Post Gate / Post Route 仿真需要网表文件——这一步 Project Simulation 会自动做)。
第一步:仿真设置
选择 Project → Project Setting 打开 Properties Setting 窗口,点击 Simulation 选项卡。这些设置与当前创建的工程相关联:

三个关键选项:
- simulator:选择仿真工具,目前支持 Modelsim 以及 Questasim;
- simulator_location:选择仿真工具的可执行文件路径;
- flow_sim_filelist:选择是否在各仿真阶段生成仿真所需的文件列表(包括 testbench 文件、源文件、IP 的仿真文件及其他源语文件)。打开后,用 Project Simulation 生成 testbench 和仿真脚本时还会在仿真目录下生成 .f 文件(filelist),可直接用于 VCS 仿真。
常见坑:.f 文件的位置
生成的 .f 文件用于 VCS 仿真时,需要放在 testbench 文件的同级目录,否则 VCS 找不到文件列表。
第二步:编译器件库
仿真库包括器件模型、IP 行为模型和时序模型。这些模型以库文件的形式随 TD 分发,需要用第三方仿真工具编译一次;编译后的库可以直接应用在多个工程设计中。
TD 把编译封装成了 GUI:Tools → Simulation Libraries Compiler 打开窗口:

各选项含义:
- Simulator:选择仿真工具,支持 Modelsim 以及 Questasim;
- Executable Path:仿真工具可执行文件路径。Windows 版 TD 默认会自动识别目标仿真工具的路径;只有当仿真器没加入 PATH 环境变量、或者想覆盖环境变量中的路径时才需要手动设置;
- Device Family:选择要编译的器件库,可选 common 库、EF2、EF3、EF4、EG、PH1、PH2、SF1 以及 all,默认编译全部;
- Compiled Library Location:编译库的保存路径,TD 会在该路径下创建相应文件夹存放各器件库的编译结果;
- Recompile:勾选后对已编译过的库重新编译,默认不重编译。
器件库名字和器件系列的对应关系(EG4S20 对应 EG 库):
| Library Name | Device Name |
|---|---|
| common | 器件公共库 |
| EF2 | SALELF® 2 系列 |
| EF3 | SALELF® 3 系列 |
| EG | SALEAGLE® 系列 |
| PH1 | SALPHOENIX® 1A 系列 |
| PH2 | SALPHOENIX® 2A 系列 |
| SF1 | SALSWIFT® 系列 |
点击 Compile 后,编译日志会实时显示在 TD 的 console 窗口,能看到进度和告警,Log 文件自动保存在编译库目录下。典型日志长这样:
# Start time: 16:01:46 on Feb 01,2024
---- Compiling module common,current progress: 100.00%. ----
# End time: 16:01:47 on Feb 01,2024, Elapsed time: 0:00:01
# Errors: 0,Warnings: 0
# ** Warning: (vlog-2070) Existing unprotected design unit "apm_memory_base" is being recompiled as protected.
# ** Warning: (vlog-2070) Existing unprotected design unit "apm_memory_sprom" is being recompiled as protected.
---- Compiling module ph2,current progress: 45.95%. ----看到 Errors: 0 就说明这批库编干净了。日志里那些 vlog-2070 告警属于”同一设计单元被重编译为 protected”的正常提示,不影响使用。
第三步:testbench 与仿真脚本
testbench 是用来仿真的激励文件,可用 Verilog 或 SystemVerilog 编写,三大功能:例化并初始化设计、为工程设计产生激励、模拟设计输出结果并验证功能准确性。它可以按顺序对特定输入施加激励,也可以包含子程序调用、从外部文件读取激励、条件激励等更复杂的结构。
相比交互式仿真,testbench 有两个独特优势:支持在整个设计过程中的重复仿真;可提供测试条件的文档。
仿真脚本(如 Modelsim 的 .do 文件)用于配置仿真环境、控制仿真任务、查看波形、分析结果,让测试流程可自动化、可重复。如果你已经编译过器件库,Project Simulation 生成的脚本会以映射库的形式加载器件库:

如果没编译过库,脚本会直接把器件源语所在路径写进去,跑脚本时现场编译源语——能跑,但仿真编译的时间可能稍长。
testbench 怎么写才好用
必须遵守的四条铁律
创建 testbench 文件时要注意:
- **添加
timescale**,即时间单位与仿真精度,例如timescale 1ns/1ps; - 仿真开始时刻给设计的所有输入赋初始值,以正确开启仿真过程;
- 在释放全局复位(GSR)之后启动时钟源;
- 使用安路器件源语时,testbench 需要调用全局复位文件。
第 4 条最容易被忽略。不同器件系列用的 GSR 源语不同:PH1_PHY_GSR 用于 PH1 系列,PH2_PHY_GSR 用于 PH2 系列,glbl 源语用于 EF2、EF3、EF4、EG、SF1 器件库——你的 EG4S20 属于 EG 系列,要用 glbl:

让 Project Simulation 替你搭骨架
Project Simulation 会自动生成 testbench 模板,你只需要在标注的位置填激励代码。模板长这样:
`timescale 1ns/1ps
module top_tb();
reg clk;
reg rst_n;
wire led;
//Clock process
parameter PERIOD = 10;
always #(PERIOD/2) clk = ~clk;
PH2_PHY_GSR PH2_PHY_GSR();
//Unit Instantiate
top u_dut(
.clk(clk),
.rst_n(rst_n),
.led(led)
);
//Stimulus process
initial begin
//To be inserted
end
endmodule常见坑:时钟周期没改
自动生成的 testbench 默认时钟周期 PERIOD 为 10ns(100MHz)。务必根据目标工程设计修改 PERIOD,否则仿真时钟频率和真实板子对不上,可能掩盖时序问题。
六条激励编写规范
以下规范来自官方建议,能显著提高测试的有效性和可维护性:
(1)用 initial 块或 always 块:initial 块适用于一次性的初始化和测试,always 块适用于循环测试。
(2)添加时间延迟:确保测试激励的顺序和时序正确:
initial begin
//Stimulate input signals
input_signal = 1'b0;
#10;
input_signal = 1'b1;
#20;
input_signal = 1'b0;
end
//Additional testbench code(3)用 task 或 function 封装:把重复使用的激励封装起来,提高可维护性和复用性:
task apply_test_pattern;
//Your testbench code here
endtask
//……
initial begin
apply_test_pattern;
end(4)添加注释:解释测试激励的目的、顺序和预期结果。
(5)使用测试配置参数:用 parameter 配置测试,不同场景可复用同一个 testbench:
parameter TEST_DELAY = 10;
initial begin
//Stimulate input signals
input_signal = 1'b0;
#TEST_DELAY;
input_signal = 1'b1;
#TEST_DELAY;
input_signal = 1'b0;
end(6)模拟结束:在最后添加结束语句,确保仿真正确收尾:
initial begin
//Your testbench code here
#100; //Run Simulation for 100 time units
$finish; //Finish simulation
end当然,规范是手段不是目的——最重要的是确保 testbench 能准确、全面地验证设计的功能和时序。
Modelsim 仿真脚本常用指令也值得存一份,手动跑 .do 文件或排查脚本问题时会用到:
| 命令 | 参数 | 释义 |
|---|---|---|
| vlib | 创建一个新的库或者设置当前工作库 | |
| vlog | 编译 Verilog 源代码文件 | |
| -incr | 启用增量编译 | |
| -vopt | 启用编译优化 | |
| -sv | 指示编译 SystemVerilog 源文件 | |
| vcom | 编译 VHDL 源代码文件 | |
| vmap | 设置库映射,将库名映射到具体的目录路径 | |
| vsim | 启动 Modelsim 仿真的指令 | |
| -t | 指定仿真的时间精度 | |
| -vopt | 启用编译优化 | |
| -L | 指定库的搜索路径 | |
| -sdftyp | 指定特定实例的 sdf 仿真文件 |
功能仿真实操:四个阶段逐一跑通
四个阶段的操作模式完全一致,区别只在于 Flow 跑到哪一步、菜单点哪一项。以 Post Route Simulation 为例,先看它的设置窗口:

窗口选项解读(这套选项在四个阶段的窗口里基本通用):
- Add an existing testbench:添加一个已写好的 testbench;
- Create a new testbench:创建新 testbench,选中并点击 OK 后,TD 会在工程目录下自动创建 testbench 文件并关联顶层模块;
- Function Simulation / Timing Simulation:只有 Post Route Simulation 阶段有这个二选一,其他阶段默认做功能仿真。选 Timing Simulation 并点 OK 后,TD 会在工程对应仿真目录下生成 SDF 延时文件;
- Run:勾选后,点 OK 即启动第三方仿真工具执行仿真脚本并生成仿真波形。
逐步操作
Behavioral Simulation:Flow 运行完 Read Design 阶段并执行 open run 后,选择 Tools → Project Simulation → Behavioral Simulation,添加准备好的 testbench,勾选 Run,点 OK 即自动执行行为级功能仿真。
Post RTL Simulation:Flow 运行完 Optimize RTL 并 open run 后,选 Tools → Project Simulation → Post RTL Simulation,添加 testbench、勾选 Run、点 OK。
Post Gate Simulation:Flow 运行完 Optimize Gate 并 open run 后,选 Tools → Project Simulation → Post Gate Simulation,同样操作。
Post Route Simulation(功能仿真):Flow 运行完 Optimize Placement 和 Optimize Routing 并 open run 后,选 Tools → Project Simulation → Post Route Simulation,添加 testbench,选择 Function Simulation,勾选 Run,点 OK。
如果选了 Timing Simulation,点 OK 后 TD 会生成 SDF 反标文件。SDF 文件是时序反标文件,用于后仿真时适配网表文件中 cell 的延迟信息:

选择 Create a new testbench 并选 Timing Simulation 后,TD 会在指定目录下一口气自动生成 testbench 文件、仿真脚本文件、网表文件以及 SDF 文件:

注意:在 Post RTL、Post Gate、Post Route 三个阶段,Project Simulation 会自动生成综合或布局布线后的网表文件,不需要你手动跑网表导出。
时序仿真与 SDF 反标
时序仿真只在 Post Route Simulation 阶段进行。前提是 TD 已计算出最差情况下布局布线后的延时信息——TD 做时序分析时会按最坏工艺角算延迟,所以 SDF 里的延时是保守值,时序仿真通过意味着真实芯片大概率也满足时序。
操作流程:Flow 运行完 Optimize Routing 并 open run 后,选择 Tools → Project Simulation → Post Route Simulation,添加 testbench,选择 Timing Simulation,勾选 Run,点 OK,TD 即调用第三方仿真工具自动执行后 Route 级时序仿真。这时的波形里能看到信号翻转的真实延迟,建立/保持时间违例会以警告形式暴露。
常见坑:忘了 SDF 的作用范围
SDF 适配的是网表文件中 cell 的延迟信息,不包含你 testbench 里自己写的
#10延迟。如果发现波形里”延迟怎么这么小”,先确认仿真的对象是网表而不是 RTL——拿 RTL 顶层的端口直接对照 SDF 延迟是查不到东西的。
时序仿真报错后不要急着改 RTL:先看时序分析报告的 WNS/TNS 负了多少,是关键路径太长(改 RTL)还是扇出/布线拥塞(加约束、改物理优化),对症下药。
进阶:Linux 下用 VCS 仿真
VCS 未关联 TD,但竞赛里如果用到 Linux 服务器做大规模仿真,VCS 依然可用。流程五步:
(1)设置环境变量:让系统能找到 VCS 的可执行文件。
(2)生成 filelist 文件:文件列表包括 testbench 文件、源文件、IP 的仿真文件及其他源语文件。仿真设置必须勾选 flow_sim_filelist,然后在对应 flow 阶段用 Project Simulation 工具,即可在仿真目录下自动生成 testbench 和 filelist 文件。
(3)编写 Makefile 脚本:在对应仿真目录下创建 Makefile,添加编译命令和选项。官方示例:
## vcs run options
VCS_FLAGS = vcs -full64 -R -sverilog -kdb -lca +v2k +lint=TFIPC-L -debug_access+all \
+nospecify \
-timescale=1ps/1fs \
-v2k_generate \
+incdir+. \
-ucli -i vcs_ucli.tcl \
-t ps -l ./$(LOG)/test_fec_only$(COMPILE_TIME).log
run:
mkdir -p $(LOG)
rm -rf simv* csrc
$(VCS_FLAGS) $(TB_TOP) $(SIMDefines) -f $(FILELIST)
mv *.fsdb $(LOG)(4)运行 VCS 编译:执行 make 命令启动仿真编译。
(5)查看波形:编译完成后通过 DVE 或 verdi 工具查看仿真波形。若添加了 verdi 环境变量并在 Makefile 中加了 verdi 命令选项,可用 make verdi 命令直接启动。
VCS 做 SDF 反标有两种方式:
- 在 Makefile 中调用:
vcs -sdf min|typ|max:instance_name:file.sdf,其中 min|typ|max 选择最小/典型/最大时序信息,instance_name 指定时序信息应用的实例名称,file.sdf 是 SDF 文件; - **使用 sdf_annotate (“sdf_file”[,module_instance][,“sdf_configfile”][,“sdf_logfile”][,“mtm_spec”][,“scale_factors”][,“scale_type”])`,参数细节查 VCS 文档。
常见坑:VCS 时序仿真的参数冲突
用 VCS 做 SDF 时序仿真时,需要把
+notimingcheck、+nospecify等屏蔽时序检查的参数去掉——官方示例 Makefile 里恰恰带着+nospecify,那是为了功能仿真提速;做时序仿真前务必删掉,否则 SDF 反标形同虚设。另外一个已知限制:PH2 仿真模型没有 SDF 相关的 specify 描述,暂不支持时序仿真(EG 系列不受影响)。
通关标准:
① 能说出四个仿真阶段分别验证什么、对应 Flow 的哪个节点;② 能独立完成”设置仿真器 → 编译 EG 器件库 → 写 testbench 激励 → 一键跑 Post Route 时序仿真”的完整闭环;③ 遇到时序仿真失败时,知道从 SDF、时序报告两条线索定位问题。
自测:Behavioral Simulation 和 Post Gate Simulation 仿真的是同一份代码吗?
不是。Behavioral 仿真的是你写的 RTL 源码(综合前);Post Gate 仿真的是综合后转换为安路器件库底层物理电路的网表。前者查逻辑错误,后者验证”TD 翻译成真实电路后功能没变”。
自测:EG4S20 的 testbench 里该例化哪个全局复位源语?
用 glbl 源语。PH1_PHY_GSR 只用于 PH1 系列,PH2_PHY_GSR 只用于 PH2 系列;EF2、EF3、EF4、EG、SF1 器件库都用 glbl。而且要在释放全局复位(GSR)之后再启动时钟源。
自测:什么时候需要重新编译仿真库?
只要不换工具版本,编译一次即可反复使用;但更换了 TD 工具版本或仿真工具版本之后,就必须重新编译器件库,因为库与工具版本是绑定的。
自测:Post Route Simulation 阶段怎么从功能仿真切到时序仿真?
在 Post Route Simulation 窗口里把 Function Simulation 切换为 Timing Simulation。点 OK 后 TD 自动生成 SDF 延时文件并在仿真时反标;这是唯一提供该选项的仿真阶段。
自测:为什么建议每个阶段都仿真,而不是只跑最终的时序仿真?
行为级仿真最快、报错最贴近源码,能在设计早期廉价地修 bug;每一层”翻译”(RTL 优化、门级映射、布局布线)都可能引入新问题,逐层仿真能把问题锁定在刚引入它的那一层,定位成本最低。