Vivado与VS Code:强强联手2-自动生成 Testbench

在对 Verilog 代码进行 行为级仿真 时,测试文件是必不可少的。可以自己写测试文件,但也会花费一些时间。利用 VS Code 可以自动生成测试文件,快速例化对应的模块,仅需要我们自己初始化时钟复位等信号,添加测试文件的运行时间等。

前言

​ 在对 Verilog 代码进行 行为级仿真 时,测试文件是必不可少的。可以自己写测试文件,但也会花费一些时间。利用 VS Code 可以自动生成测试文件,快速例化对应的模块,仅需要我们自己初始化时钟复位等信号,添加测试文件的运行时间等。

步骤

自动生成 Testbench 的大致步骤如下:

(1)在VS Code中安装插件 Verilog_Testbench

(2)给电脑安装 python3 及以上的环境。

(3)为确保VS Code中的插件可用,安装 chardet

(4)修改插件的原始py文件

(5)效果展示。

(1)安装插件 Verilog_Testbench

具体的使用方法为:在快捷键 ctrl + shift + p 下打开命令行,输入 Testbench 或者 Instance

刚开始没有设置的时候效果如下:

在 VS Code的应用商店搜索并安装插件 Verilog_Testbench

(2)给电脑安装 python3 及以上的环境

下载地址: https://www.python.org/downloads/

进行安装;

说明:

​ 1、安装时需要设置添加到系统变量的路径中。

​ 2、记住 python 的安装路径,后面可用。

或者根据安装地址添加两个环境变量到path下:

在运行窗口下,输入 cmd ,打开终端命令窗口,输入 python 进行验证是否安装成功!

出现该信息则为成功;

(3)为确保VS Code中的插件可用,安装 chardet

说明:

​ 需要使用 chardet 检测编码,是适用于 Python 2和3的通用编码检测器。chardet 支持检测中文、日文、韩文等多种语言。

下载地址:

https://pypi.org/project/chardet/

下载压缩文件 “ chardet-3.0.4.tar.gz ”;

解压该压缩文件到 python 安装位置下的 ‘site-packages’ 目录下,例如:”C:\Python\Python38-32\Lib\site-packages\chardet-3.0.4”

在运行窗口下,输入 cmd ,打开终端命令窗口,进入解压的 ‘chardet’ 目录下,执行命令:python setup.py install

显示出如此的信息即可。

(4)修改插件的原始py文件

在打开 .v 文件的 VS Code 下按 ctrl+p,输入 instance 可出现下述界面。

使用 Ctrl +单击 打开该链接,复制以下的代码来替换原文件中的代码,保存,重启 VS Code。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
#! /usr/bin/env python

'''
vTbgenerator.py -- generate verilog module Testbench
generated bench file like this:

fifo_sc #(
.DATA_WIDTH ( 8 ),
.ADDR_WIDTH ( 8 )
)
u_fifo_sc (
.CLK ( CLK ),
.RST_N ( RST_N ),
.RD_EN ( RD_EN ),
.WR_EN ( WR_EN ),
.DIN ( DIN [DATA_WIDTH-1 :0] ),
.DOUT ( DOUT [DATA_WIDTH-1 :0] ),
.EMPTY ( EMPTY ),
.FULL ( FULL )
);

Usage:
python vTbgenerator.py ModuleFileName.v

'''
import random
import re
import sys
from queue import Queue

import chardet


def delComment(Text):
""" removed comment """
single_line_comment = re.compile(r"//(.*)$", re.MULTILINE)
multi_line_comment = re.compile(r"/\*(.*?)\*/", re.DOTALL)
Text = multi_line_comment.sub('\n', Text)
Text = single_line_comment.sub('\n', Text)
return Text


def delBlock(Text):
""" removed task and function block """
Text = re.sub(r'\Wtask\W[\W\w]*?\Wendtask\W', '\n', Text)
Text = re.sub(r'\Wfunction\W[\W\w]*?\Wendfunction\W', '\n', Text)
return Text


def findName(inText):
""" find module name and port list"""
p = re.search(r'([a-zA-Z_][a-zA-Z_0-9]*)\s*', inText)
mo_Name = p.group(0).strip()
return mo_Name


def paraDeclare(inText, portArr):
""" find parameter declare """
pat = r'\s' + portArr + r'\s[\w\W]*?[;,)]'
ParaList = re.findall(pat, inText)

return ParaList


def portDeclare(inText, portArr):
"""find port declare, Syntax:
input [ net_type ] [ signed ] [ range ] list_of_port_identifiers

return list as : (port, [range])
"""
port_definition = re.compile(
r'\b' + portArr +
r''' (\s+(wire|reg)\s+)* (\s*signed\s+)* (\s*\[.*?:.*?\]\s*)*
(?P<port_list>.*?)
(?= \binput\b | \boutput\b | \binout\b | ; | \) )
''',
re.VERBOSE | re.MULTILINE | re.DOTALL
)

pList = port_definition.findall(inText)
t = []
for ls in pList:
if len(ls) >= 2:
t = t + portDic(ls[-2:])
return t


def portDic(port):
"""delet as : input a =c &d;
return list as : (port, [range])
"""
pRe = re.compile(r'(.*?)\s*=.*', re.DOTALL)

pRange = port[0]
pList = port[1].split(',')
pList = [i.strip() for i in pList if i.strip() != '']
pList = [(pRe.sub(r'\1', p), pRange.strip()) for p in pList]

return pList


def formatPort(AllPortList, isPortRange=1):
PortList = AllPortList

str = ''
if PortList != []:
l1 = max([len(i[0]) for i in PortList])+2
l3 = max(18, l1)

strList = []
str = ',\n'.join([' ' * 4 + '.' + i[0].ljust(l3)
+ '(' + (i[0]) + ')' for i in AllPortList])
strList = strList + [str]

str = ',\n\n'.join(strList)

return str


def formatDeclare(PortList, portArr, initial=""):
str = ''

if PortList != []:
str = '\n'.join([portArr.ljust(4) + ' '+(i[1]+min(len(i[1]), 1)*' '
+ i[0]) + ';' for i in PortList])
return str


def formatPara(ParaList):
paraDec = ''
paraDef = ''
if ParaList != []:
s = '\n'.join(ParaList)
pat = r'([a-zA-Z_][a-zA-Z_0-9]*)\s*=\s*([\w\W]*?)\s*[;,)]'
p = re.findall(pat, s)

l1 = max([len(i[0]) for i in p])
l2 = max([len(i[1]) for i in p])
paraDec = '\n'.join(['parameter %s = %s;'
% (i[0].ljust(l1 + 1), i[1].ljust(l2))
for i in p])
paraDef = '#(\n' + ',\n'.join([' .' + i[0].ljust(l1 + 1)
+ '( ' + i[1].ljust(l2)+' )' for i in p]) + ')\n'
return paraDec, paraDef


def portT(inText, ioPadAttr):
x = {}
count_list = []
order_list = []
for i in ioPadAttr:
p = port_index_list(inText, i)
for j in p:
count_list.append(j)
x[j] = i
count_list = quick_sort(count_list, 0, len(count_list)-1)
for c in count_list:
order_list.append(x.get(c))
return order_list


def quick_sort(myList, start, end):
if start < end:
i, j = start, end
base = myList[i]
while i < j:
while (i < j) and (myList[j] >= base):
j = j - 1

myList[i] = myList[j]

while (i < j) and (myList[i] <= base):
i = i + 1
myList[j] = myList[i]
myList[i] = base

quick_sort(myList, start, i - 1)
quick_sort(myList, j + 1, end)
return myList


def formatPort_order(padAttr, orderList):

for p in padAttr:
q = Queue()
for i in padAttr.get(p):
q.put(i)
padAttr[p] = q
AllPortList = []
for o in orderList:
AllPortList.append(padAttr.get(o).get())
return AllPortList


def port_index_list(intext, text):
l = []
t = intext
index = t.find(text)
while index > -1:
t = t.replace(text, random_str(len(text)), 1)
l.append(index)
index = t.find(text)
return l


def random_str(size):
s = ''
for i in range(size):
s += str(random.randint(0, 9))
return s


def getPortMap(AllPortList, ioPadAttr):
if len(AllPortList) != len(ioPadAttr):
return

p_map = {}
for i in range(len(AllPortList)):
p_map[ioPadAttr[i]] = AllPortList[i]
return p_map


def writeTestBench(input_file):
""" write testbench to file """
with open(input_file, 'rb') as f:
f_info = chardet.detect(f.read())
f_encoding = f_info['encoding']
with open(input_file, encoding=f_encoding) as inFile:
inText = inFile.read()

# removed comment,task,function
inText = delComment(inText)
inText = delBlock(inText)

# moduel ... endmodule #
moPos_begin = re.search(r'(\b|^)module\b', inText).end()
moPos_end = re.search(r'\bendmodule\b', inText).start()
inText = inText[moPos_begin:moPos_end]

name = findName(inText)
paraList = paraDeclare(inText, 'parameter')
paraDec, paraDef = formatPara(paraList)

ioPadAttr = ['input', 'output', 'inout']
orlder = portT(inText, ioPadAttr)
input = portDeclare(inText, ioPadAttr[0])
output = portDeclare(inText, ioPadAttr[1])
inout = portDeclare(inText, ioPadAttr[2])

portList = formatPort(formatPort_order(
getPortMap([input, output, inout], ioPadAttr), orlder))

input = formatDeclare(input, 'reg')
output = formatDeclare(output, 'wire')
inout = formatDeclare(inout, 'wire')

# write Instance

# module_parameter_port_list
if(paraDec != ''):
print("// %s Parameters\n%s\n" % (name, paraDec))

# list_of_port_declarations
#print("// %s Inputs\n%s\n" % (name, input))
#print("// %s Outputs\n%s\n" % (name, output))
#if(inout != ''):
# print("// %s Bidirs\n%s\n" % (name, inout))
print("\n")
# UUT
print("%s %s inst_%s (\n%s\n);" % (name, paraDef, name, portList))


if __name__ == '__main__':
writeTestBench(sys.argv[1])

(5)效果展示

在快捷键 ctrl + shift + p 下打开命令行,输入 Testbench 或者 Instance

输入 Instance,仅仅是对该 .v 文件模块的快速例化;

输入 Testbench,可以得到一个更为完整的测试文件;

参考

python–“chardet”安装方式

Vscode自动生成verilog例化


本文来自实验室同学 @WanTwoWan 投稿,在此表示感谢 🍰