(原創) 有限狀態機FSM coding style整理 (SOC) (Verilog)
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寫法有幾個優點:
2.可輕易地將state diagram改用Verilog表示
3.將Next state logic與output logic分開,可降低code的複雜度,便於日後維護
3個always是一個推薦的寫法。
Testbench
simple_fsm_tb.v / Verilog
2 (C) OOMusou 2011 http://oomusou.cnblogs.com
3
4 Filename : simple_fsm_tb.v
5 Simulator : ModelSim SE 6.3e + Debussy 5.4 v9
6 Description : testbench for FSM
7 Release : Jun.05,2011 1.0
8 */
9
10 module simple_fsm_tb;
11
12 reg clk = 1'b1;
13 reg rst_n = 1'b1;
14 reg w_i = 1'b0;
15 wire z_o;
16
17 // clk
18 always #10 clk = ~clk;
19
20 event after_rst;
21
22 // rst_n
23 initial begin
24 #6; // 6ns
25 rst_n = 1'b0;
26 #30; // 36ns
27 rst_n = 1'b1;
28 ->after_rst;
29 end
30
31 // w_i
32 initial begin
33 @(after_rst);
34 repeat(2)@(posedge clk); // 60ns
35 w_i <= 1'b1;
36 @(posedge clk); // 80 ns
37 w_i <= 1'b0;
38 @(posedge clk); // 100 ns
39 w_i <= 1'b1;
40 repeat(2)@(posedge clk); // 140 ns
41 w_i <= 1'b0;
42 @(posedge clk); // 160 ns
43 w_i <= 1'b1;
44 repeat(3)@(posedge clk); // 220 ns
45 w_i <= 1'b0;
46 end
47
48 initial begin
49 $fsdbDumpfile("simple_fsm.fsdb");
50 $fsdbDumpvars(0, simple_fsm_tb);
51 end
52
53 simple_fsm u_simple_fsm (
54 .clk (clk),
55 .rst_n (rst_n),
56 .w_i (w_i),
57 .z_o (z_o)
58 );
59
60 endmodule
執行結果
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.可輕易地將state diagram改用Verilog表示
3.因為state register原本程式碼就不多,將next state logic與state register合一後,next state logic仍與output logic分開,因此不會增加code的複雜度,便於日後維護
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.很類似Mealy FSM寫法,容易讓人誤以為是Mealy FSM
不推薦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寫法的缺點:
2.1個always內同時包含next state logic與output logic,會增加code的複雜度,日後維護更加困難
不推薦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寫法有幾個優點:
2.可輕易地將state diagram改用Verilog表示
3.將Next state logic與output logic分開,可降低code的複雜度,便於日後維護
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.可輕易地將state diagram改用Verilog表示
3.因為state register原本程式碼就不多,將next state logic與state register合一後,next state logic仍與output logic分開,因此不會增加code的複雜度,便於日後維護
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,很容易出錯,觀念必須非常清楚。
完整程式碼下載
simple_fsm_moore_3_always_best.7z (Moore FSM 3 always)
simple_fsm_moore_2_always_0_cs_ns_good.7z (Moore FSM 2 always [state register + next state logic合一])
simple_fsm_moore_2_always_1_cs_ol_ng.7z (Moore FSM 2 always [state register + output logic合一])
simple_fsm_moore_2_always_2_ns_ol_ng.7z (Moore FSM 2 always [next state + output logic合一])
simple_fsm_moore_1_always_ng.7z (Moore FSM 1 always)
simple_fsm_mealy_3_always_best.7z (Mealy FSM 3 always)
simple_fsm_mealy_2_always_0_cs_ns_good.7z (Mealy FSM 2 always [state register + next state logic合一])
simple_fsm_mealy_2_always_2_ns_ol_ng.7z (Mealy FSM 2 always [next state logic + output logic合一])
simple_fsm_moore_2_always_0_cs_ns_good_practical.7z (Moore FSM 2 always [state register + next state logic合一] with better timing)
simple_fsm_moore_3_always_practical.7z (Moore FSM 3 always with better timing)
simple_fsm_moore_3_always_practical2.7z (Moore FSM 3 always with better timing and no delay 1 clk)
Conclusion
1.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
全文完。