基于脚本的ModelSim自动化仿真(Xilinx FPGA篇)
-
- 一、为什么要用脚本仿真?
-
- [1.1 传统GUI仿真的痛点](#1.1 传统GUI仿真的痛点)
- [1.2 脚本仿真的核心价值](#1.2 脚本仿真的核心价值)
- [1.3 Xilinx FPGA的特别需求](#1.3 Xilinx FPGA的特别需求)
- 二、准备工作------仿真环境搭建
-
- [2.1 软件环境要求](#2.1 软件环境要求)
- [2.2 编译Xilinx仿真库](#2.2 编译Xilinx仿真库)
- [2.4 验证库配置成功](#2.4 验证库配置成功)
- 三、脚本编写实战
-
- [3.1 项目目录结构](#3.1 项目目录结构)
- [3.2 编写待测模块](#3.2 编写待测模块)
- [3.3 编写Testbench](#3.3 编写Testbench)
- [3.4 编写仿真脚本](#3.4 编写仿真脚本)
- [3.5 编写批处理命令](#3.5 编写批处理命令)
- [3.6 编写波形文件](#3.6 编写波形文件)
- [3.7 执行仿真](#3.7 执行仿真)
- [3.8 修改代码后重新仿真](#3.8 修改代码后重新仿真)
- 四、高级脚本技巧
-
- [4.1 do文件实现 ``define`](#4.1 do文件实现 ``define`)
- [4.2 设置仿真时间精度](#4.2 设置仿真时间精度)
- [4.3 状态机添加到波形](#4.3 状态机添加到波形)
- [4.4 设置显示格式(Radix)](#4.4 设置显示格式(Radix))
- [4.5 波形颜色设置](#4.5 波形颜色设置)
- [4.6 波形分组设置](#4.6 波形分组设置)
一、为什么要用脚本仿真?
1.1 传统GUI仿真的痛点
在FPGA开发中,仿真验证是不可或缺的环节。传统的ModelSim图形界面操作流程通常是:启动软件 → 创建工程 → 添加文件 → 编译 → 加载仿真 → 添加波形 → 运行。每次修改代码后,这套流程都要重复一遍,存在三个明显问题:
- 重复操作耗时:每次修改代码都要重新点击一遍流程
- 容易遗漏步骤:尤其是Xilinx仿真库的设置,经常被忽略导致报错
- 无法版本控制和团队复用:GUI操作无法被Git追踪,换个电脑就要重新配置
1.2 脚本仿真的核心价值
使用Tcl脚本进行自动化仿真,可以解决上述所有问题:
- 一键式自动化 :一个
do命令完成全部流程 - 可复现性强:脚本保存为文件,团队成员可直接使用
- 便于回归测试:修改代码后快速重新仿真,适合频繁迭代
掌握脚本仿真方法后,每次仿真只需双击一个.bat文件,可以把更多精力放在代码设计和功能验证上。
1.3 Xilinx FPGA的特别需求
Xilinx FPGA开发中经常使用IP核(PLL、RAM、FIFO等),这些IP核的仿真需要专用的仿真库(unisim、simprim、xilinxcorelib等)。手动配置这些库比较繁琐,而脚本方式可以统一管理库的映射,一次配置永久使用。本文聚焦Xilinx + ModelSim这一组合。
二、准备工作------仿真环境搭建
2.1 软件环境要求
- ModelSim SE 2020.4,Modelsim 安装破解步骤如下:
- 复制MentorKG.exe,mgls64.dll以及patch64_dll.bat这三个文件到Modelsim安装目录的win64文件夹下,如E:\modeltech64_2020.4\win64
- 双击win64文件夹内粘贴过去的patch64_dll.bat文件,会生成LICENSE.TXT,另存到安装目录
- 设置系统环境变量,变量值为LICENSE.TXT的路径 变量名:LM_LICENSE_FILE 变量值:E:\modeltech64_2020.4\LICENSE.TXT
- 完成。
- Xilinx Vivado 2021.1官网下载链接
2.2 编译Xilinx仿真库
这是使用Xilinx IP核仿真的前置条件,编译一次即可重复使用。
-
生成库文件
打开Vivado,点击 Tools → Compile Simulation Libraries:
- Simulation simulator:选择 ModelSim
- Library language:选择 Verilog 或 VHDL(根据项目)
- Compilation:选择器件系列(选择自己需要,选择太多后续加载库慢)
- Output directory:指定库输出目录(路径建议不含空格和中文)
点击Compile,等待编译完成即可。
在Vivado2020.2之后的版本中,需要指定GCC路径,可以指定Vivado自带的GCC路径:D:\Xilinx\Vivado\2020.2\tps\win64\msys64\mingw64\bin

-
从库文件夹里的 modelsim.ini 文件中复制如下内容

-
将 modelsim 根目录下 modelsim.ini 文件属性
只读关闭 -
粘贴刚刚复制的内容到该文件的如下位置,然后保存,勾选
只读
2.4 验证库配置成功
打开ModelSim,在Library窗口中应能看到unisim、simprim、xilinxcorelib等库,说明配置成功。

三、脚本编写实战
3.1 项目目录结构
在开始实践前,先建立规范的目录结构:
project/
├── sim/ # 仿真脚本
├── src/
│ ├── ip/ # IP核文件
│ ├── rtl/ # RTL源文件
│ ├── tb/ # Testbench文件
3.2 编写待测模块
以计数器模块为例,在src/rtl/目录下创建counter.v:
verilog
// 计数器模块
module counter (
input clk,
input reset,
output [7:0] cnt_out
);
//------------------------------------
// Local Signal
//------------------------------------
reg [7:0] cnt = 0;
//------------------------------------
// User Logic
//------------------------------------
always @ (posedge clk)
if (reset)
cnt <= 8'd0;
else
cnt <= cnt + 8'b1;
//------------------------------------
// Output Port
//------------------------------------
assign cnt_out = cnt;
endmodule
3.3 编写Testbench
在src/tb/目录下创建tb_counter.v:
verilog
`timescale 1ns/1ns
module tb_counter;
//*************************** Parameters ***************************
parameter PERIOD_CLK = 10;
//*************************** Signals ***************************
reg clk = 0;
reg reset = 1;
wire [7:0] cnt;
//*************************** Test Logic ***************************
always # (PERIOD_CLK/2) clk = ~clk;
initial
begin
#100
reset = 0;
#1000;
$stop;
end
//*************************** Task ***************************
//*************************** Instance ***************************
// 计数器模块
counter u_counter(
.clk (clk),
.reset (reset),
.cnt_out (cnt)
);
endmodule
3.4 编写仿真脚本
在sim/目录下创建sim.do:
tcl
# 1. 清除之前的工作
quit -sim
.main clear
# 2. 设置库路径
set VIVADO_DIR "D:/software/Xilinx/Vivado/2021.1/data"
set VIVADO_LIB_DIR "D:/software/modeltech64_2020.4/vivado_lib_2021_1"
set IP_DIR "../src/ip"
set RTL_DIR "../src/rtl"
set TB_DIR "../src/tb"
# 3. 创建库
# unisim 库 提供Xilinx FPGA所有基本硬件原语(LUT/FDCE/RAMB36E1/BUFG/MMCM/IO原语等)的行为级和结构级仿真模型
vlib unisim
# unimacro库 提供复杂功能模块(如FIFO/移位寄存器/DSP宏)的仿真模型
vlib unimacro
# secureip 库 提供Xilinx加密IP核(如PCIe/GTX收发器)的仿真模型
vlib secureip
# xpm 库 提供Xilinx CDC/XPM_MEMORY/XPM_FIFO参数宏的仿真模型
vlib xpm_lib
# xil_defaultlib 库 存放Vivado自动生成的IP核仿真模型(如FIFO/DDR控制器等)
vlib xil_defaultlib
# work 库 为用户自定义库
vlib work
# 4. 映射库
vmap unisim "$VIVADO_LIB_DIR/unisim"
vmap unimacro "$VIVADO_LIB_DIR/unimacro"
vmap secureip "$VIVADO_LIB_DIR/secureip"
vmap xpm_lib "$VIVADO_LIB_DIR/xpm"
vmap xil_defaultlib ./xil_defaultlib
vmap work ./work
# 5. 编译库文件
# vcom -2008: 编译VHDL(支持2008标准)
# vlog : 编译 Verilog/SystemVerilog
# 编译 glbl 模块
vlog -work xil_defaultlib "$VIVADO_DIR/verilog/src/glbl.v"
# 编译 xpm 模块
# vlog -work xpm_lib "$VIVADO_DIR/ip/xpm/xpm_fifo/hdl/xpm_fifo.sv"
# 编译 Xilinx IP
# vlog -work xil_defaultlib "$IP_DIR/blk_mem_gen_0/blk_mem_gen_0_sim_netlist.v"
# 编译设计文件
vlog -work work +define+SIM "$RTL_DIR/counter.v"
vlog -work work "$TB_DIR/*.v"
# 6. 启动仿真
vsim -t 1ns -voptargs="+acc" -L unisim -L unimacro -L secureip -L xpm_lib -L xil_defaultlib -L work xil_defaultlib.glbl work.tb_counter
# 7. 添加波形
do wave_counter.do
# 8. 运行仿真
run -all
wave zoom full
3.5 编写批处理命令
这是本节的重点------无需手动打开ModelSim,通过Windows批处理命令一键启动仿真。
在sim/目录下创建run_sim.bat:
bash
vsim -do sim.do
3.6 编写波形文件
在sim/目录下创建wave_counter.do:
TCL
add wave -noupdate /tb_counter/u_counter/clk
add wave -noupdate /tb_counter/u_counter/reset
add wave -noupdate /tb_counter/u_counter/cnt_out
3.7 执行仿真
双击run_sim.bat执行。
3.8 修改代码后重新仿真
修改代码后不需要关闭软件,只需要在modelsim的 Transcript窗口输入do sim.do`,然后回车即可,软件会自动执行仿真进程。

四、高级脚本技巧
4.1 do文件实现 ``define`
在编译文件时增加 +define+SIM,相当于在.v文件增加``define SIM`的功能。
tcl
vlog -work work +define+SIM "$RTL_DIR/counter.v"
4.2 设置仿真时间精度
增加-t 1ns设置仿真时间精度为1ns。
tcl
vsim -t 1ns -voptargs="+acc" -L unisim -L unimacro -L secureip -L xpm_lib -L xil_defaultlib -L work xil_defaultlib.glbl work.tb_counter
4.3 状态机添加到波形
tcl
# 定义虚拟类型(将二进制值映射为状态名)
virtual type {
{IDLE 2'b00}
{READ 2'b01}
{WRITE 2'b10}
{DONE 2'b11}
} state_type
# 将实际信号转换为虚拟类型
virtual function {(state_type)/tb_cmos_init/u_cmos_init/cmos_init_state} cmos_init_state_named
# 添加到波形(显示为状态名)
add wave -noupdate /tb_cmos_init/u_cmos_init/cmos_init_state_named
4.4 设置显示格式(Radix)
| 格式 | 命令参数 | 说明 |
|---|---|---|
| 二进制 | -radix binary |
显示为0/1 |
| 十六进制 | -radix hex |
显示为0-F |
| 十进制 | -radix decimal |
显示为整数 |
| 无符号十进制 | -radix unsigned |
显示为正整数 |
| 八进制 | -radix octal |
显示为0-7 |
| ASCII | -radix ascii |
显示为字符 |
| 符号化 | -radix symbolic |
显示为状态机名称 |
| 默认 | -radix default |
使用原类型默认格式 |
tcl
# 以无符号十进制显示
add wave -radix unsigned /tb_top/uut/data_bus
4.5 波形颜色设置
tcl
# 为不同信号设置颜色
add wave -color "Red" /tb_top/uut/error_flag
# 可用颜色:Red, Green, Blue, Yellow, Cyan, Magenta, White, Black
4.6 波形分组设置
可以在 -group 前增加 -expand 用来展开分组,默认不展开。
tcl
# ===== 添加分隔线 =====
add wave -divider {===== Top Level Signals =====}
# ===== 时钟和复位 =====
add wave -expand -group "Clock_Reset" \
-color "Yellow" \
/tb_top/clk \
/tb_top/rst_n
# ===== 状态机 =====
add wave -group "FSM" \
-radix symbolic \
/tb_top/uut/current_state \
/tb_top/uut/next_state
# ===== 内部计数器(十六进制)=====
add wave -group "Counters" \
-radix hex \
/tb_top/uut/cnt_8bit \
/tb_top/uut/cnt_16bit
# ===== 错误标志 =====
add wave -group "Error_Flags" \
-color "Red" \
/tb_top/uut/parity_err \
/tb_top/uut/timeout_err