Tiny-GPU 仿真与静态分析完整指南:Pyslang + Cocotb 实战
摘要
本文详细介绍如何将 Pyslang 静态分析工具 与 Cocotb 动态仿真框架结合,对 Tiny-GPU(一个极简的 GPU 实现)进行全面的硬件验证。我们将从环境搭建、问题解决、仿真运行到信号分析,提供完整的实践教程。
关键词:SystemVerilog、Pyslang、Cocotb、Icarus Verilog、GPU 仿真、静态分析、动态仿真
目录
- 工具介绍与安装
- [Cocotb 仿真环境搭建](#Cocotb 仿真环境搭建)
- 运行仿真测试
- [Pyslang 信号分析](#Pyslang 信号分析)
- 静态分析与动态仿真的关联
- 常见问题与解决方案
- 经验总结
- 实战教程
工具介绍与安装
核心工具
| 工具 | 用途 | 安装方式 | 位置 |
|---|---|---|---|
| iverilog | Verilog 编译器 | oss-cad-suite | /home/relis/tools/oss-cad-suite/bin/iverilog |
| vvp | Icarus 仿真器 | oss-cad-suite | /home/relis/tools/oss-cad-suite/bin/vvp |
| cocotb | Python 测试平台 | oss-cad-suite Python | /home/relis/tools/oss-cad-suite/py3bin/python3.11 |
| pyslang | SystemVerilog 分析 | 自定义安装 | /home/relis/pyslang/pyslangenv |
| sv2v | SystemVerilog 转 Verilog | oss-cad-suite | /home/relis/tools/sv2v-Linux/sv2v |
| gtkwave | 波形查看器 | oss-cad-suite | /home/relis/tools/oss-cad-suite/bin/gtkwave |
工具验证
运行以下命令确认工具安装正确:
bash
# 检查 iverilog 和 vvp
iverilog -v
vvp -v
# 检查 cocotb 版本
cocotb-config --version
/home/relis/tools/oss-cad-suite/py3bin/python3.11 -c "import cocotb; print(cocotb.__version__)"
# 检查 pyslang 分析模块
/home/relis/pyslang/pyslangenv/bin/python3 -c "from pyslang.analysis import AnalysisManager; print('pyslang OK')"
Cocotb 仿真环境搭建
什么是 Cocotb?
Cocotb (Coroutine-based Cosimulation Testbench) 是一个基于 Python 的硬件测试平台,允许:
- 用 Python 编写测试用例(而不是 Verilog)
- 使用 Python 库进行验证
- 支持多种仿真器(Icarus、Verilator 等)
- 更好的调试能力和 CI/CD 集成
初始环境问题
Tiny-GPU 项目的 Makefile 是为旧版本 cocotb 设计的,我们需要解决几个问题:
问题 1:Python 环境错误
现象:默认 Python 没有 cocotb 模块
bash
ModuleNotFoundError: No module named 'cocotb'
解决方案:使用 oss-cad-suite 自带的 Python(已安装 cocotb)
makefile
PYTHON_BIN = /home/relis/tools/oss-cad-suite/py3bin/python3.11
问题 2:Makefile 语法过时
现象 :使用 MODULE= 在 cocotb 2.x 中已被弃用
解决方案:使用 cocotb 的 makefile 模板
makefile
include $(shell $(PYTHON_BIN) -m cocotb_tools.config --makefiles)/Makefile.sim
问题 3:GPI_USERS 环境变量
现象:VPI 模块无法加载
ERROR: No GPI_USERS specified, exiting...
解决方案:使用 cocotb 的内置 makefile 系统,它会自动处理所有环境变量。
创建工作 Makefile
文件 :Makefile.cocotb
makefile
# Cocotb-based Makefile for tiny-gpu simulation
PYTHON_BIN = /home/relis/tools/oss-cad-suite/py3bin/python3.11
# 仿真器设置
SIM = icarus
TOPLEVEL = gpu
VERILOG_SOURCES = build/gpu.v
# Python 测试模块 - 可从命令行指定
MODULE ?= test.test_matadd
# 启用波形转储
WAVES = 1
# 包含 cocotb makefiles
include $(shell $(PYTHON_BIN) -m cocotb_tools.config --makefiles)/Makefile.sim
关键点:
- 使用
cocotb_tools.config而非cocotb-config(新语法) - 设置
SIM = icarus指定使用 Icarus Verilog VERILOG_SOURCES指向编译后的 Verilog(不是 SystemVerilog)WAVES = 1启用波形转储
Cocotb 2.x 兼容性问题
测试代码是为旧版 cocotb 编写的,需要更新:
问题:时钟参数弃用警告
位置 :test/helpers/setup.py:16
python
# 旧代码:clock = Clock(dut.clk, 25, units="us")
# 新代码:clock = Clock(dut.clk, 25, unit="us")
问题:LogicArray 类型兼容性
位置 :test/helpers/format.py
错误:
python
TypeError: unsupported operand type(s) for *: 'LogicArray' and 'LogicArray'
TypeError: unsupported operand type(s) for *: 'LogicArray' and 'int'
原因 :Cocotb 2.x 改变了信号值类型,现在是 LogicArray 对象。
解决方案:在进行数学运算前将 LogicArray 转换为整数:
修改前(第 102 行):
python
if int(str(dut.thread_count.value), 2) <= core.i.value * dut.THREADS_PER_BLOCK.value:
修改后:
python
thread_count_val = int(str(dut.thread_count.value), 2)
core_i_val = int(str(core.i.value))
threads_per_block_val = int(str(dut.THREADS_PER_BLOCK.value))
if thread_count_val <= core_i_val * threads_per_block_val:
修改前(第 109-113 行):
python
block_idx = core.core_instance.block_id.value
block_dim = int(core.core_instance.THREADS_PER_BLOCK)
thread_idx = thread.register_instance.THREAD_ID.value
idx = block_idx * block_dim + thread_idx
修改后:
python
block_idx = int(str(core.core_instance.block_id.value), 2)
block_dim = int(core.core_instance.THREADS_PER_BLOCK.value)
thread_idx = int(thread.register_instance.THREAD_ID.value)
idx = block_idx * block_dim + thread_idx
运行仿真测试
基本仿真命令
bash
# 运行矩阵加法测试
make -f Makefile.cocotb
# 运行矩阵乘法测试
make -f Makefile.cocotb MODULE=test.test_matmul
# 清理构建产物
make -f Makefile.cocotb clean
理解测试结果
仿真完成时,你会看到类似输出:
** TEST STATUS SIM TIME (ns) REAL TIME (s) RATIO (ns/s) **
**************************************************************************************
** test.test_matadd.test_matadd PASS 4475000.00 0.24 18560849.33 **
**************************************************************************************
** TESTS=1 PASS=1 FAIL=0 SKIP=0 4475000.00 0.24 18289520.23 **
**************************************************************************************
FST info: dumpfile sim_build/gpu.fst opened for output.
字段说明:
- STATUS: 测试状态(PASS/FAIL/SKIP)
- SIM TIME (ns): 仿真时间(纳秒)
- REAL TIME (s): 实际执行时间(秒)
- RATIO (ns/s): 仿真速度(每秒模拟的纳秒数)
测试用例详解
测试 1:矩阵加法(test_matadd)
描述:使用 8 个线程对两个 1×8 矩阵进行逐元素加法。
输入数据:
矩阵 A (地址 0-7): [0, 1, 2, 3, 4, 5, 6, 7]
矩阵 B (地址 8-15): [0, 1, 2, 3, 4, 5, 6, 7]
预期输出:
矩阵 C (地址 16-23): [0, 2, 4, 6, 8, 10, 12, 14]
结果:
- 周期数:178
- 仿真时间:4475us
- 状态:✓ PASS
测试 2:矩阵乘法(test_matmul)
描述:使用 4 个线程对两个 2×2 矩阵进行乘法运算。
输入数据:
矩阵 A (2×2): [[1, 2],
[3, 4]]
矩阵 B (2×2): [[1, 2],
[3, 4]]
预期输出:
矩阵 C = A × B: [[7, 10],
[15, 22]]
结果:
- 周期数:491
- 仿真时间:12300us
- 状态:✓ PASS
查看执行日志
日志保存在 test/logs/ 目录,文件名带时间戳:
bash
# 列出所有日志
ls -lt test/logs/
# 查看最新日志
cat test/logs/log_$(date +%Y%m%d)*.txt
# 只查看最终结果
tail -50 test/logs/log_*.txt
日志结构:
DATA MEMORY
+----------------+
| Addr | Data |
+----------------+
| 0 | 0 |
...
================================== Cycle 0 ==================================
+--------------------- Core 0 ---------------------+
+-------- Thread 0 --------+
PC: 0
Instruction: NOP
Core State: IDLE
...
Completed in 178 cycles
DATA MEMORY (Final)
+----------------+
| Addr | Data |
+----------------+
| 16 | 0 |
| 17 | 2 |
...
查看波形
当设置 WAVES = 1 时,cocotb 会生成波形文件:
bash
# 打开波形查看器
gtkwave sim_build/gpu.fst
注意:在 WSL 环境下 gtkwave 可能显示 X11 警告,但波形文件已成功生成。波形文件可以传输到有 GUI 的系统查看。
波形层次:与设计实例层次匹配
gpu.startgpu.donegpu.thread_countgpu.clkgpu.resetgpu.dcr_instance.*gpu.dispatch_instance.*gpu.cores[0].*- 等等
Pyslang 信号分析
运行信号追踪器
信号追踪器使用 pyslang 的分析模块查找信号驱动:
bash
# 追踪特定信号
/home/relis/pyslang/pyslangenv/bin/python3 signal_tracker.py -t "gpu.thread_count"
# 运行完整演示(追踪多个信号)
/home/relis/pyslang/pyslangenv/bin/python3 signal_tracker.py
# 在指定源目录中追踪信号
/home/relis/pyslang/pyslangenv/bin/python3 signal_tracker.py -s src -t "gpu.start"
理解信号驱动输出
============================================================
Tracking signal: gpu.thread_count
============================================================
Symbol found: thread_count
Kind: SymbolKind.Net
Found 1 driver(s):
Driver #1:
Kind: DriverKind.Continuous
Containing symbol: dcr_instance
Bit range: 0:7
LSP: Expression(ExpressionKind.NamedValue)
Flags: unidirectional_port
字段说明:
- Symbol found: 信号名称
- Kind: 符号类型(Net、Variable 等)
- Driver Kind (驱动类型):
Continuous:端口连接、连续赋值Procedural:在always_ff块中驱动的信号
- Containing symbol:驱动该信号的模块实例
- Bit range:驱动的位范围(多位信号)
- LSP:最长静态前缀(表达式类型)
- Flags (标志):
input_port:信号是输入端口unidirectional_port:端口方向固定isClockVar:信号是时钟
跨模块信号追踪
bash
/home/relis/pyslang/pyslangenv/bin/python3 cross_module_demo.py
这演示了跨模块边界的信号追踪,展示:
- 顶层端口 → 内部实例连接
- Continuous 与 Procedural 驱动器的区别
输出示例:
Tracking signal: gpu.thread_count
Symbol: thread_count
Kind: SymbolKind.Net
Found 1 driver(s):
Driver #1:
Kind: DriverKind.Continuous
Containing symbol: dcr_instance
Bit range: 0:7
Tracking signal: gpu.dcr_instance.thread_count
Symbol: thread_count
Kind: SymbolKind.Variable
Found 1 driver(s):
Driver #1:
Kind: DriverKind.Procedural
Containing symbol: (always_ff block)
Bit range: 0:7
静态分析与动态仿真的关联
关联概念
目标:使用 pyslang 分析实际在仿真 VCD/FST 文件中转储的信号。
tiny-gpu RTL 设计
│
├───► Pyslang(静态分析)
│ "谁驱动这个信号?"
│ "信号层次结构是什么?"
│ "模块如何连接?"
│
└───► Cocotb(动态仿真)
"信号有什么值?"
"信号何时变化?"
"时序如何?"
VCD 信号分析器
创建了一个新脚本来桥接这两个工具:
文件 :vcd_signal_analyzer.py
bash
# 运行分析器
/home/relis/pyslang/pyslangenv/bin/python3 vcd_signal_analyzer.py
该脚本:
- 加载 tiny-gpu 设计
- 运行 pyslang 分析
- 显示信号驱动信息
- 将信号映射到 VCD 波形路径
- 显示测试用例上下文(matadd vs matmul)
输出示例
======================================================================
KEY SIGNALS WITH DRIVER INFO (VCD Waveform Correlation)
======================================================================
gpu.thread_count - Number of threads for kernel (from DCR)
VCD Path: gpu.thread_count
└─ Driver: dcr_instance, Kind: Continuous, Bits: 0:7
gpu.done - Output signal - kernel completion status
VCD Path: gpu.done
└─ Driver: dispatch_instance, Kind: Continuous, Bits: 0:0
======================================================================
CROSS-MODULE SIGNAL FLOWS (Sim Test Case Context)
======================================================================
Top level: gpu.thread_count (VCD: gpu.thread_count)
Used in: test_matadd (8 threads), test_matmul (4 threads)
└─ Driven by: dcr_instance
Internal: gpu.dcr_instance.thread_count (VCD: gpu.dcr_instance.thread_count)
This is the actual register holding the thread count value
└─ Driven by: Continuous (procedural assignment in always_ff)
信号关联表
| 信号路径 | Pyslang 静态分析 | Cocotb 动态观察 |
|---|---|---|
gpu.thread_count |
8 位,由 dcr_instance 驱动 | matadd: 8,matmul: 4 |
gpu.done |
1 位,由 dispatch_instance 驱动 | matadd: 高@178,matmul: 高@491 |
gpu.start |
输入端口 | 测试平台驱动以启动内核 |
gpu.clk |
时钟信号 | 测试中 25us 周期 |
gpu.core_done |
2 位数组,core_instance | 显示核心完成状态 |
常见问题与解决方案
问题 1:"No module named 'cocotb'"
现象:
ModuleNotFoundError: No module named 'cocotb'
原因:Python 环境错误
解决方案:
bash
# 使用正确的 Python
export PYTHON_BIN=/home/relis/tools/oss-cad-suite/py3bin/python3.11
$PYTHON_BIN -c "import cocotb; print(cocotb.__version__)"
问题 2:"GPI_USERS specified, exiting"
现象:
ERROR: No GPI_USERS specified, exiting...
原因:cocotb 2.x 的 Makefile 语法不正确
解决方案:使用 cocotb 的 makefile 模板
makefile
include $(shell $(PYTHON_BIN) -m cocotb_tools.config --makefiles)/Makefile.sim
问题 3:"unsupported operand type(s) for *: 'LogicArray' and 'int'"
现象:
TypeError: unsupported operand type(s) for *: 'LogicArray' and 'int'
原因:Cocotb 2.x 改变了信号值类型
解决方案:在运算前将 LogicArray 转换为整数
python
val = int(str(signal.value), 2)
问题 4:重复模块错误
现象:
error: 'alu' has already been declared in this scope.
原因:build/gpu.v 已包含 alu 模块(编译时连接)
解决方案:在 VERILOG_SOURCES 中只包含 build/gpu.v
makefile
VERILOG_SOURCES = build/gpu.v
# 不要使用:VERILOG_SOURCES = $(shell ls build/*.v)
问题 5:gtkwave X11 错误(WSL)
现象:
xkbcommon: ERROR: failed to add default include path /usr/share/X11/xkb
原因:WSL X11 转发问题
解决方案:
- 波形文件仍已生成,可到其他地方查看
- 使用 VcXsrv 等 X 服务器
- 或使用 Linux GUI 系统查看波形
经验总结
1. 使用 Cocotb 的 Makefile 模板
经验:不要手动设置 cocotb 环境变量。使用内置的 makefile 模板。
原因:
- 自动处理所有环境变量
- 跨 cocotb 版本兼容
- 更少错误
方法:
makefile
include $(shell $(PYTHON_BIN) -m cocotb_tools.config --makefiles)/Makefile.sim
2. 理解 Python 环境依赖
经验:cocotb 等工具与特定 Python 环境绑定。
原因:
- oss-cad-suite 有自己的 Python 和已安装的 cocotb
- 不同的 Python = 不同的可用包
- 必须为正确的工具使用正确的 Python
方法:
bash
# 检查哪个 Python 有哪些包
/home/relis/tools/oss-cad-suite/py3bin/python3.11 -c "import cocotb"
/home/relis/pyslang/pyslangenv/bin/python3 -c "from pyslang.analysis import AnalysisManager"
3. 处理版本间的类型变化
经验:Cocotb 2.x 引入了信号值类型的重大变化。
原因:
- Cocotb 1.x 使用简单整数表示值
- Cocotb 2.x 使用 LogicArray 对象
- 数学运算需要显式转换
方法:
python
# 将 LogicArray 转换为整数
int_val = int(str(signal.value), 2)
4. 分离编译与仿真
经验:Tiny-GPU 项目首先将 SystemVerilog 编译为 Verilog。
原因:
- Icarus Verilog 对 SystemVerilog 支持有限
- sv2v 将 SystemVerilog 转换为 Verilog
- 测试文件使用编译后的 Verilog
方法:
bash
# 编译 SV 到 V(由 make compile 完成)
sv2v -I src/* -w build/gpu.v
# 然后使用 V 文件进行仿真
VERILOG_SOURCES = build/gpu.v
5. 静态和动态分析相辅相成
经验:Pyslang 和 cocotb 从不同角度分析同一设计。
静态(Pyslang):
- "设计结构如何?"
- "谁驱动这个信号?"
- "模块如何连接?"
动态(Cocotb):
- "设计运行时会发生什么?"
- "信号何时变化?"
- "实际值是什么?"
结合:
- 完整验证:结构 + 行为
- 更好的调试:追踪连接并观察行为
- 文档化:从多角度理解设计
实战教程
教程 1:运行第一次仿真
目标:运行矩阵加法测试并查看结果。
步骤:
-
进入项目目录
bashcd /home/relis/asic/tiny-gpu-master -
确保 build 目录存在
bashmkdir -p build -
编译设计
bashmake compile -
运行仿真
bashmake -f Makefile.cocotb -
检查结果
bash# 查看测试输出 # 应该看到 "test_matadd PASSED" # 查看执行日志 ls -lt test/logs/ cat test/logs/log_$(date +%Y%m%d)*.txt | tail -50
预期输出:
** test.test_matadd.test_matadd PASS 4475000.00
** TESTS=1 PASS=1 FAIL=0 SKIP=0
教程 2:使用 Pyslang 追踪信号
目标 :找出谁驱动 gpu.done 信号。
步骤:
-
运行信号追踪器
bash/home/relis/pyslang/pyslangenv/bin/python3 signal_tracker.py -t "gpu.done" -
解读结果
Symbol found: done Kind: SymbolKind.Net Found 1 driver(s): Driver #1: Kind: DriverKind.Continuous Containing symbol: dispatch_instance -
结论 :
done信号由dispatch_instance使用连续赋值驱动。
教程 3:将分析与仿真关联
目标:理解信号在静态和动态上下文中的行为。
步骤:
-
运行 VCD 信号分析器
bash/home/relis/pyslang/pyslangenv/bin/python3 vcd_signal_analyzer.py -
运行带波形的仿真
bashmake -f Makefile.cocotb -
关联发现
- Pyslang 说:
gpu.done由dispatch_instance驱动 - 仿真显示:
gpu.done在 178 周期变高 - 结论:调度器逻辑在 178 周期后正确地发出完成信号
- Pyslang 说:
教程 4:对比两个测试用例
目标:比较矩阵加法和乘法的性能。
步骤:
-
运行两个测试
bashmake -f Makefile.cocotb clean make -f Makefile.cocotb # matadd make -f Makefile.cocotb MODULE=test.test_matmul # matmul -
比较结果
matadd: 178 周期, 4475us matmul: 491 周期, 12300us -
追踪 thread_count 信号
bash/home/relis/pyslang/pyslangenv/bin/python3 signal_tracker.py -t "gpu.thread_count" -
结论:
- matadd 使用 8 个线程(更简单的操作)
- matmul 使用 4 个线程(更复杂的循环)
- matmul 耗时更长,因为有嵌套循环和内存访问
项目文件结构
tiny-gpu-master/
├── src/ # SystemVerilog 源文件
│ ├── alu.sv
│ ├── core.sv
│ ├── gpu.sv
│ └── ...
├── test/ # Cocotb 测试文件
│ ├── test_matadd.py # 矩阵加法测试
│ ├── test_matmul.py # 矩阵乘法测试
│ ├── helpers/ # 测试辅助模块
│ │ ├── setup.py # 测试设置工具
│ │ ├── memory.py # 内存仿真
│ │ ├── format.py # 输出格式化
│ │ └── logger.py # 日志工具
│ └── logs/ # 执行日志
│ └── log_*.txt # 逐周期追踪
├── build/ # 编译后的 Verilog
│ ├── gpu.v # 编译的 GPU 模块
│ └── alu.v # 编译的 ALU 模块
├── sim_build/ # Cocotb 构建目录
│ ├── sim.vvp # 编译的仿真
│ └── gpu.fst # 波形文件
├── Makefile # 原始 Makefile(已过时)
├── Makefile.cocotb # 新:Cocotb Makefile
├── signal_tracker.py # Pyslang 信号追踪
├── cross_module_demo.py # 跨模块演示
├── vcd_signal_analyzer.py # 新:VCD 关联
├── checkpoint.md # 前一个检查点(pyslang 设置)
├── checkpoint_simulation_analysis.md # 新:本文档
└── simulation_summary.md # 新:完整摘要文档
关键文件总结
| 文件 | 用途 | 工具 |
|---|---|---|
Makefile.cocotb |
运行 cocotb 仿真 | Cocotb |
signal_tracker.py |
追踪信号驱动 | Pyslang |
vcd_signal_analyzer.py |
关联信号与 VCD | Pyslang + Cocotb |
test/helpers/format.py |
格式化仿真输出 | Cocotb(已更新) |
sim_build/gpu.fst |
波形数据 | Cocotb |
test/logs/log_*.txt |
执行追踪 | Cocotb |
快速参考
常用命令
bash
# 仿真
make -f Makefile.cocotb # matadd 测试
make -f Makefile.cocotb MODULE=test.test_matmul # matmul 测试
make -f Makefile.cocotb clean # 清理构建
make -f Makefile.cocotb WAVES=0 # 无波形
# 信号分析
/home/relis/pyslang/pyslangenv/bin/python3 signal_tracker.py
/home/relis/pyslang/pyslangenv/bin/python3 signal_tracker.py -t "gpu.done"
/home/relis/pyslang/pyslangenv/bin/python3 vcd_signal_analyzer.py
# 波形查看
gtkwave sim_build/gpu.fst
# 日志
ls -lt test/logs/
tail -50 test/logs/log_*.txt
环境变量
bash
# Python 路径
export PYTHON_BIN=/home/relis/tools/oss-cad-suite/py3bin/python3.11
export PYSLANG_BIN=/home/relis/pyslang/pyslangenv/bin/python3
# 工具路径
export PATH=/home/relis/tools/oss-cad-suite/bin:$PATH
export PATH=/home/relis/tools/sv2v-Linux:$PATH
总结
本文档记录了以下工具的成功集成:
- Cocotb 仿真 - Tiny-GPU 内核测试的动态执行
- Pyslang 分析 - 信号驱动和层次结构的静态分析
- 波形关联 - 将静态分析与 VCD 文件连接
- 测试用例 - 矩阵加法和乘法内核
主要成就:
- ✓ 解决了 cocotb 2.x 兼容性问题
- ✓ 创建了可工作的 Makefile.cocotb
- ✓ 两个测试用例通过且结果正确
- ✓ 生成了用于分析的波形文件
- ✓ 将 pyslang 分析与仿真结果关联
- ✓ 创建了完整的文档
验证可用的工具:
- iverilog(Verilog 编译器)
- vvp(Icarus 仿真器)
- cocotb 2.1.0.dev0(Python 测试平台)
- pyslang(SystemVerilog 分析)
- sv2v(SystemVerilog 转 Verilog)
- gtkwave(波形查看器)
测试结果:
- 矩阵加法:178 周期,4475us ✓
- 矩阵乘法:491 周期,12300us ✓
参考资料
版权声明:本文档基于 Tiny-GPU 项目和实际实践总结整理。
作者 :Claude Sonnet
日期 :2026-02-16
项目位置 :/home/relis/asic/tiny-gpu-master
标签
#SystemVerilog #Pyslang #Cocotb #IcarusVerilog #GPU仿真 #硬件验证 #静态分析 #动态仿真 #RTL设计 #波形分析