Chapitre 19 — Arithmétique numérique en VHDL
Objectif — Finalité du chapitre Maîtriser les représentations signées et non signées, les opérations arithmétiques, la virgule fixe et la conception d’une unité arithmétique et logique 8 bits avec indicateurs. |
Objectifs pédagogiques
À la fin de ce chapitre, l’étudiant devra être capable de :
- interpréter un vecteur comme unsigned ou signed ;
- calculer la plage représentable sur N bits ;
- expliquer le complément à deux et l’extension de signe ;
- détecter le report et le débordement signé ;
- dimensionner les additions, soustractions et multiplications ;
- favoriser l’utilisation des blocs DSP ;
- représenter une valeur en virgule fixe QI.F ;
- réaliser addition, multiplication, troncature et arrondi en virgule fixe ;
- mettre en œuvre une saturation ;
- concevoir une UAL 8 bits et ses indicateurs ;
- vérifier automatiquement les opérations et les cas limites.
Prérequis
- types signed et unsigned de numeric_std ;
- opérateurs VHDL et conversions ;
- processus combinatoires ;
- synthèse logique et blocs DSP ;
- bancs de test automatisés.
Organisation du chapitre
Section | Contenu | Compétence principale |
|---|---|---|
| 19.1 | Représentation des nombres | Interpréter et dimensionner |
| 19.2 | Addition et soustraction | Gérer report et débordement |
| 19.3 | Multiplication | Dimensionner et exploiter les DSP |
| 19.4 | Virgule fixe | Traiter des nombres fractionnaires |
| 19.5 | Unité arithmétique et logique | Combiner opérations et indicateurs |
| TP 15 | UAL 8 bits | Vérifier et analyser le matériel |
Concept clé — Les bits n’ont pas de signe intrinsèque Le même motif binaire peut représenter des valeurs différentes selon qu’il est interprété comme unsigned, signed ou nombre en virgule fixe. |
19.1 Représentation des nombres
L’arithmétique VHDL exige de choisir explicitement le type numérique et la largeur. Cette décision détermine la plage, les conversions et le comportement des opérateurs.
19.1.1 Entiers non signés
Un unsigned de N bits représente les entiers de 0 à 2^N - 1.
Largeur N | Minimum | Maximum |
|---|---|---|
| 4 bits | 0 | 15 |
| 8 bits | 0 | 255 |
| 12 bits | 0 | 4 095 |
| 16 bits | 0 | 65 535 |
| Déclaration non signée | VHDL |
| signal compteur : unsigned(7 downto 0); signal adresse : unsigned(11 downto 0); |
19.1.2 Conversion non signée
| Conversions usuelles | VHDL |
| u <= unsigned(vecteur_slv); vecteur_slv <= std_logic_vector(u); u <= to_unsigned(150, u'length); valeur_entiere := to_integer(u); |
19.1.3 Entiers signés
Le type signed de numeric_std utilise le complément à deux. Sur N bits, la plage est -2^(N-1) à 2^(N-1)-1.
Largeur N | Minimum | Maximum |
|---|---|---|
| 4 bits | -8 | 7 |
| 8 bits | -128 | 127 |
| 12 bits | -2 048 | 2 047 |
| 16 bits | -32 768 | 32 767 |
| Déclaration signée | VHDL |
| signal temperature : signed(11 downto 0); signal erreur : signed(15 downto 0); |
19.1.4 Même motif, deux valeurs
Motif 8 bits | unsigned | signed |
|---|---|---|
| 00000101 | 5 | 5 |
| 01111111 | 127 | 127 |
| 10000000 | 128 | -128 |
| 11111111 | 255 | -1 |
| 11110110 | 246 | -10 |
19.1.5 Complément à deux
Pour coder -x sur N bits, on inverse les bits du code de x puis on ajoute 1, en conservant N bits.
Étape pour -5 sur 8 bits | Valeur |
|---|---|
| +5 | 00000101 |
| Inversion | 11111010 |
| Ajout de 1 | 11111011 |
| Interprétation signed | -5 |
| Négation signée | VHDL |
| resultat <= -signed(entree); |
19.1.6 Valeur minimale particulière
Sur 8 bits signés, -128 est représentable, mais +128 ne l’est pas. La négation de la valeur minimale déborde si la largeur reste inchangée.
Attention — Asymétrie La plage signée contient une valeur négative supplémentaire : -128 à 127 sur 8 bits. |
19.1.7 Extension par zéro
| Extension d’un unsigned | VHDL |
| u_large <= resize(u_court, u_large'length); -- Équivalent conceptuel : u_large <= "0000" & u_court; |
Les nouveaux bits de poids fort valent zéro.
19.1.8 Extension de signe
Pour conserver la valeur d’un signed, les nouveaux bits doivent recopier le bit de signe.
| Extension signée | VHDL |
| s_large <= resize(s_court, s_large'length); -- Exemple manuel : s_large <= (7 downto 4 => s_court(3)) & s_court; |
19.1.9 Exemple d’extension
Valeur | 4 bits | Extension à 8 bits |
|---|---|---|
| +5 | 0101 | 00000101 |
| -3 | 1101 | 11111101 |
| -8 | 1000 | 11111000 |
19.1.10 Réduction de largeur
La réduction supprime des bits et peut modifier la valeur. resize conserve les bits de poids faible pour unsigned et maintient le bit de signe selon les règles du type signed, mais ne garantit pas l’absence de dépassement.
| Réduction explicite | VHDL |
| sortie8 <= entree16(7 downto 0); |
Dimensionnement — Vérifier avant de réduire Comparer la valeur aux limites de la destination, puis choisir troncature, arrondi, saturation ou signalement d’erreur. |
19.1.11 Débordement non signé
Un débordement non signé se produit lorsque le résultat dépasse 2^N-1. Un bit supplémentaire permet de conserver le report.
| Addition étendue | VHDL |
| somme_etendue <= ('0' & a) + ('0' & b); resultat <= somme_etendue(N-1 downto 0); report <= somme_etendue(N); |
19.1.12 Débordement signé
Un débordement signé apparaît lorsque le résultat mathématique sort de la plage, et non simplement lorsqu’un report est produit.
Opération 8 bits | Résultat binaire | Report | Débordement signé |
|---|---|---|---|
| 127 + 1 | 10000000 (-128) | 0 | Oui |
| -128 - 1 | 01111111 (127) | 0 | Oui |
| -1 + 1 | 00000000 | 1 | Non |
| 100 + 20 | 01111000 (120) | 0 | Non |
19.1.13 Détection par les signes
- Addition : opérandes de même signe et résultat de signe différent.
- Soustraction : opérandes de signes différents et résultat de signe différent de a.
19.1.14 Valeur absolue
| Valeur absolue signée | VHDL |
| magnitude <= abs(valeur_signee); |
Le cas de la valeur minimale doit être traité avec une largeur supplémentaire si sa magnitude positive doit être représentée.
19.1.15 Littéraux et largeur
| Constantes typées | VHDL |
| constant MAX_U8 : unsigned(7 downto 0) := to_unsigned(255, 8); constant MIN_S8 : signed(7 downto 0) := to_signed(-128, 8); |
19.2 Addition et soustraction
Les opérateurs + et - sont définis par numeric_std pour signed et unsigned. La largeur du résultat doit être planifiée explicitement.
19.2.1 Additionneur parallèle
Un additionneur parallèle calcule tous les bits d’une addition dans un même bloc combinatoire. Sur FPGA, les chaînes de retenue dédiées accélèrent ce calcul.
| Addition N bits | VHDL |
| somme <= a + b; |
Si somme possède la même largeur que a et b, le report final est perdu.
19.2.2 Addition avec report
| Somme sur N+1 bits | VHDL |
| signal ext_a, ext_b : unsigned(N downto 0); signal ext_somme : unsigned(N downto 0); ext_a <= resize(a, N+1); ext_b <= resize(b, N+1); ext_somme <= ext_a + ext_b; somme <= ext_somme(N-1 downto 0); carry <= ext_somme(N); |
19.2.3 Addition avec retenue d’entrée
| Addition a + b + cin | VHDL |
| process(all) variable total : unsigned(N downto 0); begin total := resize(a, N+1) + resize(b, N+1); if cin = '1' then total := total + 1; end if; somme <= total(N-1 downto 0); cout <= total(N); end process; |
19.2.4 Débordement de l’addition signée
| Formule logique | VHDL |
| overflow_add <= (not (a(a'high) xor b(b'high))) and (resultat(resultat'high) xor a(a'high)); |
Signe a | Signe b | Signe résultat | Overflow |
|---|---|---|---|
| 0 | 0 | 1 | 1 |
| 1 | 1 | 0 | 1 |
| 0 | 1 | 0 ou 1 | 0 |
| 1 | 0 | 0 ou 1 | 0 |
19.2.5 Soustracteur
| Soustraction non signée modulo 2^N | VHDL |
| difference <= a - b; |
Si a < b, le résultat s’enroule dans la largeur disponible. Un indicateur d’emprunt doit compléter la sortie.
19.2.6 Détection de l’emprunt
| Borrow | VHDL |
| borrow <= '1' when a < b else '0'; difference <= a - b; |
19.2.7 Soustraction par complément à deux
Le matériel peut réaliser a - b comme a + not(b) + 1. Les FPGA exploitent la même chaîne de retenue pour l’addition et la soustraction.
| Principe | Équation |
| difference <= a + (not b) + 1; |
Dans le code RTL, l’opérateur - reste plus clair et permet au synthétiseur de choisir l’implémentation.
19.2.8 Débordement de la soustraction signée
| Formule logique | VHDL |
| overflow_sub <= (a(a'high) xor b(b'high)) and (resultat(resultat'high) xor a(a'high)); |
19.2.9 Addition saturante non signée
| Saturation à 255 | VHDL |
| process(all) variable total : unsigned(8 downto 0); begin total := ('0' & a) + ('0' & b); if total(8) = '1' then resultat <= (others => '1'); else resultat <= total(7 downto 0); end if; end process; |
19.2.10 Addition saturante signée
| Saturation 8 bits signée | VHDL |
| process(all) variable total : signed(8 downto 0); begin total := resize(a, 9) + resize(b, 9); if total > to_signed(127, 9) then resultat <= to_signed(127, 8); elsif total < to_signed(-128, 9) then resultat <= to_signed(-128, 8); else resultat <= resize(total, 8); end if; end process; |
19.2.11 Additionneur structurel
Un additionneur peut être construit par une chaîne d’additionneurs complets. Cette description est utile pour comprendre la retenue, mais l’opérateur + est généralement préférable en RTL.
| Chaîne de retenue conceptuelle | VHDL |
| GEN_ADD : for i in 0 to N-1 generate begin U_FA : entity work.additionneur_complet(rtl) port map ( a => a(i), b => b(i), cin => retenue(i), s => somme(i), cout => retenue(i+1) ); end generate; |
19.2.12 Comparaison arithmétique
| Comparaisons explicites | VHDL |
| inferieur_u <= '1' when unsigned(a_slv) < unsigned(b_slv) else '0'; inferieur_s <= '1' when signed(a_slv) < signed(b_slv) else '0'; |
19.2.13 Coût matériel
Fonction | Ressource probable |
|---|---|
| Addition N bits | Chaîne de retenue + LUT |
| Addition/soustraction sélectionnée | Additionneur + contrôle XOR/carry |
| Saturation | Comparateurs + multiplexeur |
| Overflow | Quelques portes logiques |
| Addition pipeline | Additionneur + registres |
19.3 Multiplication
La multiplication combine les bits des opérandes pour produire un résultat dont la largeur complète est la somme des largeurs.
19.3.1 Dimensionnement non signé
Le produit d’un unsigned de NA bits par un unsigned de NB bits nécessite jusqu’à NA + NB bits.
Opérandes | Largeur complète du produit |
|---|---|
| 8 × 8 bits | 16 bits |
| 12 × 10 bits | 22 bits |
| 16 × 16 bits | 32 bits |
| 24 × 18 bits | 42 bits |
19.3.2 Multiplication combinatoire
| Produit complet | VHDL |
| signal a, b : unsigned(7 downto 0); signal produit : unsigned(15 downto 0); produit <= a * b; |
19.3.3 Multiplication signée
| Produit signé | VHDL |
| signal a, b : signed(7 downto 0); signal produit : signed(15 downto 0); produit <= a * b; |
Les deux opérandes doivent être interprétés avec le type approprié. Mélanger signed et unsigned nécessite des conversions explicites.
19.3.4 Troncature du produit
| Conservation des bits faibles | VHDL |
| resultat8 <= produit(7 downto 0); |
Cette troncature calcule le produit modulo 256 pour un résultat non signé. Pour une mesure numérique, il faut analyser l’échelle et le risque de dépassement.
19.3.5 Bits forts
| Conservation des bits de poids fort | VHDL |
| resultat8 <= produit(15 downto 8); |
Cette sélection peut être utilisée dans certains redimensionnements en virgule fixe, mais elle change l’échelle.
19.3.6 Utilisation des blocs DSP
Les FPGA possèdent souvent des blocs DSP contenant des multiplicateurs, additionneurs et registres. L’opérateur * permet au synthétiseur de les reconnaître.
Description | Implémentation possible |
|---|---|
| Petit produit constant | Décalages et additions dans les LUT |
| Produit 8 × 8 | DSP ou LUT selon la cible |
| Produit 18 × 18 | Bloc DSP typique |
| Multiply-accumulate | DSP avec addition intégrée |
| Produit pipeline | Registres internes du DSP |
19.3.7 Attribut d’outil
| Exemple conceptuel | VHDL |
| attribute use_dsp : string; attribute use_dsp of produit : signal is "yes"; |
L’attribut est spécifique à l’outil et ne remplace pas la vérification du rapport de synthèse.
19.3.8 Multiplication par constante
| Multiplication par 10 | VHDL |
| y <= shift_left(x, 3) + shift_left(x, 1); -- 8x + 2x |
Le synthétiseur peut effectuer automatiquement cette optimisation à partir de y <= x * 10.
19.3.9 Produit-addition
| Multiply-accumulate combinatoire | VHDL |
| acc_suivant <= acc + resize(a * b, acc'length); |
| MAC cadencé | VHDL |
| process(clk) begin if rising_edge(clk) then if enable = '1' then acc <= acc + resize(a * b, acc'length); end if; end if; end process; |
19.3.10 Pipelining du multiplicateur
| Produit enregistré | VHDL |
| process(clk) begin if rising_edge(clk) then produit <= a * b; end if; end process; |
Les registres peuvent être absorbés dans le bloc DSP et améliorer la fréquence maximale au prix d’une latence.
19.3.11 Multiplication séquentielle : principe
Une multiplication séquentielle réutilise un additionneur sur plusieurs cycles. Elle réduit la surface mais augmente la latence.
Cycle | Action shift-and-add |
|---|---|
| Initial | accumulateur = 0 |
| Chaque cycle | Si le bit du multiplicateur vaut 1, ajouter le multiplicande décalé |
| Puis | Décaler et avancer au bit suivant |
| Fin | Produit disponible après N cycles |
19.3.12 Registres d’un multiplicateur itératif
| Architecture conceptuelle | Architecture |
| -- Registres : -- multiplicande_decale : 2N bits -- multiplicateur : N bits -- accumulateur : 2N bits -- compteur_bits : log2(N) bits -- FSM : IDLE, CALCUL, FIN |
19.3.13 Compromis
Architecture | Surface | Latence | Débit |
|---|---|---|---|
| Combinatoire | Élevée | Chemin combinatoire | Un résultat par changement |
| DSP pipeline | Bloc dédié + registres | Quelques cycles | Souvent un par cycle |
| Séquentielle | Faible | N cycles ou plus | Un résultat par opération |
19.3.14 Analyse du rapport
- nombre de blocs DSP ;
- nombre de LUT si le produit est distribué ;
- registres de pipeline ;
- largeur réelle des opérateurs ;
- fréquence maximale et chemin critique.
19.4 Virgule fixe
La virgule fixe représente une valeur réelle par un entier mis à l’échelle. Elle est efficace sur FPGA car elle utilise des opérateurs entiers.
19.4.1 Convention QI.F
Dans ce chapitre, QI.F désigne un mot de I+F bits. Pour une valeur signée, I inclut le bit de signe et F indique le nombre de bits fractionnaires.
| Relation valeur brute / valeur réelle | Calcul |
| VALEUR_REELLE = ENTIER_STOCKE / 2^F |
19.4.2 Exemple Q4.4 signé
Q4.4 utilise 8 bits : quatre bits à gauche de la virgule, signe inclus, et quatre bits fractionnaires.
Motif | Entier signed | Valeur réelle |
|---|---|---|
| 00111000 | 56 | 3,5 |
| 00011000 | 24 | 1,5 |
| 11101000 | -24 | -1,5 |
| 01111111 | 127 | 7,9375 |
| 10000000 | -128 | -8 |
Notation — Conventions variables Certains documents comptent le bit de signe séparément. Toujours préciser la convention, la largeur totale et le nombre F de bits fractionnaires. |
19.4.3 Pas de quantification
Le plus petit incrément vaut 2^-F. Pour Q4.4, le pas vaut 1/16 = 0,0625.
F | Pas |
|---|---|
| 4 | 0,0625 |
| 8 | 0,00390625 |
| 12 | 0,000244140625 |
| 16 | 0,0000152587890625 |
19.4.4 Conversion d’une constante réelle
Pour stocker x, on calcule idéalement round(x × 2^F), puis on code cet entier.
| Exemple 3,25 en Q4.4 | Calcul |
| 3,25 * 16 = 52 52 decimal = 00110100 binary |
19.4.5 Déclaration VHDL
| Valeurs Q4.4 | VHDL |
| subtype q4_4_t is signed(7 downto 0); constant UN_ET_DEMI : q4_4_t := to_signed(24, 8); -- 24 / 16 = 1,5 |
19.4.6 Addition en virgule fixe
Deux valeurs ayant le même F peuvent être additionnées directement. Le résultat possède le même nombre de bits fractionnaires.
| Addition Q4.4 étendue | VHDL |
| signal somme_q : signed(8 downto 0); somme_q <= resize(a_q, 9) + resize(b_q, 9); |
Le bit supplémentaire protège contre un débordement immédiat. Une saturation ou réduction doit ensuite être choisie.
19.4.7 Alignement de formats différents
Avant d’additionner QI1.F1 et QI2.F2, les points binaires doivent être alignés, généralement en augmentant le nombre de bits fractionnaires du format le moins précis.
| Conversion Q4.4 vers Q4.8 | VHDL |
| a_q4_8 <= shift_left( resize(a_q4_4, a_q4_8'length), 4 ); |
19.4.8 Multiplication en virgule fixe
Le produit d’un QI1.F1 par un QI2.F2 possède F1+F2 bits fractionnaires et une largeur complète égale à la somme des largeurs.
| Q4.4 × Q4.4 | VHDL |
| signal produit_q8_8 : signed(15 downto 0); produit_q8_8 <= a_q4_4 * b_q4_4; |
19.4.9 Retour vers Q4.4
Pour retrouver quatre bits fractionnaires, le produit Q8.8 est décalé arithmétiquement de quatre positions, puis réduit.
| Troncature après multiplication | VHDL |
| signal produit_decale : signed(15 downto 0); produit_decale <= shift_right(produit_q8_8, 4); resultat_q4_4 <= resize(produit_decale, 8); |
resize seul ne réalise pas une saturation. Les bits supprimés doivent être contrôlés si le dépassement est interdit.
19.4.10 Troncature
La troncature supprime les bits fractionnaires faibles. Elle est simple mais introduit une erreur orientée vers les valeurs inférieures pour les nombres positifs.
Valeur exacte | Troncature au pas 0,25 | Erreur |
|---|---|---|
| 1,37 | 1,25 | -0,12 |
| 2,99 | 2,75 | -0,24 |
| -1,37 | Selon règle de décalage signé | À spécifier |
19.4.11 Arrondi
Un arrondi au plus proche peut être obtenu en ajoutant un demi-LSB avant la suppression des bits pour les valeurs positives. La règle pour les valeurs négatives doit être explicitement choisie.
| Arrondi symétrique simplifié | VHDL |
| constant DEMI_LSB : signed(15 downto 0) := to_signed(2**3, 16); -- F=4 process(all) variable p_arrondi : signed(15 downto 0); begin if produit_q8_8 >= 0 then p_arrondi := produit_q8_8 + DEMI_LSB; else p_arrondi := produit_q8_8 - DEMI_LSB; end if; resultat_etendu <= shift_right(p_arrondi, 4); end process; |
Cet exemple réalise un arrondi au plus proche avec les demi-valeurs éloignées de zéro. D’autres politiques existent : vers zéro, vers +∞, vers -∞ ou ties-to-even.
19.4.12 Saturation
| Fonction de saturation vers 8 bits | VHDL |
| function saturer_s8( x : signed(15 downto 0) ) return signed is begin if x > to_signed(127, 16) then return to_signed(127, 8); elsif x < to_signed(-128, 16) then return to_signed(-128, 8); else return resize(x, 8); end if; end function; |
19.4.13 Exemple de chaîne fixe
| Multiplier, arrondir, décaler, saturer | Méthode |
| produit_large <= a_q * b_q; -- Ajout de la correction d'arrondi. -- Décalage de F bits. -- Comparaison aux limites de destination. -- Saturation ou conversion finale. |
19.4.14 Choix du format
Augmenter I | Augmenter F |
|---|---|
| Plage plus grande | Précision fractionnaire meilleure |
| Moins de risque de saturation | Erreur de quantification réduite |
| Mot potentiellement plus large | Multiplicateurs et mémoires plus larges |
| Pas inchangé | Plage réduite si largeur totale fixe |
19.4.15 Bruit de quantification
La différence entre une valeur réelle et sa représentation fixe constitue l’erreur de quantification. Dans une chaîne de calcul, les erreurs peuvent s’accumuler.
19.4.16 Bit de garde
Des bits supplémentaires internes, appelés bits de garde, limitent les pertes intermédiaires. La réduction est effectuée à la sortie de la chaîne.
| Accumulateur élargi | VHDL |
| signal accumulateur : signed(23 downto 0); accumulateur <= accumulateur + resize(produit, accumulateur'length); |
19.4.17 Bibliothèques fixed_pkg
VHDL-2008 propose des paquetages de virgule fixe dans IEEE. Leur disponibilité et leur synthèse dépendent de l’outil. numeric_std avec une convention explicite reste très portable.
Méthode — Vérification Les calculs en virgule fixe doivent être comparés à un modèle de référence logiciel et testés aux valeurs minimales, maximales et proches des seuils d’arrondi. |
19.5 Unité arithmétique et logique
Une UAL regroupe plusieurs fonctions arithmétiques et logiques. Un code d’opération sélectionne le résultat et les indicateurs décrivent son état.
19.5.1 Opérations proposées
op | Fonction | Indicateur C |
|---|---|---|
| 000 | a AND b | 0 |
| 001 | a OR b | 0 |
| 010 | a XOR b | 0 |
| 011 | NOT a | 0 |
| 100 | a + b | Report |
| 101 | a - b | Emprunt : 1 si a < b |
| 110 | Décalage gauche de a | Bit a(7) éjecté |
| 111 | Décalage droit logique de a | Bit a(0) éjecté |
19.5.2 Indicateur zéro Z
| Détection de zéro | VHDL |
| z <= '1' when resultat = x"00" else '0'; |
19.5.3 Indicateur signe N
| Bit de signe | VHDL |
| n <= resultat(7); |
N indique seulement le bit de poids fort. Son interprétation comme signe suppose un résultat signé.
19.5.4 Indicateur C
Dans ce chapitre, C signifie report pour l’addition, emprunt pour la soustraction et bit éjecté pour les décalages. Sa convention doit être documentée.
19.5.5 Indicateur V
V signale un débordement en arithmétique signée pour ADD et SUB. Il vaut zéro pour les opérations logiques et les décalages de cette UAL.
19.5.6 Entité de l’UAL
| ual8.vhd — Entité | VHDL |
| library IEEE; use IEEE.STD_LOGIC_1164.ALL; use IEEE.NUMERIC_STD.ALL; entity ual8 is port ( a, b : in std_logic_vector(7 downto 0); op : in std_logic_vector(2 downto 0); resultat : out std_logic_vector(7 downto 0); z : out std_logic; c : out std_logic; n : out std_logic; v : out std_logic ); end entity ual8; |
19.5.7 Architecture : variables par défaut
| ual8.vhd — Déclarations du processus | VHDL |
| architecture rtl of ual8 is begin process(all) variable r_v : std_logic_vector(7 downto 0); variable c_v : std_logic; variable v_v : std_logic; variable ext_u : unsigned(8 downto 0); begin r_v := (others => '0'); c_v := '0'; v_v := '0'; |
19.5.8 Architecture : opérations logiques
| ual8.vhd — Opérations 000 à 011 | VHDL |
| case op is when "000" => r_v := a and b; when "001" => r_v := a or b; when "010" => r_v := a xor b; when "011" => r_v := not a; |
19.5.9 Architecture : addition
| ual8.vhd — Addition | VHDL |
| when "100" => ext_u := ('0' & unsigned(a)) + ('0' & unsigned(b)); r_v := std_logic_vector( ext_u(7 downto 0) ); c_v := ext_u(8); v_v := (not (a(7) xor b(7))) and (r_v(7) xor a(7)); |
19.5.10 Architecture : soustraction
| ual8.vhd — Soustraction | VHDL |
| when "101" => r_v := std_logic_vector( unsigned(a) - unsigned(b) ); if unsigned(a) < unsigned(b) then c_v := '1'; else c_v := '0'; end if; v_v := (a(7) xor b(7)) and (r_v(7) xor a(7)); |
19.5.11 Architecture : décalages et sorties
| ual8.vhd — Fin du case | VHDL |
| when "110" => r_v := a(6 downto 0) & '0'; c_v := a(7); when others => r_v := '0' & a(7 downto 1); c_v := a(0); end case; resultat <= r_v; c <= c_v; v <= v_v; if r_v = x"00" then z <= '1'; else z <= '0'; end if; n <= r_v(7); end process; end architecture rtl; |
19.5.12 Décalage arithmétique optionnel
| Décalage droit signé | VHDL |
| resultat <= std_logic_vector( shift_right(signed(a), 1) ); |
Le bit de signe est recopié. Cette opération peut remplacer ou compléter le décalage logique selon le cahier des charges.
19.5.13 Rotation optionnelle
| Rotation gauche | VHDL |
| resultat <= a(6 downto 0) & a(7); |
19.5.14 Priorité et combinatoire
L’architecture affecte toutes les variables par défaut et couvre tous les codes. Aucun latch ne doit être inféré.
19.5.15 UAL combinatoire ou enregistrée
UAL combinatoire | UAL enregistrée |
|---|---|
| Résultat après propagation | Résultat capturé au front |
| Latence inférieure à un cycle | Latence d’un cycle ou plus |
| Chemin critique potentiellement long | Timing mieux maîtrisé |
| Pas de bascules de sortie | Bascules supplémentaires |
19.5.16 Pipeline
Une UAL large peut enregistrer les entrées, le résultat ou des étapes internes. La latence augmente mais le débit peut atteindre une opération par cycle.
19.5.17 Ressources attendues
Fonction | Ressource |
|---|---|
| AND/OR/XOR/NOT | LUT |
| ADD/SUB | LUT + chaîne de retenue |
| Décalage fixe de 1 | Routage/LUT faible |
| Sélection op | Multiplexeurs/LUT |
| Indicateurs | Comparateurs et logique légère |
Méthode de conception arithmétique
1. Définir signed ou unsigned pour chaque donnée.
2. Calculer la plage des entrées.
3. Dimensionner chaque résultat complet.
4. Choisir le comportement en cas de dépassement.
5. Définir report, emprunt et overflow.
6. Fixer le format Q des données fractionnaires.
7. Conserver des bits de garde.
8. Choisir troncature, arrondi et saturation.
9. Simuler les valeurs limites.
10. Examiner DSP, LUT, bascules et chemin critique.
Checklist
- Les conversions signed/unsigned sont-elles explicites ?
- La largeur complète du produit est-elle conservée ?
- Le carry est-il distinct de l’overflow signé ?
- La convention de l’emprunt est-elle documentée ?
- Le nombre F de bits fractionnaires est-il connu ?
- La réduction de largeur est-elle contrôlée ?
- Les flags Z, C, N et V sont-ils vérifiés ?
- Le rapport confirme-t-il l’utilisation des DSP attendus ?
Erreurs fréquentes
Erreur | Conséquence | Correction |
|---|---|---|
| Utiliser std_logic_vector pour l’arithmétique | Opérateurs ambigus | Convertir vers signed/unsigned |
| Produit stocké sur 8 bits | Perte des bits forts | Résultat sur 16 bits |
| Confondre carry et overflow | Flags incorrects | Détecter séparément |
| Oublier l’extension de signe | Valeur négative modifiée | Utiliser resize(signed) |
| Additionner des formats Q différents | Échelles incompatibles | Aligner les points |
| Tronquer sans analyse | Biais et dépassement | Arrondir/saturer |
| Tester uniquement des valeurs positives | Erreurs signées non détectées | Cas limites négatifs |
Travaux pratiques
TP 15 — Conception d’une UAL
1. Objectifs du TP
- réaliser une UAL combinatoire 8 bits ;
- sélectionner huit opérations ;
- gérer Z, C, N et V ;
- tester automatiquement les opérations ;
- réaliser des tests exhaustifs pour ADD et SUB ;
- analyser les ressources matérielles et le chemin critique.
2. Organisation du projet
| Arborescence proposée | Arborescence |
| tp15_ual/ ├── src/ │ └── ual8.vhd ├── sim/ │ └── tb_ual8.vhd └── constraints/ └── carte.xdc_ou_qsf |
3. Codes d’opération
Code | Opération | Résultat |
|---|---|---|
| 000 | AND | a and b |
| 001 | OR | a or b |
| 010 | XOR | a xor b |
| 011 | NOT | not a |
| 100 | ADD | a + b |
| 101 | SUB | a - b |
| 110 | SHL | a << 1 |
| 111 | SHR | a >> 1 logique |
4. Source de l’UAL
Utiliser l’entité et l’architecture complètes présentées dans la section 19.5. Simuler avant la synthèse.
5. Banc de test : déclarations
| tb_ual8.vhd — Partie 1 | VHDL |
| library IEEE; use IEEE.STD_LOGIC_1164.ALL; use IEEE.NUMERIC_STD.ALL; use STD.ENV.ALL; entity tb_ual8 is end entity tb_ual8; architecture simulation of tb_ual8 is signal a, b : std_logic_vector(7 downto 0) := (others => '0'); signal op : std_logic_vector(2 downto 0) := (others => '0'); signal resultat : std_logic_vector(7 downto 0); signal z, c, n, v : std_logic; function entier_s8(x : natural) return integer is begin if x >= 128 then return integer(x) - 256; else return integer(x); end if; end function; begin DUT : entity work.ual8(rtl) port map ( a => a, b => b, op => op, resultat => resultat, z => z, c => c, n => n, v => v ); |
6. Procédure de vérification
| tb_ual8.vhd — Partie 2 | VHDL |
| stimuli : process procedure tester( constant code_op : natural; constant va : natural; constant vb : natural; constant attendu : natural; constant c_att : std_logic; constant v_att : std_logic ) is variable z_att : std_logic; variable n_att : std_logic; begin a <= std_logic_vector(to_unsigned(va, 8)); b <= std_logic_vector(to_unsigned(vb, 8)); op <= std_logic_vector(to_unsigned(code_op, 3)); wait for 1 ns; if attendu = 0 then z_att := '1'; else z_att := '0'; end if; if attendu >= 128 then n_att := '1'; else n_att := '0'; end if; |
| tb_ual8.vhd — Partie 3 | VHDL |
| assert unsigned(resultat) = to_unsigned(attendu, 8) report "Resultat incorrect, op=" & integer'image(code_op) & ", a=" & integer'image(va) & ", b=" & integer'image(vb) severity error; assert c = c_att and v = v_att and z = z_att and n = n_att report "Indicateur incorrect" severity error; end procedure; variable somme_i : integer; variable diff_i : integer; variable signe_a : integer; variable signe_b : integer; variable signe_r : integer; variable c_exp : std_logic; variable v_exp : std_logic; begin |
7. Tests dirigés
| tb_ual8.vhd — Partie 4 | VHDL |
| tester(0, 16#A5#, 16#3C#, 16#24#, '0', '0'); tester(1, 16#A5#, 16#3C#, 16#BD#, '0', '0'); tester(2, 16#A5#, 16#3C#, 16#99#, '0', '0'); tester(3, 16#A5#, 0, 16#5A#, '0', '0'); tester(6, 16#A5#, 0, 16#4A#, '1', '0'); tester(7, 16#A5#, 0, 16#52#, '1', '0'); -- Débordement signé positif. tester(4, 100, 50, 150, '0', '1'); -- -128 - 1 donne 127 avec overflow. tester(5, 128, 1, 127, '0', '1'); |
8. Test exhaustif de l’addition
| tb_ual8.vhd — Partie 5 | VHDL |
| for va in 0 to 255 loop for vb in 0 to 255 loop somme_i := va + vb; if somme_i > 255 then c_exp := '1'; else c_exp := '0'; end if; signe_a := entier_s8(va); signe_b := entier_s8(vb); signe_r := signe_a + signe_b; if signe_r > 127 or signe_r < -128 then v_exp := '1'; else v_exp := '0'; end if; tester( 4, va, vb, somme_i mod 256, c_exp, v_exp ); end loop; end loop; |
9. Test exhaustif de la soustraction
| tb_ual8.vhd — Partie 6 | VHDL |
| for va in 0 to 255 loop for vb in 0 to 255 loop diff_i := va - vb; if va < vb then c_exp := '1'; else c_exp := '0'; end if; signe_a := entier_s8(va); signe_b := entier_s8(vb); signe_r := signe_a - signe_b; if signe_r > 127 or signe_r < -128 then v_exp := '1'; else v_exp := '0'; end if; tester( 5, va, vb, diff_i mod 256, c_exp, v_exp ); end loop; end loop; |
10. Fin automatique
| tb_ual8.vhd — Partie 7 | VHDL |
| report "UAL : tests diriges et exhaustifs valides" severity note; stop; wait; end process; timeout : process begin wait for 200 us; assert false report "Timeout du TP 15" severity failure; end process; end architecture simulation; |
11. Remarque sur la durée
Les deux tests exhaustifs couvrent 131 072 couples arithmétiques. Avec une attente de 1 ns par cas, le temps simulé dépasse 131 µs. Pour un premier essai, réduire les boucles à quelques valeurs représentatives.
12. Cas limites à observer
Opération | a | b | Résultat | Flags principaux |
|---|---|---|---|---|
| ADD | 255 | 1 | 0 | C=1, Z=1 |
| ADD signé | 127 | 1 | -128 | V=1, N=1 |
| SUB | 0 | 1 | 255 | C(emprunt)=1 |
| SUB signé | -128 | 1 | 127 | V=1 |
| SHL | 10000001 | — | 00000010 | C=1 |
| SHR | 00000001 | — | 00000000 | C=1, Z=1 |
13. Commandes GHDL
| Compilation et simulation | Terminal |
| ghdl -a --std=08 src/ual8.vhd ghdl -a --std=08 sim/tb_ual8.vhd ghdl -e --std=08 tb_ual8 ghdl -r --std=08 tb_ual8 --vcd=tb_ual8.vcd gtkwave tb_ual8.vcd |
14. Synthèse et analyse des ressources
1. Définir ual8 comme entité supérieure.
2. Lancer la synthèse.
3. Ouvrir le schéma RTL.
4. Identifier l’additionneur/soustracteur et le multiplexeur d’opération.
5. Relever les LUT et chaînes de retenue.
6. Vérifier l’absence de latch et de bascule.
7. Consulter le chemin critique.
8. Comparer avec une version enregistrée de l’UAL.
15. Version enregistrée à comparer
| Registre de sortie | VHDL |
| process(clk) begin if rising_edge(clk) then if reset = '1' then resultat_reg <= (others => '0'); flags_reg <= (others => '0'); elsif enable = '1' then resultat_reg <= resultat_comb; flags_reg <= z_comb & c_comb & n_comb & v_comb; end if; end if; end process; |
16. Tableau d’analyse
Indicateur | UAL combinatoire | UAL enregistrée |
|---|---|---|
| LUT | ||
| Bascules | 0 attendu | |
| Chaîne de retenue | ||
| Chemin critique | ||
| Fréquence maximale | Dépend du chemin combinatoire | |
| Latence | Combinatoire | 1 cycle |
17. Questions d’analyse
1. Pourquoi le produit de deux nombres 8 bits nécessite-t-il 16 bits ?
2. Quelle différence existe entre carry et overflow signé ?
3. Pourquoi -128 possède-t-il un cas particulier sur 8 bits ?
4. Quelle extension faut-il appliquer à un signed négatif ?
5. Que représente F dans le format QI.F ?
6. Pourquoi un produit Q4.4 devient-il Q8.8 ?
7. Quelle différence existe entre troncature et arrondi ?
8. Pourquoi la saturation ajoute-t-elle des comparateurs ?
9. Que signifie C lors d’une soustraction dans cette UAL ?
10. Quel matériel principal est attendu pour ADD et SUB ?
18. Extensions proposées
- Ajouter une comparaison signed et unsigned.
- Ajouter un décalage arithmétique.
- Ajouter des rotations.
- Créer une UAL 16 bits générique.
- Ajouter une multiplication 8 × 8 avec résultat 16 bits.
- Créer une addition saturante.
- Ajouter un mode Q4.4.
- Pipeline l’UAL en deux étages.
- Comparer une multiplication LUT et DSP.
19. Barème indicatif
Critère | Points |
|---|---|
| UAL 8 bits et sélection | 4 |
| Addition, soustraction et décalages | 3 |
| Indicateurs Z, C, N et V | 4 |
| Testbench automatique | 3 |
| Tests exhaustifs ADD/SUB | 2 |
| Analyse des ressources | 2 |
| Comparaison combinatoire/enregistrée | 1 |
| Qualité du compte rendu | 1 |
| Total | 20 |
Synthèse du chapitre
Notion | Résumé |
|---|---|
| unsigned | Entier de 0 à 2^N-1. |
| signed | Entier en complément à deux. |
| Extension de signe | Recopie du bit de signe vers les bits ajoutés. |
| Carry | Report non signé de l’addition. |
| Borrow | Emprunt de la soustraction. |
| Overflow | Résultat signé hors plage. |
| Produit complet | Largeur égale à la somme des largeurs. |
| DSP | Bloc spécialisé pour multiplication et accumulation. |
| QI.F | Format fixe avec F bits fractionnaires. |
| Arrondi | Réduction de l’erreur lors du changement d’échelle. |
| Saturation | Limitation aux bornes représentables. |
| UAL | Bloc sélectionnant plusieurs opérations et indicateurs. |
Autoévaluation
1. Quelle plage possède unsigned(7 downto 0) ?
Réponse : 0 à 255.
2. Quelle plage possède signed(7 downto 0) ?
Réponse : -128 à 127.
3. Comment étendre un nombre signé ?
Réponse : En recopiant son bit de signe.
4. Carry et overflow sont-ils identiques ?
Réponse : Non.
5. Quelle largeur pour un produit 8 × 8 ?
Réponse : 16 bits.
6. Que vaut le pas d’un Q4.4 ?
Réponse : 1/16 = 0,0625.
7. Que devient F lors d’une multiplication ?
Réponse : Les nombres de bits fractionnaires s’additionnent.
8. Pourquoi arrondir ?
Réponse : Pour réduire l’erreur de quantification.
9. Que fait la saturation ?
Réponse : Elle bloque la valeur aux limites.
10. Que signifie Z dans l’UAL ?
Réponse : Le résultat est nul.
Exercice de consolidation
Calculer la largeur et le format du produit de deux nombres Q3.5 signés sur 8 bits. Proposer le redimensionnement vers Q3.5.
| Correction proposée | Analyse |
| -- Q3.5 × Q3.5 : -- largeur complète : 8 + 8 = 16 bits -- bits fractionnaires : 5 + 5 = 10 -- format intermédiaire : Q6.10 produit_q6_10 <= a_q3_5 * b_q3_5; -- Retour à 5 bits fractionnaires : produit_decale <= shift_right(produit_q6_10, 5); -- Puis arrondi éventuel, contrôle des limites -- et saturation vers 8 bits Q3.5. |
Suite du cours — Transition vers la suite Après l’arithmétique numérique, le cours peut aborder les interfaces et périphériques courants : UART, SPI, I²C et génération PWM. |