- Software Engineer: To gain insight into both user and technical needs, while assisting in decision-making and strategic planning. Provide clarity on risks, challenges, and the additional technical considerations.
- Program Manager: To ensure coherence with the Functional Specification.
- Quality Assurance: To support the creation of the Test Plan Document and act as a guide during issue validation.
- Project Manager: To help identify risks and dependencies at every stage of the project lifecycle.
The project involves the development of a hardware-accelerated version of the Frogger arcade game on an FPGA using Verilog. The game will run on a VGA display and be controlled through integrated board switches, with a focus on optimizing performance and resource usage on the FPGA.
This project leverages FPGA technology to implement the Frogger game in hardware, aiming to exploit the parallelism and real-time capabilities of the platform. The use of Verilog allows for direct control of the FPGA's resources, ensuring precise timing and efficient logic design.
Mandatory Requirements
- FPGA-based Frogger game using Verilog with VGA output.
- VGA output with correct signal synchronization and real-time gameplay.
- Functional input via FPGA switches with reliable debouncing.
- Display a 1x1 frog sprite and at least one car on screen at all times.
- Game logic includes frog movement, car collision detection, and level progression.
- Efficient memory and clock management.
Nice-to-Have Objectives
- Detailed, colored frog sprite and up to 16 cars with varying speeds.
- Multiple levels (at least 8) with increasing difficulty.
- Visual feedback via 7-Segment Displays.
- Performance optimization and maintanibility for future features.
- The development board has sufficient resources to implement the game logic and display requirements.
- The VGA display is compatible with the signal output from the FPGA.
- The input switches on the FPGA board are fully functional and responsive for user control.
- Power supply to the FPGA will be stable and sufficient for the game's hardware demands.
- No external systems or APIs will be required to complete the project.
Board Name | The Go Board | |
Power Requirement | Operating Voltage: | Typically 5V supplied via USB. |
Environmental Specifications | Operating Temperature Range: | 0°C to 70°C. |
Form Factor | Dimensions: | 66 mm in width and 48 mm in height |
Educational Purpose | Designed for easy experimentation and prototyping without the need for additional breadboards or soldering. |
Number of Switches: | 4 (tactile pushbutton switches) | |||||
Functionality: | Allow the User to move the Frog in four directions : | Switch 1: | Switch 2: | Switch 3: | Switch 4: | |
Up | Left | Right | Down |
The code does not contain any references or functionalities related to the use of LEDs because we have chosen not to incorporate LED indicators into the game design, focusing instead on other visual outputs such as the VGA display.
Number of Displays: | 2 |
Functionality: | Displays numeric and alphanumeric information such as the current level of the User's game. |
VGA Output | Connector Type: | 15-pin D-sub connector (standard VGA) |
Supported Resolutions: | 640x480, 800x600, 1024x768, 1280x1024 | |
Functionality: | Provides video output for displaying game graphics and user interface. | |
Access to horizontal and vertical sync signals for display timing. |
Frequency: | 25 MHz | |
Functionality: | Synchronizes game logic, display rendering, and user input processing. | |
Clock Division: | Certain modules require slower clock signals to function correctly. Clock division is used to generate these lower-frequency clocks, ensuring that these modules operate at an appropriate rate. For example, the car movement in the game is controlled using a slower clock to be seen by the human eyes and to make appropriate movements. |
Module Name | Description |
---|---|
main.v |
This is the entry point of the game, responsible for initializing and orchestrating the other modules. It manages the clock signals and ensures proper sequencing of game functions. |
frog.v |
This module handles all the logic related to the frog’s movements on the screen. It manages user inputs to control the frog, ensuring it moves across the lanes while avoiding cars. |
car.v |
Defines the behavior of the cars that the frog must avoid. It manages car speed, direction, and position. Multiple cars can be controlled through this module, with varying speeds across different levels. |
game_controller.v |
Manages the game state, including start, play, game over, and level progression. This module ensures the game follows the intended flow, including tracking the player’s progress and adjusting difficulty. |
display_controller.v |
Responsible for interfacing with the VGA display, controlling what is shown on the screen. It manages the rendering of the frog, cars, game background, and any other visual elements. |
collision_detector.v |
Implements logic to detect when the frog collides with a car. This module ensures accurate hit detection, which is critical for determining when the player loses the game. |
level_manager.v |
Manages the level increments and difficulty scaling. It handles transitions between levels, adjusting car speed and other parameters to make the game more challenging. |
graph TD;
A[main.v]:::highlight -->B{Clock Signals}:::grey --> C[frog.v]:::highlight;
B{Clock Signals}:::grey --> D[car.v]:::highlight;
B{Clock Signals}:::grey --> E[game_controller.v]:::highlight;
B{Clock Signals}:::grey --> F[display_controller.v]:::highlight;
B{Clock Signals}:::grey --> G[collision_detector.v]:::highlight;
B{Clock Signals}:::grey --> H[level_manager.v]:::highlight;
C[frog.v]:::highlight -->J((User Inputs)):::small --> E[game_controller.v]:::highlight;
D[car.v]:::highlight -->K((Car Positions)):::small --> G[collision_detector.v]:::highlight;
C[frog.v]:::highlight -->L((Frog Position)):::small --> G[collision_detector.v]:::highlight;
G[collision_detector.v]:::highlight -->M((Collision Status)):::small --> E[game_controller.v]:::highlight;
E[game_controller.v]:::highlight -->N((Game State)):::small --> F[display_controller.v]:::highlight;
E[game_controller.v]:::highlight -->P((Level Info)):::small --> H[level_manager.v]:::highlight;
H[level_manager.v]:::highlight -->Q((Level Data)):::small --> D[car.v]:::highlight;
F[display_controller.v]:::highlight -->R((Render Data)):::small -->I{VGA}:::vga;
classDef highlight fill:#5C955C,stroke:#333,stroke-width:3px,color:#fff;
classDef small fill:#333,font-size:10px,color:#fff;
classDef vga fill:#ff9,stroke:#333,stroke-width:2px;
classDef grey fill:#333,stroke:#333,stroke-width:3px,color:#fff;
Input Control | Game Action | Description |
---|---|---|
Switch 1 | Move Frog Up | Moves the frog one position up on the screen. |
Switch 2 | Move Frog Left | Moves the frog one position to the left. |
Switch 3 | Move Frog Right | Moves the frog one position to the right. |
Switch 4 | Move Frog Down | Moves the frog one position down on the screen. |
All the Switch | Reset Game | Resets the game to the initial state. |
Output | Display Element | Description |
---|---|---|
VGA Output | Frog Sprite | Displays the frog at its current position. |
VGA Output | Car Sprites | Displays cars moving across the screen. |
VGA Output | Background | Displays the game background (e.g., road, river). |
7-Segment Display | Current Level | Shows the current level of the game. |
Category | Variable | Description |
---|---|---|
Clock | i_Clk |
System clock input |
7-Segment Display | o_Segment1 |
7-segment display output for digit 1 |
o_Segment2 |
7-segment display output for digit 2 | |
Switches | i_Switch_1 |
Up switch input |
i_Switch_2 |
Left switch input | |
i_Switch_3 |
Right switch input | |
i_Switch_4 |
Down switch input | |
VGA Output | o_VGA_HSync |
VGA horizontal sync output |
o_VGA_VSync |
VGA vertical sync output | |
o_VGA_Red |
VGA red component output | |
o_VGA_Grn |
VGA green component output | |
o_VGA_Blu |
VGA blue component output |
In the Frogger FPGA project, memory is managed to optimize the limited resources of the FPGA. Key data such as the frog’s position, car positions, and game state are stored in registers for fast access. Larger data sets, like sprite information and display buffers, are stored in Block RAM (BRAM).
Memory optimization strategies include:
- Registers for small, frequently updated variables.
- BRAM for larger assets like sprites and backgrounds. The main module can utilize BRAM to store the game layout or configuration data for quick retrieval.
// Example for BRAM implementation
wire [7:0] bram_data_out;
reg [7:0] bram_data_in;
reg [11:0] bram_addr;
reg bram_we;
- Look-Up Tables (LUTs) to store fixed graphical elements.
The primary clock signal (i_Clk
) is distributed to all modules to maintain synchronization. This ensures that all processes in the system operate in unison. For instance, the i_Clk
signal drives the VGA controller, sprite renderers, and the game controller module.
Example:
Insrc/main.v
, thei_Clk
signal is connected to the VGA controller and other modules:always @(posedge i_Clk) begin // Clock-driven processes end
Certain modules require slower clock signals to function correctly, such as game elements with slower movement. Clock division is used to generate these lower-frequency clocks, ensuring that these modules operate at an appropriate rate. For example, the car movement in the game is controlled using a slower clock.
Example:
Insrc/sprites/car.v
, a clock divider is used to slow down the car’s movement:reg [N:0] counter; always @(posedge i_Clk) begin if (counter == TARGET) begin // Example for every 10 clock cycles for slower car movement counter <= 0; end else begin counter <= counter + 1; end end
Game timing is managed by counting clock cycles, which ensures smooth gameplay and synchronized events like movement and level progression. By counting the clock pulses, the system can keep track of in-game time intervals, ensuring that actions like jumping, obstacles moving, or time limits are handled consistently.
Example:
Insrc/main.v
, clock cycles are counted to implement a timer that controls game events:reg [31:0] timer; always @(posedge i_Clk) begin timer <= timer + 1; if (timer == TIME_LIMIT) begin // Trigger event (e.g., level progression) end end
Synchronizing the frame rate with the VGA display’s refresh rate ensures that the graphics are updated smoothly, preventing issues like screen tearing. The system is designed to match the frame update frequency with the VGA display's 50Hz refresh rate.
Example:
In the VGA controller withinsrc/main.v
, frame synchronization ensures smooth rendering:always @(posedge i_Clk) begin if (vga_refresh_ready) begin // Update frame data end end
The core gameplay revolves around guiding a frog safely across a perilous road filled with moving cars, leading to increasingly challenging levels. Key mechanics include:
- Frog Movement: The player controls a frog that moves up, down, left, and right across the screen.
Example (from player.v):
The player's movement is controlled using switches for up, down, left, and right. The player.v module handles debouncing for smooth input and updates the player's x and y coordinates based on directional inputs:always @(posedge i_Clk) begin if (w_player_up == 1'b0 && r_player_up == 1'b1) begin if (o_player_y > 1) begin o_player_y <= o_player_y - 1; end end else if (w_player_down == 1'b0 && r_player_down == 1'b1) begin if (o_player_y < 15) begin o_player_y <= o_player_y + 1; end end // Similar logic for left and right... end
- Cars: Vehicles of varying speeds move horizontally across the screen, posing obstacles for the frog.
Example (from car.v):
The car.v module simulates the movement of cars across the screen, resetting their position after they pass the right side of the display:// moving the cars and resetting their position always @(posedge i_Clk) begin if (clock_tick < 12500000) begin clock_tick <= clock_tick + 1; end else begin clock_tick <= 0; if (o_car_x < 20) begin o_car_x <= o_car_x + 1; end else begin o_car_x <= 0; end end end
- Collision Detection: If the frog collides with a car, the game ends in a "Game Over" state.
Example (from main.v):
Collision detection checks whether the player's position matches that of any car:if ((car_x == player_x && car_y == player_y)) begin o_reset <= 1; // Reset condition upon collision end
- Goal: Each level has a designated goal area that the frog must reach to progress.
Example (from main.v):
The game keeps track of the current level and increments it when the frog reaches the top of the screen:if (player_y == 1) begin // if the player reaches the top if (level == 99) begin level <= 0; end else begin level <= level + 1; end end
User input is critical for navigating the game world. The system will respond to:
- Directional Movement: The FPGA switches will control the frog’s movements in four directions—up, down, left, and right.
Example (from player.v):
The player uses switches for directional control, with debounced inputs processed to ensure accurate movements:always @(posedge i_Clk) begin r_player_up <= w_player_up; r_player_down <= w_player_down; // Logic for left and right movements... if (w_player_up == 1'b0 && r_player_up == 1'b1) begin if (o_player_y > 1) begin o_player_y <= o_player_y - 1; end end // Similarly for down, left, and right... end
- Game Reset: Pressing all switches simultaneously will reset the game, restarting from the beginning.
Example (from player.v):
The reset functionality resets the player's position and the game state:always @(posedge i_Clk) begin // Reset game when all switches are pressed if (i_Switch_1 && i_Switch_2 && i_Switch_3 && i_Switch_4) begin i_reset <= 1; // Activate reset end else begin i_reset <= 0; // Deactivate reset end // Reset level based on reset signal if (i_reset) begin level <= 0; // Reset level on active reset end else if (/* condition for level progression */) begin level <= (level == 99) ? 0 : level + 1; // Increment level or wrap around end end
- Debounced Inputs: Debouncing ensures reliable control, preventing unintended multiple actions from a single switch press.
Example (from debounce_switch.v):
The debounce logic stabilizes the input switch state before passing it to the player control module:always @(posedge i_Clk) begin if (i_Switch !== r_State && r_Count < c_DEBOUNCE_LIMIT) r_Count <= r_Count + 1; // Switch stabilization logic... end assign o_Switch = r_State;
To enhance the gaming experience, the visual display on the VGA screen should include:
- Frog Sprite: A clearly defined, smoothly frog that the player controls.
Example (from main.v):
The output logic for the player sprite indicates its position:if (cell_x == player_x && cell_y == player_y) begin // Color the player o_VGA_Red = 3'b000; o_VGA_Grn = 3'b000; o_VGA_Blu = 3'b111; // Blue for player end
- Moving Cars: Vehicles of varying speeds and sizes that traverse the screen, presenting obstacles.
Example (from car.v):
The car module controls the horizontal velocity of cars based on a clock:// Car modules instantiation wire [4:0] car1_x; wire [3:0] car1_y = 3; car #(.CAR_START(1), .CAR_SPEED(1)) car1_module ( .i_Clk(i_Clk), .o_car_x(car1_x), ); wire [4:0] car2_x; wire [3:0] car2_y = 4; car #(.CAR_START(15), .CAR_SPEED(2)) car2_module ( .i_Clk(i_Clk), .o_car_x(car2_x), );
- Background Elements: A thematic background (road, grass, etc.) that sets the game environment.
Example (from main.v):
always @(posedge i_Clk) begin // VGA refresh logic to handle color output if (h_active && v_active) begin // Road background logic if ((cell_y >= road_top_start && cell_y < road_top_end) || (cell_y >= road_bottom_start && cell_y < road_bottom_end)) begin o_VGA_Red <= 3'b001; // Set Road Color (Grey) o_VGA_Grn <= 3'b001; o_VGA_Blu <= 3'b001; end // Grass background logic else if ((cell_y >= grass_arrival_start && cell_y <= grass_arrival_end) || (cell_y >= grass_middle_start && cell_y <= grass_middle_end) || (cell_y >= grass_spawn_start && cell_y <= grass_spawn_end)) begin o_VGA_Red <= 3'b000; // Set Grass Color (Green) o_VGA_Grn <= 3'b101; o_VGA_Blu <= 3'b001; end // Background clearing else begin o_VGA_Red <= 3'b000; o_VGA_Grn <= 3'b000; o_VGA_Blu <= 3'b000; // Set Background to Black end end end
- 7-Segment Display: Displays the current level and other important game information.
Example (from seven_segments.v):
The 7-segment display updates to show the current level:module seven_segments ( input [6:0] counter, // level counter output reg [6:0] o_Segment1, output reg [6:0] o_Segment2 ); always @(*) begin case (counter / 10) // Display for first 7-segment display 1: o_Segment1 = 7'b1111001; // 1 2: o_Segment1 = 7'b0100100; // 2 3: o_Segment1 = 7'b0110000; // 3 4: o_Segment1 = 7'b0011001; // 4 5: o_Segment1 = 7'b0010010; // 5 6: o_Segment1 = 7'b0000010; // 6 7: o_Segment1 = 7'b1111000; // 7 8: o_Segment1 = 7'b0000000; // 8 9: o_Segment1 = 7'b0010000; // 9 default: o_Segment1 = 7'b1000000; // 0 endcase case (counter % 10) // Display for second 7-segment display 1: o_Segment2 = 7'b1111001; // 1 2: o_Segment2 = 7'b0100100; // 2 3: o_Segment2 = 7'b0110000; // 3 4: o_Segment2 = 7'b0011001; // 4 5: o_Segment2 = 7'b0010010; // 5 6: o_Segment2 = 7'b0000010; // 6 7: o_Segment2 = 7'b1111000; // 7 8: o_Segment2 = 7'b0000000; // 8 9: o_Segment2 = 7'b0010000; // 9 default: o_Segment2 = 7'b1000000; // 0 endcase end endmodule
The game will handle multiple states, ensuring smooth transitions and clear objectives at all times:
-
Start Screen: There won’t be a dedicated start screen as we decide to focus on the main requirement of the game.
-
Play State: The core gameplay where the player controls the frog, attempting to cross the screen without hitting a car.
-
Game Over State: If a collision occurs, the game ends with a game over screen.
Example (from main.v):
Collision detection triggers the game over state:// Game states typedef enum logic [1:0] { STATE_PLAY, STATE_GAME_OVER } game_state_t; game_state_t game_state = STATE_PLAY; always @(posedge i_Clk) begin case (game_state) STATE_PLAY: begin if (collision_detected) begin game_state <= STATE_GAME_OVER; end else if (player_y == 1) begin if (level == 99) begin level <= 0; end else begin level <= level + 1; end i_reset <= 1; end else begin i_reset <= 0; end end STATE_GAME_OVER: begin // Logic to display game over message and stop the game // Reset the game state and level if needed end endcase end
Level progression is a key part of keeping players engaged and challenged. The game will feature:
- Level Progression: With each completed level, difficulty ramps up—cars move faster, lanes get narrower, and new obstacles appear.
Example (from main.v): The game manages level progression, adjusting car speeds and other parameters based on the current level:
always @(posedge i_Clk) begin if (player_y == 1) begin if (level == 99) begin //check for max level level <= 0; end else begin level <= level + 1; // Increment level end end else if (o_reset == 1) begin level <= 0; end // Adjust car parameters based on the current level if (level > 10) begin // Add more cars or increase speed car1_module.CAR_SPEED <= 2; car2_module.CAR_SPEED <= 3; // more car instances end else if (level > 20) begin // Further increase difficulty car1_module.CAR_SPEED <= 3; car2_module.CAR_SPEED <= 4; // more car instances end end
- Level Information: The 7-segment display will clearly indicate the current level, helping players track their progress.
Example (from seven_segments.v):
The display shows level information as part of the score:o_Segment1 = level; // Display current level
The cars are more than just obstacles; they introduce variability and challenge. Their behavior includes:
- Horizontal Movement: Cars move steadily across the screen from either left to right or right to left.
Example (from car.v):
Cars reset their position after traveling a defined distance:if (o_car_x < 20) begin o_car_x <= o_car_x + 1; // Move car horizontally end else begin o_car_x <= 0; // Reset position end
- Lane Occupancy: Only one car per lane at any given time, avoiding lane overcrowding.
Example (from main.v):
// Lane occupancy logic reg [3:0] lanes [0:9]; // Array to keep track of lane occupancy always @(posedge i_Clk) begin // Reset lane occupancy integer j; for (j = 0; j < 10; j = j + 1) begin lanes[j] <= 0; end // Update lane occupancy based on car positions lanes[car1_y] <= 1; lanes[car2_y] <= 1; for (i = 3; i <= 10; i = i + 1) begin lanes[car_gen[i].car_y] <= 1; end end
-
Hardware Limitations: The FPGA development board may have finite resources, such as the number of logic cells and RAM available. These limitations could hinder the ability to implement all desired game features, such as advanced graphics or complex gameplay mechanics. For example, if too much logic is required for player controls and graphics, the overall system may become unresponsive.
-
Timing Issues: Ensuring smooth performance without lag can be particularly challenging due to the strict timing requirements of the VGA output and the handling of user inputs. If the timing of these signals is off, it can lead to visual artifacts or delayed responses, greatly affecting gameplay.
-
Input Debouncing: The physical switches used for player input can experience bouncing, causing multiple triggers from a single press. If this bouncing is not controlled, the player may experience unintended movements during gameplay. This can lead to frustration and affect the overall gaming experience.
-
Collision Detection Challenges: Detecting collisions accurately between the frog and the moving cars or other obstacles can be difficult. If this aspect is not well-implemented, players might find themselves colliding inconsistently, which can frustrate players and make the game feel unfair or broken.
-
Integration Issues: Integrating various modules (e.g., player controls, cars, road layout) may lead to unexpected interactions and bugs. For instance, if the collision detection relies on player position but the position is not calculated correctly due to integration errors, this could lead to game malfunction.
-
Resource Overuse: Some game features may consume more computational resources than anticipated, resulting in performance drops. For example, if too many simultaneous vehicles are being processed, it may slow down the game due to inadequate computational resources.
-
Resource Management: Regularly monitor and evaluate resource usage throughout development. This includes profiling your logic and memory consumption to ensure that you stay within the limits of the FPGA. If resource constraints arise, consider simplifying graphics or features. Example: If memory utilization approaches max capacity, implementing sprite sheets for characters could be a solution to lower memory demands.
-
Timing Analysis: Conduct a thorough examination of the timing requirements and refine your design to ensure smooth performance. This can include calculating the required delays for different signals and ensuring they meet the necessary timing constraints. Utilize tools like Logicly for manual timing checks to verify that your signal outputs are correct.
-
Robust Debouncing Logic: Develop an effective debouncing mechanism to filter out noise from switch inputs, ensuring that only stable signals are registered for game controls.
For example:
always @(posedge i_Clk) begin if (i_Switch !== r_State) begin r_Count <= r_Count + 1; // Increment count for bouncing end else if (r_Count > DEBOUNCE_LIMIT) begin r_State <= i_Switch; // Update state only on stable signal r_Count <= 0; // Reset count end end
-
Comprehensive Collision Testing: Create a detailed collision detection algorithm that is tested against various scenarios. Extensive testing will ensure that the game properly detects all collision cases, diminishing player frustration. For example, if a player is at the edge of a car sprite, your algorithm should still recognize their position accurately.
-
Iterative Testing: Instead of focusing on complex debugging routines in Verilog, we have chosen to streamline our process by utilizing direct monitoring to test the code. This approach allows us to observe the performance and functionality of the game as it runs on the FPGA, enabling quicker validation and troubleshooting without getting bogged down in debug complexities.
-
Module Integration Testing: Before final implementation, conduct robust tests of how all modules work together. Identify potential conflicts or unexpected behaviors. Creating testbenches for each module will validate interactions and help isolate issues early.
-
Resource Monitoring: Continuously monitor the FPGA to ensure it is within its resource capacity. If issues are detected, you can identify features that are consuming excess resources and either modify or remove them for optimal performance.
Terms | Definitions | Source |
---|---|---|
Frogger | A classic arcade game where the player navigates a frog across a busy road and river. | Wikipedia |
FPGA | Field-Programmable Gate Array, an integrated circuit designed to be configured by a customer or a designer after manufacturing. | ARM |
Verilog | A hardware description language used to model electronic systems. | Nandland |
VGA | Video Graphics Array, a display standard for PC computers. | Wikipedia |
Debouncing | The process of removing noise or bounce from a signal, typically used for switch inputs in digital systems. | Techtarget |
Sprite | A two-dimensional image or animation that is integrated into a larger scene. | Wikipedia |
7-Segment Displays | A form of electronic display device for displaying decimal numerals. | Wikipedia |
Maintainability | The ease with which a software system can be maintained or modified. | Sealights |
APIs | Application Programming Interfaces, a set of rules and protocols for building and interacting with software applications. | Mulesoft |
breadboards | A construction base for prototyping of electronics. | Makeuseof |
LEDs | Light-Emitting Diode, a semiconductor light source. | Wikipedia |
15-pin D-sub | A type of electrical connector commonly used for VGA connections. | EDAC |
Clock division | A circuit that divides the frequency of an input clock signal to produce a lower-frequency output. | Wikipedia |
Clock signal | A periodic signal used to synchronize the operation of digital circuits. | ScienceDirect |
Game State | The current state of the game, including the position of the player, obstacles, and other game elements. | |
Horizontal Sync | A signal used in video systems to coordinate the refresh rate of the display. | ScienceDirect |
Vertical Sync | A signal used in video systems to indicate the end of a frame and the beginning of a new one. | ScienceDirect |
Registers | Small, fast memory elements used to store data within digital circuits. | TutorialsPoint |
BRAM | Block RAM, a type of memory available in FPGAs that can be used for storing data. | VHDLwhiz |
LUTs | Look-Up Table, a type of memory used in FPGAs to implement logic functions. | FPGAinsights |