As mentioned in the handout, the game will be played on a 40x30 grid. To represent this properly in binary, we must have separate bits for the horizontal and vertical positions, which also means that the maximum values in either direction must be rounded up to the next power of two. So although we can only see 40 by 30, the memory must be stored at a minimum of 64 by 32. This corresponds to 6 bits representing the horizontal, 5 bits representing vertical, and a total 11 describing the entirety of the 64 by 32 memory. Also note, as is the case with previous labs, the vertical position is represented as the most significant bits. However, as this is an open ended design, students are free to make whatever internal design choices they would like.
![]() tron.mif |
| ||||||||||
While using this file, it is important to note a few differences with the previous roms that you have used. First, this rom has only two bits per location, containing just enough data to represent the four possible states of each location. Second, this rom has an 11 bit address line. You will need to form the address with a concatenation (the amperstand operator) of a 5 bit vertical index with a 6 bit horizontal. As a first step, these may be taken directly from the row and column outputs of the vga_sync VHDL module. To do this, use the logic from square.vhd in lab 3 as a reference. When done correctly, you will see an image similar to the one above. I used white as a wall color, and green and yellow as the two players. Your colors might be different depending on what logic you use to convert the two bit data to RGB. Additionally, this rom is only an example, feel free to make whatever modifications that might be required by your design.
By now, you realize that synchronization between project components is tricky to manage, and difficult to debug. So one of the most important steps in this design will be the coordination of your game controlling process with the process which controls the VGA output. As with any open ended design, there are numerous ways to manage this resource sharing. The first, and simplest way is to have each memory read or write have its own sequence of actions within the main state machine, each waiting until the memory is not being used, then performing its respective operation (this is what is done in the labs). However, as you will be doing a minimum of four memory operations per game movement, this may take a significant amount of states just to perform the needed memory accesses. It may be easier and cleaner to find an alternate way to access memory.
One possibility is a sort of memory access subroutine. As FPGAs can contain arbitrarilly complex state machines, you can have more than one state signal at a given time. One could form the appropriate actions in a common state sequence, and have it shared from multiple points. One might set the main state signal to enter this sequence, and a second as a return point. Once the sequence has completed its actions, the subroutine could assign the contents of the second signal to the main state, effectively returning to given point set by the calling state.
Another possibility is to make a separate module which arbitrates data access between two or more read/write ports. Considering that the VGA sync module draws blocks of eight by eight, it would not likely notice if its data were only refreshed every other cycle. Thus, the address and data lines could be shared on alternating cycles instead of vga sync pulses. Although this would require a state machine just to manage data, it would eliminate any timing issues if done correctly. However, it is recommended to simulate such a controller by itself before combining it with other modules, to isolate any logical errors which may be present in the code.
Do note that you should chose the method which makes the most sense to you, and that you can work with most easilly.
As I have noticed that several groups are lagging behind where I feel they should be in this project, I have decided to post the memory controller that I used in my version of the project. Hopefully this should give a boost to those groups that are struggling. Do note that there are a number of differences between this and the raw LPM RAM interface which is used in most of the labs, and that the read and write cycles are very different (although somewhat easier to manage). First and foremost, there are two independent read/write ports for the memory, each with its own address, data input, data_output, and write enable. The various data lines are time multiplexed within the controller, and are triggered on alternating clock cycles. This implies that the controller still can do two operations, but will require more than one cycle to complete. Considering that the controller operates on the falling clock edge (labs use the rising edge), and that there is a minimum of two cycles operation time, data read or written should not be considered done until three cycles after the inputs were last changed. This does mean that the VGA colors could be shifted by one pixel, but overall the image should remain unchanged.
| Read Cycle | Write Cycle |
|---|---|
... when sMove => -- X/Y should set address lines sposx <= "000101"; sposy <= "01100"; sReg <= sRead; -- Prepare a read, hold values for three cycles when sRead => -- Address is already set via x/y position write <= '0'; sReg <= sWait1; when sWait1 => sReg <= sWait2; when sWait2 => sReg <= sReadDone; when sReadDone => -- Ram output is stable at value ... |
... -- Prepare a write when sSet => -- X/Y should set address lines sposx <= "000101"; sposy <= "01100"; data_in <= "01"; sReg <= sWrite; when sWrite => -- Address is already set via x/y position write <= '1'; sReg <= sWait1; when sWait1 => sReg <= sWait2; when sWait2 => sReg <= sWriteDone; when sWriteDone => write <= '0'; -- Ram write cycle is completed ... |
Do note that this WILL require changes to your state machine, so copying read/write code directly from previous labs probably won't work. However, if the VGA and the main process are given their own read/write ports, you should not have too many difficulties.