Diseño y verificación de la máquina de estado de rebote de clave FPGA

Diseño y verificación antirrebote de clave FPGA

¿Por qué tenemos que eliminar el rebote de los botones?

La siguiente figura muestra la aplicación del botón en el circuito real. Cuando no hay ningún botón presionado, las teclas [2: 0] lo subirán a un nivel alto. Primero, hay un resorte de reacción en la estructura de hardware del Cuando se presiona o se suelta A veces, se generan vibraciones físicas adicionales, lo que hace que el nivel del botón tiemble simultáneamente.

Inserte la descripción de la imagen aquí
Durante el proceso desde presionar hasta soltar la tecla, los cambios ideales y reales del nivel se muestran en la siguiente figura.
Inserte la descripción de la imagen aquí

Puede verse que el número y el intervalo de la fluctuación se generan aleatoriamente. La única certeza es que, en circunstancias normales, el tiempo de presionar la fluctuación y liberar la fluctuación durará entre 5 ms ~ 10 ms.

Máquina de estado más contador para realizar el rebote de clave

Estado de transición de la máquina de estado

En el cambio real del nivel de señal del botón en la figura anterior, hay cuatro estados:

1. Período estable de alto nivel, el llamado estado inactivo;

2. Presione jitter. Este estado indica que durante el proceso de cambio de nivel alto a nivel bajo, después de que se detecta el borde descendente, el borde ascendente se detecta dentro de los 10 ms, luego se determina que el botón sigue temblando y vuelve al estado inactivo inactivo; llamado filter0 ;

3. En el período estable de nivel bajo, después de detectar el borde descendente, el nivel bajo es estable dentro de los 10 ms, y se determina que se presiona el botón, denominado como estado hacia abajo;

4. Suelte la fluctuación. Este estado indica que durante el proceso de cambio de nivel bajo a nivel alto, después de que se detecta el borde ascendente y descendente, el borde ascendente descendente se detecta dentro de los 10 ms, luego se considera que el botón todavía está fluctuando, y vuelve al estado inactivo; llamado filter1.
Inserte la descripción de la imagen aquí

Procesamiento síncrono de señales asíncronas

¿Por qué el procesamiento síncrono de señales asíncronas?

Primero, la señal clave es una señal asíncrona para la señal interna de la FPGA (cuando una señal cruza un cierto dominio de reloj, es una señal asíncrona para el circuito del nuevo dominio de reloj, y el circuito que recibe la señal necesita sincronizarla ), Es decir, el estado de la señal del botón no depende del reloj del dispositivo FPGA. Si no se procesa, conducirá fácilmente a un estado metaestable (el estado de la señal no se puede predecir). El proceso de sincronización puede evitar que el estado metaestable se extendiéndose en el nuevo dominio de reloj.

Inserte la descripción de la imagen aquí

Un sincronizador simple se compone de dos registros en serie sin ningún otro circuito combinacional en el medio. Como se muestra en la figura anterior, este diseño puede garantizar que cuando el siguiente flip-flop obtenga la salida del flip-flop anterior, el flip-flop anterior -flop ha salido del estado metaestable y la salida es estable.

Inserte la descripción de la imagen aquí
Inserte la descripción de la imagen aquí

Para este diseño experimental, esta parte es solo un proceso de sincronización para una señal de cambio de clave para evitar que la clave interfiera con otras señales en el nuevo dominio de reloj y no juzga si es un flanco ascendente o descendente.

Diseño de circuito de detección de bordes

¿Por qué la detección de bordes?

Primero, la parte de procesamiento de sincronización de señal asíncrona no juzga el borde ascendente y el borde descendente, que es otra señal importante para la transición de estado de la máquina de estado.

Por tanto, la función del circuito de detección de flanco es detectar la transición de la señal de entrada o la señal lógica interna, es decir, la detección del flanco ascendente o del flanco descendente.

¿Cómo entender el circuito de detección de bordes?

Sabemos que en la lista de señales sensibles del bloque always, puede usar directamente posedge y negedge para extraer directamente los bordes ascendentes y descendentes, pero ¿qué sucede si desea detectar los bordes ascendentes y descendentes dentro del bloque always? ¿Todavía usas posedge y negedge? Obviamente, esto no es posible. Es imposible sintetizar tal declaración, y se informará un error durante la compilación. De hecho, posedge y negedge solo se pueden usar en la lista de señales sensibles o en el banco de pruebas del bloque always, por lo que la detección de bordes Se genera una aplicación de circuito.

Inserte la descripción de la imagen aquí
La figura anterior es un circuito de detección de bordes de un registro de dos niveles que se usa ampliamente. Generalmente, para evitar la fluctuación de la señal de disparo, puede agregar algunos niveles más de flip-flops para eliminar la fluctuación y hacer que la señal mas estable.

El principio de detección de flancos es comparar y juzgar utilizando la característica de que el estado de entrada es el estado de salida en el momento siguiente bajo el control de la señal de reloj del registro.

Inserte la descripción de la imagen aquí

¿Cómo detecta el circuito anterior el flanco ascendente o descendente?

Detección de borde ascendente

1. Antes del flanco ascendente, la señal trigger_r ha mantenido un nivel bajo durante un período de tiempo, los registros trigger_rf y trigger_rs mantienen el nivel bajo de salida y una de las señales se somete a una operación AND (&) con la otra señal a través de la puerta NOT, pos_edge y Both neg_edge generan un nivel bajo.

2. Cuando la señal trigger_r cambia de 0 a 1, que es el flanco ascendente, cuando llega el primer flanco del reloj, la salida del primer registro trigger_rf es 1, y el segundo registro trigger_rs debe serlo cuando llega el segundo flanco del reloj. Salida 1, por lo que en el primer flanco del reloj, la salida 0 del segundo registro trigger_rs pasa a través de la puerta NOT y la salida 1 del primer registro trigger_rf realiza una operación Y (&), en este momento pos_edge genera un nivel alto, y neg_edge sigue siendo el mismo Nivel bajo de salida, por lo tanto, use pos_edge para detectar el flanco ascendente.

Inserte la descripción de la imagen aquí
3. Cuando llega el segundo reloj de clk, trigger_r todavía mantiene un nivel alto en este momento, pero en este momento los registros trigger_rf y trigger_rs siguen emitiendo un nivel alto, lo que hace que pos_edge y neg_edge sigan emitiendo un nivel bajo, lo que explica cuándo hay Cuando el aumento edge llega, pos_edge también genera un nivel alto de un ciclo de reloj.Si no hay ningún cambio de flanco ascendente o descendente, tanto pos_edge como neg_edge permanecen bajos.

Detección de borde descendente

1. Antes del flanco ascendente, la señal trigger_r ha mantenido un nivel alto durante un período de tiempo, los registros trigger_rf y trigger_rs mantienen el nivel alto de salida y una de las señales se somete a una operación AND (&) con la otra señal a través de la puerta NOT, pos_edge y Both neg_edge generan un nivel bajo.

2. Cuando la señal trigger_r cambia de 1 a 0, es decir, el flanco descendente, cuando llega el primer flanco del reloj, la salida del primer registro trigger_rf es 0, y el segundo registro trigger_rs debe serlo cuando llega el segundo flanco del reloj. .Salida 0, por lo que en el primer flanco del reloj, la salida 0 del primer registro trigger_rf pasa a través de la puerta NOT y la salida 1 del segundo registro trigger_rs para la operación Y (&), en este momento neg_edge salidas de nivel alto, pos_edge permanece igual La salida es baja, por lo que neg_edge se usa para detectar el flanco descendente.

Inserte la descripción de la imagen aquí

3. Cuando llega el segundo reloj de clk, trigger_r todavía se mantiene en un nivel bajo en este momento, pero en este momento los registros trigger_rf y trigger_rs mantienen la salida en un nivel bajo, lo que da como resultado que pos_edge y neg_edge todavía emitan un nivel bajo, lo que explica cuándo hay Cuando la caída Cuando llega el flanco ascendente, neg_edge también genera un nivel alto de un ciclo de reloj No hay cambio de flanco ascendente o descendente, por lo que tanto pos_edge como neg_edge permanecen bajos.

La idea lógica importante aquí es que no importa que el circuito de flanco detecte el flanco ascendente o el flanco descendente, generará un pulso de nivel alto después de detectar el flanco requerido. El flanco ascendente emite un nivel alto durante un ciclo de reloj en pos_edge, y el flanco descendente El flanco también genera un nivel alto durante un ciclo de reloj en neg_edge.

Módulo contador

Esta parte es un signo de juicio importante para la transición de estado de la máquina de estado. Debido a que el tiempo de fluctuación del botón está entre 5 ms y 10 ms, el contador es 10 ms. Cuando el contador está contando y el estado del botón es estable, se determina que el botón ingresa al período estable.

Inserte la descripción de la imagen aquí

Parte del código Verilog

//--------------------------------------------------------------------------------------------
//      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

Inserte la descripción de la imagen aquí

Prueba de incentivos y simulación

Esta parte continuará acumulando habilidades de escritura en el banco de pruebas e ideas de simulación en el aprendizaje de proyectos pequeños, y se agregará después de una comprensión profunda de los modelos de simulación del banco de pruebas.

Se adjunta un código de banco de pruebas simple como referencia solo para verificación

//--------------------------------------------------------------------------------------------
//      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

Supongo que te gusta

Origin blog.csdn.net/sinat_41653350/article/details/105580888
Recomendado
Clasificación