Files
skills-win/vivado-sim/SKILL.md
T
2026-05-09 01:12:34 -05:00

491 lines
16 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
---
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 安装路径(Windows) | `C:\Xilinx\Vivado\2018.1` |
| Vivado 安装路径(Git Bash) | `/c/Xilinx/Vivado/2018.1` |
| xsim 工具目录 | `/c/Xilinx/Vivado/2018.1/bin/` |
| Vivado 数据目录 | `/c/Xilinx/Vivado/2018.1/data` |
| 目标器件 | `xc7a200tfbg676-2`(Artix-7 XC7A200T) |
| Vivado 批处理启动器 | `/c/Xilinx/Vivado/2018.1/bin/unwrapped/win64.o/vvgl.exe` |
| Vivado 批处理脚本 | `/c/Xilinx/Vivado/2018.1/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 模板结构
```verilog
`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 文件):**
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/<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_<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
```
**运行命令(首次):**
```bash
/c/Xilinx/Vivado/2018.1/bin/unwrapped/win64.o/vvgl.exe \
/c/Xilinx/Vivado/2018.1/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`):
```bash
#!/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_BIN="/c/Xilinx/Vivado/2018.1/bin"
VIVADO_DATA="/c/Xilinx/Vivado/2018.1/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 中查看波形)
```bash
/c/Xilinx/Vivado/2018.1/bin/unwrapped/win64.o/vvgl.exe \
/c/Xilinx/Vivado/2018.1/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 <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`
---
## 第五步:向用户报告结果
仿真完成后,汇报以下信息:
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_<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 # 仿真运行日志
```