Aller au contenu

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

Un livre de Wikilivres.

Dans les chapitres précédents, nous avons vu les circuits pour l'addition, la soustraction et les comparaisons. Nous avons aussi vu qu'il est très facile d'implémenter la soustraction en rajoutant quelques portes logiques à un additionneur. Et de même, une fois qu'on sait faire la soustraction, implémenter les comparaisons demande juste d'ajouter quelques portes logiques. Mais il est possible d'aller plus loin ! Dans ce chapitre, nous allons voir un 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 additions, des soustractions, des comparaisons et des opérations bit à bit. La plupart des ALUs ne gèrent pas les multiplications/divisions et vous comprendrez pourquoi dans ce qui suit.

Tous les processeurs contiennent au moins une ALU. En fait, créer un processeur demande une unité de calcul, des registres, un circuit de communication avec la mémoire et d'interconnecter le tout. Il faut aussi ajouter des circuits pour commander le tout, qui sont regroupés dans l'unité de contrôle. L'unité de contrôle lit les instructions en mémoire, puis commande l'unité de calcul, les registres et la mémoire pour que l'instruction soit exécutée correctement. L'unité de contrôle est assez complexe et aura droit à plusieurs chapitres dédiés, nous avons déjà vu les registres, il est temps de voir l'unité de calcul.

Microarchitecture d'un processeur

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

[modifier | modifier le wikicode]

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, qui varie d'une ALU à l'autre. La suite de bit peut être vu est aussi appelée l'opcode, ce qui est un diminution de code opération.

L'ALU a aussi une entrée de retenue entrante, sur le même modèle que les additionneurs. Pour rappel, les additionneurs sont conçus avec des additionneurs complets, qui prennent trois bits en entrée : deux bits d'opérande et un bit de retenue. Pour la colonne des bits de poids faible, il y a aussi un additionneur complet qui prend en opérande les deux bits de poids faible, mais aussi une retenue entrante. Les unité de calcul entières contiennent un additionneur entier, ce qui fait qu'elles aussi disposent de cette entrée de retenue. Elles fournissent aussi la retenue en sortie, avec d'autres informations, ce qui nous amène à parler des sorties de l'ALU.

En plus de la sortie pour le résultat, l'ALU a des sorties de 1 bit appelées des flags, ou indicateurs. Les plus fréquents sont les fameux bits intermédiaires vu dans le chapitre sur les comparaisons : un bit qui est à 1 si un débordement d'entier a eu lieu (la retenue de sortie), un bit qui est à 1 si un débordement d'entier en complètement à deux a eu lieu, un bit qui indique si le résultat est zéro, le bit de signe du résultat en complément à deux. Si c'est le cas, les bits intermédiaires alimentent souvent un circuit qui calcule le résultat d'une comparaison, qui est considéré comme séparé de l'ALU.

Mais une ALU peut fournir d'autres flags en plus de ces 4 bits intermédiaires, voire ne pas fournir les 4 bits précédents, tout dépend de l'ALU. Par exemple, certains processeurs avaient un flag qui donnait le bit de parité du résultat. Autre exemple, les processeurs avec un support du BCD avaient des flags dédiés à la gestion du BCD. Le processeur Z80 fournissait les deux flags des exemples précédents, à savoir un flag pour le bit de parité du résultat, un autre pour la gestion du BCD, et un autre pour indiquer que le résultat valait zéro.

Interface d'une ALU

Le bit-slicing

[modifier | modifier le wikicode]

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 des opérandes de 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 pour 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 le 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. Il n'y a pas, à ma connaissance, d'ALU en bit-slicing capable d'effectuer une multiplication ou une division.

L'implémentation des opérations bit à bit avec une ALU bit-slice est triviale, la seule complication mineure 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. Il faut que l'ALU ait 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

[modifier | modifier le wikicode]

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.

Unité de calcul conçue avec des sous-ALU reliées par des multiplexeurs.

Mais les ALU que nous allons voir fonctionnent autrement. Elles sont construites sur le même modèle que l'additionneur-soustracteur, qui est un circuit configurable. On lui envoie un bit de commande qui décide entre addition ou soustraction, ce bit de commande configure un inverseur commandable et la retenue entrante. Les ALU qui vont suivre disposent de plusieurs circuits semblables à l'inverseur commandable. Ils possèdent une entrée de commande, dont la valeur est déduite par un circuit combinatoire à partir du code opération (généralement un décodeur).

ALU composée de sous-ALU configurables.

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

[modifier | modifier le wikicode]

Pour rappel, un additionneur soustracteur est fait en combinant un additionneur avec un inverseur commandable. L'entrée de retenue et l'entrée de commande de l'inverseur sont partagée, c'est le même bit qui est envoyé sur les deux. Mais dans ce qui suit, on va supposer qu'elles sont découplées, qu'on peut envoyer des bits différents sur les deux. Le circuit est donc celui-ci :

Additionneur soustracteur

De plus, nous allons ajouter un circuit commandable de mise à zéro pour la seconde entrée d'opérande.

ALU basée sur un additionneur soustracteur modifié

L'ALU obtenue ainsi supporte 8 opérations distinctes, résumées dans le tableau ci-dessous. Les principales sont l'addition, la soustraction, l'opération NOT, l'incrémentation, le calcul du complément à deux, et l'identité (une entrée est recopiée sur la sortie).

Reset Invert Retenue entrante Sortie de l'ALU
0 0 0 A + B
0 0 1 A + B + 1
0 1 0 A + = A - B - 1
0 1 1 A - B
1 0 0 B
1 0 1 B + 1
1 1 0
1 1 1 + 1 (complément à deux)

Pour les autres opérations bit à bit, l'idéal est d'ajouter des circuits pour les opérations ET/OU/XOR en parallèle de l'additionneur-soustracteur et d'utiliser un multiplexeur pour choisir quel circuit donne le résultat. Une amélioration relie l'inverseur commandable non seulement à l'additionneur, mais aussi aux portes ET/OU/XOR. Il est aussi possible de faire pareil avec le circuit pour mettre à zéro l'opérande non inversée. Le tout permet d'ajouter quelques opérations logiques gratuitement, juste en changeant le câblage du circuit

ALU simplifiée.

Les ALU basées sur la manipulation des retenues

[modifier | modifier le wikicode]

L'ALU précédente implémente pas les opérations bit à bit en ajoutant des circuits autour de l'additionneur. Cependant, il existe une alternative qui modifie l'additionneur pour qu'il devienne capable de faire des opérations ET/OU/XOR. Pour comprendre comment faire, il faut rappeler qu'un additionneur est composé de deux parties : une couche d'additionneurs complets, et le reste qui s'occupe du calcul ou de la propagation des retenues. Et il se trouve qu'en manipulant les retenues, on peut émuler d'autres opérations à partir de l'addition.

Par exemple, nous avons déjà vu que l'opération XOR est une addition dans laquelle les retenues seraient ignorées. En conséquence, on peut émuler un XOR à partir d'une addition, en rajoutant un circuit pour mettre les retenues à 0, simplement composé de portes ET. 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. Mais on peut aller encore plus loin...

Circuit qui fait ADD et XOR.

Les unités de calcul logiques fabriquées avec des additionneurs complets

[modifier | modifier le wikicode]

Mine de rien, un additionneur complet seul est capable d'exécuter de nombreuses opérations bit à bit, ce qui permet d'implémenter une unité de calcul logique avec des additionneurs complets. Pour rappel, une unité de calcul logique ne gère que les opérations bit à bit, pas l'addition ni la soustraction. Les opérations supportées sont les opérations NOT, OU, ET, XOR, parfois d'autres comme NXOR. Et un additionneur complet gère ces opérations nativement. Pour rappel, un additionneur complet additionne trois bits, en faisant deux XOR :

Il est alors intéressant de voir ce qui se passe si on force la retenue entrante à 0 ou 1. Si on force la retenue entrante à 0, le tout se simplifie grandement. On rappelle à toute fin utile que . Les équations précédentes deviennent :

A l'opposé, si on force les retenues à 1, les équations deviennent totalement différentes. Sachant que , on obtient :

Pour résumer :

  • Si la retenue d'entrée est à 0, la retenue de sortie est un ET entre les deux bits d'opérandes, le bit de somme en est le XOR.
  • Si on met la retenue entrante à 1, alors la retenue sortante sera un OU entre les deux bits d'opérandes, le bit de somme en est le NXOR.

L'unité de calcul à forçage des retenues

[modifier | modifier le wikicode]

Pour manipuler des retenues, il faut ajouter un circuit de masquage dans l'additionneur-soustracteur, pour mettre les retenues à 0/1. 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. Le circuit de masquage est composé de portes universelles 1 bit, un circuit qu'on a abordé dans le chapitre sur les opérations bit à bit, avec une porte universelle par retenue.

Additionneur modifiée en ALU entière capable de faire des XOR et NXOR

Pour finaliser le circuit, il faut connecter la sortie soit aux bits de résultat, soit aux entrées de retenue, ce qui demande un simple multiplexeur.

Implémentation d'une ALU entière simple

Les ALU basées sur des ALU 1 bit

[modifier | modifier le wikicode]

Les ALU précédentes sont basées sur des additionneurs auxquels on a rajouté des circuits, mais où les additionneurs complets eux-mêmes n'ont pas été modifiés. Les ALU que l'on va voir dans ce qui suit font l'inverse : elle transforment les additionneurs complets en de véritables ALU de 1 bit, capables de faire des opérations logiques ET/OU/XOR/NXOR. Les ALU 1 bit sont souvent connectées comme dans un additionneur à propagation de retenue, mais ce n'est pas systématique et on peut faire la même chose avec des additionneurs plus complexes.

ALU parallèle fabriquée à partir d'ALU 1 bit.

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

[modifier | modifier le wikicode]

Voyons maintenant l'ALU du processeur 8086 d'Intel, un des tout premier de la marque. Elle est basée sur un additionneur complet qui calcule la retenue sortante avec un multiplexeur 2 vers 1, illustré ci-dessous.

Additionneur complet basé sur un MUX

Sur le 8086, la porte XOR et la porte ET sont remplacées par une porte logique universelle commandable 2 bit, à savoir un circuit qui peut remplacer toutes les portes logiques 2 bit existantes. Pour configurer les deux portes, l'ALU contient un petit circuit combinatoire qui traduit l'opcode en signaux envoyés aux portes universelles.

ALU du 8086 (bloc de 1 bit)

Pour l'addition et la soustraction, les deux portes sont configurées pour reformer sur un additionneur complet. Pour les opérations bit à bit, la porte qui remplace le XOR est alors configurée pour donner la porte voulue : soit un ET, soit un OU, soit un XOR, soit.... En parallèle, l'autre porte logique a un 0 sur sa sortie, afin de mettre les retenues à 0.

ALU du 8086 lors d'une opération logique

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écaler est envoyé sur les entrées A de chaque additionneur complet. Les deux portes logiques universelles sont alors configurées comme suit : la porte de propagation se comporte comme une porte FALSE, l'autre comme une porte OUI qui recopie l'entrée A.

ALU du 8086 lors d'un décalage à gauche d'un rang

Pour ceux qui veulent en savoir plus sur les circuits de calcul de l'Intel 8086, voici un lien :

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

[modifier | modifier le wikicode]

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. Leur budget en transistors était en faveur des additionneurs à propagation de retenue. Elle est construite en assemblant plusieurs ALU de 1 bits, chacune basée sur un additionneur implémenté avec le circuit suivant, abordé précédemment dans ce chapitre :

Full adder basé sur une modification de la retenue

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 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 :

L'exemple de l'unité de calcul 74181

[modifier | modifier le wikicode]

L'unité de calcul 74181 est très souvent présentée dans les cours d'architecture des ordinateurs, pour son aspect pédagogique indéniable. Elle a été commercialisée dans les années 60, à une époque où processeurs étaient vendus en kit, en pièces détachées. 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.

Le 74181 était une ALU de 4 bits, ce qui veut dire qu'elle prenait en entrée deux opérandes entiers 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

[modifier | modifier le wikicode]

Le 74181 fonctionne concrètement comme un additionneur-soustracteur amélioré sur deux points. Premièrement, l'inverseur commandable est remplacé par une porte universelle 2 bits. Pour l'additionneur, il conserve son entrée de retenue, mais il est désactivable. Concrètement, il y a un MUX en sortie de l'ALU qui choisit la sortie parmi : la sortie des portes universelles 2 bits, la sortie de l'additionneur. 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 M qui indique s'il faut faire l'addition ou non. Dans le groupe de 4 bits, les bits sont notés s0, s1, s2 et s3.

Schéma fonctionnel du 74181.

En conséquence, le 74181 peut combiner l'addition et les 16 opérations bit à bit (donc toutes les opérations de ce type possibles entre deux bits). L'ALU 74181 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. Un exemple d'opération de ce genre est la soustraction, obtenue en combinant l'opération bit à bit NOT, une retenue d'entrée à 1, et une addition. En tout, le 74181 était capable de réaliser 32 opérations différentes : les 16 opérations bit à bit seules, et 16 autres opérations obtenues en combinant une opération bit à bit avec une addition.

L'implémentation de l'ALU 74181

[modifier | modifier le wikicode]

Le 74181 comprend 75 portes logiques, mais 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. Le circuit est cependant très optimisé, dans le sens où l'additionneur complet est fusionné avec la porte logique universelle.

L'idée part d'un additionneur PG, qui génère deux signaux de propagation et de génération de retenue sont calculés. Le 8086 remplace les deux portes qui calculent ces signaux par des portes universelles 2 bits. Le 74181 n'utilise qu'une seule porte logique universelle, très modifiée. En clair, le 714181 est composé d'ALU 1 bit reliées à un circuit d’anticipation de retenue. La table de vérité de vérité des ALU 1 bit est la suivante. 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 :

Circuit de base du 74181, avant l'additionneur

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.

Schéma des portes logique de l'ALU 74181.

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 :

Les ALU sérielles

[modifier | modifier le wikicode]

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.

L'ALU sérielle est facile à concevoir à partir de sa table de vérité, aussi je ne va pas détailler sa conception, je laisse le tout en exercice au lecteur. Mais un moyen de la concevoir facilement est simplement d'utiliser un additionneur complet avec de quoi mettre la retenue à 0/1, idem pour une des deux entrées d'opérande.

ALU sérielle

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.