FPGA 學習之路(八)深入理解狀態機

狀態機定義及分類

什麼是狀態機:狀態機通過不同的狀態遷移來完成特定的邏輯操作(時序操作)

狀態機的分類:Moore型狀態機和Mealy型狀態機

Moore型:狀態機的變化只與當前的狀態有關
Mealy型:狀態機的變化不僅與當前的狀態有關,還與輸入有關

如何創建狀態機:狀態機的創建可以分爲一段式,兩段式和三段式:

一段式:所有的狀態變化以及導致的輸出變化都寫在了一個always快中。在該模塊中既描述狀態轉移,又描述狀態的輸入和輸出。

兩段式:用兩個always模塊來描述狀態機。其中一個模塊採用同步時序邏輯電路描述狀態轉移,另一個模塊採用組合邏輯判斷狀態轉移條件。它需要兩個狀態——現態和次態,然後通過現態和次態的轉換來實現時序邏輯。

三段式:用三個always模塊來描述狀態機。其中一個模塊採用同步時序邏輯電路描述狀態轉移,另一個模塊採用組合邏輯判斷狀態轉移條件(注意和兩段式的區別)。第三個模塊描述狀態的輸出(既可以用組合邏輯也可以用時序邏輯)

三段式狀態機有什麼優點?

爲了說明這個問題,我們先來看另外一個問題:

(1)組合電路中的競爭與冒險

1、競爭與冒險產生的原因:在組合電路中信號的高低變化是需要一定的過渡時間的。當多路信號的電平發生變化時,在信號變化的瞬間,組合邏輯的輸出也有先後順序,並不是同步變化。比如000101要變化到00100 在變化的過程中可能會出現00110信號,被稱爲尖峯信號“毛刺”。時鐘端口、清零和置位端口對毛刺信號十分敏感。(在數字電路中,信號由於經由不同路徑傳輸達到某一匯合點的時間有先有後的現象,就稱之爲競爭;由於競爭現象所引起的電路輸出發生瞬間錯誤的現象,就稱之爲冒險;)
2、解決競爭與冒險的方案
(1)採用格雷碼取代普通的二進制計數器
(2) D觸發器是一種比較傳統的去除毛刺的方法。利用D觸發器對輸入信號的毛刺不敏感的特點(只要毛刺不出現在上升沿並且滿足數據的建立和保持時間就不會對系統造成傷害),從而去除信號中的毛刺。

(2)三段式狀態機的優點

1、一段式狀態機只涉及時序電路,沒有競爭與冒險,同時消耗邏輯比較少。但是如果狀態非常多,一段式狀態機顯得比較臃腫,不利於維護。
2、兩段式狀態機:當一個模塊採用時序(狀態轉移),一個模塊採用組合時候(狀態機輸出),組合邏輯電路容易造成競爭與冒險;當兩個模塊都採用時序,可以避免競爭與冒險的存在,但是整個狀態機的時序上會延時一個週期。
3、三段式狀態機:三段式狀態機在狀態轉移時採用組合邏輯電路+格雷碼,避免了組合邏輯的競爭與冒險;狀態機輸出採用了同步寄存器輸出,也可以避免組合邏輯電路的競爭與冒險;採用這兩種方法極大的降低了競爭冒險。並且在狀態機的採用這種組合邏輯電路+次態寄存器輸出,避免了兩段式狀態機的延時一個週期(三段式狀態機在上一狀態中根據輸入條件判斷當前狀態的輸出,從而在不插入額外時鐘節拍的前提下,實現寄存器的輸出)。

靈活選擇狀態機,不一定要拘泥理論,怎樣方面怎樣來

三段式狀態機模板:

module three_fsm(clk,rst_n,a,z);  

    input clk;
    input rst_n;      
    input a;      

    output reg z;      

    reg [5:0] current_state;
    reg [5:0] next_state;    

    parameter S0 = 6'b00_0001;      
    parameter S1 = 6'b00_0010;      
    parameter S2 = 6'b00_0100;      
    parameter S3 = 6'b00_1000;    
    parameter S4 = 6'b01_0000;    
    parameter S5 = 6'b10_0000;  

    //第一個進程,同步時序always模塊,格式化描述次態寄存器遷移到現態寄存器
    //相當於是一個D觸發器

    always@(posedge clk or negedge rst_n)      
    begin          
        if(!rst_n)              
            current_state <= S0;        
        else
            current_state <= next_state;   
    end 

    //第二個進程,組合邏輯always模塊,描述狀態轉移條件判斷     

    always@(*)                     
     begin             
        case(current_state)                
         S0: next_state = (a==1)?S1:S0;            
         S1: next_state = (a==0)?S2:S1;            
         S2: next_state = (a==0)?S3:S1;     
         S3: next_state = (a==1)?S4:S0;         
         S4: next_state = (a==0)?S5:S1;       
         S5: next_state = (a==0)?S3:S1;       
         default: next_state = S0;          
        endcase    
     end 

    //第三個進程,同步時序always模塊,格式化描述次態寄存器輸出

    always@(posedge clk or negedge rst_n)       
    begin         
        if(!rst_n)    
            z = 0;      
        else     
            case(next_state) 
                S0: z = 0;
                S1: z = 0;
                S2: z = 0;    
                S3: z = 0;
                S4: z = 0;
                S5: z = 1;     
                default: z = 0;        
            endcase    
     end 

endmodule 
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章