cocotb(Coroutine-based COsimulation TestBench)是用 Python 写 RTL 测试的开源验证框架。让你用 Python 的简洁语法驱动 Verilog/SystemVerilog 信号、写断言、做仿真,替代传统的 SystemVerilog testbench。
和传统方式的对比:
| Python | ||
适合谁:RTL 工程师快速验证模块功能;对 SystemVerilog 验证方法学(UVM)不熟悉的人。
你的测试代码(Python) DUT(Verilog/SystemVerilog)┌──────────────────┐ ┌────────────────────┐│ test_counter.py │ cocotb │ counter.sv ││ 控制时钟、信号 │ ────────→ │ 被测模块 ││ 写断言检查结果 │ 驱动/采样 │ │└──────────────────┘ └────────────────────┘ │ ▼ HDL 仿真器(iverilog / verilator / Vivado xsim / Questa).py) + 一个 Makefile + DUT(.sv),三件套即可运行# 方案 A:Icarus Verilog(开源,推荐入门,cocotb 官方默认支持)sudo apt install iverilog -yiverilog -V # 应显示 Icarus Verilog version 12.0# 方案 B:Verilator(开源,速度快但语法要求严格)# sudo apt install verilator -y💡 cocotb 也支持 Vivado xsim、Questa、VCS 等商业仿真器(
SIM=xcelium等参数),本指南以 iverilog 为例。
Ubuntu 24.04 的 pip 受 PEP 668 保护,不允许直接装系统包,用 venv 虚拟环境最规范:
# 1. 创建虚拟环境(路径自定,如 ~/cocotb-env)python3 -m venv ~/cocotb-env# 2. 激活(每次开新终端跑测试前都要激活)source ~/cocotb-env/bin/activate# 3. 安装 cocotbpip install cocotb# 4. 验证cocotb-config --version # 应显示 2.x.x# 5.(可选)安装 pytest:断言失败时能显示更友好的报错信息pip install pytest⚠️ PEP 668 报错:直接在系统里
pip install cocotb会报externally-managed-environment——这是 Ubuntu 24.04 的保护机制,不要用--break-system-packages绕过,用 venv 即可。⚠️ 每次使用前记得
source激活:新开终端后 venv 不会自动生效,未激活时cocotb-config找不到。
~/projects/cocotb_counter/├── counter.sv ← 被测模块(DUT)├── test_counter.py ← cocotb 测试代码└── Makefile ← 构建/运行脚本module counter #( parameter WIDTH = 8) ( input wire clk, input wire rst, output reg [WIDTH-1:0] count); always @(posedge clk) begin if (rst) count <= 0; else count <= count + 1; endendmoduleimport cocotbfrom cocotb.clock import Clockfrom cocotb.triggers import FallingEdge@cocotb.test()asyncdeftest_counter(dut):"""测试计数器复位后正常递增"""# 启动 10ns 周期时钟(cocotb 2.x 参数名为 unit,旧版为 units) cocotb.start_soon(Clock(dut.clk, 10, unit="ns").start())# 复位await FallingEdge(dut.clk) dut.rst.value = 1await FallingEdge(dut.clk)assert dut.count.value == 0, f"复位失败: {dut.count.value}"# 释放复位,验证递增 1-4 dut.rst.value = 0for i inrange(1, 5):await FallingEdge(dut.clk)assert dut.count.value == i, f"期望 {i}, 实际 {dut.count.value}"print("counter 测试通过!")逐行解释:
@cocotb.test() | |
Clock(dut.clk, 10, units="ns") | |
cocotb.start_soon(...) | |
await FallingEdge(dut.clk) | |
dut.rst.value = 1 | |
dut.count.value | |
assert ... |
SIM ?= icarusTOPLEVEL_LANG ?= verilogVERILOG_SOURCES = $(PWD)/counter.svTOPLEVEL = counterCOCOTB_TEST_MODULES = test_counter # cocotb 2.x 写法(旧版 MODULE 已弃用)include$(shell cocotb-config --makefiles)/Makefile.simSIM | |
TOPLEVEL_LANG | |
VERILOG_SOURCES | |
TOPLEVEL | |
COCOTB_TEST_MODULES | MODULE,兼容但会提示弃用) |
SIM ?= icarus?= 表示"如果没设置才赋值"——这样可以在命令行覆盖:make SIM=verilator 就换仿真器,不用改文件。icarus = Icarus Verilog。
TOPLEVEL_LANG ?= verilogDUT 的语言。counter.sv 虽是 SystemVerilog(.sv 扩展名),但这里写 verilog 即可——iverilog 会按扩展名自动识别语法。
VERILOG_SOURCES = $(PWD)/counter.sv被测模块的文件列表(可多个,空格分隔)。$(PWD) 是 make 的内置变量(当前目录),拼成绝对路径,保证在任意目录执行 make 都能找到文件。
TOPLEVEL = counter顶层模块名 = DUT 的名字。cocotb 用它找到被测模块;也决定波形文件名(sim_build/counter.fst)。
COCOTB_TEST_MODULES = test_counterPython 测试文件名(不带.py 后缀)。
include$(shell cocotb-config --makefiles)/Makefile.sim最关键的一行:$(shell ...) 让 make 执行 shell 命令——cocotb-config --makefiles 会输出 cocotb 自带 Makefile 的路径(在 venv 里),include 把它加载进来。
💡 理解方式:我们写的 6 行是"配置",最后一行才是"引擎"。cocotb 自带几百行的 Makefile.sim 包含了编译、仿真、结果检查的全部规则,我们只是告诉它"用哪个仿真器、测哪个模块、跑哪个测试"。
make 运行时到底做了什么(实测)make │ ├─ 1. 生成转储模块 sim_build/cocotb_iverilog_dump.v(WAVES=1 时) ├─ 2. 编译:iverilog -o sim_build/sim.vvp counter.sv ... ├─ 3. 运行:vvp sim_build/sim.vvp(加载 cocotb VPI 库 + 设置环境变量) ├─ 4. 执行 Python 测试:test_counter.py ├─ 5. 检查结果 results.xml → 打印 PASS / FAIL └─ 结束💡 环境未创建过? 先看 §3.2 安装 cocotb 的①创建 venv 步骤(
python3 -m venv ~/cocotb-env),再继续下面。
source ~/cocotb-env/bin/activate # 激活 venvcd ~/projects/cocotb_countermake预期输出关键行:
-!- test_counter.test_counter passed ✓TEST PASS ✓全部 PASS 即测试通过。
排错提示:
make: *** No rule to make target '/Makefile.sim' | cocotb-config | COCOTB_CONFIG = /home/<用户名>/cocotb-env/bin/cocotb-config,再 include $(shell $(COCOTB_CONFIG) --makefiles)/Makefile.sim |
No module named cocotb | source ~/cocotb-env/bin/activate | |
iverilog: command not found | sudo apt install iverilog -y | |
Traceback 和 AssertionError |
推荐工作流:VS Code(WSL 窗口)写代码 → 集成终端跑仿真 → GTKWave 看波形。
第一步:打开项目(WSL 窗口)
cd ~/projects/cocotb_countercode .左下角出现绿色 >< WSL: Ubuntu 角标 = 已连入 WSL(详见 03 文档)。
第二步:集成终端中编译 + 仿真
按 `Ctrl+``(反引号)打开 VS Code 集成终端(自动是 Ubuntu bash):
source ~/cocotb-env/bin/activate # 激活虚拟环境(每个新终端都要)make # 编译 + 仿真看到 TESTS=1 PASS=1 FAIL=0 SKIP=0 即通过。
第三步:打开波形
gtkwave sim_build/counter.fst & # 或 sim_build/counter.vcd窗口弹出后:SST 栏选 counter 模块 → 选中 clk/rst/count → Append → 查看波形。
💡 在自己的 VS Code 集成终端里启动 gtkwave,窗口会一直开着(临时会话启动的窗口会随会话关闭而消失)。
进阶:配置 VS Code 任务,一键编译/开波形
在项目根目录创建 .vscode/tasks.json:
{"version":"2.0.0","tasks":[{"label":"cocotb: 编译+仿真","type":"shell","command":"source ~/cocotb-env/bin/activate && make","group":{"kind":"build","isDefault":true},"problemMatcher":[]},{"label":"gtkwave: 打开波形","type":"shell","command":"gtkwave sim_build/counter.fst &","group":"test"},{"label":"cocotb: 清理","type":"shell","command":"source ~/cocotb-env/bin/activate && make clean","group":"clean"}]}之后:
Ctrl+Shift+BCtrl+Shift+PTasks: Run Task → 选 gtkwave: 打开波形# 时钟:多种周期(cocotb 2.x 参数名为 unit)cocotb.start_soon(Clock(dut.clk, 10, unit="ns").start()) # 10nscocotb.start_soon(Clock(dut.clk, 100, unit="ps").start()) # 100ps# 等待事件await RisingEdge(dut.clk) # 上升沿await FallingEdge(dut.clk) # 下降沿await Timer(5, units="ns") # 固定延迟await ReadOnly() # 信号稳定后(组合逻辑结果)# 信号赋值(驱动)dut.data_in.value = 0xFFdut.enable.value = 1dut.data.value = 0b1010# 信号读取(采样)val = dut.data_out.valueif dut.valid.value == 1: ...# 日志输出dut._log.info("状态信息") # 带模块名的日志cocotb 是轻量的 Python 验证方案,适合模块级快速验证;UVM 是重型的 SystemVerilog 验证方法学,适合大型项目/SoC 验证。新手和中小模块 cocotb 上手更快。
可以。TOPLEVEL_LANG = systemverilog,配合 cocotb-bus 库(pip install cocotb-bus)有现成的 AXI-Stream / AXI-Lite 总线驱动。
官方不支持。 cocotb(含 2.0.1)没有 SIM=xsim——makefiles/simulators/ 目录里没有 Makefile.xsim,执行 make SIM=xsim 会因找不到仿真器 makefile 而报错。cocotb 官方确认 Vivado xsim 不受支持(功能请求见 cocotb issue #2416)。
推荐的替代方案(本流程,实测可用):
# 仿真器:iverilog + cocotb(Python 写 tb)source ~/cocotb-env/bin/activate && make vcd # 或 make waves# 波形 VCD 生成后,照样能在 Vivado 里打开分析!# Vivado: File → Open Waveform Configuration → 选 .vcd这样既用 Python 写 testbench(cocotb),又能把波形拿到 Vivado 里看,不需要 xsim。VCD 是 IEEE 标准格式,GTKWave 和 Vivado 都能开(见 §8.4 互通表)。
社区桥接方案(不推荐日常使用,限制大):
pip install cocotb-vivado | ||
pip install git+https://github.com/kiran-vuksanaj/vicoco.git@stable |
# Makefile 中加(实测有效):WAVES=1# 运行后(实测):ls sim_build/counter.fst # 波形文件位置gtkwave sim_build/counter.fst # 用 GTKWave 打开波形⚠️ 实测修正:网上常见的
COMPILE_ARGS += --trace是 Verilator 的写法,iverilog 会报invalid option -- '-'。iverilog + cocotb 请用WAVES=1。
units 参数改名、MODULE deprecated 之类的警告?cocotb 2.x 的 API 变化:Clock(..., units=...) 改名为 unit=...;Makefile 的 MODULE 改为 COCOTB_TEST_MODULES。警告不影响结果,但建议按新写法更新(本文档代码已更新)。
pytest not found?可选优化。安装 pytest 后断言失败时会显示更详细的报错(哪个值、期望什么):pip install pytest。
$(PWD) 变成了空路径?(实测坑)通过 wsl.exe -u 用户 -- bash -c '...' 多层 shell 传参时,$(PWD)、$(shell ...) 会被中间层 shell 提前展开成空,导致 Makefile 变成 VERILOG_SOURCES = /counter.sv、include /Makefile.sim,报 No rule to make target。
解法(二选一):
vim/gedit 等编辑器创建 Makefile(推荐,不会经过多层 shell)echo '<编码>' | base64 -d > Makefilemake 运行完后,工程目录下的产物:
| 测试结果 | sim_build/results.xml | ||
| 波形文件(默认) | sim_build/counter.fst | ||
| 波形文件(VCD 模式) | sim_build/counter.vcd | ||
sim_build/sim.vvp | |||
sim_build/libcocotbvpi_icarus.so | |||
sim_build/cocotb_iverilog_dump.v | |||
sim_build/cmds.f | -f 参数文件 |
💡 文件名中的
counter来自 Makefile 的TOPLEVEL——TOPLEVEL = counter决定波形文件名counter.fst/counter.vcd。
| FST | VCD | |
|---|---|---|
| IEEE 1364 标准格式 | ||
| 自己日常调试看波形 | 交给商用 EDA 分析 / 团队协作 / 存档 |
决策流程:
仿真完成,需要波形? │ ├─ 自己看 / 快速调试 / GTKWave 看 → 用默认 FST(最快,无需任何操作) │ └─ 需要 VCD? ├─ 波形要交给 Vivado/Questa/Verdi 分析 → VCD(EDA 通用格式) ├─ 波形要跨工具交换、存档 → VCD └─ 只是想多留一份 VCD → fst2vcd 一步转换(方法一)方法一:转换(最简单,日常推荐)
# cocotb 默认生成 fst 后,用 GTKWave 自带的 fst2vcd 转换fst2vcd sim_build/counter.fst > sim_build/counter.vcdgtkwave sim_build/counter.vcd # 或交给 Vivado/Questa 等 EDA 工具方法二:自定义 dump 模块直接生成(不需要 fst 中间过程)
① 工程根目录新建 my_dump.v:
module my_dump();initialbegin$dumpfile("sim_build/counter.vcd"); // 后缀 .vcd → 输出 VCD 格式$dumpvars(0, counter); // 0 = 该模块及以下全部层级;counter 换成你的顶层模块名endendmodule② Makefile 修改(实测通过的关键写法):
#WAVES=1 # ① 注释掉:禁用 cocotb 默认的 fst dumpVERILOG_SOURCES = $(PWD)/counter.sv $(PWD)/my_dump.v # ② 加入自定义 dump 模块COMPILE_ARGS += -s my_dump # ③ ⭐ 关键:把 my_dump 设为顶层模块include$(shell cocotb-config --makefiles)/Makefile.sim③ 运行:make → 直接生成 sim_build/counter.vcd(测试照常 PASS)。
原理:iverilog 只执行"顶层模块"的 initial 块——$dumpfile/$dumpvars 写在未例化的普通模块里不生效。cocotb 自己的 dump 模块就是靠 -s 参数设为顶层;自定义版本仿照同样做法即可。
⚠️ 不要用
COMPILE_ARGS += --trace:iverilog 12.0 已移除--trace/--trace-fst编译选项(iverilog --help查不到),会报invalid option -- '-'(详见 06 文档 §7)。新版波形生成统一走$dumpfile/$dumpvars+ 顶层激活机制。
| Vivado | .vcd;或 TCL 命令 open_vcd wave.vcd |
| QuestaSim / ModelSim | vcd2wlf wave.vcd wave.wlf → vsim -view wave.wlf |
| Verdi(Synopsys VCS) | |
| GTKWave | gtkwave wave.vcd |
📌 cocotb 官方文档:https://docs.cocotb.org

如需转载,请联系作者获得授权(点击公众号主页的“联系我们”)