上節(jié)說到pyverilog有很多示例腳本,本節(jié)開始逐個分析。
1?示例腳本下載及說明
可以在github下載,這里提供百度網(wǎng)盤下載
?
解壓后可以看到如下示例腳本
?
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的制約,方便后續(xù)處理。
每行腳本分析如下所示:
?
# 這行代碼是使用絕對導入的未來語法。在Python 2.x 版本中,導入模塊時,如果模塊與當前腳本的名稱沖突,Python會優(yōu)先導入當前腳本。使用from __future__ import absolute_import可以確保導入模塊時,不會優(yōu)先導入當前腳本。 from __future__ import absolute_import #?這行代碼是使用print()函數(shù)的未來語法。在Python 2.x 版本中,print是一個關鍵字而不是函數(shù),不需要使用括號。使用from __future__ import print_function可以讓Python 2.x 版本中的print行為與Python 3.x 版本中的print()函數(shù)一致。 from __future__ import print_function # 這行代碼導入了Python標準庫中的sys模塊,用于訪問與Python解釋器相關的變量和函數(shù)。 import sys # 這行代碼導入了Python標準庫中的os模塊,用于與操作系統(tǒng)進行交互,例如文件和目錄操作。 import os # 這行代碼導入了Python標準庫中的optparse模塊中的OptionParser類,用于解析命令行選項和參數(shù)。 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中一個內(nèi)置變量,表示當前腳本的文件名。os.path.abspath()函數(shù)用于獲取當前腳本的絕對路徑。例如,如果腳本文件位于/home/user/example.py,那么os.path.abspath(__file__)將返回/home/user/example.py # os.path.dirname():os.path.dirname()函數(shù)用于獲取路徑中的目錄部分。將上一步得到的絕對路徑傳遞給os.path.dirname()函數(shù),將返回/home/user,即父目錄的路徑 # os.path.dirname(os.path.dirname(os.path.abspath(__file__)))):通過多次調用os.path.dirname()函數(shù),可以獲取到腳本所在文件的父目錄的父目錄 # sys.path.insert(0, ...):sys.path是Python中的一個列表,包含了Python模塊搜索路徑。sys.path.insert(0, ...)將指定的路徑插入到列表的第一個位置,即將腳本所在文件的父目錄添加到Python模塊搜索路徑的最前面 # 這行代碼導入了Pyverilog庫,它是一個用于解析和處理Verilog代碼的Python庫。 import pyverilog # 這行代碼從Pyverilog庫的vparser.preprocessor模塊中導入了preprocess函數(shù)。preprocess函數(shù)用于對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子函數(shù),打印信息 def showVersion(): print(INFO) print(VERSION) print(USAGE) sys.exit() # optparser.add_option()方法用于添加選項。每個選項都是一個參數(shù)的配置,包括名稱、選項類型、目標變量、默認值和幫助信息等。具體參數(shù)解釋如下: # -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()方法用于解析命令行參數(shù),并將解析結果賦值給options和args變量。options是一個對象,包含了解析后的選項和參數(shù)的值;args是一個列表,包含了解析后的位置參數(shù)的值。 (options, args) = optparser.parse_args() filelist = args if options.showversion: showVersion() for f in filelist: if not os.path.exists(f): ????# os.path.exists()函數(shù)判斷文件是否存在。如果文件不存在,則拋出一個IOError異常,并將異常消息設為"file not found: " + f,其中f是文件路徑 raise IOError("file not found: " + f) ????#?如果filist為空,則輸出自定義子函數(shù) if len(filelist) == 0: showVersion() # preprocess()函數(shù)是在pyverilog.vparser.preprocessor模塊中定義的,用于對Verilog文件進行預處理。它接受三個參數(shù): ????# filelist:一個包含文件路徑的列表,表示需要進行預處理的文件。 # include:一個包含包含路徑的列表,用于指定預處理時的包含路徑。 # define:一個包含宏定義的列表,用于指定預處理時的宏定義。 # 該函數(shù)返回預處理后的文本,將其存儲在text變量中。 text = preprocess(filelist, include=options.include, define=options.define) print(text) # __name__是一個特殊的內(nèi)置變量,表示當前模塊的名稱。當一個Python腳本直接被運行時,__name__的值為'__main__';當一個Python模塊被導入時,__name__的值為模塊的名稱。 # 因此,if __name__?==?'__main__':這個條件判斷語句的作用是,只有當當前腳本直接被運行時,才會執(zhí)行main()函數(shù)。 #?這樣做的好處是,可以在腳本中定義一些測試代碼或者執(zhí)行一些初始化操作,而這些代碼在腳本被導入時不會執(zhí)行。只有當腳本直接被運行時,才會執(zhí)行這些代碼。 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()函數(shù)是在pyverilog.vparser.parser模塊中定義的,用于解析Verilog文件。它接受三個參數(shù): # filelist:一個包含文件路徑的列表,表示需要進行解析的文件。 # preprocess_include:一個包含包含路徑的列表,用于指定預處理時的包含路徑。 # preprocess_define:一個包含宏定義的列表,用于指定預處理時的宏定義。 # 該函數(shù)返回解析后的抽象語法樹(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是一個包含行號和指令的元組列表。通過循環(huán)遍歷directives,可以逐行打印出每個指令的行號和內(nèi)容。 # 這段代碼的作用是先展示解析后的抽象語法樹的結構,然后逐行打印出每個指令的行號和內(nèi)容。這樣可以更好地了解解析后的結果,并進行后續(xù)的處理或分析。 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抽象語法樹的各種類和函數(shù)。 # 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。
本節(jié)介紹結束。
審核編輯:湯梓紅
評論
查看更多