Пошаговое руководство: создание мигающего светодиода на FPGA (VHDL/Verilog)
Пошаговое руководство: создание мигающего светодиода на FPGA
Часть 1: Проектирование на VHDL или Verilog
В этом уроке вы научитесь писать код на VHDL и Verilog для управления светодиодом с настраиваемой частотой. Выбирайте язык, который лучше подходит вашему рабочему процессу.
При написании HDL-кода необходимо проверять, что проект ведёт себя как задумано. Ошибки неизбежны, поэтому симуляция обязательна. Урок разбит на два ключевых этапа:
- Проектирование HDL
- Симуляция HDL
Пропуск симуляции может привести к затратной отладке на железе. Считайте симуляцию обязательной контрольной точкой.
Требования к проекту
Написать HDL-код, который мигает светодиодом на частотах 100 Гц, 50 Гц, 10 Гц или 1 Гц со скважностью 50%. Два переключателя выбирают нужную частоту, а дополнительный переключатель LED_EN должен быть в логической единице для включения светодиода. FPGA тактируется от генератора 25 МГц.
Таблица истинности для выбора частоты:
| Enable | Switch 1 | Switch 2 | Частота светодиода |
|---|---|---|---|
| 0 | – | – | выключен |
| 1 | 0 | 0 | 100 Гц |
| 1 | 0 | 1 | 50 Гц |
| 1 | 1 | 0 | 10 Гц |
| 1 | 1 | 1 | 1 Гц |
Сводка сигналов:
| Имя сигнала | Направление | Описание |
|---|---|---|
| i_clock | Вход | Тактирующий сигнал 25 МГц |
| i_enable | Вход | Переключатель включения (логический 0 = светодиод выключен) |
| i_switch_1 | Вход | Переключатель выбора частоты 1 |
| i_switch_2 | Вход | Переключатель выбора частоты 2 |
| o_led_drive | Выход | Сигнал управления светодиодом |
Четыре параллельных процесса-счётчика отслеживают тактовый сигнал 25 МГц и генерируют переключения для каждой целевой частоты. Даже когда конкретная частота не выбрана, её счётчик продолжает работать — это фундаментальный принцип параллелизма в аппаратуре.
Переключатели формируют мультиплексор, который направляет выбранный сигнал переключения на выход светодиода. Мультиплексоры — это чисто комбинационная логика, они работают без тактового сигнала.
Ниже приведена блок-схема архитектуры:

Реализация на 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
- Использование списков чувствительности в процессах VHDL для надежного RTL-проектирования
- Интеграция библиотек IP Quartus Prime в VUnit: пошаговое руководство
- Бесплатный симулятор и редактор VHDL: руководство для студентов
- Проверьте свои знания VHDL — Интерактивный тест, часть 1
- Руководство новичка по ModelSim для симуляции FPGA и ASIC
- Создание тактированного процесса в VHDL: практическое руководство
- Освоение циклов While в VHDL: динамическое управление итерациями
- Использование операторов Wait For для задержки выполнения в VHDL
- Использование процедур в VHDL: упростите свой дизайн с повторно используемым кодом
- Освоение инстанцирования port map в VHDL: практическое руководство