pyverilog脚本的使用

嵌入式技术

1361人已加入

描述

上节说到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。

本节介绍结束。

  审核编辑:汤梓红

打开APP阅读更多精彩内容
声明:本文内容及配图由入驻作者撰写或者入驻合作网站授权转载。文章观点仅代表作者本人,不代表电子发烧友网立场。文章及其配图仅供工程师学习之用,如有内容侵权或者其他违规问题,请联系本站处理。 举报投诉

全部0条评论

快来发表一下你的评论吧 !

×
20
完善资料,
赚取积分