Vous êtes sur la page 1sur 26

Travaux Pratiques de VHDL

avec Quartus
MASTERS (ETC, TSE, MECA) /M1

Responsable
Naima Amar Touhami

Année Universitaire : 2019/2020


INTRODUCTION

VHDL est un langage de description de matériel, utilisé pour décrire un système numérique
matériel, comme, par exemple, une bascule D ou un microprocesseur. Il peut modéliser un
système par n'importe quelle vue, structurelle ou comportementale, à tous les niveaux de
description.

De plus il peut servir non seulement à simuler un système mais aussi à le synthétiser, c'est-à-dire
être transformé par des logiciels adaptés (synthétiseurs) en une série de portes logiques prêtes à
être gravées sur du silicium.

Le but des ces séances de travaux pratiques est d’intégrer les différentes notions vues en cours
concernant l’apprentissage du langage VHDL et de donner une initiation simple au langage
VHDL qui permettra une meilleure compréhension des différents aspects du langage. Afin de
prendre en main le langage et les différents outils logiciels que nous devrons utiliser, une
présentation de chacun vous est présentée. Ils sont tous intégrés dans l’environnement Quartus
qui propose à la fois éditeur de texte, synthétiseur logique, simulateur, implémentation et
programmation des composants logiques (FPGA).

Enfin, nous conseillons de bien lire la partie théorique avant de passer aux exemples proposés
dans le but d’éviter tout problème du à une mauvaise connaissance des règles d’écriture, des types
et tout ce qui en à avoir avec l’organisation du langage.

Chaque binôme rendra un rapport de type industriel sur le mini projet. La notation tiendra
compte des différents fonctionnements réalisés lors des travaux pratiques et du rapport sur le
mini-projet.
Ce rapport devra contenir :
- Une vue d’ensemble du système.
- Les réponses au cahier des charges.
- Les simulations.
- Les codes sources en annexe.
1. ORGANISATION DU LANGAGE
1.1 Principes d’utilisation du langage
Pour simuler ou effectuer la synthèse logique d'un modèle VHDL, il faut d'abord le compiler (on
dit analyser pour le VHDL). Les résultats d'analyse sont stockés dans une bibliothèque qu’il
faut préciser au compilateur. Dans un programme VHDL, on peut ainsi faire référence à un objet
déjà analysé en précisant dans quelle bibliothèque il se trouve.

1.2 Structure du langage


L'analyse d'un modèle VHDL peut s'effectuer sur des parties du code. L'entité et l'architecture sont
des unités de compilation obligatoires pour décrire un modèle et les autres unités sont optionnelles.
Une unité de compilation doit être écrite dans un même fichier.

1.3 Conventions lexicales


Les conventions lexicales de VHDL sont les mêmes que celles utilisées en C.
1.3.1 Casse
VHDL est insensible à la casse. Un mot en majuscule est identique à un mot en minuscule.
1.3.2 Commentaires
Ils commencent par deux tirets (--) et se terminent en fin de ligne.
1.3.3 Identificateurs
Ce sont les noms de variables, de signaux, de fonctions, ...
· Ils ne peuvent contenir que des lettres, des chiffres et le "underscore" _.
· Ils doivent commencer par une lettre.
· Ils ne peuvent pas contenir d'espace.
· Les mots-clefs du langage ne peuvent pas être utilisés comme identificateurs.
1.3.4 Expressions
Elles se terminent par un point virgule ;
1.3.5 Littéraux
Ce sont des valeurs explicites :
· 67 est un littéral pour le type entier
· '0' est un littéral pour un bit
· "001" O"562" X"FF1" sont des littéraux pour les vecteurs de bits
· "chaine" est un littéral de type chaine de caractères
· null est un littéral pointeur

1.4 Bibliothèques
Les bibliothèques permettent à plusieurs concepteurs de travailler ensemble sur le même projet et
rendent le langage indépendant du système d’exploitation de la machine. Pour accéder à la
bibliothèque BIB il est nécessaire de la déclarer :
library BIB;
La bibliothèque par défaut est WORK qui est aussi le nom symbolique de la bibliothèque dans
laquelle sont stockés les résultats. La bibliothèque STD est une bibliothèque standard fournie avec le
langage, elle contient les définitions des types et des fonctions de base (integer, BIT,
BOOLEAN,...)
Par défaut, les bibliothèques STD et WORK n’ont pas besoin d’être déclarées pour être utilisables.
Tout se passe comme si un programme VHDL commençait toujours par :
Library STD;
library WORK;

1.5 Entité
L'entité est la description de l'interface externe du circuit.
L'entité précise :
· le nom du circuit
· Les ports d'entrée-sortie :
o Leur nom
o Leur direction (in, out, inout,...)
o Leur type (bit, bit_vector, integer, std_logic,...)
L'écriture de l'entité pour l'additionneur 1 bit FA (la figure en dessous) peut être la suivante :
library ieee;
use ieee.std_logic_1164.all;
use ieee.numeric_std.all;
entity FA is
port (
a, b, cin : in std_logic;
s, cout : out std_logic);
end FA;
Il faut noter la déclaration préalable de la bibliothèque IEEE et des paquetages qui permet d'utiliser
le type std_logic. (std_logic_1164) et des fonctions arithmétiques (numeric_std).

1.6 Architecture
L'architecture est la description interne du circuit. Elle est toujours associée à une entité. Une même
entité peut avoir plusieurs architectures. L'exemple suivant montre un schéma de l'additionneur 1 bit
FA et 2 architectures possibles écrites en VHDL :
architecture arc2 of ADD1 is
begin
s <= a xor b xor cin;
cout <= (a and b) or ((a xor b) and
cin);
end arc1;
Dans une architecture il est nécessaire de déclarer les objets utilisés et leur type avant la zone
décrivant l'architecture (avant begin). Parmi les objets très utilisés, figurent les composants
(correspondent effectivement aux composants dont on a besoin dans l'architecture pour une
description structurelle) et les signaux.

2. TYPES-EXPRESSIONS
VHDL est un langage typé où il est obligatoire de spécifier le type des objets utilisés. Le schéma ci
dessous illustre la classification des types du langage VHDL :

2.1 Type entier


Le type entier integer prédéfini dans le paquetage standard STD permet de définir des nombres
signés sur 32 bits entre -2-31 et 231 - 1.

2.2 Type tableau


Les types TABLEAU ou array sont des collections d'objets de même type, indexés par des entiers
ou des énumérés.
type bus is array (0 to 31) of bit;
type RAM is array (0 to 1024, 0 to 31) of bit;
type PRIX is ranger 0 to 1000;
type COULEURS is (BLANC, BLEU, VERT, ROUGE, JAUNE, NOIR, ARC_EN_CIEL);
type PRIX_PEINTURES is array (COULEUR range BLANC to NOIR) of PRIX;
Il faut noter que l'indexation peut être MSB en tête (31 downto 0) , la plus courante, ou LSB en tête
(0 to 31).
Il peut y avoir des tableaux de tableaux ou des tableaux à plusieurs dimensions; Les affectations
diffèrent quelque peu comme illustré dans l'exemple suivant :
type TAB1 is array(0 to 2) of bit_vector(7 downto 0);
type TAB2 is array(0 to 3, 1 to 8) of bit;
signal A : TAB1;
signal B : TAB2;
begin
--tableau de tableau
A(0) <="01001111";
A(2)(5) <= '1';
-- tableau à 2 dimensions
B(3,5) <= '0';
end exemple;

2.3 Autres types


2.3.1 Type réel
Il existe un type réel prédéfini REAL. Il permet de représenter des nombres entre -1.0E+38 à
1.0E+38. Il n'est pas synthétisable.
Voici quelques exemples d'affectation d'un signal de type réel:
A <= 1.0;
B <= 5.9E10;
Notez la présence obligatoire du point.
2.3.2 Type STRING
Ce type permet de définir les chaînes de caractères. Il est définit comme un tableau d'éléments de type
CHARACTER dans la bibliothèque STD.
type string is array (positive range <>) of character;

2.3.3 Type enregistrement


Ce type permet de définir un objet dont les composantes sont hétérogènes.
2.4 Opérateurs
Les opérateurs prédifinis en VHDL sont classiques. Ils ne portent que sur les types prédéfinis,
BOOLEAN, BIT, INTEGER. Il faut donc définir une surcharge d'opérateur lorsqu'il s'agit
d'effectuer des opérations sur un nouveau type. Il faut noter l'absence du xnor et la différence entre
REM et MOD pour exprimer le reste de la division.

3. DESCRIPTION STRUCTURELLE
3.1 Eléments de base
VHDL permet l'assemblage de "composants" ce qui constitue une description structurelle. Ce
composant peut être appelé plusieurs fois dans un même circuit. Pour différencier ces mêmes
composants, il est nécessaire de leur donner un nom d'"instance". L'appel d'un composant se dit
aussi "instanciation"
De façon à instancier un composant il est nécessaire de connaître:
· Le prototype du composant (ses ports d'entrée et de sortie). La directive component peut être
utilisée.
·A quelle entité et architecture est liée chaque instance de composant.
Il est important de noter :
· La déclaration du composant (directive component) est redondante textuellement avec celle de
l'entité associée mais permet :
1. Une compilation indépendante entre l'entité associée au composant et le circuit utilisant le
composant.
2. La conception descendante. Le composant peut être déclaré avant l'entité associée.
· La configuration est une unité de compilation optionnelle, très utile pour les gros circuits. Par
exemple pour accélérer la simulation, un même composant peut être associé à un couple
entité/architecture détaillé et synthétisable ou un autre couple plus abstrait et plus rapide à simuler.
Pour ne pas utiliser de configuration, une règle fréquente est d'utiliser le même nom pour le
composant et l'entité associée.

3.2 Déclaration et instanciation des composants


3.2.1 Déclaration
Le mot clé component sert à déclarer le prototype d'interconnexion.
component AND_2
port (
a : in bit;
b : in bit;
s : out bit);
end component;

3.2.2 Instanciation
L'instanciation d'un composant se fait dans le corps de l'architecture de cette façon :
<NOM_INSTANCE>:<NOM_COMPOSANT> port map(LISTE DES CONNEXIONS);
Exemple:
entity AND_3 is Dans cet exemple, 2 instances de composant
port( e1 : in bit; "and2" sont appelées pour créer une porte ET à
e2 : in bit; 3 entrées.
e3 : in bit; L'association des ports du composant aux
s : out bit); signaux de l'instance se fait à l'aide de la clause
end entity; port map.
architecture arc of AND_3 is La syntaxe des associations est soit :
signal z : bit; 1. par nom où chaque broche du composant est
component and2 associée à un signal : cas de inst_1
port ( a : bit; 2. positionnelle où l'ordre des signaux correspond
b : bit; à l'ordre des broches : cas de inst_2
s : bit);
end component;
begin
inst1 : and2 port map
(a=>e1, b=>e2 , s=>z);
inst2 : and2 port map (z, e3, s);
end arc
4.DESCRIPTION COMPORTEMENTALE
4.1 Instructions concurrentes et séquentielles
Comme tout langage de description de matériel, le VHDL décrit des structures par assemblage
d'instructions concurrentes dont l'ordre d'écriture n'a aucune importance, contrairement aux
instructions séquentielles qui sont exécutées les unes après les autres.
VHDL offre cependant la possibilité d'utiliser des instructions séquentielles, plus naturelles pour
l'homme, par le biais de processus. Les processus peuvent avoir leurs propres variables locales.
Il existe 3 principales instructions concurrentes :
1. Les processus, qui offrent la possibilité d'utiliser des instructions séquentielles.
2. Les instanciations de composants
3. les affectations concurrentes de signaux, qui peuvent être conditionnelles

4.2 Processus
Les différentes tâches d'un programme VHDL s'exécutent en parallèle les unes des autres. Ces tâches
sont appelées processus. Toutes les instructions concurrentes sont en fait des processus mais la
déclaration explicite de processus par le mot clé process permet de construire sa propre instruction
par le biais d'instructions séquentielles internes au processus. Un processus peut avoir des variables
locales. Le fonctionnement du processus est régi par les règles suivantes :
1. Un processus est une boucle infinie, lorsqu'il arrive à la fin du code, il reprend automatiquement au
début.
2. Un processus doit être sensible des points d'arrêt de façon à le synchroniser. La synchronisation
est donc indiquée par un point d'arrêt qui est un évènement particulier. Il existe 2 types de points
d'arrêts :
o Le processus est associé à une "liste de sensibilité" qui contient une liste de signaux qui
réveillent le processus lors d'un changement d'un des signaux. Sa syntaxe est
process(liste de signaux)
o Le processus a des instructions d'arrêt wait dans sa description interne. Le wait est sensible
soit à un signal soit à un temps physique.
3. Les variables sont internes au processus et sont affectées immédiatement, contrairement aux
signaux qui eux ne sont pas affectés directement et qui sont mis à jour en fin de processus avec la
nouvelle valeur.
L'instruction WAIT permet de mettre des points d'arrêt dans le corps du processus. La syntaxe de
l'instruction est la suivante :
wait [on S1,S2,...] [until CONDITION] [for DUREE]
S1 et S2 sont des signaux, CONDITION est une expression générant un booléen, et DUREE est le
temps physique d'attente.
La liste des signaux réveillant le processus est indiquée dans la liste de sensibilité suivant cette syntaxe
Process (liste de sensibilité).

4.3 Structures de contrôle


Dans les processus, il est possible d'utiliser des structures de contrôle similaires à celles du C :
· Les instructions de test (if, case)
· Les boucles (loop, for loop, while loop)
4.3.1 Instruction if
L’instruction IF repose sur le test d'une condition qui génère un booléen. Si celui ci est "TRUE"
l'instruction qui suit est exécutée
Syntaxe de l’IF:
if condition1 then instruction;
[else if condition2 then instruction;]
...
[else instruction;]
end if;
4.3.2 Instruction case
L'instruction CASE repose sur le test d'un signal ou d'une variable. En fonction de la valeur, une
instruction spécifique est exécutée
Syntaxe du CASE :
case A is
when -7 => B := 10;
C :='0'
when -3 => B :=15;
C :='0';
when others => B :=2;
C :='1';
end case;
Pour coder un processus combinatoire, l'utilisation du when others est obligatoire de façon à traiter
toutes les combinaisons (sinon il y a mémorisation donc c'est da la logqiue séquentielle)
4.3.3 Instructions de boucle
L1: for i in 0 to 10 loop
...
L2: loop
...
L3 : while non_stop_L3 loop
...
exit L2 when stop_L2;
next L3 when suite_L3;
if stop_L1 then
exit L1;
end if;
end loop L3;
end loop L2;
end loop L1;
Il faut noter :
1. Les indices de boucles (ici i pour L1) ne sont pas à déclarer
2. Les étiquettes de boucles (L1, L2, L3) sont optionnelles
3. L'exécution peut être altérée avec les clauses next et exit.
NOTICE DE PRISE EN MAIN DU
LOGICIEL QUARTUS

1. Introduction
Quartus est un logiciel développé par la société Altera, permettant la gestion complète d'un flot
de conception des circuits logiques programmables CPLD ou FPGA . Ce logiciel permet de faire
une saisie graphique ou une description HDL (VHDL ou verilog) d'architecture numérique, d'en
réaliser une simulation, une synthèse et une implémentation sur cible reprogrammable.
VHDL est un langage de description de matériel destiné à représenter le comportement ainsi que
l'architecture d’un système électronique numérique. Son nom complet est VHSIC (Very High
Speed Integrated Circuit) Hardware Description Language.
2. Création d'un projet
Quartus est un logiciel qui travaille sous forme de projets c'est à dire qu'il gère un design sous
forme d'entités hiérarchiques. Un projet est l'ensemble des fichiers d'un design que ce soit des
saisies graphiques ou des fichiers VHDL ou bien encore des configurations de composants.
Pour lancer le logiciel, on cliquera sur :

Afin de créer un nouveau projet, aller dans le menu :


File —>New Project Wizard
Cliquer sur next, une fenêtre apparait:

Dans cette dernière trois champs sont à renseigner :


- L'emplacement du répertoire où seront stockés tous les fichiers.
- Le nom du projet,
- Le nom de l'entité maître du projet. On appelle entité maître, le design qui correspond à la
couche hiérarchique la plus haute. Par exemple si on conçoit un schéma nommé ALU dans
lequel il y aura plusieurs composants graphiques ou décrit en VHDL, alors ALU sera l'entité
maître. En d'autres termes, ALU ne dépend pas d'autres fichiers, c'est le niveau le plus haut dans
le design.
Cliquer sur Next, puis quand la fenêtre Add Files apparaît cliquer ensuite sur Next. Dans la
fenêtre suivante intitulée Family & Device Settings, choisir le circuit logique programmable
que l’on souhaite utiliser. Dans notre cas, on choisira le composant «EPM1270F256C5» de la
famille : «MAX II»

Quand la fenêtre EDA Tool Settings apparaît cliquer sur Next. Une fenêtre récapitulative
apparaît:

Valider les choix par Finish ou bien faire Back pour des modifications éventuelles. Dans le
navigateur de Projet, un onglet avec le type composant et l'entité maître apparaît :
3. Saisie d'un projet
Cette étape permet de définir et configurer les différentes parties du projet. Quartus accepte
plusieurs types de saisie à savoir :
- une saisie graphique en assemblant des symboles,
- une saisie textuelle à l'aide de différents langages (VHDL, Verilog, AHDL,...)
3.1. Saisie graphique
Pour saisir un projet en mode graphique, aller dans le menu : File —>New
La fenêtre suivante apparaît :

Choisir Block Diagram/Schematic File et faire OK. Une feuille blanche se crée intitulée
Block1.bdf.
On prendra soin de sauver cette feuille sous le nom de l'entité maître (ALU). C'est cette
feuille de saisie graphique qui a la hiérarchie la plus haute dans le projet.
Il convient après d'insérer des symboles dans notre feuille. Pour cela, nous pouvons soit choisir
des composants de la librairie Altera soit en créer en les décrivant en VHDL, ce qui sera l’objet
de l’étape suivante.
3.2. Saisie textuelle en VHDL
La saisie d'un composant VHDL se fait de la même manière que précédemment. Pour cela, aller
dans le menu :
File —>New

La fenêtre suivante apparaît :

Choisir VHDL File et cliquer sur OK. Un éditeur de texte apparaît

Comme exemple, on va créer un petit composant réalisant un ET logique sur 2 bits par le
programme suivant :

library ieee;
use ieee.std_logic_1164.all;
ENTITY ET IS
PORT (
a, b : IN bit;
s : OUT bit);
END ET ;
ARCHITECTURE data OF ET IS
BEGIN
s<= a AND b;
END data;
Une fois le code VHDL saisie, il faut le sauver: (File —>Save as ).
Il est important de sauver le fichier sous le même nom que l'entité. Dans notre cas, l'entité
s'appelle ET donc nous sauverons le fichier sous ET.vhdl. On prendra aussi garde à ne pas
appeler une entité du même nom qu'un composant natif Altera comme par exemple AND.
Vérifier ensuite la syntaxe, en cliquant sur:
Processing → Analyse Current File

Et on suivra l'évolution de la vérification et les différentes informations s'afficheront.

Une fois que le fichier est OK (aucune erreur détectée), on peut alors créer un symbole graphique
qui nous permettra d'insérer le composant dans la feuille graphique initiale (logic1).
Pour cela, aller dans:
File —»Create/Update —>Create Symbol File from Current File
Le symbole correspondant à notre composant VHDL est maintenant créé. Nous pouvons
l'instancier dans la feuille graphique ALU.
L'insertion d'un symbole se fait alors en cliquant dans la feuille graphique avec le bouton de
droite et en allant dans:
Insert —>Symbol.
La fenêtre suivante s'ouvre :

Normalement, le composant ET doit se trouver sous l'onglet Projet. On le choisit et on clique


sur OK
On remarquera sur l'onglet de gauche (Librairies), les bibliothèques propres au projet (personnel)
et les bibliothèques natives d'Altera.
Afin d'en faire une simulation, on lui adjoindra des composants d'entrées/sorties (IO) permettant
de repérer les signaux. Ces derniers se trouvent dans la librairie :
Altera —>Primitives —>Pin
Après qu’on a crée toutes les pins d’entrées /sortie, on lie les composants à l'aide de l'icône
entouré en noir dans la fenêtre ci-dessus.
On notera la différence entre les équipotentielles simples (trait fin) et les bus (trait large).
Pour notre cas, on choisit le trait fin .
Il est possible de renommer les pins en double-cliquant dessus. Une fenêtre de propriétés
apparaît et il suffit de se laisser guider.
On donne un exemple d’une entrée qu’on va nommer « a » en lui affectant la valeur initiale «
GND »
On fait de même pour les autres entrées et on nome aussi la sortie.
Finalement, on doit se trouver avec le schéma ci-après:

4. Compilation
Cette étape consiste maintenant à compiler le schéma précédemment réalisé.
Pour lancer la compilation, cliquer sur :
Processing —>Compiler Tool.
La fenêtre suivante apparait. On cliquera sur Start pour la compilation.
Normalement, il ne doit pas y avoir de warning ou d'erreur. Si ce n'est pas le cas vérifiez dans la
zone Processing (en bas où s'affichent les messages) la source du problème.
Dans certains cas, la compilation réussit même avec la présence de certains wanings qui n’ont pas
d’effet sur l’exécution.
on pourra ensuite lancer la commande:
Tools —> Netlist Viewers—> RTL Viewer
qui permet de voir comment le schéma (ALU) contenant le code VHDL a été transformé en
portes et bascules. Cela permet de voir comment la synthèse logique s'est déroulée.
La première fenêtre qui apparaît est:

En cliquant dessus l’objet graphique, on visualise:

5. Simulation
Afin de simuler le design réalisé, il convient de lui injecter des stimuli. Lorsque ces stimuli sont
générés à partir d'un fichier on dit que l'on utilise un fichier de Bench.
Dans le cas présent, nous allons simuler notre schéma en générant les stimuli à partir du Wave
Editor.
Pour cela, appliquer: File —>New, aller dans l'onglet Other Files et sélectionnez Vector
Waveform File (Fichier de simulation).

Faire une sauvegarde du fichier par File —> Save en lui donnant le nom (ALU ). Le fichier
sera du type .vwf.

Pour changer la durée de simulation, on fera Edit —>End Time

Aussi pour réaliser des zooms ou voir toute la simulation, il est possible de cliquer dans la fenêtre
avec le bouton de droite et de choisir Zoom dans le menu et Fit window
Insérer ensuite les différents signaux de simulation c'est à dire les signaux d'entrée et les sorties.
Pour cela, on fera:
Edit —>Insert Node or Bus.
Dans la fenêtre ci-dessus, cliquer sur Node Finder, ce qui permet de lancer le navigateur de
signaux
Dans le Node Finder, cela se déroule en 5 étapes :
1. Cliquer sur list afin de faire apparaître les signaux du design,
2. Sélectionner les signaux voulus,
3. Cliquer sur la flèche correspondante
4. Vérifier que tous les signaux que vous voulez visualiser sont dans Selected Nodes
5. Valider par OK puis Cliquer sur OK dans la fenêtre Insert Node or Bus

Afin de donner des valeurs de stimuli, on sélectionne avec la souris une partie du signal (en blue)
et on clique dessus deux fois. Une fenêtre apparait et on peut choisir les états logiques en binaire.
On notera la possibilité d'insérer automatiquement une horloge (clock) par exemple.
Les signaux d’entrée a et b choisis sont comme suit:

Finalement, il ne reste plus qu'à lancer la simulation par la commande :


processing →Simulator Tool.
La fenêtre suivante s'ouvre :
Dans cette fenêtre, il faut tout d'abord spécifier le fichier de simulation ( ALU.wvf ).
Choisir ensuite le mode de simulation : Timing (délais)
Cocher aussi la case Overwrite simulation input file with
simulation results (permettre le rafraîchissement automatique du visualiseur de signal)
Le bouton Start permet de lancer la simulation et il est possible de voir le résultat en cliquant
sur Report.
Une fois que la simulation est OK Les signaux de sortie apparaissent comme suit:

Vous aimerez peut-être aussi