Introduction: FPGA Tic Tac Toe

Picture of FPGA Tic Tac Toe
"Tic Tac Toe? What's that? I've never heard of that."

- Nobody ever

By Ryan Frawley and Derek Nguyen

This guide will show you how to make a working Tic Tac Toe game in VHDL on a Nexys 2 FPGA board. This tutorial was done for part of a Cal Poly CPE 133 final project.

Parts list:

- RGB LED

http://www.amazon.com/microtivity-IL612-Diffused-C...

- Breadboards x3

http://www.amazon.com/microtivity-IB830-830-point-...

- Buttons

http://www.amazon.com/microtivity-IM206-6x6x6mm-Ta...

- Jumper Cable

http://www.amazon.com/Wosang-Solderless-Flexible-B...

- NEXYS 2

http://www.digilentinc.com/Products/Detail.cfm?Pro...

WARNING: We are assuming that you, the reader, have some kind of experience with Xilinx and basic understanding of Digital Design. If "YES!" then what you're about to read won't seem like a black box.

Step 1: Single LED Controller

Picture of Single LED Controller

For this step, we will create a little controller for each individual LED.
We will be using the fundamental D - Flip Flop with a synchronous enable and an asynchronous reset.

Since our LED is the fancy RGB LED, it is wiser to use a 2 bit bus to determine which color gets turned on. To do so, we need 2 D Flip Flops -- one for each color of the LED.

Each of the flip flops will share a clock, the input of the button, and a reset button. However, each of the D flip flops will get a D value determined by the multi-LEDs controller.

The press of the button will latch in the D value of each flip flop and outputs it. The output will be a 2 bit bus formed by the individual Q output of each flip flop and another 2 bit bus formed by the Q inverse of each flip flop.

The 2 bit bus consisting of the Q inverse output will be directly connected to the LED output signal. Meanwhile, the 2 bit bus consisting of the Q output will go to the multi-LEDs controller to determine which player gets to go next.

We will be using 9 of these flip flops for each button.

entity Single_LED_Controller is    
      port (Clock        : in std_logic;
            Button    : in std_logic;
            Reset        : in std_logic;
            Player1    : in std_logic;
            Player2    : in std_logic;
            LED_Output    : out std_logic_vector(1 downto 0);
            Handle_Input : out std_logic_vector(1 downto 0));            
end Single_LED_Controller;

architecture controller of Single_LED_Controller is
component D_FlipFlop is
    port (Enable: in std_logic;
            D         : in std_logic;
            Reset    : in std_logic;
            Clock    : in std_logic;
            Q         : out std_logic);
end component;

signal temp1, temp2 : std_logic;

begin
    D_FF_Main_1 : D_FlipFlop port map(Button, Player1, Reset, Clock, temp1);
    D_FF_Main_2 : D_FlipFlop port map(Button, Player2, Reset, Clock, temp2);
    
    LED_Output(0) <= not(temp1);
    LED_Output(1) <= not(temp2);

    Handle_Input(0) <= temp1;
    Handle_Input(1) <= temp2;

end controller;

Step 2: Configure Your Multi-LEDs Controller

Picture of Configure Your Multi-LEDs Controller

Determining who gets to go next is simple.


Remember the good old T Flip Flop that let the user “toggle” the output value between 1 and 0? We won’t be using that. With the Nexys 2 Board's “advanced” clock speed, life will be very difficult for you if you ever decide to use that for this game.

Another major flaw of using the T Flip flop is that we have to make sure the user can’t change the output value once he or she already pressed the button -- that’s a no no.

Therefore, for this design, we have decided to use XOR logic gates to do the function of switching between each players. The fundamental idea of XOR is that it will output ‘1’ if there is an odd number of 1's and output a ‘0’ if there is an even number of 1's. We will be using that idea to determine which player's turn it is.

For the first layer of XOR gates, their input will be the 2 bit Q output of the D-flip flops setup in the single LED controller. Each of these XOR gates correspond to a button of our Tic Tac Toe game.

If you take a look at the schematic, the output of the module will be a 2 bit output: Index 0 will be the direct output of the XOR and Index 1 will be its inverse. This combination of “10”, red, and “01”, blue, will be switching back and forth determine on the number of buttons that have been pressed.

The output of the XOR will be feed straight to each individual Single LED Controller for the D’s input of the D flip flops set up. Since we will have either a “01” or “10” output of the D flip flops setup, case “11” or “00” will never be the input for our XOR setup.


WOW! That was a lot to explain. But trust us, it is infinitely easier than going with the T flip flop or the linear shift register route. YUCK!

entity Multi_LED_Handle is   
      port (Reset        : in std_logic;
            Input1     : in std_logic_vector(1 downto 0);
            Input2     : in std_logic_vector(1 downto 0);
            Input3     : in std_logic_vector(1 downto 0);
            Input4     : in std_logic_vector(1 downto 0);
            Input5     : in std_logic_vector(1 downto 0);
            Input6     : in std_logic_vector(1 downto 0);
            Input7     : in std_logic_vector(1 downto 0);
            Input8     : in std_logic_vector(1 downto 0);
            Input9     : in std_logic_vector(1 downto 0);
            Output     : out std_logic_vector(1 downto 0));
end Multi_LED_Handle;

architecture Dataflow of Multi_LED_Handle is

signal temp : std_logic_vector(9 downto 0);
begin
    
    -- XOR First level
    temp(0) <= input1(0) XOR input1(1);
    temp(1) <= input2(0) XOR input2(1);
    temp(2) <= input3(0) XOR input3(1);
    temp(3) <= input4(0) XOR input4(1);
    temp(4) <= input5(0) XOR input5(1);
    temp(5) <= input6(0) XOR input6(1);
    temp(6) <= input7(0) XOR input7(1);
    temp(7) <= input8(0) XOR input8(1);
    temp(8) <= input9(0) XOR input9(1);
    
    -- XOR Second level
    temp(9) <= temp(0) XOR temp(1) XOR temp(2) XOR temp(3) XOR temp(4) XOR temp(5) XOR temp(6) XOR temp(7) XOR temp(8);
    
    Output(0) <= temp(9);
    Output(1) <= not(temp(9));

end Dataflow;

Step 3: Victory Conditions Module

Picture of Victory Conditions Module

Now that we have the LEDs and buttons working, we need to tell the board to look for win conditions (or lack thereof). In order to do this, we will be using a couple big "if" statements inside of processes.

The first process checks all 8 possible wins (3 horizontal, 3 vertical, and 2 diagonal) for each player for a total of 16 if statements.

process (in1t, in2t, in3t, in4t, in5t, in6t, in7t, in8t, in9t) is begin
	--player 1 conditions
	
	-- horizontal
	if (in1t(1) = '1' and in2t(1) = '1' and in3t(1) = '1') then
		p1 <= '1';	
	elsif (in4t(1) = '1' and in5t(1) = '1' and in6t(1) = '1') then
		p1 <= '1';	

	elsif (in7t(1) = '1' and in8t(1) = '1' and in9t(1) = '1') then
		p1 <= '1';	
	-- vertical
	elsif (in1t(1) = '1' and in4t(1) = '1' and in7t(1) = '1') then
		p1 <= '1';
	elsif (in2t(1) = '1' and in5t(1) = '1' and in8t(1) = '1') then
		p1 <= '1';	
	elsif (in3t(1) = '1' and in6t(1) = '1' and in9t(1) = '1') then
		p1 <= '1';	
	-- diagonal
	elsif (in1t(1) = '1' and in5t(1) = '1' and in9t(1) = '1') then
		p1 <= '1';
	elsif (in3t(1) = '1' and in5t(1) = '1' and in7t(1) = '1') then
		p1 <= '1';
	else 
		p1 <= '0';
	end if;	-- player 2 conditions
	
	-- horizontal
	if (in1t(0) = '1' and in2t(0) = '1' and in3t(0) = '1') then
		p2 <= '1';
	elsif (in4t(0) = '1' and in5t(0) = '1' and in6t(0) = '1') then
		p2 <= '1';
	elsif (in7t(0) = '1' and in8t(0) = '1' and in9t(0) = '1') then
		p2 <= '1';
	
	
	-- vertical
	
	elsif (in1t(0) = '1' and in4t(0) = '1' and in7t(0) = '1') then
		p2 <= '1';
	elsif (in2t(0) = '1' and in5t(0) = '1' and in8t(0) = '1') then
		p2 <= '1';
	elsif (in3t(0) = '1' and in6t(0) = '1' and in9t(0) = '1') then
		p2 <= '1';
	
	-- diagonal
	
	elsif (in1t(0) = '1' and in5t(0) = '1' and in9t(0) = '1') then
		p2 <= '1';
	elsif (in3t(0) = '1' and in5t(0) = '1' and in7t(0) = '1') then
		p2 <= '1';
	else 
		p2 <= '0';
	end if;
end process;

Next, we must make a much smaller second process that compares the P1 and P2 win state to determine if there was a tie.

process (P1, P2) is<br>begin
	if (P1 = '0' and P2 = '0') then
		T <= '1';
	else
		T <= '0';
	end if;
end process;

Finally, we must pass the values stored in the signals to our outputs.

p1win <= P1;
p2win <= P2;
tie <= t;

Step 4: 7 Segment Decoder Module

We will be using the 7 segment display on the Nexys 2 to display whose turn it is. In order to do this, we'll use a simple 7 segment decoder.

entity Seg is   

Port (     Turn : in  STD_LOGIC_VECTOR (1 downto 0);
           Clock : in STD_LOGIC;
           Anode : out  STD_LOGIC_VECTOR (3 downto 0);
           Segment : out  STD_LOGIC_VECTOR (7 downto 0));
end Seg;

architecture Behavioral of Seg is
signal Seg_temp : std_logic_vector(7 downto 0) := "11111111";

begin
anode <= "1110";
process (turn, clock) is
begin
	if (rising_edge(clock)) then
	     if (turn = "01") then
	         seg_temp <= "10011111";
	     elsif (turn = "10") then
	         seg_temp <= "00100101";
	     else null;
	     end if;
	end if;
end process;
segment <= seg_temp;
end Behavioral;

As you can see, we started out by enabling only the last anode. After that, we check the current turn and change the segment output accordingly. For segment mapping, check out the Nexys 2 reference manual or the Nexys 3 reference manual.

Step 5: Main Game Module

Picture of Main Game Module

The VHDL section of the game is almost complete! All we have to do now is connect all of the modules we just wrote. First, we need to declare all of our inputs and outputs. We have 9 separate LED outputs o1-o9 plus one extra (o10) that lights up to show who won at the end of the game. There is also a reset button that clears the board once the game ends.

entity Main_Game_Board is
	port (CLK		  : in std_logic;
		BTN		  : in std_logic_vector(8 downto 0);
		MAIN_RESET : in std_logic;
		o1	: out std_logic_vector(1 downto 0);
		o2	: out std_logic_vector(1 downto 0);
		o3	: out std_logic_vector(1 downto 0);
		o4	: out std_logic_vector(1 downto 0);
		o5	: out std_logic_vector(1 downto 0);
		o6	: out std_logic_vector(1 downto 0);
		o7	: out std_logic_vector(1 downto 0);
		o8	: out std_logic_vector(1 downto 0);
		o9	: out std_logic_vector(1 downto 0);
		o10 		  : out std_logic_vector(1 downto 0);
		MAIN_AN 	  : out  STD_LOGIC_VECTOR(3 downto 0);
                MAIN_SEG   : out  STD_LOGIC_VECTOR(7 downto 0));
end Main_Game_Board

Next, we need to integrate all of the modules we wrote. In order to do this, we'll use a bunch of components.

component Single_LED_Controller is
		port (Clock				: in std_logic;
			Button			: in std_logic;
			Reset				: in std_logic;
			Player1			: in std_logic;
			Player2			: in std_logic;
			LED_Output		: out std_logic_vector(1 downto 0);
			Handle_Input 	: out std_logic_vector(1 downto 0));		
end component;
component Multi_LED_Handle is
	port (Reset		: in std_logic;
			Input1 	: in std_logic_vector(1 downto 0);
			Input2 	: in std_logic_vector(1 downto 0);
			Input3 	: in std_logic_vector(1 downto 0);
			Input4 	: in std_logic_vector(1 downto 0);
			Input5 	: in std_logic_vector(1 downto 0);
			Input6 	: in std_logic_vector(1 downto 0);
			Input7 	: in std_logic_vector(1 downto 0);
			Input8 	: in std_logic_vector(1 downto 0);
			Input9 	: in std_logic_vector(1 downto 0);
			Output 	: out std_logic_vector(1 downto 0));
end component;
component Victory is
    Port ( in1 : in  STD_LOGIC_VECTOR(1 downto 0);
           in2 : in  STD_LOGIC_VECTOR(1 downto 0);
           in3 : in  STD_LOGIC_VECTOR(1 downto 0);
           in4 : in  STD_LOGIC_VECTOR(1 downto 0);
           in5 : in  STD_LOGIC_VECTOR(1 downto 0);
           in6 : in  STD_LOGIC_VECTOR(1 downto 0);
           in7 : in  STD_LOGIC_VECTOR(1 downto 0);
           in8 : in  STD_LOGIC_VECTOR(1 downto 0);
           in9 : in  STD_LOGIC_VECTOR(1 downto 0);
	   Tie : out STD_LOGIC;
           P1win : out  STD_LOGIC;
           P2win : out  STD_LOGIC);
end component;
component Seg is
    Port ( Turn : in  STD_LOGIC_VECTOR (1 downto 0);
	   Clock : in STD_LOGIC;
           Anode : out  STD_LOGIC_VECTOR (3 downto 0);
           Segment : out  STD_LOGIC_VECTOR (7 downto 0));
end component;
component End_Tie is
	port (Button		: in std_logic_vector(8 downto 0);
			Reset			: in std_logic;
			clock			: in std_logic;
			Judgement	: out std_logic);
end component;

Now all that's left is to create signals and pass them between the inputs and outputs of the modules we wrote. After the signals go through all of the modules, they are sent to the outputs of the main game module.

signal PL: std_logic_vector(1 downto 0) := "10";
signal H1, H2, H3, H4, H5, H6, H7, H8, H9 : std_logic_vector(1 downto 0) := "00";
signal T, P1, P2 : std_logic;
signal An : STD_LOGIC_VECTOR (3 downto 0);
signal Seg_temp : STD_LOGIC_VECTOR (7 downto 0);
signal Bn : std_logic_vector (8 downto 0);
signal Judgement_temp : std_logic;
begin
	process(T, BTN, CLK) is
	begin
		if (rising_edge(CLK)) then
		if (T = '0' or Judgement_temp = '1') then
			Bn <= "000000000";
		else 
			Bn(0) <= BTN(0);
			Bn(1) <= BTN(1);
			Bn(2) <= BTN(2);
			Bn(3) <= BTN(3);
			Bn(4) <= BTN(4);
			Bn(5) <= BTN(5);
			Bn(6) <= BTN(6);
			Bn(7) <= BTN(7);
			Bn(8) <= BTN(8);
		end if;
		end if;
	end process;</p><p>	LED1 : Single_LED_Controller port map (CLK, BN(0), MAIN_RESET, PL(0), PL(1), o1, H1);
	LED2 : Single_LED_Controller port map (CLK, BN(1), MAIN_RESET, PL(0), PL(1), o2, H2);
	LED3 : Single_LED_Controller port map (CLK, BN(2), MAIN_RESET, PL(0), PL(1), o3, H3);
	LED4 : Single_LED_Controller port map (CLK, BN(3), MAIN_RESET, PL(0), PL(1), o4, H4);
	LED5 : Single_LED_Controller port map (CLK, BN(4), MAIN_RESET, PL(0), PL(1), o5, H5);
	LED6 : Single_LED_Controller port map (CLK, BN(5), MAIN_RESET, PL(0), PL(1), o6, H6);
	LED7 : Single_LED_Controller port map (CLK, BN(6), MAIN_RESET, PL(0), PL(1), o7, H7);
	LED8 : Single_LED_Controller port map (CLK, BN(7), MAIN_RESET, PL(0), PL(1), o8, H8);
	LED9 : Single_LED_Controller port map (CLK, BN(8), MAIN_RESET, PL(0), PL(1), o9, H9);	

MLEDH : Multi_LED_Handle port map(MAIN_RESET, H1, H2, H3, H4, H5, H6, H7, H8, H9, PL);
	
	Victory_module : victory port map(H1, H2, H3, H4, H5, H6, H7, H8, H9, T, P1, P2);
	
	display : seg port map(PL, CLK, An, Seg_temp);
	
	End_game : End_tie port map(Bn, MAIN_RESET, CLK, Judgement_temp);
	
	process(T, P1, P2, An, Seg_temp, Judgement_temp, CLK) is
	begin
	if (rising_edge(CLK)) then
		if(T = '1') then
			o10 <= "00";
			MAIN_AN <= an;
			MAIN_SEG <= Seg_temp;
		elsif(P1 = '1') then
			o10 <= "01";
			MAIN_AN <= "1111";
			MAIN_SEG <= "11111111";
		elsif(P2 = '1') then
			o10 <= "10";
			MAIN_AN <= "1111";
			MAIN_SEG <= "11111111";
		else 
			o10 <= "11";
		end if;
		if(Judgement_temp = '1') then
			MAIN_AN <= "1111";
			MAIN_SEG <= "11111111";	
		end if;
	end if;
	end process;

Step 6: Ending the Game With a Tie

This module is different. We considered this to be the last baby step before you can run free.

What happens when we end with a tie? Do you want the user to continue changing the input?

No, we do not want the user to change his or her input. That would be cheating!

To prevent the user to change their input once the game ends with a tie and no buttons are left to press, we will be using 9 D flip flops with D set to ‘1’ automatically to determine if the buttons have been pressed or not.

Then, using the AND gate, we will “And” all of the outputs of the D flip flops together. The module’s output will get the result of this AND gate.

In the Main Game board module, If the output of the END-Tie module equals to ‘1’ then the button will all get the value of ‘0’. It means that the user won’t be able to press any button and have it register.

entity End_Tie is
      port (Button        : in std_logic_vector(8 downto 0);
            Reset            : in std_logic;
            clock            : in std_logic;
            Judgement    : out std_logic);
end End_Tie;

architecture Behavioral of End_Tie is

component D_FlipFlop is
    port (Enable: in std_logic;
            D         : in std_logic;
            Reset    : in std_logic;
            Clock    : in std_logic;
            Q         : out std_logic);
end component;

signal btn0, btn1, btn2, btn3, btn4, btn5, btn6, btn7, btn8 : std_logic;

begin

    D_FF0 : D_FlipFlop port map(Button(0), '1', Reset, Clock, btn0);
    D_FF1 : D_FlipFlop port map(Button(1), '1', Reset, Clock, btn1);
    D_FF2 : D_FlipFlop port map(Button(2), '1', Reset, Clock, btn2);
    D_FF3 : D_FlipFlop port map(Button(3), '1', Reset, Clock, btn3);
    D_FF4 : D_FlipFlop port map(Button(4), '1', Reset, Clock, btn4);
    D_FF5 : D_FlipFlop port map(Button(5), '1', Reset, Clock, btn5);
    D_FF6 : D_FlipFlop port map(Button(6), '1', Reset, Clock, btn6);
    D_FF7 : D_FlipFlop port map(Button(7), '1', Reset, Clock, btn7);
    D_FF8 : D_FlipFlop port map(Button(8), '1', Reset, Clock, btn8);
    
    Judgement <= btn0 and btn1 and btn2 and btn3 and btn4 and btn5 and btn6 and btn7 and btn8;
    
end Behavioral;

Step 7: Connecting the LEDs

Picture of Connecting the LEDs

Now that the software part is complete, it's time to move on to the hardware of the game. To begin, we will need to connect an RGB LED to the PMOD port on the Nexys 2. The wires in the picture above are color coded to show which goes where. The black wire is the power which goes to the common anode of the LED. The red and blue wires are self explanatory.

On the "fun" side of things, please refer to the nexys 2's reference manual to determine the ground port of the PMOD. Remember this, do not plug the power cable or any cable to that port! The last time we did that, we burned out the LED...

LED #1, you will forever be missed.

Step 8: Connecting the Buttons

Picture of Connecting the Buttons

Next, we need to connect all of the buttons to the PMOD ports on the Nexys 2. One side of the button gets power (green wire) and the other goes to the PMOD port (yellow wire).

Step 9: Connections Complete

Picture of Connections Complete

When all LEDs and buttons are connected, there will be a huge mess of wires. This will be a good test of your cable management skills!

"... or you could recreate a miniature Amazon rainforest on your breadboards." - Derek Nguyen

Step 10: Try It for Yourself

Included in this step is a .zip file with all of the VHDL modules and the generated .bit file. If you already have the hardware built, you can skipped all of the software aspect and just run the .bit file with Adept. If you ever find any bug in the vhdl description, don't worry because this project will be updated every time we discover a new "break through".

Have fun!

Comments

kpovos (author)2015-02-21

hello, may i know the variable assignments in the codes that corresponds to the pins of LEDs and buttons, for connection purposes ty. cool instructable tho

peppypickle (author)2014-12-12

whoa, super neat! thanks for sharing this really cool Instructable!

DerekTNguyen (author)peppypickle2014-12-12

Thank you! This project will be updated every time we find a spot that we can improve on.

About This Instructable

8,591views

44favorites

License:

More by RyanFrawley:FPGA Tic Tac Toe
Add instructable to: