设计说明书——拆弹游戏
一. 设计背景
拆弹游戏,现有一个定时炸弹,设有一个计时器,如果不能在限定时间内找出唯一的密码,会发生爆炸,若在规定时间内完成,则相当于炸弹被拆除。
二.使用说明
接通电路,计时器自动开始计时,显示在数码管上。利用八个开关输入二进制数(从左到右依次为高位到低位),led灯v16亮起则表示输入密码偏小,led灯u16亮起则显示输入密码偏大。若输入正确,则炸弹拆除,显示“炫酷“的流水灯。若未能在限定时间内完成,则在数码管上会有相应提示表示”炸弹“已经爆炸。
三.设计说明
1 . 主要部分:分秒计时状态机,数码管显示模块,密码比较器,流水灯控制模块
2 . 分秒计时器:
clk频率50Mhz,及每个时钟周期20ns,考虑1000_000ns即1ms为一个延迟。
设置一个状态机,转换机制如下:计数延迟个数,每经历1000个延迟,状态转换一次每次转换,秒的个位sec_l增加1,当sec_l=9时,其变为0,十位sec_h加一。当sel_l=9且sec_h=5时,分的各位min_l增加一。同理易得min_h的变化规律。当计时器统计了60分钟后,所有位归零从新计时。
3 . 密码比较器:不涉及时序,仅由逻辑电路构成。将输入数据(一个8位无符号数)与内置密码比较,若后者较大,则输出相应信号,表示输入密码变大,反之则输出相应信号,表明输入偏小。如果恰好为密码,则炸弹被拆除,则将rst_n置为0,控制数码管清零,以及显示流水灯表示庆祝。
4 . 数码管显示模块:利用四位从左到右分别显示计时的分十位,分个位,秒十位,秒个位。利用视觉留影原理,动态扫描四个数码管,时间每个显示时间为一个延迟,即1ms,小于人眼暂留时间20ms,经测试显示稳定。
5 . 流水灯,使用了T11,M11,R11,N11四个led作为输出,具体输出样式参见代码。
四.代码
module top(
input clk,
input [7:0] datain,
output big,
output smal,
output [7:0] seg,
output [3:0] sel,
output [3:0] led_out
);
wire rst_n;
cmp c(datain,big,smal,rst_n);
timera t(rst_n,clk,seg,sel);
Led_Top l(clk,~rst_n,led_out);
endmodule
module cmp(
input [7:0]datain,
output big,
output smal,
output rst_n
);
integer key=8'b10101010;
assign smal=(datain<key);
assign big=(datain>key);
assign rst_n=~(datain==key);
endmodule
`define T1MS 16'd49_999
`define FLASH_FREQUENCY 14'd500
module Led_Top
(
input clk,rst_n,
output[3:0] ledOut
);
wire[3:0] isStart;
wire[55:0] SetMSTimes;
wire[3:0] isDone;
Led_Control Led_Control
(
.clk(clk),
.rst_n(rst_n),
.isDone(isDone),
.isStart(isStart),
.SetMSTimes(SetMSTimes)
);
Led_Driver Led_Driver[3:0]
(
.clk(clk),
.rst_n(rst_n),
.StartSig(isStart),
.setMSTimes(SetMSTimes),
.DoneSig(isDone),
.ledOut(ledOut)
);
endmodule
module Led_Control
(
input clk,rst_n,
input[3:0] isDone,
output[3:0] isStart,
output[55:0] SetMSTimes
);
reg[3:0] i,j,k;
reg[3:0] regStart;
reg[13:0] regSetMSTimes[3:0];
always@(posedge clk,negedge rst_n)
if(!rst_n) //³õʼ»¯
begin
i<=4'd0;
j<=4'd0;
k<=4'd0;
regStart<=4'b0000;
regSetMSTimes[0]<=14'd0;
regSetMSTimes[1]<=14'd0;
regSetMSTimes[2]<=14'd0;
regSetMSTimes[3]<=14'd0;
end
else
case(i)
0:SerialLight(`FLASH_FREQUENCY,`FLASH_FREQUENCY,`FLASH_FREQUENCY,`FLASH_FREQUENCY);
1:PipeLineLight(`FLASH_FREQUENCY,`FLASH_FREQUENCY,`FLASH_FREQUENCY,`FLASH_FREQUENCY);
2:ParallelPipeLineLight(`FLASH_FREQUENCY,`FLASH_FREQUENCY,`FLASH_FREQUENCY,`FLASH_FREQUENCY);
3:i<=4'd0; //¿ØÖÆÑ»·
endcase
assign isStart=regStart;
assign SetMSTimes={regSetMSTimes[3],regSetMSTimes[2],regSetMSTimes[1],regSetMSTimes[0]};
task SerialLight
(
input[13:0] serialSetMSTimes0,serialSetMSTimes1,
serialSetMSTimes2,serialSetMSTimes3
);
case(j)
0:
begin
regSetMSTimes[0]<=serialSetMSTimes0;
regSetMSTimes[1]<=serialSetMSTimes1;
regSetMSTimes[2]<=serialSetMSTimes2;
regSetMSTimes[3]<=serialSetMSTimes3;
regStart[0]<=1'b1;
j<=j+1'b1;
end
1:
if(isDone[0])
begin
regStart[0]<=1'b0;
regStart[1]<=regStart[0];
j<=j+1'b1;
end
2:
if(isDone[1])
begin
regStart[1]<=1'b0;
regStart[2]<=regStart[1];
j<=j+1'b1;
end
3:
if(isDone[2])
begin
regStart[2]<=1'b0;
regStart[3]<=regStart[2];
j<=j+1'b1;
end
4:
if(isDone[3])
begin
regStart[3]<=1'b0;
k<=k+1'b1;
if(k==4'd3)
begin
k<=4'd0;
j<=j+1'b1;
end
else
begin
regStart[0]<=regStart[3];
j<=4'd1;
end
end
5:
begin
j<=4'd0;
i<=i+1'b1;
end
endcase
endtask
task PipeLineLight
(
input[13:0] PipeLineSetMSTimes0,PipeLineSetMSTimes1,
PipeLineSetMSTimes2,PipeLineSetMSTimes3
);
case(j)
0:
begin
regSetMSTimes[0]<=PipeLineSetMSTimes0;
regSetMSTimes[1]<=PipeLineSetMSTimes1;
regSetMSTimes[2]<=PipeLineSetMSTimes2;
regSetMSTimes[3]<=PipeLineSetMSTimes3;
regStart[0]<=1'b1;
j<=j+1'b1;
end
1:
if(isDone[0])
begin
regStart[0]<=1'b1;
regStart[1]<=regStart[0];
regStart[2]<=regStart[1];
regStart[3]<=regStart[2];
k<=k+1'b1;
if(k==4'd2)
begin
k<=4'd0;
j<=j+1'b1;
end
else
j<=j;
end
2:
if(isDone[3])
begin
regStart[0]<=1'b0;
regStart[1]<=regStart[0];
regStart[2]<=regStart[1];
regStart[3]<=regStart[2];
k<=k+1'b1;
if(k==4'd3)
begin
k<=4'd0;
j<=j+1'b1;
end
else
j<=j;
end
3:
begin
j<=4'd0;
i<=i+1'b1;
end
endcase
endtask
task ParallelPipeLineLight
(
input[13:0] PipeLineSetMSTimes0,PipeLineSetMSTimes1,
PipeLineSetMSTimes2,PipeLineSetMSTimes3
);
case(j)
0:
begin
regSetMSTimes[0]<=PipeLineSetMSTimes0;
regSetMSTimes[1]<=PipeLineSetMSTimes1;
regSetMSTimes[2]<=PipeLineSetMSTimes2;
regSetMSTimes[3]<=PipeLineSetMSTimes3;
regStart[0]<=1'b1;
regStart[2]<=1'b1;
j<=j+1'b1;
end
1:
if(isDone[0])
begin
regStart[0]<=1'b1;
regStart[1]<=regStart[0];
regStart[2]<=1'b1;
regStart[3]<=regStart[2];
k<=k+1'b1;
if(k==4'd0)
begin
k<=4'd0;
j<=j+1'b1;
end
else
j<=j;
end
2:
if(isDone[1])
begin
regStart[0]<=1'b0;
regStart[1]<=regStart[0];
regStart[2]<=1'b0;
regStart[3]<=regStart[2];
k<=k+1'b1;
if(k==4'd1)
begin
k<=4'd0;
j<=j+1'b1;
end
else
j<=j;
end
3:
begin
j<=4'd0;
i<=i+1'b1;
end
endcase
endtask
endmodule
module Led_Driver
(
input clk,rst_n,
input StartSig,
input[55:0] setMSTimes,
output DoneSig,
output ledOut
);
wire[13:0] halrSetMSTimes;
wire timerOut;
Led_Driver_Control Led_Driver_Control
(
.clk(clk),
.rst_n(rst_n),
.StartSig(StartSig),
.setMSTimes(setMSTimes),
.timerOut(timerOut),
.DoneSig(DoneSig),
.halrSetMSTimes(halrSetMSTimes)
);
//ÏÈÇó³öÖظ´ÊµÀýµÄ×ÜλÊý£¬È»ºó°´Ã¿¸öʵÀý¶ÔӦλ´ÓµÍµ½¸ß½Ø¶Ï£¬Ã»ÓиßλµÄ¾ÍÊÇÖظ´
Timer Timer //ÿ¸ösetMSTimesÓÐ14룬4¸öTimerÓÐ14*4=56λsetMSTimes£¬
( //Ôò¸øsetMSTimes¸³ÖµÎª56룬setMSTimes»á°´¶ÔӦλ½Ø¶Ï£¬
.clk(clk), //setMSTimes[0]¶ÔÓ¦[13:0]룬setMSTimes[1]¶ÔÓ¦[27:14]λ
.rst_n(rst_n), //setMSTimes[2]¶ÔÓ¦[41:28]룬setMSTimes[3]¶ÔÓ¦[55:42]λ
.StartSig(StartSig),
.setMSTimes(halrSetMSTimes),
.timerOut(timerOut)
);
Led_Interface Led_Interface
(
.clk(clk),
.rst_n(rst_n),
.StartSig(StartSig),
.timerIn(timerOut),
.ledOut(ledOut)
);
endmodule
module Led_Driver_Control
(
input clk,rst_n,
input StartSig,
input[13:0] setMSTimes,
input timerOut,
output DoneSig,
output[13:0] halrSetMSTimes
);
reg countTimerOut;
always@(posedge clk,negedge rst_n) //¶ÔʱÖÓ¼ÆÊý£¬ÅжÏÇ°Ò»¸öµÆµÄÁÁÃð
if(!rst_n)
countTimerOut<=1'b0;
else if(StartSig)
begin
if(timerOut && (countTimerOut==1'b1) )
countTimerOut<=1'b0;
else if(timerOut)
countTimerOut<=countTimerOut+1'b1;
end
else
countTimerOut<=1'b0;
assign DoneSig=(StartSig && timerOut && (countTimerOut==1'b1) )?1'b1:1'b0;
assign halrSetMSTimes=setMSTimes>>1;
endmodule
module Timer
(
input clk,rst_n,
input[13:0] setMSTimes,
input StartSig,
output timerOut
);
wire MSOut;
MSTimer MSTimer
(
.clk(clk),
.rst_n(rst_n),
.StartSig(StartSig),
.MSOut(MSOut)
);
NMSTimer NMSTimer
(
.clk(clk),
.rst_n(rst_n),
.setMSTimes(setMSTimes),
.StartSig(StartSig),
.MSIn(MSOut),
.NMSOut(timerOut)
);
endmodule
module MSTimer
(
input clk,rst_n,
input StartSig,
output MSOut
);
reg[15:0] countMS;
always@(posedge clk,negedge rst_n)
if(!rst_n)
countMS<=16'd0;
else if(StartSig)
begin
if(countMS == `T1MS)
countMS<=16'd0;
else
countMS<=countMS+1'b1;
end
else
countMS<=16'd0;
assign MSOut=(StartSig && (countMS == `T1MS) )?1'b1:1'b0;
endmodule
module NMSTimer
(
input clk,rst_n,
input MSIn,
input StartSig,
input[13:0] setMSTimes,
output NMSOut
);
reg[13:0] countNMS;
always@(posedge clk,negedge rst_n)
if(!rst_n)
countNMS<=14'd0;
else if(StartSig)
begin
if(MSIn && (countNMS==(setMSTimes-1'd1) ) )
countNMS<=14'd0;
else if(MSIn)
countNMS<=countNMS+1'b1;
end
else
countNMS<=14'd0;
assign NMSOut=(StartSig && MSIn && (countNMS==(setMSTimes-1'd1) ) )?1'b1:1'b0;
endmodule
module Led_Interface
(
input clk,rst_n,
input StartSig,
input timerIn,
output ledOut
);
reg regLedOut;
always@(posedge clk,negedge rst_n)
if(!rst_n)
regLedOut<=1'b0;
else if(StartSig)
begin
if(timerIn)
regLedOut<=~regLedOut;
end
else
regLedOut<=1'b0;
assign ledOut=regLedOut;
endmodule
// synopsys translate_off
`timescale 1 ns / 1 ps
// synopsys translate_on
module display(
rst_n,
clk,
min_h,
min_l,
sec_h,
sec_l,
display_flag,
seg,
sel
);
input rst_n; // 全局复位,低电平有效
input clk; // 全局时钟,50MHz
input [2:0] min_h; // 分的十位数
input [3:0] min_l; // 分的个位数
input [2:0] sec_h; // 秒的十位数
input [3:0] sec_l; // 秒的个位数
input display_flag; // 数码管动态显示标志位
output reg [7:0] seg; // 编码后的数码管输出
output reg [3:0] sel; // 数码管的位选
function [7:0] seg_data;
input [3:0] din; // 待编码数据
input dp; // 决定数码管点号是否点亮,1为点亮
begin
case(din)
4'd0 : seg_data = {dp,7'b1000000};
4'd1 : seg_data = {dp,7'b1111001};
4'd2 : seg_data = {dp,7'b0100100};
4'd3 : seg_data = {dp,7'b0110000};
4'd4 : seg_data = {dp,7'b0011001};
4'd5 : seg_data = {dp,7'b0010010};
4'd6 : seg_data = {dp,7'b0000010};
4'd7 : seg_data = {dp,7'b1111000};
4'd8 : seg_data = {dp,7'b0000000};
4'd9 : seg_data = {dp,7'b0010000};
endcase
end
endfunction
wire flag;
assign flag=(min_l>=3'b010);
reg [1:0] cnt; // 由于只有四个数码管,故只需两位
always @(posedge clk or negedge rst_n)
begin
if(rst_n == 1'b0)
cnt <= (0);
else if(display_flag == 1'b1)
cnt <= cnt + 1'b1;
else
cnt <= cnt;
end
always @(posedge clk or negedge rst_n)
begin
if(rst_n == 1'b0)
begin
seg <= (0);
sel <= (0);
end
else if(flag==1'b1)
begin
case(cnt)
2'b00 : // 显示秒个位数
begin
seg <= {1'b1,7'b0111111};
sel <= 4'b0111;
end
2'b01 : // 显示秒十位数
begin
seg <= {1'b1,7'b1000000};
sel <= 4'b1011;
end
2'b10 : // 显示分个位数
begin
seg <= {1'b1,7'b0001000};
sel <= 4'b1101;
end
2'b11 : // 显示分十位数
begin
seg <= {1'b1,7'b0000011};
sel <= 4'b1110;
end
endcase
end
else
begin
case(cnt)
2'b00 : // 显示秒个位数
begin
seg <= seg_data(sec_l,1'b1);
sel <= 4'b0111;
end
2'b01 : // 显示秒十位数
begin
seg <= seg_data({1'b0,sec_h},1'b1);
sel <= 4'b1011;
end
2'b10 : // 显示分个位数
begin
seg <= seg_data(min_l,1'b0);
sel <= 4'b1101;
end
2'b11 : // 显示分十位数
begin
seg <= seg_data({1'b0,min_h},1'b1);
sel <= 4'b1110;
end
endcase
end
end
endmodule
// synopsys translate_off
`timescale 1 ns / 1 ps
// synopsys translate_on
module time_counter(
rst_n,
clk,
min_h,
min_l,
sec_h,
sec_l,
display_flag
);
parameter CLK_CYCLE = 20; // 时钟周期,单位ns
parameter T0 = 1000_000; // 1ms延时
parameter T0_VAL = T0/CLK_CYCLE-1; // 1ms延时
input rst_n; // 全局复位,低电平有效
input clk; // 全局时钟,50MHz
output reg [2:0] min_h; // 分的十位数
output reg [3:0] min_l; // 分的个位数
output reg [2:0] sec_h; // 秒的十位数
output reg [3:0] sec_l; // 秒的个位数
output display_flag; // 数码管动态扫描标志位
reg [15:0] cnt;
always@(posedge clk or negedge rst_n)
begin
if(rst_n == 1'b0)
cnt <= (0);
else if(cnt < T0_VAL)
cnt <= cnt + 1'b1;
else
cnt <= (0);
end
assign delay_1ms = (cnt == T0_VAL); // 1ms延时完成标志位
assign display_flag = delay_1ms; // 数码管动态扫描标志位
reg [9:0] mse;
always@(posedge clk or negedge rst_n)
begin
if(rst_n == 1'b0)
mse <= (0);
else
begin
if(delay_1ms == 1'b1)
begin
if(mse < 10'd999)
mse <= mse + 1'b1;
else
mse <= (0);
end
end
end
wire sec_l_flag = ((mse == 10'd999) && (delay_1ms == 1'b1)); // 1s延时完成标志位
// 秒个位数计数
always@(posedge clk or negedge rst_n)
begin
if(rst_n == 1'b0)
sec_l <= 0;
else
begin
if(sec_l_flag == 1'b1)
begin
if(sec_l < 4'd9)
sec_l <= sec_l + 1'b1;
else
sec_l <= 0;
end
end
end
wire sec_h_flag = ((sec_l == 4'd9) && (sec_l_flag == 1'b1)); // 秒个位数进位标志位
// 秒十位数计数
always@(posedge clk or negedge rst_n)
begin
if(rst_n == 1'b0)
sec_h <= 0;
else
begin
if(sec_h_flag == 1'b1)
begin
if(sec_h < 3'd5)
sec_h <= sec_h + 1'b1;
else
sec_h <= 0;
end
end
end
wire min_l_flag = ((sec_h == 3'd5) && (sec_h_flag == 1'b1)); // 秒十位数进位标志位
// 分个位数计数
always@(posedge clk or negedge rst_n)
begin
if(rst_n == 1'b0)
min_l <= 0;
else
begin
if(min_l_flag == 1'b1)
begin
if(min_l < 4'd9)
min_l <= min_l + 1'b1;
else
min_l <= 0;
end
end
end
wire min_h_flag = ((min_l == 4'd9) && (min_l_flag == 1'b1)); // 分个位数进位标志位
// 分十位数计数
always@(posedge clk or negedge rst_n)
begin
if(rst_n == 1'b0)
min_h <= 0;
else
begin
if(min_h_flag == 1'b1)
begin
if(min_h < 3'd5)
min_h <= min_h + 1'b1;
else
min_h <= 0;
end
end
end
endmodule
五。总结
本设计中主体部分是一个分秒计时器的状态机,运用了分频,数码管的动态扫描等原理。灵活使用了数码管,开关,led等设备实现了计时器,流水灯,显示等,从而完成了一个具有一定可玩性的小游戏。