Vivado + Questa+ cocotb 联合仿真 Vivado Questasim(或vivado和cocotb都支持的仿真工具) cocotb 联合仿真方法论下面是我和AI讨论 实战总结出来的一套 vivado xxx仿真平台 cocotb 的一套方法论 因为cocotb 比UVM轻量,且能做到一些普通testbench,完成不了的事, 再加上我们绝大多数人,最终要在xilinx平台上跑写出来的RTL,难免用到xilinx私有ip,所以说导出vivado的ip来仿真,才能更符合实际上板情况.三个软件的安装方法,自行探索,注意版本最好一致兼容,我是 vivado2022.2 QuestaSim 2021.2 cocotb2.0.11. 总体流程推荐流程1. 复制一份 Vivado 工程作为仿真工程,如果你要仿真的DUT,几乎就是整个工程,且工程比较干净,可以忽略(1和2).但还是建议复制一份,然后在新工程,reset_project减少占用空间 2. 手动精准删除无关模块 / BD / IP / 板级逻辑 3. 在 Vivado 中设置 DUT 为 top 4. 生成 Xilinx IP 仿真模型 5. 导出 Questa 仿真脚本 6. 执行 compile.do / elaborate.do生成 xxx_opt 7. 确认 Questa 可以独立加载 DUT 8. 再接入 cocotb 9. 先跑 smoke test 10. 参考cocotba官方手册,写driver / monitor / scoreboard / reference model核心分工Vivado负责工程依赖、Xilinx IP、仿真模型、编译顺序 Questa负责 compile / elaborate / simulate cocotb负责 Python testbench Makefile负责把命令固化成一键流程2. 建立仿真专用 Vivado 工程不要直接在原始上板工程里删东西。推荐cp -a 原始工程目录 仿真工程目录然后打开仿真工程在 Vivado GUI 里手动删除无关内容。建议删除板级 top BD / block design PS / MicroBlaze 系统 AXI DMA / Ethernet / PCIe 等无关外设 ILA / VIO 调试核 无关 IP 无关约束 无关 wrapper 无关 testbench保留当前 DUT DUT 依赖的 RTL DUT 依赖的 include / package DUT 依赖的 Xilinx IP 必要的仿真初始化文件禁忌不要在原始工程里直接删 不要用脚本批量删除磁盘源文件 不要以为 set top 后无关文件就自动失效 不要把 BD / PS / DMA / VIP 留在模块级仿真工程里3. Vivado 中设置仿真 top在 Vivado Tcl Console 中执行set top_name 你的DUT顶层模块名 set_property top $top_name [get_filesets sources_1] set_property top $top_name [get_filesets sim_1] update_compile_order -fileset sources_1 update_compile_order -fileset sim_1检查当前 topget_property top [get_filesets sources_1] get_property top [get_filesets sim_1]4. 生成 Xilinx IP 仿真模型只要工程里用了 Xilinx IP例如 FIFO、AXIS width converter、BRAM、XPM、Clock Wizard 等就要生成仿真模型。在 Vivado Tcl Console 中执行generate_target simulation [get_ips] export_ip_user_files -of_objects [get_ips] -no_script -sync -force如果你的仿真工程已经被手动裁剪干净那么[get_ips]应该只包含 DUT 真实需要的 IP。检查 IP 列表get_ips如果这里还能看到明显无关的 IP说明仿真工程还没裁干净。5. 编译 Xilinx 仿真库 simlib(耐心等待,可能需要半小时以上)这一步通常只需要做一次。 除非换 Vivado 版本、换仿真器版本、库损坏否则不用每个工程都重新编。写一个 Tcl 文件例如compile_simlib_questa.tclset simlib_dir [file normalize $::env(HOME)/xilinx_simlib/questa] compile_simlib \ -simulator questa \ -directory $simlib_dir \ -language all \ -family all \ -force用 Vivado batch 执行source /path/to/Vivado/settings64.sh vivado -mode batch -source compile_simlib_questa.tcl完成后一般会得到类似目录~/xilinx_simlib/questa/ ├── modelsim.ini ├── xpm/ ├── unisims_ver/ ├── secureip/ ├── fifo_generator_xxx/ ├── axis_dwidth_converter_xxx/ └── ...以后运行 Questa 前建议指定export MODELSIM$HOME/xilinx_simlib/questa/modelsim.ini检查库映射vmap | grep -E xpm|unisims|secureip|fifo|axis6. 导出 Questa 仿真脚本在 Vivado Tcl Console 中执行set sim_export_dir [file normalize ./sim_export_questa] set simlib_dir [file normalize $::env(HOME)/xilinx_simlib/questa] export_simulation \ -simulator questa \ -directory $sim_export_dir \ -lib_map_path $simlib_dir \ -force注意这一步只导出脚本不会执行脚本。导出后通常会看到sim_export_questa/questa/ ├── compile.do ├── elaborate.do ├── simulate.do ├── 你的top.sh ├── glbl.v ├── wave.do └── README.txt此时还没有你的top_opt。也就是说如果现在直接跑vsim -c \ -pli $(cocotb-config --lib-name-path vpi questa) \ -lib xil_defaultlib 你的top_opt \ -do run -all; quit -f很可能会失败因为你的top_opt还没有由vopt生成。7. 执行 compile / elaborate生成 xxx_opt进入导出的 Questa 目录cd 仿真工程目录/sim_export_questa/questa指定 Xilinx simlibexport MODELSIM$HOME/xilinx_simlib/questa/modelsim.ini执行 Vivado 导出的 shell 脚本通常会有一个类似你的top.sh执行chmod x 你的top.sh ./你的top.sh \ -lib_map_path $HOME/xilinx_simlib/questa \ -reset_run | tee run.log上面的xilinx_simlib/questa 是我们之前编译库的路径这个脚本一般会自动执行compile.do elaborate.do simulate.do成功时日志中会看到类似Optimized design name is 你的top_opt Errors: 0如果看到Optimized design name is 你的top_opt说明xxx_opt已经生成。8. 先不用 cocotb确认 Questa 能独立加载 DUT这一步很重要。 先证明 HDL 仿真器自己能跑再接 cocotb。执行vsim -c \ -lib xil_defaultlib 你的top_opt \ -do run -all; quit -f | tee simulate.log或者执行导出的vsim -c -do do simulate.do; quit -f | tee simulate.log成功标志Loading work.你的top Loading ... Errors: 0这一步只是空仿真 / 加载测试不代表功能正确。 它验证的是RTL 能编译 IP 库能找到 设计能 elaborate 仿真器能 load9. 准备 cocotb 测试目录建议在仿真工程中单独建一个目录cocotb_test/ ├── Makefile ├── test_top.py ├── axis.py ├── reset.py ├── monitor.py ├── scoreboard.py └── ref_model.py一开始可以只有Makefile test_top.py10. 最小 cocotb 测试脚本test_top.pyimport cocotb from cocotb.clock import Clock from cocotb.triggers import RisingEdge, Timer CLK_NAME clk RST_NAME rst_n async def reset_dut(dut): clk getattr(dut, CLK_NAME) rst getattr(dut, RST_NAME) rst.value 0 await Timer(100, unitns) for _ in range(5): await RisingEdge(clk) rst.value 1 for _ in range(10): await RisingEdge(clk) cocotb.test() async def smoke_test(dut): clk getattr(dut, CLK_NAME) cocotb.start_soon(Clock(clk, 10, unitns).start()) dut._log.info(cocotb connected to DUT) await reset_dut(dut) for _ in range(100): await RisingEdge(clk) dut._log.info(smoke test finished)如果你的复位是高电平有效就改成rst.value 1 ... rst.value 0如果时钟或复位名字不同改CLK_NAME 你的top module真实时钟名 RST_NAME 你的top module 真实复位名11. cocotb Makefile下面这个 Makefile 的前提是compile.do / elaborate.do 已经由 Vivado export_simulation 生成 xxx_opt 由 elaborate.do 生成 cocotb 只负责接入已经 elaborated 的设计Makefile# 用户需要修改的部分 SIM_DIR : ../sim_export_questa/questa SIMLIB_INI : $(HOME)/xilinx_simlib/questa/modelsim.ini PYTHON_BIN : $(HOME)/venvs/cocotb/bin/python3 TOPLEVEL : 你的DUT顶层模块名 OPT_TOP : $(TOPLEVEL)_opt TEST_MODULE : test_top # cocotb / Questa 环境 export MODELSIM : $(SIMLIB_INI) export PYGPI_PYTHON_BIN : $(PYTHON_BIN) # cocotb v2 推荐变量 export COCOTB_TEST_MODULES : $(TEST_MODULE) export COCOTB_TOPLEVEL : $(TOPLEVEL) export TOPLEVEL_LANG : verilog export PYTHONPATH : $(CURDIR):$(PYTHONPATH) export COCOTB_REDUCED_LOG_FMT : 1 COCOTB_VPI : $(shell cocotb-config --lib-name-path vpi questa) .PHONY: build run all gui clean check # 只编译和 elaborate生成 xxx_opt build: cd $(SIM_DIR) \ vsim -c -do do compile.do; quit -f cd $(SIM_DIR) \ vsim -c -do do elaborate.do; quit -f # 运行 cocotb前提是 xxx_opt 已经存在 run: cd $(SIM_DIR) \ vsim -c \ -pli $(COCOTB_VPI) \ -lib xil_defaultlib $(OPT_TOP) \ -do run -all; quit -f # 一键完整流程先 build再 cocotb run all: build run # 打开 GUI 调试 gui: cd $(SIM_DIR) \ vsim -gui \ -pli $(COCOTB_VPI) \ -lib xil_defaultlib $(OPT_TOP) \ -do log -r /*; run -all # 简单检查日志 check: cd $(SIM_DIR) \ grep -nEi error|failed|cannot|undefined|not found *.log transcript || true clean: rm -f $(SIM_DIR)/transcript rm -f $(SIM_DIR)/vsim.wlf rm -f $(SIM_DIR)/results.xml运行cd cocotb_test source ~/venvs/cocotb/bin/activate make all如果只改 Python 测试不改 RTL / IPmake run如果改了 RTLmake build make run如果改了 Xilinx IP 配置重新在 Vivado 中 generate_target simulation 重新 export_simulation 再 make build 再 make run12. 为什么必须先 build 再 run这一点必须写清楚。export_simulation只是生成compile.do elaborate.do simulate.do它不会自动生成你的top_opt你的top_opt是elaborate.do里的vopt生成的。所以正确顺序是Vivado export_simulation ↓ Questa 执行 compile.do ↓ Questa 执行 elaborate.do ↓ 生成 你的top_opt ↓ vsim -pli cocotb ... 你的top_opt也就是说make run之前必须至少成功执行过一次make build或者make all13. 常见错误和含义错误 1找不到 xxx_opt现象Failed to find design unit xxx_opt原因只 export_simulation 了但没有执行 elaborate.do解决make build或者手动cd sim_export_questa/questa vsim -c -do do compile.do; quit -f vsim -c -do do elaborate.do; quit -f错误 2找不到 Xilinx IP 库现象Library xpm not found Library fifo_generator_xxx not found Library axis_xxx not found原因modelsim.ini / vmap / simlib 路径没配置好解决export MODELSIM$HOME/xilinx_simlib/questa/modelsim.ini vmap | grep -E xpm|fifo|axis|unisims|secureip错误 3PYGPI_PYTHON_BIN 没设置现象PYGPI_PYTHON_BIN variable not set. Cant initialize Python interpreter!解决export PYGPI_PYTHON_BIN$HOME/venvs/cocotb/bin/python3错误 4COCOTB_TEST_MODULES 没设置现象Environment variable COCOTB_TEST_MODULES is not defined or empty解决export COCOTB_TEST_MODULEStest_top或者写进 Makefile。错误 5dut.clk 找不到现象AttributeError: DUT contains no object named clk原因Python 脚本里的端口名和 Verilog 顶层端口名不一致解决查看 DUT 顶层端口 修改 CLK_NAME / RST_NAME / AXIS prefix14. 后续 cocotb 测试怎么推进不要一开始就写大而全的测试。推荐顺序1. smoke test 只测 clk/reset/cocotb 接入 2. init test 初始化所有输入避免 X 3. interface test 写 driver / monitor 4. protocol test 检查 valid/ready/last 等协议 5. function test 引入 Python reference model 6. random test 随机输入、随机 backpressure、随机包长 7. stress test 长时间、多包、边界条件、reset 插入15. Questa 可以换成其他仿真器吗可以。 这套方法论不绑定 Questa。抽象流程是Vivado / 工程工具 生成 IP 仿真模型和仿真脚本 HDL 仿真器 compile / elaborate / simulate cocotb 通过 VPI / VHPI / FLI 接入仿真器 Python testbench 负责驱动、监控、比对Questa 只是其中一种仿真器。可替换为ModelSim / Questa Vivado XSim Cadence Xcelium Synopsys VCS Aldec Riviera-PRO Verilator Icarus Verilog但要注意含大量 Xilinx IP / encrypted IP / secureip / 复杂 XPM 的工程 更推荐 Questa / XSim / VCS / Xcelium / Riviera-PRO 纯 RTL 工程 可以考虑 Verilator / Icarus换仿真器时主要改这些Vivado export_simulation 的 -simulator 参数 仿真器 compile / elaborate / simulate 命令 库映射方式 cocotb-config 对应 backend Makefile尽量保持这些不变test_top.py axis.py monitor.py scoreboard.py ref_model.py16. 最终标准流程完整命令流程可以概括为# 1. 进入 Vivado手动裁剪仿真工程 # 2. Vivado Tcl Console 中执行 generate_target simulation [get_ips] export_ip_user_files -of_objects [get_ips] -no_script -sync -force set sim_export_dir [file normalize ./sim_export_questa] set simlib_dir [file normalize $::env(HOME)/xilinx_simlib/questa] export_simulation \ -simulator questa \ -directory $sim_export_dir \ -lib_map_path $simlib_dir \ -force然后在 Linux 终端# 3. 进入导出的 Questa 目录 cd 仿真工程目录/sim_export_questa/questa # 4. 指定库映射 export MODELSIM$HOME/xilinx_simlib/questa/modelsim.ini # 5. 编译并 elaborate生成 xxx_opt vsim -c -do do compile.do; quit -f | tee compile.log vsim -c -do do elaborate.do; quit -f | tee elaborate.log # 6. 确认 xxx_opt 可以被 Questa 加载 vsim -c -lib xil_defaultlib 你的top_opt -do run -all; quit -f # 7. 进入 cocotb 测试目录 cd ../../cocotb_test # 8. 激活 Python 环境 source ~/venvs/cocotb/bin/activate # 9. 一键运行 cocotb make run或者直接make all其中make build执行 compile.do elaborate.do生成 xxx_opt make run加载 xxx_opt并通过 -pli 接入 cocotb make all先 build再 run17. 一句话总结这套流程最重要的不是 cocotb 语法而是顺序先让 Vivado 导出正确脚本 再让 Questa 生成 xxx_opt 再确认 HDL 仿真能独立加载 最后才把 cocotb 接进去