Chapitre 8 — Styles de description VHDL
Objectif — Finalité du chapitre Comprendre les trois grands styles de description VHDL — flot de données, comportemental et structurel — puis choisir et combiner ces styles selon le niveau d’abstraction, la réutilisabilité et la complexité du circuit. |
Objectifs pédagogiques
À la fin de ce chapitre, l’étudiant devra être capable de :
- définir le style par flot de données ;
- traduire des équations logiques en affectations concurrentes ;
- utiliser des processus pour une description comportementale ;
- décomposer un circuit en sous-composants réutilisables ;
- déclarer et instancier un composant ;
- réaliser les connexions avec port map ;
- utiliser l’instanciation directe d’entité ;
- distinguer association par position et association par nom ;
- comparer les styles selon la lisibilité, la réutilisabilité et l’abstraction ;
- combiner plusieurs styles dans une architecture hiérarchique ;
- concevoir un additionneur 4 bits à partir de demi-additionneurs et d’additionneurs complets ;
- comparer une version structurelle à une version comportementale.
Prérequis
- entités, architectures, ports et signaux internes ;
- affectations concurrentes ;
- processus, instructions if et case ;
- types std_logic, std_logic_vector et unsigned ;
- instanciation directe rencontrée dans les travaux pratiques précédents ;
- tables de vérité du demi-additionneur et de l’additionneur complet.
Organisation du chapitre
Section | Contenu | Compétence principale |
|---|---|---|
| 8.1 | Description par flot de données | Exprimer directement les relations entre signaux |
| 8.2 | Description comportementale | Décrire un algorithme ou une décision dans un processus |
| 8.3 | Description structurelle | Assembler des sous-composants |
| 8.4 | Instanciation directe | Référencer une entité compilée sans composant intermédiaire |
| 8.5 | Comparaison des styles | Choisir le niveau d’abstraction adapté |
| TP 5 | Additionneur hiérarchique | Concevoir et vérifier un système à plusieurs niveaux |
Concept clé — Styles complémentaires Les styles de description ne sont pas exclusifs. Un projet réel combine fréquemment une architecture structurelle au niveau supérieur, des processus comportementaux dans les contrôleurs et des équations de flot de données pour les fonctions simples. |
8.1 Description par flot de données
Le style par flot de données décrit les relations entre les entrées, les signaux internes et les sorties. Il met l’accent sur la circulation et la transformation des données plutôt que sur la décomposition physique en composants.
8.1.1 Principe
Les sorties sont écrites sous forme d’équations ou d’affectations concurrentes. Chaque équation est active en permanence et est réévaluée lorsque l’un de ses opérandes change.
| Porte logique simple | VHDL |
| architecture flot_donnees of porte_et is begin s <= a and b; end architecture flot_donnees; |
8.1.2 Équations logiques
Une fonction booléenne peut être transcrite presque directement en VHDL. Les parenthèses rendent les niveaux logiques et la priorité explicites.
| Fonction combinatoire | VHDL |
| architecture flot_donnees of fonction_logique is begin y <= (a and b) or (c and not d); end architecture flot_donnees; |
Élément de l’équation | Matériel probable |
|---|---|
| a and b | Porte AND |
| not d | Inverseur |
| c and not d | Porte AND |
| Somme logique des deux termes | Porte OR |
8.1.3 Affectations concurrentes
| Décomposition avec signaux intermédiaires | VHDL |
| architecture flot_donnees of circuit is signal terme_1 : std_logic; signal terme_2 : std_logic; begin terme_1 <= a and b; terme_2 <= c xor d; y <= terme_1 or terme_2; end architecture flot_donnees; |
Les trois affectations fonctionnent en parallèle. L’ordre de leur écriture n’impose pas un ordre d’exécution.
8.1.4 Circuits combinatoires simples
- portes logiques ;
- comparateurs élémentaires ;
- multiplexeurs ;
- décodeurs ;
- encodeurs simples ;
- additionneurs combinatoires ;
- opérations de masquage et de concaténation.
| Multiplexeur 2 vers 1 | VHDL |
| architecture flot_donnees of mux2 is begin y <= a when sel = '0' else b; end architecture flot_donnees; |
| Décodeur 2 vers 4 | VHDL |
| architecture flot_donnees of decodeur2_4 is begin with entree select sortie <= "0001" when "00", "0010" when "01", "0100" when "10", "1000" when others; end architecture flot_donnees; |
8.1.5 Opérations vectorielles
| Masquage d’un bus | VHDL |
| architecture flot_donnees of masque_bus is begin sortie <= donnees and masque; end architecture flot_donnees; |
| Concaténation de champs | VHDL |
| trame <= entete & donnee & controle; |
8.1.6 Opérations arithmétiques
Le style par flot de données convient aussi aux opérations arithmétiques lorsque les types et les largeurs sont explicites.
| Addition non signée avec retenue | VHDL |
| architecture flot_donnees of additionneur is begin somme <= resize(a, somme'length) + resize(b, somme'length); end architecture flot_donnees; |
8.1.7 Avantages du flot de données
- code compact pour les fonctions simples ;
- correspondance directe entre équations et matériel ;
- concurrence visible ;
- facilité de lecture pour un petit réseau combinatoire ;
- synthèse généralement prévisible ;
- peu de structure syntaxique.
8.1.8 Limites du flot de données
- expressions longues difficiles à maintenir ;
- description moins naturelle pour une logique algorithmique complexe ;
- gestion de nombreuses conditions parfois peu lisible ;
- réutilisation limitée si le circuit n’est pas décomposé ;
- hiérarchie matérielle moins explicite.
Choix — Quand l’utiliser ? Le flot de données est particulièrement adapté aux petits circuits combinatoires, aux équations booléennes, aux connexions directes et aux opérations vectorielles simples. |
8.1.9 Exemple : comparateur simple
| Comparateur non signé | VHDL |
| architecture flot_donnees of comparateur is begin egal <= '1' when a = b else '0'; inferieur <= '1' when unsigned(a) < unsigned(b) else '0'; superieur <= '1' when unsigned(a) > unsigned(b) else '0'; end architecture flot_donnees; |
8.1.10 Exemple : demi-additionneur
Le demi-additionneur possède deux entrées et deux sorties. La somme est obtenue par XOR et la retenue par AND.
| Demi-additionneur par flot de données | VHDL |
| library IEEE; use IEEE.STD_LOGIC_1164.ALL; entity demi_additionneur is port ( a, b : in std_logic; somme : out std_logic; retenue : out std_logic ); end entity demi_additionneur; architecture flot_donnees of demi_additionneur is begin somme <= a xor b; retenue <= a and b; end architecture flot_donnees; |
a | b | somme | retenue |
|---|---|---|---|
| 0 | 0 | 0 | 0 |
| 0 | 1 | 1 | 0 |
| 1 | 0 | 1 | 0 |
| 1 | 1 | 0 | 1 |
8.2 Description comportementale
Le style comportemental décrit ce que le circuit doit faire à l’aide d’instructions séquentielles regroupées dans des processus. Le code se rapproche d’un algorithme, tout en décrivant du matériel synthétisable.
8.2.1 Utilisation des processus
| Processus combinatoire | VHDL |
| architecture comportementale of mux2 is begin process(all) begin if sel = '0' then y <= a; else y <= b; end if; end process; end architecture comportementale; |
Le processus est concurrent vis-à-vis du reste de l’architecture, mais ses instructions internes sont exécutées séquentiellement.
8.2.2 Description algorithmique
Une description algorithmique exprime des étapes de calcul, des décisions, des valeurs par défaut et des boucles. Elle est utile lorsque la fonction est plus simple à comprendre sous forme d’algorithme que sous forme d’équation.
| Comptage des bits à 1 | VHDL |
| architecture comportementale of compteur_uns is begin process(all) variable total : natural range 0 to donnees'length; begin total := 0; for i in donnees'range loop if donnees(i) = '1' then total := total + 1; end if; end loop; nombre <= to_unsigned(total, nombre'length); end process; end architecture comportementale; |
8.2.3 Conditions avec if
| Comparateur comportemental | VHDL |
| process(all) begin egal <= '0'; inferieur <= '0'; superieur <= '0'; if unsigned(a) = unsigned(b) then egal <= '1'; elsif unsigned(a) < unsigned(b) then inferieur <= '1'; else superieur <= '1'; end if; end process; |
8.2.4 Sélections avec case
| Unité logique comportementale | VHDL |
| process(all) begin case operation is when "00" => y <= a and b; when "01" => y <= a or b; when "10" => y <= a xor b; when others => y <= not a; end case; end process; |
8.2.5 Valeurs par défaut
Un processus combinatoire doit définir toutes ses sorties pour tous les chemins. Une valeur par défaut réduit le risque de latch.
| Valeurs par défaut | VHDL |
| process(all) begin y <= (others => '0'); valide <= '0'; if enable = '1' then y <= resultat; valide <= '1'; end if; end process; |
8.2.6 Variables locales
| Calcul intermédiaire | VHDL |
| process(all) variable somme_v : unsigned(8 downto 0); begin somme_v := resize(a, 9) + resize(b, 9); somme <= somme_v(7 downto 0); retenue <= somme_v(8); end process; |
8.2.7 Description combinatoire et séquentielle
Type de processus | Rôle | Exemple |
|---|---|---|
| Combinatoire | Sorties fonction immédiate des entrées | Multiplexeur, comparateur |
| Cadencé | Mémorise les données au front d’horloge | Registre, compteur |
| Avec reset asynchrone | Initialisation immédiate | Registre réinitialisable |
| Banc de test | Produit des stimuli temporels | Processus avec wait |
| Registre comportemental | VHDL |
| architecture comportementale of registre is begin process(clk, reset) begin if reset = '1' then q <= (others => '0'); elsif rising_edge(clk) then q <= d; end if; end process; end architecture comportementale; |
8.2.8 Avantages du style comportemental
- adapté aux décisions complexes ;
- description naturelle des machines à états ;
- utilisation de variables et de boucles ;
- regroupement cohérent des sorties d’une fonction ;
- lecture proche d’un algorithme ;
- facilité d’évolution des conditions.
8.2.9 Limites du style comportemental
- le matériel inféré peut être moins évident pour un débutant ;
- une affectation incomplète peut créer un latch ;
- une longue chaîne if/elsif peut augmenter le chemin critique ;
- certaines constructions algorithmiques ne sont pas synthétisables ;
- les boucles doivent être bornées et maîtrisées.
Attention — Comportemental ne signifie pas logiciel Même si le code utilise des instructions algorithmiques, il est traduit en portes, registres, multiplexeurs et réseaux combinatoires. La consommation de ressources dépend de la structure décrite. |
8.2.10 Exemple : demi-additionneur comportemental
| Version avec case | VHDL |
| architecture comportementale of demi_additionneur is begin process(all) begin case a & b is when "00" => somme <= '0'; retenue <= '0'; when "01" | "10" => somme <= '1'; retenue <= '0'; when others => somme <= '0'; retenue <= '1'; end case; end process; end architecture comportementale; |
8.3 Description structurelle
Le style structurel décrit un circuit comme un assemblage de sous-composants. Il rend explicites la hiérarchie, les instances et les connexions, à la manière d’un schéma électronique textuel.
8.3.1 Décomposition en sous-composants
Un système complexe est divisé en blocs plus simples, chacun possédant sa propre entité et une architecture validée.
Système | Sous-composants possibles |
|---|---|
| Additionneur 4 bits | Quatre additionneurs complets |
| Processeur simple | UAL, registres, contrôleur, mémoire |
| Interface UART | Générateur de baud rate, émetteur, récepteur |
| Chronomètre | Diviseur de fréquence, compteurs, décodeur sept segments |
| Filtre numérique | Multiplicateurs, additionneurs, registres |
8.3.2 Signaux internes
Les sous-composants sont reliés par des signaux internes. Ces signaux correspondent aux fils du niveau hiérarchique.
| Déclaration de connexions internes | VHDL |
| architecture structurelle of circuit is signal sortie_et : std_logic; signal sortie_ou : std_logic; begin -- Instances et connexions end architecture structurelle; |
8.3.3 Déclaration de composants
Dans la forme traditionnelle, l’architecture déclare une interface de composant avant de l’instancier. La déclaration doit être compatible avec l’entité qui sera liée.
| Déclaration d’un composant | VHDL |
| component and_gate is port ( a : in std_logic; b : in std_logic; s : out std_logic ); end component; |
8.3.4 Instanciation d’un composant
| Instance d’une porte AND | VHDL |
| U1 : and_gate port map ( a => x, b => y, s => z ); |
U1 est l’étiquette unique de l’instance. and_gate est le nom du composant déclaré. La clause port map associe les ports formels du composant aux signaux réels du niveau supérieur.
8.3.5 Connexion par port map
Terme | Définition | Exemple |
|---|---|---|
| Port formel | Port déclaré dans le composant ou l’entité | a, b, s |
| Signal réel | Signal ou port du niveau supérieur | x, y, z |
| Association | Lien entre le formel et le réel | a => x |
| Étiquette d’instance | Nom unique de l’exemplaire | U1 |
8.3.6 Association par nom
| Association nommée | VHDL |
| U1 : and_gate port map ( a => x, b => y, s => z ); |
- ordre libre ;
- lecture explicite ;
- maintenance plus sûre ;
- adaptée aux composants possédant de nombreux ports.
8.3.7 Association par position
| Association positionnelle | VHDL |
| U1 : and_gate port map ( x, y, z ); |
Les signaux sont associés selon l’ordre de déclaration des ports. Une modification de l’interface peut rendre cette écriture dangereuse.
Bonne pratique — Recommandation Privilégier l’association par nom dans les supports pédagogiques et les projets maintenables. |
8.3.8 Exemple complet traditionnel
| Entité de niveau supérieur | VHDL |
| library IEEE; use IEEE.STD_LOGIC_1164.ALL; entity circuit_structurel is port ( a, b, c : in std_logic; y : out std_logic ); end entity circuit_structurel; |
| Architecture structurelle — Partie 1 | VHDL |
| architecture structurelle of circuit_structurel is component and_gate is port ( a, b : in std_logic; s : out std_logic ); end component; component or_gate is port ( a, b : in std_logic; s : out std_logic ); end component; signal s_et : std_logic; begin |
| Architecture structurelle — Partie 2 | VHDL |
| U_AND : and_gate port map ( a => a, b => b, s => s_et ); U_OR : or_gate port map ( a => s_et, b => c, s => y ); end architecture structurelle; |
8.3.9 Liaison des composants
Lors de l’élaboration, l’outil doit déterminer quelle entité et quelle architecture correspondent à chaque déclaration de composant. Cette liaison peut être implicite, définie par une configuration ou évitée grâce à l’instanciation directe d’entité.
8.3.10 Génériques dans une instance
| Composant paramétrable | VHDL |
| component registre 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 component; |
| Association du générique | VHDL |
| U_REG : registre generic map ( N => 16 ) port map ( clk => clk, d => donnee_16bits, q => sortie_16bits ); |
8.3.11 Avantages du style structurel
- hiérarchie matérielle explicite ;
- réutilisation des composants validés ;
- développement en équipe facilité ;
- tests unitaires possibles pour chaque bloc ;
- correspondance naturelle avec une architecture système ;
- remplacement d’une implémentation sans modifier les interfaces.
8.3.12 Limites du style structurel
- code plus long ;
- nombreux signaux internes ;
- risque d’erreurs de connexion ;
- déclarations de composants redondantes avec la forme traditionnelle ;
- peu adapté à une fonction logique très simple ;
- hiérarchie excessive si les blocs sont trop petits.
Choix — Quand l’utiliser ? Le style structurel devient particulièrement utile lorsque le système possède plusieurs blocs réutilisables, des interfaces stables et une hiérarchie fonctionnelle claire. |
8.4 Instanciation directe d’entité
L’instanciation directe référence explicitement une entité déjà compilée, sa bibliothèque et éventuellement son architecture. Elle évite la déclaration préalable d’un composant.
8.4.1 Syntaxe demandée
| Instanciation directe | VHDL |
| U1 : entity work.and_gate port map ( a => x, b => y, s => z ); |
Le mot entity indique une instanciation directe. work représente la bibliothèque de travail. and_gate est l’entité à instancier.
8.4.2 Sélection explicite de l’architecture
| Entité et architecture | VHDL |
| U1 : entity work.and_gate(rtl) port map ( a => x, b => y, s => z ); |
La mention (rtl) sélectionne l’architecture nommée rtl. Sans cette mention, l’outil applique ses règles de liaison.
8.4.3 Avantages
- aucune déclaration de composant à dupliquer ;
- référence explicite à la bibliothèque ;
- possibilité de sélectionner une architecture ;
- réduction des incompatibilités entre composant et entité ;
- code plus court et plus facile à maintenir.
8.4.4 Comparaison avec l’instanciation traditionnelle
Critère | Composant déclaré | Entité directe |
|---|---|---|
| Déclaration locale | Nécessaire | Non |
| Bibliothèque visible | Liaison séparée | Écrite dans l’instance |
| Choix d’architecture | Configuration ou liaison | Possible avec entité(architecture) |
| Risque d’interface désynchronisée | Plus élevé | Réduit |
| Compatibilité avec anciens styles/outils | Très large | Large dans les outils modernes |
8.4.5 Instanciation directe avec générique
| Registre générique | VHDL |
| U_REG16 : entity work.registre(rtl) generic map ( N => 16 ) port map ( clk => clk, d => entree_16bits, q => sortie_16bits ); |
8.4.6 Instances multiples
| Deux instances de la même entité | VHDL |
| U_AND1 : entity work.and_gate(rtl) port map ( a => a, b => b, s => s1 ); U_AND2 : entity work.and_gate(rtl) port map ( a => c, b => d, s => s2 ); |
Chaque instance possède ses propres connexions et correspond à une ressource matérielle distincte, sauf optimisation possible par l’outil.
8.4.7 Ports laissés ouverts
| Sortie non utilisée | VHDL |
| U_BLOC : entity work.bloc(rtl) port map ( entree => entree, sortie_utile => sortie, diagnostic => open ); |
Le mot open peut être utilisé lorsqu’un port de sortie n’est pas connecté. Son usage sur les ports d’entrée dépend de l’existence d’une valeur par défaut et des règles du langage.
8.4.8 Connexion à une constante
| Entrée fixée | VHDL |
| U_BLOC : entity work.bloc(rtl) port map ( enable => '1', entree => donnee, sortie => resultat ); |
8.4.9 Ordre de compilation
L’entité instanciée doit être analysée avant le niveau qui l’utilise. L’ordre de compilation est donc important.
| Ordre indicatif | Terminal |
| ghdl -a --std=08 and_gate.vhd ghdl -a --std=08 top_level.vhd |
8.4.10 Erreurs fréquentes
Erreur | Cause | Correction |
|---|---|---|
| Entité introuvable | Fichier non compilé ou mauvaise bibliothèque | Compiler dans le bon ordre |
| Architecture introuvable | Nom incorrect | Vérifier l’architecture sélectionnée |
| Port inconnu | Nom différent dans l’entité | Comparer le port map à l’interface |
| Largeur incompatible | Vecteurs de tailles différentes | Adapter ou convertir |
| Type incompatible | signed, unsigned ou std_logic_vector mélangés | Ajouter une conversion explicite |
| Étiquette dupliquée | Deux instances portent le même nom | Utiliser une étiquette unique |
8.5 Comparaison des styles
Le choix d’un style dépend de la taille du circuit, de son niveau hiérarchique, de la nature de la fonction et de la facilité avec laquelle le concepteur souhaite relier le code au matériel.
8.5.1 Comparaison générale
Critère | Flot de données | Comportemental | Structurel |
|---|---|---|---|
| Vision principale | Relations entre signaux | Fonction ou algorithme | Assemblage de blocs |
| Instructions typiques | Affectations concurrentes | Processus, if, case, boucles | Instances, port map |
| Abstraction | Moyenne | Élevée | Faible à moyenne |
| Hiérarchie explicite | Faible | Faible à moyenne | Forte |
| Usage | Combinatoire simple | Logique complexe et cadencée | Systèmes hiérarchiques |
| Longueur du code | Courte | Moyenne | Souvent longue |
8.5.2 Lisibilité
La lisibilité dépend du type de problème. Une équation simple est plus lisible en flot de données, tandis qu’un contrôleur complexe est souvent plus clair dans un processus.
Fonction | Style généralement le plus lisible |
|---|---|
| Porte logique | Flot de données |
| Multiplexeur simple | Flot de données ou comportemental |
| Encodeur prioritaire | Comportemental |
| Machine à états | Comportemental |
| Additionneur à propagation de retenue étudié comme réseau | Structurel |
| Système composé de plusieurs sous-fonctions | Structurel au niveau supérieur |
8.5.3 Réutilisabilité
La réutilisabilité repose surtout sur des entités bien définies, des génériques et une hiérarchie stable. Le style structurel facilite l’assemblage de blocs réutilisables, mais chaque bloc peut être décrit par n’importe quel style.
- interfaces clairement définies ;
- génériques pour les dimensions ;
- absence de dépendance inutile au niveau supérieur ;
- bancs de test unitaires ;
- bibliothèques de composants validés.
8.5.4 Niveau d’abstraction
Niveau | Description | Exemple |
|---|---|---|
| Comportemental élevé | Ce que le circuit doit faire | if, case, algorithme |
| RTL | Transferts, registres et logique combinatoire | processus cadencé et logique de prochaine valeur |
| Flot de données | Relations entre valeurs | équations et affectations |
| Structurel | Blocs et connexions | instances de composants |
8.5.5 Facilité de synthèse
Les trois styles sont synthétisables lorsqu’ils utilisent des constructions prises en charge. La facilité de synthèse dépend davantage de la clarté du matériel décrit que du nom du style.
Situation | Impact possible |
|---|---|
| Équation combinatoire simple | Synthèse directe et optimisable |
| Processus combinatoire complet | Synthèse correcte sans latch |
| Boucle statique | Déroulement matériel |
| Processus avec affectations incomplètes | Latch involontaire |
| Hiérarchie structurelle profonde | Conservation ou aplatissement selon l’outil |
| Division algorithmique complexe | Ressources importantes, quel que soit le style |
8.5.6 Adaptation à la complexité
Complexité du bloc | Style conseillé |
|---|---|
| Très faible : porte, équation | Flot de données |
| Faible : multiplexeur, décodeur | Flot de données ou comportemental |
| Moyenne : UAL, encodeur prioritaire | Comportemental |
| Élevée : contrôleur, protocole | Comportemental structuré |
| Système de plusieurs modules | Structurel au niveau supérieur |
| Projet complet | Combinaison des trois styles |
8.5.7 Un même circuit, trois descriptions
Considérons la fonction y = (a AND b) OR c.
| Version flot de données | VHDL |
| architecture flot_donnees of exemple is begin y <= (a and b) or c; end architecture flot_donnees; |
| Version comportementale | VHDL |
| architecture comportementale of exemple is begin process(all) begin if a = '1' and b = '1' then y <= '1'; else y <= c; end if; end process; end architecture comportementale; |
| Version structurelle | VHDL |
| architecture structurelle of exemple is signal s_et : std_logic; begin U_ET : entity work.and_gate(rtl) port map (a => a, b => b, s => s_et); U_OU : entity work.or_gate(rtl) port map (a => s_et, b => c, s => y); end architecture structurelle; |
Le synthétiseur peut produire une logique équivalente pour les trois architectures. La différence principale se situe dans la manière dont le concepteur exprime et organise la fonction.
8.5.8 Style mixte
| Architecture mixte — Partie 1 | VHDL |
| architecture mixte of systeme is signal resultat_alu : std_logic_vector(7 downto 0); signal valide_i : std_logic; begin U_ALU : entity work.ual(comportementale) port map ( a => a, b => b, operation => operation, resultat => resultat_alu ); |
| Architecture mixte — Partie 2 | VHDL |
| valide_i <= enable and not erreur; process(clk) begin if rising_edge(clk) then if valide_i = '1' then sortie <= resultat_alu; end if; end if; end process; end architecture mixte; |
Pratique — Approche réaliste Le style mixte est généralement le plus adapté aux projets réels : hiérarchie structurelle, calculs combinatoires par flot de données et logique de contrôle comportementale. |
8.5.9 Choix guidé
Question | Si la réponse est oui | Style à envisager |
|---|---|---|
| La fonction s’écrit-elle en quelques équations ? | Expression directe | Flot de données |
| Plusieurs conditions ou boucles sont-elles nécessaires ? | Algorithme interne | Comportemental |
| Le bloc assemble-t-il des modules existants ? | Hiérarchie et connexions | Structurel |
| Faut-il visualiser clairement la propagation entre étages ? | Réseau explicite | Structurel |
| Le circuit doit-il évoluer fréquemment ? | Regroupement fonctionnel | Comportemental |
| Le système possède-t-il plusieurs niveaux ? | Combinaison | Style mixte |
8.5.10 Erreurs de conception à éviter
Erreur | Conséquence | Prévention |
|---|---|---|
| Décomposer une porte en trop de sous-composants | Code inutilement long | Conserver le flot de données |
| Écrire une UAL complexe en une seule expression | Lisibilité faible | Utiliser un processus case |
| Dupliquer les déclarations de composants | Maintenance difficile | Instanciation directe |
| Utiliser un processus incomplet | Latch | Valeurs par défaut |
| Mélanger les niveaux d’abstraction sans organisation | Architecture confuse | Définir des frontières de blocs |
| Instancier avant compilation | Entité introuvable | Gérer les dépendances |
Méthode de choix et de combinaison des styles
- Définir la fonction et les interfaces du bloc.
- Identifier les sous-fonctions réutilisables.
- Choisir le niveau d’abstraction de chaque sous-fonction.
- Utiliser le flot de données pour les équations simples.
- Utiliser un processus pour les décisions et algorithmes.
- Créer une hiérarchie structurelle pour assembler les blocs.
- Privilégier l’instanciation directe d’entité.
- Écrire un banc de test pour chaque niveau.
- Comparer le schéma RTL au matériel attendu.
- Mesurer les ressources et les performances avant de conclure qu’un style est meilleur.
Checklist d’une architecture structurelle
- Chaque entité instanciée est-elle compilée ?
- Chaque instance possède-t-elle une étiquette unique ?
- Les noms de ports sont-ils corrects ?
- Les types et largeurs sont-ils compatibles ?
- Les signaux internes sont-ils déclarés ?
- Aucun signal ne possède-t-il plusieurs pilotes involontaires ?
- Les génériques sont-ils correctement associés ?
- Le banc de test couvre-t-il la propagation entre les niveaux ?
Travaux pratiques
TP 5 — Additionneur hiérarchique
1. Objectifs du TP
- décrire un demi-additionneur par flot de données ;
- construire un additionneur complet à partir de deux demi-additionneurs ;
- construire un additionneur 4 bits par propagation de retenue ;
- instancier plusieurs composants ;
- utiliser des signaux internes pour les retenues ;
- écrire une version comportementale équivalente ;
- simuler les deux versions ;
- comparer les résultats, le code et le schéma RTL.
2. Rappel fonctionnel
Bloc | Entrées | Sorties | Fonction |
|---|---|---|---|
| Demi-additionneur | a, b | somme, retenue | Addition de deux bits |
| Additionneur complet | a, b, cin | somme, cout | Addition de trois bits |
| Additionneur 4 bits | a(3:0), b(3:0), cin | somme(3:0), cout | Addition de deux mots |
3. Organisation des fichiers
| Arborescence proposée | Arborescence |
| tp5_additionneur_hierarchique/ ├── src/ │ ├── demi_additionneur.vhd │ ├── additionneur_complet.vhd │ ├── additionneur4_structurel.vhd │ └── additionneur4_comportemental.vhd └── sim/ ├── tb_demi_additionneur.vhd ├── tb_additionneur_complet.vhd └── tb_additionneur4.vhd |
4. Partie A — Demi-additionneur
| demi_additionneur.vhd | VHDL |
| library IEEE; use IEEE.STD_LOGIC_1164.ALL; entity demi_additionneur is port ( a, b : in std_logic; somme : out std_logic; retenue : out std_logic ); end entity demi_additionneur; architecture flot_donnees of demi_additionneur is begin somme <= a xor b; retenue <= a and b; end architecture flot_donnees; |
5. Banc de test du demi-additionneur
| tb_demi_additionneur.vhd — Partie 1 | VHDL |
| library IEEE; use IEEE.STD_LOGIC_1164.ALL; use STD.ENV.ALL; entity tb_demi_additionneur is end entity tb_demi_additionneur; architecture simulation of tb_demi_additionneur is signal a, b : std_logic := '0'; signal somme : std_logic; signal retenue : std_logic; begin DUT : entity work.demi_additionneur(flot_donnees) port map ( a => a, b => b, somme => somme, retenue => retenue ); |
| tb_demi_additionneur.vhd — Partie 2 | VHDL |
| stimuli : process begin a <= '0'; b <= '0'; wait for 10 ns; assert somme = '0' and retenue = '0' severity error; a <= '0'; b <= '1'; wait for 10 ns; assert somme = '1' and retenue = '0' severity error; a <= '1'; b <= '0'; wait for 10 ns; assert somme = '1' and retenue = '0' severity error; a <= '1'; b <= '1'; wait for 10 ns; assert somme = '0' and retenue = '1' severity error; report "Demi-additionneur valide" severity note; stop; wait; end process; end architecture simulation; |
6. Partie B — Additionneur complet
Un additionneur complet peut être construit avec deux demi-additionneurs et une porte OR. Le premier additionne a et b. Le second additionne la somme intermédiaire et cin.
| additionneur_complet.vhd — Entité | 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; |
| additionneur_complet.vhd — Architecture, partie 1 | VHDL |
| architecture structurelle of additionneur_complet is signal somme_1 : std_logic; signal retenue_1 : std_logic; signal retenue_2 : std_logic; begin U_HA1 : entity work.demi_additionneur(flot_donnees) port map ( a => a, b => b, somme => somme_1, retenue => retenue_1 ); |
| additionneur_complet.vhd — Architecture, partie 2 | VHDL |
| U_HA2 : entity work.demi_additionneur(flot_donnees) port map ( a => somme_1, b => cin, somme => somme, retenue => retenue_2 ); cout <= retenue_1 or retenue_2; end architecture structurelle; |
7. Équations de l’additionneur complet
| Équations équivalentes | Équations |
| somme = a XOR b XOR cin cout = (a AND b) OR (a AND cin) OR (b AND cin) |
a | b | cin | somme | cout |
|---|---|---|---|---|
| 0 | 0 | 0 | 0 | 0 |
| 0 | 0 | 1 | 1 | 0 |
| 0 | 1 | 0 | 1 | 0 |
| 0 | 1 | 1 | 0 | 1 |
| 1 | 0 | 0 | 1 | 0 |
| 1 | 0 | 1 | 0 | 1 |
| 1 | 1 | 0 | 0 | 1 |
| 1 | 1 | 1 | 1 | 1 |
8. Banc de test de l’additionneur complet
| tb_additionneur_complet.vhd — Partie 1 | VHDL |
| library IEEE; use IEEE.STD_LOGIC_1164.ALL; use IEEE.NUMERIC_STD.ALL; use STD.ENV.ALL; entity tb_additionneur_complet is end entity tb_additionneur_complet; architecture simulation of tb_additionneur_complet is signal entree : std_logic_vector(2 downto 0) := (others => '0'); signal somme : std_logic; signal cout : std_logic; begin DUT : entity work.additionneur_complet(structurelle) port map ( a => entree(2), b => entree(1), cin => entree(0), somme => somme, cout => cout ); |
| tb_additionneur_complet.vhd — Partie 2A | VHDL |
| stimuli : process variable valeur : natural; variable attendu : unsigned(1 downto 0); begin for i in 0 to 7 loop entree <= std_logic_vector(to_unsigned(i, 3)); wait for 1 ns; valeur := to_integer(unsigned(entree(2 downto 2))) + to_integer(unsigned(entree(1 downto 1))) + to_integer(unsigned(entree(0 downto 0))); |
| tb_additionneur_complet.vhd — Partie 2B | VHDL |
| attendu := to_unsigned(valeur, 2); assert cout = attendu(1) and somme = attendu(0) report "Erreur additionneur complet" severity error; wait for 9 ns; end loop; report "Additionneur complet valide" severity note; stop; wait; end process; end architecture simulation; |
9. Partie C — Additionneur 4 bits structurel
L’additionneur 4 bits est construit avec quatre additionneurs complets. La retenue de chaque étage alimente l’entrée cin de l’étage suivant.
| additionneur4_structurel.vhd — Entité | VHDL |
| library IEEE; use IEEE.STD_LOGIC_1164.ALL; entity additionneur4_structurel is port ( a, b : in std_logic_vector(3 downto 0); cin : in std_logic; somme : out std_logic_vector(3 downto 0); cout : out std_logic ); end entity additionneur4_structurel; |
| additionneur4_structurel.vhd — Architecture, partie 1 | VHDL |
| architecture structurelle of additionneur4_structurel is signal retenues : std_logic_vector(4 downto 0); begin retenues(0) <= cin; U_FA0 : entity work.additionneur_complet(structurelle) port map ( a => a(0), b => b(0), cin => retenues(0), somme => somme(0), cout => retenues(1) ); U_FA1 : entity work.additionneur_complet(structurelle) port map ( a => a(1), b => b(1), cin => retenues(1), somme => somme(1), cout => retenues(2) ); |
| additionneur4_structurel.vhd — Architecture, partie 2 | VHDL |
| U_FA2 : entity work.additionneur_complet(structurelle) port map ( a => a(2), b => b(2), cin => retenues(2), somme => somme(2), cout => retenues(3) ); U_FA3 : entity work.additionneur_complet(structurelle) port map ( a => a(3), b => b(3), cin => retenues(3), somme => somme(3), cout => retenues(4) ); cout <= retenues(4); end architecture structurelle; |
10. Propagation de la retenue
Étage | Bits additionnés | Retenue d’entrée | Retenue de sortie |
|---|---|---|---|
| 0 | a(0), b(0) | cin | retenues(1) |
| 1 | a(1), b(1) | retenues(1) | retenues(2) |
| 2 | a(2), b(2) | retenues(2) | retenues(3) |
| 3 | a(3), b(3) | retenues(3) | cout |
Architecture — Ripple-carry adder Cette architecture est appelée additionneur à propagation de retenue. Le chemin critique traverse potentiellement les quatre étages, car chaque retenue dépend de l’étage précédent. |
11. Partie D — Version comportementale
| additionneur4_comportemental.vhd — Entité | VHDL |
| library IEEE; use IEEE.STD_LOGIC_1164.ALL; use IEEE.NUMERIC_STD.ALL; entity additionneur4_comportemental is port ( a, b : in std_logic_vector(3 downto 0); cin : in std_logic; somme : out std_logic_vector(3 downto 0); cout : out std_logic ); end entity additionneur4_comportemental; |
| additionneur4_comportemental.vhd — Architecture | VHDL |
| architecture comportementale of additionneur4_comportemental is begin process(all) variable resultat_v : unsigned(4 downto 0); begin resultat_v := resize(unsigned(a), 5) + resize(unsigned(b), 5); if cin = '1' then resultat_v := resultat_v + 1; end if; somme <= std_logic_vector(resultat_v(3 downto 0)); cout <= resultat_v(4); end process; end architecture comportementale; |
12. Variante compacte par flot de données
| Expression concurrente | VHDL |
| resultat <= ('0' & unsigned(a)) + ('0' & unsigned(b)) + to_unsigned( 1 when cin = '1' else 0, 5 ); |
Cette variante est concise mais peut être moins accessible pour un débutant. La version comportementale explicite le traitement de cin.
13. Banc de test commun aux deux versions
| tb_additionneur4.vhd — Déclarations | VHDL |
| library IEEE; use IEEE.STD_LOGIC_1164.ALL; use IEEE.NUMERIC_STD.ALL; use STD.ENV.ALL; entity tb_additionneur4 is end entity tb_additionneur4; architecture simulation of tb_additionneur4 is signal a, b : std_logic_vector(3 downto 0) := (others => '0'); signal cin : std_logic := '0'; signal somme_struct : std_logic_vector(3 downto 0); signal cout_struct : std_logic; signal somme_comp : std_logic_vector(3 downto 0); signal cout_comp : std_logic; begin |
| Instance de la version structurelle | VHDL |
| DUT_STRUCTUREL : entity work.additionneur4_structurel(structurelle) port map ( a => a, b => b, cin => cin, somme => somme_struct, cout => cout_struct ); |
| Instance de la version comportementale | VHDL |
| DUT_COMPORTEMENTAL : entity work.additionneur4_comportemental(comportementale) port map ( a => a, b => b, cin => cin, somme => somme_comp, cout => cout_comp ); |
| Tests exhaustifs — Partie 1A | VHDL |
| stimuli : process variable valeur_attendue : natural range 0 to 31; variable mot_attendu : unsigned(4 downto 0); begin for va in 0 to 15 loop for vb in 0 to 15 loop for vc in 0 to 1 loop a <= std_logic_vector(to_unsigned(va, 4)); b <= std_logic_vector(to_unsigned(vb, 4)); if vc = 0 then cin <= '0'; else cin <= '1'; end if; |
| Tests exhaustifs — Partie 1B | VHDL |
| wait for 1 ns; valeur_attendue := va + vb + vc; mot_attendu := to_unsigned( valeur_attendue, mot_attendu'length ); |
| Tests exhaustifs — Partie 2 | VHDL |
| assert cout_struct = mot_attendu(4) and unsigned(somme_struct) = mot_attendu(3 downto 0) report "Erreur version structurelle" severity error; assert cout_comp = mot_attendu(4) and unsigned(somme_comp) = mot_attendu(3 downto 0) report "Erreur version comportementale" severity error; assert somme_struct = somme_comp and cout_struct = cout_comp report "Les deux versions different" severity error; wait for 1 ns; end loop; end loop; end loop; report "512 combinaisons validees" severity note; stop; wait; end process; end architecture simulation; |
14. Compilation avec GHDL
| Compilation dans l’ordre des dépendances | Terminal |
| ghdl -a --std=08 src/demi_additionneur.vhd ghdl -a --std=08 src/additionneur_complet.vhd ghdl -a --std=08 src/additionneur4_structurel.vhd ghdl -a --std=08 src/additionneur4_comportemental.vhd ghdl -a --std=08 sim/tb_additionneur4.vhd ghdl -e --std=08 tb_additionneur4 ghdl -r --std=08 tb_additionneur4 --vcd=tb_additionneur4.vcd |
| Observation du chronogramme | Terminal |
| gtkwave tb_additionneur4.vcd |
15. Cas de test particuliers
a | b | cin | Résultat sur 5 bits | Interprétation |
|---|---|---|---|---|
| 0000 | 0000 | 0 | 00000 | 0 + 0 |
| 0011 | 0101 | 0 | 01000 | 3 + 5 = 8 |
| 1111 | 0001 | 0 | 10000 | 15 + 1 = 16 |
| 1111 | 1111 | 0 | 11110 | 15 + 15 = 30 |
| 1111 | 1111 | 1 | 11111 | 15 + 15 + 1 = 31 |
16. Comparaison structurelle/comportementale
Critère | Version structurelle | Version comportementale |
|---|---|---|
| Longueur du code | Plus longue | Plus courte |
| Propagation des retenues | Visible | Implicite dans l’opérateur + |
| Réutilisation | Additionneur complet réutilisé | Bloc autonome |
| Compréhension pédagogique | Montre la hiérarchie | Montre la fonction |
| Évolution vers N bits | Generate ou plusieurs instances | Générique et opérateur + |
| Optimisation par synthèse | Possible | Possible |
| Matériel final | Souvent équivalent après optimisation | Souvent équivalent après optimisation |
17. Observations demandées
- Vérifier les quatre cas du demi-additionneur.
- Vérifier les huit cas de l’additionneur complet.
- Observer la propagation des retenues internes.
- Tester les valeurs maximales de l’additionneur 4 bits.
- Comparer les sorties des deux architectures.
- Examiner les schémas RTL générés.
- Comparer le nombre de niveaux hiérarchiques.
- Comparer les ressources et la fréquence maximale après synthèse.
18. Questions d’analyse
1. Pourquoi le demi-additionneur est-il bien adapté au flot de données ?
2. Pourquoi l’additionneur complet peut-il être décrit structurellement avec deux demi-additionneurs ?
3. Quel est le rôle du signal somme_1 ?
4. Pourquoi retenue_1 et retenue_2 sont-elles combinées par OR ?
5. Comment la retenue se propage-t-elle dans l’additionneur 4 bits ?
6. Quelle différence existe entre la hiérarchie du code et le matériel optimisé ?
7. Pourquoi la version comportementale est-elle plus courte ?
8. Les deux versions produisent-elles nécessairement des performances différentes ?
9. Comment généraliser l’additionneur structurel à N bits ?
10. Quel style choisir pour un cours sur la propagation de retenue ?
19. Extensions proposées
- Créer un additionneur 8 bits.
- Utiliser une instruction for generate pour N bits.
- Ajouter un indicateur de débordement signé.
- Créer un additionneur/soustracteur commandé.
- Comparer ripple-carry et carry-lookahead.
- Ajouter un générique définissant la largeur.
- Implanter l’additionneur sur une carte FPGA.
- Afficher le résultat sur des LED ou des afficheurs sept segments.
20. Barème indicatif
Critère | Points |
|---|---|
| Demi-additionneur | 2 |
| Additionneur complet structurel | 3 |
| Additionneur 4 bits structurel | 4 |
| Version comportementale | 3 |
| Bancs de test et assertions | 3 |
| Comparaison des architectures | 2 |
| Analyse du schéma RTL | 2 |
| Qualité du code et du compte rendu | 1 |
| Total | 20 |
Synthèse du chapitre
Notion | Résumé |
|---|---|
| Flot de données | Décrit les relations et équations par affectations concurrentes. |
| Comportemental | Décrit un algorithme avec processus, conditions et boucles. |
| Structurel | Décrit une hiérarchie d’instances et de connexions. |
| Component | Déclaration traditionnelle d’une interface à instancier. |
| port map | Associe les ports formels aux signaux réels. |
| Association nommée | Connexion explicite avec =>. |
| Instanciation directe | Référence entity bibliothèque.nom_entité. |
| Style mixte | Combine hiérarchie, équations et processus. |
| Réutilisabilité | Dépend des interfaces, génériques et tests. |
| Synthèse | Peut optimiser des descriptions différentes vers une logique équivalente. |
Autoévaluation
1. Quel style utilise principalement des affectations concurrentes ?
Réponse : Le flot de données.
2. Quel style utilise principalement des processus ?
Réponse : Le style comportemental.
3. Quel style explicite les sous-composants ?
Réponse : Le style structurel.
4. Quel mot-clé associe les ports d’une instance ?
Réponse : port map.
5. Quel symbole est utilisé pour une association par nom ?
Réponse : =>.
6. Que représente work ?
Réponse : La bibliothèque de travail.
7. Quel avantage offre l’instanciation directe ?
Réponse : Elle évite la déclaration du composant.
8. Un même circuit peut-il avoir plusieurs architectures ?
Réponse : Oui.
9. Des styles différents peuvent-ils produire le même matériel ?
Réponse : Oui, après synthèse et optimisation.
10. Quel style est généralement utilisé au niveau supérieur d’un grand système ?
Réponse : Le style structurel ou mixte.
Exercice de consolidation
Décrire un circuit qui calcule y = (a XOR b) AND enable selon deux architectures : une architecture par flot de données et une architecture structurelle utilisant une porte XOR et une porte AND.
| Architecture par flot de données | VHDL |
| architecture flot_donnees of exercice is begin y <= (a xor b) and enable; end architecture flot_donnees; |
| Architecture structurelle — Partie 1 | VHDL |
| architecture structurelle of exercice is signal s_xor : std_logic; begin U_XOR : entity work.xor_gate(rtl) port map ( a => a, b => b, s => s_xor ); |
| Architecture structurelle — Partie 2 | VHDL |
| U_AND : entity work.and_gate(rtl) port map ( a => s_xor, b => enable, s => y ); end architecture structurelle; |
Suite du cours — Transition vers la suite Après les styles de description, le cours peut approfondir les génériques, la génération de matériel et la conception de composants paramétrables et réutilisables. |