开发板:EGO1
开发环境:Windows10 + Xilinx Vivado 2020
数字逻辑大作业题目 7: 乒乓球比赛模拟机的设计
乒乓球比赛模拟机用发光二极管(LED)模拟乒乓球运动轨迹,是由甲乙双方参赛,加上裁判的三人游戏(也可以不用裁判)。
管脚约束代码:
点击查看代码set_property IOSTANDARD LVCMOS33 [get_ports CLK]set_property IOSTANDARD LVCMOS33 [get_ports hitA]set_property IOSTANDARD LVCMOS33 [get_ports hitB]set_property PACKAGE_PIN P17 [get_ports CLK]set_property PACKAGE_PIN P5 [get_ports hitA]set_property PACKAGE_PIN R1 [get_ports hitB]set_property IOSTANDARD LVCMOS33 [get_ports {ballLocation[5]}]set_property IOSTANDARD LVCMOS33 [get_ports {ballLocation[4]}]set_property IOSTANDARD LVCMOS33 [get_ports {ballLocation[0]}]set_property IOSTANDARD LVCMOS33 [get_ports {ballLocation[3]}]set_property IOSTANDARD LVCMOS33 [get_ports {ballLocation[2]}]set_property IOSTANDARD LVCMOS33 [get_ports {ballLocation[1]}]set_property IOSTANDARD LVCMOS33 [get_ports {ballLocation[7]}]set_property IOSTANDARD LVCMOS33 [get_ports {ballLocation[6]}]set_property PACKAGE_PIN F6 [get_ports {ballLocation[7]}]set_property PACKAGE_PIN G4 [get_ports {ballLocation[6]}]set_property PACKAGE_PIN G3 [get_ports {ballLocation[5]}]set_property PACKAGE_PIN J4 [get_ports {ballLocation[4]}]set_property PACKAGE_PIN H4 [get_ports {ballLocation[3]}]set_property PACKAGE_PIN J3 [get_ports {ballLocation[2]}]set_property PACKAGE_PIN J2 [get_ports {ballLocation[1]}]set_property PACKAGE_PIN K2 [get_ports {ballLocation[0]}]set_property IOSTANDARD LVCMOS33 [get_ports speedA]set_property PACKAGE_PIN P4 [get_ports speedA]set_property IOSTANDARD LVCMOS33 [get_ports speedB]set_property PACKAGE_PIN N4 [get_ports speedB]set_property IOSTANDARD LVCMOS33 [get_ports {statusOut[3]}]set_property IOSTANDARD LVCMOS33 [get_ports {statusOut[2]}]set_property IOSTANDARD LVCMOS33 [get_ports {statusOut[1]}]set_property PACKAGE_PIN K1 [get_ports {statusOut[3]}]set_property PACKAGE_PIN H6 [get_ports {statusOut[2]}]set_property PACKAGE_PIN M1 [get_ports {statusOut[1]}]set_property PACKAGE_PIN K3 [get_ports {statusOut[0]}]set_property IOSTANDARD LVCMOS33 [get_ports {statusOut[0]}]set_property IOSTANDARD LVCMOS33 [get_ports {LED1[5]}]set_property IOSTANDARD LVCMOS33 [get_ports {LED0[0]}]set_property IOSTANDARD LVCMOS33 [get_ports {LED0[3]}]set_property IOSTANDARD LVCMOS33 [get_ports {LED1[2]}]set_property IOSTANDARD LVCMOS33 [get_ports {LED0[6]}]set_property IOSTANDARD LVCMOS33 [get_ports {LEDBit[1]}]set_property IOSTANDARD LVCMOS33 [get_ports {LEDBit[4]}]set_property IOSTANDARD LVCMOS33 [get_ports {LEDBit[7]}]set_property IOSTANDARD LVCMOS33 [get_ports {LED1[6]}]set_property IOSTANDARD LVCMOS33 [get_ports {LED0[1]}]set_property IOSTANDARD LVCMOS33 [get_ports {LED1[3]}]set_property IOSTANDARD LVCMOS33 [get_ports {LED0[4]}]set_property IOSTANDARD LVCMOS33 [get_ports {LED0[7]}]set_property IOSTANDARD LVCMOS33 [get_ports {LED1[0]}]set_property IOSTANDARD LVCMOS33 [get_ports {LEDBit[2]}]set_property IOSTANDARD LVCMOS33 [get_ports {LEDBit[0]}]set_property IOSTANDARD LVCMOS33 [get_ports {LEDBit[5]}]set_property IOSTANDARD LVCMOS33 [get_ports {LED1[4]}]set_property IOSTANDARD LVCMOS33 [get_ports {LED0[2]}]set_property IOSTANDARD LVCMOS33 [get_ports {LED0[5]}]set_property IOSTANDARD LVCMOS33 [get_ports {LED1[1]}]set_property IOSTANDARD LVCMOS33 [get_ports {LEDBit[3]}]set_property IOSTANDARD LVCMOS33 [get_ports {LEDBit[6]}]set_property IOSTANDARD LVCMOS33 [get_ports {LED1[7]}]set_property PACKAGE_PIN B4 [get_ports {LED0[0]}]set_property PACKAGE_PIN A4 [get_ports {LED0[1]}]set_property PACKAGE_PIN A3 [get_ports {LED0[2]}]set_property PACKAGE_PIN B1 [get_ports {LED0[3]}]set_property PACKAGE_PIN A1 [get_ports {LED0[4]}]set_property PACKAGE_PIN B3 [get_ports {LED0[5]}]set_property PACKAGE_PIN B2 [get_ports {LED0[6]}]set_property PACKAGE_PIN D5 [get_ports {LED0[7]}]set_property PACKAGE_PIN D4 [get_ports {LED1[0]}]set_property PACKAGE_PIN E3 [get_ports {LED1[1]}]set_property PACKAGE_PIN D3 [get_ports {LED1[2]}]set_property PACKAGE_PIN F4 [get_ports {LED1[3]}]set_property PACKAGE_PIN F3 [get_ports {LED1[4]}]set_property PACKAGE_PIN E2 [get_ports {LED1[5]}]set_property PACKAGE_PIN D2 [get_ports {LED1[6]}]set_property PACKAGE_PIN H2 [get_ports {LED1[7]}]set_property PACKAGE_PIN G2 [get_ports {LEDBit[0]}]set_property PACKAGE_PIN C2 [get_ports {LEDBit[1]}]set_property PACKAGE_PIN C1 [get_ports {LEDBit[2]}]set_property PACKAGE_PIN H1 [get_ports {LEDBit[3]}]set_property PACKAGE_PIN G1 [get_ports {LEDBit[4]}]set_property PACKAGE_PIN F1 [get_ports {LEDBit[5]}]set_property PACKAGE_PIN E1 [get_ports {LEDBit[6]}]set_property PACKAGE_PIN G6 [get_ports {LEDBit[7]}]set_property IOSTANDARD LVCMOS33 [get_ports reset]set_property PACKAGE_PIN P2 [get_ports reset]设计要求主要功能模拟乒乓球比赛,用发光二极管(LED)模拟乒乓球运动轨迹,由甲乙双方参赛;用8个LED灯表示球桌,其中点亮的LED来回移动表示乒乓球的运动,球速可以调节;当球移动到最左侧或最右侧时,表示一方的击球位置。如果提前击球,或未及时击球,则对方得一分;甲乙得分使用数码管计分,一局11球;用发光二极管表示甲乙的发球权,每5分交换发球权。附加功能用发光二极管提示甲乙的接球和发球;比赛结束后,用数码管动态显示胜利的一方。工作原理本电路由时钟分频模块,玩家控制器模块,分数处理模块,游戏控制模块,乒乓球运动控制模块和数码管显示模块组成。
比赛开始前,可以通过reset开关重置比赛;比赛进行时,甲乙两位选手通过扳动开关来实现挥动球拍和控制球速的效果。当乒乓球到击球位置时,若选手未及时击球,或提前击球,则输掉一球,对方加一分。每打5球,就交换一次球权,共打11球,数码管上会显示当前得分,分高者获胜;比赛结束后,数码管会显示箭头来表示一方的获胜;另外还有4个LED来表示双方的发球和接球。系统方框图:注意语言为SystemVerilog,源代码文件后缀为sv。乒乓球控制模块为顶层模块各部分模块具体功能及设计思路游戏控制器模块模块功能:控制整个模拟器各组件状态;设计思路:该模块主要是用于控制比赛的进行。在设计中,使用status表示当前的比赛状态。010表示A发球,001表示B发球,110表示玩家A接球,101表示玩家B接球。这样的规定能够有效区分乒乓球不同的运动状态,并判定发/击球的有效性,同时显示在LED灯上来提示选手。另外再用accurateBallLocation [32:0]来表示球的精确位置,范围为$1000_{10} - 9000_{10} $,这样使球在LED显示的误差范围内,可以被击中。代码:点击查看代码`timescale 1ns / 1psmodule GameController( //全局状态控制器input CLK, input reg hitA, //玩家A输入input [1: 0] speedA, //玩家A速度input reg hitB, //玩家B输入input [1: 0] speedB, //玩家B速度input reg serviceSide, //发球方input reg reset,//重置output reg [2: 0] status, //全局状态output reg [7: 0] ballLocation, //球位置output reg getScoreA,//A得分output reg getScoreB//B得分);reg hitATrigger;reg hitBTrigger;reg [2: 0] speed;reg [15: 0] accurateBallLocation;reg resetTrigger;// reg serviceSide;initial begin//初始化变量hitATrigger = 'b0;hitBTrigger = 'b0;status = 'b010;accurateBallLocation = 'd2000;speed = 'd2;// serviceSide = 'b0;getScoreA = 'b0;getScoreB = 'b0;resetTrigger = 'b0;endalways @(posedge CLK) begin //根据报告所述转换状态if(resetTrigger == 'b0 && reset == 'b1) beginhitATrigger = 'b0;hitBTrigger = 'b0;status = 'b010;accurateBallLocation = 'd2000;speed = 'd2;// serviceSide = 'b0;getScoreA = 'b0;getScoreB = 'b0;endelse beginif(status == 'b010 || status == 'b001) begin//换发球status = serviceSide == 'b0 ? 'b010 : 'b001;getScoreA = 'b0;getScoreB = 'b0;endif(status == 'b010) begin //A发球accurateBallLocation = 'd2000;if(hitATrigger == 'b0 && hitA == 'b1) beginstatus = 'b101;if(speedA == 'd00) speed = 'd2;else speed = 'd4;end hitATrigger = hitA;endelse if(status == 'b001) begin //B发球accurateBallLocation = 'd10000;if(hitBTrigger == 'b0 && hitB == 'b1) beginstatus = 'b110;if(speedB == 'd00) speed = 'd2;else speed = 'd4;end hitBTrigger = hitB;endelse if(status == 'b110) begin //A接球if(hitATrigger == 'b0 && hitA == 'b1) beginif(accurateBallLocation >= 'd1000 && accurateBallLocation = 'd9000 && accurateBallLocation = 'd3000 && accurateBallLocation < 'd4000) ballLocation = 'b01000000;if(accurateBallLocation >= 'd4000 && accurateBallLocation < 'd5000) ballLocation = 'b00100000;if(accurateBallLocation >= 'd5000 && accurateBallLocation < 'd6000) ballLocation = 'b00010000;if(accurateBallLocation >= 'd6000 && accurateBallLocation < 'd7000) ballLocation = 'b00001000;if(accurateBallLocation >= 'd7000 && accurateBallLocation < 'd8000) ballLocation = 'b00000100;if(accurateBallLocation >= 'd8000 && accurateBallLocation < 'd9000) ballLocation = 'b00000010;if(accurateBallLocation >= 'd9000 && accurateBallLocation = activeInterval) beginhitOut = hit;endendelse if(hitTrigger == 'b1 && hit == 'b0) begininterval = 'd0;hitOut = hit;endhitTrigger = hit;interval += 1;if(speed == 'b0) beginspeedOut = 'd00;endelse beginspeedOut = 'd01;endendendendmodule时钟分频模块模块功能:对时钟分频;
设计思路:将EG01的100MHZ的时钟分频为1000HZ。
代码:
点击查看代码`timescale 1ns / 1psmodule ClockDivider(originCLK, dividedCLK);input originCLK;output dividedCLK;reg tempDivCLK;reg [31: 0] count;// reg [31: 0] ratio = 'd2;reg [31: 0] ratio = 'd100_000; //时钟分频器,将P17的100MHz分为1000Hzinitial begintempDivCLK = 'b0;count = 'd0;endalways @(posedge originCLK) begincount = count + 1;if(count == ratio)count = 'd0;if(count == 'd0) tempDivCLK = 'b0;if(count == ratio / 2) tempDivCLK = 'b1;endassign dividedCLK = tempDivCLK;endmodule乒乓球控制模块模块功能:接受信号控制乒乓球从左向右移动,或者从右向左移动,并且可以根据玩家选择的击球速度去调整;
设计思路:用8个LED模拟,点亮的灯表示球的位置,然后像流水灯一样来回滚动,在发球时暂停。
代码:这里实际上包括在了游戏控制,下面代码是调用其他的Main。
点击查看代码`timescale 1ns / 1psmodule Main(input CLK, input hitA, input speedA, input hitB, input speedB, input reset,output reg [3: 0] statusOut, output wire [7: 0] ballLocation,output wire [7:0] LED0, output wire [7:0] LED1, output wire [7:0] LEDBit);wire [2: 0] status;wire dividedCLK;wire [1: 0] speedOutA;wire [1: 0] speedOutB;wire getScoreA, getScoreB;ClockDivider clockDivider(CLK, dividedCLK);wire serviceSide;reg EnA;reg EnB;initial beginEnA = 'b1;EnB = 'b1;endPlayer player1(dividedCLK, EnA, hitA, speedA, hitOutA, speedOutA);Player player2(dividedCLK, EnB, hitB, speedB, hitOutB, speedOutB);GameController gameController( //调用全局状态控制器dividedCLK, hitOutA, speedOutA, hitOutB, speedOutB, serviceSide,reset,status, ballLocation, getScoreA, getScoreB);always @(posedge dividedCLK) beginif(status == 'b010) beginstatusOut = 'b1000;endelse if(status == 'b001) beginstatusOut = 'b0001;endelse if(status == 'b110) beginstatusOut = 'b0100;endelse if(status == 'b101) beginstatusOut = 'b0010;endendreg [7:0][7:0] dataIn;reg [31:0] count;initial begin count = 'd0;while(count < 8) begindataIn[count] = 'd100;count ++;endcount = 'd0;endDigitalTubeDriver digitalTubeDriver(//调用数码管驱动dividedCLK, dataIn, LED0, LED1, LEDBit);wire endGame;wire [1:0] winner;wire [15: 0] scoreA;wire [15: 0] scoreB;ScoreBoard scoreBoard(dividedCLK, getScoreA, getScoreB, reset,serviceSide, endGame, winner, scoreA, scoreB);reg [7:0] i;reg [7:0] j;reg [31:0] countTemp;reg [31:0] countTemp2;reg resetTrigger;reg [31: 0] flowLightCount;reg endGameTrigger;initial beginresetTrigger = 'b0;flowLightCount = 'd0;endGameTrigger = 'd0;endalways @(posedge dividedCLK) beginif(resetTrigger == 'b0 && reset == 'b1) beginEnA = 'b1;EnB = 'b1;dataIn[2] = 'd100;//不显示dataIn[3] = 'd100;dataIn[4] = 'd100;dataIn[5] = 'd100;endGameTrigger = 'd0;endresetTrigger = reset;i = 'd0;countTemp = scoreB;while(i < 'd2) begindataIn[i] = countTemp % 'd10;countTemp /= 'd10;i++;endj = 'd6;countTemp2 = scoreA;while(j < 'd8) begindataIn[j] = countTemp2 % 'd10;countTemp2 /= 'd10;j++;endif(endGame == 'b1) begin//游戏结束时显示箭头指向赢的玩家if(endGameTrigger == 'b0) beginEnA = 'b0;EnB = 'b0;endif(winner == 'b10) begincase(flowLightCount)'d100: dataIn[2] = 'd22;//箭头'd200: dataIn[3] = 'd22;'d300: dataIn[4] = 'd22;'d400: dataIn[5] = 'd22;endcaseflowLightCount++;if(flowLightCount == 'd500) beginflowLightCount = 'd0;dataIn[2] = 'd100;dataIn[3] = 'd100;dataIn[4] = 'd100;dataIn[5] = 'd100;end endelse begincase(flowLightCount)'d100: dataIn[5] = 'd21;//箭头'd200: dataIn[4] = 'd21;'d300: dataIn[3] = 'd21;'d400: dataIn[2] = 'd21;endcaseflowLightCount++;if(flowLightCount == 'd500) beginflowLightCount = 'd0;dataIn[2] = 'd100;dataIn[3] = 'd100;dataIn[4] = 'd100;dataIn[5] = 'd100;end endendendGameTrigger = endGame;endendmodule分数处理模块模块功能:计数。每进行一轮控制分数加1,判断是否已打够11球,是则判别出获胜方。
设计思路:在A,B两人分数上升沿时,对总分加1,然后判断是否已满11球。若满11球,比较判断出胜利的一方,随后将其状态传给显示模块用于显示结果。
代码:
点击查看代码`timescale 1ns / 1psmodule ScoreBoard(input CLK, input getScoreA, input getScoreB, input reset,output reg serviceSide, output reg endGame, output reg [1:0] winner, output reg [15: 0] scoreA, output reg [15: 0] scoreB);reg getScoreATrigger;reg getScoreBTrigger;reg resetTrigger;initial beginserviceSide = 'b0;endGame = 'b0;getScoreATrigger = 'b0;getScoreBTrigger = 'b0;scoreA = 'b0;scoreB = 'b0;resetTrigger = 'b0;endalways @(posedge CLK) beginif(resetTrigger == 'b0 && reset == 'b1) beginserviceSide = 'b0;endGame = 'b0;getScoreATrigger = 'b0;getScoreBTrigger = 'b0;scoreA = 'b0;scoreB = 'b0;endelse begin //getScoreA或getScoreB出现上升沿,对应玩家得分if(getScoreATrigger == 'b0 && getScoreA == 'b1)scoreA ++;if(getScoreBTrigger == 'b0 && getScoreB == 'b1)scoreB ++;getScoreATrigger = getScoreA;getScoreBTrigger = getScoreB;if((scoreA + scoreB) / 5 % 2 == 'd0)//每5个球换发serviceSide = 'b0;elseserviceSide = 'b1;if(scoreA + scoreB == 'd11) //到达11个球时游戏结束endGame = 'b1;if(endGame == 1) begin //游戏结束时判断赢的那方if(scoreA > scoreB)winner = 'b10;else if(scoreA < scoreB)winner = 'b01;elsewinner = 'b11;endelse beginwinner = 'b00;endendresetTrigger = reset;endendmodule数码管显示模块模块功能:利用数码管显示比赛数据;
设计思路:使用$ 8 * 8 $的矩阵显示每个数码管的显示情况,另外设有对每个数码管表示显示的标志,从而动态地去更新。在有一方获胜后,会将不显示分数的数码管动态地闪烁箭头,以此来表示获胜的一方。
代码:
点击查看代码`timescale 1ns / 1ps//参考EGO1的数码管显示模块module DigitalTubeDriver(//数码管驱动input CLK, input reg [7:0][7:0] dataIn,//输入数据output reg [7:0] LED0, //输出的LED0,管理前4位显示output reg [7:0] LED1, //输出的LED1,管理后4位显示output reg [7:0] LEDBit //LEDBIT,管理每个亮或不亮);reg [3:0] count;wire [7:0] data0;initial beginLEDBit = 'b00000001;count = 'd0;end// assign LED1 = LED0;always @(posedge CLK) begincase(dataIn[count]) //检查每种数字或符号对应亮哪些边'd0: LED0 = 'b00111111;'d1: LED0 = 'b00000110;'d2: LED0 = 'b01011011;'d3: LED0 = 'b01001111;'d4: LED0 = 'b01100110;'d5: LED0 = 'b01101101;'d6: LED0 = 'b01111101;'d7: LED0 = 'b00000111;'d8: LED0 = 'b01111111;'d9: LED0 = 'b01101111;'d21: LED0 = 'b01110000;'d22: LED0 = 'b01000110;default: LED0 = 'b00000000;endcaseif(count == 'd7) begincount = 'd0;LEDBit = 'b00000001;endelse if(count == 'd0) beginLEDBit = 'b10000000;count = 'd1;endelse begincount++;LEDBit = LEDBit >> 1;endLED1 = LED0;endendmodule参考文献[1] Vivado环境下多个并行的仿真测试文件如何支持单独仿真。
https://blog.csdn.net/CDCL19_220327/article/details/125802252?spm=1001.2014.3001.5502
[2] Vivado里程序固化详细教程。
https://blog.csdn.net/sinat_15674025/article/details/84535754?spm=1001.2014.3001.5502
[3] xilinx vivado 自带仿真工具xsim信号为蓝色Z态的解决办法。
https://blog.csdn.net/Shawge/article/details/107592471?spm=1001.2014.3001.5502
[4] Vivado环境下多个并行的仿真测试文件如何支持单独仿真?
https://blog.csdn.net/CDCL19_220327/article/details/125802252?spm=1001.2014.3001.5502