✎ 编 者 按
打造的一个基于 python 得时序图绘制工具 wavedraw,让绘图变得更符合习惯。
少不了的时序图绘制
无论是从事 FPGA 还是 ASIC,在做方案设计时总少不了借口时序图的绘制。而时序图的绘制所用到的也基本是 wavedrom 居多。wavedrom 绘图本质上是利用输入 json 来达到绘图的效果。之前也曾写过一篇文章《学会用代码绘制接口时序图》中也曾提到过如何使用代码进行绘图。然而在使用时却仍有些不便、难以符合阅读习惯。
来看这张两张图:
采用 wavedrom 进行绘图若直接手写 json 会存在一个问题就是要来回不停地调整每个信号的描述,一个信号变化的时钟位置发生了调整那么所有的信号都要调整,像上面描述 node 处有一个信号的 node 发生了变化就要调整所有信号的波形,十分不爽。
绘制接口时序图我们正常的思维应该是描述在每个时钟周期各信号的变化,而不是像 wavedrom 中直接描述那样先完全定义好一个信号的所有时钟周期变化后再去描述另外一个信号。
wavedraw
wavedraw 为一个基于 python 描述接口时序的 lib 库。主要包含四个类:
- WaveBool : 用于绘制单比特信号的波形时序。
- WaveBits:用于绘制多比特信号的波形时序。
- WaveGroup:用于生命一个信号 Group,类似上图中的 Master、Salve,WaveGroup 中可以创建波 WaveBool、WaveBits、WaveBits。
- WaveDraw:用于创建接口时序图,用于最终生成符合 wavedrom 要求的 json 格式。
在 WaveDraw 中,维护了一个时钟变量 currentClk,其用于指定当前波形待绘制的时钟周期位置,可一通过调用 incrClk 来实现时钟向前移动,同时通过函数 addSplit 支持为所有信号都添加一个时钟周期的"|"描述。
而在 WaveBool、WaveBits 中,分别有定义一个 wave 函数,其定义如下:
# WaveBool 定义
def wave(self,wave:str,node:str=None,offset:int=0,endWave:str="0",endNode:str=".",holdCycle:int=None):
""" add wave to signal
Args:
wave (str): signal wave string
node (str, optional): signal node define. Defaults to None.
offset (int, optional): wave start offset based on current clock time. Defaults to 0.
endWave (str, optional): end wave string,only used when holdCycle!=None. Defaults to "x".
endNode (str, optional): end node string,only used when holdCycle!=None. Defaults to ".".
holdCycle (int, optional): repeat wave holdCycle times and then end with endwave/enddata/endnode when holdCycle!=None. Defaults to None.
"""
# WaveBits 定义
def wave(self,wave:str,data:list[str],node:str=None,offset:int=0,endWave:str="x",endData:str=None,endNode:str=".",holdCycle:int=None):
""" add wave to signal
Args:
wave (str): signal wave string
data (list[str]): wave data list
node (str, optional): signal node define. Defaults to None.
offset (int, optional): wave start offset based on current clock time. Defaults to 0.
endWave (str, optional): end wave string,only used when holdCycle!=None. Defaults to "x".
endData (str, optional): end data string,only used when holdCycle!=None. Defaults to None.
endNode (str, optional): end node string,only used when holdCycle!=None. Defaults to ".".
holdCycle (int, optional): repeat wave holdCycle times and then end with endwave/enddata/endnode when holdCycle!=None. Defaults to None.
"""
两者唯一差别在于 WaveBits 中支持添加波形的 data 描述。
wave 函数有两种用法,一种是 HoldCycle=None,此时会向信号中添加一段 wave 波形,而另一种是 HoldCycle>0,此时则是重复 wave HoldCycle 个时钟周期,随后再添加一排 endwave 作为结尾。
demo0
demo0 用 wavedraw 来绘制下面这个波形:
其描述代码为:
这里代码 10~22 行用于声明信号及 Group 注册,代码 25 行至 29 行定义了写的接口时序,第 31 行则将时钟周期向前推进三个时钟周期,32 行则对所有信号添加一个"|",而 34 行至 38 行则添加读接口时序,其中第 35 行采用 holdCycle=3 的形式添加重复波形,而第 37 行指定 offset=2 则意味着相较于当前时钟,rdata 在两排过后方拉起,最终生成如下的 json:
{"signal":[
{"name":"clk","wave":'P...|.....',"node":'..........',"period":1,"phase":1},
["Master",
["ctrl",
{"name":"write","wave":'01.0|.....',"node":'.a........',"period":1,"phase":1},
{"name":"read","wave":'0...|1..0.',"node":'.....c....',"period":1,"phase":1},
],
{"name":"addr","wave":'x3.x|4..x.',"data":["A1","A2"],"node":'.........',"period":1,"phase":1},
{"name":"wdata","wave":'x3.x|.....',"data":["D1"],"node":'.........',"period":1,"phase":1},
],
["Slave",
["ctrl",
{"name":"ack","wave":'0.10|..10.',"node":'...b....d.',"period":1,"phase":1},
],
{"name":"rdata","wave":'x...|..5x.',"data":["Q2"],"node":'.........',"period":1,"phase":1},
],
],
"head":{"text":"demo","tick":0},
"edge":['a->b write','c->d read',],
"config":{"hscale":1}}
demo1
demo1 用于绘制下面的这幅波形图:
其描述代码如下:
同样,代码 12~16 行用于声明信号,19 行指定信号 A 维持高电平 9 个时钟周期,而 B 指定维持高电平 8 个时钟周期,通过 offset 指定其晚于当前时钟一个时钟周期后拉起,其他信号以此类推。在代码 31 行则添加 edges 的描述,最终生成的 json 格式如下:
{"signal":[
{"name":"A","wave":'0.1........0......',"node":'..a........j......',"period":1,"phase":1},
{"name":"B","wave":'0..1.......0.1....',"node":'...b.......i......',"period":1,"phase":1},
{"name":"C","wave":'0...1....0...1....',"node":'....c....h........',"period":1,"phase":1},
{"name":"D","wave":'0....1..0.....1...',"node":'.....d..g.........',"period":1,"phase":1},
{"name":"E","wave":'0.....10.......1..',"node":'......dg..........',"period":1,"phase":1},
],
"head":{"text":"demo1","tick":0},
"edge":['a~b t1','c-~a t2','c-~>d time 3','d~-e','e~>f','f->g','g-~>h','h~>i some text','h~->j',],
"config":{"hscale":1}}
写在最后
wavedraw 已放置在 github 上,感兴趣的小伙伴可以去下载安装,本文中的两个 demo 代码在 github 上也有上传,github 地址见阅读原文。
github 上也有一个 wavedrompy,其输入 json 能直接生成对应的接口时序图 SVG 格式文件,感兴趣的小伙伴也可以在这里生成 json 后调用该库直接生成 svg 图。
END
作者:玉骐
文章来源:Spinal FPGA
推荐阅读
更多 IC 设计干货请关注IC设计专栏。欢迎添加极术小姐姐微信(id:aijishu20)加入技术交流群,请备注研究方向。