VHDL - Syntaxe et règles
Arthur Garreau 8 oct 2015 19:12:21 CEST - GNU Free Documentation License
remarques :
- Le document peut présenter des erreurs ou inexactitudes.
- Les éléments mis entre crochets sont optionnels, ils peuvent être omis sans qu’il y ait d’erreur de syntaxe.
Sommaire
- introduction et définition
- Structure du langage
- Style de description
- Bibliothèques
- Les commentaires
- Les types
- Créer ses propres types, Syntaxe des types, Déclaration de signaux
- Les signaux
- Les ports
- Les entités
- Les architectures
- Les opérateurs
- Description flot de données, instructions concurrentes
- Description comportemental et instructions séquentielles
- Résume, instructions concurrentes, séquentielles & types de description
- Description Structurelle
- Sources
Introduction et Définition
Avant de commencer rien de mieux qu’une petite définition wikipédia pour se rappeler ce qu’est le VHDL :
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 VHSIC1 Hardware Description Language.
Le VHDL est standardisé par IEEE (Institue of Electrical and Electronics Engineers).
Le VHDL n’est pas un langage de programmation, son objectif est de décrire le comportement de matériel et de l’implémenter sur des composants programmables qui n’exécutent pas d’instructions.
Le VHDL est composé de :
- commentaires
- délimiteurs
- identificateurs
- expressions
- mots-clés
- littéraux (objets : signaux, ports, variables, constantes) (ex : 67 est un entier, 0 est un bit, “chaine” est une chaine de caractères …)
- VHDL est insensible à la casse, mais généralement on écrit les mots réservés en MAJUSCULE
Structure du langage
Il existe “5 unités de compilation” permettant de décrire des composants :
- l’entité : description du composant en terme d’entrées et de sorties, comme une boîte noire
- l’architecture : décrit l’intérieur du composant (de l’entité)
- la déclaration de paquetage : un paquetage est une collection d’objets réutilisables (constantes, types, composants, procédures)
- le corps de paquetage
- la configuration indiquant quelle architecture utiliser pour chaque entité.
résumé : Chaque composant est réprésenté par une entité (boîte noire), son fonctionnement est décrit dans une architecture. Chaque entité peut avoir plusieurs architectures.
Un ou plusieurs composants peuvent composer un paquetage réutilisable dans d’autres programmes VHDL.
Style de description
Il existe 3 types de description pour décrire un système matériel. Tous les styles peuvent être utilisés dans une même programme VHDL, cependant les styles n’acceptent pas les mêmes instructions :
- le style comportemental permet de décrire une fonctionnalité complexe de manière algorithmique comme dans un langage de programmation en y ajoutant la dimension temporelle. Les instructions sont “exécutées” séquentiellement. On utilise le concept de proccesus.
- le style flot de données permet de décrire les chemins de données et les équations logiques sur ces données. Les instructions sont concurrentes (parallèles).
- le style structurel permet de décrire un système sous la forme d’une interconnexion de composants (paquetage) vus comme des boîtes noires.
Bibliothèques
Les bibliothèques sont déclarées avec le mot clé library
, elle dépendent des outils utilisés.
LIBRARY nom_bibliothèque;
Elle contiennent des paquetages que l’on déclare vouloir utiliser avec le mot clé use
use BIBLIOTHEQUE.PAQUETAGE.all;
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,…).
Par défaut, les outils considère que l’on utilise les bibliothèques STD et WORK. Il y a donc implicitement :
library STD;
library WORK;
(Remarque : library signifie bibliothèque et non librairie : bookstore en anglais).
Les paquetages de la bibliotèques IEEE
En général, on utilise la bibliothèque IEEE
qui permet l’utilisation des fonctions logiques et normalisées (notamment std_logic
).
En résumé, on pourra être amené à faire quelque chose comme :
library IEEE;
use IEEE.std_logic_1164.all;
use IEEE.std_logic_textio.all;
use IEEE.std_logic_arith.all;
use IEEE.numeric_bit.all;
use IEEE.numeric_std.all;
use IEEE.std_logic_signed.all;
use IEEE.std_logic_unsigned.all;
use IEEE.math_real.all;
use IEEE.math_complex.all;
En réalité, on utilise principalement trois de ces paquetages :
std_logic_1164
définit les types, les fonctions de conversion, les opérateurs logiques et les fonctions de recherches de frontsrising_edge()
etfalling_edge()
.numeric_bit
définit les opérateurs arithmétiques agissant sur desbit_vector
interprétés comme des nombres entiers.numeric_std
définit les opérateurs arithmétiques agissant sur desstd_logic_vector
interprétés comme des nombres entiers.
Les commentaires
En vhdl pour écrire des commentaires on utilise deux tirets : --
Les types
Le VHDL est un langage typé où il est obligatoire de spécifier le type des objets/signaux utilisés.
Il existe des types :
- scalaire
- entier:
integer
prédéfini dans le paquetage STD permet de définir des nombres signés sur 32 bits entre -2^-31 et 2^31 - 1. Il existe 2 “sous types”subtype
associés àinteger
:natural
positive
- En général, on spécifie le
range
pour restreindre le nombre de bits qu’occupe un nombre.
- Les paquetages
numeric_std
etnumeric_bit
définissent deux types correspondant à l’utilisation d’entiers sous forme de tableau destd_logic
etbit
. Les deux types ont les mêmes noms dans les deux modules, ainsi on ne peut pas utiliser ces deux modules en même temps dans un même programmeUNSIGNED
: nombre entier non signé (positif)SIGNED
: nombre entier signé
- réel :
REAL
permet de représenter des nombres entre -1.0E+38 à 1.0E+38. Il n’est pas synthétisable (c-à-d qu’il ne peut pas être représenté en matériel). - physique : le VHDL permet de définir des types physiques pour représenter une grandeur physque, comme le temps, la tension… Un type physique est la combinaison d’un type entier et d’un système d’unité.
TIME
, est le seul type physique prédéfini (cf. remarque 3)
- énuméré : Un type énuméré est un type défini par une énumération exhaustive. Un signal de ce type prendra une certaine valeur de ce type et rien d’autre.
- syntaxe :
TYPE nom_enum IS (valeur1, valeur2, ...);
- dans le paquetage
STANDARD
deSTD
on trouve :type boolean is (FALSE, TRUE);
pour les booléentype bit is ('0', '1');
– noter que ‘0’ et ‘1’ sont des caractères entre apostrophes.type severity_level is (NOTE, WARNING, ERROR, FAILURE);
type character is ( NUL, SOH, STX, ETX,..., '0','1', ...);
pour les caractères.
- dans le paquetage
STD_LOGIC_1164
deIEEE
type std_ulogic is ('U', 'X, 'O', '1', 'Z', 'W', 'L', 'H', '-');
avec : * 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é. *std_logic
est un sous-type destd_ulogic
et est plus(+) utilisé. Il a un attribut en plus : il est résolu s’il y a plusieurs pilotes/sources (cf. remarque 4). * L’ordre de déclaration est important. Lors de l’initialisation d’un signal de type enuméréstd_ulogic
, le signal prend la valeurstd_ulogic'LEFT
(cf. attributs), c’est-à-dire ‘U’.
- syntaxe :
- entier:
- composite
- tableau (ou
array
) est une collection d’objets de même type, indexés par des entiers ou des énumérés. On trouve :- dans la bibliothèque
STD
STRING
permet de définir les chaînes de caractères. Il est définit comme un tableau d’éléments de typeCHARACTER
.bit_vector
: tableau de bits
- dans le paquetage
STD_LOGIC_1164
deIEEE
on trouve :std_logic_vector
permet de déclarer des groupements de signaux de typestd_logic
.std_ulogic_vector
: même chose questd_logic_vector
mais avec des signaux de typestd_ulogic
- dans la bibliothèque
- enregistrement permet de définir des collections de valeurs de types différentes. On utilise le mot clé
RECORD
. Les enregistrements 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 typeRECORD
et ne changent pas. En cas de modification il suffit de modifier le contenu du typeRECORD
plutôt que de modifier les ports dans les entités
- tableau (ou
- fichier :
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 ouTESTBENCH
- le paquetage
TEXTIO
de la bibliothèqueSTD
définit un type fichier texteTEXT
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;
- le paquetage
- pointeur : Les pointeurs sont peu utilisés en VHDL car on préfère les tableaux indicés qui peuvent être synthétisables, à la différence des pointeurs.
ACCESS
FIN TYPES
Remarques
remarque 1
Un sous type subtype
permet de déclarer un type héritant des propriétés du type père.
Exemples, déclaration de natural
et positive
:
subtype natural is integer range 0 to integer'high;
subtype positive is integer range 1 to integer'high;
Notez que
range
permet d’indiquer l’intervalle'HIGH
indique la plus grande valeur du typeINTEGER
, c’est un attribut de type
remarque 2
Les types prédéfinis reconnaissent six objets scalaires :
- Bit : représente un élément binaire avec les valeurs ‘0’ et ‘1’ ;
- Bit_vector : représente un tableau d’éléments binaires ;
- Boolean : représente une donnée pouvant être TRUE ou FALSE ;
- Real : représente un nombre réel ;
- Integer : est un entier de 32 éléments binaires ;
- Character : représente un charactère ASCII.
remarque 3
Syntaxe
Définition du type TIME
:
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;
remarque 4
Un signal multisources doit être associé à une fonction de résolution. C’est un mécanisme de résolution qu’offre VHDL et qui permet de résoudre les conflits susceptibles de survenir et de calculer la valeur résultante de ce conflit (on dit la valeur résolue du signal).
Un signal possédant une fonction de résolution est appelé signal résolu. Il peut donc être la cible de plusieurs affectations différentes.
remarque 5 : exemple lecture et écriture dans un fichier
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;
FIN REMARQUES
Les attributs
Les attributs sont des caractéristique (variables ou fonctions) de types ou d’objet qu’il est possible d’utiliser dans le modèle.
Il est possible de créer ces propres attributs. Certains outils de synthèse en tirent profit pour passer des arguments de synthèse.
Syntaxe
nom_objet'nom_attribut
Syntaxe des attributs pour le type ARRAY ou scalaire
X'HIGH
élément le plus grand ou borne maximaleX'LOW
élément le plus petit ou borne minimaleX'LEFT
élément de gauche ou borne de gaucheX'RIGHT
élément de droite ou borne de droite
Syntaxe des attributs pour les objets déclarés dans un ARRAY ou un SUBTYPE
x'RANGE
élément range de xx'REVERSE_RANGE
élément range de x dans l’ordre inversex'LENGTH x'HIGH
- x’LOW + 1 (integer)
Syntaxe des attributs pour SIGNAL
x'EVENT
retourne TRUE si x change d’étatx'ACTIVE
retourne TRUE si x a changé durant le dernier intervalx'LAST_EVENT
retourne une valeur temporelle depuis le dernier changement de xx'LAST_ACTIVE
retourne une valeur temporelle depuis la dernière transition de xx'LAST_VALUE
retourne la dernière valeur de x
Ces attributs créent un nouveau SIGNAL
x'DELAYED(t)
crée un signal du type de x retardé par tx'STABLE(t)
retourne TRUE si x n’est pas modifié pendant le temps tx'QUIET(t)
crée un signal logique à TRUE si x n’est pas modifié pendant un temps tx'TRANSACTION
crée un signal logique qui bascule lorsque x est change d’état
Créer ses propres types, Syntaxe des types, Déclaration de signaux
Maintenant que l’on a une vue d’ensemble sur les (une partie des) types existants. Intéressons nous un peu plus à leur syntaxe de déclaration, pour créer nos propres types et savoir déclarer des objets (signaux,ports…).
On utilise le mot clé TYPE
pour définir un type utilisateur auquel on peut assigner un objet. Tous les objets doivent être assignés à un type. Chaque TYPE déclaré doit être unique.
On utilise le mot clé SUBTYPE
pour déclarer des sous-types héritant des attributs du type.
Le TYPE énuméré
Syntaxe
TYPE identifier IS enumeration_type_literals;
Exemple
TYPE op_type IS (opadd, opor, opand, opxor);
TYPE letters IS ('A', 'a', 'R', 'r');
TYPE std_ulogic IS ('U', 'X', '0', '1', 'Z', 'W', 'L', 'H', '-');
Le TYPE numérique
Cette définition de type doit être dans une échelle de valeur. La base par défaut est la décimale.
Syntaxe
TYPE identifier IS RANGE implementation_defined;
Exemples
TYPE byte IS RANGE 0 to 255; -- byte varie de 0 à 255
TYPE index IS RANGE 7 DOWNTO 0; -- index varie de 7 à 0
SUBTYPE byte IS INTEGER RANGE 0 TO 255; -- byte est un entier de 0 à 255 sous-type de INTEGER
Le TYPE physique
Cette définition de type représente une quantité physique. La base des unités doit être spécifiée. Les valeurs sont calculées à partir de cette base.
Syntaxe
TYPE identifier IS RANGE implementation_defined;
UNITS
declaration_unite_de_base
[declaration_unites_secondaires]
END UNITS;
Exemple
TYPE time IS RANGE implementation_defined;
UNITS
fs;
ps = 1000fs;
ns = 1000fs;
us = 1000ns;
ms = 1000us;
sec = 1000ms;
min = 60sec;
hr = 60min;
END UNITS;
Le TYPE tableau
Le type tableau est un groupement de types identiques. Les tableaux peuvent être multidimentionels. Il existe deux sortes de tableaux :
- Les tableaux avec contrainte (Constrained Array) : Ces tableaux sont définis avec des dimensions figées (rangées colonnes).
- Les tableaux sans contrainte (Unconstrained Array) : Ces tableaux sont définis sans préciser les dimensions. Le tableau est donc dynamique. Ces tableaux doivent définir le type de leur index dans leur déclaration. La constrainte de taille est exprimée dans la déclaration de l’objet.
Syntaxe
--vhdl
TYPE nom_tableau IS ARRAY (nombre_elements) OF type_des_elements_du_tableau;
Ici nombre_element représente la taille du tableau dont l’expression dépend du type de tableau :
- Un tableau peut avoir une taille inconnue, donc non constraint, on utilise l’expression
type_index RANGE <>
- Un tableau peut avoir une taille constrainte, on utilise :
MSB downto LSB
- bit de poids faible à la case 0, bit de poids fort en têteLSB to MSB
- bit de poids fort à la case 0, bit de poids faible en tête- MSB = bit de poids fort, LSB = bit de poids faible
Exemples
type bit_vector is array (natural range <>) of bit; -- déclaration d'un tableau de bis de taille inconnue et d'index de type natural
TYPE data_bus IS ARRAY (0 TO 31) OF bit; -- tableau de 32 bit
TYPE bit4 IS ARRAY (3 DOWNTO 0) OF bit; -- tableau de 4 bit : bit4(3) à bit4(0)
TYPE dim2 IS ARRAY (0 TO 7, 0 TO 7) OF bit; -- tableau à deux dimensions
type COULEURS is (BLANC, BLEU, VERT, ROUGE, JAUNE, NOIR, ARC_EN_CIEL);
type PRIX_PEINTURES is array (COULEUR range BLANC to NOIR) of PRIX;
Utilisation
SIGNAL entree : std_logic_vector(N-1 downto 0)
SIGNAL sortie : std_logic_vector(0 to N-1)
entree
etsortie
sont des bus de N signaux de type std_logic accessible globalement ou de manière individuelle.entree(2)
permet d’accéder au troisième signal en partant du “signal de poids faible”.sortie(0)
permet d’accéder au premier signal en partant de la gauche (bit de poid fort)
le TYPE enregistrement
Les enregistrements permettent de définir des collections d’objets de types différents
Syntaxe
--vhdl
TYPE nom_enregistrement IS RECORD
obj1 : type1;
obj1 : type2;
END RECORD;
Exemple
TYPE date IS RECORD
jour : natural RANGE 1 TO 31;
mois : string;
annee : integer RANGE 0 TO 4000;
END RECORD;
Fonctions de conversions de types
Il arrive souvent de devoir faire des opérations entre des objets de types différents, or celles-ci (les opérations) requierent que les types soient les mêmes, c’est pourquoi des fonctions de conversions sont définies.
Des fonctions de conversions permettent de passer du type binaire aux types IEEE et réciproquement, ou d’un type IEEE à l’autre :
function To_bit ( s : std_ulogic; xmap : bit := '0') return bit;
function To_bitvector ( s : std_logic_vector ; xmap : bit := '0') return bit_vector;
function To_bitvector ( s : std_ulogic_vector; xmap : bit := '0') return bit_vector;
function To_StdULogic ( b : bit ) return std_ulogic;
function To_StdLogicVector ( b : bit_vector ) return std_logic_vector;
function To_StdLogicVector ( s : std_ulogic_vector ) return std_logic_vector;
function To_StdULogicVector ( b : bit_vector ) return std_ulogic_vector;
Par défaut les fonctions comme To_bit remplacent, au moyen du paramètre xmap, toutes les valeurs autres que ‘1’ et ‘H’ par ‘0’.
Conversion std_numeric vers std : numeric_std_conversion
Les signaux
Les signaux représentent des signaux logiques ou des fils dans un circuit. Les signaux peuvent aussi représenter l’état d’une mémoire. Un signal est un objet qui permet de transporter l’information. On utilise le mot clé signal
. On peut les déclarer dans les : entités, architectures et paquetages. Ils peuvent être utilisés en lecture et/ou en écriture.
Syntaxe
SIGNAL sig_name {, sig_name} : signal_type [:=initial_value];
Avec signal_type
, un des types vu précedemment.
On assigne une valeur à un signal en suivant ce format :
sig_name <= expression;
Exemple
SIGNAL entree : std_logic;
entree <= '1';
Les ports
Un port est un canal de communication dynamique entre un bloc et son environnement. En pratique les ports sont la plupart du temps utilisés dans les entités et composants pour déclarer des signaux d’interface.
Les signaux “externes” d’un composant sont appelés ports.
Syntaxe :
PORT port_name {, port_name} : mode type [:= default_value];
Il existe 5 modes :
- in : le port est accessible uniquement en lecture (correspond à une entrée)
- out : on peut assigner un valeur (écriture), le port ne peut pas être lu
- inout : lecture et écriture sont autorisées
- buffer : sortie avec possibilité de lecture
- linkage : liaison vers une entité non exprimé en termes de sémantique VHDL
Le mode par défaut, s’il n’est pas spécifié est in
.
exemple :
PORT entree1, entree2 : in std_logic;
Les entités
Une entité est une description d’un composant en terme d’entrées et de sorties, comme une boîte noire. L’entité ne s’intéresse pas au fonctionnement interne du composant, c’est le rôle de l’architecture.
Pour déclarer une entité, on utilise les mots clés ENTITY ... IS ... END;
.
ENTITY nom_entite IS
[clause de port]
END nom_entite;
Les signaux externes et visibles (entrées, sorties) d’une entité sont décrits avec la fonction port
:
port(port_interface_list);
port(signal_name {, signal_name} : mode type [:= valeur par défaut])
exemple
ENTITY nom_entite IS
port(
entree1, entree2 : in std_logic;
sortie : out std_logic;
);
END nom_entite; -- nom_entite est facultatif ici
les Architectures
L’architecture décrit le fonctionnement d’un circuit.
Elle est toujours associée à une entité. Une entité peut avoir plusieurs architectures.
Les architectures peuvent être de type combinatoires, séquentielles ou regroupant les 2 types de fonctionnement.
Syntaxe
--vhdl
ARCHITECTURE nom_arch OF nom_entite IS
[subprogram_declaration] -- zone de declaration
[subprogram_body]
[type_declarations]
[constant_declarations]
[signal_declarations]
BEGIN
--instructions, process, concurrents
END [nom_arch];
Exemple
--vhdl
ENTITY inverseur IS
port(
e : in std_logic;
s : out std_logic;
);
END inverseur;
ARCHITECTURE arch OF inverseur IS
-- dans ce cas pas besoin de déclarer des signaux, types ...
BEGIN
s <= NOT(e);
END arch;
Les opérateurs
Affectation simple
opérateur : <=
Exemple
--vhdl
C <= '1';
Concaténation
opérateur : &
Exemple
--vhdl
SIGNAL A, B : std_logic_vector(1 downto 0);
SIGNAL S : std_logic_vector(3 downto 0);
-- ...
S <= A & B;
Opérateurs logiques
- ET
AND
- NON ET
NAND
- OU
OR
- NON OU
NOR
- OU EXCLUSIF
XOR
- NON OU EXCLUSIF
XNOR
- NON
NOT
- Décalage à gauche
SLL
- Décalage à droite
SRL
- Rotation à gauche
ROL
- Rotation à droite
ROR
Opérateurs arithmétiques
- Addition
+
- Soustraction
-
- Multiplication
*
- Division
/
(opération qui n’est pas toujours supportée)
Opérateurs relationnels
- Egal
=
- Non Egal
/=
- Inférieur
<
- Inférieur ou Egal
<=
- Supérieur
>
- Supérieur ou Egal
>=
Description flot de données, instructions concurentes
Une description flot de données spécifie comment la donnée est transférée de signal à signal sans utiliser d’affectations séquentielles, elle utilise des instructions concurrentes.
Ce type de description est plus naturel au sens où l’on manipule des signaux et indiquons les chemins à suivre.
Instructions concurrentes
Les instructions sont concurrentes (parallèles), c’est-à-dire qu’elles sont évaluées en même temps ; l’ordre d’écriture n’a donc aucune importance.
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 :
- Les affectations concurrentes de signaux
- Les instanciations de composants pour la description structurale
- Les processus qui offrent la possibilités d’utiliser des instructions séquentielles
Affectation conditionnelle (instru concurrentes)
L’affectation conditionnelle permet d’affecter une valeur à un signal en fonction d’une condition.
Syntaxe
--vhdl
nom_signal <= expression WHEN condition
[ELSE expression2 WHEN condition2]
[ELSE expression];
Exemple
S <= "01" WHEN (A=B)
ELSE "10" WHEN (A=C)
ELSE "00";
Affectation sélective
L’affectation sélective permet d’affecter à un signal une valeur selon la valeur d’un autre signal dit signal de sélection.
Syntaxe
WITH signal_de_selection SELECT
nom_signal <= expression WHEN val_de_selection,
[expression2 WHEN val_de_selection2,]
[expression3 WHEN OTHERS];
--end with
Exemple
WITH E SELECT
S <= "11" WHEN "00", -- S prend la valeur 11 quand E vaut 00
"00" WHEN "01",
"10" WHEN OTHERS;
--end with
Boucle (concurrentes)
Une instruction generate
permet de dupliquer un bloc d’instructions concurrentes un certain nombre de fois, ou de créer un tel bloc si une condition est vérifiée.
Ne Pas Confondre avec FOR LOOP - séquentiel
Syntaxe
Label:FOR indice IN val_min TO val_max GENERATE
--instructions
END GENERATE;
Exemple
--Q : out std_logic_vector(2 downto 0);
--D : IN std_logic_vector(2 downto 0);
--H : IN std_logic
ET:FOR i IN 0 TO 2 GENERATE
Q(i) <= D(i) and H;
END GENERATE;
Description comportementale, instructions séquentielles
Pour décrire le comportement d’un composant, le vhdl permet de le faire est des instructions séquentielles (qui “s’exécutent” les unes après les autres). On appelle cela la description comportementale.
Les variables
Les variables sont des objets qui servent à stocker un résultat intermédiaire pour faciliter la construction d’un algorithme séquentiel.
Les variables sont utilisées dans un PROCESS, une PROCEDURE ou une FONCTION et sont affectées immédiatement.
On peut leur donner une valeur initiale. Cette valeur initiale est attribuée à chaque appel de la PROCEDURE ou de la FONCTION. Elles servent à manipuler des variables temporaires/intermédiaires pour faciliter le développement d’un algorithme séquentiel.
Syntaxe de déclaration
--vhdl
variable nom1, nom2 : type_variable [:=expression];
nom:= valeur_compatible_avec_le_type ;
Exemple
--vhdl
variable nom : integer;
Les constantes
Les constantes sont similaires aux variables à la différence que leur valeur ne peut pas changer.
Syntaxe
--vhdl
constant nom1 : type [ := valeur_constante ];
Exemple
constant nom : integer;
Process
Un PROCESS
est une déclaration concurrente dans une architecture, dont le but est de contenir des instructions séquentielles.
Les PROCESS
contiennet seulement des déclarations séquentielles qui sont évaluées dans l’ordre spécifié. On ne peut pas utiliser d’instructions concurrentes. On ne peut pas déclarer de signaux.
Le fonctionnement d’un PROCESS
est régi par les règles suivantes :
- Un processus est une boucle infine, lorsqu’il arrive à la fin du code, il recommence automatiquement
- Un processus doit être sensible à des points d’arrêt, il en existe 2 types :
- Le processus est sensible à une liste de sensibilité, c-à-d à une liste de signaux, qui réveille le processus lorsque la valeur d’un d’eux change
- Le processus a des intructions d’arrêt
WAIT
dans sa description interne
Toutes les instructions séquentielles doivent être à l’intérieur d’un PROCESS
Syntaxe
[label]: PROCESS [(sensitive_signal_name {, sensitive_signal_name})]
[constant_declarations]
[variable_declarations]
BEGIN
[sequential_statements]
END PROCESS [label];
Exemple
LIBRARY ieee;
USE ieee.std_logic_1164.all;
ENTITY nor2 IS
PORT (a,b : IN std_logic:='0';
qn : OUT std_logic);
END nor2;
ARCHITECTURE proc_behv OF nor2 IS
BEGIN
ex1: PROCESS(a,b) -- ce PROCESS est décrit avec une liste de sensibilité, quand a ou b change, process est exécuté
BEGIN
qn <= a NOR b;
END PROCESS ex1;
ex2: PROCESS -- ce PROCESS n'a pas de liste de sensibilité
BEGIN
qn <= a NOR b;
WAIT ON a,b; -- WAIT permet d'attendre un événement sur a ou b
END PROCESS ex2;
END proc_behv;
Retour sur les signaux
Contrairement aux variables, l’affectation du signal n’a pas un effet immédiat. (cf processus)
Ainsi, dans un process, après cinq instructions A<=A+1;
, le signal A n’est pas augmenté de 5 mais seulement de 1.
Affectation conditionnelle (séquentielle)
Dans un programme séquentiel (dans un PROCESS
), on peut utiliser les structures algorithmiques telles que les conditions :
Syntaxe
IF condition THEN
--instructions
ELSIF condition2 THEN
--instructions
ELSE
--instructions
END IF;
Exemple
PROCESS(H)
BEGIN
IF(rising_edge(H)) THEN
Q<=D;
END IF;
END PROCESS;
Affectation sélective (séquentielle)
(dans un PROCESS
)
Syntaxe
CASE expression IS
WHEN etat1 => instru;
WHEN etat2 => instru;
-- ...
WHEN OTHERS => instru;
END CASE;
Exemple
CASE state IS -- state est la variable du choix
WHEN "00" => state <= st1;
WHEN "01" => state <= st2;
WHEN "10" => state <= st3;
WHEN "11" => state <= st0;
WHEN OTHERS => NULL;
END CASE;
Boucle (séquentielle)
Une boucle permet de répéter des instructions séquentielles (dans un PROCESS
).
Syntaxe
FOR indice IN val_min TO val_max LOOP
--instu
END LOOP;
Exemple
PROCESS(H)
BEGIN
IF(rising_edge(H)) THEN
FOR I IN 0 TO 6 LOOP
x(I+1) <= X(I);
END LOOP;
END IF;
END PROCESS;
WAIT
WAIT
est utilisé dans une déclaration séquentielle de type PROCESS
ou PROCEDURE
.
WAIT
remplace une liste de sensibilité pour contrôler l’exécution et la suspension d’un PROCESS
.
WAIT peut être placé n’importe où dans le PROCESS
.
Sans WAIT
ou sans liste de sensibilité, un PROCESS
se boucle indéfiniment.
Il existe trois syntaxes différentes autour de WAIT :
Wait ON
WAIT ON
est équivalent à une liste de sensibilité. La liste des signaux suit. Ainsi :
PROCESS (A,B)
BEGIN
--insru
END PROCESS;
est équivalent à :
PROCESS
BEGIN
--insru
WAIT ON A,B;
END PROCESS;
WAIT UNTIL
La condition testée par WAIT UNTIL
d’expression booléenne doit parvenir à TRUE
pour continuer l’exécution.
PROCESS
BEGIN
WAIT UNTIL condition;
--instruc
end PROCESS;
WAIT FOR
Le temps imparti par WAIT FOR
spécifie la durée maximale pendant laquelle le PROCESS reste suspendu.
STIMULUS: process
BEGIN
EN_1 <= '0';
EN_2 <= '1';
WAIT FOR 10 ns; -- attend 10ns avant de passer à la ligne suivante
EN_1 <= '1';
EN_2 <= '0';
WAIT FOR 10 ns;
EN_1 <= '0';
WAIT; -- attent indéfiniment
END PROCESS STIMULUS;
Gestion du délai
Une affectation de signal avec la commande after
modélise un comportement temporel. Il s’agit d’un modèle destiné à la simulation
Exemple
S <= ‘1’, ‘0’ after 5 ns, ‘1’ after 10 ns, ‘0’ after 15 ns;
Les expressions doivent être ordonnées selon un ordre croissant des valeurs des délais.
La valeur du délai de chaque expression est relative au moment de l’évaluation globale de l’affectation et non pas du délai précédent.
Toutes les expressions sont calculées au même moment et les valeurs résultats sont mémorisées dans le pilote du signal.
Fonction
FUNCTION
: exécute une suite d’instructions séquentielles sur les paramètres d’entree (constrante ou signaux) et retourne une valeur et une seule.
Syntaxe
FUNCTION nom_fonction [ ( liste de paramètres formels ) ] RETURN nom_du_type IS
[ déclarations ]
BEGIN
--instructions séquentielles
END [ nom_fonction ] ;
Exemple
FUNCTION PARITY (X : std_ulogic_vector) RETURN std_ulogic IS
variable TMP : std_ulogic := '0';
BEGIN
for J in X'range loop
TMP := TMP xor X(J);
end loop; -- works for any size X
return TMP;
END PARITY;
PROCEDURE
PROCEDURE
: idem fonction mais peut retourner plusieurs paramètres de sorties définis dans sa déclaration
Syntaxe
PROCEDURE nom_procedure [ ( liste de paramètres formels ) ] IS [ déclarations ]
BEGIN
--instructions séquentielles
END [ nom_procedure ] ;
Exemple
PROCEDURE MAX2 (N1,N2:IN INTEGER; N3 : OUT INTEGER) IS
BEGIN
IF N1>N2 THEN N3 <= N1;
ELSE N3 <= N2;
END IF;
END MAX2;
Résumé, instructions concurrentes, séquentielles & types de description
Il y a deux types d’instructions :
- concurrentes : elles sont évaluées en même temps, elles manipulent des signaux (uniquement). Les principales sont :
- Les affectations
- Les processus
- Les instanciations de composants
- les fonctions
- Séquentielles : elles sont “exécutées” l’une après l’autre, elles sont obligatoirement placées dans une instruction concurrente : processus, fonctions, procédures. Elles manipulent des variables, des constantes et des signaux, les signaux ne sont pas affectés immédiatement.
Description structurelle
Méthode de description par assemblage de composants élémentaires. 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
Principe
- On définit les composants dans des fichiers séparés (contenant entité, architecture(s)).
- Un composant a une entité, et une/des architecture(s).
- Les composants doivent être définis dans un package et compilés dans une bibliothèque.
- Les bibliothèques sont attachées par une déclaration dans le programme dans lequel on veut les utiliser.
- On déclare le composant avec
COMPONENT
- On instancie un composant avec
port map
Composant (componet)
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”.
Pour instancier un composant il est nécessaire de connaître :
- le prototype du composant (ports d’E/S). On utilise la directive
COMPONENT
pour ça. - A quelle entité et architecture est lié chaque instance de composant. Ce lien peut être connu grâce à l’unité de
configuration
. La configuration est une unité de compilation optionnelle
Remarque
La déclaration du composant (directive component
) est redondante textuellement avec celle de l’entité associée mais permet :
- Une compilation indépendante entre l’entité associée au composant et le circuit utilisant le composant.
- La conception descendante. Le composant peut être déclaré avant l’entité associée.
Déclaration
Le mot clé component
sert à déclarer le prototype d’interconnexion. La syntaxe est presque identique à celle de l’entité :
COMPONENT nom_composant
port(liste_ports);
END COMPONENT;
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 de connexions);
Exemple
--fichier composant and2
ENTITY and2 IS
port(
a : bit;
b : bit;
s : bit;
);
END ENTITY;
ARCHITECTURE arch of and2 IS
BEGIN
s <= a AND b;
END arch;
---------------------------
--fichier principal
ENTITY and_3 IS
port(
e1 : in bit;
e2 : in bit;
e3 : in bit;
s : out bit;
);
END ENTITY;
ARCHITECTURE arc OF and_3 IS
SIGNAL z : bit;
COMPONENT and2
port(
a : bit;
b : bit;
s : bit;
);
END COMPONENT;
BEGIN
inst1 : and2 port map(a=>e1, b=>e2, s=>z);
inst2 : and2 port map(z, e3, s);
END arc;
Dans cet exemple , deux instances de composant “and2” sont appelées pour créer une porte ET à 3 entrées.
L’association des ports du composants aux signaux de l’instance se fait à l’aide de la clause `port map.
La syntaxe des associations est soit
- par nom où chaque broche du composant est associée à un signal : cas de inst_1
- positionnelle où l’ordre des signaux correspond à l’ordre des broches : cas de inst_2
Genericité
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.
[à compléter]
Configuration
à faire En attendant http://hdl.telecom-paristech.fr/vhdl_structurel.html#header
sources (notamment)
- https://fr.wikipedia.org/wiki/VHDL
- Cours X5SI070 Université de Nantes - Licence Electronique, Energie Electrique, Automatique
- http://www.sfa.univ-savoie.fr/formations/masters/electronique-telecoms/wp-content/files/ETRS-511/Cours/Cours%20VHDL%20FPGA%202.pdf
- http://jacques.weber.pagesperso-orange.fr/vhdl_html/Vhdl.htm
- http://hdl.telecom-paristech.fr/vhdl_syntaxe.html
- http://www.vhdl.renerta.com/mobile/source/vhd00051.htm
- http://amouf.chez.com/syntaxe.htm regroupe l’ensemble des mots clés, leur définition et utilisation
- http://www2.ece.ohio-state.edu/~degroat/ee762/Lectures/L2-VHDL%20Introduction.pdf
- http://iroi.seu.edu.cn/books/asics/Book2/CH10/CH10.07.htm : interface declarations
- http://electronics.stackexchange.com/questions/17524/std-logic-or-std-ulogic : std_logic or std_ulogic ?
- https://books.google.fr/books?id=W1fsnS5fU_YC&pg=PA71&lpg=PA71&dq=signal+r%C3%A9solu+vhdl&source=bl&ots=LsTAupMcOM&sig=-RsYzbgzR1KLQ3opWXiEbPUf72w&hl=fr&sa=X&ved=0CCMQ6AEwAGoVChMIgtKYkqW4yAIVQYAaCh3DXwDg#v=onepage&q=signal%20r%C3%A9solu%20vhdl&f=false : fonction de résolution
- http://www.ics.uci.edu/~jmoorkan/vhdlref/arrays.html : array
- https://www.doulos.com/knowhow/vhdl_designers_guide/numeric_std/numeric_std_conversions.gif : schéma résumé des conversions numérique - std