之前尝试用CIRCT中的llhd-sim
进行了一个简单电路的仿真,但是llhd-sim
目前的输出是自定义格式,无法通过gtkwave
这样的工具查看仿真结果。本文记录将该格式转换为gtkwave
支持的vcd格式的过程。
vcd格式
VCD(Value change dump)是一种基于ASCII码的文件格式,用于记录由EDA仿真工具产生的信号信息。一个VCD文件通常包含了3个段,分别是:头信息(日期,仿真器,时间精度)、变量定义、值变化信息。这里以之前计数器数字电路仿真结果的vcd文件为例做一下介绍:
代码语言:javascript复制$version
llhd-sim 0.16.0
$end
$timescale 1ps $end
$scope module @updowncounter_tb $end
$var wire 1 ! clk $end
$var wire 1 " reset $end
$var wire 1 # up_down $end
$var wire 4 $ counter $end
$scope module @up_down_counter_param1 $end
$var wire 1 % clk $end
$var wire 1 & reset $end
$var wire 1 ' up_down $end
$var wire 4 ( counter $end
$var wire 4 ) counter_up_down $end
......
$upscope $end
$upscope $end
$enddefinitions $end
$dumpvars
b0 )
b0 -
b0 #
b0 '
b0 ,
b0 0
b0 "
b0 &
b0
b0 /
b0 $
b0 (
b0 !
b0 %
b0 *
b0 .
$end
#0
b1 "
b1 &
b1
......
上述vcd文件中,头信息段包含了版本信息和时间精度,分别包含在version和timescale关键字之内,每一部分通过end关键字指示结尾。变量定义部分,在scope和upscope关键字之间构成一个模块层级,通过这两个关键字的嵌套可以构成不同的模块层级;变量定义部分通过enddefinitions关键字指示变量定义结束。值变化区域先通过
LLHD-SIM输出格式
在CIRCT
中的llhd-sim
目前的输出为各个信号在不同时刻的取值,输出的每一行中都包含了时间、信号、信号取值三部分信息。这种格式包含大量重复信息,占用空间大,应该只是在开发调试阶段一种临时输出,我们想要采用gtkwave查看仿真波形需要自己写脚本转换一下。
0ps 0d 0e updowncounter_tb/clk 0x00
0ps 0d 0e updowncounter_tb/counter 0x00
0ps 0d 0e updowncounter_tb/inst/clk 0x00
0ps 0d 0e updowncounter_tb/inst/counter 0x00
0ps 0d 0e updowncounter_tb/inst/counter_up_down 0x00
上述数据中,0ps 0d 0e
表示时间信息,ps
为时间单位,另外的d
和e
是两个时间的微分(没找到相关资料,盲猜是可以用来保存建立时间和保持时间等参数,LLHD在设计上考虑得还是很完善的)。updowncounter_tb/clk
表示信号所在的电路模块以及信号名,采用/
分割后,最后一个值为信号名,前面的字符代表模块间的层级关系。最后一个值0x00
表示信号在当前时刻的取值为0。
格式转换
了解了vcd格式和llvm的原生格式后,可以考虑编写脚本进行转换。python中有现成的模块pyvcd
可以用来读取或者创建vcd格式的文件,我们就可以不用重复造轮子了,只要看下pyvcd
的api怎么调用就好了。
参考pyvcd
给出的使用示例:
>>> import sys
>>> from vcd import VCDWriter
>>> with VCDWriter(sys.stdout, timescale='1 ns', date='today') as writer:
... counter_var = writer.register_var('a.b.c', 'counter', 'integer', size=8)
... real_var = writer.register_var('a.b.c', 'x', 'real', init=1.23)
... for timestamp, value in enumerate(range(10, 20, 2)):
... writer.change(counter_var, timestamp, value)
... writer.change(real_var, 5, 3.21)
可以发现只要简单三个API就可以创建一个vcd格式的文件: 1. 使用VCDWriter()
创建一个写入对象writer
; 2. 使用register_var()
创建信号; 3. 使用change()
改变信号值。
这三个API的具体使用参数可以参考文档:
代码语言:javascript复制classvcd.writer.VCDWriter(file: IO[str], timescale: Union[vcd.common.Timescale, Tuple[int, str], str] = '1 us', date: Optional[str] = None, comment: str = '', version: str = '', default_scope_type: Union[vcd.common.ScopeType, str] = <ScopeType.module: 'module'>, scope_sep: str = '.', check_values: bool = True, init_timestamp: Union[int, float] = 0)
register_var(scope: Union[str, Sequence[str]], name: str, var_type: Union[vcd.common.VarType, str], size: Union[int, Sequence[int], None] = None, init: Union[bool, int, float, str, None, Sequence[Union[int, bool, str, None]]] = None) → vcd.writer.Variable
change(var: vcd.writer.Variable, timestamp: Union[int, float], value: Union[bool, int, float, str, None, Sequence[Union[int, bool, str, None]]]) → None
另外还需要注意的一点是,vcd格式要求信号的定义必须在改变之前(VCD格式的所有信号定义需要在变量定义过程中完成),因此在将LLHD格式转换成VCD格式时需要先把所有信号提取出来,然后再根据时间先后顺序修改各个变量的值。一个简单的转换脚本放在github上了。转换后的vcd文件可以用gtkwave
查看,以之前仿真的计数器为例,CIRCT
中的llhd-sim
目前也能基本实现正确的逻辑仿真。