Generating testbench written in python for VUnit
Project description
背景
这个项目起源于个人的硬件项目。
在项目开发过程之中,缺乏一个好的(面向软件从业人员的)单元测试工具非常让人头疼。即使 VUnit 提供了一个很好的测试框架,然而用 verilog 写单元测试这件事本身就很令人头疼。那么有没有一个测试框架可以用软件编程语言来描述硬件接口呢?我并没有找到,于是我在 VUnit 基础上,用 python 搭了一个简单的测试框架,提供了最简单的时序和逻辑描述和测试。
使用方法
首先安装 VUnit,常用方法是运行
> pip install --user vunit_hdl
或参照 VUnit 项目说明。
其次参(co)见(py) example/tests/adder.py,里面有详细使用方法的实例。说实话看代码比看下面的字要快。
创建一个测试用例
一个测试用例包括以下几个方面:模块、事件时钟和信号、测试。
from tests import Test
t = Test("需要测试的模块名",
"测试用例名",
"生成文件路径,建议使用 tests/__autogen__,需要事先创建好",
in_ports=["输入端口名,宽度默认为 1", ("输入端口名", 输入端口宽度)],
out_ports=["输出端口名,宽度默认为 1", ("输出端口名", 输出端口宽度)],
parameters={"参数名": 参数值})
t.addEventClock("事件时钟名", [事件间隔], 偏移)
t["输入端口名"] ** "事件时钟名" // 初始值 << [值序列]
t["输出端口名"] ** "事件时钟名" >> [期望值序列]
Test.run([t], ["测试模块源文件所在文件夹"])
定义事件时钟
一个事件时钟定义为一系列事件的循环。给定这一系列事件的时间间隔,以及整个时钟的偏移,就唯一确定了这些事件的发生时间。由于仿真器的限制,所有时间为负或者为零的事件均视为不发生。
事件时钟示例如下:
| [间隔], 偏移 | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 |
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| [1], 0 | x | x | x | x | x | x | x | x | x | x | x | x | x | x | x | |
| [1], 3 | x | x | x | x | x | x | x | x | x | x | x | x | ||||
| [1, 2], 0 | x | x | x | x | x | x | x | x | x | x | ||||||
| [1, 2], 4 | x | x | x | x | x | x | x | |||||||||
| [1, 2], -4 | x | x | x | x | x | x | x | x | x | x | x | |||||
| [2, 3, 2, 10000], 0 | x | x | x |
定义输入信号
一个输入端口必须依附于一个事件时钟,当事件发生时,输入值发生变化。例如如下定义
t["in"] ** "ec" // 0 << [2, 3, 5]
意味着输入端口 in 的初始值为 0,并且事件时钟 ec 的第 0、1、2 次事件发生时,输入信号分别变为 2、3、5。其中 t["in"] 返回一个端口,端口 ** “事件时钟名” 将输入端口依附于时钟;端口 // 初始值 给定端口初始值;端口 << [序列] 定义端口输入。
注意: 由于以上操作实际上是 python 操作符的重载,因此操作符的优先顺序没有发生变化,同样为
**>//><<,因此类似t["in"] // 0 ** "ec"的代码是错误的。由于<<的优先级比+低,因此t["in"] << [1] + [2]和t["in"] << [1] * 2是安全的,但是由于//的优先级和*一致,t["in"] // [1] + [2]和t["in"] // [1] * 2是错误的。
注意:一个端口只可以依附于一个事件时钟,并且只能有一个初始值,因此
t["in"] ** "ec0" ** "ec1"会使端口in依附于ec1,以最后出现的事件时钟为准;t["in"] // 0 // 1会使端口in的初始值设置为 1,以最后出现的值为准。如果端口已有输入信号序列,则不能设置(改变)初始值。
注意:输入信号是可扩展的,因此
t["in"] << [0, 1] << [2, 3]会使端口in的输入信号序列变为[0 1 2 3]。
定义输出信号
输出端口与输入端口类似,不同的是输出端口不能定义初始值,并且信号序列用大于号定义。输出端口的信号序列用于检查模块输出。例如如下定义
t["out"] ** "ec" >> [2, 3, 5]
意味着在事件时钟 ec 的第 0、1、2 次事件发生时,检查输出信号是否分别为 2、3、5。如果给定的输出信号序列包含 x,则对应事件跳过检查(亦即 x 可以对应任意值)。
信号序列表示方法
信号序列可以被简单的表示为数组。数组的每个元素可以是整数或者字符串。例如 [1, 2, 3]、["01", "xz"] 或者 [1, "xz"]。如果元素为整数,则需要非负且位宽小于端口宽度;如果元素为字符串,则字符串长度和端口宽度需要一致,并且只能包含 01xXzZ 6 种字符。
信号序列也可以被表示为字典。字典的键为事件次数,值可以是整数或者字符串,与数组表示方法里元素的要求一致。例如 {2: 2, 5: "xz"}。使用字典表示方法时,信号序列会被填充至字典中最大的事件次数,但是输入和输出端口对应填充值不一致:
- 输出端口:所有位填充
x - 输入端口
- 如果之前某一时刻有定义:填充为最近的有定义的时刻对应的值
- 如果之前没有定义(输入序列为空)
- 如果有初始值:填充为初始值
- 如果没有初始值:所有位填充
x字典表示方法尤其适用于输出端口,当只需要检测某一时刻的值时,使用字典比使用数组并且手写x要方便很多。
某些可序列化的信号序列可以被表示为字节。例子包括大部分的单 bit 串行接口。使用字节表示方法时,端口宽度需要为 2 的幂次,字节从前到后从高到低依次展开。例如 b"\xab" 对于一个宽度为 1 的端口等效为 ["1", "0", "1", "0", "1", "0", "1", "1"];对于一个宽度为 4 的端口等效为 ["1010", "1011"]。使用字节表示方法时,总位数一定是 8 的倍数,并且无法表示 x 和 z。
字节表示方法同样可以用于数组中,但是总位数需要和端口宽度完全一致。
信号与 python 类型的转换
为了方便用 python 进行逻辑建模,所有的信号序列都是 List[List[Value]] 类型。在使用 python 建模时,可以直接操作 Value 枚举类型,或者转换为数组表达形式,例如
input = [Value.valuesToInteger(i) for i in t["in"]._input]
output = [Value.valuesToString(o) for o in t["out"]._output]
其中 Value.valuesToString() 将一个信号转换为字符串;Value.valuesToInteger() 将一个仅包含 0 和 1 的信号转换为整数。
自问自答
- 为什么要搞这么一个东西?仿真器不好用吗?
一部分原因是我在 Mac 下用 VSCode 连到 Windows 虚拟机下写代码,所以一般不会真的去打开一个仿真器。再说 python 可以完成一部分计算任务(比如序列化),不需要靠人工去写测试向量和肉眼观察结果。
另外一部分原因是自动化测试脚本是没法使用仿真器界面的,只能靠命令行。
- 为什么默认 SystemVerilog 而不是 verilog?
因为 VUnit 用到了 SystemVerilog。当然,如果代码是 verilog,那么只需要在 tests.py 里面找到
`include "{module}.sv"
这一行,把 sv 改成 v 即可。
- 为什么是 python?
因为 VUnit 是用 python 实现的。再说了,python 它不香么?
- 为什么只有接口测试?内部信号怎么办?
因为我不会 ¯\_(ツ)_/¯。当然了,感谢 VUnit 的强大功能,使用 --gui 参数就可以打开默认的仿真器界面查看波形了。
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file vunit_py-0.0.2.tar.gz.
File metadata
- Download URL: vunit_py-0.0.2.tar.gz
- Upload date:
- Size: 31.7 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.1.0 CPython/3.12.3
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
2be74a68aeead726db94d2b2258671fd1331f5aacd0b4ae19852550613cfd61a
|
|
| MD5 |
276f569b37dca928237d641a22788ffd
|
|
| BLAKE2b-256 |
6ae575132a689f33a094f20bf77eab16683682b96ef40fba40afac7793f5b3f6
|
File details
Details for the file vunit_py-0.0.2-py3-none-any.whl.
File metadata
- Download URL: vunit_py-0.0.2-py3-none-any.whl
- Upload date:
- Size: 29.7 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.1.0 CPython/3.12.3
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
e9c2d34c90f6ed023970edf4122b14e643ebd0718b7a0f5c895c01d332e7c868
|
|
| MD5 |
22cd5d0feff847c2e210db16490ac3c2
|
|
| BLAKE2b-256 |
b8725031d5e4f5aebe59d81712a4c151be77c756d50e49d3e3c1789295d6bbb0
|