Industrial manufacturing
Industrial Internet of Things | Industrial materials | Equipment Maintenance and Repair | Industrial programming |
home  MfgRobots >> Industrial manufacturing >  >> Industrial programming >> VHDL

Building Your First FPGA: LED Blink Tutorial (VHDL & Verilog)

Introduction

Welcome to a hands‑on guide that walks you through creating a simple LED blinker on an FPGA using either VHDL or Verilog. The example demonstrates core FPGA concepts: concurrent processes, frequency synthesis, and multiplexer logic—all driven by a 25 MHz clock.

Project Overview

Frequency Selection Truth Table

EnableSwitch 1Switch 2LED Drive Frequency
0--(disabled)
100100 Hz
10150 Hz
11010 Hz
1111 Hz

Signal Definitions

Signal NameDirectionDescription
i_clockInput25 MHz system clock
i_enableInputLED enable switch (logic 0 = off)
i_switch_1InputFrequency selector bit 1
i_switch_2InputFrequency selector bit 0
o_led_driveOutputDrive signal for the LED

Design Philosophy

Four independent counter processes run concurrently, each toggling a local signal at its target frequency. Even when a particular frequency is not selected, its counter continues to run—this is the hallmark of FPGA concurrency.

The switches implement a combinational multiplexer that routes the desired toggle signal to the LED driver. The LED_EN switch acts as an active‑high gate, ensuring the LED turns off when the enable is low.

Block Diagram

Building Your First FPGA: LED Blink Tutorial (VHDL & Verilog)

VHDL Implementation

Below is a fully commented VHDL module that satisfies the specification. The counter constants are calculated using the formula:
count = (clock frequency / target frequency) × 0.5 to achieve a 50 % duty cycle.

library ieee;
use ieee.std_logic_1164.all;
use ieee.numeric_std.all;

entity tutorial_led_blink is
  port (
    i_clock      : in  std_logic;
    i_enable     : in  std_logic;
    i_switch_1   : in  std_logic;
    i_switch_2   : in  std_logic;
    o_led_drive  : out std_logic
  );
end tutorial_led_blink;

architecture rtl of tutorial_led_blink is

  -- Frequency constants (half‑period counts)
  constant c_CNT_100HZ : natural := 125000;   -- 25 MHz / 100 Hz × 0.5
  constant c_CNT_50HZ  : natural := 250000;   -- 25 MHz / 50 Hz  × 0.5
  constant c_CNT_10HZ  : natural := 1250000;  -- 25 MHz / 10 Hz  × 0.5
  constant c_CNT_1HZ   : natural := 12500000; -- 25 MHz / 1 Hz   × 0.5

  -- Counter signals
  signal r_CNT_100HZ : natural range 0 to c_CNT_100HZ;
  signal r_CNT_50HZ  : natural range 0 to c_CNT_50HZ;
  signal r_CNT_10HZ  : natural range 0 to c_CNT_10HZ;
  signal r_CNT_1HZ   : natural range 0 to c_CNT_1HZ;

  -- Toggle outputs
  signal r_TOGGLE_100HZ : std_logic := '0';
  signal r_TOGGLE_50HZ  : std_logic := '0';
  signal r_TOGGLE_10HZ  : std_logic := '0';
  signal r_TOGGLE_1HZ   : std_logic := '0';

  -- Multiplexer selection
  signal w_LED_SELECT : std_logic;

begin

  -- 100 Hz toggling process
  p_100_HZ : process (i_clock) is
  begin
    if rising_edge(i_clock) then
      if r_CNT_100HZ = c_CNT_100HZ-1 then
        r_TOGGLE_100HZ <= not r_TOGGLE_100HZ;
        r_CNT_100HZ    <= 0;
      else
        r_CNT_100HZ <= r_CNT_100HZ + 1;
      end if;
    end if;
  end process p_100_HZ;

  -- 50 Hz toggling process
  p_50_HZ : process (i_clock) is
  begin
    if rising_edge(i_clock) then
      if r_CNT_50HZ = c_CNT_50HZ-1 then
        r_TOGGLE_50HZ <= not r_TOGGLE_50HZ;
        r_CNT_50HZ    <= 0;
      else
        r_CNT_50HZ <= r_CNT_50HZ + 1;
      end if;
    end if;
  end process p_50_HZ;

  -- 10 Hz toggling process
  p_10_HZ : process (i_clock) is
  begin
    if rising_edge(i_clock) then
      if r_CNT_10HZ = c_CNT_10HZ-1 then
        r_TOGGLE_10HZ <= not r_TOGGLE_10HZ;
        r_CNT_10HZ    <= 0;
      else
        r_CNT_10HZ <= r_CNT_10HZ + 1;
      end if;
    end if;
  end process p_10_HZ;

  -- 1 Hz toggling process
  p_1_HZ : process (i_clock) is
  begin
    if rising_edge(i_clock) then
      if r_CNT_1HZ = c_CNT_1HZ-1 then
        r_TOGGLE_1HZ <= not r_TOGGLE_1HZ;
        r_CNT_1HZ    <= 0;
      else
        r_CNT_1HZ <= r_CNT_1HZ + 1;
      end if;
    end if;
  end process p_1_HZ;

  -- Multiplexer based on switch inputs
  w_LED_SELECT <= r_TOGGLE_100HZ when (i_switch_1 = '0' and i_switch_2 = '0') else
                  r_TOGGLE_50HZ  when (i_switch_1 = '0' and i_switch_2 = '1') else
                  r_TOGGLE_10HZ  when (i_switch_1 = '1' and i_switch_2 = '0') else
                  r_TOGGLE_1HZ;

  -- Gate the LED drive with the enable switch
  o_led_drive <= w_LED_SELECT and i_enable;

end rtl;

Verilog Implementation

Below is an equivalent Verilog module. The design philosophy is identical—four independent always blocks, each toggling at a specific frequency, and a combinational multiplexer driven by the switches.

module tutorial_led_blink 
  (
   i_clock,
   i_enable,
   i_switch_1,
   i_switch_2,
   o_led_drive
   );

  input i_clock;
  input i_enable;
  input i_switch_1;
  input i_switch_2;
  output o_led_drive;
   
  // Frequency parameters (half‑period counts)
  parameter c_CNT_100HZ = 125000;
  parameter c_CNT_50HZ  = 250000;
  parameter c_CNT_10HZ  = 1250000;
  parameter c_CNT_1HZ   = 12500000;

  // Counter registers
  reg [31:0] r_CNT_100HZ = 0;
  reg [31:0] r_CNT_50HZ  = 0;
  reg [31:0] r_CNT_10HZ  = 0;
  reg [31:0] r_CNT_1HZ   = 0;
  
  // Toggle registers
  reg r_TOGGLE_100HZ = 1'b0;
  reg r_TOGGLE_50HZ  = 1'b0;
  reg r_TOGGLE_10HZ  = 1'b0;
  reg r_TOGGLE_1HZ   = 1'b0;
  
  // Multiplexer selection
  reg r_LED_SELECT;
  wire w_LED_SELECT;
  
  // 100 Hz toggle block
  always @(posedge i_clock) begin
    if (r_CNT_100HZ == c_CNT_100HZ-1) begin
      r_TOGGLE_100HZ <= ~r_TOGGLE_100HZ;
      r_CNT_100HZ    <= 0;
    end else
      r_CNT_100HZ <= r_CNT_100HZ + 1;
  end

  // 50 Hz toggle block
  always @(posedge i_clock) begin
    if (r_CNT_50HZ == c_CNT_50HZ-1) begin
      r_TOGGLE_50HZ <= ~r_TOGGLE_50HZ;
      r_CNT_50HZ    <= 0;
    end else
      r_CNT_50HZ <= r_CNT_50HZ + 1;
  end

  // 10 Hz toggle block
  always @(posedge i_clock) begin
    if (r_CNT_10HZ == c_CNT_10HZ-1) begin
      r_TOGGLE_10HZ <= ~r_TOGGLE_10HZ;
      r_CNT_10HZ    <= 0;
    end else
      r_CNT_10HZ <= r_CNT_10HZ + 1;
  end

  // 1 Hz toggle block
  always @(posedge i_clock) begin
    if (r_CNT_1HZ == c_CNT_1HZ-1) begin
      r_TOGGLE_1HZ <= ~r_TOGGLE_1HZ;
      r_CNT_1HZ    <= 0;
    end else
      r_CNT_1HZ <= r_CNT_1HZ + 1;
  end

  // Combinational multiplexer
  always @(*) begin
    case ({i_switch_1, i_switch_2})
      2'b11: r_LED_SELECT <= r_TOGGLE_1HZ;
      2'b10: r_LED_SELECT <= r_TOGGLE_10HZ;
      2'b01: r_LED_SELECT <= r_TOGGLE_50HZ;
      2'b00: r_LED_SELECT <= r_TOGGLE_100HZ;
    endcase
  end

  // Drive LED with enable gating
  assign o_led_drive = r_LED_SELECT & i_enable;

endmodule

Simulation & Verification

Prior to programming the FPGA, run a behavioral simulation (e.g., with ModelSim, VCS, or Vivado Simulator). Verify that each toggle signal matches the expected period and that the multiplexer correctly routes the signal based on the switch combination. Simulating early eliminates costly hardware debugging and confirms that the 50 % duty cycle is maintained.

Conclusion

This tutorial demonstrates how to build a compact, reusable LED blinker that can be adapted to other timing tasks. The same counter‑based frequency synthesis pattern scales to more complex state machines, making it a foundational skill for any FPGA developer.

VHDL

  1. C# Hello World – Building Your First C# Application
  2. Your First VHDL Program: A Step‑by‑Step Hello World Tutorial
  3. C Hello World: Your First Program – A Step‑by‑Step Guide
  4. Java Hello World: Step‑by‑Step Guide to Writing Your First Java Program
  5. Hello World: Building Your First Python Application
  6. Master Verilog: Complete Tutorial for Digital Circuit Design
  7. Fast-Track Your Custom Quote: Accurate Pricing in 2–3 Days
  8. Designing an Effective Preventative Maintenance Program for Equipment
  9. Master PADS PCB Design: Advanced Features & Tutorials
  10. First CNC Program: Step-by-Step CNC Machining Guide