Académique Documents
Professionnel Documents
Culture Documents
For the project, as it was required, we did use Verilog to code our game. For
implementation we utilized the FPGAs in our laboratory. Since we are already well aware of
Verilog and FPGAs thanks to the laboratory sessions, we were able focus on the game design
more.
Details of our source code along with some screenshots from the gameplay can be
found in the following sections.
The code segment that includes our variable definitions is given below.
input kontrol, clk_in, l1, l2, r1, r2, u1, u2, d1, d2;
output vga_h_sync, vga_v_sync, vga_R, vga_G, vga_B, vga_R2, vga_G2,
vga_B2,vga_R1, vga_G1, vga_B1, ses;
2.2. Initializations
Upon defining our variables, we initialized the variables that need proper values
before getting processed. For this purpose, we did use an initial block. The related source
code can be seen as follows;
// All Initializations
initial
begin
topyh=1; //2: up ,1:down
topxh=1; //2: left , 1:right
m1x= 7;
m1y= 199;
m2x = 552;
m2y= 201;
topx = 50;
topy = 200;
ml1=0;
ml2=0;
mr1=0;
mr2=0;
mu1=0;
mu2=0;
md1=0;
md2=0;
ses=0;
sag_mal=0;
sol_mal=0;
clk=0;
klk=0;
saat=0;
ses1=0;
ses2=0;
ses3=0;
counter=0;
ctr=0;
ctrc=0;
ctrb=0;
ctra=0;
cnr=0;
alert=0;
biter=0;
aa = 2;
bb = 0;
cc = 3;
kontrol2 = 0;
end
2.3. Frequency Divider (User Defined Clocks)
The FPGA board that we used in the laboratory has a 100 MHz internal clock. However,
to be able to achieve vertical and horizontal synchronization, we had to divide it to get a
frequency of 25 MHz Furthermore; we did define other clocks such as klk, ses1 for the
convenience of gameplay and for specific sounds. Source code for the clocks that we defined
is given below.
// All clock divisions
// 25MHz , gaming clock(250Hz), real time clock (1Hz), sound effects
// 25 Mhz
always @ (posedge clk_in)
begin
counter = counter+1;
if (counter ==2)
begin
clk=~clk;
counter=0;
end
end
// Gaming clock (klk 250Hz)
always @ (posedge clk)
begin
if (kontrol==0&&kontrol2==0)
begin
ctr = ctr+1;
if (ctr == 50000)
begin
klk=~klk;
ctr=0;
end
end
else if (kontrol==1||kontrol2==1)
begin klk=0; end
end
// Sound Effect 1
always @ (posedge clk)
begin
ctra = ctra+1;
if (ctra == 5000)
begin
ses1=~ses1;
ctra=0;
end
end
// Sound Effect 2
always @ (posedge clk)
begin
ctrb = ctrb+1;
if (ctrb == 2500)
begin
ses2=~ses2;
ctrb=0;
end
end
// Sound Effect 3
always @ (posedge clk)
begin
ctrc = ctrc+1;
if (ctrc == 7500)
begin
ses3=~ses3;
ctrc=0;
end
end
// VGA Synchronizations
always @ (posedge clk)
begin
if (countx<799)
countx=countx+1;
else
begin
countx=0;
if (county< 520)
county=county+1;
else
county=0;
end
assign vga_h_sync=vga_h;
assign vga_v_sync=vga_v;
2.5. Borders
We created our game screen considering 640x480 display resolution. When we create
the gameplay area, we left a place for countdown and scoreboard parts at the bottom of the
screen and all the mullets and the ball are restricted by the borders of the air hockey field.
The borders are created by the following code segment:
wire
border=(county==0)||(county==400)||(((county<140)||(county>260))&&((countx=
=0)||(countx==559)))||((countx==279)&&((county>0)||(county<400)));
Here we set up and down borders by a straightforward manner. On the other hand,
for the left and right parts we also considered goal areas. The borders are displayed in white
color in normal game play.
Here, top stands for the ball, m1 and m2 stand for the mullets. The ball is a round shaped
with radius=10pixels. Mullets are rectangular with 60x6 pixels dimensions. topx , topy,
m1x, m1y, m2x, m2y are the registers that store the midpoint information of the ball,
mullet1 and mullet2.
For the mullet & ball interactions algorithm is almost the same as ball & border
interactions. The code checks the location of the midpoints at every gaming clock and
decides whether the ball is one of the interaction zones. There are four zones for each
mullet: upper-left, upper-right, lower-left, lower-right. For example, if the ball comes from
the upper-right of the one of the mullet it bounces back, preserving its y-directional speed
and inverts x-directional speed.
For the mullet movements, we are required to move each mullet to the four
directions by external commands. We assigned 8 pins on the fpga board as our inputs. These
pins send up, down, left, right commands for each mullet. When there is a command to the
mullet the midpoint of the mullet moves accordingly. We adjusted our game so that the
mullet cannot pass the midline of the game field as well as the borders.
We created all these movement and interaction code in one always block with some
clock signal. The clock signal of this part klk is determines the gaming speed, that is why we
used another divided clock with 250Hz. In this part of the code we also added an external
control to pause the game when the value of this pause pin is high then 0 is assigned to klk
signal and the whole game and the countdown is stopped.
In the following code segment you can see a few conditions on the interaction and
movements. The whole code block can be reached from the Appendix C. Here, topxh and
topyh are the information about the balls movement, i.e. the directional speed or the goal
situation. l1, l2, u1 are the inputs for the mullets and m1x, m1y, m2x, m2y are the
midpoint information for mullets.
// ball directions
case (topyh)
1: topy<=topy+1; // goes down
2: topy<=topy-1; // goes up
0: topy<=topy; // no change
3: topy<=201; // after goal initialization
4: topy<=201; // after goal initialization
endcase
case (topxh)
1: topx<= topx+1; // goes right
2: .
// mullet inputs
ml1<=l1;
ml2<=l2;
// mullet 1 movements
if ((ml1==1)&(m1x>8)) begin m1x<=m1x-1; end // goes left
// mullet 2 movements
if ((ml2==1)&(m2x>284)) begin m2x<=m2x-1; end // goes left
end
2.8. Goal
The main purpose of the game is to score goals in order to win the game, beat the
opponent. For this purpose, we need to let goals to be scored when ball comes some specific
areas. These goal areas were defined when we created the borders.
When there is a goal, the display under the mullet which scores the goal increases by
one. Also, when a mullet concedes a goal the ball appears right in front of it, as it was
required. After a goal mullet locations are also initialized. And then the mullet who conceded
a goal starts the game. The related code segment is this;
// GOAL!
if ((topx==3)&((topy<260)&(topy>140))) // goal to the left side
begin sag_mal<=sag_mal+1; topxh<=3; topyh<=3; m1x<=7; m2x<=552;
m1y<=201; m2y<=201; end
Here, sol_mal and sag_mal are the scores of the mullets. This score information is
stored in registers. By controlling topxh, topyh the reappearing of the ball in from of the
mullet is also provided. This segment is also located in the always block that we considered
the interactions and movements.
2.9. Scoreboard
We created one digit seven-segment-like displays to shows the score. There are two
such displays under the each mullet showing the score of the mullet. After each goal the
display under the mullet which scores increases by one. These displays are also colored with
the some color of each mullet. At the end of the game the winner is decided by considering
these scores.
The code of these displays can be reached from the whole project code that is
available in the Appendix C. We created all numbers pixel by pixels with case function in
Verilog and assigned each of them to vga output according to their desired colors.
2.10. Timing
We were required to create a countdown to indicate time left until the end of the
game. For this purpose we created three more seven segment-like displays. Our countdown
counts in seconds from an initial value to 0. The initial value can easily be adjusted from 0
to 999. To count in 1Hz rate (real timing frequency) we created another clock signal saat by
dividing the internal clock. And then in an always block controlled by this saat signal, we
made assignments of the digits and adjusted a nice countdown in real time.
Here, aa, bb, cc are the three digits of the time left from least significant to most
significant. For example, when cc=2, bb=6, aa=4, the time left is 264 seconds. And each digit
is displayed with 7-segment-like displays on the screen. The code of these 7segment-like
displays can be reached from the Appendix C.
Here, sol_mal and sag_mal are the scored goal numbers of the each mullet. sol,
sag, ber indicates the winner mullet and accordingly the sound and screen colorings are
made.
We assigned out sound signals to an output pin available on the fpga board and we
connected a speaker to that pin in order to hear those signals.
When a goal is scored one the sound signals is heard until the mullet which conceded
the goal starts the game. The same sound is also heard when the time left is less than 10
seconds as an alert.
The other two distinct sounds are heard at the end of the game indicating the winner.
We adjusted one signal for blue wins case and the other signal to green wins case.
3. Conclusion
In this project, we learned lots of thing about digital electronics, logic design, FPGA and
Verilog coding. We made research on arcade games and Verilog and gained insight into
Verilog coding as well as FPGA. This project is an implementation of the topics covered
during this semester in the digital electronics and logic design lectures and digital electronics
laboratory. Actually, the whole project was enjoyable to deal with. We have successfully
managed to solve the problems we faced during the project. We learnt how to drive a VGA
monitor by using FPGA. Then, we learnt drawing images on the screen using Verilog codes.
We created our code step by step and block by block and design summary of our project can
be seen from Appendix B.
Fortunately, we have reached a good final. Our project satisfies all the requirements
introduced: Two different colored mullet, time limitation and countdown, scoreboard,
disappeared puck when the goal is scored. Also we have created sound affects for some
specific cases as bonus objective.
4. Appendix
4.1. Appendix A
4.3 Appendix C
The whole Air-Hockey Project Code can be seen below.
input kontrol, clk_in, l1, l2, r1, r2, u1, u2, d1, d2;
output vga_h_sync, vga_v_sync, vga_R, vga_G, vga_B, vga_R2, vga_G2,
vga_B2,vga_R1, vga_G1, vga_B1, ses;
// All Initializations
initial
begin
topyh=1; //2: up ,1:down
topxh=1; //2: left , 1:right
m1x= 7;
m1y= 199;
m2x = 552;
m2y= 201;
topx = 50;
topy = 200;
ml1=0;
ml2=0;
mr1=0;
mr2=0;
mu1=0;
mu2=0;
md1=0;
md2=0;
ses=0;
sag_mal=0;
sol_mal=0;
clk=0;
klk=0;
saat=0;
ses1=0;
ses2=0;
ses3=0;
counter=0;
ctr=0;
ctrc=0;
ctrb=0;
ctra=0;
cnr=0;
alert=0;
biter=0;
aa = 2;
bb = 0;
cc = 3;
kontrol2 = 0;
end
// All clock divisions
// 25MHz , gaming clock(250Hz), real time clock (1Hz), sound effects
// 25 Mhz
always @ (posedge clk_in)
begin
counter = counter+1;
if (counter ==2)
begin
clk=~clk;
counter=0;
end
end
// VGA Synchronizations
always @ (posedge clk)
begin
if (countx<799)
countx=countx+1;
else
begin
countx=0;
if (county< 520)
county=county+1;
else
county=0;
end
assign vga_h_sync=vga_h;
assign vga_v_sync=vga_v;
// Sound Effect 1
always @ (posedge clk)
begin
ctra = ctra+1;
if (ctra == 5000)
begin
ses1=~ses1;
ctra=0;
end
end
// Sound Effect 2
always @ (posedge clk)
begin
ctrb = ctrb+1;
if (ctrb == 2500)
begin
ses2=~ses2;
ctrb=0;
end
end
// Sound Effect 3
always @ (posedge clk)
begin
ctrc = ctrc+1;
if (ctrc == 7500)
begin
ses3=~ses3;
ctrc=0;
end
end
// GOAL!
if ((topx==3)&((topy<260)&(topy>140)))
begin sag_mal<=sag_mal+1; topxh<=3; topyh<=3; m1x<=7; m2x<=552;
m1y<=201; m2y<=201; end
// ball directions
case (topyh)
1: topy<=topy+1; // goes down
2: topy<=topy-1; // goes up
0: topy<=topy; // no change
3: topy<=201; // after goal initialization
4: topy<=201; // after goal initialization
endcase
case (topxh)
1: topx<= topx+1; // goes right
2: topx<= topx-1; // goes left
0: topx<= topx; // no change
3: topx<= 50; // after goal initialization
4: topx<= 509; // after goal initialization
endcase
// mullet inputs
ml1<=l1;
ml2<=l2;
mr1<=r1;
mr2<=r2;
mu1<=u1;
mu2<=u2;
md1<=d1;
md2<=d2;
// mullet 1 movements
if ((ml1==1)&(m1x>8)) begin m1x<=m1x-1; end // goes left
if ((mr1==1)&(m1x<275)) begin m1x<=m1x+1; end // goes right
if ((mu1==1)&(m1y>40)) begin m1y<=m1y-1; end // goes up
if ((md1==1)&(m1y<360)) begin m1y<=m1y+1; end // goes down
// mullet 2 movements
if ((ml2==1)&(m2x>284)) begin m2x<=m2x-1; end // goes left
if ((mr2==1)&(m2x<551)) begin m2x<=m2x+1; end // goes right
if ((mu2==1)&(m2y>40)) begin m2y<=m2y-1; end // goes up
if ((md2==1)&(m2y<360)) begin m2y<=m2y+1; end // goes down
end
// End of the Interaction and Movements
// Countdown
always @ (posedge saat)
begin
if ((aa!=0)&(biter==0))
begin aa<=aa-1; end
else if((aa==0)&(bb!=0)&(biter==0))
begin aa<=9; bb<=bb-1; end
else if ((aa==0)&(bb==0)&(biter==0))
begin aa<=9; bb<=9; end
if ((bb==0)&(cc!=0)&(aa==0)&(biter==0))
begin bb<=9; cc<=cc-1; end
else if ((bb==0)&(aa==0)&(cc==0)&(biter==0))
begin bb<=9; cc<=0; end
end
/// The End of the Game and the Sound Effects
always @ (posedge clk)
begin
if (biter==1 & sol_mal>sag_mal)
begin sol <= (county>0)&(county<400)& (countx>0) &(countx<559);
ses<=ses1; kontrol2<=1; end //the mullet on the left won
if (biter==1 & sol_mal<sag_mal)
begin sag <= (county>0)&(county<400)& (countx>0) &(countx<559);
ses<=ses2; kontrol2<=1; end // the mullet on the right won
if (biter==1 & sol_mal==sag_mal)
begin ber <= (county>0)&(county<400)& (countx>0) &(countx<559);
kontrol2<=1; end // no winner, score is even
if((topx==50&topy==201&biter==0)||(topx==509& topy==201&biter==0))
begin ses<=ses3; end
if(((topx!=50 & topy!=201) || (topx!=509 & topy!=201))&(biter==0))
begin ses<=0; end
if ((cc==0)&(bb<=0)&((aa<=9)||(aa>=0))& biter==0)
begin alert=(((county>410)||(county<430))&
((countx>220)||(countx<320))); ses<=ses3; end
if(aa==0&bb==0&cc==0)
begin biter<=1; end
end
// Countdown cc_bb_aa
always @ (posedge clk)
begin
case (aa)
0: begin
a1 = (county==435 && (countx>330&&countx<345));
a2 = (countx==345 && (county>435&&county<450));
a3 = (countx==345 && (county>450&&county<465));
a4 = (county==465 && (countx>330&&countx<345));
a5 = (countx==330 && (county<465&&county>450));
a6 = (countx==330 && (county<450&&county>435));
a7 = 0;
end
1: begin
a1 = 0;
a2 = (countx==345 && (county>435&&county<450));
a3 = (countx==345 && (county>450&&county<465));
a4 = 0;
a5 = 0;
a6 = 0;
a7 = 0;
end
2: begin
a1 = (county==435 && (countx>330&&countx<345));
a2 = (countx==345 && (county>435&&county<450));
a3 = 0;
a4 = (county==465 && (countx>330&&countx<345));
a5 = (countx==330 && (county<465&&county>450));
a6 = 0;
a7 = (county==450 && (countx>330&&countx<345));
end
3: begin
a1 = (county==435 && (countx>330&&countx<345));
a2 = (countx==345 && (county>435&&county<450));
a3 = (countx==345 && (county>450&&county<465));
a4 = (county==465 && (countx>330&&countx<345));
a5 = 0;
a6 = 0;
a7 = (county==450 && (countx>330&&countx<345));
end
4: begin
a1 = 0;
a2 = (countx==345 && (county>435&&county<450));
a3 = (countx==345 && (county>450&&county<465));
a4 = 0;
a5 = 0;
a6 = (countx==330 && (county<450&&county>435));
a7 = (county==450 && (countx>330&&countx<345));
end
5: begin
a1 = (county==435 && (countx>330&&countx<345));
a2 = 0;
a3 = (countx==345 && (county>450&&county<465));
a4 = (county==465 && (countx>330&&countx<345));
a5 = 0;
a6 = (countx==330 && (county<450&&county>435));
a7 = (county==450 && (countx>330&&countx<345));
end
6: begin
a1 = (county==435 && (countx>330&&countx<345));
a2 = 0;
a3 = (countx==345 && (county>450&&county<465));
a4 = (county==465 && (countx>330&&countx<345));
a5 = (countx==330 && (county<465&&county>450));
a6 = (countx==330 && (county<450&&county>435));
a7 = (county==450 && (countx>330&&countx<345));
end
7: begin
a1 = (county==435 && (countx>330&&countx<345));
a2 = (countx==345 && (county>435&&county<450));
a3 = (countx==345 && (county>450&&county<465));
a4 = 0;
a5 = 0;
a6 = 0;
a7 = 0;
end
8: begin
a1 = (county==435 && (countx>330&&countx<345));
a2 = (countx==345 && (county>435&&county<450));
a3 = (countx==345 && (county>450&&county<465));
a4 = (county==465 && (countx>330&&countx<345));
a5 = (countx==330 && (county<465&&county>450));
a6 = (countx==330 && (county<450&&county>435));
a7 = (county==450 && (countx>330&&countx<345));
end
9: begin
a1 = (county==435 && (countx>330&&countx<345));
a2 = (countx==345 && (county>435&&county<450));
a3 = (countx==345 && (county>450&&county<465));
a4 = (county==465 && (countx>330&&countx<345));
a5 = 0;
a6 = (countx==330 && (county<450&&county>435));
a7 = (county==450 && (countx>330&&countx<345));
end
endcase
end
always @ (posedge clk)
begin
case (bb)
0: begin
b1 = (county==435 && (countx>305&&countx<320));
b2 = (countx==320 && (county>435&&county<450));
b3 = (countx==320 && (county>450&&county<465));
b4 = (county==465 && (countx>305&&countx<320));
b5 = (countx==305 && (county<465&&county>450));
b6 = (countx==305 && (county<450&&county>435));
b7 = 0;
end
1: begin
b1 = 0;
b2 = (countx==320 && (county>435&&county<450));
b3 = (countx==320 && (county>450&&county<465));
b4 = 0;
b5 = 0;
b6 = 0;
b7 = 0;
end
2: begin
b1 = (county==435 && (countx>305&&countx<320));
b2 = (countx==320 && (county>435&&county<450));
b3 = 0;
b4 = (county==465 && (countx>305&&countx<320));
b5 = (countx==305 && (county<465&&county>450));
b6 = 0;
b7 = (county==450 && (countx>305&&countx<320));
end
3: begin
b1 = (county==435 && (countx>305&&countx<320));
b2 = (countx==320 && (county>435&&county<450));
b3 = (countx==320 && (county>450&&county<465));
b4 = (county==465 && (countx>305&&countx<320));
b5 = 0;
b6 = 0;
b7 = (county==450 && (countx>305&&countx<320));
end
4: begin
b1 = 0;
b2 = (countx==320 && (county>435&&county<450));
b3 = (countx==320 && (county>450&&county<465));
b4 = 0;
b5 = 0;
b6 = (countx==305 && (county<450&&county>435));
b7 = (county==450 && (countx>305&&countx<320));
end
5: begin
b1 = (county==435 && (countx>305&&countx<320));
b2 = 0;
b3 = (countx==320 && (county>450&&county<465));
b4 = (county==465 && (countx>305&&countx<320));
b5 = 0;
b6 = (countx==305 && (county<450&&county>435));
b7 = (county==450 && (countx>305&&countx<320));
end
6: begin
b1 = (county==435 && (countx>305&&countx<320));
b2 = 0;
b3 = (countx==320 && (county>450&&county<465));
b4 = (county==465 && (countx>305&&countx<320));
b5 = (countx==305 && (county<465&&county>450));
b6 = (countx==305 && (county<450&&county>435));
b7 = (county==450 && (countx>305&&countx<320));
end
7: begin
b1 = (county==435 && (countx>305&&countx<320));
b2 = (countx==320 && (county>435&&county<450));
b3 = (countx==320 && (county>450&&county<465));
b4 = 0;
b5 = 0;
b6 = 0;
b7 = 0;
end
8: begin
b1 = (county==435 && (countx>305&&countx<320));
b2 = (countx==320 && (county>435&&county<450));
b3 = (countx==320 && (county>450&&county<465));
b4 = (county==465 && (countx>305&&countx<320));
b5 = (countx==305 && (county<465&&county>450));
b6 = (countx==305 && (county<450&&county>435));
b7 = (county==450 && (countx>305&&countx<320));
end
9: begin
b1 = (county==435 && (countx>305&&countx<320));
b2 = (countx==320 && (county>435&&county<450));
b3 = (countx==320 && (county>450&&county<465));
b4 = (county==465 && (countx>305&&countx<320));
b5 = 0;
b6 = (countx==305 && (county<450&&county>435));
b7 = (county==450 && (countx>305&&countx<320));
end
endcase
end
// Color Outputs
wire
R=border||top||a1||a2||a3||a4||a5||a6||a7||b1||b2||b3||b4||b5||b6||b7||c1||
c2||c3||c4||c5||c6||c7;
wire
R1=border||top||a1||a2||a3||a4||a5||a6||a7||b1||b2||b3||b4||b5||b6||b7||c1|
|c2||c3||c4||c5||c6||c7;
wire
R2=border||top||a1||a2||a3||a4||a5||a6||a7||b1||b2||b3||b4||b5||b6||b7||c1|
|c2||c3||c4||c5||c6||c7;
wire G = border||m1||top||w1||w2||w3||w4||w5||w6||w7||sol||ber;
wire G1= border||m1||top||w1||w2||w3||w4||w5||w6||w7||sol||ber;
wire
G2=border||m1||top||alert||a1||a2||a3||a4||a5||a6||a7||b1||b2||b3||b4||b5||
b6||b7||c1||c2||c3||c4||c5||c6||c7||w1||w2||w3||w4||w5||w6||w7;
wire B = border||m2||top||e1||e2||e3||e4||e5||e6||e7||sag||ber;
wire B1= border||m2||top||e1||e2||e3||e4||e5||e6||e7||sag||ber;
wire
B2=border||m2||top||alert||a1||a2||a3||a4||a5||a6||a7||b1||b2||b3||b4||b5||
b6||b7||c1||c2||c3||c4||c5||c6||c7||e1||e2||e3||e4||e5||e6||e7;
/// FINISH