Programming lesson
Building a Retro-Style Arcade Game on the DE1-SoC: From Concept to VGA Display
Learn how to design and implement a custom arcade game on the DE1-SoC board using VGA, memory, and user input. Step-by-step guidance for your Ee371 Lab 6 project.
Introduction: Your Lab 6 Adventure Awaits
Ee371 Lab 6 is your chance to shine. The 'Choose Your Own Project' assignment invites you to combine everything you've learned about digital design, SystemVerilog, and the DE1-SoC board into a single, creative endeavor. Whether you dream of a side-scrolling platformer, a tile-matching puzzle, or an audiovisual synthesizer, this tutorial will guide you through the essential steps—from brainstorming to a working demo on LabsLand.
In June 2026, retro gaming is experiencing a massive revival, with classic arcade cabinets and NES-style games trending on social media. That makes now the perfect time to build your own retro-inspired game. This tutorial focuses on a side-scrolling shooter, but the principles apply to any project that uses VGA, memory, and user input.
Project Planning: Define Your Scope
Before writing a single line of SystemVerilog, sketch your game on paper. Your design must include:
- VGA output (at least 640x480 resolution, 60 Hz refresh)
- Memory storage for pixel data, game state, or audio samples
- User input from switches, push buttons, joystick, or NES controller
For a side-scrolling game, you'll need to store background tiles, sprite data, and a score counter. Use on-chip M10K blocks for small data (e.g., 320x240 pixel buffer) or external SDRAM for larger assets. Remember: LabsLand's virtual peripherals include a 360-degree joystick and an NES-style controller, so you can design with those in mind.
Setting Up the VGA Display
Your VGA controller from Lab 5 is the foundation. Extend it to support multiple layers: a scrolling background, moving sprites, and a HUD. Use a double-buffering technique to avoid flicker—write to a back buffer while the front buffer is displayed.
// Simplified VGA timing parameters
parameter H_ACTIVE = 640;
parameter H_FRONT = 16;
parameter H_SYNC = 96;
parameter H_BACK = 48;
parameter V_ACTIVE = 480;
parameter V_FRONT = 10;
parameter V_SYNC = 2;
parameter V_BACK = 33;Instantiate a dual-port RAM for the pixel buffer. One port connects to the VGA controller (read-only), the other to your game logic (write). This allows you to update pixels without disrupting the display.
Memory Management: Storing Game Assets
For a side-scrolling game, you'll need to store tile maps and sprite bitmaps. Use a ROM initialized with your graphics. For example, a 16x16 pixel tile uses 256 bits (assuming 1-bit color). With 256 tiles, that's 64 Kbits—easily fit in M10K blocks.
If you're generating audio (e.g., a beat when the player shoots), store waveform samples in a separate ROM. Recall Lab 3's audio output: you can play a short PCM sample by reading the ROM at a rate determined by the audio clock.
// Example: instantiate a sprite ROM
sprite_rom #(.WIDTH(16), .HEIGHT(16)) rom_inst (
.clk(clk),
.addr(sprite_addr),
.data(sprite_data)
);For dynamic data like score or player position, use registers or small dual-port RAMs.
User Input: Reading the NES Controller
The virtual NES controller has a 4-directional pad, Select, Start, A, and B. You'll need to write a controller reader module that samples the button states at the start of each frame. LabsLand provides the interface; your module should debounce and edge-detect to prevent multiple actions from a single press.
// Pseudo-code for controller input
always @(posedge clk) begin
if (nes_latch) begin
// latch parallel data from shift register
buttons <= nes_data;
end
endMap the D-pad to player movement and A/B to actions (shoot, jump). The joystick can be used for analog control if you prefer.
Game Logic: A Simple Side-Scroller Example
Let's implement a basic shooter: the player moves left/right and shoots projectiles upward. Enemies scroll from the right. Collision detection checks if a projectile overlaps an enemy's bounding box.
// Collision detection example
if (proj_x < enemy_x + 16 && proj_x + 4 > enemy_x &&
proj_y < enemy_y + 16 && proj_y + 4 > enemy_y) begin
// destroy enemy and projectile
enemy_active <= 0;
proj_active <= 0;
score <= score + 10;
endUpdate the score on the VGA using a simple text overlay. You can predefine a font ROM (8x8 pixels per character) and write the score digits to the pixel buffer each frame.
Testing on LabsLand
LabsLand's remote environment lets you compile and run your design. Use the virtual peripherals to test input. For audio, you won't hear it live, but you can record the output and verify it later. Ensure your design meets timing—use the Timing Analyzer to check for setup/hold violations.
If your game uses SDRAM, be careful with the memory controller's latency. It's often easier to use on-chip memory for fast access.
Going Beyond: Add Polish
To earn bonus points, consider adding:
- Multiple levels with different tile maps
- Sound effects (e.g., a short PCM clip for shooting)
- High-score persistence using the SD card (if available)
- Two-player mode using both the NES controller and joystick
Remember to cite any code you borrow from online sources or previous projects. Originality is valued, but building on existing work is encouraged as long as you understand and extend it.
Conclusion
Lab 6 is your opportunity to create something fun and impressive. By following this guide, you'll have a solid framework for a side-scrolling game that meets all requirements. Start early, test incrementally, and don't be afraid to experiment. Good luck, and have fun!