Laboratoires VHDL
Rémi ADDE – M1
Sommaire :
2
TP1 – Exemple de circuit combinatoire : afficheur sept segments.
L’objectif de ce TP est de développer et tester un modèle synthétisable de décodeur : hexadécimal vers l’afficheur 7
segments.
Cet afficheur 7 segments est purement combinatoire et est commandé par une entrée hexadécimale.
• Hex : code d’entrée binaire, représentant un nombre hexadécimal, sur 4 bits (std_logic_vector(3downto0)).
• Aff : entrée de sélection de l’afficheur (1 parmi 4), sur 2 bits (std_logic_vector(1downto0)).
• Anode : signal de sortie pour activer l’anode de l’afficheur sélectionné, sur 4 bits, 1 bit par afficheur
(std_logic_vector(3downto0)).
• Segments : signal de sortie pour commander les 7 segments de l’afficheur sélectionné, sur 7 bits, 1 bit par
segment (std_logic_vector(6downto0)).
La table de vérité du décodeur est la suivante. On a donc pour chaque caractère l’état des 7 segments. L’état ‘0’
correspond à un état allumé et l’état ‘1’ correspond à un état éteint.
HEX g f e d c b a
0 1 0 0 0 0 0 0
1 1 1 1 1 0 0 1
2 0 1 0 0 1 0 0
3 0 1 1 0 0 0 0
4 0 0 1 1 0 0 1
5 0 0 1 0 0 1 0
6 0 0 0 0 0 1 0
7 1 1 1 1 0 0 0
8 0 0 0 0 0 0 0
9 0 0 1 0 0 0 0
A 0 0 0 1 0 0 0
3
b 0 0 0 0 0 1 1
C 1 0 0 0 1 1 0
d 0 1 0 0 0 0 1
E 0 0 0 0 1 1 0
F 0 0 0 1 1 1 0
Aff Anode
00 0
Pour décrire ces tables de vérité en VHDL on peut utiliser la description structurelle ou flot de données.
Le résultat sur carte est celui de la photo ci-dessous. J’ai affiché la lettre ‘d’ sur les 8 afficheurs car je n’ai pas changé
l’état de l’anode ni sélectionne d’afficheur.
Ensuite, grâce aux anodes, j’ai pu afficher les symboles (ici le chiffre 8) sur différents afficheurs :
4
TP2 – Exemple de circuit combinatoire : les multiplieurs.
L’objectif de ce TP est de réaliser plusieurs architectures de multiplicateur afin de pouvoir extraite leurs paramètres
de performances (rapidité, consommation d’éléments logiques programmables).
On va donc tout d’abord multiplier un vecteur par une constante. La multiplication par une constante non signée
sous forme d’une puissance de 2 se déroule de la manière suivante :
Le multiplieur est un vecteur A qui s’écrit en base binaire sous forme A = (a3,a2,a1,a0). Le multiplicande est une
constante de taille 4 bits qui est fixée à la valeur 4 pour le moment (4 = 0100). On a donc la multiplication qui est de
cette forme : A*4 = (a3,a2,a1,a0)*(0100) = (a3,a2,a1,a0)&(00). Une multiplication par une constance non signée
(positive) sous forme 2^N se traduit par un décalage vers la gauche du vecteur à multiplier par N positions. Exemple :
avec un multiplicande de 4, on a juste à déplacer le multiplieur vers la gauche de N positions, ici N = 2.
5
Maintenant, on va ajouter une entrée d’horloge qui va cadencer le système et une entrée asynchrone d’initialisation
qu’on appelle RST. Cette entrée initialise à tout moment le résultat de multiplication si elle est égale à 1.
Tests en simulation :
Le multiplieur et le multiplicande sont désormais deux vecteurs de type std_logic de taille 4 (avant on avait un
vecteur et une constante).
6
Dans cette partie du TP, nous allons pouvoir comparer différents multiplieurs, pour cela, nous allons créer un
testbench que nous allons réutiliser dans les 4 types de multiplicateurs.
1. Multiplication combinatoire : ici, nous allons simplement utiliser l’opérateur « * » pour développer le
multiplicateur.
Avec ce type de multiplication, il ne suffit que d’un coup d’horloge pour obtenir le résultat.
2. Multiplication par table de transcodage : la table de transcodage fait correspondre à chaque valeur des
vecteurs d’entrée une valeur de sortie. Si on utilise des vecteurs d’entrées de taille 4, on a 16 combinaisons
pour chaque vecteur et le nombre total des combinaisons est égale à 256. Ici, je n’ai fait que quelques cas.
Il ne faut une fois de plus qu’un seul coup d’horloge pour obtenir le résultat. Ceci est du au fait que tous les résultats
possibles sont enregistrés dans le code, il faut juste le piocher.
Pour bien comprendre cet algorithme, j’ai commencé par l’utiliser à la main :
J’ai décider de voir comment de coups d’horloge il faut pour avoir le résultat avec la pire situation, c’est-à-dire a =
1111 et b = 1111. Il faut donc 17 coups d’horloge ce qui est nettement plus long que pour les algorithmes
précédents.
4. Multiplication par addition et décalage : on multiplie des nombre n-bits par n-bits non signés grâce à des
additions et des décalages. On additionne uniquement si le LSB du multiplicande (b) vaut 1. Ensuite, on fait
un décalage à droite du multiplicande et à gauche du multiplieur.
8
J’ai, cette fois aussi utilisé l’algorithme à la main pour mieux en comprendre le fonctionnement. J’ai directement
commencé avec des nombres binaires :
9
Résultat de la simulation pour ce type de multiplicateur :
Dans le cas où on prend a = 1111 et b = 1111 il faut 6 coups d’horloges pour avoir le résultat. Ceci est toujours plus
élevée que les deux premiers multiplicateurs de vecteurs mais moins élevé que le précédent.
3. Implantation du multiplicateur :
Cette partie consiste à implémenter un des multiplieurs sur la carte. Pour ceci, nous avons dû utiliser des capteurs
maps.
10
Le code tu top est celui ci-dessous conformément au schéma :
L’architecture vient appeler différents composants créés. Tout d’abord un multiplieur (celui de notre choix, j’ai pris le
multiplieur combinatoire) puis deux afficheurs car le résultat est sur 8 bits. Enfin, le composant mux_tps permet
d’afficher sur plusieurs afficheurs en même temps (en réalité, les deux afficheurs s’allument à la suite mais assez
rapidement pour qu’on voit les deux en permanence).
Le résultat de la multiplication de 12*13 en décimal est 156. En hexadécimal, cette multiplication est égale à ‘9C’.
On a : a = 12 = 1100 et b = 13 = 1101.
11
Le résultat en hexadécimal est donc : . C’est bien ce que l’on cherchait, de plus, on l’obtient en un seul coup
d’horloge lorsque le calcul est initialisé.
C’est bel et bien ‘9C’, on peut voir que les 8 switch en partant de droite sont bien a et b. Le chiffre b correspond aux
4 premiers switchs et est égal à 13, les switchs 5 à 8 correspondent à a et a est bien égal à 12. De plus, la LED s’est
allumée car le calcul est fini.
12
Projet : Détecteur d’obstacle à base de capteur ultrason.
Nous allons réaliser un système embarqué pour le calcul de distance qui sépare une carte électronique d’un
obstacle. La carte est une Nexys4 et le capteur à ultrasons est un SRF05.
Le module global (top level) est implanté dans la carte FPGA et va permettre de calculer la distance qui sépare le
capteur de l’obstacle.
• Le bloc générateur_de_trigger : il permet d’envoyer des impulsions de 100us toutes les 250ms afin d’activer
le capteur.
• Le bloc calcul_de_distance : il convertit la durée entre l’envoi et la réception de l’écho en une distance en
cm.
• Le bloc conversion_bcd : il convertit cette distance en 3 variables : unités, dizaines et centaines afin de
pouvoir l’afficher.
Fonctionnement du capteur : le SFR05 a besoin d’un trigger de 100µs envoyé par le FPGA pour se déclencher. Cette
impulsion est symbolisée par « Trigger pulse input to SRF05 » sur le schéma ci-dessous :
13
Le capteur envoie alors une rafale de 8 cycles d’ultrason à 40kHz « Ultrasonic burst transmitted from SRF05 ». Cette
partie se situe dans le capteur et se fait lorsque le trigger est reçu (on ne la gère pas dans le projet). Quand les 8
cycles sont envoyés, la ligne d’écho « Echo pulse output » passe à l’état haut. Quand l’écho arrive, elle repasse à
l’état bas. Lorsque l’on calcule la longueur de l’état haut de la sortie, on peut donc obtenir la distance qui sépare le
capteur de l’obstacle (si aucun n’est détecté, la ligne d’écho repasse à l’état bas après 30ms).
Au bout de 30ms, on considère qu’il n’y a pas d’obstacle. Donc, puisque la vitesse du son est 340m/s, il suffit de
faire un produit en croix pour obtenir la distance maximale que l’on peut détecter.
Produit en croix :
30ms 1000ms
340 ∗ 30 340m
𝐷𝑚𝑎𝑥 = = 10,2𝑚
1000
On obtient une distance max de 10,2m mais on doit la diviser par deux car le temps correspond à l’aller retour donc
la distance maximale qui peut être mesurée est de 5,1m.
Question 2 : Quelle est la précision du système et sur combien de bits sera codées la distance ?
La précision du système est en centimètres, la distance sera codée sur 9 bits puisque la distance maximale est de
5,1m soit 510 cm.
Notre carte FPGA a une horloge de 100Mhz (période = 10ns). Il faut donc utiliser un compteur qui compte les fronts
montant de son horloge. La comparaison de la sortie du compteur aux constantes qui caractérisent le signal trigger
permet de générer correctement le signal trigger. Au départ, la valeur du compteur est initialisée à 0 : RST = ‘0’.
Question 1 : développer une architecture comportement du générateur du trigger. On peut utiliser un compteur
comme dans le TP3, le générateur du trigger (et le compteur) possède une entrée RST active au niveau bas et qui
initialise le compteur.
Le compteur compte les fronts montant de l’horloge. Puisque la période est de 10ns, il y a front montant toutes les
10ns. Le nombre de fronts montant pour envoyer une impulsion toutes les 250ms est : 𝑁𝑏𝐹𝑟𝑜𝑛𝑡𝑠𝑀𝑜𝑛𝑡𝑎𝑛𝑡𝑠 =
250
1𝑒 −5
= 25 000 000. Il faut donc 25 000 000 de fronts montant avant d’envoyer un trigger. Le trigger dure 100µs =
100 000
100 000ns. On a donc : 𝑁𝑏𝐹𝑟𝑜𝑛𝑡𝑠𝑀𝑜𝑛𝑡𝑎𝑛𝑡𝑠𝑇𝑟𝑖𝑔𝑔𝑒𝑟 = 10
= 10 000.
Donc, dans notre compteur, de 0 à 10 000 fronts d’horloges l’état du signal est à 1 et ensuite de 10 000 à 25 000 000
il est à 0.
14
Le résultat de la simulation est le suivant :
On voit bien que le trigger est à 1 pendant 100µs avant de revenir à 0 jusqu’à 250ms.
3. Calcul de distance :
Le bloc « calcul de distance » permet de convertir la durée de l’écho (sortie du capteur ultrason) en une distance en
cm. Ce bloc a donc besoin d’un compteur interne afin de calculer la durée.
15
• Reset : relié à la sortie du trigger_generator.
Le compteur se lance lorsque l’écho passe à l’état 1 (quand lance l’écho). Il s’arrête automatiquement au bout de
30ms si il n’est pas déjà repassé à 0. Donc on peut compter au maximum jusqu’à 30ms. Le nombre de fronts
30
montants va donc être : 𝑁𝑏𝐹𝑟𝑜𝑛𝑡𝑠𝑀𝑜𝑛𝑡𝑎𝑛𝑡𝑠 = −5 = 3 000 000. La valeur binaire de 3 000 000 est :
1𝑒
1011011100011011000000. Sa taille est donc de 22bits.
Question 3 :
Il me semble plus intéressant de coder les constantes en binaire car si on les codes en décimal, la carte FPGA aura
des conversions à faire alors qu’en binaire on peut directement passer à hexadécimal. Sinon on ferait Décimal vers
Binaire vers Hexadécimal.
16
4. Conversion BCD :
Le but d’une conversion BCD est de convertir un nombre binaire sur 10 bits à 3 nombres entiers correspondant aux
centaines, dizaines et unités. A chaque nouvelle valeur de distance, 3 signaux sont créés pour indiquer la valeur en
centimètre de la distanc
A l’initialisation : bcd vaut : 00 000 0000 000XX XXXX XXXX ou les X représentent la distance en entrée (ici choisie sur
10 bits).
Question 1 : développer l’architecture du bloc « Conversion BCD » et vérifier le bon fonctionnement par simulations.
J’ai, à plusieurs reprises essayé de faire fonctionner mon architecture BCD (dont j’ai oublié de prendre des screens)
sans succès. J’ai donc récupéré celle disponible sur l’ENT afin de pouvoir terminer le projet.
L’architecture de l’ensemble contient plusieurs composants. Tout d’abord, il contient un générateur de trigger, un
programme de conversion BCD ainsi qu’un programme de calcul de la distance. Puisque le résultat peut être sur 22
bits, l’architecture contient 3 afficheurs qu’il conviendra d’afficher simultanément avec le composant mux_tps
comme pour les multiplicateurs.
17
Question 3 : Test sur carte.
Pour finir ce projet, nous avons dû câbler le capteur sur la carte FPGA grâce au schéma suivant :
Pour un obstacle à 30 cm :
On peut voir que le programme est relativement possible, de plus, le résultat se mets à jour à la volée lorsque l’on
déplace le capteur, pas besoin d’initialiser la mesure à chaque fois.
18
Conclusion :
Pour les tableaux ci-dessous, les niveaux sont : Acquis, partiellement acquis, non acquis.
Tableau de connaissances :
Connaissance Niveau
La syntaxe du VHDL. Je pense avoir acquis une bonne connaissance de la syntaxe du VHDL.
Flot de conception FPGA : étapes de Je pense bien réussir les étapes de conception et les simulations par
conception avec simulation, synthèse, … saisie ou par testbench. Cependant, j’ai du mal à comprendre les
rapports de synthèse.
Les notions de traitement séquentiel et Je pense avoir acquis une bonne connaissance des notions de
concurrentiel. traitement séquentiel et concurrentiel.
Capacité Niveau
Modéliser un système à base de capteur
et d’unité de traitement.
Décrire en VHDL un circuit simple.
Faire une description structurelle :
réaliser un système à base de circuits
simples (notion de component et de
map).
Utiliser la chaine d’outils Xilinx pour
modéliser un circuit et/ou un système
de bout en bout.
Dimensionner un circuit/système en
déterminant le nombre
d’entrées/sorties et leurs types.
Dimensionner un circuit en choisissant
le nombre de bits requis pour les
opérations arithmétiques.
Pour conclure, c’est une matière que j’ai aussi beaucoup apprécié, j’espère pouvoir en apprendre plus et, lorsque j’aurais les
moyens, vais aussi m’équiper afin de réaliser de petits projets bien que la suite Xilinx est compliquée à utiliser car instable et non
fonctionnelle pour le moment sur Windows 11 donc était équipé d’origine mon ordinateur.
19