Pong on the Mojo Revisited

In my previous post, I briefly discussed the implementation of a pseudo-pong game on my Mojo FPGA developer board.
I was able to implement the VHDL version that I linked to later in the post and it works much better.
The biggest challenge I had with it was converting the two-button input to a potentiometer input. It turns out all I had to do was convert an array to an integer as input to the paddle's left position.
Here is a link to the article describing the implementation that I used:
FPGA Pong From FPGACenter.com.
The VHDL was a big challenge for me because I had to learn it as I went along. It works just like Verilog, but the syntax is so different.
Also, I modified my User Constraints File to make the RGB an array for compatibility with this particular implementation.
Here are the new custom constraints:
NET "hsync" LOC = P50 | IOSTANDARD = LVTTL;
NET "vsync" LOC = P51 | IOSTANDARD = LVTTL;
NET "rgb<2>" LOC = P41 | IOSTANDARD = LVTTL;
NET "rgb<1>" LOC = P40 | IOSTANDARD = LVTTL;
NET "rgb<0>" LOC = P35 | IOSTANDARD = LVTTL;
Here is the code containing my changes (in the vga_control
and img_gen
modules):
vga_control:
library IEEE;
use IEEE.STD_LOGIC_1164.ALL;
use IEEE.STD_LOGIC_UNSIGNED.ALL;
entity vga_control is
Port ( clk : in STD_LOGIC;
start : in STD_LOGIC;
reset : in STD_LOGIC;
paddle : IN STD_LOGIC_VECTOR(9 downto 0);
rgb : out STD_LOGIC_VECTOR (2 downto 0);
h_s : out STD_LOGIC;
v_s : out STD_LOGIC);
end vga_control;
architecture Behavioral of vga_control is
COMPONENT img_gen
PORT( clk : IN std_logic;
x_control : IN std_logic_vector(9 downto 0);
paddle : IN std_logic_vector(9 downto 0);
y_control : IN std_logic_vector(9 downto 0);
video_on : IN std_logic;
rgb : OUT std_logic_vector(2 downto 0) );
END COMPONENT;
COMPONENT sync_mod
PORT( clk : IN std_logic;
reset : IN std_logic;
start : IN std_logic;
y_control : OUT std_logic_vector(9 downto 0);
x_control : OUT std_logic_vector(9 downto 0);
h_s : OUT std_logic;
v_s : OUT std_logic;
video_on : OUT std_logic );
END COMPONENT;
signal x,y:std_logic_vector(9 downto 0);
signal video:std_logic;
begin
U1: img_gen PORT MAP( clk =>clk , x_control => x, paddle => paddle ,
y_control => y, video_on =>video , rgb => rgb );
U2: sync_mod PORT MAP( clk => clk, reset => reset, start => start, y_control => y, x_control =>x ,
h_s => h_s , v_s => v_s, video_on =>video );
end Behavioral;
My changes are on lines 9, 20, and 41. This sends the output from the potentiometer hooked up to the ADC on the Mojo to the img_gen module where it's converted to position data for the paddle control.
Here's the modified img_gen
code:
library IEEE;
use IEEE.STD_LOGIC_1164.ALL;
use IEEE.STD_LOGIC_ARITH.ALL;
use IEEE.STD_LOGIC_UNSIGNED.ALL;
entity img_gen is
Port ( clk : in STD_LOGIC;
x_control : in STD_LOGIC_VECTOR(9 downto 0);
paddle : in STD_LOGIC_VECTOR(9 downto 0);
y_control : in STD_LOGIC_VECTOR(9 downto 0);
video_on : in STD_LOGIC;
rgb : out STD_LOGIC_VECTOR(2 downto 0));
end img_gen;
architecture Behavioral of img_gen is
--wall
constant wall_l:integer :=10;--the distance between wall and left side of screen
constant wall_t:integer :=10;--the distance between wall and top side of screen
constant wall_k:integer :=10;--wall thickness
signal wall_on:std_logic;
signal rgb_wall:std_logic_vector(2 downto 0);
--bar
signal bar_l, bar_l_next: integer:=100;
constant bar_t:integer :=420;--the distance between bar and top side of screen
constant bar_k:integer :=10;--bar thickness
constant bar_w:integer:=120;--bar width
constant bar_v:integer:=10;--velocity of the bar
signal bar_on:std_logic;
signal rgb_bar:std_logic_vector(2 downto 0);
--ball
signal ball_l,ball_l_next:integer :=100;--the distance between ball and left side of screen
signal ball_t,ball_t_next:integer :=100; --the distance between ball and top side of screen
constant ball_w:integer :=20;--ball Height
constant ball_u:integer :=20;--ball width
constant x_v,y_v:integer:=3;-- horizontal and vertical speeds of the ball
signal ball_on:std_logic;
signal rgb_ball:std_logic_vector(2 downto 0);
--refreshing(1/60)
signal refresh_reg,refresh_next:integer;
constant refresh_constant:integer:=830000;
signal refresh_tick:std_logic;
--ball animation
signal xv_reg,xv_next:integer:=3;--variable of the horizontal speed
signal yv_reg,yv_next:integer:=3;--variable of the vertical speed
--x,y pixel cursor
signal x,y:integer range 0 to 650;
--mux
signal vdbt:std_logic_vector(3 downto 0);
--buffer
signal rgb_reg,rgb_next:std_logic_vector(2 downto 0);
begin
--x,y pixel cursor
x <= conv_integer(x_control);
y <= conv_integer(y_control );
--refreshing
process(clk)
begin
if clk'event and clk='1' then
refresh_reg<=refresh_next;
end if;
end process;
refresh_next <= 0 when refresh_reg= refresh_constant else
refresh_reg+1;
refresh_tick <= '1' when refresh_reg = 0 else
'0';
--register part
process(clk)
begin
if clk'event and clk='1' then
ball_l <= ball_l_next;
ball_t <= ball_t_next;
xv_reg <= xv_next;
yv_reg <= yv_next;
bar_l <= bar_l_next;
end if;
end process;
--bar animation
process(refresh_tick,paddle)
begin
if refresh_tick= '1' then
bar_l_next <= conv_integer(paddle);
end if;
end process;
--ball animation
process(refresh_tick,ball_l,ball_t,xv_reg,yv_reg)
begin
ball_l_next <=ball_l;
ball_t_next <=ball_t;
xv_next <= xv_reg;
yv_next <= yv_reg;
if refresh_tick = '1' then
if ball_t > 400 and ball_l > (bar_l -ball_u) and ball_l < (bar_l +120) then --the ball hits the bar
yv_next <= -y_v ;
elsif ball_t < 35 then--The ball hits the wall
yv_next <= y_v;
end if;
if ball_l < 10 then --The ball hits the left side of the screen
xv_next <= x_v;
elsif ball_l> 600 then
xv_next <= -x_v ; --The ball hits the right side of the screen
end if;
ball_l_next <= ball_l + xv_reg;
ball_t_next <= ball_t + yv_reg;
end if;
end process;
--wall object
wall_on <= '1' when x > wall_l and x < (640-wall_l) and y> wall_t and y < (wall_t+ wall_k) else
'0';
rgb_wall <= "000";--Black
--bar object
bar_on <= '1' when x > bar_l and x < (bar_l+bar_w) and y> bar_t and y < (bar_t+ bar_k) else
'0';
rgb_bar <= "001";--blue
--ball object
ball_on <= '1' when x > ball_l and x < (ball_l+ball_u) and y> ball_t and y < (ball_t+ ball_w) else
'0';
rgb_ball <= "010"; --Green
--buffer
process(clk)
begin
if clk'event and clk = '1' then
rgb_reg <= rgb_next;
end if;
end process;
--mux
vdbt<=video_on & wall_on & bar_on &ball_on;
with vdbt select
rgb_next <= "100" when "1000",--Background of the screen is red
rgb_wall when "1100",
rgb_wall when "1101",
rgb_bar when "1010",
rgb_bar when "1011",
rgb_ball when "1001",
"000" when others;
--output
rgb <= rgb_reg;
end Behavioral;
My changes are on lines 10 and 95 to make the potentiometer control the paddle.