基于FPGA的乒乓球游戏系统
文档
系统说明:
我们组主要完成的是乒乓球游戏的实现,此游戏是在两个人之间进行的,两个开关键决定了开始是由左右哪一方先发球,按空格键表示发球开始;接下来,球在矩形框图内(相当于球桌)运动,游戏设计成:如果球碰到了矩形上下边界会弹会矩形内继续运动,在球出左右边界之前,如果有一方的球拍未能碰到球,那么对方得分。每两球后换对方发球,如果有一方先得到十分,那么该局游戏结束;然后按空格键开始下一局的游戏。拍子的移动通过键盘输入控制,可以进行上下左右的移动;显示部分则通过VGA接口连接到显示器上,主要包括:背景部分,矩形边框,拍子,球运动四部分显示。
系统模块和任务分配:
1. 键盘输入(read_ps2) -徐敬德
2. VGA显示(vga_display)-许科
3. 拍子移动(bat) -杜俊蛟
4. 游戏主控与随机数产生(ctrl_fsm & random)-黄磊
系统顶层框图:
系统接口说明:
接口名称
类型
封装管脚
说明
clk_i
In
P54
系统时钟
reset_i
In
P
连接到按钮,进行全局复位
ps2_clk_i
In
P96
键盘输入时钟
ps2_data_i
In
P97
键盘输入数据
serve_1_i
In
P
开关1,高电平左方先发球
serve_2_i
In
P
开关2,高电平右方先发球
vga_vs_o
Out
P35
场同步信号,输出到显示器
vga_hs_o
Out
P39
行同步信号,输出到显示器
r_o
Out
P67
红色信号
g_o
Out
P50
绿色信号
b_o
Out
P43
蓝色信号
系统说明:
系统仿真:
由于整个系统分成了四个模块,每个人各自完成自己的部分,并且每个人都做好自己的仿真与测试工作,系统级联时,如果各个部分都做好了,只要把相应的管脚连接设置清楚,就可以进行整体调试了,而没必要再做系统仿真了。
系统模块原理图:
系统整体资源
Selected Device : 3s100etq144-5
Number of Slices: 456 out of 960 47%
Number of Slice Flip Flops: 210 out of 1920 10%
Number of 4 input LUTs: 852 out of 1920 44%
Number of IOs: 16
Number of bonded IOBs: 16 out of 108 14%
Number of GCLKs: 1 out of 24 4%
Timing constraint: Default period analysis for Clock 'clk_i'
Clock period: 11.208ns (frequency: 89.223MHz)
Total number of paths / destination ports: 62084 / 317
实验结果:
以下是我们的实验结果的照片显示:
总结与说明:
自从这门课结束后,我们组就一直在讨论实验的主
,大家也都各自去查找相关资料,最终敲定了乒乓球游戏。对于系统
,我们做了精心的讨论,最终把整个系统分成四个模块,由四个同学分别独立完成,把游戏最核心部分-游戏控制模块交给黄磊同学来完成。就这样,两个星期前我们开始动手实验,经过两个星期的努力,整个系统最终大功告成;可以说,经过这次实验,我们的收获还是很大的,不仅在编程能力和经验上有了很大的提高,也对系统设计有了大致的框图,并且进一步锻炼了团队合作能力。在编程中,由于对语法和指令不甚熟悉,刚开始的进度还是比较缓慢的,在调试过程中,更是遇到了很多问题,最终也都一一得以解决了。
当然,由于时间和水平有限,整个系统还是有一些问题,今后可以进一步改进:
1.我们设计的游戏规则还是相对简单,距离实际的乒乓游戏还是有一定差距,比如没有考虑球碰到桌面反弹的过程,也没有考虑中间有拦截网的问题;
2.在一局游戏结束后,原本设想把获胜一方显示在屏幕上,由于时间有限,这部分最终没有去实现;
以上就是整个系统的设计文档,请老师指正!
键盘模块文档
-徐敬德
模块描述:
此模块使用verilog语言,主要完成的是键盘与游戏主控部分的接口,先接收键盘的时钟和数据信号,以此来接收通码,然后与程序中已保存的通码做比较,从而达到识别按键的目的,如果输入的按键是w,s,a,d(左拍子的上下左右),y,h,g,jt(右拍子的上下左右),space中的某一个,那么相应的把输出信号left_dir,right_dir,serve设置为高电平,传给下一个模块,作为游戏主控模块的输入信号。
按键原理介绍:
当一个键被按下,这个键的通码就被发送到计算机。多数第二套通码只有一个字节宽,但也有少数扩展按键的通码是两字节或四字节宽,这类通码的第一个字节总是E0h。
只要键一释放,断码就会被发送。每个键都有它自己唯一的通码和唯一的断码。多数第二套断码有两字节长,它们的第一个字节是F0h 第二个字节是这个键的通码。扩展按键的断码通常有三个字节,它们前两个字节是E0h,F0h ,最后一个字节是这个按键通码的最后一个字节。
如果按了一个键,这个键的通码被发送到计算机;当你按下并按住这个键,则键盘将一直发送这个键的通码直到它被释放或者其他键被按下。
以下就是从键盘发送数据的时序图:
值得一提的是:这里可以用键盘时钟,也可以用高速的系统时钟来进行数据的采集。
模块管脚说明:
键盘模块包含两个部分,read_ps2_new和ps2_ctrl_new。
ps2_ctrl_new部分的管脚说明如下:
接口名称
类型
说明
Clk
in
时钟
Reset
in
复位
PS2_Clk
in
键盘输入时钟
PS2_Data
in
键盘输入数据
enable
out
使能信号,
主模块来调用
Scan_Code
out
通码或者断码
read_ps2_new部分的管脚说明如下:
接口名称
类型
说明
Reset
in
复位
PS2_Clk
in
键盘输入时钟
PS2_Data
in
键盘输入数据
Clk
in
时钟输入
left_dir
out
3位输出信号,001,010,100,011分别表示左拍子上下左右的移动
right_dir
out
3位输出信号,001,010,100,011分别表示右拍子上下左右的移动
serve
out
为1时,表示开始发球
ps2_code
out
按键的通码或者断码值
模块说明:
根据如上的管脚说明,可以看出ps2_ctrl_new主要完成按键通码与断码的采集。程序中,第一个always主要是根据系统时钟以及键盘输入来的时钟,产生一个稳定的时钟信号filter_clk,在此时钟的上升沿采集键盘输入的数据,因此把它做为第二个always的敏感表;第二个always中采用了状态机的
,分为idle与shifting两个状态,其中shifting主要用作数据的输入,以及寄存器的移位。当输入数据计算器计到8时,就可以判定输入的断码与通码发送完成了,这个时候把使能信号enable设置为1,并把此通码或者断码赋给Scan_Code做为输出;而当计数器计到9时,就把enable设置为0,并把Scan_Code清0,这样,Scan_Code就有值的时间就仅为一个filter_clk周期。
read_ps2_new做为主模块,调用了ps2_ctrl_new模块,并把ps2_ctrl_new的输出信号Scan_Code和enable做为输入,来获得通码或短码值以及相应的使能信号。在该模块中,使用三段式状态机的写法:第一段使用时序逻辑,进行状态转移;第二段使用组合逻辑,根据状态标志位进行状态转移的判断。第三断也使用时序逻辑,根据输入信号的不同,作出相应的输出。采用三段状态机的写法,不仅有利与阅读和理解,更重要的是有利于综合器的优化代码,有利于添加合适的时序约束条件,有利于布局布线。
状态转移部分分成了四个状态:通码输入状态make_code_begin;断码值F0输入状态break_code;断码值输入状态make_code_end以及最后的状态标志位,输出清空状态clear_state。
模块状态机:
make_code_begin:完成通码输入判断功能。把输入的通码与事先保存好的拍子上下左右移动的通码做比较,如果吻合,就进入break_code,并且把fsm1设置为1,相应的输出left_dir,right_dir或serve设置为相应的高电平,指示给游戏主控部分和拍子模块;如果不吻合,也就是输出的不是我们想要的拍子移动的按键,那么继续处于此状态。
break_code:进入此状态,说明已经有通码输入了,那么判断接下来的输入数据是否为F0h,如果是的话,就表示按键松开了,那么就把fsm2设置为1,进入下一个状态make_code_en,如果不是,就表示按键一直按住,这个时候返回上一个状态make_code_begin。
make_code_end:判断接下来的输入是不是与刚才输入的通码值一致,如果是的话,进入下一个状态,把fsm3设置为1,否则,处于等待状态。
clear_state:这个状态主要完成的是几个标志位的清0,以及把left_dir,right_dir或serve清0。
仿真波形:
对于该模块的仿真,原本打算在modesim下完成的,但是考虑到该部分仿真比较简单,因此决定用xinlinx自带的仿真工具来进行。
以上是ps2_ctrl_new模块的仿真波形图,有四个输入信号,分别是Clk,Reset,PS2_Clk,PS2_Data。我们产生相应的波形来做为键盘输入时钟与数据的时序,当发送完毕后,enable就设置为了1,且Scan_Code有了通码或者断码值了。
以上是read_ps2_new模块的仿真波形图,此处的时序仿真为了方便,只输入一个通码来做仿真,我们也可以看出able信号设置为1了。
综合:
接下来对该模块进行综合,以下是综合的RTL图与综合结果:
资源占用情况:
Selected Device : 3s100etq144-5 :
Number of Slices: 61 out of 960 6%
Number of Slice Flip Flops: 52 out of 1920 2%
Number of 4 input LUTs: 110 out of 1920 5%
Number of IOs: 19
Number of bonded IOBs: 19 out of 108 17%
Number of GCLKs: 1 out of 24 4%
Timing constraint: Default period analysis for Clock 'Clk'
Clock period: 4.961ns (frequency: 201.558MHz)
Total number of paths / destination ports: 385 / 48
总结:
此模块主要完成的任务就是键盘数据的采集,以及根据采集到的数据判断输入情况,以驱动下一个模块。程序采用verilog语言,都是自己手写完成。程序原理虽然不难,但是还是有关键的地方,比如:输入时序的判断,状态机的写法等等。刚开始调试中,程序碰到一个问题,即按了一下方向键,拍子就一直移动,而不是事先想象的那样只移动一次,经过耐心调试,后来发现是由于采集到的通码值ps2_code一直有效,只在有其他通码输入时才变化,这样,程序误以为接收到的ps2_cod是新的一个通码输入,这样就把拍子移动标志位一直设置为高电平,使得拍子一直处于移动状态。为了解决这个问题,就加了一个状态标志位able,有通码输入时able设置为1,并且clear_state状态一直等待able清0了才进入make_code_begin状态。
VGA模块文档
-许科
一.功能描述:
此模块主要完成的功能就是显示部分,它主要完成三部分的功能:一个是比分模块的地址产生,以找到相应的比分数;一个是行同步和场同步信号的产生;另一个是在此时序基础上,根据输入的球,拍子,比分牌的坐标位置,输出不同的颜色。
二.管脚说明:
接口名称
类型
说明
reset_i
in
复位
clk_i
in
系统输入时钟
ball_y_i
in
球纵坐标
ball_x_i
in
球横坐标
bat_1_x_i
in
左拍子横坐标
bat_1_y_i
in
左拍子纵坐标
bat_2_x_i
in
右拍子横坐标
bat_2_y_i
in
右拍子纵坐标
left_score_i
in
right_score_i
in
右边得分
ball_finish_i
in
球打完
score_updata_i
in
比分更新
vga_vs_n_o
out
场同步信号输出
vga_hs_n_o
out
行同步信号输出
clk_en_o
out
时钟使能输出
r_o
out
红色信号
g_o
out
绿色信号
b_o
out
蓝色信号
三.VGA原理介绍:
VS为场同步信号,场周期为16.683ms,每场有525行,其中480行为有效显示行,45行为场消隐区,场同步信号每场有一个脉冲,该脉冲的低电平宽度为63μs(2行)。行周期为31.78μs,每显示行包括800点,其中640点为有效显示区,160点为行消隐区(非显示区)。行同步信号HS每行有一个脉冲。该脉冲的低电平宽度为3.81μs(即96个脉冲)。因此,VGA控制器的任务就是按要求产生所需要的时序。DISCLK为视频显示时钟,频率为25MHz,首先输入到模等于800的像素计数器中,输出的计数值与一个预先设好的比较器进行比较,当计数器的值大于160时,输出高电平,反之输出低电平,作为行同步信号;同理,利用一个模等于525的计数器对行同步信号进行计数和一个阈值为45的比较器可以产生所需要的场同步脉冲VS。
产生的行、场同步信号和像素显示时钟分别被送到两个地址发生器中,产生所需要的控制帧存储器的地址信号。
四.模块介绍:
1. ram字符产生:
比分输入做为字符rom的基地址,然后结合变址去访问每一个元素。
2. 块地址产生:
因为以像素为单位进行显示会使显示的图像很小,所以这里采用以块为单位进
行显示,块地址产生模块就是为了产生相应以块为单位的地址。
3. 行,场同步脉冲
依照vga时序进行产生。但要注意这里采用延迟一拍的实现,因为r,g,b输出采
用寄存器输出。
4. 显示
根据输入的数据参数进行显示,输入的值都是以块为单位的。寄存器输出避免
毛刺带来影响。
五.仿真:
对每个模块,都分别做了仿真:
VGA部分的仿真图
块地址产生
R,g,b产生
Ram字符产生
综合结果
Device utilization summary:
Selected Device : 3s100etq144-5 :
Number of Slices: 171 out of 960 17%
Number of Slice Flip Flops: 66 out of 1920 3%
Number of 4 input LUTs: 319 out of 1920 16%
Number of IOs: 65
Number of bonded IOBs: 65 out of 108 60%
Number of GCLKs: 1 out of 24 4%
Timing constraint: Default period analysis for Clock 'clk_i'
Clock period: 8.721ns (frequency: 114.668MHz)
Total number of paths / destination ports: 2494 / 101
六.模块特点:
1. 此模块设计采用了纯参数PARAMETER的设计思想,模块中没有数字出现,不但便于阅读,而且便于修改;
2. 考虑到输入时钟可能不尽相同,我们考虑到了三种时钟:25m,50m,100m,针对三种情况对参数值进行了定义;
3. 考虑到像素为640×480,为了减少仿真时间,定义了debug,把仿真像素减少到100×100;
4. 各个模块划分清晰,功能完整;
5. 使用全同步设计思想,以像素显示时钟做为全局时钟。
拍子模块文档
-杜俊蛟
1.模块描述:
此模块完成的功能相对简单,主要就是根据输入拍子的移动标志,对拍子进行上下左右的移动。需要注意的地方有两个:一个是拍子的移动范围限制在矩形球桌内,因此当碰到四个边界时,再往四个边界的移动都视为无效;另一个是,当拍子击打完球后,如果有那么拍子的移动速度不能超过球的移动速度,也就是说球往对面运动时,拍子的横坐标位置只限制在球与边界之间。
2.管脚定义:
名称
说明
clk_i
时钟输入
reset_i
复位
bat_clk_en_i,
拍子时钟使能
direct_1_i,
左拍子移动标志
direct_2_i,
右拍子移动标志
ball_x_i,
球的横坐标值
ball_x_dir_i,
球速率
bat_1_x_o,
左拍子横坐标值
bat_1_y_o,
左拍子纵坐标值
bat_2_x_o,
右拍子横坐标值
bat_2_y_o
右拍子横坐标值
3.仿真:
对拍子模块进行功能仿真,如下:
4.综合后的RTL图:
Selected Device : 3s100etq144-5
Number of Slices: 91 out of 960 9%
Number of Slice Flip Flops: 50 out of 1920 2%
Number of 4 input LUTs: 174 out of 1920 9%
Number of IOs: 45
Number of bonded IOBs: 45 out of 108 41%
Number of GCLKs: 1 out of 24 4%
Timing constraint: Default period analysis for Clock 'clk_i'
Clock period: 6.052ns (frequency: 165.248MHz)
Total number of paths / destination ports: 1170 / 76
游戏控制部分文档
-黄磊
1. 模块描述
控制部分是整个乒乓球游戏的核心部分,主要功能是完成球的坐标生成,比
分生成,胜负判断,以及整个游戏流程的控制。它的输入分别来自三个部分,即拍子控制模块,随机数生成模块,以及板上的开关及键盘。它的输出主要包括球的坐标,比分和一些控制信号,在模块说明部分会给出详细的解释。
2. 模块引脚说明
引脚
引脚功能说明
Clk_i
时钟输入(25MHz)
Reset_i
复位输入(高电平有效)
fsm_clk_en_i
时钟始能输入
speed_x_i
x方向速度输入
speed_y_i
y方向速度输入
initial_direct_i
初始方向输入
bat_1_x_i
左面拍子的x坐标输入
bat_1_y_i
左面拍子的y坐标输入
bat_2_x_i
右面拍子的x坐标输入
bat_2_y_i
右面拍子的y坐标输入
start_i
开始输入(来自键盘模块)
serve_1_i
左边发球权输入(来自板上开关)
serve_2_i
右边发球权输入(来自板上开关)
ball_x_o
球x坐标输出(到显示模块)
ball_y_o
球y坐标输出(到显示模块)
left_score_o
左边比分输出(到显示模块)
right_score_o
右边比分输出(到显示模块)
ball_finish_o
一局比赛结束输出(到显示模块)
ball_x_dir_o
球x运动方向输出(到键盘控制模块)
score_updata_o
显示比分更新输出(到显示模块)
3. 模块说明
· 控制部分完成的最核心的功能是游戏流程的控制,这可以由下面的状态
机进行很好的阐述。
上面是核心状态机转移图,一共分为6个状态,下面分别对各个状态的功能
以及状态转移进行一下解释。
(1) IDLE是系统复位时候的初始状态也是当进入某种未定义状态时的下一
状态,这样可以保证状态机的自启动。正常情况下,IDLE的下一状态为GAME_TITLE。
(2) GAME_TITLE这个状态是我为了今后系统扩展时候预留的状态,这个
状态下检测start_i是否为高电平,是的话转移到CHOOSE_SIDE状态,否则保持状态不变。
(3) CHOOSE_SIDE用来决定初始发球方,如果在一局比赛开始的时候,初
始发球方由serve_1_i和serve_2_i两个信号决定,当serve_1_i为高电平的时候,左边拍子发球,当serve_2_i为高电平时右边拍子发球,其他情况默认是左边发球。如果是在比赛中出现需要重新发球的情况(一方没有接到来球),这时候发球方由内部信号控制,这样做可以实现发球的轮换(三分一轮换)。这个状态无条件转移到下一个状态DECIDE_SPEED。
(4) DECIDE_SPEED表示决定球初始运动速度和运动方向。运动速度包括
X方向速度,y方向速度,运动方向主要指y轴的方向,这里方向和速度都是随机的。DECIDE_SPEED无条件转移到LOOP状态。
(5) LOOP状态,这个状态是整个状态机中最主要的状态,在这个状态下完
成了球的更新。如果检测到球碰壁,则转换到INCSCORE状态,否则保持LOOP状态不变。
(6) INCSCORE状态完成比分的更新,如果一方首先达到10分,则进入
FINISH状态,否则进入CHOOSE_SIDE状态。
(7) FINISH状态表示一方获胜,如果start_i有效,则开始新的一局,否则
保持状态三不变。
· 球更新
控制模块的核心功能是完成球位置的更新,以及游戏流程控制信号的产生。
下面进行一下简单的说明。
(1) 球的数据结构包括6个部分分别是,水平坐标,垂直坐标,水平方向速
度,垂直方向速度,水平运动方向,垂直运动方向。水平坐标和垂直坐标按照下式更新。
球下周期水平坐标=(向右运动)?当前水平坐标+水平速度:当前水平坐标-水平速度
球下周期垂直坐标=(向下运动)?当前垂直坐标+垂直速度:当前垂直坐标-垂直速度
(2) 游戏流程控制信号,主要包括两个方面,一个是碰拍子的判断,另一个
是碰壁的判断。
碰拍子判断主要是比较球左上角水平坐标与拍子水平坐标之间的关系。
当发生碰拍子的情况,一是要改变球的水平运动方向,另一方面要改变球水平和垂直运动速度,这样可以增加游戏性。
碰水平壁的判断主要是比较球左上角水平坐标与壁水平坐标之间的关系,当发生碰水平壁的时候,意味着一方得一分,这会导致状态转移的发生。
碰垂直壁的判断主要是比较球左上角垂直坐标与壁垂直坐标之间的关
系,发生垂直碰壁的时候主要改变垂直运动方向,其他不会发生变化。
· 发球权轮换
这里实现的是3分轮换制,每发三次球进行一次发球轮换。
· 时钟控制问题
由于整个系统采用完全同步设计,系统时钟采用的是25MHz的像素时
钟,这个对于状态转移来说太快了,所以这里我采用时钟使能来解决,时钟
使能信号有显示模块提供,每一帧产生一个时钟周期的时能信号,主状态的
的转移只有在时钟使能信号有效的情况下才会发生,但这个信号用于球位置
的更新就会使球运动太快,所以将这个信号进行一定的分频,产生另外一个
球更新使能信号,这这个信号的控制下,进行球的更新。
4. 仿真说明
在整个游戏开发的过程中,仿真都是在modelsim下完成的,这里我专门做
了一个仿真版本的程序,这个程序绝大部分与最后的程序是一样的,但是有一些
参数不一样,比如说屏幕大小的设定,在仿真版本中行我设为100,列设为100,再如在仿真版本中,我的时钟使能信号是在一行结束后产生的,这样做的目的是
如果完全采用标准的参数方针时间会非常长,严重影响方针的效率,具体参数的设定在game_define.v中直接修改就可以了,下面给出一些具体的仿真结果图。
时钟系统:
球位置更新:
比分更新:
5. 综合说明
· 综合电路图:synplify pro 8.1
· 综合资源需求:ise 9.1
Device utilization summary:
---------------------------
Selected Device : 3s100etq144-5
Number of Slices: 152 out of 960 15%
Number of Slice Flip Flops: 43 out of 1920 2%
Number of 4 input LUTs: 279 out of 1920 14%
Number of IOs: 66
Number of bonded IOBs: 66 out of 108 61%
Number of GCLKs: 1 out of 24 4%
· 时钟
Timing constraint: Default period analysis for Clock 'clk_i'
Clock period: 10.852ns (frequency: 92.152MHz)
Total number of paths / destination ports: 24447 / 55
因为时钟不用跑得太高,所以我没有加时钟约束。
随机数部分文档
-黄磊
1. 模块描述
随机数模块主要是用来产生水平速度,垂直速度和初始运动方向。
2. 模块引脚说明
引脚
引脚功能说明
clk_i
时钟输入(25MHz)
reset_i
复位输入
speed_x_o
水平速度输出
speed_y_o
垂直速度输出
initial_direct_o
初始方向输出
3. 模块说明
随机数采用8位伪随机数发生器,生成多项式为
然后将输出的结果分成4个区间,每个区间返回不同的水平和垂直速度值,
方向输出分成两个区间,返回向上或向下两个方向。
4. 仿真说明
给出modelsim下的仿真结果
5. 综合说明
· 综合电路
· 资源需求
Device utilization summary:
---------------------------
Selected Device : 3s100etq144-5
Number of Slices: 6 out of 960 0%
Number of Slice Flip Flops: 8 out of 1920 0%
Number of 4 input LUTs: 11 out of 1920 0%
Number of IOs: 9
Number of bonded IOBs: 9 out of 108 8%
Number of GCLKs: 1 out of 24 4%
· 时钟
Timing constraint: Default period analysis for Clock 'clk_i'
Clock period: 2.115ns (frequency: 472.891MHz)
Total number of paths / destination ports: 9 / 8
reset_i
clk_i
乒乓球游戏
的FPGA实现
serve_1_i
serve_2_i
ps2_clk_i
ps2_data_i
r_o
b_o
g_o
vga_vs_o
vga_hs_o
_1259910121.unknown