shtaxxx日記

コンピュータアーキテクチャについて研究している研究者の日記や技術紹介

Verilog HDLのデザイン解析・コード生成のためのPythonベースのオープンソースツールキットPyverilogをリリースしました

Pyverilogは,ハードウェア記述言語Verilog HDLで記述されたハードウェアデザインの解析とコード生成を行うための,Python実装のツールキットです.

構成

Pyverilogは4つのツールで構成されています.

  • 構文解析器 (vparser)
  • データフロー解析器 (dataflow)
  • コントロールフロー解析器 (controlflow)
  • コード生成器 (ast_code_generator)

すべてのコードはPythonで記述されており,全体で12,000行程度とコンパクトなツールに仕上がっています.

構文解析

構文解析器は,最もポピュラーなコンパイラコンパイラLex-YaccPython実装のPLY (Python Lex-Yacc)を用いて実装されています.
サポートする構文はVerilog-2005の殆どで,generate文などにも対応します.ただし,defparam等などいくつか未対応の構文が存在します.
プリプロセッサにはオープンソースVerilogシミュレータのIcarus Verilogを利用しています.
構文解析器は最後にAST (Abstract Syntax Tree)を出力します.このASTを用いてデータフロー解析やコントロールフロー解析などを行います.

データフロー解析器

データフロー解析器は,構文解析の結果を基に,各信号を定義するグラフを作成します.

解析は

  1. モジュール定義解析 (modulevisitor)
  2. 信号宣言解析 (signalvisitor)
  3. 信号定義解析 (bindvisitor)

の3-passで行われます.

これにより,Icarus Verilogなどと同様に,信号宣言と定義の位置に関わらずコードを解析することができます.
初期段階では,各reg/wire変数に対する代入関係を解析します.この結果を元に,wire変数への定義を辿っていき,reg変数のみの集合で定義される代入関係のグラフを作成することができます.
この結果を用いてさらに,各変数の取り得る値を推論することなどが可能になります.

データフロー解析途中の値を外部で利用することが可能であるため,構文解析器を拡張し,Veirlog HDLソースコードの静的変換を行うことが可能です.
実際に,PyCoRAM (PythonからVerilogへの高位合成とメモリシステムの抽象化によるAMBA AXI4 IPコア合成フレームワーク)では,制御用信号の挿入にこれらの機能が利用されています.

また,データフロー解析の結果をそのままVerilog HDLのコードとして,Graphvizを用いての画像としての出力がそれぞれ可能です.

コントロールフロー解析器

コントロールフロー解析器は,データフロー解析の結果を利用して,有限ステートマシン(FSM: Finite State Machine)の推定と,各信号のアサートされる条件などを解析します.
これより,ある信号がアサートされる条件を推論し,その先読み回路を自動生成する,などといったことが可能になります.
応用例としてはキャッシュメモリベースのFPGAアクセラレータにおけるプリフェッチャーの自動合成などが挙げられます.
ステートマシンの推定結果は,データフロー解析と同様にGraphvizにより画像として出力可能です.

コード生成器

コード生成器は,抽象構文木からVerilog HDLのソースコードを出力します.
生成には,テキストテンプレートエンジンのJinja2を利用しています.
Pythonのコードに直接Verilogのコードを埋め込まないため,実装がシンプルになりました.加えて,コード生成が高速です.

ソフトウェア条件

  • Python 2.7 or 3.3 (or later)
    • Graphvizを利用するには2.7系が必要.そうでなければ3.3系を推奨
  • GraphvizとPygraphviz
    • データフローグラフやコントロールフローグラフの画像ファイルを出力するために利用します.必要なければスキップ可能.Python2.7系が必要.
    • Graphvizは apt-get install graphviz などでインストール
    • Pygraphvizはpip install pygraphvizなどでインストール
  • Icarus Verilog (0.9.6 or later)
  • Jinja2 (2.7 or later)
    • pip install jinja2などでインストール

使ってみる

試しに,簡単な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が定義されていることがわかります.

f:id:sxhxtxa:20140101045641p:plain


次は,コントロールフロー解析をしてみましょう.以下のコマンドを入力しましょう.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)が同時に出力されています.

f:id:sxhxtxa:20140101045835p:plain


最後に,抽象構文木から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

最後に

Pyverilogを用いると,Verilog HDLで記述されたハードウェアデザインの解析が簡単に行えます.
FPGAを用いたアクセラレータが最近注目されていますが,より効率的なアクセラレータのデザインを自動合成したりしたりと,いろいろと静的解析で出来ることがあると思います.

是非使ってみてください!バグレポートや感想等をお待ちしています.