SpaceInvaders Report
SpaceInvaders Report
Project Report
In this project, we have recreated the classic “Space Invaders” arcade game from the
1970s on the De1-SoC FPGA. In this game, the player controls a defender spaceship
that moves horizontally across the bottom of the screen and fires missiles at enemy
alien ships. Every few seconds, the enemy alien ships shift down and approach the
defender spaceship. Additionally, the enemy alien ships also drop bombs that the
defender must avoid when moving around at the bottom of the screen. To proceed to
the next level, the player must destroy all of the enemy alien ships before they reach the
bottom of the screen.
To recreate this game on FPGA, we used a retro NES USB controller to get the user
input. To handle this, we wrote a device driver in software that correctly forwards the
user input to the top level software logic. Our software controls all of the Space Invaders
logic and passes the data to byte-addressable VRAM in hardware, which is then
displayed on a VGA monitor.
Top-Level Architecture
Hardware
- Our approach for displaying the graphics data involved utilizing a tile-and-sprites
method. This process is done with four tables: a pattern name, pattern generation, sprite
attribute, and sprite generation table.
- Tiles were employed to display the user interface and gameplay messages. To
accomplish this, we used a pattern generator table, which is addressed by 12 bits and
results in 4096 rows. Since every pattern tile requires 32 bytes, we had the capacity to
store up to 128 distinct patterns. A pattern name table parses the generator table and
obtains the corresponding pattern attributes. The pattern table does not require all 12
bits of addressing; this was kept in case new patterns and UI features wanted to be
added.
- The sprites are displayed in a similar structure to the pattern tiles, except we assumed
that all of the sprites are moving components – unlike the tiles. Because Space Invaders
has a lot of moving parts (20+ ships, missiles, bombs), we needed to create a very large
sprite generator table. Each sprite requires 128 bytes and 32 address bits were required
to create the rows in our sprite generator table. We used a sprite attribute
table to store the addresses of each sprite. Each sprite attribute contains the y position, x
position, and the sprite address from the generator table. A combinational block allows
for colors to be prioritized and for sprites to be displayed in front of the tiles.
- Additionally, each sprite requires its own state machine. This is particularly tricky with
Space Invaders, since there are many enemies and the horizontal count of a sprite must
not overlap with another sprite. If a sprite needs to be displayed on the following line, the
sprite generator table is accessed from the designated base address. The horizontal
position of the sprite is then loaded into a down counter, while the sprite pixels are
loaded into a shift register. When the next vertical line is reached, the down counter
decreases, and a 4-bit pixel value is retrieved from the shift register, which corresponds
to the 24-bit RGB color value. Since Space Invaders only needs green and white as
colors, a color translation table was created, where a 4-bit pixel value maps to the 24-bit
RGB value.
Avalon Bus: HW/SW Interface
- Our hardware interface accepts a 32 bit write packet from software that is structured as
follows:
1) Bits 0 - 1: Table Selector [pat_name, pat_gen, sprite_attr, sprite_gen]
2) Bits 2 - 17: 16-bit Destination Address
3) Bits 24 - 31: 8-bit Data to Write at Destination Address
AUDIO INTERFACE
Audio CODEC is interfaced with the Avalon bus using Avalon Stream Interface. Avalon-ST is an
interface that supports the unidirectional flow of data, including multiplexed streams, packets,
and DSP data. The audio streaming interface consists of 3 signals: data signal, read signal,
and a valid signal. The data signal carries the actual audio data, while the valid signal indicates
when the data is valid and should be processed, and the read signal is used to control the flow
of the data.
Software
Game Logic
- Game State:
The game state holds information related to the objects it must keep track of during
updates and relevant inputs. The struct show below holds all the information:
The struct on the left shows the defender, the enemies, bullets, bombs, and game stage;
along with settable parameters for bullets, bombs, lives, and score. The struct in the
middle explains the direction attributes that are set within the different instances of the
game state. The struct on the right explains the relevant attributes that sprite will take
into consideration when creating the instances on screen.
The enemy instance is similar to the defender instance except for the lives being an
attribute rather than an overall calculation. This is due to the fact that multiple instances
of an enemy are produced and must be looped through during each update to check for
events. The alive count will be increased as the levels get more difficult; when the alive
count reaches 0, the enemy dies. Enemies are allowed to move at a starting rate of 2
pixels every 8ms.
Following suit with the previous structs, the direction and sprite attributes are also listed
here. Once a bomb has hit the defender or gone off screen, the bomb sprite is reset and
the count is decremented.
Gamepad Controller
The joystick peripheral must have communication algorithms that will relay important information
for each button. The following functions will be implemented:
- move_left() → button movement will indicate left translation of pixels of player ship
- move_right() → button movement will indicate right translation of pixels of player ship
- fire_bullet() → button press will launch bullet pixels from player ship
- start() → start button will start game in beginning
- select() → select button will reset game after lives are terminated or game is won
- Get the HW/SW interface working as soon as possible. Coding and understanding how
software passes information to the hardware is vital to any video game project that
requires a display. Following the interface, sprites and bitmapping can be implemented
to see how changes in software display on the actual VGA peripheral.
- Start with a strong basis on hardware. Once the hardware is correctly implemented with
basic test cases in software, this will make the challenge of software testing an isolated
experiment. During our programming, we progressed with the hardware at a level to
comfortably test 5 sprites. We perfected the algorithms for 5 sprites assuming that
adding more hardware to support more sprites would be intuitive. However, once the
sprites would not perform as expected, the isolation of errors was now expanded to both
the hardware and software. This made the process extremely time consuming and
difficult.
Project Breakdown
vga_ball.sv
Code Screenshots - Software
map.h
map.c
pattern.h
pattern.c
color.h
sprite.h
sprite.c
joystick.h
joystick.c
gameplay.h
gameplay.c
spaceinvaders.c