本次实验目的在于熟悉状态机的控制,需要实现的功能如下:
按下按键并保持然后松开
当保持时间小于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