Veriloggen: PythonでVerilog HDLのソースコードを組み立てるためのライブラリ
海外出張の帰りの飛行機の中でちょっと暇だったので,Verilog HDLのソースコードをPythonで組み立てるためのライブラリを作りました.Python2.x, 3.x両対応です.
PyCoRAMなどの高位合成(動作合成)系のようにPythonのソースコードをVerilog HDLに変換するのではなく,PythonでVerilog HDLの抽象構文木(AST)を組み立てるためのライブラリです.
Veriloggenの名前は,PyCUDAなどで使われている,C/C++のソースコードをPythonで組み立てるライブラリのcgenに倣いました.
そのため,通常のRTL設計と同様に,プログラマが明示的にクロックサイクルレベルで回路の振る舞いを定義します. 同様のライブラリとしては,MyHDLがあります.MyHDLとの大きな違いは,Veriloggenは(現時点では)Pythonのastモジュールを使わずに,階層化されたオブジェクトとしてVerilog HDLのASTを直接組み立てる点です.
MyHDLでは,Pythonの文法のサブセットを用いてalways文風の定義を行うことができますが,その関数のソースコードはPythonのastモジュールを用いて解析されています.そのため,Pythonの処理系が本来持つすべての機能を利用することはできません.
一方で,Veriloggenは,Verilog HDLのASTの軽量な抽象化に留めることで,ASTの組み立てにPythonの処理系が本来持つすべての機能を利用することができます.もちろん,このままでは記述があまり楽ではないので,Veriloggenの上に更なる抽象化の階層を,別のライブラリとして実装するのが良いかもしれません.
VeriloggenとPyverilogのインストール
VeriloggenはPyverilogのASTのラッパーとして実装されています.そのためPyverilogとjinja2をインストールする必要があります.
Virtualenvの設定 (スキップ可能)
ここではせっかくなので,Pythonの仮想化環境であるvirtualenvを使って,新たにPyverilog, jinja2, Veriloggenをインストールしましょう.
virtualenvがシステムにインストールされていない場合には,pipコマンドを使ってインストールしましょう.
pip install virtualenv
仮想環境として新しいディレクトリを作り,virtualenvでPythonの環境を構築します.
mkdir veriloggen-test virtualenv veriloggen-test
まず,仮想環境に切り替えます.
cd veriloggen-test source bin/activate
Pyverilog, Jinja2, Veriloggenのインストール
ソースコード用ディレクトリを作成し,PyverilogとVeriloggenをcloneしましょう.
mkdir src cd src git clone https://github.com/shtaxxx/Pyverilog git clone https://github.com/shtaxxx/veriloggen
インストールします.
pip install jinja2 cd Pyverilog python setup.py install cd ../veriloggen python setup.py install cd ../
最新版はGitHub上にありますが,PyPIから安定版(?)をインストールすることもできます.
pip install pyverilog pip install veriloggen
LEDチカチカ回路を書いてみる
Veriloggenを使ってハードウェアを書いてみましょう.CLK, RST, LEDの3つのポートを持つモジュールを定義します. "led.py"という名前で以下のソースコードを書きましょう.
なんとなくVerilog HDLに似ている気がしませんか?
import sys import os from veriloggen import * def mkLed(): m = Module('blinkled') width = m.Parameter('WIDTH', 8) clk = m.Input('CLK') rst = m.Input('RST') led = m.OutputReg('LED', width) count = m.Reg('count', 32) m.Always(Posedge(clk))( If(rst)( count(0) ).Else( If(count == 1023)( count(0) ).Else( count(count + 1) ) )) m.Always(Posedge(clk))( If(rst)( led(0) ).Else( If(count == 1024 - 1)( led(led + 1) ) )) return m if __name__ == '__main__': led = mkLed() # led.to_verilog(filename='tmp.v') verilog = led.to_verilog() print(verilog)
実行してみましょう.MyHDLやPyCoRAMとは異なり,コードの実行によりはじめて,Verilog HDLのコードが生成されます.
python led.py
そうすると,以下の様なVerilog HDLのソースコードが出力されるはずです.
module blinkled # ( parameter WIDTH = 8 ) ( input CLK, input RST, output reg [(WIDTH - 1):0] LED ); reg [(32 - 1):0] count; always @(posedge CLK) begin if(RST) begin count <= 0; end else begin if((count == 1023)) begin count <= 0; end else begin count <= (count + 1); end end end always @(posedge CLK) begin if(RST) begin LED <= 0; end else begin if((count == 1023)) begin LED <= (LED + 1); end end end endmodule
インデントが揃っていないので,emacsやvimなどのテキストエディタで自動で修正してください.
シミュレーションをしてみる
Veriloggenは合成対象のVerilog HDLのソースコードだけではなく,シミュレーション用のソースコード(テストベンチ)の組み立てにも対応しています.
以下のソースコードはテストベントを含む場合のものです.
import sys import os from veriloggen import * def mkLed(): m = Module('blinkled') width = m.Parameter('WIDTH', 8) clk = m.Input('CLK') rst = m.Input('RST') led = m.OutputReg('LED', width) count = m.Reg('count', 32) m.Always(Posedge(clk))( If(rst)( count(0) ).Else( If(count == 1023)( count(0) ).Else( count(count + 1) ) )) m.Always(Posedge(clk))( If(rst)( led(0) ).Else( If(count == 1024 - 1)( led(led + 1) ) )) return m def mkTest(): m = Module('test') # target instance led = mkLed() # copy paras and ports params = m.copy_params(led) ports = m.copy_sim_ports(led) clk = ports['CLK'] rst = ports['RST'] uut = m.Instance(led, 'uut', params=m.connect_params(led), ports=m.connect_ports(led)) lib.simulation.setup_waveform(m, uut, m.get_vars()) lib.simulation.setup_clock(m, clk, hperiod=5) init = lib.simulation.setup_reset(m, rst, m.reset(), period=100) init.add( Delay(1000 * 100), Systask('finish'), ) return m if __name__ == '__main__': test = mkTest() verilog = test.to_verilog('tmp.v') print(verilog)
再度実行してみましょう.今度はテストベンチも一緒に生成されます.
python led.py
生成されたVerilog HDLのソースコードをシミュレーションしてみましょう.GTKwaveを使って波形を見てみましょう.
iverilog -Wall tmp.v ./a.out gtkwave uut.vcd &
こんな感じで回路の動作が波形で見えるはずです.Verilog HDLのソースコードを1文字も書いていないのに,ハードウェアのシミュレーションができました!
まとめ
PythonでVerilog HDLのASTを組み立てるためのライブラリVeriloggenを公開しました.Verilog HDLとしての文法チェック機能や,VPIや高位IP設計フレームワークのPyCoRAMとの連携,新たな抽象化レイヤーの追加など,課題は沢山ありますが,追々対応していきます.
追記
括弧が多くて対応関係が分かりづらいですので,括弧に色を付けるのがおすすめです. Emacsの人はこちらを参考に,rainbow-delimitersを導入すると良いと思います.