add all source and report

master
cxz66666 2021-01-17 23:11:31 +08:00
parent bedf471d40
commit b4bb415f1e
54 changed files with 885 additions and 57 deletions

732
README.md
View File

@ -1,9 +1,729 @@
# FPGA-TankGame
sz @ 12.28
距离v1.0还有的bug和未完成的 ## 一、项目背景介绍
《坦克大战》Battle City是1985年日本南梦宫Namco游戏公司在任天堂FC平台上推出的一款多方位平面射击游戏。游戏以坦克战斗及保卫基地为主题属于策略型联机类。同时也是FC平台上少有的内建关卡编辑器的几个游戏之一玩家可自己创建独特的关卡并通过获取一些道具使坦克和基地得到强化。本游戏从此获取灵感并基于此进行改编是一款规则简单内容趣味性高可玩性强的游戏。
## 二、游戏说明
#### 1. 游戏基本规则
1. 该游戏为双人游戏,两位玩家同属一方,分别控制一辆坦克,根据模式的不同,开展竞争或是合作,摧毁敌方坦克。
2. 敌方坦克共有四辆均由AI控制。
3. 坦克可以在地图范围内自由移动,除非其与其他坦克发生碰撞。
4. 坦克可以向当前运动方向射击,但是同一时间一辆坦克在地图内**只允许存在一发子弹**。
5. 当坦克与非本方子弹产生任何接触坦克即视作被摧毁并在2s后复活同时该子弹也会被摧毁。
6. 坦克重生后将有1s的无敌时间。
7. 场上将会随机产生道具:加速,冻结,时间增加,生命增加,无敌,我方坦克吃到之后将会有相应效果。
本游戏共有两种模式:标准模式和无尽模式,下面我们分别对这两个模式的规则和胜利条件进行简要介绍。
##### (a) 经典模式
场上将会有两辆我方坦克和四辆敌方坦克,两位玩家将会展开对抗,**最先摧毁14辆敌方坦克的玩家获得胜利**。每辆我方坦克拥有预先设定好的生命值若被敌方坦克击中生命值减一并会在2s后重新投入战斗。**若生命值降为0则游戏结束**,另一方获得胜利。
##### (b) 无尽模式
场上将会有两辆我方坦克和四辆敌方坦克,两位玩家将展开合作,共同在预先设定好的时间内**摧毁尽可能多的坦克**。每辆我方坦克拥有3点生命值每摧毁一辆敌方坦克我方的剩余时间将会增加不超过预设上限**若剩余时间降为0则游戏结束**。此模式无胜利方。
#### 2. 游戏流程及操作说明
1. 游戏开始前在主界面中利用SW[0]选择模式0为经典模式1为无尽模式。
2. 在经典模式下可以设定血量的多少SW[1]控制血量加减0为减1为加再通过设定SW[2]完成加减操作。
3. 在无尽模式下可以设定时间的多少每个时间图标代表1s具体操作与血量设定相同。
4. 按下BTNC开始游戏。
5. 对于玩家A键盘WSAD分别控制上下左右J键开火。
6. 对于玩家B键盘上下左右分别控制上下左右0键开火。
7. 游戏结束时按下BTNU可以回到主界面
#### 3. 道具说明
**加速**加快全场运动物敌我双方坦克和子弹的运动速度持续9s。
**冻结**使敌方坦克停止运动持续4s。
**时间增加**:将时间增加到预设的初始值。
**生命增加**增加拾取坦克的生命值1。
**无敌**坦克免疫所有敌方子弹攻击持续8s。
#### 4. 游戏界面说明
**(a) 经典模式**
经典模式的游戏界面如下图所示。屏幕上方显示了当前模式经典以及双方坦克所摧毁的敌方坦克数。每摧毁一个敌方坦克我们就为ta的计分板上插上一面红旗。屏幕左右两侧分别显示的是玩家1和玩家2所剩生命值每一颗红心代表一条命。
**(b) 无尽模式**
无尽模式的游戏界面如下图所示。屏幕上方显示了当前模式无尽以及当前所剩余时间每一个计时器代表1s。屏幕左右两侧显示的也是玩家生命值。
## 三、整体结构
整个项目的框架如下所示,部分细节点由于空间原因没有全部画出
![image-20210114205435987](pic/image-20210114205435987.png)
#### 无尽模式的游戏逻辑:
![infinity](pic/infinity.png)
#### 经典模式的游戏逻辑
![classic](pic/classic.png)
## 四、模块介绍
#### 1. VGA
VGA作为一种标准的显示接口得到了广泛的应用其信号类型为模拟类型显示卡端的接口为 15 针母插座。
常见的彩色显示器一般都是由CRT阴极射线管阴极射线管构成其引出线共含 5 个信号R、G、B三基色信号三基色信号、HS行同步信号行同步信号、VS场同步信号场同步信号。每一个像素的色彩由R红红 .G绿绿. B蓝蓝三基色构成。显示屏一般通过光栅扫描的方式扫描一幅屏幕图像上的各个点形成整个图片。
另外VGA 时序控制模块是本设计的重要部分最终的输出信号行、场同步信号必须严格按照VGA 时序标准产生相应的脉冲信号如下图所示以640*480@60HZ为例
![vga_pic](pic/vga_pic.png)
具体细节这里不再赘述。
### 2. PS/2
作为一款对战类游戏良好的键盘输入体验肯定是必不可少的。我们采用的A7提供了基于usb接口、PS/2通信协议。PS/2 通信协议是一种双向同步串行通信协议。通信的两端通过 CLOCK时钟脚时钟脚同步并通过DATA数据脚数据脚交换数据。
如下是PS/2和主机间的通信时许图。数据在该时钟的下降沿进行读取对于 PS/2 设备一般来说从时钟脉冲的上升沿到一个数据转变的时间至少要有5us数据变化到下降沿的时间至少要有 5us并且不大于 25us。
![ps2](pic/ps2.png)
在查阅资料后我们得知我们得知该协议需要6个引脚。
![image-20210113194117915](pic/image-20210113194117915.png)
但实际上PS/2协议只需要4个针脚也就是GND、VCC、PS2_CLK和PS2_DATA而该协议的时序图如下:
![image-20210114190153221](pic/image-20210114190153221.png)
FPGA或主机接收键盘发回的数据通过键盘的编码规则判定键盘当前的操作扫描码有两种不同的类型通码(make code)和断码(break code)。当一个键被按下或持续按住时,键盘会将该键的通码发送给主机;而当一个键被释放时,键盘会将该键的断码发送给主机。值得注意的是,最后一位校验位有的芯片使用的是奇校验,而有的芯片使用的是偶校验,需要我们根据不同的板子进行调整。
根据这个原理我们很快写出来了一个简单的键盘接收驱动在烧到板子内进行验证时却怎么页也读不出来键盘输入完全无效USB-HID旁边的busy灯长亮。
在查阅大量资料后我恍然大悟我使用的键盘为杜伽K320在我的电脑中被识别为了多个键盘输入如下图所示
![image-20210114190141227](pic/image-20210114190141227.png)
除了PS/2外还存在着其他虚拟出的键盘输入因此无法传输数据。
为此我们在淘宝上购买了一块原生支持PS/2协议的键盘之后使用该键盘可以很流畅的读出我们的键盘输入调试工作终于有救了。
### 3. ROM IP核模块
坦克大战中的游戏界面贴图均为静态图片通过选择将其放置在合适的位置上。为了让玩家获得更好的游戏体验我们从网上搜集了大量的png格式的图片通过软件首先转化为bmp再通过自己写的python脚本转化为coe文件。利用这些图片我们创建了游戏开始界面、敌我坦克图片、子弹图片、道具图片以及游戏结束图片一共有29张之多。
具体使用的ip核如下所示
<img src="pic/ip_pic.png" alt="ip_pic" style="zoom:75%;" />
其中只有background图片使用了最近邻插值法通过采用320*240的图片可以减少1/4的BRAM空间占用而敌我坦克、子弹等图片使用了PS进行处理坦克、子弹等图片的边缘均为白色。因此当读到12'b1111_1111_1111时将其转化为background的图像信息即可以此实现图片的有效覆盖。
#### 问题分析
#### (1) 资源问题
我们采用的是Nexys4 DDR-A7开发板其具体参数如下图所示
![image-20210113195559225](pic/image-20210113195559225.png)
在使用vivado查看资源使用率后我们发现该板子一共有135个fast block RAM但是一张640*48012bits的图片居然能使用105个block RAM也就是说我们差不多连一张背景图都放不下因此每个图片就只需要26.5个BRAM。
而在实际中我们则将该图片再转变为640*480的图片。假如图中的5个有坐标的点为320x240网格上的点图中除了黄色点以为的其他点都是进行线性变换后640x480网格上的像素点现在我们可以看到P(x,y)附近的四个点都没有正好落在格点上我们先不管其他点而他们四个距离黄色点最近那么我在显示的时候就把这四个点的颜色都显示成P(x,y)的颜色。
这是一种很简单很基础的插值但是对于我们这种小图片还是很管用的大大减少了Block Memory资源的浪费。
#### (2) 边缘白框处理
在图片显示出来后还是存在很多问题最典型的问题就是图像边框白色和游戏背景图的黑色对比过于强烈已经极大的影响了游戏体验我的解决方案也比较简单就是选择在处理图片的时候将方块周围的不需要部分全部转变为0xffffff在转换为coe文件后只要读取到这种颜色编码oxfff就选择不显示ROM中的数据而是显示游戏背景这样就避免了刚刚提到的问题。而实现起来也比较简单如下图所示
~~~verilog
.in2( ( player1_tank_data == 12'hfff ) ? 12'h000 : player1_tank_data ),
.in3( ( player1_bullet_data == 12'hfff ) ? 12'h000 : player1_bullet_data ),
.in4( ( player2_tank_data == 12'hfff ) ? 12'h000 : player2_tank_data ),
.in5( ( player2_bullet_data == 12'hfff ) ? 12'h000 : player2_bullet_data ),
~~~
不过另一个问题是因为初始的bmp图片是24位的在转换为12位coe的时候就会使得很多颜色相近的颜色编码暴力变为同一种颜色编码这个我最初在图片中埋藏的信息就不知道被转换成什么了。这个问题目前还没有得到很好的解决总体而言并不是很影响整体游戏体验。
#### (3) 同步显示问题
我发现我显示的图片整体向右偏移了一个细条我经过反复查阅资料和参考优秀实验报告得出这是因为我VGA模块输出的xy坐标在always语句中不会立刻计算这个xy对应的ROM地址值他会等到下一个时钟周期到来的时候才会更新ROM地址更新后ROM的data也不会立刻更新而是等到再过一个时钟周期而VGA模块接受到Data后也是要再过一个时钟周期才能更新这就是为什么我的模块无法预期进行。
解决方案实际上是这样的首先把根据x、y计算地址值的always模块改成always@()这样他就会退化成时序逻辑就不存在非阻塞赋值引发的延时问题了但ROM模块必须是时序的不过也有办法解决。我最开始给ROM的时钟是25MHz与VGA同步这样就一定会造成延迟但我可以把ROM的时钟加速到VGA时钟的整数倍如100MHz就可以在xy改变后、下一个vga时钟前拿到合适的输出。
#### (4) COE文件与BMP文件互相转换
为了更好、更方便的生成coe文件我们通过python将bmp文件转化为需要指定宽度、高度的coe格式文件同时我们也使用c语言将coe文件转化为bmp格式文件**上述代码我们在附录中提供**,实验结果证明我们的代码效果非常好,达到我们预期的要求!
### 4. 随机数生成模块
在该游戏中,有两个部分需要用到随机数产生,一个是道具初始位置的选择,我们需要随机产生[0,619]和[0,459]处的随机数同时敌方坦克控制上也要用到00-11的随机数控制方向因此需要一个真题产生随机数的模块。
在Verilog中实际上是预制了随机数生成的方法也就是使用$random 但是这个方法无法正常通过综合百度后我发现该随机数实际上只能在testbench中进行使用。这就比较麻烦我因此搜索了一些通过硬件实现随机数的方法其中最常用的是LFSR法。
![lfsr](pic/lfsr.png)
通过奇怪的选择实现在[0,2^n-1]之间周期很长的周期序列,但在使用中一定要注意很重要的问题:时钟选择一定要合适!时钟不能过快,否则只能得到相同的随机数。
整体代码请参见附录我们使用了15位的随机数产生经过测试已经能很好的满足我们的需要。而针对xy的位置只需mod max_num即可而敌方1,2,3,4号坦克控制只需要取随机数的[7:6],[5:4],[3:2],[1:0]位即可。
~~~verilog
rand_num[ 0 ] <= rand_num[ 14 ];
rand_num[ 1 ] <= rand_num[ 0 ];
rand_num[ 2 ] <= rand_num[ 1 ];
rand_num[ 3 ] <= rand_num[ 2 ];
rand_num[ 4 ] <= rand_num[ 3 ] ^ rand_num[ 14 ];
rand_num[ 5 ] <= rand_num[ 4 ] ^ rand_num[ 14 ];
rand_num[ 6 ] <= rand_num[ 5 ] ^ rand_num[ 14 ];
rand_num[ 7 ] <= rand_num[ 6 ];
rand_num[ 9 ] <= rand_num[ 7 ];
rand_num[ 8 ] <= rand_num[ 8 ] ^ rand_num[ 14 ];
rand_num[ 10 ] <= rand_num[ 9 ];
rand_num[ 11 ] <= rand_num[ 10 ];
rand_num[ 12 ] <= rand_num[ 11 ] ^ rand_num[ 14 ];
rand_num[ 13 ] <= rand_num[ 12 ];
rand_num[ 14 ] <= rand_num[ 13 ];
~~~
### 5. 游戏整体界面
整体而言我们的游戏界面分为三部分:
1. 游戏开始界面
![img](pic/2B5415CA01BF08735B530F066AFCA2DA.jpg)
2. 游戏进行中界面
![img](pic/B50C4FA408CC7C83005330BE642C4F73.jpg)
3. 游戏结束界面
![img](pic/C253B1E5BAB779624248D60D55C37741-1610548821772.jpg)
整体状态由顶层的game_mode模块控制,我们提供了经典模式和无尽模式也在此进行选择,因此我们一共需要四个状态:
1. 初始开始界面状态
2. 经典模式状态
3. 无尽模式状态
4. 游戏介绍状态
而几种模式的选择和切换也在game_mode模块中进行,通过BTN按钮进行切换,而输出模块也根据当前的mode状态来输出其中的enable和gameover信号可以自行查阅附录中的代码。
### 6. 坦克控制
一个完整的坦克控制需要三个模块协同:
1. 坦克使能信号控制game_mode
2. 坦克移动控制 tank_move
3. 坦克显示 (tank_display)
在使能信号控制中我们根据当前的mode判断是否让坦克移动通过产生enable信号影响下面两个模块。
在坦克移动控制中,我们采用有限状态机设计思路,如下图所示
~~~verilog
always @( * ) begin: signals
counter_en = 1'b0;
init = 1'b0;
tank_dir_feedback[ 2 ] = 1;
case ( current_state )
INITIAL: begin
init = 1;
end
UP: begin
counter_en = 1;
tank_dir_feedback = 3'b000;
end
DOWN: begin
counter_en = 1;
tank_dir_feedback = 3'b001;
end
LEFT: begin
counter_en = 1;
tank_dir_feedback = 3'b010;
end
RIGHT: begin
counter_en = 1;
tank_dir_feedback = 3'b011;
end
endcase
end
~~~
通过控制坦克的当前状态计算坦克的坐标,另一方面计算坦克的次态将其付给先态,这样写起来也更加方便具体。
值得注意的是我们根据当前是否有item_faster即加速道具来控制坦克的移动速度如下所示
~~~verilog
wire [ 31: 0 ] counter_num = item_faster ? 1_500_000 : 2_100_000;
~~~
之后我们进行计数,如同所示:
~~~verilog
always @( posedge clk ) begin
if ( !reset_n ) begin
counter <= 0;
counter_move_en <= 0;
end
else if ( counter == counter_num ) begin
counter <= 0;
counter_move_en <= 1;
end
else if ( counter_en == 0 ) begin
counter <= 0;
counter_move_en <= 0;
end
else begin
counter <= counter + 1;
counter_move_en <= 0;
end
end
~~~
当计数器达到设定的counter_num时候我们就将坦克移动一个像素这样就达到了控制坦克移动速度的目的。
而在坦克展示模块中似乎没有那么多值得注意的点一个简单的位置判断和状态判断即可但要看到我们由于无敌星的展示是在坦克原有的背景上额外添加一层黄色五角星因此同时需要item_invincible信号进行输出判断。
一方面坦克四个方向如果使用一张图片配合纯计算实现难度较高因此我们采用四张图片展示坦克的四种方向另一方面敌方坦克和我方坦克的图片并不相同因此总的来说我们一共需要2*4=8张图片进行展示最后使用数据选择器根据坦克的方向、敌我坦克的区分来进行选择最后配上是否有无敌星的数据进行或运算即可以得到最终的坦克展示数据写出来也非常的难看。
~~~verilog
assign tankData = ( ( tank_en & ~tank_destroyed ) ? ( player_enermy ? outData_enermy : outData ) : 0 ) | ( ( ( tank_revive || item_invincible ) && tank_en ) ? outData_star : 0 );
~~~
实际上这样并不算好因为一个坦克我们使用了8个图片的资源但是由于图片较小32*32我也没有对此进行足够的改进。实际上应该将敌我坦克进行区分分别使用不同的模块展示这样可以只使用4个图片六个图片总的来说可以节省大约5个BRAM。
### 7. 敌方坦克移动控制
实际上这部分是我们整体设计中最有难度的部分,也是我们调试时间最长的部分,前前后后一共花了一整天的时间在这个模块上,最终实现的效果也只能说一般。
很容易我们首先想到的是让坦克完全随机游走即每一个时钟周期内通过产生的随机数来得到该时钟周期内坦克行走的方向而开火键直接全部设置为1这样虽然方便写但是最后实现出来的效果非常非常不理想敌方坦克就像跳舞一样不知道在干嘛这让人也很苦恼。
我们最初的目的是做一个躲子弹飞快、碰见人就发射的高级AI但是由于都是第一次使用verilog对其中的组合逻辑控制还不够了解也没法使用类似c语言中的dijkstra、A-star等著名的寻路算法但是我们采用了一种比较简单的方法基本也能保证游戏性
1. 判断自己当前位置是否在玩家1、玩家2的子弹射线上
- True根据当前位置选择正确的逃跑路线并持续至少3个时钟周期。路线选择的方法如下只考虑和子弹垂直方向的两个方向如子弹在上下方向那么坦克必定左右游走并持续至少三个时钟如果坦克过于靠近一侧边界那么就向另一个边界走如果在中间则那边更容易不被子弹射中往那边走。
- False根据随机数模块选择是从横/竖方向接近某一玩家的坦克
2. 判断自己是否和玩家1、玩家2的坦克在同一水平线或同一竖直线中
- True调整方向射击
- False根据随机数模块选择是从横/竖方向接近某一玩家的坦克
3. 根据随机数模块选择是从横/竖方向接近某一玩家的坦克,但每隔三个周期随机向某个方向游走一个周期。
4. 每个10个时钟周期随机选择目前追逐的玩家1/2并追逐10个周期。
这样虽然没有达到我们最初的目的但也很好的满足了游戏性和随机性的需求因为有至多1/3的周期在随机方向上进行而其他时间都在做有效移动。
但是这样会导致一个问题:坦克被卡住!
因为整体的碰撞检测和敌方坦克逻辑分属两个模块,在不重构代码的基础上我们很难检测到坦克被卡住了,也有可能出现四个敌方坦克在某个角度刚好被互相卡住的情况,但一般来说出现该情况的概论较小,出现该情况也不是特别影响游戏体验,因此在时间仓促的情况下我没有进行很好的优化,在展望部分我们详细对此进行了分析。
### 8. 子弹控制
在子弹控制中,我们只采用了一个大模块进行控制,并没有采取子弹展示和移动逻辑分开的情况。整体实现过程也是使用了一个和坦克控制类似的状态机,如图所示:
~~~verilog
always @( * ) begin: state_table
case ( current_state )
WAIT:
next_state = start ? READY : WAIT;
READY: begin
if ( !tank_fire ) begin
next_state = READY;
end
else begin
case ( tank_dir )
2'b00:
next_state = UP;
2'b01:
next_state = DOWN;
2'b10:
next_state = LEFT;
2'b11:
next_state = RIGHT;
endcase
end
end
UP:
next_state = ready ? READY : UP;
DOWN:
next_state = ready ? READY : DOWN;
LEFT:
next_state = ready ? READY : LEFT;
RIGHT:
next_state = ready ? READY : RIGHT;
default:
next_state = READY;
endcase
end
~~~
炮弹的速度控制这里不再细表,值得注意的是坦克子弹也需要四张图片来选择,最后使用数据选择器进行选择当前方向的图片。
还有一点需要额外说明,针对每个数据的位置,我们记录的都是其左上角点的坐标,无论是任何方向坦克还是炮弹都是如此,因此需要进行的额外计算也比较多,但只要记住每个坐标都是左上角,很多问题都可以慢慢解决。
### 9. 碰撞检测与信号控制
这个模块我也踩了很多的坑首先我在多个always里对一个变量进行赋值但是代码始终过不去综合最后查阅资料才发现原理只能在一个always里综合就很自闭最后信心满满的写完了结果出现了critical warning一直在说我有timing loop又是找了很久后才发现是混合使用了组合逻辑和时序逻辑结果一个的输出影响了另一个的输入就很让人自闭。
最后我们采用了细分模块object_collide_detection设计传入两个物体的当前坐标、当前位置和当前状态从而给出该两个物体是否产生碰撞但是有一个很重要的问题该游戏涉及的物体非常的多6个坦克与6个子弹如果都要进行该检测结果将会非常的大。最后我们选择使用了约40个object_collide_detection来对需要的物体坦克对坦克、坦克对子弹进行检测最后使用时序电路对结果进行分析处理即时给出合理的信号如图是一个object_collide_detection使用样例
~~~verilog
object_collide_detection tank1_enermy1(
player1_tank_H, player1_tank_V, player1_tank_en_feedback, player1_tank_dir,
TANK_HEIGHT, TANK_WIDTH,
enermy1_tank_H, enermy1_tank_V, enermy1_tank_en_feedback, enermy1_tank_dir,
TANK_HEIGHT, TANK_WIDTH,
player1_tank_collide[ 0 ], player1_tank_tmp[ 0 ]
);
~~~
player信号控制例子
~~~verilog
always @( posedge clk ) begin: player2_tank_enable_signal
if ( player2_revive || item_invincible ) begin
player2_tank_en_feedback <= 1;
end
else if ( !reset_n ) begin
player2_tank_en_feedback <= 1;
end
else if ( enermy1_bullet_collide[ 1 ] || enermy2_bullet_collide[ 1 ] || enermy3_bullet_collide[ 1 ] || enermy4_bullet_collide[ 1 ] ) begin
player2_tank_en_feedback <= 0;
end
// player2_tank_en_feedback <= ~player1_bullet_collide[1];
player2_tank_move_en <= ~( | player2_tank_collide );
end
always @( posedge clk ) begin: player1_tank_enable_signal
if ( player1_revive || item_invincible ) begin
player1_tank_en_feedback <= 1;
end
else if ( !reset_n ) begin
player1_tank_en_feedback <= 1;
end
else if ( enermy1_bullet_collide[ 0 ] || enermy2_bullet_collide[ 0 ] || enermy3_bullet_collide[ 0 ] || enermy4_bullet_collide[ 0 ] ) begin
player1_tank_en_feedback <= 0;
end
player1_tank_move_en <= ~( | player1_tank_collide );
end
~~~
最后我们检测各个模块的高电位即可进行判断赋值。
结果表明我们设计的object_collide_detection非常的合理跑起来效果出乎意料的好可以很好的检测到坦克间的碰撞。
### 10. 物品生成控制
实际上这部分代码也比较好理解,物品生成主要有三部分组成:
1. item_logic控制物品生成逻辑
2. item_display控制物品展示
3. item_random_generate控制物品随机位置
item_logic通过控制产生的信号作用到其他两个模块每隔一段时间如果本物品还没有坦克碰到则控制下一个物品的准备生成而如果在物品展示时间内有坦克碰到则将该item的输出置为1代表在该物品的生效时间内一段时间后将其置为0代表该物品失效。
而item_random和item_display比较简单这里不再赘述。
这里需要提及一下各个物品的生效方法:
1. 冰冻如果该信号存在坦克的状态时钟是ready无法移动
2. 生命值:收到该信号的上升沿后在控制模块中增加对应玩家的生命值
3. 时间:收到该信号的上升沿后在控制模块中增加总的游戏剩余
4. 无敌收到伤害后如果有该状态HP<=HP
5. 加速器将时钟的counter_num调小变相加快运动速度
### 11. DispNumber模块
在数逻实验中我们曾经自己通过ISE画图的方式设计过并行数码管显示模块但是我觉得我当时画图得到的模块不是很好用并不能满足我们的需求。针对两块板子(A7/K7)我们分别设计了串行和并行的显示模块因为A7的8个七段数码管只支持并行输入而K7的七段数码管只支持串行输入这就比较伤脑筋。
因此我改进了一下实验课中实现过的Disp_num使其可以有32位的输入整体实现过程并不算复杂但是还是花费了一点心思代码在附录中。
![seg_a7](pic/seg_a7.jpg)
A7的并行七段数码管
而串行输入就更具有挑战性了我们实际上采用了65位的移位寄存器进行左移操作同时在寄存器的同步输入端补0当检测到基本上全部充满0时可以证明我们这组数据已经完全被移入七段数码管中此时我们终止clk的输入在一定时间后我们再重复上述的操作因此就可以得到串行的输入。LED灯同理采用17位移位寄存器即可整体代码在附录中。
![seg_k7](pic/seg_k7.jpg)
K7的串行七段数码管
## 五、资源分配和RTL
1. ### 资源分配和引脚图
此次课程设计使用的板子是Artix-7我使用的资源也非常的多包括并行七段数码管、LED灯、VGA、USB-HID等等综合后资源使用图如下
![image-20210114194154906](pic/image-20210114194154906.png)
引脚分配图如下
![image-20210114194259553](pic/image-20210114194259553.png)
逻辑图如下
![image-20210114202833948](pic/image-20210114202833948.png)
从上面这几张图我们可以看到这次课程作业使用的板上资源是非常非常多的BRAM的使用率达到了95%一共使用197cells总计4406nets连输出输入口都有58个实际上在期中报告中我本来规划做出更多的功能但未能完全实现主要还是受到了板子资源的制约和时间制约。
## 六、物理验证
本次课程设计基本上全部在Nexys4 Artix-7进行后期移植到SWORD2.0上我们通过在Nexys4和SWORD2.0上开展,顺便也验证工程的可移植性。
以下综合、实现、生成比特流均在下述配置下进行使用开发工具为Vivado 2019.2
**CPU Name: Intel CoreTM i5-9300H CPU @ 2.40GHz**
| Property | Value |
| :------------------ | :---------------------- |
| Base Frequency | 2.4 GHz |
| Max Turbo Frequency | 4.10 GHz |
| Cache | 4 MB Intel® Smart Cache |
| Cores Number | 4 |
| Threads | 8 |
| TDP | 45W |
**RAM: 16GB DDR4 2666MHz with Two channel memory**
**SSD: SAMSUNG MZVLB1T0HBLR-000L2**
### 一、Nexys4物理验证
开始界面
![img](pic/532271460A0702A8E095097E2E562CE1.jpg)
七段数码管显示右半部分为初始生命值,左半部分为初始时间
![image-20210114201846276](pic/image-20210114201846276.png)
进入游戏(经典模式)
![img](pic/DEF66C6936F6A86DCBB5CB35318C150F.jpg)
![img](pic/22954A272C5153FA72DBB5A97BF2CBF5.jpg)
![img](pic/5D25CF3D639AFEF57BAFE94E94FDF727.jpg)
此时我们看下七段数码管和LED灯的输出
![img](pic/92B3FE920089039B22A286ACF464DE3F.jpg)
LED灯正确表示了剩余生命值而第三位和第七位的七段数码管的得分也显示正常
重新进入游戏(无尽模式)
![img](pic/DE0F7B4D4A9A38B42DC0BAF6723C5C1F.jpg)
![img](pic/C7C15025DB58E4F151CD6B65FCADDFAC.jpg)
七段数码管最右边一位表示剩余时间LED灯同时也表示剩余时间
![img](pic/E8282707C22A608BC2C372CADB52E344.jpg)
可以看到显示正确
游戏结束界面:
![img](pic/3E76777AE83C2FA9843676868AA19B0B.jpg)
可以看到显示数据也是完全正确的。
至此,我们简单的物理验证成功,满足了最初的设计需求。
### 二、SWORD2.0物理验证
开始界面
![img](pic/0842B11D646670FA36C3C9561F2CDED4.jpg)
经典模式
![img](pic/9FD7850BEE9D35BCD8F1A918592778B5.jpg)
![img](pic/88B4F91A52686042330224E756EDE976.jpg)
可以看到七段数码管和LED灯均正常显示
游戏结束画面:
![img](pic/155144434FFB37603AC66A15B922C198.jpg)
无尽模式:
![img](pic/72E03706BFB99ED7B96A2718E637C5E5.jpg)
![img](pic/ED2E9089766F3ED06DDEED0929A1DB9D.jpg)
可以看到LED灯表示剩余时间正确七段数码管显示正确同时上侧的时间展示正确。
游戏结束画面:
![img](pic/C2A8FAE8B279770017CCF60FCDB2C56E.jpg)
## 七、改进思路
由于时间有限,这个游戏的水准并不是特别理想。我们也想到了很多改进的方案。
**1. AI坦克的移动**
我们目前的随机移动策略还是很有效的至少能让AI坦克进行较为有效的移动并开火。但是这还是显得有些笨拙我们可以考虑换一种更加有效的移动和开火策略以增加游戏难度。比如说移动决策不仅要根据当前敌方位置和是否开火还应该考虑到其余三个队友的位置。同时我们可以让坦克进行一些预判误导比如说先向下走很长一段路然后突然掉头这样子会显著增加难度。
**2. 添加障碍物**
经典坦克大战的地图中都是有很多障碍物的,比如草,河流,砖块,石头等。它们有着不同的特性和功能,能显著增加游戏的趣味性。我们可以在将来的改进中加入这一特性。
![image-20210113220701419](pic/image-20210113220701419.png)
**3. 改进图像插值算法**
为了节省空间我们将背景图缩小为320*240等到使用的时候再使用最近邻插值变换回来。但我们希望在不影响空间占用的情况下进一步提升画质那么我们就想到了双线性插值。
设目标点$P$和其他四个点$Q_{11},Q_{12},Q_{21},Q_{22}$的颜色为$f(x,y),f(x_1,y_1),f(x_1,y_2),f(x_2,y_1),f(x_2,y_2)$
对目标点在$x$轴的两个投影点进行线性Lagrange插值
$$
\displaystyle {
\begin{aligned}
f(x,y_{1}) \approx {\frac {x_{2}-x}{x_{2}-x_{1}}}f(Q_{11})+{\frac {x-x_{1}}{x_{2}-x_{1}}}f(Q_{21}),\
f(x,y_{2}) \approx {\frac {x_{2}-x}{x_{2}-x_{1}}}f(Q_{12})+{\frac {x-x_{1}}{x_{2}-x_{1}}}f(Q_{22}).
\end{aligned}}
$$
然后再对这两个点使用线性Lagrange插值得出
$$
{\displaystyle {\begin{aligned}f(x,y) \approx {\frac {y_{2}-y}{y_{2}-y_{1}}}\left({\frac {x_{2}-x}{x_{2}-x_{1}}}f(Q_{11})+{\frac {x-x_{1}}{x_{2}-x_{1}}}f(Q_{21})\right)+{\frac {y-y_{1}}{y_{2}-y_{1}}}\left({\frac {x_{2}-x}{x_{2}-x_{1}}}f(Q_{12})+{\frac {x-x_{1}}{x_{2}-x_{1}}}f(Q_{22})\right)\end{aligned}}}
$$
事实上我们在网上已经看到了相关代码和ip核比如说有一个实现方式是这样的。但是由于实现难度较大也考虑到如此低分辨率下该算法对画质提升有限我们便暂时放弃了此算法。
![img](pic/20151223204326956)
**4. 优化坦克碰撞逻辑**
我们当前只是机械的比较坦克图片的外轮廓,这并不是很精确。我们可以考虑取更多特征点进行碰撞检测,让其与视觉效果相匹配。我们可以忽略掉坦克图片四周的白边框,同时加上伸出来的炮管的碰撞检测,比如说,将炮管头也作为一个特征点,将坦克的碰撞体积改为多边形,这样可以让碰撞和实际观测相符合。
##### 5. 添加音效
一个真正的坦克大战游戏怎么能少了音效。实际上这个功能的实现应当不会很复杂。虽然Nexys4-DDR板子上并没有蜂鸣器但是我们可以利用其提供的3.5mm接口进行音频输出。Digilent上有着音频输出的相关教程。
但是这样的输出功率很可能不够那我们就可以使用板子上的pmod扩展一个音频输出。在Digilent官网上我们很容易就能搜索到这类组件并且找到很多相关教程。
![img](pic/1YUAFsi2lQ4bcMW.png)
上述改进思路我们都有进行一定思考,但是苦于板上资源限制和时间限制,并没能得到实现。
## 八、总结
数逻的课程设计是我们大学这段时间碰到的最困难的课程设计其困难的主要原因在于我们对Verilog语法和对开发环境的不熟悉。硬件开发与以往使用高级语言进行软件开发的感觉完全不同两者有着截然不同的逻辑电路的同步性也让我们需要认真思考代码的实现方式比如说在Verilog中我们难以使用if语句进行条件块的跳转而是需要在模块中生成相关信号基于此进行分支在移动逻辑中使用状态机往往能够使代码更加清晰易读。除此之外EDA工具生成过于缓慢也占用了我们大量的时间。这个作业从头到尾花了我们接近三周的时间由于临近期末ddl压力大我们做的也就比较紧张。如果有更加充裕的时间来做这个作业我们会做的更好。
## 九、成员分工
+ raynor道具逻辑模块游戏界面设计游戏逻辑模块PS2驱动数码管显示模块
+ sz坦克逻辑模块子弹逻辑模块碰撞检测逻辑VGA驱动
+ 坦克在被子弹射中的时候不会消失
+ 子弹的图案显示有问题mem读取的问题
+ 复活计时器

View File

@ -66,6 +66,9 @@ set_property PACKAGE_PIN J15 [get_ports {SW[0]}]
set_property PACKAGE_PIN L16 [get_ports {SW[1]}] set_property PACKAGE_PIN L16 [get_ports {SW[1]}]
set_property PACKAGE_PIN M13 [get_ports {SW[2]}] set_property PACKAGE_PIN M13 [get_ports {SW[2]}]
set_property PACKAGE_PIN R15 [get_ports {SW[3]}] set_property PACKAGE_PIN R15 [get_ports {SW[3]}]
set_property CLOCK_DEDICATED_ROUTE FALSE [get_nets {SW[3]}]
set_property CLOCK_DEDICATED_ROUTE FALSE [get_nets {SW[2]}]
set_property IOSTANDARD LVCMOS33 [get_ports {SW[3]}] set_property IOSTANDARD LVCMOS33 [get_ports {SW[3]}]
set_property IOSTANDARD LVCMOS33 [get_ports {SW[2]}] set_property IOSTANDARD LVCMOS33 [get_ports {SW[2]}]
set_property IOSTANDARD LVCMOS33 [get_ports {SW[1]}] set_property IOSTANDARD LVCMOS33 [get_ports {SW[1]}]

View File

@ -0,0 +1,39 @@
`timescale 1ns / 1ps
//////////////////////////////////////////////////////////////////////////////////
// Company:
// Engineer:
//
// Create Date: 20:12:55 10/27/2020
// Design Name:
// Module Name: CreateNumber
// Project Name:
// Target Devices:
// Tool versions:
// Description:
//
// Dependencies:
//
// Revision:
// Revision 0.01 - File Created
// Additional Comments:
//
//////////////////////////////////////////////////////////////////////////////////
module CreateNumber(
input wire [ 2: 0 ] btn,
output reg [ 7: 0 ] num
);
wire [ 3: 0 ] heart_add, heart_minus, time_add, time_minus;
initial
num <= 8'b1111_0100;
assign heart_add = num[ 3: 0 ] == 6 ? num[ 3 : 0 ] : num[ 3 : 0 ] + 1'b1;
assign heart_minus = num[ 3: 0 ] == 0 ? num[ 3 : 0 ] : num[ 3 : 0 ] - 1'b1;
assign time_add = num[ 7: 4 ] + 1'b1;
assign time_minus = num[ 7: 4 ] - 1'b1;
always @( posedge btn[ 1 ] )
num[ 3: 0 ] <= btn[ 0 ] == 1'b1 ? heart_add : heart_minus;
always @( posedge btn[ 2 ] )
num[ 7: 4 ] <= btn[ 0 ] == 1'b1 ? time_add : time_minus;
endmodule

View File

@ -103,6 +103,7 @@ wire enermy1_revive, enermy2_revive, enermy3_revive, enermy4_revive;
wire player1_scored, player2_scored; wire player1_scored, player2_scored;
wire [ 1: 0 ] winner; wire [ 1: 0 ] winner;
wire timeup; wire timeup;
wire [ 7: 0 ] initial_num;
assign reset_n = ~BTNC; assign reset_n = ~BTNC;
clock MyClock( clock MyClock(
@ -120,7 +121,10 @@ KeyBoard_PS2 My_Ps2(
.out( KeyBoard_Output ) .out( KeyBoard_Output )
); );
clk_wiz_0 clk_vga( .clk_in1( clk ), .reset( 1'b0 ), .clk_25m( clk_VGA ) , .locked() ); clk_wiz_0 clk_vga( .clk_in1( clk ), .reset( 1'b0 ), .clk_25m( clk_VGA ) , .locked() );
CreateNumber u_CreateNumber(
.btn( SW[ 3: 1 ] ),
.num( initial_num )
);
game_mode u_game_mode( game_mode u_game_mode(
.clk( clk ), .clk( clk ),
@ -161,6 +165,7 @@ game_logic_classic u_game_logic_classic(
.btn_return( BTNU ), .btn_return( BTNU ),
.btn_stop( BTND ), .btn_stop( BTND ),
.enable_game_classic( enable_game_classic ), .enable_game_classic( enable_game_classic ),
.initial_num( initial_num[ 3: 0 ] ),
.mytank1_state( player1_tank_en ), .mytank1_state( player1_tank_en ),
.mytank2_state( player2_tank_en ), .mytank2_state( player2_tank_en ),
.scorea1( scorea1 ), .scorea1( scorea1 ),
@ -186,6 +191,7 @@ game_logic_infinity u_game_logic_infinity(
.btn_return( BTNU ), .btn_return( BTNU ),
.btn_stop( BTND ), .btn_stop( BTND ),
.enable_game_infinity( enable_game_infinity ), .enable_game_infinity( enable_game_infinity ),
.initial_num( initial_num[ 7: 4 ] ),
.mytank1_state( player1_tank_en ), .mytank1_state( player1_tank_en ),
.mytank2_state( player2_tank_en ), .mytank2_state( player2_tank_en ),
.scorea1( scorea1 ), .scorea1( scorea1 ),
@ -236,21 +242,21 @@ vga_data_background u_data_background(
vga_data_selector u_vga_data_selector( vga_data_selector u_vga_data_selector(
.clk( clk ), .clk( clk ),
.in1( backgroundData ), .in1( backgroundData ),
.in2( player1_tank_data ), .in2( ( player1_tank_data == 12'hfff ) ? 12'h000 : player1_tank_data ),
.in3( player1_bullet_data ), .in3( ( player1_bullet_data == 12'hfff ) ? 12'h000 : player1_bullet_data ),
.in4( player2_tank_data ), .in4( ( player2_tank_data == 12'hfff ) ? 12'h000 : player2_tank_data ),
.in5( player2_bullet_data ), .in5( ( player2_bullet_data == 12'hfff ) ? 12'h000 : player2_bullet_data ),
.in6( enermy1_tank_data ), .in6( ( enermy1_tank_data == 12'hfff ) ? 12'h000 : enermy1_tank_data ),
.in7( enermy2_tank_data ), .in7( ( enermy2_tank_data == 12'hfff ) ? 12'h000 : enermy2_tank_data ),
.in8( enermy3_tank_data ), .in8( ( enermy3_tank_data == 12'hfff ) ? 12'h000 : enermy3_tank_data ),
.in9( enermy4_tank_data ), .in9( ( enermy4_tank_data == 12'hfff ) ? 12'h000 : enermy4_tank_data ),
.in10( enermy1_bullet_data ), .in10( ( enermy1_bullet_data == 12'hfff ) ? 12'h000 : enermy1_bullet_data ),
.in11( enermy2_bullet_data ), .in11( ( enermy2_bullet_data == 12'hfff ) ? 12'h000 : enermy2_bullet_data ),
.in12( enermy3_bullet_data ), .in12( ( enermy3_bullet_data == 12'hfff ) ? 12'h000 : enermy3_bullet_data ),
.in13( enermy4_bullet_data ), .in13( ( enermy4_bullet_data == 12'hfff ) ? 12'h000 : enermy4_bullet_data ),
.in14( game_information_data ), .in14( game_information_data ),
.in15( heart_gametips_data ), .in15( heart_gametips_data ),
.in16( item_data ), .in16( ( item_data == 12'hfff ) ? 12'h000 : item_data ),
.in17(), .in17(),
.out( VGAData ) .out( VGAData )
); );
@ -296,6 +302,7 @@ tank_display enermy1_tank_display(
.vgaV( vgaV ), .vgaV( vgaV ),
.tankH( enermy1_tank_H ), .tankH( enermy1_tank_H ),
.tankV( enermy1_tank_V ), .tankV( enermy1_tank_V ),
.item_invincible( 0 ),
.tankData( enermy1_tank_data ) .tankData( enermy1_tank_data )
); );
@ -310,6 +317,7 @@ tank_display enermy2_tank_display(
.vgaV( vgaV ), .vgaV( vgaV ),
.tankH( enermy2_tank_H ), .tankH( enermy2_tank_H ),
.tankV( enermy2_tank_V ), .tankV( enermy2_tank_V ),
.item_invincible( 0 ),
.tankData( enermy2_tank_data ) .tankData( enermy2_tank_data )
); );
tank_display enermy3_tank_display( tank_display enermy3_tank_display(
@ -323,6 +331,7 @@ tank_display enermy3_tank_display(
.vgaV( vgaV ), .vgaV( vgaV ),
.tankH( enermy3_tank_H ), .tankH( enermy3_tank_H ),
.tankV( enermy3_tank_V ), .tankV( enermy3_tank_V ),
.item_invincible( 0 ),
.tankData( enermy3_tank_data ) .tankData( enermy3_tank_data )
); );
tank_display enermy4_tank_display( tank_display enermy4_tank_display(
@ -336,6 +345,7 @@ tank_display enermy4_tank_display(
.vgaV( vgaV ), .vgaV( vgaV ),
.tankH( enermy4_tank_H ), .tankH( enermy4_tank_H ),
.tankV( enermy4_tank_V ), .tankV( enermy4_tank_V ),
.item_invincible( 0 ),
.tankData( enermy4_tank_data ) .tankData( enermy4_tank_data )
); );
@ -536,7 +546,7 @@ bullet_control bullet_player1(
.item_faster( item_faster ), .item_faster( item_faster ),
.vgaV( vgaV ), .vgaV( vgaV ),
.vgaH( vgaH ), .vgaH( vgaH ),
.start( 1 ), .start( enable_game_classic | enable_game_infinity ),
.ready( player1_bullet_en ), .ready( player1_bullet_en ),
.bulletData( player1_bullet_data ), .bulletData( player1_bullet_data ),
.bullet_H_feedback( player1_bullet_H ), .bullet_H_feedback( player1_bullet_H ),
@ -556,7 +566,7 @@ bullet_control bullet_player2(
.item_faster( item_faster ), .item_faster( item_faster ),
.vgaV( vgaV ), .vgaV( vgaV ),
.vgaH( vgaH ), .vgaH( vgaH ),
.start( 1 ), .start( enable_game_classic | enable_game_infinity ),
.ready( player2_bullet_en ), .ready( player2_bullet_en ),
.bulletData( player2_bullet_data ), .bulletData( player2_bullet_data ),
.bullet_H_feedback( player2_bullet_H ), .bullet_H_feedback( player2_bullet_H ),
@ -576,7 +586,7 @@ bullet_control bullet_enermy1(
.item_faster( item_faster ), .item_faster( item_faster ),
.vgaV( vgaV ), .vgaV( vgaV ),
.vgaH( vgaH ), .vgaH( vgaH ),
.start( 1 ), .start( enable_game_classic | enable_game_infinity ),
.ready( enermy1_bullet_en ), .ready( enermy1_bullet_en ),
.bulletData( enermy1_bullet_data ), .bulletData( enermy1_bullet_data ),
.bullet_H_feedback( enermy1_bullet_H ), .bullet_H_feedback( enermy1_bullet_H ),
@ -596,7 +606,7 @@ bullet_control bullet_enermy2(
.item_faster( item_faster ), .item_faster( item_faster ),
.vgaV( vgaV ), .vgaV( vgaV ),
.vgaH( vgaH ), .vgaH( vgaH ),
.start( 1 ), .start( enable_game_classic | enable_game_infinity ),
.ready( enermy2_bullet_en ), .ready( enermy2_bullet_en ),
.bulletData( enermy2_bullet_data ), .bulletData( enermy2_bullet_data ),
.bullet_H_feedback( enermy2_bullet_H ), .bullet_H_feedback( enermy2_bullet_H ),
@ -616,7 +626,7 @@ bullet_control bullet_enermy3(
.item_faster( item_faster ), .item_faster( item_faster ),
.vgaV( vgaV ), .vgaV( vgaV ),
.vgaH( vgaH ), .vgaH( vgaH ),
.start( 1 ), .start( enable_game_classic | enable_game_infinity ),
.ready( enermy3_bullet_en ), .ready( enermy3_bullet_en ),
.bulletData( enermy3_bullet_data ), .bulletData( enermy3_bullet_data ),
.bullet_H_feedback( enermy3_bullet_H ), .bullet_H_feedback( enermy3_bullet_H ),
@ -636,7 +646,7 @@ bullet_control bullet_enermy4(
.item_faster( item_faster ), .item_faster( item_faster ),
.vgaV( vgaV ), .vgaV( vgaV ),
.vgaH( vgaH ), .vgaH( vgaH ),
.start( 1 ), .start( enable_game_classic | enable_game_infinity ),
.ready( enermy4_bullet_en ), .ready( enermy4_bullet_en ),
.bulletData( enermy4_bullet_data ), .bulletData( enermy4_bullet_data ),
.bullet_H_feedback( enermy4_bullet_H ), .bullet_H_feedback( enermy4_bullet_H ),
@ -668,6 +678,8 @@ game_information_display u_game_information_display(
vga_data_heart_gametips u_vga_data_heart_gametips( vga_data_heart_gametips u_vga_data_heart_gametips(
.clk( clk ), .clk( clk ),
.mode( mode ), .mode( mode ),
.initial_num( initial_num ),
.choose_mode( SW[ 0 ] ),
.vgaH( vgaH ), .vgaH( vgaH ),
.vgaV( vgaV ), .vgaV( vgaV ),
.winner( winner ), .winner( winner ),
@ -716,7 +728,7 @@ wire [ 31: 0 ] num ;
// assign num = { 3'b000, player1_tank_collide[7], 3'b000, player1_tank_collide[6], 3'b000, player1_tank_collide[5], 3'b000, player1_tank_collide[4], // assign num = { 3'b000, player1_tank_collide[7], 3'b000, player1_tank_collide[6], 3'b000, player1_tank_collide[5], 3'b000, player1_tank_collide[4],
// player1_bullet_H[7:0], player1_bullet_V[7:0] }; // player1_bullet_H[7:0], player1_bullet_V[7:0] };
assign num = { 3'b000, player1_moving, 3'b000, player1_tank_dir[ 1 ], 3'b000, player1_tank_dir[ 0 ], 4'b0000, 3'b000, player1_moving, 3'b000, player1_tank_en, 3'b000, player2_tank_en, 3'b000, player1_tank_move_en }; assign num = { 3'b0, initial_num[ 7 ], 3'b0, initial_num[ 6 ], 3'b0, initial_num[ 5 ], 3'b0, initial_num[ 4 ], 3'b0, initial_num[ 3 ], 3'b0, initial_num[ 2 ], 3'b0, initial_num[ 1 ], 3'b0, initial_num[ 0 ] };
// Disp_Num my_Disp_Num( // Disp_Num my_Disp_Num(
// .clk( clk ), // .clk( clk ),
// .RST( 1'b0 ), // .RST( 1'b0 ),

View File

@ -78,8 +78,8 @@ assign enermy_moving = enermy_tank_en;
initial begin initial begin
enermy_fire <= 1'b0; enermy_fire <= 1'b0;
counter_num <= flag; counter_num <= 0;
rand <= flag[ 0 ]; rand <= 0;
Continue <= 0; Continue <= 0;
Continue_num <= 0; Continue_num <= 0;
enermy_fire_tmp <= 0; enermy_fire_tmp <= 0;

View File

@ -24,6 +24,7 @@ module game_information_display(
input clk, input clk,
input enable_game_classic, input enable_game_classic,
input enable_game_infinity, input enable_game_infinity,
input [ 7: 0 ] score_classic, input [ 7: 0 ] score_classic,
input [ 4: 0 ] timer, input [ 4: 0 ] timer,
input [ 10: 0 ] vgaH, input [ 10: 0 ] vgaH,

View File

@ -28,6 +28,7 @@ module game_logic_classic(
input enable_game_classic, input enable_game_classic,
input mytank1_state, input mytank1_state,
input mytank2_state, input mytank2_state,
input [ 3: 0 ] initial_num,
input [ 3: 0 ] scorea1, input [ 3: 0 ] scorea1,
input [ 3: 0 ] scorea2, input [ 3: 0 ] scorea2,
input [ 3: 0 ] scoreb1, input [ 3: 0 ] scoreb1,
@ -75,8 +76,8 @@ always @( posedge clk ) begin
HP2_value <= HP2_value; HP2_value <= HP2_value;
end end
if ( enable_game_classic == 1'b0 ) begin if ( enable_game_classic == 1'b0 ) begin
HP1_value <= 4; HP1_value <= initial_num;
HP2_value <= 4; HP2_value <= initial_num;
end end
if ( item_addHP && ~item_addHP_last ) begin if ( item_addHP && ~item_addHP_last ) begin
if ( which_player == 0 ) begin if ( which_player == 0 ) begin

View File

@ -27,6 +27,7 @@ module game_logic_infinity(
input enable_game_infinity, input enable_game_infinity,
input mytank1_state, input mytank1_state,
input mytank2_state, input mytank2_state,
input [ 3: 0 ] initial_num,
input [ 3: 0 ] scorea1, input [ 3: 0 ] scorea1,
input [ 3: 0 ] scorea2, input [ 3: 0 ] scorea2,
input [ 3: 0 ] scoreb1, input [ 3: 0 ] scoreb1,
@ -60,7 +61,7 @@ reg item_addtime_last;
initial begin initial begin
gameover_infinity <= 0; gameover_infinity <= 0;
cnt <= 0; cnt <= 0;
timer <= 16; timer <= 0;
score_infinity <= 0; score_infinity <= 0;
score1 <= 0; score1 <= 0;
score2 <= 0; score2 <= 0;
@ -121,7 +122,7 @@ always @( posedge clk ) begin
if ( !enable_game_infinity ) begin if ( !enable_game_infinity ) begin
gameover_infinity <= 0; gameover_infinity <= 0;
cnt <= 0; cnt <= 0;
timer <= 16; timer <= initial_num;
if ( btn_return ) begin if ( btn_return ) begin
score1 <= 0; score1 <= 0;
score2 <= 0; score2 <= 0;
@ -136,13 +137,13 @@ always @( posedge clk ) begin
else begin else begin
if ( timer == 0 || btn_stop || ( | HP1_value == 0 ) || ( | HP2_value == 0 ) ) begin if ( timer == 0 || btn_stop || ( | HP1_value == 0 ) || ( | HP2_value == 0 ) ) begin
timer <= 16; timer <= initial_num;
gameover_infinity <= 1; gameover_infinity <= 1;
timeup <= 1'b1; timeup <= 1'b1;
end end
else begin else begin
if ( score1 < scorea1 + scoreb1 + scorec1 + scored1 ) begin if ( score1 < scorea1 + scoreb1 + scorec1 + scored1 ) begin
if ( add_flag == 0 && timer > 0 && timer < 16 ) begin if ( add_flag == 0 && timer > 0 && timer < initial_num ) begin
timer <= timer + 3; timer <= timer + 3;
cnt <= 0; cnt <= 0;
add_flag = 1; add_flag = 1;
@ -152,7 +153,7 @@ always @( posedge clk ) begin
add_flag = 0; add_flag = 0;
end end
if ( score2 < scorea2 + scoreb2 + scorec2 + scored2 ) begin if ( score2 < scorea2 + scoreb2 + scorec2 + scored2 ) begin
if ( add_flag == 0 && timer > 0 && timer < 16 ) begin if ( add_flag == 0 && timer > 0 && timer < initial_num ) begin
timer <= timer + 3; timer <= timer + 3;
cnt <= 0; cnt <= 0;
add_flag = 1; add_flag = 1;
@ -166,12 +167,12 @@ always @( posedge clk ) begin
score2 <= scorea2 + scoreb2 + scorec2 + scored2; score2 <= scorea2 + scoreb2 + scorec2 + scored2;
if ( item_addtime == 1 ) begin if ( item_addtime == 1 ) begin
if ( item_flag == 0 && timer > 0 && timer < 16 ) begin if ( item_flag == 0 && timer > 0 && timer < initial_num ) begin
begin begin
if ( timer == 15 ) begin if ( timer == initial_num - 1 ) begin
timer <= timer + 1; timer <= timer + 1;
end end
else if ( timer == 14 ) begin else if ( timer == initial_num - 2 ) begin
timer <= timer + 2; timer <= timer + 2;
end end
else begin else begin
@ -197,7 +198,7 @@ always @( posedge clk ) begin
end end
if ( item_addtime && ~item_addtime_last ) begin if ( item_addtime && ~item_addtime_last ) begin
timer <= timer + 10; timer <= 4'b1111;
end end
item_addtime_last <= item_addtime; item_addtime_last <= item_addtime;
end end

View File

@ -92,7 +92,7 @@ startpic_450_200 u_start_pic(
always@( posedge clk ) begin always@( posedge clk ) begin
if ( !sw_mode_sel ) begin if ( !sw_mode_sel ) begin
if ( vgaH >= 229 && vgaH <= 233 && vgaV >= 233 && vgaV <= 237 ) begin if ( vgaH >= 223 && vgaH <= 233 && vgaV >= 227 && vgaV <= 237 ) begin
VGA_data_cursor <= 12'h0F0; VGA_data_cursor <= 12'h0F0;
end end
else begin else begin
@ -100,7 +100,7 @@ always@( posedge clk ) begin
end end
end end
else begin else begin
if ( vgaH >= 229 && vgaH <= 233 && vgaV >= 252 && vgaV <= 256 ) begin if ( vgaH >= 223 && vgaH <= 233 && vgaV >= 246 && vgaV <= 256 ) begin
VGA_data_cursor <= 12'h0F0; VGA_data_cursor <= 12'h0F0;
end end
else begin else begin

View File

@ -23,6 +23,8 @@
module vga_data_heart_gametips( module vga_data_heart_gametips(
input clk, input clk,
input [ 2: 0 ] mode, input [ 2: 0 ] mode,
input [ 7: 0 ] initial_num,
input choose_mode,
input [ 10: 0 ] vgaH, input [ 10: 0 ] vgaH,
input [ 10: 0 ] vgaV, input [ 10: 0 ] vgaV,
input [ 1: 0 ] winner, input [ 1: 0 ] winner,
@ -42,6 +44,52 @@ wire [ 11: 0 ] heart_pic1 , heart_pic2, classic_gameover_tips_pic1, classic_game
reg [ 8: 0 ] addra_heart_pic1, addra_heart_pic2; reg [ 8: 0 ] addra_heart_pic1, addra_heart_pic2;
reg [ 12: 0 ] addr_classic_gameover_tips_pic1, addr_classic_gameover_tips_pic2, addr_infinity_gameover_tips_pic; reg [ 12: 0 ] addr_classic_gameover_tips_pic1, addr_classic_gameover_tips_pic2, addr_infinity_gameover_tips_pic;
reg [ 8: 0 ] addra_initial_heart;
reg [ 7: 0 ] addra_initial_time;
wire [ 11: 0 ] initial_heart_pic, initial_time_pic;
reg [ 11: 0 ] initial_heart_reg, initial_time_reg;
always @( posedge clk ) begin
if ( mode != 0 ) begin
initial_heart_reg <= 0;
initial_time_reg <= 0;
end
else begin
if ( choose_mode == 0 ) begin
initial_time_reg <= 0;
if ( vgaV >= 320 && vgaV < 340 && vgaH >= 320 && vgaH < 500 && ( vgaH - 320 ) < initial_num[ 3: 0 ] * 20 ) begin
addra_initial_heart <= ( vgaH - 320 ) % 20 + ( vgaV - 320 ) * 20;
initial_heart_reg <= initial_heart_pic;
end
else begin
initial_heart_reg <= 0;
end
end
else begin
initial_heart_reg <= 0;
if ( vgaV >= 320 && vgaV < 335 && vgaH >= 320 && vgaH < 640 && ( vgaH - 320 ) < initial_num[ 7: 4 ] * 15 ) begin
addra_initial_time <= ( vgaH - 320 ) % 15 + ( vgaV - 320 ) * 15;
initial_time_reg <= initial_time_pic;
end
else begin
initial_time_reg <= 0;
end
end
end
end
heart_20_20 u1_heart_20_20(
.clka( clk ),
.addra( addra_initial_heart ),
.ena( 1'b1 ),
.douta( initial_heart_pic )
);
timing_15_15 u2_timing_15_15(
.clka( clk ),
.addra( addra_initial_time ),
.ena( 1'b1 ),
.douta( initial_time_pic )
);
always @( posedge clk ) begin always @( posedge clk ) begin
if ( mode == 0 || mode == 3 ) begin if ( mode == 0 || mode == 3 ) begin
heart_reg1 <= 0; heart_reg1 <= 0;
@ -129,8 +177,6 @@ heart_20_20 player1_heart(
.douta( heart_pic1 ), .douta( heart_pic1 ),
.ena( 1'b1 ) .ena( 1'b1 )
); );
heart_20_20 player2_heart( heart_20_20 player2_heart(
.addra( addra_heart_pic2 ), .addra( addra_heart_pic2 ),
.clka( clk ), .clka( clk ),
@ -158,5 +204,5 @@ timeisup_180_38 u_timeisup_180_38(
.douta( infinity_gameover_tips_pic ), .douta( infinity_gameover_tips_pic ),
.ena( 1'b1 ) .ena( 1'b1 )
); );
assign vgaData = heart_reg1 | heart_reg2 | classic_gameover_tips_reg1 | classic_gameover_tips_reg2 | infinity_gameover_tips_reg; assign vgaData = heart_reg1 | heart_reg2 | classic_gameover_tips_reg1 | classic_gameover_tips_reg2 | infinity_gameover_tips_reg | initial_heart_reg | initial_time_reg;
endmodule endmodule

View File

@ -54,6 +54,15 @@
<FileSets Version="1" Minor="31"> <FileSets Version="1" Minor="31">
<FileSet Name="sources_1" Type="DesignSrcs" RelSrcDir="$PSRCDIR/sources_1"> <FileSet Name="sources_1" Type="DesignSrcs" RelSrcDir="$PSRCDIR/sources_1">
<Filter Type="Srcs"/> <Filter Type="Srcs"/>
<File Path="$PSRCDIR/sources_1/imports/ScoreBoard/CreateNumber.v">
<FileInfo>
<Attr Name="ImportPath" Val="$PPRDIR/../../../ISE/Project/ScoreBoard/CreateNumber.v"/>
<Attr Name="ImportTime" Val="1603800848"/>
<Attr Name="UsedIn" Val="synthesis"/>
<Attr Name="UsedIn" Val="implementation"/>
<Attr Name="UsedIn" Val="simulation"/>
</FileInfo>
</File>
<File Path="$PSRCDIR/sources_1/new/Disp_Num.v"> <File Path="$PSRCDIR/sources_1/new/Disp_Num.v">
<FileInfo> <FileInfo>
<Attr Name="UsedIn" Val="synthesis"/> <Attr Name="UsedIn" Val="synthesis"/>
@ -561,7 +570,7 @@
<File Path="$PSRCDIR/utils_1/imports/impl_1/Top_routed.dcp"> <File Path="$PSRCDIR/utils_1/imports/impl_1/Top_routed.dcp">
<FileInfo> <FileInfo>
<Attr Name="ImportPath" Val="$PRUNDIR/impl_1/Top_routed.dcp"/> <Attr Name="ImportPath" Val="$PRUNDIR/impl_1/Top_routed.dcp"/>
<Attr Name="ImportTime" Val="1609939059"/> <Attr Name="ImportTime" Val="1610626227"/>
<Attr Name="UsedIn" Val="synthesis"/> <Attr Name="UsedIn" Val="synthesis"/>
<Attr Name="UsedIn" Val="implementation"/> <Attr Name="UsedIn" Val="implementation"/>
<Attr Name="UsedInSteps" Val="impl_1"/> <Attr Name="UsedInSteps" Val="impl_1"/>
@ -1271,9 +1280,7 @@
</Run> </Run>
<Run Id="lightning_20_20_synth_1" Type="Ft3:Synth" SrcSet="lightning_20_20" Part="xc7a100tcsg324-1" ConstrsSet="lightning_20_20" Description="Vivado Synthesis Defaults" AutoIncrementalCheckpoint="false" WriteIncrSynthDcp="false" Dir="$PRUNDIR/lightning_20_20_synth_1" IncludeInArchive="true"> <Run Id="lightning_20_20_synth_1" Type="Ft3:Synth" SrcSet="lightning_20_20" Part="xc7a100tcsg324-1" ConstrsSet="lightning_20_20" Description="Vivado Synthesis Defaults" AutoIncrementalCheckpoint="false" WriteIncrSynthDcp="false" Dir="$PRUNDIR/lightning_20_20_synth_1" IncludeInArchive="true">
<Strategy Version="1" Minor="2"> <Strategy Version="1" Minor="2">
<StratHandle Name="Vivado Synthesis Defaults" Flow="Vivado Synthesis 2019"> <StratHandle Name="Vivado Synthesis Defaults" Flow="Vivado Synthesis 2019"/>
<Desc>Vivado Synthesis Defaults</Desc>
</StratHandle>
<Step Id="synth_design"/> <Step Id="synth_design"/>
</Strategy> </Strategy>
<GeneratedRun Dir="$PRUNDIR" File="gen_run.xml"/> <GeneratedRun Dir="$PRUNDIR" File="gen_run.xml"/>
@ -1777,9 +1784,7 @@
</Run> </Run>
<Run Id="lightning_20_20_impl_1" Type="Ft2:EntireDesign" Part="xc7a100tcsg324-1" ConstrsSet="lightning_20_20" Description="Default settings for Implementation." AutoIncrementalCheckpoint="false" WriteIncrSynthDcp="false" SynthRun="lightning_20_20_synth_1" IncludeInArchive="false" GenFullBitstream="true"> <Run Id="lightning_20_20_impl_1" Type="Ft2:EntireDesign" Part="xc7a100tcsg324-1" ConstrsSet="lightning_20_20" Description="Default settings for Implementation." AutoIncrementalCheckpoint="false" WriteIncrSynthDcp="false" SynthRun="lightning_20_20_synth_1" IncludeInArchive="false" GenFullBitstream="true">
<Strategy Version="1" Minor="2"> <Strategy Version="1" Minor="2">
<StratHandle Name="Vivado Implementation Defaults" Flow="Vivado Implementation 2019"> <StratHandle Name="Vivado Implementation Defaults" Flow="Vivado Implementation 2019"/>
<Desc>Default settings for Implementation.</Desc>
</StratHandle>
<Step Id="init_design"/> <Step Id="init_design"/>
<Step Id="opt_design"/> <Step Id="opt_design"/>
<Step Id="power_opt_design"/> <Step Id="power_opt_design"/>

Binary file not shown.

After

Width:  |  Height:  |  Size: 338 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 364 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 464 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 30 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 202 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 235 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 252 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 241 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 231 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 338 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 340 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 290 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 335 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 176 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 244 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 244 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 353 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 333 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 234 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 201 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 273 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 344 KiB

BIN
pic/classic.png 100644

Binary file not shown.

After

Width:  |  Height:  |  Size: 62 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 132 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 128 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 17 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 65 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 198 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 17 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 128 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 28 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 114 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 44 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.3 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 28 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 58 KiB

BIN
pic/infinity.png 100644

Binary file not shown.

After

Width:  |  Height:  |  Size: 43 KiB

BIN
pic/ip_pic.png 100644

Binary file not shown.

After

Width:  |  Height:  |  Size: 76 KiB

BIN
pic/lfsr.png 100644

Binary file not shown.

After

Width:  |  Height:  |  Size: 82 KiB

BIN
pic/ps2.png 100644

Binary file not shown.

After

Width:  |  Height:  |  Size: 103 KiB

BIN
pic/seg_a7.jpg 100644

Binary file not shown.

After

Width:  |  Height:  |  Size: 129 KiB

BIN
pic/seg_k7.jpg 100644

Binary file not shown.

After

Width:  |  Height:  |  Size: 85 KiB

BIN
pic/vga_pic.png 100644

Binary file not shown.

After

Width:  |  Height:  |  Size: 150 KiB