目录
一、摘要
1.1功能:
按照篮球赛赛制进行设计。须具有24秒倒计时功能,十二分钟计时功能,暂停功能,进球计分功能(1分,2分,3分)等。
1.2要求:
比分与计时须在数码管实时显示,显示状态可通过按键或者拨码开关切换,计分可通过按键进行设计(不限制)。
1.3要点:
顶层模块的使用(Top-Down思想),倒计时的实现,两队的记分(10进制转8421BCD码),动态数码管,按键消抖。
二、各模块的总结
2.1、功能描述
功能1:24s倒计时
(1)实现从24:00s至00:00s的倒计时,计时精度为0.01s。
(2)暂停计时功能。
(3)每节比赛和每个进攻回合结束,开始新的一轮24s倒计时。
功能2:12min倒计时
(1)实现从12:00min至00:00min的倒计时,计时精度为1s。
(2)暂停计时功能。
(3)每节比赛,开始新的一轮12min倒计时。
功能3:两个比赛队记分
(1)实现红蓝两队的计分。
(2)进球记分功能,包含三分球,两分球和一分球。
2.2系统结构框图
2.3I/O说明
输入端口:
clk
rst_n //总开关
sys_key_1//一分球按键
sys_key_2//两分球按键
sys_key_3//三分球按键
key_team //切换队伍进行进球记分
sys_key_ctrl//按键控制
en_24s //24s使能(复位)
en_score //记分使能(复位)
en_stop_n//暂停使能
en_time //倒计时复位
输出端口:
dis_sel //位选输出
dis_seg //段选输出
三、各模块详细设计
3.1倒计时模块:
小模块1:16位输出的控制(倒计时的主模块)
这一部分的算法我没有使用加三移位转BCD码的方法,而是使用最小精度的时钟分频(24s倒计时最小精度为0.01s),再利用if-else语句对多种情况进行判断赋值。
具体解释:
if-else语句判别条件的罗列
- :if(复位信号==0)
- :else if([最高位:0]==0)
- :else if([本位 :0]==0)
- :else if([低位 :0]==0) (低位=(本位—4))
- : else
([本位 :本位—4])
各判别语句的赋值,
①,② 输出: [本位 :本位—4]=初始值;
③ 输出:[本位 :本位—4]= 4’h9
④ 输出:[本位 :本位—4]= [本位 :本位—4]—1’b1;
⑤ 输出:[本位 :本位—4]= [本位 :本位—4];
3.2两队比分模块
小模块1:16位记分输出
首先定义两个8位的reg信号r_out和b_out,用来寄存两队的得分,输出时用assign语句将r_out和b_out拼接后赋值给16位的输出score。
在描述两个reg信号时,例如r_out,由于牵扯到要将十进制数转化为8421BCD码,这里还是没有采用加三移位法,我利用的是将一分、两分和三分分为三种情况。
例如三分,只需考虑低四位r_out[3:0]。
当(r_out[3:0]<4'd7)时,不需要考虑向高四位进位,只需给第三位正常加3即可。
当(r_out[3:0]>=4'd7)时,此时需要向高四位进位,并且根据加三后的数值大小
判断低四位大小,此例为每次加三分,因而考虑三种情况即可。
代码展示:
always@(posedge clk or negedge rst_n)begin if(!rst_n) r_out<= 8'd0; else if(!en_score) r_out<= 8'd0; else if(en_stop_n==0) r_out<= r_out; 3fen else if(key_3==1&&key_team==0)begin if(r_out[3:0]<4'd7)begin r_out[3:0]<= r_out[3:0]+2'd3; r_out[7:4]<= r_out[7:4]; end else if(r_out[3:0]==4'd7)begin r_out[3:0]<= 4'h0; r_out[7:4]<= r_out[7:4]+4'h1; end else if(r_out[3:0]==4'd8)begin r_out[3:0]<= 4'h1; r_out[7:4]<= r_out[7:4]+4'h1; end else if(r_out[3:0]==4'd9)begin r_out[3:0]<= 4'h2; r_out[7:4]<= r_out[7:4]+4'h1; end end end
3.3 control控制及数码管模块
设计思想:
在control模块中,利用计数器计数,当输入信号(key_ctrl)为1时cnt复位,使输出 (ctrl)循环计数,从而确定数码管显示哪组数据。
在动态数码管模块中,定义两个reg信号 [15:0] dis_num, [3:0] dis_tmp,第一个用来寄存要显示的16位数据,第二个用来寄存16位数据中4位的值,而寄存哪四位由分频时钟控制,使16位数据循环输入,从而形成动态显示。
四、代码设计
4.1顶层模块
module top_basketball( input rst_n, input clk, input sys_key_1, input sys_key_2, input sys_key_3, input key_team, input sys_key_ctrl, input en_24s, input en_score, input en_stop_n, input en_time, output wire[3:0] dis_sel, output wire[6:0] dis_seg ); wire [15:0]times_24s; wire [15:0]times_12min; wire [15:0]score; wire [2:0] ctrl; wire key_1; wire key_2; wire key_3; wire key_ctrl; xiaodou s0 ( .clk(clk), .rst_n(rst_n), .key_in(sys_key_1), .key_flag(key_1) ); xiaodou s1 ( .clk(clk), .rst_n(rst_n), .key_in(sys_key_2), .key_flag(key_2) ); xiaodou s2 ( .clk(clk), .rst_n(rst_n), .key_in(sys_key_3), .key_flag(key_3) ); counter_r_b s4( .clk(clk), .rst_n(rst_n), .key_1(key_1), .key_2(key_2), .key_3(key_3), .en_score(en_score), .en_stop_n(en_stop_n), .key_team(key_team), .score(score) ); xiaodou s3 ( .clk(clk), .rst_n(rst_n), .key_in(sys_key_ctrl), .key_flag(key_ctrl) ); control_1 b1( .clk(clk), .rst_n(rst_n), .key_ctrl(key_ctrl), .ctrl(ctrl) ); counter_24s h0( . clk(clk), . rst_n(rst_n), . en_stop_n(en_stop_n), . en_time(en_time), . en_24s(en_24s), . times_24s(times_24s) ); counter_12min p1( . clk(clk), . rst_n(rst_n), . en_stop_n(en_stop_n), . en_time(en_time), . times_12min(times_12min) ); shumaguan_basket n1( .clk(clk), .ctrl(ctrl), .rst_n(rst_n), .times_24s(times_24s), .times_12min(times_12min), .score(score), .dis_sel (dis_sel), .dis_seg (dis_seg) ); endmodule
4.2按键消抖模块
module xiaodou( input clk, input rst_n, input key_in, output reg key_flag ); localparam IDEL = 4'b0001, FILTER0 = 4'b0010, DOWN = 4'b0100, FILTER1 = 4'b1000; reg [3:0] state; reg key_tem0; reg key_tem1; wire nedge; wire pedge; reg [19:0] cnt; reg en_cnt; reg cnt_full; always@(posedge clk or negedge rst_n) begin if(rst_n==0) begin key_tem0<=0; key_tem1<=0; end else begin key_tem0 <= key_in; key_tem1 <= key_tem0; end end assign nedge = !key_tem0 & key_tem1; assign pedge = key_tem0 & (!key_tem1); always@(posedge clk or negedge rst_n )begin if(rst_n==0) begin state <= IDEL; en_cnt <= 1'b0; key_flag<=1'd0; end else case(state) IDEL: begin key_flag<=1'b0; if(nedge) begin state <= FILTER0; en_cnt <= 1'b1; end else state <= IDEL; end FILTER0: if(cnt_full) begin state<= DOWN; en_cnt<=1'b0; key_flag<=1'b1; end else if(pedge) begin state<= IDEL; en_cnt <= 1'b0; end else state<= FILTER0; DOWN: begin key_flag<=1'b0; if(pedge) begin state<=FILTER1; en_cnt<=1'b1; end else state<= DOWN; end FILTER1: if(cnt_full) begin state<= IDEL; en_cnt<=1'b0; key_flag<=1'b0; end else if(nedge) begin state<= DOWN; en_cnt <= 1'b0; end else state<= FILTER1; default: begin state<=IDEL; en_cnt<=1'b0; key_flag<=1'b0; end endcase end always@(posedge clk or negedge rst_n) begin if(rst_n==0) cnt <= 20'd0; else if(en_cnt) cnt <= cnt + 1'b1; else cnt<= 20'd0; end always@(posedge clk or negedge rst_n) begin if(rst_n==0) cnt_full <= 1'b0; else if(cnt == 'd999_999) cnt_full <= 1'b1; else cnt_full <= 1'b0; end endmodule
4.3control控制模块
module control_1( input clk, input rst_n, input key_ctrl, output reg [2:0] ctrl ); reg [1:0] cnt; always@(posedge clk or negedge rst_n) begin if(rst_n==0) cnt<=0; else if(cnt==2'd2&&key_ctrl == 1) cnt<=0; else if(key_ctrl == 1) cnt<=cnt+1'd1; end always@(posedge clk or negedge rst_n) begin if(rst_n==0) begin ctrl<=0; end else if(cnt==0) begin ctrl<=3'b000; end else if(cnt==1) begin ctrl<=3'b001; end else if(cnt==2'd2) begin ctrl<=3'b010; end end endmodule
4.4 24s倒计时模块
module counter_24s( input clk,rst_n,en_stop_n,en_time,en_24s, output reg [15:0]times_24s ); parameter timer_1 = 19'd250_000; reg [18:0]cnt; reg div_1s; always@(posedge clk or negedge rst_n)begin if(!rst_n) begin cnt<=1'b0; div_1s<=1'b1; end else if(cnt==(timer_1-1))begin cnt<=1'b0; div_1s<=~div_1s; end else if(en_stop_n==0) cnt<= cnt; else cnt<=cnt+1'b1; end always @(posedge div_1s or negedge rst_n)begin if(!rst_n) times_24s[7:4]<=4'h0; else if(en_time==0) times_24s[7:4]<=4'h0; else if(en_24s==0) times_24s[7:4]<=4'h0; else if({times_24s[15:12],times_24s[11:8],times_24s[7:4],times_24s[3:0]}==16'h0000) times_24s[7:4]<=4'h0; else if({times_24s[7:4],times_24s[3:0]}==8'h00) times_24s[7:4]<=4'h9; else if(times_24s[3:0]==4'h0) times_24s[7:4]<=times_24s[7:4]-1'b1; else times_24s[7:4]<=times_24s[7:4]; end always@(posedge div_1s or negedge rst_n)begin if(!rst_n) times_24s[3:0]<=4'h0; else if(en_time==0) times_24s[3:0]<=4'h0; else if(en_24s==0) times_24s[3:0]<=4'h0; else if({times_24s[15:12],times_24s[11:8],times_24s[7:4],times_24s[3:0]}==16'h0000) times_24s[3:0]<=4'h0; else if(times_24s[3:0]==4'h0) times_24s[3:0]<=4'h9; else times_24s[3:0]<=times_24s[3:0]-1'b1; end always @(posedge div_1s or negedge rst_n)begin if(!rst_n) times_24s[15:12]<=4'h2; else if(en_time==0) times_24s[15:12]<=4'h2; else if(en_24s==0) times_24s[15:12]<=4'h2; else if({times_24s[15:12],times_24s[11:8],times_24s[7:4],times_24s[3:0]}==16'h0000) times_24s[15:12]<=4'h2; else if({times_24s[11:8],times_24s[7:4],times_24s[3:0]}==12'h000) times_24s[15:12]<=times_24s[15:12]-1'b1; else times_24s[15:12]<=times_24s[15:12]; end always@(posedge div_1s or negedge rst_n)begin if(!rst_n) times_24s[11:8]<=4'h4; else if(en_time==0) times_24s[11:8]<=4'h4; else if(en_24s==0) times_24s[11:8]<=4'h4; else if({times_24s[15:12],times_24s[11:8],times_24s[7:4],times_24s[3:0]}==16'h0000) times_24s[11:8]<=4'h4; else if({times_24s[11:8],times_24s[7:4],times_24s[3:0]}==12'h000) times_24s[11:8]<=4'h9; else if({times_24s[7:4],times_24s[3:0]}==8'h00) times_24s[11:8]<=times_24s[11:8]-1'b1; else times_24s[11:8]<=times_24s[11:8]; end endmodule
4.5 12min倒计时模块
module counter_12min( input clk, input rst_n, input en_stop_n,//暂停功能——>控制计数器 input en_time, //一节比赛结束——>24s以及12min倒计时复位 output reg [15:0]times_12min// ); ///分频模块(分频出1秒钟时钟信号) parameter timer = 26'd25_000_000;///1s=10^9ns=50_000_000*20ns reg [25:0]cnt; reg div_1s; always@(posedge clk or negedge rst_n)begin if(!rst_n) begin cnt<=1'b0; div_1s<=1'b1; end else if(cnt==(timer-1))begin cnt<=1'b0; div_1s<=~div_1s; end else if(en_stop_n==0) cnt<= cnt; else cnt<=cnt+1'b1; end ///倒计时模块 always @(posedge div_1s or negedge rst_n)begin if(!rst_n) times_12min[7:4]<=4'h0; else if(en_time==0) times_12min[7:4]<=4'h0; else if({times_12min[15:12],times_12min[11:8],times_12min[7:4],times_12min[3:0]}==16'h0000) times_12min[7:4]<=4'h0; else if({times_12min[7:4],times_12min[3:0]}==8'h00) times_12min[7:4]<=4'h5; else if(times_12min[3:0]==4'h0) times_12min[7:4]<=times_12min[7:4]-1'b1; else times_12min[7:4]<=times_12min[7:4]; end always@(posedge div_1s or negedge rst_n)begin if(!rst_n) times_12min[3:0]<=4'h0; else if(en_time==0) times_12min[3:0]<=4'h0; else if({times_12min[15:12],times_12min[11:8],times_12min[7:4],times_12min[3:0]}==16'h0000) times_12min[3:0]<=4'h0; else if(times_12min[3:0]==4'h0) times_12min[3:0]<=4'h9; else times_12min[3:0]<=times_12min[3:0]-1'b1; end always @(posedge div_1s or negedge rst_n)begin if(!rst_n) times_12min[15:12]<=4'h1; else if(en_time==0) times_12min[15:12]<=4'h1; else if({times_12min[15:12],times_12min[11:8],times_12min[7:4],times_12min[3:0]}==16'h0000) times_12min[15:12]<=4'h1; else if({times_12min[11:8],times_12min[7:4],times_12min[3:0]}==12'h000) times_12min[15:12]<=times_12min[15:12]-1'b1; else times_12min[15:12]<=times_12min[15:12]; end always@(posedge div_1s or negedge rst_n)begin if(!rst_n) times_12min[11:8]<=4'h2; else if(en_time==0) times_12min[11:8]<=4'h2; else if({times_12min[15:12],times_12min[11:8],times_12min[7:4],times_12min[3:0]}==16'h0000) times_12min[11:8]<=4'h2; else if({times_12min[11:8],times_12min[7:4],times_12min[3:0]}==12'h000) times_12min[11:8]<=4'h9; else if({times_12min[7:4],times_12min[3:0]}==8'h00) times_12min[11:8]<=times_12min[11:8]-1'b1; else times_12min[11:8]<=times_12min[11:8]; end endmodule
4.6两个比赛队记分模块
module counter_r_b( input rst_n, input clk, input key_1, input key_2, input key_3, input key_team, input en_score, input en_stop_n, output [15:0] score ); reg [7:0] r_out; reg [7:0] b_out; ///蓝队 always@(posedge clk or negedge rst_n)begin if(!rst_n) b_out<= 8'd0; else if(!en_score) b_out<= 8'd0; else if(en_stop_n==0) b_out<= b_out; 1fen else if(key_1==1&&key_team==1)begin if(b_out[3:0]<4'd9)begin b_out[3:0]<= b_out[3:0]+2'd1; b_out[7:4]<= b_out[7:4]; end else if(b_out[3:0]==4'd9)begin b_out[3:0]<= 4'h0; b_out[7:4]<= b_out[7:4]+4'h1; end end 2fen else if(key_2==1&&key_team==1)begin if(b_out[3:0]<4'd8)begin b_out[3:0]<= b_out[3:0]+2'd2; b_out[7:4]<= b_out[7:4]; end else if(b_out[3:0]==4'd8)begin b_out[3:0]<= 4'h0; b_out[7:4]<= b_out[7:4]+4'h1; end else if(b_out[3:0]==4'd9)begin b_out[3:0]<= 4'h1; b_out[7:4]<= b_out[7:4]+4'h1; end end 3fen else if(key_3==1&&key_team==1)begin if(b_out[3:0]<4'd7)begin b_out[3:0]<= b_out[3:0]+2'd3; b_out[7:4]<= b_out[7:4]; end else if(b_out[3:0]==4'd7)begin b_out[3:0]<= 4'h0; b_out[7:4]<= b_out[7:4]+4'h1; end else if(b_out[3:0]==4'd8)begin b_out[3:0]<= 4'h1; b_out[7:4]<= b_out[7:4]+4'h1; end else if(b_out[3:0]==4'd9)begin b_out[3:0]<= 4'h2; b_out[7:4]<= b_out[7:4]+4'h1; end end end ///红队得分 always@(posedge clk or negedge rst_n)begin if(!rst_n) r_out<= 8'd0; else if(!en_score) r_out<= 8'd0; else if(en_stop_n==0) r_out<= r_out; 1fen else if(key_1==1&&key_team==0)begin if(r_out[3:0]<4'd9)begin r_out[3:0]<= r_out[3:0]+2'd1; r_out[7:4]<= r_out[7:4]; end else if(r_out[3:0]==4'd9)begin r_out[3:0]<= 4'h0; r_out[7:4]<= r_out[7:4]+4'h1; end end 2fen else if(key_2==1&&key_team==0)begin if(r_out[3:0]<4'd8)begin r_out[3:0]<= r_out[3:0]+2'd2; r_out[7:4]<= r_out[7:4]; end else if(r_out[3:0]==4'd8)begin r_out[3:0]<= 4'h0; r_out[7:4]<= r_out[7:4]+4'h1; end else if(r_out[3:0]==4'd9)begin r_out[3:0]<= 4'h1; r_out[7:4]<= r_out[7:4]+4'h1; end end 3fen else if(key_3==1&&key_team==0)begin if(r_out[3:0]<4'd7)begin r_out[3:0]<= r_out[3:0]+2'd3; r_out[7:4]<= r_out[7:4]; end else if(r_out[3:0]==4'd7)begin r_out[3:0]<= 4'h0; r_out[7:4]<= r_out[7:4]+4'h1; end else if(r_out[3:0]==4'd8)begin r_out[3:0]<= 4'h1; r_out[7:4]<= r_out[7:4]+4'h1; end else if(r_out[3:0]==4'd9)begin r_out[3:0]<= 4'h2; r_out[7:4]<= r_out[7:4]+4'h1; end end end assign score = {r_out[7:0],b_out[7:0]}; endmodule
4.7数码管显示模块
module shumaguan_basket( input clk, input rst_n, input [2:0]ctrl, input [15:0]times_12min, input [15:0]times_24s, input [15:0]score, output reg [3:0] dis_sel, output reg [6:0] dis_seg ); reg [15:0] dis_num; reg [3:0] dis_tmp; reg [14:0] div_cnt; reg div_clk; parameter timer = 15'd25000; //分频信号实现动态显示的位选 always@(posedge clk or negedge rst_n) if(!rst_n)begin div_cnt<= 15'd0; div_clk<= 0; end else if(div_cnt>=(timer-1))begin div_cnt<= 15'd0; div_clk<= ~div_clk; end else begin div_cnt<= div_cnt+1; div_clk<= div_clk; end always@(posedge div_clk or negedge rst_n) begin if(rst_n==0) dis_sel<=4'b1110; else dis_sel<={dis_sel[2:0],dis_sel[3]}; end //ctrl控制显示哪个数据 always@(posedge clk or negedge rst_n) begin if(rst_n==0) dis_num<=0; else case(ctrl) 3'b000:dis_num<=times_24s; 3'b001:dis_num<=times_12min; 3'b010:dis_num<=score; default:dis_num<=16'd0; endcase end //分配数据的16位,低电平有效 always@(*) begin case(dis_sel) 4'b1110 : dis_tmp<= dis_num[3:0]; 4'b1101 : dis_tmp<= dis_num[7:4]; 4'b1011 : dis_tmp<= dis_num[11:8]; 4'b0111 : dis_tmp<= dis_num[15:12]; default:dis_tmp<=4'b1111; endcase end always@(*)begin if(!rst_n) dis_seg = 7'h0000000; else begin case(dis_tmp) 4'h0: dis_seg = 7'b1000000; 4'h1: dis_seg = 7'b1111001; 4'h2: dis_seg = 7'b0100100; 4'h3: dis_seg = 7'b0110000; 4'h4: dis_seg = 7'b0011001; 4'h5: dis_seg = 7'b0010010; 4'h6: dis_seg = 7'b0000010; 4'h7: dis_seg = 7'b1111000; 4'h8: dis_seg = 7'b0000000; 4'h9: dis_seg = 7'b0010000; default:dis_seg = 7'b0000000; endcase end end endmodule
五、仿真验证
5.1 tb仿真文件
module tb_top_basket; // Inputs reg rst_n; reg clk; reg en_24s; reg en_score; reg en_stop_n; reg en_time; reg sys_key_ctrl; reg sys_key_1; reg sys_key_2; reg sys_key_3; reg key_team; // Outputs wire [3:0] dis_sel; wire [6:0] dis_seg; // Instantiate the Unit Under Test (UUT) top_basketball uut ( .rst_n(rst_n), .clk(clk), .en_24s(en_24s), .en_score(en_score), .en_stop_n(en_stop_n), .sys_key_ctrl(sys_key_ctrl), .en_time(en_time), .sys_key_1(sys_key_1), .sys_key_2(sys_key_2), .sys_key_3(sys_key_3), .key_team(key_team), .dis_sel(dis_sel), .dis_seg(dis_seg) ); always #50 clk = ~clk; initial begin // Initialize Inputs rst_n = 0; clk = 1; ///拨码开关 en_24s = 0; en_time = 0; en_stop_n = 0; en_score = 0; key_team = 0; ///按键 sys_key_ctrl = 0; sys_key_1 = 0; sys_key_2 = 0; sys_key_3 = 0; // Wait 100 ns for global reset to finish #201; rst_n = 1; ///正常倒计时 #100; en_24s = 1; en_time = 1; en_stop_n = 1; ///暂停功能 #100000000; en_stop_n = 0; #100000; en_stop_n = 1; ///切换显示12min倒计时 #100; sys_key_ctrl = 1; ///切换显示两队比分 #1000000; sys_key_ctrl = 1; 红队得分 #100; sys_key_1 = 1; sys_key_2 = 0; sys_key_3 = 0; #100; sys_key_1 = 0; sys_key_2 = 1; sys_key_3 = 0; #100; sys_key_1 = 0; sys_key_2 = 0; sys_key_3 = 1; 蓝队得分 #100; key_team = 1; sys_key_1 = 1; sys_key_2 = 0; sys_key_3 = 0; #100; sys_key_1 = 0; sys_key_2 = 1; sys_key_3 = 0; #100; sys_key_1 = 0; sys_key_2 = 0; sys_key_3 = 1; end endmodule
5.2 basys2开发板验证
FPGA篮球计分器-Verilog语言