您的位置:首页 > 编程语言 > Python开发

3---Python初体验之简单TestBench自动生成+Verilog模块信号提取

2017-08-31 14:53 1341 查看
学习了一些Python的基础语法之后,确实被Python优美的表达方式所吸引,所以想用Python来干点什么,之前一直想用MATLAB脚本来完成TestBench的自动生成,这里正好用Python来完成。

该TestBench主要包含以下三个部分:

基本的时钟clk和复位rst的生成

读取一个文件中的数据

提取一个模块的接口信号并将其实例化

前两个部分使用普通的文件写入操作就可以完成,第三个部分稍微复杂一点,需要用到正则表达式提取模块的名字、信号的名字和位宽等。总的来说,这是一段很简单的代码。

另外:可将第三部分用作Verilog子模块的顶层文件自动生成,省去复制粘贴连线的麻烦。

贴一个分分钟学会Python的链接:http://www.code123.cc/1049.html

1. 程序结构和运行流程

1.1 程序结构

该程序有三个文件。

主文件字符串文件模块提取函数文件
verilog_tb_gen.pyconfig.pyextract_interface.py
_

字符串文件中存着一些固定的打印信息

模块提取函数文件中则完成了整个模块接口信息的提取和打印

主文件完成调度管理

1.2 运行流程

打开或新建一个sim文件;

从config中提取生成时钟和复位的字符串并写入sim文件中;

同上:提取memory初始化字符串;

同上:提取memory数据读取的always块字符串;

调用模块提取函数提取指定文件的接口信息并写入sim文件;

关闭文件。

_

模块提取函数流程:

该函数有三个部分,每个部分的流程基本一致。

将提取文件中的字符按行分离为一个字符串列表,并赋给一个变量;

预编译含有接口信息的正则表达式;

利用for循环在每一行里面去搜索该正则表达式,如果搜索到了则进入下一步;

将搜索到的字符串按照空格或者逗号分离成一个个单独的字符串,这样就可以根据下标提取接口的信息;

将提取到的信息写入sim文件。

2. 流程中用到的操作说明

按1.2的流程:

1. 打开、关闭、及写入等文件操作与其他语言基本一致,详细可参考链接:http://blog.csdn.net/qq_16923717/article/details/77716137

2. 2、3、4步就是基本的打印和文本替换,与一般的print一样;

3. 模块接口信息提取用到的操作及参考链接如下:

读取并分行,用到了read()splitlines()http://blog.csdn.net/nanjunxiao/article/details/9086079

正则表达式,注意re.matchre.search的区别,以及为什么要预编译正则表达式

菜鸟教程正则表达式

廖雪峰Python教程正则表达式

正则表达式说明:

下面是该程序中最长的正则表达式

re_interface = re.compile('(input|output)(\s+|\[\d+:0\]|\s+\[\d+:0\]|\[\d+:0\]\s+|\s+\[\d+:0\]\s+)\w+(,(\s+|)\w+){0,10}')


该正则表达式匹配的是输入和输出信号的名字和位宽信息,因为考虑了Verilog书写的各种情况,所以该正则表达式才那么长,如果接口很规范,其实很短就可以了。

先注意正则表达式中的三个符号:’\’ ‘|’ ‘()’

反斜杠’\’:用作转义字符,如表达式中的\s+表示匹配至少一个空格,\w+匹配至少一个字母、数字或下划线,\d+匹配至少一个数字;

竖线’|’:表示或操作,如(input|output)即可匹配输入也可匹配输出;

圆括号()”:逻辑分离

Verilog输入输出的可能写法有以下几种:

// 第一个括号中匹配的情况
input   sig_1;
output  sig_2;
// 第二个括号中匹配的情况,按从左到右的顺序
input           signal_1;               // 位宽为1,中间只有空格
input[1:0]signal_2;                     // 完全没有空格
input   [1:0]signal_3;                  // 左边有空格
input[1:0]  signal_4;                   // 右边有空格
input   [1:0]   signal_5;               // 两边都有空格
// 第二个括号和第三个括号中间的 '\w+' 是匹配第一个信号的名字
// 第三个括号匹配的是多个信号中第一个信号后面的情况,如 ', signal_7',花括号里数字表示最多匹配10个
input           signal_6, signal_7;     // 一行有多个信号,信号间有逗号和空格
input           signal_8,signal_9;      // 信号间只有逗号


3. 附录

verilog_tb_gen.py代码

'''
Verilog testbench generator
'''
import re
import config
import extract_interface

# parameter
NAME = 'fir_sim'                            # tb name
PERIOD = 10                                 # clock period: ns
RST_DELAY = 200                             # reset delay : ns
DATA_WIDTH = 32                             # rom data width
DATA_LEN = 301                              # rom data length
PATH = r'D:\my_prj\my_filter_hdl_1\my_filter_hdl_1.srcs\msg.dat'
# memory path

# open and truncate file
fp = open('%s.v' % NAME, 'w')
fp.truncate()

# print clk and rst
fp.write(config.mod_clk_rst % (NAME, PERIOD, RST_DELAY))
fp.write(config.divide)
# print rom
fp.write(config.memory % (DATA_WIDTH - 1, DATA_LEN, PATH.replace('\\','/')))
fp.write(config.divide)
# print source data
fp.write(config.src_data % (DATA_WIDTH - 1, DATA_LEN))
fp.write(config.divide)
fp.write(config.divide)
'''
Extract module interface
'''
extract_interface.extract_print('fir_hdl.v', 0, fp)     # Extract file1
extract_interface.extract_print('fir.v', 1, fp)         # Extract file2

# The end, close the file
fp.write('\n\nendmodule')
fp.close()


config.py代码

'''
Verilog testbench generator
String
'''

# module clk rst
mod_clk_rst = 'module %s;\n\
parameter PERIOD = %d;\n\
reg clk = 1;\n\
reg rst = 1;\n\n\
initial begin\n\
forever\n\
#(PERIOD/2) clk = ~clk;\n\
end\n\n\
initial begin\n\
#%d\n\
rst = 0;\n\
end\n\n\
'

# divide
divide = '//------------------------------------------------------------\n'

# memory
memory = 'reg [%d:0] datain[0:%d];\n\
initial begin\n\
$readmemb("%s", datain);\n\
end\n\n\
'

# source data
src_data = "reg\t[15:0]\t\ti;\n\
reg\t[%d:0]\t\tsource_data;\n\
reg\t\t\t\tsource_data_vld;\n\n\
always @ (posedge clk) begin\n\
if (rst) begin\n\
i <= 'd0;\n\
source_data <= 'd0;\n\
source_data_vld <= 'd0;\n\
end\n\
else if (i < %d) begin\n\
i <= i + 1;\n\
source_data <= datain[i];\n\
source_data_vld <= 1;\n\
end\n\
else begin\n\
i <= i;\n\
source_data <= 'd0;\n\
source_data_vld <= 'd0;\n\
end\n\
end\n\n\
"


extract_interface.py代码

'''
Verilog testbench generator
Extract module interface
'''

import re

def extract_print( path, u, fp ):                               # u是例化模块时的编号
with open(path, 'r') as dotv:
split_as_lines = dotv.read().splitlines()
# Extract output wire
re_wire = re.compile('output(\s+|\[\d+:0\]|\s+\[\d+:0\]|\[\d+:0\]\s+|\s+\[\d+:0\]\s+)\w+(,(\s+|)\w+){0,10}')
for i in range(len(split_as_lines)):
x = re_wire.search(split_as_lines[i])
if x:
wire_list = re.split(r'[\s\,]+', x.group(0))
if re.match('\[', wire_list[1]):
width_info = wire_list[1]
indx = 2
else:
width_info = '\t'
indx = 1
for j in range(len(wire_list) - indx):
fp.write('wire\t%s\t\t%-20s;\n' % (width_info, wire_list[j + indx]))
fp.write('\n')

# Extract module name
re_module_name = re.compile('module\s+\w+')                 # 预编译正则表达式
for i in range(len(split_as_lines)):
y = re_module_name.search(split_as_lines[i])
if y:
module_name = re.split('\s+', y.group(0))[1]
fp.write('%-19su%-6d(\n' % (module_name, u))                # 打印module name

# Extract interface
re_interface = re.compile('(input|output)(\s+|\[\d+:0\]|\s+\[\d+:0\]|\[\d+:0\]\s+|\s+\[\d+:0\]\s+)\w+(,(\s+|)\w+){0,10}')
k = 0
for i in range(len(split_as_lines)):
z = re_interface.search(split_as_lines[i])
if z:
interface_string = re.sub(r'\[\d+:0\]', ' ', z.group(0))
interface_list = re.split(r'[\s\,]+', interface_string)
for j in range(len(interface_list) - 1):
if (k == 0):
fp.write('\t.%-21s( %-20s)\n' % (interface_list[j+1], interface_list[j+1]))
else:
fp.write('\t,.%-20s( %-20s)\n' % (interface_list[j+1], interface_list[j+1]))
k += 1
fp.write('\t);\n\n')
return


生成的tb文件

注意该tb还不是一个完整的tb

module fir_sim;
parameter PERIOD = 10;
reg clk = 1;
reg rst = 1;

initial begin
forever
#(PERIOD/2) clk = ~clk;
end

initial begin
#200
rst = 0;
end

//------------------------------------------------------------
reg [31:0] datain[0:301];
initial begin
$readmemb("D:/my_prj/my_filter_hdl_1/my_filter_hdl_1.srcs/msg.dat", datain);
end

//------------------------------------------------------------
reg [15:0]      i;
reg [31:0]      source_data;
reg             source_data_vld;

always @ (posedge clk) begin
if (rst) begin
i <= 'd0;
source_data <= 'd0;
source_data_vld <= 'd0;
end
else if (i < 301) begin
i <= i + 1;
source_data <= datain[i];
source_data_vld <= 1;
end
else begin
i <= i;
source_data <= 'd0;
source_data_vld <= 'd0;
end
end

//------------------------------------------------------------
//------------------------------------------------------------
wire    [31:0]      fir_out             ;

fir_hdl            u0     (
.clk                  ( clk                 )
,.fir_in              ( fir_in              )
,.fir_out             ( fir_out             )
);

wire    [31:0]      fir_out_TDATA       ;
wire                fir_in_TREADY       ;
wire                fir_out_TVALID      ;

fir                u1     (
.fir_in_TDATA         ( fir_in_TDATA        )
,.fir_out_TDATA       ( fir_out_TDATA       )
,.ap_clk              ( ap_clk              )
,.ap_rst_n            ( ap_rst_n            )
,.fir_in_TVALID       ( fir_in_TVALID       )
,.fir_in_TREADY       ( fir_in_TREADY       )
,.fir_out_TVALID      ( fir_out_TVALID      )
,.fir_out_TREADY      ( fir_out_TREADY      )
);

endmodule


提取的源文件接口

fir.v

module fir (
fir_in_TDATA,
fir_out_TDATA,
ap_clk,
ap_rst_n,
fir_in_TVALID,
fir_in_TREADY,
fir_out_TVALID,
fir_out_TREADY
);

input  [31:0] fir_in_TDATA;
output  [31:0] fir_out_TDATA;
input   ap_clk;
input   ap_rst_n;
input   fir_in_TVALID;
output   fir_in_TREADY;
output   fir_out_TVALID;
input   fir_out_TREADY;


fir_hdl.v

module fir_hdl(
input clk,
input [31:0] fir_in,
output [31:0] fir_out
);
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息