読者です 読者をやめる 読者になる 読者になる

shtaxxx日記

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

Veriloggen: PythonでVerilog HDLのソースコードを組み立てるためのライブラリ

海外出張の帰りの飛行機の中でちょっと暇だったので,Verilog HDLのソースコードPythonで組み立てるためのライブラリを作りました.Python2.x, 3.x両対応です.

github.com

pypi.python.org

PyCoRAMなどの高位合成(動作合成)系のようにPythonソースコードVerilog HDLに変換するのではなく,PythonVerilog 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

インデントが揃っていないので,emacsvimなどのテキストエディタで自動で修正してください.

シミュレーションをしてみる

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文字も書いていないのに,ハードウェアのシミュレーションができました!

f:id:sxhxtxa:20150831184600p:plain

まとめ

PythonVerilog HDLのASTを組み立てるためのライブラリVeriloggenを公開しました.Verilog HDLとしての文法チェック機能や,VPIや高位IP設計フレームワークのPyCoRAMとの連携,新たな抽象化レイヤーの追加など,課題は沢山ありますが,追々対応していきます.

追記

括弧が多くて対応関係が分かりづらいですので,括弧に色を付けるのがおすすめです. Emacsの人はこちらを参考に,rainbow-delimitersを導入すると良いと思います.