You are on page 1of 56

PRÉAMBULE

Remarque préliminaire : ce cours n'est pas un manuel complet de vhdl. C'est plutôt


une introduction aux bases de vhdl. Nous y avons inclus ce qui nous semble
important, en laissant de côté les possibilités les plus complexes. Il part aussi du
principe que vous avez suivi le cours sur les HDL en général.

Plan du cours

Ce cours est séparé en chapitres. Il est conseillé de les suivre dans l'ordre, même si
ce n'est pas une nécessité absolue.

 Accueil

Vous trouverez dans ce chapitre l'introduction au cours, le plan du cours,


une FAQ, et un Changelog.
La FAQ ne vous sera probablement utile qu'après avoir suivi le cours...
Temps estimé : 10 minutes + autant que vous le souhaiterez pour la FAQ

 Exemples réels

Quelques exemples valant mieux qu'un long discours, ce chapitre vous


plonge directement dans le langage. Les exemples clefs sont présentés,
chacun introduisant une construction nouvelle. Ces exemples sont à étudier
attentivement.
Temps estimé : 1 heure 30 minutes
 
 Organisation du langage

Ce chapitre traite de l'organisation du langage : en entité de compilation


d'un côté et en bibliothèques de résultats de l'autre.
 
 Types de données, expressions

Avant de voir comment on écrit un modèle vhdl, il faut étudier les types de
données disponibles, les expressions disponibles ainsi que leur taille. En deux
mots, les types et expressions utilisables, et les pièges à éviter.
Temps estimé : 1 heure.
 
 vhdl structurel

Présentation des éléments structurels de vhdl. Ou, plus simplement,


comment écrire et instancier des composants, comment les relier.
Temps estimé : 30 minutes.  

 vhdl comportemental

Présentation des éléments fonctionnels de vhdl. Ou, plus simplement,


comment décrire la fonction d'un composant.
Temps estimé : 1heure 45 minutes

 Synthèse et simulation

Présentation des différentes sémantiques. Ou, plus simplement, comment


écrire du vhdl synthétisable.
Temps estimé : 30 minutes 

En pratique

Chaque chapitre est séparé en sous-partie. Vous pouvez naviguer dans les chapitres
et sous-parties à l'aide du menu en haut de page.
Les icônes en bas de page vous permettent de passer au chapitre précédent ou
suivant, ou de revenir à l'accueil du site sur les HDL.

Un enseignant se tient à votre disposition pendant les heures de cours, mais vous
pouvez toujours nous contacter par courrier électronique.

Dernier détail, merci d'utiliser un navigateur acceptant les CCS et le javascript


(firefox, mozilla, ...)

Bon courage !

Back to Top

HISTORIQUE RAPIDE
VHDL (VHSIC Hardware Description Langage) est un langage de description de
matériel, c'est-à-dire un langage utilisé pour décrire un système numérique
matériel, comme, par exemple, un flip-flop (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.

VHDL est l'un des trois grands langages de description de matériel utilisés
majoritairement dans l'industrie, avec VHDL et SystemC. Chaque langage a ses
propres avantages et inconvénients, ainsi que ses spécificités. Pour plus de détails,
on pourra se référer à une comparaison objective de VHDL et Verilog.

Le langage standard IEEE VHDL a été développé par le Groupe d’Analyse et de


Standardisation VHDL (VASG, pour “VHDL Analysis and Standardization Group”).
Larry Saunders est le coordinateur de VASG. La société CLSI (CAD Langage Systems
Inc.), représentée par le Docteur Moe Shahdad et M. Erich Marschner a préparé une
série d’analyses et de recommandations dont a été tirée en Février 1986 la version
7.2 de VHDL, point de départ du futur standard. La collaboration de CLSI au projet
était financée par un contrat passé avec l’Air Force Wright Aeronautical
Laboratories, représentée par le Docteur John Hines. Le standard définitif a été
adopté vers le milieu de l’année 1987. La dernière version date de 2002 mais ce
cours présente le langage dans la version 1993, correspondant au standard IEEE
1076-1993, qui est supporté par de nombreux outils de simulation et synthèse.
Pour en savoir plus : http://vhdl.org
PRÉAMBULE
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. Avant analyse Il faut donc préciser au compilateur la
bibliothèque dans laquelle sera rangé le résultat. Dans un programme VHDL, on
peut ainsi faire référence à un objet préalablement analysé en prenant soin de
préciser dans quelle bibliothèque se trouve l'objet.

Une fois la compilation terminée, il faut effectuer l'édition de lien (on parle
d'élaboration pour le VHDL). L'élaboration s'effectue sur la description structurelle
de plus haut niveau hierarchique (l'équivalent du schéma au plus haut niveau).

La simulation peut alors s'effectuer sur le résultat de l'élaboration (rangé dans la


bibliothèque spécifiée avant élaboration).

Structure du langage

L'analyse d'un modèle VHDL peut s'effectuer sur des parties du code ou "unités de
compilation". Il existe 5 types d'unités de compilation :

 L'entité 
 L'architecture
 Le paquetage
 Le corps du paquetage
 La configuration

L'entité et l'architecture sont des unités de compilation  obligatoires pour décrire


un modèle. Les autres unités sont optionnelles mais quasiment indispensables pour
concevoir des gros circuits nécessitant une méthode de conception efficace.

Une unité de compilation doit être écrite dans le même fichier et un même fichier
peut contenir plusieurs unités de compilation. 

L'objectif de ce chapitre est de vous présenter les conventions lexicales de VHDL,


les bibliothèques et les unités de compilation  (à part la configuration qui sera vue
dans le chapitre sur le VHDL structurel)

Plan du chapitre

 Conventions lexicales
   Caractères utilisables, casse, ...

 Bibliothèques
    Où sont stockés les résultats
 Entité
    La vue externe du circuit

 Architecture
    La vue interne du circuit

 Paquetage
  Les objets communs

 Résumé

Back to Top

CONVENTIONS LEXICALES
Les conventions lexicales de vhdl sont les mêmes que celles utilisées en C. Un code
vhdl est composé d'une suite d'éléments :

 commentaires
 délimiteurs
 nombres
 chaînes de caractères
 identificateurs
 mots-clefs

Casse
VHDL est insensible à la casse. Un mot en majuscule est identique à un mot
en minuscule. Il est cependant conseillé d'avoir des règles d'écriture
cohérentes. Par exemple, les mots reservés du langage peuvent être en
majuscule et les autres mots en minuscule.

Commentaires
Les commentaires doivent être inclus dans le code, pour augmenter la
lisibilité et la documentation. Ils commencent par 2 tirets (--) en se terminent
en fin de ligne

Il est impératif de documenter vos codes, à l'école comme en entreprise.

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.
Expressions
Elles se terminent par un point virgule ;

Lité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 litéral pointeur

Back to Top

BIBLIOTHEQUES
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 hôte.

La création de la bibliothèque ne fait pas partie du langage VHDL Chaque outil VHDL a donc
ses propres règles de création..  Par exemple avec le simulateur ModeSim il faut créer la
bibliothèque avec cette commande :

vlib BIB

L'accès à la bibliothèque fait partie du langage VHDL. 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 . WORK 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 des définitions des types et des
fonctions de base (integer, BIT, BOOLEAN,...) dans le paquetage STANDARD et des
fonctions sur les caractères dans le paquetage TEXTIO.  

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;

Il est très pratique d'utiliser les paquetages  des bibliothèques ce qui permet d'utiliser des
objets (constantes, fonctions, composants,...) qui peuvent être définis dans une bibliothèque
différente de celle en cours. C'est le cas des bibliothèques standards comme l'IEEE qui définit
des types et objets normalisés compris  par les outils de synthèse.
Pour utiliser le contenu d’un paquetage, il faut déclarer la bibliothèque dans laquelle il se
trouve (sauf, éventuellement, si c’est WORK) et le paquetage :

use BIBLIOTHEQUE.PAQUETAGE.all;

ou, si l’on ne veut pas utiliser tout le paquetage mais un seul objet :

use BIBLIOTHEQUE.PAQUETAGE.OBJET;

  

Back to Top

ENTITE
L'entité est la description de l'interface du circuit . Elle correspond au symbole
dans les représentations schématiques :

symbole de l'additionneur 1 bit

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,...)
 Les paramètres éventuels pour les modèles génériques

L'écriture de l'entité pour l'additioneur 1 bit fa peut être la suivante . 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).

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 entity;
Back to Top

ARCHITECTURE
L'architecture est la description interne du circuit. Elle est toujours associée à une
entité. Une même entité peut avoir plusieurs architecture.  Le mécanisme de
configuration (décrit dans le VHDL structurel) permet d'indiquer l'architecture
rattachée à une entité. L'exemple suivant montre un schéma de l'additionneur 1 bit
fa et 2 architectures possibles écrites en VHDL :

       

architecture arc1 of fa is architecture arc2 of fa is


-- --
signal resultat : unsigned(1 downto 0); begin
-- --
begin s <= a xor b xor cin;
-- cout <= (a and b) or ((a xor b)
resultat <= ('0' & a) + ('0' & b) + ('0' & cin); --
s <= resultat(0); end arc1;
cout <= resultat(1); --
--
end arc1;
             

VHDL = langage déclaratif et typé


VHDL est un langage très déclaratif qui permet au compilateur d'effectuer des
vérifications poussées et fiabiliser ainsi l'écriture d'un modèle.

Dans une architecture il est nécessaire de déclarer les objets utilisés et leur type
avant la zone décrivant l'architecture (le corps de l'architecture situé entre begin ...
end ).

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 (equipotentielles reliant les instances de
composants et les instructions "concurrentes" et).
Les ports déclarés dans l'entité sont des signaux utilisables dans l'architecture, il ne
faut pas les redéclarer.

L'exemple ci-dessous illustre l'architecture d'un additionneur 1 bit avec les signaux
A, B , Cin (qui sont des ports donc des signaux implicites) , le signal Si et le
composant MAJ .

Le schéma équivalent de l'additionneur 1 bit peut être le suivant avec 1 composant


MAJ et 2 portes XOR qui peuvent être codées d'une façon comportementale. 

Le code VHDL correspondant :

entity FA is Entité de l'additioneur 1 bit FA


port (
A, B, Cin :
in bit;
S, Cout :
out bit
);
end entity;
architecture arc3 La partie déclarative de l'architecture, avant le mot clé
of fa is
-- begin, permet de déclarer le signal SI et le composant
signal SI : bit; MAJ.
--
component MAJ
port ( X,Y,Z : in
bit;
M : out
bit);
end component;
begin Dans le corps de l'architecture (entre begin et end) se
--
p1 : process(SI, trouvent les 3 types d'instructions concurrentes de
Cin) VHDLqui peuvent être écrites dans n'importe quel ordre :
begin
S <= SI xor
Cin;  Un processus avec le mot clé process
end process;  Une instruction concurrente d'affectation S <= SI xor
-- Cin ( on dit S reçoit Si xor Cin)
SI <= A xor B;
--  Une instruction d'appel de composant (instanciation)
inst_MAJ : MAJ port ou un nom d'instance est donné (INST_MAJ) de
map ( façon à pouvoir appeler plusieurs composants du
X => A, Y =>
même type

B,Z => Cin, M => Les ports  sont des signaux implicites dont le sens doit être
Cout); respecté. Le compilateur VHDL aurait mis une erreur si
-- dans le corps de l'architecture s'était trouvé l'instruction :
end arc3;
Cout <= S and B  car S est une sortie qui ne peut pas servir
d'entrée.

     

Back to Top

Back to Top

PAQUETAGE
Le paquetage est une collection d'objets réutilisables. Il se compose de 2 unités de
compilation :

1. La déclaration du paquetage
2. Le corps du paquetage

Le contenu de la déclaration du paquetage est visible depuis l'extérieur. alors que le corps
décrit la fonction des objets nécessaires à l'utilisation du paquetage. Lorsqu'une unité de
compilation utilise un paquetage, la déclaration de celui-ci doit être analysée en premier.
Le corps du paquetage n'est pas toujours nécessaire.

Exemple de déclaration de paquetage


package PAQ is Le paquetage PAQ est déclaré avec le mot clé package.
--
subtype MOT is Des nouveaux types type et sous-types subtype sont
bit_vector(7 downto déclarés.
0);
type RAM is array Dans cet exemple on déclare le type MOT qui est un
(natural range <>) of vecteur de 8 bit, un type RAM qui est un tableau de MOT
MOT; et un sous type MRAM qui est une RAM de 1024 MOTS.
subtype MRAM is RAM(0
to 1023);
-- Des constantes  "constant" N_BITS et REL sont
constant N_BITS :
positive; déclarées. N_BITS est un entier > 0 "positive" et REL est
constant REL :
natural := 8; un entier naturel "NATURAL" et est initialisée à la
-- valeur 8.
component BRIQUE
generic (N :
positive := 12); Le composant "component" BRIQUE est déclaré ici en
port ( A,B : précisnat ces ports et paramètres. La déclaration d'un
in bit;
S : out bit);
composant permet son instanciation dans l'architecture
end component; de façon à effectuer une description structurelle
--
function MIN(A,B,C :
integer) return Le sous-programme ou fonction "function" MIN est
integer; déclarée dans le paquetage. La description de la fonction
--
ser effectuée dans le corps du paquetage.
end package PAQ;

Exemple de corps de paquetage


package body PAQ is Le corps du paquetage  "package body" permet de
--
constant N_BITS := 16; décrire les sous-programmes et d'initialiser les
-- constantes.
function
MIN(A,B:integer) return
integer is La constante N_BITS est initialisée ici. Elle aurait pu
begin être utilisée localement au corps du sous programme
if (A<B) then
sans être visible de l'extérieur.
return A;
else
return B; Les fonctions peuvent être surchargées en nom ou en
end if;
end if;
type. Ici 2 fonctions MIN sont décrites , l'une avec 2
end MIN; entrées et l'autre avec 3 entrées utilisant celle à 2
-- entrées.
function MIN(A,B,C :
integer) return integer
is
begin
return MIN(A,
MIN(B,C));
end MIN;
--
end package body PAQ;

Back to Top
EN RÉSUMÉ
On a vu

 Les bibliothèques 
o permettent de stocker les résultats et de les réutiliser.
o certaines sont par defaut WORK et STD, d'autres sont standard IEEE,
d'autres sont personnelles.
 L'entité
o Elle décrit l'interface du circuit avec le monde extérieur.
o Il faut spécifier les ports, leur sens et leur type.
o Les paramètres d'un circuit générique sont déclarés dans l'entité.
 L'architecture
o Décrit l'intérieur du circuit , en structurel (schéma) ou en comportemental
ou les 2.
o Avant de les utiliser dans le corps de l'architecture, il faut déclarer les
objets et les types (signaux, composants, constantes,fonctions,...).

Prochain chapitre
Le prochain chapitre traite des types et des opérateurs de base en VHDL
PRÉAMBULE
Objectifs

VHDL est un langage typé où il est obligatoire de spécifier le type des objets


utilisés. Le tableau ci dessous illustre la classification des types du langage VHDL :

L'objectif de ce chapitre est de vous présenter les types et les opérateurs. A l'issue
de ce chapitre, vous serez en mesure de choisir le meilleur type pour l'objet que
vous voulez modéliser.

Plan du chapitre.

 Types entiers
  C'est le type de base, sur 32 bits

 Types énumérés
  Les types Booléens et bits  font partie de cette catégorie.

 Types tableau
  Indispendables pour représenter des vecteurs, des matrices, des mémoires.

 Types fichiers
 Servent à effectuer des tests en lisant et/ou générant des données
contenues dans ces fichiers.

 Autres types
    Une vue d'ensemble des autres types définis dans les bibliothèques
standards.
 Attibuts
    Permettent de consulter les caractéristiques des types ou d'objets
déclarés. Il est possible de déclarer ses propres attributs.

 Opérateurs
    La palette d'opérateurs standards.

 IEEE
    Bibliothèque utilisée par tous les outils de synthèse et permettant de
travailler sur des types logiques à 9 états.

 Résumé

Remarque importante

Ce chapitre, comme tous les chapitres sur la syntaxe des langages, est un peu
indigeste. Il vaut mieux le voir comme une référence, une page à laquelle vous
pourrez vous rapporter tout au long du cours, ou en cas de problème. Par contre, il
est conseillé de l'avoir lue au moins une fois, pour garder en tête où se trouvent les
pièges éventuels.
Back to Top

TYPES ENTIERS
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  . 

Un sous type subtype permet de déclarer un type héritant des propriétés du type
père.

Il existe 2 "sous types" subtype associés à INTEGER : les entiers naturels et les
entiers positifs. Leur déclaration dans le paquetage STD est la suivante :

subtype natural is integer range 0 to


integer'high;
subtype positive is integer range 1 to
integer'high
Notez que

1. range permet d'indiquer l'intervalle 


2. 'HIGH indique la plus grande valeur du type INTEGER, c'est un attribut de
type

Les types entiers servent à définir des indices de tableaux et de boucles. Pour cela
il est intéressant de les restreindre de façon à contrôler les débordements.
Par exemple on peut déclarer :
subtype UN_A_DIX is natural range (1 to 10);
subtype DIX_A_UN is natural range (10 downto 1);

Il s'agit d'objets compatibles entre eux mais avec un ordre


d'indexation différent.
Il est donc possible d'effectuer des opérations entre ces objets 
En revanche si on avait déclaré :

type UN_A_DIX is natural range (1 to 10);


type DIX_A_UN is natural range (10 downto 1);

Les 2 types étant indépendants, une opération entre des objets UN_A_DIX et
DIX_A_UN  aurait  causé une erreur de compilation.
  

Back to Top

TYPES ENUMERE
Un type énuméré est un type défini par une énumération exhaustive :

type COULEURS is (ROUGE, JAUNE, BLEU, VERT,


ORANGE);

L'ordre de déclaration est important. Lors de l'initialisation d'un signal de type


enuméré T, le signal prend la valeur T'LEFT.
Par exemple un signal de type COULEURS sera ROUGE en début de simulation.

Dans le paquetage STANDARD de la bibliothèque STD, plusieurs types énumérés


sont définis :

type boolean is (FALSE, TRUE);


type bit is ('0', '1');
type severity_level is (NOTE, WARNING, ERROR, FAILURE);
type character is ( NUL, SOH, STX, ETX,..., '0','1', ...);

Notez que la valeur d'un bit est équivalente à un caractère et est toujours entre
quotes : '0' et  '1'  différents des entiers 0 et 1.

Dans le paquetage STD_LOGIC_1164 de la bibliothèque IEEE, le type


STD_ULOGIC est défini par :

type std_ulogic is ('U', 'X, 'O', '1', 'Z', 'W', 'L', 'H',
'-');

Ce permet d'avoir 9 états significatifs de la logique. Ces états illustrent les cas où
le signal est soumis à de multiples affectations. Dans ce cas chaque valeur à un
niveau de priorité.
La liste suivante donne la signification de ces 9 valeurs en commençant par la
valeur de plus grande priorité :

 Au démarrage les signaux sont dans un état inconnu 'U'.


 'X' indique un conflit, le signal est affecté d'un côté à '1' et d'un autre à '0'.
 '0' et '1' correspondant aux valeurs booleennes du signal.
 'Z' correspond à l'état haute 'impédance".
 'W' est la valeur d'un signal relié à 2 résistances de tirage, une tirant à 0 et
l'autre à 1.
 'H' et 'L'  sont des valeurs d'un signal relié respectivement à une résistance
de tirage à 1 et à 0.
 '-' est un état indifférent. Utile pour décrire les tables de vérité.

Le type énuméré est aussi très utile pour définir les états d'une machine à états
d'une façon symbolique, sans avoir à définir le codage.

Back to Top

TYPES TABLEAU
Les types TABLEAU ou array sont des collections d'objets de même type, indéxés
par des entiers ou des énumérés.

Exemples :

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;
Un tableau peut avoir une taille inconnue donc non contraint, par exemple le type
BIT_VECTOR de la bibliothèque STD est  un tableau de dimension 1 (vecteur) de
taille quelconque :

type bit_vector is array (natural range <>) of bit;


et le type STD_ULOGIC_VECTOR de la bibliothèque IEEE :

type std_ulogic_vector is array (natural range <>) of


std_logic_vector;

La contrainte de taille est alors exprimée dans la déclaration de l'objet :

signal toto bit_vector(31 downto 0);


signal titi std_ulogic_vector(7 downto 0);
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).
Attention, une affectation de signaux vectoriels ayant des indexations différente
provoque une inversion des composantes du signal. 
La valeur d'un vecteur peut être représentée dans une base différente :  "001100" =
0"14" = X"0C" 

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;

 Notation d'agrégat

Cette notation permet d'initialiser les tableaux facilement.


Exemple :

type OPTYPE is (ADD,SUB,MUL,DIV,BRA);


type T is array ( 1 to 10) of OPTYPE;
signal A : T;
--
A <= (ADD,SUB,MUL,DIV,BRA);
A <= (ADD,SUB,5=>BRA,4=>DIV,3=>MUL);
A <= (ADD,2|4=>SUB,others => DIV);
--

Cet exemple illustre l'affectation par position et à la position par dénomination.


Notez l'utilisation de la clause 'others' pour compléter automatiquement les valeurs
du tableau.

Back to Top

TYPES FICHIERS 
Les types fichiers FILE permet l'échange de données entre l'extérieur et le
simulateur VHDL. Il est utilisé principalement pour créer des fichiers de test ou
TESTBENCH de modèles.

Le paquetage TEXTIO de la bibliothèque STD définit un type fichier texte TEXT


et des procédures pour accéder aux lignes du fichier et aux chaînes dans la ligne.  
Pour l'utiliser il est nécessaire de le déclarer en début de fichier :

use STD.TEXTIO.ALL;

Un fichier peut être soit en lecture soit en écriture mais pas les 2 en même temps.
L'exemple commenté suivant illustre 2 processus permettant respectivement de lire
le fichier "entrees.dat" et d'écrire les résultats dans "sorties.dat" :

LECTURE: process
variable L: line; -- le type LINE est un pointeur
file ENTREES: text open READ_MODE is "entrees.dat"; -- fichier spécifié
variable A: bit_vector(7 downto 0); -- variables à lire
variable B: natural range 0 to 11;
begin
readline(ENTREES, L); -- lecture d'une nouvelle ligne dans le fichier
read(L, A); -- lecture dans la ligne du 1er symbole => BIT
VA <= A; -- utilisation pour la simulation
read(L, B); -- lecture dans la ligne du 2ème symbole => entier
VB <= B; -- utilisation pour la simulation
wait for 20 ns; -- attente de 20 ns;
end process LECTURE;
--
ECRITURE: process(S)
variable L: line;
file SORTIES: text open WRITE_MODE is "sorties.dat";
begin
write(L, S); -- écriture de S dans la ligne
write(L, string'(" à t = ")); -- écriture de texte dans la ligne
write(L, now); -- écriture du temps de simulation dans la ligne
writeline(SORTIES, L); -- écriture de la ligne dans le fichier
end process ECRITURE;

Back to Top

 
AUTRES TYPES
Types réels

 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;
C <= -8.5E20;

Notez la présence obligatoire du point.

Types physiques

VHDL permet de définir des types physiques pour représenter une grandeur
physique, comme le temps, la tension, etc... 

Un type physique est la combinaison d'un type entier et d'un système d'unité.
Le type TIME, est le seul type physique prédéfini :

type time is range $- to $+ -- l'intervalle dépend de la machine


units
fs;
ps = 1000 fs;
ns = 1000 ps;
us = 1000 ns;
Ms = 1000 us;
sec = 1000 ms;
min = 60 sec;
hr = 60 min;
end units;
Les simulateurs VHDL utilsent la fonction now, de la bibliothèque STD , qui
retourne le temps physique de type TIME.

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;


--
"ceci est une chaîne de caractère"

Type enregistrement RECORD

Ce type permet de définir un objet dont les composantes sont hétérogènes.

type OPTYPE is (MOV, ADD, SUB, JMP, CALL)


type INSTRUCTION is record
--
OPCODE : OPTYPE;
ADR : bit_vector(7 downto 0);
OP2 : bit_vector(7 downto 0);
--
end record;
L'affectation d'un objet de type RECORD  peut s'effectuer de différentes façons :

signal INST1 : INSTRUCTION;


signal INST2 : INSTRUCTION:
--
begin
--
INST1 <= (MOV, "00011100", X"FF");
INST2.ADR <= X"8A";
--
end;
Les RECORD sont très utiles pour les entités dont les ports peuvent être amenés à
changer en nombre et en type. Les ports sont alors de type RECORD et ne
changent pas. En cas de modification il suffit de modifier le contenu du type
RECORD plutôt que de modifier les ports dans les entités.

Type pointeur ACCESS

Les pointeurs sont peu utilisés en VHDL car on leur préfère les tableaux indicés qui
peuvent être synthétisables, à la différence des pointeurs.  Nous  ne les étudieront
pas dans ce cours. 

Back to Top

ATTRIBUTS
Il s'agit de caractéristiques de types ou d'objet qu'il est possible d'utiliser dans le
modèle. Ils sont représentés de cette façon :

<OBJET>'<ATTRIBUT>

Il existe des attributs sur les types, sur les objets de type tableau  et sur les
signaux. 

Il est possible de créer ces propres attributs. Certains outils de synthèse en tirent
profit pour passer des arguments de synthèse.

Attributs sur les types 

L'exemple suivant illustre les principaux attributs de type :

type COULEUR is (BLEU, ROUGE, VERT);


--
COULEUR'left renvoie BLEU
COULEUR'right renvoie VERT
COULEUR'pos(BLEU) renvoie 0
COULEUR'val(0) renvoie BLEU
COULEUR'succ(BLEU) renvoie ROUGE
COULEUR'pred(ROUGE) renvoie BLEU
 

Attributs sur les objets de type tableau

exemples :

type MOT is bit_vector(7 downto 0);


type TAB is array (4 downto 0) of MOT;
signal NOM : MOT;
signal TABLEAU : TAB;

MOT'LEFT renvoie 7;
MOT'LENGTH renvoie 8;
TABLEAU'RIGHT renvoie 0;
TABLEAU'RANGE renvoie 4 downto 0;

Ces attributs sont très utiles pour créer des indices ou pour écrire des sous-
programmes manipulant des tableaux de taille variable.

Attributs sur les signaux

Ils servent à indiquer les caractéristiques d'évolution temporelle des signaux.


Exemples :
CLK'EVENT renvoie un BOOLEAN indiquant si le signal CLK a changé.
CLK'DELAYED(1 ns) est un signal identique à CLK décalé de 1 ns.

Attributs définis par le concepteur

Ils permettent d'associer des caractéristiques propres aux objets. Certains outils de
synthèse ont leurs propres attributs pour rentrer des contraintes de synthèse dans
le code.
L'exemple suivant défini le brochage de certains signaux.  
Exemple :
ATTRIBUTE NUMERO_BROCHE : POSITIVE;
ATTRIBUTE NUMERO_BROCHE of ENTREE1 is 12;
ATTRIBUTE NUMERO_BROCHE of ENTREE2 is 17;
Back to Top

OPERATEURS
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.

Type
opérateurs notes
d'opérations
Logiques and, or, nand, nor, xor, not
Relationnels =, /=, < , <=, > , <=
Arithmétique *, / , mod, rem  (A rem B) a le
signe de A
(A mod B) a le
signe de B
** :
exponentiation
Divers **, abs, & abs : valeur
absolue
& : concaténation

Back to Top

EN RÉSUMÉ
On a vu:

 La diversité des types  VHDL


 les attributs
 les opérateurs

Prochain chapitre

le VHDL structurel.
PRÉAMBULE
Objectifs

La description structurelle d'un circuit complexe en vhdl présente de nombreux


avantages :

  Une architecture hiérarchique compréhensible : il est plus simple de


séparer un circuit en un ensemble de blocs plus petits, ayant des
fonctions bien identifiées. Ces blocs pourront alors être décrits sous
forme comportementale, ou bien à leur tour être séparés en blocs
encore plus simples.
 Une synthèse logique efficace : la synthèse est un processus lent (en
terme de temps de calcul). Plus un bloc est gros et complexe, plus sa
synthèse prendra du temps. Il vaut donc mieux travailler sur des blocs
plus petits, plus simples à synthétiser, et rassembler le tout à la fin.

L'objectif de ce chapitre est de voir précisément comment coder une


représentation structurelle d'un circuit, autrement dit :

 comment déclarer des blocs (qu'on appellera composant)


 comment utiliser un composant (qui devient une "instance") et déclarer
la façon dont il est  connecté.
 comment choisir l'architecture d'un composant quand il y en a plusieurs
(configurations)

Plan du chapitre

 Eléments de base
   qu'est-ce qu'un composant, que contient-il ?
 Déclaration et instanciation des composants
    comment déclare-t-on un composant  (déclaration) et comment l'appeler
pour le connecter (instanciation) ?
 Composants génériques
    VHDL offre la possibilité d'avoir des composants génériques comme par
exemple un compteur sur N bits où N est un paramètre qu'il faut indiquer au
moment de l'instanciation. Comment fait on pour passer des paramètres à ce
type de composants
 Configurations
    VHDL offre la possibilité d'avoir plusieurs scénarios d'architecture pour un
même composant. Comment configurer le composant ?
 Exercices

Back to Top
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 à cette fin. 
 A quelle entité et architecture est lié chaque instance de composant. Ce lien
peut être connu grâce à l'unité de configuration.

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, c'est le cas pour ModelSim et les
outils de synthèse FPGA.

La description structurelle est  nécessaire pour simuler un circuit dont les vecteurs
stimulis sont eux mêmes issus d'un modèle VHDL. Le modèle de plus haut niveau
fait donc appel au circuit à tester (Device Under Test) et d'un générateur de
stimulis. Ces deux objets sont instanciés dans un même circuit, généralement
appelé "testbench" (mais ce n'est pas une obligation) qui est autonome : il n'aura
pas d'entrées ni de sorties.

Exemple : le circuit top, servant à simuler le circuit  "module a" doit être autonome
: son entité n'a pas d'entrée ni de sortie.
Cas particulier de la simulation : circuit "top" sans entrée ni sortie

Back to Top

DÉCLARATION ET INSTANCIATION DES


COMPOSANTS
Déclaration

Le mot clé component sert à déclarer le prototype d'interconnexion. La syntaxe est


presque identique à celle de l'entité :
component AND_2
port (
a : in bit;
b : in bit;
s : out bit);
end component;

Pour créer rapidement un composant, une opération copier/coller de l'entité en


enlevant le litéral "IS" suffit.

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 c
port( créer une porte ET à 3
e1 : in bit; entrées.
e2 : in bit;
e3 : in bit;
s : out bit L'association des ports du composan
); à l'aide de la
end entity; clause port map.
--
architecture arc of AND_3 is
-- La syntaxe des associations est soi
signal z : bit;
component and2 1. par nom où chaque broche du co
port ( cas de inst_1
a : bit; 2. positionnelle où l'ordre des s
b : bit; 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

Back
to 

GENERICITE
Déclaration
Un composant peut être générique en définissant les paramètres qui seront vus
comme des constantes à chaque instance de composant. Il est ainsi possible de
n'avoir qu'un seul composant  pour différentes instances ayant des paramètres
différents. Dans la déclaration du composant, la clause generic sert à passer les
paramètres au composant. Dans l'exemple suivant, l'entier positif N indique le
nombre de bits de l'additionneur.

component ADD Le paramètre N permet de


generic dimensionner la taille de l'a
(
N : positive range 0 to 16
clause generic
);
port
(
A: in std_logic_vector(N-1 downto 0);
B: in std_logic_vector(N-1 downto 0);
S: out std_logic_vector(N-1 downto 0)
);
end component;
De même l'entité associée au composant doit comporter la clause generic pour
déclarer le(s) paramètre(s)

entity ADD is
generic
(
N : positive range 0 to 16
);
port
(
A: in std_logic_vector(N-1 downto 0);
B: in std_logic_vector(N-1 downto 0);
S: out std_logic_vector(N-1 downto 0)
);
end entity ADD;
 

Instanciation :

L'instanciation d'un composant se fait dans le corps de l'architecture de cette façon


:

architecture arc of mult is La clause generic map


component ADD
generic (
dans l'instanciation du compos
N : positive range 0 to 16); paramètre.
port (
A: in std_logic_vector(N-1 downto 0);
B: in std_logic_vector(N-1 downto 0);
S: out std_logic_vector(N-1 downto 0));
end component;
signal OP1,OP2,S std_logic_vector(N-1 downto 0);
. . .
--
begin
inst_ADD : ADD
generic map(N=>12);
port map(A=>OP1, B=>OP2,S=>S);
. . .
end arc;

Instanciation de multiples composants

Les paramètres ne sont parfois pas suffisants pour écrire un code générique : on
peut aussi vouloir instancier un nombre variable de composants (en fonction d'un
paramètre, par exemple). Ceci est fait au moyen des mots-clef for generate..et if
generate.
  
Exemple 1 : on veut décrire un multiplieur générique, tel que :

 L'architecture Carry Lookhead (CLA) est utilisée s'il doit manipuler des
nombres de largeur inférieure à 8 bits, 
 L'architecture en arbre de Wallace est utilisée sinon.

L'exemple ci-dessous fait appel à la clause IF GENERATE en testant le paramètre


width.
Notez que :

1. le ELSE n'existe pas (oubli de VHDL ?)  et qu'il faut refaire un 2ème IF.
2. l'instruction IF GENERATE a besoin obligatoirement d'une étiquette

use work.pack.all;
-- paquetage où sont déclarés les composants CLA- L'exemple ci-co
multplier en testant
-- et WAL_multiplier le paramètre wi

entity multiplier is Notez que :


generic( 1. le ELSE
width : positive :=8;); n'existe p
port(
a : in signed(width-1 downto 0); faut refai
b : in signed(width-1 downto 0); 2. l'instruct
product : out (2*width-1 downto 0)); a besoin o
end entity; étiquette
--
architecture arc of multiplier is
--
begin
--
CLA_gen : if width < 8 generate
inst_cla : CLA_multiplier generic map (width) port
map(a, b, product);
--
end generate CLA_gen;
--
WAL_gen : if width >= 8 generate
inst_wal : WAL_multiplier generic map (width) port
map(a, b, product);
end generate WAL_gen;
--
end arc;
--

Exemple 2 : l'exemple classique de l'additionneur n bits... On utilise ici une


boucle FOR GENERATE pour instancier automatiquement les différentes
primitives ainsi que les noeuds les connectant entre elles. Notez qu'il n'est pas
nécessaire de déclarer la variable de boucle mais que l'instruction FOR
GENERATE nécessite une étiquette.
entity Nbit_adder is
generic( SIZE = 4);
port (
a,b : in unsigned(SIZE-1 downto 0);
ci : in std_logic;
sum : out unsigned(SIZE-1 downto 0);
co : out std_logic);
end entity;
--
component FA
port(A,B,Cin : in std_logic;
S, Cout : out std_logic);
end component;
--
architecture arc of Nbit_adder is
--
signal c :unsigned(SIZE downto 0);;
--
begin
--
C(0) <= Cin;
co <= C(SIZE);
--
G: for I in 0 to N-1 generate
inst: FA port map(A(I), B(I), C(I), sum(I),
C(I+1));
end generate G;
--
end arc;

Back to 

LA CONFIGURATION
VHDL dispose d'un mécanisme appelé "configuration" permettant d'associer une
instance de composant à un couple entité/architecture. La configuration est une
unité de compilation à part, tout comme l'entité et l'architecture. Pour la plupart
des outils , la configuration est optionnelle. Le lien composant/entité s'effectue
généralement en imposant un nom de l'entité identique à celui du composant, et le
lien entité/architecture s'effectue soit en considérant la dernière architecture
compilée, soit l'outil impose une architecture unique.

La configuration est donc l'unité de compilation de plus haut niveau car elle permet
l'élaboration de l'ensemble (l'édition de liens) pour pouvoir simuler. Son
intérêt apparaît pour gérer des gros circuits où plusieurs architectures sont
possibles pour une même entité. C'est la cas quend il existe des modèles
d'abstraction différentes, ce qui est très utilisé pour les méthodes de conception
"top down" . Par exemple une équipe peut avoir des modèles abstraits de tous les
blocs de façon à accélérer les temps de simulation et travailler sur plusieurs
architectures du bloc à concevoir.

Il existe plusieurs façons de configurer un projet :

 configuration hiérarchique
 configuration à plat
 configuration immédiate
 instanciation de configuration

Configuration hiérarchique

Pour chaque entité, une configuration est créée. Dans chaque configuration, la
clause use configuration est utilisée pour les instances de composant de niveau
inférieur.

configuration CF_AND2 of AND_2 La configuration est vide car l'entité AND_2 n'a pas d'in
is
for arc La configuration existe toutefois car tous les composant
end for; cette méthode.
end configuration CF_AND2;

configuration CF_AND3 of AND_3 Le composant AND2 est instancié dans AND_3. A chaq
is
for arc utilisée.  
for all : AND2
use configuration                                          
work.CF_AND2;
end for;
end for;
end configuration CF_AND3;
Pour les gros circuits, il est fortement recommandé d'utiliser ce type de
configuration car c'est la plus simple à maintenir.

Configuration à plat

Elle peut être unique et indique les liens entité/architecture explicitement avec la
clause use entity(architecture). Elle est utilisée pour des circuits simples car il n'est
pas nécessaire d'avoir des fichiers de configuration au niveau hiérarchique
inférieur.

configuration CF_AND3_APLAT of AND_3 is


for arc
for i1 : AND2
use entity work.AND2(a1);
end for;
for i2 : AND2
use entity work.AND2(a2);
end for;
end for;
end configuration CF_AND3_APLAT;

Configuration immédiate

Les associations entité/architecture des composants peuvent être déclarés


directement dans l'architecture avant le corps de l'architecture. Elle est réservée
aux petits circuits pour des essais de différentes architectures.

architecture arc of TOP is


for all : AND2
use configuration work.CF_AND2;
end for;
for inst_block1 : compo
use entity work.ctr(arc);
end for;
begin
I1 : AND2 port map (...);
I2 : AND2 port map (...);
inst_block1 : compo port map (...);
end arc;

Instanciation de configuration

Dans ce cas , il n'est pas nécessaire de déclarer de composant et l'instanciation se


fait en spécifiant le couple entité/architecture. La conception dans ce cas est
nécessairement "bottom-up" et non "top-down" car il faut obligatoirement avoir
concçu les entités appelées au niveau le plus haut.

architecture arc of Cette méthode ne nécessite pas de configuration.


TOP is
begin Elle est la plus simple mais peut être aussi moins
I1 : entity adaptée aux gros circuits du fait de la conception
work.and2(arc1) port "bottom up" uniquement.
map (...);
I2 : entity
work.ctrl(arch) port
map (...);
end arc;
Back to Top

EXERCICES
Nous avons vu :

 comment déclarer un composant


 comment instancier un composant
 comment instancier un composant générique
 comment les relier (en utilisant la clause port map et generic map)

Faisons tout de suite des exemples !

Exercices:

1. Codez un additionneur générique de 2 nombres non signés sur n bits. 


Afficher la réponse

library ieee; Notez l'utilisation de la bibliothèque IEEE,


use ieee.std_logic_1164.all;
use ieee.numeric_std.all; permettant d'utiliser le type std_logic
-- (paquetage std_logic_1164 et le sous type
entity GEN_ADD is unsigned (paquetage numeric_std).
generic
(
n : postive
:=8
);
port
(
A : in
unsigned(n-1 downto 0);
B : in
unsigned(n-1 downto 0);
S : out
unsigned(n-1 downto 0)
);
end GEN_ADD;
--
architecture RTL of GEN_ADD
is
--
begin
--
S <= A + B;
--
end RTL;
--
--

2. Codez un registre générique de n bits.   Afficher la réponse

library ieee; Notez l'utilisation de la bibliothèque IEEE,


use ieee.std_logic_1164.all; permettant d'utiliser le type std_logic
use ieee.numeric_std.all;
-- (paquetage std_logic_1164 et le sous type
entity GEN_REG is unsigned (paquetage numeric_std).
generic
(
n : integer :=8
);
port
(
CLK : in
std_logic;
NRST : in
std_logic;
EN : in
std_logic;
D : in
unsigned(n-1 downto 0);
Q : out
unsigned(n-1 downto 0)
);
end GEN_REG;
--
architecture RTL of GEN_REG
is
--
begin
--
SYNC : process(CLK,NRST)
begin
if NRST='0' then
Q <= (others =>
'0');
elsif (CLK'event and
CLK='1')then
if en='1' then
Q <= D;
end if;
end if;
end process sync;
--
end rtl;
--

3. Codez en VHDL structurel un accumulateur n bits à l'aide du registre


générique et de l'additionneur générique. Afficher la réponse

library ieee; Notez l'utilisation de


use ieee.std_logic_1164.all;
use ieee.numeric_std.all; la bibliothèque IEEE,
-- permettant d'utiliser
entity ACCU is le type std_logic
port
(paquetage
(
CLK : in std_logic; std_logic_1164 et le
NRST : in std_logic; sous type unsigned
EN : in std_logic; (paquetage
D : in unsigned(7 downto 0);
Q : out unsigned(7 downto 0) numeric_std).
);
end ACCU;
--
architecture RTL of ACCU is
--
constant N : integer := 8; --default value
signal Q_int : unsigned(7 downto 0);
signal D_int : unsigned(7 downto 0);
--
--
begin
--
INST_REG : entity work.GEN_REG(RTL) generic map
(N=>n) port
map(CLK=>CLK,NRST=>NRST,EN=>EN,D=>D_int,Q=>Q_int);
INST_ADD : entity work.GEN_ADD(RTL) generic map
(N=>n) port map(A=>D,B=>Q_int,S=>D_int);
Q <= Q_int;
--
end RTL;
--

4. Ecrivez un testbench simple pour tester l'accumulateur par


simulation.Afficher la réponse

--------------------------------------------------------------------------
-----
--
--
-- FILE : ACCU_TEST.VHD
--
-- Related files : ACCU.VHD
--
--
--
-- Author(s) : J-L DANGER
--
--
--
-- Project : TUTORIEL METHODE
--
--
--
--
--
-- Description : testbench du composant accu
--
--
--
-- Copyright GET-ENST 2004
--
--
--
--------------------------------------------------------------------------
-----
-- Modifications :
--
--------------------------------------------------------------------------
-----
--
library ieee;
use ieee.std_logic_1164.all;
use ieee.numeric_std.all;
--
use std.textio.all;
--
--use WORK.ACCU_PACK.ALL;
--
entity ACCU_TEST is
end ACCU_TEST;
--
--
architecture ARCHI of ACCU_TEST is
--
constant N : integer :=8;
signal CLK : std_logic;
signal N_RESET : std_logic;
signal EN : std_logic;
signal D : signed(N-1 downto 0);
signal Q : signed(N-1 downto 0);
file VECT : text open READ_MODE is "./simu/accu_test_in.dat";
file EXP : text open READ_MODE is "./simu/accu_test_exp.dat";
file RESULT : text open WRITE_MODE is "./simu/accu_test_out.dat";
--
--
--
begin
--
--
--
EN <= '1';
--
---------------------------------------------------------------
-- Processus d'horloge
---------------------------------------------------------------
horloge :process
begin
CLK <='1';
wait for 50 ns;
CLK <= '0';
wait for 50 ns;
end process horloge;
--
---------------------------------------------------------------
-- Processus du RESET
---------------------------------------------------------------
RESET : process
begin
N_RESET <= '0';
wait until CLK'event and CLK='0';
N_RESET <= '1';
wait;
end process RESET;
--
---------------------------------------------------------------
-- Processus de génération des données à partir d'un fichier
---------------------------------------------------------------
DATA :process
variable L1 : line;
variable L2 : line;
variable L3 : line;
variable D_in : integer;
variable D_exp : integer;
variable D_out : integer;
begin
write(L3,string'(" temps entrée attendu resultat"));
write(L3,LF);
writeline(RESULT,L3);
loop
wait until CLK'event and CLK='0';
readline(VECT,L1);
read(L1,D_in);
readline(EXP,L2);
read(L2,D_exp);
D <= to_signed(D_in,N);
write(L3,now,field=>10);
write(L3,D_in,field=>8);
write(L3,D_exp,field=>8);
write(L3,to_integer(Q),field=>9);
if to_integer(Q) /= D_exp then
write(L3,string'(" => erreur à cette ligne"));
end if;
writeline(RESULT,L3);
assert (to_integer(Q)=D_exp) report "comparaison fausse" severity
failure;
assert (endfile(VECT)=FALSE) report "fin de simulation" severity
failure;
end loop;
end process;
--
---------------------------------------------------------------
-- instanciation de ACCU
---------------------------------------------------------------
--
INST_ACCU : entity WORK.ACCU(RTL) port map (CLK=>CLK,NRST=>N_RESET,EN=>EN,
D=>D,Q=>Q);
--
end ARCHI;

5. Compilez les circuits et simulez le tesbench avec ModelSim. Déboguez si


nécessaire

Back to Top

EN RÉSUMÉ
On a vu dans ce chapitre comment déclarer, instancier et relier les composants
entre eux.
L'objectif du prochain chapitre est de savoir comment décrire le contenu d'un
composant, son fonctionnement

PRÉAMBULE
Objectifs

Passons maintenant aux descriptions comportementales. Dans ce type de


descriptions, il y a plusieurs niveaux. 

Nous allons ici étudier les principales descriptions : 

 celle au niveau des équations booléennes, appelée aussi parfois flot de données.
Ce type de représentation modélise les circuits combinatoires, non pas en
instanciant des portes, mais sous forme d'équations booléennes.
 Pour les circuits séquentiels, il faudra faire appel à un niveau plus abstrait, appelé
RTL.

Les objectifs de ce chapitre sont de comprendre :

 Les différences entre instructions concurrentes et séquentielles.


 Les processus et les différences entre variables et signal.
 Les structures de contrôle utilisées dans les processus.
 Les affectations concurrentes des signaux, les raccourcis d'écriture.
 Les différents types de délais dans les affectations.
 Comment vérifier une propriété par les assertions.
 Les différents types de sous-programmes.

Plan du chapitre

 Instructions concurrentes et séquentielles


VHDL utilise aussi bien les 2 types pour coder le comportement d'un circuit. Quelle
différence ?

 Processus et synchronisation
Comment démarrer un processus ?

 Variables et signaux
Comment utiliser ces 2 types d'objets dans un processus ?
 Stuctures de contrôle
Là où vhdl rejoint le C.

 Affectations concurrentes
Comment écrire une équation sans passer par un processus ?

 Affectations et délais
Pour modéliser le temps. Attention, piège.

 Assertions
Comment vérifier une propriété et envoyer un message associé en cours de
simulation ?

 Sous-programmes
Comment créer des fonctions et des procédures ?

 Fonctions de résolution
Quelle valeur prend un signal affecté dans plusieurs processus ?

 Résumé

Back to Top

INSTRUCTIONS CONCURRENTES ET
SEQUENTIELLES
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, comme c'est la cas du C.

VHDL offre cependant la possibilité d'utiliser des instructions séquentielles, plus


naturelles pour l'homme, par le biais de processus process. Les processus peuvent
avoir leurs propres variables locales variable . 

Les objets manipulés par les instructions concurrentes sont les signaux signal qui
disposent chacun d'un échéancier de façon à effectuer une simulation d'instructions
concurrentes sur une machine séquentielle (l'ordinateur).

Il existe 3 principales instructions concurrentes :

1. Les processus, qui offrent la possibilité d'utiliser des instructions séquentielles.


2. Les instanciations de composants (étudiées aux chapitre précédent à propos du
VHDL structurel)
3. les affectations concurrentes de signaux, qui peuvent être conditionnelles

Il existe également les assertions et les procédures concurrentes qui ont la même syntaxe
que les assertions et procédures séquentielles (étudiées en fin de ce chapitre) mais
utilisées en dehors  des processus.
Back to Top

PROCESSUS ET SYNCHRONISATION
 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'instruction
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  é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 mais par le
biais de leur échéancier qui est mis à jour en fin de processus avec la nouvelle
valeur et le temps d'affectation qui correspond à un delta-cycle après le signal
ayant réveillé le processus.

Synchronisation des processus par WAIT


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] où
S1 et S2 sont des signaux, CONDITION est une expression générant un booléen, et DUREE
est le temps physique d'attente.

L'instruction WAIT n'est pas synthétisable avec la condition de durée. Elle est très utile
pour les testbench pour générer précisément des formes de vecteurs d'entrée.
L'exemple suivant génère une trame de 10 impulsions espacées de 100 microsecondes.

trame : process -- le processus peut avoir une étiquette, ici "trame"

-- il n'y a pas de liste de sensibilité donc il faut des "wait"

pulse <= '0'; -- pulse est à 0 en début de trame


for i in 0 to 9 loop -- on génère 10 impulsions larges de 2 périodes
d'horloge

wait until clk'event and clk='1';

pulse <= '1';

wait until clk'event and clk='1'

pulse <= '0';

end loop;

wait for 100 us; -- après 100 us on reprend le process donc pulse
va repasser à 0

end process;

Exercice :

Ecrivez un processus permettant de générer un train d'impulsions toutes les


microsecondes : pas d'impulsion, puis une impulsions de100 ns puis une impulsions
de 200ns, jusqu'à 1 microseconde comme le montre la figure ci-dessous.

Afficher la réponse

Synchronisation des processus par liste de sensibilité


La liste des signaux réveillant le processus est indiquée dans la liste de sensibilité suivant
cette syntaxe : process(liste de sensibilité). Cette  méthode est tout à fait équivalente à
celle consistant à utiliser un wait on liste à la fin du corps du processus.
L' exemple suivant permet de coder la logique combinatoire.

process(x,y)

begin

if x='1' then
z<=y;

else

z<='0';

end if;

end process;

--

De quelle fonction s'agit-il ? Afficher la réponse

Notez la présence dans la liste de sensibilité des entrées uniquement.

Attention un processus correspondra à de la logique combinatoire seulement si :

  la liste de sensibilité contient TOUTES les entrées, sinon la fonctionnalité n'est pas
respectée car l'entrée omise  ne réveille pas le processus
  Il faut traiter TOUS les cas dans le chemin de contrôle, sinon il y a implicitement
mémorisation de la sortie pour les cas non traités

Si ces règles ne sont pas respectées, le processus est séquentiel.

Exercices :

 Que se passe t'il si y est omis de la liste de sensibilité ? Afficher la réponse

 Que se passe t'il si la ligne else z <='0' est omise ? Afficher la réponse

 Codez la fonction multiplexeur à 2 entrées :


o avec les opérateurs logiques Afficher la réponse

o avec un processus combinatoire Afficher la réponse

Pour fiabiliser le processus séquentiel , nous avons vu qu'il était fortement


recommandé de fonctionner en mode synchrone, c'est à dire avec l'utiliation d'un
horloge qui échantillonne le calcul. Autrement dit un processus séquentiel
synchrone a une liste de sensibilité qui se réduit simplement à l'horloge. Les
signaux codés dans ce processus seront donc des sorties de bascule D.
S'il est impératif d'initialiser les bascules (cas des machines à états), le reset
asynchrone peut être employé. Dans ce cas il faut le rajouter dans la liste de
sensibilité car il est prioritaire sur l'horloge quand il est actif. La syntaxe générique
d'un bloc de logique séquentiel est donc la suivante:

process(clk, n_reset)

--

begin

if n_reset='0' then

sortie <= (others=>'0'); -- tous les bits de sortie sont initialisés à 0 par le reset

elsif clk'event and clk='1' then

liste d'instructions codant "sortie"

end if;

end process;

--

Exercice : 

 Donnez une description d'une bascule D possédant un reset synchrone (qui remet la
bascule à zéro s'il est à l'état bas lors d'un front montant de l'horloge)
[Afficher la réponse]

Back to Top

SIGNAUX VS VARIABLES
Les signaux sont équivalents à des variables globales assurant les communications
entre processus. Ils sont équivalents à des équipotentielles si le code est
synthétisable. Les signaux ne sont pas mis à jour tout de suite mais à la fin du
processus avec un delta-cycle de retard par rapport au signal ayant déclenché le
processus. Les signaux sont affectés avec l'instruction <= qui se dit aussi "reçoit"
plutôt que "égal" car le signal va recevoir cette valeur en fin de processus avec un
delta-cycle de retard.   

Les variables sont locales à chaque processus et sont mises à jour immédiatement.
Elles sont très utiles pour effectuer un codage séquentiel classique comme avec le
langage C. Les variables sont déclarées juste avant le corps du processus et sont
affectées avec l'instruction d'affectation immédiate := de façon à bien ne pas
confondre avec l'instruction <= "reçoit" pour les signaux. Les variables gardent leur
valeur quand le processus est terminé.

Dans l'exemple suivant, a,b,c sont des signaux et x une variable. Enfin de
processus, a et b vont prendre la valeur a+1 après un delta-cycle alors que c
prendre la valeur a après un delta-cycle.

process (a)

variable x : std_logic;

begin

x := a+1;

a <= a+1;

b <= x;

c <= a;

end if;

end process;

--
Exercice: Ecrivez un processus qui effectue la multiplication de 2 signaux a et b. Si le
résultat de la multiplication dépasse le seuil c alors la sortie prend la valeur c. Afficher la
réponse]

Back to Top

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)

Instruction IF  
L' instructions IF reposent 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 du IF:

if condition1 then instruction;

[elsif condition2 then instruction;]

...

[else instruction;]

end if;

Pour coder un processus combinatoire, l'utilisation du ELSE est obligatoire de


façon à traiter toutes les combinaisons (sinon il y a mémorisation donc c'est da la
logqiue séquentielle)

Instruction CASE
L' instructions CASE reposent 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)

Instruction 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'excution peut être altérée avec les clauses next et exit

 Exemple : la boucle loop de cet exemple permet de compter le nombre de bits à 1


d'un signal.

library ieee; Le paquetage IEEE.numeric.std permet d'avoir accès


use ieee.std_logic_1164.all;
use ieee.numeric_std.all; fonction arithmétiques sur les types vecteurs non-sig
-- unsigned ou signés signed.
entity nb_un is
port
( Remarquez que l'addition se fait en concaténant cha
a : in unsigned(7 downto 0); avec '0' car l'opérateur + du paquetage n'opère pas
s : out unsigned(3 downto 0)
); bit simple.
end entity;
--
architecture rtl of nb_un is
begin
process(a)
variable x : unsigned (3 downto 0);
begin
x:= (others => '0');
for i in 0 to 7 loop
x := x + ('0' & a(i));
end loop;
s <= x;
end process;
end rtl;

 Exercice : Ecrivez un processus calculant la parité d'un signal sur n bits.

Afficher la réponse]

Back to Top

AFFECTATIONS CONCURRENTES DE SIGNAUX


Ces affectations sont des instructions concurrentes au même titre que les processus et les
instanciations de composants. Le codage comportemental d'une architecture repose sur
l'utilisation  de ces 3 types d'instructions concurrentes qui peuvent apparaître dans
n'importe quel ordre.
Une instruction concurrente est équivalente à un processus et correspond à un raccourci
d'écriture pour éviter le verbosité du processus.
Exemple :

Architecture avec processus Architecture avec affectation concurrente

architecture arc of adder is


architecture arc of adder is
begin
begin
process(A,B,Cin)
S <= A xor B xor Cin;
begin
end arc;
S <= A xor B xor Cin;

Affectation concurrente conditionnelle

Il existe également des instruction concurrentes conditionnelles  permettant d'effectuer


des raccourcis d'écriture pour remplacer des processus simples à base de  IF et CASE
Exemples :

Architecture avec processus Architecture avec affectation concurrente

architecture arc of adder is architecture arc of adder is


begin begin
process(A,B,Cin) S <= B xor Cin when A = '0' else
begin
if A = '0' then
S <= B xor Cin;
elsif B = '0' then
S <= not(Cin); not Cin when B = '0' else
else Cin;
S <= Cin;
end if;
end process;
end arc;
architecture arc of MUX is
begin
process(SEL)
begin
case SEL is architecture arc of MUX is
when 0 => begin
sortie <= A; with SEL select
when 1 => sortie <= A when 0,
sortie <= B; B when 1,
when 2 => C when 2,
sortie <= C; D when others;
when others => end arc;
sortie <= D;
end case;
end process;
end arc;

Pour les fonctions combinatoires, il est souvent plus prudent d'utiliser les affectations
concurrentes plutôt que les processus où il est possible d'omettre un signal d'entrée dans la
liste de sensibilité.
Il faut toutefois avoir un chemin de contrôle exhaustif et toujours avoir la clause else ou
when others pour ne rien omettre.

Back to Top

AFFECTATIONS ET DÉLAIS
VHDL permet de spécifier des délais dans les affectations. 

Il existe deux types d'affectations avec délai :

 affectation avec délai inertiel :  x <= 3 after 2 ns; ou plus explicitement x <=
inertial 3 after 2 ns;
 affectation avec délai de transport : x = transport 3 after 2 ns;

Le type inertiel permet de "filtrer" les variations de X trop courtes par rapport au délai de
la transaction. Par exemple si x est à 1 pendant 1ns, l'affectation x <= 3 after 2 ns; ne
changera pas la forme d'onde de x qui restera à 0. Contrairement au mode inertiel , le
type transport n'opère pas de  "réjection des parasites". Il respecte les temps de
propagation mais est certainement moins réaliste que le mode inertiel.

Back to Top

ASSERTIONS
Les assertions permettent de vérifier une propriété et générer un message d'erreur si cette
propriété n'est pas vérifiée.
La syntaxe est la suivante :
assert CONDITION
[report MESSAGE]
[severity NIVEAU];

CONDITION =  condition générant un booleén. Cette condition doit être vraie pour que
rien ne se passe
MESSAGE = chaîne de caractères renseignant le fait que la condition n'a pas été remplie
NIVEAU = niveau d'erreur. Ilen existe 4 prédifinis : NOTE,WARNING, ERROR,
FAILURE

Les assertions peuvent font aprtie du domaine séquentiel (dans un processus) ou du


domaine concurrent

exemple :

assert NOW < 10 ms -- NOW est une fonction renvoyant le temps physique
    report "Fin de simulation"
    severity failure

Back to Top

SOUS-PROGRAMMES
IL existe 2 types de sous-programmes:

1. Les fonctions
2. Les procédures
Les sous-programmes font partie du domaine séquentiel et ont la même utilité que pour
les autres langages de programmation : regrouper les instructions qu'on utilise souvent. Les
procédures font peuvent être appelées aussi bien dans le domaine séquentiel que
concurrent
La différence entre fonctions et procédures sont les suivantes:

fonction procédure

paramètres
non modifiable (entrées) les sorties sont modifiables
d'appels

valeur
oui non
retournée

function F(A: unsigned(3 procedure P(A: in unsigned(3


downto 0)) downto 0))
return std_logic is             S : out std_logic ) is
syntaxe ... ...
begin begin
... ...
end function F; end procedure P;

 
Par rapport à la fonction, La procédure permet d'avoir plusieurs sorties, mais à condition
de déclarer les entrées et les sorties comme dans une entité ou composant.  
Exemples :

Fonction de calcul de minimum et maximum en procédure.

procedure MinMax( a,b : in unigned (7 downto 0);


min : out unsigned(7 downto 0);
max : out unsigned(7 downto 0)) is
begin
if (a < b) then
min <= a;
max <= b;
else
min <= b;
max <= a;

end if;
end procedure MinMax;
MinMax(x, y, z, t);

Il faut 2 fonctions min et max pour avoir léquivalent en fonction. Voici l'exemple
de min :

Exercices :

function min (a,b : unsigned(7 downto 0)


return unsigned(7 downto 0) is 
varaiable min : unsigned(7 downto0);
begin
if (a < b) then
min := a;
else
min := b;
end if;
return min;
endfunction

...

z := min(x, y);

Trouvez le code de la fonction comptant le nombre de bits à 1 dans un nombre de 8


bits.

Back to Top

EN RÉSUMÉ
Ce chapitre vous a présenté les façons de décrire la fonctionnalité des processus,
c'est-à-dire leur description comportementale.

Les descriptions comportementales ne s'opposent pas aux description structurelles.


Elle se complètent : on adopte une description structurelle pour séparer un bloc en
sous-systèmes simples, qui eux seront décrits comportementalement.

 pour de la logique combinatoire ayant une expression simple : affectation


concurrente
 pour le la logique séquentielle, ou combinatoire ayant une expression complexe : 
processus 

Les processus s'exécutent en parallèle les uns des autres, mais leur déroulement
interne est séquentiel. Ils peuvent utiliser la plupart des structures de contrôle du
C.

Les processus always s'exécutent en boucle, et nécessitent donc un point d'arrêt


pour que le temps puisse s'écouler. Il en existe trois :

 wait
  listes de sensibilité

L'objectif du prochain chapitre est de faire la différence entre les constructions


synthétisables, et celles spécifiques à la simulation.
.

PRÉAMBULE
Objectifs

Plutôt que de long discours, voici quelques exemples de modules écrits en VHDL.
Chaque exemple introduit un nouveau type de construction / type de donnée.

Pour chaque exemple, passez la souris sur le code pour avoir des explications sur la
façon dont il est construit.

Plan du chapitre

 combinatoire : AND3
 additionneur complet 1 bit
 additionneur complet 4 bit
 bascule D
 compteur 8 bits
 registre à décalage
 simulation
 Résumé

Back to Top

AND3
On va voir

 structure d'un module simple


 affectations concurrentes 

Commençons par le plus simple des modules ou presque : une porte combinatoire
AND à trois entrées. En VHDL, les modules sont appelés "entités" (mot-clef entity). 
Voici une description possible d'une porte AND à 3 entrées.

Passez la souris sur les différents éléments du code pour avoir des explications sur
la signification de chaque instruction

ADDITIONNEUR COMPLET 1 BIT


Nous allons voir

 les bibliothèques 
 l'utilisation de signaux intermédiaires
 La concaténation de signaux pour créer un bus 

Voici une description possible d'un additionneur complet 1 bit.

Rappel : un additionneur complet 1 bit a trois entrées (a, b, et cin la retenue


entrante), et deux sorties (le bit de somme s et le bit de retenue sortante cout).
ADDITIONNEUR 4 BITS
On va voir

 comment instancier un composant (ou comment faire du schéma en textuel) 


 comment relier des composants entre eux 

VHDL, comme tous les HDL, permet de décrire les système sous forme structurelle.
C'est-à-dire, au lieu de décrire un gros système dans un seul gros fichier, de le
séparer en sous-systèmes (sous-modules), eux-mêmes subdivisés en sous-modules,
jusqu'à obtenir des modules facilement compréhensibles.

Nous allons ici décrire un additionneur 4 bits en assemblant des additionneurs 1 bit.
On aurait aussi pu décrire la fonctionnalité de l'additionneur 4 bits (par opposition
à sa structure), cela sera vu à la fin de cet exemple-ci.

On suppose ici que le code de l'additionneur 1 bit vu précédement est disponible


dans un fichier à part.

You might also like