Leçon 5 sur 22

Chapitre 5 — Opérateurs et expressions VHDL

Objectif — Finalité du chapitre

Maîtriser les opérateurs logiques, relationnels, arithmétiques, de décalage et de concaténation afin de construire des expressions VHDL correctes, lisibles et synthétisables, puis valider leur comportement par simulation.

Objectifs pédagogiques

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

  • utiliser les opérateurs logiques sur des signaux scalaires et vectoriels ;
  • interpréter correctement les opérateurs relationnels et leurs types de résultat ;
  • réaliser des opérations arithmétiques avec integer, signed et unsigned ;
  • distinguer modulo et reste ;
  • gérer les largeurs de résultat et les risques de débordement ;
  • effectuer des décalages logiques, arithmétiques et des rotations ;
  • utiliser shift_left et shift_right avec IEEE.NUMERIC_STD ;
  • concaténer des bits et des vecteurs avec l’opérateur & ;
  • construire une extension par zéro ou une extension de signe ;
  • appliquer les règles de priorité des opérateurs et utiliser les parenthèses ;
  • développer puis simuler une unité logique et arithmétique simple.

Prérequis

  • types std_logic, std_logic_vector, signed et unsigned ;
  • bibliothèques IEEE.STD_LOGIC_1164 et IEEE.NUMERIC_STD ;
  • conversions entre vecteurs et types numériques ;
  • entité, architecture, instructions concurrentes et banc de test.

Organisation du chapitre

Section

Contenu

Compétence

5.1Opérateurs logiquesExprimer des fonctions booléennes
5.2Opérateurs relationnelsComparer des valeurs compatibles
5.3Opérateurs arithmétiquesCalculer et dimensionner les résultats
5.4Décalages et rotationsDéplacer ou faire tourner les bits
5.5ConcaténationAssembler et étendre des vecteurs
5.6Priorité des opérateursÉviter les expressions ambiguës
TP 2Unité logique et arithmétiqueConcevoir et vérifier un bloc complet

 

Concept clé — Typage des expressions

Un opérateur VHDL n’est disponible que si une définition compatible existe pour les types de ses opérandes. Deux symboles identiques peuvent avoir plusieurs surcharges, mais le compilateur doit pouvoir déterminer sans ambiguïté celle qui convient.

 

5.1 Opérateurs logiques

Les opérateurs logiques combinent des valeurs booléennes ou logiques. Sur des vecteurs de même longueur, l’opération est généralement appliquée bit par bit. Ils sont utilisés pour traduire des équations de Boole, construire des masques et générer des signaux de commande.

5.1.1 Vue d’ensemble

Opérateur

Expression

Résultat pour bits

Interprétation

anda and b1 si a=1 et b=1Conjonction
ora or b1 si au moins une entrée vaut 1Disjonction inclusive
nanda nand bnot(a and b)ET nié
nora nor bnot(a or b)OU nié
xora xor b1 si a et b sont différentsOU exclusif
xnora xnor b1 si a et b sont identiquesÉquivalence
notnot aInverse la valeurNégation unaire

 

5.1.2 Opérateur and

L’opérateur and produit un niveau actif uniquement lorsque tous les opérandes concernés sont actifs.

Porte ET à deux entrées  |  VHDL
y <= a and b;

 

Masquage d’un vecteur  |  VHDL
signal donnees : std_logic_vector(7 downto 0);
signal masque  : std_logic_vector(7 downto 0);
signal sortie  : std_logic_vector(7 downto 0);

sortie <= donnees and masque;

 

a

b

a and b

000
010
100
111

 

5.1.3 Opérateur or

L’opérateur or produit 1 lorsqu’au moins un opérande vaut 1.

Combinaison de deux demandes  |  VHDL
demande_globale <= demande_a or demande_b;

 

a

b

a or b

000
011
101
111

 

5.1.4 Opérateur nand

nand est la négation de and. Il produit 0 uniquement lorsque les deux entrées valent 1.

Porte NAND  |   VHDL
y <= a nand b;

-- Écriture équivalente
y_equiv <= not (a and b);

 

Lisibilité — Parenthèses recommandées

Même lorsqu’un opérateur composé existe, l’écriture not (a and b) peut être plus explicite lors d’une démonstration. L’opérateur nand reste concis et directement synthétisable.

 

5.1.5 Opérateur nor

nor est la négation de or. Il produit 1 uniquement lorsque toutes les entrées valent 0.

Porte NOR  |   VHDL
y <= a nor b;

-- Écriture équivalente
y_equiv <= not (a or b);

 

5.1.6 Opérateur xor

xor produit 1 lorsque les opérandes binaires sont différents. Il est utilisé dans les additionneurs, les calculs de parité et certains algorithmes de détection d’erreur.

Porte XOR  |   VHDL
y <= a xor b;

 

Parité de quatre bits  |   VHDL
parite <= d(3) xor d(2) xor d(1) xor d(0);

 

a

b

a xor b

000
011
101
110

 

5.1.7 Opérateur xnor

xnor est le complément de xor. Il produit 1 lorsque les entrées sont égales.

Détection d’égalité bit à bit  |  VHDL
egaux_bit <= a xnor b;

 

Comparaison bit à bit d’un bus  |  VHDL
comparaison_bits <= bus_a xnor bus_b;

 

Distinction — Vecteur xnor

Sur un vecteur, xnor fournit un vecteur de résultats bit à bit. Pour tester l’égalité globale de deux mots, l’opérateur relationnel = est généralement plus direct.

 

5.1.8 Opérateur not

not est un opérateur unaire. Il inverse un bit ou chaque élément d’un vecteur.

Inversion scalaire et vectorielle  |  VHDL
n_a <= not a;
complement <= not donnees;

 

5.1.9 Opérations logiques sur std_logic

Lorsque les opérandes contiennent des valeurs telles que 'U', 'X' ou 'Z', le résultat suit les tables définies par IEEE.STD_LOGIC_1164. Une inconnue peut se propager, sauf lorsque le résultat est imposé par une valeur dominante.

Expression

Résultat

Explication

'0' and 'X''0'Le zéro impose le résultat de l’ET
'1' and 'X''X'Le résultat dépend de l’inconnue
'1' or 'X''1'Le un impose le résultat du OU
'0' or 'X''X'Le résultat dépend de l’inconnue
not 'Z''X'La haute impédance n’est pas un bit binaire connu

 

5.1.10 Réduction logique

Pour déterminer si tous les bits d’un vecteur sont à 1 ou si au moins un bit est actif, on peut utiliser une boucle portable. VHDL-2008 introduit également des formes de réduction unaire selon les outils.

Réduction AND portable  |  VHDL
process(donnees)
    variable tous_a_un : std_logic;
begin
    tous_a_un := '1';

    for i in donnees'range loop
        tous_a_un := tous_a_un and donnees(i);
    end loop;

    resultat <= tous_a_un;
end process;

 

Réduction OR portable  |  VHDL
process(donnees)
    variable au_moins_un : std_logic;
begin
    au_moins_un := '0';

    for i in donnees'range loop
        au_moins_un := au_moins_un or donnees(i);
    end loop;

    resultat <= au_moins_un;
end process;

 

5.1.11 Exemple combiné

Fonction logique à quatre entrées  |  VHDL
y <= ((a and b) or (c xor d))
     and not inhibition;

 

Bonne pratique — Décomposition utile

Pour faciliter la simulation et le débogage, une expression complexe peut être divisée en signaux intermédiaires nommés.

 

Version décomposée  |  VHDL
signal terme_et  : std_logic;
signal terme_xor : std_logic;
signal demande   : std_logic;

terme_et  <= a and b;
terme_xor <= c xor d;
demande   <= terme_et or terme_xor;
y         <= demande and not inhibition;

 

5.2 Opérateurs relationnels

Les opérateurs relationnels comparent deux valeurs compatibles et produisent un résultat de type boolean. Ils sont utilisés dans les conditions if, les affectations conditionnelles et les assertions.

Opérateur

Signification

Exemple

=Égal àa = b
/=Différent dea /= b
<Strictement inférieurcompteur < limite
<=Inférieur ou égalvaleur <= maximum
>Strictement supérieura > b
>=Supérieur ou égaltemperature >= seuil

 

5.2.1 Égalité =

Test d’égalité  |   VHDL
if a = b then
    egaux <= '1';
else
    egaux <= '0';
end if;

 

Pour std_logic_vector, l’égalité compare les éléments position par position. Les valeurs inconnues sont comparées comme des valeurs du type ; une égalité contenant 'X' n’a pas le même sens qu’une comparaison physique de niveaux binaires.

Affectation conditionnelle  |  VHDL
egaux <= '1' when mot_a = mot_b else '0';

 

5.2.2 Différence /=

Détection de modification  |  VHDL
modifie <= '1' when valeur_nouvelle /= valeur_ancienne
           else '0';

 

5.2.3 Relations d’ordre

Les opérateurs <, <=, > et >= dépendent du type. Sur unsigned, la comparaison est non signée ; sur signed, elle respecte le complément à deux ; sur integer, elle suit l’ordre numérique.

Comparaison non signée  |  VHDL
signal a_u, b_u : unsigned(7 downto 0);

a_superieur <= '1' when a_u > b_u else '0';

 

Comparaison signée  |  VHDL
signal x_s, y_s : signed(7 downto 0);

x_negatif_par_rapport_y <= '1'
    when x_s < y_s
    else '0';

 

Attention — Attention au symbole <=

Dans une expression, <= peut être l’opérateur « inférieur ou égal ». Dans une instruction d’affectation, le même symbole sert à affecter un signal. Le contexte syntaxique permet au compilateur de les distinguer.

 

5.2.4 Comparaison de std_logic_vector

Une comparaison directe de deux std_logic_vector suit l’ordre prédéfini des éléments du tableau et ne traduit pas nécessairement l’ordre numérique recherché. Pour une comparaison numérique portable et explicite, convertir les vecteurs en unsigned ou signed.

Comparaison non signée de deux ports  |  VHDL
plus_grand <= '1'
    when unsigned(a_slv) > unsigned(b_slv)
    else '0';

 

Comparaison signée de deux ports  |  VHDL
plus_petit <= '1'
    when signed(a_slv) < signed(b_slv)
    else '0';

 

5.2.5 Exemple montrant l’impact du signe

Bits

Interprétation unsigned

Interprétation signed

"11111111"255-1
"10000000"128-128
"01111111"127127

 

Ainsi, comparer «11111111» et «00000001» donne vrai pour la relation > en unsigned, mais faux en signed.

5.2.6 Comparaisons avec des constantes

Comparaison avec un entier  |  VHDL
signal compteur : unsigned(7 downto 0);

fin <= '1'
    when compteur = to_unsigned(99, compteur'length)
    else '0';

 

Comparaison simplifiée autorisée par numeric_std   |  VHDL
if compteur >= 100 then
    compteur <= (others => '0');
end if;

 

Conseil — Portabilité

Même lorsque la surcharge avec un entier naturel est acceptée, une conversion explicite peut rendre la largeur et l’intention plus visibles dans un support pédagogique.

 

5.2.7 Comparaison dans une instruction assert

Vérification automatique  |  VHDL
assert resultat = attendu
    report "Le resultat calcule est incorrect"
    severity error;

 

5.3 Opérateurs arithmétiques

Les opérateurs arithmétiques manipulent des types numériques. Avec IEEE.NUMERIC_STD, les types signed et unsigned peuvent être additionnés, soustraits, multipliés et comparés. La division et les opérations mod/rem doivent être utilisées avec prudence en synthèse en raison du coût matériel.

5.3.1 Addition

Addition d’entiers  |  VHDL
signal a, b : integer range 0 to 100;
signal s    : integer range 0 to 200;

s <= a + b;

 

Addition unsigned avec retenue  |  VHDL
signal a, b : unsigned(7 downto 0);
signal s    : unsigned(8 downto 0);

s <= resize(a, s'length)
   + resize(b, s'length);

 

Addition signed  |  VHDL
signal x, y : signed(7 downto 0);
signal r    : signed(8 downto 0);

r <= resize(x, r'length)
   + resize(y, r'length);

 

Dimensionnement — Largeur du résultat

Pour conserver une retenue, le résultat d’une addition de deux mots non signés de N bits doit généralement disposer de N+1 bits. Pour une addition signée, un bit supplémentaire évite le débordement pour la plage complète.

 

5.3.2 Soustraction

Soustraction signed  |  VHDL
signal a, b : signed(7 downto 0);
signal d    : signed(8 downto 0);

d <= resize(a, d'length)
   - resize(b, d'length);

 

Soustraction d’unsigned pouvant être négative   |  VHDL
signal a, b : unsigned(7 downto 0);
signal d    : signed(8 downto 0);

d <= signed('0' & a)
   - signed('0' & b);

 

5.3.3 Multiplication

Le produit de deux opérandes de N et M bits peut nécessiter jusqu’à N+M bits. Les FPGA disposent souvent de blocs DSP optimisés pour cette opération.

Multiplication unsigned  |  VHDL
signal a, b : unsigned(7 downto 0);
signal p    : unsigned(15 downto 0);

p <= a * b;

 

Multiplication signée  |  VHDL
signal x, y : signed(11 downto 0);
signal p    : signed(23 downto 0);

p <= x * y;

 

Attention — Troncature

Affecter un produit à un vecteur trop court peut supprimer les bits de poids fort ou provoquer une erreur de taille. Toujours définir explicitement la politique de conservation, d’arrondi ou de saturation.

 

5.3.4 Division

La division entière calcule le quotient sans partie fractionnaire. Elle est synthétisable dans certains contextes, mais une division par une valeur variable peut consommer beaucoup de ressources et réduire la fréquence maximale.

Division entière  |  VHDL
signal nombre   : unsigned(15 downto 0);
signal quotient : unsigned(15 downto 0);

quotient <= nombre / 10;

 

Division par une puissance de deux  |  VHDL
-- Division non signée par 8
quotient <= shift_right(nombre, 3);

 

Matériel — Optimisation

Une division non signée par 2^k peut être réalisée par un décalage logique vers la droite de k positions. Pour les nombres signés, il faut vérifier la règle d’arrondi souhaitée.

 

5.3.5 Modulo mod

L’opérateur mod fournit un résultat dont le signe suit le second opérande. Il est utile pour le comptage cyclique, l’indexation et les calculs périodiques.

Modulo positif  |   VHDL
reste_cycle <= compteur mod 10;

 

Expression

Résultat

17 mod 52
-17 mod 53
17 mod -5-3
-17 mod -5-2

 

5.3.6 Reste rem

L’opérateur rem fournit un reste dont le signe suit le premier opérande. Il diffère donc de mod lorsqu’un opérande est négatif.

Calcul du reste  |  VHDL
r <= a rem b;

 

Expression

Résultat

17 rem 52
-17 rem 5-2
17 rem -52
-17 rem -5-2

 

Distinction — mod ou rem

Avec des opérandes non négatifs, mod et rem donnent le même résultat. La différence devient visible avec des valeurs négatives.

 

5.3.7 Valeur absolue abs

abs est un opérateur unaire qui retourne la valeur absolue d’un nombre. Il s’applique notamment à integer et signed selon les surcharges disponibles.

Valeur absolue d’un entier  |  VHDL
signal erreur : integer range -255 to 255;
signal amplitude : integer range 0 to 255;

amplitude <= abs erreur;

 

Valeur absolue signée  |  VHDL
signal x : signed(7 downto 0);
signal amplitude : signed(7 downto 0);

amplitude <= abs x;

 

Cas limite — Valeur minimale signée

La valeur la plus négative d’un mot signé, par exemple -128 sur 8 bits, n’a pas d’équivalent positif représentable sur la même largeur. Une extension du résultat peut être nécessaire.

 

5.3.8 Puissance et opérateurs unaires

L’opérateur ** calcule une puissance, tandis que les signes unaires + et - indiquent respectivement une valeur inchangée ou opposée. Leur synthèse doit être évaluée selon les constantes et l’outil.

Exemples  |   VHDL
carre <= valeur ** 2;
oppose <= -valeur_signee;

 

5.3.9 Coût matériel des opérations

Opération

Matériel généralement inféré

Coût relatif

Addition / soustractionChaîne de retenue, additionneurFaible à modéré
MultiplicationBloc DSP ou réseau logiqueModéré
Division par constante simpleDécalages et additions optimiséesVariable
Division par opérande variableDiviseur combinatoire ou séquentielÉlevé
mod/rem par puissance de deuxSélection de bitsFaible
abs signéTest de signe et négation conditionnelleFaible à modéré

 

5.4 Opérateurs de décalage

Les décalages déplacent les bits d’un vecteur. Ils servent à multiplier ou diviser par des puissances de deux, à extraire des champs, à construire des registres à décalage et à réaliser des communications série.

5.4.1 Décalage logique

Un décalage logique introduit des zéros dans les positions libérées.

Décalage logique manuel à gauche  |  VHDL
signal a : std_logic_vector(7 downto 0);
signal y : std_logic_vector(7 downto 0);

y <= a(6 downto 0) & '0';

 

Décalage logique manuel à droite  |  VHDL
y <= '0' & a(7 downto 1);

 

5.4.2 Fonctions shift_left et shift_right

IEEE.NUMERIC_STD fournit shift_left et shift_right pour signed et unsigned. Le deuxième argument indique le nombre de positions.

Décalages sur unsigned  |  VHDL
signal a : unsigned(7 downto 0);
signal gauche, droite : unsigned(7 downto 0);

gauche <= shift_left(a, 2);
droite <= shift_right(a, 3);

 

Pour unsigned, les positions libérées sont remplies avec des zéros.

5.4.3 Décalage arithmétique

Le décalage arithmétique vers la droite d’un nombre signed conserve le signe en recopiant le bit de poids fort. Il correspond approximativement à une division par une puissance de deux, avec une règle d’arrondi dépendant de la représentation.

Décalage arithmétique vers la droite  |  VHDL
signal x : signed(7 downto 0);
signal y : signed(7 downto 0);

y <= shift_right(x, 2);

 

Règle — signed et shift_right

Avec un opérande signed, shift_right remplit les bits libérés à gauche avec le bit de signe. Avec unsigned, les bits ajoutés valent zéro.

 

5.4.4 Décalage arithmétique vers la gauche

Un décalage vers la gauche introduit généralement des zéros à droite. Il multiplie la valeur par 2^k tant qu’aucun bit significatif n’est perdu.

Multiplication par quatre  |  VHDL
resultat <= shift_left(valeur, 2);

 

Attention — Débordement

Un décalage ne crée pas automatiquement de bits supplémentaires. Pour préserver la valeur complète, redimensionner avant le décalage.

 

Décalage avec extension préalable  |  VHDL
signal a : unsigned(7 downto 0);
signal y : unsigned(9 downto 0);

y <= shift_left(resize(a, y'length), 2);

 

5.4.5 Rotation

Une rotation réintroduit à l’autre extrémité les bits qui sortent du vecteur. Aucun bit n’est perdu.

Rotation gauche manuelle d’un bit  |  VHDL
rotation_gauche <= a(6 downto 0) & a(7);

 

Rotation droite manuelle d’un bit  |  VHDL
rotation_droite <= a(0) & a(7 downto 1);

 

Opérateurs rol et ror  |   VHDL
rotation_gauche <= a rol 2;
rotation_droite <= a ror 3;

 

Portabilité — Compatibilité des outils

Les opérateurs de rotation et de décalage du langage peuvent être pris en charge selon la version VHDL et l’outil. Les fonctions numeric_std sont souvent privilégiées pour les calculs signed et unsigned.

 

5.4.6 Registre à décalage

Registre à décalage série-parallèle  |  VHDL
process(clk)
begin
    if rising_edge(clk) then
        if enable = '1' then
            registre <= registre(registre'high-1 downto 0)
                        & entree_serie;
        end if;
    end if;
end process;

 

5.4.7 Décalage variable

Lorsque le nombre de positions est variable, le synthétiseur peut inférer un barrel shifter, un réseau de multiplexeurs capable de décaler en un cycle.

Décalage commandé  |  VHDL
signal decalage : natural range 0 to 7;

sortie <= shift_left(entree, decalage);

 

5.4.8 Exemple numérique

Entrée

Opération

Résultat sur 8 bits

"00110101"shift_left(...,1)"01101010"
"00110101"shift_right(...,2)"00001101"
"10110001"rol 1"01100011"
"10110001"ror 1"11011000"

 

5.5 Concaténation

L’opérateur & assemble des valeurs compatibles afin de former un tableau plus long. Il est utilisé pour construire des mots, ajouter des bits, regrouper des champs et effectuer des décalages manuels.

5.5.1 Utilisation de l’opérateur &

Assemblage de deux demi-octets  |  VHDL
signal poids_fort  : std_logic_vector(3 downto 0);
signal poids_faible: std_logic_vector(3 downto 0);
signal octet       : std_logic_vector(7 downto 0);

octet <= poids_fort & poids_faible;

 

5.5.2 Assemblage de bits et de vecteurs

Ajout de bits de contrôle  |  VHDL
trame <= start_bit & donnees & parite & stop_bit;

 

Création d’un mot à partir de bits  |  VHDL
commande <= enable & lecture & ecriture & valide;

 

5.5.3 Concaténation répétée

Un agrégat avec others est souvent plus clair pour remplir plusieurs bits identiques.

Extension par quatre zéros  |  VHDL
mot_12bits <= "0000" & mot_8bits;

-- Variante générique
mot_12bits <= (3 downto 0 => '0') & mot_8bits;

 

5.5.4 Extension d’un mot non signé

L’extension par zéro ajoute des zéros à gauche afin de conserver la valeur d’un nombre non signé.

Extension par zéro  |   VHDL
signal a8  : unsigned(7 downto 0);
signal a12 : unsigned(11 downto 0);

a12 <= "0000" & a8;

 

Extension générique recommandée  |  VHDL
a12 <= resize(a8, a12'length);

 

5.5.5 Extension de signe

Pour un nombre signé, les nouveaux bits de poids fort doivent reproduire le bit de signe.

Extension de signe manuelle  |  VHDL
signal s8  : signed(7 downto 0);
signal s12 : signed(11 downto 0);

s12 <= (3 downto 0 => s8(7)) & s8;

 

Extension de signe avec resize  |  VHDL
s12 <= resize(s8, s12'length);

 

Bonne pratique — Privilégier resize

resize évite les erreurs de calcul du nombre de bits et applique automatiquement une extension par zéro pour unsigned ou une extension de signe pour signed.

 

5.5.6 Construction d’une somme avec retenue

Ajout d’un zéro avant l’addition  |  VHDL
signal a, b : unsigned(7 downto 0);
signal s    : unsigned(8 downto 0);

s <= ('0' & a) + ('0' & b);

 

5.5.7 Extraction et réassemblage de champs

Découpage d’une instruction  |  VHDL
opcode   <= instruction(15 downto 12);
registre_a<= instruction(11 downto 8);
registre_b<= instruction(7 downto 4);
immediat  <= instruction(3 downto 0);

 

Réassemblage  |   VHDL
instruction <= opcode
             & registre_a
             & registre_b
             & immediat;

 

5.5.8 Sens et dimensions

La longueur du résultat d’une concaténation est la somme des longueurs des opérandes. L’opérande de gauche devient la partie gauche du résultat.

Expression

Longueur

Résultat

"10" & "0110"6"100110"
'1' & "001"4"1001"
"101" & '0'4"1010"

 

5.6 Priorité des opérateurs

La priorité détermine l’ordre dans lequel les opérateurs sont regroupés lorsque les parenthèses sont absentes. Toutefois, la lisibilité d’une description matérielle est aussi importante que sa validité syntaxique : les parenthèses doivent rendre l’intention évidente.

5.6.1 Principes généraux

  • Les expressions entre parenthèses sont évaluées en premier.
  • Les opérateurs unaires et de puissance ont une priorité élevée.
  • Les multiplications, divisions, mod et rem précèdent généralement les additions et soustractions.
  • La concaténation appartient à la famille des opérateurs d’addition dans la grammaire VHDL.
  • Les opérateurs de décalage sont évalués avant les relations.
  • Les opérateurs relationnels produisent un boolean.
  • Les opérateurs logiques sont évalués après les relations, selon leur classe.

5.6.2 Tableau pédagogique de priorité

Niveau approximatif

Famille

Opérateurs ou constructions

1 — le plus élevéParenthèses / primaire(expression), littéraux, appels de fonction
2Divers unaire / puissance**, abs, not, signe unaire
3Multiplicatif*, /, mod, rem
4Additif / concaténation+ , - , &
5Décalagesll, srl, sla, sra, rol, ror
6Relationnel=, /=, <, <=, >, >=
7 — le plus faibleLogiqueand, or, nand, nor, xor, xnor

 

Prudence — Tableau d’apprentissage

Ce tableau sert à guider la lecture des expressions usuelles. Pour éviter toute dépendance à une mémorisation imparfaite ou à des restrictions syntaxiques entre versions, ajouter des parenthèses dès que plusieurs familles d’opérateurs sont combinées.

 

5.6.3 Ordre d’évaluation

Expression arithmétique  |  VHDL
resultat <= a + b * c;

-- Interprétation recherchée
resultat <= a + (b * c);

 

Expression logique  |  VHDL
y <= a and b or c;

-- Forme explicitement documentée
y <= (a and b) or c;

 

Même lorsqu’une expression est légalement interprétée par le compilateur, la version parenthésée est généralement préférable pour une équipe de développement.

5.6.4 Utilisation des parenthèses

Expression claire  |  VHDL
y <= ((a xor b) and enable)
     or (reset and not inhibition);

 

Calcul numérique clair  |  VHDL
somme_moyenne <=
    (resize(a, 10) + resize(b, 10)) / 2;

 

5.6.5 Prévention des ambiguïtés de typage

Une ambiguïté ne vient pas seulement de la priorité. Elle peut provenir de plusieurs surcharges possibles ou d’une taille de résultat non déterminée.

Expression potentiellement ambiguë  |  VHDL
resultat <= a + 1;

 

Si a est unsigned, le littéral 1 peut être résolu par numeric_std, mais une conversion explicite améliore parfois la compréhension.

Version explicite  |  VHDL
resultat <= a
          + to_unsigned(1, a'length);

 

5.6.6 Parenthèses et conversions

Conversion de l’ensemble du résultat  |  VHDL
s_slv <= std_logic_vector(
    resize(unsigned(a_slv), 9)
    + resize(unsigned(b_slv), 9)
);

 

Les parenthèses indiquent que l’addition est effectuée sur des unsigned et que seule la somme finale est convertie en std_logic_vector.

5.6.7 Erreurs fréquentes

Erreur

Conséquence

Correction

Oublier les parenthèses autour d’une condition composéeIntention difficile à lireRegrouper explicitement chaque sous-condition
Mélanger signed et unsignedAucune surcharge compatibleConvertir les deux opérandes vers un type commun
Additionner deux std_logic_vectorOpérateur + introuvable avec numeric_stdConvertir en unsigned ou signed
Sous-dimensionner le résultatPerte de bits ou erreur de tailleresize ou destination élargie
Utiliser un décalage après une conversion mal placéeDécalage appliqué au mauvais typeParenthéser la conversion et l’opération
Confondre <= relationnel et affectationLecture incorrecte du codeIdentifier le contexte ou reformuler

 

5.6.8 Recommandations de style

  • Une expression complexe ne doit pas tenir sur une seule ligne si sa lecture devient difficile.
  • Nommer les résultats intermédiaires significatifs.
  • Utiliser des conversions proches des données auxquelles elles s’appliquent.
  • Dimensionner explicitement les additions, produits et décalages.
  • Ajouter un commentaire lorsque l’opération réalise une optimisation matérielle.
  • Tester les valeurs limites, les nombres négatifs et les débordements.

Travaux pratiques

TP 2 — Opérations logiques et arithmétiques

1. Objectifs du TP

  • réaliser une unité logique simple sélectionnée par un code d’opération ;
  • additionner deux nombres binaires non signés en conservant la retenue ;
  • comparer deux mots et produire trois indicateurs ;
  • effectuer des décalages logiques commandés ;
  • vérifier les résultats dans un simulateur ;
  • construire un banc de test auto-vérifiant avec assertions.

2. Cahier des charges

Le composant reçoit deux opérandes a et b sur 8 bits, un code operation sur 3 bits et une quantité de décalage sur 3 bits. Il produit un résultat sur 9 bits ainsi que les indicateurs egal, inferieur, superieur et retenue.

Code operation

Fonction

Résultat

"000"AND bit à bit0 & (a AND b)
"001"OR bit à bit0 & (a OR b)
"010"XOR bit à bit0 & (a XOR b)
"011"NOT de a0 & (NOT a)
"100"Addition a + bSomme sur 9 bits
"101"Soustraction a - bRésultat sur 9 bits signés ou politique définie
"110"Décalage gauche de aDécalage logique
"111"Décalage droit de aDécalage logique

 

3. Organisation proposée

Arborescence du projet  |  Arborescence
tp2_operateurs/
├── src/
│   └── unite_ops.vhd
└── sim/
    └── tb_unite_ops.vhd

 

4. Entité de l’unité

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

entity unite_ops is
    port (
        a, b       : in   std_logic_vector(7 downto 0);
        operation  : in   std_logic_vector(2 downto 0);
        decalage   : in   unsigned(2 downto 0);

        resultat   : out std_logic_vector(8 downto 0);
        retenue    : out std_logic;
        egal       : out std_logic;
        inferieur  : out std_logic;
        superieur  : out std_logic
    );
end entity unite_ops;

 

5. Architecture — déclarations

unite_ops.vhd — Signaux internes  |  VHDL
architecture rtl of unite_ops is
    signal a_u       : unsigned(7 downto 0);
    signal b_u       : unsigned(7 downto 0);
    signal somme_u   : unsigned(8 downto 0);
    signal resultat_u: unsigned(8 downto 0);
begin
    a_u <= unsigned(a);
    b_u <= unsigned(b);

    somme_u <= resize(a_u, 9)
             + resize(b_u, 9);

 

6. Architecture — comparaisons

Indicateurs relationnels  |  VHDL
    egal <= '1' when a_u = b_u else '0';

    inferieur <= '1' when a_u < b_u else '0';

    superieur <= '1' when a_u > b_u else '0';

 

7. Architecture — sélection des opérations

Processus combinatoire — Partie 1  |  VHDL
    process(a_u, b_u, operation, decalage, somme_u)
    begin
        resultat_u <= (others => '0');

        case operation is
            when "000" =>
                resultat_u <= '0' & (a_u and b_u);

            when "001" =>
                resultat_u <= '0' & (a_u or b_u);

            when "010" =>
                resultat_u <= '0' & (a_u xor b_u);

            when "011" =>
                resultat_u <= '0' & (not a_u);

 

Processus combinatoire — Partie 2  |  VHDL
            when "100" =>
                resultat_u <= somme_u;

            when "101" =>
                resultat_u <= resize(a_u, 9)
                             - resize(b_u, 9);

            when "110" =>
                resultat_u <= shift_left(
                    resize(a_u, 9),
                     to_integer(decalage)
                );

            when others =>
                resultat_u <= shift_right(
                    resize(a_u, 9),
                     to_integer(decalage)
                );
        end case;
    end process;

 

Affectations finales  |  VHDL
    resultat <= std_logic_vector(resultat_u);

    retenue <= somme_u(8)
        when operation = "100"
        else '0';

end architecture rtl;

 

Limite à analyser — Soustraction non signée

La soustraction proposée travaille sur unsigned. Si a < b, le résultat s’enroule modulo 2^9. Une extension du TP peut ajouter une sortie signed ou un indicateur emprunt afin de représenter explicitement un résultat négatif.

 

8. Banc de test — déclarations

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

entity tb_unite_ops is
end entity tb_unite_ops;

architecture simulation of tb_unite_ops is
    signal a, b      : std_logic_vector(7 downto 0)
                        := (others => '0');
    signal operation : std_logic_vector(2 downto 0)
                        := (others => '0');
    signal decalage  : unsigned(2 downto 0)
                        := (others => '0');

    signal resultat  : std_logic_vector(8 downto 0);
    signal retenue   : std_logic;
    signal egal      : std_logic;
    signal inferieur : std_logic;
    signal superieur : std_logic;
begin

 

9. Banc de test — instanciation

Instanciation du composant  |  VHDL
    DUT : entity work.unite_ops(rtl)
        port map (
            a          => a,
            b          => b,
            operation  => operation,
            decalage   => decalage,
            resultat   => resultat,
            retenue    => retenue,
            egal       => egal,
            inferieur  => inferieur,
            superieur  => superieur
        );

 

10. Banc de test — opérations logiques

Tests AND, OR et XOR  |   VHDL
    stimuli : process
    begin
        a <= x"55";
        b <= x"0F";

        operation <= "000";
        wait for 10 ns;
        assert resultat = ('0' & x"05")
            report "Erreur operation AND"
            severity error;

        operation <= "001";
        wait for 10 ns;
        assert resultat = ('0' & x"5F")
            report "Erreur operation OR"
            severity error;

        operation <= "010";
        wait for 10 ns;
        assert resultat = ('0' & x"5A")
            report "Erreur operation XOR"
            severity error;

 

11. Banc de test — addition

Test de l’addition et de la retenue  |  VHDL
        a <= std_logic_vector(to_unsigned(200, 8));
        b <= std_logic_vector(to_unsigned(100, 8));
        operation <= "100";
        wait for 10 ns;

        assert unsigned(resultat) = to_unsigned(300, 9)
            report "Erreur addition 200 + 100"
            severity error;

        assert retenue = '1'
            report "Retenue incorrecte"
            severity error;

 

12. Banc de test — comparaisons

Tests relationnels  |  VHDL
        a <= x"14";  -- 20
        b <= x"1E";  -- 30
        wait for 10 ns;

        assert egal = '0'
            report "Indicateur egal incorrect"
            severity error;

        assert inferieur = '1'
            report "Indicateur inferieur incorrect"
            severity error;

        assert superieur = '0'
            report "Indicateur superieur incorrect"
            severity error;

 

13. Banc de test — décalages

Tests de décalage  |  VHDL
        a <= "00010110";  -- 22
        decalage <= to_unsigned(2, decalage'length);

        operation <= "110";
        wait for 10 ns;
        assert unsigned(resultat) = to_unsigned(88, 9)
            report "Erreur decalage gauche"
            severity error;

        operation <= "111";
        wait for 10 ns;
        assert unsigned(resultat) = to_unsigned(5, 9)
            report "Erreur decalage droit"
            severity error;

 

14. Fin du banc de test

Clôture de la simulation  |  VHDL
        report "Tous les tests du TP 2 sont termines"
            severity note;

        stop;
        wait;
    end process;
end architecture simulation;

 

15. Compilation avec GHDL

Analyse, élaboration et simulation  |  Terminal
ghdl -a --std=08 src/unite_ops.vhd
ghdl -a --std=08 sim/tb_unite_ops.vhd
ghdl -e --std=08 tb_unite_ops

ghdl -r --std=08 tb_unite_ops    --vcd=tb_unite_ops.vcd

 

Ouverture du chronogramme  |  Terminal
gtkwave tb_unite_ops.vcd

 

16. Observations demandées

  • Relever les valeurs de a, b, operation et resultat pour chaque scénario.
  • Vérifier que les opérations logiques sont effectuées bit par bit.
  • Observer le neuvième bit lors de l’addition 200 + 100.
  • Comparer les indicateurs egal, inferieur et superieur.
  • Observer les zéros introduits lors des décalages.
  • Identifier le résultat obtenu pour une soustraction avec a < b.
  • Confirmer qu’aucune assertion de niveau error n’est affichée.

17. Tableau de résultats à compléter

a

b

Opération

Décalage

Résultat observé

Indicateurs

0x550x0FAND  
0x550x0FOR  
0x550x0FXOR  
200100ADD  
2030Comparaison  
22SHIFT LEFT2  
22SHIFT RIGHT2  

 

18. Questions d’analyse

  1. Pourquoi les entrées a et b sont-elles converties en unsigned ?
  2. Pourquoi la somme comporte-t-elle neuf bits ?
  3. Que représente le bit resultat(8) pour les opérations logiques ?
  4. Quelle différence existerait si a et b étaient interprétés comme signed ?
  5. Quel matériel peut être inféré lorsque decalage est variable ?
  6. Pourquoi une valeur par défaut est-elle affectée à resultat_u au début du processus ?
  7. Comment modifier le circuit pour signaler un emprunt lors d’une soustraction ?
  8. Comment ajouter une opération de rotation gauche ?
  9. Pourquoi les parenthèses et resize sont-ils importants dans les expressions numériques ?

19. Extensions proposées

  • Ajouter les opérations NAND, NOR et XNOR.
  • Ajouter une rotation gauche et une rotation droite.
  • Produire un indicateur zero lorsque le résultat est nul.
  • Produire un indicateur negatif pour une version signed.
  • Ajouter un mode de saturation pour l’addition.
  • Créer une unité arithmétique signée distincte.
  • Implanter le composant sur FPGA avec interrupteurs, LED et afficheurs.

20. Barème indicatif

Critère

Points

Entité et types des ports2
Opérations logiques3
Addition et gestion de la retenue3
Comparaisons2
Décalages2
Processus combinatoire complet2
Banc de test et assertions3
Analyse des résultats2
Qualité du code et du compte rendu1
Total20

 

Synthèse du chapitre

Notion

Résumé

and, or, nand, norCombinaisons logiques de base
xor, xnorDifférence ou équivalence logique
notNégation d’un bit ou d’un vecteur
=, /=, <, <=, >, >=Comparaisons produisant un boolean
+, -, *, /Opérations arithmétiques dépendant du type et de la largeur
mod et remDeux définitions du reste, différentes avec des nombres négatifs
absValeur absolue d’un nombre
shift_left/rightDécalages normalisés pour signed et unsigned
rol, rorRotations sans perte des bits
&Concaténation de bits et de vecteurs
resizeExtension par zéro ou extension de signe
ParenthèsesMoyen principal d’expliciter l’ordre et le typage

 

Autoévaluation

1. Quel opérateur réalise un OU exclusif ?

Réponse : xor.

2. Quel opérateur teste la différence ?

Réponse : /=.

3. Quelle largeur minimale peut conserver la somme de deux unsigned de 8 bits ?

Réponse : 9 bits.

4. Quelle différence existe entre mod et rem ?

Réponse : Le signe du résultat : mod suit le diviseur, rem suit le dividende.

5. Quelle fonction décale un unsigned vers la droite ?

Réponse : shift_right.

6. Comment effectuer une extension de signe sûre ?

Réponse : Avec resize sur un signed.

7. Que fait l’opérateur & ?

Réponse : Il concatène des éléments ou des vecteurs.

8. Pourquoi utiliser des parenthèses ?

Réponse : Pour expliciter l’ordre d’évaluation et éviter les ambiguïtés.

9. Comment comparer numériquement deux std_logic_vector non signés ?

Réponse : Les convertir en unsigned.

10. Que faut-il vérifier avant une multiplication ?

Réponse : La largeur du produit et le coût matériel.

Exercice de consolidation

Écrire une expression VHDL produisant un résultat de 11 bits correspondant à la somme d’un mot unsigned de 8 bits et de sa version décalée de deux positions vers la gauche. Justifier pourquoi 11 bits sont nécessaires pour conserver tous les résultats possibles.

Correction proposée  |  VHDL
signal a : unsigned(7 downto 0);
signal resultat : unsigned(10 downto 0);

resultat <= resize(a, resultat'length)
          + shift_left(
                resize(a, resultat'length),
                2
            );

 

Suite du cours — Transition vers le chapitre suivant

Après la maîtrise des opérateurs et expressions, le cours peut aborder les affectations concurrentes conditionnelles et sélectionnées, qui permettent de décrire efficacement multiplexeurs, décodeurs et autres circuits combinatoires.