# Fonctionnement d'un ordinateur/Les circuits de sélection

Dans les chapitres précédents, nous avons vu comment fabriquer des circuits relativement généraux. Il est maintenant temps de voir quelques circuits relativement simples, très utilisés. Ces circuits simples sont utilisés pour construire des circuits plus complexes, comme des processeurs, des mémoires, et bien d'autres. Les prochains chapitres vont se concentrer exclusivement sur ces circuits simples, mais courants. Nous allons donner quelques exemples de circuits assez fréquents dans un ordinateur et voir comment construire ceux-ci avec des portes logiques.
Dans ce chapitre, nous allons nous concentrer sur quelques circuits, que j'ai décidé de regrouper sous le nom de circuits de sélection. Les circuits que nous allons présenter sont utilisés dans les mémoires, ainsi que dans certains circuits de calcul. Il est important de bien mémoriser ces circuits, ainsi que la procédure pour les concevoir : nous en aurons besoin dans la suite du cours. Ils sont au nombre de quatre : le décodeur, l'encodeur, le multiplexeur et le démultiplexeur.

## Le décodeur

Le premier circuit que nous allons voir est le décodeur, un composant qui contient un grand nombre d'entrées et de sorties, avec des sorties qui sont numérotées. Un décodeur possède une entrée sur laquelle on envoie un nombre codé $N$ bits et $2^{N}$ sorties de 1 bit. Par exemple, un décodeur avec une entrée de 2 bits aura 4 sorties, un décodeur avec une entrée de 3 bits aura 8 sorties, un décodeur avec une entrée de 8 bits aura 256 sorties, etc. Généralement, on précise le nombre de bits d'entrée et de sortie comme suit : on parle d'un décodeur X vers Y pour X bits d'entrée et Y de sortie. Ce qui fait qu'on peut parler de décodeur 3 vers 8 pour un décodeur à 3 bits d'entrée et 8 de sortie, de décodeur 4 vers 16, etc.
Le fonctionnement d'un décodeur est très simple : il prend sur son entrée un nombre entier x codé en binaire, puis il positionne à 1 la sortie numérotée x et met à zéro toutes les autres sorties. Par exemple, si on envoie la valeur 6 sur ses entrées, il mettra la sortie numéro 6 à 1 et les autres à zéro.
Pour résumer, un décodeur est un circuit :
* avec une entrée de $N$ bits ;
* avec $2^{N}$ sorties de 1 bit ;
* où les sorties sont numérotées en partant de zéro ;
* où on ne peut sélectionner qu'une seule sortie à la fois : une seule sortie devra être placée à 1, et toutes les autres à zéro ;
* et où deux nombres d'entrée différents devront sélectionner des sorties différentes : la sortie de notre contrôleur qui sera mise à 1 sera différente pour deux nombres différents placés sur son entrée.
Une autre manière d'expliquer leur fonctionnement est qu'il traduisent un nombre encodé en binaire vers la représentation one-hot. Pour rappel, sur cette dernière, le nombre N est encodé en mettant le énième bit à 1, les autres sont à 0. Le bit de poids faible compte pour le zéro.
Les décodeurs sont très utilisés, au point que faire la liste de leurs utilisations serait bien trop long. Par contre, on peut d'or et déjà prévenir que les décodeurs sont utilisés dans toutes les mémoires RAM et ROM, présentes dans tout ordinateur. La RAM de votre ordinateur contient un ou plusieurs décodeurs, idem pour la mémoire caché intégrée dans le processeur, etc. C'est donc un circuit absolument primordial à étudier, qui reviendra souvent dans ce cours.

### La table de vérité d'un décodeur

Au vu de ce qui vient d'être dit, on peut facilement écrire la table de vérité d'un décodeur. Pour l'exemple, prenons un décodeur 2 vers 4, pour simplifier la table de vérité. Voici sa table de vérité complète, c’est-à-dire qui contient toutes les sorties regroupées :
| E0 | E1 | S0 | S1 | S2 | S3 |
| 0 | 0 | 1 | 0 | 0 | 0 |
| 0 | 1 | 0 | 1 | 0 | 0 |
| 1 | 0 | 0 | 0 | 1 | 0 |
| 1 | 1 | 0 | 0 | 0 | 1 |
Vous remarquerez que la table de vérité est assez spéciale. Les seuls bits à 1 sont sur la diagonale. Et cela ne vaut pas que dans l'exemple choisit, mais cela se généralise pour tous les décodeurs. Sur chaque ligne, il n'y a qu'un seul bit à 1, ce qui traduit le fait qu'une entrée ne met qu'une seule sortie est à 1 et met les autres à 0. Si on traduit la table de vérité sous la forme d'équations logiques et de circuit, on obtient ceci :
Il y a des choses intéressantes à remarquer sur les équations logiques. Pour rappel, l'équation logique d'une sortie est composée, dans le cas général, soit d'un minterm unique, soit d'un OU entre plusieurs minterms. Chaque minterm est l'équation d'un circuit qui compare l'entrée à un nombre bien précis et dépendant du minterm. Si on regarde bien, l'équation de chaque sortie correspond à un minterm et à rien d'autre, il n'y a pas de OU entre plusieurs minterms. Les minterms sont de plus différents pour chaque sortie et on ne trouve pas deux sorties avec le même minterm. Enfin, chaque minterm possible est présent : X bits d'entrée nous donnent 2^X entrées différentes possibles, donc 2^X minterms possibles. Et il se trouve que tous ces minterms possibles sont représentés dans un décodeur, ils ont tous leur sortie associée. C'est une autre manière de définir un décodeur : toutes ses sorties codent un minterm, deux sorties différentes ont des minterms différents et tous les minterms possibles sur n bits sont représentés.
Ces informations vont nous être utiles pour la suite. En effet, grâce à elles, nous allons en déduire une méthode générale pour fabriquer un décodeur, peu importe son nombre de bits d'entrée et de sortie. Mais elles permettent aussi de montrer que l'on peut créer n'importe quel circuit combinatoire quelconque à partir d'un décodeur et de quelques portes logiques. Dans ce qui suit, on suppose que le circuit combinatoire en question a une entrée de n bits et une seule sortie de 1 bit. Pour rappel, ce genre de circuit se conçoit en utilisant une table de vérité qu'on traduit en équations logiques, puis en circuits. Le circuit obtenu est alors soit un simple minterm, soit un OU entre plusieurs minterms. Or, le décodeur contient tous les minterms possibles pour une entrée de n bits, avec un minterm par sortie. Il suffit donc de prendre une porte OU et de la connecter aux minterms/sorties adéquats.
Fabriquer un circuit combinatoire avec un décodeur gaspille pas mal de portes logiques. En effet, le décodeur fournit tous les minterms possibles, alors que seule une minorité est réellement utilisée pour fabriquer le circuit combinatoire. Les minterms en trop correspondent à des paquets de portes NON et ET reliées entre elles, qui ne servent à rien. De plus, les minterms ne sont pas simplifiés. On ne peut pas utiliser les techniques vues dans les chapitres précédents pour simplifier les minterms et réduire le nombre de portes logiques utilisées. Le décodeur reste tel qu'il est, avec l'ensemble des minterms non-simplifiés. Mais la simplicité de conception du circuit reste un avantage dans certaines situations. Notamment, les circuits avec plusieurs bits de sortie sont faciles à fabriquer, notamment si les sorties partagent des minterms (si un minterm est présent dans l'équation de plusieurs sorties différentes, l'usage d'un décodeur permet de facilement factoriser celui-ci).
Ceci étant dit, passons à la conception d'un décodeur avec des portes logiques.

### L'intérieur d'un décodeur

On vient de voir que chaque sortie d'un décodeur correspond à son propre minterm, et que tous les minterms possibles sont représentés. Rappelons que chaque minterm est associé à un circuit qui compare l'entrée à une constante X, X dépendant du minterm. En combinant ces deux informations, on devine qu'un décodeur est simplement composé de comparateurs avec une constante que de minterms/sorties. Par exemple, si je prends un décodeur 7 vers 128, cela veut dire qu'on peut envoyer en entrée un nombre codé entre 0 et 127 et que chaque nombre aura son propre minterm associé : il y aura un minterm qui vérifie si l'entrée vaut 0, un autre vérifie si elle vaut 1, un autre qui vérifie si elle vaut 2, ... , un minterm qui vérifie si l'entrée vaut 126, et enfin un minterm qui vérifie si l'entrée vaut 127.
Pour reformuler d'une manière bien plus simple, on peut voir les choses comme suit. Si l'entrée du décodeur vaut N, la sortie mise à 1 est la sortie N. Bref, déduire quand mettre à 1 la sortie N est facile : il suffit de comparer l'entrée avec N. Si l'adresse vaut N, on envoie un 1 sur la sortie, et on envoie un zéro sinon. Pour cela, j'ai donc besoin d'un comparateur pour chaque sortie, et le tour est joué. Précisons cependant que cette méthode gaspille beaucoup de circuits et qu'il y a une certaine redondance. En effet, les comparateurs ont souvent des portions de circuits qui sont identiques et ne diffèrent parfois que ce quelques portes logiques. En utilisant des comparateurs séparés, ces portions de circuits sont dupliquées, alors qu'il serait judicieux de partager.
Comme autre méthode, plus économe en circuits, on peut créer un décodeur en assemblant plusieurs décodeurs plus simples, nommés sous-décodeurs. Ces sous-décodeurs sont des décodeurs normaux, auxquels on a ajouté une entrée RAZ, qui permet de mettre à zéro toutes les sorties : si on met un 0 sur cette entrée, toutes les sorties passent à 0, alors que le décodeur fonctionne normalement sinon. Construire un décodeur demande suffisamment de sous-décodeurs pour combler toutes les sorties. Si on utilise des sous-décodeurs à n entrées, ceux-ci prendront en entrée les n bits de poids faible de l'entrée du décodeur que l'on souhaite construire (le décodeur final). Dans ces conditions, les n décodeurs auront une de leurs sorties à 1. Pour que le décodeur final se comporte comme il faut, il faut désactiver tous les sous-décodeurs, sauf un avec l'entrée RAZ. Pour commander les n bits RAZ des sous-décodeurs, il suffit d'utiliser un décodeur qui est commandé par les bits de poids fort du décodeur final.

## Le démultiplexeur

Les décodeurs ont des cousins : les multiplexeurs et les démultiplexeurs. Un démultiplexeur a plusieurs sorties et une seule entrée. Les sorties sont numérotées de 0 à la valeur maximale. Il permet de sélectionner une sortie et de recopier l'entrée dessus, les autres sorties sont mises à 0. Pour séléctionner la sortie, le démultiplexeur possède une entrée de commande, sur laquelle on envoie le numéro de la sortie de destination. Comme le nom l'indique, le démultiplexeur fait l'exact inverse du multiplexeur, que nous verrons plus bas.

### Le démultiplexeur à deux sorties

Le démultiplexeur le plus simple est le démultiplexeur à deux sorties. Il possède une entrée de donnée, une entrée de commande et deux sorties, toutes de 1 bit. Suivant la valeur du bit sur l'entrée de commande, il recopie le bit d'entrée, soit sur la première sortie, soit sur la seconde. Les deux sorties sont numérotées respectivement 0 et 1.
On peut le concevoir facilement en partant de sa table de vérité.
| Entrée de commande Select | Entrée de donnée Input | Sortie 1 | Sortie 0 |
| 0 | 0 | 0 | 0 |
| 0 | 1 | 0 | 1 |
| 1 | 0 | 0 | 0 |
| 1 | 1 | 1 | 0 |
Le circuit obtenu est le suivant :

### Les démultiplexeurs à plus de deux sorties

Il est parfaitement possible de créer des démultiplexeurs en utilisant les méthodes du chapitre sur les circuits combinatoires, comme ma méthode des minterms ou les tableaux de Karnaugh. On obtient alors un démultiplexeur assez simple, composé de deux couches de portes logiques : une couche de portes NON et une couche de portes ET à plusieurs entrées.
Mais cette méthode n'est pas pratique, car elle utilise beaucoup de portes logiques et que les portes logiques avec beaucoup d'entrées sont difficiles à fabriquer. Pour contourner ces problèmes, on peut ruser. Ce qui a été fait pour les multiplexeurs peut aussi s'adapter aux démultiplexeurs : il est possible de créer des démultiplexeurs en assemblant des démultiplexeurs 1 vers 2. Évidemment, le même principe s'applique à des démultiplexeurs plus complexes : il suffit de rajouter des couches.
Un démultiplexeur peut aussi se fabriquer en utilisant un décodeur et quelques portes ET. Pour comprendre pourquoi, regardons la table de vérité d'un démultiplexeur à quatre sorties. Si vous éliminez le cas où l'entrée de donnée Input vaut 0, et que vous tenez compte uniquement des entrées de commande, vous retombez sur la table de vérité d'un décodeur. Cela correspond aux cases en rouge.
| Input | E0 | E1 | S0 | S1 | S2 | S3 |
| 0 | 0 | 0 | 0 | 0 | 0 | 0 |
| 0 | 0 | 1 | 0 | 0 | 0 | 0 |
| 0 | 1 | 0 | 0 | 0 | 0 | 0 |
| 0 | 1 | 1 | 0 | 0 | 0 | 0 |
| 1 | 0 | 0 | 1 | 0 | 0 | 0 |
| 1 | 0 | 1 | 0 | 1 | 0 | 0 |
| 1 | 1 | 0 | 0 | 0 | 1 | 0 |
| 1 | 1 | 1 | 0 | 0 | 0 | 1 |
En réalité, Le fonctionnement d'un démultiplexeur peut se résumer comme suit : soit l'entrée Input est à 1 et il fonctionne comme un décodeur dont l'entrée est l'entrée de commande, soit l'entrée Input vaut 0 et sa sortie est mise à 0. On devine donc qu'il faut combiner un décodeur avec le circuit de mise à zéro vu dans le chapitre précédent. On devine rapidement que l'entrée Input commande la mise à zéro de la sortie, ce qui donne le circuit suivant :

## Le multiplexeur

Les décodeurs ont des cousins : les multiplexeurs et les démultiplexeurs. Les multiplexeurs sont des composants qui possèdent un nombre variable d'entrées, mais une seule sortie. Un multiplexeur permet de sélectionner une entrée et de recopier son contenu sur sa sortie, les entrées non-sélectionnées étant ignorées. Sélectionner l'entrée à recopier sur la sortie se fait en configurant une entrée de commande du multiplexeur. Les entrées sont numérotées de 0 à la valeur maximale. Configurer l'entrée de commande demande juste d'envoyer le numéro de l'entrée sélectionnée dessus.
Les multiplexeurs sont très utilisés et on en retrouve partout : dans les mémoires RAM, dans les processeurs, dans les circuits de calcul, dans les circuits pour communiquer avec les périphériques, et j'en passe. Il s'agit d'un composant très utilisé, qu'il est primordial de bien comprendre avant de passer à la suite du cours.

### Le multiplexeur à deux entrées

Le multiplexeur le plus simple est le multiplexeur à deux entrées et une sortie. Il est facile de le construire avec des portes logiques, dans les implémentations les plus simples. Sachez toutefois que les multiplexeurs utilisés dans les ordinateurs récents ne sont pas forcément fabriqués avec des portes logiques, mais qu'on peut aussi les fabriquer directement avec des transistors.
Pour commencer, établissons sa table de vérité. On va supposer qu'un 0 sur l'entrée de commande sélectionne l'entrée a. La table de vérité devrait être la suivante :
| Entrée de commande | Entrée a | Entrée b | Sortie |
| 0 | 0 | 0 | 0 |
| 0 | 0 | 1 | 0 |
| 0 | 1 | 0 | 1 |
| 0 | 1 | 1 | 1 |
| 1 | 0 | 0 | 0 |
| 1 | 0 | 1 | 1 |
| 1 | 1 | 0 | 0 |
| 1 | 1 | 1 | 1 |
Sélectionnons les lignes qui mettent la sortie à 1 :
| Entrée de commande | Entrée a | Entrée b | Sortie |
| 0 | 1 | 0 | 1 |
| 0 | 1 | 1 | 1 |
| 1 | 0 | 1 | 1 |
| 1 | 1 | 1 | 1 |
On sait maintenant quels comparateurs avec une constante utiliser. On peut, écrire l'équation logique du circuit. La première ligne donne l'équation suivante : ${\overline {E_{c}}}.a.{\overline {b}}$, la seconde donne l'équation ${\overline {E_{c}}}.a.b$ , la troisième l'équation $E_{c}.{\overline {a}}.b$ et la quatrième l'équation $E_{c}.a.b$. L'équation finale obtenue est donc :
    $({\overline {E_{c}}}.a.{\overline {b}})+({\overline {E_{c}}}.a.b)+(E_{c}.{\overline {a}}.b)+E_{c}.a.b$
L'équation précédente est assez compliquée, mais il y a moyen de la simplifier assez radicalement. Pour cela, nous allons utiliser les règles de l’algèbre de Boole. Pour commencer, nous allons factoriser $({\overline {E_{c}}}$ et $E_{c}$ :
    $\left[{\overline {E_{c}}}.[(a.{\overline {b}})+(a.b)]\right]+\left[E_{c}.[({\overline {a}}.b)+(a.b)]\right]$
Ensuite, factorisons $a$ dans le premier terme et $b$ dans le second :
    $\left[{\overline {E_{c}}}.a.({\overline {b}}+b)\right]+\left[E_{c}.b.({\overline {a}}+a)\right]$
Les termes ${\overline {b}}+b$ et ${\overline {a}}+a$ valent 1 :
    $\left[{\overline {E_{c}}}.a.1\right]+\left[E_{c}.b.1\right]$
On sait que $a.1=a$, ce qui fait que l'équation simplifiée est la suivante :
    $({\overline {E_{c}}}.a)+(E_{c}.b)$
Le circuit qui correspond est :
Il est aussi possible de fabriquer un multiplexeur 2 vers 1 en utilisant des portes à transmission. L'idée est de relier chaque entrée à la sortie par l'intermédiaire d'une porte à transmission. Quand l'une sera ouverte, l'autre sera fermée. Le résultat n'utilise que deux portes à transmission et une porte NON. Voici le circuit qui en découle :

### Les multiplexeurs à plus de deux entrées

Il est possible de concevoir un multiplexeur quelconque à partir de sa table de vérité. Le résultat est alors un circuit composé d'une porte OU à plusieurs entrées, de plusieurs portes ET, et de quelques portes NON. Un exemple est illustré ci-dessous. Vous remarquerez cependant que ce circuit a un défaut : la porte OU finale a beaucoup d'entrées, ce qui pose de nombreux problèmes techniques. Il est difficile de concevoir des portes logiques avec un très grand nombre d'entrées. Aussi, les applications à haute performance demandent d'utiliser d'autres solutions.
Il existe toutefois une manière bien plus simple pour créer des multiplexeurs : il suffit d'utiliser un décodeur, quelques portes OU, et quelques portes ET. L'idée est de :
* sélectionner l'entrée à recopier sur la sortie ;
* mettre les autres entrées à zéro ;
* faire un OU entre toutes les entrées : vu que toutes les entrées non-sélectionnées sont à zéro, la sortie de la porte OU aura la même valeur que l'entrée sélectionnée.
Pour sélectionner l'entrée adéquate du multiplexeur, on utilise un décodeur : si la sortie n du décodeur est à 1, alors l'entrée numéro n du multiplexeur sera recopiée sur sa sortie. Dans ces conditions, l'entrée de commande du multiplexeur correspond à l'entrée du décodeur. Pour mettre à zéro les entrées non-sélectionnées, on adapte le circuit de mise à zéro précédent, basé sur une couche de portes ET, sauf que chaque porte ET est connectée à sa propre entrée. En fait, les entrées forment un nombre, tous les bits sauf un doivent être mis à 0. Pour cela, on applique un masque calculé par le décodeur.
Il est possible de remplacer les portes ET par des portes à transmission. Remplacer les portes ET par des portes à transmission permet de se passer de la porte OU, qui est remplacée par un simple fil. Il n'y a qu'une seule entrée qui est connectée à la sortie à chaque instant, pas besoin d'utiliser de porte OU. Le résultat est le circuit suivant :
Une solution alternative est de concevoir un multiplexeur à plus de deux entrées en combinant des multiplexeurs plus simples. Par exemple, en prenant deux multiplexeurs plus simples, et en ajoutant un multiplexeur 2 vers 1 sur leurs sorties respectives. Le multiplexeur final se contente de sélectionner une sortie parmi les deux sorties des multiplexeurs précédents, qui ont déjà effectué une sorte de présélection.

## L'encodeur

Il existe un circuit qui fait exactement l'inverse du décodeur : c'est l'encodeur. Là où les décodeurs ont une entrée de $N$ bits et $2^{N}$ sorties de 1 bit, l'encodeur a à l'inverse $2^{N}$ entrées de 1 bit avec une sortie de $N$ bits. Par exemple, un encodeur avec une entrée de 4 bits aura 2 sorties, un décodeur avec une entrée de 8 bits aura 3 sorties, un décodeur avec une entrée de 256 bits aura 8 sorties, etc. Comme pour les décodeurs, on parle d'un encodeur X vers Y pour X bits d'entrée et Y de sortie. Ce qui fait qu'on peut parler de décodeur 8 vers 3 pour un décodeur à 8 bits d'entrée et 3 de sortie, de décodeur 16 vers 4, etc.
De plus, contrairement au décodeur, ce sont les entrées qui sont numérotées de 0 à N et non les sorties. Dans ce qui suit, on va supposer qu'une seule des entrées est à 1. Il existe des encodeurs capables de traiter le cas où plusieurs bits d'entrée sont à 1, qui sont appelés des encodeurs à priorité, mais nous les laissons pour le chapitre suivant. Le chapitre suivant sera totalement dédié aux encodeurs à priorité, aussi nous préférons nous focaliser sur le cas d'un encodeur simple, capable de traiter uniquement le cas où une seule entrée est à 1. En sortie, l'encodeur donne le numéro de l'entrée qui est à 1. Par exemple, si l'entrée numéro 5 est à 1 et les autres à 0, alors l'encodeur envoie un 5 sur sa sortie.
Une autre manière d'expliquer son fonctionnement est la suivant : un encodeur traduit un nombre codé en représentation one-hot vers du binaire normal.
L'utilité d'un encodeur n'est pas très évidente à ce moment du cours, mais nous pouvons déjà dire qu'ils seront utiles dans certaines formes de mémoires RAM appelées des mémoires associatives, qui sont utilisées dans des routeurs, switchs et autre matériel réseau. La majorité des mémoires caches de nos ordinateurs sont de ce type, bien que leur implémentation exacte ne fasse pas usage d'un encodeur. Une autre utilisation est la transformation d'un nombre codé en représentation one-hot vers du binaire normal, chose marginalement utile.

### L'encodeur 4 vers 2

Prenons l'exemple d'un encodeur à 4 entrées et 2 sorties. Écrivons sa table de vérité. D'après la description du circuit, on devrait trouver ceci :
Table de vérité d'un encodeur 4 vers 2 :
| E3 | E2 | E1 | E0 | S1 | S0 |
| 0 | 0 | 0 | 1 | 0 | 0 |
| 0 | 0 | 1 | 0 | 0 | 1 |
| 0 | 1 | 0 | 0 | 1 | 0 |
| 1 | 0 | 0 | 0 | 1 | 1 |
Vous voyez que la table de vérité est incomplète. En effet, l'encodeur fonctionne tant qu'une seule de ses entrées est à 1. L'encodeur dit alors quelle est la sortie à 1, mais cela suppose que les autres soient à 0. Si plusieurs entrées sont à 1, le comportement de l'encodeur est potentiellement erroné. En effet, il donnera un résultat incorrect sur certaines entrées. Mais passons cela sous silence et ne tenons compte que de la table de vérité partielle précédente. On peut traduire cette table de vérité en circuit logique. On obtient alors les équations suivantes :
    $S1=E3+E2$
    $S0=E3+E1$
Le tout donne le circuit suivant :

### Les encodeurs à plus de deux sorties

Il est possible de créer un encodeur complexe en combinant plusieurs encodeurs simples. C'est un peu la même chose qu'avec les décodeurs, pour lesquels on peut créer un décodeur 8 vers 256 à base de deux décodeurs 7 vers 128, ou de quatre décodeurs 6 vers 64. L'idée de découper le nombre d'entrée en morceaux séparés, chaque morceau étant traité par un encodeur à priorité distinct des autres. Les résultats des différents encodeurs sont ensuite combinés pour donner le résultat final.
Pour comprendre l'idée, prenons la table de vérité d'un encodeur 8 vers 3; donnée dans le tableau ci-dessous.
Table de vérité d'un encodeur 8 vers 3 :
| E7 | E6 | E5 | E4 | E3 | E2 | E1 | E0 | S2 | S1 | S0 |
| 0 | 0 | 0 | 0 | 0 | 0 | 0 | 1 | 0 | 0 | 0 |
| 0 | 0 | 0 | 0 | 0 | 0 | 1 | 0 | 0 | 0 | 1 |
| 0 | 0 | 0 | 0 | 0 | 1 | 0 | 0 | 0 | 1 | 0 |
| 0 | 0 | 0 | 0 | 1 | 0 | 0 | 0 | 0 | 1 | 1 |
| 0 | 0 | 0 | 1 | 0 | 0 | 0 | 0 | 1 | 0 | 0 |
| 0 | 0 | 1 | 0 | 0 | 0 | 0 | 0 | 1 | 0 | 1 |
| 0 | 1 | 0 | 0 | 0 | 0 | 0 | 0 | 1 | 1 | 0 |
| 1 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 1 | 1 | 1 |
En regardant bien, vous verrez que vous pouvez trouver la table de vérité d'un encodeur 4 vers 2 en deux exemplaires, indiquées en rouge.
Table de vérité d'un encodeur 8 vers 3 :
| E7 | E6 | E5 | E4 | E3 | E2 | E1 | E0 | S2 | S1 | S0 |
| 0 | 0 | 0 | 0 | 0 | 0 | 0 | 1 | 0 | 0 | 0 |
| 0 | 0 | 0 | 0 | 0 | 0 | 1 | 0 | 0 | 0 | 1 |
| 0 | 0 | 0 | 0 | 0 | 1 | 0 | 0 | 0 | 1 | 0 |
| 0 | 0 | 0 | 0 | 1 | 0 | 0 | 0 | 0 | 1 | 1 |
| 0 | 0 | 0 | 1 | 0 | 0 | 0 | 0 | 1 | 0 | 0 |
| 0 | 0 | 1 | 0 | 0 | 0 | 0 | 0 | 1 | 0 | 1 |
| 0 | 1 | 0 | 0 | 0 | 0 | 0 | 0 | 1 | 1 | 0 |
| 1 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 1 | 1 | 1 |
On voit que les deux bits de poids faibles correspondent à la sortie de l'encodeur activé par l'entrée. Si le premier encodeur est activé, c'est lui qui fournit les bits de poids faibles. Inversement, si c'est le second encodeur qui a un résultat non-nul, c'est lui qui fournit les bits de poids faible. Notons que seul un des deux encodeurs a une sortie non-nulle à la fois : soit le premier a une sortie non-nulle, soit c'est le second, mais c'est impossible que ce soit les deux en même temps. Cela permet de déduire quelle opération permet de mixer les deux résultats : un simple OU logique suffit. Car, pour rappel, 0 OU X donne X, quelque que soit le X en question. Les bits de poids faible du résultat se calculent en faisant un OU entre les deux résultats des encodeurs.
Ensuite, il faut déterminer comment fixer le bit de poids fort du résultat. Il vaut 0 si le premier encodeur a une entrée non-nulle, et 1 si c'est le premier encodeur qui a une entrée non-nulle. Pour cela, il suffit de vérifier si les bits de poids forts, associés au premier encodeur, contiennent un 1. Si c'est le cas, alors on met la troisième sortie à 1.
Notons que cette procédure, à savoir faire un OU entre les sorties de deux encodeurs simples, puis faire un OU pour calculer le troisième bit, marche pour tout encodeur de taille quelconque. À vrai dire, le circuit obtenu plus haut d'un encodeur 4 vers 2 est conçu ainsi, mais en combinant deux encodeurs 2 vers 1.
La procédure consiste à ajouter trois portes OU à deux encodeurs. Mais ceux-ci sont eux-même composés de portes OU associées à des encodeurs plus petits, et ainsi de suite. On peut poursuivre ainsi jusqu’à tomber sur des encodeurs 4 vers 2, qui sont eux-mêmes composés de deux portes OU. Au final, on se retrouve avec un circuit conçu uniquement à partir de portes OU. Notons qu'il est possible de simplifier le circuit obtenu avec la procédure en fusionnant des portes OU. Si on simplifie vraiment au maximum, le circuit consiste alors en une porte OU à plusieurs entrées par sortie, chacune étant connectée à certaines entrées bien précises. Pour un encodeur 8 vers 3, la simplification du circuit devrait donner ceci :

## L'encodeur à priorité

L'encodeur à priorité est un dérivé du circuit encodeur, vu dans la section précédente. La différence ne se situe pas dans le nombre d'entrée ou de sortie, ni même dans son interface extérieure. Comme pour l'encodeur normal, l'encodeur à priorité possède $2^{N}$ entrées numérotées de 0 à $2^{N}-1$ et N sorties. Une autre manière plus intuitive de le dire est qu'il possède N entrées et $\log _{2}{N}$ sorties. Pas de changement de ce point de vue.
La différence entre encodeur simple et encodeur à priorité tient dans leur fonctionnement, dans le calcul qu'ils font. Avec un encodeur normal, on a supposé que seul un bit d'entrée pouvait être à 1, les autres étant systématiquement à 0. Si cette condition est naturellement remplie dans certains cas d’utilisation, ce n'est pas le cas dans d'autres. L'encodeur à priorité est un encodeur amélioré dans le sens où il donne un résultat valide même quand plusieurs bits d'entrée sont à 1. Il donne donc un résultat pour n'importe quel nombre passé en entrée.
Mais avant de passer aux explications, un peu de terminologie utile. Dans ce qui suit, nous aurons à utiliser des expressions du type "le 1 de poids faible", "le 1 de poids fort" et quelques autres du même genre. Quand nous parlerons du 1 de poids faible, nous voudrons parler du premier 1 que l'on croise dans un nombre en partant de sa droite. Par exemple, dans le nombre 0110 1000, le 1 de poids faible est le quatrième bit. Quant au "1 de poids fort", c'est le premier 1 que l'on croise quand on parcourt le nombre à partir de sa gauche. Dans le cas le plus fréquent, l'encodeur à priorité prend en entrée un nombre et donne la position du 1 de poids fort. Mais dans d'autres cas, l'encodeur à priorité donne la position du 1 de poids faible. Il existe des équivalents, mais qui trouvent cette fois-ci les zéros de poids fort/faible, mais nous n'en parlerons pas dans ce chapitre.

### L'encodeur à priorité conçu à partir de sa table de vérité

Il est possible de concevoir l'encodeur à priorité à partir de sa table de vérité, mais les méthodes des minterms ou des maxterms ne donnent pas de très bons résultats.
Notons que ces encodeurs ont souvent une nouvelle entrée notée V, qui indique si la sortie est valide, et qui indique qu'au moins une entrée est à 1. Elle vaut 1 si au moins une entrée est à 1, 0 si toutes les entrées sont à 0.
À titre d'exemple, la table de vérité d'un encodeur à priorité 4 vers 2 est illustré ci-dessous. Le signe X signifie que le bit peut prendre la valeur 0 ou 1 sans que cela change quoique ce soit à l'entrée.
| E3 | E2 | E1 | E0 | S1 | S0 | V |
| 0 | 0 | 0 | 0 | 0 | 0 | 0 |
| 0 | 0 | 0 | 1 | 0 | 0 | 1 |
| 0 | 0 | 1 | X | 0 | 1 | 1 |
| 0 | 1 | X | X | 1 | 0 | 1 |
| 1 | X | X | X | 1 | 1 | 1 |
Les équations logiques obtenues sont donc les suivantes :
    $V=E3+E2+E1+E0$
    $S0=E3+({\overline {E3}}.{\overline {E2}}.E1)$
    $S1=E3+({\overline {E3}}.E2)$
On voit quelle est la logique de chaque équation. Pour chaque ligne de la table de vérité, il faut vérifier si les bits de poids fort sont à 0, suivi par un 1, les bits de poids faible après le 1 étant oubliées. Pour le bit de validité, il suffit de faire un OU entre toutes les entrées. Les deux dernières équations se simplifient en :
    $S0=E3+({\overline {E2}}.E1)$
    $S1=E3+E2$,
Le circuit obtenu est le suivant :
La table de vérité d'un encodeur à priorité 8 vers 3 est illustré ci-dessous. Le signe X signifie que le bit peut prendre la valeur 0 ou 1 sans que cela change quoique ce soit à l'entrée.
Utiliser la table de vérité a des défauts. Premièrement, ce n'est pas la meilleure des solutions pour des circuits avec un grand nombre d'entrée. Faire cela donne des tables de vérité rapidement importantes, mêmes pour des encodeurs avec peu de sorties. Le circuit final utilise beaucoup de portes logiques comparé aux autres méthodes. Les solutions alternatives que nous allons voir dans ce qui suit permettent de résoudre ces deux problèmes en même temps.

### Les encodeurs à priorité récursifs

Une première solution consiste à créer un gros encodeur à base d'encodeurs plus petits.L'idée de découper le nombre d'entrée en morceaux séparés, chaque morceau étant traité par un encodeur à priorité distinct des autres. Les résultats des différents encodeurs sont ensuite combinés pour donner le résultat final. Naturellement, il est préférable d'utiliser plusieurs exemplaires d'un même encodeur, c'est à dire que pour une entrée de 256 bits, il vaut mieux utiliser soit deux décodeurs 7 vers 128, soit quatre décodeurs 6 vers 64, etc. La construction est similaire à celle vue dans le chapitre précédent, dans la section sur les encodeurs. La différence est que le OU entre les sorties des encodeurs est remplacé par un multiplexeur. Une version générale est illustrée ci-dessous. On voit que les encodeurs ont une sortie de résultat de X bits notée idx et une sortie de validité notée vld.
La sortie de validité finale se calcule en combinant les sorties de validité de chaque encodeur. La sortie est par définition à 1 tant qu'un seul encodeur a une sortie non-nulle, donc quand un seul encodeur a un bit de validité à 1. En clair, c'est un simple OU entre les bits de validité. Reste à déterminer la sortie de donnée, celle qui donne la position du 1 de poids fort. On peut dire que si l'on utilise des encodeurs avec N bits de sortie, alors les N bits de poids faible du résultat seront donnés par le premier encodeur avec une sortie non-nulle. Les résultats de chaque encodeur donnent doncles X bits de poids faible, un seul résultat devant être sélectionné. Le résultat à sélectionner est le premier à avoir un résultat non-nul, donc à avoir un bit de validité à 1. En clair, on peut déterminer quel est le bon encodeur, le bon résultat, en analysant les bits de validité. Mieux : d'après ce qui a été dit, on peut deviner que l'analyse réalisée correspond à trouver la position du premier encodeur à avoir un bit de validité à 1. En clair, c'est l'opération réalisée par un encodeur à priorité lui-même.
Tout cela permet de déterminer les N bits de poids faible, amis les autres bits, ceux de poids fort, sont encore à déterminer. Pour cela, on peut remarquer que ceux-ci sont eux-même fournit par l'encodeur à priorité qui commande le MUX.
Notons qu'avec cette méthode, il est possible, mais pas très intuitif, de fabriquer un encodeur configurable, capable de se comporter soit comme un encodeur de type Find Highest Set, soit de type Find First Set. L'implémentation la plus simple demande de modifier le circuit qui combine les résultats pour qu'il soit configurable et puisse faire les deux opérations à la demande.

### L'encodeur à priorité avec un circuit d'isolation du 1 de poids fort/faible

Une autre solution part d'un encodeur normal, auquel on ajoute un circuit qui se charge de sélectionner un seul des bits passé sur son entrée. Le circuit de gestion des priorités a pour fonction de trouver sélectionner un bit et de mettre les autres 1 à 0. Suivant le circuit de priorité considéré, le bit sélectionné est soit le 1 de poids fort, soit le 1 de poids faible. Dans certains cas, le circuit de priorité est configurable et peut trouver l'un ou l'autre suivant ce qu'on lui demande. Dans ce qui va suivre, nous allons partir du principe que l'on souhaite avoir un encodeur qui trouve le 1 de poids fort, sauf indication contraire.
Une méthode assez pratique découpe le circuit de gestion des priorité en petites briques de bases, reliées les unes à la suite des autres. L'idée est que les briques de base sont connectées de manière à propager un signal de mise à zéro. Si une brique détecte un 1, elle envoie un signal aux briques précédentes/suivantes, qui leur dit de mettre leur sortie à zéro. Ce faisant, une fois le premier 1 trouvé, on est certain que les autres bits précédents/suivants sont mis à zéro. Suivant les connexions des briques de base, on peut obtenir soit un encodeur qui effectue l'opération Find First Set, soit encodeur de type Find Highest Set et réciproquement. En fait, suivant que les briques soient reliées de droite à gauche ou de gauche à droite, on obtiendra l'un ou l'autre de ces deux encodeurs.
Chaque brique de base peut soit recopier le bit en entrée, soit le mettre à zéro. Pour décider quoi faire, elle regarde le signal d'entrée RAZ (Remise A Zéro). Si le bit RAZ vaut 1, la sortie est mise à zéro automatiquement. Dans le cas contraire, le bit passé en entrée est recopié. De plus, chaque brique de base doit fournir un signal de remise à zéro RAZ à destination de la brique suivante. Ce signal RAZ de sortie est mis à 1 dans deux cas : soit si le bit d'entrée vaut, soit quand le signal d'entrée RAZ est à 1. Si vous cherchez à la concevoir à partir d'un table de vérité, vous obtiendrez ceci :
| Brique de base du circuit de gestion des priorités d'un encodeur à priorité. | Circuit de gestion des priorité - Circuit de la brique de base. |
Le circuit complet d'un encodeur à priorité peut être déduit facilement à partir des raisonnements précédents. Après quelques simplifications, on peut obtenir le circuit suivant. On voit qu'on a ajouté une ligne de briques RAZ à l'encodeur 8 vers 3 vu plus haut.
Le défaut de cette méthode est que le circuit de gestion des priorité est assez lent. Dans le pire des cas, le signal de remise à zéro traverse toutes les briques de base, soit autant qu'il y a de bits d'entrée. Si chaque brique de base met un certain temps, le temps mis pour que le circuit de priorité fasse son travail est proportionnel au nombre de bits de l'entrée. Cela n'a l'air de rien, mais cela peut prendre un temps rédhibitoire pour les circuits de haute performance, destinés à fonctionner à haute fréquence. Pour ces circuits, on préfère que le temps de calcul soit proportionnel au logarithme du nombre de bits d'entrée, un temps proportionnel étant considéré comme trop lent, surtout pour des opérations simples comme celles étudiées ici.
Une version légèrement différente de ce circuit est utilisée dans le processeur ARM1, un des tout premiers processeur ARM. L'encodeur à priorité était bidirectionnel, à savoir capable de déterminer soit la place du 1 de poids faible, soit du 1 de poids fort. Pour ceux qui veulent en savoir plus, et qui ont déjà un bagage solide en architecture des ordinateurs, voici un lien à ce sujet :
    More ARM1 processor reverse engineering: the priority encoder