上节说到pyverilog有很多示例脚本,本节开始逐个分析。
1 示例脚本下载及说明
可以在github下载,这里提供百度网盘下载
解压后可以看到如下示例脚本
unzip Pyverilog-develop.zip cd Pyverilog-develop/examples ll total 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的制约,方便后续处理。
每行脚本分析如下所示:
# 这行代码是使用绝对导入的未来语法。在Python 2.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类,用于解析命令行选项和参数。 from optparse import OptionParser # the next line can be removed after installation sys.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 preprocess def 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_import from __future__ import print_function import sys import os from optparse import OptionParser # the next line can be removed after installation sys.path.insert(0, os.path.dirname(os.path.dirname(os.path.abspath(__file__)))) import pyverilog from pyverilog.vparser.parser import parse def 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_import from __future__ import print_function import sys import os # the next line can be removed after installation sys.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 vast from pyverilog.ast_code_generator.codegen import ASTCodeGenerator def 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 end endmodule
话说通过ATS生成verilog确实很繁琐,还不如直接上手写个veirlog。
本节介绍结束。
审核编辑:汤梓红
评论
查看更多