SKILL.md 把 Vivado 安装路径写死成 C:\Xilinx\Vivado\2018.1,但各机器盘符不同 (HP_BOWEI 上实际是 D:\),照抄命令会直接失败。改为从 ~\skills_env.md 的 VIVADO_ROOT 取值,命令块统一用 $VIVADO_BIN / $VIVADO_DATA 变量。
17 KiB
name, description
| name | description |
|---|---|
| vivado-sim | 为 Verilog/VHDL 模块自动生成 testbench 并在 Vivado 中运行仿真,将波形导出为 CSV 文件。当用户想对某个 RTL 模块进行仿真、验证信号波形、自动生成激励、查看仿真结果时,务必使用此技能。触发关键词包括:仿真、testbench、波形、xsim、vivado仿真、激励生成、sim、simulation、验证模块行为。即使用户没有明确说"用我这个技能",只要涉及 Vivado 仿真流程,都应触发此技能。 |
Vivado 模块仿真技能
本技能帮助你为任意 Verilog/VHDL 模块自动生成 testbench,调用 Vivado 自带的 xsim 仿真器运行仿真,并将波形数据导出为 CSV 文件。
整体工作流程
1. 获取模块接口信息(读取文件 或 用户描述)
2. 分析端口列表,理解模块功能
3. 生成 testbench(自动激励 或 用户提供激励)
4. 创建 Vivado 仿真工程(生成 .prj 文件)
5. 直接调用 xvlog/xvhdl/xelab/xsim 运行仿真(绕过 launch_simulation)
6. 导出波形 CSV,向用户报告结果
环境配置
Vivado 的安装路径因机器而异(盘符经常不同),不要写死。开工前先读
~\skills_env.md 的 VIVADO_ROOT 条目,把它转成 Git Bash 形式后代入下面所有命令:
# 例:skills_env.md 里 VIVADO_ROOT = D:\Xilinx\Vivado\2018.1
# → Git Bash 形式 /d/Xilinx/Vivado/2018.1(首字母小写、去冒号、反斜杠转正斜杠)
VIVADO_ROOT="/d/Xilinx/Vivado/2018.1" # ← 按本机 skills_env.md 取值替换
VIVADO_BIN="$VIVADO_ROOT/bin"
VIVADO_DATA="$VIVADO_ROOT/data"
| 配置项 | 值 |
|---|---|
| Vivado 安装路径 | ~\skills_env.md 的 VIVADO_ROOT |
| xsim 工具目录 | $VIVADO_BIN/ |
| Vivado 数据目录 | $VIVADO_DATA |
| 目标器件 | xc7a200tfbg676-2(Artix-7 XC7A200T) |
| Vivado 批处理启动器 | $VIVADO_BIN/unwrapped/win64.o/vvgl.exe |
| Vivado 批处理脚本 | $VIVADO_BIN/vivado.bat |
第一步:获取模块接口
两种方式,按用户提供的信息选择:
方式 A:用户提供文件路径
读取 .v 或 .vhd 文件,解析模块端口声明。关注:
- 模块名(
module <name>) - 端口方向(
input/output/inout) - 位宽(
[N:0]) - 时钟和复位信号(通常命名含
clk、rst、rst_n)
方式 B:用户口头描述接口
按以下格式向用户确认端口列表:
模块名: <module_name>
端口:
input clk -- 时钟
input rst_n -- 低有效复位
input [7:0] din -- 数据输入
output [7:0] dout -- 数据输出
output valid -- 输出有效
如果用户描述不完整,主动追问:时钟频率是多少?复位是高有效还是低有效?
Vivado IP 核的特殊处理
若被测模块是 Vivado 生成的 IP 核(而非普通 .v 文件),需要找到其仿真模型文件:
仿真模型路径规律:
<project>.ip_user_files/ip/<ip_name>/sim/<ip_name>.vhd # VHDL 行为模型(通常是这个)
<project>.ip_user_files/ip/<ip_name>/sim/<ip_name>.v # Verilog 行为模型(较少)
MIF 系数文件(FIR 等需要):
<project>.ip_user_files/mem_init_files/<ip_name>.mif
Vivado 在创建工程时会自动将 MIF 复制到 xsim 运行目录,无需手动处理。
查找 IP 端口: 直接读取 .vhd 文件中的 ENTITY ... PORT(...) 块,或读取 .veo(Verilog 例化模板)文件。
第二步:生成 Testbench
根据接口信息生成 tb_<module_name>.v。遵循以下原则:
Testbench 模板结构
`timescale 1ns/1ps
module tb_<module_name>;
// ---- 参数 ----
parameter CLK_PERIOD = <cycle>; // 根据时钟频率计算,单位 ns
// ---- 信号声明 ----
// input → reg,output → wire
reg clk;
reg rst_n;
// ... 其他端口
// ---- 被测模块例化 ----
<module_name> uut (
.clk (clk),
.rst_n (rst_n),
// ...
);
// ---- 时钟生成 ----
initial clk = 1'b0;
always #(CLK_PERIOD/2.0) clk = ~clk;
// ---- 复位与激励 ----
initial begin
rst_n = 0;
repeat(5) @(posedge clk);
rst_n = 1;
// === 激励序列 ===
repeat(20) @(posedge clk);
$finish;
end
// ---- VCD 波形记录(用于后续导出 CSV)----
initial begin
$dumpfile("tb_<module_name>.vcd");
$dumpvars(0, tb_<module_name>);
end
endmodule
激励生成策略
自动生成激励(无用户指定):
- 对每个数据输入端口,按时钟节拍施加递增/随机变化的值
- 对握手信号(valid/ready),模拟正常传输时序
- 仿真时长默认 100 个时钟周期,若模块有明显的处理延时,适当延长
从 ILA 捕获数据作为激励(用户提供 CSV 文件):
-
将 ILA CSV 中的十六进制数据转换为
$readmemh格式的.mem文件:# ILA CSV 格式:Sample in Buffer, Sample in Window, TRIGGER, signal_i[15:0], signal_q[15:0] awk -F',' 'NR>1 {gsub(/ /,"",$4); gsub(/ /,"",$5); print $4 $5}' input.csv > stimulus_iq.mem -
Testbench 中读取并回放:
parameter integer NUM_SAMPLES = 4096; reg [31:0] stim_mem [0:NUM_SAMPLES-1]; integer sample_idx; initial begin $readmemh("stimulus_iq.mem", stim_mem); // 文件必须在 xsim 运行目录下 s_axis_data_tvalid = 1'b0; repeat (20) @(posedge clk); s_axis_data_tvalid = 1'b1; for (sample_idx = 0; sample_idx < NUM_SAMPLES; sample_idx = sample_idx + 1) begin s_axis_data_tdata <= stim_mem[sample_idx]; @(posedge clk); while (!s_axis_data_tready) @(posedge clk); end s_axis_data_tvalid = 1'b0; repeat (512) @(posedge clk); // 等待流水线排空 $finish; end
重要:$readmemh 和 $fopen 的文件路径
xsim 以其运行目录(sim_proj/<proj>.sim/sim_1/behav/xsim/)为工作目录,所有相对路径均相对于此。
务必在运行仿真前将激励文件复制到该目录:
cp stimulus_iq.mem "$SIM_DIR/"
输出文件($fopen 写入)也会出现在该目录,仿真结束后再复制回输出目录。
第三步:创建仿真工程并运行
⚠️ Git Bash 下的 "Broken Pipe" 问题
现象: 在 Git Bash 中通过 vivado -mode batch 调用 launch_simulation,会出现:
ERROR: [Common 17-180] Spawn failed: Broken pipe
ERROR: [USF-XSim-62] 'compile' step failed
原因: Vivado 的 launch_simulation 在 Windows Git Bash 环境下尝试用 cmd.exe 启动 compile.bat,子进程 spawn 机制失败。
解决方案:直接调用 xsim 工具链(两阶段工作流)
阶段一:用 Vivado batch 创建工程(首次,生成 .prj 和 .ini 文件)
作用: 让 Vivado 生成工程结构,输出 tb_xxx_vlog.prj、tb_xxx_vhdl.prj、xsim.ini,以及自动导出 MIF 等初始化文件。即使 launch_simulation 报 Broken Pipe 错误,这些文件也已生成。
run_sim.tcl 模板:
# run_sim.tcl - 用于创建仿真工程结构(生成 .prj 文件)
# 注意:launch_simulation 在 Git Bash 下可能报 Broken Pipe,属正常,.prj 已生成
set script_dir [file dirname [file normalize [info script]]]
set proj_dir [file normalize "$script_dir/.."]
create_project -force sim_<module> "$script_dir/sim_proj" -part xc7a200tfbg676-2
set_property simulator_language Mixed [current_project]
# 添加 IP 仿真模型(VHDL)
set ip_vhd "$proj_dir/my_exciter.ip_user_files/ip/<ip_name>/sim/<ip_name>.vhd"
add_files -norecurse $ip_vhd
set_property file_type {VHDL} [get_files [file tail $ip_vhd]]
# 添加 testbench(Verilog)
add_files -fileset sim_1 -norecurse "$script_dir/tb_<module_name>.v"
# MIF 系数文件(FIR IP 等需要)
set mif "$proj_dir/my_exciter.ip_user_files/mem_init_files/<ip_name>.mif"
if {[file exists $mif]} { add_files -norecurse $mif }
# 设置顶层
set_property top tb_<module_name> [get_filesets sim_1]
set_property top_lib xil_defaultlib [get_filesets sim_1]
# 启动仿真(Git Bash 下报 Broken Pipe 是正常的,.prj 文件已生成)
set_property -name {xsim.simulate.runtime} -value {30000ns} -objects [get_filesets sim_1]
launch_simulation
运行命令(首次):
"$VIVADO_BIN/unwrapped/win64.o/vvgl.exe" \
"$VIVADO_BIN/vivado.bat" \
-mode batch \
-source run_sim.tcl \
-log sim.log \
-journal sim.jou || true # 忽略 Broken Pipe 导致的非零退出码
运行后,xsim 工程目录为:
sim_proj/<proj>.sim/sim_1/behav/xsim/
├── tb_<module_name>_vlog.prj ← Verilog 编译文件列表
├── tb_<module_name>_vhdl.prj ← VHDL 编译文件列表
├── xsim.ini ← 用户库路径(仅包含 xil_defaultlib)
├── glbl.v ← Xilinx 全局模块
└── <ip_name>.mif ← 系数文件(自动导出)
阶段二:直接调用 xsim 工具链
完整流程脚本(run_all.sh):
#!/usr/bin/env bash
set -e
SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
SIM_DIR="$SCRIPT_DIR/sim_proj/sim_<proj>.sim/sim_1/behav/xsim"
# Vivado 路径按本机 ~\skills_env.md 的 VIVADO_ROOT 取值(盘符因机器而异)
VIVADO_ROOT="/d/Xilinx/Vivado/2018.1"
VIVADO_BIN="$VIVADO_ROOT/bin"
VIVADO_DATA="$VIVADO_ROOT/data"
export RDI_DATADIR="$VIVADO_DATA"
# ---- Step 1: 构建合并的 xsim.ini ----
# xsim 需要找到 Vivado 内置 IP 库(如 fir_compiler_v7_2_11),
# 但 Vivado 生成的本地 xsim.ini 只含用户库,需要合并系统库。
{
echo "xil_defaultlib=xsim.dir/xil_defaultlib"
cat "$VIVADO_DATA/xsim/xsim.ini"
cat "$VIVADO_DATA/xsim/ip/xsim_ip.ini"
} > "$SIM_DIR/xsim_merged.ini"
# ---- Step 2: 复制激励文件到 xsim 运行目录 ----
cp "$SCRIPT_DIR/stimulus_iq.mem" "$SIM_DIR/"
cd "$SIM_DIR"
# ---- Step 3: 编译 Verilog testbench ----
"$VIVADO_BIN/xvlog" --incr --relax \
--initfile xsim_merged.ini \
-prj tb_<module_name>_vlog.prj \
-log xvlog.log
# ---- Step 4: 编译 VHDL IP 仿真模型 ----
# 如果模块是纯 Verilog,跳过此步
"$VIVADO_BIN/xvhdl" --incr --relax \
--initfile xsim_merged.ini \
-prj tb_<module_name>_vhdl.prj \
-log xvhdl.log
# ---- Step 5: Elaboration(综合展开)----
"$VIVADO_BIN/xelab" --relax --debug typical --mt auto \
-L xil_defaultlib \
--initfile xsim_merged.ini \
--snapshot tb_<module_name>_behav \
xil_defaultlib.tb_<module_name> xil_defaultlib.glbl \
-log elaborate.log
# ---- Step 6: 运行仿真 ----
# 注意:xsim 不支持 --initfile 参数,通过 TCL batch 脚本控制
cat > run.tcl << 'EOF'
run all
quit
EOF
"$VIVADO_BIN/xsim" tb_<module_name>_behav \
--tclbatch run.tcl \
--log xsim_run.log
# ---- Step 7: 将输出文件复制回输出目录 ----
[ -f "$SIM_DIR/output_iq.txt" ] && cp "$SIM_DIR/output_iq.txt" "$SCRIPT_DIR/"
echo "仿真完成。输出文件在 $SCRIPT_DIR/"
方式 B:交互 GUI 模式(用户需要在 Vivado 中查看波形)
"$VIVADO_BIN/unwrapped/win64.o/vvgl.exe" \
"$VIVADO_BIN/vivado.bat" \
-source run_sim.tcl
# 不加 -mode batch,Vivado GUI 会打开
在 run_sim.tcl 中去掉 exit,改为:
launch_simulation
run all
# 不调用 close_sim,让 Vivado 保持打开以供查看波形
第四步:导出波形为 CSV
方式 A:testbench 直接写 CSV(推荐,针对关键信号)
在 testbench 中用 $fopen 直接写目标格式,避免解析 VCD:
integer fd_out;
initial begin
fd_out = $fopen("output_iq.txt", "w");
$fwrite(fd_out, "time_ns,m_i_24b_hex,m_q_24b_hex,m_i_dec,m_q_dec\n");
end
// 有符号扩展辅助函数
function signed [31:0] sign24;
input [23:0] v;
sign24 = $signed({v[23], v});
endfunction
always @(posedge aclk) begin
if (m_axis_data_tvalid) begin
$fwrite(fd_out, "%0d,%06h,%06h,%0d,%0d\n",
$time,
m_axis_data_tdata[47:24],
m_axis_data_tdata[23:0],
sign24(m_axis_data_tdata[47:24]),
sign24(m_axis_data_tdata[23:0]));
end
end
方式 B:VCD 转 CSV(全信号波形)
将以下脚本保存为 vcd_to_csv.py 并运行:
#!/usr/bin/env python
"""将 VCD 波形文件转换为 CSV 格式"""
import re, csv, sys
def parse_vcd(path):
signals, changes, t = {}, [], 0
with open(path, 'r') as f:
for line in f:
line = line.strip()
m = re.match(r'\$var\s+\w+\s+(\d+)\s+(\S+)\s+(\S+)', line)
if m:
signals[m.group(2)] = (m.group(3), int(m.group(1))); continue
m = re.match(r'#(\d+)', line)
if m:
t = int(m.group(1)); continue
m = re.match(r'([01xzXZ])(\S+)$', line)
if m and m.group(2) in signals:
changes.append((t, m.group(2), m.group(1))); continue
m = re.match(r'[bB]([01xzXZ_]+)\s+(\S+)$', line)
if m and m.group(2) in signals:
changes.append((t, m.group(2), m.group(1).replace('_','')))
return signals, changes
def vcd_to_csv(vcd_path, csv_path):
signals, changes = parse_vcd(vcd_path)
if not signals: print("未找到信号"); return
time_events = {}
for t, ic, v in changes:
time_events.setdefault(t, {})[ic] = v
ids = list(signals.keys())
names = [signals[i][0] for i in ids]
cur = {i: 'x' for i in ids}
rows = []
for t in sorted(time_events):
cur.update(time_events[t])
row = {'time_ns': t}
for i, n in zip(ids, names): row[n] = cur[i]
rows.append(row)
if not rows: print("没有波形数据"); return
with open(csv_path, 'w', newline='') as f:
w = csv.DictWriter(f, fieldnames=['time_ns'] + names)
w.writeheader(); w.writerows(rows)
print(f"已导出 {len(rows)} 个时间点,{len(names)} 个信号 → {csv_path}")
if __name__ == '__main__':
if len(sys.argv) < 3: print("用法: python vcd_to_csv.py <input.vcd> <output.csv>"); sys.exit(1)
vcd_to_csv(sys.argv[1], sys.argv[2])
VCD 文件位置:sim_proj/<proj>.sim/sim_1/behav/xsim/tb_<module_name>.vcd
第五步:向用户报告结果
仿真完成后,汇报以下信息:
- 仿真是否成功:检查
xsim_run.log是否有 Error/Fatal,看$finish是否被调用 - 输出文件路径:告知用户 CSV 文件保存位置
- 波形摘要:读取 CSV 前几行,展示关键信号的变化情况
- 如有仿真报错:摘录 log 中的错误信息,帮助用户定位问题
常见问题处理
问题:Broken Pipe(launch_simulation 失败)
→ 正常现象(Git Bash 环境下),.prj 文件已生成。直接进入阶段二,用 xvlog/xvhdl/xelab/xsim 手动运行。
问题:fir_compiler_v7_2_11 is not compiled(VHDL 编译失败)
→ 本地 xsim.ini 缺少 Vivado 内置 IP 库路径。需构建 xsim_merged.ini(合并 $VIVADO_DATA/xsim/ip/xsim_ip.ini),并在 xvhdl 命令加 --initfile xsim_merged.ini。同时设置 RDI_DATADIR。
问题:Cannot find design unit xil_defaultlib.tb_xxx(xelab 找不到模块)
→ xvlog 必须在 xsim 运行目录(sim_proj/.../.../xsim/)下执行,编译结果写入该目录下的 xsim.dir/。
问题:xsim: Unexpected option --initfile
→ xsim 不支持 --initfile,只能通过 TCL batch(--tclbatch run.tcl)控制运行行为。
问题:$readmemh 找不到文件
→ xsim 以 sim_proj/.../.../xsim/ 为工作目录,激励文件必须提前复制到该目录。
问题:output_iq.txt 没有数据($fopen 写入为空)
→ 检查 m_axis_data_tvalid 是否有效拉高;FIR 插值核有几十到几百个周期的流水线延迟,仿真时长要足够长(输入周期数 + 流水线延迟 + 裕量)。
问题:VCD 文件为空
→ 确认 testbench 包含 $dumpfile 和 $dumpvars,且在 $finish 之前有时序事件。
问题:端口连接错误 → 重新核对模块声明与 testbench 例化的端口名/位宽是否一致。
文件组织建议
<输出目录>/(例如 sim-dbg/)
├── tb_<module_name>.v # 生成的 testbench
├── stimulus_iq.mem # 激励数据($readmemh 格式)
├── run_sim.tcl # Vivado 工程创建脚本(首次运行)
├── run_all.sh # 完整仿真运行脚本
├── vcd_to_csv.py # 波形导出脚本
├── output_iq.txt # 仿真输出 CSV(从 xsim 目录复制)
└── sim_proj/ # Vivado 仿真工程(自动生成)
└── <proj>.sim/sim_1/behav/xsim/
├── tb_xxx_vlog.prj # Verilog 编译清单
├── tb_xxx_vhdl.prj # VHDL 编译清单
├── xsim_merged.ini # 合并后的库路径配置
├── stimulus_iq.mem # 激励文件副本(xsim 工作目录)
├── output_iq.txt # 仿真原始输出
├── tb_xxx.vcd # 波形文件
└── xsim_run.log # 仿真运行日志