shtaxxx日記

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

VeriloggenとPythonでハードウェアのRTLシミュレーションをする

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 install pytest pytest-pythonpath

1: Veriloggenを使ってPython上でハードウェアを書いてみる

サンプルのコードがexamplestestsにあります.

今回はまず,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のみに対応しています.

では,作成したPythonスクリプトを実行してみましょう.

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が起動し波形が観測できます.

f:id:sxhxtxa:20151104003228p:plain

まとめ

Veriloggenを使うと,Pythonだけでハードウェアモデリングとシミュレーションができます.

VeriloggenはHDLの構文木オブジェクトをどうするかをPythonの機能を使って定義します.一方で,同じPythonをベースとしたRTL設計ツールのMyHDLやPyMTLは,メソッド定義などのPython構文木を,自前のパーサーでHDLに変換します.Pythonの文法で直接RTLモデリングをしたいだけであれば,後者の方がより簡単です.

Veriloggenはソースコードそのものでハードウェアの振る舞いを定義するためのツールではなく,「どのようにRTLを組み立てるか」のルールを書くためのツールです.そのため,いろいろな構成のハードウェアをパラメータを変えて自動生成したり,高位合成系のバックエンドとして使う,などの,メタプログラミング的な使い方に適しています.