Veriloggenって何だろう(4)Seq
Verilog HDL を生成するための記述を python で行うことで FPGA 回路の生成を楽にする為のライブラリー Veriloggen を使ってみたいと思います。
前回は Always 文で非同期リセットの記述を試してみました。今回は Always の代わりに順序回路を記述するSeqを使ってみたいと思います。
Seq を使った順序回路の記述
前回の Always の代わりに Veriloggen に組み込まれている Seq ライブラリーを使用して、読み易く、そして短いプログラムが書けます。
作り方は Always の時と同じで、Seq でモジュールオブジェクトを作り、それに always の内側で処理する内容を足しこんでいきます。
Seq オブジェクトは、always とその初期化部分を自動で作成してくれます。
主要部分のみ抜き出してみると、次のような記述になります。
seq = vg.Seq(m, 'seq', clk, rst) seq.add( vg.Systask('display', 'LED:%d count:%d', led, count) ) seq.add( count(count + 1), cond=(count < interval-1) ) seq.add( count(0), cond=(count == interval-1) ) seq.add( led(led + 1), cond=(count == interval-1) ) seq.make_always()
1行目:モジュール名として 'seq' となるオブジェクト seq を作ります。
3行目:(count < interval-1) の時に count に1を足します。
4行目:(count == interval-1) の時に count を0にします。
5行目:(count == interval-1) の時に led に1 足します。
6行目:always 分を作成します。
これを to_verilog() で Verilog HDL に変換すると次のようになります。
always @(posedge CLK) begin if(RST) begin count <= 0; LED <= 0; end else begin $display("LED:%d count:%d", LED, count); if(count < INTERVAL - 1) begin count <= count + 1; end if(count == INTERVAL - 1) begin count <= 0; end if(count == INTERVAL - 1) begin LED <= LED + 1; end end
シミュレーションまで含めた記述サンプル
テストベンチ含めた全体のプログラムは次のようになります。
import veriloggen as vg def mkLed(): m = vg.Module('blinkled') interval = m.Parameter('INTERVAL', 16) clk = m.Input('CLK') rst = m.Input('RST') led = m.OutputReg('LED', 8, initval=0) count = m.Reg('count', 32, initval=0) seq = vg.Seq(m, 'seq', clk, rst) seq.add( vg.Systask('display', 'LED:%d count:%d', led, count) ) seq.add( count(count + 1), cond=(count < interval-1) ) seq.add( count(0), cond=(count == interval-1) ) seq.add( led(led + 1), cond=(count == interval-1) ) seq.make_always() 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 = ports['RST'] uut = m.Instance(led, 'uut', params=m.connect_params(led), ports=m.connect_ports(led)) #simulation.setup_waveform(m, uut) vg.simulation.setup_waveform(m, uut, m.get_vars()) vg.simulation.setup_clock(m, clk, hperiod=5) init = vg.simulation.setup_reset(m, rst, m.make_reset(), period=100) init.add( vg.Delay(1000), vg.Systask('finish'), ) return m if __name__ == '__main__': test = mkTest() verilog = test.to_verilog('tmp.v') print(verilog) sim = vg.simulation.Simulator(test) rslt = sim.run() print(rslt) sim.view_waveform()
これを実行すると次のような tmp.v ファイルが作られ、GTKWave で波形の確認ができます。
module test # ( parameter INTERVAL = 16 ) ( ); reg CLK; reg RST; wire [8-1:0] LED; blinkled #( .INTERVAL(INTERVAL) ) uut ( .CLK(CLK), .RST(RST), .LED(LED) ); initial begin $dumpfile("uut.vcd"); $dumpvars(0, uut, CLK, RST, LED); end initial begin CLK = 0; forever begin #5 CLK = !CLK; end end initial begin RST = 0; #100; RST = 1; #100; RST = 0; #1000; $finish; end endmodule module blinkled # ( parameter INTERVAL = 16 ) ( input CLK, input RST, output reg [8-1:0] LED ); reg [32-1:0] count; always @(posedge CLK) begin if(RST) begin count <= 0; LED <= 0; end else begin $display("LED:%d count:%d", LED, count); if(count < INTERVAL - 1) begin count <= count + 1; end if(count == INTERVAL - 1) begin count <= 0; end if(count == INTERVAL - 1) begin LED <= LED + 1; end end end endmodule
Seq での負論理非同期リセット
Seq を使用した時に負論理非同期リセットを記述するにはどうしたらいいのでしょうか。よくわからないので、make_always() の定義を見てみました。
Spyder を使用している時には make_always() 上で右クリックし '定義へ移動' で Seq.py ファイルの定義箇所が表示されます。
これを見ると、Posedgeで決め打ちされているようなので、Negedge でリセットが掛かるような always を組むように、Seq.py に make_always_n() を作って使う事にします。
自分が使いたいように自由に組み替えられるのがオープンソースの良いところでしょう。
def make_always_n(self, reset=(), body=()): if self.done: #raise ValueError('make_always() has been already called.') return self.done = True part_reset = list(reset) + list(self.make_reset()) part_body = list(body) + list(self.make_code()) if not part_reset and not part_body: pass elif not part_reset or self.rst is None: self.m.Always(vtypes.Posedge(self.clk))( part_body, ) else: self.m.Always(vtypes.Posedge(self.clk),vtypes.Negedge(self.rst))( vtypes.If(~self.rst)( part_reset, )( part_body, ))
Pythonファイルは次のようになります。
import veriloggen as vg def mkLed(): m = vg.Module('blinkled') interval = m.Parameter('INTERVAL', 16) clk = m.Input('CLK') rst_n = m.Input('RST_n') led = m.OutputReg('LED', 8, initval=0) count = m.Reg('count', 32, initval=0) seq = vg.Seq(m, 'seq', clk, rst_n) seq.add( vg.Systask('display', 'LED:%d count:%d', led, count) ) seq.add( count(count + 1), cond=(count < interval-1) ) seq.add( count(0), cond=(count == interval-1) ) seq.add( led(led + 1), cond=(count == interval-1) ) seq.make_always_n() 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)) #simulation.setup_waveform(m, uut) 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), vg.Systask('finish'), ) return m if __name__ == '__main__': test = mkTest() verilog = test.to_verilog('tmp.v') print(verilog) sim = vg.simulation.Simulator(test) rslt = sim.run() print(rslt) sim.view_waveform()
変換された Verilog HDL ファイルは以下の様になります。
module test # ( parameter INTERVAL = 16 ) ( ); reg CLK; reg RST_n; wire [8-1:0] LED; blinkled #( .INTERVAL(INTERVAL) ) 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; #1000; $finish; end endmodule module blinkled # ( parameter INTERVAL = 16 ) ( input CLK, input RST_n, output reg [8-1:0] LED ); reg [32-1:0] count; always @(posedge CLK or negedge RST_n) begin if(~RST_n) begin count <= 0; LED <= 0; end else begin $display("LED:%d count:%d", LED, count); if(count < INTERVAL - 1) begin count <= count + 1; end if(count == INTERVAL - 1) begin count <= 0; end if(count == INTERVAL - 1) begin LED <= LED + 1; end end end endmodule
シミュレーション結果は下図の様になり、前回と同じように非同期リセットが確認できます。