Pyverilogは,ハードウェア記述言語Verilog HDLで記述されたハードウェアデザインの解析とコード生成を行うための,Python実装のツールキットです.
構成
Pyverilogは4つのツールで構成されています.
- 構文解析器 (vparser)
- データフロー解析器 (dataflow)
- コントロールフロー解析器 (controlflow)
- コード生成器 (ast_code_generator)
すべてのコードはPythonで記述されており,全体で12,000行程度とコンパクトなツールに仕上がっています.
構文解析器
構文解析器は,最もポピュラーなコンパイラコンパイラLex-YaccのPython実装のPLY (Python Lex-Yacc)を用いて実装されています.
サポートする構文はVerilog-2005の殆どで,generate文などにも対応します.ただし,defparam等などいくつか未対応の構文が存在します.
プリプロセッサにはオープンソースなVerilogシミュレータのIcarus Verilogを利用しています.
構文解析器は最後にAST (Abstract Syntax Tree)を出力します.このASTを用いてデータフロー解析やコントロールフロー解析などを行います.
データフロー解析器
データフロー解析器は,構文解析の結果を基に,各信号を定義するグラフを作成します.
解析は
- モジュール定義解析 (modulevisitor)
- 信号宣言解析 (signalvisitor)
- 信号定義解析 (bindvisitor)
の3-passで行われます.
これにより,Icarus Verilogなどと同様に,信号宣言と定義の位置に関わらずコードを解析することができます.
初期段階では,各reg/wire変数に対する代入関係を解析します.この結果を元に,wire変数への定義を辿っていき,reg変数のみの集合で定義される代入関係のグラフを作成することができます.
この結果を用いてさらに,各変数の取り得る値を推論することなどが可能になります.
データフロー解析途中の値を外部で利用することが可能であるため,構文解析器を拡張し,Veirlog HDLソースコードの静的変換を行うことが可能です.
実際に,PyCoRAM (PythonからVerilogへの高位合成とメモリシステムの抽象化によるAMBA AXI4 IPコア合成フレームワーク)では,制御用信号の挿入にこれらの機能が利用されています.
また,データフロー解析の結果をそのままVerilog HDLのコードとして,Graphvizを用いての画像としての出力がそれぞれ可能です.
ソフトウェア条件
使ってみる
試しに,簡単なVerilog HDLのコードを解析してみましょう.
以下のようなVerilog HDLのコードを用意します.ファイル名はtest.vとしましょう.
これは,enableが外部からアサートされたあと,内部でその値を加算し,一部をLEDに出力する回路です.
module top ( input CLK, input RST, input enable, input [31:0] value, output [7:0] led ); reg [31:0] count; reg [7:0] state; assign led = count[23:16]; always @(posedge CLK) begin if(RST) begin count <= 0; state <= 0; end else begin if(state == 0) begin if(enable) state <= 1; end else if(state == 1) begin state <= 2; end else if(state == 2) begin count <= count + value; state <= 0; end end end endmodule
まず,構文解析をしてみましょう.次のコマンドを入力しましょう.
python3.3 pyverilog/vparser/parser.py test.v
そうすると,以下のような結果が得られるはずです.上記のコードを構文解析した結果の抽象構文木が表示されています.
Source: Description: ModuleDef: top Paramlist: Portlist: Ioport: Input: CLK, False Width: IntConst: 0 IntConst: 0 Ioport: Input: RST, False Width: IntConst: 0 IntConst: 0 Ioport: Input: enable, False Width: IntConst: 0 IntConst: 0 Ioport: Input: value, False Width: IntConst: 31 IntConst: 0 Ioport: Output: led, False Width: IntConst: 7 IntConst: 0 Decl: Reg: count, False Width: IntConst: 31 IntConst: 0 Decl: Reg: state, False Width: IntConst: 7 IntConst: 0 Assign: Lvalue: Identifier: led Rvalue: Partselect: Identifier: count IntConst: 23 IntConst: 16 Always: SensList: Sens: posedge Identifier: CLK Block: None IfStatement: Identifier: RST Block: None NonblockingSubstitution: Lvalue: Identifier: count Rvalue: IntConst: 0 NonblockingSubstitution: Lvalue: Identifier: state Rvalue: IntConst: 0 Block: None IfStatement: Eq: Identifier: state IntConst: 0 Block: None IfStatement: Identifier: enable NonblockingSubstitution: Lvalue: Identifier: state Rvalue: IntConst: 1 IfStatement: Eq: Identifier: state IntConst: 1 Block: None NonblockingSubstitution: Lvalue: Identifier: state Rvalue: IntConst: 2 IfStatement: Eq: Identifier: state IntConst: 2 Block: None NonblockingSubstitution: Lvalue: Identifier: count Rvalue: Plus: Identifier: count Identifier: value NonblockingSubstitution: Lvalue: Identifier: state Rvalue: IntConst: 0
次に,データフロー解析解析をしてみましょう.以下のコマンドを入力しましょう.
python3.3 pyverilog/dataflow/dataflow_analyzer.py -t top test.v
そうすると,以下のような結果が得られるはずです.上記のコードで定義されている変数と値の代入関係が表示されています.
Directive: Instance: (top, 'top') Term: (Term name:top.led type:{'Output'} msb:(IntConst 7) lsb:(IntConst 0)) (Term name:top.enable type:{'Input'} msb:(IntConst 0) lsb:(IntConst 0)) (Term name:top.CLK type:{'Input'} msb:(IntConst 0) lsb:(IntConst 0)) (Term name:top.count type:{'Reg'} msb:(IntConst 31) lsb:(IntConst 0)) (Term name:top.state type:{'Reg'} msb:(IntConst 7) lsb:(IntConst 0)) (Term name:top.RST type:{'Input'} msb:(IntConst 0) lsb:(IntConst 0)) (Term name:top.value type:{'Input'} msb:(IntConst 31) lsb:(IntConst 0)) Bind: (Bind dest:top.count tree:(Branch Cond:(Terminal top.RST) True:(IntConst 0) False:(Branch Cond:(Operator Eq Next:(Terminal top.state),(IntConst 0)) False:(Branch Cond:(Operator Eq Next:(Terminal top.state),(IntConst 1)) False:(Branch Cond:(Operator Eq Next:(Terminal top.state),(IntConst 2)) True:(Operator Plus Next:(Terminal top.count),(Terminal top.value))))))) (Bind dest:top.state tree:(Branch Cond:(Terminal top.RST) True:(IntConst 0) False:(Branch Cond:(Operator Eq Next:(Terminal top.state),(IntConst 0)) True:(Branch Cond:(Terminal top.enable) True:(IntConst 1)) False:(Branch Cond:(Operator Eq Next:(Terminal top.state),(IntConst 1)) True:(IntConst 2) False:(Branch Cond:(Operator Eq Next:(Terminal top.state),(IntConst 2)) True:(IntConst 0)))))) (Bind dest:top.led tree:(Partselect Var:(Terminal top.count) MSB:(IntConst 23) LSB:(IntConst 16)))
さらに,データフロー解析の結果を画像として出力してみましょう.信号ledの定義を出力してみます.以下のコマンドを入力しましょう.
python3.3 pyverilog/dataflow/graphgen.py -t top -s top.led test.v
そうすると,以下のような画像ファイル(out.png)が出力されたはずです.信号countの23ビット目から16ビット目の切り出しとしてledが定義されていることがわかります.
次は,コントロールフロー解析をしてみましょう.以下のコマンドを入力しましょう.Pygraphvizを利用するためにPython2.7を使います.
python2.7 pyverilog/controlflow/controlflow_analyzer.py -t top test.v
そうすると,以下のような結果が得られるはずです.変数stateに対するステートマシンの形状と各状態のへの遷移条件が解析されています.
FSM signal: top.count, Condition list length: 4 FSM signal: top.state, Condition list length: 5 Condition: (Ulnot, Eq), Inferring transition condition Condition: (Eq, top.enable), Inferring transition condition Condition: (Ulnot, Ulnot, Eq), Inferring transition condition # SIGNAL NAME: top.state # DELAY CNT: 0 0 --(top_enable>'d0)--> 1 1 --None--> 2 2 --None--> 0 Loop (0, 1, 2)
以下のような,ステートマシンの画像ファイル(top_state.png)が同時に出力されています.
最後に,抽象構文木からVerilog HDLのコードを生成してみましょう.まず以下のコードを用意します.ファイル名はtest.pyとしましょう.
vparser.astで定義されているASTクラスを直接インスタンス化して,Verilog HDLのコードを表現しています.
import pyverilog.vparser.ast as vast from pyverilog.ast_code_generator.codegen import ASTCodeGenerator params = vast.Paramlist(()) 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) ) items = ( vast.Assign( vast.Identifier('led'), vast.IntConst('8') ) ,) ast = vast.ModuleDef("top", params, ports, items) codegen = ASTCodeGenerator() rslt = codegen.visit(ast) print(rslt)
次のコマンドを実行しましょう.pyverilogのディレクトリがあるのと同じ階層で実行しましょう.
python3.3 test.py
そうすると以下のような結果が出力されたはずです.
module top ( input [0:0] CLK, input [0:0] RST, output [7:0] led ); assign led = 8; endmodule