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寫法有幾個優點:

1.可忠實地反映出原本的Moore FSM硬體架構
2.可輕易地將state diagram改用Verilog表示
3.將Next state logic與output logic分開,可降低code的複雜度,便於日後維護

3個always是一個推薦的寫法。

Testbench

simple_fsm_tb.v / Verilog

1 /* 
2 (C) OOMusou 2011 http://oomusou.cnblogs.com

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  */

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合一)寫法有幾個優點:

1.程式碼較3個always寫法精簡
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合一)寫法的缺點:

當使用1個always去描述state register與output logic時,output logic必須使用next_state做判斷,而非用目前state判斷,由於寫法不直覺,一不小心很容易弄錯

不推薦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合一)寫法的缺點:

1.將next state logic與output logic合一只會增加code的複雜度,日後維護會更加困難
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行

S0 : if (w_i) {curr_state, z_o} <= {S1, 1'b1};

當目前state為S0且輸入為1'b1時,output必須提前為1,因為這是在nonblocking內!!

37行

S1 : if (w_i) {curr_state, z_o} <= {S1, 1'b1};

同理,在目前state為S1且輸入為1'b1時,output也必須提前為1,也因為這是在nonblocking內!!

使用1個always寫法的缺點:

1.因為使用nonblocking去描述output logic,所以要提早1個clk判斷,要特別小心處理,一不小心很容易弄錯
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寫法有幾個優點:

1.可忠實地反映出原本的Mealy FSM硬體架構
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合一)寫法有幾個優點:

1.程式碼較3個always寫法精簡
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合一)寫法的缺點:

將next state logic與output logic合一只會增加code的複雜度,日後維護會更加困難

不推薦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。

只要output logic使用nonblocking去描述,就無法實現Mealy FSM output。

實務上推薦的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

全文完。

转载自:http://www.cnblogs.com/oomusou/archive/2011/06/05/fsm_coding_style.html

有限狀態機FSM coding style整理 (SOC) (Verilog)的更多相关文章

  1. (转帖) 有限狀態機FSM coding style整理 (SOC) (Verilog)

    来源:http://www.codesoso.net/Record/101092_95120_21.html 来源:http://www.cnblogs.com/oomusou/archive/201 ...

  2. [Gem] AASM 狀態機

    @(Ruby on Rails)[rails, gem] 1234 # AASM is a continuation of the acts-as-state-machine rails plugin ...

  3. Linux 内核Coding Style整理

    转载:http://www.cnblogs.com/wang_yb/p/3532349.html 总结linux内核开发的coding style, 便于以后写代码时参考. 下面只是罗列一些规则, 具 ...

  4. 有限状态机FSM(自动售报机Verilog实现)

    有限状态机FSM(自动售报机Verilog实现) FSM 状态机就是一种能够描述具有逻辑顺序和时序顺序事件的方法. 状态机有两大类:Mealy型和Moore型. Moore型状态机的输出只与当前状态有 ...

  5. paper:synthesizable finite state machine design techniques using the new systemverilog 3.0 enhancements 之 FSM Coding Goals

    1.the fsm coding style should be easily modifiable to change state encoding and FSM styles. FSM 的的 状 ...

  6. SSIS 無法將保護的 XML 節點 "DTS:Password" 解密,錯誤為 0x8009000B "機碼用在特定狀態時無效

    发现之前部署的SSIS,执行失败,查看日志 來源: 描述: 無法將保護的 XML 節點 -- ::-- ::-- :: DataReader 來源 [] 描述: System.Exception: S ...

  7. Win10還原成最乾淨的狀態 不必重灌

    系統不穩定時我們想到的第一個選擇就是重灌,如果你的作業系統是win10將會有另外一個新選擇,就是透過程式進行還原,讓你的電腦回到剛安裝時的清爽. 工具資訊 [軟體名稱]微軟 Refresh Windo ...

  8. Google's C++ coding style

    v0.2 - Last updated November 8, 2013 源自 Google's C++ coding style rev. 3.274 目录 由 DocToc生成     头文件   ...

  9. Win10還原成最乾淨的狀態

    系統不穩定時我們想到的第一個選擇就是重灌,如果你的作業系統是win10將會有另外一個新選擇,就是透過程式進行還原,讓你的電腦回到剛安裝時的清爽. 工具資訊 [軟體名稱]微軟 Refresh Windo ...

随机推荐

  1. 漫话Asp.net

    经过一段时间的接触,对asp.net这一块进行了很多其它的了解,漫话一下. Asp.net与Web : asp.net属于动态网页技术,属于web应用程序开发. Web应用程序通常是B/S模式. 和B ...

  2. Oracle 之 获取当前日期及日期格式化

    Oracle 获取当前日期及日期格式: 获取系统日期:  SYSDATE 格式化日期: TO_CHAR(SYSDATE, 'YY/MM/DD HH24:MI:SS)                   ...

  3. asp.net 使用JS获得串口数据

    使用JS获得串口数据 JavaScript语言通常是一种网页编程语言,比较适合前台的一些应用设计.对于本地设备和数据的操作有比较大的限制.由于项目的需要,我需要获得本机的串口数据并显示在web端.我们 ...

  4. gzip和zipfile模块

    # -*- coding: utf-8 -*- #python 27 #xiaodeng #gzip和zipfile模块 #http://www.open-open.com/lib/view/open ...

  5. 4、第一个JAVA程序(Hello World)

    第一步: 新建一个文本文档,在里面输入内容 public class HelloWorld { public static void main(String[] args){ System.out.p ...

  6. Win7命令行下查看无线网络信息

    查看本机记录的wifi热点 C:\Users\Milton>netsh wlan show profiles Profiles on interface Wireless Network Con ...

  7. idea搭建简易ssm项目

    按照http://www.cnblogs.com/hackyo/p/6646051.html来搭建ssm框架,一步成功,特此记录一下. 本机环境: idea2017.3 jdk1.8 mysql5.5 ...

  8. 将数据库dbcp连接池改为c3p0连接池(草稿,别点)

    D:\Develop\apache-tomcat-7.0.52\bin\catalina.bat run[2017-12-26 02:31:29,174] Artifact pers:war expl ...

  9. php 内存管理

    内存是计算机⾮常关键的部件之⼀,是暂时存储程序以及数据的空间,CPU只有有限的寄存器可以⽤于存储计算数据,⽽⼤部分的数据都是存储在内存中的,程序运⾏都是在内存中进⾏的.和CPU计算能⼒⼀样, 内存也是 ...

  10. Android音乐播放器开发

    今日看书,看到这个播放器,我就写了个例子,感觉还行,这个播放器能播放后缀是.MP3的音乐,这个例子在main.xml设置listView的时候,注意:android:id="@+id/and ...