--- name: vivado-sim description: 为 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 形式后代入下面所有命令: ```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 `) - 端口方向(`input`/`output`/`inout`) - 位宽(`[N:0]`) - 时钟和复位信号(通常命名含 `clk`、`rst`、`rst_n`) ### 方式 B:用户口头描述接口 按以下格式向用户确认端口列表: ``` 模块名: 端口: input clk -- 时钟 input rst_n -- 低有效复位 input [7:0] din -- 数据输入 output [7:0] dout -- 数据输出 output valid -- 输出有效 ``` 如果用户描述不完整,主动追问:时钟频率是多少?复位是高有效还是低有效? ### Vivado IP 核的特殊处理 若被测模块是 Vivado 生成的 IP 核(而非普通 .v 文件),需要找到其仿真模型文件: **仿真模型路径规律:** ``` .ip_user_files/ip//sim/.vhd # VHDL 行为模型(通常是这个) .ip_user_files/ip//sim/.v # Verilog 行为模型(较少) ``` **MIF 系数文件(FIR 等需要):** ``` .ip_user_files/mem_init_files/.mif ``` Vivado 在创建工程时会自动将 MIF 复制到 xsim 运行目录,无需手动处理。 **查找 IP 端口:** 直接读取 `.vhd` 文件中的 `ENTITY ... PORT(...)` 块,或读取 `.veo`(Verilog 例化模板)文件。 --- ## 第二步:生成 Testbench 根据接口信息生成 `tb_.v`。遵循以下原则: ### Testbench 模板结构 ```verilog `timescale 1ns/1ps module tb_; // ---- 参数 ---- parameter CLK_PERIOD = ; // 根据时钟频率计算,单位 ns // ---- 信号声明 ---- // input → reg,output → wire reg clk; reg rst_n; // ... 其他端口 // ---- 被测模块例化 ---- 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_.vcd"); $dumpvars(0, tb_); end endmodule ``` ### 激励生成策略 **自动生成激励(无用户指定):** - 对每个数据输入端口,按时钟节拍施加递增/随机变化的值 - 对握手信号(valid/ready),模拟正常传输时序 - 仿真时长默认 100 个时钟周期,若模块有明显的处理延时,适当延长 **从 ILA 捕获数据作为激励(用户提供 CSV 文件):** 1. 将 ILA CSV 中的十六进制数据转换为 `$readmemh` 格式的 `.mem` 文件: ```bash # 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 ``` 2. Testbench 中读取并回放: ```verilog 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/.sim/sim_1/behav/xsim/`)为工作目录,所有相对路径均相对于此。 务必在运行仿真前将激励文件复制到该目录: ```bash 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 模板:** ```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_ "$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//sim/.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_.v" # MIF 系数文件(FIR IP 等需要) set mif "$proj_dir/my_exciter.ip_user_files/mem_init_files/.mif" if {[file exists $mif]} { add_files -norecurse $mif } # 设置顶层 set_property top tb_ [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 ``` **运行命令(首次):** ```bash "$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/.sim/sim_1/behav/xsim/ ├── tb__vlog.prj ← Verilog 编译文件列表 ├── tb__vhdl.prj ← VHDL 编译文件列表 ├── xsim.ini ← 用户库路径(仅包含 xil_defaultlib) ├── glbl.v ← Xilinx 全局模块 └── .mif ← 系数文件(自动导出) ``` --- ### 阶段二:直接调用 xsim 工具链 完整流程脚本(`run_all.sh`): ```bash #!/usr/bin/env bash set -e SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)" SIM_DIR="$SCRIPT_DIR/sim_proj/sim_.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__vlog.prj \ -log xvlog.log # ---- Step 4: 编译 VHDL IP 仿真模型 ---- # 如果模块是纯 Verilog,跳过此步 "$VIVADO_BIN/xvhdl" --incr --relax \ --initfile xsim_merged.ini \ -prj tb__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__behav \ xil_defaultlib.tb_ xil_defaultlib.glbl \ -log elaborate.log # ---- Step 6: 运行仿真 ---- # 注意:xsim 不支持 --initfile 参数,通过 TCL batch 脚本控制 cat > run.tcl << 'EOF' run all quit EOF "$VIVADO_BIN/xsim" tb__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 中查看波形) ```bash "$VIVADO_BIN/unwrapped/win64.o/vvgl.exe" \ "$VIVADO_BIN/vivado.bat" \ -source run_sim.tcl # 不加 -mode batch,Vivado GUI 会打开 ``` 在 run_sim.tcl 中去掉 `exit`,改为: ```tcl launch_simulation run all # 不调用 close_sim,让 Vivado 保持打开以供查看波形 ``` --- ## 第四步:导出波形为 CSV ### 方式 A:testbench 直接写 CSV(推荐,针对关键信号) 在 testbench 中用 `$fopen` 直接写目标格式,避免解析 VCD: ```verilog 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` 并运行: ```python #!/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 "); sys.exit(1) vcd_to_csv(sys.argv[1], sys.argv[2]) ``` VCD 文件位置:`sim_proj/.sim/sim_1/behav/xsim/tb_.vcd` --- ## 第五步:向用户报告结果 仿真完成后,汇报以下信息: 1. **仿真是否成功**:检查 `xsim_run.log` 是否有 Error/Fatal,看 `$finish` 是否被调用 2. **输出文件路径**:告知用户 CSV 文件保存位置 3. **波形摘要**:读取 CSV 前几行,展示关键信号的变化情况 4. **如有仿真报错**:摘录 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_.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 仿真工程(自动生成) └── .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 # 仿真运行日志 ```