PONG game in VHDL and Verilog on the Go Board

Recreate the high-tech of the 70’s

Welcome to the last Go Board tutorial. It’s been a long climb, but this project represents the culmination of all of your hard work. And what better way to end everything than by recreating PONG? I assume you know how Pong works. Two paddles on either side of the screen, a ball in the middle, and all the madness that black and white graphics has to offer. You try to hit the ball with your paddle to keep it bouncing back toward your opponent. If you miss the ball, you opponent scores, if your opponent misses, you score.

Should you prefer to follow along with a YouTube video, well you can do that too! Please subscribe to my channel.

This project is going to use the push-buttons on the Go Board to control the paddles. Player 1 is going to use Switches 1 and 2, Player 2 is going to use Switches 3 and 4. This does require getting your fingers pretty close together, so play against someone you don’t mind brushing fingers with. Let’s jump right in and look at the Block Diagram of the code to show how this project is going to be organized. I’ll go through each of these blocks individually and talk about them briefly. Note that all of the blocks in GREEN are reused verbatim from previous Go Board Projects. Neat how all of this code builds on itself huh? It’s almost like I planned all of this.

Picture of Pong Project for Go Board

Go Board – PONG Project Block Diagram

Overview of Pong Game:

The way I decided to implement this game is by creating little functional blocks for each of the paddles and the ball itself. The Pong Board I set to be 40×30 pixels. I chose these because 40×30 is equal to 640×480 divided by 16 (640/16=40, 480/16=30). One thing about FPGAs, it’s really hard to do division inside of an FPGA unless you are dividing by a power of 2. When dividing by a power of 2, simply drop the number of least significant bits that are required to represent the divisor. For our example, we are dividing by 16, which takes 4 bits to represent. So if we drop the least significant 4 bits off of the Row and Column indexes then we have divided them by 16.

Base-2 multiplying and dividing is done constantly inside of an FPGA, so it’s important to understand. If you want to multiply a number by 2, simply add on a single bit on the right of the number. To multiply by 8, add on 3 bits (set to zero). Remove those bits to divide.

Each of the Paddle and Ball modules keeps track of their single component based on the current Row/Col index of the frame. If the current pixel is equal to the location of the paddle or the ball, those modules will drive their output high. This tells the Pong Top to draw white on that pixel. See below for a complete description of each module.

UART RX:

There needs to be a way to kick-off a new Pong Game, to tell the Go Board to start moving the ball. It would have been easier if there were five push-button switches on the Go Board, so that one of them could be use to start the game, but sadly there isn’t. You could have chosen to use one of the other buttons to start a new game, but I just decided to reuse the UART RX module from a previous project. Any time the UART RX asserts its Data Valid (DV) pulse, then the game starts.

Sync Pulse Generator, Sync To Count, VGA Sync Porch:

All three of these modules came directly from the previous project where we drove Test Patterns to a VGA monitor from the Go Board. To learn how each of those work go back and review that project, they are used as-is here.

Debounce Switches:

One thing’s for sure, we can’t use the raw switch inputs. We need to debounce those buttons. This code was already written in a previous project, so just lift that and instantiate it once for each of the four switch inputs.

P1/P2 Paddle Control:

Here’s the actual first new piece of code thus far. The Paddle Control logic is exactly the same for Player 1 and Player 2, the only way to know which is which is by setting a Generic in VHDL or a Parameter in Verilog. This module takes in the Current Row/Col that we are drawing on the screen. It knows where the paddle is located and keeps track of that if the player moves their paddle up or down. If the current active row/col index is equal to the location of the paddle, it will draw the paddle on the screen. The output o_Draw_Paddle when 1 will draw a pixel at the current active row/col location.

Ball Control:

This module keeps track of the ball location in Row/Col. The row/col that is being drawn on the VGA display is sent as an input to this module. If the current active pixel is the same pixel as where the ball is located, the output o_Draw_Ball becomes a 1. Otherwise it’s a 0.

Big Or Gate:

This isn’t its own module. The purpose of this Or Gate is to look at the outputs of P1 Paddle Control, P2 Paddle Control, and Ball Control. If any of them are telling the VGA display to draw at this Row/Col index, then it will allow the output to go high.

That’s it! Below is the code for each of these new modules

Your Future Improvements

There are a few things that you can change about Pong to make it better. Here are a few ideas:

  1. Keep Score Using the 7-Segment Displays (P1=Left, P2=Right)
  2. Add Ability to Change Colors of Pong Game, Paddles, Ball
  3. Add UART Control of Dynamic Color Selections
  4. Anything Else?

Go Board Conclusion

I sincerely hope that you have found your development using the Go Board to be fun and worthwhile. I have spend thousands of hours creating this website, designing the Go Board, and writing all of these tutorials. Please post to social media and tell others about this place, that’s the best way to help! If you have any feedback, either email me using the link on the bottom of this webpage or post to the comment section. And thank you so much for your support, I could not have done all of this without you and your positive encouragement!


Project10_Pong_Top.vhd:

-------------------------------------------------------------------------------
-- File Downloaded From http://www.nandland.com
-------------------------------------------------------------------------------
library ieee;
use ieee.std_logic_1164.all;

entity Project10_Pong_Top is
  port (
    -- Main Clock (25 MHz)
    i_Clk         : in std_logic;

    -- UART Data
    i_UART_RX : in  std_logic;

    -- Push Buttons
    i_Switch_1 : in std_logic;
    i_Switch_2 : in std_logic;
    i_Switch_3 : in std_logic;
    i_Switch_4 : in std_logic;
    
    -- VGA
    o_VGA_HSync : out std_logic;
    o_VGA_VSync : out std_logic;
    o_VGA_Red_0 : out std_logic;
    o_VGA_Red_1 : out std_logic;
    o_VGA_Red_2 : out std_logic;
    o_VGA_Grn_0 : out std_logic;
    o_VGA_Grn_1 : out std_logic;
    o_VGA_Grn_2 : out std_logic;
    o_VGA_Blu_0 : out std_logic;
    o_VGA_Blu_1 : out std_logic;
    o_VGA_Blu_2 : out std_logic
    );
end entity Project10_Pong_Top;

architecture RTL of Project10_Pong_Top is

  signal w_RX_DV : std_logic;
  
  -- VGA Constants to set Frame Size
  constant c_VIDEO_WIDTH : integer := 3;
  constant c_TOTAL_COLS  : integer := 800;
  constant c_TOTAL_ROWS  : integer := 525;
  constant c_ACTIVE_COLS : integer := 640;
  constant c_ACTIVE_ROWS : integer := 480;
  
  -- Common VGA Signals
  signal w_HSync_VGA       : std_logic;
  signal w_VSync_VGA       : std_logic;
  signal w_Red_Video_Porch : std_logic_vector(c_VIDEO_WIDTH-1 downto 0);
  signal w_Grn_Video_Porch : std_logic_vector(c_VIDEO_WIDTH-1 downto 0);
  signal w_Blu_Video_Porch : std_logic_vector(c_VIDEO_WIDTH-1 downto 0);

  signal w_Switch_1 : std_logic;
  signal w_Switch_2 : std_logic;
  signal w_Switch_3 : std_logic;
  signal w_Switch_4 : std_logic;
  
  -- Pong Signals
  signal w_HSync_Pong     : std_logic;
  signal w_VSync_Pong     : std_logic;
  signal w_Red_Video_Pong : std_logic_vector(c_VIDEO_WIDTH-1 downto 0);
  signal w_Grn_Video_Pong : std_logic_vector(c_VIDEO_WIDTH-1 downto 0);
  signal w_Blu_Video_Pong : std_logic_vector(c_VIDEO_WIDTH-1 downto 0);  
  
begin

  UART_RX_Inst : entity work.UART_RX
    generic map (
      g_CLKS_PER_BIT => 217)            -- 25,000,000 / 115,200
    port map (
      i_Clk       => i_Clk,
      i_RX_Serial => i_UART_RX,
      o_RX_DV     => w_RX_DV,
      o_RX_Byte   => open);


  VGA_Sync_Pulses_inst : entity work.VGA_Sync_Pulses
    generic map (
      g_TOTAL_COLS  => c_TOTAL_COLS,
      g_TOTAL_ROWS  => c_TOTAL_ROWS,
      g_ACTIVE_COLS => c_ACTIVE_COLS,
      g_ACTIVE_ROWS => c_ACTIVE_ROWS
      )
    port map (
      i_Clk       => i_Clk,
      o_HSync     => w_HSync_VGA,
      o_VSync     => w_VSync_VGA,
      o_Col_Count => open,
      o_Row_Count => open
      );

  Debounce_Switch_1: entity work.Debounce_Switch
    port map (
      i_Clk    => i_Clk,
      i_Switch => i_Switch_1,
      o_Switch => w_Switch_1);
  
  Debounce_Switch_2: entity work.Debounce_Switch
    port map (
      i_Clk    => i_Clk,
      i_Switch => i_Switch_2,
      o_Switch => w_Switch_2);
  
  Debounce_Switch_3: entity work.Debounce_Switch
    port map (
      i_Clk    => i_Clk,
      i_Switch => i_Switch_3,
      o_Switch => w_Switch_3);
  
  Debounce_Switch_4: entity work.Debounce_Switch
    port map (
      i_Clk    => i_Clk,
      i_Switch => i_Switch_4,
      o_Switch => w_Switch_4);
  
  Pong_Top_1: entity work.Pong_Top
    generic map (
      g_Video_Width => c_VIDEO_WIDTH,
      g_Total_Cols  => c_TOTAL_COLS, 
      g_Total_Rows  => c_TOTAL_ROWS, 
      g_Active_Cols => c_ACTIVE_COLS,
      g_Active_Rows => c_ACTIVE_ROWS) 
    port map (
      i_Clk          => i_Clk,
      i_HSync        => w_HSync_VGA,
      i_VSync        => w_VSync_VGA,
      i_Game_Start   => w_RX_DV,
      i_Paddle_Up_P1 => w_Switch_1,
      i_Paddle_Dn_P1 => w_Switch_2,
      i_Paddle_Up_P2 => w_Switch_3,
      i_Paddle_Dn_P2 => w_Switch_4,
      o_HSync        => w_HSync_Pong,
      o_VSync        => w_VSync_Pong,
      o_Red_Video    => w_Red_Video_Pong,
      o_Blu_Video    => w_Blu_Video_Pong,
      o_Grn_Video    => w_Grn_Video_Pong);

      
  VGA_Sync_Porch_Inst : entity work.VGA_Sync_Porch
    generic map (
      g_Video_Width => c_VIDEO_WIDTH,
      g_TOTAL_COLS  => c_TOTAL_COLS,
      g_TOTAL_ROWS  => c_TOTAL_ROWS,
      g_ACTIVE_COLS => c_ACTIVE_COLS,
      g_ACTIVE_ROWS => c_ACTIVE_ROWS 
      )
    port map (
      i_Clk       => i_Clk,
      i_HSync     => w_HSync_Pong,
      i_VSync     => w_VSync_Pong,
      i_Red_Video => w_Red_Video_Pong,
      i_Grn_Video => w_Blu_Video_Pong,
      i_Blu_Video => w_Grn_Video_Pong,
      --
      o_HSync     => o_VGA_HSync,
      o_VSync     => o_VGA_VSync,
      o_Red_Video => w_Red_Video_Porch,
      o_Grn_Video => w_Blu_Video_Porch,
      o_Blu_Video => w_Grn_Video_Porch
      );
	  
  o_VGA_Red_0 <= w_Red_Video_Porch(0);
  o_VGA_Red_1 <= w_Red_Video_Porch(1);
  o_VGA_Red_2 <= w_Red_Video_Porch(2);
  
  o_VGA_Grn_0 <= w_Grn_Video_Porch(0);
  o_VGA_Grn_1 <= w_Grn_Video_Porch(1);
  o_VGA_Grn_2 <= w_Grn_Video_Porch(2);

  o_VGA_Blu_0 <= w_Blu_Video_Porch(0);
  o_VGA_Blu_1 <= w_Blu_Video_Porch(1);
  o_VGA_Blu_2 <= w_Blu_Video_Porch(2);
  
end architecture RTL;

Pong_Top.vhd:

-- This module is designed for 640x480 with a 25 MHz input clock.

library ieee;
use ieee.std_logic_1164.all;
use ieee.numeric_std.all;

library work;
use work.Pong_Pkg.all;

entity Pong_Top is
  generic (
    g_Video_Width : integer;
    g_Total_Cols  : integer;
    g_Total_Rows  : integer;
    g_Active_Cols : integer;
    g_Active_Rows : integer
    );
  port (
    i_Clk     : in std_logic;
    i_HSync   : in std_logic;
    i_VSync   : in std_logic;

    -- Game Start Button
    i_Game_Start : in std_logic;
    
    -- Player 1 & Player 2 Controls (Controls Paddles)
    i_Paddle_Up_P1 : in std_logic;
    i_Paddle_Dn_P1 : in std_logic;
    i_Paddle_Up_P2 : in std_logic;
    i_Paddle_Dn_P2 : in std_logic;
    
    o_HSync     : out std_logic;
    o_VSync     : out std_logic;
    o_Red_Video : out std_logic_vector(g_Video_Width-1 downto 0);
    o_Blu_Video : out std_logic_vector(g_Video_Width-1 downto 0);
    o_Grn_Video : out std_logic_vector(g_Video_Width-1 downto 0)
    );
end entity Pong_Top;

architecture rtl of Pong_Top is

  type t_SM_Main is (s_Idle, s_Running, s_P1_Wins, s_P2_Wins, s_Cleanup);
  signal r_SM_Main : t_SM_Main := s_Idle;
  
  signal w_HSync : std_logic;
  signal w_VSync : std_logic;
  
  -- Make these unsigned counters (always positive)
  signal w_Col_Count : std_logic_vector(9 downto 0);
  signal w_Row_Count : std_logic_vector(9 downto 0);

  -- Divided version of the Row/Col Counters.
  -- Allows us to make the board 40x30
  signal w_Col_Count_Div : std_logic_vector(5 downto 0) := (others => '0');
  signal w_Row_Count_Div : std_logic_vector(5 downto 0) := (others => '0');

  -- Integer representation of the above counters.
  -- Integers are easier to work with conceptually
  signal w_Col_Index : integer range 0 to 2**w_Col_Count_Div'length-1 := 0;
  signal w_Row_Index : integer range 0 to 2**w_Row_Count_Div'length-1 := 0; 

  signal w_Draw_Paddle_P1 : std_logic;
  signal w_Draw_Paddle_P2 : std_logic;
  signal w_Paddle_Y_P1    : std_logic_vector(5 downto 0);
  signal w_Paddle_Y_P2    : std_logic_vector(5 downto 0);
  signal w_Draw_Ball      : std_logic;
  signal w_Ball_X         : std_logic_vector(5 downto 0);
  signal w_Ball_Y         : std_logic_vector(5 downto 0);
  signal w_Draw_Any       : std_logic;
  
  signal w_Game_Active : std_logic;

  signal w_Paddle_Y_P1_Top : unsigned(5 downto 0);
  signal w_Paddle_Y_P1_Bot : unsigned(5 downto 0);
  signal w_Paddle_Y_P2_Top : unsigned(5 downto 0);
  signal w_Paddle_Y_P2_Bot : unsigned(5 downto 0);

  signal r_P1_Score : integer range 0 to c_Score_Limit := 0;
  signal r_P2_Score : integer range 0 to c_Score_Limit := 0;
  
begin

  
  Sync_To_Count_inst : entity work.Sync_To_Count
    generic map (
      g_Total_Cols => g_Total_Cols,
      g_Total_Rows => g_Total_Rows
      )
    port map (
      i_Clk       => i_Clk,
      i_HSync     => i_HSync,
      i_VSync     => i_VSync,
      o_HSync     => w_HSync,
      o_VSync     => w_VSync,
      o_Col_Count => w_Col_Count,
      o_Row_Count => w_Row_Count
      );

  -- Register syncs to align with output data.
  p_Reg_Syncs : process (i_Clk) is
  begin
    if rising_edge(i_Clk) then
      o_VSync <= w_VSync;
      o_HSync <= w_HSync;
    end if;
  end process p_Reg_Syncs; 

  
  -- Drop 4 LSBs, which effectively divides by 16
  w_Col_Count_Div <= w_Col_Count(w_Col_Count'left downto 4);
  w_Row_Count_Div <= w_Row_Count(w_Row_Count'left downto 4);

  -- Instantiation of Paddle Control + Draw for Player 1 
  Paddle_Ctrl_P1_inst : Pong_Paddle_Ctrl generic map (
    g_Player_Paddle_X => c_Paddle_Col_Location_P1
    )
    port map (
      i_Clk           => i_Clk,
      i_Col_Count_Div => w_Col_Count_Div,
      i_Row_Count_Div => w_Row_Count_Div,
      i_Paddle_Up     => i_Paddle_Up_P1,
      i_Paddle_Dn     => i_Paddle_Dn_P1,
      o_Draw_Paddle   => w_Draw_Paddle_P1,
      o_Paddle_Y      => w_Paddle_Y_P1
      );

  
  -- Instantiation of Paddle Control + Draw for Player 2
  Paddle_Ctrl_P2_inst : Pong_Paddle_Ctrl
    generic map (
      g_Player_Paddle_X => c_Paddle_Col_Location_P2
      )
    port map (
      i_Clk           => i_Clk,
      i_Col_Count_Div => w_Col_Count_Div,
      i_Row_Count_Div => w_Row_Count_Div,
      i_Paddle_Up     => i_Paddle_Up_P2,
      i_Paddle_Dn     => i_Paddle_Dn_P2,
      o_Draw_Paddle   => w_Draw_Paddle_P2,
      o_Paddle_Y      => w_Paddle_Y_P2
      );

  
  -- Instantiation of Ball Control + Draw 
  Pong_Ball_Ctrl_inst : Pong_Ball_Ctrl
    port map (
      i_Clk           => i_Clk,
      i_Game_Active   => w_Game_Active,
      i_Col_Count_Div => w_Col_Count_Div,
      i_Row_Count_Div => w_Row_Count_Div,
      o_Draw_Ball     => w_Draw_Ball,
      o_Ball_X        => w_Ball_X,
      o_Ball_Y        => w_Ball_Y
      );

  -- Create Intermediary signals for P1 and P2 Paddle Top and Bottom positions
  w_Paddle_Y_P1_Bot <= unsigned(w_Paddle_Y_P1);
  w_Paddle_Y_P1_Top <= w_Paddle_Y_P1_Bot + to_unsigned(c_Paddle_Height, w_Paddle_Y_P1_Bot'length);

  w_Paddle_Y_P2_Bot <= unsigned(w_Paddle_Y_P2);
  w_Paddle_Y_P2_Top <= w_Paddle_Y_P2_Bot + to_unsigned(c_Paddle_Height, w_Paddle_Y_P2_Bot'length);

  -- Create a state machine to control the state of play
  p_SM_Main : process (i_Clk) is 
  begin 
    if rising_edge(i_Clk) then
      case r_SM_Main is -- Stay in this state until Game Start button is hit 
        when s_Idle =>
          if i_Game_Start = '1' then
            r_SM_Main <= s_Running;
          end if;
        
        -- Stay in this state until either player misses the ball 
        -- Can only occur when the Ball is at 0 or c_Game_Width-1 
        when s_Running =>

          -- Player 1's Side:
          if w_Ball_X = std_logic_vector(to_unsigned(0, w_Ball_X'length)) then
            if (unsigned(w_Ball_Y) < w_Paddle_Y_P1_Bot or unsigned(w_Ball_Y) > w_Paddle_Y_P1_Top) then
              r_SM_Main <= s_P2_Wins;
            end if;

          -- Player 2's Side:
          elsif w_Ball_X = std_logic_vector(to_unsigned(c_Game_Width-1, w_Ball_X'length)) then
            if (unsigned(w_Ball_Y) < w_Paddle_Y_P2_Bot or unsigned(w_Ball_Y) > w_Paddle_Y_P2_Top) then
              r_SM_Main <= s_P1_Wins; end if; end if; when s_P1_Wins =>
          if r_P1_Score = c_Score_Limit then
            r_P1_Score <= 0;
          else
            r_P1_Score <= r_P1_Score + 1;
          end if;
          r_SM_Main  <= s_Cleanup; when s_P2_Wins =>
          if r_P2_Score = c_Score_Limit then
            r_P2_Score <= 0;
          else
            r_P2_Score <= r_P2_Score + 1;
          end if;
          r_SM_Main <= s_Cleanup; when s_Cleanup =>
          r_SM_Main <= s_Idle; when others =>
          r_SM_Main <= s_Idle;
          
      end case;
    end if;
  end process p_SM_Main;

  -- Conditional Assignment of Game Active based on State Machine
  w_Game_Active <= '1' when r_SM_Main = s_Running else '0';

  w_Draw_Any <= w_Draw_Ball or w_Draw_Paddle_P1 or w_Draw_Paddle_P2;
  
  -- Assign Color outputs, only two colors, White or Black
  o_Red_Video <= (others => '1') when w_Draw_Any = '1' else (others => '0');
  o_Blu_Video <= (others => '1') when w_Draw_Any = '1' else (others => '0');
  o_Grn_Video <= (others => '1') when w_Draw_Any = '1' else (others => '0');
  
  
end architecture rtl;

Pong_Paddle_Ctrl.vhd:

-- This module is designed for 640x480 with a 25 MHz input clock.
-- Signal o_Paddle_Y represents the index of the top of the paddle in Y dimension.

library ieee;
use ieee.std_logic_1164.all;
use ieee.numeric_std.all;

library work;
use work.Pong_Pkg.all;

entity Pong_Paddle_Ctrl is
  generic (
    g_Player_Paddle_X : integer  -- Changes for P1 vs P2
    );
  port (
    i_Clk : in std_logic;

    i_Col_Count_Div : in std_logic_vector(5 downto 0);
    i_Row_Count_Div : in std_logic_vector(5 downto 0);

    -- Player Paddle Control
    i_Paddle_Up : in std_logic;
    i_Paddle_Dn : in std_logic;

    o_Draw_Paddle : out std_logic;
    o_Paddle_Y    : out std_logic_vector(5 downto 0)
    );
end entity Pong_Paddle_Ctrl;

architecture rtl of Pong_Paddle_Ctrl is

  -- Integer representation of the above 6 downto 0 counters.
  -- Integers are easier to work with conceptually
  signal w_Col_Index : integer range 0 to 2**i_Col_Count_Div'length := 0;
  signal w_Row_Index : integer range 0 to 2**i_Row_Count_Div'length := 0;

  signal w_Paddle_Count_En : std_logic;

  signal r_Paddle_Count : integer range 0 to c_Paddle_Speed := 0;
  
  -- Start Location (Top Left) of Paddles
  signal r_Paddle_Y : integer range 0 to c_Game_Height-c_Paddle_Height-1 := 0;

  signal r_Draw_Paddle : std_logic := '0';
  
begin

  w_Col_Index <= to_integer(unsigned(i_Col_Count_Div));
  w_Row_Index <= to_integer(unsigned(i_Row_Count_Div));  

  -- Only allow paddles to move if only one button is pushed.
  w_Paddle_Count_En <= i_Paddle_Up xor i_Paddle_Dn;

  -- Controls how the paddles are moved.  Sets r_Paddle_Y.
  -- Can change the movement speed by changing the constant in Package file.
  p_Move_Paddles : process (i_Clk) is
  begin
    if rising_edge(i_Clk) then

      -- Update the paddle counter when either switch is pushed and held.
      if w_Paddle_Count_En = '1' then
        if r_Paddle_Count = c_Paddle_Speed then
          r_Paddle_Count <= 0;
        else
          r_Paddle_Count <= r_Paddle_Count + 1;
        end if;
      else
        r_Paddle_Count <= 0;
      end if;

      -- Update the Paddle Location Slowly, only allowed when the Paddle Count
      -- reaches its limit
      if (i_Paddle_Up = '1' and r_Paddle_Count = c_Paddle_Speed) then

        -- If Paddle is already at the top, do not update it
        if r_Paddle_Y /= 0 then
          r_Paddle_Y <= r_Paddle_Y - 1;
        end if;

      elsif (i_Paddle_Dn = '1' and r_Paddle_Count = c_Paddle_Speed) then

        -- If Paddle is already at the bottom, do not update it
        if r_Paddle_Y /= c_Game_Height-c_Paddle_Height-1 then
          r_Paddle_Y <= r_Paddle_Y + 1;
        end if; 
      end if;
    end if;
  end process p_Move_Paddles;
  
  -- Draws the Paddles as deteremined by input Generic g_Player_Paddle_X 
  -- as well as r_Paddle_Y.
  p_Draw_Paddles : process (i_Clk) is
  begin
    if rising_edge(i_Clk) then
    -- Draws in a single column and in a range of rows.
    -- Range of rows is determined by c_Paddle_Height
      if (w_Col_Index = g_Player_Paddle_X and w_Row_Index >= r_Paddle_Y and
          w_Row_Index <= r_Paddle_Y + c_Paddle_Height) then
          r_Draw_Paddle <= '1';
      else
        r_Draw_Paddle <= '0';
      end if;
    end if;
  end process p_Draw_Paddles;

  -- Assign output for next higher module to use
  o_Draw_Paddle <= r_Draw_Paddle;
  o_Paddle_Y    <= std_logic_vector(to_unsigned(r_Paddle_Y, o_Paddle_Y'length));
  
end architecture rtl;

Pong_Ball_Ctrl.vhd:

-- This module is designed for 640x480 with a 25 MHz input clock.

library ieee;
use ieee.std_logic_1164.all;
use ieee.numeric_std.all;

library work;
use work.Pong_Pkg.all;

entity Pong_Ball_Ctrl is
  port (
    i_Clk           : in  std_logic;
    i_Game_Active   : in  std_logic;
    i_Col_Count_Div : in  std_logic_vector(5 downto 0);
    i_Row_Count_Div : in  std_logic_vector(5 downto 0);
    --
    o_Draw_Ball     : out std_logic;
    o_Ball_X        : out std_logic_vector(5 downto 0);
    o_Ball_Y        : out std_logic_vector(5 downto 0)
    );
end entity Pong_Ball_Ctrl;

architecture rtl of Pong_Ball_Ctrl is

  -- Integer representation of the above 6 downto 0 counters.
  -- Integers are easier to work with conceptually
  signal w_Col_Index : integer range 0 to 2**i_Col_Count_Div'length := 0;
  signal w_Row_Index : integer range 0 to 2**i_Row_Count_Div'length := 0;
  
  signal r_Ball_Count : integer range 0 to c_Ball_Speed := 0;
  
  -- X and Y location (Col, Row) for Pong Ball, also Previous Locations
  signal r_Ball_X      : integer range 0 to 2**i_Col_Count_Div'length := 0;
  signal r_Ball_Y      : integer range 0 to 2**i_Row_Count_Div'length := 0;
  signal r_Ball_X_Prev : integer range 0 to 2**i_Col_Count_Div'length := 0;
  signal r_Ball_Y_Prev : integer range 0 to 2**i_Row_Count_Div'length := 0;

  signal r_Draw_Ball : std_logic := '0';
  
begin

  w_Col_Index <= to_integer(unsigned(i_Col_Count_Div));
  w_Row_Index <= to_integer(unsigned(i_Row_Count_Div));  

    
  p_Move_Ball : process (i_Clk) is
  begin
    if rising_edge(i_Clk) then
      -- If the game is not active, ball stays in the middle of the screen
      -- until the game starts.
      if i_Game_Active = '0' then
        r_Ball_X      <= c_Game_Width/2;
        r_Ball_Y      <= c_Game_Height/2;
        r_Ball_X_Prev <= c_Game_Width/2 + 1; 
        r_Ball_Y_Prev <= c_Game_Height/2 - 1;

      else
        -- Update the ball counter continuously.  Ball movement update rate is
        -- determined by a constant in the package file.
        if r_Ball_Count = c_Ball_Speed then
          r_Ball_Count <= 0;
        else
          r_Ball_Count <= r_Ball_Count + 1;
        end if;

        -----------------------------------------------------------------------
        -- Control X Position (Col)
        -----------------------------------------------------------------------
        if r_Ball_Count = c_Ball_Speed then
          
          -- Store Previous Location to keep track of ball movement
          r_Ball_X_Prev <= r_Ball_X;
          
          -- If ball is moving to the right, keep it moving right, but check
          -- that it's not at the wall (in which case it bounces back)
          if r_Ball_X_Prev < r_Ball_X then
            if r_Ball_X = c_Game_Width-1 then
              r_Ball_X <= r_Ball_X - 1;
            else
              r_Ball_X <= r_Ball_X + 1;
            end if;
          -- Ball is moving left, keep it moving left, check for wall impact
          elsif r_Ball_X_Prev > r_Ball_X then
            if r_Ball_X = 0 then
              r_Ball_X <= r_Ball_X + 1;
            else
              r_Ball_X <= r_Ball_X - 1;
            end if;
          end if;
        end if;

        
        -----------------------------------------------------------------------
        -- Control Y Position (Row)
        -----------------------------------------------------------------------
        if r_Ball_Count = c_Ball_Speed then
          
          -- Store Previous Location to keep track of ball movement
          r_Ball_Y_Prev <= r_Ball_Y;
          
          -- If ball is moving to the up, keep it moving up, but check
          -- that it's not at the wall (in which case it bounces back)
          if r_Ball_Y_Prev < r_Ball_Y then
            if r_Ball_Y = c_Game_Height-1 then
              r_Ball_Y <= r_Ball_Y - 1;
            else
              r_Ball_Y <= r_Ball_Y + 1;
            end if;
          -- Ball is moving down, keep it moving down, check for wall impact
          elsif r_Ball_Y_Prev > r_Ball_Y then
            if r_Ball_Y = 0 then
              r_Ball_Y <= r_Ball_Y + 1;
            else
              r_Ball_Y <= r_Ball_Y - 1;
            end if;
          end if;
        end if;
      end if;                           -- w_Game_Active = '1'
    end if;                             -- rising_edge(i_Clk)
  end process p_Move_Ball;


  -- Draws a ball at the location determined by X and Y indexes.
  p_Draw_Ball : process (i_Clk) is
  begin
    if rising_edge(i_Clk) then
      if (w_Col_Index = r_Ball_X and w_Row_Index = r_Ball_Y) then
        r_Draw_Ball <= '1';
      else
        r_Draw_Ball <= '0';
      end if;
    end if;
  end process p_Draw_Ball;

  o_Draw_Ball <= r_Draw_Ball;
  o_Ball_X    <= std_logic_vector(to_unsigned(r_Ball_X, o_Ball_X'length));
  o_Ball_Y    <= std_logic_vector(to_unsigned(r_Ball_Y, o_Ball_Y'length));
  
  
end architecture rtl;

Pong_Pkg:

-- Package file containing all Constants and Components used in Pong Game

library ieee;
use ieee.std_logic_1164.all;
use ieee.numeric_std.all;

package Pong_Pkg is

  -----------------------------------------------------------------------------
  -- Constants
  -----------------------------------------------------------------------------
  
  -- Set the Width and Height of the Game Board
  constant c_Game_Width    : integer := 40;
  constant c_Game_Height   : integer := 30;

  -- Set the number of points to play to
  constant c_Score_Limit : integer := 9;
  
  -- Set the Height (in board game units) of the paddle.
  constant c_Paddle_Height : integer := 6;

  -- Set the Speed of the paddle movement.  In this case, the paddle will move
  -- one board game unit every 50 milliseconds that the button is held down.
  constant c_Paddle_Speed : integer := 1250000;

  -- Set the Speed of the ball movement.  In this case, the ball will move
  -- one board game unit every 50 milliseconds that the button is held down.   
  constant c_Ball_Speed : integer  := 1250000;
  
  -- Sets Column index to draw Player 1 & Player 2 Paddles.
  constant c_Paddle_Col_Location_P1 : integer := 0;
  constant c_Paddle_Col_Location_P2 : integer := c_Game_Width-1;


  -----------------------------------------------------------------------------
  -- Component Declarations
  -----------------------------------------------------------------------------
  component Pong_Paddle_Ctrl is
    generic (
      g_Player_Paddle_X : integer  -- Changes for P1 vs P2
      );
    port (
      i_Clk : in std_logic;

      i_Col_Count_Div : in std_logic_vector(5 downto 0);
      i_Row_Count_Div : in std_logic_vector(5 downto 0);

      -- Player Paddle Control
      i_Paddle_Up : in std_logic;
      i_Paddle_Dn : in std_logic;

      o_Draw_Paddle : out std_logic;
      o_Paddle_Y    : out std_logic_vector(5 downto 0)
      );
  end component Pong_Paddle_Ctrl;


  component Pong_Ball_Ctrl is
    port (
      i_Clk           : in  std_logic;
      i_Game_Active   : in  std_logic;
      i_Col_Count_Div : in  std_logic_vector(5 downto 0);
      i_Row_Count_Div : in  std_logic_vector(5 downto 0);
      --
      o_Draw_Ball     : out std_logic;
      o_Ball_X        : out std_logic_vector(5 downto 0);
      o_Ball_Y        : out std_logic_vector(5 downto 0)
      );
  end component Pong_Ball_Ctrl;

  
end package Pong_Pkg;  

Project10_Pong_Top.v:

///////////////////////////////////////////////////////////////////////////
// File Downloaded From http://www.nandland.com
///////////////////////////////////////////////////////////////////////////
module Project10_Pong_Top
  (input  i_Clk,       // Main Clock
   input  i_UART_RX, // UART RX Data

   // Push BUttons
   input  i_Switch_1,
   input  i_Switch_2,
   input  i_Switch_3,
   input  i_Switch_4,

   // VGA
   output o_VGA_HSync,
   output o_VGA_VSync,
   output o_VGA_Red_0,
   output o_VGA_Red_1,
   output o_VGA_Red_2,
   output o_VGA_Grn_0,
   output o_VGA_Grn_1,
   output o_VGA_Grn_2,
   output o_VGA_Blu_0,
   output o_VGA_Blu_1,
   output o_VGA_Blu_2   
   );
   
  // VGA Constants to set Frame Size
  parameter c_VIDEO_WIDTH = 3;
  parameter c_TOTAL_COLS  = 800;
  parameter c_TOTAL_ROWS  = 525;
  parameter c_ACTIVE_COLS = 640;
  parameter c_ACTIVE_ROWS = 480;
    
  // Common VGA Signals
  wire [c_VIDEO_WIDTH-1:0] w_Red_Video_Pong, w_Red_Video_Porch;
  wire [c_VIDEO_WIDTH-1:0] w_Grn_Video_Pong, w_Grn_Video_Porch;
  wire [c_VIDEO_WIDTH-1:0] w_Blu_Video_Pong, w_Blu_Video_Porch;
  
  // 25,000,000 / 115,200 = 217
  UART_RX #(.CLKS_PER_BIT(217)) UART_RX_Inst
  (.i_Clock(i_Clk),
   .i_RX_Serial(i_UART_RX),
   .o_RX_DV(w_RX_DV),
   .o_RX_Byte());
   
  // Generates Sync Pulses to run VGA
  VGA_Sync_Pulses #(.TOTAL_COLS(c_TOTAL_COLS),
                    .TOTAL_ROWS(c_TOTAL_ROWS),
                    .ACTIVE_COLS(c_ACTIVE_COLS),
                    .ACTIVE_ROWS(c_ACTIVE_ROWS)) VGA_Sync_Pulses_Inst 
  (.i_Clk(i_Clk),
   .o_HSync(w_HSync_VGA),
   .o_VSync(w_VSync_VGA),
   .o_Col_Count(),
   .o_Row_Count()
  );
  
  // Debounce All Switches
  Debounce_Switch Switch_1
    (.i_Clk(i_Clk),
     .i_Switch(i_Switch_1),
     .o_Switch(w_Switch_1));
  
  Debounce_Switch Switch_2
    (.i_Clk(i_Clk),
     .i_Switch(i_Switch_2),
     .o_Switch(w_Switch_2));
  
  Debounce_Switch Switch_3
    (.i_Clk(i_Clk),
     .i_Switch(i_Switch_3),
     .o_Switch(w_Switch_3));
  
  Debounce_Switch Switch_4
    (.i_Clk(i_Clk),
     .i_Switch(i_Switch_4),
     .o_Switch(w_Switch_4));
  
  Pong_Top #(.c_TOTAL_COLS(c_TOTAL_COLS),
             .c_TOTAL_ROWS(c_TOTAL_ROWS),
             .c_ACTIVE_COLS(c_ACTIVE_COLS),
             .c_ACTIVE_ROWS(c_ACTIVE_ROWS)) Pong_Inst
  (.i_Clk(i_Clk),
   .i_HSync(w_HSync_VGA),
   .i_VSync(w_VSync_VGA),
   .i_Game_Start(w_RX_DV),
   .i_Paddle_Up_P1(w_Switch_1),
   .i_Paddle_Dn_P1(w_Switch_2),
   .i_Paddle_Up_P2(w_Switch_3),
   .i_Paddle_Dn_P2(w_Switch_4),
   .o_HSync(w_HSync_Pong),
   .o_VSync(w_VSync_Pong),
   .o_Red_Video(w_Red_Video_Pong),
   .o_Grn_Video(w_Grn_Video_Pong),
   .o_Blu_Video(w_Blu_Video_Pong));
	
  VGA_Sync_Porch  #(.VIDEO_WIDTH(c_VIDEO_WIDTH),
                    .TOTAL_COLS(c_TOTAL_COLS),
                    .TOTAL_ROWS(c_TOTAL_ROWS),
                    .ACTIVE_COLS(c_ACTIVE_COLS),
                    .ACTIVE_ROWS(c_ACTIVE_ROWS))
  VGA_Sync_Porch_Inst
   (.i_Clk(i_Clk),
    .i_HSync(w_HSync_Pong),
    .i_VSync(w_VSync_Pong),
    .i_Red_Video(w_Red_Video_Pong),
    .i_Grn_Video(w_Grn_Video_Pong),
    .i_Blu_Video(w_Blu_Video_Pong),
    .o_HSync(o_VGA_HSync),
    .o_VSync(o_VGA_VSync),
    .o_Red_Video(w_Red_Video_Porch),
    .o_Grn_Video(w_Grn_Video_Porch),
    .o_Blu_Video(w_Blu_Video_Porch));
	  
  assign o_VGA_Red_0 = w_Red_Video_Porch[0];
  assign o_VGA_Red_1 = w_Red_Video_Porch[1];
  assign o_VGA_Red_2 = w_Red_Video_Porch[2];
  
  assign o_VGA_Grn_0 = w_Grn_Video_Porch[0];
  assign o_VGA_Grn_1 = w_Grn_Video_Porch[1];
  assign o_VGA_Grn_2 = w_Grn_Video_Porch[2];

  assign o_VGA_Blu_0 = w_Blu_Video_Porch[0];
  assign o_VGA_Blu_1 = w_Blu_Video_Porch[1];
  assign o_VGA_Blu_2 = w_Blu_Video_Porch[2];

endmodule

Pong_Top.v:

module Pong_Top
  #(parameter c_TOTAL_COLS=800,
    parameter c_TOTAL_ROWS=525,
    parameter c_ACTIVE_COLS=640,
    parameter c_ACTIVE_ROWS=480)
  (input            i_Clk,
   input            i_HSync,
   input            i_VSync,
   // Game Start Button   
   input            i_Game_Start,
   // Player 1 and Player 2 Controls (Controls Paddles)
   input            i_Paddle_Up_P1,
   input            i_Paddle_Dn_P1,
   input            i_Paddle_Up_P2,
   input            i_Paddle_Dn_P2,
   // Output Video
   output reg       o_HSync,
   output reg       o_VSync,
   output [3:0] o_Red_Video,
   output [3:0] o_Grn_Video,
   output [3:0] o_Blu_Video);

  // Local Constants to Determine Game Play
  parameter c_GAME_WIDTH  = 40;
  parameter c_GAME_HEIGHT = 30;
  parameter c_SCORE_LIMIT = 9;
  parameter c_PADDLE_HEIGHT = 6;
  parameter c_PADDLE_COL_P1 = 0;  // Col Index of Paddle for P1
  parameter c_PADDLE_COL_P2 = c_GAME_WIDTH-1; // Index for P2

  // State machine enumerations
  parameter IDLE    = 3'b000;
  parameter RUNNING = 3'b001;
  parameter P1_WINS = 3'b010;
  parameter P2_WINS = 3'b011;
  parameter CLEANUP = 3'b100;

  reg [2:0] r_SM_Main = IDLE;

  wire       w_HSync, w_VSync;
  wire [9:0] w_Col_Count, w_Row_Count;
  
  wire       w_Draw_Paddle_P1, w_Draw_Paddle_P2;
  wire [5:0] w_Paddle_Y_P1, w_Paddle_Y_P2;
  wire       w_Draw_Ball, w_Draw_Any;
  wire [5:0] w_Ball_X, w_Ball_Y;

  reg [3:0] r_P1_Score = 0;
  reg [3:0] r_P2_Score = 0;

  // Divided version of the Row/Col Counters
  // Allows us to make the board 40x30
  wire [5:0] w_Col_Count_Div, w_Row_Count_Div;

  Sync_To_Count #(.TOTAL_COLS(c_TOTAL_COLS),
                  .TOTAL_ROWS(c_TOTAL_ROWS)) Sync_To_Count_Inst
    (.i_Clk(i_Clk),
     .i_HSync(i_HSync),
     .i_VSync(i_VSync),
     .o_HSync(w_HSync),
     .o_VSync(w_VSync),
     .o_Col_Count(w_Col_Count),
     .o_Row_Count(w_Row_Count));

  // Register syncs to align with output data.
  always @(posedge i_Clk)
  begin
    o_HSync <= w_HSync;
    o_VSync <= w_VSync;
  end

  // Drop 4 LSBs, which effectively divides by 16
  assign w_Col_Count_Div = w_Col_Count[9:4];
  assign w_Row_Count_Div = w_Row_Count[9:4];


  // Instantiation of Paddle Control + Draw for Player 1
  Pong_Paddle_Ctrl #(.c_PLAYER_PADDLE_X(c_PADDLE_COL_P1),
                     .c_GAME_HEIGHT(c_GAME_HEIGHT)) P1_Inst
    (.i_Clk(i_Clk),
     .i_Col_Count_Div(w_Col_Count_Div),
     .i_Row_Count_Div(w_Row_Count_Div),
     .i_Paddle_Up(i_Paddle_Up_P1),
     .i_Paddle_Dn(i_Paddle_Dn_P1),
     .o_Draw_Paddle(w_Draw_Paddle_P1),
     .o_Paddle_Y(w_Paddle_Y_P1));

  // Instantiation of Paddle Control + Draw for Player 2
  Pong_Paddle_Ctrl #(.c_PLAYER_PADDLE_X(c_PADDLE_COL_P2),
                     .c_GAME_HEIGHT(c_GAME_HEIGHT)) P2_Inst
    (.i_Clk(i_Clk),
     .i_Col_Count_Div(w_Col_Count_Div),
     .i_Row_Count_Div(w_Row_Count_Div),
     .i_Paddle_Up(i_Paddle_Up_P2),
     .i_Paddle_Dn(i_Paddle_Dn_P2),
     .o_Draw_Paddle(w_Draw_Paddle_P2),
     .o_Paddle_Y(w_Paddle_Y_P2));

  // Instantiation of Ball Control + Draw
  Pong_Ball_Ctrl Pong_Ball_Ctrl_Inst
    (.i_Clk(i_Clk),
     .i_Game_Active(w_Game_Active),
     .i_Col_Count_Div(w_Col_Count_Div),
     .i_Row_Count_Div(w_Row_Count_Div),
     .o_Draw_Ball(w_Draw_Ball),
     .o_Ball_X(w_Ball_X),
     .o_Ball_Y(w_Ball_Y));


  // Create a state machine to control the state of play
  always @(posedge i_Clk)
  begin
    case (r_SM_Main)

    // Stay in this state until Game Start button is hit
    IDLE :
    begin
      if (i_Game_Start == 1'b1)
        r_SM_Main <= RUNNING;
    end

    // Stay in this state until either player misses the ball
    // can only occur when the Ball is at 0 to c_GAME_WIDTH-1
    RUNNING :
    begin
      // Player 1 Side
      if (w_Ball_X == 0 && 
          (w_Ball_Y < w_Paddle_Y_P1 || w_Ball_Y > w_Paddle_Y_P1 + c_PADDLE_HEIGHT))
        r_SM_Main <= P2_WINS;

      // Player 2 Side
      else if (w_Ball_X == c_GAME_WIDTH-1 && 
               (w_Ball_Y < w_Paddle_Y_P2 || w_Ball_Y > w_Paddle_Y_P2  + c_PADDLE_HEIGHT))
        r_SM_Main <= P1_WINS;
    end

    P1_WINS :
    begin
      if (r_P1_Score == c_SCORE_LIMIT-1)
        r_P1_Score <= 0;
      else
      begin
        r_P1_Score <= r_P1_Score + 1;
        r_SM_Main <= CLEANUP;
      end
    end

    P2_WINS :
    begin
      if (r_P2_Score == c_SCORE_LIMIT-1)
        r_P2_Score <= 0;
      else 
      begin
        r_P2_Score <= r_P2_Score + 1;
        r_SM_Main <= CLEANUP;
      end
    end

    CLEANUP :
      r_SM_Main <= IDLE;

    endcase
  end

  // Conditional Assignment based on State Machine state
  assign w_Game_Active = (r_SM_Main == RUNNING) ? 1'b1 : 1'b0;

  assign w_Draw_Any = w_Draw_Ball | w_Draw_Paddle_P1 | w_Draw_Paddle_P2;

  // Assign colors. Currently set to only 2 colors, white or black.
  assign o_Red_Video = w_Draw_Any ? 4'b1111 : 4'b0000;
  assign o_Grn_Video = w_Draw_Any ? 4'b1111 : 4'b0000;
  assign o_Blu_Video = w_Draw_Any ? 4'b1111 : 4'b0000;

endmodule // Pong_Top

Pong_Paddle_Ctrl.v:

module Pong_Paddle_Ctrl
  #(parameter c_PLAYER_PADDLE_X=0,
    parameter c_PADDLE_HEIGHT=6,
    parameter c_GAME_HEIGHT=30)
  (input            i_Clk,
   input [5:0]      i_Col_Count_Div,
   input [5:0]      i_Row_Count_Div,
   input            i_Paddle_Up,
   input            i_Paddle_Dn,
   output reg       o_Draw_Paddle,
   output reg [5:0] o_Paddle_Y);

  // Set the Speed of the paddle movement.  
  // In this case, the paddle will move one board game unit
  // every 50 milliseconds that the button is held down.
  parameter c_PADDLE_SPEED = 1250000;

  reg [31:0] r_Paddle_Count = 0;

  wire w_Paddle_Count_En;

  // Only allow paddles to move if only one button is pushed.
  // ^ is an XOR bitwise operation.
  assign w_Paddle_Count_En = i_Paddle_Up ^ i_Paddle_Dn;

  always @(posedge i_Clk)
  begin
    if (w_Paddle_Count_En == 1'b1)
    begin
      if (r_Paddle_Count == c_PADDLE_SPEED)
        r_Paddle_Count <= 0;
      else
        r_Paddle_Count <= r_Paddle_Count + 1;
    end

    // Update the Paddle Location slowly.  Only allowed when the
    // Paddle Count reaches its limit.  Don't update if paddle is
    // already at the top of the screen.
    if (i_Paddle_Up == 1'b1 && r_Paddle_Count == c_PADDLE_SPEED &&
        o_Paddle_Y !== 0)
      o_Paddle_Y <= o_Paddle_Y - 1;
    else if (i_Paddle_Dn == 1'b1 && r_Paddle_Count == c_PADDLE_SPEED &&
             o_Paddle_Y !== c_GAME_HEIGHT-c_PADDLE_HEIGHT-1)
      o_Paddle_Y <= o_Paddle_Y + 1;
    end
    
    // Draws the Paddles as determined by input parameter 
    // c_PLAYER_PADDLE_X as well as o_Paddle_Y 
    always @(posedge i_Clk) begin
    // Draws in a single column and in a range of rows.
    // Range of rows is determined by c_PADDLE_HEIGHT
      if (i_Col_Count_Div == c_PLAYER_PADDLE_X && i_Row_Count_Div >= o_Paddle_Y &&
        i_Row_Count_Div <= o_Paddle_Y + c_PADDLE_HEIGHT)
      o_Draw_Paddle <= 1'b1;
    else
      o_Draw_Paddle <= 1'b0;
  end

endmodule // Pong_Paddle_Ctrl

Pong_Ball_Ctrl.v:

module Pong_Ball_Ctrl 
  #(parameter c_GAME_WIDTH=40, 
    parameter c_GAME_HEIGHT=30) 
  (input            i_Clk,
   input            i_Game_Active,
   input [5:0]      i_Col_Count_Div,
   input [5:0]      i_Row_Count_Div,
   output reg       o_Draw_Ball,
   output reg [5:0] o_Ball_X = 0,
   output reg [5:0] o_Ball_Y = 0);

  // Set the Speed of the ball movement.
  // In this case, the ball will move one board game unit 
  // every 50 milliseconds that the button is held down.   
  parameter c_BALL_SPEED = 1250000;

  reg [5:0]  r_Ball_X_Prev = 0;
  reg [5:0]  r_Ball_Y_Prev = 0;    
  reg [31:0] r_Ball_Count = 0;

  always @(posedge i_Clk)
  begin
    // If the game is not active, ball stays in the middle of
    // screen until the game starts.
    if (i_Game_Active == 1'b0)
    begin
      o_Ball_X      <= c_GAME_WIDTH/2;
      o_Ball_Y      <= c_GAME_HEIGHT/2;
      r_Ball_X_Prev <= c_GAME_WIDTH/2 + 1;
      r_Ball_Y_Prev <= c_GAME_HEIGHT/2 - 1;
    end

    // Update the ball counter continuously.  Ball movement
    // update rate is determined by input parameter
    // If ball counter is at its limit, update the ball position
    // in both X and Y.
    else
    begin
      if (r_Ball_Count < c_BALL_SPEED)
        r_Ball_Count <= r_Ball_Count + 1;
      else
      begin
        r_Ball_Count <= 0;

        // Store Previous Location to keep track of movement
        r_Ball_X_Prev <= o_Ball_X;
        r_Ball_Y_Prev <= o_Ball_Y;

        // When Previous Value is less than current value, ball is moving
        // to right.  Keep it moving to the right unless we are at wall.
        // When Prevous value is greater than current value, ball is moving
        // to left.  Keep it moving to the left unless we are at a wall.
        // Same philosophy for both X and Y.
        
        if ((r_Ball_X_Prev < o_Ball_X && o_Ball_X == c_GAME_WIDTH-1) || (r_Ball_X_Prev > o_Ball_X && o_Ball_X != 0))
          o_Ball_X <= o_Ball_X - 1;
        else
          o_Ball_X <= o_Ball_X + 1;

        if ((r_Ball_Y_Prev < o_Ball_Y && o_Ball_Y == c_GAME_HEIGHT-1) || (r_Ball_Y_Prev > o_Ball_Y && o_Ball_Y != 0))
          o_Ball_Y <= o_Ball_Y - 1;
        else
          o_Ball_Y <= o_Ball_Y + 1;
      end
    end
  end // always @ (posedge i_Clk)


  // Draws a ball at the location determined by X and Y indexes.
  always @(posedge i_Clk)
  begin
    if (i_Col_Count_Div == o_Ball_X && i_Row_Count_Div == o_Ball_Y)
      o_Draw_Ball <= 1'b1;
    else
      o_Draw_Ball <= 1'b0;
  end


endmodule // Pong_Ball_Ctrl

Congratulations! You’ve reached the end of the Go Board tutorials! I hope that you’ve enjoyed learning VHDL and Verilog with the Go Board. Please subscribe to my YouTube channel at YouTube. Lots more content is coming, so bookmark nandland.com and check back often.