FPGA-Key Debounce State Machine Design and Verification

FPGA-key debounce design and verification

Why do we need to debounce the buttons?

The following figure shows the application of the button in the actual circuit. When there is no button pressed, the keys[2:0] will pull it up to a high level. First, there is a reaction spring in the hardware structure of the button. When pressed or released At times, additional physical vibrations are generated, causing the button level to jitter simultaneously.

Insert picture description here
During the process from pressing to releasing the key, the ideal and actual changes of the level are shown in the figure below.
Insert picture description here

It can be seen that the number and interval of the jitter are randomly generated. The only certainty is that under normal circumstances, the time of pressing the jitter and releasing the jitter will last within 5ms~10ms.

State machine plus counter to realize key debounce

State machine transition state

In the actual change of the button signal level in the above figure, there are four states:

1. High level stable period, the so-called idle state;

2. Press jitter. This state indicates that during the process of changing from high level to low level, after the falling edge is detected, the rising edge is detected within 10ms, then the button is determined to be still jittering and return to idle state idle; named filter0 ;

3. In the low-level stable period, after detecting the falling edge, the low-level is stable within 10ms, and it is determined that the button is pressed; it is named as the down state;

4. Release jitter. This state indicates that during the process of changing from low level to high level, after the rising and falling edge is detected, the falling rising edge is detected within 10ms, then the button is considered to be still jittering, and it returns to the down state; named filter1 .
Insert picture description here

Asynchronous signal synchronous processing

Why do asynchronous signal synchronous processing?

First, the key signal is an asynchronous signal for the internal signal of the FPGA (when a signal crosses a certain clock domain, it is an asynchronous signal for the circuit of the new clock domain, and the circuit receiving the signal needs to synchronize it), That is, the button signal state does not depend on the FPGA device clock. If it is not processed, it will easily lead to a metastable state (the signal state cannot be predicted). The synchronization process can prevent the metastable state from spreading in the new clock domain.

Insert picture description here

A simple synchronizer is made up of two registers in series with no other combinational circuit in between. As shown in the figure above, this design can ensure that when the following flip-flop obtains the output of the previous flip-flop, the previous flip-flop has exited metastable State, and the output is stable.

Insert picture description here
Insert picture description here

For this experimental design, this part is only a synchronization process for a key change signal to prevent the key from interfering with other signals in the new clock domain, and does not judge whether it is a rising edge or a falling edge.

Edge detection circuit design

Why do edge detection?

First, the asynchronous signal synchronization processing part does not judge the rising edge and the falling edge, which is another important signal for the state transition of the state machine.

Therefore, the function of the edge detection circuit is to detect the transition of the input signal or the internal logic signal, that is, the detection of the rising edge or the falling edge.

How to understand the edge detection circuit?

We know that in the sensitive signal list of the always block, you can directly use posedge and negedge to directly extract the rising and falling edges, but what if you want to detect the rising and falling edges inside the always block? Still use posedge and negedge? Obviously this is not possible. It is impossible to synthesize such a statement, and an error will be reported during compilation. In fact, posedge and negedge can only be used in the sensitive signal list or testbench of the always block, so the edge detection circuit application is generated.

Insert picture description here
The above figure is an edge detection circuit of a two-level register that is widely used. Generally, in order to prevent the fluctuation of the trigger signal, you can add a few more levels of flip-flops to eliminate jitter and make the signal more stable.

The principle of edge detection is to use the characteristic that the input state is the output state at the next moment of the register under the control of the clock signal for comparison and judgment.

Insert picture description here

How does the above circuit detect the rising or falling edge?

Rising edge detection

1. Before the rising edge, the trigger_r signal has maintained a low level for a period of time, the trigger_rf and trigger_rs registers both maintain the output low level, and one of the signals undergoes an AND (&) operation with the other signal through the NOT gate, pos_edge and Both neg_edge output low level.

2. When the trigger_r signal changes from 0 to 1, which is the rising edge, when the first clock edge arrives, the output of the first register trigger_rf is 1, and the second register trigger_rs needs to be when the second clock edge arrives. Output 1, so at the first clock edge, the output 0 of the second register trigger_rs passes through the NOT gate and the output 1 of the first register trigger_rf performs an AND (&) operation, at this time pos_edge outputs a high level, and neg_edge remains Output low level, therefore, use pos_edge to detect the rising edge.

Insert picture description here
3. When the second clock of clk comes, trigger_r still keeps high level at this time, but at this time trigger_rf and trigger_rs registers keep outputting high level, causing pos_edge and neg_edge to still output low level, which explains when there is When the rising edge arrives, pos_edge also generates a high level of one clock cycle. If there is no rising or falling edge change, both pos_edge and neg_edge remain low.

Falling edge detection

1. Before the rising edge, the trigger_r signal has maintained a high level for a period of time, the trigger_rf and trigger_rs registers both maintain the output high level, and one of the signals passes through the NOT gate and performs an AND (&) operation with the other signal, pos_edge and Both neg_edge output low level.

2. When the trigger_r signal changes from 1 to 0, that is, the falling edge, when the first clock edge arrives, the output of the first register trigger_rf is 0, and the second register trigger_rs needs to be when the second clock edge arrives. Output 0, so at the first clock edge, the output 0 of the first register trigger_rf passes through the NOT gate and the output 1 of the second register trigger_rs for the AND (&) operation, at this time neg_edge outputs high level, pos_edge remains the same The output is low, so neg_edge is used to detect the falling edge.

Insert picture description here

3. When the second clock of clk comes, trigger_r still remains low at this time, but at this time trigger_rf and trigger_rs registers keep output low, resulting in pos_edge and neg_edge still output low, which explains when there is When the falling rising edge arrives, neg_edge also generates a high level of one clock cycle. There is no rising or falling edge change, so both pos_edge and neg_edge remain low.

The important logical idea here is that no matter the edge circuit detects a rising edge or a falling edge, it will generate a high-level pulse after detecting the required edge, and the rising edge outputs a high level for one clock cycle at pos_edge, and the falling edge The edge also outputs a high level for one clock cycle at neg_edge.

Counter module

This part is an important judgment sign for the state transition of the state machine. Because the jitter time of the button is between 5ms and 10ms, the counter is 10ms. When the counter is counting and the button state is stable, it is determined that the button enters the stable period.

Insert picture description here

Verilog code part

//--------------------------------------------------------------------------------------------
//      Component name  : key_filter
//      Author          : 硬件嘟嘟嘟
//      time            : 2020.04.17
//      Description     : 按键消抖设计
//      src             : FPGA系统设计与验证实战指南_V1.2
//--------------------------------------------------------------------------------------------

module key_filter(				
				clk,      //50m时钟
				rst_n,    //复位信号
				key_in,   //按键输入
				key_flag, //按键状态切换标志
				key_state //按键状态
);

input clk,rst_n;
input key_in;
output  reg key_flag,key_state;

//对key_in作异步信号同步处理
reg key_in_a,key_in_b;   //定义两个寄存器

always@(posedge clk,negedge rst_n)
	if(!rst_n)begin
			key_in_a <= 1'b1; 
			key_in_b <= 1'b1;
	end
	else begin  //input : key_in -->output :key_in_b  
			key_in_a <= key_in;
			key_in_b <=key_in_a;   
	end

//对key_in_b(异步信号同步处理输出)进行边沿检测

reg trigger_rf,trigger_rs;
wire pos_edge,neg_edge;

always@(posedge clk,negedge rst_n)
	if(!rst_n) begin
			trigger_rf <= 1'b0;
			trigger_rs <= 1'b0;
	end
	else begin
			trigger_rf <= key_in_b;
			trigger_rs <= trigger_rf;
	end

assign pos_edge = trigger_rf & (!trigger_rs);
assign neg_edge = (!trigger_rf) & trigger_rs;

//定义一个10ms计数器
reg [18:0]cnt;  //10_000_000 ns /20ns =500000
reg en_cnt ;  //定义使能寄存器,要让在按键抖动器件才开始计数

//使能信号产生,计数器开始计数
always@(posedge clk,negedge rst_n)
	if(!rst_n) 
			cnt <= 19'd0;
	else if(en_cnt)
			cnt <= cnt + 1'b1;
	else 
			cnt <= 19'd0;
			
//	计数器计满10ms后产生计满标志
reg cnt_full;   //计满标志

always@(posedge clk,negedge rst_n)
	if(!rst_n)
			cnt_full <= 1'b0;
	else if(cnt == 19'd499_999)
			cnt_full <= 1'b1;
	else
			cnt_full <= 1'b0;

//状态机状态转移

//localparam定义四种状态
localparam   key_idle   			= 3'd1,
             key_filter0 			= 3'd2,
				 key_down 				= 3'd3,
				 key_filter1 			= 3'd4;
  
  
reg  [2:0] current_state;   //定义当前态

//描述状态转移及输出
	always@(posedge clk or negedge rst_n)
	if(!rst_n)begin
		en_cnt <= 1'b0;
		current_state <= key_idle;
		key_flag <= 1'b0;
		key_state <= 1'b1;
	end
	else begin
		case(current_state)
			key_idle :
				begin
					key_flag <= 1'b0;
					if(neg_edge)begin
						current_state <= key_filter0;
						en_cnt <= 1'b1;
					end
					else
						current_state <= key_idle;
				end
					
			key_filter0:
				if(cnt_full)begin
					key_flag <= 1'b1;
					key_state <= 1'b0;
					en_cnt <= 1'b0;
					current_state <= key_down;
				end
				else if(pos_edge)begin
					current_state <= key_idle;
					en_cnt <= 1'b0;
				end
				else
					current_state <= key_filter0;
					
			key_down:
				begin
					key_flag <= 1'b0;
					if(pos_edge)begin
						current_state <= key_filter1;
						en_cnt <= 1'b1;
					end
					else
						current_state <= key_down;
				end
			
			key_filter1:
				if(cnt_full)begin
					key_flag <= 1'b1;
					key_state <= 1'b1;
					current_state <= key_idle;
					en_cnt <= 1'b0;
				end
				else if(neg_edge)begin
					en_cnt <= 1'b0;
					current_state <= key_down;
				end
				else
					current_state <= key_filter1;
			
			default:
				begin 
					current_state <= key_idle; 
					en_cnt <= 1'b0;		
					key_flag <= 1'b0;
					key_state <= 1'b1;
				end
				
		endcase	
	end
endmodule

Insert picture description here

Incentive and simulation test

This part will continue to accumulate testbench writing skills and simulation ideas in learning small projects, and will be added after in-depth understanding of testbench simulation models.

A simple testbench code is attached for reference only for verification

//--------------------------------------------------------------------------------------------
//      src             : FPGA系统设计与验证实战指南_V1.2
//--------------------------------------------------------------------------------------------
`timescale 1ns/1ns

`define clk_period 20

module key_filter_tb;

	reg clk;
	reg rst_n;
	reg key_in;
	
	wire key_flag;
	wire key_state;
	
	key_filter key_filter0(
		.clk(clk),
		.rst_n(rst_n),
		.key_in(key_in),
		.key_flag(key_flag),
		.key_state(key_state)
	);
	
	initial clk= 1;
	always#(`clk_period/2) clk = ~clk;
	
	initial begin
		rst_n = 1'b0;
		key_in = 1'b1;
		#(`clk_period*10) rst_n = 1'b1;
		#(`clk_period*10 + 1);
		key_in = 1'b0;#1000;
		key_in = 1'b1;#2000;
		key_in = 1'b0;#1400;
		key_in = 1'b1;#2600;
		key_in = 1'b0;#1300;
		key_in = 1'b1;#200;
		key_in = 1'b0;#2000100;
		#50000100;
		
		key_in = 1'b1;#2000;
		key_in = 1'b0;#1000;
		key_in = 1'b1;#2000;
		key_in = 1'b0;#1400;
		key_in = 1'b1;#2600;
		key_in = 1'b0;#1300;
		key_in = 1'b1;#2000100;
		#50000100;
		
		key_in = 1'b0;#1000;
		key_in = 1'b1;#2000;
		key_in = 1'b0;#1400;
		key_in = 1'b1;#2600;
		key_in = 1'b0;#1300;
		key_in = 1'b1;#200;
		key_in = 1'b0;#2000100;
		#50000100;
		
		key_in = 1'b1;#2000;
		key_in = 1'b0;#1000;
		key_in = 1'b1;#2000;
		key_in = 1'b0;#1400;
		key_in = 1'b1;#2600;
		key_in = 1'b0;#1300;
		key_in = 1'b1;#2000100;
		#50000100;
		$stop;		
	end
	
endmodule

Guess you like

Origin blog.csdn.net/sinat_41653350/article/details/105580888