Une entité décrit l’interface d’un circuit. Une entité sans paramètre générique s’écrit de la manière suivante :
déclaration de bibliothèque ou import de paquetage
...
entity nom de l'entité is
port(
nom, ... nom : mode type;
...
nom, ... nom : mode type
);
end nom de l'entité;
La clause port()
contient la déclaration des ports de l’entité.
Un port est caractérisé par :
Il est possible de regrouper dans une même déclaration les noms des ports qui ont le même mode et le même type comme ceci :
entity Comparator is
port(
a_i, b_i : in integer;
lt_o, eq_o, gt_o : out std_logic
);
end Comparator;
Le « ;
» joue le rôle de séparateur entre des listes de ports.
Il n’y a pas de « ;
» avant la parenthèse fermante.
Le mode d’un port définit le sens de circulation des données entre un circuit et son environnement. Parmi les modes autorisés par le standard VHDL, nous utiliserons les suivants :
Mode | Signification | Sens de circulation des données |
---|---|---|
in |
Entrée | De l’extérieur vers l’intérieur du circuit |
out |
Sortie | De l’intérieur vers l’extérieur du circuit |
inout |
Entrée-sortie | Alternativement de l’extérieur vers l’intérieur, ou de l’intérieur vers l’extérieur, du circuit |
Dans le mode inout
, les données ne circulent pas simultanément dans les deux sens.
Ce mode permet de transporter des informations soit dans un sens, soit dans l’autre,
avec la possibilité de changer ce sens au cours du fonctionnement.
Par convention, nous donnerons le suffixe _i
aux ports d’entrée,
_o
aux ports de sortie, et _io
aux ports bidirectionnels.
Ce nommage n’est pas imposé par le langage VHDL, mais c’est une pratique très répandue.
Pour les versions de VHDL antérieures au standard VHDL-2008, les ports de mode
out
sont en écriture seule.
Il est donc interdit de les utiliser dans une expression.
Si vos outils de développement prennent en charge VHDL-2008, l’architecture
Modern
ci-dessous est correcte.
Sinon, vous devrez créer des signaux intermédiaires comme dans l’architecture
OldStyle
:
entity Comparator is
port(
a_i, b_i : in integer;
lt_o, eq_o, gt_o : out std_logic
);
end Comparator;
architecture Modern of Comparator is
begin
lt_o <= '1' when a_i < b_i else '0';
gt_o <= '1' when a_i > b_i else '0';
eq_o <= lt_o nor gt_o;
end Modern;
architecture OldStyle of Comparator is
signal lt, gt : std_logic;
begin
lt <= '1' when a_i < b_i else '0';
gt <= '1' when a_i > b_i else '0';
lt_o <= lt;
gt_o <= gt;
eq_o <= lt nor gt;
end OldStyle;
Même si leur valeur définitive est inconnue au moment où l’on écrit une entité, les paramètres génériques sont considérés comme des constantes. En effet, un paramètre générique représente une propriété intrinsèque du circuit, un réglage effectué à la construction, qui ne peut pas varier au cours de son fonctionnement.
Lorsqu’une entité possède des paramètres génériques, il sont déclarés dans une clause
generic
avant la liste des ports.
Un paramètre générique est caractérisé par :
déclaration de bibliothèque ou import de paquetage
...
entity nom de l'entité is
generic(
nom, ... nom : type;
...
nom, ... nom : type
);
port(
nom, ... nom : mode type;
...
nom, ... nom : mode type
);
end nom de l'entité;
Il est possible de regrouper dans une même déclaration les noms des paramètres
qui ont le même type.
Dans cet exemple, nous utilisons deux paramètres génériques pour restreindre
l’intervalle des valeurs de a_i
et b_i
:
entity Comparator is
generic(
MIN, MAX : integer
);
port(
a_i, b_i : in integer range MIN to MAX;
lt_o, eq_o, gt_o : out std_logic
);
end Comparator;
Le « ;
» joue le rôle de séparateur entre des listes de paramètres.
Il n’y a pas de « ;
» avant la parenthèse fermante.
Une architecture décrit une implémentation possible d’une entité.
Elle possède un nom et est associée à une entité existante. Deux architectures peuvent porter le même nom si elles sont associées à des entités différentes.
Une architecture est composée de deux sections :
begin
, contient les déclarations de signaux, constantes ou types de données
utilisés dans cette architecture. Ces déclarations ne sont pas visibles à l’extérieur de l’architecture.begin
, décrit le comportement ou la structure du circuit.déclaration de bibliothèque ou import de paquetage
...
architecture nom de l'architecture of nom d'entité is
déclaration
...
déclaration
begin
instruction concurrente
...
instruction concurrente
end nom de l'architecture;
Le corps d’une architecture est composée d’instructions concurrentes, c’est-à-dire qu’elles s’exécutent en parallèle. On peut donc les écrire dans n’importe quel ordre.
Dans les langages de programmation que vous connaissez, vous avez l’habitude de considérer que les instructions d’un programme sont exécutées dans l’ordre où vous les avez écrites. Par construction, un ordinateur lit et exécute un programme en respectant l’ordre des instructions.
Dans un circuit électronique, les composants travaillent en parallèle. À tout moment, ils réagissent aux variations de leurs entrées et mettent à jour leurs sorties. Pour cette raison, le parallélisme est un élément fondamental des langages de description de matériel.
Une paquetage regroupe des déclarations que l’on souhaite mettre en commun entre différentes unités de conception. On y trouvera typiquement des déclarations de constantes, de types et de sous-programmes.
déclaration de bibliothèques ou import de paquetage
...
package nom du paquetage is
déclaration
...
déclaration
end nom du paquetage;
Un paquetage peut contenir des déclarations de sous-programmes, mais le corps de ces sous-programmes doit alors être placé dans un corps de paquetage (package body) portant le même nom.
déclaration de bibliothèques ou import de paquetage
...
package body nom du paquetage is
déclaration
...
déclaration
end body nom du paquetage;
Un corps de paquetage peut contenir d’autres déclarations à usage interne.
Pour qu’une unité de conception (par exemple une architecture) puisse utiliser
un élément déclaré dans un paquetage, il faut importer cet élément au moyen
d’une clause use
:
use nom de bibliothèque.nom de paquetage.nom d'élément;
On peut également importer tout le contenu d’un paquetage en utilisant le mot-clé all
:
use nom de bibliothèque.nom de paquetage.all;
La bibliothèque doit avoir été déclarée auparavant en utilisant une clause library
.
Ce n’est pas nécessaire pour la bibliothèque de travail par défaut work
.
Dans cet exemple, le paquetage Color_pkg
importe uniquement le type std_logic_vector
défini dans le paquetage std_logic_1164
de la bibliothèque ieee
.
L’entité ColorExtractor
importe tout le contenu du paquetage Color_pkg
.
library ieee;
use ieee.std_logic_1164.std_logic_vector;
package Color_pkg is
subtype color_t is std_logic_vector(31 downto 0);
subtype color_element_t is std_logic_vector(7 downto 0);
end Color_pkg;
------
use work.Color_pkg.all;
entity ColorExtractor is
port(
c : in color_t;
r, v, b, a : out color_element_t
);
end ColorExtractor;
architecture Behavioral of ColorExtractor is
begin
r <= c(31 downto 24);
v <= c(23 downto 16);
b <= c(15 downto 8);
a <= c(7 downto 0);
end Behavioral;