设计目的
使用FPGA实验班设计一个数字逻辑系统,巩固数字逻辑电路的相关知识,锻炼熟练使用FPGA设计软件和硬件和独立完成项目设计的能力。
二、设计内容
2.1摘要
设计一个乒乓球游戏,实现游戏功能和计分功能。
通过开发板的按键实现左右球拍的击拍和游戏的复位;通过数码管显示游戏的比分;通过led显示乒乓球的运动轨迹。
2.2游戏规则
第一步:按下复位,led不亮,数码管显示比分00–00。
第二步:按下左球拍或者右球拍,发球,游戏开始,发球方向随机。
第三步:乒乓球在左半场/右半场并且靠近左侧/右侧的时候,左球拍/右球拍可以击球。如果在错误的时机击球或未成功接球,判负,对方加一分。
第四步:当有一方积得11分,游戏结束。再次开始游戏需要进行复位。
三、设计思路
3.2流程图
3.2顶层框图
3.3接口说明
名称 类型 封装管脚 说明
clk input PIN_AF14 时钟信号
rst_n input PIN_AA15 全局复位
key_left input PIN_Y16 左球拍
key_right input PIN_AA14 右球拍
Seven_segment output 数码管对应管脚 计分器
led output led对应管脚 运球轨迹
3.4分模块框图
四、代码实现
4.1顶层文件
module play_table_tennis (
inputwireclk,// 50Mhzinputwirerst_n,// low validoutputwire[41:0]seven_segment,// low validoutputwire[9:0]led,// high validinputwirekey_left,// low validinputwirekey_right// low valid
);
wireflag_left;wireflag_right;wire[3:0]left_num;wire[3:0]right_num;key_processor key_processor_inst(.clk(clk),.rst_n(rst_n),.key_left(key_left),.key_right(key_right),.flag_left(flag_left),.flag_right(flag_right));controller controller_inst(.clk(clk),.rst_n(rst_n),.flag_left(flag_left),.flag_right(flag_right),.left_num(left_num),.right_num(right_num),.led(led));seven_tube_drive seven_tube_drive_inst(.clk(clk),.rst_n(rst_n),.left_num(left_num),.right_num(right_num),.seven_segment(seven_segment));
endmodule
4.2按键
module key_processor (
inputwireclk,inputwirerst_n,inputwirekey_left,inputwirekey_right,outputwireflag_left,outputwireflag_right
);
key_filter key_filter_inst_left(.clk(clk),.rst_n(rst_n),.key(key_left),.flag(flag_left));key_filter key_filter_inst_right(.clk(clk),.rst_n(rst_n),.key(key_right),.flag(flag_right));
endmodule
module key_filter (
inputwireclk,inputwirerst_n,inputwirekey,outputregflag
);
parameter T =500_000;reg[18:0]cnt;reg[2:0]state;regkey_r;regkey_rr;always @ (posedge clk, negedge rst_n) beginif (rst_n == 1'b0) beginkey_r <= 1'b1;key_rr <= 1'b1;endelse beginkey_r <= key;key_rr <= key_r;endendalways @ (posedge clk, negedge rst_n) beginif (rst_n == 1'b0) beginstate <= 3'd0;cnt <= 19'd0;flag <= 1'b0;endelse case (state)3'd0:beginstate <= 3'd1;cnt <= 19'd0;flag <= 1'b0;end3'd1:beginif (key_rr == 1'b0)state <= 3'd2;elsestate <= 3'd1;end3'd2:beginif (key_rr == 1'b1) beginstate <= 3'd1;cnt <= 19'd0;endelse if (cnt < T - 1'b1)cnt <= cnt + 1'b1;else begincnt <= 19'd0;state <= 3'd3;flag <= 1'b1;endend3'd3:beginflag <= 1'b0;if (key_rr == 1'b1)state <= 3'd4;elsestate <= 3'd3;end3'd4:beginif (key_rr == 1'b0) beginstate <= 3'd3;cnt <= 19'd0;endelse if (cnt < T - 1'b1)cnt <= cnt + 1'b1;else begincnt <= 19'd0;state <= 3'd0;endenddefault : state <= 3'd0;endcaseend
endmodule
4.3控制器
module controller (
inputwireclk,inputwirerst_n,inputwireflag_left,inputwireflag_right,outputreg[3:0]left_num,outputreg[3:0]right_num,outputreg[9:0]led
);
parameterT=50_000_000;reg[25:0]cnt;wireflag_t;regtime_on;reginit_dir;regdir; // 0 right 1 leftreg[3:0]state;always @ (posedge clk, negedge rst_n) beginif (rst_n == 1'b0)cnt <= 26'd0;elseif (time_on == 1'b0 || flag_left == 1'b1 || flag_right == 1'b1)cnt <= 26'd0;elseif (cnt < T - 1'b1)cnt <= cnt + 1'b1;elsecnt <= 26'd0;endassign flag_t = (cnt == T - 1'b1);always @ (posedge clk, negedge rst_n) beginif (rst_n == 1'b0)init_dir <= 1'b0;elseinit_dir <= ~init_dir;endalways @ (posedge clk, negedge rst_n) beginif (rst_n == 1'b0) beginstate <= 4'd0;dir <= 1'b0;left_num <= 4'd0;right_num <= 4'd0;led <= 10'd0;time_on <= 1'b0;endelsecase (state)4'd0:beginstate <= 4'd1;dir <= 1'b0;left_num <= 4'd0;right_num <= 4'd0;led <= 10'd0;time_on <= 1'b0;end4'd1:beginif (flag_left ==1'b1 || flag_right == 1'b1) beginif (init_dir == 1'b1) begindir <= init_dir;state <= 4'd6;time_on <= 1'b1;endelse begindir <= init_dir;state <= 4'd7;time_on <= 1'b1;endendelse state <= 4'd1;end4'd2:beginled <= 10'b10000_00000;if (flag_right == 1'b1)state <= 4'd12;elseif (flag_left == 1'b1)if (dir == 1'b1) begindir <= ~dir;state <= 4'd3;endelse state <= 4'd13;elseif (flag_t == 1'b1)if (dir == 1'b0)state <= 4'd3;elsestate <= 4'd13;elsestate <= 4'd2;end4'd3:beginled <= 10'b01000_00000;if (flag_right == 1'b1)state <= 4'd12;elseif (flag_left == 1'b1)if (dir == 1'b1) begindir <= ~dir;state <= 4'd4;endelse state <= 4'd13;elseif (flag_t == 1'b1)if (dir == 1'b0)state <= 4'd4;elsestate <= 4'd2;elsestate <= 4'd3;end4'd4:beginled <= 10'b00100_00000;if (flag_right == 1'b1)state <= 4'd12;elseif (flag_left == 1'b1)if (dir == 1'b1) begindir <= ~dir;state <= 4'd5;endelse state <= 4'd13;elseif (flag_t == 1'b1)if (dir == 1'b0)state <= 4'd5;elsestate <= 4'd3;elsestate <= 4'd4;end4'd5:beginled <= 10'b00010_00000;if (flag_right == 1'b1)state <= 4'd12;elseif (flag_left == 1'b1)if (dir == 1'b1) begindir <= ~dir;state <= 4'd6;endelse state <= 4'd13;elseif (flag_t == 1'b1)if (dir == 1'b0)state <= 4'd6;elsestate <= 4'd4;elsestate <= 4'd5;end4'd6:beginled <= 10'b00001_00000;if (flag_right == 1'b1)state <= 4'd12;elseif (flag_left == 1'b1)if (dir == 1'b1) begindir <= ~dir;state <= 4'd7;endelse state <= 4'd13;elseif (flag_t == 1'b1)if (dir == 1'b0)state <= 4'd7;elsestate <= 4'd5;elsestate <= 4'd6;end4'd7:beginled <= 10'b00000_10000;if (flag_left == 1'b1)state <= 4'd13;elseif (flag_right == 1'b1)if (dir == 1'b0) begindir <= ~dir;state <= 4'd6;endelse state <= 4'd12;elseif (flag_t == 1'b1)if (dir == 1'b0)state <= 4'd8;elsestate <= 4'd6;elsestate <= 4'd7;end4'd8:beginled <= 10'b00000_01000;if (flag_left == 1'b1)state <= 4'd13;elseif (flag_right == 1'b1)if (dir == 1'b0) begindir <= ~dir;state <= 4'd7;endelse state <= 4'd12;elseif (flag_t == 1'b1)if (dir == 1'b0)state <= 4'd9;elsestate <= 4'd7;elsestate <= 4'd8;end4'd9:beginled <= 10'b00000_00100;if (flag_left == 1'b1)state <= 4'd13;elseif (flag_right == 1'b1)if (dir == 1'b0) begindir <= ~dir;state <= 4'd8;endelse state <= 4'd12;elseif (flag_t == 1'b1)if (dir == 1'b0)state <= 4'd10;elsestate <= 4'd8;elsestate <= 4'd9;end4'd10:beginled <= 10'b00000_00010;if (flag_left == 1'b1)state <= 4'd13;elseif (flag_right == 1'b1)if (dir == 1'b0) begindir <= ~dir;state <= 4'd9;endelse state <= 4'd12;elseif (flag_t == 1'b1)if (dir == 1'b0)state <= 4'd11;elsestate <= 4'd9;elsestate <= 4'd10;end4'd11:beginled <= 10'b00000_00001;if (flag_left == 1'b1)state <= 4'd13;elseif (flag_right == 1'b1)if (dir == 1'b0) begindir <= ~dir;state <= 4'd10;endelse state <= 4'd12;elseif (flag_t == 1'b1)if (dir == 1'b0)state <= 4'd12;elsestate <= 4'd10;elsestate <= 4'd11;end4'd12:beginled <= 10'd0;time_on <= 1'b0;if (left_num < 4'd10) beginstate <= 4'd1;left_num <= left_num + 1'b1;endelse beginstate <= 4'd14;left_num <= left_num + 1'b1;endend4'd13:beginled <= 10'd0;time_on <= 1'b0;if (right_num < 4'd10) beginstate <= 4'd1;right_num <= right_num + 1'b1;endelse beginstate <= 4'd14;right_num <= right_num + 1'b1;endend4'd14:state <= 4'd14;default : state <= 4'd0;endcaseend
endmodule
4.4数码管显示
module seven_tube_drive (
inputwireclk,inputwirerst_n,inputwire[3:0]left_num,inputwire[3:0]right_num,outputwire[41:0]seven_segment
);
wire[3:0]data_0;wire[3:0]data_1;wire[3:0]data_2;wire[3:0]data_3;wire[3:0]data_4;wire[3:0]data_5;show_data_sel show_data_sel_inst(.clk(clk),.rst_n(rst_n),.left_num(left_num),.right_num(right_num),.data_0(data_0),.data_1(data_1),.data_2(data_2),.data_3(data_3),.data_4(data_4),.data_5(data_5));single_seven_tube_drive single_seven_tube_drive_inst0(.clk(clk),.rst_n(rst_n),.data(data_0),.seven_segment(seven_segment[6:0]));single_seven_tube_drive single_seven_tube_drive_inst1(.clk(clk),.rst_n(rst_n),.data(data_1),.seven_segment(seven_segment[13:7]));single_seven_tube_drive single_seven_tube_drive_inst2(.clk(clk),.rst_n(rst_n),.data(data_2),.seven_segment(seven_segment[20:14]));single_seven_tube_drive single_seven_tube_drive_inst3(.clk(clk),.rst_n(rst_n),.data(data_3),.seven_segment(seven_segment[27:21]));single_seven_tube_drive single_seven_tube_drive_inst4(.clk(clk),.rst_n(rst_n),.data(data_4),.seven_segment(seven_segment[34:28]));single_seven_tube_drive single_seven_tube_drive_inst5(.clk(clk),.rst_n(rst_n),.data(data_5),.seven_segment(seven_segment[41:35]));
endmodule
module single_seven_tube_drive (
inputwireclk,inputwirerst_n,inputwire[3:0]data,outputreg[6:0]seven_segment
);
always @ (posedge clk, negedge rst_n) beginif (rst_n == 1'b0)seven_segment <= 7'h7f;elsecase (data)4'd0:seven_segment <= 7'b100_0000;4'd1:seven_segment <= 7'b111_1001;4'd2:seven_segment <= 7'b010_0100;4'd3:seven_segment <= 7'b011_0000;4'd4:seven_segment <= 7'b001_1001;4'd5:seven_segment <= 7'b001_0010;4'd6:seven_segment <= 7'b000_0010;4'd7:seven_segment <= 7'b111_1000;4'd8:seven_segment <= 7'b000_0000;4'd9:seven_segment <= 7'b001_0000;4'd10:seven_segment <= 7'b011_1111;default:seven_segment <= 7'h7f;endcaseend
endmodule
module show_data_sel (
inputwireclk,inputwirerst_n,inputwire[3:0]left_num,inputwire[3:0]right_num,outputreg[3:0]data_0,outputreg[3:0]data_1,outputwire[3:0]data_2,outputwire[3:0]data_3,outputreg[3:0]data_4,outputreg[3:0]data_5
);
assign data_2 = 4'ha;assign data_3 = 4'ha;always @ (posedge clk, negedge rst_n) beginif (rst_n == 1'b0)data_0 <= 4'hb;elsedata_0 <= right_num%10;endalways @ (posedge clk, negedge rst_n) beginif (rst_n == 1'b0)data_1 <= 4'hb;elsedata_1 <= right_num/10;endalways @ (posedge clk, negedge rst_n) beginif (rst_n == 1'b0)data_4 <= 4'hb;elsedata_4 <= left_num%10;endalways @ (posedge clk, negedge rst_n) beginif (rst_n == 1'b0)data_5 <= 4'hb;elsedata_5 <= left_num/10;end
endmodule
五、顶层仿真
5.1testbench代码
`timescale 1ns/1ps
module play_table_tennis_tb;
regclk;regrst_n;wire[41:0]seven_segment;wire[9:0]led;regkey_left;regkey_right;defparam play_table_tennis_inst.key_processor_inst.key_filter_inst_left.T = 10;defparam play_table_tennis_inst.key_processor_inst.key_filter_inst_right.T = 10;defparam play_table_tennis_inst.controller_inst.T = 100;play_table_tennis play_table_tennis_inst(clk,// 50Mhzrst_n,// low validseven_segment,// low validled,// high validkey_left,// low validkey_right// low valid);initial clk = 1'b0;always # 10 clk = ~clk;initial beginrst_n = 1'b0;key_left = 1'b1;key_right = 1'b1;# 201rst_n = 1'b1;# 200;press_left;press_left;press_left;press_left;press_left;press_left;press_left;press_left;press_left;press_right;press_right;press_right;press_right;press_right;press_right;$stop;endtask press_left;begin@ (posedge clk);# 2;key_left = 1'b0;# 500@ (posedge clk);# 2;key_left = 1'b1;# 5000;endendtasktask press_right;begin@ (posedge clk);# 2;key_right = 1'b0;# 500@ (posedge clk);# 2;key_right = 1'b1;# 5000;endendtask
endmodule
5.2仿真波形
在仿真中,模拟击打左球拍和击打有球拍的情况,从仿真波形中可以看出,当模拟击打球拍时,LED显示出乒乓球运动轨迹,再次击打球拍后,重新发球,数码管计分器数字变化。
六、下板实验
6.1引脚绑定
6.2下板实验