1、fsm_key_filter.v
module fsm_key_filter(
input wire clk ,
input wire rst_n ,
input wire key ,
output reg key_flag
);
//定义状态变量独热码编码
localparam IDLE = 4'b0001,//表示空闲状态
FILETER_DOWN = 4'b0010,//按键按下前抖动状态
HOLD_DOWN = 4'b0100,//按键稳定时的状态
FILETER_UP = 4'b1000;//按键松手后,后抖动状态
//定义状态
reg [3:0] state_c ;
reg [3:0] state_n ;
//定义状态转移条件
wire [3:0] idle_2_F_D;
wire [3:0] F_D_2_H_D ;
wire [3:0] F_D_2_idle;
wire [3:0] H_D_2_F_U ;
wire [3:0] F_U_2_idle;
wire [3:0] F_U_2_H_D ;
//计数器模块定义
parameter TIME_20MS = 20'd999_999;
reg [19:0] cnt_20ms ;
wire add_cnt ;
wire end_cnt ;
//寄存按键信号同步打拍,检测下降沿与上升沿
reg key_r0 ;//同步信号寄存
reg key_r1 ;//打拍信号
wire nedge ;//下降沿信号
wire pedge ;//上升沿信号
//状态机设计三段式时序逻辑描述状态转移
//第一段
always @(posedge clk or negedge rst_n) begin
if(!rst_n)begin
state_c <= IDLE ;
end
else begin
state_c <= state_n;
end
end
//第二段,组合逻辑,更据状态转移图描述状态转移条件
always@(*)begin
case(state_c)
IDLE : begin
if(idle_2_F_D)begin//条件成立状态转移
state_n = FILETER_DOWN;
end
else begin
state_n = IDLE ;
end
end
FILETER_DOWN :begin
if(F_D_2_idle)begin
state_n = IDLE;
end
else if(F_D_2_H_D)begin
state_n = HOLD_DOWN;
end
else begin
state_n = FILETER_DOWN;
end
end
HOLD_DOWN : begin
if(H_D_2_F_U)begin
state_n = FILETER_UP ;
end
else begin
state_n = HOLD_DOWN ;
end
end
FILETER_UP : begin
if(F_U_2_H_D)begin
state_n = HOLD_DOWN ;
end
else if(F_U_2_idle)begin
state_n = IDLE ;
end
else begin
state_n = FILETER_UP ;
end
end
default : state_n = IDLE ;
endcase
end
assign idle_2_F_D = (state_c == IDLE ) && nedge ;
assign F_D_2_idle = (state_c == FILETER_DOWN) && pedge ;
assign F_D_2_H_D = (state_c == FILETER_DOWN) && end_cnt;
assign H_D_2_F_U = (state_c == HOLD_DOWN ) && pedge ;
assign F_U_2_idle = (state_c == FILETER_UP ) && end_cnt;
assign F_U_2_H_D = (state_c == FILETER_UP ) && nedge ;
//第三段,描述输出
always@(posedge clk or negedge rst_n)begin
if(!rst_n)begin
key_flag <= 1'b0;
end
else if(F_D_2_H_D)begin
key_flag <= ~key;
end
else
key_flag <= 1'b0;
end
//20ms计数器模块
always @(posedge clk or negedge rst_n)begin
if(!rst_n)begin
cnt_20ms <= 20'd0;
end
else if(add_cnt)begin
if(end_cnt)begin
cnt_20ms <= 20'd0;
end
else begin
cnt_20ms <= cnt_20ms +1'd1;
end
end
else begin
cnt_20ms <= 20'd0;
end
end
assign add_cnt = (state_c == FILETER_DOWN) || (state_c == FILETER_UP);
assign end_cnt = (add_cnt && cnt_20ms == TIME_20MS );
//延时打拍按键信号
always@(posedge clk or negedge rst_n)begin
if(!rst_n)begin
key_r0 <= 1'b1;
key_r1 <= 1'b1;
end
else begin
key_r0 <= key ;//同步
key_r1 <= key_r0;//打拍
end
end
//nedge pedge
assign nedge = key_r1 & (~key_r0);//检测下降沿
assign pedge = (~key_r1) & key_r0;//检测上升沿
endmodule
2、fsm_led.v
//状态机实现流水灯
module fsm_led (
input wire clk ,
input wire rst_n ,
output reg [3:0] led
);
//对应的状态进行独热码编码定义
//一般情况下对状态进行编码定义一般采用局部变量定义
localparam IDLE = 4'b0001 ,//初始状态,led[0]亮
S1 = 4'b0010,
S2 = 4'b0100,
S3 = 4'b1000;
//当前状态和次态的位宽由状态机有几个状态决定的
reg [3:0] state_c;//当前状态
reg [3:0] state_n;//次态
//定义状态跳转条件
wire idle_2_S1;
wire S1_2_S2 ;
wire S2_2_S3 ;
wire S3_2_idle;
//定义计数器参数
parameter TIME_500MS = 25'd24_999_999;
reg [24:0] cnt_500ms ;
wire add_cnt_500ms;
wire end_cnt_500ms;
//三段式状态机
//第一段,时序逻辑描述状态转移
always @(posedge clk or negedge rst_n)begin
if(!rst_n)begin
state_c <= IDLE ;//c-Current,初始状态为IDLE
end
else begin
state_c <= state_n; //时钟上升沿将次态给现态
end
end
//第二段组合逻辑,描述状态转移条件
always @(*)begin
case(state_c) //以现态作为判断条件
IDLE : begin
if(idle_2_S1)begin
state_n = S1 ;
end
else begin
state_n = state_c ; //条件未成立,保持原来状态不变
end
end
S1 : begin
if(S1_2_S2)begin
state_n = S2 ;
end
else begin
state_n = S1 ;
end
end
S2 : begin
if(S2_2_S3)begin
state_n = S3 ;
end
else begin
state_n = S2 ;
end
end
S3 : begin
if(S3_2_idle)begin
state_n = IDLE ;
end
else begin
state_n = S3;
end
end
default : state_n = IDLE ;
endcase
end
//描述状态转移跳转的条件
assign idle_S1 = state_c == IDLE && end_cnt_500ms;
assign S1_S2 = state_c == S1 && end_cnt_500ms;
assign S2_S3 = state_c == S2 && end_cnt_500ms;
assign S3_idle = state_c == S3 && end_cnt_500ms;
//第三段,可时序也可组合,描述输出状态
always @(*)begin
case(state_c)
IDLE : led = 4'b0001;
S1 : led = 4'b0010;
S2 : led = 4'b0100;
S3 : led = 4'b1000;
default : led = led;
endcase
end
//计数器模块
always@(posedge clk or negedge rst_n)begin
if(!rst_n)begin
cnt_500ms <= 25'd0;
end
else if(add_cnt_500ms)begin
if(end_cnt_500ms)begin
cnt_500ms <= 25'd0;
end
else begin
cnt_500ms <= cnt_500ms + 1'd1;
end
end
else begin
cnt_500ms <= cnt_500ms;
end
end
assign add_cnt_500ms = 1'b1;
assign end_cnt_500ms = add_cnt_500ms && cnt_500ms == TIME_500MS;
endmodule
3、fsm_key_filter.v
`timescale 1ns/1ns
module fsm_key_filter_tb();
reg clk ;
reg rst_n ;
reg key ;
wire key_flag;
parameter CYCLE = 20;
defparam u_fsm_key_filter.TIME_20MS = 100;
//模拟复位信号与时钟信号初始化
initial begin
clk = 1'b1;
rst_n <= 1'b0;
#15
rst_n <= 1'b1;
end
//模拟时钟信号
always #(CYCLE/2) clk = ~clk;
//模拟按键按下抖动过程
initial begin
key = 1'b1;
#(1000*CYCLE)
//按下时的抖动
key = 1'b0;
#(15*CYCLE)
key = 1'b1;
#(20*CYCLE)
key = 1'b0;
#(9*CYCLE)
key = 1'b1;
#(6*CYCLE)
key = 1'b0;
#(1000*CYCLE)//稳定状态
//按键松手时的后抖动
key = 1'b1;
#(10*CYCLE)
key = 1'b0;
#(8*CYCLE)
key = 1'b1;
#(9*CYCLE)
key = 1'b0;
#(15*CYCLE)
key = 1'b1;
#(10000*CYCLE)
$stop;
end
fsm_key_filter u_fsm_key_filter(
. clk (clk),
. rst_n (rst_n),
. key (key),
. key_flag (key_flag)
);
endmodule