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

仿真波形如下:

猜你喜欢

转载自www.cnblogs.com/xiaozhu5208/p/12358873.html