Промышленное производство
Промышленный Интернет вещей | Промышленные материалы | Техническое обслуживание и ремонт оборудования | Промышленное программирование |
home  MfgRobots >> Промышленное производство >  >> Industrial programming >> VHDL

Пошаговое руководство: создание мигающего светодиода на FPGA (VHDL/Verilog)

Пошаговое руководство: создание мигающего светодиода на FPGA

Часть 1: Проектирование на VHDL или Verilog

В этом уроке вы научитесь писать код на VHDL и Verilog для управления светодиодом с настраиваемой частотой. Выбирайте язык, который лучше подходит вашему рабочему процессу.

При написании HDL-кода необходимо проверять, что проект ведёт себя как задумано. Ошибки неизбежны, поэтому симуляция обязательна. Урок разбит на два ключевых этапа:

  1. Проектирование HDL
  2. Симуляция HDL

Пропуск симуляции может привести к затратной отладке на железе. Считайте симуляцию обязательной контрольной точкой.

Требования к проекту

Написать HDL-код, который мигает светодиодом на частотах 100 Гц, 50 Гц, 10 Гц или 1 Гц со скважностью 50%. Два переключателя выбирают нужную частоту, а дополнительный переключатель LED_EN должен быть в логической единице для включения светодиода. FPGA тактируется от генератора 25 МГц.

Таблица истинности для выбора частоты:

EnableSwitch 1Switch 2Частота светодиода
0––выключен
100100 Гц
10150 Гц
11010 Гц
1111 Гц

Сводка сигналов:

Имя сигналаНаправлениеОписание
i_clockВходТактирующий сигнал 25 МГц
i_enableВходПереключатель включения (логический 0 = светодиод выключен)
i_switch_1ВходПереключатель выбора частоты 1
i_switch_2ВходПереключатель выбора частоты 2
o_led_driveВыходСигнал управления светодиодом

Четыре параллельных процесса-счётчика отслеживают тактовый сигнал 25 МГц и генерируют переключения для каждой целевой частоты. Даже когда конкретная частота не выбрана, её счётчик продолжает работать — это фундаментальный принцип параллелизма в аппаратуре.

Переключатели формируют мультиплексор, который направляет выбранный сигнал переключения на выход светодиода. Мультиплексоры — это чисто комбинационная логика, они работают без тактового сигнала.

Ниже приведена блок-схема архитектуры:

Пошаговое руководство: создание мигающего светодиода на FPGA (VHDL/Verilog)

Реализация на VHDL

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
    -- Константы для создания нужных частот:
    -- Формула: (25 МГц / частота * 50% скважность)
    -- Для 100 Гц: 25_000_000 / 100 * 0.5 = 125_000
    constant c_CNT_100HZ : natural := 125000;
    constant c_CNT_50HZ  : natural := 250000;
    constant c_CNT_10HZ  : natural := 1250000;
    constant c_CNT_1HZ   : natural := 12500000;

    -- Счётчики
    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;

    -- Сигналы переключения на нужных частотах
    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';

    -- Один бит выбора
    signal w_LED_SELECT : std_logic;
begin
    -- Все процессы переключают свой сигнал на своей частоте.
    -- Они работают непрерывно, даже если переключатели не выбирают их выход.
    p_100_HZ : process (i_clock) is
    begin
        if rising_edge(i_clock) then
            if r_CNT_100HZ = c_CNT_100HZ-1 then -- -1, так как счётчик начинается с 0
                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;

    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;

    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;

    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;

    -- Мультиплексор на основе входов переключателей
    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;

    -- Разрешаем o_led_drive только когда i_enable = '1' (логическое И).
    o_led_drive <= w_LED_SELECT and i_enable;
end rtl;

Реализация на Verilog

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;

    // Константы (параметры) для создания нужных частот:
    // Входная частота 25 кГц выбрана произвольно.
    // Формула: (25 кГц / 100 Гц * 50% скважность)
    // Для 100 Гц: 25_000 / 100 * 0.5 = 125
    parameter c_CNT_100HZ = 125;
    parameter c_CNT_50HZ  = 250;
    parameter c_CNT_10HZ  = 1250;
    parameter c_CNT_1HZ   = 12500;

    // Счётчики
    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;

    // Сигналы переключения
    reg r_TOGGLE_100HZ = 1'b0;
    reg r_TOGGLE_50HZ  = 1'b0;
    reg r_TOGGLE_10HZ  = 1'b0;
    reg r_TOGGLE_1HZ   = 1'b0;

    // Один бит выбора
    reg r_LED_SELECT;
    wire w_LED_SELECT;

    // Все always-блоки переключают свой сигнал на своей частоте.
    // Они работают непрерывно, даже если переключатели не выбирают их выход.
    always @ (posedge i_clock) begin
        if (r_CNT_100HZ == c_CNT_100HZ-1) // -1, так как счётчик начинается с 0
        begin
            r_TOGGLE_100HZ <= !r_TOGGLE_100HZ;
            r_CNT_100HZ <= 0;
        end else
            r_CNT_100HZ <= r_CNT_100HZ + 1;
    end

    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

    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

    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

    // Мультиплексор на основе входов переключателей
    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

    assign o_led_drive = r_LED_SELECT & i_enable;

    // Альтернативный способ мультиплексора (тот же результат):
    // Более компактный, но сложнее для чтения новичкам
    // assign w_LED_SELECT = i_switch_1 ? (i_switch_2 ? r_TOGGLE_1HZ : r_TOGGLE_10HZ) :
    //                       (i_switch_2 ? r_TOGGLE_50HZ : r_TOGGLE_100HZ);
    // assign o_led_drive = w_LED_SELECT & i_enable;
endmodule

Следующий шаг: просимулируйте этот проект на VHDL или Verilog, чтобы подтвердить корректное поведение перед загрузкой в FPGA.

VHDL

  1. Использование списков чувствительности в процессах VHDL для надежного RTL-проектирования
  2. Интеграция библиотек IP Quartus Prime в VUnit: пошаговое руководство
  3. Бесплатный симулятор и редактор VHDL: руководство для студентов
  4. Проверьте свои знания VHDL — Интерактивный тест, часть 1
  5. Руководство новичка по ModelSim для симуляции FPGA и ASIC
  6. Создание тактированного процесса в VHDL: практическое руководство
  7. Освоение циклов While в VHDL: динамическое управление итерациями
  8. Использование операторов Wait For для задержки выполнения в VHDL
  9. Использование процедур в VHDL: упростите свой дизайн с повторно используемым кодом
  10. Освоение инстанцирования port map в VHDL: практическое руководство