Leçon 7 sur 22

Chapitre 7 — Processus et instructions séquentielles

Objectif — Finalité du chapitre

Maîtriser le processus VHDL et ses instructions séquentielles afin de décrire des circuits combinatoires structurés, des choix complexes et des traitements répétitifs, tout en contrôlant les listes de sensibilité et l’usage des variables.

Objectifs pédagogiques

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

  • identifier la partie déclarative et la partie exécutive d’un processus ;
  • construire une liste de sensibilité correcte ;
  • utiliser les instructions if, elsif, else et case ;
  • choisir entre if et case selon le besoin fonctionnel ;
  • utiliser les boucles for, while et loop dans un contexte synthétisable ;
  • arrêter une boucle avec exit ou exit when ;
  • déclarer et utiliser des variables locales ;
  • expliquer la différence de mise à jour entre une variable et un signal ;
  • distinguer un processus combinatoire d’un processus séquentiel cadencé ;
  • utiliser process(all) en VHDL-2008 ;
  • détecter les listes de sensibilité incomplètes ;
  • concevoir et simuler des circuits combinatoires à l’aide de processus.

Prérequis

  • entités, architectures et signaux internes ;
  • affectations concurrentes ;
  • types std_logic, std_logic_vector, signed et unsigned ;
  • opérateurs logiques, relationnels et arithmétiques ;
  • principes de simulation et bancs de test.

Organisation du chapitre

Section

Contenu

Compétence principale

7.1Structure d’un processusOrganiser un bloc séquentiel
7.2Instructions if/elsif/elseExprimer des décisions prioritaires
7.3Instruction caseSélectionner entre des choix exclusifs
7.4BouclesRépéter un traitement de manière statique ou contrôlée
7.5VariablesEffectuer des calculs locaux immédiats
7.6SensibilitéGarantir une simulation combinatoire correcte
TP 4Circuits combinatoires avec processusConcevoir, tester et corriger plusieurs blocs

 

Concept clé — Deux niveaux de concurrence

Un processus entier est concurrent par rapport aux autres instructions de l’architecture. En revanche, les instructions situées à l’intérieur du processus sont exécutées séquentiellement, dans l’ordre où elles sont écrites.

 

7.1 Définition d’un processus

Un processus est une instruction concurrente qui regroupe une suite d’instructions séquentielles. Il permet d’exprimer des décisions, des calculs intermédiaires, des boucles et des comportements cadencés qui seraient difficiles à écrire avec de simples affectations concurrentes.

7.1.1 Structure générale d’un processus

Forme générale  |   VHDL
etiquette : process(liste_de_sensibilite)
    -- Partie déclarative
    variable temp : std_logic;
begin
    -- Instructions séquentielles
    temp := a and b;
    y <= temp;
end process etiquette;

 

L’étiquette est facultative, mais elle améliore la lisibilité et les messages du simulateur.

7.1.2 Éléments constitutifs

Élément

Rôle

Exemple

ÉtiquetteNomme le processus.logique_sortie : process(...)
Mot-clé processDébute la déclaration du processus.process(a, b, sel)
Liste de sensibilitéIndique les signaux dont un événement réveille le processus.(a, b, sel)
Partie déclarativeContient variables, constantes, types et sous-programmes locaux.variable temp : std_logic;
Mot-clé beginSépare déclarations et instructions.begin
Instructions séquentiellesDécrivent les opérations dans l’ordre.if, case, boucles, affectations
Fin du processusTermine le bloc.end process;

 

7.1.3 Liste de sensibilité

La liste de sensibilité est une liste de signaux. Le processus est exécuté une première fois au début de la simulation, puis à chaque événement sur l’un de ces signaux. Pour un processus combinatoire, elle doit contenir tous les signaux lus.

Processus combinatoire  |  VHDL
process(a, b, sel)
begin
    if sel = '0' then
        y <= a;
    else
        y <= b;
    end if;
end process;

 

Règle — Un signal lu doit être déclaré

Dans cet exemple, a, b et sel sont lus. Tous doivent donc apparaître dans la liste de sensibilité afin que y soit recalculé après chaque changement pertinent.

 

7.1.4 Partie déclarative

La partie déclarative est située entre process(...) et begin. Elle peut contenir des objets et déclarations visibles uniquement à l’intérieur du processus.

  • variables locales ;
  • constantes locales ;
  • types et sous-types locaux ;
  • alias ;
  • fonctions et procédures locales ;
  • fichiers pour un processus de simulation.
Partie déclarative détaillée  |  VHDL
process(a, b, commande)
    variable resultat_v : std_logic_vector(7 downto 0);
    constant ZERO       : std_logic_vector(7 downto 0)
                          := (others => '0');
begin
    resultat_v := ZERO;

    if commande = '1' then
        resultat_v := a and b;
    end if;

    y <= resultat_v;
end process;

 

7.1.5 Instructions séquentielles

Les instructions du corps sont exécutées dans l’ordre. Une variable affectée par := change immédiatement, alors qu’une affectation de signal avec <= planifie une mise à jour.

Ordre séquentiel  |  VHDL
process(a, b, c)
    variable temp : std_logic;
begin
    temp := a and b;
    temp := temp or c;
    y <= temp;
end process;

 

La deuxième instruction utilise immédiatement la valeur de temp calculée par la première.

7.1.6 Processus équivalent à une affectation concurrente

Affectation concurrente  |  VHDL
y <= a and b;

 

Processus équivalent  |  VHDL
process(a, b)
begin
    y <= a and b;
end process;

 

Le matériel inféré est généralement identique. Le processus devient avantageux lorsque plusieurs branches ou calculs intermédiaires sont nécessaires.

7.1.7 Processus avec instruction wait

Un processus sans liste de sensibilité peut utiliser une ou plusieurs instructions wait. Cette forme est courante dans les bancs de test.

Génération de stimuli  |  VHDL
stimuli : process
begin
    a <= '0';
    b <= '0';
    wait for 10 ns;

    a <= '1';
    wait for 10 ns;

    wait;
end process;

 

Attention — Exclusion mutuelle

Un même processus ne doit pas combiner une liste de sensibilité et une instruction wait. On choisit l’une ou l’autre méthode de suspension.

 

7.1.8 Processus combinatoire et processus cadencé

Critère

Processus combinatoire

Processus cadencé

DéclenchementTout changement d’un signal luFront d’horloge, éventuellement reset asynchrone
Mémoire souhaitéeNonOui, registres ou bascules
Liste de sensibilitéToutes les entrées lues ou allHorloge et reset asynchrone
Affectation complèteToutes les sorties dans tous les cheminsLes registres peuvent conserver leur valeur
ExempleMultiplexeur, décodeur, comparateurCompteur, registre, machine à états

 

7.1.9 Processus et pilote de signal

Toutes les affectations d’un signal réalisées dans un même processus appartiennent à un seul pilote.

Deux affectations séquentielles, un seul pilote  |  VHDL
process(a, b, sel)
begin
    y <= a;

    if sel = '1' then
        y <= b;
    end if;
end process;

 

Si sel vaut 1, la dernière affectation exécutée détermine la transaction finale de y. Si sel vaut 0, la valeur par défaut a est conservée.

7.1.10 Nommage et organisation

Processus nommé  |  VHDL
combinaison_sortie : process(all)
    variable valeur_v : std_logic_vector(7 downto 0);
begin
    -- Calculs
end process combinaison_sortie;

 

  • Choisir un nom décrivant le rôle du bloc : calcul_sortie, registre_etat, decode_commande.
  • Éviter des processus trop longs regroupant des fonctions indépendantes.
  • Séparer clairement logique combinatoire et logique cadencée.
  • Définir des valeurs par défaut au début des processus combinatoires.

7.2 Instructions conditionnelles

Les instructions if, elsif et else permettent d’exécuter différentes séquences selon des conditions booléennes. L’ordre des branches introduit une priorité lorsque plusieurs conditions peuvent être vraies.

7.2.1 Instruction if simple

If sans else  |   VHDL
if enable = '1' then
    y <= a;
end if;

 

Dans un processus combinatoire, cette forme est incomplète si y n’a pas reçu de valeur par défaut. Elle peut entraîner la conservation de la valeur précédente et l’inférence d’un latch.

Correction avec valeur par défaut  |  VHDL
y <= '0';

if enable = '1' then
    y <= a;
end if;

 

Bonne pratique — Valeur par défaut

Affecter les sorties au début d’un processus combinatoire simplifie la couverture de tous les chemins et évite les latches involontaires.

 

7.2.2 Structure if / else

Multiplexeur 2 vers 1  |   VHDL
if sel = '0' then
    y <= a;
else
    y <= b;
end if;

 

Les deux branches affectent y : la description est combinatoire et complète.

7.2.3 Structure if / elsif / else

Sélection prioritaire  |  VHDL
if entree(3) = '1' then
    code <= "11";
elsif entree(2) = '1' then
    code <= "10";
elsif entree(1) = '1' then
    code <= "01";
else
    code <= "00";
end if;

 

La première condition vraie est sélectionnée. Les branches suivantes ne sont pas évaluées pour ce passage.

7.2.4 Conditions booléennes

Comparaisons numériques  |  VHDL
if unsigned(a) > unsigned(b) then
    maximum <= a;
else
    maximum <= b;
end if;

 

Combinaison de conditions  |  VHDL
if enable = '1'
   and erreur = '0'
   and compteur < LIMITE then
    autorisation <= '1';
else
    autorisation <= '0';
end if;

 

7.2.5 Conditions imbriquées

If imbriqué  |   VHDL
if enable = '1' then
    if mode = "00" then
        y <= a;
    elsif mode = "01" then
        y <= b;
    else
        y <= c;
    end if;
else
    y <= (others => '0');
end if;

 

L’imbrication est utile lorsque certaines décisions ne sont pertinentes que dans un contexte donné. Une profondeur excessive nuit toutefois à la lisibilité et peut créer une longue chaîne prioritaire.

7.2.6 Priorité matérielle

Structure

Priorité

Matériel probable

if / elseUne conditionMultiplexeur 2 vers 1
if / elsif / elsePriorité de haut en basChaîne de multiplexeurs prioritaires
if imbriquésPriorité hiérarchiqueRéseau de comparateurs et multiplexeurs
Conditions exclusivesToujours écrites prioritairementOptimisation possible par le synthétiseur

 

7.2.7 Affectation complète et latch

Description incomplète  |  VHDL
process(a, enable)
begin
    if enable = '1' then
        y <= a;
    end if;
end process;

 

Quand enable vaut 0, aucune nouvelle valeur n’est affectée à y. Pour reproduire ce comportement, le matériel doit mémoriser l’ancienne valeur : un latch est inféré.

Description combinatoire corrigée  |  VHDL
process(a, enable)
begin
    y <= '0';

    if enable = '1' then
        y <= a;
    end if;
end process;

 

7.2.8 If dans un processus cadencé

Registre avec validation  |  VHDL
process(clk)
begin
    if rising_edge(clk) then
        if enable = '1' then
            q <= d;
        end if;
    end if;
end process;

 

Ici, l’absence de else est volontaire : lorsque enable vaut 0, le registre conserve sa valeur. Il ne s’agit pas d’un latch, mais d’une bascule avec signal d’activation.

Distinction — Le contexte compte

Une affectation incomplète est généralement une erreur dans un processus combinatoire, mais elle peut exprimer une conservation légitime dans un processus cadencé.

 

7.2.9 Reset asynchrone et synchrone

Reset asynchrone  |  VHDL
process(clk, reset)
begin
    if reset = '1' then
        q <= (others => '0');
    elsif rising_edge(clk) then
        q <= d;
    end if;
end process;

 

Reset synchrone  |  VHDL
process(clk)
begin
    if rising_edge(clk) then
        if reset = '1' then
            q <= (others => '0');
        else
            q <= d;
        end if;
    end if;
end process;

 

Le reset asynchrone apparaît dans la liste de sensibilité ; le reset synchrone est testé uniquement au front d’horloge.

7.2.10 Lisibilité des conditions

Condition complexe nommée  |  VHDL
condition_valide <= enable
                    and not erreur
                    and donnees_disponibles;

process(all)
begin
    if condition_valide = '1' then
        y <= resultat;
    else
        y <= (others => '0');
    end if;
end process;

 

  • Nommer les conditions fonctionnelles complexes.
  • Aligner les opérateurs logiques.
  • Limiter l’imbrication lorsque des signaux intermédiaires suffisent.
  • Conserver une branche else explicite dans les processus combinatoires.

7.3 Instruction case

L’instruction case sélectionne une séquence selon la valeur d’une expression discrète. Les choix sont mutuellement exclusifs et aucune priorité n’est associée à leur ordre.

7.3.1 Syntaxe générale

Forme générale  |   VHDL
case expression is
    when choix_1 =>
        instructions_1;

    when choix_2 =>
        instructions_2;

    when others =>
        instructions_par_defaut;
end case;

 

7.3.2 Multiplexeur avec case

Multiplexeur 4 vers 1  |   VHDL
case sel is
    when "00" =>
        y <= a;

    when "01" =>
        y <= b;

    when "10" =>
        y <= c;

    when others =>
        y <= d;
end case;

 

Tous les choix affectent y. Le processus est combinatoire si la liste de sensibilité est complète.

7.3.3 Sélection entre plusieurs choix

Unité logique  |   VHDL
case operation is
    when "000" =>
        y <= a and b;

    when "001" =>
        y <= a or b;

    when "010" =>
        y <= a xor b;

    when "011" =>
        y <= not a;

    when others =>
        y <= (others => '0');
end case;

 

7.3.4 Cas multiples

Regroupement de choix  |  VHDL
case commande is
    when "001" | "010" | "100" =>
        actif <= '1';

    when others =>
        actif <= '0';
end case;

 

Le symbole | associe plusieurs valeurs au même ensemble d’instructions.

7.3.5 Utilisation de others

La branche others couvre toutes les valeurs non écrites. Elle est souvent indispensable avec std_logic_vector, car les bits peuvent prendre des valeurs comme U, X ou Z.

Valeur de sécurité  |  VHDL
case sel is
    when "00" =>
        y <= a;
    when "01" =>
        y <= b;
    when "10" =>
        y <= c;
    when "11" =>
        y <= d;
    when others =>
        y <= (others => 'X');
end case;

 

Choix de conception — Simulation défensive

Une valeur X dans others facilite la détection d’un sélecteur non initialisé. Dans une conception de production, une valeur sûre peut être préférable selon l’analyse de risque.

 

7.3.6 Couverture complète

Les choix d’un case doivent être complets et non recouvrants.

Propriété

Exigence

ComplétudeToute valeur possible de l’expression doit être couverte.
ExclusivitéUne valeur ne doit apparaître que dans une seule alternative.
TypeLes choix doivent être compatibles avec le type de l’expression.
AffectationsPour un processus combinatoire, toutes les sorties doivent être définies dans tous les cas.

 

7.3.7 Case sur un type énuméré

Machine à états combinatoire  |  VHDL
type etat_t is (ATTENTE, CHARGE, CALCUL, FIN);
signal etat : etat_t;

process(all)
begin
    pret <= '0';

    case etat is
        when ATTENTE =>
            pret <= '1';

        when CHARGE =>
            pret <= '0';

        when CALCUL =>
            pret <= '0';

        when FIN =>
            pret <= '1';
    end case;
end process;

 

Un type énuméré rend les choix plus lisibles et permet au compilateur de vérifier la couverture.

7.3.8 Différence entre if et case

Critère

if / elsif

case

Nature des conditionsConditions quelconques et différentesValeurs d’une même expression
PrioritéOui, de haut en basNon
Choix multiplesPar conditions combinéesAvec |
ComplétudeBranche else ou valeurs par défautTous les choix ou others
Usage typiqueComparaisons, seuils, prioritéOpcode, état, sélecteur
LisibilitéMeilleure pour décisions hétérogènesMeilleure pour table de sélection

 

7.3.9 Même multiplexeur avec if et case

Version if  |   VHDL
if sel = "00" then
    y <= a;
elsif sel = "01" then
    y <= b;
elsif sel = "10" then
    y <= c;
else
    y <= d;
end if;

 

Version case  |   VHDL
case sel is
    when "00" => y <= a;
    when "01" => y <= b;
    when "10" => y <= c;
    when others => y <= d;
end case;

 

Les deux descriptions peuvent produire le même matériel. La version case exprime plus directement une sélection par code.

7.3.10 Case et valeurs par défaut

Valeurs par défaut avant case  |  VHDL
process(all)
begin
    y     <= (others => '0');
    valide <= '0';

    case operation is
        when "00" =>
            y <= a;
            valide <= '1';

        when "01" =>
            y <= b;
            valide <= '1';

        when others =>
            null;
    end case;
end process;

 

L’instruction null indique qu’aucune autre action n’est réalisée ; les valeurs par défaut restent actives.

7.4 Boucles

Les boucles répètent des instructions séquentielles. En synthèse, elles ne représentent pas nécessairement une exécution dans le temps : lorsque les limites sont statiques, le synthétiseur déroule souvent la boucle et crée plusieurs ressources matérielles.

7.4.1 Boucle for

Forme générale  |   VHDL
for i in plage loop
    instructions;
end loop;

 

Calcul de parité  |  VHDL
process(donnees)
    variable parite_v : std_logic;
begin
    parite_v := '0';

    for i in donnees'range loop
        parite_v := parite_v xor donnees(i);
    end loop;

    parite <= parite_v;
end process;

 

La boucle parcourt tous les bits du vecteur, quelle que soit son orientation.

7.4.2 Indice de boucle

L’indice de boucle est déclaré implicitement et n’a pas besoin d’être déclaré dans la partie déclarative. Sa portée est limitée au corps de la boucle.

Initialisation d’un tableau  |  VHDL
for i in registre'range loop
    registre(i) <= '0';
end loop;

 

7.4.3 Déroulement matériel

Une boucle for dont la plage est connue lors de l’élaboration est généralement déroulée. Par exemple, une boucle de huit itérations calculant huit bits décrit huit opérations matérielles, potentiellement parallèles.

Boucle

Interprétation synthétique probable

for i in 0 to 7Huit copies ou huit étapes de logique combinatoire
for i in vecteur'rangeUne copie par élément du vecteur
Boucle dépendant d’un générique NN copies après élaboration
Boucle avec limites variables à l’exécutionSouvent non synthétisable ou coûteuse

 

7.4.4 Boucle while

Forme générale  |   VHDL
while condition loop
    instructions;
end loop;

 

Recherche simulée  |  VHDL
variable i : integer := 0;

while i < 8 loop
    -- Traitement
    i := i + 1;
end loop;

 

La boucle while est naturelle en algorithmique, mais sa synthèse est plus restrictive. L’outil doit pouvoir déterminer une borne maximale statique et garantir la terminaison.

Attention — Synthèse prudente

Pour les descriptions RTL portables, préférer une boucle for à bornes statiques. Un traitement itératif dépendant des données doit souvent être réalisé avec un compteur et une machine à états sur plusieurs cycles.

 

7.4.5 Instruction loop

Boucle générale  |  VHDL
loop
    instructions;
    exit when condition;
end loop;

 

Une boucle loop n’a pas de condition intégrée. Elle doit être interrompue par exit, return, wait ou une autre structure adaptée.

7.4.6 Instruction exit

Sortie conditionnelle  |  VHDL
for i in donnees'range loop
    if donnees(i) = '1' then
        position_v := i;
        trouve_v := true;
        exit;
    end if;
end loop;

 

Forme exit when  |   VHDL
loop
    compteur := compteur + 1;
    exit when compteur = 10;
end loop;

 

7.4.7 Boucles imbriquées

Traitement d’une matrice  |  VHDL
for ligne in 0 to 3 loop
    for colonne in 0 to 3 loop
        sortie(ligne, colonne) <= entree(ligne, colonne);
    end loop;
end loop;

 

Le nombre total d’itérations est le produit des tailles. Une boucle imbriquée peut donc entraîner une forte croissance des ressources.

7.4.8 Boucle et addition cumulative

Somme des bits d’un vecteur  |  VHDL
process(donnees)
    variable somme_v : natural range 0 to donnees'length;
begin
    somme_v := 0;

    for i in donnees'range loop
        if donnees(i) = '1' then
            somme_v := somme_v + 1;
        end if;
    end loop;

    nombre_uns <= somme_v;
end process;

 

Le synthétiseur peut construire un arbre ou une chaîne d’additionneurs selon ses optimisations.

7.4.9 Boucle generate et boucle séquentielle

Boucle dans un processus

Instruction generate

Séquentielle dans le code du processusConcurrente au niveau de l’architecture
Utilise for, while ou loopUtilise for generate ou if generate
Manipule variables et signauxCrée des instances ou structures répétées
Décrit un calcul regroupéDécrit explicitement plusieurs blocs matériels

 

Distinction — Ne pas confondre

La boucle for d’un processus est une instruction séquentielle. La construction for ... generate, étudiée plus tard, est une instruction concurrente de génération matérielle.

 

7.4.10 Règles de synthèse

  • Utiliser des limites statiques ou déterminées par des génériques.
  • Garantir une terminaison finie et démontrable.
  • Éviter les boucles while dépendant de données non bornées.
  • Évaluer le nombre de ressources créées après déroulement.
  • Pour un calcul séquentiel sur plusieurs cycles, utiliser une machine à états et un compteur.
  • Vérifier le rapport de synthèse et le chemin critique.

7.5 Variables dans un processus

Une variable est un objet local mis à jour immédiatement. Elle est particulièrement utile pour accumuler un résultat, mémoriser un calcul intermédiaire ou construire une décision avant l’affectation finale d’un signal.

7.5.1 Déclaration

Variable locale  |  VHDL
process(a, b, c)
    variable temp : std_logic;
begin
    temp := a and b;
    temp := temp or c;
    y <= temp;
end process;

 

La variable temp existe à chaque activation du processus et conserve sa valeur entre activations si elle n’est pas réaffectée, ce qui peut avoir des conséquences en simulation et en synthèse.

7.5.2 Affectation avec :=

Affectation immédiate  |  VHDL
temp := nouvelle_valeur;

 

L’opérateur := est utilisé pour les variables et certaines initialisations. Il ne doit pas être confondu avec <=, réservé à l’affectation de signal.

7.5.3 Mise à jour immédiate

Deux incréments de variable  |  VHDL
process(clk)
    variable compteur_v : integer := 0;
begin
    if rising_edge(clk) then
        compteur_v := compteur_v + 1;
        compteur_v := compteur_v + 1;

        sortie <= compteur_v;
    end if;
end process;

 

À chaque front, compteur_v augmente de deux avant d’être copié vers sortie.

7.5.4 Signal affecté plusieurs fois

Deux affectations de signal  |  VHDL
process(clk)
begin
    if rising_edge(clk) then
        compteur_s <= compteur_s + 1;
        compteur_s <= compteur_s + 1;

        sortie <= compteur_s;
    end if;
end process;

 

Les deux expressions utilisent l’ancienne valeur de compteur_s. La dernière transaction remplace la précédente pour le même instant. compteur_s n’augmente donc pas de deux.

7.5.5 Comparaison signal/variable

Critère

Variable

Signal

Opérateur:=<=
Mise à jourImmédiatePlanifiée
Portée typiqueProcessus ou sous-programmeArchitecture, entité, paquetage
Communication entre processusNon directementOui
Déclenchement d’un processusNonOui, par événement
UsageCalcul intermédiaireConnexion, sortie, registre

 

7.5.6 Variable avec valeur par défaut

Calcul combinatoire sûr  |   VHDL
process(all)
    variable resultat_v : std_logic_vector(7 downto 0);
begin
    resultat_v := (others => '0');

    if enable = '1' then
        resultat_v := a xor b;
    end if;

    y <= resultat_v;
end process;

 

La variable est définie dans tous les chemins avant d’être utilisée.

7.5.7 Variable non initialisée dans un chemin

Description dangereuse  |  VHDL
process(a, enable)
    variable temp : std_logic;
begin
    if enable = '1' then
        temp := a;
    end if;

    y <= temp;
end process;

 

Lorsque enable vaut 0, temp conserve sa valeur précédente. Cette description peut inférer une mémoire. Il faut initialiser temp au début du processus si un circuit combinatoire est souhaité.

7.5.8 Accumulateur dans une boucle

Calcul de parité avec variable  |  VHDL
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;

 

La mise à jour immédiate permet à chaque itération d’utiliser le résultat de l’itération précédente.

7.5.9 Variable et signal dans un processus cadencé

Calcul de prochaine valeur  |  VHDL
process(clk)
    variable prochaine_v : unsigned(7 downto 0);
begin
    if rising_edge(clk) then
        prochaine_v := compteur + 1;

        if prochaine_v = 100 then
            compteur <= (others => '0');
        else
            compteur <= prochaine_v;
        end if;
    end if;
end process;

 

La variable calcule immédiatement la prochaine valeur, tandis que compteur reste un registre mis à jour après le front.

7.5.10 Variables partagées

Les variables partagées existent en VHDL, mais leur utilisation demande des mécanismes protégés et une grande prudence pour éviter les accès concurrents indéterminés. Elles sont rarement nécessaires dans un cours RTL d’introduction.

Prudence — Recommandation

Pour communiquer entre processus synthétisables, utiliser des signaux. Réserver les variables aux calculs locaux et les variables partagées aux modèles avancés maîtrisés.

 

7.6 Sensibilité d’un processus

La sensibilité détermine quand un processus est réexécuté en simulation. Une liste incorrecte peut produire une simulation différente du circuit synthétisé, car le synthétiseur analyse généralement les dépendances logiques indépendamment de cette liste.

7.6.1 Processus combinatoire

Liste complète  |   VHDL
process(a, b, sel)
begin
    if sel = '0' then
        y <= a;
    else
        y <= b;
    end if;
end process;

 

Les trois signaux a, b et sel sont lus et figurent dans la liste.

7.6.2 Liste incomplète

Erreur classique  |  VHDL
process(a, sel)
begin
    if sel = '0' then
        y <= a;
    else
        y <= b;
    end if;
end process;

 

Le signal b est lu mais absent. Si b change alors que a et sel restent constants, le processus ne se réexécute pas : y reste incorrect en simulation.

Erreur critique — Simulation/synthèse

Le matériel synthétisé peut tout de même dépendre de b. Une liste incomplète peut donc créer un écart dangereux entre la simulation RTL et le fonctionnement réel.

 

7.6.3 Liste complète des signaux lus

  • Inclure tous les ports et signaux lus directement.
  • Inclure les signaux lus dans les conditions if ou case.
  • Inclure les signaux lus dans les expressions de calcul.
  • Inclure les signaux lus dans les appels de fonction, lorsque leur valeur est passée comme argument.
  • Ne pas ajouter les variables locales.
  • Ne pas ajouter les signaux uniquement écrits.

7.6.4 Utilisation de process(all) en VHDL-2008

Processus combinatoire automatique  |  VHDL
process(all)
begin
    if sel = '0' then
        y <= a;
    else
        y <= b;
    end if;
end process;

 

Le mot all demande à l’outil d’inclure automatiquement tous les signaux lus par le processus. Cette forme réduit les erreurs de maintenance lorsque l’expression change.

Compatibilité — Version du langage

process(all) nécessite VHDL-2008. Le projet et le simulateur doivent être configurés avec la norme correspondante, par exemple --std=08 avec GHDL.

 

7.6.5 Processus séquentiel cadencé

Processus sur front d’horloge  |  VHDL
process(clk)
begin
    if rising_edge(clk) then
        q <= d;
    end if;
end process;

 

La donnée d n’apparaît pas dans la liste. Le registre ne doit être mis à jour qu’au front d’horloge, même si d change entre deux fronts.

7.6.6 Processus avec reset asynchrone

Horloge et reset  |  VHDL
process(clk, reset)
begin
    if reset = '1' then
        q <= (others => '0');
    elsif rising_edge(clk) then
        q <= d;
    end if;
end process;

 

Le reset doit figurer dans la liste, car il agit immédiatement sans attendre un front d’horloge.

7.6.7 Processus avec reset synchrone

Horloge seule  |   VHDL
process(clk)
begin
    if rising_edge(clk) then
        if reset = '1' then
            q <= (others => '0');
        else
            q <= d;
        end if;
    end if;
end process;

 

Le reset synchrone est lu uniquement lorsque le processus est réveillé par clk ; il n’apparaît donc pas dans la liste.

7.6.8 Tableau récapitulatif

Type de processus

Liste de sensibilité recommandée

Combinatoire VHDL antérieur à 2008Tous les signaux lus
Combinatoire VHDL-2008all
Cadencé sans reset asynchroneclk
Cadencé avec reset asynchroneclk, reset
Banc de test avec waitAucune liste

 

7.6.9 Détection d’une liste incomplète

  1. Identifier tous les signaux lus dans le processus.
  2. Comparer cette liste à la liste de sensibilité.
  3. Simuler un changement isolé de chaque entrée.
  4. Vérifier que la sortie est mise à jour.
  5. Utiliser les avertissements du compilateur ou du linter.
  6. Préférer process(all) pour les nouveaux projets VHDL-2008.

7.6.10 Exemple de correction

Version incorrecte  |  VHDL
process(a, sel)
begin
    y <= a when sel = '0' else b;
end process;

 

Version corrigée classique  |  VHDL
process(a, b, sel)
begin
    y <= a when sel = '0' else b;
end process;

 

Version corrigée VHDL-2008  |  VHDL
process(all)
begin
    y <= a when sel = '0' else b;
end process;

 

7.6.11 Sensibilité et fonctions

Lorsque le processus appelle une fonction pure, les signaux passés en arguments doivent être pris en compte. Avec process(all), l’outil déduit ces dépendances.

Fonction et process(all)  |  VHDL
process(all)
begin
    y <= fonction_logique(a, b, commande);
end process;

 

7.6.12 Bonnes pratiques de processus combinatoire

  • Utiliser process(all) lorsque VHDL-2008 est disponible.
  • Affecter des valeurs par défaut à toutes les sorties et variables.
  • Couvrir toutes les branches de if et case.
  • Éviter les latches involontaires.
  • Limiter un processus à une fonction cohérente.
  • Tester chaque entrée indépendamment dans le banc de test.

Méthode de conception d’un processus combinatoire

1. Établir la table de vérité ou l’algorithme combinatoire.

2. Identifier toutes les entrées lues et toutes les sorties pilotées.

3. Choisir process(all) ou construire une liste complète.

4. Déclarer les variables nécessaires.

5. Attribuer des valeurs par défaut aux sorties et variables.

6. Écrire les décisions avec if ou case.

7. Vérifier que chaque chemin affecte les sorties.

8. Simuler toutes les combinaisons pertinentes.

9. Vérifier le schéma RTL et l’absence de latch.

Choisir entre if et case

Question

Réponse

Choix conseillé

Les décisions portent-elles sur une seule expression ?Ouicase
Les conditions sont-elles des comparaisons différentes ?Ouiif / elsif
Une priorité est-elle nécessaire ?Ouiif / elsif
Les choix sont-ils exclusifs et codés ?Ouicase
Le traitement comporte-t-il des seuils ?Ouiif / elsif
Le traitement correspond-il à un opcode ?Ouicase

 

Erreurs fréquentes

Erreur

Conséquence

Correction

Signal lu absent de la sensibilitéSimulation obsolèteAjouter le signal ou utiliser all
Sortie non affectée dans un cheminLatch involontaireValeur par défaut ou branche complète
Variable non initialiséeMémoire ou valeur indéterminéeInitialiser à chaque activation
Confusion := et <=Erreur ou comportement différentVariable :=, signal <=
Case incompletErreur d’analyse ou comportement incompletAjouter tous les choix ou others
While non bornéNon-synthétisableFor statique ou machine à états
If prioritaire non vouluChemin long ou fonction incorrecteCase ou conditions exclusives

 

Travaux pratiques

TP 4 — Circuits combinatoires avec processus

1. Objectifs du TP

  • réaliser un multiplexeur avec if ;
  • réaliser un multiplexeur avec case ;
  • concevoir un comparateur numérique ;
  • concevoir un encodeur de priorité ;
  • écrire des bancs de test auto-vérifiants ;
  • reproduire une erreur de liste de sensibilité ;
  • corriger la description avec une liste complète et process(all).

2. Organisation proposée

Arborescence du projet  |  Arborescence
tp4_processus_combinatoires/
├── src/
│   ├── mux2_if.vhd
│   ├── mux4_case.vhd
│   ├── comparateur8.vhd
│   ├── encodeur_priorite.vhd
│   ├── mux_sensibilite_incorrect.vhd
│   └── mux_sensibilite_corrige.vhd
└── sim/
    ├── tb_mux2_if.vhd
    ├── tb_mux4_case.vhd
    ├── tb_comparateur8.vhd
    ├── tb_encodeur_priorite.vhd
    └── tb_sensibilite.vhd

 

3. Partie A — Multiplexeur utilisant if

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

entity mux2_if is
    port (
        a, b : in  std_logic_vector(7 downto 0);
        sel  : in   std_logic;
        y    : out std_logic_vector(7 downto 0)
    );
end entity mux2_if;

architecture rtl of mux2_if is
begin
    process(all)
    begin
        if sel = '0' then
            y <= a;
        else
            y <= b;
        end if;
    end process;
end architecture rtl;

 

4. Banc de test du multiplexeur if

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

entity tb_mux2_if is
end entity tb_mux2_if;

architecture simulation of tb_mux2_if is
    signal a   : std_logic_vector(7 downto 0) := x"12";
    signal b   : std_logic_vector(7 downto 0) := x"A5";
    signal sel : std_logic := '0';
    signal y   : std_logic_vector(7 downto 0);
begin
    DUT : entity work.mux2_if(rtl)
        port map (a => a, b => b, sel => sel, y => y);

 

tb_mux2_if.vhd — Stimuli  |  VHDL
    stimuli : process
    begin
        sel <= '0';
        wait for 10 ns;

        assert y = x"12"
            report "Erreur mux2_if pour sel=0"
            severity error;

        sel <= '1';
        wait for 10 ns;

        assert y = x"A5"
            report "Erreur mux2_if pour sel=1"
            severity error;

        report "Test mux2_if termine" severity note;
        stop;
        wait;
    end process;
end architecture simulation;

 

5. Partie B — Multiplexeur utilisant case

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

entity mux4_case is
    port (
        a, b, c, d : in  std_logic_vector(7 downto 0);
        sel        : in   std_logic_vector(1 downto 0);
        y          : out std_logic_vector(7 downto 0)
    );
end entity mux4_case;

architecture rtl of mux4_case is
begin
    process(all)
    begin
        case sel is
            when "00" =>
                y <= a;
            when "01" =>
                y <= b;
            when "10" =>
                y <= c;
            when others =>
                y <= d;
        end case;
    end process;
end architecture rtl;

 

6. Banc de test du multiplexeur case

tb_mux4_case.vhd — Partie 1  |  VHDL
library IEEE;
use IEEE.STD_LOGIC_1164.ALL;
use STD.ENV.ALL;

entity tb_mux4_case is
end entity tb_mux4_case;

architecture simulation of tb_mux4_case is
    signal a : std_logic_vector(7 downto 0) := x"11";
    signal b : std_logic_vector(7 downto 0) := x"22";
    signal c : std_logic_vector(7 downto 0) := x"44";
    signal d : std_logic_vector(7 downto 0) := x"88";
    signal sel : std_logic_vector(1 downto 0) := "00";
    signal y : std_logic_vector(7 downto 0);
begin
    DUT : entity work.mux4_case(rtl)
        port map (
            a => a, b => b, c => c, d => d,
            sel => sel, y => y
        );

 

tb_mux4_case.vhd — Partie 2  |  VHDL
    stimuli : process
    begin
        sel <= "00"; wait for 10 ns;
        assert y = x"11" severity error;

        sel <= "01"; wait for 10 ns;
        assert y = x"22" severity error;

        sel <= "10"; wait for 10 ns;
        assert y = x"44" severity error;

        sel <= "11"; wait for 10 ns;
        assert y = x"88" severity error;

        report "Test mux4_case termine" severity note;
        stop;
        wait;
    end process;
end architecture simulation;

 

7. Partie C — Comparateur numérique

Le comparateur reçoit deux mots non signés sur 8 bits et produit trois indicateurs mutuellement exclusifs.

comparateur8.vhd  |   VHDL
library IEEE;
use IEEE.STD_LOGIC_1164.ALL;
use IEEE.NUMERIC_STD.ALL;

entity comparateur8 is
    port (
        a, b       : in   std_logic_vector(7 downto 0);
        egal       : out std_logic;
        inferieur  : out std_logic;
        superieur  : out std_logic
    );
end entity comparateur8;

architecture rtl of comparateur8 is
begin
    process(all)
        variable a_u : unsigned(7 downto 0);
        variable b_u : unsigned(7 downto 0);
    begin
        a_u := unsigned(a);
        b_u := unsigned(b);

        egal      <= '0';
        inferieur <= '0';
        superieur <= '0';

        if a_u = b_u then
            egal <= '1';
        elsif a_u < b_u then
            inferieur <= '1';
        else
            superieur <= '1';
        end if;
    end process;
end architecture rtl;

 

8. Banc de test du comparateur

tb_comparateur8.vhd — Partie 1  |  VHDL
library IEEE;
use IEEE.STD_LOGIC_1164.ALL;
use IEEE.NUMERIC_STD.ALL;
use STD.ENV.ALL;

entity tb_comparateur8 is
end entity tb_comparateur8;

architecture simulation of tb_comparateur8 is
    signal a, b : std_logic_vector(7 downto 0)
                  := (others => '0');
    signal egal, inferieur, superieur : std_logic;
begin
    DUT : entity work.comparateur8(rtl)
        port map (
            a => a, b => b,
            egal => egal,
            inferieur => inferieur,
            superieur => superieur
        );

 

tb_comparateur8.vhd — Boucles de test  |  VHDL
    stimuli : process
    begin
        for va in 0 to 15 loop
            for vb in 0 to 15 loop
                a <= std_logic_vector(to_unsigned(va, 8));
                b <= std_logic_vector(to_unsigned(vb, 8));
                wait for 1 ns;

                assert egal = ('1' when va = vb else '0')
                    report "Erreur egal"
                    severity error;

                assert inferieur = ('1' when va < vb else '0')
                    report "Erreur inferieur"
                    severity error;

                assert superieur = ('1' when va > vb else '0')
                    report "Erreur superieur"
                    severity error;
            end loop;
        end loop;

        report "Test comparateur termine" severity note;
        stop;
        wait;
    end process;
end architecture simulation;

 

9. Partie D — Encodeur de priorité

L’entrée de poids fort est prioritaire. Une sortie valide indique qu’au moins une entrée est active.

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

entity encodeur_priorite is
    port (
        entree : in  std_logic_vector(3 downto 0);
        code   : out std_logic_vector(1 downto 0);
        valide : out std_logic
    );
end entity encodeur_priorite;

architecture rtl of encodeur_priorite is
begin
    process(all)
    begin
        code   <= "00";
        valide <= '1';

        if entree(3) = '1' then
            code <= "11";
        elsif entree(2) = '1' then
            code <= "10";
        elsif entree(1) = '1' then
            code <= "01";
        elsif entree(0) = '1' then
            code <= "00";
        else
            valide <= '0';
        end if;
    end process;
end architecture rtl;

 

10. Banc de test de l’encodeur

tb_encodeur_priorite.vhd — Partie 1  |  VHDL
library IEEE;
use IEEE.STD_LOGIC_1164.ALL;
use STD.ENV.ALL;

entity tb_encodeur_priorite is
end entity tb_encodeur_priorite;

architecture simulation of tb_encodeur_priorite is
    signal entree : std_logic_vector(3 downto 0)
                    := (others => '0');
    signal code : std_logic_vector(1 downto 0);
    signal valide : std_logic;
begin
    DUT : entity work.encodeur_priorite(rtl)
        port map (
            entree => entree,
            code => code,
            valide => valide
        );

 

tb_encodeur_priorite.vhd — Scénarios  |  VHDL
    stimuli : process
    begin
        entree <= "0000"; wait for 10 ns;
        assert valide = '0' severity error;

        entree <= "0001"; wait for 10 ns;
        assert valide = '1' and code = "00"
            severity error;

        entree <= "0010"; wait for 10 ns;
        assert code = "01" severity error;

        entree <= "0110"; wait for 10 ns;
        assert code = "10"
            report "La priorite de l'entree 2 est incorrecte"
            severity error;

        entree <= "1111"; wait for 10 ns;
        assert code = "11"
            report "La priorite de l'entree 3 est incorrecte"
            severity error;

        report "Test encodeur termine" severity note;
        stop;
        wait;
    end process;
end architecture simulation;

 

11. Partie E — Liste de sensibilité incomplète

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

entity mux_sensibilite_incorrect is
    port (
        a, b : in  std_logic;
        sel  : in   std_logic;
        y    : out std_logic
    );
end entity mux_sensibilite_incorrect;

architecture rtl of mux_sensibilite_incorrect is
begin
    process(a, sel)
    begin
        if sel = '0' then
            y <= a;
        else
            y <= b;
        end if;
    end process;
end architecture rtl;

 

Le signal b est lu mais absent de la liste de sensibilité.

12. Banc de test révélant le défaut

tb_sensibilite.vhd — Scénario de diagnostic  |  VHDL
stimuli : process
begin
    a <= '0';
    b <= '0';
    sel <= '1';
    wait for 10 ns;

    assert y_incorrect = '0'
        severity error;

    -- Seul b change. Le processus incorrect
    -- ne se réveille pas.
    b <= '1';
    wait for 10 ns;

    assert y_incorrect = '1'
        report "Defaut detecte : b manque dans la sensibilite"
        severity warning;

    wait;
end process;

 

13. Correction classique

mux_sensibilite_corrige.vhd  |   VHDL
architecture rtl_liste of mux_sensibilite_corrige is
begin
    process(a, b, sel)
    begin
        if sel = '0' then
            y <= a;
        else
            y <= b;
        end if;
    end process;
end architecture rtl_liste;

 

14. Correction VHDL-2008

Correction avec process(all)  |  VHDL
architecture rtl_all of mux_sensibilite_corrige is
begin
    process(all)
    begin
        if sel = '0' then
            y <= a;
        else
            y <= b;
        end if;
    end process;
end architecture rtl_all;

 

15. Compilation avec GHDL

Compilation des sources  |  Terminal
ghdl -a --std=08 src/mux2_if.vhd
ghdl -a --std=08 src/mux4_case.vhd
ghdl -a --std=08 src/comparateur8.vhd
ghdl -a --std=08 src/encodeur_priorite.vhd
ghdl -a --std=08 src/mux_sensibilite_incorrect.vhd
ghdl -a --std=08 src/mux_sensibilite_corrige.vhd

 

Exemple de simulation  |  Terminal
ghdl -a --std=08 sim/tb_mux4_case.vhd
ghdl -e --std=08 tb_mux4_case
ghdl -r --std=08 tb_mux4_case    --vcd=tb_mux4_case.vcd

gtkwave tb_mux4_case.vcd

 

16. Observations demandées

  • Vérifier les deux positions du multiplexeur if.
  • Vérifier les quatre valeurs du sélecteur du multiplexeur case.
  • Confirmer qu’un seul indicateur du comparateur vaut 1.
  • Observer la priorité de l’entrée 3 dans l’encodeur.
  • Modifier uniquement b dans la version à sensibilité incomplète.
  • Comparer les chronogrammes des versions incorrecte et corrigée.
  • Vérifier l’absence de latch dans le schéma RTL.

17. Tableau de résultats

Circuit

Entrées

Résultat attendu

Résultat observé

Conforme

MUX ifsel=0y=a  
MUX ifsel=1y=b  
MUX casesel="10"y=c  
Comparateura=12, b=20inferieur=1  
Encodeurentree="0110"code="10"  
Sensibilité incorrecteb change seuly doit changer  

 

18. Questions d’analyse

1. Pourquoi le processus du multiplexeur est-il concurrent alors que son if est séquentiel ?

2. Quelle différence matérielle peut exister entre un if prioritaire et un case ?

3. Pourquoi des valeurs par défaut sont-elles affectées dans le comparateur ?

4. Pourquoi l’encodeur de priorité doit-il utiliser if/elsif plutôt que case sur les quatre bits ?

5. Quel défaut apparaît lorsque b manque dans la liste de sensibilité ?

6. Pourquoi le synthétiseur peut-il produire un circuit correct malgré une simulation erronée ?

7. Quel avantage offre process(all) ?

8. Quand une absence de else est-elle volontaire dans un processus cadencé ?

9. Comment vérifier qu’une boucle for est synthétisable ?

10. Pourquoi une variable est-elle adaptée au calcul de parité ?

19. Extensions proposées

  • Créer un multiplexeur 8 vers 1 générique.
  • Ajouter un comparateur signé.
  • Créer un encodeur prioritaire 8 vers 3.
  • Ajouter un indicateur d’entrée invalide X ou Z.
  • Comparer les ressources d’une version if et d’une version case.
  • Écrire un détecteur du premier bit à 1 avec une boucle for et exit.
  • Implanter les circuits sur une carte FPGA.

20. Barème indicatif

Critère

Points

Multiplexeur avec if2
Multiplexeur avec case2
Comparateur numérique3
Encodeur de priorité3
Bancs de test et assertions3
Diagnostic de sensibilité3
Correction avec liste complète et all2
Analyse et qualité du compte rendu2
Total20

 

Synthèse du chapitre

Notion

Résumé

ProcessusInstruction concurrente contenant des instructions séquentielles.
Liste de sensibilitéDétermine les événements qui réveillent le processus.
Partie déclarativeContient variables, constantes et types locaux.
if / elsif / elseDécisions potentiellement prioritaires.
caseChoix mutuellement exclusifs selon une expression.
forBoucle à plage généralement statique et synthétisable.
while / loopBoucles plus générales, à utiliser prudemment en synthèse.
exitInterrompt une boucle.
VariableObjet local mis à jour immédiatement avec :=.
SignalObjet de communication mis à jour avec <=.
process(all)Sensibilité automatique en VHDL-2008.
Latch involontaireRésulte d’une affectation combinatoire incomplète.

 

Autoévaluation

1. Un processus est-il concurrent ou séquentiel ?

Réponse : Il est concurrent comme bloc ; ses instructions internes sont séquentielles.

2. Où déclare-t-on une variable locale ?

Réponse : Entre process(...) et begin.

3. Quel opérateur affecte une variable ?

Réponse : :=.

4. Quel opérateur affecte un signal ?

Réponse : <=.

5. Quel choix convient à des conditions prioritaires ?

Réponse : if / elsif.

6. Quel choix convient à un opcode ?

Réponse : case.

7. Que doit contenir la sensibilité d’un processus combinatoire ?

Réponse : Tous les signaux lus, ou all en VHDL-2008.

8. Quel risque produit une sortie non affectée dans tous les chemins ?

Réponse : L’inférence d’un latch.

9. Une boucle for est-elle exécutée dans le temps matériel ?

Réponse : Pas nécessairement ; elle est souvent déroulée en matériel parallèle.

10. Quand faut-il inclure reset dans la sensibilité ?

Réponse : Lorsqu’il est asynchrone.

Exercice de consolidation

Écrire un processus combinatoire qui reçoit un vecteur de 8 bits, compte le nombre de bits à 1 et produit un résultat unsigned sur 4 bits. Utiliser une variable et une boucle for.

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

entity compteur_uns is
    port (
        donnees : in  std_logic_vector(7 downto 0);
        nombre  : out unsigned(3 downto 0)
    );
end entity compteur_uns;

architecture rtl of compteur_uns is
begin
    process(all)
        variable total_v : natural range 0 to 8;
    begin
        total_v := 0;

        for i in donnees'range loop
            if donnees(i) = '1' then
                total_v := total_v + 1;
            end if;
        end loop;

        nombre <= to_unsigned(total_v, nombre'length);
    end process;
end architecture rtl;

 

Suite du cours — Transition vers la suite

Après les processus combinatoires, le cours peut comparer les styles de description VHDL : flot de données, comportemental et structurel, puis introduire la conception hiérarchique.