shtaxxx日記

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

PythonとVeriloggenで既存のVerilogモジュールを読み込んで改造する

前回に引き続きVeriloggenの話.今回は, read_verilog_module(), read_verilog_module_str() を使って,Verilog HDLで書かれた既存のハードウェア構成を取り込んで,更に改変する方法についてまとめます.

VeriloggenはPythonVerilog HDLのソースコードを組み立てることができるライブラリです. 今回の例はGitHubここにあります.

github.com

既存の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に自動で変換したり,自作の高位合成処理系のコード生成器の一部として使ったりもできると思います.是非お試しください.