How to build an FPGA DIY Memory Game.
Team Project: Memory Blitz. By: Bit By Bit Productions
- Kim Laberinto
- Dana Carver
- Megan VanHumbeck
This project is for our ECE2220 Digital Logic Systems course at the University of Manitoba. Inspired by the handheld game Simon, we thought it would be a sufficiently challenging project which proved to be very true. However we overcame it! Good luck with the project, and we hope you learn as much from it as we did.
Features of our Memory Game Implementation
- DIY Tilt Sensor
- Different sound tones (E, C#, and A, and E [lower octave than the first])
- LEDS of various colours
- Enabling Different Modules through Finite State Machines
- Sequential Presentation of Output
- Reset switch
- 4-modes of difficulty/speed
Step 1: How It All Works
The device works by presenting a sequence to the player, beginning with length 1, using 4 LEDs and a speaker and then taking in input from our DIY tilt sensor to see if the player can match the sequence. Each direction of tilt corresponds to an LED colour. If the player inputs the sequence incorrectly, 4 seven segment displays display the word "FAIL". However, if the player inputs the sequence correctly, the sequence will be presented again with one more step than the previous "level". If the player inputs the sequence correctly 9 times, then the 4 seven segment displays will display the word "PASS". At the end of each game, if the player would like to play again, they just have to set the reset switch to high for a few seconds. When the sequence for the first level is presented the switch can be set back to low and the player can continue playing.
The game is made more difficult by the clock. Each time the sequence is presented, the player has a single clock cycle before they need to give any inputs. The trick is that after that, each part of the sequence must be input by the next time the clock goes high. This is because the system checks to see if the input matches the presented sequence on the positive edge of the clock. The clock used was a 25MHz clock on the FPGA board, but is slowed down by a counter. Each time the clock goes high, the counter increases and when the counter gets to the set value, a signal goes to the system and the counter resets. This signal is what we used for a clock because it allows us to set the game "pace" by simply changing the value the counter has to reach. This modified clock (when the counter reaches the given value) is displayed to the player on the breadboard by a single yellow LED. The player has 4 speed option that can be changed before or during the game. There are 2 switches to chose between the levels. If both are low, the game is very slow speed, if only the right most switch is high, the game is slow speed, if only the second right most switch is high, then the game is medium speed and if both switches are high, then the game is fast speed.
Step 2: Materials and Equipment
FPGA Board and Peripherals
- Altera Cyclone 4E DE2 FPGA board(from our ECE2220 course)
- Expansion Header/2x40 ribbon cable with header pins (from the University of Manitoba ECE tech shop)
DIY Tilt Sensor (for Input)
- PVC 4-way Cross (we used 1/2" in diameter)
- Conductive Ball Bearing
- Popsicle Sticks
- Foam pieces (or something to cap off the ends)
- Tape (to hold the foam in place)
- 8 LEDS
- Appropriate current limiting resistors
In our project we used 8 LEDs of various colours (2 red, 2 blue, 2 green and 2 yellow). We found the red, yellow and green LEDs to be much dimmer than the blue ones, so we chose a lower valued-resistor in series. In our project, we used 1kΩs for the blue leds, and 100Ωs for the red, yellow and green LEDs.
- Headphone female port (1/8")
- 10 uF capacitor
- Soldering iron
- 1 Yellow led
- 25MHz clock (on the FPGA board)
- Glue gun
- Wire strippers (optional, but helpful)
- Hand sander or sand paper
We were able to find most of the things we needed from the UofM ECE Tech Shop. For those outside the UofM, you should be able to find these materials online.
Step 3: Building the Tilt Sensor
- You're going to need a four-way plumbing fitting, a metal ball-bearing, some popsicle sticks, a hot glue gun, a hand sander or sand paper, wires, foam and tape.
- You need to make 4 ramps out of the popsicle sticks so that the sensor has a "neutral position" when it isn't tilted. Measure from the end of one of the fittings to where it meets all of the others. This basically just ensures that the ramps don't stick too far out of the plumbing fitting. I used roughly 2.75cm.
- Cut the 8 pieces of popsicle sticks to your measured length. Make sure that they don't splinter.
- In order to make the tilt sensor more sensitive (not needing the device to be horizontal or vertical before the ball moves), you'll need to sand one end of each of the popsicle stick pieces so that it goes to a point. The thinner it is at the end, the less you'll need to tilt the sensor to get a reading.
- You will need to glue the popsicles stick pieces together in pairs to make a v-shape, with the longer sides of the popsicle stick pieces touching. I used the inside edge of the plumbing fitting to make sure that the ramp would be as flat as possible. I held the popsicle pieces against the edge with a little bit of them sticking out so that I could put some hot glue along where the pieces touched to hold them at the correct angle and then glued the rest of the length of the ramp at that angle so that was more secure. Make sure that there is only a thin layer of glue because too much makes the ramp ends sit too far away from the base of the plumbing fitting and then the ball-bearing can't roll onto the ramps easily.
- Once all 4 ramps are all assembled, the next step is putting them into the plumbing fitting. To do this, you'll need to decide how sensitive you want the sensor to be. This means deciding the incline of the ramp. You'll need to glue the ramp pieces in, holding each at both ends. I used a finger to hold the bottom of the ramp down through the opposite side of the fitting and then I put foam pieces under the other end of the ramp to keep the ramp at the angle I wanted. I glued at the top corners of the v shape for each ramp.
- Once the glue is completely dry, you'll need to connect a wire to either side of each ramp. I put a wire going in the same direction of the ramp with about 0.75cm of exposed wire on the end of the ramp and then bent the wire so that it followed the contour of the plumbing fitting and all of the wires extended downwards from the centre of the underside of the plumping fitting. Glue the wires down at the end of the ramp, and at the bottom of the fitting. You just need to make sure that the wire is secure and can't move or rotate. This will ensure that the sensor is reliable. When the ball-bearing touches both wires on one end, it will ground the circuit and the sensor will detect this as a tilt in that direction.
- The last step in building the tilt sensor is to insert the ball-bearing and then cap-off all of the ends. I did this with a small rectangular piece of foam and used the electrical tape to secure the foam. You'll want to experiment with the size and placement of the foam to ensure that when the sensor is tilted that the ball bearing can actually touch both pieces of wire.
Step 4: Connecting and Testing the Tilt Sensor
- You will need the bread board, the FPGA board, the ribbon cable (for the GPIO bank on the Altera board), wires, resistors, capacitors and LEDs.
- To simplify the wiring, pick one side, I used the right side, and connect each wire on the that side and connect it to ground. Then take each wire on the other side, for me this was the wires on the left and connected them to ground in parallel to a voltage source. The wire should go to ground through a capacitor, I used 10 micro Farads, and to the voltage source through a resistor, I used 20 kilo Ohms. You're going to need to connect the ribbon cable from the FPGA board to the breadboard using the ribbon cable and header pins if you have them. I used the GND GPIO pin on the FPGA board and just connected it to a rail and for the voltage source I set a pin on the FPGA board to 1 and then connected this pin to a rail.
- You also need to connect the wire from the sensor that went to ground and a voltage source to a pin on the ribbon cable. This is how you read in from the sensor. When the ball-bearing grounds the circuit, the sensor will send a 0 to the FPGA via the GPIO pins and the ribbon cable.
- To test the sensor we connected 4 resistors to pins on the ribbon cable and then connected the resistor to ground through an LED. When the GPIO pin goes high, it will output a voltage and turn on the LED. We used 100 Ohm resistors for red, green and yellow LEDs and 1000 Ohm resistors for blue LEDs. Make sure the positive end of the LED (the longer end) connects to the resistor and the negative end to ground.
- To make this work, you'll need to write a module in a Quartus file using Verilog hardware coding language. The pins connected to the sensor will be your inputs and the pins connected to the LEDs will be the outputs. You can simply set each LED pin to one of the sensor pins using assign statements.
- Note: These LEDs will be used in the game to show the user what they have input.
Step 5: Setting Up the Speakers and LEDs
Soldering the audio components together
- Get the headphone female port and the capacitor. Get two short wires, one red, one black. This will be the signal and ground wires respectively.
- Connect the one lead of the capacitor to both the left and right channel pins of the audio port. (The center pin is ground.)
- Solder the capacitor to the left and right. Clip off the excess.
- Solder the ground wire (black) to the center pin. Clip off any excess.
- Solder the signal wire (red) to the other lead of the capacitor. Clip off any excess.
- Connect your speaker through the audio port! Done!
Now your audio signal through the red wire will drive any connected speakers.
Setting up the LEDs that will present the sequence to the player
- Connect 4 pins on the ribbon cable to resistors, we used 100 Ohm from red, green and yellow LEDs and 1000 Ohms for the blue LED.
- Connect each resistor to an LED and ground the other side of the LED.
Remember, current only flows into the positive end of the LED. [Just a small reminder that the positive end of the LED is usually the longer lead].
3.Test these LEDs by writing a short Verilog module that assigns each GPIO pin connected to these LEDs to 1 (HIGH).
Check Point: At this point your sensor should light up a different LED for each direction it is tilted in and the speaker and output LEDs should all be set up and functional.
Step 6: Writing the Present Module
In a memory game, a pattern must be presented to the user for them to repeat. This is the purpose of the Present module. Each time the Present module is run, the pattern of LEDs from the last level is shown, plus one more. In order to implement this, we used a finite state machine and a counter. The counter, called “i”, keeps track of the level. This counter keeps track of how many states should be run before the program goes to the intermediate state, named the IN_BETWEEN_STATE. In the intermediate state no LEDs are shown and, as we will explain later, the Compare module runs.
The code for the finite state machine may look complicated at first glance, but logically it is quite simple. Each state has three parts: output, “if”, and “else”. The output section controls the LEDs and the HEX display. It sets the individual LEDs on or off, showing the user which order to perform the actions, as well as displays the level on a HEX display. The “if” statement checks to see if “i”, the level, matches the state that we are in. If the “if” statement is true, the finite state machine jumps directly to the intermediate stage to wait for Compare to run. “i” is also incremented so that when the Present module runs again, the user is on the next level. Finally, the “else” statement causes the state machine to go to the next state in the sequence, adding another LED to the presentation.
In addition to the LEDs, there is also sound. Each LED has a different pitch associated to it. When an LED is turned on, the pitch linked to it plays. We created the right frequencies for the pitch to match the Simon game by mathematically manipulating the clock frequency. This is explored in depth in the allTones module.
Please note that our code is attached in step 10.
Step 7: Writing the Compare Module
The purpose of the Compare module is to process input from the player and to compare it to the LED sequence to determine if the player properly memorized and repeated the sequence. The code for this process is very similar to the code for the Present module. They both have the same format of determining which state to go to depending on the counter.
In order to make it easier to interpret the player input we have a separate always loop from that of the finite state machine. The purpose of this loop is to take all four possible inputs and turn it into a four bit one hot encoded number. This number matches the one hot encoded number for the four LEDs, and lights up only one for each input direction.
In comparison to the Present module, each state of the Compare module has a few more sections. While it still maintains the “if” and “for” loops, it removes the LED display portion of the output section and adds an “if” and “else” to control whether the state has output at all. The top “if” checks to see if the user chose the right input. If the user input does not match the desired four bit one hot encoded number then the finite state machine jumps directly to the FAIL state, which ends the game and presents “FAIL” onto four HEX displays. If the user chose the correct direction, it goes on to the “else” statement and faces the second “if” and “else” statement. These “if” and “else” statements are the same as the ones in Present; they check which level the player is at (the value of the variable “i”) and where the finite state matching should go from there. In the final level, if the player responded correctly to all of the states, the final level causes the state machine to the PASS state. In this state, “PASS” is displayed on four HEX screen and the game has been won.
Step 8: Writing the AllTones (Sound) Module
In our project, whenever the FPGA board is presenting the output to the player, an appropriate sound comes out of the board depending on what color is shown. All of this handling of the speaker is handled by the allTones (sound) module.
Taking advantage of the onboard 25MHz clock and a counter that counts to the specific number, we can toggle the speaker with a square wave of a specified frequency. The pitches of the 4 colours in our game are, E, C#, A and E(octave lower than the first E). Taking in the same register bits which light up the LEDs as input into the module, the allTones module has an always@ block in Verilog which changes the specific frequency to always appropriate drive the speaker with the specified tone for that color LED.
Step 9: Writing the Main Module
The main module of the Verilog code is a finite state machine that uses its states to change the values of the enables for both the Present and the Compare modules. It is also is responsible for resetting the game and for changing the clock speed depending on the difficulty input by the user.
The main module uses three switches to run the game. The first switch is the reset switch. As the name suggests, the reset switch resets the game into its original state, allowing the player to play the game again. This also resets the count variables present in the other modules. The other two switches controls the speed. Our game has four speeds, each of which is a combination of the two switches. The speed controls the adjusted clock, known as modclk, which is used in the program. The faster the clock, the more difficult the game.
The part of the project the most difficult to understand is the finite state machine in the main module. While it only has two states, the circumstances for which they change may be difficult to process. To be able to understand why this finite state machine is needed, one needs fully comprehend that Verilog is not a software language. Modules do not only run when called. They run continuously. As one may suspect, this is not ideal for a game with two very distinct and separate modules. To be able to control when a module is run, we placed enables on the finite state machines of both Present and Compare. This limits them to only run while the enable is high. We also placed a variable in the aforementioned intermediate state of both modules. This variable goes high when the respective finite state machine has finished the processing for that level. The main function uses those four values to control which module is running and to prevent the other module from starting until the first has finished.
Step 10: Our Code
Step 11: Final Comments
Special thanks to Dr. Ahmad Byagowi for supporting and helping us throughout the project.
Step 12: Possible Improvements for the Future
Possible Improvements for the Future
This next section mentions the different parts where future students may build on or modify in our game.
- Randomize the sequence so that the game isn't exactly the same every time you play. We hard coded a sequence, but this could get boring for the player after a while.
- Change the method of input in this game. Right now we use tilt sensors, however the game could use a variety of different methods of taking in input from the player (along with appropriate changes to the code if required).
- Create a more aesthetically pleasing tone with the FPGA rather than a square wave input to the speaker.
- Experiment and feel free to build off of any part of our code!