Veriloggenって何だろう(3)Always

Verilog HDL を生成するための記述を python で行うことで FPGA 回路の生成を楽にする為のライブラリー Veriloggen を使ってみたいと思います。
前回は led.py を動かして、Always 文の変換を試してみました。今回はモジュールの組み立て方と Always について確認します。

モジュールの組み立て方

 Python のエディター Spyder ではコードの自動補完が行われますが

from veriloggen import *

の書き方では機能しないため

import veriloggen as vg

として使用した方が間違いが少ないと思います。
キー入力が多くなってしまうので好き嫌いだと思いますが、python の記述をしているのか verilog の為の記述をしているのかを混乱しないためにも、その方が良いかなと思います。

 Veriloggen では、まずモジュールを作ってそれにいろいろと足しこんでいくことを行います。

    m = vg.Module('modulename')
    width = m.Parameter('WIDTH', 8)
    clk = m.Input('CLK')
    rst = m.Input('RST')
    led = m.OutputReg('LED', width)
    count = m.Reg('count', 32)

Verilog HDL に変換された時のモジュールの名前が 'modulename' になります。python 上でのこのモジュールのオブジェクト名は m です。
このオブジェクトに、これも Verilog HDL での名前 'WIDTH' で 8 なるパラメータを設定しています。

1bit の input 'CLK' と 'RST' を追加していますが、その次の OutputReg は ビット幅を指定して 'output reg' を追加しています。ここのビット幅には3行上の Verilog 用のオブジェクト width が指定されています。ですので、Verilog HDL に展開された時には width の名前の WIDTH が使われます。OutputReg の第2引数には、直接数字を書いてもいいですし、python の変数を書いても良いです。python の変数を書いた場合には計算結果が Verilog HDL に書き込まれることになります。例えば、扱いたい数値の最大が何ビットになるかを python で計算して書き込むようにしておけば、類似設計が簡単になるでしょう。
上記を verilog HDL に展開すると次のようになります。

module modulename #
(
  parameter WIDTH = 8
)
(
  input CLK,
  input RST,
  output reg [WIDTH-1:0] LED
);

Always の使い方

always @(posedge CLK) はVeriloggenのモジュール m に Always を足してみたいにして組み立てていきます。

    m.Always(vg.Posedge(clk))(
        vg.If(rst)(
            count(0)
        ).Else(
            vg.If(count == 1023)(
                count(0)
            ).Else(
                count(count + 1)
            )
        ))

これが次のように変換されます。

  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

負論理非同期リセットの書き方

サンプルではすべて 正論理 の同期リセット表記ですが、負論理の非同期リセットの場合には次のようになります。

def mkLed():
    m = vg.Module('blinkled')
    width = m.Parameter('WIDTH', 8)
    clk = m.Input('CLK')
    rst_n = m.Input('RST_n')
    led = m.OutputReg('LED', width)
    count = m.Reg('count', 32)

    m.Always(vg.Posedge(clk),vg.Negedge(rst_n))(
        vg.If(~rst_n)(
            count(0)
        ).Else(
            vg.If(count == 1023)(
                count(0)
            ).Else(
                count(count + 1)
            )
        ))

    m.Always(vg.Posedge(clk),vg.Negedge(rst_n))(
        vg.If(~rst_n)(
            led(0)
        ).Else(
            vg.If(count == 1024 - 1)(
                led(led + 1)
            )
        ))
    
    return m

リセットの論理を変えたので、テストパターンも次のように変更します。

def mkTest():
    m = vg.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_n = ports['RST_n']
    
    uut = m.Instance(led, 'uut',
                     params=m.connect_params(led),
                     ports=m.connect_ports(led))
    
    vg.simulation.setup_waveform(m, uut, m.get_vars())
    vg.simulation.setup_clock(m, clk, hperiod=5)
    init = vg.simulation.setup_reset(m, rst_n, m.make_reset(), 
                                     period=97, positive=False)
    
    init.add(
        vg.Delay(1000 * 100),
        vg.Systask('finish'),
    )

    return m

実行すると次のようなHDLが生成されます。

module test #
(
  parameter WIDTH = 8
)
(

);

  reg CLK;
  reg RST_n;
  wire [WIDTH-1:0] LED;

  blinkled
  #(
    .WIDTH(WIDTH)
  )
  uut
  (
    .CLK(CLK),
    .RST_n(RST_n),
    .LED(LED)
  );

  initial begin
    $dumpfile("uut.vcd");
    $dumpvars(0, uut, CLK, RST_n, LED);
  end

  initial begin
    CLK = 0;
    forever begin
      #5 CLK = !CLK;
    end
  end

  initial begin
    RST_n = 1;
    #97;
    RST_n = 0;
    #97;
    RST_n = 1;
    #100000;
    $finish;
  end

endmodule


module blinkled #
(
  parameter WIDTH = 8
)
(
  input CLK,
  input RST_n,
  output reg [WIDTH-1:0] LED
);

  reg [32-1:0] count;

  always @(posedge CLK or negedge RST_n) begin
    if(~RST_n) begin
      count <= 0;
    end else begin
      if(count == 1023) begin
        count <= 0;
      end else begin
        count <= count + 1;
      end
    end
  end

  always @(posedge CLK or negedge RST_n) begin
    if(~RST_n) begin
      LED <= 0;
    end else begin
      if(count == 1023) begin
        LED <= LED + 1;
      end 
    end
  end

endmodule

シミュレーション結果は次の様になります。
f:id:feynman911:20181022214216j:plain

リセットのタイミングも変えたので、非同期リセットが掛かっている事が確認できます。
次回は、Veriloggen で always の代わりに使える Seq を扱います。