Leçon 9 sur 22

Chapitre 9 — Génériques, composants réutilisables et hiérarchie

Objectif — Finalité du chapitre

Concevoir des blocs VHDL paramétrables et réutilisables, maîtriser le passage des génériques, générer des structures matérielles régulières et organiser un projet en composants hiérarchiques validés séparément.

Objectifs pédagogiques

À la fin de ce chapitre, l’étudiant devra être capable de :

  • expliquer le principe de généricité en VHDL ;
  • déclarer des génériques de types integer, positive, boolean et time ;
  • définir une valeur par défaut ;
  • utiliser generic map lors d’une instanciation ;
  • distinguer association par position et association par nom ;
  • utiliser for generate pour répéter une structure ;
  • utiliser if generate pour inclure conditionnellement du matériel ;
  • identifier les conditions statiques requises lors de l’élaboration ;
  • construire une hiérarchie à partir de composants réutilisables ;
  • organiser une bibliothèque de blocs validés ;
  • développer un additionneur, un registre et un multiplexeur paramétrables ;
  • comparer plusieurs valeurs du générique N en simulation et en synthèse.

Prérequis

  • entités, architectures et instanciation directe ;
  • styles structurel, comportemental et par flot de données ;
  • types std_logic_vector, unsigned et positive ;
  • processus cadencés et logique combinatoire ;
  • additionneurs complets et propagation de retenue.

Organisation du chapitre

Section

Contenu

Compétence principale

9.1Notion de généricitéParamétrer la taille et le comportement
9.2Déclaration des génériquesÉcrire une interface configurable
9.3Passage des paramètresConfigurer une instance avec generic map
9.4Génération de matérielCréer des structures répétitives ou conditionnelles
9.5Réutilisation et hiérarchieOrganiser, maintenir et valider les blocs
TP 6Composants paramétrablesDévelopper et comparer plusieurs configurations

 

Concept clé — Moment de la configuration

La valeur d’un générique est fixée lors de l’élaboration, avant la simulation du circuit et avant la synthèse. Elle configure la structure matérielle ; elle ne se modifie pas dynamiquement pendant le fonctionnement.

 

9.1 Notion de généricité

La généricité permet de décrire une famille de composants à partir d’un seul code source. Les différences entre les membres de la famille sont exprimées par des paramètres appelés génériques.

9.1.1 Pourquoi paramétrer un composant ?

  • éviter de dupliquer le même code pour 4, 8, 16 ou 32 bits ;
  • adapter un composant à plusieurs projets ;
  • centraliser les corrections et améliorations ;
  • rendre l’intention de dimensionnement explicite ;
  • faciliter les tests automatiques sur plusieurs configurations ;
  • constituer une bibliothèque de blocs génériques.

9.1.2 Composant fixe et composant générique

Registre fixé à 8 bits  |   VHDL
entity registre_8bits is
    port (
        clk : in  std_logic;
        d   : in   std_logic_vector(7 downto 0);
        q   : out std_logic_vector(7 downto 0)
    );
end entity registre_8bits;

 

Registre générique  |  VHDL
entity registre_nbits is
    generic (
        N : positive := 8
    );
    port (
        clk : in  std_logic;
        d   : in   std_logic_vector(N-1 downto 0);
        q   : out std_logic_vector(N-1 downto 0)
    );
end entity registre_nbits;

 

La même entité peut être élaborée avec N = 4, 8, 16 ou toute autre valeur positive compatible avec les contraintes du projet.

9.1.3 Paramétrage de la taille d’un vecteur

Largeur définie par N  |   VHDL
generic (
    N : positive := 8
);

port (
    entree : in  std_logic_vector(N-1 downto 0);
    sortie : out std_logic_vector(N-1 downto 0)
);

 

Repère — Pourquoi N-1 downto 0 ?

Un vecteur de N bits possède N indices. Avec la convention descendante courante, les indices vont de N-1 à 0.

 

9.1.4 Paramétrage du comportement

Un générique ne sert pas uniquement à définir une largeur. Il peut activer une option, choisir une polarité, fixer un nombre d’étages ou définir une durée de simulation.

Génériques de différents types  |  VHDL
generic (
    N               : positive := 8;
    RESET_ACTIF_HAUT: boolean  := true;
    VALEUR_INITIALE : std_logic := '0';
    PERIODE_SIM     : time := 10 ns
);

 

9.1.5 Valeurs par défaut

Une valeur par défaut rend le générique facultatif lors de l’instanciation. Si generic map est absent ou ne mentionne pas ce paramètre, la valeur par défaut est utilisée.

Valeur par défaut de 8 bits  |  VHDL
generic (
    N : positive := 8
);

 

Instance utilisant la valeur par défaut  |  VHDL
U_REG : entity work.registre_nbits(rtl)
    port map (
        clk => clk,
        d   => d8,
        q   => q8
    );

 

9.1.6 Contraindre les valeurs

Le type du générique limite les valeurs autorisées. positive interdit zéro et les valeurs négatives, ce qui évite des plages de vecteurs invalides.

Déclaration

Valeurs autorisées

Usage

N : positive := 81, 2, 3, ...Largeur, nombre d’étages
M : natural := 00, 1, 2, ...Nombre pouvant être nul
MODE : boolean := falsetrue ou falseActiver une option
MAXIMUM : integer range 1 to 1024 := 2551 à 1024Plage contrôlée
PERIODE : time := 20 nsValeurs temporellesBancs de test et modèles

 

9.1.7 Génériques et ressources matérielles

Modifier N change généralement le nombre de bascules, de LUT, de bits de mémoire ou d’étages arithmétiques instanciés.

Composant

N = 4

N = 8

N = 16

Registre N bits4 bascules8 bascules16 bascules
Additionneur à propagation4 étages8 étages16 étages
Bus logique4 fils8 fils16 fils
Masque bit à bit4 opérations8 opérations16 opérations

 

9.1.8 Générique et signal de commande

Générique

Signal

Fixé avant l’exécutionÉvolue pendant l’exécution
Configure la structureTransporte une donnée ou une commande
Pas de broche physique directePeut être relié à une broche
Utilisé dans les tailles et generateUtilisé dans les équations et processus
Valeur constante pour une instanceValeur temporelle

 

Attention — Erreur fréquente

Un générique ne remplace pas un signal de sélection dynamique. Pour changer une opération pendant le fonctionnement, il faut utiliser un port ou un signal, pas un générique.

 

9.1.9 Généricité et réutilisation

Une bonne généricité évite de multiplier les versions _4bits, _8bits et _16bits. Elle ne doit toutefois pas rendre l’interface excessivement complexe.

  • paramétrer les dimensions réellement variables ;
  • fournir des valeurs par défaut raisonnables ;
  • documenter les plages valides ;
  • éviter des génériques dépendants ou contradictoires ;
  • tester plusieurs configurations représentatives.

9.2 Déclaration d’un générique

La clause generic est placée dans l’entité avant la clause port. Les génériques deviennent visibles dans les déclarations de ports et dans les architectures associées.

9.2.1 Syntaxe demandée

Déclaration minimale  |  VHDL
generic (
    N : positive := 8
);

 

9.2.2 Entité complète

Composant largeur N  |   VHDL
library IEEE;
use IEEE.STD_LOGIC_1164.ALL;

entity inverseur_nbits is
    generic (
        N : positive := 8
    );
    port (
        entree : in  std_logic_vector(N-1 downto 0);
        sortie : out std_logic_vector(N-1 downto 0)
    );
end entity inverseur_nbits;

 

Architecture utilisant N  |  VHDL
architecture rtl of inverseur_nbits is
begin
    sortie <= not entree;
end architecture rtl;

 

9.2.3 Plusieurs génériques

Largeur et valeur de reset  |  VHDL
entity registre_nbits is
    generic (
        N           : positive := 8;
        RESET_VALUE : std_logic := '0'
    );
    port (
        clk, reset : in  std_logic;
        d          : in  std_logic_vector(N-1 downto 0);
        q          : out std_logic_vector(N-1 downto 0)
    );
end entity registre_nbits;

 

9.2.4 Générique vectoriel

Un générique peut contenir une valeur vectorielle, mais sa taille doit être déterminable. Une méthode robuste consiste à utiliser un vecteur non contraint en VHDL-2008 ou à lier sa longueur à un autre générique selon les capacités de l’outil.

Valeur initiale liée à N  |  VHDL
generic (
    N          : positive := 8;
    INIT_VALUE : std_logic_vector(N-1 downto 0)
                 := (others => '0')
);

 

Règle — Ordre des génériques

Lorsqu’un générique utilise un paramètre précédent dans sa contrainte, N doit être déclaré avant INIT_VALUE.

 

9.2.5 Générique boolean

Option de parité  |  VHDL
generic (
    N              : positive := 8;
    AJOUTER_PARITE : boolean  := false
);

 

Ce type de paramètre peut être utilisé dans une instruction if generate pour inclure ou supprimer une partie du matériel.

9.2.6 Générique de type énuméré

Un paquetage peut définir un type énuméré représentant plusieurs politiques de fonctionnement.

Type de stratégie dans un paquetage  |  VHDL
package options_pkg is
    type saturation_t is (
        TRONCATURE,
        SATURATION
    );
end package options_pkg;

 

Utilisation comme générique  |  VHDL
generic (
    N     : positive := 8;
    MODE : saturation_t := TRONCATURE
);

 

9.2.7 Génériques dans l’architecture

Compteur paramétrable  |  VHDL
architecture rtl of compteur_modulo is
    signal compteur : natural range 0 to MODULO-1 := 0;
begin
    process(clk, reset)
    begin
        if reset = '1' then
            compteur <= 0;
        elsif rising_edge(clk) then
            if compteur = MODULO-1 then
                compteur <= 0;
            else
                compteur <= compteur + 1;
            end if;
        end if;
    end process;
end architecture rtl;

 

9.2.8 Assertions sur les génériques

Une assertion concurrente peut vérifier qu’une combinaison de paramètres respecte les hypothèses du composant.

Vérification statique  |  VHDL
assert N >= 2
    report "N doit etre superieur ou egal a 2"
    severity failure;

 

Vérification d’une profondeur paire  |  VHDL
assert (PROFONDEUR mod 2) = 0
    report "La profondeur doit etre paire"
    severity failure;

 

Validation — Échec précoce

Les assertions sur les génériques détectent les configurations invalides dès l’élaboration ou le début de la simulation, avant qu’une erreur subtile n’apparaisse dans le circuit.

 

9.2.9 Valeur par défaut et documentation

Élément

Recommandation

NomCourt et explicite : N, LARGEUR, PROFONDEUR, MODE
TypeLe plus restrictif possible
Valeur par défautConfiguration courante et valide
CommentaireUnité, plage et effet matériel
AssertionConditions impossibles à exprimer par le type

 

9.2.10 Erreurs fréquentes

Erreur

Conséquence

Correction

N déclaré integer sans plageValeurs nulles ou négatives possiblesUtiliser positive
Générique déclaré après portErreur de syntaxePlacer generic avant port
Valeur par défaut incompatibleErreur d’analyseRespecter le type et la plage
Vecteur INIT de mauvaise tailleErreur de contrainteLier sa taille à N
Générique inutiliséInterface trompeuseLe supprimer ou l’implémenter
Paramètres trop nombreuxComposant difficile à comprendreSéparer les fonctions

 

9.3 Passage des paramètres

La clause generic map associe les valeurs choisies aux génériques formels d’une instance. Cette association peut être effectuée par position ou par nom.

9.3.1 Forme générale

Instanciation avec generic map  |  VHDL
U1 : entity work.nom_entite(rtl)
    generic map (
        parametre_1 => valeur_1,
        parametre_2 => valeur_2
    )
    port map (
        -- Connexions
    );

 

9.3.2 Association par nom

Registre de 16 bits  |   VHDL
U_REG16 : entity work.registre_nbits(rtl)
    generic map (
        N           => 16,
        RESET_VALUE => '0'
    )
    port map (
        clk   => clk,
        reset => reset,
        d     => entree16,
        q     => sortie16
    );

 

  • ordre libre ;
  • intention explicite ;
  • possibilité d’omettre des génériques possédant une valeur par défaut ;
  • résistance aux changements d’ordre dans l’entité.

9.3.3 Association par position

Association positionnelle  |  VHDL
U_REG16 : entity work.registre_nbits(rtl)
    generic map (
        16,
        '0'
    )
    port map (
        clk,
        reset,
        entree16,
        sortie16
    );

 

La première valeur correspond au premier générique, la deuxième au second. La même règle s’applique aux ports si le port map est positionnel.

Attention — Risque de maintenance

L’association par position est compacte mais sensible à l’ordre des déclarations. Une permutation de deux génériques de même type peut produire une configuration valide mais incorrecte.

 

9.3.4 Association mixte

VHDL autorise certaines associations mêlant position et noms, mais les éléments positionnels doivent précéder les éléments nommés. Pour une lecture claire, il est préférable d’utiliser une seule méthode.

Exemple mixte à éviter  |  VHDL
generic map (
    16,
    RESET_VALUE => '0'
);

 

9.3.5 Utilisation de la valeur par défaut

Seul N est redéfini  |  VHDL
U_REG32 : entity work.registre_nbits(rtl)
    generic map (
        N => 32
    )
    port map (
        clk   => clk,
        reset => reset,
        d     => entree32,
        q     => sortie32
    );

 

RESET_VALUE conserve sa valeur par défaut.

9.3.6 Plusieurs instances, plusieurs configurations

Trois largeurs différentes  |  VHDL
U_REG4 : entity work.registre_nbits(rtl)
    generic map (N => 4)
    port map (clk => clk, reset => reset, d => d4, q => q4);

U_REG8 : entity work.registre_nbits(rtl)
    generic map (N => 8)
    port map (clk => clk, reset => reset, d => d8, q => q8);

U_REG16 : entity work.registre_nbits(rtl)
    generic map (N => 16)
    port map (clk => clk, reset => reset, d => d16, q => q16);

 

9.3.7 Passage d’un générique du niveau supérieur

Un composant générique peut transmettre son propre paramètre à ses sous-composants.

Propagation de N dans la hiérarchie  |  VHDL
entity systeme_nbits is
    generic (
        N : positive := 8
    );
    port (
        clk : in  std_logic;
        a   : in   std_logic_vector(N-1 downto 0);
        q   : out std_logic_vector(N-1 downto 0)
    );
end entity systeme_nbits;

 

Sous-instance utilisant N  |  VHDL
U_REG : entity work.registre_nbits(rtl)
    generic map (
        N => N
    )
    port map (
        clk   => clk,
        reset => '0',
        d     => a,
        q     => q
    );

 

9.3.8 Expressions constantes dans generic map

Expression élaborée  |  VHDL
U_MEM : entity work.memoire(rtl)
    generic map (
        LARGEUR    => N,
        PROFONDEUR => 2 ** ADRESSE_BITS
    )
    port map (
        -- Connexions
    );

 

L’expression doit être calculable lors de l’élaboration. Elle peut utiliser des constantes et des génériques du niveau supérieur.

9.3.9 Association de génériques de type time

Banc de test configurable  |  VHDL
U_CLOCK : entity work.generateur_horloge(simulation)
    generic map (
        PERIODE => 20 ns
    )
    port map (
        clk => clk
    );

 

9.3.10 Erreurs de generic map

Message ou symptôme

Cause probable

Correction

No association for genericGénérique obligatoire non fourniAjouter une valeur ou une valeur par défaut
Type mismatchValeur incompatibleConvertir ou corriger le type
Bounds errorValeur hors plageRespecter la contrainte
Port width mismatchN ne correspond pas aux signauxAdapter la largeur des signaux
Unexpected hardware sizeMauvaise valeur passée par positionUtiliser l’association nommée
Entity not foundOrdre de compilation incorrectCompiler le composant avant le niveau supérieur

 

9.4 Génération de matériel

Les instructions generate créent conditionnellement ou répétitivement des instructions concurrentes. Elles sont évaluées lors de l’élaboration et permettent de construire des structures régulières sans dupliquer manuellement le code.

9.4.1 Caractère concurrent

generate est placé dans le corps d’une architecture. Les éléments générés sont des instances, des affectations concurrentes, des processus ou d’autres blocs generate.

9.4.2 Génération répétitive avec for generate

Forme générale  |   VHDL
etiquette : for i in plage generate
begin
    -- Instructions concurrentes répétées
end generate etiquette;

 

9.4.3 Banque d’inverseurs

Un inverseur par bit  |   VHDL
GEN_INV : for i in 0 to N-1 generate
begin
    sortie(i) <= not entree(i);
end generate GEN_INV;

 

Le synthétiseur crée N inverseurs. L’indice i est une constante locale différente pour chaque itération.

9.4.4 Instanciation répétitive

Chaîne de registres  |  VHDL
GEN_REG : for i in 0 to N-1 generate
begin
    U_DFF : entity work.bascule_d(rtl)
        port map (
            clk => clk,
            d   => d(i),
            q   => q(i)
        );
end generate GEN_REG;

 

9.4.5 Déclarations locales au generate

Bloc généré avec signal local  |  VHDL
GEN_ETAGE : for i in 0 to N-1 generate
    signal local_i : std_logic;
begin
    local_i <= a(i) and b(i);
    y(i) <= local_i xor commande;
end generate GEN_ETAGE;

 

9.4.6 Génération conditionnelle avec if generate

Forme générale  |   VHDL
GEN_OPTION : if CONDITION_STATIQUE generate
begin
    -- Matériel présent si la condition est vraie
end generate GEN_OPTION;

 

9.4.7 Parité optionnelle

Ajout conditionnel de la parité  |  VHDL
GEN_PARITE : if AJOUTER_PARITE generate
begin
    process(all)
        variable p : std_logic;
    begin
        p := '0';
        for i in donnees'range loop
            p := p xor donnees(i);
        end loop;
        parite <= p;
    end process;
end generate GEN_PARITE;

GEN_SANS_PARITE : if not AJOUTER_PARITE generate
begin
    parite <= '0';
end generate GEN_SANS_PARITE;

 

9.4.8 If generate avec else en VHDL-2008

Deux architectures conditionnelles  |  VHDL
GEN_RESET : if RESET_ACTIF_HAUT generate
begin
    reset_interne <= reset;
else generate
    reset_interne <= not reset;
end generate GEN_RESET;

 

9.4.9 Condition statique

La condition d’un if generate doit être déterminable à l’élaboration. Elle peut dépendre d’un générique ou d’une constante, mais pas d’un signal qui change pendant le fonctionnement.

Expression

Valide pour generate ?

Pourquoi

N > 8OuiN est un générique
AJOUTER_PARITEOuiBoolean générique
MODE = RAPIDEOuiValeur générique énumérée
enable = '1'Nonenable est un signal dynamique
compteur = 10Noncompteur évolue dans le temps

 

9.4.10 Génération conditionnelle selon la taille

Traitement réservé aux grandes largeurs  |  VHDL
GEN_LARGE : if N >= 16 generate
begin
    indicateur_large <= '1';
end generate GEN_LARGE;

GEN_PETIT : if N < 16 generate
begin
    indicateur_large <= '0';
end generate GEN_PETIT;

 

9.4.11 Création de structures régulières

  • additionneurs à propagation de retenue ;
  • registres et bancs de bascules ;
  • chaînes de délais cadencés ;
  • réseaux de comparateurs ;
  • arbres logiques ;
  • interfaces répliquées ;
  • matrices de traitement.

9.4.12 Chaîne d’additionneurs complets

Signaux de retenue  |  VHDL
signal retenues : std_logic_vector(N downto 0);

retenues(0) <= cin;
cout <= retenues(N);

 

Génération des N étages  |  VHDL
GEN_FA : for i in 0 to N-1 generate
begin
    U_FA : entity work.additionneur_complet(structurelle)
        port map (
            a     => a(i),
            b     => b(i),
            cin   => retenues(i),
            somme => somme(i),
            cout  => retenues(i+1)
        );
end generate GEN_FA;

 

9.4.13 Noms hiérarchiques

Chaque itération crée une instance distincte. Les outils affichent généralement des noms hiérarchiques contenant l’étiquette du generate et l’indice, par exemple GEN_FA(3).U_FA.

9.4.14 Generate et processus

Un processus par voie  |   VHDL
GEN_VOIE : for i in 0 to N-1 generate
begin
    process(clk)
    begin
        if rising_edge(clk) then
            q(i) <= d(i);
        end if;
    end process;
end generate GEN_VOIE;

 

9.4.15 Case generate en VHDL-2008

Choix d’une structure  |  VHDL
GEN_MODE : case MODE generate
    when ECONOMIQUE =>
        -- Architecture peu coûteuse

    when RAPIDE =>
        -- Architecture plus parallèle
end generate GEN_MODE;

 

Compatibilité — Support des outils

Les formes avancées de generate dépendent de la version VHDL activée. Les exemples VHDL-2008 nécessitent une configuration explicite de l’outil.

 

9.4.16 Coût du déroulement

Une génération répétitive crée du matériel réel. Doubler N peut presque doubler certaines ressources et augmenter le chemin critique, selon l’architecture.

Structure

Ressources en fonction de N

Effet temporel

Registre N bitsN basculesChemin généralement constant
Additionneur ripple N bitsN additionneurs completsPropagation approximativement croissante
Comparaison bit à bitN comparateurs élémentairesRéduction logique supplémentaire
Multiplexeur N voiesCroissance avec N et la largeurProfondeur de sélection variable

 

9.4.17 Erreurs fréquentes avec generate

Erreur

Conséquence

Correction

Étiquette absente ou dupliquéeHiérarchie confuse ou erreurNommer chaque generate
Limite dépendant d’un signalNon élaborableUtiliser un générique ou une constante
Indice hors plageErreur d’élaborationVérifier i et i+1
Plusieurs branches pilotent la même sortiePilotes multiplesRendre les conditions exclusives
N = 0Plage vide ou invalideDéclarer N positive
Structure très grandeRessources excessivesAnalyser avant synthèse

 

9.5 Réutilisation des composants

La réutilisation consiste à concevoir un bloc une fois, le vérifier indépendamment puis l’intégrer dans plusieurs systèmes. Les génériques et la hiérarchie rendent cette démarche plus efficace.

9.5.1 Conception modulaire

  • une responsabilité claire par composant ;
  • une interface stable ;
  • des dépendances limitées ;
  • des génériques cohérents ;
  • un banc de test associé ;
  • une documentation des hypothèses et latences.

9.5.2 Granularité des composants

Un composant doit être suffisamment autonome pour être réutilisé, mais pas si petit que la hiérarchie devienne inutilement profonde.

Granularité

Exemple

Évaluation

Trop fineUne entité pour chaque inverseur interneCode et hiérarchie lourds
AdaptéeAdditionneur, registre, UART TXFonction autonome testable
Trop largeSystème complet monolithiqueRéutilisation et test difficiles

 

9.5.3 Bibliothèque de composants

Les unités validées peuvent être compilées dans une bibliothèque dédiée au lieu de la bibliothèque work.

Référence à une bibliothèque  |  VHDL
library composants;

U_ADD : entity composants.additionneur_nbits(rtl)
    generic map (
        N => 16
    )
    port map (
        a     => a16,
        b     => b16,
        cin   => cin,
        somme => somme16,
        cout  => cout
    );

 

9.5.4 Paquetage de types et constantes

Paquetage partagé  |  VHDL
package projet_pkg is
    constant LARGEUR_PAR_DEFAUT : positive := 8;

    subtype octet_t is
        std_logic_vector(7 downto 0);
end package projet_pkg;

 

9.5.5 Déclaration de composants dans un paquetage

La méthode traditionnelle peut centraliser les déclarations component dans un paquetage. Avec l’instanciation directe, cette duplication est souvent inutile, mais elle reste rencontrée dans des projets anciens.

Paquetage de composants  |  VHDL
package composants_pkg is
    component registre_nbits is
        generic (
            N : positive := 8
        );
        port (
            clk, reset : in  std_logic;
            d          : in  std_logic_vector(N-1 downto 0);
            q          : out std_logic_vector(N-1 downto 0)
        );
    end component;
end package composants_pkg;

 

9.5.6 Maintenance

La modularité limite l’impact d’une modification. Si l’interface reste stable, une architecture interne peut être optimisée sans changer les niveaux supérieurs.

  • corriger une seule source générique ;
  • versionner les interfaces ;
  • conserver des tests de non-régression ;
  • documenter les changements de comportement ;
  • éviter de modifier silencieusement les valeurs par défaut.

9.5.7 Validation séparée des blocs

  1. Écrire les exigences du composant.
  2. Tester chaque valeur limite des génériques.
  3. Simuler les combinaisons fonctionnelles.
  4. Utiliser des assertions automatiques.
  5. Synthétiser plusieurs configurations.
  6. Vérifier ressources et timing.
  7. Intégrer ensuite le bloc dans le niveau supérieur.
  8. Réaliser un test d’intégration.

9.5.8 Banc de test générique

Un banc de test peut lui-même contenir un générique afin d’être relancé avec plusieurs tailles, ou instancier plusieurs configurations simultanément.

Banc de test avec N  |   VHDL
entity tb_additionneur is
    generic (
        N : positive := 8
    );
end entity tb_additionneur;

 

Exécution GHDL avec générique  |  Terminal
ghdl -r --std=08 tb_additionneur   -gN=16

 

9.5.9 Tests de non-régression

Configuration

But du test

N = 1Taille minimale et indices limites
N = 4Cas pédagogique facile à observer
N = 8Configuration par défaut
N = 16Croissance des ressources
Valeurs maximalesPropagation de retenue et débordement
Reset et enableComportement temporel du registre

 

9.5.10 Documentation d’un composant

Rubrique

Contenu attendu

FonctionDescription concise du bloc
GénériquesType, défaut, plage et effet
PortsSens, type, polarité et protocole
LatenceCombinatoire ou nombre de cycles
ResetSynchrone/asynchrone et valeur
HypothèsesFréquence, valeurs autorisées
ValidationBancs de test et configurations
SynthèseRessources et technologie ciblée

 

9.5.11 Hiérarchie de projet

Organisation possible  |  Arborescence
projet/
├── rtl/
│   ├── commun/
│   │    ├── additionneur_nbits.vhd
│   │    └── registre_nbits.vhd
│   ├── controle/
│   └── top/
├── tb/
├── contraintes/
└── scripts/

 

9.5.12 Critères de qualité

  • génériques utiles et correctement typés ;
  • interface cohérente pour toutes les configurations ;
  • aucune hypothèse cachée sur N ;
  • code compatible avec plusieurs outils ;
  • tests reproductibles ;
  • messages d’assertion compréhensibles ;
  • absence d’avertissements de synthèse non justifiés.

Méthode de conception d’un composant paramétrable

1. Identifier ce qui varie réellement entre les utilisations.

2. Choisir des génériques de types restrictifs.

3. Définir une configuration par défaut valide.

4. Écrire les tailles à partir des génériques et des attributs.

5. Éviter les constantes numériques cachées.

6. Ajouter des assertions de configuration.

7. Utiliser generate pour les répétitions statiques.

8. Tester la valeur minimale, la valeur par défaut et une grande valeur.

9. Comparer les ressources et le timing.

10. Documenter l’interface et les limites.

Checklist avant réutilisation

  • Le composant compile-t-il dans une bibliothèque propre ?
  • Les génériques ont-ils des noms et types explicites ?
  • Les ports dépendent-ils correctement de N ?
  • Les valeurs par défaut sont-elles sûres ?
  • Les branches generate sont-elles exclusives ?
  • Les indices i et i+1 restent-ils dans les plages ?
  • Le banc de test couvre-t-il plusieurs configurations ?
  • Les résultats de synthèse sont-ils acceptables ?

Travaux pratiques

TP 6 — Composants paramétrables

1. Objectifs du TP

  • développer un additionneur sur N bits ;
  • développer un registre sur N bits ;
  • développer un multiplexeur de données de largeur N ;
  • générer une chaîne de N additionneurs complets ;
  • utiliser generic map par nom ;
  • tester N = 4, 8 et 16 ;
  • comparer la version structurelle et une version arithmétique ;
  • observer la croissance des ressources et du chemin critique.

2. Organisation des fichiers

Arborescence proposée  |  Arborescence
tp6_composants_parametrables/
├── src/
│   ├── additionneur_complet.vhd
│   ├── additionneur_nbits_structurel.vhd
│   ├── additionneur_nbits_rtl.vhd
│   ├── registre_nbits.vhd
│   └── mux2_nbits.vhd
└── sim/
    ├── tb_additionneur_multi_n.vhd
    ├── tb_registre_multi_n.vhd
    └── tb_mux_multi_n.vhd

 

3. Partie A — Additionneur complet élémentaire

additionneur_complet.vhd  |   VHDL
library IEEE;
use IEEE.STD_LOGIC_1164.ALL;

entity additionneur_complet is
    port (
        a, b, cin : in  std_logic;
        somme     : out std_logic;
        cout      : out std_logic
    );
end entity additionneur_complet;

architecture rtl of additionneur_complet is
begin
    somme <= a xor b xor cin;
    cout  <= (a and b)
          or (a and cin)
          or (b and cin);
end architecture rtl;

 

4. Partie B — Additionneur structurel sur N bits

additionneur_nbits_structurel.vhd — Entité  |  VHDL
library IEEE;
use IEEE.STD_LOGIC_1164.ALL;

entity additionneur_nbits_structurel is
    generic (
        N : positive := 8
    );
    port (
        a, b  : in   std_logic_vector(N-1 downto 0);
        cin   : in   std_logic;
        somme : out std_logic_vector(N-1 downto 0);
        cout  : out std_logic
    );
end entity additionneur_nbits_structurel;

 

additionneur_nbits_structurel.vhd — Architecture  |  VHDL
architecture structurelle of additionneur_nbits_structurel is
    signal retenues : std_logic_vector(N downto 0);
begin
    retenues(0) <= cin;
    cout <= retenues(N);

    GEN_FA : for i in 0 to N-1 generate
    begin
        U_FA : entity work.additionneur_complet(rtl)
            port map (
                a     => a(i),
                b     => b(i),
                cin   => retenues(i),
                somme => somme(i),
                cout  => retenues(i+1)
            );
    end generate GEN_FA;
end architecture structurelle;

 

5. Partie C — Additionneur RTL sur N bits

additionneur_nbits_rtl.vhd — Entité  |  VHDL
library IEEE;
use IEEE.STD_LOGIC_1164.ALL;
use IEEE.NUMERIC_STD.ALL;

entity additionneur_nbits_rtl is
    generic (
        N : positive := 8
    );
    port (
        a, b  : in   std_logic_vector(N-1 downto 0);
        cin   : in   std_logic;
        somme : out std_logic_vector(N-1 downto 0);
        cout  : out std_logic
    );
end entity additionneur_nbits_rtl;
additionneur_nbits_rtl.vhd — Architecture  |  VHDL
architecture rtl of additionneur_nbits_rtl is
    signal resultat : unsigned(N downto 0);
begin
    process(all)
        variable resultat_v : unsigned(N downto 0);
    begin
        resultat_v := resize(unsigned(a), N+1)
                      + resize(unsigned(b), N+1);

        if cin = '1' then
            resultat_v := resultat_v + 1;
        end if;

        resultat <= resultat_v;
    end process;

    somme <= std_logic_vector(resultat(N-1 downto 0));
    cout  <= resultat(N);
end architecture rtl;

 

6. Partie D — Registre sur N bits

registre_nbits.vhd — Entité  |  VHDL
library IEEE;
use IEEE.STD_LOGIC_1164.ALL;

entity registre_nbits is
    generic (
        N           : positive := 8;
        RESET_VALUE : std_logic := '0'
    );
    port (
        clk, reset : in  std_logic;
        enable     : in   std_logic;
        d          : in  std_logic_vector(N-1 downto 0);
        q          : out std_logic_vector(N-1 downto 0)
    );
end entity registre_nbits;

 

registre_nbits.vhd — Architecture  |  VHDL
architecture rtl of registre_nbits is
begin
    process(clk, reset)
    begin
        if reset = '1' then
            q <= (others => RESET_VALUE);
        elsif rising_edge(clk) then
            if enable = '1' then
                q <= d;
            end if;
        end if;
    end process;
end architecture rtl;

 

7. Partie E — Multiplexeur paramétrable

Le nombre d’entrées reste 2, mais la largeur des mots est définie par N.

mux2_nbits.vhd  |   VHDL
library IEEE;
use IEEE.STD_LOGIC_1164.ALL;

entity mux2_nbits is
    generic (
        N : positive := 8
    );
    port (
        a, b : in  std_logic_vector(N-1 downto 0);
        sel  : in   std_logic;
        y    : out std_logic_vector(N-1 downto 0)
    );
end entity mux2_nbits;

architecture rtl of mux2_nbits is
begin
    y <= a when sel = '0'
         else b;
end architecture rtl;

 

8. Partie F — Instanciation de plusieurs valeurs de N

Trois additionneurs structurels  |  VHDL
U_ADD4 : entity work.additionneur_nbits_structurel(structurelle)
    generic map (N => 4)
    port map (
        a => a4, b => b4, cin => cin,
        somme => somme4, cout => cout4
    );

U_ADD8 : entity work.additionneur_nbits_structurel(structurelle)
    generic map (N => 8)
    port map (
        a => a8, b => b8, cin => cin,
        somme => somme8, cout => cout8
    );

U_ADD16 : entity work.additionneur_nbits_structurel(structurelle)
    generic map (N => 16)
    port map (
        a => a16, b => b16, cin => cin,
        somme => somme16, cout => cout16
    );

 

9. Banc de test comparatif des additionneurs

tb_additionneur_multi_n.vhd — Déclarations  |  VHDL
library IEEE;
use IEEE.STD_LOGIC_1164.ALL;
use IEEE.NUMERIC_STD.ALL;
use STD.ENV.ALL;

entity tb_additionneur_multi_n is
end entity tb_additionneur_multi_n;

architecture simulation of tb_additionneur_multi_n is
    signal a4, b4 : std_logic_vector(3 downto 0);
    signal s4_struct, s4_rtl : std_logic_vector(3 downto 0);
    signal c4_struct, c4_rtl : std_logic;

    signal a8, b8 : std_logic_vector(7 downto 0);
    signal s8_struct, s8_rtl : std_logic_vector(7 downto 0);
    signal c8_struct, c8_rtl : std_logic;

    signal a16, b16 : std_logic_vector(15 downto 0);
    signal s16_struct, s16_rtl : std_logic_vector(15 downto 0);
    signal c16_struct, c16_rtl : std_logic;

    signal cin : std_logic := '0';
begin

 

Instances N = 4 — Partie 1  |  VHDL
    U_S4 : entity work.additionneur_nbits_structurel(structurelle)
        generic map (N => 4)
        port map (
            a => a4, b => b4, cin => cin,
            somme => s4_struct, cout => c4_struct
        );

    U_R4 : entity work.additionneur_nbits_rtl(rtl)
        generic map (N => 4)
        port map (
            a => a4, b => b4, cin => cin,
            somme => s4_rtl, cout => c4_rtl
        );

 

Instances N = 8 et N = 16 — Partie 2  |  VHDL
    U_S8 : entity work.additionneur_nbits_structurel(structurelle)
        generic map (N => 8)
        port map (
            a => a8, b => b8, cin => cin,
            somme => s8_struct, cout => c8_struct
        );

    U_R8 : entity work.additionneur_nbits_rtl(rtl)
        generic map (N => 8)
        port map (
            a => a8, b => b8, cin => cin,
            somme => s8_rtl, cout => c8_rtl
        );

    U_S16 : entity work.additionneur_nbits_structurel(structurelle)
        generic map (N => 16)
        port map (
            a => a16, b => b16, cin => cin,
            somme => s16_struct, cout => c16_struct
        );

    U_R16 : entity work.additionneur_nbits_rtl(rtl)
        generic map (N => 16)
        port map (
            a => a16, b => b16, cin => cin,
            somme => s16_rtl, cout => c16_rtl
        );

 

10. Stimuli et assertions

Tests des trois largeurs  |  VHDL
    stimuli : process
    begin
        cin <= '0';

        a4 <= x"F"; b4 <= x"1";
        a8 <= x"C8"; b8 <= x"64";
        a16 <= x"FFFF"; b16 <= x"0001";
        wait for 1 ns;

        assert s4_struct = s4_rtl
           and c4_struct = c4_rtl
            report "Divergence N=4"
            severity error;

        assert s8_struct = s8_rtl
           and c8_struct = c8_rtl
            report "Divergence N=8"
            severity error;

        assert s16_struct = s16_rtl
           and c16_struct = c16_rtl
            report "Divergence N=16"
            severity error;

        assert c4_struct = '1' and s4_struct = x"0"
            report "Erreur 15 + 1"
            severity error;

        assert unsigned(c8_struct & s8_struct) = to_unsigned(300, 9)
            report "Erreur 200 + 100"
            severity error;

        assert c16_struct = '1' and s16_struct = x"0000"
            report "Erreur 65535 + 1"
            severity error;

        report "Comparaison multi-N terminee"
            severity note;
        stop;
        wait;
    end process;
end architecture simulation;

 

11. Banc de test du registre

Horloge et instance  |  VHDL
signal clk    : std_logic := '0';
signal reset  : std_logic := '1';
signal enable : std_logic := '0';
signal d8, q8 : std_logic_vector(7 downto 0);

clk <= not clk after 5 ns;

U_REG8 : entity work.registre_nbits(rtl)
    generic map (
        N           => 8,
        RESET_VALUE => '0'
    )
    port map (
        clk => clk, reset => reset,
        enable => enable, d => d8, q => q8
    );

 

Scénario du registre  |  VHDL
stimuli : process
begin
    d8 <= x"A5";
    wait for 12 ns;
    reset <= '0';

    wait until rising_edge(clk);
    wait for 1 ns;
    assert q8 = x"00"
        report "Le registre ne doit pas charger si enable=0"
        severity error;

    enable <= '1';
    wait until rising_edge(clk);
    wait for 1 ns;
    assert q8 = x"A5"
        report "Chargement incorrect"
        severity error;

    d8 <= x"3C";
    enable <= '0';
    wait until rising_edge(clk);
    wait for 1 ns;
    assert q8 = x"A5"
        report "Conservation incorrecte"
        severity error;

    wait;
end process;

 

12. Banc de test du multiplexeur

Test de N = 4 et N = 16  |   VHDL
a4_mux <= x"3";
b4_mux <= x"C";
sel <= '0';
wait for 1 ns;
assert y4_mux = x"3" severity error;

sel <= '1';
wait for 1 ns;
assert y4_mux = x"C" severity error;

a16_mux <= x"1234";
b16_mux <= x"ABCD";
sel <= '0';
wait for 1 ns;
assert y16_mux = x"1234" severity error;

 

13. Compilation avec GHDL

Ordre de compilation  |  Terminal
ghdl -a --std=08 src/additionneur_complet.vhd
ghdl -a --std=08 src/additionneur_nbits_structurel.vhd
ghdl -a --std=08 src/additionneur_nbits_rtl.vhd
ghdl -a --std=08 src/registre_nbits.vhd
ghdl -a --std=08 src/mux2_nbits.vhd
ghdl -a --std=08 sim/tb_additionneur_multi_n.vhd

ghdl -e --std=08 tb_additionneur_multi_n
ghdl -r --std=08 tb_additionneur_multi_n    --vcd=tb_additionneur_multi_n.vcd

 

14. Comparaison des configurations

Composant

N = 4

N = 8

N = 16

Tendance attendue

Additionneur structurel4 étages8 étages16 étagesRessources et délai augmentent
Additionneur RTLLargeur 4Largeur 8Largeur 16Outil choisit l’implémentation
Registre4 bascules8 bascules16 basculesCroissance linéaire
Multiplexeur 2:18 LUT possibles selon architectureidem largeur 8idem largeur 16Croissance avec la largeur

 

15. Mesures de synthèse à relever

Configuration

LUT

Bascules

Chemin critique

Fréquence maximale

Additionneur structurel N=4    
Additionneur structurel N=8    
Additionneur structurel N=16    
Additionneur RTL N=4    
Additionneur RTL N=8    
Additionneur RTL N=16    

 

16. Observations demandées

  • Vérifier que la même source fonctionne pour N = 4, 8 et 16.
  • Comparer les sorties structurelles et RTL.
  • Observer les noms hiérarchiques générés.
  • Vérifier le comportement du reset et de enable.
  • Comparer la taille des vecteurs dans le simulateur.
  • Relever les ressources de synthèse.
  • Comparer la fréquence maximale des additionneurs.
  • Expliquer l’impact du ripple-carry lorsque N augmente.

17. Questions d’analyse

1. Pourquoi le type positive est-il préférable pour N ?

2. À quel moment la valeur de N est-elle fixée ?

3. Quelle différence existe entre generic map et port map ?

4. Pourquoi l’association nommée est-elle plus sûre ?

5. Quel matériel crée la boucle for generate ?

6. Pourquoi le signal retenues possède-t-il N+1 bits ?

7. Comment tester la taille minimale N = 1 ?

8. Pourquoi les versions structurelle et RTL peuvent-elles produire le même schéma après optimisation ?

9. Quel effet N a-t-il sur le registre ?

10. Comment transformer le multiplexeur 2:1 en multiplexeur à K entrées ?

18. Extensions proposées

  • Créer un additionneur/soustracteur générique.
  • Ajouter un générique de reset synchrone ou asynchrone.
  • Créer un registre à décalage sur N bits.
  • Créer un multiplexeur à K entrées en VHDL-2008.
  • Ajouter une parité optionnelle avec if generate.
  • Créer un comparateur N bits.
  • Comparer ripple-carry et carry-lookahead génériques.
  • Compiler les composants dans une bibliothèque dédiée.

19. Barème indicatif

Critère

Points

Additionneur complet élémentaire1
Additionneur structurel générique4
Additionneur RTL générique2
Registre générique3
Multiplexeur générique2
Bancs de test multi-N3
Comparaison synthèse et timing3
Qualité, modularité et compte rendu2
Total20

 

Synthèse du chapitre

Notion

Résumé

GénériqueParamètre fixé lors de l’élaboration.
Valeur par défautConfiguration utilisée si aucune valeur n’est passée.
generic mapAssocie les valeurs aux paramètres d’une instance.
Association nomméeUtilise nom => valeur et améliore la maintenance.
Association positionnelleDépend strictement de l’ordre.
for generateRépète une structure matérielle un nombre statique de fois.
if generateInclut du matériel selon une condition statique.
HiérarchieAssemble des composants paramétrables.
BibliothèqueRegroupe des unités compilées et réutilisables.
Validation séparéeTeste chaque bloc avant intégration.
NInfluence les largeurs, ressources et parfois le timing.

 

Autoévaluation

1. Quand un générique est-il évalué ?

Réponse : Lors de l’élaboration.

2. Quel type convient à une largeur strictement positive ?

Réponse : positive.

3. Quel mot-clé passe les valeurs des génériques ?

Réponse : generic map.

4. Quelle association est la plus lisible ?

Réponse : L’association par nom.

5. Que produit for generate ?

Réponse : Plusieurs copies de matériel concurrent.

6. Une condition generate peut-elle dépendre de enable ?

Réponse : Non, si enable est un signal dynamique.

7. Pourquoi retenues a-t-il N+1 bits ?

Réponse : Pour la retenue d’entrée et les N retenues de sortie.

8. Quel avantage offre un composant paramétrable ?

Réponse : Une source unique pour plusieurs tailles.

9. Comment vérifier une configuration invalide ?

Réponse : Avec un type restrictif et des assertions.

10. Que faut-il comparer pour N = 4, 8 et 16 ?

Réponse : Fonction, ressources et timing.

Exercice de consolidation

Créer un bloc générique qui effectue un AND bit à bit entre deux vecteurs de N bits. Ajouter un générique INVERSION permettant de choisir entre AND et NAND lors de l’élaboration.

Correction proposée — Entité  |  VHDL
library IEEE;
use IEEE.STD_LOGIC_1164.ALL;

entity bloc_and_generique is
    generic (
        N         : positive := 8;
        INVERSION : boolean  := false
    );
    port (
        a, b : in  std_logic_vector(N-1 downto 0);
        y    : out std_logic_vector(N-1 downto 0)
    );
end entity bloc_and_generique;

 

Correction proposée — Architecture  |  VHDL
architecture rtl of bloc_and_generique is
    signal and_i : std_logic_vector(N-1 downto 0);
begin
    and_i <= a and b;

    GEN_AND : if not INVERSION generate
    begin
        y <= and_i;
    end generate GEN_AND;

    GEN_NAND : if INVERSION generate
    begin
        y <= not and_i;
    end generate GEN_NAND;
end architecture rtl;

 

Suite du cours — Transition vers la suite

La maîtrise des composants génériques et de la hiérarchie prépare l’étude des circuits séquentiels : horloges, bascules, registres, resets et synchronisation.