有限状态机(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
仿真波形如下:
原文:https://www.cnblogs.com/xiaozhu5208/p/12358873.html