实验现象:
每次按下按键0,4个LED显示状态以二进制加法格式加1。
每次按下按键1,4个LED显示状态以二进制加法格式减1。
知识点:
1、testbench中随机数发生函数$random的使用;
2、仿真模型的概念
1、按键波形分析:
按键未按,FPGA管脚检测到高电平。
按键按下,FPGA管脚检测到低电平。
2、设置四个状态:
1、未按下时,空闲态;
2、抖动滤除状态;
3、按下稳定状态;
4、释放抖动滤除状态
localparam
IDEL = 4'b0001, //空闲状态
FILTER0 = 4'b0010, //抖动滤除状态
DOWN = 4'b0100, //按下稳定状态
FILTER1 = 4'b1000; //释放抖动滤除状态
reg [3:0]state;
3、状态迁移图:
IDEL:空闲状态
FILTER:抖动滤除状态(过了20ms后还未出现上升沿,进入DOWN状态,否则进入IDEL状态)
DOWN:按下稳定状态
FILTER1:释放抖动滤除状态((过了20ms后还未出现下降沿,进入IDEL状态,否则进入DOWN状态))
4、消抖模块模型:
module key_filter(
Clk,
Rst_n,
key_in,
key_flag,
key_state
);
input Clk; //时钟信号
input Rst_n; //复位信号
input key_in; //按键输入信号
output reg key_flag; //按键状态标志信号
output reg key_state; //按键状态信号
endmodule
5、边沿检测电路:
reg key_tmp0,key_tmp1;
wire pedge,nedge;
always@(posedge Clk or negedge Rst_n)
if(!Rst_n)begin
key_tmp0 <= 1'b0;
key_tmp1 <= 1'b0;
end
else begin
key_tmp0 <= key_in;
key_tmp1 <= key_tmp0;
end
assign nedge = !key_tmp0 & key_tmp1; //下降沿
assign pedge = key_tmp0 & !key_tmp1; //上升沿
6、设置20ms计数器
reg [19:0]cnt; //计数器
reg en_cnt; //使能计数寄存器,控制计数器cnt
reg cnt_full; //计满标志信号
always@(posedge Clk or negedge Rst_n)
if(!Rst_n)
cnt <= 20'd0;
else if(en_cnt)
cnt <= cnt + 1'b1;
else
cnt <= 20'd0;
always@(posedge Clk or negedge Rst_n)
if(!Rst_n)
cnt_full <= 1'b0;
else if(cnt == 999_999) //计数1_000_000次
cnt_full <= 1'b1;
else
cnt_full <= 1'b0;
7、设置状态机
always@(posedge Clk or negedge Rst_n)
if(!Rst_n)begin
en_cnt <= 1'b0;
state <= IDEL;
key_flag <= 1'b0;
key_state <= 1'b1;
end
else begin
case(state)
IDEL:begin
key_flag <= 1'b0;
if(nedge)begin //出现下降沿,转到FILTER0状态
state <= FILTER0;
en_cnt <= 1'b1; //开始计数
end
else
state <= IDEL;
end
FILTER0:
if(cnt_full)begin //计数器计满,转到DOWN状态
key_flag <= 1'b1;
key_state <= 1'b0;
state <= DOWN;
en_cnt <= 1'b0; //停止计数,并使计数器清零
end
else if(pedge)begin //出现上升沿,转到IDEL状态
state <= IDEL;
en_cnt <= 1'b0; //停止计数,并使计数器清零
end
else
state <= FILTER0;
DOWN:begin
key_flag <= 1'b0;
if(pedge)begin //出现上升沿,转到FILTER0状态
state <= FILTER1;
en_cnt <= 1'b1; //开始计数
end
else
state <= DOWN;
end
FILTER1:
if(cnt_full)begin //计数器计满,转到IDEL状态
key_flag <= 1'b1;
key_state <= 1'b1;
state <= IDEL;
en_cnt <= 1'b0; //停止计数,并使计数器清零
end
else if(nedge)begin //出下降沿,转到DOWN状态
state <= DOWN;
en_cnt <= 1'b0; //停止计数,并使计数器清零
end
else
state <= FILTER1;
default:begin
state <= IDEL;
en_cnt <=1'b0;
key_flag <= 1'b0;
key_state <= 1'b1;
end
endcase
end
8、状态图(State Machine Viewer):
9、设置testbench文件(仿真模型)
9.1、设置仿真模型key_model.v
`timescale 1ns/1ns
module key_model(key);
output reg key;
reg [15:0]myrand;
initial begin
key = 1'b1;
press_key;
#10000;
press_key;
#10000;
press_key;
$stop;
end
task press_key; //设置press_key任务
begin
repeat(50)begin
myrand = {$random}%65536; //产生0~65535之间的随机数
#myrand key = ~key;
end
key = 0;
#50000000;
repeat(50)begin
myrand = {$random}%65536; //0~65535之间的随机数
#myrand key = ~key;
end
key = 1;
#50000000;
end
endtask
endmodule
9.2、设置testbench文件key_filter_tb.v
`timescale 1ns/1ns
`define clock_period 20
module key_filter_tb;
reg Clk;
reg Rst_n;
wire key_in; //key_in在此时相当于是一根线,所以设置为wire类型
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)
);
key_model key_model(.key(key_in));
initial Clk = 1;
always#(`clock_period/2) Clk = ~Clk;
initial begin
Rst_n = 1'b0;
#(`clock_period*10) Rst_n = 1'b1;
#(`clock_period*10 + 1);
end
endmodule
9.3、将仿真模型与testbench文件链接
Assignments->Settings...->Test Benches...->Edit...
在testbench文件夹中选中key_model文件,点击Add
标签:状态,task,FPGA,Clk,状态机,key,按键,Rst,reg From: https://www.cnblogs.com/little55/p/17866867.html