<
返回首页

pyverilog脚本的使用

来源:IC技术交流

上节说到pyverilog有很多示例脚本,本节开始逐个分析。

1示例脚本下载及说明

可以在github下载,这里提供百度网盘下载


(相关资料图)

解压后可以看到如下示例脚本

unzip Pyverilog-develop.zipcd Pyverilog-develop/exampleslltotal 80-rw-r--r--. 1  3153 Jul 31 18:25 example_active_analyzer.py-rw-r--r--. 1  2996 Jul 31 18:25 example_active_range.py-rw-r--r--. 1  2227 Jul 31 18:25 example_ast_code.py-rw-r--r--. 1  1749 Jul 31 18:25 example_codegen.py-rw-r--r--. 1  3648 Jul 31 18:25 example_controlflow_analyzer.py-rw-r--r--. 1  3176 Jul 31 18:25 example_dataflow_analyzer.py-rw-r--r--. 1  3952 Jul 31 18:25 example_dataflow_codegen.py-rw-r--r--. 1  4555 Jul 31 18:25 example_graphgen.py-rw-r--r--. 1   560 Jul 31 18:25 example_identifierreplace.py-rw-r--r--. 1   508 Jul 31 18:25 example_identifiervisitor.py-rw-r--r--. 1  1549 Jul 31 18:25 example_lexer.py-rw-r--r--. 1  3199 Jul 31 18:25 example_merge.py-rw-r--r--. 1  2230 Jul 31 18:25 example_optimizer.py-rw-r--r--. 1  1599 Jul 31 18:25 example_parser.py-rw-r--r--. 1  1441 Jul 31 18:25 example_preprocessor.py-rw-r--r--. 1  4210 Jul 31 18:25 example_subset.py-rw-r--r--. 1  3138 Jul 31 18:25 example_walker.py-rw-r--r--. 1  2130 Jul 31 18:25 Makefile

2 example_preprocessor.py分析

该脚本的主要作用是预处理verilog文件,预处理verilog中的宏定义和include文件,然后输出一个纯粹的verilog文件,不再受define和include的制约,方便后续处理。

每行脚本分析如下所示:

# 这行代码是使用绝对导入的未来语法。在Python2.x 版本中,导入模块时,如果模块与当前脚本的名称冲突,Python会优先导入当前脚本。使用from __future__ import absolute_import可以确保导入模块时,不会优先导入当前脚本。from __future__ import absolute_import#这行代码是使用print()函数的未来语法。在Python 2.x 版本中,print是一个关键字而不是函数,不需要使用括号。使用from __future__ import print_function可以让Python 2.x 版本中的print行为与Python 3.x 版本中的print()函数一致。from __future__ import print_function# 这行代码导入了Python标准库中的sys模块,用于访问与Python解释器相关的变量和函数。import sys# 这行代码导入了Python标准库中的os模块,用于与操作系统进行交互,例如文件和目录操作。import os# 这行代码导入了Python标准库中的optparse模块中的OptionParser类,用于解析命令行选项和参数。fromoptparseimportOptionParser#thenextlinecanberemovedafterinstallationsys.path.insert(0, os.path.dirname(os.path.dirname(os.path.abspath(__file__))))# 这行代码的作用是将脚本所在文件的父目录添加到Python模块搜索路径中# os.path.abspath(__file__):__file__是Python中一个内置变量,表示当前脚本的文件名。os.path.abspath()函数用于获取当前脚本的绝对路径。例如,如果脚本文件位于/home/user/example.py,那么os.path.abspath(__file__)将返回/home/user/example.py# os.path.dirname():os.path.dirname()函数用于获取路径中的目录部分。将上一步得到的绝对路径传递给os.path.dirname()函数,将返回/home/user,即父目录的路径# os.path.dirname(os.path.dirname(os.path.abspath(__file__)))):通过多次调用os.path.dirname()函数,可以获取到脚本所在文件的父目录的父目录# sys.path.insert(0, ...):sys.path是Python中的一个列表,包含了Python模块搜索路径。sys.path.insert(0, ...)将指定的路径插入到列表的第一个位置,即将脚本所在文件的父目录添加到Python模块搜索路径的最前面# 这行代码导入了Pyverilog库,它是一个用于解析和处理Verilog代码的Python库。import pyverilog# 这行代码从Pyverilog库的vparser.preprocessor模块中导入了preprocess函数。preprocess函数用于对Verilog代码进行预处理,包括宏展开、头文件包含等操作from pyverilog.vparser.preprocessor import preprocessdef main():    INFO = "Verilog Preprocessor"    # pyverilog.__version__是Pyverilog库的版本号    VERSION = pyverilog.__version__    USAGE = "Usage: python example_preprocessor.py file ..."    # 定义showVersion子函数,打印信息    def showVersion():        print(INFO)        print(VERSION)        print(USAGE)        sys.exit()            # optparser.add_option()方法用于添加选项。每个选项都是一个参数的配置,包括名称、选项类型、目标变量、默认值和帮助信息等。具体参数解释如下:    # -v 或--version:短选项和长选项,用来显示版本号。    # action="store_true":当选项被指定时,将目标变量设为True。    # dest="showversion":将选项的值存储到showversion变量中。    # default=False:如果选项未被指定,则将showversion变量的默认值设为False。    # help="Show the version":选项的帮助信息。    # -I 或--include:短选项和长选项,用来指定包含路径。    # dest="include":将选项的值存储到include变量中。    # action="append":当选项被指定时,将选项的值追加到include变量中。    # default=[]:如果选项未被指定,则将include变量的默认值设为一个空列表。    # help="Include path":选项的帮助信息。    # -D:短选项,用来指定宏定义。    # dest="define":将选项的值存储到define变量中。    # action="append":当选项被指定时,将选项的值追加到define变量中。# default=[]:如果选项未被指定,则将define变量的默认值设为一个空列表。# help="Macro Definition":选项的帮助信息。    optparser = OptionParser()    optparser.add_option("-v", "--version", action="store_true", dest="showversion",                         default=False, help="Show the version")    optparser.add_option("-I", "--include", dest="include", action="append",                         default=[], help="Include path")    optparser.add_option("-D", dest="define", action="append",                         default=[], help="Macro Definition")# optparser.parse_args()方法用于解析命令行参数,并将解析结果赋值给options和args变量。options是一个对象,包含了解析后的选项和参数的值;args是一个列表,包含了解析后的位置参数的值。    (options, args) = optparser.parse_args()    filelist = args    if options.showversion:        showVersion()    for f in filelist:        if not os.path.exists(f):# os.path.exists()函数判断文件是否存在。如果文件不存在,则抛出一个IOError异常,并将异常消息设为"file not found: " + f,其中f是文件路径            raise IOError("file not found: " + f)#如果filist为空,则输出自定义子函数    if len(filelist) == 0:        showVersion()    # preprocess()函数是在pyverilog.vparser.preprocessor模块中定义的,用于对Verilog文件进行预处理。它接受三个参数:# filelist:一个包含文件路径的列表,表示需要进行预处理的文件。    # include:一个包含包含路径的列表,用于指定预处理时的包含路径。    # define:一个包含宏定义的列表,用于指定预处理时的宏定义。    # 该函数返回预处理后的文本,将其存储在text变量中。    text = preprocess(filelist, include=options.include, define=options.define)    print(text)# __name__是一个特殊的内置变量,表示当前模块的名称。当一个Python脚本直接被运行时,__name__的值为"__main__";当一个Python模块被导入时,__name__的值为模块的名称。# 因此,if __name__=="__main__":这个条件判断语句的作用是,只有当当前脚本直接被运行时,才会执行main()函数。#这样做的好处是,可以在脚本中定义一些测试代码或者执行一些初始化操作,而这些代码在脚本被导入时不会执行。只有当脚本直接被运行时,才会执行这些代码。if __name__ == "__main__":    main()

该脚本的应用示例如下所示:

3 example_parser.py分析

该模块用于分析verilog代码,生成抽象语法树(ATS)和指令列表(directives)。

from __future__ import absolute_importfrom __future__ import print_functionimport sysimport osfromoptparseimportOptionParser# the next line can be removed after installationsys.path.insert(0, os.path.dirname(os.path.dirname(os.path.abspath(__file__))))import pyverilogfrom pyverilog.vparser.parser import parsedef main():    INFO = "Verilog code parser"    VERSION = pyverilog.__version__    USAGE = "Usage: python example_parser.py file ..."    def showVersion():        print(INFO)        print(VERSION)        print(USAGE)        sys.exit()    optparser = OptionParser()    optparser.add_option("-v", "--version", action="store_true", dest="showversion",                         default=False, help="Show the version")    optparser.add_option("-I", "--include", dest="include", action="append",                         default=[], help="Include path")    optparser.add_option("-D", dest="define", action="append",                         default=[], help="Macro Definition")    (options, args) = optparser.parse_args()    # parse()函数是在pyverilog.vparser.parser模块中定义的,用于解析Verilog文件。它接受三个参数:    # filelist:一个包含文件路径的列表,表示需要进行解析的文件。    # preprocess_include:一个包含包含路径的列表,用于指定预处理时的包含路径。    # preprocess_define:一个包含宏定义的列表,用于指定预处理时的宏定义。    # 该函数返回解析后的抽象语法树(AST)和指令(directives),将其分别存储在ast和directives变量中。    filelist = args    if options.showversion:        showVersion()    for f in filelist:        if not os.path.exists(f):            raise IOError("file not found: " + f)    if len(filelist) == 0:        showVersion()    # ast.show()是AST对象的一个方法,用于以可读的形式打印出整个抽象语法树的结构。调用ast.show()后,会将抽象语法树的结构输出到控制台。    # directives是一个包含行号和指令的元组列表。通过循环遍历directives,可以逐行打印出每个指令的行号和内容。    # 这段代码的作用是先展示解析后的抽象语法树的结构,然后逐行打印出每个指令的行号和内容。这样可以更好地了解解析后的结果,并进行后续的处理或分析。    ast, directives = parse(filelist,                            preprocess_include=options.include,                            preprocess_define=options.define)    ast.show()    for lineno, directive in directives:        print("Line %d : %s" % (lineno, directive))if __name__ == "__main__":    main()

4 example_ast_code.py分析

该脚本用于构建ATS抽象树,进而生成rtl代码

# from __future__ import absolute_importfrom __future__ import print_functionimport sysimport os# the next line can be removed after installationsys.path.insert(0, os.path.dirname(os.path.dirname(os.path.abspath(__file__))))# vast模块是pyverilog库中定义的抽象语法树(AST)的模块。它包含了用于构建和操作Verilog抽象语法树的各种类和函数。# ASTCodeGenerator类是pyverilog库中的一个代码生成器类。它提供了将抽象语法树(AST)转换成为Verilog代码的功能。#通过导入这两个模块和类,我们可以使用vast模块中的类来构建Verilog抽象语法树(AST),然后使用ASTCodeGenerator类来将AST转换成为Verilog代码。import pyverilog.vparser.ast as vastfrom pyverilog.ast_code_generator.codegen import ASTCodeGeneratordef main():    datawid = vast.Parameter("DATAWID", vast.Rvalue(vast.IntConst("32")))    params = vast.Paramlist([datawid])    clk = vast.Ioport(vast.Input("CLK"))    rst = vast.Ioport(vast.Input("RST"))    width = vast.Width(vast.IntConst("7"), vast.IntConst("0"))    led= vast.Ioport(vast.Output("led", width=width))    ports = vast.Portlist([clk, rst, led])    width = vast.Width(vast.Minus(vast.Identifier("DATAWID"),                                  vast.IntConst("1")), vast.IntConst("0"))    count = vast.Reg("count", width=width)    assign = vast.Assign(        vast.Lvalue(vast.Identifier("led")),        vast.Rvalue(            vast.Partselect(                vast.Identifier("count"),                vast.Minus(vast.Identifier("DATAWID"), vast.IntConst("1")),                 vast.Minus(vast.Identifier("DATAWID"), vast.IntConst("8")))))     sens = vast.Sens(vast.Identifier("CLK"), type="posedge")    senslist = vast.SensList([sens])    assign_count_true = vast.NonblockingSubstitution(        vast.Lvalue(vast.Identifier("count")),        vast.Rvalue(vast.IntConst("0")))    if0_true = vast.Block([assign_count_true])    # count + 1    count_plus_1 = vast.Plus(vast.Identifier("count"), vast.IntConst("1"))    assign_count_false = vast.NonblockingSubstitution(        vast.Lvalue(vast.Identifier("count")),        vast.Rvalue(count_plus_1))    if0_false = vast.Block([assign_count_false])    if0 = vast.IfStatement(vast.Identifier("RST"), if0_true, if0_false)    statement = vast.Block([if0])    always = vast.Always(senslist, statement)    items = []    items.append(count)    items.append(assign)    items.append(always)    ast = vast.ModuleDef("top", params, ports, items)    codegen = ASTCodeGenerator()    rslt = codegen.visit(ast)    print(rslt)if __name__ == "__main__":    main()

生成的verilog代码如下所示:

module top #(  parameter DATAWID = 32)(  input CLK,  input RST,  output [7:0] led);  reg [DATAWID-1:0] count;  assign led = count[DATAWID-1:DATAWID-8];  always @(posedge CLK) begin    if(RST) begin      count <= 0;    end else begin      count <= count + 1;    end  endendmodule

话说通过ATS生成verilog确实很繁琐,还不如直接上手写个veirlog。

本节介绍结束。

审核编辑:汤梓红

标签: