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

学习了一些Python的基础语法之后,确实被Python优美的表达方式所吸引,所以想用Python来干点什么,之前一直想用MATLAB脚本来完成TestBench的自动生成,这里正好用Python来完成。
该TestBench主要包含以下三个部分:

  • 基本的时钟clk和复位rst的生成
  • 读取一个文件中的数据
  • 提取一个模块的接口信号并将其实例化

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

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

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

1. 程序结构和运行流程

1.1 程序结构

该程序有三个文件。

主文件 字符串文件 模块提取函数文件
verilog_tb_gen.py config.py extract_interface.py

_

  • 字符串文件中存着一些固定的打印信息
  • 模块提取函数文件中则完成了整个模块接口信息的提取和打印
  • 主文件完成调度管理

1.2 运行流程

  1. 打开或新建一个sim文件;
  2. 从config中提取生成时钟和复位的字符串并写入sim文件中;
  3. 同上:提取memory初始化字符串;
  4. 同上:提取memory数据读取的always块字符串;
  5. 调用模块提取函数提取指定文件的接口信息并写入sim文件;
  6. 关闭文件。
    _
    模块提取函数流程:
    该函数有三个部分,每个部分的流程基本一致。
    • 将提取文件中的字符按行分离为一个字符串列表,并赋给一个变量;
    • 预编译含有接口信息的正则表达式;
    • 利用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
    );

你可能感兴趣的:(FPGA,Python)