I. Introduction – Présentation de la cible Terasic DE0-Nano▲
Le kit Terasic DE0-Nano est la cible choisie pour ce tutoriel. Cette carte au format très compact avec un SoC FPGA Cyclone IV (22 320 éléments logiques) conviendra très bien pour de petites applications embarquées. Elle constitue aussi un bon choix pour débuter.
Caractéristiques principales |
|
|
|
|
|
|
|
|
|
Ce kit éducatif fait en effet partie de la série DE (Development and Education) du programme FPGA Academic d’Intel. Il reste que le but de ce tutoriel est de se familiariser aux principes du développement FPGA avec la suite Quartus Prime d’Intel (la version Lite, gratuite), et que cette carte est un support matériel parmi d’autres. Le lecteur devra adapter les principes vus ici à la cible dont il dispose.
Si vous êtes un adepte de la plateforme Arduino, la carte Arduino MKR Vidor 4000 avec son SoC FPGA Cyclone 10 LP d’Intel est aussi une bonne candidate pour débuter sur FPGA.
Voir les tutoriels :
Mais comme tout le monde avant de faire parler la poudre, il va falloir se familiariser avec la bête, de façon modeste, par exemple en pilotant un afficheur 7-segments. Le but est d'incrémenter un compteur qui évolue entre 0 et 9 à chaque appui sur le bouton-poussoir intégré en surface de la carte :
II. Démarche générale de conception▲
L’algorigramme ci-dessous récapitule la démarche de conception typique pour un projet sur FPGA :
- Description du projet : le circuit prévu est décrit au moyen de schémas ou en passant par un langage de description de matériel tel Verilog ou VHDL.
- Synthèse : le projet décrit est analysé pour être synthétisé en un circuit à partir des blocs logiques disponibles depuis le SoC FPGA.
- Simulation fonctionnelle : le circuit synthétisé est testé afin de vérifier que son principe de fonctionnement est correct, mais les contraintes physiques liées aux délais de propagation et autres problèmes de métastabilité ne sont pas pris en compte.
- Placement et routage (fitting) : à partir de la liste des blocs logiques nécessaires (netlist), l’outil choisit l’emplacement des blocs logiques au sein du SoC FPGA et détermine la meilleure route à suivre pour établir les connexions entre les blocs du circuit.
- Analyse temporelle : les délais de propagation dans les circuits sont analysés afin d’amener des indications sur la performance du circuit.
- Simulation temporelle : le circuit est simulé en prenant en compte cette fois les contraintes temporelles.
- Programmation et configuration : le circuit est physiquement implémenté dans le SoC FPGA.
III. Configuration initiale du projet▲
Je suppose que vous avez installé Quartus Prime (édition lite) ainsi que les pilotes nécessaires sur votre PC en suivant les indications du guide Getting Started with DE0-Nano.
Parmi les fichiers fournis sur le CD d’accompagnement de la carte (à télécharger), l’utilitaire DE0_Nano_SystemBuilder.exe permet de préparer la trame de tout projet Quartus Prime sur cette cible, et ce, en quelques clics :
Après avoir nommé le projet puis sélectionné les périphériques de la carte dont vous aurez besoin : l’horloge, le bandeau de 8 LED, les deux boutons-poussoirs, et un port GPIO 2x20 broches (GPIO-1 Header) pour le branchement de l’afficheur 7-segments, on génère automatiquement le dossier du projet avec les fichiers essentiels pour démarrer.
- Quartus project file (.qpf) : le fichier projet à ouvrir avec Quartus.
- Quartus setting file (.qsf) : un fichier de configuration avec les paramètres et l’affectation des broches pour le projet.
- Top-level design file (.v) : gabarit de fichier du module principal (de plus haut niveau hiérarchique) en langage Verilog.
- Synopsis design constraints file (.sdc)Â : fichier des contraintes temporelles.
- Pin assignment document (.htm) : tableaux d’affectation et localisation des broches du projet.
Vous pouvez maintenant ouvrir le fichier afficheur7seg.qpf avec Quartus Prime :
IV. Un premier projet : un compteur sur afficheur à LED 7-segments▲
Comme dit en introduction, ce compteur (de 0 à 9) dont la valeur sera restituée sur un afficheur à LED 7-segments (cathode commune) devra s’incrémenter à chaque appui sur un bouton-poussoir.
IV-A. Bouton-poussoir : détection de front▲
Le schéma de câblage des boutons KEY0 et KEY1 en surface de la carte ci-dessous montre des résistances de tirage. Le niveau logique est donc à l’état haut lorsque le bouton est relâché et passe au niveau bas sur appui du bouton.
De plus, un circuit tampon à entrée trigger de Schmitt (74AUC17) en amont du SoC FPGA élimine le risque de rebonds sur appui ou relâchement du bouton :
Pour incrémenter notre compteur, il faut détecter l’appui sur le bouton, c.-à -d. l’instant où le signal bascule de l’état haut à l’état bas, autrement dit, lors d’un front descendant du signal (falling edge).
Structurellement, la détection synchrone de front peut être obtenue à l’aide de bascules D (ou D flip-flop) comme sur le schéma ci-dessous :
Le signal de sortie est à l’état haut si l’entrée est à 0 ET si l’entrée retardée du cycle d’horloge précédent est à 1.
Deux méthodes s’offrent à vous pour la description de cette détection de front :
- description graphique par schéma-blocs : il s’agit de réaliser structurellement ce circuit en déposant des primitives dans la fenêtre de travail et en réalisant les connexions à la souris ;
- programmation textuelle avec un langage de description de matériel de haut niveau comme Verilog ou VHDL : la description par ces langages peut être structurelle, mais aussi comportementale. Dans ce dernier cas, vous décrivez le comportement du circuit et vous laissez le système « synthétiser » le circuit répondant au comportement décrit.
Vous allez découvrir les deux méthodes.
IV-A-1. Description graphique par schéma-blocs▲
Pour créer un nouveau schéma-bloc, rendez-vous dans le menu File → New :
Sélectionner Block Diagram/Schematic File, le fichier sera sauvegardé sous le nom edgeDetector.bdf.
Dans la fenêtre de travail, après un clic droit : insert → Symbol…
Puis rechercher dans les primitives de la bibliothèque le composant dff (D Flip Flop, ou bascule D). Il faudra coller deux instances de ce composant dans la fenêtre de travail.
La première bascule permettra la synchronisation avec l’horloge, la seconde la détection du front sur appui du bouton.
De la même façon, trouver les composants logiques and2 (fonction ET), not (fonction NON), puis le composant Vcc (dans Others) :
Les connexions se font entièrement à la souris :
Insérer et raccorder des entrées Input (ou depuis l’icône pinTool de la barre d’outils) :
Enfin, insérer et raccorder une sortie Output :
Le schéma final avec les blocs renommés devrait ressembler à la copie d’écran ci-dessous :
IV-A-2. Simulation fonctionnelle▲
Afin de vérifier la correction de ce circuit logique, un éditeur de signaux est fourni avec Quartus Prime (Waveform Editor). L’idée générale est de construire sous forme de chronogrammes les signaux d’entrée (test vectors) et de simuler le comportement du circuit (Functional simulation) pour finalement produire les chronogrammes sur les sorties du système. Cette simulation fonctionnelle ne prend pas en compte les contraintes physiques au niveau temporel comme les retards dus aux délais de propagation, même l’horloge ici est simulée.
Auparavant, il faut penser à définir le fichier edgeDetector.bdf au niveau le plus élevé dans la hiérarchie du projet (même si notre schéma-bloc est la seule description du projet pour l’instant, d’autres vont suivre dans ce tutoriel). Pour cela, faire un clic droit sur le fichier dans le navigateur du projet, puis sélectionner Set as Top-Level Entity dans le menu contextuel.
On lance ensuite une première analyse du projet par le menu Processing → Start → Start Analysis & Synthesis (ou en cliquant sur le bouton correspondant). Normalement, l’analyse se termine victorieusement (à quelques avertissements près que l’on va ignorer) avec le message :
- Info: Quartus Prime Analysis & Synthesis was successful. 0 errors, 4 warnings
Pour créer le fichier de simulation, aller dans le menu File → New… → Verification/Debugging Files → University Program VWF, puis commencer par sauvegarder le fichier dans la fenêtre Simulation Waveform Editor, menu File → Save As…, afficheur7seg_Waveform.vwf.
Préciser la durée de simulation (menu Edit → Set End Time…), 20 µs par exemple.
Préciser ensuite le pas de la grille (menu Edit → Grid size…), 1 µs par exemple.
Pour insérer les entrées-sorties, aller dans le menu Insert → Insert Node or Bus…, puis cliquer sur le bouton Node Finder… dans la fenêtre qui s’ouvre.
Dans la fenêtre Node Finder, valider le filtre Pins : all et cliquer sur le bouton List.
Sélectionner tous les signaux clk, in et out, les faire passer dans la fenêtre Selected Nodes de droite, et valider le tout.
Il reste à mettre en forme l’allure des chronogrammes, à commencer par le signal d’horloge clk.
Sélectionner le signal clk sur toute la durée de la simulation, puis cliquer sur le bouton OverWrite Clock. Saisir 1 µs pour la période. En fait, pour cette simulation fonctionnelle il n’est pas obligatoire d’avoir la fréquence du signal qui correspond avec la fréquence de l’horloge en fonctionnement réel.
Sélectionner maintenant le signal in et le forcer à 1 sur toute la durée de la simulation (bouton Forcing High) pour simuler le bouton-poussoir relâché. Il reste à forcer quelques intervalles de temps du signal in à l’état bas pour simuler des appuis sur le bouton-poussoir (bouton Forcing Low). Les appuis sur le bouton pouvant survenir à tout instant, penser à supprimer l’alignement systématique de la sélection avec la grille (menu Edit → Snap to grid à décocher).
Dans la fenêtre qui s’ouvre depuis le menu Simulation → Simulation Settings, onglet Functional Simulation Settings, cliquer sur le bouton Restore Defaults puis sur Save.
En lançant la simulation avec le bouton Run Functional Simulation, vous risquez d’avoir le message d’erreur suivant :
- # ** Error (suppressible): (vsim-12110) The -novopt option has no effect on this product. -novopt option is now deprecated and will be removed in future releases.
En supprimant cette option -novopt dans le script de configuration, et en relançant la simulation, le problème est réglé :
Le chronogramme du signal de sortie out est bien conforme, avec une impulsion sur chaque front descendant du signal d’entrée in.
IV-A-3. Description en langage Verilog▲
Avec quelques rudiments de Verilog, la description est rapide. Le code Verilog pour décrire le comportement de notre détecteur de front descendant sera le suivant :
module
edgeDetector2
(
input
clk, // horloge
input
in, // signal d'entrée
output
out); // sortie = impulsion si front descendant détecté
reg
reg1, reg2; // reg1 et reg2, signaux internes
always
@(
posedge
clk) begin
// sur front montant de l'horloge
reg1 <=
in;
reg2 <=
reg1; // reg2, signal d'entrée retardé d’un cycle
end
// front descendant détecté si :
// entrée = 0
// ET
// entrée = 1 au cycle précédent
assign
out =
~
reg1 &
reg2;
endmodule
On passe par deux « variables » déclarées avec le type reg (pour registers, un objet qui a de la mémoire). reg1 et reg2 ont le même rôle qu’une variable au sens traditionnel du terme en langage C, C++, etc. La différence est qu’on ne peut pas leur affecter des valeurs en continu, mais seulement à des moments précis (synchronisme avec l’horloge).
Vous pouvez provisoirement retirer la description graphique précédente (clic droit sur le fichier .bdf, puis Remove File from Project) et ajouter un fichier de description en Verilog (New → Design Files → Verilog HDL File) que vous mettrez au sommet de la hiérarchie du projet (clic droit sur le fichier .v, puis Set as Top-Level Entity).
Quoi qu’il en soit, la simulation fonctionnelle sur la description graphique par schéma-bloc ou sur la description Verilog donne le même résultat en sortie.
Pour aller un peu plus loin, on peut regarder ce qui a été synthétisé dans les deux cas (menu Tools → Netlist Viewers → Technology Map Viewer (Post-Fitting)) :
Les deux synthèses sont identiques. Dans les deux cas, vous retrouvez les deux bascules D. La fonction logique AND et son entrée inversée NOT ont été synthétisées dans une LUT (sortie d’équation COMBOUT = !DATAA & DATAB).
IV-B. Démarche de conception modulaire▲
Vous venez de voir que les descriptions peuvent se faire de façons différentes : graphiquement par schéma-blocs pour une description structurelle, textuellement par description comportementale dans un langage de haut niveau comme Verilog. Il y a encore d’autres façons que nous n’allons pas évoquer dans ce tutoriel.
Quel que soit le mode de description, il faut architecturer votre projet de façon modulaire. Pour afficher un compteur sur un 7-segments, il faut :
- détecter les fronts sur appui du bouton-poussoir, l’objet du chapitre précédent ;
- sur chaque front détecté, augmenter la valeur d’un compteur (qui évolue entre 0 et 9, puis qui reprend à zéro) ;
- piloter les segments de l’afficheur en fonction de la valeur du compteur.
La description au sommet de la hiérarchie qui va relier ces descriptions secondaires peut être un fichier Verilog qui instancie des éléments de modules secondaires ou un fichier de description graphique qui relie des blocs.
C’est cette seconde option que nous choisirons pour la suite de ce tutoriel.
Quelle que soit la description choisie pour la détection de front descendant, créer son bloc graphique correspondant (clic droit sur le fichier, puis Create Symbol Files for Current File) :
Si tout se passe bien, vous aurez le message :
- Info: Quartus Prime Create Symbol File was successful. 0 errors, 0 warnings
Insérer un nouveau schéma-bloc au projet (File → New → Design Files → Block Diagram/Schematic File), sauvegardé sous le nom afficheur7seg.bdf. Mettre ce nouveau fichier de description au sommet de la hiérarchie dans le projet (clic droit sur le fichier, puis Set as Top-Level Entity).
Dans la fenêtre graphique de travail, un clic droit puis Insert → Symbol… permet maintenant d’insérer le symbole du bloc de description pour la détection de front :
La description du projet peut se poursuivre…
IV-C. Description du compteur▲
Le compteur peut être décrit en Verilog, mais ce genre de primitives existe déjà dans un catalogue (menu Tools → IP Catalog) :
Les IP cores (ou noyaux IP, IP pour Intellectual Property) peuvent être vus ici comme des éléments de bibliothèques. Chaque IP est donc un composant logique réutilisable avec une interface et un comportement vérifié par son créateur et qui peut être intégré dans un projet plus large.
Un clic droit sur l’IP LPM-COUNTER et la sélection de l’élément Add Component dans le menu contextuel ouvre un assistant de configuration du compteur en cinq étapes résumées ci-dessous.
On déclare un compteur avec une sortie sur 4 bits :
Le compteur est modulo 10. Après avoir atteint 9, il repart à zéro. Il faut activer l’entrée Count Enable :
Pour la suite, il faut aussi générer un fichier .bsf pour la représentation graphique du compteur :
Après sauvegarde, le bloc symbolique du compteur peut être inséré dans la fenêtre de travail du projet (clic droit dans la fenêtre, puis Insert → Symbol), à côté du bloc de détection de front descendant :
Il reste à insérer et renommer les entrées et les sorties, puis à faire les connexions :
La sortie out du détecteur de front est reliée à l’entrée cnt_en (enable) du compteur pour autoriser son incrémentation à chaque appui sur le bouton.
La sortie q[3..0] du compteur est un bus de largeur 4 bits, on le précise dans le nom de la sortie counter_out[3..0] avant de connecter le bus (avec l’icône de l’outil Orthogonal Bus Tool).
Vous pouvez comme précédemment faire une simulation fonctionnelle pour valider le principe :
On voit que le compteur s’incrémente bien à chaque appui du bouton (front descendant du signal button). Le compteur (modulo 10) repart bien à zéro après avoir atteint 9.
IV-D. Premier test : affectation des broches et programmation du FPGA▲
Il est temps de faire un essai en programmant la cible. Si vous ouvrez le fichier du projet avec l’extension .htm (un fichier HTML qui s’ouvre dans un navigateur), vous verrez que l’affectation des broches avec leur localisation est déjà faite depuis l’étape de configuration initialeConfiguration initiale du projet :
Si vous n’êtes pas passé par cette étape initiale, vous pouvez encore effectuer ces affectations dans Quartus Prime (menu Assignments → Pin planner). Référez-vous au tutoriel How to Program Your First FPGA Device (Step 4: Choose Pin Assignments) pour voir la démarche.
Il suffit de nommer les entrées-sorties par leur nom dans Quartus Prime pour les diriger vers l’horloge, les boutons-poussoirs ou les LED de la carte, soit : CLOCK_50 pour l’horloge 50 MHz, KEY[1] pour le bouton-poussoir et LED[7..4] pour les quatre premières LED du bandeau :
Après avoir lancé l’analyse et la synthèse (bouton Start Analysis & Synthesis), les étiquettes avec les noms des broches du SoC FPGA s’affichent dans le schéma :
Vous devez maintenant lancer la compilation du projet par le menu Processing → Start Compilation. La phase de compilation comprend l’analyse, la synthèse, le placement et routage des composants jusqu’à la génération du fichier bitstream :
La compilation complète peut prendre plusieurs minutes.
Finalement, pour programmer la puce FPGA, il faut raccorder la carte au PC via la liaison USB Blaster et se rendre dans le menu Tools → Programmer.
Une fois la liaison configurée, et le fichier compilé avec l’extension .sof (SRAM Object File) sélectionné, il reste à cliquer sur le bouton Start.
Si les LED non utilisées sont légèrement allumées, il faut reconfigurer les broches correspondantes. Pour cela, suivre le menu Assignments → Device et cliquer sur le bouton Device and Pin Options… dans la fenêtre qui s’ouvre. Mettre le champ Reserve all unused pins avec l’option As input tri-stated.
Relancer la compilation et programmer à nouveau la puce.
Les quatre LED devraient s’allumer au rythme des appuis sur le bouton-poussoir selon la séquence : 0000, 0001, 0010, 0011, 0100, …, 1000, 1001, 0000, etc.
Les quatre LED du bandeau en surface de la carte n’étaient qu’une étape intermédiaire, mais le but est d’afficher la valeur du compteur sur un afficheur 7-segments…
IV-E. Afficheur 7-segments : description du décodeur DCB vers 7-segments▲
Le compteur sur son bus de sortie délivre une valeur décimale entre 0 et 9 exprimée dans le système décimal codé binaire (ou DCB). Pour les valeurs décimales entre 0 et 9, le codage DCB donne : 0000, 0001, 0010, 0011, 0100, 0101, 0110, 0111, 1000 et 1001.
Un décodeur DCB pour un afficheur 7-segments devra produire la séquence binaire sur 7 bits (voire 8 bits avec le point décimal) correspondant aux segments que l’on veut allumer ou éteindre. Par exemple, pour afficher le chiffre 7 (0111 en DCB) avec le point décimal (dot point dp) éteint, il faut allumer les segments a, b et c, soit en sortie du décodeur : 1110 0000.
Pour toutes les valeurs décimales de 0 à 9, on obtient la table de vérité ci-dessous :
Ce décodeur est donc un pur circuit de logique combinatoire, et il constitue un bel exercice de manipulation d’équations logiques et de leur simplification par les tableaux de Karnaugh.
Le schéma logique d’un tel décodeur, même avec les équations logiques simplifiées, n’est pas des plus sympathiques :
Mais si l’on fait confiance au synthétiseur de Quartus Prime, la description en langage Verilog nous facilite la tâche :
module
bcd_decoder_7seg
(
in_bcd, out_7seg);
input
[3
:0
] in_bcd;
output
reg
[7
:0
] out_7seg;
always
@(
in_bcd) begin
case
(
in_bcd)
// abcdefg(dp)
0
: out_7seg =
8'b11111100
;
1
: out_7seg =
8'b01100000
;
2
: out_7seg =
8'b11011010
;
3
: out_7seg =
8'b11110010
;
4
: out_7seg =
8'b01100110
;
5
: out_7seg =
8'b10110110
;
6
: out_7seg =
8'b10111110
;
7
: out_7seg =
8'b11100000
;
8
: out_7seg =
8'b11111110
;
9
: out_7seg =
8'b11110110
;
default
: out_7seg =
8'b00000000
;
endcase
end
endmodule
Une fois le fichier bcd_decoder_7seg.v ajouté au projet, il faut penser à générer son symbole (clic droit sur le fichier dans le navigateur, puis Create Symbol Files For Current File). On peut alors l’insérer et le connecter dans le schéma global du projet :
Connecter les entrées-sorties grâce au bouton Orthogonal Bus Tool. La sortie sera renommée en GPIO[33..26] pour la diriger vers le port GPIO de la carte.
Si la compilation est un succès (Start Compilation), il reste à connecter l’afficheur au port GPIO avant de programmer le SoC FPGA.
L’afficheur à LED rouges choisi (référence : LTS-5474AP) est à cathode commune :
De cette façon, c’est bien un état haut (sortie TTL +3,3 V) de l’anode du segment reliée au port GPIO qui va allumer le segment.
Pour le câblage de l’afficheur 7-segments, la documentation dans le manuel utilisateur (User Manual) vous donnera l’emplacement des broches GPIO[26] à GPIO[33].
Bien entendu, vous n’oublierez pas les huit indispensables résistances de limitation de courant à connecter en série avec chaque segment de l’afficheur (330 Ω conviendra).
GPIO[33] → segment a |
GPIO[32] → segment b |
GPIO[31] → segment c |
GPIO[30] → segment d |
GPIO[29] → segment e |
GPIO[28] → segment f |
GPIO[27] → segment g |
GPIO[26] → point décimal dp |
GND → commun |
IV-F. Le projet complet en Verilog▲
Enfin, pour ceux qui préfèrent tout décrire en Verilog, voici le code complet du projet découpé en trois modules. Dans cette version, on a en plus implémenté une remise à zéro synchrone du compteur sur appui du deuxième bouton-poussoir KEY[0] :
module
afficheur7seg
(
input
CLOCK_50, // horloge 50 MHz
input
[1
:0
] KEY, // bouton-poussoir x 2
output
[35
:0
] GPIO, // port GPIO pour afficheur 7-segments
output
[7
:0
] LED // bandeau de LED x 8
);
reg
[3
:0
] counter; // compteur modulo 10
wire
button_pressed; // = 1 si appui sur bouton-poussoir détecté
edgeDetector edgeDetector_inst
(
.clk (
CLOCK_50),
.in (
KEY[1
]),
.out (
button_pressed)
);
bcd_decoder_7seg bcd_decoder_7seg_inst
(
.in_bcd (
counter),
.out_7seg (
GPIO[33
:26
]) // vers l'afficheur 7_segments
);
assign
LED [7
:4
] =
counter; // compteur binaire dirigé vers LED
always
@ (
posedge
CLOCK_50) begin
// sur front montant de l’horloge
if
(!
KEY[0
])
counter <=
4'b0000
; // reset synchrone
else
begin
if
(
button_pressed) begin
if
(
counter ==
4'b1001
) // modulo 10
counter <=
4'b0000
;
else
counter <=
counter +
4'b0001
;
end
end
end
endmodule
module
edgeDetector
(
input
clk, // horloge
input
in, // signal d'entrée
output
out // sortie = impulsion si front descendant détecté
);
reg
reg1, reg2; // reg1 et reg2, signaux internes
always
@(
posedge
clk) begin
// sur front montant de l'horloge
reg1 <=
in;
reg2 <=
reg1; // reg2, signal retardé de l'entrée au cycle précédent
end
// front descendant détecté si :
// entrée = 0
// ET
// entrée = 1 au cycle précédent retardé
assign
out =
~
reg1 &
reg2;
endmodule
module
bcd_decoder_7seg
(
input
[3
:0
] in_bcd,
output
reg
[7
:0
] out_7seg
);
always
@(
in_bcd) begin
case
(
in_bcd)
// segments abcdefg(dp)
0
: out_7seg =
8'b11111100
;
1
: out_7seg =
8'b01100000
;
2
: out_7seg =
8'b11011010
;
3
: out_7seg =
8'b11110010
;
4
: out_7seg =
8'b01100110
;
5
: out_7seg =
8'b10110110
;
6
: out_7seg =
8'b10111110
;
7
: out_7seg =
8'b11100000
;
8
: out_7seg =
8'b11111110
;
9
: out_7seg =
8'b11110110
;
default
: out_7seg =
8'b00000000
;
endcase
end
endmodule
La simulation ci-dessous montre bien la remise à zéro du compteur sur appui du bouton KEY[0] :
V. Conclusion▲
Cette activité n’est évidemment qu’un point de départ dans vos apprentissages sur FPGA, une étape supplémentaire après un « Hello world ».
Pour la compléter, vous pourriez :
- rajouter un deuxième afficheur pour faire un compteur entre 0 et 99 ;
- se servir d’un bouton pour incrémenter le compteur, et de l’autre bouton pour le décrémenter ;
- faire clignoter quelques secondes le point décimal de l’afficheur à chaque appui sur le bouton ;
- etc.
Après avoir suivi ce tutoriel, j’espère que vous aurez l’autonomie suffisante pour monter progressivement en compétence dans la programmation des FPGA.
Un grand merci à Vincent PETIT et Claude Leloup pour leur travail de relecture.