在Verilog中可以采用多種方法來描述有限狀態機最常見的方法就是用always和case語句。如下圖所示的狀態轉移圖就表示了一個簡單的有限狀態機:
圖中:圖表示了一個四狀態的狀態機,輸入為A和Reset,同步時鍾為clk,輸出信號是K1和K2,狀態機只能在信號的上升沿發生。
(A)下面是可綜合的Verilog模塊設計狀態機的典型方法:(格雷碼表示狀態)
1 module fsm(A,Reset,K2,K1,clk,state); 2 input A,Reset,clk; 3 output K2,K1; 4 output [1:0]state; 5 reg K2,K1; 6 reg [1:0]state; 7 parameter Idel=2'b00, 8 start=2'b01, 9 stop=2'b10, 10 clear=2'b11; 11 always @(posedge clk) 12 if (!Reset) 13 begin 14 state<=Idel; 15 K2<=0; 16 K1<=0; 17 end 18 else 19 case(state) 20 Idel:if (A) 21 begin 22 state<=start; 23 K1<=0; 24 end 25 else 26 begin 27 state<=Idel; 28 K2<=0; 29 K1<=0; 30 end 31 start:if(!A) state<=stop; 32 else state<=start; 33 stop: if(A) 34 begin 35 state<=clear; 36 K2<=1; 37 end 38 else 39 begin 40 state<=stop; 41 K2<=0; 42 K1<=0; 43 end 44 clear:if(!A) 45 begin 46 state<=Idel; 47 K2<=0; 48 K1<=1; 49 end 50 else 51 begin 52 state<=clear; 53 K2<=0; 54 K1<=0; 55 end 56 default:state<=2'bxx; 57 endcase 58 endmodule
(B)用可以綜合的Verilog模塊設計、用獨熱碼表示狀態的狀態機
獨熱碼,在英文文獻中稱做 one-hot code, 直觀來說就是有多少個狀態就有多少比特,而且只有一個比特為1,其他全為0的一種碼制。

1 module fsm(A,Reset,K2,K1,clk,state); 2 input A,Reset,clk; 3 output K2,K1; 4 output [3:0]state; 5 reg K2,K1; 6 reg [3:0]state; 7 parameter Idel=4'b1000, 8 start=4'b0100, 9 stop=4'b0010, 10 clear=4'b0001; 11 always @(posedge clk) 12 if (!Reset) 13 begin 14 state<=Idel; 15 K2<=0; 16 K1<=0; 17 end 18 else 19 case(state) 20 Idel:if (A) 21 begin 22 state<=start; 23 K1<=0; 24 end 25 else 26 begin 27 state<=Idel; 28 K2<=0; 29 K1<=0; 30 end 31 start:if(!A) state<=stop; 32 else state<=start; 33 stop: if(A) 34 begin 35 state<=clear; 36 K2<=1; 37 end 38 else 39 begin 40 state<=stop; 41 K2<=0; 42 K1<=0; 43 end 44 clear:if(!A) 45 begin 46 state<=Idel; 47 K2<=0; 48 K1<=1; 49 end 50 else 51 begin 52 state<=clear; 53 K2<=0; 54 K1<=0; 55 end 56 default:state<=Idel; 57 endcase 58 endmodule
二進制編碼、格雷碼編碼使用最少的觸發器,消耗較多的組合邏輯,而獨熱碼編碼反之。獨熱碼編碼的最大優勢在於狀態比較時僅僅需要比較一個位,從而一定程度上簡化了譯碼邏輯。雖然在需要表示同樣的狀態數時,獨熱編碼占用較多的位,也就是消耗較多的觸發器,但這些額外觸發器占用的面積可與譯碼電路(組合電路)省下來的面積相抵消。並且采用獨熱碼可以使電路的速度和可靠性有顯著提高,而總的單元數並無增加。
(C)用可以綜合的Verilog模塊設計,用狀態碼直接作為輸出

1 module fsm(A,Reset,K2,K1,clk,state); 2 input A,Reset,clk; 3 output K2,K1; 4 output [4:0]state; 5 reg [4:0]state; 6 assign K2=state[4]; 7 assign K1=state[0]; 8 parameter Idel = 5'b0_000_0, 9 start = 5'b0_010_0, 10 stop = 5'b0_010_0, 11 stoptoclear=5'b1_100_0, 12 clear = 5'b0_101_0, 13 cleartoIdel=5'b0_011_1; 14 always @(posedge clk) 15 if (!Reset) 16 begin 17 state<=Idel; 18 end 19 else 20 case(state) 21 Idel: if (A) state<=start; 22 else state<=Idel; 23 start:if(!A) state<=stop; 24 else state<=start; 25 stop: if(A) state<=stoptoclear; 26 else state<=stop; 27 stoptoclear: state<=clear; 28 clear:if(!A) state<=cleartoIdel; 29 else state<=clear; 30 cleartoIdel: state<=Idel; 31 default:state<=Idel; 32 endcase 33 endmodule
利用狀態機的狀態直接作為輸出可以提升信號的開關速度並節省電路器件,但是開關的維持時間必須與狀態的維持時間一致,如果要實現上述的例子必須增加狀態才能實現。
在這里state[4],state[0]分別表示前面的K2和K1。
(D)用可以綜合的Verilog模塊設計,設計復雜多輸出狀態機時常用的方法

1 module fsm(A,Reset,K2,K1,clk,state); 2 input A,Reset,clk; 3 output K2,K1; 4 output [1:0]state; 5 reg K2,K1; 6 reg [1:0]state,nextstate; 7 parameter Idel = 2'b00, 8 start = 2'b01, 9 stop = 2'b10, 10 clear = 2'b11; 11 always @(posedge clk) //沒一個時鍾沿產生一次可能的變化 12 if (!Reset) 13 state<=Idel; 14 else 15 state<=nextstate; 16 always @(state or A) //產生下一狀態的組合邏輯F 17 case(state) 18 Idel: if (A) nextstate=start; 19 else nextstate=Idel; 20 start:if(!A) nextstate=stop; 21 else nextstate=start; 22 stop: if(A) nextstate=clear; 23 else nextstate=stop; 24 clear:if(!A) nextstate=Idel; 25 else nextstate=clear; 26 default: nextstate=2'bxx; 27 endcase 28 always @(state or Reset or A) //產生輸出K1的組合邏輯 29 if(!Reset) K1=0; 30 else if(state==clear&&!A) K1=1; //由clear轉向Idel,因為state為非阻塞賦值,所以此時state的狀態還是clear 31 else K1=0; 32 always @(state or Reset or A) //產生輸出K2的組合邏輯 33 if(!Reset) K2=0; 34 else if(state==stop&&A) K2=1; //由stop轉向clear,因為state為非阻塞賦值,所以此時state的狀態還是stop 35 else K2=0; 36 endmodule
在比較復雜的狀態機設計過程中,往往把狀態的變化與輸出開關的控制分為兩部分,就像前面的Mealy型狀態機輸出部分的組合邏輯一樣。為了調試方便還將每一個輸出的開關寫成一個個的獨立的always塊以方便調試。在設計復雜的多輸出狀態及時建議采用這種方法。
因為大多數的FPGA內部的觸發器數目相當多,又加上獨熱碼狀態機(one hot code machine)的譯碼邏輯最為簡單,所以在FPGA實現狀態機時,往往采用獨熱碼狀態機(即每個狀態只有一個寄存器置位的狀態機)。建議采用case語句來建立狀態機的模型,因為這些語句表達清晰明了,可以方便的由當前狀態轉向下一個狀態並設置輸出。記得:不要忘記在case語句的最后寫上default分支,並將狀態設置為'bx這就等於告訴綜合器case語句已經指定了所有的狀態。這樣綜合器就可以刪除不必要的譯碼電路使生成的電路簡潔。
如果將默認的狀態設置為某一確定值的狀態可不可以呢?
這樣會導致,雖然綜合器產生的邏輯和設置default:state=’bx;相同,但是狀態機的Verilog模型綜合前和綜合后的仿真結果不一致。因為啟動仿真器時,狀態機的所有狀態都不確定,因此立即進入default狀態。這時便會導致開始的狀態為state1,但是實際的硬件電路在通電以后,進入的狀態是不確定的,很可能不是state1的狀態,因此'bx更切合實際一些。但是再有多余狀態的情況下,可以通過哦綜合指令(高級教程)將默認狀態設置為某一確定的有效狀態,因為這樣能夠使得狀態機在偶然進入多余狀態后,仍能在下一時鍾跳變沿返回正常工作狀態,否則引起死鎖。
目前大多數綜合其往往不支持在一個always快中由多個事件觸發的狀態機(隱含狀態機,implicit state machine),為了能綜合出有效的電路,用Verilog描述的狀態機應明確的由唯一時鍾觸發。如果比需要用到不同時鍾觸發的狀態機,可以采用以下程序:編寫另一個模塊,並采用第二種時鍾觸發;然后用適中調用的方法再領一個模塊中將它們連接起來。為了使設計更簡單,調試更加容易,通常使得兩個狀態機的周期由一定的關系。
在Verilog中狀態必須明確賦值,使用參數parameter和define都可以實現:
參數定義:parameter state1=2'0,state2=2'1;
...
current_state=state1;
宏定義:`define state1=2'b0,state2=2'b1;
....
current_state=`state2;