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
シミュレーション結果は次の様になります。
リセットのタイミングも変えたので、非同期リセットが掛かっている事が確認できます。
次回は、Veriloggen で always の代わりに使える Seq を扱います。