PythonとVeriloggenで既存のVerilogモジュールを読み込んで改造する
前回に引き続きVeriloggenの話.今回は, read_verilog_module(), read_verilog_module_str() を使って,Verilog HDLで書かれた既存のハードウェア構成を取り込んで,更に改変する方法についてまとめます.
VeriloggenはPythonでVerilog HDLのソースコードを組み立てることができるライブラリです. 今回の例はGitHubのここにあります.
既存のVerilog HDLのソースコードをインポートする
from veriloggen import * led_v = '''\ 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 ''' modules = read_verilog_module_str(led_v) m = modules['blinkled']
read_verilog_module_str(code)を使うと,テキスト形式のVerilog HDLのソースコードをVeriloggenの内部形式(Module)に変換して取り込むことができる. 辞書形式でモジュール一覧が返ってくるので,モジュール名をキーにしてモジュールに参照できる.
modules = read_verilog_module('led.v') m = modules['blinkled']
既存のVerilog HDLのファイル(.v)を取り込むにはread_verilog_module(*filename)を使う.返り値はテキストの場合と同様.
インポートしたVerilogモジュールを改造する
もちろんインポートしたモジュールをそのまま利用して,インスタンスを作成したりできる.Veriloggenではさらに,インポートしたモジュールを元に,拡張や変更がPythonのモジュールをいじくり回すだけでできる.
def mkLed(): modules = read_verilog_module_str(led_v) m = modules['blinkled'] # change the module name m.name = 'modified_led' # add new statements enable = m.Input('enable') busy = m.Output('busy') old_statement = m.always[0].statement[0].false_statement m.always[0].statement[0].false_statement = If(enable)(*old_statement) m.Assign( busy(m.variable['count'] < 1023) ) return m
この例ではまず,取り込んだblinkledモジュールの名前を'modified_led'に変更している.これはテキストの置換でできるくらいに簡単.
Veriloggenならではの機能としては,Verilog HDLのソースコードに後から,ポートを追加したり,回路を追加したりできる点がある.例では,入力ポートの'enable'や出力ポートの'busy'を追加している.そしてalways文の定義を変えたり,assign文を追加したりしている.具体的には,上で示したblinkledの1つ目のalways文でcountをインクリメントしているのだが,その条件にenableを追加している.加えて,countの値を使ってbusyを定義するassign文を追加している.
インポートした時点でModuleオブジェクトはユーザーがPythonで組み立てたModuleと全く同じなため,一から組み立てるのと同じように,m.Reg()やm.Always()などで信号や回路を後から自由に追加できる.簡単♪
Veriloggenのソースコード
import sys import os import collections from veriloggen import * led_v = '''\ 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 ''' def mkLed(): modules = read_verilog_module_str(led_v) m = modules['blinkled'] # change the module name m.name = 'modified_led' # add new statements enable = m.Input('enable') busy = m.Output('busy') old_statement = m.always[0].statement[0].false_statement m.always[0].statement[0].false_statement = If(enable)(*old_statement) m.Assign( busy(m.variable['count'] < 1023) ) return m if __name__ == '__main__': led_module = mkLed() led_code = led_module.to_verilog() print(led_code)
出力されるVerilog HDLのソースコード
module modified_led # ( parameter WIDTH = 8 ) ( input CLK, input RST, output reg [(((WIDTH - 1) + 1) - 1):0] LED, input enable, output busy ); reg [(((32 - 1) + 1) - 1):0] count; always @(posedge CLK) begin if(RST) begin count <= 0; end else if(enable) 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 assign busy = (count < 1023); endmodule
入力Verilogコードと比べて,if(enable) という条件やassign busy = ... という定義が追加されているのがわかる.
何が嬉しいのか?
基本となる回路をVerilog HDLを書いておいて,それを特定のルールでチューニングしたりするのが自動できるようになります.あとはある回路をプロトタイピング用の小さなRTLに自動で変換したり,自作の高位合成処理系のコード生成器の一部として使ったりもできると思います.是非お試しください.