Comment construire votre premier clignotant LED FPGA : Tutoriel pas à pas
Guide pas à pas : Construction de votre premier clignotant LED FPGA
Partie 1 : Conception VHDL ou Verilog
Dans ce tutoriel, vous apprendrez à écrire du code VHDL et Verilog qui fait clignoter une LED à une fréquence définie par l'utilisateur. Choisissez le langage qui convient le mieux à votre flux de travail.
Lors de l'écriture en HDL, vous devez vérifier que le design se comporte comme prévu. Les erreurs sont inévitables, donc la simulation est indispensable. Ce tutoriel est divisé en deux phases critiques :
- Conception HDL
- Simulation HDL
Sauter la simulation peut mener à un débogage matériel coûteux. Traitez la simulation comme un point de contrôle obligatoire.
Exigences du projet
Écrivez du HDL qui fait clignoter une LED à 100 Hz, 50 Hz, 10 Hz ou 1 Hz avec un cycle de service de 50 %. Deux interrupteurs sélectionnent la fréquence désirée, et un interrupteur LED_EN supplémentaire doit être à l'état haut pour activer la LED. Le FPGA fonctionne sur un oscillateur de 25 MHz.
Table de vérité pour le sélecteur de fréquence :
| Activer | Interrupteur 1 | Interrupteur 2 | Fréquence LED |
|---|---|---|---|
| 0 | – | – | désactivé |
| 1 | 0 | 0 | 100 Hz |
| 1 | 0 | 1 | 50 Hz |
| 1 | 1 | 0 | 10 Hz |
| 1 | 1 | 1 | 1 Hz |
Résumé des signaux :
| Nom du signal | Direction | Description |
|---|---|---|
| i_clock | Entrée | Horloge 25 MHz |
| i_enable | Entrée | Interrupteur d'activation (logique 0 = LED éteinte) |
| i_switch_1 | Entrée | Interrupteur sélecteur de fréquence 1 |
| i_switch_2 | Entrée | Interrupteur sélecteur de fréquence 2 |
| o_led_drive | Sortie | Signal de commande LED |
Quatre processus de compteur concurrents surveillent l'horloge 25 MHz et génèrent des basculements pour chaque fréquence cible. Même lorsqu'une fréquence particulière n'est pas sélectionnée, son compteur continue de s'exécuter – un principe fondamental de la concurrence matérielle.
Les interrupteurs forment un multiplexeur qui route le basculement choisi vers la sortie LED. Les multiplexeurs sont une logique purement combinatoire, ils fonctionnent donc sans horloge.
Ci-dessous, un diagramme de blocs illustrant l'architecture :

Implémentation 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
-- Constantes pour créer les fréquences nécessaires :
-- Formule : (25 MHz / 100 Hz * 50% duty cycle)
-- Pour 100 Hz : 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;
-- Ces signaux seront les compteurs :
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;
-- Ces signaux basculeront aux fréquences nécessaires :
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';
-- Fil de sélection d'un bit.
signal w_LED_SELECT : std_logic;
begin
-- Tous les processus basculent un signal spécifique à une fréquence différente.
-- Ils s'exécutent tous continuellement même si les interrupteurs
-- ne sélectionnent pas leur sortie particulière.
p_100_HZ : process (i_clock) is
begin
if rising_edge(i_clock) then
if r_CNT_100HZ = c_CNT_100HZ-1 then
-- -1, car le compteur commence à 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
-- -1, car le compteur commence à 0
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
-- -1, car le compteur commence à 0
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
-- -1, car le compteur commence à 0
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;
-- Créer un multiplexeur basé sur les entrées d'interrupteurs
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;
-- N'autoriser o_led_drive à piloter que lorsque i_enable est haut (porte ET).
o_led_drive <= w_LED_SELECT and i_enable;
end rtl;
Implémentation 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;
// Constantes (paramètres) pour créer les fréquences nécessaires :
// Horloge d'entrée 25 kHz, choisie arbitrairement.
// Formule : (25 kHz / 100 Hz * 50% duty cycle)
// Pour 100 Hz : 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;
// Ces signaux seront les compteurs :
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;
// Ces signaux basculeront aux fréquences nécessaires :
reg r_TOGGLE_100HZ = 1'b0;
reg r_TOGGLE_50HZ = 1'b0;
reg r_TOGGLE_10HZ = 1'b0;
reg r_TOGGLE_1HZ = 1'b0;
// Sélection d'un bit
reg r_LED_SELECT;
wire w_LED_SELECT;
begin
// Tous les blocs always basculent un signal spécifique à une fréquence différente.
// Ils s'exécutent tous continuellement même si les interrupteurs
-- ne sélectionnent pas leur sortie particulière.
always @ (posedge i_clock) begin
if (r_CNT_100HZ == c_CNT_100HZ-1) // -1, car le compteur commence à 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) // -1, car le compteur commence à 0
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) // -1, car le compteur commence à 0
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) // -1, car le compteur commence à 0
begin
r_TOGGLE_1HZ <= !r_TOGGLE_1HZ;
r_CNT_1HZ <= 0;
end else
r_CNT_1HZ <= r_CNT_1HZ + 1;
end
// Créer un multiplexeur basé sur les entrées d'interrupteurs
always @ (*) begin
case ({i_switch_1, i_switch_2}) // Opérateur de concaténation
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;
// Méthode alternative pour concevoir le multiplexeur (identique à ci-dessus) :
// Plus compacte, mais plus difficile à lire, surtout pour les débutants en Verilog
// 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;
end
endmodule
Étape suivante : Simulez ce design en VHDL ou Verilog pour confirmer le comportement correct avant le déploiement.
VHDL
- Implémentation d'une liste chainée dynamique en VHDL avec types protégés et pointeurs d'accès
- Implémenter un contrôleur PWM en VHDL : Conception, simulation et démo FPGA
- Maîtriser l'instanciation par port map en VHDL : Guide pratique
- Implémenter une FIFO AXI en RAM bloc avec handshake Ready/Valid
- Créer un effet LED respirant avec une onde sinusoïdale depuis la RAM bloc
- Construction d'un processus déclenché par horloge en VHDL : Guide pratique
- Quiz VHDL Avancé - Partie 3 : Testez votre expertise HDL
- Utilisation des procédures en VHDL : Simplifiez votre design avec du code réutilisable
- Comment construire un testbench auto-vérifiant en VHDL - Guide pratique