Files
Myfro9 8eddd0985a sync: HP_BOWEI 2026-07-27 vivado-sim 去掉写死的 Vivado 盘符路径
SKILL.md 把 Vivado 安装路径写死成 C:\Xilinx\Vivado\2018.1,但各机器盘符不同
(HP_BOWEI 上实际是 D:\),照抄命令会直接失败。改为从 ~\skills_env.md 的
VIVADO_ROOT 取值,命令块统一用 $VIVADO_BIN / $VIVADO_DATA 变量。
2026-07-27 13:56:51 +08:00

17 KiB
Raw Permalink Blame History

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 文件):

  1. 将 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
    
  2. 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


第五步:向用户报告结果

仿真完成后,汇报以下信息:

  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         # 仿真运行日志