Veriloggenって何だろう(5)FSM
Verilog HDL を生成するための記述を python で行うことで FPGA 回路の生成を楽にする為のライブラリー Veriloggen を使ってみたいと思います。
前回までは 順序回路を記述するための Always と Seq に関して書いてきましたが、今回は状態遷移マシンを記述するFSMについて書いていきたいと思います。
状態遷移マシンFSM基本形
これもまた、モジュールにFSMを足しこみ、順番に状態を足していきます。
fsm = FSM(m, 'fsm', clk, rst)
fsm.add( *** ) でその状態での処理を加えていき、fsm.goto_next(cond= **** )で条件がそろった時に次の状態へ移行するようにします。
goto_nextは次の状態を作るとともに、そこへの移動を同時に行います。
これを繰り返すと、まっすぐに流れる状態遷移が作成できます。
最後には、初めに戻るためのgoto_initが書かれることになります。
一連の手続きを状態遷移マシンで記述する時には、これが基本形となるでしょう。
コードの前にこれも負論理非同期リセットが書けるように改造しておきます。
簡単なFSMの記述例とシミュレーション結果
状態3つを順番に繰り返すFSMを書いてみます。入力AがHighになると状態0から状態1に遷移し、さらに入力BがHighになると状態2に遷移。入力AとBがLOWに落ちると状態0に戻るというFSMになります。FSMを作るときにwidthが省略されると32bit分取られてもったいないのでFSMにwidth=2を追加して4状態まで表現できるようにしてあります。
def mkStateMachine(): m = vg.Module('state_machine') clk = m.Input('CLK') rst = m.Input('RST') a = m.Input('A') b = m.Input('B') led = m.OutputReg('LED', 2,initval=0) fsm = vg.FSM(m, 'fsm', clk, rst, width = 2) #state0 fsm.add(led(0)) fsm.goto_next(cond=(a==1)) #state1 fsm.add(led(1)) fsm.goto_next(cond=(b==1)) #state2 fsm.add(led(2)) fsm.goto_init(cond=vg.AndList(a==0,b==0)) #build always statement #reset時にled=0 fsm.make_always(reset=[led(0)]) return m
Verilog変換結果は次のようになります。
module state_machine ( input CLK, input RST, input A, input B, output reg [2-1:0] LED ); reg [2-1:0] fsm; localparam fsm_init = 0; localparam fsm_1 = 1; localparam fsm_2 = 2; always @(posedge CLK) begin if(RST) begin LED <= 0; fsm <= fsm_init; end else begin case(fsm) fsm_init: begin LED <= 0; if(A == 1) begin fsm <= fsm_1; end end fsm_1: begin LED <= 1; if(B == 1) begin fsm <= fsm_2; end end fsm_2: begin LED <= 2; if((A == 0) && (B == 0)) begin fsm <= fsm_init; end end endcase end end endmodule
シミュレーション結果
任意の状態遷移の書き方
流れが一直線ではない時はどう書いたらいいのか試してみます。
次のような状態遷移を書いてみます。
複数の行き先がある場合には、goto_nextで繋げられないので、下のような書き方になるでしょう。
inc( )で次の状態を作るのと、go_to( )で移動先の番号と移動の条件を記述します。
def mkStateMachine(): m = vg.Module('state_machine') clk = m.Input('CLK') rst = m.Input('RST') start = m.Input('START') stop = m.Input('STOP') count = m.OutputReg('COUNT', 8,initval=0) fsm = vg.FSM(m, 'fsm', clk, rst, width = 2) #state0 Clear fsm.add(count(0)) fsm.goto_next(cond=(start==1)) #state1 Count up fsm.add(count.inc()) fsm.goto(3,cond=(count>=30)) fsm.goto(0,cond=vg.AndList(start==0,stop==0)) fsm.goto_next(cond=(stop==1)) #state2 Hold fsm.goto(0, cond=vg.AndList(start==0,stop==0)) fsm.goto(1, cond=vg.AndList(start==1,stop==0)) fsm.inc() #state3 End fsm.goto(0,cond=vg.AndList(start==0,stop==0)) #build always statement #reset時にcount=0 fsm.make_always(reset=[count(0)]) return m
変換結果とシミュレーション結果は下記になります。
module state_machine ( input CLK, input RST, input START, input STOP, output reg [8-1:0] COUNT ); reg [2-1:0] fsm; localparam fsm_init = 0; localparam fsm_1 = 1; localparam fsm_2 = 2; localparam fsm_3 = 3; always @(posedge CLK) begin if(RST) begin COUNT <= 0; fsm <= fsm_init; end else begin case(fsm) fsm_init: begin COUNT <= 0; if(START == 1) begin fsm <= fsm_1; end end fsm_1: begin COUNT <= COUNT + 1; if(COUNT >= 30) begin fsm <= fsm_3; end if((START == 0) && (STOP == 0)) begin fsm <= fsm_init; end if(STOP == 1) begin fsm <= fsm_2; end end fsm_2: begin if((START == 0) && (STOP == 0)) begin fsm <= fsm_init; end if((START == 1) && (STOP == 0)) begin fsm <= fsm_1; end end fsm_3: begin if((START == 0) && (STOP == 0)) begin fsm <= fsm_init; end end endcase end end endmodule
FSMでの負論理非同期リセット
負論理非同期リセットを使いたい場合には fsm.py に次の定義を追加して、使用します。
def make_always_n(self, reset=(), body=(), case=True): if self.done: #raise ValueError('make_always() has been already called.') return self.done = True part_reset = self.make_reset(reset) part_body = list(body) + list(self.make_case() if case else self.make_if()) self.m.Always(vtypes.Posedge(self.clk),vtypes.Negedge(self.rst))( vtypes.If(~self.rst)( part_reset, )( part_body, ))
リセットの難しさ
同期リセットをするべきなのか、非同期リセットをするべきなのかは、使うFGPAのメーカにもよる話で、安定動作をさせるためには、かなり難しい話の様です。次のリンクが参考になりそうです。
dora.bk.tsukuba.ac.jp