Unités de conception

Entité

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

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;

Paramètres génériques

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.

Architecture

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 :

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.

Paquetage

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.

Import de paquetage

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;