Implementing Ping Pong game on an FPGA in VHDL

Background

Pong - a timeless and iconic classic. Code in VHDL.




VHDL Implementation on an Altera DE1 FPGA:

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

entity ERSPrac3 is
 port (
   CLOCK_50 : IN std_logic;
   SW : in std_logic_vector(9 DOWNTO 0);
   LEDR : out std_logic_vector(9 DOWNTO 0);
   LEDG : out std_logic_vector(7 DOWNTO 0);
   KEY : in std_logic_vector(3 DOWNTO 0);
   
   HEX0 : OUT std_logic_vector(6 DOWNTO 0);
   HEX1 : OUT std_logic_vector(6 DOWNTO 0);
   HEX2 : OUT std_logic_vector(6 DOWNTO 0);
   HEX3 : OUT std_logic_vector(6 DOWNTO 0)
   
   
   ) ;
end ERSPrac3;

architecture behaviour of ERSPrac3 is
 type stateType is (S_HoldR, S_HoldL, RL1, RL2, RL3, RL4, RL5, RL6, RL7 , RR1,RR2,RR3,RR4,RR5,RR6, RR7, WINL, WINR);
 signal currentState : stateType; 
 signal nextState: stateType;
 
 signal clk_1 : std_logic;
 
 signal scoreL : unsigned(3 DOWNTO 0) := "0000";
 
 signal scoreR : unsigned(3 DOWNTO 0) := "0000";
 
 signal outvector : std_logic_vector(7 DOWNTO 0);
 
begin

clock_gen : process(CLOCK_50)
 variable count : natural;
begin

 if rising_edge(CLOCK_50) then
  clk_1 <= '0';
  count := count + 1;
  if count = 12500000 then
   clk_1 <= '1';
   count := 0;
  end if;
 end if;

end process;

statereg : process(clk_1) -- state:
begin
 -- state register logic stuff
 
 if (SW(9) = '1') then
  currentState <= S_HoldR;
 elsif (rising_edge(clk_1) and SW(9) = '0') then
  currentState <= nextState;
 end if;

end process;

next_statelogic: process (currentState)
begin

 case currentState is
  when S_HoldL =>
   if (KEY(3) = '1') then
    nextState <= RR1;
   else
    nextState <= S_HoldL;
   end if;
   
  when S_HoldR =>
   if (KEY(0) = '1') then
    nextState <= RL1;
   else
    nextState <= S_HoldR;
   end if;
   
  when RL1 =>
   nextState <= RL2;
  
  when RL2 =>
   nextState <= RL3;
   
  when RL3 =>
   nextState <= RL4;
  
  when RL4 =>
   nextState <= RL5;
   
  when RL5 =>
   nextState <= RL6;
   
  when RL6 =>
   nextState <= RL7;
   
  when RL7 =>
   if (KEY(3) = '0') then
    nextState <= S_HoldL;
   else
    -- SCORE INCREASE, SERVE FROM RIGHT AGAIN, RIGHT PLAYER WINS A POINT
    nextState <= WINR;

   end if;
   
  when RR1 =>
   nextState <= RR2;
  
  when RR2 =>
   nextState <= RR3;
   
  when RR3 =>
   nextState <= RR4;
  
  when RR4 =>
   nextState <= RR5;
   
  when RR5 =>
   nextState <= RR6;
   
  when RR6 =>
   nextState <= RR7;
   
  when RR7 =>
   if (KEY(0) = '0') then
    nextState <= S_HoldR;
   else
    -- SCORE INCREASE LEFT PLAYER, SERVE FROM RIGHT AGAIN
    nextState <= WINL;
    
   end if;
   
  when WINL =>
    nextState <= S_HoldR;
   
  when WINR =>
    nextState <= S_HoldR;
    scoreR <= scoreR + 1;
  
 end case;
   
end process;
 
out_logic : process (currentState)
begin

 LEDR(9 DOWNTO 0) <= (others => '0');
 case currentState is
  when S_HoldR =>
   outvector <= "00000001";
   
  when S_HoldL =>
   outvector <= "10000000";
  
  when RL1 =>
   outvector <= "00000010";
  when RL2 =>
   outvector <= "00000100";
  when RL3 =>
   outvector <= "00001000";
  when RL4 =>
   outvector <= "00010000";
  when RL5 =>
   outvector <= "00100000";
  when RL6 =>
   outvector <= "01000000";
  when RL7 =>
   outvector <= "10000000";
   
  when RR1 =>
   outvector <= "01000000";
  when RR2 =>
   outvector <= "00100000";
  when RR3 =>
   outvector <= "00010000";
  when RR4 =>
   outvector <= "00001000";
  when RR5 =>
   outvector <= "00000100";
  when RR6 =>
   outvector <= "00000010";
  when RR7 =>
   outvector <= "00000001";
  when WINL =>
   outvector <= "11111111";
   LEDR(9 DOWNTO 0) <= (others => '1');
  when WINR =>
   outvector <= "11111111";
   LEDR(9 DOWNTO 0) <= (others => '1');

   
 end case;

 LEDG(7 DOWNTO 0) <= outvector(7 DOWNTO 0);
 
end process;

score_disp : process(clk_1)
begin

 if (rising_edge(clk_1)) then
  CASE scoreL IS
    WHEN "0000"=>HEX3<="1000000";
    WHEN "0001"=>HEX3<="1111001";
    WHEN "0010"=>HEX3<="0100100";
    WHEN "0011"=>HEX3<="0110000";
    WHEN "0100"=>HEX3<="0011001";
    WHEN "0101"=>HEX3<="0010010";
    WHEN "0110"=>HEX3<="0000010";
    WHEN "0111"=>HEX3<="1111000";
    WHEN "1000"=>HEX3<="0000000";
    WHEN "1001"=>HEX3<="0011000";
    WHEN OTHERS=>HEX3<="1111111";
  END CASE;
  
  CASE scoreR IS
    WHEN "0000"=>HEX0<="1000000";
    WHEN "0001"=>HEX0<="1111001";
    WHEN "0010"=>HEX0<="0100100";
    WHEN "0011"=>HEX0<="0110000";
    WHEN "0100"=>HEX0<="0011001";
    WHEN "0101"=>HEX0<="0010010";
    WHEN "0110"=>HEX0<="0000010";
    WHEN "0111"=>HEX0<="1111000";
    WHEN "1000"=>HEX0<="0000000";
    WHEN "1001"=>HEX0<="0011000";
    WHEN OTHERS=>HEX0<="1111111";
  END CASE;
  

  HEX1 <= (others => '1');
  HEX2 <= (others => '1');
  
  end if;
end process;
end behaviour;

Implementation of an 8-bit Hamming code error-detecting decoder on an Altera FPGA

Background:

The system takes a potentially corrupted 8-bit Hamming code as input and at its output will display a diagnosis of the error which occurred in transmission. The encoded binary word contains four data bits and four redundancy (parity-check) bits. The code is defined by a parity-check matrix:



A comprehensive explanation will soon follow on this page. But for now, use Google to find what you want to know.



VHDL Implementation on an Altera DE1 FPGA:


library ieee;
use ieee.std_logic_1164.all;

entity ERSPrac2 is
port (
  LEDG : OUT std_logic_vector(7 DOWNTO 0);
  LEDR : OUT std_logic_vector(9 DOWNTO 0);
  SW : IN std_logic_vector(9 DOWNTO 0);
  
  
  HEX0 : OUT std_logic_vector(6 DOWNTO 0);
  HEX1 : OUT std_logic_vector(6 DOWNTO 0);
  HEX2 : OUT std_logic_vector(6 DOWNTO 0);
  HEX3 : OUT std_logic_vector(6 DOWNTO 0)
  );
  
end ERSPrac2;

architecture behave of ERSPrac2 is
  signal pg1out : std_logic := '0';
  signal pg2out : std_logic := '0';
  signal pg3out : std_logic := '0';
  signal pg4out : std_logic := '0';
  
  signal syndrome : std_logic_vector(3 downto 0);
  signal BCDout : std_logic_vector(3 downto 0);
  
  --signal input : std_logic_vector(7 downto 0);

begin

 PROCESS(SW(9)) is
  --variable c_zero : std_logic := '0'
 begin
 
 if (SW(9) = '1') then
 
  -- load the states of switches into red leds for aesthetics
  LEDR(7 downto 0) <= SW(7 downto 0);
  
  -- parity generators according to matrix H
  pg1out <= SW(7) XOR SW(2) XOR SW(1) XOR SW(0);
  pg2out <= SW(6) XOR SW(3) XOR SW(1) XOR SW(0);
  pg3out <= SW(5) XOR SW(3) XOR SW(2) XOR SW(0);
  pg4out <= SW(4) XOR SW(3) XOR SW(2) XOR SW(1);
  
  --LEDG(3) <= pg1out;
  --LEDG(2) <= pg2out;
  --LEDG(1) <= pg3out;
  --LEDG(0) <= pg4out;
  
  syndrome(3) <= pg1out;
  syndrome(2) <= pg2out;
  syndrome(1) <= pg3out;
  syndrome(0) <= pg4out;
  
  LEDG(3 DOWNTO 0) <= syndrome(3 DOWNTO 0);
  
  -- at this point, syndrome vector is in syndrome signal vector.
  
  CASE syndrome IS
   WHEN "0000"=>BCDout<="1001";
   WHEN "0001"=>BCDout<="0100";
   WHEN "0010"=>BCDout<="0101";
   WHEN "0100"=>BCDout<="0110";
   WHEN "0111"=>BCDout<="0011";
   WHEN "1000"=>BCDout<="0111";
   WHEN "1011"=>BCDout<="0010";   
   WHEN "1101"=>BCDout<="0001";
   WHEN "1110"=>BCDout<="0000";
   
   WHEN OTHERS=>BCDout<="1000";
  END CASE;
   
  -- at this point we have the syndrome vector converted into a BCD type output in signal BCDout
  -- now display the BCDout in hex0.
  
  CASE BCDout IS
    WHEN "0000"=>HEX0<="1000000";
    WHEN "0001"=>HEX0<="1111001";
    WHEN "0010"=>HEX0<="0100100";
    WHEN "0011"=>HEX0<="0110000";
    WHEN "0100"=>HEX0<="0011001";
    WHEN "0101"=>HEX0<="0010010";
    WHEN "0110"=>HEX0<="0000010";
    WHEN "0111"=>HEX0<="1111000";
    WHEN "1000"=>HEX0<="0000000";
    WHEN "1001"=>HEX0<="0011000";
    WHEN "1010"=>HEX0<="0001000"; -- A
    WHEN "1011"=>HEX0<="0000011"; -- B
    WHEN "1100"=>HEX0<="0100111"; -- C
    WHEN "1101"=>HEX0<="0100001"; -- d
    WHEN "1110"=>HEX0<="0000110"; -- e
    WHEN OTHERS=>HEX0<="1111111";
  END CASE;
  
  
 else
 
  HEX0 <= (others => '1');
  HEX1 <= (others => '1');
  HEX2 <= (others => '1');
  HEX3 <= (others => '1');
  
  LEDG(7 DOWNTO 0) <= (others => '0');
  LEDR(9 DOWNTO 0) <= (others => '0');
 
 end if;
 
 end process;
 
end behave;

How to implement a 3-bit Adder in VHDL (For an Altera FPGA utilizing a 7-segment display)

This page covers some of the design process and finishes with VHDL code, scroll down for VHDL.

Adders - Introduction

Addition is arguably one of the most important arithmetic operations used in the world we live in. Being one of the first mathematical operations ever conceived, it is a fundamental problem in everyday life. Adders are incredibly important in digital electronics and computers. Modern computers perform millions of addition operations a second. Adders are extremely important not only in the ALU but also in other parts of the processor where memory addresses must be calculated.
The value of the adder circuit in modern electronics is paramount.



Building Blocks of the 3-bit Adder

Half Adder:

The half adder adds two single binary digits A and B. It has two outputs, sum (S) and carry (C). A carry bit is required to 'carry' the overflow product present in some addition cases.


Full Adder:

A full adder adds binary numbers and calculates both for values carried in, as well as carried out. A one-bit full adder adds three one-bit numbers, often written as A, B, and Cin; A and B are the operands, and Cin is a bit carried in from the previous less significant stage. Just like the half adder, it produces two output bits Cout and S.


With the case of the Full Adder, we see that the last OR gate can actually be replaced with an XOR gate without affecting the working of the circuit. This fact may be helpful in determining the resources needed for our circuit at a later stage as we can completely get rid of OR gates.



Putting it all together:

By laying the 1 bit full adders in an array, we can simulate the addition of binary numbers. Since the first adder of the circuit does not take a carry in input, we can substitute it with a half-adder to cut down on resources and number of gates required. The block diagrams below actually represent the circuits drawn above.





Implementation in VHDL (For Altera DE1 using Altera's Pin-out plan) :

Pin mapping for Altera Quartus - click here.



library ieee;
use ieee.std_logic_1164.all;

entity ERS220eg is
port (
  LEDG : OUT std_logic_vector(7 DOWNTO 0);
  LEDR : OUT std_logic_vector(9 DOWNTO 0);
  SW : IN std_logic_vector(9 DOWNTO 0);
  HEX0 : OUT std_logic_vector(6 DOWNTO 0);
  HEX1 : OUT std_logic_vector(6 DOWNTO 0);
  HEX2 : OUT std_logic_vector(6 DOWNTO 0);
  HEX3 : OUT std_logic_vector(6 DOWNTO 0)
  
  );
  
end ERS220eg;

architecture behave of ERS220eg is
begin
 
 LEDG(7) <= SW(9);

 PROCESS(SW(9)) is
 
  variable c_zero : std_logic := '0';
  variable c_one : std_logic := '0';
  variable c_two : std_logic := '0';
  variable ans : std_logic_vector(3 DOWNTO 0);
  variable a,b : std_logic_vector(2 DOWNTO 0);
  
  begin
  
  LEDR(9) <= SW(9);
  
  if (SW(9) = '1') then
  
   HEX0 <= (others => '0');
   HEX1 <= (others => '1');
  
 --  LEDG(0) <= SW(0) AND SW(1);
 --  LEDG(1) <= SW(0) OR SW(1);
 --  LEDG(2) <= SW(0) XOR SW(1);

   LEDR(5 DOWNTO 0) <= SW(5 DOWNTO 0);

   -- LEDG0 is D0 which is carry out from half adder:
   -- LSB:
   
   ANS(0) := SW(0) XOR SW(3);
   
   c_zero := SW(0) AND SW(3);
   
   -- Second bit - first full adder
   
   ANS(1) := c_zero XOR (SW(1) XOR SW(4));
   c_one := (SW(1) AND SW(4)) XOR (c_zero AND (SW(1) XOR SW(4)));
   
   -- Third bit - second full adder
   
   ANS(2) := c_one XOR (SW(2) XOR SW(5));
   c_two := (SW(2) AND SW(5)) XOR (c_one AND (SW(2) XOR SW(5)));
   
   -- resultant carry
   ANS(3) := c_two;
   
   LEDG(3 DOWNTO 0) <= ANS(3 DOWNTO 0);
   
   -- 7 seg:
   
   a := SW(2 DOWNTO 0);
   b := SW(5 DOWNTO 3);
   
   
   CASE b IS
    WHEN "000"=>HEX3<="1000000";
    WHEN "001"=>HEX3<="1111001";
    WHEN "010"=>HEX3<="0100100";
    WHEN "011"=>HEX3<="0110000";
    WHEN "100"=>HEX3<="0011001";
    WHEN "101"=>HEX3<="0010010";
    WHEN "110"=>HEX3<="0000010";
    WHEN "111"=>HEX3<="1111000";
    WHEN OTHERS=>HEX3<="1111111";
   END CASE;
   
   CASE a IS
    WHEN "000"=>HEX2<="1000000";
    WHEN "001"=>HEX2<="1111001";
    WHEN "010"=>HEX2<="0100100";
    WHEN "011"=>HEX2<="0110000";
    WHEN "100"=>HEX2<="0011001";
    WHEN "101"=>HEX2<="0010010";
    WHEN "110"=>HEX2<="0000010";
    WHEN "111"=>HEX2<="1111000";
    WHEN OTHERS=>HEX2<="1111111";
   END CASE;
   
   HEX1 <= "0110111";
   
   
   CASE ANS IS
    WHEN "0000"=>HEX0<="1000000";
    WHEN "0001"=>HEX0<="1111001";
    WHEN "0010"=>HEX0<="0100100";
    WHEN "0011"=>HEX0<="0110000";
    WHEN "0100"=>HEX0<="0011001";
    WHEN "0101"=>HEX0<="0010010";
    WHEN "0110"=>HEX0<="0000010";
    WHEN "0111"=>HEX0<="1111000";
    WHEN "1000"=>HEX0<="0000000";
    WHEN "1001"=>HEX0<="0011000";
    WHEN "1010"=>HEX0<="0001000"; -- A
    WHEN "1011"=>HEX0<="0000011"; -- B
    WHEN "1100"=>HEX0<="0100111"; -- C
    WHEN "1101"=>HEX0<="0100001"; -- d
    WHEN "1110"=>HEX0<="0000110"; -- e
    WHEN OTHERS=>HEX0<="1111111";
   END CASE;
   
  else
  
   HEX0 <= (others => '1');
   HEX1 <= (others => '1');
   HEX2 <= (others => '1');
   HEX3 <= (others => '1');
  
   LEDG(6 DOWNTO 0) <= (others => '0');
   LEDR(6 DOWNTO 0) <= (others => '0');
  
  end if;
 end process;
end behave;

Content brought to you by RAPAX