Veriloggenをいろいろ更新し,Version 0.4.3をリリースしました. github.com
同時にPyverilogも1.0.1をリリースしました.テスト周りを補強しリファクタリングを行った安定版です. github.com
準備: PyverilogとVeriloggenのインストール
安定版のインストールなら,pipでのインストールが簡単でおすすめです. メインの開発環境が汚れるのが心配な方はvirtualenvを使いましょう. virtualenvを使う場合には,以下の手順でインストールできます.
Python以外のソフトウェアとしてIcarus verilog (iverilog) が必要です. 例えばUbuntuなら,
sudo apt-get install iverilog
とすればイントールできるでしょう. Pythonのパッケージでは,Jinja2が別途必要ですが,Veriloggen/Pyverilogのインストールと同時に自動的にインストールされるはずです. また,テストにはpytestとpytest-pythonが必要なので,一緒にインストールしましょう.
mkdir testdir cd testdir virtualenv --python=python3 . source bin/activate pip install pyverilog pip install veriloggen pip pytest pytest-pythonpath
1: Veriloggenを使ってPython上でハードウェアを書いてみる
今回はまず,HDLにおけるHello, world!として,LEDをチカチカ光らせる回路を作りましょう. 適当なエディタで新しいファイル(led.pyなど)を作り,以下のコードを書きましょう. 順序回路を管理するライブラリSeqを利用して,一定周期毎にLEDの値がインクリメントされる回路を作っています. シミュレーション用にdisplay文(Cでいうprintf)を挿入しています.
import sys import os from veriloggen import * def mkLed(): m = Module('blinkled') interval = m.Parameter('INTERVAL', 16) clk = m.Input('CLK') rst = m.Input('RST') led = m.OutputReg('LED', 8, initval=0) count = m.Reg('count', 32, initval=0) seq = lib.Seq(m, 'seq') seq.add( Systask('display', 'LED:%d count:%d', led, count) ) seq.add( count(count + 1), cond=count<interval-1 ) seq.add( count(0), cond=count==interval-1 ) seq.add( led(led + 1), cond=count==interval-1 ) seq.make_always(clk, rst) return m
2. シミュレーション用のコード(テストベンチ)を書いてみる
上記のコードだけではシミュレーションができないので,シミュレーション用のコードを書きましょう. 上記と同じファイルに追記しましょう.
m.copy_params()とm.copy_sim_ports()で,パラメータ定義・ポート定義をblinkledモジュールからコピーしています. これで面倒なポート生成を自動化できます.
lib.simulation.setup_waveform()で波形生成の設定,lib.simulation.setup_clock()でクロック信号の生成,lib.simulation.setup_reset()でリセット信号の設定をそれぞれ行っています.
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) lib.simulation.setup_clock(m, clk, hperiod=5) init = lib.simulation.setup_reset(m, rst, m.make_reset(), period=100) init.add( Delay(1000), Systask('finish'), ) return m
3. main部を書いて実行してみる
最後に,上記で定義したメソッドを使ってハードウェアとテストベンチを生成しましょう.そして,シミュレーションをしてみましょう.
まず,上記のファイルに以下のコードを追加しましょう.以下のコードはそのスクリプトが主体となって起動される場合のみに実行されます.
if __name__ == '__main__': test = mkTest() verilog = test.to_verilog() print(verilog) sim = lib.simulation.Simulator(test) rslt = sim.run() print(rslt)
mkTest()でテストベンチを作成しています.mkTest()内では,mkLed()を呼び出しLEDハードウェアを生成しています.ハードウェアの再帰構造は自動的に解析されますので,mkTest()でテストベンチを作成するだけで,LEDハードウェアも生成されます.
次にlib.simulation.Simulator()でシミュレータの設定を作成します.シミュレーション対象のオブジェクトを引数で渡します. そして,sim.run()でシミュレータを起動して,RTLシミュレーションを実行します. 現在の実装ではIcarus Verilogのみに対応しています.
python3 led.py
すると,display文によってLEDの値とcountの値が表示されています.
LED: x count: x ... LED: x count: x LED: 0 count: 0 LED: 0 count: 1 LED: 0 count: 2 LED: 0 count: 3 LED: 0 count: 4 LED: 0 count: 5 LED: 0 count: 6 LED: 0 count: 7 LED: 0 count: 8 LED: 0 count: 9 LED: 0 count: 10 LED: 0 count: 11 LED: 0 count: 12 LED: 0 count: 13 LED: 0 count: 14 LED: 0 count: 15 LED: 1 count: 0 LED: 1 count: 1 LED: 1 count: 2 LED: 1 count: 3 ...
シミュレーションの実体は裏でiverilogを起動しているだけなのですが,別に起動する必要がなくて手間が減りました. また実行結果をstr形式で取得できるので,ハードウェア構成のテストの自動化も簡単になりました. 今後はcocotbなどと連携して,Pythonそのものでシミュレータの制御をできるようにもしたいですね.
おまけ 波形を見る
システムにGTKwaveがインストールされていれば,シミュレーション結果を波形で観測することができます. 実行スクリプトに"sim.view_waveform()"の一行を追加しましょう.
if __name__ == '__main__': test = mkTest() verilog = test.to_verilog() print(verilog) sim = lib.simulation.Simulator(test) rslt = sim.run() print(rslt) sim.view_waveform()
そしてもう一度実行してみましょう.
python3 led.py
GTKwaveが起動し波形が観測できます.
まとめ
Veriloggenを使うと,Pythonだけでハードウェアモデリングとシミュレーションができます.
VeriloggenはHDLの構文木オブジェクトをどうするかをPythonの機能を使って定義します.一方で,同じPythonをベースとしたRTL設計ツールのMyHDLやPyMTLは,メソッド定義などのPythonの構文木を,自前のパーサーでHDLに変換します.Pythonの文法で直接RTLモデリングをしたいだけであれば,後者の方がより簡単です.
Veriloggenはソースコードそのものでハードウェアの振る舞いを定義するためのツールではなく,「どのようにRTLを組み立てるか」のルールを書くためのツールです.そのため,いろいろな構成のハードウェアをパラメータを変えて自動生成したり,高位合成系のバックエンドとして使う,などの,メタプログラミング的な使い方に適しています.