Wikilivres
frwikibooks
https://fr.wikibooks.org/wiki/Accueil
MediaWiki 1.47.0-wmf.8
first-letter
Média
Spécial
Discussion
Utilisateur
Discussion utilisateur
Wikilivres
Discussion Wikilivres
Fichier
Discussion fichier
MediaWiki
Discussion MediaWiki
Modèle
Discussion modèle
Aide
Discussion aide
Catégorie
Discussion catégorie
Transwiki
Discussion Transwiki
Wikijunior
Discussion Wikijunior
TimedText
TimedText talk
Module
Discussion module
Event
Event talk
Dictionnaire de philosophie/M
0
30586
768827
767697
2026-06-28T05:05:02Z
PandaMystique
119061
768827
wikitext
text/x-wiki
{{DicoPhilo|M}}
<div style="display: grid; grid-template-columns: repeat(auto-fit, minmax(280px, 1fr)); gap: 1.5em; width: 100%;">
<div style="padding: 1.5em; background: linear-gradient(135deg, #ffffff40 0%, #e0e4e840 100%); border-radius: 10px; box-shadow: 0 3px 10px rgba(0,0,0,0.1);">
:Machine
:Magnanimité
:Maïeutique
:Maïmonide
:Maine de Biran
:Maîtrise
:Mal
:Nicolas Malebranche
:Manichéisme
:Marc Aurèle
:Gabriel Marcel
:Marché
:Herbert Marcuse
:Jacques Maritain
:[[Dictionnaire de philosophie/Karl Marx|Karl Marx]]
:Marxisme
:Masse
:Matérialisme
:Mathématiques
:Matière
:Mauvaise foi
:Maxime
:George Herbert Mead
:Mécanisme
:Médiation
</div>
<div style="padding: 1.5em; background: linear-gradient(135deg, #ffffff40 0%, #e0e4e840 100%); border-radius: 10px; box-shadow: 0 3px 10px rgba(0,0,0,0.1);">
:[[Philosophie/Méditations Métaphysiques|''Méditations métaphysiques'']]
:Alexius Meinong
:Mélancolie
:Mémoire
:Mencius
:Mensonge
:Mérite
:Maurice Merleau-Ponty
:Mesure
:Métaphore
:[[Dictionnaire de philosophie/Métaphysique|Métaphysique]]
:Méthode
:Mètis
:Milieu
:John Stuart Mill
:Mimésis
:Miracle
:Modalité
:Mode
:Modèle
:Modernité
:Moi
:Monade
</div>
<div style="padding: 1.5em; background: linear-gradient(135deg, #ffffff40 0%, #e0e4e840 100%); border-radius: 10px; box-shadow: 0 3px 10px rgba(0,0,0,0.1);">
:Monde
:Monisme
:[[Dictionnaire de philosophie/Monothéisme|Monothéisme]]
:Michel de Montaigne
:Montesquieu
:George Edward Moore
:[[Dictionnaire de philosophie/Morale|Morale]]
:Moralité
:Thomas More
:[[Dictionnaire de philosophie/Mort|Mort]]
:Mot
:Motivation
:Emmanuel Mounier
:Mouvement
:Mozi
:Multiculturalisme
:Multitude
:Mythe
</div></div>
{{PhiloRecherche}}
{{autocat}}
tmcoqo2dzkhzyyesx306gtscxadlcwx
768832
768827
2026-06-28T07:22:57Z
PandaMystique
119061
768832
wikitext
text/x-wiki
{{DicoPhilo|M}}
<div style="display: grid; grid-template-columns: repeat(auto-fit, minmax(280px, 1fr)); gap: 1.5em; width: 100%;">
<div style="padding: 1.5em; background: linear-gradient(135deg, #ffffff40 0%, #e0e4e840 100%); border-radius: 10px; box-shadow: 0 3px 10px rgba(0,0,0,0.1);">
:Machine
:Magnanimité
:Maïeutique
:Maïmonide
:Maine de Biran
:Maîtrise
:Mal
:Nicolas Malebranche
:Manichéisme
:Marc Aurèle
:Gabriel Marcel
:Marché
:Herbert Marcuse
:Jacques Maritain
:[[Dictionnaire de philosophie/Karl Marx|Karl Marx]]
:Marxisme
:Masse
:Matérialisme
:Mathématiques
:Matière
:Mauvaise foi
:Maxime
:George Herbert Mead
:Mécanisme
:Médiation
</div>
<div style="padding: 1.5em; background: linear-gradient(135deg, #ffffff40 0%, #e0e4e840 100%); border-radius: 10px; box-shadow: 0 3px 10px rgba(0,0,0,0.1);">
:[[Philosophie/Méditations Métaphysiques|''Méditations métaphysiques'']]
:Alexius Meinong
:Mélancolie
:Mémoire
:Mencius
:Mensonge
:Mérite
:[[Dictionnaire de philosophie/Maurice Merleau-Ponty|Merleau-Ponty, Maurice]]
:Mesure
:Métaphore
:[[Dictionnaire de philosophie/Métaphysique|Métaphysique]]
:Méthode
:Mètis
:Milieu
:John Stuart Mill
:Mimésis
:Miracle
:Modalité
:Mode
:Modèle
:Modernité
:Moi
:Monade
</div>
<div style="padding: 1.5em; background: linear-gradient(135deg, #ffffff40 0%, #e0e4e840 100%); border-radius: 10px; box-shadow: 0 3px 10px rgba(0,0,0,0.1);">
:Monde
:Monisme
:[[Dictionnaire de philosophie/Monothéisme|Monothéisme]]
:Michel de Montaigne
:Montesquieu
:George Edward Moore
:[[Dictionnaire de philosophie/Morale|Morale]]
:Moralité
:Thomas More
:[[Dictionnaire de philosophie/Mort|Mort]]
:Mot
:Motivation
:Emmanuel Mounier
:Mouvement
:Mozi
:Multiculturalisme
:Multitude
:Mythe
</div></div>
{{PhiloRecherche}}
{{autocat}}
9j5po8toeygrfm6h1u0wd4m7uav2nm9
Dictionnaire de philosophie/S
0
30594
768824
767520
2026-06-28T04:45:06Z
PandaMystique
119061
768824
wikitext
text/x-wiki
{{DicoPhilo|S}}
<div style="display: grid; grid-template-columns: repeat(auto-fit, minmax(280px, 1fr)); gap: 1.5em; width: 100%;">
<div style="padding: 1.5em; background: linear-gradient(135deg, #ffffff40 0%, #e0e4e840 100%); border-radius: 10px; box-shadow: 0 3px 10px rgba(0,0,0,0.1);">
:Sacré
:Sagesse
:Sanction
:Savoir
:{{Page|Scepticisme}}
:Schématisme
:{{Page|Moritz Schlick|Schlick, Moritz}}
:Science
:Sémantique
:Sémiotique
:Sens
:Sensation
:Sensibilité
:Sentiment
:Signe
:Signification
:Silence
:Singularité
</div>
<div style="padding: 1.5em; background: linear-gradient(135deg, #ffffff40 0%, #e0e4e840 100%); border-radius: 10px; box-shadow: 0 3px 10px rgba(0,0,0,0.1);">
:Socialisme
:Société
:Solidarité
:Solipsisme
:Sophisme
:Sophistique
:Souci
:Souffrance
:Souveraineté
:Spectacle
:Spéculation
:Spontanéité
:{{Page|Max Stirner|Stirner, Max}}
:Stoïcisme
:Structuralisme
:Structure
:Style
</div>
<div style="padding: 1.5em; background: linear-gradient(135deg, #ffffff40 0%, #e0e4e840 100%); border-radius: 10px; box-shadow: 0 3px 10px rgba(0,0,0,0.1);">
:Subjectivité
:Sublimation
:Sublime
:Substance
:''Suède''
::{{Page|Philosophie suédoise contemporaine|Philosophie contemporaine}}
:Suicide
:[[Dictionnaire de philosophie/Sujet|Sujet]]
:Superstition
:Surhomme
:Syllogisme
:Symbole
:Sympathie
:Synthèse
:Système
</div></div>
{{PhiloRecherche}}
{{autocat}}
488ts1p9rhxayt9ursyx0x8dpcwywpl
768826
768824
2026-06-28T04:51:22Z
PandaMystique
119061
768826
wikitext
text/x-wiki
{{DicoPhilo|S}}
<div style="display: grid; grid-template-columns: repeat(auto-fit, minmax(280px, 1fr)); gap: 1.5em; width: 100%;">
<div style="padding: 1.5em; background: linear-gradient(135deg, #ffffff40 0%, #e0e4e840 100%); border-radius: 10px; box-shadow: 0 3px 10px rgba(0,0,0,0.1);">
:Sacré
:Sagesse
:Sanction
:Sartre, Jean-Paul
:Saussure, Ferdinand de
:Savoir
:{{Page|Scepticisme}}
:Scheler, Max
:Schelling, Friedrich
:Schématisme
:Schleiermacher, Friedrich
:{{Page|Moritz Schlick|Schlick, Moritz}}
:Schopenhauer, Arthur
:Science
:Searle, John
:Sellars, Wilfrid
:Sémantique
:Sémiotique
:Sénèque
:Sens
:Sensation
:Sensibilité
:Sentiment
:Serres, Michel
:Sextus Empiricus
:Shaftesbury
</div>
<div style="padding: 1.5em; background: linear-gradient(135deg, #ffffff40 0%, #e0e4e840 100%); border-radius: 10px; box-shadow: 0 3px 10px rgba(0,0,0,0.1);">
:Sidgwick, Henry
:Signe
:Signification
:Silence
:Simmel, Georg
:Simondon, Gilbert
:Singer, Peter
:Singularité
:Sloterdijk, Peter
:Smith, Adam
:Socialisme
:Société
:Socrate
:Solidarité
:Solipsisme
:Sophisme
:Sophistique
:Souci
:Souffrance
:Souveraineté
:Spectacle
:Spéculation
:Spencer, Herbert
:Spinoza, Baruch
:Spontanéité
</div>
<div style="padding: 1.5em; background: linear-gradient(135deg, #ffffff40 0%, #e0e4e840 100%); border-radius: 10px; box-shadow: 0 3px 10px rgba(0,0,0,0.1);">
:{{Page|Max Stirner|Stirner, Max}}
:Stoïcisme
:Strauss, Leo
:Strawson, Peter
:Structuralisme
:Structure
:Style
:Suárez, Francisco
:Subjectivité
:Sublimation
:Sublime
:Substance
:''Suède''
::{{Page|Philosophie suédoise contemporaine|Philosophie contemporaine}}
:Suicide
:[[Dictionnaire de philosophie/Sujet|Sujet]]
:Superstition
:Surhomme
:Syllogisme
:Symbole
:Sympathie
:Synthèse
:Système
</div></div>
{{PhiloRecherche}}
{{autocat}}
790xdbxm97w6umwnoqgcxr4v2zimu7d
Dictionnaire de philosophie/P
0
32933
768823
767502
2026-06-28T04:38:26Z
PandaMystique
119061
768823
wikitext
text/x-wiki
{{DicoPhilo|P}}
<div style="display: grid; grid-template-columns: repeat(auto-fit, minmax(280px, 1fr)); gap: 1.5em; width: 100%;">
<div style="padding: 1.5em; background: linear-gradient(135deg, #ffffff40 0%, #e0e4e840 100%); border-radius: 10px; box-shadow: 0 3px 10px rgba(0,0,0,0.1);">
:Paix
:[[Dictionnaire de philosophie/Panthéisme|Panthéisme]]
:Paradigme
:Paradoxe
:Pari
:Parole
:Particulier
:Passion
:Pensée
:[[Philosophie/Perception|Perception]]
:Perfection
:Performatif
:Personne
::[[Dictionnaire de philosophie/Personne non-humaine|Personne non-humaine]]
:Pessimisme
:Phénomène
:Phénoménologie
</div>
<div style="padding: 1.5em; background: linear-gradient(135deg, #ffffff40 0%, #e0e4e840 100%); border-radius: 10px; box-shadow: 0 3px 10px rgba(0,0,0,0.1);">
:''Philosophie''
::[[Philosophie/Une brève introduction|Introduction]]
::[[Dictionnaire de philosophie/Philosophie africaine|Philosophie africaine]]
::[[Philosophie/Philosophie analytique|Philosophie analytique]]
::[[Philosophie/Philosophie de l'esprit|Philosophie de l'esprit]]
::[[Dictionnaire de philosophie/Philosophie suédoise contemporaine|Philosophie suédoise contemporaine]]
:[[Philosophie de l'esprit/Physicalisme|Physicalisme]]
:Pitié
:Plaisir
:Pluralisme
:Pneuma
:Poétique
:Politique
:[[Dictionnaire de philosophie/Polythéisme|Polythéisme]]
:Positivisme
:Possible
:Postmodernité
:Pouvoir
</div>
<div style="padding: 1.5em; background: linear-gradient(135deg, #ffffff40 0%, #e0e4e840 100%); border-radius: 10px; box-shadow: 0 3px 10px rgba(0,0,0,0.1);">
:Pragmatisme
:Praxis
:Préjugé
:Présence
:[[Philosophie/Présocratiques|Présocratiques]]
:Probabilité
:Problème
:Progrès
:Projet
:Proposition
:Propriété
:Providence
:Prudence
:Puissance
:Pulsion
:{{page|Pyrrhon d'Élis}}
:Pyrrhonisme
:[[Philosophie/Pythagore|Pythagore]]
</div></div>
{{PhiloRecherche}}
{{autocat}}
kjy92ba32ju9gjjtpck3x27eqst8bii
Dictionnaire de philosophie/V
0
34064
768815
753982
2026-06-28T04:02:25Z
PandaMystique
119061
768815
wikitext
text/x-wiki
{{DicoPhilo|V}}
<div style="display:grid; grid-template-columns:repeat(auto-fit, minmax(280px, 1fr)); gap:2em; width:100%; margin-bottom:2em;">
<div style="padding:1.5em; background:linear-gradient(135deg, #ffffff40 0%, #e0e4e840 100%); border-radius:10px; box-shadow:0 3px 10px rgba(0,0,0,0.1);">
:[[Dictionnaire de philosophie/Vérité|Vérité]]
</div>
<div style="padding:1.5em; background:linear-gradient(135deg, #ffffff40 0%, #e0e4e840 100%); border-radius:10px; box-shadow:0 3px 10px rgba(0,0,0,0.1);">
</div>
<div style="padding:1.5em; background:linear-gradient(135deg, #ffffff40 0%, #e0e4e840 100%); border-radius:10px; box-shadow:0 3px 10px rgba(0,0,0,0.1);">
</div>
</div>
{{PhiloRecherche}}
{{autocat}}
bvcnfc9ll6vv8djobf3zws9nb0tofs4
768818
768815
2026-06-28T04:12:18Z
PandaMystique
119061
768818
wikitext
text/x-wiki
{{DicoPhilo|V}}
<div style="display:grid; grid-template-columns:repeat(auto-fit, minmax(280px, 1fr)); gap:2em; width:100%; margin-bottom:2em;">
<div style="padding:1.5em; background:linear-gradient(135deg, #ffffff40 0%, #e0e4e840 100%); border-radius:10px; box-shadow:0 3px 10px rgba(0,0,0,0.1);">
:Vacuité
:Valeur
:Validité
:Vanité
:Vécu
:Vengeance
:Vérification
:[[Dictionnaire de philosophie/Vérité|Vérité]]
</div>
<div style="padding:1.5em; background:linear-gradient(135deg, #ffffff40 0%, #e0e4e840 100%); border-radius:10px; box-shadow:0 3px 10px rgba(0,0,0,0.1);">
:Vertu
:Vice
:Vide
:Vie
:Vieillesse
:Violence
:Virtuel
:Visage
</div>
<div style="padding:1.5em; background:linear-gradient(135deg, #ffffff40 0%, #e0e4e840 100%); border-radius:10px; box-shadow:0 3px 10px rgba(0,0,0,0.1);">
:Vision
:Vitalisme
:Vivant
:Voile d'ignorance
:Volonté
:Volupté
:Vouloir-vivre
:Vraisemblance
</div>
</div>
{{PhiloRecherche}}
{{autocat}}
fhidyz9wntt4u1fi9ehfjmi3k5e0tct
Dictionnaire de philosophie/T
0
37436
768816
753980
2026-06-28T04:03:07Z
PandaMystique
119061
768816
wikitext
text/x-wiki
{{DicoPhilo|T}}
<div style="display:grid; grid-template-columns:repeat(auto-fit, minmax(280px, 1fr)); gap:2em; width:100%; margin-bottom:2em;">
<div style="padding:1.5em; background:linear-gradient(135deg, #ffffff40 0%, #e0e4e840 100%); border-radius:10px; box-shadow:0 3px 10px rgba(0,0,0,0.1);">
:[[Dictionnaire de philosophie/Théisme|Théisme]]
</div>
<div style="padding:1.5em; background:linear-gradient(135deg, #ffffff40 0%, #e0e4e840 100%); border-radius:10px; box-shadow:0 3px 10px rgba(0,0,0,0.1);">
</div>
<div style="padding:1.5em; background:linear-gradient(135deg, #ffffff40 0%, #e0e4e840 100%); border-radius:10px; box-shadow:0 3px 10px rgba(0,0,0,0.1);">
</div>
</div>
{{PhiloRecherche}}
{{autocat}}
lh50dqo3ywz1adxe4ebbfy9mesvcfw4
768817
768816
2026-06-28T04:08:41Z
PandaMystique
119061
768817
wikitext
text/x-wiki
{{DicoPhilo|T}}
<div style="display:grid; grid-template-columns:repeat(auto-fit, minmax(280px, 1fr)); gap:2em; width:100%; margin-bottom:2em;">
<div style="padding:1.5em; background:linear-gradient(135deg, #ffffff40 0%, #e0e4e840 100%); border-radius:10px; box-shadow:0 3px 10px rgba(0,0,0,0.1);">
:Tabou
:Tautologie
:Technique
:Téléologie
:Téléonomie
:Témoignage
:Tempérance
:Temporalité
:Temps
:[[Dictionnaire de philosophie/Théisme|Théisme]]
</div>
<div style="padding:1.5em; background:linear-gradient(135deg, #ffffff40 0%, #e0e4e840 100%); border-radius:10px; box-shadow:0 3px 10px rgba(0,0,0,0.1);">
:Théodicée
:Théologie
:Théorie
:Thèse
:Thymos
:Tolérance
:Topique
:Totalitarisme
:Totalité
</div>
<div style="padding:1.5em; background:linear-gradient(135deg, #ffffff40 0%, #e0e4e840 100%); border-radius:10px; box-shadow:0 3px 10px rgba(0,0,0,0.1);">
:Tradition
:Tragique
:Transcendance
:Transcendantal
:Travail
:Trivium
:Trope
:Type
:Tyrannie
</div>
</div>
{{PhiloRecherche}}
{{autocat}}
ib8nomslj1ogooqq6hyaaao0jhpmv5c
Ubuntu
0
45210
768829
680359
2026-06-28T05:37:37Z
Matthius
11787
/* Sommaire */
768829
wikitext
text/x-wiki
{{Page de garde|image=Ubuntu-logo-2022.svg|imagedesc=Logo d'Ubuntu|description=
Ubuntu est un système d'exploitation, distribution de Linux. Ce livre est un guide d'utilisateur et se veut donc accessible à tous. Pour une utilisation plus technique destinée aux administrateurs, voir ''[[Le système d'exploitation GNU-Linux]]''.
|avancement=Terminé
|cdu=
* {{CDU item|6/68/681|681.3/681.3.0}}
|versions=
{{version imprimable}}
}}
== Sommaire ==
{{/Sommaire}}
Les chapitres 1 à 9 sans mailman proviennent du livrel libre [https://archive.org/details/Astucieux-Linux L'Astucieux GNU Linux].
== Voir aussi ==
{{Autres projets
|w=Ubuntu (système d'exploitation)
|wikt=Ubuntu
|commons=Ubuntu}}
* [[Firefox]]
* [[LibreOffice et OpenOffice.org]]
[[Catégorie:Ubuntu (livre)|*]]
gnugupz3xiu3aoxmtjykkpn39h7qkwf
768830
768829
2026-06-28T05:37:45Z
Matthius
11787
/* Sommaire */
768830
wikitext
text/x-wiki
{{Page de garde|image=Ubuntu-logo-2022.svg|imagedesc=Logo d'Ubuntu|description=
Ubuntu est un système d'exploitation, distribution de Linux. Ce livre est un guide d'utilisateur et se veut donc accessible à tous. Pour une utilisation plus technique destinée aux administrateurs, voir ''[[Le système d'exploitation GNU-Linux]]''.
|avancement=Terminé
|cdu=
* {{CDU item|6/68/681|681.3/681.3.0}}
|versions=
{{version imprimable}}
}}
== Sommaire ==
{{/Sommaire}}
Les chapitres 2 à 9 sans mailman proviennent du livrel libre [https://archive.org/details/Astucieux-Linux L'Astucieux GNU Linux].
== Voir aussi ==
{{Autres projets
|w=Ubuntu (système d'exploitation)
|wikt=Ubuntu
|commons=Ubuntu}}
* [[Firefox]]
* [[LibreOffice et OpenOffice.org]]
[[Catégorie:Ubuntu (livre)|*]]
hdsfmcww7j6cad3jwd7pf2nfn8yjtus
768831
768830
2026-06-28T06:18:48Z
Matthius
11787
/* Sommaire */
768831
wikitext
text/x-wiki
{{Page de garde|image=Ubuntu-logo-2022.svg|imagedesc=Logo d'Ubuntu|description=
Ubuntu est un système d'exploitation, distribution de Linux. Ce livre est un guide d'utilisateur et se veut donc accessible à tous. Pour une utilisation plus technique destinée aux administrateurs, voir ''[[Le système d'exploitation GNU-Linux]]''.
|avancement=Terminé
|cdu=
* {{CDU item|6/68/681|681.3/681.3.0}}
|versions=
{{version imprimable}}
}}
== Sommaire ==
{{/Sommaire}}
Les chapitres 2 à 9 sans Mailman proviennent du livrel libre [https://archive.org/details/Astucieux-Linux L'Astucieux GNU Linux].
== Voir aussi ==
{{Autres projets
|w=Ubuntu (système d'exploitation)
|wikt=Ubuntu
|commons=Ubuntu}}
* [[Firefox]]
* [[LibreOffice et OpenOffice.org]]
[[Catégorie:Ubuntu (livre)|*]]
03tr77ax4cluocvudmuwo9b61cb95t3
Fonctionnement d'un ordinateur/Les circuits pour l'addition et la soustraction
0
65787
768768
768747
2026-06-27T12:39:52Z
Mewtow
31375
/* Le soustracteur à propagation de retenue */
768768
wikitext
text/x-wiki
Dans ce chapitre, nous allons voir les circuits capables de faire une addition ou une soustraction, ainsi que quelques circuits spécialisés. Précisons cependant que les fabricants de processeurs travaillent d'arrache-pied pour trouver des moyens de rendre ces circuits de calcul plus rapides et plus économes en énergie. Autant vous dire que les circuits que vous allez voir sont vraiment des circuits qui font pâle figure comparé à ce que l'on peut trouver dans un vrai processeur commercial !
==Les circuits pour additionner 2 ou 3 bits==
L'addition se fait en binaire de la même manière qu'en décimal. On additionne les chiffres/bits colonne par colonne, une éventuelle retenue est propagée à la colonne d'à côté. La soustraction fonctionne sur le même principe, sur le même modèle qu'en décimal.
[[File:Binary Addition Demonstration.svg|centre|vignette|Exemple d'addition en binaire.]]
En clair, additionner deux nombres demande d'additionner 2 bits et une retenue sur chaque colonne, et de propager les retenues d'une colonne à l'autre. La propagation des retenues est quelque chose de simple en apparence, mais qui est sujet à des optimisations extraordinairement nombreuses. Aussi, pour simplifier l'exposition, nous allons voir comment gérer une colonne avant de voir comment sont propagées les retenues.
[[File:Full Adder Block.svg|vignette|upright=1|class=transparent|Additionneur complet.]]
Si on effectue une addition en colonne, on doit additionner les deux bits sur la colonne, mais aussi additionner une éventuelle retenue. Il faut donc créer un circuit qui additionne trois bits : deux bits de données, plus une retenue. Ce circuit qui additionne trois bits est appelé un '''additionneur complet'''. Il fournit en sortie deux bits : un bit de somme et une retenue sortante. Voici sa table de vérité :
{|class="wikitable"
|-
! Retenue entrante !! Opérande 1 !! Opérande 2 !! !! Retenue sortante !! Bit de somme
|-
||0||0||0|| ||0||0
|-
||0||0||1|| ||0||1
|-
||0||1||0|| ||0||1
|-
||0||1||1|| ||1||0
|-
||1||0||0|| ||0||1
|-
||1||0||1|| ||1||0
|-
||1||1||0|| ||1||0
|-
||1||1||1|| ||1||1
|}
Il est possible d'utiliser un tableau de Karnaugh pour traduire la table de vérité, mais elle donne un résultat assez compliqué. La retenue et le bit de somme sont calculés à part. La retenue est calculée avec trois portes ET et une porte OU. Le bit de somme est calculé lui avec 4 portes ET, une porte OU, et trois portes NON.
[[File:Full Adler with 3 entries.png|centre|vignette|upright=2|Additionneur complet, fabriqué à partir de la table de vérité.]]
D'autres méthodes donnent des résultats plus compréhensibles. Nous allons les voir dans la suite de cette section.
===L'additionneur complet basé sur des demi-additionneurs===
[[File:1-bit half-adder.svg|class=transparent|vignette|Demi-addtionneur.]]
Nous avons déjà vu comment additionner deux bits dans le chapitre sur les incrémenteurs, mais quelques rappels ne seront pas de trop. Le '''demi-additionneur''' est un circuit qui additionne deux bits. Il implémente la table d'addition, qui est très simple en binaire. Jugez plutôt :
* 0 + 0 = 0, retenue = 0 ;
* 0 + 1 = 1, retenue = 0 ;
* 1 + 0 = 1, retenue = 0 ;
* 1 + 1 = 0, retenue = 1.
Mais un demi-additionneur n'additionne que deux bits, il manque de quoi additionner la retenue. Une solution pour cela est d'enchaîner deux demi-additionneurs : un qui additionne les deux bits de données, et un second qui additionne la retenue au résultat du précédent. La retenue finale se calcule en combinant les sorties de retenue des deux demi-additionneurs, avec une porte OU. Le résultat est un additionneur complet fabriqué avec deux demi-additionneurs. Pour vous en convaincre, établissez la table de vérité de ce circuit, vous verrez que ça marche.
[[File:Full Adder Blocks.svg|centre|vignette|upright=2|Additionneur complet fabriqué avec deux demi-additionneurs.]]
[[File:Half-adder.svg|vignette|upright=1|Circuit d'un demi-additionneur naïf.]]
L'implémentation naïve d'un demi-additionneur utilise une porte XOR et une porte ET, comme illustré ci-contre. Le circuit s'obtient en faisant uen traduction littérale de la table de vérité du circuit. Si je dis qu'elle est naïve, c'est car il est possible de l'optimiser, de manière à éliminer des redondances cachées. Mais nous avions déjà vu cela dans le chapitre sur les incrémenteurs, aussi laissons cela pour plus tard.
En combinant deux demi-additionneurs de ce type, on obtient l'additionneur complet suivant. Les deux sorties de retenue des demi-additionneurs sont combinées avec une porte OU, pour calculer la retenue finale.
{| class="flexible"
|[[File:Full Adder Modules.svg|class=transparent|Composition d'un additionneur complet. On voit bien que celui-ci est composé de deux demi-additionneurs, en rouge et en bleu, auxquels on a ajouté une porte OU pour calculer la retenue finale.]]
|[[File:Full-adder.svg|300px|class=transparent|Circuit d'un additionneur complet.]]
|}
Le fait de combiner les deux retenues avec une porte OU n'est pas ce qu'il y a de plus intuitif. La table de vérité nous dit que ça fonctionne, mais on comprend mal pourquoi. Mais tout devient plus clair quand on sait que les deux retenues ne peuvent pas être à 1 en même temps. Même dans l'addition 1 + 1 + 1, seule une retenue est à 1. Soit l'addition des deux bits d'opérande donne naissance à une retenue, soit c'est l'addition ''résultat + retenue entrante''. La retenue sortant est donc à 1 quand une des deux addition donne une retenue. D'où l'usage d'une porte OU. Remarquez qu'une porte XOR donne le même résultat, vu que le cas où les deux retenues sont à 1 n'est jamais rencontré.
===La propagation et la génération des retenues===
L'additionneur complet que nous allons voir dans cette section sert à introduire les concepts de propagation, génération et absorption de retenue. Il s'agit de concepts très importants quand on étudie les additionneurs. l'idée part d'un principe très simple : la retenue sortante dépend de la retenue d'entrée. La relation entre les deux se résume à trois cas, qui dépendent de la valeur des deux bits additionnés, nommés A et B.
* Dans le premier cas, la retenue entrante est égale à la retenue sortante. On dit que la retenue entrante est propagée par l'additionneur.
* Dans le second cas, la retenue est forcée à 0 : la retenue sortante vaut 0, peu importe la valeur de la retenue entrante.
* Dans le troisième cas, la retenue est forcée à 1 : la retenue entrante vaut 1, peu importe la valeur de la retenue entrante.
[[File:Signaux P et G fournis par un demi-additionneur.png|vignette|Signaux P et G fournis par un demi-additionneur]]
Maintenant, créons un circuit qui nous dise si une retenue est propagée ou générée. Il a deux bits de sortie, nommés P et G : P pour ''Propagate'', G pour ''Generate''. Le bit P est à 1 si la retenue entrante est propagée, il est à 0 sinon. Le bit G est à 1 si une retenue est générée, à 0 sinon. Une retenue est considérée comme absorbée si elle n'est pas ni propagée ni générée, pas besoin d'un troisième bit pour gérer ce cas.
Il se trouve que ce circuit n'est autre qu'un demi-additionneur ! Pour vous en rendre compte, regardez la table de vérité d'un additionneur complet, illustrée ci-dessous. Lorsque les deux bits d'opérande sont à 0, la retenue sortante vaut toujours 0. Si ils sont tous deux à 1, alors la retenue sortante vaut 1. S'ils sont différents, alors retenues sortante et entrante sont égales. Le bit P est donc généré par une simple porte XOR. Quant au bit G, il est à 1 si les deux bits d'opérandes sont à 1, ce qui correspond à une porte ET. Il se trouve que ces deux portes forment un demi-additionneur !
{|class="wikitable"
|-
! Opérande 1 !! Opérande 2 !! Retenue entrante !! !! Retenue sortante
|- class="f_rouge"
||0||0||0|| ||0
|- class="f_rouge"
||0||0||1|| ||0
|- class="f_bleu"
||0||1||0|| ||0
|- class="f_bleu"
||0||1||1|| ||1
|- class="f_bleu"
||1||0||0|| ||0
|- class="f_bleu"
||1||0||1|| ||1
|- class="f_vert"
||1||1||0|| ||1
|- class="f_vert"
||1||1||1|| ||1
|}
Ensuite, créons un circuit qui prend ces deux sorties P et G, et calcule la retenue sortante en fonction. Le circuit en question a trois entrées : la retenue entrante, les deux bits P et G. La retenue sortante vaut 1 soit si une retenue est générée, soit si la retenue entrante est propagée et qu'elle vaut 1. En notant <math>{C_{in}}</math> et <math>{C_{out}}</math> les retenues entrantes et sortante, on a :
: <math>{C_{out}} = G + \left( P . C_{in} \right)</math>
Le circuit est donc composé d'une porte OU et d'une porte ET. En combinant un demi-additionneur avec le circuit de calcul de retenue sortante vu plus haut, on a :
[[File:Additionneur complet avec propagation et génération de retenue.png|class=transparent|centre|vignette|upright=2|Additionneur complet avec propagation et génération de retenue.]]
Certes, on retombe sur le circuit vu plus haut. Mais il est possible de remplacer le circuit en orange par un autre. Par exemple, il est possible de le remplacer par multiplexeur, qui choisit entre la retenue générée et la retenue entrante (propagée). Le choix se fait selon la valeur du bit P, qui chosiit entre propager la retenue et la générer.
[[File:Additionneur crée avec un multiplexeur.png|centre|vignette|upright=1.5|Additionneur crée avec un multiplexeur]]
Le circuit exact est illustré ci-dessous. Il semble utiliser plus de portes logiques que l'additionneur complet précédent. Cependant, nous verrons dans quelques chapitres qu'il est possible d'implémenter un multiplexeur avec seulement 6 transistors, voire moins ! L'implémentation utilise des portes à transmission, mais nous en reparlerons dans le chapitre sur les transistors, quand nous verrons les additionneurs à ''Manchester Carry Chain''. Au passage, une variante de ce circuit a été utilisée dans le processeur processeur 8086 d'Intel, comme on le verra dans le chapitre suivant.
[[File:Additionneur complet basé sur un MUX.png|centre|vignette|upright=1|Additionneur complet basé sur un MUX]]
[[File:FulladderPG.png|vignette|upright=1|Additionneur complet avec deux sorties P et G.]]
Pour finir, sachez qu'il existe des additionneurs qui fournissent : le bit de résultat, deux sorties P et G qui indiquent si l'addition propage ou génère une retenue. Il ne s'agit pas d'additionneurs complets, car il manque de quoi calculer la retenue sortante. De tels additionneurs seront appelés des '''additionneurs partiels'''. De tels additionneurs sont utilisés dans certains additionneurs pour gagner en performance.
Enfin, il existe aussi ce que j'ai décidé d'appeler des '''additionneurs P/G''', qui sont des additionneurs complets auxquels on a ajouté deux sorties P et G, en plus de la sortie de retenue. Leur circuit est le même que celui d'un additionneur complet, auquel on a ajouté deux fils. Vu que ces deux sorties sont fournies par le premier demi-additionneur, ajouter ces deux sorties demande d'ajouter des fils, pas de portes logiques. De tels additionneurs sont aussi utilisés dans certains additionneurs pour gagner en performance.
[[File:FAwithGP.svg|centre|vignette|upright=2|Additionneur complet avec sorties P et G : circuit.Ç]]
===L'additionneur complet basé sur une porte à majorité===
Maintenant, voyons une dernière implémentation possible de l'additionneur complet. Mais avant de voir comment l'additionneur est implémenté, nous allons devoir faire un petit focus sur la retenue sortante. Reprenons la table de vérité, et regardons ce qu'il en est pour la retenue sortante uniquement.
{|class="wikitable"
|-
! Retenue entrante !! Opérande 1 !! Opérande 2 !! !! Retenue sortante
|-
||0||0||0|| ||0
|-
||0||0||1|| ||0
|-
||0||1||0|| ||0
|-
||0||1||1|| ||1
|-
||1||0||0|| ||0
|-
||1||0||1|| ||1
|-
||1||1||0|| ||1
|-
||1||1||1|| ||1
|}
Vous remarquerez que la retenue sortante est égale au bit majoritaire, parmi les trois bits d'opérande. Si deux ou trois bits sont à 1, la retenue sortante vaut 1. Et réciproquement avec 0. Le circuit de calcul de la retenue peut donc être remplacé par une '''porte à majorité'''.
[[File:Additionneur crée avec une porte à majorité.png|centre|vignette|upright=2|Additionneur crée avec une porte à majorité]]
Mais une porte à majorité est assez gourmande en circuit, ce qui fait qu'un additionneur pareil ne serait pas pratique, ni utile. Du moins, ce serait le cas s'il n'y avait pas une possibilité d'optimisation extrêmement intéressante. Il est possible de calculer le 'bit de somme' à partir de la retenue sortante ! En effet, le bit de somme est l'inverse de la retenue sortante, sauf dans deux cas : les trois bits d'entrée sont à 0, où ils sont tous à 1. Le bit de somme vaut 0 dans le premier cas, 1 dans le second cas.
{|class="wikitable"
|-
! Retenue entrante !! Opérande 1 !! Opérande 2 !! !! Retenue sortante !! Bit de somme
|- class="f_bleu"
||0||0||0|| ||0||0
|- class="f_vert"
||0||0||1|| ||0||1
|- class="f_vert"
||0||1||0|| ||0||1
|- class="f_vert"
||0||1||1|| ||1||0
|- class="f_vert"
||1||0||0|| ||0||1
|- class="f_vert"
||1||0||1|| ||1||0
|- class="f_vert"
||1||1||0|| ||1||0
|- class="f_rouge"
||1||1||1|| ||1||1
|}
En clair, 6 lignes de la table de vérité sur 8 peuvent se calculer avec une porte à majorité, le reste demandant quelques portes logiques pour faire la correction. L'implémentation en circuit demande donc trois choses :
* de calculer la retenue sortante et de quoi l'inverser ;
* un circuit qui vérifie si tous les bits opérande valent 0 : une porte NOR ou OU fait l'affaire ;
* un circuit qui vérifie s'ils valent tous 1, à savoir une porte ET.
Ensuite, on combine le résultat des trois circuits précédents pour obtenir le résultat final. La combinaison est le fait du circuit en jaune dans le schéma ci-dessous.
[[File:Full adder basé sur une modification de la retenue.png|centre|vignette|upright=2|Full adder basé sur une modification de la retenue]]
Le circuit en jaune est assez simple. Quand la porte ET sort un 1, la sortie doit être forcée à 1. Par contre, quand la porte OU sort un zéro, la sortie doit être forcée à 0. Il suffit donc d'enchainer un circuit de mise à 1 et un circuit de mise à 0. Nous avons vu dans le chapitre sur les opérations de masquage qu'il s'agit respectivement d'une porte OU et d'une porte ET.
[[File:Full adder basé sur une modification de la retenue interne, au complet.png|centre|vignette|upright=2|Full adder basé sur une modification de la retenue interne, au complet]]
Divers processeurs utilisaient des additionneurs complets de ce type : le fameux Z80, l'Intel 4004 et de l'Intel 8008 sont les exemples les plus notables. Pourtant, on pourrait s'étonner qu'un tel circuit ait existé. Il utilise beaucoup de portes logiques, a une profondeur logique supérieure : il n'a rien d'avantageux. Sauf qu'il était utilisé sur d'anciens processeurs, qui utilisaient la technologie dite TTL, différente de la technologie CMOS des transistors modernes. Et avec la technologie TTL, il est possible de fusionner plusieurs portes logiques ET et NOR en une seule porte logique ET/OU/NON ! Un additionneur complet construit ainsi ne prenait que deux portes logiques : une pour le calcul de la retenue sortante, une autre pour le reste du circuit.
==L'addition non signée==
Voyons maintenant un circuit capable d'additionner deux nombres entiers: l''''additionneur'''. Dans la version qu'on va voir, ce circuit manipulera des nombres strictement positifs. L'addition des nombres codés en complètement à deux sera vu dans une section ultérieure.
[[File:Full-adder-4bits-serial-CEI.svg|vignette|upright=0.5|Additionneur 4 bits, un bloc.]]
L'interface d'un additionneur est illustrée ci-contre. Un additionneur prend deux opérandes sur deux entrées séparées, et fournit le résultat sur une sortie dédiée. De plus, il a une entrée sur laquelle envoyer une retenue entrante, et a une sortie pour la retenue sortante. La sortie de retenue indique que le résultat de l'addition a débordé (au sens d'un débordement d'entier). Pour l'entrée de retenue, son utilité deviendra plus claire dans ce qui suit, mais souvenez-vous qu'elle existe.
L'entrée et la sortie de retenue sont parfois utilisées pour combiner plusieurs petits additionneurs, pour former un additionneur plus grand. Nous verrons de nombreux exemples dans ce qui suit. Il est assez commun de créer des additionneurs pour des opérandes de 16/32/64 bits, avec des additionneurs de 4/5 bits.
===L'additionneur série===
Il est possible d'additionner deux nombres bit par bit, grâce à un additionneur complet associé à plusieurs registres à décalages. Les opérandes sont placées chacune dans un registre à décalage, et l'additionneur complet utilise les bits sortants de ces registres à décalage. Le bit du résultat est envoyé au registre à décalage pour le résultat. La retenue de l'addition est stockée dans une bascule de 1 bit, en attente du prochain cycle d'horloge. Un tel additionneur est appelé un '''additionneur série'''. Il a été utilisé sur d'anciens ordinateurs dans les années 50-60, aussi bien des prototypes que des ordinateurs commerciaux.
[[File:Additionneur série.jpg|centre|vignette|upright=2.5|Additionneur série.]]
===L'additionneur à propagation de retenue===
L''''additionneur à propagation de retenue''' est aussi appelé l'additionneur ''ripple carry'', c'est son nom anglais. J'utiliserais occasionnellement le nom anglais dans ce qui suit, car celui-ci est plus court. Ilutilise un additionneur complet pour chaque colonne de l'addition. Pour gérer les retenues, il suffit ainsi de câbler des additionneurs complets les uns à la suite des autres, chacun envoyant sa retenue à la colonne suivante.
[[File:Ripplecarryadder.png|centre|vignette|upright=2.5|Additionneur à propagation de retenue.]]
Notons la présence de la retenue sortante, qui est utilisée pour détecter les débordements d'entier, ainsi que pour d'autres opérations. Le bit de retenue final est souvent stocké dans un registre spécial du processeur (généralement appelé ''carry flag'').
Notez aussi, à droite du schéma précédent, l'entrée de retenue <math>\text{R}_\text{Entrée}</math>. Elle est très utile pour l'implémentation de certaines opérations comme l'inversion de signe, la soustraction, l'incrémentation, etc. Certains processeurs sont capables de faire une opération appelée ADC, ADDC ou autre nom signifiant ''Addition with Carry'', qui permet de faire le calcul A + B + Retenue (la retenue en question est la retenue sortante de l'addition précédente, stockée dans le registre ''carry flag''). Son utilité principale est de permettre des additions d'entiers plus grands que ceux supportés par le processeur. Par exemple, cela permet de faire des additions d'entiers 32 bits sur un processeur 16 bits.
[[File:Adder Network 3 Bit Sum Delay 003.svg|vignette|upright=0.5|Propagation de retenue dans l'additionneur.]]
Cet additionneur utilise très peu de portes logiques, ce qui fait qu'il était utilisé sur certains processeurs 8 et 16 bits, dont le budget en portes logiques était limité. Bien que très simple, cet additionneur est cependant peu performant. Son temps de calcul est proportionnel à la taille des opérandes. Par exemple, additionner deux opérandes de 32 bits prendra deux fois plus de temps que pour des opérandes de 16 bits. La raison est que les retenues doivent se propager à travers le circuit, du premier additionneur jusqu'au dernier.
L'addition étant une opération fréquente, d'autres additionneurs ont été inventés. Ils utilisent des optimisations qui utilisent plus de circuits pour gagner en rapidité. Mais avant de voir ces optimisations, nous allons voir s'il est possible d'optimiser les additionneurs à propagation de retenue.
===L'additionneur à saut de retenue===
L'additionneur à propagation de retenue peut être optimisé de deux manières. La première accélère la propagation de la retenue, en agissant au niveau des additionneurs complets. La ''Manchester carry chain'' est une optimisation de ce type, mais on ne peut pas encore expliquer à ce stade du cours, car elle optimisait les additionneurs complets au niveau des transistors eux-mêmes. Elle a été utilisée sur de nombreux processeurs connus, comme le 8086 d'Intel ou le Z80.
La seconde manière rend l'addition plus rapide dans certains cas. Le résultat est un additionneur dont le temps de calcul est variable. Le calcul prendra quelques cycles d'horloges avec certains opérandes, mais d'autres opérandes ne verront aucune amélioration. Il n'améliore pas le pire des cas, dans lequel la retenue doit être propagée du début à la fin, du bit de poids faible au bit de poids fort. Mais dans d'autres cas, il permet d'avoir le résultat en avance. L'optimisation en question donne l''''additionneur à saut de retenue''' (''carry-skip adder'').
Il est composé en enchainant plusieurs additionneurs plus petits, qui additionnent 4/5 bits, rarement plus. De tels additionneurs de petite taille sont appelés des '''blocs''', dans ce qui suit. L'idée est de détecter précocement si la retenue entrante est propagée à travers tout le bloc. Si la retenue est propagée dans tous le bloc, on peut directement l'envoyer sur la sortie de retenue, la retenue saute le bloc entier. Dans le cas contraire, on doit calculer la retenue normalement. Le choix entre les deux est le fait d'un multiplexeur. Toute la difficulté est de savoir comment commander le multiplexeur.
[[File:Carry skip adder, principe de base.png|centre|vignette|upright=2.5|Carry skip adder : principe de base]]
Pour savoir si une retenue est propagée dans un bloc entier, il faut utiliser des additionneurs complets, qui ont en plus une sortie P indiquant que l'additionneur propage la retenue. Le bloc propage une retenue si chaque additionneur complet propage la retenue, donc s'ils ont tous leur sortie P à 1. Un simple ET logique suffit à déterminer si c'est le cas. Le signal de commande du multiplexeur est donc un vulgaire ET entre toutes les sorties P des additionneurs complets du bloc.
[[File:Additionneur à saut de retenue.png|centre|vignette|upright=2.5|Calcul de la commande du MUX.]]
Voici ce que ça donne pour un bloc de 4 bits :
[[File:CSAdder4Bit.svg|centre|vignette|upright=1.5|Additionneur ''carry skip'' : bloc de 4 bits.]]
L'additionneur à saut de retenue est construit en assemblant plusieurs blocs de ce type.
[[File:BCSAdder16Bit.svg|centre|vignette|upright=3|Additionneur à saut de retenue.]]
Les blocs sont tous identiques dans le cas le plus simple, mais il est possible d'utiliser des blocs de taille variable. Par exemple, le premier bloc peut avoir des opérandes de 6 bits, le second des opérandes de 7 bits, etc. Faire ainsi permet de gagner un petit peu en performances, si la taille de chaque bloc est bien choisie. La raison est une question de temps de propagation des retenues. La retenue met plus de temps à se propager à travers 8 blocs qu'à travers 4, ce qui prend plus de temps qu'à travers 2 blocs, etc. En tenir compte fait que la taille des blocs tend à augmenter ou diminuer quand on se rapproche des bits de poids fort.
===L'additionneur à sélection de retenue===
L''''additionneur à sélection de retenue''' utilise aussi des blocs, comme les additionneurs précédents. L'addition se fait en deux versions : une avec la retenue du bloc précédent valant zéro, et une autre version avec la retenue du bloc précédent valant 1. Il suffira alors de choisir le bon résultat avec un multiplexeur, une fois cette retenue connue. On gagne ainsi du temps en calculant à l'avance les valeurs de certains bits du résultat, sans connaître la valeur de la retenue. Petit détail : sur certains additionneurs à sélection de retenue, les blocs de base n'ont pas la même taille. Cela permet de tenir compte des temps de propagation des retenues entre les blocs.
[[File:Additionneur à sélection de retenue.png|centre|vignette|upright=1.5|Additionneur à sélection de retenue avec seulement deux blocs.]]
Dans les exemples du dessus, chaque sous-additionneur étaient des additionneurs à propagation de retenue. Mais ce n'est pas une obligation, et tout autre type d’additionneur peut être utilisé. Par exemple, on peut faire en sorte que les sous-additionneurs soient eux-mêmes des additionneurs à sélection de retenue, et poursuivre ainsi de suite, récursivement. On obtient alors un '''additionneur à somme conditionnelle''', plus rapide que l'additionneur à sélection de retenue, mais qui utilise beaucoup plus de portes logiques.
===Les additionneurs à anticipation de retenue===
Les '''additionneurs à anticipation de retenue''' calculent chaque retenue sans avoir à propager les retenues précédentes. Au lieu de calculer les retenues une par une, ils calculent toutes les retenues en parallèle, à partir des bits des opérandes. Une fois les retenues pré-calculées, il suffit de les additionner avec les deux bits adéquats, pour obtenir le résultat. Ces additionneurs sont composés de deux parties :
* un circuit qui pré-calcule les retenues, sans les propager, directement à partir des opérandes ;
* d'une couche d'additionneurs complets, qui additionnent chacun deux bits d'opérande et la retenue pré-calculée.
[[File:Additionneur à anticipation de retenue.png|centre|vignette|upright=1.5|Additionneur à anticipation de retenue.]]
Le calcul des retenues se fait en deux étapes. La première détermine si les retenues sont propagées ou générées, pour chaque colonne. Elle détermine, pour chaque colonne, les bits P et G qui indiquent respectivement : qu'une retenue a été générée sur cette colonne, que la colonne propage la retenue précédente. La seconde étape utilise ces signaux P et G pour déterminer la retenue sur chaque colonne. Le circuit de calcul des retenues est donc composé de deux parties : une couche de demi-additionneurs pour générer les signaux P et G, un circuit de calcul des retenues proprement dit.
[[File:Circuit complet d'un additionneur à anticipation de retenue.png|centre|vignette|upright=1.5|Circuit complet d'un additionneur à anticipation de retenue.]]
Pour rappel, la retenue de la colonne i, notée <math>C_i</math> est égale à :
: <math>G_i + (P_i . C_{i-1})</math>, avec G le signal de génération de retenue, P le signal de propagation de retenue.
Si on utilisait cette formule sans trop réfléchir, on retomberait sur un additionneur à propagation de retenue inutilement compliqué. L'astuce des additionneurs à anticipation de retenue consiste à remplacer le terme Ci−1 par sa valeur calculée avant. Par exemple, je prends un additionneur 4 bits. Je dispose de deux nombres A et B, contenant chacun 4 bits : A3, A2, A1, et A0 pour le nombre A, et B3, B2, B1, et B0 pour le nombre B. Si j'effectue les remplacements, j'obtiens les formules suivantes :
* C1 = G0 + ( P0 · C0 ) ;
* C2 = G1 + ( P1 · G0 ) + ( P1 · P0 · C0 ) ;
* C3 = G2 + ( P2 · G1 ) + ( P2 · P1 · G0 ) + ( P2 · P1 · P0 · C0 ) ;
* C4 = G3 + ( P3 · G2 ) + ( P3 · P2 · G1 ) + ( P3 · P2 · P1 · G0 ) + ( P3 · P2 · P1 · P0 · C0 ).
Ces formules nous permettent de déduire la valeur d'une retenue directement : il reste alors à créer un circuit qui implémente ces formules, et le tour est joué.
[[File:Four bit adder with carry lookahead.svg|centre|vignette|upright=1.5|Additionneur à anticipation de retenue de 4 bits.]]
Plus haut, j'ai dit que le circuit était composé de trois couches : une couche de demi-additionneur pour les signaux P et G ; un circuit d'anticipation de retenue, puis l'addition finale. Mais les deux bits d'opérandes sont déjà additionnés dans la couche de demi-additionneur, autant réutiliser le résultat pour l'additionner à la retenue. Pour éliminer toute redondance de ce type, il est possible d'utiliser des additionneurs partiels (des additionneurs où la sortie de retenue a été remplacée par deux sorties P et G). L'additionneur final est alors composé de plusieurs additionneurs complets de ce type, couplé à un circuit d'anticipation de retenue.
[[File:Cla4bits.png|centre|vignette|upright=2.5|Additionneur à anticipation de retenue de 4 bits.]]
Les additionneurs à anticipation de retenue sont plus rapides que les additionneurs à propagation de retenue. La raison est que les retenues sont calculées rapidement, dans un temps plus petit qu'en propageant la retenue. Pour rentrer dans les détails mathématiques, le temps de calcul n'est pas proportionnel au nombre de bits, mais proportionnel au logarithme de ce nombre de bits. Par contre, le gain en temps de calcul s'accompagne d'un cout en portes logiques conséquent. Le circuit qui calcule les retenues anticipées utilise beaucoup de portes logiques.
===Les additionneurs à calcul parallèle de préfixe===
Du fait du cout en portes logiques, utiliser un additionneur à anticipation de retenue est une solution pratique pour additionner 4 ou 8 bits. Mais pour des opérandes plus grands, de 16 ou 32 bits, cela utiliserait trop de portes logiques. Une partie de ce cout en portes logiques est cependant lié à des redondances. Si on analyse deux retenues différentes, à des colonnes différentes. Les circuits qui calculent ces deux retenues seront différents, mais ils contiendront des sous-circuits identiques. Pour vous en rendre compte, reprenons les quatre équations obtenues précédemment et essayez de trouver des redondances :
* C1 = G0 + ( P0 · C0 ) ;
* C2 = G1 + ( P1 · G0 ) + ( P1 · P0 · C0 ) ;
* C3 = G2 + ( P2 · G1 ) + ( P2 · P1 · G0 ) + ( P2 · P1 · P0 · C0 ) ;
* C4 = G3 + ( P3 · G2 ) + ( P3 · P2 · G1 ) + ( P3 · P2 · P1 · G0 ) + ( P3 · P2 · P1 · P0 · C0 ).
Vous devriez trouver :
* le terme ''P0 · C0'' dans toutes les lignes ;
* les termes ''P1 · P0 · C0'' et ''P1 · P0'' dans les trois dernières lignes ;
* le terme ''P2 · P1 · P0 · C0'' ; ''P2 · P1 · G0'' et ''P2 · G1'' dans les deux dernières lignes.
Il existe plusieurs manières d'exploiter de telles redondances, exploitées dans les '''additionneurs à calcul parallèle de préfixe'''. L'optimisation apportée est de générer des signaux ''propagate'' et ''generate'' pour des groupes de 2, 3, 4, ..., N colonnes. Par exemple, il est possible de générer un signal ''P 0 vers 3'', qui indique si la retenue de la première colonne est propagée jusqu'à la 4ème colonne. Un autre exemple est un signal qui indique qu'une retenue a été générée entre les colonnes 4 à 7 génèrent une retenue ou non. En clair, les signaux P et G ont maintenant un intervalle, qui précise de quelle colonne vers quelle colonne se fait la propagation, ou entre quelles colonnes se fait la génération.
Il est possible de combiner les signaux P et G de deux groupes de colonne, s'ils sont contiguës (ils peuvent aussi se recouvrir). Par exemple, il est possible de calculer les bits P et G pour les colonnes 0 à 10, à partir des deux signaux P/G des colonnes 0-4 et 5-10. Pour cela, les équations sont assez simples. Si je prends deux groupes nommés A et B, avec A pour les colonnes de poids fort et B celles de poids faible, on a :
: <math>P_{sortie} = P_A . P_B</math>
: <math>G_{sortie} = G_A . P_A . G_B</math>
Pour les bits des colonnes 2, 4, 8, 16, 32, 64, et autres, le calcul est simple. L'idée est de grouper les colonnes par groupes de 2. Puis, on calcule chaque groupe de 4 bits à partir de deux groupes de 2. Puis les groupes de 8 bits sont calculés à partir de deux groupes de 4 bits, et ainsi de suite pour toute puissance de deux.
[[File:Squelette d'un additionneur à calcul parallèle de préfixe.jpg|centre|vignette|upright=2|Squelette d'un additionneur à calcul parallèle de préfixe]]
Les seules difficultés surviennent pour les colonnes dont le nombre n'est pas une puissance de deux. Il y a plusieurs manières pour subdiviser ces colonnes en intervalles plus petits et combiner le tout. Et elles donnent chacune des additionneurs différents. Ils portent les noms d''''additionneur de Ladner-Fisher''', d''''additionneur de Brent-Kung''', d''''additionneur de Kogge-Stone''', etc. Dans le cas général, l'additionneur de Kogge-Stone est un des plus rapide, mais aussi un des plus gourmand en circuits, alors que c'est l'inverse pour l'additionneur de Brent-Kung. Les deux additionneurs sont illustrés ci-dessous, pour des opérandes de 8 bits. Dans ceux-ci, chaque losange calculent les bits P et G, à partir des signaux P et G de deux groupes précédents.
{|
|[[File:Brent-kung-8-bit.png|vignette|Brent-kung-8-bit]]
|[[File:Kogge-stone-8-bit.png|vignette|upright=1.2|Kogge-stone-8-bit]]
|}
===Le calcul parallèle de la retenue===
L'anticipation de retenue est très rapide, mais utilise beaucoup de circuits, même en utilisant le calcul parallèle de préfixe. Les additionneurs à propagation de retenue font eux le compromis inverse. Mais il existe des intermédiaires, qui visent à obtenir un compromis entre performance et cout en portes logiques.
Un de ces compromis découpe un additionneur de 16/32/64 bits en additionneurs de 4/5 bits, qui sont enchainés en utilisant la propagation de retenue. Par contre, ces petits additionneurs de 4/5 bits utilisent l'anticipation de retenue. Ces additionneurs simples seront nommés ''blocs'' dans ce qui suit. Par exemple, on peut utiliser 4 additionneurs à anticipation de retenue de 4 bits, et propager les retenues entre eux. Le premier bloc calcule une retenue, qui est propagée au second bloc. Le second bloc calcule lui aussi une retenue, qui est propagée au troisième bloc, etc.
[[File:4008 Functional Diagram.svg|vignette|upright=1|4008 Functional Diagram]]
Le gain en performance est significatif, mais il est possible de faire presque aussi rapide, tout en économisant beaucoup de circuits. L'idée est de simplifier les additionneurs 4 bits, afin de ne pas utiliser une anticipation de retenue complète. L'anticipation de retenue n'est utilisée que pour la retenue sortante, alors que les bits de somme sont calculées avec propagation de retenue. La retenue sortante est donc calculée en parallèle de l'addition. Chaque bloc contient, à côté d'un additionneur proprement dit, un circuit qui calcule la retenue sortante. Il existe de nombreuses manières de calculer la retenue sortante.
Le gain en portes logiques est assez significatif. En effet, on se passe de circuits d'anticipation de retenue pour les 4 bits de somme, on ne garde que celui pour la retenue sortante. Le fait d'utiliser la propagation de retenue pour calculer les 4 bits de somme est censé avoir un léger cout en performance, mais il se trouve que ce cout est très faible. En effet, le résultat n'est connu qu'une fois les retenues propagées jusqu'au dernier bloc. Et malgré l'anticipation des retenues, la propagation est assez lente. les additionneurs à propagation de retenue auront terminé leur travail bien avant.
Un exemple est celui de l'additionneur CMOS 4008, un additionneur de 4 bit. Il est composé en trois sections. Une première couche de demi-additionneurs calcule les signaux P et G utilisés à la fois pour l'anticipation de la retenue sortante, que pour calculer les bits de somme. Le circuit de calcul de la retenue sortante utilise l'anticipation de retenue. Le reste du circuit fait l'addition en propageant les retenues.
[[File:CMOS 4008, circuit découpé en sections.png|centre|vignette|upright=1.5|CMOS 4008, circuit découpé en sections]]
===Les additionneurs à anticipation de retenue multi-niveau===
Les additionneurs précédents mixent anticipation et propagation de retenue. Ceux que nous allons voir utilisent des blocs, mais utilisent l'anticipation de retenue entre les blocs. Les blocs utilisés peuvent être un additionneur à propagation de retenue, ou à anticipation de retenue, peu importe. Ce qui compte est que la retenue entrante de chaque bloc est calculée par anticipation de retenue.
Par exemple, il est possible de créer un additionneur 16 bits en utilisant 4 additionneurs/blocs de 4 bits, couplé à un circuit d’anticipation de retenue par bloc. Pour cela, les additionneurs de 4 bits doivent fournir deux sorties P et G. Les deux sorties indiquent que le bloc soit a généré une retenue, soit a propagé la retenue entrante. Par propagé, on veut dire que la retenue entrante du bloc s'est retrouvée sur la sortie de retenue du bloc. C'est le cas si les 4 colonnes propagent la retenue, ce qui signifie que :
: <math>P_\text{4 bits} = P_0 \cdot P_1 \cdot P_2 \cdot P_3</math>
Pour la génération, cela veut dire qu'une colonne a générée une retenue, qui a été propagée jusqu'à la sortie. En clair, pour chaque colonne, il faut que le G = 1, et que les signaux P des colonnes soient à 1. Pour les 4 colonnes, cela donne :
: <math>G_\text{4 bits} = G_3 + G_2 \cdot P_3 + G_1 \cdot P_3 \cdot P_2 + G_0 \cdot P_3 \cdot P_2 \cdot P_1</math>
[[File:Cla4bitsPG.png|centre|vignette|upright=2|Additionneur à anticipation de retenue de 4 bits.]]
Pour combiner 4 blocs de 4 bits, on devra donc combiner 4 bits P et G, avec un circuit d'anticipation de retenue.
[[File:16-bit lookahead carry unit.svg|centre|vignette|upright=2|Additionneur à anticipation de retenue de 16 bits.]]
Il est même possible d'aller plus loin et de combiner 4 additionneurs précédents pour obtenir un additionneur 64 bits.
[[File:64-bit lookahead carry unit.svg|centre|vignette|upright=2|Additionneur à anticipation de retenue de 64 bits.]]
Le résultat est un additionneur à plusieurs niveaux d’anticipation de retenue. Une anticipation de retenue dans un blocs de 4 bits (facultative), entre blocs de 4 bits, entre blocs de 16 bits. La même logique peut être utilisée avec des blocs de taille différente de 4, 16 et 64 bits.
==L'addition signée et la soustraction==
Après avoir vu l'addition, il est logique de passer à la soustraction, les deux opérations étant très proches. Si on sait câbler une addition entre entiers positifs, câbler une soustraction n'est pas très compliqué. De plus, la soustraction permet de faire des additions de nombres signés.
===Le soustracteur à propagation de retenue===
[[File:Half Subtractor Vektor.svg|vignette|Demi-soustracteur.]]
Dans le chapitre sur les incrémenteurs/décrémenteurs, nous avons vu un circuit qui peut soustraire deux bits. Celui-ci est appelé un '''demi-soustracteur'''. Il ressemble beaucoup à un demi-additionneur, les différences se résumant à une porte NON ajoutée pour le calcul de la retenue. Il implémente la table de soustraction, qui est très simple en binaire :
* 0 - 0 = 0 ;
* 0 - 1 = 1 et une retenue ;
* 1 - 0 = 1 ;
* 1 - 1 = 0.
Pour soustraire deux nombres entiers, on peut adapter l'algorithme de soustraction utilisé en décimal, celui que vous avez appris à l'école. Celui-ci ressemble fortement à l'algorithme d'addition : on soustrait les bits de même poids, et on propage éventuellement une retenue sur la colonne suivante. À la différence de l'addition, la retenue est soustraite, et non ajoutée. En clair, une soustraction en binaire demande de faire deux soustraction : pour chaque bit de la première opérande, on soustrait le bit associé de la seconde, puis on soustrait la retenue entrante.
[[File:Algebra1 05 fig014.svg|centre|vignette|Soustraction en binaire, avec les retenues en rouge.]]
Pour cela, comme pour l'additionneur, deux demi-soustracteurs peuvent être combinés pour donner un '''soustracteur complet'''. Le calcul de la retenue se fait en combinant les deux retenues des demi-soustracteurs avec une porte OU.
[[File:FullSubtractor.svg|centre|vignette|upright=2|Soustracteur complet.]]
Les soustracteurs complets sont utilisés pour créer des soustracteurs à propagation de retenue ou tout autre circuit soustracteur, sur le même modèle que les additionneurs.
[[File:Sub Network 3 Bit Step 002.svg|centre|vignette|upright=2|Soustracteur à propagation de retenue pour opérandes de 3 bits.]]
Un défaut est que cela ne fonctionne bien que pour les additionneurs/soustracteurs à propagation de retenue. L'anticipation de retenue et les autres optimisations ne marchent pas de la même manière entre addition et soustraction. Les signaux de propagation et de génération de retenue ne sont pas générés pareil, idem pour la manière de les combiner. Il est toujours possible de créer un soustracteur avec ces techniques, mais aucune mutualisation de circuit ne sera possible. Ce qui n'est pas le cas avec les solutions que nous allons voir dans ce qui suit.
Il est possible de fusionner un additionneur à propagation de retenue et un soustracteur de même type. Pour cela, il suffit de remarquer que les demi-soustracteurs et les demi-additionneurs se ressemblent beaucoup. La seule différence entre les deux est une vulgaire porte NON. En remplaçant cette porte NON par un inverseur commandable, on obtient un circuit qui sert soit de demi-additionneur, soit de demi-soustracteur, suivant ce qu'on envoie sur l'entrée de commande. Ci-dessous est illustré ce que donne ce remplacement pour un soustracteur complet. Le résultat est un '''additionneur/soustracteur à propagation de retenue'''.
[[File:Additionneur-soustracteur complet.png|centre|vignette|upright=2|Additionneur-soustracteur complet]]
===L'additionneur-soustracteur pour opérandes codées en complément à deux===
Étudions le cas de la soustraction en complément à deux, dans l'objectif de créer un circuit soustracteur. Vous savez sûrement que a−b et a+(−b) sont deux expressions équivalentes. Et en complément à deux, − b = not(b) + 1. Dit autrement, a − b = a + not(b) + 1. On pourrait se dire qu'il faut deux additionneurs pour faire le calcul, mais la majorité des additionneurs possède une entrée de retenue pour incrémenter le résultat de l'addition. Un soustracteur en complément à deux est donc simplement composé d'un additionneur et d'un inverseur.
[[File:Additionneur soustracteur.png|centre|vignette|upright=2|Soustracteur en complément à deux.]]
Il est possible de créer un circuit capable d'effectuer soit une addition, soit une soustraction : il suffit de remplacer l'inverseur par un inverseur commandable, qui peut être désactivé. On a vu comment créer un tel inverseur commandable dans le chapitre sur les circuits combinatoires. On peut remarquer que l'entrée de retenue et l'entrée de commande de l'inverseur sont activées en même temps : on peut fusionner les deux signaux en un seul.
[[File:Additionneur-soustracteur en complément à deux.png|centre|vignette|upright=2|Additionneur-soustracteur en complément à deux.]]
Une implémentation alternative est la suivante. Elle remplace l'inverseur commandable par un multiplexeur.
[[File:4-bit ripple carry adder-subtracter.svg|centre|vignette|upright=2|Additionneur-soustracteur en complément à deux, version alternative.]]
===L'additionneur-soustracteur pour opérandes codées en signe-magnitude===
Passons maintenant aux nombres codés en signe-valeur absolue, les deux opérandes étant notées A et B. Suivant les signes des deux opérandes, on a quatre cas possibles : A + B, A − B (B négatif), −A + B (A négatif) et −A − B (A et B négatifs). Une astuce est que le circuit n'a besoin que de calculer A + B et A − B : il peut les inverser pour obtenir − A − B ou B − A. A + B et A − B peuvent se calculer avec un additionneur-soustracteur, reste à corriger le résultat. Il suffit de lui ajouter un inverseur commandable pour obtenir le circuit d'addition finale.
[[File:Additionneur en signe-valeur absolue.png|centre|vignette|upright=2|Additionneur en signe-valeur absolue.]]
Toute la difficulté tient dans le calcul du bit de signe du résultat, quand interviennent des soustractions. Autant l'addition de deux nombres de même signe (A + B et −A − B) ne pose aucun problème, autant les soustractions posent problème (A − B et −A + B). Suivant que <math>A<B</math> ou que <math>A>B</math>, le signe du résultat ne sera pas le même. Déterminer le signe du résultat se fait en regardant les bits de débordement d'entier, comme on le verra plus bas.
===L'additionneur-soustracteur pour opérandes codées en représentation par excès===
Passons maintenant aux nombres codés en représentation par excès. On pourrait croire que ces nombres s'additionnent comme des nombres non-signés, mais ce serait oublier la présence du biais, qui pose problème. Dans les cas de nombres signés gérés avec un biais, voyons ce que donne l'addition de deux nombres :
: <math>( n_1 + biais ) + ( n_2 + biais ) = n_1 + n_2 + 2 \times biais</math>
Or, le résultat correct serait :
: <math>n_1 + n_2 + biais</math>
En effectuant l'addition telle quelle, le biais est compté deux fois. On doit donc le soustraire après l'addition pour obtenir le résultat correct.
Même chose pour la soustraction qui donne ceci :
: <math>(n_1 + biais) - (n_2 + biais) = n_1 - n_2</math>
Or, le résultat correct serait :
: <math> ( n_1 - n_2 ) + biais </math>
Il faut rajouter le biais pour obtenir l'exposant correct.
On a donc besoin de deux additionneurs/soustracteurs : un pour additionner/soustraire les représentations binaires des opérandes, et un autre pour ajouter/retirer le biais en trop/manquant.
==L'additionneur BCD==
Maintenant, voyons un additionneur qui additionne deux entiers au format BCD. Pour cela, nous allons devoir passer par deux étapes. La première est de créer un circuit capable d'additionneur deux chiffres BCD. Ensuite, nous allons voir comment enchaîner ces circuits pour créer un additionneur BCD complet.
===L'additionneur BCD qui fait l'opération chiffre par chiffre===
Nous allons commencer par voir un additionneur qui additionne deux chiffres en BCD, une sorte d'équivalent BCD de l'additionneur complet. Il fournit un résultat sur 4 bits et une retenue qui est mise à 1 si le résultat dépasse 10 (la limite d'un chiffre BCD). Les deux opérandes sont des chiffres BCD codés sur 4 bits et sont additionnés en binaire par un additionneur des plus normaux, similaire à ceux vus plus haut. Le résultat est alors un entier codé en binaire, sur 5 bits, qu'on corrige/convertit pour obtenir un chiffre BCD et une retenue sortante.
Pour corriger le résultat, une idée intuitive serait de prendre le résultat et de faire une division par 10. Le quotient donne la retenue, alors que le reste est le résultat, le chiffre BCD. Mais un circuit diviseur par 10 utilise beaucoup de portes logiques, ce qui ne vaut pas le coup. Une autre méthode détecte si le résultat est égal ou supérieur à 10, ce qui correspond à un "débordement" (on dépasse les limites d'un chiffre BCD). Si le résultat est plus petit que 10, il n'y a rien à faire : le résultat est bon et la retenue est de zéro. Par contre, si le résultat vaut 10 ou plus, il faut corriger le résultat et générer une retenue à 1.
Il faut donc ajouter un circuit qui détecte si le résultat est supérieur à 9, qui calcule directement la retenue. Ce circuit peut se fabriquer simplement à partir de sa table de vérité, ou en utilisant les techniques que nous verrons dans un chapitre ultérieur sur les comparateurs. La solution la plus simple est clairement d'utiliser la table de vérité, ce qui est très simple, assez pour être laissé en exercice au lecteur. Pour comprendre comment corriger le résultat, établissons une table de vérité qui associe le résultat et le résultat corrigé. L'entrée vaut au minimum 10 et au maximum 9 + 9 = 18. On considère la sortie comme un tout, la retenue étant un 5ème bit, le bit de poids fort.
{|class="wikitable"
|-
! colspan="5" | Entrée
!
! rowspan="10" |
! Retenue
! Résultat corrigé (sans retenue)
! interprétation de la sortie en binaire (retenue inclue)
|-
| 0 || 1 || 0 || 1 || 0
| (10)
| 1 || 0000
| (16)
|-
| 0 || 1 || 0 || 1 || 1
| (11)
| 1 || 0001
| (17)
|-
| 0 || 1 || 1 || 0 || 0
| (12)
| 1 || 0010
| (18)
|-
| 0 || 1 || 1 || 0 || 1
| (13)
| 1 || 0011
| (19)
|-
| 0 || 1 || 1 || 1 || 0
| (14)
| 1 || 0100
| (20)
|-
| 0 || 1 || 1 || 1 || 1
| (15)
| 1 || 0101
| (21)
|-
| 1 || 0 || 0 || 0 || 0
| (16)
| 1 || 0110
| (22)
|-
| 1 || 0 || 0 || 0 || 1
| (17)
| 1 || 0111
| (23)
|-
| 1 || 0 || 0 || 1 || 0
| (18)
| 1 || 1000
| (24)
|}
En analysant le tableau, on voit que pour corriger le résultat, il suffit d'ajouter 6. La raison est que le résultat déborde d'un nibble à 16 en binaire, mais à 10 en décimal : il suffit d'ajouter la différence entre les deux, à savoir 6, et le débordement binaire fait son travail. Donc, la correction après une addition est très simple : si le résultat dépasse 9, on ajoute 6.
On peut maintenant implémenter l'additionneur BCD, en combinant le comparateur avec 10, le circuit de correction, et l'additionneur. La première solution calcule deux versions du résultat : la version corrigée, la version normale. Le choix entre les deux est réalisée par un multiplexeur, commandé par le comparateur.
[[File:Additionneur BCD.png|centre|vignette|upright=1.5|Additionneur BCD]]
L'autre solution utilise un circuit commandable qui soit additionne 6, soit ne fait rien. Le choix entre les deux est commandé par le bit calculé par le comparateur.
[[File:Additionneur BCD, seconde version.png|centre|vignette|upright=1.5|Additionneur BCD, seconde version.]]
Une version alternative du circuit précédent est la suivante. Il contient deux additionneurs : un pour additionner les deux chiffres BCD, un autre pour additionner 6 si besoin. Le résultat du comparateur est directement utilisé pour générer l'opérande du second additionneur : 0 ou 6. Le circuit est simple à concevoir, mais gaspille beaucoup de circuit. Idéalement, il vaudrait mieux utiliser un circuit combinatoire d'addition avec une constante.
[[File:Adder 4 Bit BCD.svg|centre|vignette|upright=2|Additionneur BCD, circuit complet.]]
Pour obtenir un additionneur BCD complet, il suffit d’enchaîner les additionneurs précédents, comme on le ferait avec les additionneurs complets dans un additionneur à propagation de retenue. Au final, l'additionneur BCD est beaucoup plus compliqué qu'un additionneur normal, car il rajoute un comparateur ">9", un petit additionneur pour ajouter 6 et éventuellement d'autres circuits. De plus, il est difficile d'appliquer les optimisations disponibles sur les additionneurs non-BCD. Notamment, les circuits d'anticipation de retenue sont totalement à refaire et le résultat est relativement compliqué. C'est ce qui explique pourquoi le BCD a progressivement été abandonné au profit du binaire simple.
La soustraction en BCD se fait comme en binaire : le nombre à soustraire est remplacé par son complément, le circuit additionne le complément et l'autre opérande, le débordement d'entier fait que le résultat marche. Sauf qu'ici, le complément est un complément à 9. Il se calcule chiffre par chiffre : chaque chiffre est remplacé par (9 - le chiffre en question).
===L'additionneur BCD par ajustement décimal===
L'additionneur BCD précédent effectuait son travail chiffre BCD par chiffre BCD, mais il existe des additionneurs BCD qui font autrement. Sur les premiers processeurs x86, il n'y avait pas d'opération d'addition BCD proprement dit, seulement une addition binaire normale de 8, 16 ou 32 bits. Par contre, elle était secondée par une opération dite d''''ajustement décimal''' qui transformait un nombre binaire en nombre codé en BCD. L'opération d'ajustement décimal prenait un opérande de 8 bits codé en binaire et fournissait un résultat de la même taille, c'est à dire deux chiffres BCD. Effectuer une addition BCD demandait donc de faire deux opérations à la suite : une addition binaire simple, suivie par l'opération d'ajustement décimal. Cela permettait de gérer des nombres entiers en binaire usuel et des entiers BCD sans avoir deux instructions d'addition séparées pour les deux, sans compter que cela simplifiait aussi les circuits d'addition.
L'ajustement décimal s'effectue en ajoutant une constante bien précise à l'opérande à convertir en BCD. L'idée est que la constante est découpée en morceaux de 4 bits, correspondant chacun à un chiffre BCD de l'opérande, chaque morceau contenant soit un 0, soit 6. Cela permet d'ajouter soit 0, soit 6, à chaque chiffre BCD, et donc de le corriger. La propagation des retenues d'un chiffre à l'autre est effectuée automatiquement par l'addition binaire de la constante. L'opération d'ajustement décimal calcule automatiquement la constante. Elle découpe l'opérande en ''nibbles'', vérifie si chaque ''nibble'' est supérieur ou égal à 10, puis détermine la valeur de chaque ''nibble'' de la constante finale. Par exemple, si je prends l'opérande 1001 1110, le ''nibble'' de poids faible déborde, alors que celui de poids fort non. La constante sera donc 0000 0110 : 0x06. Inversement, si le ''nibble'' de poids fort déborde et pas celui de poids faible, la constante sera alors 0x60. Et la constante est de 0x66 si les deux ''nibbles'' débordent, de 0x00 si aucun ne déborde.
Le circuit d’ajustement décimal est donc composé de trois étapes : deux étapes pour calculer la constante, et un circuit d'addition pour additionner cette constante au nombre de départ. La première étape découpe l'opérande en morceaux de 4 bits, en chiffres BCD, et vérifie si chacun d'entre eux vaut 10 ou plus. La seconde étape prend les résultats de la première étape, et les combine pour calculer la constante. Enfin, on trouve l'addition finale, qui était réalisée par un circuit d'addition utilisé à la fois pour l'ajustement décimal et l'addition binaire. La différence entre une addition normale et une opération d'ajustement décimal tient dans le fait que les deux premières étapes sont désactivées dans une addition normale.
[[File:Additionneur BCD parallèle.png|centre|vignette|upright=1.5|Additionneur BCD parallèle]]
===L'additionneur biquinaire===
Les entiers BCD ne sont qu'un des encodages hybrides entre décimal et binaire. L'encodage biquinaire est l'un d'entre eux et nous allons faire un rappel rapide à ce sujet. Pour simplifier, un chiffre encodé en biquinaire est composé de deux parties : un bit, couplé à une partie quinaire encodée en représentation ''one-hot''. La partie quinaire encode un nombre allant de 0 à 4, ce qui prend 5 bits (0, 1, 2, 3 et 4). Le bit indique s'il faut ou non ajouter 5 à la valeur encodée par la partie quinaire. Ainsi, on peut coder tous les nombres de 0 à 9.
Additionner deux nombres de biquinaire demande donc d'additionner deux parties quinaires encodées en ''one-hot'' et d'additionner deux bits. Mais attention : il faut tenir compte de la retenue de l'addition des parties quinaires. Et idéalement, il faut aussi tenir compte d'une retenue entrante, provenant de l'addition de la colonne de chiffres précédente. Toute la difficulté vient de la création de l'additionneur ''one-hot''. Heureusement, vu qu'il n'y a que 4-5 bits à additionner, il est souvent fabriqué à partir de sa table de vérité.
[[File:Additionneur bi-quinaire.png|centre|vignette|upright=2|Additionneur bi-quinaire]]
Un avantage du biquinaire est que le calcul du complément à 9 est très simple. Il faut pour cela : inverser la partie binaire avec une porte NON, puis inverser l'ordre des bits de la partie quinaire. Concrètement, le bit de poids faible devient le bit de poids fort, et ainsi de suite. Par exemple, une partie quinaire 01000 devient 00010, 10000 devient 00001, 00100 ne change pas, etc. Le tout peut se calculer avec une porte NON et 5 multiplexeurs.
===L'additionneur BCD avec calculs intermédiaires en biquinaire===
L'ordinateur IBM 1401, un ancien ''mainframe'' des années 60, utilisait un additionneur BCD un peu particulier. Les nombres étaient encodés en BCD dans la mémoire de l'ordinateur, mais les circuits de calcul utilisaient la représentation biquinaire. Lors d'un calcul, le processeur de l'ordinateur traduisait les chiffres BCD en représentation biquinaire, faisait une addition en biquinaire, avant de traduire le résultat en BCD normal.
Pour être précis, l'IBM 1401 utilisait une variante du biquinaire. L'encodage biquinaire de l'IBM 1401 est le suivant : la partie binaire disait si le chiffre était pair ou non, la partie quinaire encodait les valeurs 0, 2, 4, 6 et 8. Le chiffre se calculait en additionnant la partie binaire (0 ou 1) au nombre pair encodé par la partie quinaire. Si l'IBM 1401 utilisait cette variante du biquinaire, c'est car elle donnait des circuits de conversion BCD-biquinaire plus économes en portes logiques et plus rapides.
La partie binaire est le bit de poids faible du chiffre BCD, la partie biquinaire est calculée par un simple décodeur qui prend en entrée le chiffre BCD, amputé de son bit de poids faible. La traduction inverse demande d'utiliser un encodeur, à la place du décodeur. Par contre, le circuit d'addition biquinaire était plus compliqué du fait de la gestion des retenues. L'addition des parties binaires et quinaires se faisait en parallèle, dans deux additionneurs séparés. Cependant, l'addition des parties binaire fournit une retenue, qu'il faut prendre en compte. Pour cela, l'IBM 1401 disposait d'un troisième additionneur qui fournissait le résultat final, encodé en biquinaire.
[[File:Additionneur bi-quinaire de l'IBM 1401.png|centre|vignette|upright=2|Additionneur biquinaire de l'IBM 1401]]
Une implémentation moderne demanderait d'utiliser des portes ET combinées à des portes OU, le circuit pouvant être construit simplement à partir de sa table de vérité. Sur l'IBM 1401, le circuit était cependant différent, en raison de l'utilisation de OU câblés, des croisements de fils qui fonctionnent comme des portes OU, que nous n'avons pas encore vu pour le moment, mais qui seront détaillés dans quelques chapitres. Les OU câblés étaient utilisés pour simplifier le design du circuit, mais demandaient des portes logiques spécifiques, ce qui collait avec le fait que ce ''mainframe'' utilisait des transistors en Germanium. L'implémentation exacte est décrite dans cet article de blog, mais je ne recommande sa lecture qu'à ceux qui savent ce qu'est un OU câblé :
* [https://www.righto.com/2015/10/qui-binary-arithmetic-how-1960s-ibm.html Qui-binary arithmetic: how a 1960s IBM mainframe does math].
==Les débordements d'entier lors d'une addition/soustraction==
Les instructions arithmétiques manipulent des entiers codés sur un nombre fixe de bits, qui ne peuvent prendre leurs valeurs que dans un intervalle. Pour les nombres positifs, un ordinateur qui code ses entiers sur n bits pourra coder tous les entiers allant de 0 à <math>2^n -1</math>. Pour les nombres négatifs, l'intervalle est différent et dépend de la représentation utilisée. Dans le cas général, l'ordinateur peut coder les valeurs comprises de <math>N_\text{min}</math> à <math>N_\text{max}</math>. Si le résultat d'un calcul sort de cet intervalle, il ne peut pas être représenté par l'ordinateur et il se produit ce qu'on appelle un '''débordement d'entier'''.
La '''valeur haute de débordement''' désigne la première valeur qui est trop grande pour être représentée par l'ordinateur. Par exemple, pour un ordinateur qui peut coder tous les nombres entre 0 et 7, la valeur haute de débordement est égale à 8. On peut aussi définir la '''valeur basse de débordement''', qui est la première valeur trop petite pour être codée par l'ordinateur. Par exemple, pour un ordinateur qui peut coder tous les nombres entre 8 et 250, la valeur basse de débordement est égale à 7. Pour les nombres entiers, la valeur haute de débordement vaut <math>N_\text{max} + 1</math> , alors que la valeur basse vaut <math>N_\text{min} - 1</math> (avec <math>N_\text{max}</math> et <math>N_\text{min}</math> respectivement la plus grande et la plus petite valeur codable par l'ordinateur).
===La correction des débordements d'entier : l'arithmétique saturée===
Quand un débordement d'entier survient, tous les circuits de calcul ne procèdent pas de la même manière. Dans les grandes lignes, il y a deux réactions possibles : soit on corrige automatiquement le résultat du débordement, soit on ne fait rien et on se contente de détecter le débordement.
Si le débordement n'est pas corrigé automatiquement, le circuit ne conserve que les bits de poids faibles du résultat. Les bits en trop sont simplement ignorés. On dit qu'on utilise l''''arithmétique modulaire'''. Le problème avec ce genre d'arithmétique, c'est qu'une opération entre deux grands nombres peut donner un résultat très petit. Par exemple, si je dispose de registres 4 bits et que je souhaite faire l'addition 1111 + 0010 (ce qui donne 15 + 2), le résultat est censé être 10001 (17), ce qui est un résultat plus grand que la taille d'un registre. En conservant les 4 bits de poids faible, j’obtiens 0001 (1). En clair, un résultat très grand est transformé en un résultat très petit. Cela peut poser problèmes si on travaille uniquement avec des nombres positifs, mais c'est aussi utilisé pour coder des nombres en complément à deux.
D'autres circuits utilisent ce qu'on appelle l''''arithmétique saturée''' : si un calcul génère un débordement, on arrondi le résultat au plus grand entier supporté par le circuit. Les circuits capables de calculer en arithmétique saturée sont un peu plus complexes, vu qu'il faut rajouter des circuits pour corriger le résultat en cas de débordement. Il suffit généralement de rajouter un ''circuit de saturation'', qui prend en entrée le résultat et le corrige en cas de débordement. Ce circuit de saturation met la valeur maximale en sortie si un débordement survient, mais se contente de recopier le résultat du calcul sur sa sortie s'il n'y a pas de débordement. Typiquement, il est composé d'une couche de multiplexeurs, qui sélectionnent quelle valeur mettre sur la sortie : soit le résultat du calcul, soit le plus grand nombre entier géré par le processeur, soit le plus petit (pour les nombres négatifs/soustractions).
L'arithmétique saturée est utilisée pour les additions et soustractions, mais c'est plus rare pour les multiplications/divisions. Une des raisons est que le résultat d'une addition/soustraction prend un bit de plus que le résultat, là où les multiplications doublent le nombre de bits. Quand une addition déborde, le résultat réel est proche de la valeur maximale codable. mais quand une multiplication déborde, le résultat peut parfois valoir 200 à 60000 fois plus que la valeur maximale codable. Les calculs avec une valeur saturée/corrigée sont donc crédibles pour une suite d'additions, mais pas pour une suite de multiplications.
===La détection des débordements entiers===
Quand un débordement d'entier a eu lieu, il vaut mieux que l'additionneur prévienne ! Pour cela, l'additionneur a une '''sortie de débordement''', parfois nommée ''Overflow'', dont la valeur indique si l'addition a généré un débordement d'entier ou non. Reste que détecter un débordement ne se fait pas de la même manière selon que l'on parle d'un additionneur non-signé ou signé.
Pour les additionneur non-signés, l'additionneur calcule un bit de plus que ce qui est supporté par l'ordinateur. Par exemple, un additionneur 32 bits fournit un résultat sur 33 bits, un débordement d'entier a lieu quand le 33ème bit est à 1. Précisément, la sortie de débordement n'est autre que la retenue finale, celle fournie par le dernier additionneur complet. Le seul type de débordement possible est un débordement par le haut, où le résultat dépasse la valeur maximale. Avec l'arithmétique saturée, le circuit de saturation consiste en une seule couche de multiplexeurs, voire en un circuit de mise à la valeur maximale tel que vu dans le chapitre sur les opérations bits à bits.
[[File:Gestion des débordements d'entiers.png|centre|vignette|upright=2.5|Gestion des débordements d'entiers lors d'une addition non-signée.]]
Pour les additionneurs non-signés, la gestion des débordements d'entiers dépend fortement de la représentation signée. Nous allons étudier le cas du complément à deux. Si vous vous rappelez le chapitre 1, les calculs sur des nombres en complètement à deux utilisent les règles de l'arithmétique modulaire, c'est une condition nécessaire. À priori, on peut penser que dans ces conditions, les débordements d'entiers sont une chose parfaitement normale, qui nous permet d'avoir des résultats corrects. Néanmoins, certains débordements d'entiers peuvent survenir malgré tout et produire des bugs assez ennuyeux.
Si l'on tient en compte les règles du complément à deux, on sait que le bit de poids fort (le plus à gauche) permet de déterminer si le nombre est positif ou négatif : il indique le signe du nombre. Tout se passe comme si les entiers en complément à deux étaient codés sur un bit de moins, et avaient leur longueur amputé du bit de poids fort. Si le résultat d'un calcul écrase le bit de poids fort, il y a un débordement d'entiers. Il existe une règle simple qui permet de détecter ces débordements d'entiers. L'addition de deux nombres positifs ne peut pas être un nombre négatif. Si on additionne deux nombres dont le bit de signe est à 0 et que le bit de signe du résultat est à 1, on est en face d'un débordement d'entiers. Même chose pour deux nombres négatifs : le résultat de l'addition ne peut pas être positif. On peut résumer cela en une phrase : si deux nombres de même signe sont ajoutés, un débordement a lieu quand le bit du signe du résultat a le signe opposé.
Modifier les circuits d'au-dessus pour qu'ils détectent les débordements en complément à deux est simple comme bonjour : il suffit créer un petit circuit combinatoire qui prenne en entrée les bits de signe des opérandes et du résultat, et qui fasse le calcul de l'indicateur de débordements. Si l'on rédige sa table de vérité, on doit se retrouver avec la table suivante :
{|class="wikitable"
|-
!Entrées
!Sortie
|-
|000||0
|-
|001||1
|-
|010||0
|-
|011||0
|-
|100||0
|-
|101||0
|-
|110||1
|-
|111||0
|}
L'équation de ce circuit est la suivante, avec <math>S_a</math> et <math>S_b</math> les signes des deux opérandes, et <math>C_i</math> la retenue de la colonne précédente :
: <math> ( S_a . S_b . \overline{C_i} ) + ( \overline{S_a} . \overline{S_b} . C_i )</math>
En simplifiant, on obtient alors :
: <math>( S_a . S_b ) \oplus C_i</math>
Or, il se trouve que <math>S_a . S_b</math> est tout simplement la retenue en sortie du dernier additionneur, que nous noterons <math>C_o</math>. On trouve donc :
: <math>C_o \oplus C_i</math>
Il suffit donc de faire un XOR entre la dernière retenue et la précédente pour obtenir le bit de débordement.
<noinclude>
{{NavChapitre | book=Fonctionnement d'un ordinateur
| prev=Les circuits de décalage et de rotation
| prevText=Les circuits de décalage et de rotation
| next=Les circuits de comparaison
| nextText=Les circuits de comparaison
}}
</noinclude>
sim41b7mxu55jij1hzmosxa00b47u2f
768769
768768
2026-06-27T12:40:10Z
Mewtow
31375
/* Le soustracteur à propagation de retenue */
768769
wikitext
text/x-wiki
Dans ce chapitre, nous allons voir les circuits capables de faire une addition ou une soustraction, ainsi que quelques circuits spécialisés. Précisons cependant que les fabricants de processeurs travaillent d'arrache-pied pour trouver des moyens de rendre ces circuits de calcul plus rapides et plus économes en énergie. Autant vous dire que les circuits que vous allez voir sont vraiment des circuits qui font pâle figure comparé à ce que l'on peut trouver dans un vrai processeur commercial !
==Les circuits pour additionner 2 ou 3 bits==
L'addition se fait en binaire de la même manière qu'en décimal. On additionne les chiffres/bits colonne par colonne, une éventuelle retenue est propagée à la colonne d'à côté. La soustraction fonctionne sur le même principe, sur le même modèle qu'en décimal.
[[File:Binary Addition Demonstration.svg|centre|vignette|Exemple d'addition en binaire.]]
En clair, additionner deux nombres demande d'additionner 2 bits et une retenue sur chaque colonne, et de propager les retenues d'une colonne à l'autre. La propagation des retenues est quelque chose de simple en apparence, mais qui est sujet à des optimisations extraordinairement nombreuses. Aussi, pour simplifier l'exposition, nous allons voir comment gérer une colonne avant de voir comment sont propagées les retenues.
[[File:Full Adder Block.svg|vignette|upright=1|class=transparent|Additionneur complet.]]
Si on effectue une addition en colonne, on doit additionner les deux bits sur la colonne, mais aussi additionner une éventuelle retenue. Il faut donc créer un circuit qui additionne trois bits : deux bits de données, plus une retenue. Ce circuit qui additionne trois bits est appelé un '''additionneur complet'''. Il fournit en sortie deux bits : un bit de somme et une retenue sortante. Voici sa table de vérité :
{|class="wikitable"
|-
! Retenue entrante !! Opérande 1 !! Opérande 2 !! !! Retenue sortante !! Bit de somme
|-
||0||0||0|| ||0||0
|-
||0||0||1|| ||0||1
|-
||0||1||0|| ||0||1
|-
||0||1||1|| ||1||0
|-
||1||0||0|| ||0||1
|-
||1||0||1|| ||1||0
|-
||1||1||0|| ||1||0
|-
||1||1||1|| ||1||1
|}
Il est possible d'utiliser un tableau de Karnaugh pour traduire la table de vérité, mais elle donne un résultat assez compliqué. La retenue et le bit de somme sont calculés à part. La retenue est calculée avec trois portes ET et une porte OU. Le bit de somme est calculé lui avec 4 portes ET, une porte OU, et trois portes NON.
[[File:Full Adler with 3 entries.png|centre|vignette|upright=2|Additionneur complet, fabriqué à partir de la table de vérité.]]
D'autres méthodes donnent des résultats plus compréhensibles. Nous allons les voir dans la suite de cette section.
===L'additionneur complet basé sur des demi-additionneurs===
[[File:1-bit half-adder.svg|class=transparent|vignette|Demi-addtionneur.]]
Nous avons déjà vu comment additionner deux bits dans le chapitre sur les incrémenteurs, mais quelques rappels ne seront pas de trop. Le '''demi-additionneur''' est un circuit qui additionne deux bits. Il implémente la table d'addition, qui est très simple en binaire. Jugez plutôt :
* 0 + 0 = 0, retenue = 0 ;
* 0 + 1 = 1, retenue = 0 ;
* 1 + 0 = 1, retenue = 0 ;
* 1 + 1 = 0, retenue = 1.
Mais un demi-additionneur n'additionne que deux bits, il manque de quoi additionner la retenue. Une solution pour cela est d'enchaîner deux demi-additionneurs : un qui additionne les deux bits de données, et un second qui additionne la retenue au résultat du précédent. La retenue finale se calcule en combinant les sorties de retenue des deux demi-additionneurs, avec une porte OU. Le résultat est un additionneur complet fabriqué avec deux demi-additionneurs. Pour vous en convaincre, établissez la table de vérité de ce circuit, vous verrez que ça marche.
[[File:Full Adder Blocks.svg|centre|vignette|upright=2|Additionneur complet fabriqué avec deux demi-additionneurs.]]
[[File:Half-adder.svg|vignette|upright=1|Circuit d'un demi-additionneur naïf.]]
L'implémentation naïve d'un demi-additionneur utilise une porte XOR et une porte ET, comme illustré ci-contre. Le circuit s'obtient en faisant uen traduction littérale de la table de vérité du circuit. Si je dis qu'elle est naïve, c'est car il est possible de l'optimiser, de manière à éliminer des redondances cachées. Mais nous avions déjà vu cela dans le chapitre sur les incrémenteurs, aussi laissons cela pour plus tard.
En combinant deux demi-additionneurs de ce type, on obtient l'additionneur complet suivant. Les deux sorties de retenue des demi-additionneurs sont combinées avec une porte OU, pour calculer la retenue finale.
{| class="flexible"
|[[File:Full Adder Modules.svg|class=transparent|Composition d'un additionneur complet. On voit bien que celui-ci est composé de deux demi-additionneurs, en rouge et en bleu, auxquels on a ajouté une porte OU pour calculer la retenue finale.]]
|[[File:Full-adder.svg|300px|class=transparent|Circuit d'un additionneur complet.]]
|}
Le fait de combiner les deux retenues avec une porte OU n'est pas ce qu'il y a de plus intuitif. La table de vérité nous dit que ça fonctionne, mais on comprend mal pourquoi. Mais tout devient plus clair quand on sait que les deux retenues ne peuvent pas être à 1 en même temps. Même dans l'addition 1 + 1 + 1, seule une retenue est à 1. Soit l'addition des deux bits d'opérande donne naissance à une retenue, soit c'est l'addition ''résultat + retenue entrante''. La retenue sortant est donc à 1 quand une des deux addition donne une retenue. D'où l'usage d'une porte OU. Remarquez qu'une porte XOR donne le même résultat, vu que le cas où les deux retenues sont à 1 n'est jamais rencontré.
===La propagation et la génération des retenues===
L'additionneur complet que nous allons voir dans cette section sert à introduire les concepts de propagation, génération et absorption de retenue. Il s'agit de concepts très importants quand on étudie les additionneurs. l'idée part d'un principe très simple : la retenue sortante dépend de la retenue d'entrée. La relation entre les deux se résume à trois cas, qui dépendent de la valeur des deux bits additionnés, nommés A et B.
* Dans le premier cas, la retenue entrante est égale à la retenue sortante. On dit que la retenue entrante est propagée par l'additionneur.
* Dans le second cas, la retenue est forcée à 0 : la retenue sortante vaut 0, peu importe la valeur de la retenue entrante.
* Dans le troisième cas, la retenue est forcée à 1 : la retenue entrante vaut 1, peu importe la valeur de la retenue entrante.
[[File:Signaux P et G fournis par un demi-additionneur.png|vignette|Signaux P et G fournis par un demi-additionneur]]
Maintenant, créons un circuit qui nous dise si une retenue est propagée ou générée. Il a deux bits de sortie, nommés P et G : P pour ''Propagate'', G pour ''Generate''. Le bit P est à 1 si la retenue entrante est propagée, il est à 0 sinon. Le bit G est à 1 si une retenue est générée, à 0 sinon. Une retenue est considérée comme absorbée si elle n'est pas ni propagée ni générée, pas besoin d'un troisième bit pour gérer ce cas.
Il se trouve que ce circuit n'est autre qu'un demi-additionneur ! Pour vous en rendre compte, regardez la table de vérité d'un additionneur complet, illustrée ci-dessous. Lorsque les deux bits d'opérande sont à 0, la retenue sortante vaut toujours 0. Si ils sont tous deux à 1, alors la retenue sortante vaut 1. S'ils sont différents, alors retenues sortante et entrante sont égales. Le bit P est donc généré par une simple porte XOR. Quant au bit G, il est à 1 si les deux bits d'opérandes sont à 1, ce qui correspond à une porte ET. Il se trouve que ces deux portes forment un demi-additionneur !
{|class="wikitable"
|-
! Opérande 1 !! Opérande 2 !! Retenue entrante !! !! Retenue sortante
|- class="f_rouge"
||0||0||0|| ||0
|- class="f_rouge"
||0||0||1|| ||0
|- class="f_bleu"
||0||1||0|| ||0
|- class="f_bleu"
||0||1||1|| ||1
|- class="f_bleu"
||1||0||0|| ||0
|- class="f_bleu"
||1||0||1|| ||1
|- class="f_vert"
||1||1||0|| ||1
|- class="f_vert"
||1||1||1|| ||1
|}
Ensuite, créons un circuit qui prend ces deux sorties P et G, et calcule la retenue sortante en fonction. Le circuit en question a trois entrées : la retenue entrante, les deux bits P et G. La retenue sortante vaut 1 soit si une retenue est générée, soit si la retenue entrante est propagée et qu'elle vaut 1. En notant <math>{C_{in}}</math> et <math>{C_{out}}</math> les retenues entrantes et sortante, on a :
: <math>{C_{out}} = G + \left( P . C_{in} \right)</math>
Le circuit est donc composé d'une porte OU et d'une porte ET. En combinant un demi-additionneur avec le circuit de calcul de retenue sortante vu plus haut, on a :
[[File:Additionneur complet avec propagation et génération de retenue.png|class=transparent|centre|vignette|upright=2|Additionneur complet avec propagation et génération de retenue.]]
Certes, on retombe sur le circuit vu plus haut. Mais il est possible de remplacer le circuit en orange par un autre. Par exemple, il est possible de le remplacer par multiplexeur, qui choisit entre la retenue générée et la retenue entrante (propagée). Le choix se fait selon la valeur du bit P, qui chosiit entre propager la retenue et la générer.
[[File:Additionneur crée avec un multiplexeur.png|centre|vignette|upright=1.5|Additionneur crée avec un multiplexeur]]
Le circuit exact est illustré ci-dessous. Il semble utiliser plus de portes logiques que l'additionneur complet précédent. Cependant, nous verrons dans quelques chapitres qu'il est possible d'implémenter un multiplexeur avec seulement 6 transistors, voire moins ! L'implémentation utilise des portes à transmission, mais nous en reparlerons dans le chapitre sur les transistors, quand nous verrons les additionneurs à ''Manchester Carry Chain''. Au passage, une variante de ce circuit a été utilisée dans le processeur processeur 8086 d'Intel, comme on le verra dans le chapitre suivant.
[[File:Additionneur complet basé sur un MUX.png|centre|vignette|upright=1|Additionneur complet basé sur un MUX]]
[[File:FulladderPG.png|vignette|upright=1|Additionneur complet avec deux sorties P et G.]]
Pour finir, sachez qu'il existe des additionneurs qui fournissent : le bit de résultat, deux sorties P et G qui indiquent si l'addition propage ou génère une retenue. Il ne s'agit pas d'additionneurs complets, car il manque de quoi calculer la retenue sortante. De tels additionneurs seront appelés des '''additionneurs partiels'''. De tels additionneurs sont utilisés dans certains additionneurs pour gagner en performance.
Enfin, il existe aussi ce que j'ai décidé d'appeler des '''additionneurs P/G''', qui sont des additionneurs complets auxquels on a ajouté deux sorties P et G, en plus de la sortie de retenue. Leur circuit est le même que celui d'un additionneur complet, auquel on a ajouté deux fils. Vu que ces deux sorties sont fournies par le premier demi-additionneur, ajouter ces deux sorties demande d'ajouter des fils, pas de portes logiques. De tels additionneurs sont aussi utilisés dans certains additionneurs pour gagner en performance.
[[File:FAwithGP.svg|centre|vignette|upright=2|Additionneur complet avec sorties P et G : circuit.Ç]]
===L'additionneur complet basé sur une porte à majorité===
Maintenant, voyons une dernière implémentation possible de l'additionneur complet. Mais avant de voir comment l'additionneur est implémenté, nous allons devoir faire un petit focus sur la retenue sortante. Reprenons la table de vérité, et regardons ce qu'il en est pour la retenue sortante uniquement.
{|class="wikitable"
|-
! Retenue entrante !! Opérande 1 !! Opérande 2 !! !! Retenue sortante
|-
||0||0||0|| ||0
|-
||0||0||1|| ||0
|-
||0||1||0|| ||0
|-
||0||1||1|| ||1
|-
||1||0||0|| ||0
|-
||1||0||1|| ||1
|-
||1||1||0|| ||1
|-
||1||1||1|| ||1
|}
Vous remarquerez que la retenue sortante est égale au bit majoritaire, parmi les trois bits d'opérande. Si deux ou trois bits sont à 1, la retenue sortante vaut 1. Et réciproquement avec 0. Le circuit de calcul de la retenue peut donc être remplacé par une '''porte à majorité'''.
[[File:Additionneur crée avec une porte à majorité.png|centre|vignette|upright=2|Additionneur crée avec une porte à majorité]]
Mais une porte à majorité est assez gourmande en circuit, ce qui fait qu'un additionneur pareil ne serait pas pratique, ni utile. Du moins, ce serait le cas s'il n'y avait pas une possibilité d'optimisation extrêmement intéressante. Il est possible de calculer le 'bit de somme' à partir de la retenue sortante ! En effet, le bit de somme est l'inverse de la retenue sortante, sauf dans deux cas : les trois bits d'entrée sont à 0, où ils sont tous à 1. Le bit de somme vaut 0 dans le premier cas, 1 dans le second cas.
{|class="wikitable"
|-
! Retenue entrante !! Opérande 1 !! Opérande 2 !! !! Retenue sortante !! Bit de somme
|- class="f_bleu"
||0||0||0|| ||0||0
|- class="f_vert"
||0||0||1|| ||0||1
|- class="f_vert"
||0||1||0|| ||0||1
|- class="f_vert"
||0||1||1|| ||1||0
|- class="f_vert"
||1||0||0|| ||0||1
|- class="f_vert"
||1||0||1|| ||1||0
|- class="f_vert"
||1||1||0|| ||1||0
|- class="f_rouge"
||1||1||1|| ||1||1
|}
En clair, 6 lignes de la table de vérité sur 8 peuvent se calculer avec une porte à majorité, le reste demandant quelques portes logiques pour faire la correction. L'implémentation en circuit demande donc trois choses :
* de calculer la retenue sortante et de quoi l'inverser ;
* un circuit qui vérifie si tous les bits opérande valent 0 : une porte NOR ou OU fait l'affaire ;
* un circuit qui vérifie s'ils valent tous 1, à savoir une porte ET.
Ensuite, on combine le résultat des trois circuits précédents pour obtenir le résultat final. La combinaison est le fait du circuit en jaune dans le schéma ci-dessous.
[[File:Full adder basé sur une modification de la retenue.png|centre|vignette|upright=2|Full adder basé sur une modification de la retenue]]
Le circuit en jaune est assez simple. Quand la porte ET sort un 1, la sortie doit être forcée à 1. Par contre, quand la porte OU sort un zéro, la sortie doit être forcée à 0. Il suffit donc d'enchainer un circuit de mise à 1 et un circuit de mise à 0. Nous avons vu dans le chapitre sur les opérations de masquage qu'il s'agit respectivement d'une porte OU et d'une porte ET.
[[File:Full adder basé sur une modification de la retenue interne, au complet.png|centre|vignette|upright=2|Full adder basé sur une modification de la retenue interne, au complet]]
Divers processeurs utilisaient des additionneurs complets de ce type : le fameux Z80, l'Intel 4004 et de l'Intel 8008 sont les exemples les plus notables. Pourtant, on pourrait s'étonner qu'un tel circuit ait existé. Il utilise beaucoup de portes logiques, a une profondeur logique supérieure : il n'a rien d'avantageux. Sauf qu'il était utilisé sur d'anciens processeurs, qui utilisaient la technologie dite TTL, différente de la technologie CMOS des transistors modernes. Et avec la technologie TTL, il est possible de fusionner plusieurs portes logiques ET et NOR en une seule porte logique ET/OU/NON ! Un additionneur complet construit ainsi ne prenait que deux portes logiques : une pour le calcul de la retenue sortante, une autre pour le reste du circuit.
==L'addition non signée==
Voyons maintenant un circuit capable d'additionner deux nombres entiers: l''''additionneur'''. Dans la version qu'on va voir, ce circuit manipulera des nombres strictement positifs. L'addition des nombres codés en complètement à deux sera vu dans une section ultérieure.
[[File:Full-adder-4bits-serial-CEI.svg|vignette|upright=0.5|Additionneur 4 bits, un bloc.]]
L'interface d'un additionneur est illustrée ci-contre. Un additionneur prend deux opérandes sur deux entrées séparées, et fournit le résultat sur une sortie dédiée. De plus, il a une entrée sur laquelle envoyer une retenue entrante, et a une sortie pour la retenue sortante. La sortie de retenue indique que le résultat de l'addition a débordé (au sens d'un débordement d'entier). Pour l'entrée de retenue, son utilité deviendra plus claire dans ce qui suit, mais souvenez-vous qu'elle existe.
L'entrée et la sortie de retenue sont parfois utilisées pour combiner plusieurs petits additionneurs, pour former un additionneur plus grand. Nous verrons de nombreux exemples dans ce qui suit. Il est assez commun de créer des additionneurs pour des opérandes de 16/32/64 bits, avec des additionneurs de 4/5 bits.
===L'additionneur série===
Il est possible d'additionner deux nombres bit par bit, grâce à un additionneur complet associé à plusieurs registres à décalages. Les opérandes sont placées chacune dans un registre à décalage, et l'additionneur complet utilise les bits sortants de ces registres à décalage. Le bit du résultat est envoyé au registre à décalage pour le résultat. La retenue de l'addition est stockée dans une bascule de 1 bit, en attente du prochain cycle d'horloge. Un tel additionneur est appelé un '''additionneur série'''. Il a été utilisé sur d'anciens ordinateurs dans les années 50-60, aussi bien des prototypes que des ordinateurs commerciaux.
[[File:Additionneur série.jpg|centre|vignette|upright=2.5|Additionneur série.]]
===L'additionneur à propagation de retenue===
L''''additionneur à propagation de retenue''' est aussi appelé l'additionneur ''ripple carry'', c'est son nom anglais. J'utiliserais occasionnellement le nom anglais dans ce qui suit, car celui-ci est plus court. Ilutilise un additionneur complet pour chaque colonne de l'addition. Pour gérer les retenues, il suffit ainsi de câbler des additionneurs complets les uns à la suite des autres, chacun envoyant sa retenue à la colonne suivante.
[[File:Ripplecarryadder.png|centre|vignette|upright=2.5|Additionneur à propagation de retenue.]]
Notons la présence de la retenue sortante, qui est utilisée pour détecter les débordements d'entier, ainsi que pour d'autres opérations. Le bit de retenue final est souvent stocké dans un registre spécial du processeur (généralement appelé ''carry flag'').
Notez aussi, à droite du schéma précédent, l'entrée de retenue <math>\text{R}_\text{Entrée}</math>. Elle est très utile pour l'implémentation de certaines opérations comme l'inversion de signe, la soustraction, l'incrémentation, etc. Certains processeurs sont capables de faire une opération appelée ADC, ADDC ou autre nom signifiant ''Addition with Carry'', qui permet de faire le calcul A + B + Retenue (la retenue en question est la retenue sortante de l'addition précédente, stockée dans le registre ''carry flag''). Son utilité principale est de permettre des additions d'entiers plus grands que ceux supportés par le processeur. Par exemple, cela permet de faire des additions d'entiers 32 bits sur un processeur 16 bits.
[[File:Adder Network 3 Bit Sum Delay 003.svg|vignette|upright=0.5|Propagation de retenue dans l'additionneur.]]
Cet additionneur utilise très peu de portes logiques, ce qui fait qu'il était utilisé sur certains processeurs 8 et 16 bits, dont le budget en portes logiques était limité. Bien que très simple, cet additionneur est cependant peu performant. Son temps de calcul est proportionnel à la taille des opérandes. Par exemple, additionner deux opérandes de 32 bits prendra deux fois plus de temps que pour des opérandes de 16 bits. La raison est que les retenues doivent se propager à travers le circuit, du premier additionneur jusqu'au dernier.
L'addition étant une opération fréquente, d'autres additionneurs ont été inventés. Ils utilisent des optimisations qui utilisent plus de circuits pour gagner en rapidité. Mais avant de voir ces optimisations, nous allons voir s'il est possible d'optimiser les additionneurs à propagation de retenue.
===L'additionneur à saut de retenue===
L'additionneur à propagation de retenue peut être optimisé de deux manières. La première accélère la propagation de la retenue, en agissant au niveau des additionneurs complets. La ''Manchester carry chain'' est une optimisation de ce type, mais on ne peut pas encore expliquer à ce stade du cours, car elle optimisait les additionneurs complets au niveau des transistors eux-mêmes. Elle a été utilisée sur de nombreux processeurs connus, comme le 8086 d'Intel ou le Z80.
La seconde manière rend l'addition plus rapide dans certains cas. Le résultat est un additionneur dont le temps de calcul est variable. Le calcul prendra quelques cycles d'horloges avec certains opérandes, mais d'autres opérandes ne verront aucune amélioration. Il n'améliore pas le pire des cas, dans lequel la retenue doit être propagée du début à la fin, du bit de poids faible au bit de poids fort. Mais dans d'autres cas, il permet d'avoir le résultat en avance. L'optimisation en question donne l''''additionneur à saut de retenue''' (''carry-skip adder'').
Il est composé en enchainant plusieurs additionneurs plus petits, qui additionnent 4/5 bits, rarement plus. De tels additionneurs de petite taille sont appelés des '''blocs''', dans ce qui suit. L'idée est de détecter précocement si la retenue entrante est propagée à travers tout le bloc. Si la retenue est propagée dans tous le bloc, on peut directement l'envoyer sur la sortie de retenue, la retenue saute le bloc entier. Dans le cas contraire, on doit calculer la retenue normalement. Le choix entre les deux est le fait d'un multiplexeur. Toute la difficulté est de savoir comment commander le multiplexeur.
[[File:Carry skip adder, principe de base.png|centre|vignette|upright=2.5|Carry skip adder : principe de base]]
Pour savoir si une retenue est propagée dans un bloc entier, il faut utiliser des additionneurs complets, qui ont en plus une sortie P indiquant que l'additionneur propage la retenue. Le bloc propage une retenue si chaque additionneur complet propage la retenue, donc s'ils ont tous leur sortie P à 1. Un simple ET logique suffit à déterminer si c'est le cas. Le signal de commande du multiplexeur est donc un vulgaire ET entre toutes les sorties P des additionneurs complets du bloc.
[[File:Additionneur à saut de retenue.png|centre|vignette|upright=2.5|Calcul de la commande du MUX.]]
Voici ce que ça donne pour un bloc de 4 bits :
[[File:CSAdder4Bit.svg|centre|vignette|upright=1.5|Additionneur ''carry skip'' : bloc de 4 bits.]]
L'additionneur à saut de retenue est construit en assemblant plusieurs blocs de ce type.
[[File:BCSAdder16Bit.svg|centre|vignette|upright=3|Additionneur à saut de retenue.]]
Les blocs sont tous identiques dans le cas le plus simple, mais il est possible d'utiliser des blocs de taille variable. Par exemple, le premier bloc peut avoir des opérandes de 6 bits, le second des opérandes de 7 bits, etc. Faire ainsi permet de gagner un petit peu en performances, si la taille de chaque bloc est bien choisie. La raison est une question de temps de propagation des retenues. La retenue met plus de temps à se propager à travers 8 blocs qu'à travers 4, ce qui prend plus de temps qu'à travers 2 blocs, etc. En tenir compte fait que la taille des blocs tend à augmenter ou diminuer quand on se rapproche des bits de poids fort.
===L'additionneur à sélection de retenue===
L''''additionneur à sélection de retenue''' utilise aussi des blocs, comme les additionneurs précédents. L'addition se fait en deux versions : une avec la retenue du bloc précédent valant zéro, et une autre version avec la retenue du bloc précédent valant 1. Il suffira alors de choisir le bon résultat avec un multiplexeur, une fois cette retenue connue. On gagne ainsi du temps en calculant à l'avance les valeurs de certains bits du résultat, sans connaître la valeur de la retenue. Petit détail : sur certains additionneurs à sélection de retenue, les blocs de base n'ont pas la même taille. Cela permet de tenir compte des temps de propagation des retenues entre les blocs.
[[File:Additionneur à sélection de retenue.png|centre|vignette|upright=1.5|Additionneur à sélection de retenue avec seulement deux blocs.]]
Dans les exemples du dessus, chaque sous-additionneur étaient des additionneurs à propagation de retenue. Mais ce n'est pas une obligation, et tout autre type d’additionneur peut être utilisé. Par exemple, on peut faire en sorte que les sous-additionneurs soient eux-mêmes des additionneurs à sélection de retenue, et poursuivre ainsi de suite, récursivement. On obtient alors un '''additionneur à somme conditionnelle''', plus rapide que l'additionneur à sélection de retenue, mais qui utilise beaucoup plus de portes logiques.
===Les additionneurs à anticipation de retenue===
Les '''additionneurs à anticipation de retenue''' calculent chaque retenue sans avoir à propager les retenues précédentes. Au lieu de calculer les retenues une par une, ils calculent toutes les retenues en parallèle, à partir des bits des opérandes. Une fois les retenues pré-calculées, il suffit de les additionner avec les deux bits adéquats, pour obtenir le résultat. Ces additionneurs sont composés de deux parties :
* un circuit qui pré-calcule les retenues, sans les propager, directement à partir des opérandes ;
* d'une couche d'additionneurs complets, qui additionnent chacun deux bits d'opérande et la retenue pré-calculée.
[[File:Additionneur à anticipation de retenue.png|centre|vignette|upright=1.5|Additionneur à anticipation de retenue.]]
Le calcul des retenues se fait en deux étapes. La première détermine si les retenues sont propagées ou générées, pour chaque colonne. Elle détermine, pour chaque colonne, les bits P et G qui indiquent respectivement : qu'une retenue a été générée sur cette colonne, que la colonne propage la retenue précédente. La seconde étape utilise ces signaux P et G pour déterminer la retenue sur chaque colonne. Le circuit de calcul des retenues est donc composé de deux parties : une couche de demi-additionneurs pour générer les signaux P et G, un circuit de calcul des retenues proprement dit.
[[File:Circuit complet d'un additionneur à anticipation de retenue.png|centre|vignette|upright=1.5|Circuit complet d'un additionneur à anticipation de retenue.]]
Pour rappel, la retenue de la colonne i, notée <math>C_i</math> est égale à :
: <math>G_i + (P_i . C_{i-1})</math>, avec G le signal de génération de retenue, P le signal de propagation de retenue.
Si on utilisait cette formule sans trop réfléchir, on retomberait sur un additionneur à propagation de retenue inutilement compliqué. L'astuce des additionneurs à anticipation de retenue consiste à remplacer le terme Ci−1 par sa valeur calculée avant. Par exemple, je prends un additionneur 4 bits. Je dispose de deux nombres A et B, contenant chacun 4 bits : A3, A2, A1, et A0 pour le nombre A, et B3, B2, B1, et B0 pour le nombre B. Si j'effectue les remplacements, j'obtiens les formules suivantes :
* C1 = G0 + ( P0 · C0 ) ;
* C2 = G1 + ( P1 · G0 ) + ( P1 · P0 · C0 ) ;
* C3 = G2 + ( P2 · G1 ) + ( P2 · P1 · G0 ) + ( P2 · P1 · P0 · C0 ) ;
* C4 = G3 + ( P3 · G2 ) + ( P3 · P2 · G1 ) + ( P3 · P2 · P1 · G0 ) + ( P3 · P2 · P1 · P0 · C0 ).
Ces formules nous permettent de déduire la valeur d'une retenue directement : il reste alors à créer un circuit qui implémente ces formules, et le tour est joué.
[[File:Four bit adder with carry lookahead.svg|centre|vignette|upright=1.5|Additionneur à anticipation de retenue de 4 bits.]]
Plus haut, j'ai dit que le circuit était composé de trois couches : une couche de demi-additionneur pour les signaux P et G ; un circuit d'anticipation de retenue, puis l'addition finale. Mais les deux bits d'opérandes sont déjà additionnés dans la couche de demi-additionneur, autant réutiliser le résultat pour l'additionner à la retenue. Pour éliminer toute redondance de ce type, il est possible d'utiliser des additionneurs partiels (des additionneurs où la sortie de retenue a été remplacée par deux sorties P et G). L'additionneur final est alors composé de plusieurs additionneurs complets de ce type, couplé à un circuit d'anticipation de retenue.
[[File:Cla4bits.png|centre|vignette|upright=2.5|Additionneur à anticipation de retenue de 4 bits.]]
Les additionneurs à anticipation de retenue sont plus rapides que les additionneurs à propagation de retenue. La raison est que les retenues sont calculées rapidement, dans un temps plus petit qu'en propageant la retenue. Pour rentrer dans les détails mathématiques, le temps de calcul n'est pas proportionnel au nombre de bits, mais proportionnel au logarithme de ce nombre de bits. Par contre, le gain en temps de calcul s'accompagne d'un cout en portes logiques conséquent. Le circuit qui calcule les retenues anticipées utilise beaucoup de portes logiques.
===Les additionneurs à calcul parallèle de préfixe===
Du fait du cout en portes logiques, utiliser un additionneur à anticipation de retenue est une solution pratique pour additionner 4 ou 8 bits. Mais pour des opérandes plus grands, de 16 ou 32 bits, cela utiliserait trop de portes logiques. Une partie de ce cout en portes logiques est cependant lié à des redondances. Si on analyse deux retenues différentes, à des colonnes différentes. Les circuits qui calculent ces deux retenues seront différents, mais ils contiendront des sous-circuits identiques. Pour vous en rendre compte, reprenons les quatre équations obtenues précédemment et essayez de trouver des redondances :
* C1 = G0 + ( P0 · C0 ) ;
* C2 = G1 + ( P1 · G0 ) + ( P1 · P0 · C0 ) ;
* C3 = G2 + ( P2 · G1 ) + ( P2 · P1 · G0 ) + ( P2 · P1 · P0 · C0 ) ;
* C4 = G3 + ( P3 · G2 ) + ( P3 · P2 · G1 ) + ( P3 · P2 · P1 · G0 ) + ( P3 · P2 · P1 · P0 · C0 ).
Vous devriez trouver :
* le terme ''P0 · C0'' dans toutes les lignes ;
* les termes ''P1 · P0 · C0'' et ''P1 · P0'' dans les trois dernières lignes ;
* le terme ''P2 · P1 · P0 · C0'' ; ''P2 · P1 · G0'' et ''P2 · G1'' dans les deux dernières lignes.
Il existe plusieurs manières d'exploiter de telles redondances, exploitées dans les '''additionneurs à calcul parallèle de préfixe'''. L'optimisation apportée est de générer des signaux ''propagate'' et ''generate'' pour des groupes de 2, 3, 4, ..., N colonnes. Par exemple, il est possible de générer un signal ''P 0 vers 3'', qui indique si la retenue de la première colonne est propagée jusqu'à la 4ème colonne. Un autre exemple est un signal qui indique qu'une retenue a été générée entre les colonnes 4 à 7 génèrent une retenue ou non. En clair, les signaux P et G ont maintenant un intervalle, qui précise de quelle colonne vers quelle colonne se fait la propagation, ou entre quelles colonnes se fait la génération.
Il est possible de combiner les signaux P et G de deux groupes de colonne, s'ils sont contiguës (ils peuvent aussi se recouvrir). Par exemple, il est possible de calculer les bits P et G pour les colonnes 0 à 10, à partir des deux signaux P/G des colonnes 0-4 et 5-10. Pour cela, les équations sont assez simples. Si je prends deux groupes nommés A et B, avec A pour les colonnes de poids fort et B celles de poids faible, on a :
: <math>P_{sortie} = P_A . P_B</math>
: <math>G_{sortie} = G_A . P_A . G_B</math>
Pour les bits des colonnes 2, 4, 8, 16, 32, 64, et autres, le calcul est simple. L'idée est de grouper les colonnes par groupes de 2. Puis, on calcule chaque groupe de 4 bits à partir de deux groupes de 2. Puis les groupes de 8 bits sont calculés à partir de deux groupes de 4 bits, et ainsi de suite pour toute puissance de deux.
[[File:Squelette d'un additionneur à calcul parallèle de préfixe.jpg|centre|vignette|upright=2|Squelette d'un additionneur à calcul parallèle de préfixe]]
Les seules difficultés surviennent pour les colonnes dont le nombre n'est pas une puissance de deux. Il y a plusieurs manières pour subdiviser ces colonnes en intervalles plus petits et combiner le tout. Et elles donnent chacune des additionneurs différents. Ils portent les noms d''''additionneur de Ladner-Fisher''', d''''additionneur de Brent-Kung''', d''''additionneur de Kogge-Stone''', etc. Dans le cas général, l'additionneur de Kogge-Stone est un des plus rapide, mais aussi un des plus gourmand en circuits, alors que c'est l'inverse pour l'additionneur de Brent-Kung. Les deux additionneurs sont illustrés ci-dessous, pour des opérandes de 8 bits. Dans ceux-ci, chaque losange calculent les bits P et G, à partir des signaux P et G de deux groupes précédents.
{|
|[[File:Brent-kung-8-bit.png|vignette|Brent-kung-8-bit]]
|[[File:Kogge-stone-8-bit.png|vignette|upright=1.2|Kogge-stone-8-bit]]
|}
===Le calcul parallèle de la retenue===
L'anticipation de retenue est très rapide, mais utilise beaucoup de circuits, même en utilisant le calcul parallèle de préfixe. Les additionneurs à propagation de retenue font eux le compromis inverse. Mais il existe des intermédiaires, qui visent à obtenir un compromis entre performance et cout en portes logiques.
Un de ces compromis découpe un additionneur de 16/32/64 bits en additionneurs de 4/5 bits, qui sont enchainés en utilisant la propagation de retenue. Par contre, ces petits additionneurs de 4/5 bits utilisent l'anticipation de retenue. Ces additionneurs simples seront nommés ''blocs'' dans ce qui suit. Par exemple, on peut utiliser 4 additionneurs à anticipation de retenue de 4 bits, et propager les retenues entre eux. Le premier bloc calcule une retenue, qui est propagée au second bloc. Le second bloc calcule lui aussi une retenue, qui est propagée au troisième bloc, etc.
[[File:4008 Functional Diagram.svg|vignette|upright=1|4008 Functional Diagram]]
Le gain en performance est significatif, mais il est possible de faire presque aussi rapide, tout en économisant beaucoup de circuits. L'idée est de simplifier les additionneurs 4 bits, afin de ne pas utiliser une anticipation de retenue complète. L'anticipation de retenue n'est utilisée que pour la retenue sortante, alors que les bits de somme sont calculées avec propagation de retenue. La retenue sortante est donc calculée en parallèle de l'addition. Chaque bloc contient, à côté d'un additionneur proprement dit, un circuit qui calcule la retenue sortante. Il existe de nombreuses manières de calculer la retenue sortante.
Le gain en portes logiques est assez significatif. En effet, on se passe de circuits d'anticipation de retenue pour les 4 bits de somme, on ne garde que celui pour la retenue sortante. Le fait d'utiliser la propagation de retenue pour calculer les 4 bits de somme est censé avoir un léger cout en performance, mais il se trouve que ce cout est très faible. En effet, le résultat n'est connu qu'une fois les retenues propagées jusqu'au dernier bloc. Et malgré l'anticipation des retenues, la propagation est assez lente. les additionneurs à propagation de retenue auront terminé leur travail bien avant.
Un exemple est celui de l'additionneur CMOS 4008, un additionneur de 4 bit. Il est composé en trois sections. Une première couche de demi-additionneurs calcule les signaux P et G utilisés à la fois pour l'anticipation de la retenue sortante, que pour calculer les bits de somme. Le circuit de calcul de la retenue sortante utilise l'anticipation de retenue. Le reste du circuit fait l'addition en propageant les retenues.
[[File:CMOS 4008, circuit découpé en sections.png|centre|vignette|upright=1.5|CMOS 4008, circuit découpé en sections]]
===Les additionneurs à anticipation de retenue multi-niveau===
Les additionneurs précédents mixent anticipation et propagation de retenue. Ceux que nous allons voir utilisent des blocs, mais utilisent l'anticipation de retenue entre les blocs. Les blocs utilisés peuvent être un additionneur à propagation de retenue, ou à anticipation de retenue, peu importe. Ce qui compte est que la retenue entrante de chaque bloc est calculée par anticipation de retenue.
Par exemple, il est possible de créer un additionneur 16 bits en utilisant 4 additionneurs/blocs de 4 bits, couplé à un circuit d’anticipation de retenue par bloc. Pour cela, les additionneurs de 4 bits doivent fournir deux sorties P et G. Les deux sorties indiquent que le bloc soit a généré une retenue, soit a propagé la retenue entrante. Par propagé, on veut dire que la retenue entrante du bloc s'est retrouvée sur la sortie de retenue du bloc. C'est le cas si les 4 colonnes propagent la retenue, ce qui signifie que :
: <math>P_\text{4 bits} = P_0 \cdot P_1 \cdot P_2 \cdot P_3</math>
Pour la génération, cela veut dire qu'une colonne a générée une retenue, qui a été propagée jusqu'à la sortie. En clair, pour chaque colonne, il faut que le G = 1, et que les signaux P des colonnes soient à 1. Pour les 4 colonnes, cela donne :
: <math>G_\text{4 bits} = G_3 + G_2 \cdot P_3 + G_1 \cdot P_3 \cdot P_2 + G_0 \cdot P_3 \cdot P_2 \cdot P_1</math>
[[File:Cla4bitsPG.png|centre|vignette|upright=2|Additionneur à anticipation de retenue de 4 bits.]]
Pour combiner 4 blocs de 4 bits, on devra donc combiner 4 bits P et G, avec un circuit d'anticipation de retenue.
[[File:16-bit lookahead carry unit.svg|centre|vignette|upright=2|Additionneur à anticipation de retenue de 16 bits.]]
Il est même possible d'aller plus loin et de combiner 4 additionneurs précédents pour obtenir un additionneur 64 bits.
[[File:64-bit lookahead carry unit.svg|centre|vignette|upright=2|Additionneur à anticipation de retenue de 64 bits.]]
Le résultat est un additionneur à plusieurs niveaux d’anticipation de retenue. Une anticipation de retenue dans un blocs de 4 bits (facultative), entre blocs de 4 bits, entre blocs de 16 bits. La même logique peut être utilisée avec des blocs de taille différente de 4, 16 et 64 bits.
==L'addition signée et la soustraction==
Après avoir vu l'addition, il est logique de passer à la soustraction, les deux opérations étant très proches. Si on sait câbler une addition entre entiers positifs, câbler une soustraction n'est pas très compliqué. De plus, la soustraction permet de faire des additions de nombres signés.
===Le soustracteur à propagation de retenue===
[[File:Half Subtractor Vektor.svg|vignette|Demi-soustracteur.]]
Dans le chapitre sur les incrémenteurs/décrémenteurs, nous avons vu un circuit qui peut soustraire deux bits. Celui-ci est appelé un '''demi-soustracteur'''. Il ressemble beaucoup à un demi-additionneur, les différences se résumant à une porte NON ajoutée pour le calcul de la retenue. Il implémente la table de soustraction, qui est très simple en binaire :
* 0 - 0 = 0 ;
* 0 - 1 = 1 et une retenue ;
* 1 - 0 = 1 ;
* 1 - 1 = 0.
Pour soustraire deux nombres entiers, on peut adapter l'algorithme de soustraction utilisé en décimal, celui que vous avez appris à l'école. Celui-ci ressemble fortement à l'algorithme d'addition : on soustrait les bits de même poids, et on propage éventuellement une retenue sur la colonne suivante. À la différence de l'addition, la retenue est soustraite, et non ajoutée. En clair, une soustraction en binaire demande de faire deux soustraction : pour chaque bit de la première opérande, on soustrait le bit associé de la seconde, puis on soustrait la retenue entrante.
[[File:Algebra1 05 fig014.svg|centre|vignette|Soustraction en binaire, avec les retenues en rouge.]]
Pour cela, comme pour l'additionneur, deux demi-soustracteurs peuvent être combinés pour donner un '''soustracteur complet'''. Le calcul de la retenue se fait en combinant les deux retenues des demi-soustracteurs avec une porte OU.
[[File:FullSubtractor.svg|centre|vignette|upright=2|Soustracteur complet.]]
[[File:Sub Network 3 Bit Step 002.svg|vignette|upright=1|Soustracteur à propagation de retenue pour opérandes de 3 bits.]]
Les soustracteurs complets sont utilisés pour créer des soustracteurs à propagation de retenue ou tout autre circuit soustracteur, sur le même modèle que les additionneurs.
Un défaut est que cela ne fonctionne bien que pour les additionneurs/soustracteurs à propagation de retenue. L'anticipation de retenue et les autres optimisations ne marchent pas de la même manière entre addition et soustraction. Les signaux de propagation et de génération de retenue ne sont pas générés pareil, idem pour la manière de les combiner. Il est toujours possible de créer un soustracteur avec ces techniques, mais aucune mutualisation de circuit ne sera possible. Ce qui n'est pas le cas avec les solutions que nous allons voir dans ce qui suit.
Il est possible de fusionner un additionneur à propagation de retenue et un soustracteur de même type. Pour cela, il suffit de remarquer que les demi-soustracteurs et les demi-additionneurs se ressemblent beaucoup. La seule différence entre les deux est une vulgaire porte NON. En remplaçant cette porte NON par un inverseur commandable, on obtient un circuit qui sert soit de demi-additionneur, soit de demi-soustracteur, suivant ce qu'on envoie sur l'entrée de commande. Ci-dessous est illustré ce que donne ce remplacement pour un soustracteur complet. Le résultat est un '''additionneur/soustracteur à propagation de retenue'''.
[[File:Additionneur-soustracteur complet.png|centre|vignette|upright=2|Additionneur-soustracteur complet]]
===L'additionneur-soustracteur pour opérandes codées en complément à deux===
Étudions le cas de la soustraction en complément à deux, dans l'objectif de créer un circuit soustracteur. Vous savez sûrement que a−b et a+(−b) sont deux expressions équivalentes. Et en complément à deux, − b = not(b) + 1. Dit autrement, a − b = a + not(b) + 1. On pourrait se dire qu'il faut deux additionneurs pour faire le calcul, mais la majorité des additionneurs possède une entrée de retenue pour incrémenter le résultat de l'addition. Un soustracteur en complément à deux est donc simplement composé d'un additionneur et d'un inverseur.
[[File:Additionneur soustracteur.png|centre|vignette|upright=2|Soustracteur en complément à deux.]]
Il est possible de créer un circuit capable d'effectuer soit une addition, soit une soustraction : il suffit de remplacer l'inverseur par un inverseur commandable, qui peut être désactivé. On a vu comment créer un tel inverseur commandable dans le chapitre sur les circuits combinatoires. On peut remarquer que l'entrée de retenue et l'entrée de commande de l'inverseur sont activées en même temps : on peut fusionner les deux signaux en un seul.
[[File:Additionneur-soustracteur en complément à deux.png|centre|vignette|upright=2|Additionneur-soustracteur en complément à deux.]]
Une implémentation alternative est la suivante. Elle remplace l'inverseur commandable par un multiplexeur.
[[File:4-bit ripple carry adder-subtracter.svg|centre|vignette|upright=2|Additionneur-soustracteur en complément à deux, version alternative.]]
===L'additionneur-soustracteur pour opérandes codées en signe-magnitude===
Passons maintenant aux nombres codés en signe-valeur absolue, les deux opérandes étant notées A et B. Suivant les signes des deux opérandes, on a quatre cas possibles : A + B, A − B (B négatif), −A + B (A négatif) et −A − B (A et B négatifs). Une astuce est que le circuit n'a besoin que de calculer A + B et A − B : il peut les inverser pour obtenir − A − B ou B − A. A + B et A − B peuvent se calculer avec un additionneur-soustracteur, reste à corriger le résultat. Il suffit de lui ajouter un inverseur commandable pour obtenir le circuit d'addition finale.
[[File:Additionneur en signe-valeur absolue.png|centre|vignette|upright=2|Additionneur en signe-valeur absolue.]]
Toute la difficulté tient dans le calcul du bit de signe du résultat, quand interviennent des soustractions. Autant l'addition de deux nombres de même signe (A + B et −A − B) ne pose aucun problème, autant les soustractions posent problème (A − B et −A + B). Suivant que <math>A<B</math> ou que <math>A>B</math>, le signe du résultat ne sera pas le même. Déterminer le signe du résultat se fait en regardant les bits de débordement d'entier, comme on le verra plus bas.
===L'additionneur-soustracteur pour opérandes codées en représentation par excès===
Passons maintenant aux nombres codés en représentation par excès. On pourrait croire que ces nombres s'additionnent comme des nombres non-signés, mais ce serait oublier la présence du biais, qui pose problème. Dans les cas de nombres signés gérés avec un biais, voyons ce que donne l'addition de deux nombres :
: <math>( n_1 + biais ) + ( n_2 + biais ) = n_1 + n_2 + 2 \times biais</math>
Or, le résultat correct serait :
: <math>n_1 + n_2 + biais</math>
En effectuant l'addition telle quelle, le biais est compté deux fois. On doit donc le soustraire après l'addition pour obtenir le résultat correct.
Même chose pour la soustraction qui donne ceci :
: <math>(n_1 + biais) - (n_2 + biais) = n_1 - n_2</math>
Or, le résultat correct serait :
: <math> ( n_1 - n_2 ) + biais </math>
Il faut rajouter le biais pour obtenir l'exposant correct.
On a donc besoin de deux additionneurs/soustracteurs : un pour additionner/soustraire les représentations binaires des opérandes, et un autre pour ajouter/retirer le biais en trop/manquant.
==L'additionneur BCD==
Maintenant, voyons un additionneur qui additionne deux entiers au format BCD. Pour cela, nous allons devoir passer par deux étapes. La première est de créer un circuit capable d'additionneur deux chiffres BCD. Ensuite, nous allons voir comment enchaîner ces circuits pour créer un additionneur BCD complet.
===L'additionneur BCD qui fait l'opération chiffre par chiffre===
Nous allons commencer par voir un additionneur qui additionne deux chiffres en BCD, une sorte d'équivalent BCD de l'additionneur complet. Il fournit un résultat sur 4 bits et une retenue qui est mise à 1 si le résultat dépasse 10 (la limite d'un chiffre BCD). Les deux opérandes sont des chiffres BCD codés sur 4 bits et sont additionnés en binaire par un additionneur des plus normaux, similaire à ceux vus plus haut. Le résultat est alors un entier codé en binaire, sur 5 bits, qu'on corrige/convertit pour obtenir un chiffre BCD et une retenue sortante.
Pour corriger le résultat, une idée intuitive serait de prendre le résultat et de faire une division par 10. Le quotient donne la retenue, alors que le reste est le résultat, le chiffre BCD. Mais un circuit diviseur par 10 utilise beaucoup de portes logiques, ce qui ne vaut pas le coup. Une autre méthode détecte si le résultat est égal ou supérieur à 10, ce qui correspond à un "débordement" (on dépasse les limites d'un chiffre BCD). Si le résultat est plus petit que 10, il n'y a rien à faire : le résultat est bon et la retenue est de zéro. Par contre, si le résultat vaut 10 ou plus, il faut corriger le résultat et générer une retenue à 1.
Il faut donc ajouter un circuit qui détecte si le résultat est supérieur à 9, qui calcule directement la retenue. Ce circuit peut se fabriquer simplement à partir de sa table de vérité, ou en utilisant les techniques que nous verrons dans un chapitre ultérieur sur les comparateurs. La solution la plus simple est clairement d'utiliser la table de vérité, ce qui est très simple, assez pour être laissé en exercice au lecteur. Pour comprendre comment corriger le résultat, établissons une table de vérité qui associe le résultat et le résultat corrigé. L'entrée vaut au minimum 10 et au maximum 9 + 9 = 18. On considère la sortie comme un tout, la retenue étant un 5ème bit, le bit de poids fort.
{|class="wikitable"
|-
! colspan="5" | Entrée
!
! rowspan="10" |
! Retenue
! Résultat corrigé (sans retenue)
! interprétation de la sortie en binaire (retenue inclue)
|-
| 0 || 1 || 0 || 1 || 0
| (10)
| 1 || 0000
| (16)
|-
| 0 || 1 || 0 || 1 || 1
| (11)
| 1 || 0001
| (17)
|-
| 0 || 1 || 1 || 0 || 0
| (12)
| 1 || 0010
| (18)
|-
| 0 || 1 || 1 || 0 || 1
| (13)
| 1 || 0011
| (19)
|-
| 0 || 1 || 1 || 1 || 0
| (14)
| 1 || 0100
| (20)
|-
| 0 || 1 || 1 || 1 || 1
| (15)
| 1 || 0101
| (21)
|-
| 1 || 0 || 0 || 0 || 0
| (16)
| 1 || 0110
| (22)
|-
| 1 || 0 || 0 || 0 || 1
| (17)
| 1 || 0111
| (23)
|-
| 1 || 0 || 0 || 1 || 0
| (18)
| 1 || 1000
| (24)
|}
En analysant le tableau, on voit que pour corriger le résultat, il suffit d'ajouter 6. La raison est que le résultat déborde d'un nibble à 16 en binaire, mais à 10 en décimal : il suffit d'ajouter la différence entre les deux, à savoir 6, et le débordement binaire fait son travail. Donc, la correction après une addition est très simple : si le résultat dépasse 9, on ajoute 6.
On peut maintenant implémenter l'additionneur BCD, en combinant le comparateur avec 10, le circuit de correction, et l'additionneur. La première solution calcule deux versions du résultat : la version corrigée, la version normale. Le choix entre les deux est réalisée par un multiplexeur, commandé par le comparateur.
[[File:Additionneur BCD.png|centre|vignette|upright=1.5|Additionneur BCD]]
L'autre solution utilise un circuit commandable qui soit additionne 6, soit ne fait rien. Le choix entre les deux est commandé par le bit calculé par le comparateur.
[[File:Additionneur BCD, seconde version.png|centre|vignette|upright=1.5|Additionneur BCD, seconde version.]]
Une version alternative du circuit précédent est la suivante. Il contient deux additionneurs : un pour additionner les deux chiffres BCD, un autre pour additionner 6 si besoin. Le résultat du comparateur est directement utilisé pour générer l'opérande du second additionneur : 0 ou 6. Le circuit est simple à concevoir, mais gaspille beaucoup de circuit. Idéalement, il vaudrait mieux utiliser un circuit combinatoire d'addition avec une constante.
[[File:Adder 4 Bit BCD.svg|centre|vignette|upright=2|Additionneur BCD, circuit complet.]]
Pour obtenir un additionneur BCD complet, il suffit d’enchaîner les additionneurs précédents, comme on le ferait avec les additionneurs complets dans un additionneur à propagation de retenue. Au final, l'additionneur BCD est beaucoup plus compliqué qu'un additionneur normal, car il rajoute un comparateur ">9", un petit additionneur pour ajouter 6 et éventuellement d'autres circuits. De plus, il est difficile d'appliquer les optimisations disponibles sur les additionneurs non-BCD. Notamment, les circuits d'anticipation de retenue sont totalement à refaire et le résultat est relativement compliqué. C'est ce qui explique pourquoi le BCD a progressivement été abandonné au profit du binaire simple.
La soustraction en BCD se fait comme en binaire : le nombre à soustraire est remplacé par son complément, le circuit additionne le complément et l'autre opérande, le débordement d'entier fait que le résultat marche. Sauf qu'ici, le complément est un complément à 9. Il se calcule chiffre par chiffre : chaque chiffre est remplacé par (9 - le chiffre en question).
===L'additionneur BCD par ajustement décimal===
L'additionneur BCD précédent effectuait son travail chiffre BCD par chiffre BCD, mais il existe des additionneurs BCD qui font autrement. Sur les premiers processeurs x86, il n'y avait pas d'opération d'addition BCD proprement dit, seulement une addition binaire normale de 8, 16 ou 32 bits. Par contre, elle était secondée par une opération dite d''''ajustement décimal''' qui transformait un nombre binaire en nombre codé en BCD. L'opération d'ajustement décimal prenait un opérande de 8 bits codé en binaire et fournissait un résultat de la même taille, c'est à dire deux chiffres BCD. Effectuer une addition BCD demandait donc de faire deux opérations à la suite : une addition binaire simple, suivie par l'opération d'ajustement décimal. Cela permettait de gérer des nombres entiers en binaire usuel et des entiers BCD sans avoir deux instructions d'addition séparées pour les deux, sans compter que cela simplifiait aussi les circuits d'addition.
L'ajustement décimal s'effectue en ajoutant une constante bien précise à l'opérande à convertir en BCD. L'idée est que la constante est découpée en morceaux de 4 bits, correspondant chacun à un chiffre BCD de l'opérande, chaque morceau contenant soit un 0, soit 6. Cela permet d'ajouter soit 0, soit 6, à chaque chiffre BCD, et donc de le corriger. La propagation des retenues d'un chiffre à l'autre est effectuée automatiquement par l'addition binaire de la constante. L'opération d'ajustement décimal calcule automatiquement la constante. Elle découpe l'opérande en ''nibbles'', vérifie si chaque ''nibble'' est supérieur ou égal à 10, puis détermine la valeur de chaque ''nibble'' de la constante finale. Par exemple, si je prends l'opérande 1001 1110, le ''nibble'' de poids faible déborde, alors que celui de poids fort non. La constante sera donc 0000 0110 : 0x06. Inversement, si le ''nibble'' de poids fort déborde et pas celui de poids faible, la constante sera alors 0x60. Et la constante est de 0x66 si les deux ''nibbles'' débordent, de 0x00 si aucun ne déborde.
Le circuit d’ajustement décimal est donc composé de trois étapes : deux étapes pour calculer la constante, et un circuit d'addition pour additionner cette constante au nombre de départ. La première étape découpe l'opérande en morceaux de 4 bits, en chiffres BCD, et vérifie si chacun d'entre eux vaut 10 ou plus. La seconde étape prend les résultats de la première étape, et les combine pour calculer la constante. Enfin, on trouve l'addition finale, qui était réalisée par un circuit d'addition utilisé à la fois pour l'ajustement décimal et l'addition binaire. La différence entre une addition normale et une opération d'ajustement décimal tient dans le fait que les deux premières étapes sont désactivées dans une addition normale.
[[File:Additionneur BCD parallèle.png|centre|vignette|upright=1.5|Additionneur BCD parallèle]]
===L'additionneur biquinaire===
Les entiers BCD ne sont qu'un des encodages hybrides entre décimal et binaire. L'encodage biquinaire est l'un d'entre eux et nous allons faire un rappel rapide à ce sujet. Pour simplifier, un chiffre encodé en biquinaire est composé de deux parties : un bit, couplé à une partie quinaire encodée en représentation ''one-hot''. La partie quinaire encode un nombre allant de 0 à 4, ce qui prend 5 bits (0, 1, 2, 3 et 4). Le bit indique s'il faut ou non ajouter 5 à la valeur encodée par la partie quinaire. Ainsi, on peut coder tous les nombres de 0 à 9.
Additionner deux nombres de biquinaire demande donc d'additionner deux parties quinaires encodées en ''one-hot'' et d'additionner deux bits. Mais attention : il faut tenir compte de la retenue de l'addition des parties quinaires. Et idéalement, il faut aussi tenir compte d'une retenue entrante, provenant de l'addition de la colonne de chiffres précédente. Toute la difficulté vient de la création de l'additionneur ''one-hot''. Heureusement, vu qu'il n'y a que 4-5 bits à additionner, il est souvent fabriqué à partir de sa table de vérité.
[[File:Additionneur bi-quinaire.png|centre|vignette|upright=2|Additionneur bi-quinaire]]
Un avantage du biquinaire est que le calcul du complément à 9 est très simple. Il faut pour cela : inverser la partie binaire avec une porte NON, puis inverser l'ordre des bits de la partie quinaire. Concrètement, le bit de poids faible devient le bit de poids fort, et ainsi de suite. Par exemple, une partie quinaire 01000 devient 00010, 10000 devient 00001, 00100 ne change pas, etc. Le tout peut se calculer avec une porte NON et 5 multiplexeurs.
===L'additionneur BCD avec calculs intermédiaires en biquinaire===
L'ordinateur IBM 1401, un ancien ''mainframe'' des années 60, utilisait un additionneur BCD un peu particulier. Les nombres étaient encodés en BCD dans la mémoire de l'ordinateur, mais les circuits de calcul utilisaient la représentation biquinaire. Lors d'un calcul, le processeur de l'ordinateur traduisait les chiffres BCD en représentation biquinaire, faisait une addition en biquinaire, avant de traduire le résultat en BCD normal.
Pour être précis, l'IBM 1401 utilisait une variante du biquinaire. L'encodage biquinaire de l'IBM 1401 est le suivant : la partie binaire disait si le chiffre était pair ou non, la partie quinaire encodait les valeurs 0, 2, 4, 6 et 8. Le chiffre se calculait en additionnant la partie binaire (0 ou 1) au nombre pair encodé par la partie quinaire. Si l'IBM 1401 utilisait cette variante du biquinaire, c'est car elle donnait des circuits de conversion BCD-biquinaire plus économes en portes logiques et plus rapides.
La partie binaire est le bit de poids faible du chiffre BCD, la partie biquinaire est calculée par un simple décodeur qui prend en entrée le chiffre BCD, amputé de son bit de poids faible. La traduction inverse demande d'utiliser un encodeur, à la place du décodeur. Par contre, le circuit d'addition biquinaire était plus compliqué du fait de la gestion des retenues. L'addition des parties binaires et quinaires se faisait en parallèle, dans deux additionneurs séparés. Cependant, l'addition des parties binaire fournit une retenue, qu'il faut prendre en compte. Pour cela, l'IBM 1401 disposait d'un troisième additionneur qui fournissait le résultat final, encodé en biquinaire.
[[File:Additionneur bi-quinaire de l'IBM 1401.png|centre|vignette|upright=2|Additionneur biquinaire de l'IBM 1401]]
Une implémentation moderne demanderait d'utiliser des portes ET combinées à des portes OU, le circuit pouvant être construit simplement à partir de sa table de vérité. Sur l'IBM 1401, le circuit était cependant différent, en raison de l'utilisation de OU câblés, des croisements de fils qui fonctionnent comme des portes OU, que nous n'avons pas encore vu pour le moment, mais qui seront détaillés dans quelques chapitres. Les OU câblés étaient utilisés pour simplifier le design du circuit, mais demandaient des portes logiques spécifiques, ce qui collait avec le fait que ce ''mainframe'' utilisait des transistors en Germanium. L'implémentation exacte est décrite dans cet article de blog, mais je ne recommande sa lecture qu'à ceux qui savent ce qu'est un OU câblé :
* [https://www.righto.com/2015/10/qui-binary-arithmetic-how-1960s-ibm.html Qui-binary arithmetic: how a 1960s IBM mainframe does math].
==Les débordements d'entier lors d'une addition/soustraction==
Les instructions arithmétiques manipulent des entiers codés sur un nombre fixe de bits, qui ne peuvent prendre leurs valeurs que dans un intervalle. Pour les nombres positifs, un ordinateur qui code ses entiers sur n bits pourra coder tous les entiers allant de 0 à <math>2^n -1</math>. Pour les nombres négatifs, l'intervalle est différent et dépend de la représentation utilisée. Dans le cas général, l'ordinateur peut coder les valeurs comprises de <math>N_\text{min}</math> à <math>N_\text{max}</math>. Si le résultat d'un calcul sort de cet intervalle, il ne peut pas être représenté par l'ordinateur et il se produit ce qu'on appelle un '''débordement d'entier'''.
La '''valeur haute de débordement''' désigne la première valeur qui est trop grande pour être représentée par l'ordinateur. Par exemple, pour un ordinateur qui peut coder tous les nombres entre 0 et 7, la valeur haute de débordement est égale à 8. On peut aussi définir la '''valeur basse de débordement''', qui est la première valeur trop petite pour être codée par l'ordinateur. Par exemple, pour un ordinateur qui peut coder tous les nombres entre 8 et 250, la valeur basse de débordement est égale à 7. Pour les nombres entiers, la valeur haute de débordement vaut <math>N_\text{max} + 1</math> , alors que la valeur basse vaut <math>N_\text{min} - 1</math> (avec <math>N_\text{max}</math> et <math>N_\text{min}</math> respectivement la plus grande et la plus petite valeur codable par l'ordinateur).
===La correction des débordements d'entier : l'arithmétique saturée===
Quand un débordement d'entier survient, tous les circuits de calcul ne procèdent pas de la même manière. Dans les grandes lignes, il y a deux réactions possibles : soit on corrige automatiquement le résultat du débordement, soit on ne fait rien et on se contente de détecter le débordement.
Si le débordement n'est pas corrigé automatiquement, le circuit ne conserve que les bits de poids faibles du résultat. Les bits en trop sont simplement ignorés. On dit qu'on utilise l''''arithmétique modulaire'''. Le problème avec ce genre d'arithmétique, c'est qu'une opération entre deux grands nombres peut donner un résultat très petit. Par exemple, si je dispose de registres 4 bits et que je souhaite faire l'addition 1111 + 0010 (ce qui donne 15 + 2), le résultat est censé être 10001 (17), ce qui est un résultat plus grand que la taille d'un registre. En conservant les 4 bits de poids faible, j’obtiens 0001 (1). En clair, un résultat très grand est transformé en un résultat très petit. Cela peut poser problèmes si on travaille uniquement avec des nombres positifs, mais c'est aussi utilisé pour coder des nombres en complément à deux.
D'autres circuits utilisent ce qu'on appelle l''''arithmétique saturée''' : si un calcul génère un débordement, on arrondi le résultat au plus grand entier supporté par le circuit. Les circuits capables de calculer en arithmétique saturée sont un peu plus complexes, vu qu'il faut rajouter des circuits pour corriger le résultat en cas de débordement. Il suffit généralement de rajouter un ''circuit de saturation'', qui prend en entrée le résultat et le corrige en cas de débordement. Ce circuit de saturation met la valeur maximale en sortie si un débordement survient, mais se contente de recopier le résultat du calcul sur sa sortie s'il n'y a pas de débordement. Typiquement, il est composé d'une couche de multiplexeurs, qui sélectionnent quelle valeur mettre sur la sortie : soit le résultat du calcul, soit le plus grand nombre entier géré par le processeur, soit le plus petit (pour les nombres négatifs/soustractions).
L'arithmétique saturée est utilisée pour les additions et soustractions, mais c'est plus rare pour les multiplications/divisions. Une des raisons est que le résultat d'une addition/soustraction prend un bit de plus que le résultat, là où les multiplications doublent le nombre de bits. Quand une addition déborde, le résultat réel est proche de la valeur maximale codable. mais quand une multiplication déborde, le résultat peut parfois valoir 200 à 60000 fois plus que la valeur maximale codable. Les calculs avec une valeur saturée/corrigée sont donc crédibles pour une suite d'additions, mais pas pour une suite de multiplications.
===La détection des débordements entiers===
Quand un débordement d'entier a eu lieu, il vaut mieux que l'additionneur prévienne ! Pour cela, l'additionneur a une '''sortie de débordement''', parfois nommée ''Overflow'', dont la valeur indique si l'addition a généré un débordement d'entier ou non. Reste que détecter un débordement ne se fait pas de la même manière selon que l'on parle d'un additionneur non-signé ou signé.
Pour les additionneur non-signés, l'additionneur calcule un bit de plus que ce qui est supporté par l'ordinateur. Par exemple, un additionneur 32 bits fournit un résultat sur 33 bits, un débordement d'entier a lieu quand le 33ème bit est à 1. Précisément, la sortie de débordement n'est autre que la retenue finale, celle fournie par le dernier additionneur complet. Le seul type de débordement possible est un débordement par le haut, où le résultat dépasse la valeur maximale. Avec l'arithmétique saturée, le circuit de saturation consiste en une seule couche de multiplexeurs, voire en un circuit de mise à la valeur maximale tel que vu dans le chapitre sur les opérations bits à bits.
[[File:Gestion des débordements d'entiers.png|centre|vignette|upright=2.5|Gestion des débordements d'entiers lors d'une addition non-signée.]]
Pour les additionneurs non-signés, la gestion des débordements d'entiers dépend fortement de la représentation signée. Nous allons étudier le cas du complément à deux. Si vous vous rappelez le chapitre 1, les calculs sur des nombres en complètement à deux utilisent les règles de l'arithmétique modulaire, c'est une condition nécessaire. À priori, on peut penser que dans ces conditions, les débordements d'entiers sont une chose parfaitement normale, qui nous permet d'avoir des résultats corrects. Néanmoins, certains débordements d'entiers peuvent survenir malgré tout et produire des bugs assez ennuyeux.
Si l'on tient en compte les règles du complément à deux, on sait que le bit de poids fort (le plus à gauche) permet de déterminer si le nombre est positif ou négatif : il indique le signe du nombre. Tout se passe comme si les entiers en complément à deux étaient codés sur un bit de moins, et avaient leur longueur amputé du bit de poids fort. Si le résultat d'un calcul écrase le bit de poids fort, il y a un débordement d'entiers. Il existe une règle simple qui permet de détecter ces débordements d'entiers. L'addition de deux nombres positifs ne peut pas être un nombre négatif. Si on additionne deux nombres dont le bit de signe est à 0 et que le bit de signe du résultat est à 1, on est en face d'un débordement d'entiers. Même chose pour deux nombres négatifs : le résultat de l'addition ne peut pas être positif. On peut résumer cela en une phrase : si deux nombres de même signe sont ajoutés, un débordement a lieu quand le bit du signe du résultat a le signe opposé.
Modifier les circuits d'au-dessus pour qu'ils détectent les débordements en complément à deux est simple comme bonjour : il suffit créer un petit circuit combinatoire qui prenne en entrée les bits de signe des opérandes et du résultat, et qui fasse le calcul de l'indicateur de débordements. Si l'on rédige sa table de vérité, on doit se retrouver avec la table suivante :
{|class="wikitable"
|-
!Entrées
!Sortie
|-
|000||0
|-
|001||1
|-
|010||0
|-
|011||0
|-
|100||0
|-
|101||0
|-
|110||1
|-
|111||0
|}
L'équation de ce circuit est la suivante, avec <math>S_a</math> et <math>S_b</math> les signes des deux opérandes, et <math>C_i</math> la retenue de la colonne précédente :
: <math> ( S_a . S_b . \overline{C_i} ) + ( \overline{S_a} . \overline{S_b} . C_i )</math>
En simplifiant, on obtient alors :
: <math>( S_a . S_b ) \oplus C_i</math>
Or, il se trouve que <math>S_a . S_b</math> est tout simplement la retenue en sortie du dernier additionneur, que nous noterons <math>C_o</math>. On trouve donc :
: <math>C_o \oplus C_i</math>
Il suffit donc de faire un XOR entre la dernière retenue et la précédente pour obtenir le bit de débordement.
<noinclude>
{{NavChapitre | book=Fonctionnement d'un ordinateur
| prev=Les circuits de décalage et de rotation
| prevText=Les circuits de décalage et de rotation
| next=Les circuits de comparaison
| nextText=Les circuits de comparaison
}}
</noinclude>
ckq3jj7doghor0mx6zqgpf6bdxzf6nf
768771
768769
2026-06-27T12:49:24Z
Mewtow
31375
/* Les circuits pour additionner 2 ou 3 bits */
768771
wikitext
text/x-wiki
Dans ce chapitre, nous allons voir les circuits capables de faire une addition ou une soustraction, ainsi que quelques circuits spécialisés. Précisons cependant que les fabricants de processeurs travaillent d'arrache-pied pour trouver des moyens de rendre ces circuits de calcul plus rapides et plus économes en énergie. Autant vous dire que les circuits que vous allez voir sont vraiment des circuits qui font pâle figure comparé à ce que l'on peut trouver dans un vrai processeur commercial !
==Les circuits pour additionner 2 ou 3 bits==
L'addition se fait en binaire de la même manière qu'en décimal. On additionne les chiffres/bits colonne par colonne, une éventuelle retenue est propagée à la colonne d'à côté. La soustraction fonctionne sur le même principe, sur le même modèle qu'en décimal.
[[File:Binary Addition Demonstration.svg|centre|vignette|Exemple d'addition en binaire.]]
En clair, additionner deux nombres demande d'additionner 2 bits et une retenue sur chaque colonne, et de propager les retenues d'une colonne à l'autre. La propagation des retenues est quelque chose de simple en apparence, mais qui est sujet à des optimisations extraordinairement nombreuses. Aussi, pour simplifier l'exposition, nous allons voir comment gérer une colonne avant de voir comment sont propagées les retenues.
[[File:Full Adder Block.svg|vignette|upright=1|class=transparent|Additionneur complet.]]
Si on effectue une addition en colonne, on doit additionner les deux bits sur la colonne, mais aussi additionner une éventuelle retenue. Il faut donc créer un circuit qui additionne trois bits : deux bits de données, plus une retenue. Ce circuit qui additionne trois bits est appelé un '''additionneur complet'''. Il fournit en sortie deux bits : un bit de somme et une retenue sortante. Voici sa table de vérité :
{|class="wikitable"
|-
! Retenue entrante !! Opérande 1 !! Opérande 2 !! !! Retenue sortante !! Bit de somme
|-
||0||0||0|| ||0||0
|-
||0||0||1|| ||0||1
|-
||0||1||0|| ||0||1
|-
||0||1||1|| ||1||0
|-
||1||0||0|| ||0||1
|-
||1||0||1|| ||1||0
|-
||1||1||0|| ||1||0
|-
||1||1||1|| ||1||1
|}
Il est possible d'utiliser un tableau de Karnaugh pour traduire la table de vérité, mais elle donne un résultat assez compliqué. La retenue et le bit de somme sont calculés à part. La retenue est calculée avec trois portes ET et une porte OU. Le bit de somme est calculé lui avec 4 portes ET, une porte OU, et trois portes NON.
[[File:Full Adler with 3 entries.png|centre|vignette|upright=2|Additionneur complet, fabriqué à partir de la table de vérité.]]
Le circuit obtenu est pas loin d'être optimal, que ce soit en termes de rapidité que de portes logiques utilisées. Mais il est assez compliqué à comprendre, vu que c'est une traduction littérale de la table de vérité. D'autres implémentations alternatives donnent des résultats plus compréhensibles. Nous allons les voir dans la suite de cette section.
===L'additionneur complet basé sur des demi-additionneurs===
[[File:1-bit half-adder.svg|class=transparent|vignette|Demi-addtionneur.]]
Nous avons déjà vu comment additionner deux bits dans le chapitre sur les incrémenteurs, mais quelques rappels ne seront pas de trop. Le '''demi-additionneur''' est un circuit qui additionne deux bits. Il implémente la table d'addition, qui est très simple en binaire. Jugez plutôt :
* 0 + 0 = 0, retenue = 0 ;
* 0 + 1 = 1, retenue = 0 ;
* 1 + 0 = 1, retenue = 0 ;
* 1 + 1 = 0, retenue = 1.
Mais un demi-additionneur n'additionne que deux bits, il manque de quoi additionner la retenue. Une solution pour cela est d'enchaîner deux demi-additionneurs : un qui additionne les deux bits de données, et un second qui additionne la retenue au résultat du précédent. La retenue finale se calcule en combinant les sorties de retenue des deux demi-additionneurs, avec une porte OU. Le résultat est un additionneur complet fabriqué avec deux demi-additionneurs. Pour vous en convaincre, établissez la table de vérité de ce circuit, vous verrez que ça marche.
[[File:Full Adder Blocks.svg|centre|vignette|upright=2|Additionneur complet fabriqué avec deux demi-additionneurs.]]
[[File:Half-adder.svg|vignette|upright=1|Circuit d'un demi-additionneur naïf.]]
L'implémentation naïve d'un demi-additionneur utilise une porte XOR et une porte ET, comme illustré ci-contre. Le circuit s'obtient en faisant uen traduction littérale de la table de vérité du circuit. Si je dis qu'elle est naïve, c'est car il est possible de l'optimiser, de manière à éliminer des redondances cachées. Mais nous avions déjà vu cela dans le chapitre sur les incrémenteurs, aussi laissons cela pour plus tard.
En combinant deux demi-additionneurs de ce type, on obtient l'additionneur complet suivant. Les deux sorties de retenue des demi-additionneurs sont combinées avec une porte OU, pour calculer la retenue finale.
{| class="flexible"
|[[File:Full Adder Modules.svg|class=transparent|Composition d'un additionneur complet. On voit bien que celui-ci est composé de deux demi-additionneurs, en rouge et en bleu, auxquels on a ajouté une porte OU pour calculer la retenue finale.]]
|[[File:Full-adder.svg|300px|class=transparent|Circuit d'un additionneur complet.]]
|}
Le fait de combiner les deux retenues avec une porte OU n'est pas ce qu'il y a de plus intuitif. La table de vérité nous dit que ça fonctionne, mais on comprend mal pourquoi. Mais tout devient plus clair quand on sait que les deux retenues ne peuvent pas être à 1 en même temps. Même dans l'addition 1 + 1 + 1, seule une retenue est à 1. Soit l'addition des deux bits d'opérande donne naissance à une retenue, soit c'est l'addition ''résultat + retenue entrante''. La retenue sortant est donc à 1 quand une des deux addition donne une retenue. D'où l'usage d'une porte OU. Remarquez qu'une porte XOR donne le même résultat, vu que le cas où les deux retenues sont à 1 n'est jamais rencontré.
===La propagation et la génération des retenues===
L'additionneur complet que nous allons voir dans cette section sert à introduire les concepts de propagation, génération et absorption de retenue. Il s'agit de concepts très importants quand on étudie les additionneurs. l'idée part d'un principe très simple : la retenue sortante dépend de la retenue d'entrée. La relation entre les deux se résume à trois cas, qui dépendent de la valeur des deux bits additionnés, nommés A et B.
* Dans le premier cas, la retenue entrante est égale à la retenue sortante. On dit que la retenue entrante est propagée par l'additionneur.
* Dans le second cas, la retenue est forcée à 0 : la retenue sortante vaut 0, peu importe la valeur de la retenue entrante.
* Dans le troisième cas, la retenue est forcée à 1 : la retenue entrante vaut 1, peu importe la valeur de la retenue entrante.
[[File:Signaux P et G fournis par un demi-additionneur.png|vignette|Signaux P et G fournis par un demi-additionneur]]
Maintenant, créons un circuit qui nous dise si une retenue est propagée ou générée. Il a deux bits de sortie, nommés P et G : P pour ''Propagate'', G pour ''Generate''. Le bit P est à 1 si la retenue entrante est propagée, il est à 0 sinon. Le bit G est à 1 si une retenue est générée, à 0 sinon. Une retenue est considérée comme absorbée si elle n'est pas ni propagée ni générée, pas besoin d'un troisième bit pour gérer ce cas.
Il se trouve que ce circuit n'est autre qu'un demi-additionneur ! Pour vous en rendre compte, regardez la table de vérité d'un additionneur complet, illustrée ci-dessous. Lorsque les deux bits d'opérande sont à 0, la retenue sortante vaut toujours 0. Si ils sont tous deux à 1, alors la retenue sortante vaut 1. S'ils sont différents, alors retenues sortante et entrante sont égales. Le bit P est donc généré par une simple porte XOR. Quant au bit G, il est à 1 si les deux bits d'opérandes sont à 1, ce qui correspond à une porte ET. Il se trouve que ces deux portes forment un demi-additionneur !
{|class="wikitable"
|-
! Opérande 1 !! Opérande 2 !! Retenue entrante !! !! Retenue sortante
|- class="f_rouge"
||0||0||0|| ||0
|- class="f_rouge"
||0||0||1|| ||0
|- class="f_bleu"
||0||1||0|| ||0
|- class="f_bleu"
||0||1||1|| ||1
|- class="f_bleu"
||1||0||0|| ||0
|- class="f_bleu"
||1||0||1|| ||1
|- class="f_vert"
||1||1||0|| ||1
|- class="f_vert"
||1||1||1|| ||1
|}
Ensuite, créons un circuit qui prend ces deux sorties P et G, et calcule la retenue sortante en fonction. Le circuit en question a trois entrées : la retenue entrante, les deux bits P et G. La retenue sortante vaut 1 soit si une retenue est générée, soit si la retenue entrante est propagée et qu'elle vaut 1. En notant <math>{C_{in}}</math> et <math>{C_{out}}</math> les retenues entrantes et sortante, on a :
: <math>{C_{out}} = G + \left( P . C_{in} \right)</math>
Le circuit est donc composé d'une porte OU et d'une porte ET. En combinant un demi-additionneur avec le circuit de calcul de retenue sortante vu plus haut, on a :
[[File:Additionneur complet avec propagation et génération de retenue.png|class=transparent|centre|vignette|upright=2|Additionneur complet avec propagation et génération de retenue.]]
Certes, on retombe sur le circuit vu plus haut. Mais il est possible de remplacer le circuit en orange par un autre. Par exemple, il est possible de le remplacer par multiplexeur, qui choisit entre la retenue générée et la retenue entrante (propagée). Le choix se fait selon la valeur du bit P, qui chosiit entre propager la retenue et la générer.
[[File:Additionneur crée avec un multiplexeur.png|centre|vignette|upright=1.5|Additionneur crée avec un multiplexeur]]
Le circuit exact est illustré ci-dessous. Il semble utiliser plus de portes logiques que l'additionneur complet précédent. Cependant, nous verrons dans quelques chapitres qu'il est possible d'implémenter un multiplexeur avec seulement 6 transistors, voire moins ! L'implémentation utilise des portes à transmission, mais nous en reparlerons dans le chapitre sur les transistors, quand nous verrons les additionneurs à ''Manchester Carry Chain''. Au passage, une variante de ce circuit a été utilisée dans le processeur processeur 8086 d'Intel, comme on le verra dans le chapitre suivant.
[[File:Additionneur complet basé sur un MUX.png|centre|vignette|upright=1|Additionneur complet basé sur un MUX]]
[[File:FulladderPG.png|vignette|upright=1|Additionneur complet avec deux sorties P et G.]]
Pour finir, sachez qu'il existe des additionneurs qui fournissent : le bit de résultat, deux sorties P et G qui indiquent si l'addition propage ou génère une retenue. Il ne s'agit pas d'additionneurs complets, car il manque de quoi calculer la retenue sortante. De tels additionneurs seront appelés des '''additionneurs partiels'''. De tels additionneurs sont utilisés dans certains additionneurs pour gagner en performance.
Enfin, il existe aussi ce que j'ai décidé d'appeler des '''additionneurs P/G''', qui sont des additionneurs complets auxquels on a ajouté deux sorties P et G, en plus de la sortie de retenue. Leur circuit est le même que celui d'un additionneur complet, auquel on a ajouté deux fils. Vu que ces deux sorties sont fournies par le premier demi-additionneur, ajouter ces deux sorties demande d'ajouter des fils, pas de portes logiques. De tels additionneurs sont aussi utilisés dans certains additionneurs pour gagner en performance.
[[File:FAwithGP.svg|centre|vignette|upright=2|Additionneur complet avec sorties P et G : circuit.Ç]]
===L'additionneur complet basé sur une porte à majorité===
Maintenant, voyons une dernière implémentation possible de l'additionneur complet. Mais avant de voir comment l'additionneur est implémenté, nous allons devoir faire un petit focus sur la retenue sortante. Reprenons la table de vérité, et regardons ce qu'il en est pour la retenue sortante uniquement.
{|class="wikitable"
|-
! Retenue entrante !! Opérande 1 !! Opérande 2 !! !! Retenue sortante
|-
||0||0||0|| ||0
|-
||0||0||1|| ||0
|-
||0||1||0|| ||0
|-
||0||1||1|| ||1
|-
||1||0||0|| ||0
|-
||1||0||1|| ||1
|-
||1||1||0|| ||1
|-
||1||1||1|| ||1
|}
Vous remarquerez que la retenue sortante est égale au bit majoritaire, parmi les trois bits d'opérande. Si deux ou trois bits sont à 1, la retenue sortante vaut 1. Et réciproquement avec 0. Le circuit de calcul de la retenue peut donc être remplacé par une '''porte à majorité'''.
[[File:Additionneur crée avec une porte à majorité.png|centre|vignette|upright=2|Additionneur crée avec une porte à majorité]]
Mais une porte à majorité est assez gourmande en circuit, ce qui fait qu'un additionneur pareil ne serait pas pratique, ni utile. Du moins, ce serait le cas s'il n'y avait pas une possibilité d'optimisation extrêmement intéressante. Il est possible de calculer le 'bit de somme' à partir de la retenue sortante ! En effet, le bit de somme est l'inverse de la retenue sortante, sauf dans deux cas : les trois bits d'entrée sont à 0, où ils sont tous à 1. Le bit de somme vaut 0 dans le premier cas, 1 dans le second cas.
{|class="wikitable"
|-
! Retenue entrante !! Opérande 1 !! Opérande 2 !! !! Retenue sortante !! Bit de somme
|- class="f_bleu"
||0||0||0|| ||0||0
|- class="f_vert"
||0||0||1|| ||0||1
|- class="f_vert"
||0||1||0|| ||0||1
|- class="f_vert"
||0||1||1|| ||1||0
|- class="f_vert"
||1||0||0|| ||0||1
|- class="f_vert"
||1||0||1|| ||1||0
|- class="f_vert"
||1||1||0|| ||1||0
|- class="f_rouge"
||1||1||1|| ||1||1
|}
En clair, 6 lignes de la table de vérité sur 8 peuvent se calculer avec une porte à majorité, le reste demandant quelques portes logiques pour faire la correction. L'implémentation en circuit demande donc trois choses :
* de calculer la retenue sortante et de quoi l'inverser ;
* un circuit qui vérifie si tous les bits opérande valent 0 : une porte NOR ou OU fait l'affaire ;
* un circuit qui vérifie s'ils valent tous 1, à savoir une porte ET.
Ensuite, on combine le résultat des trois circuits précédents pour obtenir le résultat final. La combinaison est le fait du circuit en jaune dans le schéma ci-dessous.
[[File:Full adder basé sur une modification de la retenue.png|centre|vignette|upright=2|Full adder basé sur une modification de la retenue]]
Le circuit en jaune est assez simple. Quand la porte ET sort un 1, la sortie doit être forcée à 1. Par contre, quand la porte OU sort un zéro, la sortie doit être forcée à 0. Il suffit donc d'enchainer un circuit de mise à 1 et un circuit de mise à 0. Nous avons vu dans le chapitre sur les opérations de masquage qu'il s'agit respectivement d'une porte OU et d'une porte ET.
[[File:Full adder basé sur une modification de la retenue interne, au complet.png|centre|vignette|upright=2|Full adder basé sur une modification de la retenue interne, au complet]]
Divers processeurs utilisaient des additionneurs complets de ce type : le fameux Z80, l'Intel 4004 et de l'Intel 8008 sont les exemples les plus notables. Pourtant, on pourrait s'étonner qu'un tel circuit ait existé. Il utilise beaucoup de portes logiques, a une profondeur logique supérieure : il n'a rien d'avantageux. Sauf qu'il était utilisé sur d'anciens processeurs, qui utilisaient la technologie dite TTL, différente de la technologie CMOS des transistors modernes. Et avec la technologie TTL, il est possible de fusionner plusieurs portes logiques ET et NOR en une seule porte logique ET/OU/NON ! Un additionneur complet construit ainsi ne prenait que deux portes logiques : une pour le calcul de la retenue sortante, une autre pour le reste du circuit.
==L'addition non signée==
Voyons maintenant un circuit capable d'additionner deux nombres entiers: l''''additionneur'''. Dans la version qu'on va voir, ce circuit manipulera des nombres strictement positifs. L'addition des nombres codés en complètement à deux sera vu dans une section ultérieure.
[[File:Full-adder-4bits-serial-CEI.svg|vignette|upright=0.5|Additionneur 4 bits, un bloc.]]
L'interface d'un additionneur est illustrée ci-contre. Un additionneur prend deux opérandes sur deux entrées séparées, et fournit le résultat sur une sortie dédiée. De plus, il a une entrée sur laquelle envoyer une retenue entrante, et a une sortie pour la retenue sortante. La sortie de retenue indique que le résultat de l'addition a débordé (au sens d'un débordement d'entier). Pour l'entrée de retenue, son utilité deviendra plus claire dans ce qui suit, mais souvenez-vous qu'elle existe.
L'entrée et la sortie de retenue sont parfois utilisées pour combiner plusieurs petits additionneurs, pour former un additionneur plus grand. Nous verrons de nombreux exemples dans ce qui suit. Il est assez commun de créer des additionneurs pour des opérandes de 16/32/64 bits, avec des additionneurs de 4/5 bits.
===L'additionneur série===
Il est possible d'additionner deux nombres bit par bit, grâce à un additionneur complet associé à plusieurs registres à décalages. Les opérandes sont placées chacune dans un registre à décalage, et l'additionneur complet utilise les bits sortants de ces registres à décalage. Le bit du résultat est envoyé au registre à décalage pour le résultat. La retenue de l'addition est stockée dans une bascule de 1 bit, en attente du prochain cycle d'horloge. Un tel additionneur est appelé un '''additionneur série'''. Il a été utilisé sur d'anciens ordinateurs dans les années 50-60, aussi bien des prototypes que des ordinateurs commerciaux.
[[File:Additionneur série.jpg|centre|vignette|upright=2.5|Additionneur série.]]
===L'additionneur à propagation de retenue===
L''''additionneur à propagation de retenue''' est aussi appelé l'additionneur ''ripple carry'', c'est son nom anglais. J'utiliserais occasionnellement le nom anglais dans ce qui suit, car celui-ci est plus court. Ilutilise un additionneur complet pour chaque colonne de l'addition. Pour gérer les retenues, il suffit ainsi de câbler des additionneurs complets les uns à la suite des autres, chacun envoyant sa retenue à la colonne suivante.
[[File:Ripplecarryadder.png|centre|vignette|upright=2.5|Additionneur à propagation de retenue.]]
Notons la présence de la retenue sortante, qui est utilisée pour détecter les débordements d'entier, ainsi que pour d'autres opérations. Le bit de retenue final est souvent stocké dans un registre spécial du processeur (généralement appelé ''carry flag'').
Notez aussi, à droite du schéma précédent, l'entrée de retenue <math>\text{R}_\text{Entrée}</math>. Elle est très utile pour l'implémentation de certaines opérations comme l'inversion de signe, la soustraction, l'incrémentation, etc. Certains processeurs sont capables de faire une opération appelée ADC, ADDC ou autre nom signifiant ''Addition with Carry'', qui permet de faire le calcul A + B + Retenue (la retenue en question est la retenue sortante de l'addition précédente, stockée dans le registre ''carry flag''). Son utilité principale est de permettre des additions d'entiers plus grands que ceux supportés par le processeur. Par exemple, cela permet de faire des additions d'entiers 32 bits sur un processeur 16 bits.
[[File:Adder Network 3 Bit Sum Delay 003.svg|vignette|upright=0.5|Propagation de retenue dans l'additionneur.]]
Cet additionneur utilise très peu de portes logiques, ce qui fait qu'il était utilisé sur certains processeurs 8 et 16 bits, dont le budget en portes logiques était limité. Bien que très simple, cet additionneur est cependant peu performant. Son temps de calcul est proportionnel à la taille des opérandes. Par exemple, additionner deux opérandes de 32 bits prendra deux fois plus de temps que pour des opérandes de 16 bits. La raison est que les retenues doivent se propager à travers le circuit, du premier additionneur jusqu'au dernier.
L'addition étant une opération fréquente, d'autres additionneurs ont été inventés. Ils utilisent des optimisations qui utilisent plus de circuits pour gagner en rapidité. Mais avant de voir ces optimisations, nous allons voir s'il est possible d'optimiser les additionneurs à propagation de retenue.
===L'additionneur à saut de retenue===
L'additionneur à propagation de retenue peut être optimisé de deux manières. La première accélère la propagation de la retenue, en agissant au niveau des additionneurs complets. La ''Manchester carry chain'' est une optimisation de ce type, mais on ne peut pas encore expliquer à ce stade du cours, car elle optimisait les additionneurs complets au niveau des transistors eux-mêmes. Elle a été utilisée sur de nombreux processeurs connus, comme le 8086 d'Intel ou le Z80.
La seconde manière rend l'addition plus rapide dans certains cas. Le résultat est un additionneur dont le temps de calcul est variable. Le calcul prendra quelques cycles d'horloges avec certains opérandes, mais d'autres opérandes ne verront aucune amélioration. Il n'améliore pas le pire des cas, dans lequel la retenue doit être propagée du début à la fin, du bit de poids faible au bit de poids fort. Mais dans d'autres cas, il permet d'avoir le résultat en avance. L'optimisation en question donne l''''additionneur à saut de retenue''' (''carry-skip adder'').
Il est composé en enchainant plusieurs additionneurs plus petits, qui additionnent 4/5 bits, rarement plus. De tels additionneurs de petite taille sont appelés des '''blocs''', dans ce qui suit. L'idée est de détecter précocement si la retenue entrante est propagée à travers tout le bloc. Si la retenue est propagée dans tous le bloc, on peut directement l'envoyer sur la sortie de retenue, la retenue saute le bloc entier. Dans le cas contraire, on doit calculer la retenue normalement. Le choix entre les deux est le fait d'un multiplexeur. Toute la difficulté est de savoir comment commander le multiplexeur.
[[File:Carry skip adder, principe de base.png|centre|vignette|upright=2.5|Carry skip adder : principe de base]]
Pour savoir si une retenue est propagée dans un bloc entier, il faut utiliser des additionneurs complets, qui ont en plus une sortie P indiquant que l'additionneur propage la retenue. Le bloc propage une retenue si chaque additionneur complet propage la retenue, donc s'ils ont tous leur sortie P à 1. Un simple ET logique suffit à déterminer si c'est le cas. Le signal de commande du multiplexeur est donc un vulgaire ET entre toutes les sorties P des additionneurs complets du bloc.
[[File:Additionneur à saut de retenue.png|centre|vignette|upright=2.5|Calcul de la commande du MUX.]]
Voici ce que ça donne pour un bloc de 4 bits :
[[File:CSAdder4Bit.svg|centre|vignette|upright=1.5|Additionneur ''carry skip'' : bloc de 4 bits.]]
L'additionneur à saut de retenue est construit en assemblant plusieurs blocs de ce type.
[[File:BCSAdder16Bit.svg|centre|vignette|upright=3|Additionneur à saut de retenue.]]
Les blocs sont tous identiques dans le cas le plus simple, mais il est possible d'utiliser des blocs de taille variable. Par exemple, le premier bloc peut avoir des opérandes de 6 bits, le second des opérandes de 7 bits, etc. Faire ainsi permet de gagner un petit peu en performances, si la taille de chaque bloc est bien choisie. La raison est une question de temps de propagation des retenues. La retenue met plus de temps à se propager à travers 8 blocs qu'à travers 4, ce qui prend plus de temps qu'à travers 2 blocs, etc. En tenir compte fait que la taille des blocs tend à augmenter ou diminuer quand on se rapproche des bits de poids fort.
===L'additionneur à sélection de retenue===
L''''additionneur à sélection de retenue''' utilise aussi des blocs, comme les additionneurs précédents. L'addition se fait en deux versions : une avec la retenue du bloc précédent valant zéro, et une autre version avec la retenue du bloc précédent valant 1. Il suffira alors de choisir le bon résultat avec un multiplexeur, une fois cette retenue connue. On gagne ainsi du temps en calculant à l'avance les valeurs de certains bits du résultat, sans connaître la valeur de la retenue. Petit détail : sur certains additionneurs à sélection de retenue, les blocs de base n'ont pas la même taille. Cela permet de tenir compte des temps de propagation des retenues entre les blocs.
[[File:Additionneur à sélection de retenue.png|centre|vignette|upright=1.5|Additionneur à sélection de retenue avec seulement deux blocs.]]
Dans les exemples du dessus, chaque sous-additionneur étaient des additionneurs à propagation de retenue. Mais ce n'est pas une obligation, et tout autre type d’additionneur peut être utilisé. Par exemple, on peut faire en sorte que les sous-additionneurs soient eux-mêmes des additionneurs à sélection de retenue, et poursuivre ainsi de suite, récursivement. On obtient alors un '''additionneur à somme conditionnelle''', plus rapide que l'additionneur à sélection de retenue, mais qui utilise beaucoup plus de portes logiques.
===Les additionneurs à anticipation de retenue===
Les '''additionneurs à anticipation de retenue''' calculent chaque retenue sans avoir à propager les retenues précédentes. Au lieu de calculer les retenues une par une, ils calculent toutes les retenues en parallèle, à partir des bits des opérandes. Une fois les retenues pré-calculées, il suffit de les additionner avec les deux bits adéquats, pour obtenir le résultat. Ces additionneurs sont composés de deux parties :
* un circuit qui pré-calcule les retenues, sans les propager, directement à partir des opérandes ;
* d'une couche d'additionneurs complets, qui additionnent chacun deux bits d'opérande et la retenue pré-calculée.
[[File:Additionneur à anticipation de retenue.png|centre|vignette|upright=1.5|Additionneur à anticipation de retenue.]]
Le calcul des retenues se fait en deux étapes. La première détermine si les retenues sont propagées ou générées, pour chaque colonne. Elle détermine, pour chaque colonne, les bits P et G qui indiquent respectivement : qu'une retenue a été générée sur cette colonne, que la colonne propage la retenue précédente. La seconde étape utilise ces signaux P et G pour déterminer la retenue sur chaque colonne. Le circuit de calcul des retenues est donc composé de deux parties : une couche de demi-additionneurs pour générer les signaux P et G, un circuit de calcul des retenues proprement dit.
[[File:Circuit complet d'un additionneur à anticipation de retenue.png|centre|vignette|upright=1.5|Circuit complet d'un additionneur à anticipation de retenue.]]
Pour rappel, la retenue de la colonne i, notée <math>C_i</math> est égale à :
: <math>G_i + (P_i . C_{i-1})</math>, avec G le signal de génération de retenue, P le signal de propagation de retenue.
Si on utilisait cette formule sans trop réfléchir, on retomberait sur un additionneur à propagation de retenue inutilement compliqué. L'astuce des additionneurs à anticipation de retenue consiste à remplacer le terme Ci−1 par sa valeur calculée avant. Par exemple, je prends un additionneur 4 bits. Je dispose de deux nombres A et B, contenant chacun 4 bits : A3, A2, A1, et A0 pour le nombre A, et B3, B2, B1, et B0 pour le nombre B. Si j'effectue les remplacements, j'obtiens les formules suivantes :
* C1 = G0 + ( P0 · C0 ) ;
* C2 = G1 + ( P1 · G0 ) + ( P1 · P0 · C0 ) ;
* C3 = G2 + ( P2 · G1 ) + ( P2 · P1 · G0 ) + ( P2 · P1 · P0 · C0 ) ;
* C4 = G3 + ( P3 · G2 ) + ( P3 · P2 · G1 ) + ( P3 · P2 · P1 · G0 ) + ( P3 · P2 · P1 · P0 · C0 ).
Ces formules nous permettent de déduire la valeur d'une retenue directement : il reste alors à créer un circuit qui implémente ces formules, et le tour est joué.
[[File:Four bit adder with carry lookahead.svg|centre|vignette|upright=1.5|Additionneur à anticipation de retenue de 4 bits.]]
Plus haut, j'ai dit que le circuit était composé de trois couches : une couche de demi-additionneur pour les signaux P et G ; un circuit d'anticipation de retenue, puis l'addition finale. Mais les deux bits d'opérandes sont déjà additionnés dans la couche de demi-additionneur, autant réutiliser le résultat pour l'additionner à la retenue. Pour éliminer toute redondance de ce type, il est possible d'utiliser des additionneurs partiels (des additionneurs où la sortie de retenue a été remplacée par deux sorties P et G). L'additionneur final est alors composé de plusieurs additionneurs complets de ce type, couplé à un circuit d'anticipation de retenue.
[[File:Cla4bits.png|centre|vignette|upright=2.5|Additionneur à anticipation de retenue de 4 bits.]]
Les additionneurs à anticipation de retenue sont plus rapides que les additionneurs à propagation de retenue. La raison est que les retenues sont calculées rapidement, dans un temps plus petit qu'en propageant la retenue. Pour rentrer dans les détails mathématiques, le temps de calcul n'est pas proportionnel au nombre de bits, mais proportionnel au logarithme de ce nombre de bits. Par contre, le gain en temps de calcul s'accompagne d'un cout en portes logiques conséquent. Le circuit qui calcule les retenues anticipées utilise beaucoup de portes logiques.
===Les additionneurs à calcul parallèle de préfixe===
Du fait du cout en portes logiques, utiliser un additionneur à anticipation de retenue est une solution pratique pour additionner 4 ou 8 bits. Mais pour des opérandes plus grands, de 16 ou 32 bits, cela utiliserait trop de portes logiques. Une partie de ce cout en portes logiques est cependant lié à des redondances. Si on analyse deux retenues différentes, à des colonnes différentes. Les circuits qui calculent ces deux retenues seront différents, mais ils contiendront des sous-circuits identiques. Pour vous en rendre compte, reprenons les quatre équations obtenues précédemment et essayez de trouver des redondances :
* C1 = G0 + ( P0 · C0 ) ;
* C2 = G1 + ( P1 · G0 ) + ( P1 · P0 · C0 ) ;
* C3 = G2 + ( P2 · G1 ) + ( P2 · P1 · G0 ) + ( P2 · P1 · P0 · C0 ) ;
* C4 = G3 + ( P3 · G2 ) + ( P3 · P2 · G1 ) + ( P3 · P2 · P1 · G0 ) + ( P3 · P2 · P1 · P0 · C0 ).
Vous devriez trouver :
* le terme ''P0 · C0'' dans toutes les lignes ;
* les termes ''P1 · P0 · C0'' et ''P1 · P0'' dans les trois dernières lignes ;
* le terme ''P2 · P1 · P0 · C0'' ; ''P2 · P1 · G0'' et ''P2 · G1'' dans les deux dernières lignes.
Il existe plusieurs manières d'exploiter de telles redondances, exploitées dans les '''additionneurs à calcul parallèle de préfixe'''. L'optimisation apportée est de générer des signaux ''propagate'' et ''generate'' pour des groupes de 2, 3, 4, ..., N colonnes. Par exemple, il est possible de générer un signal ''P 0 vers 3'', qui indique si la retenue de la première colonne est propagée jusqu'à la 4ème colonne. Un autre exemple est un signal qui indique qu'une retenue a été générée entre les colonnes 4 à 7 génèrent une retenue ou non. En clair, les signaux P et G ont maintenant un intervalle, qui précise de quelle colonne vers quelle colonne se fait la propagation, ou entre quelles colonnes se fait la génération.
Il est possible de combiner les signaux P et G de deux groupes de colonne, s'ils sont contiguës (ils peuvent aussi se recouvrir). Par exemple, il est possible de calculer les bits P et G pour les colonnes 0 à 10, à partir des deux signaux P/G des colonnes 0-4 et 5-10. Pour cela, les équations sont assez simples. Si je prends deux groupes nommés A et B, avec A pour les colonnes de poids fort et B celles de poids faible, on a :
: <math>P_{sortie} = P_A . P_B</math>
: <math>G_{sortie} = G_A . P_A . G_B</math>
Pour les bits des colonnes 2, 4, 8, 16, 32, 64, et autres, le calcul est simple. L'idée est de grouper les colonnes par groupes de 2. Puis, on calcule chaque groupe de 4 bits à partir de deux groupes de 2. Puis les groupes de 8 bits sont calculés à partir de deux groupes de 4 bits, et ainsi de suite pour toute puissance de deux.
[[File:Squelette d'un additionneur à calcul parallèle de préfixe.jpg|centre|vignette|upright=2|Squelette d'un additionneur à calcul parallèle de préfixe]]
Les seules difficultés surviennent pour les colonnes dont le nombre n'est pas une puissance de deux. Il y a plusieurs manières pour subdiviser ces colonnes en intervalles plus petits et combiner le tout. Et elles donnent chacune des additionneurs différents. Ils portent les noms d''''additionneur de Ladner-Fisher''', d''''additionneur de Brent-Kung''', d''''additionneur de Kogge-Stone''', etc. Dans le cas général, l'additionneur de Kogge-Stone est un des plus rapide, mais aussi un des plus gourmand en circuits, alors que c'est l'inverse pour l'additionneur de Brent-Kung. Les deux additionneurs sont illustrés ci-dessous, pour des opérandes de 8 bits. Dans ceux-ci, chaque losange calculent les bits P et G, à partir des signaux P et G de deux groupes précédents.
{|
|[[File:Brent-kung-8-bit.png|vignette|Brent-kung-8-bit]]
|[[File:Kogge-stone-8-bit.png|vignette|upright=1.2|Kogge-stone-8-bit]]
|}
===Le calcul parallèle de la retenue===
L'anticipation de retenue est très rapide, mais utilise beaucoup de circuits, même en utilisant le calcul parallèle de préfixe. Les additionneurs à propagation de retenue font eux le compromis inverse. Mais il existe des intermédiaires, qui visent à obtenir un compromis entre performance et cout en portes logiques.
Un de ces compromis découpe un additionneur de 16/32/64 bits en additionneurs de 4/5 bits, qui sont enchainés en utilisant la propagation de retenue. Par contre, ces petits additionneurs de 4/5 bits utilisent l'anticipation de retenue. Ces additionneurs simples seront nommés ''blocs'' dans ce qui suit. Par exemple, on peut utiliser 4 additionneurs à anticipation de retenue de 4 bits, et propager les retenues entre eux. Le premier bloc calcule une retenue, qui est propagée au second bloc. Le second bloc calcule lui aussi une retenue, qui est propagée au troisième bloc, etc.
[[File:4008 Functional Diagram.svg|vignette|upright=1|4008 Functional Diagram]]
Le gain en performance est significatif, mais il est possible de faire presque aussi rapide, tout en économisant beaucoup de circuits. L'idée est de simplifier les additionneurs 4 bits, afin de ne pas utiliser une anticipation de retenue complète. L'anticipation de retenue n'est utilisée que pour la retenue sortante, alors que les bits de somme sont calculées avec propagation de retenue. La retenue sortante est donc calculée en parallèle de l'addition. Chaque bloc contient, à côté d'un additionneur proprement dit, un circuit qui calcule la retenue sortante. Il existe de nombreuses manières de calculer la retenue sortante.
Le gain en portes logiques est assez significatif. En effet, on se passe de circuits d'anticipation de retenue pour les 4 bits de somme, on ne garde que celui pour la retenue sortante. Le fait d'utiliser la propagation de retenue pour calculer les 4 bits de somme est censé avoir un léger cout en performance, mais il se trouve que ce cout est très faible. En effet, le résultat n'est connu qu'une fois les retenues propagées jusqu'au dernier bloc. Et malgré l'anticipation des retenues, la propagation est assez lente. les additionneurs à propagation de retenue auront terminé leur travail bien avant.
Un exemple est celui de l'additionneur CMOS 4008, un additionneur de 4 bit. Il est composé en trois sections. Une première couche de demi-additionneurs calcule les signaux P et G utilisés à la fois pour l'anticipation de la retenue sortante, que pour calculer les bits de somme. Le circuit de calcul de la retenue sortante utilise l'anticipation de retenue. Le reste du circuit fait l'addition en propageant les retenues.
[[File:CMOS 4008, circuit découpé en sections.png|centre|vignette|upright=1.5|CMOS 4008, circuit découpé en sections]]
===Les additionneurs à anticipation de retenue multi-niveau===
Les additionneurs précédents mixent anticipation et propagation de retenue. Ceux que nous allons voir utilisent des blocs, mais utilisent l'anticipation de retenue entre les blocs. Les blocs utilisés peuvent être un additionneur à propagation de retenue, ou à anticipation de retenue, peu importe. Ce qui compte est que la retenue entrante de chaque bloc est calculée par anticipation de retenue.
Par exemple, il est possible de créer un additionneur 16 bits en utilisant 4 additionneurs/blocs de 4 bits, couplé à un circuit d’anticipation de retenue par bloc. Pour cela, les additionneurs de 4 bits doivent fournir deux sorties P et G. Les deux sorties indiquent que le bloc soit a généré une retenue, soit a propagé la retenue entrante. Par propagé, on veut dire que la retenue entrante du bloc s'est retrouvée sur la sortie de retenue du bloc. C'est le cas si les 4 colonnes propagent la retenue, ce qui signifie que :
: <math>P_\text{4 bits} = P_0 \cdot P_1 \cdot P_2 \cdot P_3</math>
Pour la génération, cela veut dire qu'une colonne a générée une retenue, qui a été propagée jusqu'à la sortie. En clair, pour chaque colonne, il faut que le G = 1, et que les signaux P des colonnes soient à 1. Pour les 4 colonnes, cela donne :
: <math>G_\text{4 bits} = G_3 + G_2 \cdot P_3 + G_1 \cdot P_3 \cdot P_2 + G_0 \cdot P_3 \cdot P_2 \cdot P_1</math>
[[File:Cla4bitsPG.png|centre|vignette|upright=2|Additionneur à anticipation de retenue de 4 bits.]]
Pour combiner 4 blocs de 4 bits, on devra donc combiner 4 bits P et G, avec un circuit d'anticipation de retenue.
[[File:16-bit lookahead carry unit.svg|centre|vignette|upright=2|Additionneur à anticipation de retenue de 16 bits.]]
Il est même possible d'aller plus loin et de combiner 4 additionneurs précédents pour obtenir un additionneur 64 bits.
[[File:64-bit lookahead carry unit.svg|centre|vignette|upright=2|Additionneur à anticipation de retenue de 64 bits.]]
Le résultat est un additionneur à plusieurs niveaux d’anticipation de retenue. Une anticipation de retenue dans un blocs de 4 bits (facultative), entre blocs de 4 bits, entre blocs de 16 bits. La même logique peut être utilisée avec des blocs de taille différente de 4, 16 et 64 bits.
==L'addition signée et la soustraction==
Après avoir vu l'addition, il est logique de passer à la soustraction, les deux opérations étant très proches. Si on sait câbler une addition entre entiers positifs, câbler une soustraction n'est pas très compliqué. De plus, la soustraction permet de faire des additions de nombres signés.
===Le soustracteur à propagation de retenue===
[[File:Half Subtractor Vektor.svg|vignette|Demi-soustracteur.]]
Dans le chapitre sur les incrémenteurs/décrémenteurs, nous avons vu un circuit qui peut soustraire deux bits. Celui-ci est appelé un '''demi-soustracteur'''. Il ressemble beaucoup à un demi-additionneur, les différences se résumant à une porte NON ajoutée pour le calcul de la retenue. Il implémente la table de soustraction, qui est très simple en binaire :
* 0 - 0 = 0 ;
* 0 - 1 = 1 et une retenue ;
* 1 - 0 = 1 ;
* 1 - 1 = 0.
Pour soustraire deux nombres entiers, on peut adapter l'algorithme de soustraction utilisé en décimal, celui que vous avez appris à l'école. Celui-ci ressemble fortement à l'algorithme d'addition : on soustrait les bits de même poids, et on propage éventuellement une retenue sur la colonne suivante. À la différence de l'addition, la retenue est soustraite, et non ajoutée. En clair, une soustraction en binaire demande de faire deux soustraction : pour chaque bit de la première opérande, on soustrait le bit associé de la seconde, puis on soustrait la retenue entrante.
[[File:Algebra1 05 fig014.svg|centre|vignette|Soustraction en binaire, avec les retenues en rouge.]]
Pour cela, comme pour l'additionneur, deux demi-soustracteurs peuvent être combinés pour donner un '''soustracteur complet'''. Le calcul de la retenue se fait en combinant les deux retenues des demi-soustracteurs avec une porte OU.
[[File:FullSubtractor.svg|centre|vignette|upright=2|Soustracteur complet.]]
[[File:Sub Network 3 Bit Step 002.svg|vignette|upright=1|Soustracteur à propagation de retenue pour opérandes de 3 bits.]]
Les soustracteurs complets sont utilisés pour créer des soustracteurs à propagation de retenue ou tout autre circuit soustracteur, sur le même modèle que les additionneurs.
Un défaut est que cela ne fonctionne bien que pour les additionneurs/soustracteurs à propagation de retenue. L'anticipation de retenue et les autres optimisations ne marchent pas de la même manière entre addition et soustraction. Les signaux de propagation et de génération de retenue ne sont pas générés pareil, idem pour la manière de les combiner. Il est toujours possible de créer un soustracteur avec ces techniques, mais aucune mutualisation de circuit ne sera possible. Ce qui n'est pas le cas avec les solutions que nous allons voir dans ce qui suit.
Il est possible de fusionner un additionneur à propagation de retenue et un soustracteur de même type. Pour cela, il suffit de remarquer que les demi-soustracteurs et les demi-additionneurs se ressemblent beaucoup. La seule différence entre les deux est une vulgaire porte NON. En remplaçant cette porte NON par un inverseur commandable, on obtient un circuit qui sert soit de demi-additionneur, soit de demi-soustracteur, suivant ce qu'on envoie sur l'entrée de commande. Ci-dessous est illustré ce que donne ce remplacement pour un soustracteur complet. Le résultat est un '''additionneur/soustracteur à propagation de retenue'''.
[[File:Additionneur-soustracteur complet.png|centre|vignette|upright=2|Additionneur-soustracteur complet]]
===L'additionneur-soustracteur pour opérandes codées en complément à deux===
Étudions le cas de la soustraction en complément à deux, dans l'objectif de créer un circuit soustracteur. Vous savez sûrement que a−b et a+(−b) sont deux expressions équivalentes. Et en complément à deux, − b = not(b) + 1. Dit autrement, a − b = a + not(b) + 1. On pourrait se dire qu'il faut deux additionneurs pour faire le calcul, mais la majorité des additionneurs possède une entrée de retenue pour incrémenter le résultat de l'addition. Un soustracteur en complément à deux est donc simplement composé d'un additionneur et d'un inverseur.
[[File:Additionneur soustracteur.png|centre|vignette|upright=2|Soustracteur en complément à deux.]]
Il est possible de créer un circuit capable d'effectuer soit une addition, soit une soustraction : il suffit de remplacer l'inverseur par un inverseur commandable, qui peut être désactivé. On a vu comment créer un tel inverseur commandable dans le chapitre sur les circuits combinatoires. On peut remarquer que l'entrée de retenue et l'entrée de commande de l'inverseur sont activées en même temps : on peut fusionner les deux signaux en un seul.
[[File:Additionneur-soustracteur en complément à deux.png|centre|vignette|upright=2|Additionneur-soustracteur en complément à deux.]]
Une implémentation alternative est la suivante. Elle remplace l'inverseur commandable par un multiplexeur.
[[File:4-bit ripple carry adder-subtracter.svg|centre|vignette|upright=2|Additionneur-soustracteur en complément à deux, version alternative.]]
===L'additionneur-soustracteur pour opérandes codées en signe-magnitude===
Passons maintenant aux nombres codés en signe-valeur absolue, les deux opérandes étant notées A et B. Suivant les signes des deux opérandes, on a quatre cas possibles : A + B, A − B (B négatif), −A + B (A négatif) et −A − B (A et B négatifs). Une astuce est que le circuit n'a besoin que de calculer A + B et A − B : il peut les inverser pour obtenir − A − B ou B − A. A + B et A − B peuvent se calculer avec un additionneur-soustracteur, reste à corriger le résultat. Il suffit de lui ajouter un inverseur commandable pour obtenir le circuit d'addition finale.
[[File:Additionneur en signe-valeur absolue.png|centre|vignette|upright=2|Additionneur en signe-valeur absolue.]]
Toute la difficulté tient dans le calcul du bit de signe du résultat, quand interviennent des soustractions. Autant l'addition de deux nombres de même signe (A + B et −A − B) ne pose aucun problème, autant les soustractions posent problème (A − B et −A + B). Suivant que <math>A<B</math> ou que <math>A>B</math>, le signe du résultat ne sera pas le même. Déterminer le signe du résultat se fait en regardant les bits de débordement d'entier, comme on le verra plus bas.
===L'additionneur-soustracteur pour opérandes codées en représentation par excès===
Passons maintenant aux nombres codés en représentation par excès. On pourrait croire que ces nombres s'additionnent comme des nombres non-signés, mais ce serait oublier la présence du biais, qui pose problème. Dans les cas de nombres signés gérés avec un biais, voyons ce que donne l'addition de deux nombres :
: <math>( n_1 + biais ) + ( n_2 + biais ) = n_1 + n_2 + 2 \times biais</math>
Or, le résultat correct serait :
: <math>n_1 + n_2 + biais</math>
En effectuant l'addition telle quelle, le biais est compté deux fois. On doit donc le soustraire après l'addition pour obtenir le résultat correct.
Même chose pour la soustraction qui donne ceci :
: <math>(n_1 + biais) - (n_2 + biais) = n_1 - n_2</math>
Or, le résultat correct serait :
: <math> ( n_1 - n_2 ) + biais </math>
Il faut rajouter le biais pour obtenir l'exposant correct.
On a donc besoin de deux additionneurs/soustracteurs : un pour additionner/soustraire les représentations binaires des opérandes, et un autre pour ajouter/retirer le biais en trop/manquant.
==L'additionneur BCD==
Maintenant, voyons un additionneur qui additionne deux entiers au format BCD. Pour cela, nous allons devoir passer par deux étapes. La première est de créer un circuit capable d'additionneur deux chiffres BCD. Ensuite, nous allons voir comment enchaîner ces circuits pour créer un additionneur BCD complet.
===L'additionneur BCD qui fait l'opération chiffre par chiffre===
Nous allons commencer par voir un additionneur qui additionne deux chiffres en BCD, une sorte d'équivalent BCD de l'additionneur complet. Il fournit un résultat sur 4 bits et une retenue qui est mise à 1 si le résultat dépasse 10 (la limite d'un chiffre BCD). Les deux opérandes sont des chiffres BCD codés sur 4 bits et sont additionnés en binaire par un additionneur des plus normaux, similaire à ceux vus plus haut. Le résultat est alors un entier codé en binaire, sur 5 bits, qu'on corrige/convertit pour obtenir un chiffre BCD et une retenue sortante.
Pour corriger le résultat, une idée intuitive serait de prendre le résultat et de faire une division par 10. Le quotient donne la retenue, alors que le reste est le résultat, le chiffre BCD. Mais un circuit diviseur par 10 utilise beaucoup de portes logiques, ce qui ne vaut pas le coup. Une autre méthode détecte si le résultat est égal ou supérieur à 10, ce qui correspond à un "débordement" (on dépasse les limites d'un chiffre BCD). Si le résultat est plus petit que 10, il n'y a rien à faire : le résultat est bon et la retenue est de zéro. Par contre, si le résultat vaut 10 ou plus, il faut corriger le résultat et générer une retenue à 1.
Il faut donc ajouter un circuit qui détecte si le résultat est supérieur à 9, qui calcule directement la retenue. Ce circuit peut se fabriquer simplement à partir de sa table de vérité, ou en utilisant les techniques que nous verrons dans un chapitre ultérieur sur les comparateurs. La solution la plus simple est clairement d'utiliser la table de vérité, ce qui est très simple, assez pour être laissé en exercice au lecteur. Pour comprendre comment corriger le résultat, établissons une table de vérité qui associe le résultat et le résultat corrigé. L'entrée vaut au minimum 10 et au maximum 9 + 9 = 18. On considère la sortie comme un tout, la retenue étant un 5ème bit, le bit de poids fort.
{|class="wikitable"
|-
! colspan="5" | Entrée
!
! rowspan="10" |
! Retenue
! Résultat corrigé (sans retenue)
! interprétation de la sortie en binaire (retenue inclue)
|-
| 0 || 1 || 0 || 1 || 0
| (10)
| 1 || 0000
| (16)
|-
| 0 || 1 || 0 || 1 || 1
| (11)
| 1 || 0001
| (17)
|-
| 0 || 1 || 1 || 0 || 0
| (12)
| 1 || 0010
| (18)
|-
| 0 || 1 || 1 || 0 || 1
| (13)
| 1 || 0011
| (19)
|-
| 0 || 1 || 1 || 1 || 0
| (14)
| 1 || 0100
| (20)
|-
| 0 || 1 || 1 || 1 || 1
| (15)
| 1 || 0101
| (21)
|-
| 1 || 0 || 0 || 0 || 0
| (16)
| 1 || 0110
| (22)
|-
| 1 || 0 || 0 || 0 || 1
| (17)
| 1 || 0111
| (23)
|-
| 1 || 0 || 0 || 1 || 0
| (18)
| 1 || 1000
| (24)
|}
En analysant le tableau, on voit que pour corriger le résultat, il suffit d'ajouter 6. La raison est que le résultat déborde d'un nibble à 16 en binaire, mais à 10 en décimal : il suffit d'ajouter la différence entre les deux, à savoir 6, et le débordement binaire fait son travail. Donc, la correction après une addition est très simple : si le résultat dépasse 9, on ajoute 6.
On peut maintenant implémenter l'additionneur BCD, en combinant le comparateur avec 10, le circuit de correction, et l'additionneur. La première solution calcule deux versions du résultat : la version corrigée, la version normale. Le choix entre les deux est réalisée par un multiplexeur, commandé par le comparateur.
[[File:Additionneur BCD.png|centre|vignette|upright=1.5|Additionneur BCD]]
L'autre solution utilise un circuit commandable qui soit additionne 6, soit ne fait rien. Le choix entre les deux est commandé par le bit calculé par le comparateur.
[[File:Additionneur BCD, seconde version.png|centre|vignette|upright=1.5|Additionneur BCD, seconde version.]]
Une version alternative du circuit précédent est la suivante. Il contient deux additionneurs : un pour additionner les deux chiffres BCD, un autre pour additionner 6 si besoin. Le résultat du comparateur est directement utilisé pour générer l'opérande du second additionneur : 0 ou 6. Le circuit est simple à concevoir, mais gaspille beaucoup de circuit. Idéalement, il vaudrait mieux utiliser un circuit combinatoire d'addition avec une constante.
[[File:Adder 4 Bit BCD.svg|centre|vignette|upright=2|Additionneur BCD, circuit complet.]]
Pour obtenir un additionneur BCD complet, il suffit d’enchaîner les additionneurs précédents, comme on le ferait avec les additionneurs complets dans un additionneur à propagation de retenue. Au final, l'additionneur BCD est beaucoup plus compliqué qu'un additionneur normal, car il rajoute un comparateur ">9", un petit additionneur pour ajouter 6 et éventuellement d'autres circuits. De plus, il est difficile d'appliquer les optimisations disponibles sur les additionneurs non-BCD. Notamment, les circuits d'anticipation de retenue sont totalement à refaire et le résultat est relativement compliqué. C'est ce qui explique pourquoi le BCD a progressivement été abandonné au profit du binaire simple.
La soustraction en BCD se fait comme en binaire : le nombre à soustraire est remplacé par son complément, le circuit additionne le complément et l'autre opérande, le débordement d'entier fait que le résultat marche. Sauf qu'ici, le complément est un complément à 9. Il se calcule chiffre par chiffre : chaque chiffre est remplacé par (9 - le chiffre en question).
===L'additionneur BCD par ajustement décimal===
L'additionneur BCD précédent effectuait son travail chiffre BCD par chiffre BCD, mais il existe des additionneurs BCD qui font autrement. Sur les premiers processeurs x86, il n'y avait pas d'opération d'addition BCD proprement dit, seulement une addition binaire normale de 8, 16 ou 32 bits. Par contre, elle était secondée par une opération dite d''''ajustement décimal''' qui transformait un nombre binaire en nombre codé en BCD. L'opération d'ajustement décimal prenait un opérande de 8 bits codé en binaire et fournissait un résultat de la même taille, c'est à dire deux chiffres BCD. Effectuer une addition BCD demandait donc de faire deux opérations à la suite : une addition binaire simple, suivie par l'opération d'ajustement décimal. Cela permettait de gérer des nombres entiers en binaire usuel et des entiers BCD sans avoir deux instructions d'addition séparées pour les deux, sans compter que cela simplifiait aussi les circuits d'addition.
L'ajustement décimal s'effectue en ajoutant une constante bien précise à l'opérande à convertir en BCD. L'idée est que la constante est découpée en morceaux de 4 bits, correspondant chacun à un chiffre BCD de l'opérande, chaque morceau contenant soit un 0, soit 6. Cela permet d'ajouter soit 0, soit 6, à chaque chiffre BCD, et donc de le corriger. La propagation des retenues d'un chiffre à l'autre est effectuée automatiquement par l'addition binaire de la constante. L'opération d'ajustement décimal calcule automatiquement la constante. Elle découpe l'opérande en ''nibbles'', vérifie si chaque ''nibble'' est supérieur ou égal à 10, puis détermine la valeur de chaque ''nibble'' de la constante finale. Par exemple, si je prends l'opérande 1001 1110, le ''nibble'' de poids faible déborde, alors que celui de poids fort non. La constante sera donc 0000 0110 : 0x06. Inversement, si le ''nibble'' de poids fort déborde et pas celui de poids faible, la constante sera alors 0x60. Et la constante est de 0x66 si les deux ''nibbles'' débordent, de 0x00 si aucun ne déborde.
Le circuit d’ajustement décimal est donc composé de trois étapes : deux étapes pour calculer la constante, et un circuit d'addition pour additionner cette constante au nombre de départ. La première étape découpe l'opérande en morceaux de 4 bits, en chiffres BCD, et vérifie si chacun d'entre eux vaut 10 ou plus. La seconde étape prend les résultats de la première étape, et les combine pour calculer la constante. Enfin, on trouve l'addition finale, qui était réalisée par un circuit d'addition utilisé à la fois pour l'ajustement décimal et l'addition binaire. La différence entre une addition normale et une opération d'ajustement décimal tient dans le fait que les deux premières étapes sont désactivées dans une addition normale.
[[File:Additionneur BCD parallèle.png|centre|vignette|upright=1.5|Additionneur BCD parallèle]]
===L'additionneur biquinaire===
Les entiers BCD ne sont qu'un des encodages hybrides entre décimal et binaire. L'encodage biquinaire est l'un d'entre eux et nous allons faire un rappel rapide à ce sujet. Pour simplifier, un chiffre encodé en biquinaire est composé de deux parties : un bit, couplé à une partie quinaire encodée en représentation ''one-hot''. La partie quinaire encode un nombre allant de 0 à 4, ce qui prend 5 bits (0, 1, 2, 3 et 4). Le bit indique s'il faut ou non ajouter 5 à la valeur encodée par la partie quinaire. Ainsi, on peut coder tous les nombres de 0 à 9.
Additionner deux nombres de biquinaire demande donc d'additionner deux parties quinaires encodées en ''one-hot'' et d'additionner deux bits. Mais attention : il faut tenir compte de la retenue de l'addition des parties quinaires. Et idéalement, il faut aussi tenir compte d'une retenue entrante, provenant de l'addition de la colonne de chiffres précédente. Toute la difficulté vient de la création de l'additionneur ''one-hot''. Heureusement, vu qu'il n'y a que 4-5 bits à additionner, il est souvent fabriqué à partir de sa table de vérité.
[[File:Additionneur bi-quinaire.png|centre|vignette|upright=2|Additionneur bi-quinaire]]
Un avantage du biquinaire est que le calcul du complément à 9 est très simple. Il faut pour cela : inverser la partie binaire avec une porte NON, puis inverser l'ordre des bits de la partie quinaire. Concrètement, le bit de poids faible devient le bit de poids fort, et ainsi de suite. Par exemple, une partie quinaire 01000 devient 00010, 10000 devient 00001, 00100 ne change pas, etc. Le tout peut se calculer avec une porte NON et 5 multiplexeurs.
===L'additionneur BCD avec calculs intermédiaires en biquinaire===
L'ordinateur IBM 1401, un ancien ''mainframe'' des années 60, utilisait un additionneur BCD un peu particulier. Les nombres étaient encodés en BCD dans la mémoire de l'ordinateur, mais les circuits de calcul utilisaient la représentation biquinaire. Lors d'un calcul, le processeur de l'ordinateur traduisait les chiffres BCD en représentation biquinaire, faisait une addition en biquinaire, avant de traduire le résultat en BCD normal.
Pour être précis, l'IBM 1401 utilisait une variante du biquinaire. L'encodage biquinaire de l'IBM 1401 est le suivant : la partie binaire disait si le chiffre était pair ou non, la partie quinaire encodait les valeurs 0, 2, 4, 6 et 8. Le chiffre se calculait en additionnant la partie binaire (0 ou 1) au nombre pair encodé par la partie quinaire. Si l'IBM 1401 utilisait cette variante du biquinaire, c'est car elle donnait des circuits de conversion BCD-biquinaire plus économes en portes logiques et plus rapides.
La partie binaire est le bit de poids faible du chiffre BCD, la partie biquinaire est calculée par un simple décodeur qui prend en entrée le chiffre BCD, amputé de son bit de poids faible. La traduction inverse demande d'utiliser un encodeur, à la place du décodeur. Par contre, le circuit d'addition biquinaire était plus compliqué du fait de la gestion des retenues. L'addition des parties binaires et quinaires se faisait en parallèle, dans deux additionneurs séparés. Cependant, l'addition des parties binaire fournit une retenue, qu'il faut prendre en compte. Pour cela, l'IBM 1401 disposait d'un troisième additionneur qui fournissait le résultat final, encodé en biquinaire.
[[File:Additionneur bi-quinaire de l'IBM 1401.png|centre|vignette|upright=2|Additionneur biquinaire de l'IBM 1401]]
Une implémentation moderne demanderait d'utiliser des portes ET combinées à des portes OU, le circuit pouvant être construit simplement à partir de sa table de vérité. Sur l'IBM 1401, le circuit était cependant différent, en raison de l'utilisation de OU câblés, des croisements de fils qui fonctionnent comme des portes OU, que nous n'avons pas encore vu pour le moment, mais qui seront détaillés dans quelques chapitres. Les OU câblés étaient utilisés pour simplifier le design du circuit, mais demandaient des portes logiques spécifiques, ce qui collait avec le fait que ce ''mainframe'' utilisait des transistors en Germanium. L'implémentation exacte est décrite dans cet article de blog, mais je ne recommande sa lecture qu'à ceux qui savent ce qu'est un OU câblé :
* [https://www.righto.com/2015/10/qui-binary-arithmetic-how-1960s-ibm.html Qui-binary arithmetic: how a 1960s IBM mainframe does math].
==Les débordements d'entier lors d'une addition/soustraction==
Les instructions arithmétiques manipulent des entiers codés sur un nombre fixe de bits, qui ne peuvent prendre leurs valeurs que dans un intervalle. Pour les nombres positifs, un ordinateur qui code ses entiers sur n bits pourra coder tous les entiers allant de 0 à <math>2^n -1</math>. Pour les nombres négatifs, l'intervalle est différent et dépend de la représentation utilisée. Dans le cas général, l'ordinateur peut coder les valeurs comprises de <math>N_\text{min}</math> à <math>N_\text{max}</math>. Si le résultat d'un calcul sort de cet intervalle, il ne peut pas être représenté par l'ordinateur et il se produit ce qu'on appelle un '''débordement d'entier'''.
La '''valeur haute de débordement''' désigne la première valeur qui est trop grande pour être représentée par l'ordinateur. Par exemple, pour un ordinateur qui peut coder tous les nombres entre 0 et 7, la valeur haute de débordement est égale à 8. On peut aussi définir la '''valeur basse de débordement''', qui est la première valeur trop petite pour être codée par l'ordinateur. Par exemple, pour un ordinateur qui peut coder tous les nombres entre 8 et 250, la valeur basse de débordement est égale à 7. Pour les nombres entiers, la valeur haute de débordement vaut <math>N_\text{max} + 1</math> , alors que la valeur basse vaut <math>N_\text{min} - 1</math> (avec <math>N_\text{max}</math> et <math>N_\text{min}</math> respectivement la plus grande et la plus petite valeur codable par l'ordinateur).
===La correction des débordements d'entier : l'arithmétique saturée===
Quand un débordement d'entier survient, tous les circuits de calcul ne procèdent pas de la même manière. Dans les grandes lignes, il y a deux réactions possibles : soit on corrige automatiquement le résultat du débordement, soit on ne fait rien et on se contente de détecter le débordement.
Si le débordement n'est pas corrigé automatiquement, le circuit ne conserve que les bits de poids faibles du résultat. Les bits en trop sont simplement ignorés. On dit qu'on utilise l''''arithmétique modulaire'''. Le problème avec ce genre d'arithmétique, c'est qu'une opération entre deux grands nombres peut donner un résultat très petit. Par exemple, si je dispose de registres 4 bits et que je souhaite faire l'addition 1111 + 0010 (ce qui donne 15 + 2), le résultat est censé être 10001 (17), ce qui est un résultat plus grand que la taille d'un registre. En conservant les 4 bits de poids faible, j’obtiens 0001 (1). En clair, un résultat très grand est transformé en un résultat très petit. Cela peut poser problèmes si on travaille uniquement avec des nombres positifs, mais c'est aussi utilisé pour coder des nombres en complément à deux.
D'autres circuits utilisent ce qu'on appelle l''''arithmétique saturée''' : si un calcul génère un débordement, on arrondi le résultat au plus grand entier supporté par le circuit. Les circuits capables de calculer en arithmétique saturée sont un peu plus complexes, vu qu'il faut rajouter des circuits pour corriger le résultat en cas de débordement. Il suffit généralement de rajouter un ''circuit de saturation'', qui prend en entrée le résultat et le corrige en cas de débordement. Ce circuit de saturation met la valeur maximale en sortie si un débordement survient, mais se contente de recopier le résultat du calcul sur sa sortie s'il n'y a pas de débordement. Typiquement, il est composé d'une couche de multiplexeurs, qui sélectionnent quelle valeur mettre sur la sortie : soit le résultat du calcul, soit le plus grand nombre entier géré par le processeur, soit le plus petit (pour les nombres négatifs/soustractions).
L'arithmétique saturée est utilisée pour les additions et soustractions, mais c'est plus rare pour les multiplications/divisions. Une des raisons est que le résultat d'une addition/soustraction prend un bit de plus que le résultat, là où les multiplications doublent le nombre de bits. Quand une addition déborde, le résultat réel est proche de la valeur maximale codable. mais quand une multiplication déborde, le résultat peut parfois valoir 200 à 60000 fois plus que la valeur maximale codable. Les calculs avec une valeur saturée/corrigée sont donc crédibles pour une suite d'additions, mais pas pour une suite de multiplications.
===La détection des débordements entiers===
Quand un débordement d'entier a eu lieu, il vaut mieux que l'additionneur prévienne ! Pour cela, l'additionneur a une '''sortie de débordement''', parfois nommée ''Overflow'', dont la valeur indique si l'addition a généré un débordement d'entier ou non. Reste que détecter un débordement ne se fait pas de la même manière selon que l'on parle d'un additionneur non-signé ou signé.
Pour les additionneur non-signés, l'additionneur calcule un bit de plus que ce qui est supporté par l'ordinateur. Par exemple, un additionneur 32 bits fournit un résultat sur 33 bits, un débordement d'entier a lieu quand le 33ème bit est à 1. Précisément, la sortie de débordement n'est autre que la retenue finale, celle fournie par le dernier additionneur complet. Le seul type de débordement possible est un débordement par le haut, où le résultat dépasse la valeur maximale. Avec l'arithmétique saturée, le circuit de saturation consiste en une seule couche de multiplexeurs, voire en un circuit de mise à la valeur maximale tel que vu dans le chapitre sur les opérations bits à bits.
[[File:Gestion des débordements d'entiers.png|centre|vignette|upright=2.5|Gestion des débordements d'entiers lors d'une addition non-signée.]]
Pour les additionneurs non-signés, la gestion des débordements d'entiers dépend fortement de la représentation signée. Nous allons étudier le cas du complément à deux. Si vous vous rappelez le chapitre 1, les calculs sur des nombres en complètement à deux utilisent les règles de l'arithmétique modulaire, c'est une condition nécessaire. À priori, on peut penser que dans ces conditions, les débordements d'entiers sont une chose parfaitement normale, qui nous permet d'avoir des résultats corrects. Néanmoins, certains débordements d'entiers peuvent survenir malgré tout et produire des bugs assez ennuyeux.
Si l'on tient en compte les règles du complément à deux, on sait que le bit de poids fort (le plus à gauche) permet de déterminer si le nombre est positif ou négatif : il indique le signe du nombre. Tout se passe comme si les entiers en complément à deux étaient codés sur un bit de moins, et avaient leur longueur amputé du bit de poids fort. Si le résultat d'un calcul écrase le bit de poids fort, il y a un débordement d'entiers. Il existe une règle simple qui permet de détecter ces débordements d'entiers. L'addition de deux nombres positifs ne peut pas être un nombre négatif. Si on additionne deux nombres dont le bit de signe est à 0 et que le bit de signe du résultat est à 1, on est en face d'un débordement d'entiers. Même chose pour deux nombres négatifs : le résultat de l'addition ne peut pas être positif. On peut résumer cela en une phrase : si deux nombres de même signe sont ajoutés, un débordement a lieu quand le bit du signe du résultat a le signe opposé.
Modifier les circuits d'au-dessus pour qu'ils détectent les débordements en complément à deux est simple comme bonjour : il suffit créer un petit circuit combinatoire qui prenne en entrée les bits de signe des opérandes et du résultat, et qui fasse le calcul de l'indicateur de débordements. Si l'on rédige sa table de vérité, on doit se retrouver avec la table suivante :
{|class="wikitable"
|-
!Entrées
!Sortie
|-
|000||0
|-
|001||1
|-
|010||0
|-
|011||0
|-
|100||0
|-
|101||0
|-
|110||1
|-
|111||0
|}
L'équation de ce circuit est la suivante, avec <math>S_a</math> et <math>S_b</math> les signes des deux opérandes, et <math>C_i</math> la retenue de la colonne précédente :
: <math> ( S_a . S_b . \overline{C_i} ) + ( \overline{S_a} . \overline{S_b} . C_i )</math>
En simplifiant, on obtient alors :
: <math>( S_a . S_b ) \oplus C_i</math>
Or, il se trouve que <math>S_a . S_b</math> est tout simplement la retenue en sortie du dernier additionneur, que nous noterons <math>C_o</math>. On trouve donc :
: <math>C_o \oplus C_i</math>
Il suffit donc de faire un XOR entre la dernière retenue et la précédente pour obtenir le bit de débordement.
<noinclude>
{{NavChapitre | book=Fonctionnement d'un ordinateur
| prev=Les circuits de décalage et de rotation
| prevText=Les circuits de décalage et de rotation
| next=Les circuits de comparaison
| nextText=Les circuits de comparaison
}}
</noinclude>
1njdm1gpy3q6zwupvtchw1meih9gooy
768775
768771
2026-06-27T13:11:51Z
Mewtow
31375
/* Le soustracteur à propagation de retenue */
768775
wikitext
text/x-wiki
Dans ce chapitre, nous allons voir les circuits capables de faire une addition ou une soustraction, ainsi que quelques circuits spécialisés. Précisons cependant que les fabricants de processeurs travaillent d'arrache-pied pour trouver des moyens de rendre ces circuits de calcul plus rapides et plus économes en énergie. Autant vous dire que les circuits que vous allez voir sont vraiment des circuits qui font pâle figure comparé à ce que l'on peut trouver dans un vrai processeur commercial !
==Les circuits pour additionner 2 ou 3 bits==
L'addition se fait en binaire de la même manière qu'en décimal. On additionne les chiffres/bits colonne par colonne, une éventuelle retenue est propagée à la colonne d'à côté. La soustraction fonctionne sur le même principe, sur le même modèle qu'en décimal.
[[File:Binary Addition Demonstration.svg|centre|vignette|Exemple d'addition en binaire.]]
En clair, additionner deux nombres demande d'additionner 2 bits et une retenue sur chaque colonne, et de propager les retenues d'une colonne à l'autre. La propagation des retenues est quelque chose de simple en apparence, mais qui est sujet à des optimisations extraordinairement nombreuses. Aussi, pour simplifier l'exposition, nous allons voir comment gérer une colonne avant de voir comment sont propagées les retenues.
[[File:Full Adder Block.svg|vignette|upright=1|class=transparent|Additionneur complet.]]
Si on effectue une addition en colonne, on doit additionner les deux bits sur la colonne, mais aussi additionner une éventuelle retenue. Il faut donc créer un circuit qui additionne trois bits : deux bits de données, plus une retenue. Ce circuit qui additionne trois bits est appelé un '''additionneur complet'''. Il fournit en sortie deux bits : un bit de somme et une retenue sortante. Voici sa table de vérité :
{|class="wikitable"
|-
! Retenue entrante !! Opérande 1 !! Opérande 2 !! !! Retenue sortante !! Bit de somme
|-
||0||0||0|| ||0||0
|-
||0||0||1|| ||0||1
|-
||0||1||0|| ||0||1
|-
||0||1||1|| ||1||0
|-
||1||0||0|| ||0||1
|-
||1||0||1|| ||1||0
|-
||1||1||0|| ||1||0
|-
||1||1||1|| ||1||1
|}
Il est possible d'utiliser un tableau de Karnaugh pour traduire la table de vérité, mais elle donne un résultat assez compliqué. La retenue et le bit de somme sont calculés à part. La retenue est calculée avec trois portes ET et une porte OU. Le bit de somme est calculé lui avec 4 portes ET, une porte OU, et trois portes NON.
[[File:Full Adler with 3 entries.png|centre|vignette|upright=2|Additionneur complet, fabriqué à partir de la table de vérité.]]
Le circuit obtenu est pas loin d'être optimal, que ce soit en termes de rapidité que de portes logiques utilisées. Mais il est assez compliqué à comprendre, vu que c'est une traduction littérale de la table de vérité. D'autres implémentations alternatives donnent des résultats plus compréhensibles. Nous allons les voir dans la suite de cette section.
===L'additionneur complet basé sur des demi-additionneurs===
[[File:1-bit half-adder.svg|class=transparent|vignette|Demi-addtionneur.]]
Nous avons déjà vu comment additionner deux bits dans le chapitre sur les incrémenteurs, mais quelques rappels ne seront pas de trop. Le '''demi-additionneur''' est un circuit qui additionne deux bits. Il implémente la table d'addition, qui est très simple en binaire. Jugez plutôt :
* 0 + 0 = 0, retenue = 0 ;
* 0 + 1 = 1, retenue = 0 ;
* 1 + 0 = 1, retenue = 0 ;
* 1 + 1 = 0, retenue = 1.
Mais un demi-additionneur n'additionne que deux bits, il manque de quoi additionner la retenue. Une solution pour cela est d'enchaîner deux demi-additionneurs : un qui additionne les deux bits de données, et un second qui additionne la retenue au résultat du précédent. La retenue finale se calcule en combinant les sorties de retenue des deux demi-additionneurs, avec une porte OU. Le résultat est un additionneur complet fabriqué avec deux demi-additionneurs. Pour vous en convaincre, établissez la table de vérité de ce circuit, vous verrez que ça marche.
[[File:Full Adder Blocks.svg|centre|vignette|upright=2|Additionneur complet fabriqué avec deux demi-additionneurs.]]
[[File:Half-adder.svg|vignette|upright=1|Circuit d'un demi-additionneur naïf.]]
L'implémentation naïve d'un demi-additionneur utilise une porte XOR et une porte ET, comme illustré ci-contre. Le circuit s'obtient en faisant uen traduction littérale de la table de vérité du circuit. Si je dis qu'elle est naïve, c'est car il est possible de l'optimiser, de manière à éliminer des redondances cachées. Mais nous avions déjà vu cela dans le chapitre sur les incrémenteurs, aussi laissons cela pour plus tard.
En combinant deux demi-additionneurs de ce type, on obtient l'additionneur complet suivant. Les deux sorties de retenue des demi-additionneurs sont combinées avec une porte OU, pour calculer la retenue finale.
{| class="flexible"
|[[File:Full Adder Modules.svg|class=transparent|Composition d'un additionneur complet. On voit bien que celui-ci est composé de deux demi-additionneurs, en rouge et en bleu, auxquels on a ajouté une porte OU pour calculer la retenue finale.]]
|[[File:Full-adder.svg|300px|class=transparent|Circuit d'un additionneur complet.]]
|}
Le fait de combiner les deux retenues avec une porte OU n'est pas ce qu'il y a de plus intuitif. La table de vérité nous dit que ça fonctionne, mais on comprend mal pourquoi. Mais tout devient plus clair quand on sait que les deux retenues ne peuvent pas être à 1 en même temps. Même dans l'addition 1 + 1 + 1, seule une retenue est à 1. Soit l'addition des deux bits d'opérande donne naissance à une retenue, soit c'est l'addition ''résultat + retenue entrante''. La retenue sortant est donc à 1 quand une des deux addition donne une retenue. D'où l'usage d'une porte OU. Remarquez qu'une porte XOR donne le même résultat, vu que le cas où les deux retenues sont à 1 n'est jamais rencontré.
===La propagation et la génération des retenues===
L'additionneur complet que nous allons voir dans cette section sert à introduire les concepts de propagation, génération et absorption de retenue. Il s'agit de concepts très importants quand on étudie les additionneurs. l'idée part d'un principe très simple : la retenue sortante dépend de la retenue d'entrée. La relation entre les deux se résume à trois cas, qui dépendent de la valeur des deux bits additionnés, nommés A et B.
* Dans le premier cas, la retenue entrante est égale à la retenue sortante. On dit que la retenue entrante est propagée par l'additionneur.
* Dans le second cas, la retenue est forcée à 0 : la retenue sortante vaut 0, peu importe la valeur de la retenue entrante.
* Dans le troisième cas, la retenue est forcée à 1 : la retenue entrante vaut 1, peu importe la valeur de la retenue entrante.
[[File:Signaux P et G fournis par un demi-additionneur.png|vignette|Signaux P et G fournis par un demi-additionneur]]
Maintenant, créons un circuit qui nous dise si une retenue est propagée ou générée. Il a deux bits de sortie, nommés P et G : P pour ''Propagate'', G pour ''Generate''. Le bit P est à 1 si la retenue entrante est propagée, il est à 0 sinon. Le bit G est à 1 si une retenue est générée, à 0 sinon. Une retenue est considérée comme absorbée si elle n'est pas ni propagée ni générée, pas besoin d'un troisième bit pour gérer ce cas.
Il se trouve que ce circuit n'est autre qu'un demi-additionneur ! Pour vous en rendre compte, regardez la table de vérité d'un additionneur complet, illustrée ci-dessous. Lorsque les deux bits d'opérande sont à 0, la retenue sortante vaut toujours 0. Si ils sont tous deux à 1, alors la retenue sortante vaut 1. S'ils sont différents, alors retenues sortante et entrante sont égales. Le bit P est donc généré par une simple porte XOR. Quant au bit G, il est à 1 si les deux bits d'opérandes sont à 1, ce qui correspond à une porte ET. Il se trouve que ces deux portes forment un demi-additionneur !
{|class="wikitable"
|-
! Opérande 1 !! Opérande 2 !! Retenue entrante !! !! Retenue sortante
|- class="f_rouge"
||0||0||0|| ||0
|- class="f_rouge"
||0||0||1|| ||0
|- class="f_bleu"
||0||1||0|| ||0
|- class="f_bleu"
||0||1||1|| ||1
|- class="f_bleu"
||1||0||0|| ||0
|- class="f_bleu"
||1||0||1|| ||1
|- class="f_vert"
||1||1||0|| ||1
|- class="f_vert"
||1||1||1|| ||1
|}
Ensuite, créons un circuit qui prend ces deux sorties P et G, et calcule la retenue sortante en fonction. Le circuit en question a trois entrées : la retenue entrante, les deux bits P et G. La retenue sortante vaut 1 soit si une retenue est générée, soit si la retenue entrante est propagée et qu'elle vaut 1. En notant <math>{C_{in}}</math> et <math>{C_{out}}</math> les retenues entrantes et sortante, on a :
: <math>{C_{out}} = G + \left( P . C_{in} \right)</math>
Le circuit est donc composé d'une porte OU et d'une porte ET. En combinant un demi-additionneur avec le circuit de calcul de retenue sortante vu plus haut, on a :
[[File:Additionneur complet avec propagation et génération de retenue.png|class=transparent|centre|vignette|upright=2|Additionneur complet avec propagation et génération de retenue.]]
Certes, on retombe sur le circuit vu plus haut. Mais il est possible de remplacer le circuit en orange par un autre. Par exemple, il est possible de le remplacer par multiplexeur, qui choisit entre la retenue générée et la retenue entrante (propagée). Le choix se fait selon la valeur du bit P, qui chosiit entre propager la retenue et la générer.
[[File:Additionneur crée avec un multiplexeur.png|centre|vignette|upright=1.5|Additionneur crée avec un multiplexeur]]
Le circuit exact est illustré ci-dessous. Il semble utiliser plus de portes logiques que l'additionneur complet précédent. Cependant, nous verrons dans quelques chapitres qu'il est possible d'implémenter un multiplexeur avec seulement 6 transistors, voire moins ! L'implémentation utilise des portes à transmission, mais nous en reparlerons dans le chapitre sur les transistors, quand nous verrons les additionneurs à ''Manchester Carry Chain''. Au passage, une variante de ce circuit a été utilisée dans le processeur processeur 8086 d'Intel, comme on le verra dans le chapitre suivant.
[[File:Additionneur complet basé sur un MUX.png|centre|vignette|upright=1|Additionneur complet basé sur un MUX]]
[[File:FulladderPG.png|vignette|upright=1|Additionneur complet avec deux sorties P et G.]]
Pour finir, sachez qu'il existe des additionneurs qui fournissent : le bit de résultat, deux sorties P et G qui indiquent si l'addition propage ou génère une retenue. Il ne s'agit pas d'additionneurs complets, car il manque de quoi calculer la retenue sortante. De tels additionneurs seront appelés des '''additionneurs partiels'''. De tels additionneurs sont utilisés dans certains additionneurs pour gagner en performance.
Enfin, il existe aussi ce que j'ai décidé d'appeler des '''additionneurs P/G''', qui sont des additionneurs complets auxquels on a ajouté deux sorties P et G, en plus de la sortie de retenue. Leur circuit est le même que celui d'un additionneur complet, auquel on a ajouté deux fils. Vu que ces deux sorties sont fournies par le premier demi-additionneur, ajouter ces deux sorties demande d'ajouter des fils, pas de portes logiques. De tels additionneurs sont aussi utilisés dans certains additionneurs pour gagner en performance.
[[File:FAwithGP.svg|centre|vignette|upright=2|Additionneur complet avec sorties P et G : circuit.Ç]]
===L'additionneur complet basé sur une porte à majorité===
Maintenant, voyons une dernière implémentation possible de l'additionneur complet. Mais avant de voir comment l'additionneur est implémenté, nous allons devoir faire un petit focus sur la retenue sortante. Reprenons la table de vérité, et regardons ce qu'il en est pour la retenue sortante uniquement.
{|class="wikitable"
|-
! Retenue entrante !! Opérande 1 !! Opérande 2 !! !! Retenue sortante
|-
||0||0||0|| ||0
|-
||0||0||1|| ||0
|-
||0||1||0|| ||0
|-
||0||1||1|| ||1
|-
||1||0||0|| ||0
|-
||1||0||1|| ||1
|-
||1||1||0|| ||1
|-
||1||1||1|| ||1
|}
Vous remarquerez que la retenue sortante est égale au bit majoritaire, parmi les trois bits d'opérande. Si deux ou trois bits sont à 1, la retenue sortante vaut 1. Et réciproquement avec 0. Le circuit de calcul de la retenue peut donc être remplacé par une '''porte à majorité'''.
[[File:Additionneur crée avec une porte à majorité.png|centre|vignette|upright=2|Additionneur crée avec une porte à majorité]]
Mais une porte à majorité est assez gourmande en circuit, ce qui fait qu'un additionneur pareil ne serait pas pratique, ni utile. Du moins, ce serait le cas s'il n'y avait pas une possibilité d'optimisation extrêmement intéressante. Il est possible de calculer le 'bit de somme' à partir de la retenue sortante ! En effet, le bit de somme est l'inverse de la retenue sortante, sauf dans deux cas : les trois bits d'entrée sont à 0, où ils sont tous à 1. Le bit de somme vaut 0 dans le premier cas, 1 dans le second cas.
{|class="wikitable"
|-
! Retenue entrante !! Opérande 1 !! Opérande 2 !! !! Retenue sortante !! Bit de somme
|- class="f_bleu"
||0||0||0|| ||0||0
|- class="f_vert"
||0||0||1|| ||0||1
|- class="f_vert"
||0||1||0|| ||0||1
|- class="f_vert"
||0||1||1|| ||1||0
|- class="f_vert"
||1||0||0|| ||0||1
|- class="f_vert"
||1||0||1|| ||1||0
|- class="f_vert"
||1||1||0|| ||1||0
|- class="f_rouge"
||1||1||1|| ||1||1
|}
En clair, 6 lignes de la table de vérité sur 8 peuvent se calculer avec une porte à majorité, le reste demandant quelques portes logiques pour faire la correction. L'implémentation en circuit demande donc trois choses :
* de calculer la retenue sortante et de quoi l'inverser ;
* un circuit qui vérifie si tous les bits opérande valent 0 : une porte NOR ou OU fait l'affaire ;
* un circuit qui vérifie s'ils valent tous 1, à savoir une porte ET.
Ensuite, on combine le résultat des trois circuits précédents pour obtenir le résultat final. La combinaison est le fait du circuit en jaune dans le schéma ci-dessous.
[[File:Full adder basé sur une modification de la retenue.png|centre|vignette|upright=2|Full adder basé sur une modification de la retenue]]
Le circuit en jaune est assez simple. Quand la porte ET sort un 1, la sortie doit être forcée à 1. Par contre, quand la porte OU sort un zéro, la sortie doit être forcée à 0. Il suffit donc d'enchainer un circuit de mise à 1 et un circuit de mise à 0. Nous avons vu dans le chapitre sur les opérations de masquage qu'il s'agit respectivement d'une porte OU et d'une porte ET.
[[File:Full adder basé sur une modification de la retenue interne, au complet.png|centre|vignette|upright=2|Full adder basé sur une modification de la retenue interne, au complet]]
Divers processeurs utilisaient des additionneurs complets de ce type : le fameux Z80, l'Intel 4004 et de l'Intel 8008 sont les exemples les plus notables. Pourtant, on pourrait s'étonner qu'un tel circuit ait existé. Il utilise beaucoup de portes logiques, a une profondeur logique supérieure : il n'a rien d'avantageux. Sauf qu'il était utilisé sur d'anciens processeurs, qui utilisaient la technologie dite TTL, différente de la technologie CMOS des transistors modernes. Et avec la technologie TTL, il est possible de fusionner plusieurs portes logiques ET et NOR en une seule porte logique ET/OU/NON ! Un additionneur complet construit ainsi ne prenait que deux portes logiques : une pour le calcul de la retenue sortante, une autre pour le reste du circuit.
==L'addition non signée==
Voyons maintenant un circuit capable d'additionner deux nombres entiers: l''''additionneur'''. Dans la version qu'on va voir, ce circuit manipulera des nombres strictement positifs. L'addition des nombres codés en complètement à deux sera vu dans une section ultérieure.
[[File:Full-adder-4bits-serial-CEI.svg|vignette|upright=0.5|Additionneur 4 bits, un bloc.]]
L'interface d'un additionneur est illustrée ci-contre. Un additionneur prend deux opérandes sur deux entrées séparées, et fournit le résultat sur une sortie dédiée. De plus, il a une entrée sur laquelle envoyer une retenue entrante, et a une sortie pour la retenue sortante. La sortie de retenue indique que le résultat de l'addition a débordé (au sens d'un débordement d'entier). Pour l'entrée de retenue, son utilité deviendra plus claire dans ce qui suit, mais souvenez-vous qu'elle existe.
L'entrée et la sortie de retenue sont parfois utilisées pour combiner plusieurs petits additionneurs, pour former un additionneur plus grand. Nous verrons de nombreux exemples dans ce qui suit. Il est assez commun de créer des additionneurs pour des opérandes de 16/32/64 bits, avec des additionneurs de 4/5 bits.
===L'additionneur série===
Il est possible d'additionner deux nombres bit par bit, grâce à un additionneur complet associé à plusieurs registres à décalages. Les opérandes sont placées chacune dans un registre à décalage, et l'additionneur complet utilise les bits sortants de ces registres à décalage. Le bit du résultat est envoyé au registre à décalage pour le résultat. La retenue de l'addition est stockée dans une bascule de 1 bit, en attente du prochain cycle d'horloge. Un tel additionneur est appelé un '''additionneur série'''. Il a été utilisé sur d'anciens ordinateurs dans les années 50-60, aussi bien des prototypes que des ordinateurs commerciaux.
[[File:Additionneur série.jpg|centre|vignette|upright=2.5|Additionneur série.]]
===L'additionneur à propagation de retenue===
L''''additionneur à propagation de retenue''' est aussi appelé l'additionneur ''ripple carry'', c'est son nom anglais. J'utiliserais occasionnellement le nom anglais dans ce qui suit, car celui-ci est plus court. Ilutilise un additionneur complet pour chaque colonne de l'addition. Pour gérer les retenues, il suffit ainsi de câbler des additionneurs complets les uns à la suite des autres, chacun envoyant sa retenue à la colonne suivante.
[[File:Ripplecarryadder.png|centre|vignette|upright=2.5|Additionneur à propagation de retenue.]]
Notons la présence de la retenue sortante, qui est utilisée pour détecter les débordements d'entier, ainsi que pour d'autres opérations. Le bit de retenue final est souvent stocké dans un registre spécial du processeur (généralement appelé ''carry flag'').
Notez aussi, à droite du schéma précédent, l'entrée de retenue <math>\text{R}_\text{Entrée}</math>. Elle est très utile pour l'implémentation de certaines opérations comme l'inversion de signe, la soustraction, l'incrémentation, etc. Certains processeurs sont capables de faire une opération appelée ADC, ADDC ou autre nom signifiant ''Addition with Carry'', qui permet de faire le calcul A + B + Retenue (la retenue en question est la retenue sortante de l'addition précédente, stockée dans le registre ''carry flag''). Son utilité principale est de permettre des additions d'entiers plus grands que ceux supportés par le processeur. Par exemple, cela permet de faire des additions d'entiers 32 bits sur un processeur 16 bits.
[[File:Adder Network 3 Bit Sum Delay 003.svg|vignette|upright=0.5|Propagation de retenue dans l'additionneur.]]
Cet additionneur utilise très peu de portes logiques, ce qui fait qu'il était utilisé sur certains processeurs 8 et 16 bits, dont le budget en portes logiques était limité. Bien que très simple, cet additionneur est cependant peu performant. Son temps de calcul est proportionnel à la taille des opérandes. Par exemple, additionner deux opérandes de 32 bits prendra deux fois plus de temps que pour des opérandes de 16 bits. La raison est que les retenues doivent se propager à travers le circuit, du premier additionneur jusqu'au dernier.
L'addition étant une opération fréquente, d'autres additionneurs ont été inventés. Ils utilisent des optimisations qui utilisent plus de circuits pour gagner en rapidité. Mais avant de voir ces optimisations, nous allons voir s'il est possible d'optimiser les additionneurs à propagation de retenue.
===L'additionneur à saut de retenue===
L'additionneur à propagation de retenue peut être optimisé de deux manières. La première accélère la propagation de la retenue, en agissant au niveau des additionneurs complets. La ''Manchester carry chain'' est une optimisation de ce type, mais on ne peut pas encore expliquer à ce stade du cours, car elle optimisait les additionneurs complets au niveau des transistors eux-mêmes. Elle a été utilisée sur de nombreux processeurs connus, comme le 8086 d'Intel ou le Z80.
La seconde manière rend l'addition plus rapide dans certains cas. Le résultat est un additionneur dont le temps de calcul est variable. Le calcul prendra quelques cycles d'horloges avec certains opérandes, mais d'autres opérandes ne verront aucune amélioration. Il n'améliore pas le pire des cas, dans lequel la retenue doit être propagée du début à la fin, du bit de poids faible au bit de poids fort. Mais dans d'autres cas, il permet d'avoir le résultat en avance. L'optimisation en question donne l''''additionneur à saut de retenue''' (''carry-skip adder'').
Il est composé en enchainant plusieurs additionneurs plus petits, qui additionnent 4/5 bits, rarement plus. De tels additionneurs de petite taille sont appelés des '''blocs''', dans ce qui suit. L'idée est de détecter précocement si la retenue entrante est propagée à travers tout le bloc. Si la retenue est propagée dans tous le bloc, on peut directement l'envoyer sur la sortie de retenue, la retenue saute le bloc entier. Dans le cas contraire, on doit calculer la retenue normalement. Le choix entre les deux est le fait d'un multiplexeur. Toute la difficulté est de savoir comment commander le multiplexeur.
[[File:Carry skip adder, principe de base.png|centre|vignette|upright=2.5|Carry skip adder : principe de base]]
Pour savoir si une retenue est propagée dans un bloc entier, il faut utiliser des additionneurs complets, qui ont en plus une sortie P indiquant que l'additionneur propage la retenue. Le bloc propage une retenue si chaque additionneur complet propage la retenue, donc s'ils ont tous leur sortie P à 1. Un simple ET logique suffit à déterminer si c'est le cas. Le signal de commande du multiplexeur est donc un vulgaire ET entre toutes les sorties P des additionneurs complets du bloc.
[[File:Additionneur à saut de retenue.png|centre|vignette|upright=2.5|Calcul de la commande du MUX.]]
Voici ce que ça donne pour un bloc de 4 bits :
[[File:CSAdder4Bit.svg|centre|vignette|upright=1.5|Additionneur ''carry skip'' : bloc de 4 bits.]]
L'additionneur à saut de retenue est construit en assemblant plusieurs blocs de ce type.
[[File:BCSAdder16Bit.svg|centre|vignette|upright=3|Additionneur à saut de retenue.]]
Les blocs sont tous identiques dans le cas le plus simple, mais il est possible d'utiliser des blocs de taille variable. Par exemple, le premier bloc peut avoir des opérandes de 6 bits, le second des opérandes de 7 bits, etc. Faire ainsi permet de gagner un petit peu en performances, si la taille de chaque bloc est bien choisie. La raison est une question de temps de propagation des retenues. La retenue met plus de temps à se propager à travers 8 blocs qu'à travers 4, ce qui prend plus de temps qu'à travers 2 blocs, etc. En tenir compte fait que la taille des blocs tend à augmenter ou diminuer quand on se rapproche des bits de poids fort.
===L'additionneur à sélection de retenue===
L''''additionneur à sélection de retenue''' utilise aussi des blocs, comme les additionneurs précédents. L'addition se fait en deux versions : une avec la retenue du bloc précédent valant zéro, et une autre version avec la retenue du bloc précédent valant 1. Il suffira alors de choisir le bon résultat avec un multiplexeur, une fois cette retenue connue. On gagne ainsi du temps en calculant à l'avance les valeurs de certains bits du résultat, sans connaître la valeur de la retenue. Petit détail : sur certains additionneurs à sélection de retenue, les blocs de base n'ont pas la même taille. Cela permet de tenir compte des temps de propagation des retenues entre les blocs.
[[File:Additionneur à sélection de retenue.png|centre|vignette|upright=1.5|Additionneur à sélection de retenue avec seulement deux blocs.]]
Dans les exemples du dessus, chaque sous-additionneur étaient des additionneurs à propagation de retenue. Mais ce n'est pas une obligation, et tout autre type d’additionneur peut être utilisé. Par exemple, on peut faire en sorte que les sous-additionneurs soient eux-mêmes des additionneurs à sélection de retenue, et poursuivre ainsi de suite, récursivement. On obtient alors un '''additionneur à somme conditionnelle''', plus rapide que l'additionneur à sélection de retenue, mais qui utilise beaucoup plus de portes logiques.
===Les additionneurs à anticipation de retenue===
Les '''additionneurs à anticipation de retenue''' calculent chaque retenue sans avoir à propager les retenues précédentes. Au lieu de calculer les retenues une par une, ils calculent toutes les retenues en parallèle, à partir des bits des opérandes. Une fois les retenues pré-calculées, il suffit de les additionner avec les deux bits adéquats, pour obtenir le résultat. Ces additionneurs sont composés de deux parties :
* un circuit qui pré-calcule les retenues, sans les propager, directement à partir des opérandes ;
* d'une couche d'additionneurs complets, qui additionnent chacun deux bits d'opérande et la retenue pré-calculée.
[[File:Additionneur à anticipation de retenue.png|centre|vignette|upright=1.5|Additionneur à anticipation de retenue.]]
Le calcul des retenues se fait en deux étapes. La première détermine si les retenues sont propagées ou générées, pour chaque colonne. Elle détermine, pour chaque colonne, les bits P et G qui indiquent respectivement : qu'une retenue a été générée sur cette colonne, que la colonne propage la retenue précédente. La seconde étape utilise ces signaux P et G pour déterminer la retenue sur chaque colonne. Le circuit de calcul des retenues est donc composé de deux parties : une couche de demi-additionneurs pour générer les signaux P et G, un circuit de calcul des retenues proprement dit.
[[File:Circuit complet d'un additionneur à anticipation de retenue.png|centre|vignette|upright=1.5|Circuit complet d'un additionneur à anticipation de retenue.]]
Pour rappel, la retenue de la colonne i, notée <math>C_i</math> est égale à :
: <math>G_i + (P_i . C_{i-1})</math>, avec G le signal de génération de retenue, P le signal de propagation de retenue.
Si on utilisait cette formule sans trop réfléchir, on retomberait sur un additionneur à propagation de retenue inutilement compliqué. L'astuce des additionneurs à anticipation de retenue consiste à remplacer le terme Ci−1 par sa valeur calculée avant. Par exemple, je prends un additionneur 4 bits. Je dispose de deux nombres A et B, contenant chacun 4 bits : A3, A2, A1, et A0 pour le nombre A, et B3, B2, B1, et B0 pour le nombre B. Si j'effectue les remplacements, j'obtiens les formules suivantes :
* C1 = G0 + ( P0 · C0 ) ;
* C2 = G1 + ( P1 · G0 ) + ( P1 · P0 · C0 ) ;
* C3 = G2 + ( P2 · G1 ) + ( P2 · P1 · G0 ) + ( P2 · P1 · P0 · C0 ) ;
* C4 = G3 + ( P3 · G2 ) + ( P3 · P2 · G1 ) + ( P3 · P2 · P1 · G0 ) + ( P3 · P2 · P1 · P0 · C0 ).
Ces formules nous permettent de déduire la valeur d'une retenue directement : il reste alors à créer un circuit qui implémente ces formules, et le tour est joué.
[[File:Four bit adder with carry lookahead.svg|centre|vignette|upright=1.5|Additionneur à anticipation de retenue de 4 bits.]]
Plus haut, j'ai dit que le circuit était composé de trois couches : une couche de demi-additionneur pour les signaux P et G ; un circuit d'anticipation de retenue, puis l'addition finale. Mais les deux bits d'opérandes sont déjà additionnés dans la couche de demi-additionneur, autant réutiliser le résultat pour l'additionner à la retenue. Pour éliminer toute redondance de ce type, il est possible d'utiliser des additionneurs partiels (des additionneurs où la sortie de retenue a été remplacée par deux sorties P et G). L'additionneur final est alors composé de plusieurs additionneurs complets de ce type, couplé à un circuit d'anticipation de retenue.
[[File:Cla4bits.png|centre|vignette|upright=2.5|Additionneur à anticipation de retenue de 4 bits.]]
Les additionneurs à anticipation de retenue sont plus rapides que les additionneurs à propagation de retenue. La raison est que les retenues sont calculées rapidement, dans un temps plus petit qu'en propageant la retenue. Pour rentrer dans les détails mathématiques, le temps de calcul n'est pas proportionnel au nombre de bits, mais proportionnel au logarithme de ce nombre de bits. Par contre, le gain en temps de calcul s'accompagne d'un cout en portes logiques conséquent. Le circuit qui calcule les retenues anticipées utilise beaucoup de portes logiques.
===Les additionneurs à calcul parallèle de préfixe===
Du fait du cout en portes logiques, utiliser un additionneur à anticipation de retenue est une solution pratique pour additionner 4 ou 8 bits. Mais pour des opérandes plus grands, de 16 ou 32 bits, cela utiliserait trop de portes logiques. Une partie de ce cout en portes logiques est cependant lié à des redondances. Si on analyse deux retenues différentes, à des colonnes différentes. Les circuits qui calculent ces deux retenues seront différents, mais ils contiendront des sous-circuits identiques. Pour vous en rendre compte, reprenons les quatre équations obtenues précédemment et essayez de trouver des redondances :
* C1 = G0 + ( P0 · C0 ) ;
* C2 = G1 + ( P1 · G0 ) + ( P1 · P0 · C0 ) ;
* C3 = G2 + ( P2 · G1 ) + ( P2 · P1 · G0 ) + ( P2 · P1 · P0 · C0 ) ;
* C4 = G3 + ( P3 · G2 ) + ( P3 · P2 · G1 ) + ( P3 · P2 · P1 · G0 ) + ( P3 · P2 · P1 · P0 · C0 ).
Vous devriez trouver :
* le terme ''P0 · C0'' dans toutes les lignes ;
* les termes ''P1 · P0 · C0'' et ''P1 · P0'' dans les trois dernières lignes ;
* le terme ''P2 · P1 · P0 · C0'' ; ''P2 · P1 · G0'' et ''P2 · G1'' dans les deux dernières lignes.
Il existe plusieurs manières d'exploiter de telles redondances, exploitées dans les '''additionneurs à calcul parallèle de préfixe'''. L'optimisation apportée est de générer des signaux ''propagate'' et ''generate'' pour des groupes de 2, 3, 4, ..., N colonnes. Par exemple, il est possible de générer un signal ''P 0 vers 3'', qui indique si la retenue de la première colonne est propagée jusqu'à la 4ème colonne. Un autre exemple est un signal qui indique qu'une retenue a été générée entre les colonnes 4 à 7 génèrent une retenue ou non. En clair, les signaux P et G ont maintenant un intervalle, qui précise de quelle colonne vers quelle colonne se fait la propagation, ou entre quelles colonnes se fait la génération.
Il est possible de combiner les signaux P et G de deux groupes de colonne, s'ils sont contiguës (ils peuvent aussi se recouvrir). Par exemple, il est possible de calculer les bits P et G pour les colonnes 0 à 10, à partir des deux signaux P/G des colonnes 0-4 et 5-10. Pour cela, les équations sont assez simples. Si je prends deux groupes nommés A et B, avec A pour les colonnes de poids fort et B celles de poids faible, on a :
: <math>P_{sortie} = P_A . P_B</math>
: <math>G_{sortie} = G_A . P_A . G_B</math>
Pour les bits des colonnes 2, 4, 8, 16, 32, 64, et autres, le calcul est simple. L'idée est de grouper les colonnes par groupes de 2. Puis, on calcule chaque groupe de 4 bits à partir de deux groupes de 2. Puis les groupes de 8 bits sont calculés à partir de deux groupes de 4 bits, et ainsi de suite pour toute puissance de deux.
[[File:Squelette d'un additionneur à calcul parallèle de préfixe.jpg|centre|vignette|upright=2|Squelette d'un additionneur à calcul parallèle de préfixe]]
Les seules difficultés surviennent pour les colonnes dont le nombre n'est pas une puissance de deux. Il y a plusieurs manières pour subdiviser ces colonnes en intervalles plus petits et combiner le tout. Et elles donnent chacune des additionneurs différents. Ils portent les noms d''''additionneur de Ladner-Fisher''', d''''additionneur de Brent-Kung''', d''''additionneur de Kogge-Stone''', etc. Dans le cas général, l'additionneur de Kogge-Stone est un des plus rapide, mais aussi un des plus gourmand en circuits, alors que c'est l'inverse pour l'additionneur de Brent-Kung. Les deux additionneurs sont illustrés ci-dessous, pour des opérandes de 8 bits. Dans ceux-ci, chaque losange calculent les bits P et G, à partir des signaux P et G de deux groupes précédents.
{|
|[[File:Brent-kung-8-bit.png|vignette|Brent-kung-8-bit]]
|[[File:Kogge-stone-8-bit.png|vignette|upright=1.2|Kogge-stone-8-bit]]
|}
===Le calcul parallèle de la retenue===
L'anticipation de retenue est très rapide, mais utilise beaucoup de circuits, même en utilisant le calcul parallèle de préfixe. Les additionneurs à propagation de retenue font eux le compromis inverse. Mais il existe des intermédiaires, qui visent à obtenir un compromis entre performance et cout en portes logiques.
Un de ces compromis découpe un additionneur de 16/32/64 bits en additionneurs de 4/5 bits, qui sont enchainés en utilisant la propagation de retenue. Par contre, ces petits additionneurs de 4/5 bits utilisent l'anticipation de retenue. Ces additionneurs simples seront nommés ''blocs'' dans ce qui suit. Par exemple, on peut utiliser 4 additionneurs à anticipation de retenue de 4 bits, et propager les retenues entre eux. Le premier bloc calcule une retenue, qui est propagée au second bloc. Le second bloc calcule lui aussi une retenue, qui est propagée au troisième bloc, etc.
[[File:4008 Functional Diagram.svg|vignette|upright=1|4008 Functional Diagram]]
Le gain en performance est significatif, mais il est possible de faire presque aussi rapide, tout en économisant beaucoup de circuits. L'idée est de simplifier les additionneurs 4 bits, afin de ne pas utiliser une anticipation de retenue complète. L'anticipation de retenue n'est utilisée que pour la retenue sortante, alors que les bits de somme sont calculées avec propagation de retenue. La retenue sortante est donc calculée en parallèle de l'addition. Chaque bloc contient, à côté d'un additionneur proprement dit, un circuit qui calcule la retenue sortante. Il existe de nombreuses manières de calculer la retenue sortante.
Le gain en portes logiques est assez significatif. En effet, on se passe de circuits d'anticipation de retenue pour les 4 bits de somme, on ne garde que celui pour la retenue sortante. Le fait d'utiliser la propagation de retenue pour calculer les 4 bits de somme est censé avoir un léger cout en performance, mais il se trouve que ce cout est très faible. En effet, le résultat n'est connu qu'une fois les retenues propagées jusqu'au dernier bloc. Et malgré l'anticipation des retenues, la propagation est assez lente. les additionneurs à propagation de retenue auront terminé leur travail bien avant.
Un exemple est celui de l'additionneur CMOS 4008, un additionneur de 4 bit. Il est composé en trois sections. Une première couche de demi-additionneurs calcule les signaux P et G utilisés à la fois pour l'anticipation de la retenue sortante, que pour calculer les bits de somme. Le circuit de calcul de la retenue sortante utilise l'anticipation de retenue. Le reste du circuit fait l'addition en propageant les retenues.
[[File:CMOS 4008, circuit découpé en sections.png|centre|vignette|upright=1.5|CMOS 4008, circuit découpé en sections]]
===Les additionneurs à anticipation de retenue multi-niveau===
Les additionneurs précédents mixent anticipation et propagation de retenue. Ceux que nous allons voir utilisent des blocs, mais utilisent l'anticipation de retenue entre les blocs. Les blocs utilisés peuvent être un additionneur à propagation de retenue, ou à anticipation de retenue, peu importe. Ce qui compte est que la retenue entrante de chaque bloc est calculée par anticipation de retenue.
Par exemple, il est possible de créer un additionneur 16 bits en utilisant 4 additionneurs/blocs de 4 bits, couplé à un circuit d’anticipation de retenue par bloc. Pour cela, les additionneurs de 4 bits doivent fournir deux sorties P et G. Les deux sorties indiquent que le bloc soit a généré une retenue, soit a propagé la retenue entrante. Par propagé, on veut dire que la retenue entrante du bloc s'est retrouvée sur la sortie de retenue du bloc. C'est le cas si les 4 colonnes propagent la retenue, ce qui signifie que :
: <math>P_\text{4 bits} = P_0 \cdot P_1 \cdot P_2 \cdot P_3</math>
Pour la génération, cela veut dire qu'une colonne a générée une retenue, qui a été propagée jusqu'à la sortie. En clair, pour chaque colonne, il faut que le G = 1, et que les signaux P des colonnes soient à 1. Pour les 4 colonnes, cela donne :
: <math>G_\text{4 bits} = G_3 + G_2 \cdot P_3 + G_1 \cdot P_3 \cdot P_2 + G_0 \cdot P_3 \cdot P_2 \cdot P_1</math>
[[File:Cla4bitsPG.png|centre|vignette|upright=2|Additionneur à anticipation de retenue de 4 bits.]]
Pour combiner 4 blocs de 4 bits, on devra donc combiner 4 bits P et G, avec un circuit d'anticipation de retenue.
[[File:16-bit lookahead carry unit.svg|centre|vignette|upright=2|Additionneur à anticipation de retenue de 16 bits.]]
Il est même possible d'aller plus loin et de combiner 4 additionneurs précédents pour obtenir un additionneur 64 bits.
[[File:64-bit lookahead carry unit.svg|centre|vignette|upright=2|Additionneur à anticipation de retenue de 64 bits.]]
Le résultat est un additionneur à plusieurs niveaux d’anticipation de retenue. Une anticipation de retenue dans un blocs de 4 bits (facultative), entre blocs de 4 bits, entre blocs de 16 bits. La même logique peut être utilisée avec des blocs de taille différente de 4, 16 et 64 bits.
==L'addition signée et la soustraction==
Après avoir vu l'addition, il est logique de passer à la soustraction, les deux opérations étant très proches. Si on sait câbler une addition entre entiers positifs, câbler une soustraction n'est pas très compliqué. De plus, la soustraction permet de faire des additions de nombres signés.
===Le soustracteur à propagation de retenue===
Pour soustraire deux nombres entiers, on peut adapter l'algorithme de soustraction utilisé en décimal, celui que vous avez appris à l'école. Celui-ci ressemble fortement à l'algorithme d'addition : on soustrait les bits de même poids, et on propage éventuellement une retenue sur la colonne suivante. À la différence de l'addition, la retenue est soustraite, et non ajoutée. En clair, une soustraction en binaire demande de faire deux soustraction : pour chaque bit de la première opérande, on soustrait le bit associé de la seconde, puis on soustrait la retenue entrante.
[[File:Algebra1 05 fig014.svg|centre|vignette|Soustraction en binaire, avec les retenues en rouge.]]
[[File:Half Subtractor Vektor.svg|vignette|Demi-soustracteur.]]
Dans le chapitre sur les incrémenteurs/décrémenteurs, nous avons vu un circuit qui soustrait deux bits, appelé le '''demi-soustracteur'''. Il ressemble beaucoup à un demi-additionneur, les différences se résumant à une porte NON ajoutée pour le calcul de la retenue. Il implémente la table de soustraction, qui est très simple en binaire :
* 0 - 0 = 0 ;
* 0 - 1 = 1 et une retenue ;
* 1 - 0 = 1 ;
* 1 - 1 = 0.
Comme pour les additionneurs, deux demi-soustracteurs peuvent être combinés pour donner un '''soustracteur complet'''. Le calcul de la retenue se fait en combinant les deux retenues des demi-soustracteurs avec une porte OU. Là encore, si on regarde les sorties des deux demi-soustracteurs, il est impossible d'avoir les deux sorties de retenues à 1 en même temps.
[[File:FullSubtractor.svg|centre|vignette|upright=2|Soustracteur complet.]]
[[File:Sub Network 3 Bit Step 002.svg|vignette|upright=1|Soustracteur à propagation de retenue pour opérandes de 3 bits.]]
Les soustracteurs complets sont utilisés pour créer des soustracteurs à propagation de retenue, sur le même modèle que les additionneurs. Et je précise bien "à propagation de retenue", car cela ne fonctionne bien que pour les additionneurs/soustracteurs à propagation de retenue. L'anticipation de retenue et les autres optimisations ne marchent pas de la même manière entre addition et soustraction. Les signaux de propagation et de génération de retenue ne sont pas générés pareil, idem pour la manière de les combiner.
Il est possible de fusionner un additionneur à propagation de retenue et un soustracteur de même type. Pour cela, il suffit de remarquer que les demi-soustracteurs et les demi-additionneurs se ressemblent beaucoup. La seule différence entre les deux est une vulgaire porte NON. En remplaçant cette porte NON par un inverseur commandable, on obtient un circuit qui sert soit de demi-additionneur, soit de demi-soustracteur, suivant ce qu'on envoie sur l'entrée de commande. Ci-dessous est illustré ce que donne ce remplacement pour un soustracteur complet. Le résultat est un '''additionneur/soustracteur à propagation de retenue'''.
[[File:Additionneur-soustracteur complet.png|centre|vignette|upright=2|Additionneur-soustracteur complet]]
Il est donc possible de fusionner un additionneur avec un soustracteur, ce qui donne un '''additionneur-/soustracteur'''. Mais comme je l'ai dit plus haut, cela ne marche que si l'''additionneur-/soustracteur'' utilise la propagation de retenue. Dès qu'on souhaite utiliser l'anticipation de retenue ou tout autre technique, les choses sont plus compliquées. Le calcul des retenues étant différent entre addition et soustraction, mutualiser les circuits d'anticipation de retenue pour les deux opérations ne donnera pas un résultat très propre. Ce qui n'est pas le cas avec les solutions que nous allons voir dans ce qui suit.
===L'additionneur-soustracteur pour opérandes codées en complément à deux===
Étudions le cas de la soustraction en complément à deux, dans l'objectif de créer un circuit soustracteur. Vous savez sûrement que a−b et a+(−b) sont deux expressions équivalentes. Et en complément à deux, − b = not(b) + 1. Dit autrement, a − b = a + not(b) + 1. On pourrait se dire qu'il faut deux additionneurs pour faire le calcul, mais la majorité des additionneurs possède une entrée de retenue pour incrémenter le résultat de l'addition. Un soustracteur en complément à deux est donc simplement composé d'un additionneur et d'un inverseur.
[[File:Additionneur soustracteur.png|centre|vignette|upright=2|Soustracteur en complément à deux.]]
Il est possible de créer un circuit capable d'effectuer soit une addition, soit une soustraction : il suffit de remplacer l'inverseur par un inverseur commandable, qui peut être désactivé. On a vu comment créer un tel inverseur commandable dans le chapitre sur les circuits combinatoires. On peut remarquer que l'entrée de retenue et l'entrée de commande de l'inverseur sont activées en même temps : on peut fusionner les deux signaux en un seul.
[[File:Additionneur-soustracteur en complément à deux.png|centre|vignette|upright=2|Additionneur-soustracteur en complément à deux.]]
Une implémentation alternative est la suivante. Elle remplace l'inverseur commandable par un multiplexeur.
[[File:4-bit ripple carry adder-subtracter.svg|centre|vignette|upright=2|Additionneur-soustracteur en complément à deux, version alternative.]]
===L'additionneur-soustracteur pour opérandes codées en signe-magnitude===
Passons maintenant aux nombres codés en signe-valeur absolue, les deux opérandes étant notées A et B. Suivant les signes des deux opérandes, on a quatre cas possibles : A + B, A − B (B négatif), −A + B (A négatif) et −A − B (A et B négatifs). Une astuce est que le circuit n'a besoin que de calculer A + B et A − B : il peut les inverser pour obtenir − A − B ou B − A. A + B et A − B peuvent se calculer avec un additionneur-soustracteur, reste à corriger le résultat. Il suffit de lui ajouter un inverseur commandable pour obtenir le circuit d'addition finale.
[[File:Additionneur en signe-valeur absolue.png|centre|vignette|upright=2|Additionneur en signe-valeur absolue.]]
Toute la difficulté tient dans le calcul du bit de signe du résultat, quand interviennent des soustractions. Autant l'addition de deux nombres de même signe (A + B et −A − B) ne pose aucun problème, autant les soustractions posent problème (A − B et −A + B). Suivant que <math>A<B</math> ou que <math>A>B</math>, le signe du résultat ne sera pas le même. Déterminer le signe du résultat se fait en regardant les bits de débordement d'entier, comme on le verra plus bas.
===L'additionneur-soustracteur pour opérandes codées en représentation par excès===
Passons maintenant aux nombres codés en représentation par excès. On pourrait croire que ces nombres s'additionnent comme des nombres non-signés, mais ce serait oublier la présence du biais, qui pose problème. Dans les cas de nombres signés gérés avec un biais, voyons ce que donne l'addition de deux nombres :
: <math>( n_1 + biais ) + ( n_2 + biais ) = n_1 + n_2 + 2 \times biais</math>
Or, le résultat correct serait :
: <math>n_1 + n_2 + biais</math>
En effectuant l'addition telle quelle, le biais est compté deux fois. On doit donc le soustraire après l'addition pour obtenir le résultat correct.
Même chose pour la soustraction qui donne ceci :
: <math>(n_1 + biais) - (n_2 + biais) = n_1 - n_2</math>
Or, le résultat correct serait :
: <math> ( n_1 - n_2 ) + biais </math>
Il faut rajouter le biais pour obtenir l'exposant correct.
On a donc besoin de deux additionneurs/soustracteurs : un pour additionner/soustraire les représentations binaires des opérandes, et un autre pour ajouter/retirer le biais en trop/manquant.
==L'additionneur BCD==
Maintenant, voyons un additionneur qui additionne deux entiers au format BCD. Pour cela, nous allons devoir passer par deux étapes. La première est de créer un circuit capable d'additionneur deux chiffres BCD. Ensuite, nous allons voir comment enchaîner ces circuits pour créer un additionneur BCD complet.
===L'additionneur BCD qui fait l'opération chiffre par chiffre===
Nous allons commencer par voir un additionneur qui additionne deux chiffres en BCD, une sorte d'équivalent BCD de l'additionneur complet. Il fournit un résultat sur 4 bits et une retenue qui est mise à 1 si le résultat dépasse 10 (la limite d'un chiffre BCD). Les deux opérandes sont des chiffres BCD codés sur 4 bits et sont additionnés en binaire par un additionneur des plus normaux, similaire à ceux vus plus haut. Le résultat est alors un entier codé en binaire, sur 5 bits, qu'on corrige/convertit pour obtenir un chiffre BCD et une retenue sortante.
Pour corriger le résultat, une idée intuitive serait de prendre le résultat et de faire une division par 10. Le quotient donne la retenue, alors que le reste est le résultat, le chiffre BCD. Mais un circuit diviseur par 10 utilise beaucoup de portes logiques, ce qui ne vaut pas le coup. Une autre méthode détecte si le résultat est égal ou supérieur à 10, ce qui correspond à un "débordement" (on dépasse les limites d'un chiffre BCD). Si le résultat est plus petit que 10, il n'y a rien à faire : le résultat est bon et la retenue est de zéro. Par contre, si le résultat vaut 10 ou plus, il faut corriger le résultat et générer une retenue à 1.
Il faut donc ajouter un circuit qui détecte si le résultat est supérieur à 9, qui calcule directement la retenue. Ce circuit peut se fabriquer simplement à partir de sa table de vérité, ou en utilisant les techniques que nous verrons dans un chapitre ultérieur sur les comparateurs. La solution la plus simple est clairement d'utiliser la table de vérité, ce qui est très simple, assez pour être laissé en exercice au lecteur. Pour comprendre comment corriger le résultat, établissons une table de vérité qui associe le résultat et le résultat corrigé. L'entrée vaut au minimum 10 et au maximum 9 + 9 = 18. On considère la sortie comme un tout, la retenue étant un 5ème bit, le bit de poids fort.
{|class="wikitable"
|-
! colspan="5" | Entrée
!
! rowspan="10" |
! Retenue
! Résultat corrigé (sans retenue)
! interprétation de la sortie en binaire (retenue inclue)
|-
| 0 || 1 || 0 || 1 || 0
| (10)
| 1 || 0000
| (16)
|-
| 0 || 1 || 0 || 1 || 1
| (11)
| 1 || 0001
| (17)
|-
| 0 || 1 || 1 || 0 || 0
| (12)
| 1 || 0010
| (18)
|-
| 0 || 1 || 1 || 0 || 1
| (13)
| 1 || 0011
| (19)
|-
| 0 || 1 || 1 || 1 || 0
| (14)
| 1 || 0100
| (20)
|-
| 0 || 1 || 1 || 1 || 1
| (15)
| 1 || 0101
| (21)
|-
| 1 || 0 || 0 || 0 || 0
| (16)
| 1 || 0110
| (22)
|-
| 1 || 0 || 0 || 0 || 1
| (17)
| 1 || 0111
| (23)
|-
| 1 || 0 || 0 || 1 || 0
| (18)
| 1 || 1000
| (24)
|}
En analysant le tableau, on voit que pour corriger le résultat, il suffit d'ajouter 6. La raison est que le résultat déborde d'un nibble à 16 en binaire, mais à 10 en décimal : il suffit d'ajouter la différence entre les deux, à savoir 6, et le débordement binaire fait son travail. Donc, la correction après une addition est très simple : si le résultat dépasse 9, on ajoute 6.
On peut maintenant implémenter l'additionneur BCD, en combinant le comparateur avec 10, le circuit de correction, et l'additionneur. La première solution calcule deux versions du résultat : la version corrigée, la version normale. Le choix entre les deux est réalisée par un multiplexeur, commandé par le comparateur.
[[File:Additionneur BCD.png|centre|vignette|upright=1.5|Additionneur BCD]]
L'autre solution utilise un circuit commandable qui soit additionne 6, soit ne fait rien. Le choix entre les deux est commandé par le bit calculé par le comparateur.
[[File:Additionneur BCD, seconde version.png|centre|vignette|upright=1.5|Additionneur BCD, seconde version.]]
Une version alternative du circuit précédent est la suivante. Il contient deux additionneurs : un pour additionner les deux chiffres BCD, un autre pour additionner 6 si besoin. Le résultat du comparateur est directement utilisé pour générer l'opérande du second additionneur : 0 ou 6. Le circuit est simple à concevoir, mais gaspille beaucoup de circuit. Idéalement, il vaudrait mieux utiliser un circuit combinatoire d'addition avec une constante.
[[File:Adder 4 Bit BCD.svg|centre|vignette|upright=2|Additionneur BCD, circuit complet.]]
Pour obtenir un additionneur BCD complet, il suffit d’enchaîner les additionneurs précédents, comme on le ferait avec les additionneurs complets dans un additionneur à propagation de retenue. Au final, l'additionneur BCD est beaucoup plus compliqué qu'un additionneur normal, car il rajoute un comparateur ">9", un petit additionneur pour ajouter 6 et éventuellement d'autres circuits. De plus, il est difficile d'appliquer les optimisations disponibles sur les additionneurs non-BCD. Notamment, les circuits d'anticipation de retenue sont totalement à refaire et le résultat est relativement compliqué. C'est ce qui explique pourquoi le BCD a progressivement été abandonné au profit du binaire simple.
La soustraction en BCD se fait comme en binaire : le nombre à soustraire est remplacé par son complément, le circuit additionne le complément et l'autre opérande, le débordement d'entier fait que le résultat marche. Sauf qu'ici, le complément est un complément à 9. Il se calcule chiffre par chiffre : chaque chiffre est remplacé par (9 - le chiffre en question).
===L'additionneur BCD par ajustement décimal===
L'additionneur BCD précédent effectuait son travail chiffre BCD par chiffre BCD, mais il existe des additionneurs BCD qui font autrement. Sur les premiers processeurs x86, il n'y avait pas d'opération d'addition BCD proprement dit, seulement une addition binaire normale de 8, 16 ou 32 bits. Par contre, elle était secondée par une opération dite d''''ajustement décimal''' qui transformait un nombre binaire en nombre codé en BCD. L'opération d'ajustement décimal prenait un opérande de 8 bits codé en binaire et fournissait un résultat de la même taille, c'est à dire deux chiffres BCD. Effectuer une addition BCD demandait donc de faire deux opérations à la suite : une addition binaire simple, suivie par l'opération d'ajustement décimal. Cela permettait de gérer des nombres entiers en binaire usuel et des entiers BCD sans avoir deux instructions d'addition séparées pour les deux, sans compter que cela simplifiait aussi les circuits d'addition.
L'ajustement décimal s'effectue en ajoutant une constante bien précise à l'opérande à convertir en BCD. L'idée est que la constante est découpée en morceaux de 4 bits, correspondant chacun à un chiffre BCD de l'opérande, chaque morceau contenant soit un 0, soit 6. Cela permet d'ajouter soit 0, soit 6, à chaque chiffre BCD, et donc de le corriger. La propagation des retenues d'un chiffre à l'autre est effectuée automatiquement par l'addition binaire de la constante. L'opération d'ajustement décimal calcule automatiquement la constante. Elle découpe l'opérande en ''nibbles'', vérifie si chaque ''nibble'' est supérieur ou égal à 10, puis détermine la valeur de chaque ''nibble'' de la constante finale. Par exemple, si je prends l'opérande 1001 1110, le ''nibble'' de poids faible déborde, alors que celui de poids fort non. La constante sera donc 0000 0110 : 0x06. Inversement, si le ''nibble'' de poids fort déborde et pas celui de poids faible, la constante sera alors 0x60. Et la constante est de 0x66 si les deux ''nibbles'' débordent, de 0x00 si aucun ne déborde.
Le circuit d’ajustement décimal est donc composé de trois étapes : deux étapes pour calculer la constante, et un circuit d'addition pour additionner cette constante au nombre de départ. La première étape découpe l'opérande en morceaux de 4 bits, en chiffres BCD, et vérifie si chacun d'entre eux vaut 10 ou plus. La seconde étape prend les résultats de la première étape, et les combine pour calculer la constante. Enfin, on trouve l'addition finale, qui était réalisée par un circuit d'addition utilisé à la fois pour l'ajustement décimal et l'addition binaire. La différence entre une addition normale et une opération d'ajustement décimal tient dans le fait que les deux premières étapes sont désactivées dans une addition normale.
[[File:Additionneur BCD parallèle.png|centre|vignette|upright=1.5|Additionneur BCD parallèle]]
===L'additionneur biquinaire===
Les entiers BCD ne sont qu'un des encodages hybrides entre décimal et binaire. L'encodage biquinaire est l'un d'entre eux et nous allons faire un rappel rapide à ce sujet. Pour simplifier, un chiffre encodé en biquinaire est composé de deux parties : un bit, couplé à une partie quinaire encodée en représentation ''one-hot''. La partie quinaire encode un nombre allant de 0 à 4, ce qui prend 5 bits (0, 1, 2, 3 et 4). Le bit indique s'il faut ou non ajouter 5 à la valeur encodée par la partie quinaire. Ainsi, on peut coder tous les nombres de 0 à 9.
Additionner deux nombres de biquinaire demande donc d'additionner deux parties quinaires encodées en ''one-hot'' et d'additionner deux bits. Mais attention : il faut tenir compte de la retenue de l'addition des parties quinaires. Et idéalement, il faut aussi tenir compte d'une retenue entrante, provenant de l'addition de la colonne de chiffres précédente. Toute la difficulté vient de la création de l'additionneur ''one-hot''. Heureusement, vu qu'il n'y a que 4-5 bits à additionner, il est souvent fabriqué à partir de sa table de vérité.
[[File:Additionneur bi-quinaire.png|centre|vignette|upright=2|Additionneur bi-quinaire]]
Un avantage du biquinaire est que le calcul du complément à 9 est très simple. Il faut pour cela : inverser la partie binaire avec une porte NON, puis inverser l'ordre des bits de la partie quinaire. Concrètement, le bit de poids faible devient le bit de poids fort, et ainsi de suite. Par exemple, une partie quinaire 01000 devient 00010, 10000 devient 00001, 00100 ne change pas, etc. Le tout peut se calculer avec une porte NON et 5 multiplexeurs.
===L'additionneur BCD avec calculs intermédiaires en biquinaire===
L'ordinateur IBM 1401, un ancien ''mainframe'' des années 60, utilisait un additionneur BCD un peu particulier. Les nombres étaient encodés en BCD dans la mémoire de l'ordinateur, mais les circuits de calcul utilisaient la représentation biquinaire. Lors d'un calcul, le processeur de l'ordinateur traduisait les chiffres BCD en représentation biquinaire, faisait une addition en biquinaire, avant de traduire le résultat en BCD normal.
Pour être précis, l'IBM 1401 utilisait une variante du biquinaire. L'encodage biquinaire de l'IBM 1401 est le suivant : la partie binaire disait si le chiffre était pair ou non, la partie quinaire encodait les valeurs 0, 2, 4, 6 et 8. Le chiffre se calculait en additionnant la partie binaire (0 ou 1) au nombre pair encodé par la partie quinaire. Si l'IBM 1401 utilisait cette variante du biquinaire, c'est car elle donnait des circuits de conversion BCD-biquinaire plus économes en portes logiques et plus rapides.
La partie binaire est le bit de poids faible du chiffre BCD, la partie biquinaire est calculée par un simple décodeur qui prend en entrée le chiffre BCD, amputé de son bit de poids faible. La traduction inverse demande d'utiliser un encodeur, à la place du décodeur. Par contre, le circuit d'addition biquinaire était plus compliqué du fait de la gestion des retenues. L'addition des parties binaires et quinaires se faisait en parallèle, dans deux additionneurs séparés. Cependant, l'addition des parties binaire fournit une retenue, qu'il faut prendre en compte. Pour cela, l'IBM 1401 disposait d'un troisième additionneur qui fournissait le résultat final, encodé en biquinaire.
[[File:Additionneur bi-quinaire de l'IBM 1401.png|centre|vignette|upright=2|Additionneur biquinaire de l'IBM 1401]]
Une implémentation moderne demanderait d'utiliser des portes ET combinées à des portes OU, le circuit pouvant être construit simplement à partir de sa table de vérité. Sur l'IBM 1401, le circuit était cependant différent, en raison de l'utilisation de OU câblés, des croisements de fils qui fonctionnent comme des portes OU, que nous n'avons pas encore vu pour le moment, mais qui seront détaillés dans quelques chapitres. Les OU câblés étaient utilisés pour simplifier le design du circuit, mais demandaient des portes logiques spécifiques, ce qui collait avec le fait que ce ''mainframe'' utilisait des transistors en Germanium. L'implémentation exacte est décrite dans cet article de blog, mais je ne recommande sa lecture qu'à ceux qui savent ce qu'est un OU câblé :
* [https://www.righto.com/2015/10/qui-binary-arithmetic-how-1960s-ibm.html Qui-binary arithmetic: how a 1960s IBM mainframe does math].
==Les débordements d'entier lors d'une addition/soustraction==
Les instructions arithmétiques manipulent des entiers codés sur un nombre fixe de bits, qui ne peuvent prendre leurs valeurs que dans un intervalle. Pour les nombres positifs, un ordinateur qui code ses entiers sur n bits pourra coder tous les entiers allant de 0 à <math>2^n -1</math>. Pour les nombres négatifs, l'intervalle est différent et dépend de la représentation utilisée. Dans le cas général, l'ordinateur peut coder les valeurs comprises de <math>N_\text{min}</math> à <math>N_\text{max}</math>. Si le résultat d'un calcul sort de cet intervalle, il ne peut pas être représenté par l'ordinateur et il se produit ce qu'on appelle un '''débordement d'entier'''.
La '''valeur haute de débordement''' désigne la première valeur qui est trop grande pour être représentée par l'ordinateur. Par exemple, pour un ordinateur qui peut coder tous les nombres entre 0 et 7, la valeur haute de débordement est égale à 8. On peut aussi définir la '''valeur basse de débordement''', qui est la première valeur trop petite pour être codée par l'ordinateur. Par exemple, pour un ordinateur qui peut coder tous les nombres entre 8 et 250, la valeur basse de débordement est égale à 7. Pour les nombres entiers, la valeur haute de débordement vaut <math>N_\text{max} + 1</math> , alors que la valeur basse vaut <math>N_\text{min} - 1</math> (avec <math>N_\text{max}</math> et <math>N_\text{min}</math> respectivement la plus grande et la plus petite valeur codable par l'ordinateur).
===La correction des débordements d'entier : l'arithmétique saturée===
Quand un débordement d'entier survient, tous les circuits de calcul ne procèdent pas de la même manière. Dans les grandes lignes, il y a deux réactions possibles : soit on corrige automatiquement le résultat du débordement, soit on ne fait rien et on se contente de détecter le débordement.
Si le débordement n'est pas corrigé automatiquement, le circuit ne conserve que les bits de poids faibles du résultat. Les bits en trop sont simplement ignorés. On dit qu'on utilise l''''arithmétique modulaire'''. Le problème avec ce genre d'arithmétique, c'est qu'une opération entre deux grands nombres peut donner un résultat très petit. Par exemple, si je dispose de registres 4 bits et que je souhaite faire l'addition 1111 + 0010 (ce qui donne 15 + 2), le résultat est censé être 10001 (17), ce qui est un résultat plus grand que la taille d'un registre. En conservant les 4 bits de poids faible, j’obtiens 0001 (1). En clair, un résultat très grand est transformé en un résultat très petit. Cela peut poser problèmes si on travaille uniquement avec des nombres positifs, mais c'est aussi utilisé pour coder des nombres en complément à deux.
D'autres circuits utilisent ce qu'on appelle l''''arithmétique saturée''' : si un calcul génère un débordement, on arrondi le résultat au plus grand entier supporté par le circuit. Les circuits capables de calculer en arithmétique saturée sont un peu plus complexes, vu qu'il faut rajouter des circuits pour corriger le résultat en cas de débordement. Il suffit généralement de rajouter un ''circuit de saturation'', qui prend en entrée le résultat et le corrige en cas de débordement. Ce circuit de saturation met la valeur maximale en sortie si un débordement survient, mais se contente de recopier le résultat du calcul sur sa sortie s'il n'y a pas de débordement. Typiquement, il est composé d'une couche de multiplexeurs, qui sélectionnent quelle valeur mettre sur la sortie : soit le résultat du calcul, soit le plus grand nombre entier géré par le processeur, soit le plus petit (pour les nombres négatifs/soustractions).
L'arithmétique saturée est utilisée pour les additions et soustractions, mais c'est plus rare pour les multiplications/divisions. Une des raisons est que le résultat d'une addition/soustraction prend un bit de plus que le résultat, là où les multiplications doublent le nombre de bits. Quand une addition déborde, le résultat réel est proche de la valeur maximale codable. mais quand une multiplication déborde, le résultat peut parfois valoir 200 à 60000 fois plus que la valeur maximale codable. Les calculs avec une valeur saturée/corrigée sont donc crédibles pour une suite d'additions, mais pas pour une suite de multiplications.
===La détection des débordements entiers===
Quand un débordement d'entier a eu lieu, il vaut mieux que l'additionneur prévienne ! Pour cela, l'additionneur a une '''sortie de débordement''', parfois nommée ''Overflow'', dont la valeur indique si l'addition a généré un débordement d'entier ou non. Reste que détecter un débordement ne se fait pas de la même manière selon que l'on parle d'un additionneur non-signé ou signé.
Pour les additionneur non-signés, l'additionneur calcule un bit de plus que ce qui est supporté par l'ordinateur. Par exemple, un additionneur 32 bits fournit un résultat sur 33 bits, un débordement d'entier a lieu quand le 33ème bit est à 1. Précisément, la sortie de débordement n'est autre que la retenue finale, celle fournie par le dernier additionneur complet. Le seul type de débordement possible est un débordement par le haut, où le résultat dépasse la valeur maximale. Avec l'arithmétique saturée, le circuit de saturation consiste en une seule couche de multiplexeurs, voire en un circuit de mise à la valeur maximale tel que vu dans le chapitre sur les opérations bits à bits.
[[File:Gestion des débordements d'entiers.png|centre|vignette|upright=2.5|Gestion des débordements d'entiers lors d'une addition non-signée.]]
Pour les additionneurs non-signés, la gestion des débordements d'entiers dépend fortement de la représentation signée. Nous allons étudier le cas du complément à deux. Si vous vous rappelez le chapitre 1, les calculs sur des nombres en complètement à deux utilisent les règles de l'arithmétique modulaire, c'est une condition nécessaire. À priori, on peut penser que dans ces conditions, les débordements d'entiers sont une chose parfaitement normale, qui nous permet d'avoir des résultats corrects. Néanmoins, certains débordements d'entiers peuvent survenir malgré tout et produire des bugs assez ennuyeux.
Si l'on tient en compte les règles du complément à deux, on sait que le bit de poids fort (le plus à gauche) permet de déterminer si le nombre est positif ou négatif : il indique le signe du nombre. Tout se passe comme si les entiers en complément à deux étaient codés sur un bit de moins, et avaient leur longueur amputé du bit de poids fort. Si le résultat d'un calcul écrase le bit de poids fort, il y a un débordement d'entiers. Il existe une règle simple qui permet de détecter ces débordements d'entiers. L'addition de deux nombres positifs ne peut pas être un nombre négatif. Si on additionne deux nombres dont le bit de signe est à 0 et que le bit de signe du résultat est à 1, on est en face d'un débordement d'entiers. Même chose pour deux nombres négatifs : le résultat de l'addition ne peut pas être positif. On peut résumer cela en une phrase : si deux nombres de même signe sont ajoutés, un débordement a lieu quand le bit du signe du résultat a le signe opposé.
Modifier les circuits d'au-dessus pour qu'ils détectent les débordements en complément à deux est simple comme bonjour : il suffit créer un petit circuit combinatoire qui prenne en entrée les bits de signe des opérandes et du résultat, et qui fasse le calcul de l'indicateur de débordements. Si l'on rédige sa table de vérité, on doit se retrouver avec la table suivante :
{|class="wikitable"
|-
!Entrées
!Sortie
|-
|000||0
|-
|001||1
|-
|010||0
|-
|011||0
|-
|100||0
|-
|101||0
|-
|110||1
|-
|111||0
|}
L'équation de ce circuit est la suivante, avec <math>S_a</math> et <math>S_b</math> les signes des deux opérandes, et <math>C_i</math> la retenue de la colonne précédente :
: <math> ( S_a . S_b . \overline{C_i} ) + ( \overline{S_a} . \overline{S_b} . C_i )</math>
En simplifiant, on obtient alors :
: <math>( S_a . S_b ) \oplus C_i</math>
Or, il se trouve que <math>S_a . S_b</math> est tout simplement la retenue en sortie du dernier additionneur, que nous noterons <math>C_o</math>. On trouve donc :
: <math>C_o \oplus C_i</math>
Il suffit donc de faire un XOR entre la dernière retenue et la précédente pour obtenir le bit de débordement.
<noinclude>
{{NavChapitre | book=Fonctionnement d'un ordinateur
| prev=Les circuits de décalage et de rotation
| prevText=Les circuits de décalage et de rotation
| next=Les circuits de comparaison
| nextText=Les circuits de comparaison
}}
</noinclude>
lyikj45tviimk3zgzqf7qs31zfnl7yd
768776
768775
2026-06-27T13:13:34Z
Mewtow
31375
/* Le soustracteur à propagation de retenue */
768776
wikitext
text/x-wiki
Dans ce chapitre, nous allons voir les circuits capables de faire une addition ou une soustraction, ainsi que quelques circuits spécialisés. Précisons cependant que les fabricants de processeurs travaillent d'arrache-pied pour trouver des moyens de rendre ces circuits de calcul plus rapides et plus économes en énergie. Autant vous dire que les circuits que vous allez voir sont vraiment des circuits qui font pâle figure comparé à ce que l'on peut trouver dans un vrai processeur commercial !
==Les circuits pour additionner 2 ou 3 bits==
L'addition se fait en binaire de la même manière qu'en décimal. On additionne les chiffres/bits colonne par colonne, une éventuelle retenue est propagée à la colonne d'à côté. La soustraction fonctionne sur le même principe, sur le même modèle qu'en décimal.
[[File:Binary Addition Demonstration.svg|centre|vignette|Exemple d'addition en binaire.]]
En clair, additionner deux nombres demande d'additionner 2 bits et une retenue sur chaque colonne, et de propager les retenues d'une colonne à l'autre. La propagation des retenues est quelque chose de simple en apparence, mais qui est sujet à des optimisations extraordinairement nombreuses. Aussi, pour simplifier l'exposition, nous allons voir comment gérer une colonne avant de voir comment sont propagées les retenues.
[[File:Full Adder Block.svg|vignette|upright=1|class=transparent|Additionneur complet.]]
Si on effectue une addition en colonne, on doit additionner les deux bits sur la colonne, mais aussi additionner une éventuelle retenue. Il faut donc créer un circuit qui additionne trois bits : deux bits de données, plus une retenue. Ce circuit qui additionne trois bits est appelé un '''additionneur complet'''. Il fournit en sortie deux bits : un bit de somme et une retenue sortante. Voici sa table de vérité :
{|class="wikitable"
|-
! Retenue entrante !! Opérande 1 !! Opérande 2 !! !! Retenue sortante !! Bit de somme
|-
||0||0||0|| ||0||0
|-
||0||0||1|| ||0||1
|-
||0||1||0|| ||0||1
|-
||0||1||1|| ||1||0
|-
||1||0||0|| ||0||1
|-
||1||0||1|| ||1||0
|-
||1||1||0|| ||1||0
|-
||1||1||1|| ||1||1
|}
Il est possible d'utiliser un tableau de Karnaugh pour traduire la table de vérité, mais elle donne un résultat assez compliqué. La retenue et le bit de somme sont calculés à part. La retenue est calculée avec trois portes ET et une porte OU. Le bit de somme est calculé lui avec 4 portes ET, une porte OU, et trois portes NON.
[[File:Full Adler with 3 entries.png|centre|vignette|upright=2|Additionneur complet, fabriqué à partir de la table de vérité.]]
Le circuit obtenu est pas loin d'être optimal, que ce soit en termes de rapidité que de portes logiques utilisées. Mais il est assez compliqué à comprendre, vu que c'est une traduction littérale de la table de vérité. D'autres implémentations alternatives donnent des résultats plus compréhensibles. Nous allons les voir dans la suite de cette section.
===L'additionneur complet basé sur des demi-additionneurs===
[[File:1-bit half-adder.svg|class=transparent|vignette|Demi-addtionneur.]]
Nous avons déjà vu comment additionner deux bits dans le chapitre sur les incrémenteurs, mais quelques rappels ne seront pas de trop. Le '''demi-additionneur''' est un circuit qui additionne deux bits. Il implémente la table d'addition, qui est très simple en binaire. Jugez plutôt :
* 0 + 0 = 0, retenue = 0 ;
* 0 + 1 = 1, retenue = 0 ;
* 1 + 0 = 1, retenue = 0 ;
* 1 + 1 = 0, retenue = 1.
Mais un demi-additionneur n'additionne que deux bits, il manque de quoi additionner la retenue. Une solution pour cela est d'enchaîner deux demi-additionneurs : un qui additionne les deux bits de données, et un second qui additionne la retenue au résultat du précédent. La retenue finale se calcule en combinant les sorties de retenue des deux demi-additionneurs, avec une porte OU. Le résultat est un additionneur complet fabriqué avec deux demi-additionneurs. Pour vous en convaincre, établissez la table de vérité de ce circuit, vous verrez que ça marche.
[[File:Full Adder Blocks.svg|centre|vignette|upright=2|Additionneur complet fabriqué avec deux demi-additionneurs.]]
[[File:Half-adder.svg|vignette|upright=1|Circuit d'un demi-additionneur naïf.]]
L'implémentation naïve d'un demi-additionneur utilise une porte XOR et une porte ET, comme illustré ci-contre. Le circuit s'obtient en faisant uen traduction littérale de la table de vérité du circuit. Si je dis qu'elle est naïve, c'est car il est possible de l'optimiser, de manière à éliminer des redondances cachées. Mais nous avions déjà vu cela dans le chapitre sur les incrémenteurs, aussi laissons cela pour plus tard.
En combinant deux demi-additionneurs de ce type, on obtient l'additionneur complet suivant. Les deux sorties de retenue des demi-additionneurs sont combinées avec une porte OU, pour calculer la retenue finale.
{| class="flexible"
|[[File:Full Adder Modules.svg|class=transparent|Composition d'un additionneur complet. On voit bien que celui-ci est composé de deux demi-additionneurs, en rouge et en bleu, auxquels on a ajouté une porte OU pour calculer la retenue finale.]]
|[[File:Full-adder.svg|300px|class=transparent|Circuit d'un additionneur complet.]]
|}
Le fait de combiner les deux retenues avec une porte OU n'est pas ce qu'il y a de plus intuitif. La table de vérité nous dit que ça fonctionne, mais on comprend mal pourquoi. Mais tout devient plus clair quand on sait que les deux retenues ne peuvent pas être à 1 en même temps. Même dans l'addition 1 + 1 + 1, seule une retenue est à 1. Soit l'addition des deux bits d'opérande donne naissance à une retenue, soit c'est l'addition ''résultat + retenue entrante''. La retenue sortant est donc à 1 quand une des deux addition donne une retenue. D'où l'usage d'une porte OU. Remarquez qu'une porte XOR donne le même résultat, vu que le cas où les deux retenues sont à 1 n'est jamais rencontré.
===La propagation et la génération des retenues===
L'additionneur complet que nous allons voir dans cette section sert à introduire les concepts de propagation, génération et absorption de retenue. Il s'agit de concepts très importants quand on étudie les additionneurs. l'idée part d'un principe très simple : la retenue sortante dépend de la retenue d'entrée. La relation entre les deux se résume à trois cas, qui dépendent de la valeur des deux bits additionnés, nommés A et B.
* Dans le premier cas, la retenue entrante est égale à la retenue sortante. On dit que la retenue entrante est propagée par l'additionneur.
* Dans le second cas, la retenue est forcée à 0 : la retenue sortante vaut 0, peu importe la valeur de la retenue entrante.
* Dans le troisième cas, la retenue est forcée à 1 : la retenue entrante vaut 1, peu importe la valeur de la retenue entrante.
[[File:Signaux P et G fournis par un demi-additionneur.png|vignette|Signaux P et G fournis par un demi-additionneur]]
Maintenant, créons un circuit qui nous dise si une retenue est propagée ou générée. Il a deux bits de sortie, nommés P et G : P pour ''Propagate'', G pour ''Generate''. Le bit P est à 1 si la retenue entrante est propagée, il est à 0 sinon. Le bit G est à 1 si une retenue est générée, à 0 sinon. Une retenue est considérée comme absorbée si elle n'est pas ni propagée ni générée, pas besoin d'un troisième bit pour gérer ce cas.
Il se trouve que ce circuit n'est autre qu'un demi-additionneur ! Pour vous en rendre compte, regardez la table de vérité d'un additionneur complet, illustrée ci-dessous. Lorsque les deux bits d'opérande sont à 0, la retenue sortante vaut toujours 0. Si ils sont tous deux à 1, alors la retenue sortante vaut 1. S'ils sont différents, alors retenues sortante et entrante sont égales. Le bit P est donc généré par une simple porte XOR. Quant au bit G, il est à 1 si les deux bits d'opérandes sont à 1, ce qui correspond à une porte ET. Il se trouve que ces deux portes forment un demi-additionneur !
{|class="wikitable"
|-
! Opérande 1 !! Opérande 2 !! Retenue entrante !! !! Retenue sortante
|- class="f_rouge"
||0||0||0|| ||0
|- class="f_rouge"
||0||0||1|| ||0
|- class="f_bleu"
||0||1||0|| ||0
|- class="f_bleu"
||0||1||1|| ||1
|- class="f_bleu"
||1||0||0|| ||0
|- class="f_bleu"
||1||0||1|| ||1
|- class="f_vert"
||1||1||0|| ||1
|- class="f_vert"
||1||1||1|| ||1
|}
Ensuite, créons un circuit qui prend ces deux sorties P et G, et calcule la retenue sortante en fonction. Le circuit en question a trois entrées : la retenue entrante, les deux bits P et G. La retenue sortante vaut 1 soit si une retenue est générée, soit si la retenue entrante est propagée et qu'elle vaut 1. En notant <math>{C_{in}}</math> et <math>{C_{out}}</math> les retenues entrantes et sortante, on a :
: <math>{C_{out}} = G + \left( P . C_{in} \right)</math>
Le circuit est donc composé d'une porte OU et d'une porte ET. En combinant un demi-additionneur avec le circuit de calcul de retenue sortante vu plus haut, on a :
[[File:Additionneur complet avec propagation et génération de retenue.png|class=transparent|centre|vignette|upright=2|Additionneur complet avec propagation et génération de retenue.]]
Certes, on retombe sur le circuit vu plus haut. Mais il est possible de remplacer le circuit en orange par un autre. Par exemple, il est possible de le remplacer par multiplexeur, qui choisit entre la retenue générée et la retenue entrante (propagée). Le choix se fait selon la valeur du bit P, qui chosiit entre propager la retenue et la générer.
[[File:Additionneur crée avec un multiplexeur.png|centre|vignette|upright=1.5|Additionneur crée avec un multiplexeur]]
Le circuit exact est illustré ci-dessous. Il semble utiliser plus de portes logiques que l'additionneur complet précédent. Cependant, nous verrons dans quelques chapitres qu'il est possible d'implémenter un multiplexeur avec seulement 6 transistors, voire moins ! L'implémentation utilise des portes à transmission, mais nous en reparlerons dans le chapitre sur les transistors, quand nous verrons les additionneurs à ''Manchester Carry Chain''. Au passage, une variante de ce circuit a été utilisée dans le processeur processeur 8086 d'Intel, comme on le verra dans le chapitre suivant.
[[File:Additionneur complet basé sur un MUX.png|centre|vignette|upright=1|Additionneur complet basé sur un MUX]]
[[File:FulladderPG.png|vignette|upright=1|Additionneur complet avec deux sorties P et G.]]
Pour finir, sachez qu'il existe des additionneurs qui fournissent : le bit de résultat, deux sorties P et G qui indiquent si l'addition propage ou génère une retenue. Il ne s'agit pas d'additionneurs complets, car il manque de quoi calculer la retenue sortante. De tels additionneurs seront appelés des '''additionneurs partiels'''. De tels additionneurs sont utilisés dans certains additionneurs pour gagner en performance.
Enfin, il existe aussi ce que j'ai décidé d'appeler des '''additionneurs P/G''', qui sont des additionneurs complets auxquels on a ajouté deux sorties P et G, en plus de la sortie de retenue. Leur circuit est le même que celui d'un additionneur complet, auquel on a ajouté deux fils. Vu que ces deux sorties sont fournies par le premier demi-additionneur, ajouter ces deux sorties demande d'ajouter des fils, pas de portes logiques. De tels additionneurs sont aussi utilisés dans certains additionneurs pour gagner en performance.
[[File:FAwithGP.svg|centre|vignette|upright=2|Additionneur complet avec sorties P et G : circuit.Ç]]
===L'additionneur complet basé sur une porte à majorité===
Maintenant, voyons une dernière implémentation possible de l'additionneur complet. Mais avant de voir comment l'additionneur est implémenté, nous allons devoir faire un petit focus sur la retenue sortante. Reprenons la table de vérité, et regardons ce qu'il en est pour la retenue sortante uniquement.
{|class="wikitable"
|-
! Retenue entrante !! Opérande 1 !! Opérande 2 !! !! Retenue sortante
|-
||0||0||0|| ||0
|-
||0||0||1|| ||0
|-
||0||1||0|| ||0
|-
||0||1||1|| ||1
|-
||1||0||0|| ||0
|-
||1||0||1|| ||1
|-
||1||1||0|| ||1
|-
||1||1||1|| ||1
|}
Vous remarquerez que la retenue sortante est égale au bit majoritaire, parmi les trois bits d'opérande. Si deux ou trois bits sont à 1, la retenue sortante vaut 1. Et réciproquement avec 0. Le circuit de calcul de la retenue peut donc être remplacé par une '''porte à majorité'''.
[[File:Additionneur crée avec une porte à majorité.png|centre|vignette|upright=2|Additionneur crée avec une porte à majorité]]
Mais une porte à majorité est assez gourmande en circuit, ce qui fait qu'un additionneur pareil ne serait pas pratique, ni utile. Du moins, ce serait le cas s'il n'y avait pas une possibilité d'optimisation extrêmement intéressante. Il est possible de calculer le 'bit de somme' à partir de la retenue sortante ! En effet, le bit de somme est l'inverse de la retenue sortante, sauf dans deux cas : les trois bits d'entrée sont à 0, où ils sont tous à 1. Le bit de somme vaut 0 dans le premier cas, 1 dans le second cas.
{|class="wikitable"
|-
! Retenue entrante !! Opérande 1 !! Opérande 2 !! !! Retenue sortante !! Bit de somme
|- class="f_bleu"
||0||0||0|| ||0||0
|- class="f_vert"
||0||0||1|| ||0||1
|- class="f_vert"
||0||1||0|| ||0||1
|- class="f_vert"
||0||1||1|| ||1||0
|- class="f_vert"
||1||0||0|| ||0||1
|- class="f_vert"
||1||0||1|| ||1||0
|- class="f_vert"
||1||1||0|| ||1||0
|- class="f_rouge"
||1||1||1|| ||1||1
|}
En clair, 6 lignes de la table de vérité sur 8 peuvent se calculer avec une porte à majorité, le reste demandant quelques portes logiques pour faire la correction. L'implémentation en circuit demande donc trois choses :
* de calculer la retenue sortante et de quoi l'inverser ;
* un circuit qui vérifie si tous les bits opérande valent 0 : une porte NOR ou OU fait l'affaire ;
* un circuit qui vérifie s'ils valent tous 1, à savoir une porte ET.
Ensuite, on combine le résultat des trois circuits précédents pour obtenir le résultat final. La combinaison est le fait du circuit en jaune dans le schéma ci-dessous.
[[File:Full adder basé sur une modification de la retenue.png|centre|vignette|upright=2|Full adder basé sur une modification de la retenue]]
Le circuit en jaune est assez simple. Quand la porte ET sort un 1, la sortie doit être forcée à 1. Par contre, quand la porte OU sort un zéro, la sortie doit être forcée à 0. Il suffit donc d'enchainer un circuit de mise à 1 et un circuit de mise à 0. Nous avons vu dans le chapitre sur les opérations de masquage qu'il s'agit respectivement d'une porte OU et d'une porte ET.
[[File:Full adder basé sur une modification de la retenue interne, au complet.png|centre|vignette|upright=2|Full adder basé sur une modification de la retenue interne, au complet]]
Divers processeurs utilisaient des additionneurs complets de ce type : le fameux Z80, l'Intel 4004 et de l'Intel 8008 sont les exemples les plus notables. Pourtant, on pourrait s'étonner qu'un tel circuit ait existé. Il utilise beaucoup de portes logiques, a une profondeur logique supérieure : il n'a rien d'avantageux. Sauf qu'il était utilisé sur d'anciens processeurs, qui utilisaient la technologie dite TTL, différente de la technologie CMOS des transistors modernes. Et avec la technologie TTL, il est possible de fusionner plusieurs portes logiques ET et NOR en une seule porte logique ET/OU/NON ! Un additionneur complet construit ainsi ne prenait que deux portes logiques : une pour le calcul de la retenue sortante, une autre pour le reste du circuit.
==L'addition non signée==
Voyons maintenant un circuit capable d'additionner deux nombres entiers: l''''additionneur'''. Dans la version qu'on va voir, ce circuit manipulera des nombres strictement positifs. L'addition des nombres codés en complètement à deux sera vu dans une section ultérieure.
[[File:Full-adder-4bits-serial-CEI.svg|vignette|upright=0.5|Additionneur 4 bits, un bloc.]]
L'interface d'un additionneur est illustrée ci-contre. Un additionneur prend deux opérandes sur deux entrées séparées, et fournit le résultat sur une sortie dédiée. De plus, il a une entrée sur laquelle envoyer une retenue entrante, et a une sortie pour la retenue sortante. La sortie de retenue indique que le résultat de l'addition a débordé (au sens d'un débordement d'entier). Pour l'entrée de retenue, son utilité deviendra plus claire dans ce qui suit, mais souvenez-vous qu'elle existe.
L'entrée et la sortie de retenue sont parfois utilisées pour combiner plusieurs petits additionneurs, pour former un additionneur plus grand. Nous verrons de nombreux exemples dans ce qui suit. Il est assez commun de créer des additionneurs pour des opérandes de 16/32/64 bits, avec des additionneurs de 4/5 bits.
===L'additionneur série===
Il est possible d'additionner deux nombres bit par bit, grâce à un additionneur complet associé à plusieurs registres à décalages. Les opérandes sont placées chacune dans un registre à décalage, et l'additionneur complet utilise les bits sortants de ces registres à décalage. Le bit du résultat est envoyé au registre à décalage pour le résultat. La retenue de l'addition est stockée dans une bascule de 1 bit, en attente du prochain cycle d'horloge. Un tel additionneur est appelé un '''additionneur série'''. Il a été utilisé sur d'anciens ordinateurs dans les années 50-60, aussi bien des prototypes que des ordinateurs commerciaux.
[[File:Additionneur série.jpg|centre|vignette|upright=2.5|Additionneur série.]]
===L'additionneur à propagation de retenue===
L''''additionneur à propagation de retenue''' est aussi appelé l'additionneur ''ripple carry'', c'est son nom anglais. J'utiliserais occasionnellement le nom anglais dans ce qui suit, car celui-ci est plus court. Ilutilise un additionneur complet pour chaque colonne de l'addition. Pour gérer les retenues, il suffit ainsi de câbler des additionneurs complets les uns à la suite des autres, chacun envoyant sa retenue à la colonne suivante.
[[File:Ripplecarryadder.png|centre|vignette|upright=2.5|Additionneur à propagation de retenue.]]
Notons la présence de la retenue sortante, qui est utilisée pour détecter les débordements d'entier, ainsi que pour d'autres opérations. Le bit de retenue final est souvent stocké dans un registre spécial du processeur (généralement appelé ''carry flag'').
Notez aussi, à droite du schéma précédent, l'entrée de retenue <math>\text{R}_\text{Entrée}</math>. Elle est très utile pour l'implémentation de certaines opérations comme l'inversion de signe, la soustraction, l'incrémentation, etc. Certains processeurs sont capables de faire une opération appelée ADC, ADDC ou autre nom signifiant ''Addition with Carry'', qui permet de faire le calcul A + B + Retenue (la retenue en question est la retenue sortante de l'addition précédente, stockée dans le registre ''carry flag''). Son utilité principale est de permettre des additions d'entiers plus grands que ceux supportés par le processeur. Par exemple, cela permet de faire des additions d'entiers 32 bits sur un processeur 16 bits.
[[File:Adder Network 3 Bit Sum Delay 003.svg|vignette|upright=0.5|Propagation de retenue dans l'additionneur.]]
Cet additionneur utilise très peu de portes logiques, ce qui fait qu'il était utilisé sur certains processeurs 8 et 16 bits, dont le budget en portes logiques était limité. Bien que très simple, cet additionneur est cependant peu performant. Son temps de calcul est proportionnel à la taille des opérandes. Par exemple, additionner deux opérandes de 32 bits prendra deux fois plus de temps que pour des opérandes de 16 bits. La raison est que les retenues doivent se propager à travers le circuit, du premier additionneur jusqu'au dernier.
L'addition étant une opération fréquente, d'autres additionneurs ont été inventés. Ils utilisent des optimisations qui utilisent plus de circuits pour gagner en rapidité. Mais avant de voir ces optimisations, nous allons voir s'il est possible d'optimiser les additionneurs à propagation de retenue.
===L'additionneur à saut de retenue===
L'additionneur à propagation de retenue peut être optimisé de deux manières. La première accélère la propagation de la retenue, en agissant au niveau des additionneurs complets. La ''Manchester carry chain'' est une optimisation de ce type, mais on ne peut pas encore expliquer à ce stade du cours, car elle optimisait les additionneurs complets au niveau des transistors eux-mêmes. Elle a été utilisée sur de nombreux processeurs connus, comme le 8086 d'Intel ou le Z80.
La seconde manière rend l'addition plus rapide dans certains cas. Le résultat est un additionneur dont le temps de calcul est variable. Le calcul prendra quelques cycles d'horloges avec certains opérandes, mais d'autres opérandes ne verront aucune amélioration. Il n'améliore pas le pire des cas, dans lequel la retenue doit être propagée du début à la fin, du bit de poids faible au bit de poids fort. Mais dans d'autres cas, il permet d'avoir le résultat en avance. L'optimisation en question donne l''''additionneur à saut de retenue''' (''carry-skip adder'').
Il est composé en enchainant plusieurs additionneurs plus petits, qui additionnent 4/5 bits, rarement plus. De tels additionneurs de petite taille sont appelés des '''blocs''', dans ce qui suit. L'idée est de détecter précocement si la retenue entrante est propagée à travers tout le bloc. Si la retenue est propagée dans tous le bloc, on peut directement l'envoyer sur la sortie de retenue, la retenue saute le bloc entier. Dans le cas contraire, on doit calculer la retenue normalement. Le choix entre les deux est le fait d'un multiplexeur. Toute la difficulté est de savoir comment commander le multiplexeur.
[[File:Carry skip adder, principe de base.png|centre|vignette|upright=2.5|Carry skip adder : principe de base]]
Pour savoir si une retenue est propagée dans un bloc entier, il faut utiliser des additionneurs complets, qui ont en plus une sortie P indiquant que l'additionneur propage la retenue. Le bloc propage une retenue si chaque additionneur complet propage la retenue, donc s'ils ont tous leur sortie P à 1. Un simple ET logique suffit à déterminer si c'est le cas. Le signal de commande du multiplexeur est donc un vulgaire ET entre toutes les sorties P des additionneurs complets du bloc.
[[File:Additionneur à saut de retenue.png|centre|vignette|upright=2.5|Calcul de la commande du MUX.]]
Voici ce que ça donne pour un bloc de 4 bits :
[[File:CSAdder4Bit.svg|centre|vignette|upright=1.5|Additionneur ''carry skip'' : bloc de 4 bits.]]
L'additionneur à saut de retenue est construit en assemblant plusieurs blocs de ce type.
[[File:BCSAdder16Bit.svg|centre|vignette|upright=3|Additionneur à saut de retenue.]]
Les blocs sont tous identiques dans le cas le plus simple, mais il est possible d'utiliser des blocs de taille variable. Par exemple, le premier bloc peut avoir des opérandes de 6 bits, le second des opérandes de 7 bits, etc. Faire ainsi permet de gagner un petit peu en performances, si la taille de chaque bloc est bien choisie. La raison est une question de temps de propagation des retenues. La retenue met plus de temps à se propager à travers 8 blocs qu'à travers 4, ce qui prend plus de temps qu'à travers 2 blocs, etc. En tenir compte fait que la taille des blocs tend à augmenter ou diminuer quand on se rapproche des bits de poids fort.
===L'additionneur à sélection de retenue===
L''''additionneur à sélection de retenue''' utilise aussi des blocs, comme les additionneurs précédents. L'addition se fait en deux versions : une avec la retenue du bloc précédent valant zéro, et une autre version avec la retenue du bloc précédent valant 1. Il suffira alors de choisir le bon résultat avec un multiplexeur, une fois cette retenue connue. On gagne ainsi du temps en calculant à l'avance les valeurs de certains bits du résultat, sans connaître la valeur de la retenue. Petit détail : sur certains additionneurs à sélection de retenue, les blocs de base n'ont pas la même taille. Cela permet de tenir compte des temps de propagation des retenues entre les blocs.
[[File:Additionneur à sélection de retenue.png|centre|vignette|upright=1.5|Additionneur à sélection de retenue avec seulement deux blocs.]]
Dans les exemples du dessus, chaque sous-additionneur étaient des additionneurs à propagation de retenue. Mais ce n'est pas une obligation, et tout autre type d’additionneur peut être utilisé. Par exemple, on peut faire en sorte que les sous-additionneurs soient eux-mêmes des additionneurs à sélection de retenue, et poursuivre ainsi de suite, récursivement. On obtient alors un '''additionneur à somme conditionnelle''', plus rapide que l'additionneur à sélection de retenue, mais qui utilise beaucoup plus de portes logiques.
===Les additionneurs à anticipation de retenue===
Les '''additionneurs à anticipation de retenue''' calculent chaque retenue sans avoir à propager les retenues précédentes. Au lieu de calculer les retenues une par une, ils calculent toutes les retenues en parallèle, à partir des bits des opérandes. Une fois les retenues pré-calculées, il suffit de les additionner avec les deux bits adéquats, pour obtenir le résultat. Ces additionneurs sont composés de deux parties :
* un circuit qui pré-calcule les retenues, sans les propager, directement à partir des opérandes ;
* d'une couche d'additionneurs complets, qui additionnent chacun deux bits d'opérande et la retenue pré-calculée.
[[File:Additionneur à anticipation de retenue.png|centre|vignette|upright=1.5|Additionneur à anticipation de retenue.]]
Le calcul des retenues se fait en deux étapes. La première détermine si les retenues sont propagées ou générées, pour chaque colonne. Elle détermine, pour chaque colonne, les bits P et G qui indiquent respectivement : qu'une retenue a été générée sur cette colonne, que la colonne propage la retenue précédente. La seconde étape utilise ces signaux P et G pour déterminer la retenue sur chaque colonne. Le circuit de calcul des retenues est donc composé de deux parties : une couche de demi-additionneurs pour générer les signaux P et G, un circuit de calcul des retenues proprement dit.
[[File:Circuit complet d'un additionneur à anticipation de retenue.png|centre|vignette|upright=1.5|Circuit complet d'un additionneur à anticipation de retenue.]]
Pour rappel, la retenue de la colonne i, notée <math>C_i</math> est égale à :
: <math>G_i + (P_i . C_{i-1})</math>, avec G le signal de génération de retenue, P le signal de propagation de retenue.
Si on utilisait cette formule sans trop réfléchir, on retomberait sur un additionneur à propagation de retenue inutilement compliqué. L'astuce des additionneurs à anticipation de retenue consiste à remplacer le terme Ci−1 par sa valeur calculée avant. Par exemple, je prends un additionneur 4 bits. Je dispose de deux nombres A et B, contenant chacun 4 bits : A3, A2, A1, et A0 pour le nombre A, et B3, B2, B1, et B0 pour le nombre B. Si j'effectue les remplacements, j'obtiens les formules suivantes :
* C1 = G0 + ( P0 · C0 ) ;
* C2 = G1 + ( P1 · G0 ) + ( P1 · P0 · C0 ) ;
* C3 = G2 + ( P2 · G1 ) + ( P2 · P1 · G0 ) + ( P2 · P1 · P0 · C0 ) ;
* C4 = G3 + ( P3 · G2 ) + ( P3 · P2 · G1 ) + ( P3 · P2 · P1 · G0 ) + ( P3 · P2 · P1 · P0 · C0 ).
Ces formules nous permettent de déduire la valeur d'une retenue directement : il reste alors à créer un circuit qui implémente ces formules, et le tour est joué.
[[File:Four bit adder with carry lookahead.svg|centre|vignette|upright=1.5|Additionneur à anticipation de retenue de 4 bits.]]
Plus haut, j'ai dit que le circuit était composé de trois couches : une couche de demi-additionneur pour les signaux P et G ; un circuit d'anticipation de retenue, puis l'addition finale. Mais les deux bits d'opérandes sont déjà additionnés dans la couche de demi-additionneur, autant réutiliser le résultat pour l'additionner à la retenue. Pour éliminer toute redondance de ce type, il est possible d'utiliser des additionneurs partiels (des additionneurs où la sortie de retenue a été remplacée par deux sorties P et G). L'additionneur final est alors composé de plusieurs additionneurs complets de ce type, couplé à un circuit d'anticipation de retenue.
[[File:Cla4bits.png|centre|vignette|upright=2.5|Additionneur à anticipation de retenue de 4 bits.]]
Les additionneurs à anticipation de retenue sont plus rapides que les additionneurs à propagation de retenue. La raison est que les retenues sont calculées rapidement, dans un temps plus petit qu'en propageant la retenue. Pour rentrer dans les détails mathématiques, le temps de calcul n'est pas proportionnel au nombre de bits, mais proportionnel au logarithme de ce nombre de bits. Par contre, le gain en temps de calcul s'accompagne d'un cout en portes logiques conséquent. Le circuit qui calcule les retenues anticipées utilise beaucoup de portes logiques.
===Les additionneurs à calcul parallèle de préfixe===
Du fait du cout en portes logiques, utiliser un additionneur à anticipation de retenue est une solution pratique pour additionner 4 ou 8 bits. Mais pour des opérandes plus grands, de 16 ou 32 bits, cela utiliserait trop de portes logiques. Une partie de ce cout en portes logiques est cependant lié à des redondances. Si on analyse deux retenues différentes, à des colonnes différentes. Les circuits qui calculent ces deux retenues seront différents, mais ils contiendront des sous-circuits identiques. Pour vous en rendre compte, reprenons les quatre équations obtenues précédemment et essayez de trouver des redondances :
* C1 = G0 + ( P0 · C0 ) ;
* C2 = G1 + ( P1 · G0 ) + ( P1 · P0 · C0 ) ;
* C3 = G2 + ( P2 · G1 ) + ( P2 · P1 · G0 ) + ( P2 · P1 · P0 · C0 ) ;
* C4 = G3 + ( P3 · G2 ) + ( P3 · P2 · G1 ) + ( P3 · P2 · P1 · G0 ) + ( P3 · P2 · P1 · P0 · C0 ).
Vous devriez trouver :
* le terme ''P0 · C0'' dans toutes les lignes ;
* les termes ''P1 · P0 · C0'' et ''P1 · P0'' dans les trois dernières lignes ;
* le terme ''P2 · P1 · P0 · C0'' ; ''P2 · P1 · G0'' et ''P2 · G1'' dans les deux dernières lignes.
Il existe plusieurs manières d'exploiter de telles redondances, exploitées dans les '''additionneurs à calcul parallèle de préfixe'''. L'optimisation apportée est de générer des signaux ''propagate'' et ''generate'' pour des groupes de 2, 3, 4, ..., N colonnes. Par exemple, il est possible de générer un signal ''P 0 vers 3'', qui indique si la retenue de la première colonne est propagée jusqu'à la 4ème colonne. Un autre exemple est un signal qui indique qu'une retenue a été générée entre les colonnes 4 à 7 génèrent une retenue ou non. En clair, les signaux P et G ont maintenant un intervalle, qui précise de quelle colonne vers quelle colonne se fait la propagation, ou entre quelles colonnes se fait la génération.
Il est possible de combiner les signaux P et G de deux groupes de colonne, s'ils sont contiguës (ils peuvent aussi se recouvrir). Par exemple, il est possible de calculer les bits P et G pour les colonnes 0 à 10, à partir des deux signaux P/G des colonnes 0-4 et 5-10. Pour cela, les équations sont assez simples. Si je prends deux groupes nommés A et B, avec A pour les colonnes de poids fort et B celles de poids faible, on a :
: <math>P_{sortie} = P_A . P_B</math>
: <math>G_{sortie} = G_A . P_A . G_B</math>
Pour les bits des colonnes 2, 4, 8, 16, 32, 64, et autres, le calcul est simple. L'idée est de grouper les colonnes par groupes de 2. Puis, on calcule chaque groupe de 4 bits à partir de deux groupes de 2. Puis les groupes de 8 bits sont calculés à partir de deux groupes de 4 bits, et ainsi de suite pour toute puissance de deux.
[[File:Squelette d'un additionneur à calcul parallèle de préfixe.jpg|centre|vignette|upright=2|Squelette d'un additionneur à calcul parallèle de préfixe]]
Les seules difficultés surviennent pour les colonnes dont le nombre n'est pas une puissance de deux. Il y a plusieurs manières pour subdiviser ces colonnes en intervalles plus petits et combiner le tout. Et elles donnent chacune des additionneurs différents. Ils portent les noms d''''additionneur de Ladner-Fisher''', d''''additionneur de Brent-Kung''', d''''additionneur de Kogge-Stone''', etc. Dans le cas général, l'additionneur de Kogge-Stone est un des plus rapide, mais aussi un des plus gourmand en circuits, alors que c'est l'inverse pour l'additionneur de Brent-Kung. Les deux additionneurs sont illustrés ci-dessous, pour des opérandes de 8 bits. Dans ceux-ci, chaque losange calculent les bits P et G, à partir des signaux P et G de deux groupes précédents.
{|
|[[File:Brent-kung-8-bit.png|vignette|Brent-kung-8-bit]]
|[[File:Kogge-stone-8-bit.png|vignette|upright=1.2|Kogge-stone-8-bit]]
|}
===Le calcul parallèle de la retenue===
L'anticipation de retenue est très rapide, mais utilise beaucoup de circuits, même en utilisant le calcul parallèle de préfixe. Les additionneurs à propagation de retenue font eux le compromis inverse. Mais il existe des intermédiaires, qui visent à obtenir un compromis entre performance et cout en portes logiques.
Un de ces compromis découpe un additionneur de 16/32/64 bits en additionneurs de 4/5 bits, qui sont enchainés en utilisant la propagation de retenue. Par contre, ces petits additionneurs de 4/5 bits utilisent l'anticipation de retenue. Ces additionneurs simples seront nommés ''blocs'' dans ce qui suit. Par exemple, on peut utiliser 4 additionneurs à anticipation de retenue de 4 bits, et propager les retenues entre eux. Le premier bloc calcule une retenue, qui est propagée au second bloc. Le second bloc calcule lui aussi une retenue, qui est propagée au troisième bloc, etc.
[[File:4008 Functional Diagram.svg|vignette|upright=1|4008 Functional Diagram]]
Le gain en performance est significatif, mais il est possible de faire presque aussi rapide, tout en économisant beaucoup de circuits. L'idée est de simplifier les additionneurs 4 bits, afin de ne pas utiliser une anticipation de retenue complète. L'anticipation de retenue n'est utilisée que pour la retenue sortante, alors que les bits de somme sont calculées avec propagation de retenue. La retenue sortante est donc calculée en parallèle de l'addition. Chaque bloc contient, à côté d'un additionneur proprement dit, un circuit qui calcule la retenue sortante. Il existe de nombreuses manières de calculer la retenue sortante.
Le gain en portes logiques est assez significatif. En effet, on se passe de circuits d'anticipation de retenue pour les 4 bits de somme, on ne garde que celui pour la retenue sortante. Le fait d'utiliser la propagation de retenue pour calculer les 4 bits de somme est censé avoir un léger cout en performance, mais il se trouve que ce cout est très faible. En effet, le résultat n'est connu qu'une fois les retenues propagées jusqu'au dernier bloc. Et malgré l'anticipation des retenues, la propagation est assez lente. les additionneurs à propagation de retenue auront terminé leur travail bien avant.
Un exemple est celui de l'additionneur CMOS 4008, un additionneur de 4 bit. Il est composé en trois sections. Une première couche de demi-additionneurs calcule les signaux P et G utilisés à la fois pour l'anticipation de la retenue sortante, que pour calculer les bits de somme. Le circuit de calcul de la retenue sortante utilise l'anticipation de retenue. Le reste du circuit fait l'addition en propageant les retenues.
[[File:CMOS 4008, circuit découpé en sections.png|centre|vignette|upright=1.5|CMOS 4008, circuit découpé en sections]]
===Les additionneurs à anticipation de retenue multi-niveau===
Les additionneurs précédents mixent anticipation et propagation de retenue. Ceux que nous allons voir utilisent des blocs, mais utilisent l'anticipation de retenue entre les blocs. Les blocs utilisés peuvent être un additionneur à propagation de retenue, ou à anticipation de retenue, peu importe. Ce qui compte est que la retenue entrante de chaque bloc est calculée par anticipation de retenue.
Par exemple, il est possible de créer un additionneur 16 bits en utilisant 4 additionneurs/blocs de 4 bits, couplé à un circuit d’anticipation de retenue par bloc. Pour cela, les additionneurs de 4 bits doivent fournir deux sorties P et G. Les deux sorties indiquent que le bloc soit a généré une retenue, soit a propagé la retenue entrante. Par propagé, on veut dire que la retenue entrante du bloc s'est retrouvée sur la sortie de retenue du bloc. C'est le cas si les 4 colonnes propagent la retenue, ce qui signifie que :
: <math>P_\text{4 bits} = P_0 \cdot P_1 \cdot P_2 \cdot P_3</math>
Pour la génération, cela veut dire qu'une colonne a générée une retenue, qui a été propagée jusqu'à la sortie. En clair, pour chaque colonne, il faut que le G = 1, et que les signaux P des colonnes soient à 1. Pour les 4 colonnes, cela donne :
: <math>G_\text{4 bits} = G_3 + G_2 \cdot P_3 + G_1 \cdot P_3 \cdot P_2 + G_0 \cdot P_3 \cdot P_2 \cdot P_1</math>
[[File:Cla4bitsPG.png|centre|vignette|upright=2|Additionneur à anticipation de retenue de 4 bits.]]
Pour combiner 4 blocs de 4 bits, on devra donc combiner 4 bits P et G, avec un circuit d'anticipation de retenue.
[[File:16-bit lookahead carry unit.svg|centre|vignette|upright=2|Additionneur à anticipation de retenue de 16 bits.]]
Il est même possible d'aller plus loin et de combiner 4 additionneurs précédents pour obtenir un additionneur 64 bits.
[[File:64-bit lookahead carry unit.svg|centre|vignette|upright=2|Additionneur à anticipation de retenue de 64 bits.]]
Le résultat est un additionneur à plusieurs niveaux d’anticipation de retenue. Une anticipation de retenue dans un blocs de 4 bits (facultative), entre blocs de 4 bits, entre blocs de 16 bits. La même logique peut être utilisée avec des blocs de taille différente de 4, 16 et 64 bits.
==L'addition signée et la soustraction==
Après avoir vu l'addition, il est logique de passer à la soustraction, les deux opérations étant très proches. Si on sait câbler une addition entre entiers positifs, câbler une soustraction n'est pas très compliqué. De plus, la soustraction permet de faire des additions de nombres signés.
===Le soustracteur à propagation de retenue===
Pour soustraire deux nombres entiers, on peut adapter l'algorithme de soustraction utilisé en décimal, celui que vous avez appris à l'école. Celui-ci ressemble fortement à l'algorithme d'addition : on soustrait les bits de même poids, et on propage éventuellement une retenue sur la colonne suivante. À la différence de l'addition, la retenue est soustraite, et non ajoutée. En clair, une soustraction en binaire demande de faire deux soustraction : pour chaque bit de la première opérande, on soustrait le bit associé de la seconde, puis on soustrait la retenue entrante.
[[File:Algebra1 05 fig014.svg|centre|vignette|Soustraction en binaire, avec les retenues en rouge.]]
[[File:Half Subtractor Vektor.svg|vignette|Demi-soustracteur.]]
Dans le chapitre sur les incrémenteurs/décrémenteurs, nous avons vu un circuit qui soustrait deux bits, appelé le '''demi-soustracteur'''. Il ressemble beaucoup à un demi-additionneur, les différences se résumant à une porte NON ajoutée pour le calcul de la retenue. Il implémente la table de soustraction, qui est très simple en binaire :
* 0 - 0 = 0 ;
* 0 - 1 = 1 et une retenue ;
* 1 - 0 = 1 ;
* 1 - 1 = 0.
Comme pour les additionneurs, deux demi-soustracteurs peuvent être combinés pour donner un '''soustracteur complet'''. Le calcul de la retenue se fait en combinant les deux retenues des demi-soustracteurs avec une porte OU. Là encore, si on regarde les sorties des deux demi-soustracteurs, il est impossible d'avoir les deux sorties de retenues à 1 en même temps.
[[File:FullSubtractor.svg|centre|vignette|upright=2|Soustracteur complet.]]
[[File:Sub Network 3 Bit Step 002.svg|vignette|upright=1|Soustracteur à propagation de retenue pour opérandes de 3 bits.]]
Les soustracteurs complets sont utilisés pour créer des soustracteurs à propagation de retenue, sur le même modèle que les additionneurs. Et je précise bien "à propagation de retenue", car l'anticipation de retenue et les autres optimisations ne marchent pas exactement pareil entre addition et soustraction. Les signaux de propagation et de génération de retenue ne sont pas générés pareil, idem pour la manière de les combiner.
Il est possible de fusionner un additionneur à propagation de retenue et un soustracteur de même type. Pour cela, il suffit de remarquer que les demi-soustracteurs et les demi-additionneurs se ressemblent beaucoup. La seule différence entre les deux est une vulgaire porte NON. En remplaçant cette porte NON par un inverseur commandable, on obtient un circuit qui sert soit de demi-additionneur, soit de demi-soustracteur, suivant ce qu'on envoie sur l'entrée de commande. Ci-dessous est illustré ce que donne ce remplacement pour un soustracteur complet. Le résultat est un '''additionneur/soustracteur à propagation de retenue'''.
[[File:Additionneur-soustracteur complet.png|centre|vignette|upright=2|Additionneur-soustracteur complet]]
Il est donc possible de fusionner un additionneur avec un soustracteur, ce qui donne un '''additionneur/soustracteur'''. Mais comme je l'ai dit plus haut, cela ne marche que si l'''additionneur-/soustracteur'' utilise la propagation de retenue. Dès qu'on souhaite utiliser l'anticipation de retenue ou tout autre technique, les choses sont plus compliquées. Le calcul des retenues étant différent entre addition et soustraction, mutualiser les circuits d'anticipation de retenue pour les deux opérations ne donnera pas un résultat très propre. Ce qui n'est pas le cas avec les solutions que nous allons voir dans ce qui suit.
===L'additionneur-soustracteur pour opérandes codées en complément à deux===
Étudions le cas de la soustraction en complément à deux, dans l'objectif de créer un circuit soustracteur. Vous savez sûrement que a−b et a+(−b) sont deux expressions équivalentes. Et en complément à deux, − b = not(b) + 1. Dit autrement, a − b = a + not(b) + 1. On pourrait se dire qu'il faut deux additionneurs pour faire le calcul, mais la majorité des additionneurs possède une entrée de retenue pour incrémenter le résultat de l'addition. Un soustracteur en complément à deux est donc simplement composé d'un additionneur et d'un inverseur.
[[File:Additionneur soustracteur.png|centre|vignette|upright=2|Soustracteur en complément à deux.]]
Il est possible de créer un circuit capable d'effectuer soit une addition, soit une soustraction : il suffit de remplacer l'inverseur par un inverseur commandable, qui peut être désactivé. On a vu comment créer un tel inverseur commandable dans le chapitre sur les circuits combinatoires. On peut remarquer que l'entrée de retenue et l'entrée de commande de l'inverseur sont activées en même temps : on peut fusionner les deux signaux en un seul.
[[File:Additionneur-soustracteur en complément à deux.png|centre|vignette|upright=2|Additionneur-soustracteur en complément à deux.]]
Une implémentation alternative est la suivante. Elle remplace l'inverseur commandable par un multiplexeur.
[[File:4-bit ripple carry adder-subtracter.svg|centre|vignette|upright=2|Additionneur-soustracteur en complément à deux, version alternative.]]
===L'additionneur-soustracteur pour opérandes codées en signe-magnitude===
Passons maintenant aux nombres codés en signe-valeur absolue, les deux opérandes étant notées A et B. Suivant les signes des deux opérandes, on a quatre cas possibles : A + B, A − B (B négatif), −A + B (A négatif) et −A − B (A et B négatifs). Une astuce est que le circuit n'a besoin que de calculer A + B et A − B : il peut les inverser pour obtenir − A − B ou B − A. A + B et A − B peuvent se calculer avec un additionneur-soustracteur, reste à corriger le résultat. Il suffit de lui ajouter un inverseur commandable pour obtenir le circuit d'addition finale.
[[File:Additionneur en signe-valeur absolue.png|centre|vignette|upright=2|Additionneur en signe-valeur absolue.]]
Toute la difficulté tient dans le calcul du bit de signe du résultat, quand interviennent des soustractions. Autant l'addition de deux nombres de même signe (A + B et −A − B) ne pose aucun problème, autant les soustractions posent problème (A − B et −A + B). Suivant que <math>A<B</math> ou que <math>A>B</math>, le signe du résultat ne sera pas le même. Déterminer le signe du résultat se fait en regardant les bits de débordement d'entier, comme on le verra plus bas.
===L'additionneur-soustracteur pour opérandes codées en représentation par excès===
Passons maintenant aux nombres codés en représentation par excès. On pourrait croire que ces nombres s'additionnent comme des nombres non-signés, mais ce serait oublier la présence du biais, qui pose problème. Dans les cas de nombres signés gérés avec un biais, voyons ce que donne l'addition de deux nombres :
: <math>( n_1 + biais ) + ( n_2 + biais ) = n_1 + n_2 + 2 \times biais</math>
Or, le résultat correct serait :
: <math>n_1 + n_2 + biais</math>
En effectuant l'addition telle quelle, le biais est compté deux fois. On doit donc le soustraire après l'addition pour obtenir le résultat correct.
Même chose pour la soustraction qui donne ceci :
: <math>(n_1 + biais) - (n_2 + biais) = n_1 - n_2</math>
Or, le résultat correct serait :
: <math> ( n_1 - n_2 ) + biais </math>
Il faut rajouter le biais pour obtenir l'exposant correct.
On a donc besoin de deux additionneurs/soustracteurs : un pour additionner/soustraire les représentations binaires des opérandes, et un autre pour ajouter/retirer le biais en trop/manquant.
==L'additionneur BCD==
Maintenant, voyons un additionneur qui additionne deux entiers au format BCD. Pour cela, nous allons devoir passer par deux étapes. La première est de créer un circuit capable d'additionneur deux chiffres BCD. Ensuite, nous allons voir comment enchaîner ces circuits pour créer un additionneur BCD complet.
===L'additionneur BCD qui fait l'opération chiffre par chiffre===
Nous allons commencer par voir un additionneur qui additionne deux chiffres en BCD, une sorte d'équivalent BCD de l'additionneur complet. Il fournit un résultat sur 4 bits et une retenue qui est mise à 1 si le résultat dépasse 10 (la limite d'un chiffre BCD). Les deux opérandes sont des chiffres BCD codés sur 4 bits et sont additionnés en binaire par un additionneur des plus normaux, similaire à ceux vus plus haut. Le résultat est alors un entier codé en binaire, sur 5 bits, qu'on corrige/convertit pour obtenir un chiffre BCD et une retenue sortante.
Pour corriger le résultat, une idée intuitive serait de prendre le résultat et de faire une division par 10. Le quotient donne la retenue, alors que le reste est le résultat, le chiffre BCD. Mais un circuit diviseur par 10 utilise beaucoup de portes logiques, ce qui ne vaut pas le coup. Une autre méthode détecte si le résultat est égal ou supérieur à 10, ce qui correspond à un "débordement" (on dépasse les limites d'un chiffre BCD). Si le résultat est plus petit que 10, il n'y a rien à faire : le résultat est bon et la retenue est de zéro. Par contre, si le résultat vaut 10 ou plus, il faut corriger le résultat et générer une retenue à 1.
Il faut donc ajouter un circuit qui détecte si le résultat est supérieur à 9, qui calcule directement la retenue. Ce circuit peut se fabriquer simplement à partir de sa table de vérité, ou en utilisant les techniques que nous verrons dans un chapitre ultérieur sur les comparateurs. La solution la plus simple est clairement d'utiliser la table de vérité, ce qui est très simple, assez pour être laissé en exercice au lecteur. Pour comprendre comment corriger le résultat, établissons une table de vérité qui associe le résultat et le résultat corrigé. L'entrée vaut au minimum 10 et au maximum 9 + 9 = 18. On considère la sortie comme un tout, la retenue étant un 5ème bit, le bit de poids fort.
{|class="wikitable"
|-
! colspan="5" | Entrée
!
! rowspan="10" |
! Retenue
! Résultat corrigé (sans retenue)
! interprétation de la sortie en binaire (retenue inclue)
|-
| 0 || 1 || 0 || 1 || 0
| (10)
| 1 || 0000
| (16)
|-
| 0 || 1 || 0 || 1 || 1
| (11)
| 1 || 0001
| (17)
|-
| 0 || 1 || 1 || 0 || 0
| (12)
| 1 || 0010
| (18)
|-
| 0 || 1 || 1 || 0 || 1
| (13)
| 1 || 0011
| (19)
|-
| 0 || 1 || 1 || 1 || 0
| (14)
| 1 || 0100
| (20)
|-
| 0 || 1 || 1 || 1 || 1
| (15)
| 1 || 0101
| (21)
|-
| 1 || 0 || 0 || 0 || 0
| (16)
| 1 || 0110
| (22)
|-
| 1 || 0 || 0 || 0 || 1
| (17)
| 1 || 0111
| (23)
|-
| 1 || 0 || 0 || 1 || 0
| (18)
| 1 || 1000
| (24)
|}
En analysant le tableau, on voit que pour corriger le résultat, il suffit d'ajouter 6. La raison est que le résultat déborde d'un nibble à 16 en binaire, mais à 10 en décimal : il suffit d'ajouter la différence entre les deux, à savoir 6, et le débordement binaire fait son travail. Donc, la correction après une addition est très simple : si le résultat dépasse 9, on ajoute 6.
On peut maintenant implémenter l'additionneur BCD, en combinant le comparateur avec 10, le circuit de correction, et l'additionneur. La première solution calcule deux versions du résultat : la version corrigée, la version normale. Le choix entre les deux est réalisée par un multiplexeur, commandé par le comparateur.
[[File:Additionneur BCD.png|centre|vignette|upright=1.5|Additionneur BCD]]
L'autre solution utilise un circuit commandable qui soit additionne 6, soit ne fait rien. Le choix entre les deux est commandé par le bit calculé par le comparateur.
[[File:Additionneur BCD, seconde version.png|centre|vignette|upright=1.5|Additionneur BCD, seconde version.]]
Une version alternative du circuit précédent est la suivante. Il contient deux additionneurs : un pour additionner les deux chiffres BCD, un autre pour additionner 6 si besoin. Le résultat du comparateur est directement utilisé pour générer l'opérande du second additionneur : 0 ou 6. Le circuit est simple à concevoir, mais gaspille beaucoup de circuit. Idéalement, il vaudrait mieux utiliser un circuit combinatoire d'addition avec une constante.
[[File:Adder 4 Bit BCD.svg|centre|vignette|upright=2|Additionneur BCD, circuit complet.]]
Pour obtenir un additionneur BCD complet, il suffit d’enchaîner les additionneurs précédents, comme on le ferait avec les additionneurs complets dans un additionneur à propagation de retenue. Au final, l'additionneur BCD est beaucoup plus compliqué qu'un additionneur normal, car il rajoute un comparateur ">9", un petit additionneur pour ajouter 6 et éventuellement d'autres circuits. De plus, il est difficile d'appliquer les optimisations disponibles sur les additionneurs non-BCD. Notamment, les circuits d'anticipation de retenue sont totalement à refaire et le résultat est relativement compliqué. C'est ce qui explique pourquoi le BCD a progressivement été abandonné au profit du binaire simple.
La soustraction en BCD se fait comme en binaire : le nombre à soustraire est remplacé par son complément, le circuit additionne le complément et l'autre opérande, le débordement d'entier fait que le résultat marche. Sauf qu'ici, le complément est un complément à 9. Il se calcule chiffre par chiffre : chaque chiffre est remplacé par (9 - le chiffre en question).
===L'additionneur BCD par ajustement décimal===
L'additionneur BCD précédent effectuait son travail chiffre BCD par chiffre BCD, mais il existe des additionneurs BCD qui font autrement. Sur les premiers processeurs x86, il n'y avait pas d'opération d'addition BCD proprement dit, seulement une addition binaire normale de 8, 16 ou 32 bits. Par contre, elle était secondée par une opération dite d''''ajustement décimal''' qui transformait un nombre binaire en nombre codé en BCD. L'opération d'ajustement décimal prenait un opérande de 8 bits codé en binaire et fournissait un résultat de la même taille, c'est à dire deux chiffres BCD. Effectuer une addition BCD demandait donc de faire deux opérations à la suite : une addition binaire simple, suivie par l'opération d'ajustement décimal. Cela permettait de gérer des nombres entiers en binaire usuel et des entiers BCD sans avoir deux instructions d'addition séparées pour les deux, sans compter que cela simplifiait aussi les circuits d'addition.
L'ajustement décimal s'effectue en ajoutant une constante bien précise à l'opérande à convertir en BCD. L'idée est que la constante est découpée en morceaux de 4 bits, correspondant chacun à un chiffre BCD de l'opérande, chaque morceau contenant soit un 0, soit 6. Cela permet d'ajouter soit 0, soit 6, à chaque chiffre BCD, et donc de le corriger. La propagation des retenues d'un chiffre à l'autre est effectuée automatiquement par l'addition binaire de la constante. L'opération d'ajustement décimal calcule automatiquement la constante. Elle découpe l'opérande en ''nibbles'', vérifie si chaque ''nibble'' est supérieur ou égal à 10, puis détermine la valeur de chaque ''nibble'' de la constante finale. Par exemple, si je prends l'opérande 1001 1110, le ''nibble'' de poids faible déborde, alors que celui de poids fort non. La constante sera donc 0000 0110 : 0x06. Inversement, si le ''nibble'' de poids fort déborde et pas celui de poids faible, la constante sera alors 0x60. Et la constante est de 0x66 si les deux ''nibbles'' débordent, de 0x00 si aucun ne déborde.
Le circuit d’ajustement décimal est donc composé de trois étapes : deux étapes pour calculer la constante, et un circuit d'addition pour additionner cette constante au nombre de départ. La première étape découpe l'opérande en morceaux de 4 bits, en chiffres BCD, et vérifie si chacun d'entre eux vaut 10 ou plus. La seconde étape prend les résultats de la première étape, et les combine pour calculer la constante. Enfin, on trouve l'addition finale, qui était réalisée par un circuit d'addition utilisé à la fois pour l'ajustement décimal et l'addition binaire. La différence entre une addition normale et une opération d'ajustement décimal tient dans le fait que les deux premières étapes sont désactivées dans une addition normale.
[[File:Additionneur BCD parallèle.png|centre|vignette|upright=1.5|Additionneur BCD parallèle]]
===L'additionneur biquinaire===
Les entiers BCD ne sont qu'un des encodages hybrides entre décimal et binaire. L'encodage biquinaire est l'un d'entre eux et nous allons faire un rappel rapide à ce sujet. Pour simplifier, un chiffre encodé en biquinaire est composé de deux parties : un bit, couplé à une partie quinaire encodée en représentation ''one-hot''. La partie quinaire encode un nombre allant de 0 à 4, ce qui prend 5 bits (0, 1, 2, 3 et 4). Le bit indique s'il faut ou non ajouter 5 à la valeur encodée par la partie quinaire. Ainsi, on peut coder tous les nombres de 0 à 9.
Additionner deux nombres de biquinaire demande donc d'additionner deux parties quinaires encodées en ''one-hot'' et d'additionner deux bits. Mais attention : il faut tenir compte de la retenue de l'addition des parties quinaires. Et idéalement, il faut aussi tenir compte d'une retenue entrante, provenant de l'addition de la colonne de chiffres précédente. Toute la difficulté vient de la création de l'additionneur ''one-hot''. Heureusement, vu qu'il n'y a que 4-5 bits à additionner, il est souvent fabriqué à partir de sa table de vérité.
[[File:Additionneur bi-quinaire.png|centre|vignette|upright=2|Additionneur bi-quinaire]]
Un avantage du biquinaire est que le calcul du complément à 9 est très simple. Il faut pour cela : inverser la partie binaire avec une porte NON, puis inverser l'ordre des bits de la partie quinaire. Concrètement, le bit de poids faible devient le bit de poids fort, et ainsi de suite. Par exemple, une partie quinaire 01000 devient 00010, 10000 devient 00001, 00100 ne change pas, etc. Le tout peut se calculer avec une porte NON et 5 multiplexeurs.
===L'additionneur BCD avec calculs intermédiaires en biquinaire===
L'ordinateur IBM 1401, un ancien ''mainframe'' des années 60, utilisait un additionneur BCD un peu particulier. Les nombres étaient encodés en BCD dans la mémoire de l'ordinateur, mais les circuits de calcul utilisaient la représentation biquinaire. Lors d'un calcul, le processeur de l'ordinateur traduisait les chiffres BCD en représentation biquinaire, faisait une addition en biquinaire, avant de traduire le résultat en BCD normal.
Pour être précis, l'IBM 1401 utilisait une variante du biquinaire. L'encodage biquinaire de l'IBM 1401 est le suivant : la partie binaire disait si le chiffre était pair ou non, la partie quinaire encodait les valeurs 0, 2, 4, 6 et 8. Le chiffre se calculait en additionnant la partie binaire (0 ou 1) au nombre pair encodé par la partie quinaire. Si l'IBM 1401 utilisait cette variante du biquinaire, c'est car elle donnait des circuits de conversion BCD-biquinaire plus économes en portes logiques et plus rapides.
La partie binaire est le bit de poids faible du chiffre BCD, la partie biquinaire est calculée par un simple décodeur qui prend en entrée le chiffre BCD, amputé de son bit de poids faible. La traduction inverse demande d'utiliser un encodeur, à la place du décodeur. Par contre, le circuit d'addition biquinaire était plus compliqué du fait de la gestion des retenues. L'addition des parties binaires et quinaires se faisait en parallèle, dans deux additionneurs séparés. Cependant, l'addition des parties binaire fournit une retenue, qu'il faut prendre en compte. Pour cela, l'IBM 1401 disposait d'un troisième additionneur qui fournissait le résultat final, encodé en biquinaire.
[[File:Additionneur bi-quinaire de l'IBM 1401.png|centre|vignette|upright=2|Additionneur biquinaire de l'IBM 1401]]
Une implémentation moderne demanderait d'utiliser des portes ET combinées à des portes OU, le circuit pouvant être construit simplement à partir de sa table de vérité. Sur l'IBM 1401, le circuit était cependant différent, en raison de l'utilisation de OU câblés, des croisements de fils qui fonctionnent comme des portes OU, que nous n'avons pas encore vu pour le moment, mais qui seront détaillés dans quelques chapitres. Les OU câblés étaient utilisés pour simplifier le design du circuit, mais demandaient des portes logiques spécifiques, ce qui collait avec le fait que ce ''mainframe'' utilisait des transistors en Germanium. L'implémentation exacte est décrite dans cet article de blog, mais je ne recommande sa lecture qu'à ceux qui savent ce qu'est un OU câblé :
* [https://www.righto.com/2015/10/qui-binary-arithmetic-how-1960s-ibm.html Qui-binary arithmetic: how a 1960s IBM mainframe does math].
==Les débordements d'entier lors d'une addition/soustraction==
Les instructions arithmétiques manipulent des entiers codés sur un nombre fixe de bits, qui ne peuvent prendre leurs valeurs que dans un intervalle. Pour les nombres positifs, un ordinateur qui code ses entiers sur n bits pourra coder tous les entiers allant de 0 à <math>2^n -1</math>. Pour les nombres négatifs, l'intervalle est différent et dépend de la représentation utilisée. Dans le cas général, l'ordinateur peut coder les valeurs comprises de <math>N_\text{min}</math> à <math>N_\text{max}</math>. Si le résultat d'un calcul sort de cet intervalle, il ne peut pas être représenté par l'ordinateur et il se produit ce qu'on appelle un '''débordement d'entier'''.
La '''valeur haute de débordement''' désigne la première valeur qui est trop grande pour être représentée par l'ordinateur. Par exemple, pour un ordinateur qui peut coder tous les nombres entre 0 et 7, la valeur haute de débordement est égale à 8. On peut aussi définir la '''valeur basse de débordement''', qui est la première valeur trop petite pour être codée par l'ordinateur. Par exemple, pour un ordinateur qui peut coder tous les nombres entre 8 et 250, la valeur basse de débordement est égale à 7. Pour les nombres entiers, la valeur haute de débordement vaut <math>N_\text{max} + 1</math> , alors que la valeur basse vaut <math>N_\text{min} - 1</math> (avec <math>N_\text{max}</math> et <math>N_\text{min}</math> respectivement la plus grande et la plus petite valeur codable par l'ordinateur).
===La correction des débordements d'entier : l'arithmétique saturée===
Quand un débordement d'entier survient, tous les circuits de calcul ne procèdent pas de la même manière. Dans les grandes lignes, il y a deux réactions possibles : soit on corrige automatiquement le résultat du débordement, soit on ne fait rien et on se contente de détecter le débordement.
Si le débordement n'est pas corrigé automatiquement, le circuit ne conserve que les bits de poids faibles du résultat. Les bits en trop sont simplement ignorés. On dit qu'on utilise l''''arithmétique modulaire'''. Le problème avec ce genre d'arithmétique, c'est qu'une opération entre deux grands nombres peut donner un résultat très petit. Par exemple, si je dispose de registres 4 bits et que je souhaite faire l'addition 1111 + 0010 (ce qui donne 15 + 2), le résultat est censé être 10001 (17), ce qui est un résultat plus grand que la taille d'un registre. En conservant les 4 bits de poids faible, j’obtiens 0001 (1). En clair, un résultat très grand est transformé en un résultat très petit. Cela peut poser problèmes si on travaille uniquement avec des nombres positifs, mais c'est aussi utilisé pour coder des nombres en complément à deux.
D'autres circuits utilisent ce qu'on appelle l''''arithmétique saturée''' : si un calcul génère un débordement, on arrondi le résultat au plus grand entier supporté par le circuit. Les circuits capables de calculer en arithmétique saturée sont un peu plus complexes, vu qu'il faut rajouter des circuits pour corriger le résultat en cas de débordement. Il suffit généralement de rajouter un ''circuit de saturation'', qui prend en entrée le résultat et le corrige en cas de débordement. Ce circuit de saturation met la valeur maximale en sortie si un débordement survient, mais se contente de recopier le résultat du calcul sur sa sortie s'il n'y a pas de débordement. Typiquement, il est composé d'une couche de multiplexeurs, qui sélectionnent quelle valeur mettre sur la sortie : soit le résultat du calcul, soit le plus grand nombre entier géré par le processeur, soit le plus petit (pour les nombres négatifs/soustractions).
L'arithmétique saturée est utilisée pour les additions et soustractions, mais c'est plus rare pour les multiplications/divisions. Une des raisons est que le résultat d'une addition/soustraction prend un bit de plus que le résultat, là où les multiplications doublent le nombre de bits. Quand une addition déborde, le résultat réel est proche de la valeur maximale codable. mais quand une multiplication déborde, le résultat peut parfois valoir 200 à 60000 fois plus que la valeur maximale codable. Les calculs avec une valeur saturée/corrigée sont donc crédibles pour une suite d'additions, mais pas pour une suite de multiplications.
===La détection des débordements entiers===
Quand un débordement d'entier a eu lieu, il vaut mieux que l'additionneur prévienne ! Pour cela, l'additionneur a une '''sortie de débordement''', parfois nommée ''Overflow'', dont la valeur indique si l'addition a généré un débordement d'entier ou non. Reste que détecter un débordement ne se fait pas de la même manière selon que l'on parle d'un additionneur non-signé ou signé.
Pour les additionneur non-signés, l'additionneur calcule un bit de plus que ce qui est supporté par l'ordinateur. Par exemple, un additionneur 32 bits fournit un résultat sur 33 bits, un débordement d'entier a lieu quand le 33ème bit est à 1. Précisément, la sortie de débordement n'est autre que la retenue finale, celle fournie par le dernier additionneur complet. Le seul type de débordement possible est un débordement par le haut, où le résultat dépasse la valeur maximale. Avec l'arithmétique saturée, le circuit de saturation consiste en une seule couche de multiplexeurs, voire en un circuit de mise à la valeur maximale tel que vu dans le chapitre sur les opérations bits à bits.
[[File:Gestion des débordements d'entiers.png|centre|vignette|upright=2.5|Gestion des débordements d'entiers lors d'une addition non-signée.]]
Pour les additionneurs non-signés, la gestion des débordements d'entiers dépend fortement de la représentation signée. Nous allons étudier le cas du complément à deux. Si vous vous rappelez le chapitre 1, les calculs sur des nombres en complètement à deux utilisent les règles de l'arithmétique modulaire, c'est une condition nécessaire. À priori, on peut penser que dans ces conditions, les débordements d'entiers sont une chose parfaitement normale, qui nous permet d'avoir des résultats corrects. Néanmoins, certains débordements d'entiers peuvent survenir malgré tout et produire des bugs assez ennuyeux.
Si l'on tient en compte les règles du complément à deux, on sait que le bit de poids fort (le plus à gauche) permet de déterminer si le nombre est positif ou négatif : il indique le signe du nombre. Tout se passe comme si les entiers en complément à deux étaient codés sur un bit de moins, et avaient leur longueur amputé du bit de poids fort. Si le résultat d'un calcul écrase le bit de poids fort, il y a un débordement d'entiers. Il existe une règle simple qui permet de détecter ces débordements d'entiers. L'addition de deux nombres positifs ne peut pas être un nombre négatif. Si on additionne deux nombres dont le bit de signe est à 0 et que le bit de signe du résultat est à 1, on est en face d'un débordement d'entiers. Même chose pour deux nombres négatifs : le résultat de l'addition ne peut pas être positif. On peut résumer cela en une phrase : si deux nombres de même signe sont ajoutés, un débordement a lieu quand le bit du signe du résultat a le signe opposé.
Modifier les circuits d'au-dessus pour qu'ils détectent les débordements en complément à deux est simple comme bonjour : il suffit créer un petit circuit combinatoire qui prenne en entrée les bits de signe des opérandes et du résultat, et qui fasse le calcul de l'indicateur de débordements. Si l'on rédige sa table de vérité, on doit se retrouver avec la table suivante :
{|class="wikitable"
|-
!Entrées
!Sortie
|-
|000||0
|-
|001||1
|-
|010||0
|-
|011||0
|-
|100||0
|-
|101||0
|-
|110||1
|-
|111||0
|}
L'équation de ce circuit est la suivante, avec <math>S_a</math> et <math>S_b</math> les signes des deux opérandes, et <math>C_i</math> la retenue de la colonne précédente :
: <math> ( S_a . S_b . \overline{C_i} ) + ( \overline{S_a} . \overline{S_b} . C_i )</math>
En simplifiant, on obtient alors :
: <math>( S_a . S_b ) \oplus C_i</math>
Or, il se trouve que <math>S_a . S_b</math> est tout simplement la retenue en sortie du dernier additionneur, que nous noterons <math>C_o</math>. On trouve donc :
: <math>C_o \oplus C_i</math>
Il suffit donc de faire un XOR entre la dernière retenue et la précédente pour obtenir le bit de débordement.
<noinclude>
{{NavChapitre | book=Fonctionnement d'un ordinateur
| prev=Les circuits de décalage et de rotation
| prevText=Les circuits de décalage et de rotation
| next=Les circuits de comparaison
| nextText=Les circuits de comparaison
}}
</noinclude>
os8ad7r5ogi8ooekp29q2tcrbcke6xg
768778
768776
2026-06-27T13:16:04Z
Mewtow
31375
/* L'additionneur-soustracteur pour opérandes codées en complément à deux */
768778
wikitext
text/x-wiki
Dans ce chapitre, nous allons voir les circuits capables de faire une addition ou une soustraction, ainsi que quelques circuits spécialisés. Précisons cependant que les fabricants de processeurs travaillent d'arrache-pied pour trouver des moyens de rendre ces circuits de calcul plus rapides et plus économes en énergie. Autant vous dire que les circuits que vous allez voir sont vraiment des circuits qui font pâle figure comparé à ce que l'on peut trouver dans un vrai processeur commercial !
==Les circuits pour additionner 2 ou 3 bits==
L'addition se fait en binaire de la même manière qu'en décimal. On additionne les chiffres/bits colonne par colonne, une éventuelle retenue est propagée à la colonne d'à côté. La soustraction fonctionne sur le même principe, sur le même modèle qu'en décimal.
[[File:Binary Addition Demonstration.svg|centre|vignette|Exemple d'addition en binaire.]]
En clair, additionner deux nombres demande d'additionner 2 bits et une retenue sur chaque colonne, et de propager les retenues d'une colonne à l'autre. La propagation des retenues est quelque chose de simple en apparence, mais qui est sujet à des optimisations extraordinairement nombreuses. Aussi, pour simplifier l'exposition, nous allons voir comment gérer une colonne avant de voir comment sont propagées les retenues.
[[File:Full Adder Block.svg|vignette|upright=1|class=transparent|Additionneur complet.]]
Si on effectue une addition en colonne, on doit additionner les deux bits sur la colonne, mais aussi additionner une éventuelle retenue. Il faut donc créer un circuit qui additionne trois bits : deux bits de données, plus une retenue. Ce circuit qui additionne trois bits est appelé un '''additionneur complet'''. Il fournit en sortie deux bits : un bit de somme et une retenue sortante. Voici sa table de vérité :
{|class="wikitable"
|-
! Retenue entrante !! Opérande 1 !! Opérande 2 !! !! Retenue sortante !! Bit de somme
|-
||0||0||0|| ||0||0
|-
||0||0||1|| ||0||1
|-
||0||1||0|| ||0||1
|-
||0||1||1|| ||1||0
|-
||1||0||0|| ||0||1
|-
||1||0||1|| ||1||0
|-
||1||1||0|| ||1||0
|-
||1||1||1|| ||1||1
|}
Il est possible d'utiliser un tableau de Karnaugh pour traduire la table de vérité, mais elle donne un résultat assez compliqué. La retenue et le bit de somme sont calculés à part. La retenue est calculée avec trois portes ET et une porte OU. Le bit de somme est calculé lui avec 4 portes ET, une porte OU, et trois portes NON.
[[File:Full Adler with 3 entries.png|centre|vignette|upright=2|Additionneur complet, fabriqué à partir de la table de vérité.]]
Le circuit obtenu est pas loin d'être optimal, que ce soit en termes de rapidité que de portes logiques utilisées. Mais il est assez compliqué à comprendre, vu que c'est une traduction littérale de la table de vérité. D'autres implémentations alternatives donnent des résultats plus compréhensibles. Nous allons les voir dans la suite de cette section.
===L'additionneur complet basé sur des demi-additionneurs===
[[File:1-bit half-adder.svg|class=transparent|vignette|Demi-addtionneur.]]
Nous avons déjà vu comment additionner deux bits dans le chapitre sur les incrémenteurs, mais quelques rappels ne seront pas de trop. Le '''demi-additionneur''' est un circuit qui additionne deux bits. Il implémente la table d'addition, qui est très simple en binaire. Jugez plutôt :
* 0 + 0 = 0, retenue = 0 ;
* 0 + 1 = 1, retenue = 0 ;
* 1 + 0 = 1, retenue = 0 ;
* 1 + 1 = 0, retenue = 1.
Mais un demi-additionneur n'additionne que deux bits, il manque de quoi additionner la retenue. Une solution pour cela est d'enchaîner deux demi-additionneurs : un qui additionne les deux bits de données, et un second qui additionne la retenue au résultat du précédent. La retenue finale se calcule en combinant les sorties de retenue des deux demi-additionneurs, avec une porte OU. Le résultat est un additionneur complet fabriqué avec deux demi-additionneurs. Pour vous en convaincre, établissez la table de vérité de ce circuit, vous verrez que ça marche.
[[File:Full Adder Blocks.svg|centre|vignette|upright=2|Additionneur complet fabriqué avec deux demi-additionneurs.]]
[[File:Half-adder.svg|vignette|upright=1|Circuit d'un demi-additionneur naïf.]]
L'implémentation naïve d'un demi-additionneur utilise une porte XOR et une porte ET, comme illustré ci-contre. Le circuit s'obtient en faisant uen traduction littérale de la table de vérité du circuit. Si je dis qu'elle est naïve, c'est car il est possible de l'optimiser, de manière à éliminer des redondances cachées. Mais nous avions déjà vu cela dans le chapitre sur les incrémenteurs, aussi laissons cela pour plus tard.
En combinant deux demi-additionneurs de ce type, on obtient l'additionneur complet suivant. Les deux sorties de retenue des demi-additionneurs sont combinées avec une porte OU, pour calculer la retenue finale.
{| class="flexible"
|[[File:Full Adder Modules.svg|class=transparent|Composition d'un additionneur complet. On voit bien que celui-ci est composé de deux demi-additionneurs, en rouge et en bleu, auxquels on a ajouté une porte OU pour calculer la retenue finale.]]
|[[File:Full-adder.svg|300px|class=transparent|Circuit d'un additionneur complet.]]
|}
Le fait de combiner les deux retenues avec une porte OU n'est pas ce qu'il y a de plus intuitif. La table de vérité nous dit que ça fonctionne, mais on comprend mal pourquoi. Mais tout devient plus clair quand on sait que les deux retenues ne peuvent pas être à 1 en même temps. Même dans l'addition 1 + 1 + 1, seule une retenue est à 1. Soit l'addition des deux bits d'opérande donne naissance à une retenue, soit c'est l'addition ''résultat + retenue entrante''. La retenue sortant est donc à 1 quand une des deux addition donne une retenue. D'où l'usage d'une porte OU. Remarquez qu'une porte XOR donne le même résultat, vu que le cas où les deux retenues sont à 1 n'est jamais rencontré.
===La propagation et la génération des retenues===
L'additionneur complet que nous allons voir dans cette section sert à introduire les concepts de propagation, génération et absorption de retenue. Il s'agit de concepts très importants quand on étudie les additionneurs. l'idée part d'un principe très simple : la retenue sortante dépend de la retenue d'entrée. La relation entre les deux se résume à trois cas, qui dépendent de la valeur des deux bits additionnés, nommés A et B.
* Dans le premier cas, la retenue entrante est égale à la retenue sortante. On dit que la retenue entrante est propagée par l'additionneur.
* Dans le second cas, la retenue est forcée à 0 : la retenue sortante vaut 0, peu importe la valeur de la retenue entrante.
* Dans le troisième cas, la retenue est forcée à 1 : la retenue entrante vaut 1, peu importe la valeur de la retenue entrante.
[[File:Signaux P et G fournis par un demi-additionneur.png|vignette|Signaux P et G fournis par un demi-additionneur]]
Maintenant, créons un circuit qui nous dise si une retenue est propagée ou générée. Il a deux bits de sortie, nommés P et G : P pour ''Propagate'', G pour ''Generate''. Le bit P est à 1 si la retenue entrante est propagée, il est à 0 sinon. Le bit G est à 1 si une retenue est générée, à 0 sinon. Une retenue est considérée comme absorbée si elle n'est pas ni propagée ni générée, pas besoin d'un troisième bit pour gérer ce cas.
Il se trouve que ce circuit n'est autre qu'un demi-additionneur ! Pour vous en rendre compte, regardez la table de vérité d'un additionneur complet, illustrée ci-dessous. Lorsque les deux bits d'opérande sont à 0, la retenue sortante vaut toujours 0. Si ils sont tous deux à 1, alors la retenue sortante vaut 1. S'ils sont différents, alors retenues sortante et entrante sont égales. Le bit P est donc généré par une simple porte XOR. Quant au bit G, il est à 1 si les deux bits d'opérandes sont à 1, ce qui correspond à une porte ET. Il se trouve que ces deux portes forment un demi-additionneur !
{|class="wikitable"
|-
! Opérande 1 !! Opérande 2 !! Retenue entrante !! !! Retenue sortante
|- class="f_rouge"
||0||0||0|| ||0
|- class="f_rouge"
||0||0||1|| ||0
|- class="f_bleu"
||0||1||0|| ||0
|- class="f_bleu"
||0||1||1|| ||1
|- class="f_bleu"
||1||0||0|| ||0
|- class="f_bleu"
||1||0||1|| ||1
|- class="f_vert"
||1||1||0|| ||1
|- class="f_vert"
||1||1||1|| ||1
|}
Ensuite, créons un circuit qui prend ces deux sorties P et G, et calcule la retenue sortante en fonction. Le circuit en question a trois entrées : la retenue entrante, les deux bits P et G. La retenue sortante vaut 1 soit si une retenue est générée, soit si la retenue entrante est propagée et qu'elle vaut 1. En notant <math>{C_{in}}</math> et <math>{C_{out}}</math> les retenues entrantes et sortante, on a :
: <math>{C_{out}} = G + \left( P . C_{in} \right)</math>
Le circuit est donc composé d'une porte OU et d'une porte ET. En combinant un demi-additionneur avec le circuit de calcul de retenue sortante vu plus haut, on a :
[[File:Additionneur complet avec propagation et génération de retenue.png|class=transparent|centre|vignette|upright=2|Additionneur complet avec propagation et génération de retenue.]]
Certes, on retombe sur le circuit vu plus haut. Mais il est possible de remplacer le circuit en orange par un autre. Par exemple, il est possible de le remplacer par multiplexeur, qui choisit entre la retenue générée et la retenue entrante (propagée). Le choix se fait selon la valeur du bit P, qui chosiit entre propager la retenue et la générer.
[[File:Additionneur crée avec un multiplexeur.png|centre|vignette|upright=1.5|Additionneur crée avec un multiplexeur]]
Le circuit exact est illustré ci-dessous. Il semble utiliser plus de portes logiques que l'additionneur complet précédent. Cependant, nous verrons dans quelques chapitres qu'il est possible d'implémenter un multiplexeur avec seulement 6 transistors, voire moins ! L'implémentation utilise des portes à transmission, mais nous en reparlerons dans le chapitre sur les transistors, quand nous verrons les additionneurs à ''Manchester Carry Chain''. Au passage, une variante de ce circuit a été utilisée dans le processeur processeur 8086 d'Intel, comme on le verra dans le chapitre suivant.
[[File:Additionneur complet basé sur un MUX.png|centre|vignette|upright=1|Additionneur complet basé sur un MUX]]
[[File:FulladderPG.png|vignette|upright=1|Additionneur complet avec deux sorties P et G.]]
Pour finir, sachez qu'il existe des additionneurs qui fournissent : le bit de résultat, deux sorties P et G qui indiquent si l'addition propage ou génère une retenue. Il ne s'agit pas d'additionneurs complets, car il manque de quoi calculer la retenue sortante. De tels additionneurs seront appelés des '''additionneurs partiels'''. De tels additionneurs sont utilisés dans certains additionneurs pour gagner en performance.
Enfin, il existe aussi ce que j'ai décidé d'appeler des '''additionneurs P/G''', qui sont des additionneurs complets auxquels on a ajouté deux sorties P et G, en plus de la sortie de retenue. Leur circuit est le même que celui d'un additionneur complet, auquel on a ajouté deux fils. Vu que ces deux sorties sont fournies par le premier demi-additionneur, ajouter ces deux sorties demande d'ajouter des fils, pas de portes logiques. De tels additionneurs sont aussi utilisés dans certains additionneurs pour gagner en performance.
[[File:FAwithGP.svg|centre|vignette|upright=2|Additionneur complet avec sorties P et G : circuit.Ç]]
===L'additionneur complet basé sur une porte à majorité===
Maintenant, voyons une dernière implémentation possible de l'additionneur complet. Mais avant de voir comment l'additionneur est implémenté, nous allons devoir faire un petit focus sur la retenue sortante. Reprenons la table de vérité, et regardons ce qu'il en est pour la retenue sortante uniquement.
{|class="wikitable"
|-
! Retenue entrante !! Opérande 1 !! Opérande 2 !! !! Retenue sortante
|-
||0||0||0|| ||0
|-
||0||0||1|| ||0
|-
||0||1||0|| ||0
|-
||0||1||1|| ||1
|-
||1||0||0|| ||0
|-
||1||0||1|| ||1
|-
||1||1||0|| ||1
|-
||1||1||1|| ||1
|}
Vous remarquerez que la retenue sortante est égale au bit majoritaire, parmi les trois bits d'opérande. Si deux ou trois bits sont à 1, la retenue sortante vaut 1. Et réciproquement avec 0. Le circuit de calcul de la retenue peut donc être remplacé par une '''porte à majorité'''.
[[File:Additionneur crée avec une porte à majorité.png|centre|vignette|upright=2|Additionneur crée avec une porte à majorité]]
Mais une porte à majorité est assez gourmande en circuit, ce qui fait qu'un additionneur pareil ne serait pas pratique, ni utile. Du moins, ce serait le cas s'il n'y avait pas une possibilité d'optimisation extrêmement intéressante. Il est possible de calculer le 'bit de somme' à partir de la retenue sortante ! En effet, le bit de somme est l'inverse de la retenue sortante, sauf dans deux cas : les trois bits d'entrée sont à 0, où ils sont tous à 1. Le bit de somme vaut 0 dans le premier cas, 1 dans le second cas.
{|class="wikitable"
|-
! Retenue entrante !! Opérande 1 !! Opérande 2 !! !! Retenue sortante !! Bit de somme
|- class="f_bleu"
||0||0||0|| ||0||0
|- class="f_vert"
||0||0||1|| ||0||1
|- class="f_vert"
||0||1||0|| ||0||1
|- class="f_vert"
||0||1||1|| ||1||0
|- class="f_vert"
||1||0||0|| ||0||1
|- class="f_vert"
||1||0||1|| ||1||0
|- class="f_vert"
||1||1||0|| ||1||0
|- class="f_rouge"
||1||1||1|| ||1||1
|}
En clair, 6 lignes de la table de vérité sur 8 peuvent se calculer avec une porte à majorité, le reste demandant quelques portes logiques pour faire la correction. L'implémentation en circuit demande donc trois choses :
* de calculer la retenue sortante et de quoi l'inverser ;
* un circuit qui vérifie si tous les bits opérande valent 0 : une porte NOR ou OU fait l'affaire ;
* un circuit qui vérifie s'ils valent tous 1, à savoir une porte ET.
Ensuite, on combine le résultat des trois circuits précédents pour obtenir le résultat final. La combinaison est le fait du circuit en jaune dans le schéma ci-dessous.
[[File:Full adder basé sur une modification de la retenue.png|centre|vignette|upright=2|Full adder basé sur une modification de la retenue]]
Le circuit en jaune est assez simple. Quand la porte ET sort un 1, la sortie doit être forcée à 1. Par contre, quand la porte OU sort un zéro, la sortie doit être forcée à 0. Il suffit donc d'enchainer un circuit de mise à 1 et un circuit de mise à 0. Nous avons vu dans le chapitre sur les opérations de masquage qu'il s'agit respectivement d'une porte OU et d'une porte ET.
[[File:Full adder basé sur une modification de la retenue interne, au complet.png|centre|vignette|upright=2|Full adder basé sur une modification de la retenue interne, au complet]]
Divers processeurs utilisaient des additionneurs complets de ce type : le fameux Z80, l'Intel 4004 et de l'Intel 8008 sont les exemples les plus notables. Pourtant, on pourrait s'étonner qu'un tel circuit ait existé. Il utilise beaucoup de portes logiques, a une profondeur logique supérieure : il n'a rien d'avantageux. Sauf qu'il était utilisé sur d'anciens processeurs, qui utilisaient la technologie dite TTL, différente de la technologie CMOS des transistors modernes. Et avec la technologie TTL, il est possible de fusionner plusieurs portes logiques ET et NOR en une seule porte logique ET/OU/NON ! Un additionneur complet construit ainsi ne prenait que deux portes logiques : une pour le calcul de la retenue sortante, une autre pour le reste du circuit.
==L'addition non signée==
Voyons maintenant un circuit capable d'additionner deux nombres entiers: l''''additionneur'''. Dans la version qu'on va voir, ce circuit manipulera des nombres strictement positifs. L'addition des nombres codés en complètement à deux sera vu dans une section ultérieure.
[[File:Full-adder-4bits-serial-CEI.svg|vignette|upright=0.5|Additionneur 4 bits, un bloc.]]
L'interface d'un additionneur est illustrée ci-contre. Un additionneur prend deux opérandes sur deux entrées séparées, et fournit le résultat sur une sortie dédiée. De plus, il a une entrée sur laquelle envoyer une retenue entrante, et a une sortie pour la retenue sortante. La sortie de retenue indique que le résultat de l'addition a débordé (au sens d'un débordement d'entier). Pour l'entrée de retenue, son utilité deviendra plus claire dans ce qui suit, mais souvenez-vous qu'elle existe.
L'entrée et la sortie de retenue sont parfois utilisées pour combiner plusieurs petits additionneurs, pour former un additionneur plus grand. Nous verrons de nombreux exemples dans ce qui suit. Il est assez commun de créer des additionneurs pour des opérandes de 16/32/64 bits, avec des additionneurs de 4/5 bits.
===L'additionneur série===
Il est possible d'additionner deux nombres bit par bit, grâce à un additionneur complet associé à plusieurs registres à décalages. Les opérandes sont placées chacune dans un registre à décalage, et l'additionneur complet utilise les bits sortants de ces registres à décalage. Le bit du résultat est envoyé au registre à décalage pour le résultat. La retenue de l'addition est stockée dans une bascule de 1 bit, en attente du prochain cycle d'horloge. Un tel additionneur est appelé un '''additionneur série'''. Il a été utilisé sur d'anciens ordinateurs dans les années 50-60, aussi bien des prototypes que des ordinateurs commerciaux.
[[File:Additionneur série.jpg|centre|vignette|upright=2.5|Additionneur série.]]
===L'additionneur à propagation de retenue===
L''''additionneur à propagation de retenue''' est aussi appelé l'additionneur ''ripple carry'', c'est son nom anglais. J'utiliserais occasionnellement le nom anglais dans ce qui suit, car celui-ci est plus court. Ilutilise un additionneur complet pour chaque colonne de l'addition. Pour gérer les retenues, il suffit ainsi de câbler des additionneurs complets les uns à la suite des autres, chacun envoyant sa retenue à la colonne suivante.
[[File:Ripplecarryadder.png|centre|vignette|upright=2.5|Additionneur à propagation de retenue.]]
Notons la présence de la retenue sortante, qui est utilisée pour détecter les débordements d'entier, ainsi que pour d'autres opérations. Le bit de retenue final est souvent stocké dans un registre spécial du processeur (généralement appelé ''carry flag'').
Notez aussi, à droite du schéma précédent, l'entrée de retenue <math>\text{R}_\text{Entrée}</math>. Elle est très utile pour l'implémentation de certaines opérations comme l'inversion de signe, la soustraction, l'incrémentation, etc. Certains processeurs sont capables de faire une opération appelée ADC, ADDC ou autre nom signifiant ''Addition with Carry'', qui permet de faire le calcul A + B + Retenue (la retenue en question est la retenue sortante de l'addition précédente, stockée dans le registre ''carry flag''). Son utilité principale est de permettre des additions d'entiers plus grands que ceux supportés par le processeur. Par exemple, cela permet de faire des additions d'entiers 32 bits sur un processeur 16 bits.
[[File:Adder Network 3 Bit Sum Delay 003.svg|vignette|upright=0.5|Propagation de retenue dans l'additionneur.]]
Cet additionneur utilise très peu de portes logiques, ce qui fait qu'il était utilisé sur certains processeurs 8 et 16 bits, dont le budget en portes logiques était limité. Bien que très simple, cet additionneur est cependant peu performant. Son temps de calcul est proportionnel à la taille des opérandes. Par exemple, additionner deux opérandes de 32 bits prendra deux fois plus de temps que pour des opérandes de 16 bits. La raison est que les retenues doivent se propager à travers le circuit, du premier additionneur jusqu'au dernier.
L'addition étant une opération fréquente, d'autres additionneurs ont été inventés. Ils utilisent des optimisations qui utilisent plus de circuits pour gagner en rapidité. Mais avant de voir ces optimisations, nous allons voir s'il est possible d'optimiser les additionneurs à propagation de retenue.
===L'additionneur à saut de retenue===
L'additionneur à propagation de retenue peut être optimisé de deux manières. La première accélère la propagation de la retenue, en agissant au niveau des additionneurs complets. La ''Manchester carry chain'' est une optimisation de ce type, mais on ne peut pas encore expliquer à ce stade du cours, car elle optimisait les additionneurs complets au niveau des transistors eux-mêmes. Elle a été utilisée sur de nombreux processeurs connus, comme le 8086 d'Intel ou le Z80.
La seconde manière rend l'addition plus rapide dans certains cas. Le résultat est un additionneur dont le temps de calcul est variable. Le calcul prendra quelques cycles d'horloges avec certains opérandes, mais d'autres opérandes ne verront aucune amélioration. Il n'améliore pas le pire des cas, dans lequel la retenue doit être propagée du début à la fin, du bit de poids faible au bit de poids fort. Mais dans d'autres cas, il permet d'avoir le résultat en avance. L'optimisation en question donne l''''additionneur à saut de retenue''' (''carry-skip adder'').
Il est composé en enchainant plusieurs additionneurs plus petits, qui additionnent 4/5 bits, rarement plus. De tels additionneurs de petite taille sont appelés des '''blocs''', dans ce qui suit. L'idée est de détecter précocement si la retenue entrante est propagée à travers tout le bloc. Si la retenue est propagée dans tous le bloc, on peut directement l'envoyer sur la sortie de retenue, la retenue saute le bloc entier. Dans le cas contraire, on doit calculer la retenue normalement. Le choix entre les deux est le fait d'un multiplexeur. Toute la difficulté est de savoir comment commander le multiplexeur.
[[File:Carry skip adder, principe de base.png|centre|vignette|upright=2.5|Carry skip adder : principe de base]]
Pour savoir si une retenue est propagée dans un bloc entier, il faut utiliser des additionneurs complets, qui ont en plus une sortie P indiquant que l'additionneur propage la retenue. Le bloc propage une retenue si chaque additionneur complet propage la retenue, donc s'ils ont tous leur sortie P à 1. Un simple ET logique suffit à déterminer si c'est le cas. Le signal de commande du multiplexeur est donc un vulgaire ET entre toutes les sorties P des additionneurs complets du bloc.
[[File:Additionneur à saut de retenue.png|centre|vignette|upright=2.5|Calcul de la commande du MUX.]]
Voici ce que ça donne pour un bloc de 4 bits :
[[File:CSAdder4Bit.svg|centre|vignette|upright=1.5|Additionneur ''carry skip'' : bloc de 4 bits.]]
L'additionneur à saut de retenue est construit en assemblant plusieurs blocs de ce type.
[[File:BCSAdder16Bit.svg|centre|vignette|upright=3|Additionneur à saut de retenue.]]
Les blocs sont tous identiques dans le cas le plus simple, mais il est possible d'utiliser des blocs de taille variable. Par exemple, le premier bloc peut avoir des opérandes de 6 bits, le second des opérandes de 7 bits, etc. Faire ainsi permet de gagner un petit peu en performances, si la taille de chaque bloc est bien choisie. La raison est une question de temps de propagation des retenues. La retenue met plus de temps à se propager à travers 8 blocs qu'à travers 4, ce qui prend plus de temps qu'à travers 2 blocs, etc. En tenir compte fait que la taille des blocs tend à augmenter ou diminuer quand on se rapproche des bits de poids fort.
===L'additionneur à sélection de retenue===
L''''additionneur à sélection de retenue''' utilise aussi des blocs, comme les additionneurs précédents. L'addition se fait en deux versions : une avec la retenue du bloc précédent valant zéro, et une autre version avec la retenue du bloc précédent valant 1. Il suffira alors de choisir le bon résultat avec un multiplexeur, une fois cette retenue connue. On gagne ainsi du temps en calculant à l'avance les valeurs de certains bits du résultat, sans connaître la valeur de la retenue. Petit détail : sur certains additionneurs à sélection de retenue, les blocs de base n'ont pas la même taille. Cela permet de tenir compte des temps de propagation des retenues entre les blocs.
[[File:Additionneur à sélection de retenue.png|centre|vignette|upright=1.5|Additionneur à sélection de retenue avec seulement deux blocs.]]
Dans les exemples du dessus, chaque sous-additionneur étaient des additionneurs à propagation de retenue. Mais ce n'est pas une obligation, et tout autre type d’additionneur peut être utilisé. Par exemple, on peut faire en sorte que les sous-additionneurs soient eux-mêmes des additionneurs à sélection de retenue, et poursuivre ainsi de suite, récursivement. On obtient alors un '''additionneur à somme conditionnelle''', plus rapide que l'additionneur à sélection de retenue, mais qui utilise beaucoup plus de portes logiques.
===Les additionneurs à anticipation de retenue===
Les '''additionneurs à anticipation de retenue''' calculent chaque retenue sans avoir à propager les retenues précédentes. Au lieu de calculer les retenues une par une, ils calculent toutes les retenues en parallèle, à partir des bits des opérandes. Une fois les retenues pré-calculées, il suffit de les additionner avec les deux bits adéquats, pour obtenir le résultat. Ces additionneurs sont composés de deux parties :
* un circuit qui pré-calcule les retenues, sans les propager, directement à partir des opérandes ;
* d'une couche d'additionneurs complets, qui additionnent chacun deux bits d'opérande et la retenue pré-calculée.
[[File:Additionneur à anticipation de retenue.png|centre|vignette|upright=1.5|Additionneur à anticipation de retenue.]]
Le calcul des retenues se fait en deux étapes. La première détermine si les retenues sont propagées ou générées, pour chaque colonne. Elle détermine, pour chaque colonne, les bits P et G qui indiquent respectivement : qu'une retenue a été générée sur cette colonne, que la colonne propage la retenue précédente. La seconde étape utilise ces signaux P et G pour déterminer la retenue sur chaque colonne. Le circuit de calcul des retenues est donc composé de deux parties : une couche de demi-additionneurs pour générer les signaux P et G, un circuit de calcul des retenues proprement dit.
[[File:Circuit complet d'un additionneur à anticipation de retenue.png|centre|vignette|upright=1.5|Circuit complet d'un additionneur à anticipation de retenue.]]
Pour rappel, la retenue de la colonne i, notée <math>C_i</math> est égale à :
: <math>G_i + (P_i . C_{i-1})</math>, avec G le signal de génération de retenue, P le signal de propagation de retenue.
Si on utilisait cette formule sans trop réfléchir, on retomberait sur un additionneur à propagation de retenue inutilement compliqué. L'astuce des additionneurs à anticipation de retenue consiste à remplacer le terme Ci−1 par sa valeur calculée avant. Par exemple, je prends un additionneur 4 bits. Je dispose de deux nombres A et B, contenant chacun 4 bits : A3, A2, A1, et A0 pour le nombre A, et B3, B2, B1, et B0 pour le nombre B. Si j'effectue les remplacements, j'obtiens les formules suivantes :
* C1 = G0 + ( P0 · C0 ) ;
* C2 = G1 + ( P1 · G0 ) + ( P1 · P0 · C0 ) ;
* C3 = G2 + ( P2 · G1 ) + ( P2 · P1 · G0 ) + ( P2 · P1 · P0 · C0 ) ;
* C4 = G3 + ( P3 · G2 ) + ( P3 · P2 · G1 ) + ( P3 · P2 · P1 · G0 ) + ( P3 · P2 · P1 · P0 · C0 ).
Ces formules nous permettent de déduire la valeur d'une retenue directement : il reste alors à créer un circuit qui implémente ces formules, et le tour est joué.
[[File:Four bit adder with carry lookahead.svg|centre|vignette|upright=1.5|Additionneur à anticipation de retenue de 4 bits.]]
Plus haut, j'ai dit que le circuit était composé de trois couches : une couche de demi-additionneur pour les signaux P et G ; un circuit d'anticipation de retenue, puis l'addition finale. Mais les deux bits d'opérandes sont déjà additionnés dans la couche de demi-additionneur, autant réutiliser le résultat pour l'additionner à la retenue. Pour éliminer toute redondance de ce type, il est possible d'utiliser des additionneurs partiels (des additionneurs où la sortie de retenue a été remplacée par deux sorties P et G). L'additionneur final est alors composé de plusieurs additionneurs complets de ce type, couplé à un circuit d'anticipation de retenue.
[[File:Cla4bits.png|centre|vignette|upright=2.5|Additionneur à anticipation de retenue de 4 bits.]]
Les additionneurs à anticipation de retenue sont plus rapides que les additionneurs à propagation de retenue. La raison est que les retenues sont calculées rapidement, dans un temps plus petit qu'en propageant la retenue. Pour rentrer dans les détails mathématiques, le temps de calcul n'est pas proportionnel au nombre de bits, mais proportionnel au logarithme de ce nombre de bits. Par contre, le gain en temps de calcul s'accompagne d'un cout en portes logiques conséquent. Le circuit qui calcule les retenues anticipées utilise beaucoup de portes logiques.
===Les additionneurs à calcul parallèle de préfixe===
Du fait du cout en portes logiques, utiliser un additionneur à anticipation de retenue est une solution pratique pour additionner 4 ou 8 bits. Mais pour des opérandes plus grands, de 16 ou 32 bits, cela utiliserait trop de portes logiques. Une partie de ce cout en portes logiques est cependant lié à des redondances. Si on analyse deux retenues différentes, à des colonnes différentes. Les circuits qui calculent ces deux retenues seront différents, mais ils contiendront des sous-circuits identiques. Pour vous en rendre compte, reprenons les quatre équations obtenues précédemment et essayez de trouver des redondances :
* C1 = G0 + ( P0 · C0 ) ;
* C2 = G1 + ( P1 · G0 ) + ( P1 · P0 · C0 ) ;
* C3 = G2 + ( P2 · G1 ) + ( P2 · P1 · G0 ) + ( P2 · P1 · P0 · C0 ) ;
* C4 = G3 + ( P3 · G2 ) + ( P3 · P2 · G1 ) + ( P3 · P2 · P1 · G0 ) + ( P3 · P2 · P1 · P0 · C0 ).
Vous devriez trouver :
* le terme ''P0 · C0'' dans toutes les lignes ;
* les termes ''P1 · P0 · C0'' et ''P1 · P0'' dans les trois dernières lignes ;
* le terme ''P2 · P1 · P0 · C0'' ; ''P2 · P1 · G0'' et ''P2 · G1'' dans les deux dernières lignes.
Il existe plusieurs manières d'exploiter de telles redondances, exploitées dans les '''additionneurs à calcul parallèle de préfixe'''. L'optimisation apportée est de générer des signaux ''propagate'' et ''generate'' pour des groupes de 2, 3, 4, ..., N colonnes. Par exemple, il est possible de générer un signal ''P 0 vers 3'', qui indique si la retenue de la première colonne est propagée jusqu'à la 4ème colonne. Un autre exemple est un signal qui indique qu'une retenue a été générée entre les colonnes 4 à 7 génèrent une retenue ou non. En clair, les signaux P et G ont maintenant un intervalle, qui précise de quelle colonne vers quelle colonne se fait la propagation, ou entre quelles colonnes se fait la génération.
Il est possible de combiner les signaux P et G de deux groupes de colonne, s'ils sont contiguës (ils peuvent aussi se recouvrir). Par exemple, il est possible de calculer les bits P et G pour les colonnes 0 à 10, à partir des deux signaux P/G des colonnes 0-4 et 5-10. Pour cela, les équations sont assez simples. Si je prends deux groupes nommés A et B, avec A pour les colonnes de poids fort et B celles de poids faible, on a :
: <math>P_{sortie} = P_A . P_B</math>
: <math>G_{sortie} = G_A . P_A . G_B</math>
Pour les bits des colonnes 2, 4, 8, 16, 32, 64, et autres, le calcul est simple. L'idée est de grouper les colonnes par groupes de 2. Puis, on calcule chaque groupe de 4 bits à partir de deux groupes de 2. Puis les groupes de 8 bits sont calculés à partir de deux groupes de 4 bits, et ainsi de suite pour toute puissance de deux.
[[File:Squelette d'un additionneur à calcul parallèle de préfixe.jpg|centre|vignette|upright=2|Squelette d'un additionneur à calcul parallèle de préfixe]]
Les seules difficultés surviennent pour les colonnes dont le nombre n'est pas une puissance de deux. Il y a plusieurs manières pour subdiviser ces colonnes en intervalles plus petits et combiner le tout. Et elles donnent chacune des additionneurs différents. Ils portent les noms d''''additionneur de Ladner-Fisher''', d''''additionneur de Brent-Kung''', d''''additionneur de Kogge-Stone''', etc. Dans le cas général, l'additionneur de Kogge-Stone est un des plus rapide, mais aussi un des plus gourmand en circuits, alors que c'est l'inverse pour l'additionneur de Brent-Kung. Les deux additionneurs sont illustrés ci-dessous, pour des opérandes de 8 bits. Dans ceux-ci, chaque losange calculent les bits P et G, à partir des signaux P et G de deux groupes précédents.
{|
|[[File:Brent-kung-8-bit.png|vignette|Brent-kung-8-bit]]
|[[File:Kogge-stone-8-bit.png|vignette|upright=1.2|Kogge-stone-8-bit]]
|}
===Le calcul parallèle de la retenue===
L'anticipation de retenue est très rapide, mais utilise beaucoup de circuits, même en utilisant le calcul parallèle de préfixe. Les additionneurs à propagation de retenue font eux le compromis inverse. Mais il existe des intermédiaires, qui visent à obtenir un compromis entre performance et cout en portes logiques.
Un de ces compromis découpe un additionneur de 16/32/64 bits en additionneurs de 4/5 bits, qui sont enchainés en utilisant la propagation de retenue. Par contre, ces petits additionneurs de 4/5 bits utilisent l'anticipation de retenue. Ces additionneurs simples seront nommés ''blocs'' dans ce qui suit. Par exemple, on peut utiliser 4 additionneurs à anticipation de retenue de 4 bits, et propager les retenues entre eux. Le premier bloc calcule une retenue, qui est propagée au second bloc. Le second bloc calcule lui aussi une retenue, qui est propagée au troisième bloc, etc.
[[File:4008 Functional Diagram.svg|vignette|upright=1|4008 Functional Diagram]]
Le gain en performance est significatif, mais il est possible de faire presque aussi rapide, tout en économisant beaucoup de circuits. L'idée est de simplifier les additionneurs 4 bits, afin de ne pas utiliser une anticipation de retenue complète. L'anticipation de retenue n'est utilisée que pour la retenue sortante, alors que les bits de somme sont calculées avec propagation de retenue. La retenue sortante est donc calculée en parallèle de l'addition. Chaque bloc contient, à côté d'un additionneur proprement dit, un circuit qui calcule la retenue sortante. Il existe de nombreuses manières de calculer la retenue sortante.
Le gain en portes logiques est assez significatif. En effet, on se passe de circuits d'anticipation de retenue pour les 4 bits de somme, on ne garde que celui pour la retenue sortante. Le fait d'utiliser la propagation de retenue pour calculer les 4 bits de somme est censé avoir un léger cout en performance, mais il se trouve que ce cout est très faible. En effet, le résultat n'est connu qu'une fois les retenues propagées jusqu'au dernier bloc. Et malgré l'anticipation des retenues, la propagation est assez lente. les additionneurs à propagation de retenue auront terminé leur travail bien avant.
Un exemple est celui de l'additionneur CMOS 4008, un additionneur de 4 bit. Il est composé en trois sections. Une première couche de demi-additionneurs calcule les signaux P et G utilisés à la fois pour l'anticipation de la retenue sortante, que pour calculer les bits de somme. Le circuit de calcul de la retenue sortante utilise l'anticipation de retenue. Le reste du circuit fait l'addition en propageant les retenues.
[[File:CMOS 4008, circuit découpé en sections.png|centre|vignette|upright=1.5|CMOS 4008, circuit découpé en sections]]
===Les additionneurs à anticipation de retenue multi-niveau===
Les additionneurs précédents mixent anticipation et propagation de retenue. Ceux que nous allons voir utilisent des blocs, mais utilisent l'anticipation de retenue entre les blocs. Les blocs utilisés peuvent être un additionneur à propagation de retenue, ou à anticipation de retenue, peu importe. Ce qui compte est que la retenue entrante de chaque bloc est calculée par anticipation de retenue.
Par exemple, il est possible de créer un additionneur 16 bits en utilisant 4 additionneurs/blocs de 4 bits, couplé à un circuit d’anticipation de retenue par bloc. Pour cela, les additionneurs de 4 bits doivent fournir deux sorties P et G. Les deux sorties indiquent que le bloc soit a généré une retenue, soit a propagé la retenue entrante. Par propagé, on veut dire que la retenue entrante du bloc s'est retrouvée sur la sortie de retenue du bloc. C'est le cas si les 4 colonnes propagent la retenue, ce qui signifie que :
: <math>P_\text{4 bits} = P_0 \cdot P_1 \cdot P_2 \cdot P_3</math>
Pour la génération, cela veut dire qu'une colonne a générée une retenue, qui a été propagée jusqu'à la sortie. En clair, pour chaque colonne, il faut que le G = 1, et que les signaux P des colonnes soient à 1. Pour les 4 colonnes, cela donne :
: <math>G_\text{4 bits} = G_3 + G_2 \cdot P_3 + G_1 \cdot P_3 \cdot P_2 + G_0 \cdot P_3 \cdot P_2 \cdot P_1</math>
[[File:Cla4bitsPG.png|centre|vignette|upright=2|Additionneur à anticipation de retenue de 4 bits.]]
Pour combiner 4 blocs de 4 bits, on devra donc combiner 4 bits P et G, avec un circuit d'anticipation de retenue.
[[File:16-bit lookahead carry unit.svg|centre|vignette|upright=2|Additionneur à anticipation de retenue de 16 bits.]]
Il est même possible d'aller plus loin et de combiner 4 additionneurs précédents pour obtenir un additionneur 64 bits.
[[File:64-bit lookahead carry unit.svg|centre|vignette|upright=2|Additionneur à anticipation de retenue de 64 bits.]]
Le résultat est un additionneur à plusieurs niveaux d’anticipation de retenue. Une anticipation de retenue dans un blocs de 4 bits (facultative), entre blocs de 4 bits, entre blocs de 16 bits. La même logique peut être utilisée avec des blocs de taille différente de 4, 16 et 64 bits.
==L'addition signée et la soustraction==
Après avoir vu l'addition, il est logique de passer à la soustraction, les deux opérations étant très proches. Si on sait câbler une addition entre entiers positifs, câbler une soustraction n'est pas très compliqué. De plus, la soustraction permet de faire des additions de nombres signés.
===Le soustracteur à propagation de retenue===
Pour soustraire deux nombres entiers, on peut adapter l'algorithme de soustraction utilisé en décimal, celui que vous avez appris à l'école. Celui-ci ressemble fortement à l'algorithme d'addition : on soustrait les bits de même poids, et on propage éventuellement une retenue sur la colonne suivante. À la différence de l'addition, la retenue est soustraite, et non ajoutée. En clair, une soustraction en binaire demande de faire deux soustraction : pour chaque bit de la première opérande, on soustrait le bit associé de la seconde, puis on soustrait la retenue entrante.
[[File:Algebra1 05 fig014.svg|centre|vignette|Soustraction en binaire, avec les retenues en rouge.]]
[[File:Half Subtractor Vektor.svg|vignette|Demi-soustracteur.]]
Dans le chapitre sur les incrémenteurs/décrémenteurs, nous avons vu un circuit qui soustrait deux bits, appelé le '''demi-soustracteur'''. Il ressemble beaucoup à un demi-additionneur, les différences se résumant à une porte NON ajoutée pour le calcul de la retenue. Il implémente la table de soustraction, qui est très simple en binaire :
* 0 - 0 = 0 ;
* 0 - 1 = 1 et une retenue ;
* 1 - 0 = 1 ;
* 1 - 1 = 0.
Comme pour les additionneurs, deux demi-soustracteurs peuvent être combinés pour donner un '''soustracteur complet'''. Le calcul de la retenue se fait en combinant les deux retenues des demi-soustracteurs avec une porte OU. Là encore, si on regarde les sorties des deux demi-soustracteurs, il est impossible d'avoir les deux sorties de retenues à 1 en même temps.
[[File:FullSubtractor.svg|centre|vignette|upright=2|Soustracteur complet.]]
[[File:Sub Network 3 Bit Step 002.svg|vignette|upright=1|Soustracteur à propagation de retenue pour opérandes de 3 bits.]]
Les soustracteurs complets sont utilisés pour créer des soustracteurs à propagation de retenue, sur le même modèle que les additionneurs. Et je précise bien "à propagation de retenue", car l'anticipation de retenue et les autres optimisations ne marchent pas exactement pareil entre addition et soustraction. Les signaux de propagation et de génération de retenue ne sont pas générés pareil, idem pour la manière de les combiner.
Il est possible de fusionner un additionneur à propagation de retenue et un soustracteur de même type. Pour cela, il suffit de remarquer que les demi-soustracteurs et les demi-additionneurs se ressemblent beaucoup. La seule différence entre les deux est une vulgaire porte NON. En remplaçant cette porte NON par un inverseur commandable, on obtient un circuit qui sert soit de demi-additionneur, soit de demi-soustracteur, suivant ce qu'on envoie sur l'entrée de commande. Ci-dessous est illustré ce que donne ce remplacement pour un soustracteur complet. Le résultat est un '''additionneur/soustracteur à propagation de retenue'''.
[[File:Additionneur-soustracteur complet.png|centre|vignette|upright=2|Additionneur-soustracteur complet]]
Il est donc possible de fusionner un additionneur avec un soustracteur, ce qui donne un '''additionneur/soustracteur'''. Mais comme je l'ai dit plus haut, cela ne marche que si l'''additionneur-/soustracteur'' utilise la propagation de retenue. Dès qu'on souhaite utiliser l'anticipation de retenue ou tout autre technique, les choses sont plus compliquées. Le calcul des retenues étant différent entre addition et soustraction, mutualiser les circuits d'anticipation de retenue pour les deux opérations ne donnera pas un résultat très propre. Ce qui n'est pas le cas avec les solutions que nous allons voir dans ce qui suit.
===L'additionneur-soustracteur pour opérandes codées en complément à deux===
Étudions le cas de la soustraction en complément à deux. Vous savez sûrement que a−b et a+(−b) sont deux expressions équivalentes. Et en complément à deux, − b = not(b) + 1. Dit autrement, a − b = a + not(b) + 1. On pourrait se dire qu'il faut deux additionneurs pour faire le calcul, mais la majorité des additionneurs possède une entrée de retenue pour incrémenter le résultat de l'addition. Un soustracteur en complément à deux est donc simplement composé d'un additionneur et d'un inverseur.
[[File:Additionneur soustracteur.png|centre|vignette|upright=2|Soustracteur en complément à deux.]]
Il est possible de créer un circuit capable d'effectuer soit une addition, soit une soustraction : il suffit de remplacer l'inverseur par un inverseur commandable, qui peut être désactivé. On a vu comment créer un tel inverseur commandable dans le chapitre sur les circuits combinatoires. On peut remarquer que l'entrée de retenue et l'entrée de commande de l'inverseur sont activées en même temps : on peut fusionner les deux signaux en un seul.
[[File:Additionneur-soustracteur en complément à deux.png|centre|vignette|upright=2|Additionneur-soustracteur en complément à deux.]]
Une implémentation alternative est la suivante. Elle remplace l'inverseur commandable par un multiplexeur.
[[File:4-bit ripple carry adder-subtracter.svg|centre|vignette|upright=2|Additionneur-soustracteur en complément à deux, version alternative.]]
===L'additionneur-soustracteur pour opérandes codées en signe-magnitude===
Passons maintenant aux nombres codés en signe-valeur absolue, les deux opérandes étant notées A et B. Suivant les signes des deux opérandes, on a quatre cas possibles : A + B, A − B (B négatif), −A + B (A négatif) et −A − B (A et B négatifs). Une astuce est que le circuit n'a besoin que de calculer A + B et A − B : il peut les inverser pour obtenir − A − B ou B − A. A + B et A − B peuvent se calculer avec un additionneur-soustracteur, reste à corriger le résultat. Il suffit de lui ajouter un inverseur commandable pour obtenir le circuit d'addition finale.
[[File:Additionneur en signe-valeur absolue.png|centre|vignette|upright=2|Additionneur en signe-valeur absolue.]]
Toute la difficulté tient dans le calcul du bit de signe du résultat, quand interviennent des soustractions. Autant l'addition de deux nombres de même signe (A + B et −A − B) ne pose aucun problème, autant les soustractions posent problème (A − B et −A + B). Suivant que <math>A<B</math> ou que <math>A>B</math>, le signe du résultat ne sera pas le même. Déterminer le signe du résultat se fait en regardant les bits de débordement d'entier, comme on le verra plus bas.
===L'additionneur-soustracteur pour opérandes codées en représentation par excès===
Passons maintenant aux nombres codés en représentation par excès. On pourrait croire que ces nombres s'additionnent comme des nombres non-signés, mais ce serait oublier la présence du biais, qui pose problème. Dans les cas de nombres signés gérés avec un biais, voyons ce que donne l'addition de deux nombres :
: <math>( n_1 + biais ) + ( n_2 + biais ) = n_1 + n_2 + 2 \times biais</math>
Or, le résultat correct serait :
: <math>n_1 + n_2 + biais</math>
En effectuant l'addition telle quelle, le biais est compté deux fois. On doit donc le soustraire après l'addition pour obtenir le résultat correct.
Même chose pour la soustraction qui donne ceci :
: <math>(n_1 + biais) - (n_2 + biais) = n_1 - n_2</math>
Or, le résultat correct serait :
: <math> ( n_1 - n_2 ) + biais </math>
Il faut rajouter le biais pour obtenir l'exposant correct.
On a donc besoin de deux additionneurs/soustracteurs : un pour additionner/soustraire les représentations binaires des opérandes, et un autre pour ajouter/retirer le biais en trop/manquant.
==L'additionneur BCD==
Maintenant, voyons un additionneur qui additionne deux entiers au format BCD. Pour cela, nous allons devoir passer par deux étapes. La première est de créer un circuit capable d'additionneur deux chiffres BCD. Ensuite, nous allons voir comment enchaîner ces circuits pour créer un additionneur BCD complet.
===L'additionneur BCD qui fait l'opération chiffre par chiffre===
Nous allons commencer par voir un additionneur qui additionne deux chiffres en BCD, une sorte d'équivalent BCD de l'additionneur complet. Il fournit un résultat sur 4 bits et une retenue qui est mise à 1 si le résultat dépasse 10 (la limite d'un chiffre BCD). Les deux opérandes sont des chiffres BCD codés sur 4 bits et sont additionnés en binaire par un additionneur des plus normaux, similaire à ceux vus plus haut. Le résultat est alors un entier codé en binaire, sur 5 bits, qu'on corrige/convertit pour obtenir un chiffre BCD et une retenue sortante.
Pour corriger le résultat, une idée intuitive serait de prendre le résultat et de faire une division par 10. Le quotient donne la retenue, alors que le reste est le résultat, le chiffre BCD. Mais un circuit diviseur par 10 utilise beaucoup de portes logiques, ce qui ne vaut pas le coup. Une autre méthode détecte si le résultat est égal ou supérieur à 10, ce qui correspond à un "débordement" (on dépasse les limites d'un chiffre BCD). Si le résultat est plus petit que 10, il n'y a rien à faire : le résultat est bon et la retenue est de zéro. Par contre, si le résultat vaut 10 ou plus, il faut corriger le résultat et générer une retenue à 1.
Il faut donc ajouter un circuit qui détecte si le résultat est supérieur à 9, qui calcule directement la retenue. Ce circuit peut se fabriquer simplement à partir de sa table de vérité, ou en utilisant les techniques que nous verrons dans un chapitre ultérieur sur les comparateurs. La solution la plus simple est clairement d'utiliser la table de vérité, ce qui est très simple, assez pour être laissé en exercice au lecteur. Pour comprendre comment corriger le résultat, établissons une table de vérité qui associe le résultat et le résultat corrigé. L'entrée vaut au minimum 10 et au maximum 9 + 9 = 18. On considère la sortie comme un tout, la retenue étant un 5ème bit, le bit de poids fort.
{|class="wikitable"
|-
! colspan="5" | Entrée
!
! rowspan="10" |
! Retenue
! Résultat corrigé (sans retenue)
! interprétation de la sortie en binaire (retenue inclue)
|-
| 0 || 1 || 0 || 1 || 0
| (10)
| 1 || 0000
| (16)
|-
| 0 || 1 || 0 || 1 || 1
| (11)
| 1 || 0001
| (17)
|-
| 0 || 1 || 1 || 0 || 0
| (12)
| 1 || 0010
| (18)
|-
| 0 || 1 || 1 || 0 || 1
| (13)
| 1 || 0011
| (19)
|-
| 0 || 1 || 1 || 1 || 0
| (14)
| 1 || 0100
| (20)
|-
| 0 || 1 || 1 || 1 || 1
| (15)
| 1 || 0101
| (21)
|-
| 1 || 0 || 0 || 0 || 0
| (16)
| 1 || 0110
| (22)
|-
| 1 || 0 || 0 || 0 || 1
| (17)
| 1 || 0111
| (23)
|-
| 1 || 0 || 0 || 1 || 0
| (18)
| 1 || 1000
| (24)
|}
En analysant le tableau, on voit que pour corriger le résultat, il suffit d'ajouter 6. La raison est que le résultat déborde d'un nibble à 16 en binaire, mais à 10 en décimal : il suffit d'ajouter la différence entre les deux, à savoir 6, et le débordement binaire fait son travail. Donc, la correction après une addition est très simple : si le résultat dépasse 9, on ajoute 6.
On peut maintenant implémenter l'additionneur BCD, en combinant le comparateur avec 10, le circuit de correction, et l'additionneur. La première solution calcule deux versions du résultat : la version corrigée, la version normale. Le choix entre les deux est réalisée par un multiplexeur, commandé par le comparateur.
[[File:Additionneur BCD.png|centre|vignette|upright=1.5|Additionneur BCD]]
L'autre solution utilise un circuit commandable qui soit additionne 6, soit ne fait rien. Le choix entre les deux est commandé par le bit calculé par le comparateur.
[[File:Additionneur BCD, seconde version.png|centre|vignette|upright=1.5|Additionneur BCD, seconde version.]]
Une version alternative du circuit précédent est la suivante. Il contient deux additionneurs : un pour additionner les deux chiffres BCD, un autre pour additionner 6 si besoin. Le résultat du comparateur est directement utilisé pour générer l'opérande du second additionneur : 0 ou 6. Le circuit est simple à concevoir, mais gaspille beaucoup de circuit. Idéalement, il vaudrait mieux utiliser un circuit combinatoire d'addition avec une constante.
[[File:Adder 4 Bit BCD.svg|centre|vignette|upright=2|Additionneur BCD, circuit complet.]]
Pour obtenir un additionneur BCD complet, il suffit d’enchaîner les additionneurs précédents, comme on le ferait avec les additionneurs complets dans un additionneur à propagation de retenue. Au final, l'additionneur BCD est beaucoup plus compliqué qu'un additionneur normal, car il rajoute un comparateur ">9", un petit additionneur pour ajouter 6 et éventuellement d'autres circuits. De plus, il est difficile d'appliquer les optimisations disponibles sur les additionneurs non-BCD. Notamment, les circuits d'anticipation de retenue sont totalement à refaire et le résultat est relativement compliqué. C'est ce qui explique pourquoi le BCD a progressivement été abandonné au profit du binaire simple.
La soustraction en BCD se fait comme en binaire : le nombre à soustraire est remplacé par son complément, le circuit additionne le complément et l'autre opérande, le débordement d'entier fait que le résultat marche. Sauf qu'ici, le complément est un complément à 9. Il se calcule chiffre par chiffre : chaque chiffre est remplacé par (9 - le chiffre en question).
===L'additionneur BCD par ajustement décimal===
L'additionneur BCD précédent effectuait son travail chiffre BCD par chiffre BCD, mais il existe des additionneurs BCD qui font autrement. Sur les premiers processeurs x86, il n'y avait pas d'opération d'addition BCD proprement dit, seulement une addition binaire normale de 8, 16 ou 32 bits. Par contre, elle était secondée par une opération dite d''''ajustement décimal''' qui transformait un nombre binaire en nombre codé en BCD. L'opération d'ajustement décimal prenait un opérande de 8 bits codé en binaire et fournissait un résultat de la même taille, c'est à dire deux chiffres BCD. Effectuer une addition BCD demandait donc de faire deux opérations à la suite : une addition binaire simple, suivie par l'opération d'ajustement décimal. Cela permettait de gérer des nombres entiers en binaire usuel et des entiers BCD sans avoir deux instructions d'addition séparées pour les deux, sans compter que cela simplifiait aussi les circuits d'addition.
L'ajustement décimal s'effectue en ajoutant une constante bien précise à l'opérande à convertir en BCD. L'idée est que la constante est découpée en morceaux de 4 bits, correspondant chacun à un chiffre BCD de l'opérande, chaque morceau contenant soit un 0, soit 6. Cela permet d'ajouter soit 0, soit 6, à chaque chiffre BCD, et donc de le corriger. La propagation des retenues d'un chiffre à l'autre est effectuée automatiquement par l'addition binaire de la constante. L'opération d'ajustement décimal calcule automatiquement la constante. Elle découpe l'opérande en ''nibbles'', vérifie si chaque ''nibble'' est supérieur ou égal à 10, puis détermine la valeur de chaque ''nibble'' de la constante finale. Par exemple, si je prends l'opérande 1001 1110, le ''nibble'' de poids faible déborde, alors que celui de poids fort non. La constante sera donc 0000 0110 : 0x06. Inversement, si le ''nibble'' de poids fort déborde et pas celui de poids faible, la constante sera alors 0x60. Et la constante est de 0x66 si les deux ''nibbles'' débordent, de 0x00 si aucun ne déborde.
Le circuit d’ajustement décimal est donc composé de trois étapes : deux étapes pour calculer la constante, et un circuit d'addition pour additionner cette constante au nombre de départ. La première étape découpe l'opérande en morceaux de 4 bits, en chiffres BCD, et vérifie si chacun d'entre eux vaut 10 ou plus. La seconde étape prend les résultats de la première étape, et les combine pour calculer la constante. Enfin, on trouve l'addition finale, qui était réalisée par un circuit d'addition utilisé à la fois pour l'ajustement décimal et l'addition binaire. La différence entre une addition normale et une opération d'ajustement décimal tient dans le fait que les deux premières étapes sont désactivées dans une addition normale.
[[File:Additionneur BCD parallèle.png|centre|vignette|upright=1.5|Additionneur BCD parallèle]]
===L'additionneur biquinaire===
Les entiers BCD ne sont qu'un des encodages hybrides entre décimal et binaire. L'encodage biquinaire est l'un d'entre eux et nous allons faire un rappel rapide à ce sujet. Pour simplifier, un chiffre encodé en biquinaire est composé de deux parties : un bit, couplé à une partie quinaire encodée en représentation ''one-hot''. La partie quinaire encode un nombre allant de 0 à 4, ce qui prend 5 bits (0, 1, 2, 3 et 4). Le bit indique s'il faut ou non ajouter 5 à la valeur encodée par la partie quinaire. Ainsi, on peut coder tous les nombres de 0 à 9.
Additionner deux nombres de biquinaire demande donc d'additionner deux parties quinaires encodées en ''one-hot'' et d'additionner deux bits. Mais attention : il faut tenir compte de la retenue de l'addition des parties quinaires. Et idéalement, il faut aussi tenir compte d'une retenue entrante, provenant de l'addition de la colonne de chiffres précédente. Toute la difficulté vient de la création de l'additionneur ''one-hot''. Heureusement, vu qu'il n'y a que 4-5 bits à additionner, il est souvent fabriqué à partir de sa table de vérité.
[[File:Additionneur bi-quinaire.png|centre|vignette|upright=2|Additionneur bi-quinaire]]
Un avantage du biquinaire est que le calcul du complément à 9 est très simple. Il faut pour cela : inverser la partie binaire avec une porte NON, puis inverser l'ordre des bits de la partie quinaire. Concrètement, le bit de poids faible devient le bit de poids fort, et ainsi de suite. Par exemple, une partie quinaire 01000 devient 00010, 10000 devient 00001, 00100 ne change pas, etc. Le tout peut se calculer avec une porte NON et 5 multiplexeurs.
===L'additionneur BCD avec calculs intermédiaires en biquinaire===
L'ordinateur IBM 1401, un ancien ''mainframe'' des années 60, utilisait un additionneur BCD un peu particulier. Les nombres étaient encodés en BCD dans la mémoire de l'ordinateur, mais les circuits de calcul utilisaient la représentation biquinaire. Lors d'un calcul, le processeur de l'ordinateur traduisait les chiffres BCD en représentation biquinaire, faisait une addition en biquinaire, avant de traduire le résultat en BCD normal.
Pour être précis, l'IBM 1401 utilisait une variante du biquinaire. L'encodage biquinaire de l'IBM 1401 est le suivant : la partie binaire disait si le chiffre était pair ou non, la partie quinaire encodait les valeurs 0, 2, 4, 6 et 8. Le chiffre se calculait en additionnant la partie binaire (0 ou 1) au nombre pair encodé par la partie quinaire. Si l'IBM 1401 utilisait cette variante du biquinaire, c'est car elle donnait des circuits de conversion BCD-biquinaire plus économes en portes logiques et plus rapides.
La partie binaire est le bit de poids faible du chiffre BCD, la partie biquinaire est calculée par un simple décodeur qui prend en entrée le chiffre BCD, amputé de son bit de poids faible. La traduction inverse demande d'utiliser un encodeur, à la place du décodeur. Par contre, le circuit d'addition biquinaire était plus compliqué du fait de la gestion des retenues. L'addition des parties binaires et quinaires se faisait en parallèle, dans deux additionneurs séparés. Cependant, l'addition des parties binaire fournit une retenue, qu'il faut prendre en compte. Pour cela, l'IBM 1401 disposait d'un troisième additionneur qui fournissait le résultat final, encodé en biquinaire.
[[File:Additionneur bi-quinaire de l'IBM 1401.png|centre|vignette|upright=2|Additionneur biquinaire de l'IBM 1401]]
Une implémentation moderne demanderait d'utiliser des portes ET combinées à des portes OU, le circuit pouvant être construit simplement à partir de sa table de vérité. Sur l'IBM 1401, le circuit était cependant différent, en raison de l'utilisation de OU câblés, des croisements de fils qui fonctionnent comme des portes OU, que nous n'avons pas encore vu pour le moment, mais qui seront détaillés dans quelques chapitres. Les OU câblés étaient utilisés pour simplifier le design du circuit, mais demandaient des portes logiques spécifiques, ce qui collait avec le fait que ce ''mainframe'' utilisait des transistors en Germanium. L'implémentation exacte est décrite dans cet article de blog, mais je ne recommande sa lecture qu'à ceux qui savent ce qu'est un OU câblé :
* [https://www.righto.com/2015/10/qui-binary-arithmetic-how-1960s-ibm.html Qui-binary arithmetic: how a 1960s IBM mainframe does math].
==Les débordements d'entier lors d'une addition/soustraction==
Les instructions arithmétiques manipulent des entiers codés sur un nombre fixe de bits, qui ne peuvent prendre leurs valeurs que dans un intervalle. Pour les nombres positifs, un ordinateur qui code ses entiers sur n bits pourra coder tous les entiers allant de 0 à <math>2^n -1</math>. Pour les nombres négatifs, l'intervalle est différent et dépend de la représentation utilisée. Dans le cas général, l'ordinateur peut coder les valeurs comprises de <math>N_\text{min}</math> à <math>N_\text{max}</math>. Si le résultat d'un calcul sort de cet intervalle, il ne peut pas être représenté par l'ordinateur et il se produit ce qu'on appelle un '''débordement d'entier'''.
La '''valeur haute de débordement''' désigne la première valeur qui est trop grande pour être représentée par l'ordinateur. Par exemple, pour un ordinateur qui peut coder tous les nombres entre 0 et 7, la valeur haute de débordement est égale à 8. On peut aussi définir la '''valeur basse de débordement''', qui est la première valeur trop petite pour être codée par l'ordinateur. Par exemple, pour un ordinateur qui peut coder tous les nombres entre 8 et 250, la valeur basse de débordement est égale à 7. Pour les nombres entiers, la valeur haute de débordement vaut <math>N_\text{max} + 1</math> , alors que la valeur basse vaut <math>N_\text{min} - 1</math> (avec <math>N_\text{max}</math> et <math>N_\text{min}</math> respectivement la plus grande et la plus petite valeur codable par l'ordinateur).
===La correction des débordements d'entier : l'arithmétique saturée===
Quand un débordement d'entier survient, tous les circuits de calcul ne procèdent pas de la même manière. Dans les grandes lignes, il y a deux réactions possibles : soit on corrige automatiquement le résultat du débordement, soit on ne fait rien et on se contente de détecter le débordement.
Si le débordement n'est pas corrigé automatiquement, le circuit ne conserve que les bits de poids faibles du résultat. Les bits en trop sont simplement ignorés. On dit qu'on utilise l''''arithmétique modulaire'''. Le problème avec ce genre d'arithmétique, c'est qu'une opération entre deux grands nombres peut donner un résultat très petit. Par exemple, si je dispose de registres 4 bits et que je souhaite faire l'addition 1111 + 0010 (ce qui donne 15 + 2), le résultat est censé être 10001 (17), ce qui est un résultat plus grand que la taille d'un registre. En conservant les 4 bits de poids faible, j’obtiens 0001 (1). En clair, un résultat très grand est transformé en un résultat très petit. Cela peut poser problèmes si on travaille uniquement avec des nombres positifs, mais c'est aussi utilisé pour coder des nombres en complément à deux.
D'autres circuits utilisent ce qu'on appelle l''''arithmétique saturée''' : si un calcul génère un débordement, on arrondi le résultat au plus grand entier supporté par le circuit. Les circuits capables de calculer en arithmétique saturée sont un peu plus complexes, vu qu'il faut rajouter des circuits pour corriger le résultat en cas de débordement. Il suffit généralement de rajouter un ''circuit de saturation'', qui prend en entrée le résultat et le corrige en cas de débordement. Ce circuit de saturation met la valeur maximale en sortie si un débordement survient, mais se contente de recopier le résultat du calcul sur sa sortie s'il n'y a pas de débordement. Typiquement, il est composé d'une couche de multiplexeurs, qui sélectionnent quelle valeur mettre sur la sortie : soit le résultat du calcul, soit le plus grand nombre entier géré par le processeur, soit le plus petit (pour les nombres négatifs/soustractions).
L'arithmétique saturée est utilisée pour les additions et soustractions, mais c'est plus rare pour les multiplications/divisions. Une des raisons est que le résultat d'une addition/soustraction prend un bit de plus que le résultat, là où les multiplications doublent le nombre de bits. Quand une addition déborde, le résultat réel est proche de la valeur maximale codable. mais quand une multiplication déborde, le résultat peut parfois valoir 200 à 60000 fois plus que la valeur maximale codable. Les calculs avec une valeur saturée/corrigée sont donc crédibles pour une suite d'additions, mais pas pour une suite de multiplications.
===La détection des débordements entiers===
Quand un débordement d'entier a eu lieu, il vaut mieux que l'additionneur prévienne ! Pour cela, l'additionneur a une '''sortie de débordement''', parfois nommée ''Overflow'', dont la valeur indique si l'addition a généré un débordement d'entier ou non. Reste que détecter un débordement ne se fait pas de la même manière selon que l'on parle d'un additionneur non-signé ou signé.
Pour les additionneur non-signés, l'additionneur calcule un bit de plus que ce qui est supporté par l'ordinateur. Par exemple, un additionneur 32 bits fournit un résultat sur 33 bits, un débordement d'entier a lieu quand le 33ème bit est à 1. Précisément, la sortie de débordement n'est autre que la retenue finale, celle fournie par le dernier additionneur complet. Le seul type de débordement possible est un débordement par le haut, où le résultat dépasse la valeur maximale. Avec l'arithmétique saturée, le circuit de saturation consiste en une seule couche de multiplexeurs, voire en un circuit de mise à la valeur maximale tel que vu dans le chapitre sur les opérations bits à bits.
[[File:Gestion des débordements d'entiers.png|centre|vignette|upright=2.5|Gestion des débordements d'entiers lors d'une addition non-signée.]]
Pour les additionneurs non-signés, la gestion des débordements d'entiers dépend fortement de la représentation signée. Nous allons étudier le cas du complément à deux. Si vous vous rappelez le chapitre 1, les calculs sur des nombres en complètement à deux utilisent les règles de l'arithmétique modulaire, c'est une condition nécessaire. À priori, on peut penser que dans ces conditions, les débordements d'entiers sont une chose parfaitement normale, qui nous permet d'avoir des résultats corrects. Néanmoins, certains débordements d'entiers peuvent survenir malgré tout et produire des bugs assez ennuyeux.
Si l'on tient en compte les règles du complément à deux, on sait que le bit de poids fort (le plus à gauche) permet de déterminer si le nombre est positif ou négatif : il indique le signe du nombre. Tout se passe comme si les entiers en complément à deux étaient codés sur un bit de moins, et avaient leur longueur amputé du bit de poids fort. Si le résultat d'un calcul écrase le bit de poids fort, il y a un débordement d'entiers. Il existe une règle simple qui permet de détecter ces débordements d'entiers. L'addition de deux nombres positifs ne peut pas être un nombre négatif. Si on additionne deux nombres dont le bit de signe est à 0 et que le bit de signe du résultat est à 1, on est en face d'un débordement d'entiers. Même chose pour deux nombres négatifs : le résultat de l'addition ne peut pas être positif. On peut résumer cela en une phrase : si deux nombres de même signe sont ajoutés, un débordement a lieu quand le bit du signe du résultat a le signe opposé.
Modifier les circuits d'au-dessus pour qu'ils détectent les débordements en complément à deux est simple comme bonjour : il suffit créer un petit circuit combinatoire qui prenne en entrée les bits de signe des opérandes et du résultat, et qui fasse le calcul de l'indicateur de débordements. Si l'on rédige sa table de vérité, on doit se retrouver avec la table suivante :
{|class="wikitable"
|-
!Entrées
!Sortie
|-
|000||0
|-
|001||1
|-
|010||0
|-
|011||0
|-
|100||0
|-
|101||0
|-
|110||1
|-
|111||0
|}
L'équation de ce circuit est la suivante, avec <math>S_a</math> et <math>S_b</math> les signes des deux opérandes, et <math>C_i</math> la retenue de la colonne précédente :
: <math> ( S_a . S_b . \overline{C_i} ) + ( \overline{S_a} . \overline{S_b} . C_i )</math>
En simplifiant, on obtient alors :
: <math>( S_a . S_b ) \oplus C_i</math>
Or, il se trouve que <math>S_a . S_b</math> est tout simplement la retenue en sortie du dernier additionneur, que nous noterons <math>C_o</math>. On trouve donc :
: <math>C_o \oplus C_i</math>
Il suffit donc de faire un XOR entre la dernière retenue et la précédente pour obtenir le bit de débordement.
<noinclude>
{{NavChapitre | book=Fonctionnement d'un ordinateur
| prev=Les circuits de décalage et de rotation
| prevText=Les circuits de décalage et de rotation
| next=Les circuits de comparaison
| nextText=Les circuits de comparaison
}}
</noinclude>
nj0s7hogow6v645wiaent25mdg1nw8m
768779
768778
2026-06-27T13:38:04Z
Mewtow
31375
/* Le soustracteur à propagation de retenue */
768779
wikitext
text/x-wiki
Dans ce chapitre, nous allons voir les circuits capables de faire une addition ou une soustraction, ainsi que quelques circuits spécialisés. Précisons cependant que les fabricants de processeurs travaillent d'arrache-pied pour trouver des moyens de rendre ces circuits de calcul plus rapides et plus économes en énergie. Autant vous dire que les circuits que vous allez voir sont vraiment des circuits qui font pâle figure comparé à ce que l'on peut trouver dans un vrai processeur commercial !
==Les circuits pour additionner 2 ou 3 bits==
L'addition se fait en binaire de la même manière qu'en décimal. On additionne les chiffres/bits colonne par colonne, une éventuelle retenue est propagée à la colonne d'à côté. La soustraction fonctionne sur le même principe, sur le même modèle qu'en décimal.
[[File:Binary Addition Demonstration.svg|centre|vignette|Exemple d'addition en binaire.]]
En clair, additionner deux nombres demande d'additionner 2 bits et une retenue sur chaque colonne, et de propager les retenues d'une colonne à l'autre. La propagation des retenues est quelque chose de simple en apparence, mais qui est sujet à des optimisations extraordinairement nombreuses. Aussi, pour simplifier l'exposition, nous allons voir comment gérer une colonne avant de voir comment sont propagées les retenues.
[[File:Full Adder Block.svg|vignette|upright=1|class=transparent|Additionneur complet.]]
Si on effectue une addition en colonne, on doit additionner les deux bits sur la colonne, mais aussi additionner une éventuelle retenue. Il faut donc créer un circuit qui additionne trois bits : deux bits de données, plus une retenue. Ce circuit qui additionne trois bits est appelé un '''additionneur complet'''. Il fournit en sortie deux bits : un bit de somme et une retenue sortante. Voici sa table de vérité :
{|class="wikitable"
|-
! Retenue entrante !! Opérande 1 !! Opérande 2 !! !! Retenue sortante !! Bit de somme
|-
||0||0||0|| ||0||0
|-
||0||0||1|| ||0||1
|-
||0||1||0|| ||0||1
|-
||0||1||1|| ||1||0
|-
||1||0||0|| ||0||1
|-
||1||0||1|| ||1||0
|-
||1||1||0|| ||1||0
|-
||1||1||1|| ||1||1
|}
Il est possible d'utiliser un tableau de Karnaugh pour traduire la table de vérité, mais elle donne un résultat assez compliqué. La retenue et le bit de somme sont calculés à part. La retenue est calculée avec trois portes ET et une porte OU. Le bit de somme est calculé lui avec 4 portes ET, une porte OU, et trois portes NON.
[[File:Full Adler with 3 entries.png|centre|vignette|upright=2|Additionneur complet, fabriqué à partir de la table de vérité.]]
Le circuit obtenu est pas loin d'être optimal, que ce soit en termes de rapidité que de portes logiques utilisées. Mais il est assez compliqué à comprendre, vu que c'est une traduction littérale de la table de vérité. D'autres implémentations alternatives donnent des résultats plus compréhensibles. Nous allons les voir dans la suite de cette section.
===L'additionneur complet basé sur des demi-additionneurs===
[[File:1-bit half-adder.svg|class=transparent|vignette|Demi-addtionneur.]]
Nous avons déjà vu comment additionner deux bits dans le chapitre sur les incrémenteurs, mais quelques rappels ne seront pas de trop. Le '''demi-additionneur''' est un circuit qui additionne deux bits. Il implémente la table d'addition, qui est très simple en binaire. Jugez plutôt :
* 0 + 0 = 0, retenue = 0 ;
* 0 + 1 = 1, retenue = 0 ;
* 1 + 0 = 1, retenue = 0 ;
* 1 + 1 = 0, retenue = 1.
Mais un demi-additionneur n'additionne que deux bits, il manque de quoi additionner la retenue. Une solution pour cela est d'enchaîner deux demi-additionneurs : un qui additionne les deux bits de données, et un second qui additionne la retenue au résultat du précédent. La retenue finale se calcule en combinant les sorties de retenue des deux demi-additionneurs, avec une porte OU. Le résultat est un additionneur complet fabriqué avec deux demi-additionneurs. Pour vous en convaincre, établissez la table de vérité de ce circuit, vous verrez que ça marche.
[[File:Full Adder Blocks.svg|centre|vignette|upright=2|Additionneur complet fabriqué avec deux demi-additionneurs.]]
[[File:Half-adder.svg|vignette|upright=1|Circuit d'un demi-additionneur naïf.]]
L'implémentation naïve d'un demi-additionneur utilise une porte XOR et une porte ET, comme illustré ci-contre. Le circuit s'obtient en faisant uen traduction littérale de la table de vérité du circuit. Si je dis qu'elle est naïve, c'est car il est possible de l'optimiser, de manière à éliminer des redondances cachées. Mais nous avions déjà vu cela dans le chapitre sur les incrémenteurs, aussi laissons cela pour plus tard.
En combinant deux demi-additionneurs de ce type, on obtient l'additionneur complet suivant. Les deux sorties de retenue des demi-additionneurs sont combinées avec une porte OU, pour calculer la retenue finale.
{| class="flexible"
|[[File:Full Adder Modules.svg|class=transparent|Composition d'un additionneur complet. On voit bien que celui-ci est composé de deux demi-additionneurs, en rouge et en bleu, auxquels on a ajouté une porte OU pour calculer la retenue finale.]]
|[[File:Full-adder.svg|300px|class=transparent|Circuit d'un additionneur complet.]]
|}
Le fait de combiner les deux retenues avec une porte OU n'est pas ce qu'il y a de plus intuitif. La table de vérité nous dit que ça fonctionne, mais on comprend mal pourquoi. Mais tout devient plus clair quand on sait que les deux retenues ne peuvent pas être à 1 en même temps. Même dans l'addition 1 + 1 + 1, seule une retenue est à 1. Soit l'addition des deux bits d'opérande donne naissance à une retenue, soit c'est l'addition ''résultat + retenue entrante''. La retenue sortant est donc à 1 quand une des deux addition donne une retenue. D'où l'usage d'une porte OU. Remarquez qu'une porte XOR donne le même résultat, vu que le cas où les deux retenues sont à 1 n'est jamais rencontré.
===La propagation et la génération des retenues===
L'additionneur complet que nous allons voir dans cette section sert à introduire les concepts de propagation, génération et absorption de retenue. Il s'agit de concepts très importants quand on étudie les additionneurs. l'idée part d'un principe très simple : la retenue sortante dépend de la retenue d'entrée. La relation entre les deux se résume à trois cas, qui dépendent de la valeur des deux bits additionnés, nommés A et B.
* Dans le premier cas, la retenue entrante est égale à la retenue sortante. On dit que la retenue entrante est propagée par l'additionneur.
* Dans le second cas, la retenue est forcée à 0 : la retenue sortante vaut 0, peu importe la valeur de la retenue entrante.
* Dans le troisième cas, la retenue est forcée à 1 : la retenue entrante vaut 1, peu importe la valeur de la retenue entrante.
[[File:Signaux P et G fournis par un demi-additionneur.png|vignette|Signaux P et G fournis par un demi-additionneur]]
Maintenant, créons un circuit qui nous dise si une retenue est propagée ou générée. Il a deux bits de sortie, nommés P et G : P pour ''Propagate'', G pour ''Generate''. Le bit P est à 1 si la retenue entrante est propagée, il est à 0 sinon. Le bit G est à 1 si une retenue est générée, à 0 sinon. Une retenue est considérée comme absorbée si elle n'est pas ni propagée ni générée, pas besoin d'un troisième bit pour gérer ce cas.
Il se trouve que ce circuit n'est autre qu'un demi-additionneur ! Pour vous en rendre compte, regardez la table de vérité d'un additionneur complet, illustrée ci-dessous. Lorsque les deux bits d'opérande sont à 0, la retenue sortante vaut toujours 0. Si ils sont tous deux à 1, alors la retenue sortante vaut 1. S'ils sont différents, alors retenues sortante et entrante sont égales. Le bit P est donc généré par une simple porte XOR. Quant au bit G, il est à 1 si les deux bits d'opérandes sont à 1, ce qui correspond à une porte ET. Il se trouve que ces deux portes forment un demi-additionneur !
{|class="wikitable"
|-
! Opérande 1 !! Opérande 2 !! Retenue entrante !! !! Retenue sortante
|- class="f_rouge"
||0||0||0|| ||0
|- class="f_rouge"
||0||0||1|| ||0
|- class="f_bleu"
||0||1||0|| ||0
|- class="f_bleu"
||0||1||1|| ||1
|- class="f_bleu"
||1||0||0|| ||0
|- class="f_bleu"
||1||0||1|| ||1
|- class="f_vert"
||1||1||0|| ||1
|- class="f_vert"
||1||1||1|| ||1
|}
Ensuite, créons un circuit qui prend ces deux sorties P et G, et calcule la retenue sortante en fonction. Le circuit en question a trois entrées : la retenue entrante, les deux bits P et G. La retenue sortante vaut 1 soit si une retenue est générée, soit si la retenue entrante est propagée et qu'elle vaut 1. En notant <math>{C_{in}}</math> et <math>{C_{out}}</math> les retenues entrantes et sortante, on a :
: <math>{C_{out}} = G + \left( P . C_{in} \right)</math>
Le circuit est donc composé d'une porte OU et d'une porte ET. En combinant un demi-additionneur avec le circuit de calcul de retenue sortante vu plus haut, on a :
[[File:Additionneur complet avec propagation et génération de retenue.png|class=transparent|centre|vignette|upright=2|Additionneur complet avec propagation et génération de retenue.]]
Certes, on retombe sur le circuit vu plus haut. Mais il est possible de remplacer le circuit en orange par un autre. Par exemple, il est possible de le remplacer par multiplexeur, qui choisit entre la retenue générée et la retenue entrante (propagée). Le choix se fait selon la valeur du bit P, qui chosiit entre propager la retenue et la générer.
[[File:Additionneur crée avec un multiplexeur.png|centre|vignette|upright=1.5|Additionneur crée avec un multiplexeur]]
Le circuit exact est illustré ci-dessous. Il semble utiliser plus de portes logiques que l'additionneur complet précédent. Cependant, nous verrons dans quelques chapitres qu'il est possible d'implémenter un multiplexeur avec seulement 6 transistors, voire moins ! L'implémentation utilise des portes à transmission, mais nous en reparlerons dans le chapitre sur les transistors, quand nous verrons les additionneurs à ''Manchester Carry Chain''. Au passage, une variante de ce circuit a été utilisée dans le processeur processeur 8086 d'Intel, comme on le verra dans le chapitre suivant.
[[File:Additionneur complet basé sur un MUX.png|centre|vignette|upright=1|Additionneur complet basé sur un MUX]]
[[File:FulladderPG.png|vignette|upright=1|Additionneur complet avec deux sorties P et G.]]
Pour finir, sachez qu'il existe des additionneurs qui fournissent : le bit de résultat, deux sorties P et G qui indiquent si l'addition propage ou génère une retenue. Il ne s'agit pas d'additionneurs complets, car il manque de quoi calculer la retenue sortante. De tels additionneurs seront appelés des '''additionneurs partiels'''. De tels additionneurs sont utilisés dans certains additionneurs pour gagner en performance.
Enfin, il existe aussi ce que j'ai décidé d'appeler des '''additionneurs P/G''', qui sont des additionneurs complets auxquels on a ajouté deux sorties P et G, en plus de la sortie de retenue. Leur circuit est le même que celui d'un additionneur complet, auquel on a ajouté deux fils. Vu que ces deux sorties sont fournies par le premier demi-additionneur, ajouter ces deux sorties demande d'ajouter des fils, pas de portes logiques. De tels additionneurs sont aussi utilisés dans certains additionneurs pour gagner en performance.
[[File:FAwithGP.svg|centre|vignette|upright=2|Additionneur complet avec sorties P et G : circuit.Ç]]
===L'additionneur complet basé sur une porte à majorité===
Maintenant, voyons une dernière implémentation possible de l'additionneur complet. Mais avant de voir comment l'additionneur est implémenté, nous allons devoir faire un petit focus sur la retenue sortante. Reprenons la table de vérité, et regardons ce qu'il en est pour la retenue sortante uniquement.
{|class="wikitable"
|-
! Retenue entrante !! Opérande 1 !! Opérande 2 !! !! Retenue sortante
|-
||0||0||0|| ||0
|-
||0||0||1|| ||0
|-
||0||1||0|| ||0
|-
||0||1||1|| ||1
|-
||1||0||0|| ||0
|-
||1||0||1|| ||1
|-
||1||1||0|| ||1
|-
||1||1||1|| ||1
|}
Vous remarquerez que la retenue sortante est égale au bit majoritaire, parmi les trois bits d'opérande. Si deux ou trois bits sont à 1, la retenue sortante vaut 1. Et réciproquement avec 0. Le circuit de calcul de la retenue peut donc être remplacé par une '''porte à majorité'''.
[[File:Additionneur crée avec une porte à majorité.png|centre|vignette|upright=2|Additionneur crée avec une porte à majorité]]
Mais une porte à majorité est assez gourmande en circuit, ce qui fait qu'un additionneur pareil ne serait pas pratique, ni utile. Du moins, ce serait le cas s'il n'y avait pas une possibilité d'optimisation extrêmement intéressante. Il est possible de calculer le 'bit de somme' à partir de la retenue sortante ! En effet, le bit de somme est l'inverse de la retenue sortante, sauf dans deux cas : les trois bits d'entrée sont à 0, où ils sont tous à 1. Le bit de somme vaut 0 dans le premier cas, 1 dans le second cas.
{|class="wikitable"
|-
! Retenue entrante !! Opérande 1 !! Opérande 2 !! !! Retenue sortante !! Bit de somme
|- class="f_bleu"
||0||0||0|| ||0||0
|- class="f_vert"
||0||0||1|| ||0||1
|- class="f_vert"
||0||1||0|| ||0||1
|- class="f_vert"
||0||1||1|| ||1||0
|- class="f_vert"
||1||0||0|| ||0||1
|- class="f_vert"
||1||0||1|| ||1||0
|- class="f_vert"
||1||1||0|| ||1||0
|- class="f_rouge"
||1||1||1|| ||1||1
|}
En clair, 6 lignes de la table de vérité sur 8 peuvent se calculer avec une porte à majorité, le reste demandant quelques portes logiques pour faire la correction. L'implémentation en circuit demande donc trois choses :
* de calculer la retenue sortante et de quoi l'inverser ;
* un circuit qui vérifie si tous les bits opérande valent 0 : une porte NOR ou OU fait l'affaire ;
* un circuit qui vérifie s'ils valent tous 1, à savoir une porte ET.
Ensuite, on combine le résultat des trois circuits précédents pour obtenir le résultat final. La combinaison est le fait du circuit en jaune dans le schéma ci-dessous.
[[File:Full adder basé sur une modification de la retenue.png|centre|vignette|upright=2|Full adder basé sur une modification de la retenue]]
Le circuit en jaune est assez simple. Quand la porte ET sort un 1, la sortie doit être forcée à 1. Par contre, quand la porte OU sort un zéro, la sortie doit être forcée à 0. Il suffit donc d'enchainer un circuit de mise à 1 et un circuit de mise à 0. Nous avons vu dans le chapitre sur les opérations de masquage qu'il s'agit respectivement d'une porte OU et d'une porte ET.
[[File:Full adder basé sur une modification de la retenue interne, au complet.png|centre|vignette|upright=2|Full adder basé sur une modification de la retenue interne, au complet]]
Divers processeurs utilisaient des additionneurs complets de ce type : le fameux Z80, l'Intel 4004 et de l'Intel 8008 sont les exemples les plus notables. Pourtant, on pourrait s'étonner qu'un tel circuit ait existé. Il utilise beaucoup de portes logiques, a une profondeur logique supérieure : il n'a rien d'avantageux. Sauf qu'il était utilisé sur d'anciens processeurs, qui utilisaient la technologie dite TTL, différente de la technologie CMOS des transistors modernes. Et avec la technologie TTL, il est possible de fusionner plusieurs portes logiques ET et NOR en une seule porte logique ET/OU/NON ! Un additionneur complet construit ainsi ne prenait que deux portes logiques : une pour le calcul de la retenue sortante, une autre pour le reste du circuit.
==L'addition non signée==
Voyons maintenant un circuit capable d'additionner deux nombres entiers: l''''additionneur'''. Dans la version qu'on va voir, ce circuit manipulera des nombres strictement positifs. L'addition des nombres codés en complètement à deux sera vu dans une section ultérieure.
[[File:Full-adder-4bits-serial-CEI.svg|vignette|upright=0.5|Additionneur 4 bits, un bloc.]]
L'interface d'un additionneur est illustrée ci-contre. Un additionneur prend deux opérandes sur deux entrées séparées, et fournit le résultat sur une sortie dédiée. De plus, il a une entrée sur laquelle envoyer une retenue entrante, et a une sortie pour la retenue sortante. La sortie de retenue indique que le résultat de l'addition a débordé (au sens d'un débordement d'entier). Pour l'entrée de retenue, son utilité deviendra plus claire dans ce qui suit, mais souvenez-vous qu'elle existe.
L'entrée et la sortie de retenue sont parfois utilisées pour combiner plusieurs petits additionneurs, pour former un additionneur plus grand. Nous verrons de nombreux exemples dans ce qui suit. Il est assez commun de créer des additionneurs pour des opérandes de 16/32/64 bits, avec des additionneurs de 4/5 bits.
===L'additionneur série===
Il est possible d'additionner deux nombres bit par bit, grâce à un additionneur complet associé à plusieurs registres à décalages. Les opérandes sont placées chacune dans un registre à décalage, et l'additionneur complet utilise les bits sortants de ces registres à décalage. Le bit du résultat est envoyé au registre à décalage pour le résultat. La retenue de l'addition est stockée dans une bascule de 1 bit, en attente du prochain cycle d'horloge. Un tel additionneur est appelé un '''additionneur série'''. Il a été utilisé sur d'anciens ordinateurs dans les années 50-60, aussi bien des prototypes que des ordinateurs commerciaux.
[[File:Additionneur série.jpg|centre|vignette|upright=2.5|Additionneur série.]]
===L'additionneur à propagation de retenue===
L''''additionneur à propagation de retenue''' est aussi appelé l'additionneur ''ripple carry'', c'est son nom anglais. J'utiliserais occasionnellement le nom anglais dans ce qui suit, car celui-ci est plus court. Ilutilise un additionneur complet pour chaque colonne de l'addition. Pour gérer les retenues, il suffit ainsi de câbler des additionneurs complets les uns à la suite des autres, chacun envoyant sa retenue à la colonne suivante.
[[File:Ripplecarryadder.png|centre|vignette|upright=2.5|Additionneur à propagation de retenue.]]
Notons la présence de la retenue sortante, qui est utilisée pour détecter les débordements d'entier, ainsi que pour d'autres opérations. Le bit de retenue final est souvent stocké dans un registre spécial du processeur (généralement appelé ''carry flag'').
Notez aussi, à droite du schéma précédent, l'entrée de retenue <math>\text{R}_\text{Entrée}</math>. Elle est très utile pour l'implémentation de certaines opérations comme l'inversion de signe, la soustraction, l'incrémentation, etc. Certains processeurs sont capables de faire une opération appelée ADC, ADDC ou autre nom signifiant ''Addition with Carry'', qui permet de faire le calcul A + B + Retenue (la retenue en question est la retenue sortante de l'addition précédente, stockée dans le registre ''carry flag''). Son utilité principale est de permettre des additions d'entiers plus grands que ceux supportés par le processeur. Par exemple, cela permet de faire des additions d'entiers 32 bits sur un processeur 16 bits.
[[File:Adder Network 3 Bit Sum Delay 003.svg|vignette|upright=0.5|Propagation de retenue dans l'additionneur.]]
Cet additionneur utilise très peu de portes logiques, ce qui fait qu'il était utilisé sur certains processeurs 8 et 16 bits, dont le budget en portes logiques était limité. Bien que très simple, cet additionneur est cependant peu performant. Son temps de calcul est proportionnel à la taille des opérandes. Par exemple, additionner deux opérandes de 32 bits prendra deux fois plus de temps que pour des opérandes de 16 bits. La raison est que les retenues doivent se propager à travers le circuit, du premier additionneur jusqu'au dernier.
L'addition étant une opération fréquente, d'autres additionneurs ont été inventés. Ils utilisent des optimisations qui utilisent plus de circuits pour gagner en rapidité. Mais avant de voir ces optimisations, nous allons voir s'il est possible d'optimiser les additionneurs à propagation de retenue.
===L'additionneur à saut de retenue===
L'additionneur à propagation de retenue peut être optimisé de deux manières. La première accélère la propagation de la retenue, en agissant au niveau des additionneurs complets. La ''Manchester carry chain'' est une optimisation de ce type, mais on ne peut pas encore expliquer à ce stade du cours, car elle optimisait les additionneurs complets au niveau des transistors eux-mêmes. Elle a été utilisée sur de nombreux processeurs connus, comme le 8086 d'Intel ou le Z80.
La seconde manière rend l'addition plus rapide dans certains cas. Le résultat est un additionneur dont le temps de calcul est variable. Le calcul prendra quelques cycles d'horloges avec certains opérandes, mais d'autres opérandes ne verront aucune amélioration. Il n'améliore pas le pire des cas, dans lequel la retenue doit être propagée du début à la fin, du bit de poids faible au bit de poids fort. Mais dans d'autres cas, il permet d'avoir le résultat en avance. L'optimisation en question donne l''''additionneur à saut de retenue''' (''carry-skip adder'').
Il est composé en enchainant plusieurs additionneurs plus petits, qui additionnent 4/5 bits, rarement plus. De tels additionneurs de petite taille sont appelés des '''blocs''', dans ce qui suit. L'idée est de détecter précocement si la retenue entrante est propagée à travers tout le bloc. Si la retenue est propagée dans tous le bloc, on peut directement l'envoyer sur la sortie de retenue, la retenue saute le bloc entier. Dans le cas contraire, on doit calculer la retenue normalement. Le choix entre les deux est le fait d'un multiplexeur. Toute la difficulté est de savoir comment commander le multiplexeur.
[[File:Carry skip adder, principe de base.png|centre|vignette|upright=2.5|Carry skip adder : principe de base]]
Pour savoir si une retenue est propagée dans un bloc entier, il faut utiliser des additionneurs complets, qui ont en plus une sortie P indiquant que l'additionneur propage la retenue. Le bloc propage une retenue si chaque additionneur complet propage la retenue, donc s'ils ont tous leur sortie P à 1. Un simple ET logique suffit à déterminer si c'est le cas. Le signal de commande du multiplexeur est donc un vulgaire ET entre toutes les sorties P des additionneurs complets du bloc.
[[File:Additionneur à saut de retenue.png|centre|vignette|upright=2.5|Calcul de la commande du MUX.]]
Voici ce que ça donne pour un bloc de 4 bits :
[[File:CSAdder4Bit.svg|centre|vignette|upright=1.5|Additionneur ''carry skip'' : bloc de 4 bits.]]
L'additionneur à saut de retenue est construit en assemblant plusieurs blocs de ce type.
[[File:BCSAdder16Bit.svg|centre|vignette|upright=3|Additionneur à saut de retenue.]]
Les blocs sont tous identiques dans le cas le plus simple, mais il est possible d'utiliser des blocs de taille variable. Par exemple, le premier bloc peut avoir des opérandes de 6 bits, le second des opérandes de 7 bits, etc. Faire ainsi permet de gagner un petit peu en performances, si la taille de chaque bloc est bien choisie. La raison est une question de temps de propagation des retenues. La retenue met plus de temps à se propager à travers 8 blocs qu'à travers 4, ce qui prend plus de temps qu'à travers 2 blocs, etc. En tenir compte fait que la taille des blocs tend à augmenter ou diminuer quand on se rapproche des bits de poids fort.
===L'additionneur à sélection de retenue===
L''''additionneur à sélection de retenue''' utilise aussi des blocs, comme les additionneurs précédents. L'addition se fait en deux versions : une avec la retenue du bloc précédent valant zéro, et une autre version avec la retenue du bloc précédent valant 1. Il suffira alors de choisir le bon résultat avec un multiplexeur, une fois cette retenue connue. On gagne ainsi du temps en calculant à l'avance les valeurs de certains bits du résultat, sans connaître la valeur de la retenue. Petit détail : sur certains additionneurs à sélection de retenue, les blocs de base n'ont pas la même taille. Cela permet de tenir compte des temps de propagation des retenues entre les blocs.
[[File:Additionneur à sélection de retenue.png|centre|vignette|upright=1.5|Additionneur à sélection de retenue avec seulement deux blocs.]]
Dans les exemples du dessus, chaque sous-additionneur étaient des additionneurs à propagation de retenue. Mais ce n'est pas une obligation, et tout autre type d’additionneur peut être utilisé. Par exemple, on peut faire en sorte que les sous-additionneurs soient eux-mêmes des additionneurs à sélection de retenue, et poursuivre ainsi de suite, récursivement. On obtient alors un '''additionneur à somme conditionnelle''', plus rapide que l'additionneur à sélection de retenue, mais qui utilise beaucoup plus de portes logiques.
===Les additionneurs à anticipation de retenue===
Les '''additionneurs à anticipation de retenue''' calculent chaque retenue sans avoir à propager les retenues précédentes. Au lieu de calculer les retenues une par une, ils calculent toutes les retenues en parallèle, à partir des bits des opérandes. Une fois les retenues pré-calculées, il suffit de les additionner avec les deux bits adéquats, pour obtenir le résultat. Ces additionneurs sont composés de deux parties :
* un circuit qui pré-calcule les retenues, sans les propager, directement à partir des opérandes ;
* d'une couche d'additionneurs complets, qui additionnent chacun deux bits d'opérande et la retenue pré-calculée.
[[File:Additionneur à anticipation de retenue.png|centre|vignette|upright=1.5|Additionneur à anticipation de retenue.]]
Le calcul des retenues se fait en deux étapes. La première détermine si les retenues sont propagées ou générées, pour chaque colonne. Elle détermine, pour chaque colonne, les bits P et G qui indiquent respectivement : qu'une retenue a été générée sur cette colonne, que la colonne propage la retenue précédente. La seconde étape utilise ces signaux P et G pour déterminer la retenue sur chaque colonne. Le circuit de calcul des retenues est donc composé de deux parties : une couche de demi-additionneurs pour générer les signaux P et G, un circuit de calcul des retenues proprement dit.
[[File:Circuit complet d'un additionneur à anticipation de retenue.png|centre|vignette|upright=1.5|Circuit complet d'un additionneur à anticipation de retenue.]]
Pour rappel, la retenue de la colonne i, notée <math>C_i</math> est égale à :
: <math>G_i + (P_i . C_{i-1})</math>, avec G le signal de génération de retenue, P le signal de propagation de retenue.
Si on utilisait cette formule sans trop réfléchir, on retomberait sur un additionneur à propagation de retenue inutilement compliqué. L'astuce des additionneurs à anticipation de retenue consiste à remplacer le terme Ci−1 par sa valeur calculée avant. Par exemple, je prends un additionneur 4 bits. Je dispose de deux nombres A et B, contenant chacun 4 bits : A3, A2, A1, et A0 pour le nombre A, et B3, B2, B1, et B0 pour le nombre B. Si j'effectue les remplacements, j'obtiens les formules suivantes :
* C1 = G0 + ( P0 · C0 ) ;
* C2 = G1 + ( P1 · G0 ) + ( P1 · P0 · C0 ) ;
* C3 = G2 + ( P2 · G1 ) + ( P2 · P1 · G0 ) + ( P2 · P1 · P0 · C0 ) ;
* C4 = G3 + ( P3 · G2 ) + ( P3 · P2 · G1 ) + ( P3 · P2 · P1 · G0 ) + ( P3 · P2 · P1 · P0 · C0 ).
Ces formules nous permettent de déduire la valeur d'une retenue directement : il reste alors à créer un circuit qui implémente ces formules, et le tour est joué.
[[File:Four bit adder with carry lookahead.svg|centre|vignette|upright=1.5|Additionneur à anticipation de retenue de 4 bits.]]
Plus haut, j'ai dit que le circuit était composé de trois couches : une couche de demi-additionneur pour les signaux P et G ; un circuit d'anticipation de retenue, puis l'addition finale. Mais les deux bits d'opérandes sont déjà additionnés dans la couche de demi-additionneur, autant réutiliser le résultat pour l'additionner à la retenue. Pour éliminer toute redondance de ce type, il est possible d'utiliser des additionneurs partiels (des additionneurs où la sortie de retenue a été remplacée par deux sorties P et G). L'additionneur final est alors composé de plusieurs additionneurs complets de ce type, couplé à un circuit d'anticipation de retenue.
[[File:Cla4bits.png|centre|vignette|upright=2.5|Additionneur à anticipation de retenue de 4 bits.]]
Les additionneurs à anticipation de retenue sont plus rapides que les additionneurs à propagation de retenue. La raison est que les retenues sont calculées rapidement, dans un temps plus petit qu'en propageant la retenue. Pour rentrer dans les détails mathématiques, le temps de calcul n'est pas proportionnel au nombre de bits, mais proportionnel au logarithme de ce nombre de bits. Par contre, le gain en temps de calcul s'accompagne d'un cout en portes logiques conséquent. Le circuit qui calcule les retenues anticipées utilise beaucoup de portes logiques.
===Les additionneurs à calcul parallèle de préfixe===
Du fait du cout en portes logiques, utiliser un additionneur à anticipation de retenue est une solution pratique pour additionner 4 ou 8 bits. Mais pour des opérandes plus grands, de 16 ou 32 bits, cela utiliserait trop de portes logiques. Une partie de ce cout en portes logiques est cependant lié à des redondances. Si on analyse deux retenues différentes, à des colonnes différentes. Les circuits qui calculent ces deux retenues seront différents, mais ils contiendront des sous-circuits identiques. Pour vous en rendre compte, reprenons les quatre équations obtenues précédemment et essayez de trouver des redondances :
* C1 = G0 + ( P0 · C0 ) ;
* C2 = G1 + ( P1 · G0 ) + ( P1 · P0 · C0 ) ;
* C3 = G2 + ( P2 · G1 ) + ( P2 · P1 · G0 ) + ( P2 · P1 · P0 · C0 ) ;
* C4 = G3 + ( P3 · G2 ) + ( P3 · P2 · G1 ) + ( P3 · P2 · P1 · G0 ) + ( P3 · P2 · P1 · P0 · C0 ).
Vous devriez trouver :
* le terme ''P0 · C0'' dans toutes les lignes ;
* les termes ''P1 · P0 · C0'' et ''P1 · P0'' dans les trois dernières lignes ;
* le terme ''P2 · P1 · P0 · C0'' ; ''P2 · P1 · G0'' et ''P2 · G1'' dans les deux dernières lignes.
Il existe plusieurs manières d'exploiter de telles redondances, exploitées dans les '''additionneurs à calcul parallèle de préfixe'''. L'optimisation apportée est de générer des signaux ''propagate'' et ''generate'' pour des groupes de 2, 3, 4, ..., N colonnes. Par exemple, il est possible de générer un signal ''P 0 vers 3'', qui indique si la retenue de la première colonne est propagée jusqu'à la 4ème colonne. Un autre exemple est un signal qui indique qu'une retenue a été générée entre les colonnes 4 à 7 génèrent une retenue ou non. En clair, les signaux P et G ont maintenant un intervalle, qui précise de quelle colonne vers quelle colonne se fait la propagation, ou entre quelles colonnes se fait la génération.
Il est possible de combiner les signaux P et G de deux groupes de colonne, s'ils sont contiguës (ils peuvent aussi se recouvrir). Par exemple, il est possible de calculer les bits P et G pour les colonnes 0 à 10, à partir des deux signaux P/G des colonnes 0-4 et 5-10. Pour cela, les équations sont assez simples. Si je prends deux groupes nommés A et B, avec A pour les colonnes de poids fort et B celles de poids faible, on a :
: <math>P_{sortie} = P_A . P_B</math>
: <math>G_{sortie} = G_A . P_A . G_B</math>
Pour les bits des colonnes 2, 4, 8, 16, 32, 64, et autres, le calcul est simple. L'idée est de grouper les colonnes par groupes de 2. Puis, on calcule chaque groupe de 4 bits à partir de deux groupes de 2. Puis les groupes de 8 bits sont calculés à partir de deux groupes de 4 bits, et ainsi de suite pour toute puissance de deux.
[[File:Squelette d'un additionneur à calcul parallèle de préfixe.jpg|centre|vignette|upright=2|Squelette d'un additionneur à calcul parallèle de préfixe]]
Les seules difficultés surviennent pour les colonnes dont le nombre n'est pas une puissance de deux. Il y a plusieurs manières pour subdiviser ces colonnes en intervalles plus petits et combiner le tout. Et elles donnent chacune des additionneurs différents. Ils portent les noms d''''additionneur de Ladner-Fisher''', d''''additionneur de Brent-Kung''', d''''additionneur de Kogge-Stone''', etc. Dans le cas général, l'additionneur de Kogge-Stone est un des plus rapide, mais aussi un des plus gourmand en circuits, alors que c'est l'inverse pour l'additionneur de Brent-Kung. Les deux additionneurs sont illustrés ci-dessous, pour des opérandes de 8 bits. Dans ceux-ci, chaque losange calculent les bits P et G, à partir des signaux P et G de deux groupes précédents.
{|
|[[File:Brent-kung-8-bit.png|vignette|Brent-kung-8-bit]]
|[[File:Kogge-stone-8-bit.png|vignette|upright=1.2|Kogge-stone-8-bit]]
|}
===Le calcul parallèle de la retenue===
L'anticipation de retenue est très rapide, mais utilise beaucoup de circuits, même en utilisant le calcul parallèle de préfixe. Les additionneurs à propagation de retenue font eux le compromis inverse. Mais il existe des intermédiaires, qui visent à obtenir un compromis entre performance et cout en portes logiques.
Un de ces compromis découpe un additionneur de 16/32/64 bits en additionneurs de 4/5 bits, qui sont enchainés en utilisant la propagation de retenue. Par contre, ces petits additionneurs de 4/5 bits utilisent l'anticipation de retenue. Ces additionneurs simples seront nommés ''blocs'' dans ce qui suit. Par exemple, on peut utiliser 4 additionneurs à anticipation de retenue de 4 bits, et propager les retenues entre eux. Le premier bloc calcule une retenue, qui est propagée au second bloc. Le second bloc calcule lui aussi une retenue, qui est propagée au troisième bloc, etc.
[[File:4008 Functional Diagram.svg|vignette|upright=1|4008 Functional Diagram]]
Le gain en performance est significatif, mais il est possible de faire presque aussi rapide, tout en économisant beaucoup de circuits. L'idée est de simplifier les additionneurs 4 bits, afin de ne pas utiliser une anticipation de retenue complète. L'anticipation de retenue n'est utilisée que pour la retenue sortante, alors que les bits de somme sont calculées avec propagation de retenue. La retenue sortante est donc calculée en parallèle de l'addition. Chaque bloc contient, à côté d'un additionneur proprement dit, un circuit qui calcule la retenue sortante. Il existe de nombreuses manières de calculer la retenue sortante.
Le gain en portes logiques est assez significatif. En effet, on se passe de circuits d'anticipation de retenue pour les 4 bits de somme, on ne garde que celui pour la retenue sortante. Le fait d'utiliser la propagation de retenue pour calculer les 4 bits de somme est censé avoir un léger cout en performance, mais il se trouve que ce cout est très faible. En effet, le résultat n'est connu qu'une fois les retenues propagées jusqu'au dernier bloc. Et malgré l'anticipation des retenues, la propagation est assez lente. les additionneurs à propagation de retenue auront terminé leur travail bien avant.
Un exemple est celui de l'additionneur CMOS 4008, un additionneur de 4 bit. Il est composé en trois sections. Une première couche de demi-additionneurs calcule les signaux P et G utilisés à la fois pour l'anticipation de la retenue sortante, que pour calculer les bits de somme. Le circuit de calcul de la retenue sortante utilise l'anticipation de retenue. Le reste du circuit fait l'addition en propageant les retenues.
[[File:CMOS 4008, circuit découpé en sections.png|centre|vignette|upright=1.5|CMOS 4008, circuit découpé en sections]]
===Les additionneurs à anticipation de retenue multi-niveau===
Les additionneurs précédents mixent anticipation et propagation de retenue. Ceux que nous allons voir utilisent des blocs, mais utilisent l'anticipation de retenue entre les blocs. Les blocs utilisés peuvent être un additionneur à propagation de retenue, ou à anticipation de retenue, peu importe. Ce qui compte est que la retenue entrante de chaque bloc est calculée par anticipation de retenue.
Par exemple, il est possible de créer un additionneur 16 bits en utilisant 4 additionneurs/blocs de 4 bits, couplé à un circuit d’anticipation de retenue par bloc. Pour cela, les additionneurs de 4 bits doivent fournir deux sorties P et G. Les deux sorties indiquent que le bloc soit a généré une retenue, soit a propagé la retenue entrante. Par propagé, on veut dire que la retenue entrante du bloc s'est retrouvée sur la sortie de retenue du bloc. C'est le cas si les 4 colonnes propagent la retenue, ce qui signifie que :
: <math>P_\text{4 bits} = P_0 \cdot P_1 \cdot P_2 \cdot P_3</math>
Pour la génération, cela veut dire qu'une colonne a générée une retenue, qui a été propagée jusqu'à la sortie. En clair, pour chaque colonne, il faut que le G = 1, et que les signaux P des colonnes soient à 1. Pour les 4 colonnes, cela donne :
: <math>G_\text{4 bits} = G_3 + G_2 \cdot P_3 + G_1 \cdot P_3 \cdot P_2 + G_0 \cdot P_3 \cdot P_2 \cdot P_1</math>
[[File:Cla4bitsPG.png|centre|vignette|upright=2|Additionneur à anticipation de retenue de 4 bits.]]
Pour combiner 4 blocs de 4 bits, on devra donc combiner 4 bits P et G, avec un circuit d'anticipation de retenue.
[[File:16-bit lookahead carry unit.svg|centre|vignette|upright=2|Additionneur à anticipation de retenue de 16 bits.]]
Il est même possible d'aller plus loin et de combiner 4 additionneurs précédents pour obtenir un additionneur 64 bits.
[[File:64-bit lookahead carry unit.svg|centre|vignette|upright=2|Additionneur à anticipation de retenue de 64 bits.]]
Le résultat est un additionneur à plusieurs niveaux d’anticipation de retenue. Une anticipation de retenue dans un blocs de 4 bits (facultative), entre blocs de 4 bits, entre blocs de 16 bits. La même logique peut être utilisée avec des blocs de taille différente de 4, 16 et 64 bits.
==L'addition signée et la soustraction==
Après avoir vu l'addition, il est logique de passer à la soustraction, les deux opérations étant très proches. Si on sait câbler une addition entre entiers positifs, câbler une soustraction n'est pas très compliqué. De plus, la soustraction permet de faire des additions de nombres signés.
===Le soustracteur à propagation de retenue===
Pour soustraire deux nombres entiers, on peut adapter l'algorithme de soustraction utilisé en décimal, celui que vous avez appris à l'école. Celui-ci ressemble fortement à l'algorithme d'addition : on soustrait les bits de même poids, et on propage éventuellement une retenue sur la colonne suivante. À la différence de l'addition, la retenue est soustraite, et non ajoutée. En clair, une soustraction en binaire demande de faire deux soustraction : pour chaque bit de la première opérande, on soustrait le bit associé de la seconde, puis on soustrait la retenue entrante.
[[File:Algebra1 05 fig014.svg|centre|vignette|Soustraction en binaire, avec les retenues en rouge.]]
[[File:Half Subtractor Vektor.svg|vignette|Demi-soustracteur.]]
Dans le chapitre sur les incrémenteurs/décrémenteurs, nous avons vu un circuit qui soustrait deux bits, appelé le '''demi-soustracteur'''. Il ressemble beaucoup à un demi-additionneur, les différences se résumant à une porte NON ajoutée pour le calcul de la retenue. Il implémente la table de soustraction, qui est très simple en binaire :
* 0 - 0 = 0 ;
* 0 - 1 = 1 et une retenue ;
* 1 - 0 = 1 ;
* 1 - 1 = 0.
Comme pour les additionneurs, deux demi-soustracteurs peuvent être combinés pour donner un '''soustracteur complet'''. Le calcul de la retenue se fait en combinant les deux retenues des demi-soustracteurs avec une porte OU. Là encore, si on regarde les sorties des deux demi-soustracteurs, il est impossible d'avoir les deux sorties de retenues à 1 en même temps.
[[File:FullSubtractor.svg|centre|vignette|upright=2|Soustracteur complet.]]
[[File:Sub Network 3 Bit Step 002.svg|vignette|upright=1|Soustracteur à propagation de retenue pour opérandes de 3 bits.]]
Les soustracteurs complets sont utilisés pour créer des soustracteurs à propagation de retenue, sur le même modèle que les additionneurs. Et je précise bien "à propagation de retenue", car l'anticipation de retenue et les autres optimisations ne marchent pas exactement pareil entre addition et soustraction. Les signaux de propagation et de génération de retenue ne sont pas générés pareil, idem pour la manière de les combiner.
Il est possible de fusionner un additionneur à propagation de retenue et un soustracteur de même type. Pour cela, il suffit de remarquer que les demi-soustracteurs et les demi-additionneurs se ressemblent beaucoup. La seule différence entre les deux est une vulgaire porte NON. En remplaçant cette porte NON par un inverseur commandable, on obtient un circuit qui sert soit de demi-additionneur, soit de demi-soustracteur, suivant ce qu'on envoie sur l'entrée de commande. Ci-dessous est illustré ce que donne ce remplacement pour un soustracteur complet. Le résultat est un '''additionneur/soustracteur à propagation de retenue'''.
[[File:Additionneur-soustracteur complet.png|centre|vignette|upright=2|Additionneur-soustracteur complet, naïf.]]
Le circuit précédent peut cependant se simplifier drastiquement.
[[File:Full sub.svg|centre|vignette|upright=2|Additionneur-soustracteur complet, simplifié.]]
Il est donc possible de fusionner un additionneur avec un soustracteur, ce qui donne un '''additionneur/soustracteur'''. Mais comme je l'ai dit plus haut, cela ne marche que si l'''additionneur-/soustracteur'' utilise la propagation de retenue. Dès qu'on souhaite utiliser l'anticipation de retenue ou tout autre technique, les choses sont plus compliquées. Le calcul des retenues étant différent entre addition et soustraction, mutualiser les circuits d'anticipation de retenue pour les deux opérations ne donnera pas un résultat très propre. Ce qui n'est pas le cas avec les solutions que nous allons voir dans ce qui suit.
===L'additionneur-soustracteur pour opérandes codées en complément à deux===
Étudions le cas de la soustraction en complément à deux. Vous savez sûrement que a−b et a+(−b) sont deux expressions équivalentes. Et en complément à deux, − b = not(b) + 1. Dit autrement, a − b = a + not(b) + 1. On pourrait se dire qu'il faut deux additionneurs pour faire le calcul, mais la majorité des additionneurs possède une entrée de retenue pour incrémenter le résultat de l'addition. Un soustracteur en complément à deux est donc simplement composé d'un additionneur et d'un inverseur.
[[File:Additionneur soustracteur.png|centre|vignette|upright=2|Soustracteur en complément à deux.]]
Il est possible de créer un circuit capable d'effectuer soit une addition, soit une soustraction : il suffit de remplacer l'inverseur par un inverseur commandable, qui peut être désactivé. On a vu comment créer un tel inverseur commandable dans le chapitre sur les circuits combinatoires. On peut remarquer que l'entrée de retenue et l'entrée de commande de l'inverseur sont activées en même temps : on peut fusionner les deux signaux en un seul.
[[File:Additionneur-soustracteur en complément à deux.png|centre|vignette|upright=2|Additionneur-soustracteur en complément à deux.]]
Une implémentation alternative est la suivante. Elle remplace l'inverseur commandable par un multiplexeur.
[[File:4-bit ripple carry adder-subtracter.svg|centre|vignette|upright=2|Additionneur-soustracteur en complément à deux, version alternative.]]
===L'additionneur-soustracteur pour opérandes codées en signe-magnitude===
Passons maintenant aux nombres codés en signe-valeur absolue, les deux opérandes étant notées A et B. Suivant les signes des deux opérandes, on a quatre cas possibles : A + B, A − B (B négatif), −A + B (A négatif) et −A − B (A et B négatifs). Une astuce est que le circuit n'a besoin que de calculer A + B et A − B : il peut les inverser pour obtenir − A − B ou B − A. A + B et A − B peuvent se calculer avec un additionneur-soustracteur, reste à corriger le résultat. Il suffit de lui ajouter un inverseur commandable pour obtenir le circuit d'addition finale.
[[File:Additionneur en signe-valeur absolue.png|centre|vignette|upright=2|Additionneur en signe-valeur absolue.]]
Toute la difficulté tient dans le calcul du bit de signe du résultat, quand interviennent des soustractions. Autant l'addition de deux nombres de même signe (A + B et −A − B) ne pose aucun problème, autant les soustractions posent problème (A − B et −A + B). Suivant que <math>A<B</math> ou que <math>A>B</math>, le signe du résultat ne sera pas le même. Déterminer le signe du résultat se fait en regardant les bits de débordement d'entier, comme on le verra plus bas.
===L'additionneur-soustracteur pour opérandes codées en représentation par excès===
Passons maintenant aux nombres codés en représentation par excès. On pourrait croire que ces nombres s'additionnent comme des nombres non-signés, mais ce serait oublier la présence du biais, qui pose problème. Dans les cas de nombres signés gérés avec un biais, voyons ce que donne l'addition de deux nombres :
: <math>( n_1 + biais ) + ( n_2 + biais ) = n_1 + n_2 + 2 \times biais</math>
Or, le résultat correct serait :
: <math>n_1 + n_2 + biais</math>
En effectuant l'addition telle quelle, le biais est compté deux fois. On doit donc le soustraire après l'addition pour obtenir le résultat correct.
Même chose pour la soustraction qui donne ceci :
: <math>(n_1 + biais) - (n_2 + biais) = n_1 - n_2</math>
Or, le résultat correct serait :
: <math> ( n_1 - n_2 ) + biais </math>
Il faut rajouter le biais pour obtenir l'exposant correct.
On a donc besoin de deux additionneurs/soustracteurs : un pour additionner/soustraire les représentations binaires des opérandes, et un autre pour ajouter/retirer le biais en trop/manquant.
==L'additionneur BCD==
Maintenant, voyons un additionneur qui additionne deux entiers au format BCD. Pour cela, nous allons devoir passer par deux étapes. La première est de créer un circuit capable d'additionneur deux chiffres BCD. Ensuite, nous allons voir comment enchaîner ces circuits pour créer un additionneur BCD complet.
===L'additionneur BCD qui fait l'opération chiffre par chiffre===
Nous allons commencer par voir un additionneur qui additionne deux chiffres en BCD, une sorte d'équivalent BCD de l'additionneur complet. Il fournit un résultat sur 4 bits et une retenue qui est mise à 1 si le résultat dépasse 10 (la limite d'un chiffre BCD). Les deux opérandes sont des chiffres BCD codés sur 4 bits et sont additionnés en binaire par un additionneur des plus normaux, similaire à ceux vus plus haut. Le résultat est alors un entier codé en binaire, sur 5 bits, qu'on corrige/convertit pour obtenir un chiffre BCD et une retenue sortante.
Pour corriger le résultat, une idée intuitive serait de prendre le résultat et de faire une division par 10. Le quotient donne la retenue, alors que le reste est le résultat, le chiffre BCD. Mais un circuit diviseur par 10 utilise beaucoup de portes logiques, ce qui ne vaut pas le coup. Une autre méthode détecte si le résultat est égal ou supérieur à 10, ce qui correspond à un "débordement" (on dépasse les limites d'un chiffre BCD). Si le résultat est plus petit que 10, il n'y a rien à faire : le résultat est bon et la retenue est de zéro. Par contre, si le résultat vaut 10 ou plus, il faut corriger le résultat et générer une retenue à 1.
Il faut donc ajouter un circuit qui détecte si le résultat est supérieur à 9, qui calcule directement la retenue. Ce circuit peut se fabriquer simplement à partir de sa table de vérité, ou en utilisant les techniques que nous verrons dans un chapitre ultérieur sur les comparateurs. La solution la plus simple est clairement d'utiliser la table de vérité, ce qui est très simple, assez pour être laissé en exercice au lecteur. Pour comprendre comment corriger le résultat, établissons une table de vérité qui associe le résultat et le résultat corrigé. L'entrée vaut au minimum 10 et au maximum 9 + 9 = 18. On considère la sortie comme un tout, la retenue étant un 5ème bit, le bit de poids fort.
{|class="wikitable"
|-
! colspan="5" | Entrée
!
! rowspan="10" |
! Retenue
! Résultat corrigé (sans retenue)
! interprétation de la sortie en binaire (retenue inclue)
|-
| 0 || 1 || 0 || 1 || 0
| (10)
| 1 || 0000
| (16)
|-
| 0 || 1 || 0 || 1 || 1
| (11)
| 1 || 0001
| (17)
|-
| 0 || 1 || 1 || 0 || 0
| (12)
| 1 || 0010
| (18)
|-
| 0 || 1 || 1 || 0 || 1
| (13)
| 1 || 0011
| (19)
|-
| 0 || 1 || 1 || 1 || 0
| (14)
| 1 || 0100
| (20)
|-
| 0 || 1 || 1 || 1 || 1
| (15)
| 1 || 0101
| (21)
|-
| 1 || 0 || 0 || 0 || 0
| (16)
| 1 || 0110
| (22)
|-
| 1 || 0 || 0 || 0 || 1
| (17)
| 1 || 0111
| (23)
|-
| 1 || 0 || 0 || 1 || 0
| (18)
| 1 || 1000
| (24)
|}
En analysant le tableau, on voit que pour corriger le résultat, il suffit d'ajouter 6. La raison est que le résultat déborde d'un nibble à 16 en binaire, mais à 10 en décimal : il suffit d'ajouter la différence entre les deux, à savoir 6, et le débordement binaire fait son travail. Donc, la correction après une addition est très simple : si le résultat dépasse 9, on ajoute 6.
On peut maintenant implémenter l'additionneur BCD, en combinant le comparateur avec 10, le circuit de correction, et l'additionneur. La première solution calcule deux versions du résultat : la version corrigée, la version normale. Le choix entre les deux est réalisée par un multiplexeur, commandé par le comparateur.
[[File:Additionneur BCD.png|centre|vignette|upright=1.5|Additionneur BCD]]
L'autre solution utilise un circuit commandable qui soit additionne 6, soit ne fait rien. Le choix entre les deux est commandé par le bit calculé par le comparateur.
[[File:Additionneur BCD, seconde version.png|centre|vignette|upright=1.5|Additionneur BCD, seconde version.]]
Une version alternative du circuit précédent est la suivante. Il contient deux additionneurs : un pour additionner les deux chiffres BCD, un autre pour additionner 6 si besoin. Le résultat du comparateur est directement utilisé pour générer l'opérande du second additionneur : 0 ou 6. Le circuit est simple à concevoir, mais gaspille beaucoup de circuit. Idéalement, il vaudrait mieux utiliser un circuit combinatoire d'addition avec une constante.
[[File:Adder 4 Bit BCD.svg|centre|vignette|upright=2|Additionneur BCD, circuit complet.]]
Pour obtenir un additionneur BCD complet, il suffit d’enchaîner les additionneurs précédents, comme on le ferait avec les additionneurs complets dans un additionneur à propagation de retenue. Au final, l'additionneur BCD est beaucoup plus compliqué qu'un additionneur normal, car il rajoute un comparateur ">9", un petit additionneur pour ajouter 6 et éventuellement d'autres circuits. De plus, il est difficile d'appliquer les optimisations disponibles sur les additionneurs non-BCD. Notamment, les circuits d'anticipation de retenue sont totalement à refaire et le résultat est relativement compliqué. C'est ce qui explique pourquoi le BCD a progressivement été abandonné au profit du binaire simple.
La soustraction en BCD se fait comme en binaire : le nombre à soustraire est remplacé par son complément, le circuit additionne le complément et l'autre opérande, le débordement d'entier fait que le résultat marche. Sauf qu'ici, le complément est un complément à 9. Il se calcule chiffre par chiffre : chaque chiffre est remplacé par (9 - le chiffre en question).
===L'additionneur BCD par ajustement décimal===
L'additionneur BCD précédent effectuait son travail chiffre BCD par chiffre BCD, mais il existe des additionneurs BCD qui font autrement. Sur les premiers processeurs x86, il n'y avait pas d'opération d'addition BCD proprement dit, seulement une addition binaire normale de 8, 16 ou 32 bits. Par contre, elle était secondée par une opération dite d''''ajustement décimal''' qui transformait un nombre binaire en nombre codé en BCD. L'opération d'ajustement décimal prenait un opérande de 8 bits codé en binaire et fournissait un résultat de la même taille, c'est à dire deux chiffres BCD. Effectuer une addition BCD demandait donc de faire deux opérations à la suite : une addition binaire simple, suivie par l'opération d'ajustement décimal. Cela permettait de gérer des nombres entiers en binaire usuel et des entiers BCD sans avoir deux instructions d'addition séparées pour les deux, sans compter que cela simplifiait aussi les circuits d'addition.
L'ajustement décimal s'effectue en ajoutant une constante bien précise à l'opérande à convertir en BCD. L'idée est que la constante est découpée en morceaux de 4 bits, correspondant chacun à un chiffre BCD de l'opérande, chaque morceau contenant soit un 0, soit 6. Cela permet d'ajouter soit 0, soit 6, à chaque chiffre BCD, et donc de le corriger. La propagation des retenues d'un chiffre à l'autre est effectuée automatiquement par l'addition binaire de la constante. L'opération d'ajustement décimal calcule automatiquement la constante. Elle découpe l'opérande en ''nibbles'', vérifie si chaque ''nibble'' est supérieur ou égal à 10, puis détermine la valeur de chaque ''nibble'' de la constante finale. Par exemple, si je prends l'opérande 1001 1110, le ''nibble'' de poids faible déborde, alors que celui de poids fort non. La constante sera donc 0000 0110 : 0x06. Inversement, si le ''nibble'' de poids fort déborde et pas celui de poids faible, la constante sera alors 0x60. Et la constante est de 0x66 si les deux ''nibbles'' débordent, de 0x00 si aucun ne déborde.
Le circuit d’ajustement décimal est donc composé de trois étapes : deux étapes pour calculer la constante, et un circuit d'addition pour additionner cette constante au nombre de départ. La première étape découpe l'opérande en morceaux de 4 bits, en chiffres BCD, et vérifie si chacun d'entre eux vaut 10 ou plus. La seconde étape prend les résultats de la première étape, et les combine pour calculer la constante. Enfin, on trouve l'addition finale, qui était réalisée par un circuit d'addition utilisé à la fois pour l'ajustement décimal et l'addition binaire. La différence entre une addition normale et une opération d'ajustement décimal tient dans le fait que les deux premières étapes sont désactivées dans une addition normale.
[[File:Additionneur BCD parallèle.png|centre|vignette|upright=1.5|Additionneur BCD parallèle]]
===L'additionneur biquinaire===
Les entiers BCD ne sont qu'un des encodages hybrides entre décimal et binaire. L'encodage biquinaire est l'un d'entre eux et nous allons faire un rappel rapide à ce sujet. Pour simplifier, un chiffre encodé en biquinaire est composé de deux parties : un bit, couplé à une partie quinaire encodée en représentation ''one-hot''. La partie quinaire encode un nombre allant de 0 à 4, ce qui prend 5 bits (0, 1, 2, 3 et 4). Le bit indique s'il faut ou non ajouter 5 à la valeur encodée par la partie quinaire. Ainsi, on peut coder tous les nombres de 0 à 9.
Additionner deux nombres de biquinaire demande donc d'additionner deux parties quinaires encodées en ''one-hot'' et d'additionner deux bits. Mais attention : il faut tenir compte de la retenue de l'addition des parties quinaires. Et idéalement, il faut aussi tenir compte d'une retenue entrante, provenant de l'addition de la colonne de chiffres précédente. Toute la difficulté vient de la création de l'additionneur ''one-hot''. Heureusement, vu qu'il n'y a que 4-5 bits à additionner, il est souvent fabriqué à partir de sa table de vérité.
[[File:Additionneur bi-quinaire.png|centre|vignette|upright=2|Additionneur bi-quinaire]]
Un avantage du biquinaire est que le calcul du complément à 9 est très simple. Il faut pour cela : inverser la partie binaire avec une porte NON, puis inverser l'ordre des bits de la partie quinaire. Concrètement, le bit de poids faible devient le bit de poids fort, et ainsi de suite. Par exemple, une partie quinaire 01000 devient 00010, 10000 devient 00001, 00100 ne change pas, etc. Le tout peut se calculer avec une porte NON et 5 multiplexeurs.
===L'additionneur BCD avec calculs intermédiaires en biquinaire===
L'ordinateur IBM 1401, un ancien ''mainframe'' des années 60, utilisait un additionneur BCD un peu particulier. Les nombres étaient encodés en BCD dans la mémoire de l'ordinateur, mais les circuits de calcul utilisaient la représentation biquinaire. Lors d'un calcul, le processeur de l'ordinateur traduisait les chiffres BCD en représentation biquinaire, faisait une addition en biquinaire, avant de traduire le résultat en BCD normal.
Pour être précis, l'IBM 1401 utilisait une variante du biquinaire. L'encodage biquinaire de l'IBM 1401 est le suivant : la partie binaire disait si le chiffre était pair ou non, la partie quinaire encodait les valeurs 0, 2, 4, 6 et 8. Le chiffre se calculait en additionnant la partie binaire (0 ou 1) au nombre pair encodé par la partie quinaire. Si l'IBM 1401 utilisait cette variante du biquinaire, c'est car elle donnait des circuits de conversion BCD-biquinaire plus économes en portes logiques et plus rapides.
La partie binaire est le bit de poids faible du chiffre BCD, la partie biquinaire est calculée par un simple décodeur qui prend en entrée le chiffre BCD, amputé de son bit de poids faible. La traduction inverse demande d'utiliser un encodeur, à la place du décodeur. Par contre, le circuit d'addition biquinaire était plus compliqué du fait de la gestion des retenues. L'addition des parties binaires et quinaires se faisait en parallèle, dans deux additionneurs séparés. Cependant, l'addition des parties binaire fournit une retenue, qu'il faut prendre en compte. Pour cela, l'IBM 1401 disposait d'un troisième additionneur qui fournissait le résultat final, encodé en biquinaire.
[[File:Additionneur bi-quinaire de l'IBM 1401.png|centre|vignette|upright=2|Additionneur biquinaire de l'IBM 1401]]
Une implémentation moderne demanderait d'utiliser des portes ET combinées à des portes OU, le circuit pouvant être construit simplement à partir de sa table de vérité. Sur l'IBM 1401, le circuit était cependant différent, en raison de l'utilisation de OU câblés, des croisements de fils qui fonctionnent comme des portes OU, que nous n'avons pas encore vu pour le moment, mais qui seront détaillés dans quelques chapitres. Les OU câblés étaient utilisés pour simplifier le design du circuit, mais demandaient des portes logiques spécifiques, ce qui collait avec le fait que ce ''mainframe'' utilisait des transistors en Germanium. L'implémentation exacte est décrite dans cet article de blog, mais je ne recommande sa lecture qu'à ceux qui savent ce qu'est un OU câblé :
* [https://www.righto.com/2015/10/qui-binary-arithmetic-how-1960s-ibm.html Qui-binary arithmetic: how a 1960s IBM mainframe does math].
==Les débordements d'entier lors d'une addition/soustraction==
Les instructions arithmétiques manipulent des entiers codés sur un nombre fixe de bits, qui ne peuvent prendre leurs valeurs que dans un intervalle. Pour les nombres positifs, un ordinateur qui code ses entiers sur n bits pourra coder tous les entiers allant de 0 à <math>2^n -1</math>. Pour les nombres négatifs, l'intervalle est différent et dépend de la représentation utilisée. Dans le cas général, l'ordinateur peut coder les valeurs comprises de <math>N_\text{min}</math> à <math>N_\text{max}</math>. Si le résultat d'un calcul sort de cet intervalle, il ne peut pas être représenté par l'ordinateur et il se produit ce qu'on appelle un '''débordement d'entier'''.
La '''valeur haute de débordement''' désigne la première valeur qui est trop grande pour être représentée par l'ordinateur. Par exemple, pour un ordinateur qui peut coder tous les nombres entre 0 et 7, la valeur haute de débordement est égale à 8. On peut aussi définir la '''valeur basse de débordement''', qui est la première valeur trop petite pour être codée par l'ordinateur. Par exemple, pour un ordinateur qui peut coder tous les nombres entre 8 et 250, la valeur basse de débordement est égale à 7. Pour les nombres entiers, la valeur haute de débordement vaut <math>N_\text{max} + 1</math> , alors que la valeur basse vaut <math>N_\text{min} - 1</math> (avec <math>N_\text{max}</math> et <math>N_\text{min}</math> respectivement la plus grande et la plus petite valeur codable par l'ordinateur).
===La correction des débordements d'entier : l'arithmétique saturée===
Quand un débordement d'entier survient, tous les circuits de calcul ne procèdent pas de la même manière. Dans les grandes lignes, il y a deux réactions possibles : soit on corrige automatiquement le résultat du débordement, soit on ne fait rien et on se contente de détecter le débordement.
Si le débordement n'est pas corrigé automatiquement, le circuit ne conserve que les bits de poids faibles du résultat. Les bits en trop sont simplement ignorés. On dit qu'on utilise l''''arithmétique modulaire'''. Le problème avec ce genre d'arithmétique, c'est qu'une opération entre deux grands nombres peut donner un résultat très petit. Par exemple, si je dispose de registres 4 bits et que je souhaite faire l'addition 1111 + 0010 (ce qui donne 15 + 2), le résultat est censé être 10001 (17), ce qui est un résultat plus grand que la taille d'un registre. En conservant les 4 bits de poids faible, j’obtiens 0001 (1). En clair, un résultat très grand est transformé en un résultat très petit. Cela peut poser problèmes si on travaille uniquement avec des nombres positifs, mais c'est aussi utilisé pour coder des nombres en complément à deux.
D'autres circuits utilisent ce qu'on appelle l''''arithmétique saturée''' : si un calcul génère un débordement, on arrondi le résultat au plus grand entier supporté par le circuit. Les circuits capables de calculer en arithmétique saturée sont un peu plus complexes, vu qu'il faut rajouter des circuits pour corriger le résultat en cas de débordement. Il suffit généralement de rajouter un ''circuit de saturation'', qui prend en entrée le résultat et le corrige en cas de débordement. Ce circuit de saturation met la valeur maximale en sortie si un débordement survient, mais se contente de recopier le résultat du calcul sur sa sortie s'il n'y a pas de débordement. Typiquement, il est composé d'une couche de multiplexeurs, qui sélectionnent quelle valeur mettre sur la sortie : soit le résultat du calcul, soit le plus grand nombre entier géré par le processeur, soit le plus petit (pour les nombres négatifs/soustractions).
L'arithmétique saturée est utilisée pour les additions et soustractions, mais c'est plus rare pour les multiplications/divisions. Une des raisons est que le résultat d'une addition/soustraction prend un bit de plus que le résultat, là où les multiplications doublent le nombre de bits. Quand une addition déborde, le résultat réel est proche de la valeur maximale codable. mais quand une multiplication déborde, le résultat peut parfois valoir 200 à 60000 fois plus que la valeur maximale codable. Les calculs avec une valeur saturée/corrigée sont donc crédibles pour une suite d'additions, mais pas pour une suite de multiplications.
===La détection des débordements entiers===
Quand un débordement d'entier a eu lieu, il vaut mieux que l'additionneur prévienne ! Pour cela, l'additionneur a une '''sortie de débordement''', parfois nommée ''Overflow'', dont la valeur indique si l'addition a généré un débordement d'entier ou non. Reste que détecter un débordement ne se fait pas de la même manière selon que l'on parle d'un additionneur non-signé ou signé.
Pour les additionneur non-signés, l'additionneur calcule un bit de plus que ce qui est supporté par l'ordinateur. Par exemple, un additionneur 32 bits fournit un résultat sur 33 bits, un débordement d'entier a lieu quand le 33ème bit est à 1. Précisément, la sortie de débordement n'est autre que la retenue finale, celle fournie par le dernier additionneur complet. Le seul type de débordement possible est un débordement par le haut, où le résultat dépasse la valeur maximale. Avec l'arithmétique saturée, le circuit de saturation consiste en une seule couche de multiplexeurs, voire en un circuit de mise à la valeur maximale tel que vu dans le chapitre sur les opérations bits à bits.
[[File:Gestion des débordements d'entiers.png|centre|vignette|upright=2.5|Gestion des débordements d'entiers lors d'une addition non-signée.]]
Pour les additionneurs non-signés, la gestion des débordements d'entiers dépend fortement de la représentation signée. Nous allons étudier le cas du complément à deux. Si vous vous rappelez le chapitre 1, les calculs sur des nombres en complètement à deux utilisent les règles de l'arithmétique modulaire, c'est une condition nécessaire. À priori, on peut penser que dans ces conditions, les débordements d'entiers sont une chose parfaitement normale, qui nous permet d'avoir des résultats corrects. Néanmoins, certains débordements d'entiers peuvent survenir malgré tout et produire des bugs assez ennuyeux.
Si l'on tient en compte les règles du complément à deux, on sait que le bit de poids fort (le plus à gauche) permet de déterminer si le nombre est positif ou négatif : il indique le signe du nombre. Tout se passe comme si les entiers en complément à deux étaient codés sur un bit de moins, et avaient leur longueur amputé du bit de poids fort. Si le résultat d'un calcul écrase le bit de poids fort, il y a un débordement d'entiers. Il existe une règle simple qui permet de détecter ces débordements d'entiers. L'addition de deux nombres positifs ne peut pas être un nombre négatif. Si on additionne deux nombres dont le bit de signe est à 0 et que le bit de signe du résultat est à 1, on est en face d'un débordement d'entiers. Même chose pour deux nombres négatifs : le résultat de l'addition ne peut pas être positif. On peut résumer cela en une phrase : si deux nombres de même signe sont ajoutés, un débordement a lieu quand le bit du signe du résultat a le signe opposé.
Modifier les circuits d'au-dessus pour qu'ils détectent les débordements en complément à deux est simple comme bonjour : il suffit créer un petit circuit combinatoire qui prenne en entrée les bits de signe des opérandes et du résultat, et qui fasse le calcul de l'indicateur de débordements. Si l'on rédige sa table de vérité, on doit se retrouver avec la table suivante :
{|class="wikitable"
|-
!Entrées
!Sortie
|-
|000||0
|-
|001||1
|-
|010||0
|-
|011||0
|-
|100||0
|-
|101||0
|-
|110||1
|-
|111||0
|}
L'équation de ce circuit est la suivante, avec <math>S_a</math> et <math>S_b</math> les signes des deux opérandes, et <math>C_i</math> la retenue de la colonne précédente :
: <math> ( S_a . S_b . \overline{C_i} ) + ( \overline{S_a} . \overline{S_b} . C_i )</math>
En simplifiant, on obtient alors :
: <math>( S_a . S_b ) \oplus C_i</math>
Or, il se trouve que <math>S_a . S_b</math> est tout simplement la retenue en sortie du dernier additionneur, que nous noterons <math>C_o</math>. On trouve donc :
: <math>C_o \oplus C_i</math>
Il suffit donc de faire un XOR entre la dernière retenue et la précédente pour obtenir le bit de débordement.
<noinclude>
{{NavChapitre | book=Fonctionnement d'un ordinateur
| prev=Les circuits de décalage et de rotation
| prevText=Les circuits de décalage et de rotation
| next=Les circuits de comparaison
| nextText=Les circuits de comparaison
}}
</noinclude>
gd535um2jx63q94eps3bzm0a10b61ya
768780
768779
2026-06-27T13:38:58Z
Mewtow
31375
/* Le soustracteur à propagation de retenue */
768780
wikitext
text/x-wiki
Dans ce chapitre, nous allons voir les circuits capables de faire une addition ou une soustraction, ainsi que quelques circuits spécialisés. Précisons cependant que les fabricants de processeurs travaillent d'arrache-pied pour trouver des moyens de rendre ces circuits de calcul plus rapides et plus économes en énergie. Autant vous dire que les circuits que vous allez voir sont vraiment des circuits qui font pâle figure comparé à ce que l'on peut trouver dans un vrai processeur commercial !
==Les circuits pour additionner 2 ou 3 bits==
L'addition se fait en binaire de la même manière qu'en décimal. On additionne les chiffres/bits colonne par colonne, une éventuelle retenue est propagée à la colonne d'à côté. La soustraction fonctionne sur le même principe, sur le même modèle qu'en décimal.
[[File:Binary Addition Demonstration.svg|centre|vignette|Exemple d'addition en binaire.]]
En clair, additionner deux nombres demande d'additionner 2 bits et une retenue sur chaque colonne, et de propager les retenues d'une colonne à l'autre. La propagation des retenues est quelque chose de simple en apparence, mais qui est sujet à des optimisations extraordinairement nombreuses. Aussi, pour simplifier l'exposition, nous allons voir comment gérer une colonne avant de voir comment sont propagées les retenues.
[[File:Full Adder Block.svg|vignette|upright=1|class=transparent|Additionneur complet.]]
Si on effectue une addition en colonne, on doit additionner les deux bits sur la colonne, mais aussi additionner une éventuelle retenue. Il faut donc créer un circuit qui additionne trois bits : deux bits de données, plus une retenue. Ce circuit qui additionne trois bits est appelé un '''additionneur complet'''. Il fournit en sortie deux bits : un bit de somme et une retenue sortante. Voici sa table de vérité :
{|class="wikitable"
|-
! Retenue entrante !! Opérande 1 !! Opérande 2 !! !! Retenue sortante !! Bit de somme
|-
||0||0||0|| ||0||0
|-
||0||0||1|| ||0||1
|-
||0||1||0|| ||0||1
|-
||0||1||1|| ||1||0
|-
||1||0||0|| ||0||1
|-
||1||0||1|| ||1||0
|-
||1||1||0|| ||1||0
|-
||1||1||1|| ||1||1
|}
Il est possible d'utiliser un tableau de Karnaugh pour traduire la table de vérité, mais elle donne un résultat assez compliqué. La retenue et le bit de somme sont calculés à part. La retenue est calculée avec trois portes ET et une porte OU. Le bit de somme est calculé lui avec 4 portes ET, une porte OU, et trois portes NON.
[[File:Full Adler with 3 entries.png|centre|vignette|upright=2|Additionneur complet, fabriqué à partir de la table de vérité.]]
Le circuit obtenu est pas loin d'être optimal, que ce soit en termes de rapidité que de portes logiques utilisées. Mais il est assez compliqué à comprendre, vu que c'est une traduction littérale de la table de vérité. D'autres implémentations alternatives donnent des résultats plus compréhensibles. Nous allons les voir dans la suite de cette section.
===L'additionneur complet basé sur des demi-additionneurs===
[[File:1-bit half-adder.svg|class=transparent|vignette|Demi-addtionneur.]]
Nous avons déjà vu comment additionner deux bits dans le chapitre sur les incrémenteurs, mais quelques rappels ne seront pas de trop. Le '''demi-additionneur''' est un circuit qui additionne deux bits. Il implémente la table d'addition, qui est très simple en binaire. Jugez plutôt :
* 0 + 0 = 0, retenue = 0 ;
* 0 + 1 = 1, retenue = 0 ;
* 1 + 0 = 1, retenue = 0 ;
* 1 + 1 = 0, retenue = 1.
Mais un demi-additionneur n'additionne que deux bits, il manque de quoi additionner la retenue. Une solution pour cela est d'enchaîner deux demi-additionneurs : un qui additionne les deux bits de données, et un second qui additionne la retenue au résultat du précédent. La retenue finale se calcule en combinant les sorties de retenue des deux demi-additionneurs, avec une porte OU. Le résultat est un additionneur complet fabriqué avec deux demi-additionneurs. Pour vous en convaincre, établissez la table de vérité de ce circuit, vous verrez que ça marche.
[[File:Full Adder Blocks.svg|centre|vignette|upright=2|Additionneur complet fabriqué avec deux demi-additionneurs.]]
[[File:Half-adder.svg|vignette|upright=1|Circuit d'un demi-additionneur naïf.]]
L'implémentation naïve d'un demi-additionneur utilise une porte XOR et une porte ET, comme illustré ci-contre. Le circuit s'obtient en faisant uen traduction littérale de la table de vérité du circuit. Si je dis qu'elle est naïve, c'est car il est possible de l'optimiser, de manière à éliminer des redondances cachées. Mais nous avions déjà vu cela dans le chapitre sur les incrémenteurs, aussi laissons cela pour plus tard.
En combinant deux demi-additionneurs de ce type, on obtient l'additionneur complet suivant. Les deux sorties de retenue des demi-additionneurs sont combinées avec une porte OU, pour calculer la retenue finale.
{| class="flexible"
|[[File:Full Adder Modules.svg|class=transparent|Composition d'un additionneur complet. On voit bien que celui-ci est composé de deux demi-additionneurs, en rouge et en bleu, auxquels on a ajouté une porte OU pour calculer la retenue finale.]]
|[[File:Full-adder.svg|300px|class=transparent|Circuit d'un additionneur complet.]]
|}
Le fait de combiner les deux retenues avec une porte OU n'est pas ce qu'il y a de plus intuitif. La table de vérité nous dit que ça fonctionne, mais on comprend mal pourquoi. Mais tout devient plus clair quand on sait que les deux retenues ne peuvent pas être à 1 en même temps. Même dans l'addition 1 + 1 + 1, seule une retenue est à 1. Soit l'addition des deux bits d'opérande donne naissance à une retenue, soit c'est l'addition ''résultat + retenue entrante''. La retenue sortant est donc à 1 quand une des deux addition donne une retenue. D'où l'usage d'une porte OU. Remarquez qu'une porte XOR donne le même résultat, vu que le cas où les deux retenues sont à 1 n'est jamais rencontré.
===La propagation et la génération des retenues===
L'additionneur complet que nous allons voir dans cette section sert à introduire les concepts de propagation, génération et absorption de retenue. Il s'agit de concepts très importants quand on étudie les additionneurs. l'idée part d'un principe très simple : la retenue sortante dépend de la retenue d'entrée. La relation entre les deux se résume à trois cas, qui dépendent de la valeur des deux bits additionnés, nommés A et B.
* Dans le premier cas, la retenue entrante est égale à la retenue sortante. On dit que la retenue entrante est propagée par l'additionneur.
* Dans le second cas, la retenue est forcée à 0 : la retenue sortante vaut 0, peu importe la valeur de la retenue entrante.
* Dans le troisième cas, la retenue est forcée à 1 : la retenue entrante vaut 1, peu importe la valeur de la retenue entrante.
[[File:Signaux P et G fournis par un demi-additionneur.png|vignette|Signaux P et G fournis par un demi-additionneur]]
Maintenant, créons un circuit qui nous dise si une retenue est propagée ou générée. Il a deux bits de sortie, nommés P et G : P pour ''Propagate'', G pour ''Generate''. Le bit P est à 1 si la retenue entrante est propagée, il est à 0 sinon. Le bit G est à 1 si une retenue est générée, à 0 sinon. Une retenue est considérée comme absorbée si elle n'est pas ni propagée ni générée, pas besoin d'un troisième bit pour gérer ce cas.
Il se trouve que ce circuit n'est autre qu'un demi-additionneur ! Pour vous en rendre compte, regardez la table de vérité d'un additionneur complet, illustrée ci-dessous. Lorsque les deux bits d'opérande sont à 0, la retenue sortante vaut toujours 0. Si ils sont tous deux à 1, alors la retenue sortante vaut 1. S'ils sont différents, alors retenues sortante et entrante sont égales. Le bit P est donc généré par une simple porte XOR. Quant au bit G, il est à 1 si les deux bits d'opérandes sont à 1, ce qui correspond à une porte ET. Il se trouve que ces deux portes forment un demi-additionneur !
{|class="wikitable"
|-
! Opérande 1 !! Opérande 2 !! Retenue entrante !! !! Retenue sortante
|- class="f_rouge"
||0||0||0|| ||0
|- class="f_rouge"
||0||0||1|| ||0
|- class="f_bleu"
||0||1||0|| ||0
|- class="f_bleu"
||0||1||1|| ||1
|- class="f_bleu"
||1||0||0|| ||0
|- class="f_bleu"
||1||0||1|| ||1
|- class="f_vert"
||1||1||0|| ||1
|- class="f_vert"
||1||1||1|| ||1
|}
Ensuite, créons un circuit qui prend ces deux sorties P et G, et calcule la retenue sortante en fonction. Le circuit en question a trois entrées : la retenue entrante, les deux bits P et G. La retenue sortante vaut 1 soit si une retenue est générée, soit si la retenue entrante est propagée et qu'elle vaut 1. En notant <math>{C_{in}}</math> et <math>{C_{out}}</math> les retenues entrantes et sortante, on a :
: <math>{C_{out}} = G + \left( P . C_{in} \right)</math>
Le circuit est donc composé d'une porte OU et d'une porte ET. En combinant un demi-additionneur avec le circuit de calcul de retenue sortante vu plus haut, on a :
[[File:Additionneur complet avec propagation et génération de retenue.png|class=transparent|centre|vignette|upright=2|Additionneur complet avec propagation et génération de retenue.]]
Certes, on retombe sur le circuit vu plus haut. Mais il est possible de remplacer le circuit en orange par un autre. Par exemple, il est possible de le remplacer par multiplexeur, qui choisit entre la retenue générée et la retenue entrante (propagée). Le choix se fait selon la valeur du bit P, qui chosiit entre propager la retenue et la générer.
[[File:Additionneur crée avec un multiplexeur.png|centre|vignette|upright=1.5|Additionneur crée avec un multiplexeur]]
Le circuit exact est illustré ci-dessous. Il semble utiliser plus de portes logiques que l'additionneur complet précédent. Cependant, nous verrons dans quelques chapitres qu'il est possible d'implémenter un multiplexeur avec seulement 6 transistors, voire moins ! L'implémentation utilise des portes à transmission, mais nous en reparlerons dans le chapitre sur les transistors, quand nous verrons les additionneurs à ''Manchester Carry Chain''. Au passage, une variante de ce circuit a été utilisée dans le processeur processeur 8086 d'Intel, comme on le verra dans le chapitre suivant.
[[File:Additionneur complet basé sur un MUX.png|centre|vignette|upright=1|Additionneur complet basé sur un MUX]]
[[File:FulladderPG.png|vignette|upright=1|Additionneur complet avec deux sorties P et G.]]
Pour finir, sachez qu'il existe des additionneurs qui fournissent : le bit de résultat, deux sorties P et G qui indiquent si l'addition propage ou génère une retenue. Il ne s'agit pas d'additionneurs complets, car il manque de quoi calculer la retenue sortante. De tels additionneurs seront appelés des '''additionneurs partiels'''. De tels additionneurs sont utilisés dans certains additionneurs pour gagner en performance.
Enfin, il existe aussi ce que j'ai décidé d'appeler des '''additionneurs P/G''', qui sont des additionneurs complets auxquels on a ajouté deux sorties P et G, en plus de la sortie de retenue. Leur circuit est le même que celui d'un additionneur complet, auquel on a ajouté deux fils. Vu que ces deux sorties sont fournies par le premier demi-additionneur, ajouter ces deux sorties demande d'ajouter des fils, pas de portes logiques. De tels additionneurs sont aussi utilisés dans certains additionneurs pour gagner en performance.
[[File:FAwithGP.svg|centre|vignette|upright=2|Additionneur complet avec sorties P et G : circuit.Ç]]
===L'additionneur complet basé sur une porte à majorité===
Maintenant, voyons une dernière implémentation possible de l'additionneur complet. Mais avant de voir comment l'additionneur est implémenté, nous allons devoir faire un petit focus sur la retenue sortante. Reprenons la table de vérité, et regardons ce qu'il en est pour la retenue sortante uniquement.
{|class="wikitable"
|-
! Retenue entrante !! Opérande 1 !! Opérande 2 !! !! Retenue sortante
|-
||0||0||0|| ||0
|-
||0||0||1|| ||0
|-
||0||1||0|| ||0
|-
||0||1||1|| ||1
|-
||1||0||0|| ||0
|-
||1||0||1|| ||1
|-
||1||1||0|| ||1
|-
||1||1||1|| ||1
|}
Vous remarquerez que la retenue sortante est égale au bit majoritaire, parmi les trois bits d'opérande. Si deux ou trois bits sont à 1, la retenue sortante vaut 1. Et réciproquement avec 0. Le circuit de calcul de la retenue peut donc être remplacé par une '''porte à majorité'''.
[[File:Additionneur crée avec une porte à majorité.png|centre|vignette|upright=2|Additionneur crée avec une porte à majorité]]
Mais une porte à majorité est assez gourmande en circuit, ce qui fait qu'un additionneur pareil ne serait pas pratique, ni utile. Du moins, ce serait le cas s'il n'y avait pas une possibilité d'optimisation extrêmement intéressante. Il est possible de calculer le 'bit de somme' à partir de la retenue sortante ! En effet, le bit de somme est l'inverse de la retenue sortante, sauf dans deux cas : les trois bits d'entrée sont à 0, où ils sont tous à 1. Le bit de somme vaut 0 dans le premier cas, 1 dans le second cas.
{|class="wikitable"
|-
! Retenue entrante !! Opérande 1 !! Opérande 2 !! !! Retenue sortante !! Bit de somme
|- class="f_bleu"
||0||0||0|| ||0||0
|- class="f_vert"
||0||0||1|| ||0||1
|- class="f_vert"
||0||1||0|| ||0||1
|- class="f_vert"
||0||1||1|| ||1||0
|- class="f_vert"
||1||0||0|| ||0||1
|- class="f_vert"
||1||0||1|| ||1||0
|- class="f_vert"
||1||1||0|| ||1||0
|- class="f_rouge"
||1||1||1|| ||1||1
|}
En clair, 6 lignes de la table de vérité sur 8 peuvent se calculer avec une porte à majorité, le reste demandant quelques portes logiques pour faire la correction. L'implémentation en circuit demande donc trois choses :
* de calculer la retenue sortante et de quoi l'inverser ;
* un circuit qui vérifie si tous les bits opérande valent 0 : une porte NOR ou OU fait l'affaire ;
* un circuit qui vérifie s'ils valent tous 1, à savoir une porte ET.
Ensuite, on combine le résultat des trois circuits précédents pour obtenir le résultat final. La combinaison est le fait du circuit en jaune dans le schéma ci-dessous.
[[File:Full adder basé sur une modification de la retenue.png|centre|vignette|upright=2|Full adder basé sur une modification de la retenue]]
Le circuit en jaune est assez simple. Quand la porte ET sort un 1, la sortie doit être forcée à 1. Par contre, quand la porte OU sort un zéro, la sortie doit être forcée à 0. Il suffit donc d'enchainer un circuit de mise à 1 et un circuit de mise à 0. Nous avons vu dans le chapitre sur les opérations de masquage qu'il s'agit respectivement d'une porte OU et d'une porte ET.
[[File:Full adder basé sur une modification de la retenue interne, au complet.png|centre|vignette|upright=2|Full adder basé sur une modification de la retenue interne, au complet]]
Divers processeurs utilisaient des additionneurs complets de ce type : le fameux Z80, l'Intel 4004 et de l'Intel 8008 sont les exemples les plus notables. Pourtant, on pourrait s'étonner qu'un tel circuit ait existé. Il utilise beaucoup de portes logiques, a une profondeur logique supérieure : il n'a rien d'avantageux. Sauf qu'il était utilisé sur d'anciens processeurs, qui utilisaient la technologie dite TTL, différente de la technologie CMOS des transistors modernes. Et avec la technologie TTL, il est possible de fusionner plusieurs portes logiques ET et NOR en une seule porte logique ET/OU/NON ! Un additionneur complet construit ainsi ne prenait que deux portes logiques : une pour le calcul de la retenue sortante, une autre pour le reste du circuit.
==L'addition non signée==
Voyons maintenant un circuit capable d'additionner deux nombres entiers: l''''additionneur'''. Dans la version qu'on va voir, ce circuit manipulera des nombres strictement positifs. L'addition des nombres codés en complètement à deux sera vu dans une section ultérieure.
[[File:Full-adder-4bits-serial-CEI.svg|vignette|upright=0.5|Additionneur 4 bits, un bloc.]]
L'interface d'un additionneur est illustrée ci-contre. Un additionneur prend deux opérandes sur deux entrées séparées, et fournit le résultat sur une sortie dédiée. De plus, il a une entrée sur laquelle envoyer une retenue entrante, et a une sortie pour la retenue sortante. La sortie de retenue indique que le résultat de l'addition a débordé (au sens d'un débordement d'entier). Pour l'entrée de retenue, son utilité deviendra plus claire dans ce qui suit, mais souvenez-vous qu'elle existe.
L'entrée et la sortie de retenue sont parfois utilisées pour combiner plusieurs petits additionneurs, pour former un additionneur plus grand. Nous verrons de nombreux exemples dans ce qui suit. Il est assez commun de créer des additionneurs pour des opérandes de 16/32/64 bits, avec des additionneurs de 4/5 bits.
===L'additionneur série===
Il est possible d'additionner deux nombres bit par bit, grâce à un additionneur complet associé à plusieurs registres à décalages. Les opérandes sont placées chacune dans un registre à décalage, et l'additionneur complet utilise les bits sortants de ces registres à décalage. Le bit du résultat est envoyé au registre à décalage pour le résultat. La retenue de l'addition est stockée dans une bascule de 1 bit, en attente du prochain cycle d'horloge. Un tel additionneur est appelé un '''additionneur série'''. Il a été utilisé sur d'anciens ordinateurs dans les années 50-60, aussi bien des prototypes que des ordinateurs commerciaux.
[[File:Additionneur série.jpg|centre|vignette|upright=2.5|Additionneur série.]]
===L'additionneur à propagation de retenue===
L''''additionneur à propagation de retenue''' est aussi appelé l'additionneur ''ripple carry'', c'est son nom anglais. J'utiliserais occasionnellement le nom anglais dans ce qui suit, car celui-ci est plus court. Ilutilise un additionneur complet pour chaque colonne de l'addition. Pour gérer les retenues, il suffit ainsi de câbler des additionneurs complets les uns à la suite des autres, chacun envoyant sa retenue à la colonne suivante.
[[File:Ripplecarryadder.png|centre|vignette|upright=2.5|Additionneur à propagation de retenue.]]
Notons la présence de la retenue sortante, qui est utilisée pour détecter les débordements d'entier, ainsi que pour d'autres opérations. Le bit de retenue final est souvent stocké dans un registre spécial du processeur (généralement appelé ''carry flag'').
Notez aussi, à droite du schéma précédent, l'entrée de retenue <math>\text{R}_\text{Entrée}</math>. Elle est très utile pour l'implémentation de certaines opérations comme l'inversion de signe, la soustraction, l'incrémentation, etc. Certains processeurs sont capables de faire une opération appelée ADC, ADDC ou autre nom signifiant ''Addition with Carry'', qui permet de faire le calcul A + B + Retenue (la retenue en question est la retenue sortante de l'addition précédente, stockée dans le registre ''carry flag''). Son utilité principale est de permettre des additions d'entiers plus grands que ceux supportés par le processeur. Par exemple, cela permet de faire des additions d'entiers 32 bits sur un processeur 16 bits.
[[File:Adder Network 3 Bit Sum Delay 003.svg|vignette|upright=0.5|Propagation de retenue dans l'additionneur.]]
Cet additionneur utilise très peu de portes logiques, ce qui fait qu'il était utilisé sur certains processeurs 8 et 16 bits, dont le budget en portes logiques était limité. Bien que très simple, cet additionneur est cependant peu performant. Son temps de calcul est proportionnel à la taille des opérandes. Par exemple, additionner deux opérandes de 32 bits prendra deux fois plus de temps que pour des opérandes de 16 bits. La raison est que les retenues doivent se propager à travers le circuit, du premier additionneur jusqu'au dernier.
L'addition étant une opération fréquente, d'autres additionneurs ont été inventés. Ils utilisent des optimisations qui utilisent plus de circuits pour gagner en rapidité. Mais avant de voir ces optimisations, nous allons voir s'il est possible d'optimiser les additionneurs à propagation de retenue.
===L'additionneur à saut de retenue===
L'additionneur à propagation de retenue peut être optimisé de deux manières. La première accélère la propagation de la retenue, en agissant au niveau des additionneurs complets. La ''Manchester carry chain'' est une optimisation de ce type, mais on ne peut pas encore expliquer à ce stade du cours, car elle optimisait les additionneurs complets au niveau des transistors eux-mêmes. Elle a été utilisée sur de nombreux processeurs connus, comme le 8086 d'Intel ou le Z80.
La seconde manière rend l'addition plus rapide dans certains cas. Le résultat est un additionneur dont le temps de calcul est variable. Le calcul prendra quelques cycles d'horloges avec certains opérandes, mais d'autres opérandes ne verront aucune amélioration. Il n'améliore pas le pire des cas, dans lequel la retenue doit être propagée du début à la fin, du bit de poids faible au bit de poids fort. Mais dans d'autres cas, il permet d'avoir le résultat en avance. L'optimisation en question donne l''''additionneur à saut de retenue''' (''carry-skip adder'').
Il est composé en enchainant plusieurs additionneurs plus petits, qui additionnent 4/5 bits, rarement plus. De tels additionneurs de petite taille sont appelés des '''blocs''', dans ce qui suit. L'idée est de détecter précocement si la retenue entrante est propagée à travers tout le bloc. Si la retenue est propagée dans tous le bloc, on peut directement l'envoyer sur la sortie de retenue, la retenue saute le bloc entier. Dans le cas contraire, on doit calculer la retenue normalement. Le choix entre les deux est le fait d'un multiplexeur. Toute la difficulté est de savoir comment commander le multiplexeur.
[[File:Carry skip adder, principe de base.png|centre|vignette|upright=2.5|Carry skip adder : principe de base]]
Pour savoir si une retenue est propagée dans un bloc entier, il faut utiliser des additionneurs complets, qui ont en plus une sortie P indiquant que l'additionneur propage la retenue. Le bloc propage une retenue si chaque additionneur complet propage la retenue, donc s'ils ont tous leur sortie P à 1. Un simple ET logique suffit à déterminer si c'est le cas. Le signal de commande du multiplexeur est donc un vulgaire ET entre toutes les sorties P des additionneurs complets du bloc.
[[File:Additionneur à saut de retenue.png|centre|vignette|upright=2.5|Calcul de la commande du MUX.]]
Voici ce que ça donne pour un bloc de 4 bits :
[[File:CSAdder4Bit.svg|centre|vignette|upright=1.5|Additionneur ''carry skip'' : bloc de 4 bits.]]
L'additionneur à saut de retenue est construit en assemblant plusieurs blocs de ce type.
[[File:BCSAdder16Bit.svg|centre|vignette|upright=3|Additionneur à saut de retenue.]]
Les blocs sont tous identiques dans le cas le plus simple, mais il est possible d'utiliser des blocs de taille variable. Par exemple, le premier bloc peut avoir des opérandes de 6 bits, le second des opérandes de 7 bits, etc. Faire ainsi permet de gagner un petit peu en performances, si la taille de chaque bloc est bien choisie. La raison est une question de temps de propagation des retenues. La retenue met plus de temps à se propager à travers 8 blocs qu'à travers 4, ce qui prend plus de temps qu'à travers 2 blocs, etc. En tenir compte fait que la taille des blocs tend à augmenter ou diminuer quand on se rapproche des bits de poids fort.
===L'additionneur à sélection de retenue===
L''''additionneur à sélection de retenue''' utilise aussi des blocs, comme les additionneurs précédents. L'addition se fait en deux versions : une avec la retenue du bloc précédent valant zéro, et une autre version avec la retenue du bloc précédent valant 1. Il suffira alors de choisir le bon résultat avec un multiplexeur, une fois cette retenue connue. On gagne ainsi du temps en calculant à l'avance les valeurs de certains bits du résultat, sans connaître la valeur de la retenue. Petit détail : sur certains additionneurs à sélection de retenue, les blocs de base n'ont pas la même taille. Cela permet de tenir compte des temps de propagation des retenues entre les blocs.
[[File:Additionneur à sélection de retenue.png|centre|vignette|upright=1.5|Additionneur à sélection de retenue avec seulement deux blocs.]]
Dans les exemples du dessus, chaque sous-additionneur étaient des additionneurs à propagation de retenue. Mais ce n'est pas une obligation, et tout autre type d’additionneur peut être utilisé. Par exemple, on peut faire en sorte que les sous-additionneurs soient eux-mêmes des additionneurs à sélection de retenue, et poursuivre ainsi de suite, récursivement. On obtient alors un '''additionneur à somme conditionnelle''', plus rapide que l'additionneur à sélection de retenue, mais qui utilise beaucoup plus de portes logiques.
===Les additionneurs à anticipation de retenue===
Les '''additionneurs à anticipation de retenue''' calculent chaque retenue sans avoir à propager les retenues précédentes. Au lieu de calculer les retenues une par une, ils calculent toutes les retenues en parallèle, à partir des bits des opérandes. Une fois les retenues pré-calculées, il suffit de les additionner avec les deux bits adéquats, pour obtenir le résultat. Ces additionneurs sont composés de deux parties :
* un circuit qui pré-calcule les retenues, sans les propager, directement à partir des opérandes ;
* d'une couche d'additionneurs complets, qui additionnent chacun deux bits d'opérande et la retenue pré-calculée.
[[File:Additionneur à anticipation de retenue.png|centre|vignette|upright=1.5|Additionneur à anticipation de retenue.]]
Le calcul des retenues se fait en deux étapes. La première détermine si les retenues sont propagées ou générées, pour chaque colonne. Elle détermine, pour chaque colonne, les bits P et G qui indiquent respectivement : qu'une retenue a été générée sur cette colonne, que la colonne propage la retenue précédente. La seconde étape utilise ces signaux P et G pour déterminer la retenue sur chaque colonne. Le circuit de calcul des retenues est donc composé de deux parties : une couche de demi-additionneurs pour générer les signaux P et G, un circuit de calcul des retenues proprement dit.
[[File:Circuit complet d'un additionneur à anticipation de retenue.png|centre|vignette|upright=1.5|Circuit complet d'un additionneur à anticipation de retenue.]]
Pour rappel, la retenue de la colonne i, notée <math>C_i</math> est égale à :
: <math>G_i + (P_i . C_{i-1})</math>, avec G le signal de génération de retenue, P le signal de propagation de retenue.
Si on utilisait cette formule sans trop réfléchir, on retomberait sur un additionneur à propagation de retenue inutilement compliqué. L'astuce des additionneurs à anticipation de retenue consiste à remplacer le terme Ci−1 par sa valeur calculée avant. Par exemple, je prends un additionneur 4 bits. Je dispose de deux nombres A et B, contenant chacun 4 bits : A3, A2, A1, et A0 pour le nombre A, et B3, B2, B1, et B0 pour le nombre B. Si j'effectue les remplacements, j'obtiens les formules suivantes :
* C1 = G0 + ( P0 · C0 ) ;
* C2 = G1 + ( P1 · G0 ) + ( P1 · P0 · C0 ) ;
* C3 = G2 + ( P2 · G1 ) + ( P2 · P1 · G0 ) + ( P2 · P1 · P0 · C0 ) ;
* C4 = G3 + ( P3 · G2 ) + ( P3 · P2 · G1 ) + ( P3 · P2 · P1 · G0 ) + ( P3 · P2 · P1 · P0 · C0 ).
Ces formules nous permettent de déduire la valeur d'une retenue directement : il reste alors à créer un circuit qui implémente ces formules, et le tour est joué.
[[File:Four bit adder with carry lookahead.svg|centre|vignette|upright=1.5|Additionneur à anticipation de retenue de 4 bits.]]
Plus haut, j'ai dit que le circuit était composé de trois couches : une couche de demi-additionneur pour les signaux P et G ; un circuit d'anticipation de retenue, puis l'addition finale. Mais les deux bits d'opérandes sont déjà additionnés dans la couche de demi-additionneur, autant réutiliser le résultat pour l'additionner à la retenue. Pour éliminer toute redondance de ce type, il est possible d'utiliser des additionneurs partiels (des additionneurs où la sortie de retenue a été remplacée par deux sorties P et G). L'additionneur final est alors composé de plusieurs additionneurs complets de ce type, couplé à un circuit d'anticipation de retenue.
[[File:Cla4bits.png|centre|vignette|upright=2.5|Additionneur à anticipation de retenue de 4 bits.]]
Les additionneurs à anticipation de retenue sont plus rapides que les additionneurs à propagation de retenue. La raison est que les retenues sont calculées rapidement, dans un temps plus petit qu'en propageant la retenue. Pour rentrer dans les détails mathématiques, le temps de calcul n'est pas proportionnel au nombre de bits, mais proportionnel au logarithme de ce nombre de bits. Par contre, le gain en temps de calcul s'accompagne d'un cout en portes logiques conséquent. Le circuit qui calcule les retenues anticipées utilise beaucoup de portes logiques.
===Les additionneurs à calcul parallèle de préfixe===
Du fait du cout en portes logiques, utiliser un additionneur à anticipation de retenue est une solution pratique pour additionner 4 ou 8 bits. Mais pour des opérandes plus grands, de 16 ou 32 bits, cela utiliserait trop de portes logiques. Une partie de ce cout en portes logiques est cependant lié à des redondances. Si on analyse deux retenues différentes, à des colonnes différentes. Les circuits qui calculent ces deux retenues seront différents, mais ils contiendront des sous-circuits identiques. Pour vous en rendre compte, reprenons les quatre équations obtenues précédemment et essayez de trouver des redondances :
* C1 = G0 + ( P0 · C0 ) ;
* C2 = G1 + ( P1 · G0 ) + ( P1 · P0 · C0 ) ;
* C3 = G2 + ( P2 · G1 ) + ( P2 · P1 · G0 ) + ( P2 · P1 · P0 · C0 ) ;
* C4 = G3 + ( P3 · G2 ) + ( P3 · P2 · G1 ) + ( P3 · P2 · P1 · G0 ) + ( P3 · P2 · P1 · P0 · C0 ).
Vous devriez trouver :
* le terme ''P0 · C0'' dans toutes les lignes ;
* les termes ''P1 · P0 · C0'' et ''P1 · P0'' dans les trois dernières lignes ;
* le terme ''P2 · P1 · P0 · C0'' ; ''P2 · P1 · G0'' et ''P2 · G1'' dans les deux dernières lignes.
Il existe plusieurs manières d'exploiter de telles redondances, exploitées dans les '''additionneurs à calcul parallèle de préfixe'''. L'optimisation apportée est de générer des signaux ''propagate'' et ''generate'' pour des groupes de 2, 3, 4, ..., N colonnes. Par exemple, il est possible de générer un signal ''P 0 vers 3'', qui indique si la retenue de la première colonne est propagée jusqu'à la 4ème colonne. Un autre exemple est un signal qui indique qu'une retenue a été générée entre les colonnes 4 à 7 génèrent une retenue ou non. En clair, les signaux P et G ont maintenant un intervalle, qui précise de quelle colonne vers quelle colonne se fait la propagation, ou entre quelles colonnes se fait la génération.
Il est possible de combiner les signaux P et G de deux groupes de colonne, s'ils sont contiguës (ils peuvent aussi se recouvrir). Par exemple, il est possible de calculer les bits P et G pour les colonnes 0 à 10, à partir des deux signaux P/G des colonnes 0-4 et 5-10. Pour cela, les équations sont assez simples. Si je prends deux groupes nommés A et B, avec A pour les colonnes de poids fort et B celles de poids faible, on a :
: <math>P_{sortie} = P_A . P_B</math>
: <math>G_{sortie} = G_A . P_A . G_B</math>
Pour les bits des colonnes 2, 4, 8, 16, 32, 64, et autres, le calcul est simple. L'idée est de grouper les colonnes par groupes de 2. Puis, on calcule chaque groupe de 4 bits à partir de deux groupes de 2. Puis les groupes de 8 bits sont calculés à partir de deux groupes de 4 bits, et ainsi de suite pour toute puissance de deux.
[[File:Squelette d'un additionneur à calcul parallèle de préfixe.jpg|centre|vignette|upright=2|Squelette d'un additionneur à calcul parallèle de préfixe]]
Les seules difficultés surviennent pour les colonnes dont le nombre n'est pas une puissance de deux. Il y a plusieurs manières pour subdiviser ces colonnes en intervalles plus petits et combiner le tout. Et elles donnent chacune des additionneurs différents. Ils portent les noms d''''additionneur de Ladner-Fisher''', d''''additionneur de Brent-Kung''', d''''additionneur de Kogge-Stone''', etc. Dans le cas général, l'additionneur de Kogge-Stone est un des plus rapide, mais aussi un des plus gourmand en circuits, alors que c'est l'inverse pour l'additionneur de Brent-Kung. Les deux additionneurs sont illustrés ci-dessous, pour des opérandes de 8 bits. Dans ceux-ci, chaque losange calculent les bits P et G, à partir des signaux P et G de deux groupes précédents.
{|
|[[File:Brent-kung-8-bit.png|vignette|Brent-kung-8-bit]]
|[[File:Kogge-stone-8-bit.png|vignette|upright=1.2|Kogge-stone-8-bit]]
|}
===Le calcul parallèle de la retenue===
L'anticipation de retenue est très rapide, mais utilise beaucoup de circuits, même en utilisant le calcul parallèle de préfixe. Les additionneurs à propagation de retenue font eux le compromis inverse. Mais il existe des intermédiaires, qui visent à obtenir un compromis entre performance et cout en portes logiques.
Un de ces compromis découpe un additionneur de 16/32/64 bits en additionneurs de 4/5 bits, qui sont enchainés en utilisant la propagation de retenue. Par contre, ces petits additionneurs de 4/5 bits utilisent l'anticipation de retenue. Ces additionneurs simples seront nommés ''blocs'' dans ce qui suit. Par exemple, on peut utiliser 4 additionneurs à anticipation de retenue de 4 bits, et propager les retenues entre eux. Le premier bloc calcule une retenue, qui est propagée au second bloc. Le second bloc calcule lui aussi une retenue, qui est propagée au troisième bloc, etc.
[[File:4008 Functional Diagram.svg|vignette|upright=1|4008 Functional Diagram]]
Le gain en performance est significatif, mais il est possible de faire presque aussi rapide, tout en économisant beaucoup de circuits. L'idée est de simplifier les additionneurs 4 bits, afin de ne pas utiliser une anticipation de retenue complète. L'anticipation de retenue n'est utilisée que pour la retenue sortante, alors que les bits de somme sont calculées avec propagation de retenue. La retenue sortante est donc calculée en parallèle de l'addition. Chaque bloc contient, à côté d'un additionneur proprement dit, un circuit qui calcule la retenue sortante. Il existe de nombreuses manières de calculer la retenue sortante.
Le gain en portes logiques est assez significatif. En effet, on se passe de circuits d'anticipation de retenue pour les 4 bits de somme, on ne garde que celui pour la retenue sortante. Le fait d'utiliser la propagation de retenue pour calculer les 4 bits de somme est censé avoir un léger cout en performance, mais il se trouve que ce cout est très faible. En effet, le résultat n'est connu qu'une fois les retenues propagées jusqu'au dernier bloc. Et malgré l'anticipation des retenues, la propagation est assez lente. les additionneurs à propagation de retenue auront terminé leur travail bien avant.
Un exemple est celui de l'additionneur CMOS 4008, un additionneur de 4 bit. Il est composé en trois sections. Une première couche de demi-additionneurs calcule les signaux P et G utilisés à la fois pour l'anticipation de la retenue sortante, que pour calculer les bits de somme. Le circuit de calcul de la retenue sortante utilise l'anticipation de retenue. Le reste du circuit fait l'addition en propageant les retenues.
[[File:CMOS 4008, circuit découpé en sections.png|centre|vignette|upright=1.5|CMOS 4008, circuit découpé en sections]]
===Les additionneurs à anticipation de retenue multi-niveau===
Les additionneurs précédents mixent anticipation et propagation de retenue. Ceux que nous allons voir utilisent des blocs, mais utilisent l'anticipation de retenue entre les blocs. Les blocs utilisés peuvent être un additionneur à propagation de retenue, ou à anticipation de retenue, peu importe. Ce qui compte est que la retenue entrante de chaque bloc est calculée par anticipation de retenue.
Par exemple, il est possible de créer un additionneur 16 bits en utilisant 4 additionneurs/blocs de 4 bits, couplé à un circuit d’anticipation de retenue par bloc. Pour cela, les additionneurs de 4 bits doivent fournir deux sorties P et G. Les deux sorties indiquent que le bloc soit a généré une retenue, soit a propagé la retenue entrante. Par propagé, on veut dire que la retenue entrante du bloc s'est retrouvée sur la sortie de retenue du bloc. C'est le cas si les 4 colonnes propagent la retenue, ce qui signifie que :
: <math>P_\text{4 bits} = P_0 \cdot P_1 \cdot P_2 \cdot P_3</math>
Pour la génération, cela veut dire qu'une colonne a générée une retenue, qui a été propagée jusqu'à la sortie. En clair, pour chaque colonne, il faut que le G = 1, et que les signaux P des colonnes soient à 1. Pour les 4 colonnes, cela donne :
: <math>G_\text{4 bits} = G_3 + G_2 \cdot P_3 + G_1 \cdot P_3 \cdot P_2 + G_0 \cdot P_3 \cdot P_2 \cdot P_1</math>
[[File:Cla4bitsPG.png|centre|vignette|upright=2|Additionneur à anticipation de retenue de 4 bits.]]
Pour combiner 4 blocs de 4 bits, on devra donc combiner 4 bits P et G, avec un circuit d'anticipation de retenue.
[[File:16-bit lookahead carry unit.svg|centre|vignette|upright=2|Additionneur à anticipation de retenue de 16 bits.]]
Il est même possible d'aller plus loin et de combiner 4 additionneurs précédents pour obtenir un additionneur 64 bits.
[[File:64-bit lookahead carry unit.svg|centre|vignette|upright=2|Additionneur à anticipation de retenue de 64 bits.]]
Le résultat est un additionneur à plusieurs niveaux d’anticipation de retenue. Une anticipation de retenue dans un blocs de 4 bits (facultative), entre blocs de 4 bits, entre blocs de 16 bits. La même logique peut être utilisée avec des blocs de taille différente de 4, 16 et 64 bits.
==L'addition signée et la soustraction==
Après avoir vu l'addition, il est logique de passer à la soustraction, les deux opérations étant très proches. Si on sait câbler une addition entre entiers positifs, câbler une soustraction n'est pas très compliqué. De plus, la soustraction permet de faire des additions de nombres signés.
===Le soustracteur à propagation de retenue===
Pour soustraire deux nombres entiers, on peut adapter l'algorithme de soustraction utilisé en décimal, celui que vous avez appris à l'école. Celui-ci ressemble fortement à l'algorithme d'addition : on soustrait les bits de même poids, et on propage éventuellement une retenue sur la colonne suivante. À la différence de l'addition, la retenue est soustraite, et non ajoutée. En clair, une soustraction en binaire demande de faire deux soustraction : pour chaque bit de la première opérande, on soustrait le bit associé de la seconde, puis on soustrait la retenue entrante.
[[File:Algebra1 05 fig014.svg|centre|vignette|Soustraction en binaire, avec les retenues en rouge.]]
[[File:Half Subtractor Vektor.svg|vignette|Demi-soustracteur.]]
Dans le chapitre sur les incrémenteurs/décrémenteurs, nous avons vu un circuit qui soustrait deux bits, appelé le '''demi-soustracteur'''. Il ressemble beaucoup à un demi-additionneur, les différences se résumant à une porte NON ajoutée pour le calcul de la retenue. Il implémente la table de soustraction, qui est très simple en binaire :
* 0 - 0 = 0 ;
* 0 - 1 = 1 et une retenue ;
* 1 - 0 = 1 ;
* 1 - 1 = 0.
Comme pour les additionneurs, deux demi-soustracteurs peuvent être combinés pour donner un '''soustracteur complet'''. Le calcul de la retenue se fait en combinant les deux retenues des demi-soustracteurs avec une porte OU. Là encore, si on regarde les sorties des deux demi-soustracteurs, il est impossible d'avoir les deux sorties de retenues à 1 en même temps.
[[File:FullSubtractor.svg|centre|vignette|upright=2|Soustracteur complet.]]
[[File:Sub Network 3 Bit Step 002.svg|vignette|upright=1|Soustracteur à propagation de retenue pour opérandes de 3 bits.]]
Les soustracteurs complets sont utilisés pour créer des soustracteurs à propagation de retenue, sur le même modèle que les additionneurs. Et je précise bien "à propagation de retenue", car l'anticipation de retenue et les autres optimisations ne marchent pas exactement pareil entre addition et soustraction. Les signaux de propagation et de génération de retenue ne sont pas générés pareil, idem pour la manière de les combiner.
Il est possible de fusionner un additionneur à propagation de retenue et un soustracteur de même type. Pour cela, il suffit de remarquer que les demi-soustracteurs et les demi-additionneurs se ressemblent beaucoup. La seule différence entre les deux est une vulgaire porte NON. En remplaçant cette porte NON par un inverseur commandable, on obtient un circuit qui sert soit de demi-additionneur, soit de demi-soustracteur, suivant ce qu'on envoie sur l'entrée de commande. Ci-dessous est illustré ce que donne ce remplacement pour un soustracteur complet. Le résultat est un '''additionneur/soustracteur à propagation de retenue'''.
[[File:Additionneur-soustracteur complet.png|centre|vignette|upright=2|Additionneur-soustracteur complet, naïf.]]
Le circuit précédent peut cependant se simplifier drastiquement.
[[File:Full subtractor.svg|centre|vignette|upright=2|Additionneur-soustracteur complet, simplifié.]]
Il est donc possible de fusionner un additionneur avec un soustracteur, ce qui donne un '''additionneur/soustracteur'''. Mais comme je l'ai dit plus haut, cela ne marche que si l'''additionneur-/soustracteur'' utilise la propagation de retenue. Dès qu'on souhaite utiliser l'anticipation de retenue ou tout autre technique, les choses sont plus compliquées. Le calcul des retenues étant différent entre addition et soustraction, mutualiser les circuits d'anticipation de retenue pour les deux opérations ne donnera pas un résultat très propre. Ce qui n'est pas le cas avec les solutions que nous allons voir dans ce qui suit.
===L'additionneur-soustracteur pour opérandes codées en complément à deux===
Étudions le cas de la soustraction en complément à deux. Vous savez sûrement que a−b et a+(−b) sont deux expressions équivalentes. Et en complément à deux, − b = not(b) + 1. Dit autrement, a − b = a + not(b) + 1. On pourrait se dire qu'il faut deux additionneurs pour faire le calcul, mais la majorité des additionneurs possède une entrée de retenue pour incrémenter le résultat de l'addition. Un soustracteur en complément à deux est donc simplement composé d'un additionneur et d'un inverseur.
[[File:Additionneur soustracteur.png|centre|vignette|upright=2|Soustracteur en complément à deux.]]
Il est possible de créer un circuit capable d'effectuer soit une addition, soit une soustraction : il suffit de remplacer l'inverseur par un inverseur commandable, qui peut être désactivé. On a vu comment créer un tel inverseur commandable dans le chapitre sur les circuits combinatoires. On peut remarquer que l'entrée de retenue et l'entrée de commande de l'inverseur sont activées en même temps : on peut fusionner les deux signaux en un seul.
[[File:Additionneur-soustracteur en complément à deux.png|centre|vignette|upright=2|Additionneur-soustracteur en complément à deux.]]
Une implémentation alternative est la suivante. Elle remplace l'inverseur commandable par un multiplexeur.
[[File:4-bit ripple carry adder-subtracter.svg|centre|vignette|upright=2|Additionneur-soustracteur en complément à deux, version alternative.]]
===L'additionneur-soustracteur pour opérandes codées en signe-magnitude===
Passons maintenant aux nombres codés en signe-valeur absolue, les deux opérandes étant notées A et B. Suivant les signes des deux opérandes, on a quatre cas possibles : A + B, A − B (B négatif), −A + B (A négatif) et −A − B (A et B négatifs). Une astuce est que le circuit n'a besoin que de calculer A + B et A − B : il peut les inverser pour obtenir − A − B ou B − A. A + B et A − B peuvent se calculer avec un additionneur-soustracteur, reste à corriger le résultat. Il suffit de lui ajouter un inverseur commandable pour obtenir le circuit d'addition finale.
[[File:Additionneur en signe-valeur absolue.png|centre|vignette|upright=2|Additionneur en signe-valeur absolue.]]
Toute la difficulté tient dans le calcul du bit de signe du résultat, quand interviennent des soustractions. Autant l'addition de deux nombres de même signe (A + B et −A − B) ne pose aucun problème, autant les soustractions posent problème (A − B et −A + B). Suivant que <math>A<B</math> ou que <math>A>B</math>, le signe du résultat ne sera pas le même. Déterminer le signe du résultat se fait en regardant les bits de débordement d'entier, comme on le verra plus bas.
===L'additionneur-soustracteur pour opérandes codées en représentation par excès===
Passons maintenant aux nombres codés en représentation par excès. On pourrait croire que ces nombres s'additionnent comme des nombres non-signés, mais ce serait oublier la présence du biais, qui pose problème. Dans les cas de nombres signés gérés avec un biais, voyons ce que donne l'addition de deux nombres :
: <math>( n_1 + biais ) + ( n_2 + biais ) = n_1 + n_2 + 2 \times biais</math>
Or, le résultat correct serait :
: <math>n_1 + n_2 + biais</math>
En effectuant l'addition telle quelle, le biais est compté deux fois. On doit donc le soustraire après l'addition pour obtenir le résultat correct.
Même chose pour la soustraction qui donne ceci :
: <math>(n_1 + biais) - (n_2 + biais) = n_1 - n_2</math>
Or, le résultat correct serait :
: <math> ( n_1 - n_2 ) + biais </math>
Il faut rajouter le biais pour obtenir l'exposant correct.
On a donc besoin de deux additionneurs/soustracteurs : un pour additionner/soustraire les représentations binaires des opérandes, et un autre pour ajouter/retirer le biais en trop/manquant.
==L'additionneur BCD==
Maintenant, voyons un additionneur qui additionne deux entiers au format BCD. Pour cela, nous allons devoir passer par deux étapes. La première est de créer un circuit capable d'additionneur deux chiffres BCD. Ensuite, nous allons voir comment enchaîner ces circuits pour créer un additionneur BCD complet.
===L'additionneur BCD qui fait l'opération chiffre par chiffre===
Nous allons commencer par voir un additionneur qui additionne deux chiffres en BCD, une sorte d'équivalent BCD de l'additionneur complet. Il fournit un résultat sur 4 bits et une retenue qui est mise à 1 si le résultat dépasse 10 (la limite d'un chiffre BCD). Les deux opérandes sont des chiffres BCD codés sur 4 bits et sont additionnés en binaire par un additionneur des plus normaux, similaire à ceux vus plus haut. Le résultat est alors un entier codé en binaire, sur 5 bits, qu'on corrige/convertit pour obtenir un chiffre BCD et une retenue sortante.
Pour corriger le résultat, une idée intuitive serait de prendre le résultat et de faire une division par 10. Le quotient donne la retenue, alors que le reste est le résultat, le chiffre BCD. Mais un circuit diviseur par 10 utilise beaucoup de portes logiques, ce qui ne vaut pas le coup. Une autre méthode détecte si le résultat est égal ou supérieur à 10, ce qui correspond à un "débordement" (on dépasse les limites d'un chiffre BCD). Si le résultat est plus petit que 10, il n'y a rien à faire : le résultat est bon et la retenue est de zéro. Par contre, si le résultat vaut 10 ou plus, il faut corriger le résultat et générer une retenue à 1.
Il faut donc ajouter un circuit qui détecte si le résultat est supérieur à 9, qui calcule directement la retenue. Ce circuit peut se fabriquer simplement à partir de sa table de vérité, ou en utilisant les techniques que nous verrons dans un chapitre ultérieur sur les comparateurs. La solution la plus simple est clairement d'utiliser la table de vérité, ce qui est très simple, assez pour être laissé en exercice au lecteur. Pour comprendre comment corriger le résultat, établissons une table de vérité qui associe le résultat et le résultat corrigé. L'entrée vaut au minimum 10 et au maximum 9 + 9 = 18. On considère la sortie comme un tout, la retenue étant un 5ème bit, le bit de poids fort.
{|class="wikitable"
|-
! colspan="5" | Entrée
!
! rowspan="10" |
! Retenue
! Résultat corrigé (sans retenue)
! interprétation de la sortie en binaire (retenue inclue)
|-
| 0 || 1 || 0 || 1 || 0
| (10)
| 1 || 0000
| (16)
|-
| 0 || 1 || 0 || 1 || 1
| (11)
| 1 || 0001
| (17)
|-
| 0 || 1 || 1 || 0 || 0
| (12)
| 1 || 0010
| (18)
|-
| 0 || 1 || 1 || 0 || 1
| (13)
| 1 || 0011
| (19)
|-
| 0 || 1 || 1 || 1 || 0
| (14)
| 1 || 0100
| (20)
|-
| 0 || 1 || 1 || 1 || 1
| (15)
| 1 || 0101
| (21)
|-
| 1 || 0 || 0 || 0 || 0
| (16)
| 1 || 0110
| (22)
|-
| 1 || 0 || 0 || 0 || 1
| (17)
| 1 || 0111
| (23)
|-
| 1 || 0 || 0 || 1 || 0
| (18)
| 1 || 1000
| (24)
|}
En analysant le tableau, on voit que pour corriger le résultat, il suffit d'ajouter 6. La raison est que le résultat déborde d'un nibble à 16 en binaire, mais à 10 en décimal : il suffit d'ajouter la différence entre les deux, à savoir 6, et le débordement binaire fait son travail. Donc, la correction après une addition est très simple : si le résultat dépasse 9, on ajoute 6.
On peut maintenant implémenter l'additionneur BCD, en combinant le comparateur avec 10, le circuit de correction, et l'additionneur. La première solution calcule deux versions du résultat : la version corrigée, la version normale. Le choix entre les deux est réalisée par un multiplexeur, commandé par le comparateur.
[[File:Additionneur BCD.png|centre|vignette|upright=1.5|Additionneur BCD]]
L'autre solution utilise un circuit commandable qui soit additionne 6, soit ne fait rien. Le choix entre les deux est commandé par le bit calculé par le comparateur.
[[File:Additionneur BCD, seconde version.png|centre|vignette|upright=1.5|Additionneur BCD, seconde version.]]
Une version alternative du circuit précédent est la suivante. Il contient deux additionneurs : un pour additionner les deux chiffres BCD, un autre pour additionner 6 si besoin. Le résultat du comparateur est directement utilisé pour générer l'opérande du second additionneur : 0 ou 6. Le circuit est simple à concevoir, mais gaspille beaucoup de circuit. Idéalement, il vaudrait mieux utiliser un circuit combinatoire d'addition avec une constante.
[[File:Adder 4 Bit BCD.svg|centre|vignette|upright=2|Additionneur BCD, circuit complet.]]
Pour obtenir un additionneur BCD complet, il suffit d’enchaîner les additionneurs précédents, comme on le ferait avec les additionneurs complets dans un additionneur à propagation de retenue. Au final, l'additionneur BCD est beaucoup plus compliqué qu'un additionneur normal, car il rajoute un comparateur ">9", un petit additionneur pour ajouter 6 et éventuellement d'autres circuits. De plus, il est difficile d'appliquer les optimisations disponibles sur les additionneurs non-BCD. Notamment, les circuits d'anticipation de retenue sont totalement à refaire et le résultat est relativement compliqué. C'est ce qui explique pourquoi le BCD a progressivement été abandonné au profit du binaire simple.
La soustraction en BCD se fait comme en binaire : le nombre à soustraire est remplacé par son complément, le circuit additionne le complément et l'autre opérande, le débordement d'entier fait que le résultat marche. Sauf qu'ici, le complément est un complément à 9. Il se calcule chiffre par chiffre : chaque chiffre est remplacé par (9 - le chiffre en question).
===L'additionneur BCD par ajustement décimal===
L'additionneur BCD précédent effectuait son travail chiffre BCD par chiffre BCD, mais il existe des additionneurs BCD qui font autrement. Sur les premiers processeurs x86, il n'y avait pas d'opération d'addition BCD proprement dit, seulement une addition binaire normale de 8, 16 ou 32 bits. Par contre, elle était secondée par une opération dite d''''ajustement décimal''' qui transformait un nombre binaire en nombre codé en BCD. L'opération d'ajustement décimal prenait un opérande de 8 bits codé en binaire et fournissait un résultat de la même taille, c'est à dire deux chiffres BCD. Effectuer une addition BCD demandait donc de faire deux opérations à la suite : une addition binaire simple, suivie par l'opération d'ajustement décimal. Cela permettait de gérer des nombres entiers en binaire usuel et des entiers BCD sans avoir deux instructions d'addition séparées pour les deux, sans compter que cela simplifiait aussi les circuits d'addition.
L'ajustement décimal s'effectue en ajoutant une constante bien précise à l'opérande à convertir en BCD. L'idée est que la constante est découpée en morceaux de 4 bits, correspondant chacun à un chiffre BCD de l'opérande, chaque morceau contenant soit un 0, soit 6. Cela permet d'ajouter soit 0, soit 6, à chaque chiffre BCD, et donc de le corriger. La propagation des retenues d'un chiffre à l'autre est effectuée automatiquement par l'addition binaire de la constante. L'opération d'ajustement décimal calcule automatiquement la constante. Elle découpe l'opérande en ''nibbles'', vérifie si chaque ''nibble'' est supérieur ou égal à 10, puis détermine la valeur de chaque ''nibble'' de la constante finale. Par exemple, si je prends l'opérande 1001 1110, le ''nibble'' de poids faible déborde, alors que celui de poids fort non. La constante sera donc 0000 0110 : 0x06. Inversement, si le ''nibble'' de poids fort déborde et pas celui de poids faible, la constante sera alors 0x60. Et la constante est de 0x66 si les deux ''nibbles'' débordent, de 0x00 si aucun ne déborde.
Le circuit d’ajustement décimal est donc composé de trois étapes : deux étapes pour calculer la constante, et un circuit d'addition pour additionner cette constante au nombre de départ. La première étape découpe l'opérande en morceaux de 4 bits, en chiffres BCD, et vérifie si chacun d'entre eux vaut 10 ou plus. La seconde étape prend les résultats de la première étape, et les combine pour calculer la constante. Enfin, on trouve l'addition finale, qui était réalisée par un circuit d'addition utilisé à la fois pour l'ajustement décimal et l'addition binaire. La différence entre une addition normale et une opération d'ajustement décimal tient dans le fait que les deux premières étapes sont désactivées dans une addition normale.
[[File:Additionneur BCD parallèle.png|centre|vignette|upright=1.5|Additionneur BCD parallèle]]
===L'additionneur biquinaire===
Les entiers BCD ne sont qu'un des encodages hybrides entre décimal et binaire. L'encodage biquinaire est l'un d'entre eux et nous allons faire un rappel rapide à ce sujet. Pour simplifier, un chiffre encodé en biquinaire est composé de deux parties : un bit, couplé à une partie quinaire encodée en représentation ''one-hot''. La partie quinaire encode un nombre allant de 0 à 4, ce qui prend 5 bits (0, 1, 2, 3 et 4). Le bit indique s'il faut ou non ajouter 5 à la valeur encodée par la partie quinaire. Ainsi, on peut coder tous les nombres de 0 à 9.
Additionner deux nombres de biquinaire demande donc d'additionner deux parties quinaires encodées en ''one-hot'' et d'additionner deux bits. Mais attention : il faut tenir compte de la retenue de l'addition des parties quinaires. Et idéalement, il faut aussi tenir compte d'une retenue entrante, provenant de l'addition de la colonne de chiffres précédente. Toute la difficulté vient de la création de l'additionneur ''one-hot''. Heureusement, vu qu'il n'y a que 4-5 bits à additionner, il est souvent fabriqué à partir de sa table de vérité.
[[File:Additionneur bi-quinaire.png|centre|vignette|upright=2|Additionneur bi-quinaire]]
Un avantage du biquinaire est que le calcul du complément à 9 est très simple. Il faut pour cela : inverser la partie binaire avec une porte NON, puis inverser l'ordre des bits de la partie quinaire. Concrètement, le bit de poids faible devient le bit de poids fort, et ainsi de suite. Par exemple, une partie quinaire 01000 devient 00010, 10000 devient 00001, 00100 ne change pas, etc. Le tout peut se calculer avec une porte NON et 5 multiplexeurs.
===L'additionneur BCD avec calculs intermédiaires en biquinaire===
L'ordinateur IBM 1401, un ancien ''mainframe'' des années 60, utilisait un additionneur BCD un peu particulier. Les nombres étaient encodés en BCD dans la mémoire de l'ordinateur, mais les circuits de calcul utilisaient la représentation biquinaire. Lors d'un calcul, le processeur de l'ordinateur traduisait les chiffres BCD en représentation biquinaire, faisait une addition en biquinaire, avant de traduire le résultat en BCD normal.
Pour être précis, l'IBM 1401 utilisait une variante du biquinaire. L'encodage biquinaire de l'IBM 1401 est le suivant : la partie binaire disait si le chiffre était pair ou non, la partie quinaire encodait les valeurs 0, 2, 4, 6 et 8. Le chiffre se calculait en additionnant la partie binaire (0 ou 1) au nombre pair encodé par la partie quinaire. Si l'IBM 1401 utilisait cette variante du biquinaire, c'est car elle donnait des circuits de conversion BCD-biquinaire plus économes en portes logiques et plus rapides.
La partie binaire est le bit de poids faible du chiffre BCD, la partie biquinaire est calculée par un simple décodeur qui prend en entrée le chiffre BCD, amputé de son bit de poids faible. La traduction inverse demande d'utiliser un encodeur, à la place du décodeur. Par contre, le circuit d'addition biquinaire était plus compliqué du fait de la gestion des retenues. L'addition des parties binaires et quinaires se faisait en parallèle, dans deux additionneurs séparés. Cependant, l'addition des parties binaire fournit une retenue, qu'il faut prendre en compte. Pour cela, l'IBM 1401 disposait d'un troisième additionneur qui fournissait le résultat final, encodé en biquinaire.
[[File:Additionneur bi-quinaire de l'IBM 1401.png|centre|vignette|upright=2|Additionneur biquinaire de l'IBM 1401]]
Une implémentation moderne demanderait d'utiliser des portes ET combinées à des portes OU, le circuit pouvant être construit simplement à partir de sa table de vérité. Sur l'IBM 1401, le circuit était cependant différent, en raison de l'utilisation de OU câblés, des croisements de fils qui fonctionnent comme des portes OU, que nous n'avons pas encore vu pour le moment, mais qui seront détaillés dans quelques chapitres. Les OU câblés étaient utilisés pour simplifier le design du circuit, mais demandaient des portes logiques spécifiques, ce qui collait avec le fait que ce ''mainframe'' utilisait des transistors en Germanium. L'implémentation exacte est décrite dans cet article de blog, mais je ne recommande sa lecture qu'à ceux qui savent ce qu'est un OU câblé :
* [https://www.righto.com/2015/10/qui-binary-arithmetic-how-1960s-ibm.html Qui-binary arithmetic: how a 1960s IBM mainframe does math].
==Les débordements d'entier lors d'une addition/soustraction==
Les instructions arithmétiques manipulent des entiers codés sur un nombre fixe de bits, qui ne peuvent prendre leurs valeurs que dans un intervalle. Pour les nombres positifs, un ordinateur qui code ses entiers sur n bits pourra coder tous les entiers allant de 0 à <math>2^n -1</math>. Pour les nombres négatifs, l'intervalle est différent et dépend de la représentation utilisée. Dans le cas général, l'ordinateur peut coder les valeurs comprises de <math>N_\text{min}</math> à <math>N_\text{max}</math>. Si le résultat d'un calcul sort de cet intervalle, il ne peut pas être représenté par l'ordinateur et il se produit ce qu'on appelle un '''débordement d'entier'''.
La '''valeur haute de débordement''' désigne la première valeur qui est trop grande pour être représentée par l'ordinateur. Par exemple, pour un ordinateur qui peut coder tous les nombres entre 0 et 7, la valeur haute de débordement est égale à 8. On peut aussi définir la '''valeur basse de débordement''', qui est la première valeur trop petite pour être codée par l'ordinateur. Par exemple, pour un ordinateur qui peut coder tous les nombres entre 8 et 250, la valeur basse de débordement est égale à 7. Pour les nombres entiers, la valeur haute de débordement vaut <math>N_\text{max} + 1</math> , alors que la valeur basse vaut <math>N_\text{min} - 1</math> (avec <math>N_\text{max}</math> et <math>N_\text{min}</math> respectivement la plus grande et la plus petite valeur codable par l'ordinateur).
===La correction des débordements d'entier : l'arithmétique saturée===
Quand un débordement d'entier survient, tous les circuits de calcul ne procèdent pas de la même manière. Dans les grandes lignes, il y a deux réactions possibles : soit on corrige automatiquement le résultat du débordement, soit on ne fait rien et on se contente de détecter le débordement.
Si le débordement n'est pas corrigé automatiquement, le circuit ne conserve que les bits de poids faibles du résultat. Les bits en trop sont simplement ignorés. On dit qu'on utilise l''''arithmétique modulaire'''. Le problème avec ce genre d'arithmétique, c'est qu'une opération entre deux grands nombres peut donner un résultat très petit. Par exemple, si je dispose de registres 4 bits et que je souhaite faire l'addition 1111 + 0010 (ce qui donne 15 + 2), le résultat est censé être 10001 (17), ce qui est un résultat plus grand que la taille d'un registre. En conservant les 4 bits de poids faible, j’obtiens 0001 (1). En clair, un résultat très grand est transformé en un résultat très petit. Cela peut poser problèmes si on travaille uniquement avec des nombres positifs, mais c'est aussi utilisé pour coder des nombres en complément à deux.
D'autres circuits utilisent ce qu'on appelle l''''arithmétique saturée''' : si un calcul génère un débordement, on arrondi le résultat au plus grand entier supporté par le circuit. Les circuits capables de calculer en arithmétique saturée sont un peu plus complexes, vu qu'il faut rajouter des circuits pour corriger le résultat en cas de débordement. Il suffit généralement de rajouter un ''circuit de saturation'', qui prend en entrée le résultat et le corrige en cas de débordement. Ce circuit de saturation met la valeur maximale en sortie si un débordement survient, mais se contente de recopier le résultat du calcul sur sa sortie s'il n'y a pas de débordement. Typiquement, il est composé d'une couche de multiplexeurs, qui sélectionnent quelle valeur mettre sur la sortie : soit le résultat du calcul, soit le plus grand nombre entier géré par le processeur, soit le plus petit (pour les nombres négatifs/soustractions).
L'arithmétique saturée est utilisée pour les additions et soustractions, mais c'est plus rare pour les multiplications/divisions. Une des raisons est que le résultat d'une addition/soustraction prend un bit de plus que le résultat, là où les multiplications doublent le nombre de bits. Quand une addition déborde, le résultat réel est proche de la valeur maximale codable. mais quand une multiplication déborde, le résultat peut parfois valoir 200 à 60000 fois plus que la valeur maximale codable. Les calculs avec une valeur saturée/corrigée sont donc crédibles pour une suite d'additions, mais pas pour une suite de multiplications.
===La détection des débordements entiers===
Quand un débordement d'entier a eu lieu, il vaut mieux que l'additionneur prévienne ! Pour cela, l'additionneur a une '''sortie de débordement''', parfois nommée ''Overflow'', dont la valeur indique si l'addition a généré un débordement d'entier ou non. Reste que détecter un débordement ne se fait pas de la même manière selon que l'on parle d'un additionneur non-signé ou signé.
Pour les additionneur non-signés, l'additionneur calcule un bit de plus que ce qui est supporté par l'ordinateur. Par exemple, un additionneur 32 bits fournit un résultat sur 33 bits, un débordement d'entier a lieu quand le 33ème bit est à 1. Précisément, la sortie de débordement n'est autre que la retenue finale, celle fournie par le dernier additionneur complet. Le seul type de débordement possible est un débordement par le haut, où le résultat dépasse la valeur maximale. Avec l'arithmétique saturée, le circuit de saturation consiste en une seule couche de multiplexeurs, voire en un circuit de mise à la valeur maximale tel que vu dans le chapitre sur les opérations bits à bits.
[[File:Gestion des débordements d'entiers.png|centre|vignette|upright=2.5|Gestion des débordements d'entiers lors d'une addition non-signée.]]
Pour les additionneurs non-signés, la gestion des débordements d'entiers dépend fortement de la représentation signée. Nous allons étudier le cas du complément à deux. Si vous vous rappelez le chapitre 1, les calculs sur des nombres en complètement à deux utilisent les règles de l'arithmétique modulaire, c'est une condition nécessaire. À priori, on peut penser que dans ces conditions, les débordements d'entiers sont une chose parfaitement normale, qui nous permet d'avoir des résultats corrects. Néanmoins, certains débordements d'entiers peuvent survenir malgré tout et produire des bugs assez ennuyeux.
Si l'on tient en compte les règles du complément à deux, on sait que le bit de poids fort (le plus à gauche) permet de déterminer si le nombre est positif ou négatif : il indique le signe du nombre. Tout se passe comme si les entiers en complément à deux étaient codés sur un bit de moins, et avaient leur longueur amputé du bit de poids fort. Si le résultat d'un calcul écrase le bit de poids fort, il y a un débordement d'entiers. Il existe une règle simple qui permet de détecter ces débordements d'entiers. L'addition de deux nombres positifs ne peut pas être un nombre négatif. Si on additionne deux nombres dont le bit de signe est à 0 et que le bit de signe du résultat est à 1, on est en face d'un débordement d'entiers. Même chose pour deux nombres négatifs : le résultat de l'addition ne peut pas être positif. On peut résumer cela en une phrase : si deux nombres de même signe sont ajoutés, un débordement a lieu quand le bit du signe du résultat a le signe opposé.
Modifier les circuits d'au-dessus pour qu'ils détectent les débordements en complément à deux est simple comme bonjour : il suffit créer un petit circuit combinatoire qui prenne en entrée les bits de signe des opérandes et du résultat, et qui fasse le calcul de l'indicateur de débordements. Si l'on rédige sa table de vérité, on doit se retrouver avec la table suivante :
{|class="wikitable"
|-
!Entrées
!Sortie
|-
|000||0
|-
|001||1
|-
|010||0
|-
|011||0
|-
|100||0
|-
|101||0
|-
|110||1
|-
|111||0
|}
L'équation de ce circuit est la suivante, avec <math>S_a</math> et <math>S_b</math> les signes des deux opérandes, et <math>C_i</math> la retenue de la colonne précédente :
: <math> ( S_a . S_b . \overline{C_i} ) + ( \overline{S_a} . \overline{S_b} . C_i )</math>
En simplifiant, on obtient alors :
: <math>( S_a . S_b ) \oplus C_i</math>
Or, il se trouve que <math>S_a . S_b</math> est tout simplement la retenue en sortie du dernier additionneur, que nous noterons <math>C_o</math>. On trouve donc :
: <math>C_o \oplus C_i</math>
Il suffit donc de faire un XOR entre la dernière retenue et la précédente pour obtenir le bit de débordement.
<noinclude>
{{NavChapitre | book=Fonctionnement d'un ordinateur
| prev=Les circuits de décalage et de rotation
| prevText=Les circuits de décalage et de rotation
| next=Les circuits de comparaison
| nextText=Les circuits de comparaison
}}
</noinclude>
rcd5az84klscc6rsfhgtd7bi86ezii1
768782
768780
2026-06-27T13:44:34Z
Mewtow
31375
/* Le soustracteur à propagation de retenue */
768782
wikitext
text/x-wiki
Dans ce chapitre, nous allons voir les circuits capables de faire une addition ou une soustraction, ainsi que quelques circuits spécialisés. Précisons cependant que les fabricants de processeurs travaillent d'arrache-pied pour trouver des moyens de rendre ces circuits de calcul plus rapides et plus économes en énergie. Autant vous dire que les circuits que vous allez voir sont vraiment des circuits qui font pâle figure comparé à ce que l'on peut trouver dans un vrai processeur commercial !
==Les circuits pour additionner 2 ou 3 bits==
L'addition se fait en binaire de la même manière qu'en décimal. On additionne les chiffres/bits colonne par colonne, une éventuelle retenue est propagée à la colonne d'à côté. La soustraction fonctionne sur le même principe, sur le même modèle qu'en décimal.
[[File:Binary Addition Demonstration.svg|centre|vignette|Exemple d'addition en binaire.]]
En clair, additionner deux nombres demande d'additionner 2 bits et une retenue sur chaque colonne, et de propager les retenues d'une colonne à l'autre. La propagation des retenues est quelque chose de simple en apparence, mais qui est sujet à des optimisations extraordinairement nombreuses. Aussi, pour simplifier l'exposition, nous allons voir comment gérer une colonne avant de voir comment sont propagées les retenues.
[[File:Full Adder Block.svg|vignette|upright=1|class=transparent|Additionneur complet.]]
Si on effectue une addition en colonne, on doit additionner les deux bits sur la colonne, mais aussi additionner une éventuelle retenue. Il faut donc créer un circuit qui additionne trois bits : deux bits de données, plus une retenue. Ce circuit qui additionne trois bits est appelé un '''additionneur complet'''. Il fournit en sortie deux bits : un bit de somme et une retenue sortante. Voici sa table de vérité :
{|class="wikitable"
|-
! Retenue entrante !! Opérande 1 !! Opérande 2 !! !! Retenue sortante !! Bit de somme
|-
||0||0||0|| ||0||0
|-
||0||0||1|| ||0||1
|-
||0||1||0|| ||0||1
|-
||0||1||1|| ||1||0
|-
||1||0||0|| ||0||1
|-
||1||0||1|| ||1||0
|-
||1||1||0|| ||1||0
|-
||1||1||1|| ||1||1
|}
Il est possible d'utiliser un tableau de Karnaugh pour traduire la table de vérité, mais elle donne un résultat assez compliqué. La retenue et le bit de somme sont calculés à part. La retenue est calculée avec trois portes ET et une porte OU. Le bit de somme est calculé lui avec 4 portes ET, une porte OU, et trois portes NON.
[[File:Full Adler with 3 entries.png|centre|vignette|upright=2|Additionneur complet, fabriqué à partir de la table de vérité.]]
Le circuit obtenu est pas loin d'être optimal, que ce soit en termes de rapidité que de portes logiques utilisées. Mais il est assez compliqué à comprendre, vu que c'est une traduction littérale de la table de vérité. D'autres implémentations alternatives donnent des résultats plus compréhensibles. Nous allons les voir dans la suite de cette section.
===L'additionneur complet basé sur des demi-additionneurs===
[[File:1-bit half-adder.svg|class=transparent|vignette|Demi-addtionneur.]]
Nous avons déjà vu comment additionner deux bits dans le chapitre sur les incrémenteurs, mais quelques rappels ne seront pas de trop. Le '''demi-additionneur''' est un circuit qui additionne deux bits. Il implémente la table d'addition, qui est très simple en binaire. Jugez plutôt :
* 0 + 0 = 0, retenue = 0 ;
* 0 + 1 = 1, retenue = 0 ;
* 1 + 0 = 1, retenue = 0 ;
* 1 + 1 = 0, retenue = 1.
Mais un demi-additionneur n'additionne que deux bits, il manque de quoi additionner la retenue. Une solution pour cela est d'enchaîner deux demi-additionneurs : un qui additionne les deux bits de données, et un second qui additionne la retenue au résultat du précédent. La retenue finale se calcule en combinant les sorties de retenue des deux demi-additionneurs, avec une porte OU. Le résultat est un additionneur complet fabriqué avec deux demi-additionneurs. Pour vous en convaincre, établissez la table de vérité de ce circuit, vous verrez que ça marche.
[[File:Full Adder Blocks.svg|centre|vignette|upright=2|Additionneur complet fabriqué avec deux demi-additionneurs.]]
[[File:Half-adder.svg|vignette|upright=1|Circuit d'un demi-additionneur naïf.]]
L'implémentation naïve d'un demi-additionneur utilise une porte XOR et une porte ET, comme illustré ci-contre. Le circuit s'obtient en faisant uen traduction littérale de la table de vérité du circuit. Si je dis qu'elle est naïve, c'est car il est possible de l'optimiser, de manière à éliminer des redondances cachées. Mais nous avions déjà vu cela dans le chapitre sur les incrémenteurs, aussi laissons cela pour plus tard.
En combinant deux demi-additionneurs de ce type, on obtient l'additionneur complet suivant. Les deux sorties de retenue des demi-additionneurs sont combinées avec une porte OU, pour calculer la retenue finale.
{| class="flexible"
|[[File:Full Adder Modules.svg|class=transparent|Composition d'un additionneur complet. On voit bien que celui-ci est composé de deux demi-additionneurs, en rouge et en bleu, auxquels on a ajouté une porte OU pour calculer la retenue finale.]]
|[[File:Full-adder.svg|300px|class=transparent|Circuit d'un additionneur complet.]]
|}
Le fait de combiner les deux retenues avec une porte OU n'est pas ce qu'il y a de plus intuitif. La table de vérité nous dit que ça fonctionne, mais on comprend mal pourquoi. Mais tout devient plus clair quand on sait que les deux retenues ne peuvent pas être à 1 en même temps. Même dans l'addition 1 + 1 + 1, seule une retenue est à 1. Soit l'addition des deux bits d'opérande donne naissance à une retenue, soit c'est l'addition ''résultat + retenue entrante''. La retenue sortant est donc à 1 quand une des deux addition donne une retenue. D'où l'usage d'une porte OU. Remarquez qu'une porte XOR donne le même résultat, vu que le cas où les deux retenues sont à 1 n'est jamais rencontré.
===La propagation et la génération des retenues===
L'additionneur complet que nous allons voir dans cette section sert à introduire les concepts de propagation, génération et absorption de retenue. Il s'agit de concepts très importants quand on étudie les additionneurs. l'idée part d'un principe très simple : la retenue sortante dépend de la retenue d'entrée. La relation entre les deux se résume à trois cas, qui dépendent de la valeur des deux bits additionnés, nommés A et B.
* Dans le premier cas, la retenue entrante est égale à la retenue sortante. On dit que la retenue entrante est propagée par l'additionneur.
* Dans le second cas, la retenue est forcée à 0 : la retenue sortante vaut 0, peu importe la valeur de la retenue entrante.
* Dans le troisième cas, la retenue est forcée à 1 : la retenue entrante vaut 1, peu importe la valeur de la retenue entrante.
[[File:Signaux P et G fournis par un demi-additionneur.png|vignette|Signaux P et G fournis par un demi-additionneur]]
Maintenant, créons un circuit qui nous dise si une retenue est propagée ou générée. Il a deux bits de sortie, nommés P et G : P pour ''Propagate'', G pour ''Generate''. Le bit P est à 1 si la retenue entrante est propagée, il est à 0 sinon. Le bit G est à 1 si une retenue est générée, à 0 sinon. Une retenue est considérée comme absorbée si elle n'est pas ni propagée ni générée, pas besoin d'un troisième bit pour gérer ce cas.
Il se trouve que ce circuit n'est autre qu'un demi-additionneur ! Pour vous en rendre compte, regardez la table de vérité d'un additionneur complet, illustrée ci-dessous. Lorsque les deux bits d'opérande sont à 0, la retenue sortante vaut toujours 0. Si ils sont tous deux à 1, alors la retenue sortante vaut 1. S'ils sont différents, alors retenues sortante et entrante sont égales. Le bit P est donc généré par une simple porte XOR. Quant au bit G, il est à 1 si les deux bits d'opérandes sont à 1, ce qui correspond à une porte ET. Il se trouve que ces deux portes forment un demi-additionneur !
{|class="wikitable"
|-
! Opérande 1 !! Opérande 2 !! Retenue entrante !! !! Retenue sortante
|- class="f_rouge"
||0||0||0|| ||0
|- class="f_rouge"
||0||0||1|| ||0
|- class="f_bleu"
||0||1||0|| ||0
|- class="f_bleu"
||0||1||1|| ||1
|- class="f_bleu"
||1||0||0|| ||0
|- class="f_bleu"
||1||0||1|| ||1
|- class="f_vert"
||1||1||0|| ||1
|- class="f_vert"
||1||1||1|| ||1
|}
Ensuite, créons un circuit qui prend ces deux sorties P et G, et calcule la retenue sortante en fonction. Le circuit en question a trois entrées : la retenue entrante, les deux bits P et G. La retenue sortante vaut 1 soit si une retenue est générée, soit si la retenue entrante est propagée et qu'elle vaut 1. En notant <math>{C_{in}}</math> et <math>{C_{out}}</math> les retenues entrantes et sortante, on a :
: <math>{C_{out}} = G + \left( P . C_{in} \right)</math>
Le circuit est donc composé d'une porte OU et d'une porte ET. En combinant un demi-additionneur avec le circuit de calcul de retenue sortante vu plus haut, on a :
[[File:Additionneur complet avec propagation et génération de retenue.png|class=transparent|centre|vignette|upright=2|Additionneur complet avec propagation et génération de retenue.]]
Certes, on retombe sur le circuit vu plus haut. Mais il est possible de remplacer le circuit en orange par un autre. Par exemple, il est possible de le remplacer par multiplexeur, qui choisit entre la retenue générée et la retenue entrante (propagée). Le choix se fait selon la valeur du bit P, qui chosiit entre propager la retenue et la générer.
[[File:Additionneur crée avec un multiplexeur.png|centre|vignette|upright=1.5|Additionneur crée avec un multiplexeur]]
Le circuit exact est illustré ci-dessous. Il semble utiliser plus de portes logiques que l'additionneur complet précédent. Cependant, nous verrons dans quelques chapitres qu'il est possible d'implémenter un multiplexeur avec seulement 6 transistors, voire moins ! L'implémentation utilise des portes à transmission, mais nous en reparlerons dans le chapitre sur les transistors, quand nous verrons les additionneurs à ''Manchester Carry Chain''. Au passage, une variante de ce circuit a été utilisée dans le processeur processeur 8086 d'Intel, comme on le verra dans le chapitre suivant.
[[File:Additionneur complet basé sur un MUX.png|centre|vignette|upright=1|Additionneur complet basé sur un MUX]]
[[File:FulladderPG.png|vignette|upright=1|Additionneur complet avec deux sorties P et G.]]
Pour finir, sachez qu'il existe des additionneurs qui fournissent : le bit de résultat, deux sorties P et G qui indiquent si l'addition propage ou génère une retenue. Il ne s'agit pas d'additionneurs complets, car il manque de quoi calculer la retenue sortante. De tels additionneurs seront appelés des '''additionneurs partiels'''. De tels additionneurs sont utilisés dans certains additionneurs pour gagner en performance.
Enfin, il existe aussi ce que j'ai décidé d'appeler des '''additionneurs P/G''', qui sont des additionneurs complets auxquels on a ajouté deux sorties P et G, en plus de la sortie de retenue. Leur circuit est le même que celui d'un additionneur complet, auquel on a ajouté deux fils. Vu que ces deux sorties sont fournies par le premier demi-additionneur, ajouter ces deux sorties demande d'ajouter des fils, pas de portes logiques. De tels additionneurs sont aussi utilisés dans certains additionneurs pour gagner en performance.
[[File:FAwithGP.svg|centre|vignette|upright=2|Additionneur complet avec sorties P et G : circuit.Ç]]
===L'additionneur complet basé sur une porte à majorité===
Maintenant, voyons une dernière implémentation possible de l'additionneur complet. Mais avant de voir comment l'additionneur est implémenté, nous allons devoir faire un petit focus sur la retenue sortante. Reprenons la table de vérité, et regardons ce qu'il en est pour la retenue sortante uniquement.
{|class="wikitable"
|-
! Retenue entrante !! Opérande 1 !! Opérande 2 !! !! Retenue sortante
|-
||0||0||0|| ||0
|-
||0||0||1|| ||0
|-
||0||1||0|| ||0
|-
||0||1||1|| ||1
|-
||1||0||0|| ||0
|-
||1||0||1|| ||1
|-
||1||1||0|| ||1
|-
||1||1||1|| ||1
|}
Vous remarquerez que la retenue sortante est égale au bit majoritaire, parmi les trois bits d'opérande. Si deux ou trois bits sont à 1, la retenue sortante vaut 1. Et réciproquement avec 0. Le circuit de calcul de la retenue peut donc être remplacé par une '''porte à majorité'''.
[[File:Additionneur crée avec une porte à majorité.png|centre|vignette|upright=2|Additionneur crée avec une porte à majorité]]
Mais une porte à majorité est assez gourmande en circuit, ce qui fait qu'un additionneur pareil ne serait pas pratique, ni utile. Du moins, ce serait le cas s'il n'y avait pas une possibilité d'optimisation extrêmement intéressante. Il est possible de calculer le 'bit de somme' à partir de la retenue sortante ! En effet, le bit de somme est l'inverse de la retenue sortante, sauf dans deux cas : les trois bits d'entrée sont à 0, où ils sont tous à 1. Le bit de somme vaut 0 dans le premier cas, 1 dans le second cas.
{|class="wikitable"
|-
! Retenue entrante !! Opérande 1 !! Opérande 2 !! !! Retenue sortante !! Bit de somme
|- class="f_bleu"
||0||0||0|| ||0||0
|- class="f_vert"
||0||0||1|| ||0||1
|- class="f_vert"
||0||1||0|| ||0||1
|- class="f_vert"
||0||1||1|| ||1||0
|- class="f_vert"
||1||0||0|| ||0||1
|- class="f_vert"
||1||0||1|| ||1||0
|- class="f_vert"
||1||1||0|| ||1||0
|- class="f_rouge"
||1||1||1|| ||1||1
|}
En clair, 6 lignes de la table de vérité sur 8 peuvent se calculer avec une porte à majorité, le reste demandant quelques portes logiques pour faire la correction. L'implémentation en circuit demande donc trois choses :
* de calculer la retenue sortante et de quoi l'inverser ;
* un circuit qui vérifie si tous les bits opérande valent 0 : une porte NOR ou OU fait l'affaire ;
* un circuit qui vérifie s'ils valent tous 1, à savoir une porte ET.
Ensuite, on combine le résultat des trois circuits précédents pour obtenir le résultat final. La combinaison est le fait du circuit en jaune dans le schéma ci-dessous.
[[File:Full adder basé sur une modification de la retenue.png|centre|vignette|upright=2|Full adder basé sur une modification de la retenue]]
Le circuit en jaune est assez simple. Quand la porte ET sort un 1, la sortie doit être forcée à 1. Par contre, quand la porte OU sort un zéro, la sortie doit être forcée à 0. Il suffit donc d'enchainer un circuit de mise à 1 et un circuit de mise à 0. Nous avons vu dans le chapitre sur les opérations de masquage qu'il s'agit respectivement d'une porte OU et d'une porte ET.
[[File:Full adder basé sur une modification de la retenue interne, au complet.png|centre|vignette|upright=2|Full adder basé sur une modification de la retenue interne, au complet]]
Divers processeurs utilisaient des additionneurs complets de ce type : le fameux Z80, l'Intel 4004 et de l'Intel 8008 sont les exemples les plus notables. Pourtant, on pourrait s'étonner qu'un tel circuit ait existé. Il utilise beaucoup de portes logiques, a une profondeur logique supérieure : il n'a rien d'avantageux. Sauf qu'il était utilisé sur d'anciens processeurs, qui utilisaient la technologie dite TTL, différente de la technologie CMOS des transistors modernes. Et avec la technologie TTL, il est possible de fusionner plusieurs portes logiques ET et NOR en une seule porte logique ET/OU/NON ! Un additionneur complet construit ainsi ne prenait que deux portes logiques : une pour le calcul de la retenue sortante, une autre pour le reste du circuit.
==L'addition non signée==
Voyons maintenant un circuit capable d'additionner deux nombres entiers: l''''additionneur'''. Dans la version qu'on va voir, ce circuit manipulera des nombres strictement positifs. L'addition des nombres codés en complètement à deux sera vu dans une section ultérieure.
[[File:Full-adder-4bits-serial-CEI.svg|vignette|upright=0.5|Additionneur 4 bits, un bloc.]]
L'interface d'un additionneur est illustrée ci-contre. Un additionneur prend deux opérandes sur deux entrées séparées, et fournit le résultat sur une sortie dédiée. De plus, il a une entrée sur laquelle envoyer une retenue entrante, et a une sortie pour la retenue sortante. La sortie de retenue indique que le résultat de l'addition a débordé (au sens d'un débordement d'entier). Pour l'entrée de retenue, son utilité deviendra plus claire dans ce qui suit, mais souvenez-vous qu'elle existe.
L'entrée et la sortie de retenue sont parfois utilisées pour combiner plusieurs petits additionneurs, pour former un additionneur plus grand. Nous verrons de nombreux exemples dans ce qui suit. Il est assez commun de créer des additionneurs pour des opérandes de 16/32/64 bits, avec des additionneurs de 4/5 bits.
===L'additionneur série===
Il est possible d'additionner deux nombres bit par bit, grâce à un additionneur complet associé à plusieurs registres à décalages. Les opérandes sont placées chacune dans un registre à décalage, et l'additionneur complet utilise les bits sortants de ces registres à décalage. Le bit du résultat est envoyé au registre à décalage pour le résultat. La retenue de l'addition est stockée dans une bascule de 1 bit, en attente du prochain cycle d'horloge. Un tel additionneur est appelé un '''additionneur série'''. Il a été utilisé sur d'anciens ordinateurs dans les années 50-60, aussi bien des prototypes que des ordinateurs commerciaux.
[[File:Additionneur série.jpg|centre|vignette|upright=2.5|Additionneur série.]]
===L'additionneur à propagation de retenue===
L''''additionneur à propagation de retenue''' est aussi appelé l'additionneur ''ripple carry'', c'est son nom anglais. J'utiliserais occasionnellement le nom anglais dans ce qui suit, car celui-ci est plus court. Ilutilise un additionneur complet pour chaque colonne de l'addition. Pour gérer les retenues, il suffit ainsi de câbler des additionneurs complets les uns à la suite des autres, chacun envoyant sa retenue à la colonne suivante.
[[File:Ripplecarryadder.png|centre|vignette|upright=2.5|Additionneur à propagation de retenue.]]
Notons la présence de la retenue sortante, qui est utilisée pour détecter les débordements d'entier, ainsi que pour d'autres opérations. Le bit de retenue final est souvent stocké dans un registre spécial du processeur (généralement appelé ''carry flag'').
Notez aussi, à droite du schéma précédent, l'entrée de retenue <math>\text{R}_\text{Entrée}</math>. Elle est très utile pour l'implémentation de certaines opérations comme l'inversion de signe, la soustraction, l'incrémentation, etc. Certains processeurs sont capables de faire une opération appelée ADC, ADDC ou autre nom signifiant ''Addition with Carry'', qui permet de faire le calcul A + B + Retenue (la retenue en question est la retenue sortante de l'addition précédente, stockée dans le registre ''carry flag''). Son utilité principale est de permettre des additions d'entiers plus grands que ceux supportés par le processeur. Par exemple, cela permet de faire des additions d'entiers 32 bits sur un processeur 16 bits.
[[File:Adder Network 3 Bit Sum Delay 003.svg|vignette|upright=0.5|Propagation de retenue dans l'additionneur.]]
Cet additionneur utilise très peu de portes logiques, ce qui fait qu'il était utilisé sur certains processeurs 8 et 16 bits, dont le budget en portes logiques était limité. Bien que très simple, cet additionneur est cependant peu performant. Son temps de calcul est proportionnel à la taille des opérandes. Par exemple, additionner deux opérandes de 32 bits prendra deux fois plus de temps que pour des opérandes de 16 bits. La raison est que les retenues doivent se propager à travers le circuit, du premier additionneur jusqu'au dernier.
L'addition étant une opération fréquente, d'autres additionneurs ont été inventés. Ils utilisent des optimisations qui utilisent plus de circuits pour gagner en rapidité. Mais avant de voir ces optimisations, nous allons voir s'il est possible d'optimiser les additionneurs à propagation de retenue.
===L'additionneur à saut de retenue===
L'additionneur à propagation de retenue peut être optimisé de deux manières. La première accélère la propagation de la retenue, en agissant au niveau des additionneurs complets. La ''Manchester carry chain'' est une optimisation de ce type, mais on ne peut pas encore expliquer à ce stade du cours, car elle optimisait les additionneurs complets au niveau des transistors eux-mêmes. Elle a été utilisée sur de nombreux processeurs connus, comme le 8086 d'Intel ou le Z80.
La seconde manière rend l'addition plus rapide dans certains cas. Le résultat est un additionneur dont le temps de calcul est variable. Le calcul prendra quelques cycles d'horloges avec certains opérandes, mais d'autres opérandes ne verront aucune amélioration. Il n'améliore pas le pire des cas, dans lequel la retenue doit être propagée du début à la fin, du bit de poids faible au bit de poids fort. Mais dans d'autres cas, il permet d'avoir le résultat en avance. L'optimisation en question donne l''''additionneur à saut de retenue''' (''carry-skip adder'').
Il est composé en enchainant plusieurs additionneurs plus petits, qui additionnent 4/5 bits, rarement plus. De tels additionneurs de petite taille sont appelés des '''blocs''', dans ce qui suit. L'idée est de détecter précocement si la retenue entrante est propagée à travers tout le bloc. Si la retenue est propagée dans tous le bloc, on peut directement l'envoyer sur la sortie de retenue, la retenue saute le bloc entier. Dans le cas contraire, on doit calculer la retenue normalement. Le choix entre les deux est le fait d'un multiplexeur. Toute la difficulté est de savoir comment commander le multiplexeur.
[[File:Carry skip adder, principe de base.png|centre|vignette|upright=2.5|Carry skip adder : principe de base]]
Pour savoir si une retenue est propagée dans un bloc entier, il faut utiliser des additionneurs complets, qui ont en plus une sortie P indiquant que l'additionneur propage la retenue. Le bloc propage une retenue si chaque additionneur complet propage la retenue, donc s'ils ont tous leur sortie P à 1. Un simple ET logique suffit à déterminer si c'est le cas. Le signal de commande du multiplexeur est donc un vulgaire ET entre toutes les sorties P des additionneurs complets du bloc.
[[File:Additionneur à saut de retenue.png|centre|vignette|upright=2.5|Calcul de la commande du MUX.]]
Voici ce que ça donne pour un bloc de 4 bits :
[[File:CSAdder4Bit.svg|centre|vignette|upright=1.5|Additionneur ''carry skip'' : bloc de 4 bits.]]
L'additionneur à saut de retenue est construit en assemblant plusieurs blocs de ce type.
[[File:BCSAdder16Bit.svg|centre|vignette|upright=3|Additionneur à saut de retenue.]]
Les blocs sont tous identiques dans le cas le plus simple, mais il est possible d'utiliser des blocs de taille variable. Par exemple, le premier bloc peut avoir des opérandes de 6 bits, le second des opérandes de 7 bits, etc. Faire ainsi permet de gagner un petit peu en performances, si la taille de chaque bloc est bien choisie. La raison est une question de temps de propagation des retenues. La retenue met plus de temps à se propager à travers 8 blocs qu'à travers 4, ce qui prend plus de temps qu'à travers 2 blocs, etc. En tenir compte fait que la taille des blocs tend à augmenter ou diminuer quand on se rapproche des bits de poids fort.
===L'additionneur à sélection de retenue===
L''''additionneur à sélection de retenue''' utilise aussi des blocs, comme les additionneurs précédents. L'addition se fait en deux versions : une avec la retenue du bloc précédent valant zéro, et une autre version avec la retenue du bloc précédent valant 1. Il suffira alors de choisir le bon résultat avec un multiplexeur, une fois cette retenue connue. On gagne ainsi du temps en calculant à l'avance les valeurs de certains bits du résultat, sans connaître la valeur de la retenue. Petit détail : sur certains additionneurs à sélection de retenue, les blocs de base n'ont pas la même taille. Cela permet de tenir compte des temps de propagation des retenues entre les blocs.
[[File:Additionneur à sélection de retenue.png|centre|vignette|upright=1.5|Additionneur à sélection de retenue avec seulement deux blocs.]]
Dans les exemples du dessus, chaque sous-additionneur étaient des additionneurs à propagation de retenue. Mais ce n'est pas une obligation, et tout autre type d’additionneur peut être utilisé. Par exemple, on peut faire en sorte que les sous-additionneurs soient eux-mêmes des additionneurs à sélection de retenue, et poursuivre ainsi de suite, récursivement. On obtient alors un '''additionneur à somme conditionnelle''', plus rapide que l'additionneur à sélection de retenue, mais qui utilise beaucoup plus de portes logiques.
===Les additionneurs à anticipation de retenue===
Les '''additionneurs à anticipation de retenue''' calculent chaque retenue sans avoir à propager les retenues précédentes. Au lieu de calculer les retenues une par une, ils calculent toutes les retenues en parallèle, à partir des bits des opérandes. Une fois les retenues pré-calculées, il suffit de les additionner avec les deux bits adéquats, pour obtenir le résultat. Ces additionneurs sont composés de deux parties :
* un circuit qui pré-calcule les retenues, sans les propager, directement à partir des opérandes ;
* d'une couche d'additionneurs complets, qui additionnent chacun deux bits d'opérande et la retenue pré-calculée.
[[File:Additionneur à anticipation de retenue.png|centre|vignette|upright=1.5|Additionneur à anticipation de retenue.]]
Le calcul des retenues se fait en deux étapes. La première détermine si les retenues sont propagées ou générées, pour chaque colonne. Elle détermine, pour chaque colonne, les bits P et G qui indiquent respectivement : qu'une retenue a été générée sur cette colonne, que la colonne propage la retenue précédente. La seconde étape utilise ces signaux P et G pour déterminer la retenue sur chaque colonne. Le circuit de calcul des retenues est donc composé de deux parties : une couche de demi-additionneurs pour générer les signaux P et G, un circuit de calcul des retenues proprement dit.
[[File:Circuit complet d'un additionneur à anticipation de retenue.png|centre|vignette|upright=1.5|Circuit complet d'un additionneur à anticipation de retenue.]]
Pour rappel, la retenue de la colonne i, notée <math>C_i</math> est égale à :
: <math>G_i + (P_i . C_{i-1})</math>, avec G le signal de génération de retenue, P le signal de propagation de retenue.
Si on utilisait cette formule sans trop réfléchir, on retomberait sur un additionneur à propagation de retenue inutilement compliqué. L'astuce des additionneurs à anticipation de retenue consiste à remplacer le terme Ci−1 par sa valeur calculée avant. Par exemple, je prends un additionneur 4 bits. Je dispose de deux nombres A et B, contenant chacun 4 bits : A3, A2, A1, et A0 pour le nombre A, et B3, B2, B1, et B0 pour le nombre B. Si j'effectue les remplacements, j'obtiens les formules suivantes :
* C1 = G0 + ( P0 · C0 ) ;
* C2 = G1 + ( P1 · G0 ) + ( P1 · P0 · C0 ) ;
* C3 = G2 + ( P2 · G1 ) + ( P2 · P1 · G0 ) + ( P2 · P1 · P0 · C0 ) ;
* C4 = G3 + ( P3 · G2 ) + ( P3 · P2 · G1 ) + ( P3 · P2 · P1 · G0 ) + ( P3 · P2 · P1 · P0 · C0 ).
Ces formules nous permettent de déduire la valeur d'une retenue directement : il reste alors à créer un circuit qui implémente ces formules, et le tour est joué.
[[File:Four bit adder with carry lookahead.svg|centre|vignette|upright=1.5|Additionneur à anticipation de retenue de 4 bits.]]
Plus haut, j'ai dit que le circuit était composé de trois couches : une couche de demi-additionneur pour les signaux P et G ; un circuit d'anticipation de retenue, puis l'addition finale. Mais les deux bits d'opérandes sont déjà additionnés dans la couche de demi-additionneur, autant réutiliser le résultat pour l'additionner à la retenue. Pour éliminer toute redondance de ce type, il est possible d'utiliser des additionneurs partiels (des additionneurs où la sortie de retenue a été remplacée par deux sorties P et G). L'additionneur final est alors composé de plusieurs additionneurs complets de ce type, couplé à un circuit d'anticipation de retenue.
[[File:Cla4bits.png|centre|vignette|upright=2.5|Additionneur à anticipation de retenue de 4 bits.]]
Les additionneurs à anticipation de retenue sont plus rapides que les additionneurs à propagation de retenue. La raison est que les retenues sont calculées rapidement, dans un temps plus petit qu'en propageant la retenue. Pour rentrer dans les détails mathématiques, le temps de calcul n'est pas proportionnel au nombre de bits, mais proportionnel au logarithme de ce nombre de bits. Par contre, le gain en temps de calcul s'accompagne d'un cout en portes logiques conséquent. Le circuit qui calcule les retenues anticipées utilise beaucoup de portes logiques.
===Les additionneurs à calcul parallèle de préfixe===
Du fait du cout en portes logiques, utiliser un additionneur à anticipation de retenue est une solution pratique pour additionner 4 ou 8 bits. Mais pour des opérandes plus grands, de 16 ou 32 bits, cela utiliserait trop de portes logiques. Une partie de ce cout en portes logiques est cependant lié à des redondances. Si on analyse deux retenues différentes, à des colonnes différentes. Les circuits qui calculent ces deux retenues seront différents, mais ils contiendront des sous-circuits identiques. Pour vous en rendre compte, reprenons les quatre équations obtenues précédemment et essayez de trouver des redondances :
* C1 = G0 + ( P0 · C0 ) ;
* C2 = G1 + ( P1 · G0 ) + ( P1 · P0 · C0 ) ;
* C3 = G2 + ( P2 · G1 ) + ( P2 · P1 · G0 ) + ( P2 · P1 · P0 · C0 ) ;
* C4 = G3 + ( P3 · G2 ) + ( P3 · P2 · G1 ) + ( P3 · P2 · P1 · G0 ) + ( P3 · P2 · P1 · P0 · C0 ).
Vous devriez trouver :
* le terme ''P0 · C0'' dans toutes les lignes ;
* les termes ''P1 · P0 · C0'' et ''P1 · P0'' dans les trois dernières lignes ;
* le terme ''P2 · P1 · P0 · C0'' ; ''P2 · P1 · G0'' et ''P2 · G1'' dans les deux dernières lignes.
Il existe plusieurs manières d'exploiter de telles redondances, exploitées dans les '''additionneurs à calcul parallèle de préfixe'''. L'optimisation apportée est de générer des signaux ''propagate'' et ''generate'' pour des groupes de 2, 3, 4, ..., N colonnes. Par exemple, il est possible de générer un signal ''P 0 vers 3'', qui indique si la retenue de la première colonne est propagée jusqu'à la 4ème colonne. Un autre exemple est un signal qui indique qu'une retenue a été générée entre les colonnes 4 à 7 génèrent une retenue ou non. En clair, les signaux P et G ont maintenant un intervalle, qui précise de quelle colonne vers quelle colonne se fait la propagation, ou entre quelles colonnes se fait la génération.
Il est possible de combiner les signaux P et G de deux groupes de colonne, s'ils sont contiguës (ils peuvent aussi se recouvrir). Par exemple, il est possible de calculer les bits P et G pour les colonnes 0 à 10, à partir des deux signaux P/G des colonnes 0-4 et 5-10. Pour cela, les équations sont assez simples. Si je prends deux groupes nommés A et B, avec A pour les colonnes de poids fort et B celles de poids faible, on a :
: <math>P_{sortie} = P_A . P_B</math>
: <math>G_{sortie} = G_A . P_A . G_B</math>
Pour les bits des colonnes 2, 4, 8, 16, 32, 64, et autres, le calcul est simple. L'idée est de grouper les colonnes par groupes de 2. Puis, on calcule chaque groupe de 4 bits à partir de deux groupes de 2. Puis les groupes de 8 bits sont calculés à partir de deux groupes de 4 bits, et ainsi de suite pour toute puissance de deux.
[[File:Squelette d'un additionneur à calcul parallèle de préfixe.jpg|centre|vignette|upright=2|Squelette d'un additionneur à calcul parallèle de préfixe]]
Les seules difficultés surviennent pour les colonnes dont le nombre n'est pas une puissance de deux. Il y a plusieurs manières pour subdiviser ces colonnes en intervalles plus petits et combiner le tout. Et elles donnent chacune des additionneurs différents. Ils portent les noms d''''additionneur de Ladner-Fisher''', d''''additionneur de Brent-Kung''', d''''additionneur de Kogge-Stone''', etc. Dans le cas général, l'additionneur de Kogge-Stone est un des plus rapide, mais aussi un des plus gourmand en circuits, alors que c'est l'inverse pour l'additionneur de Brent-Kung. Les deux additionneurs sont illustrés ci-dessous, pour des opérandes de 8 bits. Dans ceux-ci, chaque losange calculent les bits P et G, à partir des signaux P et G de deux groupes précédents.
{|
|[[File:Brent-kung-8-bit.png|vignette|Brent-kung-8-bit]]
|[[File:Kogge-stone-8-bit.png|vignette|upright=1.2|Kogge-stone-8-bit]]
|}
===Le calcul parallèle de la retenue===
L'anticipation de retenue est très rapide, mais utilise beaucoup de circuits, même en utilisant le calcul parallèle de préfixe. Les additionneurs à propagation de retenue font eux le compromis inverse. Mais il existe des intermédiaires, qui visent à obtenir un compromis entre performance et cout en portes logiques.
Un de ces compromis découpe un additionneur de 16/32/64 bits en additionneurs de 4/5 bits, qui sont enchainés en utilisant la propagation de retenue. Par contre, ces petits additionneurs de 4/5 bits utilisent l'anticipation de retenue. Ces additionneurs simples seront nommés ''blocs'' dans ce qui suit. Par exemple, on peut utiliser 4 additionneurs à anticipation de retenue de 4 bits, et propager les retenues entre eux. Le premier bloc calcule une retenue, qui est propagée au second bloc. Le second bloc calcule lui aussi une retenue, qui est propagée au troisième bloc, etc.
[[File:4008 Functional Diagram.svg|vignette|upright=1|4008 Functional Diagram]]
Le gain en performance est significatif, mais il est possible de faire presque aussi rapide, tout en économisant beaucoup de circuits. L'idée est de simplifier les additionneurs 4 bits, afin de ne pas utiliser une anticipation de retenue complète. L'anticipation de retenue n'est utilisée que pour la retenue sortante, alors que les bits de somme sont calculées avec propagation de retenue. La retenue sortante est donc calculée en parallèle de l'addition. Chaque bloc contient, à côté d'un additionneur proprement dit, un circuit qui calcule la retenue sortante. Il existe de nombreuses manières de calculer la retenue sortante.
Le gain en portes logiques est assez significatif. En effet, on se passe de circuits d'anticipation de retenue pour les 4 bits de somme, on ne garde que celui pour la retenue sortante. Le fait d'utiliser la propagation de retenue pour calculer les 4 bits de somme est censé avoir un léger cout en performance, mais il se trouve que ce cout est très faible. En effet, le résultat n'est connu qu'une fois les retenues propagées jusqu'au dernier bloc. Et malgré l'anticipation des retenues, la propagation est assez lente. les additionneurs à propagation de retenue auront terminé leur travail bien avant.
Un exemple est celui de l'additionneur CMOS 4008, un additionneur de 4 bit. Il est composé en trois sections. Une première couche de demi-additionneurs calcule les signaux P et G utilisés à la fois pour l'anticipation de la retenue sortante, que pour calculer les bits de somme. Le circuit de calcul de la retenue sortante utilise l'anticipation de retenue. Le reste du circuit fait l'addition en propageant les retenues.
[[File:CMOS 4008, circuit découpé en sections.png|centre|vignette|upright=1.5|CMOS 4008, circuit découpé en sections]]
===Les additionneurs à anticipation de retenue multi-niveau===
Les additionneurs précédents mixent anticipation et propagation de retenue. Ceux que nous allons voir utilisent des blocs, mais utilisent l'anticipation de retenue entre les blocs. Les blocs utilisés peuvent être un additionneur à propagation de retenue, ou à anticipation de retenue, peu importe. Ce qui compte est que la retenue entrante de chaque bloc est calculée par anticipation de retenue.
Par exemple, il est possible de créer un additionneur 16 bits en utilisant 4 additionneurs/blocs de 4 bits, couplé à un circuit d’anticipation de retenue par bloc. Pour cela, les additionneurs de 4 bits doivent fournir deux sorties P et G. Les deux sorties indiquent que le bloc soit a généré une retenue, soit a propagé la retenue entrante. Par propagé, on veut dire que la retenue entrante du bloc s'est retrouvée sur la sortie de retenue du bloc. C'est le cas si les 4 colonnes propagent la retenue, ce qui signifie que :
: <math>P_\text{4 bits} = P_0 \cdot P_1 \cdot P_2 \cdot P_3</math>
Pour la génération, cela veut dire qu'une colonne a générée une retenue, qui a été propagée jusqu'à la sortie. En clair, pour chaque colonne, il faut que le G = 1, et que les signaux P des colonnes soient à 1. Pour les 4 colonnes, cela donne :
: <math>G_\text{4 bits} = G_3 + G_2 \cdot P_3 + G_1 \cdot P_3 \cdot P_2 + G_0 \cdot P_3 \cdot P_2 \cdot P_1</math>
[[File:Cla4bitsPG.png|centre|vignette|upright=2|Additionneur à anticipation de retenue de 4 bits.]]
Pour combiner 4 blocs de 4 bits, on devra donc combiner 4 bits P et G, avec un circuit d'anticipation de retenue.
[[File:16-bit lookahead carry unit.svg|centre|vignette|upright=2|Additionneur à anticipation de retenue de 16 bits.]]
Il est même possible d'aller plus loin et de combiner 4 additionneurs précédents pour obtenir un additionneur 64 bits.
[[File:64-bit lookahead carry unit.svg|centre|vignette|upright=2|Additionneur à anticipation de retenue de 64 bits.]]
Le résultat est un additionneur à plusieurs niveaux d’anticipation de retenue. Une anticipation de retenue dans un blocs de 4 bits (facultative), entre blocs de 4 bits, entre blocs de 16 bits. La même logique peut être utilisée avec des blocs de taille différente de 4, 16 et 64 bits.
==L'addition signée et la soustraction==
Après avoir vu l'addition, il est logique de passer à la soustraction, les deux opérations étant très proches. Si on sait câbler une addition entre entiers positifs, câbler une soustraction n'est pas très compliqué. De plus, la soustraction permet de faire des additions de nombres signés.
===Le soustracteur à propagation de retenue===
Pour soustraire deux nombres entiers, on peut adapter l'algorithme de soustraction utilisé en décimal, celui que vous avez appris à l'école. Celui-ci ressemble fortement à l'algorithme d'addition : on soustrait les bits de même poids, et on propage éventuellement une retenue sur la colonne suivante. À la différence de l'addition, la retenue est soustraite, et non ajoutée. En clair, une soustraction en binaire demande de faire deux soustraction : pour chaque bit de la première opérande, on soustrait le bit associé de la seconde, puis on soustrait la retenue entrante.
[[File:Algebra1 05 fig014.svg|centre|vignette|Soustraction en binaire, avec les retenues en rouge.]]
[[File:Half Subtractor Vektor.svg|vignette|Demi-soustracteur.]]
Dans le chapitre sur les incrémenteurs/décrémenteurs, nous avons vu un circuit qui soustrait deux bits, appelé le '''demi-soustracteur'''. Il ressemble beaucoup à un demi-additionneur, les différences se résumant à une porte NON ajoutée pour le calcul de la retenue. Il implémente la table de soustraction, qui est très simple en binaire :
* 0 - 0 = 0 ;
* 0 - 1 = 1 et une retenue ;
* 1 - 0 = 1 ;
* 1 - 1 = 0.
Comme pour les additionneurs, deux demi-soustracteurs peuvent être combinés pour donner un '''soustracteur complet'''. Le calcul de la retenue se fait en combinant les deux retenues des demi-soustracteurs avec une porte OU. Là encore, si on regarde les sorties des deux demi-soustracteurs, il est impossible d'avoir les deux sorties de retenues à 1 en même temps.
[[File:Full Subtractor logic diagram.png|centre|vignette|upright=2|Soustracteur complet.]]
[[File:FullSubtractor.svg|centre|vignette|upright=2|Soustracteur complet.]]
Le circuit précédent peut se simplifier pour ne garder qu'une seule porte NON.
[[File:Full subtractor.svg|centre|vignette|upright=2|Soustracteur complet, simplifié.]]
[[File:Sub Network 3 Bit Step 002.svg|vignette|upright=1|Soustracteur à propagation de retenue pour opérandes de 3 bits.]]
Les soustracteurs complets sont utilisés pour créer des soustracteurs à propagation de retenue, sur le même modèle que les additionneurs. Et je précise bien "à propagation de retenue", car l'anticipation de retenue et les autres optimisations ne marchent pas exactement pareil entre addition et soustraction. Les signaux de propagation et de génération de retenue ne sont pas générés pareil, idem pour la manière de les combiner.
Il est possible de fusionner un additionneur à propagation de retenue et un soustracteur de même type. Pour cela, il suffit de remarquer que les demi-soustracteurs et les demi-additionneurs se ressemblent beaucoup. La seule différence entre les deux est une vulgaire porte NON. En remplaçant cette porte NON par un inverseur commandable, on obtient un circuit qui sert soit de demi-additionneur, soit de demi-soustracteur, suivant ce qu'on envoie sur l'entrée de commande. Ci-dessous est illustré ce que donne ce remplacement pour un soustracteur complet. Le résultat est un '''additionneur/soustracteur à propagation de retenue'''.
[[File:Additionneur-soustracteur complet.png|centre|vignette|upright=2|Additionneur-soustracteur complet, naïf.]]
Il est donc possible de fusionner un additionneur avec un soustracteur, ce qui donne un '''additionneur/soustracteur'''. Mais comme je l'ai dit plus haut, cela ne marche que si l'''additionneur-/soustracteur'' utilise la propagation de retenue. Dès qu'on souhaite utiliser l'anticipation de retenue ou tout autre technique, les choses sont plus compliquées. Le calcul des retenues étant différent entre addition et soustraction, mutualiser les circuits d'anticipation de retenue pour les deux opérations ne donnera pas un résultat très propre. Ce qui n'est pas le cas avec les solutions que nous allons voir dans ce qui suit.
===L'additionneur-soustracteur pour opérandes codées en complément à deux===
Étudions le cas de la soustraction en complément à deux. Vous savez sûrement que a−b et a+(−b) sont deux expressions équivalentes. Et en complément à deux, − b = not(b) + 1. Dit autrement, a − b = a + not(b) + 1. On pourrait se dire qu'il faut deux additionneurs pour faire le calcul, mais la majorité des additionneurs possède une entrée de retenue pour incrémenter le résultat de l'addition. Un soustracteur en complément à deux est donc simplement composé d'un additionneur et d'un inverseur.
[[File:Additionneur soustracteur.png|centre|vignette|upright=2|Soustracteur en complément à deux.]]
Il est possible de créer un circuit capable d'effectuer soit une addition, soit une soustraction : il suffit de remplacer l'inverseur par un inverseur commandable, qui peut être désactivé. On a vu comment créer un tel inverseur commandable dans le chapitre sur les circuits combinatoires. On peut remarquer que l'entrée de retenue et l'entrée de commande de l'inverseur sont activées en même temps : on peut fusionner les deux signaux en un seul.
[[File:Additionneur-soustracteur en complément à deux.png|centre|vignette|upright=2|Additionneur-soustracteur en complément à deux.]]
Une implémentation alternative est la suivante. Elle remplace l'inverseur commandable par un multiplexeur.
[[File:4-bit ripple carry adder-subtracter.svg|centre|vignette|upright=2|Additionneur-soustracteur en complément à deux, version alternative.]]
===L'additionneur-soustracteur pour opérandes codées en signe-magnitude===
Passons maintenant aux nombres codés en signe-valeur absolue, les deux opérandes étant notées A et B. Suivant les signes des deux opérandes, on a quatre cas possibles : A + B, A − B (B négatif), −A + B (A négatif) et −A − B (A et B négatifs). Une astuce est que le circuit n'a besoin que de calculer A + B et A − B : il peut les inverser pour obtenir − A − B ou B − A. A + B et A − B peuvent se calculer avec un additionneur-soustracteur, reste à corriger le résultat. Il suffit de lui ajouter un inverseur commandable pour obtenir le circuit d'addition finale.
[[File:Additionneur en signe-valeur absolue.png|centre|vignette|upright=2|Additionneur en signe-valeur absolue.]]
Toute la difficulté tient dans le calcul du bit de signe du résultat, quand interviennent des soustractions. Autant l'addition de deux nombres de même signe (A + B et −A − B) ne pose aucun problème, autant les soustractions posent problème (A − B et −A + B). Suivant que <math>A<B</math> ou que <math>A>B</math>, le signe du résultat ne sera pas le même. Déterminer le signe du résultat se fait en regardant les bits de débordement d'entier, comme on le verra plus bas.
===L'additionneur-soustracteur pour opérandes codées en représentation par excès===
Passons maintenant aux nombres codés en représentation par excès. On pourrait croire que ces nombres s'additionnent comme des nombres non-signés, mais ce serait oublier la présence du biais, qui pose problème. Dans les cas de nombres signés gérés avec un biais, voyons ce que donne l'addition de deux nombres :
: <math>( n_1 + biais ) + ( n_2 + biais ) = n_1 + n_2 + 2 \times biais</math>
Or, le résultat correct serait :
: <math>n_1 + n_2 + biais</math>
En effectuant l'addition telle quelle, le biais est compté deux fois. On doit donc le soustraire après l'addition pour obtenir le résultat correct.
Même chose pour la soustraction qui donne ceci :
: <math>(n_1 + biais) - (n_2 + biais) = n_1 - n_2</math>
Or, le résultat correct serait :
: <math> ( n_1 - n_2 ) + biais </math>
Il faut rajouter le biais pour obtenir l'exposant correct.
On a donc besoin de deux additionneurs/soustracteurs : un pour additionner/soustraire les représentations binaires des opérandes, et un autre pour ajouter/retirer le biais en trop/manquant.
==L'additionneur BCD==
Maintenant, voyons un additionneur qui additionne deux entiers au format BCD. Pour cela, nous allons devoir passer par deux étapes. La première est de créer un circuit capable d'additionneur deux chiffres BCD. Ensuite, nous allons voir comment enchaîner ces circuits pour créer un additionneur BCD complet.
===L'additionneur BCD qui fait l'opération chiffre par chiffre===
Nous allons commencer par voir un additionneur qui additionne deux chiffres en BCD, une sorte d'équivalent BCD de l'additionneur complet. Il fournit un résultat sur 4 bits et une retenue qui est mise à 1 si le résultat dépasse 10 (la limite d'un chiffre BCD). Les deux opérandes sont des chiffres BCD codés sur 4 bits et sont additionnés en binaire par un additionneur des plus normaux, similaire à ceux vus plus haut. Le résultat est alors un entier codé en binaire, sur 5 bits, qu'on corrige/convertit pour obtenir un chiffre BCD et une retenue sortante.
Pour corriger le résultat, une idée intuitive serait de prendre le résultat et de faire une division par 10. Le quotient donne la retenue, alors que le reste est le résultat, le chiffre BCD. Mais un circuit diviseur par 10 utilise beaucoup de portes logiques, ce qui ne vaut pas le coup. Une autre méthode détecte si le résultat est égal ou supérieur à 10, ce qui correspond à un "débordement" (on dépasse les limites d'un chiffre BCD). Si le résultat est plus petit que 10, il n'y a rien à faire : le résultat est bon et la retenue est de zéro. Par contre, si le résultat vaut 10 ou plus, il faut corriger le résultat et générer une retenue à 1.
Il faut donc ajouter un circuit qui détecte si le résultat est supérieur à 9, qui calcule directement la retenue. Ce circuit peut se fabriquer simplement à partir de sa table de vérité, ou en utilisant les techniques que nous verrons dans un chapitre ultérieur sur les comparateurs. La solution la plus simple est clairement d'utiliser la table de vérité, ce qui est très simple, assez pour être laissé en exercice au lecteur. Pour comprendre comment corriger le résultat, établissons une table de vérité qui associe le résultat et le résultat corrigé. L'entrée vaut au minimum 10 et au maximum 9 + 9 = 18. On considère la sortie comme un tout, la retenue étant un 5ème bit, le bit de poids fort.
{|class="wikitable"
|-
! colspan="5" | Entrée
!
! rowspan="10" |
! Retenue
! Résultat corrigé (sans retenue)
! interprétation de la sortie en binaire (retenue inclue)
|-
| 0 || 1 || 0 || 1 || 0
| (10)
| 1 || 0000
| (16)
|-
| 0 || 1 || 0 || 1 || 1
| (11)
| 1 || 0001
| (17)
|-
| 0 || 1 || 1 || 0 || 0
| (12)
| 1 || 0010
| (18)
|-
| 0 || 1 || 1 || 0 || 1
| (13)
| 1 || 0011
| (19)
|-
| 0 || 1 || 1 || 1 || 0
| (14)
| 1 || 0100
| (20)
|-
| 0 || 1 || 1 || 1 || 1
| (15)
| 1 || 0101
| (21)
|-
| 1 || 0 || 0 || 0 || 0
| (16)
| 1 || 0110
| (22)
|-
| 1 || 0 || 0 || 0 || 1
| (17)
| 1 || 0111
| (23)
|-
| 1 || 0 || 0 || 1 || 0
| (18)
| 1 || 1000
| (24)
|}
En analysant le tableau, on voit que pour corriger le résultat, il suffit d'ajouter 6. La raison est que le résultat déborde d'un nibble à 16 en binaire, mais à 10 en décimal : il suffit d'ajouter la différence entre les deux, à savoir 6, et le débordement binaire fait son travail. Donc, la correction après une addition est très simple : si le résultat dépasse 9, on ajoute 6.
On peut maintenant implémenter l'additionneur BCD, en combinant le comparateur avec 10, le circuit de correction, et l'additionneur. La première solution calcule deux versions du résultat : la version corrigée, la version normale. Le choix entre les deux est réalisée par un multiplexeur, commandé par le comparateur.
[[File:Additionneur BCD.png|centre|vignette|upright=1.5|Additionneur BCD]]
L'autre solution utilise un circuit commandable qui soit additionne 6, soit ne fait rien. Le choix entre les deux est commandé par le bit calculé par le comparateur.
[[File:Additionneur BCD, seconde version.png|centre|vignette|upright=1.5|Additionneur BCD, seconde version.]]
Une version alternative du circuit précédent est la suivante. Il contient deux additionneurs : un pour additionner les deux chiffres BCD, un autre pour additionner 6 si besoin. Le résultat du comparateur est directement utilisé pour générer l'opérande du second additionneur : 0 ou 6. Le circuit est simple à concevoir, mais gaspille beaucoup de circuit. Idéalement, il vaudrait mieux utiliser un circuit combinatoire d'addition avec une constante.
[[File:Adder 4 Bit BCD.svg|centre|vignette|upright=2|Additionneur BCD, circuit complet.]]
Pour obtenir un additionneur BCD complet, il suffit d’enchaîner les additionneurs précédents, comme on le ferait avec les additionneurs complets dans un additionneur à propagation de retenue. Au final, l'additionneur BCD est beaucoup plus compliqué qu'un additionneur normal, car il rajoute un comparateur ">9", un petit additionneur pour ajouter 6 et éventuellement d'autres circuits. De plus, il est difficile d'appliquer les optimisations disponibles sur les additionneurs non-BCD. Notamment, les circuits d'anticipation de retenue sont totalement à refaire et le résultat est relativement compliqué. C'est ce qui explique pourquoi le BCD a progressivement été abandonné au profit du binaire simple.
La soustraction en BCD se fait comme en binaire : le nombre à soustraire est remplacé par son complément, le circuit additionne le complément et l'autre opérande, le débordement d'entier fait que le résultat marche. Sauf qu'ici, le complément est un complément à 9. Il se calcule chiffre par chiffre : chaque chiffre est remplacé par (9 - le chiffre en question).
===L'additionneur BCD par ajustement décimal===
L'additionneur BCD précédent effectuait son travail chiffre BCD par chiffre BCD, mais il existe des additionneurs BCD qui font autrement. Sur les premiers processeurs x86, il n'y avait pas d'opération d'addition BCD proprement dit, seulement une addition binaire normale de 8, 16 ou 32 bits. Par contre, elle était secondée par une opération dite d''''ajustement décimal''' qui transformait un nombre binaire en nombre codé en BCD. L'opération d'ajustement décimal prenait un opérande de 8 bits codé en binaire et fournissait un résultat de la même taille, c'est à dire deux chiffres BCD. Effectuer une addition BCD demandait donc de faire deux opérations à la suite : une addition binaire simple, suivie par l'opération d'ajustement décimal. Cela permettait de gérer des nombres entiers en binaire usuel et des entiers BCD sans avoir deux instructions d'addition séparées pour les deux, sans compter que cela simplifiait aussi les circuits d'addition.
L'ajustement décimal s'effectue en ajoutant une constante bien précise à l'opérande à convertir en BCD. L'idée est que la constante est découpée en morceaux de 4 bits, correspondant chacun à un chiffre BCD de l'opérande, chaque morceau contenant soit un 0, soit 6. Cela permet d'ajouter soit 0, soit 6, à chaque chiffre BCD, et donc de le corriger. La propagation des retenues d'un chiffre à l'autre est effectuée automatiquement par l'addition binaire de la constante. L'opération d'ajustement décimal calcule automatiquement la constante. Elle découpe l'opérande en ''nibbles'', vérifie si chaque ''nibble'' est supérieur ou égal à 10, puis détermine la valeur de chaque ''nibble'' de la constante finale. Par exemple, si je prends l'opérande 1001 1110, le ''nibble'' de poids faible déborde, alors que celui de poids fort non. La constante sera donc 0000 0110 : 0x06. Inversement, si le ''nibble'' de poids fort déborde et pas celui de poids faible, la constante sera alors 0x60. Et la constante est de 0x66 si les deux ''nibbles'' débordent, de 0x00 si aucun ne déborde.
Le circuit d’ajustement décimal est donc composé de trois étapes : deux étapes pour calculer la constante, et un circuit d'addition pour additionner cette constante au nombre de départ. La première étape découpe l'opérande en morceaux de 4 bits, en chiffres BCD, et vérifie si chacun d'entre eux vaut 10 ou plus. La seconde étape prend les résultats de la première étape, et les combine pour calculer la constante. Enfin, on trouve l'addition finale, qui était réalisée par un circuit d'addition utilisé à la fois pour l'ajustement décimal et l'addition binaire. La différence entre une addition normale et une opération d'ajustement décimal tient dans le fait que les deux premières étapes sont désactivées dans une addition normale.
[[File:Additionneur BCD parallèle.png|centre|vignette|upright=1.5|Additionneur BCD parallèle]]
===L'additionneur biquinaire===
Les entiers BCD ne sont qu'un des encodages hybrides entre décimal et binaire. L'encodage biquinaire est l'un d'entre eux et nous allons faire un rappel rapide à ce sujet. Pour simplifier, un chiffre encodé en biquinaire est composé de deux parties : un bit, couplé à une partie quinaire encodée en représentation ''one-hot''. La partie quinaire encode un nombre allant de 0 à 4, ce qui prend 5 bits (0, 1, 2, 3 et 4). Le bit indique s'il faut ou non ajouter 5 à la valeur encodée par la partie quinaire. Ainsi, on peut coder tous les nombres de 0 à 9.
Additionner deux nombres de biquinaire demande donc d'additionner deux parties quinaires encodées en ''one-hot'' et d'additionner deux bits. Mais attention : il faut tenir compte de la retenue de l'addition des parties quinaires. Et idéalement, il faut aussi tenir compte d'une retenue entrante, provenant de l'addition de la colonne de chiffres précédente. Toute la difficulté vient de la création de l'additionneur ''one-hot''. Heureusement, vu qu'il n'y a que 4-5 bits à additionner, il est souvent fabriqué à partir de sa table de vérité.
[[File:Additionneur bi-quinaire.png|centre|vignette|upright=2|Additionneur bi-quinaire]]
Un avantage du biquinaire est que le calcul du complément à 9 est très simple. Il faut pour cela : inverser la partie binaire avec une porte NON, puis inverser l'ordre des bits de la partie quinaire. Concrètement, le bit de poids faible devient le bit de poids fort, et ainsi de suite. Par exemple, une partie quinaire 01000 devient 00010, 10000 devient 00001, 00100 ne change pas, etc. Le tout peut se calculer avec une porte NON et 5 multiplexeurs.
===L'additionneur BCD avec calculs intermédiaires en biquinaire===
L'ordinateur IBM 1401, un ancien ''mainframe'' des années 60, utilisait un additionneur BCD un peu particulier. Les nombres étaient encodés en BCD dans la mémoire de l'ordinateur, mais les circuits de calcul utilisaient la représentation biquinaire. Lors d'un calcul, le processeur de l'ordinateur traduisait les chiffres BCD en représentation biquinaire, faisait une addition en biquinaire, avant de traduire le résultat en BCD normal.
Pour être précis, l'IBM 1401 utilisait une variante du biquinaire. L'encodage biquinaire de l'IBM 1401 est le suivant : la partie binaire disait si le chiffre était pair ou non, la partie quinaire encodait les valeurs 0, 2, 4, 6 et 8. Le chiffre se calculait en additionnant la partie binaire (0 ou 1) au nombre pair encodé par la partie quinaire. Si l'IBM 1401 utilisait cette variante du biquinaire, c'est car elle donnait des circuits de conversion BCD-biquinaire plus économes en portes logiques et plus rapides.
La partie binaire est le bit de poids faible du chiffre BCD, la partie biquinaire est calculée par un simple décodeur qui prend en entrée le chiffre BCD, amputé de son bit de poids faible. La traduction inverse demande d'utiliser un encodeur, à la place du décodeur. Par contre, le circuit d'addition biquinaire était plus compliqué du fait de la gestion des retenues. L'addition des parties binaires et quinaires se faisait en parallèle, dans deux additionneurs séparés. Cependant, l'addition des parties binaire fournit une retenue, qu'il faut prendre en compte. Pour cela, l'IBM 1401 disposait d'un troisième additionneur qui fournissait le résultat final, encodé en biquinaire.
[[File:Additionneur bi-quinaire de l'IBM 1401.png|centre|vignette|upright=2|Additionneur biquinaire de l'IBM 1401]]
Une implémentation moderne demanderait d'utiliser des portes ET combinées à des portes OU, le circuit pouvant être construit simplement à partir de sa table de vérité. Sur l'IBM 1401, le circuit était cependant différent, en raison de l'utilisation de OU câblés, des croisements de fils qui fonctionnent comme des portes OU, que nous n'avons pas encore vu pour le moment, mais qui seront détaillés dans quelques chapitres. Les OU câblés étaient utilisés pour simplifier le design du circuit, mais demandaient des portes logiques spécifiques, ce qui collait avec le fait que ce ''mainframe'' utilisait des transistors en Germanium. L'implémentation exacte est décrite dans cet article de blog, mais je ne recommande sa lecture qu'à ceux qui savent ce qu'est un OU câblé :
* [https://www.righto.com/2015/10/qui-binary-arithmetic-how-1960s-ibm.html Qui-binary arithmetic: how a 1960s IBM mainframe does math].
==Les débordements d'entier lors d'une addition/soustraction==
Les instructions arithmétiques manipulent des entiers codés sur un nombre fixe de bits, qui ne peuvent prendre leurs valeurs que dans un intervalle. Pour les nombres positifs, un ordinateur qui code ses entiers sur n bits pourra coder tous les entiers allant de 0 à <math>2^n -1</math>. Pour les nombres négatifs, l'intervalle est différent et dépend de la représentation utilisée. Dans le cas général, l'ordinateur peut coder les valeurs comprises de <math>N_\text{min}</math> à <math>N_\text{max}</math>. Si le résultat d'un calcul sort de cet intervalle, il ne peut pas être représenté par l'ordinateur et il se produit ce qu'on appelle un '''débordement d'entier'''.
La '''valeur haute de débordement''' désigne la première valeur qui est trop grande pour être représentée par l'ordinateur. Par exemple, pour un ordinateur qui peut coder tous les nombres entre 0 et 7, la valeur haute de débordement est égale à 8. On peut aussi définir la '''valeur basse de débordement''', qui est la première valeur trop petite pour être codée par l'ordinateur. Par exemple, pour un ordinateur qui peut coder tous les nombres entre 8 et 250, la valeur basse de débordement est égale à 7. Pour les nombres entiers, la valeur haute de débordement vaut <math>N_\text{max} + 1</math> , alors que la valeur basse vaut <math>N_\text{min} - 1</math> (avec <math>N_\text{max}</math> et <math>N_\text{min}</math> respectivement la plus grande et la plus petite valeur codable par l'ordinateur).
===La correction des débordements d'entier : l'arithmétique saturée===
Quand un débordement d'entier survient, tous les circuits de calcul ne procèdent pas de la même manière. Dans les grandes lignes, il y a deux réactions possibles : soit on corrige automatiquement le résultat du débordement, soit on ne fait rien et on se contente de détecter le débordement.
Si le débordement n'est pas corrigé automatiquement, le circuit ne conserve que les bits de poids faibles du résultat. Les bits en trop sont simplement ignorés. On dit qu'on utilise l''''arithmétique modulaire'''. Le problème avec ce genre d'arithmétique, c'est qu'une opération entre deux grands nombres peut donner un résultat très petit. Par exemple, si je dispose de registres 4 bits et que je souhaite faire l'addition 1111 + 0010 (ce qui donne 15 + 2), le résultat est censé être 10001 (17), ce qui est un résultat plus grand que la taille d'un registre. En conservant les 4 bits de poids faible, j’obtiens 0001 (1). En clair, un résultat très grand est transformé en un résultat très petit. Cela peut poser problèmes si on travaille uniquement avec des nombres positifs, mais c'est aussi utilisé pour coder des nombres en complément à deux.
D'autres circuits utilisent ce qu'on appelle l''''arithmétique saturée''' : si un calcul génère un débordement, on arrondi le résultat au plus grand entier supporté par le circuit. Les circuits capables de calculer en arithmétique saturée sont un peu plus complexes, vu qu'il faut rajouter des circuits pour corriger le résultat en cas de débordement. Il suffit généralement de rajouter un ''circuit de saturation'', qui prend en entrée le résultat et le corrige en cas de débordement. Ce circuit de saturation met la valeur maximale en sortie si un débordement survient, mais se contente de recopier le résultat du calcul sur sa sortie s'il n'y a pas de débordement. Typiquement, il est composé d'une couche de multiplexeurs, qui sélectionnent quelle valeur mettre sur la sortie : soit le résultat du calcul, soit le plus grand nombre entier géré par le processeur, soit le plus petit (pour les nombres négatifs/soustractions).
L'arithmétique saturée est utilisée pour les additions et soustractions, mais c'est plus rare pour les multiplications/divisions. Une des raisons est que le résultat d'une addition/soustraction prend un bit de plus que le résultat, là où les multiplications doublent le nombre de bits. Quand une addition déborde, le résultat réel est proche de la valeur maximale codable. mais quand une multiplication déborde, le résultat peut parfois valoir 200 à 60000 fois plus que la valeur maximale codable. Les calculs avec une valeur saturée/corrigée sont donc crédibles pour une suite d'additions, mais pas pour une suite de multiplications.
===La détection des débordements entiers===
Quand un débordement d'entier a eu lieu, il vaut mieux que l'additionneur prévienne ! Pour cela, l'additionneur a une '''sortie de débordement''', parfois nommée ''Overflow'', dont la valeur indique si l'addition a généré un débordement d'entier ou non. Reste que détecter un débordement ne se fait pas de la même manière selon que l'on parle d'un additionneur non-signé ou signé.
Pour les additionneur non-signés, l'additionneur calcule un bit de plus que ce qui est supporté par l'ordinateur. Par exemple, un additionneur 32 bits fournit un résultat sur 33 bits, un débordement d'entier a lieu quand le 33ème bit est à 1. Précisément, la sortie de débordement n'est autre que la retenue finale, celle fournie par le dernier additionneur complet. Le seul type de débordement possible est un débordement par le haut, où le résultat dépasse la valeur maximale. Avec l'arithmétique saturée, le circuit de saturation consiste en une seule couche de multiplexeurs, voire en un circuit de mise à la valeur maximale tel que vu dans le chapitre sur les opérations bits à bits.
[[File:Gestion des débordements d'entiers.png|centre|vignette|upright=2.5|Gestion des débordements d'entiers lors d'une addition non-signée.]]
Pour les additionneurs non-signés, la gestion des débordements d'entiers dépend fortement de la représentation signée. Nous allons étudier le cas du complément à deux. Si vous vous rappelez le chapitre 1, les calculs sur des nombres en complètement à deux utilisent les règles de l'arithmétique modulaire, c'est une condition nécessaire. À priori, on peut penser que dans ces conditions, les débordements d'entiers sont une chose parfaitement normale, qui nous permet d'avoir des résultats corrects. Néanmoins, certains débordements d'entiers peuvent survenir malgré tout et produire des bugs assez ennuyeux.
Si l'on tient en compte les règles du complément à deux, on sait que le bit de poids fort (le plus à gauche) permet de déterminer si le nombre est positif ou négatif : il indique le signe du nombre. Tout se passe comme si les entiers en complément à deux étaient codés sur un bit de moins, et avaient leur longueur amputé du bit de poids fort. Si le résultat d'un calcul écrase le bit de poids fort, il y a un débordement d'entiers. Il existe une règle simple qui permet de détecter ces débordements d'entiers. L'addition de deux nombres positifs ne peut pas être un nombre négatif. Si on additionne deux nombres dont le bit de signe est à 0 et que le bit de signe du résultat est à 1, on est en face d'un débordement d'entiers. Même chose pour deux nombres négatifs : le résultat de l'addition ne peut pas être positif. On peut résumer cela en une phrase : si deux nombres de même signe sont ajoutés, un débordement a lieu quand le bit du signe du résultat a le signe opposé.
Modifier les circuits d'au-dessus pour qu'ils détectent les débordements en complément à deux est simple comme bonjour : il suffit créer un petit circuit combinatoire qui prenne en entrée les bits de signe des opérandes et du résultat, et qui fasse le calcul de l'indicateur de débordements. Si l'on rédige sa table de vérité, on doit se retrouver avec la table suivante :
{|class="wikitable"
|-
!Entrées
!Sortie
|-
|000||0
|-
|001||1
|-
|010||0
|-
|011||0
|-
|100||0
|-
|101||0
|-
|110||1
|-
|111||0
|}
L'équation de ce circuit est la suivante, avec <math>S_a</math> et <math>S_b</math> les signes des deux opérandes, et <math>C_i</math> la retenue de la colonne précédente :
: <math> ( S_a . S_b . \overline{C_i} ) + ( \overline{S_a} . \overline{S_b} . C_i )</math>
En simplifiant, on obtient alors :
: <math>( S_a . S_b ) \oplus C_i</math>
Or, il se trouve que <math>S_a . S_b</math> est tout simplement la retenue en sortie du dernier additionneur, que nous noterons <math>C_o</math>. On trouve donc :
: <math>C_o \oplus C_i</math>
Il suffit donc de faire un XOR entre la dernière retenue et la précédente pour obtenir le bit de débordement.
<noinclude>
{{NavChapitre | book=Fonctionnement d'un ordinateur
| prev=Les circuits de décalage et de rotation
| prevText=Les circuits de décalage et de rotation
| next=Les circuits de comparaison
| nextText=Les circuits de comparaison
}}
</noinclude>
luiuftrvn4qd11ipo70c9r8ouds41bd
768783
768782
2026-06-27T13:46:05Z
Mewtow
31375
/* Le soustracteur à propagation de retenue */
768783
wikitext
text/x-wiki
Dans ce chapitre, nous allons voir les circuits capables de faire une addition ou une soustraction, ainsi que quelques circuits spécialisés. Précisons cependant que les fabricants de processeurs travaillent d'arrache-pied pour trouver des moyens de rendre ces circuits de calcul plus rapides et plus économes en énergie. Autant vous dire que les circuits que vous allez voir sont vraiment des circuits qui font pâle figure comparé à ce que l'on peut trouver dans un vrai processeur commercial !
==Les circuits pour additionner 2 ou 3 bits==
L'addition se fait en binaire de la même manière qu'en décimal. On additionne les chiffres/bits colonne par colonne, une éventuelle retenue est propagée à la colonne d'à côté. La soustraction fonctionne sur le même principe, sur le même modèle qu'en décimal.
[[File:Binary Addition Demonstration.svg|centre|vignette|Exemple d'addition en binaire.]]
En clair, additionner deux nombres demande d'additionner 2 bits et une retenue sur chaque colonne, et de propager les retenues d'une colonne à l'autre. La propagation des retenues est quelque chose de simple en apparence, mais qui est sujet à des optimisations extraordinairement nombreuses. Aussi, pour simplifier l'exposition, nous allons voir comment gérer une colonne avant de voir comment sont propagées les retenues.
[[File:Full Adder Block.svg|vignette|upright=1|class=transparent|Additionneur complet.]]
Si on effectue une addition en colonne, on doit additionner les deux bits sur la colonne, mais aussi additionner une éventuelle retenue. Il faut donc créer un circuit qui additionne trois bits : deux bits de données, plus une retenue. Ce circuit qui additionne trois bits est appelé un '''additionneur complet'''. Il fournit en sortie deux bits : un bit de somme et une retenue sortante. Voici sa table de vérité :
{|class="wikitable"
|-
! Retenue entrante !! Opérande 1 !! Opérande 2 !! !! Retenue sortante !! Bit de somme
|-
||0||0||0|| ||0||0
|-
||0||0||1|| ||0||1
|-
||0||1||0|| ||0||1
|-
||0||1||1|| ||1||0
|-
||1||0||0|| ||0||1
|-
||1||0||1|| ||1||0
|-
||1||1||0|| ||1||0
|-
||1||1||1|| ||1||1
|}
Il est possible d'utiliser un tableau de Karnaugh pour traduire la table de vérité, mais elle donne un résultat assez compliqué. La retenue et le bit de somme sont calculés à part. La retenue est calculée avec trois portes ET et une porte OU. Le bit de somme est calculé lui avec 4 portes ET, une porte OU, et trois portes NON.
[[File:Full Adler with 3 entries.png|centre|vignette|upright=2|Additionneur complet, fabriqué à partir de la table de vérité.]]
Le circuit obtenu est pas loin d'être optimal, que ce soit en termes de rapidité que de portes logiques utilisées. Mais il est assez compliqué à comprendre, vu que c'est une traduction littérale de la table de vérité. D'autres implémentations alternatives donnent des résultats plus compréhensibles. Nous allons les voir dans la suite de cette section.
===L'additionneur complet basé sur des demi-additionneurs===
[[File:1-bit half-adder.svg|class=transparent|vignette|Demi-addtionneur.]]
Nous avons déjà vu comment additionner deux bits dans le chapitre sur les incrémenteurs, mais quelques rappels ne seront pas de trop. Le '''demi-additionneur''' est un circuit qui additionne deux bits. Il implémente la table d'addition, qui est très simple en binaire. Jugez plutôt :
* 0 + 0 = 0, retenue = 0 ;
* 0 + 1 = 1, retenue = 0 ;
* 1 + 0 = 1, retenue = 0 ;
* 1 + 1 = 0, retenue = 1.
Mais un demi-additionneur n'additionne que deux bits, il manque de quoi additionner la retenue. Une solution pour cela est d'enchaîner deux demi-additionneurs : un qui additionne les deux bits de données, et un second qui additionne la retenue au résultat du précédent. La retenue finale se calcule en combinant les sorties de retenue des deux demi-additionneurs, avec une porte OU. Le résultat est un additionneur complet fabriqué avec deux demi-additionneurs. Pour vous en convaincre, établissez la table de vérité de ce circuit, vous verrez que ça marche.
[[File:Full Adder Blocks.svg|centre|vignette|upright=2|Additionneur complet fabriqué avec deux demi-additionneurs.]]
[[File:Half-adder.svg|vignette|upright=1|Circuit d'un demi-additionneur naïf.]]
L'implémentation naïve d'un demi-additionneur utilise une porte XOR et une porte ET, comme illustré ci-contre. Le circuit s'obtient en faisant uen traduction littérale de la table de vérité du circuit. Si je dis qu'elle est naïve, c'est car il est possible de l'optimiser, de manière à éliminer des redondances cachées. Mais nous avions déjà vu cela dans le chapitre sur les incrémenteurs, aussi laissons cela pour plus tard.
En combinant deux demi-additionneurs de ce type, on obtient l'additionneur complet suivant. Les deux sorties de retenue des demi-additionneurs sont combinées avec une porte OU, pour calculer la retenue finale.
{| class="flexible"
|[[File:Full Adder Modules.svg|class=transparent|Composition d'un additionneur complet. On voit bien que celui-ci est composé de deux demi-additionneurs, en rouge et en bleu, auxquels on a ajouté une porte OU pour calculer la retenue finale.]]
|[[File:Full-adder.svg|300px|class=transparent|Circuit d'un additionneur complet.]]
|}
Le fait de combiner les deux retenues avec une porte OU n'est pas ce qu'il y a de plus intuitif. La table de vérité nous dit que ça fonctionne, mais on comprend mal pourquoi. Mais tout devient plus clair quand on sait que les deux retenues ne peuvent pas être à 1 en même temps. Même dans l'addition 1 + 1 + 1, seule une retenue est à 1. Soit l'addition des deux bits d'opérande donne naissance à une retenue, soit c'est l'addition ''résultat + retenue entrante''. La retenue sortant est donc à 1 quand une des deux addition donne une retenue. D'où l'usage d'une porte OU. Remarquez qu'une porte XOR donne le même résultat, vu que le cas où les deux retenues sont à 1 n'est jamais rencontré.
===La propagation et la génération des retenues===
L'additionneur complet que nous allons voir dans cette section sert à introduire les concepts de propagation, génération et absorption de retenue. Il s'agit de concepts très importants quand on étudie les additionneurs. l'idée part d'un principe très simple : la retenue sortante dépend de la retenue d'entrée. La relation entre les deux se résume à trois cas, qui dépendent de la valeur des deux bits additionnés, nommés A et B.
* Dans le premier cas, la retenue entrante est égale à la retenue sortante. On dit que la retenue entrante est propagée par l'additionneur.
* Dans le second cas, la retenue est forcée à 0 : la retenue sortante vaut 0, peu importe la valeur de la retenue entrante.
* Dans le troisième cas, la retenue est forcée à 1 : la retenue entrante vaut 1, peu importe la valeur de la retenue entrante.
[[File:Signaux P et G fournis par un demi-additionneur.png|vignette|Signaux P et G fournis par un demi-additionneur]]
Maintenant, créons un circuit qui nous dise si une retenue est propagée ou générée. Il a deux bits de sortie, nommés P et G : P pour ''Propagate'', G pour ''Generate''. Le bit P est à 1 si la retenue entrante est propagée, il est à 0 sinon. Le bit G est à 1 si une retenue est générée, à 0 sinon. Une retenue est considérée comme absorbée si elle n'est pas ni propagée ni générée, pas besoin d'un troisième bit pour gérer ce cas.
Il se trouve que ce circuit n'est autre qu'un demi-additionneur ! Pour vous en rendre compte, regardez la table de vérité d'un additionneur complet, illustrée ci-dessous. Lorsque les deux bits d'opérande sont à 0, la retenue sortante vaut toujours 0. Si ils sont tous deux à 1, alors la retenue sortante vaut 1. S'ils sont différents, alors retenues sortante et entrante sont égales. Le bit P est donc généré par une simple porte XOR. Quant au bit G, il est à 1 si les deux bits d'opérandes sont à 1, ce qui correspond à une porte ET. Il se trouve que ces deux portes forment un demi-additionneur !
{|class="wikitable"
|-
! Opérande 1 !! Opérande 2 !! Retenue entrante !! !! Retenue sortante
|- class="f_rouge"
||0||0||0|| ||0
|- class="f_rouge"
||0||0||1|| ||0
|- class="f_bleu"
||0||1||0|| ||0
|- class="f_bleu"
||0||1||1|| ||1
|- class="f_bleu"
||1||0||0|| ||0
|- class="f_bleu"
||1||0||1|| ||1
|- class="f_vert"
||1||1||0|| ||1
|- class="f_vert"
||1||1||1|| ||1
|}
Ensuite, créons un circuit qui prend ces deux sorties P et G, et calcule la retenue sortante en fonction. Le circuit en question a trois entrées : la retenue entrante, les deux bits P et G. La retenue sortante vaut 1 soit si une retenue est générée, soit si la retenue entrante est propagée et qu'elle vaut 1. En notant <math>{C_{in}}</math> et <math>{C_{out}}</math> les retenues entrantes et sortante, on a :
: <math>{C_{out}} = G + \left( P . C_{in} \right)</math>
Le circuit est donc composé d'une porte OU et d'une porte ET. En combinant un demi-additionneur avec le circuit de calcul de retenue sortante vu plus haut, on a :
[[File:Additionneur complet avec propagation et génération de retenue.png|class=transparent|centre|vignette|upright=2|Additionneur complet avec propagation et génération de retenue.]]
Certes, on retombe sur le circuit vu plus haut. Mais il est possible de remplacer le circuit en orange par un autre. Par exemple, il est possible de le remplacer par multiplexeur, qui choisit entre la retenue générée et la retenue entrante (propagée). Le choix se fait selon la valeur du bit P, qui chosiit entre propager la retenue et la générer.
[[File:Additionneur crée avec un multiplexeur.png|centre|vignette|upright=1.5|Additionneur crée avec un multiplexeur]]
Le circuit exact est illustré ci-dessous. Il semble utiliser plus de portes logiques que l'additionneur complet précédent. Cependant, nous verrons dans quelques chapitres qu'il est possible d'implémenter un multiplexeur avec seulement 6 transistors, voire moins ! L'implémentation utilise des portes à transmission, mais nous en reparlerons dans le chapitre sur les transistors, quand nous verrons les additionneurs à ''Manchester Carry Chain''. Au passage, une variante de ce circuit a été utilisée dans le processeur processeur 8086 d'Intel, comme on le verra dans le chapitre suivant.
[[File:Additionneur complet basé sur un MUX.png|centre|vignette|upright=1|Additionneur complet basé sur un MUX]]
[[File:FulladderPG.png|vignette|upright=1|Additionneur complet avec deux sorties P et G.]]
Pour finir, sachez qu'il existe des additionneurs qui fournissent : le bit de résultat, deux sorties P et G qui indiquent si l'addition propage ou génère une retenue. Il ne s'agit pas d'additionneurs complets, car il manque de quoi calculer la retenue sortante. De tels additionneurs seront appelés des '''additionneurs partiels'''. De tels additionneurs sont utilisés dans certains additionneurs pour gagner en performance.
Enfin, il existe aussi ce que j'ai décidé d'appeler des '''additionneurs P/G''', qui sont des additionneurs complets auxquels on a ajouté deux sorties P et G, en plus de la sortie de retenue. Leur circuit est le même que celui d'un additionneur complet, auquel on a ajouté deux fils. Vu que ces deux sorties sont fournies par le premier demi-additionneur, ajouter ces deux sorties demande d'ajouter des fils, pas de portes logiques. De tels additionneurs sont aussi utilisés dans certains additionneurs pour gagner en performance.
[[File:FAwithGP.svg|centre|vignette|upright=2|Additionneur complet avec sorties P et G : circuit.Ç]]
===L'additionneur complet basé sur une porte à majorité===
Maintenant, voyons une dernière implémentation possible de l'additionneur complet. Mais avant de voir comment l'additionneur est implémenté, nous allons devoir faire un petit focus sur la retenue sortante. Reprenons la table de vérité, et regardons ce qu'il en est pour la retenue sortante uniquement.
{|class="wikitable"
|-
! Retenue entrante !! Opérande 1 !! Opérande 2 !! !! Retenue sortante
|-
||0||0||0|| ||0
|-
||0||0||1|| ||0
|-
||0||1||0|| ||0
|-
||0||1||1|| ||1
|-
||1||0||0|| ||0
|-
||1||0||1|| ||1
|-
||1||1||0|| ||1
|-
||1||1||1|| ||1
|}
Vous remarquerez que la retenue sortante est égale au bit majoritaire, parmi les trois bits d'opérande. Si deux ou trois bits sont à 1, la retenue sortante vaut 1. Et réciproquement avec 0. Le circuit de calcul de la retenue peut donc être remplacé par une '''porte à majorité'''.
[[File:Additionneur crée avec une porte à majorité.png|centre|vignette|upright=2|Additionneur crée avec une porte à majorité]]
Mais une porte à majorité est assez gourmande en circuit, ce qui fait qu'un additionneur pareil ne serait pas pratique, ni utile. Du moins, ce serait le cas s'il n'y avait pas une possibilité d'optimisation extrêmement intéressante. Il est possible de calculer le 'bit de somme' à partir de la retenue sortante ! En effet, le bit de somme est l'inverse de la retenue sortante, sauf dans deux cas : les trois bits d'entrée sont à 0, où ils sont tous à 1. Le bit de somme vaut 0 dans le premier cas, 1 dans le second cas.
{|class="wikitable"
|-
! Retenue entrante !! Opérande 1 !! Opérande 2 !! !! Retenue sortante !! Bit de somme
|- class="f_bleu"
||0||0||0|| ||0||0
|- class="f_vert"
||0||0||1|| ||0||1
|- class="f_vert"
||0||1||0|| ||0||1
|- class="f_vert"
||0||1||1|| ||1||0
|- class="f_vert"
||1||0||0|| ||0||1
|- class="f_vert"
||1||0||1|| ||1||0
|- class="f_vert"
||1||1||0|| ||1||0
|- class="f_rouge"
||1||1||1|| ||1||1
|}
En clair, 6 lignes de la table de vérité sur 8 peuvent se calculer avec une porte à majorité, le reste demandant quelques portes logiques pour faire la correction. L'implémentation en circuit demande donc trois choses :
* de calculer la retenue sortante et de quoi l'inverser ;
* un circuit qui vérifie si tous les bits opérande valent 0 : une porte NOR ou OU fait l'affaire ;
* un circuit qui vérifie s'ils valent tous 1, à savoir une porte ET.
Ensuite, on combine le résultat des trois circuits précédents pour obtenir le résultat final. La combinaison est le fait du circuit en jaune dans le schéma ci-dessous.
[[File:Full adder basé sur une modification de la retenue.png|centre|vignette|upright=2|Full adder basé sur une modification de la retenue]]
Le circuit en jaune est assez simple. Quand la porte ET sort un 1, la sortie doit être forcée à 1. Par contre, quand la porte OU sort un zéro, la sortie doit être forcée à 0. Il suffit donc d'enchainer un circuit de mise à 1 et un circuit de mise à 0. Nous avons vu dans le chapitre sur les opérations de masquage qu'il s'agit respectivement d'une porte OU et d'une porte ET.
[[File:Full adder basé sur une modification de la retenue interne, au complet.png|centre|vignette|upright=2|Full adder basé sur une modification de la retenue interne, au complet]]
Divers processeurs utilisaient des additionneurs complets de ce type : le fameux Z80, l'Intel 4004 et de l'Intel 8008 sont les exemples les plus notables. Pourtant, on pourrait s'étonner qu'un tel circuit ait existé. Il utilise beaucoup de portes logiques, a une profondeur logique supérieure : il n'a rien d'avantageux. Sauf qu'il était utilisé sur d'anciens processeurs, qui utilisaient la technologie dite TTL, différente de la technologie CMOS des transistors modernes. Et avec la technologie TTL, il est possible de fusionner plusieurs portes logiques ET et NOR en une seule porte logique ET/OU/NON ! Un additionneur complet construit ainsi ne prenait que deux portes logiques : une pour le calcul de la retenue sortante, une autre pour le reste du circuit.
==L'addition non signée==
Voyons maintenant un circuit capable d'additionner deux nombres entiers: l''''additionneur'''. Dans la version qu'on va voir, ce circuit manipulera des nombres strictement positifs. L'addition des nombres codés en complètement à deux sera vu dans une section ultérieure.
[[File:Full-adder-4bits-serial-CEI.svg|vignette|upright=0.5|Additionneur 4 bits, un bloc.]]
L'interface d'un additionneur est illustrée ci-contre. Un additionneur prend deux opérandes sur deux entrées séparées, et fournit le résultat sur une sortie dédiée. De plus, il a une entrée sur laquelle envoyer une retenue entrante, et a une sortie pour la retenue sortante. La sortie de retenue indique que le résultat de l'addition a débordé (au sens d'un débordement d'entier). Pour l'entrée de retenue, son utilité deviendra plus claire dans ce qui suit, mais souvenez-vous qu'elle existe.
L'entrée et la sortie de retenue sont parfois utilisées pour combiner plusieurs petits additionneurs, pour former un additionneur plus grand. Nous verrons de nombreux exemples dans ce qui suit. Il est assez commun de créer des additionneurs pour des opérandes de 16/32/64 bits, avec des additionneurs de 4/5 bits.
===L'additionneur série===
Il est possible d'additionner deux nombres bit par bit, grâce à un additionneur complet associé à plusieurs registres à décalages. Les opérandes sont placées chacune dans un registre à décalage, et l'additionneur complet utilise les bits sortants de ces registres à décalage. Le bit du résultat est envoyé au registre à décalage pour le résultat. La retenue de l'addition est stockée dans une bascule de 1 bit, en attente du prochain cycle d'horloge. Un tel additionneur est appelé un '''additionneur série'''. Il a été utilisé sur d'anciens ordinateurs dans les années 50-60, aussi bien des prototypes que des ordinateurs commerciaux.
[[File:Additionneur série.jpg|centre|vignette|upright=2.5|Additionneur série.]]
===L'additionneur à propagation de retenue===
L''''additionneur à propagation de retenue''' est aussi appelé l'additionneur ''ripple carry'', c'est son nom anglais. J'utiliserais occasionnellement le nom anglais dans ce qui suit, car celui-ci est plus court. Ilutilise un additionneur complet pour chaque colonne de l'addition. Pour gérer les retenues, il suffit ainsi de câbler des additionneurs complets les uns à la suite des autres, chacun envoyant sa retenue à la colonne suivante.
[[File:Ripplecarryadder.png|centre|vignette|upright=2.5|Additionneur à propagation de retenue.]]
Notons la présence de la retenue sortante, qui est utilisée pour détecter les débordements d'entier, ainsi que pour d'autres opérations. Le bit de retenue final est souvent stocké dans un registre spécial du processeur (généralement appelé ''carry flag'').
Notez aussi, à droite du schéma précédent, l'entrée de retenue <math>\text{R}_\text{Entrée}</math>. Elle est très utile pour l'implémentation de certaines opérations comme l'inversion de signe, la soustraction, l'incrémentation, etc. Certains processeurs sont capables de faire une opération appelée ADC, ADDC ou autre nom signifiant ''Addition with Carry'', qui permet de faire le calcul A + B + Retenue (la retenue en question est la retenue sortante de l'addition précédente, stockée dans le registre ''carry flag''). Son utilité principale est de permettre des additions d'entiers plus grands que ceux supportés par le processeur. Par exemple, cela permet de faire des additions d'entiers 32 bits sur un processeur 16 bits.
[[File:Adder Network 3 Bit Sum Delay 003.svg|vignette|upright=0.5|Propagation de retenue dans l'additionneur.]]
Cet additionneur utilise très peu de portes logiques, ce qui fait qu'il était utilisé sur certains processeurs 8 et 16 bits, dont le budget en portes logiques était limité. Bien que très simple, cet additionneur est cependant peu performant. Son temps de calcul est proportionnel à la taille des opérandes. Par exemple, additionner deux opérandes de 32 bits prendra deux fois plus de temps que pour des opérandes de 16 bits. La raison est que les retenues doivent se propager à travers le circuit, du premier additionneur jusqu'au dernier.
L'addition étant une opération fréquente, d'autres additionneurs ont été inventés. Ils utilisent des optimisations qui utilisent plus de circuits pour gagner en rapidité. Mais avant de voir ces optimisations, nous allons voir s'il est possible d'optimiser les additionneurs à propagation de retenue.
===L'additionneur à saut de retenue===
L'additionneur à propagation de retenue peut être optimisé de deux manières. La première accélère la propagation de la retenue, en agissant au niveau des additionneurs complets. La ''Manchester carry chain'' est une optimisation de ce type, mais on ne peut pas encore expliquer à ce stade du cours, car elle optimisait les additionneurs complets au niveau des transistors eux-mêmes. Elle a été utilisée sur de nombreux processeurs connus, comme le 8086 d'Intel ou le Z80.
La seconde manière rend l'addition plus rapide dans certains cas. Le résultat est un additionneur dont le temps de calcul est variable. Le calcul prendra quelques cycles d'horloges avec certains opérandes, mais d'autres opérandes ne verront aucune amélioration. Il n'améliore pas le pire des cas, dans lequel la retenue doit être propagée du début à la fin, du bit de poids faible au bit de poids fort. Mais dans d'autres cas, il permet d'avoir le résultat en avance. L'optimisation en question donne l''''additionneur à saut de retenue''' (''carry-skip adder'').
Il est composé en enchainant plusieurs additionneurs plus petits, qui additionnent 4/5 bits, rarement plus. De tels additionneurs de petite taille sont appelés des '''blocs''', dans ce qui suit. L'idée est de détecter précocement si la retenue entrante est propagée à travers tout le bloc. Si la retenue est propagée dans tous le bloc, on peut directement l'envoyer sur la sortie de retenue, la retenue saute le bloc entier. Dans le cas contraire, on doit calculer la retenue normalement. Le choix entre les deux est le fait d'un multiplexeur. Toute la difficulté est de savoir comment commander le multiplexeur.
[[File:Carry skip adder, principe de base.png|centre|vignette|upright=2.5|Carry skip adder : principe de base]]
Pour savoir si une retenue est propagée dans un bloc entier, il faut utiliser des additionneurs complets, qui ont en plus une sortie P indiquant que l'additionneur propage la retenue. Le bloc propage une retenue si chaque additionneur complet propage la retenue, donc s'ils ont tous leur sortie P à 1. Un simple ET logique suffit à déterminer si c'est le cas. Le signal de commande du multiplexeur est donc un vulgaire ET entre toutes les sorties P des additionneurs complets du bloc.
[[File:Additionneur à saut de retenue.png|centre|vignette|upright=2.5|Calcul de la commande du MUX.]]
Voici ce que ça donne pour un bloc de 4 bits :
[[File:CSAdder4Bit.svg|centre|vignette|upright=1.5|Additionneur ''carry skip'' : bloc de 4 bits.]]
L'additionneur à saut de retenue est construit en assemblant plusieurs blocs de ce type.
[[File:BCSAdder16Bit.svg|centre|vignette|upright=3|Additionneur à saut de retenue.]]
Les blocs sont tous identiques dans le cas le plus simple, mais il est possible d'utiliser des blocs de taille variable. Par exemple, le premier bloc peut avoir des opérandes de 6 bits, le second des opérandes de 7 bits, etc. Faire ainsi permet de gagner un petit peu en performances, si la taille de chaque bloc est bien choisie. La raison est une question de temps de propagation des retenues. La retenue met plus de temps à se propager à travers 8 blocs qu'à travers 4, ce qui prend plus de temps qu'à travers 2 blocs, etc. En tenir compte fait que la taille des blocs tend à augmenter ou diminuer quand on se rapproche des bits de poids fort.
===L'additionneur à sélection de retenue===
L''''additionneur à sélection de retenue''' utilise aussi des blocs, comme les additionneurs précédents. L'addition se fait en deux versions : une avec la retenue du bloc précédent valant zéro, et une autre version avec la retenue du bloc précédent valant 1. Il suffira alors de choisir le bon résultat avec un multiplexeur, une fois cette retenue connue. On gagne ainsi du temps en calculant à l'avance les valeurs de certains bits du résultat, sans connaître la valeur de la retenue. Petit détail : sur certains additionneurs à sélection de retenue, les blocs de base n'ont pas la même taille. Cela permet de tenir compte des temps de propagation des retenues entre les blocs.
[[File:Additionneur à sélection de retenue.png|centre|vignette|upright=1.5|Additionneur à sélection de retenue avec seulement deux blocs.]]
Dans les exemples du dessus, chaque sous-additionneur étaient des additionneurs à propagation de retenue. Mais ce n'est pas une obligation, et tout autre type d’additionneur peut être utilisé. Par exemple, on peut faire en sorte que les sous-additionneurs soient eux-mêmes des additionneurs à sélection de retenue, et poursuivre ainsi de suite, récursivement. On obtient alors un '''additionneur à somme conditionnelle''', plus rapide que l'additionneur à sélection de retenue, mais qui utilise beaucoup plus de portes logiques.
===Les additionneurs à anticipation de retenue===
Les '''additionneurs à anticipation de retenue''' calculent chaque retenue sans avoir à propager les retenues précédentes. Au lieu de calculer les retenues une par une, ils calculent toutes les retenues en parallèle, à partir des bits des opérandes. Une fois les retenues pré-calculées, il suffit de les additionner avec les deux bits adéquats, pour obtenir le résultat. Ces additionneurs sont composés de deux parties :
* un circuit qui pré-calcule les retenues, sans les propager, directement à partir des opérandes ;
* d'une couche d'additionneurs complets, qui additionnent chacun deux bits d'opérande et la retenue pré-calculée.
[[File:Additionneur à anticipation de retenue.png|centre|vignette|upright=1.5|Additionneur à anticipation de retenue.]]
Le calcul des retenues se fait en deux étapes. La première détermine si les retenues sont propagées ou générées, pour chaque colonne. Elle détermine, pour chaque colonne, les bits P et G qui indiquent respectivement : qu'une retenue a été générée sur cette colonne, que la colonne propage la retenue précédente. La seconde étape utilise ces signaux P et G pour déterminer la retenue sur chaque colonne. Le circuit de calcul des retenues est donc composé de deux parties : une couche de demi-additionneurs pour générer les signaux P et G, un circuit de calcul des retenues proprement dit.
[[File:Circuit complet d'un additionneur à anticipation de retenue.png|centre|vignette|upright=1.5|Circuit complet d'un additionneur à anticipation de retenue.]]
Pour rappel, la retenue de la colonne i, notée <math>C_i</math> est égale à :
: <math>G_i + (P_i . C_{i-1})</math>, avec G le signal de génération de retenue, P le signal de propagation de retenue.
Si on utilisait cette formule sans trop réfléchir, on retomberait sur un additionneur à propagation de retenue inutilement compliqué. L'astuce des additionneurs à anticipation de retenue consiste à remplacer le terme Ci−1 par sa valeur calculée avant. Par exemple, je prends un additionneur 4 bits. Je dispose de deux nombres A et B, contenant chacun 4 bits : A3, A2, A1, et A0 pour le nombre A, et B3, B2, B1, et B0 pour le nombre B. Si j'effectue les remplacements, j'obtiens les formules suivantes :
* C1 = G0 + ( P0 · C0 ) ;
* C2 = G1 + ( P1 · G0 ) + ( P1 · P0 · C0 ) ;
* C3 = G2 + ( P2 · G1 ) + ( P2 · P1 · G0 ) + ( P2 · P1 · P0 · C0 ) ;
* C4 = G3 + ( P3 · G2 ) + ( P3 · P2 · G1 ) + ( P3 · P2 · P1 · G0 ) + ( P3 · P2 · P1 · P0 · C0 ).
Ces formules nous permettent de déduire la valeur d'une retenue directement : il reste alors à créer un circuit qui implémente ces formules, et le tour est joué.
[[File:Four bit adder with carry lookahead.svg|centre|vignette|upright=1.5|Additionneur à anticipation de retenue de 4 bits.]]
Plus haut, j'ai dit que le circuit était composé de trois couches : une couche de demi-additionneur pour les signaux P et G ; un circuit d'anticipation de retenue, puis l'addition finale. Mais les deux bits d'opérandes sont déjà additionnés dans la couche de demi-additionneur, autant réutiliser le résultat pour l'additionner à la retenue. Pour éliminer toute redondance de ce type, il est possible d'utiliser des additionneurs partiels (des additionneurs où la sortie de retenue a été remplacée par deux sorties P et G). L'additionneur final est alors composé de plusieurs additionneurs complets de ce type, couplé à un circuit d'anticipation de retenue.
[[File:Cla4bits.png|centre|vignette|upright=2.5|Additionneur à anticipation de retenue de 4 bits.]]
Les additionneurs à anticipation de retenue sont plus rapides que les additionneurs à propagation de retenue. La raison est que les retenues sont calculées rapidement, dans un temps plus petit qu'en propageant la retenue. Pour rentrer dans les détails mathématiques, le temps de calcul n'est pas proportionnel au nombre de bits, mais proportionnel au logarithme de ce nombre de bits. Par contre, le gain en temps de calcul s'accompagne d'un cout en portes logiques conséquent. Le circuit qui calcule les retenues anticipées utilise beaucoup de portes logiques.
===Les additionneurs à calcul parallèle de préfixe===
Du fait du cout en portes logiques, utiliser un additionneur à anticipation de retenue est une solution pratique pour additionner 4 ou 8 bits. Mais pour des opérandes plus grands, de 16 ou 32 bits, cela utiliserait trop de portes logiques. Une partie de ce cout en portes logiques est cependant lié à des redondances. Si on analyse deux retenues différentes, à des colonnes différentes. Les circuits qui calculent ces deux retenues seront différents, mais ils contiendront des sous-circuits identiques. Pour vous en rendre compte, reprenons les quatre équations obtenues précédemment et essayez de trouver des redondances :
* C1 = G0 + ( P0 · C0 ) ;
* C2 = G1 + ( P1 · G0 ) + ( P1 · P0 · C0 ) ;
* C3 = G2 + ( P2 · G1 ) + ( P2 · P1 · G0 ) + ( P2 · P1 · P0 · C0 ) ;
* C4 = G3 + ( P3 · G2 ) + ( P3 · P2 · G1 ) + ( P3 · P2 · P1 · G0 ) + ( P3 · P2 · P1 · P0 · C0 ).
Vous devriez trouver :
* le terme ''P0 · C0'' dans toutes les lignes ;
* les termes ''P1 · P0 · C0'' et ''P1 · P0'' dans les trois dernières lignes ;
* le terme ''P2 · P1 · P0 · C0'' ; ''P2 · P1 · G0'' et ''P2 · G1'' dans les deux dernières lignes.
Il existe plusieurs manières d'exploiter de telles redondances, exploitées dans les '''additionneurs à calcul parallèle de préfixe'''. L'optimisation apportée est de générer des signaux ''propagate'' et ''generate'' pour des groupes de 2, 3, 4, ..., N colonnes. Par exemple, il est possible de générer un signal ''P 0 vers 3'', qui indique si la retenue de la première colonne est propagée jusqu'à la 4ème colonne. Un autre exemple est un signal qui indique qu'une retenue a été générée entre les colonnes 4 à 7 génèrent une retenue ou non. En clair, les signaux P et G ont maintenant un intervalle, qui précise de quelle colonne vers quelle colonne se fait la propagation, ou entre quelles colonnes se fait la génération.
Il est possible de combiner les signaux P et G de deux groupes de colonne, s'ils sont contiguës (ils peuvent aussi se recouvrir). Par exemple, il est possible de calculer les bits P et G pour les colonnes 0 à 10, à partir des deux signaux P/G des colonnes 0-4 et 5-10. Pour cela, les équations sont assez simples. Si je prends deux groupes nommés A et B, avec A pour les colonnes de poids fort et B celles de poids faible, on a :
: <math>P_{sortie} = P_A . P_B</math>
: <math>G_{sortie} = G_A . P_A . G_B</math>
Pour les bits des colonnes 2, 4, 8, 16, 32, 64, et autres, le calcul est simple. L'idée est de grouper les colonnes par groupes de 2. Puis, on calcule chaque groupe de 4 bits à partir de deux groupes de 2. Puis les groupes de 8 bits sont calculés à partir de deux groupes de 4 bits, et ainsi de suite pour toute puissance de deux.
[[File:Squelette d'un additionneur à calcul parallèle de préfixe.jpg|centre|vignette|upright=2|Squelette d'un additionneur à calcul parallèle de préfixe]]
Les seules difficultés surviennent pour les colonnes dont le nombre n'est pas une puissance de deux. Il y a plusieurs manières pour subdiviser ces colonnes en intervalles plus petits et combiner le tout. Et elles donnent chacune des additionneurs différents. Ils portent les noms d''''additionneur de Ladner-Fisher''', d''''additionneur de Brent-Kung''', d''''additionneur de Kogge-Stone''', etc. Dans le cas général, l'additionneur de Kogge-Stone est un des plus rapide, mais aussi un des plus gourmand en circuits, alors que c'est l'inverse pour l'additionneur de Brent-Kung. Les deux additionneurs sont illustrés ci-dessous, pour des opérandes de 8 bits. Dans ceux-ci, chaque losange calculent les bits P et G, à partir des signaux P et G de deux groupes précédents.
{|
|[[File:Brent-kung-8-bit.png|vignette|Brent-kung-8-bit]]
|[[File:Kogge-stone-8-bit.png|vignette|upright=1.2|Kogge-stone-8-bit]]
|}
===Le calcul parallèle de la retenue===
L'anticipation de retenue est très rapide, mais utilise beaucoup de circuits, même en utilisant le calcul parallèle de préfixe. Les additionneurs à propagation de retenue font eux le compromis inverse. Mais il existe des intermédiaires, qui visent à obtenir un compromis entre performance et cout en portes logiques.
Un de ces compromis découpe un additionneur de 16/32/64 bits en additionneurs de 4/5 bits, qui sont enchainés en utilisant la propagation de retenue. Par contre, ces petits additionneurs de 4/5 bits utilisent l'anticipation de retenue. Ces additionneurs simples seront nommés ''blocs'' dans ce qui suit. Par exemple, on peut utiliser 4 additionneurs à anticipation de retenue de 4 bits, et propager les retenues entre eux. Le premier bloc calcule une retenue, qui est propagée au second bloc. Le second bloc calcule lui aussi une retenue, qui est propagée au troisième bloc, etc.
[[File:4008 Functional Diagram.svg|vignette|upright=1|4008 Functional Diagram]]
Le gain en performance est significatif, mais il est possible de faire presque aussi rapide, tout en économisant beaucoup de circuits. L'idée est de simplifier les additionneurs 4 bits, afin de ne pas utiliser une anticipation de retenue complète. L'anticipation de retenue n'est utilisée que pour la retenue sortante, alors que les bits de somme sont calculées avec propagation de retenue. La retenue sortante est donc calculée en parallèle de l'addition. Chaque bloc contient, à côté d'un additionneur proprement dit, un circuit qui calcule la retenue sortante. Il existe de nombreuses manières de calculer la retenue sortante.
Le gain en portes logiques est assez significatif. En effet, on se passe de circuits d'anticipation de retenue pour les 4 bits de somme, on ne garde que celui pour la retenue sortante. Le fait d'utiliser la propagation de retenue pour calculer les 4 bits de somme est censé avoir un léger cout en performance, mais il se trouve que ce cout est très faible. En effet, le résultat n'est connu qu'une fois les retenues propagées jusqu'au dernier bloc. Et malgré l'anticipation des retenues, la propagation est assez lente. les additionneurs à propagation de retenue auront terminé leur travail bien avant.
Un exemple est celui de l'additionneur CMOS 4008, un additionneur de 4 bit. Il est composé en trois sections. Une première couche de demi-additionneurs calcule les signaux P et G utilisés à la fois pour l'anticipation de la retenue sortante, que pour calculer les bits de somme. Le circuit de calcul de la retenue sortante utilise l'anticipation de retenue. Le reste du circuit fait l'addition en propageant les retenues.
[[File:CMOS 4008, circuit découpé en sections.png|centre|vignette|upright=1.5|CMOS 4008, circuit découpé en sections]]
===Les additionneurs à anticipation de retenue multi-niveau===
Les additionneurs précédents mixent anticipation et propagation de retenue. Ceux que nous allons voir utilisent des blocs, mais utilisent l'anticipation de retenue entre les blocs. Les blocs utilisés peuvent être un additionneur à propagation de retenue, ou à anticipation de retenue, peu importe. Ce qui compte est que la retenue entrante de chaque bloc est calculée par anticipation de retenue.
Par exemple, il est possible de créer un additionneur 16 bits en utilisant 4 additionneurs/blocs de 4 bits, couplé à un circuit d’anticipation de retenue par bloc. Pour cela, les additionneurs de 4 bits doivent fournir deux sorties P et G. Les deux sorties indiquent que le bloc soit a généré une retenue, soit a propagé la retenue entrante. Par propagé, on veut dire que la retenue entrante du bloc s'est retrouvée sur la sortie de retenue du bloc. C'est le cas si les 4 colonnes propagent la retenue, ce qui signifie que :
: <math>P_\text{4 bits} = P_0 \cdot P_1 \cdot P_2 \cdot P_3</math>
Pour la génération, cela veut dire qu'une colonne a générée une retenue, qui a été propagée jusqu'à la sortie. En clair, pour chaque colonne, il faut que le G = 1, et que les signaux P des colonnes soient à 1. Pour les 4 colonnes, cela donne :
: <math>G_\text{4 bits} = G_3 + G_2 \cdot P_3 + G_1 \cdot P_3 \cdot P_2 + G_0 \cdot P_3 \cdot P_2 \cdot P_1</math>
[[File:Cla4bitsPG.png|centre|vignette|upright=2|Additionneur à anticipation de retenue de 4 bits.]]
Pour combiner 4 blocs de 4 bits, on devra donc combiner 4 bits P et G, avec un circuit d'anticipation de retenue.
[[File:16-bit lookahead carry unit.svg|centre|vignette|upright=2|Additionneur à anticipation de retenue de 16 bits.]]
Il est même possible d'aller plus loin et de combiner 4 additionneurs précédents pour obtenir un additionneur 64 bits.
[[File:64-bit lookahead carry unit.svg|centre|vignette|upright=2|Additionneur à anticipation de retenue de 64 bits.]]
Le résultat est un additionneur à plusieurs niveaux d’anticipation de retenue. Une anticipation de retenue dans un blocs de 4 bits (facultative), entre blocs de 4 bits, entre blocs de 16 bits. La même logique peut être utilisée avec des blocs de taille différente de 4, 16 et 64 bits.
==L'addition signée et la soustraction==
Après avoir vu l'addition, il est logique de passer à la soustraction, les deux opérations étant très proches. Si on sait câbler une addition entre entiers positifs, câbler une soustraction n'est pas très compliqué. De plus, la soustraction permet de faire des additions de nombres signés.
===Le soustracteur à propagation de retenue===
Pour soustraire deux nombres entiers, on peut adapter l'algorithme de soustraction utilisé en décimal, celui que vous avez appris à l'école. Celui-ci ressemble fortement à l'algorithme d'addition : on soustrait les bits de même poids, et on propage éventuellement une retenue sur la colonne suivante. À la différence de l'addition, la retenue est soustraite, et non ajoutée. En clair, une soustraction en binaire demande de faire deux soustraction : pour chaque bit de la première opérande, on soustrait le bit associé de la seconde, puis on soustrait la retenue entrante.
[[File:Algebra1 05 fig014.svg|centre|vignette|Soustraction en binaire, avec les retenues en rouge.]]
[[File:Half Subtractor Vektor.svg|vignette|Demi-soustracteur.]]
Dans le chapitre sur les incrémenteurs/décrémenteurs, nous avons vu un circuit qui soustrait deux bits, appelé le '''demi-soustracteur'''. Il ressemble beaucoup à un demi-additionneur, les différences se résumant à une porte NON ajoutée pour le calcul de la retenue. Il implémente la table de soustraction, qui est très simple en binaire :
* 0 - 0 = 0 ;
* 0 - 1 = 1 et une retenue ;
* 1 - 0 = 1 ;
* 1 - 1 = 0.
Comme pour les additionneurs, deux demi-soustracteurs peuvent être combinés pour donner un '''soustracteur complet'''. Le calcul de la retenue se fait en combinant les deux retenues des demi-soustracteurs avec une porte OU. Là encore, si on regarde les sorties des deux demi-soustracteurs, il est impossible d'avoir les deux sorties de retenues à 1 en même temps.
[[File:Full Subtractor logic diagram.png|centre|vignette|upright=2|Soustracteur complet.]]
[[File:FullSubtractor.svg|centre|vignette|upright=2|Soustracteur complet.]]
Le circuit précédent peut se simplifier pour ne garder qu'une seule porte NON.
[[File:Full subtractor.svg|centre|vignette|upright=1.5|Soustracteur complet, simplifié.]]
[[File:Sub Network 3 Bit Step 002.svg|vignette|upright=1|Soustracteur à propagation de retenue pour opérandes de 3 bits.]]
Les soustracteurs complets sont utilisés pour créer des soustracteurs à propagation de retenue, sur le même modèle que les additionneurs. Et je précise bien "à propagation de retenue", car l'anticipation de retenue et les autres optimisations ne marchent pas exactement pareil entre addition et soustraction. Les signaux de propagation et de génération de retenue ne sont pas générés pareil, idem pour la manière de les combiner.
Il est possible de fusionner un additionneur à propagation de retenue et un soustracteur de même type. Pour cela, il suffit de remarquer que les demi-soustracteurs et les demi-additionneurs se ressemblent beaucoup. La seule différence entre les deux est une vulgaire porte NON. En remplaçant cette porte NON par un inverseur commandable, on obtient un circuit qui sert soit de demi-additionneur, soit de demi-soustracteur, suivant ce qu'on envoie sur l'entrée de commande. Ci-dessous est illustré ce que donne ce remplacement pour un soustracteur complet. Le résultat est un '''additionneur/soustracteur à propagation de retenue'''.
[[File:Additionneur-soustracteur complet.png|centre|vignette|upright=2|Additionneur-soustracteur complet, naïf.]]
Il est donc possible de fusionner un additionneur avec un soustracteur, ce qui donne un '''additionneur/soustracteur'''. Mais comme je l'ai dit plus haut, cela ne marche que si l'''additionneur-/soustracteur'' utilise la propagation de retenue. Dès qu'on souhaite utiliser l'anticipation de retenue ou tout autre technique, les choses sont plus compliquées. Le calcul des retenues étant différent entre addition et soustraction, mutualiser les circuits d'anticipation de retenue pour les deux opérations ne donnera pas un résultat très propre. Ce qui n'est pas le cas avec les solutions que nous allons voir dans ce qui suit.
===L'additionneur-soustracteur pour opérandes codées en complément à deux===
Étudions le cas de la soustraction en complément à deux. Vous savez sûrement que a−b et a+(−b) sont deux expressions équivalentes. Et en complément à deux, − b = not(b) + 1. Dit autrement, a − b = a + not(b) + 1. On pourrait se dire qu'il faut deux additionneurs pour faire le calcul, mais la majorité des additionneurs possède une entrée de retenue pour incrémenter le résultat de l'addition. Un soustracteur en complément à deux est donc simplement composé d'un additionneur et d'un inverseur.
[[File:Additionneur soustracteur.png|centre|vignette|upright=2|Soustracteur en complément à deux.]]
Il est possible de créer un circuit capable d'effectuer soit une addition, soit une soustraction : il suffit de remplacer l'inverseur par un inverseur commandable, qui peut être désactivé. On a vu comment créer un tel inverseur commandable dans le chapitre sur les circuits combinatoires. On peut remarquer que l'entrée de retenue et l'entrée de commande de l'inverseur sont activées en même temps : on peut fusionner les deux signaux en un seul.
[[File:Additionneur-soustracteur en complément à deux.png|centre|vignette|upright=2|Additionneur-soustracteur en complément à deux.]]
Une implémentation alternative est la suivante. Elle remplace l'inverseur commandable par un multiplexeur.
[[File:4-bit ripple carry adder-subtracter.svg|centre|vignette|upright=2|Additionneur-soustracteur en complément à deux, version alternative.]]
===L'additionneur-soustracteur pour opérandes codées en signe-magnitude===
Passons maintenant aux nombres codés en signe-valeur absolue, les deux opérandes étant notées A et B. Suivant les signes des deux opérandes, on a quatre cas possibles : A + B, A − B (B négatif), −A + B (A négatif) et −A − B (A et B négatifs). Une astuce est que le circuit n'a besoin que de calculer A + B et A − B : il peut les inverser pour obtenir − A − B ou B − A. A + B et A − B peuvent se calculer avec un additionneur-soustracteur, reste à corriger le résultat. Il suffit de lui ajouter un inverseur commandable pour obtenir le circuit d'addition finale.
[[File:Additionneur en signe-valeur absolue.png|centre|vignette|upright=2|Additionneur en signe-valeur absolue.]]
Toute la difficulté tient dans le calcul du bit de signe du résultat, quand interviennent des soustractions. Autant l'addition de deux nombres de même signe (A + B et −A − B) ne pose aucun problème, autant les soustractions posent problème (A − B et −A + B). Suivant que <math>A<B</math> ou que <math>A>B</math>, le signe du résultat ne sera pas le même. Déterminer le signe du résultat se fait en regardant les bits de débordement d'entier, comme on le verra plus bas.
===L'additionneur-soustracteur pour opérandes codées en représentation par excès===
Passons maintenant aux nombres codés en représentation par excès. On pourrait croire que ces nombres s'additionnent comme des nombres non-signés, mais ce serait oublier la présence du biais, qui pose problème. Dans les cas de nombres signés gérés avec un biais, voyons ce que donne l'addition de deux nombres :
: <math>( n_1 + biais ) + ( n_2 + biais ) = n_1 + n_2 + 2 \times biais</math>
Or, le résultat correct serait :
: <math>n_1 + n_2 + biais</math>
En effectuant l'addition telle quelle, le biais est compté deux fois. On doit donc le soustraire après l'addition pour obtenir le résultat correct.
Même chose pour la soustraction qui donne ceci :
: <math>(n_1 + biais) - (n_2 + biais) = n_1 - n_2</math>
Or, le résultat correct serait :
: <math> ( n_1 - n_2 ) + biais </math>
Il faut rajouter le biais pour obtenir l'exposant correct.
On a donc besoin de deux additionneurs/soustracteurs : un pour additionner/soustraire les représentations binaires des opérandes, et un autre pour ajouter/retirer le biais en trop/manquant.
==L'additionneur BCD==
Maintenant, voyons un additionneur qui additionne deux entiers au format BCD. Pour cela, nous allons devoir passer par deux étapes. La première est de créer un circuit capable d'additionneur deux chiffres BCD. Ensuite, nous allons voir comment enchaîner ces circuits pour créer un additionneur BCD complet.
===L'additionneur BCD qui fait l'opération chiffre par chiffre===
Nous allons commencer par voir un additionneur qui additionne deux chiffres en BCD, une sorte d'équivalent BCD de l'additionneur complet. Il fournit un résultat sur 4 bits et une retenue qui est mise à 1 si le résultat dépasse 10 (la limite d'un chiffre BCD). Les deux opérandes sont des chiffres BCD codés sur 4 bits et sont additionnés en binaire par un additionneur des plus normaux, similaire à ceux vus plus haut. Le résultat est alors un entier codé en binaire, sur 5 bits, qu'on corrige/convertit pour obtenir un chiffre BCD et une retenue sortante.
Pour corriger le résultat, une idée intuitive serait de prendre le résultat et de faire une division par 10. Le quotient donne la retenue, alors que le reste est le résultat, le chiffre BCD. Mais un circuit diviseur par 10 utilise beaucoup de portes logiques, ce qui ne vaut pas le coup. Une autre méthode détecte si le résultat est égal ou supérieur à 10, ce qui correspond à un "débordement" (on dépasse les limites d'un chiffre BCD). Si le résultat est plus petit que 10, il n'y a rien à faire : le résultat est bon et la retenue est de zéro. Par contre, si le résultat vaut 10 ou plus, il faut corriger le résultat et générer une retenue à 1.
Il faut donc ajouter un circuit qui détecte si le résultat est supérieur à 9, qui calcule directement la retenue. Ce circuit peut se fabriquer simplement à partir de sa table de vérité, ou en utilisant les techniques que nous verrons dans un chapitre ultérieur sur les comparateurs. La solution la plus simple est clairement d'utiliser la table de vérité, ce qui est très simple, assez pour être laissé en exercice au lecteur. Pour comprendre comment corriger le résultat, établissons une table de vérité qui associe le résultat et le résultat corrigé. L'entrée vaut au minimum 10 et au maximum 9 + 9 = 18. On considère la sortie comme un tout, la retenue étant un 5ème bit, le bit de poids fort.
{|class="wikitable"
|-
! colspan="5" | Entrée
!
! rowspan="10" |
! Retenue
! Résultat corrigé (sans retenue)
! interprétation de la sortie en binaire (retenue inclue)
|-
| 0 || 1 || 0 || 1 || 0
| (10)
| 1 || 0000
| (16)
|-
| 0 || 1 || 0 || 1 || 1
| (11)
| 1 || 0001
| (17)
|-
| 0 || 1 || 1 || 0 || 0
| (12)
| 1 || 0010
| (18)
|-
| 0 || 1 || 1 || 0 || 1
| (13)
| 1 || 0011
| (19)
|-
| 0 || 1 || 1 || 1 || 0
| (14)
| 1 || 0100
| (20)
|-
| 0 || 1 || 1 || 1 || 1
| (15)
| 1 || 0101
| (21)
|-
| 1 || 0 || 0 || 0 || 0
| (16)
| 1 || 0110
| (22)
|-
| 1 || 0 || 0 || 0 || 1
| (17)
| 1 || 0111
| (23)
|-
| 1 || 0 || 0 || 1 || 0
| (18)
| 1 || 1000
| (24)
|}
En analysant le tableau, on voit que pour corriger le résultat, il suffit d'ajouter 6. La raison est que le résultat déborde d'un nibble à 16 en binaire, mais à 10 en décimal : il suffit d'ajouter la différence entre les deux, à savoir 6, et le débordement binaire fait son travail. Donc, la correction après une addition est très simple : si le résultat dépasse 9, on ajoute 6.
On peut maintenant implémenter l'additionneur BCD, en combinant le comparateur avec 10, le circuit de correction, et l'additionneur. La première solution calcule deux versions du résultat : la version corrigée, la version normale. Le choix entre les deux est réalisée par un multiplexeur, commandé par le comparateur.
[[File:Additionneur BCD.png|centre|vignette|upright=1.5|Additionneur BCD]]
L'autre solution utilise un circuit commandable qui soit additionne 6, soit ne fait rien. Le choix entre les deux est commandé par le bit calculé par le comparateur.
[[File:Additionneur BCD, seconde version.png|centre|vignette|upright=1.5|Additionneur BCD, seconde version.]]
Une version alternative du circuit précédent est la suivante. Il contient deux additionneurs : un pour additionner les deux chiffres BCD, un autre pour additionner 6 si besoin. Le résultat du comparateur est directement utilisé pour générer l'opérande du second additionneur : 0 ou 6. Le circuit est simple à concevoir, mais gaspille beaucoup de circuit. Idéalement, il vaudrait mieux utiliser un circuit combinatoire d'addition avec une constante.
[[File:Adder 4 Bit BCD.svg|centre|vignette|upright=2|Additionneur BCD, circuit complet.]]
Pour obtenir un additionneur BCD complet, il suffit d’enchaîner les additionneurs précédents, comme on le ferait avec les additionneurs complets dans un additionneur à propagation de retenue. Au final, l'additionneur BCD est beaucoup plus compliqué qu'un additionneur normal, car il rajoute un comparateur ">9", un petit additionneur pour ajouter 6 et éventuellement d'autres circuits. De plus, il est difficile d'appliquer les optimisations disponibles sur les additionneurs non-BCD. Notamment, les circuits d'anticipation de retenue sont totalement à refaire et le résultat est relativement compliqué. C'est ce qui explique pourquoi le BCD a progressivement été abandonné au profit du binaire simple.
La soustraction en BCD se fait comme en binaire : le nombre à soustraire est remplacé par son complément, le circuit additionne le complément et l'autre opérande, le débordement d'entier fait que le résultat marche. Sauf qu'ici, le complément est un complément à 9. Il se calcule chiffre par chiffre : chaque chiffre est remplacé par (9 - le chiffre en question).
===L'additionneur BCD par ajustement décimal===
L'additionneur BCD précédent effectuait son travail chiffre BCD par chiffre BCD, mais il existe des additionneurs BCD qui font autrement. Sur les premiers processeurs x86, il n'y avait pas d'opération d'addition BCD proprement dit, seulement une addition binaire normale de 8, 16 ou 32 bits. Par contre, elle était secondée par une opération dite d''''ajustement décimal''' qui transformait un nombre binaire en nombre codé en BCD. L'opération d'ajustement décimal prenait un opérande de 8 bits codé en binaire et fournissait un résultat de la même taille, c'est à dire deux chiffres BCD. Effectuer une addition BCD demandait donc de faire deux opérations à la suite : une addition binaire simple, suivie par l'opération d'ajustement décimal. Cela permettait de gérer des nombres entiers en binaire usuel et des entiers BCD sans avoir deux instructions d'addition séparées pour les deux, sans compter que cela simplifiait aussi les circuits d'addition.
L'ajustement décimal s'effectue en ajoutant une constante bien précise à l'opérande à convertir en BCD. L'idée est que la constante est découpée en morceaux de 4 bits, correspondant chacun à un chiffre BCD de l'opérande, chaque morceau contenant soit un 0, soit 6. Cela permet d'ajouter soit 0, soit 6, à chaque chiffre BCD, et donc de le corriger. La propagation des retenues d'un chiffre à l'autre est effectuée automatiquement par l'addition binaire de la constante. L'opération d'ajustement décimal calcule automatiquement la constante. Elle découpe l'opérande en ''nibbles'', vérifie si chaque ''nibble'' est supérieur ou égal à 10, puis détermine la valeur de chaque ''nibble'' de la constante finale. Par exemple, si je prends l'opérande 1001 1110, le ''nibble'' de poids faible déborde, alors que celui de poids fort non. La constante sera donc 0000 0110 : 0x06. Inversement, si le ''nibble'' de poids fort déborde et pas celui de poids faible, la constante sera alors 0x60. Et la constante est de 0x66 si les deux ''nibbles'' débordent, de 0x00 si aucun ne déborde.
Le circuit d’ajustement décimal est donc composé de trois étapes : deux étapes pour calculer la constante, et un circuit d'addition pour additionner cette constante au nombre de départ. La première étape découpe l'opérande en morceaux de 4 bits, en chiffres BCD, et vérifie si chacun d'entre eux vaut 10 ou plus. La seconde étape prend les résultats de la première étape, et les combine pour calculer la constante. Enfin, on trouve l'addition finale, qui était réalisée par un circuit d'addition utilisé à la fois pour l'ajustement décimal et l'addition binaire. La différence entre une addition normale et une opération d'ajustement décimal tient dans le fait que les deux premières étapes sont désactivées dans une addition normale.
[[File:Additionneur BCD parallèle.png|centre|vignette|upright=1.5|Additionneur BCD parallèle]]
===L'additionneur biquinaire===
Les entiers BCD ne sont qu'un des encodages hybrides entre décimal et binaire. L'encodage biquinaire est l'un d'entre eux et nous allons faire un rappel rapide à ce sujet. Pour simplifier, un chiffre encodé en biquinaire est composé de deux parties : un bit, couplé à une partie quinaire encodée en représentation ''one-hot''. La partie quinaire encode un nombre allant de 0 à 4, ce qui prend 5 bits (0, 1, 2, 3 et 4). Le bit indique s'il faut ou non ajouter 5 à la valeur encodée par la partie quinaire. Ainsi, on peut coder tous les nombres de 0 à 9.
Additionner deux nombres de biquinaire demande donc d'additionner deux parties quinaires encodées en ''one-hot'' et d'additionner deux bits. Mais attention : il faut tenir compte de la retenue de l'addition des parties quinaires. Et idéalement, il faut aussi tenir compte d'une retenue entrante, provenant de l'addition de la colonne de chiffres précédente. Toute la difficulté vient de la création de l'additionneur ''one-hot''. Heureusement, vu qu'il n'y a que 4-5 bits à additionner, il est souvent fabriqué à partir de sa table de vérité.
[[File:Additionneur bi-quinaire.png|centre|vignette|upright=2|Additionneur bi-quinaire]]
Un avantage du biquinaire est que le calcul du complément à 9 est très simple. Il faut pour cela : inverser la partie binaire avec une porte NON, puis inverser l'ordre des bits de la partie quinaire. Concrètement, le bit de poids faible devient le bit de poids fort, et ainsi de suite. Par exemple, une partie quinaire 01000 devient 00010, 10000 devient 00001, 00100 ne change pas, etc. Le tout peut se calculer avec une porte NON et 5 multiplexeurs.
===L'additionneur BCD avec calculs intermédiaires en biquinaire===
L'ordinateur IBM 1401, un ancien ''mainframe'' des années 60, utilisait un additionneur BCD un peu particulier. Les nombres étaient encodés en BCD dans la mémoire de l'ordinateur, mais les circuits de calcul utilisaient la représentation biquinaire. Lors d'un calcul, le processeur de l'ordinateur traduisait les chiffres BCD en représentation biquinaire, faisait une addition en biquinaire, avant de traduire le résultat en BCD normal.
Pour être précis, l'IBM 1401 utilisait une variante du biquinaire. L'encodage biquinaire de l'IBM 1401 est le suivant : la partie binaire disait si le chiffre était pair ou non, la partie quinaire encodait les valeurs 0, 2, 4, 6 et 8. Le chiffre se calculait en additionnant la partie binaire (0 ou 1) au nombre pair encodé par la partie quinaire. Si l'IBM 1401 utilisait cette variante du biquinaire, c'est car elle donnait des circuits de conversion BCD-biquinaire plus économes en portes logiques et plus rapides.
La partie binaire est le bit de poids faible du chiffre BCD, la partie biquinaire est calculée par un simple décodeur qui prend en entrée le chiffre BCD, amputé de son bit de poids faible. La traduction inverse demande d'utiliser un encodeur, à la place du décodeur. Par contre, le circuit d'addition biquinaire était plus compliqué du fait de la gestion des retenues. L'addition des parties binaires et quinaires se faisait en parallèle, dans deux additionneurs séparés. Cependant, l'addition des parties binaire fournit une retenue, qu'il faut prendre en compte. Pour cela, l'IBM 1401 disposait d'un troisième additionneur qui fournissait le résultat final, encodé en biquinaire.
[[File:Additionneur bi-quinaire de l'IBM 1401.png|centre|vignette|upright=2|Additionneur biquinaire de l'IBM 1401]]
Une implémentation moderne demanderait d'utiliser des portes ET combinées à des portes OU, le circuit pouvant être construit simplement à partir de sa table de vérité. Sur l'IBM 1401, le circuit était cependant différent, en raison de l'utilisation de OU câblés, des croisements de fils qui fonctionnent comme des portes OU, que nous n'avons pas encore vu pour le moment, mais qui seront détaillés dans quelques chapitres. Les OU câblés étaient utilisés pour simplifier le design du circuit, mais demandaient des portes logiques spécifiques, ce qui collait avec le fait que ce ''mainframe'' utilisait des transistors en Germanium. L'implémentation exacte est décrite dans cet article de blog, mais je ne recommande sa lecture qu'à ceux qui savent ce qu'est un OU câblé :
* [https://www.righto.com/2015/10/qui-binary-arithmetic-how-1960s-ibm.html Qui-binary arithmetic: how a 1960s IBM mainframe does math].
==Les débordements d'entier lors d'une addition/soustraction==
Les instructions arithmétiques manipulent des entiers codés sur un nombre fixe de bits, qui ne peuvent prendre leurs valeurs que dans un intervalle. Pour les nombres positifs, un ordinateur qui code ses entiers sur n bits pourra coder tous les entiers allant de 0 à <math>2^n -1</math>. Pour les nombres négatifs, l'intervalle est différent et dépend de la représentation utilisée. Dans le cas général, l'ordinateur peut coder les valeurs comprises de <math>N_\text{min}</math> à <math>N_\text{max}</math>. Si le résultat d'un calcul sort de cet intervalle, il ne peut pas être représenté par l'ordinateur et il se produit ce qu'on appelle un '''débordement d'entier'''.
La '''valeur haute de débordement''' désigne la première valeur qui est trop grande pour être représentée par l'ordinateur. Par exemple, pour un ordinateur qui peut coder tous les nombres entre 0 et 7, la valeur haute de débordement est égale à 8. On peut aussi définir la '''valeur basse de débordement''', qui est la première valeur trop petite pour être codée par l'ordinateur. Par exemple, pour un ordinateur qui peut coder tous les nombres entre 8 et 250, la valeur basse de débordement est égale à 7. Pour les nombres entiers, la valeur haute de débordement vaut <math>N_\text{max} + 1</math> , alors que la valeur basse vaut <math>N_\text{min} - 1</math> (avec <math>N_\text{max}</math> et <math>N_\text{min}</math> respectivement la plus grande et la plus petite valeur codable par l'ordinateur).
===La correction des débordements d'entier : l'arithmétique saturée===
Quand un débordement d'entier survient, tous les circuits de calcul ne procèdent pas de la même manière. Dans les grandes lignes, il y a deux réactions possibles : soit on corrige automatiquement le résultat du débordement, soit on ne fait rien et on se contente de détecter le débordement.
Si le débordement n'est pas corrigé automatiquement, le circuit ne conserve que les bits de poids faibles du résultat. Les bits en trop sont simplement ignorés. On dit qu'on utilise l''''arithmétique modulaire'''. Le problème avec ce genre d'arithmétique, c'est qu'une opération entre deux grands nombres peut donner un résultat très petit. Par exemple, si je dispose de registres 4 bits et que je souhaite faire l'addition 1111 + 0010 (ce qui donne 15 + 2), le résultat est censé être 10001 (17), ce qui est un résultat plus grand que la taille d'un registre. En conservant les 4 bits de poids faible, j’obtiens 0001 (1). En clair, un résultat très grand est transformé en un résultat très petit. Cela peut poser problèmes si on travaille uniquement avec des nombres positifs, mais c'est aussi utilisé pour coder des nombres en complément à deux.
D'autres circuits utilisent ce qu'on appelle l''''arithmétique saturée''' : si un calcul génère un débordement, on arrondi le résultat au plus grand entier supporté par le circuit. Les circuits capables de calculer en arithmétique saturée sont un peu plus complexes, vu qu'il faut rajouter des circuits pour corriger le résultat en cas de débordement. Il suffit généralement de rajouter un ''circuit de saturation'', qui prend en entrée le résultat et le corrige en cas de débordement. Ce circuit de saturation met la valeur maximale en sortie si un débordement survient, mais se contente de recopier le résultat du calcul sur sa sortie s'il n'y a pas de débordement. Typiquement, il est composé d'une couche de multiplexeurs, qui sélectionnent quelle valeur mettre sur la sortie : soit le résultat du calcul, soit le plus grand nombre entier géré par le processeur, soit le plus petit (pour les nombres négatifs/soustractions).
L'arithmétique saturée est utilisée pour les additions et soustractions, mais c'est plus rare pour les multiplications/divisions. Une des raisons est que le résultat d'une addition/soustraction prend un bit de plus que le résultat, là où les multiplications doublent le nombre de bits. Quand une addition déborde, le résultat réel est proche de la valeur maximale codable. mais quand une multiplication déborde, le résultat peut parfois valoir 200 à 60000 fois plus que la valeur maximale codable. Les calculs avec une valeur saturée/corrigée sont donc crédibles pour une suite d'additions, mais pas pour une suite de multiplications.
===La détection des débordements entiers===
Quand un débordement d'entier a eu lieu, il vaut mieux que l'additionneur prévienne ! Pour cela, l'additionneur a une '''sortie de débordement''', parfois nommée ''Overflow'', dont la valeur indique si l'addition a généré un débordement d'entier ou non. Reste que détecter un débordement ne se fait pas de la même manière selon que l'on parle d'un additionneur non-signé ou signé.
Pour les additionneur non-signés, l'additionneur calcule un bit de plus que ce qui est supporté par l'ordinateur. Par exemple, un additionneur 32 bits fournit un résultat sur 33 bits, un débordement d'entier a lieu quand le 33ème bit est à 1. Précisément, la sortie de débordement n'est autre que la retenue finale, celle fournie par le dernier additionneur complet. Le seul type de débordement possible est un débordement par le haut, où le résultat dépasse la valeur maximale. Avec l'arithmétique saturée, le circuit de saturation consiste en une seule couche de multiplexeurs, voire en un circuit de mise à la valeur maximale tel que vu dans le chapitre sur les opérations bits à bits.
[[File:Gestion des débordements d'entiers.png|centre|vignette|upright=2.5|Gestion des débordements d'entiers lors d'une addition non-signée.]]
Pour les additionneurs non-signés, la gestion des débordements d'entiers dépend fortement de la représentation signée. Nous allons étudier le cas du complément à deux. Si vous vous rappelez le chapitre 1, les calculs sur des nombres en complètement à deux utilisent les règles de l'arithmétique modulaire, c'est une condition nécessaire. À priori, on peut penser que dans ces conditions, les débordements d'entiers sont une chose parfaitement normale, qui nous permet d'avoir des résultats corrects. Néanmoins, certains débordements d'entiers peuvent survenir malgré tout et produire des bugs assez ennuyeux.
Si l'on tient en compte les règles du complément à deux, on sait que le bit de poids fort (le plus à gauche) permet de déterminer si le nombre est positif ou négatif : il indique le signe du nombre. Tout se passe comme si les entiers en complément à deux étaient codés sur un bit de moins, et avaient leur longueur amputé du bit de poids fort. Si le résultat d'un calcul écrase le bit de poids fort, il y a un débordement d'entiers. Il existe une règle simple qui permet de détecter ces débordements d'entiers. L'addition de deux nombres positifs ne peut pas être un nombre négatif. Si on additionne deux nombres dont le bit de signe est à 0 et que le bit de signe du résultat est à 1, on est en face d'un débordement d'entiers. Même chose pour deux nombres négatifs : le résultat de l'addition ne peut pas être positif. On peut résumer cela en une phrase : si deux nombres de même signe sont ajoutés, un débordement a lieu quand le bit du signe du résultat a le signe opposé.
Modifier les circuits d'au-dessus pour qu'ils détectent les débordements en complément à deux est simple comme bonjour : il suffit créer un petit circuit combinatoire qui prenne en entrée les bits de signe des opérandes et du résultat, et qui fasse le calcul de l'indicateur de débordements. Si l'on rédige sa table de vérité, on doit se retrouver avec la table suivante :
{|class="wikitable"
|-
!Entrées
!Sortie
|-
|000||0
|-
|001||1
|-
|010||0
|-
|011||0
|-
|100||0
|-
|101||0
|-
|110||1
|-
|111||0
|}
L'équation de ce circuit est la suivante, avec <math>S_a</math> et <math>S_b</math> les signes des deux opérandes, et <math>C_i</math> la retenue de la colonne précédente :
: <math> ( S_a . S_b . \overline{C_i} ) + ( \overline{S_a} . \overline{S_b} . C_i )</math>
En simplifiant, on obtient alors :
: <math>( S_a . S_b ) \oplus C_i</math>
Or, il se trouve que <math>S_a . S_b</math> est tout simplement la retenue en sortie du dernier additionneur, que nous noterons <math>C_o</math>. On trouve donc :
: <math>C_o \oplus C_i</math>
Il suffit donc de faire un XOR entre la dernière retenue et la précédente pour obtenir le bit de débordement.
<noinclude>
{{NavChapitre | book=Fonctionnement d'un ordinateur
| prev=Les circuits de décalage et de rotation
| prevText=Les circuits de décalage et de rotation
| next=Les circuits de comparaison
| nextText=Les circuits de comparaison
}}
</noinclude>
obm3bd41jg91kimw9k9zfqakk1au9n5
Wikilivres:GUS2Wiki
4
78643
768811
768214
2026-06-27T20:04:16Z
Alexis Jazz
81580
Updating gadget usage statistics from [[Special:GadgetUsage]] ([[phab:T121049]])
768811
wikitext
text/x-wiki
{{#ifexist:Project:GUS2Wiki/top|{{/top}}|This page provides a historical record of [[Special:GadgetUsage]] through its page history. To get the data in CSV format, see wikitext. To customize this message or add categories, create [[/top]].}}
Les données suivantes sont en cache et ont été mises à jour pour la dernière fois le 2026-06-25T11:30:24Z. {{PLURAL:5000|1=Un seul résultat|5000 résultats}} au maximum {{PLURAL:5000|est disponible|sont disponibles}} dans le cache.
{| class="sortable wikitable"
! Gadget !! data-sort-type="number" | Nombre d’utilisateurs !! data-sort-type="number" | Utilisateurs actifs
|-
|AncreTitres || 34 || 0
|-
|ArchiveLinks || 13 || 0
|-
|Barre de luxe || 36 || 2
|-
|BoutonsLiens || 42 || 0
|-
|CategoryAboveAll || 20 || 0
|-
|CategorySeparator || 23 || 0
|-
|CoinsArrondis || 99 || 1
|-
|CollapseSidebox || 36 || 1
|-
|CouleurContributions || 33 || 1
|-
|CouleursLiens || 26 || 1
|-
|DeluxeAdmin || 5 || 1
|-
|DeluxeEdit || 36 || 1
|-
|DeluxeHistory || 46 || 2
|-
|DeluxeImport || 19 || 1
|-
|DeluxeRename || 16 || 1
|-
|DeluxeSummary || 37 || 2
|-
|DevTools || 18 || 1
|-
|DirectPageLink || 26 || 1
|-
|Emoticons || 44 || 1
|-
|EmoticonsToolbar || 45 || 1
|-
|FastRevert || 37 || 2
|-
|FixArrayAltLines || 23 || 1
|-
|FlecheHaut || 66 || 1
|-
|GoogleTrans || 28 || 0
|-
|HotCats || 81 || 5
|-
|JournalDebug || 22 || 1
|-
|JournalEnTable || 3 || 1
|-
|LeftPaneSwitch || 6 || 1
|-
|ListeABordure || 30 || 1
|-
|LiveRC || 30 || 0
|-
|LocalLiveClock || 27 || 1
|-
|Logo || 42 || 0
|-
|MobileView || 18 || 2
|-
|NavigAdmin || 38 || 0
|-
|OngletEditCount || 45 || 2
|-
|OngletEditZeroth || 67 || 1
|-
|OngletGoogle || 29 || 1
|-
|OngletPurge || 56 || 3
|-
|OptimizedSuivi || 18 || 0
|-
|Popups || 61 || 1
|-
|RenommageCategorie || 6 || 2
|-
|RestaurationDeluxe || 12 || 1
|-
|RevertDiff || 35 || 4
|-
|ScriptAutoVersion || 17 || 1
|-
|ScriptSidebox || 32 || 0
|-
|ScriptToolbar || 43 || 0
|-
|SisterProjects || 17 || 1
|-
|SkinPreview || 4 || 1
|-
|Smart patrol || 2 || 1
|-
|SourceLanguage || 24 || 1
|-
|SousPages || 57 || 3
|-
|SpaceToolbar || 30 || 0
|-
|TableUnicode || 56 || 0
|-
|Tableau || 67 || 1
|-
|TitreDeluxe || 56 || 1
|-
|TitreHierarchique || 17 || 0
|-
|UTCLiveClock || 27 || 0
|-
|UnicodeEditRendering || 29 || 1
|-
|WikEd || 30 || 0
|-
|autonum || 2 || 0
|-
|massblock || 3 || 1
|-
|monBrouillon || 20 || 2
|-
|perpagecustomization || 17 || 1
|-
|recentchangesbox || 15 || 0
|-
|searchFocus || 19 || 0
|-
|searchbox || 30 || 2
|}
* [[Spécial:GadgetUsage]]
* [[m:Meta:GUS2Wiki/Script|GUS2Wiki]]
<!-- data in CSV format:
AncreTitres,34,0
ArchiveLinks,13,0
Barre de luxe,36,2
BoutonsLiens,42,0
CategoryAboveAll,20,0
CategorySeparator,23,0
CoinsArrondis,99,1
CollapseSidebox,36,1
CouleurContributions,33,1
CouleursLiens,26,1
DeluxeAdmin,5,1
DeluxeEdit,36,1
DeluxeHistory,46,2
DeluxeImport,19,1
DeluxeRename,16,1
DeluxeSummary,37,2
DevTools,18,1
DirectPageLink,26,1
Emoticons,44,1
EmoticonsToolbar,45,1
FastRevert,37,2
FixArrayAltLines,23,1
FlecheHaut,66,1
GoogleTrans,28,0
HotCats,81,5
JournalDebug,22,1
JournalEnTable,3,1
LeftPaneSwitch,6,1
ListeABordure,30,1
LiveRC,30,0
LocalLiveClock,27,1
Logo,42,0
MobileView,18,2
NavigAdmin,38,0
OngletEditCount,45,2
OngletEditZeroth,67,1
OngletGoogle,29,1
OngletPurge,56,3
OptimizedSuivi,18,0
Popups,61,1
RenommageCategorie,6,2
RestaurationDeluxe,12,1
RevertDiff,35,4
ScriptAutoVersion,17,1
ScriptSidebox,32,0
ScriptToolbar,43,0
SisterProjects,17,1
SkinPreview,4,1
Smart patrol,2,1
SourceLanguage,24,1
SousPages,57,3
SpaceToolbar,30,0
TableUnicode,56,0
Tableau,67,1
TitreDeluxe,56,1
TitreHierarchique,17,0
UTCLiveClock,27,0
UnicodeEditRendering,29,1
WikEd,30,0
autonum,2,0
massblock,3,1
monBrouillon,20,2
perpagecustomization,17,1
recentchangesbox,15,0
searchFocus,19,0
searchbox,30,2
-->
k2otydpmsevnb5uloap27gvzd77o8mk
Dictionnaire de philosophie/E
0
83024
768828
767508
2026-06-28T05:06:19Z
PandaMystique
119061
768828
wikitext
text/x-wiki
{{DicoPhilo |E}}
<div style="display: grid; grid-template-columns: repeat(auto-fit, minmax(280px, 1fr)); gap: 2em; width: 100%; margin-bottom: 2em;">
<div style="padding: 1.5em; background: linear-gradient(135deg, #ffffff40 0%, #e0e4e840 100%); border-radius: 10px; box-shadow: 0 3px 10px rgba(0,0,0,0.1);">
:{{Page|Énésidème}}
:''Esthétique''
::[[Dictionnaire de philosophie/Esthétique dans la pensée chinoise|Esthétique dans la pensée chinoise]]
:{{Page|Effet}}
:{{Page|Égalité}}
:{{Page|Émergence}}
:{{Page|Émotion}}
:{{Page|Empédocle}}
:[[Dictionnaire de philosophie/Empirisme|Empirisme]]
:[[Dictionnaire de philosophie/Épistémologie|Épistémologie]]
:{{Page|Erreur}}
</div>
<div style="padding: 1.5em; background: linear-gradient(135deg, #ffffff40 0%, #e0e4e840 100%); border-radius: 10px; box-shadow: 0 3px 10px rgba(0,0,0,0.1);">
:{{Page|Espèce}}
:{{Page|Esprit}}
:{{Page|Essence}}
:''État''
:— {{Page|État de droit}}
:— {{Page|État de nature}}
:{{Page|Éthique}}
:{{Page|Euthanasie}}
:{{Page|Événement}}
:{{Page|Existence}}
:{{Page|Expérience}}
: — {{Page|Expérience de pensée}}
</div>
<div style="padding: 1.5em; background: linear-gradient(135deg, #ffffff40 0%, #e0e4e840 100%); border-radius: 10px; box-shadow: 0 3px 10px rgba(0,0,0,0.1);">
:{{Page|Explication}}
:{{Page|Extension}}
</div>
</div>
{{PhiloRecherche}}
{{autocat}}
cus7pmdwxbcfxiuulj3d0mvivnw8jf6
Latin/Vocabulaire/Verba Latina frequentissima/051-075
0
83945
768812
767889
2026-06-28T03:41:27Z
PandaMystique
119061
768812
wikitext
text/x-wiki
{{Sous-pages|Latin/Vocabulaire/Verba Latina frequentissima}}
== iam ==
(adv.) : déjà, désormais
''Exempla gradatim digesta''
# ''Iam venio!'' (J’arrive !)
# ''Iam diu te exspecto.'' (Je t’attends depuis longtemps.) Nota : iam diu + présent.
# ''Non iam spes ulla salutis erat.'' (Il n’y avait plus aucun espoir de salut.) Nota : non iam = « ne... plus ».
''Exempla elocutionis''
* ''Iamque vale!'' (Et maintenant, adieu !) (Virgile)
*: ''Commentaire :'' iamque marque l’instant du basculement ; dans la bouche de l’ombre de Créuse, l’adverbe temporel porte tout le pathétique de la séparation définitive.
* ''Hostes iam iamque aderunt.'' (Les ennemis vont arriver d’un instant à l’autre.)
*: ''Commentaire :'' gémination iam iamque = l’imminence haletante ; la répétition mime les regards jetés vers l’horizon.
== quam ==
(adv. / coni.) : que, combien ; que (comparaison)
''Exempla gradatim digesta''
# ''Quam pulchra est rosa!'' (Que la rose est belle !)
# ''Doctior est quam frater.'' (Il est plus savant que son frère.)
# ''Quam quisque norit artem, in hac se exerceat.'' (Que chacun s’exerce dans l’art qu’il connaît.) (Cicéron) Nota : attraction de l’antécédent.
''Exempla elocutionis''
* ''Quam multa, quam paucis!'' (Que de choses, en combien peu de mots !)
*: ''Commentaire :'' double exclamation antithétique, elle-même réduite à quatre mots ; l’éloge de la brevitas qui s’applique à lui-même.
* ''Quam primum veni!'' (Viens au plus tôt !)
*: ''Commentaire :'' quam + superlatif = le degré maximal possible ; tour usuel mais expressif de l’urgence épistolaire.
== autem ==
(coni.) : or, mais, quant à
''Exempla gradatim digesta''
# ''Ego maneo, tu autem abis.'' (Moi je reste, toi en revanche tu pars.)
# ''Corpus mortale est, animus autem immortalis.'' (Le corps est mortel, l’âme en revanche est immortelle.)
# ''Quod autem ad amicitiam pertinet, postea dicam.'' (Quant à ce qui concerne l’amitié, j’en parlerai plus tard.) Nota : quod ad... pertinet.
''Exempla elocutionis''
* ''Quid autem est mors?'' (Mais qu’est-ce donc que la mort ?)
*: ''Commentaire :'' autem introduit la question dialectique, le tournant de l’examen ; c’est la particule du dialogue philosophique, qui relance sans opposer brutalement.
* ''Hoc autem ipsum, quod dico, difficile est dictu.'' (Mais cela même que je dis est difficile à dire.)
*: ''Commentaire :'' autem de la reprise réflexive ; la prose philosophique revient sur ses propres mots, et le supin dictu fait écho à dico.
== de ==
(praep. + abl.) : au sujet de ; du haut de, de
''Exempla gradatim digesta''
# ''De monte descendimus.'' (Nous descendons de la montagne.)
# ''De pace agitur.'' (Il est question de la paix.) Nota : agitur de, impersonnel.
# ''De summa rerum senatus decrevit.'' (Le sénat statua sur la situation générale.) Nota : summa rerum = « l’ensemble des affaires ».
''Exempla elocutionis''
* ''De amicitia, de senectute, de officiis Cicero scripsit.'' (Cicéron a écrit sur l’amitié, la vieillesse, les devoirs.)
*: ''Commentaire :'' de + ablatif, formule des titres de traités, calque du grec peri ; toute une bibliothèque philosophique tient dans cette préposition.
* ''De nihilo nihil fit.'' (Rien ne naît de rien.) (d’après Lucrèce)
*: ''Commentaire :'' polyptote nihilo / nihil aux deux pôles de la phrase ; l’axiome épicurien frappé comme une monnaie, la forme close disant la clôture de l’être.
== noster ==
''noster, nostra, nostrum'' (pron. poss.) : notre
''Exempla gradatim digesta''
# ''Noster canis latrat.'' (Notre chien aboie.)
# ''Nostri maiores rem publicam sapienter constituerunt.'' (Nos ancêtres ont organisé la république avec sagesse.)
# ''Nostra interest hoc quam primum scire.'' (Il est de notre intérêt de le savoir au plus tôt.) Nota : interest + nostra.
''Exempla elocutionis''
* ''Plato noster haec docet.'' (Notre cher Platon enseigne ceci.)
*: ''Commentaire :'' possessif d’affection et d’appartenance d’école (« notre Platon » = celui que nous lisons et aimons) ; tour fréquent chez Cicéron et Sénèque.
* ''Nostri acriter restiterunt.'' (Les nôtres résistèrent avec acharnement.) (d’après César)
*: ''Commentaire :'' nostri substantivé, langue du communiqué militaire ; le possessif enrôle le lecteur dans le camp romain sans même le nommer.
== meus ==
''meus, mea, meum'' (pron. poss.) : mon, ma
''Exempla gradatim digesta''
# ''Mea soror cantat.'' (Ma sœur chante.)
# ''Mea quidem sententia, erras.'' (À mon avis du moins, tu te trompes.)
# ''Meum est officium te monere, tuum parere.'' (Mon devoir est de t’avertir, le tien d’obéir.)
''Exempla elocutionis''
* ''Mi fili!'' (Mon fils !)
*: ''Commentaire :'' vocatif mi, forme propre à l’adresse affectueuse ; un seul phonème de différence avec meus, mais tout un registre de tendresse.
* ''Mea mihi conscientia pluris est quam omnium sermo.'' (Ma conscience compte plus pour moi que les propos de tous.) (Cicéron)
*: ''Commentaire :'' redoublement mea mihi en tête ; le possessif et le datif concentrent la phrase sur l’intériorité, contre la rumeur (sermo) rejetée à la fin.
== tuus ==
''tuus, tua, tuum'' (pron. poss.) : ton, ta
''Exempla gradatim digesta''
# ''Tuus liber in mensa iacet.'' (Ton livre est posé sur la table.)
# ''Tua sponte hoc fecisti.'' (Tu as fait cela de ton propre gré.)
# ''Non tuum est de hac re iudicare.'' (Ce n’est pas à toi de juger de cette affaire.)
''Exempla elocutionis''
* ''Vale. Tuus Marcus.'' (Porte-toi bien. Ton Marcus.)
*: ''Commentaire :'' formule de clôture épistolaire ; le possessif seul, sans verbe, dit le lien. Degré zéro et degré intime de la syntaxe.
* ''Quae tua est humanitas, ignosces.'' (Avec la bonté qui est la tienne, tu pardonneras.)
*: ''Commentaire :'' relative incise de courtoisie, tournure cicéronienne par excellence ; on enrobe la requête dans l’éloge anticipé.
== venio ==
''venio, venire, veni, ventum'' (v.) : venir
''Exempla gradatim digesta''
# ''Amici veniunt.'' (Les amis viennent.)
# ''Mihi in mentem venit veteris amicitiae.'' (Le souvenir de notre vieille amitié me revient à l’esprit.) Nota : venit in mentem + génitif.
# ''Ventum est ad summum montis.'' (On parvint au sommet de la montagne.) Nota : passif impersonnel.
''Exempla elocutionis''
* ''Veni, vidi, vici.'' (Je suis venu, j’ai vu, j’ai vaincu.) (César)
*: ''Commentaire :'' asyndète, allitération, isocolie, trois parfaits de même structure ; la vitesse du style égale la vitesse de la campagne. Sommet du laconisme latin.
* ''Venit summa dies.'' (Il est venu, le jour suprême.) (Virgile)
*: ''Commentaire :'' verbe en tête, position rare et donc dramatique : l’événement surgit avant son sujet ; l’adjectif summa achève le vers funèbre de Troie.
== tantus ==
''tantus, tanta, tantum'' (adi.) : si grand
''Exempla gradatim digesta''
# ''Tanta turba in foro est!'' (Il y a une si grande foule au forum !)
# ''Tanta vis tempestatis fuit ut naves delerentur.'' (La violence de la tempête fut telle que les navires furent détruits.)
# ''Tantae molis erat Romanam condere gentem.'' (Tant il était laborieux de fonder la nation romaine.) (Virgile) Nota : génitif de qualité.
''Exempla elocutionis''
* ''Tantum religio potuit suadere malorum.'' (Tant la superstition a pu conseiller de crimes.) (Lucrèce)
*: ''Commentaire :'' le génitif partitif malorum est rejeté à l’extrême fin du vers : la disjonction tantum... malorum suspend le jugement jusqu’au mot le plus noir. Vers-réquisitoire célèbre.
* ''Tanto melior!'' (À la bonne heure ! / Bravo !)
*: ''Commentaire :'' ablatif de mesure exclamatif, registre parlé ; la langue familière abrège là où la langue écrite construirait.
== nullus ==
''nullus, nulla, nullum'' (adi.) : aucun, nul
''Exempla gradatim digesta''
# ''Nullus discipulus abest.'' (Aucun élève n’est absent.)
# ''Nullo modo hoc fieri potest.'' (Cela ne peut se faire d’aucune manière.)
# ''Nulla dies sine linea.'' (Pas un jour sans une ligne.) (adage rapporté par Pline l’Ancien)
''Exempla elocutionis''
* ''Nullius addictus iurare in verba magistri.'' (Tenu de jurer sur les mots d’aucun maître.) (Horace)
*: ''Commentaire :'' nullius en tête, disjoint de magistri par tout le vers ; la déclaration d’indépendance philosophique commence par le mot de la négation d’appartenance.
* ''Nullus sum!'' (Je suis perdu !) (Plaute)
*: ''Commentaire :'' hyperbole comique : « je ne suis plus personne » = « c’en est fait de moi » ; nullus prédicat d’un sujet personnel, audace de la langue parlée.
== locus ==
''locus, loci'' (subst. m.) : lieu, place
''Exempla gradatim digesta''
# ''Hic locus pulcher est.'' (Ce lieu est beau.)
# ''Loco cedere turpe est militi.'' (Reculer est honteux pour un soldat.) Nota : loco cedere = « quitter sa position ».
# ''Preces apud eum locum non habuerunt.'' (Les prières n’eurent aucun effet sur lui.) Nota : locum habere = « avoir prise ».
''Exempla elocutionis''
* ''Hi sunt loci communes oratorum.'' (Ce sont là les lieux communs des orateurs.)
*: ''Commentaire :'' locus, terme technique de la rhétorique (réservoir d’arguments) ; le sens spatial passé en métaphore institutionnalisée, jusqu’à notre « lieu commun ».
* ''Est locus, Hesperiam Grai cognomine dicunt.'' (Il est un lieu, les Grecs le nomment Hespérie.) (Virgile)
*: ''Commentaire :'' est locus, formule d’ouverture de l’ecphrasis épique ; le récit s’arrête, la géographie commence. Module narratif hérité d’Homère.
== manus ==
''manus, manus'' (subst. f.) : main ; troupe
''Exempla gradatim digesta''
# ''Manus lavo.'' (Je me lave les mains.)
# ''Parva manus militum urbem defendit.'' (Une petite troupe de soldats défendit la ville.) Nota : manus = « troupe ».
# ''Hostes victi manus dederunt.'' (Les ennemis vaincus se rendirent.) Nota : manus dare = « se rendre ».
''Exempla elocutionis''
* ''Manus manum lavat.'' (Une main lave l’autre.) (Sénèque, Pétrone)
*: ''Commentaire :'' polyptote proverbial ; la réciprocité intéressée dite par la grammaire de la réflexivité. Registre populaire assumé.
* ''Res in manibus est.'' (L’affaire est en cours. / L’affaire nous occupe.)
*: ''Commentaire :'' métaphore de la proximité immédiate : ce qu’on a « dans les mains » est ce qu’on traite ; idiome de la prose d’affaires et des lettres.
== bellum ==
''bellum, belli'' (subst. n.) : guerre
''Exempla gradatim digesta''
# ''Bellum malum est.'' (La guerre est un mal.)
# ''Bello confecto, milites domum redierunt.'' (La guerre achevée, les soldats rentrèrent chez eux.) Nota : ablatif absolu.
# ''Bellum nec timendum nec provocandum est.'' (Il ne faut ni craindre la guerre ni la provoquer.) Nota : double adjectif verbal.
''Exempla elocutionis''
* ''Bellum omnium contra omnes.'' (La guerre de tous contre tous.) (Hobbes)
*: ''Commentaire :'' formule néo-latine (XVIIe siècle), à signaler comme telle dans un dictionnaire de philosophie ; le polyptote omnium / omnes lui donne sa frappe pseudo-antique.
* ''Bella, horrida bella cerno.'' (Des guerres, d’horribles guerres, voilà ce que je vois.) (Virgile)
*: ''Commentaire :'' gémination prophétique de bella, épithète expressive horrida ; la Sibylle parle par répétitions, le style mime la transe.
== causa ==
''causa, causae'' (subst. f.) : cause, raison ; procès
''Exempla gradatim digesta''
# ''Causa morbi ignota est.'' (La cause de la maladie est inconnue.)
# ''Honoris causa venit.'' (Il est venu pour rendre hommage.) Nota : causa postposé + génitif.
# ''Multae sunt causae cur hoc credam.'' (Il y a bien des raisons pour que je le croie.) Nota : cur + subjonctif.
''Exempla elocutionis''
* ''Et propter vitam vivendi perdere causas.'' (Et pour sauver sa vie, perdre les raisons de vivre.) (Juvénal)
*: ''Commentaire :'' paradoxe construit sur l’opposition vitam / vivendi causas ; la satire morale atteint ici la densité d’une thèse philosophique.
* ''Reus causam dixit.'' (L’accusé a plaidé sa cause.)
*: ''Commentaire :'' causam dicere, locution technique de la langue judiciaire ; le style, c’est aussi savoir reconnaître les syntagmes figés des institutions.
== pars ==
''pars, partis'' (subst. f.) : partie, côté
''Exempla gradatim digesta''
# ''Partem panis accipio.'' (Je reçois une part de pain.)
# ''Maxima pars hominum hoc credit.'' (La plupart des hommes le croient.)
# ''Partes boni civis agere debemus.'' (Nous devons jouer le rôle d’un bon citoyen.) Nota : partes agere.
''Exempla elocutionis''
* ''Pars sanitatis velle sanari fuit.'' (Vouloir guérir était déjà une part de la guérison.) (Sénèque)
*: ''Commentaire :'' maxime à pointe : l’infinitif sujet (velle sanari) et le parfait gnomique ; la psychologie morale condensée en sept mots.
* ''Pro mea parte adiuvabo.'' (J’aiderai pour ma part.)
*: ''Commentaire :'' formule de contribution mesurée ; la modestie codifiée de la prose de collaboration, sans figure mais non sans politesse.
== urbs ==
''urbs, urbis'' (subst. f.) : ville
''Exempla gradatim digesta''
# ''Urbs magna est Roma.'' (Rome est une grande ville.)
# ''Urbe capta, cives fugerunt.'' (La ville prise, les citoyens s’enfuirent.) Nota : ablatif absolu.
# ''Romae, in urbe omnium pulcherrima, diu vixit.'' (Il vécut longtemps à Rome, la plus belle des villes.) Nota : locatif + apposition.
''Exempla elocutionis''
* ''Romanae spatium est urbis et orbis idem.'' (L’espace de la ville de Rome et celui du monde ne font qu’un.) (Ovide)
*: ''Commentaire :'' paronomase urbis / orbis, un seul phonème d’écart pour dire l’idéologie impériale ; le calembour élevé à la dignité de doctrine.
* ''In Urbem redire cupio.'' (Je désire rentrer à Rome.)
*: ''Commentaire :'' antonomase : urbs absolu désigne Rome seule ; pour un Romain, il n’y a qu’une Ville, et l’article manquant du latin est suppléé par l’évidence.
== capio ==
''capio, capere, cepi, captum'' (v.) : prendre, saisir
''Exempla gradatim digesta''
# ''Piscem cepi.'' (J’ai pris un poisson.)
# ''Consilium cepimus fugiendi.'' (Nous avons formé le projet de fuir.) Nota : consilium capere + génitif du gérondif.
# ''Amore captus, omnia reliquit.'' (Saisi par l’amour, il abandonna tout.)
''Exempla elocutionis''
* ''Exordium captat benevolentiam iudicum.'' (L’exorde cherche à capter la bienveillance des juges.)
*: ''Commentaire :'' capere et son fréquentatif captare ont fourni le vocabulaire technique de la rhétorique (captatio benevolentiae) ; la chasse comme métaphore du discours.
* ''Nec te Troia capit.'' (Troie même est trop petite pour toi.) (Virgile)
*: ''Commentaire :'' capere = « contenir » ; l’hyperbole héroïque loge la grandeur d’un homme dans un verbe d’espace. Litote et démesure à la fois.
== populus ==
''populus, populi'' (subst. m.) : peuple
''Exempla gradatim digesta''
# ''Populus in foro convenit.'' (Le peuple se rassemble au forum.)
# ''Senatus populusque Romanus legatos misit.'' (Le sénat et le peuple romain envoyèrent des ambassadeurs.)
# ''Salus populi suprema lex esto.'' (Que le salut du peuple soit la loi suprême.) (Cicéron) Nota : esto, impératif futur.
''Exempla elocutionis''
* ''Maiestas populi Romani laesa est.'' (La majesté du peuple romain a été lésée.)
*: ''Commentaire :'' génitif officiel des formules d’État (populi Romani) ; style juridique, où chaque mot est pesé et aucun n’est imagé.
* ''Populus me sibilat, at mihi plaudo.'' (Le peuple me siffle, mais moi je m’applaudis.) (Horace)
*: ''Commentaire :'' antithèse de la foule et de l’individu (populus / mihi, sibilat / plaudo) ; le sage se passe du suffrage du nombre, et le balancement de la phrase oppose le jugement public à la conscience de soi.
== idem ==
''idem, eadem, idem'' (pron.) : le même
''Exempla gradatim digesta''
# ''Eundem librum lego.'' (Je lis le même livre.)
# ''Eadem nocte profecti sumus.'' (Nous sommes partis la même nuit.)
# ''Idem velle atque idem nolle, ea demum firma amicitia est.'' (Vouloir et refuser les mêmes choses, voilà la solide amitié.) (Salluste)
''Exempla elocutionis''
* ''Vultus erat semper idem.'' (Son visage était toujours le même.)
*: ''Commentaire :'' l’identité d’humeur du sage (on le disait de Socrate) ; idem prédicat, sans image ni emphase : la constance dite par la constance du style.
* ''Vir doctissimus idemque modestissimus.'' (Un homme très savant et tout aussi modeste.)
*: ''Commentaire :'' idemque cumule les qualités en signalant que leur réunion est remarquable ; l’éloge procède par addition étonnée.
== corpus ==
''corpus, corporis'' (subst. n.) : corps
''Exempla gradatim digesta''
# ''Corpus meum fessum est.'' (Mon corps est fatigué.)
# ''Mens sana in corpore sano optanda est.'' (Il faut souhaiter un esprit sain dans un corps sain.) (d’après Juvénal)
# ''Animus corpori imperat sicut rex civibus suis.'' (L’âme commande au corps comme un roi à ses citoyens.) (d’après Salluste)
''Exempla elocutionis''
* ''Res publica unum corpus est.'' (L’État est un seul corps.)
*: ''Commentaire :'' métaphore organiciste, fondatrice de toute une tradition politique (l’apologue de Ménénius Agrippa) ; l’image fait la doctrine.
* ''Corpora prima Lucretius ea vocat quae dividi non possunt.'' (Lucrèce appelle corps premiers ceux qui ne peuvent être divisés.)
*: ''Commentaire :'' corpora prima, périphrase latine pour les atomes grecs ; le traducteur philosophe préfère la périphrase native à l’emprunt. Leçon de méthode pour tout lexique philosophique latin.
== vita ==
''vita, vitae'' (subst. f.) : vie
''Exempla gradatim digesta''
# ''Vita pulchra est.'' (La vie est belle.)
# ''Vitam rusticam poetae laudant.'' (Les poètes louent la vie des champs.)
# ''Non est vivere sed valere vita est.'' (Vivre, ce n’est pas être en vie, c’est être en bonne santé.) (Martial)
''Exempla elocutionis''
* ''O vita misero longa, felici brevis!'' (Ô vie, longue pour le malheureux, brève pour l’heureux !) (Publilius Syrus)
*: ''Commentaire :'' apostrophe à la vie, double antithèse en chiasme (misero longa / felici brevis) ; la sentence dramatique faite pour être déclamée.
* ''Vitam beatam vivere omnes cupiunt.'' (Tous désirent vivre une vie heureuse.)
*: ''Commentaire :'' figure étymologique vitam vivere ; le complément interne, banal en grec, garde en latin une légère solennité. Vita beata, terme technique de l’éthique antique.
== modus ==
''modus, modi'' (subst. m.) : manière, mesure
''Exempla gradatim digesta''
# ''Quo modo vales?'' (Comment te portes-tu ?)
# ''Servandus est modus in omnibus rebus.'' (Il faut garder la mesure en toutes choses.)
# ''Eius modi homines vitandi sunt.'' (Il faut éviter les hommes de cette espèce.) Nota : eius modi = « de ce genre ».
''Exempla elocutionis''
* ''Est modus in rebus, sunt certi denique fines.'' (Il y a une mesure dans les choses, il y a enfin des limites précises.) (Horace)
*: ''Commentaire :'' parallélisme est / sunt, et denique qui feint l’impatience ; la doctrine du juste milieu énoncée sur le ton de l’évidence excédée.
* ''Modo ridet, modo flet.'' (Tantôt il rit, tantôt il pleure.)
*: ''Commentaire :'' modo... modo, anaphore alternative (cf. nunc... nunc) ; l’instabilité d’humeur peinte par le balancement de la phrase.
== nomen ==
''nomen, nominis'' (subst. n.) : nom
''Exempla gradatim digesta''
# ''Nomen meum scribo.'' (J’écris mon nom.)
# ''Nomine tantum rex erat, re vera servus.'' (Il n’était roi que de nom ; en réalité, il était esclave.) Nota : nomine / re opposés.
# ''Nomen atque omen.'' (Le nom est un présage.) (Plaute)
''Exempla elocutionis''
* ''Stat magni nominis umbra.'' (Il demeure, ombre d’un grand nom.) (Lucain)
*: ''Commentaire :'' métaphore de l’ombre pour la grandeur survivante d’elle-même (Pompée) ; le génitif magni nominis enchâssé fait tout le vers. L’un des plus beaux raccourcis de la poésie latine.
* ''Amicitiae nomine multa peccantur.'' (Bien des fautes se commettent au nom de l’amitié.)
*: ''Commentaire :'' nomine = « sous couvert de » ; l’ablatif seul suffit à dénoncer l’écart entre le mot et la chose, thème moraliste par excellence.
== miles ==
''miles, militis'' (subst. m.) : soldat
''Exempla gradatim digesta''
# ''Miles gladium gerit.'' (Le soldat porte un glaive.)
# ''Milites urbi praesidio erant.'' (Les soldats servaient de garnison à la ville.) Nota : double datif.
# ''Militis est parere, imperatoris providere.'' (Au soldat d’obéir, au général de prévoir.)
''Exempla elocutionis''
* ''Miles Romanus laborare didicit.'' (Le soldat romain a appris à peiner.)
*: ''Commentaire :'' singulier collectif des historiens ; miles = l’armée entière, vue comme un type. Stylisation épique de la prose historique.
* ''Miles gloriosus a Plauto deridetur.'' (Le soldat fanfaron est raillé par Plaute.)
*: ''Commentaire :'' miles gloriosus, type comique devenu titre ; l’épithète suffit à camper le personnage. Le style crée des emplois figés que la culture transmet.
== mors ==
''mors, mortis'' (subst. f.) : mort
''Exempla gradatim digesta''
# ''Mors vitae finis est.'' (La mort est la fin de la vie.)
# ''Morte patris audita, domum rediit.'' (À la nouvelle de la mort de son père, il rentra chez lui.) Nota : ablatif absolu.
# ''Mors, ut Stoicis videtur, nec bonum nec malum est.'' (La mort, selon les stoïciens, n’est ni un bien ni un mal.) Nota : ut videtur incise.
''Exempla elocutionis''
* ''Mors ultima linea rerum est.'' (La mort est la ligne d’arrivée des choses.) (Horace)
*: ''Commentaire :'' métaphore empruntée au cirque (la ligne blanche au bout de la piste) ; l’image sportive apprivoise l’effroi. Définition par déplacement.
* ''Pallida Mors aequo pulsat pede pauperum tabernas regumque turris.'' (La pâle Mort frappe d’un pied égal les cabanes des pauvres et les tours des rois.) (Horace)
*: ''Commentaire :'' personnification, allitération en p, antithèse sociale pauperum / regum (turris, accusatif pluriel archaïque) ; l’égalité devant la mort dite par une symétrie parfaite des images.
{{#invoke:Sous-Page|sousPage|taille préfixe=0.7em|taille titre=115%|italique=non}}
{{AutoCat}}
1upquee8yl4fn97m06y8jzsd4myoc3j
768813
768812
2026-06-28T03:43:05Z
PandaMystique
119061
768813
wikitext
text/x-wiki
{{Sous-pages|Latin/Vocabulaire/Verba Latina frequentissima}}
<div style="text-align: center; font-size: 90%;">
[[#iam|iam]] · [[#quam|quam]] · [[#autem|autem]] · [[#de|de]] · [[#noster|noster]] · [[#meus|meus]] · [[#tuus|tuus]] · [[#venio|venio]] · [[#tantus|tantus]] · [[#nullus|nullus]] · [[#locus|locus]] · [[#manus|manus]] · [[#bellum|bellum]] · [[#causa|causa]] · [[#pars|pars]] · [[#urbs|urbs]] · [[#capio|capio]] · [[#populus|populus]] · [[#idem|idem]] · [[#corpus|corpus]] · [[#vita|vita]] · [[#modus|modus]] · [[#nomen|nomen]] · [[#miles|miles]] · [[#mors|mors]]
</div>
== iam ==
(adv.) : déjà, désormais
''Exempla gradatim digesta''
# ''Iam venio!'' (J’arrive !)
# ''Iam diu te exspecto.'' (Je t’attends depuis longtemps.) Nota : iam diu + présent.
# ''Non iam spes ulla salutis erat.'' (Il n’y avait plus aucun espoir de salut.) Nota : non iam = « ne... plus ».
''Exempla elocutionis''
* ''Iamque vale!'' (Et maintenant, adieu !) (Virgile)
*: ''Commentaire :'' iamque marque l’instant du basculement ; dans la bouche de l’ombre de Créuse, l’adverbe temporel porte tout le pathétique de la séparation définitive.
* ''Hostes iam iamque aderunt.'' (Les ennemis vont arriver d’un instant à l’autre.)
*: ''Commentaire :'' gémination iam iamque = l’imminence haletante ; la répétition mime les regards jetés vers l’horizon.
== quam ==
(adv. / coni.) : que, combien ; que (comparaison)
''Exempla gradatim digesta''
# ''Quam pulchra est rosa!'' (Que la rose est belle !)
# ''Doctior est quam frater.'' (Il est plus savant que son frère.)
# ''Quam quisque norit artem, in hac se exerceat.'' (Que chacun s’exerce dans l’art qu’il connaît.) (Cicéron) Nota : attraction de l’antécédent.
''Exempla elocutionis''
* ''Quam multa, quam paucis!'' (Que de choses, en combien peu de mots !)
*: ''Commentaire :'' double exclamation antithétique, elle-même réduite à quatre mots ; l’éloge de la brevitas qui s’applique à lui-même.
* ''Quam primum veni!'' (Viens au plus tôt !)
*: ''Commentaire :'' quam + superlatif = le degré maximal possible ; tour usuel mais expressif de l’urgence épistolaire.
== autem ==
(coni.) : or, mais, quant à
''Exempla gradatim digesta''
# ''Ego maneo, tu autem abis.'' (Moi je reste, toi en revanche tu pars.)
# ''Corpus mortale est, animus autem immortalis.'' (Le corps est mortel, l’âme en revanche est immortelle.)
# ''Quod autem ad amicitiam pertinet, postea dicam.'' (Quant à ce qui concerne l’amitié, j’en parlerai plus tard.) Nota : quod ad... pertinet.
''Exempla elocutionis''
* ''Quid autem est mors?'' (Mais qu’est-ce donc que la mort ?)
*: ''Commentaire :'' autem introduit la question dialectique, le tournant de l’examen ; c’est la particule du dialogue philosophique, qui relance sans opposer brutalement.
* ''Hoc autem ipsum, quod dico, difficile est dictu.'' (Mais cela même que je dis est difficile à dire.)
*: ''Commentaire :'' autem de la reprise réflexive ; la prose philosophique revient sur ses propres mots, et le supin dictu fait écho à dico.
== de ==
(praep. + abl.) : au sujet de ; du haut de, de
''Exempla gradatim digesta''
# ''De monte descendimus.'' (Nous descendons de la montagne.)
# ''De pace agitur.'' (Il est question de la paix.) Nota : agitur de, impersonnel.
# ''De summa rerum senatus decrevit.'' (Le sénat statua sur la situation générale.) Nota : summa rerum = « l’ensemble des affaires ».
''Exempla elocutionis''
* ''De amicitia, de senectute, de officiis Cicero scripsit.'' (Cicéron a écrit sur l’amitié, la vieillesse, les devoirs.)
*: ''Commentaire :'' de + ablatif, formule des titres de traités, calque du grec peri ; toute une bibliothèque philosophique tient dans cette préposition.
* ''De nihilo nihil fit.'' (Rien ne naît de rien.) (d’après Lucrèce)
*: ''Commentaire :'' polyptote nihilo / nihil aux deux pôles de la phrase ; l’axiome épicurien frappé comme une monnaie, la forme close disant la clôture de l’être.
== noster ==
''noster, nostra, nostrum'' (pron. poss.) : notre
''Exempla gradatim digesta''
# ''Noster canis latrat.'' (Notre chien aboie.)
# ''Nostri maiores rem publicam sapienter constituerunt.'' (Nos ancêtres ont organisé la république avec sagesse.)
# ''Nostra interest hoc quam primum scire.'' (Il est de notre intérêt de le savoir au plus tôt.) Nota : interest + nostra.
''Exempla elocutionis''
* ''Plato noster haec docet.'' (Notre cher Platon enseigne ceci.)
*: ''Commentaire :'' possessif d’affection et d’appartenance d’école (« notre Platon » = celui que nous lisons et aimons) ; tour fréquent chez Cicéron et Sénèque.
* ''Nostri acriter restiterunt.'' (Les nôtres résistèrent avec acharnement.) (d’après César)
*: ''Commentaire :'' nostri substantivé, langue du communiqué militaire ; le possessif enrôle le lecteur dans le camp romain sans même le nommer.
== meus ==
''meus, mea, meum'' (pron. poss.) : mon, ma
''Exempla gradatim digesta''
# ''Mea soror cantat.'' (Ma sœur chante.)
# ''Mea quidem sententia, erras.'' (À mon avis du moins, tu te trompes.)
# ''Meum est officium te monere, tuum parere.'' (Mon devoir est de t’avertir, le tien d’obéir.)
''Exempla elocutionis''
* ''Mi fili!'' (Mon fils !)
*: ''Commentaire :'' vocatif mi, forme propre à l’adresse affectueuse ; un seul phonème de différence avec meus, mais tout un registre de tendresse.
* ''Mea mihi conscientia pluris est quam omnium sermo.'' (Ma conscience compte plus pour moi que les propos de tous.) (Cicéron)
*: ''Commentaire :'' redoublement mea mihi en tête ; le possessif et le datif concentrent la phrase sur l’intériorité, contre la rumeur (sermo) rejetée à la fin.
== tuus ==
''tuus, tua, tuum'' (pron. poss.) : ton, ta
''Exempla gradatim digesta''
# ''Tuus liber in mensa iacet.'' (Ton livre est posé sur la table.)
# ''Tua sponte hoc fecisti.'' (Tu as fait cela de ton propre gré.)
# ''Non tuum est de hac re iudicare.'' (Ce n’est pas à toi de juger de cette affaire.)
''Exempla elocutionis''
* ''Vale. Tuus Marcus.'' (Porte-toi bien. Ton Marcus.)
*: ''Commentaire :'' formule de clôture épistolaire ; le possessif seul, sans verbe, dit le lien. Degré zéro et degré intime de la syntaxe.
* ''Quae tua est humanitas, ignosces.'' (Avec la bonté qui est la tienne, tu pardonneras.)
*: ''Commentaire :'' relative incise de courtoisie, tournure cicéronienne par excellence ; on enrobe la requête dans l’éloge anticipé.
== venio ==
''venio, venire, veni, ventum'' (v.) : venir
''Exempla gradatim digesta''
# ''Amici veniunt.'' (Les amis viennent.)
# ''Mihi in mentem venit veteris amicitiae.'' (Le souvenir de notre vieille amitié me revient à l’esprit.) Nota : venit in mentem + génitif.
# ''Ventum est ad summum montis.'' (On parvint au sommet de la montagne.) Nota : passif impersonnel.
''Exempla elocutionis''
* ''Veni, vidi, vici.'' (Je suis venu, j’ai vu, j’ai vaincu.) (César)
*: ''Commentaire :'' asyndète, allitération, isocolie, trois parfaits de même structure ; la vitesse du style égale la vitesse de la campagne. Sommet du laconisme latin.
* ''Venit summa dies.'' (Il est venu, le jour suprême.) (Virgile)
*: ''Commentaire :'' verbe en tête, position rare et donc dramatique : l’événement surgit avant son sujet ; l’adjectif summa achève le vers funèbre de Troie.
== tantus ==
''tantus, tanta, tantum'' (adi.) : si grand
''Exempla gradatim digesta''
# ''Tanta turba in foro est!'' (Il y a une si grande foule au forum !)
# ''Tanta vis tempestatis fuit ut naves delerentur.'' (La violence de la tempête fut telle que les navires furent détruits.)
# ''Tantae molis erat Romanam condere gentem.'' (Tant il était laborieux de fonder la nation romaine.) (Virgile) Nota : génitif de qualité.
''Exempla elocutionis''
* ''Tantum religio potuit suadere malorum.'' (Tant la superstition a pu conseiller de crimes.) (Lucrèce)
*: ''Commentaire :'' le génitif partitif malorum est rejeté à l’extrême fin du vers : la disjonction tantum... malorum suspend le jugement jusqu’au mot le plus noir. Vers-réquisitoire célèbre.
* ''Tanto melior!'' (À la bonne heure ! / Bravo !)
*: ''Commentaire :'' ablatif de mesure exclamatif, registre parlé ; la langue familière abrège là où la langue écrite construirait.
== nullus ==
''nullus, nulla, nullum'' (adi.) : aucun, nul
''Exempla gradatim digesta''
# ''Nullus discipulus abest.'' (Aucun élève n’est absent.)
# ''Nullo modo hoc fieri potest.'' (Cela ne peut se faire d’aucune manière.)
# ''Nulla dies sine linea.'' (Pas un jour sans une ligne.) (adage rapporté par Pline l’Ancien)
''Exempla elocutionis''
* ''Nullius addictus iurare in verba magistri.'' (Tenu de jurer sur les mots d’aucun maître.) (Horace)
*: ''Commentaire :'' nullius en tête, disjoint de magistri par tout le vers ; la déclaration d’indépendance philosophique commence par le mot de la négation d’appartenance.
* ''Nullus sum!'' (Je suis perdu !) (Plaute)
*: ''Commentaire :'' hyperbole comique : « je ne suis plus personne » = « c’en est fait de moi » ; nullus prédicat d’un sujet personnel, audace de la langue parlée.
== locus ==
''locus, loci'' (subst. m.) : lieu, place
''Exempla gradatim digesta''
# ''Hic locus pulcher est.'' (Ce lieu est beau.)
# ''Loco cedere turpe est militi.'' (Reculer est honteux pour un soldat.) Nota : loco cedere = « quitter sa position ».
# ''Preces apud eum locum non habuerunt.'' (Les prières n’eurent aucun effet sur lui.) Nota : locum habere = « avoir prise ».
''Exempla elocutionis''
* ''Hi sunt loci communes oratorum.'' (Ce sont là les lieux communs des orateurs.)
*: ''Commentaire :'' locus, terme technique de la rhétorique (réservoir d’arguments) ; le sens spatial passé en métaphore institutionnalisée, jusqu’à notre « lieu commun ».
* ''Est locus, Hesperiam Grai cognomine dicunt.'' (Il est un lieu, les Grecs le nomment Hespérie.) (Virgile)
*: ''Commentaire :'' est locus, formule d’ouverture de l’ecphrasis épique ; le récit s’arrête, la géographie commence. Module narratif hérité d’Homère.
== manus ==
''manus, manus'' (subst. f.) : main ; troupe
''Exempla gradatim digesta''
# ''Manus lavo.'' (Je me lave les mains.)
# ''Parva manus militum urbem defendit.'' (Une petite troupe de soldats défendit la ville.) Nota : manus = « troupe ».
# ''Hostes victi manus dederunt.'' (Les ennemis vaincus se rendirent.) Nota : manus dare = « se rendre ».
''Exempla elocutionis''
* ''Manus manum lavat.'' (Une main lave l’autre.) (Sénèque, Pétrone)
*: ''Commentaire :'' polyptote proverbial ; la réciprocité intéressée dite par la grammaire de la réflexivité. Registre populaire assumé.
* ''Res in manibus est.'' (L’affaire est en cours. / L’affaire nous occupe.)
*: ''Commentaire :'' métaphore de la proximité immédiate : ce qu’on a « dans les mains » est ce qu’on traite ; idiome de la prose d’affaires et des lettres.
== bellum ==
''bellum, belli'' (subst. n.) : guerre
''Exempla gradatim digesta''
# ''Bellum malum est.'' (La guerre est un mal.)
# ''Bello confecto, milites domum redierunt.'' (La guerre achevée, les soldats rentrèrent chez eux.) Nota : ablatif absolu.
# ''Bellum nec timendum nec provocandum est.'' (Il ne faut ni craindre la guerre ni la provoquer.) Nota : double adjectif verbal.
''Exempla elocutionis''
* ''Bellum omnium contra omnes.'' (La guerre de tous contre tous.) (Hobbes)
*: ''Commentaire :'' formule néo-latine (XVIIe siècle), à signaler comme telle dans un dictionnaire de philosophie ; le polyptote omnium / omnes lui donne sa frappe pseudo-antique.
* ''Bella, horrida bella cerno.'' (Des guerres, d’horribles guerres, voilà ce que je vois.) (Virgile)
*: ''Commentaire :'' gémination prophétique de bella, épithète expressive horrida ; la Sibylle parle par répétitions, le style mime la transe.
== causa ==
''causa, causae'' (subst. f.) : cause, raison ; procès
''Exempla gradatim digesta''
# ''Causa morbi ignota est.'' (La cause de la maladie est inconnue.)
# ''Honoris causa venit.'' (Il est venu pour rendre hommage.) Nota : causa postposé + génitif.
# ''Multae sunt causae cur hoc credam.'' (Il y a bien des raisons pour que je le croie.) Nota : cur + subjonctif.
''Exempla elocutionis''
* ''Et propter vitam vivendi perdere causas.'' (Et pour sauver sa vie, perdre les raisons de vivre.) (Juvénal)
*: ''Commentaire :'' paradoxe construit sur l’opposition vitam / vivendi causas ; la satire morale atteint ici la densité d’une thèse philosophique.
* ''Reus causam dixit.'' (L’accusé a plaidé sa cause.)
*: ''Commentaire :'' causam dicere, locution technique de la langue judiciaire ; le style, c’est aussi savoir reconnaître les syntagmes figés des institutions.
== pars ==
''pars, partis'' (subst. f.) : partie, côté
''Exempla gradatim digesta''
# ''Partem panis accipio.'' (Je reçois une part de pain.)
# ''Maxima pars hominum hoc credit.'' (La plupart des hommes le croient.)
# ''Partes boni civis agere debemus.'' (Nous devons jouer le rôle d’un bon citoyen.) Nota : partes agere.
''Exempla elocutionis''
* ''Pars sanitatis velle sanari fuit.'' (Vouloir guérir était déjà une part de la guérison.) (Sénèque)
*: ''Commentaire :'' maxime à pointe : l’infinitif sujet (velle sanari) et le parfait gnomique ; la psychologie morale condensée en sept mots.
* ''Pro mea parte adiuvabo.'' (J’aiderai pour ma part.)
*: ''Commentaire :'' formule de contribution mesurée ; la modestie codifiée de la prose de collaboration, sans figure mais non sans politesse.
== urbs ==
''urbs, urbis'' (subst. f.) : ville
''Exempla gradatim digesta''
# ''Urbs magna est Roma.'' (Rome est une grande ville.)
# ''Urbe capta, cives fugerunt.'' (La ville prise, les citoyens s’enfuirent.) Nota : ablatif absolu.
# ''Romae, in urbe omnium pulcherrima, diu vixit.'' (Il vécut longtemps à Rome, la plus belle des villes.) Nota : locatif + apposition.
''Exempla elocutionis''
* ''Romanae spatium est urbis et orbis idem.'' (L’espace de la ville de Rome et celui du monde ne font qu’un.) (Ovide)
*: ''Commentaire :'' paronomase urbis / orbis, un seul phonème d’écart pour dire l’idéologie impériale ; le calembour élevé à la dignité de doctrine.
* ''In Urbem redire cupio.'' (Je désire rentrer à Rome.)
*: ''Commentaire :'' antonomase : urbs absolu désigne Rome seule ; pour un Romain, il n’y a qu’une Ville, et l’article manquant du latin est suppléé par l’évidence.
== capio ==
''capio, capere, cepi, captum'' (v.) : prendre, saisir
''Exempla gradatim digesta''
# ''Piscem cepi.'' (J’ai pris un poisson.)
# ''Consilium cepimus fugiendi.'' (Nous avons formé le projet de fuir.) Nota : consilium capere + génitif du gérondif.
# ''Amore captus, omnia reliquit.'' (Saisi par l’amour, il abandonna tout.)
''Exempla elocutionis''
* ''Exordium captat benevolentiam iudicum.'' (L’exorde cherche à capter la bienveillance des juges.)
*: ''Commentaire :'' capere et son fréquentatif captare ont fourni le vocabulaire technique de la rhétorique (captatio benevolentiae) ; la chasse comme métaphore du discours.
* ''Nec te Troia capit.'' (Troie même est trop petite pour toi.) (Virgile)
*: ''Commentaire :'' capere = « contenir » ; l’hyperbole héroïque loge la grandeur d’un homme dans un verbe d’espace. Litote et démesure à la fois.
== populus ==
''populus, populi'' (subst. m.) : peuple
''Exempla gradatim digesta''
# ''Populus in foro convenit.'' (Le peuple se rassemble au forum.)
# ''Senatus populusque Romanus legatos misit.'' (Le sénat et le peuple romain envoyèrent des ambassadeurs.)
# ''Salus populi suprema lex esto.'' (Que le salut du peuple soit la loi suprême.) (Cicéron) Nota : esto, impératif futur.
''Exempla elocutionis''
* ''Maiestas populi Romani laesa est.'' (La majesté du peuple romain a été lésée.)
*: ''Commentaire :'' génitif officiel des formules d’État (populi Romani) ; style juridique, où chaque mot est pesé et aucun n’est imagé.
* ''Populus me sibilat, at mihi plaudo.'' (Le peuple me siffle, mais moi je m’applaudis.) (Horace)
*: ''Commentaire :'' antithèse de la foule et de l’individu (populus / mihi, sibilat / plaudo) ; le sage se passe du suffrage du nombre, et le balancement de la phrase oppose le jugement public à la conscience de soi.
== idem ==
''idem, eadem, idem'' (pron.) : le même
''Exempla gradatim digesta''
# ''Eundem librum lego.'' (Je lis le même livre.)
# ''Eadem nocte profecti sumus.'' (Nous sommes partis la même nuit.)
# ''Idem velle atque idem nolle, ea demum firma amicitia est.'' (Vouloir et refuser les mêmes choses, voilà la solide amitié.) (Salluste)
''Exempla elocutionis''
* ''Vultus erat semper idem.'' (Son visage était toujours le même.)
*: ''Commentaire :'' l’identité d’humeur du sage (on le disait de Socrate) ; idem prédicat, sans image ni emphase : la constance dite par la constance du style.
* ''Vir doctissimus idemque modestissimus.'' (Un homme très savant et tout aussi modeste.)
*: ''Commentaire :'' idemque cumule les qualités en signalant que leur réunion est remarquable ; l’éloge procède par addition étonnée.
== corpus ==
''corpus, corporis'' (subst. n.) : corps
''Exempla gradatim digesta''
# ''Corpus meum fessum est.'' (Mon corps est fatigué.)
# ''Mens sana in corpore sano optanda est.'' (Il faut souhaiter un esprit sain dans un corps sain.) (d’après Juvénal)
# ''Animus corpori imperat sicut rex civibus suis.'' (L’âme commande au corps comme un roi à ses citoyens.) (d’après Salluste)
''Exempla elocutionis''
* ''Res publica unum corpus est.'' (L’État est un seul corps.)
*: ''Commentaire :'' métaphore organiciste, fondatrice de toute une tradition politique (l’apologue de Ménénius Agrippa) ; l’image fait la doctrine.
* ''Corpora prima Lucretius ea vocat quae dividi non possunt.'' (Lucrèce appelle corps premiers ceux qui ne peuvent être divisés.)
*: ''Commentaire :'' corpora prima, périphrase latine pour les atomes grecs ; le traducteur philosophe préfère la périphrase native à l’emprunt. Leçon de méthode pour tout lexique philosophique latin.
== vita ==
''vita, vitae'' (subst. f.) : vie
''Exempla gradatim digesta''
# ''Vita pulchra est.'' (La vie est belle.)
# ''Vitam rusticam poetae laudant.'' (Les poètes louent la vie des champs.)
# ''Non est vivere sed valere vita est.'' (Vivre, ce n’est pas être en vie, c’est être en bonne santé.) (Martial)
''Exempla elocutionis''
* ''O vita misero longa, felici brevis!'' (Ô vie, longue pour le malheureux, brève pour l’heureux !) (Publilius Syrus)
*: ''Commentaire :'' apostrophe à la vie, double antithèse en chiasme (misero longa / felici brevis) ; la sentence dramatique faite pour être déclamée.
* ''Vitam beatam vivere omnes cupiunt.'' (Tous désirent vivre une vie heureuse.)
*: ''Commentaire :'' figure étymologique vitam vivere ; le complément interne, banal en grec, garde en latin une légère solennité. Vita beata, terme technique de l’éthique antique.
== modus ==
''modus, modi'' (subst. m.) : manière, mesure
''Exempla gradatim digesta''
# ''Quo modo vales?'' (Comment te portes-tu ?)
# ''Servandus est modus in omnibus rebus.'' (Il faut garder la mesure en toutes choses.)
# ''Eius modi homines vitandi sunt.'' (Il faut éviter les hommes de cette espèce.) Nota : eius modi = « de ce genre ».
''Exempla elocutionis''
* ''Est modus in rebus, sunt certi denique fines.'' (Il y a une mesure dans les choses, il y a enfin des limites précises.) (Horace)
*: ''Commentaire :'' parallélisme est / sunt, et denique qui feint l’impatience ; la doctrine du juste milieu énoncée sur le ton de l’évidence excédée.
* ''Modo ridet, modo flet.'' (Tantôt il rit, tantôt il pleure.)
*: ''Commentaire :'' modo... modo, anaphore alternative (cf. nunc... nunc) ; l’instabilité d’humeur peinte par le balancement de la phrase.
== nomen ==
''nomen, nominis'' (subst. n.) : nom
''Exempla gradatim digesta''
# ''Nomen meum scribo.'' (J’écris mon nom.)
# ''Nomine tantum rex erat, re vera servus.'' (Il n’était roi que de nom ; en réalité, il était esclave.) Nota : nomine / re opposés.
# ''Nomen atque omen.'' (Le nom est un présage.) (Plaute)
''Exempla elocutionis''
* ''Stat magni nominis umbra.'' (Il demeure, ombre d’un grand nom.) (Lucain)
*: ''Commentaire :'' métaphore de l’ombre pour la grandeur survivante d’elle-même (Pompée) ; le génitif magni nominis enchâssé fait tout le vers. L’un des plus beaux raccourcis de la poésie latine.
* ''Amicitiae nomine multa peccantur.'' (Bien des fautes se commettent au nom de l’amitié.)
*: ''Commentaire :'' nomine = « sous couvert de » ; l’ablatif seul suffit à dénoncer l’écart entre le mot et la chose, thème moraliste par excellence.
== miles ==
''miles, militis'' (subst. m.) : soldat
''Exempla gradatim digesta''
# ''Miles gladium gerit.'' (Le soldat porte un glaive.)
# ''Milites urbi praesidio erant.'' (Les soldats servaient de garnison à la ville.) Nota : double datif.
# ''Militis est parere, imperatoris providere.'' (Au soldat d’obéir, au général de prévoir.)
''Exempla elocutionis''
* ''Miles Romanus laborare didicit.'' (Le soldat romain a appris à peiner.)
*: ''Commentaire :'' singulier collectif des historiens ; miles = l’armée entière, vue comme un type. Stylisation épique de la prose historique.
* ''Miles gloriosus a Plauto deridetur.'' (Le soldat fanfaron est raillé par Plaute.)
*: ''Commentaire :'' miles gloriosus, type comique devenu titre ; l’épithète suffit à camper le personnage. Le style crée des emplois figés que la culture transmet.
== mors ==
''mors, mortis'' (subst. f.) : mort
''Exempla gradatim digesta''
# ''Mors vitae finis est.'' (La mort est la fin de la vie.)
# ''Morte patris audita, domum rediit.'' (À la nouvelle de la mort de son père, il rentra chez lui.) Nota : ablatif absolu.
# ''Mors, ut Stoicis videtur, nec bonum nec malum est.'' (La mort, selon les stoïciens, n’est ni un bien ni un mal.) Nota : ut videtur incise.
''Exempla elocutionis''
* ''Mors ultima linea rerum est.'' (La mort est la ligne d’arrivée des choses.) (Horace)
*: ''Commentaire :'' métaphore empruntée au cirque (la ligne blanche au bout de la piste) ; l’image sportive apprivoise l’effroi. Définition par déplacement.
* ''Pallida Mors aequo pulsat pede pauperum tabernas regumque turris.'' (La pâle Mort frappe d’un pied égal les cabanes des pauvres et les tours des rois.) (Horace)
*: ''Commentaire :'' personnification, allitération en p, antithèse sociale pauperum / regum (turris, accusatif pluriel archaïque) ; l’égalité devant la mort dite par une symétrie parfaite des images.
{{#invoke:Sous-Page|sousPage|taille préfixe=0.7em|taille titre=115%|italique=non}}
{{AutoCat}}
bj9kb09ivw0cmxkcz2llkmb8buyg74c
768814
768813
2026-06-28T03:58:52Z
PandaMystique
119061
768814
wikitext
text/x-wiki
{{Sous-pages|Latin/Vocabulaire/Verba Latina frequentissima}}
<div style="text-align: center; font-size: 90%;">
[[#iam|iam]] · [[#quam|quam]] · [[#autem|autem]] · [[#de|de]] · [[#noster|noster]] · [[#meus|meus]] · [[#tuus|tuus]] · [[#venio|venio]] · [[#tantus|tantus]] · [[#nullus|nullus]] · [[#locus|locus]] · [[#manus|manus]] · [[#bellum|bellum]] · [[#causa|causa]] · [[#pars|pars]] · [[#urbs|urbs]] · [[#capio|capio]] · [[#populus|populus]] · [[#idem|idem]] · [[#corpus|corpus]] · [[#vita|vita]] · [[#modus|modus]] · [[#nomen|nomen]] · [[#miles|miles]] · [[#mors|mors]]
</div>
== iam ==
(adv.) : déjà, désormais
''Exempla gradatim digesta''
# ''Iam venio!'' (J’arrive !)
# ''Iam diu te exspecto.'' (Je t’attends depuis longtemps.) Nota : iam diu + présent.
# ''Non iam spes ulla salutis erat.'' (Il n’y avait plus aucun espoir de salut.) Nota : non iam = « ne... plus ».
''Exempla elocutionis''
* ''Iamque vale!'' (Et maintenant, adieu !) (Virgile)
*: ''Commentaire :'' iamque marque l’instant du basculement ; dans la bouche de l’ombre de Créuse, l’adverbe temporel porte tout le pathétique de la séparation définitive.
* ''Hostes iam iamque aderunt.'' (Les ennemis vont arriver d’un instant à l’autre.)
*: ''Commentaire :'' gémination iam iamque = l’imminence haletante ; la répétition mime les regards jetés vers l’horizon.
== quam ==
(adv. / coni.) : que, combien ; que (comparaison)
''Exempla gradatim digesta''
# ''Quam pulchra est rosa!'' (Que la rose est belle !)
# ''Doctior est quam frater.'' (Il est plus savant que son frère.)
# ''Quam quisque norit artem, in hac se exerceat.'' (Que chacun s’exerce dans l’art qu’il connaît.) (Cicéron) Nota : attraction de l’antécédent.
''Exempla elocutionis''
* ''Quam multa, quam paucis!'' (Que de choses, en combien peu de mots !)
*: ''Commentaire :'' double exclamation antithétique, elle-même réduite à quatre mots ; l’éloge de la brevitas qui s’applique à lui-même.
* ''Quam primum veni!'' (Viens au plus tôt !)
*: ''Commentaire :'' quam + superlatif = le degré maximal possible ; tour usuel mais expressif de l’urgence épistolaire.
== autem ==
(coni.) : or, mais, quant à
''Exempla gradatim digesta''
# ''Ego maneo, tu autem abis.'' (Moi je reste, toi en revanche tu pars.)
# ''Corpus mortale est, animus autem immortalis.'' (Le corps est mortel, l’âme en revanche est immortelle.)
# ''Quod autem ad amicitiam pertinet, postea dicam.'' (Quant à ce qui concerne l’amitié, j’en parlerai plus tard.) Nota : quod ad... pertinet.
''Exempla elocutionis''
* ''Quid autem est mors?'' (Mais qu’est-ce donc que la mort ?)
*: ''Commentaire :'' autem introduit la question dialectique, le tournant de l’examen ; c’est la particule du dialogue philosophique, qui relance sans opposer brutalement.
* ''Hoc autem ipsum, quod dico, difficile est dictu.'' (Mais cela même que je dis est difficile à dire.)
*: ''Commentaire :'' autem de la reprise réflexive ; la prose philosophique revient sur ses propres mots, et le supin dictu fait écho à dico.
== de ==
(praep. + abl.) : au sujet de ; du haut de, de
''Exempla gradatim digesta''
# ''De monte descendimus.'' (Nous descendons de la montagne.)
# ''De pace agitur.'' (Il est question de la paix.) Nota : agitur de, impersonnel.
# ''De summa rerum senatus decrevit.'' (Le sénat statua sur la situation générale.) Nota : summa rerum = « l’ensemble des affaires ».
''Exempla elocutionis''
* ''De amicitia, de senectute, de officiis Cicero scripsit.'' (Cicéron a écrit sur l’amitié, la vieillesse, les devoirs.)
*: ''Commentaire :'' de + ablatif, formule des titres de traités, calque du grec peri ; toute une bibliothèque philosophique tient dans cette préposition.
* ''De nihilo nihil fit.'' (Rien ne naît de rien.) (d’après Lucrèce)
*: ''Commentaire :'' polyptote nihilo / nihil aux deux pôles de la phrase ; l’axiome épicurien frappé comme une monnaie, la forme close disant la clôture de l’être.
== noster ==
''noster, nostra, nostrum'' (pron. poss.) : notre
''Exempla gradatim digesta''
# ''Noster canis latrat.'' (Notre chien aboie.)
# ''Nostri maiores rem publicam sapienter constituerunt.'' (Nos ancêtres ont organisé la république avec sagesse.)
# ''Nostra interest hoc quam primum scire.'' (Il est de notre intérêt de le savoir au plus tôt.) Nota : interest + nostra.
''Exempla elocutionis''
* ''Plato noster haec docet.'' (Notre cher Platon enseigne ceci.)
*: ''Commentaire :'' possessif d’affection et d’appartenance d’école (« notre Platon » = celui que nous lisons et aimons) ; tour fréquent chez Cicéron et Sénèque.
* ''Nostri acriter restiterunt.'' (Les nôtres résistèrent avec acharnement.) (d’après César)
*: ''Commentaire :'' nostri substantivé, langue du communiqué militaire ; le possessif enrôle le lecteur dans le camp romain sans même le nommer.
== meus ==
''meus, mea, meum'' (pron. poss.) : mon, ma
''Exempla gradatim digesta''
# ''Mea soror cantat.'' (Ma sœur chante.)
# ''Mea quidem sententia, erras.'' (À mon avis du moins, tu te trompes.)
# ''Meum est officium te monere, tuum parere.'' (Mon devoir est de t’avertir, le tien d’obéir.)
''Exempla elocutionis''
* ''Mi fili!'' (Mon fils !)
*: ''Commentaire :'' vocatif mi, forme propre à l’adresse affectueuse ; un seul phonème de différence avec meus, mais tout un registre de tendresse.
* ''Mea mihi conscientia pluris est quam omnium sermo.'' (Ma conscience compte plus pour moi que les propos de tous.) (Cicéron)
*: ''Commentaire :'' redoublement mea mihi en tête ; le possessif et le datif concentrent la phrase sur l’intériorité, contre la rumeur (sermo) rejetée à la fin.
== tuus ==
''tuus, tua, tuum'' (pron. poss.) : ton, ta
''Exempla gradatim digesta''
# ''Tuus liber in mensa iacet.'' (Ton livre est posé sur la table.)
# ''Tua sponte hoc fecisti.'' (Tu as fait cela de ton propre gré.)
# ''Non tuum est de hac re iudicare.'' (Ce n’est pas à toi de juger de cette affaire.)
''Exempla elocutionis''
* ''Vale. Tuus Marcus.'' (Porte-toi bien. Ton Marcus.)
*: ''Commentaire :'' formule de clôture épistolaire ; le possessif seul, sans verbe, dit le lien. Degré zéro et degré intime de la syntaxe.
* ''Quae tua est humanitas, ignosces.'' (Avec la bonté qui est la tienne, tu pardonneras.)
*: ''Commentaire :'' relative incise de courtoisie, tournure cicéronienne par excellence ; on enrobe la requête dans l’éloge anticipé.
== venio ==
''venio, venire, veni, ventum'' (v.) : venir
''Exempla gradatim digesta''
# ''Amici veniunt.'' (Les amis viennent.)
# ''Mihi in mentem venit veteris amicitiae.'' (Le souvenir de notre vieille amitié me revient à l’esprit.) Nota : venit in mentem + génitif.
# ''Ventum est ad summum montis.'' (On parvint au sommet de la montagne.) Nota : passif impersonnel.
''Exempla elocutionis''
* ''Veni, vidi, vici.'' (Je suis venu, j’ai vu, j’ai vaincu.) (César)
*: ''Commentaire :'' asyndète, allitération, isocolie, trois parfaits de même structure ; la vitesse du style égale la vitesse de la campagne. Sommet du laconisme latin.
* ''Venit summa dies.'' (Il est venu, le jour suprême.) (Virgile)
*: ''Commentaire :'' verbe en tête, position rare et donc dramatique : l’événement surgit avant son sujet ; l’adjectif summa achève le vers funèbre de Troie.
== tantus ==
''tantus, tanta, tantum'' (adi.) : si grand
''Exempla gradatim digesta''
# ''Tanta turba in foro est!'' (Il y a une si grande foule au forum !)
# ''Tanta vis tempestatis fuit ut naves delerentur.'' (La violence de la tempête fut telle que les navires furent détruits.)
# ''Tantae molis erat Romanam condere gentem.'' (Tant il était laborieux de fonder la nation romaine.) (Virgile) Nota : génitif de qualité.
''Exempla elocutionis''
* ''Tantum religio potuit suadere malorum.'' (Tant la superstition a pu conseiller de crimes.) (Lucrèce)
*: ''Commentaire :'' le génitif partitif malorum est rejeté à l’extrême fin du vers : la disjonction tantum... malorum suspend le jugement jusqu’au mot le plus noir. Vers-réquisitoire célèbre.
* ''Tanto melior!'' (À la bonne heure ! / Bravo !)
*: ''Commentaire :'' ablatif de mesure exclamatif, registre parlé ; la langue familière abrège là où la langue écrite construirait.
== nullus ==
''nullus, nulla, nullum'' (adi.) : aucun, nul
''Exempla gradatim digesta''
# ''Nullus discipulus abest.'' (Aucun élève n’est absent.)
# ''Nullo modo hoc fieri potest.'' (Cela ne peut se faire d’aucune manière.)
# ''Nulla dies sine linea.'' (Pas un jour sans une ligne.) (adage, d’après l’anecdote d’Apelle chez Pline l’Ancien)
''Exempla elocutionis''
* ''Nullius addictus iurare in verba magistri.'' (Tenu de jurer sur les mots d’aucun maître.) (Horace)
*: ''Commentaire :'' nullius en tête, disjoint de magistri par tout le vers ; la déclaration d’indépendance philosophique commence par le mot de la négation d’appartenance.
* ''Nullus sum!'' (Je suis perdu !) (Plaute)
*: ''Commentaire :'' hyperbole comique : « je ne suis plus personne » = « c’en est fait de moi » ; nullus prédicat d’un sujet personnel, audace de la langue parlée.
== locus ==
''locus, loci'' (subst. m.) : lieu, place
''Exempla gradatim digesta''
# ''Hic locus pulcher est.'' (Ce lieu est beau.)
# ''Loco cedere turpe est militi.'' (Reculer est honteux pour un soldat.) Nota : loco cedere = « quitter sa position ».
# ''Preces apud eum locum non habuerunt.'' (Les prières n’eurent aucun effet sur lui.) Nota : locum habere = « avoir prise ».
''Exempla elocutionis''
* ''Hi sunt loci communes oratorum.'' (Ce sont là les lieux communs des orateurs.)
*: ''Commentaire :'' locus, terme technique de la rhétorique (réservoir d’arguments) ; le sens spatial passé en métaphore institutionnalisée, jusqu’à notre « lieu commun ».
* ''Est locus, Hesperiam Grai cognomine dicunt.'' (Il est un lieu, les Grecs le nomment Hespérie.) (Virgile)
*: ''Commentaire :'' est locus, formule d’ouverture de l’ecphrasis épique ; le récit s’arrête, la géographie commence. Module narratif hérité d’Homère.
== manus ==
''manus, manus'' (subst. f.) : main ; troupe
''Exempla gradatim digesta''
# ''Manus lavo.'' (Je me lave les mains.)
# ''Parva manus militum urbem defendit.'' (Une petite troupe de soldats défendit la ville.) Nota : manus = « troupe ».
# ''Hostes victi manus dederunt.'' (Les ennemis vaincus se rendirent.) Nota : manus dare = « se rendre ».
''Exempla elocutionis''
* ''Manus manum lavat.'' (Une main lave l’autre.) (Sénèque, Pétrone)
*: ''Commentaire :'' polyptote proverbial ; la réciprocité intéressée dite par la grammaire de la réflexivité. Registre populaire assumé.
* ''Res in manibus est.'' (L’affaire est en cours. / L’affaire nous occupe.)
*: ''Commentaire :'' métaphore de la proximité immédiate : ce qu’on a « dans les mains » est ce qu’on traite ; idiome de la prose d’affaires et des lettres.
== bellum ==
''bellum, belli'' (subst. n.) : guerre
''Exempla gradatim digesta''
# ''Bellum malum est.'' (La guerre est un mal.)
# ''Bello confecto, milites domum redierunt.'' (La guerre achevée, les soldats rentrèrent chez eux.) Nota : ablatif absolu.
# ''Bellum nec timendum nec provocandum est.'' (Il ne faut ni craindre la guerre ni la provoquer.) Nota : double adjectif verbal.
''Exempla elocutionis''
* ''Bellum omnium contra omnes.'' (La guerre de tous contre tous.) (Hobbes)
*: ''Commentaire :'' formule néo-latine (XVIIe siècle), à signaler comme telle dans un dictionnaire de philosophie ; le polyptote omnium / omnes lui donne sa frappe pseudo-antique.
* ''Bella, horrida bella cerno.'' (Des guerres, d’horribles guerres, voilà ce que je vois.) (Virgile)
*: ''Commentaire :'' gémination prophétique de bella, épithète expressive horrida ; la Sibylle parle par répétitions, le style mime la transe.
== causa ==
''causa, causae'' (subst. f.) : cause, raison ; procès
''Exempla gradatim digesta''
# ''Causa morbi ignota est.'' (La cause de la maladie est inconnue.)
# ''Honoris causa venit.'' (Il est venu pour rendre hommage.) Nota : causa postposé + génitif.
# ''Multae sunt causae cur hoc credam.'' (Il y a bien des raisons pour que je le croie.) Nota : cur + subjonctif.
''Exempla elocutionis''
* ''Et propter vitam vivendi perdere causas.'' (Et pour sauver sa vie, perdre les raisons de vivre.) (Juvénal)
*: ''Commentaire :'' paradoxe construit sur l’opposition vitam / vivendi causas ; la satire morale atteint ici la densité d’une thèse philosophique.
* ''Reus causam dixit.'' (L’accusé a plaidé sa cause.)
*: ''Commentaire :'' causam dicere, locution technique de la langue judiciaire ; le style, c’est aussi savoir reconnaître les syntagmes figés des institutions.
== pars ==
''pars, partis'' (subst. f.) : partie, côté
''Exempla gradatim digesta''
# ''Partem panis accipio.'' (Je reçois une part de pain.)
# ''Maxima pars hominum hoc credit.'' (La plupart des hommes le croient.)
# ''Partes boni civis agere debemus.'' (Nous devons jouer le rôle d’un bon citoyen.) Nota : partes agere.
''Exempla elocutionis''
* ''Pars sanitatis velle sanari fuit.'' (Vouloir guérir était déjà une part de la guérison.) (Sénèque)
*: ''Commentaire :'' maxime à pointe : l’infinitif sujet (velle sanari) et le parfait gnomique ; la psychologie morale condensée en sept mots.
* ''Pro mea parte adiuvabo.'' (J’aiderai pour ma part.)
*: ''Commentaire :'' formule de contribution mesurée ; la modestie codifiée de la prose de collaboration, sans figure mais non sans politesse.
== urbs ==
''urbs, urbis'' (subst. f.) : ville
''Exempla gradatim digesta''
# ''Urbs magna est Roma.'' (Rome est une grande ville.)
# ''Urbe capta, cives fugerunt.'' (La ville prise, les citoyens s’enfuirent.) Nota : ablatif absolu.
# ''Romae, in urbe omnium pulcherrima, diu vixit.'' (Il vécut longtemps à Rome, la plus belle des villes.) Nota : locatif + apposition.
''Exempla elocutionis''
* ''Romanae spatium est urbis et orbis idem.'' (L’espace de la ville de Rome et celui du monde ne font qu’un.) (Ovide)
*: ''Commentaire :'' paronomase urbis / orbis, un seul phonème d’écart pour dire l’idéologie impériale ; le calembour élevé à la dignité de doctrine.
* ''In Urbem redire cupio.'' (Je désire rentrer à Rome.)
*: ''Commentaire :'' antonomase : urbs absolu désigne Rome seule ; pour un Romain, il n’y a qu’une Ville, et l’article manquant du latin est suppléé par l’évidence.
== capio ==
''capio, capere, cepi, captum'' (v.) : prendre, saisir
''Exempla gradatim digesta''
# ''Piscem cepi.'' (J’ai pris un poisson.)
# ''Consilium cepimus fugiendi.'' (Nous avons formé le projet de fuir.) Nota : consilium capere + génitif du gérondif.
# ''Amore captus, omnia reliquit.'' (Saisi par l’amour, il abandonna tout.)
''Exempla elocutionis''
* ''Exordium captat benevolentiam iudicum.'' (L’exorde cherche à capter la bienveillance des juges.)
*: ''Commentaire :'' capere et son fréquentatif captare ont fourni le vocabulaire technique de la rhétorique (captatio benevolentiae) ; la chasse comme métaphore du discours.
* ''Nec te Troia capit.'' (Troie même est trop petite pour toi.) (Virgile)
*: ''Commentaire :'' capere = « contenir » ; l’hyperbole héroïque loge la grandeur d’un homme dans un verbe d’espace. Litote et démesure à la fois.
== populus ==
''populus, populi'' (subst. m.) : peuple
''Exempla gradatim digesta''
# ''Populus in foro convenit.'' (Le peuple se rassemble au forum.)
# ''Senatus populusque Romanus legatos misit.'' (Le sénat et le peuple romain envoyèrent des ambassadeurs.)
# ''Salus populi suprema lex esto.'' (Que le salut du peuple soit la loi suprême.) (Cicéron) Nota : esto, impératif futur.
''Exempla elocutionis''
* ''Maiestas populi Romani laesa est.'' (La majesté du peuple romain a été lésée.)
*: ''Commentaire :'' génitif officiel des formules d’État (populi Romani) ; style juridique, où chaque mot est pesé et aucun n’est imagé.
* ''Populus me sibilat, at mihi plaudo.'' (Le peuple me siffle, mais moi je m’applaudis.) (Horace)
*: ''Commentaire :'' antithèse de la foule et de l’individu (populus / mihi, sibilat / plaudo) ; le sage se passe du suffrage du nombre, et le balancement de la phrase oppose le jugement public à la conscience de soi.
== idem ==
''idem, eadem, idem'' (pron.) : le même
''Exempla gradatim digesta''
# ''Eundem librum lego.'' (Je lis le même livre.)
# ''Eadem nocte profecti sumus.'' (Nous sommes partis la même nuit.)
# ''Idem velle atque idem nolle, ea demum firma amicitia est.'' (Vouloir et refuser les mêmes choses, voilà la solide amitié.) (Salluste)
''Exempla elocutionis''
* ''Vultus erat semper idem.'' (Son visage était toujours le même.)
*: ''Commentaire :'' l’identité d’humeur du sage (on le disait de Socrate) ; idem prédicat, sans image ni emphase : la constance dite par la constance du style.
* ''Vir doctissimus idemque modestissimus.'' (Un homme très savant et tout aussi modeste.)
*: ''Commentaire :'' idemque cumule les qualités en signalant que leur réunion est remarquable ; l’éloge procède par addition étonnée.
== corpus ==
''corpus, corporis'' (subst. n.) : corps
''Exempla gradatim digesta''
# ''Corpus meum fessum est.'' (Mon corps est fatigué.)
# ''Mens sana in corpore sano optanda est.'' (Il faut souhaiter un esprit sain dans un corps sain.) (d’après Juvénal)
# ''Animus corpori imperat sicut rex civibus suis.'' (L’âme commande au corps comme un roi à ses citoyens.) (d’après Salluste et Cicéron)
''Exempla elocutionis''
* ''Res publica unum corpus est.'' (L’État est un seul corps.)
*: ''Commentaire :'' métaphore organiciste, fondatrice de toute une tradition politique (l’apologue de Ménénius Agrippa) ; l’image fait la doctrine.
* ''Corpora prima Lucretius ea vocat quae dividi non possunt.'' (Lucrèce appelle corps premiers ceux qui ne peuvent être divisés.)
*: ''Commentaire :'' corpora prima, périphrase latine pour les atomes grecs ; le traducteur philosophe préfère la périphrase native à l’emprunt. Leçon de méthode pour tout lexique philosophique latin.
== vita ==
''vita, vitae'' (subst. f.) : vie
''Exempla gradatim digesta''
# ''Vita pulchra est.'' (La vie est belle.)
# ''Vitam rusticam poetae laudant.'' (Les poètes louent la vie des champs.)
# ''Non est vivere sed valere vita est.'' (Vivre, ce n’est pas être en vie, c’est être en bonne santé.) (Martial)
''Exempla elocutionis''
* ''O vita misero longa, felici brevis!'' (Ô vie, longue pour le malheureux, brève pour l’heureux !) (Publilius Syrus)
*: ''Commentaire :'' apostrophe à la vie, double antithèse en chiasme (misero longa / felici brevis) ; la sentence dramatique faite pour être déclamée.
* ''Vitam beatam vivere omnes cupiunt.'' (Tous désirent vivre une vie heureuse.)
*: ''Commentaire :'' figure étymologique vitam vivere ; le complément interne, banal en grec, garde en latin une légère solennité. Vita beata, terme technique de l’éthique antique.
== modus ==
''modus, modi'' (subst. m.) : manière, mesure
''Exempla gradatim digesta''
# ''Quo modo vales?'' (Comment te portes-tu ?)
# ''Servandus est modus in omnibus rebus.'' (Il faut garder la mesure en toutes choses.)
# ''Eius modi homines vitandi sunt.'' (Il faut éviter les hommes de cette espèce.) Nota : eius modi = « de ce genre ».
''Exempla elocutionis''
* ''Est modus in rebus, sunt certi denique fines.'' (Il y a une mesure dans les choses, il y a enfin des limites précises.) (Horace)
*: ''Commentaire :'' parallélisme est / sunt, et denique qui feint l’impatience ; la doctrine du juste milieu énoncée sur le ton de l’évidence excédée.
* ''Modo ridet, modo flet.'' (Tantôt il rit, tantôt il pleure.)
*: ''Commentaire :'' modo... modo, anaphore alternative (cf. nunc... nunc) ; l’instabilité d’humeur peinte par le balancement de la phrase.
== nomen ==
''nomen, nominis'' (subst. n.) : nom
''Exempla gradatim digesta''
# ''Nomen meum scribo.'' (J’écris mon nom.)
# ''Nomine tantum rex erat, re vera servus.'' (Il n’était roi que de nom ; en réalité, il était esclave.) Nota : nomine / re opposés.
# ''Nomen atque omen.'' (Le nom est un présage.) (Plaute)
''Exempla elocutionis''
* ''Stat magni nominis umbra.'' (Il demeure, ombre d’un grand nom.) (Lucain)
*: ''Commentaire :'' métaphore de l’ombre pour la grandeur survivante d’elle-même (Pompée) ; le génitif magni nominis enchâssé fait tout le vers. L’un des plus beaux raccourcis de la poésie latine.
* ''Amicitiae nomine multa peccantur.'' (Bien des fautes se commettent au nom de l’amitié.)
*: ''Commentaire :'' nomine = « sous couvert de » ; l’ablatif seul suffit à dénoncer l’écart entre le mot et la chose, thème moraliste par excellence.
== miles ==
''miles, militis'' (subst. m.) : soldat
''Exempla gradatim digesta''
# ''Miles gladium gerit.'' (Le soldat porte un glaive.)
# ''Milites urbi praesidio erant.'' (Les soldats servaient de garnison à la ville.) Nota : double datif.
# ''Militis est parere, imperatoris providere.'' (Au soldat d’obéir, au général de prévoir.)
''Exempla elocutionis''
* ''Miles Romanus laborare didicit.'' (Le soldat romain a appris à peiner.)
*: ''Commentaire :'' singulier collectif des historiens ; miles = l’armée entière, vue comme un type. Stylisation épique de la prose historique.
* ''Miles gloriosus a Plauto deridetur.'' (Le soldat fanfaron est raillé par Plaute.)
*: ''Commentaire :'' miles gloriosus, type comique devenu titre ; l’épithète suffit à camper le personnage. Le style crée des emplois figés que la culture transmet.
== mors ==
''mors, mortis'' (subst. f.) : mort
''Exempla gradatim digesta''
# ''Mors vitae finis est.'' (La mort est la fin de la vie.)
# ''Morte patris audita, domum rediit.'' (À la nouvelle de la mort de son père, il rentra chez lui.) Nota : ablatif absolu.
# ''Mors, ut Stoicis videtur, nec bonum nec malum est.'' (La mort, selon les stoïciens, n’est ni un bien ni un mal.) Nota : ut videtur incise.
''Exempla elocutionis''
* ''Mors ultima linea rerum est.'' (La mort est la ligne d’arrivée des choses.) (Horace)
*: ''Commentaire :'' métaphore empruntée au cirque (la ligne blanche au bout de la piste) ; l’image sportive apprivoise l’effroi. Définition par déplacement.
* ''Pallida Mors aequo pulsat pede pauperum tabernas regumque turris.'' (La pâle Mort frappe d’un pied égal les cabanes des pauvres et les tours des rois.) (Horace)
*: ''Commentaire :'' personnification, allitération en p, antithèse sociale pauperum / regum (turris, accusatif pluriel archaïque) ; l’égalité devant la mort dite par une symétrie parfaite des images.
{{#invoke:Sous-Page|sousPage|taille préfixe=0.7em|taille titre=115%|italique=non}}
{{AutoCat}}
mn7ptrhcvw7cszjfzi1zmiz3rmara5h
Fonctionnement d'un ordinateur/Les circuits incrémenteurs/décrémenteurs
0
83953
768770
768743
2026-06-27T12:44:03Z
Mewtow
31375
/* Les implémentations optimisées */
768770
wikitext
text/x-wiki
Le circuit '''incrémenteur''' incrémente un nombre entier. Plus précisément, il prend en entrée un nombre entier, et fournit en entier le nombre suivant, il lui ajoute 1. Une telle opération peut paraitre triviale et peu intéressante. Cependant, elle est très utilisée et est absolument cruciale pour créer des compteurs, des circuits capables de compter ou décompter, qui auront droit à un chapitre dédié. De plus, au-delà de leur utilisation dans les compteurs, les incrémenteurs étaient très utilisés sur les premiers processeurs 8 bits, comme le Z-80, le 6502, les premiers processeurs x86 comme le 8008, le 8086, le 8085, et bien d'autres.
Il existe aussi des ''circuits décrémenteurs'', qui décrémentent un opérande, ainsi que des ''circuits incrémenteur-décrémenteur'', qui peuvent incrémenter ou décrémenter, selon comment on les configure. Je ne vais pas détailler ces circuits plus que ça, car de tels circuits sont assez rares, comparé à un circuit incrémenteur simple.
==Le demi-additionneur==
Le circuit incrémenteur effectue l'opération suivante :
<math>a_7</math> <math>a_6</math> <math>a_5</math> <math>a_4</math> <math>a_3</math> <math>a_2</math> <math>a_1</math> <math>a_0</math>
+ 0 0 0 0 0 0 0 1
Un incrémenteur basique doit donc faire une addition pour chaque colonne, et précisément une addition de deux bits. Il se trouve que la table d'addition est très simple en binaire. Jugez plutôt :
* 0 + 0 = 0, retenue = 0 ;
* 0 + 1 = 1, retenue = 0 ;
* 1 + 0 = 1, retenue = 0 ;
* 1 + 1 = 0, retenue = 1.
Un circuit capable d'additionner deux bits est appelé un '''demi-additionneur'''. Il dispose d'une sortie S pour la somme, et C pour la retenue (''carry'' an anglais).
===Le demi-additionneur classique===
Un demi-additionneur est très simple à construire avec les techniques vues dans les premiers chapitres. Voici sa table de vérité :
{|class="wikitable"
|-
! Retenue entrante !! Opérande 1 !! !! Retenue sortante !! Bit de somme
|-
||0||0|| ||0||0
|-
||0||1|| ||0||1
|-
||1||0|| ||0||1
|-
||1||1|| ||1||0
|}
On voit immédiatement que la colonne des retenues donne une porte ET, alors que celle du bit de somme est calculé par un XOR.
{| class="flexible"
|[[File:1-bit half-adder.svg|class=transparent|centre|Demi-addtionneur.]]
|[[File:Half-adder.svg|centre|class=transparent|Circuit d'un demi-addtionneur.]]
|}
Il existe beaucoup d'autres implémentations alternatives, qui utilisent moins de portes logiques, calculent la retenue plus rapidement, consomment moins d'énergie, et autres. Et il est intéressant d'étudier quelques alternatives, surtout qu'elles serviront plus bas, quand on étudiera le circuit incrémenteur du 8085.
===Le demi-additionneur basé sur une modification de la retenue===
Une source d'amélioration est liée à la porte XOR. En pratique, une porte XOR est composée en combinant plusieurs portes logiques ET/OU/NOR/NAND ensemble. Et il se trouve qu'il y a des redondances entre celles-ci et les portes utilisées pour calculer la retenue. Par exemple, rappelons qu'une porte XOR peut être construite avec une porte ET et deux portes NOR, comme illustré ci-dessous. Et il se trouve que la porte ET interne calcule la retenue sortante. En supprimant cette redondance, on économise quelques portes logiques. De plus, on se retrouve avec un demi-additionneur qui calcule le bit de somme à partir de la retenue sortante.
[[File:Demi-additionneur avec redondances éliminées.png|centre|vignette|upright=2|Demi-additionneur avec redondances éliminées]]
Le circuit précédent a une interprétation logique. Si vous regardez la table de vérité, vous remarquerez que la somme de deux bits est égale à l'inverse de la retenue sortante, sauf dans le cas où les deux bits additionnés valent zéro. Et le circuit précédent est basé là-dessus.
{|class="wikitable"
|-
! Retenue entrante !! Opérande 1 !! !! Retenue sortante !! Bit de somme
|- class="f_vert"
||0||0|| ||0||0
|- class="f_rouge"
||0||1|| ||0||1
|- class="f_rouge"
||1||0|| ||0||1
|- class="f_rouge"
||1||1|| ||1||0
|}
L'idée est de calculer l'inverse du bit de somme, avant de l'inverser avec une porte NON. L'inverse du bit de somme vaut 1, soit quand la retenue est à 1, soit quand les deux bits additionnés sont à 0. La porte ET calcule la retenue sortante, la première porte NOR détecte sur les deux bits d'entrée valent zéro. Une porte OU combine les deux résultat pour obtenir l'inverse du bit d'entrée, puis une porte NOn inverse le tout pour obtenir le bit de somme adéquat. Les deux portes sont fusionnées : c'est la seconde porte NOR.
[[File:Full adder HA + MUX.png|centre|vignette|upright=2.5|Demi-additionneur basé sur une inversion de la retenue sortante.]]
===Les implémentations alternatives===
Une implémentation alternative, qui n'utilise que des portes NOR, est la suivante :
[[File:Demi-additionneur fait avec des portes NOR et NAND.png|centre|vignette|upright=2|Demi-additionneur fait avec des portes NOR et NAND.]]
Une implémentation alternative utilise des portes NAND. Pour rappel, il est possible de créer une porte XOR avec 4 portes NAND. La première d'entre elle fait un NAND entre les deux bits d'entrée, ce qui fait qu'elle calcule l'inverse de la retenue sortante. Le tout est illustré ci-dessous. En théorie, on devrait utiliser une porte NON pour récupérer la retenue correcte. Mais nous verrons plus bas que ce n'est pas une obligation. Nous verrons plus bas un exemple où c'est l'inverse de la retenue qui est utilisée dans les calculs, pour rendre les calculs plus rapides.
[[File:Demi-additionneur fait avec des portes NAND.png|centre|vignette|upright=2|Demi-additionneur fait avec des portes NAND]]
==L'incrémenteur à propagation de retenue==
Maintenant que l'on sait comment additionner deux bits, reprenons l'opération d'incrémentation :
<math>a_7</math> <math>a_6</math> <math>a_5</math> <math>a_4</math> <math>a_3</math> <math>a_2</math> <math>a_1</math> <math>a_0</math>
+ 0 0 0 0 0 0 0 1
------------------------------
Sur la colonne la plus à droite, il doit ajouter un au bit de poids faible. Pour les colonnes suivantes, il faut additionner le bit de l'opérande avec la retenue de la colonne précédente. En clair, on n'additionne que deux bits à chaque colonne : un 1 sur celle tout à droite, la retenue de la colonne précédente pour les autres. Et cela nous donne une idée de comment faire pour créer le circuit incrémenteur.
Il suffit d'utiliser un demi-additionneur par colonne, et de les enchainer les uns à la suite des autres. Chaque demi-additionneur additionne le bit de l'opérande avec la retenue fournie par le demi-additionneur précédent. Le seul qui fait exception est celui pour la colonne de poids faible. Pour celui-là, il doit ajouter 1 au bit de poids faible. Le résultat est appelé un '''incrémenteur à propagation de retenue'''. Il est constitué de demi-additionneurs enchaînés les uns à la suite des autres, du bit de poids faible vers le bit de poids fort.
[[File:Circuit incrémenteur.png|centre|vignette|upright=3|Circuit incrémenteur.]]
Maintenant, regardons le demi-additionneur le plus à droite, celui pour le bit de poids faible. Son entrée de retenue entrante est mise à 1 pour faire l'incrémentation. Quelques incrémenteurs permettent de configurer cette entrée de retenue à 0 ou à 1, ce qui effectue : soit une opération identité (l'opérande est recopié sur la sortie), soit une incrémentation. Un tel circuit est nommé un '''incrémenteur commandable'''. Nous aurons à utiliser une fois ou deux de tels incrémenteurs commandables dans la suite du cours.
L'incrémenteur à propagation de retenue est le plus simple et le plus économe en portes logiques. Mais de tels incrémenteurs sont rarement utilisés. À la place, on leur préfère des incrémenteurs plus rapides, mais qui utilisent plus de portes logiques. De tels incrémenteurs accélèrent le calcul des retenues. En effet, la rapidité d'une incrémentation est limitée par la propagation de la retenue : les retenues commencent à être calculées au bit de poids fort et on doit les calculer une par une, jusqu’à atteindre le bit de poids fort. Et cette "propagation des retenues" prend du temps, d'autant plus de temps que l'opérande est longue. Il y a deux optimisations principales, appelées le ''carry skip'' et l'anticipation de retenue, que nous allons décrire ci-dessous.
==Les incrémenteurs ''carry skip''==
L'optimisation '''''carry skip''''' effectue l'incrémentation, non pas bit par bit, mais par paquets de deux bits. Le résultat est que l'incrémentation est deux fois plus rapide, ou presque. Le circuit incrémenteur est donc composé en enchainant non pas des demi-additionneurs, mais des '''incrémenteurs 2 bits''' qui incrémentent un opérande de deux bits.
[[File:Circuit incrémenteur optimisé, faisant l'incrémentation par paquet de deux bits.png|centre|vignette|upright=2.5|Circuit incrémenteur optimisé, faisant l'incrémentation par paquet de deux bits]]
Reste à concevoir l'incrémenteur 2 bits.
===Les optimisations au niveau des transistors et l'exemple de l'incrémenteur du 8085 d'Intel===
Une première solution part de deux demi-additionneurs et effectue quelques simplifications au niveau des transistors et des portes logiques. Par exemple, l'Intel 8085 a utilisé un circuit optimisé, fabriqué avec des portes NAND et NOR. La raison est qu'avec les technologies de transistors CMOS actuelles, les portes NAND et NOR utilisent moins de transistors que les portes ET et OU. Et cette possibilité a été utilisée pour effectuer des simplifications assez mineures, mais toujours bonnes à prendre. Mais nous ne pouvons pas en parler en détail ici, car nous n'avons pas encore parlé en détail des transistors. Cependant, un exemple bien précis nous est accesible : celui de l’incrémenteur du processeur 8085 d'Intel.
Le processeur 8085 d'Intel est un processeur 8 bits très ancien. Il contenait un incrémenteur 16 bits, qui était utilisé pour calculer des adresses mémoire. Et cet incrémenteur était un incrémenteur à propagation de retenue optimisé. Il utilisait lui aussi des incrémenteurs 2 bits un peu modifiés.
L'idée était que les retenues sortant des colonnes paires étaient inversées, les colonnes impaires faisaient les calculs à partir de cette retenue inversée. Les demi-additionneurs des colonnes impaires n'étaient donc pas les mêmes que ceux des colonnes paires. Leurs tables de vérité sont différentes, leurs circuits aussi, et aucun des deux ne ressemble à un demi-additionneur normal. En réalité, ils sont fabriqués à partir des implémentations vues plus haut, qui utilisent uniquement des portes NAND ou uniquement des portes NOR.
Pour comprendre l'intérêt de faire ainsi, nous devons préciser une chose importante : avec les technologies CMOS utilisées pour les processeurs depuis les années 70, les portes logiques les plus simples sont les portes NON, NOR et NAND. Les portes ET/OU sont fabriquées en combinant des portes NOR/NAND avec une porte NON, ce qui prend plus de transistors.
Un demi-additionneur est donc fabriqué comme illustré ci-dessous, en logique CMOS. Seules les portes pour le calcul de la retenue sont indiquées, la porte XOR pour le bit de somme est fabriquée à partir de plusieurs portes logiques.
[[File:Demi-additionneur en CMOS.png|centre|vignette|upright=2|Demi-additionneur en CMOS]]
Si on enchaine deux demi-additionneurs, cela donne ceci :
[[File:Brique de base de l'incrémenteur du 8085.png|centre|vignette|upright=2|Brique de base de l'incrémenteur du 8085]]
La retenue doit donc traverser quatre portes logiques, dont deux portes NON. En inversant la retenue, les portes en jaune dans le schéma précédent se simplifient. Il suffit d'utiliser la loi de de Morgan pour trouver un circuit équivalent aux portes jaunes, qui n'est autre qu'une porte NOR couplée à une porte NON. Un autre moyen d'obtenir le même résultat est de prendre un demi-additionneur fabriqué avec uniquement des portes NOR, et de retirer la porte NON adéquate. Il faut ensuite corriger l'entrée de retenue de la porte XOR, pour qu'elle fasse l'addition avec la retenue correctement. Le résultat est illustré ci-dessous.
[[File:Incrémenteur 2 bits de l'incrémenteur du 8085.png|centre|vignette|upright=2|Incrémenteur 2 bits de l'incrémenteur du 8085]]
Le circuit utilise le même nombre de portes logiques, il n'économise pas de transistors. Par contre, la retenue n'a plus qu'à traverser deux portes logiques, au lieu de quatre. La propagation de la retenue est donc plus rapide, l'incrémentation est donc plus rapide.
===Les incrémenteurs 2 bits ''carry skip''===
Une solution alternative fabrique des incrémenteurs 2 bits qui calculent séparément les deux bits du résultat et la retenue sortante. La retenue sortante est calculée à partir des bits de l’opérande, sans propager des retenues. Ainsi, la retenue sortante est connue en avance, sans passer par deux demi-additionneurs. Pour créer un tel incrémenteur, le mieux est de partir de sa table de vérité.
{|class="wikitable"
|-
! Opérande !! Retenue entrante !! !! Retenue sortante !! Résultat
|-
| 00 || 0 || || 0 || 00
|-
| 01 || 0 || || 0 || 01
|-
| 10 || 0 || || 0 || 10
|-
| 11 || 0 || || 0 || 11
|-
| colspan="5" |
|-
| 00 || 1 || || 0 || 01
|-
| 01 || 1 || || 0 || 10
|-
| 10 || 1 || || 0 || 11
|-
| 11 || 1 || || 1 || 00
|}
Elle nous dit que la retenue sortante ne vaut 1 que dans un seul cas : les deux bits d'opérande valent 1, la retenue entrante vaut 1. Elle se calcule alors avec une porte ET à trois entrées. Pour les deux bits du résultat, les équations logiques ne donnent pas un résultat satisfaisant. La solution la plus simple est d'utiliser deux demi-additionneurs et de retirer les portes logiques superflues. Le résultat est alors un '''incrémenteur 2 bits ''carry skip''''', où la retenue sortante est calculée séparément du reste.
[[File:Incrémenteur carry skip 2 bits.png|centre|vignette|upright=2.5|Incrémenteur ''carry skip'' 2 bits]]
==Les incrémenteurs à anticipation de retenue==
L''''anticipation de retenue''' calcule la retenue d'une colonne sans attendre les retenues des colonnes précédentes. Dans le cas idéal, toutes les retenues sont calculées en parallèle, en même temps, et sont ensuite envoyées aux demi-additionneurs. Il s'agit d'une optimisation qui est utilisée pour l'incrémentation, l'addition et d'autres opérations similaires. Pour l'incrémentation, déterminer la retenue ne demande pas de calculs complexes, contrairement à l'addition.
[[File:Incrémenteur à anticipation de retenues, 8 bits.png|centre|vignette|upright=2.5|Incrémenteur à anticipation de retenues, 8 bits.]]
Pour le comprendre, on peut regarder comment la retenue sortant d'un demi-additionneur est formée. Elle est calculée en faisant un ET logique entre la retenue entrante et le bit d'opérande. Une retenue est donc un ET logique entre toutes les retenues précédentes. Un incrémenteur à anticipation de retenue utilise donc une porte ET à plusieurs entrées pour calculer une retenue.
[[File:Anticipation de retenue pour un bit du résultat, incrémenteur.png|centre|vignette|upright=2.5|Anticipation de retenue pour un bit du résultat, incrémenteur]]
En théorie, on peut utiliser une porte ET à plusieurs entrées pour chaque bit de l'opérande. Cependant, cela entrainera un cout en transistors très important. Pour éviter de gaspiller trop de portes logiques, une solution est de mélanger anticipation de retenues et propagation de retenue. Par exemple, pour un incrémenteur 32 bits, on peut découper l'opérande en 4 octets : on anticipe les retenues pour chaque octet, mais l'incrémentation de chaque octet se fait avec propagation de retenue et/ou ''carry skip''.
[[File:Incrémenteur à anticipation de retenues.png|centre|vignette|upright=3|Incrémenteur hybride utilisant partiellement l'anticipation de retenues.]]
Quelques processeurs utilisaient l'anticipation de retenues. Par exemple, le processeur Z-80 de Zilog utilisait un incrémenteur pour des nombres de 16 bits, ce qui demandait des performances assez élevées. Et cet incrémenteur utilisait à la fois anticipation de retenues et ''carry skip''. Il était découpé en quatre blocs avant anticipation de retenues entre eux : un bloc regroupant les 7 bits de poids faible, suivi par un bloc de 5 bits, lui-même suivi par un bloc de 3 bits, terminé par un dernier bit isolé. A l'intérieur de ces blocs, les bits sont regroupés en paires utilisant le ''carry skip''. Pour ceux qui veulent en savoir plus sur cet incrémenteur, voici un lien sur le sujet :
* [https://www.righto.com/2013/11/the-z-80s-16-bit-incrementdecrement.html The Z-80's 16-bit increment/decrement circuit reverse engineered].
==Les décrémenteurs==
Les circuits décrémenteurs font l'inverse d'un incrémenteur : au lieu d'ajouter 1 à un opérande, ils lui soustraient 1. La différence n'est pas énorme, ce qui fait que les circuits incrémenteurs et décrémenteurs se ressemblent beaucoup. Là où les incrémenteurs sont fabriqués à partir de demi-additionneurs, les décrémenteurs sont bâtis avec des demi-soustracteurs.
La soustraction se fait en binaire comme en décimal. On soustrait deux chiffres, puis on doit propager une éventuelle retenue sur la colonne suivante. La retenue apparait quand le chiffre soustrait est plus grand que l'autre chiffre. Elle est propagée sur la colonne suivante, où elle doit être soustraite du résultat. Pour le dire autrement, après avoir soustrait un chiffre, on doit de plus soustraire la retenue de la colonne précédente.
La décrémentation effectue le calcul suivant :
<math>a_7</math> <math>a_6</math> <math>a_5</math> <math>a_4</math> <math>a_3</math> <math>a_2</math> <math>a_1</math> <math>a_0</math>
- 0 0 0 0 0 0 0 1
La colonne la plus à droite soustrait un 1. Par contre, les colonnes suivantes soustraient un zéro. Soustraire un zéro ne change rien. Mais il ne faut pas oublier de soustraire une éventuellement retenue, propagée depuis la colonne précédente. En clair, un décrémenteur peut se construire si on sait soustraire deux bits. On soustrait un 1 sur la colonne la plus à droite, on soustrait une retenue sur les autres colonnes. Et pour soustraire deux bits, il faut utiliser un demi-soustracteur.
===Le demi-soustracteur===
Un '''demi-soustracteur''' soustrait deux bits. Pour comprendre comment soustraire deux bits, traitons les quatre cas possibles un par un.
Premièrement, soustraire zéro à un bit ne changera rien :
* 0 - 0 = 0 ;
* 1 - 0 = 1.
Si on soustrait 1 à un bit qui vaut 1, on obtient zéro.
* 1 - 1 = 0.
Maintenant, que se passe-t-il si on soustrait 1 à 0 ? Voici le résultat :
* 0 - 1 = 1 et une retenue propagée sur la colonne suivante, où elle est soustraite.
La table de soustraction est donc al suivante :
* 0 - 0 = 0 ;
* 0 - 1 = 1 et une retenue ;
* 1 - 0 = 1 ;
* 1 - 1 = 0.
Si on construit la table de vérité, et qu'on construit le circuit associé, on tombe sur ce circuit.
[[File:Half Subtractor Vektor.svg|centre|vignette|Demi-soustracteur.]]
un décrémenteur à propagation de retenue se construit comme un incrémenteur, sauf qu'on remplace les demi-additionneurs par des demi-soustracteurs. Il est possible d'utiliser l'anticipation de retenue, mais les circuits sont cependant différents. IOdem avec le ''carry skip'', et toute autre optimisation possible.
===La fusion entre incrémenteur et décrémenteur===
Vous l'aurez sans doute remarqué, mais le demi-soustracteur ressemble beaucoup au demi-additionneur. La seule différence est une porte NON ajoutée au bon endroit dans le demi-soustracteur.
{|
|[[File:Half-adder.svg|vignette|Demi-additionneur]]
|[[File:Half Subtractor Vektor.svg|vignette|upright=1.3|Demi-soustracteur]]
|}
La conséquence est qu'il est possible de fusionner un demi-additionneur avec un demi-soustracteur. L'idée est de remplacer la porte NON du demi-soustracteur par un inverseur commandable. Si on envoie un zéro sur l'entrée de commande, l'inverseur commandable se comportera comme une porte OUI. Le circuit sera alors identique à un demi-additionneur. Si on envoie un 1 sur l'entrée de commande, l'inverseur commandable se comportera comme une porte NON. le circuit sera alors un demi-soustracteur.
Le résultat est que l'on peut créer un circuit qui sert alternativement d'incrémenteur et de décrémenteur. Il suffit pour cela d'utiliser plusieurs circuits précédents, et de commander tous les inverseurs commandables avec le même bit d'entrée. Ce bit sélectionne l'opération demandée : incrémentation ou décrémentation.
<noinclude>
{{NavChapitre | book=Fonctionnement d'un ordinateur
| prev=Les circuits de sélection
| prevText=Les circuits de sélection
| next=Les bascules : des mémoires de 1 bit
| nextText=Les bascules : des mémoires de 1 bit
}}
</noinclude>
bjfpji883lgfxy2w19hznsrlz1rocsh
768772
768770
2026-06-27T13:01:19Z
Mewtow
31375
/* Les décrémenteurs */
768772
wikitext
text/x-wiki
Le circuit '''incrémenteur''' incrémente un nombre entier. Plus précisément, il prend en entrée un nombre entier, et fournit en entier le nombre suivant, il lui ajoute 1. Une telle opération peut paraitre triviale et peu intéressante. Cependant, elle est très utilisée et est absolument cruciale pour créer des compteurs, des circuits capables de compter ou décompter, qui auront droit à un chapitre dédié. De plus, au-delà de leur utilisation dans les compteurs, les incrémenteurs étaient très utilisés sur les premiers processeurs 8 bits, comme le Z-80, le 6502, les premiers processeurs x86 comme le 8008, le 8086, le 8085, et bien d'autres.
Il existe aussi des ''circuits décrémenteurs'', qui décrémentent un opérande, ainsi que des ''circuits incrémenteur-décrémenteur'', qui peuvent incrémenter ou décrémenter, selon comment on les configure. Je ne vais pas détailler ces circuits plus que ça, car de tels circuits sont assez rares, comparé à un circuit incrémenteur simple.
==Le demi-additionneur==
Le circuit incrémenteur effectue l'opération suivante :
<math>a_7</math> <math>a_6</math> <math>a_5</math> <math>a_4</math> <math>a_3</math> <math>a_2</math> <math>a_1</math> <math>a_0</math>
+ 0 0 0 0 0 0 0 1
Un incrémenteur basique doit donc faire une addition pour chaque colonne, et précisément une addition de deux bits. Il se trouve que la table d'addition est très simple en binaire. Jugez plutôt :
* 0 + 0 = 0, retenue = 0 ;
* 0 + 1 = 1, retenue = 0 ;
* 1 + 0 = 1, retenue = 0 ;
* 1 + 1 = 0, retenue = 1.
Un circuit capable d'additionner deux bits est appelé un '''demi-additionneur'''. Il dispose d'une sortie S pour la somme, et C pour la retenue (''carry'' an anglais).
===Le demi-additionneur classique===
Un demi-additionneur est très simple à construire avec les techniques vues dans les premiers chapitres. Voici sa table de vérité :
{|class="wikitable"
|-
! Retenue entrante !! Opérande 1 !! !! Retenue sortante !! Bit de somme
|-
||0||0|| ||0||0
|-
||0||1|| ||0||1
|-
||1||0|| ||0||1
|-
||1||1|| ||1||0
|}
On voit immédiatement que la colonne des retenues donne une porte ET, alors que celle du bit de somme est calculé par un XOR.
{| class="flexible"
|[[File:1-bit half-adder.svg|class=transparent|centre|Demi-addtionneur.]]
|[[File:Half-adder.svg|centre|class=transparent|Circuit d'un demi-addtionneur.]]
|}
Il existe beaucoup d'autres implémentations alternatives, qui utilisent moins de portes logiques, calculent la retenue plus rapidement, consomment moins d'énergie, et autres. Et il est intéressant d'étudier quelques alternatives, surtout qu'elles serviront plus bas, quand on étudiera le circuit incrémenteur du 8085.
===Le demi-additionneur basé sur une modification de la retenue===
Une source d'amélioration est liée à la porte XOR. En pratique, une porte XOR est composée en combinant plusieurs portes logiques ET/OU/NOR/NAND ensemble. Et il se trouve qu'il y a des redondances entre celles-ci et les portes utilisées pour calculer la retenue. Par exemple, rappelons qu'une porte XOR peut être construite avec une porte ET et deux portes NOR, comme illustré ci-dessous. Et il se trouve que la porte ET interne calcule la retenue sortante. En supprimant cette redondance, on économise quelques portes logiques. De plus, on se retrouve avec un demi-additionneur qui calcule le bit de somme à partir de la retenue sortante.
[[File:Demi-additionneur avec redondances éliminées.png|centre|vignette|upright=2|Demi-additionneur avec redondances éliminées]]
Le circuit précédent a une interprétation logique. Si vous regardez la table de vérité, vous remarquerez que la somme de deux bits est égale à l'inverse de la retenue sortante, sauf dans le cas où les deux bits additionnés valent zéro. Et le circuit précédent est basé là-dessus.
{|class="wikitable"
|-
! Retenue entrante !! Opérande 1 !! !! Retenue sortante !! Bit de somme
|- class="f_vert"
||0||0|| ||0||0
|- class="f_rouge"
||0||1|| ||0||1
|- class="f_rouge"
||1||0|| ||0||1
|- class="f_rouge"
||1||1|| ||1||0
|}
L'idée est de calculer l'inverse du bit de somme, avant de l'inverser avec une porte NON. L'inverse du bit de somme vaut 1, soit quand la retenue est à 1, soit quand les deux bits additionnés sont à 0. La porte ET calcule la retenue sortante, la première porte NOR détecte sur les deux bits d'entrée valent zéro. Une porte OU combine les deux résultat pour obtenir l'inverse du bit d'entrée, puis une porte NOn inverse le tout pour obtenir le bit de somme adéquat. Les deux portes sont fusionnées : c'est la seconde porte NOR.
[[File:Full adder HA + MUX.png|centre|vignette|upright=2.5|Demi-additionneur basé sur une inversion de la retenue sortante.]]
===Les implémentations alternatives===
Une implémentation alternative, qui n'utilise que des portes NOR, est la suivante :
[[File:Demi-additionneur fait avec des portes NOR et NAND.png|centre|vignette|upright=2|Demi-additionneur fait avec des portes NOR et NAND.]]
Une implémentation alternative utilise des portes NAND. Pour rappel, il est possible de créer une porte XOR avec 4 portes NAND. La première d'entre elle fait un NAND entre les deux bits d'entrée, ce qui fait qu'elle calcule l'inverse de la retenue sortante. Le tout est illustré ci-dessous. En théorie, on devrait utiliser une porte NON pour récupérer la retenue correcte. Mais nous verrons plus bas que ce n'est pas une obligation. Nous verrons plus bas un exemple où c'est l'inverse de la retenue qui est utilisée dans les calculs, pour rendre les calculs plus rapides.
[[File:Demi-additionneur fait avec des portes NAND.png|centre|vignette|upright=2|Demi-additionneur fait avec des portes NAND]]
==L'incrémenteur à propagation de retenue==
Maintenant que l'on sait comment additionner deux bits, reprenons l'opération d'incrémentation :
<math>a_7</math> <math>a_6</math> <math>a_5</math> <math>a_4</math> <math>a_3</math> <math>a_2</math> <math>a_1</math> <math>a_0</math>
+ 0 0 0 0 0 0 0 1
------------------------------
Sur la colonne la plus à droite, il doit ajouter un au bit de poids faible. Pour les colonnes suivantes, il faut additionner le bit de l'opérande avec la retenue de la colonne précédente. En clair, on n'additionne que deux bits à chaque colonne : un 1 sur celle tout à droite, la retenue de la colonne précédente pour les autres. Et cela nous donne une idée de comment faire pour créer le circuit incrémenteur.
Il suffit d'utiliser un demi-additionneur par colonne, et de les enchainer les uns à la suite des autres. Chaque demi-additionneur additionne le bit de l'opérande avec la retenue fournie par le demi-additionneur précédent. Le seul qui fait exception est celui pour la colonne de poids faible. Pour celui-là, il doit ajouter 1 au bit de poids faible. Le résultat est appelé un '''incrémenteur à propagation de retenue'''. Il est constitué de demi-additionneurs enchaînés les uns à la suite des autres, du bit de poids faible vers le bit de poids fort.
[[File:Circuit incrémenteur.png|centre|vignette|upright=3|Circuit incrémenteur.]]
Maintenant, regardons le demi-additionneur le plus à droite, celui pour le bit de poids faible. Son entrée de retenue entrante est mise à 1 pour faire l'incrémentation. Quelques incrémenteurs permettent de configurer cette entrée de retenue à 0 ou à 1, ce qui effectue : soit une opération identité (l'opérande est recopié sur la sortie), soit une incrémentation. Un tel circuit est nommé un '''incrémenteur commandable'''. Nous aurons à utiliser une fois ou deux de tels incrémenteurs commandables dans la suite du cours.
L'incrémenteur à propagation de retenue est le plus simple et le plus économe en portes logiques. Mais de tels incrémenteurs sont rarement utilisés. À la place, on leur préfère des incrémenteurs plus rapides, mais qui utilisent plus de portes logiques. De tels incrémenteurs accélèrent le calcul des retenues. En effet, la rapidité d'une incrémentation est limitée par la propagation de la retenue : les retenues commencent à être calculées au bit de poids fort et on doit les calculer une par une, jusqu’à atteindre le bit de poids fort. Et cette "propagation des retenues" prend du temps, d'autant plus de temps que l'opérande est longue. Il y a deux optimisations principales, appelées le ''carry skip'' et l'anticipation de retenue, que nous allons décrire ci-dessous.
==Les incrémenteurs ''carry skip''==
L'optimisation '''''carry skip''''' effectue l'incrémentation, non pas bit par bit, mais par paquets de deux bits. Le résultat est que l'incrémentation est deux fois plus rapide, ou presque. Le circuit incrémenteur est donc composé en enchainant non pas des demi-additionneurs, mais des '''incrémenteurs 2 bits''' qui incrémentent un opérande de deux bits.
[[File:Circuit incrémenteur optimisé, faisant l'incrémentation par paquet de deux bits.png|centre|vignette|upright=2.5|Circuit incrémenteur optimisé, faisant l'incrémentation par paquet de deux bits]]
Reste à concevoir l'incrémenteur 2 bits.
===Les optimisations au niveau des transistors et l'exemple de l'incrémenteur du 8085 d'Intel===
Une première solution part de deux demi-additionneurs et effectue quelques simplifications au niveau des transistors et des portes logiques. Par exemple, l'Intel 8085 a utilisé un circuit optimisé, fabriqué avec des portes NAND et NOR. La raison est qu'avec les technologies de transistors CMOS actuelles, les portes NAND et NOR utilisent moins de transistors que les portes ET et OU. Et cette possibilité a été utilisée pour effectuer des simplifications assez mineures, mais toujours bonnes à prendre. Mais nous ne pouvons pas en parler en détail ici, car nous n'avons pas encore parlé en détail des transistors. Cependant, un exemple bien précis nous est accesible : celui de l’incrémenteur du processeur 8085 d'Intel.
Le processeur 8085 d'Intel est un processeur 8 bits très ancien. Il contenait un incrémenteur 16 bits, qui était utilisé pour calculer des adresses mémoire. Et cet incrémenteur était un incrémenteur à propagation de retenue optimisé. Il utilisait lui aussi des incrémenteurs 2 bits un peu modifiés.
L'idée était que les retenues sortant des colonnes paires étaient inversées, les colonnes impaires faisaient les calculs à partir de cette retenue inversée. Les demi-additionneurs des colonnes impaires n'étaient donc pas les mêmes que ceux des colonnes paires. Leurs tables de vérité sont différentes, leurs circuits aussi, et aucun des deux ne ressemble à un demi-additionneur normal. En réalité, ils sont fabriqués à partir des implémentations vues plus haut, qui utilisent uniquement des portes NAND ou uniquement des portes NOR.
Pour comprendre l'intérêt de faire ainsi, nous devons préciser une chose importante : avec les technologies CMOS utilisées pour les processeurs depuis les années 70, les portes logiques les plus simples sont les portes NON, NOR et NAND. Les portes ET/OU sont fabriquées en combinant des portes NOR/NAND avec une porte NON, ce qui prend plus de transistors.
Un demi-additionneur est donc fabriqué comme illustré ci-dessous, en logique CMOS. Seules les portes pour le calcul de la retenue sont indiquées, la porte XOR pour le bit de somme est fabriquée à partir de plusieurs portes logiques.
[[File:Demi-additionneur en CMOS.png|centre|vignette|upright=2|Demi-additionneur en CMOS]]
Si on enchaine deux demi-additionneurs, cela donne ceci :
[[File:Brique de base de l'incrémenteur du 8085.png|centre|vignette|upright=2|Brique de base de l'incrémenteur du 8085]]
La retenue doit donc traverser quatre portes logiques, dont deux portes NON. En inversant la retenue, les portes en jaune dans le schéma précédent se simplifient. Il suffit d'utiliser la loi de de Morgan pour trouver un circuit équivalent aux portes jaunes, qui n'est autre qu'une porte NOR couplée à une porte NON. Un autre moyen d'obtenir le même résultat est de prendre un demi-additionneur fabriqué avec uniquement des portes NOR, et de retirer la porte NON adéquate. Il faut ensuite corriger l'entrée de retenue de la porte XOR, pour qu'elle fasse l'addition avec la retenue correctement. Le résultat est illustré ci-dessous.
[[File:Incrémenteur 2 bits de l'incrémenteur du 8085.png|centre|vignette|upright=2|Incrémenteur 2 bits de l'incrémenteur du 8085]]
Le circuit utilise le même nombre de portes logiques, il n'économise pas de transistors. Par contre, la retenue n'a plus qu'à traverser deux portes logiques, au lieu de quatre. La propagation de la retenue est donc plus rapide, l'incrémentation est donc plus rapide.
===Les incrémenteurs 2 bits ''carry skip''===
Une solution alternative fabrique des incrémenteurs 2 bits qui calculent séparément les deux bits du résultat et la retenue sortante. La retenue sortante est calculée à partir des bits de l’opérande, sans propager des retenues. Ainsi, la retenue sortante est connue en avance, sans passer par deux demi-additionneurs. Pour créer un tel incrémenteur, le mieux est de partir de sa table de vérité.
{|class="wikitable"
|-
! Opérande !! Retenue entrante !! !! Retenue sortante !! Résultat
|-
| 00 || 0 || || 0 || 00
|-
| 01 || 0 || || 0 || 01
|-
| 10 || 0 || || 0 || 10
|-
| 11 || 0 || || 0 || 11
|-
| colspan="5" |
|-
| 00 || 1 || || 0 || 01
|-
| 01 || 1 || || 0 || 10
|-
| 10 || 1 || || 0 || 11
|-
| 11 || 1 || || 1 || 00
|}
Elle nous dit que la retenue sortante ne vaut 1 que dans un seul cas : les deux bits d'opérande valent 1, la retenue entrante vaut 1. Elle se calcule alors avec une porte ET à trois entrées. Pour les deux bits du résultat, les équations logiques ne donnent pas un résultat satisfaisant. La solution la plus simple est d'utiliser deux demi-additionneurs et de retirer les portes logiques superflues. Le résultat est alors un '''incrémenteur 2 bits ''carry skip''''', où la retenue sortante est calculée séparément du reste.
[[File:Incrémenteur carry skip 2 bits.png|centre|vignette|upright=2.5|Incrémenteur ''carry skip'' 2 bits]]
==Les incrémenteurs à anticipation de retenue==
L''''anticipation de retenue''' calcule la retenue d'une colonne sans attendre les retenues des colonnes précédentes. Dans le cas idéal, toutes les retenues sont calculées en parallèle, en même temps, et sont ensuite envoyées aux demi-additionneurs. Il s'agit d'une optimisation qui est utilisée pour l'incrémentation, l'addition et d'autres opérations similaires. Pour l'incrémentation, déterminer la retenue ne demande pas de calculs complexes, contrairement à l'addition.
[[File:Incrémenteur à anticipation de retenues, 8 bits.png|centre|vignette|upright=2.5|Incrémenteur à anticipation de retenues, 8 bits.]]
Pour le comprendre, on peut regarder comment la retenue sortant d'un demi-additionneur est formée. Elle est calculée en faisant un ET logique entre la retenue entrante et le bit d'opérande. Une retenue est donc un ET logique entre toutes les retenues précédentes. Un incrémenteur à anticipation de retenue utilise donc une porte ET à plusieurs entrées pour calculer une retenue.
[[File:Anticipation de retenue pour un bit du résultat, incrémenteur.png|centre|vignette|upright=2.5|Anticipation de retenue pour un bit du résultat, incrémenteur]]
En théorie, on peut utiliser une porte ET à plusieurs entrées pour chaque bit de l'opérande. Cependant, cela entrainera un cout en transistors très important. Pour éviter de gaspiller trop de portes logiques, une solution est de mélanger anticipation de retenues et propagation de retenue. Par exemple, pour un incrémenteur 32 bits, on peut découper l'opérande en 4 octets : on anticipe les retenues pour chaque octet, mais l'incrémentation de chaque octet se fait avec propagation de retenue et/ou ''carry skip''.
[[File:Incrémenteur à anticipation de retenues.png|centre|vignette|upright=3|Incrémenteur hybride utilisant partiellement l'anticipation de retenues.]]
Quelques processeurs utilisaient l'anticipation de retenues. Par exemple, le processeur Z-80 de Zilog utilisait un incrémenteur pour des nombres de 16 bits, ce qui demandait des performances assez élevées. Et cet incrémenteur utilisait à la fois anticipation de retenues et ''carry skip''. Il était découpé en quatre blocs avant anticipation de retenues entre eux : un bloc regroupant les 7 bits de poids faible, suivi par un bloc de 5 bits, lui-même suivi par un bloc de 3 bits, terminé par un dernier bit isolé. A l'intérieur de ces blocs, les bits sont regroupés en paires utilisant le ''carry skip''. Pour ceux qui veulent en savoir plus sur cet incrémenteur, voici un lien sur le sujet :
* [https://www.righto.com/2013/11/the-z-80s-16-bit-incrementdecrement.html The Z-80's 16-bit increment/decrement circuit reverse engineered].
==Les décrémenteurs==
Les circuits décrémenteurs font l'inverse d'un incrémenteur : au lieu d'ajouter 1 à un opérande, ils lui soustraient 1. La différence n'est pas énorme, ce qui fait que les circuits incrémenteurs et décrémenteurs se ressemblent beaucoup. Là où les incrémenteurs sont fabriqués à partir de demi-additionneurs, les décrémenteurs sont bâtis avec des demi-soustracteurs.
La soustraction se fait en binaire comme en décimal. On soustrait deux chiffres, puis on doit propager une éventuelle retenue sur la colonne suivante. La retenue apparait quand le chiffre soustrait est plus grand que l'autre chiffre. Elle est propagée sur la colonne suivante, où elle doit être soustraite du résultat. Pour le dire autrement, après avoir soustrait un chiffre, on doit de plus soustraire la retenue de la colonne précédente.
La décrémentation effectue le calcul suivant :
<math>a_7</math> <math>a_6</math> <math>a_5</math> <math>a_4</math> <math>a_3</math> <math>a_2</math> <math>a_1</math> <math>a_0</math>
- 0 0 0 0 0 0 0 1
La colonne la plus à droite soustrait un 1. Par contre, les colonnes suivantes soustraient un zéro. Soustraire un zéro ne change rien. Mais il ne faut pas oublier de soustraire une éventuellement retenue, propagée depuis la colonne précédente. En clair, un décrémenteur peut se construire si on sait soustraire deux bits. On soustrait un 1 sur la colonne la plus à droite, on soustrait une retenue sur les autres colonnes. Et pour soustraire deux bits, il faut utiliser un demi-soustracteur.
===Le demi-soustracteur===
Un '''demi-soustracteur''' soustrait deux bits. Pour comprendre comment soustraire deux bits, traitons les quatre cas possibles un par un.
Premièrement, soustraire zéro à un bit ne changera rien :
* 0 - 0 = 0 ;
* 1 - 0 = 1.
Si on soustrait 1 à un bit qui vaut 1, on obtient zéro.
* 1 - 1 = 0.
Maintenant, que se passe-t-il si on soustrait 1 à 0 ? Voici le résultat :
* 0 - 1 = 1 et une retenue propagée sur la colonne suivante, où elle est soustraite.
La table de soustraction est donc la suivante :
* 0 - 0 = 0 ;
* 0 - 1 = 1 et une retenue ;
* 1 - 0 = 1 ;
* 1 - 1 = 0.
Si on construit la table de vérité, et qu'on construit le circuit associé, on tombe sur ce circuit. Détail important : le demi-soustracteur fait ici la soustraction X - Y. En anglais, X est appelé le ''Minuend'' et Y est appelé le ''subtrahend'', mais ces termes n'ont pas de traduction française.
[[File:Half Subtractor Vektor.svg|centre|vignette|Demi-soustracteur.]]
===Les demi-additionneurs et demi-soustracteurs===
Vous l'aurez sans doute remarqué, mais le demi-soustracteur ressemble beaucoup au demi-additionneur. La seule différence est une porte NON ajoutée au bon endroit dans le demi-soustracteur. Et attention : cette porte NON inverse le bit X, sachant que le demi-soustracteur fait la soustraction X - Y.
{|
|[[File:Half-adder.svg|vignette|Demi-additionneur]]
|[[File:Half Subtractor Vektor.svg|vignette|upright=1.3|Demi-soustracteur]]
|}
La conséquence est qu'il est possible de fusionner un demi-additionneur avec un demi-soustracteur. L'idée est de remplacer la porte NON du demi-soustracteur par un inverseur commandable. Si on envoie un zéro sur l'entrée de commande, l'inverseur commandable se comportera comme une porte OUI. Le circuit sera alors identique à un demi-additionneur. Si on envoie un 1 sur l'entrée de commande, l'inverseur commandable se comportera comme une porte NON. le circuit sera alors un demi-soustracteur.
Le résultat est que l'on peut créer un circuit qui sert alternativement d'incrémenteur et de décrémenteur. Il suffit pour cela d'utiliser plusieurs circuits précédents, et de commander tous les inverseurs commandables avec le même bit d'entrée. Ce bit sélectionne l'opération demandée : incrémentation ou décrémentation.
===L'incrémenteur/décrémenteurs à propagation de retenue===
[[File:AddSub Network 3 Bit.svg|vignette|Décrémenteur à propagation de retenue.]]
un décrémenteur à propagation de retenue se construit comme un incrémenteur, sauf qu'on remplace les demi-additionneurs par des demi-soustracteurs. En clair, on enchaine plusieurs demi-soustracteurs les uns à la suite des autres. Le circuit est équivalent à un incrémenteur à propagation de retenue, auquel on aurait ajouté un circuit inverseur. Le circuit inverseur en question est juste composé d'une couche de portes NON, qui inverse l'opérande à décrémenter.
Il est possible d'utiliser l'anticipation de retenue, mais les circuits sont cependant différents. Idem avec le ''carry skip'', et toute autre optimisation possible. La raison est que les retenues sont calculées différemment, ce qui fait que l’anticipation de retenue doit être adaptée.
Il est possible de fusionner un décrémenteur et un incrémenteur assez simplement. La seule différence étant une couche de portes NON qui inverse l'opérande, on peut remplacer ces portes NON par des inverseurs commandables. Les inverseurs commandables sont configurables pour servir soit de portes NON (mode décrémenteur), soit de portes OUI (mode incrémenteur).
<noinclude>
{{NavChapitre | book=Fonctionnement d'un ordinateur
| prev=Les circuits de sélection
| prevText=Les circuits de sélection
| next=Les bascules : des mémoires de 1 bit
| nextText=Les bascules : des mémoires de 1 bit
}}
</noinclude>
3kjmcx0er0pjk4k43omzm8olefnywow
768773
768772
2026-06-27T13:01:40Z
Mewtow
31375
/* Les demi-additionneurs et demi-soustracteurs */
768773
wikitext
text/x-wiki
Le circuit '''incrémenteur''' incrémente un nombre entier. Plus précisément, il prend en entrée un nombre entier, et fournit en entier le nombre suivant, il lui ajoute 1. Une telle opération peut paraitre triviale et peu intéressante. Cependant, elle est très utilisée et est absolument cruciale pour créer des compteurs, des circuits capables de compter ou décompter, qui auront droit à un chapitre dédié. De plus, au-delà de leur utilisation dans les compteurs, les incrémenteurs étaient très utilisés sur les premiers processeurs 8 bits, comme le Z-80, le 6502, les premiers processeurs x86 comme le 8008, le 8086, le 8085, et bien d'autres.
Il existe aussi des ''circuits décrémenteurs'', qui décrémentent un opérande, ainsi que des ''circuits incrémenteur-décrémenteur'', qui peuvent incrémenter ou décrémenter, selon comment on les configure. Je ne vais pas détailler ces circuits plus que ça, car de tels circuits sont assez rares, comparé à un circuit incrémenteur simple.
==Le demi-additionneur==
Le circuit incrémenteur effectue l'opération suivante :
<math>a_7</math> <math>a_6</math> <math>a_5</math> <math>a_4</math> <math>a_3</math> <math>a_2</math> <math>a_1</math> <math>a_0</math>
+ 0 0 0 0 0 0 0 1
Un incrémenteur basique doit donc faire une addition pour chaque colonne, et précisément une addition de deux bits. Il se trouve que la table d'addition est très simple en binaire. Jugez plutôt :
* 0 + 0 = 0, retenue = 0 ;
* 0 + 1 = 1, retenue = 0 ;
* 1 + 0 = 1, retenue = 0 ;
* 1 + 1 = 0, retenue = 1.
Un circuit capable d'additionner deux bits est appelé un '''demi-additionneur'''. Il dispose d'une sortie S pour la somme, et C pour la retenue (''carry'' an anglais).
===Le demi-additionneur classique===
Un demi-additionneur est très simple à construire avec les techniques vues dans les premiers chapitres. Voici sa table de vérité :
{|class="wikitable"
|-
! Retenue entrante !! Opérande 1 !! !! Retenue sortante !! Bit de somme
|-
||0||0|| ||0||0
|-
||0||1|| ||0||1
|-
||1||0|| ||0||1
|-
||1||1|| ||1||0
|}
On voit immédiatement que la colonne des retenues donne une porte ET, alors que celle du bit de somme est calculé par un XOR.
{| class="flexible"
|[[File:1-bit half-adder.svg|class=transparent|centre|Demi-addtionneur.]]
|[[File:Half-adder.svg|centre|class=transparent|Circuit d'un demi-addtionneur.]]
|}
Il existe beaucoup d'autres implémentations alternatives, qui utilisent moins de portes logiques, calculent la retenue plus rapidement, consomment moins d'énergie, et autres. Et il est intéressant d'étudier quelques alternatives, surtout qu'elles serviront plus bas, quand on étudiera le circuit incrémenteur du 8085.
===Le demi-additionneur basé sur une modification de la retenue===
Une source d'amélioration est liée à la porte XOR. En pratique, une porte XOR est composée en combinant plusieurs portes logiques ET/OU/NOR/NAND ensemble. Et il se trouve qu'il y a des redondances entre celles-ci et les portes utilisées pour calculer la retenue. Par exemple, rappelons qu'une porte XOR peut être construite avec une porte ET et deux portes NOR, comme illustré ci-dessous. Et il se trouve que la porte ET interne calcule la retenue sortante. En supprimant cette redondance, on économise quelques portes logiques. De plus, on se retrouve avec un demi-additionneur qui calcule le bit de somme à partir de la retenue sortante.
[[File:Demi-additionneur avec redondances éliminées.png|centre|vignette|upright=2|Demi-additionneur avec redondances éliminées]]
Le circuit précédent a une interprétation logique. Si vous regardez la table de vérité, vous remarquerez que la somme de deux bits est égale à l'inverse de la retenue sortante, sauf dans le cas où les deux bits additionnés valent zéro. Et le circuit précédent est basé là-dessus.
{|class="wikitable"
|-
! Retenue entrante !! Opérande 1 !! !! Retenue sortante !! Bit de somme
|- class="f_vert"
||0||0|| ||0||0
|- class="f_rouge"
||0||1|| ||0||1
|- class="f_rouge"
||1||0|| ||0||1
|- class="f_rouge"
||1||1|| ||1||0
|}
L'idée est de calculer l'inverse du bit de somme, avant de l'inverser avec une porte NON. L'inverse du bit de somme vaut 1, soit quand la retenue est à 1, soit quand les deux bits additionnés sont à 0. La porte ET calcule la retenue sortante, la première porte NOR détecte sur les deux bits d'entrée valent zéro. Une porte OU combine les deux résultat pour obtenir l'inverse du bit d'entrée, puis une porte NOn inverse le tout pour obtenir le bit de somme adéquat. Les deux portes sont fusionnées : c'est la seconde porte NOR.
[[File:Full adder HA + MUX.png|centre|vignette|upright=2.5|Demi-additionneur basé sur une inversion de la retenue sortante.]]
===Les implémentations alternatives===
Une implémentation alternative, qui n'utilise que des portes NOR, est la suivante :
[[File:Demi-additionneur fait avec des portes NOR et NAND.png|centre|vignette|upright=2|Demi-additionneur fait avec des portes NOR et NAND.]]
Une implémentation alternative utilise des portes NAND. Pour rappel, il est possible de créer une porte XOR avec 4 portes NAND. La première d'entre elle fait un NAND entre les deux bits d'entrée, ce qui fait qu'elle calcule l'inverse de la retenue sortante. Le tout est illustré ci-dessous. En théorie, on devrait utiliser une porte NON pour récupérer la retenue correcte. Mais nous verrons plus bas que ce n'est pas une obligation. Nous verrons plus bas un exemple où c'est l'inverse de la retenue qui est utilisée dans les calculs, pour rendre les calculs plus rapides.
[[File:Demi-additionneur fait avec des portes NAND.png|centre|vignette|upright=2|Demi-additionneur fait avec des portes NAND]]
==L'incrémenteur à propagation de retenue==
Maintenant que l'on sait comment additionner deux bits, reprenons l'opération d'incrémentation :
<math>a_7</math> <math>a_6</math> <math>a_5</math> <math>a_4</math> <math>a_3</math> <math>a_2</math> <math>a_1</math> <math>a_0</math>
+ 0 0 0 0 0 0 0 1
------------------------------
Sur la colonne la plus à droite, il doit ajouter un au bit de poids faible. Pour les colonnes suivantes, il faut additionner le bit de l'opérande avec la retenue de la colonne précédente. En clair, on n'additionne que deux bits à chaque colonne : un 1 sur celle tout à droite, la retenue de la colonne précédente pour les autres. Et cela nous donne une idée de comment faire pour créer le circuit incrémenteur.
Il suffit d'utiliser un demi-additionneur par colonne, et de les enchainer les uns à la suite des autres. Chaque demi-additionneur additionne le bit de l'opérande avec la retenue fournie par le demi-additionneur précédent. Le seul qui fait exception est celui pour la colonne de poids faible. Pour celui-là, il doit ajouter 1 au bit de poids faible. Le résultat est appelé un '''incrémenteur à propagation de retenue'''. Il est constitué de demi-additionneurs enchaînés les uns à la suite des autres, du bit de poids faible vers le bit de poids fort.
[[File:Circuit incrémenteur.png|centre|vignette|upright=3|Circuit incrémenteur.]]
Maintenant, regardons le demi-additionneur le plus à droite, celui pour le bit de poids faible. Son entrée de retenue entrante est mise à 1 pour faire l'incrémentation. Quelques incrémenteurs permettent de configurer cette entrée de retenue à 0 ou à 1, ce qui effectue : soit une opération identité (l'opérande est recopié sur la sortie), soit une incrémentation. Un tel circuit est nommé un '''incrémenteur commandable'''. Nous aurons à utiliser une fois ou deux de tels incrémenteurs commandables dans la suite du cours.
L'incrémenteur à propagation de retenue est le plus simple et le plus économe en portes logiques. Mais de tels incrémenteurs sont rarement utilisés. À la place, on leur préfère des incrémenteurs plus rapides, mais qui utilisent plus de portes logiques. De tels incrémenteurs accélèrent le calcul des retenues. En effet, la rapidité d'une incrémentation est limitée par la propagation de la retenue : les retenues commencent à être calculées au bit de poids fort et on doit les calculer une par une, jusqu’à atteindre le bit de poids fort. Et cette "propagation des retenues" prend du temps, d'autant plus de temps que l'opérande est longue. Il y a deux optimisations principales, appelées le ''carry skip'' et l'anticipation de retenue, que nous allons décrire ci-dessous.
==Les incrémenteurs ''carry skip''==
L'optimisation '''''carry skip''''' effectue l'incrémentation, non pas bit par bit, mais par paquets de deux bits. Le résultat est que l'incrémentation est deux fois plus rapide, ou presque. Le circuit incrémenteur est donc composé en enchainant non pas des demi-additionneurs, mais des '''incrémenteurs 2 bits''' qui incrémentent un opérande de deux bits.
[[File:Circuit incrémenteur optimisé, faisant l'incrémentation par paquet de deux bits.png|centre|vignette|upright=2.5|Circuit incrémenteur optimisé, faisant l'incrémentation par paquet de deux bits]]
Reste à concevoir l'incrémenteur 2 bits.
===Les optimisations au niveau des transistors et l'exemple de l'incrémenteur du 8085 d'Intel===
Une première solution part de deux demi-additionneurs et effectue quelques simplifications au niveau des transistors et des portes logiques. Par exemple, l'Intel 8085 a utilisé un circuit optimisé, fabriqué avec des portes NAND et NOR. La raison est qu'avec les technologies de transistors CMOS actuelles, les portes NAND et NOR utilisent moins de transistors que les portes ET et OU. Et cette possibilité a été utilisée pour effectuer des simplifications assez mineures, mais toujours bonnes à prendre. Mais nous ne pouvons pas en parler en détail ici, car nous n'avons pas encore parlé en détail des transistors. Cependant, un exemple bien précis nous est accesible : celui de l’incrémenteur du processeur 8085 d'Intel.
Le processeur 8085 d'Intel est un processeur 8 bits très ancien. Il contenait un incrémenteur 16 bits, qui était utilisé pour calculer des adresses mémoire. Et cet incrémenteur était un incrémenteur à propagation de retenue optimisé. Il utilisait lui aussi des incrémenteurs 2 bits un peu modifiés.
L'idée était que les retenues sortant des colonnes paires étaient inversées, les colonnes impaires faisaient les calculs à partir de cette retenue inversée. Les demi-additionneurs des colonnes impaires n'étaient donc pas les mêmes que ceux des colonnes paires. Leurs tables de vérité sont différentes, leurs circuits aussi, et aucun des deux ne ressemble à un demi-additionneur normal. En réalité, ils sont fabriqués à partir des implémentations vues plus haut, qui utilisent uniquement des portes NAND ou uniquement des portes NOR.
Pour comprendre l'intérêt de faire ainsi, nous devons préciser une chose importante : avec les technologies CMOS utilisées pour les processeurs depuis les années 70, les portes logiques les plus simples sont les portes NON, NOR et NAND. Les portes ET/OU sont fabriquées en combinant des portes NOR/NAND avec une porte NON, ce qui prend plus de transistors.
Un demi-additionneur est donc fabriqué comme illustré ci-dessous, en logique CMOS. Seules les portes pour le calcul de la retenue sont indiquées, la porte XOR pour le bit de somme est fabriquée à partir de plusieurs portes logiques.
[[File:Demi-additionneur en CMOS.png|centre|vignette|upright=2|Demi-additionneur en CMOS]]
Si on enchaine deux demi-additionneurs, cela donne ceci :
[[File:Brique de base de l'incrémenteur du 8085.png|centre|vignette|upright=2|Brique de base de l'incrémenteur du 8085]]
La retenue doit donc traverser quatre portes logiques, dont deux portes NON. En inversant la retenue, les portes en jaune dans le schéma précédent se simplifient. Il suffit d'utiliser la loi de de Morgan pour trouver un circuit équivalent aux portes jaunes, qui n'est autre qu'une porte NOR couplée à une porte NON. Un autre moyen d'obtenir le même résultat est de prendre un demi-additionneur fabriqué avec uniquement des portes NOR, et de retirer la porte NON adéquate. Il faut ensuite corriger l'entrée de retenue de la porte XOR, pour qu'elle fasse l'addition avec la retenue correctement. Le résultat est illustré ci-dessous.
[[File:Incrémenteur 2 bits de l'incrémenteur du 8085.png|centre|vignette|upright=2|Incrémenteur 2 bits de l'incrémenteur du 8085]]
Le circuit utilise le même nombre de portes logiques, il n'économise pas de transistors. Par contre, la retenue n'a plus qu'à traverser deux portes logiques, au lieu de quatre. La propagation de la retenue est donc plus rapide, l'incrémentation est donc plus rapide.
===Les incrémenteurs 2 bits ''carry skip''===
Une solution alternative fabrique des incrémenteurs 2 bits qui calculent séparément les deux bits du résultat et la retenue sortante. La retenue sortante est calculée à partir des bits de l’opérande, sans propager des retenues. Ainsi, la retenue sortante est connue en avance, sans passer par deux demi-additionneurs. Pour créer un tel incrémenteur, le mieux est de partir de sa table de vérité.
{|class="wikitable"
|-
! Opérande !! Retenue entrante !! !! Retenue sortante !! Résultat
|-
| 00 || 0 || || 0 || 00
|-
| 01 || 0 || || 0 || 01
|-
| 10 || 0 || || 0 || 10
|-
| 11 || 0 || || 0 || 11
|-
| colspan="5" |
|-
| 00 || 1 || || 0 || 01
|-
| 01 || 1 || || 0 || 10
|-
| 10 || 1 || || 0 || 11
|-
| 11 || 1 || || 1 || 00
|}
Elle nous dit que la retenue sortante ne vaut 1 que dans un seul cas : les deux bits d'opérande valent 1, la retenue entrante vaut 1. Elle se calcule alors avec une porte ET à trois entrées. Pour les deux bits du résultat, les équations logiques ne donnent pas un résultat satisfaisant. La solution la plus simple est d'utiliser deux demi-additionneurs et de retirer les portes logiques superflues. Le résultat est alors un '''incrémenteur 2 bits ''carry skip''''', où la retenue sortante est calculée séparément du reste.
[[File:Incrémenteur carry skip 2 bits.png|centre|vignette|upright=2.5|Incrémenteur ''carry skip'' 2 bits]]
==Les incrémenteurs à anticipation de retenue==
L''''anticipation de retenue''' calcule la retenue d'une colonne sans attendre les retenues des colonnes précédentes. Dans le cas idéal, toutes les retenues sont calculées en parallèle, en même temps, et sont ensuite envoyées aux demi-additionneurs. Il s'agit d'une optimisation qui est utilisée pour l'incrémentation, l'addition et d'autres opérations similaires. Pour l'incrémentation, déterminer la retenue ne demande pas de calculs complexes, contrairement à l'addition.
[[File:Incrémenteur à anticipation de retenues, 8 bits.png|centre|vignette|upright=2.5|Incrémenteur à anticipation de retenues, 8 bits.]]
Pour le comprendre, on peut regarder comment la retenue sortant d'un demi-additionneur est formée. Elle est calculée en faisant un ET logique entre la retenue entrante et le bit d'opérande. Une retenue est donc un ET logique entre toutes les retenues précédentes. Un incrémenteur à anticipation de retenue utilise donc une porte ET à plusieurs entrées pour calculer une retenue.
[[File:Anticipation de retenue pour un bit du résultat, incrémenteur.png|centre|vignette|upright=2.5|Anticipation de retenue pour un bit du résultat, incrémenteur]]
En théorie, on peut utiliser une porte ET à plusieurs entrées pour chaque bit de l'opérande. Cependant, cela entrainera un cout en transistors très important. Pour éviter de gaspiller trop de portes logiques, une solution est de mélanger anticipation de retenues et propagation de retenue. Par exemple, pour un incrémenteur 32 bits, on peut découper l'opérande en 4 octets : on anticipe les retenues pour chaque octet, mais l'incrémentation de chaque octet se fait avec propagation de retenue et/ou ''carry skip''.
[[File:Incrémenteur à anticipation de retenues.png|centre|vignette|upright=3|Incrémenteur hybride utilisant partiellement l'anticipation de retenues.]]
Quelques processeurs utilisaient l'anticipation de retenues. Par exemple, le processeur Z-80 de Zilog utilisait un incrémenteur pour des nombres de 16 bits, ce qui demandait des performances assez élevées. Et cet incrémenteur utilisait à la fois anticipation de retenues et ''carry skip''. Il était découpé en quatre blocs avant anticipation de retenues entre eux : un bloc regroupant les 7 bits de poids faible, suivi par un bloc de 5 bits, lui-même suivi par un bloc de 3 bits, terminé par un dernier bit isolé. A l'intérieur de ces blocs, les bits sont regroupés en paires utilisant le ''carry skip''. Pour ceux qui veulent en savoir plus sur cet incrémenteur, voici un lien sur le sujet :
* [https://www.righto.com/2013/11/the-z-80s-16-bit-incrementdecrement.html The Z-80's 16-bit increment/decrement circuit reverse engineered].
==Les décrémenteurs==
Les circuits décrémenteurs font l'inverse d'un incrémenteur : au lieu d'ajouter 1 à un opérande, ils lui soustraient 1. La différence n'est pas énorme, ce qui fait que les circuits incrémenteurs et décrémenteurs se ressemblent beaucoup. Là où les incrémenteurs sont fabriqués à partir de demi-additionneurs, les décrémenteurs sont bâtis avec des demi-soustracteurs.
La soustraction se fait en binaire comme en décimal. On soustrait deux chiffres, puis on doit propager une éventuelle retenue sur la colonne suivante. La retenue apparait quand le chiffre soustrait est plus grand que l'autre chiffre. Elle est propagée sur la colonne suivante, où elle doit être soustraite du résultat. Pour le dire autrement, après avoir soustrait un chiffre, on doit de plus soustraire la retenue de la colonne précédente.
La décrémentation effectue le calcul suivant :
<math>a_7</math> <math>a_6</math> <math>a_5</math> <math>a_4</math> <math>a_3</math> <math>a_2</math> <math>a_1</math> <math>a_0</math>
- 0 0 0 0 0 0 0 1
La colonne la plus à droite soustrait un 1. Par contre, les colonnes suivantes soustraient un zéro. Soustraire un zéro ne change rien. Mais il ne faut pas oublier de soustraire une éventuellement retenue, propagée depuis la colonne précédente. En clair, un décrémenteur peut se construire si on sait soustraire deux bits. On soustrait un 1 sur la colonne la plus à droite, on soustrait une retenue sur les autres colonnes. Et pour soustraire deux bits, il faut utiliser un demi-soustracteur.
===Le demi-soustracteur===
Un '''demi-soustracteur''' soustrait deux bits. Pour comprendre comment soustraire deux bits, traitons les quatre cas possibles un par un.
Premièrement, soustraire zéro à un bit ne changera rien :
* 0 - 0 = 0 ;
* 1 - 0 = 1.
Si on soustrait 1 à un bit qui vaut 1, on obtient zéro.
* 1 - 1 = 0.
Maintenant, que se passe-t-il si on soustrait 1 à 0 ? Voici le résultat :
* 0 - 1 = 1 et une retenue propagée sur la colonne suivante, où elle est soustraite.
La table de soustraction est donc la suivante :
* 0 - 0 = 0 ;
* 0 - 1 = 1 et une retenue ;
* 1 - 0 = 1 ;
* 1 - 1 = 0.
Si on construit la table de vérité, et qu'on construit le circuit associé, on tombe sur ce circuit. Détail important : le demi-soustracteur fait ici la soustraction X - Y. En anglais, X est appelé le ''Minuend'' et Y est appelé le ''subtrahend'', mais ces termes n'ont pas de traduction française.
[[File:Half Subtractor Vektor.svg|centre|vignette|Demi-soustracteur.]]
===Les demi-additionneurs et demi-soustracteurs===
Vous l'aurez sans doute remarqué, mais le demi-soustracteur ressemble beaucoup au demi-additionneur. La seule différence est une porte NON ajoutée au bon endroit dans le demi-soustracteur. Et attention : cette porte NON inverse le bit X, sachant que le demi-soustracteur fait la soustraction X - Y.
{|
|[[File:Half-adder.svg|vignette|Demi-additionneur]]
|[[File:Half Subtractor Vektor.svg|vignette|upright=1.3|Demi-soustracteur]]
|}
La conséquence est qu'il est possible de fusionner un demi-additionneur avec un demi-soustracteur. L'idée est de remplacer la porte NON du demi-soustracteur par un inverseur commandable. Si on envoie un zéro sur l'entrée de commande, l'inverseur commandable se comportera comme une porte OUI. Le circuit sera alors identique à un demi-additionneur. Si on envoie un 1 sur l'entrée de commande, l'inverseur commandable se comportera comme une porte NON. le circuit sera alors un demi-soustracteur.
===L'incrémenteur/décrémenteurs à propagation de retenue===
[[File:AddSub Network 3 Bit.svg|vignette|Décrémenteur à propagation de retenue.]]
un décrémenteur à propagation de retenue se construit comme un incrémenteur, sauf qu'on remplace les demi-additionneurs par des demi-soustracteurs. En clair, on enchaine plusieurs demi-soustracteurs les uns à la suite des autres. Le circuit est équivalent à un incrémenteur à propagation de retenue, auquel on aurait ajouté un circuit inverseur. Le circuit inverseur en question est juste composé d'une couche de portes NON, qui inverse l'opérande à décrémenter.
Il est possible d'utiliser l'anticipation de retenue, mais les circuits sont cependant différents. Idem avec le ''carry skip'', et toute autre optimisation possible. La raison est que les retenues sont calculées différemment, ce qui fait que l’anticipation de retenue doit être adaptée.
Il est possible de fusionner un décrémenteur et un incrémenteur assez simplement. La seule différence étant une couche de portes NON qui inverse l'opérande, on peut remplacer ces portes NON par des inverseurs commandables. Les inverseurs commandables sont configurables pour servir soit de portes NON (mode décrémenteur), soit de portes OUI (mode incrémenteur).
<noinclude>
{{NavChapitre | book=Fonctionnement d'un ordinateur
| prev=Les circuits de sélection
| prevText=Les circuits de sélection
| next=Les bascules : des mémoires de 1 bit
| nextText=Les bascules : des mémoires de 1 bit
}}
</noinclude>
ipivbb79dpbtx6dku52jc68wrn7dyr2
768774
768773
2026-06-27T13:01:47Z
Mewtow
31375
/* L'incrémenteur/décrémenteurs à propagation de retenue */
768774
wikitext
text/x-wiki
Le circuit '''incrémenteur''' incrémente un nombre entier. Plus précisément, il prend en entrée un nombre entier, et fournit en entier le nombre suivant, il lui ajoute 1. Une telle opération peut paraitre triviale et peu intéressante. Cependant, elle est très utilisée et est absolument cruciale pour créer des compteurs, des circuits capables de compter ou décompter, qui auront droit à un chapitre dédié. De plus, au-delà de leur utilisation dans les compteurs, les incrémenteurs étaient très utilisés sur les premiers processeurs 8 bits, comme le Z-80, le 6502, les premiers processeurs x86 comme le 8008, le 8086, le 8085, et bien d'autres.
Il existe aussi des ''circuits décrémenteurs'', qui décrémentent un opérande, ainsi que des ''circuits incrémenteur-décrémenteur'', qui peuvent incrémenter ou décrémenter, selon comment on les configure. Je ne vais pas détailler ces circuits plus que ça, car de tels circuits sont assez rares, comparé à un circuit incrémenteur simple.
==Le demi-additionneur==
Le circuit incrémenteur effectue l'opération suivante :
<math>a_7</math> <math>a_6</math> <math>a_5</math> <math>a_4</math> <math>a_3</math> <math>a_2</math> <math>a_1</math> <math>a_0</math>
+ 0 0 0 0 0 0 0 1
Un incrémenteur basique doit donc faire une addition pour chaque colonne, et précisément une addition de deux bits. Il se trouve que la table d'addition est très simple en binaire. Jugez plutôt :
* 0 + 0 = 0, retenue = 0 ;
* 0 + 1 = 1, retenue = 0 ;
* 1 + 0 = 1, retenue = 0 ;
* 1 + 1 = 0, retenue = 1.
Un circuit capable d'additionner deux bits est appelé un '''demi-additionneur'''. Il dispose d'une sortie S pour la somme, et C pour la retenue (''carry'' an anglais).
===Le demi-additionneur classique===
Un demi-additionneur est très simple à construire avec les techniques vues dans les premiers chapitres. Voici sa table de vérité :
{|class="wikitable"
|-
! Retenue entrante !! Opérande 1 !! !! Retenue sortante !! Bit de somme
|-
||0||0|| ||0||0
|-
||0||1|| ||0||1
|-
||1||0|| ||0||1
|-
||1||1|| ||1||0
|}
On voit immédiatement que la colonne des retenues donne une porte ET, alors que celle du bit de somme est calculé par un XOR.
{| class="flexible"
|[[File:1-bit half-adder.svg|class=transparent|centre|Demi-addtionneur.]]
|[[File:Half-adder.svg|centre|class=transparent|Circuit d'un demi-addtionneur.]]
|}
Il existe beaucoup d'autres implémentations alternatives, qui utilisent moins de portes logiques, calculent la retenue plus rapidement, consomment moins d'énergie, et autres. Et il est intéressant d'étudier quelques alternatives, surtout qu'elles serviront plus bas, quand on étudiera le circuit incrémenteur du 8085.
===Le demi-additionneur basé sur une modification de la retenue===
Une source d'amélioration est liée à la porte XOR. En pratique, une porte XOR est composée en combinant plusieurs portes logiques ET/OU/NOR/NAND ensemble. Et il se trouve qu'il y a des redondances entre celles-ci et les portes utilisées pour calculer la retenue. Par exemple, rappelons qu'une porte XOR peut être construite avec une porte ET et deux portes NOR, comme illustré ci-dessous. Et il se trouve que la porte ET interne calcule la retenue sortante. En supprimant cette redondance, on économise quelques portes logiques. De plus, on se retrouve avec un demi-additionneur qui calcule le bit de somme à partir de la retenue sortante.
[[File:Demi-additionneur avec redondances éliminées.png|centre|vignette|upright=2|Demi-additionneur avec redondances éliminées]]
Le circuit précédent a une interprétation logique. Si vous regardez la table de vérité, vous remarquerez que la somme de deux bits est égale à l'inverse de la retenue sortante, sauf dans le cas où les deux bits additionnés valent zéro. Et le circuit précédent est basé là-dessus.
{|class="wikitable"
|-
! Retenue entrante !! Opérande 1 !! !! Retenue sortante !! Bit de somme
|- class="f_vert"
||0||0|| ||0||0
|- class="f_rouge"
||0||1|| ||0||1
|- class="f_rouge"
||1||0|| ||0||1
|- class="f_rouge"
||1||1|| ||1||0
|}
L'idée est de calculer l'inverse du bit de somme, avant de l'inverser avec une porte NON. L'inverse du bit de somme vaut 1, soit quand la retenue est à 1, soit quand les deux bits additionnés sont à 0. La porte ET calcule la retenue sortante, la première porte NOR détecte sur les deux bits d'entrée valent zéro. Une porte OU combine les deux résultat pour obtenir l'inverse du bit d'entrée, puis une porte NOn inverse le tout pour obtenir le bit de somme adéquat. Les deux portes sont fusionnées : c'est la seconde porte NOR.
[[File:Full adder HA + MUX.png|centre|vignette|upright=2.5|Demi-additionneur basé sur une inversion de la retenue sortante.]]
===Les implémentations alternatives===
Une implémentation alternative, qui n'utilise que des portes NOR, est la suivante :
[[File:Demi-additionneur fait avec des portes NOR et NAND.png|centre|vignette|upright=2|Demi-additionneur fait avec des portes NOR et NAND.]]
Une implémentation alternative utilise des portes NAND. Pour rappel, il est possible de créer une porte XOR avec 4 portes NAND. La première d'entre elle fait un NAND entre les deux bits d'entrée, ce qui fait qu'elle calcule l'inverse de la retenue sortante. Le tout est illustré ci-dessous. En théorie, on devrait utiliser une porte NON pour récupérer la retenue correcte. Mais nous verrons plus bas que ce n'est pas une obligation. Nous verrons plus bas un exemple où c'est l'inverse de la retenue qui est utilisée dans les calculs, pour rendre les calculs plus rapides.
[[File:Demi-additionneur fait avec des portes NAND.png|centre|vignette|upright=2|Demi-additionneur fait avec des portes NAND]]
==L'incrémenteur à propagation de retenue==
Maintenant que l'on sait comment additionner deux bits, reprenons l'opération d'incrémentation :
<math>a_7</math> <math>a_6</math> <math>a_5</math> <math>a_4</math> <math>a_3</math> <math>a_2</math> <math>a_1</math> <math>a_0</math>
+ 0 0 0 0 0 0 0 1
------------------------------
Sur la colonne la plus à droite, il doit ajouter un au bit de poids faible. Pour les colonnes suivantes, il faut additionner le bit de l'opérande avec la retenue de la colonne précédente. En clair, on n'additionne que deux bits à chaque colonne : un 1 sur celle tout à droite, la retenue de la colonne précédente pour les autres. Et cela nous donne une idée de comment faire pour créer le circuit incrémenteur.
Il suffit d'utiliser un demi-additionneur par colonne, et de les enchainer les uns à la suite des autres. Chaque demi-additionneur additionne le bit de l'opérande avec la retenue fournie par le demi-additionneur précédent. Le seul qui fait exception est celui pour la colonne de poids faible. Pour celui-là, il doit ajouter 1 au bit de poids faible. Le résultat est appelé un '''incrémenteur à propagation de retenue'''. Il est constitué de demi-additionneurs enchaînés les uns à la suite des autres, du bit de poids faible vers le bit de poids fort.
[[File:Circuit incrémenteur.png|centre|vignette|upright=3|Circuit incrémenteur.]]
Maintenant, regardons le demi-additionneur le plus à droite, celui pour le bit de poids faible. Son entrée de retenue entrante est mise à 1 pour faire l'incrémentation. Quelques incrémenteurs permettent de configurer cette entrée de retenue à 0 ou à 1, ce qui effectue : soit une opération identité (l'opérande est recopié sur la sortie), soit une incrémentation. Un tel circuit est nommé un '''incrémenteur commandable'''. Nous aurons à utiliser une fois ou deux de tels incrémenteurs commandables dans la suite du cours.
L'incrémenteur à propagation de retenue est le plus simple et le plus économe en portes logiques. Mais de tels incrémenteurs sont rarement utilisés. À la place, on leur préfère des incrémenteurs plus rapides, mais qui utilisent plus de portes logiques. De tels incrémenteurs accélèrent le calcul des retenues. En effet, la rapidité d'une incrémentation est limitée par la propagation de la retenue : les retenues commencent à être calculées au bit de poids fort et on doit les calculer une par une, jusqu’à atteindre le bit de poids fort. Et cette "propagation des retenues" prend du temps, d'autant plus de temps que l'opérande est longue. Il y a deux optimisations principales, appelées le ''carry skip'' et l'anticipation de retenue, que nous allons décrire ci-dessous.
==Les incrémenteurs ''carry skip''==
L'optimisation '''''carry skip''''' effectue l'incrémentation, non pas bit par bit, mais par paquets de deux bits. Le résultat est que l'incrémentation est deux fois plus rapide, ou presque. Le circuit incrémenteur est donc composé en enchainant non pas des demi-additionneurs, mais des '''incrémenteurs 2 bits''' qui incrémentent un opérande de deux bits.
[[File:Circuit incrémenteur optimisé, faisant l'incrémentation par paquet de deux bits.png|centre|vignette|upright=2.5|Circuit incrémenteur optimisé, faisant l'incrémentation par paquet de deux bits]]
Reste à concevoir l'incrémenteur 2 bits.
===Les optimisations au niveau des transistors et l'exemple de l'incrémenteur du 8085 d'Intel===
Une première solution part de deux demi-additionneurs et effectue quelques simplifications au niveau des transistors et des portes logiques. Par exemple, l'Intel 8085 a utilisé un circuit optimisé, fabriqué avec des portes NAND et NOR. La raison est qu'avec les technologies de transistors CMOS actuelles, les portes NAND et NOR utilisent moins de transistors que les portes ET et OU. Et cette possibilité a été utilisée pour effectuer des simplifications assez mineures, mais toujours bonnes à prendre. Mais nous ne pouvons pas en parler en détail ici, car nous n'avons pas encore parlé en détail des transistors. Cependant, un exemple bien précis nous est accesible : celui de l’incrémenteur du processeur 8085 d'Intel.
Le processeur 8085 d'Intel est un processeur 8 bits très ancien. Il contenait un incrémenteur 16 bits, qui était utilisé pour calculer des adresses mémoire. Et cet incrémenteur était un incrémenteur à propagation de retenue optimisé. Il utilisait lui aussi des incrémenteurs 2 bits un peu modifiés.
L'idée était que les retenues sortant des colonnes paires étaient inversées, les colonnes impaires faisaient les calculs à partir de cette retenue inversée. Les demi-additionneurs des colonnes impaires n'étaient donc pas les mêmes que ceux des colonnes paires. Leurs tables de vérité sont différentes, leurs circuits aussi, et aucun des deux ne ressemble à un demi-additionneur normal. En réalité, ils sont fabriqués à partir des implémentations vues plus haut, qui utilisent uniquement des portes NAND ou uniquement des portes NOR.
Pour comprendre l'intérêt de faire ainsi, nous devons préciser une chose importante : avec les technologies CMOS utilisées pour les processeurs depuis les années 70, les portes logiques les plus simples sont les portes NON, NOR et NAND. Les portes ET/OU sont fabriquées en combinant des portes NOR/NAND avec une porte NON, ce qui prend plus de transistors.
Un demi-additionneur est donc fabriqué comme illustré ci-dessous, en logique CMOS. Seules les portes pour le calcul de la retenue sont indiquées, la porte XOR pour le bit de somme est fabriquée à partir de plusieurs portes logiques.
[[File:Demi-additionneur en CMOS.png|centre|vignette|upright=2|Demi-additionneur en CMOS]]
Si on enchaine deux demi-additionneurs, cela donne ceci :
[[File:Brique de base de l'incrémenteur du 8085.png|centre|vignette|upright=2|Brique de base de l'incrémenteur du 8085]]
La retenue doit donc traverser quatre portes logiques, dont deux portes NON. En inversant la retenue, les portes en jaune dans le schéma précédent se simplifient. Il suffit d'utiliser la loi de de Morgan pour trouver un circuit équivalent aux portes jaunes, qui n'est autre qu'une porte NOR couplée à une porte NON. Un autre moyen d'obtenir le même résultat est de prendre un demi-additionneur fabriqué avec uniquement des portes NOR, et de retirer la porte NON adéquate. Il faut ensuite corriger l'entrée de retenue de la porte XOR, pour qu'elle fasse l'addition avec la retenue correctement. Le résultat est illustré ci-dessous.
[[File:Incrémenteur 2 bits de l'incrémenteur du 8085.png|centre|vignette|upright=2|Incrémenteur 2 bits de l'incrémenteur du 8085]]
Le circuit utilise le même nombre de portes logiques, il n'économise pas de transistors. Par contre, la retenue n'a plus qu'à traverser deux portes logiques, au lieu de quatre. La propagation de la retenue est donc plus rapide, l'incrémentation est donc plus rapide.
===Les incrémenteurs 2 bits ''carry skip''===
Une solution alternative fabrique des incrémenteurs 2 bits qui calculent séparément les deux bits du résultat et la retenue sortante. La retenue sortante est calculée à partir des bits de l’opérande, sans propager des retenues. Ainsi, la retenue sortante est connue en avance, sans passer par deux demi-additionneurs. Pour créer un tel incrémenteur, le mieux est de partir de sa table de vérité.
{|class="wikitable"
|-
! Opérande !! Retenue entrante !! !! Retenue sortante !! Résultat
|-
| 00 || 0 || || 0 || 00
|-
| 01 || 0 || || 0 || 01
|-
| 10 || 0 || || 0 || 10
|-
| 11 || 0 || || 0 || 11
|-
| colspan="5" |
|-
| 00 || 1 || || 0 || 01
|-
| 01 || 1 || || 0 || 10
|-
| 10 || 1 || || 0 || 11
|-
| 11 || 1 || || 1 || 00
|}
Elle nous dit que la retenue sortante ne vaut 1 que dans un seul cas : les deux bits d'opérande valent 1, la retenue entrante vaut 1. Elle se calcule alors avec une porte ET à trois entrées. Pour les deux bits du résultat, les équations logiques ne donnent pas un résultat satisfaisant. La solution la plus simple est d'utiliser deux demi-additionneurs et de retirer les portes logiques superflues. Le résultat est alors un '''incrémenteur 2 bits ''carry skip''''', où la retenue sortante est calculée séparément du reste.
[[File:Incrémenteur carry skip 2 bits.png|centre|vignette|upright=2.5|Incrémenteur ''carry skip'' 2 bits]]
==Les incrémenteurs à anticipation de retenue==
L''''anticipation de retenue''' calcule la retenue d'une colonne sans attendre les retenues des colonnes précédentes. Dans le cas idéal, toutes les retenues sont calculées en parallèle, en même temps, et sont ensuite envoyées aux demi-additionneurs. Il s'agit d'une optimisation qui est utilisée pour l'incrémentation, l'addition et d'autres opérations similaires. Pour l'incrémentation, déterminer la retenue ne demande pas de calculs complexes, contrairement à l'addition.
[[File:Incrémenteur à anticipation de retenues, 8 bits.png|centre|vignette|upright=2.5|Incrémenteur à anticipation de retenues, 8 bits.]]
Pour le comprendre, on peut regarder comment la retenue sortant d'un demi-additionneur est formée. Elle est calculée en faisant un ET logique entre la retenue entrante et le bit d'opérande. Une retenue est donc un ET logique entre toutes les retenues précédentes. Un incrémenteur à anticipation de retenue utilise donc une porte ET à plusieurs entrées pour calculer une retenue.
[[File:Anticipation de retenue pour un bit du résultat, incrémenteur.png|centre|vignette|upright=2.5|Anticipation de retenue pour un bit du résultat, incrémenteur]]
En théorie, on peut utiliser une porte ET à plusieurs entrées pour chaque bit de l'opérande. Cependant, cela entrainera un cout en transistors très important. Pour éviter de gaspiller trop de portes logiques, une solution est de mélanger anticipation de retenues et propagation de retenue. Par exemple, pour un incrémenteur 32 bits, on peut découper l'opérande en 4 octets : on anticipe les retenues pour chaque octet, mais l'incrémentation de chaque octet se fait avec propagation de retenue et/ou ''carry skip''.
[[File:Incrémenteur à anticipation de retenues.png|centre|vignette|upright=3|Incrémenteur hybride utilisant partiellement l'anticipation de retenues.]]
Quelques processeurs utilisaient l'anticipation de retenues. Par exemple, le processeur Z-80 de Zilog utilisait un incrémenteur pour des nombres de 16 bits, ce qui demandait des performances assez élevées. Et cet incrémenteur utilisait à la fois anticipation de retenues et ''carry skip''. Il était découpé en quatre blocs avant anticipation de retenues entre eux : un bloc regroupant les 7 bits de poids faible, suivi par un bloc de 5 bits, lui-même suivi par un bloc de 3 bits, terminé par un dernier bit isolé. A l'intérieur de ces blocs, les bits sont regroupés en paires utilisant le ''carry skip''. Pour ceux qui veulent en savoir plus sur cet incrémenteur, voici un lien sur le sujet :
* [https://www.righto.com/2013/11/the-z-80s-16-bit-incrementdecrement.html The Z-80's 16-bit increment/decrement circuit reverse engineered].
==Les décrémenteurs==
Les circuits décrémenteurs font l'inverse d'un incrémenteur : au lieu d'ajouter 1 à un opérande, ils lui soustraient 1. La différence n'est pas énorme, ce qui fait que les circuits incrémenteurs et décrémenteurs se ressemblent beaucoup. Là où les incrémenteurs sont fabriqués à partir de demi-additionneurs, les décrémenteurs sont bâtis avec des demi-soustracteurs.
La soustraction se fait en binaire comme en décimal. On soustrait deux chiffres, puis on doit propager une éventuelle retenue sur la colonne suivante. La retenue apparait quand le chiffre soustrait est plus grand que l'autre chiffre. Elle est propagée sur la colonne suivante, où elle doit être soustraite du résultat. Pour le dire autrement, après avoir soustrait un chiffre, on doit de plus soustraire la retenue de la colonne précédente.
La décrémentation effectue le calcul suivant :
<math>a_7</math> <math>a_6</math> <math>a_5</math> <math>a_4</math> <math>a_3</math> <math>a_2</math> <math>a_1</math> <math>a_0</math>
- 0 0 0 0 0 0 0 1
La colonne la plus à droite soustrait un 1. Par contre, les colonnes suivantes soustraient un zéro. Soustraire un zéro ne change rien. Mais il ne faut pas oublier de soustraire une éventuellement retenue, propagée depuis la colonne précédente. En clair, un décrémenteur peut se construire si on sait soustraire deux bits. On soustrait un 1 sur la colonne la plus à droite, on soustrait une retenue sur les autres colonnes. Et pour soustraire deux bits, il faut utiliser un demi-soustracteur.
===Le demi-soustracteur===
Un '''demi-soustracteur''' soustrait deux bits. Pour comprendre comment soustraire deux bits, traitons les quatre cas possibles un par un.
Premièrement, soustraire zéro à un bit ne changera rien :
* 0 - 0 = 0 ;
* 1 - 0 = 1.
Si on soustrait 1 à un bit qui vaut 1, on obtient zéro.
* 1 - 1 = 0.
Maintenant, que se passe-t-il si on soustrait 1 à 0 ? Voici le résultat :
* 0 - 1 = 1 et une retenue propagée sur la colonne suivante, où elle est soustraite.
La table de soustraction est donc la suivante :
* 0 - 0 = 0 ;
* 0 - 1 = 1 et une retenue ;
* 1 - 0 = 1 ;
* 1 - 1 = 0.
Si on construit la table de vérité, et qu'on construit le circuit associé, on tombe sur ce circuit. Détail important : le demi-soustracteur fait ici la soustraction X - Y. En anglais, X est appelé le ''Minuend'' et Y est appelé le ''subtrahend'', mais ces termes n'ont pas de traduction française.
[[File:Half Subtractor Vektor.svg|centre|vignette|Demi-soustracteur.]]
===Les demi-additionneurs et demi-soustracteurs===
Vous l'aurez sans doute remarqué, mais le demi-soustracteur ressemble beaucoup au demi-additionneur. La seule différence est une porte NON ajoutée au bon endroit dans le demi-soustracteur. Et attention : cette porte NON inverse le bit X, sachant que le demi-soustracteur fait la soustraction X - Y.
{|
|[[File:Half-adder.svg|vignette|Demi-additionneur]]
|[[File:Half Subtractor Vektor.svg|vignette|upright=1.3|Demi-soustracteur]]
|}
La conséquence est qu'il est possible de fusionner un demi-additionneur avec un demi-soustracteur. L'idée est de remplacer la porte NON du demi-soustracteur par un inverseur commandable. Si on envoie un zéro sur l'entrée de commande, l'inverseur commandable se comportera comme une porte OUI. Le circuit sera alors identique à un demi-additionneur. Si on envoie un 1 sur l'entrée de commande, l'inverseur commandable se comportera comme une porte NON. le circuit sera alors un demi-soustracteur.
===L'incrémenteur/décrémenteurs à propagation de retenue===
[[File:AddSub Network 3 Bit.svg|vignette|Décrémenteur à propagation de retenue.]]
un décrémenteur à propagation de retenue se construit comme un incrémenteur, sauf qu'on remplace les demi-additionneurs par des demi-soustracteurs. En clair, on enchaine plusieurs demi-soustracteurs les uns à la suite des autres. Le circuit est équivalent à un incrémenteur à propagation de retenue, auquel on aurait ajouté un circuit inverseur. Le circuit inverseur en question est juste composé d'une couche de portes NON, qui inverse l'opérande à décrémenter.
Il est possible d'utiliser l'anticipation de retenue, mais les circuits sont cependant différents. Idem avec le ''carry skip'', et toute autre optimisation possible. La raison est que les retenues sont calculées différemment, ce qui fait que l’anticipation de retenue doit être adaptée.
Il est possible de fusionner un décrémenteur et un incrémenteur assez simplement. La seule différence étant une couche de portes NON qui inverse l'opérande, on peut remplacer ces portes NON par des inverseurs commandables. Les inverseurs commandables sont configurables pour servir soit de portes NON (mode décrémenteur), soit de portes OUI (mode incrémenteur). Il suffit pour cela d'utiliser plusieurs circuits précédents, et de commander tous les inverseurs commandables avec le même bit d'entrée. Ce bit sélectionne l'opération demandée : incrémentation ou décrémentation.
<noinclude>
{{NavChapitre | book=Fonctionnement d'un ordinateur
| prev=Les circuits de sélection
| prevText=Les circuits de sélection
| next=Les bascules : des mémoires de 1 bit
| nextText=Les bascules : des mémoires de 1 bit
}}
</noinclude>
sc5sr7qc2xuai1avp6wm2ger79kp1qd
768777
768774
2026-06-27T13:14:50Z
Mewtow
31375
/* L'incrémenteur/décrémenteurs à propagation de retenue */
768777
wikitext
text/x-wiki
Le circuit '''incrémenteur''' incrémente un nombre entier. Plus précisément, il prend en entrée un nombre entier, et fournit en entier le nombre suivant, il lui ajoute 1. Une telle opération peut paraitre triviale et peu intéressante. Cependant, elle est très utilisée et est absolument cruciale pour créer des compteurs, des circuits capables de compter ou décompter, qui auront droit à un chapitre dédié. De plus, au-delà de leur utilisation dans les compteurs, les incrémenteurs étaient très utilisés sur les premiers processeurs 8 bits, comme le Z-80, le 6502, les premiers processeurs x86 comme le 8008, le 8086, le 8085, et bien d'autres.
Il existe aussi des ''circuits décrémenteurs'', qui décrémentent un opérande, ainsi que des ''circuits incrémenteur-décrémenteur'', qui peuvent incrémenter ou décrémenter, selon comment on les configure. Je ne vais pas détailler ces circuits plus que ça, car de tels circuits sont assez rares, comparé à un circuit incrémenteur simple.
==Le demi-additionneur==
Le circuit incrémenteur effectue l'opération suivante :
<math>a_7</math> <math>a_6</math> <math>a_5</math> <math>a_4</math> <math>a_3</math> <math>a_2</math> <math>a_1</math> <math>a_0</math>
+ 0 0 0 0 0 0 0 1
Un incrémenteur basique doit donc faire une addition pour chaque colonne, et précisément une addition de deux bits. Il se trouve que la table d'addition est très simple en binaire. Jugez plutôt :
* 0 + 0 = 0, retenue = 0 ;
* 0 + 1 = 1, retenue = 0 ;
* 1 + 0 = 1, retenue = 0 ;
* 1 + 1 = 0, retenue = 1.
Un circuit capable d'additionner deux bits est appelé un '''demi-additionneur'''. Il dispose d'une sortie S pour la somme, et C pour la retenue (''carry'' an anglais).
===Le demi-additionneur classique===
Un demi-additionneur est très simple à construire avec les techniques vues dans les premiers chapitres. Voici sa table de vérité :
{|class="wikitable"
|-
! Retenue entrante !! Opérande 1 !! !! Retenue sortante !! Bit de somme
|-
||0||0|| ||0||0
|-
||0||1|| ||0||1
|-
||1||0|| ||0||1
|-
||1||1|| ||1||0
|}
On voit immédiatement que la colonne des retenues donne une porte ET, alors que celle du bit de somme est calculé par un XOR.
{| class="flexible"
|[[File:1-bit half-adder.svg|class=transparent|centre|Demi-addtionneur.]]
|[[File:Half-adder.svg|centre|class=transparent|Circuit d'un demi-addtionneur.]]
|}
Il existe beaucoup d'autres implémentations alternatives, qui utilisent moins de portes logiques, calculent la retenue plus rapidement, consomment moins d'énergie, et autres. Et il est intéressant d'étudier quelques alternatives, surtout qu'elles serviront plus bas, quand on étudiera le circuit incrémenteur du 8085.
===Le demi-additionneur basé sur une modification de la retenue===
Une source d'amélioration est liée à la porte XOR. En pratique, une porte XOR est composée en combinant plusieurs portes logiques ET/OU/NOR/NAND ensemble. Et il se trouve qu'il y a des redondances entre celles-ci et les portes utilisées pour calculer la retenue. Par exemple, rappelons qu'une porte XOR peut être construite avec une porte ET et deux portes NOR, comme illustré ci-dessous. Et il se trouve que la porte ET interne calcule la retenue sortante. En supprimant cette redondance, on économise quelques portes logiques. De plus, on se retrouve avec un demi-additionneur qui calcule le bit de somme à partir de la retenue sortante.
[[File:Demi-additionneur avec redondances éliminées.png|centre|vignette|upright=2|Demi-additionneur avec redondances éliminées]]
Le circuit précédent a une interprétation logique. Si vous regardez la table de vérité, vous remarquerez que la somme de deux bits est égale à l'inverse de la retenue sortante, sauf dans le cas où les deux bits additionnés valent zéro. Et le circuit précédent est basé là-dessus.
{|class="wikitable"
|-
! Retenue entrante !! Opérande 1 !! !! Retenue sortante !! Bit de somme
|- class="f_vert"
||0||0|| ||0||0
|- class="f_rouge"
||0||1|| ||0||1
|- class="f_rouge"
||1||0|| ||0||1
|- class="f_rouge"
||1||1|| ||1||0
|}
L'idée est de calculer l'inverse du bit de somme, avant de l'inverser avec une porte NON. L'inverse du bit de somme vaut 1, soit quand la retenue est à 1, soit quand les deux bits additionnés sont à 0. La porte ET calcule la retenue sortante, la première porte NOR détecte sur les deux bits d'entrée valent zéro. Une porte OU combine les deux résultat pour obtenir l'inverse du bit d'entrée, puis une porte NOn inverse le tout pour obtenir le bit de somme adéquat. Les deux portes sont fusionnées : c'est la seconde porte NOR.
[[File:Full adder HA + MUX.png|centre|vignette|upright=2.5|Demi-additionneur basé sur une inversion de la retenue sortante.]]
===Les implémentations alternatives===
Une implémentation alternative, qui n'utilise que des portes NOR, est la suivante :
[[File:Demi-additionneur fait avec des portes NOR et NAND.png|centre|vignette|upright=2|Demi-additionneur fait avec des portes NOR et NAND.]]
Une implémentation alternative utilise des portes NAND. Pour rappel, il est possible de créer une porte XOR avec 4 portes NAND. La première d'entre elle fait un NAND entre les deux bits d'entrée, ce qui fait qu'elle calcule l'inverse de la retenue sortante. Le tout est illustré ci-dessous. En théorie, on devrait utiliser une porte NON pour récupérer la retenue correcte. Mais nous verrons plus bas que ce n'est pas une obligation. Nous verrons plus bas un exemple où c'est l'inverse de la retenue qui est utilisée dans les calculs, pour rendre les calculs plus rapides.
[[File:Demi-additionneur fait avec des portes NAND.png|centre|vignette|upright=2|Demi-additionneur fait avec des portes NAND]]
==L'incrémenteur à propagation de retenue==
Maintenant que l'on sait comment additionner deux bits, reprenons l'opération d'incrémentation :
<math>a_7</math> <math>a_6</math> <math>a_5</math> <math>a_4</math> <math>a_3</math> <math>a_2</math> <math>a_1</math> <math>a_0</math>
+ 0 0 0 0 0 0 0 1
------------------------------
Sur la colonne la plus à droite, il doit ajouter un au bit de poids faible. Pour les colonnes suivantes, il faut additionner le bit de l'opérande avec la retenue de la colonne précédente. En clair, on n'additionne que deux bits à chaque colonne : un 1 sur celle tout à droite, la retenue de la colonne précédente pour les autres. Et cela nous donne une idée de comment faire pour créer le circuit incrémenteur.
Il suffit d'utiliser un demi-additionneur par colonne, et de les enchainer les uns à la suite des autres. Chaque demi-additionneur additionne le bit de l'opérande avec la retenue fournie par le demi-additionneur précédent. Le seul qui fait exception est celui pour la colonne de poids faible. Pour celui-là, il doit ajouter 1 au bit de poids faible. Le résultat est appelé un '''incrémenteur à propagation de retenue'''. Il est constitué de demi-additionneurs enchaînés les uns à la suite des autres, du bit de poids faible vers le bit de poids fort.
[[File:Circuit incrémenteur.png|centre|vignette|upright=3|Circuit incrémenteur.]]
Maintenant, regardons le demi-additionneur le plus à droite, celui pour le bit de poids faible. Son entrée de retenue entrante est mise à 1 pour faire l'incrémentation. Quelques incrémenteurs permettent de configurer cette entrée de retenue à 0 ou à 1, ce qui effectue : soit une opération identité (l'opérande est recopié sur la sortie), soit une incrémentation. Un tel circuit est nommé un '''incrémenteur commandable'''. Nous aurons à utiliser une fois ou deux de tels incrémenteurs commandables dans la suite du cours.
L'incrémenteur à propagation de retenue est le plus simple et le plus économe en portes logiques. Mais de tels incrémenteurs sont rarement utilisés. À la place, on leur préfère des incrémenteurs plus rapides, mais qui utilisent plus de portes logiques. De tels incrémenteurs accélèrent le calcul des retenues. En effet, la rapidité d'une incrémentation est limitée par la propagation de la retenue : les retenues commencent à être calculées au bit de poids fort et on doit les calculer une par une, jusqu’à atteindre le bit de poids fort. Et cette "propagation des retenues" prend du temps, d'autant plus de temps que l'opérande est longue. Il y a deux optimisations principales, appelées le ''carry skip'' et l'anticipation de retenue, que nous allons décrire ci-dessous.
==Les incrémenteurs ''carry skip''==
L'optimisation '''''carry skip''''' effectue l'incrémentation, non pas bit par bit, mais par paquets de deux bits. Le résultat est que l'incrémentation est deux fois plus rapide, ou presque. Le circuit incrémenteur est donc composé en enchainant non pas des demi-additionneurs, mais des '''incrémenteurs 2 bits''' qui incrémentent un opérande de deux bits.
[[File:Circuit incrémenteur optimisé, faisant l'incrémentation par paquet de deux bits.png|centre|vignette|upright=2.5|Circuit incrémenteur optimisé, faisant l'incrémentation par paquet de deux bits]]
Reste à concevoir l'incrémenteur 2 bits.
===Les optimisations au niveau des transistors et l'exemple de l'incrémenteur du 8085 d'Intel===
Une première solution part de deux demi-additionneurs et effectue quelques simplifications au niveau des transistors et des portes logiques. Par exemple, l'Intel 8085 a utilisé un circuit optimisé, fabriqué avec des portes NAND et NOR. La raison est qu'avec les technologies de transistors CMOS actuelles, les portes NAND et NOR utilisent moins de transistors que les portes ET et OU. Et cette possibilité a été utilisée pour effectuer des simplifications assez mineures, mais toujours bonnes à prendre. Mais nous ne pouvons pas en parler en détail ici, car nous n'avons pas encore parlé en détail des transistors. Cependant, un exemple bien précis nous est accesible : celui de l’incrémenteur du processeur 8085 d'Intel.
Le processeur 8085 d'Intel est un processeur 8 bits très ancien. Il contenait un incrémenteur 16 bits, qui était utilisé pour calculer des adresses mémoire. Et cet incrémenteur était un incrémenteur à propagation de retenue optimisé. Il utilisait lui aussi des incrémenteurs 2 bits un peu modifiés.
L'idée était que les retenues sortant des colonnes paires étaient inversées, les colonnes impaires faisaient les calculs à partir de cette retenue inversée. Les demi-additionneurs des colonnes impaires n'étaient donc pas les mêmes que ceux des colonnes paires. Leurs tables de vérité sont différentes, leurs circuits aussi, et aucun des deux ne ressemble à un demi-additionneur normal. En réalité, ils sont fabriqués à partir des implémentations vues plus haut, qui utilisent uniquement des portes NAND ou uniquement des portes NOR.
Pour comprendre l'intérêt de faire ainsi, nous devons préciser une chose importante : avec les technologies CMOS utilisées pour les processeurs depuis les années 70, les portes logiques les plus simples sont les portes NON, NOR et NAND. Les portes ET/OU sont fabriquées en combinant des portes NOR/NAND avec une porte NON, ce qui prend plus de transistors.
Un demi-additionneur est donc fabriqué comme illustré ci-dessous, en logique CMOS. Seules les portes pour le calcul de la retenue sont indiquées, la porte XOR pour le bit de somme est fabriquée à partir de plusieurs portes logiques.
[[File:Demi-additionneur en CMOS.png|centre|vignette|upright=2|Demi-additionneur en CMOS]]
Si on enchaine deux demi-additionneurs, cela donne ceci :
[[File:Brique de base de l'incrémenteur du 8085.png|centre|vignette|upright=2|Brique de base de l'incrémenteur du 8085]]
La retenue doit donc traverser quatre portes logiques, dont deux portes NON. En inversant la retenue, les portes en jaune dans le schéma précédent se simplifient. Il suffit d'utiliser la loi de de Morgan pour trouver un circuit équivalent aux portes jaunes, qui n'est autre qu'une porte NOR couplée à une porte NON. Un autre moyen d'obtenir le même résultat est de prendre un demi-additionneur fabriqué avec uniquement des portes NOR, et de retirer la porte NON adéquate. Il faut ensuite corriger l'entrée de retenue de la porte XOR, pour qu'elle fasse l'addition avec la retenue correctement. Le résultat est illustré ci-dessous.
[[File:Incrémenteur 2 bits de l'incrémenteur du 8085.png|centre|vignette|upright=2|Incrémenteur 2 bits de l'incrémenteur du 8085]]
Le circuit utilise le même nombre de portes logiques, il n'économise pas de transistors. Par contre, la retenue n'a plus qu'à traverser deux portes logiques, au lieu de quatre. La propagation de la retenue est donc plus rapide, l'incrémentation est donc plus rapide.
===Les incrémenteurs 2 bits ''carry skip''===
Une solution alternative fabrique des incrémenteurs 2 bits qui calculent séparément les deux bits du résultat et la retenue sortante. La retenue sortante est calculée à partir des bits de l’opérande, sans propager des retenues. Ainsi, la retenue sortante est connue en avance, sans passer par deux demi-additionneurs. Pour créer un tel incrémenteur, le mieux est de partir de sa table de vérité.
{|class="wikitable"
|-
! Opérande !! Retenue entrante !! !! Retenue sortante !! Résultat
|-
| 00 || 0 || || 0 || 00
|-
| 01 || 0 || || 0 || 01
|-
| 10 || 0 || || 0 || 10
|-
| 11 || 0 || || 0 || 11
|-
| colspan="5" |
|-
| 00 || 1 || || 0 || 01
|-
| 01 || 1 || || 0 || 10
|-
| 10 || 1 || || 0 || 11
|-
| 11 || 1 || || 1 || 00
|}
Elle nous dit que la retenue sortante ne vaut 1 que dans un seul cas : les deux bits d'opérande valent 1, la retenue entrante vaut 1. Elle se calcule alors avec une porte ET à trois entrées. Pour les deux bits du résultat, les équations logiques ne donnent pas un résultat satisfaisant. La solution la plus simple est d'utiliser deux demi-additionneurs et de retirer les portes logiques superflues. Le résultat est alors un '''incrémenteur 2 bits ''carry skip''''', où la retenue sortante est calculée séparément du reste.
[[File:Incrémenteur carry skip 2 bits.png|centre|vignette|upright=2.5|Incrémenteur ''carry skip'' 2 bits]]
==Les incrémenteurs à anticipation de retenue==
L''''anticipation de retenue''' calcule la retenue d'une colonne sans attendre les retenues des colonnes précédentes. Dans le cas idéal, toutes les retenues sont calculées en parallèle, en même temps, et sont ensuite envoyées aux demi-additionneurs. Il s'agit d'une optimisation qui est utilisée pour l'incrémentation, l'addition et d'autres opérations similaires. Pour l'incrémentation, déterminer la retenue ne demande pas de calculs complexes, contrairement à l'addition.
[[File:Incrémenteur à anticipation de retenues, 8 bits.png|centre|vignette|upright=2.5|Incrémenteur à anticipation de retenues, 8 bits.]]
Pour le comprendre, on peut regarder comment la retenue sortant d'un demi-additionneur est formée. Elle est calculée en faisant un ET logique entre la retenue entrante et le bit d'opérande. Une retenue est donc un ET logique entre toutes les retenues précédentes. Un incrémenteur à anticipation de retenue utilise donc une porte ET à plusieurs entrées pour calculer une retenue.
[[File:Anticipation de retenue pour un bit du résultat, incrémenteur.png|centre|vignette|upright=2.5|Anticipation de retenue pour un bit du résultat, incrémenteur]]
En théorie, on peut utiliser une porte ET à plusieurs entrées pour chaque bit de l'opérande. Cependant, cela entrainera un cout en transistors très important. Pour éviter de gaspiller trop de portes logiques, une solution est de mélanger anticipation de retenues et propagation de retenue. Par exemple, pour un incrémenteur 32 bits, on peut découper l'opérande en 4 octets : on anticipe les retenues pour chaque octet, mais l'incrémentation de chaque octet se fait avec propagation de retenue et/ou ''carry skip''.
[[File:Incrémenteur à anticipation de retenues.png|centre|vignette|upright=3|Incrémenteur hybride utilisant partiellement l'anticipation de retenues.]]
Quelques processeurs utilisaient l'anticipation de retenues. Par exemple, le processeur Z-80 de Zilog utilisait un incrémenteur pour des nombres de 16 bits, ce qui demandait des performances assez élevées. Et cet incrémenteur utilisait à la fois anticipation de retenues et ''carry skip''. Il était découpé en quatre blocs avant anticipation de retenues entre eux : un bloc regroupant les 7 bits de poids faible, suivi par un bloc de 5 bits, lui-même suivi par un bloc de 3 bits, terminé par un dernier bit isolé. A l'intérieur de ces blocs, les bits sont regroupés en paires utilisant le ''carry skip''. Pour ceux qui veulent en savoir plus sur cet incrémenteur, voici un lien sur le sujet :
* [https://www.righto.com/2013/11/the-z-80s-16-bit-incrementdecrement.html The Z-80's 16-bit increment/decrement circuit reverse engineered].
==Les décrémenteurs==
Les circuits décrémenteurs font l'inverse d'un incrémenteur : au lieu d'ajouter 1 à un opérande, ils lui soustraient 1. La différence n'est pas énorme, ce qui fait que les circuits incrémenteurs et décrémenteurs se ressemblent beaucoup. Là où les incrémenteurs sont fabriqués à partir de demi-additionneurs, les décrémenteurs sont bâtis avec des demi-soustracteurs.
La soustraction se fait en binaire comme en décimal. On soustrait deux chiffres, puis on doit propager une éventuelle retenue sur la colonne suivante. La retenue apparait quand le chiffre soustrait est plus grand que l'autre chiffre. Elle est propagée sur la colonne suivante, où elle doit être soustraite du résultat. Pour le dire autrement, après avoir soustrait un chiffre, on doit de plus soustraire la retenue de la colonne précédente.
La décrémentation effectue le calcul suivant :
<math>a_7</math> <math>a_6</math> <math>a_5</math> <math>a_4</math> <math>a_3</math> <math>a_2</math> <math>a_1</math> <math>a_0</math>
- 0 0 0 0 0 0 0 1
La colonne la plus à droite soustrait un 1. Par contre, les colonnes suivantes soustraient un zéro. Soustraire un zéro ne change rien. Mais il ne faut pas oublier de soustraire une éventuellement retenue, propagée depuis la colonne précédente. En clair, un décrémenteur peut se construire si on sait soustraire deux bits. On soustrait un 1 sur la colonne la plus à droite, on soustrait une retenue sur les autres colonnes. Et pour soustraire deux bits, il faut utiliser un demi-soustracteur.
===Le demi-soustracteur===
Un '''demi-soustracteur''' soustrait deux bits. Pour comprendre comment soustraire deux bits, traitons les quatre cas possibles un par un.
Premièrement, soustraire zéro à un bit ne changera rien :
* 0 - 0 = 0 ;
* 1 - 0 = 1.
Si on soustrait 1 à un bit qui vaut 1, on obtient zéro.
* 1 - 1 = 0.
Maintenant, que se passe-t-il si on soustrait 1 à 0 ? Voici le résultat :
* 0 - 1 = 1 et une retenue propagée sur la colonne suivante, où elle est soustraite.
La table de soustraction est donc la suivante :
* 0 - 0 = 0 ;
* 0 - 1 = 1 et une retenue ;
* 1 - 0 = 1 ;
* 1 - 1 = 0.
Si on construit la table de vérité, et qu'on construit le circuit associé, on tombe sur ce circuit. Détail important : le demi-soustracteur fait ici la soustraction X - Y. En anglais, X est appelé le ''Minuend'' et Y est appelé le ''subtrahend'', mais ces termes n'ont pas de traduction française.
[[File:Half Subtractor Vektor.svg|centre|vignette|Demi-soustracteur.]]
===Les demi-additionneurs et demi-soustracteurs===
Vous l'aurez sans doute remarqué, mais le demi-soustracteur ressemble beaucoup au demi-additionneur. La seule différence est une porte NON ajoutée au bon endroit dans le demi-soustracteur. Et attention : cette porte NON inverse le bit X, sachant que le demi-soustracteur fait la soustraction X - Y.
{|
|[[File:Half-adder.svg|vignette|Demi-additionneur]]
|[[File:Half Subtractor Vektor.svg|vignette|upright=1.3|Demi-soustracteur]]
|}
La conséquence est qu'il est possible de fusionner un demi-additionneur avec un demi-soustracteur. L'idée est de remplacer la porte NON du demi-soustracteur par un inverseur commandable. Si on envoie un zéro sur l'entrée de commande, l'inverseur commandable se comportera comme une porte OUI. Le circuit sera alors identique à un demi-additionneur. Si on envoie un 1 sur l'entrée de commande, l'inverseur commandable se comportera comme une porte NON. le circuit sera alors un demi-soustracteur.
===L'incrémenteur/décrémenteurs à propagation de retenue===
un décrémenteur à propagation de retenue se construit comme un incrémenteur, sauf qu'on remplace les demi-additionneurs par des demi-soustracteurs. En clair, on enchaine plusieurs demi-soustracteurs les uns à la suite des autres. Le circuit est équivalent à un incrémenteur à propagation de retenue, auquel on aurait ajouté un circuit inverseur. Le circuit inverseur en question est juste composé d'une couche de portes NON, qui inverse l'opérande à décrémenter.
Il est possible d'utiliser l'anticipation de retenue, mais les circuits sont cependant différents. Idem avec le ''carry skip'', et toute autre optimisation possible. La raison est que les retenues sont calculées différemment, ce qui fait que l’anticipation de retenue doit être adaptée.
Il est possible de fusionner un décrémenteur et un incrémenteur assez simplement. La seule différence étant une couche de portes NON qui inverse l'opérande, on peut remplacer ces portes NON par des inverseurs commandables. Les inverseurs commandables sont configurables pour servir soit de portes NON (mode décrémenteur), soit de portes OUI (mode incrémenteur). Il suffit pour cela d'utiliser plusieurs circuits précédents, et de commander tous les inverseurs commandables avec le même bit d'entrée. Ce bit sélectionne l'opération demandée : incrémentation ou décrémentation.
<noinclude>
{{NavChapitre | book=Fonctionnement d'un ordinateur
| prev=Les circuits de sélection
| prevText=Les circuits de sélection
| next=Les bascules : des mémoires de 1 bit
| nextText=Les bascules : des mémoires de 1 bit
}}
</noinclude>
himv0gxa722gjrqcgolo2y5ozleztao
768781
768777
2026-06-27T13:41:56Z
Mewtow
31375
/* L'incrémenteur/décrémenteurs à propagation de retenue */
768781
wikitext
text/x-wiki
Le circuit '''incrémenteur''' incrémente un nombre entier. Plus précisément, il prend en entrée un nombre entier, et fournit en entier le nombre suivant, il lui ajoute 1. Une telle opération peut paraitre triviale et peu intéressante. Cependant, elle est très utilisée et est absolument cruciale pour créer des compteurs, des circuits capables de compter ou décompter, qui auront droit à un chapitre dédié. De plus, au-delà de leur utilisation dans les compteurs, les incrémenteurs étaient très utilisés sur les premiers processeurs 8 bits, comme le Z-80, le 6502, les premiers processeurs x86 comme le 8008, le 8086, le 8085, et bien d'autres.
Il existe aussi des ''circuits décrémenteurs'', qui décrémentent un opérande, ainsi que des ''circuits incrémenteur-décrémenteur'', qui peuvent incrémenter ou décrémenter, selon comment on les configure. Je ne vais pas détailler ces circuits plus que ça, car de tels circuits sont assez rares, comparé à un circuit incrémenteur simple.
==Le demi-additionneur==
Le circuit incrémenteur effectue l'opération suivante :
<math>a_7</math> <math>a_6</math> <math>a_5</math> <math>a_4</math> <math>a_3</math> <math>a_2</math> <math>a_1</math> <math>a_0</math>
+ 0 0 0 0 0 0 0 1
Un incrémenteur basique doit donc faire une addition pour chaque colonne, et précisément une addition de deux bits. Il se trouve que la table d'addition est très simple en binaire. Jugez plutôt :
* 0 + 0 = 0, retenue = 0 ;
* 0 + 1 = 1, retenue = 0 ;
* 1 + 0 = 1, retenue = 0 ;
* 1 + 1 = 0, retenue = 1.
Un circuit capable d'additionner deux bits est appelé un '''demi-additionneur'''. Il dispose d'une sortie S pour la somme, et C pour la retenue (''carry'' an anglais).
===Le demi-additionneur classique===
Un demi-additionneur est très simple à construire avec les techniques vues dans les premiers chapitres. Voici sa table de vérité :
{|class="wikitable"
|-
! Retenue entrante !! Opérande 1 !! !! Retenue sortante !! Bit de somme
|-
||0||0|| ||0||0
|-
||0||1|| ||0||1
|-
||1||0|| ||0||1
|-
||1||1|| ||1||0
|}
On voit immédiatement que la colonne des retenues donne une porte ET, alors que celle du bit de somme est calculé par un XOR.
{| class="flexible"
|[[File:1-bit half-adder.svg|class=transparent|centre|Demi-addtionneur.]]
|[[File:Half-adder.svg|centre|class=transparent|Circuit d'un demi-addtionneur.]]
|}
Il existe beaucoup d'autres implémentations alternatives, qui utilisent moins de portes logiques, calculent la retenue plus rapidement, consomment moins d'énergie, et autres. Et il est intéressant d'étudier quelques alternatives, surtout qu'elles serviront plus bas, quand on étudiera le circuit incrémenteur du 8085.
===Le demi-additionneur basé sur une modification de la retenue===
Une source d'amélioration est liée à la porte XOR. En pratique, une porte XOR est composée en combinant plusieurs portes logiques ET/OU/NOR/NAND ensemble. Et il se trouve qu'il y a des redondances entre celles-ci et les portes utilisées pour calculer la retenue. Par exemple, rappelons qu'une porte XOR peut être construite avec une porte ET et deux portes NOR, comme illustré ci-dessous. Et il se trouve que la porte ET interne calcule la retenue sortante. En supprimant cette redondance, on économise quelques portes logiques. De plus, on se retrouve avec un demi-additionneur qui calcule le bit de somme à partir de la retenue sortante.
[[File:Demi-additionneur avec redondances éliminées.png|centre|vignette|upright=2|Demi-additionneur avec redondances éliminées]]
Le circuit précédent a une interprétation logique. Si vous regardez la table de vérité, vous remarquerez que la somme de deux bits est égale à l'inverse de la retenue sortante, sauf dans le cas où les deux bits additionnés valent zéro. Et le circuit précédent est basé là-dessus.
{|class="wikitable"
|-
! Retenue entrante !! Opérande 1 !! !! Retenue sortante !! Bit de somme
|- class="f_vert"
||0||0|| ||0||0
|- class="f_rouge"
||0||1|| ||0||1
|- class="f_rouge"
||1||0|| ||0||1
|- class="f_rouge"
||1||1|| ||1||0
|}
L'idée est de calculer l'inverse du bit de somme, avant de l'inverser avec une porte NON. L'inverse du bit de somme vaut 1, soit quand la retenue est à 1, soit quand les deux bits additionnés sont à 0. La porte ET calcule la retenue sortante, la première porte NOR détecte sur les deux bits d'entrée valent zéro. Une porte OU combine les deux résultat pour obtenir l'inverse du bit d'entrée, puis une porte NOn inverse le tout pour obtenir le bit de somme adéquat. Les deux portes sont fusionnées : c'est la seconde porte NOR.
[[File:Full adder HA + MUX.png|centre|vignette|upright=2.5|Demi-additionneur basé sur une inversion de la retenue sortante.]]
===Les implémentations alternatives===
Une implémentation alternative, qui n'utilise que des portes NOR, est la suivante :
[[File:Demi-additionneur fait avec des portes NOR et NAND.png|centre|vignette|upright=2|Demi-additionneur fait avec des portes NOR et NAND.]]
Une implémentation alternative utilise des portes NAND. Pour rappel, il est possible de créer une porte XOR avec 4 portes NAND. La première d'entre elle fait un NAND entre les deux bits d'entrée, ce qui fait qu'elle calcule l'inverse de la retenue sortante. Le tout est illustré ci-dessous. En théorie, on devrait utiliser une porte NON pour récupérer la retenue correcte. Mais nous verrons plus bas que ce n'est pas une obligation. Nous verrons plus bas un exemple où c'est l'inverse de la retenue qui est utilisée dans les calculs, pour rendre les calculs plus rapides.
[[File:Demi-additionneur fait avec des portes NAND.png|centre|vignette|upright=2|Demi-additionneur fait avec des portes NAND]]
==L'incrémenteur à propagation de retenue==
Maintenant que l'on sait comment additionner deux bits, reprenons l'opération d'incrémentation :
<math>a_7</math> <math>a_6</math> <math>a_5</math> <math>a_4</math> <math>a_3</math> <math>a_2</math> <math>a_1</math> <math>a_0</math>
+ 0 0 0 0 0 0 0 1
------------------------------
Sur la colonne la plus à droite, il doit ajouter un au bit de poids faible. Pour les colonnes suivantes, il faut additionner le bit de l'opérande avec la retenue de la colonne précédente. En clair, on n'additionne que deux bits à chaque colonne : un 1 sur celle tout à droite, la retenue de la colonne précédente pour les autres. Et cela nous donne une idée de comment faire pour créer le circuit incrémenteur.
Il suffit d'utiliser un demi-additionneur par colonne, et de les enchainer les uns à la suite des autres. Chaque demi-additionneur additionne le bit de l'opérande avec la retenue fournie par le demi-additionneur précédent. Le seul qui fait exception est celui pour la colonne de poids faible. Pour celui-là, il doit ajouter 1 au bit de poids faible. Le résultat est appelé un '''incrémenteur à propagation de retenue'''. Il est constitué de demi-additionneurs enchaînés les uns à la suite des autres, du bit de poids faible vers le bit de poids fort.
[[File:Circuit incrémenteur.png|centre|vignette|upright=3|Circuit incrémenteur.]]
Maintenant, regardons le demi-additionneur le plus à droite, celui pour le bit de poids faible. Son entrée de retenue entrante est mise à 1 pour faire l'incrémentation. Quelques incrémenteurs permettent de configurer cette entrée de retenue à 0 ou à 1, ce qui effectue : soit une opération identité (l'opérande est recopié sur la sortie), soit une incrémentation. Un tel circuit est nommé un '''incrémenteur commandable'''. Nous aurons à utiliser une fois ou deux de tels incrémenteurs commandables dans la suite du cours.
L'incrémenteur à propagation de retenue est le plus simple et le plus économe en portes logiques. Mais de tels incrémenteurs sont rarement utilisés. À la place, on leur préfère des incrémenteurs plus rapides, mais qui utilisent plus de portes logiques. De tels incrémenteurs accélèrent le calcul des retenues. En effet, la rapidité d'une incrémentation est limitée par la propagation de la retenue : les retenues commencent à être calculées au bit de poids fort et on doit les calculer une par une, jusqu’à atteindre le bit de poids fort. Et cette "propagation des retenues" prend du temps, d'autant plus de temps que l'opérande est longue. Il y a deux optimisations principales, appelées le ''carry skip'' et l'anticipation de retenue, que nous allons décrire ci-dessous.
==Les incrémenteurs ''carry skip''==
L'optimisation '''''carry skip''''' effectue l'incrémentation, non pas bit par bit, mais par paquets de deux bits. Le résultat est que l'incrémentation est deux fois plus rapide, ou presque. Le circuit incrémenteur est donc composé en enchainant non pas des demi-additionneurs, mais des '''incrémenteurs 2 bits''' qui incrémentent un opérande de deux bits.
[[File:Circuit incrémenteur optimisé, faisant l'incrémentation par paquet de deux bits.png|centre|vignette|upright=2.5|Circuit incrémenteur optimisé, faisant l'incrémentation par paquet de deux bits]]
Reste à concevoir l'incrémenteur 2 bits.
===Les optimisations au niveau des transistors et l'exemple de l'incrémenteur du 8085 d'Intel===
Une première solution part de deux demi-additionneurs et effectue quelques simplifications au niveau des transistors et des portes logiques. Par exemple, l'Intel 8085 a utilisé un circuit optimisé, fabriqué avec des portes NAND et NOR. La raison est qu'avec les technologies de transistors CMOS actuelles, les portes NAND et NOR utilisent moins de transistors que les portes ET et OU. Et cette possibilité a été utilisée pour effectuer des simplifications assez mineures, mais toujours bonnes à prendre. Mais nous ne pouvons pas en parler en détail ici, car nous n'avons pas encore parlé en détail des transistors. Cependant, un exemple bien précis nous est accesible : celui de l’incrémenteur du processeur 8085 d'Intel.
Le processeur 8085 d'Intel est un processeur 8 bits très ancien. Il contenait un incrémenteur 16 bits, qui était utilisé pour calculer des adresses mémoire. Et cet incrémenteur était un incrémenteur à propagation de retenue optimisé. Il utilisait lui aussi des incrémenteurs 2 bits un peu modifiés.
L'idée était que les retenues sortant des colonnes paires étaient inversées, les colonnes impaires faisaient les calculs à partir de cette retenue inversée. Les demi-additionneurs des colonnes impaires n'étaient donc pas les mêmes que ceux des colonnes paires. Leurs tables de vérité sont différentes, leurs circuits aussi, et aucun des deux ne ressemble à un demi-additionneur normal. En réalité, ils sont fabriqués à partir des implémentations vues plus haut, qui utilisent uniquement des portes NAND ou uniquement des portes NOR.
Pour comprendre l'intérêt de faire ainsi, nous devons préciser une chose importante : avec les technologies CMOS utilisées pour les processeurs depuis les années 70, les portes logiques les plus simples sont les portes NON, NOR et NAND. Les portes ET/OU sont fabriquées en combinant des portes NOR/NAND avec une porte NON, ce qui prend plus de transistors.
Un demi-additionneur est donc fabriqué comme illustré ci-dessous, en logique CMOS. Seules les portes pour le calcul de la retenue sont indiquées, la porte XOR pour le bit de somme est fabriquée à partir de plusieurs portes logiques.
[[File:Demi-additionneur en CMOS.png|centre|vignette|upright=2|Demi-additionneur en CMOS]]
Si on enchaine deux demi-additionneurs, cela donne ceci :
[[File:Brique de base de l'incrémenteur du 8085.png|centre|vignette|upright=2|Brique de base de l'incrémenteur du 8085]]
La retenue doit donc traverser quatre portes logiques, dont deux portes NON. En inversant la retenue, les portes en jaune dans le schéma précédent se simplifient. Il suffit d'utiliser la loi de de Morgan pour trouver un circuit équivalent aux portes jaunes, qui n'est autre qu'une porte NOR couplée à une porte NON. Un autre moyen d'obtenir le même résultat est de prendre un demi-additionneur fabriqué avec uniquement des portes NOR, et de retirer la porte NON adéquate. Il faut ensuite corriger l'entrée de retenue de la porte XOR, pour qu'elle fasse l'addition avec la retenue correctement. Le résultat est illustré ci-dessous.
[[File:Incrémenteur 2 bits de l'incrémenteur du 8085.png|centre|vignette|upright=2|Incrémenteur 2 bits de l'incrémenteur du 8085]]
Le circuit utilise le même nombre de portes logiques, il n'économise pas de transistors. Par contre, la retenue n'a plus qu'à traverser deux portes logiques, au lieu de quatre. La propagation de la retenue est donc plus rapide, l'incrémentation est donc plus rapide.
===Les incrémenteurs 2 bits ''carry skip''===
Une solution alternative fabrique des incrémenteurs 2 bits qui calculent séparément les deux bits du résultat et la retenue sortante. La retenue sortante est calculée à partir des bits de l’opérande, sans propager des retenues. Ainsi, la retenue sortante est connue en avance, sans passer par deux demi-additionneurs. Pour créer un tel incrémenteur, le mieux est de partir de sa table de vérité.
{|class="wikitable"
|-
! Opérande !! Retenue entrante !! !! Retenue sortante !! Résultat
|-
| 00 || 0 || || 0 || 00
|-
| 01 || 0 || || 0 || 01
|-
| 10 || 0 || || 0 || 10
|-
| 11 || 0 || || 0 || 11
|-
| colspan="5" |
|-
| 00 || 1 || || 0 || 01
|-
| 01 || 1 || || 0 || 10
|-
| 10 || 1 || || 0 || 11
|-
| 11 || 1 || || 1 || 00
|}
Elle nous dit que la retenue sortante ne vaut 1 que dans un seul cas : les deux bits d'opérande valent 1, la retenue entrante vaut 1. Elle se calcule alors avec une porte ET à trois entrées. Pour les deux bits du résultat, les équations logiques ne donnent pas un résultat satisfaisant. La solution la plus simple est d'utiliser deux demi-additionneurs et de retirer les portes logiques superflues. Le résultat est alors un '''incrémenteur 2 bits ''carry skip''''', où la retenue sortante est calculée séparément du reste.
[[File:Incrémenteur carry skip 2 bits.png|centre|vignette|upright=2.5|Incrémenteur ''carry skip'' 2 bits]]
==Les incrémenteurs à anticipation de retenue==
L''''anticipation de retenue''' calcule la retenue d'une colonne sans attendre les retenues des colonnes précédentes. Dans le cas idéal, toutes les retenues sont calculées en parallèle, en même temps, et sont ensuite envoyées aux demi-additionneurs. Il s'agit d'une optimisation qui est utilisée pour l'incrémentation, l'addition et d'autres opérations similaires. Pour l'incrémentation, déterminer la retenue ne demande pas de calculs complexes, contrairement à l'addition.
[[File:Incrémenteur à anticipation de retenues, 8 bits.png|centre|vignette|upright=2.5|Incrémenteur à anticipation de retenues, 8 bits.]]
Pour le comprendre, on peut regarder comment la retenue sortant d'un demi-additionneur est formée. Elle est calculée en faisant un ET logique entre la retenue entrante et le bit d'opérande. Une retenue est donc un ET logique entre toutes les retenues précédentes. Un incrémenteur à anticipation de retenue utilise donc une porte ET à plusieurs entrées pour calculer une retenue.
[[File:Anticipation de retenue pour un bit du résultat, incrémenteur.png|centre|vignette|upright=2.5|Anticipation de retenue pour un bit du résultat, incrémenteur]]
En théorie, on peut utiliser une porte ET à plusieurs entrées pour chaque bit de l'opérande. Cependant, cela entrainera un cout en transistors très important. Pour éviter de gaspiller trop de portes logiques, une solution est de mélanger anticipation de retenues et propagation de retenue. Par exemple, pour un incrémenteur 32 bits, on peut découper l'opérande en 4 octets : on anticipe les retenues pour chaque octet, mais l'incrémentation de chaque octet se fait avec propagation de retenue et/ou ''carry skip''.
[[File:Incrémenteur à anticipation de retenues.png|centre|vignette|upright=3|Incrémenteur hybride utilisant partiellement l'anticipation de retenues.]]
Quelques processeurs utilisaient l'anticipation de retenues. Par exemple, le processeur Z-80 de Zilog utilisait un incrémenteur pour des nombres de 16 bits, ce qui demandait des performances assez élevées. Et cet incrémenteur utilisait à la fois anticipation de retenues et ''carry skip''. Il était découpé en quatre blocs avant anticipation de retenues entre eux : un bloc regroupant les 7 bits de poids faible, suivi par un bloc de 5 bits, lui-même suivi par un bloc de 3 bits, terminé par un dernier bit isolé. A l'intérieur de ces blocs, les bits sont regroupés en paires utilisant le ''carry skip''. Pour ceux qui veulent en savoir plus sur cet incrémenteur, voici un lien sur le sujet :
* [https://www.righto.com/2013/11/the-z-80s-16-bit-incrementdecrement.html The Z-80's 16-bit increment/decrement circuit reverse engineered].
==Les décrémenteurs==
Les circuits décrémenteurs font l'inverse d'un incrémenteur : au lieu d'ajouter 1 à un opérande, ils lui soustraient 1. La différence n'est pas énorme, ce qui fait que les circuits incrémenteurs et décrémenteurs se ressemblent beaucoup. Là où les incrémenteurs sont fabriqués à partir de demi-additionneurs, les décrémenteurs sont bâtis avec des demi-soustracteurs.
La soustraction se fait en binaire comme en décimal. On soustrait deux chiffres, puis on doit propager une éventuelle retenue sur la colonne suivante. La retenue apparait quand le chiffre soustrait est plus grand que l'autre chiffre. Elle est propagée sur la colonne suivante, où elle doit être soustraite du résultat. Pour le dire autrement, après avoir soustrait un chiffre, on doit de plus soustraire la retenue de la colonne précédente.
La décrémentation effectue le calcul suivant :
<math>a_7</math> <math>a_6</math> <math>a_5</math> <math>a_4</math> <math>a_3</math> <math>a_2</math> <math>a_1</math> <math>a_0</math>
- 0 0 0 0 0 0 0 1
La colonne la plus à droite soustrait un 1. Par contre, les colonnes suivantes soustraient un zéro. Soustraire un zéro ne change rien. Mais il ne faut pas oublier de soustraire une éventuellement retenue, propagée depuis la colonne précédente. En clair, un décrémenteur peut se construire si on sait soustraire deux bits. On soustrait un 1 sur la colonne la plus à droite, on soustrait une retenue sur les autres colonnes. Et pour soustraire deux bits, il faut utiliser un demi-soustracteur.
===Le demi-soustracteur===
Un '''demi-soustracteur''' soustrait deux bits. Pour comprendre comment soustraire deux bits, traitons les quatre cas possibles un par un.
Premièrement, soustraire zéro à un bit ne changera rien :
* 0 - 0 = 0 ;
* 1 - 0 = 1.
Si on soustrait 1 à un bit qui vaut 1, on obtient zéro.
* 1 - 1 = 0.
Maintenant, que se passe-t-il si on soustrait 1 à 0 ? Voici le résultat :
* 0 - 1 = 1 et une retenue propagée sur la colonne suivante, où elle est soustraite.
La table de soustraction est donc la suivante :
* 0 - 0 = 0 ;
* 0 - 1 = 1 et une retenue ;
* 1 - 0 = 1 ;
* 1 - 1 = 0.
Si on construit la table de vérité, et qu'on construit le circuit associé, on tombe sur ce circuit. Détail important : le demi-soustracteur fait ici la soustraction X - Y. En anglais, X est appelé le ''Minuend'' et Y est appelé le ''subtrahend'', mais ces termes n'ont pas de traduction française.
[[File:Half Subtractor Vektor.svg|centre|vignette|Demi-soustracteur.]]
===Les demi-additionneurs et demi-soustracteurs===
Vous l'aurez sans doute remarqué, mais le demi-soustracteur ressemble beaucoup au demi-additionneur. La seule différence est une porte NON ajoutée au bon endroit dans le demi-soustracteur. Et attention : cette porte NON inverse le bit X, sachant que le demi-soustracteur fait la soustraction X - Y.
{|
|[[File:Half-adder.svg|vignette|Demi-additionneur]]
|[[File:Half Subtractor Vektor.svg|vignette|upright=1.3|Demi-soustracteur]]
|}
La conséquence est qu'il est possible de fusionner un demi-additionneur avec un demi-soustracteur. L'idée est de remplacer la porte NON du demi-soustracteur par un inverseur commandable. Si on envoie un zéro sur l'entrée de commande, l'inverseur commandable se comportera comme une porte OUI. Le circuit sera alors identique à un demi-additionneur. Si on envoie un 1 sur l'entrée de commande, l'inverseur commandable se comportera comme une porte NON. le circuit sera alors un demi-soustracteur.
===L'incrémenteur/décrémenteurs à propagation de retenue===
un décrémenteur à propagation de retenue se construit comme un incrémenteur, sauf qu'on remplace les demi-additionneurs par des demi-soustracteurs. En clair, on enchaine plusieurs demi-soustracteurs les uns à la suite des autres. Il est possible d'utiliser l'anticipation de retenue, mais les circuits sont cependant différents. Idem avec le ''carry skip'', et toute autre optimisation possible. La raison est que les retenues sont calculées différemment, ce qui fait que l’anticipation de retenue doit être adaptée.
Il est possible de fusionner un décrémenteur et un incrémenteur assez simplement. La seule différence étant une couche de portes NON qui inverse l'opérande, on peut remplacer ces portes NON par des inverseurs commandables. Les inverseurs commandables sont configurables pour servir soit de portes NON (mode décrémenteur), soit de portes OUI (mode incrémenteur). Il suffit pour cela d'utiliser plusieurs circuits précédents, et de commander tous les inverseurs commandables avec le même bit d'entrée. Ce bit sélectionne l'opération demandée : incrémentation ou décrémentation.
<noinclude>
{{NavChapitre | book=Fonctionnement d'un ordinateur
| prev=Les circuits de sélection
| prevText=Les circuits de sélection
| next=Les bascules : des mémoires de 1 bit
| nextText=Les bascules : des mémoires de 1 bit
}}
</noinclude>
nkd79hx7yh9ptsawmy7ryrhm0zxpabe
768784
768781
2026-06-27T13:46:16Z
Mewtow
31375
/* L'incrémenteur/décrémenteurs à propagation de retenue */
768784
wikitext
text/x-wiki
Le circuit '''incrémenteur''' incrémente un nombre entier. Plus précisément, il prend en entrée un nombre entier, et fournit en entier le nombre suivant, il lui ajoute 1. Une telle opération peut paraitre triviale et peu intéressante. Cependant, elle est très utilisée et est absolument cruciale pour créer des compteurs, des circuits capables de compter ou décompter, qui auront droit à un chapitre dédié. De plus, au-delà de leur utilisation dans les compteurs, les incrémenteurs étaient très utilisés sur les premiers processeurs 8 bits, comme le Z-80, le 6502, les premiers processeurs x86 comme le 8008, le 8086, le 8085, et bien d'autres.
Il existe aussi des ''circuits décrémenteurs'', qui décrémentent un opérande, ainsi que des ''circuits incrémenteur-décrémenteur'', qui peuvent incrémenter ou décrémenter, selon comment on les configure. Je ne vais pas détailler ces circuits plus que ça, car de tels circuits sont assez rares, comparé à un circuit incrémenteur simple.
==Le demi-additionneur==
Le circuit incrémenteur effectue l'opération suivante :
<math>a_7</math> <math>a_6</math> <math>a_5</math> <math>a_4</math> <math>a_3</math> <math>a_2</math> <math>a_1</math> <math>a_0</math>
+ 0 0 0 0 0 0 0 1
Un incrémenteur basique doit donc faire une addition pour chaque colonne, et précisément une addition de deux bits. Il se trouve que la table d'addition est très simple en binaire. Jugez plutôt :
* 0 + 0 = 0, retenue = 0 ;
* 0 + 1 = 1, retenue = 0 ;
* 1 + 0 = 1, retenue = 0 ;
* 1 + 1 = 0, retenue = 1.
Un circuit capable d'additionner deux bits est appelé un '''demi-additionneur'''. Il dispose d'une sortie S pour la somme, et C pour la retenue (''carry'' an anglais).
===Le demi-additionneur classique===
Un demi-additionneur est très simple à construire avec les techniques vues dans les premiers chapitres. Voici sa table de vérité :
{|class="wikitable"
|-
! Retenue entrante !! Opérande 1 !! !! Retenue sortante !! Bit de somme
|-
||0||0|| ||0||0
|-
||0||1|| ||0||1
|-
||1||0|| ||0||1
|-
||1||1|| ||1||0
|}
On voit immédiatement que la colonne des retenues donne une porte ET, alors que celle du bit de somme est calculé par un XOR.
{| class="flexible"
|[[File:1-bit half-adder.svg|class=transparent|centre|Demi-addtionneur.]]
|[[File:Half-adder.svg|centre|class=transparent|Circuit d'un demi-addtionneur.]]
|}
Il existe beaucoup d'autres implémentations alternatives, qui utilisent moins de portes logiques, calculent la retenue plus rapidement, consomment moins d'énergie, et autres. Et il est intéressant d'étudier quelques alternatives, surtout qu'elles serviront plus bas, quand on étudiera le circuit incrémenteur du 8085.
===Le demi-additionneur basé sur une modification de la retenue===
Une source d'amélioration est liée à la porte XOR. En pratique, une porte XOR est composée en combinant plusieurs portes logiques ET/OU/NOR/NAND ensemble. Et il se trouve qu'il y a des redondances entre celles-ci et les portes utilisées pour calculer la retenue. Par exemple, rappelons qu'une porte XOR peut être construite avec une porte ET et deux portes NOR, comme illustré ci-dessous. Et il se trouve que la porte ET interne calcule la retenue sortante. En supprimant cette redondance, on économise quelques portes logiques. De plus, on se retrouve avec un demi-additionneur qui calcule le bit de somme à partir de la retenue sortante.
[[File:Demi-additionneur avec redondances éliminées.png|centre|vignette|upright=2|Demi-additionneur avec redondances éliminées]]
Le circuit précédent a une interprétation logique. Si vous regardez la table de vérité, vous remarquerez que la somme de deux bits est égale à l'inverse de la retenue sortante, sauf dans le cas où les deux bits additionnés valent zéro. Et le circuit précédent est basé là-dessus.
{|class="wikitable"
|-
! Retenue entrante !! Opérande 1 !! !! Retenue sortante !! Bit de somme
|- class="f_vert"
||0||0|| ||0||0
|- class="f_rouge"
||0||1|| ||0||1
|- class="f_rouge"
||1||0|| ||0||1
|- class="f_rouge"
||1||1|| ||1||0
|}
L'idée est de calculer l'inverse du bit de somme, avant de l'inverser avec une porte NON. L'inverse du bit de somme vaut 1, soit quand la retenue est à 1, soit quand les deux bits additionnés sont à 0. La porte ET calcule la retenue sortante, la première porte NOR détecte sur les deux bits d'entrée valent zéro. Une porte OU combine les deux résultat pour obtenir l'inverse du bit d'entrée, puis une porte NOn inverse le tout pour obtenir le bit de somme adéquat. Les deux portes sont fusionnées : c'est la seconde porte NOR.
[[File:Full adder HA + MUX.png|centre|vignette|upright=2.5|Demi-additionneur basé sur une inversion de la retenue sortante.]]
===Les implémentations alternatives===
Une implémentation alternative, qui n'utilise que des portes NOR, est la suivante :
[[File:Demi-additionneur fait avec des portes NOR et NAND.png|centre|vignette|upright=2|Demi-additionneur fait avec des portes NOR et NAND.]]
Une implémentation alternative utilise des portes NAND. Pour rappel, il est possible de créer une porte XOR avec 4 portes NAND. La première d'entre elle fait un NAND entre les deux bits d'entrée, ce qui fait qu'elle calcule l'inverse de la retenue sortante. Le tout est illustré ci-dessous. En théorie, on devrait utiliser une porte NON pour récupérer la retenue correcte. Mais nous verrons plus bas que ce n'est pas une obligation. Nous verrons plus bas un exemple où c'est l'inverse de la retenue qui est utilisée dans les calculs, pour rendre les calculs plus rapides.
[[File:Demi-additionneur fait avec des portes NAND.png|centre|vignette|upright=2|Demi-additionneur fait avec des portes NAND]]
==L'incrémenteur à propagation de retenue==
Maintenant que l'on sait comment additionner deux bits, reprenons l'opération d'incrémentation :
<math>a_7</math> <math>a_6</math> <math>a_5</math> <math>a_4</math> <math>a_3</math> <math>a_2</math> <math>a_1</math> <math>a_0</math>
+ 0 0 0 0 0 0 0 1
------------------------------
Sur la colonne la plus à droite, il doit ajouter un au bit de poids faible. Pour les colonnes suivantes, il faut additionner le bit de l'opérande avec la retenue de la colonne précédente. En clair, on n'additionne que deux bits à chaque colonne : un 1 sur celle tout à droite, la retenue de la colonne précédente pour les autres. Et cela nous donne une idée de comment faire pour créer le circuit incrémenteur.
Il suffit d'utiliser un demi-additionneur par colonne, et de les enchainer les uns à la suite des autres. Chaque demi-additionneur additionne le bit de l'opérande avec la retenue fournie par le demi-additionneur précédent. Le seul qui fait exception est celui pour la colonne de poids faible. Pour celui-là, il doit ajouter 1 au bit de poids faible. Le résultat est appelé un '''incrémenteur à propagation de retenue'''. Il est constitué de demi-additionneurs enchaînés les uns à la suite des autres, du bit de poids faible vers le bit de poids fort.
[[File:Circuit incrémenteur.png|centre|vignette|upright=3|Circuit incrémenteur.]]
Maintenant, regardons le demi-additionneur le plus à droite, celui pour le bit de poids faible. Son entrée de retenue entrante est mise à 1 pour faire l'incrémentation. Quelques incrémenteurs permettent de configurer cette entrée de retenue à 0 ou à 1, ce qui effectue : soit une opération identité (l'opérande est recopié sur la sortie), soit une incrémentation. Un tel circuit est nommé un '''incrémenteur commandable'''. Nous aurons à utiliser une fois ou deux de tels incrémenteurs commandables dans la suite du cours.
L'incrémenteur à propagation de retenue est le plus simple et le plus économe en portes logiques. Mais de tels incrémenteurs sont rarement utilisés. À la place, on leur préfère des incrémenteurs plus rapides, mais qui utilisent plus de portes logiques. De tels incrémenteurs accélèrent le calcul des retenues. En effet, la rapidité d'une incrémentation est limitée par la propagation de la retenue : les retenues commencent à être calculées au bit de poids fort et on doit les calculer une par une, jusqu’à atteindre le bit de poids fort. Et cette "propagation des retenues" prend du temps, d'autant plus de temps que l'opérande est longue. Il y a deux optimisations principales, appelées le ''carry skip'' et l'anticipation de retenue, que nous allons décrire ci-dessous.
==Les incrémenteurs ''carry skip''==
L'optimisation '''''carry skip''''' effectue l'incrémentation, non pas bit par bit, mais par paquets de deux bits. Le résultat est que l'incrémentation est deux fois plus rapide, ou presque. Le circuit incrémenteur est donc composé en enchainant non pas des demi-additionneurs, mais des '''incrémenteurs 2 bits''' qui incrémentent un opérande de deux bits.
[[File:Circuit incrémenteur optimisé, faisant l'incrémentation par paquet de deux bits.png|centre|vignette|upright=2.5|Circuit incrémenteur optimisé, faisant l'incrémentation par paquet de deux bits]]
Reste à concevoir l'incrémenteur 2 bits.
===Les optimisations au niveau des transistors et l'exemple de l'incrémenteur du 8085 d'Intel===
Une première solution part de deux demi-additionneurs et effectue quelques simplifications au niveau des transistors et des portes logiques. Par exemple, l'Intel 8085 a utilisé un circuit optimisé, fabriqué avec des portes NAND et NOR. La raison est qu'avec les technologies de transistors CMOS actuelles, les portes NAND et NOR utilisent moins de transistors que les portes ET et OU. Et cette possibilité a été utilisée pour effectuer des simplifications assez mineures, mais toujours bonnes à prendre. Mais nous ne pouvons pas en parler en détail ici, car nous n'avons pas encore parlé en détail des transistors. Cependant, un exemple bien précis nous est accesible : celui de l’incrémenteur du processeur 8085 d'Intel.
Le processeur 8085 d'Intel est un processeur 8 bits très ancien. Il contenait un incrémenteur 16 bits, qui était utilisé pour calculer des adresses mémoire. Et cet incrémenteur était un incrémenteur à propagation de retenue optimisé. Il utilisait lui aussi des incrémenteurs 2 bits un peu modifiés.
L'idée était que les retenues sortant des colonnes paires étaient inversées, les colonnes impaires faisaient les calculs à partir de cette retenue inversée. Les demi-additionneurs des colonnes impaires n'étaient donc pas les mêmes que ceux des colonnes paires. Leurs tables de vérité sont différentes, leurs circuits aussi, et aucun des deux ne ressemble à un demi-additionneur normal. En réalité, ils sont fabriqués à partir des implémentations vues plus haut, qui utilisent uniquement des portes NAND ou uniquement des portes NOR.
Pour comprendre l'intérêt de faire ainsi, nous devons préciser une chose importante : avec les technologies CMOS utilisées pour les processeurs depuis les années 70, les portes logiques les plus simples sont les portes NON, NOR et NAND. Les portes ET/OU sont fabriquées en combinant des portes NOR/NAND avec une porte NON, ce qui prend plus de transistors.
Un demi-additionneur est donc fabriqué comme illustré ci-dessous, en logique CMOS. Seules les portes pour le calcul de la retenue sont indiquées, la porte XOR pour le bit de somme est fabriquée à partir de plusieurs portes logiques.
[[File:Demi-additionneur en CMOS.png|centre|vignette|upright=2|Demi-additionneur en CMOS]]
Si on enchaine deux demi-additionneurs, cela donne ceci :
[[File:Brique de base de l'incrémenteur du 8085.png|centre|vignette|upright=2|Brique de base de l'incrémenteur du 8085]]
La retenue doit donc traverser quatre portes logiques, dont deux portes NON. En inversant la retenue, les portes en jaune dans le schéma précédent se simplifient. Il suffit d'utiliser la loi de de Morgan pour trouver un circuit équivalent aux portes jaunes, qui n'est autre qu'une porte NOR couplée à une porte NON. Un autre moyen d'obtenir le même résultat est de prendre un demi-additionneur fabriqué avec uniquement des portes NOR, et de retirer la porte NON adéquate. Il faut ensuite corriger l'entrée de retenue de la porte XOR, pour qu'elle fasse l'addition avec la retenue correctement. Le résultat est illustré ci-dessous.
[[File:Incrémenteur 2 bits de l'incrémenteur du 8085.png|centre|vignette|upright=2|Incrémenteur 2 bits de l'incrémenteur du 8085]]
Le circuit utilise le même nombre de portes logiques, il n'économise pas de transistors. Par contre, la retenue n'a plus qu'à traverser deux portes logiques, au lieu de quatre. La propagation de la retenue est donc plus rapide, l'incrémentation est donc plus rapide.
===Les incrémenteurs 2 bits ''carry skip''===
Une solution alternative fabrique des incrémenteurs 2 bits qui calculent séparément les deux bits du résultat et la retenue sortante. La retenue sortante est calculée à partir des bits de l’opérande, sans propager des retenues. Ainsi, la retenue sortante est connue en avance, sans passer par deux demi-additionneurs. Pour créer un tel incrémenteur, le mieux est de partir de sa table de vérité.
{|class="wikitable"
|-
! Opérande !! Retenue entrante !! !! Retenue sortante !! Résultat
|-
| 00 || 0 || || 0 || 00
|-
| 01 || 0 || || 0 || 01
|-
| 10 || 0 || || 0 || 10
|-
| 11 || 0 || || 0 || 11
|-
| colspan="5" |
|-
| 00 || 1 || || 0 || 01
|-
| 01 || 1 || || 0 || 10
|-
| 10 || 1 || || 0 || 11
|-
| 11 || 1 || || 1 || 00
|}
Elle nous dit que la retenue sortante ne vaut 1 que dans un seul cas : les deux bits d'opérande valent 1, la retenue entrante vaut 1. Elle se calcule alors avec une porte ET à trois entrées. Pour les deux bits du résultat, les équations logiques ne donnent pas un résultat satisfaisant. La solution la plus simple est d'utiliser deux demi-additionneurs et de retirer les portes logiques superflues. Le résultat est alors un '''incrémenteur 2 bits ''carry skip''''', où la retenue sortante est calculée séparément du reste.
[[File:Incrémenteur carry skip 2 bits.png|centre|vignette|upright=2.5|Incrémenteur ''carry skip'' 2 bits]]
==Les incrémenteurs à anticipation de retenue==
L''''anticipation de retenue''' calcule la retenue d'une colonne sans attendre les retenues des colonnes précédentes. Dans le cas idéal, toutes les retenues sont calculées en parallèle, en même temps, et sont ensuite envoyées aux demi-additionneurs. Il s'agit d'une optimisation qui est utilisée pour l'incrémentation, l'addition et d'autres opérations similaires. Pour l'incrémentation, déterminer la retenue ne demande pas de calculs complexes, contrairement à l'addition.
[[File:Incrémenteur à anticipation de retenues, 8 bits.png|centre|vignette|upright=2.5|Incrémenteur à anticipation de retenues, 8 bits.]]
Pour le comprendre, on peut regarder comment la retenue sortant d'un demi-additionneur est formée. Elle est calculée en faisant un ET logique entre la retenue entrante et le bit d'opérande. Une retenue est donc un ET logique entre toutes les retenues précédentes. Un incrémenteur à anticipation de retenue utilise donc une porte ET à plusieurs entrées pour calculer une retenue.
[[File:Anticipation de retenue pour un bit du résultat, incrémenteur.png|centre|vignette|upright=2.5|Anticipation de retenue pour un bit du résultat, incrémenteur]]
En théorie, on peut utiliser une porte ET à plusieurs entrées pour chaque bit de l'opérande. Cependant, cela entrainera un cout en transistors très important. Pour éviter de gaspiller trop de portes logiques, une solution est de mélanger anticipation de retenues et propagation de retenue. Par exemple, pour un incrémenteur 32 bits, on peut découper l'opérande en 4 octets : on anticipe les retenues pour chaque octet, mais l'incrémentation de chaque octet se fait avec propagation de retenue et/ou ''carry skip''.
[[File:Incrémenteur à anticipation de retenues.png|centre|vignette|upright=3|Incrémenteur hybride utilisant partiellement l'anticipation de retenues.]]
Quelques processeurs utilisaient l'anticipation de retenues. Par exemple, le processeur Z-80 de Zilog utilisait un incrémenteur pour des nombres de 16 bits, ce qui demandait des performances assez élevées. Et cet incrémenteur utilisait à la fois anticipation de retenues et ''carry skip''. Il était découpé en quatre blocs avant anticipation de retenues entre eux : un bloc regroupant les 7 bits de poids faible, suivi par un bloc de 5 bits, lui-même suivi par un bloc de 3 bits, terminé par un dernier bit isolé. A l'intérieur de ces blocs, les bits sont regroupés en paires utilisant le ''carry skip''. Pour ceux qui veulent en savoir plus sur cet incrémenteur, voici un lien sur le sujet :
* [https://www.righto.com/2013/11/the-z-80s-16-bit-incrementdecrement.html The Z-80's 16-bit increment/decrement circuit reverse engineered].
==Les décrémenteurs==
Les circuits décrémenteurs font l'inverse d'un incrémenteur : au lieu d'ajouter 1 à un opérande, ils lui soustraient 1. La différence n'est pas énorme, ce qui fait que les circuits incrémenteurs et décrémenteurs se ressemblent beaucoup. Là où les incrémenteurs sont fabriqués à partir de demi-additionneurs, les décrémenteurs sont bâtis avec des demi-soustracteurs.
La soustraction se fait en binaire comme en décimal. On soustrait deux chiffres, puis on doit propager une éventuelle retenue sur la colonne suivante. La retenue apparait quand le chiffre soustrait est plus grand que l'autre chiffre. Elle est propagée sur la colonne suivante, où elle doit être soustraite du résultat. Pour le dire autrement, après avoir soustrait un chiffre, on doit de plus soustraire la retenue de la colonne précédente.
La décrémentation effectue le calcul suivant :
<math>a_7</math> <math>a_6</math> <math>a_5</math> <math>a_4</math> <math>a_3</math> <math>a_2</math> <math>a_1</math> <math>a_0</math>
- 0 0 0 0 0 0 0 1
La colonne la plus à droite soustrait un 1. Par contre, les colonnes suivantes soustraient un zéro. Soustraire un zéro ne change rien. Mais il ne faut pas oublier de soustraire une éventuellement retenue, propagée depuis la colonne précédente. En clair, un décrémenteur peut se construire si on sait soustraire deux bits. On soustrait un 1 sur la colonne la plus à droite, on soustrait une retenue sur les autres colonnes. Et pour soustraire deux bits, il faut utiliser un demi-soustracteur.
===Le demi-soustracteur===
Un '''demi-soustracteur''' soustrait deux bits. Pour comprendre comment soustraire deux bits, traitons les quatre cas possibles un par un.
Premièrement, soustraire zéro à un bit ne changera rien :
* 0 - 0 = 0 ;
* 1 - 0 = 1.
Si on soustrait 1 à un bit qui vaut 1, on obtient zéro.
* 1 - 1 = 0.
Maintenant, que se passe-t-il si on soustrait 1 à 0 ? Voici le résultat :
* 0 - 1 = 1 et une retenue propagée sur la colonne suivante, où elle est soustraite.
La table de soustraction est donc la suivante :
* 0 - 0 = 0 ;
* 0 - 1 = 1 et une retenue ;
* 1 - 0 = 1 ;
* 1 - 1 = 0.
Si on construit la table de vérité, et qu'on construit le circuit associé, on tombe sur ce circuit. Détail important : le demi-soustracteur fait ici la soustraction X - Y. En anglais, X est appelé le ''Minuend'' et Y est appelé le ''subtrahend'', mais ces termes n'ont pas de traduction française.
[[File:Half Subtractor Vektor.svg|centre|vignette|Demi-soustracteur.]]
===Les demi-additionneurs et demi-soustracteurs===
Vous l'aurez sans doute remarqué, mais le demi-soustracteur ressemble beaucoup au demi-additionneur. La seule différence est une porte NON ajoutée au bon endroit dans le demi-soustracteur. Et attention : cette porte NON inverse le bit X, sachant que le demi-soustracteur fait la soustraction X - Y.
{|
|[[File:Half-adder.svg|vignette|Demi-additionneur]]
|[[File:Half Subtractor Vektor.svg|vignette|upright=1.3|Demi-soustracteur]]
|}
La conséquence est qu'il est possible de fusionner un demi-additionneur avec un demi-soustracteur. L'idée est de remplacer la porte NON du demi-soustracteur par un inverseur commandable. Si on envoie un zéro sur l'entrée de commande, l'inverseur commandable se comportera comme une porte OUI. Le circuit sera alors identique à un demi-additionneur. Si on envoie un 1 sur l'entrée de commande, l'inverseur commandable se comportera comme une porte NON. le circuit sera alors un demi-soustracteur.
===L'incrémenteur/décrémenteur à propagation de retenue===
Un décrémenteur à propagation de retenue se construit comme un incrémenteur, sauf qu'on remplace les demi-additionneurs par des demi-soustracteurs. En clair, on enchaine plusieurs demi-soustracteurs les uns à la suite des autres. Il est possible d'utiliser l'anticipation de retenue, mais les circuits sont cependant différents. Idem avec le ''carry skip'', et toute autre optimisation possible. La raison est que les retenues sont calculées différemment, ce qui fait que l’anticipation de retenue doit être adaptée.
Il est possible de fusionner un décrémenteur et un incrémenteur assez simplement. La seule différence étant une couche de portes NON qui inverse l'opérande, on peut remplacer ces portes NON par des inverseurs commandables. Les inverseurs commandables sont configurables pour servir soit de portes NON (mode décrémenteur), soit de portes OUI (mode incrémenteur). Il suffit pour cela d'utiliser plusieurs circuits précédents, et de commander tous les inverseurs commandables avec le même bit d'entrée. Ce bit sélectionne l'opération demandée : incrémentation ou décrémentation.
<noinclude>
{{NavChapitre | book=Fonctionnement d'un ordinateur
| prev=Les circuits de sélection
| prevText=Les circuits de sélection
| next=Les bascules : des mémoires de 1 bit
| nextText=Les bascules : des mémoires de 1 bit
}}
</noinclude>
2bvkm8ldrf2ikcwuamfykcn0a9fzei7
768785
768784
2026-06-27T13:54:31Z
Mewtow
31375
/* Les décrémenteurs */
768785
wikitext
text/x-wiki
Le circuit '''incrémenteur''' incrémente un nombre entier. Plus précisément, il prend en entrée un nombre entier, et fournit en entier le nombre suivant, il lui ajoute 1. Une telle opération peut paraitre triviale et peu intéressante. Cependant, elle est très utilisée et est absolument cruciale pour créer des compteurs, des circuits capables de compter ou décompter, qui auront droit à un chapitre dédié. De plus, au-delà de leur utilisation dans les compteurs, les incrémenteurs étaient très utilisés sur les premiers processeurs 8 bits, comme le Z-80, le 6502, les premiers processeurs x86 comme le 8008, le 8086, le 8085, et bien d'autres.
Il existe aussi des ''circuits décrémenteurs'', qui décrémentent un opérande, ainsi que des ''circuits incrémenteur-décrémenteur'', qui peuvent incrémenter ou décrémenter, selon comment on les configure. Je ne vais pas détailler ces circuits plus que ça, car de tels circuits sont assez rares, comparé à un circuit incrémenteur simple.
==Le demi-additionneur==
Le circuit incrémenteur effectue l'opération suivante :
<math>a_7</math> <math>a_6</math> <math>a_5</math> <math>a_4</math> <math>a_3</math> <math>a_2</math> <math>a_1</math> <math>a_0</math>
+ 0 0 0 0 0 0 0 1
Un incrémenteur basique doit donc faire une addition pour chaque colonne, et précisément une addition de deux bits. Il se trouve que la table d'addition est très simple en binaire. Jugez plutôt :
* 0 + 0 = 0, retenue = 0 ;
* 0 + 1 = 1, retenue = 0 ;
* 1 + 0 = 1, retenue = 0 ;
* 1 + 1 = 0, retenue = 1.
Un circuit capable d'additionner deux bits est appelé un '''demi-additionneur'''. Il dispose d'une sortie S pour la somme, et C pour la retenue (''carry'' an anglais).
===Le demi-additionneur classique===
Un demi-additionneur est très simple à construire avec les techniques vues dans les premiers chapitres. Voici sa table de vérité :
{|class="wikitable"
|-
! Retenue entrante !! Opérande 1 !! !! Retenue sortante !! Bit de somme
|-
||0||0|| ||0||0
|-
||0||1|| ||0||1
|-
||1||0|| ||0||1
|-
||1||1|| ||1||0
|}
On voit immédiatement que la colonne des retenues donne une porte ET, alors que celle du bit de somme est calculé par un XOR.
{| class="flexible"
|[[File:1-bit half-adder.svg|class=transparent|centre|Demi-addtionneur.]]
|[[File:Half-adder.svg|centre|class=transparent|Circuit d'un demi-addtionneur.]]
|}
Il existe beaucoup d'autres implémentations alternatives, qui utilisent moins de portes logiques, calculent la retenue plus rapidement, consomment moins d'énergie, et autres. Et il est intéressant d'étudier quelques alternatives, surtout qu'elles serviront plus bas, quand on étudiera le circuit incrémenteur du 8085.
===Le demi-additionneur basé sur une modification de la retenue===
Une source d'amélioration est liée à la porte XOR. En pratique, une porte XOR est composée en combinant plusieurs portes logiques ET/OU/NOR/NAND ensemble. Et il se trouve qu'il y a des redondances entre celles-ci et les portes utilisées pour calculer la retenue. Par exemple, rappelons qu'une porte XOR peut être construite avec une porte ET et deux portes NOR, comme illustré ci-dessous. Et il se trouve que la porte ET interne calcule la retenue sortante. En supprimant cette redondance, on économise quelques portes logiques. De plus, on se retrouve avec un demi-additionneur qui calcule le bit de somme à partir de la retenue sortante.
[[File:Demi-additionneur avec redondances éliminées.png|centre|vignette|upright=2|Demi-additionneur avec redondances éliminées]]
Le circuit précédent a une interprétation logique. Si vous regardez la table de vérité, vous remarquerez que la somme de deux bits est égale à l'inverse de la retenue sortante, sauf dans le cas où les deux bits additionnés valent zéro. Et le circuit précédent est basé là-dessus.
{|class="wikitable"
|-
! Retenue entrante !! Opérande 1 !! !! Retenue sortante !! Bit de somme
|- class="f_vert"
||0||0|| ||0||0
|- class="f_rouge"
||0||1|| ||0||1
|- class="f_rouge"
||1||0|| ||0||1
|- class="f_rouge"
||1||1|| ||1||0
|}
L'idée est de calculer l'inverse du bit de somme, avant de l'inverser avec une porte NON. L'inverse du bit de somme vaut 1, soit quand la retenue est à 1, soit quand les deux bits additionnés sont à 0. La porte ET calcule la retenue sortante, la première porte NOR détecte sur les deux bits d'entrée valent zéro. Une porte OU combine les deux résultat pour obtenir l'inverse du bit d'entrée, puis une porte NOn inverse le tout pour obtenir le bit de somme adéquat. Les deux portes sont fusionnées : c'est la seconde porte NOR.
[[File:Full adder HA + MUX.png|centre|vignette|upright=2.5|Demi-additionneur basé sur une inversion de la retenue sortante.]]
===Les implémentations alternatives===
Une implémentation alternative, qui n'utilise que des portes NOR, est la suivante :
[[File:Demi-additionneur fait avec des portes NOR et NAND.png|centre|vignette|upright=2|Demi-additionneur fait avec des portes NOR et NAND.]]
Une implémentation alternative utilise des portes NAND. Pour rappel, il est possible de créer une porte XOR avec 4 portes NAND. La première d'entre elle fait un NAND entre les deux bits d'entrée, ce qui fait qu'elle calcule l'inverse de la retenue sortante. Le tout est illustré ci-dessous. En théorie, on devrait utiliser une porte NON pour récupérer la retenue correcte. Mais nous verrons plus bas que ce n'est pas une obligation. Nous verrons plus bas un exemple où c'est l'inverse de la retenue qui est utilisée dans les calculs, pour rendre les calculs plus rapides.
[[File:Demi-additionneur fait avec des portes NAND.png|centre|vignette|upright=2|Demi-additionneur fait avec des portes NAND]]
==L'incrémenteur à propagation de retenue==
Maintenant que l'on sait comment additionner deux bits, reprenons l'opération d'incrémentation :
<math>a_7</math> <math>a_6</math> <math>a_5</math> <math>a_4</math> <math>a_3</math> <math>a_2</math> <math>a_1</math> <math>a_0</math>
+ 0 0 0 0 0 0 0 1
------------------------------
Sur la colonne la plus à droite, il doit ajouter un au bit de poids faible. Pour les colonnes suivantes, il faut additionner le bit de l'opérande avec la retenue de la colonne précédente. En clair, on n'additionne que deux bits à chaque colonne : un 1 sur celle tout à droite, la retenue de la colonne précédente pour les autres. Et cela nous donne une idée de comment faire pour créer le circuit incrémenteur.
Il suffit d'utiliser un demi-additionneur par colonne, et de les enchainer les uns à la suite des autres. Chaque demi-additionneur additionne le bit de l'opérande avec la retenue fournie par le demi-additionneur précédent. Le seul qui fait exception est celui pour la colonne de poids faible. Pour celui-là, il doit ajouter 1 au bit de poids faible. Le résultat est appelé un '''incrémenteur à propagation de retenue'''. Il est constitué de demi-additionneurs enchaînés les uns à la suite des autres, du bit de poids faible vers le bit de poids fort.
[[File:Circuit incrémenteur.png|centre|vignette|upright=3|Circuit incrémenteur.]]
Maintenant, regardons le demi-additionneur le plus à droite, celui pour le bit de poids faible. Son entrée de retenue entrante est mise à 1 pour faire l'incrémentation. Quelques incrémenteurs permettent de configurer cette entrée de retenue à 0 ou à 1, ce qui effectue : soit une opération identité (l'opérande est recopié sur la sortie), soit une incrémentation. Un tel circuit est nommé un '''incrémenteur commandable'''. Nous aurons à utiliser une fois ou deux de tels incrémenteurs commandables dans la suite du cours.
L'incrémenteur à propagation de retenue est le plus simple et le plus économe en portes logiques. Mais de tels incrémenteurs sont rarement utilisés. À la place, on leur préfère des incrémenteurs plus rapides, mais qui utilisent plus de portes logiques. De tels incrémenteurs accélèrent le calcul des retenues. En effet, la rapidité d'une incrémentation est limitée par la propagation de la retenue : les retenues commencent à être calculées au bit de poids fort et on doit les calculer une par une, jusqu’à atteindre le bit de poids fort. Et cette "propagation des retenues" prend du temps, d'autant plus de temps que l'opérande est longue. Il y a deux optimisations principales, appelées le ''carry skip'' et l'anticipation de retenue, que nous allons décrire ci-dessous.
==Les incrémenteurs ''carry skip''==
L'optimisation '''''carry skip''''' effectue l'incrémentation, non pas bit par bit, mais par paquets de deux bits. Le résultat est que l'incrémentation est deux fois plus rapide, ou presque. Le circuit incrémenteur est donc composé en enchainant non pas des demi-additionneurs, mais des '''incrémenteurs 2 bits''' qui incrémentent un opérande de deux bits.
[[File:Circuit incrémenteur optimisé, faisant l'incrémentation par paquet de deux bits.png|centre|vignette|upright=2.5|Circuit incrémenteur optimisé, faisant l'incrémentation par paquet de deux bits]]
Reste à concevoir l'incrémenteur 2 bits.
===Les optimisations au niveau des transistors et l'exemple de l'incrémenteur du 8085 d'Intel===
Une première solution part de deux demi-additionneurs et effectue quelques simplifications au niveau des transistors et des portes logiques. Par exemple, l'Intel 8085 a utilisé un circuit optimisé, fabriqué avec des portes NAND et NOR. La raison est qu'avec les technologies de transistors CMOS actuelles, les portes NAND et NOR utilisent moins de transistors que les portes ET et OU. Et cette possibilité a été utilisée pour effectuer des simplifications assez mineures, mais toujours bonnes à prendre. Mais nous ne pouvons pas en parler en détail ici, car nous n'avons pas encore parlé en détail des transistors. Cependant, un exemple bien précis nous est accesible : celui de l’incrémenteur du processeur 8085 d'Intel.
Le processeur 8085 d'Intel est un processeur 8 bits très ancien. Il contenait un incrémenteur 16 bits, qui était utilisé pour calculer des adresses mémoire. Et cet incrémenteur était un incrémenteur à propagation de retenue optimisé. Il utilisait lui aussi des incrémenteurs 2 bits un peu modifiés.
L'idée était que les retenues sortant des colonnes paires étaient inversées, les colonnes impaires faisaient les calculs à partir de cette retenue inversée. Les demi-additionneurs des colonnes impaires n'étaient donc pas les mêmes que ceux des colonnes paires. Leurs tables de vérité sont différentes, leurs circuits aussi, et aucun des deux ne ressemble à un demi-additionneur normal. En réalité, ils sont fabriqués à partir des implémentations vues plus haut, qui utilisent uniquement des portes NAND ou uniquement des portes NOR.
Pour comprendre l'intérêt de faire ainsi, nous devons préciser une chose importante : avec les technologies CMOS utilisées pour les processeurs depuis les années 70, les portes logiques les plus simples sont les portes NON, NOR et NAND. Les portes ET/OU sont fabriquées en combinant des portes NOR/NAND avec une porte NON, ce qui prend plus de transistors.
Un demi-additionneur est donc fabriqué comme illustré ci-dessous, en logique CMOS. Seules les portes pour le calcul de la retenue sont indiquées, la porte XOR pour le bit de somme est fabriquée à partir de plusieurs portes logiques.
[[File:Demi-additionneur en CMOS.png|centre|vignette|upright=2|Demi-additionneur en CMOS]]
Si on enchaine deux demi-additionneurs, cela donne ceci :
[[File:Brique de base de l'incrémenteur du 8085.png|centre|vignette|upright=2|Brique de base de l'incrémenteur du 8085]]
La retenue doit donc traverser quatre portes logiques, dont deux portes NON. En inversant la retenue, les portes en jaune dans le schéma précédent se simplifient. Il suffit d'utiliser la loi de de Morgan pour trouver un circuit équivalent aux portes jaunes, qui n'est autre qu'une porte NOR couplée à une porte NON. Un autre moyen d'obtenir le même résultat est de prendre un demi-additionneur fabriqué avec uniquement des portes NOR, et de retirer la porte NON adéquate. Il faut ensuite corriger l'entrée de retenue de la porte XOR, pour qu'elle fasse l'addition avec la retenue correctement. Le résultat est illustré ci-dessous.
[[File:Incrémenteur 2 bits de l'incrémenteur du 8085.png|centre|vignette|upright=2|Incrémenteur 2 bits de l'incrémenteur du 8085]]
Le circuit utilise le même nombre de portes logiques, il n'économise pas de transistors. Par contre, la retenue n'a plus qu'à traverser deux portes logiques, au lieu de quatre. La propagation de la retenue est donc plus rapide, l'incrémentation est donc plus rapide.
===Les incrémenteurs 2 bits ''carry skip''===
Une solution alternative fabrique des incrémenteurs 2 bits qui calculent séparément les deux bits du résultat et la retenue sortante. La retenue sortante est calculée à partir des bits de l’opérande, sans propager des retenues. Ainsi, la retenue sortante est connue en avance, sans passer par deux demi-additionneurs. Pour créer un tel incrémenteur, le mieux est de partir de sa table de vérité.
{|class="wikitable"
|-
! Opérande !! Retenue entrante !! !! Retenue sortante !! Résultat
|-
| 00 || 0 || || 0 || 00
|-
| 01 || 0 || || 0 || 01
|-
| 10 || 0 || || 0 || 10
|-
| 11 || 0 || || 0 || 11
|-
| colspan="5" |
|-
| 00 || 1 || || 0 || 01
|-
| 01 || 1 || || 0 || 10
|-
| 10 || 1 || || 0 || 11
|-
| 11 || 1 || || 1 || 00
|}
Elle nous dit que la retenue sortante ne vaut 1 que dans un seul cas : les deux bits d'opérande valent 1, la retenue entrante vaut 1. Elle se calcule alors avec une porte ET à trois entrées. Pour les deux bits du résultat, les équations logiques ne donnent pas un résultat satisfaisant. La solution la plus simple est d'utiliser deux demi-additionneurs et de retirer les portes logiques superflues. Le résultat est alors un '''incrémenteur 2 bits ''carry skip''''', où la retenue sortante est calculée séparément du reste.
[[File:Incrémenteur carry skip 2 bits.png|centre|vignette|upright=2.5|Incrémenteur ''carry skip'' 2 bits]]
==Les incrémenteurs à anticipation de retenue==
L''''anticipation de retenue''' calcule la retenue d'une colonne sans attendre les retenues des colonnes précédentes. Dans le cas idéal, toutes les retenues sont calculées en parallèle, en même temps, et sont ensuite envoyées aux demi-additionneurs. Il s'agit d'une optimisation qui est utilisée pour l'incrémentation, l'addition et d'autres opérations similaires. Pour l'incrémentation, déterminer la retenue ne demande pas de calculs complexes, contrairement à l'addition.
[[File:Incrémenteur à anticipation de retenues, 8 bits.png|centre|vignette|upright=2.5|Incrémenteur à anticipation de retenues, 8 bits.]]
Pour le comprendre, on peut regarder comment la retenue sortant d'un demi-additionneur est formée. Elle est calculée en faisant un ET logique entre la retenue entrante et le bit d'opérande. Une retenue est donc un ET logique entre toutes les retenues précédentes. Un incrémenteur à anticipation de retenue utilise donc une porte ET à plusieurs entrées pour calculer une retenue.
[[File:Anticipation de retenue pour un bit du résultat, incrémenteur.png|centre|vignette|upright=2.5|Anticipation de retenue pour un bit du résultat, incrémenteur]]
En théorie, on peut utiliser une porte ET à plusieurs entrées pour chaque bit de l'opérande. Cependant, cela entrainera un cout en transistors très important. Pour éviter de gaspiller trop de portes logiques, une solution est de mélanger anticipation de retenues et propagation de retenue. Par exemple, pour un incrémenteur 32 bits, on peut découper l'opérande en 4 octets : on anticipe les retenues pour chaque octet, mais l'incrémentation de chaque octet se fait avec propagation de retenue et/ou ''carry skip''.
[[File:Incrémenteur à anticipation de retenues.png|centre|vignette|upright=3|Incrémenteur hybride utilisant partiellement l'anticipation de retenues.]]
Quelques processeurs utilisaient l'anticipation de retenues. Par exemple, le processeur Z-80 de Zilog utilisait un incrémenteur pour des nombres de 16 bits, ce qui demandait des performances assez élevées. Et cet incrémenteur utilisait à la fois anticipation de retenues et ''carry skip''. Il était découpé en quatre blocs avant anticipation de retenues entre eux : un bloc regroupant les 7 bits de poids faible, suivi par un bloc de 5 bits, lui-même suivi par un bloc de 3 bits, terminé par un dernier bit isolé. A l'intérieur de ces blocs, les bits sont regroupés en paires utilisant le ''carry skip''. Pour ceux qui veulent en savoir plus sur cet incrémenteur, voici un lien sur le sujet :
* [https://www.righto.com/2013/11/the-z-80s-16-bit-incrementdecrement.html The Z-80's 16-bit increment/decrement circuit reverse engineered].
==Les décrémenteurs==
Les circuits décrémenteurs font l'inverse d'un incrémenteur : au lieu d'ajouter 1 à un opérande, ils lui soustraient 1. La différence n'est pas énorme, ce qui fait que les circuits incrémenteurs et décrémenteurs se ressemblent beaucoup. Là où les incrémenteurs sont fabriqués à partir de demi-additionneurs, les décrémenteurs sont bâtis avec des demi-soustracteurs.
La soustraction se fait en binaire comme en décimal. On soustrait deux chiffres, puis on doit propager une éventuelle retenue sur la colonne suivante. La retenue apparait quand le chiffre soustrait est plus grand que l'autre chiffre. Elle est propagée sur la colonne suivante, où elle doit être soustraite du résultat. Pour le dire autrement, après avoir soustrait un chiffre, on doit de plus soustraire la retenue de la colonne précédente.
La décrémentation effectue le calcul suivant :
<math>a_7</math> <math>a_6</math> <math>a_5</math> <math>a_4</math> <math>a_3</math> <math>a_2</math> <math>a_1</math> <math>a_0</math>
- 0 0 0 0 0 0 0 1
La colonne la plus à droite soustrait un 1. Par contre, les colonnes suivantes soustraient un zéro. Soustraire un zéro ne change rien. Mais il ne faut pas oublier de soustraire une éventuellement retenue, propagée depuis la colonne précédente. En clair, un décrémenteur peut se construire si on sait soustraire deux bits. On soustrait un 1 sur la colonne la plus à droite, on soustrait une retenue sur les autres colonnes. Et pour soustraire deux bits, il faut utiliser un demi-soustracteur.
===Le demi-soustracteur===
Un '''demi-soustracteur''' soustrait deux bits. Pour comprendre comment soustraire deux bits, traitons les quatre cas possibles un par un.
Premièrement, soustraire zéro à un bit ne changera rien :
* 0 - 0 = 0 ;
* 1 - 0 = 1.
Si on soustrait 1 à un bit qui vaut 1, on obtient zéro.
* 1 - 1 = 0.
Maintenant, que se passe-t-il si on soustrait 1 à 0 ? Voici le résultat :
* 0 - 1 = 1 et une retenue propagée sur la colonne suivante, où elle est soustraite.
La table de soustraction est donc la suivante :
* 0 - 0 = 0 ;
* 0 - 1 = 1 et une retenue ;
* 1 - 0 = 1 ;
* 1 - 1 = 0.
Si on construit la table de vérité, et qu'on construit le circuit associé, on tombe sur ce circuit. Détail important : le demi-soustracteur fait ici la soustraction X - Y. En anglais, X est appelé le ''Minuend'' et Y est appelé le ''subtrahend'', mais ces termes n'ont pas de traduction française.
[[File:Half Subtractor Vektor.svg|centre|vignette|Demi-soustracteur.]]
Un décrémenteur à propagation de retenue se construit comme un incrémenteur, sauf qu'on remplace les demi-additionneurs par des demi-soustracteurs. En clair, on enchaine plusieurs demi-soustracteurs les uns à la suite des autres. Il est possible d'utiliser l'anticipation de retenue, mais les circuits sont cependant différents de l'incrémenteur. Idem avec le ''carry skip'', et toute autre optimisation possible. La raison est que les retenues sont calculées différemment, ce qui fait que l’anticipation de retenue doit être adaptée.
===Les demi-additionneurs et demi-soustracteurs===
Vous l'aurez sans doute remarqué, mais le demi-soustracteur ressemble beaucoup au demi-additionneur. La seule différence est une porte NON ajoutée au bon endroit dans le demi-soustracteur. Vous aurez remarqué que la porte NON inverse l'opérande, mais seulement pour le calcul des retenues, le bit de somme est calculé normalement. Et attention : cette porte NON inverse le bit X, sachant que le demi-soustracteur fait la soustraction X - Y.
{|
|[[File:Half-adder.svg|vignette|Demi-additionneur]]
|[[File:Half Subtractor Vektor.svg|vignette|upright=1.3|Demi-soustracteur]]
|}
La conséquence est qu'il est possible de fusionner un demi-additionneur avec un demi-soustracteur. L'idée est de remplacer la porte NON du demi-soustracteur par un inverseur commandable. Si on envoie un zéro sur l'entrée de commande, l'inverseur commandable se comportera comme une porte OUI. Le circuit sera alors identique à un demi-additionneur. Si on envoie un 1 sur l'entrée de commande, l'inverseur commandable se comportera comme une porte NON. le circuit sera alors un demi-soustracteur.
En utilisant de tels circuits, on peut obtenir un circuit faisant à la fois incrémenteur et décrémenteur. Mais le circuit doit utiliser la propagation de retenue. Rappelons que l'anticipation de retenue se fait différemment entre incrémentation et décrémentation. Cependant, d'autres méthodes permettent d'utiliser un incrémenteur à anticipation de retenue sans trop de modification.
<noinclude>
{{NavChapitre | book=Fonctionnement d'un ordinateur
| prev=Les circuits de sélection
| prevText=Les circuits de sélection
| next=Les bascules : des mémoires de 1 bit
| nextText=Les bascules : des mémoires de 1 bit
}}
</noinclude>
axy5vlefhmlu12t5u2v2plcwf2pj7jv
768786
768785
2026-06-27T14:08:02Z
Mewtow
31375
/* Les demi-additionneurs et demi-soustracteurs */
768786
wikitext
text/x-wiki
Le circuit '''incrémenteur''' incrémente un nombre entier. Plus précisément, il prend en entrée un nombre entier, et fournit en entier le nombre suivant, il lui ajoute 1. Une telle opération peut paraitre triviale et peu intéressante. Cependant, elle est très utilisée et est absolument cruciale pour créer des compteurs, des circuits capables de compter ou décompter, qui auront droit à un chapitre dédié. De plus, au-delà de leur utilisation dans les compteurs, les incrémenteurs étaient très utilisés sur les premiers processeurs 8 bits, comme le Z-80, le 6502, les premiers processeurs x86 comme le 8008, le 8086, le 8085, et bien d'autres.
Il existe aussi des ''circuits décrémenteurs'', qui décrémentent un opérande, ainsi que des ''circuits incrémenteur-décrémenteur'', qui peuvent incrémenter ou décrémenter, selon comment on les configure. Je ne vais pas détailler ces circuits plus que ça, car de tels circuits sont assez rares, comparé à un circuit incrémenteur simple.
==Le demi-additionneur==
Le circuit incrémenteur effectue l'opération suivante :
<math>a_7</math> <math>a_6</math> <math>a_5</math> <math>a_4</math> <math>a_3</math> <math>a_2</math> <math>a_1</math> <math>a_0</math>
+ 0 0 0 0 0 0 0 1
Un incrémenteur basique doit donc faire une addition pour chaque colonne, et précisément une addition de deux bits. Il se trouve que la table d'addition est très simple en binaire. Jugez plutôt :
* 0 + 0 = 0, retenue = 0 ;
* 0 + 1 = 1, retenue = 0 ;
* 1 + 0 = 1, retenue = 0 ;
* 1 + 1 = 0, retenue = 1.
Un circuit capable d'additionner deux bits est appelé un '''demi-additionneur'''. Il dispose d'une sortie S pour la somme, et C pour la retenue (''carry'' an anglais).
===Le demi-additionneur classique===
Un demi-additionneur est très simple à construire avec les techniques vues dans les premiers chapitres. Voici sa table de vérité :
{|class="wikitable"
|-
! Retenue entrante !! Opérande 1 !! !! Retenue sortante !! Bit de somme
|-
||0||0|| ||0||0
|-
||0||1|| ||0||1
|-
||1||0|| ||0||1
|-
||1||1|| ||1||0
|}
On voit immédiatement que la colonne des retenues donne une porte ET, alors que celle du bit de somme est calculé par un XOR.
{| class="flexible"
|[[File:1-bit half-adder.svg|class=transparent|centre|Demi-addtionneur.]]
|[[File:Half-adder.svg|centre|class=transparent|Circuit d'un demi-addtionneur.]]
|}
Il existe beaucoup d'autres implémentations alternatives, qui utilisent moins de portes logiques, calculent la retenue plus rapidement, consomment moins d'énergie, et autres. Et il est intéressant d'étudier quelques alternatives, surtout qu'elles serviront plus bas, quand on étudiera le circuit incrémenteur du 8085.
===Le demi-additionneur basé sur une modification de la retenue===
Une source d'amélioration est liée à la porte XOR. En pratique, une porte XOR est composée en combinant plusieurs portes logiques ET/OU/NOR/NAND ensemble. Et il se trouve qu'il y a des redondances entre celles-ci et les portes utilisées pour calculer la retenue. Par exemple, rappelons qu'une porte XOR peut être construite avec une porte ET et deux portes NOR, comme illustré ci-dessous. Et il se trouve que la porte ET interne calcule la retenue sortante. En supprimant cette redondance, on économise quelques portes logiques. De plus, on se retrouve avec un demi-additionneur qui calcule le bit de somme à partir de la retenue sortante.
[[File:Demi-additionneur avec redondances éliminées.png|centre|vignette|upright=2|Demi-additionneur avec redondances éliminées]]
Le circuit précédent a une interprétation logique. Si vous regardez la table de vérité, vous remarquerez que la somme de deux bits est égale à l'inverse de la retenue sortante, sauf dans le cas où les deux bits additionnés valent zéro. Et le circuit précédent est basé là-dessus.
{|class="wikitable"
|-
! Retenue entrante !! Opérande 1 !! !! Retenue sortante !! Bit de somme
|- class="f_vert"
||0||0|| ||0||0
|- class="f_rouge"
||0||1|| ||0||1
|- class="f_rouge"
||1||0|| ||0||1
|- class="f_rouge"
||1||1|| ||1||0
|}
L'idée est de calculer l'inverse du bit de somme, avant de l'inverser avec une porte NON. L'inverse du bit de somme vaut 1, soit quand la retenue est à 1, soit quand les deux bits additionnés sont à 0. La porte ET calcule la retenue sortante, la première porte NOR détecte sur les deux bits d'entrée valent zéro. Une porte OU combine les deux résultat pour obtenir l'inverse du bit d'entrée, puis une porte NOn inverse le tout pour obtenir le bit de somme adéquat. Les deux portes sont fusionnées : c'est la seconde porte NOR.
[[File:Full adder HA + MUX.png|centre|vignette|upright=2.5|Demi-additionneur basé sur une inversion de la retenue sortante.]]
===Les implémentations alternatives===
Une implémentation alternative, qui n'utilise que des portes NOR, est la suivante :
[[File:Demi-additionneur fait avec des portes NOR et NAND.png|centre|vignette|upright=2|Demi-additionneur fait avec des portes NOR et NAND.]]
Une implémentation alternative utilise des portes NAND. Pour rappel, il est possible de créer une porte XOR avec 4 portes NAND. La première d'entre elle fait un NAND entre les deux bits d'entrée, ce qui fait qu'elle calcule l'inverse de la retenue sortante. Le tout est illustré ci-dessous. En théorie, on devrait utiliser une porte NON pour récupérer la retenue correcte. Mais nous verrons plus bas que ce n'est pas une obligation. Nous verrons plus bas un exemple où c'est l'inverse de la retenue qui est utilisée dans les calculs, pour rendre les calculs plus rapides.
[[File:Demi-additionneur fait avec des portes NAND.png|centre|vignette|upright=2|Demi-additionneur fait avec des portes NAND]]
==L'incrémenteur à propagation de retenue==
Maintenant que l'on sait comment additionner deux bits, reprenons l'opération d'incrémentation :
<math>a_7</math> <math>a_6</math> <math>a_5</math> <math>a_4</math> <math>a_3</math> <math>a_2</math> <math>a_1</math> <math>a_0</math>
+ 0 0 0 0 0 0 0 1
------------------------------
Sur la colonne la plus à droite, il doit ajouter un au bit de poids faible. Pour les colonnes suivantes, il faut additionner le bit de l'opérande avec la retenue de la colonne précédente. En clair, on n'additionne que deux bits à chaque colonne : un 1 sur celle tout à droite, la retenue de la colonne précédente pour les autres. Et cela nous donne une idée de comment faire pour créer le circuit incrémenteur.
Il suffit d'utiliser un demi-additionneur par colonne, et de les enchainer les uns à la suite des autres. Chaque demi-additionneur additionne le bit de l'opérande avec la retenue fournie par le demi-additionneur précédent. Le seul qui fait exception est celui pour la colonne de poids faible. Pour celui-là, il doit ajouter 1 au bit de poids faible. Le résultat est appelé un '''incrémenteur à propagation de retenue'''. Il est constitué de demi-additionneurs enchaînés les uns à la suite des autres, du bit de poids faible vers le bit de poids fort.
[[File:Circuit incrémenteur.png|centre|vignette|upright=3|Circuit incrémenteur.]]
Maintenant, regardons le demi-additionneur le plus à droite, celui pour le bit de poids faible. Son entrée de retenue entrante est mise à 1 pour faire l'incrémentation. Quelques incrémenteurs permettent de configurer cette entrée de retenue à 0 ou à 1, ce qui effectue : soit une opération identité (l'opérande est recopié sur la sortie), soit une incrémentation. Un tel circuit est nommé un '''incrémenteur commandable'''. Nous aurons à utiliser une fois ou deux de tels incrémenteurs commandables dans la suite du cours.
L'incrémenteur à propagation de retenue est le plus simple et le plus économe en portes logiques. Mais de tels incrémenteurs sont rarement utilisés. À la place, on leur préfère des incrémenteurs plus rapides, mais qui utilisent plus de portes logiques. De tels incrémenteurs accélèrent le calcul des retenues. En effet, la rapidité d'une incrémentation est limitée par la propagation de la retenue : les retenues commencent à être calculées au bit de poids fort et on doit les calculer une par une, jusqu’à atteindre le bit de poids fort. Et cette "propagation des retenues" prend du temps, d'autant plus de temps que l'opérande est longue. Il y a deux optimisations principales, appelées le ''carry skip'' et l'anticipation de retenue, que nous allons décrire ci-dessous.
==Les incrémenteurs ''carry skip''==
L'optimisation '''''carry skip''''' effectue l'incrémentation, non pas bit par bit, mais par paquets de deux bits. Le résultat est que l'incrémentation est deux fois plus rapide, ou presque. Le circuit incrémenteur est donc composé en enchainant non pas des demi-additionneurs, mais des '''incrémenteurs 2 bits''' qui incrémentent un opérande de deux bits.
[[File:Circuit incrémenteur optimisé, faisant l'incrémentation par paquet de deux bits.png|centre|vignette|upright=2.5|Circuit incrémenteur optimisé, faisant l'incrémentation par paquet de deux bits]]
Reste à concevoir l'incrémenteur 2 bits.
===Les optimisations au niveau des transistors et l'exemple de l'incrémenteur du 8085 d'Intel===
Une première solution part de deux demi-additionneurs et effectue quelques simplifications au niveau des transistors et des portes logiques. Par exemple, l'Intel 8085 a utilisé un circuit optimisé, fabriqué avec des portes NAND et NOR. La raison est qu'avec les technologies de transistors CMOS actuelles, les portes NAND et NOR utilisent moins de transistors que les portes ET et OU. Et cette possibilité a été utilisée pour effectuer des simplifications assez mineures, mais toujours bonnes à prendre. Mais nous ne pouvons pas en parler en détail ici, car nous n'avons pas encore parlé en détail des transistors. Cependant, un exemple bien précis nous est accesible : celui de l’incrémenteur du processeur 8085 d'Intel.
Le processeur 8085 d'Intel est un processeur 8 bits très ancien. Il contenait un incrémenteur 16 bits, qui était utilisé pour calculer des adresses mémoire. Et cet incrémenteur était un incrémenteur à propagation de retenue optimisé. Il utilisait lui aussi des incrémenteurs 2 bits un peu modifiés.
L'idée était que les retenues sortant des colonnes paires étaient inversées, les colonnes impaires faisaient les calculs à partir de cette retenue inversée. Les demi-additionneurs des colonnes impaires n'étaient donc pas les mêmes que ceux des colonnes paires. Leurs tables de vérité sont différentes, leurs circuits aussi, et aucun des deux ne ressemble à un demi-additionneur normal. En réalité, ils sont fabriqués à partir des implémentations vues plus haut, qui utilisent uniquement des portes NAND ou uniquement des portes NOR.
Pour comprendre l'intérêt de faire ainsi, nous devons préciser une chose importante : avec les technologies CMOS utilisées pour les processeurs depuis les années 70, les portes logiques les plus simples sont les portes NON, NOR et NAND. Les portes ET/OU sont fabriquées en combinant des portes NOR/NAND avec une porte NON, ce qui prend plus de transistors.
Un demi-additionneur est donc fabriqué comme illustré ci-dessous, en logique CMOS. Seules les portes pour le calcul de la retenue sont indiquées, la porte XOR pour le bit de somme est fabriquée à partir de plusieurs portes logiques.
[[File:Demi-additionneur en CMOS.png|centre|vignette|upright=2|Demi-additionneur en CMOS]]
Si on enchaine deux demi-additionneurs, cela donne ceci :
[[File:Brique de base de l'incrémenteur du 8085.png|centre|vignette|upright=2|Brique de base de l'incrémenteur du 8085]]
La retenue doit donc traverser quatre portes logiques, dont deux portes NON. En inversant la retenue, les portes en jaune dans le schéma précédent se simplifient. Il suffit d'utiliser la loi de de Morgan pour trouver un circuit équivalent aux portes jaunes, qui n'est autre qu'une porte NOR couplée à une porte NON. Un autre moyen d'obtenir le même résultat est de prendre un demi-additionneur fabriqué avec uniquement des portes NOR, et de retirer la porte NON adéquate. Il faut ensuite corriger l'entrée de retenue de la porte XOR, pour qu'elle fasse l'addition avec la retenue correctement. Le résultat est illustré ci-dessous.
[[File:Incrémenteur 2 bits de l'incrémenteur du 8085.png|centre|vignette|upright=2|Incrémenteur 2 bits de l'incrémenteur du 8085]]
Le circuit utilise le même nombre de portes logiques, il n'économise pas de transistors. Par contre, la retenue n'a plus qu'à traverser deux portes logiques, au lieu de quatre. La propagation de la retenue est donc plus rapide, l'incrémentation est donc plus rapide.
===Les incrémenteurs 2 bits ''carry skip''===
Une solution alternative fabrique des incrémenteurs 2 bits qui calculent séparément les deux bits du résultat et la retenue sortante. La retenue sortante est calculée à partir des bits de l’opérande, sans propager des retenues. Ainsi, la retenue sortante est connue en avance, sans passer par deux demi-additionneurs. Pour créer un tel incrémenteur, le mieux est de partir de sa table de vérité.
{|class="wikitable"
|-
! Opérande !! Retenue entrante !! !! Retenue sortante !! Résultat
|-
| 00 || 0 || || 0 || 00
|-
| 01 || 0 || || 0 || 01
|-
| 10 || 0 || || 0 || 10
|-
| 11 || 0 || || 0 || 11
|-
| colspan="5" |
|-
| 00 || 1 || || 0 || 01
|-
| 01 || 1 || || 0 || 10
|-
| 10 || 1 || || 0 || 11
|-
| 11 || 1 || || 1 || 00
|}
Elle nous dit que la retenue sortante ne vaut 1 que dans un seul cas : les deux bits d'opérande valent 1, la retenue entrante vaut 1. Elle se calcule alors avec une porte ET à trois entrées. Pour les deux bits du résultat, les équations logiques ne donnent pas un résultat satisfaisant. La solution la plus simple est d'utiliser deux demi-additionneurs et de retirer les portes logiques superflues. Le résultat est alors un '''incrémenteur 2 bits ''carry skip''''', où la retenue sortante est calculée séparément du reste.
[[File:Incrémenteur carry skip 2 bits.png|centre|vignette|upright=2.5|Incrémenteur ''carry skip'' 2 bits]]
==Les incrémenteurs à anticipation de retenue==
L''''anticipation de retenue''' calcule la retenue d'une colonne sans attendre les retenues des colonnes précédentes. Dans le cas idéal, toutes les retenues sont calculées en parallèle, en même temps, et sont ensuite envoyées aux demi-additionneurs. Il s'agit d'une optimisation qui est utilisée pour l'incrémentation, l'addition et d'autres opérations similaires. Pour l'incrémentation, déterminer la retenue ne demande pas de calculs complexes, contrairement à l'addition.
[[File:Incrémenteur à anticipation de retenues, 8 bits.png|centre|vignette|upright=2.5|Incrémenteur à anticipation de retenues, 8 bits.]]
Pour le comprendre, on peut regarder comment la retenue sortant d'un demi-additionneur est formée. Elle est calculée en faisant un ET logique entre la retenue entrante et le bit d'opérande. Une retenue est donc un ET logique entre toutes les retenues précédentes. Un incrémenteur à anticipation de retenue utilise donc une porte ET à plusieurs entrées pour calculer une retenue.
[[File:Anticipation de retenue pour un bit du résultat, incrémenteur.png|centre|vignette|upright=2.5|Anticipation de retenue pour un bit du résultat, incrémenteur]]
En théorie, on peut utiliser une porte ET à plusieurs entrées pour chaque bit de l'opérande. Cependant, cela entrainera un cout en transistors très important. Pour éviter de gaspiller trop de portes logiques, une solution est de mélanger anticipation de retenues et propagation de retenue. Par exemple, pour un incrémenteur 32 bits, on peut découper l'opérande en 4 octets : on anticipe les retenues pour chaque octet, mais l'incrémentation de chaque octet se fait avec propagation de retenue et/ou ''carry skip''.
[[File:Incrémenteur à anticipation de retenues.png|centre|vignette|upright=3|Incrémenteur hybride utilisant partiellement l'anticipation de retenues.]]
Quelques processeurs utilisaient l'anticipation de retenues. Par exemple, le processeur Z-80 de Zilog utilisait un incrémenteur pour des nombres de 16 bits, ce qui demandait des performances assez élevées. Et cet incrémenteur utilisait à la fois anticipation de retenues et ''carry skip''. Il était découpé en quatre blocs avant anticipation de retenues entre eux : un bloc regroupant les 7 bits de poids faible, suivi par un bloc de 5 bits, lui-même suivi par un bloc de 3 bits, terminé par un dernier bit isolé. A l'intérieur de ces blocs, les bits sont regroupés en paires utilisant le ''carry skip''. Pour ceux qui veulent en savoir plus sur cet incrémenteur, voici un lien sur le sujet :
* [https://www.righto.com/2013/11/the-z-80s-16-bit-incrementdecrement.html The Z-80's 16-bit increment/decrement circuit reverse engineered].
==Les décrémenteurs==
Les circuits décrémenteurs font l'inverse d'un incrémenteur : au lieu d'ajouter 1 à un opérande, ils lui soustraient 1. La différence n'est pas énorme, ce qui fait que les circuits incrémenteurs et décrémenteurs se ressemblent beaucoup. Là où les incrémenteurs sont fabriqués à partir de demi-additionneurs, les décrémenteurs sont bâtis avec des demi-soustracteurs.
La soustraction se fait en binaire comme en décimal. On soustrait deux chiffres, puis on doit propager une éventuelle retenue sur la colonne suivante. La retenue apparait quand le chiffre soustrait est plus grand que l'autre chiffre. Elle est propagée sur la colonne suivante, où elle doit être soustraite du résultat. Pour le dire autrement, après avoir soustrait un chiffre, on doit de plus soustraire la retenue de la colonne précédente.
La décrémentation effectue le calcul suivant :
<math>a_7</math> <math>a_6</math> <math>a_5</math> <math>a_4</math> <math>a_3</math> <math>a_2</math> <math>a_1</math> <math>a_0</math>
- 0 0 0 0 0 0 0 1
La colonne la plus à droite soustrait un 1. Par contre, les colonnes suivantes soustraient un zéro. Soustraire un zéro ne change rien. Mais il ne faut pas oublier de soustraire une éventuellement retenue, propagée depuis la colonne précédente. En clair, un décrémenteur peut se construire si on sait soustraire deux bits. On soustrait un 1 sur la colonne la plus à droite, on soustrait une retenue sur les autres colonnes. Et pour soustraire deux bits, il faut utiliser un demi-soustracteur.
===Le demi-soustracteur===
Un '''demi-soustracteur''' soustrait deux bits. Pour comprendre comment soustraire deux bits, traitons les quatre cas possibles un par un.
Premièrement, soustraire zéro à un bit ne changera rien :
* 0 - 0 = 0 ;
* 1 - 0 = 1.
Si on soustrait 1 à un bit qui vaut 1, on obtient zéro.
* 1 - 1 = 0.
Maintenant, que se passe-t-il si on soustrait 1 à 0 ? Voici le résultat :
* 0 - 1 = 1 et une retenue propagée sur la colonne suivante, où elle est soustraite.
La table de soustraction est donc la suivante :
* 0 - 0 = 0 ;
* 0 - 1 = 1 et une retenue ;
* 1 - 0 = 1 ;
* 1 - 1 = 0.
Si on construit la table de vérité, et qu'on construit le circuit associé, on tombe sur ce circuit. Détail important : le demi-soustracteur fait ici la soustraction X - Y. En anglais, X est appelé le ''Minuend'' et Y est appelé le ''subtrahend'', mais ces termes n'ont pas de traduction française.
[[File:Half Subtractor Vektor.svg|centre|vignette|Demi-soustracteur.]]
Un décrémenteur à propagation de retenue se construit comme un incrémenteur, sauf qu'on remplace les demi-additionneurs par des demi-soustracteurs. En clair, on enchaine plusieurs demi-soustracteurs les uns à la suite des autres. Il est possible d'utiliser l'anticipation de retenue, mais les circuits sont cependant différents de l'incrémenteur. Idem avec le ''carry skip'', et toute autre optimisation possible. La raison est que les retenues sont calculées différemment, ce qui fait que l’anticipation de retenue doit être adaptée.
===Les demi-additionneurs et demi-soustracteurs===
Vous l'aurez sans doute remarqué, mais le demi-soustracteur ressemble beaucoup au demi-additionneur. La seule différence est une porte NON ajoutée au bon endroit dans le demi-soustracteur. La porte NON inverse le bit X, sachant que le demi-soustracteur fait la soustraction X - Y. De plus, l'opérande inversée est utilisée seulement pour le calcul des retenues, le bit de somme est calculé normalement.
{|
|[[File:Half-adder.svg|vignette|Demi-additionneur]]
|[[File:Half Subtractor Vektor.svg|vignette|upright=1.3|Demi-soustracteur]]
|}
La conséquence est qu'il est possible de fusionner un demi-additionneur avec un demi-soustracteur. L'idée est de remplacer la porte NON du demi-soustracteur par un inverseur commandable. Si on envoie un zéro sur l'entrée de commande, l'inverseur commandable se comportera comme une porte OUI et le circuit sera alors identique à un demi-additionneur. Si on envoie un 1 sur l'entrée de commande, l'inverseur commandable se comportera comme une porte NON. le circuit sera alors un demi-soustracteur. En remplaçant les demi-additionneurs (ou demi-soustracteurs) par de tels circuits, on peut obtenir un circuit faisant à la fois incrémenteur et décrémenteur.
Une implémentation alternative se base sur les propriétés du complément à deux, et plus précisément sur cette équation :
: <math>\overline{x} + 1 = \overline{x - 1}
Les deux termes de cette équation sont simplement des définitions équivalentes du complément à deux d'un nombre entier. Celui-ci se calcule de deux manières : soit on incrémente son complément à 1, soit on prend le complément à 1 de son décrément. Et cela nous permet de déterminer comment obtenir une incrémentation à partir d'une décrémentation et inversement.
Que nous dit cette équation ? Simplement que l'on peut décrémenter un nombre si on a deux inverseurs commandables, et un circuit incrémenteur. La décrémentation se fait alors en deux étapes : on calcule son complément à deux, puis on inverse les bits du résultat précédent. Le calcul du complément à deux demande d'avoir un circuit inverseur, et un incrémenteur.
<noinclude>
{{NavChapitre | book=Fonctionnement d'un ordinateur
| prev=Les circuits de sélection
| prevText=Les circuits de sélection
| next=Les bascules : des mémoires de 1 bit
| nextText=Les bascules : des mémoires de 1 bit
}}
</noinclude>
i5gaoe6ovzaxbm2mjjuqxite5pgq4yg
768787
768786
2026-06-27T14:08:11Z
Mewtow
31375
/* Les demi-additionneurs et demi-soustracteurs */
768787
wikitext
text/x-wiki
Le circuit '''incrémenteur''' incrémente un nombre entier. Plus précisément, il prend en entrée un nombre entier, et fournit en entier le nombre suivant, il lui ajoute 1. Une telle opération peut paraitre triviale et peu intéressante. Cependant, elle est très utilisée et est absolument cruciale pour créer des compteurs, des circuits capables de compter ou décompter, qui auront droit à un chapitre dédié. De plus, au-delà de leur utilisation dans les compteurs, les incrémenteurs étaient très utilisés sur les premiers processeurs 8 bits, comme le Z-80, le 6502, les premiers processeurs x86 comme le 8008, le 8086, le 8085, et bien d'autres.
Il existe aussi des ''circuits décrémenteurs'', qui décrémentent un opérande, ainsi que des ''circuits incrémenteur-décrémenteur'', qui peuvent incrémenter ou décrémenter, selon comment on les configure. Je ne vais pas détailler ces circuits plus que ça, car de tels circuits sont assez rares, comparé à un circuit incrémenteur simple.
==Le demi-additionneur==
Le circuit incrémenteur effectue l'opération suivante :
<math>a_7</math> <math>a_6</math> <math>a_5</math> <math>a_4</math> <math>a_3</math> <math>a_2</math> <math>a_1</math> <math>a_0</math>
+ 0 0 0 0 0 0 0 1
Un incrémenteur basique doit donc faire une addition pour chaque colonne, et précisément une addition de deux bits. Il se trouve que la table d'addition est très simple en binaire. Jugez plutôt :
* 0 + 0 = 0, retenue = 0 ;
* 0 + 1 = 1, retenue = 0 ;
* 1 + 0 = 1, retenue = 0 ;
* 1 + 1 = 0, retenue = 1.
Un circuit capable d'additionner deux bits est appelé un '''demi-additionneur'''. Il dispose d'une sortie S pour la somme, et C pour la retenue (''carry'' an anglais).
===Le demi-additionneur classique===
Un demi-additionneur est très simple à construire avec les techniques vues dans les premiers chapitres. Voici sa table de vérité :
{|class="wikitable"
|-
! Retenue entrante !! Opérande 1 !! !! Retenue sortante !! Bit de somme
|-
||0||0|| ||0||0
|-
||0||1|| ||0||1
|-
||1||0|| ||0||1
|-
||1||1|| ||1||0
|}
On voit immédiatement que la colonne des retenues donne une porte ET, alors que celle du bit de somme est calculé par un XOR.
{| class="flexible"
|[[File:1-bit half-adder.svg|class=transparent|centre|Demi-addtionneur.]]
|[[File:Half-adder.svg|centre|class=transparent|Circuit d'un demi-addtionneur.]]
|}
Il existe beaucoup d'autres implémentations alternatives, qui utilisent moins de portes logiques, calculent la retenue plus rapidement, consomment moins d'énergie, et autres. Et il est intéressant d'étudier quelques alternatives, surtout qu'elles serviront plus bas, quand on étudiera le circuit incrémenteur du 8085.
===Le demi-additionneur basé sur une modification de la retenue===
Une source d'amélioration est liée à la porte XOR. En pratique, une porte XOR est composée en combinant plusieurs portes logiques ET/OU/NOR/NAND ensemble. Et il se trouve qu'il y a des redondances entre celles-ci et les portes utilisées pour calculer la retenue. Par exemple, rappelons qu'une porte XOR peut être construite avec une porte ET et deux portes NOR, comme illustré ci-dessous. Et il se trouve que la porte ET interne calcule la retenue sortante. En supprimant cette redondance, on économise quelques portes logiques. De plus, on se retrouve avec un demi-additionneur qui calcule le bit de somme à partir de la retenue sortante.
[[File:Demi-additionneur avec redondances éliminées.png|centre|vignette|upright=2|Demi-additionneur avec redondances éliminées]]
Le circuit précédent a une interprétation logique. Si vous regardez la table de vérité, vous remarquerez que la somme de deux bits est égale à l'inverse de la retenue sortante, sauf dans le cas où les deux bits additionnés valent zéro. Et le circuit précédent est basé là-dessus.
{|class="wikitable"
|-
! Retenue entrante !! Opérande 1 !! !! Retenue sortante !! Bit de somme
|- class="f_vert"
||0||0|| ||0||0
|- class="f_rouge"
||0||1|| ||0||1
|- class="f_rouge"
||1||0|| ||0||1
|- class="f_rouge"
||1||1|| ||1||0
|}
L'idée est de calculer l'inverse du bit de somme, avant de l'inverser avec une porte NON. L'inverse du bit de somme vaut 1, soit quand la retenue est à 1, soit quand les deux bits additionnés sont à 0. La porte ET calcule la retenue sortante, la première porte NOR détecte sur les deux bits d'entrée valent zéro. Une porte OU combine les deux résultat pour obtenir l'inverse du bit d'entrée, puis une porte NOn inverse le tout pour obtenir le bit de somme adéquat. Les deux portes sont fusionnées : c'est la seconde porte NOR.
[[File:Full adder HA + MUX.png|centre|vignette|upright=2.5|Demi-additionneur basé sur une inversion de la retenue sortante.]]
===Les implémentations alternatives===
Une implémentation alternative, qui n'utilise que des portes NOR, est la suivante :
[[File:Demi-additionneur fait avec des portes NOR et NAND.png|centre|vignette|upright=2|Demi-additionneur fait avec des portes NOR et NAND.]]
Une implémentation alternative utilise des portes NAND. Pour rappel, il est possible de créer une porte XOR avec 4 portes NAND. La première d'entre elle fait un NAND entre les deux bits d'entrée, ce qui fait qu'elle calcule l'inverse de la retenue sortante. Le tout est illustré ci-dessous. En théorie, on devrait utiliser une porte NON pour récupérer la retenue correcte. Mais nous verrons plus bas que ce n'est pas une obligation. Nous verrons plus bas un exemple où c'est l'inverse de la retenue qui est utilisée dans les calculs, pour rendre les calculs plus rapides.
[[File:Demi-additionneur fait avec des portes NAND.png|centre|vignette|upright=2|Demi-additionneur fait avec des portes NAND]]
==L'incrémenteur à propagation de retenue==
Maintenant que l'on sait comment additionner deux bits, reprenons l'opération d'incrémentation :
<math>a_7</math> <math>a_6</math> <math>a_5</math> <math>a_4</math> <math>a_3</math> <math>a_2</math> <math>a_1</math> <math>a_0</math>
+ 0 0 0 0 0 0 0 1
------------------------------
Sur la colonne la plus à droite, il doit ajouter un au bit de poids faible. Pour les colonnes suivantes, il faut additionner le bit de l'opérande avec la retenue de la colonne précédente. En clair, on n'additionne que deux bits à chaque colonne : un 1 sur celle tout à droite, la retenue de la colonne précédente pour les autres. Et cela nous donne une idée de comment faire pour créer le circuit incrémenteur.
Il suffit d'utiliser un demi-additionneur par colonne, et de les enchainer les uns à la suite des autres. Chaque demi-additionneur additionne le bit de l'opérande avec la retenue fournie par le demi-additionneur précédent. Le seul qui fait exception est celui pour la colonne de poids faible. Pour celui-là, il doit ajouter 1 au bit de poids faible. Le résultat est appelé un '''incrémenteur à propagation de retenue'''. Il est constitué de demi-additionneurs enchaînés les uns à la suite des autres, du bit de poids faible vers le bit de poids fort.
[[File:Circuit incrémenteur.png|centre|vignette|upright=3|Circuit incrémenteur.]]
Maintenant, regardons le demi-additionneur le plus à droite, celui pour le bit de poids faible. Son entrée de retenue entrante est mise à 1 pour faire l'incrémentation. Quelques incrémenteurs permettent de configurer cette entrée de retenue à 0 ou à 1, ce qui effectue : soit une opération identité (l'opérande est recopié sur la sortie), soit une incrémentation. Un tel circuit est nommé un '''incrémenteur commandable'''. Nous aurons à utiliser une fois ou deux de tels incrémenteurs commandables dans la suite du cours.
L'incrémenteur à propagation de retenue est le plus simple et le plus économe en portes logiques. Mais de tels incrémenteurs sont rarement utilisés. À la place, on leur préfère des incrémenteurs plus rapides, mais qui utilisent plus de portes logiques. De tels incrémenteurs accélèrent le calcul des retenues. En effet, la rapidité d'une incrémentation est limitée par la propagation de la retenue : les retenues commencent à être calculées au bit de poids fort et on doit les calculer une par une, jusqu’à atteindre le bit de poids fort. Et cette "propagation des retenues" prend du temps, d'autant plus de temps que l'opérande est longue. Il y a deux optimisations principales, appelées le ''carry skip'' et l'anticipation de retenue, que nous allons décrire ci-dessous.
==Les incrémenteurs ''carry skip''==
L'optimisation '''''carry skip''''' effectue l'incrémentation, non pas bit par bit, mais par paquets de deux bits. Le résultat est que l'incrémentation est deux fois plus rapide, ou presque. Le circuit incrémenteur est donc composé en enchainant non pas des demi-additionneurs, mais des '''incrémenteurs 2 bits''' qui incrémentent un opérande de deux bits.
[[File:Circuit incrémenteur optimisé, faisant l'incrémentation par paquet de deux bits.png|centre|vignette|upright=2.5|Circuit incrémenteur optimisé, faisant l'incrémentation par paquet de deux bits]]
Reste à concevoir l'incrémenteur 2 bits.
===Les optimisations au niveau des transistors et l'exemple de l'incrémenteur du 8085 d'Intel===
Une première solution part de deux demi-additionneurs et effectue quelques simplifications au niveau des transistors et des portes logiques. Par exemple, l'Intel 8085 a utilisé un circuit optimisé, fabriqué avec des portes NAND et NOR. La raison est qu'avec les technologies de transistors CMOS actuelles, les portes NAND et NOR utilisent moins de transistors que les portes ET et OU. Et cette possibilité a été utilisée pour effectuer des simplifications assez mineures, mais toujours bonnes à prendre. Mais nous ne pouvons pas en parler en détail ici, car nous n'avons pas encore parlé en détail des transistors. Cependant, un exemple bien précis nous est accesible : celui de l’incrémenteur du processeur 8085 d'Intel.
Le processeur 8085 d'Intel est un processeur 8 bits très ancien. Il contenait un incrémenteur 16 bits, qui était utilisé pour calculer des adresses mémoire. Et cet incrémenteur était un incrémenteur à propagation de retenue optimisé. Il utilisait lui aussi des incrémenteurs 2 bits un peu modifiés.
L'idée était que les retenues sortant des colonnes paires étaient inversées, les colonnes impaires faisaient les calculs à partir de cette retenue inversée. Les demi-additionneurs des colonnes impaires n'étaient donc pas les mêmes que ceux des colonnes paires. Leurs tables de vérité sont différentes, leurs circuits aussi, et aucun des deux ne ressemble à un demi-additionneur normal. En réalité, ils sont fabriqués à partir des implémentations vues plus haut, qui utilisent uniquement des portes NAND ou uniquement des portes NOR.
Pour comprendre l'intérêt de faire ainsi, nous devons préciser une chose importante : avec les technologies CMOS utilisées pour les processeurs depuis les années 70, les portes logiques les plus simples sont les portes NON, NOR et NAND. Les portes ET/OU sont fabriquées en combinant des portes NOR/NAND avec une porte NON, ce qui prend plus de transistors.
Un demi-additionneur est donc fabriqué comme illustré ci-dessous, en logique CMOS. Seules les portes pour le calcul de la retenue sont indiquées, la porte XOR pour le bit de somme est fabriquée à partir de plusieurs portes logiques.
[[File:Demi-additionneur en CMOS.png|centre|vignette|upright=2|Demi-additionneur en CMOS]]
Si on enchaine deux demi-additionneurs, cela donne ceci :
[[File:Brique de base de l'incrémenteur du 8085.png|centre|vignette|upright=2|Brique de base de l'incrémenteur du 8085]]
La retenue doit donc traverser quatre portes logiques, dont deux portes NON. En inversant la retenue, les portes en jaune dans le schéma précédent se simplifient. Il suffit d'utiliser la loi de de Morgan pour trouver un circuit équivalent aux portes jaunes, qui n'est autre qu'une porte NOR couplée à une porte NON. Un autre moyen d'obtenir le même résultat est de prendre un demi-additionneur fabriqué avec uniquement des portes NOR, et de retirer la porte NON adéquate. Il faut ensuite corriger l'entrée de retenue de la porte XOR, pour qu'elle fasse l'addition avec la retenue correctement. Le résultat est illustré ci-dessous.
[[File:Incrémenteur 2 bits de l'incrémenteur du 8085.png|centre|vignette|upright=2|Incrémenteur 2 bits de l'incrémenteur du 8085]]
Le circuit utilise le même nombre de portes logiques, il n'économise pas de transistors. Par contre, la retenue n'a plus qu'à traverser deux portes logiques, au lieu de quatre. La propagation de la retenue est donc plus rapide, l'incrémentation est donc plus rapide.
===Les incrémenteurs 2 bits ''carry skip''===
Une solution alternative fabrique des incrémenteurs 2 bits qui calculent séparément les deux bits du résultat et la retenue sortante. La retenue sortante est calculée à partir des bits de l’opérande, sans propager des retenues. Ainsi, la retenue sortante est connue en avance, sans passer par deux demi-additionneurs. Pour créer un tel incrémenteur, le mieux est de partir de sa table de vérité.
{|class="wikitable"
|-
! Opérande !! Retenue entrante !! !! Retenue sortante !! Résultat
|-
| 00 || 0 || || 0 || 00
|-
| 01 || 0 || || 0 || 01
|-
| 10 || 0 || || 0 || 10
|-
| 11 || 0 || || 0 || 11
|-
| colspan="5" |
|-
| 00 || 1 || || 0 || 01
|-
| 01 || 1 || || 0 || 10
|-
| 10 || 1 || || 0 || 11
|-
| 11 || 1 || || 1 || 00
|}
Elle nous dit que la retenue sortante ne vaut 1 que dans un seul cas : les deux bits d'opérande valent 1, la retenue entrante vaut 1. Elle se calcule alors avec une porte ET à trois entrées. Pour les deux bits du résultat, les équations logiques ne donnent pas un résultat satisfaisant. La solution la plus simple est d'utiliser deux demi-additionneurs et de retirer les portes logiques superflues. Le résultat est alors un '''incrémenteur 2 bits ''carry skip''''', où la retenue sortante est calculée séparément du reste.
[[File:Incrémenteur carry skip 2 bits.png|centre|vignette|upright=2.5|Incrémenteur ''carry skip'' 2 bits]]
==Les incrémenteurs à anticipation de retenue==
L''''anticipation de retenue''' calcule la retenue d'une colonne sans attendre les retenues des colonnes précédentes. Dans le cas idéal, toutes les retenues sont calculées en parallèle, en même temps, et sont ensuite envoyées aux demi-additionneurs. Il s'agit d'une optimisation qui est utilisée pour l'incrémentation, l'addition et d'autres opérations similaires. Pour l'incrémentation, déterminer la retenue ne demande pas de calculs complexes, contrairement à l'addition.
[[File:Incrémenteur à anticipation de retenues, 8 bits.png|centre|vignette|upright=2.5|Incrémenteur à anticipation de retenues, 8 bits.]]
Pour le comprendre, on peut regarder comment la retenue sortant d'un demi-additionneur est formée. Elle est calculée en faisant un ET logique entre la retenue entrante et le bit d'opérande. Une retenue est donc un ET logique entre toutes les retenues précédentes. Un incrémenteur à anticipation de retenue utilise donc une porte ET à plusieurs entrées pour calculer une retenue.
[[File:Anticipation de retenue pour un bit du résultat, incrémenteur.png|centre|vignette|upright=2.5|Anticipation de retenue pour un bit du résultat, incrémenteur]]
En théorie, on peut utiliser une porte ET à plusieurs entrées pour chaque bit de l'opérande. Cependant, cela entrainera un cout en transistors très important. Pour éviter de gaspiller trop de portes logiques, une solution est de mélanger anticipation de retenues et propagation de retenue. Par exemple, pour un incrémenteur 32 bits, on peut découper l'opérande en 4 octets : on anticipe les retenues pour chaque octet, mais l'incrémentation de chaque octet se fait avec propagation de retenue et/ou ''carry skip''.
[[File:Incrémenteur à anticipation de retenues.png|centre|vignette|upright=3|Incrémenteur hybride utilisant partiellement l'anticipation de retenues.]]
Quelques processeurs utilisaient l'anticipation de retenues. Par exemple, le processeur Z-80 de Zilog utilisait un incrémenteur pour des nombres de 16 bits, ce qui demandait des performances assez élevées. Et cet incrémenteur utilisait à la fois anticipation de retenues et ''carry skip''. Il était découpé en quatre blocs avant anticipation de retenues entre eux : un bloc regroupant les 7 bits de poids faible, suivi par un bloc de 5 bits, lui-même suivi par un bloc de 3 bits, terminé par un dernier bit isolé. A l'intérieur de ces blocs, les bits sont regroupés en paires utilisant le ''carry skip''. Pour ceux qui veulent en savoir plus sur cet incrémenteur, voici un lien sur le sujet :
* [https://www.righto.com/2013/11/the-z-80s-16-bit-incrementdecrement.html The Z-80's 16-bit increment/decrement circuit reverse engineered].
==Les décrémenteurs==
Les circuits décrémenteurs font l'inverse d'un incrémenteur : au lieu d'ajouter 1 à un opérande, ils lui soustraient 1. La différence n'est pas énorme, ce qui fait que les circuits incrémenteurs et décrémenteurs se ressemblent beaucoup. Là où les incrémenteurs sont fabriqués à partir de demi-additionneurs, les décrémenteurs sont bâtis avec des demi-soustracteurs.
La soustraction se fait en binaire comme en décimal. On soustrait deux chiffres, puis on doit propager une éventuelle retenue sur la colonne suivante. La retenue apparait quand le chiffre soustrait est plus grand que l'autre chiffre. Elle est propagée sur la colonne suivante, où elle doit être soustraite du résultat. Pour le dire autrement, après avoir soustrait un chiffre, on doit de plus soustraire la retenue de la colonne précédente.
La décrémentation effectue le calcul suivant :
<math>a_7</math> <math>a_6</math> <math>a_5</math> <math>a_4</math> <math>a_3</math> <math>a_2</math> <math>a_1</math> <math>a_0</math>
- 0 0 0 0 0 0 0 1
La colonne la plus à droite soustrait un 1. Par contre, les colonnes suivantes soustraient un zéro. Soustraire un zéro ne change rien. Mais il ne faut pas oublier de soustraire une éventuellement retenue, propagée depuis la colonne précédente. En clair, un décrémenteur peut se construire si on sait soustraire deux bits. On soustrait un 1 sur la colonne la plus à droite, on soustrait une retenue sur les autres colonnes. Et pour soustraire deux bits, il faut utiliser un demi-soustracteur.
===Le demi-soustracteur===
Un '''demi-soustracteur''' soustrait deux bits. Pour comprendre comment soustraire deux bits, traitons les quatre cas possibles un par un.
Premièrement, soustraire zéro à un bit ne changera rien :
* 0 - 0 = 0 ;
* 1 - 0 = 1.
Si on soustrait 1 à un bit qui vaut 1, on obtient zéro.
* 1 - 1 = 0.
Maintenant, que se passe-t-il si on soustrait 1 à 0 ? Voici le résultat :
* 0 - 1 = 1 et une retenue propagée sur la colonne suivante, où elle est soustraite.
La table de soustraction est donc la suivante :
* 0 - 0 = 0 ;
* 0 - 1 = 1 et une retenue ;
* 1 - 0 = 1 ;
* 1 - 1 = 0.
Si on construit la table de vérité, et qu'on construit le circuit associé, on tombe sur ce circuit. Détail important : le demi-soustracteur fait ici la soustraction X - Y. En anglais, X est appelé le ''Minuend'' et Y est appelé le ''subtrahend'', mais ces termes n'ont pas de traduction française.
[[File:Half Subtractor Vektor.svg|centre|vignette|Demi-soustracteur.]]
Un décrémenteur à propagation de retenue se construit comme un incrémenteur, sauf qu'on remplace les demi-additionneurs par des demi-soustracteurs. En clair, on enchaine plusieurs demi-soustracteurs les uns à la suite des autres. Il est possible d'utiliser l'anticipation de retenue, mais les circuits sont cependant différents de l'incrémenteur. Idem avec le ''carry skip'', et toute autre optimisation possible. La raison est que les retenues sont calculées différemment, ce qui fait que l’anticipation de retenue doit être adaptée.
===Les demi-additionneurs et demi-soustracteurs===
Vous l'aurez sans doute remarqué, mais le demi-soustracteur ressemble beaucoup au demi-additionneur. La seule différence est une porte NON ajoutée au bon endroit dans le demi-soustracteur. La porte NON inverse le bit X, sachant que le demi-soustracteur fait la soustraction X - Y. De plus, l'opérande inversée est utilisée seulement pour le calcul des retenues, le bit de somme est calculé normalement.
{|
|[[File:Half-adder.svg|vignette|Demi-additionneur]]
|[[File:Half Subtractor Vektor.svg|vignette|upright=1.3|Demi-soustracteur]]
|}
La conséquence est qu'il est possible de fusionner un demi-additionneur avec un demi-soustracteur. L'idée est de remplacer la porte NON du demi-soustracteur par un inverseur commandable. Si on envoie un zéro sur l'entrée de commande, l'inverseur commandable se comportera comme une porte OUI et le circuit sera alors identique à un demi-additionneur. Si on envoie un 1 sur l'entrée de commande, l'inverseur commandable se comportera comme une porte NON. le circuit sera alors un demi-soustracteur. En remplaçant les demi-additionneurs (ou demi-soustracteurs) par de tels circuits, on peut obtenir un circuit faisant à la fois incrémenteur et décrémenteur.
Une implémentation alternative se base sur les propriétés du complément à deux, et plus précisément sur cette équation :
: <math>\overline{x} + 1 = \overline{x - 1}</math>
Les deux termes de cette équation sont simplement des définitions équivalentes du complément à deux d'un nombre entier. Celui-ci se calcule de deux manières : soit on incrémente son complément à 1, soit on prend le complément à 1 de son décrément. Et cela nous permet de déterminer comment obtenir une incrémentation à partir d'une décrémentation et inversement.
Que nous dit cette équation ? Simplement que l'on peut décrémenter un nombre si on a deux inverseurs commandables, et un circuit incrémenteur. La décrémentation se fait alors en deux étapes : on calcule son complément à deux, puis on inverse les bits du résultat précédent. Le calcul du complément à deux demande d'avoir un circuit inverseur, et un incrémenteur.
<noinclude>
{{NavChapitre | book=Fonctionnement d'un ordinateur
| prev=Les circuits de sélection
| prevText=Les circuits de sélection
| next=Les bascules : des mémoires de 1 bit
| nextText=Les bascules : des mémoires de 1 bit
}}
</noinclude>
8r38uzdb64igsxmwgdqxlwpaf62ph6e
768788
768787
2026-06-27T15:36:40Z
Mewtow
31375
/* Les demi-additionneurs et demi-soustracteurs */
768788
wikitext
text/x-wiki
Le circuit '''incrémenteur''' incrémente un nombre entier. Plus précisément, il prend en entrée un nombre entier, et fournit en entier le nombre suivant, il lui ajoute 1. Une telle opération peut paraitre triviale et peu intéressante. Cependant, elle est très utilisée et est absolument cruciale pour créer des compteurs, des circuits capables de compter ou décompter, qui auront droit à un chapitre dédié. De plus, au-delà de leur utilisation dans les compteurs, les incrémenteurs étaient très utilisés sur les premiers processeurs 8 bits, comme le Z-80, le 6502, les premiers processeurs x86 comme le 8008, le 8086, le 8085, et bien d'autres.
Il existe aussi des ''circuits décrémenteurs'', qui décrémentent un opérande, ainsi que des ''circuits incrémenteur-décrémenteur'', qui peuvent incrémenter ou décrémenter, selon comment on les configure. Je ne vais pas détailler ces circuits plus que ça, car de tels circuits sont assez rares, comparé à un circuit incrémenteur simple.
==Le demi-additionneur==
Le circuit incrémenteur effectue l'opération suivante :
<math>a_7</math> <math>a_6</math> <math>a_5</math> <math>a_4</math> <math>a_3</math> <math>a_2</math> <math>a_1</math> <math>a_0</math>
+ 0 0 0 0 0 0 0 1
Un incrémenteur basique doit donc faire une addition pour chaque colonne, et précisément une addition de deux bits. Il se trouve que la table d'addition est très simple en binaire. Jugez plutôt :
* 0 + 0 = 0, retenue = 0 ;
* 0 + 1 = 1, retenue = 0 ;
* 1 + 0 = 1, retenue = 0 ;
* 1 + 1 = 0, retenue = 1.
Un circuit capable d'additionner deux bits est appelé un '''demi-additionneur'''. Il dispose d'une sortie S pour la somme, et C pour la retenue (''carry'' an anglais).
===Le demi-additionneur classique===
Un demi-additionneur est très simple à construire avec les techniques vues dans les premiers chapitres. Voici sa table de vérité :
{|class="wikitable"
|-
! Retenue entrante !! Opérande 1 !! !! Retenue sortante !! Bit de somme
|-
||0||0|| ||0||0
|-
||0||1|| ||0||1
|-
||1||0|| ||0||1
|-
||1||1|| ||1||0
|}
On voit immédiatement que la colonne des retenues donne une porte ET, alors que celle du bit de somme est calculé par un XOR.
{| class="flexible"
|[[File:1-bit half-adder.svg|class=transparent|centre|Demi-addtionneur.]]
|[[File:Half-adder.svg|centre|class=transparent|Circuit d'un demi-addtionneur.]]
|}
Il existe beaucoup d'autres implémentations alternatives, qui utilisent moins de portes logiques, calculent la retenue plus rapidement, consomment moins d'énergie, et autres. Et il est intéressant d'étudier quelques alternatives, surtout qu'elles serviront plus bas, quand on étudiera le circuit incrémenteur du 8085.
===Le demi-additionneur basé sur une modification de la retenue===
Une source d'amélioration est liée à la porte XOR. En pratique, une porte XOR est composée en combinant plusieurs portes logiques ET/OU/NOR/NAND ensemble. Et il se trouve qu'il y a des redondances entre celles-ci et les portes utilisées pour calculer la retenue. Par exemple, rappelons qu'une porte XOR peut être construite avec une porte ET et deux portes NOR, comme illustré ci-dessous. Et il se trouve que la porte ET interne calcule la retenue sortante. En supprimant cette redondance, on économise quelques portes logiques. De plus, on se retrouve avec un demi-additionneur qui calcule le bit de somme à partir de la retenue sortante.
[[File:Demi-additionneur avec redondances éliminées.png|centre|vignette|upright=2|Demi-additionneur avec redondances éliminées]]
Le circuit précédent a une interprétation logique. Si vous regardez la table de vérité, vous remarquerez que la somme de deux bits est égale à l'inverse de la retenue sortante, sauf dans le cas où les deux bits additionnés valent zéro. Et le circuit précédent est basé là-dessus.
{|class="wikitable"
|-
! Retenue entrante !! Opérande 1 !! !! Retenue sortante !! Bit de somme
|- class="f_vert"
||0||0|| ||0||0
|- class="f_rouge"
||0||1|| ||0||1
|- class="f_rouge"
||1||0|| ||0||1
|- class="f_rouge"
||1||1|| ||1||0
|}
L'idée est de calculer l'inverse du bit de somme, avant de l'inverser avec une porte NON. L'inverse du bit de somme vaut 1, soit quand la retenue est à 1, soit quand les deux bits additionnés sont à 0. La porte ET calcule la retenue sortante, la première porte NOR détecte sur les deux bits d'entrée valent zéro. Une porte OU combine les deux résultat pour obtenir l'inverse du bit d'entrée, puis une porte NOn inverse le tout pour obtenir le bit de somme adéquat. Les deux portes sont fusionnées : c'est la seconde porte NOR.
[[File:Full adder HA + MUX.png|centre|vignette|upright=2.5|Demi-additionneur basé sur une inversion de la retenue sortante.]]
===Les implémentations alternatives===
Une implémentation alternative, qui n'utilise que des portes NOR, est la suivante :
[[File:Demi-additionneur fait avec des portes NOR et NAND.png|centre|vignette|upright=2|Demi-additionneur fait avec des portes NOR et NAND.]]
Une implémentation alternative utilise des portes NAND. Pour rappel, il est possible de créer une porte XOR avec 4 portes NAND. La première d'entre elle fait un NAND entre les deux bits d'entrée, ce qui fait qu'elle calcule l'inverse de la retenue sortante. Le tout est illustré ci-dessous. En théorie, on devrait utiliser une porte NON pour récupérer la retenue correcte. Mais nous verrons plus bas que ce n'est pas une obligation. Nous verrons plus bas un exemple où c'est l'inverse de la retenue qui est utilisée dans les calculs, pour rendre les calculs plus rapides.
[[File:Demi-additionneur fait avec des portes NAND.png|centre|vignette|upright=2|Demi-additionneur fait avec des portes NAND]]
==L'incrémenteur à propagation de retenue==
Maintenant que l'on sait comment additionner deux bits, reprenons l'opération d'incrémentation :
<math>a_7</math> <math>a_6</math> <math>a_5</math> <math>a_4</math> <math>a_3</math> <math>a_2</math> <math>a_1</math> <math>a_0</math>
+ 0 0 0 0 0 0 0 1
------------------------------
Sur la colonne la plus à droite, il doit ajouter un au bit de poids faible. Pour les colonnes suivantes, il faut additionner le bit de l'opérande avec la retenue de la colonne précédente. En clair, on n'additionne que deux bits à chaque colonne : un 1 sur celle tout à droite, la retenue de la colonne précédente pour les autres. Et cela nous donne une idée de comment faire pour créer le circuit incrémenteur.
Il suffit d'utiliser un demi-additionneur par colonne, et de les enchainer les uns à la suite des autres. Chaque demi-additionneur additionne le bit de l'opérande avec la retenue fournie par le demi-additionneur précédent. Le seul qui fait exception est celui pour la colonne de poids faible. Pour celui-là, il doit ajouter 1 au bit de poids faible. Le résultat est appelé un '''incrémenteur à propagation de retenue'''. Il est constitué de demi-additionneurs enchaînés les uns à la suite des autres, du bit de poids faible vers le bit de poids fort.
[[File:Circuit incrémenteur.png|centre|vignette|upright=3|Circuit incrémenteur.]]
Maintenant, regardons le demi-additionneur le plus à droite, celui pour le bit de poids faible. Son entrée de retenue entrante est mise à 1 pour faire l'incrémentation. Quelques incrémenteurs permettent de configurer cette entrée de retenue à 0 ou à 1, ce qui effectue : soit une opération identité (l'opérande est recopié sur la sortie), soit une incrémentation. Un tel circuit est nommé un '''incrémenteur commandable'''. Nous aurons à utiliser une fois ou deux de tels incrémenteurs commandables dans la suite du cours.
L'incrémenteur à propagation de retenue est le plus simple et le plus économe en portes logiques. Mais de tels incrémenteurs sont rarement utilisés. À la place, on leur préfère des incrémenteurs plus rapides, mais qui utilisent plus de portes logiques. De tels incrémenteurs accélèrent le calcul des retenues. En effet, la rapidité d'une incrémentation est limitée par la propagation de la retenue : les retenues commencent à être calculées au bit de poids fort et on doit les calculer une par une, jusqu’à atteindre le bit de poids fort. Et cette "propagation des retenues" prend du temps, d'autant plus de temps que l'opérande est longue. Il y a deux optimisations principales, appelées le ''carry skip'' et l'anticipation de retenue, que nous allons décrire ci-dessous.
==Les incrémenteurs ''carry skip''==
L'optimisation '''''carry skip''''' effectue l'incrémentation, non pas bit par bit, mais par paquets de deux bits. Le résultat est que l'incrémentation est deux fois plus rapide, ou presque. Le circuit incrémenteur est donc composé en enchainant non pas des demi-additionneurs, mais des '''incrémenteurs 2 bits''' qui incrémentent un opérande de deux bits.
[[File:Circuit incrémenteur optimisé, faisant l'incrémentation par paquet de deux bits.png|centre|vignette|upright=2.5|Circuit incrémenteur optimisé, faisant l'incrémentation par paquet de deux bits]]
Reste à concevoir l'incrémenteur 2 bits.
===Les optimisations au niveau des transistors et l'exemple de l'incrémenteur du 8085 d'Intel===
Une première solution part de deux demi-additionneurs et effectue quelques simplifications au niveau des transistors et des portes logiques. Par exemple, l'Intel 8085 a utilisé un circuit optimisé, fabriqué avec des portes NAND et NOR. La raison est qu'avec les technologies de transistors CMOS actuelles, les portes NAND et NOR utilisent moins de transistors que les portes ET et OU. Et cette possibilité a été utilisée pour effectuer des simplifications assez mineures, mais toujours bonnes à prendre. Mais nous ne pouvons pas en parler en détail ici, car nous n'avons pas encore parlé en détail des transistors. Cependant, un exemple bien précis nous est accesible : celui de l’incrémenteur du processeur 8085 d'Intel.
Le processeur 8085 d'Intel est un processeur 8 bits très ancien. Il contenait un incrémenteur 16 bits, qui était utilisé pour calculer des adresses mémoire. Et cet incrémenteur était un incrémenteur à propagation de retenue optimisé. Il utilisait lui aussi des incrémenteurs 2 bits un peu modifiés.
L'idée était que les retenues sortant des colonnes paires étaient inversées, les colonnes impaires faisaient les calculs à partir de cette retenue inversée. Les demi-additionneurs des colonnes impaires n'étaient donc pas les mêmes que ceux des colonnes paires. Leurs tables de vérité sont différentes, leurs circuits aussi, et aucun des deux ne ressemble à un demi-additionneur normal. En réalité, ils sont fabriqués à partir des implémentations vues plus haut, qui utilisent uniquement des portes NAND ou uniquement des portes NOR.
Pour comprendre l'intérêt de faire ainsi, nous devons préciser une chose importante : avec les technologies CMOS utilisées pour les processeurs depuis les années 70, les portes logiques les plus simples sont les portes NON, NOR et NAND. Les portes ET/OU sont fabriquées en combinant des portes NOR/NAND avec une porte NON, ce qui prend plus de transistors.
Un demi-additionneur est donc fabriqué comme illustré ci-dessous, en logique CMOS. Seules les portes pour le calcul de la retenue sont indiquées, la porte XOR pour le bit de somme est fabriquée à partir de plusieurs portes logiques.
[[File:Demi-additionneur en CMOS.png|centre|vignette|upright=2|Demi-additionneur en CMOS]]
Si on enchaine deux demi-additionneurs, cela donne ceci :
[[File:Brique de base de l'incrémenteur du 8085.png|centre|vignette|upright=2|Brique de base de l'incrémenteur du 8085]]
La retenue doit donc traverser quatre portes logiques, dont deux portes NON. En inversant la retenue, les portes en jaune dans le schéma précédent se simplifient. Il suffit d'utiliser la loi de de Morgan pour trouver un circuit équivalent aux portes jaunes, qui n'est autre qu'une porte NOR couplée à une porte NON. Un autre moyen d'obtenir le même résultat est de prendre un demi-additionneur fabriqué avec uniquement des portes NOR, et de retirer la porte NON adéquate. Il faut ensuite corriger l'entrée de retenue de la porte XOR, pour qu'elle fasse l'addition avec la retenue correctement. Le résultat est illustré ci-dessous.
[[File:Incrémenteur 2 bits de l'incrémenteur du 8085.png|centre|vignette|upright=2|Incrémenteur 2 bits de l'incrémenteur du 8085]]
Le circuit utilise le même nombre de portes logiques, il n'économise pas de transistors. Par contre, la retenue n'a plus qu'à traverser deux portes logiques, au lieu de quatre. La propagation de la retenue est donc plus rapide, l'incrémentation est donc plus rapide.
===Les incrémenteurs 2 bits ''carry skip''===
Une solution alternative fabrique des incrémenteurs 2 bits qui calculent séparément les deux bits du résultat et la retenue sortante. La retenue sortante est calculée à partir des bits de l’opérande, sans propager des retenues. Ainsi, la retenue sortante est connue en avance, sans passer par deux demi-additionneurs. Pour créer un tel incrémenteur, le mieux est de partir de sa table de vérité.
{|class="wikitable"
|-
! Opérande !! Retenue entrante !! !! Retenue sortante !! Résultat
|-
| 00 || 0 || || 0 || 00
|-
| 01 || 0 || || 0 || 01
|-
| 10 || 0 || || 0 || 10
|-
| 11 || 0 || || 0 || 11
|-
| colspan="5" |
|-
| 00 || 1 || || 0 || 01
|-
| 01 || 1 || || 0 || 10
|-
| 10 || 1 || || 0 || 11
|-
| 11 || 1 || || 1 || 00
|}
Elle nous dit que la retenue sortante ne vaut 1 que dans un seul cas : les deux bits d'opérande valent 1, la retenue entrante vaut 1. Elle se calcule alors avec une porte ET à trois entrées. Pour les deux bits du résultat, les équations logiques ne donnent pas un résultat satisfaisant. La solution la plus simple est d'utiliser deux demi-additionneurs et de retirer les portes logiques superflues. Le résultat est alors un '''incrémenteur 2 bits ''carry skip''''', où la retenue sortante est calculée séparément du reste.
[[File:Incrémenteur carry skip 2 bits.png|centre|vignette|upright=2.5|Incrémenteur ''carry skip'' 2 bits]]
==Les incrémenteurs à anticipation de retenue==
L''''anticipation de retenue''' calcule la retenue d'une colonne sans attendre les retenues des colonnes précédentes. Dans le cas idéal, toutes les retenues sont calculées en parallèle, en même temps, et sont ensuite envoyées aux demi-additionneurs. Il s'agit d'une optimisation qui est utilisée pour l'incrémentation, l'addition et d'autres opérations similaires. Pour l'incrémentation, déterminer la retenue ne demande pas de calculs complexes, contrairement à l'addition.
[[File:Incrémenteur à anticipation de retenues, 8 bits.png|centre|vignette|upright=2.5|Incrémenteur à anticipation de retenues, 8 bits.]]
Pour le comprendre, on peut regarder comment la retenue sortant d'un demi-additionneur est formée. Elle est calculée en faisant un ET logique entre la retenue entrante et le bit d'opérande. Une retenue est donc un ET logique entre toutes les retenues précédentes. Un incrémenteur à anticipation de retenue utilise donc une porte ET à plusieurs entrées pour calculer une retenue.
[[File:Anticipation de retenue pour un bit du résultat, incrémenteur.png|centre|vignette|upright=2.5|Anticipation de retenue pour un bit du résultat, incrémenteur]]
En théorie, on peut utiliser une porte ET à plusieurs entrées pour chaque bit de l'opérande. Cependant, cela entrainera un cout en transistors très important. Pour éviter de gaspiller trop de portes logiques, une solution est de mélanger anticipation de retenues et propagation de retenue. Par exemple, pour un incrémenteur 32 bits, on peut découper l'opérande en 4 octets : on anticipe les retenues pour chaque octet, mais l'incrémentation de chaque octet se fait avec propagation de retenue et/ou ''carry skip''.
[[File:Incrémenteur à anticipation de retenues.png|centre|vignette|upright=3|Incrémenteur hybride utilisant partiellement l'anticipation de retenues.]]
Quelques processeurs utilisaient l'anticipation de retenues. Par exemple, le processeur Z-80 de Zilog utilisait un incrémenteur pour des nombres de 16 bits, ce qui demandait des performances assez élevées. Et cet incrémenteur utilisait à la fois anticipation de retenues et ''carry skip''. Il était découpé en quatre blocs avant anticipation de retenues entre eux : un bloc regroupant les 7 bits de poids faible, suivi par un bloc de 5 bits, lui-même suivi par un bloc de 3 bits, terminé par un dernier bit isolé. A l'intérieur de ces blocs, les bits sont regroupés en paires utilisant le ''carry skip''. Pour ceux qui veulent en savoir plus sur cet incrémenteur, voici un lien sur le sujet :
* [https://www.righto.com/2013/11/the-z-80s-16-bit-incrementdecrement.html The Z-80's 16-bit increment/decrement circuit reverse engineered].
==Les décrémenteurs==
Les circuits décrémenteurs font l'inverse d'un incrémenteur : au lieu d'ajouter 1 à un opérande, ils lui soustraient 1. La différence n'est pas énorme, ce qui fait que les circuits incrémenteurs et décrémenteurs se ressemblent beaucoup. Là où les incrémenteurs sont fabriqués à partir de demi-additionneurs, les décrémenteurs sont bâtis avec des demi-soustracteurs.
La soustraction se fait en binaire comme en décimal. On soustrait deux chiffres, puis on doit propager une éventuelle retenue sur la colonne suivante. La retenue apparait quand le chiffre soustrait est plus grand que l'autre chiffre. Elle est propagée sur la colonne suivante, où elle doit être soustraite du résultat. Pour le dire autrement, après avoir soustrait un chiffre, on doit de plus soustraire la retenue de la colonne précédente.
La décrémentation effectue le calcul suivant :
<math>a_7</math> <math>a_6</math> <math>a_5</math> <math>a_4</math> <math>a_3</math> <math>a_2</math> <math>a_1</math> <math>a_0</math>
- 0 0 0 0 0 0 0 1
La colonne la plus à droite soustrait un 1. Par contre, les colonnes suivantes soustraient un zéro. Soustraire un zéro ne change rien. Mais il ne faut pas oublier de soustraire une éventuellement retenue, propagée depuis la colonne précédente. En clair, un décrémenteur peut se construire si on sait soustraire deux bits. On soustrait un 1 sur la colonne la plus à droite, on soustrait une retenue sur les autres colonnes. Et pour soustraire deux bits, il faut utiliser un demi-soustracteur.
===Le demi-soustracteur===
Un '''demi-soustracteur''' soustrait deux bits. Pour comprendre comment soustraire deux bits, traitons les quatre cas possibles un par un.
Premièrement, soustraire zéro à un bit ne changera rien :
* 0 - 0 = 0 ;
* 1 - 0 = 1.
Si on soustrait 1 à un bit qui vaut 1, on obtient zéro.
* 1 - 1 = 0.
Maintenant, que se passe-t-il si on soustrait 1 à 0 ? Voici le résultat :
* 0 - 1 = 1 et une retenue propagée sur la colonne suivante, où elle est soustraite.
La table de soustraction est donc la suivante :
* 0 - 0 = 0 ;
* 0 - 1 = 1 et une retenue ;
* 1 - 0 = 1 ;
* 1 - 1 = 0.
Si on construit la table de vérité, et qu'on construit le circuit associé, on tombe sur ce circuit. Détail important : le demi-soustracteur fait ici la soustraction X - Y. En anglais, X est appelé le ''Minuend'' et Y est appelé le ''subtrahend'', mais ces termes n'ont pas de traduction française.
[[File:Half Subtractor Vektor.svg|centre|vignette|Demi-soustracteur.]]
Un décrémenteur à propagation de retenue se construit comme un incrémenteur, sauf qu'on remplace les demi-additionneurs par des demi-soustracteurs. En clair, on enchaine plusieurs demi-soustracteurs les uns à la suite des autres. Il est possible d'utiliser l'anticipation de retenue, mais les circuits sont cependant différents de l'incrémenteur. Idem avec le ''carry skip'', et toute autre optimisation possible. La raison est que les retenues sont calculées différemment, ce qui fait que l’anticipation de retenue doit être adaptée.
==Les incrémenteurs-décrémenteurs==
Les circuits décrémenteurs ressemblent beaucoup aux incrémenteurs. Aussi, vous ne serez pas étonné qu'il soit possible de fusionner un incrémenteur et un décrémenteur en un seul circuit. Un tel circuit s'appelle un '''incrémenteur-décrémenteur'''. Un tel circuit a une interface similaire à celle d'un incrémenteur, avec une entrée sur laquelle présenter l'opérande, et une sortie sur laquelle récupérer le résultat. Il a aussi une entrée de retenue, sur laquelle envoyer un 1 (incrémentation) ou un 0 (opération NOP/identité). Et il a aussi une sortie de retenue, qui indique que le résultat du calcul a débordé.
Mais en plus de cela, il dispose d'une entrée de commande qui permet de choisir entre incrémentation et décrémentation.
Il existe plusieurs manières de créer un incrémenteur-décrémenteur, que nous allons voir si dessous. Nous n'allons pas voir le cas le plus évident, aussi ne vous étonnez pas si vous avez remarqué qu'un demi-additionneur et un demi-soustracteur se ressemblent beaucoup.
===L'incrémenteur-décrémenteur basé sur des demi-additionneurs===
Une première implémentation se base sur les propriétés du complément à deux, et plus précisément sur cette équation :
: <math>\overline{x} + 1 = \overline{x - 1}</math>
Les deux termes de cette équation sont simplement des définitions équivalentes du complément à deux d'un nombre entier. Celui-ci se calcule de deux manières : soit on incrémente son complément à 1, soit on prend le complément à 1 de son décrément. Et cela nous permet de déterminer comment obtenir une incrémentation à partir d'une décrémentation et inversement.
Que nous dit cette équation ? Simplement que l'on peut décrémenter un nombre si on a deux inverseurs commandables, et un circuit incrémenteur. La décrémentation se fait alors en deux étapes : on calcule son complément à deux, puis on inverse les bits du résultat précédent. Le calcul du complément à deux demande d'avoir un circuit inverseur, et un incrémenteur.
===L'incrémenteur-décrémenteur basé sur des demi-additionneurs===
Vous l'aurez sans doute remarqué, mais le demi-soustracteur ressemble beaucoup au demi-additionneur. La seule différence est une porte NON ajoutée au bon endroit dans le demi-soustracteur. La porte NON inverse le bit X, sachant que le demi-soustracteur fait la soustraction X - Y. De plus, l'opérande inversée est utilisée seulement pour le calcul des retenues, le bit de somme est calculé normalement.
{|
|[[File:Half-adder.svg|vignette|Demi-additionneur]]
|[[File:Half Subtractor Vektor.svg|vignette|upright=1.3|Demi-soustracteur]]
|}
La conséquence est qu'il est possible de fusionner un demi-additionneur avec un demi-soustracteur. L'idée est de remplacer la porte NON du demi-soustracteur par un inverseur commandable. Si on envoie un zéro sur l'entrée de commande, l'inverseur commandable se comportera comme une porte OUI et le circuit sera alors identique à un demi-additionneur. Si on envoie un 1 sur l'entrée de commande, l'inverseur commandable se comportera comme une porte NON. le circuit sera alors un demi-soustracteur. En remplaçant les demi-additionneurs (ou demi-soustracteurs) par de tels circuits, on peut obtenir un circuit faisant à la fois incrémenteur et décrémenteur.
<noinclude>
{{NavChapitre | book=Fonctionnement d'un ordinateur
| prev=Les circuits de sélection
| prevText=Les circuits de sélection
| next=Les bascules : des mémoires de 1 bit
| nextText=Les bascules : des mémoires de 1 bit
}}
</noinclude>
bdkw833kyld8xgbr48quflaynu5ximb
768789
768788
2026-06-27T15:36:59Z
Mewtow
31375
/* Le demi-soustracteur */
768789
wikitext
text/x-wiki
Le circuit '''incrémenteur''' incrémente un nombre entier. Plus précisément, il prend en entrée un nombre entier, et fournit en entier le nombre suivant, il lui ajoute 1. Une telle opération peut paraitre triviale et peu intéressante. Cependant, elle est très utilisée et est absolument cruciale pour créer des compteurs, des circuits capables de compter ou décompter, qui auront droit à un chapitre dédié. De plus, au-delà de leur utilisation dans les compteurs, les incrémenteurs étaient très utilisés sur les premiers processeurs 8 bits, comme le Z-80, le 6502, les premiers processeurs x86 comme le 8008, le 8086, le 8085, et bien d'autres.
Il existe aussi des ''circuits décrémenteurs'', qui décrémentent un opérande, ainsi que des ''circuits incrémenteur-décrémenteur'', qui peuvent incrémenter ou décrémenter, selon comment on les configure. Je ne vais pas détailler ces circuits plus que ça, car de tels circuits sont assez rares, comparé à un circuit incrémenteur simple.
==Le demi-additionneur==
Le circuit incrémenteur effectue l'opération suivante :
<math>a_7</math> <math>a_6</math> <math>a_5</math> <math>a_4</math> <math>a_3</math> <math>a_2</math> <math>a_1</math> <math>a_0</math>
+ 0 0 0 0 0 0 0 1
Un incrémenteur basique doit donc faire une addition pour chaque colonne, et précisément une addition de deux bits. Il se trouve que la table d'addition est très simple en binaire. Jugez plutôt :
* 0 + 0 = 0, retenue = 0 ;
* 0 + 1 = 1, retenue = 0 ;
* 1 + 0 = 1, retenue = 0 ;
* 1 + 1 = 0, retenue = 1.
Un circuit capable d'additionner deux bits est appelé un '''demi-additionneur'''. Il dispose d'une sortie S pour la somme, et C pour la retenue (''carry'' an anglais).
===Le demi-additionneur classique===
Un demi-additionneur est très simple à construire avec les techniques vues dans les premiers chapitres. Voici sa table de vérité :
{|class="wikitable"
|-
! Retenue entrante !! Opérande 1 !! !! Retenue sortante !! Bit de somme
|-
||0||0|| ||0||0
|-
||0||1|| ||0||1
|-
||1||0|| ||0||1
|-
||1||1|| ||1||0
|}
On voit immédiatement que la colonne des retenues donne une porte ET, alors que celle du bit de somme est calculé par un XOR.
{| class="flexible"
|[[File:1-bit half-adder.svg|class=transparent|centre|Demi-addtionneur.]]
|[[File:Half-adder.svg|centre|class=transparent|Circuit d'un demi-addtionneur.]]
|}
Il existe beaucoup d'autres implémentations alternatives, qui utilisent moins de portes logiques, calculent la retenue plus rapidement, consomment moins d'énergie, et autres. Et il est intéressant d'étudier quelques alternatives, surtout qu'elles serviront plus bas, quand on étudiera le circuit incrémenteur du 8085.
===Le demi-additionneur basé sur une modification de la retenue===
Une source d'amélioration est liée à la porte XOR. En pratique, une porte XOR est composée en combinant plusieurs portes logiques ET/OU/NOR/NAND ensemble. Et il se trouve qu'il y a des redondances entre celles-ci et les portes utilisées pour calculer la retenue. Par exemple, rappelons qu'une porte XOR peut être construite avec une porte ET et deux portes NOR, comme illustré ci-dessous. Et il se trouve que la porte ET interne calcule la retenue sortante. En supprimant cette redondance, on économise quelques portes logiques. De plus, on se retrouve avec un demi-additionneur qui calcule le bit de somme à partir de la retenue sortante.
[[File:Demi-additionneur avec redondances éliminées.png|centre|vignette|upright=2|Demi-additionneur avec redondances éliminées]]
Le circuit précédent a une interprétation logique. Si vous regardez la table de vérité, vous remarquerez que la somme de deux bits est égale à l'inverse de la retenue sortante, sauf dans le cas où les deux bits additionnés valent zéro. Et le circuit précédent est basé là-dessus.
{|class="wikitable"
|-
! Retenue entrante !! Opérande 1 !! !! Retenue sortante !! Bit de somme
|- class="f_vert"
||0||0|| ||0||0
|- class="f_rouge"
||0||1|| ||0||1
|- class="f_rouge"
||1||0|| ||0||1
|- class="f_rouge"
||1||1|| ||1||0
|}
L'idée est de calculer l'inverse du bit de somme, avant de l'inverser avec une porte NON. L'inverse du bit de somme vaut 1, soit quand la retenue est à 1, soit quand les deux bits additionnés sont à 0. La porte ET calcule la retenue sortante, la première porte NOR détecte sur les deux bits d'entrée valent zéro. Une porte OU combine les deux résultat pour obtenir l'inverse du bit d'entrée, puis une porte NOn inverse le tout pour obtenir le bit de somme adéquat. Les deux portes sont fusionnées : c'est la seconde porte NOR.
[[File:Full adder HA + MUX.png|centre|vignette|upright=2.5|Demi-additionneur basé sur une inversion de la retenue sortante.]]
===Les implémentations alternatives===
Une implémentation alternative, qui n'utilise que des portes NOR, est la suivante :
[[File:Demi-additionneur fait avec des portes NOR et NAND.png|centre|vignette|upright=2|Demi-additionneur fait avec des portes NOR et NAND.]]
Une implémentation alternative utilise des portes NAND. Pour rappel, il est possible de créer une porte XOR avec 4 portes NAND. La première d'entre elle fait un NAND entre les deux bits d'entrée, ce qui fait qu'elle calcule l'inverse de la retenue sortante. Le tout est illustré ci-dessous. En théorie, on devrait utiliser une porte NON pour récupérer la retenue correcte. Mais nous verrons plus bas que ce n'est pas une obligation. Nous verrons plus bas un exemple où c'est l'inverse de la retenue qui est utilisée dans les calculs, pour rendre les calculs plus rapides.
[[File:Demi-additionneur fait avec des portes NAND.png|centre|vignette|upright=2|Demi-additionneur fait avec des portes NAND]]
==L'incrémenteur à propagation de retenue==
Maintenant que l'on sait comment additionner deux bits, reprenons l'opération d'incrémentation :
<math>a_7</math> <math>a_6</math> <math>a_5</math> <math>a_4</math> <math>a_3</math> <math>a_2</math> <math>a_1</math> <math>a_0</math>
+ 0 0 0 0 0 0 0 1
------------------------------
Sur la colonne la plus à droite, il doit ajouter un au bit de poids faible. Pour les colonnes suivantes, il faut additionner le bit de l'opérande avec la retenue de la colonne précédente. En clair, on n'additionne que deux bits à chaque colonne : un 1 sur celle tout à droite, la retenue de la colonne précédente pour les autres. Et cela nous donne une idée de comment faire pour créer le circuit incrémenteur.
Il suffit d'utiliser un demi-additionneur par colonne, et de les enchainer les uns à la suite des autres. Chaque demi-additionneur additionne le bit de l'opérande avec la retenue fournie par le demi-additionneur précédent. Le seul qui fait exception est celui pour la colonne de poids faible. Pour celui-là, il doit ajouter 1 au bit de poids faible. Le résultat est appelé un '''incrémenteur à propagation de retenue'''. Il est constitué de demi-additionneurs enchaînés les uns à la suite des autres, du bit de poids faible vers le bit de poids fort.
[[File:Circuit incrémenteur.png|centre|vignette|upright=3|Circuit incrémenteur.]]
Maintenant, regardons le demi-additionneur le plus à droite, celui pour le bit de poids faible. Son entrée de retenue entrante est mise à 1 pour faire l'incrémentation. Quelques incrémenteurs permettent de configurer cette entrée de retenue à 0 ou à 1, ce qui effectue : soit une opération identité (l'opérande est recopié sur la sortie), soit une incrémentation. Un tel circuit est nommé un '''incrémenteur commandable'''. Nous aurons à utiliser une fois ou deux de tels incrémenteurs commandables dans la suite du cours.
L'incrémenteur à propagation de retenue est le plus simple et le plus économe en portes logiques. Mais de tels incrémenteurs sont rarement utilisés. À la place, on leur préfère des incrémenteurs plus rapides, mais qui utilisent plus de portes logiques. De tels incrémenteurs accélèrent le calcul des retenues. En effet, la rapidité d'une incrémentation est limitée par la propagation de la retenue : les retenues commencent à être calculées au bit de poids fort et on doit les calculer une par une, jusqu’à atteindre le bit de poids fort. Et cette "propagation des retenues" prend du temps, d'autant plus de temps que l'opérande est longue. Il y a deux optimisations principales, appelées le ''carry skip'' et l'anticipation de retenue, que nous allons décrire ci-dessous.
==Les incrémenteurs ''carry skip''==
L'optimisation '''''carry skip''''' effectue l'incrémentation, non pas bit par bit, mais par paquets de deux bits. Le résultat est que l'incrémentation est deux fois plus rapide, ou presque. Le circuit incrémenteur est donc composé en enchainant non pas des demi-additionneurs, mais des '''incrémenteurs 2 bits''' qui incrémentent un opérande de deux bits.
[[File:Circuit incrémenteur optimisé, faisant l'incrémentation par paquet de deux bits.png|centre|vignette|upright=2.5|Circuit incrémenteur optimisé, faisant l'incrémentation par paquet de deux bits]]
Reste à concevoir l'incrémenteur 2 bits.
===Les optimisations au niveau des transistors et l'exemple de l'incrémenteur du 8085 d'Intel===
Une première solution part de deux demi-additionneurs et effectue quelques simplifications au niveau des transistors et des portes logiques. Par exemple, l'Intel 8085 a utilisé un circuit optimisé, fabriqué avec des portes NAND et NOR. La raison est qu'avec les technologies de transistors CMOS actuelles, les portes NAND et NOR utilisent moins de transistors que les portes ET et OU. Et cette possibilité a été utilisée pour effectuer des simplifications assez mineures, mais toujours bonnes à prendre. Mais nous ne pouvons pas en parler en détail ici, car nous n'avons pas encore parlé en détail des transistors. Cependant, un exemple bien précis nous est accesible : celui de l’incrémenteur du processeur 8085 d'Intel.
Le processeur 8085 d'Intel est un processeur 8 bits très ancien. Il contenait un incrémenteur 16 bits, qui était utilisé pour calculer des adresses mémoire. Et cet incrémenteur était un incrémenteur à propagation de retenue optimisé. Il utilisait lui aussi des incrémenteurs 2 bits un peu modifiés.
L'idée était que les retenues sortant des colonnes paires étaient inversées, les colonnes impaires faisaient les calculs à partir de cette retenue inversée. Les demi-additionneurs des colonnes impaires n'étaient donc pas les mêmes que ceux des colonnes paires. Leurs tables de vérité sont différentes, leurs circuits aussi, et aucun des deux ne ressemble à un demi-additionneur normal. En réalité, ils sont fabriqués à partir des implémentations vues plus haut, qui utilisent uniquement des portes NAND ou uniquement des portes NOR.
Pour comprendre l'intérêt de faire ainsi, nous devons préciser une chose importante : avec les technologies CMOS utilisées pour les processeurs depuis les années 70, les portes logiques les plus simples sont les portes NON, NOR et NAND. Les portes ET/OU sont fabriquées en combinant des portes NOR/NAND avec une porte NON, ce qui prend plus de transistors.
Un demi-additionneur est donc fabriqué comme illustré ci-dessous, en logique CMOS. Seules les portes pour le calcul de la retenue sont indiquées, la porte XOR pour le bit de somme est fabriquée à partir de plusieurs portes logiques.
[[File:Demi-additionneur en CMOS.png|centre|vignette|upright=2|Demi-additionneur en CMOS]]
Si on enchaine deux demi-additionneurs, cela donne ceci :
[[File:Brique de base de l'incrémenteur du 8085.png|centre|vignette|upright=2|Brique de base de l'incrémenteur du 8085]]
La retenue doit donc traverser quatre portes logiques, dont deux portes NON. En inversant la retenue, les portes en jaune dans le schéma précédent se simplifient. Il suffit d'utiliser la loi de de Morgan pour trouver un circuit équivalent aux portes jaunes, qui n'est autre qu'une porte NOR couplée à une porte NON. Un autre moyen d'obtenir le même résultat est de prendre un demi-additionneur fabriqué avec uniquement des portes NOR, et de retirer la porte NON adéquate. Il faut ensuite corriger l'entrée de retenue de la porte XOR, pour qu'elle fasse l'addition avec la retenue correctement. Le résultat est illustré ci-dessous.
[[File:Incrémenteur 2 bits de l'incrémenteur du 8085.png|centre|vignette|upright=2|Incrémenteur 2 bits de l'incrémenteur du 8085]]
Le circuit utilise le même nombre de portes logiques, il n'économise pas de transistors. Par contre, la retenue n'a plus qu'à traverser deux portes logiques, au lieu de quatre. La propagation de la retenue est donc plus rapide, l'incrémentation est donc plus rapide.
===Les incrémenteurs 2 bits ''carry skip''===
Une solution alternative fabrique des incrémenteurs 2 bits qui calculent séparément les deux bits du résultat et la retenue sortante. La retenue sortante est calculée à partir des bits de l’opérande, sans propager des retenues. Ainsi, la retenue sortante est connue en avance, sans passer par deux demi-additionneurs. Pour créer un tel incrémenteur, le mieux est de partir de sa table de vérité.
{|class="wikitable"
|-
! Opérande !! Retenue entrante !! !! Retenue sortante !! Résultat
|-
| 00 || 0 || || 0 || 00
|-
| 01 || 0 || || 0 || 01
|-
| 10 || 0 || || 0 || 10
|-
| 11 || 0 || || 0 || 11
|-
| colspan="5" |
|-
| 00 || 1 || || 0 || 01
|-
| 01 || 1 || || 0 || 10
|-
| 10 || 1 || || 0 || 11
|-
| 11 || 1 || || 1 || 00
|}
Elle nous dit que la retenue sortante ne vaut 1 que dans un seul cas : les deux bits d'opérande valent 1, la retenue entrante vaut 1. Elle se calcule alors avec une porte ET à trois entrées. Pour les deux bits du résultat, les équations logiques ne donnent pas un résultat satisfaisant. La solution la plus simple est d'utiliser deux demi-additionneurs et de retirer les portes logiques superflues. Le résultat est alors un '''incrémenteur 2 bits ''carry skip''''', où la retenue sortante est calculée séparément du reste.
[[File:Incrémenteur carry skip 2 bits.png|centre|vignette|upright=2.5|Incrémenteur ''carry skip'' 2 bits]]
==Les incrémenteurs à anticipation de retenue==
L''''anticipation de retenue''' calcule la retenue d'une colonne sans attendre les retenues des colonnes précédentes. Dans le cas idéal, toutes les retenues sont calculées en parallèle, en même temps, et sont ensuite envoyées aux demi-additionneurs. Il s'agit d'une optimisation qui est utilisée pour l'incrémentation, l'addition et d'autres opérations similaires. Pour l'incrémentation, déterminer la retenue ne demande pas de calculs complexes, contrairement à l'addition.
[[File:Incrémenteur à anticipation de retenues, 8 bits.png|centre|vignette|upright=2.5|Incrémenteur à anticipation de retenues, 8 bits.]]
Pour le comprendre, on peut regarder comment la retenue sortant d'un demi-additionneur est formée. Elle est calculée en faisant un ET logique entre la retenue entrante et le bit d'opérande. Une retenue est donc un ET logique entre toutes les retenues précédentes. Un incrémenteur à anticipation de retenue utilise donc une porte ET à plusieurs entrées pour calculer une retenue.
[[File:Anticipation de retenue pour un bit du résultat, incrémenteur.png|centre|vignette|upright=2.5|Anticipation de retenue pour un bit du résultat, incrémenteur]]
En théorie, on peut utiliser une porte ET à plusieurs entrées pour chaque bit de l'opérande. Cependant, cela entrainera un cout en transistors très important. Pour éviter de gaspiller trop de portes logiques, une solution est de mélanger anticipation de retenues et propagation de retenue. Par exemple, pour un incrémenteur 32 bits, on peut découper l'opérande en 4 octets : on anticipe les retenues pour chaque octet, mais l'incrémentation de chaque octet se fait avec propagation de retenue et/ou ''carry skip''.
[[File:Incrémenteur à anticipation de retenues.png|centre|vignette|upright=3|Incrémenteur hybride utilisant partiellement l'anticipation de retenues.]]
Quelques processeurs utilisaient l'anticipation de retenues. Par exemple, le processeur Z-80 de Zilog utilisait un incrémenteur pour des nombres de 16 bits, ce qui demandait des performances assez élevées. Et cet incrémenteur utilisait à la fois anticipation de retenues et ''carry skip''. Il était découpé en quatre blocs avant anticipation de retenues entre eux : un bloc regroupant les 7 bits de poids faible, suivi par un bloc de 5 bits, lui-même suivi par un bloc de 3 bits, terminé par un dernier bit isolé. A l'intérieur de ces blocs, les bits sont regroupés en paires utilisant le ''carry skip''. Pour ceux qui veulent en savoir plus sur cet incrémenteur, voici un lien sur le sujet :
* [https://www.righto.com/2013/11/the-z-80s-16-bit-incrementdecrement.html The Z-80's 16-bit increment/decrement circuit reverse engineered].
==Les décrémenteurs==
Les circuits décrémenteurs font l'inverse d'un incrémenteur : au lieu d'ajouter 1 à un opérande, ils lui soustraient 1. La différence n'est pas énorme, ce qui fait que les circuits incrémenteurs et décrémenteurs se ressemblent beaucoup. Là où les incrémenteurs sont fabriqués à partir de demi-additionneurs, les décrémenteurs sont bâtis avec des demi-soustracteurs.
La soustraction se fait en binaire comme en décimal. On soustrait deux chiffres, puis on doit propager une éventuelle retenue sur la colonne suivante. La retenue apparait quand le chiffre soustrait est plus grand que l'autre chiffre. Elle est propagée sur la colonne suivante, où elle doit être soustraite du résultat. Pour le dire autrement, après avoir soustrait un chiffre, on doit de plus soustraire la retenue de la colonne précédente.
La décrémentation effectue le calcul suivant :
<math>a_7</math> <math>a_6</math> <math>a_5</math> <math>a_4</math> <math>a_3</math> <math>a_2</math> <math>a_1</math> <math>a_0</math>
- 0 0 0 0 0 0 0 1
La colonne la plus à droite soustrait un 1. Par contre, les colonnes suivantes soustraient un zéro. Soustraire un zéro ne change rien. Mais il ne faut pas oublier de soustraire une éventuellement retenue, propagée depuis la colonne précédente. En clair, un décrémenteur peut se construire si on sait soustraire deux bits. On soustrait un 1 sur la colonne la plus à droite, on soustrait une retenue sur les autres colonnes. Et pour soustraire deux bits, il faut utiliser un demi-soustracteur.
===Le demi-soustracteur===
Un '''demi-soustracteur''' soustrait deux bits. Pour comprendre comment soustraire deux bits, traitons les quatre cas possibles un par un.
Premièrement, soustraire zéro à un bit ne changera rien :
* 0 - 0 = 0 ;
* 1 - 0 = 1.
Si on soustrait 1 à un bit qui vaut 1, on obtient zéro.
* 1 - 1 = 0.
Maintenant, que se passe-t-il si on soustrait 1 à 0 ? Voici le résultat :
* 0 - 1 = 1 et une retenue propagée sur la colonne suivante, où elle est soustraite.
La table de soustraction est donc la suivante :
* 0 - 0 = 0 ;
* 0 - 1 = 1 et une retenue ;
* 1 - 0 = 1 ;
* 1 - 1 = 0.
Si on construit la table de vérité, et qu'on construit le circuit associé, on tombe sur ce circuit. Détail important : le demi-soustracteur fait ici la soustraction X - Y. En anglais, X est appelé le ''Minuend'' et Y est appelé le ''subtrahend'', mais ces termes n'ont pas de traduction française.
[[File:Half Subtractor Vektor.svg|centre|vignette|Demi-soustracteur.]]
===Le décrémenteur à propagation de retenue===
Un décrémenteur à propagation de retenue se construit comme un incrémenteur, sauf qu'on remplace les demi-additionneurs par des demi-soustracteurs. En clair, on enchaine plusieurs demi-soustracteurs les uns à la suite des autres. Il est possible d'utiliser l'anticipation de retenue, mais les circuits sont cependant différents de l'incrémenteur. Idem avec le ''carry skip'', et toute autre optimisation possible. La raison est que les retenues sont calculées différemment, ce qui fait que l’anticipation de retenue doit être adaptée.
==Les incrémenteurs-décrémenteurs==
Les circuits décrémenteurs ressemblent beaucoup aux incrémenteurs. Aussi, vous ne serez pas étonné qu'il soit possible de fusionner un incrémenteur et un décrémenteur en un seul circuit. Un tel circuit s'appelle un '''incrémenteur-décrémenteur'''. Un tel circuit a une interface similaire à celle d'un incrémenteur, avec une entrée sur laquelle présenter l'opérande, et une sortie sur laquelle récupérer le résultat. Il a aussi une entrée de retenue, sur laquelle envoyer un 1 (incrémentation) ou un 0 (opération NOP/identité). Et il a aussi une sortie de retenue, qui indique que le résultat du calcul a débordé.
Mais en plus de cela, il dispose d'une entrée de commande qui permet de choisir entre incrémentation et décrémentation.
Il existe plusieurs manières de créer un incrémenteur-décrémenteur, que nous allons voir si dessous. Nous n'allons pas voir le cas le plus évident, aussi ne vous étonnez pas si vous avez remarqué qu'un demi-additionneur et un demi-soustracteur se ressemblent beaucoup.
===L'incrémenteur-décrémenteur basé sur des demi-additionneurs===
Une première implémentation se base sur les propriétés du complément à deux, et plus précisément sur cette équation :
: <math>\overline{x} + 1 = \overline{x - 1}</math>
Les deux termes de cette équation sont simplement des définitions équivalentes du complément à deux d'un nombre entier. Celui-ci se calcule de deux manières : soit on incrémente son complément à 1, soit on prend le complément à 1 de son décrément. Et cela nous permet de déterminer comment obtenir une incrémentation à partir d'une décrémentation et inversement.
Que nous dit cette équation ? Simplement que l'on peut décrémenter un nombre si on a deux inverseurs commandables, et un circuit incrémenteur. La décrémentation se fait alors en deux étapes : on calcule son complément à deux, puis on inverse les bits du résultat précédent. Le calcul du complément à deux demande d'avoir un circuit inverseur, et un incrémenteur.
===L'incrémenteur-décrémenteur basé sur des demi-additionneurs===
Vous l'aurez sans doute remarqué, mais le demi-soustracteur ressemble beaucoup au demi-additionneur. La seule différence est une porte NON ajoutée au bon endroit dans le demi-soustracteur. La porte NON inverse le bit X, sachant que le demi-soustracteur fait la soustraction X - Y. De plus, l'opérande inversée est utilisée seulement pour le calcul des retenues, le bit de somme est calculé normalement.
{|
|[[File:Half-adder.svg|vignette|Demi-additionneur]]
|[[File:Half Subtractor Vektor.svg|vignette|upright=1.3|Demi-soustracteur]]
|}
La conséquence est qu'il est possible de fusionner un demi-additionneur avec un demi-soustracteur. L'idée est de remplacer la porte NON du demi-soustracteur par un inverseur commandable. Si on envoie un zéro sur l'entrée de commande, l'inverseur commandable se comportera comme une porte OUI et le circuit sera alors identique à un demi-additionneur. Si on envoie un 1 sur l'entrée de commande, l'inverseur commandable se comportera comme une porte NON. le circuit sera alors un demi-soustracteur. En remplaçant les demi-additionneurs (ou demi-soustracteurs) par de tels circuits, on peut obtenir un circuit faisant à la fois incrémenteur et décrémenteur.
<noinclude>
{{NavChapitre | book=Fonctionnement d'un ordinateur
| prev=Les circuits de sélection
| prevText=Les circuits de sélection
| next=Les bascules : des mémoires de 1 bit
| nextText=Les bascules : des mémoires de 1 bit
}}
</noinclude>
3709ptos6lgdkqggxcvzsqcyhr4i52a
768790
768789
2026-06-27T15:39:48Z
Mewtow
31375
/* L'incrémenteur-décrémenteur basé sur des demi-additionneurs */
768790
wikitext
text/x-wiki
Le circuit '''incrémenteur''' incrémente un nombre entier. Plus précisément, il prend en entrée un nombre entier, et fournit en entier le nombre suivant, il lui ajoute 1. Une telle opération peut paraitre triviale et peu intéressante. Cependant, elle est très utilisée et est absolument cruciale pour créer des compteurs, des circuits capables de compter ou décompter, qui auront droit à un chapitre dédié. De plus, au-delà de leur utilisation dans les compteurs, les incrémenteurs étaient très utilisés sur les premiers processeurs 8 bits, comme le Z-80, le 6502, les premiers processeurs x86 comme le 8008, le 8086, le 8085, et bien d'autres.
Il existe aussi des ''circuits décrémenteurs'', qui décrémentent un opérande, ainsi que des ''circuits incrémenteur-décrémenteur'', qui peuvent incrémenter ou décrémenter, selon comment on les configure. Je ne vais pas détailler ces circuits plus que ça, car de tels circuits sont assez rares, comparé à un circuit incrémenteur simple.
==Le demi-additionneur==
Le circuit incrémenteur effectue l'opération suivante :
<math>a_7</math> <math>a_6</math> <math>a_5</math> <math>a_4</math> <math>a_3</math> <math>a_2</math> <math>a_1</math> <math>a_0</math>
+ 0 0 0 0 0 0 0 1
Un incrémenteur basique doit donc faire une addition pour chaque colonne, et précisément une addition de deux bits. Il se trouve que la table d'addition est très simple en binaire. Jugez plutôt :
* 0 + 0 = 0, retenue = 0 ;
* 0 + 1 = 1, retenue = 0 ;
* 1 + 0 = 1, retenue = 0 ;
* 1 + 1 = 0, retenue = 1.
Un circuit capable d'additionner deux bits est appelé un '''demi-additionneur'''. Il dispose d'une sortie S pour la somme, et C pour la retenue (''carry'' an anglais).
===Le demi-additionneur classique===
Un demi-additionneur est très simple à construire avec les techniques vues dans les premiers chapitres. Voici sa table de vérité :
{|class="wikitable"
|-
! Retenue entrante !! Opérande 1 !! !! Retenue sortante !! Bit de somme
|-
||0||0|| ||0||0
|-
||0||1|| ||0||1
|-
||1||0|| ||0||1
|-
||1||1|| ||1||0
|}
On voit immédiatement que la colonne des retenues donne une porte ET, alors que celle du bit de somme est calculé par un XOR.
{| class="flexible"
|[[File:1-bit half-adder.svg|class=transparent|centre|Demi-addtionneur.]]
|[[File:Half-adder.svg|centre|class=transparent|Circuit d'un demi-addtionneur.]]
|}
Il existe beaucoup d'autres implémentations alternatives, qui utilisent moins de portes logiques, calculent la retenue plus rapidement, consomment moins d'énergie, et autres. Et il est intéressant d'étudier quelques alternatives, surtout qu'elles serviront plus bas, quand on étudiera le circuit incrémenteur du 8085.
===Le demi-additionneur basé sur une modification de la retenue===
Une source d'amélioration est liée à la porte XOR. En pratique, une porte XOR est composée en combinant plusieurs portes logiques ET/OU/NOR/NAND ensemble. Et il se trouve qu'il y a des redondances entre celles-ci et les portes utilisées pour calculer la retenue. Par exemple, rappelons qu'une porte XOR peut être construite avec une porte ET et deux portes NOR, comme illustré ci-dessous. Et il se trouve que la porte ET interne calcule la retenue sortante. En supprimant cette redondance, on économise quelques portes logiques. De plus, on se retrouve avec un demi-additionneur qui calcule le bit de somme à partir de la retenue sortante.
[[File:Demi-additionneur avec redondances éliminées.png|centre|vignette|upright=2|Demi-additionneur avec redondances éliminées]]
Le circuit précédent a une interprétation logique. Si vous regardez la table de vérité, vous remarquerez que la somme de deux bits est égale à l'inverse de la retenue sortante, sauf dans le cas où les deux bits additionnés valent zéro. Et le circuit précédent est basé là-dessus.
{|class="wikitable"
|-
! Retenue entrante !! Opérande 1 !! !! Retenue sortante !! Bit de somme
|- class="f_vert"
||0||0|| ||0||0
|- class="f_rouge"
||0||1|| ||0||1
|- class="f_rouge"
||1||0|| ||0||1
|- class="f_rouge"
||1||1|| ||1||0
|}
L'idée est de calculer l'inverse du bit de somme, avant de l'inverser avec une porte NON. L'inverse du bit de somme vaut 1, soit quand la retenue est à 1, soit quand les deux bits additionnés sont à 0. La porte ET calcule la retenue sortante, la première porte NOR détecte sur les deux bits d'entrée valent zéro. Une porte OU combine les deux résultat pour obtenir l'inverse du bit d'entrée, puis une porte NOn inverse le tout pour obtenir le bit de somme adéquat. Les deux portes sont fusionnées : c'est la seconde porte NOR.
[[File:Full adder HA + MUX.png|centre|vignette|upright=2.5|Demi-additionneur basé sur une inversion de la retenue sortante.]]
===Les implémentations alternatives===
Une implémentation alternative, qui n'utilise que des portes NOR, est la suivante :
[[File:Demi-additionneur fait avec des portes NOR et NAND.png|centre|vignette|upright=2|Demi-additionneur fait avec des portes NOR et NAND.]]
Une implémentation alternative utilise des portes NAND. Pour rappel, il est possible de créer une porte XOR avec 4 portes NAND. La première d'entre elle fait un NAND entre les deux bits d'entrée, ce qui fait qu'elle calcule l'inverse de la retenue sortante. Le tout est illustré ci-dessous. En théorie, on devrait utiliser une porte NON pour récupérer la retenue correcte. Mais nous verrons plus bas que ce n'est pas une obligation. Nous verrons plus bas un exemple où c'est l'inverse de la retenue qui est utilisée dans les calculs, pour rendre les calculs plus rapides.
[[File:Demi-additionneur fait avec des portes NAND.png|centre|vignette|upright=2|Demi-additionneur fait avec des portes NAND]]
==L'incrémenteur à propagation de retenue==
Maintenant que l'on sait comment additionner deux bits, reprenons l'opération d'incrémentation :
<math>a_7</math> <math>a_6</math> <math>a_5</math> <math>a_4</math> <math>a_3</math> <math>a_2</math> <math>a_1</math> <math>a_0</math>
+ 0 0 0 0 0 0 0 1
------------------------------
Sur la colonne la plus à droite, il doit ajouter un au bit de poids faible. Pour les colonnes suivantes, il faut additionner le bit de l'opérande avec la retenue de la colonne précédente. En clair, on n'additionne que deux bits à chaque colonne : un 1 sur celle tout à droite, la retenue de la colonne précédente pour les autres. Et cela nous donne une idée de comment faire pour créer le circuit incrémenteur.
Il suffit d'utiliser un demi-additionneur par colonne, et de les enchainer les uns à la suite des autres. Chaque demi-additionneur additionne le bit de l'opérande avec la retenue fournie par le demi-additionneur précédent. Le seul qui fait exception est celui pour la colonne de poids faible. Pour celui-là, il doit ajouter 1 au bit de poids faible. Le résultat est appelé un '''incrémenteur à propagation de retenue'''. Il est constitué de demi-additionneurs enchaînés les uns à la suite des autres, du bit de poids faible vers le bit de poids fort.
[[File:Circuit incrémenteur.png|centre|vignette|upright=3|Circuit incrémenteur.]]
Maintenant, regardons le demi-additionneur le plus à droite, celui pour le bit de poids faible. Son entrée de retenue entrante est mise à 1 pour faire l'incrémentation. Quelques incrémenteurs permettent de configurer cette entrée de retenue à 0 ou à 1, ce qui effectue : soit une opération identité (l'opérande est recopié sur la sortie), soit une incrémentation. Un tel circuit est nommé un '''incrémenteur commandable'''. Nous aurons à utiliser une fois ou deux de tels incrémenteurs commandables dans la suite du cours.
L'incrémenteur à propagation de retenue est le plus simple et le plus économe en portes logiques. Mais de tels incrémenteurs sont rarement utilisés. À la place, on leur préfère des incrémenteurs plus rapides, mais qui utilisent plus de portes logiques. De tels incrémenteurs accélèrent le calcul des retenues. En effet, la rapidité d'une incrémentation est limitée par la propagation de la retenue : les retenues commencent à être calculées au bit de poids fort et on doit les calculer une par une, jusqu’à atteindre le bit de poids fort. Et cette "propagation des retenues" prend du temps, d'autant plus de temps que l'opérande est longue. Il y a deux optimisations principales, appelées le ''carry skip'' et l'anticipation de retenue, que nous allons décrire ci-dessous.
==Les incrémenteurs ''carry skip''==
L'optimisation '''''carry skip''''' effectue l'incrémentation, non pas bit par bit, mais par paquets de deux bits. Le résultat est que l'incrémentation est deux fois plus rapide, ou presque. Le circuit incrémenteur est donc composé en enchainant non pas des demi-additionneurs, mais des '''incrémenteurs 2 bits''' qui incrémentent un opérande de deux bits.
[[File:Circuit incrémenteur optimisé, faisant l'incrémentation par paquet de deux bits.png|centre|vignette|upright=2.5|Circuit incrémenteur optimisé, faisant l'incrémentation par paquet de deux bits]]
Reste à concevoir l'incrémenteur 2 bits.
===Les optimisations au niveau des transistors et l'exemple de l'incrémenteur du 8085 d'Intel===
Une première solution part de deux demi-additionneurs et effectue quelques simplifications au niveau des transistors et des portes logiques. Par exemple, l'Intel 8085 a utilisé un circuit optimisé, fabriqué avec des portes NAND et NOR. La raison est qu'avec les technologies de transistors CMOS actuelles, les portes NAND et NOR utilisent moins de transistors que les portes ET et OU. Et cette possibilité a été utilisée pour effectuer des simplifications assez mineures, mais toujours bonnes à prendre. Mais nous ne pouvons pas en parler en détail ici, car nous n'avons pas encore parlé en détail des transistors. Cependant, un exemple bien précis nous est accesible : celui de l’incrémenteur du processeur 8085 d'Intel.
Le processeur 8085 d'Intel est un processeur 8 bits très ancien. Il contenait un incrémenteur 16 bits, qui était utilisé pour calculer des adresses mémoire. Et cet incrémenteur était un incrémenteur à propagation de retenue optimisé. Il utilisait lui aussi des incrémenteurs 2 bits un peu modifiés.
L'idée était que les retenues sortant des colonnes paires étaient inversées, les colonnes impaires faisaient les calculs à partir de cette retenue inversée. Les demi-additionneurs des colonnes impaires n'étaient donc pas les mêmes que ceux des colonnes paires. Leurs tables de vérité sont différentes, leurs circuits aussi, et aucun des deux ne ressemble à un demi-additionneur normal. En réalité, ils sont fabriqués à partir des implémentations vues plus haut, qui utilisent uniquement des portes NAND ou uniquement des portes NOR.
Pour comprendre l'intérêt de faire ainsi, nous devons préciser une chose importante : avec les technologies CMOS utilisées pour les processeurs depuis les années 70, les portes logiques les plus simples sont les portes NON, NOR et NAND. Les portes ET/OU sont fabriquées en combinant des portes NOR/NAND avec une porte NON, ce qui prend plus de transistors.
Un demi-additionneur est donc fabriqué comme illustré ci-dessous, en logique CMOS. Seules les portes pour le calcul de la retenue sont indiquées, la porte XOR pour le bit de somme est fabriquée à partir de plusieurs portes logiques.
[[File:Demi-additionneur en CMOS.png|centre|vignette|upright=2|Demi-additionneur en CMOS]]
Si on enchaine deux demi-additionneurs, cela donne ceci :
[[File:Brique de base de l'incrémenteur du 8085.png|centre|vignette|upright=2|Brique de base de l'incrémenteur du 8085]]
La retenue doit donc traverser quatre portes logiques, dont deux portes NON. En inversant la retenue, les portes en jaune dans le schéma précédent se simplifient. Il suffit d'utiliser la loi de de Morgan pour trouver un circuit équivalent aux portes jaunes, qui n'est autre qu'une porte NOR couplée à une porte NON. Un autre moyen d'obtenir le même résultat est de prendre un demi-additionneur fabriqué avec uniquement des portes NOR, et de retirer la porte NON adéquate. Il faut ensuite corriger l'entrée de retenue de la porte XOR, pour qu'elle fasse l'addition avec la retenue correctement. Le résultat est illustré ci-dessous.
[[File:Incrémenteur 2 bits de l'incrémenteur du 8085.png|centre|vignette|upright=2|Incrémenteur 2 bits de l'incrémenteur du 8085]]
Le circuit utilise le même nombre de portes logiques, il n'économise pas de transistors. Par contre, la retenue n'a plus qu'à traverser deux portes logiques, au lieu de quatre. La propagation de la retenue est donc plus rapide, l'incrémentation est donc plus rapide.
===Les incrémenteurs 2 bits ''carry skip''===
Une solution alternative fabrique des incrémenteurs 2 bits qui calculent séparément les deux bits du résultat et la retenue sortante. La retenue sortante est calculée à partir des bits de l’opérande, sans propager des retenues. Ainsi, la retenue sortante est connue en avance, sans passer par deux demi-additionneurs. Pour créer un tel incrémenteur, le mieux est de partir de sa table de vérité.
{|class="wikitable"
|-
! Opérande !! Retenue entrante !! !! Retenue sortante !! Résultat
|-
| 00 || 0 || || 0 || 00
|-
| 01 || 0 || || 0 || 01
|-
| 10 || 0 || || 0 || 10
|-
| 11 || 0 || || 0 || 11
|-
| colspan="5" |
|-
| 00 || 1 || || 0 || 01
|-
| 01 || 1 || || 0 || 10
|-
| 10 || 1 || || 0 || 11
|-
| 11 || 1 || || 1 || 00
|}
Elle nous dit que la retenue sortante ne vaut 1 que dans un seul cas : les deux bits d'opérande valent 1, la retenue entrante vaut 1. Elle se calcule alors avec une porte ET à trois entrées. Pour les deux bits du résultat, les équations logiques ne donnent pas un résultat satisfaisant. La solution la plus simple est d'utiliser deux demi-additionneurs et de retirer les portes logiques superflues. Le résultat est alors un '''incrémenteur 2 bits ''carry skip''''', où la retenue sortante est calculée séparément du reste.
[[File:Incrémenteur carry skip 2 bits.png|centre|vignette|upright=2.5|Incrémenteur ''carry skip'' 2 bits]]
==Les incrémenteurs à anticipation de retenue==
L''''anticipation de retenue''' calcule la retenue d'une colonne sans attendre les retenues des colonnes précédentes. Dans le cas idéal, toutes les retenues sont calculées en parallèle, en même temps, et sont ensuite envoyées aux demi-additionneurs. Il s'agit d'une optimisation qui est utilisée pour l'incrémentation, l'addition et d'autres opérations similaires. Pour l'incrémentation, déterminer la retenue ne demande pas de calculs complexes, contrairement à l'addition.
[[File:Incrémenteur à anticipation de retenues, 8 bits.png|centre|vignette|upright=2.5|Incrémenteur à anticipation de retenues, 8 bits.]]
Pour le comprendre, on peut regarder comment la retenue sortant d'un demi-additionneur est formée. Elle est calculée en faisant un ET logique entre la retenue entrante et le bit d'opérande. Une retenue est donc un ET logique entre toutes les retenues précédentes. Un incrémenteur à anticipation de retenue utilise donc une porte ET à plusieurs entrées pour calculer une retenue.
[[File:Anticipation de retenue pour un bit du résultat, incrémenteur.png|centre|vignette|upright=2.5|Anticipation de retenue pour un bit du résultat, incrémenteur]]
En théorie, on peut utiliser une porte ET à plusieurs entrées pour chaque bit de l'opérande. Cependant, cela entrainera un cout en transistors très important. Pour éviter de gaspiller trop de portes logiques, une solution est de mélanger anticipation de retenues et propagation de retenue. Par exemple, pour un incrémenteur 32 bits, on peut découper l'opérande en 4 octets : on anticipe les retenues pour chaque octet, mais l'incrémentation de chaque octet se fait avec propagation de retenue et/ou ''carry skip''.
[[File:Incrémenteur à anticipation de retenues.png|centre|vignette|upright=3|Incrémenteur hybride utilisant partiellement l'anticipation de retenues.]]
Quelques processeurs utilisaient l'anticipation de retenues. Par exemple, le processeur Z-80 de Zilog utilisait un incrémenteur pour des nombres de 16 bits, ce qui demandait des performances assez élevées. Et cet incrémenteur utilisait à la fois anticipation de retenues et ''carry skip''. Il était découpé en quatre blocs avant anticipation de retenues entre eux : un bloc regroupant les 7 bits de poids faible, suivi par un bloc de 5 bits, lui-même suivi par un bloc de 3 bits, terminé par un dernier bit isolé. A l'intérieur de ces blocs, les bits sont regroupés en paires utilisant le ''carry skip''. Pour ceux qui veulent en savoir plus sur cet incrémenteur, voici un lien sur le sujet :
* [https://www.righto.com/2013/11/the-z-80s-16-bit-incrementdecrement.html The Z-80's 16-bit increment/decrement circuit reverse engineered].
==Les décrémenteurs==
Les circuits décrémenteurs font l'inverse d'un incrémenteur : au lieu d'ajouter 1 à un opérande, ils lui soustraient 1. La différence n'est pas énorme, ce qui fait que les circuits incrémenteurs et décrémenteurs se ressemblent beaucoup. Là où les incrémenteurs sont fabriqués à partir de demi-additionneurs, les décrémenteurs sont bâtis avec des demi-soustracteurs.
La soustraction se fait en binaire comme en décimal. On soustrait deux chiffres, puis on doit propager une éventuelle retenue sur la colonne suivante. La retenue apparait quand le chiffre soustrait est plus grand que l'autre chiffre. Elle est propagée sur la colonne suivante, où elle doit être soustraite du résultat. Pour le dire autrement, après avoir soustrait un chiffre, on doit de plus soustraire la retenue de la colonne précédente.
La décrémentation effectue le calcul suivant :
<math>a_7</math> <math>a_6</math> <math>a_5</math> <math>a_4</math> <math>a_3</math> <math>a_2</math> <math>a_1</math> <math>a_0</math>
- 0 0 0 0 0 0 0 1
La colonne la plus à droite soustrait un 1. Par contre, les colonnes suivantes soustraient un zéro. Soustraire un zéro ne change rien. Mais il ne faut pas oublier de soustraire une éventuellement retenue, propagée depuis la colonne précédente. En clair, un décrémenteur peut se construire si on sait soustraire deux bits. On soustrait un 1 sur la colonne la plus à droite, on soustrait une retenue sur les autres colonnes. Et pour soustraire deux bits, il faut utiliser un demi-soustracteur.
===Le demi-soustracteur===
Un '''demi-soustracteur''' soustrait deux bits. Pour comprendre comment soustraire deux bits, traitons les quatre cas possibles un par un.
Premièrement, soustraire zéro à un bit ne changera rien :
* 0 - 0 = 0 ;
* 1 - 0 = 1.
Si on soustrait 1 à un bit qui vaut 1, on obtient zéro.
* 1 - 1 = 0.
Maintenant, que se passe-t-il si on soustrait 1 à 0 ? Voici le résultat :
* 0 - 1 = 1 et une retenue propagée sur la colonne suivante, où elle est soustraite.
La table de soustraction est donc la suivante :
* 0 - 0 = 0 ;
* 0 - 1 = 1 et une retenue ;
* 1 - 0 = 1 ;
* 1 - 1 = 0.
Si on construit la table de vérité, et qu'on construit le circuit associé, on tombe sur ce circuit. Détail important : le demi-soustracteur fait ici la soustraction X - Y. En anglais, X est appelé le ''Minuend'' et Y est appelé le ''subtrahend'', mais ces termes n'ont pas de traduction française.
[[File:Half Subtractor Vektor.svg|centre|vignette|Demi-soustracteur.]]
===Le décrémenteur à propagation de retenue===
Un décrémenteur à propagation de retenue se construit comme un incrémenteur, sauf qu'on remplace les demi-additionneurs par des demi-soustracteurs. En clair, on enchaine plusieurs demi-soustracteurs les uns à la suite des autres. Il est possible d'utiliser l'anticipation de retenue, mais les circuits sont cependant différents de l'incrémenteur. Idem avec le ''carry skip'', et toute autre optimisation possible. La raison est que les retenues sont calculées différemment, ce qui fait que l’anticipation de retenue doit être adaptée.
==Les incrémenteurs-décrémenteurs==
Les circuits décrémenteurs ressemblent beaucoup aux incrémenteurs. Aussi, vous ne serez pas étonné qu'il soit possible de fusionner un incrémenteur et un décrémenteur en un seul circuit. Un tel circuit s'appelle un '''incrémenteur-décrémenteur'''. Un tel circuit a une interface similaire à celle d'un incrémenteur, avec une entrée sur laquelle présenter l'opérande, et une sortie sur laquelle récupérer le résultat. Il a aussi une entrée de retenue, sur laquelle envoyer un 1 (incrémentation) ou un 0 (opération NOP/identité). Et il a aussi une sortie de retenue, qui indique que le résultat du calcul a débordé.
Mais en plus de cela, il dispose d'une entrée de commande qui permet de choisir entre incrémentation et décrémentation.
Il existe plusieurs manières de créer un incrémenteur-décrémenteur, que nous allons voir si dessous. Nous n'allons pas voir le cas le plus évident, aussi ne vous étonnez pas si vous avez remarqué qu'un demi-additionneur et un demi-soustracteur se ressemblent beaucoup.
===L'incrémenteur-décrémenteur basé sur des demi-additionneurs===
Une première implémentation se base sur les propriétés du complément à deux, et plus précisément sur cette équation :
: <math>\overline{x} + 1 = \overline{x - 1}</math>
Les deux termes de cette équation sont simplement des définitions équivalentes du complément à deux d'un nombre entier. Celui-ci se calcule de deux manières : soit on incrémente son complément à 1, soit on prend le complément à 1 de son décrément. Et cela nous permet de déterminer comment obtenir une incrémentation à partir d'une décrémentation et inversement.
Que nous dit cette équation ? Simplement que l'on peut décrémenter un nombre si on a deux inverseurs commandables, et un circuit incrémenteur. La décrémentation se fait alors en deux étapes : on calcule son complément à deux, puis on inverse les bits du résultat précédent. Le calcul du complément à deux demande d'avoir un circuit inverseur, et un incrémenteur.
===L'incrémenteur-décrémenteur basé sur des demi-additionneurs===
Vous l'aurez sans doute remarqué, mais le demi-soustracteur ressemble beaucoup au demi-additionneur. La seule différence est une porte NON ajoutée au bon endroit dans le demi-soustracteur. La porte NON inverse le bit X, sachant que le demi-soustracteur fait la soustraction X - Y. De plus, l'opérande inversée est utilisée seulement pour le calcul des retenues, le bit de somme est calculé normalement.
{|
|[[File:Half-adder.svg|vignette|Demi-additionneur]]
|[[File:Half Subtractor Vektor.svg|vignette|upright=1.3|Demi-soustracteur]]
|}
La conséquence est qu'il est possible de fusionner un demi-additionneur avec un demi-soustracteur. L'idée est de remplacer la porte NON du demi-soustracteur par un inverseur commandable. Si on envoie un zéro sur l'entrée de commande, l'inverseur commandable se comportera comme une porte OUI et le circuit sera alors identique à un demi-additionneur. Si on envoie un 1 sur l'entrée de commande, l'inverseur commandable se comportera comme une porte NON. le circuit sera alors un demi-soustracteur. En remplaçant les demi-additionneurs (ou demi-soustracteurs) par de tels circuits, on peut obtenir un circuit faisant à la fois incrémenteur et décrémenteur.
Un défaut de cette approche est que les optimisations vues plus haut pour les demi-additionneurs ne fonctionne tout simplement plus. On est obligé d'utiliser une porte XOR et une porte ET séparées, pas question d'utiliser les demi-additionneurs optimisés vu au début du chapitre.
<noinclude>
{{NavChapitre | book=Fonctionnement d'un ordinateur
| prev=Les circuits de sélection
| prevText=Les circuits de sélection
| next=Les bascules : des mémoires de 1 bit
| nextText=Les bascules : des mémoires de 1 bit
}}
</noinclude>
9d3fiqikorn8f4500ey18p9cfy36pp5
768791
768790
2026-06-27T15:42:26Z
Mewtow
31375
/* Le demi-soustracteur */
768791
wikitext
text/x-wiki
Le circuit '''incrémenteur''' incrémente un nombre entier. Plus précisément, il prend en entrée un nombre entier, et fournit en entier le nombre suivant, il lui ajoute 1. Une telle opération peut paraitre triviale et peu intéressante. Cependant, elle est très utilisée et est absolument cruciale pour créer des compteurs, des circuits capables de compter ou décompter, qui auront droit à un chapitre dédié. De plus, au-delà de leur utilisation dans les compteurs, les incrémenteurs étaient très utilisés sur les premiers processeurs 8 bits, comme le Z-80, le 6502, les premiers processeurs x86 comme le 8008, le 8086, le 8085, et bien d'autres.
Il existe aussi des ''circuits décrémenteurs'', qui décrémentent un opérande, ainsi que des ''circuits incrémenteur-décrémenteur'', qui peuvent incrémenter ou décrémenter, selon comment on les configure. Je ne vais pas détailler ces circuits plus que ça, car de tels circuits sont assez rares, comparé à un circuit incrémenteur simple.
==Le demi-additionneur==
Le circuit incrémenteur effectue l'opération suivante :
<math>a_7</math> <math>a_6</math> <math>a_5</math> <math>a_4</math> <math>a_3</math> <math>a_2</math> <math>a_1</math> <math>a_0</math>
+ 0 0 0 0 0 0 0 1
Un incrémenteur basique doit donc faire une addition pour chaque colonne, et précisément une addition de deux bits. Il se trouve que la table d'addition est très simple en binaire. Jugez plutôt :
* 0 + 0 = 0, retenue = 0 ;
* 0 + 1 = 1, retenue = 0 ;
* 1 + 0 = 1, retenue = 0 ;
* 1 + 1 = 0, retenue = 1.
Un circuit capable d'additionner deux bits est appelé un '''demi-additionneur'''. Il dispose d'une sortie S pour la somme, et C pour la retenue (''carry'' an anglais).
===Le demi-additionneur classique===
Un demi-additionneur est très simple à construire avec les techniques vues dans les premiers chapitres. Voici sa table de vérité :
{|class="wikitable"
|-
! Retenue entrante !! Opérande 1 !! !! Retenue sortante !! Bit de somme
|-
||0||0|| ||0||0
|-
||0||1|| ||0||1
|-
||1||0|| ||0||1
|-
||1||1|| ||1||0
|}
On voit immédiatement que la colonne des retenues donne une porte ET, alors que celle du bit de somme est calculé par un XOR.
{| class="flexible"
|[[File:1-bit half-adder.svg|class=transparent|centre|Demi-addtionneur.]]
|[[File:Half-adder.svg|centre|class=transparent|Circuit d'un demi-addtionneur.]]
|}
Il existe beaucoup d'autres implémentations alternatives, qui utilisent moins de portes logiques, calculent la retenue plus rapidement, consomment moins d'énergie, et autres. Et il est intéressant d'étudier quelques alternatives, surtout qu'elles serviront plus bas, quand on étudiera le circuit incrémenteur du 8085.
===Le demi-additionneur basé sur une modification de la retenue===
Une source d'amélioration est liée à la porte XOR. En pratique, une porte XOR est composée en combinant plusieurs portes logiques ET/OU/NOR/NAND ensemble. Et il se trouve qu'il y a des redondances entre celles-ci et les portes utilisées pour calculer la retenue. Par exemple, rappelons qu'une porte XOR peut être construite avec une porte ET et deux portes NOR, comme illustré ci-dessous. Et il se trouve que la porte ET interne calcule la retenue sortante. En supprimant cette redondance, on économise quelques portes logiques. De plus, on se retrouve avec un demi-additionneur qui calcule le bit de somme à partir de la retenue sortante.
[[File:Demi-additionneur avec redondances éliminées.png|centre|vignette|upright=2|Demi-additionneur avec redondances éliminées]]
Le circuit précédent a une interprétation logique. Si vous regardez la table de vérité, vous remarquerez que la somme de deux bits est égale à l'inverse de la retenue sortante, sauf dans le cas où les deux bits additionnés valent zéro. Et le circuit précédent est basé là-dessus.
{|class="wikitable"
|-
! Retenue entrante !! Opérande 1 !! !! Retenue sortante !! Bit de somme
|- class="f_vert"
||0||0|| ||0||0
|- class="f_rouge"
||0||1|| ||0||1
|- class="f_rouge"
||1||0|| ||0||1
|- class="f_rouge"
||1||1|| ||1||0
|}
L'idée est de calculer l'inverse du bit de somme, avant de l'inverser avec une porte NON. L'inverse du bit de somme vaut 1, soit quand la retenue est à 1, soit quand les deux bits additionnés sont à 0. La porte ET calcule la retenue sortante, la première porte NOR détecte sur les deux bits d'entrée valent zéro. Une porte OU combine les deux résultat pour obtenir l'inverse du bit d'entrée, puis une porte NOn inverse le tout pour obtenir le bit de somme adéquat. Les deux portes sont fusionnées : c'est la seconde porte NOR.
[[File:Full adder HA + MUX.png|centre|vignette|upright=2.5|Demi-additionneur basé sur une inversion de la retenue sortante.]]
===Les implémentations alternatives===
Une implémentation alternative, qui n'utilise que des portes NOR, est la suivante :
[[File:Demi-additionneur fait avec des portes NOR et NAND.png|centre|vignette|upright=2|Demi-additionneur fait avec des portes NOR et NAND.]]
Une implémentation alternative utilise des portes NAND. Pour rappel, il est possible de créer une porte XOR avec 4 portes NAND. La première d'entre elle fait un NAND entre les deux bits d'entrée, ce qui fait qu'elle calcule l'inverse de la retenue sortante. Le tout est illustré ci-dessous. En théorie, on devrait utiliser une porte NON pour récupérer la retenue correcte. Mais nous verrons plus bas que ce n'est pas une obligation. Nous verrons plus bas un exemple où c'est l'inverse de la retenue qui est utilisée dans les calculs, pour rendre les calculs plus rapides.
[[File:Demi-additionneur fait avec des portes NAND.png|centre|vignette|upright=2|Demi-additionneur fait avec des portes NAND]]
==L'incrémenteur à propagation de retenue==
Maintenant que l'on sait comment additionner deux bits, reprenons l'opération d'incrémentation :
<math>a_7</math> <math>a_6</math> <math>a_5</math> <math>a_4</math> <math>a_3</math> <math>a_2</math> <math>a_1</math> <math>a_0</math>
+ 0 0 0 0 0 0 0 1
------------------------------
Sur la colonne la plus à droite, il doit ajouter un au bit de poids faible. Pour les colonnes suivantes, il faut additionner le bit de l'opérande avec la retenue de la colonne précédente. En clair, on n'additionne que deux bits à chaque colonne : un 1 sur celle tout à droite, la retenue de la colonne précédente pour les autres. Et cela nous donne une idée de comment faire pour créer le circuit incrémenteur.
Il suffit d'utiliser un demi-additionneur par colonne, et de les enchainer les uns à la suite des autres. Chaque demi-additionneur additionne le bit de l'opérande avec la retenue fournie par le demi-additionneur précédent. Le seul qui fait exception est celui pour la colonne de poids faible. Pour celui-là, il doit ajouter 1 au bit de poids faible. Le résultat est appelé un '''incrémenteur à propagation de retenue'''. Il est constitué de demi-additionneurs enchaînés les uns à la suite des autres, du bit de poids faible vers le bit de poids fort.
[[File:Circuit incrémenteur.png|centre|vignette|upright=3|Circuit incrémenteur.]]
Maintenant, regardons le demi-additionneur le plus à droite, celui pour le bit de poids faible. Son entrée de retenue entrante est mise à 1 pour faire l'incrémentation. Quelques incrémenteurs permettent de configurer cette entrée de retenue à 0 ou à 1, ce qui effectue : soit une opération identité (l'opérande est recopié sur la sortie), soit une incrémentation. Un tel circuit est nommé un '''incrémenteur commandable'''. Nous aurons à utiliser une fois ou deux de tels incrémenteurs commandables dans la suite du cours.
L'incrémenteur à propagation de retenue est le plus simple et le plus économe en portes logiques. Mais de tels incrémenteurs sont rarement utilisés. À la place, on leur préfère des incrémenteurs plus rapides, mais qui utilisent plus de portes logiques. De tels incrémenteurs accélèrent le calcul des retenues. En effet, la rapidité d'une incrémentation est limitée par la propagation de la retenue : les retenues commencent à être calculées au bit de poids fort et on doit les calculer une par une, jusqu’à atteindre le bit de poids fort. Et cette "propagation des retenues" prend du temps, d'autant plus de temps que l'opérande est longue. Il y a deux optimisations principales, appelées le ''carry skip'' et l'anticipation de retenue, que nous allons décrire ci-dessous.
==Les incrémenteurs ''carry skip''==
L'optimisation '''''carry skip''''' effectue l'incrémentation, non pas bit par bit, mais par paquets de deux bits. Le résultat est que l'incrémentation est deux fois plus rapide, ou presque. Le circuit incrémenteur est donc composé en enchainant non pas des demi-additionneurs, mais des '''incrémenteurs 2 bits''' qui incrémentent un opérande de deux bits.
[[File:Circuit incrémenteur optimisé, faisant l'incrémentation par paquet de deux bits.png|centre|vignette|upright=2.5|Circuit incrémenteur optimisé, faisant l'incrémentation par paquet de deux bits]]
Reste à concevoir l'incrémenteur 2 bits.
===Les optimisations au niveau des transistors et l'exemple de l'incrémenteur du 8085 d'Intel===
Une première solution part de deux demi-additionneurs et effectue quelques simplifications au niveau des transistors et des portes logiques. Par exemple, l'Intel 8085 a utilisé un circuit optimisé, fabriqué avec des portes NAND et NOR. La raison est qu'avec les technologies de transistors CMOS actuelles, les portes NAND et NOR utilisent moins de transistors que les portes ET et OU. Et cette possibilité a été utilisée pour effectuer des simplifications assez mineures, mais toujours bonnes à prendre. Mais nous ne pouvons pas en parler en détail ici, car nous n'avons pas encore parlé en détail des transistors. Cependant, un exemple bien précis nous est accesible : celui de l’incrémenteur du processeur 8085 d'Intel.
Le processeur 8085 d'Intel est un processeur 8 bits très ancien. Il contenait un incrémenteur 16 bits, qui était utilisé pour calculer des adresses mémoire. Et cet incrémenteur était un incrémenteur à propagation de retenue optimisé. Il utilisait lui aussi des incrémenteurs 2 bits un peu modifiés.
L'idée était que les retenues sortant des colonnes paires étaient inversées, les colonnes impaires faisaient les calculs à partir de cette retenue inversée. Les demi-additionneurs des colonnes impaires n'étaient donc pas les mêmes que ceux des colonnes paires. Leurs tables de vérité sont différentes, leurs circuits aussi, et aucun des deux ne ressemble à un demi-additionneur normal. En réalité, ils sont fabriqués à partir des implémentations vues plus haut, qui utilisent uniquement des portes NAND ou uniquement des portes NOR.
Pour comprendre l'intérêt de faire ainsi, nous devons préciser une chose importante : avec les technologies CMOS utilisées pour les processeurs depuis les années 70, les portes logiques les plus simples sont les portes NON, NOR et NAND. Les portes ET/OU sont fabriquées en combinant des portes NOR/NAND avec une porte NON, ce qui prend plus de transistors.
Un demi-additionneur est donc fabriqué comme illustré ci-dessous, en logique CMOS. Seules les portes pour le calcul de la retenue sont indiquées, la porte XOR pour le bit de somme est fabriquée à partir de plusieurs portes logiques.
[[File:Demi-additionneur en CMOS.png|centre|vignette|upright=2|Demi-additionneur en CMOS]]
Si on enchaine deux demi-additionneurs, cela donne ceci :
[[File:Brique de base de l'incrémenteur du 8085.png|centre|vignette|upright=2|Brique de base de l'incrémenteur du 8085]]
La retenue doit donc traverser quatre portes logiques, dont deux portes NON. En inversant la retenue, les portes en jaune dans le schéma précédent se simplifient. Il suffit d'utiliser la loi de de Morgan pour trouver un circuit équivalent aux portes jaunes, qui n'est autre qu'une porte NOR couplée à une porte NON. Un autre moyen d'obtenir le même résultat est de prendre un demi-additionneur fabriqué avec uniquement des portes NOR, et de retirer la porte NON adéquate. Il faut ensuite corriger l'entrée de retenue de la porte XOR, pour qu'elle fasse l'addition avec la retenue correctement. Le résultat est illustré ci-dessous.
[[File:Incrémenteur 2 bits de l'incrémenteur du 8085.png|centre|vignette|upright=2|Incrémenteur 2 bits de l'incrémenteur du 8085]]
Le circuit utilise le même nombre de portes logiques, il n'économise pas de transistors. Par contre, la retenue n'a plus qu'à traverser deux portes logiques, au lieu de quatre. La propagation de la retenue est donc plus rapide, l'incrémentation est donc plus rapide.
===Les incrémenteurs 2 bits ''carry skip''===
Une solution alternative fabrique des incrémenteurs 2 bits qui calculent séparément les deux bits du résultat et la retenue sortante. La retenue sortante est calculée à partir des bits de l’opérande, sans propager des retenues. Ainsi, la retenue sortante est connue en avance, sans passer par deux demi-additionneurs. Pour créer un tel incrémenteur, le mieux est de partir de sa table de vérité.
{|class="wikitable"
|-
! Opérande !! Retenue entrante !! !! Retenue sortante !! Résultat
|-
| 00 || 0 || || 0 || 00
|-
| 01 || 0 || || 0 || 01
|-
| 10 || 0 || || 0 || 10
|-
| 11 || 0 || || 0 || 11
|-
| colspan="5" |
|-
| 00 || 1 || || 0 || 01
|-
| 01 || 1 || || 0 || 10
|-
| 10 || 1 || || 0 || 11
|-
| 11 || 1 || || 1 || 00
|}
Elle nous dit que la retenue sortante ne vaut 1 que dans un seul cas : les deux bits d'opérande valent 1, la retenue entrante vaut 1. Elle se calcule alors avec une porte ET à trois entrées. Pour les deux bits du résultat, les équations logiques ne donnent pas un résultat satisfaisant. La solution la plus simple est d'utiliser deux demi-additionneurs et de retirer les portes logiques superflues. Le résultat est alors un '''incrémenteur 2 bits ''carry skip''''', où la retenue sortante est calculée séparément du reste.
[[File:Incrémenteur carry skip 2 bits.png|centre|vignette|upright=2.5|Incrémenteur ''carry skip'' 2 bits]]
==Les incrémenteurs à anticipation de retenue==
L''''anticipation de retenue''' calcule la retenue d'une colonne sans attendre les retenues des colonnes précédentes. Dans le cas idéal, toutes les retenues sont calculées en parallèle, en même temps, et sont ensuite envoyées aux demi-additionneurs. Il s'agit d'une optimisation qui est utilisée pour l'incrémentation, l'addition et d'autres opérations similaires. Pour l'incrémentation, déterminer la retenue ne demande pas de calculs complexes, contrairement à l'addition.
[[File:Incrémenteur à anticipation de retenues, 8 bits.png|centre|vignette|upright=2.5|Incrémenteur à anticipation de retenues, 8 bits.]]
Pour le comprendre, on peut regarder comment la retenue sortant d'un demi-additionneur est formée. Elle est calculée en faisant un ET logique entre la retenue entrante et le bit d'opérande. Une retenue est donc un ET logique entre toutes les retenues précédentes. Un incrémenteur à anticipation de retenue utilise donc une porte ET à plusieurs entrées pour calculer une retenue.
[[File:Anticipation de retenue pour un bit du résultat, incrémenteur.png|centre|vignette|upright=2.5|Anticipation de retenue pour un bit du résultat, incrémenteur]]
En théorie, on peut utiliser une porte ET à plusieurs entrées pour chaque bit de l'opérande. Cependant, cela entrainera un cout en transistors très important. Pour éviter de gaspiller trop de portes logiques, une solution est de mélanger anticipation de retenues et propagation de retenue. Par exemple, pour un incrémenteur 32 bits, on peut découper l'opérande en 4 octets : on anticipe les retenues pour chaque octet, mais l'incrémentation de chaque octet se fait avec propagation de retenue et/ou ''carry skip''.
[[File:Incrémenteur à anticipation de retenues.png|centre|vignette|upright=3|Incrémenteur hybride utilisant partiellement l'anticipation de retenues.]]
Quelques processeurs utilisaient l'anticipation de retenues. Par exemple, le processeur Z-80 de Zilog utilisait un incrémenteur pour des nombres de 16 bits, ce qui demandait des performances assez élevées. Et cet incrémenteur utilisait à la fois anticipation de retenues et ''carry skip''. Il était découpé en quatre blocs avant anticipation de retenues entre eux : un bloc regroupant les 7 bits de poids faible, suivi par un bloc de 5 bits, lui-même suivi par un bloc de 3 bits, terminé par un dernier bit isolé. A l'intérieur de ces blocs, les bits sont regroupés en paires utilisant le ''carry skip''. Pour ceux qui veulent en savoir plus sur cet incrémenteur, voici un lien sur le sujet :
* [https://www.righto.com/2013/11/the-z-80s-16-bit-incrementdecrement.html The Z-80's 16-bit increment/decrement circuit reverse engineered].
==Les décrémenteurs==
Les circuits décrémenteurs font l'inverse d'un incrémenteur : au lieu d'ajouter 1 à un opérande, ils lui soustraient 1. La différence n'est pas énorme, ce qui fait que les circuits incrémenteurs et décrémenteurs se ressemblent beaucoup. Là où les incrémenteurs sont fabriqués à partir de demi-additionneurs, les décrémenteurs sont bâtis avec des demi-soustracteurs.
La soustraction se fait en binaire comme en décimal. On soustrait deux chiffres, puis on doit propager une éventuelle retenue sur la colonne suivante. La retenue apparait quand le chiffre soustrait est plus grand que l'autre chiffre. Elle est propagée sur la colonne suivante, où elle doit être soustraite du résultat. Pour le dire autrement, après avoir soustrait un chiffre, on doit de plus soustraire la retenue de la colonne précédente.
La décrémentation effectue le calcul suivant :
<math>a_7</math> <math>a_6</math> <math>a_5</math> <math>a_4</math> <math>a_3</math> <math>a_2</math> <math>a_1</math> <math>a_0</math>
- 0 0 0 0 0 0 0 1
La colonne la plus à droite soustrait un 1. Par contre, les colonnes suivantes soustraient un zéro. Soustraire un zéro ne change rien. Mais il ne faut pas oublier de soustraire une éventuellement retenue, propagée depuis la colonne précédente. En clair, un décrémenteur peut se construire si on sait soustraire deux bits. On soustrait un 1 sur la colonne la plus à droite, on soustrait une retenue sur les autres colonnes. Et pour soustraire deux bits, il faut utiliser un demi-soustracteur.
===Le demi-soustracteur===
Un '''demi-soustracteur''' soustrait deux bits. Pour comprendre comment soustraire deux bits, traitons les quatre cas possibles un par un.
Premièrement, soustraire zéro à un bit ne changera rien :
* 0 - 0 = 0 ;
* 1 - 0 = 1.
Si on soustrait 1 à un bit qui vaut 1, on obtient zéro.
* 1 - 1 = 0.
Maintenant, que se passe-t-il si on soustrait 1 à 0 ? Voici le résultat :
* 0 - 1 = 1 et une retenue propagée sur la colonne suivante, où elle est soustraite.
La table de soustraction est donc la suivante :
* 0 - 0 = 0 ;
* 0 - 1 = 1 et une retenue ;
* 1 - 0 = 1 ;
* 1 - 1 = 0.
La table de vérité est donc la suivante. Détail important : le demi-soustracteur fait ici la soustraction X - Y. En anglais, X est appelé le ''Minuend'' et Y est appelé le ''Subtrahend'', mais ces termes n'ont pas de traduction française. Aussi, je vais les utiliser tels quels dans ce qui suit.
{|class="wikitable"
|-
! ''Minuend'' !! ''Subtrahend'' !! !! Retenue sortante !! Bit de somme
|-
||0||0|| ||0||0
|-
||0||1|| ||1||1
|-
||1||0|| ||0||1
|-
||1||1|| ||0||0
|}
Si on construit la table de vérité, et qu'on construit le circuit associé, on tombe sur ce circuit.
[[File:Half Subtractor Vektor.svg|centre|vignette|Demi-soustracteur.]]
Vous remarquerez que le bit de somme se calcule de la même manière que pour l'addition, seul le calcul de la retenue change.
===Le décrémenteur à propagation de retenue===
Un décrémenteur à propagation de retenue se construit comme un incrémenteur, sauf qu'on remplace les demi-additionneurs par des demi-soustracteurs. En clair, on enchaine plusieurs demi-soustracteurs les uns à la suite des autres. Il est possible d'utiliser l'anticipation de retenue, mais les circuits sont cependant différents de l'incrémenteur. Idem avec le ''carry skip'', et toute autre optimisation possible. La raison est que les retenues sont calculées différemment, ce qui fait que l’anticipation de retenue doit être adaptée.
==Les incrémenteurs-décrémenteurs==
Les circuits décrémenteurs ressemblent beaucoup aux incrémenteurs. Aussi, vous ne serez pas étonné qu'il soit possible de fusionner un incrémenteur et un décrémenteur en un seul circuit. Un tel circuit s'appelle un '''incrémenteur-décrémenteur'''. Un tel circuit a une interface similaire à celle d'un incrémenteur, avec une entrée sur laquelle présenter l'opérande, et une sortie sur laquelle récupérer le résultat. Il a aussi une entrée de retenue, sur laquelle envoyer un 1 (incrémentation) ou un 0 (opération NOP/identité). Et il a aussi une sortie de retenue, qui indique que le résultat du calcul a débordé.
Mais en plus de cela, il dispose d'une entrée de commande qui permet de choisir entre incrémentation et décrémentation.
Il existe plusieurs manières de créer un incrémenteur-décrémenteur, que nous allons voir si dessous. Nous n'allons pas voir le cas le plus évident, aussi ne vous étonnez pas si vous avez remarqué qu'un demi-additionneur et un demi-soustracteur se ressemblent beaucoup.
===L'incrémenteur-décrémenteur basé sur des demi-additionneurs===
Une première implémentation se base sur les propriétés du complément à deux, et plus précisément sur cette équation :
: <math>\overline{x} + 1 = \overline{x - 1}</math>
Les deux termes de cette équation sont simplement des définitions équivalentes du complément à deux d'un nombre entier. Celui-ci se calcule de deux manières : soit on incrémente son complément à 1, soit on prend le complément à 1 de son décrément. Et cela nous permet de déterminer comment obtenir une incrémentation à partir d'une décrémentation et inversement.
Que nous dit cette équation ? Simplement que l'on peut décrémenter un nombre si on a deux inverseurs commandables, et un circuit incrémenteur. La décrémentation se fait alors en deux étapes : on calcule son complément à deux, puis on inverse les bits du résultat précédent. Le calcul du complément à deux demande d'avoir un circuit inverseur, et un incrémenteur.
===L'incrémenteur-décrémenteur basé sur des demi-additionneurs===
Vous l'aurez sans doute remarqué, mais le demi-soustracteur ressemble beaucoup au demi-additionneur. La seule différence est une porte NON ajoutée au bon endroit dans le demi-soustracteur. La porte NON inverse le bit X, sachant que le demi-soustracteur fait la soustraction X - Y. De plus, l'opérande inversée est utilisée seulement pour le calcul des retenues, le bit de somme est calculé normalement.
{|
|[[File:Half-adder.svg|vignette|Demi-additionneur]]
|[[File:Half Subtractor Vektor.svg|vignette|upright=1.3|Demi-soustracteur]]
|}
La conséquence est qu'il est possible de fusionner un demi-additionneur avec un demi-soustracteur. L'idée est de remplacer la porte NON du demi-soustracteur par un inverseur commandable. Si on envoie un zéro sur l'entrée de commande, l'inverseur commandable se comportera comme une porte OUI et le circuit sera alors identique à un demi-additionneur. Si on envoie un 1 sur l'entrée de commande, l'inverseur commandable se comportera comme une porte NON. le circuit sera alors un demi-soustracteur. En remplaçant les demi-additionneurs (ou demi-soustracteurs) par de tels circuits, on peut obtenir un circuit faisant à la fois incrémenteur et décrémenteur.
Un défaut de cette approche est que les optimisations vues plus haut pour les demi-additionneurs ne fonctionne tout simplement plus. On est obligé d'utiliser une porte XOR et une porte ET séparées, pas question d'utiliser les demi-additionneurs optimisés vu au début du chapitre.
<noinclude>
{{NavChapitre | book=Fonctionnement d'un ordinateur
| prev=Les circuits de sélection
| prevText=Les circuits de sélection
| next=Les bascules : des mémoires de 1 bit
| nextText=Les bascules : des mémoires de 1 bit
}}
</noinclude>
3sqk0hghnukhhw7porx4rna8x38mse3
768792
768791
2026-06-27T15:43:20Z
Mewtow
31375
/* Le décrémenteur à propagation de retenue */
768792
wikitext
text/x-wiki
Le circuit '''incrémenteur''' incrémente un nombre entier. Plus précisément, il prend en entrée un nombre entier, et fournit en entier le nombre suivant, il lui ajoute 1. Une telle opération peut paraitre triviale et peu intéressante. Cependant, elle est très utilisée et est absolument cruciale pour créer des compteurs, des circuits capables de compter ou décompter, qui auront droit à un chapitre dédié. De plus, au-delà de leur utilisation dans les compteurs, les incrémenteurs étaient très utilisés sur les premiers processeurs 8 bits, comme le Z-80, le 6502, les premiers processeurs x86 comme le 8008, le 8086, le 8085, et bien d'autres.
Il existe aussi des ''circuits décrémenteurs'', qui décrémentent un opérande, ainsi que des ''circuits incrémenteur-décrémenteur'', qui peuvent incrémenter ou décrémenter, selon comment on les configure. Je ne vais pas détailler ces circuits plus que ça, car de tels circuits sont assez rares, comparé à un circuit incrémenteur simple.
==Le demi-additionneur==
Le circuit incrémenteur effectue l'opération suivante :
<math>a_7</math> <math>a_6</math> <math>a_5</math> <math>a_4</math> <math>a_3</math> <math>a_2</math> <math>a_1</math> <math>a_0</math>
+ 0 0 0 0 0 0 0 1
Un incrémenteur basique doit donc faire une addition pour chaque colonne, et précisément une addition de deux bits. Il se trouve que la table d'addition est très simple en binaire. Jugez plutôt :
* 0 + 0 = 0, retenue = 0 ;
* 0 + 1 = 1, retenue = 0 ;
* 1 + 0 = 1, retenue = 0 ;
* 1 + 1 = 0, retenue = 1.
Un circuit capable d'additionner deux bits est appelé un '''demi-additionneur'''. Il dispose d'une sortie S pour la somme, et C pour la retenue (''carry'' an anglais).
===Le demi-additionneur classique===
Un demi-additionneur est très simple à construire avec les techniques vues dans les premiers chapitres. Voici sa table de vérité :
{|class="wikitable"
|-
! Retenue entrante !! Opérande 1 !! !! Retenue sortante !! Bit de somme
|-
||0||0|| ||0||0
|-
||0||1|| ||0||1
|-
||1||0|| ||0||1
|-
||1||1|| ||1||0
|}
On voit immédiatement que la colonne des retenues donne une porte ET, alors que celle du bit de somme est calculé par un XOR.
{| class="flexible"
|[[File:1-bit half-adder.svg|class=transparent|centre|Demi-addtionneur.]]
|[[File:Half-adder.svg|centre|class=transparent|Circuit d'un demi-addtionneur.]]
|}
Il existe beaucoup d'autres implémentations alternatives, qui utilisent moins de portes logiques, calculent la retenue plus rapidement, consomment moins d'énergie, et autres. Et il est intéressant d'étudier quelques alternatives, surtout qu'elles serviront plus bas, quand on étudiera le circuit incrémenteur du 8085.
===Le demi-additionneur basé sur une modification de la retenue===
Une source d'amélioration est liée à la porte XOR. En pratique, une porte XOR est composée en combinant plusieurs portes logiques ET/OU/NOR/NAND ensemble. Et il se trouve qu'il y a des redondances entre celles-ci et les portes utilisées pour calculer la retenue. Par exemple, rappelons qu'une porte XOR peut être construite avec une porte ET et deux portes NOR, comme illustré ci-dessous. Et il se trouve que la porte ET interne calcule la retenue sortante. En supprimant cette redondance, on économise quelques portes logiques. De plus, on se retrouve avec un demi-additionneur qui calcule le bit de somme à partir de la retenue sortante.
[[File:Demi-additionneur avec redondances éliminées.png|centre|vignette|upright=2|Demi-additionneur avec redondances éliminées]]
Le circuit précédent a une interprétation logique. Si vous regardez la table de vérité, vous remarquerez que la somme de deux bits est égale à l'inverse de la retenue sortante, sauf dans le cas où les deux bits additionnés valent zéro. Et le circuit précédent est basé là-dessus.
{|class="wikitable"
|-
! Retenue entrante !! Opérande 1 !! !! Retenue sortante !! Bit de somme
|- class="f_vert"
||0||0|| ||0||0
|- class="f_rouge"
||0||1|| ||0||1
|- class="f_rouge"
||1||0|| ||0||1
|- class="f_rouge"
||1||1|| ||1||0
|}
L'idée est de calculer l'inverse du bit de somme, avant de l'inverser avec une porte NON. L'inverse du bit de somme vaut 1, soit quand la retenue est à 1, soit quand les deux bits additionnés sont à 0. La porte ET calcule la retenue sortante, la première porte NOR détecte sur les deux bits d'entrée valent zéro. Une porte OU combine les deux résultat pour obtenir l'inverse du bit d'entrée, puis une porte NOn inverse le tout pour obtenir le bit de somme adéquat. Les deux portes sont fusionnées : c'est la seconde porte NOR.
[[File:Full adder HA + MUX.png|centre|vignette|upright=2.5|Demi-additionneur basé sur une inversion de la retenue sortante.]]
===Les implémentations alternatives===
Une implémentation alternative, qui n'utilise que des portes NOR, est la suivante :
[[File:Demi-additionneur fait avec des portes NOR et NAND.png|centre|vignette|upright=2|Demi-additionneur fait avec des portes NOR et NAND.]]
Une implémentation alternative utilise des portes NAND. Pour rappel, il est possible de créer une porte XOR avec 4 portes NAND. La première d'entre elle fait un NAND entre les deux bits d'entrée, ce qui fait qu'elle calcule l'inverse de la retenue sortante. Le tout est illustré ci-dessous. En théorie, on devrait utiliser une porte NON pour récupérer la retenue correcte. Mais nous verrons plus bas que ce n'est pas une obligation. Nous verrons plus bas un exemple où c'est l'inverse de la retenue qui est utilisée dans les calculs, pour rendre les calculs plus rapides.
[[File:Demi-additionneur fait avec des portes NAND.png|centre|vignette|upright=2|Demi-additionneur fait avec des portes NAND]]
==L'incrémenteur à propagation de retenue==
Maintenant que l'on sait comment additionner deux bits, reprenons l'opération d'incrémentation :
<math>a_7</math> <math>a_6</math> <math>a_5</math> <math>a_4</math> <math>a_3</math> <math>a_2</math> <math>a_1</math> <math>a_0</math>
+ 0 0 0 0 0 0 0 1
------------------------------
Sur la colonne la plus à droite, il doit ajouter un au bit de poids faible. Pour les colonnes suivantes, il faut additionner le bit de l'opérande avec la retenue de la colonne précédente. En clair, on n'additionne que deux bits à chaque colonne : un 1 sur celle tout à droite, la retenue de la colonne précédente pour les autres. Et cela nous donne une idée de comment faire pour créer le circuit incrémenteur.
Il suffit d'utiliser un demi-additionneur par colonne, et de les enchainer les uns à la suite des autres. Chaque demi-additionneur additionne le bit de l'opérande avec la retenue fournie par le demi-additionneur précédent. Le seul qui fait exception est celui pour la colonne de poids faible. Pour celui-là, il doit ajouter 1 au bit de poids faible. Le résultat est appelé un '''incrémenteur à propagation de retenue'''. Il est constitué de demi-additionneurs enchaînés les uns à la suite des autres, du bit de poids faible vers le bit de poids fort.
[[File:Circuit incrémenteur.png|centre|vignette|upright=3|Circuit incrémenteur.]]
Maintenant, regardons le demi-additionneur le plus à droite, celui pour le bit de poids faible. Son entrée de retenue entrante est mise à 1 pour faire l'incrémentation. Quelques incrémenteurs permettent de configurer cette entrée de retenue à 0 ou à 1, ce qui effectue : soit une opération identité (l'opérande est recopié sur la sortie), soit une incrémentation. Un tel circuit est nommé un '''incrémenteur commandable'''. Nous aurons à utiliser une fois ou deux de tels incrémenteurs commandables dans la suite du cours.
L'incrémenteur à propagation de retenue est le plus simple et le plus économe en portes logiques. Mais de tels incrémenteurs sont rarement utilisés. À la place, on leur préfère des incrémenteurs plus rapides, mais qui utilisent plus de portes logiques. De tels incrémenteurs accélèrent le calcul des retenues. En effet, la rapidité d'une incrémentation est limitée par la propagation de la retenue : les retenues commencent à être calculées au bit de poids fort et on doit les calculer une par une, jusqu’à atteindre le bit de poids fort. Et cette "propagation des retenues" prend du temps, d'autant plus de temps que l'opérande est longue. Il y a deux optimisations principales, appelées le ''carry skip'' et l'anticipation de retenue, que nous allons décrire ci-dessous.
==Les incrémenteurs ''carry skip''==
L'optimisation '''''carry skip''''' effectue l'incrémentation, non pas bit par bit, mais par paquets de deux bits. Le résultat est que l'incrémentation est deux fois plus rapide, ou presque. Le circuit incrémenteur est donc composé en enchainant non pas des demi-additionneurs, mais des '''incrémenteurs 2 bits''' qui incrémentent un opérande de deux bits.
[[File:Circuit incrémenteur optimisé, faisant l'incrémentation par paquet de deux bits.png|centre|vignette|upright=2.5|Circuit incrémenteur optimisé, faisant l'incrémentation par paquet de deux bits]]
Reste à concevoir l'incrémenteur 2 bits.
===Les optimisations au niveau des transistors et l'exemple de l'incrémenteur du 8085 d'Intel===
Une première solution part de deux demi-additionneurs et effectue quelques simplifications au niveau des transistors et des portes logiques. Par exemple, l'Intel 8085 a utilisé un circuit optimisé, fabriqué avec des portes NAND et NOR. La raison est qu'avec les technologies de transistors CMOS actuelles, les portes NAND et NOR utilisent moins de transistors que les portes ET et OU. Et cette possibilité a été utilisée pour effectuer des simplifications assez mineures, mais toujours bonnes à prendre. Mais nous ne pouvons pas en parler en détail ici, car nous n'avons pas encore parlé en détail des transistors. Cependant, un exemple bien précis nous est accesible : celui de l’incrémenteur du processeur 8085 d'Intel.
Le processeur 8085 d'Intel est un processeur 8 bits très ancien. Il contenait un incrémenteur 16 bits, qui était utilisé pour calculer des adresses mémoire. Et cet incrémenteur était un incrémenteur à propagation de retenue optimisé. Il utilisait lui aussi des incrémenteurs 2 bits un peu modifiés.
L'idée était que les retenues sortant des colonnes paires étaient inversées, les colonnes impaires faisaient les calculs à partir de cette retenue inversée. Les demi-additionneurs des colonnes impaires n'étaient donc pas les mêmes que ceux des colonnes paires. Leurs tables de vérité sont différentes, leurs circuits aussi, et aucun des deux ne ressemble à un demi-additionneur normal. En réalité, ils sont fabriqués à partir des implémentations vues plus haut, qui utilisent uniquement des portes NAND ou uniquement des portes NOR.
Pour comprendre l'intérêt de faire ainsi, nous devons préciser une chose importante : avec les technologies CMOS utilisées pour les processeurs depuis les années 70, les portes logiques les plus simples sont les portes NON, NOR et NAND. Les portes ET/OU sont fabriquées en combinant des portes NOR/NAND avec une porte NON, ce qui prend plus de transistors.
Un demi-additionneur est donc fabriqué comme illustré ci-dessous, en logique CMOS. Seules les portes pour le calcul de la retenue sont indiquées, la porte XOR pour le bit de somme est fabriquée à partir de plusieurs portes logiques.
[[File:Demi-additionneur en CMOS.png|centre|vignette|upright=2|Demi-additionneur en CMOS]]
Si on enchaine deux demi-additionneurs, cela donne ceci :
[[File:Brique de base de l'incrémenteur du 8085.png|centre|vignette|upright=2|Brique de base de l'incrémenteur du 8085]]
La retenue doit donc traverser quatre portes logiques, dont deux portes NON. En inversant la retenue, les portes en jaune dans le schéma précédent se simplifient. Il suffit d'utiliser la loi de de Morgan pour trouver un circuit équivalent aux portes jaunes, qui n'est autre qu'une porte NOR couplée à une porte NON. Un autre moyen d'obtenir le même résultat est de prendre un demi-additionneur fabriqué avec uniquement des portes NOR, et de retirer la porte NON adéquate. Il faut ensuite corriger l'entrée de retenue de la porte XOR, pour qu'elle fasse l'addition avec la retenue correctement. Le résultat est illustré ci-dessous.
[[File:Incrémenteur 2 bits de l'incrémenteur du 8085.png|centre|vignette|upright=2|Incrémenteur 2 bits de l'incrémenteur du 8085]]
Le circuit utilise le même nombre de portes logiques, il n'économise pas de transistors. Par contre, la retenue n'a plus qu'à traverser deux portes logiques, au lieu de quatre. La propagation de la retenue est donc plus rapide, l'incrémentation est donc plus rapide.
===Les incrémenteurs 2 bits ''carry skip''===
Une solution alternative fabrique des incrémenteurs 2 bits qui calculent séparément les deux bits du résultat et la retenue sortante. La retenue sortante est calculée à partir des bits de l’opérande, sans propager des retenues. Ainsi, la retenue sortante est connue en avance, sans passer par deux demi-additionneurs. Pour créer un tel incrémenteur, le mieux est de partir de sa table de vérité.
{|class="wikitable"
|-
! Opérande !! Retenue entrante !! !! Retenue sortante !! Résultat
|-
| 00 || 0 || || 0 || 00
|-
| 01 || 0 || || 0 || 01
|-
| 10 || 0 || || 0 || 10
|-
| 11 || 0 || || 0 || 11
|-
| colspan="5" |
|-
| 00 || 1 || || 0 || 01
|-
| 01 || 1 || || 0 || 10
|-
| 10 || 1 || || 0 || 11
|-
| 11 || 1 || || 1 || 00
|}
Elle nous dit que la retenue sortante ne vaut 1 que dans un seul cas : les deux bits d'opérande valent 1, la retenue entrante vaut 1. Elle se calcule alors avec une porte ET à trois entrées. Pour les deux bits du résultat, les équations logiques ne donnent pas un résultat satisfaisant. La solution la plus simple est d'utiliser deux demi-additionneurs et de retirer les portes logiques superflues. Le résultat est alors un '''incrémenteur 2 bits ''carry skip''''', où la retenue sortante est calculée séparément du reste.
[[File:Incrémenteur carry skip 2 bits.png|centre|vignette|upright=2.5|Incrémenteur ''carry skip'' 2 bits]]
==Les incrémenteurs à anticipation de retenue==
L''''anticipation de retenue''' calcule la retenue d'une colonne sans attendre les retenues des colonnes précédentes. Dans le cas idéal, toutes les retenues sont calculées en parallèle, en même temps, et sont ensuite envoyées aux demi-additionneurs. Il s'agit d'une optimisation qui est utilisée pour l'incrémentation, l'addition et d'autres opérations similaires. Pour l'incrémentation, déterminer la retenue ne demande pas de calculs complexes, contrairement à l'addition.
[[File:Incrémenteur à anticipation de retenues, 8 bits.png|centre|vignette|upright=2.5|Incrémenteur à anticipation de retenues, 8 bits.]]
Pour le comprendre, on peut regarder comment la retenue sortant d'un demi-additionneur est formée. Elle est calculée en faisant un ET logique entre la retenue entrante et le bit d'opérande. Une retenue est donc un ET logique entre toutes les retenues précédentes. Un incrémenteur à anticipation de retenue utilise donc une porte ET à plusieurs entrées pour calculer une retenue.
[[File:Anticipation de retenue pour un bit du résultat, incrémenteur.png|centre|vignette|upright=2.5|Anticipation de retenue pour un bit du résultat, incrémenteur]]
En théorie, on peut utiliser une porte ET à plusieurs entrées pour chaque bit de l'opérande. Cependant, cela entrainera un cout en transistors très important. Pour éviter de gaspiller trop de portes logiques, une solution est de mélanger anticipation de retenues et propagation de retenue. Par exemple, pour un incrémenteur 32 bits, on peut découper l'opérande en 4 octets : on anticipe les retenues pour chaque octet, mais l'incrémentation de chaque octet se fait avec propagation de retenue et/ou ''carry skip''.
[[File:Incrémenteur à anticipation de retenues.png|centre|vignette|upright=3|Incrémenteur hybride utilisant partiellement l'anticipation de retenues.]]
Quelques processeurs utilisaient l'anticipation de retenues. Par exemple, le processeur Z-80 de Zilog utilisait un incrémenteur pour des nombres de 16 bits, ce qui demandait des performances assez élevées. Et cet incrémenteur utilisait à la fois anticipation de retenues et ''carry skip''. Il était découpé en quatre blocs avant anticipation de retenues entre eux : un bloc regroupant les 7 bits de poids faible, suivi par un bloc de 5 bits, lui-même suivi par un bloc de 3 bits, terminé par un dernier bit isolé. A l'intérieur de ces blocs, les bits sont regroupés en paires utilisant le ''carry skip''. Pour ceux qui veulent en savoir plus sur cet incrémenteur, voici un lien sur le sujet :
* [https://www.righto.com/2013/11/the-z-80s-16-bit-incrementdecrement.html The Z-80's 16-bit increment/decrement circuit reverse engineered].
==Les décrémenteurs==
Les circuits décrémenteurs font l'inverse d'un incrémenteur : au lieu d'ajouter 1 à un opérande, ils lui soustraient 1. La différence n'est pas énorme, ce qui fait que les circuits incrémenteurs et décrémenteurs se ressemblent beaucoup. Là où les incrémenteurs sont fabriqués à partir de demi-additionneurs, les décrémenteurs sont bâtis avec des demi-soustracteurs.
La soustraction se fait en binaire comme en décimal. On soustrait deux chiffres, puis on doit propager une éventuelle retenue sur la colonne suivante. La retenue apparait quand le chiffre soustrait est plus grand que l'autre chiffre. Elle est propagée sur la colonne suivante, où elle doit être soustraite du résultat. Pour le dire autrement, après avoir soustrait un chiffre, on doit de plus soustraire la retenue de la colonne précédente.
La décrémentation effectue le calcul suivant :
<math>a_7</math> <math>a_6</math> <math>a_5</math> <math>a_4</math> <math>a_3</math> <math>a_2</math> <math>a_1</math> <math>a_0</math>
- 0 0 0 0 0 0 0 1
La colonne la plus à droite soustrait un 1. Par contre, les colonnes suivantes soustraient un zéro. Soustraire un zéro ne change rien. Mais il ne faut pas oublier de soustraire une éventuellement retenue, propagée depuis la colonne précédente. En clair, un décrémenteur peut se construire si on sait soustraire deux bits. On soustrait un 1 sur la colonne la plus à droite, on soustrait une retenue sur les autres colonnes. Et pour soustraire deux bits, il faut utiliser un demi-soustracteur.
===Le demi-soustracteur===
Un '''demi-soustracteur''' soustrait deux bits. Pour comprendre comment soustraire deux bits, traitons les quatre cas possibles un par un.
Premièrement, soustraire zéro à un bit ne changera rien :
* 0 - 0 = 0 ;
* 1 - 0 = 1.
Si on soustrait 1 à un bit qui vaut 1, on obtient zéro.
* 1 - 1 = 0.
Maintenant, que se passe-t-il si on soustrait 1 à 0 ? Voici le résultat :
* 0 - 1 = 1 et une retenue propagée sur la colonne suivante, où elle est soustraite.
La table de soustraction est donc la suivante :
* 0 - 0 = 0 ;
* 0 - 1 = 1 et une retenue ;
* 1 - 0 = 1 ;
* 1 - 1 = 0.
La table de vérité est donc la suivante. Détail important : le demi-soustracteur fait ici la soustraction X - Y. En anglais, X est appelé le ''Minuend'' et Y est appelé le ''Subtrahend'', mais ces termes n'ont pas de traduction française. Aussi, je vais les utiliser tels quels dans ce qui suit.
{|class="wikitable"
|-
! ''Minuend'' !! ''Subtrahend'' !! !! Retenue sortante !! Bit de somme
|-
||0||0|| ||0||0
|-
||0||1|| ||1||1
|-
||1||0|| ||0||1
|-
||1||1|| ||0||0
|}
Si on construit la table de vérité, et qu'on construit le circuit associé, on tombe sur ce circuit.
[[File:Half Subtractor Vektor.svg|centre|vignette|Demi-soustracteur.]]
Vous remarquerez que le bit de somme se calcule de la même manière que pour l'addition, seul le calcul de la retenue change.
===Le décrémenteur à propagation de retenue===
Un décrémenteur à propagation de retenue se construit comme un incrémenteur, sauf qu'on remplace les demi-additionneurs par des demi-soustracteurs. En clair, on enchaine plusieurs demi-soustracteurs les uns à la suite des autres.
Il est possible d'utiliser l'anticipation de retenue, mais les circuits sont cependant différents de l'incrémenteur. Idem avec le ''carry skip'', et toute autre optimisation possible. La raison est que les retenues sont calculées différemment, ce qui fait que l’anticipation de retenue doit être adaptée.
==Les incrémenteurs-décrémenteurs==
Les circuits décrémenteurs ressemblent beaucoup aux incrémenteurs. Aussi, vous ne serez pas étonné qu'il soit possible de fusionner un incrémenteur et un décrémenteur en un seul circuit. Un tel circuit s'appelle un '''incrémenteur-décrémenteur'''. Un tel circuit a une interface similaire à celle d'un incrémenteur, avec une entrée sur laquelle présenter l'opérande, et une sortie sur laquelle récupérer le résultat. Il a aussi une entrée de retenue, sur laquelle envoyer un 1 (incrémentation) ou un 0 (opération NOP/identité). Et il a aussi une sortie de retenue, qui indique que le résultat du calcul a débordé.
Mais en plus de cela, il dispose d'une entrée de commande qui permet de choisir entre incrémentation et décrémentation.
Il existe plusieurs manières de créer un incrémenteur-décrémenteur, que nous allons voir si dessous. Nous n'allons pas voir le cas le plus évident, aussi ne vous étonnez pas si vous avez remarqué qu'un demi-additionneur et un demi-soustracteur se ressemblent beaucoup.
===L'incrémenteur-décrémenteur basé sur des demi-additionneurs===
Une première implémentation se base sur les propriétés du complément à deux, et plus précisément sur cette équation :
: <math>\overline{x} + 1 = \overline{x - 1}</math>
Les deux termes de cette équation sont simplement des définitions équivalentes du complément à deux d'un nombre entier. Celui-ci se calcule de deux manières : soit on incrémente son complément à 1, soit on prend le complément à 1 de son décrément. Et cela nous permet de déterminer comment obtenir une incrémentation à partir d'une décrémentation et inversement.
Que nous dit cette équation ? Simplement que l'on peut décrémenter un nombre si on a deux inverseurs commandables, et un circuit incrémenteur. La décrémentation se fait alors en deux étapes : on calcule son complément à deux, puis on inverse les bits du résultat précédent. Le calcul du complément à deux demande d'avoir un circuit inverseur, et un incrémenteur.
===L'incrémenteur-décrémenteur basé sur des demi-additionneurs===
Vous l'aurez sans doute remarqué, mais le demi-soustracteur ressemble beaucoup au demi-additionneur. La seule différence est une porte NON ajoutée au bon endroit dans le demi-soustracteur. La porte NON inverse le bit X, sachant que le demi-soustracteur fait la soustraction X - Y. De plus, l'opérande inversée est utilisée seulement pour le calcul des retenues, le bit de somme est calculé normalement.
{|
|[[File:Half-adder.svg|vignette|Demi-additionneur]]
|[[File:Half Subtractor Vektor.svg|vignette|upright=1.3|Demi-soustracteur]]
|}
La conséquence est qu'il est possible de fusionner un demi-additionneur avec un demi-soustracteur. L'idée est de remplacer la porte NON du demi-soustracteur par un inverseur commandable. Si on envoie un zéro sur l'entrée de commande, l'inverseur commandable se comportera comme une porte OUI et le circuit sera alors identique à un demi-additionneur. Si on envoie un 1 sur l'entrée de commande, l'inverseur commandable se comportera comme une porte NON. le circuit sera alors un demi-soustracteur. En remplaçant les demi-additionneurs (ou demi-soustracteurs) par de tels circuits, on peut obtenir un circuit faisant à la fois incrémenteur et décrémenteur.
Un défaut de cette approche est que les optimisations vues plus haut pour les demi-additionneurs ne fonctionne tout simplement plus. On est obligé d'utiliser une porte XOR et une porte ET séparées, pas question d'utiliser les demi-additionneurs optimisés vu au début du chapitre.
<noinclude>
{{NavChapitre | book=Fonctionnement d'un ordinateur
| prev=Les circuits de sélection
| prevText=Les circuits de sélection
| next=Les bascules : des mémoires de 1 bit
| nextText=Les bascules : des mémoires de 1 bit
}}
</noinclude>
ti5rqvn7u0ts3eame4d8bzij69mgmpx
768793
768792
2026-06-27T15:46:03Z
Mewtow
31375
/* Les incrémenteurs carry skip */
768793
wikitext
text/x-wiki
Le circuit '''incrémenteur''' incrémente un nombre entier. Plus précisément, il prend en entrée un nombre entier, et fournit en entier le nombre suivant, il lui ajoute 1. Une telle opération peut paraitre triviale et peu intéressante. Cependant, elle est très utilisée et est absolument cruciale pour créer des compteurs, des circuits capables de compter ou décompter, qui auront droit à un chapitre dédié. De plus, au-delà de leur utilisation dans les compteurs, les incrémenteurs étaient très utilisés sur les premiers processeurs 8 bits, comme le Z-80, le 6502, les premiers processeurs x86 comme le 8008, le 8086, le 8085, et bien d'autres.
Il existe aussi des ''circuits décrémenteurs'', qui décrémentent un opérande, ainsi que des ''circuits incrémenteur-décrémenteur'', qui peuvent incrémenter ou décrémenter, selon comment on les configure. Je ne vais pas détailler ces circuits plus que ça, car de tels circuits sont assez rares, comparé à un circuit incrémenteur simple.
==Le demi-additionneur==
Le circuit incrémenteur effectue l'opération suivante :
<math>a_7</math> <math>a_6</math> <math>a_5</math> <math>a_4</math> <math>a_3</math> <math>a_2</math> <math>a_1</math> <math>a_0</math>
+ 0 0 0 0 0 0 0 1
Un incrémenteur basique doit donc faire une addition pour chaque colonne, et précisément une addition de deux bits. Il se trouve que la table d'addition est très simple en binaire. Jugez plutôt :
* 0 + 0 = 0, retenue = 0 ;
* 0 + 1 = 1, retenue = 0 ;
* 1 + 0 = 1, retenue = 0 ;
* 1 + 1 = 0, retenue = 1.
Un circuit capable d'additionner deux bits est appelé un '''demi-additionneur'''. Il dispose d'une sortie S pour la somme, et C pour la retenue (''carry'' an anglais).
===Le demi-additionneur classique===
Un demi-additionneur est très simple à construire avec les techniques vues dans les premiers chapitres. Voici sa table de vérité :
{|class="wikitable"
|-
! Retenue entrante !! Opérande 1 !! !! Retenue sortante !! Bit de somme
|-
||0||0|| ||0||0
|-
||0||1|| ||0||1
|-
||1||0|| ||0||1
|-
||1||1|| ||1||0
|}
On voit immédiatement que la colonne des retenues donne une porte ET, alors que celle du bit de somme est calculé par un XOR.
{| class="flexible"
|[[File:1-bit half-adder.svg|class=transparent|centre|Demi-addtionneur.]]
|[[File:Half-adder.svg|centre|class=transparent|Circuit d'un demi-addtionneur.]]
|}
Il existe beaucoup d'autres implémentations alternatives, qui utilisent moins de portes logiques, calculent la retenue plus rapidement, consomment moins d'énergie, et autres. Et il est intéressant d'étudier quelques alternatives, surtout qu'elles serviront plus bas, quand on étudiera le circuit incrémenteur du 8085.
===Le demi-additionneur basé sur une modification de la retenue===
Une source d'amélioration est liée à la porte XOR. En pratique, une porte XOR est composée en combinant plusieurs portes logiques ET/OU/NOR/NAND ensemble. Et il se trouve qu'il y a des redondances entre celles-ci et les portes utilisées pour calculer la retenue. Par exemple, rappelons qu'une porte XOR peut être construite avec une porte ET et deux portes NOR, comme illustré ci-dessous. Et il se trouve que la porte ET interne calcule la retenue sortante. En supprimant cette redondance, on économise quelques portes logiques. De plus, on se retrouve avec un demi-additionneur qui calcule le bit de somme à partir de la retenue sortante.
[[File:Demi-additionneur avec redondances éliminées.png|centre|vignette|upright=2|Demi-additionneur avec redondances éliminées]]
Le circuit précédent a une interprétation logique. Si vous regardez la table de vérité, vous remarquerez que la somme de deux bits est égale à l'inverse de la retenue sortante, sauf dans le cas où les deux bits additionnés valent zéro. Et le circuit précédent est basé là-dessus.
{|class="wikitable"
|-
! Retenue entrante !! Opérande 1 !! !! Retenue sortante !! Bit de somme
|- class="f_vert"
||0||0|| ||0||0
|- class="f_rouge"
||0||1|| ||0||1
|- class="f_rouge"
||1||0|| ||0||1
|- class="f_rouge"
||1||1|| ||1||0
|}
L'idée est de calculer l'inverse du bit de somme, avant de l'inverser avec une porte NON. L'inverse du bit de somme vaut 1, soit quand la retenue est à 1, soit quand les deux bits additionnés sont à 0. La porte ET calcule la retenue sortante, la première porte NOR détecte sur les deux bits d'entrée valent zéro. Une porte OU combine les deux résultat pour obtenir l'inverse du bit d'entrée, puis une porte NOn inverse le tout pour obtenir le bit de somme adéquat. Les deux portes sont fusionnées : c'est la seconde porte NOR.
[[File:Full adder HA + MUX.png|centre|vignette|upright=2.5|Demi-additionneur basé sur une inversion de la retenue sortante.]]
===Les implémentations alternatives===
Une implémentation alternative, qui n'utilise que des portes NOR, est la suivante :
[[File:Demi-additionneur fait avec des portes NOR et NAND.png|centre|vignette|upright=2|Demi-additionneur fait avec des portes NOR et NAND.]]
Une implémentation alternative utilise des portes NAND. Pour rappel, il est possible de créer une porte XOR avec 4 portes NAND. La première d'entre elle fait un NAND entre les deux bits d'entrée, ce qui fait qu'elle calcule l'inverse de la retenue sortante. Le tout est illustré ci-dessous. En théorie, on devrait utiliser une porte NON pour récupérer la retenue correcte. Mais nous verrons plus bas que ce n'est pas une obligation. Nous verrons plus bas un exemple où c'est l'inverse de la retenue qui est utilisée dans les calculs, pour rendre les calculs plus rapides.
[[File:Demi-additionneur fait avec des portes NAND.png|centre|vignette|upright=2|Demi-additionneur fait avec des portes NAND]]
==L'incrémenteur à propagation de retenue==
Maintenant que l'on sait comment additionner deux bits, reprenons l'opération d'incrémentation :
<math>a_7</math> <math>a_6</math> <math>a_5</math> <math>a_4</math> <math>a_3</math> <math>a_2</math> <math>a_1</math> <math>a_0</math>
+ 0 0 0 0 0 0 0 1
------------------------------
Sur la colonne la plus à droite, il doit ajouter un au bit de poids faible. Pour les colonnes suivantes, il faut additionner le bit de l'opérande avec la retenue de la colonne précédente. En clair, on n'additionne que deux bits à chaque colonne : un 1 sur celle tout à droite, la retenue de la colonne précédente pour les autres. Et cela nous donne une idée de comment faire pour créer le circuit incrémenteur.
Il suffit d'utiliser un demi-additionneur par colonne, et de les enchainer les uns à la suite des autres. Chaque demi-additionneur additionne le bit de l'opérande avec la retenue fournie par le demi-additionneur précédent. Le seul qui fait exception est celui pour la colonne de poids faible. Pour celui-là, il doit ajouter 1 au bit de poids faible. Le résultat est appelé un '''incrémenteur à propagation de retenue'''. Il est constitué de demi-additionneurs enchaînés les uns à la suite des autres, du bit de poids faible vers le bit de poids fort.
[[File:Circuit incrémenteur.png|centre|vignette|upright=3|Circuit incrémenteur.]]
Maintenant, regardons le demi-additionneur le plus à droite, celui pour le bit de poids faible. Son entrée de retenue entrante est mise à 1 pour faire l'incrémentation. Quelques incrémenteurs permettent de configurer cette entrée de retenue à 0 ou à 1, ce qui effectue : soit une opération identité (l'opérande est recopié sur la sortie), soit une incrémentation. Un tel circuit est nommé un '''incrémenteur commandable'''. Nous aurons à utiliser une fois ou deux de tels incrémenteurs commandables dans la suite du cours.
L'incrémenteur à propagation de retenue est le plus simple et le plus économe en portes logiques. Mais de tels incrémenteurs sont rarement utilisés. À la place, on leur préfère des incrémenteurs plus rapides, mais qui utilisent plus de portes logiques. De tels incrémenteurs accélèrent le calcul des retenues. En effet, la rapidité d'une incrémentation est limitée par la propagation de la retenue : les retenues commencent à être calculées au bit de poids fort et on doit les calculer une par une, jusqu’à atteindre le bit de poids fort. Et cette "propagation des retenues" prend du temps, d'autant plus de temps que l'opérande est longue. Il y a deux optimisations principales, appelées le ''carry skip'' et l'anticipation de retenue, que nous allons décrire ci-dessous.
===Les incrémenteurs optimisés===
L'optimisation que nous allns voir effectue l'incrémentation, non pas bit par bit, mais par paquets de deux bits. Le résultat est que l'incrémentation est deux fois plus rapide, ou presque. Le circuit incrémenteur est donc composé en enchainant non pas des demi-additionneurs, mais des '''incrémenteurs 2 bits''' qui incrémentent un opérande de deux bits.
[[File:Circuit incrémenteur optimisé, faisant l'incrémentation par paquet de deux bits.png|centre|vignette|upright=2.5|Circuit incrémenteur optimisé, faisant l'incrémentation par paquet de deux bits]]
Reste à concevoir l'incrémenteur 2 bits. Une première méthode part de deux demi-additionneurs et effectue quelques simplifications au niveau des transistors et des portes logiques. Par exemple, l'Intel 8085 a utilisé un circuit optimisé, fabriqué avec des portes NAND et NOR. La raison est qu'avec les technologies de transistors CMOS actuelles, les portes NAND et NOR utilisent moins de transistors que les portes ET et OU. Et cette possibilité a été utilisée pour effectuer des simplifications assez mineures, mais toujours bonnes à prendre. Mais nous ne pouvons pas en parler en détail ici, car nous n'avons pas encore parlé en détail des transistors. Cependant, un exemple bien précis nous est accesible : celui de l’incrémenteur du processeur 8085 d'Intel.
Le processeur 8085 d'Intel est un processeur 8 bits très ancien. Il contenait un incrémenteur 16 bits, qui était utilisé pour calculer des adresses mémoire. Et cet incrémenteur était un incrémenteur à propagation de retenue optimisé. Il utilisait lui aussi des incrémenteurs 2 bits un peu modifiés.
L'idée était que les retenues sortant des colonnes paires étaient inversées, les colonnes impaires faisaient les calculs à partir de cette retenue inversée. Les demi-additionneurs des colonnes impaires n'étaient donc pas les mêmes que ceux des colonnes paires. Leurs tables de vérité sont différentes, leurs circuits aussi, et aucun des deux ne ressemble à un demi-additionneur normal. En réalité, ils sont fabriqués à partir des implémentations vues plus haut, qui utilisent uniquement des portes NAND ou uniquement des portes NOR.
Pour comprendre l'intérêt de faire ainsi, nous devons préciser une chose importante : avec les technologies CMOS utilisées pour les processeurs depuis les années 70, les portes logiques les plus simples sont les portes NON, NOR et NAND. Les portes ET/OU sont fabriquées en combinant des portes NOR/NAND avec une porte NON, ce qui prend plus de transistors.
Un demi-additionneur est donc fabriqué comme illustré ci-dessous, en logique CMOS. Seules les portes pour le calcul de la retenue sont indiquées, la porte XOR pour le bit de somme est fabriquée à partir de plusieurs portes logiques.
[[File:Demi-additionneur en CMOS.png|centre|vignette|upright=2|Demi-additionneur en CMOS]]
Si on enchaine deux demi-additionneurs, cela donne ceci :
[[File:Brique de base de l'incrémenteur du 8085.png|centre|vignette|upright=2|Brique de base de l'incrémenteur du 8085]]
La retenue doit donc traverser quatre portes logiques, dont deux portes NON. En inversant la retenue, les portes en jaune dans le schéma précédent se simplifient. Il suffit d'utiliser la loi de de Morgan pour trouver un circuit équivalent aux portes jaunes, qui n'est autre qu'une porte NOR couplée à une porte NON. Un autre moyen d'obtenir le même résultat est de prendre un demi-additionneur fabriqué avec uniquement des portes NOR, et de retirer la porte NON adéquate. Il faut ensuite corriger l'entrée de retenue de la porte XOR, pour qu'elle fasse l'addition avec la retenue correctement. Le résultat est illustré ci-dessous.
[[File:Incrémenteur 2 bits de l'incrémenteur du 8085.png|centre|vignette|upright=2|Incrémenteur 2 bits de l'incrémenteur du 8085]]
Le circuit utilise le même nombre de portes logiques, il n'économise pas de transistors. Par contre, la retenue n'a plus qu'à traverser deux portes logiques, au lieu de quatre. La propagation de la retenue est donc plus rapide, l'incrémentation est donc plus rapide.
==Les incrémenteurs à anticipation de retenue==
L''''anticipation de retenue''' calcule la retenue d'une colonne sans attendre les retenues des colonnes précédentes. Dans le cas idéal, toutes les retenues sont calculées en parallèle, en même temps, et sont ensuite envoyées aux demi-additionneurs. Il s'agit d'une optimisation qui est utilisée pour l'incrémentation, l'addition et d'autres opérations similaires. Pour l'incrémentation, déterminer la retenue ne demande pas de calculs complexes, contrairement à l'addition.
[[File:Incrémenteur à anticipation de retenues, 8 bits.png|centre|vignette|upright=2.5|Incrémenteur à anticipation de retenues, 8 bits.]]
Pour le comprendre, on peut regarder comment la retenue sortant d'un demi-additionneur est formée. Elle est calculée en faisant un ET logique entre la retenue entrante et le bit d'opérande. Une retenue est donc un ET logique entre toutes les retenues précédentes. Un incrémenteur à anticipation de retenue utilise donc une porte ET à plusieurs entrées pour calculer une retenue.
[[File:Anticipation de retenue pour un bit du résultat, incrémenteur.png|centre|vignette|upright=2.5|Anticipation de retenue pour un bit du résultat, incrémenteur]]
En théorie, on peut utiliser une porte ET à plusieurs entrées pour chaque bit de l'opérande. Cependant, cela entrainera un cout en transistors très important. Pour éviter de gaspiller trop de portes logiques, une solution est de mélanger anticipation de retenues et propagation de retenue. Par exemple, pour un incrémenteur 32 bits, on peut découper l'opérande en 4 octets : on anticipe les retenues pour chaque octet, mais l'incrémentation de chaque octet se fait avec propagation de retenue et/ou ''carry skip''.
[[File:Incrémenteur à anticipation de retenues.png|centre|vignette|upright=3|Incrémenteur hybride utilisant partiellement l'anticipation de retenues.]]
Quelques processeurs utilisaient l'anticipation de retenues. Par exemple, le processeur Z-80 de Zilog utilisait un incrémenteur pour des nombres de 16 bits, ce qui demandait des performances assez élevées. Et cet incrémenteur utilisait à la fois anticipation de retenues et ''carry skip''. Il était découpé en quatre blocs avant anticipation de retenues entre eux : un bloc regroupant les 7 bits de poids faible, suivi par un bloc de 5 bits, lui-même suivi par un bloc de 3 bits, terminé par un dernier bit isolé. A l'intérieur de ces blocs, les bits sont regroupés en paires utilisant le ''carry skip''. Pour ceux qui veulent en savoir plus sur cet incrémenteur, voici un lien sur le sujet :
* [https://www.righto.com/2013/11/the-z-80s-16-bit-incrementdecrement.html The Z-80's 16-bit increment/decrement circuit reverse engineered].
==Les décrémenteurs==
Les circuits décrémenteurs font l'inverse d'un incrémenteur : au lieu d'ajouter 1 à un opérande, ils lui soustraient 1. La différence n'est pas énorme, ce qui fait que les circuits incrémenteurs et décrémenteurs se ressemblent beaucoup. Là où les incrémenteurs sont fabriqués à partir de demi-additionneurs, les décrémenteurs sont bâtis avec des demi-soustracteurs.
La soustraction se fait en binaire comme en décimal. On soustrait deux chiffres, puis on doit propager une éventuelle retenue sur la colonne suivante. La retenue apparait quand le chiffre soustrait est plus grand que l'autre chiffre. Elle est propagée sur la colonne suivante, où elle doit être soustraite du résultat. Pour le dire autrement, après avoir soustrait un chiffre, on doit de plus soustraire la retenue de la colonne précédente.
La décrémentation effectue le calcul suivant :
<math>a_7</math> <math>a_6</math> <math>a_5</math> <math>a_4</math> <math>a_3</math> <math>a_2</math> <math>a_1</math> <math>a_0</math>
- 0 0 0 0 0 0 0 1
La colonne la plus à droite soustrait un 1. Par contre, les colonnes suivantes soustraient un zéro. Soustraire un zéro ne change rien. Mais il ne faut pas oublier de soustraire une éventuellement retenue, propagée depuis la colonne précédente. En clair, un décrémenteur peut se construire si on sait soustraire deux bits. On soustrait un 1 sur la colonne la plus à droite, on soustrait une retenue sur les autres colonnes. Et pour soustraire deux bits, il faut utiliser un demi-soustracteur.
===Le demi-soustracteur===
Un '''demi-soustracteur''' soustrait deux bits. Pour comprendre comment soustraire deux bits, traitons les quatre cas possibles un par un.
Premièrement, soustraire zéro à un bit ne changera rien :
* 0 - 0 = 0 ;
* 1 - 0 = 1.
Si on soustrait 1 à un bit qui vaut 1, on obtient zéro.
* 1 - 1 = 0.
Maintenant, que se passe-t-il si on soustrait 1 à 0 ? Voici le résultat :
* 0 - 1 = 1 et une retenue propagée sur la colonne suivante, où elle est soustraite.
La table de soustraction est donc la suivante :
* 0 - 0 = 0 ;
* 0 - 1 = 1 et une retenue ;
* 1 - 0 = 1 ;
* 1 - 1 = 0.
La table de vérité est donc la suivante. Détail important : le demi-soustracteur fait ici la soustraction X - Y. En anglais, X est appelé le ''Minuend'' et Y est appelé le ''Subtrahend'', mais ces termes n'ont pas de traduction française. Aussi, je vais les utiliser tels quels dans ce qui suit.
{|class="wikitable"
|-
! ''Minuend'' !! ''Subtrahend'' !! !! Retenue sortante !! Bit de somme
|-
||0||0|| ||0||0
|-
||0||1|| ||1||1
|-
||1||0|| ||0||1
|-
||1||1|| ||0||0
|}
Si on construit la table de vérité, et qu'on construit le circuit associé, on tombe sur ce circuit.
[[File:Half Subtractor Vektor.svg|centre|vignette|Demi-soustracteur.]]
Vous remarquerez que le bit de somme se calcule de la même manière que pour l'addition, seul le calcul de la retenue change.
===Le décrémenteur à propagation de retenue===
Un décrémenteur à propagation de retenue se construit comme un incrémenteur, sauf qu'on remplace les demi-additionneurs par des demi-soustracteurs. En clair, on enchaine plusieurs demi-soustracteurs les uns à la suite des autres.
Il est possible d'utiliser l'anticipation de retenue, mais les circuits sont cependant différents de l'incrémenteur. Idem avec le ''carry skip'', et toute autre optimisation possible. La raison est que les retenues sont calculées différemment, ce qui fait que l’anticipation de retenue doit être adaptée.
==Les incrémenteurs-décrémenteurs==
Les circuits décrémenteurs ressemblent beaucoup aux incrémenteurs. Aussi, vous ne serez pas étonné qu'il soit possible de fusionner un incrémenteur et un décrémenteur en un seul circuit. Un tel circuit s'appelle un '''incrémenteur-décrémenteur'''. Un tel circuit a une interface similaire à celle d'un incrémenteur, avec une entrée sur laquelle présenter l'opérande, et une sortie sur laquelle récupérer le résultat. Il a aussi une entrée de retenue, sur laquelle envoyer un 1 (incrémentation) ou un 0 (opération NOP/identité). Et il a aussi une sortie de retenue, qui indique que le résultat du calcul a débordé.
Mais en plus de cela, il dispose d'une entrée de commande qui permet de choisir entre incrémentation et décrémentation.
Il existe plusieurs manières de créer un incrémenteur-décrémenteur, que nous allons voir si dessous. Nous n'allons pas voir le cas le plus évident, aussi ne vous étonnez pas si vous avez remarqué qu'un demi-additionneur et un demi-soustracteur se ressemblent beaucoup.
===L'incrémenteur-décrémenteur basé sur des demi-additionneurs===
Une première implémentation se base sur les propriétés du complément à deux, et plus précisément sur cette équation :
: <math>\overline{x} + 1 = \overline{x - 1}</math>
Les deux termes de cette équation sont simplement des définitions équivalentes du complément à deux d'un nombre entier. Celui-ci se calcule de deux manières : soit on incrémente son complément à 1, soit on prend le complément à 1 de son décrément. Et cela nous permet de déterminer comment obtenir une incrémentation à partir d'une décrémentation et inversement.
Que nous dit cette équation ? Simplement que l'on peut décrémenter un nombre si on a deux inverseurs commandables, et un circuit incrémenteur. La décrémentation se fait alors en deux étapes : on calcule son complément à deux, puis on inverse les bits du résultat précédent. Le calcul du complément à deux demande d'avoir un circuit inverseur, et un incrémenteur.
===L'incrémenteur-décrémenteur basé sur des demi-additionneurs===
Vous l'aurez sans doute remarqué, mais le demi-soustracteur ressemble beaucoup au demi-additionneur. La seule différence est une porte NON ajoutée au bon endroit dans le demi-soustracteur. La porte NON inverse le bit X, sachant que le demi-soustracteur fait la soustraction X - Y. De plus, l'opérande inversée est utilisée seulement pour le calcul des retenues, le bit de somme est calculé normalement.
{|
|[[File:Half-adder.svg|vignette|Demi-additionneur]]
|[[File:Half Subtractor Vektor.svg|vignette|upright=1.3|Demi-soustracteur]]
|}
La conséquence est qu'il est possible de fusionner un demi-additionneur avec un demi-soustracteur. L'idée est de remplacer la porte NON du demi-soustracteur par un inverseur commandable. Si on envoie un zéro sur l'entrée de commande, l'inverseur commandable se comportera comme une porte OUI et le circuit sera alors identique à un demi-additionneur. Si on envoie un 1 sur l'entrée de commande, l'inverseur commandable se comportera comme une porte NON. le circuit sera alors un demi-soustracteur. En remplaçant les demi-additionneurs (ou demi-soustracteurs) par de tels circuits, on peut obtenir un circuit faisant à la fois incrémenteur et décrémenteur.
Un défaut de cette approche est que les optimisations vues plus haut pour les demi-additionneurs ne fonctionne tout simplement plus. On est obligé d'utiliser une porte XOR et une porte ET séparées, pas question d'utiliser les demi-additionneurs optimisés vu au début du chapitre.
<noinclude>
{{NavChapitre | book=Fonctionnement d'un ordinateur
| prev=Les circuits de sélection
| prevText=Les circuits de sélection
| next=Les bascules : des mémoires de 1 bit
| nextText=Les bascules : des mémoires de 1 bit
}}
</noinclude>
eoaoetqiha28ip18f4mwncgddix2cpk
768794
768793
2026-06-27T15:47:03Z
Mewtow
31375
/* Les incrémenteurs à anticipation de retenue */
768794
wikitext
text/x-wiki
Le circuit '''incrémenteur''' incrémente un nombre entier. Plus précisément, il prend en entrée un nombre entier, et fournit en entier le nombre suivant, il lui ajoute 1. Une telle opération peut paraitre triviale et peu intéressante. Cependant, elle est très utilisée et est absolument cruciale pour créer des compteurs, des circuits capables de compter ou décompter, qui auront droit à un chapitre dédié. De plus, au-delà de leur utilisation dans les compteurs, les incrémenteurs étaient très utilisés sur les premiers processeurs 8 bits, comme le Z-80, le 6502, les premiers processeurs x86 comme le 8008, le 8086, le 8085, et bien d'autres.
Il existe aussi des ''circuits décrémenteurs'', qui décrémentent un opérande, ainsi que des ''circuits incrémenteur-décrémenteur'', qui peuvent incrémenter ou décrémenter, selon comment on les configure. Je ne vais pas détailler ces circuits plus que ça, car de tels circuits sont assez rares, comparé à un circuit incrémenteur simple.
==Le demi-additionneur==
Le circuit incrémenteur effectue l'opération suivante :
<math>a_7</math> <math>a_6</math> <math>a_5</math> <math>a_4</math> <math>a_3</math> <math>a_2</math> <math>a_1</math> <math>a_0</math>
+ 0 0 0 0 0 0 0 1
Un incrémenteur basique doit donc faire une addition pour chaque colonne, et précisément une addition de deux bits. Il se trouve que la table d'addition est très simple en binaire. Jugez plutôt :
* 0 + 0 = 0, retenue = 0 ;
* 0 + 1 = 1, retenue = 0 ;
* 1 + 0 = 1, retenue = 0 ;
* 1 + 1 = 0, retenue = 1.
Un circuit capable d'additionner deux bits est appelé un '''demi-additionneur'''. Il dispose d'une sortie S pour la somme, et C pour la retenue (''carry'' an anglais).
===Le demi-additionneur classique===
Un demi-additionneur est très simple à construire avec les techniques vues dans les premiers chapitres. Voici sa table de vérité :
{|class="wikitable"
|-
! Retenue entrante !! Opérande 1 !! !! Retenue sortante !! Bit de somme
|-
||0||0|| ||0||0
|-
||0||1|| ||0||1
|-
||1||0|| ||0||1
|-
||1||1|| ||1||0
|}
On voit immédiatement que la colonne des retenues donne une porte ET, alors que celle du bit de somme est calculé par un XOR.
{| class="flexible"
|[[File:1-bit half-adder.svg|class=transparent|centre|Demi-addtionneur.]]
|[[File:Half-adder.svg|centre|class=transparent|Circuit d'un demi-addtionneur.]]
|}
Il existe beaucoup d'autres implémentations alternatives, qui utilisent moins de portes logiques, calculent la retenue plus rapidement, consomment moins d'énergie, et autres. Et il est intéressant d'étudier quelques alternatives, surtout qu'elles serviront plus bas, quand on étudiera le circuit incrémenteur du 8085.
===Le demi-additionneur basé sur une modification de la retenue===
Une source d'amélioration est liée à la porte XOR. En pratique, une porte XOR est composée en combinant plusieurs portes logiques ET/OU/NOR/NAND ensemble. Et il se trouve qu'il y a des redondances entre celles-ci et les portes utilisées pour calculer la retenue. Par exemple, rappelons qu'une porte XOR peut être construite avec une porte ET et deux portes NOR, comme illustré ci-dessous. Et il se trouve que la porte ET interne calcule la retenue sortante. En supprimant cette redondance, on économise quelques portes logiques. De plus, on se retrouve avec un demi-additionneur qui calcule le bit de somme à partir de la retenue sortante.
[[File:Demi-additionneur avec redondances éliminées.png|centre|vignette|upright=2|Demi-additionneur avec redondances éliminées]]
Le circuit précédent a une interprétation logique. Si vous regardez la table de vérité, vous remarquerez que la somme de deux bits est égale à l'inverse de la retenue sortante, sauf dans le cas où les deux bits additionnés valent zéro. Et le circuit précédent est basé là-dessus.
{|class="wikitable"
|-
! Retenue entrante !! Opérande 1 !! !! Retenue sortante !! Bit de somme
|- class="f_vert"
||0||0|| ||0||0
|- class="f_rouge"
||0||1|| ||0||1
|- class="f_rouge"
||1||0|| ||0||1
|- class="f_rouge"
||1||1|| ||1||0
|}
L'idée est de calculer l'inverse du bit de somme, avant de l'inverser avec une porte NON. L'inverse du bit de somme vaut 1, soit quand la retenue est à 1, soit quand les deux bits additionnés sont à 0. La porte ET calcule la retenue sortante, la première porte NOR détecte sur les deux bits d'entrée valent zéro. Une porte OU combine les deux résultat pour obtenir l'inverse du bit d'entrée, puis une porte NOn inverse le tout pour obtenir le bit de somme adéquat. Les deux portes sont fusionnées : c'est la seconde porte NOR.
[[File:Full adder HA + MUX.png|centre|vignette|upright=2.5|Demi-additionneur basé sur une inversion de la retenue sortante.]]
===Les implémentations alternatives===
Une implémentation alternative, qui n'utilise que des portes NOR, est la suivante :
[[File:Demi-additionneur fait avec des portes NOR et NAND.png|centre|vignette|upright=2|Demi-additionneur fait avec des portes NOR et NAND.]]
Une implémentation alternative utilise des portes NAND. Pour rappel, il est possible de créer une porte XOR avec 4 portes NAND. La première d'entre elle fait un NAND entre les deux bits d'entrée, ce qui fait qu'elle calcule l'inverse de la retenue sortante. Le tout est illustré ci-dessous. En théorie, on devrait utiliser une porte NON pour récupérer la retenue correcte. Mais nous verrons plus bas que ce n'est pas une obligation. Nous verrons plus bas un exemple où c'est l'inverse de la retenue qui est utilisée dans les calculs, pour rendre les calculs plus rapides.
[[File:Demi-additionneur fait avec des portes NAND.png|centre|vignette|upright=2|Demi-additionneur fait avec des portes NAND]]
==L'incrémenteur à propagation de retenue==
Maintenant que l'on sait comment additionner deux bits, reprenons l'opération d'incrémentation :
<math>a_7</math> <math>a_6</math> <math>a_5</math> <math>a_4</math> <math>a_3</math> <math>a_2</math> <math>a_1</math> <math>a_0</math>
+ 0 0 0 0 0 0 0 1
------------------------------
Sur la colonne la plus à droite, il doit ajouter un au bit de poids faible. Pour les colonnes suivantes, il faut additionner le bit de l'opérande avec la retenue de la colonne précédente. En clair, on n'additionne que deux bits à chaque colonne : un 1 sur celle tout à droite, la retenue de la colonne précédente pour les autres. Et cela nous donne une idée de comment faire pour créer le circuit incrémenteur.
Il suffit d'utiliser un demi-additionneur par colonne, et de les enchainer les uns à la suite des autres. Chaque demi-additionneur additionne le bit de l'opérande avec la retenue fournie par le demi-additionneur précédent. Le seul qui fait exception est celui pour la colonne de poids faible. Pour celui-là, il doit ajouter 1 au bit de poids faible. Le résultat est appelé un '''incrémenteur à propagation de retenue'''. Il est constitué de demi-additionneurs enchaînés les uns à la suite des autres, du bit de poids faible vers le bit de poids fort.
[[File:Circuit incrémenteur.png|centre|vignette|upright=3|Circuit incrémenteur.]]
Maintenant, regardons le demi-additionneur le plus à droite, celui pour le bit de poids faible. Son entrée de retenue entrante est mise à 1 pour faire l'incrémentation. Quelques incrémenteurs permettent de configurer cette entrée de retenue à 0 ou à 1, ce qui effectue : soit une opération identité (l'opérande est recopié sur la sortie), soit une incrémentation. Un tel circuit est nommé un '''incrémenteur commandable'''. Nous aurons à utiliser une fois ou deux de tels incrémenteurs commandables dans la suite du cours.
L'incrémenteur à propagation de retenue est le plus simple et le plus économe en portes logiques. Mais de tels incrémenteurs sont rarement utilisés. À la place, on leur préfère des incrémenteurs plus rapides, mais qui utilisent plus de portes logiques. De tels incrémenteurs accélèrent le calcul des retenues. En effet, la rapidité d'une incrémentation est limitée par la propagation de la retenue : les retenues commencent à être calculées au bit de poids fort et on doit les calculer une par une, jusqu’à atteindre le bit de poids fort. Et cette "propagation des retenues" prend du temps, d'autant plus de temps que l'opérande est longue. Il y a deux optimisations principales, appelées le ''carry skip'' et l'anticipation de retenue, que nous allons décrire ci-dessous.
===Les incrémenteurs optimisés===
L'optimisation que nous allns voir effectue l'incrémentation, non pas bit par bit, mais par paquets de deux bits. Le résultat est que l'incrémentation est deux fois plus rapide, ou presque. Le circuit incrémenteur est donc composé en enchainant non pas des demi-additionneurs, mais des '''incrémenteurs 2 bits''' qui incrémentent un opérande de deux bits.
[[File:Circuit incrémenteur optimisé, faisant l'incrémentation par paquet de deux bits.png|centre|vignette|upright=2.5|Circuit incrémenteur optimisé, faisant l'incrémentation par paquet de deux bits]]
Reste à concevoir l'incrémenteur 2 bits. Une première méthode part de deux demi-additionneurs et effectue quelques simplifications au niveau des transistors et des portes logiques. Par exemple, l'Intel 8085 a utilisé un circuit optimisé, fabriqué avec des portes NAND et NOR. La raison est qu'avec les technologies de transistors CMOS actuelles, les portes NAND et NOR utilisent moins de transistors que les portes ET et OU. Et cette possibilité a été utilisée pour effectuer des simplifications assez mineures, mais toujours bonnes à prendre. Mais nous ne pouvons pas en parler en détail ici, car nous n'avons pas encore parlé en détail des transistors. Cependant, un exemple bien précis nous est accesible : celui de l’incrémenteur du processeur 8085 d'Intel.
Le processeur 8085 d'Intel est un processeur 8 bits très ancien. Il contenait un incrémenteur 16 bits, qui était utilisé pour calculer des adresses mémoire. Et cet incrémenteur était un incrémenteur à propagation de retenue optimisé. Il utilisait lui aussi des incrémenteurs 2 bits un peu modifiés.
L'idée était que les retenues sortant des colonnes paires étaient inversées, les colonnes impaires faisaient les calculs à partir de cette retenue inversée. Les demi-additionneurs des colonnes impaires n'étaient donc pas les mêmes que ceux des colonnes paires. Leurs tables de vérité sont différentes, leurs circuits aussi, et aucun des deux ne ressemble à un demi-additionneur normal. En réalité, ils sont fabriqués à partir des implémentations vues plus haut, qui utilisent uniquement des portes NAND ou uniquement des portes NOR.
Pour comprendre l'intérêt de faire ainsi, nous devons préciser une chose importante : avec les technologies CMOS utilisées pour les processeurs depuis les années 70, les portes logiques les plus simples sont les portes NON, NOR et NAND. Les portes ET/OU sont fabriquées en combinant des portes NOR/NAND avec une porte NON, ce qui prend plus de transistors.
Un demi-additionneur est donc fabriqué comme illustré ci-dessous, en logique CMOS. Seules les portes pour le calcul de la retenue sont indiquées, la porte XOR pour le bit de somme est fabriquée à partir de plusieurs portes logiques.
[[File:Demi-additionneur en CMOS.png|centre|vignette|upright=2|Demi-additionneur en CMOS]]
Si on enchaine deux demi-additionneurs, cela donne ceci :
[[File:Brique de base de l'incrémenteur du 8085.png|centre|vignette|upright=2|Brique de base de l'incrémenteur du 8085]]
La retenue doit donc traverser quatre portes logiques, dont deux portes NON. En inversant la retenue, les portes en jaune dans le schéma précédent se simplifient. Il suffit d'utiliser la loi de de Morgan pour trouver un circuit équivalent aux portes jaunes, qui n'est autre qu'une porte NOR couplée à une porte NON. Un autre moyen d'obtenir le même résultat est de prendre un demi-additionneur fabriqué avec uniquement des portes NOR, et de retirer la porte NON adéquate. Il faut ensuite corriger l'entrée de retenue de la porte XOR, pour qu'elle fasse l'addition avec la retenue correctement. Le résultat est illustré ci-dessous.
[[File:Incrémenteur 2 bits de l'incrémenteur du 8085.png|centre|vignette|upright=2|Incrémenteur 2 bits de l'incrémenteur du 8085]]
Le circuit utilise le même nombre de portes logiques, il n'économise pas de transistors. Par contre, la retenue n'a plus qu'à traverser deux portes logiques, au lieu de quatre. La propagation de la retenue est donc plus rapide, l'incrémentation est donc plus rapide.
===Les incrémenteurs à anticipation de retenue===
L''''anticipation de retenue''' calcule la retenue d'une colonne sans attendre les retenues des colonnes précédentes. Dans le cas idéal, toutes les retenues sont calculées en parallèle, en même temps, et sont ensuite envoyées aux demi-additionneurs. Il s'agit d'une optimisation qui est utilisée pour l'incrémentation, l'addition et d'autres opérations similaires. Pour l'incrémentation, déterminer la retenue ne demande pas de calculs complexes, contrairement à l'addition.
[[File:Incrémenteur à anticipation de retenues, 8 bits.png|centre|vignette|upright=2.5|Incrémenteur à anticipation de retenues, 8 bits.]]
Pour le comprendre, on peut regarder comment la retenue sortant d'un demi-additionneur est formée. Elle est calculée en faisant un ET logique entre la retenue entrante et le bit d'opérande. Une retenue est donc un ET logique entre toutes les retenues précédentes. Un incrémenteur à anticipation de retenue utilise donc une porte ET à plusieurs entrées pour calculer une retenue.
[[File:Anticipation de retenue pour un bit du résultat, incrémenteur.png|centre|vignette|upright=2.5|Anticipation de retenue pour un bit du résultat, incrémenteur]]
En théorie, on peut utiliser une porte ET à plusieurs entrées pour chaque bit de l'opérande. Cependant, cela entrainera un cout en transistors très important. Pour éviter de gaspiller trop de portes logiques, une solution est de mélanger anticipation de retenues et propagation de retenue. Par exemple, pour un incrémenteur 32 bits, on peut découper l'opérande en 4 octets : on anticipe les retenues pour chaque octet, mais l'incrémentation de chaque octet se fait avec propagation de retenue et/ou ''carry skip''.
[[File:Incrémenteur à anticipation de retenues.png|centre|vignette|upright=3|Incrémenteur hybride utilisant partiellement l'anticipation de retenues.]]
===Les incrémenteurs 2 bits à calcul séparé de la retenue===
Il est possible de mixer les deux optimisations précédentes, à savoir mixer anticipation de retenue et incrémentation par paquets de deux bits. L'idée est de créer des incrémenteurs 2 bits, qui utilisent l'anticipation de retenue en interne. Ces incrémenteurs 2 bits qui calculent séparément les deux bits du résultat et la retenue sortante. La retenue sortante est calculée à partir des bits de l’opérande, sans propager des retenues. Ainsi, la retenue sortante est connue en avance, sans passer par deux demi-additionneurs. Pour créer un tel incrémenteur, le mieux est de partir de sa table de vérité.
{|class="wikitable"
|-
! Opérande !! Retenue entrante !! !! Retenue sortante !! Résultat
|-
| 00 || 0 || || 0 || 00
|-
| 01 || 0 || || 0 || 01
|-
| 10 || 0 || || 0 || 10
|-
| 11 || 0 || || 0 || 11
|-
| colspan="5" |
|-
| 00 || 1 || || 0 || 01
|-
| 01 || 1 || || 0 || 10
|-
| 10 || 1 || || 0 || 11
|-
| 11 || 1 || || 1 || 00
|}
Elle nous dit que la retenue sortante ne vaut 1 que dans un seul cas : les deux bits d'opérande valent 1, la retenue entrante vaut 1. Elle se calcule alors avec une porte ET à trois entrées. Pour les deux bits du résultat, les équations logiques ne donnent pas un résultat satisfaisant. La solution la plus simple est d'utiliser deux demi-additionneurs et de retirer les portes logiques superflues. Le résultat est alors un '''incrémenteur 2 bits ''carry skip''''', où la retenue sortante est calculée séparément du reste.
[[File:Incrémenteur carry skip 2 bits.png|centre|vignette|upright=2.5|Incrémenteur ''carry skip'' 2 bits]]
Quelques processeurs utilisaient l'anticipation de retenues. Par exemple, le processeur Z-80 de Zilog utilisait un incrémenteur pour des nombres de 16 bits, ce qui demandait des performances assez élevées. Et cet incrémenteur utilisait à la fois anticipation de retenues et ''carry skip''. Il était découpé en quatre blocs avant anticipation de retenues entre eux : un bloc regroupant les 7 bits de poids faible, suivi par un bloc de 5 bits, lui-même suivi par un bloc de 3 bits, terminé par un dernier bit isolé. A l'intérieur de ces blocs, les bits sont regroupés en paires utilisant le ''carry skip''. Pour ceux qui veulent en savoir plus sur cet incrémenteur, voici un lien sur le sujet :
* [https://www.righto.com/2013/11/the-z-80s-16-bit-incrementdecrement.html The Z-80's 16-bit increment/decrement circuit reverse engineered].
==Les décrémenteurs==
Les circuits décrémenteurs font l'inverse d'un incrémenteur : au lieu d'ajouter 1 à un opérande, ils lui soustraient 1. La différence n'est pas énorme, ce qui fait que les circuits incrémenteurs et décrémenteurs se ressemblent beaucoup. Là où les incrémenteurs sont fabriqués à partir de demi-additionneurs, les décrémenteurs sont bâtis avec des demi-soustracteurs.
La soustraction se fait en binaire comme en décimal. On soustrait deux chiffres, puis on doit propager une éventuelle retenue sur la colonne suivante. La retenue apparait quand le chiffre soustrait est plus grand que l'autre chiffre. Elle est propagée sur la colonne suivante, où elle doit être soustraite du résultat. Pour le dire autrement, après avoir soustrait un chiffre, on doit de plus soustraire la retenue de la colonne précédente.
La décrémentation effectue le calcul suivant :
<math>a_7</math> <math>a_6</math> <math>a_5</math> <math>a_4</math> <math>a_3</math> <math>a_2</math> <math>a_1</math> <math>a_0</math>
- 0 0 0 0 0 0 0 1
La colonne la plus à droite soustrait un 1. Par contre, les colonnes suivantes soustraient un zéro. Soustraire un zéro ne change rien. Mais il ne faut pas oublier de soustraire une éventuellement retenue, propagée depuis la colonne précédente. En clair, un décrémenteur peut se construire si on sait soustraire deux bits. On soustrait un 1 sur la colonne la plus à droite, on soustrait une retenue sur les autres colonnes. Et pour soustraire deux bits, il faut utiliser un demi-soustracteur.
===Le demi-soustracteur===
Un '''demi-soustracteur''' soustrait deux bits. Pour comprendre comment soustraire deux bits, traitons les quatre cas possibles un par un.
Premièrement, soustraire zéro à un bit ne changera rien :
* 0 - 0 = 0 ;
* 1 - 0 = 1.
Si on soustrait 1 à un bit qui vaut 1, on obtient zéro.
* 1 - 1 = 0.
Maintenant, que se passe-t-il si on soustrait 1 à 0 ? Voici le résultat :
* 0 - 1 = 1 et une retenue propagée sur la colonne suivante, où elle est soustraite.
La table de soustraction est donc la suivante :
* 0 - 0 = 0 ;
* 0 - 1 = 1 et une retenue ;
* 1 - 0 = 1 ;
* 1 - 1 = 0.
La table de vérité est donc la suivante. Détail important : le demi-soustracteur fait ici la soustraction X - Y. En anglais, X est appelé le ''Minuend'' et Y est appelé le ''Subtrahend'', mais ces termes n'ont pas de traduction française. Aussi, je vais les utiliser tels quels dans ce qui suit.
{|class="wikitable"
|-
! ''Minuend'' !! ''Subtrahend'' !! !! Retenue sortante !! Bit de somme
|-
||0||0|| ||0||0
|-
||0||1|| ||1||1
|-
||1||0|| ||0||1
|-
||1||1|| ||0||0
|}
Si on construit la table de vérité, et qu'on construit le circuit associé, on tombe sur ce circuit.
[[File:Half Subtractor Vektor.svg|centre|vignette|Demi-soustracteur.]]
Vous remarquerez que le bit de somme se calcule de la même manière que pour l'addition, seul le calcul de la retenue change.
===Le décrémenteur à propagation de retenue===
Un décrémenteur à propagation de retenue se construit comme un incrémenteur, sauf qu'on remplace les demi-additionneurs par des demi-soustracteurs. En clair, on enchaine plusieurs demi-soustracteurs les uns à la suite des autres.
Il est possible d'utiliser l'anticipation de retenue, mais les circuits sont cependant différents de l'incrémenteur. Idem avec le ''carry skip'', et toute autre optimisation possible. La raison est que les retenues sont calculées différemment, ce qui fait que l’anticipation de retenue doit être adaptée.
==Les incrémenteurs-décrémenteurs==
Les circuits décrémenteurs ressemblent beaucoup aux incrémenteurs. Aussi, vous ne serez pas étonné qu'il soit possible de fusionner un incrémenteur et un décrémenteur en un seul circuit. Un tel circuit s'appelle un '''incrémenteur-décrémenteur'''. Un tel circuit a une interface similaire à celle d'un incrémenteur, avec une entrée sur laquelle présenter l'opérande, et une sortie sur laquelle récupérer le résultat. Il a aussi une entrée de retenue, sur laquelle envoyer un 1 (incrémentation) ou un 0 (opération NOP/identité). Et il a aussi une sortie de retenue, qui indique que le résultat du calcul a débordé.
Mais en plus de cela, il dispose d'une entrée de commande qui permet de choisir entre incrémentation et décrémentation.
Il existe plusieurs manières de créer un incrémenteur-décrémenteur, que nous allons voir si dessous. Nous n'allons pas voir le cas le plus évident, aussi ne vous étonnez pas si vous avez remarqué qu'un demi-additionneur et un demi-soustracteur se ressemblent beaucoup.
===L'incrémenteur-décrémenteur basé sur des demi-additionneurs===
Une première implémentation se base sur les propriétés du complément à deux, et plus précisément sur cette équation :
: <math>\overline{x} + 1 = \overline{x - 1}</math>
Les deux termes de cette équation sont simplement des définitions équivalentes du complément à deux d'un nombre entier. Celui-ci se calcule de deux manières : soit on incrémente son complément à 1, soit on prend le complément à 1 de son décrément. Et cela nous permet de déterminer comment obtenir une incrémentation à partir d'une décrémentation et inversement.
Que nous dit cette équation ? Simplement que l'on peut décrémenter un nombre si on a deux inverseurs commandables, et un circuit incrémenteur. La décrémentation se fait alors en deux étapes : on calcule son complément à deux, puis on inverse les bits du résultat précédent. Le calcul du complément à deux demande d'avoir un circuit inverseur, et un incrémenteur.
===L'incrémenteur-décrémenteur basé sur des demi-additionneurs===
Vous l'aurez sans doute remarqué, mais le demi-soustracteur ressemble beaucoup au demi-additionneur. La seule différence est une porte NON ajoutée au bon endroit dans le demi-soustracteur. La porte NON inverse le bit X, sachant que le demi-soustracteur fait la soustraction X - Y. De plus, l'opérande inversée est utilisée seulement pour le calcul des retenues, le bit de somme est calculé normalement.
{|
|[[File:Half-adder.svg|vignette|Demi-additionneur]]
|[[File:Half Subtractor Vektor.svg|vignette|upright=1.3|Demi-soustracteur]]
|}
La conséquence est qu'il est possible de fusionner un demi-additionneur avec un demi-soustracteur. L'idée est de remplacer la porte NON du demi-soustracteur par un inverseur commandable. Si on envoie un zéro sur l'entrée de commande, l'inverseur commandable se comportera comme une porte OUI et le circuit sera alors identique à un demi-additionneur. Si on envoie un 1 sur l'entrée de commande, l'inverseur commandable se comportera comme une porte NON. le circuit sera alors un demi-soustracteur. En remplaçant les demi-additionneurs (ou demi-soustracteurs) par de tels circuits, on peut obtenir un circuit faisant à la fois incrémenteur et décrémenteur.
Un défaut de cette approche est que les optimisations vues plus haut pour les demi-additionneurs ne fonctionne tout simplement plus. On est obligé d'utiliser une porte XOR et une porte ET séparées, pas question d'utiliser les demi-additionneurs optimisés vu au début du chapitre.
<noinclude>
{{NavChapitre | book=Fonctionnement d'un ordinateur
| prev=Les circuits de sélection
| prevText=Les circuits de sélection
| next=Les bascules : des mémoires de 1 bit
| nextText=Les bascules : des mémoires de 1 bit
}}
</noinclude>
1ix4fnvwpdlc93agh2xth4ceprqw7jg
768795
768794
2026-06-27T15:47:38Z
Mewtow
31375
/* L'incrémenteur à propagation de retenue */
768795
wikitext
text/x-wiki
Le circuit '''incrémenteur''' incrémente un nombre entier. Plus précisément, il prend en entrée un nombre entier, et fournit en entier le nombre suivant, il lui ajoute 1. Une telle opération peut paraitre triviale et peu intéressante. Cependant, elle est très utilisée et est absolument cruciale pour créer des compteurs, des circuits capables de compter ou décompter, qui auront droit à un chapitre dédié. De plus, au-delà de leur utilisation dans les compteurs, les incrémenteurs étaient très utilisés sur les premiers processeurs 8 bits, comme le Z-80, le 6502, les premiers processeurs x86 comme le 8008, le 8086, le 8085, et bien d'autres.
Il existe aussi des ''circuits décrémenteurs'', qui décrémentent un opérande, ainsi que des ''circuits incrémenteur-décrémenteur'', qui peuvent incrémenter ou décrémenter, selon comment on les configure. Je ne vais pas détailler ces circuits plus que ça, car de tels circuits sont assez rares, comparé à un circuit incrémenteur simple.
==Le demi-additionneur==
Le circuit incrémenteur effectue l'opération suivante :
<math>a_7</math> <math>a_6</math> <math>a_5</math> <math>a_4</math> <math>a_3</math> <math>a_2</math> <math>a_1</math> <math>a_0</math>
+ 0 0 0 0 0 0 0 1
Un incrémenteur basique doit donc faire une addition pour chaque colonne, et précisément une addition de deux bits. Il se trouve que la table d'addition est très simple en binaire. Jugez plutôt :
* 0 + 0 = 0, retenue = 0 ;
* 0 + 1 = 1, retenue = 0 ;
* 1 + 0 = 1, retenue = 0 ;
* 1 + 1 = 0, retenue = 1.
Un circuit capable d'additionner deux bits est appelé un '''demi-additionneur'''. Il dispose d'une sortie S pour la somme, et C pour la retenue (''carry'' an anglais).
===Le demi-additionneur classique===
Un demi-additionneur est très simple à construire avec les techniques vues dans les premiers chapitres. Voici sa table de vérité :
{|class="wikitable"
|-
! Retenue entrante !! Opérande 1 !! !! Retenue sortante !! Bit de somme
|-
||0||0|| ||0||0
|-
||0||1|| ||0||1
|-
||1||0|| ||0||1
|-
||1||1|| ||1||0
|}
On voit immédiatement que la colonne des retenues donne une porte ET, alors que celle du bit de somme est calculé par un XOR.
{| class="flexible"
|[[File:1-bit half-adder.svg|class=transparent|centre|Demi-addtionneur.]]
|[[File:Half-adder.svg|centre|class=transparent|Circuit d'un demi-addtionneur.]]
|}
Il existe beaucoup d'autres implémentations alternatives, qui utilisent moins de portes logiques, calculent la retenue plus rapidement, consomment moins d'énergie, et autres. Et il est intéressant d'étudier quelques alternatives, surtout qu'elles serviront plus bas, quand on étudiera le circuit incrémenteur du 8085.
===Le demi-additionneur basé sur une modification de la retenue===
Une source d'amélioration est liée à la porte XOR. En pratique, une porte XOR est composée en combinant plusieurs portes logiques ET/OU/NOR/NAND ensemble. Et il se trouve qu'il y a des redondances entre celles-ci et les portes utilisées pour calculer la retenue. Par exemple, rappelons qu'une porte XOR peut être construite avec une porte ET et deux portes NOR, comme illustré ci-dessous. Et il se trouve que la porte ET interne calcule la retenue sortante. En supprimant cette redondance, on économise quelques portes logiques. De plus, on se retrouve avec un demi-additionneur qui calcule le bit de somme à partir de la retenue sortante.
[[File:Demi-additionneur avec redondances éliminées.png|centre|vignette|upright=2|Demi-additionneur avec redondances éliminées]]
Le circuit précédent a une interprétation logique. Si vous regardez la table de vérité, vous remarquerez que la somme de deux bits est égale à l'inverse de la retenue sortante, sauf dans le cas où les deux bits additionnés valent zéro. Et le circuit précédent est basé là-dessus.
{|class="wikitable"
|-
! Retenue entrante !! Opérande 1 !! !! Retenue sortante !! Bit de somme
|- class="f_vert"
||0||0|| ||0||0
|- class="f_rouge"
||0||1|| ||0||1
|- class="f_rouge"
||1||0|| ||0||1
|- class="f_rouge"
||1||1|| ||1||0
|}
L'idée est de calculer l'inverse du bit de somme, avant de l'inverser avec une porte NON. L'inverse du bit de somme vaut 1, soit quand la retenue est à 1, soit quand les deux bits additionnés sont à 0. La porte ET calcule la retenue sortante, la première porte NOR détecte sur les deux bits d'entrée valent zéro. Une porte OU combine les deux résultat pour obtenir l'inverse du bit d'entrée, puis une porte NOn inverse le tout pour obtenir le bit de somme adéquat. Les deux portes sont fusionnées : c'est la seconde porte NOR.
[[File:Full adder HA + MUX.png|centre|vignette|upright=2.5|Demi-additionneur basé sur une inversion de la retenue sortante.]]
===Les implémentations alternatives===
Une implémentation alternative, qui n'utilise que des portes NOR, est la suivante :
[[File:Demi-additionneur fait avec des portes NOR et NAND.png|centre|vignette|upright=2|Demi-additionneur fait avec des portes NOR et NAND.]]
Une implémentation alternative utilise des portes NAND. Pour rappel, il est possible de créer une porte XOR avec 4 portes NAND. La première d'entre elle fait un NAND entre les deux bits d'entrée, ce qui fait qu'elle calcule l'inverse de la retenue sortante. Le tout est illustré ci-dessous. En théorie, on devrait utiliser une porte NON pour récupérer la retenue correcte. Mais nous verrons plus bas que ce n'est pas une obligation. Nous verrons plus bas un exemple où c'est l'inverse de la retenue qui est utilisée dans les calculs, pour rendre les calculs plus rapides.
[[File:Demi-additionneur fait avec des portes NAND.png|centre|vignette|upright=2|Demi-additionneur fait avec des portes NAND]]
==Le circuit incrémenteur==
Maintenant que l'on sait comment additionner deux bits, reprenons l'opération d'incrémentation :
<math>a_7</math> <math>a_6</math> <math>a_5</math> <math>a_4</math> <math>a_3</math> <math>a_2</math> <math>a_1</math> <math>a_0</math>
+ 0 0 0 0 0 0 0 1
------------------------------
Sur la colonne la plus à droite, il doit ajouter un au bit de poids faible. Pour les colonnes suivantes, il faut additionner le bit de l'opérande avec la retenue de la colonne précédente. En clair, on n'additionne que deux bits à chaque colonne : un 1 sur celle tout à droite, la retenue de la colonne précédente pour les autres. Et cela nous donne une idée de comment faire pour créer le circuit incrémenteur.
===L'incrémenteur à propagation de retenue===
Il suffit d'utiliser un demi-additionneur par colonne, et de les enchainer les uns à la suite des autres. Chaque demi-additionneur additionne le bit de l'opérande avec la retenue fournie par le demi-additionneur précédent. Le seul qui fait exception est celui pour la colonne de poids faible. Pour celui-là, il doit ajouter 1 au bit de poids faible. Le résultat est appelé un '''incrémenteur à propagation de retenue'''. Il est constitué de demi-additionneurs enchaînés les uns à la suite des autres, du bit de poids faible vers le bit de poids fort.
[[File:Circuit incrémenteur.png|centre|vignette|upright=3|Circuit incrémenteur.]]
Maintenant, regardons le demi-additionneur le plus à droite, celui pour le bit de poids faible. Son entrée de retenue entrante est mise à 1 pour faire l'incrémentation. Quelques incrémenteurs permettent de configurer cette entrée de retenue à 0 ou à 1, ce qui effectue : soit une opération identité (l'opérande est recopié sur la sortie), soit une incrémentation. Un tel circuit est nommé un '''incrémenteur commandable'''. Nous aurons à utiliser une fois ou deux de tels incrémenteurs commandables dans la suite du cours.
L'incrémenteur à propagation de retenue est le plus simple et le plus économe en portes logiques. Mais de tels incrémenteurs sont rarement utilisés. À la place, on leur préfère des incrémenteurs plus rapides, mais qui utilisent plus de portes logiques. De tels incrémenteurs accélèrent le calcul des retenues. En effet, la rapidité d'une incrémentation est limitée par la propagation de la retenue : les retenues commencent à être calculées au bit de poids fort et on doit les calculer une par une, jusqu’à atteindre le bit de poids fort. Et cette "propagation des retenues" prend du temps, d'autant plus de temps que l'opérande est longue. Il y a deux optimisations principales, appelées le ''carry skip'' et l'anticipation de retenue, que nous allons décrire ci-dessous.
===Les incrémenteurs optimisés===
L'optimisation que nous allns voir effectue l'incrémentation, non pas bit par bit, mais par paquets de deux bits. Le résultat est que l'incrémentation est deux fois plus rapide, ou presque. Le circuit incrémenteur est donc composé en enchainant non pas des demi-additionneurs, mais des '''incrémenteurs 2 bits''' qui incrémentent un opérande de deux bits.
[[File:Circuit incrémenteur optimisé, faisant l'incrémentation par paquet de deux bits.png|centre|vignette|upright=2.5|Circuit incrémenteur optimisé, faisant l'incrémentation par paquet de deux bits]]
Reste à concevoir l'incrémenteur 2 bits. Une première méthode part de deux demi-additionneurs et effectue quelques simplifications au niveau des transistors et des portes logiques. Par exemple, l'Intel 8085 a utilisé un circuit optimisé, fabriqué avec des portes NAND et NOR. La raison est qu'avec les technologies de transistors CMOS actuelles, les portes NAND et NOR utilisent moins de transistors que les portes ET et OU. Et cette possibilité a été utilisée pour effectuer des simplifications assez mineures, mais toujours bonnes à prendre. Mais nous ne pouvons pas en parler en détail ici, car nous n'avons pas encore parlé en détail des transistors. Cependant, un exemple bien précis nous est accesible : celui de l’incrémenteur du processeur 8085 d'Intel.
Le processeur 8085 d'Intel est un processeur 8 bits très ancien. Il contenait un incrémenteur 16 bits, qui était utilisé pour calculer des adresses mémoire. Et cet incrémenteur était un incrémenteur à propagation de retenue optimisé. Il utilisait lui aussi des incrémenteurs 2 bits un peu modifiés.
L'idée était que les retenues sortant des colonnes paires étaient inversées, les colonnes impaires faisaient les calculs à partir de cette retenue inversée. Les demi-additionneurs des colonnes impaires n'étaient donc pas les mêmes que ceux des colonnes paires. Leurs tables de vérité sont différentes, leurs circuits aussi, et aucun des deux ne ressemble à un demi-additionneur normal. En réalité, ils sont fabriqués à partir des implémentations vues plus haut, qui utilisent uniquement des portes NAND ou uniquement des portes NOR.
Pour comprendre l'intérêt de faire ainsi, nous devons préciser une chose importante : avec les technologies CMOS utilisées pour les processeurs depuis les années 70, les portes logiques les plus simples sont les portes NON, NOR et NAND. Les portes ET/OU sont fabriquées en combinant des portes NOR/NAND avec une porte NON, ce qui prend plus de transistors.
Un demi-additionneur est donc fabriqué comme illustré ci-dessous, en logique CMOS. Seules les portes pour le calcul de la retenue sont indiquées, la porte XOR pour le bit de somme est fabriquée à partir de plusieurs portes logiques.
[[File:Demi-additionneur en CMOS.png|centre|vignette|upright=2|Demi-additionneur en CMOS]]
Si on enchaine deux demi-additionneurs, cela donne ceci :
[[File:Brique de base de l'incrémenteur du 8085.png|centre|vignette|upright=2|Brique de base de l'incrémenteur du 8085]]
La retenue doit donc traverser quatre portes logiques, dont deux portes NON. En inversant la retenue, les portes en jaune dans le schéma précédent se simplifient. Il suffit d'utiliser la loi de de Morgan pour trouver un circuit équivalent aux portes jaunes, qui n'est autre qu'une porte NOR couplée à une porte NON. Un autre moyen d'obtenir le même résultat est de prendre un demi-additionneur fabriqué avec uniquement des portes NOR, et de retirer la porte NON adéquate. Il faut ensuite corriger l'entrée de retenue de la porte XOR, pour qu'elle fasse l'addition avec la retenue correctement. Le résultat est illustré ci-dessous.
[[File:Incrémenteur 2 bits de l'incrémenteur du 8085.png|centre|vignette|upright=2|Incrémenteur 2 bits de l'incrémenteur du 8085]]
Le circuit utilise le même nombre de portes logiques, il n'économise pas de transistors. Par contre, la retenue n'a plus qu'à traverser deux portes logiques, au lieu de quatre. La propagation de la retenue est donc plus rapide, l'incrémentation est donc plus rapide.
===Les incrémenteurs à anticipation de retenue===
L''''anticipation de retenue''' calcule la retenue d'une colonne sans attendre les retenues des colonnes précédentes. Dans le cas idéal, toutes les retenues sont calculées en parallèle, en même temps, et sont ensuite envoyées aux demi-additionneurs. Il s'agit d'une optimisation qui est utilisée pour l'incrémentation, l'addition et d'autres opérations similaires. Pour l'incrémentation, déterminer la retenue ne demande pas de calculs complexes, contrairement à l'addition.
[[File:Incrémenteur à anticipation de retenues, 8 bits.png|centre|vignette|upright=2.5|Incrémenteur à anticipation de retenues, 8 bits.]]
Pour le comprendre, on peut regarder comment la retenue sortant d'un demi-additionneur est formée. Elle est calculée en faisant un ET logique entre la retenue entrante et le bit d'opérande. Une retenue est donc un ET logique entre toutes les retenues précédentes. Un incrémenteur à anticipation de retenue utilise donc une porte ET à plusieurs entrées pour calculer une retenue.
[[File:Anticipation de retenue pour un bit du résultat, incrémenteur.png|centre|vignette|upright=2.5|Anticipation de retenue pour un bit du résultat, incrémenteur]]
En théorie, on peut utiliser une porte ET à plusieurs entrées pour chaque bit de l'opérande. Cependant, cela entrainera un cout en transistors très important. Pour éviter de gaspiller trop de portes logiques, une solution est de mélanger anticipation de retenues et propagation de retenue. Par exemple, pour un incrémenteur 32 bits, on peut découper l'opérande en 4 octets : on anticipe les retenues pour chaque octet, mais l'incrémentation de chaque octet se fait avec propagation de retenue et/ou ''carry skip''.
[[File:Incrémenteur à anticipation de retenues.png|centre|vignette|upright=3|Incrémenteur hybride utilisant partiellement l'anticipation de retenues.]]
===Les incrémenteurs 2 bits à calcul séparé de la retenue===
Il est possible de mixer les deux optimisations précédentes, à savoir mixer anticipation de retenue et incrémentation par paquets de deux bits. L'idée est de créer des incrémenteurs 2 bits, qui utilisent l'anticipation de retenue en interne. Ces incrémenteurs 2 bits qui calculent séparément les deux bits du résultat et la retenue sortante. La retenue sortante est calculée à partir des bits de l’opérande, sans propager des retenues. Ainsi, la retenue sortante est connue en avance, sans passer par deux demi-additionneurs. Pour créer un tel incrémenteur, le mieux est de partir de sa table de vérité.
{|class="wikitable"
|-
! Opérande !! Retenue entrante !! !! Retenue sortante !! Résultat
|-
| 00 || 0 || || 0 || 00
|-
| 01 || 0 || || 0 || 01
|-
| 10 || 0 || || 0 || 10
|-
| 11 || 0 || || 0 || 11
|-
| colspan="5" |
|-
| 00 || 1 || || 0 || 01
|-
| 01 || 1 || || 0 || 10
|-
| 10 || 1 || || 0 || 11
|-
| 11 || 1 || || 1 || 00
|}
Elle nous dit que la retenue sortante ne vaut 1 que dans un seul cas : les deux bits d'opérande valent 1, la retenue entrante vaut 1. Elle se calcule alors avec une porte ET à trois entrées. Pour les deux bits du résultat, les équations logiques ne donnent pas un résultat satisfaisant. La solution la plus simple est d'utiliser deux demi-additionneurs et de retirer les portes logiques superflues. Le résultat est alors un '''incrémenteur 2 bits ''carry skip''''', où la retenue sortante est calculée séparément du reste.
[[File:Incrémenteur carry skip 2 bits.png|centre|vignette|upright=2.5|Incrémenteur ''carry skip'' 2 bits]]
Quelques processeurs utilisaient l'anticipation de retenues. Par exemple, le processeur Z-80 de Zilog utilisait un incrémenteur pour des nombres de 16 bits, ce qui demandait des performances assez élevées. Et cet incrémenteur utilisait à la fois anticipation de retenues et ''carry skip''. Il était découpé en quatre blocs avant anticipation de retenues entre eux : un bloc regroupant les 7 bits de poids faible, suivi par un bloc de 5 bits, lui-même suivi par un bloc de 3 bits, terminé par un dernier bit isolé. A l'intérieur de ces blocs, les bits sont regroupés en paires utilisant le ''carry skip''. Pour ceux qui veulent en savoir plus sur cet incrémenteur, voici un lien sur le sujet :
* [https://www.righto.com/2013/11/the-z-80s-16-bit-incrementdecrement.html The Z-80's 16-bit increment/decrement circuit reverse engineered].
==Les décrémenteurs==
Les circuits décrémenteurs font l'inverse d'un incrémenteur : au lieu d'ajouter 1 à un opérande, ils lui soustraient 1. La différence n'est pas énorme, ce qui fait que les circuits incrémenteurs et décrémenteurs se ressemblent beaucoup. Là où les incrémenteurs sont fabriqués à partir de demi-additionneurs, les décrémenteurs sont bâtis avec des demi-soustracteurs.
La soustraction se fait en binaire comme en décimal. On soustrait deux chiffres, puis on doit propager une éventuelle retenue sur la colonne suivante. La retenue apparait quand le chiffre soustrait est plus grand que l'autre chiffre. Elle est propagée sur la colonne suivante, où elle doit être soustraite du résultat. Pour le dire autrement, après avoir soustrait un chiffre, on doit de plus soustraire la retenue de la colonne précédente.
La décrémentation effectue le calcul suivant :
<math>a_7</math> <math>a_6</math> <math>a_5</math> <math>a_4</math> <math>a_3</math> <math>a_2</math> <math>a_1</math> <math>a_0</math>
- 0 0 0 0 0 0 0 1
La colonne la plus à droite soustrait un 1. Par contre, les colonnes suivantes soustraient un zéro. Soustraire un zéro ne change rien. Mais il ne faut pas oublier de soustraire une éventuellement retenue, propagée depuis la colonne précédente. En clair, un décrémenteur peut se construire si on sait soustraire deux bits. On soustrait un 1 sur la colonne la plus à droite, on soustrait une retenue sur les autres colonnes. Et pour soustraire deux bits, il faut utiliser un demi-soustracteur.
===Le demi-soustracteur===
Un '''demi-soustracteur''' soustrait deux bits. Pour comprendre comment soustraire deux bits, traitons les quatre cas possibles un par un.
Premièrement, soustraire zéro à un bit ne changera rien :
* 0 - 0 = 0 ;
* 1 - 0 = 1.
Si on soustrait 1 à un bit qui vaut 1, on obtient zéro.
* 1 - 1 = 0.
Maintenant, que se passe-t-il si on soustrait 1 à 0 ? Voici le résultat :
* 0 - 1 = 1 et une retenue propagée sur la colonne suivante, où elle est soustraite.
La table de soustraction est donc la suivante :
* 0 - 0 = 0 ;
* 0 - 1 = 1 et une retenue ;
* 1 - 0 = 1 ;
* 1 - 1 = 0.
La table de vérité est donc la suivante. Détail important : le demi-soustracteur fait ici la soustraction X - Y. En anglais, X est appelé le ''Minuend'' et Y est appelé le ''Subtrahend'', mais ces termes n'ont pas de traduction française. Aussi, je vais les utiliser tels quels dans ce qui suit.
{|class="wikitable"
|-
! ''Minuend'' !! ''Subtrahend'' !! !! Retenue sortante !! Bit de somme
|-
||0||0|| ||0||0
|-
||0||1|| ||1||1
|-
||1||0|| ||0||1
|-
||1||1|| ||0||0
|}
Si on construit la table de vérité, et qu'on construit le circuit associé, on tombe sur ce circuit.
[[File:Half Subtractor Vektor.svg|centre|vignette|Demi-soustracteur.]]
Vous remarquerez que le bit de somme se calcule de la même manière que pour l'addition, seul le calcul de la retenue change.
===Le décrémenteur à propagation de retenue===
Un décrémenteur à propagation de retenue se construit comme un incrémenteur, sauf qu'on remplace les demi-additionneurs par des demi-soustracteurs. En clair, on enchaine plusieurs demi-soustracteurs les uns à la suite des autres.
Il est possible d'utiliser l'anticipation de retenue, mais les circuits sont cependant différents de l'incrémenteur. Idem avec le ''carry skip'', et toute autre optimisation possible. La raison est que les retenues sont calculées différemment, ce qui fait que l’anticipation de retenue doit être adaptée.
==Les incrémenteurs-décrémenteurs==
Les circuits décrémenteurs ressemblent beaucoup aux incrémenteurs. Aussi, vous ne serez pas étonné qu'il soit possible de fusionner un incrémenteur et un décrémenteur en un seul circuit. Un tel circuit s'appelle un '''incrémenteur-décrémenteur'''. Un tel circuit a une interface similaire à celle d'un incrémenteur, avec une entrée sur laquelle présenter l'opérande, et une sortie sur laquelle récupérer le résultat. Il a aussi une entrée de retenue, sur laquelle envoyer un 1 (incrémentation) ou un 0 (opération NOP/identité). Et il a aussi une sortie de retenue, qui indique que le résultat du calcul a débordé.
Mais en plus de cela, il dispose d'une entrée de commande qui permet de choisir entre incrémentation et décrémentation.
Il existe plusieurs manières de créer un incrémenteur-décrémenteur, que nous allons voir si dessous. Nous n'allons pas voir le cas le plus évident, aussi ne vous étonnez pas si vous avez remarqué qu'un demi-additionneur et un demi-soustracteur se ressemblent beaucoup.
===L'incrémenteur-décrémenteur basé sur des demi-additionneurs===
Une première implémentation se base sur les propriétés du complément à deux, et plus précisément sur cette équation :
: <math>\overline{x} + 1 = \overline{x - 1}</math>
Les deux termes de cette équation sont simplement des définitions équivalentes du complément à deux d'un nombre entier. Celui-ci se calcule de deux manières : soit on incrémente son complément à 1, soit on prend le complément à 1 de son décrément. Et cela nous permet de déterminer comment obtenir une incrémentation à partir d'une décrémentation et inversement.
Que nous dit cette équation ? Simplement que l'on peut décrémenter un nombre si on a deux inverseurs commandables, et un circuit incrémenteur. La décrémentation se fait alors en deux étapes : on calcule son complément à deux, puis on inverse les bits du résultat précédent. Le calcul du complément à deux demande d'avoir un circuit inverseur, et un incrémenteur.
===L'incrémenteur-décrémenteur basé sur des demi-additionneurs===
Vous l'aurez sans doute remarqué, mais le demi-soustracteur ressemble beaucoup au demi-additionneur. La seule différence est une porte NON ajoutée au bon endroit dans le demi-soustracteur. La porte NON inverse le bit X, sachant que le demi-soustracteur fait la soustraction X - Y. De plus, l'opérande inversée est utilisée seulement pour le calcul des retenues, le bit de somme est calculé normalement.
{|
|[[File:Half-adder.svg|vignette|Demi-additionneur]]
|[[File:Half Subtractor Vektor.svg|vignette|upright=1.3|Demi-soustracteur]]
|}
La conséquence est qu'il est possible de fusionner un demi-additionneur avec un demi-soustracteur. L'idée est de remplacer la porte NON du demi-soustracteur par un inverseur commandable. Si on envoie un zéro sur l'entrée de commande, l'inverseur commandable se comportera comme une porte OUI et le circuit sera alors identique à un demi-additionneur. Si on envoie un 1 sur l'entrée de commande, l'inverseur commandable se comportera comme une porte NON. le circuit sera alors un demi-soustracteur. En remplaçant les demi-additionneurs (ou demi-soustracteurs) par de tels circuits, on peut obtenir un circuit faisant à la fois incrémenteur et décrémenteur.
Un défaut de cette approche est que les optimisations vues plus haut pour les demi-additionneurs ne fonctionne tout simplement plus. On est obligé d'utiliser une porte XOR et une porte ET séparées, pas question d'utiliser les demi-additionneurs optimisés vu au début du chapitre.
<noinclude>
{{NavChapitre | book=Fonctionnement d'un ordinateur
| prev=Les circuits de sélection
| prevText=Les circuits de sélection
| next=Les bascules : des mémoires de 1 bit
| nextText=Les bascules : des mémoires de 1 bit
}}
</noinclude>
mlv5hpg1x2kwo3tgkw85zxppxg37vtj
768796
768795
2026-06-27T15:50:48Z
Mewtow
31375
/* Le circuit incrémenteur */
768796
wikitext
text/x-wiki
Le circuit '''incrémenteur''' incrémente un nombre entier. Plus précisément, il prend en entrée un nombre entier, et fournit en entier le nombre suivant, il lui ajoute 1. Une telle opération peut paraitre triviale et peu intéressante. Cependant, elle est très utilisée et est absolument cruciale pour créer des compteurs, des circuits capables de compter ou décompter, qui auront droit à un chapitre dédié. De plus, au-delà de leur utilisation dans les compteurs, les incrémenteurs étaient très utilisés sur les premiers processeurs 8 bits, comme le Z-80, le 6502, les premiers processeurs x86 comme le 8008, le 8086, le 8085, et bien d'autres.
Il existe aussi des ''circuits décrémenteurs'', qui décrémentent un opérande, ainsi que des ''circuits incrémenteur-décrémenteur'', qui peuvent incrémenter ou décrémenter, selon comment on les configure. Je ne vais pas détailler ces circuits plus que ça, car de tels circuits sont assez rares, comparé à un circuit incrémenteur simple.
==Le demi-additionneur==
Le circuit incrémenteur effectue l'opération suivante :
<math>a_7</math> <math>a_6</math> <math>a_5</math> <math>a_4</math> <math>a_3</math> <math>a_2</math> <math>a_1</math> <math>a_0</math>
+ 0 0 0 0 0 0 0 1
Un incrémenteur basique doit donc faire une addition pour chaque colonne, et précisément une addition de deux bits. Il se trouve que la table d'addition est très simple en binaire. Jugez plutôt :
* 0 + 0 = 0, retenue = 0 ;
* 0 + 1 = 1, retenue = 0 ;
* 1 + 0 = 1, retenue = 0 ;
* 1 + 1 = 0, retenue = 1.
Un circuit capable d'additionner deux bits est appelé un '''demi-additionneur'''. Il dispose d'une sortie S pour la somme, et C pour la retenue (''carry'' an anglais).
===Le demi-additionneur classique===
Un demi-additionneur est très simple à construire avec les techniques vues dans les premiers chapitres. Voici sa table de vérité :
{|class="wikitable"
|-
! Retenue entrante !! Opérande 1 !! !! Retenue sortante !! Bit de somme
|-
||0||0|| ||0||0
|-
||0||1|| ||0||1
|-
||1||0|| ||0||1
|-
||1||1|| ||1||0
|}
On voit immédiatement que la colonne des retenues donne une porte ET, alors que celle du bit de somme est calculé par un XOR.
{| class="flexible"
|[[File:1-bit half-adder.svg|class=transparent|centre|Demi-addtionneur.]]
|[[File:Half-adder.svg|centre|class=transparent|Circuit d'un demi-addtionneur.]]
|}
Il existe beaucoup d'autres implémentations alternatives, qui utilisent moins de portes logiques, calculent la retenue plus rapidement, consomment moins d'énergie, et autres. Et il est intéressant d'étudier quelques alternatives, surtout qu'elles serviront plus bas, quand on étudiera le circuit incrémenteur du 8085.
===Le demi-additionneur basé sur une modification de la retenue===
Une source d'amélioration est liée à la porte XOR. En pratique, une porte XOR est composée en combinant plusieurs portes logiques ET/OU/NOR/NAND ensemble. Et il se trouve qu'il y a des redondances entre celles-ci et les portes utilisées pour calculer la retenue. Par exemple, rappelons qu'une porte XOR peut être construite avec une porte ET et deux portes NOR, comme illustré ci-dessous. Et il se trouve que la porte ET interne calcule la retenue sortante. En supprimant cette redondance, on économise quelques portes logiques. De plus, on se retrouve avec un demi-additionneur qui calcule le bit de somme à partir de la retenue sortante.
[[File:Demi-additionneur avec redondances éliminées.png|centre|vignette|upright=2|Demi-additionneur avec redondances éliminées]]
Le circuit précédent a une interprétation logique. Si vous regardez la table de vérité, vous remarquerez que la somme de deux bits est égale à l'inverse de la retenue sortante, sauf dans le cas où les deux bits additionnés valent zéro. Et le circuit précédent est basé là-dessus.
{|class="wikitable"
|-
! Retenue entrante !! Opérande 1 !! !! Retenue sortante !! Bit de somme
|- class="f_vert"
||0||0|| ||0||0
|- class="f_rouge"
||0||1|| ||0||1
|- class="f_rouge"
||1||0|| ||0||1
|- class="f_rouge"
||1||1|| ||1||0
|}
L'idée est de calculer l'inverse du bit de somme, avant de l'inverser avec une porte NON. L'inverse du bit de somme vaut 1, soit quand la retenue est à 1, soit quand les deux bits additionnés sont à 0. La porte ET calcule la retenue sortante, la première porte NOR détecte sur les deux bits d'entrée valent zéro. Une porte OU combine les deux résultat pour obtenir l'inverse du bit d'entrée, puis une porte NOn inverse le tout pour obtenir le bit de somme adéquat. Les deux portes sont fusionnées : c'est la seconde porte NOR.
[[File:Full adder HA + MUX.png|centre|vignette|upright=2.5|Demi-additionneur basé sur une inversion de la retenue sortante.]]
===Les implémentations alternatives===
Une implémentation alternative, qui n'utilise que des portes NOR, est la suivante :
[[File:Demi-additionneur fait avec des portes NOR et NAND.png|centre|vignette|upright=2|Demi-additionneur fait avec des portes NOR et NAND.]]
Une implémentation alternative utilise des portes NAND. Pour rappel, il est possible de créer une porte XOR avec 4 portes NAND. La première d'entre elle fait un NAND entre les deux bits d'entrée, ce qui fait qu'elle calcule l'inverse de la retenue sortante. Le tout est illustré ci-dessous. En théorie, on devrait utiliser une porte NON pour récupérer la retenue correcte. Mais nous verrons plus bas que ce n'est pas une obligation. Nous verrons plus bas un exemple où c'est l'inverse de la retenue qui est utilisée dans les calculs, pour rendre les calculs plus rapides.
[[File:Demi-additionneur fait avec des portes NAND.png|centre|vignette|upright=2|Demi-additionneur fait avec des portes NAND]]
==Le circuit incrémenteur==
Maintenant que l'on sait comment additionner deux bits, reprenons l'opération d'incrémentation :
<math>a_7</math> <math>a_6</math> <math>a_5</math> <math>a_4</math> <math>a_3</math> <math>a_2</math> <math>a_1</math> <math>a_0</math>
+ 0 0 0 0 0 0 0 1
------------------------------
Sur la colonne la plus à droite, il doit ajouter un au bit de poids faible. Pour les colonnes suivantes, il faut additionner le bit de l'opérande avec la retenue de la colonne précédente. En clair, on n'additionne que deux bits à chaque colonne : un 1 sur celle tout à droite, la retenue de la colonne précédente pour les autres. Et cela nous donne une idée de comment faire pour créer le circuit incrémenteur.
===L'incrémenteur à propagation de retenue===
Il suffit d'utiliser un demi-additionneur par colonne, et de les enchainer les uns à la suite des autres. Chaque demi-additionneur additionne le bit de l'opérande avec la retenue fournie par le demi-additionneur précédent. Le seul qui fait exception est celui pour la colonne de poids faible. Pour celui-là, il doit ajouter 1 au bit de poids faible. Le résultat est appelé un '''incrémenteur à propagation de retenue'''. Il est constitué de demi-additionneurs enchaînés les uns à la suite des autres, du bit de poids faible vers le bit de poids fort.
[[File:Circuit incrémenteur.png|centre|vignette|upright=3|Circuit incrémenteur.]]
Maintenant, regardons le demi-additionneur le plus à droite, celui pour le bit de poids faible. Son entrée de retenue entrante est mise à 1 pour faire l'incrémentation. Quelques incrémenteurs permettent de configurer cette entrée de retenue à 0 ou à 1, ce qui effectue : soit une opération identité (l'opérande est recopié sur la sortie), soit une incrémentation. Un tel circuit est nommé un '''incrémenteur commandable'''. Nous aurons à utiliser une fois ou deux de tels incrémenteurs commandables dans la suite du cours.
L'incrémenteur à propagation de retenue est le plus simple et le plus économe en portes logiques. Mais de tels incrémenteurs sont rarement utilisés. À la place, on leur préfère des incrémenteurs plus rapides, mais qui utilisent plus de portes logiques. De tels incrémenteurs accélèrent le calcul des retenues. En effet, la rapidité d'une incrémentation est limitée par la propagation de la retenue : les retenues commencent à être calculées au bit de poids fort et on doit les calculer une par une, jusqu’à atteindre le bit de poids fort. Et cette "propagation des retenues" prend du temps, d'autant plus de temps que l'opérande est longue. Il y a deux optimisations principales, appelées le ''carry skip'' et l'anticipation de retenue, que nous allons décrire ci-dessous.
===Les incrémenteurs optimisés===
L'optimisation que nous allns voir effectue l'incrémentation, non pas bit par bit, mais par paquets de deux bits. Le résultat est que l'incrémentation est deux fois plus rapide, ou presque. Le circuit incrémenteur est donc composé en enchainant non pas des demi-additionneurs, mais des '''incrémenteurs 2 bits''' qui incrémentent un opérande de deux bits.
[[File:Circuit incrémenteur optimisé, faisant l'incrémentation par paquet de deux bits.png|centre|vignette|upright=2.5|Circuit incrémenteur optimisé, faisant l'incrémentation par paquet de deux bits]]
Reste à concevoir l'incrémenteur 2 bits. Une première méthode part de deux demi-additionneurs et effectue quelques simplifications au niveau des transistors et des portes logiques. Par exemple, l'Intel 8085 a utilisé un circuit optimisé, fabriqué avec des portes NAND et NOR. La raison est qu'avec les technologies de transistors CMOS actuelles, les portes NAND et NOR utilisent moins de transistors que les portes ET et OU. Et cette possibilité a été utilisée pour effectuer des simplifications assez mineures, mais toujours bonnes à prendre. Mais nous ne pouvons pas en parler en détail ici, car nous n'avons pas encore parlé en détail des transistors. Cependant, un exemple bien précis nous est accesible : celui de l’incrémenteur du processeur 8085 d'Intel.
Le processeur 8085 d'Intel est un processeur 8 bits très ancien. Il contenait un incrémenteur 16 bits, qui était utilisé pour calculer des adresses mémoire. Et cet incrémenteur était un incrémenteur à propagation de retenue optimisé. Il utilisait lui aussi des incrémenteurs 2 bits un peu modifiés.
L'idée était que les retenues sortant des colonnes paires étaient inversées, les colonnes impaires faisaient les calculs à partir de cette retenue inversée. Les demi-additionneurs des colonnes impaires n'étaient donc pas les mêmes que ceux des colonnes paires. Leurs tables de vérité sont différentes, leurs circuits aussi, et aucun des deux ne ressemble à un demi-additionneur normal. En réalité, ils sont fabriqués à partir des implémentations vues plus haut, qui utilisent uniquement des portes NAND ou uniquement des portes NOR.
Pour comprendre l'intérêt de faire ainsi, nous devons préciser une chose importante : avec les technologies CMOS utilisées pour les processeurs depuis les années 70, les portes logiques les plus simples sont les portes NON, NOR et NAND. Les portes ET/OU sont fabriquées en combinant des portes NOR/NAND avec une porte NON, ce qui prend plus de transistors.
Un demi-additionneur est donc fabriqué comme illustré ci-dessous, en logique CMOS. Seules les portes pour le calcul de la retenue sont indiquées, la porte XOR pour le bit de somme est fabriquée à partir de plusieurs portes logiques.
[[File:Demi-additionneur en CMOS.png|centre|vignette|upright=2|Demi-additionneur en CMOS]]
Si on enchaine deux demi-additionneurs, cela donne ceci :
[[File:Brique de base de l'incrémenteur du 8085.png|centre|vignette|upright=2|Brique de base de l'incrémenteur du 8085]]
La retenue doit donc traverser quatre portes logiques, dont deux portes NON. En inversant la retenue, les portes en jaune dans le schéma précédent se simplifient. Il suffit d'utiliser la loi de de Morgan pour trouver un circuit équivalent aux portes jaunes, qui n'est autre qu'une porte NOR couplée à une porte NON. Un autre moyen d'obtenir le même résultat est de prendre un demi-additionneur fabriqué avec uniquement des portes NOR, et de retirer la porte NON adéquate. Il faut ensuite corriger l'entrée de retenue de la porte XOR, pour qu'elle fasse l'addition avec la retenue correctement. Le résultat est illustré ci-dessous.
[[File:Incrémenteur 2 bits de l'incrémenteur du 8085.png|centre|vignette|upright=2|Incrémenteur 2 bits de l'incrémenteur du 8085]]
Le circuit utilise le même nombre de portes logiques, il n'économise pas de transistors. Par contre, la retenue n'a plus qu'à traverser deux portes logiques, au lieu de quatre. La propagation de la retenue est donc plus rapide, l'incrémentation est donc plus rapide.
===Les incrémenteurs 2 bits à calcul séparé de la retenue===
Il existe une autre méthode qui permet de créer des incrémenteurs 2 bits plus rapides que les précédents, mais qui utilisent plus de portes logiques. Ces incrémenteurs 2 bits calculent séparément les deux bits du résultat et la retenue sortante. La retenue sortante est calculée à partir des bits de l’opérande, sans avoir besoin de propager des retenues. Ainsi, la retenue sortante est connue en avance, sans passer par deux demi-additionneurs. Formellement, ils sont un cas particulier d'une optimisation appelée l'''anticipation de retenue''. Si nous verrons le cas général dans la section suivante, ce cas particulier est une très bonne introduction.
Pour créer un tel incrémenteur, le mieux est de partir de sa table de vérité.
{|class="wikitable"
|-
! Opérande !! Retenue entrante !! !! Retenue sortante !! Résultat
|-
| 00 || 0 || || 0 || 00
|-
| 01 || 0 || || 0 || 01
|-
| 10 || 0 || || 0 || 10
|-
| 11 || 0 || || 0 || 11
|-
| colspan="5" |
|-
| 00 || 1 || || 0 || 01
|-
| 01 || 1 || || 0 || 10
|-
| 10 || 1 || || 0 || 11
|-
| 11 || 1 || || 1 || 00
|}
Elle nous dit que la retenue sortante ne vaut 1 que dans un seul cas : les deux bits d'opérande valent 1, la retenue entrante vaut 1. Elle se calcule alors avec une porte ET à trois entrées. Pour les deux bits du résultat, les équations logiques ne donnent pas un résultat satisfaisant. La solution la plus simple est d'utiliser deux demi-additionneurs et de retirer les portes logiques superflues. Le résultat est alors un '''incrémenteur 2 bits ''carry skip''''', où la retenue sortante est calculée séparément du reste.
[[File:Incrémenteur carry skip 2 bits.png|centre|vignette|upright=2.5|Incrémenteur ''carry skip'' 2 bits]]
===Les incrémenteurs à anticipation de retenue===
L''''anticipation de retenue''' calcule la retenue d'une colonne sans attendre les retenues des colonnes précédentes. Dans le cas idéal, toutes les retenues sont calculées en parallèle, en même temps, et sont ensuite envoyées aux demi-additionneurs. Il s'agit d'une optimisation qui est utilisée pour l'incrémentation, l'addition et d'autres opérations similaires. Pour l'incrémentation, déterminer la retenue ne demande pas de calculs complexes, contrairement à l'addition.
[[File:Incrémenteur à anticipation de retenues, 8 bits.png|centre|vignette|upright=2.5|Incrémenteur à anticipation de retenues, 8 bits.]]
Pour le comprendre, on peut regarder comment la retenue sortant d'un demi-additionneur est formée. Elle est calculée en faisant un ET logique entre la retenue entrante et le bit d'opérande. Une retenue est donc un ET logique entre toutes les retenues précédentes. Un incrémenteur à anticipation de retenue utilise donc une porte ET à plusieurs entrées pour calculer une retenue.
[[File:Anticipation de retenue pour un bit du résultat, incrémenteur.png|centre|vignette|upright=2.5|Anticipation de retenue pour un bit du résultat, incrémenteur]]
En théorie, on peut utiliser une porte ET à plusieurs entrées pour chaque bit de l'opérande. Cependant, cela entrainera un cout en transistors très important. Pour éviter de gaspiller trop de portes logiques, une solution est de mélanger anticipation de retenues et propagation de retenue. Par exemple, pour un incrémenteur 32 bits, on peut découper l'opérande en 4 octets : on anticipe les retenues pour chaque octet, mais l'incrémentation de chaque octet se fait avec propagation de retenue et/ou ''carry skip''.
[[File:Incrémenteur à anticipation de retenues.png|centre|vignette|upright=3|Incrémenteur hybride utilisant partiellement l'anticipation de retenues.]]
Quelques processeurs utilisaient l'anticipation de retenues. Par exemple, le processeur Z-80 de Zilog utilisait un incrémenteur pour des nombres de 16 bits, ce qui demandait des performances assez élevées. Et cet incrémenteur utilisait à la fois anticipation de retenues et ''carry skip''. Il était découpé en quatre blocs avant anticipation de retenues entre eux : un bloc regroupant les 7 bits de poids faible, suivi par un bloc de 5 bits, lui-même suivi par un bloc de 3 bits, terminé par un dernier bit isolé. A l'intérieur de ces blocs, les bits sont regroupés en paires utilisant le ''carry skip''. Pour ceux qui veulent en savoir plus sur cet incrémenteur, voici un lien sur le sujet :
* [https://www.righto.com/2013/11/the-z-80s-16-bit-incrementdecrement.html The Z-80's 16-bit increment/decrement circuit reverse engineered].
==Les décrémenteurs==
Les circuits décrémenteurs font l'inverse d'un incrémenteur : au lieu d'ajouter 1 à un opérande, ils lui soustraient 1. La différence n'est pas énorme, ce qui fait que les circuits incrémenteurs et décrémenteurs se ressemblent beaucoup. Là où les incrémenteurs sont fabriqués à partir de demi-additionneurs, les décrémenteurs sont bâtis avec des demi-soustracteurs.
La soustraction se fait en binaire comme en décimal. On soustrait deux chiffres, puis on doit propager une éventuelle retenue sur la colonne suivante. La retenue apparait quand le chiffre soustrait est plus grand que l'autre chiffre. Elle est propagée sur la colonne suivante, où elle doit être soustraite du résultat. Pour le dire autrement, après avoir soustrait un chiffre, on doit de plus soustraire la retenue de la colonne précédente.
La décrémentation effectue le calcul suivant :
<math>a_7</math> <math>a_6</math> <math>a_5</math> <math>a_4</math> <math>a_3</math> <math>a_2</math> <math>a_1</math> <math>a_0</math>
- 0 0 0 0 0 0 0 1
La colonne la plus à droite soustrait un 1. Par contre, les colonnes suivantes soustraient un zéro. Soustraire un zéro ne change rien. Mais il ne faut pas oublier de soustraire une éventuellement retenue, propagée depuis la colonne précédente. En clair, un décrémenteur peut se construire si on sait soustraire deux bits. On soustrait un 1 sur la colonne la plus à droite, on soustrait une retenue sur les autres colonnes. Et pour soustraire deux bits, il faut utiliser un demi-soustracteur.
===Le demi-soustracteur===
Un '''demi-soustracteur''' soustrait deux bits. Pour comprendre comment soustraire deux bits, traitons les quatre cas possibles un par un.
Premièrement, soustraire zéro à un bit ne changera rien :
* 0 - 0 = 0 ;
* 1 - 0 = 1.
Si on soustrait 1 à un bit qui vaut 1, on obtient zéro.
* 1 - 1 = 0.
Maintenant, que se passe-t-il si on soustrait 1 à 0 ? Voici le résultat :
* 0 - 1 = 1 et une retenue propagée sur la colonne suivante, où elle est soustraite.
La table de soustraction est donc la suivante :
* 0 - 0 = 0 ;
* 0 - 1 = 1 et une retenue ;
* 1 - 0 = 1 ;
* 1 - 1 = 0.
La table de vérité est donc la suivante. Détail important : le demi-soustracteur fait ici la soustraction X - Y. En anglais, X est appelé le ''Minuend'' et Y est appelé le ''Subtrahend'', mais ces termes n'ont pas de traduction française. Aussi, je vais les utiliser tels quels dans ce qui suit.
{|class="wikitable"
|-
! ''Minuend'' !! ''Subtrahend'' !! !! Retenue sortante !! Bit de somme
|-
||0||0|| ||0||0
|-
||0||1|| ||1||1
|-
||1||0|| ||0||1
|-
||1||1|| ||0||0
|}
Si on construit la table de vérité, et qu'on construit le circuit associé, on tombe sur ce circuit.
[[File:Half Subtractor Vektor.svg|centre|vignette|Demi-soustracteur.]]
Vous remarquerez que le bit de somme se calcule de la même manière que pour l'addition, seul le calcul de la retenue change.
===Le décrémenteur à propagation de retenue===
Un décrémenteur à propagation de retenue se construit comme un incrémenteur, sauf qu'on remplace les demi-additionneurs par des demi-soustracteurs. En clair, on enchaine plusieurs demi-soustracteurs les uns à la suite des autres.
Il est possible d'utiliser l'anticipation de retenue, mais les circuits sont cependant différents de l'incrémenteur. Idem avec le ''carry skip'', et toute autre optimisation possible. La raison est que les retenues sont calculées différemment, ce qui fait que l’anticipation de retenue doit être adaptée.
==Les incrémenteurs-décrémenteurs==
Les circuits décrémenteurs ressemblent beaucoup aux incrémenteurs. Aussi, vous ne serez pas étonné qu'il soit possible de fusionner un incrémenteur et un décrémenteur en un seul circuit. Un tel circuit s'appelle un '''incrémenteur-décrémenteur'''. Un tel circuit a une interface similaire à celle d'un incrémenteur, avec une entrée sur laquelle présenter l'opérande, et une sortie sur laquelle récupérer le résultat. Il a aussi une entrée de retenue, sur laquelle envoyer un 1 (incrémentation) ou un 0 (opération NOP/identité). Et il a aussi une sortie de retenue, qui indique que le résultat du calcul a débordé.
Mais en plus de cela, il dispose d'une entrée de commande qui permet de choisir entre incrémentation et décrémentation.
Il existe plusieurs manières de créer un incrémenteur-décrémenteur, que nous allons voir si dessous. Nous n'allons pas voir le cas le plus évident, aussi ne vous étonnez pas si vous avez remarqué qu'un demi-additionneur et un demi-soustracteur se ressemblent beaucoup.
===L'incrémenteur-décrémenteur basé sur des demi-additionneurs===
Une première implémentation se base sur les propriétés du complément à deux, et plus précisément sur cette équation :
: <math>\overline{x} + 1 = \overline{x - 1}</math>
Les deux termes de cette équation sont simplement des définitions équivalentes du complément à deux d'un nombre entier. Celui-ci se calcule de deux manières : soit on incrémente son complément à 1, soit on prend le complément à 1 de son décrément. Et cela nous permet de déterminer comment obtenir une incrémentation à partir d'une décrémentation et inversement.
Que nous dit cette équation ? Simplement que l'on peut décrémenter un nombre si on a deux inverseurs commandables, et un circuit incrémenteur. La décrémentation se fait alors en deux étapes : on calcule son complément à deux, puis on inverse les bits du résultat précédent. Le calcul du complément à deux demande d'avoir un circuit inverseur, et un incrémenteur.
===L'incrémenteur-décrémenteur basé sur des demi-additionneurs===
Vous l'aurez sans doute remarqué, mais le demi-soustracteur ressemble beaucoup au demi-additionneur. La seule différence est une porte NON ajoutée au bon endroit dans le demi-soustracteur. La porte NON inverse le bit X, sachant que le demi-soustracteur fait la soustraction X - Y. De plus, l'opérande inversée est utilisée seulement pour le calcul des retenues, le bit de somme est calculé normalement.
{|
|[[File:Half-adder.svg|vignette|Demi-additionneur]]
|[[File:Half Subtractor Vektor.svg|vignette|upright=1.3|Demi-soustracteur]]
|}
La conséquence est qu'il est possible de fusionner un demi-additionneur avec un demi-soustracteur. L'idée est de remplacer la porte NON du demi-soustracteur par un inverseur commandable. Si on envoie un zéro sur l'entrée de commande, l'inverseur commandable se comportera comme une porte OUI et le circuit sera alors identique à un demi-additionneur. Si on envoie un 1 sur l'entrée de commande, l'inverseur commandable se comportera comme une porte NON. le circuit sera alors un demi-soustracteur. En remplaçant les demi-additionneurs (ou demi-soustracteurs) par de tels circuits, on peut obtenir un circuit faisant à la fois incrémenteur et décrémenteur.
Un défaut de cette approche est que les optimisations vues plus haut pour les demi-additionneurs ne fonctionne tout simplement plus. On est obligé d'utiliser une porte XOR et une porte ET séparées, pas question d'utiliser les demi-additionneurs optimisés vu au début du chapitre.
<noinclude>
{{NavChapitre | book=Fonctionnement d'un ordinateur
| prev=Les circuits de sélection
| prevText=Les circuits de sélection
| next=Les bascules : des mémoires de 1 bit
| nextText=Les bascules : des mémoires de 1 bit
}}
</noinclude>
dfm83hvthyfuvvjfn9pzjb19sip3lcq
768797
768796
2026-06-27T15:51:57Z
Mewtow
31375
/* Les incrémenteurs à anticipation de retenue */
768797
wikitext
text/x-wiki
Le circuit '''incrémenteur''' incrémente un nombre entier. Plus précisément, il prend en entrée un nombre entier, et fournit en entier le nombre suivant, il lui ajoute 1. Une telle opération peut paraitre triviale et peu intéressante. Cependant, elle est très utilisée et est absolument cruciale pour créer des compteurs, des circuits capables de compter ou décompter, qui auront droit à un chapitre dédié. De plus, au-delà de leur utilisation dans les compteurs, les incrémenteurs étaient très utilisés sur les premiers processeurs 8 bits, comme le Z-80, le 6502, les premiers processeurs x86 comme le 8008, le 8086, le 8085, et bien d'autres.
Il existe aussi des ''circuits décrémenteurs'', qui décrémentent un opérande, ainsi que des ''circuits incrémenteur-décrémenteur'', qui peuvent incrémenter ou décrémenter, selon comment on les configure. Je ne vais pas détailler ces circuits plus que ça, car de tels circuits sont assez rares, comparé à un circuit incrémenteur simple.
==Le demi-additionneur==
Le circuit incrémenteur effectue l'opération suivante :
<math>a_7</math> <math>a_6</math> <math>a_5</math> <math>a_4</math> <math>a_3</math> <math>a_2</math> <math>a_1</math> <math>a_0</math>
+ 0 0 0 0 0 0 0 1
Un incrémenteur basique doit donc faire une addition pour chaque colonne, et précisément une addition de deux bits. Il se trouve que la table d'addition est très simple en binaire. Jugez plutôt :
* 0 + 0 = 0, retenue = 0 ;
* 0 + 1 = 1, retenue = 0 ;
* 1 + 0 = 1, retenue = 0 ;
* 1 + 1 = 0, retenue = 1.
Un circuit capable d'additionner deux bits est appelé un '''demi-additionneur'''. Il dispose d'une sortie S pour la somme, et C pour la retenue (''carry'' an anglais).
===Le demi-additionneur classique===
Un demi-additionneur est très simple à construire avec les techniques vues dans les premiers chapitres. Voici sa table de vérité :
{|class="wikitable"
|-
! Retenue entrante !! Opérande 1 !! !! Retenue sortante !! Bit de somme
|-
||0||0|| ||0||0
|-
||0||1|| ||0||1
|-
||1||0|| ||0||1
|-
||1||1|| ||1||0
|}
On voit immédiatement que la colonne des retenues donne une porte ET, alors que celle du bit de somme est calculé par un XOR.
{| class="flexible"
|[[File:1-bit half-adder.svg|class=transparent|centre|Demi-addtionneur.]]
|[[File:Half-adder.svg|centre|class=transparent|Circuit d'un demi-addtionneur.]]
|}
Il existe beaucoup d'autres implémentations alternatives, qui utilisent moins de portes logiques, calculent la retenue plus rapidement, consomment moins d'énergie, et autres. Et il est intéressant d'étudier quelques alternatives, surtout qu'elles serviront plus bas, quand on étudiera le circuit incrémenteur du 8085.
===Le demi-additionneur basé sur une modification de la retenue===
Une source d'amélioration est liée à la porte XOR. En pratique, une porte XOR est composée en combinant plusieurs portes logiques ET/OU/NOR/NAND ensemble. Et il se trouve qu'il y a des redondances entre celles-ci et les portes utilisées pour calculer la retenue. Par exemple, rappelons qu'une porte XOR peut être construite avec une porte ET et deux portes NOR, comme illustré ci-dessous. Et il se trouve que la porte ET interne calcule la retenue sortante. En supprimant cette redondance, on économise quelques portes logiques. De plus, on se retrouve avec un demi-additionneur qui calcule le bit de somme à partir de la retenue sortante.
[[File:Demi-additionneur avec redondances éliminées.png|centre|vignette|upright=2|Demi-additionneur avec redondances éliminées]]
Le circuit précédent a une interprétation logique. Si vous regardez la table de vérité, vous remarquerez que la somme de deux bits est égale à l'inverse de la retenue sortante, sauf dans le cas où les deux bits additionnés valent zéro. Et le circuit précédent est basé là-dessus.
{|class="wikitable"
|-
! Retenue entrante !! Opérande 1 !! !! Retenue sortante !! Bit de somme
|- class="f_vert"
||0||0|| ||0||0
|- class="f_rouge"
||0||1|| ||0||1
|- class="f_rouge"
||1||0|| ||0||1
|- class="f_rouge"
||1||1|| ||1||0
|}
L'idée est de calculer l'inverse du bit de somme, avant de l'inverser avec une porte NON. L'inverse du bit de somme vaut 1, soit quand la retenue est à 1, soit quand les deux bits additionnés sont à 0. La porte ET calcule la retenue sortante, la première porte NOR détecte sur les deux bits d'entrée valent zéro. Une porte OU combine les deux résultat pour obtenir l'inverse du bit d'entrée, puis une porte NOn inverse le tout pour obtenir le bit de somme adéquat. Les deux portes sont fusionnées : c'est la seconde porte NOR.
[[File:Full adder HA + MUX.png|centre|vignette|upright=2.5|Demi-additionneur basé sur une inversion de la retenue sortante.]]
===Les implémentations alternatives===
Une implémentation alternative, qui n'utilise que des portes NOR, est la suivante :
[[File:Demi-additionneur fait avec des portes NOR et NAND.png|centre|vignette|upright=2|Demi-additionneur fait avec des portes NOR et NAND.]]
Une implémentation alternative utilise des portes NAND. Pour rappel, il est possible de créer une porte XOR avec 4 portes NAND. La première d'entre elle fait un NAND entre les deux bits d'entrée, ce qui fait qu'elle calcule l'inverse de la retenue sortante. Le tout est illustré ci-dessous. En théorie, on devrait utiliser une porte NON pour récupérer la retenue correcte. Mais nous verrons plus bas que ce n'est pas une obligation. Nous verrons plus bas un exemple où c'est l'inverse de la retenue qui est utilisée dans les calculs, pour rendre les calculs plus rapides.
[[File:Demi-additionneur fait avec des portes NAND.png|centre|vignette|upright=2|Demi-additionneur fait avec des portes NAND]]
==Le circuit incrémenteur==
Maintenant que l'on sait comment additionner deux bits, reprenons l'opération d'incrémentation :
<math>a_7</math> <math>a_6</math> <math>a_5</math> <math>a_4</math> <math>a_3</math> <math>a_2</math> <math>a_1</math> <math>a_0</math>
+ 0 0 0 0 0 0 0 1
------------------------------
Sur la colonne la plus à droite, il doit ajouter un au bit de poids faible. Pour les colonnes suivantes, il faut additionner le bit de l'opérande avec la retenue de la colonne précédente. En clair, on n'additionne que deux bits à chaque colonne : un 1 sur celle tout à droite, la retenue de la colonne précédente pour les autres. Et cela nous donne une idée de comment faire pour créer le circuit incrémenteur.
===L'incrémenteur à propagation de retenue===
Il suffit d'utiliser un demi-additionneur par colonne, et de les enchainer les uns à la suite des autres. Chaque demi-additionneur additionne le bit de l'opérande avec la retenue fournie par le demi-additionneur précédent. Le seul qui fait exception est celui pour la colonne de poids faible. Pour celui-là, il doit ajouter 1 au bit de poids faible. Le résultat est appelé un '''incrémenteur à propagation de retenue'''. Il est constitué de demi-additionneurs enchaînés les uns à la suite des autres, du bit de poids faible vers le bit de poids fort.
[[File:Circuit incrémenteur.png|centre|vignette|upright=3|Circuit incrémenteur.]]
Maintenant, regardons le demi-additionneur le plus à droite, celui pour le bit de poids faible. Son entrée de retenue entrante est mise à 1 pour faire l'incrémentation. Quelques incrémenteurs permettent de configurer cette entrée de retenue à 0 ou à 1, ce qui effectue : soit une opération identité (l'opérande est recopié sur la sortie), soit une incrémentation. Un tel circuit est nommé un '''incrémenteur commandable'''. Nous aurons à utiliser une fois ou deux de tels incrémenteurs commandables dans la suite du cours.
L'incrémenteur à propagation de retenue est le plus simple et le plus économe en portes logiques. Mais de tels incrémenteurs sont rarement utilisés. À la place, on leur préfère des incrémenteurs plus rapides, mais qui utilisent plus de portes logiques. De tels incrémenteurs accélèrent le calcul des retenues. En effet, la rapidité d'une incrémentation est limitée par la propagation de la retenue : les retenues commencent à être calculées au bit de poids fort et on doit les calculer une par une, jusqu’à atteindre le bit de poids fort. Et cette "propagation des retenues" prend du temps, d'autant plus de temps que l'opérande est longue. Il y a deux optimisations principales, appelées le ''carry skip'' et l'anticipation de retenue, que nous allons décrire ci-dessous.
===Les incrémenteurs optimisés===
L'optimisation que nous allns voir effectue l'incrémentation, non pas bit par bit, mais par paquets de deux bits. Le résultat est que l'incrémentation est deux fois plus rapide, ou presque. Le circuit incrémenteur est donc composé en enchainant non pas des demi-additionneurs, mais des '''incrémenteurs 2 bits''' qui incrémentent un opérande de deux bits.
[[File:Circuit incrémenteur optimisé, faisant l'incrémentation par paquet de deux bits.png|centre|vignette|upright=2.5|Circuit incrémenteur optimisé, faisant l'incrémentation par paquet de deux bits]]
Reste à concevoir l'incrémenteur 2 bits. Une première méthode part de deux demi-additionneurs et effectue quelques simplifications au niveau des transistors et des portes logiques. Par exemple, l'Intel 8085 a utilisé un circuit optimisé, fabriqué avec des portes NAND et NOR. La raison est qu'avec les technologies de transistors CMOS actuelles, les portes NAND et NOR utilisent moins de transistors que les portes ET et OU. Et cette possibilité a été utilisée pour effectuer des simplifications assez mineures, mais toujours bonnes à prendre. Mais nous ne pouvons pas en parler en détail ici, car nous n'avons pas encore parlé en détail des transistors. Cependant, un exemple bien précis nous est accesible : celui de l’incrémenteur du processeur 8085 d'Intel.
Le processeur 8085 d'Intel est un processeur 8 bits très ancien. Il contenait un incrémenteur 16 bits, qui était utilisé pour calculer des adresses mémoire. Et cet incrémenteur était un incrémenteur à propagation de retenue optimisé. Il utilisait lui aussi des incrémenteurs 2 bits un peu modifiés.
L'idée était que les retenues sortant des colonnes paires étaient inversées, les colonnes impaires faisaient les calculs à partir de cette retenue inversée. Les demi-additionneurs des colonnes impaires n'étaient donc pas les mêmes que ceux des colonnes paires. Leurs tables de vérité sont différentes, leurs circuits aussi, et aucun des deux ne ressemble à un demi-additionneur normal. En réalité, ils sont fabriqués à partir des implémentations vues plus haut, qui utilisent uniquement des portes NAND ou uniquement des portes NOR.
Pour comprendre l'intérêt de faire ainsi, nous devons préciser une chose importante : avec les technologies CMOS utilisées pour les processeurs depuis les années 70, les portes logiques les plus simples sont les portes NON, NOR et NAND. Les portes ET/OU sont fabriquées en combinant des portes NOR/NAND avec une porte NON, ce qui prend plus de transistors.
Un demi-additionneur est donc fabriqué comme illustré ci-dessous, en logique CMOS. Seules les portes pour le calcul de la retenue sont indiquées, la porte XOR pour le bit de somme est fabriquée à partir de plusieurs portes logiques.
[[File:Demi-additionneur en CMOS.png|centre|vignette|upright=2|Demi-additionneur en CMOS]]
Si on enchaine deux demi-additionneurs, cela donne ceci :
[[File:Brique de base de l'incrémenteur du 8085.png|centre|vignette|upright=2|Brique de base de l'incrémenteur du 8085]]
La retenue doit donc traverser quatre portes logiques, dont deux portes NON. En inversant la retenue, les portes en jaune dans le schéma précédent se simplifient. Il suffit d'utiliser la loi de de Morgan pour trouver un circuit équivalent aux portes jaunes, qui n'est autre qu'une porte NOR couplée à une porte NON. Un autre moyen d'obtenir le même résultat est de prendre un demi-additionneur fabriqué avec uniquement des portes NOR, et de retirer la porte NON adéquate. Il faut ensuite corriger l'entrée de retenue de la porte XOR, pour qu'elle fasse l'addition avec la retenue correctement. Le résultat est illustré ci-dessous.
[[File:Incrémenteur 2 bits de l'incrémenteur du 8085.png|centre|vignette|upright=2|Incrémenteur 2 bits de l'incrémenteur du 8085]]
Le circuit utilise le même nombre de portes logiques, il n'économise pas de transistors. Par contre, la retenue n'a plus qu'à traverser deux portes logiques, au lieu de quatre. La propagation de la retenue est donc plus rapide, l'incrémentation est donc plus rapide.
===Les incrémenteurs 2 bits à calcul séparé de la retenue===
Il existe une autre méthode qui permet de créer des incrémenteurs 2 bits plus rapides que les précédents, mais qui utilisent plus de portes logiques. Ces incrémenteurs 2 bits calculent séparément les deux bits du résultat et la retenue sortante. La retenue sortante est calculée à partir des bits de l’opérande, sans avoir besoin de propager des retenues. Ainsi, la retenue sortante est connue en avance, sans passer par deux demi-additionneurs. Formellement, ils sont un cas particulier d'une optimisation appelée l'''anticipation de retenue''. Si nous verrons le cas général dans la section suivante, ce cas particulier est une très bonne introduction.
Pour créer un tel incrémenteur, le mieux est de partir de sa table de vérité.
{|class="wikitable"
|-
! Opérande !! Retenue entrante !! !! Retenue sortante !! Résultat
|-
| 00 || 0 || || 0 || 00
|-
| 01 || 0 || || 0 || 01
|-
| 10 || 0 || || 0 || 10
|-
| 11 || 0 || || 0 || 11
|-
| colspan="5" |
|-
| 00 || 1 || || 0 || 01
|-
| 01 || 1 || || 0 || 10
|-
| 10 || 1 || || 0 || 11
|-
| 11 || 1 || || 1 || 00
|}
Elle nous dit que la retenue sortante ne vaut 1 que dans un seul cas : les deux bits d'opérande valent 1, la retenue entrante vaut 1. Elle se calcule alors avec une porte ET à trois entrées. Pour les deux bits du résultat, les équations logiques ne donnent pas un résultat satisfaisant. La solution la plus simple est d'utiliser deux demi-additionneurs et de retirer les portes logiques superflues. Le résultat est alors un '''incrémenteur 2 bits ''carry skip''''', où la retenue sortante est calculée séparément du reste.
[[File:Incrémenteur carry skip 2 bits.png|centre|vignette|upright=2.5|Incrémenteur ''carry skip'' 2 bits]]
===Les incrémenteurs à anticipation de retenue===
L''''anticipation de retenue''' calcule la retenue d'une colonne sans attendre les retenues des colonnes précédentes. Dans le cas idéal, toutes les retenues sont calculées en parallèle, en même temps, et sont ensuite envoyées aux demi-additionneurs. Il s'agit d'une optimisation qui est utilisée pour l'incrémentation, l'addition et d'autres opérations similaires. Pour l'incrémentation, déterminer la retenue ne demande pas de calculs complexes, contrairement à l'addition.
[[File:Incrémenteur à anticipation de retenues, 8 bits.png|centre|vignette|upright=2.5|Incrémenteur à anticipation de retenues, 8 bits.]]
Pour le comprendre, on peut regarder comment la retenue sortant d'un demi-additionneur est formée. Elle est calculée en faisant un ET logique entre la retenue entrante et le bit d'opérande. Une retenue est donc un ET logique entre toutes les retenues précédentes. Un incrémenteur à anticipation de retenue utilise donc une porte ET à plusieurs entrées pour calculer une retenue.
[[File:Anticipation de retenue pour un bit du résultat, incrémenteur.png|centre|vignette|upright=2.5|Anticipation de retenue pour un bit du résultat, incrémenteur]]
En théorie, on peut utiliser une porte ET à plusieurs entrées pour chaque bit de l'opérande. Cependant, cela entrainera un cout en transistors très important. Pour éviter de gaspiller trop de portes logiques, une solution est de mélanger anticipation de retenues et propagation de retenue. Par exemple, pour un incrémenteur 32 bits, on peut découper l'opérande en 4 octets : on anticipe les retenues pour chaque octet, mais l'incrémentation de chaque octet se fait avec propagation de retenue et/ou ''carry skip''. Plus haut, nous avons vu un cas particulier où les retenues étaient anticipées par blocs de deux bits.
[[File:Incrémenteur à anticipation de retenues.png|centre|vignette|upright=3|Incrémenteur hybride utilisant partiellement l'anticipation de retenues.]]
Quelques processeurs utilisaient l'anticipation de retenues. Par exemple, le processeur Z-80 de Zilog utilisait un incrémenteur pour des nombres de 16 bits, ce qui demandait des performances assez élevées. Et cet incrémenteur utilisait à la fois anticipation de retenues et ''carry skip''. Il était découpé en quatre blocs avant anticipation de retenues entre eux : un bloc regroupant les 7 bits de poids faible, suivi par un bloc de 5 bits, lui-même suivi par un bloc de 3 bits, terminé par un dernier bit isolé. A l'intérieur de ces blocs, les bits sont regroupés en paires utilisant le ''carry skip''. Pour ceux qui veulent en savoir plus sur cet incrémenteur, voici un lien sur le sujet :
* [https://www.righto.com/2013/11/the-z-80s-16-bit-incrementdecrement.html The Z-80's 16-bit increment/decrement circuit reverse engineered].
==Les décrémenteurs==
Les circuits décrémenteurs font l'inverse d'un incrémenteur : au lieu d'ajouter 1 à un opérande, ils lui soustraient 1. La différence n'est pas énorme, ce qui fait que les circuits incrémenteurs et décrémenteurs se ressemblent beaucoup. Là où les incrémenteurs sont fabriqués à partir de demi-additionneurs, les décrémenteurs sont bâtis avec des demi-soustracteurs.
La soustraction se fait en binaire comme en décimal. On soustrait deux chiffres, puis on doit propager une éventuelle retenue sur la colonne suivante. La retenue apparait quand le chiffre soustrait est plus grand que l'autre chiffre. Elle est propagée sur la colonne suivante, où elle doit être soustraite du résultat. Pour le dire autrement, après avoir soustrait un chiffre, on doit de plus soustraire la retenue de la colonne précédente.
La décrémentation effectue le calcul suivant :
<math>a_7</math> <math>a_6</math> <math>a_5</math> <math>a_4</math> <math>a_3</math> <math>a_2</math> <math>a_1</math> <math>a_0</math>
- 0 0 0 0 0 0 0 1
La colonne la plus à droite soustrait un 1. Par contre, les colonnes suivantes soustraient un zéro. Soustraire un zéro ne change rien. Mais il ne faut pas oublier de soustraire une éventuellement retenue, propagée depuis la colonne précédente. En clair, un décrémenteur peut se construire si on sait soustraire deux bits. On soustrait un 1 sur la colonne la plus à droite, on soustrait une retenue sur les autres colonnes. Et pour soustraire deux bits, il faut utiliser un demi-soustracteur.
===Le demi-soustracteur===
Un '''demi-soustracteur''' soustrait deux bits. Pour comprendre comment soustraire deux bits, traitons les quatre cas possibles un par un.
Premièrement, soustraire zéro à un bit ne changera rien :
* 0 - 0 = 0 ;
* 1 - 0 = 1.
Si on soustrait 1 à un bit qui vaut 1, on obtient zéro.
* 1 - 1 = 0.
Maintenant, que se passe-t-il si on soustrait 1 à 0 ? Voici le résultat :
* 0 - 1 = 1 et une retenue propagée sur la colonne suivante, où elle est soustraite.
La table de soustraction est donc la suivante :
* 0 - 0 = 0 ;
* 0 - 1 = 1 et une retenue ;
* 1 - 0 = 1 ;
* 1 - 1 = 0.
La table de vérité est donc la suivante. Détail important : le demi-soustracteur fait ici la soustraction X - Y. En anglais, X est appelé le ''Minuend'' et Y est appelé le ''Subtrahend'', mais ces termes n'ont pas de traduction française. Aussi, je vais les utiliser tels quels dans ce qui suit.
{|class="wikitable"
|-
! ''Minuend'' !! ''Subtrahend'' !! !! Retenue sortante !! Bit de somme
|-
||0||0|| ||0||0
|-
||0||1|| ||1||1
|-
||1||0|| ||0||1
|-
||1||1|| ||0||0
|}
Si on construit la table de vérité, et qu'on construit le circuit associé, on tombe sur ce circuit.
[[File:Half Subtractor Vektor.svg|centre|vignette|Demi-soustracteur.]]
Vous remarquerez que le bit de somme se calcule de la même manière que pour l'addition, seul le calcul de la retenue change.
===Le décrémenteur à propagation de retenue===
Un décrémenteur à propagation de retenue se construit comme un incrémenteur, sauf qu'on remplace les demi-additionneurs par des demi-soustracteurs. En clair, on enchaine plusieurs demi-soustracteurs les uns à la suite des autres.
Il est possible d'utiliser l'anticipation de retenue, mais les circuits sont cependant différents de l'incrémenteur. Idem avec le ''carry skip'', et toute autre optimisation possible. La raison est que les retenues sont calculées différemment, ce qui fait que l’anticipation de retenue doit être adaptée.
==Les incrémenteurs-décrémenteurs==
Les circuits décrémenteurs ressemblent beaucoup aux incrémenteurs. Aussi, vous ne serez pas étonné qu'il soit possible de fusionner un incrémenteur et un décrémenteur en un seul circuit. Un tel circuit s'appelle un '''incrémenteur-décrémenteur'''. Un tel circuit a une interface similaire à celle d'un incrémenteur, avec une entrée sur laquelle présenter l'opérande, et une sortie sur laquelle récupérer le résultat. Il a aussi une entrée de retenue, sur laquelle envoyer un 1 (incrémentation) ou un 0 (opération NOP/identité). Et il a aussi une sortie de retenue, qui indique que le résultat du calcul a débordé.
Mais en plus de cela, il dispose d'une entrée de commande qui permet de choisir entre incrémentation et décrémentation.
Il existe plusieurs manières de créer un incrémenteur-décrémenteur, que nous allons voir si dessous. Nous n'allons pas voir le cas le plus évident, aussi ne vous étonnez pas si vous avez remarqué qu'un demi-additionneur et un demi-soustracteur se ressemblent beaucoup.
===L'incrémenteur-décrémenteur basé sur des demi-additionneurs===
Une première implémentation se base sur les propriétés du complément à deux, et plus précisément sur cette équation :
: <math>\overline{x} + 1 = \overline{x - 1}</math>
Les deux termes de cette équation sont simplement des définitions équivalentes du complément à deux d'un nombre entier. Celui-ci se calcule de deux manières : soit on incrémente son complément à 1, soit on prend le complément à 1 de son décrément. Et cela nous permet de déterminer comment obtenir une incrémentation à partir d'une décrémentation et inversement.
Que nous dit cette équation ? Simplement que l'on peut décrémenter un nombre si on a deux inverseurs commandables, et un circuit incrémenteur. La décrémentation se fait alors en deux étapes : on calcule son complément à deux, puis on inverse les bits du résultat précédent. Le calcul du complément à deux demande d'avoir un circuit inverseur, et un incrémenteur.
===L'incrémenteur-décrémenteur basé sur des demi-additionneurs===
Vous l'aurez sans doute remarqué, mais le demi-soustracteur ressemble beaucoup au demi-additionneur. La seule différence est une porte NON ajoutée au bon endroit dans le demi-soustracteur. La porte NON inverse le bit X, sachant que le demi-soustracteur fait la soustraction X - Y. De plus, l'opérande inversée est utilisée seulement pour le calcul des retenues, le bit de somme est calculé normalement.
{|
|[[File:Half-adder.svg|vignette|Demi-additionneur]]
|[[File:Half Subtractor Vektor.svg|vignette|upright=1.3|Demi-soustracteur]]
|}
La conséquence est qu'il est possible de fusionner un demi-additionneur avec un demi-soustracteur. L'idée est de remplacer la porte NON du demi-soustracteur par un inverseur commandable. Si on envoie un zéro sur l'entrée de commande, l'inverseur commandable se comportera comme une porte OUI et le circuit sera alors identique à un demi-additionneur. Si on envoie un 1 sur l'entrée de commande, l'inverseur commandable se comportera comme une porte NON. le circuit sera alors un demi-soustracteur. En remplaçant les demi-additionneurs (ou demi-soustracteurs) par de tels circuits, on peut obtenir un circuit faisant à la fois incrémenteur et décrémenteur.
Un défaut de cette approche est que les optimisations vues plus haut pour les demi-additionneurs ne fonctionne tout simplement plus. On est obligé d'utiliser une porte XOR et une porte ET séparées, pas question d'utiliser les demi-additionneurs optimisés vu au début du chapitre.
<noinclude>
{{NavChapitre | book=Fonctionnement d'un ordinateur
| prev=Les circuits de sélection
| prevText=Les circuits de sélection
| next=Les bascules : des mémoires de 1 bit
| nextText=Les bascules : des mémoires de 1 bit
}}
</noinclude>
ef81w76sl5b25p441gl856axspeib8q
768798
768797
2026-06-27T16:00:07Z
Mewtow
31375
/* Le décrémenteur à propagation de retenue */
768798
wikitext
text/x-wiki
Le circuit '''incrémenteur''' incrémente un nombre entier. Plus précisément, il prend en entrée un nombre entier, et fournit en entier le nombre suivant, il lui ajoute 1. Une telle opération peut paraitre triviale et peu intéressante. Cependant, elle est très utilisée et est absolument cruciale pour créer des compteurs, des circuits capables de compter ou décompter, qui auront droit à un chapitre dédié. De plus, au-delà de leur utilisation dans les compteurs, les incrémenteurs étaient très utilisés sur les premiers processeurs 8 bits, comme le Z-80, le 6502, les premiers processeurs x86 comme le 8008, le 8086, le 8085, et bien d'autres.
Il existe aussi des ''circuits décrémenteurs'', qui décrémentent un opérande, ainsi que des ''circuits incrémenteur-décrémenteur'', qui peuvent incrémenter ou décrémenter, selon comment on les configure. Je ne vais pas détailler ces circuits plus que ça, car de tels circuits sont assez rares, comparé à un circuit incrémenteur simple.
==Le demi-additionneur==
Le circuit incrémenteur effectue l'opération suivante :
<math>a_7</math> <math>a_6</math> <math>a_5</math> <math>a_4</math> <math>a_3</math> <math>a_2</math> <math>a_1</math> <math>a_0</math>
+ 0 0 0 0 0 0 0 1
Un incrémenteur basique doit donc faire une addition pour chaque colonne, et précisément une addition de deux bits. Il se trouve que la table d'addition est très simple en binaire. Jugez plutôt :
* 0 + 0 = 0, retenue = 0 ;
* 0 + 1 = 1, retenue = 0 ;
* 1 + 0 = 1, retenue = 0 ;
* 1 + 1 = 0, retenue = 1.
Un circuit capable d'additionner deux bits est appelé un '''demi-additionneur'''. Il dispose d'une sortie S pour la somme, et C pour la retenue (''carry'' an anglais).
===Le demi-additionneur classique===
Un demi-additionneur est très simple à construire avec les techniques vues dans les premiers chapitres. Voici sa table de vérité :
{|class="wikitable"
|-
! Retenue entrante !! Opérande 1 !! !! Retenue sortante !! Bit de somme
|-
||0||0|| ||0||0
|-
||0||1|| ||0||1
|-
||1||0|| ||0||1
|-
||1||1|| ||1||0
|}
On voit immédiatement que la colonne des retenues donne une porte ET, alors que celle du bit de somme est calculé par un XOR.
{| class="flexible"
|[[File:1-bit half-adder.svg|class=transparent|centre|Demi-addtionneur.]]
|[[File:Half-adder.svg|centre|class=transparent|Circuit d'un demi-addtionneur.]]
|}
Il existe beaucoup d'autres implémentations alternatives, qui utilisent moins de portes logiques, calculent la retenue plus rapidement, consomment moins d'énergie, et autres. Et il est intéressant d'étudier quelques alternatives, surtout qu'elles serviront plus bas, quand on étudiera le circuit incrémenteur du 8085.
===Le demi-additionneur basé sur une modification de la retenue===
Une source d'amélioration est liée à la porte XOR. En pratique, une porte XOR est composée en combinant plusieurs portes logiques ET/OU/NOR/NAND ensemble. Et il se trouve qu'il y a des redondances entre celles-ci et les portes utilisées pour calculer la retenue. Par exemple, rappelons qu'une porte XOR peut être construite avec une porte ET et deux portes NOR, comme illustré ci-dessous. Et il se trouve que la porte ET interne calcule la retenue sortante. En supprimant cette redondance, on économise quelques portes logiques. De plus, on se retrouve avec un demi-additionneur qui calcule le bit de somme à partir de la retenue sortante.
[[File:Demi-additionneur avec redondances éliminées.png|centre|vignette|upright=2|Demi-additionneur avec redondances éliminées]]
Le circuit précédent a une interprétation logique. Si vous regardez la table de vérité, vous remarquerez que la somme de deux bits est égale à l'inverse de la retenue sortante, sauf dans le cas où les deux bits additionnés valent zéro. Et le circuit précédent est basé là-dessus.
{|class="wikitable"
|-
! Retenue entrante !! Opérande 1 !! !! Retenue sortante !! Bit de somme
|- class="f_vert"
||0||0|| ||0||0
|- class="f_rouge"
||0||1|| ||0||1
|- class="f_rouge"
||1||0|| ||0||1
|- class="f_rouge"
||1||1|| ||1||0
|}
L'idée est de calculer l'inverse du bit de somme, avant de l'inverser avec une porte NON. L'inverse du bit de somme vaut 1, soit quand la retenue est à 1, soit quand les deux bits additionnés sont à 0. La porte ET calcule la retenue sortante, la première porte NOR détecte sur les deux bits d'entrée valent zéro. Une porte OU combine les deux résultat pour obtenir l'inverse du bit d'entrée, puis une porte NOn inverse le tout pour obtenir le bit de somme adéquat. Les deux portes sont fusionnées : c'est la seconde porte NOR.
[[File:Full adder HA + MUX.png|centre|vignette|upright=2.5|Demi-additionneur basé sur une inversion de la retenue sortante.]]
===Les implémentations alternatives===
Une implémentation alternative, qui n'utilise que des portes NOR, est la suivante :
[[File:Demi-additionneur fait avec des portes NOR et NAND.png|centre|vignette|upright=2|Demi-additionneur fait avec des portes NOR et NAND.]]
Une implémentation alternative utilise des portes NAND. Pour rappel, il est possible de créer une porte XOR avec 4 portes NAND. La première d'entre elle fait un NAND entre les deux bits d'entrée, ce qui fait qu'elle calcule l'inverse de la retenue sortante. Le tout est illustré ci-dessous. En théorie, on devrait utiliser une porte NON pour récupérer la retenue correcte. Mais nous verrons plus bas que ce n'est pas une obligation. Nous verrons plus bas un exemple où c'est l'inverse de la retenue qui est utilisée dans les calculs, pour rendre les calculs plus rapides.
[[File:Demi-additionneur fait avec des portes NAND.png|centre|vignette|upright=2|Demi-additionneur fait avec des portes NAND]]
==Le circuit incrémenteur==
Maintenant que l'on sait comment additionner deux bits, reprenons l'opération d'incrémentation :
<math>a_7</math> <math>a_6</math> <math>a_5</math> <math>a_4</math> <math>a_3</math> <math>a_2</math> <math>a_1</math> <math>a_0</math>
+ 0 0 0 0 0 0 0 1
------------------------------
Sur la colonne la plus à droite, il doit ajouter un au bit de poids faible. Pour les colonnes suivantes, il faut additionner le bit de l'opérande avec la retenue de la colonne précédente. En clair, on n'additionne que deux bits à chaque colonne : un 1 sur celle tout à droite, la retenue de la colonne précédente pour les autres. Et cela nous donne une idée de comment faire pour créer le circuit incrémenteur.
===L'incrémenteur à propagation de retenue===
Il suffit d'utiliser un demi-additionneur par colonne, et de les enchainer les uns à la suite des autres. Chaque demi-additionneur additionne le bit de l'opérande avec la retenue fournie par le demi-additionneur précédent. Le seul qui fait exception est celui pour la colonne de poids faible. Pour celui-là, il doit ajouter 1 au bit de poids faible. Le résultat est appelé un '''incrémenteur à propagation de retenue'''. Il est constitué de demi-additionneurs enchaînés les uns à la suite des autres, du bit de poids faible vers le bit de poids fort.
[[File:Circuit incrémenteur.png|centre|vignette|upright=3|Circuit incrémenteur.]]
Maintenant, regardons le demi-additionneur le plus à droite, celui pour le bit de poids faible. Son entrée de retenue entrante est mise à 1 pour faire l'incrémentation. Quelques incrémenteurs permettent de configurer cette entrée de retenue à 0 ou à 1, ce qui effectue : soit une opération identité (l'opérande est recopié sur la sortie), soit une incrémentation. Un tel circuit est nommé un '''incrémenteur commandable'''. Nous aurons à utiliser une fois ou deux de tels incrémenteurs commandables dans la suite du cours.
L'incrémenteur à propagation de retenue est le plus simple et le plus économe en portes logiques. Mais de tels incrémenteurs sont rarement utilisés. À la place, on leur préfère des incrémenteurs plus rapides, mais qui utilisent plus de portes logiques. De tels incrémenteurs accélèrent le calcul des retenues. En effet, la rapidité d'une incrémentation est limitée par la propagation de la retenue : les retenues commencent à être calculées au bit de poids fort et on doit les calculer une par une, jusqu’à atteindre le bit de poids fort. Et cette "propagation des retenues" prend du temps, d'autant plus de temps que l'opérande est longue. Il y a deux optimisations principales, appelées le ''carry skip'' et l'anticipation de retenue, que nous allons décrire ci-dessous.
===Les incrémenteurs optimisés===
L'optimisation que nous allns voir effectue l'incrémentation, non pas bit par bit, mais par paquets de deux bits. Le résultat est que l'incrémentation est deux fois plus rapide, ou presque. Le circuit incrémenteur est donc composé en enchainant non pas des demi-additionneurs, mais des '''incrémenteurs 2 bits''' qui incrémentent un opérande de deux bits.
[[File:Circuit incrémenteur optimisé, faisant l'incrémentation par paquet de deux bits.png|centre|vignette|upright=2.5|Circuit incrémenteur optimisé, faisant l'incrémentation par paquet de deux bits]]
Reste à concevoir l'incrémenteur 2 bits. Une première méthode part de deux demi-additionneurs et effectue quelques simplifications au niveau des transistors et des portes logiques. Par exemple, l'Intel 8085 a utilisé un circuit optimisé, fabriqué avec des portes NAND et NOR. La raison est qu'avec les technologies de transistors CMOS actuelles, les portes NAND et NOR utilisent moins de transistors que les portes ET et OU. Et cette possibilité a été utilisée pour effectuer des simplifications assez mineures, mais toujours bonnes à prendre. Mais nous ne pouvons pas en parler en détail ici, car nous n'avons pas encore parlé en détail des transistors. Cependant, un exemple bien précis nous est accesible : celui de l’incrémenteur du processeur 8085 d'Intel.
Le processeur 8085 d'Intel est un processeur 8 bits très ancien. Il contenait un incrémenteur 16 bits, qui était utilisé pour calculer des adresses mémoire. Et cet incrémenteur était un incrémenteur à propagation de retenue optimisé. Il utilisait lui aussi des incrémenteurs 2 bits un peu modifiés.
L'idée était que les retenues sortant des colonnes paires étaient inversées, les colonnes impaires faisaient les calculs à partir de cette retenue inversée. Les demi-additionneurs des colonnes impaires n'étaient donc pas les mêmes que ceux des colonnes paires. Leurs tables de vérité sont différentes, leurs circuits aussi, et aucun des deux ne ressemble à un demi-additionneur normal. En réalité, ils sont fabriqués à partir des implémentations vues plus haut, qui utilisent uniquement des portes NAND ou uniquement des portes NOR.
Pour comprendre l'intérêt de faire ainsi, nous devons préciser une chose importante : avec les technologies CMOS utilisées pour les processeurs depuis les années 70, les portes logiques les plus simples sont les portes NON, NOR et NAND. Les portes ET/OU sont fabriquées en combinant des portes NOR/NAND avec une porte NON, ce qui prend plus de transistors.
Un demi-additionneur est donc fabriqué comme illustré ci-dessous, en logique CMOS. Seules les portes pour le calcul de la retenue sont indiquées, la porte XOR pour le bit de somme est fabriquée à partir de plusieurs portes logiques.
[[File:Demi-additionneur en CMOS.png|centre|vignette|upright=2|Demi-additionneur en CMOS]]
Si on enchaine deux demi-additionneurs, cela donne ceci :
[[File:Brique de base de l'incrémenteur du 8085.png|centre|vignette|upright=2|Brique de base de l'incrémenteur du 8085]]
La retenue doit donc traverser quatre portes logiques, dont deux portes NON. En inversant la retenue, les portes en jaune dans le schéma précédent se simplifient. Il suffit d'utiliser la loi de de Morgan pour trouver un circuit équivalent aux portes jaunes, qui n'est autre qu'une porte NOR couplée à une porte NON. Un autre moyen d'obtenir le même résultat est de prendre un demi-additionneur fabriqué avec uniquement des portes NOR, et de retirer la porte NON adéquate. Il faut ensuite corriger l'entrée de retenue de la porte XOR, pour qu'elle fasse l'addition avec la retenue correctement. Le résultat est illustré ci-dessous.
[[File:Incrémenteur 2 bits de l'incrémenteur du 8085.png|centre|vignette|upright=2|Incrémenteur 2 bits de l'incrémenteur du 8085]]
Le circuit utilise le même nombre de portes logiques, il n'économise pas de transistors. Par contre, la retenue n'a plus qu'à traverser deux portes logiques, au lieu de quatre. La propagation de la retenue est donc plus rapide, l'incrémentation est donc plus rapide.
===Les incrémenteurs 2 bits à calcul séparé de la retenue===
Il existe une autre méthode qui permet de créer des incrémenteurs 2 bits plus rapides que les précédents, mais qui utilisent plus de portes logiques. Ces incrémenteurs 2 bits calculent séparément les deux bits du résultat et la retenue sortante. La retenue sortante est calculée à partir des bits de l’opérande, sans avoir besoin de propager des retenues. Ainsi, la retenue sortante est connue en avance, sans passer par deux demi-additionneurs. Formellement, ils sont un cas particulier d'une optimisation appelée l'''anticipation de retenue''. Si nous verrons le cas général dans la section suivante, ce cas particulier est une très bonne introduction.
Pour créer un tel incrémenteur, le mieux est de partir de sa table de vérité.
{|class="wikitable"
|-
! Opérande !! Retenue entrante !! !! Retenue sortante !! Résultat
|-
| 00 || 0 || || 0 || 00
|-
| 01 || 0 || || 0 || 01
|-
| 10 || 0 || || 0 || 10
|-
| 11 || 0 || || 0 || 11
|-
| colspan="5" |
|-
| 00 || 1 || || 0 || 01
|-
| 01 || 1 || || 0 || 10
|-
| 10 || 1 || || 0 || 11
|-
| 11 || 1 || || 1 || 00
|}
Elle nous dit que la retenue sortante ne vaut 1 que dans un seul cas : les deux bits d'opérande valent 1, la retenue entrante vaut 1. Elle se calcule alors avec une porte ET à trois entrées. Pour les deux bits du résultat, les équations logiques ne donnent pas un résultat satisfaisant. La solution la plus simple est d'utiliser deux demi-additionneurs et de retirer les portes logiques superflues. Le résultat est alors un '''incrémenteur 2 bits ''carry skip''''', où la retenue sortante est calculée séparément du reste.
[[File:Incrémenteur carry skip 2 bits.png|centre|vignette|upright=2.5|Incrémenteur ''carry skip'' 2 bits]]
===Les incrémenteurs à anticipation de retenue===
L''''anticipation de retenue''' calcule la retenue d'une colonne sans attendre les retenues des colonnes précédentes. Dans le cas idéal, toutes les retenues sont calculées en parallèle, en même temps, et sont ensuite envoyées aux demi-additionneurs. Il s'agit d'une optimisation qui est utilisée pour l'incrémentation, l'addition et d'autres opérations similaires. Pour l'incrémentation, déterminer la retenue ne demande pas de calculs complexes, contrairement à l'addition.
[[File:Incrémenteur à anticipation de retenues, 8 bits.png|centre|vignette|upright=2.5|Incrémenteur à anticipation de retenues, 8 bits.]]
Pour le comprendre, on peut regarder comment la retenue sortant d'un demi-additionneur est formée. Elle est calculée en faisant un ET logique entre la retenue entrante et le bit d'opérande. Une retenue est donc un ET logique entre toutes les retenues précédentes. Un incrémenteur à anticipation de retenue utilise donc une porte ET à plusieurs entrées pour calculer une retenue.
[[File:Anticipation de retenue pour un bit du résultat, incrémenteur.png|centre|vignette|upright=2.5|Anticipation de retenue pour un bit du résultat, incrémenteur]]
En théorie, on peut utiliser une porte ET à plusieurs entrées pour chaque bit de l'opérande. Cependant, cela entrainera un cout en transistors très important. Pour éviter de gaspiller trop de portes logiques, une solution est de mélanger anticipation de retenues et propagation de retenue. Par exemple, pour un incrémenteur 32 bits, on peut découper l'opérande en 4 octets : on anticipe les retenues pour chaque octet, mais l'incrémentation de chaque octet se fait avec propagation de retenue et/ou ''carry skip''. Plus haut, nous avons vu un cas particulier où les retenues étaient anticipées par blocs de deux bits.
[[File:Incrémenteur à anticipation de retenues.png|centre|vignette|upright=3|Incrémenteur hybride utilisant partiellement l'anticipation de retenues.]]
Quelques processeurs utilisaient l'anticipation de retenues. Par exemple, le processeur Z-80 de Zilog utilisait un incrémenteur pour des nombres de 16 bits, ce qui demandait des performances assez élevées. Et cet incrémenteur utilisait à la fois anticipation de retenues et ''carry skip''. Il était découpé en quatre blocs avant anticipation de retenues entre eux : un bloc regroupant les 7 bits de poids faible, suivi par un bloc de 5 bits, lui-même suivi par un bloc de 3 bits, terminé par un dernier bit isolé. A l'intérieur de ces blocs, les bits sont regroupés en paires utilisant le ''carry skip''. Pour ceux qui veulent en savoir plus sur cet incrémenteur, voici un lien sur le sujet :
* [https://www.righto.com/2013/11/the-z-80s-16-bit-incrementdecrement.html The Z-80's 16-bit increment/decrement circuit reverse engineered].
==Les décrémenteurs==
Les circuits décrémenteurs font l'inverse d'un incrémenteur : au lieu d'ajouter 1 à un opérande, ils lui soustraient 1. La différence n'est pas énorme, ce qui fait que les circuits incrémenteurs et décrémenteurs se ressemblent beaucoup. Là où les incrémenteurs sont fabriqués à partir de demi-additionneurs, les décrémenteurs sont bâtis avec des demi-soustracteurs.
La soustraction se fait en binaire comme en décimal. On soustrait deux chiffres, puis on doit propager une éventuelle retenue sur la colonne suivante. La retenue apparait quand le chiffre soustrait est plus grand que l'autre chiffre. Elle est propagée sur la colonne suivante, où elle doit être soustraite du résultat. Pour le dire autrement, après avoir soustrait un chiffre, on doit de plus soustraire la retenue de la colonne précédente.
La décrémentation effectue le calcul suivant :
<math>a_7</math> <math>a_6</math> <math>a_5</math> <math>a_4</math> <math>a_3</math> <math>a_2</math> <math>a_1</math> <math>a_0</math>
- 0 0 0 0 0 0 0 1
La colonne la plus à droite soustrait un 1. Par contre, les colonnes suivantes soustraient un zéro. Soustraire un zéro ne change rien. Mais il ne faut pas oublier de soustraire une éventuellement retenue, propagée depuis la colonne précédente. En clair, un décrémenteur peut se construire si on sait soustraire deux bits. On soustrait un 1 sur la colonne la plus à droite, on soustrait une retenue sur les autres colonnes. Et pour soustraire deux bits, il faut utiliser un demi-soustracteur.
===Le demi-soustracteur===
Un '''demi-soustracteur''' soustrait deux bits. Pour comprendre comment soustraire deux bits, traitons les quatre cas possibles un par un.
Premièrement, soustraire zéro à un bit ne changera rien :
* 0 - 0 = 0 ;
* 1 - 0 = 1.
Si on soustrait 1 à un bit qui vaut 1, on obtient zéro.
* 1 - 1 = 0.
Maintenant, que se passe-t-il si on soustrait 1 à 0 ? Voici le résultat :
* 0 - 1 = 1 et une retenue propagée sur la colonne suivante, où elle est soustraite.
La table de soustraction est donc la suivante :
* 0 - 0 = 0 ;
* 0 - 1 = 1 et une retenue ;
* 1 - 0 = 1 ;
* 1 - 1 = 0.
La table de vérité est donc la suivante. Détail important : le demi-soustracteur fait ici la soustraction X - Y. En anglais, X est appelé le ''Minuend'' et Y est appelé le ''Subtrahend'', mais ces termes n'ont pas de traduction française. Aussi, je vais les utiliser tels quels dans ce qui suit.
{|class="wikitable"
|-
! ''Minuend'' !! ''Subtrahend'' !! !! Retenue sortante !! Bit de somme
|-
||0||0|| ||0||0
|-
||0||1|| ||1||1
|-
||1||0|| ||0||1
|-
||1||1|| ||0||0
|}
Si on construit la table de vérité, et qu'on construit le circuit associé, on tombe sur ce circuit.
[[File:Half Subtractor Vektor.svg|centre|vignette|Demi-soustracteur.]]
Vous remarquerez que le bit de somme se calcule de la même manière que pour l'addition, seul le calcul de la retenue change.
===Le décrémenteur à propagation de retenue===
Un décrémenteur à propagation de retenue se construit comme un incrémenteur, sauf qu'on remplace les demi-additionneurs par des demi-soustracteurs. En clair, on enchaine plusieurs demi-soustracteurs les uns à la suite des autres.
Il est possible d'utiliser l'anticipation de retenue, mais les circuits sont cependant différents de l'incrémenteur. Idem avec le ''carry skip'', et toute autre optimisation possible. La raison est que les retenues sont calculées différemment, ce qui fait que l’anticipation de retenue doit être adaptée. La retenue d'une colonne ne se calcule plus avec un ET des bits des colonnes précédentes. A la place, ces bits doivent être inversés avant de faire le ET. Le résultat est que la porte ET est remplacée par une porte NOR.
Pour ceux qui s'en rappelle, faire un NOR de N bits permet de vérifier s'ils sont tous à zéro. Et c'est le cas ici. Lors d'une incrémentation, on a une retenue quand tous les bits précédents de l'opérande sont à 1. Pour la décrémentation, c'est l'inverse : on a une retenue quand tous les bits précédents sont à 0.
De fait, décrémenter un nombre est assez simple. Il y a deux cas principaux :
* soit le bit de poids faible est un 1, ce qui signifie que le nombre est impair ;
* soit un ou plusieurs bits de poids faible consécutifs sont à 0, suivis par un 1.
Dans le premier cas, le bit de poids faible est juste inversé. Dans le second cas, le 1 est inversé, et les 0 de poids faible deviennent des 1. Par exemple, Prenez un nombre de la forme xxxxxxx10000. Après décrémentation, le résultat sera xxxxxxxx011111 : les 0 de poids faible deviennent des 1, le premier 1 en partant de la droite est inversé. Et la même logique se généralise pour tout nombre avant un ou plusieurs zéros dans les bits de poids faible.
==Les incrémenteurs-décrémenteurs==
Les circuits décrémenteurs ressemblent beaucoup aux incrémenteurs. Aussi, vous ne serez pas étonné qu'il soit possible de fusionner un incrémenteur et un décrémenteur en un seul circuit. Un tel circuit s'appelle un '''incrémenteur-décrémenteur'''. Un tel circuit a une interface similaire à celle d'un incrémenteur, avec une entrée sur laquelle présenter l'opérande, et une sortie sur laquelle récupérer le résultat. Il a aussi une entrée de retenue, sur laquelle envoyer un 1 (incrémentation) ou un 0 (opération NOP/identité). Et il a aussi une sortie de retenue, qui indique que le résultat du calcul a débordé.
Mais en plus de cela, il dispose d'une entrée de commande qui permet de choisir entre incrémentation et décrémentation.
Il existe plusieurs manières de créer un incrémenteur-décrémenteur, que nous allons voir si dessous. Nous n'allons pas voir le cas le plus évident, aussi ne vous étonnez pas si vous avez remarqué qu'un demi-additionneur et un demi-soustracteur se ressemblent beaucoup.
===L'incrémenteur-décrémenteur basé sur des demi-additionneurs===
Une première implémentation se base sur les propriétés du complément à deux, et plus précisément sur cette équation :
: <math>\overline{x} + 1 = \overline{x - 1}</math>
Les deux termes de cette équation sont simplement des définitions équivalentes du complément à deux d'un nombre entier. Celui-ci se calcule de deux manières : soit on incrémente son complément à 1, soit on prend le complément à 1 de son décrément. Et cela nous permet de déterminer comment obtenir une incrémentation à partir d'une décrémentation et inversement.
Que nous dit cette équation ? Simplement que l'on peut décrémenter un nombre si on a deux inverseurs commandables, et un circuit incrémenteur. La décrémentation se fait alors en deux étapes : on calcule son complément à deux, puis on inverse les bits du résultat précédent. Le calcul du complément à deux demande d'avoir un circuit inverseur, et un incrémenteur.
===L'incrémenteur-décrémenteur basé sur des demi-additionneurs===
Vous l'aurez sans doute remarqué, mais le demi-soustracteur ressemble beaucoup au demi-additionneur. La seule différence est une porte NON ajoutée au bon endroit dans le demi-soustracteur. La porte NON inverse le bit X, sachant que le demi-soustracteur fait la soustraction X - Y. De plus, l'opérande inversée est utilisée seulement pour le calcul des retenues, le bit de somme est calculé normalement.
{|
|[[File:Half-adder.svg|vignette|Demi-additionneur]]
|[[File:Half Subtractor Vektor.svg|vignette|upright=1.3|Demi-soustracteur]]
|}
La conséquence est qu'il est possible de fusionner un demi-additionneur avec un demi-soustracteur. L'idée est de remplacer la porte NON du demi-soustracteur par un inverseur commandable. Si on envoie un zéro sur l'entrée de commande, l'inverseur commandable se comportera comme une porte OUI et le circuit sera alors identique à un demi-additionneur. Si on envoie un 1 sur l'entrée de commande, l'inverseur commandable se comportera comme une porte NON. le circuit sera alors un demi-soustracteur. En remplaçant les demi-additionneurs (ou demi-soustracteurs) par de tels circuits, on peut obtenir un circuit faisant à la fois incrémenteur et décrémenteur.
Un défaut de cette approche est que les optimisations vues plus haut pour les demi-additionneurs ne fonctionne tout simplement plus. On est obligé d'utiliser une porte XOR et une porte ET séparées, pas question d'utiliser les demi-additionneurs optimisés vu au début du chapitre.
<noinclude>
{{NavChapitre | book=Fonctionnement d'un ordinateur
| prev=Les circuits de sélection
| prevText=Les circuits de sélection
| next=Les bascules : des mémoires de 1 bit
| nextText=Les bascules : des mémoires de 1 bit
}}
</noinclude>
jnsmjr7sissdx2mqxtgnz2tzobgtwpr
768799
768798
2026-06-27T16:02:12Z
Mewtow
31375
/* Le demi-soustracteur */
768799
wikitext
text/x-wiki
Le circuit '''incrémenteur''' incrémente un nombre entier. Plus précisément, il prend en entrée un nombre entier, et fournit en entier le nombre suivant, il lui ajoute 1. Une telle opération peut paraitre triviale et peu intéressante. Cependant, elle est très utilisée et est absolument cruciale pour créer des compteurs, des circuits capables de compter ou décompter, qui auront droit à un chapitre dédié. De plus, au-delà de leur utilisation dans les compteurs, les incrémenteurs étaient très utilisés sur les premiers processeurs 8 bits, comme le Z-80, le 6502, les premiers processeurs x86 comme le 8008, le 8086, le 8085, et bien d'autres.
Il existe aussi des ''circuits décrémenteurs'', qui décrémentent un opérande, ainsi que des ''circuits incrémenteur-décrémenteur'', qui peuvent incrémenter ou décrémenter, selon comment on les configure. Je ne vais pas détailler ces circuits plus que ça, car de tels circuits sont assez rares, comparé à un circuit incrémenteur simple.
==Le demi-additionneur==
Le circuit incrémenteur effectue l'opération suivante :
<math>a_7</math> <math>a_6</math> <math>a_5</math> <math>a_4</math> <math>a_3</math> <math>a_2</math> <math>a_1</math> <math>a_0</math>
+ 0 0 0 0 0 0 0 1
Un incrémenteur basique doit donc faire une addition pour chaque colonne, et précisément une addition de deux bits. Il se trouve que la table d'addition est très simple en binaire. Jugez plutôt :
* 0 + 0 = 0, retenue = 0 ;
* 0 + 1 = 1, retenue = 0 ;
* 1 + 0 = 1, retenue = 0 ;
* 1 + 1 = 0, retenue = 1.
Un circuit capable d'additionner deux bits est appelé un '''demi-additionneur'''. Il dispose d'une sortie S pour la somme, et C pour la retenue (''carry'' an anglais).
===Le demi-additionneur classique===
Un demi-additionneur est très simple à construire avec les techniques vues dans les premiers chapitres. Voici sa table de vérité :
{|class="wikitable"
|-
! Retenue entrante !! Opérande 1 !! !! Retenue sortante !! Bit de somme
|-
||0||0|| ||0||0
|-
||0||1|| ||0||1
|-
||1||0|| ||0||1
|-
||1||1|| ||1||0
|}
On voit immédiatement que la colonne des retenues donne une porte ET, alors que celle du bit de somme est calculé par un XOR.
{| class="flexible"
|[[File:1-bit half-adder.svg|class=transparent|centre|Demi-addtionneur.]]
|[[File:Half-adder.svg|centre|class=transparent|Circuit d'un demi-addtionneur.]]
|}
Il existe beaucoup d'autres implémentations alternatives, qui utilisent moins de portes logiques, calculent la retenue plus rapidement, consomment moins d'énergie, et autres. Et il est intéressant d'étudier quelques alternatives, surtout qu'elles serviront plus bas, quand on étudiera le circuit incrémenteur du 8085.
===Le demi-additionneur basé sur une modification de la retenue===
Une source d'amélioration est liée à la porte XOR. En pratique, une porte XOR est composée en combinant plusieurs portes logiques ET/OU/NOR/NAND ensemble. Et il se trouve qu'il y a des redondances entre celles-ci et les portes utilisées pour calculer la retenue. Par exemple, rappelons qu'une porte XOR peut être construite avec une porte ET et deux portes NOR, comme illustré ci-dessous. Et il se trouve que la porte ET interne calcule la retenue sortante. En supprimant cette redondance, on économise quelques portes logiques. De plus, on se retrouve avec un demi-additionneur qui calcule le bit de somme à partir de la retenue sortante.
[[File:Demi-additionneur avec redondances éliminées.png|centre|vignette|upright=2|Demi-additionneur avec redondances éliminées]]
Le circuit précédent a une interprétation logique. Si vous regardez la table de vérité, vous remarquerez que la somme de deux bits est égale à l'inverse de la retenue sortante, sauf dans le cas où les deux bits additionnés valent zéro. Et le circuit précédent est basé là-dessus.
{|class="wikitable"
|-
! Retenue entrante !! Opérande 1 !! !! Retenue sortante !! Bit de somme
|- class="f_vert"
||0||0|| ||0||0
|- class="f_rouge"
||0||1|| ||0||1
|- class="f_rouge"
||1||0|| ||0||1
|- class="f_rouge"
||1||1|| ||1||0
|}
L'idée est de calculer l'inverse du bit de somme, avant de l'inverser avec une porte NON. L'inverse du bit de somme vaut 1, soit quand la retenue est à 1, soit quand les deux bits additionnés sont à 0. La porte ET calcule la retenue sortante, la première porte NOR détecte sur les deux bits d'entrée valent zéro. Une porte OU combine les deux résultat pour obtenir l'inverse du bit d'entrée, puis une porte NOn inverse le tout pour obtenir le bit de somme adéquat. Les deux portes sont fusionnées : c'est la seconde porte NOR.
[[File:Full adder HA + MUX.png|centre|vignette|upright=2.5|Demi-additionneur basé sur une inversion de la retenue sortante.]]
===Les implémentations alternatives===
Une implémentation alternative, qui n'utilise que des portes NOR, est la suivante :
[[File:Demi-additionneur fait avec des portes NOR et NAND.png|centre|vignette|upright=2|Demi-additionneur fait avec des portes NOR et NAND.]]
Une implémentation alternative utilise des portes NAND. Pour rappel, il est possible de créer une porte XOR avec 4 portes NAND. La première d'entre elle fait un NAND entre les deux bits d'entrée, ce qui fait qu'elle calcule l'inverse de la retenue sortante. Le tout est illustré ci-dessous. En théorie, on devrait utiliser une porte NON pour récupérer la retenue correcte. Mais nous verrons plus bas que ce n'est pas une obligation. Nous verrons plus bas un exemple où c'est l'inverse de la retenue qui est utilisée dans les calculs, pour rendre les calculs plus rapides.
[[File:Demi-additionneur fait avec des portes NAND.png|centre|vignette|upright=2|Demi-additionneur fait avec des portes NAND]]
==Le circuit incrémenteur==
Maintenant que l'on sait comment additionner deux bits, reprenons l'opération d'incrémentation :
<math>a_7</math> <math>a_6</math> <math>a_5</math> <math>a_4</math> <math>a_3</math> <math>a_2</math> <math>a_1</math> <math>a_0</math>
+ 0 0 0 0 0 0 0 1
------------------------------
Sur la colonne la plus à droite, il doit ajouter un au bit de poids faible. Pour les colonnes suivantes, il faut additionner le bit de l'opérande avec la retenue de la colonne précédente. En clair, on n'additionne que deux bits à chaque colonne : un 1 sur celle tout à droite, la retenue de la colonne précédente pour les autres. Et cela nous donne une idée de comment faire pour créer le circuit incrémenteur.
===L'incrémenteur à propagation de retenue===
Il suffit d'utiliser un demi-additionneur par colonne, et de les enchainer les uns à la suite des autres. Chaque demi-additionneur additionne le bit de l'opérande avec la retenue fournie par le demi-additionneur précédent. Le seul qui fait exception est celui pour la colonne de poids faible. Pour celui-là, il doit ajouter 1 au bit de poids faible. Le résultat est appelé un '''incrémenteur à propagation de retenue'''. Il est constitué de demi-additionneurs enchaînés les uns à la suite des autres, du bit de poids faible vers le bit de poids fort.
[[File:Circuit incrémenteur.png|centre|vignette|upright=3|Circuit incrémenteur.]]
Maintenant, regardons le demi-additionneur le plus à droite, celui pour le bit de poids faible. Son entrée de retenue entrante est mise à 1 pour faire l'incrémentation. Quelques incrémenteurs permettent de configurer cette entrée de retenue à 0 ou à 1, ce qui effectue : soit une opération identité (l'opérande est recopié sur la sortie), soit une incrémentation. Un tel circuit est nommé un '''incrémenteur commandable'''. Nous aurons à utiliser une fois ou deux de tels incrémenteurs commandables dans la suite du cours.
L'incrémenteur à propagation de retenue est le plus simple et le plus économe en portes logiques. Mais de tels incrémenteurs sont rarement utilisés. À la place, on leur préfère des incrémenteurs plus rapides, mais qui utilisent plus de portes logiques. De tels incrémenteurs accélèrent le calcul des retenues. En effet, la rapidité d'une incrémentation est limitée par la propagation de la retenue : les retenues commencent à être calculées au bit de poids fort et on doit les calculer une par une, jusqu’à atteindre le bit de poids fort. Et cette "propagation des retenues" prend du temps, d'autant plus de temps que l'opérande est longue. Il y a deux optimisations principales, appelées le ''carry skip'' et l'anticipation de retenue, que nous allons décrire ci-dessous.
===Les incrémenteurs optimisés===
L'optimisation que nous allns voir effectue l'incrémentation, non pas bit par bit, mais par paquets de deux bits. Le résultat est que l'incrémentation est deux fois plus rapide, ou presque. Le circuit incrémenteur est donc composé en enchainant non pas des demi-additionneurs, mais des '''incrémenteurs 2 bits''' qui incrémentent un opérande de deux bits.
[[File:Circuit incrémenteur optimisé, faisant l'incrémentation par paquet de deux bits.png|centre|vignette|upright=2.5|Circuit incrémenteur optimisé, faisant l'incrémentation par paquet de deux bits]]
Reste à concevoir l'incrémenteur 2 bits. Une première méthode part de deux demi-additionneurs et effectue quelques simplifications au niveau des transistors et des portes logiques. Par exemple, l'Intel 8085 a utilisé un circuit optimisé, fabriqué avec des portes NAND et NOR. La raison est qu'avec les technologies de transistors CMOS actuelles, les portes NAND et NOR utilisent moins de transistors que les portes ET et OU. Et cette possibilité a été utilisée pour effectuer des simplifications assez mineures, mais toujours bonnes à prendre. Mais nous ne pouvons pas en parler en détail ici, car nous n'avons pas encore parlé en détail des transistors. Cependant, un exemple bien précis nous est accesible : celui de l’incrémenteur du processeur 8085 d'Intel.
Le processeur 8085 d'Intel est un processeur 8 bits très ancien. Il contenait un incrémenteur 16 bits, qui était utilisé pour calculer des adresses mémoire. Et cet incrémenteur était un incrémenteur à propagation de retenue optimisé. Il utilisait lui aussi des incrémenteurs 2 bits un peu modifiés.
L'idée était que les retenues sortant des colonnes paires étaient inversées, les colonnes impaires faisaient les calculs à partir de cette retenue inversée. Les demi-additionneurs des colonnes impaires n'étaient donc pas les mêmes que ceux des colonnes paires. Leurs tables de vérité sont différentes, leurs circuits aussi, et aucun des deux ne ressemble à un demi-additionneur normal. En réalité, ils sont fabriqués à partir des implémentations vues plus haut, qui utilisent uniquement des portes NAND ou uniquement des portes NOR.
Pour comprendre l'intérêt de faire ainsi, nous devons préciser une chose importante : avec les technologies CMOS utilisées pour les processeurs depuis les années 70, les portes logiques les plus simples sont les portes NON, NOR et NAND. Les portes ET/OU sont fabriquées en combinant des portes NOR/NAND avec une porte NON, ce qui prend plus de transistors.
Un demi-additionneur est donc fabriqué comme illustré ci-dessous, en logique CMOS. Seules les portes pour le calcul de la retenue sont indiquées, la porte XOR pour le bit de somme est fabriquée à partir de plusieurs portes logiques.
[[File:Demi-additionneur en CMOS.png|centre|vignette|upright=2|Demi-additionneur en CMOS]]
Si on enchaine deux demi-additionneurs, cela donne ceci :
[[File:Brique de base de l'incrémenteur du 8085.png|centre|vignette|upright=2|Brique de base de l'incrémenteur du 8085]]
La retenue doit donc traverser quatre portes logiques, dont deux portes NON. En inversant la retenue, les portes en jaune dans le schéma précédent se simplifient. Il suffit d'utiliser la loi de de Morgan pour trouver un circuit équivalent aux portes jaunes, qui n'est autre qu'une porte NOR couplée à une porte NON. Un autre moyen d'obtenir le même résultat est de prendre un demi-additionneur fabriqué avec uniquement des portes NOR, et de retirer la porte NON adéquate. Il faut ensuite corriger l'entrée de retenue de la porte XOR, pour qu'elle fasse l'addition avec la retenue correctement. Le résultat est illustré ci-dessous.
[[File:Incrémenteur 2 bits de l'incrémenteur du 8085.png|centre|vignette|upright=2|Incrémenteur 2 bits de l'incrémenteur du 8085]]
Le circuit utilise le même nombre de portes logiques, il n'économise pas de transistors. Par contre, la retenue n'a plus qu'à traverser deux portes logiques, au lieu de quatre. La propagation de la retenue est donc plus rapide, l'incrémentation est donc plus rapide.
===Les incrémenteurs 2 bits à calcul séparé de la retenue===
Il existe une autre méthode qui permet de créer des incrémenteurs 2 bits plus rapides que les précédents, mais qui utilisent plus de portes logiques. Ces incrémenteurs 2 bits calculent séparément les deux bits du résultat et la retenue sortante. La retenue sortante est calculée à partir des bits de l’opérande, sans avoir besoin de propager des retenues. Ainsi, la retenue sortante est connue en avance, sans passer par deux demi-additionneurs. Formellement, ils sont un cas particulier d'une optimisation appelée l'''anticipation de retenue''. Si nous verrons le cas général dans la section suivante, ce cas particulier est une très bonne introduction.
Pour créer un tel incrémenteur, le mieux est de partir de sa table de vérité.
{|class="wikitable"
|-
! Opérande !! Retenue entrante !! !! Retenue sortante !! Résultat
|-
| 00 || 0 || || 0 || 00
|-
| 01 || 0 || || 0 || 01
|-
| 10 || 0 || || 0 || 10
|-
| 11 || 0 || || 0 || 11
|-
| colspan="5" |
|-
| 00 || 1 || || 0 || 01
|-
| 01 || 1 || || 0 || 10
|-
| 10 || 1 || || 0 || 11
|-
| 11 || 1 || || 1 || 00
|}
Elle nous dit que la retenue sortante ne vaut 1 que dans un seul cas : les deux bits d'opérande valent 1, la retenue entrante vaut 1. Elle se calcule alors avec une porte ET à trois entrées. Pour les deux bits du résultat, les équations logiques ne donnent pas un résultat satisfaisant. La solution la plus simple est d'utiliser deux demi-additionneurs et de retirer les portes logiques superflues. Le résultat est alors un '''incrémenteur 2 bits ''carry skip''''', où la retenue sortante est calculée séparément du reste.
[[File:Incrémenteur carry skip 2 bits.png|centre|vignette|upright=2.5|Incrémenteur ''carry skip'' 2 bits]]
===Les incrémenteurs à anticipation de retenue===
L''''anticipation de retenue''' calcule la retenue d'une colonne sans attendre les retenues des colonnes précédentes. Dans le cas idéal, toutes les retenues sont calculées en parallèle, en même temps, et sont ensuite envoyées aux demi-additionneurs. Il s'agit d'une optimisation qui est utilisée pour l'incrémentation, l'addition et d'autres opérations similaires. Pour l'incrémentation, déterminer la retenue ne demande pas de calculs complexes, contrairement à l'addition.
[[File:Incrémenteur à anticipation de retenues, 8 bits.png|centre|vignette|upright=2.5|Incrémenteur à anticipation de retenues, 8 bits.]]
Pour le comprendre, on peut regarder comment la retenue sortant d'un demi-additionneur est formée. Elle est calculée en faisant un ET logique entre la retenue entrante et le bit d'opérande. Une retenue est donc un ET logique entre toutes les retenues précédentes. Un incrémenteur à anticipation de retenue utilise donc une porte ET à plusieurs entrées pour calculer une retenue.
[[File:Anticipation de retenue pour un bit du résultat, incrémenteur.png|centre|vignette|upright=2.5|Anticipation de retenue pour un bit du résultat, incrémenteur]]
En théorie, on peut utiliser une porte ET à plusieurs entrées pour chaque bit de l'opérande. Cependant, cela entrainera un cout en transistors très important. Pour éviter de gaspiller trop de portes logiques, une solution est de mélanger anticipation de retenues et propagation de retenue. Par exemple, pour un incrémenteur 32 bits, on peut découper l'opérande en 4 octets : on anticipe les retenues pour chaque octet, mais l'incrémentation de chaque octet se fait avec propagation de retenue et/ou ''carry skip''. Plus haut, nous avons vu un cas particulier où les retenues étaient anticipées par blocs de deux bits.
[[File:Incrémenteur à anticipation de retenues.png|centre|vignette|upright=3|Incrémenteur hybride utilisant partiellement l'anticipation de retenues.]]
Quelques processeurs utilisaient l'anticipation de retenues. Par exemple, le processeur Z-80 de Zilog utilisait un incrémenteur pour des nombres de 16 bits, ce qui demandait des performances assez élevées. Et cet incrémenteur utilisait à la fois anticipation de retenues et ''carry skip''. Il était découpé en quatre blocs avant anticipation de retenues entre eux : un bloc regroupant les 7 bits de poids faible, suivi par un bloc de 5 bits, lui-même suivi par un bloc de 3 bits, terminé par un dernier bit isolé. A l'intérieur de ces blocs, les bits sont regroupés en paires utilisant le ''carry skip''. Pour ceux qui veulent en savoir plus sur cet incrémenteur, voici un lien sur le sujet :
* [https://www.righto.com/2013/11/the-z-80s-16-bit-incrementdecrement.html The Z-80's 16-bit increment/decrement circuit reverse engineered].
==Les décrémenteurs==
Les circuits décrémenteurs font l'inverse d'un incrémenteur : au lieu d'ajouter 1 à un opérande, ils lui soustraient 1. La différence n'est pas énorme, ce qui fait que les circuits incrémenteurs et décrémenteurs se ressemblent beaucoup. Là où les incrémenteurs sont fabriqués à partir de demi-additionneurs, les décrémenteurs sont bâtis avec des demi-soustracteurs.
La soustraction se fait en binaire comme en décimal. On soustrait deux chiffres, puis on doit propager une éventuelle retenue sur la colonne suivante. La retenue apparait quand le chiffre soustrait est plus grand que l'autre chiffre. Elle est propagée sur la colonne suivante, où elle doit être soustraite du résultat. Pour le dire autrement, après avoir soustrait un chiffre, on doit de plus soustraire la retenue de la colonne précédente.
La décrémentation effectue le calcul suivant :
<math>a_7</math> <math>a_6</math> <math>a_5</math> <math>a_4</math> <math>a_3</math> <math>a_2</math> <math>a_1</math> <math>a_0</math>
- 0 0 0 0 0 0 0 1
La colonne la plus à droite soustrait un 1. Par contre, les colonnes suivantes soustraient un zéro. Soustraire un zéro ne change rien. Mais il ne faut pas oublier de soustraire une éventuellement retenue, propagée depuis la colonne précédente. En clair, un décrémenteur peut se construire si on sait soustraire deux bits. On soustrait un 1 sur la colonne la plus à droite, on soustrait une retenue sur les autres colonnes. Et pour soustraire deux bits, il faut utiliser un demi-soustracteur.
===Le demi-soustracteur===
Un '''demi-soustracteur''' soustrait deux bits. Pour comprendre comment soustraire deux bits, traitons les quatre cas possibles un par un.
Premièrement, soustraire zéro à un bit ne changera rien :
* 0 - 0 = 0 ;
* 1 - 0 = 1.
Si on soustrait 1 à un bit qui vaut 1, on obtient zéro.
* 1 - 1 = 0.
Maintenant, que se passe-t-il si on soustrait 1 à 0 ? Voici le résultat :
* 0 - 1 = 1 et une retenue propagée sur la colonne suivante, où elle est soustraite.
La table de soustraction est donc la suivante :
* 0 - 0 = 0 ;
* 0 - 1 = 1 et une retenue ;
* 1 - 0 = 1 ;
* 1 - 1 = 0.
La table de vérité est donc la suivante. Détail important : le demi-soustracteur fait ici la soustraction X - Y. En anglais, X est appelé le ''Minuend'' et Y est appelé le ''Subtrahend'', mais ces termes n'ont pas de traduction française. Aussi, je vais les utiliser tels quels dans ce qui suit.
{|class="wikitable"
|-
! ''Minuend'' !! ''Subtrahend'' !! !! Retenue sortante !! Bit de somme
|-
||0||0|| ||0||0
|-
||0||1|| ||1||1
|-
||1||0|| ||0||1
|-
||1||1|| ||0||0
|}
Si on construit la table de vérité, et qu'on construit le circuit associé, on tombe sur ce circuit.
[[File:Half Subtractor Vektor.svg|centre|vignette|Demi-soustracteur.]]
Vous remarquerez que le bit de somme se calcule de la même manière que pour l'addition, seul le calcul de la retenue change. Mais cette différence a une conséquence majeure : les optimisations vues pour le demi-additionneur ne fonctionnent pas pour le demi-soustracteur. Il est impossible de fusionner la porte ET et la porte XOR en un seul circuit qui élimine des redondances, comme vu plus haut. Cette fois-ci, on n'a pas le choix : on doit utiliser une porte XOR séparée de la porte ET.
===Le décrémenteur à propagation de retenue===
Un décrémenteur à propagation de retenue se construit comme un incrémenteur, sauf qu'on remplace les demi-additionneurs par des demi-soustracteurs. En clair, on enchaine plusieurs demi-soustracteurs les uns à la suite des autres.
Il est possible d'utiliser l'anticipation de retenue, mais les circuits sont cependant différents de l'incrémenteur. Idem avec le ''carry skip'', et toute autre optimisation possible. La raison est que les retenues sont calculées différemment, ce qui fait que l’anticipation de retenue doit être adaptée. La retenue d'une colonne ne se calcule plus avec un ET des bits des colonnes précédentes. A la place, ces bits doivent être inversés avant de faire le ET. Le résultat est que la porte ET est remplacée par une porte NOR.
Pour ceux qui s'en rappelle, faire un NOR de N bits permet de vérifier s'ils sont tous à zéro. Et c'est le cas ici. Lors d'une incrémentation, on a une retenue quand tous les bits précédents de l'opérande sont à 1. Pour la décrémentation, c'est l'inverse : on a une retenue quand tous les bits précédents sont à 0.
De fait, décrémenter un nombre est assez simple. Il y a deux cas principaux :
* soit le bit de poids faible est un 1, ce qui signifie que le nombre est impair ;
* soit un ou plusieurs bits de poids faible consécutifs sont à 0, suivis par un 1.
Dans le premier cas, le bit de poids faible est juste inversé. Dans le second cas, le 1 est inversé, et les 0 de poids faible deviennent des 1. Par exemple, Prenez un nombre de la forme xxxxxxx10000. Après décrémentation, le résultat sera xxxxxxxx011111 : les 0 de poids faible deviennent des 1, le premier 1 en partant de la droite est inversé. Et la même logique se généralise pour tout nombre avant un ou plusieurs zéros dans les bits de poids faible.
==Les incrémenteurs-décrémenteurs==
Les circuits décrémenteurs ressemblent beaucoup aux incrémenteurs. Aussi, vous ne serez pas étonné qu'il soit possible de fusionner un incrémenteur et un décrémenteur en un seul circuit. Un tel circuit s'appelle un '''incrémenteur-décrémenteur'''. Un tel circuit a une interface similaire à celle d'un incrémenteur, avec une entrée sur laquelle présenter l'opérande, et une sortie sur laquelle récupérer le résultat. Il a aussi une entrée de retenue, sur laquelle envoyer un 1 (incrémentation) ou un 0 (opération NOP/identité). Et il a aussi une sortie de retenue, qui indique que le résultat du calcul a débordé.
Mais en plus de cela, il dispose d'une entrée de commande qui permet de choisir entre incrémentation et décrémentation.
Il existe plusieurs manières de créer un incrémenteur-décrémenteur, que nous allons voir si dessous. Nous n'allons pas voir le cas le plus évident, aussi ne vous étonnez pas si vous avez remarqué qu'un demi-additionneur et un demi-soustracteur se ressemblent beaucoup.
===L'incrémenteur-décrémenteur basé sur des demi-additionneurs===
Une première implémentation se base sur les propriétés du complément à deux, et plus précisément sur cette équation :
: <math>\overline{x} + 1 = \overline{x - 1}</math>
Les deux termes de cette équation sont simplement des définitions équivalentes du complément à deux d'un nombre entier. Celui-ci se calcule de deux manières : soit on incrémente son complément à 1, soit on prend le complément à 1 de son décrément. Et cela nous permet de déterminer comment obtenir une incrémentation à partir d'une décrémentation et inversement.
Que nous dit cette équation ? Simplement que l'on peut décrémenter un nombre si on a deux inverseurs commandables, et un circuit incrémenteur. La décrémentation se fait alors en deux étapes : on calcule son complément à deux, puis on inverse les bits du résultat précédent. Le calcul du complément à deux demande d'avoir un circuit inverseur, et un incrémenteur.
===L'incrémenteur-décrémenteur basé sur des demi-additionneurs===
Vous l'aurez sans doute remarqué, mais le demi-soustracteur ressemble beaucoup au demi-additionneur. La seule différence est une porte NON ajoutée au bon endroit dans le demi-soustracteur. La porte NON inverse le bit X, sachant que le demi-soustracteur fait la soustraction X - Y. De plus, l'opérande inversée est utilisée seulement pour le calcul des retenues, le bit de somme est calculé normalement.
{|
|[[File:Half-adder.svg|vignette|Demi-additionneur]]
|[[File:Half Subtractor Vektor.svg|vignette|upright=1.3|Demi-soustracteur]]
|}
La conséquence est qu'il est possible de fusionner un demi-additionneur avec un demi-soustracteur. L'idée est de remplacer la porte NON du demi-soustracteur par un inverseur commandable. Si on envoie un zéro sur l'entrée de commande, l'inverseur commandable se comportera comme une porte OUI et le circuit sera alors identique à un demi-additionneur. Si on envoie un 1 sur l'entrée de commande, l'inverseur commandable se comportera comme une porte NON. le circuit sera alors un demi-soustracteur. En remplaçant les demi-additionneurs (ou demi-soustracteurs) par de tels circuits, on peut obtenir un circuit faisant à la fois incrémenteur et décrémenteur.
Un défaut de cette approche est que les optimisations vues plus haut pour les demi-additionneurs ne fonctionne tout simplement plus. On est obligé d'utiliser une porte XOR et une porte ET séparées, pas question d'utiliser les demi-additionneurs optimisés vu au début du chapitre.
<noinclude>
{{NavChapitre | book=Fonctionnement d'un ordinateur
| prev=Les circuits de sélection
| prevText=Les circuits de sélection
| next=Les bascules : des mémoires de 1 bit
| nextText=Les bascules : des mémoires de 1 bit
}}
</noinclude>
lzoyiptvqgogpvuxzyntq7fsp4ehi7w
768800
768799
2026-06-27T16:04:52Z
Mewtow
31375
/* Le demi-soustracteur */
768800
wikitext
text/x-wiki
Le circuit '''incrémenteur''' incrémente un nombre entier. Plus précisément, il prend en entrée un nombre entier, et fournit en entier le nombre suivant, il lui ajoute 1. Une telle opération peut paraitre triviale et peu intéressante. Cependant, elle est très utilisée et est absolument cruciale pour créer des compteurs, des circuits capables de compter ou décompter, qui auront droit à un chapitre dédié. De plus, au-delà de leur utilisation dans les compteurs, les incrémenteurs étaient très utilisés sur les premiers processeurs 8 bits, comme le Z-80, le 6502, les premiers processeurs x86 comme le 8008, le 8086, le 8085, et bien d'autres.
Il existe aussi des ''circuits décrémenteurs'', qui décrémentent un opérande, ainsi que des ''circuits incrémenteur-décrémenteur'', qui peuvent incrémenter ou décrémenter, selon comment on les configure. Je ne vais pas détailler ces circuits plus que ça, car de tels circuits sont assez rares, comparé à un circuit incrémenteur simple.
==Le demi-additionneur==
Le circuit incrémenteur effectue l'opération suivante :
<math>a_7</math> <math>a_6</math> <math>a_5</math> <math>a_4</math> <math>a_3</math> <math>a_2</math> <math>a_1</math> <math>a_0</math>
+ 0 0 0 0 0 0 0 1
Un incrémenteur basique doit donc faire une addition pour chaque colonne, et précisément une addition de deux bits. Il se trouve que la table d'addition est très simple en binaire. Jugez plutôt :
* 0 + 0 = 0, retenue = 0 ;
* 0 + 1 = 1, retenue = 0 ;
* 1 + 0 = 1, retenue = 0 ;
* 1 + 1 = 0, retenue = 1.
Un circuit capable d'additionner deux bits est appelé un '''demi-additionneur'''. Il dispose d'une sortie S pour la somme, et C pour la retenue (''carry'' an anglais).
===Le demi-additionneur classique===
Un demi-additionneur est très simple à construire avec les techniques vues dans les premiers chapitres. Voici sa table de vérité :
{|class="wikitable"
|-
! Retenue entrante !! Opérande 1 !! !! Retenue sortante !! Bit de somme
|-
||0||0|| ||0||0
|-
||0||1|| ||0||1
|-
||1||0|| ||0||1
|-
||1||1|| ||1||0
|}
On voit immédiatement que la colonne des retenues donne une porte ET, alors que celle du bit de somme est calculé par un XOR.
{| class="flexible"
|[[File:1-bit half-adder.svg|class=transparent|centre|Demi-addtionneur.]]
|[[File:Half-adder.svg|centre|class=transparent|Circuit d'un demi-addtionneur.]]
|}
Il existe beaucoup d'autres implémentations alternatives, qui utilisent moins de portes logiques, calculent la retenue plus rapidement, consomment moins d'énergie, et autres. Et il est intéressant d'étudier quelques alternatives, surtout qu'elles serviront plus bas, quand on étudiera le circuit incrémenteur du 8085.
===Le demi-additionneur basé sur une modification de la retenue===
Une source d'amélioration est liée à la porte XOR. En pratique, une porte XOR est composée en combinant plusieurs portes logiques ET/OU/NOR/NAND ensemble. Et il se trouve qu'il y a des redondances entre celles-ci et les portes utilisées pour calculer la retenue. Par exemple, rappelons qu'une porte XOR peut être construite avec une porte ET et deux portes NOR, comme illustré ci-dessous. Et il se trouve que la porte ET interne calcule la retenue sortante. En supprimant cette redondance, on économise quelques portes logiques. De plus, on se retrouve avec un demi-additionneur qui calcule le bit de somme à partir de la retenue sortante.
[[File:Demi-additionneur avec redondances éliminées.png|centre|vignette|upright=2|Demi-additionneur avec redondances éliminées]]
Le circuit précédent a une interprétation logique. Si vous regardez la table de vérité, vous remarquerez que la somme de deux bits est égale à l'inverse de la retenue sortante, sauf dans le cas où les deux bits additionnés valent zéro. Et le circuit précédent est basé là-dessus.
{|class="wikitable"
|-
! Retenue entrante !! Opérande 1 !! !! Retenue sortante !! Bit de somme
|- class="f_vert"
||0||0|| ||0||0
|- class="f_rouge"
||0||1|| ||0||1
|- class="f_rouge"
||1||0|| ||0||1
|- class="f_rouge"
||1||1|| ||1||0
|}
L'idée est de calculer l'inverse du bit de somme, avant de l'inverser avec une porte NON. L'inverse du bit de somme vaut 1, soit quand la retenue est à 1, soit quand les deux bits additionnés sont à 0. La porte ET calcule la retenue sortante, la première porte NOR détecte sur les deux bits d'entrée valent zéro. Une porte OU combine les deux résultat pour obtenir l'inverse du bit d'entrée, puis une porte NOn inverse le tout pour obtenir le bit de somme adéquat. Les deux portes sont fusionnées : c'est la seconde porte NOR.
[[File:Full adder HA + MUX.png|centre|vignette|upright=2.5|Demi-additionneur basé sur une inversion de la retenue sortante.]]
===Les implémentations alternatives===
Une implémentation alternative, qui n'utilise que des portes NOR, est la suivante :
[[File:Demi-additionneur fait avec des portes NOR et NAND.png|centre|vignette|upright=2|Demi-additionneur fait avec des portes NOR et NAND.]]
Une implémentation alternative utilise des portes NAND. Pour rappel, il est possible de créer une porte XOR avec 4 portes NAND. La première d'entre elle fait un NAND entre les deux bits d'entrée, ce qui fait qu'elle calcule l'inverse de la retenue sortante. Le tout est illustré ci-dessous. En théorie, on devrait utiliser une porte NON pour récupérer la retenue correcte. Mais nous verrons plus bas que ce n'est pas une obligation. Nous verrons plus bas un exemple où c'est l'inverse de la retenue qui est utilisée dans les calculs, pour rendre les calculs plus rapides.
[[File:Demi-additionneur fait avec des portes NAND.png|centre|vignette|upright=2|Demi-additionneur fait avec des portes NAND]]
==Le circuit incrémenteur==
Maintenant que l'on sait comment additionner deux bits, reprenons l'opération d'incrémentation :
<math>a_7</math> <math>a_6</math> <math>a_5</math> <math>a_4</math> <math>a_3</math> <math>a_2</math> <math>a_1</math> <math>a_0</math>
+ 0 0 0 0 0 0 0 1
------------------------------
Sur la colonne la plus à droite, il doit ajouter un au bit de poids faible. Pour les colonnes suivantes, il faut additionner le bit de l'opérande avec la retenue de la colonne précédente. En clair, on n'additionne que deux bits à chaque colonne : un 1 sur celle tout à droite, la retenue de la colonne précédente pour les autres. Et cela nous donne une idée de comment faire pour créer le circuit incrémenteur.
===L'incrémenteur à propagation de retenue===
Il suffit d'utiliser un demi-additionneur par colonne, et de les enchainer les uns à la suite des autres. Chaque demi-additionneur additionne le bit de l'opérande avec la retenue fournie par le demi-additionneur précédent. Le seul qui fait exception est celui pour la colonne de poids faible. Pour celui-là, il doit ajouter 1 au bit de poids faible. Le résultat est appelé un '''incrémenteur à propagation de retenue'''. Il est constitué de demi-additionneurs enchaînés les uns à la suite des autres, du bit de poids faible vers le bit de poids fort.
[[File:Circuit incrémenteur.png|centre|vignette|upright=3|Circuit incrémenteur.]]
Maintenant, regardons le demi-additionneur le plus à droite, celui pour le bit de poids faible. Son entrée de retenue entrante est mise à 1 pour faire l'incrémentation. Quelques incrémenteurs permettent de configurer cette entrée de retenue à 0 ou à 1, ce qui effectue : soit une opération identité (l'opérande est recopié sur la sortie), soit une incrémentation. Un tel circuit est nommé un '''incrémenteur commandable'''. Nous aurons à utiliser une fois ou deux de tels incrémenteurs commandables dans la suite du cours.
L'incrémenteur à propagation de retenue est le plus simple et le plus économe en portes logiques. Mais de tels incrémenteurs sont rarement utilisés. À la place, on leur préfère des incrémenteurs plus rapides, mais qui utilisent plus de portes logiques. De tels incrémenteurs accélèrent le calcul des retenues. En effet, la rapidité d'une incrémentation est limitée par la propagation de la retenue : les retenues commencent à être calculées au bit de poids fort et on doit les calculer une par une, jusqu’à atteindre le bit de poids fort. Et cette "propagation des retenues" prend du temps, d'autant plus de temps que l'opérande est longue. Il y a deux optimisations principales, appelées le ''carry skip'' et l'anticipation de retenue, que nous allons décrire ci-dessous.
===Les incrémenteurs optimisés===
L'optimisation que nous allns voir effectue l'incrémentation, non pas bit par bit, mais par paquets de deux bits. Le résultat est que l'incrémentation est deux fois plus rapide, ou presque. Le circuit incrémenteur est donc composé en enchainant non pas des demi-additionneurs, mais des '''incrémenteurs 2 bits''' qui incrémentent un opérande de deux bits.
[[File:Circuit incrémenteur optimisé, faisant l'incrémentation par paquet de deux bits.png|centre|vignette|upright=2.5|Circuit incrémenteur optimisé, faisant l'incrémentation par paquet de deux bits]]
Reste à concevoir l'incrémenteur 2 bits. Une première méthode part de deux demi-additionneurs et effectue quelques simplifications au niveau des transistors et des portes logiques. Par exemple, l'Intel 8085 a utilisé un circuit optimisé, fabriqué avec des portes NAND et NOR. La raison est qu'avec les technologies de transistors CMOS actuelles, les portes NAND et NOR utilisent moins de transistors que les portes ET et OU. Et cette possibilité a été utilisée pour effectuer des simplifications assez mineures, mais toujours bonnes à prendre. Mais nous ne pouvons pas en parler en détail ici, car nous n'avons pas encore parlé en détail des transistors. Cependant, un exemple bien précis nous est accesible : celui de l’incrémenteur du processeur 8085 d'Intel.
Le processeur 8085 d'Intel est un processeur 8 bits très ancien. Il contenait un incrémenteur 16 bits, qui était utilisé pour calculer des adresses mémoire. Et cet incrémenteur était un incrémenteur à propagation de retenue optimisé. Il utilisait lui aussi des incrémenteurs 2 bits un peu modifiés.
L'idée était que les retenues sortant des colonnes paires étaient inversées, les colonnes impaires faisaient les calculs à partir de cette retenue inversée. Les demi-additionneurs des colonnes impaires n'étaient donc pas les mêmes que ceux des colonnes paires. Leurs tables de vérité sont différentes, leurs circuits aussi, et aucun des deux ne ressemble à un demi-additionneur normal. En réalité, ils sont fabriqués à partir des implémentations vues plus haut, qui utilisent uniquement des portes NAND ou uniquement des portes NOR.
Pour comprendre l'intérêt de faire ainsi, nous devons préciser une chose importante : avec les technologies CMOS utilisées pour les processeurs depuis les années 70, les portes logiques les plus simples sont les portes NON, NOR et NAND. Les portes ET/OU sont fabriquées en combinant des portes NOR/NAND avec une porte NON, ce qui prend plus de transistors.
Un demi-additionneur est donc fabriqué comme illustré ci-dessous, en logique CMOS. Seules les portes pour le calcul de la retenue sont indiquées, la porte XOR pour le bit de somme est fabriquée à partir de plusieurs portes logiques.
[[File:Demi-additionneur en CMOS.png|centre|vignette|upright=2|Demi-additionneur en CMOS]]
Si on enchaine deux demi-additionneurs, cela donne ceci :
[[File:Brique de base de l'incrémenteur du 8085.png|centre|vignette|upright=2|Brique de base de l'incrémenteur du 8085]]
La retenue doit donc traverser quatre portes logiques, dont deux portes NON. En inversant la retenue, les portes en jaune dans le schéma précédent se simplifient. Il suffit d'utiliser la loi de de Morgan pour trouver un circuit équivalent aux portes jaunes, qui n'est autre qu'une porte NOR couplée à une porte NON. Un autre moyen d'obtenir le même résultat est de prendre un demi-additionneur fabriqué avec uniquement des portes NOR, et de retirer la porte NON adéquate. Il faut ensuite corriger l'entrée de retenue de la porte XOR, pour qu'elle fasse l'addition avec la retenue correctement. Le résultat est illustré ci-dessous.
[[File:Incrémenteur 2 bits de l'incrémenteur du 8085.png|centre|vignette|upright=2|Incrémenteur 2 bits de l'incrémenteur du 8085]]
Le circuit utilise le même nombre de portes logiques, il n'économise pas de transistors. Par contre, la retenue n'a plus qu'à traverser deux portes logiques, au lieu de quatre. La propagation de la retenue est donc plus rapide, l'incrémentation est donc plus rapide.
===Les incrémenteurs 2 bits à calcul séparé de la retenue===
Il existe une autre méthode qui permet de créer des incrémenteurs 2 bits plus rapides que les précédents, mais qui utilisent plus de portes logiques. Ces incrémenteurs 2 bits calculent séparément les deux bits du résultat et la retenue sortante. La retenue sortante est calculée à partir des bits de l’opérande, sans avoir besoin de propager des retenues. Ainsi, la retenue sortante est connue en avance, sans passer par deux demi-additionneurs. Formellement, ils sont un cas particulier d'une optimisation appelée l'''anticipation de retenue''. Si nous verrons le cas général dans la section suivante, ce cas particulier est une très bonne introduction.
Pour créer un tel incrémenteur, le mieux est de partir de sa table de vérité.
{|class="wikitable"
|-
! Opérande !! Retenue entrante !! !! Retenue sortante !! Résultat
|-
| 00 || 0 || || 0 || 00
|-
| 01 || 0 || || 0 || 01
|-
| 10 || 0 || || 0 || 10
|-
| 11 || 0 || || 0 || 11
|-
| colspan="5" |
|-
| 00 || 1 || || 0 || 01
|-
| 01 || 1 || || 0 || 10
|-
| 10 || 1 || || 0 || 11
|-
| 11 || 1 || || 1 || 00
|}
Elle nous dit que la retenue sortante ne vaut 1 que dans un seul cas : les deux bits d'opérande valent 1, la retenue entrante vaut 1. Elle se calcule alors avec une porte ET à trois entrées. Pour les deux bits du résultat, les équations logiques ne donnent pas un résultat satisfaisant. La solution la plus simple est d'utiliser deux demi-additionneurs et de retirer les portes logiques superflues. Le résultat est alors un '''incrémenteur 2 bits ''carry skip''''', où la retenue sortante est calculée séparément du reste.
[[File:Incrémenteur carry skip 2 bits.png|centre|vignette|upright=2.5|Incrémenteur ''carry skip'' 2 bits]]
===Les incrémenteurs à anticipation de retenue===
L''''anticipation de retenue''' calcule la retenue d'une colonne sans attendre les retenues des colonnes précédentes. Dans le cas idéal, toutes les retenues sont calculées en parallèle, en même temps, et sont ensuite envoyées aux demi-additionneurs. Il s'agit d'une optimisation qui est utilisée pour l'incrémentation, l'addition et d'autres opérations similaires. Pour l'incrémentation, déterminer la retenue ne demande pas de calculs complexes, contrairement à l'addition.
[[File:Incrémenteur à anticipation de retenues, 8 bits.png|centre|vignette|upright=2.5|Incrémenteur à anticipation de retenues, 8 bits.]]
Pour le comprendre, on peut regarder comment la retenue sortant d'un demi-additionneur est formée. Elle est calculée en faisant un ET logique entre la retenue entrante et le bit d'opérande. Une retenue est donc un ET logique entre toutes les retenues précédentes. Un incrémenteur à anticipation de retenue utilise donc une porte ET à plusieurs entrées pour calculer une retenue.
[[File:Anticipation de retenue pour un bit du résultat, incrémenteur.png|centre|vignette|upright=2.5|Anticipation de retenue pour un bit du résultat, incrémenteur]]
En théorie, on peut utiliser une porte ET à plusieurs entrées pour chaque bit de l'opérande. Cependant, cela entrainera un cout en transistors très important. Pour éviter de gaspiller trop de portes logiques, une solution est de mélanger anticipation de retenues et propagation de retenue. Par exemple, pour un incrémenteur 32 bits, on peut découper l'opérande en 4 octets : on anticipe les retenues pour chaque octet, mais l'incrémentation de chaque octet se fait avec propagation de retenue et/ou ''carry skip''. Plus haut, nous avons vu un cas particulier où les retenues étaient anticipées par blocs de deux bits.
[[File:Incrémenteur à anticipation de retenues.png|centre|vignette|upright=3|Incrémenteur hybride utilisant partiellement l'anticipation de retenues.]]
Quelques processeurs utilisaient l'anticipation de retenues. Par exemple, le processeur Z-80 de Zilog utilisait un incrémenteur pour des nombres de 16 bits, ce qui demandait des performances assez élevées. Et cet incrémenteur utilisait à la fois anticipation de retenues et ''carry skip''. Il était découpé en quatre blocs avant anticipation de retenues entre eux : un bloc regroupant les 7 bits de poids faible, suivi par un bloc de 5 bits, lui-même suivi par un bloc de 3 bits, terminé par un dernier bit isolé. A l'intérieur de ces blocs, les bits sont regroupés en paires utilisant le ''carry skip''. Pour ceux qui veulent en savoir plus sur cet incrémenteur, voici un lien sur le sujet :
* [https://www.righto.com/2013/11/the-z-80s-16-bit-incrementdecrement.html The Z-80's 16-bit increment/decrement circuit reverse engineered].
==Les décrémenteurs==
Les circuits décrémenteurs font l'inverse d'un incrémenteur : au lieu d'ajouter 1 à un opérande, ils lui soustraient 1. La différence n'est pas énorme, ce qui fait que les circuits incrémenteurs et décrémenteurs se ressemblent beaucoup. Là où les incrémenteurs sont fabriqués à partir de demi-additionneurs, les décrémenteurs sont bâtis avec des demi-soustracteurs.
La soustraction se fait en binaire comme en décimal. On soustrait deux chiffres, puis on doit propager une éventuelle retenue sur la colonne suivante. La retenue apparait quand le chiffre soustrait est plus grand que l'autre chiffre. Elle est propagée sur la colonne suivante, où elle doit être soustraite du résultat. Pour le dire autrement, après avoir soustrait un chiffre, on doit de plus soustraire la retenue de la colonne précédente.
La décrémentation effectue le calcul suivant :
<math>a_7</math> <math>a_6</math> <math>a_5</math> <math>a_4</math> <math>a_3</math> <math>a_2</math> <math>a_1</math> <math>a_0</math>
- 0 0 0 0 0 0 0 1
La colonne la plus à droite soustrait un 1. Par contre, les colonnes suivantes soustraient un zéro. Soustraire un zéro ne change rien. Mais il ne faut pas oublier de soustraire une éventuellement retenue, propagée depuis la colonne précédente. En clair, un décrémenteur peut se construire si on sait soustraire deux bits. On soustrait un 1 sur la colonne la plus à droite, on soustrait une retenue sur les autres colonnes. Et pour soustraire deux bits, il faut utiliser un demi-soustracteur.
===Le demi-soustracteur===
Un '''demi-soustracteur''' soustrait deux bits. Pour comprendre comment soustraire deux bits, traitons les quatre cas possibles un par un.
Premièrement, soustraire zéro à un bit ne changera rien :
* 0 - 0 = 0 ;
* 1 - 0 = 1.
Si on soustrait 1 à un bit qui vaut 1, on obtient zéro.
* 1 - 1 = 0.
Maintenant, que se passe-t-il si on soustrait 1 à 0 ? Voici le résultat :
* 0 - 1 = 1 et une retenue propagée sur la colonne suivante, où elle est soustraite.
La table de soustraction est donc la suivante :
* 0 - 0 = 0 ;
* 0 - 1 = 1 et une retenue ;
* 1 - 0 = 1 ;
* 1 - 1 = 0.
La table de vérité est donc la suivante. Détail important : le demi-soustracteur fait ici la soustraction X - Y. En anglais, X est appelé le ''Minuend'' et Y est appelé le ''Subtrahend'', mais ces termes n'ont pas de traduction française. Aussi, je vais les utiliser tels quels dans ce qui suit.
{|class="wikitable"
|-
! ''Minuend'' !! ''Subtrahend'' !! !! Retenue sortante !! Bit de somme
|-
||0||0|| ||0||0
|-
||0||1|| ||1||1
|-
||1||0|| ||0||1
|-
||1||1|| ||0||0
|}
Si on construit la table de vérité, et qu'on construit le circuit associé, on tombe sur ce circuit.
[[File:Half Subtractor Vektor.svg|centre|vignette|Demi-soustracteur.]]
Vous remarquerez que le bit de somme se calcule de la même manière que pour l'addition, seul le calcul de la retenue change. Mais cette différence a une conséquence majeure : les optimisations vues pour le demi-additionneur ne fonctionnent pas pour le demi-soustracteur. Du moins, pas à l'identique. Il y a bien des redondances entre calcul de la retenue et porte XOR, mais ce ne sont pas les mêmes.
Pour rappel, une porte XOR peut s'implémenter avec des portes ET, NON, OU comme ceci :
[[File:XOR als elementaire poorten.png|centre|vignette|upright=1|Porte XOR fabriquée à partir de portes ET/OU/NON.]]
Et vous devriez retrouver le calcul de la retenue juste avant la porte OU.
===Le décrémenteur à propagation de retenue===
Un décrémenteur à propagation de retenue se construit comme un incrémenteur, sauf qu'on remplace les demi-additionneurs par des demi-soustracteurs. En clair, on enchaine plusieurs demi-soustracteurs les uns à la suite des autres.
Il est possible d'utiliser l'anticipation de retenue, mais les circuits sont cependant différents de l'incrémenteur. Idem avec le ''carry skip'', et toute autre optimisation possible. La raison est que les retenues sont calculées différemment, ce qui fait que l’anticipation de retenue doit être adaptée. La retenue d'une colonne ne se calcule plus avec un ET des bits des colonnes précédentes. A la place, ces bits doivent être inversés avant de faire le ET. Le résultat est que la porte ET est remplacée par une porte NOR.
Pour ceux qui s'en rappelle, faire un NOR de N bits permet de vérifier s'ils sont tous à zéro. Et c'est le cas ici. Lors d'une incrémentation, on a une retenue quand tous les bits précédents de l'opérande sont à 1. Pour la décrémentation, c'est l'inverse : on a une retenue quand tous les bits précédents sont à 0.
De fait, décrémenter un nombre est assez simple. Il y a deux cas principaux :
* soit le bit de poids faible est un 1, ce qui signifie que le nombre est impair ;
* soit un ou plusieurs bits de poids faible consécutifs sont à 0, suivis par un 1.
Dans le premier cas, le bit de poids faible est juste inversé. Dans le second cas, le 1 est inversé, et les 0 de poids faible deviennent des 1. Par exemple, Prenez un nombre de la forme xxxxxxx10000. Après décrémentation, le résultat sera xxxxxxxx011111 : les 0 de poids faible deviennent des 1, le premier 1 en partant de la droite est inversé. Et la même logique se généralise pour tout nombre avant un ou plusieurs zéros dans les bits de poids faible.
==Les incrémenteurs-décrémenteurs==
Les circuits décrémenteurs ressemblent beaucoup aux incrémenteurs. Aussi, vous ne serez pas étonné qu'il soit possible de fusionner un incrémenteur et un décrémenteur en un seul circuit. Un tel circuit s'appelle un '''incrémenteur-décrémenteur'''. Un tel circuit a une interface similaire à celle d'un incrémenteur, avec une entrée sur laquelle présenter l'opérande, et une sortie sur laquelle récupérer le résultat. Il a aussi une entrée de retenue, sur laquelle envoyer un 1 (incrémentation) ou un 0 (opération NOP/identité). Et il a aussi une sortie de retenue, qui indique que le résultat du calcul a débordé.
Mais en plus de cela, il dispose d'une entrée de commande qui permet de choisir entre incrémentation et décrémentation.
Il existe plusieurs manières de créer un incrémenteur-décrémenteur, que nous allons voir si dessous. Nous n'allons pas voir le cas le plus évident, aussi ne vous étonnez pas si vous avez remarqué qu'un demi-additionneur et un demi-soustracteur se ressemblent beaucoup.
===L'incrémenteur-décrémenteur basé sur des demi-additionneurs===
Une première implémentation se base sur les propriétés du complément à deux, et plus précisément sur cette équation :
: <math>\overline{x} + 1 = \overline{x - 1}</math>
Les deux termes de cette équation sont simplement des définitions équivalentes du complément à deux d'un nombre entier. Celui-ci se calcule de deux manières : soit on incrémente son complément à 1, soit on prend le complément à 1 de son décrément. Et cela nous permet de déterminer comment obtenir une incrémentation à partir d'une décrémentation et inversement.
Que nous dit cette équation ? Simplement que l'on peut décrémenter un nombre si on a deux inverseurs commandables, et un circuit incrémenteur. La décrémentation se fait alors en deux étapes : on calcule son complément à deux, puis on inverse les bits du résultat précédent. Le calcul du complément à deux demande d'avoir un circuit inverseur, et un incrémenteur.
===L'incrémenteur-décrémenteur basé sur des demi-additionneurs===
Vous l'aurez sans doute remarqué, mais le demi-soustracteur ressemble beaucoup au demi-additionneur. La seule différence est une porte NON ajoutée au bon endroit dans le demi-soustracteur. La porte NON inverse le bit X, sachant que le demi-soustracteur fait la soustraction X - Y. De plus, l'opérande inversée est utilisée seulement pour le calcul des retenues, le bit de somme est calculé normalement.
{|
|[[File:Half-adder.svg|vignette|Demi-additionneur]]
|[[File:Half Subtractor Vektor.svg|vignette|upright=1.3|Demi-soustracteur]]
|}
La conséquence est qu'il est possible de fusionner un demi-additionneur avec un demi-soustracteur. L'idée est de remplacer la porte NON du demi-soustracteur par un inverseur commandable. Si on envoie un zéro sur l'entrée de commande, l'inverseur commandable se comportera comme une porte OUI et le circuit sera alors identique à un demi-additionneur. Si on envoie un 1 sur l'entrée de commande, l'inverseur commandable se comportera comme une porte NON. le circuit sera alors un demi-soustracteur. En remplaçant les demi-additionneurs (ou demi-soustracteurs) par de tels circuits, on peut obtenir un circuit faisant à la fois incrémenteur et décrémenteur.
Un défaut de cette approche est que les optimisations vues plus haut pour les demi-additionneurs ne fonctionne tout simplement plus. On est obligé d'utiliser une porte XOR et une porte ET séparées, pas question d'utiliser les demi-additionneurs optimisés vu au début du chapitre.
<noinclude>
{{NavChapitre | book=Fonctionnement d'un ordinateur
| prev=Les circuits de sélection
| prevText=Les circuits de sélection
| next=Les bascules : des mémoires de 1 bit
| nextText=Les bascules : des mémoires de 1 bit
}}
</noinclude>
5fvo819c1ye01s10ufotsbb72d7qlut
768801
768800
2026-06-27T16:12:40Z
Mewtow
31375
/* Le demi-soustracteur */
768801
wikitext
text/x-wiki
Le circuit '''incrémenteur''' incrémente un nombre entier. Plus précisément, il prend en entrée un nombre entier, et fournit en entier le nombre suivant, il lui ajoute 1. Une telle opération peut paraitre triviale et peu intéressante. Cependant, elle est très utilisée et est absolument cruciale pour créer des compteurs, des circuits capables de compter ou décompter, qui auront droit à un chapitre dédié. De plus, au-delà de leur utilisation dans les compteurs, les incrémenteurs étaient très utilisés sur les premiers processeurs 8 bits, comme le Z-80, le 6502, les premiers processeurs x86 comme le 8008, le 8086, le 8085, et bien d'autres.
Il existe aussi des ''circuits décrémenteurs'', qui décrémentent un opérande, ainsi que des ''circuits incrémenteur-décrémenteur'', qui peuvent incrémenter ou décrémenter, selon comment on les configure. Je ne vais pas détailler ces circuits plus que ça, car de tels circuits sont assez rares, comparé à un circuit incrémenteur simple.
==Le demi-additionneur==
Le circuit incrémenteur effectue l'opération suivante :
<math>a_7</math> <math>a_6</math> <math>a_5</math> <math>a_4</math> <math>a_3</math> <math>a_2</math> <math>a_1</math> <math>a_0</math>
+ 0 0 0 0 0 0 0 1
Un incrémenteur basique doit donc faire une addition pour chaque colonne, et précisément une addition de deux bits. Il se trouve que la table d'addition est très simple en binaire. Jugez plutôt :
* 0 + 0 = 0, retenue = 0 ;
* 0 + 1 = 1, retenue = 0 ;
* 1 + 0 = 1, retenue = 0 ;
* 1 + 1 = 0, retenue = 1.
Un circuit capable d'additionner deux bits est appelé un '''demi-additionneur'''. Il dispose d'une sortie S pour la somme, et C pour la retenue (''carry'' an anglais).
===Le demi-additionneur classique===
Un demi-additionneur est très simple à construire avec les techniques vues dans les premiers chapitres. Voici sa table de vérité :
{|class="wikitable"
|-
! Retenue entrante !! Opérande 1 !! !! Retenue sortante !! Bit de somme
|-
||0||0|| ||0||0
|-
||0||1|| ||0||1
|-
||1||0|| ||0||1
|-
||1||1|| ||1||0
|}
On voit immédiatement que la colonne des retenues donne une porte ET, alors que celle du bit de somme est calculé par un XOR.
{| class="flexible"
|[[File:1-bit half-adder.svg|class=transparent|centre|Demi-addtionneur.]]
|[[File:Half-adder.svg|centre|class=transparent|Circuit d'un demi-addtionneur.]]
|}
Il existe beaucoup d'autres implémentations alternatives, qui utilisent moins de portes logiques, calculent la retenue plus rapidement, consomment moins d'énergie, et autres. Et il est intéressant d'étudier quelques alternatives, surtout qu'elles serviront plus bas, quand on étudiera le circuit incrémenteur du 8085.
===Le demi-additionneur basé sur une modification de la retenue===
Une source d'amélioration est liée à la porte XOR. En pratique, une porte XOR est composée en combinant plusieurs portes logiques ET/OU/NOR/NAND ensemble. Et il se trouve qu'il y a des redondances entre celles-ci et les portes utilisées pour calculer la retenue. Par exemple, rappelons qu'une porte XOR peut être construite avec une porte ET et deux portes NOR, comme illustré ci-dessous. Et il se trouve que la porte ET interne calcule la retenue sortante. En supprimant cette redondance, on économise quelques portes logiques. De plus, on se retrouve avec un demi-additionneur qui calcule le bit de somme à partir de la retenue sortante.
[[File:Demi-additionneur avec redondances éliminées.png|centre|vignette|upright=2|Demi-additionneur avec redondances éliminées]]
Le circuit précédent a une interprétation logique. Si vous regardez la table de vérité, vous remarquerez que la somme de deux bits est égale à l'inverse de la retenue sortante, sauf dans le cas où les deux bits additionnés valent zéro. Et le circuit précédent est basé là-dessus.
{|class="wikitable"
|-
! Retenue entrante !! Opérande 1 !! !! Retenue sortante !! Bit de somme
|- class="f_vert"
||0||0|| ||0||0
|- class="f_rouge"
||0||1|| ||0||1
|- class="f_rouge"
||1||0|| ||0||1
|- class="f_rouge"
||1||1|| ||1||0
|}
L'idée est de calculer l'inverse du bit de somme, avant de l'inverser avec une porte NON. L'inverse du bit de somme vaut 1, soit quand la retenue est à 1, soit quand les deux bits additionnés sont à 0. La porte ET calcule la retenue sortante, la première porte NOR détecte sur les deux bits d'entrée valent zéro. Une porte OU combine les deux résultat pour obtenir l'inverse du bit d'entrée, puis une porte NOn inverse le tout pour obtenir le bit de somme adéquat. Les deux portes sont fusionnées : c'est la seconde porte NOR.
[[File:Full adder HA + MUX.png|centre|vignette|upright=2.5|Demi-additionneur basé sur une inversion de la retenue sortante.]]
===Les implémentations alternatives===
Une implémentation alternative, qui n'utilise que des portes NOR, est la suivante :
[[File:Demi-additionneur fait avec des portes NOR et NAND.png|centre|vignette|upright=2|Demi-additionneur fait avec des portes NOR et NAND.]]
Une implémentation alternative utilise des portes NAND. Pour rappel, il est possible de créer une porte XOR avec 4 portes NAND. La première d'entre elle fait un NAND entre les deux bits d'entrée, ce qui fait qu'elle calcule l'inverse de la retenue sortante. Le tout est illustré ci-dessous. En théorie, on devrait utiliser une porte NON pour récupérer la retenue correcte. Mais nous verrons plus bas que ce n'est pas une obligation. Nous verrons plus bas un exemple où c'est l'inverse de la retenue qui est utilisée dans les calculs, pour rendre les calculs plus rapides.
[[File:Demi-additionneur fait avec des portes NAND.png|centre|vignette|upright=2|Demi-additionneur fait avec des portes NAND]]
==Le circuit incrémenteur==
Maintenant que l'on sait comment additionner deux bits, reprenons l'opération d'incrémentation :
<math>a_7</math> <math>a_6</math> <math>a_5</math> <math>a_4</math> <math>a_3</math> <math>a_2</math> <math>a_1</math> <math>a_0</math>
+ 0 0 0 0 0 0 0 1
------------------------------
Sur la colonne la plus à droite, il doit ajouter un au bit de poids faible. Pour les colonnes suivantes, il faut additionner le bit de l'opérande avec la retenue de la colonne précédente. En clair, on n'additionne que deux bits à chaque colonne : un 1 sur celle tout à droite, la retenue de la colonne précédente pour les autres. Et cela nous donne une idée de comment faire pour créer le circuit incrémenteur.
===L'incrémenteur à propagation de retenue===
Il suffit d'utiliser un demi-additionneur par colonne, et de les enchainer les uns à la suite des autres. Chaque demi-additionneur additionne le bit de l'opérande avec la retenue fournie par le demi-additionneur précédent. Le seul qui fait exception est celui pour la colonne de poids faible. Pour celui-là, il doit ajouter 1 au bit de poids faible. Le résultat est appelé un '''incrémenteur à propagation de retenue'''. Il est constitué de demi-additionneurs enchaînés les uns à la suite des autres, du bit de poids faible vers le bit de poids fort.
[[File:Circuit incrémenteur.png|centre|vignette|upright=3|Circuit incrémenteur.]]
Maintenant, regardons le demi-additionneur le plus à droite, celui pour le bit de poids faible. Son entrée de retenue entrante est mise à 1 pour faire l'incrémentation. Quelques incrémenteurs permettent de configurer cette entrée de retenue à 0 ou à 1, ce qui effectue : soit une opération identité (l'opérande est recopié sur la sortie), soit une incrémentation. Un tel circuit est nommé un '''incrémenteur commandable'''. Nous aurons à utiliser une fois ou deux de tels incrémenteurs commandables dans la suite du cours.
L'incrémenteur à propagation de retenue est le plus simple et le plus économe en portes logiques. Mais de tels incrémenteurs sont rarement utilisés. À la place, on leur préfère des incrémenteurs plus rapides, mais qui utilisent plus de portes logiques. De tels incrémenteurs accélèrent le calcul des retenues. En effet, la rapidité d'une incrémentation est limitée par la propagation de la retenue : les retenues commencent à être calculées au bit de poids fort et on doit les calculer une par une, jusqu’à atteindre le bit de poids fort. Et cette "propagation des retenues" prend du temps, d'autant plus de temps que l'opérande est longue. Il y a deux optimisations principales, appelées le ''carry skip'' et l'anticipation de retenue, que nous allons décrire ci-dessous.
===Les incrémenteurs optimisés===
L'optimisation que nous allns voir effectue l'incrémentation, non pas bit par bit, mais par paquets de deux bits. Le résultat est que l'incrémentation est deux fois plus rapide, ou presque. Le circuit incrémenteur est donc composé en enchainant non pas des demi-additionneurs, mais des '''incrémenteurs 2 bits''' qui incrémentent un opérande de deux bits.
[[File:Circuit incrémenteur optimisé, faisant l'incrémentation par paquet de deux bits.png|centre|vignette|upright=2.5|Circuit incrémenteur optimisé, faisant l'incrémentation par paquet de deux bits]]
Reste à concevoir l'incrémenteur 2 bits. Une première méthode part de deux demi-additionneurs et effectue quelques simplifications au niveau des transistors et des portes logiques. Par exemple, l'Intel 8085 a utilisé un circuit optimisé, fabriqué avec des portes NAND et NOR. La raison est qu'avec les technologies de transistors CMOS actuelles, les portes NAND et NOR utilisent moins de transistors que les portes ET et OU. Et cette possibilité a été utilisée pour effectuer des simplifications assez mineures, mais toujours bonnes à prendre. Mais nous ne pouvons pas en parler en détail ici, car nous n'avons pas encore parlé en détail des transistors. Cependant, un exemple bien précis nous est accesible : celui de l’incrémenteur du processeur 8085 d'Intel.
Le processeur 8085 d'Intel est un processeur 8 bits très ancien. Il contenait un incrémenteur 16 bits, qui était utilisé pour calculer des adresses mémoire. Et cet incrémenteur était un incrémenteur à propagation de retenue optimisé. Il utilisait lui aussi des incrémenteurs 2 bits un peu modifiés.
L'idée était que les retenues sortant des colonnes paires étaient inversées, les colonnes impaires faisaient les calculs à partir de cette retenue inversée. Les demi-additionneurs des colonnes impaires n'étaient donc pas les mêmes que ceux des colonnes paires. Leurs tables de vérité sont différentes, leurs circuits aussi, et aucun des deux ne ressemble à un demi-additionneur normal. En réalité, ils sont fabriqués à partir des implémentations vues plus haut, qui utilisent uniquement des portes NAND ou uniquement des portes NOR.
Pour comprendre l'intérêt de faire ainsi, nous devons préciser une chose importante : avec les technologies CMOS utilisées pour les processeurs depuis les années 70, les portes logiques les plus simples sont les portes NON, NOR et NAND. Les portes ET/OU sont fabriquées en combinant des portes NOR/NAND avec une porte NON, ce qui prend plus de transistors.
Un demi-additionneur est donc fabriqué comme illustré ci-dessous, en logique CMOS. Seules les portes pour le calcul de la retenue sont indiquées, la porte XOR pour le bit de somme est fabriquée à partir de plusieurs portes logiques.
[[File:Demi-additionneur en CMOS.png|centre|vignette|upright=2|Demi-additionneur en CMOS]]
Si on enchaine deux demi-additionneurs, cela donne ceci :
[[File:Brique de base de l'incrémenteur du 8085.png|centre|vignette|upright=2|Brique de base de l'incrémenteur du 8085]]
La retenue doit donc traverser quatre portes logiques, dont deux portes NON. En inversant la retenue, les portes en jaune dans le schéma précédent se simplifient. Il suffit d'utiliser la loi de de Morgan pour trouver un circuit équivalent aux portes jaunes, qui n'est autre qu'une porte NOR couplée à une porte NON. Un autre moyen d'obtenir le même résultat est de prendre un demi-additionneur fabriqué avec uniquement des portes NOR, et de retirer la porte NON adéquate. Il faut ensuite corriger l'entrée de retenue de la porte XOR, pour qu'elle fasse l'addition avec la retenue correctement. Le résultat est illustré ci-dessous.
[[File:Incrémenteur 2 bits de l'incrémenteur du 8085.png|centre|vignette|upright=2|Incrémenteur 2 bits de l'incrémenteur du 8085]]
Le circuit utilise le même nombre de portes logiques, il n'économise pas de transistors. Par contre, la retenue n'a plus qu'à traverser deux portes logiques, au lieu de quatre. La propagation de la retenue est donc plus rapide, l'incrémentation est donc plus rapide.
===Les incrémenteurs 2 bits à calcul séparé de la retenue===
Il existe une autre méthode qui permet de créer des incrémenteurs 2 bits plus rapides que les précédents, mais qui utilisent plus de portes logiques. Ces incrémenteurs 2 bits calculent séparément les deux bits du résultat et la retenue sortante. La retenue sortante est calculée à partir des bits de l’opérande, sans avoir besoin de propager des retenues. Ainsi, la retenue sortante est connue en avance, sans passer par deux demi-additionneurs. Formellement, ils sont un cas particulier d'une optimisation appelée l'''anticipation de retenue''. Si nous verrons le cas général dans la section suivante, ce cas particulier est une très bonne introduction.
Pour créer un tel incrémenteur, le mieux est de partir de sa table de vérité.
{|class="wikitable"
|-
! Opérande !! Retenue entrante !! !! Retenue sortante !! Résultat
|-
| 00 || 0 || || 0 || 00
|-
| 01 || 0 || || 0 || 01
|-
| 10 || 0 || || 0 || 10
|-
| 11 || 0 || || 0 || 11
|-
| colspan="5" |
|-
| 00 || 1 || || 0 || 01
|-
| 01 || 1 || || 0 || 10
|-
| 10 || 1 || || 0 || 11
|-
| 11 || 1 || || 1 || 00
|}
Elle nous dit que la retenue sortante ne vaut 1 que dans un seul cas : les deux bits d'opérande valent 1, la retenue entrante vaut 1. Elle se calcule alors avec une porte ET à trois entrées. Pour les deux bits du résultat, les équations logiques ne donnent pas un résultat satisfaisant. La solution la plus simple est d'utiliser deux demi-additionneurs et de retirer les portes logiques superflues. Le résultat est alors un '''incrémenteur 2 bits ''carry skip''''', où la retenue sortante est calculée séparément du reste.
[[File:Incrémenteur carry skip 2 bits.png|centre|vignette|upright=2.5|Incrémenteur ''carry skip'' 2 bits]]
===Les incrémenteurs à anticipation de retenue===
L''''anticipation de retenue''' calcule la retenue d'une colonne sans attendre les retenues des colonnes précédentes. Dans le cas idéal, toutes les retenues sont calculées en parallèle, en même temps, et sont ensuite envoyées aux demi-additionneurs. Il s'agit d'une optimisation qui est utilisée pour l'incrémentation, l'addition et d'autres opérations similaires. Pour l'incrémentation, déterminer la retenue ne demande pas de calculs complexes, contrairement à l'addition.
[[File:Incrémenteur à anticipation de retenues, 8 bits.png|centre|vignette|upright=2.5|Incrémenteur à anticipation de retenues, 8 bits.]]
Pour le comprendre, on peut regarder comment la retenue sortant d'un demi-additionneur est formée. Elle est calculée en faisant un ET logique entre la retenue entrante et le bit d'opérande. Une retenue est donc un ET logique entre toutes les retenues précédentes. Un incrémenteur à anticipation de retenue utilise donc une porte ET à plusieurs entrées pour calculer une retenue.
[[File:Anticipation de retenue pour un bit du résultat, incrémenteur.png|centre|vignette|upright=2.5|Anticipation de retenue pour un bit du résultat, incrémenteur]]
En théorie, on peut utiliser une porte ET à plusieurs entrées pour chaque bit de l'opérande. Cependant, cela entrainera un cout en transistors très important. Pour éviter de gaspiller trop de portes logiques, une solution est de mélanger anticipation de retenues et propagation de retenue. Par exemple, pour un incrémenteur 32 bits, on peut découper l'opérande en 4 octets : on anticipe les retenues pour chaque octet, mais l'incrémentation de chaque octet se fait avec propagation de retenue et/ou ''carry skip''. Plus haut, nous avons vu un cas particulier où les retenues étaient anticipées par blocs de deux bits.
[[File:Incrémenteur à anticipation de retenues.png|centre|vignette|upright=3|Incrémenteur hybride utilisant partiellement l'anticipation de retenues.]]
Quelques processeurs utilisaient l'anticipation de retenues. Par exemple, le processeur Z-80 de Zilog utilisait un incrémenteur pour des nombres de 16 bits, ce qui demandait des performances assez élevées. Et cet incrémenteur utilisait à la fois anticipation de retenues et ''carry skip''. Il était découpé en quatre blocs avant anticipation de retenues entre eux : un bloc regroupant les 7 bits de poids faible, suivi par un bloc de 5 bits, lui-même suivi par un bloc de 3 bits, terminé par un dernier bit isolé. A l'intérieur de ces blocs, les bits sont regroupés en paires utilisant le ''carry skip''. Pour ceux qui veulent en savoir plus sur cet incrémenteur, voici un lien sur le sujet :
* [https://www.righto.com/2013/11/the-z-80s-16-bit-incrementdecrement.html The Z-80's 16-bit increment/decrement circuit reverse engineered].
==Les décrémenteurs==
Les circuits décrémenteurs font l'inverse d'un incrémenteur : au lieu d'ajouter 1 à un opérande, ils lui soustraient 1. La différence n'est pas énorme, ce qui fait que les circuits incrémenteurs et décrémenteurs se ressemblent beaucoup. Là où les incrémenteurs sont fabriqués à partir de demi-additionneurs, les décrémenteurs sont bâtis avec des demi-soustracteurs.
La soustraction se fait en binaire comme en décimal. On soustrait deux chiffres, puis on doit propager une éventuelle retenue sur la colonne suivante. La retenue apparait quand le chiffre soustrait est plus grand que l'autre chiffre. Elle est propagée sur la colonne suivante, où elle doit être soustraite du résultat. Pour le dire autrement, après avoir soustrait un chiffre, on doit de plus soustraire la retenue de la colonne précédente.
La décrémentation effectue le calcul suivant :
<math>a_7</math> <math>a_6</math> <math>a_5</math> <math>a_4</math> <math>a_3</math> <math>a_2</math> <math>a_1</math> <math>a_0</math>
- 0 0 0 0 0 0 0 1
La colonne la plus à droite soustrait un 1. Par contre, les colonnes suivantes soustraient un zéro. Soustraire un zéro ne change rien. Mais il ne faut pas oublier de soustraire une éventuellement retenue, propagée depuis la colonne précédente. En clair, un décrémenteur peut se construire si on sait soustraire deux bits. On soustrait un 1 sur la colonne la plus à droite, on soustrait une retenue sur les autres colonnes. Et pour soustraire deux bits, il faut utiliser un demi-soustracteur.
===Le demi-soustracteur===
Un '''demi-soustracteur''' soustrait deux bits. Pour comprendre comment soustraire deux bits, traitons les quatre cas possibles un par un.
Premièrement, soustraire zéro à un bit ne changera rien :
* 0 - 0 = 0 ;
* 1 - 0 = 1.
Si on soustrait 1 à un bit qui vaut 1, on obtient zéro.
* 1 - 1 = 0.
Maintenant, que se passe-t-il si on soustrait 1 à 0 ? Voici le résultat :
* 0 - 1 = 1 et une retenue propagée sur la colonne suivante, où elle est soustraite.
La table de soustraction est donc la suivante :
* 0 - 0 = 0 ;
* 0 - 1 = 1 et une retenue ;
* 1 - 0 = 1 ;
* 1 - 1 = 0.
La table de vérité est donc la suivante. Détail important : le demi-soustracteur fait ici la soustraction X - Y. En anglais, X est appelé le ''Minuend'' et Y est appelé le ''Subtrahend'', mais ces termes n'ont pas de traduction française. Aussi, je vais les utiliser tels quels dans ce qui suit.
{|class="wikitable"
|-
! ''Minuend'' !! ''Subtrahend'' !! !! Retenue sortante !! Bit de somme
|-
||0||0|| ||0||0
|-
||0||1|| ||1||1
|-
||1||0|| ||0||1
|-
||1||1|| ||0||0
|}
Si on construit la table de vérité, et qu'on construit le circuit associé, on tombe sur ce circuit.
[[File:Half Subtractor Vektor.svg|centre|vignette|Demi-soustracteur.]]
Vous remarquerez que le bit de somme se calcule de la même manière que pour l'addition, seul le calcul de la retenue change. Mais cette différence a une conséquence majeure : les optimisations vues pour le demi-additionneur ne fonctionnent pas pour le demi-soustracteur. Du moins, pas à l'identique. Il y a bien des redondances entre calcul de la retenue et porte XOR, mais ce ne sont pas les mêmes.
Pour rappel, une porte XOR peut s'implémenter avec des portes ET, NON, OU comme ceci :
[[File:XOR als elementaire poorten.png|centre|vignette|upright=1|Porte XOR fabriquée à partir de portes ET/OU/NON.]]
Et vous devriez retrouver le calcul de la retenue juste avant la porte OU.
[[File:Demi-soustracteur optimisé, sans redondances.png|centre|vignette|upright=1.5|Demi-soustracteur optimisé, sans redondances]]
===Le décrémenteur à propagation de retenue===
Un décrémenteur à propagation de retenue se construit comme un incrémenteur, sauf qu'on remplace les demi-additionneurs par des demi-soustracteurs. En clair, on enchaine plusieurs demi-soustracteurs les uns à la suite des autres.
Il est possible d'utiliser l'anticipation de retenue, mais les circuits sont cependant différents de l'incrémenteur. Idem avec le ''carry skip'', et toute autre optimisation possible. La raison est que les retenues sont calculées différemment, ce qui fait que l’anticipation de retenue doit être adaptée. La retenue d'une colonne ne se calcule plus avec un ET des bits des colonnes précédentes. A la place, ces bits doivent être inversés avant de faire le ET. Le résultat est que la porte ET est remplacée par une porte NOR.
Pour ceux qui s'en rappelle, faire un NOR de N bits permet de vérifier s'ils sont tous à zéro. Et c'est le cas ici. Lors d'une incrémentation, on a une retenue quand tous les bits précédents de l'opérande sont à 1. Pour la décrémentation, c'est l'inverse : on a une retenue quand tous les bits précédents sont à 0.
De fait, décrémenter un nombre est assez simple. Il y a deux cas principaux :
* soit le bit de poids faible est un 1, ce qui signifie que le nombre est impair ;
* soit un ou plusieurs bits de poids faible consécutifs sont à 0, suivis par un 1.
Dans le premier cas, le bit de poids faible est juste inversé. Dans le second cas, le 1 est inversé, et les 0 de poids faible deviennent des 1. Par exemple, Prenez un nombre de la forme xxxxxxx10000. Après décrémentation, le résultat sera xxxxxxxx011111 : les 0 de poids faible deviennent des 1, le premier 1 en partant de la droite est inversé. Et la même logique se généralise pour tout nombre avant un ou plusieurs zéros dans les bits de poids faible.
==Les incrémenteurs-décrémenteurs==
Les circuits décrémenteurs ressemblent beaucoup aux incrémenteurs. Aussi, vous ne serez pas étonné qu'il soit possible de fusionner un incrémenteur et un décrémenteur en un seul circuit. Un tel circuit s'appelle un '''incrémenteur-décrémenteur'''. Un tel circuit a une interface similaire à celle d'un incrémenteur, avec une entrée sur laquelle présenter l'opérande, et une sortie sur laquelle récupérer le résultat. Il a aussi une entrée de retenue, sur laquelle envoyer un 1 (incrémentation) ou un 0 (opération NOP/identité). Et il a aussi une sortie de retenue, qui indique que le résultat du calcul a débordé.
Mais en plus de cela, il dispose d'une entrée de commande qui permet de choisir entre incrémentation et décrémentation.
Il existe plusieurs manières de créer un incrémenteur-décrémenteur, que nous allons voir si dessous. Nous n'allons pas voir le cas le plus évident, aussi ne vous étonnez pas si vous avez remarqué qu'un demi-additionneur et un demi-soustracteur se ressemblent beaucoup.
===L'incrémenteur-décrémenteur basé sur des demi-additionneurs===
Une première implémentation se base sur les propriétés du complément à deux, et plus précisément sur cette équation :
: <math>\overline{x} + 1 = \overline{x - 1}</math>
Les deux termes de cette équation sont simplement des définitions équivalentes du complément à deux d'un nombre entier. Celui-ci se calcule de deux manières : soit on incrémente son complément à 1, soit on prend le complément à 1 de son décrément. Et cela nous permet de déterminer comment obtenir une incrémentation à partir d'une décrémentation et inversement.
Que nous dit cette équation ? Simplement que l'on peut décrémenter un nombre si on a deux inverseurs commandables, et un circuit incrémenteur. La décrémentation se fait alors en deux étapes : on calcule son complément à deux, puis on inverse les bits du résultat précédent. Le calcul du complément à deux demande d'avoir un circuit inverseur, et un incrémenteur.
===L'incrémenteur-décrémenteur basé sur des demi-additionneurs===
Vous l'aurez sans doute remarqué, mais le demi-soustracteur ressemble beaucoup au demi-additionneur. La seule différence est une porte NON ajoutée au bon endroit dans le demi-soustracteur. La porte NON inverse le bit X, sachant que le demi-soustracteur fait la soustraction X - Y. De plus, l'opérande inversée est utilisée seulement pour le calcul des retenues, le bit de somme est calculé normalement.
{|
|[[File:Half-adder.svg|vignette|Demi-additionneur]]
|[[File:Half Subtractor Vektor.svg|vignette|upright=1.3|Demi-soustracteur]]
|}
La conséquence est qu'il est possible de fusionner un demi-additionneur avec un demi-soustracteur. L'idée est de remplacer la porte NON du demi-soustracteur par un inverseur commandable. Si on envoie un zéro sur l'entrée de commande, l'inverseur commandable se comportera comme une porte OUI et le circuit sera alors identique à un demi-additionneur. Si on envoie un 1 sur l'entrée de commande, l'inverseur commandable se comportera comme une porte NON. le circuit sera alors un demi-soustracteur. En remplaçant les demi-additionneurs (ou demi-soustracteurs) par de tels circuits, on peut obtenir un circuit faisant à la fois incrémenteur et décrémenteur.
Un défaut de cette approche est que les optimisations vues plus haut pour les demi-additionneurs ne fonctionne tout simplement plus. On est obligé d'utiliser une porte XOR et une porte ET séparées, pas question d'utiliser les demi-additionneurs optimisés vu au début du chapitre.
<noinclude>
{{NavChapitre | book=Fonctionnement d'un ordinateur
| prev=Les circuits de sélection
| prevText=Les circuits de sélection
| next=Les bascules : des mémoires de 1 bit
| nextText=Les bascules : des mémoires de 1 bit
}}
</noinclude>
fwv6ygxl21hp423rei7o292irumtx6z
768802
768801
2026-06-27T16:14:07Z
Mewtow
31375
/* Le demi-soustracteur */
768802
wikitext
text/x-wiki
Le circuit '''incrémenteur''' incrémente un nombre entier. Plus précisément, il prend en entrée un nombre entier, et fournit en entier le nombre suivant, il lui ajoute 1. Une telle opération peut paraitre triviale et peu intéressante. Cependant, elle est très utilisée et est absolument cruciale pour créer des compteurs, des circuits capables de compter ou décompter, qui auront droit à un chapitre dédié. De plus, au-delà de leur utilisation dans les compteurs, les incrémenteurs étaient très utilisés sur les premiers processeurs 8 bits, comme le Z-80, le 6502, les premiers processeurs x86 comme le 8008, le 8086, le 8085, et bien d'autres.
Il existe aussi des ''circuits décrémenteurs'', qui décrémentent un opérande, ainsi que des ''circuits incrémenteur-décrémenteur'', qui peuvent incrémenter ou décrémenter, selon comment on les configure. Je ne vais pas détailler ces circuits plus que ça, car de tels circuits sont assez rares, comparé à un circuit incrémenteur simple.
==Le demi-additionneur==
Le circuit incrémenteur effectue l'opération suivante :
<math>a_7</math> <math>a_6</math> <math>a_5</math> <math>a_4</math> <math>a_3</math> <math>a_2</math> <math>a_1</math> <math>a_0</math>
+ 0 0 0 0 0 0 0 1
Un incrémenteur basique doit donc faire une addition pour chaque colonne, et précisément une addition de deux bits. Il se trouve que la table d'addition est très simple en binaire. Jugez plutôt :
* 0 + 0 = 0, retenue = 0 ;
* 0 + 1 = 1, retenue = 0 ;
* 1 + 0 = 1, retenue = 0 ;
* 1 + 1 = 0, retenue = 1.
Un circuit capable d'additionner deux bits est appelé un '''demi-additionneur'''. Il dispose d'une sortie S pour la somme, et C pour la retenue (''carry'' an anglais).
===Le demi-additionneur classique===
Un demi-additionneur est très simple à construire avec les techniques vues dans les premiers chapitres. Voici sa table de vérité :
{|class="wikitable"
|-
! Retenue entrante !! Opérande 1 !! !! Retenue sortante !! Bit de somme
|-
||0||0|| ||0||0
|-
||0||1|| ||0||1
|-
||1||0|| ||0||1
|-
||1||1|| ||1||0
|}
On voit immédiatement que la colonne des retenues donne une porte ET, alors que celle du bit de somme est calculé par un XOR.
{| class="flexible"
|[[File:1-bit half-adder.svg|class=transparent|centre|Demi-addtionneur.]]
|[[File:Half-adder.svg|centre|class=transparent|Circuit d'un demi-addtionneur.]]
|}
Il existe beaucoup d'autres implémentations alternatives, qui utilisent moins de portes logiques, calculent la retenue plus rapidement, consomment moins d'énergie, et autres. Et il est intéressant d'étudier quelques alternatives, surtout qu'elles serviront plus bas, quand on étudiera le circuit incrémenteur du 8085.
===Le demi-additionneur basé sur une modification de la retenue===
Une source d'amélioration est liée à la porte XOR. En pratique, une porte XOR est composée en combinant plusieurs portes logiques ET/OU/NOR/NAND ensemble. Et il se trouve qu'il y a des redondances entre celles-ci et les portes utilisées pour calculer la retenue. Par exemple, rappelons qu'une porte XOR peut être construite avec une porte ET et deux portes NOR, comme illustré ci-dessous. Et il se trouve que la porte ET interne calcule la retenue sortante. En supprimant cette redondance, on économise quelques portes logiques. De plus, on se retrouve avec un demi-additionneur qui calcule le bit de somme à partir de la retenue sortante.
[[File:Demi-additionneur avec redondances éliminées.png|centre|vignette|upright=2|Demi-additionneur avec redondances éliminées]]
Le circuit précédent a une interprétation logique. Si vous regardez la table de vérité, vous remarquerez que la somme de deux bits est égale à l'inverse de la retenue sortante, sauf dans le cas où les deux bits additionnés valent zéro. Et le circuit précédent est basé là-dessus.
{|class="wikitable"
|-
! Retenue entrante !! Opérande 1 !! !! Retenue sortante !! Bit de somme
|- class="f_vert"
||0||0|| ||0||0
|- class="f_rouge"
||0||1|| ||0||1
|- class="f_rouge"
||1||0|| ||0||1
|- class="f_rouge"
||1||1|| ||1||0
|}
L'idée est de calculer l'inverse du bit de somme, avant de l'inverser avec une porte NON. L'inverse du bit de somme vaut 1, soit quand la retenue est à 1, soit quand les deux bits additionnés sont à 0. La porte ET calcule la retenue sortante, la première porte NOR détecte sur les deux bits d'entrée valent zéro. Une porte OU combine les deux résultat pour obtenir l'inverse du bit d'entrée, puis une porte NOn inverse le tout pour obtenir le bit de somme adéquat. Les deux portes sont fusionnées : c'est la seconde porte NOR.
[[File:Full adder HA + MUX.png|centre|vignette|upright=2.5|Demi-additionneur basé sur une inversion de la retenue sortante.]]
===Les implémentations alternatives===
Une implémentation alternative, qui n'utilise que des portes NOR, est la suivante :
[[File:Demi-additionneur fait avec des portes NOR et NAND.png|centre|vignette|upright=2|Demi-additionneur fait avec des portes NOR et NAND.]]
Une implémentation alternative utilise des portes NAND. Pour rappel, il est possible de créer une porte XOR avec 4 portes NAND. La première d'entre elle fait un NAND entre les deux bits d'entrée, ce qui fait qu'elle calcule l'inverse de la retenue sortante. Le tout est illustré ci-dessous. En théorie, on devrait utiliser une porte NON pour récupérer la retenue correcte. Mais nous verrons plus bas que ce n'est pas une obligation. Nous verrons plus bas un exemple où c'est l'inverse de la retenue qui est utilisée dans les calculs, pour rendre les calculs plus rapides.
[[File:Demi-additionneur fait avec des portes NAND.png|centre|vignette|upright=2|Demi-additionneur fait avec des portes NAND]]
==Le circuit incrémenteur==
Maintenant que l'on sait comment additionner deux bits, reprenons l'opération d'incrémentation :
<math>a_7</math> <math>a_6</math> <math>a_5</math> <math>a_4</math> <math>a_3</math> <math>a_2</math> <math>a_1</math> <math>a_0</math>
+ 0 0 0 0 0 0 0 1
------------------------------
Sur la colonne la plus à droite, il doit ajouter un au bit de poids faible. Pour les colonnes suivantes, il faut additionner le bit de l'opérande avec la retenue de la colonne précédente. En clair, on n'additionne que deux bits à chaque colonne : un 1 sur celle tout à droite, la retenue de la colonne précédente pour les autres. Et cela nous donne une idée de comment faire pour créer le circuit incrémenteur.
===L'incrémenteur à propagation de retenue===
Il suffit d'utiliser un demi-additionneur par colonne, et de les enchainer les uns à la suite des autres. Chaque demi-additionneur additionne le bit de l'opérande avec la retenue fournie par le demi-additionneur précédent. Le seul qui fait exception est celui pour la colonne de poids faible. Pour celui-là, il doit ajouter 1 au bit de poids faible. Le résultat est appelé un '''incrémenteur à propagation de retenue'''. Il est constitué de demi-additionneurs enchaînés les uns à la suite des autres, du bit de poids faible vers le bit de poids fort.
[[File:Circuit incrémenteur.png|centre|vignette|upright=3|Circuit incrémenteur.]]
Maintenant, regardons le demi-additionneur le plus à droite, celui pour le bit de poids faible. Son entrée de retenue entrante est mise à 1 pour faire l'incrémentation. Quelques incrémenteurs permettent de configurer cette entrée de retenue à 0 ou à 1, ce qui effectue : soit une opération identité (l'opérande est recopié sur la sortie), soit une incrémentation. Un tel circuit est nommé un '''incrémenteur commandable'''. Nous aurons à utiliser une fois ou deux de tels incrémenteurs commandables dans la suite du cours.
L'incrémenteur à propagation de retenue est le plus simple et le plus économe en portes logiques. Mais de tels incrémenteurs sont rarement utilisés. À la place, on leur préfère des incrémenteurs plus rapides, mais qui utilisent plus de portes logiques. De tels incrémenteurs accélèrent le calcul des retenues. En effet, la rapidité d'une incrémentation est limitée par la propagation de la retenue : les retenues commencent à être calculées au bit de poids fort et on doit les calculer une par une, jusqu’à atteindre le bit de poids fort. Et cette "propagation des retenues" prend du temps, d'autant plus de temps que l'opérande est longue. Il y a deux optimisations principales, appelées le ''carry skip'' et l'anticipation de retenue, que nous allons décrire ci-dessous.
===Les incrémenteurs optimisés===
L'optimisation que nous allns voir effectue l'incrémentation, non pas bit par bit, mais par paquets de deux bits. Le résultat est que l'incrémentation est deux fois plus rapide, ou presque. Le circuit incrémenteur est donc composé en enchainant non pas des demi-additionneurs, mais des '''incrémenteurs 2 bits''' qui incrémentent un opérande de deux bits.
[[File:Circuit incrémenteur optimisé, faisant l'incrémentation par paquet de deux bits.png|centre|vignette|upright=2.5|Circuit incrémenteur optimisé, faisant l'incrémentation par paquet de deux bits]]
Reste à concevoir l'incrémenteur 2 bits. Une première méthode part de deux demi-additionneurs et effectue quelques simplifications au niveau des transistors et des portes logiques. Par exemple, l'Intel 8085 a utilisé un circuit optimisé, fabriqué avec des portes NAND et NOR. La raison est qu'avec les technologies de transistors CMOS actuelles, les portes NAND et NOR utilisent moins de transistors que les portes ET et OU. Et cette possibilité a été utilisée pour effectuer des simplifications assez mineures, mais toujours bonnes à prendre. Mais nous ne pouvons pas en parler en détail ici, car nous n'avons pas encore parlé en détail des transistors. Cependant, un exemple bien précis nous est accesible : celui de l’incrémenteur du processeur 8085 d'Intel.
Le processeur 8085 d'Intel est un processeur 8 bits très ancien. Il contenait un incrémenteur 16 bits, qui était utilisé pour calculer des adresses mémoire. Et cet incrémenteur était un incrémenteur à propagation de retenue optimisé. Il utilisait lui aussi des incrémenteurs 2 bits un peu modifiés.
L'idée était que les retenues sortant des colonnes paires étaient inversées, les colonnes impaires faisaient les calculs à partir de cette retenue inversée. Les demi-additionneurs des colonnes impaires n'étaient donc pas les mêmes que ceux des colonnes paires. Leurs tables de vérité sont différentes, leurs circuits aussi, et aucun des deux ne ressemble à un demi-additionneur normal. En réalité, ils sont fabriqués à partir des implémentations vues plus haut, qui utilisent uniquement des portes NAND ou uniquement des portes NOR.
Pour comprendre l'intérêt de faire ainsi, nous devons préciser une chose importante : avec les technologies CMOS utilisées pour les processeurs depuis les années 70, les portes logiques les plus simples sont les portes NON, NOR et NAND. Les portes ET/OU sont fabriquées en combinant des portes NOR/NAND avec une porte NON, ce qui prend plus de transistors.
Un demi-additionneur est donc fabriqué comme illustré ci-dessous, en logique CMOS. Seules les portes pour le calcul de la retenue sont indiquées, la porte XOR pour le bit de somme est fabriquée à partir de plusieurs portes logiques.
[[File:Demi-additionneur en CMOS.png|centre|vignette|upright=2|Demi-additionneur en CMOS]]
Si on enchaine deux demi-additionneurs, cela donne ceci :
[[File:Brique de base de l'incrémenteur du 8085.png|centre|vignette|upright=2|Brique de base de l'incrémenteur du 8085]]
La retenue doit donc traverser quatre portes logiques, dont deux portes NON. En inversant la retenue, les portes en jaune dans le schéma précédent se simplifient. Il suffit d'utiliser la loi de de Morgan pour trouver un circuit équivalent aux portes jaunes, qui n'est autre qu'une porte NOR couplée à une porte NON. Un autre moyen d'obtenir le même résultat est de prendre un demi-additionneur fabriqué avec uniquement des portes NOR, et de retirer la porte NON adéquate. Il faut ensuite corriger l'entrée de retenue de la porte XOR, pour qu'elle fasse l'addition avec la retenue correctement. Le résultat est illustré ci-dessous.
[[File:Incrémenteur 2 bits de l'incrémenteur du 8085.png|centre|vignette|upright=2|Incrémenteur 2 bits de l'incrémenteur du 8085]]
Le circuit utilise le même nombre de portes logiques, il n'économise pas de transistors. Par contre, la retenue n'a plus qu'à traverser deux portes logiques, au lieu de quatre. La propagation de la retenue est donc plus rapide, l'incrémentation est donc plus rapide.
===Les incrémenteurs 2 bits à calcul séparé de la retenue===
Il existe une autre méthode qui permet de créer des incrémenteurs 2 bits plus rapides que les précédents, mais qui utilisent plus de portes logiques. Ces incrémenteurs 2 bits calculent séparément les deux bits du résultat et la retenue sortante. La retenue sortante est calculée à partir des bits de l’opérande, sans avoir besoin de propager des retenues. Ainsi, la retenue sortante est connue en avance, sans passer par deux demi-additionneurs. Formellement, ils sont un cas particulier d'une optimisation appelée l'''anticipation de retenue''. Si nous verrons le cas général dans la section suivante, ce cas particulier est une très bonne introduction.
Pour créer un tel incrémenteur, le mieux est de partir de sa table de vérité.
{|class="wikitable"
|-
! Opérande !! Retenue entrante !! !! Retenue sortante !! Résultat
|-
| 00 || 0 || || 0 || 00
|-
| 01 || 0 || || 0 || 01
|-
| 10 || 0 || || 0 || 10
|-
| 11 || 0 || || 0 || 11
|-
| colspan="5" |
|-
| 00 || 1 || || 0 || 01
|-
| 01 || 1 || || 0 || 10
|-
| 10 || 1 || || 0 || 11
|-
| 11 || 1 || || 1 || 00
|}
Elle nous dit que la retenue sortante ne vaut 1 que dans un seul cas : les deux bits d'opérande valent 1, la retenue entrante vaut 1. Elle se calcule alors avec une porte ET à trois entrées. Pour les deux bits du résultat, les équations logiques ne donnent pas un résultat satisfaisant. La solution la plus simple est d'utiliser deux demi-additionneurs et de retirer les portes logiques superflues. Le résultat est alors un '''incrémenteur 2 bits ''carry skip''''', où la retenue sortante est calculée séparément du reste.
[[File:Incrémenteur carry skip 2 bits.png|centre|vignette|upright=2.5|Incrémenteur ''carry skip'' 2 bits]]
===Les incrémenteurs à anticipation de retenue===
L''''anticipation de retenue''' calcule la retenue d'une colonne sans attendre les retenues des colonnes précédentes. Dans le cas idéal, toutes les retenues sont calculées en parallèle, en même temps, et sont ensuite envoyées aux demi-additionneurs. Il s'agit d'une optimisation qui est utilisée pour l'incrémentation, l'addition et d'autres opérations similaires. Pour l'incrémentation, déterminer la retenue ne demande pas de calculs complexes, contrairement à l'addition.
[[File:Incrémenteur à anticipation de retenues, 8 bits.png|centre|vignette|upright=2.5|Incrémenteur à anticipation de retenues, 8 bits.]]
Pour le comprendre, on peut regarder comment la retenue sortant d'un demi-additionneur est formée. Elle est calculée en faisant un ET logique entre la retenue entrante et le bit d'opérande. Une retenue est donc un ET logique entre toutes les retenues précédentes. Un incrémenteur à anticipation de retenue utilise donc une porte ET à plusieurs entrées pour calculer une retenue.
[[File:Anticipation de retenue pour un bit du résultat, incrémenteur.png|centre|vignette|upright=2.5|Anticipation de retenue pour un bit du résultat, incrémenteur]]
En théorie, on peut utiliser une porte ET à plusieurs entrées pour chaque bit de l'opérande. Cependant, cela entrainera un cout en transistors très important. Pour éviter de gaspiller trop de portes logiques, une solution est de mélanger anticipation de retenues et propagation de retenue. Par exemple, pour un incrémenteur 32 bits, on peut découper l'opérande en 4 octets : on anticipe les retenues pour chaque octet, mais l'incrémentation de chaque octet se fait avec propagation de retenue et/ou ''carry skip''. Plus haut, nous avons vu un cas particulier où les retenues étaient anticipées par blocs de deux bits.
[[File:Incrémenteur à anticipation de retenues.png|centre|vignette|upright=3|Incrémenteur hybride utilisant partiellement l'anticipation de retenues.]]
Quelques processeurs utilisaient l'anticipation de retenues. Par exemple, le processeur Z-80 de Zilog utilisait un incrémenteur pour des nombres de 16 bits, ce qui demandait des performances assez élevées. Et cet incrémenteur utilisait à la fois anticipation de retenues et ''carry skip''. Il était découpé en quatre blocs avant anticipation de retenues entre eux : un bloc regroupant les 7 bits de poids faible, suivi par un bloc de 5 bits, lui-même suivi par un bloc de 3 bits, terminé par un dernier bit isolé. A l'intérieur de ces blocs, les bits sont regroupés en paires utilisant le ''carry skip''. Pour ceux qui veulent en savoir plus sur cet incrémenteur, voici un lien sur le sujet :
* [https://www.righto.com/2013/11/the-z-80s-16-bit-incrementdecrement.html The Z-80's 16-bit increment/decrement circuit reverse engineered].
==Les décrémenteurs==
Les circuits décrémenteurs font l'inverse d'un incrémenteur : au lieu d'ajouter 1 à un opérande, ils lui soustraient 1. La différence n'est pas énorme, ce qui fait que les circuits incrémenteurs et décrémenteurs se ressemblent beaucoup. Là où les incrémenteurs sont fabriqués à partir de demi-additionneurs, les décrémenteurs sont bâtis avec des demi-soustracteurs.
La soustraction se fait en binaire comme en décimal. On soustrait deux chiffres, puis on doit propager une éventuelle retenue sur la colonne suivante. La retenue apparait quand le chiffre soustrait est plus grand que l'autre chiffre. Elle est propagée sur la colonne suivante, où elle doit être soustraite du résultat. Pour le dire autrement, après avoir soustrait un chiffre, on doit de plus soustraire la retenue de la colonne précédente.
La décrémentation effectue le calcul suivant :
<math>a_7</math> <math>a_6</math> <math>a_5</math> <math>a_4</math> <math>a_3</math> <math>a_2</math> <math>a_1</math> <math>a_0</math>
- 0 0 0 0 0 0 0 1
La colonne la plus à droite soustrait un 1. Par contre, les colonnes suivantes soustraient un zéro. Soustraire un zéro ne change rien. Mais il ne faut pas oublier de soustraire une éventuellement retenue, propagée depuis la colonne précédente. En clair, un décrémenteur peut se construire si on sait soustraire deux bits. On soustrait un 1 sur la colonne la plus à droite, on soustrait une retenue sur les autres colonnes. Et pour soustraire deux bits, il faut utiliser un demi-soustracteur.
===Le demi-soustracteur===
Un '''demi-soustracteur''' soustrait deux bits. Pour comprendre comment soustraire deux bits, traitons les quatre cas possibles un par un.
Premièrement, soustraire zéro à un bit ne changera rien :
* 0 - 0 = 0 ;
* 1 - 0 = 1.
Si on soustrait 1 à un bit qui vaut 1, on obtient zéro.
* 1 - 1 = 0.
Maintenant, que se passe-t-il si on soustrait 1 à 0 ? Voici le résultat :
* 0 - 1 = 1 et une retenue propagée sur la colonne suivante, où elle est soustraite.
La table de soustraction est donc la suivante :
* 0 - 0 = 0 ;
* 0 - 1 = 1 et une retenue ;
* 1 - 0 = 1 ;
* 1 - 1 = 0.
La table de vérité est donc la suivante. Détail important : le demi-soustracteur fait ici la soustraction X - Y. En anglais, X est appelé le ''Minuend'' et Y est appelé le ''Subtrahend'', mais ces termes n'ont pas de traduction française. Aussi, je vais les utiliser tels quels dans ce qui suit.
{|class="wikitable"
|-
! ''Minuend'' !! ''Subtrahend'' !! !! Retenue sortante !! Bit de somme
|-
||0||0|| ||0||0
|-
||0||1|| ||1||1
|-
||1||0|| ||0||1
|-
||1||1|| ||0||0
|}
Si on construit la table de vérité, et qu'on construit le circuit associé, on tombe sur ce circuit.
[[File:Half Subtractor Vektor.svg|centre|vignette|Demi-soustracteur.]]
Vous remarquerez que le bit de somme se calcule de la même manière que pour l'addition, seul le calcul de la retenue change. Comme pour le demi-additionneur, on peut exploiter des redondances entre calcul de la retenue et porte XOR, mais ce ne sont pas les mêmes. Le circuit obtenu après exploitation de ces redondances est le suivant :
[[File:Demi-soustracteur optimisé, sans redondances.png|centre|vignette|upright=1.5|Demi-soustracteur optimisé, sans redondances]]
===Le décrémenteur à propagation de retenue===
Un décrémenteur à propagation de retenue se construit comme un incrémenteur, sauf qu'on remplace les demi-additionneurs par des demi-soustracteurs. En clair, on enchaine plusieurs demi-soustracteurs les uns à la suite des autres.
Il est possible d'utiliser l'anticipation de retenue, mais les circuits sont cependant différents de l'incrémenteur. Idem avec le ''carry skip'', et toute autre optimisation possible. La raison est que les retenues sont calculées différemment, ce qui fait que l’anticipation de retenue doit être adaptée. La retenue d'une colonne ne se calcule plus avec un ET des bits des colonnes précédentes. A la place, ces bits doivent être inversés avant de faire le ET. Le résultat est que la porte ET est remplacée par une porte NOR.
Pour ceux qui s'en rappelle, faire un NOR de N bits permet de vérifier s'ils sont tous à zéro. Et c'est le cas ici. Lors d'une incrémentation, on a une retenue quand tous les bits précédents de l'opérande sont à 1. Pour la décrémentation, c'est l'inverse : on a une retenue quand tous les bits précédents sont à 0.
De fait, décrémenter un nombre est assez simple. Il y a deux cas principaux :
* soit le bit de poids faible est un 1, ce qui signifie que le nombre est impair ;
* soit un ou plusieurs bits de poids faible consécutifs sont à 0, suivis par un 1.
Dans le premier cas, le bit de poids faible est juste inversé. Dans le second cas, le 1 est inversé, et les 0 de poids faible deviennent des 1. Par exemple, Prenez un nombre de la forme xxxxxxx10000. Après décrémentation, le résultat sera xxxxxxxx011111 : les 0 de poids faible deviennent des 1, le premier 1 en partant de la droite est inversé. Et la même logique se généralise pour tout nombre avant un ou plusieurs zéros dans les bits de poids faible.
==Les incrémenteurs-décrémenteurs==
Les circuits décrémenteurs ressemblent beaucoup aux incrémenteurs. Aussi, vous ne serez pas étonné qu'il soit possible de fusionner un incrémenteur et un décrémenteur en un seul circuit. Un tel circuit s'appelle un '''incrémenteur-décrémenteur'''. Un tel circuit a une interface similaire à celle d'un incrémenteur, avec une entrée sur laquelle présenter l'opérande, et une sortie sur laquelle récupérer le résultat. Il a aussi une entrée de retenue, sur laquelle envoyer un 1 (incrémentation) ou un 0 (opération NOP/identité). Et il a aussi une sortie de retenue, qui indique que le résultat du calcul a débordé.
Mais en plus de cela, il dispose d'une entrée de commande qui permet de choisir entre incrémentation et décrémentation.
Il existe plusieurs manières de créer un incrémenteur-décrémenteur, que nous allons voir si dessous. Nous n'allons pas voir le cas le plus évident, aussi ne vous étonnez pas si vous avez remarqué qu'un demi-additionneur et un demi-soustracteur se ressemblent beaucoup.
===L'incrémenteur-décrémenteur basé sur des demi-additionneurs===
Une première implémentation se base sur les propriétés du complément à deux, et plus précisément sur cette équation :
: <math>\overline{x} + 1 = \overline{x - 1}</math>
Les deux termes de cette équation sont simplement des définitions équivalentes du complément à deux d'un nombre entier. Celui-ci se calcule de deux manières : soit on incrémente son complément à 1, soit on prend le complément à 1 de son décrément. Et cela nous permet de déterminer comment obtenir une incrémentation à partir d'une décrémentation et inversement.
Que nous dit cette équation ? Simplement que l'on peut décrémenter un nombre si on a deux inverseurs commandables, et un circuit incrémenteur. La décrémentation se fait alors en deux étapes : on calcule son complément à deux, puis on inverse les bits du résultat précédent. Le calcul du complément à deux demande d'avoir un circuit inverseur, et un incrémenteur.
===L'incrémenteur-décrémenteur basé sur des demi-additionneurs===
Vous l'aurez sans doute remarqué, mais le demi-soustracteur ressemble beaucoup au demi-additionneur. La seule différence est une porte NON ajoutée au bon endroit dans le demi-soustracteur. La porte NON inverse le bit X, sachant que le demi-soustracteur fait la soustraction X - Y. De plus, l'opérande inversée est utilisée seulement pour le calcul des retenues, le bit de somme est calculé normalement.
{|
|[[File:Half-adder.svg|vignette|Demi-additionneur]]
|[[File:Half Subtractor Vektor.svg|vignette|upright=1.3|Demi-soustracteur]]
|}
La conséquence est qu'il est possible de fusionner un demi-additionneur avec un demi-soustracteur. L'idée est de remplacer la porte NON du demi-soustracteur par un inverseur commandable. Si on envoie un zéro sur l'entrée de commande, l'inverseur commandable se comportera comme une porte OUI et le circuit sera alors identique à un demi-additionneur. Si on envoie un 1 sur l'entrée de commande, l'inverseur commandable se comportera comme une porte NON. le circuit sera alors un demi-soustracteur. En remplaçant les demi-additionneurs (ou demi-soustracteurs) par de tels circuits, on peut obtenir un circuit faisant à la fois incrémenteur et décrémenteur.
Un défaut de cette approche est que les optimisations vues plus haut pour les demi-additionneurs ne fonctionne tout simplement plus. On est obligé d'utiliser une porte XOR et une porte ET séparées, pas question d'utiliser les demi-additionneurs optimisés vu au début du chapitre.
<noinclude>
{{NavChapitre | book=Fonctionnement d'un ordinateur
| prev=Les circuits de sélection
| prevText=Les circuits de sélection
| next=Les bascules : des mémoires de 1 bit
| nextText=Les bascules : des mémoires de 1 bit
}}
</noinclude>
3q6an49kram573jakpainqrg8l0wy3e
768803
768802
2026-06-27T17:19:43Z
Mewtow
31375
/* Le demi-soustracteur */
768803
wikitext
text/x-wiki
Le circuit '''incrémenteur''' incrémente un nombre entier. Plus précisément, il prend en entrée un nombre entier, et fournit en entier le nombre suivant, il lui ajoute 1. Une telle opération peut paraitre triviale et peu intéressante. Cependant, elle est très utilisée et est absolument cruciale pour créer des compteurs, des circuits capables de compter ou décompter, qui auront droit à un chapitre dédié. De plus, au-delà de leur utilisation dans les compteurs, les incrémenteurs étaient très utilisés sur les premiers processeurs 8 bits, comme le Z-80, le 6502, les premiers processeurs x86 comme le 8008, le 8086, le 8085, et bien d'autres.
Il existe aussi des ''circuits décrémenteurs'', qui décrémentent un opérande, ainsi que des ''circuits incrémenteur-décrémenteur'', qui peuvent incrémenter ou décrémenter, selon comment on les configure. Je ne vais pas détailler ces circuits plus que ça, car de tels circuits sont assez rares, comparé à un circuit incrémenteur simple.
==Le demi-additionneur==
Le circuit incrémenteur effectue l'opération suivante :
<math>a_7</math> <math>a_6</math> <math>a_5</math> <math>a_4</math> <math>a_3</math> <math>a_2</math> <math>a_1</math> <math>a_0</math>
+ 0 0 0 0 0 0 0 1
Un incrémenteur basique doit donc faire une addition pour chaque colonne, et précisément une addition de deux bits. Il se trouve que la table d'addition est très simple en binaire. Jugez plutôt :
* 0 + 0 = 0, retenue = 0 ;
* 0 + 1 = 1, retenue = 0 ;
* 1 + 0 = 1, retenue = 0 ;
* 1 + 1 = 0, retenue = 1.
Un circuit capable d'additionner deux bits est appelé un '''demi-additionneur'''. Il dispose d'une sortie S pour la somme, et C pour la retenue (''carry'' an anglais).
===Le demi-additionneur classique===
Un demi-additionneur est très simple à construire avec les techniques vues dans les premiers chapitres. Voici sa table de vérité :
{|class="wikitable"
|-
! Retenue entrante !! Opérande 1 !! !! Retenue sortante !! Bit de somme
|-
||0||0|| ||0||0
|-
||0||1|| ||0||1
|-
||1||0|| ||0||1
|-
||1||1|| ||1||0
|}
On voit immédiatement que la colonne des retenues donne une porte ET, alors que celle du bit de somme est calculé par un XOR.
{| class="flexible"
|[[File:1-bit half-adder.svg|class=transparent|centre|Demi-addtionneur.]]
|[[File:Half-adder.svg|centre|class=transparent|Circuit d'un demi-addtionneur.]]
|}
Il existe beaucoup d'autres implémentations alternatives, qui utilisent moins de portes logiques, calculent la retenue plus rapidement, consomment moins d'énergie, et autres. Et il est intéressant d'étudier quelques alternatives, surtout qu'elles serviront plus bas, quand on étudiera le circuit incrémenteur du 8085.
===Le demi-additionneur basé sur une modification de la retenue===
Une source d'amélioration est liée à la porte XOR. En pratique, une porte XOR est composée en combinant plusieurs portes logiques ET/OU/NOR/NAND ensemble. Et il se trouve qu'il y a des redondances entre celles-ci et les portes utilisées pour calculer la retenue. Par exemple, rappelons qu'une porte XOR peut être construite avec une porte ET et deux portes NOR, comme illustré ci-dessous. Et il se trouve que la porte ET interne calcule la retenue sortante. En supprimant cette redondance, on économise quelques portes logiques. De plus, on se retrouve avec un demi-additionneur qui calcule le bit de somme à partir de la retenue sortante.
[[File:Demi-additionneur avec redondances éliminées.png|centre|vignette|upright=2|Demi-additionneur avec redondances éliminées]]
Le circuit précédent a une interprétation logique. Si vous regardez la table de vérité, vous remarquerez que la somme de deux bits est égale à l'inverse de la retenue sortante, sauf dans le cas où les deux bits additionnés valent zéro. Et le circuit précédent est basé là-dessus.
{|class="wikitable"
|-
! Retenue entrante !! Opérande 1 !! !! Retenue sortante !! Bit de somme
|- class="f_vert"
||0||0|| ||0||0
|- class="f_rouge"
||0||1|| ||0||1
|- class="f_rouge"
||1||0|| ||0||1
|- class="f_rouge"
||1||1|| ||1||0
|}
L'idée est de calculer l'inverse du bit de somme, avant de l'inverser avec une porte NON. L'inverse du bit de somme vaut 1, soit quand la retenue est à 1, soit quand les deux bits additionnés sont à 0. La porte ET calcule la retenue sortante, la première porte NOR détecte sur les deux bits d'entrée valent zéro. Une porte OU combine les deux résultat pour obtenir l'inverse du bit d'entrée, puis une porte NOn inverse le tout pour obtenir le bit de somme adéquat. Les deux portes sont fusionnées : c'est la seconde porte NOR.
[[File:Full adder HA + MUX.png|centre|vignette|upright=2.5|Demi-additionneur basé sur une inversion de la retenue sortante.]]
===Les implémentations alternatives===
Une implémentation alternative, qui n'utilise que des portes NOR, est la suivante :
[[File:Demi-additionneur fait avec des portes NOR et NAND.png|centre|vignette|upright=2|Demi-additionneur fait avec des portes NOR et NAND.]]
Une implémentation alternative utilise des portes NAND. Pour rappel, il est possible de créer une porte XOR avec 4 portes NAND. La première d'entre elle fait un NAND entre les deux bits d'entrée, ce qui fait qu'elle calcule l'inverse de la retenue sortante. Le tout est illustré ci-dessous. En théorie, on devrait utiliser une porte NON pour récupérer la retenue correcte. Mais nous verrons plus bas que ce n'est pas une obligation. Nous verrons plus bas un exemple où c'est l'inverse de la retenue qui est utilisée dans les calculs, pour rendre les calculs plus rapides.
[[File:Demi-additionneur fait avec des portes NAND.png|centre|vignette|upright=2|Demi-additionneur fait avec des portes NAND]]
==Le circuit incrémenteur==
Maintenant que l'on sait comment additionner deux bits, reprenons l'opération d'incrémentation :
<math>a_7</math> <math>a_6</math> <math>a_5</math> <math>a_4</math> <math>a_3</math> <math>a_2</math> <math>a_1</math> <math>a_0</math>
+ 0 0 0 0 0 0 0 1
------------------------------
Sur la colonne la plus à droite, il doit ajouter un au bit de poids faible. Pour les colonnes suivantes, il faut additionner le bit de l'opérande avec la retenue de la colonne précédente. En clair, on n'additionne que deux bits à chaque colonne : un 1 sur celle tout à droite, la retenue de la colonne précédente pour les autres. Et cela nous donne une idée de comment faire pour créer le circuit incrémenteur.
===L'incrémenteur à propagation de retenue===
Il suffit d'utiliser un demi-additionneur par colonne, et de les enchainer les uns à la suite des autres. Chaque demi-additionneur additionne le bit de l'opérande avec la retenue fournie par le demi-additionneur précédent. Le seul qui fait exception est celui pour la colonne de poids faible. Pour celui-là, il doit ajouter 1 au bit de poids faible. Le résultat est appelé un '''incrémenteur à propagation de retenue'''. Il est constitué de demi-additionneurs enchaînés les uns à la suite des autres, du bit de poids faible vers le bit de poids fort.
[[File:Circuit incrémenteur.png|centre|vignette|upright=3|Circuit incrémenteur.]]
Maintenant, regardons le demi-additionneur le plus à droite, celui pour le bit de poids faible. Son entrée de retenue entrante est mise à 1 pour faire l'incrémentation. Quelques incrémenteurs permettent de configurer cette entrée de retenue à 0 ou à 1, ce qui effectue : soit une opération identité (l'opérande est recopié sur la sortie), soit une incrémentation. Un tel circuit est nommé un '''incrémenteur commandable'''. Nous aurons à utiliser une fois ou deux de tels incrémenteurs commandables dans la suite du cours.
L'incrémenteur à propagation de retenue est le plus simple et le plus économe en portes logiques. Mais de tels incrémenteurs sont rarement utilisés. À la place, on leur préfère des incrémenteurs plus rapides, mais qui utilisent plus de portes logiques. De tels incrémenteurs accélèrent le calcul des retenues. En effet, la rapidité d'une incrémentation est limitée par la propagation de la retenue : les retenues commencent à être calculées au bit de poids fort et on doit les calculer une par une, jusqu’à atteindre le bit de poids fort. Et cette "propagation des retenues" prend du temps, d'autant plus de temps que l'opérande est longue. Il y a deux optimisations principales, appelées le ''carry skip'' et l'anticipation de retenue, que nous allons décrire ci-dessous.
===Les incrémenteurs optimisés===
L'optimisation que nous allns voir effectue l'incrémentation, non pas bit par bit, mais par paquets de deux bits. Le résultat est que l'incrémentation est deux fois plus rapide, ou presque. Le circuit incrémenteur est donc composé en enchainant non pas des demi-additionneurs, mais des '''incrémenteurs 2 bits''' qui incrémentent un opérande de deux bits.
[[File:Circuit incrémenteur optimisé, faisant l'incrémentation par paquet de deux bits.png|centre|vignette|upright=2.5|Circuit incrémenteur optimisé, faisant l'incrémentation par paquet de deux bits]]
Reste à concevoir l'incrémenteur 2 bits. Une première méthode part de deux demi-additionneurs et effectue quelques simplifications au niveau des transistors et des portes logiques. Par exemple, l'Intel 8085 a utilisé un circuit optimisé, fabriqué avec des portes NAND et NOR. La raison est qu'avec les technologies de transistors CMOS actuelles, les portes NAND et NOR utilisent moins de transistors que les portes ET et OU. Et cette possibilité a été utilisée pour effectuer des simplifications assez mineures, mais toujours bonnes à prendre. Mais nous ne pouvons pas en parler en détail ici, car nous n'avons pas encore parlé en détail des transistors. Cependant, un exemple bien précis nous est accesible : celui de l’incrémenteur du processeur 8085 d'Intel.
Le processeur 8085 d'Intel est un processeur 8 bits très ancien. Il contenait un incrémenteur 16 bits, qui était utilisé pour calculer des adresses mémoire. Et cet incrémenteur était un incrémenteur à propagation de retenue optimisé. Il utilisait lui aussi des incrémenteurs 2 bits un peu modifiés.
L'idée était que les retenues sortant des colonnes paires étaient inversées, les colonnes impaires faisaient les calculs à partir de cette retenue inversée. Les demi-additionneurs des colonnes impaires n'étaient donc pas les mêmes que ceux des colonnes paires. Leurs tables de vérité sont différentes, leurs circuits aussi, et aucun des deux ne ressemble à un demi-additionneur normal. En réalité, ils sont fabriqués à partir des implémentations vues plus haut, qui utilisent uniquement des portes NAND ou uniquement des portes NOR.
Pour comprendre l'intérêt de faire ainsi, nous devons préciser une chose importante : avec les technologies CMOS utilisées pour les processeurs depuis les années 70, les portes logiques les plus simples sont les portes NON, NOR et NAND. Les portes ET/OU sont fabriquées en combinant des portes NOR/NAND avec une porte NON, ce qui prend plus de transistors.
Un demi-additionneur est donc fabriqué comme illustré ci-dessous, en logique CMOS. Seules les portes pour le calcul de la retenue sont indiquées, la porte XOR pour le bit de somme est fabriquée à partir de plusieurs portes logiques.
[[File:Demi-additionneur en CMOS.png|centre|vignette|upright=2|Demi-additionneur en CMOS]]
Si on enchaine deux demi-additionneurs, cela donne ceci :
[[File:Brique de base de l'incrémenteur du 8085.png|centre|vignette|upright=2|Brique de base de l'incrémenteur du 8085]]
La retenue doit donc traverser quatre portes logiques, dont deux portes NON. En inversant la retenue, les portes en jaune dans le schéma précédent se simplifient. Il suffit d'utiliser la loi de de Morgan pour trouver un circuit équivalent aux portes jaunes, qui n'est autre qu'une porte NOR couplée à une porte NON. Un autre moyen d'obtenir le même résultat est de prendre un demi-additionneur fabriqué avec uniquement des portes NOR, et de retirer la porte NON adéquate. Il faut ensuite corriger l'entrée de retenue de la porte XOR, pour qu'elle fasse l'addition avec la retenue correctement. Le résultat est illustré ci-dessous.
[[File:Incrémenteur 2 bits de l'incrémenteur du 8085.png|centre|vignette|upright=2|Incrémenteur 2 bits de l'incrémenteur du 8085]]
Le circuit utilise le même nombre de portes logiques, il n'économise pas de transistors. Par contre, la retenue n'a plus qu'à traverser deux portes logiques, au lieu de quatre. La propagation de la retenue est donc plus rapide, l'incrémentation est donc plus rapide.
===Les incrémenteurs 2 bits à calcul séparé de la retenue===
Il existe une autre méthode qui permet de créer des incrémenteurs 2 bits plus rapides que les précédents, mais qui utilisent plus de portes logiques. Ces incrémenteurs 2 bits calculent séparément les deux bits du résultat et la retenue sortante. La retenue sortante est calculée à partir des bits de l’opérande, sans avoir besoin de propager des retenues. Ainsi, la retenue sortante est connue en avance, sans passer par deux demi-additionneurs. Formellement, ils sont un cas particulier d'une optimisation appelée l'''anticipation de retenue''. Si nous verrons le cas général dans la section suivante, ce cas particulier est une très bonne introduction.
Pour créer un tel incrémenteur, le mieux est de partir de sa table de vérité.
{|class="wikitable"
|-
! Opérande !! Retenue entrante !! !! Retenue sortante !! Résultat
|-
| 00 || 0 || || 0 || 00
|-
| 01 || 0 || || 0 || 01
|-
| 10 || 0 || || 0 || 10
|-
| 11 || 0 || || 0 || 11
|-
| colspan="5" |
|-
| 00 || 1 || || 0 || 01
|-
| 01 || 1 || || 0 || 10
|-
| 10 || 1 || || 0 || 11
|-
| 11 || 1 || || 1 || 00
|}
Elle nous dit que la retenue sortante ne vaut 1 que dans un seul cas : les deux bits d'opérande valent 1, la retenue entrante vaut 1. Elle se calcule alors avec une porte ET à trois entrées. Pour les deux bits du résultat, les équations logiques ne donnent pas un résultat satisfaisant. La solution la plus simple est d'utiliser deux demi-additionneurs et de retirer les portes logiques superflues. Le résultat est alors un '''incrémenteur 2 bits ''carry skip''''', où la retenue sortante est calculée séparément du reste.
[[File:Incrémenteur carry skip 2 bits.png|centre|vignette|upright=2.5|Incrémenteur ''carry skip'' 2 bits]]
===Les incrémenteurs à anticipation de retenue===
L''''anticipation de retenue''' calcule la retenue d'une colonne sans attendre les retenues des colonnes précédentes. Dans le cas idéal, toutes les retenues sont calculées en parallèle, en même temps, et sont ensuite envoyées aux demi-additionneurs. Il s'agit d'une optimisation qui est utilisée pour l'incrémentation, l'addition et d'autres opérations similaires. Pour l'incrémentation, déterminer la retenue ne demande pas de calculs complexes, contrairement à l'addition.
[[File:Incrémenteur à anticipation de retenues, 8 bits.png|centre|vignette|upright=2.5|Incrémenteur à anticipation de retenues, 8 bits.]]
Pour le comprendre, on peut regarder comment la retenue sortant d'un demi-additionneur est formée. Elle est calculée en faisant un ET logique entre la retenue entrante et le bit d'opérande. Une retenue est donc un ET logique entre toutes les retenues précédentes. Un incrémenteur à anticipation de retenue utilise donc une porte ET à plusieurs entrées pour calculer une retenue.
[[File:Anticipation de retenue pour un bit du résultat, incrémenteur.png|centre|vignette|upright=2.5|Anticipation de retenue pour un bit du résultat, incrémenteur]]
En théorie, on peut utiliser une porte ET à plusieurs entrées pour chaque bit de l'opérande. Cependant, cela entrainera un cout en transistors très important. Pour éviter de gaspiller trop de portes logiques, une solution est de mélanger anticipation de retenues et propagation de retenue. Par exemple, pour un incrémenteur 32 bits, on peut découper l'opérande en 4 octets : on anticipe les retenues pour chaque octet, mais l'incrémentation de chaque octet se fait avec propagation de retenue et/ou ''carry skip''. Plus haut, nous avons vu un cas particulier où les retenues étaient anticipées par blocs de deux bits.
[[File:Incrémenteur à anticipation de retenues.png|centre|vignette|upright=3|Incrémenteur hybride utilisant partiellement l'anticipation de retenues.]]
Quelques processeurs utilisaient l'anticipation de retenues. Par exemple, le processeur Z-80 de Zilog utilisait un incrémenteur pour des nombres de 16 bits, ce qui demandait des performances assez élevées. Et cet incrémenteur utilisait à la fois anticipation de retenues et ''carry skip''. Il était découpé en quatre blocs avant anticipation de retenues entre eux : un bloc regroupant les 7 bits de poids faible, suivi par un bloc de 5 bits, lui-même suivi par un bloc de 3 bits, terminé par un dernier bit isolé. A l'intérieur de ces blocs, les bits sont regroupés en paires utilisant le ''carry skip''. Pour ceux qui veulent en savoir plus sur cet incrémenteur, voici un lien sur le sujet :
* [https://www.righto.com/2013/11/the-z-80s-16-bit-incrementdecrement.html The Z-80's 16-bit increment/decrement circuit reverse engineered].
==Les décrémenteurs==
Les circuits décrémenteurs font l'inverse d'un incrémenteur : au lieu d'ajouter 1 à un opérande, ils lui soustraient 1. La différence n'est pas énorme, ce qui fait que les circuits incrémenteurs et décrémenteurs se ressemblent beaucoup. Là où les incrémenteurs sont fabriqués à partir de demi-additionneurs, les décrémenteurs sont bâtis avec des demi-soustracteurs.
La soustraction se fait en binaire comme en décimal. On soustrait deux chiffres, puis on doit propager une éventuelle retenue sur la colonne suivante. La retenue apparait quand le chiffre soustrait est plus grand que l'autre chiffre. Elle est propagée sur la colonne suivante, où elle doit être soustraite du résultat. Pour le dire autrement, après avoir soustrait un chiffre, on doit de plus soustraire la retenue de la colonne précédente.
La décrémentation effectue le calcul suivant :
<math>a_7</math> <math>a_6</math> <math>a_5</math> <math>a_4</math> <math>a_3</math> <math>a_2</math> <math>a_1</math> <math>a_0</math>
- 0 0 0 0 0 0 0 1
La colonne la plus à droite soustrait un 1. Par contre, les colonnes suivantes soustraient un zéro. Soustraire un zéro ne change rien. Mais il ne faut pas oublier de soustraire une éventuellement retenue, propagée depuis la colonne précédente. En clair, un décrémenteur peut se construire si on sait soustraire deux bits. On soustrait un 1 sur la colonne la plus à droite, on soustrait une retenue sur les autres colonnes. Et pour soustraire deux bits, il faut utiliser un demi-soustracteur.
===Le demi-soustracteur===
Un '''demi-soustracteur''' soustrait deux bits. Il prend deux bits d'entrée et fournit deux sorties : un bit de différence et une retenue. Pour comprendre d'où vient cette retenue, et comment soustraire deux bits, traitons les quatre cas possibles un par un.
Premièrement, soustraire zéro à un bit ne changera rien :
* 0 - 0 = 0 ;
* 1 - 0 = 1.
Si on soustrait 1 à un bit qui vaut 1, on obtient zéro.
* 1 - 1 = 0.
Maintenant, que se passe-t-il si on soustrait 1 à 0 ? Voici le résultat :
* 0 - 1 = 1 et une retenue propagée sur la colonne suivante, où elle est soustraite.
La table de vérité est donc la suivante. Détail important : le demi-soustracteur fait ici la soustraction X - Y. En anglais, X est appelé le ''Minuend'' et Y est appelé le ''Subtrahend'', mais ces termes n'ont pas de traduction française. Aussi, je vais les utiliser tels quels dans ce qui suit.
{|class="wikitable"
|-
! ''Minuend'' !! ''Subtrahend'' !! !! Retenue sortante !! Bit de somme
|-
||0||0|| ||0||0
|-
||0||1|| ||1||1
|-
||1||0|| ||0||1
|-
||1||1|| ||0||0
|}
Si on construit la table de vérité, et qu'on construit le circuit associé, on tombe sur ce circuit.
[[File:Half Subtractor Vektor.svg|centre|vignette|Demi-soustracteur.]]
Vous remarquerez que le bit de somme se calcule de la même manière que pour l'addition, seul le calcul de la retenue change. Comme pour le demi-additionneur, on peut exploiter des redondances entre calcul de la retenue et porte XOR, mais ce ne sont pas les mêmes. Le circuit obtenu après exploitation de ces redondances est le suivant :
[[File:Demi-soustracteur optimisé, sans redondances.png|centre|vignette|upright=1.5|Demi-soustracteur optimisé, sans redondances]]
===Le décrémenteur à propagation de retenue===
Un décrémenteur à propagation de retenue se construit comme un incrémenteur, sauf qu'on remplace les demi-additionneurs par des demi-soustracteurs. En clair, on enchaine plusieurs demi-soustracteurs les uns à la suite des autres.
Il est possible d'utiliser l'anticipation de retenue, mais les circuits sont cependant différents de l'incrémenteur. Idem avec le ''carry skip'', et toute autre optimisation possible. La raison est que les retenues sont calculées différemment, ce qui fait que l’anticipation de retenue doit être adaptée. La retenue d'une colonne ne se calcule plus avec un ET des bits des colonnes précédentes. A la place, ces bits doivent être inversés avant de faire le ET. Le résultat est que la porte ET est remplacée par une porte NOR.
Pour ceux qui s'en rappelle, faire un NOR de N bits permet de vérifier s'ils sont tous à zéro. Et c'est le cas ici. Lors d'une incrémentation, on a une retenue quand tous les bits précédents de l'opérande sont à 1. Pour la décrémentation, c'est l'inverse : on a une retenue quand tous les bits précédents sont à 0.
De fait, décrémenter un nombre est assez simple. Il y a deux cas principaux :
* soit le bit de poids faible est un 1, ce qui signifie que le nombre est impair ;
* soit un ou plusieurs bits de poids faible consécutifs sont à 0, suivis par un 1.
Dans le premier cas, le bit de poids faible est juste inversé. Dans le second cas, le 1 est inversé, et les 0 de poids faible deviennent des 1. Par exemple, Prenez un nombre de la forme xxxxxxx10000. Après décrémentation, le résultat sera xxxxxxxx011111 : les 0 de poids faible deviennent des 1, le premier 1 en partant de la droite est inversé. Et la même logique se généralise pour tout nombre avant un ou plusieurs zéros dans les bits de poids faible.
==Les incrémenteurs-décrémenteurs==
Les circuits décrémenteurs ressemblent beaucoup aux incrémenteurs. Aussi, vous ne serez pas étonné qu'il soit possible de fusionner un incrémenteur et un décrémenteur en un seul circuit. Un tel circuit s'appelle un '''incrémenteur-décrémenteur'''. Un tel circuit a une interface similaire à celle d'un incrémenteur, avec une entrée sur laquelle présenter l'opérande, et une sortie sur laquelle récupérer le résultat. Il a aussi une entrée de retenue, sur laquelle envoyer un 1 (incrémentation) ou un 0 (opération NOP/identité). Et il a aussi une sortie de retenue, qui indique que le résultat du calcul a débordé.
Mais en plus de cela, il dispose d'une entrée de commande qui permet de choisir entre incrémentation et décrémentation.
Il existe plusieurs manières de créer un incrémenteur-décrémenteur, que nous allons voir si dessous. Nous n'allons pas voir le cas le plus évident, aussi ne vous étonnez pas si vous avez remarqué qu'un demi-additionneur et un demi-soustracteur se ressemblent beaucoup.
===L'incrémenteur-décrémenteur basé sur des demi-additionneurs===
Une première implémentation se base sur les propriétés du complément à deux, et plus précisément sur cette équation :
: <math>\overline{x} + 1 = \overline{x - 1}</math>
Les deux termes de cette équation sont simplement des définitions équivalentes du complément à deux d'un nombre entier. Celui-ci se calcule de deux manières : soit on incrémente son complément à 1, soit on prend le complément à 1 de son décrément. Et cela nous permet de déterminer comment obtenir une incrémentation à partir d'une décrémentation et inversement.
Que nous dit cette équation ? Simplement que l'on peut décrémenter un nombre si on a deux inverseurs commandables, et un circuit incrémenteur. La décrémentation se fait alors en deux étapes : on calcule son complément à deux, puis on inverse les bits du résultat précédent. Le calcul du complément à deux demande d'avoir un circuit inverseur, et un incrémenteur.
===L'incrémenteur-décrémenteur basé sur des demi-additionneurs===
Vous l'aurez sans doute remarqué, mais le demi-soustracteur ressemble beaucoup au demi-additionneur. La seule différence est une porte NON ajoutée au bon endroit dans le demi-soustracteur. La porte NON inverse le bit X, sachant que le demi-soustracteur fait la soustraction X - Y. De plus, l'opérande inversée est utilisée seulement pour le calcul des retenues, le bit de somme est calculé normalement.
{|
|[[File:Half-adder.svg|vignette|Demi-additionneur]]
|[[File:Half Subtractor Vektor.svg|vignette|upright=1.3|Demi-soustracteur]]
|}
La conséquence est qu'il est possible de fusionner un demi-additionneur avec un demi-soustracteur. L'idée est de remplacer la porte NON du demi-soustracteur par un inverseur commandable. Si on envoie un zéro sur l'entrée de commande, l'inverseur commandable se comportera comme une porte OUI et le circuit sera alors identique à un demi-additionneur. Si on envoie un 1 sur l'entrée de commande, l'inverseur commandable se comportera comme une porte NON. le circuit sera alors un demi-soustracteur. En remplaçant les demi-additionneurs (ou demi-soustracteurs) par de tels circuits, on peut obtenir un circuit faisant à la fois incrémenteur et décrémenteur.
Un défaut de cette approche est que les optimisations vues plus haut pour les demi-additionneurs ne fonctionne tout simplement plus. On est obligé d'utiliser une porte XOR et une porte ET séparées, pas question d'utiliser les demi-additionneurs optimisés vu au début du chapitre.
<noinclude>
{{NavChapitre | book=Fonctionnement d'un ordinateur
| prev=Les circuits de sélection
| prevText=Les circuits de sélection
| next=Les bascules : des mémoires de 1 bit
| nextText=Les bascules : des mémoires de 1 bit
}}
</noinclude>
ao7ytrnhftc1errqruh4u1j1vo83s70
768804
768803
2026-06-27T18:52:51Z
Mewtow
31375
/* Le décrémenteur à propagation de retenue */
768804
wikitext
text/x-wiki
Le circuit '''incrémenteur''' incrémente un nombre entier. Plus précisément, il prend en entrée un nombre entier, et fournit en entier le nombre suivant, il lui ajoute 1. Une telle opération peut paraitre triviale et peu intéressante. Cependant, elle est très utilisée et est absolument cruciale pour créer des compteurs, des circuits capables de compter ou décompter, qui auront droit à un chapitre dédié. De plus, au-delà de leur utilisation dans les compteurs, les incrémenteurs étaient très utilisés sur les premiers processeurs 8 bits, comme le Z-80, le 6502, les premiers processeurs x86 comme le 8008, le 8086, le 8085, et bien d'autres.
Il existe aussi des ''circuits décrémenteurs'', qui décrémentent un opérande, ainsi que des ''circuits incrémenteur-décrémenteur'', qui peuvent incrémenter ou décrémenter, selon comment on les configure. Je ne vais pas détailler ces circuits plus que ça, car de tels circuits sont assez rares, comparé à un circuit incrémenteur simple.
==Le demi-additionneur==
Le circuit incrémenteur effectue l'opération suivante :
<math>a_7</math> <math>a_6</math> <math>a_5</math> <math>a_4</math> <math>a_3</math> <math>a_2</math> <math>a_1</math> <math>a_0</math>
+ 0 0 0 0 0 0 0 1
Un incrémenteur basique doit donc faire une addition pour chaque colonne, et précisément une addition de deux bits. Il se trouve que la table d'addition est très simple en binaire. Jugez plutôt :
* 0 + 0 = 0, retenue = 0 ;
* 0 + 1 = 1, retenue = 0 ;
* 1 + 0 = 1, retenue = 0 ;
* 1 + 1 = 0, retenue = 1.
Un circuit capable d'additionner deux bits est appelé un '''demi-additionneur'''. Il dispose d'une sortie S pour la somme, et C pour la retenue (''carry'' an anglais).
===Le demi-additionneur classique===
Un demi-additionneur est très simple à construire avec les techniques vues dans les premiers chapitres. Voici sa table de vérité :
{|class="wikitable"
|-
! Retenue entrante !! Opérande 1 !! !! Retenue sortante !! Bit de somme
|-
||0||0|| ||0||0
|-
||0||1|| ||0||1
|-
||1||0|| ||0||1
|-
||1||1|| ||1||0
|}
On voit immédiatement que la colonne des retenues donne une porte ET, alors que celle du bit de somme est calculé par un XOR.
{| class="flexible"
|[[File:1-bit half-adder.svg|class=transparent|centre|Demi-addtionneur.]]
|[[File:Half-adder.svg|centre|class=transparent|Circuit d'un demi-addtionneur.]]
|}
Il existe beaucoup d'autres implémentations alternatives, qui utilisent moins de portes logiques, calculent la retenue plus rapidement, consomment moins d'énergie, et autres. Et il est intéressant d'étudier quelques alternatives, surtout qu'elles serviront plus bas, quand on étudiera le circuit incrémenteur du 8085.
===Le demi-additionneur basé sur une modification de la retenue===
Une source d'amélioration est liée à la porte XOR. En pratique, une porte XOR est composée en combinant plusieurs portes logiques ET/OU/NOR/NAND ensemble. Et il se trouve qu'il y a des redondances entre celles-ci et les portes utilisées pour calculer la retenue. Par exemple, rappelons qu'une porte XOR peut être construite avec une porte ET et deux portes NOR, comme illustré ci-dessous. Et il se trouve que la porte ET interne calcule la retenue sortante. En supprimant cette redondance, on économise quelques portes logiques. De plus, on se retrouve avec un demi-additionneur qui calcule le bit de somme à partir de la retenue sortante.
[[File:Demi-additionneur avec redondances éliminées.png|centre|vignette|upright=2|Demi-additionneur avec redondances éliminées]]
Le circuit précédent a une interprétation logique. Si vous regardez la table de vérité, vous remarquerez que la somme de deux bits est égale à l'inverse de la retenue sortante, sauf dans le cas où les deux bits additionnés valent zéro. Et le circuit précédent est basé là-dessus.
{|class="wikitable"
|-
! Retenue entrante !! Opérande 1 !! !! Retenue sortante !! Bit de somme
|- class="f_vert"
||0||0|| ||0||0
|- class="f_rouge"
||0||1|| ||0||1
|- class="f_rouge"
||1||0|| ||0||1
|- class="f_rouge"
||1||1|| ||1||0
|}
L'idée est de calculer l'inverse du bit de somme, avant de l'inverser avec une porte NON. L'inverse du bit de somme vaut 1, soit quand la retenue est à 1, soit quand les deux bits additionnés sont à 0. La porte ET calcule la retenue sortante, la première porte NOR détecte sur les deux bits d'entrée valent zéro. Une porte OU combine les deux résultat pour obtenir l'inverse du bit d'entrée, puis une porte NOn inverse le tout pour obtenir le bit de somme adéquat. Les deux portes sont fusionnées : c'est la seconde porte NOR.
[[File:Full adder HA + MUX.png|centre|vignette|upright=2.5|Demi-additionneur basé sur une inversion de la retenue sortante.]]
===Les implémentations alternatives===
Une implémentation alternative, qui n'utilise que des portes NOR, est la suivante :
[[File:Demi-additionneur fait avec des portes NOR et NAND.png|centre|vignette|upright=2|Demi-additionneur fait avec des portes NOR et NAND.]]
Une implémentation alternative utilise des portes NAND. Pour rappel, il est possible de créer une porte XOR avec 4 portes NAND. La première d'entre elle fait un NAND entre les deux bits d'entrée, ce qui fait qu'elle calcule l'inverse de la retenue sortante. Le tout est illustré ci-dessous. En théorie, on devrait utiliser une porte NON pour récupérer la retenue correcte. Mais nous verrons plus bas que ce n'est pas une obligation. Nous verrons plus bas un exemple où c'est l'inverse de la retenue qui est utilisée dans les calculs, pour rendre les calculs plus rapides.
[[File:Demi-additionneur fait avec des portes NAND.png|centre|vignette|upright=2|Demi-additionneur fait avec des portes NAND]]
==Le circuit incrémenteur==
Maintenant que l'on sait comment additionner deux bits, reprenons l'opération d'incrémentation :
<math>a_7</math> <math>a_6</math> <math>a_5</math> <math>a_4</math> <math>a_3</math> <math>a_2</math> <math>a_1</math> <math>a_0</math>
+ 0 0 0 0 0 0 0 1
------------------------------
Sur la colonne la plus à droite, il doit ajouter un au bit de poids faible. Pour les colonnes suivantes, il faut additionner le bit de l'opérande avec la retenue de la colonne précédente. En clair, on n'additionne que deux bits à chaque colonne : un 1 sur celle tout à droite, la retenue de la colonne précédente pour les autres. Et cela nous donne une idée de comment faire pour créer le circuit incrémenteur.
===L'incrémenteur à propagation de retenue===
Il suffit d'utiliser un demi-additionneur par colonne, et de les enchainer les uns à la suite des autres. Chaque demi-additionneur additionne le bit de l'opérande avec la retenue fournie par le demi-additionneur précédent. Le seul qui fait exception est celui pour la colonne de poids faible. Pour celui-là, il doit ajouter 1 au bit de poids faible. Le résultat est appelé un '''incrémenteur à propagation de retenue'''. Il est constitué de demi-additionneurs enchaînés les uns à la suite des autres, du bit de poids faible vers le bit de poids fort.
[[File:Circuit incrémenteur.png|centre|vignette|upright=3|Circuit incrémenteur.]]
Maintenant, regardons le demi-additionneur le plus à droite, celui pour le bit de poids faible. Son entrée de retenue entrante est mise à 1 pour faire l'incrémentation. Quelques incrémenteurs permettent de configurer cette entrée de retenue à 0 ou à 1, ce qui effectue : soit une opération identité (l'opérande est recopié sur la sortie), soit une incrémentation. Un tel circuit est nommé un '''incrémenteur commandable'''. Nous aurons à utiliser une fois ou deux de tels incrémenteurs commandables dans la suite du cours.
L'incrémenteur à propagation de retenue est le plus simple et le plus économe en portes logiques. Mais de tels incrémenteurs sont rarement utilisés. À la place, on leur préfère des incrémenteurs plus rapides, mais qui utilisent plus de portes logiques. De tels incrémenteurs accélèrent le calcul des retenues. En effet, la rapidité d'une incrémentation est limitée par la propagation de la retenue : les retenues commencent à être calculées au bit de poids fort et on doit les calculer une par une, jusqu’à atteindre le bit de poids fort. Et cette "propagation des retenues" prend du temps, d'autant plus de temps que l'opérande est longue. Il y a deux optimisations principales, appelées le ''carry skip'' et l'anticipation de retenue, que nous allons décrire ci-dessous.
===Les incrémenteurs optimisés===
L'optimisation que nous allns voir effectue l'incrémentation, non pas bit par bit, mais par paquets de deux bits. Le résultat est que l'incrémentation est deux fois plus rapide, ou presque. Le circuit incrémenteur est donc composé en enchainant non pas des demi-additionneurs, mais des '''incrémenteurs 2 bits''' qui incrémentent un opérande de deux bits.
[[File:Circuit incrémenteur optimisé, faisant l'incrémentation par paquet de deux bits.png|centre|vignette|upright=2.5|Circuit incrémenteur optimisé, faisant l'incrémentation par paquet de deux bits]]
Reste à concevoir l'incrémenteur 2 bits. Une première méthode part de deux demi-additionneurs et effectue quelques simplifications au niveau des transistors et des portes logiques. Par exemple, l'Intel 8085 a utilisé un circuit optimisé, fabriqué avec des portes NAND et NOR. La raison est qu'avec les technologies de transistors CMOS actuelles, les portes NAND et NOR utilisent moins de transistors que les portes ET et OU. Et cette possibilité a été utilisée pour effectuer des simplifications assez mineures, mais toujours bonnes à prendre. Mais nous ne pouvons pas en parler en détail ici, car nous n'avons pas encore parlé en détail des transistors. Cependant, un exemple bien précis nous est accesible : celui de l’incrémenteur du processeur 8085 d'Intel.
Le processeur 8085 d'Intel est un processeur 8 bits très ancien. Il contenait un incrémenteur 16 bits, qui était utilisé pour calculer des adresses mémoire. Et cet incrémenteur était un incrémenteur à propagation de retenue optimisé. Il utilisait lui aussi des incrémenteurs 2 bits un peu modifiés.
L'idée était que les retenues sortant des colonnes paires étaient inversées, les colonnes impaires faisaient les calculs à partir de cette retenue inversée. Les demi-additionneurs des colonnes impaires n'étaient donc pas les mêmes que ceux des colonnes paires. Leurs tables de vérité sont différentes, leurs circuits aussi, et aucun des deux ne ressemble à un demi-additionneur normal. En réalité, ils sont fabriqués à partir des implémentations vues plus haut, qui utilisent uniquement des portes NAND ou uniquement des portes NOR.
Pour comprendre l'intérêt de faire ainsi, nous devons préciser une chose importante : avec les technologies CMOS utilisées pour les processeurs depuis les années 70, les portes logiques les plus simples sont les portes NON, NOR et NAND. Les portes ET/OU sont fabriquées en combinant des portes NOR/NAND avec une porte NON, ce qui prend plus de transistors.
Un demi-additionneur est donc fabriqué comme illustré ci-dessous, en logique CMOS. Seules les portes pour le calcul de la retenue sont indiquées, la porte XOR pour le bit de somme est fabriquée à partir de plusieurs portes logiques.
[[File:Demi-additionneur en CMOS.png|centre|vignette|upright=2|Demi-additionneur en CMOS]]
Si on enchaine deux demi-additionneurs, cela donne ceci :
[[File:Brique de base de l'incrémenteur du 8085.png|centre|vignette|upright=2|Brique de base de l'incrémenteur du 8085]]
La retenue doit donc traverser quatre portes logiques, dont deux portes NON. En inversant la retenue, les portes en jaune dans le schéma précédent se simplifient. Il suffit d'utiliser la loi de de Morgan pour trouver un circuit équivalent aux portes jaunes, qui n'est autre qu'une porte NOR couplée à une porte NON. Un autre moyen d'obtenir le même résultat est de prendre un demi-additionneur fabriqué avec uniquement des portes NOR, et de retirer la porte NON adéquate. Il faut ensuite corriger l'entrée de retenue de la porte XOR, pour qu'elle fasse l'addition avec la retenue correctement. Le résultat est illustré ci-dessous.
[[File:Incrémenteur 2 bits de l'incrémenteur du 8085.png|centre|vignette|upright=2|Incrémenteur 2 bits de l'incrémenteur du 8085]]
Le circuit utilise le même nombre de portes logiques, il n'économise pas de transistors. Par contre, la retenue n'a plus qu'à traverser deux portes logiques, au lieu de quatre. La propagation de la retenue est donc plus rapide, l'incrémentation est donc plus rapide.
===Les incrémenteurs 2 bits à calcul séparé de la retenue===
Il existe une autre méthode qui permet de créer des incrémenteurs 2 bits plus rapides que les précédents, mais qui utilisent plus de portes logiques. Ces incrémenteurs 2 bits calculent séparément les deux bits du résultat et la retenue sortante. La retenue sortante est calculée à partir des bits de l’opérande, sans avoir besoin de propager des retenues. Ainsi, la retenue sortante est connue en avance, sans passer par deux demi-additionneurs. Formellement, ils sont un cas particulier d'une optimisation appelée l'''anticipation de retenue''. Si nous verrons le cas général dans la section suivante, ce cas particulier est une très bonne introduction.
Pour créer un tel incrémenteur, le mieux est de partir de sa table de vérité.
{|class="wikitable"
|-
! Opérande !! Retenue entrante !! !! Retenue sortante !! Résultat
|-
| 00 || 0 || || 0 || 00
|-
| 01 || 0 || || 0 || 01
|-
| 10 || 0 || || 0 || 10
|-
| 11 || 0 || || 0 || 11
|-
| colspan="5" |
|-
| 00 || 1 || || 0 || 01
|-
| 01 || 1 || || 0 || 10
|-
| 10 || 1 || || 0 || 11
|-
| 11 || 1 || || 1 || 00
|}
Elle nous dit que la retenue sortante ne vaut 1 que dans un seul cas : les deux bits d'opérande valent 1, la retenue entrante vaut 1. Elle se calcule alors avec une porte ET à trois entrées. Pour les deux bits du résultat, les équations logiques ne donnent pas un résultat satisfaisant. La solution la plus simple est d'utiliser deux demi-additionneurs et de retirer les portes logiques superflues. Le résultat est alors un '''incrémenteur 2 bits ''carry skip''''', où la retenue sortante est calculée séparément du reste.
[[File:Incrémenteur carry skip 2 bits.png|centre|vignette|upright=2.5|Incrémenteur ''carry skip'' 2 bits]]
===Les incrémenteurs à anticipation de retenue===
L''''anticipation de retenue''' calcule la retenue d'une colonne sans attendre les retenues des colonnes précédentes. Dans le cas idéal, toutes les retenues sont calculées en parallèle, en même temps, et sont ensuite envoyées aux demi-additionneurs. Il s'agit d'une optimisation qui est utilisée pour l'incrémentation, l'addition et d'autres opérations similaires. Pour l'incrémentation, déterminer la retenue ne demande pas de calculs complexes, contrairement à l'addition.
[[File:Incrémenteur à anticipation de retenues, 8 bits.png|centre|vignette|upright=2.5|Incrémenteur à anticipation de retenues, 8 bits.]]
Pour le comprendre, on peut regarder comment la retenue sortant d'un demi-additionneur est formée. Elle est calculée en faisant un ET logique entre la retenue entrante et le bit d'opérande. Une retenue est donc un ET logique entre toutes les retenues précédentes. Un incrémenteur à anticipation de retenue utilise donc une porte ET à plusieurs entrées pour calculer une retenue.
[[File:Anticipation de retenue pour un bit du résultat, incrémenteur.png|centre|vignette|upright=2.5|Anticipation de retenue pour un bit du résultat, incrémenteur]]
En théorie, on peut utiliser une porte ET à plusieurs entrées pour chaque bit de l'opérande. Cependant, cela entrainera un cout en transistors très important. Pour éviter de gaspiller trop de portes logiques, une solution est de mélanger anticipation de retenues et propagation de retenue. Par exemple, pour un incrémenteur 32 bits, on peut découper l'opérande en 4 octets : on anticipe les retenues pour chaque octet, mais l'incrémentation de chaque octet se fait avec propagation de retenue et/ou ''carry skip''. Plus haut, nous avons vu un cas particulier où les retenues étaient anticipées par blocs de deux bits.
[[File:Incrémenteur à anticipation de retenues.png|centre|vignette|upright=3|Incrémenteur hybride utilisant partiellement l'anticipation de retenues.]]
Quelques processeurs utilisaient l'anticipation de retenues. Par exemple, le processeur Z-80 de Zilog utilisait un incrémenteur pour des nombres de 16 bits, ce qui demandait des performances assez élevées. Et cet incrémenteur utilisait à la fois anticipation de retenues et ''carry skip''. Il était découpé en quatre blocs avant anticipation de retenues entre eux : un bloc regroupant les 7 bits de poids faible, suivi par un bloc de 5 bits, lui-même suivi par un bloc de 3 bits, terminé par un dernier bit isolé. A l'intérieur de ces blocs, les bits sont regroupés en paires utilisant le ''carry skip''. Pour ceux qui veulent en savoir plus sur cet incrémenteur, voici un lien sur le sujet :
* [https://www.righto.com/2013/11/the-z-80s-16-bit-incrementdecrement.html The Z-80's 16-bit increment/decrement circuit reverse engineered].
==Les décrémenteurs==
Les circuits décrémenteurs font l'inverse d'un incrémenteur : au lieu d'ajouter 1 à un opérande, ils lui soustraient 1. La différence n'est pas énorme, ce qui fait que les circuits incrémenteurs et décrémenteurs se ressemblent beaucoup. Là où les incrémenteurs sont fabriqués à partir de demi-additionneurs, les décrémenteurs sont bâtis avec des demi-soustracteurs.
La soustraction se fait en binaire comme en décimal. On soustrait deux chiffres, puis on doit propager une éventuelle retenue sur la colonne suivante. La retenue apparait quand le chiffre soustrait est plus grand que l'autre chiffre. Elle est propagée sur la colonne suivante, où elle doit être soustraite du résultat. Pour le dire autrement, après avoir soustrait un chiffre, on doit de plus soustraire la retenue de la colonne précédente.
La décrémentation effectue le calcul suivant :
<math>a_7</math> <math>a_6</math> <math>a_5</math> <math>a_4</math> <math>a_3</math> <math>a_2</math> <math>a_1</math> <math>a_0</math>
- 0 0 0 0 0 0 0 1
La colonne la plus à droite soustrait un 1. Par contre, les colonnes suivantes soustraient un zéro. Soustraire un zéro ne change rien. Mais il ne faut pas oublier de soustraire une éventuellement retenue, propagée depuis la colonne précédente. En clair, un décrémenteur peut se construire si on sait soustraire deux bits. On soustrait un 1 sur la colonne la plus à droite, on soustrait une retenue sur les autres colonnes. Et pour soustraire deux bits, il faut utiliser un demi-soustracteur.
===Le demi-soustracteur===
Un '''demi-soustracteur''' soustrait deux bits. Il prend deux bits d'entrée et fournit deux sorties : un bit de différence et une retenue. Pour comprendre d'où vient cette retenue, et comment soustraire deux bits, traitons les quatre cas possibles un par un.
Premièrement, soustraire zéro à un bit ne changera rien :
* 0 - 0 = 0 ;
* 1 - 0 = 1.
Si on soustrait 1 à un bit qui vaut 1, on obtient zéro.
* 1 - 1 = 0.
Maintenant, que se passe-t-il si on soustrait 1 à 0 ? Voici le résultat :
* 0 - 1 = 1 et une retenue propagée sur la colonne suivante, où elle est soustraite.
La table de vérité est donc la suivante. Détail important : le demi-soustracteur fait ici la soustraction X - Y. En anglais, X est appelé le ''Minuend'' et Y est appelé le ''Subtrahend'', mais ces termes n'ont pas de traduction française. Aussi, je vais les utiliser tels quels dans ce qui suit.
{|class="wikitable"
|-
! ''Minuend'' !! ''Subtrahend'' !! !! Retenue sortante !! Bit de somme
|-
||0||0|| ||0||0
|-
||0||1|| ||1||1
|-
||1||0|| ||0||1
|-
||1||1|| ||0||0
|}
Si on construit la table de vérité, et qu'on construit le circuit associé, on tombe sur ce circuit.
[[File:Half Subtractor Vektor.svg|centre|vignette|Demi-soustracteur.]]
Vous remarquerez que le bit de somme se calcule de la même manière que pour l'addition, seul le calcul de la retenue change. Comme pour le demi-additionneur, on peut exploiter des redondances entre calcul de la retenue et porte XOR, mais ce ne sont pas les mêmes. Le circuit obtenu après exploitation de ces redondances est le suivant :
[[File:Demi-soustracteur optimisé, sans redondances.png|centre|vignette|upright=1.5|Demi-soustracteur optimisé, sans redondances]]
===Le circuit décrémenteur===
Un décrémenteur à propagation de retenue se construit comme un incrémenteur, sauf qu'on remplace les demi-additionneurs par des demi-soustracteurs. En clair, on enchaine plusieurs demi-soustracteurs les uns à la suite des autres.
Il est possible d'utiliser l'anticipation de retenue, mais les circuits sont cependant différents de l'incrémenteur. Idem avec le ''carry skip'', et toute autre optimisation possible. La raison est que les retenues sont calculées différemment, ce qui fait que l’anticipation de retenue doit être adaptée. La retenue d'une colonne ne se calcule plus avec un ET des bits des colonnes précédentes. A la place, ces bits doivent être inversés avant de faire le ET. Le résultat est que la porte ET est remplacée par une porte NOR.
Pour ceux qui s'en rappelle, faire un NOR de N bits permet de vérifier s'ils sont tous à zéro. Et c'est le cas ici. Lors d'une incrémentation, on a une retenue quand tous les bits précédents de l'opérande sont à 1. Pour la décrémentation, c'est l'inverse : on a une retenue quand tous les bits précédents sont à 0.
De fait, décrémenter un nombre est assez simple. Il y a deux cas principaux :
* soit le bit de poids faible est un 1, ce qui signifie que le nombre est impair ;
* soit un ou plusieurs bits de poids faible consécutifs sont à 0, suivis par un 1.
Dans le premier cas, le bit de poids faible est juste inversé. Dans le second cas, le 1 est inversé, et les 0 de poids faible deviennent des 1. Par exemple, Prenez un nombre de la forme xxxxxxx10000. Après décrémentation, le résultat sera xxxxxxxx011111 : les 0 de poids faible deviennent des 1, le premier 1 en partant de la droite est inversé. Et la même logique se généralise pour tout nombre avant un ou plusieurs zéros dans les bits de poids faible.
==Les incrémenteurs-décrémenteurs==
Les circuits décrémenteurs ressemblent beaucoup aux incrémenteurs. Aussi, vous ne serez pas étonné qu'il soit possible de fusionner un incrémenteur et un décrémenteur en un seul circuit. Un tel circuit s'appelle un '''incrémenteur-décrémenteur'''. Un tel circuit a une interface similaire à celle d'un incrémenteur, avec une entrée sur laquelle présenter l'opérande, et une sortie sur laquelle récupérer le résultat. Il a aussi une entrée de retenue, sur laquelle envoyer un 1 (incrémentation) ou un 0 (opération NOP/identité). Et il a aussi une sortie de retenue, qui indique que le résultat du calcul a débordé.
Mais en plus de cela, il dispose d'une entrée de commande qui permet de choisir entre incrémentation et décrémentation.
Il existe plusieurs manières de créer un incrémenteur-décrémenteur, que nous allons voir si dessous. Nous n'allons pas voir le cas le plus évident, aussi ne vous étonnez pas si vous avez remarqué qu'un demi-additionneur et un demi-soustracteur se ressemblent beaucoup.
===L'incrémenteur-décrémenteur basé sur des demi-additionneurs===
Une première implémentation se base sur les propriétés du complément à deux, et plus précisément sur cette équation :
: <math>\overline{x} + 1 = \overline{x - 1}</math>
Les deux termes de cette équation sont simplement des définitions équivalentes du complément à deux d'un nombre entier. Celui-ci se calcule de deux manières : soit on incrémente son complément à 1, soit on prend le complément à 1 de son décrément. Et cela nous permet de déterminer comment obtenir une incrémentation à partir d'une décrémentation et inversement.
Que nous dit cette équation ? Simplement que l'on peut décrémenter un nombre si on a deux inverseurs commandables, et un circuit incrémenteur. La décrémentation se fait alors en deux étapes : on calcule son complément à deux, puis on inverse les bits du résultat précédent. Le calcul du complément à deux demande d'avoir un circuit inverseur, et un incrémenteur.
===L'incrémenteur-décrémenteur basé sur des demi-additionneurs===
Vous l'aurez sans doute remarqué, mais le demi-soustracteur ressemble beaucoup au demi-additionneur. La seule différence est une porte NON ajoutée au bon endroit dans le demi-soustracteur. La porte NON inverse le bit X, sachant que le demi-soustracteur fait la soustraction X - Y. De plus, l'opérande inversée est utilisée seulement pour le calcul des retenues, le bit de somme est calculé normalement.
{|
|[[File:Half-adder.svg|vignette|Demi-additionneur]]
|[[File:Half Subtractor Vektor.svg|vignette|upright=1.3|Demi-soustracteur]]
|}
La conséquence est qu'il est possible de fusionner un demi-additionneur avec un demi-soustracteur. L'idée est de remplacer la porte NON du demi-soustracteur par un inverseur commandable. Si on envoie un zéro sur l'entrée de commande, l'inverseur commandable se comportera comme une porte OUI et le circuit sera alors identique à un demi-additionneur. Si on envoie un 1 sur l'entrée de commande, l'inverseur commandable se comportera comme une porte NON. le circuit sera alors un demi-soustracteur. En remplaçant les demi-additionneurs (ou demi-soustracteurs) par de tels circuits, on peut obtenir un circuit faisant à la fois incrémenteur et décrémenteur.
Un défaut de cette approche est que les optimisations vues plus haut pour les demi-additionneurs ne fonctionne tout simplement plus. On est obligé d'utiliser une porte XOR et une porte ET séparées, pas question d'utiliser les demi-additionneurs optimisés vu au début du chapitre.
<noinclude>
{{NavChapitre | book=Fonctionnement d'un ordinateur
| prev=Les circuits de sélection
| prevText=Les circuits de sélection
| next=Les bascules : des mémoires de 1 bit
| nextText=Les bascules : des mémoires de 1 bit
}}
</noinclude>
rljzonpv5ei32lx6p9lqsyb8es4rwh8
768805
768804
2026-06-27T18:58:45Z
Mewtow
31375
/* Les décrémenteurs */
768805
wikitext
text/x-wiki
Le circuit '''incrémenteur''' incrémente un nombre entier. Plus précisément, il prend en entrée un nombre entier, et fournit en entier le nombre suivant, il lui ajoute 1. Une telle opération peut paraitre triviale et peu intéressante. Cependant, elle est très utilisée et est absolument cruciale pour créer des compteurs, des circuits capables de compter ou décompter, qui auront droit à un chapitre dédié. De plus, au-delà de leur utilisation dans les compteurs, les incrémenteurs étaient très utilisés sur les premiers processeurs 8 bits, comme le Z-80, le 6502, les premiers processeurs x86 comme le 8008, le 8086, le 8085, et bien d'autres.
Il existe aussi des ''circuits décrémenteurs'', qui décrémentent un opérande, ainsi que des ''circuits incrémenteur-décrémenteur'', qui peuvent incrémenter ou décrémenter, selon comment on les configure. Je ne vais pas détailler ces circuits plus que ça, car de tels circuits sont assez rares, comparé à un circuit incrémenteur simple.
==Le demi-additionneur==
Le circuit incrémenteur effectue l'opération suivante :
<math>a_7</math> <math>a_6</math> <math>a_5</math> <math>a_4</math> <math>a_3</math> <math>a_2</math> <math>a_1</math> <math>a_0</math>
+ 0 0 0 0 0 0 0 1
Un incrémenteur basique doit donc faire une addition pour chaque colonne, et précisément une addition de deux bits. Il se trouve que la table d'addition est très simple en binaire. Jugez plutôt :
* 0 + 0 = 0, retenue = 0 ;
* 0 + 1 = 1, retenue = 0 ;
* 1 + 0 = 1, retenue = 0 ;
* 1 + 1 = 0, retenue = 1.
Un circuit capable d'additionner deux bits est appelé un '''demi-additionneur'''. Il dispose d'une sortie S pour la somme, et C pour la retenue (''carry'' an anglais).
===Le demi-additionneur classique===
Un demi-additionneur est très simple à construire avec les techniques vues dans les premiers chapitres. Voici sa table de vérité :
{|class="wikitable"
|-
! Retenue entrante !! Opérande 1 !! !! Retenue sortante !! Bit de somme
|-
||0||0|| ||0||0
|-
||0||1|| ||0||1
|-
||1||0|| ||0||1
|-
||1||1|| ||1||0
|}
On voit immédiatement que la colonne des retenues donne une porte ET, alors que celle du bit de somme est calculé par un XOR.
{| class="flexible"
|[[File:1-bit half-adder.svg|class=transparent|centre|Demi-addtionneur.]]
|[[File:Half-adder.svg|centre|class=transparent|Circuit d'un demi-addtionneur.]]
|}
Il existe beaucoup d'autres implémentations alternatives, qui utilisent moins de portes logiques, calculent la retenue plus rapidement, consomment moins d'énergie, et autres. Et il est intéressant d'étudier quelques alternatives, surtout qu'elles serviront plus bas, quand on étudiera le circuit incrémenteur du 8085.
===Le demi-additionneur basé sur une modification de la retenue===
Une source d'amélioration est liée à la porte XOR. En pratique, une porte XOR est composée en combinant plusieurs portes logiques ET/OU/NOR/NAND ensemble. Et il se trouve qu'il y a des redondances entre celles-ci et les portes utilisées pour calculer la retenue. Par exemple, rappelons qu'une porte XOR peut être construite avec une porte ET et deux portes NOR, comme illustré ci-dessous. Et il se trouve que la porte ET interne calcule la retenue sortante. En supprimant cette redondance, on économise quelques portes logiques. De plus, on se retrouve avec un demi-additionneur qui calcule le bit de somme à partir de la retenue sortante.
[[File:Demi-additionneur avec redondances éliminées.png|centre|vignette|upright=2|Demi-additionneur avec redondances éliminées]]
Le circuit précédent a une interprétation logique. Si vous regardez la table de vérité, vous remarquerez que la somme de deux bits est égale à l'inverse de la retenue sortante, sauf dans le cas où les deux bits additionnés valent zéro. Et le circuit précédent est basé là-dessus.
{|class="wikitable"
|-
! Retenue entrante !! Opérande 1 !! !! Retenue sortante !! Bit de somme
|- class="f_vert"
||0||0|| ||0||0
|- class="f_rouge"
||0||1|| ||0||1
|- class="f_rouge"
||1||0|| ||0||1
|- class="f_rouge"
||1||1|| ||1||0
|}
L'idée est de calculer l'inverse du bit de somme, avant de l'inverser avec une porte NON. L'inverse du bit de somme vaut 1, soit quand la retenue est à 1, soit quand les deux bits additionnés sont à 0. La porte ET calcule la retenue sortante, la première porte NOR détecte sur les deux bits d'entrée valent zéro. Une porte OU combine les deux résultat pour obtenir l'inverse du bit d'entrée, puis une porte NOn inverse le tout pour obtenir le bit de somme adéquat. Les deux portes sont fusionnées : c'est la seconde porte NOR.
[[File:Full adder HA + MUX.png|centre|vignette|upright=2.5|Demi-additionneur basé sur une inversion de la retenue sortante.]]
===Les implémentations alternatives===
Une implémentation alternative, qui n'utilise que des portes NOR, est la suivante :
[[File:Demi-additionneur fait avec des portes NOR et NAND.png|centre|vignette|upright=2|Demi-additionneur fait avec des portes NOR et NAND.]]
Une implémentation alternative utilise des portes NAND. Pour rappel, il est possible de créer une porte XOR avec 4 portes NAND. La première d'entre elle fait un NAND entre les deux bits d'entrée, ce qui fait qu'elle calcule l'inverse de la retenue sortante. Le tout est illustré ci-dessous. En théorie, on devrait utiliser une porte NON pour récupérer la retenue correcte. Mais nous verrons plus bas que ce n'est pas une obligation. Nous verrons plus bas un exemple où c'est l'inverse de la retenue qui est utilisée dans les calculs, pour rendre les calculs plus rapides.
[[File:Demi-additionneur fait avec des portes NAND.png|centre|vignette|upright=2|Demi-additionneur fait avec des portes NAND]]
==Le circuit incrémenteur==
Maintenant que l'on sait comment additionner deux bits, reprenons l'opération d'incrémentation :
<math>a_7</math> <math>a_6</math> <math>a_5</math> <math>a_4</math> <math>a_3</math> <math>a_2</math> <math>a_1</math> <math>a_0</math>
+ 0 0 0 0 0 0 0 1
------------------------------
Sur la colonne la plus à droite, il doit ajouter un au bit de poids faible. Pour les colonnes suivantes, il faut additionner le bit de l'opérande avec la retenue de la colonne précédente. En clair, on n'additionne que deux bits à chaque colonne : un 1 sur celle tout à droite, la retenue de la colonne précédente pour les autres. Et cela nous donne une idée de comment faire pour créer le circuit incrémenteur.
===L'incrémenteur à propagation de retenue===
Il suffit d'utiliser un demi-additionneur par colonne, et de les enchainer les uns à la suite des autres. Chaque demi-additionneur additionne le bit de l'opérande avec la retenue fournie par le demi-additionneur précédent. Le seul qui fait exception est celui pour la colonne de poids faible. Pour celui-là, il doit ajouter 1 au bit de poids faible. Le résultat est appelé un '''incrémenteur à propagation de retenue'''. Il est constitué de demi-additionneurs enchaînés les uns à la suite des autres, du bit de poids faible vers le bit de poids fort.
[[File:Circuit incrémenteur.png|centre|vignette|upright=3|Circuit incrémenteur.]]
Maintenant, regardons le demi-additionneur le plus à droite, celui pour le bit de poids faible. Son entrée de retenue entrante est mise à 1 pour faire l'incrémentation. Quelques incrémenteurs permettent de configurer cette entrée de retenue à 0 ou à 1, ce qui effectue : soit une opération identité (l'opérande est recopié sur la sortie), soit une incrémentation. Un tel circuit est nommé un '''incrémenteur commandable'''. Nous aurons à utiliser une fois ou deux de tels incrémenteurs commandables dans la suite du cours.
L'incrémenteur à propagation de retenue est le plus simple et le plus économe en portes logiques. Mais de tels incrémenteurs sont rarement utilisés. À la place, on leur préfère des incrémenteurs plus rapides, mais qui utilisent plus de portes logiques. De tels incrémenteurs accélèrent le calcul des retenues. En effet, la rapidité d'une incrémentation est limitée par la propagation de la retenue : les retenues commencent à être calculées au bit de poids fort et on doit les calculer une par une, jusqu’à atteindre le bit de poids fort. Et cette "propagation des retenues" prend du temps, d'autant plus de temps que l'opérande est longue. Il y a deux optimisations principales, appelées le ''carry skip'' et l'anticipation de retenue, que nous allons décrire ci-dessous.
===Les incrémenteurs optimisés===
L'optimisation que nous allns voir effectue l'incrémentation, non pas bit par bit, mais par paquets de deux bits. Le résultat est que l'incrémentation est deux fois plus rapide, ou presque. Le circuit incrémenteur est donc composé en enchainant non pas des demi-additionneurs, mais des '''incrémenteurs 2 bits''' qui incrémentent un opérande de deux bits.
[[File:Circuit incrémenteur optimisé, faisant l'incrémentation par paquet de deux bits.png|centre|vignette|upright=2.5|Circuit incrémenteur optimisé, faisant l'incrémentation par paquet de deux bits]]
Reste à concevoir l'incrémenteur 2 bits. Une première méthode part de deux demi-additionneurs et effectue quelques simplifications au niveau des transistors et des portes logiques. Par exemple, l'Intel 8085 a utilisé un circuit optimisé, fabriqué avec des portes NAND et NOR. La raison est qu'avec les technologies de transistors CMOS actuelles, les portes NAND et NOR utilisent moins de transistors que les portes ET et OU. Et cette possibilité a été utilisée pour effectuer des simplifications assez mineures, mais toujours bonnes à prendre. Mais nous ne pouvons pas en parler en détail ici, car nous n'avons pas encore parlé en détail des transistors. Cependant, un exemple bien précis nous est accesible : celui de l’incrémenteur du processeur 8085 d'Intel.
Le processeur 8085 d'Intel est un processeur 8 bits très ancien. Il contenait un incrémenteur 16 bits, qui était utilisé pour calculer des adresses mémoire. Et cet incrémenteur était un incrémenteur à propagation de retenue optimisé. Il utilisait lui aussi des incrémenteurs 2 bits un peu modifiés.
L'idée était que les retenues sortant des colonnes paires étaient inversées, les colonnes impaires faisaient les calculs à partir de cette retenue inversée. Les demi-additionneurs des colonnes impaires n'étaient donc pas les mêmes que ceux des colonnes paires. Leurs tables de vérité sont différentes, leurs circuits aussi, et aucun des deux ne ressemble à un demi-additionneur normal. En réalité, ils sont fabriqués à partir des implémentations vues plus haut, qui utilisent uniquement des portes NAND ou uniquement des portes NOR.
Pour comprendre l'intérêt de faire ainsi, nous devons préciser une chose importante : avec les technologies CMOS utilisées pour les processeurs depuis les années 70, les portes logiques les plus simples sont les portes NON, NOR et NAND. Les portes ET/OU sont fabriquées en combinant des portes NOR/NAND avec une porte NON, ce qui prend plus de transistors.
Un demi-additionneur est donc fabriqué comme illustré ci-dessous, en logique CMOS. Seules les portes pour le calcul de la retenue sont indiquées, la porte XOR pour le bit de somme est fabriquée à partir de plusieurs portes logiques.
[[File:Demi-additionneur en CMOS.png|centre|vignette|upright=2|Demi-additionneur en CMOS]]
Si on enchaine deux demi-additionneurs, cela donne ceci :
[[File:Brique de base de l'incrémenteur du 8085.png|centre|vignette|upright=2|Brique de base de l'incrémenteur du 8085]]
La retenue doit donc traverser quatre portes logiques, dont deux portes NON. En inversant la retenue, les portes en jaune dans le schéma précédent se simplifient. Il suffit d'utiliser la loi de de Morgan pour trouver un circuit équivalent aux portes jaunes, qui n'est autre qu'une porte NOR couplée à une porte NON. Un autre moyen d'obtenir le même résultat est de prendre un demi-additionneur fabriqué avec uniquement des portes NOR, et de retirer la porte NON adéquate. Il faut ensuite corriger l'entrée de retenue de la porte XOR, pour qu'elle fasse l'addition avec la retenue correctement. Le résultat est illustré ci-dessous.
[[File:Incrémenteur 2 bits de l'incrémenteur du 8085.png|centre|vignette|upright=2|Incrémenteur 2 bits de l'incrémenteur du 8085]]
Le circuit utilise le même nombre de portes logiques, il n'économise pas de transistors. Par contre, la retenue n'a plus qu'à traverser deux portes logiques, au lieu de quatre. La propagation de la retenue est donc plus rapide, l'incrémentation est donc plus rapide.
===Les incrémenteurs 2 bits à calcul séparé de la retenue===
Il existe une autre méthode qui permet de créer des incrémenteurs 2 bits plus rapides que les précédents, mais qui utilisent plus de portes logiques. Ces incrémenteurs 2 bits calculent séparément les deux bits du résultat et la retenue sortante. La retenue sortante est calculée à partir des bits de l’opérande, sans avoir besoin de propager des retenues. Ainsi, la retenue sortante est connue en avance, sans passer par deux demi-additionneurs. Formellement, ils sont un cas particulier d'une optimisation appelée l'''anticipation de retenue''. Si nous verrons le cas général dans la section suivante, ce cas particulier est une très bonne introduction.
Pour créer un tel incrémenteur, le mieux est de partir de sa table de vérité.
{|class="wikitable"
|-
! Opérande !! Retenue entrante !! !! Retenue sortante !! Résultat
|-
| 00 || 0 || || 0 || 00
|-
| 01 || 0 || || 0 || 01
|-
| 10 || 0 || || 0 || 10
|-
| 11 || 0 || || 0 || 11
|-
| colspan="5" |
|-
| 00 || 1 || || 0 || 01
|-
| 01 || 1 || || 0 || 10
|-
| 10 || 1 || || 0 || 11
|-
| 11 || 1 || || 1 || 00
|}
Elle nous dit que la retenue sortante ne vaut 1 que dans un seul cas : les deux bits d'opérande valent 1, la retenue entrante vaut 1. Elle se calcule alors avec une porte ET à trois entrées. Pour les deux bits du résultat, les équations logiques ne donnent pas un résultat satisfaisant. La solution la plus simple est d'utiliser deux demi-additionneurs et de retirer les portes logiques superflues. Le résultat est alors un '''incrémenteur 2 bits ''carry skip''''', où la retenue sortante est calculée séparément du reste.
[[File:Incrémenteur carry skip 2 bits.png|centre|vignette|upright=2.5|Incrémenteur ''carry skip'' 2 bits]]
===Les incrémenteurs à anticipation de retenue===
L''''anticipation de retenue''' calcule la retenue d'une colonne sans attendre les retenues des colonnes précédentes. Dans le cas idéal, toutes les retenues sont calculées en parallèle, en même temps, et sont ensuite envoyées aux demi-additionneurs. Il s'agit d'une optimisation qui est utilisée pour l'incrémentation, l'addition et d'autres opérations similaires. Pour l'incrémentation, déterminer la retenue ne demande pas de calculs complexes, contrairement à l'addition.
[[File:Incrémenteur à anticipation de retenues, 8 bits.png|centre|vignette|upright=2.5|Incrémenteur à anticipation de retenues, 8 bits.]]
Pour le comprendre, on peut regarder comment la retenue sortant d'un demi-additionneur est formée. Elle est calculée en faisant un ET logique entre la retenue entrante et le bit d'opérande. Une retenue est donc un ET logique entre toutes les retenues précédentes. Un incrémenteur à anticipation de retenue utilise donc une porte ET à plusieurs entrées pour calculer une retenue.
[[File:Anticipation de retenue pour un bit du résultat, incrémenteur.png|centre|vignette|upright=2.5|Anticipation de retenue pour un bit du résultat, incrémenteur]]
En théorie, on peut utiliser une porte ET à plusieurs entrées pour chaque bit de l'opérande. Cependant, cela entrainera un cout en transistors très important. Pour éviter de gaspiller trop de portes logiques, une solution est de mélanger anticipation de retenues et propagation de retenue. Par exemple, pour un incrémenteur 32 bits, on peut découper l'opérande en 4 octets : on anticipe les retenues pour chaque octet, mais l'incrémentation de chaque octet se fait avec propagation de retenue et/ou ''carry skip''. Plus haut, nous avons vu un cas particulier où les retenues étaient anticipées par blocs de deux bits.
[[File:Incrémenteur à anticipation de retenues.png|centre|vignette|upright=3|Incrémenteur hybride utilisant partiellement l'anticipation de retenues.]]
Quelques processeurs utilisaient l'anticipation de retenues. Par exemple, le processeur Z-80 de Zilog utilisait un incrémenteur pour des nombres de 16 bits, ce qui demandait des performances assez élevées. Et cet incrémenteur utilisait à la fois anticipation de retenues et ''carry skip''. Il était découpé en quatre blocs avant anticipation de retenues entre eux : un bloc regroupant les 7 bits de poids faible, suivi par un bloc de 5 bits, lui-même suivi par un bloc de 3 bits, terminé par un dernier bit isolé. A l'intérieur de ces blocs, les bits sont regroupés en paires utilisant le ''carry skip''. Pour ceux qui veulent en savoir plus sur cet incrémenteur, voici un lien sur le sujet :
* [https://www.righto.com/2013/11/the-z-80s-16-bit-incrementdecrement.html The Z-80's 16-bit increment/decrement circuit reverse engineered].
==Les décrémenteurs==
Les circuits décrémenteurs font l'inverse d'un incrémenteur : au lieu d'ajouter 1 à un opérande, ils lui soustraient 1. La différence n'est pas énorme, ce qui fait que les circuits incrémenteurs et décrémenteurs se ressemblent beaucoup.
La soustraction se fait en binaire comme en décimal. On soustrait deux chiffres, puis on doit propager une éventuelle retenue sur la colonne suivante. La retenue apparait quand le chiffre soustrait est plus grand que l'autre chiffre. Elle est propagée sur la colonne suivante, où elle doit être soustraite du résultat. Pour le dire autrement, après avoir soustrait un chiffre, on doit de plus soustraire la retenue de la colonne précédente.
La décrémentation effectue le calcul suivant :
<math>a_7</math> <math>a_6</math> <math>a_5</math> <math>a_4</math> <math>a_3</math> <math>a_2</math> <math>a_1</math> <math>a_0</math>
- 0 0 0 0 0 0 0 1
La colonne la plus à droite soustrait un 1. Par contre, les colonnes suivantes soustraient un zéro. Soustraire un zéro ne change rien. Mais il ne faut pas oublier de soustraire une éventuellement retenue, propagée depuis la colonne précédente. En clair, un décrémenteur peut se construire si on sait soustraire deux bits. On soustrait un 1 sur la colonne la plus à droite, on soustrait une retenue sur les autres colonnes. Et pour soustraire deux bits, il faut utiliser un demi-soustracteur.
===Le demi-soustracteur et le décrémenteur à propagation de retenue===
Un '''demi-soustracteur''' soustrait deux bits. Il prend deux bits d'entrée et fournit deux sorties : un bit de différence et une retenue. Pour comprendre d'où vient cette retenue, et comment soustraire deux bits, traitons les quatre cas possibles un par un.
Premièrement, soustraire zéro à un bit ne changera rien :
* 0 - 0 = 0 ;
* 1 - 0 = 1.
Si on soustrait 1 à un bit qui vaut 1, on obtient zéro.
* 1 - 1 = 0.
Maintenant, que se passe-t-il si on soustrait 1 à 0 ? Voici le résultat :
* 0 - 1 = 1 et une retenue propagée sur la colonne suivante, où elle est soustraite.
La table de vérité est donc la suivante. Détail important : le demi-soustracteur fait ici la soustraction X - Y. En anglais, X est appelé le ''Minuend'' et Y est appelé le ''Subtrahend'', mais ces termes n'ont pas de traduction française. Aussi, je vais les utiliser tels quels dans ce qui suit.
{|class="wikitable"
|-
! ''Minuend'' !! ''Subtrahend'' !! !! Retenue sortante !! Bit de somme
|-
||0||0|| ||0||0
|-
||0||1|| ||1||1
|-
||1||0|| ||0||1
|-
||1||1|| ||0||0
|}
Si on construit la table de vérité, et qu'on construit le circuit associé, on tombe sur ce circuit.
[[File:Half Subtractor Vektor.svg|centre|vignette|Demi-soustracteur.]]
Vous remarquerez que le bit de somme se calcule de la même manière que pour l'addition, seul le calcul de la retenue change. Comme pour le demi-additionneur, on peut exploiter des redondances entre calcul de la retenue et porte XOR, mais ce ne sont pas les mêmes. Le circuit obtenu après exploitation de ces redondances est le suivant :
[[File:Demi-soustracteur optimisé, sans redondances.png|centre|vignette|upright=1.5|Demi-soustracteur optimisé, sans redondances]]
Un décrémenteur à propagation de retenue se construit comme un incrémenteur, sauf qu'on remplace les demi-additionneurs par des demi-soustracteurs. En clair, on enchaine plusieurs demi-soustracteurs les uns à la suite des autres.
===Le décrémenteur à anticipation de retenue===
Il est possible d'utiliser l'anticipation de retenue, mais les circuits sont cependant différents de l'incrémenteur. Idem avec le ''carry skip'', et toute autre optimisation possible. La raison est que les retenues sont calculées différemment, ce qui fait que l’anticipation de retenue doit être adaptée. La retenue d'une colonne ne se calcule plus avec un ET des bits des colonnes précédentes. A la place, ces bits doivent être inversés avant de faire le ET. Le résultat est que la porte ET est remplacée par une porte NOR.
Pour ceux qui s'en rappelle, faire un NOR de N bits permet de vérifier s'ils sont tous à zéro. Et c'est le cas ici. Lors d'une incrémentation, on a une retenue quand tous les bits précédents de l'opérande sont à 1. Pour la décrémentation, c'est l'inverse : on a une retenue quand tous les bits précédents sont à 0.
De fait, décrémenter un nombre est assez simple. Il y a deux cas principaux :
* soit le bit de poids faible est un 1, ce qui signifie que le nombre est impair ;
* soit un ou plusieurs bits de poids faible consécutifs sont à 0, suivis par un 1.
Dans le premier cas, le bit de poids faible est juste inversé. Dans le second cas, le 1 est inversé, et les 0 de poids faible deviennent des 1. Par exemple, Prenez un nombre de la forme xxxxxxx10000. Après décrémentation, le résultat sera xxxxxxxx011111 : les 0 de poids faible deviennent des 1, le premier 1 en partant de la droite est inversé. Et la même logique se généralise pour tout nombre avant un ou plusieurs zéros dans les bits de poids faible.
==Les incrémenteurs-décrémenteurs==
Les circuits décrémenteurs ressemblent beaucoup aux incrémenteurs. Aussi, vous ne serez pas étonné qu'il soit possible de fusionner un incrémenteur et un décrémenteur en un seul circuit. Un tel circuit s'appelle un '''incrémenteur-décrémenteur'''. Un tel circuit a une interface similaire à celle d'un incrémenteur, avec une entrée sur laquelle présenter l'opérande, et une sortie sur laquelle récupérer le résultat. Il a aussi une entrée de retenue, sur laquelle envoyer un 1 (incrémentation) ou un 0 (opération NOP/identité). Et il a aussi une sortie de retenue, qui indique que le résultat du calcul a débordé.
Mais en plus de cela, il dispose d'une entrée de commande qui permet de choisir entre incrémentation et décrémentation.
Il existe plusieurs manières de créer un incrémenteur-décrémenteur, que nous allons voir si dessous. Nous n'allons pas voir le cas le plus évident, aussi ne vous étonnez pas si vous avez remarqué qu'un demi-additionneur et un demi-soustracteur se ressemblent beaucoup.
===L'incrémenteur-décrémenteur basé sur des demi-additionneurs===
Une première implémentation se base sur les propriétés du complément à deux, et plus précisément sur cette équation :
: <math>\overline{x} + 1 = \overline{x - 1}</math>
Les deux termes de cette équation sont simplement des définitions équivalentes du complément à deux d'un nombre entier. Celui-ci se calcule de deux manières : soit on incrémente son complément à 1, soit on prend le complément à 1 de son décrément. Et cela nous permet de déterminer comment obtenir une incrémentation à partir d'une décrémentation et inversement.
Que nous dit cette équation ? Simplement que l'on peut décrémenter un nombre si on a deux inverseurs commandables, et un circuit incrémenteur. La décrémentation se fait alors en deux étapes : on calcule son complément à deux, puis on inverse les bits du résultat précédent. Le calcul du complément à deux demande d'avoir un circuit inverseur, et un incrémenteur.
===L'incrémenteur-décrémenteur basé sur des demi-additionneurs===
Vous l'aurez sans doute remarqué, mais le demi-soustracteur ressemble beaucoup au demi-additionneur. La seule différence est une porte NON ajoutée au bon endroit dans le demi-soustracteur. La porte NON inverse le bit X, sachant que le demi-soustracteur fait la soustraction X - Y. De plus, l'opérande inversée est utilisée seulement pour le calcul des retenues, le bit de somme est calculé normalement.
{|
|[[File:Half-adder.svg|vignette|Demi-additionneur]]
|[[File:Half Subtractor Vektor.svg|vignette|upright=1.3|Demi-soustracteur]]
|}
La conséquence est qu'il est possible de fusionner un demi-additionneur avec un demi-soustracteur. L'idée est de remplacer la porte NON du demi-soustracteur par un inverseur commandable. Si on envoie un zéro sur l'entrée de commande, l'inverseur commandable se comportera comme une porte OUI et le circuit sera alors identique à un demi-additionneur. Si on envoie un 1 sur l'entrée de commande, l'inverseur commandable se comportera comme une porte NON. le circuit sera alors un demi-soustracteur. En remplaçant les demi-additionneurs (ou demi-soustracteurs) par de tels circuits, on peut obtenir un circuit faisant à la fois incrémenteur et décrémenteur.
Un défaut de cette approche est que les optimisations vues plus haut pour les demi-additionneurs ne fonctionne tout simplement plus. On est obligé d'utiliser une porte XOR et une porte ET séparées, pas question d'utiliser les demi-additionneurs optimisés vu au début du chapitre.
<noinclude>
{{NavChapitre | book=Fonctionnement d'un ordinateur
| prev=Les circuits de sélection
| prevText=Les circuits de sélection
| next=Les bascules : des mémoires de 1 bit
| nextText=Les bascules : des mémoires de 1 bit
}}
</noinclude>
8bm40kdioucnj3s30w1rt09h6fb6k5p
768806
768805
2026-06-27T18:59:05Z
Mewtow
31375
/* L'incrémenteur-décrémenteur basé sur des demi-additionneurs */
768806
wikitext
text/x-wiki
Le circuit '''incrémenteur''' incrémente un nombre entier. Plus précisément, il prend en entrée un nombre entier, et fournit en entier le nombre suivant, il lui ajoute 1. Une telle opération peut paraitre triviale et peu intéressante. Cependant, elle est très utilisée et est absolument cruciale pour créer des compteurs, des circuits capables de compter ou décompter, qui auront droit à un chapitre dédié. De plus, au-delà de leur utilisation dans les compteurs, les incrémenteurs étaient très utilisés sur les premiers processeurs 8 bits, comme le Z-80, le 6502, les premiers processeurs x86 comme le 8008, le 8086, le 8085, et bien d'autres.
Il existe aussi des ''circuits décrémenteurs'', qui décrémentent un opérande, ainsi que des ''circuits incrémenteur-décrémenteur'', qui peuvent incrémenter ou décrémenter, selon comment on les configure. Je ne vais pas détailler ces circuits plus que ça, car de tels circuits sont assez rares, comparé à un circuit incrémenteur simple.
==Le demi-additionneur==
Le circuit incrémenteur effectue l'opération suivante :
<math>a_7</math> <math>a_6</math> <math>a_5</math> <math>a_4</math> <math>a_3</math> <math>a_2</math> <math>a_1</math> <math>a_0</math>
+ 0 0 0 0 0 0 0 1
Un incrémenteur basique doit donc faire une addition pour chaque colonne, et précisément une addition de deux bits. Il se trouve que la table d'addition est très simple en binaire. Jugez plutôt :
* 0 + 0 = 0, retenue = 0 ;
* 0 + 1 = 1, retenue = 0 ;
* 1 + 0 = 1, retenue = 0 ;
* 1 + 1 = 0, retenue = 1.
Un circuit capable d'additionner deux bits est appelé un '''demi-additionneur'''. Il dispose d'une sortie S pour la somme, et C pour la retenue (''carry'' an anglais).
===Le demi-additionneur classique===
Un demi-additionneur est très simple à construire avec les techniques vues dans les premiers chapitres. Voici sa table de vérité :
{|class="wikitable"
|-
! Retenue entrante !! Opérande 1 !! !! Retenue sortante !! Bit de somme
|-
||0||0|| ||0||0
|-
||0||1|| ||0||1
|-
||1||0|| ||0||1
|-
||1||1|| ||1||0
|}
On voit immédiatement que la colonne des retenues donne une porte ET, alors que celle du bit de somme est calculé par un XOR.
{| class="flexible"
|[[File:1-bit half-adder.svg|class=transparent|centre|Demi-addtionneur.]]
|[[File:Half-adder.svg|centre|class=transparent|Circuit d'un demi-addtionneur.]]
|}
Il existe beaucoup d'autres implémentations alternatives, qui utilisent moins de portes logiques, calculent la retenue plus rapidement, consomment moins d'énergie, et autres. Et il est intéressant d'étudier quelques alternatives, surtout qu'elles serviront plus bas, quand on étudiera le circuit incrémenteur du 8085.
===Le demi-additionneur basé sur une modification de la retenue===
Une source d'amélioration est liée à la porte XOR. En pratique, une porte XOR est composée en combinant plusieurs portes logiques ET/OU/NOR/NAND ensemble. Et il se trouve qu'il y a des redondances entre celles-ci et les portes utilisées pour calculer la retenue. Par exemple, rappelons qu'une porte XOR peut être construite avec une porte ET et deux portes NOR, comme illustré ci-dessous. Et il se trouve que la porte ET interne calcule la retenue sortante. En supprimant cette redondance, on économise quelques portes logiques. De plus, on se retrouve avec un demi-additionneur qui calcule le bit de somme à partir de la retenue sortante.
[[File:Demi-additionneur avec redondances éliminées.png|centre|vignette|upright=2|Demi-additionneur avec redondances éliminées]]
Le circuit précédent a une interprétation logique. Si vous regardez la table de vérité, vous remarquerez que la somme de deux bits est égale à l'inverse de la retenue sortante, sauf dans le cas où les deux bits additionnés valent zéro. Et le circuit précédent est basé là-dessus.
{|class="wikitable"
|-
! Retenue entrante !! Opérande 1 !! !! Retenue sortante !! Bit de somme
|- class="f_vert"
||0||0|| ||0||0
|- class="f_rouge"
||0||1|| ||0||1
|- class="f_rouge"
||1||0|| ||0||1
|- class="f_rouge"
||1||1|| ||1||0
|}
L'idée est de calculer l'inverse du bit de somme, avant de l'inverser avec une porte NON. L'inverse du bit de somme vaut 1, soit quand la retenue est à 1, soit quand les deux bits additionnés sont à 0. La porte ET calcule la retenue sortante, la première porte NOR détecte sur les deux bits d'entrée valent zéro. Une porte OU combine les deux résultat pour obtenir l'inverse du bit d'entrée, puis une porte NOn inverse le tout pour obtenir le bit de somme adéquat. Les deux portes sont fusionnées : c'est la seconde porte NOR.
[[File:Full adder HA + MUX.png|centre|vignette|upright=2.5|Demi-additionneur basé sur une inversion de la retenue sortante.]]
===Les implémentations alternatives===
Une implémentation alternative, qui n'utilise que des portes NOR, est la suivante :
[[File:Demi-additionneur fait avec des portes NOR et NAND.png|centre|vignette|upright=2|Demi-additionneur fait avec des portes NOR et NAND.]]
Une implémentation alternative utilise des portes NAND. Pour rappel, il est possible de créer une porte XOR avec 4 portes NAND. La première d'entre elle fait un NAND entre les deux bits d'entrée, ce qui fait qu'elle calcule l'inverse de la retenue sortante. Le tout est illustré ci-dessous. En théorie, on devrait utiliser une porte NON pour récupérer la retenue correcte. Mais nous verrons plus bas que ce n'est pas une obligation. Nous verrons plus bas un exemple où c'est l'inverse de la retenue qui est utilisée dans les calculs, pour rendre les calculs plus rapides.
[[File:Demi-additionneur fait avec des portes NAND.png|centre|vignette|upright=2|Demi-additionneur fait avec des portes NAND]]
==Le circuit incrémenteur==
Maintenant que l'on sait comment additionner deux bits, reprenons l'opération d'incrémentation :
<math>a_7</math> <math>a_6</math> <math>a_5</math> <math>a_4</math> <math>a_3</math> <math>a_2</math> <math>a_1</math> <math>a_0</math>
+ 0 0 0 0 0 0 0 1
------------------------------
Sur la colonne la plus à droite, il doit ajouter un au bit de poids faible. Pour les colonnes suivantes, il faut additionner le bit de l'opérande avec la retenue de la colonne précédente. En clair, on n'additionne que deux bits à chaque colonne : un 1 sur celle tout à droite, la retenue de la colonne précédente pour les autres. Et cela nous donne une idée de comment faire pour créer le circuit incrémenteur.
===L'incrémenteur à propagation de retenue===
Il suffit d'utiliser un demi-additionneur par colonne, et de les enchainer les uns à la suite des autres. Chaque demi-additionneur additionne le bit de l'opérande avec la retenue fournie par le demi-additionneur précédent. Le seul qui fait exception est celui pour la colonne de poids faible. Pour celui-là, il doit ajouter 1 au bit de poids faible. Le résultat est appelé un '''incrémenteur à propagation de retenue'''. Il est constitué de demi-additionneurs enchaînés les uns à la suite des autres, du bit de poids faible vers le bit de poids fort.
[[File:Circuit incrémenteur.png|centre|vignette|upright=3|Circuit incrémenteur.]]
Maintenant, regardons le demi-additionneur le plus à droite, celui pour le bit de poids faible. Son entrée de retenue entrante est mise à 1 pour faire l'incrémentation. Quelques incrémenteurs permettent de configurer cette entrée de retenue à 0 ou à 1, ce qui effectue : soit une opération identité (l'opérande est recopié sur la sortie), soit une incrémentation. Un tel circuit est nommé un '''incrémenteur commandable'''. Nous aurons à utiliser une fois ou deux de tels incrémenteurs commandables dans la suite du cours.
L'incrémenteur à propagation de retenue est le plus simple et le plus économe en portes logiques. Mais de tels incrémenteurs sont rarement utilisés. À la place, on leur préfère des incrémenteurs plus rapides, mais qui utilisent plus de portes logiques. De tels incrémenteurs accélèrent le calcul des retenues. En effet, la rapidité d'une incrémentation est limitée par la propagation de la retenue : les retenues commencent à être calculées au bit de poids fort et on doit les calculer une par une, jusqu’à atteindre le bit de poids fort. Et cette "propagation des retenues" prend du temps, d'autant plus de temps que l'opérande est longue. Il y a deux optimisations principales, appelées le ''carry skip'' et l'anticipation de retenue, que nous allons décrire ci-dessous.
===Les incrémenteurs optimisés===
L'optimisation que nous allns voir effectue l'incrémentation, non pas bit par bit, mais par paquets de deux bits. Le résultat est que l'incrémentation est deux fois plus rapide, ou presque. Le circuit incrémenteur est donc composé en enchainant non pas des demi-additionneurs, mais des '''incrémenteurs 2 bits''' qui incrémentent un opérande de deux bits.
[[File:Circuit incrémenteur optimisé, faisant l'incrémentation par paquet de deux bits.png|centre|vignette|upright=2.5|Circuit incrémenteur optimisé, faisant l'incrémentation par paquet de deux bits]]
Reste à concevoir l'incrémenteur 2 bits. Une première méthode part de deux demi-additionneurs et effectue quelques simplifications au niveau des transistors et des portes logiques. Par exemple, l'Intel 8085 a utilisé un circuit optimisé, fabriqué avec des portes NAND et NOR. La raison est qu'avec les technologies de transistors CMOS actuelles, les portes NAND et NOR utilisent moins de transistors que les portes ET et OU. Et cette possibilité a été utilisée pour effectuer des simplifications assez mineures, mais toujours bonnes à prendre. Mais nous ne pouvons pas en parler en détail ici, car nous n'avons pas encore parlé en détail des transistors. Cependant, un exemple bien précis nous est accesible : celui de l’incrémenteur du processeur 8085 d'Intel.
Le processeur 8085 d'Intel est un processeur 8 bits très ancien. Il contenait un incrémenteur 16 bits, qui était utilisé pour calculer des adresses mémoire. Et cet incrémenteur était un incrémenteur à propagation de retenue optimisé. Il utilisait lui aussi des incrémenteurs 2 bits un peu modifiés.
L'idée était que les retenues sortant des colonnes paires étaient inversées, les colonnes impaires faisaient les calculs à partir de cette retenue inversée. Les demi-additionneurs des colonnes impaires n'étaient donc pas les mêmes que ceux des colonnes paires. Leurs tables de vérité sont différentes, leurs circuits aussi, et aucun des deux ne ressemble à un demi-additionneur normal. En réalité, ils sont fabriqués à partir des implémentations vues plus haut, qui utilisent uniquement des portes NAND ou uniquement des portes NOR.
Pour comprendre l'intérêt de faire ainsi, nous devons préciser une chose importante : avec les technologies CMOS utilisées pour les processeurs depuis les années 70, les portes logiques les plus simples sont les portes NON, NOR et NAND. Les portes ET/OU sont fabriquées en combinant des portes NOR/NAND avec une porte NON, ce qui prend plus de transistors.
Un demi-additionneur est donc fabriqué comme illustré ci-dessous, en logique CMOS. Seules les portes pour le calcul de la retenue sont indiquées, la porte XOR pour le bit de somme est fabriquée à partir de plusieurs portes logiques.
[[File:Demi-additionneur en CMOS.png|centre|vignette|upright=2|Demi-additionneur en CMOS]]
Si on enchaine deux demi-additionneurs, cela donne ceci :
[[File:Brique de base de l'incrémenteur du 8085.png|centre|vignette|upright=2|Brique de base de l'incrémenteur du 8085]]
La retenue doit donc traverser quatre portes logiques, dont deux portes NON. En inversant la retenue, les portes en jaune dans le schéma précédent se simplifient. Il suffit d'utiliser la loi de de Morgan pour trouver un circuit équivalent aux portes jaunes, qui n'est autre qu'une porte NOR couplée à une porte NON. Un autre moyen d'obtenir le même résultat est de prendre un demi-additionneur fabriqué avec uniquement des portes NOR, et de retirer la porte NON adéquate. Il faut ensuite corriger l'entrée de retenue de la porte XOR, pour qu'elle fasse l'addition avec la retenue correctement. Le résultat est illustré ci-dessous.
[[File:Incrémenteur 2 bits de l'incrémenteur du 8085.png|centre|vignette|upright=2|Incrémenteur 2 bits de l'incrémenteur du 8085]]
Le circuit utilise le même nombre de portes logiques, il n'économise pas de transistors. Par contre, la retenue n'a plus qu'à traverser deux portes logiques, au lieu de quatre. La propagation de la retenue est donc plus rapide, l'incrémentation est donc plus rapide.
===Les incrémenteurs 2 bits à calcul séparé de la retenue===
Il existe une autre méthode qui permet de créer des incrémenteurs 2 bits plus rapides que les précédents, mais qui utilisent plus de portes logiques. Ces incrémenteurs 2 bits calculent séparément les deux bits du résultat et la retenue sortante. La retenue sortante est calculée à partir des bits de l’opérande, sans avoir besoin de propager des retenues. Ainsi, la retenue sortante est connue en avance, sans passer par deux demi-additionneurs. Formellement, ils sont un cas particulier d'une optimisation appelée l'''anticipation de retenue''. Si nous verrons le cas général dans la section suivante, ce cas particulier est une très bonne introduction.
Pour créer un tel incrémenteur, le mieux est de partir de sa table de vérité.
{|class="wikitable"
|-
! Opérande !! Retenue entrante !! !! Retenue sortante !! Résultat
|-
| 00 || 0 || || 0 || 00
|-
| 01 || 0 || || 0 || 01
|-
| 10 || 0 || || 0 || 10
|-
| 11 || 0 || || 0 || 11
|-
| colspan="5" |
|-
| 00 || 1 || || 0 || 01
|-
| 01 || 1 || || 0 || 10
|-
| 10 || 1 || || 0 || 11
|-
| 11 || 1 || || 1 || 00
|}
Elle nous dit que la retenue sortante ne vaut 1 que dans un seul cas : les deux bits d'opérande valent 1, la retenue entrante vaut 1. Elle se calcule alors avec une porte ET à trois entrées. Pour les deux bits du résultat, les équations logiques ne donnent pas un résultat satisfaisant. La solution la plus simple est d'utiliser deux demi-additionneurs et de retirer les portes logiques superflues. Le résultat est alors un '''incrémenteur 2 bits ''carry skip''''', où la retenue sortante est calculée séparément du reste.
[[File:Incrémenteur carry skip 2 bits.png|centre|vignette|upright=2.5|Incrémenteur ''carry skip'' 2 bits]]
===Les incrémenteurs à anticipation de retenue===
L''''anticipation de retenue''' calcule la retenue d'une colonne sans attendre les retenues des colonnes précédentes. Dans le cas idéal, toutes les retenues sont calculées en parallèle, en même temps, et sont ensuite envoyées aux demi-additionneurs. Il s'agit d'une optimisation qui est utilisée pour l'incrémentation, l'addition et d'autres opérations similaires. Pour l'incrémentation, déterminer la retenue ne demande pas de calculs complexes, contrairement à l'addition.
[[File:Incrémenteur à anticipation de retenues, 8 bits.png|centre|vignette|upright=2.5|Incrémenteur à anticipation de retenues, 8 bits.]]
Pour le comprendre, on peut regarder comment la retenue sortant d'un demi-additionneur est formée. Elle est calculée en faisant un ET logique entre la retenue entrante et le bit d'opérande. Une retenue est donc un ET logique entre toutes les retenues précédentes. Un incrémenteur à anticipation de retenue utilise donc une porte ET à plusieurs entrées pour calculer une retenue.
[[File:Anticipation de retenue pour un bit du résultat, incrémenteur.png|centre|vignette|upright=2.5|Anticipation de retenue pour un bit du résultat, incrémenteur]]
En théorie, on peut utiliser une porte ET à plusieurs entrées pour chaque bit de l'opérande. Cependant, cela entrainera un cout en transistors très important. Pour éviter de gaspiller trop de portes logiques, une solution est de mélanger anticipation de retenues et propagation de retenue. Par exemple, pour un incrémenteur 32 bits, on peut découper l'opérande en 4 octets : on anticipe les retenues pour chaque octet, mais l'incrémentation de chaque octet se fait avec propagation de retenue et/ou ''carry skip''. Plus haut, nous avons vu un cas particulier où les retenues étaient anticipées par blocs de deux bits.
[[File:Incrémenteur à anticipation de retenues.png|centre|vignette|upright=3|Incrémenteur hybride utilisant partiellement l'anticipation de retenues.]]
Quelques processeurs utilisaient l'anticipation de retenues. Par exemple, le processeur Z-80 de Zilog utilisait un incrémenteur pour des nombres de 16 bits, ce qui demandait des performances assez élevées. Et cet incrémenteur utilisait à la fois anticipation de retenues et ''carry skip''. Il était découpé en quatre blocs avant anticipation de retenues entre eux : un bloc regroupant les 7 bits de poids faible, suivi par un bloc de 5 bits, lui-même suivi par un bloc de 3 bits, terminé par un dernier bit isolé. A l'intérieur de ces blocs, les bits sont regroupés en paires utilisant le ''carry skip''. Pour ceux qui veulent en savoir plus sur cet incrémenteur, voici un lien sur le sujet :
* [https://www.righto.com/2013/11/the-z-80s-16-bit-incrementdecrement.html The Z-80's 16-bit increment/decrement circuit reverse engineered].
==Les décrémenteurs==
Les circuits décrémenteurs font l'inverse d'un incrémenteur : au lieu d'ajouter 1 à un opérande, ils lui soustraient 1. La différence n'est pas énorme, ce qui fait que les circuits incrémenteurs et décrémenteurs se ressemblent beaucoup.
La soustraction se fait en binaire comme en décimal. On soustrait deux chiffres, puis on doit propager une éventuelle retenue sur la colonne suivante. La retenue apparait quand le chiffre soustrait est plus grand que l'autre chiffre. Elle est propagée sur la colonne suivante, où elle doit être soustraite du résultat. Pour le dire autrement, après avoir soustrait un chiffre, on doit de plus soustraire la retenue de la colonne précédente.
La décrémentation effectue le calcul suivant :
<math>a_7</math> <math>a_6</math> <math>a_5</math> <math>a_4</math> <math>a_3</math> <math>a_2</math> <math>a_1</math> <math>a_0</math>
- 0 0 0 0 0 0 0 1
La colonne la plus à droite soustrait un 1. Par contre, les colonnes suivantes soustraient un zéro. Soustraire un zéro ne change rien. Mais il ne faut pas oublier de soustraire une éventuellement retenue, propagée depuis la colonne précédente. En clair, un décrémenteur peut se construire si on sait soustraire deux bits. On soustrait un 1 sur la colonne la plus à droite, on soustrait une retenue sur les autres colonnes. Et pour soustraire deux bits, il faut utiliser un demi-soustracteur.
===Le demi-soustracteur et le décrémenteur à propagation de retenue===
Un '''demi-soustracteur''' soustrait deux bits. Il prend deux bits d'entrée et fournit deux sorties : un bit de différence et une retenue. Pour comprendre d'où vient cette retenue, et comment soustraire deux bits, traitons les quatre cas possibles un par un.
Premièrement, soustraire zéro à un bit ne changera rien :
* 0 - 0 = 0 ;
* 1 - 0 = 1.
Si on soustrait 1 à un bit qui vaut 1, on obtient zéro.
* 1 - 1 = 0.
Maintenant, que se passe-t-il si on soustrait 1 à 0 ? Voici le résultat :
* 0 - 1 = 1 et une retenue propagée sur la colonne suivante, où elle est soustraite.
La table de vérité est donc la suivante. Détail important : le demi-soustracteur fait ici la soustraction X - Y. En anglais, X est appelé le ''Minuend'' et Y est appelé le ''Subtrahend'', mais ces termes n'ont pas de traduction française. Aussi, je vais les utiliser tels quels dans ce qui suit.
{|class="wikitable"
|-
! ''Minuend'' !! ''Subtrahend'' !! !! Retenue sortante !! Bit de somme
|-
||0||0|| ||0||0
|-
||0||1|| ||1||1
|-
||1||0|| ||0||1
|-
||1||1|| ||0||0
|}
Si on construit la table de vérité, et qu'on construit le circuit associé, on tombe sur ce circuit.
[[File:Half Subtractor Vektor.svg|centre|vignette|Demi-soustracteur.]]
Vous remarquerez que le bit de somme se calcule de la même manière que pour l'addition, seul le calcul de la retenue change. Comme pour le demi-additionneur, on peut exploiter des redondances entre calcul de la retenue et porte XOR, mais ce ne sont pas les mêmes. Le circuit obtenu après exploitation de ces redondances est le suivant :
[[File:Demi-soustracteur optimisé, sans redondances.png|centre|vignette|upright=1.5|Demi-soustracteur optimisé, sans redondances]]
Un décrémenteur à propagation de retenue se construit comme un incrémenteur, sauf qu'on remplace les demi-additionneurs par des demi-soustracteurs. En clair, on enchaine plusieurs demi-soustracteurs les uns à la suite des autres.
===Le décrémenteur à anticipation de retenue===
Il est possible d'utiliser l'anticipation de retenue, mais les circuits sont cependant différents de l'incrémenteur. Idem avec le ''carry skip'', et toute autre optimisation possible. La raison est que les retenues sont calculées différemment, ce qui fait que l’anticipation de retenue doit être adaptée. La retenue d'une colonne ne se calcule plus avec un ET des bits des colonnes précédentes. A la place, ces bits doivent être inversés avant de faire le ET. Le résultat est que la porte ET est remplacée par une porte NOR.
Pour ceux qui s'en rappelle, faire un NOR de N bits permet de vérifier s'ils sont tous à zéro. Et c'est le cas ici. Lors d'une incrémentation, on a une retenue quand tous les bits précédents de l'opérande sont à 1. Pour la décrémentation, c'est l'inverse : on a une retenue quand tous les bits précédents sont à 0.
De fait, décrémenter un nombre est assez simple. Il y a deux cas principaux :
* soit le bit de poids faible est un 1, ce qui signifie que le nombre est impair ;
* soit un ou plusieurs bits de poids faible consécutifs sont à 0, suivis par un 1.
Dans le premier cas, le bit de poids faible est juste inversé. Dans le second cas, le 1 est inversé, et les 0 de poids faible deviennent des 1. Par exemple, Prenez un nombre de la forme xxxxxxx10000. Après décrémentation, le résultat sera xxxxxxxx011111 : les 0 de poids faible deviennent des 1, le premier 1 en partant de la droite est inversé. Et la même logique se généralise pour tout nombre avant un ou plusieurs zéros dans les bits de poids faible.
==Les incrémenteurs-décrémenteurs==
Les circuits décrémenteurs ressemblent beaucoup aux incrémenteurs. Aussi, vous ne serez pas étonné qu'il soit possible de fusionner un incrémenteur et un décrémenteur en un seul circuit. Un tel circuit s'appelle un '''incrémenteur-décrémenteur'''. Un tel circuit a une interface similaire à celle d'un incrémenteur, avec une entrée sur laquelle présenter l'opérande, et une sortie sur laquelle récupérer le résultat. Il a aussi une entrée de retenue, sur laquelle envoyer un 1 (incrémentation) ou un 0 (opération NOP/identité). Et il a aussi une sortie de retenue, qui indique que le résultat du calcul a débordé.
Mais en plus de cela, il dispose d'une entrée de commande qui permet de choisir entre incrémentation et décrémentation.
Il existe plusieurs manières de créer un incrémenteur-décrémenteur, que nous allons voir si dessous. Nous n'allons pas voir le cas le plus évident, aussi ne vous étonnez pas si vous avez remarqué qu'un demi-additionneur et un demi-soustracteur se ressemblent beaucoup.
===L'incrémenteur-décrémenteur basé sur un incrémenteur===
Une première implémentation se base sur les propriétés du complément à deux, et plus précisément sur cette équation :
: <math>\overline{x} + 1 = \overline{x - 1}</math>
Les deux termes de cette équation sont simplement des définitions équivalentes du complément à deux d'un nombre entier. Celui-ci se calcule de deux manières : soit on incrémente son complément à 1, soit on prend le complément à 1 de son décrément. Et cela nous permet de déterminer comment obtenir une incrémentation à partir d'une décrémentation et inversement.
Que nous dit cette équation ? Simplement que l'on peut décrémenter un nombre si on a deux inverseurs commandables, et un circuit incrémenteur. La décrémentation se fait alors en deux étapes : on calcule son complément à deux, puis on inverse les bits du résultat précédent. Le calcul du complément à deux demande d'avoir un circuit inverseur, et un incrémenteur.
===L'incrémenteur-décrémenteur basé sur des demi-additionneurs===
Vous l'aurez sans doute remarqué, mais le demi-soustracteur ressemble beaucoup au demi-additionneur. La seule différence est une porte NON ajoutée au bon endroit dans le demi-soustracteur. La porte NON inverse le bit X, sachant que le demi-soustracteur fait la soustraction X - Y. De plus, l'opérande inversée est utilisée seulement pour le calcul des retenues, le bit de somme est calculé normalement.
{|
|[[File:Half-adder.svg|vignette|Demi-additionneur]]
|[[File:Half Subtractor Vektor.svg|vignette|upright=1.3|Demi-soustracteur]]
|}
La conséquence est qu'il est possible de fusionner un demi-additionneur avec un demi-soustracteur. L'idée est de remplacer la porte NON du demi-soustracteur par un inverseur commandable. Si on envoie un zéro sur l'entrée de commande, l'inverseur commandable se comportera comme une porte OUI et le circuit sera alors identique à un demi-additionneur. Si on envoie un 1 sur l'entrée de commande, l'inverseur commandable se comportera comme une porte NON. le circuit sera alors un demi-soustracteur. En remplaçant les demi-additionneurs (ou demi-soustracteurs) par de tels circuits, on peut obtenir un circuit faisant à la fois incrémenteur et décrémenteur.
Un défaut de cette approche est que les optimisations vues plus haut pour les demi-additionneurs ne fonctionne tout simplement plus. On est obligé d'utiliser une porte XOR et une porte ET séparées, pas question d'utiliser les demi-additionneurs optimisés vu au début du chapitre.
<noinclude>
{{NavChapitre | book=Fonctionnement d'un ordinateur
| prev=Les circuits de sélection
| prevText=Les circuits de sélection
| next=Les bascules : des mémoires de 1 bit
| nextText=Les bascules : des mémoires de 1 bit
}}
</noinclude>
mjs0r1u4as178fuma4mdx58li57kqtn
768807
768806
2026-06-27T19:01:07Z
Mewtow
31375
/* L'incrémenteur à propagation de retenue */
768807
wikitext
text/x-wiki
Le circuit '''incrémenteur''' incrémente un nombre entier. Plus précisément, il prend en entrée un nombre entier, et fournit en entier le nombre suivant, il lui ajoute 1. Une telle opération peut paraitre triviale et peu intéressante. Cependant, elle est très utilisée et est absolument cruciale pour créer des compteurs, des circuits capables de compter ou décompter, qui auront droit à un chapitre dédié. De plus, au-delà de leur utilisation dans les compteurs, les incrémenteurs étaient très utilisés sur les premiers processeurs 8 bits, comme le Z-80, le 6502, les premiers processeurs x86 comme le 8008, le 8086, le 8085, et bien d'autres.
Il existe aussi des ''circuits décrémenteurs'', qui décrémentent un opérande, ainsi que des ''circuits incrémenteur-décrémenteur'', qui peuvent incrémenter ou décrémenter, selon comment on les configure. Je ne vais pas détailler ces circuits plus que ça, car de tels circuits sont assez rares, comparé à un circuit incrémenteur simple.
==Le demi-additionneur==
Le circuit incrémenteur effectue l'opération suivante :
<math>a_7</math> <math>a_6</math> <math>a_5</math> <math>a_4</math> <math>a_3</math> <math>a_2</math> <math>a_1</math> <math>a_0</math>
+ 0 0 0 0 0 0 0 1
Un incrémenteur basique doit donc faire une addition pour chaque colonne, et précisément une addition de deux bits. Il se trouve que la table d'addition est très simple en binaire. Jugez plutôt :
* 0 + 0 = 0, retenue = 0 ;
* 0 + 1 = 1, retenue = 0 ;
* 1 + 0 = 1, retenue = 0 ;
* 1 + 1 = 0, retenue = 1.
Un circuit capable d'additionner deux bits est appelé un '''demi-additionneur'''. Il dispose d'une sortie S pour la somme, et C pour la retenue (''carry'' an anglais).
===Le demi-additionneur classique===
Un demi-additionneur est très simple à construire avec les techniques vues dans les premiers chapitres. Voici sa table de vérité :
{|class="wikitable"
|-
! Retenue entrante !! Opérande 1 !! !! Retenue sortante !! Bit de somme
|-
||0||0|| ||0||0
|-
||0||1|| ||0||1
|-
||1||0|| ||0||1
|-
||1||1|| ||1||0
|}
On voit immédiatement que la colonne des retenues donne une porte ET, alors que celle du bit de somme est calculé par un XOR.
{| class="flexible"
|[[File:1-bit half-adder.svg|class=transparent|centre|Demi-addtionneur.]]
|[[File:Half-adder.svg|centre|class=transparent|Circuit d'un demi-addtionneur.]]
|}
Il existe beaucoup d'autres implémentations alternatives, qui utilisent moins de portes logiques, calculent la retenue plus rapidement, consomment moins d'énergie, et autres. Et il est intéressant d'étudier quelques alternatives, surtout qu'elles serviront plus bas, quand on étudiera le circuit incrémenteur du 8085.
===Le demi-additionneur basé sur une modification de la retenue===
Une source d'amélioration est liée à la porte XOR. En pratique, une porte XOR est composée en combinant plusieurs portes logiques ET/OU/NOR/NAND ensemble. Et il se trouve qu'il y a des redondances entre celles-ci et les portes utilisées pour calculer la retenue. Par exemple, rappelons qu'une porte XOR peut être construite avec une porte ET et deux portes NOR, comme illustré ci-dessous. Et il se trouve que la porte ET interne calcule la retenue sortante. En supprimant cette redondance, on économise quelques portes logiques. De plus, on se retrouve avec un demi-additionneur qui calcule le bit de somme à partir de la retenue sortante.
[[File:Demi-additionneur avec redondances éliminées.png|centre|vignette|upright=2|Demi-additionneur avec redondances éliminées]]
Le circuit précédent a une interprétation logique. Si vous regardez la table de vérité, vous remarquerez que la somme de deux bits est égale à l'inverse de la retenue sortante, sauf dans le cas où les deux bits additionnés valent zéro. Et le circuit précédent est basé là-dessus.
{|class="wikitable"
|-
! Retenue entrante !! Opérande 1 !! !! Retenue sortante !! Bit de somme
|- class="f_vert"
||0||0|| ||0||0
|- class="f_rouge"
||0||1|| ||0||1
|- class="f_rouge"
||1||0|| ||0||1
|- class="f_rouge"
||1||1|| ||1||0
|}
L'idée est de calculer l'inverse du bit de somme, avant de l'inverser avec une porte NON. L'inverse du bit de somme vaut 1, soit quand la retenue est à 1, soit quand les deux bits additionnés sont à 0. La porte ET calcule la retenue sortante, la première porte NOR détecte sur les deux bits d'entrée valent zéro. Une porte OU combine les deux résultat pour obtenir l'inverse du bit d'entrée, puis une porte NOn inverse le tout pour obtenir le bit de somme adéquat. Les deux portes sont fusionnées : c'est la seconde porte NOR.
[[File:Full adder HA + MUX.png|centre|vignette|upright=2.5|Demi-additionneur basé sur une inversion de la retenue sortante.]]
===Les implémentations alternatives===
Une implémentation alternative, qui n'utilise que des portes NOR, est la suivante :
[[File:Demi-additionneur fait avec des portes NOR et NAND.png|centre|vignette|upright=2|Demi-additionneur fait avec des portes NOR et NAND.]]
Une implémentation alternative utilise des portes NAND. Pour rappel, il est possible de créer une porte XOR avec 4 portes NAND. La première d'entre elle fait un NAND entre les deux bits d'entrée, ce qui fait qu'elle calcule l'inverse de la retenue sortante. Le tout est illustré ci-dessous. En théorie, on devrait utiliser une porte NON pour récupérer la retenue correcte. Mais nous verrons plus bas que ce n'est pas une obligation. Nous verrons plus bas un exemple où c'est l'inverse de la retenue qui est utilisée dans les calculs, pour rendre les calculs plus rapides.
[[File:Demi-additionneur fait avec des portes NAND.png|centre|vignette|upright=2|Demi-additionneur fait avec des portes NAND]]
==Le circuit incrémenteur==
Maintenant que l'on sait comment additionner deux bits, reprenons l'opération d'incrémentation :
<math>a_7</math> <math>a_6</math> <math>a_5</math> <math>a_4</math> <math>a_3</math> <math>a_2</math> <math>a_1</math> <math>a_0</math>
+ 0 0 0 0 0 0 0 1
------------------------------
Sur la colonne la plus à droite, il doit ajouter un au bit de poids faible. Pour les colonnes suivantes, il faut additionner le bit de l'opérande avec la retenue de la colonne précédente. En clair, on n'additionne que deux bits à chaque colonne : un 1 sur celle tout à droite, la retenue de la colonne précédente pour les autres. Et cela nous donne une idée de comment faire pour créer le circuit incrémenteur.
===L'incrémenteur à propagation de retenue===
Il suffit d'utiliser un demi-additionneur par colonne, et de les enchainer les uns à la suite des autres. Chaque demi-additionneur additionne le bit de l'opérande avec la retenue fournie par le demi-additionneur précédent. Le seul qui fait exception est celui pour la colonne de poids faible. Pour celui-là, il doit ajouter 1 au bit de poids faible. Le résultat est appelé un '''incrémenteur à propagation de retenue'''. Il est constitué de demi-additionneurs enchaînés les uns à la suite des autres, du bit de poids faible vers le bit de poids fort.
[[File:Circuit incrémenteur.png|centre|vignette|upright=3|Circuit incrémenteur.]]
Maintenant, regardons le demi-additionneur le plus à droite, celui pour le bit de poids faible. Son entrée de retenue entrante est mise à 1 pour faire l'incrémentation. Quelques incrémenteurs permettent de configurer cette entrée de retenue à 0 ou à 1, ce qui effectue : soit une opération identité (l'opérande est recopié sur la sortie), soit une incrémentation. Un tel circuit est nommé un '''incrémenteur commandable'''. Nous aurons à utiliser une fois ou deux de tels incrémenteurs commandables dans la suite du cours.
La '''sortie de retenue''' indique si l'incrémentation a entrainé un débordement d'entier. Lors d'un débordement d'entier, la retenue de sortie est à 1. Elle est à 0 en absence de débordement d'entier. La sortie de retenue est présente sur la plupart des incrémenteurs, mais laissons cela de côté pour le moment.
L'incrémenteur à propagation de retenue est le plus simple et le plus économe en portes logiques. Mais de tels incrémenteurs sont rarement utilisés. À la place, on leur préfère des incrémenteurs plus rapides, mais qui utilisent plus de portes logiques. De tels incrémenteurs accélèrent le calcul des retenues. En effet, la rapidité d'une incrémentation est limitée par la propagation de la retenue : les retenues commencent à être calculées au bit de poids fort et on doit les calculer une par une, jusqu’à atteindre le bit de poids fort. Et cette "propagation des retenues" prend du temps, d'autant plus de temps que l'opérande est longue. Il y a deux optimisations principales, appelées le ''carry skip'' et l'anticipation de retenue, que nous allons décrire ci-dessous.
===Les incrémenteurs optimisés===
L'optimisation que nous allns voir effectue l'incrémentation, non pas bit par bit, mais par paquets de deux bits. Le résultat est que l'incrémentation est deux fois plus rapide, ou presque. Le circuit incrémenteur est donc composé en enchainant non pas des demi-additionneurs, mais des '''incrémenteurs 2 bits''' qui incrémentent un opérande de deux bits.
[[File:Circuit incrémenteur optimisé, faisant l'incrémentation par paquet de deux bits.png|centre|vignette|upright=2.5|Circuit incrémenteur optimisé, faisant l'incrémentation par paquet de deux bits]]
Reste à concevoir l'incrémenteur 2 bits. Une première méthode part de deux demi-additionneurs et effectue quelques simplifications au niveau des transistors et des portes logiques. Par exemple, l'Intel 8085 a utilisé un circuit optimisé, fabriqué avec des portes NAND et NOR. La raison est qu'avec les technologies de transistors CMOS actuelles, les portes NAND et NOR utilisent moins de transistors que les portes ET et OU. Et cette possibilité a été utilisée pour effectuer des simplifications assez mineures, mais toujours bonnes à prendre. Mais nous ne pouvons pas en parler en détail ici, car nous n'avons pas encore parlé en détail des transistors. Cependant, un exemple bien précis nous est accesible : celui de l’incrémenteur du processeur 8085 d'Intel.
Le processeur 8085 d'Intel est un processeur 8 bits très ancien. Il contenait un incrémenteur 16 bits, qui était utilisé pour calculer des adresses mémoire. Et cet incrémenteur était un incrémenteur à propagation de retenue optimisé. Il utilisait lui aussi des incrémenteurs 2 bits un peu modifiés.
L'idée était que les retenues sortant des colonnes paires étaient inversées, les colonnes impaires faisaient les calculs à partir de cette retenue inversée. Les demi-additionneurs des colonnes impaires n'étaient donc pas les mêmes que ceux des colonnes paires. Leurs tables de vérité sont différentes, leurs circuits aussi, et aucun des deux ne ressemble à un demi-additionneur normal. En réalité, ils sont fabriqués à partir des implémentations vues plus haut, qui utilisent uniquement des portes NAND ou uniquement des portes NOR.
Pour comprendre l'intérêt de faire ainsi, nous devons préciser une chose importante : avec les technologies CMOS utilisées pour les processeurs depuis les années 70, les portes logiques les plus simples sont les portes NON, NOR et NAND. Les portes ET/OU sont fabriquées en combinant des portes NOR/NAND avec une porte NON, ce qui prend plus de transistors.
Un demi-additionneur est donc fabriqué comme illustré ci-dessous, en logique CMOS. Seules les portes pour le calcul de la retenue sont indiquées, la porte XOR pour le bit de somme est fabriquée à partir de plusieurs portes logiques.
[[File:Demi-additionneur en CMOS.png|centre|vignette|upright=2|Demi-additionneur en CMOS]]
Si on enchaine deux demi-additionneurs, cela donne ceci :
[[File:Brique de base de l'incrémenteur du 8085.png|centre|vignette|upright=2|Brique de base de l'incrémenteur du 8085]]
La retenue doit donc traverser quatre portes logiques, dont deux portes NON. En inversant la retenue, les portes en jaune dans le schéma précédent se simplifient. Il suffit d'utiliser la loi de de Morgan pour trouver un circuit équivalent aux portes jaunes, qui n'est autre qu'une porte NOR couplée à une porte NON. Un autre moyen d'obtenir le même résultat est de prendre un demi-additionneur fabriqué avec uniquement des portes NOR, et de retirer la porte NON adéquate. Il faut ensuite corriger l'entrée de retenue de la porte XOR, pour qu'elle fasse l'addition avec la retenue correctement. Le résultat est illustré ci-dessous.
[[File:Incrémenteur 2 bits de l'incrémenteur du 8085.png|centre|vignette|upright=2|Incrémenteur 2 bits de l'incrémenteur du 8085]]
Le circuit utilise le même nombre de portes logiques, il n'économise pas de transistors. Par contre, la retenue n'a plus qu'à traverser deux portes logiques, au lieu de quatre. La propagation de la retenue est donc plus rapide, l'incrémentation est donc plus rapide.
===Les incrémenteurs 2 bits à calcul séparé de la retenue===
Il existe une autre méthode qui permet de créer des incrémenteurs 2 bits plus rapides que les précédents, mais qui utilisent plus de portes logiques. Ces incrémenteurs 2 bits calculent séparément les deux bits du résultat et la retenue sortante. La retenue sortante est calculée à partir des bits de l’opérande, sans avoir besoin de propager des retenues. Ainsi, la retenue sortante est connue en avance, sans passer par deux demi-additionneurs. Formellement, ils sont un cas particulier d'une optimisation appelée l'''anticipation de retenue''. Si nous verrons le cas général dans la section suivante, ce cas particulier est une très bonne introduction.
Pour créer un tel incrémenteur, le mieux est de partir de sa table de vérité.
{|class="wikitable"
|-
! Opérande !! Retenue entrante !! !! Retenue sortante !! Résultat
|-
| 00 || 0 || || 0 || 00
|-
| 01 || 0 || || 0 || 01
|-
| 10 || 0 || || 0 || 10
|-
| 11 || 0 || || 0 || 11
|-
| colspan="5" |
|-
| 00 || 1 || || 0 || 01
|-
| 01 || 1 || || 0 || 10
|-
| 10 || 1 || || 0 || 11
|-
| 11 || 1 || || 1 || 00
|}
Elle nous dit que la retenue sortante ne vaut 1 que dans un seul cas : les deux bits d'opérande valent 1, la retenue entrante vaut 1. Elle se calcule alors avec une porte ET à trois entrées. Pour les deux bits du résultat, les équations logiques ne donnent pas un résultat satisfaisant. La solution la plus simple est d'utiliser deux demi-additionneurs et de retirer les portes logiques superflues. Le résultat est alors un '''incrémenteur 2 bits ''carry skip''''', où la retenue sortante est calculée séparément du reste.
[[File:Incrémenteur carry skip 2 bits.png|centre|vignette|upright=2.5|Incrémenteur ''carry skip'' 2 bits]]
===Les incrémenteurs à anticipation de retenue===
L''''anticipation de retenue''' calcule la retenue d'une colonne sans attendre les retenues des colonnes précédentes. Dans le cas idéal, toutes les retenues sont calculées en parallèle, en même temps, et sont ensuite envoyées aux demi-additionneurs. Il s'agit d'une optimisation qui est utilisée pour l'incrémentation, l'addition et d'autres opérations similaires. Pour l'incrémentation, déterminer la retenue ne demande pas de calculs complexes, contrairement à l'addition.
[[File:Incrémenteur à anticipation de retenues, 8 bits.png|centre|vignette|upright=2.5|Incrémenteur à anticipation de retenues, 8 bits.]]
Pour le comprendre, on peut regarder comment la retenue sortant d'un demi-additionneur est formée. Elle est calculée en faisant un ET logique entre la retenue entrante et le bit d'opérande. Une retenue est donc un ET logique entre toutes les retenues précédentes. Un incrémenteur à anticipation de retenue utilise donc une porte ET à plusieurs entrées pour calculer une retenue.
[[File:Anticipation de retenue pour un bit du résultat, incrémenteur.png|centre|vignette|upright=2.5|Anticipation de retenue pour un bit du résultat, incrémenteur]]
En théorie, on peut utiliser une porte ET à plusieurs entrées pour chaque bit de l'opérande. Cependant, cela entrainera un cout en transistors très important. Pour éviter de gaspiller trop de portes logiques, une solution est de mélanger anticipation de retenues et propagation de retenue. Par exemple, pour un incrémenteur 32 bits, on peut découper l'opérande en 4 octets : on anticipe les retenues pour chaque octet, mais l'incrémentation de chaque octet se fait avec propagation de retenue et/ou ''carry skip''. Plus haut, nous avons vu un cas particulier où les retenues étaient anticipées par blocs de deux bits.
[[File:Incrémenteur à anticipation de retenues.png|centre|vignette|upright=3|Incrémenteur hybride utilisant partiellement l'anticipation de retenues.]]
Quelques processeurs utilisaient l'anticipation de retenues. Par exemple, le processeur Z-80 de Zilog utilisait un incrémenteur pour des nombres de 16 bits, ce qui demandait des performances assez élevées. Et cet incrémenteur utilisait à la fois anticipation de retenues et ''carry skip''. Il était découpé en quatre blocs avant anticipation de retenues entre eux : un bloc regroupant les 7 bits de poids faible, suivi par un bloc de 5 bits, lui-même suivi par un bloc de 3 bits, terminé par un dernier bit isolé. A l'intérieur de ces blocs, les bits sont regroupés en paires utilisant le ''carry skip''. Pour ceux qui veulent en savoir plus sur cet incrémenteur, voici un lien sur le sujet :
* [https://www.righto.com/2013/11/the-z-80s-16-bit-incrementdecrement.html The Z-80's 16-bit increment/decrement circuit reverse engineered].
==Les décrémenteurs==
Les circuits décrémenteurs font l'inverse d'un incrémenteur : au lieu d'ajouter 1 à un opérande, ils lui soustraient 1. La différence n'est pas énorme, ce qui fait que les circuits incrémenteurs et décrémenteurs se ressemblent beaucoup.
La soustraction se fait en binaire comme en décimal. On soustrait deux chiffres, puis on doit propager une éventuelle retenue sur la colonne suivante. La retenue apparait quand le chiffre soustrait est plus grand que l'autre chiffre. Elle est propagée sur la colonne suivante, où elle doit être soustraite du résultat. Pour le dire autrement, après avoir soustrait un chiffre, on doit de plus soustraire la retenue de la colonne précédente.
La décrémentation effectue le calcul suivant :
<math>a_7</math> <math>a_6</math> <math>a_5</math> <math>a_4</math> <math>a_3</math> <math>a_2</math> <math>a_1</math> <math>a_0</math>
- 0 0 0 0 0 0 0 1
La colonne la plus à droite soustrait un 1. Par contre, les colonnes suivantes soustraient un zéro. Soustraire un zéro ne change rien. Mais il ne faut pas oublier de soustraire une éventuellement retenue, propagée depuis la colonne précédente. En clair, un décrémenteur peut se construire si on sait soustraire deux bits. On soustrait un 1 sur la colonne la plus à droite, on soustrait une retenue sur les autres colonnes. Et pour soustraire deux bits, il faut utiliser un demi-soustracteur.
===Le demi-soustracteur et le décrémenteur à propagation de retenue===
Un '''demi-soustracteur''' soustrait deux bits. Il prend deux bits d'entrée et fournit deux sorties : un bit de différence et une retenue. Pour comprendre d'où vient cette retenue, et comment soustraire deux bits, traitons les quatre cas possibles un par un.
Premièrement, soustraire zéro à un bit ne changera rien :
* 0 - 0 = 0 ;
* 1 - 0 = 1.
Si on soustrait 1 à un bit qui vaut 1, on obtient zéro.
* 1 - 1 = 0.
Maintenant, que se passe-t-il si on soustrait 1 à 0 ? Voici le résultat :
* 0 - 1 = 1 et une retenue propagée sur la colonne suivante, où elle est soustraite.
La table de vérité est donc la suivante. Détail important : le demi-soustracteur fait ici la soustraction X - Y. En anglais, X est appelé le ''Minuend'' et Y est appelé le ''Subtrahend'', mais ces termes n'ont pas de traduction française. Aussi, je vais les utiliser tels quels dans ce qui suit.
{|class="wikitable"
|-
! ''Minuend'' !! ''Subtrahend'' !! !! Retenue sortante !! Bit de somme
|-
||0||0|| ||0||0
|-
||0||1|| ||1||1
|-
||1||0|| ||0||1
|-
||1||1|| ||0||0
|}
Si on construit la table de vérité, et qu'on construit le circuit associé, on tombe sur ce circuit.
[[File:Half Subtractor Vektor.svg|centre|vignette|Demi-soustracteur.]]
Vous remarquerez que le bit de somme se calcule de la même manière que pour l'addition, seul le calcul de la retenue change. Comme pour le demi-additionneur, on peut exploiter des redondances entre calcul de la retenue et porte XOR, mais ce ne sont pas les mêmes. Le circuit obtenu après exploitation de ces redondances est le suivant :
[[File:Demi-soustracteur optimisé, sans redondances.png|centre|vignette|upright=1.5|Demi-soustracteur optimisé, sans redondances]]
Un décrémenteur à propagation de retenue se construit comme un incrémenteur, sauf qu'on remplace les demi-additionneurs par des demi-soustracteurs. En clair, on enchaine plusieurs demi-soustracteurs les uns à la suite des autres.
===Le décrémenteur à anticipation de retenue===
Il est possible d'utiliser l'anticipation de retenue, mais les circuits sont cependant différents de l'incrémenteur. Idem avec le ''carry skip'', et toute autre optimisation possible. La raison est que les retenues sont calculées différemment, ce qui fait que l’anticipation de retenue doit être adaptée. La retenue d'une colonne ne se calcule plus avec un ET des bits des colonnes précédentes. A la place, ces bits doivent être inversés avant de faire le ET. Le résultat est que la porte ET est remplacée par une porte NOR.
Pour ceux qui s'en rappelle, faire un NOR de N bits permet de vérifier s'ils sont tous à zéro. Et c'est le cas ici. Lors d'une incrémentation, on a une retenue quand tous les bits précédents de l'opérande sont à 1. Pour la décrémentation, c'est l'inverse : on a une retenue quand tous les bits précédents sont à 0.
De fait, décrémenter un nombre est assez simple. Il y a deux cas principaux :
* soit le bit de poids faible est un 1, ce qui signifie que le nombre est impair ;
* soit un ou plusieurs bits de poids faible consécutifs sont à 0, suivis par un 1.
Dans le premier cas, le bit de poids faible est juste inversé. Dans le second cas, le 1 est inversé, et les 0 de poids faible deviennent des 1. Par exemple, Prenez un nombre de la forme xxxxxxx10000. Après décrémentation, le résultat sera xxxxxxxx011111 : les 0 de poids faible deviennent des 1, le premier 1 en partant de la droite est inversé. Et la même logique se généralise pour tout nombre avant un ou plusieurs zéros dans les bits de poids faible.
==Les incrémenteurs-décrémenteurs==
Les circuits décrémenteurs ressemblent beaucoup aux incrémenteurs. Aussi, vous ne serez pas étonné qu'il soit possible de fusionner un incrémenteur et un décrémenteur en un seul circuit. Un tel circuit s'appelle un '''incrémenteur-décrémenteur'''. Un tel circuit a une interface similaire à celle d'un incrémenteur, avec une entrée sur laquelle présenter l'opérande, et une sortie sur laquelle récupérer le résultat. Il a aussi une entrée de retenue, sur laquelle envoyer un 1 (incrémentation) ou un 0 (opération NOP/identité). Et il a aussi une sortie de retenue, qui indique que le résultat du calcul a débordé.
Mais en plus de cela, il dispose d'une entrée de commande qui permet de choisir entre incrémentation et décrémentation.
Il existe plusieurs manières de créer un incrémenteur-décrémenteur, que nous allons voir si dessous. Nous n'allons pas voir le cas le plus évident, aussi ne vous étonnez pas si vous avez remarqué qu'un demi-additionneur et un demi-soustracteur se ressemblent beaucoup.
===L'incrémenteur-décrémenteur basé sur un incrémenteur===
Une première implémentation se base sur les propriétés du complément à deux, et plus précisément sur cette équation :
: <math>\overline{x} + 1 = \overline{x - 1}</math>
Les deux termes de cette équation sont simplement des définitions équivalentes du complément à deux d'un nombre entier. Celui-ci se calcule de deux manières : soit on incrémente son complément à 1, soit on prend le complément à 1 de son décrément. Et cela nous permet de déterminer comment obtenir une incrémentation à partir d'une décrémentation et inversement.
Que nous dit cette équation ? Simplement que l'on peut décrémenter un nombre si on a deux inverseurs commandables, et un circuit incrémenteur. La décrémentation se fait alors en deux étapes : on calcule son complément à deux, puis on inverse les bits du résultat précédent. Le calcul du complément à deux demande d'avoir un circuit inverseur, et un incrémenteur.
===L'incrémenteur-décrémenteur basé sur des demi-additionneurs===
Vous l'aurez sans doute remarqué, mais le demi-soustracteur ressemble beaucoup au demi-additionneur. La seule différence est une porte NON ajoutée au bon endroit dans le demi-soustracteur. La porte NON inverse le bit X, sachant que le demi-soustracteur fait la soustraction X - Y. De plus, l'opérande inversée est utilisée seulement pour le calcul des retenues, le bit de somme est calculé normalement.
{|
|[[File:Half-adder.svg|vignette|Demi-additionneur]]
|[[File:Half Subtractor Vektor.svg|vignette|upright=1.3|Demi-soustracteur]]
|}
La conséquence est qu'il est possible de fusionner un demi-additionneur avec un demi-soustracteur. L'idée est de remplacer la porte NON du demi-soustracteur par un inverseur commandable. Si on envoie un zéro sur l'entrée de commande, l'inverseur commandable se comportera comme une porte OUI et le circuit sera alors identique à un demi-additionneur. Si on envoie un 1 sur l'entrée de commande, l'inverseur commandable se comportera comme une porte NON. le circuit sera alors un demi-soustracteur. En remplaçant les demi-additionneurs (ou demi-soustracteurs) par de tels circuits, on peut obtenir un circuit faisant à la fois incrémenteur et décrémenteur.
Un défaut de cette approche est que les optimisations vues plus haut pour les demi-additionneurs ne fonctionne tout simplement plus. On est obligé d'utiliser une porte XOR et une porte ET séparées, pas question d'utiliser les demi-additionneurs optimisés vu au début du chapitre.
<noinclude>
{{NavChapitre | book=Fonctionnement d'un ordinateur
| prev=Les circuits de sélection
| prevText=Les circuits de sélection
| next=Les bascules : des mémoires de 1 bit
| nextText=Les bascules : des mémoires de 1 bit
}}
</noinclude>
raodlnkeecwkbmz5ai010hirz8uagc1
768808
768807
2026-06-27T19:02:16Z
Mewtow
31375
/* Les incrémenteurs-décrémenteurs */
768808
wikitext
text/x-wiki
Le circuit '''incrémenteur''' incrémente un nombre entier. Plus précisément, il prend en entrée un nombre entier, et fournit en entier le nombre suivant, il lui ajoute 1. Une telle opération peut paraitre triviale et peu intéressante. Cependant, elle est très utilisée et est absolument cruciale pour créer des compteurs, des circuits capables de compter ou décompter, qui auront droit à un chapitre dédié. De plus, au-delà de leur utilisation dans les compteurs, les incrémenteurs étaient très utilisés sur les premiers processeurs 8 bits, comme le Z-80, le 6502, les premiers processeurs x86 comme le 8008, le 8086, le 8085, et bien d'autres.
Il existe aussi des ''circuits décrémenteurs'', qui décrémentent un opérande, ainsi que des ''circuits incrémenteur-décrémenteur'', qui peuvent incrémenter ou décrémenter, selon comment on les configure. Je ne vais pas détailler ces circuits plus que ça, car de tels circuits sont assez rares, comparé à un circuit incrémenteur simple.
==Le demi-additionneur==
Le circuit incrémenteur effectue l'opération suivante :
<math>a_7</math> <math>a_6</math> <math>a_5</math> <math>a_4</math> <math>a_3</math> <math>a_2</math> <math>a_1</math> <math>a_0</math>
+ 0 0 0 0 0 0 0 1
Un incrémenteur basique doit donc faire une addition pour chaque colonne, et précisément une addition de deux bits. Il se trouve que la table d'addition est très simple en binaire. Jugez plutôt :
* 0 + 0 = 0, retenue = 0 ;
* 0 + 1 = 1, retenue = 0 ;
* 1 + 0 = 1, retenue = 0 ;
* 1 + 1 = 0, retenue = 1.
Un circuit capable d'additionner deux bits est appelé un '''demi-additionneur'''. Il dispose d'une sortie S pour la somme, et C pour la retenue (''carry'' an anglais).
===Le demi-additionneur classique===
Un demi-additionneur est très simple à construire avec les techniques vues dans les premiers chapitres. Voici sa table de vérité :
{|class="wikitable"
|-
! Retenue entrante !! Opérande 1 !! !! Retenue sortante !! Bit de somme
|-
||0||0|| ||0||0
|-
||0||1|| ||0||1
|-
||1||0|| ||0||1
|-
||1||1|| ||1||0
|}
On voit immédiatement que la colonne des retenues donne une porte ET, alors que celle du bit de somme est calculé par un XOR.
{| class="flexible"
|[[File:1-bit half-adder.svg|class=transparent|centre|Demi-addtionneur.]]
|[[File:Half-adder.svg|centre|class=transparent|Circuit d'un demi-addtionneur.]]
|}
Il existe beaucoup d'autres implémentations alternatives, qui utilisent moins de portes logiques, calculent la retenue plus rapidement, consomment moins d'énergie, et autres. Et il est intéressant d'étudier quelques alternatives, surtout qu'elles serviront plus bas, quand on étudiera le circuit incrémenteur du 8085.
===Le demi-additionneur basé sur une modification de la retenue===
Une source d'amélioration est liée à la porte XOR. En pratique, une porte XOR est composée en combinant plusieurs portes logiques ET/OU/NOR/NAND ensemble. Et il se trouve qu'il y a des redondances entre celles-ci et les portes utilisées pour calculer la retenue. Par exemple, rappelons qu'une porte XOR peut être construite avec une porte ET et deux portes NOR, comme illustré ci-dessous. Et il se trouve que la porte ET interne calcule la retenue sortante. En supprimant cette redondance, on économise quelques portes logiques. De plus, on se retrouve avec un demi-additionneur qui calcule le bit de somme à partir de la retenue sortante.
[[File:Demi-additionneur avec redondances éliminées.png|centre|vignette|upright=2|Demi-additionneur avec redondances éliminées]]
Le circuit précédent a une interprétation logique. Si vous regardez la table de vérité, vous remarquerez que la somme de deux bits est égale à l'inverse de la retenue sortante, sauf dans le cas où les deux bits additionnés valent zéro. Et le circuit précédent est basé là-dessus.
{|class="wikitable"
|-
! Retenue entrante !! Opérande 1 !! !! Retenue sortante !! Bit de somme
|- class="f_vert"
||0||0|| ||0||0
|- class="f_rouge"
||0||1|| ||0||1
|- class="f_rouge"
||1||0|| ||0||1
|- class="f_rouge"
||1||1|| ||1||0
|}
L'idée est de calculer l'inverse du bit de somme, avant de l'inverser avec une porte NON. L'inverse du bit de somme vaut 1, soit quand la retenue est à 1, soit quand les deux bits additionnés sont à 0. La porte ET calcule la retenue sortante, la première porte NOR détecte sur les deux bits d'entrée valent zéro. Une porte OU combine les deux résultat pour obtenir l'inverse du bit d'entrée, puis une porte NOn inverse le tout pour obtenir le bit de somme adéquat. Les deux portes sont fusionnées : c'est la seconde porte NOR.
[[File:Full adder HA + MUX.png|centre|vignette|upright=2.5|Demi-additionneur basé sur une inversion de la retenue sortante.]]
===Les implémentations alternatives===
Une implémentation alternative, qui n'utilise que des portes NOR, est la suivante :
[[File:Demi-additionneur fait avec des portes NOR et NAND.png|centre|vignette|upright=2|Demi-additionneur fait avec des portes NOR et NAND.]]
Une implémentation alternative utilise des portes NAND. Pour rappel, il est possible de créer une porte XOR avec 4 portes NAND. La première d'entre elle fait un NAND entre les deux bits d'entrée, ce qui fait qu'elle calcule l'inverse de la retenue sortante. Le tout est illustré ci-dessous. En théorie, on devrait utiliser une porte NON pour récupérer la retenue correcte. Mais nous verrons plus bas que ce n'est pas une obligation. Nous verrons plus bas un exemple où c'est l'inverse de la retenue qui est utilisée dans les calculs, pour rendre les calculs plus rapides.
[[File:Demi-additionneur fait avec des portes NAND.png|centre|vignette|upright=2|Demi-additionneur fait avec des portes NAND]]
==Le circuit incrémenteur==
Maintenant que l'on sait comment additionner deux bits, reprenons l'opération d'incrémentation :
<math>a_7</math> <math>a_6</math> <math>a_5</math> <math>a_4</math> <math>a_3</math> <math>a_2</math> <math>a_1</math> <math>a_0</math>
+ 0 0 0 0 0 0 0 1
------------------------------
Sur la colonne la plus à droite, il doit ajouter un au bit de poids faible. Pour les colonnes suivantes, il faut additionner le bit de l'opérande avec la retenue de la colonne précédente. En clair, on n'additionne que deux bits à chaque colonne : un 1 sur celle tout à droite, la retenue de la colonne précédente pour les autres. Et cela nous donne une idée de comment faire pour créer le circuit incrémenteur.
===L'incrémenteur à propagation de retenue===
Il suffit d'utiliser un demi-additionneur par colonne, et de les enchainer les uns à la suite des autres. Chaque demi-additionneur additionne le bit de l'opérande avec la retenue fournie par le demi-additionneur précédent. Le seul qui fait exception est celui pour la colonne de poids faible. Pour celui-là, il doit ajouter 1 au bit de poids faible. Le résultat est appelé un '''incrémenteur à propagation de retenue'''. Il est constitué de demi-additionneurs enchaînés les uns à la suite des autres, du bit de poids faible vers le bit de poids fort.
[[File:Circuit incrémenteur.png|centre|vignette|upright=3|Circuit incrémenteur.]]
Maintenant, regardons le demi-additionneur le plus à droite, celui pour le bit de poids faible. Son entrée de retenue entrante est mise à 1 pour faire l'incrémentation. Quelques incrémenteurs permettent de configurer cette entrée de retenue à 0 ou à 1, ce qui effectue : soit une opération identité (l'opérande est recopié sur la sortie), soit une incrémentation. Un tel circuit est nommé un '''incrémenteur commandable'''. Nous aurons à utiliser une fois ou deux de tels incrémenteurs commandables dans la suite du cours.
La '''sortie de retenue''' indique si l'incrémentation a entrainé un débordement d'entier. Lors d'un débordement d'entier, la retenue de sortie est à 1. Elle est à 0 en absence de débordement d'entier. La sortie de retenue est présente sur la plupart des incrémenteurs, mais laissons cela de côté pour le moment.
L'incrémenteur à propagation de retenue est le plus simple et le plus économe en portes logiques. Mais de tels incrémenteurs sont rarement utilisés. À la place, on leur préfère des incrémenteurs plus rapides, mais qui utilisent plus de portes logiques. De tels incrémenteurs accélèrent le calcul des retenues. En effet, la rapidité d'une incrémentation est limitée par la propagation de la retenue : les retenues commencent à être calculées au bit de poids fort et on doit les calculer une par une, jusqu’à atteindre le bit de poids fort. Et cette "propagation des retenues" prend du temps, d'autant plus de temps que l'opérande est longue. Il y a deux optimisations principales, appelées le ''carry skip'' et l'anticipation de retenue, que nous allons décrire ci-dessous.
===Les incrémenteurs optimisés===
L'optimisation que nous allns voir effectue l'incrémentation, non pas bit par bit, mais par paquets de deux bits. Le résultat est que l'incrémentation est deux fois plus rapide, ou presque. Le circuit incrémenteur est donc composé en enchainant non pas des demi-additionneurs, mais des '''incrémenteurs 2 bits''' qui incrémentent un opérande de deux bits.
[[File:Circuit incrémenteur optimisé, faisant l'incrémentation par paquet de deux bits.png|centre|vignette|upright=2.5|Circuit incrémenteur optimisé, faisant l'incrémentation par paquet de deux bits]]
Reste à concevoir l'incrémenteur 2 bits. Une première méthode part de deux demi-additionneurs et effectue quelques simplifications au niveau des transistors et des portes logiques. Par exemple, l'Intel 8085 a utilisé un circuit optimisé, fabriqué avec des portes NAND et NOR. La raison est qu'avec les technologies de transistors CMOS actuelles, les portes NAND et NOR utilisent moins de transistors que les portes ET et OU. Et cette possibilité a été utilisée pour effectuer des simplifications assez mineures, mais toujours bonnes à prendre. Mais nous ne pouvons pas en parler en détail ici, car nous n'avons pas encore parlé en détail des transistors. Cependant, un exemple bien précis nous est accesible : celui de l’incrémenteur du processeur 8085 d'Intel.
Le processeur 8085 d'Intel est un processeur 8 bits très ancien. Il contenait un incrémenteur 16 bits, qui était utilisé pour calculer des adresses mémoire. Et cet incrémenteur était un incrémenteur à propagation de retenue optimisé. Il utilisait lui aussi des incrémenteurs 2 bits un peu modifiés.
L'idée était que les retenues sortant des colonnes paires étaient inversées, les colonnes impaires faisaient les calculs à partir de cette retenue inversée. Les demi-additionneurs des colonnes impaires n'étaient donc pas les mêmes que ceux des colonnes paires. Leurs tables de vérité sont différentes, leurs circuits aussi, et aucun des deux ne ressemble à un demi-additionneur normal. En réalité, ils sont fabriqués à partir des implémentations vues plus haut, qui utilisent uniquement des portes NAND ou uniquement des portes NOR.
Pour comprendre l'intérêt de faire ainsi, nous devons préciser une chose importante : avec les technologies CMOS utilisées pour les processeurs depuis les années 70, les portes logiques les plus simples sont les portes NON, NOR et NAND. Les portes ET/OU sont fabriquées en combinant des portes NOR/NAND avec une porte NON, ce qui prend plus de transistors.
Un demi-additionneur est donc fabriqué comme illustré ci-dessous, en logique CMOS. Seules les portes pour le calcul de la retenue sont indiquées, la porte XOR pour le bit de somme est fabriquée à partir de plusieurs portes logiques.
[[File:Demi-additionneur en CMOS.png|centre|vignette|upright=2|Demi-additionneur en CMOS]]
Si on enchaine deux demi-additionneurs, cela donne ceci :
[[File:Brique de base de l'incrémenteur du 8085.png|centre|vignette|upright=2|Brique de base de l'incrémenteur du 8085]]
La retenue doit donc traverser quatre portes logiques, dont deux portes NON. En inversant la retenue, les portes en jaune dans le schéma précédent se simplifient. Il suffit d'utiliser la loi de de Morgan pour trouver un circuit équivalent aux portes jaunes, qui n'est autre qu'une porte NOR couplée à une porte NON. Un autre moyen d'obtenir le même résultat est de prendre un demi-additionneur fabriqué avec uniquement des portes NOR, et de retirer la porte NON adéquate. Il faut ensuite corriger l'entrée de retenue de la porte XOR, pour qu'elle fasse l'addition avec la retenue correctement. Le résultat est illustré ci-dessous.
[[File:Incrémenteur 2 bits de l'incrémenteur du 8085.png|centre|vignette|upright=2|Incrémenteur 2 bits de l'incrémenteur du 8085]]
Le circuit utilise le même nombre de portes logiques, il n'économise pas de transistors. Par contre, la retenue n'a plus qu'à traverser deux portes logiques, au lieu de quatre. La propagation de la retenue est donc plus rapide, l'incrémentation est donc plus rapide.
===Les incrémenteurs 2 bits à calcul séparé de la retenue===
Il existe une autre méthode qui permet de créer des incrémenteurs 2 bits plus rapides que les précédents, mais qui utilisent plus de portes logiques. Ces incrémenteurs 2 bits calculent séparément les deux bits du résultat et la retenue sortante. La retenue sortante est calculée à partir des bits de l’opérande, sans avoir besoin de propager des retenues. Ainsi, la retenue sortante est connue en avance, sans passer par deux demi-additionneurs. Formellement, ils sont un cas particulier d'une optimisation appelée l'''anticipation de retenue''. Si nous verrons le cas général dans la section suivante, ce cas particulier est une très bonne introduction.
Pour créer un tel incrémenteur, le mieux est de partir de sa table de vérité.
{|class="wikitable"
|-
! Opérande !! Retenue entrante !! !! Retenue sortante !! Résultat
|-
| 00 || 0 || || 0 || 00
|-
| 01 || 0 || || 0 || 01
|-
| 10 || 0 || || 0 || 10
|-
| 11 || 0 || || 0 || 11
|-
| colspan="5" |
|-
| 00 || 1 || || 0 || 01
|-
| 01 || 1 || || 0 || 10
|-
| 10 || 1 || || 0 || 11
|-
| 11 || 1 || || 1 || 00
|}
Elle nous dit que la retenue sortante ne vaut 1 que dans un seul cas : les deux bits d'opérande valent 1, la retenue entrante vaut 1. Elle se calcule alors avec une porte ET à trois entrées. Pour les deux bits du résultat, les équations logiques ne donnent pas un résultat satisfaisant. La solution la plus simple est d'utiliser deux demi-additionneurs et de retirer les portes logiques superflues. Le résultat est alors un '''incrémenteur 2 bits ''carry skip''''', où la retenue sortante est calculée séparément du reste.
[[File:Incrémenteur carry skip 2 bits.png|centre|vignette|upright=2.5|Incrémenteur ''carry skip'' 2 bits]]
===Les incrémenteurs à anticipation de retenue===
L''''anticipation de retenue''' calcule la retenue d'une colonne sans attendre les retenues des colonnes précédentes. Dans le cas idéal, toutes les retenues sont calculées en parallèle, en même temps, et sont ensuite envoyées aux demi-additionneurs. Il s'agit d'une optimisation qui est utilisée pour l'incrémentation, l'addition et d'autres opérations similaires. Pour l'incrémentation, déterminer la retenue ne demande pas de calculs complexes, contrairement à l'addition.
[[File:Incrémenteur à anticipation de retenues, 8 bits.png|centre|vignette|upright=2.5|Incrémenteur à anticipation de retenues, 8 bits.]]
Pour le comprendre, on peut regarder comment la retenue sortant d'un demi-additionneur est formée. Elle est calculée en faisant un ET logique entre la retenue entrante et le bit d'opérande. Une retenue est donc un ET logique entre toutes les retenues précédentes. Un incrémenteur à anticipation de retenue utilise donc une porte ET à plusieurs entrées pour calculer une retenue.
[[File:Anticipation de retenue pour un bit du résultat, incrémenteur.png|centre|vignette|upright=2.5|Anticipation de retenue pour un bit du résultat, incrémenteur]]
En théorie, on peut utiliser une porte ET à plusieurs entrées pour chaque bit de l'opérande. Cependant, cela entrainera un cout en transistors très important. Pour éviter de gaspiller trop de portes logiques, une solution est de mélanger anticipation de retenues et propagation de retenue. Par exemple, pour un incrémenteur 32 bits, on peut découper l'opérande en 4 octets : on anticipe les retenues pour chaque octet, mais l'incrémentation de chaque octet se fait avec propagation de retenue et/ou ''carry skip''. Plus haut, nous avons vu un cas particulier où les retenues étaient anticipées par blocs de deux bits.
[[File:Incrémenteur à anticipation de retenues.png|centre|vignette|upright=3|Incrémenteur hybride utilisant partiellement l'anticipation de retenues.]]
Quelques processeurs utilisaient l'anticipation de retenues. Par exemple, le processeur Z-80 de Zilog utilisait un incrémenteur pour des nombres de 16 bits, ce qui demandait des performances assez élevées. Et cet incrémenteur utilisait à la fois anticipation de retenues et ''carry skip''. Il était découpé en quatre blocs avant anticipation de retenues entre eux : un bloc regroupant les 7 bits de poids faible, suivi par un bloc de 5 bits, lui-même suivi par un bloc de 3 bits, terminé par un dernier bit isolé. A l'intérieur de ces blocs, les bits sont regroupés en paires utilisant le ''carry skip''. Pour ceux qui veulent en savoir plus sur cet incrémenteur, voici un lien sur le sujet :
* [https://www.righto.com/2013/11/the-z-80s-16-bit-incrementdecrement.html The Z-80's 16-bit increment/decrement circuit reverse engineered].
==Les décrémenteurs==
Les circuits décrémenteurs font l'inverse d'un incrémenteur : au lieu d'ajouter 1 à un opérande, ils lui soustraient 1. La différence n'est pas énorme, ce qui fait que les circuits incrémenteurs et décrémenteurs se ressemblent beaucoup.
La soustraction se fait en binaire comme en décimal. On soustrait deux chiffres, puis on doit propager une éventuelle retenue sur la colonne suivante. La retenue apparait quand le chiffre soustrait est plus grand que l'autre chiffre. Elle est propagée sur la colonne suivante, où elle doit être soustraite du résultat. Pour le dire autrement, après avoir soustrait un chiffre, on doit de plus soustraire la retenue de la colonne précédente.
La décrémentation effectue le calcul suivant :
<math>a_7</math> <math>a_6</math> <math>a_5</math> <math>a_4</math> <math>a_3</math> <math>a_2</math> <math>a_1</math> <math>a_0</math>
- 0 0 0 0 0 0 0 1
La colonne la plus à droite soustrait un 1. Par contre, les colonnes suivantes soustraient un zéro. Soustraire un zéro ne change rien. Mais il ne faut pas oublier de soustraire une éventuellement retenue, propagée depuis la colonne précédente. En clair, un décrémenteur peut se construire si on sait soustraire deux bits. On soustrait un 1 sur la colonne la plus à droite, on soustrait une retenue sur les autres colonnes. Et pour soustraire deux bits, il faut utiliser un demi-soustracteur.
===Le demi-soustracteur et le décrémenteur à propagation de retenue===
Un '''demi-soustracteur''' soustrait deux bits. Il prend deux bits d'entrée et fournit deux sorties : un bit de différence et une retenue. Pour comprendre d'où vient cette retenue, et comment soustraire deux bits, traitons les quatre cas possibles un par un.
Premièrement, soustraire zéro à un bit ne changera rien :
* 0 - 0 = 0 ;
* 1 - 0 = 1.
Si on soustrait 1 à un bit qui vaut 1, on obtient zéro.
* 1 - 1 = 0.
Maintenant, que se passe-t-il si on soustrait 1 à 0 ? Voici le résultat :
* 0 - 1 = 1 et une retenue propagée sur la colonne suivante, où elle est soustraite.
La table de vérité est donc la suivante. Détail important : le demi-soustracteur fait ici la soustraction X - Y. En anglais, X est appelé le ''Minuend'' et Y est appelé le ''Subtrahend'', mais ces termes n'ont pas de traduction française. Aussi, je vais les utiliser tels quels dans ce qui suit.
{|class="wikitable"
|-
! ''Minuend'' !! ''Subtrahend'' !! !! Retenue sortante !! Bit de somme
|-
||0||0|| ||0||0
|-
||0||1|| ||1||1
|-
||1||0|| ||0||1
|-
||1||1|| ||0||0
|}
Si on construit la table de vérité, et qu'on construit le circuit associé, on tombe sur ce circuit.
[[File:Half Subtractor Vektor.svg|centre|vignette|Demi-soustracteur.]]
Vous remarquerez que le bit de somme se calcule de la même manière que pour l'addition, seul le calcul de la retenue change. Comme pour le demi-additionneur, on peut exploiter des redondances entre calcul de la retenue et porte XOR, mais ce ne sont pas les mêmes. Le circuit obtenu après exploitation de ces redondances est le suivant :
[[File:Demi-soustracteur optimisé, sans redondances.png|centre|vignette|upright=1.5|Demi-soustracteur optimisé, sans redondances]]
Un décrémenteur à propagation de retenue se construit comme un incrémenteur, sauf qu'on remplace les demi-additionneurs par des demi-soustracteurs. En clair, on enchaine plusieurs demi-soustracteurs les uns à la suite des autres.
===Le décrémenteur à anticipation de retenue===
Il est possible d'utiliser l'anticipation de retenue, mais les circuits sont cependant différents de l'incrémenteur. Idem avec le ''carry skip'', et toute autre optimisation possible. La raison est que les retenues sont calculées différemment, ce qui fait que l’anticipation de retenue doit être adaptée. La retenue d'une colonne ne se calcule plus avec un ET des bits des colonnes précédentes. A la place, ces bits doivent être inversés avant de faire le ET. Le résultat est que la porte ET est remplacée par une porte NOR.
Pour ceux qui s'en rappelle, faire un NOR de N bits permet de vérifier s'ils sont tous à zéro. Et c'est le cas ici. Lors d'une incrémentation, on a une retenue quand tous les bits précédents de l'opérande sont à 1. Pour la décrémentation, c'est l'inverse : on a une retenue quand tous les bits précédents sont à 0.
De fait, décrémenter un nombre est assez simple. Il y a deux cas principaux :
* soit le bit de poids faible est un 1, ce qui signifie que le nombre est impair ;
* soit un ou plusieurs bits de poids faible consécutifs sont à 0, suivis par un 1.
Dans le premier cas, le bit de poids faible est juste inversé. Dans le second cas, le 1 est inversé, et les 0 de poids faible deviennent des 1. Par exemple, Prenez un nombre de la forme xxxxxxx10000. Après décrémentation, le résultat sera xxxxxxxx011111 : les 0 de poids faible deviennent des 1, le premier 1 en partant de la droite est inversé. Et la même logique se généralise pour tout nombre avant un ou plusieurs zéros dans les bits de poids faible.
==Les incrémenteurs-décrémenteurs==
Les circuits décrémenteurs ressemblent beaucoup aux incrémenteurs. Aussi, vous ne serez pas étonné qu'il soit possible de fusionner un incrémenteur et un décrémenteur en un seul circuit. Un tel circuit s'appelle un '''incrémenteur-décrémenteur'''.
Un tel circuit a une interface similaire à celle d'un incrémenteur, avec une entrée sur laquelle présenter l'opérande, et une sortie sur laquelle récupérer le résultat. Il a aussi une entrée de retenue, sur laquelle envoyer un 1 (incrémentation) ou un 0 (opération NOP/identité). Et il a aussi une sortie de retenue, qui indique que le résultat du calcul a débordé. Mais en plus de cela, il dispose d'une '''entrée de commande''' qui permet de choisir entre incrémentation et décrémentation. En général, elle est mise à 0 lors d'une incrémentation et à 1 lors d'une décrémentation.
Il existe plusieurs manières de créer un incrémenteur-décrémenteur, que nous allons voir si dessous. Nous n'allons pas voir le cas le plus évident, aussi ne vous étonnez pas si vous avez remarqué qu'un demi-additionneur et un demi-soustracteur se ressemblent beaucoup.
===L'incrémenteur-décrémenteur basé sur un incrémenteur===
Une première implémentation se base sur les propriétés du complément à deux, et plus précisément sur cette équation :
: <math>\overline{x} + 1 = \overline{x - 1}</math>
Les deux termes de cette équation sont simplement des définitions équivalentes du complément à deux d'un nombre entier. Celui-ci se calcule de deux manières : soit on incrémente son complément à 1, soit on prend le complément à 1 de son décrément. Et cela nous permet de déterminer comment obtenir une incrémentation à partir d'une décrémentation et inversement.
Que nous dit cette équation ? Simplement que l'on peut décrémenter un nombre si on a deux inverseurs commandables, et un circuit incrémenteur. La décrémentation se fait alors en deux étapes : on calcule son complément à deux, puis on inverse les bits du résultat précédent. Le calcul du complément à deux demande d'avoir un circuit inverseur, et un incrémenteur.
===L'incrémenteur-décrémenteur basé sur des demi-additionneurs===
Vous l'aurez sans doute remarqué, mais le demi-soustracteur ressemble beaucoup au demi-additionneur. La seule différence est une porte NON ajoutée au bon endroit dans le demi-soustracteur. La porte NON inverse le bit X, sachant que le demi-soustracteur fait la soustraction X - Y. De plus, l'opérande inversée est utilisée seulement pour le calcul des retenues, le bit de somme est calculé normalement.
{|
|[[File:Half-adder.svg|vignette|Demi-additionneur]]
|[[File:Half Subtractor Vektor.svg|vignette|upright=1.3|Demi-soustracteur]]
|}
La conséquence est qu'il est possible de fusionner un demi-additionneur avec un demi-soustracteur. L'idée est de remplacer la porte NON du demi-soustracteur par un inverseur commandable. Si on envoie un zéro sur l'entrée de commande, l'inverseur commandable se comportera comme une porte OUI et le circuit sera alors identique à un demi-additionneur. Si on envoie un 1 sur l'entrée de commande, l'inverseur commandable se comportera comme une porte NON. le circuit sera alors un demi-soustracteur. En remplaçant les demi-additionneurs (ou demi-soustracteurs) par de tels circuits, on peut obtenir un circuit faisant à la fois incrémenteur et décrémenteur.
Un défaut de cette approche est que les optimisations vues plus haut pour les demi-additionneurs ne fonctionne tout simplement plus. On est obligé d'utiliser une porte XOR et une porte ET séparées, pas question d'utiliser les demi-additionneurs optimisés vu au début du chapitre.
<noinclude>
{{NavChapitre | book=Fonctionnement d'un ordinateur
| prev=Les circuits de sélection
| prevText=Les circuits de sélection
| next=Les bascules : des mémoires de 1 bit
| nextText=Les bascules : des mémoires de 1 bit
}}
</noinclude>
bwixop3v30wz8ns6tvm5eoce9qcac1b
768809
768808
2026-06-27T19:07:39Z
Mewtow
31375
/* L'incrémenteur-décrémenteur basé sur un incrémenteur */
768809
wikitext
text/x-wiki
Le circuit '''incrémenteur''' incrémente un nombre entier. Plus précisément, il prend en entrée un nombre entier, et fournit en entier le nombre suivant, il lui ajoute 1. Une telle opération peut paraitre triviale et peu intéressante. Cependant, elle est très utilisée et est absolument cruciale pour créer des compteurs, des circuits capables de compter ou décompter, qui auront droit à un chapitre dédié. De plus, au-delà de leur utilisation dans les compteurs, les incrémenteurs étaient très utilisés sur les premiers processeurs 8 bits, comme le Z-80, le 6502, les premiers processeurs x86 comme le 8008, le 8086, le 8085, et bien d'autres.
Il existe aussi des ''circuits décrémenteurs'', qui décrémentent un opérande, ainsi que des ''circuits incrémenteur-décrémenteur'', qui peuvent incrémenter ou décrémenter, selon comment on les configure. Je ne vais pas détailler ces circuits plus que ça, car de tels circuits sont assez rares, comparé à un circuit incrémenteur simple.
==Le demi-additionneur==
Le circuit incrémenteur effectue l'opération suivante :
<math>a_7</math> <math>a_6</math> <math>a_5</math> <math>a_4</math> <math>a_3</math> <math>a_2</math> <math>a_1</math> <math>a_0</math>
+ 0 0 0 0 0 0 0 1
Un incrémenteur basique doit donc faire une addition pour chaque colonne, et précisément une addition de deux bits. Il se trouve que la table d'addition est très simple en binaire. Jugez plutôt :
* 0 + 0 = 0, retenue = 0 ;
* 0 + 1 = 1, retenue = 0 ;
* 1 + 0 = 1, retenue = 0 ;
* 1 + 1 = 0, retenue = 1.
Un circuit capable d'additionner deux bits est appelé un '''demi-additionneur'''. Il dispose d'une sortie S pour la somme, et C pour la retenue (''carry'' an anglais).
===Le demi-additionneur classique===
Un demi-additionneur est très simple à construire avec les techniques vues dans les premiers chapitres. Voici sa table de vérité :
{|class="wikitable"
|-
! Retenue entrante !! Opérande 1 !! !! Retenue sortante !! Bit de somme
|-
||0||0|| ||0||0
|-
||0||1|| ||0||1
|-
||1||0|| ||0||1
|-
||1||1|| ||1||0
|}
On voit immédiatement que la colonne des retenues donne une porte ET, alors que celle du bit de somme est calculé par un XOR.
{| class="flexible"
|[[File:1-bit half-adder.svg|class=transparent|centre|Demi-addtionneur.]]
|[[File:Half-adder.svg|centre|class=transparent|Circuit d'un demi-addtionneur.]]
|}
Il existe beaucoup d'autres implémentations alternatives, qui utilisent moins de portes logiques, calculent la retenue plus rapidement, consomment moins d'énergie, et autres. Et il est intéressant d'étudier quelques alternatives, surtout qu'elles serviront plus bas, quand on étudiera le circuit incrémenteur du 8085.
===Le demi-additionneur basé sur une modification de la retenue===
Une source d'amélioration est liée à la porte XOR. En pratique, une porte XOR est composée en combinant plusieurs portes logiques ET/OU/NOR/NAND ensemble. Et il se trouve qu'il y a des redondances entre celles-ci et les portes utilisées pour calculer la retenue. Par exemple, rappelons qu'une porte XOR peut être construite avec une porte ET et deux portes NOR, comme illustré ci-dessous. Et il se trouve que la porte ET interne calcule la retenue sortante. En supprimant cette redondance, on économise quelques portes logiques. De plus, on se retrouve avec un demi-additionneur qui calcule le bit de somme à partir de la retenue sortante.
[[File:Demi-additionneur avec redondances éliminées.png|centre|vignette|upright=2|Demi-additionneur avec redondances éliminées]]
Le circuit précédent a une interprétation logique. Si vous regardez la table de vérité, vous remarquerez que la somme de deux bits est égale à l'inverse de la retenue sortante, sauf dans le cas où les deux bits additionnés valent zéro. Et le circuit précédent est basé là-dessus.
{|class="wikitable"
|-
! Retenue entrante !! Opérande 1 !! !! Retenue sortante !! Bit de somme
|- class="f_vert"
||0||0|| ||0||0
|- class="f_rouge"
||0||1|| ||0||1
|- class="f_rouge"
||1||0|| ||0||1
|- class="f_rouge"
||1||1|| ||1||0
|}
L'idée est de calculer l'inverse du bit de somme, avant de l'inverser avec une porte NON. L'inverse du bit de somme vaut 1, soit quand la retenue est à 1, soit quand les deux bits additionnés sont à 0. La porte ET calcule la retenue sortante, la première porte NOR détecte sur les deux bits d'entrée valent zéro. Une porte OU combine les deux résultat pour obtenir l'inverse du bit d'entrée, puis une porte NOn inverse le tout pour obtenir le bit de somme adéquat. Les deux portes sont fusionnées : c'est la seconde porte NOR.
[[File:Full adder HA + MUX.png|centre|vignette|upright=2.5|Demi-additionneur basé sur une inversion de la retenue sortante.]]
===Les implémentations alternatives===
Une implémentation alternative, qui n'utilise que des portes NOR, est la suivante :
[[File:Demi-additionneur fait avec des portes NOR et NAND.png|centre|vignette|upright=2|Demi-additionneur fait avec des portes NOR et NAND.]]
Une implémentation alternative utilise des portes NAND. Pour rappel, il est possible de créer une porte XOR avec 4 portes NAND. La première d'entre elle fait un NAND entre les deux bits d'entrée, ce qui fait qu'elle calcule l'inverse de la retenue sortante. Le tout est illustré ci-dessous. En théorie, on devrait utiliser une porte NON pour récupérer la retenue correcte. Mais nous verrons plus bas que ce n'est pas une obligation. Nous verrons plus bas un exemple où c'est l'inverse de la retenue qui est utilisée dans les calculs, pour rendre les calculs plus rapides.
[[File:Demi-additionneur fait avec des portes NAND.png|centre|vignette|upright=2|Demi-additionneur fait avec des portes NAND]]
==Le circuit incrémenteur==
Maintenant que l'on sait comment additionner deux bits, reprenons l'opération d'incrémentation :
<math>a_7</math> <math>a_6</math> <math>a_5</math> <math>a_4</math> <math>a_3</math> <math>a_2</math> <math>a_1</math> <math>a_0</math>
+ 0 0 0 0 0 0 0 1
------------------------------
Sur la colonne la plus à droite, il doit ajouter un au bit de poids faible. Pour les colonnes suivantes, il faut additionner le bit de l'opérande avec la retenue de la colonne précédente. En clair, on n'additionne que deux bits à chaque colonne : un 1 sur celle tout à droite, la retenue de la colonne précédente pour les autres. Et cela nous donne une idée de comment faire pour créer le circuit incrémenteur.
===L'incrémenteur à propagation de retenue===
Il suffit d'utiliser un demi-additionneur par colonne, et de les enchainer les uns à la suite des autres. Chaque demi-additionneur additionne le bit de l'opérande avec la retenue fournie par le demi-additionneur précédent. Le seul qui fait exception est celui pour la colonne de poids faible. Pour celui-là, il doit ajouter 1 au bit de poids faible. Le résultat est appelé un '''incrémenteur à propagation de retenue'''. Il est constitué de demi-additionneurs enchaînés les uns à la suite des autres, du bit de poids faible vers le bit de poids fort.
[[File:Circuit incrémenteur.png|centre|vignette|upright=3|Circuit incrémenteur.]]
Maintenant, regardons le demi-additionneur le plus à droite, celui pour le bit de poids faible. Son entrée de retenue entrante est mise à 1 pour faire l'incrémentation. Quelques incrémenteurs permettent de configurer cette entrée de retenue à 0 ou à 1, ce qui effectue : soit une opération identité (l'opérande est recopié sur la sortie), soit une incrémentation. Un tel circuit est nommé un '''incrémenteur commandable'''. Nous aurons à utiliser une fois ou deux de tels incrémenteurs commandables dans la suite du cours.
La '''sortie de retenue''' indique si l'incrémentation a entrainé un débordement d'entier. Lors d'un débordement d'entier, la retenue de sortie est à 1. Elle est à 0 en absence de débordement d'entier. La sortie de retenue est présente sur la plupart des incrémenteurs, mais laissons cela de côté pour le moment.
L'incrémenteur à propagation de retenue est le plus simple et le plus économe en portes logiques. Mais de tels incrémenteurs sont rarement utilisés. À la place, on leur préfère des incrémenteurs plus rapides, mais qui utilisent plus de portes logiques. De tels incrémenteurs accélèrent le calcul des retenues. En effet, la rapidité d'une incrémentation est limitée par la propagation de la retenue : les retenues commencent à être calculées au bit de poids fort et on doit les calculer une par une, jusqu’à atteindre le bit de poids fort. Et cette "propagation des retenues" prend du temps, d'autant plus de temps que l'opérande est longue. Il y a deux optimisations principales, appelées le ''carry skip'' et l'anticipation de retenue, que nous allons décrire ci-dessous.
===Les incrémenteurs optimisés===
L'optimisation que nous allns voir effectue l'incrémentation, non pas bit par bit, mais par paquets de deux bits. Le résultat est que l'incrémentation est deux fois plus rapide, ou presque. Le circuit incrémenteur est donc composé en enchainant non pas des demi-additionneurs, mais des '''incrémenteurs 2 bits''' qui incrémentent un opérande de deux bits.
[[File:Circuit incrémenteur optimisé, faisant l'incrémentation par paquet de deux bits.png|centre|vignette|upright=2.5|Circuit incrémenteur optimisé, faisant l'incrémentation par paquet de deux bits]]
Reste à concevoir l'incrémenteur 2 bits. Une première méthode part de deux demi-additionneurs et effectue quelques simplifications au niveau des transistors et des portes logiques. Par exemple, l'Intel 8085 a utilisé un circuit optimisé, fabriqué avec des portes NAND et NOR. La raison est qu'avec les technologies de transistors CMOS actuelles, les portes NAND et NOR utilisent moins de transistors que les portes ET et OU. Et cette possibilité a été utilisée pour effectuer des simplifications assez mineures, mais toujours bonnes à prendre. Mais nous ne pouvons pas en parler en détail ici, car nous n'avons pas encore parlé en détail des transistors. Cependant, un exemple bien précis nous est accesible : celui de l’incrémenteur du processeur 8085 d'Intel.
Le processeur 8085 d'Intel est un processeur 8 bits très ancien. Il contenait un incrémenteur 16 bits, qui était utilisé pour calculer des adresses mémoire. Et cet incrémenteur était un incrémenteur à propagation de retenue optimisé. Il utilisait lui aussi des incrémenteurs 2 bits un peu modifiés.
L'idée était que les retenues sortant des colonnes paires étaient inversées, les colonnes impaires faisaient les calculs à partir de cette retenue inversée. Les demi-additionneurs des colonnes impaires n'étaient donc pas les mêmes que ceux des colonnes paires. Leurs tables de vérité sont différentes, leurs circuits aussi, et aucun des deux ne ressemble à un demi-additionneur normal. En réalité, ils sont fabriqués à partir des implémentations vues plus haut, qui utilisent uniquement des portes NAND ou uniquement des portes NOR.
Pour comprendre l'intérêt de faire ainsi, nous devons préciser une chose importante : avec les technologies CMOS utilisées pour les processeurs depuis les années 70, les portes logiques les plus simples sont les portes NON, NOR et NAND. Les portes ET/OU sont fabriquées en combinant des portes NOR/NAND avec une porte NON, ce qui prend plus de transistors.
Un demi-additionneur est donc fabriqué comme illustré ci-dessous, en logique CMOS. Seules les portes pour le calcul de la retenue sont indiquées, la porte XOR pour le bit de somme est fabriquée à partir de plusieurs portes logiques.
[[File:Demi-additionneur en CMOS.png|centre|vignette|upright=2|Demi-additionneur en CMOS]]
Si on enchaine deux demi-additionneurs, cela donne ceci :
[[File:Brique de base de l'incrémenteur du 8085.png|centre|vignette|upright=2|Brique de base de l'incrémenteur du 8085]]
La retenue doit donc traverser quatre portes logiques, dont deux portes NON. En inversant la retenue, les portes en jaune dans le schéma précédent se simplifient. Il suffit d'utiliser la loi de de Morgan pour trouver un circuit équivalent aux portes jaunes, qui n'est autre qu'une porte NOR couplée à une porte NON. Un autre moyen d'obtenir le même résultat est de prendre un demi-additionneur fabriqué avec uniquement des portes NOR, et de retirer la porte NON adéquate. Il faut ensuite corriger l'entrée de retenue de la porte XOR, pour qu'elle fasse l'addition avec la retenue correctement. Le résultat est illustré ci-dessous.
[[File:Incrémenteur 2 bits de l'incrémenteur du 8085.png|centre|vignette|upright=2|Incrémenteur 2 bits de l'incrémenteur du 8085]]
Le circuit utilise le même nombre de portes logiques, il n'économise pas de transistors. Par contre, la retenue n'a plus qu'à traverser deux portes logiques, au lieu de quatre. La propagation de la retenue est donc plus rapide, l'incrémentation est donc plus rapide.
===Les incrémenteurs 2 bits à calcul séparé de la retenue===
Il existe une autre méthode qui permet de créer des incrémenteurs 2 bits plus rapides que les précédents, mais qui utilisent plus de portes logiques. Ces incrémenteurs 2 bits calculent séparément les deux bits du résultat et la retenue sortante. La retenue sortante est calculée à partir des bits de l’opérande, sans avoir besoin de propager des retenues. Ainsi, la retenue sortante est connue en avance, sans passer par deux demi-additionneurs. Formellement, ils sont un cas particulier d'une optimisation appelée l'''anticipation de retenue''. Si nous verrons le cas général dans la section suivante, ce cas particulier est une très bonne introduction.
Pour créer un tel incrémenteur, le mieux est de partir de sa table de vérité.
{|class="wikitable"
|-
! Opérande !! Retenue entrante !! !! Retenue sortante !! Résultat
|-
| 00 || 0 || || 0 || 00
|-
| 01 || 0 || || 0 || 01
|-
| 10 || 0 || || 0 || 10
|-
| 11 || 0 || || 0 || 11
|-
| colspan="5" |
|-
| 00 || 1 || || 0 || 01
|-
| 01 || 1 || || 0 || 10
|-
| 10 || 1 || || 0 || 11
|-
| 11 || 1 || || 1 || 00
|}
Elle nous dit que la retenue sortante ne vaut 1 que dans un seul cas : les deux bits d'opérande valent 1, la retenue entrante vaut 1. Elle se calcule alors avec une porte ET à trois entrées. Pour les deux bits du résultat, les équations logiques ne donnent pas un résultat satisfaisant. La solution la plus simple est d'utiliser deux demi-additionneurs et de retirer les portes logiques superflues. Le résultat est alors un '''incrémenteur 2 bits ''carry skip''''', où la retenue sortante est calculée séparément du reste.
[[File:Incrémenteur carry skip 2 bits.png|centre|vignette|upright=2.5|Incrémenteur ''carry skip'' 2 bits]]
===Les incrémenteurs à anticipation de retenue===
L''''anticipation de retenue''' calcule la retenue d'une colonne sans attendre les retenues des colonnes précédentes. Dans le cas idéal, toutes les retenues sont calculées en parallèle, en même temps, et sont ensuite envoyées aux demi-additionneurs. Il s'agit d'une optimisation qui est utilisée pour l'incrémentation, l'addition et d'autres opérations similaires. Pour l'incrémentation, déterminer la retenue ne demande pas de calculs complexes, contrairement à l'addition.
[[File:Incrémenteur à anticipation de retenues, 8 bits.png|centre|vignette|upright=2.5|Incrémenteur à anticipation de retenues, 8 bits.]]
Pour le comprendre, on peut regarder comment la retenue sortant d'un demi-additionneur est formée. Elle est calculée en faisant un ET logique entre la retenue entrante et le bit d'opérande. Une retenue est donc un ET logique entre toutes les retenues précédentes. Un incrémenteur à anticipation de retenue utilise donc une porte ET à plusieurs entrées pour calculer une retenue.
[[File:Anticipation de retenue pour un bit du résultat, incrémenteur.png|centre|vignette|upright=2.5|Anticipation de retenue pour un bit du résultat, incrémenteur]]
En théorie, on peut utiliser une porte ET à plusieurs entrées pour chaque bit de l'opérande. Cependant, cela entrainera un cout en transistors très important. Pour éviter de gaspiller trop de portes logiques, une solution est de mélanger anticipation de retenues et propagation de retenue. Par exemple, pour un incrémenteur 32 bits, on peut découper l'opérande en 4 octets : on anticipe les retenues pour chaque octet, mais l'incrémentation de chaque octet se fait avec propagation de retenue et/ou ''carry skip''. Plus haut, nous avons vu un cas particulier où les retenues étaient anticipées par blocs de deux bits.
[[File:Incrémenteur à anticipation de retenues.png|centre|vignette|upright=3|Incrémenteur hybride utilisant partiellement l'anticipation de retenues.]]
Quelques processeurs utilisaient l'anticipation de retenues. Par exemple, le processeur Z-80 de Zilog utilisait un incrémenteur pour des nombres de 16 bits, ce qui demandait des performances assez élevées. Et cet incrémenteur utilisait à la fois anticipation de retenues et ''carry skip''. Il était découpé en quatre blocs avant anticipation de retenues entre eux : un bloc regroupant les 7 bits de poids faible, suivi par un bloc de 5 bits, lui-même suivi par un bloc de 3 bits, terminé par un dernier bit isolé. A l'intérieur de ces blocs, les bits sont regroupés en paires utilisant le ''carry skip''. Pour ceux qui veulent en savoir plus sur cet incrémenteur, voici un lien sur le sujet :
* [https://www.righto.com/2013/11/the-z-80s-16-bit-incrementdecrement.html The Z-80's 16-bit increment/decrement circuit reverse engineered].
==Les décrémenteurs==
Les circuits décrémenteurs font l'inverse d'un incrémenteur : au lieu d'ajouter 1 à un opérande, ils lui soustraient 1. La différence n'est pas énorme, ce qui fait que les circuits incrémenteurs et décrémenteurs se ressemblent beaucoup.
La soustraction se fait en binaire comme en décimal. On soustrait deux chiffres, puis on doit propager une éventuelle retenue sur la colonne suivante. La retenue apparait quand le chiffre soustrait est plus grand que l'autre chiffre. Elle est propagée sur la colonne suivante, où elle doit être soustraite du résultat. Pour le dire autrement, après avoir soustrait un chiffre, on doit de plus soustraire la retenue de la colonne précédente.
La décrémentation effectue le calcul suivant :
<math>a_7</math> <math>a_6</math> <math>a_5</math> <math>a_4</math> <math>a_3</math> <math>a_2</math> <math>a_1</math> <math>a_0</math>
- 0 0 0 0 0 0 0 1
La colonne la plus à droite soustrait un 1. Par contre, les colonnes suivantes soustraient un zéro. Soustraire un zéro ne change rien. Mais il ne faut pas oublier de soustraire une éventuellement retenue, propagée depuis la colonne précédente. En clair, un décrémenteur peut se construire si on sait soustraire deux bits. On soustrait un 1 sur la colonne la plus à droite, on soustrait une retenue sur les autres colonnes. Et pour soustraire deux bits, il faut utiliser un demi-soustracteur.
===Le demi-soustracteur et le décrémenteur à propagation de retenue===
Un '''demi-soustracteur''' soustrait deux bits. Il prend deux bits d'entrée et fournit deux sorties : un bit de différence et une retenue. Pour comprendre d'où vient cette retenue, et comment soustraire deux bits, traitons les quatre cas possibles un par un.
Premièrement, soustraire zéro à un bit ne changera rien :
* 0 - 0 = 0 ;
* 1 - 0 = 1.
Si on soustrait 1 à un bit qui vaut 1, on obtient zéro.
* 1 - 1 = 0.
Maintenant, que se passe-t-il si on soustrait 1 à 0 ? Voici le résultat :
* 0 - 1 = 1 et une retenue propagée sur la colonne suivante, où elle est soustraite.
La table de vérité est donc la suivante. Détail important : le demi-soustracteur fait ici la soustraction X - Y. En anglais, X est appelé le ''Minuend'' et Y est appelé le ''Subtrahend'', mais ces termes n'ont pas de traduction française. Aussi, je vais les utiliser tels quels dans ce qui suit.
{|class="wikitable"
|-
! ''Minuend'' !! ''Subtrahend'' !! !! Retenue sortante !! Bit de somme
|-
||0||0|| ||0||0
|-
||0||1|| ||1||1
|-
||1||0|| ||0||1
|-
||1||1|| ||0||0
|}
Si on construit la table de vérité, et qu'on construit le circuit associé, on tombe sur ce circuit.
[[File:Half Subtractor Vektor.svg|centre|vignette|Demi-soustracteur.]]
Vous remarquerez que le bit de somme se calcule de la même manière que pour l'addition, seul le calcul de la retenue change. Comme pour le demi-additionneur, on peut exploiter des redondances entre calcul de la retenue et porte XOR, mais ce ne sont pas les mêmes. Le circuit obtenu après exploitation de ces redondances est le suivant :
[[File:Demi-soustracteur optimisé, sans redondances.png|centre|vignette|upright=1.5|Demi-soustracteur optimisé, sans redondances]]
Un décrémenteur à propagation de retenue se construit comme un incrémenteur, sauf qu'on remplace les demi-additionneurs par des demi-soustracteurs. En clair, on enchaine plusieurs demi-soustracteurs les uns à la suite des autres.
===Le décrémenteur à anticipation de retenue===
Il est possible d'utiliser l'anticipation de retenue, mais les circuits sont cependant différents de l'incrémenteur. Idem avec le ''carry skip'', et toute autre optimisation possible. La raison est que les retenues sont calculées différemment, ce qui fait que l’anticipation de retenue doit être adaptée. La retenue d'une colonne ne se calcule plus avec un ET des bits des colonnes précédentes. A la place, ces bits doivent être inversés avant de faire le ET. Le résultat est que la porte ET est remplacée par une porte NOR.
Pour ceux qui s'en rappelle, faire un NOR de N bits permet de vérifier s'ils sont tous à zéro. Et c'est le cas ici. Lors d'une incrémentation, on a une retenue quand tous les bits précédents de l'opérande sont à 1. Pour la décrémentation, c'est l'inverse : on a une retenue quand tous les bits précédents sont à 0.
De fait, décrémenter un nombre est assez simple. Il y a deux cas principaux :
* soit le bit de poids faible est un 1, ce qui signifie que le nombre est impair ;
* soit un ou plusieurs bits de poids faible consécutifs sont à 0, suivis par un 1.
Dans le premier cas, le bit de poids faible est juste inversé. Dans le second cas, le 1 est inversé, et les 0 de poids faible deviennent des 1. Par exemple, Prenez un nombre de la forme xxxxxxx10000. Après décrémentation, le résultat sera xxxxxxxx011111 : les 0 de poids faible deviennent des 1, le premier 1 en partant de la droite est inversé. Et la même logique se généralise pour tout nombre avant un ou plusieurs zéros dans les bits de poids faible.
==Les incrémenteurs-décrémenteurs==
Les circuits décrémenteurs ressemblent beaucoup aux incrémenteurs. Aussi, vous ne serez pas étonné qu'il soit possible de fusionner un incrémenteur et un décrémenteur en un seul circuit. Un tel circuit s'appelle un '''incrémenteur-décrémenteur'''.
Un tel circuit a une interface similaire à celle d'un incrémenteur, avec une entrée sur laquelle présenter l'opérande, et une sortie sur laquelle récupérer le résultat. Il a aussi une entrée de retenue, sur laquelle envoyer un 1 (incrémentation) ou un 0 (opération NOP/identité). Et il a aussi une sortie de retenue, qui indique que le résultat du calcul a débordé. Mais en plus de cela, il dispose d'une '''entrée de commande''' qui permet de choisir entre incrémentation et décrémentation. En général, elle est mise à 0 lors d'une incrémentation et à 1 lors d'une décrémentation.
Il existe plusieurs manières de créer un incrémenteur-décrémenteur, que nous allons voir si dessous. Nous n'allons pas voir le cas le plus évident, aussi ne vous étonnez pas si vous avez remarqué qu'un demi-additionneur et un demi-soustracteur se ressemblent beaucoup.
===L'incrémenteur-décrémenteur basé sur un incrémenteur===
Une première implémentation se base sur les propriétés du complément à deux, et plus précisément sur cette équation :
: <math>\overline{x} + 1 = \overline{x - 1}</math>
Les deux termes de cette équation sont simplement des définitions équivalentes du complément à deux d'un nombre entier. Celui-ci se calcule de deux manières : soit on incrémente son complément à 1, soit on prend le complément à 1 de son décrément. Et cela nous permet de déterminer comment obtenir une incrémentation à partir d'une décrémentation et inversement.
Que nous dit cette équation ? Simplement que l'on peut décrémenter un nombre si on a deux inverseurs commandables, et un circuit incrémenteur. La décrémentation se fait alors en deux étapes : on calcule son complément à deux, puis on inverse les bits du résultat précédent. Le calcul du complément à deux demande d'avoir un circuit inverseur, et un incrémenteur.
[[File:Circuit incrémenteur-décrémenteur.png|centre|vignette|upright=2|Circuit incrémenteur-décrémenteur.]]
===L'incrémenteur-décrémenteur basé sur des demi-additionneurs===
Vous l'aurez sans doute remarqué, mais le demi-soustracteur ressemble beaucoup au demi-additionneur. La seule différence est une porte NON ajoutée au bon endroit dans le demi-soustracteur. La porte NON inverse le bit X, sachant que le demi-soustracteur fait la soustraction X - Y. De plus, l'opérande inversée est utilisée seulement pour le calcul des retenues, le bit de somme est calculé normalement.
{|
|[[File:Half-adder.svg|vignette|Demi-additionneur]]
|[[File:Half Subtractor Vektor.svg|vignette|upright=1.3|Demi-soustracteur]]
|}
La conséquence est qu'il est possible de fusionner un demi-additionneur avec un demi-soustracteur. L'idée est de remplacer la porte NON du demi-soustracteur par un inverseur commandable. Si on envoie un zéro sur l'entrée de commande, l'inverseur commandable se comportera comme une porte OUI et le circuit sera alors identique à un demi-additionneur. Si on envoie un 1 sur l'entrée de commande, l'inverseur commandable se comportera comme une porte NON. le circuit sera alors un demi-soustracteur. En remplaçant les demi-additionneurs (ou demi-soustracteurs) par de tels circuits, on peut obtenir un circuit faisant à la fois incrémenteur et décrémenteur.
Un défaut de cette approche est que les optimisations vues plus haut pour les demi-additionneurs ne fonctionne tout simplement plus. On est obligé d'utiliser une porte XOR et une porte ET séparées, pas question d'utiliser les demi-additionneurs optimisés vu au début du chapitre.
<noinclude>
{{NavChapitre | book=Fonctionnement d'un ordinateur
| prev=Les circuits de sélection
| prevText=Les circuits de sélection
| next=Les bascules : des mémoires de 1 bit
| nextText=Les bascules : des mémoires de 1 bit
}}
</noinclude>
j58qs0u8ptl0wi8ccwq0ifmj4kex6b3
768810
768809
2026-06-27T19:08:11Z
Mewtow
31375
/* L'incrémenteur-décrémenteur basé sur un incrémenteur */
768810
wikitext
text/x-wiki
Le circuit '''incrémenteur''' incrémente un nombre entier. Plus précisément, il prend en entrée un nombre entier, et fournit en entier le nombre suivant, il lui ajoute 1. Une telle opération peut paraitre triviale et peu intéressante. Cependant, elle est très utilisée et est absolument cruciale pour créer des compteurs, des circuits capables de compter ou décompter, qui auront droit à un chapitre dédié. De plus, au-delà de leur utilisation dans les compteurs, les incrémenteurs étaient très utilisés sur les premiers processeurs 8 bits, comme le Z-80, le 6502, les premiers processeurs x86 comme le 8008, le 8086, le 8085, et bien d'autres.
Il existe aussi des ''circuits décrémenteurs'', qui décrémentent un opérande, ainsi que des ''circuits incrémenteur-décrémenteur'', qui peuvent incrémenter ou décrémenter, selon comment on les configure. Je ne vais pas détailler ces circuits plus que ça, car de tels circuits sont assez rares, comparé à un circuit incrémenteur simple.
==Le demi-additionneur==
Le circuit incrémenteur effectue l'opération suivante :
<math>a_7</math> <math>a_6</math> <math>a_5</math> <math>a_4</math> <math>a_3</math> <math>a_2</math> <math>a_1</math> <math>a_0</math>
+ 0 0 0 0 0 0 0 1
Un incrémenteur basique doit donc faire une addition pour chaque colonne, et précisément une addition de deux bits. Il se trouve que la table d'addition est très simple en binaire. Jugez plutôt :
* 0 + 0 = 0, retenue = 0 ;
* 0 + 1 = 1, retenue = 0 ;
* 1 + 0 = 1, retenue = 0 ;
* 1 + 1 = 0, retenue = 1.
Un circuit capable d'additionner deux bits est appelé un '''demi-additionneur'''. Il dispose d'une sortie S pour la somme, et C pour la retenue (''carry'' an anglais).
===Le demi-additionneur classique===
Un demi-additionneur est très simple à construire avec les techniques vues dans les premiers chapitres. Voici sa table de vérité :
{|class="wikitable"
|-
! Retenue entrante !! Opérande 1 !! !! Retenue sortante !! Bit de somme
|-
||0||0|| ||0||0
|-
||0||1|| ||0||1
|-
||1||0|| ||0||1
|-
||1||1|| ||1||0
|}
On voit immédiatement que la colonne des retenues donne une porte ET, alors que celle du bit de somme est calculé par un XOR.
{| class="flexible"
|[[File:1-bit half-adder.svg|class=transparent|centre|Demi-addtionneur.]]
|[[File:Half-adder.svg|centre|class=transparent|Circuit d'un demi-addtionneur.]]
|}
Il existe beaucoup d'autres implémentations alternatives, qui utilisent moins de portes logiques, calculent la retenue plus rapidement, consomment moins d'énergie, et autres. Et il est intéressant d'étudier quelques alternatives, surtout qu'elles serviront plus bas, quand on étudiera le circuit incrémenteur du 8085.
===Le demi-additionneur basé sur une modification de la retenue===
Une source d'amélioration est liée à la porte XOR. En pratique, une porte XOR est composée en combinant plusieurs portes logiques ET/OU/NOR/NAND ensemble. Et il se trouve qu'il y a des redondances entre celles-ci et les portes utilisées pour calculer la retenue. Par exemple, rappelons qu'une porte XOR peut être construite avec une porte ET et deux portes NOR, comme illustré ci-dessous. Et il se trouve que la porte ET interne calcule la retenue sortante. En supprimant cette redondance, on économise quelques portes logiques. De plus, on se retrouve avec un demi-additionneur qui calcule le bit de somme à partir de la retenue sortante.
[[File:Demi-additionneur avec redondances éliminées.png|centre|vignette|upright=2|Demi-additionneur avec redondances éliminées]]
Le circuit précédent a une interprétation logique. Si vous regardez la table de vérité, vous remarquerez que la somme de deux bits est égale à l'inverse de la retenue sortante, sauf dans le cas où les deux bits additionnés valent zéro. Et le circuit précédent est basé là-dessus.
{|class="wikitable"
|-
! Retenue entrante !! Opérande 1 !! !! Retenue sortante !! Bit de somme
|- class="f_vert"
||0||0|| ||0||0
|- class="f_rouge"
||0||1|| ||0||1
|- class="f_rouge"
||1||0|| ||0||1
|- class="f_rouge"
||1||1|| ||1||0
|}
L'idée est de calculer l'inverse du bit de somme, avant de l'inverser avec une porte NON. L'inverse du bit de somme vaut 1, soit quand la retenue est à 1, soit quand les deux bits additionnés sont à 0. La porte ET calcule la retenue sortante, la première porte NOR détecte sur les deux bits d'entrée valent zéro. Une porte OU combine les deux résultat pour obtenir l'inverse du bit d'entrée, puis une porte NOn inverse le tout pour obtenir le bit de somme adéquat. Les deux portes sont fusionnées : c'est la seconde porte NOR.
[[File:Full adder HA + MUX.png|centre|vignette|upright=2.5|Demi-additionneur basé sur une inversion de la retenue sortante.]]
===Les implémentations alternatives===
Une implémentation alternative, qui n'utilise que des portes NOR, est la suivante :
[[File:Demi-additionneur fait avec des portes NOR et NAND.png|centre|vignette|upright=2|Demi-additionneur fait avec des portes NOR et NAND.]]
Une implémentation alternative utilise des portes NAND. Pour rappel, il est possible de créer une porte XOR avec 4 portes NAND. La première d'entre elle fait un NAND entre les deux bits d'entrée, ce qui fait qu'elle calcule l'inverse de la retenue sortante. Le tout est illustré ci-dessous. En théorie, on devrait utiliser une porte NON pour récupérer la retenue correcte. Mais nous verrons plus bas que ce n'est pas une obligation. Nous verrons plus bas un exemple où c'est l'inverse de la retenue qui est utilisée dans les calculs, pour rendre les calculs plus rapides.
[[File:Demi-additionneur fait avec des portes NAND.png|centre|vignette|upright=2|Demi-additionneur fait avec des portes NAND]]
==Le circuit incrémenteur==
Maintenant que l'on sait comment additionner deux bits, reprenons l'opération d'incrémentation :
<math>a_7</math> <math>a_6</math> <math>a_5</math> <math>a_4</math> <math>a_3</math> <math>a_2</math> <math>a_1</math> <math>a_0</math>
+ 0 0 0 0 0 0 0 1
------------------------------
Sur la colonne la plus à droite, il doit ajouter un au bit de poids faible. Pour les colonnes suivantes, il faut additionner le bit de l'opérande avec la retenue de la colonne précédente. En clair, on n'additionne que deux bits à chaque colonne : un 1 sur celle tout à droite, la retenue de la colonne précédente pour les autres. Et cela nous donne une idée de comment faire pour créer le circuit incrémenteur.
===L'incrémenteur à propagation de retenue===
Il suffit d'utiliser un demi-additionneur par colonne, et de les enchainer les uns à la suite des autres. Chaque demi-additionneur additionne le bit de l'opérande avec la retenue fournie par le demi-additionneur précédent. Le seul qui fait exception est celui pour la colonne de poids faible. Pour celui-là, il doit ajouter 1 au bit de poids faible. Le résultat est appelé un '''incrémenteur à propagation de retenue'''. Il est constitué de demi-additionneurs enchaînés les uns à la suite des autres, du bit de poids faible vers le bit de poids fort.
[[File:Circuit incrémenteur.png|centre|vignette|upright=3|Circuit incrémenteur.]]
Maintenant, regardons le demi-additionneur le plus à droite, celui pour le bit de poids faible. Son entrée de retenue entrante est mise à 1 pour faire l'incrémentation. Quelques incrémenteurs permettent de configurer cette entrée de retenue à 0 ou à 1, ce qui effectue : soit une opération identité (l'opérande est recopié sur la sortie), soit une incrémentation. Un tel circuit est nommé un '''incrémenteur commandable'''. Nous aurons à utiliser une fois ou deux de tels incrémenteurs commandables dans la suite du cours.
La '''sortie de retenue''' indique si l'incrémentation a entrainé un débordement d'entier. Lors d'un débordement d'entier, la retenue de sortie est à 1. Elle est à 0 en absence de débordement d'entier. La sortie de retenue est présente sur la plupart des incrémenteurs, mais laissons cela de côté pour le moment.
L'incrémenteur à propagation de retenue est le plus simple et le plus économe en portes logiques. Mais de tels incrémenteurs sont rarement utilisés. À la place, on leur préfère des incrémenteurs plus rapides, mais qui utilisent plus de portes logiques. De tels incrémenteurs accélèrent le calcul des retenues. En effet, la rapidité d'une incrémentation est limitée par la propagation de la retenue : les retenues commencent à être calculées au bit de poids fort et on doit les calculer une par une, jusqu’à atteindre le bit de poids fort. Et cette "propagation des retenues" prend du temps, d'autant plus de temps que l'opérande est longue. Il y a deux optimisations principales, appelées le ''carry skip'' et l'anticipation de retenue, que nous allons décrire ci-dessous.
===Les incrémenteurs optimisés===
L'optimisation que nous allns voir effectue l'incrémentation, non pas bit par bit, mais par paquets de deux bits. Le résultat est que l'incrémentation est deux fois plus rapide, ou presque. Le circuit incrémenteur est donc composé en enchainant non pas des demi-additionneurs, mais des '''incrémenteurs 2 bits''' qui incrémentent un opérande de deux bits.
[[File:Circuit incrémenteur optimisé, faisant l'incrémentation par paquet de deux bits.png|centre|vignette|upright=2.5|Circuit incrémenteur optimisé, faisant l'incrémentation par paquet de deux bits]]
Reste à concevoir l'incrémenteur 2 bits. Une première méthode part de deux demi-additionneurs et effectue quelques simplifications au niveau des transistors et des portes logiques. Par exemple, l'Intel 8085 a utilisé un circuit optimisé, fabriqué avec des portes NAND et NOR. La raison est qu'avec les technologies de transistors CMOS actuelles, les portes NAND et NOR utilisent moins de transistors que les portes ET et OU. Et cette possibilité a été utilisée pour effectuer des simplifications assez mineures, mais toujours bonnes à prendre. Mais nous ne pouvons pas en parler en détail ici, car nous n'avons pas encore parlé en détail des transistors. Cependant, un exemple bien précis nous est accesible : celui de l’incrémenteur du processeur 8085 d'Intel.
Le processeur 8085 d'Intel est un processeur 8 bits très ancien. Il contenait un incrémenteur 16 bits, qui était utilisé pour calculer des adresses mémoire. Et cet incrémenteur était un incrémenteur à propagation de retenue optimisé. Il utilisait lui aussi des incrémenteurs 2 bits un peu modifiés.
L'idée était que les retenues sortant des colonnes paires étaient inversées, les colonnes impaires faisaient les calculs à partir de cette retenue inversée. Les demi-additionneurs des colonnes impaires n'étaient donc pas les mêmes que ceux des colonnes paires. Leurs tables de vérité sont différentes, leurs circuits aussi, et aucun des deux ne ressemble à un demi-additionneur normal. En réalité, ils sont fabriqués à partir des implémentations vues plus haut, qui utilisent uniquement des portes NAND ou uniquement des portes NOR.
Pour comprendre l'intérêt de faire ainsi, nous devons préciser une chose importante : avec les technologies CMOS utilisées pour les processeurs depuis les années 70, les portes logiques les plus simples sont les portes NON, NOR et NAND. Les portes ET/OU sont fabriquées en combinant des portes NOR/NAND avec une porte NON, ce qui prend plus de transistors.
Un demi-additionneur est donc fabriqué comme illustré ci-dessous, en logique CMOS. Seules les portes pour le calcul de la retenue sont indiquées, la porte XOR pour le bit de somme est fabriquée à partir de plusieurs portes logiques.
[[File:Demi-additionneur en CMOS.png|centre|vignette|upright=2|Demi-additionneur en CMOS]]
Si on enchaine deux demi-additionneurs, cela donne ceci :
[[File:Brique de base de l'incrémenteur du 8085.png|centre|vignette|upright=2|Brique de base de l'incrémenteur du 8085]]
La retenue doit donc traverser quatre portes logiques, dont deux portes NON. En inversant la retenue, les portes en jaune dans le schéma précédent se simplifient. Il suffit d'utiliser la loi de de Morgan pour trouver un circuit équivalent aux portes jaunes, qui n'est autre qu'une porte NOR couplée à une porte NON. Un autre moyen d'obtenir le même résultat est de prendre un demi-additionneur fabriqué avec uniquement des portes NOR, et de retirer la porte NON adéquate. Il faut ensuite corriger l'entrée de retenue de la porte XOR, pour qu'elle fasse l'addition avec la retenue correctement. Le résultat est illustré ci-dessous.
[[File:Incrémenteur 2 bits de l'incrémenteur du 8085.png|centre|vignette|upright=2|Incrémenteur 2 bits de l'incrémenteur du 8085]]
Le circuit utilise le même nombre de portes logiques, il n'économise pas de transistors. Par contre, la retenue n'a plus qu'à traverser deux portes logiques, au lieu de quatre. La propagation de la retenue est donc plus rapide, l'incrémentation est donc plus rapide.
===Les incrémenteurs 2 bits à calcul séparé de la retenue===
Il existe une autre méthode qui permet de créer des incrémenteurs 2 bits plus rapides que les précédents, mais qui utilisent plus de portes logiques. Ces incrémenteurs 2 bits calculent séparément les deux bits du résultat et la retenue sortante. La retenue sortante est calculée à partir des bits de l’opérande, sans avoir besoin de propager des retenues. Ainsi, la retenue sortante est connue en avance, sans passer par deux demi-additionneurs. Formellement, ils sont un cas particulier d'une optimisation appelée l'''anticipation de retenue''. Si nous verrons le cas général dans la section suivante, ce cas particulier est une très bonne introduction.
Pour créer un tel incrémenteur, le mieux est de partir de sa table de vérité.
{|class="wikitable"
|-
! Opérande !! Retenue entrante !! !! Retenue sortante !! Résultat
|-
| 00 || 0 || || 0 || 00
|-
| 01 || 0 || || 0 || 01
|-
| 10 || 0 || || 0 || 10
|-
| 11 || 0 || || 0 || 11
|-
| colspan="5" |
|-
| 00 || 1 || || 0 || 01
|-
| 01 || 1 || || 0 || 10
|-
| 10 || 1 || || 0 || 11
|-
| 11 || 1 || || 1 || 00
|}
Elle nous dit que la retenue sortante ne vaut 1 que dans un seul cas : les deux bits d'opérande valent 1, la retenue entrante vaut 1. Elle se calcule alors avec une porte ET à trois entrées. Pour les deux bits du résultat, les équations logiques ne donnent pas un résultat satisfaisant. La solution la plus simple est d'utiliser deux demi-additionneurs et de retirer les portes logiques superflues. Le résultat est alors un '''incrémenteur 2 bits ''carry skip''''', où la retenue sortante est calculée séparément du reste.
[[File:Incrémenteur carry skip 2 bits.png|centre|vignette|upright=2.5|Incrémenteur ''carry skip'' 2 bits]]
===Les incrémenteurs à anticipation de retenue===
L''''anticipation de retenue''' calcule la retenue d'une colonne sans attendre les retenues des colonnes précédentes. Dans le cas idéal, toutes les retenues sont calculées en parallèle, en même temps, et sont ensuite envoyées aux demi-additionneurs. Il s'agit d'une optimisation qui est utilisée pour l'incrémentation, l'addition et d'autres opérations similaires. Pour l'incrémentation, déterminer la retenue ne demande pas de calculs complexes, contrairement à l'addition.
[[File:Incrémenteur à anticipation de retenues, 8 bits.png|centre|vignette|upright=2.5|Incrémenteur à anticipation de retenues, 8 bits.]]
Pour le comprendre, on peut regarder comment la retenue sortant d'un demi-additionneur est formée. Elle est calculée en faisant un ET logique entre la retenue entrante et le bit d'opérande. Une retenue est donc un ET logique entre toutes les retenues précédentes. Un incrémenteur à anticipation de retenue utilise donc une porte ET à plusieurs entrées pour calculer une retenue.
[[File:Anticipation de retenue pour un bit du résultat, incrémenteur.png|centre|vignette|upright=2.5|Anticipation de retenue pour un bit du résultat, incrémenteur]]
En théorie, on peut utiliser une porte ET à plusieurs entrées pour chaque bit de l'opérande. Cependant, cela entrainera un cout en transistors très important. Pour éviter de gaspiller trop de portes logiques, une solution est de mélanger anticipation de retenues et propagation de retenue. Par exemple, pour un incrémenteur 32 bits, on peut découper l'opérande en 4 octets : on anticipe les retenues pour chaque octet, mais l'incrémentation de chaque octet se fait avec propagation de retenue et/ou ''carry skip''. Plus haut, nous avons vu un cas particulier où les retenues étaient anticipées par blocs de deux bits.
[[File:Incrémenteur à anticipation de retenues.png|centre|vignette|upright=3|Incrémenteur hybride utilisant partiellement l'anticipation de retenues.]]
Quelques processeurs utilisaient l'anticipation de retenues. Par exemple, le processeur Z-80 de Zilog utilisait un incrémenteur pour des nombres de 16 bits, ce qui demandait des performances assez élevées. Et cet incrémenteur utilisait à la fois anticipation de retenues et ''carry skip''. Il était découpé en quatre blocs avant anticipation de retenues entre eux : un bloc regroupant les 7 bits de poids faible, suivi par un bloc de 5 bits, lui-même suivi par un bloc de 3 bits, terminé par un dernier bit isolé. A l'intérieur de ces blocs, les bits sont regroupés en paires utilisant le ''carry skip''. Pour ceux qui veulent en savoir plus sur cet incrémenteur, voici un lien sur le sujet :
* [https://www.righto.com/2013/11/the-z-80s-16-bit-incrementdecrement.html The Z-80's 16-bit increment/decrement circuit reverse engineered].
==Les décrémenteurs==
Les circuits décrémenteurs font l'inverse d'un incrémenteur : au lieu d'ajouter 1 à un opérande, ils lui soustraient 1. La différence n'est pas énorme, ce qui fait que les circuits incrémenteurs et décrémenteurs se ressemblent beaucoup.
La soustraction se fait en binaire comme en décimal. On soustrait deux chiffres, puis on doit propager une éventuelle retenue sur la colonne suivante. La retenue apparait quand le chiffre soustrait est plus grand que l'autre chiffre. Elle est propagée sur la colonne suivante, où elle doit être soustraite du résultat. Pour le dire autrement, après avoir soustrait un chiffre, on doit de plus soustraire la retenue de la colonne précédente.
La décrémentation effectue le calcul suivant :
<math>a_7</math> <math>a_6</math> <math>a_5</math> <math>a_4</math> <math>a_3</math> <math>a_2</math> <math>a_1</math> <math>a_0</math>
- 0 0 0 0 0 0 0 1
La colonne la plus à droite soustrait un 1. Par contre, les colonnes suivantes soustraient un zéro. Soustraire un zéro ne change rien. Mais il ne faut pas oublier de soustraire une éventuellement retenue, propagée depuis la colonne précédente. En clair, un décrémenteur peut se construire si on sait soustraire deux bits. On soustrait un 1 sur la colonne la plus à droite, on soustrait une retenue sur les autres colonnes. Et pour soustraire deux bits, il faut utiliser un demi-soustracteur.
===Le demi-soustracteur et le décrémenteur à propagation de retenue===
Un '''demi-soustracteur''' soustrait deux bits. Il prend deux bits d'entrée et fournit deux sorties : un bit de différence et une retenue. Pour comprendre d'où vient cette retenue, et comment soustraire deux bits, traitons les quatre cas possibles un par un.
Premièrement, soustraire zéro à un bit ne changera rien :
* 0 - 0 = 0 ;
* 1 - 0 = 1.
Si on soustrait 1 à un bit qui vaut 1, on obtient zéro.
* 1 - 1 = 0.
Maintenant, que se passe-t-il si on soustrait 1 à 0 ? Voici le résultat :
* 0 - 1 = 1 et une retenue propagée sur la colonne suivante, où elle est soustraite.
La table de vérité est donc la suivante. Détail important : le demi-soustracteur fait ici la soustraction X - Y. En anglais, X est appelé le ''Minuend'' et Y est appelé le ''Subtrahend'', mais ces termes n'ont pas de traduction française. Aussi, je vais les utiliser tels quels dans ce qui suit.
{|class="wikitable"
|-
! ''Minuend'' !! ''Subtrahend'' !! !! Retenue sortante !! Bit de somme
|-
||0||0|| ||0||0
|-
||0||1|| ||1||1
|-
||1||0|| ||0||1
|-
||1||1|| ||0||0
|}
Si on construit la table de vérité, et qu'on construit le circuit associé, on tombe sur ce circuit.
[[File:Half Subtractor Vektor.svg|centre|vignette|Demi-soustracteur.]]
Vous remarquerez que le bit de somme se calcule de la même manière que pour l'addition, seul le calcul de la retenue change. Comme pour le demi-additionneur, on peut exploiter des redondances entre calcul de la retenue et porte XOR, mais ce ne sont pas les mêmes. Le circuit obtenu après exploitation de ces redondances est le suivant :
[[File:Demi-soustracteur optimisé, sans redondances.png|centre|vignette|upright=1.5|Demi-soustracteur optimisé, sans redondances]]
Un décrémenteur à propagation de retenue se construit comme un incrémenteur, sauf qu'on remplace les demi-additionneurs par des demi-soustracteurs. En clair, on enchaine plusieurs demi-soustracteurs les uns à la suite des autres.
===Le décrémenteur à anticipation de retenue===
Il est possible d'utiliser l'anticipation de retenue, mais les circuits sont cependant différents de l'incrémenteur. Idem avec le ''carry skip'', et toute autre optimisation possible. La raison est que les retenues sont calculées différemment, ce qui fait que l’anticipation de retenue doit être adaptée. La retenue d'une colonne ne se calcule plus avec un ET des bits des colonnes précédentes. A la place, ces bits doivent être inversés avant de faire le ET. Le résultat est que la porte ET est remplacée par une porte NOR.
Pour ceux qui s'en rappelle, faire un NOR de N bits permet de vérifier s'ils sont tous à zéro. Et c'est le cas ici. Lors d'une incrémentation, on a une retenue quand tous les bits précédents de l'opérande sont à 1. Pour la décrémentation, c'est l'inverse : on a une retenue quand tous les bits précédents sont à 0.
De fait, décrémenter un nombre est assez simple. Il y a deux cas principaux :
* soit le bit de poids faible est un 1, ce qui signifie que le nombre est impair ;
* soit un ou plusieurs bits de poids faible consécutifs sont à 0, suivis par un 1.
Dans le premier cas, le bit de poids faible est juste inversé. Dans le second cas, le 1 est inversé, et les 0 de poids faible deviennent des 1. Par exemple, Prenez un nombre de la forme xxxxxxx10000. Après décrémentation, le résultat sera xxxxxxxx011111 : les 0 de poids faible deviennent des 1, le premier 1 en partant de la droite est inversé. Et la même logique se généralise pour tout nombre avant un ou plusieurs zéros dans les bits de poids faible.
==Les incrémenteurs-décrémenteurs==
Les circuits décrémenteurs ressemblent beaucoup aux incrémenteurs. Aussi, vous ne serez pas étonné qu'il soit possible de fusionner un incrémenteur et un décrémenteur en un seul circuit. Un tel circuit s'appelle un '''incrémenteur-décrémenteur'''.
Un tel circuit a une interface similaire à celle d'un incrémenteur, avec une entrée sur laquelle présenter l'opérande, et une sortie sur laquelle récupérer le résultat. Il a aussi une entrée de retenue, sur laquelle envoyer un 1 (incrémentation) ou un 0 (opération NOP/identité). Et il a aussi une sortie de retenue, qui indique que le résultat du calcul a débordé. Mais en plus de cela, il dispose d'une '''entrée de commande''' qui permet de choisir entre incrémentation et décrémentation. En général, elle est mise à 0 lors d'une incrémentation et à 1 lors d'une décrémentation.
Il existe plusieurs manières de créer un incrémenteur-décrémenteur, que nous allons voir si dessous. Nous n'allons pas voir le cas le plus évident, aussi ne vous étonnez pas si vous avez remarqué qu'un demi-additionneur et un demi-soustracteur se ressemblent beaucoup.
===L'incrémenteur-décrémenteur basé sur un incrémenteur===
Une première implémentation se base sur les propriétés du complément à deux, et plus précisément sur cette équation :
: <math>\overline{x} + 1 = \overline{x - 1}</math>
Les deux termes de cette équation sont simplement des définitions équivalentes du complément à deux d'un nombre entier. Celui-ci se calcule de deux manières : soit on incrémente son complément à 1, soit on prend le complément à 1 de son décrément. Et cela nous permet de déterminer comment obtenir une incrémentation à partir d'une décrémentation et inversement.
Que nous dit cette équation ? Simplement que l'on peut décrémenter un nombre si on a deux inverseurs commandables, et un circuit incrémenteur. La décrémentation se fait alors en deux étapes : on calcule son complément à deux, puis on inverse les bits du résultat précédent. Le calcul du complément à deux demande d'avoir un circuit inverseur, et un incrémenteur. Les inverseurs commandables sont configurés par l'entrée de commande.
[[File:Circuit incrémenteur-décrémenteur.png|centre|vignette|upright=2|Circuit incrémenteur-décrémenteur.]]
===L'incrémenteur-décrémenteur basé sur des demi-additionneurs===
Vous l'aurez sans doute remarqué, mais le demi-soustracteur ressemble beaucoup au demi-additionneur. La seule différence est une porte NON ajoutée au bon endroit dans le demi-soustracteur. La porte NON inverse le bit X, sachant que le demi-soustracteur fait la soustraction X - Y. De plus, l'opérande inversée est utilisée seulement pour le calcul des retenues, le bit de somme est calculé normalement.
{|
|[[File:Half-adder.svg|vignette|Demi-additionneur]]
|[[File:Half Subtractor Vektor.svg|vignette|upright=1.3|Demi-soustracteur]]
|}
La conséquence est qu'il est possible de fusionner un demi-additionneur avec un demi-soustracteur. L'idée est de remplacer la porte NON du demi-soustracteur par un inverseur commandable. Si on envoie un zéro sur l'entrée de commande, l'inverseur commandable se comportera comme une porte OUI et le circuit sera alors identique à un demi-additionneur. Si on envoie un 1 sur l'entrée de commande, l'inverseur commandable se comportera comme une porte NON. le circuit sera alors un demi-soustracteur. En remplaçant les demi-additionneurs (ou demi-soustracteurs) par de tels circuits, on peut obtenir un circuit faisant à la fois incrémenteur et décrémenteur.
Un défaut de cette approche est que les optimisations vues plus haut pour les demi-additionneurs ne fonctionne tout simplement plus. On est obligé d'utiliser une porte XOR et une porte ET séparées, pas question d'utiliser les demi-additionneurs optimisés vu au début du chapitre.
<noinclude>
{{NavChapitre | book=Fonctionnement d'un ordinateur
| prev=Les circuits de sélection
| prevText=Les circuits de sélection
| next=Les bascules : des mémoires de 1 bit
| nextText=Les bascules : des mémoires de 1 bit
}}
</noinclude>
007prv7cc70i4ehxjdxh9o87a7ysqjt
Dictionnaire de philosophie/W
0
83961
768819
768376
2026-06-28T04:17:08Z
PandaMystique
119061
768819
wikitext
text/x-wiki
{{DicoPhilo|W}}
<div style="display:grid; grid-template-columns:repeat(auto-fit, minmax(280px, 1fr)); gap:2em; width:100%; margin-bottom:2em;">
<div style="padding:1.5em; background:linear-gradient(135deg, #ffffff40 0%, #e0e4e840 100%); border-radius:10px; box-shadow:0 3px 10px rgba(0,0,0,0.1);">
:Welfarisme
:Weltanschauung
</div>
<div style="padding:1.5em; background:linear-gradient(135deg, #ffffff40 0%, #e0e4e840 100%); border-radius:10px; box-shadow:0 3px 10px rgba(0,0,0,0.1);">
:Wertfreiheit
:[[Dictionnaire de philosophie/Timothy Williamson|Williamson, Timothy]]
</div>
<div style="padding:1.5em; background:linear-gradient(135deg, #ffffff40 0%, #e0e4e840 100%); border-radius:10px; box-shadow:0 3px 10px rgba(0,0,0,0.1);">
:Wu wei
</div>
</div>
{{PhiloRecherche}}
{{autocat}}
f984l5i41k4rqcp1r6r6n8tdmtb0sjg
Dictionnaire de philosophie/X
0
83978
768820
2026-06-28T04:20:47Z
PandaMystique
119061
Page créée avec « {{DicoPhilo|X}} <div style="display:grid; grid-template-columns:repeat(auto-fit, minmax(280px, 1fr)); gap:2em; width:100%; margin-bottom:2em;"> <div style="padding:1.5em; background:linear-gradient(135deg, #ffffff40 0%, #e0e4e840 100%); border-radius:10px; box-shadow:0 3px 10px rgba(0,0,0,0.1);"> :Xénocrate :Xénophane </div> <div style="padding:1.5em; background:linear-gradient(135deg, #ffffff40 0%, #e0e4e840 100%); border-radius:10px; box-shadow:0 3px 10px rgb... »
768820
wikitext
text/x-wiki
{{DicoPhilo|X}}
<div style="display:grid; grid-template-columns:repeat(auto-fit, minmax(280px, 1fr)); gap:2em; width:100%; margin-bottom:2em;">
<div style="padding:1.5em; background:linear-gradient(135deg, #ffffff40 0%, #e0e4e840 100%); border-radius:10px; box-shadow:0 3px 10px rgba(0,0,0,0.1);">
:Xénocrate
:Xénophane
</div>
<div style="padding:1.5em; background:linear-gradient(135deg, #ffffff40 0%, #e0e4e840 100%); border-radius:10px; box-shadow:0 3px 10px rgba(0,0,0,0.1);">
:Xénophobie
:Xénophon
</div>
<div style="padding:1.5em; background:linear-gradient(135deg, #ffffff40 0%, #e0e4e840 100%); border-radius:10px; box-shadow:0 3px 10px rgba(0,0,0,0.1);">
:Xénos
:Xénotransplantation
</div>
</div>
{{PhiloRecherche}}
{{autocat}}
k5vl2m0ey6fk8ijusmu4h1cx0x8t37j
Dictionnaire de philosophie/Y
0
83979
768821
2026-06-28T04:25:03Z
PandaMystique
119061
Page créée avec « {{DicoPhilo|Y}} <div style="display:grid; grid-template-columns:repeat(auto-fit, minmax(280px, 1fr)); gap:2em; width:100%; margin-bottom:2em;"> <div style="padding:1.5em; background:linear-gradient(135deg, #ffffff40 0%, #e0e4e840 100%); border-radius:10px; box-shadow:0 3px 10px rgba(0,0,0,0.1);"> :Yājñavalkya :Yang Zhu :Yi (rectitude) </div> <div style="padding:1.5em; background:linear-gradient(135deg, #ffffff40 0%, #e0e4e840 100%); border-radius:10px; box-shad... »
768821
wikitext
text/x-wiki
{{DicoPhilo|Y}}
<div style="display:grid; grid-template-columns:repeat(auto-fit, minmax(280px, 1fr)); gap:2em; width:100%; margin-bottom:2em;">
<div style="padding:1.5em; background:linear-gradient(135deg, #ffffff40 0%, #e0e4e840 100%); border-radius:10px; box-shadow:0 3px 10px rgba(0,0,0,0.1);">
:Yājñavalkya
:Yang Zhu
:Yi (rectitude)
</div>
<div style="padding:1.5em; background:linear-gradient(135deg, #ffffff40 0%, #e0e4e840 100%); border-radius:10px; box-shadow:0 3px 10px rgba(0,0,0,0.1);">
:Yi Jing
:Yin et Yang
</div>
<div style="padding:1.5em; background:linear-gradient(135deg, #ffffff40 0%, #e0e4e840 100%); border-radius:10px; box-shadow:0 3px 10px rgba(0,0,0,0.1);">
:Yogācāra
</div>
</div>
{{PhiloRecherche}}
{{autocat}}
61m61aohvgvaw6cggzwtzmwtiq7pf29
Dictionnaire de philosophie/Z
0
83980
768822
2026-06-28T04:29:30Z
PandaMystique
119061
Page créée avec « {{DicoPhilo|Z}} <div style="display:grid; grid-template-columns:repeat(auto-fit, minmax(280px, 1fr)); gap:2em; width:100%; margin-bottom:2em;"> <div style="padding:1.5em; background:linear-gradient(135deg, #ffffff40 0%, #e0e4e840 100%); border-radius:10px; box-shadow:0 3px 10px rgba(0,0,0,0.1);"> :Zeitgeist :Zen :Zénon d'Élée </div> <div style="padding:1.5em; background:linear-gradient(135deg, #ffffff40 0%, #e0e4e840 100%); border-radius:10px; box-shadow:0 3px... »
768822
wikitext
text/x-wiki
{{DicoPhilo|Z}}
<div style="display:grid; grid-template-columns:repeat(auto-fit, minmax(280px, 1fr)); gap:2em; width:100%; margin-bottom:2em;">
<div style="padding:1.5em; background:linear-gradient(135deg, #ffffff40 0%, #e0e4e840 100%); border-radius:10px; box-shadow:0 3px 10px rgba(0,0,0,0.1);">
:Zeitgeist
:Zen
:Zénon d'Élée
</div>
<div style="padding:1.5em; background:linear-gradient(135deg, #ffffff40 0%, #e0e4e840 100%); border-radius:10px; box-shadow:0 3px 10px rgba(0,0,0,0.1);">
:Zénon de Kition
:Zététique
:Zhu Xi
</div>
<div style="padding:1.5em; background:linear-gradient(135deg, #ffffff40 0%, #e0e4e840 100%); border-radius:10px; box-shadow:0 3px 10px rgba(0,0,0,0.1);">
:Zhuangzi
:Zoé
:Zoon politikon
</div>
</div>
{{PhiloRecherche}}
{{autocat}}
5ajodv4ndaaktipewllqvyi2welb7en
Latin/Vocabulaire/Verba Latina frequentissima/076-100
0
83981
768825
2026-06-28T04:46:46Z
PandaMystique
119061
Page créée avec « {{Sous-pages|Latin/Vocabulaire/Verba Latina frequentissima}} <div style="text-align: center; font-size: 90%;"> [[#annus|annus]] · [[#fero|fero]] · [[#peto|peto]] · [[#vis|vis]] · [[#eo|eo]] · [[#dominus|dominus]] · [[#terra|terra]] · [[#tempus|tempus]] · [[#mitto|mitto]] · [[#nox|nox]] · [[#ago|ago]] · [[#verbum|verbum]] · [[#accipio|accipio]] · [[#inter|inter]] · [[#credo|credo]] · [[#ita|ita]] · [[#tum, tunc|tum, tunc]] · [[#semper|semper]] ·... »
768825
wikitext
text/x-wiki
{{Sous-pages|Latin/Vocabulaire/Verba Latina frequentissima}}
<div style="text-align: center; font-size: 90%;">
[[#annus|annus]] · [[#fero|fero]] · [[#peto|peto]] · [[#vis|vis]] · [[#eo|eo]] · [[#dominus|dominus]] · [[#terra|terra]] · [[#tempus|tempus]] · [[#mitto|mitto]] · [[#nox|nox]] · [[#ago|ago]] · [[#verbum|verbum]] · [[#accipio|accipio]] · [[#inter|inter]] · [[#credo|credo]] · [[#ita|ita]] · [[#tum, tunc|tum, tunc]] · [[#semper|semper]] · [[#sine|sine]] · [[#virtus|virtus]] · [[#amor|amor]] · [[#ratio|ratio]] · [[#natura|natura]] · [[#scribo|scribo]] · [[#lego|lego]]
</div>
== annus ==
''annus, anni'' (subst. m.) : année
''Exempla gradatim digesta''
# ''Annus duodecim menses habet.'' (L’année a douze mois.)
# ''Abhinc tres annos Romae fui.'' (Il y a trois ans, j’étais à Rome.) Nota : abhinc + accusatif.
# ''Multis post annis domum revertit.'' (Bien des années plus tard, il revint chez lui.) Nota : post adverbial.
''Exempla elocutionis''
* ''Eheu fugaces labuntur anni!'' (Hélas, fugitives, les années s’écoulent !) (Horace)
*: ''Commentaire :'' interjection initiale eheu, puis disjonction fugaces... anni qui enserre le verbe ; la fuite du temps mimée par la phrase qui glisse.
* ''Anno aetatis suae octogesimo mortuus est.'' (Il mourut dans sa quatre-vingtième année.)
*: ''Commentaire :'' formule des inscriptions funéraires et des notices biographiques ; style lapidaire au sens propre, où la précision tient lieu d’émotion.
== fero ==
''fero, ferre, tuli, latum'' (v.) : porter ; supporter
''Exempla gradatim digesta''
# ''Aquam fero.'' (Je porte de l’eau.)
# ''Fama fert regem mortuum esse.'' (Le bruit court que le roi est mort.) Nota : fert + proposition infinitive.
# ''Fortiter ferendum est quod mutari non potest.'' (Il faut supporter avec courage ce qui ne peut être changé.)
''Exempla elocutionis''
* ''Tribunus legem ad populum tulit.'' (Le tribun proposa une loi au peuple.)
*: ''Commentaire :'' legem ferre, locution institutionnelle (proposer une loi au vote) ; le verbe le plus concret de la langue, « porter », ossifié en terme de droit public.
* ''Quod fors feret, feremus.'' (Ce que le sort apportera, nous le supporterons.) (d’après Térence)
*: ''Commentaire :'' paronomase fors / feret / feremus ; le même verbe dit le hasard qui donne et l’homme qui endure. L’acceptation proverbiale par le jeu phonique.
== peto ==
''peto, petere, petivi, petitum'' (v.) : chercher à atteindre, demander
''Exempla gradatim digesta''
# ''Panem a matre peto.'' (Je demande du pain à ma mère.)
# ''Consulatum eo anno petivit.'' (Il brigua le consulat cette année-là.) Nota : petere = « briguer ».
# ''Hostes summa vi castra petiverunt.'' (Les ennemis attaquèrent le camp de toutes leurs forces.) Nota : petere = « attaquer ».
''Exempla elocutionis''
* ''Quod petis, hic est.'' (Ce que tu cherches est ici.) (Horace)
*: ''Commentaire :'' brièveté et déixis : le bonheur n’est pas ailleurs, et la phrase tient en quatre mots dont un déictique ; la sagesse horatienne refuse l’amplification.
* ''Unum hoc a te peto.'' (Je ne te demande que cela.)
*: ''Commentaire :'' unum en tête : la requête se fait pathétique par sa sobriété affichée ; demander une seule chose, c’est la demander toute.
== vis ==
''vis, vis'' (subst. f.) : force, violence
''Exempla gradatim digesta''
# ''Vis venti magna est.'' (La force du vent est grande.)
# ''Vim vi repellere licet.'' (Il est permis de repousser la violence par la violence.) (principe juridique romain)
# ''Vires corporis cum annis deficiunt.'' (Les forces du corps déclinent avec les années.) Nota : vires, pluriel = « les forces ».
''Exempla elocutionis''
* ''Vi et armis urbem cepit.'' (Il prit la ville par la force des armes.)
*: ''Commentaire :'' vi et armis, hendiadys figé du vocabulaire juridique et militaire ; deux ablatifs coordonnés pour une seule notion, plénitude formulaire.
* ''In verbis inest vis quaedam mira.'' (Il y a dans les mots une étrange puissance.)
*: ''Commentaire :'' quaedam atténue et désigne à la fois l’indéfinissable ; style de l’essai, qui montre du doigt ce qu’il renonce à définir. Programme de toute stylistique.
== eo ==
''eo, ire, ivi (ii), itum'' (v.) : aller
''Exempla gradatim digesta''
# ''Domum eo.'' (Je vais à la maison.)
# ''Cubitum ire tempus est.'' (Il est temps d’aller se coucher.) Nota : supin en -um après verbe de mouvement.
# ''Obviam hostibus itum est.'' (On marcha à la rencontre des ennemis.) Nota : passif impersonnel.
''Exempla elocutionis''
* ''I, sequere Italiam ventis!'' (Va, cours vers l’Italie au gré des vents !) (Virgile)
*: ''Commentaire :'' l’impératif monosyllabique i, jeté par Didon, est l’un des mots les plus brefs et les plus durs de la langue ; l’ironie amère tient dans une seule lettre.
* ''Sic itur ad astra.'' (C’est ainsi qu’on va vers les astres.) (Virgile)
*: ''Commentaire :'' passif impersonnel + métaphore ascensionnelle ; la formule, détachée de l’Énéide, est devenue devise : preuve que le style fait la fortune des phrases.
== dominus ==
''dominus, domini'' (subst. m.) : maître
''Exempla gradatim digesta''
# ''Dominus canem vocat.'' (Le maître appelle son chien.)
# ''Domino absente, nihil in villa agitur.'' (En l’absence du maître, rien ne se fait à la ferme.) Nota : ablatif absolu.
# ''Sui quisque animi dominus esse debet.'' (Chacun doit être maître de son âme.)
''Exempla elocutionis''
* ''Domitianus se dominum et deum appellari iussit.'' (Domitien ordonna qu’on l’appelât maître et dieu.) (d’après Suétone)
*: ''Commentaire :'' dominus et deus, titulature scandaleuse : dominus suppose des esclaves ; le mot, neutre dans la maison, devient tyrannique dans l’État. Connotation, encore.
* ''Sapiens solus sui dominus est.'' (Seul le sage est maître de lui-même.)
*: ''Commentaire :'' retournement philosophique du vocabulaire servile : la domination intériorisée ; génitif sui en facteur de paradoxe. Thème stoïcien central.
== terra ==
''terra, terrae'' (subst. f.) : terre
''Exempla gradatim digesta''
# ''Terra fructus dat.'' (La terre donne des fruits.)
# ''Terra marique pugnatum est.'' (On combattit sur terre et sur mer.) Nota : locution + passif impersonnel.
# ''Quae regio in terris nostri non plena laboris?'' (Quelle région sur terre n’est pas pleine de nos épreuves ?) (Virgile)
''Exempla elocutionis''
* ''Ultra has regiones terra incognita iacet.'' (Au-delà de ces régions s’étend une terre inconnue.)
*: ''Commentaire :'' terra incognita, formule des géographes passée en locution universelle ; l’inconnu cartographié par deux mots.
* ''Caelum ac terras miscere videbantur.'' (Ils semblaient confondre ciel et terre.)
*: ''Commentaire :'' locution hyperbolique du chaos (cf. Virgile) ; mélanger le haut et le bas, image cosmique pour le désordre humain.
== tempus ==
''tempus, temporis'' (subst. n.) : temps, moment
''Exempla gradatim digesta''
# ''Tempus celeriter fugit.'' (Le temps fuit rapidement.)
# ''Tempori cedere sapientis est.'' (Céder aux circonstances est le fait du sage.) Nota : tempus = « les circonstances ».
# ''O tempora, o mores!'' (Ô temps, ô mœurs !) (Cicéron) Nota : exclamation à l’accusatif.
''Exempla elocutionis''
* ''Tempus collige et serva.'' (Recueille ton temps et garde-le.) (d’après Sénèque)
*: ''Commentaire :'' impératifs brefs et métaphore économique : le temps comme bien qu’on thésaurise ; toute la première Lettre à Lucilius est dans ce lexique comptable.
* ''Tempus edax rerum.'' (Le temps qui dévore toutes choses.) (Ovide)
*: ''Commentaire :'' épithète edax + génitif objectif rerum ; la personnification dévorante en trois mots, sans verbe : le groupe nominal suffit au mythe.
== mitto ==
''mitto, mittere, misi, missum'' (v.) : envoyer
''Exempla gradatim digesta''
# ''Litteras ad te mitto.'' (Je t’envoie une lettre.)
# ''Nuntius ad regem missus est.'' (Un messager fut envoyé au roi.)
# ''Caesar legatos pacem petitum misit.'' (César envoya des ambassadeurs demander la paix.) Nota : supin de but.
''Exempla elocutionis''
* ''Missa istaec faciamus.'' (Laissons cela.) (d’après Térence)
*: ''Commentaire :'' missum facere, locution familière de la conversation (= passons) ; istaec, forme parlée du démonstratif. La comédie est notre archive du latin oral.
* ''Mitte hanc de pectore curam.'' (Chasse ce souci de ton cœur.) (Virgile)
*: ''Commentaire :'' mittere métaphorique = « congédier, chasser » ; l’impératif lyrique adressé à l’âme, geste de consolation.
== nox ==
''nox, noctis'' (subst. f.) : nuit
''Exempla gradatim digesta''
# ''Nox obscura est.'' (La nuit est sombre.)
# ''Nocte intempesta profecti sunt.'' (Ils partirent en pleine nuit.) Nota : nocte intempesta, expression figée.
# ''Multa nocte domum rediit.'' (Il rentra chez lui tard dans la nuit.) Nota : multa nocte = « la nuit étant avancée ».
''Exempla elocutionis''
* ''Nox erat et caelo fulgebat luna sereno.'' (C’était la nuit, et la lune brillait dans un ciel serein.) (Horace)
*: ''Commentaire :'' nox erat, ouverture type de la description nocturne (topos hérité de l’épopée) ; deux mots installent le décor, la disjonction caelo... sereno l’achève.
* ''Omnes una manet nox.'' (Une même nuit nous attend tous.) (Horace)
*: ''Commentaire :'' nuit = mort, métaphore que una rend implacable ; l’hyperbate omnes... nox encadre la phrase comme la nuit encercle les vivants.
== ago ==
''ago, agere, egi, actum'' (v.) : faire, mener, agir
''Exempla gradatim digesta''
# ''Agricola boves agit.'' (Le paysan mène ses bœufs.)
# ''Gratias tibi ago.'' (Je te remercie.)
# ''Actum est de nobis, nisi auxilium venit.'' (C’en est fait de nous, si le secours n’arrive pas.) Nota : actum est de = « c’en est fait de ».
''Exempla elocutionis''
* ''Age, dic mihi verum.'' (Allons, dis-moi la vérité.)
*: ''Commentaire :'' age, impératif devenu interjection d’encouragement ; le verbe le plus général de la langue usé jusqu’à n’être plus qu’un geste oral.
* ''« Quid agitur? » « Statur. »'' (« Que fait-on ? » « On reste planté là. ») (d’après Plaute)
*: ''Commentaire :'' réplique comique jouant sur les passifs impersonnels ; la grammaire elle-même devient ressort du rire. Le latin parlé aime ces tours.
== verbum ==
''verbum, verbi'' (subst. n.) : mot, parole
''Exempla gradatim digesta''
# ''Verbum novum disco.'' (J’apprends un mot nouveau.)
# ''Verba magistri memoria tenemus.'' (Nous gardons en mémoire les paroles du maître.)
# ''Mihi verba dedisti!'' (Tu m’as trompé !) Nota : verba dare alicui = « tromper quelqu’un ».
''Exempla elocutionis''
* ''Verbum sapienti sat est.'' (À bon entendeur, un mot suffit.) (d’après Plaute et Térence)
*: ''Commentaire :'' maxime elliptique (sat = satis) ; le proverbe sur l’économie des mots est lui-même économe. Datif d’intérêt sapienti en pivot.
* ''Hunc librum ad verbum e Graeco converti.'' (J’ai traduit ce livre du grec mot à mot.)
*: ''Commentaire :'' ad verbum, terme technique de la traduction (opposé chez Cicéron à la traduction du sens) ; vocabulaire utile à tout traducteur de philosophie.
== accipio ==
''accipio, accipere, accepi, acceptum'' (v.) : recevoir, accueillir
''Exempla gradatim digesta''
# ''Epistulam accipio.'' (Je reçois une lettre.)
# ''Multa vulnera in proelio accepit.'' (Il reçut de nombreuses blessures au combat.)
# ''Quod dixi, in bonam partem accipias velim.'' (Ce que j’ai dit, je voudrais que tu le prennes en bonne part.) Nota : subjonctif de politesse.
''Exempla elocutionis''
* ''Accipe nunc Danaum insidias.'' (Apprends maintenant les ruses des Grecs.) (Virgile)
*: ''Commentaire :'' impératif adressé à l’auditeur interne (et au lecteur) ; accipe relance le récit épique comme on tend un objet. Danaum, génitif archaïque, patine voulue.
* ''Hoc tibi acceptum refero.'' (Je porte cela à ton crédit.)
*: ''Commentaire :'' acceptum referre, métaphore comptable (inscrire en recette) ; la gratitude dite dans la langue des registres, trait très romain.
== inter ==
(praep. + acc.) : entre, parmi
''Exempla gradatim digesta''
# ''Inter arbores sedemus.'' (Nous sommes assis entre les arbres.)
# ''Fratres inter se amant.'' (Les frères s’aiment l’un l’autre.) Nota : inter se, réciprocité.
# ''Inter omnes constat Romam a Romulo conditam esse.'' (Tout le monde s’accorde à dire que Rome fut fondée par Romulus.) Nota : constat + proposition infinitive.
''Exempla elocutionis''
* ''Silent enim leges inter arma.'' (Car les lois se taisent au milieu des armes.) (Cicéron)
*: ''Commentaire :'' personnification des lois muettes ; silent en tête de phrase, position de relief, fait entendre le silence avant d’en donner la cause.
* ''Haec inter nos dicta sint.'' (Que cela reste entre nous.)
*: ''Commentaire :'' formule de confidence ; subjonctif parfait de souhait et locution inter nos : la complicité a sa grammaire.
== credo ==
''credo, credere, credidi, creditum'' (v.) : croire, confier
''Exempla gradatim digesta''
# ''Matri credo.'' (Je crois ma mère.) Nota : credere + datif.
# ''Credo te fessum esse.'' (Je crois que tu es fatigué.) Nota : proposition infinitive.
# ''Nemini nimium credendum est.'' (Il ne faut trop se fier à personne.) Nota : adjectif verbal + datif.
''Exempla elocutionis''
* ''Tu, credo, numquam erras.'' (Toi, je suppose, tu ne te trompes jamais.)
*: ''Commentaire :'' credo en incise = ironie polie ; le verbe de la croyance retourné en arme du doute. L’incise est le lieu privilégié de l’ironie latine.
* ''Experto credite.'' (Croyez-en celui qui a l’expérience.) (Virgile)
*: ''Commentaire :'' datif + impératif pluriel : l’autorité de l’expérience en deux mots ; la formule a quitté l’épopée pour la sagesse commune.
== ita ==
(adv.) : ainsi, de cette manière
''Exempla gradatim digesta''
# ''Ita est.'' (C’est ainsi. / Oui.)
# ''Non ita multo post rediit.'' (Il revint peu de temps après.) Nota : non ita = « pas tellement ».
# ''Ita de me meritus est ut ei gratiam debeam.'' (Il a si bien agi envers moi que je lui dois de la reconnaissance.) Nota : ita... ut consécutif.
''Exempla elocutionis''
* ''Ita vivam, ut verum dico.'' (Aussi vrai que je vive, je dis la vérité.)
*: ''Commentaire :'' formule de serment corrélative : on gage sa vie sur sa parole ; la corrélation ita... ut mise au service du pathétique judiciaire.
* ''Non ita est.'' (Il n’en est pas ainsi.)
*: ''Commentaire :'' démenti sec du dialogue philosophique ; trois mots pour refuser, sans argument : la réfutation commence par un refus nu.
== tum, tunc ==
(adv.) : alors, à ce moment
''Exempla gradatim digesta''
# ''Tum puer eram.'' (J’étais alors enfant.)
# ''Tum demum intellexit se errare.'' (Alors seulement il comprit qu’il se trompait.)
# ''Cum omnes artes, tum praecipue philosophia colenda est.'' (Il faut cultiver tous les arts, et en particulier la philosophie.) Nota : cum... tum = « non seulement... mais surtout ».
''Exempla elocutionis''
* ''Tum clamor, tum fuga, tum caedes ubique erat.'' (Alors ce furent partout cris, fuite, carnage.)
*: ''Commentaire :'' anaphore de tum + asyndète des sujets ; l’accélération dramatique du récit de bataille, où la syntaxe court avec les fuyards.
* ''Quid tum?'' (Et après ?)
*: ''Commentaire :'' question-couperet de l’argumentation orale ; deux monosyllabes pour balayer une objection. La brièveté comme fin de non-recevoir.
== semper ==
(adv.) : toujours
''Exempla gradatim digesta''
# ''Mater me semper amat.'' (Ma mère m’aime toujours.)
# ''Semper avarus eget.'' (L’avare est toujours dans le besoin.) (Horace)
# ''Non semper erunt Saturnalia.'' (Ce ne sera pas toujours fête.) (Sénèque)
''Exempla elocutionis''
* ''Semper ego auditor tantum?'' (Serai-je donc toujours simple auditeur ?) (Juvénal)
*: ''Commentaire :'' ouverture de la première Satire ; semper en tête, exaspéré, et la question sans verbe : l’indignation fonde un genre littéraire.
* ''Ex Africa semper aliquid novi.'' (D’Afrique, toujours quelque chose de nouveau.) (d’après Pline l’Ancien)
*: ''Commentaire :'' proverbe grec latinisé ; génitif partitif novi, ellipse du verbe : la formule voyage d’autant mieux qu’elle est légère.
== sine ==
(praep. + abl.) : sans
''Exempla gradatim digesta''
# ''Sine te tristis sum.'' (Sans toi je suis triste.)
# ''Sine dubio verum dicis.'' (Sans aucun doute tu dis vrai.)
# ''Sine Cerere et Libero friget Venus.'' (Sans Cérès et Bacchus, Vénus a froid.) (Térence) Nota : métonymie : sans pain ni vin, point d’amour.
''Exempla elocutionis''
* ''Sine ira et studio.'' (Sans colère ni parti pris.) (Tacite)
*: ''Commentaire :'' programme d’impartialité de l’historien ; deux ablatifs abstraits coordonnés, aucun verbe : la méthode tient en quatre mots. Devise de toute objectivité, à citer dans un dictionnaire de philosophie.
* ''Sine me, liber, ibis in Urbem.'' (Sans moi, mon livre, tu iras à Rome.) (Ovide)
*: ''Commentaire :'' adresse au livre personnifié ; sine me en tête dit l’exil avant tout le reste. L’élégie commence par la séparation.
== virtus ==
''virtus, virtutis'' (subst. f.) : courage, vertu
''Exempla gradatim digesta''
# ''Virtus rara est.'' (La vertu est rare.)
# ''Virtute, non verbis, res geritur.'' (C’est par le courage, non par les paroles, que l’affaire se mène.)
# ''Virtus est medium vitiorum et utrimque reductum.'' (La vertu est le milieu entre les vices, à égale distance des deux.) (Horace)
''Exempla elocutionis''
* ''Virtus post nummos.'' (La vertu après l’argent.) (Horace)
*: ''Commentaire :'' l’ordre des mots est l’ordre des valeurs, ironiquement inversé ; trois mots cyniques que la satire place dans la bouche du siècle.
* ''Honor est praemium virtutis.'' (L’honneur est la récompense de la vertu.) (d’après Cicéron)
*: ''Commentaire :'' définition par genre et différence, style du traité ; aucune figure, et c’est précisément le style philosophique : la netteté comme ornement.
== amor ==
''amor, amoris'' (subst. m.) : amour
''Exempla gradatim digesta''
# ''Amor matris magnus est.'' (L’amour d’une mère est grand.)
# ''Amore urbis captus, manere constituit.'' (Saisi d’amour pour la ville, il décida de rester.)
# ''Omnia vincit Amor: et nos cedamus Amori.'' (L’Amour triomphe de tout : nous aussi, cédons à l’Amour.) (Virgile)
''Exempla elocutionis''
* ''Amor tussisque non celantur.'' (L’amour et la toux ne se cachent pas.) (proverbe ancien)
*: ''Commentaire :'' zeugme comique unissant la passion et le symptôme ; le rapprochement trivial fait la vérité du proverbe.
* ''Hei mihi, quod nullis amor est sanabilis herbis!'' (Malheur à moi : l’amour ne se guérit par aucune herbe !) (Ovide)
*: ''Commentaire :'' exclamation élégiaque hei mihi, et disjonction nullis... herbis qui enserre le vers ; le dieu de la médecine avoue son impuissance, pathétique savant.
== ratio ==
''ratio, rationis'' (subst. f.) : raison, calcul, méthode
''Exempla gradatim digesta''
# ''Rationem sequere!'' (Suis la raison !)
# ''Ratio belli gerendi mutata est.'' (La méthode de conduite de la guerre a changé.) Nota : génitif du gérondif avec complément.
# ''Cuiusque facti rationem reddere debemus.'' (Nous devons rendre compte de chacun de nos actes.) Nota : rationem reddere.
''Exempla elocutionis''
* ''Ratio et oratio homines inter se coniungunt.'' (La raison et la parole unissent les hommes entre eux.) (d’après Cicéron)
*: ''Commentaire :'' paronomase ratio / oratio, presque le même mot pour la pensée et la parole ; le latin suggère par le son ce que le grec disait d’un seul mot, logos. Trouvaille capitale pour un lexique philosophique.
* ''Lex est recta ratio.'' (La loi est la droite raison.) (d’après Cicéron)
*: ''Commentaire :'' recta ratio, calque du grec orthos logos ; définition stoïcienne frappée en trois mots. Le style des définitions est un style à part entière.
== natura ==
''natura, naturae'' (subst. f.) : nature
''Exempla gradatim digesta''
# ''Natura pulchra est.'' (La nature est belle.)
# ''Natura duce, errare non possumus.'' (Avec la nature pour guide, nous ne pouvons nous égarer.) Nota : ablatif absolu sans participe.
# ''Naturam expellas furca, tamen usque recurret.'' (Chasse la nature à coups de fourche, elle reviendra toujours au galop.) (Horace)
''Exempla elocutionis''
* ''Naturam si sequemur ducem, numquam aberrabimus.'' (Si nous suivons la nature comme guide, jamais nous ne nous égarerons.) (Cicéron)
*: ''Commentaire :'' ducem attribut de naturam, métaphore du guide de route ; la maxime stoïcienne avance comme on marche, et numquam clôt la phrase sur une assurance.
* ''Rerum natura creatrix.'' (La nature créatrice des choses.) (d’après Lucrèce)
*: ''Commentaire :'' épithète creatrix : la Nature quasi divinisée chez le poète matérialiste, paradoxe stylistique ; le groupe nominal sans verbe a la densité d’une invocation.
== scribo ==
''scribo, scribere, scripsi, scriptum'' (v.) : écrire
''Exempla gradatim digesta''
# ''Epistulam scribo.'' (J’écris une lettre.)
# ''Ad te saepe scribo, tu raro rescribis.'' (Je t’écris souvent, tu réponds rarement.)
# ''Scribendi recte sapere est et principium et fons.'' (Bien penser est le principe et la source du bien écrire.) (Horace)
''Exempla elocutionis''
* ''Saepe stilum vertas.'' (Retourne souvent ton stylet.) (Horace)
*: ''Commentaire :'' métonymie de l’outil : retourner le stylet, c’est effacer, donc corriger ; le conseil d’atelier le plus célèbre de la critique antique, en trois mots concrets.
* ''Scribe ad me quam saepissime!'' (Écris-moi le plus souvent possible !)
*: ''Commentaire :'' injonction épistolaire affectueuse ; quam + superlatif adverbial, l’hyperbole codifiée de l’amitié. Le style des lettres a ses constantes.
== lego ==
''lego, legere, legi, lectum'' (v.) : lire ; choisir
''Exempla gradatim digesta''
# ''Librum lego.'' (Je lis un livre.)
# ''Milites lecti fortissime pugnaverunt.'' (Les soldats d’élite combattirent avec la plus grande bravoure.) Nota : lecti = « choisis, d’élite ».
# ''Legendi semper occasio est, audiendi non semper.'' (L’occasion de lire se présente toujours, celle d’écouter pas toujours.) (Pline le Jeune)
''Exempla elocutionis''
* ''Aut prodest aut delectat qui legitur.'' (L’auteur qu’on lit est utile ou il plaît.) (d’après Horace)
*: ''Commentaire :'' l’alternative horatienne (instruire ou plaire) ramenée à la lecture ; deux verbes coordonnés, programme entier de la littérature.
* ''Lector, intende: laetaberis.'' (Lecteur, sois attentif : tu y trouveras du plaisir.) (Apulée)
*: ''Commentaire :'' adresse directe au lecteur au seuil du roman ; vocatif, impératif, futur de promesse : trois mots, trois actes de langage. Le pacte de lecture en une ligne.
{{#invoke:Sous-Page|sousPage|taille préfixe=0.7em|taille titre=115%|italique=non}}
{{AutoCat}}
ai2059gh86tikfer7zofnkmf3p56i7u
Dictionnaire de philosophie/Maurice Merleau-Ponty
0
83982
768833
2026-06-28T07:53:38Z
PandaMystique
119061
Page créée avec « {{DicoPhilo|Maurice Merleau-Ponty|lecture=oui}} '''Maurice Merleau-Ponty''', né à Rochefort-sur-Mer le 14 mars 1908 et mort à Paris le 3 mai 1961, est un philosophe français, l'une des figures majeures de la phénoménologie au {{s-|XX|e}}. Son œuvre reprend et élargit une même intuition : avant de penser le monde, nous l'habitons ; avant de juger, nous percevons ; et la perception n'est pas une connaissance affaiblie, mais le sol même sur lequel toute co... »
768833
wikitext
text/x-wiki
{{DicoPhilo|Maurice Merleau-Ponty|lecture=oui}}
'''Maurice Merleau-Ponty''', né à Rochefort-sur-Mer le 14 mars 1908 et mort à Paris le 3 mai 1961, est un philosophe français, l'une des figures majeures de la phénoménologie au {{s-|XX|e}}. Son œuvre reprend et élargit une même intuition : avant de penser le monde, nous l'habitons ; avant de juger, nous percevons ; et la perception n'est pas une connaissance affaiblie, mais le sol même sur lequel toute connaissance se lève. À rebours des philosophies qui font de la [[Dictionnaire de philosophie/Conscience|conscience]] un spectateur sans corps, Merleau-Ponty rappelle que nous n'avons accès au monde que par un corps qui voit, qui touche et qui se meut. De ce point de départ découle un programme : décrire l'expérience telle qu'elle se donne, avant les reconstructions de la science et les abstractions de la réflexion, et en tirer une autre idée de l'homme, du langage, de l'art, de l'histoire, et finalement de l'être.
Cette enquête s'est déployée en deux temps que la mort a laissés inégaux. Le premier, celui de la ''Phénoménologie de la perception'' (1945), met au centre le corps vécu et la perception. Le second, interrompu par une disparition brutale, cherchait une ontologie nouvelle, une pensée de la « chair » et de l'« entrelacs » dont ''L'Œil et l'Esprit'' (1961) et le manuscrit posthume du ''Visible et l'Invisible'' donnent l'esquisse. Entre les deux, une réflexion continue sur l'expression et le langage, sur autrui, sur la peinture, et sur le sens politique de l'histoire.
== Repères biographiques ==
Issu d'une famille catholique originaire de Rochefort-sur-Mer, Maurice Merleau-Ponty grandit surtout à Paris : après la mort de son père, capitaine d'artillerie coloniale et chevalier de la Légion d'honneur, en 1913<ref name="datepere">Bernard Jean Merleau-Ponty (1869-1913) meurt alors que son fils a cinq ans. On retiendra la date de 1913, de préférence aux indications tardives qui rattachent parfois ce décès aux années de guerre ; voir Ted Toadvine, « Maurice Merleau-Ponty », dans ''The Stanford Encyclopedia of Philosophy'', éd. E. N. Zalta.</ref>, sa famille s'y installe, et il fait ses études secondaires dans les lycées parisiens, à Janson-de-Sailly puis à Louis-le-Grand. Il entre ensuite à l'École normale supérieure de la rue d'Ulm, où il appartient à la même génération que Jean-Paul Sartre, Simone de Beauvoir et Jean Hyppolite. Reçu deuxième à l'agrégation de philosophie en 1930, il enseigne d'abord dans des lycées de province, puis revient à l'École normale comme agrégé-répétiteur.
Sa formation se fait à la croisée de plusieurs courants. La phénoménologie de Husserl lui parvient par la traduction et par les conférences parisiennes de 1929 ; il en lira plus tard les inédits aux Archives Husserl de Louvain, attentif surtout au dernier Husserl, celui du « monde de la vie » (''Lebenswelt''). La psychologie de la forme (''Gestaltpsychologie'') de l'école allemande lui fournit une matière clinique et expérimentale qu'il ne quittera plus. La pensée de Bergson, qu'il discute sans s'y ranger, lui transmet le souci de l'expérience concrète et de la durée. Dans les mêmes années, il suit les leçons d'Alexandre Kojève sur la ''Phénoménologie de l'esprit'' de Hegel (1933-1939) ; avec les travaux de Jean Hyppolite, elles lui font lire Hegel non comme le penseur d'un savoir achevé et clos sur lui-même, mais comme celui d'une existence aux prises avec l'histoire, et orientent sa réflexion sur le sens, la [[Dictionnaire de philosophie/Dialectique|dialectique]] et l'engagement. Il lit aussi [[Dictionnaire de philosophie/Karl Marx|Marx]], et la réflexion sur l'histoire occupe une part entière de son travail.
Ses deux thèses paraissent coup sur coup : ''La Structure du comportement'', rédigée à la fin des années 1930 et publiée en 1942, puis la ''Phénoménologie de la perception'' en 1945. La guerre l'a entre-temps mobilisé, puis engagé brièvement dans un groupe de résistance intellectuelle, « Socialisme et Liberté », aux côtés de Sartre. À la Libération, Sartre, Beauvoir, Merleau-Ponty et quelques proches fondent la revue ''Les Temps modernes'' (1945), dont Merleau-Ponty oriente, dans les premières années, la ligne politique : il rédige lui-même une bonne part des éditoriaux, signés des seules initiales de la revue, tout en refusant que son nom paraisse en couverture aux côtés de celui de Sartre.
L'enseignement le conduit de l'université de Lyon à la Sorbonne, où il occupe de 1949 à 1952 une chaire de psychologie de l'enfant et de pédagogie, avant son élection au Collège de France en 1952. À quarante-quatre ans, il compte parmi les plus jeunes titulaires d'une chaire de philosophie au Collège de France ; sa leçon inaugurale paraît sous le titre ''Éloge de la philosophie'' (1953). Les résumés annuels de ses cours, publiés après sa mort, donnent la mesure d'une pensée en mouvement, qui passe du langage à l'institution, puis à la Nature et à une « ontologie » nouvelle.
La période est aussi celle d'une rupture. Le désaccord politique avec Sartre, à propos de la guerre de Corée et de l'attitude à tenir envers l'Union soviétique, le conduit à quitter ''Les Temps modernes''. Cette séparation, d'abord personnelle, devient théorique dans ''Les Aventures de la dialectique'' (1955). Merleau-Ponty meurt subitement à Paris le 3 mai 1961, d'une crise cardiaque, à son bureau : la ''Dioptrique'' de [[Dictionnaire de philosophie/René Descartes|Descartes]], à laquelle il travaillait, était restée ouverte devant lui<ref name="sep">Ted Toadvine, « Maurice Merleau-Ponty », dans ''The Stanford Encyclopedia of Philosophy'', Edward N. Zalta (dir.).</ref>. Il laisse inachevés ''Le Visible et l'Invisible'' et ''La Prose du Monde'', que son ami Claude Lefort éditera.
== Le projet phénoménologique ==
Avant d'entrer dans le détail, il faut dire ce que Merleau-Ponty entend par phénoménologie, car le mot recouvre, chez Husserl déjà, des exigences qui semblent se contredire. L'avant-propos de la ''Phénoménologie de la perception'' les énumère sans chercher à les concilier trop vite. La phénoménologie est l'étude des essences, et pourtant elle replace ces essences dans l'[[Dictionnaire de philosophie/Existence|existence]], persuadée qu'on ne comprend l'homme et le monde qu'à partir de leur « [[Dictionnaire de philosophie/Facticité|facticité]] ». Elle est une philosophie transcendantale, qui suspend les certitudes de l'attitude naturelle pour les éclairer, et pourtant le monde est pour elle « déjà là », présent avant toute réflexion. Elle vise la rigueur d'une « science exacte », et pourtant elle se veut un compte rendu de l'espace, du temps et du monde « vécus ». Surtout, elle se règle sur une consigne première : « Il s'agit de décrire, et non pas d'expliquer ni d'analyser<ref name="phenoavp"/>. » Décrire, c'est renoncer à reconstruire l'expérience à partir de causes, qu'elles soient physiologiques, psychologiques ou sociales ; c'est retrouver, sous les savoirs constitués, le contact premier d'où ils tirent leur sens. « Tout l'univers de la science est construit sur le monde vécu<ref name="phenoavp"/> », et la tâche de la philosophie est de réveiller cette expérience que la science présuppose sans la thématiser.
=== « Revenir aux choses mêmes » ===
La phénoménologie, telle que Merleau-Ponty la reçoit de Husserl, n'est pas une doctrine de plus, mais une manière de regarder. Son mot d'ordre, « revenir aux choses mêmes », signifie d'abord un refus : celui de remplacer l'expérience par l'idée que la science s'en fait. « Revenir aux choses mêmes, écrit-il, c'est revenir à ce monde avant la connaissance dont la connaissance parle toujours<ref name="phenoavp">Maurice Merleau-Ponty, ''Phénoménologie de la perception'', Paris, Gallimard, « Bibliothèque des idées », 1945, Avant-propos.</ref>. » Le monde n'est pas pour nous un théorème à constituer, mais un milieu où nous sommes déjà engagés. Aussi la première phrase de la phénoménologie n'est-elle pas « je pense », mais « je perçois », ou mieux, « je suis au monde ».
De là une formule qui résume l'inflexion que Merleau-Ponty imprime à Husserl : « Le monde est non pas ce que je pense, mais ce que je vis<ref name="pp-vis">Maurice Merleau-Ponty, ''Phénoménologie de la perception'', Avant-propos, p. XII.</ref>. » Cette phrase ne nie pas la pensée ; elle la replace. Penser, c'est toujours penser quelque chose à partir d'une situation où je me trouve déjà pris, parmi des choses que je n'ai pas constituées et que je ne possède jamais entièrement. Le monde reste, dit-il, « inépuisable ». Le travail du philosophe consiste alors à réapprendre à voir ce que la familiarité nous cache, à décrire l'expérience perceptive comme on décrirait un paysage étranger.
=== Les limites de la réduction ===
Husserl avait nommé « réduction » (''époché'') le geste par lequel on suspend nos préjugés sur l'existence du monde pour décrire comment il apparaît à la conscience. Merleau-Ponty retient ce geste, mais il en tire une leçon que Husserl lui-même, croit-il, n'avait pas pleinement formulée. « Le plus grand enseignement de la réduction est l'impossibilité d'une réduction complète<ref name="pp-reduction">Maurice Merleau-Ponty, ''Phénoménologie de la perception'', Avant-propos, p. VIII.</ref>. » Nous ne pouvons jamais nous arracher tout à fait au monde pour le contempler de l'extérieur, car nous en sommes une partie. Le fil intentionnel qui nous relie aux choses peut être desserré, jamais coupé. La réduction, loin de nous installer dans une conscience pure et sans attaches, nous rend au contraire sensibles à l'épaisseur de notre attachement. En reconduisant la réflexion à son sol corporel, Merleau-Ponty ne quitte pas le projet transcendantal de Husserl : il le refonde. Théodore Geraets a retracé cette genèse, de ''La Structure du comportement'' à la ''Phénoménologie de la perception'', comme la marche vers une « nouvelle philosophie transcendantale » où le sujet constituant n'est plus une conscience pure, mais l'existence incarnée<ref name="geraets-g">Théodore F. Geraets, ''Vers une nouvelle philosophie transcendantale. La genèse de la philosophie de Maurice Merleau-Ponty jusqu'à la « Phénoménologie de la perception »'', préface d'Emmanuel Levinas, La Haye, Martinus Nijhoff, 1971.</ref>.
Cette finitude assumée donne à la phénoménologie de Merleau-Ponty son ton propre. Elle conjugue, dirait-on, la lucidité et le sens du mystère. Il l'écrira plus tard, en marge d'un autre travail : le philosophe « se reconnaît à ce qu'il a inséparablement le goût de l'évidence et le sens de l'ambiguïté<ref name="eloge">Maurice Merleau-Ponty, ''Éloge de la philosophie'', leçon inaugurale au Collège de France, Paris, Gallimard, 1953.</ref> ». L'évidence, parce qu'il faut décrire avec rigueur ce qui se montre ; l'ambiguïté, parce que ce qui se montre n'est jamais une donnée nette et close, mais un sens en train de se faire. Ce mot d'ambiguïté n'est pas chez lui un aveu d'imprécision : il désigne une structure de l'expérience, et la ''Phénoménologie de la perception'' en fait la signature de l'humain, persuadée que tout ce que nous vivons ou pensons comporte toujours plusieurs sens à la fois<ref name="landes">Donald A. Landes, ''The Merleau-Ponty Dictionary'', Londres, Bloomsbury Academic, 2013, entrée « Ambiguity ».</ref>.
== La critique de l'empirisme et de l'intellectualisme ==
Une part de l'argumentation de Merleau-Ponty procède par une double critique. Deux philosophies se partagent le terrain et, selon lui, manquent l'une et l'autre l'expérience perceptive. Il les nomme l'empirisme et l'intellectualisme.
L'empirisme traite la perception comme une mosaïque de sensations, chacune produite par une excitation locale de l'organe. Voir une couleur, ce serait recevoir un point coloré ; voir une chose, ce serait additionner des points. Mais l'expérience dément ce schéma. Notre rétine comporte des zones aveugles à certaines couleurs, et pourtant nous ne voyons aucune tache décolorée sur une surface unie : « dès le niveau de la simple vision des couleurs, ma perception ne se borne pas à enregistrer ce qui lui est prescrit par les excitations rétiniennes, mais les réorganise<ref name="cinema">Maurice Merleau-Ponty, « Le cinéma et la nouvelle psychologie », dans ''Sens et non-sens'', Paris, Nagel, 1948 (conférence de 1945).</ref> ». Nous ne percevons jamais des éléments juxtaposés, mais d'emblée des ensembles, des configurations. Nous groupons les étoiles en constellations alors qu'aucune ligne ne les relie dans le ciel.
L'intellectualisme commet l'erreur inverse. Constatant que la sensation pure n'existe pas, que toute perception est déjà organisée, il en conclut que c'est l'esprit qui organise, par un jugement caché. Percevoir serait juger. Mais cette solution paie trop cher la cohérence : elle dissout le monde perçu dans une activité de la pensée et ne rend plus compte de ce que la perception a de donné, de subi, d'antérieur à toute décision. Entre la chose qui s'impose et l'esprit qui constitue, l'expérience occupe un milieu que ni l'une ni l'autre des deux doctrines ne sait nommer.
=== De ''La Structure du comportement'' : la notion de forme ===
Le premier livre prépare cette critique en partant de la biologie et de la psychologie du comportement. Contre le réflexologisme, qui décompose la conduite vivante en chaînes de stimulus et de réponse, Merleau-Ponty mobilise la notion de « forme » (''Gestalt'') : un tout dont les parties n'ont de sens que par leur place dans l'ensemble, et qui ne se laisse pas reconstruire par sommation de ses éléments. Le comportement n'est ni une suite de mécanismes ni l'expression d'une conscience pure ; c'est une structure, une manière d'organiser un champ.
Il distingue trois ordres, ou trois degrés d'intégration : l'ordre physique, l'ordre vital et l'ordre humain. Chacun reprend le précédent en lui donnant un sens nouveau, sans s'y réduire ni s'en séparer. Cette idée d'une intégration qui dépasse sans abolir guide toute son œuvre. La forme, qui n'est ni chose ni idée, lui paraît « un ingrédient irréductible de l'être » : elle « remet en question l'alternative classique de l'existence comme chose et de l'existence comme conscience<ref name="koehler">Maurice Merleau-Ponty, ''Sens et non-sens'', Paris, Nagel, 1948.</ref> ». Tout le programme de la ''Phénoménologie de la perception'' est déjà là en germe : trouver, entre l'objet et le sujet, le terrain commun où ils communiquent. Cette articulation de la nature et de l'humanité, où l'ordre humain reprend le vital sans s'y réduire ni s'en détacher, forme ce qu'Étienne Bimbenet a nommé le « problème anthropologique » de Merleau-Ponty : penser l'homme comme un vivant qui se distingue de l'animal non par l'addition d'un esprit, mais par une manière neuve de reprendre ce qu'il ne cesse d'être<ref name="bimbenet">Étienne Bimbenet, ''Nature et humanité. Le problème anthropologique dans l'œuvre de Merleau-Ponty'', Paris, Vrin, 2004.</ref>.
=== Le champ phénoménal ===
[[Fichier:Two silhouette profile or a white vase.svg|vignette|Le vase de Rubin : selon qu'on prend le blanc ou le noir pour fond, on voit un vase ou deux visages. Une même donnée s'organise en figure et fond, jamais les deux à la fois.]]
À ces deux philosophies, Merleau-Ponty oppose donc le « champ phénoménal », c'est-à-dire le monde tel qu'il se présente à un sujet incarné, avant la distinction de l'objectif et du subjectif. Ce champ a sa structure propre : il s'organise en figures qui se détachent sur des fonds, il a des reliefs, des lointains, des horizons. La psychologie de la forme en a décrit la grammaire ; la phénoménologie en dégage la portée. Car ce champ n'est pas une apparence à dépasser vers la « vraie » réalité de la physique : il est le lieu où toute réalité, y compris celle de la science, prend d'abord sens pour nous.
== Le corps propre ==
Le pivot de la ''Phénoménologie de la perception'' est la réhabilitation du corps. Non le corps que décrivent l'anatomie et la physiologie, objet parmi les objets, mais le corps que je suis et par lequel j'ai un monde : ce que Merleau-Ponty appelle le « corps propre ».
=== Le membre fantôme et l'ambiguïté du corps ===
Pour faire apparaître ce corps vécu, il part d'un phénomène que ni la physiologie ni la psychologie classiques n'expliquent : le membre fantôme. Un amputé continue de sentir le bras qu'il a perdu, d'y localiser une douleur, de vouloir s'en servir. L'explication par les voies nerveuses échoue, car on peut faire disparaître le fantôme en sectionnant des connexions, et il réapparaît parfois sans cause organique. L'explication purement psychologique échoue aussi, car le fantôme tient à l'état du corps. La vérité est entre les deux : le membre fantôme manifeste un corps qui n'est ni pur mécanisme ni pure représentation, un corps habité par un projet, encore tourné vers un monde où ce bras avait sa fonction. L'amputé refuse sa mutilation comme on refuse une nouvelle située hors de l'horizon où il vivait. Le corps n'est pas dans le monde à la façon d'une chose ; il est notre point de vue sur le monde, et c'est pourquoi sa blessure est aussi une blessure de notre rapport au monde.
=== Schéma corporel, spatialité, motricité ===
Le corps propre a une unité d'un genre particulier. Je ne connais pas la position de mes membres comme je connais celle des objets, en les regardant : je la sais d'emblée, par ce que les psychologues nomment le « schéma corporel », une conscience globale et pratique de mon corps comme système d'actions possibles. Aussi Merleau-Ponty distingue-t-il deux spatialités. La « spatialité de position » est celle des objets, repérés par des coordonnées dans un espace homogène. La « spatialité de situation » est celle du corps, défini non par un lieu mais par une tâche : ma main est « vers » le travail qu'elle accomplit, mon corps « vers » le monde qu'il vise.
Les troubles neurologiques le confirment. Merleau-Ponty s'appuie longuement sur le cas Schneider, ce blessé de guerre étudié par Gelb et Goldstein, incapable d'exécuter sur commande un geste abstrait (pointer une partie de son corps les yeux fermés) tout en réussissant le geste concret correspondant (chasser un moustique au même endroit). La pathologie sépare ce que la vie réunit, et révèle ainsi une « intentionnalité motrice », une visée du corps antérieure à toute représentation. D'où une formule où s'inverse tout le vocabulaire de l'espace objectif : le corps « n'est pas dans l'espace, il habite l'espace<ref name="phenocorps">Maurice Merleau-Ponty, ''Phénoménologie de la perception'', première partie, « Le corps », chap. « La spatialité du corps propre et la motricité » et « La synthèse du corps propre ».</ref> ».
=== « Je suis mon corps » ===
De ces analyses naît la thèse qui a fait la fortune du livre. Mon corps n'est pas un instrument que ma conscience dirige de l'extérieur. « Je ne suis pas devant mon corps, je suis dans mon corps, ou plutôt je suis mon corps<ref name="pp-corps175">Maurice Merleau-Ponty, ''Phénoménologie de la perception'', première partie, « Le corps », p. 175.</ref>. » Cette identité n'est pas celle d'une chose avec elle-même, car le corps propre se sait, se sent, s'éprouve du dedans tout en étant visible du dehors. Il est, dans la langue de Merleau-Ponty, « ambigu » : à la fois sujet et objet, voyant et visible, sentant et senti.
C'est par lui que nous accédons aux choses. « Le corps est notre moyen général d'avoir un monde<ref name="pp-monde171">Maurice Merleau-Ponty, ''Phénoménologie de la perception'', première partie, « Le corps », p. 171.</ref>. » Apprendre à danser, à conduire, à se servir d'un instrument, c'est élargir ce corps, lui incorporer de nouveaux pouvoirs, comme l'aveugle dont la canne devient un prolongement sensible. L'habitude n'est ni un savoir intellectuel ni un automatisme : c'est une « connaissance dans les mains », une compréhension qui passe par le corps. Là où la tradition opposait l'âme et le corps comme deux substances, Merleau-Ponty décrit un sujet incarné, dont l'esprit n'est jamais sans chair et le corps jamais sans sens. Pour Stephen Priest, Merleau-Ponty n'apporte pas une réponse de plus au problème classique des rapports de l'âme et du corps : il le dissout, en montrant que le partage du mental et du physique est un produit second de la réflexion, qui présuppose l'unité antérieure de l'être incarné<ref name="priest">Stephen Priest, ''Merleau-Ponty'', Londres et New York, Routledge, « The Arguments of the Philosophers », 1998.</ref>.
=== Le corps comme être sexué ===
Le corps propre n'est pas seulement un corps qui perçoit et qui se meut : c'est aussi un corps qui désire. La ''Phénoménologie de la perception'' consacre à la sexualité un chapitre entier, où Merleau-Ponty refuse de la réduire soit à un mécanisme biologique, soit à une représentation que l'esprit se donnerait d'un objet. Le désir n'est pas une pensée qui viserait un terme : c'est un corps qui se porte vers un autre corps, un mode d'existence avant d'être un état de conscience. « La perception érotique n'est pas une cogitatio qui vise un cogitatum ; à travers un corps elle vise un autre corps, elle se fait dans le monde et non pas dans une conscience<ref name="pp-sexue">Maurice Merleau-Ponty, ''Phénoménologie de la perception'', première partie, « Le corps », chap. « Le corps comme être sexué », p. 183.</ref>. » La sexualité donne ainsi à voir, sur un cas privilégié, ce que vaut toute la description du corps vécu : un sens qui se cherche dans la chair, en deçà du partage du physique et du mental.
Loin d'être une région close de l'existence, la vie sexuelle en exprime l'orientation d'ensemble. Elle dit la manière dont un sujet s'ouvre au monde et aux autres, au point que Merleau-Ponty peut y lire une clé de l'existence entière : « Si l'histoire sexuelle d'un homme donne la clé de sa vie, c'est parce que dans la sexualité de l'homme se projette sa manière d'être à l'égard du monde, c'est-à-dire à l'égard du temps et à l'égard des autres hommes<ref name="pp-sexue2">Maurice Merleau-Ponty, ''Phénoménologie de la perception'', première partie, « Le corps », chap. « Le corps comme être sexué ».</ref>. » La psychanalyse de Freud reçoit ici une relecture : sa découverte ne tient pas à ce qu'elle ramènerait toute la vie au sexe, mais à ce qu'elle montre une signification existentielle là où l'on ne voyait qu'un fait organique. La sexualité, comme la perception, déborde l'alternative du corps-machine et de la conscience pure.
== La primauté de la perception ==
Tout le livre converge vers une thèse qu'il a lui-même appelée le « primat de la perception ». La perception n'est pas un mode inférieur de connaissance, dont la science viendrait corriger les illusions. Elle est l'acte par lequel un monde s'ouvre à nous, et sans lequel ni science ni pensée ne seraient possibles. « La perception n'est pas une science du monde, ce n'est pas même un acte, une prise de position délibérée, elle est le fond sur lequel tous les actes se détachent et elle est présupposée par eux<ref name="pp-perception">Maurice Merleau-Ponty, ''Phénoménologie de la perception'', Avant-propos, p. V.</ref>. »
Il faut peser chaque mot. Si la perception était une science, elle pourrait se tromper et être réfutée ; or elle est ce sur quoi toute [[Dictionnaire de philosophie/Vérité|vérité]] et toute erreur se mesurent. Si elle était un acte de la pensée, elle relèverait du jugement ; or elle est plus ancienne que le jugement, qui la suppose. La perception est notre ancrage, le « il y a » préalable à tout « je pense ». De là cette conséquence, qui congédie la figure cartésienne d'un esprit séparé : « Il n'y a pas d'homme intérieur, l'homme est au monde, c'est dans le monde qu'il se connaît<ref name="phenoavp"/>. »
Cette primauté ne fait pas du sujet le maître du sens. Le sens nous précède et nous porte autant que nous le portons. C'est ce que dit la phrase la plus citée du livre : « Parce que nous sommes au monde, nous sommes condamnés au sens<ref name="pp-sens">Maurice Merleau-Ponty, ''Phénoménologie de la perception'', Avant-propos, p. XV.</ref>. » Le mot « condamnés » est pesé. Nous ne pouvons pas ne pas trouver du sens, car notre simple présence corporelle au monde fait déjà lever des significations ; mais ce sens n'est jamais total ni achevé, et nous ne le tenons pas en notre pouvoir. Tel est l'équilibre que Merleau-Ponty cherche partout : ni un monde sans nous, ni un monde fait par nous, mais un monde où nous sommes engagés.
== La chose, l'espace et le monde naturel ==
La deuxième partie de la ''Phénoménologie de la perception'' quitte le corps propre pour le monde qu'il perçoit. Merleau-Ponty y montre que la chose, l'espace et le monde ne sont pas d'abord des objets de pensée, mais des structures de notre expérience, et qu'ils gardent toujours une part qui excède ce que nous en saisissons.
=== Les constances perceptives ===
La psychologie a décrit ce qu'elle nomme les « constances » : une assiette nous paraît ronde et blanche quels que soient l'angle sous lequel nous la voyons et l'éclairage qui la frappe, alors que son image rétinienne est tantôt elliptique, tantôt grisâtre. L'empirisme y voit le produit d'inférences inconscientes, l'intellectualisme l'œuvre d'un jugement correcteur. Pour Merleau-Ponty, ces constances ne se surajoutent pas à la perception : elles en sont la texture même. Nous ne percevons pas une couleur en soi, puis sa correction, mais d'emblée la couleur d'une chose sous un certain éclairage, comme nous entendons une voix à travers le grain d'un téléphone sans confondre l'une et l'autre. La constance dit que la perception vise la chose même, et non ses apparences : elle traverse les perspectives vers ce qu'elles donnent.
=== La chose perçue ===
De là une idée de la chose qui rompt avec l'objet de la science. Une chose perçue n'est pas une somme de qualités ni un faisceau de sensations : c'est une unité de style, une « manière d'exister » qui se confirme à travers ses aspects sans jamais se réduire à aucun. Tournons autour d'un dé : ce ne sont pas des projections planes qui se succèdent, mais des faces du même dé, et c'est lui que nous voyons, tantôt d'ici, tantôt de là. La chose se donne d'abord, dans une évidence, et c'est seulement par une suite de réductions que l'analyse en vient à parler de profils, d'aspects perspectifs, puis de sensations, qui sont des produits de la réflexion et non le matériau de la perception. Cette présence pleine a pour rançon une transcendance : la chose nous fait face, elle ne se laisse jamais épuiser, et les choses, à la différence des ustensiles que nous posons sur le monde, « sont enracinées dans un fond de nature inhumaine ». « Le réel se prête à une exploration infinie, il est inépuisable<ref name="phenochose">Maurice Merleau-Ponty, ''Phénoménologie de la perception'', deuxième partie, « Le monde perçu », chap. « La chose et le monde naturel », p. 375.</ref>. » Brisons une pierre : nous obtenons encore des morceaux de pierre. Dans le réel, observe Merleau-Ponty, le sens pénètre la matière, au lieu de flotter à sa surface comme dans nos fictions.
À cette objectivité de la chose répond, du côté du corps, une tendance à en prendre la juste mesure. Pour chaque objet, note Merleau-Ponty, « comme pour chaque tableau dans une galerie de peinture, il y a une distance optimale d'où il demande à être vu<ref name="pp-grip">Maurice Merleau-Ponty, ''Phénoménologie de la perception'', deuxième partie, « Le monde perçu », chap. « La chose et le monde naturel », p. 349.</ref> » : trop près, le détail se brouille ; trop loin, la forme se perd ; entre les deux, le regard cherche, comme on règle un microscope, le point où la chose se livre le mieux. La perception ne reçoit donc pas passivement le monde, elle s'y ajuste et tend vers un équilibre, vers ce que des lecteurs récents, à la suite de Hubert Dreyfus, ont nommé une « prise maximale » sur les choses, et dont Komarine Romdenh-Romluc a fait l'un des axes de sa lecture de la ''Phénoménologie de la perception''<ref name="krr">Komarine Romdenh-Romluc, ''Routledge Philosophy GuideBook to Merleau-Ponty and Phenomenology of Perception'', Londres et New York, Routledge, 2011.</ref>.
=== La profondeur et le monde ===
L'espace lui-même, Merleau-Ponty le pense à partir du corps. La profondeur, que la tradition tenait pour une largeur vue de côté, donc pour une dimension dérivée, est au contraire la première de toutes : elle exprime que les choses se cachent les unes les autres, qu'il y a un ici et un là-bas, qu'un monde s'ouvre devant un corps situé. Elle n'est pas une relation entre objets, mais une relation de moi aux choses. Quant au monde, il n'est pas la somme des choses ni le résultat d'une synthèse achevée de l'entendement : il est l'horizon permanent de toutes nos perceptions, le fond sur lequel chaque chose se détache, et il reste ouvert, inachevé, toujours en deçà d'une saisie complète. Le monde n'est pas ce que je pense, répète Merleau-Ponty en variant la formule, mais ce en quoi je suis pris ; et la perception ne le constitue pas, elle l'habite.
== Le temps et la liberté ==
La troisième partie du livre, consacrée à « l'être-pour-soi et l'être-au-monde », reprend les analyses précédentes pour en tirer une idée du sujet. Loin d'un esprit intemporel, le sujet s'y révèle temporel de part en part, et sa liberté apparaît située.
=== La temporalité ===
Le temps n'est pas pour Merleau-Ponty une réalité que nous observerions du dehors, à la façon d'une rivière dont nous suivrions le cours. C'est nous qui sommes temporels, et le temps n'existe que pour un être qui, présent à ce qu'il vit, retient ce qui vient de passer et anticipe ce qui s'annonce. Reprenant les analyses de Husserl sur les « rétentions » et les « protentions », il décrit un présent qui n'est jamais ponctuel : chaque instant garde une frange du passé immédiat et tend déjà vers l'avenir proche. « Le temps n'est pas une ligne, mais un réseau d'intentionnalités<ref name="phenotemps">Maurice Merleau-Ponty, ''Phénoménologie de la perception'', troisième partie, « L'être-pour-soi et l'être-au-monde », chap. « La temporalité », p. 477.</ref>. » Le passé, le présent et l'avenir ne se succèdent pas comme des points sur une file ; ils tiennent ensemble dans un même mouvement, ce que Heidegger nommait une « ek-stase », une sortie hors de soi. De là une thèse qui scelle l'analyse : le temps n'est pas un cadre où le sujet serait logé, il est l'étoffe même de la subjectivité. « Il faut comprendre le temps comme sujet et le sujet comme temps<ref name="pp-temps-sujet">Maurice Merleau-Ponty, ''Phénoménologie de la perception'', troisième partie, « L'être-pour-soi et l'être-au-monde », chap. « La temporalité », p. 483.</ref>. » Nous ne sommes pas dans le temps comme l'eau dans un vase ; nous sommes le surgissement du temps, et c'est pourquoi nous échappons à toute définition close.
=== La liberté en situation ===
La même analyse vaut pour la liberté, dont Merleau-Ponty discute la conception qu'en donnait alors Sartre, sans toujours le nommer. Si la liberté est entière, si elle est égale dans toutes nos conduites et jusque dans nos passions, alors, paradoxalement, elle se détruit. Car une liberté présente partout n'est repérable nulle part : « l'esclave témoigne autant de liberté en vivant dans la crainte qu'en brisant ses fers », et l'on ne peut plus dire où une action libre commence. Une liberté sans attaches ne s'engage en rien, puisque l'instant suivant la retrouvera intacte ; elle devient ce don étrange qui consiste à n'avoir aucun don. À cette liberté vertigineuse et vide, Merleau-Ponty oppose une liberté qui prend appui sur une situation. Un rocher n'est un obstacle que pour qui projette de l'escalader ; ce n'est donc pas la liberté qui supprime les obstacles, mais elle qui les fait paraître comme tels, en se donnant des buts. Il y a ainsi un échange perpétuel entre ce que nous sommes déjà, notre corps, notre histoire, notre monde, et ce que nous en faisons. La liberté « ne serait pas liberté sans les racines qu'elle pousse dans le monde<ref name="phenoliberte">Maurice Merleau-Ponty, ''Phénoménologie de la perception'', troisième partie, « L'être-pour-soi et l'être-au-monde », chap. « La liberté ».</ref> » : nos choix ne restreignent pas notre liberté, ils l'accomplissent, car ce sont eux qui la délivrent de l'indétermination. La liberté n'est donc ni un absolu sans monde ni un effet du monde sur nous, mais notre manière de prendre, à chaque instant, ce qui nous est donné. C'est sur ces relations vécues, et non sur une conscience souveraine, que se referme le livre, là où Merleau-Ponty fait sienne une parole de Saint-Exupéry : « L'homme n'est qu'un nœud de relations<ref name="phenoliberte"/>. »
== L'expression et le langage ==
Si la perception nous donne déjà un monde, comment le pensons-nous, le disons-nous, le partageons-nous ? La question de l'expression occupe Merleau-Ponty de la ''Phénoménologie'' jusqu'à ''La Prose du Monde'' et ''Signes''. Elle prolonge l'analyse du corps, car la parole est pour lui un geste, et le sens d'un mot adhère à ce mot comme le sens d'un sourire adhère au visage.
=== Parole parlante et parole parlée ===
Le langage n'est pas l'habillage d'une pensée déjà faite. « La pensée n'est rien d'intérieur, elle n'existe pas hors du monde et hors des mots » : nous ne pensons pas d'abord, pour traduire ensuite ; nous pensons en parlant, et le mot accomplit la pensée plutôt qu'il ne la double. Pour saisir cette genèse, Merleau-Ponty distingue deux usages du langage qu'il nomme la « parole parlante » et la « parole parlée<ref name="phenoparole">Maurice Merleau-Ponty, ''Phénoménologie de la perception'', première partie, « Le corps », chap. « Le corps comme expression et la parole », p. 229.</ref> ». La parole parlée est le trésor des significations déjà acquises, la langue disponible, celle que nous employons sans invention ; elle court de soi, comme une monnaie reçue. La parole parlante est l'acte créateur par lequel une signification neuve se fraye un chemin et fait dire à la langue ce qu'elle ne disait pas encore. L'enfant qui apprend à parler, l'écrivain qui cherche sa phrase, le philosophe qui forge un concept se tiennent du côté de la parole parlante. Toute parole parlée fut d'abord parlante, et toute parole parlante puise dans un fonds déjà parlé.
=== Le signe diacritique (Saussure) ===
Dans les essais des années 1950, Merleau-Ponty relit Saussure et y trouve la confirmation de ses analyses. Le sens, dans une langue, ne tient pas à la coïncidence d'un mot et d'une chose, mais aux écarts entre les signes. Aucun mot ne signifie par lui-même ; il signifie par ses différences avec les autres. La langue est un système où, selon la formule qu'il retient, il n'y a que des différences sans termes positifs. Cette idée du signe « diacritique », qui ne vaut que par opposition, éclaire l'expression en général : signifier, ce n'est jamais désigner une chose une fois pour toutes, c'est creuser un écart dans un champ, faire apparaître une figure sur un fond. Il en va du sens comme de la perception : il naît d'une organisation, non d'une collection.
=== La prose du monde ===
''La Prose du Monde'', laissée inachevée, devait être une théorie de l'expression et de la littérature. Merleau-Ponty y oppose le langage vivant à l'« algorithme », ce langage exact des sciences qui rêve de signes transparents et de significations sans reste. Le langage des hommes n'est pas tel : il est opaque, chargé d'histoire, et c'est par cette opacité même qu'il dit le monde. La différence entre le « langage parlé » et le « langage parlant<ref name="prose">Maurice Merleau-Ponty, ''La Prose du Monde'', Paris, Gallimard, 1969 (texte posthume édité par Claude Lefort), p. 17.</ref> » y reprend, au plan de l'écriture, la distinction de la ''Phénoménologie''. L'écrivain, comme le peintre, hérite d'une langue et la refait ; il l'emploie pour dire ce qu'elle ne contenait pas, et il « détruit la langue commune, mais en la réalisant<ref name="langageindirect">Maurice Merleau-Ponty, « Le langage indirect et les voix du silence », dans ''Signes'', Paris, Gallimard, 1960.</ref> ».
== L'art et la peinture ==
[[Fichier:1904, Cézanne, Mont Sainte-Victoire.jpg|vignette|redresse|Paul Cézanne, ''La Montagne Sainte-Victoire'' (vers 1904). Dans « Le doute de Cézanne », Merleau-Ponty fait du peintre celui qui cherche comment les choses viennent à la vision.]]
La peinture tient dans l'œuvre de Merleau-Ponty une place qui n'est pas d'ornement. Le peintre lui paraît mener, sans concepts, l'enquête même du philosophe : il interroge le visible, il cherche comment les choses viennent à la vision. De ''Sens et non-sens'' (1948) à ''L'Œil et l'Esprit'' (1961), Cézanne en est la figure exemplaire.
=== Le doute de Cézanne ===
L'essai « Le doute de Cézanne » part d'une énigme biographique : pourquoi tant d'efforts, tant d'échecs, tant d'incertitude chez ce peintre qui doutait jusqu'à se demander si son art ne tenait pas à un trouble de ses yeux ? Merleau-Ponty refuse de réduire l'œuvre à la maladie. Le doute de Cézanne n'est pas une faiblesse nerveuse, c'est la difficulté propre à ce qu'il cherchait. Car Cézanne veut tenir ensemble ce que l'école oppose : la sensation et la composition, la nature et l'art, le voir et le penser. « Il s'agit des deux », répond-il quand on lui demande de choisir entre la nature et la pensée. Il ne peint pas le résultat de la perception, l'objet net que la science reconstitue, mais « la matière en train de se donner forme », l'ordre naissant des choses sous le regard.
De là ses fameuses « déformations » : assiettes dont l'ellipse se dilate, table qui s'étale contre les lois de la perspective. Loin d'être des fautes, elles restituent la perspective vécue, celle de notre perception, qui n'est pas la perspective géométrique de l'appareil photographique. Cézanne suit dans une modulation de couleurs le renflement des choses et trace plusieurs contours, parce que le contour réel n'est pas une ligne mais la limite vers laquelle les côtés de la chose fuient en profondeur. Sa peinture « met en suspens » nos habitudes et fait voir « le fond de nature inhumaine sur lequel l'homme s'installe<ref name="cezanne">Maurice Merleau-Ponty, « Le doute de Cézanne », dans ''Sens et non-sens'', Paris, Nagel, 1948 (publié pour la première fois dans la revue ''Fontaine'', 1945).</ref> ». Et Merleau-Ponty fait sienne la confidence du peintre, qui dit l'enjeu même de toute expression : « Le paysage se pense en moi et je suis sa conscience<ref name="cezanne"/>. » La difficulté de Cézanne, conclut-il, est celle « de la première parole » : exprimer, c'est faire venir au jour un sens qui n'était écrit nulle part, ni dans les choses, ni dans l'esprit, avant l'œuvre qui le profère. Ronald Bonan lit cet essai comme une mise en œuvre de ce que Merleau-Ponty nomme la liberté en situation : le peintre n'invente pas son monde de toutes pièces et ne le subit pas davantage ; il reprend une donnée, un héritage, un corps, et les fait signifier dans un geste qui ne se sépare jamais de ses conditions<ref name="bonan-esth">Ronald Bonan, ''Premières leçons sur l'Esthétique de Merleau-Ponty'', Paris, Presses universitaires de France, 1997.</ref>.
=== ''L'Œil et l'Esprit'' : la vision et la chair ===
''L'Œil et l'Esprit'', le dernier texte achevé par Merleau-Ponty, condense sa pensée tardive en partant encore de la peinture. Sa thèse de départ est que la vision suppose le corps. « Le peintre apporte son corps, dit Valéry. Et, en effet, on ne voit pas comment un Esprit pourrait peindre. C'est en prêtant son corps au monde que le peintre change le monde en peinture<ref name="oeilesprit">Maurice Merleau-Ponty, ''L'Œil et l'Esprit'', Paris, Gallimard, 1964 (paru d'abord dans la revue ''Art de France'', 1961).</ref>. » Un pur esprit ne verrait rien, car voir, c'est être soi-même visible, situé parmi les choses que l'on voit.
C'est ici qu'apparaît la notion qui domine la dernière philosophie de Merleau-Ponty : celle de « réversibilité ». Mon corps voit, et il peut être vu ; il touche, et il peut être touché. Quand ma main droite touche ma main gauche en train de toucher, le touchant devient touché, et les deux rôles s'échangent sans jamais coïncider tout à fait. « Il se voit voyant, il se touche touchant, il est visible et sensible pour soi-même<ref name="oeilesprit"/>. » Cette structure n'est pas une curiosité ; elle est, dit Merleau-Ponty, la « définition de notre chair ». Le sujet n'est plus une conscience qui survole le monde, mais un être pris dans le visible, « du milieu » des choses, là où « un visible se met à voir<ref name="oeilesprit"/> ».
L'expérience de la peinture donne corps à cette idée. Tant de peintres ont dit que les choses les regardaient ; André Marchand, après Klee, confie qu'en forêt il a senti, certains jours, que c'étaient les arbres qui le regardaient. Merleau-Ponty prend la formule au sérieux : entre le voyant et le visible, les rôles s'inversent, et l'inspiration est « inspiration et expiration de l'Être<ref name="oeilesprit"/> », respiration où l'on ne sait plus « qui voit et qui est vu, qui peint et qui est peint ». La peinture, sous toutes ses formes, depuis Lascaux, « ne célèbre jamais d'autre énigme que celle de la visibilité<ref name="oeilesprit"/> ».
== Autrui et l'intersubjectivité ==
Le problème d'autrui hante toute philosophie de la conscience : si je ne connais que mes propres pensées, comment puis-je atteindre une autre conscience que la mienne ? La réponse classique passe par l'analogie : je verrais le corps d'autrui, ses gestes, et j'en inférerais, par comparaison avec moi-même, qu'une conscience l'anime. Merleau-Ponty récuse ce détour.
Sa solution suit de sa théorie du corps. Puisque l'émotion n'est pas un fait psychique caché derrière le visage, mais une manière d'être au monde qui se lit dans le visage et les gestes, autrui m'est donné directement, comme conduite. « Colère, honte, haine, amour ne sont pas des faits psychiques cachés au plus profond de la conscience d'autrui, ce sont des types de comportement ou des styles de conduite visibles du dehors. Ils sont sur ce visage ou dans ces gestes et non pas cachés derrière eux<ref name="cinema"/>. » L'enfant comprend la colère ou la joie sur le visage d'autrui bien avant de pouvoir l'analyser, et même avant de pouvoir la mimer. Le corps perçu d'autrui et mon propre corps appartiennent au même monde sensible ; ils se reconnaissent l'un l'autre comme deux variantes d'une même manière d'habiter l'espace. Autrui n'est donc pas une conscience que je devine, mais une présence que je perçois, dans le même tissu charnel où je me tiens moi-même.
Cette présence d'autrui, donnée dans la perception et non conclue par un raisonnement, Merleau-Ponty la nomme « intercorporéité ». Le mot apparaît dans « Le philosophe et son ombre », où il médite la phénoménologie de Husserl : du moment que mon corps n'est pas une pure conscience, mais une « chose qui perçoit », sensible et visible parmi les choses, je suis déjà préparé à reconnaître d'autres corps qui perçoivent, et donc d'autres hommes<ref name="ombre"/>. La poignée de main en offre l'image. Lorsque je serre la main d'un autre, je touche et je suis touché d'un même geste, comme lorsque ma main droite saisit ma main gauche ; mais ici les deux mains n'appartiennent pas au même corps. Entre les vivants s'ouvre alors une réversibilité semblable à celle qui m'unit à moi-même, un échange où les rôles de sentant et de senti passent de l'un à l'autre sans jamais se confondre. Autrui et moi ne sommes pas deux consciences séparées qui se feraient signe à travers leurs corps : nous sommes deux foyers d'un même tissu sensible, deux manières, pour la chair du monde, de se sentir elle-même. Scott Marratto a fait de cette intercorporéité le centre d'une lecture où le soi n'est jamais une intériorité close, mais d'emblée tourné vers les autres dans l'élément commun du sensible<ref name="marratto">Scott L. Marratto, ''The Intercorporeal Self. Merleau-Ponty on Subjectivity'', Albany, State University of New York Press, 2012.</ref>.
== L'institution ==
Entre la description de la perception et l'analyse de l'histoire, un concept fait le lien dans les cours du Collège de France : celui d'« institution ». Merleau-Ponty l'emprunte à Husserl, qui parlait de ''Stiftung'', mot que l'on traduit par institution ou fondation. Il y cherche « un remède aux difficultés de la philosophie de la conscience<ref name="institution">Maurice Merleau-Ponty, « L'institution dans l'histoire personnelle et publique », dans ''Résumés de cours. Collège de France, 1952-1960'', Paris, Gallimard, 1968.</ref> ». Le problème est celui-ci : une conscience qui ne ferait que constituer ses objets resterait seule, sans prise sur un passé qui la dépasse ni sur d'autres consciences ; rien, en elle, ne pourrait la relancer. L'institution nomme au contraire ce par quoi une expérience reçoit des dimensions durables, ouvre un avenir, et se continue au-delà de l'instant qui l'a vue naître.
Par institution, Merleau-Ponty entend donc ces événements qui déposent en nous un sens, non comme un souvenir inerte, mais comme « appel à une suite, exigence d'un avenir<ref name="institution"/> ». Une rencontre, une œuvre, une langue, une coutume s'instituent quand elles cessent d'être un fait isolé pour devenir le sol d'autres expériences à venir. Le modèle n'est plus celui de l'objet posé devant un sujet, mais celui d'un sens qui se reprend et se transforme dans le temps. C'est pourquoi l'institution se laisse retrouver partout : dans la vie personnelle, où l'enfance oriente sans déterminer ; dans l'amour, dont Proust montre qu'il mêle le passé et l'avenir ; dans la peinture, où chaque œuvre annonce les suivantes et fait qu'un style se cherche sans se déduire ; dans le savoir lui-même, qui avance par reprises plutôt que par addition de vérités closes.
Cette notion donne à l'histoire un autre statut. Elle n'est ni un enchaînement mécanique ni le déploiement d'une raison qui connaîtrait d'avance sa fin, mais un champ ouvert où des sens s'instituent, se sédimentent et se réveillent. On comprend alors pourquoi la phénoménologie de Merleau-Ponty débouche, dans ces années, sur ce qu'il nomme une métaphysique de l'histoire : décrire l'institution, c'est tenir ensemble la perception, l'expression, la vie commune et le temps, sans les réduire l'un à l'autre. Le lien se noue ainsi entre l'analyse du corps percevant et la réflexion politique, car ce qui vaut du sens perçu vaut aussi du sens historique, toujours offert et jamais achevé. Ronald Bonan voit dans cette notion d'institution le fil qui relie les recherches de la dernière période : l'institution de l'œuvre d'art, celle de l'intermonde où les sujets se rencontrent, celle de la dimension sensible elle-même se laissent décrire comme autant de figures de la chair, ce milieu commun où le voyant et le visible s'appartiennent<ref name="bonan-bl">Ronald Bonan, ''Merleau-Ponty'', Paris, Les Belles Lettres, « Figures du savoir », 2011.</ref>.
== L'histoire et la politique ==
Merleau-Ponty n'a pas séparé la philosophie de l'histoire vécue de son temps. La guerre, l'Occupation, la division du monde après 1945 l'ont conduit à réfléchir longuement sur la violence, sur le marxisme et sur la responsabilité politique.
=== ''Humanisme et Terreur'' ===
''Humanisme et Terreur'' (1947) prend pour occasion les procès de Moscou et le roman d'Arthur Koestler ''Le Zéro et l'infini''. Le livre déconcerte parce qu'il refuse les deux conforts symétriques : la dénonciation libérale de la terreur, et son apologie communiste. Sa thèse est qu'il n'existe pas d'histoire innocente. Le libéralisme se croit pur parce qu'il a inscrit la violence dans la loi et l'a rendue invisible ; mais « tous les régimes sont criminels<ref name="ht">Maurice Merleau-Ponty, ''Humanisme et Terreur. Essai sur le problème communiste'', Paris, Gallimard, 1947.</ref> », et dans les moments où le sol d'une société s'effondre, « la liberté de chacun menace de mort celle des autres et la violence reparaît<ref name="ht"/> ».
La vraie question, pour Merleau-Ponty, n'est donc pas de savoir si une politique use de violence, puisque toute politique en use, mais de savoir si cette violence ouvre ou non sur une humanité réconciliée, sur une reconnaissance possible entre les hommes. C'est à cette aune qu'il examine, sans la trancher, l'expérience soviétique de 1947, en suspendant son jugement plutôt qu'en l'absolvant. On a parfois lu le livre comme une justification de la terreur ; il est plus exact d'y voir une mise en garde contre la bonne conscience, celle qui condamne la violence d'autrui en ignorant la sienne. Reste que l'ouvrage demeure l'un des plus contestés de Merleau-Ponty. On lui a reproché, non sans motif, de mésestimer l'ampleur de la terreur stalinienne, et de mettre une analyse philosophique subtile au service d'une réalité, celle des procès et des purges, dont la brutalité débordait ses catégories<ref name="carman">Taylor Carman, ''Merleau-Ponty'', Londres et New York, Routledge, 2008.</ref>. Merleau-Ponty lui-même finira par s'éloigner de cette position : dans ''Les Aventures de la dialectique'' (1955), il rompt avec l'idée d'une histoire dont on connaîtrait d'avance le sens, et plus largement avec toute philosophie qui ferait de l'avenir une certitude.
=== La rupture avec Sartre ===
Le tournant des années 1950 défait l'amitié philosophique avec Sartre. Là où Sartre, à l'épreuve de la guerre froide, se rapproche du Parti communiste et fait de l'engagement un choix sans réserve, Merleau-Ponty se défie d'une politique qui déduirait l'action d'une certitude sur l'Histoire. ''Les Aventures de la dialectique'' (1955) contient un chapitre critique, « Sartre et l'ultra-bolchévisme », où il reproche à son ancien ami de substituer à la patience du réel la pureté d'une décision. À l'« action » comme pari volontaire, Merleau-Ponty oppose une dialectique sans garantie, attentive aux résistances de l'événement, soucieuse de ne jamais sacrifier le présent à un avenir supposé connu. Le désaccord n'est pas seulement tactique : il oppose deux idées de la liberté et deux rapports au temps.
== La pensée tardive : l'ontologie de la chair ==
Dans les dernières années, Merleau-Ponty entreprend de refonder sa philosophie sur une base nouvelle. Il juge que la ''Phénoménologie de la perception'' était restée prisonnière d'un vocabulaire qu'elle voulait pourtant dépasser, celui de la « conscience » et de l'« objet ». Il cherche désormais une ontologie, c'est-à-dire une pensée de l'être, qui parte non du sujet face au monde, mais d'un élément plus ancien où l'un et l'autre se découpent.
=== Le visible et l'invisible ===
''Le Visible et l'Invisible'', manuscrit interrompu par la mort et publié en 1964 par les soins de Claude Lefort, en porte le projet. Le titre dit déjà la thèse : le visible ne se suffit pas, il est doublé d'un invisible qui n'est pas un autre monde caché derrière lui, mais sa profondeur, son envers, ce qui le tient et le rend possible. De même que le sens d'une phrase n'est pas une autre phrase derrière les mots, mais ce qui les anime, l'invisible est l'articulation du visible, l'écart qui le creuse et le fait paraître. La pensée que Merleau-Ponty appelle de ses vœux n'est ni une intuition qui coïnciderait avec l'être, ni une dialectique qui le réduirait à une suite de positions ; c'est une « interrogation », un questionnement qui se tient à la mesure de ce qui ne se laisse jamais entièrement saisir.
Le point de départ de cette ontologie est ce que Merleau-Ponty appelle la « foi perceptive » : la certitude spontanée, antérieure à toute philosophie, que nous percevons les choses mêmes et qu'il y a un monde. Cette foi n'est ni une croyance que l'on pourrait justifier ni une illusion que l'on pourrait dissiper ; elle est notre adhésion première à l'être, et la philosophie n'a pas à la fonder, mais à l'élucider sans la rompre. Or les philosophies de la réflexion, en faisant du monde le corrélat d'une conscience, et les philosophies dialectiques, en le résorbant dans le mouvement de la négation, manquent également cette adhésion : elles la remplacent par une possession. Contre elles, Merleau-Ponty appelle de ses vœux une pensée qui ne prétende ni coïncider avec l'être ni le survoler, une « interrogation » qui se tienne dans l'écart, et qu'il nomme aussi « hyperdialectique » pour la distinguer d'une dialectique qui s'achèverait en synthèse. C'est dans cet esprit qu'il porte sur son propre passé un regard critique. Les analyses de la ''Phénoménologie de la perception'', juge-t-il, étaient restées dépendantes des termes mêmes qu'elles voulaient dépasser, ceux de « conscience » et d'« objet » ; pour penser ce qui se tient avant ce partage, il fallait un autre langage, celui de la chair, du visible et de l'invisible.
Faut-il voir dans cette ontologie de l'inachevé un renoncement à la vérité ? Ce serait mal lire. Merleau-Ponty n'abandonne pas la vérité, il en déplace l'origine. La philosophie classique la cherchait achevée, identique à elle-même, transparente à un regard sans corps ; lui fait remonter le sens même du mot « vrai » à l'expérience qui nous met en présence du réel, à cette « foi perceptive » d'où toute proposition tire sa force avant qu'aucune théorie ne la garantisse. L'ambiguïté de la chair n'est pas un défaut de l'être, mais son inachèvement positif, la marque d'un réel toujours offert et jamais clos. C'est de ce point de vue que Frank Chouraqui a rapproché Merleau-Ponty de Nietzsche : l'un et l'autre congédient la vérité-en-soi sans verser dans le [[Dictionnaire de philosophie/Scepticisme|scepticisme]], parce qu'ils enracinent le vrai dans l'épreuve du monde plutôt que dans la coïncidence d'une pensée et de son objet<ref name="chouraqui">Frank Chouraqui, ''Ambiguity and the Absolute. Nietzsche and Merleau-Ponty on the Question of Truth'', New York, Fordham University Press, 2014.</ref>.
Ce que cette pensée refuse, c'est l'idéal d'une transparence : l'idée que le sensible pourrait être rendu tout entier clair à un regard ou à un concept. Le visible garde une épaisseur, une part d'ombre qui le rend visible sans jamais se résoudre en pure clarté. Emmanuel Alloa a fait de cette « résistance du sensible » le fil d'une relecture de Merleau-Ponty comme critique de la transparence<ref name="alloa-r">Emmanuel Alloa, ''La Résistance du sensible. Merleau-Ponty critique de la transparence'', Paris, Kimé, 2008.</ref>.
=== Chiasme et réversibilité ===
Le concept central de cette ontologie est celui de « chair » (''chair''). Le mot ne désigne ni le corps biologique ni la matière au sens des physiciens. La chair est l'étoffe commune dont sont faits et mon corps et les choses, le « tissu » où le sentant et le senti tiennent ensemble. ''L'Œil et l'Esprit'' en avait donné la formule : « le monde est fait de l'étoffe même du corps<ref name="oeilesprit"/> ». Mon corps n'est pas séparé des choses qu'il voit, il en est, il est « pris dans le tissu du monde » ; et c'est pourquoi il peut les voir, comme on ne voit que ce dont on partage la nature.
L'unité de cette étoffe se dit par deux mots jumeaux. Le « chiasme », ou « entrelacs », nomme le croisement où le voyant et le visible, le touchant et le touché, se recoupent : ni fusion, ni séparation, mais empiétement. La « réversibilité » nomme la possibilité, toujours imminente et jamais achevée, que les rôles s'échangent. Quand ma main droite touche ma main gauche qui touche un objet, je suis tour à tour celui qui sent et celui qui est senti, sans que les deux moments se rejoignent en un point. Cet écart minime, cette « non-coïncidence », est pour Merleau-Ponty la marque de notre rapport à l'être : nous lui appartenons d'assez près pour le sentir, d'assez loin pour le percevoir. La conscience n'est plus posée en face du monde ; elle est un pli du monde sur lui-même, le lieu où le visible « se met à voir ».
Cette ontologie ne sépare pas le réel de l'[[Dictionnaire de philosophie/Imagination|imaginaire]]. Le visible se double d'un invisible, mais aussi d'un imaginaire qui n'en est pas le contraire : le rêve, l'image, le mythe tiennent à la même étoffe que la perception, dont ils sont l'envers. Annabelle Dufourcq a lu en ce sens l'ontologie de la chair comme une « ontologie de l'imaginaire », où l'imaginaire devient un principe de l'Être au même titre que le perçu<ref name="dufourcq">Annabelle Dufourcq, ''Merleau-Ponty : une ontologie de l'imaginaire'', Dordrecht, Springer, « Phaenomenologica », 2012.</ref>.
=== La Nature ===
Cette ontologie devait s'appuyer sur une méditation de la Nature, à laquelle Merleau-Ponty consacre plusieurs années de cours au Collège de France. Il en rappelle d'abord le sens originaire. Le mot vient du latin ''nascor'', naître : « Il y a nature partout où il y a une vie qui a un sens, mais où, cependant, il n'y a pas de pensée ; est nature ce qui a un sens, sans que ce sens ait été posé par la pensée. C'est l'autoproduction d'un sens<ref name="nature">Maurice Merleau-Ponty, ''La Nature. Notes, cours du Collège de France'', Paris, Seuil, 1995 (établi par Dominique Seglard).</ref>. » La Nature ainsi comprise n'est ni une chose inerte ni un produit de l'esprit. « Elle est notre sol, non pas ce qui est devant, mais ce qui nous porte<ref name="nature"/>. » En remontant vers cette Nature, vers cet « être brut » antérieur au partage du sujet et de l'objet, Merleau-Ponty voulait donner à la chair son assise et rejoindre, par un autre chemin, ce que le dernier Husserl avait entrevu sans le penser jusqu'au bout.
== Lecture de la tradition ==
Merleau-Ponty n'a jamais pratiqué l'histoire de la philosophie comme un inventaire de doctrines. Il y cherche un « impensé », c'est-à-dire ce qu'une pensée laisse ouvert et offre à reprendre. Commentant Husserl dans « Le philosophe et son ombre », il écrit, en s'autorisant d'une formule de Heidegger, que dans une œuvre forte « plus riche est l'impensé, c'est-à-dire ce qui, à travers cet ouvrage et par lui seul, vient vers nous comme jamais encore pensé<ref name="ombre">Maurice Merleau-Ponty, « Le philosophe et son ombre », dans ''Signes'', Paris, Gallimard, 1960.</ref> ». Penser, dit-il, « n'est pas posséder des objets de pensée, c'est circonscrire par eux un domaine à penser, que nous ne pensons donc pas encore<ref name="ombre"/> ». Cette manière de lire vaut programme : interpréter, ce n'est ni répéter ni déformer, c'est prolonger un geste, comme on retrouve un horizon plutôt qu'un objet. C'est dans cet esprit qu'il fréquente Descartes, dont la [[s:La Dioptrique|''Dioptrique'']] lui sert de contrepoint dans ''L'Œil et l'Esprit'', Bergson, dont il fait l'éloge nuancé au Collège de France, Schelling et son « principe barbare » de la Nature, et toute la lignée d'une philosophie qui, de Socrate à Husserl, tient « inséparablement le goût de l'évidence et le sens de l'ambiguïté ». Cette manière de lire est déjà une manière de faire de la philosophie : reprendre les œuvres du passé pour en relancer les questions, plutôt que les conserver comme un savoir clos ou les écarter comme un héritage périmé, c'est, ont noté ses lecteurs, transformer la tradition de l'intérieur, au lieu de la répéter ou de la rompre<ref name="flynn">''Merleau-Ponty and the Possibilities of Philosophy. Transforming the Tradition'', dir. Bernard Flynn, Wayne J. Froman et Robert Vallier, Albany, State University of New York Press, 2009.</ref>.
== Postérité et influence ==
La mort prématurée de Merleau-Ponty a laissé son ontologie à l'état de chantier, ce qui a longtemps voilé sa portée. Sa réception s'est faite par vagues. Ses proches, Claude Lefort et Cornelius Castoriadis, ont prolongé sa réflexion politique sur l'institution et l'autonomie. La génération suivante, où l'on range parfois trop vite Merleau-Ponty parmi les devanciers du structuralisme, lui doit pourtant une attention au corps, à l'expression et à la perception que ces courants avaient tendance à négliger.
Le destin éditorial de l'œuvre a pesé sur sa réception. C'est à Claude Lefort que l'on doit l'établissement des textes inachevés, ''Le Visible et l'Invisible'' et ''La Prose du Monde'', puis la publication des cours ; ce travail patient a fait apparaître peu à peu une pensée que la mort avait laissée dispersée. En France, l'attention au corps et à la perception a d'abord été recouverte par la vogue du structuralisme, avant de revenir au premier plan. Hors de France, c'est dans le monde anglophone que Merleau-Ponty a connu une seconde vie : la phénoménologie de la perception y a nourri une critique de l'intelligence artificielle classique et une réflexion sur le savoir-faire du corps, tandis que les théoriciens de l'énaction y voyaient l'annonce d'une biologie de la cognition affranchie du modèle de l'ordinateur. Son influence a gagné des domaines qu'il n'avait pas explorés lui-même : la théorie de l'art et l'histoire du regard, l'anthropologie du sensible, une géographie et une écologie attentives à l'expérience vécue du milieu, enfin une médecine et une psychiatrie soucieuses de décrire la maladie du point de vue de celui qui la vit plutôt que comme un simple désordre organique.
C'est sans doute hors de la stricte tradition phénoménologique que son influence s'est révélée la plus féconde. La théorie de l'« incarnation » (''embodiment'') et les courants de la cognition incarnée et de l'énaction, en sciences cognitives, ont retrouvé en lui un précurseur : Hubert Dreyfus a montré ce que l'intelligence artificielle devait à une description du savoir-faire corporel, et Francisco Varela a explicitement relié sa biologie de la connaissance aux analyses de la ''Phénoménologie de la perception''. La pensée féministe, l'esthétique, l'écologie philosophique, l'anthropologie des techniques ont également puisé dans son œuvre<ref name="keyconcepts">Pour un panorama de cette réception hors de la philosophie, notamment dans les sciences cognitives, la sociologie, les études sur la santé, la pensée féministe et les théories de la race, voir Rosalyn Diprose et Jack Reynolds (dir.), ''Merleau-Ponty: Key Concepts'', Stocksfield, Acumen, 2008.</ref>. À rebours de l'image d'un penseur seulement intermédiaire, placé entre Husserl et le structuralisme, la lecture la plus récente met au jour une philosophie ordonnée, qui tient ensemble la perception, l'expression et la chair, et dont la cohérence ne se réduit pas à un moment de passage : Lawrence Hass y voit moins une étape qu'une œuvre constituée, capable de nourrir aujourd'hui une pensée du corps, du sens et de l'art<ref name="hass">Lawrence Hass, ''Merleau-Ponty's Philosophy'', Bloomington, Indiana University Press, « Studies in Continental Thought », 2008.</ref>. Par-delà ces emprunts, c'est une exigence qui demeure : tenir ensemble la rigueur de la description et le sens de ce qui, dans l'expérience, ne se laisse jamais clore. Le philosophe, écrivait-il, se reconnaît à ce « mouvement qui reconduit sans cesse du savoir à l'ignorance, de l'ignorance au savoir, et une sorte de repos dans ce mouvement<ref name="eloge"/> » : non pas une marche assurée vers un terme, mais un pas toujours repris, entre l'évidence et l'énigme.
== Notes et références ==
{{references|colonnes=2}}
== Œuvres principales ==
* ''La Structure du comportement'', Paris, Presses universitaires de France, 1942.
* ''Phénoménologie de la perception'', Paris, Gallimard, 1945.
* ''Humanisme et Terreur. Essai sur le problème communiste'', Paris, Gallimard, 1947.
* ''Sens et non-sens'', Paris, Nagel, 1948.
* ''Éloge de la philosophie'', Paris, Gallimard, 1953.
* ''Les Aventures de la dialectique'', Paris, Gallimard, 1955.
* ''Signes'', Paris, Gallimard, 1960.
* ''L'Œil et l'Esprit'', Paris, Gallimard, 1964 (posthume).
* ''Le Visible et l'Invisible'', éd. Claude Lefort, Paris, Gallimard, 1964 (posthume).
* ''La Prose du Monde'', éd. Claude Lefort, Paris, Gallimard, 1969 (posthume).
* ''Résumés de cours. Collège de France, 1952-1960'', Paris, Gallimard, 1968 (posthume).
* ''La Nature. Notes, cours du Collège de France'', éd. Dominique Seglard, Paris, Seuil, 1995 (posthume).
== Bibliographie ==
=== Sources primaires citées ===
*{{Ouvrage|auteur=Maurice Merleau-Ponty|titre=Phénoménologie de la perception|lieu=Paris|éditeur=Gallimard|collection=Bibliothèque des idées|année=1945}}
*{{Ouvrage|auteur=Maurice Merleau-Ponty|titre=Sens et non-sens|lieu=Paris|éditeur=Nagel|année=1948}}
*{{Ouvrage|auteur=Maurice Merleau-Ponty|titre=Humanisme et Terreur. Essai sur le problème communiste|lieu=Paris|éditeur=Gallimard|année=1947}}
*{{Ouvrage|auteur=Maurice Merleau-Ponty|titre=Éloge de la philosophie|sous-titre=Leçon inaugurale au Collège de France|lieu=Paris|éditeur=Gallimard|année=1953}}
*{{Ouvrage|auteur=Maurice Merleau-Ponty|titre=Signes|lieu=Paris|éditeur=Gallimard|année=1960}}
*{{Ouvrage|auteur=Maurice Merleau-Ponty|titre=L'Œil et l'Esprit|lieu=Paris|éditeur=Gallimard|année=1964}}
*{{Ouvrage|auteur=Maurice Merleau-Ponty|titre=Le Visible et l'Invisible|responsabilité=éd. Claude Lefort|lieu=Paris|éditeur=Gallimard|année=1964}}
*{{Ouvrage|auteur=Maurice Merleau-Ponty|titre=La Prose du Monde|responsabilité=éd. Claude Lefort|lieu=Paris|éditeur=Gallimard|année=1969}}
*{{Ouvrage|auteur=Maurice Merleau-Ponty|titre=Résumés de cours. Collège de France, 1952-1960|lieu=Paris|éditeur=Gallimard|année=1968}}
*{{Ouvrage|auteur=Maurice Merleau-Ponty|titre=La Nature. Notes, cours du Collège de France|responsabilité=éd. Dominique Seglard|lieu=Paris|éditeur=Seuil|année=1995}}
=== Études (choix) ===
*{{Ouvrage|auteur=Théodore F. Geraets|titre=Vers une nouvelle philosophie transcendantale. La genèse de la philosophie de Maurice Merleau-Ponty jusqu'à la « Phénoménologie de la perception »|lieu=La Haye|éditeur=Martinus Nijhoff|année=1971}}
*{{Ouvrage|auteur=Renaud Barbaras|titre=De l'être du phénomène. Sur l'ontologie de Merleau-Ponty|lieu=Grenoble|éditeur=Jérôme Millon|année=1991}}
*{{Ouvrage|auteur=Pascal Dupond|titre=Le Vocabulaire de Merleau-Ponty|lieu=Paris|éditeur=Ellipses|année=2001}}
*{{Ouvrage|auteur=Ronald Bonan|titre=Premières leçons sur l'Esthétique de Merleau-Ponty|lieu=Paris|éditeur=Presses universitaires de France|année=1997}}
*{{Ouvrage|auteur=Ronald Bonan|titre=Merleau-Ponty|lieu=Paris|éditeur=Les Belles Lettres|collection=Figures du savoir|année=2011}}
*{{Ouvrage|auteur=Emmanuel Alloa|titre=La Résistance du sensible. Merleau-Ponty critique de la transparence|lieu=Paris|éditeur=Kimé|année=2008}}
*{{Ouvrage|auteur=Taylor Carman|titre=Merleau-Ponty|lieu=Londres et New York|éditeur=Routledge|année=2008}}
*{{Ouvrage|auteur=Komarine Romdenh-Romluc|titre=Routledge Philosophy GuideBook to Merleau-Ponty and Phenomenology of Perception|lieu=Londres et New York|éditeur=Routledge|année=2011}}
*{{Ouvrage|auteur=Stephen Priest|titre=Merleau-Ponty|lieu=Londres et New York|éditeur=Routledge|collection=The Arguments of the Philosophers|année=1998}}
*{{Ouvrage|auteur=Étienne Bimbenet|titre=Nature et humanité. Le problème anthropologique dans l'œuvre de Merleau-Ponty|lieu=Paris|éditeur=Vrin|année=2004}}
*{{Ouvrage|auteur=Annabelle Dufourcq|titre=Merleau-Ponty : une ontologie de l'imaginaire|lieu=Dordrecht|éditeur=Springer|collection=Phaenomenologica|année=2012}}
*{{Ouvrage|auteur=Lawrence Hass|titre=Merleau-Ponty's Philosophy|lieu=Bloomington|éditeur=Indiana University Press|collection=Studies in Continental Thought|année=2008}}
*{{Ouvrage|auteur=Rosalyn Diprose et Jack Reynolds (dir.)|titre=Merleau-Ponty: Key Concepts|lieu=Stocksfield|éditeur=Acumen|année=2008}}
*{{Ouvrage|auteur=Scott L. Marratto|titre=The Intercorporeal Self. Merleau-Ponty on Subjectivity|lieu=Albany|éditeur=State University of New York Press|année=2012}}
*{{Ouvrage|auteur=Frank Chouraqui|titre=Ambiguity and the Absolute. Nietzsche and Merleau-Ponty on the Question of Truth|lieu=New York|éditeur=Fordham University Press|année=2014}}
*{{Ouvrage|auteur=Donald A. Landes|titre=The Merleau-Ponty Dictionary|lieu=Londres|éditeur=Bloomsbury Academic|année=2013}}
*{{Ouvrage|auteur=Bernard Flynn, Wayne J. Froman et Robert Vallier (dir.)|titre=Merleau-Ponty and the Possibilities of Philosophy. Transforming the Tradition|lieu=Albany|éditeur=State University of New York Press|année=2009}}
{{DEFAULTSORT:Merleau-Ponty}}
[[Catégorie:Philosophe]]
8jplb9bx0c1hczcy0n2uz2ltvw1x9f0