Introduction: "Snake" on an FPGA
This project was completed for the class ECE2220, at the University of Manitoba, for the Fall 2015 term. The board used throughout the project is an Altera DE2 Cyclone IV board. Coding is done with Verilog HDL via Quartus II.
"Snake" is a simple game where the user controls a snake to eat items generated at random locations in the play area. The snake gets longer and harder to control the more items it consumes. You lose if the head of the snake collides with its own body, or if the snake hits one of the borders.
The game originated from the 1976 arcade game Blockade , and variations on the game are still around today. For example, in the YouTube website, you can play snake over a video while it loads, and Google has made a doodle in which you can play a slightly updated version of snake.
The four contributors to this project are:
Computer Engineering students: Daniel Lovegrove, Ian Sweetland
Electrical Engineering students: Kristjan Jacobson, Roberto Alves
What's needed to replicate this project:
- Altera DE2-115 board
- VGA cable & monitor
- PS/2 keyboard (a USB keyboard with a PS/2 adapter may or may not work, do not count on it)
- USB cable to upload program to the board
- Power cable for board
- Quartus II Software to edit and open the Verilog code
Subsequent steps will explain the features of the game, and explain VGA output/keyboard input to give future students a better idea of how they work, and how to implement them in their own projects. These were the most difficult parts of the project to do, because there is not a great amount of useful information on the internet for implementing these functions with Verilog.
**Mainly, this instructable tries to be a concise guideline for future project-goers who need a reference for various modules, and to understand more of the capabilities of the FPGA board.
[The source code is on the last page (references)]
Step 1: PS/2 Keyboard Input
The input from the keyboard is straightforward to understand. There are several things to know:
1. When the keyboard is not sending input, it is sending a constant 1 (high) for its data, as well as the clock.
2. A single key press will consist of at least 33 bits of input from the keyboard.
3. When a key is first pressed, it sends 11 bits of data in the form of a 'make' code. When it is released, it will send an 11 bit 'break' code, proceeding with the same make code again. (There are exceptions when pressing specific keys, don't worry about these too much)
4. The keyboard will send the same 11 bits containing the make code over and over again while you hold a specific key down.
5. The keyboard sends data on the negative edge of its clock.
6. The keyboard has its own clock that you can read. You do not need to define your own clock to work with the keyboard.
To expand, the first image shows what the keyboard will send within every 11 bits. It sends first a zero to start, then 8 bits of data specific to the key you pressed (the make code), a parity bit (PB), and finally a one to stop. The parity bit is either a one or a zero, depending on how many 1s are in the make code. If there are an even number of ones, it will be 1, and if there are an odd number of ones, the parity bit will be 0. It wants to make the total number of ones an odd number. 
To look up the keyboard make/break codes, there are many sources. Try this. Note that all key presses have the same break code in common: F0.
This information will only apply to a PS/2 keyboard, do not try to use a different type of keyboard with an adapter and expect the same results.
The output of our keyboard module is a 'one hot' 5 bit output, the direction the snake goes depends on the position of the 1.
Step 2: VGA Output
A 640 x 480 VGA display works like this: a pixel is activated at the top left (y = 0, x = 0). The monitor activates one pixel at a time, going right along the top of the display until it reaches (y = 0, x = 640). When it reaches the edge of the screen (x = 640), the monitor starts activating pixels at (y = 1, x = 0) and does the same thing. It keeps doing this until it reaches (y = 480, x = 640), where it will start the process all over again. Basically, it activates the pixels one by one (serially) in an orderly fashion from left to right, going downwards. More info here .
Also, If you want to display a colour other than black, you need to specify 8 bit values of red, green, and blue for the monitor to display.
To be able to play the game, we need to use a VGA compatible monitor to allow us to display the game. There are several things needed to make the VGA generator module work:
1.We set our maximum height and length to match the screen resolution we wanted for the program (640 x 480).
2. We used the positive edge of the VGA clock while using if statements and non-blocking assignments to create an x coordinate count as well as a y coordinate count. These xCount and yCount can be used to draw things on the display, as well as track the snake and apple. They track where on the screen that you are.
3. We stated that if our x coordinate and y coordinate didn't match our maximum length or maximum height, then we would increase their coordinates by one
4. Using the positive edge of our VGA clock again, we use a non-blocking assignment to define our display area as the x coordinates multiplied by our y coordinates. These values are true if either one of these coordinates is less than our screen resolution (640x480).
5. Concluding the VGA generator module, we use three assign statements, two of which "VGA_vSync and VGA_hSync", were assigned as the inverse of the vertical sync, and horizontal sync. Our final assign statement included using the display area we define in our previous assignment and using "blank_n" to define our display area.
6. Also note, the VGA cannot run on a 50MHz clock signal like the board supplies, rather, it works with a 25MHz clock. We use a separate module (pictured) to reduce the 50MHz clock to 25MHz.
Step 3: Apple Generation & Collisions
In our game, the snake eats apples to grow larger. We made a pseudo-random coordinate generating module to place an apple in a random part of the screen when the game starts, and when the snake collides with an apple.
To generate a random position for the apple to be placed on the screen, we use the "randomGrid" module to randomly select a location for the apple to appear.
A collision is detected by checking if the snake and another object are being drawn on the VGA at the same time (checking if their coordinates are the same). Lethal collisions consist of the snake colliding with its own body, and non-lethal collisions occur when the snake collides with an apple, in which case the size of the snake is increased.
If a lethal collision is detected, such that if the snake collides with the border or itself, the VGA output will display a completely red screen, signalling that the game is over.
Step 4: Snake Coding & Game Specifics
The first image above is the Verilog code that was used to program our snake. Depending on the input from the keyboard, the snake head will move 10 units in a given direction.
Game specifics not yet mentioned:
- We have the snake grow around 4 extra body sizes when it eats an apple, rather than the classic 1. This is to make the game faster paced.
- Our snake is updated at 28Hz on the VGA.
- To reset the game, the first push button is used.
- The maximum snake length is 127 body parts plus a head (so 128 total parts).
Mapping the snake:
-The extra 127 portions of the snake in the beginning of the game are hidden in the front porch of the VGA display. Once the first apple is eaten, the coordinates for some of the hidden body parts are updated to appear in the display area. Subsequent apples eaten reveal more body parts.
-To make the snake move, visually, it loses the last 'tail' block and gains a new head in one of the specific directions inputted by the keyboard. Technically, during each position update a certain body part's position gets set to the proceeding body part's then position. Note that this does not count the head of the snake, as the head's next position doesn't depend on proceeding body parts. Rather, the next position of the head depends on the keyboard input, as you may have guessed.
-If there is no key input between position updates, the program uses the last direction inputted to move the snake.
-We know how long to make the snake because we keep track of its length.
Step 5: Contributions
VGA output: Ian Sweetland, Kristjan Jacobson
Keyboard input: Ian Sweetland, Kristjan Jacobson, Daniel Lovegrove
Game specifics: Ian Sweetland, Kristjan Jacobson
Report: Roberto Alves, Daniel Lovegrove
Step 6: References & Source Code
If you do use any of our information or code here, please don't forget to credit/reference us!