How to Build a Real-Time Whac-A-Mole Game on an FPGA?
A Verilog guide to Finite State Machines, timers, and creating pseudo-randomness with Linear Feedback Shift Registers (LFSRs).
How to Build a Real-Time Whac-A-Mole Game on an FPGA?
A Verilog guide to Finite State Machines, timers, and creating pseudo-randomness with Linear Feedback Shift Registers (LFSRs).

A top-down view of the Nexys A7 FPGA development board
Remember the frantic, light-up fun of an arcade “Whac-A-Mole” game? That test of pure reaction speed is a perfect project for an FPGA. Why? Because FPGAs are masters of precise, parallel timing, making them ideal for building real-time games where every nanosecond counts.
In this guide, we will build a “Whac-A-Mole” game on a Nexys A7 FPGA Board. A “mole” (an LED) will light up at a random-seeming interval, and you will have a fraction of a second to “hit” the corresponding button. We will even keep score and track your lives as well.
This article assumes you have a basic understanding of Verilog HDL and are familiar with creating a project in the Xilinx Vivado Design Suite. We will be focusing on the game’s architecture and the interesting logic behind it.
The Game’s Blueprint
Before we write any code, let’s define our game’s rules and components:
- The Moles & Hammers: We will use 5 of the Nexys A7’s LEDs (LD0 – L D4) as our “moles” and the 5 corresponding push-buttons (BTNC, BTNU, BTND, BTNL, BTNR) as our “hammers.”
- The Brain (FSM): A Finite State Machine will control the game’s flow, moving between states like “waiting for a mole,” “mole appears,” “player scored,” and “player missed.”
- How do we make the game unpredictable with the “Randomizer”? We are going to make a Linear Feedback Shift Register (LFSR). We will use this special circuit to make a long, fake-random string of numbers that will help us choose which mole to pop up and how long to wait.
- The Timers: We need two clocks:
- A Wait Timer that counts down until a new mole shows up (to keep the player on their toes).
- A Reaction Timer that gives the player a short amount of time (0.5 seconds) to hit the active mole.
- Scoring: We will use 5 more LEDs (LD5–LD9) to show the player’s score in binary (0–31) and 3 LEDs (LD10–LD12) to show how many lives they have left (0–7).
LFSR — The Magic of Fake Randomness:
You just use the rand function in software. We make the randomizer ourselves in hardware. The LFSR is a clever, efficient way to do this.
An LFSR is a shift register where the input bit is a product of some of the previous bits, tapped and XORed together. It generates a predictable, but very long, sequence of numbers that appears random.
We will build a simple 16-bit LFSR. This will generate a new 16-bit “random” number on every clock cycle. We can use the lower 3 bits to pick a mole (0–4) and the upper 8 bits to set the delay time.
Writing the Game Logic in Verilog
Let’s build our two main modules. First, the lfsr, and second, the main whac_a_mole game logic.
1. The LFSR Module (lfsr.v)
This module is small but powerful. It just shuffles its bits on every clock cycle to produce a new pseudo-random number.
module lfsr (
input wire clk,
output reg [15:0] random_out
);
// 16-bit LFSR with taps at 16, 14, 13, 11
wire feedback;
assign feedback = random_out[15] ^ random_out[13] ^ random_out[12] ^ random_out[10];
always @(posedge clk)
begin
// Start with a non-zero seed
if (random_out == 0)
begin
random_out <= 16'hACE1;
end
else
begin
// Shift all bits and add feedback to the LSB
random_out <= {random_out[14:0], feedback};
end
end
endmodule
2. The Main Game Module (whac_a_mole.v)
This is the heart of our game. It contains the button debouncers, the FSM, all timers, and the game state (score/lives). This file is a little complex, so comments are included to explain each part.
module whac_a_mole (
input wire clk,
input wire btnC, btnU, btnD, btnL, btnR, // Our 5 "hammers"
output wire [4:0] moles_leds, // LD0-LD4 (The moles)
output wire [4:0] score_leds, // LD5-LD9 (Score in binary)
output wire [2:0] lives_leds, // LD10-LD12 (Lives in binary)
output wire game_over_led // LD15 (Game Over)
);
// 1. RANDOM NUMBER GENERATOR
wire [15:0] lfsr_out;
lfsr u_lfsr (
.clk(clk),
.random_out(lfsr_out)
);
// Use LFSR output
wire [2:0] random_mole_index = lfsr_out[2:0]; // Use 3 bits
wire [7:0] random_wait_time = lfsr_out[10:3]; // Use 8 bits for delay
// 2. BUTTON DEBOUNCING
// We need 5 debouncers. This is repetitive but crucial.
// (A more advanced design might use an array of modules)
// Simple debouncer: 2-stage synchronizer and a counter
// We will just show one, the other four are identical
reg [1:0] sync_btnC;
reg [19:0] count_btnC;
reg debounced_btnC;
always @(posedge clk)
begin
sync_btnC <= {sync_btnC[0], btnC};
if (sync_btnC[0] != sync_btnC[1])
begin
count_btnC <= 0;
end
else if (count_btnC < 20'd100000) // ~1ms
begin
count_btnC <= count_btnC + 1;
end
else
begin
debounced_btnC <= sync_btnC[1];
end
end
// ... (Repeat the debouncer logic above for btnU, btnD, btnL, btnR) ...
// ... (For brevity, we will assume debounced_btnU, debounced_btnD,
// debounced_btnL, debounced_btnR exist)
// For this guide, we will create simple wires to continue
wire debounced_btnU, debounced_btnD, debounced_btnL, debounced_btnR;
// (This is a simplification; use the 5x debouncer logic)
// 3. GAME STATE REGISTERS
reg [4:0] score = 0;
reg [2:0] lives = 3;
reg [4:0] active_mole = 5'b0; // 1-hot: 00001, 00010, etc.
reg [2:0] active_mole_index = 0; // 0, 1, 2, 3, or 4
reg [27:0] reaction_timer = 0; // 28-bit for ~0.5 sec
reg [27:0] wait_timer = 0; // 28-bit for random delay
localparam REACTION_TIME = 28'd50000000; // 0.5 sec
// 4. GAME LOGIC FSM
localparam STATE_WAIT_NEW_MOLE = 2'b00;
localparam STATE_MOLE_ACTIVE = 2'b01;
localparam STATE_HIT = 2'b10;
localparam STATE_MISS = 2'b11;
reg [1:0] state = STATE_WAIT_NEW_MOLE;
// Check for a hit
wire correct_hit;
assign correct_hit = (debounced_btnC & active_mole[0]) |
(debounced_btnU & active_mole[1]) |
(debounced_btnD & active_mole[2]) |
(debounced_btnL & active_mole[3]) |
(debounced_btnR & active_mole[4]);
// Check for a wrong hit
wire wrong_hit;
assign wrong_hit = (debounced_btnC & ~active_mole[0]) |
(debounced_btnU & ~active_mole[1]) |
(debounced_btnD & ~active_mole[2]) |
(debounced_btnL & ~active_mole[3]) |
(debounced_btnR & ~active_mole[4]);
always @(posedge clk)
begin
if (lives == 0)
begin
// Game Over state - freeze everything
state <= STATE_WAIT_NEW_MOLE;
active_mole <= 5'b0;
end
else
begin
case (state)
STATE_WAIT_NEW_MOLE:
begin
active_mole <= 5'b0; // All moles off
// Wait for a random amount of time
if (wait_timer < (random_wait_time * 10000))
begin
wait_timer <= wait_timer + 1;
end
else
begin
wait_timer <= 0;
reaction_timer <= 0;
// Pick a new mole (0-4)
if (random_mole_index <= 4)
begin
active_mole_index <= random_mole_index;
active_mole <= (1 << random_mole_index); // 1-hot
state <= STATE_MOLE_ACTIVE;
end
// if index > 4, we just wait another cycle for a new number
end
end
STATE_MOLE_ACTIVE:
begin
if (correct_hit)
begin
state <= STATE_HIT; // Player scored
end
else if (wrong_hit || (reaction_timer == REACTION_TIME))
begin
state <= STATE_MISS; // Player missed
end
else
begin
reaction_timer <= reaction_timer + 1;
end
end
STATE_HIT:
begin
score <= score + 1;
state <= STATE_WAIT_NEW_MOLE; // Go wait for next mole
end
STATE_MISS:
begin
lives <= lives - 1;
state <= STATE_WAIT_NEW_MOLE; // Go wait for next mole
end
endcase
end
end
// 5. ASSIGN OUTPUTS
assign moles_leds = active_mole;
assign score_leds = score;
assign lives_leds = lives;
assign game_over_led = (lives == 0);
endmodule
Connecting to the Nexys A7 (XDC)
With the logic complete, we just need to create our XDC constraints file to map our Verilog ports to the physical pins on the Nexys A7.
(Note: These are for the Nexys A7–100T. You need to map as per your board’s pins)
# System Clock
set_property -dict {PACKAGE_PIN E3 IOSTANDARD LVCMOS33} [ get_ports clk ]
create_clock -period 10.000 -name sys_clk_pin -waveform {0.000 5.000} [ get_ports clk ]
# Buttons ("Hammers")
set_property -dict {PACKAGE_PIN N17 IOSTANDARD LVCMOS33} [ get_ports btnC ];
set_property -dict {PACKAGE_PIN M18 IOSTANDARD LVCMOS33} [ get_ports btnU ];
set_property -dict {PACKAGE_PIN P18 IOSTANDARD LVCMOS33} [ get_ports btnD ];
set_property -dict {PACKAGE_PIN M17 IOSTANDARD LVCMOS33} [ get_ports btnL ];
set_property -dict {PACKAGE_PIN P17 IOSTANDARD LVCMOS33} [ get_ports btnR ];
# LEDs ("Moles" LD0-LD4)
set_property -dict {PACKAGE_PIN H17 IOSTANDARD LVCMOS33} [ get_ports {moles_leds[0]} ];
set_property -dict {PACKAGE_PIN K15 IOSTANDARD LVCMOS33} [ get_ports {moles_leds[1]} ];
set_property -dict {PACKAGE_PIN J13 IOSTANDARD LVCMOS33} [ get_ports {moles_leds[2]} ];
set_property -dict {PACKAGE_PIN N14 IOSTANDARD LVCMOS33} [ get_ports {moles_leds[3]} ];
set_property -dict {PACKAGE_PIN R18 IOSTANDARD LVCMOS33} [ get_ports {moles_leds[4]} ];
# LEDs ("Score" LD5-LD9)
set_property -dict {PACKAGE_PIN V17 IOSTANDARD LVCMOS33} [ get_ports {score_leds[0]} ];
set_property -dict {PACKAGE_PIN U17 IOSTANDARD LVCMOS33} [ get_ports {score_leds[1]} ];
set_property -dict {PACKAGE_PIN U16 IOSTANDARD LVCMOS33} [ get_ports {score_leds[2]} ];
set_property -dict {PACKAGE_PIN V16 IOSTANDARD LVCMOS33} [ get_ports {score_leds[3]} ];
set_property -dict {PACKAGE_PIN T15 IOSTANDARD LVCMOS33} [ get_ports {score_leds[4]} ];
# LEDs ("Lives" LD10-LD12)
set_property -dict {PACKAGE_PIN U14 IOSTANDARD LVCMOS33} [ get_ports {lives_leds[0]} ];
set_property -dict {PACKAGE_PIN T16 IOSTANDARD LVCMOS33} [ get_ports {lives_leds[1]} ];
set_property -dict {PACKAGE_PIN V15 IOSTANDARD LVCMOS33} [ get_ports {lives_leds[2]} ];
# LED ("Game Over" LD15)
set_property -dict {PACKAGE_PIN V11 IOSTANDARD LVCMOS33} [ get_ports game_over_led ];
Powering On Your Arcade
You are all set. Now, just follow the standard flow in Vivado:
- Run Run Synthesis .
- Run Run Implementation.
- Run Generate Bitstream.
- Open Hardware Manager, connect your Nexys A7 board with your machine, and Program Device.
Your FPGA is now a fully functional reaction game. With the help of parallel timers, real-time I/O, pseudo-random number generation, and FSMs, you have successfully constructed an entire system.
Watch the LEDs light up and test your reflexes! 😉
메타데이터
- post_id
- c9daae29d843
- slug
- how-to-build-a-real-time-whac-a-mole-game-on-an-fpga-c9daae29d843
- url
- https://medium.com/@msashahzaib/how-to-build-a-real-time-whac-a-mole-game-on-an-fpga-c9daae29d843
- canonical_url
- https://medium.com/@msashahzaib/how-to-build-a-real-time-whac-a-mole-game-on-an-fpga-c9daae29d843
- author_url
- https://medium.com/@msashahzaib
- status
- ok
- fetched_at
- 2026-08-30 11:14:21