本次實驗目的在於熟悉狀態機的控制,需要實現的功能如下:
按下按鍵並保持然後鬆開
當保持時間小於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
結果如下圖