前言:狀態機大法好,狀態機幾乎可以實現一切時序邏輯電路。
有限狀態機(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