-- ====================================================================================================== -- ====================================================================================================== library ieee; use ieee.std_logic_1164.all; use ieee.numeric_std.all; entity pong_graph_st is port( clk, reset: in std_logic; btn: in std_logic_vector(1 downto 0); video_on: in std_logic; pixel_x, pixel_y: in std_logic_vector(9 downto 0); graph_rgb: out std_logic_vector(2 downto 0) ); end pong_graph_st; -- ====================================================================================================== -- ====================================================================================================== architecture sq_ball_arch of pong_graph_st is -- Signal used to control speed of ball and how often pushbuttons -- are checked for paddle movement. signal refr_tick: std_logic; -- x, y coordinates (0,0 to (639, 479) signal pix_x, pix_y: unsigned(9 downto 0); -- screen dimensions constant MAX_X: integer := 640; constant MAX_Y: integer := 480; -- wall left and right boundary of wall (full height) constant WALL_X_L: integer := 32; constant WALL_X_R: integer := 35; -- paddle left, right, top, bottom and height -- left & right are constant. -- top & bottom are signals to allow movement. bar_y_t driven by register below. constant BAR_X_L: integer := 600; constant BAR_X_R: integer := 603; signal bar_y_t, bar_y_b: unsigned(9 downto 0); constant BAR_Y_SIZE: integer := 72; -- reg to track top boundary (x position is fixed) signal bar_y_reg, bar_y_next: unsigned( 9 downto 0); -- bar moving velocity when a button is pressed -- the amount the bar is moved. constant BAR_V: integer:= 4; -- square ball -- ball left, right, top and bottom all vary. Left and top -- driven by registers below. constant BALL_SIZE: integer := 8; signal ball_x_l, ball_x_r: unsigned(9 downto 0); signal ball_y_t, ball_y_b: unsigned(9 downto 0); -- reg to track left and top boundary signal ball_x_reg, ball_x_next: unsigned(9 downto 0); signal ball_y_reg, ball_y_next: unsigned(9 downto 0); -- reg to track ball speed signal x_delta_reg, x_delta_next: unsigned(9 downto 0); signal y_delta_reg, y_delta_next: unsigned(9 downto 0); -- ball movement can be pos or neg constant BALL_V_P: unsigned(9 downto 0):= to_unsigned(2,10); constant BALL_V_N: unsigned(9 downto 0):= unsigned(to_signed(-2,10)); -- round ball image type rom_type is array(0 to 7) of std_logic_vector(0 to 7); constant BALL_ROM: rom_type:= ( "00111100", "01111110", "11111111", "11111111", "11111111", "11111111", "01111110", "00111100"); signal rom_addr, rom_col: unsigned(2 downto 0); signal rom_data: std_logic_vector(7 downto 0); signal rom_bit: std_logic; -- object output signals -- new signal to indicate if scan coord is within ball signal wall_on, bar_on, sq_ball_on, rd_ball_on: std_logic; signal wall_rgb, bar_rgb, ball_rgb: std_logic_vector(2 downto 0); -- ====================================================================================================== begin process (clk, reset) begin if (reset = '1') then bar_y_reg <= (others => '0'); ball_x_reg <= (others => '0'); ball_y_reg <= (others => '0'); x_delta_reg <= ("0000000100"); y_delta_reg <= ("0000000100"); elsif (clk'event and clk = '1') then bar_y_reg <= bar_y_next; ball_x_reg <= ball_x_next; ball_y_reg <= ball_y_next; x_delta_reg <= x_delta_next; y_delta_reg <= y_delta_next; end if; end process; -- ====================================================================================================== pix_x <= unsigned(pixel_x); pix_y <= unsigned(pixel_y); -- refr_tick: 1-clock tick asserted at start of v_sync, e.g., when the screen is refreshed -- -- speed is 60 Hz refr_tick <= '1' when (pix_y = 481) and (pix_x = 0) else '0'; -- ====================================================================================================== -- wall left vertical stripe wall_on <= '1' when (WALL_X_L <= pix_x) and (pix_x <= WALL_X_R) else '0'; wall_rgb <= "001"; -- blue -- ====================================================================================================== -- pixel within paddle bar_y_t <= bar_y_reg; bar_y_b <= bar_y_t + BAR_Y_SIZE - 1; bar_on <= '1' when (BAR_X_L <= pix_x) and (pix_x <= BAR_X_R) and (bar_y_t <= pix_y) and (pix_y <= bar_y_b) else '0'; bar_rgb <= "010"; -- green -- ====================================================================================================== -- Process bar movement requests process( bar_y_reg, bar_y_b, bar_y_t, refr_tick, btn) begin bar_y_next <= bar_y_reg; -- no move if ( refr_tick = '1' ) then -- if btn 1 pressed and paddle not at bottom yet if ( btn(1) = '0' and bar_y_b < (MAX_Y - 1 - BAR_V)) then bar_y_next <= bar_y_reg + BAR_V; -- move down -- if btn 0 pressed and bar not at top yet elsif ( btn(0) = '0' and bar_y_t > BAR_V) then bar_y_next <= bar_y_reg - BAR_V; -- move up end if; end if; end process; -- ====================================================================================================== -- set coordinates of square ball. ball_x_l <= ball_x_reg; ball_y_t <= ball_y_reg; ball_x_r <= ball_x_l + BALL_SIZE - 1; ball_y_b <= ball_y_t + BALL_SIZE - 1; -- pixel within square ball sq_ball_on <= '1' when (ball_x_l <= pix_x) and (pix_x <= ball_x_r) and (ball_y_t <= pix_y) and (pix_y <= ball_y_b) else '0'; -- map scan coord to ROM addr/col -- use low order three bits of pixel and ball positions. -- ROM row rom_addr <= pix_y(2 downto 0) - ball_y_t(2 downto 0); -- ROM column rom_col <= pix_x(2 downto 0) - ball_x_l(2 downto 0); -- Get row data rom_data <= BALL_ROM(to_integer(rom_addr)); -- Get column bit rom_bit <= rom_data(to_integer(rom_col)); -- Turn ball on only if within square and the ROM bit is 1. rd_ball_on <= '1' when (sq_ball_on = '1') and (rom_bit = '1') else '0'; ball_rgb <= "100"; -- red -- Update the ball position 60 times per second. ball_x_next <= ball_x_reg + x_delta_reg when refr_tick = '1' else ball_x_reg; ball_y_next <= ball_y_reg + y_delta_reg when refr_tick = '1' else ball_y_reg; -- Set the value of the next ball position according to the boundaries. process(x_delta_reg, y_delta_reg, ball_y_t, ball_x_l, ball_x_r, ball_y_t, ball_y_b, bar_y_t, bar_y_b) begin x_delta_next <= x_delta_reg; y_delta_next <= y_delta_reg; -- ball reached top, make offset positive if ( ball_y_t < 1 ) then y_delta_next <= BALL_V_P; -- reached bottom, make negative elsif (ball_y_b > (MAX_Y - 1)) then y_delta_next <= BALL_V_N; -- reach wall, bounce back elsif (ball_x_l <= WALL_X_R ) then x_delta_next <= BALL_V_P; -- right corner of ball inside bar elsif ((BAR_X_L <= ball_x_r) and (ball_x_r <= BAR_X_R)) then -- some portion of ball hitting paddle, reverse direction if ((bar_y_t <= ball_y_b) and (ball_y_t <= bar_y_b)) then x_delta_next <= BALL_V_N; end if; end if; end process; -- ====================================================================================================== -- turn on the appropriate color depending on the current pixel position. process (video_on, wall_on, bar_on, rd_ball_on, wall_rgb, bar_rgb, ball_rgb) begin if (video_on = '0') then graph_rgb <= "000"; -- blank else if (wall_on = '1') then graph_rgb <= wall_rgb; elsif (bar_on = '1') then graph_rgb <= bar_rgb; elsif (rd_ball_on = '1') then graph_rgb <= ball_rgb; else graph_rgb <= "110"; -- yellow bkgnd end if; end if; end process; end sq_ball_arch;