Projects‎ > ‎FPGA Projects‎ > ‎

Pong via VGA

Context: Study at Beuth Hochschule, Embedded Systems with Prof. Dr.-Ing. Detlef Heinemann 
 

Concept

 

Description

 
Project team: Yvonne Wöstefeld, Manuel Jacob, Alexander Wegner

Pong is a computer game which is comparable to tennis. It's supposed to be the first computer game and was created by Atari in 1972 (Pong Arcade).

Two players are using a bat to punch a moving ball to the opponent's field. If a player is missing the ball, the other one is scoring one point. The game is over if one players scores up to nine points.
The bats are symbolized by rectangles which can be moved up and down on the screen. The ball is bouncing from the bats or from the upper and lower screen border. If the ball hits the left or right border, the player of the opposite site scores.
The score is visualized in the upper screen centre.
 
Pong Arcade von Atari aus dem Jahre 1972 (Quelle: Telegamez.de)
Figure: Pong Arcade by Atari from 1972 (Source: Telegamez.de)
 

Hardware and graphic concept

 
A Nexys2 board with a FPGA chip is used to realize the game. The hardware is descript and synthesized in VHDL
The graphical output, with the screen resolution of 640 pixel times 480 pixels, is done via the VGA port of the Nexys2 board.
Different colours are used to improve the game experience. Score and bat have different colours for each player (Player 1: red, player 2: blue). The ball and the middle line are displayed in white.
Controlling the bats is the done via four input pins of the Nexys2 board (Up and Down movement)
 
Designkonzept
Figure: Design concept
 

Game physics

 
The positions of the bat, the ball and further objects are defined trough their origin. The origin point is placed in the upper left corner of each object. This point is the reference for the game physics and the display on the screen. All objects are defined by their origin point and their height and width. A bat for example has the dimensions 15x70 pixels and the ball 15x15 pixels.
Only the origin might change during the game. The score board and the middle line have fixed origin points on the screen.
The horizontal axis is called the x-axis and the vertical axis the y axis. The origin of both axis is placed in the upper left corner of the screen. The values on the axis are increased due moving along in rightward or downward direction.
The bats can be moved vertically. Each player can move his bat parallel to the y-axis due pressing the corresponding button. The maximal movement speed is set by the game. The bats can't be moved outside the visual area.
The ball is moved in x and y direction by vector addition. The ball is moved on a random vector (28 different vectors are possible). Direction, angel and speed are set by the game. The position of the ball is manipulated after a variable time period. The vector of the movement consists of a x and a y component (values form -3 up to 3).
 
Figure: 28 different vectors for the ball movement
 
If the ball hits the upper or lower screen border, the y component of the moving vector is negated. The ball seems to bounce from the wall.
If the ball hits one of the bats (Y position is in the range of the bat's height, x position is the range of the bat's width), the x component is changed and the movement speed of the ball is increased (refresh time of the ball position is lowered).
It might happen that the ball is missed by a player. If the ball hits the left or right border, the opposite player scores, the ball position is reset to the middle of the screen, the movement speed is set to default and a new moving vector is created.
The game is over if one player scores up to 10 points. The game is resetted afterwards.
 

VGA port

 
VGA port uses basically five wires. Three of them are used to describe the colour (red, green, blue / RGB) and two wires are used to synchronize the electron beam in the monitor (Horizontal synchronization and vertical synchronisation / H-Synch, V-Synch). The electron beam is moving line by line from upper left to lower right and is displaying the pixels on the screen. The colour is changed in parallel to the movement. The current pixel's colour can be changed due simultaneously altering the colour signals.
The colours are limited to 8bit on the Nexys2 board. There are three bits for red, three bits for green and two bits for blue.
 
Figure: VGA port and connections on the Nexys2 board (Source: Nexys2 Reference Manual)
 
H-Synch is used for the end of the line and V-Synch for the end of the whole frame.
 
Figure: Electron beam and synchronization (Source: Nexys2 Reference Manual)
 
The timing tables from the manual can be used to synchronize the clock signals for the different timing signals.
 
Figure: Timing table (Source: Nexys2 Reference Manual)
 
The Nexys2 reference manual holds an example of a VGA driver as well.
 
Figure: Block diagram of a VGA driver (Source: Nexys2 Reference Manual)

Realization
 
Figure: Top-Schematic of the pong project
 

VGA driver (VGADriver)

 
I decided to use an open source VGA driver. This component was written by some student of „Department of Electrical and Computer Engineering“ from of university of Alberta (Source: [http://www.ece.ualberta.ca/~elliott/ee552/studentAppNotes/1998_w/Altera_UP1_Board_Map/vga.html ualberta.ca]).
 
Figure: Function block of the VGA driver
 

Functionality

 
The VGA driver holds four inputs, five outputs (VGA signals: R, G, B, H-Synch, V-Synch) and two vectors as outputs. It's powered by a clock signal of 25MHz. The timing table is integrated into this component and is used to generate the timed signals H-Synch and V-Synch. The VGA driver is counting column and row (x/y position) of the current pixel and is outputting this position via the output vectors. The colour inputs are directly connected to the colour outputs. An external logic is needed to set the right colour to the right time.
Library IEEE;
use IEEE.STD_Logic_1164.all;
use ieee.std_logic_arith.all;
use ieee.std_logic_unsigned.all;

entity VGAdrive is
  port( clock            : in std_logic;  -- 25Mhz clock
        red, green, blue : in std_logic;  -- input values for RGB signals
        row, column : out std_logic_vector(9 downto 0); -- for current pixel
        Rout, Gout, Bout, H, V : out std_logic); -- VGA drive signals
  -- The signals Rout, Gout, Bout, H and V are output to the monitor.
  -- The row and column outputs are used to know when to assert red,
  -- green and blue to color the current pixel.  For VGA, the column
  -- values that are valid are from 0 to 639, all other values should
  -- be ignored.  The row values that are valid are from 0 to 479 and
  -- again, all other values are ignored.  To turn on a pixel on the
  -- VGA monitor, some combination of red, green and blue should be
  -- asserted before the rising edge of the clock.  Objects which are
  -- displayed on the monitor, assert their combination of red, green and
  -- blue when they detect the row and column values are within their
  -- range.  For multiple objects sharing a screen, they must be combined
  -- using logic to create single red, green, and blue signals.
end;

architecture behaviour1 of VGAdrive is
  -- names are referenced from Altera University Program Design
  -- Laboratory Package  November 1997, ver. 1.1  User Guide Supplement
  -- clock period = 39.72 ns; the constants are integer multiples of the
  -- clock frequency and are close but not exact
  -- row counter will go from 0 to 524; column counter from 0 to 799
  subtype counter is std_logic_vector(9 downto 0);
  constant B : natural := 93;  -- T_pw - horizontal blank: 3.77 us
  constant C : natural := 45;  -- T_bp - front guard: 1.89 us
  constant D : natural := 640; -- T_disp - horizontal columns: 25.17 us
  constant E : natural := 22;  -- T_fp - rear guard: 0.94 us
  constant A : natural := B + C + D + E;  -- one horizontal sync cycle: 31.77 us
  constant P : natural := 2;   -- T_pw - vertical blank: 64 us
  constant Q : natural := 32;  -- T_bp - front guard: 1.02 ms
  constant R : natural := 480; -- T_disp - vertical rows: 15.25 ms
  constant S : natural := 11;  -- T_fp - rear guard: 0.35 ms
  constant O : natural := P + Q + R + S;  -- one vertical sync cycle: 16.6 ms
   
begin

  Rout <= red;
  Gout <= green;
  Bout <= blue;

  process
    variable vertical, horizontal : counter;  -- define counters
  begin
    wait until clock = '1';

  -- increment counters
      if  horizontal < A - 1  then
        horizontal := horizontal + 1;
      else
        horizontal := (others => '0');

        if  vertical < O - 1  then -- less than oh
          vertical := vertical + 1;
        else
          vertical := (others => '0');       -- is set to zero
        end if;
      end if;

  -- define H pulse
      if  horizontal >= (D + E)  and  horizontal < (D + E + B)  then
        H <= '0';
      else
        H <= '1';
      end if;

  -- define V pulse
      if  vertical >= (R + S)  and  vertical < (R + S + P)  then
        V <= '0';
      else
        V <= '1';
      end if;

    -- mapping of the variable to the signals
     -- negative signs are because the conversion bits are reversed
    row <= vertical;
    column <= horizontal;

  end process;

end architecture;
 

Clock divider (Clk_div_2)

 
The VGA driver needs a clock signal of 25MHz, which is realized with a 2:1 divider - the board clock with 50MHz is used.
library IEEE;
use IEEE.STD_LOGIC_1164.ALL;

entity Clk_div_2 is
    Port ( Clk_in : in  STD_LOGIC;
           Clk_out : inout  STD_LOGIC);
end Clk_div_2;

architecture Behavioral of Clk_div_2 is

begin

process (Clk_in)
begin
  if Clk_in'event and Clk_in='1' then
    if (Clk_out = '0') then
      Clk_out <= '1';
    else
      Clk_out <= '0';
    end if;
  end if;
end process;

end Behavioral;

 

Game logic and graphics (vgatest)

 
The VGA driver is used to display the game on a monitor via the VGA port of the Nexys2 board. It's important to set the right colour values at the right time/pixel to display the correct frame on the screen. The positioning of the objects is done with their origin coordinates, which might change during the game. Each frame is display separately on the screen. If the position of the objects is changed a bit from frame to frame, the human eye realizes a moving object
 
Figure: Main program
 
This module expects a 25MHz clock signal and the control buttons as inputs and outputs the VGA signals.
The component vgatest consists of function blocks, which are used for the graphical output, the game physics and the VGA port control.
The VGA port control is done by the VGA driver, which structure is descript in this component.
component vgadrive is
  port( clock          : in std_logic;  -- 25.175 Mhz clock
   red, green, blue : in std_logic;  -- input values for RGB signals
   row, column      : out std_logic_vector(9 downto 0); -- for current pixel
   Rout, Gout, Bout, H, V : out std_logic); -- VGA drive signals
end component;
VGA : component vgadrive -- Einbindung des VGA Drivers
  port map ( clock => clock, red => red, green => green, blue => blue,
               row => row, column => column,
               Rout => R, Gout => G, Bout => B, H => H, V => V);

Graphics and game physics are controlled by parallel processes.

Graphics

 
This process is responsible for the correct colour signals for the current pixel. The origins and dimensions of all objects are considered. The position of the moving objects is described by variables (Internal signal in the architecture of the module) which can be altered during the game.
signal row, column : std_logic_vector(9 downto 0); -- X,Y-Position aktuellen Bildpixels
signal red, green, blue : std_logic := '0'; -- Farbsteuerung
-- Balkenposition (Y) für Spieler links und rechts
signal beam_pos_left, beam_pos_right: integer range 0 to 650 := 0;
-- Ballkoordinaten
signal ball_pos_x, ball_pos_y: integer range 0 to 650 := 0;
-- Spielstand für links und rechts
signal s_l, s_r : integer range 0 to 10 := 0;


Process:

RGB : process(row, column) -- VGA Ausgabe
  variable rgb : std_logic_vector (2 downto 0) := "000"; -- rot, grün, blau für VGADriver
 begin
  -- linker Balken (15x70 Pixel)
  if (row > beam_pos_left and row < (beam_pos_left + 70) and column > 0 and column < 15)  then
  rgb := "100"; -- Farbe: rot 1, grün 0, blau, 0
  else
  -- rechter Balken (15x70 Pixel)
   if (row > beam_pos_right and row < (beam_pos_right + 70) and column > 624 and column < 639) then
   rgb := "001";
   else
  -- Mittellinie (6 Pixel breit)
    if (row > 0 and row < 479 and column > 317 and column < 323)  then
    rgb := "111";
    else
  -- linker Punktestand - Seg A --
     if ((row > 20 and row < 45 and column > 275 and column < 280) and (s_l = 0 or s_l = 1 or s_l = 2 or s_l = 3 or s_l = 4 or s_l = 7 or s_l = 8 or s_l = 9))  then
     rgb := "100";
     else
  -- linker Punktestand - Seg B --
     if ((row > 50 and row < 75 and column > 275 and column < 280) and (s_l = 0 or s_l = 1 or s_l = 5 or s_l = 6 or s_l = 3 or s_l = 4 or s_l = 7 or s_l = 8 or s_l = 9)) then
     rgb := "100";
     else
  -- linker Punktestand - Seg C --
     if ((row > 75 and row < 80 and column > 250 and column < 275) and (s_l = 0 or s_l = 2 or s_l = 3 or s_l = 5 or s_l = 6 or s_l = 8 or s_l = 9))  then
     rgb := "100";
     else
  -- linker Punktestand - Seg D --
     if ((row > 50 and row < 75 and column > 245 and column < 250) and (s_l = 0 or s_l = 2 or s_l = 6 or s_l = 8))  then
     rgb := "100";
     else
  -- linker Punktestand - Seg E --
     if ((row > 20 and row < 45 and column > 245 and column < 250) and (s_l = 0 or s_l = 4 or s_l = 5 or s_l = 6 or s_l = 8 or s_l = 9))  then
     rgb := "100";
     else
  -- linker Punktestand - Seg F --
     if ((row > 15 and row < 20 and column > 250 and column < 275) and (s_l = 0 or s_l = 2 or s_l = 3 or s_l = 5 or s_l = 6 or s_l = 7 or s_l = 8 or s_l = 9))  then
     rgb := "100";
     else
  -- linker Punktestand - Seg G --
     if ((row > 45 and row < 50 and column > 250 and column < 275) and (s_l = 2 or s_l = 3 or s_l = 4 or s_l = 5 or s_l = 6 or s_l = 8 or s_l = 9))  then
     rgb := "100";
     else
   -- rechter Punktestand - Seg A --
      if ((row > 20 and row < 45 and column > 390 and column < 395) and (s_r = 0 or s_r = 1 or s_r = 2 or s_r = 3 or s_r = 4 or s_r = 7 or s_r = 8 or s_r = 9))  then
      rgb := "001";
      else
   -- rechter Punktestand - Seg B --
      if ((row > 50 and row < 75 and column > 390 and column < 395) and (s_r = 0 or s_r = 1 or s_r = 5 or s_r = 6 or s_r = 3 or s_r = 4 or s_r = 7 or s_r = 8 or s_r = 9))  then
      rgb := "001";
      else
   -- rechter Punktestand - Seg C --
      if ((row > 75 and row < 80 and column > 365 and column < 390) and (s_r = 0 or s_r = 2 or s_r = 3 or s_r = 5 or s_r = 6 or s_r = 8 or s_r = 9))  then
      rgb := "001";
      else
   -- rechter Punktestand - Seg D --
      if ((row > 50 and row < 75 and column > 360 and column < 365) and (s_r = 0 or s_r = 2 or s_r = 6 or s_r = 8))  then
      rgb := "001";
      else
   -- rechter Punktestand - Seg E --
      if ((row > 20 and row < 45 and column > 360 and column < 365) and (s_r = 0 or s_r = 4 or s_r = 5 or s_r = 6 or s_r = 8 or s_r = 9))  then
      rgb := "001";
      else
   -- rechter Punktestand - Seg F --
      if ((row > 15 and row < 20 and column > 365 and column < 390) and (s_r = 0 or s_r = 2 or s_r = 3 or s_r = 5 or s_r = 6 or s_r = 7 or s_r = 8 or s_r = 9))  then
      rgb := "001";
      else
   -- rechter Punktestand - Seg G --
      if ((row > 45 and row < 50 and column > 365 and column < 390) and (s_r = 2 or s_r = 3 or s_r = 4 or s_r = 5 or s_r = 6 or s_r = 8 or s_r = 9))  then
      rgb := "001";
      else
      -- Ball (15x15 Pixel)
       if (row > ball_pos_y and row < (ball_pos_y + 15) and column > ball_pos_x and column < (ball_pos_x + 15))  then
       rgb := "111";
       else
       rgb := "000";
       end if; 
      end if; 
      end if;
      end if;
      end if;
      end if;
      end if;
      end if; 
     end if;
     end if;
     end if;
     end if;
     end if;
     end if;
     end if;     
    end if;
   end if;
  end if;
  -- Ausgabe der rgb Werte
  red <= rgb(2);
  green <= rgb(1);
  blue <= rgb(0);
 end process;
 

Game physics 

 
This process is responsible for the movement of objects, for the score board and for processing the user's input.

The moving vector for the ball is generated by a separate procedure.
-- Procedure zur zufälligen Ermittlung der Ballbewegung (in rand: Zufallszahl, out x,y: Vektor)
 procedure rand_vec(rand : in integer range 0 to 28 := 0; x,y : out integer range -3 to 3) is
 begin
  case (rand) is 
   when 0 =>
    x := -1;
    y := -1;
   when 1 =>
    x := 1;
    y := -1;
   when 2 =>
    x := 1;
    y := 1;
   when 3 =>
    x := -1;
    y := 1;
   when 4 =>
    x := -2;
    y := -1;
   when 5 =>
    x := -1;
    y := -2;
   when 6 =>
    x := 1;
    y := -2;
   when 7 =>
    x := 2;
    y := -1;
   when 8 =>
    x := 2;
    y := 1;
   when 9 =>
    x := 1;
    y := 2;
   when 10 =>
    x := -1;
    y := 2;
   when 11 =>
    x := -2;
    y := 1;
   when 12 =>
    x := -3;
    y := -1;
   when 13 =>
    x := -1;
    y := -3;
   when 14 =>
    x := 1;
    y := -3;
   when 15 =>
    x := 3;
    y := -1;
   when 16 =>
    x := 3;
    y := 1;
   when 17 =>
    x := 1;
    y := 3;
   when 18 =>
    x := -1;
    y := 3;
   when 19 =>
    x := -3;
    y := 1;
   when 20 =>
    x := -3;
    y := -2;
   when 21 =>
    x := -2;
    y := -3;
   when 22 =>
    x := 2;
    y := -3;
   when 23 =>
    x := 3;
    y := -2;
   when 24 =>
    x := 3;
    y := 2;
   when 25 =>
    x := 2;
    y := 3;
   when 26 =>
    x := -2;
    y := 3;
   when 27 =>
    x := -3;
    y := 2;
   when others =>
    x := -1;
    y := -1;
  end case;   
 end procedure rand_vec;

Process:

control: process(clock, start) -- Spielphysik und Steuerung
  variable random : integer range 0 to 28 := 0; -- Zufallszahl
  variable dir_x, dir_y : integer range -3 to 3; -- Bewegungsvektor des Balls
  -- Zählvariablen für Zeitteilung vom Clocksignal
  variable cnt_ball, cnt_balken, cnt_ball_mod : integer range 0 to 101 := 0;
  variable cnt : integer range 0 to 2002 := 0;
 begin
  if (clock'event and clock = '1') then
   if (cnt /= 2000) then -- Runterskalierung des Clocksignals
    cnt := cnt +1;
   else
    cnt := 0;
    cnt_ball := cnt_ball + 1; -- weiter Skalierung des Takts
    cnt_balken := cnt_balken + 1; -- weiter Skalierung des Takts
    random  := random  + 1; -- Zufallszahl wird durchgezählt
    if (random = 28) then -- Zurücksetzten der Zufallszahl (0..27)
     random := 0;
    end if;
    if (cnt_ball = 101) then --Zurücksetzten des Zählers
     cnt_ball := 0;
    end if;
    if (cnt_balken = 76) then --Zurücksetzten des Zählers
     cnt_balken := 0;
    end if;
    -- Resetverhalten
    if (start = '0') then 
     ball_pos_x <= 320; -- Ball wird in die Mitte positioniert
     ball_pos_y <= 240;
     s_l <= 0; -- Sore zurücksetzen
     s_r <= 0;
     cnt_ball_mod := 100; -- Ballgeschwindigkeit auf langsam
     -- Auruf der Zufallsfunktion um zufälligen Bewegungsverktor zu erhalten
     rand_vec(rand => random, x => dir_x, y => dir_y);
    else
     -- Spielstart
     -- Ballbewegung
     if (cnt_ball >= cnt_ball_mod) then
      ball_pos_y <= ball_pos_y + dir_y; -- Ballbewegung Y
      ball_pos_x <= ball_pos_x + dir_x; -- Ballbewegung X
      cnt_ball := 0; -- Counter zurücksetzten
      -- oberer Rand detektiert
      if (ball_pos_y <= 3) then -- Abfragung vorher, weil Bewegung >1 sein kann
       dir_y := (-1*dir_y); -- "Abprallen" - Y Vektor negiert
       ball_pos_y <= 4;
      end if;
      -- unterer Rand detektiert
      if (ball_pos_y >= (479-15)) then -- Abfragung vorher, weil Bewegung >1 sein kann
       dir_y := (-1*dir_y); -- "Abprallen" - Y Vektor negiert
       ball_pos_y <= 479-16;
      end if;
      -- Ball trift auf Balken (rechts)
      if (ball_pos_x >= (623-15) and dir_x > 0 and ball_pos_y > (beam_pos_right - 14) and ball_pos_y < (beam_pos_right + 70 + 14)) then
       dir_x := (-1*dir_x); -- "Abprallen" - X Vektor negiert
       if (cnt_ball_mod >= 5) then -- Erhöhung der Ballgeschwindigkeit
        cnt_ball_mod := cnt_ball_mod - 3;
       end if;
      end if;
      -- Ball trift auf Balken (links)
      if (ball_pos_x <= 16 and dir_x < 0 and ball_pos_y > (beam_pos_left - 14) and ball_pos_y < (beam_pos_left + 70 + 14)) then
       dir_x := (-1*dir_x); -- "Abprallen" - X Vektor negiert
       if (cnt_ball_mod >= 5) then -- Erhöhung der Ballgeschwindigkeit
        cnt_ball_mod := cnt_ball_mod - 3;
       end if;
      end if;
      -- Ball trift auch rechten Rand (linker Spieler punktet)
      if (ball_pos_x >= (639-15-10)) then
       ball_pos_x <= 320; -- Ball in die Mitte
       ball_pos_y <= 240; -- Ball in die Mitte
       s_l <= s_l + 1; -- Punkte erhöhen
       cnt_ball_mod := 100; -- Ballgeschwindigkeit wieder auf langsam
       -- Zufallsvektor für Ballbewegung bestimmen
       rand_vec(rand => random, x => dir_x, y => dir_y);
      end if; 
      -- Ball trift auch linkten Rand (rechter Spieler punktet)
      if (ball_pos_x <= 10) then
       ball_pos_x <= 320; -- Ball in die Mitte
       ball_pos_y <= 240; -- Ball in die Mitte
       s_r <= s_r + 1; -- Punkte erhöhen
       cnt_ball_mod := 100; -- Ballgeschwindigkeit wieder auf langsam
       -- Zufallsvektor für Ballbewegung bestimmen
       rand_vec(rand => random, x => dir_x, y => dir_y);
      end if;
     end if;
     -- Score wird zurückgesetzt wenn ein Spieler gewinnt
     if (s_l = 10) or (s_r = 10) then
      s_l <= 0;
      s_r <= 0;
     end if;
     -- Bewegung der Balken bei Tastendruck, Geschwindigkeit durch cnt_balken
     -- einstellbar. Randpositionen werden berücksichtigt.
     if (cnt_balken >= 75) then
      if (left_up = '1' and left_down = '0' and beam_pos_left > 1) then
       beam_pos_left <= beam_pos_left - 2; -- linker Balken - aufwärts
      end if;
      if (left_up = '0' and left_down = '1' and beam_pos_left < 408) then
       beam_pos_left <= beam_pos_left + 2; -- linker Balken - abwärts
      end if;
      if (right_up = '1' and right_down = '0' and beam_pos_right > 1) then
       beam_pos_right <= beam_pos_right - 2; -- rehter Balken - aufwärts
      end if;
      if (right_up = '0' and right_down = '1' and beam_pos_right < 408) then
       beam_pos_right <= beam_pos_right + 2; -- rechter Balken - abwärts
      end if;
     end if;
    end if;
   end if;
   end if;
 end process;
 

Complete source code 

Library IEEE;
use IEEE.STD_Logic_1164.all;
use ieee.std_logic_arith.all;
use ieee.std_logic_unsigned.all;
entity vgatest is
 port(clock, left_up, left_down, right_up, right_down, start : in std_logic;
       R, G, B, H, V : out std_logic);
end entity;

architecture test of vgatest is

 component vgadrive is
  port( clock          : in std_logic;  -- 25.175 Mhz clock
   red, green, blue : in std_logic;  -- input values for RGB signals
   row, column      : out std_logic_vector(9 downto 0); -- for current pixel
   Rout, Gout, Bout, H, V : out std_logic); -- VGA drive signals
 end component;
 -- Procedure zur zufälligen Ermittlung der Ballbewegung (in rand: Zufallszahl, out x,y: Vektor)
 procedure rand_vec(rand : in integer range 0 to 28 := 0; x,y : out integer range -3 to 3) is
 begin
  case (rand) is 
   when 0 =>
    x := -1;
    y := -1;
   when 1 =>
    x := 1;
    y := -1;
   when 2 =>
    x := 1;
    y := 1;
   when 3 =>
    x := -1;
    y := 1;
   when 4 =>
    x := -2;
    y := -1;
   when 5 =>
    x := -1;
    y := -2;
   when 6 =>
    x := 1;
    y := -2;
   when 7 =>
    x := 2;
    y := -1;
   when 8 =>
    x := 2;
    y := 1;
   when 9 =>
    x := 1;
    y := 2;
   when 10 =>
    x := -1;
    y := 2;
   when 11 =>
    x := -2;
    y := 1;
   when 12 =>
    x := -3;
    y := -1;
   when 13 =>
    x := -1;
    y := -3;
   when 14 =>
    x := 1;
    y := -3;
   when 15 =>
    x := 3;
    y := -1;
   when 16 =>
    x := 3;
    y := 1;
   when 17 =>
    x := 1;
    y := 3;
   when 18 =>
    x := -1;
    y := 3;
   when 19 =>
    x := -3;
    y := 1;
   when 20 =>
    x := -3;
    y := -2;
   when 21 =>
    x := -2;
    y := -3;
   when 22 =>
    x := 2;
    y := -3;
   when 23 =>
    x := 3;
    y := -2;
   when 24 =>
    x := 3;
    y := 2;
   when 25 =>
    x := 2;
    y := 3;
   when 26 =>
    x := -2;
    y := 3;
   when 27 =>
    x := -3;
    y := 2;
   when others =>
    x := -1;
    y := -1;
  end case;   
 end procedure rand_vec;
 
 signal row, column : std_logic_vector(9 downto 0); -- X,Y-Position aktuellen Bildpixels
 signal red, green, blue : std_logic := '0'; -- Farbsteuerung
 -- Balkenposition (Y) für Spieler links und rechts
 signal beam_pos_left, beam_pos_right: integer range 0 to 650 := 0;
 -- Ballkoordinaten
 signal ball_pos_x, ball_pos_y: integer range 0 to 650 := 0;
 -- Spielstand für links und rechts
 signal s_l, s_r : integer range 0 to 10 := 0;
begin
 VGA : component vgadrive -- Einbindung des VGA Drivers
  port map ( clock => clock, red => red, green => green, blue => blue,
               row => row, column => column,
               Rout => R, Gout => G, Bout => B, H => H, V => V);
 
 control: process(clock, start) -- Spielphysik und Steuerung
  variable random : integer range 0 to 28 := 0; -- Zufallszahl
  variable dir_x, dir_y : integer range -3 to 3; -- Bewegungsvektor des Balls
  -- Zählvariablen für Zeitteilung vom Clocksignal
  variable cnt_ball, cnt_balken, cnt_ball_mod : integer range 0 to 101 := 0;
  variable cnt : integer range 0 to 2002 := 0;
 begin
  if (clock'event and clock = '1') then
   if (cnt /= 2000) then -- Runterskalierung des Clocksignals
    cnt := cnt +1;
   else
    cnt := 0;
    cnt_ball := cnt_ball + 1; -- weiter Skalierung des Takts
    cnt_balken := cnt_balken + 1; -- weiter Skalierung des Takts
    random  := random  + 1; -- Zufallszahl wird durchgezählt
    if (random = 28) then -- Zurücksetzten der Zufallszahl (0..27)
     random := 0;
    end if;
    if (cnt_ball = 101) then --Zurücksetzten des Zählers
     cnt_ball := 0;
    end if;
    if (cnt_balken = 76) then --Zurücksetzten des Zählers
     cnt_balken := 0;
    end if;
    -- Resetverhalten
    if (start = '0') then 
     ball_pos_x <= 320; -- Ball wird in die Mitte positioniert
     ball_pos_y <= 240;
     s_l <= 0; -- Sore zurücksetzen
     s_r <= 0;
     cnt_ball_mod := 100; -- Ballgeschwindigkeit auf langsam
     -- Auruf der Zufallsfunktion um zufälligen Bewegungsverktor zu erhalten
     rand_vec(rand => random, x => dir_x, y => dir_y);
    else
     -- Spielstart
     -- Ballbewegung
     if (cnt_ball >= cnt_ball_mod) then
      ball_pos_y <= ball_pos_y + dir_y; -- Ballbewegung Y
      ball_pos_x <= ball_pos_x + dir_x; -- Ballbewegung X
      cnt_ball := 0; -- Counter zurücksetzten
      -- oberer Rand detektiert
      if (ball_pos_y <= 3) then -- Abfragung vorher, weil Bewegung >1 sein kann
       dir_y := (-1*dir_y); -- "Abprallen" - Y Vektor negiert
       ball_pos_y <= 4;
      end if;
      -- unterer Rand detektiert
      if (ball_pos_y >= (479-15)) then -- Abfragung vorher, weil Bewegung >1 sein kann
       dir_y := (-1*dir_y); -- "Abprallen" - Y Vektor negiert
       ball_pos_y <= 479-16;
      end if;
      -- Ball trift auf Balken (rechts)
      if (ball_pos_x >= (623-15) and dir_x > 0 and ball_pos_y > (beam_pos_right - 14) and ball_pos_y < (beam_pos_right + 70 + 14)) then
       dir_x := (-1*dir_x); -- "Abprallen" - X Vektor negiert
       if (cnt_ball_mod >= 5) then -- Erhöhung der Ballgeschwindigkeit
        cnt_ball_mod := cnt_ball_mod - 3;
       end if;
      end if;
      -- Ball trift auf Balken (links)
      if (ball_pos_x <= 16 and dir_x < 0 and ball_pos_y > (beam_pos_left - 14) and ball_pos_y < (beam_pos_left + 70 + 14)) then
       dir_x := (-1*dir_x); -- "Abprallen" - X Vektor negiert
       if (cnt_ball_mod >= 5) then -- Erhöhung der Ballgeschwindigkeit
        cnt_ball_mod := cnt_ball_mod - 3;
       end if;
      end if;
      -- Ball trift auch rechten Rand (linker Spieler punktet)
      if (ball_pos_x >= (639-15-10)) then
       ball_pos_x <= 320; -- Ball in die Mitte
       ball_pos_y <= 240; -- Ball in die Mitte
       s_l <= s_l + 1; -- Punkte erhöhen
       cnt_ball_mod := 100; -- Ballgeschwindigkeit wieder auf langsam
       -- Zufallsvektor für Ballbewegung bestimmen
       rand_vec(rand => random, x => dir_x, y => dir_y);
      end if; 
      -- Ball trift auch linkten Rand (rechter Spieler punktet)
      if (ball_pos_x <= 10) then
       ball_pos_x <= 320; -- Ball in die Mitte
       ball_pos_y <= 240; -- Ball in die Mitte
       s_r <= s_r + 1; -- Punkte erhöhen
       cnt_ball_mod := 100; -- Ballgeschwindigkeit wieder auf langsam
       -- Zufallsvektor für Ballbewegung bestimmen
       rand_vec(rand => random, x => dir_x, y => dir_y);
      end if;
     end if;
     -- Score wird zurückgesetzt wenn ein Spieler gewinnt
     if (s_l = 10) or (s_r = 10) then
      s_l <= 0;
      s_r <= 0;
     end if;
     -- Bewegung der Balken bei Tastendruck, Geschwindigkeit durch cnt_balken
     -- einstellbar. Randpositionen werden berücksichtigt.
     if (cnt_balken >= 75) then
      if (left_up = '1' and left_down = '0' and beam_pos_left > 1) then
       beam_pos_left <= beam_pos_left - 2; -- linker Balken - aufwärts
      end if;
      if (left_up = '0' and left_down = '1' and beam_pos_left < 408) then
       beam_pos_left <= beam_pos_left + 2; -- linker Balken - abwärts
      end if;
      if (right_up = '1' and right_down = '0' and beam_pos_right > 1) then
       beam_pos_right <= beam_pos_right - 2; -- rehter Balken - aufwärts
      end if;
      if (right_up = '0' and right_down = '1' and beam_pos_right < 408) then
       beam_pos_right <= beam_pos_right + 2; -- rechter Balken - abwärts
      end if;
     end if;
    end if;
   end if;
   end if;
 end process;
 
 RGB : process(row, column) -- VGA Ausgabe
  variable rgb : std_logic_vector (2 downto 0) := "000"; -- rot, grün, blau für VGADriver
 begin
  -- linker Balken (15x70 Pixel)
  if (row > beam_pos_left and row < (beam_pos_left + 70) and column > 0 and column < 15)  then
  rgb := "100"; -- Farbe: rot 1, grün 0, blau, 0
  else
  -- rechter Balken (15x70 Pixel)
   if (row > beam_pos_right and row < (beam_pos_right + 70) and column > 624 and column < 639) then
   rgb := "001";
   else
  -- Mittellinie (6 Pixel breit)
    if (row > 0 and row < 479 and column > 317 and column < 323)  then
    rgb := "111";
    else
  -- linker Punktestand - Seg A --
     if ((row > 20 and row < 45 and column > 275 and column < 280) and (s_l = 0 or s_l = 1 or s_l = 2 or s_l = 3 or s_l = 4 or s_l = 7 or s_l = 8 or s_l = 9))  then
     rgb := "100";
     else
  -- linker Punktestand - Seg B --
     if ((row > 50 and row < 75 and column > 275 and column < 280) and (s_l = 0 or s_l = 1 or s_l = 5 or s_l = 6 or s_l = 3 or s_l = 4 or s_l = 7 or s_l = 8 or s_l = 9)) then
     rgb := "100";
     else
  -- linker Punktestand - Seg C --
     if ((row > 75 and row < 80 and column > 250 and column < 275) and (s_l = 0 or s_l = 2 or s_l = 3 or s_l = 5 or s_l = 6 or s_l = 8 or s_l = 9))  then
     rgb := "100";
     else
  -- linker Punktestand - Seg D --
     if ((row > 50 and row < 75 and column > 245 and column < 250) and (s_l = 0 or s_l = 2 or s_l = 6 or s_l = 8))  then
     rgb := "100";
     else
  -- linker Punktestand - Seg E --
     if ((row > 20 and row < 45 and column > 245 and column < 250) and (s_l = 0 or s_l = 4 or s_l = 5 or s_l = 6 or s_l = 8 or s_l = 9))  then
     rgb := "100";
     else
  -- linker Punktestand - Seg F --
     if ((row > 15 and row < 20 and column > 250 and column < 275) and (s_l = 0 or s_l = 2 or s_l = 3 or s_l = 5 or s_l = 6 or s_l = 7 or s_l = 8 or s_l = 9))  then
     rgb := "100";
     else
  -- linker Punktestand - Seg G --
     if ((row > 45 and row < 50 and column > 250 and column < 275) and (s_l = 2 or s_l = 3 or s_l = 4 or s_l = 5 or s_l = 6 or s_l = 8 or s_l = 9))  then
     rgb := "100";
     else
   -- rechter Punktestand - Seg A --
      if ((row > 20 and row < 45 and column > 390 and column < 395) and (s_r = 0 or s_r = 1 or s_r = 2 or s_r = 3 or s_r = 4 or s_r = 7 or s_r = 8 or s_r = 9))  then
      rgb := "001";
      else
   -- rechter Punktestand - Seg B --
      if ((row > 50 and row < 75 and column > 390 and column < 395) and (s_r = 0 or s_r = 1 or s_r = 5 or s_r = 6 or s_r = 3 or s_r = 4 or s_r = 7 or s_r = 8 or s_r = 9))  then
      rgb := "001";
      else
   -- rechter Punktestand - Seg C --
      if ((row > 75 and row < 80 and column > 365 and column < 390) and (s_r = 0 or s_r = 2 or s_r = 3 or s_r = 5 or s_r = 6 or s_r = 8 or s_r = 9))  then
      rgb := "001";
      else
   -- rechter Punktestand - Seg D --
      if ((row > 50 and row < 75 and column > 360 and column < 365) and (s_r = 0 or s_r = 2 or s_r = 6 or s_r = 8))  then
      rgb := "001";
      else
   -- rechter Punktestand - Seg E --
      if ((row > 20 and row < 45 and column > 360 and column < 365) and (s_r = 0 or s_r = 4 or s_r = 5 or s_r = 6 or s_r = 8 or s_r = 9))  then
      rgb := "001";
      else
   -- rechter Punktestand - Seg F --
      if ((row > 15 and row < 20 and column > 365 and column < 390) and (s_r = 0 or s_r = 2 or s_r = 3 or s_r = 5 or s_r = 6 or s_r = 7 or s_r = 8 or s_r = 9))  then
      rgb := "001";
      else
   -- rechter Punktestand - Seg G --
      if ((row > 45 and row < 50 and column > 365 and column < 390) and (s_r = 2 or s_r = 3 or s_r = 4 or s_r = 5 or s_r = 6 or s_r = 8 or s_r = 9))  then
      rgb := "001";
      else
      -- Ball (15x15 Pixel)
       if (row > ball_pos_y and row < (ball_pos_y + 15) and column > ball_pos_x and column < (ball_pos_x + 15))  then
       rgb := "111";
       else
       rgb := "000";
       end if; 
      end if; 
      end if;
      end if;
      end if;
      end if;
      end if;
      end if; 
     end if;
     end if;
     end if;
     end if;
     end if;
     end if;
     end if;     
    end if;
   end if;
  end if;
  -- Ausgabe der rgb Werte
  red <= rgb(2);
  green <= rgb(1);
  blue <= rgb(0);
 end process;
end architecture;
 

Comments



Sources

ċ
EDA_Miniprojekt.zip
(658k)
Alexander Wegner,
22 Jul 2011, 03:09