嵌入式技术
上节说到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。
本节介绍结束。
审核编辑:汤梓红
全部0条评论
快来发表一下你的评论吧 !