基於FPGA的有限狀態機淺析


  前言:狀態機大法好,狀態機幾乎可以實現一切時序邏輯電路。

有限狀態機(Finite State Machine, FSM),根據狀態機的輸出是否與輸入有關,可分為Moore型狀態機和Mealy型狀態機。Moore型狀態機輸出僅僅與現態有關和Mealy型狀態機不僅與現態有關,也與輸入有關,所以會受到輸入的干擾,可能會產生毛刺(Glith)的現象,所以我們通常使用的是Moore型狀態機。

         狀態機的編碼,二進制編碼(Binary),格雷碼編碼(Gray-code),獨熱碼(One-hot)。不同的編碼方式是防止在狀態轉移中發生突變,使得狀態轉移更為穩定,系統更加可靠,但是通常情況下我們直接采用的是二進制進行編碼,除非系統對穩定性和狀態編碼有特殊要求。

         狀態機的描述,一段式、二段式、三段式。

一段式狀態機,將組合邏輯和時序邏輯混合在一起,這樣的寫法對於邏輯簡單的狀態機來說還是可以使用的,但是對於復雜的邏輯就不推薦了,如果狀態復雜也會容易出錯,而且一個always塊中信號太多也不利於維護和修改。

 1 //狀態參數聲明
 2 parameter     S0    =    4'b0000,
 3             S1    =    4'b0001,
 4             s2    =    4'b0010;
 5 //FSM one segment
 6 reg     [3:0]    state;
 7 always @(posedge clk or negedge rst_n)begin
 8     if(!rst_n)
 9         state <= S0;
10     else begin
11         case(state)
12         S0:
13         S1:
14         S2:
15         .
16         .
17         .
18         default:
19         endcase 
20     end
21 end

兩段式狀態機也是一種常用的寫法,它把組合邏輯和時序邏輯區分出來,第一段負責狀態的轉移,第二段是組合邏輯賦值,但是這種寫法的缺點是,組合邏輯較容易產生毛刺等常見問題,關於組合邏輯較容易產生毛刺原因,下文會提到。

 1 //狀態參數聲明
 2 parameter     S0    =    4'b0000,
 3             S1    =    4'b0001,
 4             s2    =    4'b0010;
 5 //FSM two segment
 6 reg     [3:0]    pre_state;
 7 reg     [3:0]    next_state;
 8 //--------------------------------------
 9 //FSM one
10 always @(posedge clk or negedge rst_n)begin
11     if(!rst_n)
12         pre_state <= S0;
13     else 
14         pre_state <= next_state;
15 end
16 
17 //FSM two
18 always    @(*)begin
19     case(pre_state)
20     S0:
21     S1:
22     S2:
23     .
24     .
25     .
26     default:;
27     endcase
28 
29 end

三段式狀態機就可以較好的解決一段二段的不足,我也是比較推薦的寫法,第一段采用時序邏輯負責狀態轉移,第二段組合邏輯負責數據賦值,第三段時序邏輯負責輸出,代碼層次清晰,容易維護,時序邏輯的輸出解決了兩段式寫法中組合邏輯的毛刺問題。但是資源消耗會多一些,此外,三段式從輸入到輸出會比一段式和二段式延遲一個時鍾周期。在書寫狀態機的時候,一定要事先設計好狀態轉移圖,將所有的狀態都考慮到,避免狀態進入死循環,或者跳到偏離態。

 1 //狀態參數聲明
 2 parameter     S0    =    4'b0000,
 3             S1    =    4'b0001,
 4             s2    =    4'b0010;
 5 //FSM three segment
 6 //--------------------------------------
 7 //FSM one
 8 always @(posedge clk or negedge rst_n)begin
 9     if(!rst_n)
10         pre_state <= S0;
11     else 
12         pre_state <= next_state;
13 end
14 
15 //FSM two
16 always    @(*)begin
17     case(pre_state)
18     S0:
19     S1:
20     S2:
21     .
22     .
23     .
24     default:;
25     endcase
26 end
27 
28 //FSM three
29 always    @(posedge clk or negedge rst_n)begin
30     if(!rst_n)
31         dout <= 'b0;
32     else begin
33         case(pre_state)
34         S0:    
35         S1:
36         S2:
37         .
38         .
39         .
40         default:;
41         endcase
42     end
43 end

         如下圖,我通過一個實例來說明一下狀態機的使用。下面是一個序列檢測狀態轉移圖,檢測是的使1101這個序列,我們給這個序列的檢測序列是11101 1101這一串數據。在這個序列檢測器中,我們允許使用重復位。也就是說,前一個“1101”最后一位的1可以作為后一個“1101”序列的起始位。如果不允許重復為位,只需要將S4到S2的轉移替換成S4到S1即可。

         首先,從輸出狀態S0開始檢測,當S0檢測到1時跳到S1,否則跳回S0,S1檢測到1狀態跳到S2,否則跳回S0,S2檢測到0狀態跳到S3,否則還停留在S2狀態,因為這里我們的檢測序列允許重復位,所以S1檢測到的1與S2檢測到的1保留,不舍棄作為一下組1101的前兩位,所以只需要繼續檢測下一位數據即可。S3、S4的狀態一次類推。這里舉着個例子是為了說明狀態機的狀態跳轉,在我們實際的設計中這種情況也是會遇到的。

         在使用狀態機來描述時序電路的時候,首先應該做的是畫出狀態轉移圖,然后根據狀態跳轉來描述代碼,最后便會事半功倍。這段序列檢測的代碼我也貼出來。當然這只是序列檢測的一個應用了,我前面也說了狀態機機會可以實現一切的時序電路。如果你遇到實在不好解決的設計,那么這個時候,你就可以考慮一下使用狀態機了。

 1 module state(
 2     input                 mclk, 
 3     input                rst_n,
 4     input                din,
 5     output     reg         dout;
 6     );
 7      
 8 parameter         s0 = 3'b000,
 9                 s1 = 3'b001,
10                 s2 = 3'b010,
11                 s3 = 3'b011,
12                 s4 = 3'b100;//狀態
13 //此為三段式狀態機,還有一段式狀態機,二段式狀態機            
14 reg [2:0] present_state, next_state;
15 //用摩爾狀態機設計1101序列檢測器
16 //狀態寄存器
17 always @(posedge mclk or negedge rst_n)
18 begin
19     if(!rst_n)
20         present_state <= s0;
21     else 
22         present_state <= next_state;
23 end
24 
25 //狀態轉換模塊
26 always @(*)
27 begin
28     case(present_state)
29     s0: if(din==1)
30             next_state = s1;
31          else 
32             next_state = s0;
33     s1: if(din==1)
34             next_state = s2;
35         else 
36             next_state = s0;
37     s2: if(din==0)
38             next_state = s3;
39         else 
40             next_state = s2;
41     s3: if(din==1)
42             next_state = s4;
43         else 
44             next_state = s0;
45     s4: if(din==0)
46             next_state = s0;
47         else 
48             next_state = s2;
49     default: next_state = s0;
50     endcase
51 end
52 
53 always @(posedge clk or negedge rst_n)begin
54     if(!rst_n)
55         dout <= 1'b0;
56     else if(present_state ==s4)
57         dout <= 1'b1;
58     else
59         dout <= 1'b0;
60 end
61      
62
63 endmodule

         在狀態機的設計中,一段式狀態機用時序邏輯,二段式狀態機第一段用時序邏輯,第二段用組合邏輯,三段式狀態機第一段用時序邏輯,第二段用組合邏輯,第三段用時序邏輯。我在設計的時候,嘗試把第二段寫成時序邏輯,最終結果並沒有影響,時序邏輯隨時鍾變化,組合邏輯是直接賦值,所以在第三段狀態機進行輸出時,輸出結果肯定是穩定的,但是這樣會限制fmax。如果用時序邏輯的主頻率過高的話,可能不如第二段組合邏輯賦值來的穩定,這里就還需要考慮到時序分析了,暫且不談。這里還需要提的是使用三段式狀態機相較於一段二段式,會延遲一個時鍾周期輸出,就是因為第三段使用了時序邏輯的緣故。

         既然談狀態機的時候,說到了組合邏輯會產生毛刺的現象,那么這里就順便整理一下,為什么組合邏輯會產生毛刺,組合邏輯的冒險與競爭分析。

         競爭(Competition)在組合邏輯電路中,某個輸入變量通過兩條或兩條以上的途徑傳到輸出端,由於每條途徑延遲時間不同,到達輸出門的時間就有先有后,這種現象稱為競爭。把不會產生錯誤輸出的競爭的現象稱為非臨界競爭。把產生暫時性的或永久性錯誤輸出的競爭現象稱為臨界競爭。

冒險(risk)信號在器件內部通過連線和邏輯單元時,都有一定的延時。延時的大小與連線的長短和邏輯單元的數目有關,同時還受器件的制造工藝、工作電壓、溫度等條件的影響。信號的高低電平轉換也需要一定的過渡時間。由於存在這兩方面因素,多路信號的電平值發生變化時,在信號變化的瞬間,組合邏輯的輸出有先后順序,並不是同時變化,往往會出現一些不正確的尖峰信號,這些尖峰信號稱為"毛刺"。如果一個組合邏輯電路中有"毛刺"出現,就說明該電路存在冒險

競爭冒險(Competition risk)產生原因:由於延遲時間的存在,當一個輸入信號經過多條路徑傳送后又重新會合到某個門上,由於不同路徑上門的級數不同,或者門電路延遲時間的差異,導致到達會合點的時間有先有后,從而產生瞬間的錯誤輸出。

       首先看下面這個電路,使用了兩個邏輯門,一個非門和一個與門,本來在理想情況下F的輸出應該是一直穩定的0輸出,但是實際上每個門電路從輸入到輸出是一定會有時間延遲的,這個時間通常叫做電路的開關延遲。而且制作工藝、門的種類甚至制造時微小的工藝偏差,都會引起這個開關延遲時間的變化。

         實際上如果算上非門的延遲的話,那么F最后就會產生毛刺。信號由於經由不同路徑傳輸達到某一匯合點的時間有先有后的現象,就稱之為競爭,由於競爭現象所引起的電路輸出發生瞬間錯誤的現象,就稱之為冒險,所以在設計中我們要注意避免這個現象,最簡單的避免方法是盡量使用時序邏輯同步輸出。

      這篇狀態機和組合邏輯的冒險競爭就聊到這里,下次我們接着說時序邏輯的冒險競爭。

 

參考資料:百度百科,冒險競爭、《FPGA設計技巧與案例開發詳解》、《FPGA數字邏輯設計教程——Verilog》、《深入淺出玩轉FPGA》等網絡文章。

 

轉載請注明出處:NingHeChuan(寧河川)

個人微信訂閱號:開源FPGA

如果你想及時收到個人撰寫的博文推送,可以掃描左邊二維碼(或者長按識別二維碼)關注個人微信訂閱號

知乎ID:NingHeChuan

微博ID:NingHeChuan

原文地址:http://www.cnblogs.com/ninghechuan/p/7898297.html

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM