# Fonctionnement d'un ordinateur/Les unités arithmétiques et logiques entières (simples)

Pour le moment, nous savons faire des additions, des soustractions, des décalages et rotations, ainsi que des opérations bit à bit. Chaque opération est réalisée par un circuit séparé. Cependant, il est possible de les fusionner en un seul circuit appelé une unité de calcul arithmétique et logique, abrévié ALU (Arithmetic and Logical Unit). Comme son nom l'indique, elle effectue des opérations arithmétiques et des opérations logiques (bit à bit). Tous les processeurs contiennent une ALU très similaire à celle qu'on va voit dans ce qui suit. La plupart des ALUs ne gèrent donc pas les opérations compliquées, comme les multiplications ou les divisions, de même que les décalages et rotation, et vous comprendrez pourquoi dans ce qui suit.

## L'interface d'une unité de calcul et sa conception

L'interface d'une ALU est assez simple. Il y a évidemment les entrées pour les opérandes et la sortie pour le résultat, mais aussi une entrée de commande qui permet de choisir l'instruction à effectuer. Sur cette entrée, on place une suite de bits qui précise l'instruction à effectuer. La suite de bit est très variable d'une ALU à l'autre. Elle peut être très structuré, chaque bit configurant une portion de l'ALU, ou être totalement arbitraire. La suite de bit peut être vu est aussi appelée l'opcode, ce qui est un diminution de code opération.
De plus, l'ALU a des sorties pour la retenue de sortie, les bits qui indiquent que le calcul a entrainé un débordement d'entier, etc. Ces bits sont appelés des flags, ou indicateurs. Les plus fréquents sont la retenue de sortie, un bit qui est à 1 si un débordement d'entier a eu lieu, un bit qui est à 1 si un débordement d'entier a eu lieu pour une addition signée (débordement en complètement à deux), un bit qui indique si le résultat est zéro, et quelques autres. Les flags sont calculés avec les circuits vus dans le chapitre précédent, dans la section sur la détection des débordements d'entiers.

### Le bit-slicing

Avant l'invention des premiers microprocesseurs, les processeurs étaient fournis en pièces détachées qu'il fallait relier entre elles. Le processeur était composé de plusieurs circuits intégrés, placés sur la même carte mère et connectés ensemble par des fils métalliques. Et l'ALU était un de ces circuits intégrés.
Les ALUs en pièces détachée de l'épique étaient assez simples et géraient 2, 4, 8 bits, rarement 16 bits. Mais il était possible d'assembler plusieurs ALU pour créer des ALU plus grandes. Par exemple, on pouvait combiner plusieurs ALU 4 bits afin de créer une unité de calcul 8 bits, 12 bits, 16 bits, etc. Par exemple, l'ALU des processeurs AMD Am2900 est une ALU de 16 bits composée de plusieurs sous-ALU de 4 bits. Cette technique qui consiste à créer des unités de calcul plus grosses à partir d’unités de calcul plus élémentaires s'appelle en jargon technique du bit slicing.
Le bit slicing est utilisé pour des ALU capables de gérer les opérations bit à bit, l'addition, la soustraction, mais guère plus. Les ALU en bit-slice qui gère les multiplications existent, mais sont rares. La raison est qu'il n'est pas facile d'implémenter une multiplication entre deux nombres de 16 bits avec deux multiplieurs de 4 bits (idem pour la division). Alors que c'est plus simple pour l'addition et la soustraction : il suffit de transmettre la retenue d'une ALU à la suivante. Bien sûr, les performances seront alors nettement moindres qu'avec des additionneurs modernes, à anticipation de retenue, mais ce n'était pas un problème pour l'époque.
L'implémentation des opérations bit à bit avec une ALU bit-slice est très simple, la seule complexité est l'addition. Si on combine deux ALU de 4 bits, la première calcule l'addition des 4 bits de poids faible, alors que le second calcule l'addition des 4 bits de poids fort. Mais il faut propager la retenue de l'addition des 4 bits de poids faible à la seconde ALU. Pour cela, l'ALU doit transmettre un bit de retenue sortant à l'ALU suivante, qui doit elle accepter celui-ci sur une entrée. Rappelons qu'une addition en binaire s'effectue comme en décimal : on additionne les bits colonne par colonne pour obtenir le bit de résultat, et il arrive qu'une retenue soit propagée à la colonne suivante.
Pour cela, l'ALU doit avoir une interface compatible : il faut qu'elle ait une entrée de retenue, et une sortie pour la retenue sortante. La retenue passée en entrée est automatiquement prise en compte lors d'une addition par l'ALU. Comme nous l'avons vu dans le chapitre dédié aux circuits de calculs, ajouter une entrée de retenue ne coute rien et est très simple à implémenter en à peine quelques portes logiques.

### L'intérieur d'une unité de calcul

Les unités de calcul les plus simples contiennent un circuit différent pour chaque opération possible. L’entrée de sélection commande des multiplexeurs pour sélectionner le bon circuit.
D'autres envoient les opérandes à tous les circuits en même temps, et activent ou désactivent chaque sous-circuit suivant les besoins. Chaque circuit possède ainsi une entrée de commande, dont la valeur est déduite par un circuit combinatoire à partir de l'entrée de sélection d'instruction de l'ALU (généralement un décodeur). Nous allons voir plusieurs exemples d'unités de calcul configurable dans ce chapitre. Pour le moment, sachez qu'un simple additionneur-soustracteur est un circuit configurable de ce genre.

## Les ALU sérielles

Les ALU sérielles effectuent leurs calculs 1 bit à la fois, bit par bit. Le circuit est alors très simple : il contient un circuit de calcul très simple, de 1 bit, couplé à trois registres à décalage : un par opérande, un pour le résultat. Le circuit de calcul prend trois bits en entrées et fournit un résultat d'un bit en sortie, avec éventuellement une retenue en sortie. Une bascule est ajoutée au circuit, pour propager les retenues des additions/soustractions, elle ne sert pas pour les opérations bit à bit.
Les ALU sérielles ne payent pas de mine, mais elles étaient très utilisées autrefois, sur les tout premiers processeurs. Les ordinateurs antérieurs aux années 50 utilisaient des ALU de ce genre. L'avantage de ces ALU est qu'elles peuvent gérer des opérandes de grande taille, avec plus d'une trentaine de bits, sans trop de problèmes. Il suffit de prévoir des registres à décalage suffisamment longs, ce qui est tout sauf un problème. Par contre, elles sont assez lentes pour faire leur calcul, vu que les calculs se font bit par bit. Elles sont d'autant plus lentes que les opérandes sont longs.

## Les ALU entières basées sur un additionneur-soustracteur

Il est possible d'obtenir une ALU entière simple en modifiant un additionneur-soustracteur simple. Pour rappel, un additionneur soustracteur est fait en combinant un additionneur avec un inverseur commandable.
Il est possible de modifier l'additionneur, mais aussi les circuits situés juste avant. L'idée est d'ajouter un circuit commandable de mise à zéro la seconde entrée d'opérande. Il est aussi possible de commander l'entrée de retenue entrante de l'additionneur séparément, via sa propre entrée.
Les opérations que peut faire cette ALU sont assez nombreuses. Déjà, elle supporte l'addition et la soustraction, quand la seconde opérande n'est pas inversée. En inversant la seconde opérande, on peut gérer deux opérations. La première est l'identité (on recopie l'opérande d'entrée sur la sortie), qui se fait en désactivant l'inverseur. En activant l'inverseur, on a l'opération d'inversion NOT, à savoir que les bits de l'opérande sont inversés. En jouant sur l'entrée de retenue, on peut aussi émuler d'autres opérations, comme l'incrémentation. Les 8 opérations possibles sont les suivantes :
| Reset | Invert | Retenue entrante | Sortie de l'ALU |
| 0 | 0 | 0 | A + B |
| 0 | 0 | 1 | A + B + 1 |
| 0 | 1 | 0 | A + $NOT(B)$ = A - B - 1 |
| 0 | 1 | 1 | A + $NOT(B)$ + 1 = A - B |
| 1 | 0 | 0 | B |
| 1 | 0 | 1 | B + 1 |
| 1 | 1 | 0 | $NOT(B)$ |
| 1 | 1 | 1 | $NOT(B)$ + 1 |

## Les ALU basées sur un additionneur, avec manipulation des retenues

Maintenant que nous avons vu ce qu'il est possible de faire en modifiant ce qu'il y a avant l'additionneur, nous allons modifier l'additionneur lui-même.

### L'implémentation du XOR/NXOR

Dans cette section, nous allons nous intéresser à un circuit qui effectue un XOR en plus de l'addition. Le choix d'utiliser à la fois une addition et un XOR peut sembler bizarre, mais s'explique par le fait qu'une opération XOR est identique à une addition binaire dont on ne tiendrait pas compte des retenues.
Pour rappel, un additionneur complet additionne trois bits, en faisant deux XOR :
    $(A\oplus B)\oplus {\text{Retenue}}$
Si on met la retenue entrante à zéro, on a :
    $(A\oplus B)\oplus 0=A\oplus B$
En clair, en manipulant les entrées de retenue des additionneurs complets, on peut avoir un XOR à partir de l'addition. Pour cela, on ajoute un circuit de masquage, comme vu dans le chapitre sur les opérations bit à bit, pour mettre les entrées à 0, on a le circuit ci-dessous. Le choix de l'opération est le fait d'une entrée de commande, mise à 0 pour un XOR et à 1 pour l'addition. Cette méthode marche avec tous les additionneurs, mais elle est plus simple à implémenter avec les additionneurs à anticipation de retenue.
Si un XOR est équivalent à une addition où les retenues sont mises à 0, on peut se demander ce qu'il se passe quand on les met à 1. Dans ce cas, pour chaque additionneur complet, le bit du résultat est :
    $(A\oplus B)\oplus 1$
Sachant que $X\oplus 1={\overline {X}}$, on se rend compte que le circuit calcule le NXOR des deux entrées.
En clair, en masquant les retenues entrantes, on peut transformer une addition en XOR ou en NXOR. Il suffit d'insérer des circuits de masquage avant les entrées de retenue. Le circuit de masquage soit recopie le bit d'entrée (pour l'addition), soit force les entrées de retenue à 0, soit les force à 1. Et on a déjà vu le circuit adéquat dans le chapitre sur les opérations bit à bit, à savoir la porte 1 bit universelle. Pour rappel, c'est un circuit avec deux bits de commandes, qui prend un bit en entrée et fournit sur la sortie : soit 0, soit 1, soit le bit d'entrée, soit son inverse. Il suffit donc d'ajouter une porte universelle 1 bit juste avant l'entrée de retenue entrante, et le tour est joué !

### L'implémentation du ET/OU avec une addition, en utilisant les retenues sortantes

Maintenant, faisons la même chose, mais regardons les retenues de sortie.
| 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 |
On remarque deux choses :
* si la retenue d'entrée est à 0, la retenue de sortie est un ET entre les deux bits d'opérandes.
* si on met la retenue entrante à 1, alors la retenue sortante sera un OU entre les deux bits d'opérandes
Pour implémenter le circuit, il faut connecter la sortie soit aux bits de résultat, soit aux entrées de retenue. Dans le circuit précédent, si la couche d'additionneurs finale est une couche d'additionneurs complets, on peut extraire le ET et le OU des deux opérandes. Un simple MUX placé à la suite permet de choisir si l'on regarde les bits du résultat ou la "retenue sortante" de ces additionneurs complets.

### L'implémentation du NOT avec une addition, en manipulant retenues et opérandes

Il est enfin possible d'implémenter l'opération NOT avec seulement un additionneur, pas besoin qu'il soit un additionneur-soustracteur. En effet, on obtient le NOT d'une opérande, via deux manières légèrement différentes.
Un additionneur complet peut se comporter comme une porte NOT à condition que l'une des entrées soit à 0 et l'autre à 1. La raison à cela est que l'additionneur complet fait un double XOR : le bit à inverser est XORé avec 0, ce qui le recopie, puis avec 1, ce qui l'inverse. Peu importe l'ordre, vu que le XOR est commutatif et associatif.
Cela donne deux possibilités : soit on met le second opérande à 0 et les retenues à 1, soit on fait l'inverse. Le résultat est disponible sur les bits de résultat. Pour cela, la solution consiste à ajouter un circuit qui met à 0 la seconde opérande, on a déjà le circuit pour manipuler les retenues. Mais cette solution est rarement utilisée, vu que la majorité des ALU utilise un additionneur-soustracteur, qui permet d'implémenter l'opération NOT facilement, tout en permettant d'implémenter aussi la soustraction, pour un cout en porte logique mineur et des performances quasiment identiques.

## Les ALU basées sur des ALU 1 bit

Les ALU précédentes sont basées sur des additionneurs auxquels on a rajouté des circuits. Mais les additionneurs complets eux-même n'ont pas été modifiés. Les ALU que l'on va voir dans ce qui suit fonctionnent sur un principe différent. Au lieu d'ajouter des circuits autour des additionneurs complets, elles modifient les additionneurs complets de manière à ce qu'il puisse faire des opérations logiques ET/OU/XOR/NXOR. Ils deviennent alors de véritables ALU de 1 bit, qui sont assemblées pour donner des ALU entières.
En plus d'assembler des ALU 1 bit, il faut aussi gérer les retenues, et les différentes manières de faire ressemblent beaucoup à ce qui se fait avec les additionneurs. Par exemple, on peut relier chaque ALU 1 bit comme dans un additionneur à propagation de retenue, où chaque ALU 1 bit envoie sa retenue sortante sur l'entrée de retenue de l'ALU 1 bit suivante. Une autre possibilité est d'utiliser un circuit de calcul des retenues, comme pour un additionneur à anticipation de retenue.

### L'exemple de l'ALU du processeur 8086 d'Intel

Voyons maintenant l'exemple du processeur 8086 d'Intel, un des tout premier de la marque. L'additionneur de cette ALU est un additionneur à propagation de retenue, avec une Manchester Carry Chain pour accélérer la propagation des retenues. Pour rappel, un additionneur Manchester carry chain génère en interne deux signaux : un signal de propagation de retenue et un signal de génération de retenue. Les deux sont combinés avec la retenue entrante pour calculer le résultat et la retenue de sortie, la combinaison se faisant avec un circuit basé sur des portes à transmission. Les deux signaux sont déterminés par une unique porte logique, qui prend en entrée les deux bits d'opérande : une porte logique détermine si l'addition génère une retenue, un autre si elle propage la retenue d'entrée sur la colonne suivante. Un tel additionneur est illustré ci-dessous, pour rappel.
Sur le 8086, ces deux portes sont remplacées par une porte logique universelle commandable 2 bit, à savoir un circuit qui peut remplacer toutes les portes logiques 2 bit existantes. Comme vu dans le chapitre sur les opérations bit à bit, cette porte universelle est un simple multiplexeur configuré convenablement. En conséquence, le signal de propagation et de génération de retenue sont configurables et on peut les utiliser pour calculer autre chose. Par exemple, la première porte XOR peut être remplacée par une porte ET, OU, NOR, FALSE (elle donne toujours zéro en sortie), OUI (recopie un bit d'entrée sur la sortie), etc.
La gestion des additions et soustractions est alors triviale. Il suffit de configurer les deux portes universelles de manière à obtenir le circuit d'un additionneur complet ou d'un soustracteur complet.
Lors des opérations bit à bit et des décalages, les deux signaux sont configurés de manière à ce qu'au moins l'un d'entre elle ait sa sortie mise à 0. C'est-à-dire que l'une des deux portes universelles sera configurée de manière à devenir une porte FALSE. En faisant cela, la porte XOR aura une entrée à 0, ce qui fait qu'elle recopiera l'autre entrée sur sa sortie. Elle se comportera comme une porte OUi pour l'autre entrée, celle pas mise à 0.
Pour les opérations logiques, l'une des portes universelle est configurée de manière à avoir la porte logique voulue, l'autre est mise à 0, la porte XOR recopie l'entrée de la première. La porte logique mise à 0 est celle qui génère les retenues. La porte qui calcule le signal de propagation de la retenue, celle qui additionne les deux bits d'opérande, est alors configurée pour donner la porte voulue : soit un ET, soit un OU, soit un XOR, soit...
L'ALU du 8086 supporte aussi les décalages d'un rang vers la gauche, qui sont équivalents à une multiplication par deux. L'opérande décalée est envoyé sur les entrées A de chaque additionneur complet. Pour effectuer, ils utilisent une solution très simple : chaque additionneur envoie le bit de l'opérande sur la sortie de retenue. De plus, les entrées d'opérandes ne sont pas additionnées. Pour résumer, il faut que le signal de propagation de retenue soit mis à zéro, alors que le signal de génération de retenue soit égal au bit d'entrée de l'opérande. Les deux portes logiques universelles sont alors configurées pour : la porte de propagation se comporte comme une porte FALSE, l'autre comme une porte OUI qui recopie l'entrée A.
En somme, l'ALU fait son travail en configurant les deux portes universelles. Pour configurer les portes, l'ALU contient un petit circuit combinatoire qui traduit l'opcode en signaux envoyés aux portes universelles.
Pour ceux qui veulent en savoir plus sur les circuits de calcul de l'Intel 8086, voici un lien :
* Reverse-engineering the 8086's Arithmetic/Logic Unit from die photos

### L'exemple de l'ALU du processeur Intel x86 8008

L'ALU du processeur Intel x86 8008 est une ALU 8 bits (les opérandes sont de 8 bits), qui implémente 4 opérations : l'addition, ET, OU, XOR. L'addition est réalisée par un circuit d'anticipation de retenue, chose assez rare sur les processeurs de l'époque. Il n'était pas possible de placer beaucoup de transistors sur les puces de l'époque, ce qui fait que les concepteurs de processeurs tournaient à l'économie et privilégiaient des additionneurs à propagation de retenue.
Comme beaucoup d'ALU des processeurs assez anciens, elle est construite en assemblant plusieurs ALU de 1 bits, chacune étant un additionneur complet amélioré. L'ALU de 1 bit utilise des additionneurs complets implémentés avec le circuit suivant :
L'additionneur précédent est modifié pour gérer les trois opérations XOR, ET, OU. Pour gérer le XOR, il suffit de mettre la retenue d'entrée à 0, ce qui est réalisé avec une vulgaire porte ET pour chaque additionneur complet, placée en aval de l'entrée de retenue. Pour gérer les deux autres opérations logiques, le circuit ne suit pas la logique précédente et n'utilise pas de multiplexeur. Le résultat du ET/OU est bien disponible sur la sortie de résultat, non sur la sortie de retenue. A la place, le circuit utilise la porte ET et la porte OU de l'additionneur complet, et désactive la porte inutile. Pour un ET/OU, le circuit met à zéro la retenue entrante. De plus, elle met aussi à zéro la retenue sortante, sans quoi le circuit donne des résultats invalides.
Dans les faits, l'implémentation exacte était légèrement plus complexe, vu que ce circuit était conçu à partir de portes TTL AND-OR-NAND, qui regroupe une porte ET, une porte OU et une porte NAND en une seule. Pour ceux qui veulent en savoir plus sur les circuits de calcul de l'Intel 8008, voici un lien qui pourrait vous intéresser :
* Reverse-engineering the surprisingly advanced ALU of the 8008 microprocessor

## L'exemple de l'unité de calcul 74181

Afin d'illustrer ce qui a été dit plus haut, nous allons étudier un exemple d'unité de calcul : l'unité de calcul 74181, très souvent utilisée dans les autres cours d'architecture des ordinateurs pour son aspect pédagogique indéniable. Il s'agit d'une unité de calcul commercialisée dans les années 60, à une époque où le microprocesseur n'existait pas. Les processeurs de l'époque étaient conçus à partir de pièces détachées assemblées et connectées les unes aux autres. Les pièces détachées en question étaient des boitiers qui contenaient des registres, l'unité de calcul, des compteurs, des PLA, qu'on assemblait sur une carte électronique pour faire le processeur. L'unité 74181 était une des toutes premières unités de calcul fournie dans un boitier, là où il fallait auparavant fabriquer une ALU à partir de circuits plus simples comme des additionneurs ou des circuits d’opérations bit à bit.
Le 74181 était une unité de calcul de 4 bits, ce qui veut dire qu'il était capable de faire des opérations arithmétiques sur des nombres entiers codés sur 4 bits. Il prenait en entrée deux nombres de 4 bits, et fournissait un résultat de 4 bits. Il était possible de faire du bit-slicing, à savoir de combiner plusieurs 74181 afin de créer une unité de calcul 8 bits, 12 bits, 16 bits, etc. Le 74181 était spécifiquement conçu pour, car il gérait un bit de retenue en entrée et fournissait une sortie pour la retenue du résultat.

### Les opérations gérées par l'ALU 74181

Les opérations de base du 74181 comprennent l'addition et 16 opérations dites bit à bit. Il peut fonctionner selon deux modes. Dans le premier mode, il effectue une opération bit à bit seule. Dans le second mode, il effectue une opération bit à bit entre les deux nombres d'entrée A et B, additionne le nombre A au résultat, et additionne la retenue d'entrée. Pour résumer, il effectue une opération bit à bit et une addition facultative. En tout, le 74181 était capable de réaliser 32 opérations différentes : les 16 opérations bit à bit seules (le maximum d'opérations de ce type possibles entre deux bits), et 16 autres opérations obtenues en combinant les 16 opérations bit à bit avec une addition.
Le fait de faire une opération bit à bit avant l'addition permet d'émuler une soustraction. Rappelons qu'une soustraction entre deux nombres A et B s'obtient en inversant les bits de B, en additionnant et en ajoutant 1. Or, il existe une opération bit à bit qui inverse tous les bits d'un nombre et celle-ci est supportée par le 74181. Ajouter 1 se fait en envoyant une retenue égale à 1 sur l'entrée de retenue.
L'entrée de sélection de l'instruction fait 5 bits, ce qui colle parfaitement avec les 32 instructions possibles. Les 5 bits en question sont séparés en deux : un groupe de 4 bits qui précise l'opération bit à bit, et un bit isolé qui indique s'il faut faire l'addition ou non. L'opération bit à bit à effectuer, est précisée par 4 bits d'entrée notés s0, s1, s2 et s3. L'activation de l'addition se fait par un bit d'entrée, le bit M, qui précise s'il faut faire ou non l'addition.

### L'implémentation de l'ALU 74181

Le 74181 comprend 75 portes logiques, du moins en théorie. Ce nombre est à relativiser, car l’implémentation utilisait des optimisations qui fusionnaient plusieurs portes entre elles. Elle utilisait notamment des portes AND-OR-NOT, identique à une porte ET suivie d'une porte NOR.
L'implémentation de ce circuit est, sur le papier, très simple. On prend un additionneur à anticipation de retenue, et chaque additionneur complet est précédé par une porte logique universelle 2 bit, réalisée avec un multiplexeur, qui implémente les 16 opérations logiques. Le circuit est cependant très optimisé, dans le sens où l'additionneur complet est fusionné avec la porte logique universelle. Comme sur le 8086, on modifie la manière dont les signaux de propagation et de génération de retenue sont calculés. Sauf qu'ici, on n'utilise qu'une seule porte logique universelle, très modifiée.
Le 74181 est composé de circuits assez semblables à une porte logique universelle de 2 bits, sauf qu'elle fournit deux sorties : un signal de propagation de retenue, un signal de génération de retenue. Pour comprendre comment il fonctionne, le mieux est d'établir sa table de vérité. On part du principe que le circuit a deux entrées A et B, et calcule A + f(A,B), avec f(A,B) une opération bit à bit.
| A | B | A PLUS f(a,b) | P | G |
| 0 | 0 | 0+f(0,0) | f(0,0) | 0 |
| 0 | 1 | 0+f(0,1) | f(0,0) | 0 |
| 1 | 0 | 1+f(1,0) | 1 | f(1,0) |
| 1 | 1 | 1+f(1,1) | 1 | f(1,1) |
Sur le 74181, il faut imaginer que le circuit qui calcule f(A,B) est une porte universelle commandable 2 bits, réalisée avec un multiplexeur. Les bits du résultat sont envoyés sur les 4 entrées du multiplexeur, et le multiplexeur choisit le bon bit à partir des entrées A et B (qui sont envoyés sur son entrée de commande. Les 4 entrées du multiplexeur sont notées S0, S1, S2 et S3. On a alors :
| A | B | A PLUS f(a,b) | P | G |
| 0 | 0 | 0+f(0,0) | S1 | 0 |
| 0 | 1 | 0+f(0,1) | S0 | 0 |
| 1 | 0 | 1+f(1,0) | 1 | S2 |
| 1 | 1 | 1+f(1,1) | 1 | S3 |
Le circuit pour faire cela est le suivant :
Le schéma du circuit est reproduit ci-dessous. Un œil entrainé peut voir du premier coup d’œil que l'additionneur utilisé est un additionneur à anticipation de retenue modifié. La première couche dans le schéma ci-dessous correspond au circuit qui calcule les signaux P et G. La seconde couche est composée du reste de l'additionneur, à savoir du circuit qui combine les signaux de propagation et de génération des retenues finales.
Pour ceux qui veulent en savoir plus sur cette unité de calcul et n'ont pas peur de lire une analyse des transistors TTL de la puce, voici deux articles très intéressant sur cette ALU :
* Inside the vintage 74181 ALU chip: how it works and why it's so strange
* Inside the 74181 ALU chip: die photos and reverse engineering

## L'implémentation naïve, avec des multiplexeurs

Pour obtenir une ALU, vous avez peut-être pensé à la solution ci-dessous. L'idée est d'utiliser un circuit par opération et de choisir le résultat adéquat avec des multiplexeurs. Le multiplexeur est évidemment commandé par l'entrée de commande, il reçoit l'opcode. Un exemple avec une ALU de 2 bits est conné ci-dessous.
Mais cette solution peut être mélangée avec les solutions précédentes. Par exemple, il est possible de mélanger cette idée avec une ALU basée sur un additionner-soustracteur. Pour obtenir un additionneur-soustracteur, on doit placer un inverseur commandable avant l'additionneur, en aval d'un opérande. L'idée est de relier l'inverseur commandable non seulement à l'additionneur, mais aussi aux portes ET/OU/XOR. Cela permet de faire les opérations NOR/NAND/NXOR, gratuitement, juste en changeant le câblage du circuit. Il est aussi possible d'ajouter un circuit pour mettre à zéro l'opérande non inversée, comme vu plus haut. Le résultat est celui vu plus haut.