按鍵延遲時間測試的Verilog實現(FPGA實驗)

本次實驗目的在於熟悉狀態機的控制,需要實現的功能如下:
按下按鍵並保持然後鬆開
當保持時間小於1s時,一個led燈閃爍2次。
當保持時間小於2s大於1s時,一個led燈閃爍4次。
當保持時間大於2s時,四個led燈閃爍4次。
本次實驗採用了狀態機的思想控制,主要的細節包括:1.輸入信號的緩衝;2輸入信號的上升沿和下降沿的檢測;3.不同延遲時間的跳轉;4.按下計時和led閃爍計時器的各自設計;5,led閃爍的控制。
RTL代碼如下:

module keykeeper(
	clk,
	rst,
	key_in,
	led,
	state_out,
	cnt,
	cnt_led,
	led_finish,
	time_flag_1,
	time_flag_2,
	key_pos,
	key_neg
	);
	
	input clk;
	input rst;
	input wire key_in;//輸入信號
	output reg [3:0]led;//led信號
	output wire [1:0]state_out;//狀態跳轉輸出
	output reg [15:0]cnt; //按下計時
	output reg [15:0]cnt_led;//led閃爍計時
	output reg led_finish;//led完成閃爍返回信號
	reg en_cnt;//按下計時使能信號
	
	reg key_temp_1;//信號緩衝同步
	reg key_temp_2;
	reg key_temp_3;//邊沿寄存
	reg key_temp_4;
	
	reg en_cnt_led;
	
	output wire key_pos;//檢測邊沿信號
	output wire key_neg;
	
	output reg time_flag_1;
	output reg time_flag_2;
	
	//設置狀態跳轉
	reg [1:0]state;
	
	assign state_out = state;
	
	localparam
		s0 = 2'b00,
		s1 = 2'b01,
		s2 = 2'b10,
		s3 = 2'b11;
	
	//兩級緩衝器對輸入信號進行同步
	always@(posedge clk or negedge rst)
		if(!rst)
		begin
			key_temp_1 <= 1'b1;
			key_temp_2 <= 1'b1;
		end
		else	
		begin
			key_temp_1 <= key_in;
			key_temp_2 <= key_temp_1;
		end		
	
	//檢測key_in的下降沿和上升沿
	always@(posedge clk or negedge rst)
		if(!rst)
		begin
			key_temp_3 <= 1'b1;
			key_temp_4 <= 1'b1;
		end
		else	
		begin
			key_temp_3 <= key_temp_2;
			key_temp_4 <= key_temp_3;
		end
	assign key_pos = (key_temp_3)&&(!key_temp_4);
	assign key_neg = (!key_temp_3)&&(key_temp_4);
	
	//設置計數使能信號
	always@(posedge clk or negedge rst)
		if(!rst)
		begin
			en_cnt = 1'b0;
		end
		else if(key_neg&&(!key_pos))
		begin
			en_cnt = 1'b1;
		end
		else if(key_pos&&(!key_neg))
		begin
			en_cnt <= 1'b0;
		end
		else
		begin
			en_cnt <= en_cnt;
		end
		
	
	//檢測en_cnt,並決定是否開始計時
	always@(posedge clk or negedge rst)
		if(!rst)
		begin
			cnt <= 27'b0;
		end
		else if(en_cnt)
		begin
			cnt <= cnt + 1;
		end
		else
		begin
			cnt <= 0;
		end
		
	//設置time_flag
	always@(posedge clk or negedge rst)
		if(!rst)
		begin
			time_flag_1 <= 1'b0;
			time_flag_2 <= 1'b0;
		end
		else if(cnt >= 12'b0100_0000_0000)
		begin
			time_flag_2 <= 1'b1;
			time_flag_1 <= 1'b1;
		end
		else if((cnt >= 12'b0010_0000_0000)&&(cnt < 12'b0100_0000_0000))
		begin
			time_flag_1 <= 1'b1;
			time_flag_2 <= 1'b0;
		end
		else
		begin
			time_flag_1 <= 1'b0;
			time_flag_2 <= 1'b0;
		end
	//設置控制led的電路

	parameter CNT_LED_CYCLE = 12'b0001_0000_0000;
	
	//設置led計數控制邏輯
	always@(posedge clk or negedge rst)
		if(!rst)
		begin
			en_cnt_led = 1'b0;
		end
		else if(key_pos&&(!led_finish))
		begin
			en_cnt_led = 1'b1;
		end
		else if(led_finish)
		begin
			en_cnt_led = 1'b0;
		end
		else
		begin
			en_cnt_led <= en_cnt_led;
		end
			
	always@(posedge clk or negedge rst)
		if(!rst)
			cnt_led <= 0;
		else if(en_cnt_led)
			cnt_led <= cnt_led + 1;
		else
			cnt_led <= 0;
			
	//設置led亮滅的邏輯

	//s1
	always@(posedge clk or negedge rst)
		if(!rst)
		begin
			led <= 4'b1111;
			led_finish <= 1'b0;
		end
		else if(state == s1)
		begin
		led = 4'b1111;
		case(cnt_led)
		CNT_LED_CYCLE: 
		begin
			led[0] <= ~led[0];
			led_finish <= 1'b0;
		end
		CNT_LED_CYCLE*2:
		begin
			led[0] <= ~led[0];
			led_finish <= 1'b0;
		end
		CNT_LED_CYCLE*3:
		begin
			led[0] <= ~led[0];
			led_finish <= 1'b0;
		end
		CNT_LED_CYCLE*4:
		begin
			led[0] <= ~led[0];
			led_finish <= 1'b0;
		end
		CNT_LED_CYCLE*5:
		begin
			led[0] <= ~led[0];
			led_finish <= 1'b1;
		end
		default:
		begin
			led <= led;
			led_finish <= 1'b0;
		end
		endcase
		end
		
		else if(state == s2)
		begin
		led <= 4'b1111;
		case(cnt_led)
		CNT_LED_CYCLE: 
		begin
			led[0] <= ~led[0];
			led_finish <= 1'b0;
		end
		CNT_LED_CYCLE*2:
		begin
			led[0] <= ~led[0];
			led_finish <= 1'b0;
		end
		CNT_LED_CYCLE*3:
		begin
			led[0] <= ~led[0];
			led_finish <= 1'b0;
		end
		CNT_LED_CYCLE*4:
		begin
			led[0] <= ~led[0];
			led_finish <= 1'b0;
		end
		CNT_LED_CYCLE*5:
		begin
			led[0] <= ~led[0];
			led_finish <= 1'b0;
		end
		CNT_LED_CYCLE*6: 
		begin
			led[0] <= ~led[0];
			led_finish <= 1'b0;
		end
		CNT_LED_CYCLE*7:
		begin
			led[0] <= ~led[0];
			led_finish <= 1'b0;
		end
		CNT_LED_CYCLE*8:
		begin
			led[0] <= ~led[0];
			led_finish <= 1'b0;
		end
		CNT_LED_CYCLE*9:
		begin
			led[0] <= ~led[0];
			led_finish <= 1'b0;
		end
		CNT_LED_CYCLE*10:
		begin
			led[0] <= ~led[0];
			led_finish <= 1'b1;
		end
		default:
		begin
			led <= led;
			led_finish <= 1'b0;
		end
		endcase
		end
		else if(state == s3)
		begin
		led <= 4'b1111;
		case(cnt_led)
		CNT_LED_CYCLE: 
		begin
			led <= ~led;
			led_finish <= 1'b0;
		end
		CNT_LED_CYCLE*2:
		begin
			led <= ~led;
			led_finish <= 1'b0;
		end
		CNT_LED_CYCLE*3:
		begin
			led <= ~led;
			led_finish <= 1'b0;
		end
		CNT_LED_CYCLE*4:
		begin
			led <= ~led;
			led_finish <= 1'b0;
		end
		CNT_LED_CYCLE*5:
		begin
			led <= ~led;
			led_finish <= 1'b0;
		end
		CNT_LED_CYCLE*6: 
		begin
			led <= ~led;
			led_finish <= 1'b0;
		end
		CNT_LED_CYCLE*7:
		begin
			led <= ~led;
			led_finish <= 1'b0;
		end
		CNT_LED_CYCLE*8:
		begin
			led <= ~led;
			led_finish <= 1'b0;
		end
		CNT_LED_CYCLE*9:
		begin
			led <= ~led;
			led_finish <= 1'b0;
		end
		CNT_LED_CYCLE*10:
		begin
			led <= ~led;
			led_finish <= 1'b1;
		end
		default:
		begin
			led <= led;
			led_finish <= 1'b0;
		end		
		endcase
		end
		else
		begin
			led <= 4'b1111;
			led_finish <= 1'b0;
		end	

	//檢測time_flag並控制狀態跳轉以及led
	
	always@(posedge clk or negedge rst)
		if(!rst)
		begin
			state <= s0;
		end
		else
		begin
			case(state)
			s0:
				begin
				if(key_neg)
				state <= s1;
				else
				state <= s0;
				end
			s1:
				begin
				if((time_flag_1)&&(!time_flag_2))
				state <= s2;
				else
				state <= s1;
				end
			s2:
				begin
				if((time_flag_1)&&(time_flag_2))
				state <= s3;
				else
				state <= s2;
				end
			s3:
				begin
				if(led_finish)
				state <= s0;
				else
				state <= s3;
				end
			endcase
		end
		
		
endmodule 

測試代碼如下:

`timescale 1ns/1ns
`define clock_period 20
module keykeeper_tb;
	reg key_in;
	reg clk;
	reg rst;
	wire [3:0] led;
	wire [1:0]state_out;
	wire [15:0] cnt; //101_1111_0101_1110_0001_0000_0000
	wire [15:0]cnt_led;
	wire led_finish;
	wire time_flag_1;
	wire time_flag_2;
	wire key_pos;
	wire key_neg;
	
	keykeeper keykeeper0(
								.clk(clk),
								.rst(rst),
								.key_in(key_in),
								.led(led),
								.state_out(state_out),
								.cnt(cnt),
								.cnt_led(cnt_led),
								.led_finish(led_finish),
								.time_flag_1(time_flag_1),
								.time_flag_2(time_flag_2),
								.key_pos(key_pos),
								.key_neg(key_neg)
								);
	initial
	clk = 1'b0;
	always#(`clock_period/2)
		clk = ~clk;
	
	initial
	begin
		key_in = 1'b1;
		rst = 1'b1;
		#20;
		rst = 1'b0;
		#40;
		rst = 1'b1;
		#20;
		key_in = 1'b1;
		#100;
		key_in = 1'b0;
		#(`clock_period*200);
		key_in = 1'b1;
		#(`clock_period*5_000);
		key_in = 1'b0;
		#(`clock_period*700)
		key_in = 1'b1;
		#(`clock_period*5_000);
		key_in = 1'b0;
		#(`clock_period*2000)
		key_in = 1'b1;
		#(`clock_period*5_000);
		$stop;		
	end
endmodule 

結果如下圖
在這裏插入圖片描述

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章