Verilog學習筆記一 狀態機


  有限狀態機(FiniteStateMachine, FSM),是由寄存器組合組合邏輯構成的硬件時序電路。

  有限狀態機一般包含:

          1.輸入;

          2.狀態;

          3.狀態轉移條件;

          4.輸出。

   三段式狀態機:三段式狀態機比一段式狀態機和兩段式狀態機更加簡潔,清晰明了,更好的描述狀態的轉移。

三段式狀態機一般有三個always塊組成。

  第一個always塊使用同步時序邏輯電路,描述狀態的跳轉條件。

 1 //==============================================================================
 2   //三段式狀態機第一段,時許邏輯電路,描述初始狀態
 3   //
 4   //
 5   //==============================================================================
 6    always@(posedge clk_50MHZ or negedge rst_n)
 7        begin
 8            if(!rst_n)
 9                Current_state <= IDLE;
10            else 
11                Current_state <= Next_state;
12        end

  第二個always塊使用組合邏輯電路,描述狀態的跳轉。

//==============================================================================
  //三段式狀態機第二段,組合邏輯電路,描述狀態跳轉
  //
  //
  //==============================================================================    
   always @(*)
       begin
               case(Current_state)
               IDLE:
                   begin
                       if(key_negedge)
                            begin
                                cnt_en<=1'b1;
                                Next_state<= S1;
                            end
                    else 
                        begin
                            cnt_en<=1'b0;
                            Next_state<= IDLE;
                        end
                end
            
            S1:
                begin
                    if(cnt_full)
                        begin
                            cnt_en<=1'b0;
                            Next_state<= S2;
                        end
                    else 
                        begin
                            if(key_posedge)
                                begin
                                    cnt_en<=1'b0;
                                    Next_state<= IDLE;
                                end
                            else
                                begin
                                    cnt_en<=1'b1;
                                    Next_state<= S1;
                                end
                        end
                end
            
            S2:
                begin
                    if(key_posedge)
                        begin
                            cnt_en<=1'b1;
                            Next_state<= S3;
                        end
                    else
                        begin
                            cnt_en<=1'b0;
                            Next_state<= S2;
                        end
                end
            
            S3:
                begin
                    if(cnt_full)
                        begin
                            cnt_en<=1'b0;
                            Next_state<= IDLE;
                        end
                    else 
                        begin
                            if(key_negedge)
                                begin
                                    cnt_en<=1'b0;
                                    Next_state<= S2;
                                end
                            else
                                begin
                                    cnt_en<=1'b1;
                                    Next_state<= S3;
                                end
                        end
                end
            default: ;
            endcase
    end

  第三個always塊使用組合邏輯電路,描述狀態輸出。

  

//==============================================================================
  //三段式狀態機第三段,組合邏輯電路。
  //
  //
  //==============================================================================
   always @(Current_state or rst_n)
        begin
            if(!rst_n)
                    key_flag<=1'b1;
            else 
                begin
                    case(Current_state)
                    IDLE:
                        key_flag<=1'b1;
                    S1:
                        key_flag<=1'b1;
                    S2:    
                        key_flag<=1'b0;
                    S3:
                        key_flag<=1'b0;

                    default :key_flag<=1'b1;
                    endcase
                end
        end

注意:

  1.三段式狀態機中狀態的定義這里采用獨熱碼的形式來定義,可以更加簡潔明了,缺點是當狀態過多的情況下使用的寄存器位數會增加。

//==============================================================================
  //狀態跳轉條件:
  //
  //
  //==============================================================================
   localparam IDLE    =4'b0001    ;//按鍵沒有按下,空閑狀態
   localparam S1        =4'b0010     ;//按鍵按下,濾除抖動狀態
   localparam S2        =4'b0100     ;//按鍵按下,穩定狀態
   localparam S3        =4'b1000     ;//按鍵釋放,濾除抖動狀態
   
   reg [3:0] Current_state        ;//初態
   reg [3:0] Next_state             ;//次態

  2.狀態機的第三段可以使用組合邏輯電路輸出,也可以使用時序邏輯電路輸出,一般推薦使用時序電路輸出,因為狀態機的設計和其它設計一樣,最好使用同步時序方式設計,以提高設計的穩定性,消除毛刺。

 

  本文使用按鍵消抖的例子,使用三段式狀態機進行按鍵軟件消抖。完整代碼如下:

`timescale 1ns / 1ps
//////////////////////////////////////////////////////////////////////////////////
// Company: 
// Engineer: 
// 
// Create Date:    20:54:39 09/03/2019 
// Design Name: 
// Module Name:    key_filter_top 
// Project Name:   key_filter
// Target Devices: XC6LX9
// Tool versions: 
// Description: 通過三段式狀態機方式實現按鍵消抖功能
//
// Dependencies: 
//
// Revision: 
// Revision 0.01 - File Created
// Additional Comments: 
//
//////////////////////////////////////////////////////////////////////////////////
module key_filter_top
    (
        input                clk_50MHZ            ,//系統時鍾50M
        input             rst_n                ,//系統復位
        input                key_in                ,//按鍵
        output    reg [3:0] led    ,              //4位LED燈
        output reg key_flag,
        output led_flag
   ); 
   
   
    reg       key_temp0     ;//用於按鍵上一次狀態存儲
    reg       key_temp1     ;//用於按鍵當前狀態存儲
    wire      key_posedge     ;//按鍵上升沿
    wire      key_negedge     ;//按鍵下降沿
    //wire       led_flag;
    reg  [20:0]cnt            ;//20位2進制計數器
    reg        cnt_en         ;//下降沿計數使能
    reg        cnt_full        ;//上升沿計數使能
    reg       key_flag_temp0     ;//用於按鍵上標志一次狀態存儲
    reg       key_flag_temp1     ;//用於按鍵標志當前狀態存儲
    
  //===============================================================================
  //
  //邊沿檢測,采用寄存器方式。
  //
  //===============================================================================
  always@(posedge clk_50MHZ or negedge rst_n)
        begin
            if(!rst_n)
                begin
                    key_temp0<=1'b0;
                    key_temp1<=1'b0;
                end
            else
                begin
                    key_temp1<=key_in;
                    key_temp0<=key_temp1;
                end
        end
        
        assign key_posedge=(!key_temp0)&key_temp1;
        assign key_negedge=(!key_temp1)&key_temp0;
        

    //===============================================================================
  //
  //檢測到計時器使能開始計數器計數20mS
  //
  //===============================================================================    
    always@(posedge clk_50MHZ or negedge rst_n)
        begin
            if(!rst_n)
                cnt<=1'b0;
            else if(cnt_en==1)
                begin 
                    if(cnt==20'd9)
                        cnt<=1'b0;
                    else
                        cnt<=cnt+1'b1;
                end
            else
                cnt<=1'b0;
        end
    
    always@(posedge clk_50MHZ or negedge rst_n)
        begin
            if(!rst_n)
                cnt_full<=1'b0;
            else if(cnt==20'd9)
                cnt_full<=1'b1;
            else
                cnt_full<=1'b0;
        end
        
  //==============================================================================
  //狀態跳轉條件:
  //
  //
  //==============================================================================
   localparam IDLE    =4'b0001    ;//按鍵沒有按下,空閑狀態
   localparam S1        =4'b0010     ;//按鍵按下,濾除抖動狀態
   localparam S2        =4'b0100     ;//按鍵按下,穩定狀態
   localparam S3        =4'b1000     ;//按鍵釋放,濾除抖動狀態
   
   reg [3:0] Current_state        ;//初態
   reg [3:0] Next_state             ;//次態

                
  //==============================================================================
  //三段式狀態機第一段,時許邏輯電路,描述初始狀態
  //
  //
  //==============================================================================
   always@(posedge clk_50MHZ or negedge rst_n)
       begin
           if(!rst_n)
               Current_state <= IDLE;
           else 
               Current_state <= Next_state;
       end
  //==============================================================================
  //三段式狀態機第二段,組合邏輯電路,描述狀態跳轉
  //
  //
  //==============================================================================    
   always @(*)
       begin
               case(Current_state)
               IDLE:
                   begin
                       if(key_negedge)
                            begin
                                cnt_en<=1'b1;
                                Next_state<= S1;
                            end
                    else 
                        begin
                            cnt_en<=1'b0;
                            Next_state<= IDLE;
                        end
                end
            
            S1:
                begin
                    if(cnt_full)
                        begin
                            cnt_en<=1'b0;
                            Next_state<= S2;
                        end
                    else 
                        begin
                            if(key_posedge)
                                begin
                                    cnt_en<=1'b0;
                                    Next_state<= IDLE;
                                end
                            else
                                begin
                                    cnt_en<=1'b1;
                                    Next_state<= S1;
                                end
                        end
                end
            
            S2:
                begin
                    if(key_posedge)
                        begin
                            cnt_en<=1'b1;
                            Next_state<= S3;
                        end
                    else
                        begin
                            cnt_en<=1'b0;
                            Next_state<= S2;
                        end
                end
            
            S3:
                begin
                    if(cnt_full)
                        begin
                            cnt_en<=1'b0;
                            Next_state<= IDLE;
                        end
                    else 
                        begin
                            if(key_negedge)
                                begin
                                    cnt_en<=1'b0;
                                    Next_state<= S2;
                                end
                            else
                                begin
                                    cnt_en<=1'b1;
                                    Next_state<= S3;
                                end
                        end
                end
            default: ;
            endcase
    end
   
  //==============================================================================
  //三段式狀態機第三段,組合邏輯電路。
  //
  //
  //==============================================================================
   always @(Current_state or rst_n)
        begin
            if(!rst_n)
                    key_flag<=1'b1;
            else 
                begin
                    case(Current_state)
                    IDLE:
                        key_flag<=1'b1;
                    S1:
                        key_flag<=1'b1;
                    S2:    
                        key_flag<=1'b0;
                    S3:
                        key_flag<=1'b0;

                    default :key_flag<=1'b1;
                    endcase
                end
        end

  //===============================================================================
  //
  //邊沿檢測,采用寄存器方式。
  //
  //===============================================================================
  always@(posedge clk_50MHZ or negedge rst_n)
        begin
            if(!rst_n)
                begin
                    key_flag_temp0<=1'b0;
                    key_flag_temp1<=1'b0;
                end
            else
                begin
                    key_flag_temp0<=key_flag;
                    key_flag_temp1<=key_flag_temp0;
                end
        end
        assign led_flag=key_flag_temp1&(!key_flag_temp0);
        
    always @(posedge led_flag or negedge rst_n)
        begin
            if(!rst_n)
                led<=4'b0001;
            else if(led==4'b1000)
                led<=4'b0001;
            else
                led<=(led<<1);
        end
    
        
endmodule

  按鍵消抖testbench仿真代碼如下:

`timescale 1ns / 1ps

////////////////////////////////////////////////////////////////////////////////
// Company: 
// Engineer:
//
// Create Date:   17:46:30 02/23/2020
// Design Name:   key_filter_top
// Module Name:   C:/mydesign/key_filter/key_filtertb.v
// Project Name:  key_filter
// Target Device:  
// Tool versions:  
// Description: 
//
// Verilog Test Fixture created by ISE for module: key_filter_top
//
// Dependencies:
// 
// Revision:
// Revision 0.01 - File Created
// Additional Comments:
// 
////////////////////////////////////////////////////////////////////////////////

module key_filtertb;

    // Inputs
    reg clk_50MHZ;
    reg rst_n;
    reg key_in;

    // Outputs
    wire [3:0] led;
    wire key_flag;
    wire led_flag;

    // Instantiate the Unit Under Test (UUT)
    key_filter_top uut (
        .clk_50MHZ(clk_50MHZ), 
        .rst_n(rst_n), 
        .key_in(key_in), 
        .led(led),
        .key_flag(key_flag),
        .led_flag(led_flag)
    );

    initial begin
        // Initialize Inputs
        clk_50MHZ = 0;
        rst_n = 0;
        key_in = 1;

        // Wait 100 ns for global reset to finish
        #10;
        rst_n = 1;
        key_in = 1;
        #10;
        key_in = 0;
        #10;
        key_in = 1;
        #10;
        key_in = 0;
        #10;
        key_in = 1;
        #10;
        key_in = 0;
        #200;
        key_in = 1;
        #10;
        key_in = 0;
        #10;
        key_in = 1;
        #10;
        key_in = 0;
        #10;
        key_in = 1;
        #10;
        key_in = 0;
        #10;
        key_in = 1;
        #200;
        key_in = 0;
        #10;
        key_in = 1;
        #10;
        key_in = 0;
        #10;
        key_in = 1;
        #10;
        key_in = 0;
        #200;
        key_in = 1;
        #10;
        key_in = 0;
        #10;
        key_in = 1;
        #10;
        key_in = 0;
        #10;
        key_in = 1;
        #10;
        key_in = 0;
        #10;
        key_in = 1;
        #200;
        
        // Add stimulus here
    end
    always #5 clk_50MHZ=~clk_50MHZ;
    
      
endmodule

仿真波形如下:

 


免責聲明!

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



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