Abstract
FSM在數位電路中非常重要,藉由FSM,可以讓數位電路也能循序地執行起演算法。本文將詳細討論各種FSM coding style的優缺點,並歸納出推薦的coding style。Introduction
使用環境:Debussy 5.4 v9 + ModelSim SE 6.3e + Quartus II 8.1本文將討論以下主題:
1.Moore FSM的架構
2.Moore FSM各種coding style比較
3.Mealy FSM架構
4.Mealy FSM各種coding style比較
5.實務上推薦的coding style
6.Conclusion
若要讓數位電路也能循序地執行演算法,最簡單的方式可以使用D-FF產生counter,根據counter的值去決定要執行不同的程式碼,如此也能達到使數位電路循序執行演算法的目的,不過這種方式僅適用於很簡單的演算法,在一般規模的演算法若使用counter方式,程式碼將不容易維護,所以實務上會使用FSM方式來實現演算法。
其實FSM方式也是利用counter來實現,所謂的counter,並不是只有counter = counter + 1才算是counter,FSM的state register就是廣義的counter,只是這種counter不是一直加1而已,而是有自己的遞增規則。FSM只是提供了一種較為高階與較容易維護的方式來實現演算法。
Moore FSM架構
一般在寫FSM時,會以Moore FSM為主,所以先討論Moore。由上圖可知,Moore FSM內部由3個block所構成:Next state logic,State register與Output logic。
Next state logic:純粹的組合邏輯,以整個module的input與目前的state為輸入,目的在產生下一個state值存入state register。
State register:由D-FF所構成,將Next state logic所產生的state存入register。
Output logic:純粹的組合邏輯,根據目前的state產生整個module的output。
所以可以發現,整個Moore FSM事實上是由2塊的組合邏輯與1塊D-FF所構成,我們常聽到所謂的一段式、二段式與三段式FSM,事實上就是由這3個block排列組合而成。
Moore FSM各種coding style比較
為了要實際比較各種coding style,在此舉一個簡單的例子,若input w_i為連續2個clk為high,則output會在下1個clk產生周期為1 T的high pulse,timing diagram如上圖所示。
因此設計了Moore FSM,state diagram如上圖所示,接下來要做的就是用各種coding style來實現這個Moore FSM。
1.使用3個always (三段式)
simple_fsm_moore_3_always_best.v / Verilog
1 /* 2 (C) OOMusou 2011 http://oomusou.cnblogs.com 3 4 Filename : simple_fsm_moore_3_always_best.v 5 Synthesizer : Quartus II 8.1 6 Description : 3 always block for moore fsm (BEST) 7 Release : Jun.05,2011 1.0 8 */ 9 10 module simple_fsm ( 11 clk, 12 rst_n, 13 w_i, 14 z_o 15 ); 16 17 input clk; 18 input rst_n; 19 input w_i; 20 output z_o; 21 22 parameter IDLE = 2'b00; 23 parameter S0 = 2'b01; 24 parameter S1 = 2'b10; 25 26 reg [1:0] curr_state; 27 reg [1:0] next_state; 28 reg z_o; 29 30 // state reg 31 always@(posedge clk or negedge rst_n) 32 if (~rst_n) curr_state <= IDLE; 33 else curr_state <= next_state; 34 35 // next state logic 36 always@(*) 37 case (curr_state) 38 IDLE : if (w_i) next_state = S0; 39 else next_state = IDLE; 40 S0 : if (w_i) next_state = S1; 41 else next_state = IDLE; 42 S1 : if (w_i) next_state = S1; 43 else next_state = IDLE; 44 default : next_state = IDLE; 45 endcase 46 47 // output logic 48 always@(*) 49 case (curr_state) 50 IDLE : z_o = 1'b0; 51 S0 : z_o = 1'b0; 52 S1 : z_o = 1'b1; 53 default : z_o = 1'b0; 54 endcase 55 56 endmodule
35行
// next state logic always@(*) case (curr_state) IDLE : if (w_i) next_state = S0; else next_state = IDLE; S0 : if (w_i) next_state = S1; else next_state = IDLE; S1 : if (w_i) next_state = S1; else next_state = IDLE; default : next_state = IDLE; endcase
使用1個always描述next state logic,因為是純粹組合邏輯,所以使用blocking。
根據Moore FSM架構圖所示,next state logic的結果與input與目前state有關,所以先用case對目前state做一次大分類,然後每個state再根據input做if判斷。
30行
// state reg always@(posedge clk or negedge rst_n) if (~rst_n) curr_state <= IDLE; else curr_state <= next_state;
使用1個always描述state register,因為是D-FF且含clk,所以使用nonblocking。
由於state register區塊並不包含任何邏輯,所以不會因為不同FSM而有不同寫法,不同FSM只會改變next state logic與output logic的寫法。
47行
// output logic always@(*) case (curr_state) IDLE : z_o = 1'b0; S0 : z_o = 1'b0; S1 : z_o = 1'b1; default : z_o = 1'b0; endcase
使用1個always描述output logic,因為是純粹組合邏輯,所以使用blocking。
根據Moore FSM架構圖所示,output logic的結果只與目前state有關,所以只需用case對state做一次分類即可。
使用3個always寫法有幾個優點:
3個always是一個推薦的寫法。
Testbench
simple_fsm_tb.v / Verilog
執行結果
2.使用2個always (二段式)
由於要使用2個always去描述3個block,根據排列組合原理,C3取2,共有3種可能,我們一個一個討論。
2.1 state register與next state logic合一
simple_fsm_moore_2_always_0_cs_ns_good.v / Verilog
1 /* 2 (C) OOMusou 2011 http://oomusou.cnblogs.com 3 4 Filename : simple_fsm_moore_2_always_0_cs_ns_good.v 5 Synthesizer : Quartus II 8.1 6 Description : 2 always block for moore fsm (GOOD) 7 Release : Jun.05,2011 1.0 8 */ 9 10 module simple_fsm ( 11 clk, 12 rst_n, 13 w_i, 14 z_o 15 ); 16 17 input clk; 18 input rst_n; 19 input w_i; 20 output z_o; 21 22 parameter IDLE = 2'b00; 23 parameter S0 = 2'b01; 24 parameter S1 = 2'b10; 25 26 reg [1:0] curr_state; 27 reg z_o; 28 29 // state reg + next state logic 30 always@(posedge clk or negedge rst_n) 31 if (~rst_n) curr_state <= IDLE; 32 else 33 case (curr_state) 34 IDLE : if (w_i) curr_state <= S0; 35 else curr_state <= IDLE; 36 S0 : if (w_i) curr_state <= S1; 37 else curr_state <= IDLE; 38 S1 : if (w_i) curr_state <= S1; 39 else curr_state <= IDLE; 40 default : curr_state <= IDLE; 41 endcase 42 43 // output logic 44 always@(*) 45 case (curr_state) 46 IDLE : z_o = 1'b0; 47 S0 : z_o = 1'b0; 48 S1 : z_o = 1'b1; 49 default : z_o = 1'b0; 50 endcase 51 52 endmodule
29行
// state reg + next state logic always@(posedge clk or negedge rst_n) if (~rst_n) curr_state <= IDLE; else case (curr_state) IDLE : if (w_i) curr_state <= S0; else curr_state <= IDLE; S0 : if (w_i) curr_state <= S1; else curr_state <= IDLE; S1 : if (w_i) curr_state <= S1; else curr_state <= IDLE; default : curr_state <= IDLE; endcase
將state register與next state logic合起來用1個always去描述,雖然next state logic是純粹的組合邏輯,為了遷就於帶clk的state register,且要用一個always描述,就必須改用nonblocking。
由於state register與next state logic合一,所以可以少宣告next_state reg,不過這並不會影響合成結果,只是可以少打幾個字而已。
因為next state logic由input與state所構成,所以先用case對state做一次大分類,然後每個state再根據input做if判斷。
43行
// output logic always@(*) case (curr_state) IDLE : z_o = 1'b0; S0 : z_o = 1'b0; S1 : z_o = 1'b1; default : z_o = 1'b0; endcase
使用1個always描述output logic,因為是純粹組合邏輯,所以使用blocking。
根據Moore FSM架構圖所示,output logic的結果只與目前state有關,所以只需用case對state做一次分類即可。
使用2個always (state register與next state logic合一)寫法有幾個優點:
2個always (state register與next state logic合一)也是一個推薦的寫法。
接下來要討論的都是不推薦的寫法,主要目的是了解為什麼不推薦的原因。
2.2 state register與output logic合一
simple_fsm_moore_2_always_1_cs_ol_ng.v / Verilog
1 /* 2 (C) OOMusou 2011 http://oomusou.cnblogs.com 3 4 Filename : simple_fsm_moore_2_always_1_cs_ol_ng.v 5 Synthesizer : Quartus II 8.1 6 Description : 2 always block for moore fsm (NO GOOD) 7 Release : Jun.05,2011 1.0 8 */ 9 10 module simple_fsm ( 11 clk, 12 rst_n, 13 w_i, 14 z_o 15 ); 16 17 input clk; 18 input rst_n; 19 input w_i; 20 output z_o; 21 22 parameter IDLE = 2'b00; 23 parameter S0 = 2'b01; 24 parameter S1 = 2'b10; 25 26 reg [1:0] curr_state; 27 reg [1:0] next_state; 28 reg z_o; 29 30 // state reg + output logic 31 always@(posedge clk or negedge rst_n) 32 if (~rst_n) {curr_state, z_o} <= {IDLE, 1'b0}; 33 else begin 34 curr_state <= next_state; 35 36 case (next_state) 37 IDLE : z_o <= 1'b0; 38 S0 : z_o <= 1'b0; 39 S1 : z_o <= 1'b1; 40 default : z_o <= 1'b0; 41 endcase 42 end 43 44 // next state logic 45 always@(*) 46 case (curr_state) 47 IDLE : if (w_i) next_state = S0; 48 else next_state = IDLE; 49 S0 : if (w_i) next_state = S1; 50 else next_state = IDLE; 51 S1 : if (w_i) next_state = S1; 52 else next_state = IDLE; 53 default : next_state = IDLE; 54 endcase 55 56 endmodule
30行
// state reg + output logic always@(posedge clk or negedge rst_n) if (~rst_n) {curr_state, z_o} <= {IDLE, 1'b0}; else begin curr_state <= next_state; case (next_state) IDLE : z_o <= 1'b0; S0 : z_o <= 1'b0; S1 : z_o <= 1'b1; default : z_o <= 1'b0; endcase
將state register與output logic合起來用1個always去描述,雖然output logic是純粹的組合邏輯,為了遷就於帶clk的state register,且要用一個always描述,就必須改用nonblocking。
因為output logic只與state有關,所以只用case對state做一次分類即可。
這種寫法最大的問題是:output logic必須用next_state去判斷!!
依照Moore FSM的架構圖得知,output logic只與目前state有關,之前的幾種FSM寫法,output logic也是由目前state去判斷,為什麼這種寫法要靠next_state去判斷呢?
主要原因是根據Moore FSM的定義,output logic只與目前state有關,且是個純粹的組合邏輯,但目前強迫將state register與output logic放在同一個always,迫使output logic必須使用nonblocking的方式呈現,也就是若output logic仍然使用目前state去做判斷,則output logic會多delay 1個clk,為了讓output logic結果正常,只好提前1個clk做判斷,也就是提前到next_state去做判斷。
所以當我們從state diagram換成Verilog表示時,若使用2個always,且是state register與output logic合一時,必須很小心要使用next_state去做output logic判斷,因為這個地方很不直覺,很容易出錯,所以不推薦這種寫法。
44行
// next state logic always@(*) case (curr_state) IDLE : if (w_i) next_state = S0; else next_state = IDLE; S0 : if (w_i) next_state = S1; else next_state = IDLE; S1 : if (w_i) next_state = S1; else next_state = IDLE; default : next_state = IDLE; endcase
使用1個always描述next state logic,因為是純粹組合邏輯,所以使用blocking。
根據Moore FSM架構圖所示,next state logic的結果與input與目前state有關,所以先用case對目前state做一次大分類,然後每個state再根據input做if判斷。
使用2個always (state register與output logic合一)寫法的缺點:
不推薦2個always (state register與output logic合一)寫法。
或許你會說,在實務上卻常看到state register與output logic合一的寫法,為什麼不會出問題?那是因為儘管是用Moore FSM,我們為了timing更好,常會在output時多敲一個D-FF,讓Output Logic的組合邏輯不要與其他module的input的組合邏輯合併,避免造成critical path,假如是這種需求,state register與output logic合一後,可以直接判斷curr_state,不用提早一個clk判斷next_state。
2.3 next state logic與output logic合一
simple_fsm_moore_2_always_2_ns_ol_ng.v / Verilog
1 /* 2 (C) OOMusou 2011 http://oomusou.cnblogs.com 3 4 Filename : simple_fsm_moore_2_always_2_ns_ol_ng.v 5 Synthesizer : Quartus II 8.1 6 Description : 2 always block for moore fsm (NO GOOD) 7 Release : Jun.05,2011 1.0 8 */ 9 10 module simple_fsm ( 11 clk, 12 rst_n, 13 w_i, 14 z_o 15 ); 16 17 input clk; 18 input rst_n; 19 input w_i; 20 output z_o; 21 22 parameter IDLE = 2'b00; 23 parameter S0 = 2'b01; 24 parameter S1 = 2'b10; 25 26 reg [1:0] curr_state; 27 reg [1:0] next_state; 28 reg z_o; 29 30 // state reg 31 always@(posedge clk or negedge rst_n) 32 if (~rst_n) curr_state <= IDLE; 33 else curr_state <= next_state; 34 35 // next state logic + output logic 36 always@(*) 37 case (curr_state) 38 IDLE : if (w_i) {next_state, z_o} = {S0 , 1'b0}; 39 else {next_state, z_o} = {IDLE, 1'b0}; 40 S0 : if (w_i) {next_state, z_o} = {S1 , 1'b0}; 41 else {next_state, z_o} = {IDLE, 1'b0}; 42 S1 : if (w_i) {next_state, z_o} = {S1 , 1'b1}; // always output 1'b1 43 else {next_state, z_o} = {IDLE, 1'b1}; // always output 1'b1 44 default : {next_state, z_o} = {IDLE, 1'b0}; 45 endcase 46 47 endmodule
30行
// state reg always@(posedge clk or negedge rst_n) if (~rst_n) curr_state <= IDLE; else curr_state <= next_state;
使用1個always描述state register,因為是D-FF且含clk,所以使用nonblocking。
由於state register區塊並不包含任何邏輯,所以不會因為不同FSM而有不同寫法,不同FSM只會改變next state logic與output logic的寫法。
35行
// next state logic + output logic always@(*) case (curr_state) IDLE : if (w_i) {next_state, z_o} = {S0 , 1'b0}; else {next_state, z_o} = {IDLE, 1'b0}; S0 : if (w_i) {next_state, z_o} = {S1 , 1'b0}; else {next_state, z_o} = {IDLE, 1'b0}; S1 : if (w_i) {next_state, z_o} = {S1 , 1'b1}; // always output 1'b1 else {next_state, z_o} = {IDLE, 1'b1}; // always output 1'b1 default : {next_state, z_o} = {IDLE, 1'b0}; endcase
將next state logic與output logic使用同一個always去描述,由於next state logic與output logic都是純粹的組合邏輯,所以使用blocking描述沒有問題。
由於next state logic與input與目前state有關,但output logic卻只與目前state有關,因為都是先用目前state做case判斷,然後再對input做if判斷,所以會出現output兩次都出現1的情形,起因於output logic只與目前state有關,與input無關,固任何input都會出現1。
使用2個always (next state logic與output logic合一)寫法的缺點:
不推薦2個always (next state logic與output logic合一)寫法。
3.使用1個always (一段式)
simple_fsm_moore_1_always_ng.v / Verilog
1 /* 2 (C) OOMusou 2011 http://oomusou.cnblogs.com 3 4 Filename : simple_fsm_moore_1_always_ng.v 5 Synthesizer : Quartus II 8.1 6 Description : 1 always block for moore fsm (NO GOOD) 7 Release : Jun.05,2011 1.0 8 */ 9 10 module simple_fsm ( 11 clk, 12 rst_n, 13 w_i, 14 z_o 15 ); 16 17 input clk; 18 input rst_n; 19 input w_i; 20 output z_o; 21 22 parameter IDLE = 2'b00; 23 parameter S0 = 2'b01; 24 parameter S1 = 2'b10; 25 26 reg [1:0] curr_state; 27 reg z_o; 28 29 always@(posedge clk or negedge rst_n) 30 if (~rst_n) {curr_state, z_o} <= {IDLE, 1'b0}; 31 else 32 case (curr_state) 33 IDLE : if (w_i) {curr_state, z_o} <= {S0, 1'b0}; 34 else {curr_state, z_o} <= {IDLE, 1'b0}; 35 S0 : if (w_i) {curr_state, z_o} <= {S1, 1'b1}; //? 36 else {curr_state, z_o} <= {IDLE, 1'b0}; 37 S1 : if (w_i) {curr_state, z_o} <= {S1, 1'b1}; 38 else {curr_state, z_o} <= {IDLE, 1'b0}; //? 39 default : {curr_state, z_o} <= {IDLE, 1'b0}; 40 endcase 41 42 endmodule
29行
always@(posedge clk or negedge rst_n) if (~rst_n) {curr_state, z_o} <= {IDLE, 1'b0}; else case (curr_state) IDLE : if (w_i) {curr_state, z_o} <= {S0, 1'b0}; else {curr_state, z_o} <= {IDLE, 1'b0}; S0 : if (w_i) {curr_state, z_o} <= {S1, 1'b1}; //? else {curr_state, z_o} <= {IDLE, 1'b0}; S1 : if (w_i) {curr_state, z_o} <= {S1, 1'b1}; else {curr_state, z_o} <= {IDLE, 1'b0}; //? default : {curr_state, z_o} <= {IDLE, 1'b0}; endcase
使用1個always同時描述next state logic、state register與output logic,雖然next state logic與output logic是純粹的組合邏輯,但為了遷就於帶clk的state register,所以必須使用nonblocking。
根據之前的經驗,由於Moore FSM的output logic只與目前state state有關,且是純粹的組合邏輯,若硬要與state register用同一個always去描述,判斷上會出現一些問題,需提早1個clk用next state判斷(在state register與output logic合一時曾經遇過)。
在1個always內,連next_state也省了,所以在35行
當目前state為S0且輸入為1'b1時,output必須提前為1,因為這是在nonblocking內!!
37行
同理,在目前state為S1且輸入為1'b1時,output也必須提前為1,也因為這是在nonblocking內!!
使用1個always寫法的缺點:
不推薦1個always寫法。
Mealy FSM架構
談完了Moore FSM,接下來談Mealy FSM,與Moore FSM的差別只在於Moore FSM的output logic只由目前state決定,但是Mealy FSM可由目前state與input共同決定。
Mealy FSM各種coding style比較
將之前的例子用Mealy FSM重新改寫,原本在Moore FSM下,若input w_i為連續2個clk為high,則output會在下1個clk產生週期為1 T的high pulse,若改用Mealy FSM,則output會提早1個clk出現,如上圖所示。
原本Moore FSM需要3個state,若改用Mealy FSM後,會只剩下2個state,接下來要用各種coding style來實現Mealy FSM。
1.使用3個always (三段式)
simple_fsm_mealy_3_always_best.v / Verilog
1 /* 2 (C) OOMusou 2011 http://oomusou.cnblogs.com 3 4 Filename : simple_fsm_mealy_3_always_best.v 5 Synthesizer : Quartus II 8.1 6 Description : 3 always block for mealy fsm (BEST) 7 Release : Jun.05,2011 1.0 8 */ 9 10 module simple_fsm ( 11 clk, 12 rst_n, 13 w_i, 14 z_o 15 ); 16 17 input clk; 18 input rst_n; 19 input w_i; 20 output z_o; 21 22 parameter IDLE = 2'b00; 23 parameter S0 = 2'b01; 24 25 reg [1:0] curr_state; 26 reg [1:0] next_state; 27 reg z; 28 reg z_o; 29 30 // state reg 31 always@(posedge clk or negedge rst_n) 32 if (~rst_n) curr_state <= IDLE; 33 else curr_state <= next_state; 34 35 // next state logic 36 always@(*) 37 case (curr_state) 38 IDLE : if (w_i) next_state = S0; 39 else next_state = IDLE; 40 S0 : if (w_i) next_state = S0; 41 else next_state = IDLE; 42 default : next_state = IDLE; 43 endcase 44 45 // output logic 46 always@(*) 47 case (curr_state) 48 IDLE : if (w_i) z = 1'b0; 49 else z = 1'b0; 50 S0 : if (w_i) z = 1'b1; 51 else z = 1'b0; 52 default : z = 1'b0; 53 endcase 54 55 // mealy output to delay 1 clk for moore 56 always@(posedge clk or negedge rst_n) 57 if (~rst_n) z_o <= 1'b0; 58 else z_o <= z; 59 60 endmodule
30行
// state reg always@(posedge clk or negedge rst_n) if (~rst_n) curr_state <= IDLE; else curr_state <= next_state;
使用1個always描述state register。
35行
// next state logic always@(*) case (curr_state) IDLE : if (w_i) next_state = S0; else next_state = IDLE; S0 : if (w_i) next_state = S0; else next_state = IDLE; default : next_state = IDLE; endcase
使用1個always描述next state logic。
45行
// output logic always@(*) case (curr_state) IDLE : if (w_i) z = 1'b0; else z = 1'b0; S0 : if (w_i) z = 1'b1; else z = 1'b0; default : z = 1'b0; endcase
使用1個always描述output logic。
以上3個always寫法與Moore FSM的3個always並無差異,基本上只要state diagram畫的出來,就能等效地用Verilog描述出來。
55行
// mealy output to delay 1 clk for moore always@(posedge clk or negedge rst_n) if (~rst_n) z_o <= 1'b0; else z_o <= z;
之前提到使用Mealy FSM會少Moore FSM 1個state,且output會早Moore FSM 1個clk,所以最後特別將output在敲一級delay 1個clk,這樣Mealy FSM就會完全與Moore FSM一樣。
使用3個always寫法有幾個優點:
3個always是一個推薦的寫法。
2.使用2個always (兩段式)
由於要使用2個always去描述3個block,根據排列組合原理,C3取2,共有3種可能,我們一個一個討論。
2.1 state register與next state logic合一
simple_fsm_mealy_2_always_0_cs_ns_good.v / Verilog
1 /* 2 (C) OOMusou 2011 http://oomusou.cnblogs.com 3 4 Filename : simple_fsm_mealy_2_always_0_cs_ns_good.v 5 Synthesizer : Quartus II 8.1 6 Description : 2 always block for mealy fsm (GOOD) 7 Release : Jun.05,2011 1.0 8 */ 9 10 module simple_fsm ( 11 clk, 12 rst_n, 13 w_i, 14 z_o 15 ); 16 17 input clk; 18 input rst_n; 19 input w_i; 20 output z_o; 21 22 parameter IDLE = 2'b00; 23 parameter S0 = 2'b01; 24 25 reg [1:0] curr_state; 26 reg z; 27 reg z_o; 28 29 // state reg + next state logic 30 always@(posedge clk or negedge rst_n) 31 if (~rst_n) curr_state <= IDLE; 32 else 33 case (curr_state) 34 IDLE : if (w_i) curr_state <= S0; 35 else curr_state <= IDLE; 36 S0 : if (w_i) curr_state <= S0; 37 else curr_state <= IDLE; 38 default : curr_state <= IDLE; 39 endcase 40 41 // output logic 42 always@(*) 43 case (curr_state) 44 IDLE : if (w_i) z = 1'b0; 45 else z = 1'b0; 46 S0 : if (w_i) z = 1'b1; 47 else z = 1'b0; 48 default : z = 1'b0; 49 endcase 50 51 // mealy output to delay 1 clk for moore 52 always@(posedge clk or negedge rst_n) 53 if (~rst_n) z_o <= 1'b0; 54 else z_o <= z; 55 56 endmodule
29行
// state reg + next state logic always@(posedge clk or negedge rst_n) if (~rst_n) curr_state <= IDLE; else case (curr_state) IDLE : if (w_i) curr_state <= S0; else curr_state <= IDLE; S0 : if (w_i) curr_state <= S0; else curr_state <= IDLE; default : curr_state <= IDLE; endcase
使用1個always同時描述state register與next state logic。
41行
// output logic always@(*) case (curr_state) IDLE : if (w_i) z = 1'b0; else z = 1'b0; S0 : if (w_i) z = 1'b1; else z = 1'b0; default : z = 1'b0; endcase
使用1個always描述output logic。
以上2個always寫法(state register與next state logic合一)與Moore FSM的2個always寫法(state register與next state logic合一)並無差異,基本上只要state diagram畫的出來,就能等效地用Verilog描述出來。
51行
// mealy output to delay 1 clk for moore always@(posedge clk or negedge rst_n) if (~rst_n) z_o <= 1'b0; else z_o <= z;
之前提到使用Mealy FSM會少Moore FSM 1個state,且output會早Moore FSM 1個clk,所以最後特別將output在敲一級delay 1個clk,這樣Mealy FSM就會完全與Moore FSM一樣。
使用2個always (state register與next state logic合一)寫法有幾個優點:
2個always (state register與next state logic合一)也是一個推薦的寫法。
接下來要討論的都是不推薦的寫法,主要目的是了解為什麼不推薦的原因。
2.2 state register與output logic合一
雖然理論上可以用1個always同時描述state register與output logic,但實際上做不到,因為Mealy FSM的output logic是目前state與input的純粹組合邏輯,與state register合一後,就必須使用nonblocking描述,之前Moore FSM還可以提前一個state去做判斷,但Mealy FSM還有input,該如何提前1個clk去判斷input呢?
2個always (state register與output logic合一)無法描述Mealy FSM。
2.3 next state logic與output logic合一
simple_fsm_mealy_2_always_2_ns_ol_ng.v / Verilog
1 /* 2 (C) OOMusou 2011 http://oomusou.cnblogs.com 3 4 Filename : simple_fsm_mealy_2_always_2_ns_ol_ng.v 5 Synthesizer : Quartus II 8.1 6 Description : 2 always block for mealy fsm (NO GOOD) 7 Release : Jun.05,2011 1.0 8 */ 9 10 module simple_fsm ( 11 clk, 12 rst_n, 13 w_i, 14 z_o 15 ); 16 17 input clk; 18 input rst_n; 19 input w_i; 20 output z_o; 21 22 parameter IDLE = 2'b00; 23 parameter S0 = 2'b01; 24 25 reg [1:0] curr_state; 26 reg [1:0] next_state; 27 reg z; 28 reg z_o; 29 30 // state reg 31 always@(posedge clk or negedge rst_n) 32 if (~rst_n) curr_state <= IDLE; 33 else curr_state <= next_state; 34 35 // next state logic + output logic 36 always@(*) 37 case (curr_state) 38 IDLE : if (w_i) {next_state, z} = {S0 , 1'b0}; 39 else {next_state, z} = {IDLE, 1'b0}; 40 S0 : if (w_i) {next_state, z} = {S0 , 1'b1}; 41 else {next_state, z} = {IDLE, 1'b0}; 42 default : {next_state, z} = {IDLE, 1'b0}; 43 endcase 44 45 // mealy output to delay 1 clk for moore 46 always@(posedge clk or negedge rst_n) 47 if (~rst_n) z_o <= 1'b0; 48 else z_o <= z; 49 50 endmodule
30行
// state reg always@(posedge clk or negedge rst_n) if (~rst_n) curr_state <= IDLE; else curr_state <= next_state;
使用1個always描述state register。
35行
// next state logic + output logic always@(*) case (curr_state) IDLE : if (w_i) {next_state, z} = {S0 , 1'b0}; else {next_state, z} = {IDLE, 1'b0}; S0 : if (w_i) {next_state, z} = {S0 , 1'b1}; else {next_state, z} = {IDLE, 1'b0}; default : {next_state, z} = {IDLE, 1'b0}; endcase
使用1個always同時描述next state logic與output logic,因為兩者都是純粹的組合邏輯,所以使用blocking。
45行
// mealy output to delay 1 clk for moore always@(posedge clk or negedge rst_n) if (~rst_n) z_o <= 1'b0; else z_o <= z;
之前提到使用Mealy FSM會少Moore FSM 1個state,且output會早Moore FSM 1個clk,所以最後特別將output在敲一級delay 1個clk,這樣Mealy FSM就會完全與Moore FSM一樣。
使用2個always (next state logic與output logic合一)寫法的缺點:
不推薦2個always (next state logic與output logic合一)寫法。
3.使用1個always (一段式)
理論上存在使用1個always同時描述next state logic、state register與output logic,但實際上做不到,理由與2個always (state register與output logic合一)的理由一樣,1個always必須使用nonblocking描述,而Mealy FSM的output logic是目前state與input的組合邏輯,我們無法提前1個clk去判斷input,所以無法使用1個always去描述。
1個always 無法描述Mealy FSM。
實務上推薦的coding style
如之前所述,實務上為了timing更好,常在Moore FSM的output logic再多敲一級,以下為推薦的coding style:
1.使用2個always (兩段式)
simple_fsm_moore_2_always_0_cs_ns_good_pratical.v / Verilog
1 /* 2 (C) OOMusou 2011 http://oomusou.cnblogs.com 3 4 Filename : simple_fsm_moore_2_always_0_cs_ns_good.v 5 Synthesizer : Quartus II 8.1 6 Description : 2 always block for moore fsm (GOOD) 7 Release : Jun.05,2011 1.0 8 */ 9 10 module simple_fsm ( 11 clk, 12 rst_n, 13 w_i, 14 z_o 15 ); 16 17 input clk; 18 input rst_n; 19 input w_i; 20 output z_o; 21 22 parameter IDLE = 2'b00; 23 parameter S0 = 2'b01; 24 parameter S1 = 2'b10; 25 26 reg [1:0] curr_state; 27 reg z_o; 28 29 // state reg + next state logic 30 always@(posedge clk or negedge rst_n) 31 if (~rst_n) curr_state <= IDLE; 32 else 33 case (curr_state) 34 IDLE : if (w_i) curr_state <= S0; 35 else curr_state <= IDLE; 36 S0 : if (w_i) curr_state <= S1; 37 else curr_state <= IDLE; 38 S1 : if (w_i) curr_state <= S1; 39 else curr_state <= IDLE; 40 default : curr_state <= IDLE; 41 endcase 42 43 // output logic 44 always@(posedge clk or negedge rst_n) 45 if (~rst_n) 46 z_o <= 1'b0; 47 else 48 case (curr_state) 49 IDLE : z_o <= 1'b0; 50 S0 : z_o <= 1'b0; 51 S1 : z_o <= 1'b1; 52 default : z_o <= 1'b0; 53 endcase 54 55 endmodule
29行
// state reg + next state logic always@(posedge clk or negedge rst_n) if (~rst_n) curr_state <= IDLE; else case (curr_state) IDLE : if (w_i) curr_state <= S0; else curr_state <= IDLE; S0 : if (w_i) curr_state <= S1; else curr_state <= IDLE; S1 : if (w_i) curr_state <= S1; else curr_state <= IDLE; default : curr_state <= IDLE; endcase
使用2個always,且state register與next state logic合一,只要判斷curr_state即可,不用擔心是否要提早1個clk判斷。
43行
// output logic always@(posedge clk or negedge rst_n) if (~rst_n) z_o <= 1'b0; else case (curr_state) IDLE : z_o <= 1'b0; S0 : z_o <= 1'b0; S1 : z_o <= 1'b1; default : z_o <= 1'b0; endcase
output logic也敲clk,只要判斷curr_state即可,不用擔心是否要提早1個clk判斷。
2.使用3個always (三段式)
simple_fsm_moore_3_always_practical.v / Verilog
1 /* 2 (C) OOMusou 2011 http://oomusou.cnblogs.com 3 4 Filename : simple_fsm_moore_3_always_practical.v 5 Synthesizer : Quartus II 8.1 6 Description : 3 always block for moore fsm (BEST) 7 Release : Jun.05,2011 1.0 8 */ 9 10 module simple_fsm ( 11 clk, 12 rst_n, 13 w_i, 14 z_o 15 ); 16 17 input clk; 18 input rst_n; 19 input w_i; 20 output z_o; 21 22 parameter IDLE = 2'b00; 23 parameter S0 = 2'b01; 24 parameter S1 = 2'b10; 25 26 reg [1:0] curr_state; 27 reg [1:0] next_state; 28 reg z_o; 29 30 // state reg 31 always@(posedge clk or negedge rst_n) 32 if (~rst_n) curr_state <= IDLE; 33 else curr_state <= next_state; 34 35 // next state logic 36 always@(*) 37 case (curr_state) 38 IDLE : if (w_i) next_state = S0; 39 else next_state = IDLE; 40 S0 : if (w_i) next_state = S1; 41 else next_state = IDLE; 42 S1 : if (w_i) next_state = S1; 43 else next_state = IDLE; 44 default : next_state = IDLE; 45 endcase 46 47 // output logic 48 always@(posedge clk or negedge rst_n) 49 if (~rst_n) z_o <= 1'b0; 50 else 51 case (curr_state) 52 IDLE : z_o <= 1'b0; 53 S0 : z_o <= 1'b0; 54 S1 : z_o <= 1'b1; 55 default : z_o <= 1'b0; 56 endcase 57 58 endmodule
30行
// state reg always@(posedge clk or negedge rst_n) if (~rst_n) curr_state <= IDLE; else curr_state <= next_state;
使用1個always描述state register。
35行
// next state logic always@(*) case (curr_state) IDLE : if (w_i) next_state = S0; else next_state = IDLE; S0 : if (w_i) next_state = S1; else next_state = IDLE; S1 : if (w_i) next_state = S1; else next_state = IDLE; default : next_state = IDLE; endcase
使用1個always描述next state logic,為純粹組合邏輯,所以使用blocking。
47行
// output logic always@(posedge clk or negedge rst_n) if (~rst_n) z_o <= 1'b0; else case (curr_state) IDLE : z_o <= 1'b0; S0 : z_o <= 1'b0; S1 : z_o <= 1'b1; default : z_o <= 1'b0; endcase
使用1個always描述output logic,因為output logic也敲clk,只要判斷curr_state即可,不用擔心是否要提早1個clk判斷。
看到這裡,或許你會問:『為了timing好,多敲一級會多delay一個clk,若我output logic提前1個clk用next_state判斷,不就既可有較好的timing,也不會多delay一個clk?』
1.使用3個always (三段式)
simple_fsm_moore_3_always_practical2.v / Verilog
1 /* 2 (C) OOMusou 2011 http://oomusou.cnblogs.com 3 4 Filename : simple_fsm_moore_3_always_practical2.v 5 Synthesizer : Quartus II 8.1 6 Description : 3 always block for moore fsm (BEST) 7 Release : Jun.05,2011 1.0 8 */ 9 10 module simple_fsm ( 11 clk, 12 rst_n, 13 w_i, 14 z_o 15 ); 16 17 input clk; 18 input rst_n; 19 input w_i; 20 output z_o; 21 22 parameter IDLE = 2'b00; 23 parameter S0 = 2'b01; 24 parameter S1 = 2'b10; 25 26 reg [1:0] curr_state; 27 reg [1:0] next_state; 28 reg z_o; 29 30 // state reg 31 always@(posedge clk or negedge rst_n) 32 if (~rst_n) curr_state <= IDLE; 33 else curr_state <= next_state; 34 35 // next state logic 36 always@(*) 37 case (curr_state) 38 IDLE : if (w_i) next_state = S0; 39 else next_state = IDLE; 40 S0 : if (w_i) next_state = S1; 41 else next_state = IDLE; 42 S1 : if (w_i) next_state = S1; 43 else next_state = IDLE; 44 default : next_state = IDLE; 45 endcase 46 47 // output logic 48 always@(posedge clk or negedge rst_n) 49 if (~rst_n) z_o <= 1'b0; 50 else 51 case (next_state) 52 IDLE : z_o <= 1'b0; 53 S0 : z_o <= 1'b0; 54 S1 : z_o <= 1'b1; 55 default : z_o <= 1'b0; 56 endcase 57 58 endmodule
47行
// output logic always@(posedge clk or negedge rst_n) if (~rst_n) z_o <= 1'b0; else case (next_state) IDLE : z_o <= 1'b0; S0 : z_o <= 1'b0; S1 : z_o <= 1'b1; default : z_o <= 1'b0; endcase
使用1個always去描述output logic,重點是,使用next_state去判斷,因此可以提早一個clk,這樣無論是在Simulator或者經過Synthesizer合成後的結果都會一樣,而且既可在output敲過D flip-flop,也不會多delay一個clk。
不過這種寫法也不是沒有缺點,由於next_state本身是一個純粹的組合邏輯,拿來當output logic的判斷,timing會稍微差一點,很可能critical path就出現在這裡,前一個例子的output logic用的是curr_state,是一個D flip-flop,沒有組合邏輯,所以timing比較好。
另外一個缺點是output logic必須判斷next_state,很容易出錯,觀念必須非常清楚。
完整程式碼下載 (Moore FSM 3 always)
(Moore FSM 2 always [state register + next state logic合一]) (Moore FSM 2 always [state register + output logic合一]) (Moore FSM 2 always [next state + output logic合一]) (Moore FSM 1 always) (Mealy FSM 3 always) (Mealy FSM 2 always [state register + next state logic合一]) (Mealy FSM 2 always [next state logic + output logic合一]) (Moore FSM 2 always [state register + next state logic合一] with better timing) (Moore FSM 3 always with better timing) (Moore FSM 3 always with better timing and no delay 1 clk)Conclusion1.3個always與2個always (state register與next state logic合一)是兩種推薦的寫法,而且這兩種寫法無論要描述Moore FSM或者Mealy FSM都沒問題,其他寫法都不推薦,個人是比較喜歡2個always寫法(state register + next state logic),因為這種寫法最精簡,各種需求都可描述,也不用擔心是否要提前一個clk判斷,最為直覺不易錯。
2.實務上不會特別拘泥使用Moore或者Mealy,只要符合需求即可,一般會以Moore FSM為正宗。
3.實務上為了timing更好,會在Moore FSM的output logic多敲一級。
4.Mealy會比Moore少1個state,且output會比Moore早1個clk。
5.Moore與Mealy之間可以互換,只要在Mealy的output多敲一級即可。
Reference[1] Douglas J. Smith, HDL Chip Design, A practical guide for designing, synthesizing and simulating ASICs and FPGAs using VHDL or Verilog
[2] Stephen Brown 2005, Zvonko Vranesic, Fundamentals of Digital Logic with VHDL Design, McGraw-Hill
全文完。
转载自:http://www.cnblogs.com/oomusou/archive/2011/06/05/fsm_coding_style.html