Skip to content

Instantly share code, notes, and snippets.

@jgibbard
Last active January 1, 2024 16:15
Show Gist options
  • Star 4 You must be signed in to star a gist
  • Fork 4 You must be signed in to fork a gist
  • Save jgibbard/467f367b733a79553e9a to your computer and use it in GitHub Desktop.
Save jgibbard/467f367b733a79553e9a to your computer and use it in GitHub Desktop.
VHDL VGA PONG
--VHDL VGA PONG demo
--An FPGA version of the classic pong game
--Score counts up to 9
--Right player uses buttons 0 and 1
--Left player uses Switch 0 (Much harder!)
--Button 2 resets the game and score
library ieee; use ieee.std_logic_1164.all; USE ieee.std_logic_arith.all;
entity FPGA_VGA is
port (
CLOCK_50 : in std_logic;
VGA_HS, VGA_VS : out std_logic;
VGA_R : out std_logic_vector(3 downto 0);
VGA_G : out std_logic_vector(3 downto 0);
VGA_B : out std_logic_vector(3 downto 0);
SW : in std_logic_vector(9 downto 0);
HEX0_D : out std_logic_vector(6 downto 0);
HEX3_D : out std_logic_vector(6 downto 0);
BUTTON : in std_logic_vector(2 downto 0)
);
end entity FPGA_VGA;
architecture main of FPGA_VGA is
--Settable constants
constant paddle_speed : integer := 5;
constant default_ball_speed : integer := 3;
--Declare components
component vga_driver is port (
vga_clk : in std_logic;
h_sync : out std_logic;
v_sync : out std_logic;
red : out std_logic_vector(3 downto 0);
green : out std_logic_vector(3 downto 0);
blue : out std_logic_vector(3 downto 0);
new_frame : out std_logic;
current_h : out integer range 0 to 2256:=0;
current_v : out integer range 0 to 1087:=0;
red_in : in std_logic_vector(3 downto 0);
green_in : in std_logic_vector(3 downto 0);
blue_in : in std_logic_vector(3 downto 0)
);
end component vga_driver;
component vga_clk_pll is port (
clk_in_clk : in std_logic := '0';
rst_in_reset : in std_logic := '0';
clk_out_clk : out std_logic
);
end component vga_clk_pll;
component seven_seg_display is port (
input : in integer range 0 to 9;
seg_out : out std_logic_vector(6 downto 0)
);
end component seven_seg_display;
--VGA signals
signal vga_clk : std_logic:='0';
signal reset : std_logic:='0';
signal new_frame : std_logic:='0';
signal set_red, set_green, set_blue : std_logic_vector(3 downto 0):= (others => '0');
signal hpos : integer range 0 to 2256:=0;
signal vpos : integer range 0 to 1087:=0;
--Paddle Signals
signal paddle_h1 : integer range 586 to 2246:= 620;
signal paddle_v1 : integer range 47 to 1077:= 515;
signal paddle_h2 : integer range 586 to 2246:= 2197;
signal paddle_v2 : integer range 47 to 1077:= 512;
--Ball signals
signal ball_pos_h1 : integer range 586 to 2246:= 1500;
signal ball_pos_v1 : integer range 47 to 1077:= 515;
signal ball_up : std_logic:= '0';
signal ball_right : std_logic:= '1';
signal ball_speed_h : integer range 0 to 15:= default_ball_speed;
signal ball_speed_v : integer range 0 to 15:= default_ball_speed;
--Score signals
signal right_player_score : integer range 0 to 10:= 0;
signal left_player_score : integer range 0 to 10:= 0;
begin
reset <= not button(2);
pll1 : vga_clk_pll port map (CLOCK_50, reset, vga_clk); --Generate a 147.14 MHz clk (actual result 147.22MHz)
--Instantiate 2 seven segment displays. One to show each players score
left_player_score_display : seven_seg_display port map (left_player_score, HEX3_D);
right_player_score_display : seven_seg_display port map (right_player_score, HEX0_D);
--Instantiate VGA driver. This Deals with H and V sync timings
--New frame goes high for one clock cycle at the start of every frame. This can be used to redraw animations
--current_h and current_v give the coordinates of the current pixel as they are scanned out.
vga1 : vga_driver port map (
vga_clk => vga_clk,
h_sync => VGA_HS,
v_sync => VGA_VS,
red => VGA_R,
green => VGA_G,
blue => VGA_B,
new_frame => new_frame,
current_h => hpos,
current_v => vpos,
red_in => set_red,
green_in => set_green,
blue_in => set_blue
);
--Draws the left and right paddles
draw_paddle : process (vga_clk)
begin
if (rising_edge(vga_clk)) then
--Paddles are 15 x 80 pixels
if ( (hpos >= paddle_h1 and hpos < paddle_h1 + 15) and (vpos >= paddle_v1 and vpos < paddle_v1 + 80) ) then
set_red <= X"F";
elsif ( (hpos >= paddle_h2 and hpos < paddle_h2 + 15) and (vpos >= paddle_v2 and vpos < paddle_v2 + 80) ) then
set_red <= X"F";
else
set_red <= X"0";
end if;
end if;
end process;
--Draws the ball
draw_ball : process (vga_clk)
begin
if (rising_edge(vga_clk)) then
--The ball is 15 x 15 pixels
if ( (hpos >= ball_pos_h1 and hpos < ball_pos_h1 + 15) and (vpos >= ball_pos_v1 and vpos < ball_pos_v1 + 15) ) then
set_blue <= X"F";
else
set_blue <= X"0";
end if;
end if;
end process;
--Moves the paddles based on user input
--There are only 3 buttons on the DE0 development board, so a switch is used for one player.
--This makes it a bit fiddly to play as the paddles are always moving
move_paddle : process (vga_clk)
begin
if (rising_edge(vga_clk) and new_frame = '1') then
--Right Player Controls
if (BUTTON(0) = '0') then --When switch is in up position move paddle up --sw(0) = '0'
if (paddle_v2 < 997) then
paddle_v2 <= paddle_v2 + paddle_speed;
else
paddle_v2 <= paddle_v2; --If at the top then dont move paddle any higher
end if;
elsif ( BUTTON(1) = '0') then --When switch is in down position move paddle down --sw(0) = '1' or
if (paddle_v2 > 47) then
paddle_v2 <= paddle_v2 - paddle_speed;
else
paddle_v2 <= paddle_v2; --If at the bottom then don't move paddle any lower
end if;
end if;
--Right Player Controls
if (sw(1) = '0') then
if (paddle_v1 < 997) then
paddle_v1 <= paddle_v1 + paddle_speed;
else
paddle_v1 <= paddle_v1;
end if;
elsif (sw(1) = '1') then
if (paddle_v1 > 47) then
paddle_v1 <= paddle_v1 - paddle_speed;
else
paddle_v1 <= paddle_v1;
end if;
end if;
end if;
end process;
--Moves the ball and detects collisions with the edges and the paddles
move_ball : process (vga_clk)
begin
if (rising_edge(vga_clk) and new_frame = '1') then
if (reset = '1') then
left_player_score <= 0;
right_player_score <= 0;
ball_pos_v1 <= 515;
ball_pos_h1 <= 1500;
ball_speed_h <= default_ball_speed;
ball_speed_v <= default_ball_speed;
else
--If ball travelling up, and not at top
if (ball_pos_v1 < 1062 and ball_up = '1') then
ball_pos_v1 <= ball_pos_v1 + ball_speed_v;
--If ball travelling up and at top
elsif (ball_up = '1') then
ball_up <= '0';
--Ball travelling down and not at bottom
elsif (ball_pos_v1 >47 and ball_up = '0') then
ball_pos_v1 <= ball_pos_v1 - ball_speed_v;
--Ball travelling down and at bottom
elsif (ball_up = '0') then
ball_up <= '1';
end if;
--If ball travelling right, and not far right
if (ball_pos_h1 < 2231 and ball_right = '1') then
ball_pos_h1 <= ball_pos_h1 + ball_speed_h;
--If ball travelling right and at far right
elsif (ball_right = '1') then
ball_right <= '0';
if (left_player_score < 9) then
left_player_score <= left_player_score + 1;
--Reset ball position
ball_pos_v1 <= 515;
ball_pos_h1 <= 1500;
else
--Force a reset by stopping the ball
ball_speed_h <= 0;
ball_speed_v <= 0;
end if;
--Ball travelling left and not at far left
elsif (ball_pos_h1 >586 and ball_right = '0') then
ball_pos_h1 <= ball_pos_h1 - ball_speed_h;
--Ball travelling left and at far left
elsif (ball_right = '0') then
ball_right <= '1';
if (right_player_score < 9) then
right_player_score <= right_player_score + 1;
--Reset ball position
ball_pos_v1 <= 515;
ball_pos_h1 <= 1500;
else
--Force a reset by stopping the ball
ball_speed_h <= 0;
ball_speed_v <= 0;
end if;
end if;
end if;
--Very simple collision detection
elsif rising_edge(vga_clk) then
--Since only the ball is blue and only the paddles are red then if they occur together a collision has happend!
if (set_blue = X"F" and set_red = X"F") then
ball_right <= ball_right XOR '1'; --Toggle horizontal ball direction on collision
end if;
end if;
end process;
end main;
--Displays a single digit base 10 number on a seven segment display
library ieee; use ieee.std_logic_1164.all;
entity seven_seg_display is port (
input : in integer range 0 to 9;
seg_out : out std_logic_vector(6 downto 0)
);
end entity seven_seg_display;
architecture main of seven_seg_display is
begin
display_output : process(input)
begin
--LEDs are inverted so 1 = off 0 = on
case input is
when 0 =>
seg_out <= B"1000000";
when 1 =>
seg_out <= B"1111001";
when 2 =>
seg_out <= B"0100100";
when 3 =>
seg_out <= B"0110000";
when 4 =>
seg_out <= B"0011001";
when 5 =>
seg_out <= B"0010010";
when 6 =>
seg_out <= B"0000010";
when 7 =>
seg_out <= B"1111000";
when 8 =>
seg_out <= B"0000000";
when 9 =>
seg_out <= B"0010000";
when others =>
seg_out <= B"0000000";
end case;
end process;
end main;
--VGA driver for 1680 x 1050 60Hz monitor
library ieee; use ieee.std_logic_1164.all;
entity vga_driver is port (
vga_clk : in std_logic;
--Outputs to VGA port
h_sync : out std_logic;
v_sync : out std_logic;
red : out std_logic_vector(3 downto 0);
green : out std_logic_vector(3 downto 0);
blue : out std_logic_vector(3 downto 0);
--Outputs so other modules know where on the screen we are
new_frame : out std_logic;
current_h : out integer range 0 to 2256:=0;
current_v : out integer range 0 to 1087:=0;
--Inputs from other modules to set display
red_in : in std_logic_vector(3 downto 0);
green_in : in std_logic_vector(3 downto 0);
blue_in : in std_logic_vector(3 downto 0)
);
end entity vga_driver;
architecture main of vga_driver is
--Store current position within screen
signal h_pos : integer range 0 to 2256:=0; --FP+SYNC+BP+Visible = 104 + 184 + 288 + 1680 = 2256
signal v_pos : integer range 0 to 1087:=0; --FP+SYNC+BP+Visible = 1 + 3 + 33 + 1050 = 1087
begin
current_h <= h_pos;
current_v <= v_pos;
vga_timing : process(vga_clk)
begin
if rising_edge(vga_clk) then
--Count up pixel position
if (h_pos < 2256) then
new_frame <= '0';
h_pos <= h_pos + 1;
else
h_pos <= 0; --Reset position at end of line
--Count up line position
if (v_pos < 1087) then
v_pos <= v_pos + 1;
else
v_pos <= 0; --Reset position at end of frame
new_frame <= '1';
end if;
end if;
--Generate horizontal sync signal (negative pulse)
if (h_pos > 103 and h_pos < 288) then
h_sync <= '0';
else
h_sync <= '1';
end if;
--Generate vertical sync signal (positive pulse)
if (v_pos > 0 and v_pos < 4) then
v_sync <= '1';
else
v_sync <= '0';
end if;
--Blank screen during FP, BP and Sync
if ( (h_pos >= 0 and h_pos < 576) or (v_pos >= 0 and v_pos < 37) ) then
red <= (others => '0');
green <= (others => '0');
blue <= (others => '0');
--In visible range of screen
else
--Print white screen boarder
if ( (h_pos >= 576 and h_pos < 586 ) or (v_pos >= 37 and v_pos < 47 ) or (h_pos >= 2246 and h_pos < 2256 ) or (v_pos >= 1077 and v_pos < 1087 ) ) then
red <= (others => '1');
green <= (others => '1');
blue <= (others => '1');
else
--Within the boarder other modules can write to the screen
red <= red_in;
green <= green_in;
blue <= blue_in;
end if;
end if;
end if;
end process;
end main;
@mayomi1
Copy link

mayomi1 commented Aug 3, 2018

Hi,

What about the vga_clk_pll.vhl file.

It showing is error, plll instantiates undefined entity vga_clk_pll

@CyberT7
Copy link

CyberT7 commented May 24, 2019

After synthesizing this code, it gives an error:
ERROR:Xst:827 - "file_location" line 190: Signal 'ball_right' cannot be synthesized, bad synchronous description.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment