I am currently working on a simple breakout game to implement on a VGA monitor, but I need some help with it actually displaying anything. I would like some insight from someone more knowledgeable as I am just beginning. (I also know this design is not the most optimal and I should split this into several modules, but I was hoping to do it in one for just this project). Thanks!
library IEEE;
use IEEE.STD_LOGIC_1164.ALL;
use IEEE.NUMERIC_STD.ALL;
-- Entity declaration for the Breakout game
entity Breakout_Game is
Port (
clk_50MHz : in STD_LOGIC; -- 50MHz Clock input
reset : in STD_LOGIC; -- Reset input
left_btn : in STD_LOGIC; -- Left button for paddle movement
right_btn : in STD_LOGIC; -- Right button for paddle movement
hsync : out STD_LOGIC; -- Horizontal sync output
vsync : out STD_LOGIC; -- Vertical sync output
rgb : out STD_LOGIC_VECTOR(2 downto 0) -- RGB output for VGA
);
end Breakout_Game;
-- Architecture of the Breakout game
architecture Behavioral of Breakout_Game is
-- Constants for screen size and paddle/ball parameters
constant H_RES : integer := 640;
constant V_RES : integer := 480;
constant H_FP : integer := 16;
constant H_PW : integer := 96;
constant H_BP : integer := 48;
constant V_FP : integer := 10;
constant V_PW : integer := 2;
constant V_BP : integer := 33;
constant PADDLE_WIDTH : integer := 80;
constant PADDLE_HEIGHT : integer := 10;
constant PADDLE_Y_POS : integer := V_RES - PADDLE_HEIGHT - 30;
constant BALL_SIZE : integer := 10;
constant BRICK_ROWS : integer := 5;
constant BRICK_COLUMNS : integer := 10;
constant BRICK_WIDTH : integer := H_RES / BRICK_COLUMNS;
constant BRICK_HEIGHT : integer := 15;
-- Type declaration for brick array
type Brick_Type is array (0 to BRICK_ROWS-1, 0 to BRICK_COLUMNS-1) of std_logic;
-- Signals
signal clk_25MHz : STD_LOGIC := '0'; -- Derived 25MHz clock
signal h_count, v_count : integer range 0 to H_RES + H_FP + H_PW + H_BP - 1 := 0;
signal paddle_x_pos : integer range 0 to H_RES - PADDLE_WIDTH := H_RES / 2 - PADDLE_WIDTH / 2;
signal ball_x_pos, ball_y_pos : integer range 0 to V_RES - BALL_SIZE := V_RES / 2 - BALL_SIZE / 2;
signal ball_x_dir, ball_y_dir : integer range -1 to 1 := 1;
signal bricks : Brick_Type := (others => (others => '1'));
-- Function to determine if a brick is visible
function gen_brick_visible(v: integer; h: integer) return std_logic is
begin
for i in 0 to BRICK_ROWS-1 loop
for j in 0 to BRICK_COLUMNS-1 loop
if bricks(i, j) = '1' and
h >= j * BRICK_WIDTH and h < (j + 1) * BRICK_WIDTH and
v >= i * BRICK_HEIGHT and v < (i + 1) * BRICK_HEIGHT then
return '1'; -- Brick is visible
end if;
end loop;
end loop;
return '0'; -- No brick is visible at this position
end function;
begin
-- Clock Divider Process
process(clk_50MHz, reset)
begin
if reset = '1' then
clk_25MHz <= '0';
elsif rising_edge(clk_50MHz) then
clk_25MHz <= not clk_25MHz; -- Toggles the clock signal, dividing by 2
end if;
end process;
-- VGA and Game Logic Process
process(clk_25MHz, reset)
begin
if rising_edge(clk_25MHz) then
if reset = '1' then
h_count <= 0;
v_count <= 0;
paddle_x_pos <= H_RES / 2 - PADDLE_WIDTH / 2;
ball_x_pos <= H_RES / 2;
ball_y_pos <= PADDLE_Y_POS - BALL_SIZE;
ball_x_dir <= 1;
ball_y_dir <= -1;
-- Reset bricks
for i in 0 to BRICK_ROWS-1 loop
for j in 0 to BRICK_COLUMNS-1 loop
bricks(i, j) <= '1'; -- Resetall bricks to intact
end loop;
end loop;
else
-- Sync signals handling
if h_count < H_PW then
hsync <= '0';
else
hsync <= '1';
end if;
if v_count < V_PW then
vsync <= '0';
else
vsync <= '1';
end if;
-- Increment counters
if h_count = H_RES + H_FP + H_PW + H_BP - 1 then
h_count <= 0;
if v_count = V_RES + V_FP + V_PW + V_BP - 1 then
v_count <= 0;
else
v_count <= v_count + 1;
end if;
else
h_count <= h_count + 1;
end if;
-- Paddle control
if left_btn = '1' and paddle_x_pos > 0 then
paddle_x_pos <= paddle_x_pos - 2; -- Move paddle left
elsif right_btn = '1' and paddle_x_pos < H_RES - PADDLE_WIDTH then
paddle_x_pos <= paddle_x_pos + 2; -- Move paddle right
end if;
-- Ball movement and collision detection
if ball_x_pos <= 0 or ball_x_pos >= H_RES - BALL_SIZE then
ball_x_dir <= -ball_x_dir; -- Reverse X direction at edges
end if;
if ball_y_pos <= 0 or (ball_y_pos >= PADDLE_Y_POS - BALL_SIZE and
ball_x_pos > paddle_x_pos and ball_x_pos < paddle_x_pos + PADDLE_WIDTH) then
ball_y_dir <= -ball_y_dir; -- Reverse Y direction at paddle collision
end if;
-- Brick collision detection
for i in 0 to BRICK_ROWS-1 loop
for j in 0 to BRICK_COLUMNS-1 loop
if bricks(i, j) = '1' and
ball_x_pos + BALL_SIZE > j * BRICK_WIDTH and
ball_x_pos < (j + 1) * BRICK_WIDTH and
ball_y_pos + BALL_SIZE > i * BRICK_HEIGHT and
ball_y_pos < (i + 1) * BRICK_HEIGHT then
bricks(i, j) <= '0'; -- Brick hit, make it disappear
ball_y_dir <= -ball_y_dir; -- Reverse ball direction
end if;
end loop;
end loop;
-- Drawing logic for VGA output
if (v_count >= ball_y_pos and v_count < ball_y_pos + BALL_SIZE and
h_count >= ball_x_pos and h_count < ball_x_pos + BALL_SIZE) or
(v_count >= PADDLE_Y_POS and v_count < PADDLE_Y_POS + PADDLE_HEIGHT and
h_count >= paddle_x_pos and h_count < paddle_x_pos + PADDLE_WIDTH) or
gen_brick_visible(v_count, h_count) = '1' then
rgb <= "111"; -- Object is visible
else
rgb <= "000"; -- Background
end if;
end if;
end if;
end process;
end Behavioral;
I have tried to simulate using a testbench, but I am either doing it wrong, or the design is not functioning correctly. When I simulate, I do not see any changes to the RGB over time. I am pretty sure that I should see this change at some point but I am not 100% sure as this is my first actual project.
Ma1z is a new contributor to this site. Take care in asking for clarification, commenting, and answering.
Check out our Code of Conduct.