当前位置:首页>python>cocotb 使用指南——用 Python 验证你的 RTL

cocotb 使用指南——用 Python 验证你的 RTL

  • 2026-09-23 15:48:29
cocotb 使用指南——用 Python 验证你的 RTL
相关文章:建议先完成VSCODE、WSL的安装和配置
基于 VSCode 打造 AI Agent 开发环境
VS Code + GitHub 完整配置指南—推送你的第一个项目
WSL 概述
WSL 安装与基础配置
配置 VS Code Remote-WSL

文档中的示例工程链接:
https://github.com/HaroldFrey/cocotb_counter_example

目录

  1. cocotb 是什么
  2. 工作原理
  3. 安装
  4. 第一个测试:计数器示例
  5. 运行与查看结果
  6. 常用操作速查
  7. 常见问题
  8. 仿真输出产物与波形格式选择

1. cocotb 是什么

cocotb(Coroutine-based COsimulation TestBench)是用 Python 写 RTL 测试的开源验证框架。让你用 Python 的简洁语法驱动 Verilog/SystemVerilog 信号、写断言、做仿真,替代传统的 SystemVerilog testbench。

和传统方式的对比:

传统 SV testbench
cocotb
语言
SystemVerilog
Python
写起来
语法繁琐,需要完整的 SV 知识
简洁直观,普通 Python 语法
复用
每次重写
Python 生态随便用(numpy、正则、pip 包)
仿真器
依赖各家仿真器的 TB 机制
同一套测试代码,换仿真器不用改

适合谁:RTL 工程师快速验证模块功能;对 SystemVerilog 验证方法学(UVM)不熟悉的人。


2. 工作原理

你的测试代码(Python)          DUT(Verilog/SystemVerilog)┌──────────────────┐            ┌────────────────────┐│ test_counter.py  │  cocotb    │   counter.sv       ││ 控制时钟、信号    │ ────────→  │   被测模块         ││ 写断言检查结果    │  驱动/采样 │                    │└──────────────────┘            └────────────────────┘          │          ▼    HDL 仿真器(iverilog / verilator / Vivado xsim / Questa)
  • cocotb 作为 Python 库,被 HDL 仿真器加载,通过 VPI 接口直接驱动/采样 DUT 的信号
  • 一个测试文件(.py) + 一个 Makefile + DUT(.sv),三件套即可运行

3. 安装

3.1 安装 HDL 仿真器

# 方案 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 为例。

3.2 安装 cocotb(venv 虚拟环境,推荐)

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 找不到。


4. 第一个测试:计数器示例

4.1 文件结构

~/projects/cocotb_counter/├── counter.sv        ← 被测模块(DUT)├── test_counter.py   ← cocotb 测试代码└── Makefile          ← 构建/运行脚本

4.2 DUT:counter.sv(8 位计数器)

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;    endendmodule

4.3 测试代码:test_counter.py

import 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")
生成 10ns 周期时钟
cocotb.start_soon(...)
后台启动时钟协程
await FallingEdge(dut.clk)
等待时钟下降沿(同步时序)
dut.rst.value = 1
给信号赋值(驱动)
dut.count.value
读取信号当前值(采样)
assert ...
断言,失败则测试失败并打印信息

4.4 Makefile

SIM ?= icarusTOPLEVEL_LANG ?= verilogVERILOG_SOURCES = $(PWD)/counter.svTOPLEVEL = counterCOCOTB_TEST_MODULES = test_counter    # cocotb 2.x 写法(旧版 MODULE 已弃用)include$(shell cocotb-config --makefiles)/Makefile.sim
变量
含义
SIM
仿真器(icarus / verilator / xcelium …)
TOPLEVEL_LANG
DUT 语言(verilog / systemverilog)
VERILOG_SOURCES
DUT 源文件列表
TOPLEVEL
顶层模块名(即 DUT 名)
COCOTB_TEST_MODULES
Python 测试文件名(不含 .py,cocotb 2.x 写法;旧版用 MODULE,兼容但会提示弃用)

逐行解释

SIM ?= icarus

?= 表示"如果没设置才赋值"——这样可以在命令行覆盖:make SIM=verilator 就换仿真器,不用改文件。icarus = Icarus Verilog。

TOPLEVEL_LANG ?= verilog

DUT 的语言。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_counter

Python 测试文件名(不带.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  └─ 结束

5. 运行与查看结果

💡 环境未创建过? 先看 §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
 在 make 中找不到
确认 venv 已激活;若 PATH 含空格的 Windows 路径(WSL 常见),Makefile 中改写成 COCOTB_CONFIG = /home/<用户名>/cocotb-env/bin/cocotb-config,再 include $(shell $(COCOTB_CONFIG) --makefiles)/Makefile.sim
No module named cocotb
venv 未激活
source ~/cocotb-env/bin/activate
iverilog: command not found
仿真器未装
sudo apt install iverilog -y
测试失败但无具体信息
断言报错
查看输出中 Traceback 和 AssertionError

5.2 在 VS Code 中开发与运行(WSL 实测)

推荐工作流: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+B
     → 一键编译 + 仿真
  • Ctrl+Shift+P
     → 输入 Tasks: Run Task → 选 gtkwave: 打开波形
  • 任务在项目根目录运行,不用手动 cd

6. 常用操作速查

# 时钟:多种周期(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("状态信息")       # 带模块名的日志

7. 常见问题

Q: cocotb 和 UVM 有什么区别?

cocotb 是轻量的 Python 验证方案,适合模块级快速验证;UVM 是重型的 SystemVerilog 验证方法学,适合大型项目/SoC 验证。新手和中小模块 cocotb 上手更快。

Q: 能测 SystemVerilog 接口(AXI-Stream 等)吗?

可以。TOPLEVEL_LANG = systemverilog,配合 cocotb-bus 库(pip install cocotb-bus)有现成的 AXI-Stream / AXI-Lite 总线驱动。

Q: 能用 Vivado 的 xsim 仿真器跑 cocotb 吗?

官方不支持。 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 互通表)。

社区桥接方案(不推荐日常使用,限制大):

方案
安装
主要限制
cocotb-vivado(themperek)
pip install cocotb-vivado
只能访问顶层端口;边沿触发仅限 cocotb 生成的时钟;仅 Verilog 顶层
Vicoco(实验性)
pip install git+https://github.com/kiran-vuksanaj/vicoco.git@stable
只能访问顶层信号;时钟需 cocotb 生成

Q: 波形文件怎么生成?

# 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。

Q: 运行时有 units 参数改名、MODULE deprecated 之类的警告?

cocotb 2.x 的 API 变化:Clock(..., units=...) 改名为 unit=...;Makefile 的 MODULE 改为 COCOTB_TEST_MODULES。警告不影响结果,但建议按新写法更新(本文档代码已更新)。

Q: 提示 pytest not found?

可选优化。安装 pytest 后断言失败时会显示更详细的报错(哪个值、期望什么):pip install pytest。

Q: 用 wsl.exe 从 Windows 侧创建 Makefile,$(PWD) 变成了空路径?(实测坑)

通过 wsl.exe -u 用户 -- bash -c '...' 多层 shell 传参时,$(PWD)、$(shell ...) 会被中间层 shell 提前展开成空,导致 Makefile 变成 VERILOG_SOURCES = /counter.sv、include /Makefile.sim,报 No rule to make target。

解法(二选一):

  • 直接在 WSL 终端里用 vim/gedit 等编辑器创建 Makefile(推荐,不会经过多层 shell)
  • 若必须从 Windows 侧写入:先把内容 base64 编码(不含 shell 特殊字符),再在 WSL 里 echo '<编码>' | base64 -d > Makefile


8. 仿真输出产物与波形格式选择

8.1 cocotb 仿真输出产物清单

make 运行完后,工程目录下的产物:

产物
文件
格式
用途
测试结果sim_build/results.xml
JUnit XML
测试报告(PASS/FAIL 明细),CI 系统(Jenkins/GitLab)可直接解析
波形文件(默认)sim_build/counter.fst
FST
GTKWave 查看波形
波形文件(VCD 模式)sim_build/counter.vcd
VCD
GTKWave 或商用 EDA 工具查看
仿真程序
sim_build/sim.vvp
iverilog 字节码
仿真可执行体(vvp 运行)
VPI 库
sim_build/libcocotbvpi_icarus.so
共享库
cocotb 与仿真器的桥接库
转储模块
sim_build/cocotb_iverilog_dump.v
Verilog
cocotb 自动生成的波形记录模块(写死 .fst)
编译文件列表
sim_build/cmds.f
文本
iverilog 的 -f 参数文件

💡 文件名中的 counter 来自 Makefile 的 TOPLEVEL——TOPLEVEL = counter 决定波形文件名 counter.fst/counter.vcd。

8.2 FST vs VCD:怎么选

对比项
FSTVCD
格式性质
GTKWave 项目专用压缩格式
IEEE 1364 标准格式
文件大小
小(压缩后通常为 VCD 的 1/10~1/50)
大(纯文本)
打开速度
快
慢
谁能打开
GTKWave 系列
几乎所有 EDA 工具(Vivado/Questa/Verdi…)
cocotb 默认
✅(WAVES=1 写死生成)
需转换或自定义 dump
适合场景
自己日常调试看波形交给商用 EDA 分析 / 团队协作 / 存档

决策流程:

仿真完成,需要波形?  │  ├─ 自己看 / 快速调试 / GTKWave 看 → 用默认 FST(最快,无需任何操作)  │  └─ 需要 VCD?       ├─ 波形要交给 Vivado/Questa/Verdi 分析 → VCD(EDA 通用格式)       ├─ 波形要跨工具交换、存档 → VCD       └─ 只是想多留一份 VCD → fst2vcd 一步转换(方法一)

8.3 生成 VCD 的两种方法(均实测通过)

方法一:转换(最简单,日常推荐)

# 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 + 顶层激活机制。

8.4 波形与商用 EDA 工具互通

工具
打开 VCD 的方式
Vivado
File → Open Waveform Configuration… → 选 .vcd;或 TCL 命令 open_vcd wave.vcd
QuestaSim / ModelSim
自带转换工具:vcd2wlf wave.vcd wave.wlf → vsim -view wave.wlf
Verdi(Synopsys VCS)
原生支持 VCD/FSDB 互转
GTKWavegtkwave wave.vcd
 直接打开

📌 cocotb 官方文档:https://docs.cocotb.org


点击公众号菜单栏可查看更多内容。

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

最新文章

随机文章