Aller au contenu

Fonctionnement d'un ordinateur/Version imprimable 2

Un livre de Wikilivres.
Circuit électronique.

Un ordinateur est un appareil électronique parmi tant d'autres. La conception de ces appareils est un domaine appelé l’électronique et les gens qui conçoivent ces appareils sont appelés des électroniciens. Tous les appareils électroniques contiennent plusieurs composants électroniques simples, qui sont placés sur un support plat, en plastique ou en céramique, appelé la carte électronique. Les composants sont soudés sur la carte, histoire qu’ils ne puisse pas s’en décrocher. Ils sont reliés entre eux par des fils conducteurs, le plus souvent du cuivre ou de l’aluminium, ce qui leur permet de s’échanger des données. Sur les cartes simples, ces fils sont intégrés dans la carte électroniques, dans des creux du plastique. Ils portent le nom de pistes. Évidemment, tous les composants ne sont pas tous reliés entre eux. Par exemple, si je prends un ordinateur, l’écran n’est pas relié au clavier.

Les composants d'un ordinateur de type PC

[modifier | modifier le wikicode]

De l'extérieur, l'ordinateur est composé d'une unité centrale sur laquelle on branche des périphériques.

Les périphériques regroupent l'écran, la souris, le clavier, l'imprimante, et bien d'autres choses. Ils permettent à l'utilisateur d'interagir avec l'ordinateur : un clavier permet de saisir du texte sous dans un fichier, une souris enregistre des déplacements de la main en déplacement du curseur, un écran affiche des données d’images/vidéos, un haut-parleur émet du son, etc. Tout ce qui est branché sur un ordinateur est, formellement un périphérique.

Exemples de périphériques, connéctés à une unité centrale (ici appelée à tord operating system).
Éclaté d'un ordinateur de type PC :
1 : Écran ;
2 : Carte mère ;
3 : Processeur ;
4 : Câble Parallel ATA ;
5 : Mémoire vive (RAM) ;
6 : Carte d'extension ;
7 : Alimentation électrique ;
8 : Lecteur de disque optique ;
9 : Disque dur ;
10 : Clavier ;
11 : Souris.

L'unité centrale est là où se trouvent tous les composants importants d'un ordinateur, ceux qui font des calculs, qui exécutent des logiciels, qui mémorisent vos données, etc. Dans les ordinateurs portables, l'unité centrale est bien là, située sous le clavier ou dans l'écran (le plus souvent sous le clavier). À l'intérieur de l'unité centrale, on trouve trois composants principaux : le processeur, la mémoire et les entrée-sorties.

  • Le processeur traite les données, les modifie, les manipule. Pour faire simple, il s’agit d’une grosse calculatrice hyper-puissante. Il comprend à la fois un circuit qui fait des calculs, et un circuit de contrôle qui s'occupe de séquencer les calculs dans l'ordre demandé.
  • La mémoire vive conserve des informations/données temporairement, tant que le processeur en a besoin.
  • La carte mère n'est autre que le circuit imprimé, la carte électronique, sur laquelle sont soudés les autres composants.

Outre ces composants dits principaux, un ordinateur peut comprendre plusieurs composants moins importants, surtout présents sur les ordinateurs personnels. Ils sont techniquement facultatifs, mais sont très présents dans les ordinateurs personnels. Cependant, certains ordinateurs spécialisés s'en passent. Les voici :

  • L'alimentation électrique convertit le courant de la prise électrique en un courant plus faible, utilisable par les autres composants.
  • Diverses cartes d'extension sont branchées sur la carte mère. Elles permettent d’accélérer certains calculs ou certaines applications, afin de décharger le processeur. Par exemple, la carte graphique s'occupe des calculs graphiques, qu'il s'agisse de graphismes 3D de jeux vidéos ou de l'affichage en 2D du bureau. Dans un autre registre, la carte son prend en charge le microphone et les haut-parleurs.
  • Les disques durs sont des mémoires de stockage, qui mémorisent vos données de manière permanente.
  • Les lecteurs CD-ROM ou DVD-ROM permettent de lire des CD ou des DVD.
  • Et ainsi de suite.

Les appareils électroniques programmables et non-programmables

[modifier | modifier le wikicode]

Un ordinateur comprend donc une unité centrale sur laquelle on connecte des périphériques. Et l'unité centrale contient un processeur, plusieurs mémoires, des cartes d'extension et une carte mère pour connecter le tout. Mais il s'agit là d'une description assez terre-à-terre de ce qu'est un ordinateur. Une autre description, plus générale, se base sur le fait qu'un ordinateur est une énorme calculatrice programmable. Elle permet d'expliquer pourquoi il y a une distinction entre processeur et mémoire, entre unité centrale et périphériques.

La séparation entre entrées-sorties et traitement

[modifier | modifier le wikicode]

L'unité centrale peut être vue comme une énorme calculatrice ultra-puissante, qui exécute des commandes/opérations. Mais à elle seule, elle ne servirait à rien, il faut interagir avec par l'intermédiaire de plusieurs périphériques. Il existe deux types de périphériques, qui sont conceptuellement différents. Les périphériques comme le clavier ou la souris permettent d'envoyer des informations à l'ordinateur, d'agir sur celui-ci. A l'inverse, les écrans transmettent des informations dans le sens inverse : de l'ordinateur vers l'utilisateur. Les premiers sont appelés des entrées, les seconds des sorties.

Un ordinateur reçoit des informations sur des entrées, effectue des opérations/traitements dessus, puis envoie le résultat sur ses sorties. Les informations sont représentées sous la forme de nombres. Toute donnée dans un ordinateur est codée avec un ou plusieurs nombres regroupés dans une donnée, un fichier, ou autre. Par exemple, quand vous appuyez sur votre clavier, le clavier envoie un numéro de touche à l'unité centrale, qui indique quelle touche a été appuyée. L'ordinateur effectue alors des traitements, et détermine quoi afficher à l'écran. L'image à afficher à l'écran est codée sous la forme d'une suite de nombres (un par pixel). L'image est envoyée à l'écran, qui traduit la suite de nombre en image à afficher.

Les entrées traduisent des actions utilisateurs en nombres, qui sont ensuite traités par l'ordinateur. On dit que les entrées encodent les actions utilisateur. Les sorties font la traduction inverse, elles transforment des suites de nombres en une action physique. On dit qu'elles décodent des informations. Pour résumer, toute appareil électronique est composé par :

  • Des entrées sur lesquelles l'utilisateur agit sur l'ordinateur. Les entrées transforment les actions de l'utilisateur en nombres, qui sont interprétés par l'ordinateur.
  • Une unité de traitement, qui manipule des nombres et fait des calculs/opérations dessus. Les nombres proviennent des entrées, du disque dur, ou d'autres sources, peu importe. L'unité de traitement effectue des calcul et mémorise des nombres.
  • Des sorties, qui va récupèrent le résultat calculé par l'unité de traitement pour en faire quelque chose : écrire sur une imprimante ou sur un moniteur, émettre du son,...
Ordinateur théorique Simple 1

L'unité centrale d'un ordinateur est découpée en deux grands composants. Une unité de traitement proprement dite qui fait les calculs, une mémoire qui mémorise les opérandes et résultats des calculs. La séparation entre processeur et mémoire est nécessaire pour qu'un appareil électronique soit qualifié d'ordinateur. De nombreux appareils n'ont pas de séparation entre unité de traitement et mémoire, comme certaines vielles radios AM/FM.

Schéma de principe d'un ordinateur

Un ordinateur est un appareil programmable

[modifier | modifier le wikicode]

Les appareils simples sont non-programmables, ce qui veut dire qu’ils sont conçus pour une utilisation particulière et qu’ils ne peuvent pas faire autre chose. Par exemple, les circuits électroniques d’un lecteur de DVD ne peuvent pas être transformé en lecteur audio ou en console de jeux... Les circuits non-programmables sont câblés une bonne fois pour toute, et on ne peut pas les modifier. On peut parfois reconfigurer le circuit, pour faire varier certains paramètres, via des interrupteurs ou des boutons, mais cela s’arrête là. Et cela pose un problème : à chaque problème qu'on veut résoudre en utilisant un automate, on doit recréer un nouveau circuit.

À l'inverse, un ordinateur n’est pas conçu pour une utilisation particulière, contrairement aux autres objets techniques. Il est possible de modifier leur fonction du jour au lendemain, on peut lui faire faire ce qu’on veut. On dit qu'ils sont programmables. Pour cela, il suffit d’utiliser un logiciel, une application, un programme (ces termes sont synonymes), qui fait ce que l'on souhaite. La totalité des logiciels présents sur un ordinateur sont des programmes comme les autres, même le système d'exploitation (Windows, Linux, ...) ne fait pas exception.

Un programme est une suite de d'instructions, chaque instruction effectuant une action dans l'ordinateur. Sur les ordinateurs modernes, la majorité de ces instructions effectuent une opération arithmétique, comme une addition, une multiplication, une soustraction, etc. Les ordinateurs modernes sont donc de grosses calculettes très puissantes, capables d'effectuer des millions d'opérations par secondes. Et qui dit opération dit nombres : un ordinateur gère nativement des nombres, qui sont codés en binaire sur la quasi-totalité des ordinateurs modernes.

Une instruction est représentée dans un ordinateur par une série de nombres : un nombre qui indique quelle opération/commande effectuer et des autres nombres pour coder les données (ou de quoi les retrouver dans l'ordinateur). L'ordinateur récupére les commandes une par une, les traduit en opération à effectuer, exécuter l'opération, et enregistre le résultat. Puis il recommence avec la commande suivante.

Ordinateur Théorique Complexe. En jaune, le programme informatique, la liste de commande. Le rectangle gris est l'ordinateur proprement dit.

Les ordinateurs à programme mémorisé ou programme externe

[modifier | modifier le wikicode]

Un programme informatique est une suite de commande, à exécuter dans l'ordre, l'une après l'autre. Les premiers ordinateurs mémorisaient les programmes sur un support externe, lu par un périphérique. Au tout début de l'informatique, on utilisait des cartes perforées en plastique, sur lesquelles on inscrivait le programme. Par la suite, les premiers ordinateurs grand public, comme les Amstrad ou les Commodore, utilisaient des cassettes audio magnétiques pour stocker les programmes. De même, les consoles de jeu utilisaient autrefois des cartouches de jeu, qui contenaient le programme du jeu à exécuter. Les anciens PC utilisaient des disquettes, de petits supports magnétiques qu’on insérait dans l’ordinateur le temps de leur utilisation. De tels ordinateurs sont dits à programme externe.

Sur d’anciens ordinateurs personnels, comme l’Amstrad ou le Commodore, on pouvait aussi taper les programmes à exécuter à la main, au clavier, avant d’appuyer une touche pour les exécuter.

Mais de nos jours, les programmes sont en réalité enregistrés sur le disque dur de l'ordinateur ou dans une mémoire intégrée à l'ordinateur. On dit qu'ils sont installés, ce qui est un mot bien compliqué pour dire que le programme est enregistré sur le disque dur de l’ordinateur. A défaut de disque dur, le programme/logiciel est enregistré dans une mémoire spécialisée pour le stockage. Par exemple, sur les cartes électroniques grand public de marque Arduino, les programmes sont envoyés à la carte via le port USB, mais les programmes sont enregistrés dans la carte Arduino, dans une mémoire FLASH dédiée. Disque dur ou non, le programme est mémorisé dans la mémoire de l'ordinateur. Il est possible d'installer ou de désinstaller les programmes en modifiant le contenu de la mémoire. Le terme utilisé est alors celui de programme stocké en mémoire.

Les programmes à installer sont disponibles soit sur un périphérique, comme un DVD ou une clé USB, soit sont téléchargés depuis internet. Les premiers PC fournissaient les logiciels sur des disquettes, qui contenaient un programme d'installation pour enregistrer le programme sur le disque dur de l'ordinateur. Par la suite, le support des logiciels a migré vers les CD et DVD, les logiciels devenant de plus en plus gros. De nos jours, la majorité des applications sont téléchargées depuis le net, l'usage de périphériques est devenu obsolète, même les consoles de jeu abandonnent cette méthode de distribution.

De nombreux appareils sont programmables, mais tous ne sont pas des ordinateurs. Par exemple, certains circuits programmables nommés FPGA n'en sont pas. Pour être qualifié d'ordinateur, un appareil programmable doit avoir d'autres propriétés. L'une d'entre elle est d'avoir un processeur séparé de la mémoire. Pour résumer, les ordinateurs sont des appareils électroniques qui ont les propriétés suivantes.

  • Ils sont programmables.
  • Ils contiennent un processeur et une mémoire, reliés à des périphériques.
  • Ils utilisent un codage numérique, chose que nous allons aborder dans le chapitre suivant.

Ces points peuvent paraitre assez abstraits, mais rassurez-vous : nous allons les détailler tout au long de ce cours, au point qu'ils n'auront plus aucun secret pour vous d'ici quelques chapitres.

L'organisation du cours : les niveaux d'abstractions en architecture des ordinateurs

[modifier | modifier le wikicode]

Le fonctionnement d'un ordinateur est assez complexe à expliquer car les explications peuvent se faire sur plusieurs niveaux. Par plusieurs niveaux, on veut dire qu'un ordinateur est composé de composants très simples, qui sont assemblés pour donner des composants eux-même plus complexes, qui sont eux-même regrouper, etc. Étudier tout cela demande de voir plusieurs niveaux, allant de transistors très petits à des processeurs multicœurs. Les niveaux les plus bas sont de l'électronique pur et dure, alors que ceux plus haut sont à mi-chemin entre électronique et informatique.

Les niveaux d'abstraction en architecture des ordinateurs

[modifier | modifier le wikicode]

Les trois premiers niveaux sont de l'électronique pur et dure. Ils correspondent aux premiers chapitres du cours, qui porteront sur les circuits électroniques en général.

  • Le premier niveau est celui des transistors, des circuits intégrés, des wafer et autres circuits de très petite taille.
  • Le second niveau est celui des portes logiques, des circuits très basiques, très simples, à la base de tous les autres.
  • Le troisième niveau est celui dit de la Register Transfer Level, où un circuit électronique est construit à partir de circuits basiques, dont des registres et autres circuits dits combinatoires.

Les deux niveaux suivants sont de l'informatique proprement dit. C'est dans ces deux niveaux qu'on étudie les ordinateurs proprement dit, les circuits qu'il y a dedans et non l'électronique en général.

  • Le quatrième niveau est celui de la microarchitecture, qui étudie ce qu'il y a à l'intérieur d'un processeur, d'une mémoire, des périphériques et autres.
  • Le cinquième niveau est celui de l'architecture externe, qui décrit l'interface du processeur, de la mémoire, d'un périphérique ou autre. Par décrire l'interface, on veut dire : comment un programmeur voit le processeur et la mémoire, comment il peut les manipuler. Une architecture externe unique peut avoir plusieurs microarchitecture, ce qui fait qu'on sépare les deux. Tout cela sera plus clair quand on passera aux chapitres sur le processeur et les mémoires.

Nous n'allons pas voir les 5 niveaux dans l'ordre, des transistors vers l'architecture externe. En réalité, nous allons procéder autrement. La première partie du cours portera sur les trois premiers niveaux, le reste sur les deux autres. Les 5 niveaux seront vus dans des chapitres séparés, du moins le plus possible. Au niveau pédagogique, tout est plus simple si on scinde les 5 niveaux. Bien sûr, il y a quelques explications qui demandent de voir plusieurs niveaux à la fois. Par exemple, dans le chapitre sur les mémoires caches, nous auront des explications portant sur la RTL, la microarchitecture du cache et son architecture externe. Mais le gros du cours tentera de séparer le plus possible les 5 niveaux.

Les trois parties principales du cours

[modifier | modifier le wikicode]

Nous allons commencer par parler du binaire, avant de voir les portes logiques. Avec ces portes logiques, nous allons voir comment fabriquer des circuits basiques qui reviendront très souvent dans la suite du cours. Nous verrons les registres, les décodeurs, les additionneurs et plein d'autres circuits. Puis, nous reviendrons au niveau des transistors pour finir la première partie. La raison est que c'est plus simple de faire comme cela. Tout ce qui a trait aux transistors sert à expliquer comment fabriquer des portes logiques, et il faut expliquer les portes logiques pour voir le niveau de la RTL.

Une fois la première partie finie, nous allons voir les différents composants d'un ordinateur. Une première partie expliquera ce qu'il y a dans un ordinateur, quels sont ses composants. Nous y parlerons de l'architecture de base, de la hiérarchie mémoire, des tendances technologiques, et d'autres généralités qui serviront de base pour la suite. Puis, nous verrons dans l'ordre les bus électroniques, les mémoires RAM/ROM, le processeur, les périphériques, les mémoires de stockage (SSD et disques durs) et les mémoires caches. Pour chaque composant, nous allons voir leur architecture externe, avant de voir leur microarchitecture. La raison est que la microarchitecture ne peut se comprendre que quand on sait quelle architecture externe elle implémente.

Enfin, dans une troisième partie, nous allons voir les optimisations majeures présentes dans tous les ordinateurs modernes, avec une partie sur le pipeline et le parallélisme d'instruction, et une autre sur les architectures parallèles. Pour finir, les annexes de fin parlerons de sujets un peu à part.


Le codage des informations

[modifier | modifier le wikicode]

Vous savez déjà qu'un ordinateur permet de faire plein de choses totalement différentes : écouter de la musique, lire des films/vidéos, afficher ou écrire du texte, retoucher des images, créer des vidéos, jouer à des jeux vidéos, etc. Pour être plus général, on devrait dire qu'un ordinateur manipule des informations, sous la forme de fichier texte, de vidéo, d'image, de morceau de musique, de niveau de jeux vidéos, etc. Dans ce qui suit, nous allons appeler ces informations par le terme données. On pourrait définir les ordinateurs comme des appareils qui manipulent des données et/ou qui traitent de l'information, mais force est de constater que cette définition, oh combien fréquente, n'est pas la bonne. Tous les appareils électroniques manipulent des données, même ceux qui ne sont pas des ordinateurs proprement dit : les exemples des décodeurs TNT et autres lecteurs de DVD sont là pour nous le rappeler. Même si la définition d’ordinateur est assez floue et que plusieurs définitions concurrentes existent, il est évident que les ordinateurs se distinguent des autres appareils électroniques programmables sur plusieurs points. Notamment, ils stockent leurs données d'une certaine manière (le codage numérique que nous allons aborder).

Le codage de l'information

[modifier | modifier le wikicode]

Avant d'être traitée, une information doit être transformée en données exploitables par l'ordinateur, sans quoi il ne pourra pas en faire quoi que ce soit. Eh bien, sachez qu'elles sont stockées… avec des nombres. Toute donnée n'est qu'un ensemble de nombres structuré pour être compréhensible par l'ordinateur : on dit que les données sont codées par des nombres. Il suffit d'utiliser une machine à calculer pour manipuler ces nombres, et donc sur les données. Une simple machine à calculer devient une machine à traiter de l'information. Aussi bizarre que cela puisse paraitre, un ordinateur n'est qu'une sorte de grosse calculatrice hyper-performante. Mais comment faire la correspondance entre ces nombres et du son, du texte, ou toute autre forme d'information ? Et comment fait notre ordinateur pour stocker ces nombres et les manipuler ? Nous allons répondre à ces questions dans ce chapitre.

Toute information présente dans un ordinateur est décomposée en petites informations de base, chacune représentée par un nombre. Par exemple, le texte sera décomposé en caractères (des lettres, des chiffres, ou des symboles). Pareil pour les images, qui sont décomposées en pixels, eux-mêmes codés par un nombre. Même chose pour la vidéo, qui n'est rien d'autre qu'une suite d'images affichées à intervalles réguliers. La façon dont un morceau d'information (lettre ou pixel, par exemple) est représenté avec des nombres est définie par ce qu'on appelle un codage, parfois appelé improprement encodage. Ce codage va attribuer un nombre à chaque morceau d'information. Pour montrer à quoi peut ressembler un codage, on va prendre trois exemples : du texte, une image et du son.

Texte : standard ASCII

[modifier | modifier le wikicode]

Pour coder un texte, il suffit de savoir coder une lettre ou tout autre symbole présent dans un texte normal (on parle de caractères). Pour coder chaque caractère avec un nombre, il existe plusieurs codages : l'ASCII, l'Unicode, etc.

Caractères ASCII imprimables.

Le codage le plus ancien, appelé l'ASCII, a été inventé pour les communications télégraphiques et a été ensuite réutilisé dans l'informatique et l'électronique à de nombreuses occasions. Il est intégralement défini par une table de correspondance entre une lettre et le nombre associé, appelée la table ASCII. Le standard ASCII originel utilise des nombres codés sur 7 bits (et non 8 comme beaucoup le croient), ce qui permet de coder 128 symboles différents.

Les lettres sont stockées dans l'ordre alphabétique, pour simplifier la vie des utilisateurs : des nombres consécutifs correspondent à des lettres consécutives. L'ASCII ne code pas seulement des lettres, mais aussi d'autres symboles, dont certains ne sont même pas affichables ! Cela peut paraitre bizarre, mais s'explique facilement quand on connait les origines du standard. Ces caractères non-affichables servent pour les imprimantes, FAX et autres systèmes de télécopies. Pour faciliter la conception de ces machines, on a placé dans cette table ASCII des symboles qui n'étaient pas destinés à être affichés, mais dont le but était de donner un ordre à l'imprimante/machine à écrire... On trouve ainsi des symboles de retour à la ligne, par exemple.

ASCII-Table

La table ASCII a cependant des limitations assez problématiques. Par exemple, vous remarquerez que les accents n'y sont pas, ce qui n'est pas étonnant quand on sait qu'il s'agit d'un standard américain. De même, impossible de coder un texte en grec ou en japonais : les idéogrammes et les lettres grecques ne sont pas dans la table ASCII. Pour combler ce manque, des codages ASCII étendus ont rajouté des caractères à la table ASCII de base. Ils sont assez nombreux et ne sont pas compatibles entre eux. Le plus connu et le plus utilisé est certainement le codage ISO 8859 et ses dérivés, utilisés par de nombreux systèmes d'exploitation et logiciels en occident. Ce codage code ses caractères sur 8 bits et est rétrocompatible ASCII, ce qui fait qu'il est parfois confondu avec ce dernier alors que les deux sont très différents.

Aujourd'hui, le standard de codage de texte le plus connu est certainement l’Unicode. L'Unicode est parfaitement compatible avec la table ASCII : les 128 premiers symboles de l’Unicode sont ceux de la table ASCII, et sont rangés dans le même ordre. Là où l'ASCII ne code que l'alphabet anglais, les codages actuels comme l'Unicode prennent en compte les caractères chinois, japonais, grecs, etc.

Image matricielle.

Le même principe peut être appliqué aux images : l'image est décomposée en morceaux de même taille qu'on appelle des pixels. L'image est ainsi vue comme un rectangle de pixels, avec une largeur et une longueur. Le nombre de pixels en largeur et en longueur définit la résolution de l'image : par exemple, une image avec 800 pixels de longueur et 600 en largeur sera une image dont la résolution est de 800*600. Il va de soi que plus cette résolution est grande, plus l'image sera fine et précise. On peut d'ailleurs remarquer que les images en basse résolution ont souvent un aspect dit pixelisé, où les bords des objets sont en marche d'escaliers.

Chaque pixel a une couleur qui est codée par un ou plusieurs nombres entiers. D'ordinaire, la couleur d'un pixel est définie par un mélange des trois couleurs primaires rouge, vert et bleu. Par exemple, la couleur jaune est composée à 50 % de rouge et à 50 % de vert. Pour coder la couleur d'un pixel, il suffit de coder chaque couleur primaire avec un nombre entier : un nombre pour le rouge, un autre pour le vert et un dernier pour le bleu. Ce codage est appelé le codage RGB. Mais il existe d'autres méthodes, qui codent un pixel non pas à partir des couleurs primaires, mais à partir d'autres espaces de couleur.

Pour stocker une image dans l'ordinateur, on a besoin de connaitre sa largeur, sa longueur et la couleur de chaque pixel. Une image peut donc être représentée dans un fichier par une suite d'entiers : un pour la largeur, un pour la longueur, et le reste pour les couleurs des pixels. Ces entiers sont stockés les uns à la suite des autres dans un fichier. Les pixels sont stockés ligne par ligne, en partant du haut, et chaque ligne est codée de gauche à droite. Les fichiers image actuels utilisent des techniques de codage plus élaborées, permettant notamment décrire une image en utilisant moins de nombres, ce qui prend moins de place dans l'ordinateur.

Pour mémoriser du son, il suffit de mémoriser l'intensité sonore reçue par un microphone à intervalles réguliers. Cette intensité est codée par un nombre entier : si le son est fort, le nombre sera élevé, tandis qu'un son faible se verra attribuer un entier petit. Ces entiers seront rassemblés dans l'ordre de mesure, et stockés dans un fichier son, comme du wav, du PCM, etc. Généralement, ces fichiers sont compressés afin de prendre moins de place.

Le support physique de l'information codée

[modifier | modifier le wikicode]

Pour pouvoir traiter de l'information, la première étape est d'abord de coder celle-ci, c'est à dire de la transformer en nombres. Et peu importe le codage utilisé, celui-ci a besoin d'un support physique, d'une grandeur physique quelconque. Et pour être franc, on peut utiliser tout et n’importe quoi. Par exemple, certains calculateurs assez anciens étaient des calculateurs pneumatiques, qui utilisaient la pression de l'air pour représenter des chiffres ou nombres : soit le nombre encodé était proportionnel à la pression, soit il existait divers intervalles de pression correspondant chacun à un nombre entier bien précis. Il a aussi existé des technologies purement mécaniques pour ce faire, comme les cartes perforées ou d'autres dispositifs encore plus ingénieux. De nos jours, ce stockage se fait soit par l'aimantation d'un support magnétique, soit par un support optique (les CD et DVD), soit par un support électronique. Les supports magnétiques sont réservés aux disques durs magnétiques, destinés à être remplacés par des disques durs entièrement électroniques (les fameux Solid State Drives, que nous verrons dans quelques chapitres).

Pour les supports de stockage électroniques, très courants dans nos ordinateurs, le support en question est une tension électrique. Ces tensions sont ensuite manipulées par des composants électriques/électroniques plus ou moins sophistiqués : résistances, condensateurs, bobines, amplificateurs opérationnels, diodes, transistors, etc. Certains d'entre eux ont besoin d'être alimentés en énergie. Pour cela, chaque circuit est relié à une tension qui l'alimente en énergie : la tension d'alimentation. Après tout, la tension qui code les nombres ne sort pas de nulle part et il faut bien qu'il trouve de quoi fournir une tension de 2, 3, 5 volts. De même, on a besoin d'une tension de référence valant zéro volt, qu'on appelle la masse, qui sert pour le zéro.

Dans les circuits électroniques actuels, ordinateurs inclus, la tension d'alimentation varie généralement entre 0 et 5 volts. Mais de plus en plus, on tend à utiliser des valeurs de plus en plus basses, histoire d'économiser un peu d'énergie. Eh oui, car plus un circuit utilise une tension élevée, plus il consomme d'énergie et plus il chauffe. Pour un processeur, il est rare que les modèles récents utilisent une tension supérieure à 2 volts : la moyenne tournant autour de 1-1.5 volts. Même chose pour les mémoires : la tension d'alimentation de celle-ci diminue au cours du temps. Pour donner des exemples, une mémoire DDR a une tension d'alimentation qui tourne autour de 2,5 volts, les mémoires DDR2 ont une tension d'alimentation qui tombe à 1,8 volts, et les mémoires DDR3 ont une tension d'alimentation qui tombe à 1,5 volts. C'est très peu : les composants qui manipulent ces tensions doivent être très précis.

Les différents codages : analogique, numérique et binaire

[modifier | modifier le wikicode]
Codage numérique : exemple du codage d'un chiffre décimal avec une tension.

Le codage, la transformation d’information en nombre, peut être fait de plusieurs façons différentes. Dans les grandes lignes, on peut identifier deux grands types de codages.

  • Le codage analogique utilise des nombres réels : il code l’information avec des grandeurs physiques (quelque chose que l'on peut mesurer par un nombre) comprises dans un intervalle. Par exemple, un thermostat analogique convertit la température en tension électrique pour la manipuler : une température de 0 degré donne une tension de 0 volts, une température de 20 degrés donne une tension de 5 Volts, une température de 40 degrés donnera du 10 Volts, etc. Un codage analogique a une précision théoriquement infinie : on peut par exemple utiliser toutes les valeurs entre 0 et 5 Volts pour coder une information, même des valeurs tordues comme 1, 2.2345646, ou pire…
  • Le codage numérique n'utilise qu'un nombre fini de valeurs, contrairement au codage analogique. Pour être plus précis, il code des informations en utilisant des nombres entiers, représentés par des suites de chiffres. Le codage numérique précise comment coder les chiffres avec une tension. Comme illustré ci-contre, chaque chiffre correspond à un intervalle de tension : la tension code pour ce chiffre si elle est comprise dans cet intervalle. Cela donnera des valeurs de tension du style : 0, 0.12, 0.24, 0.36, 0.48… jusqu'à 2 volts.

Les avantages et désavantages de l'analogique et du numérique

[modifier | modifier le wikicode]

Un calculateur analogique (qui utilise le codage analogique) peut en théorie faire ses calculs avec une précision théorique très fine, impossible à atteindre avec un calculateur numérique, notamment pour les opérations comme les dérivées, intégrations et autres calculs similaires. Mais dans les faits, aucune machine analogique n'est parfaite et la précision théorique est rarement atteinte, loin de là. Les imperfections des machines posent beaucoup plus de problèmes sur les machines analogiques que sur les machines numériques.

Obtenir des calculs précis sur un calculateur analogique demande non seulement d'utiliser des composants de très bonne qualité, à la conception quasi-parfaite, mais aussi d'utiliser des techniques de conception particulières. Même les composants de qualité ont des imperfections certes mineures, qui peuvent cependant sévèrement perturber les résultats. Les moyens pour réduire ce genre de problème sont très complexes, ce qui fait que la conception des calculateurs analogiques est diablement complexe, au point d'être une affaire de spécialistes. Concevoir ces machines est non seulement très difficile, mais tester leur bon fonctionnement ou corriger des pannes est encore plus complexe.

De plus, les calculateurs analogiques sont plus sensibles aux perturbations électromagnétiques. On dit aussi qu'ils ont une faible immunité au bruit. En effet, un signal analogique peut facilement subir des perturbations qui vont changer sa valeur, modifiant directement la valeur des nombres stockés ou manipulés. Avec un codage numérique, les perturbations ou parasites vont moins perturber le signal numérique. La raison est qu'une variation de tension qui reste dans un intervalle représentant un chiffre ne changera pas sa valeur. Il faut que la variation de tension fasse sortir la tension de l'intervalle pour changer le chiffre. Cette sensibilité aux perturbations est un désavantage net pour l'analogique et est une des raisons qui font que les calculateurs analogiques sont peu utilisés de nos jours. Elle rend difficile de faire fonctionner un calculateur analogique rapidement et limite donc sa puissance.

Un autre désavantage est que les calculateurs analogiques sont très spécialisés et qu'ils ne sont pas programmables. Un calculateur analogique est forcément conçu pour résoudre un problème bien précis. On peut le reconfigurer, le modifier à la marge, mais guère plus. Typiquement, les calculateurs analogiques sont utilisés pour résoudre des équations différentielles couplées non-linéaires, mais n'ont guère d'utilité pratique au-delà. Mais les ingénieurs ne font cela que pour les problèmes où il est pertinent de concevoir de zéro un calculateur spécialement dédié au problème à résoudre, ce qui est un cas assez rare.

Le choix de la base

[modifier | modifier le wikicode]

Au vu des défauts des calculateurs analogiques, on devine que la grosse majorité des circuits électronique actuels sont numériques. Mais il faut savoir que les ordinateurs n'utilisent pas la numération décimale normale, celle à 10 chiffres qui vont de 0 à 9. De nos jours, les ordinateurs n'utilisent que deux chiffres, 0 et 1 (on parle de « bit ») : on dit qu'ils comptent en binaire. On verra dans le chapitre suivant comment coder des nombres avec des bits, ce qui est relativement simple. Pour le moment, nous allons justifier ce choix de n'utiliser que des bits et pas les chiffres décimaux (de 0 à 9). Avec une tension électrique, il y a diverses méthodes pour coder un bit : codage Manchester, NRZ, etc. Autant trancher dans le vif tout de suite : la quasi-intégralité des circuits d'un ordinateur se basent sur le codage NRZ.

Naïvement, la solution la plus simple serait de fixer un seuil en-dessous duquel la tension code un 0, et au-dessus duquel la tension représente un 1. Mais les circuits qui manipulent des tensions n'ont pas une précision parfaite et une petite perturbation électrique pourrait alors transformer un 0 en 1. Pour limiter la casse, on préfère ajouter une sorte de marge de sécurité, ce qui fait qu'on utilise en réalité deux seuils séparés par un intervalle vide. Le résultat est le fameux codage NRZ dont nous venons de parler : la tension doit être en dessous d'un seuil donné pour un 0, et il existe un autre seuil au-dessus duquel la tension représente un 1. Tout ce qu'il faut retenir, c'est qu'il y a un intervalle pour le 0 et un autre pour le 1. En dehors de ces intervalles, on considère que le circuit est trop imprécis pour pouvoir conclure sur la valeur de la tension : on ne sait pas trop si c'est un 1 ou un 0.

Il arrive que ce soit l'inverse sur certains circuits électroniques : en dessous d'un certain seuil, c'est un 1 et si c'est au-dessus d'un autre seuil c'est 0.
Codage NRZ

L'avantage du binaire par rapport aux autres codages est qu'il permet de mieux résister aux perturbations électromagnétiques mentionnées dans le chapitre précédent. À tension d'alimentation égale, les intervalles de chaque chiffre sont plus petits pour un codage décimal : toute perturbation de la tension aura plus de chances de changer un chiffre. Mais avec des intervalles plus grands, un parasite aura nettement moins de chance de modifier la valeur du chiffre codé ainsi. La résistance aux perturbations électromagnétiques est donc meilleure avec seulement deux intervalles.

Comparaison entre codage binaire et décimal pour l'immunité au bruit.


Dans le chapitre précédent, nous avons vu que les ordinateurs actuels utilisent un codage binaire. Ce codage binaire ne vous est peut-être pas familier. Aussi, dans ce chapitre, nous allons apprendre comment coder des nombres en binaire. Nous allons commencer par le cas le plus simple : les nombres positifs. Par la suite, nous aborderons les nombres négatifs. Et nous terminerons par les nombres à virgules, appelés aussi nombres flottants.

Le codage des nombres entiers positifs

[modifier | modifier le wikicode]

Pour coder des nombres entiers positifs, il existe plusieurs méthodes : le binaire, l’hexadécimal, le code Gray, le décimal codé binaire et bien d'autres encore. La plus connue est certainement le binaire, secondée par l'hexadécimal, les autres étant plus anecdotiques. Pour comprendre ce qu'est le binaire, il nous faut faire un rappel sur les nombres entiers tel que vous les avez appris en primaire, à savoir les entiers écrits en décimal. Prenons un nombre écrit en décimal : le chiffre le plus à droite est le chiffre des unités, celui à côté est pour les dizaines, suivi du chiffre des centaines, et ainsi de suite. Dans un tel nombre :

  • on utilise une dizaine de chiffres, de 0 à 9 ;
  • chaque chiffre est multiplié par une puissance de 10 : 1, 10, 100, 1000, etc. ;
  • la position d'un chiffre dans le nombre indique par quelle puissance de 10 il faut le multiplier : le chiffre des unités doit être multiplié par 1, celui des dizaines par 10, celui des centaines par 100, et ainsi de suite.

Exemple avec le nombre 1337 :

Pour résumer, un nombre en décimal s'écrit comme la somme de produits, chaque produit multipliant un chiffre par une puissance de 10. On dit alors que le nombre est en base 10.

Ce qui peut être fait avec des puissances de 10 peut être fait avec des puissances de 2, 3, 4, 125, etc : n'importe quel nombre entier strictement positif peut servir de base. En informatique, on utilise rarement la base 10 à laquelle nous sommes tant habitués. On utilise à la place deux autres bases :

  • La base 2 (système binaire) : les chiffres utilisés sont 0 et 1 ;
  • La base 16 (système hexadécimal) : les chiffres utilisés sont 0, 1, 2, 3, 4, 5, 6, 7, 8 et 9 ; auxquels s'ajoutent les six premières lettres de notre alphabet : A, B, C, D, E et F.

Le système binaire

[modifier | modifier le wikicode]

En binaire, on compte en base 2. Cela veut dire qu'au lieu d'utiliser des puissances de 10 comme en décimal, on utilise des puissances de deux : n'importe quel nombre entier peut être écrit sous la forme d'une somme de puissances de 2. Par exemple 6 s'écrira donc 0110 en binaire : . On peut remarquer que le binaire n'autorise que deux chiffres, à savoir 0 ou 1 : ces chiffres binaires sont appelés des bits (abréviation de Binary Digit). Pour simplifier, on peut dire qu'un bit est un truc qui vaut 0 ou 1. Pour résumer, tout nombre en binaire s'écrit sous la forme d'un produit entre bits et puissances de deux de la forme :

Les coefficients sont les bits, l'exposant n qui correspond à un bit est appelé le poids du bit.

La terminologie du binaire

[modifier | modifier le wikicode]

En informatique, il est rare que l'on code une information sur un seul bit. Dans la plupart des cas, l'ordinateur manipule des nombres codés sur plusieurs bits. Les informaticiens ont donné des noms aux groupes de bits suivant leur taille. Le plus connu est certainement l'octet, qui désigne un groupe de 8 bits. Moins connu, on parle de nibble pour un groupe de 4 bits (un demi-octet), de doublet pour un groupe de 16 bits (deux octets) et de quadruplet pour un groupe de 32 bits (quatre octets).

Précisons qu'en anglais, le terme byte n'est pas synonyme d'octet. En réalité, le terme octet marche aussi bien en français qu'en anglais. Quant au terme byte, il désigne un concept complètement différent, que nous aborderons plus tard (c'est la plus petite unité de mémoire que le processeur peut adresser). Il a existé dans le passé des ordinateurs où le byte faisait 4, 7, 9, 16, voire 48 bits, par exemple. Il a même existé des ordinateur où le byte faisait exactement 1 bit ! Mais sur presque tous les ordinateurs modernes, un byte fait effectivement 8 bits, ce qui fait que le terme byte est parfois utilisé en lieu et place d'octet. Mais c'est un abus de langage, attention aux confusions ! Dans ce cours, nous parlerons d'octet pour désigner un groupe de 8 bits, en réservant le terme byte pour sa véritable signification.

À l'intérieur d'un nombre, le bit de poids faible est celui qui est le plus à droite du nombre, alors que le bit de poids fort est celui non nul qui est placé le plus à gauche, comme illustré dans le schéma ci-dessous.

Bit de poids fort.
Bit de poids faible.

Cette terminologie s'applique aussi pour les bits à l'intérieur d'un octet, d'un nibble, d'un doublet ou d'un quadruplet. Pour un nombre codés sur plusieurs octets, on peut aussi parler de l'octet de poids fort et de l'octet de poids faible, du doublet de poids fort ou de poids faible, etc.

La traduction binaire→décimal

[modifier | modifier le wikicode]

Pour traduire un nombre binaire en décimal, il faut juste se rappeler que la position d'un bit indique par quelle puissance il faut le multiplier. Ainsi, le chiffre le plus à droite est le chiffre des unités : il doit être multiplié par 1 (). Le chiffre situé immédiatement à gauche du chiffre des unités doit être multiplié par 2 (). Le chiffre encore à gauche doit être multiplié par 4 (), et ainsi de suite. Mathématiquement, on peut dire que le énième bit en partant de la droite doit être multiplié par . Par exemple, la valeur du nombre noté 1011 en binaire est de .

Value of digits in the Binary numeral system

La traduction décimal→binaire

[modifier | modifier le wikicode]

La traduction inverse, du décimal au binaire, demande d'effectuer des divisions successives par deux. Les divisions en question sont des divisions euclidiennes, avec un reste et un quotient. En lisant les restes des divisions dans un certain sens, on obtient le nombre en binaire. Voici comment il faut procéder, pour traduire le nombre 34 :

Exemple d'illustration de la méthode de conversion décimal vers binaire.

Quelques opérations en binaire

[modifier | modifier le wikicode]

Maintenant que l'on sait coder des nombres en binaire normal, il est utile de savoir comment faire quelques opérations usuelles en binaire. Nous utiliserons les acquis de cette section dans la suite du chapitre, bien que de manière assez marginale.

La première opération est assez spécifique au binaire. Il s'agit d'une opération qui inverse les bits d'un nombre : les 0 deviennent des 1 et réciproquement. Par exemple, le nombre 0001 1001 devient 1110 0110. Elle porte plusieurs noms : opération NOT, opération NON, complémentation, etc. Nous parlerons de complémentation ou d'opération NOT dans ce qui suit. Beaucoup d'ordinateurs gèrent cette opération, ils savent la faire en un seul calcul. Il faut dire que c'est une opération assez utile, bien que nous ne pouvons pas encore expliquer pourquoi.

Exemple d'addition en binaire.

La seconde opération à aborder est l'addition. Elle se fait en binaire de la même manière qu'en décimal : Pour faire une addition en binaire, on additionne les chiffres/bits colonne par colonne, une éventuelle retenue est propagée à la colonne d'à côté. Sauf que l'on additionne des bits. Heureusement, la table d'addition est très simple en binaire :

  • 0 + 0 = 0, retenue = 0 ;
  • 0 + 1 = 1, retenue = 0 ;
  • 1 + 0 = 1, retenue = 0 ;
  • 1 + 1 = 0, retenue = 1.

La troisième opération est une variante de l'addition appelée l'opération XOR, notée . Il s'agit d'une addition dans laquelle on ne propage pas les retenues. L'addition des deux bits des opérandes se fait normalement, mais les retenues sont simplement oubliées, on n'en tient pas compte. Le résultat est que l'addition se résume à appliquer la table d'addition précédente :

  • 0 0 = 0 ;
  • 0 1 = 1 ;
  • 1 0 = 1 ;
  • 1 1 = 0.

Pour résumer, le résultat vaut 1 si les deux bits sont différents, 0 s'ils sont identiques. L'opération XOR sera utilisée rapidement dans le chapitre suivant, quand nous parlerons rapidement du mot de parité. Et elle sera beaucoup utilisée dans la suite du cours, nous en feront fortement usage. Pour le moment, mémorisez juste cette opération, elle n'a rien de compliqué.

Une dernière opération est l'opération de population count. Il s'agit ni plus ni moins que de compter le nombre de bits qui sont à 1 dans un nombre. Par exemple, pour le nombre 0110 0010 1101 1110, elle donne pour résultat 9. Elle est utilisée dans certaines applications, comme le calcul de certains codes correcteurs d'erreur, comme on le verra dans le chapitre suivant. Elle est supportée sur de nombreux ordinateurs, encore que cela dépende du processeur considéré. Il s'agit cependant d'une opération assez courante, supportée par les processeurs ARM, les processeurs x86 modernes (ceux qui gèrent le SSE), et quelques autres.

L'hexadécimal

[modifier | modifier le wikicode]

L’hexadécimal est basé sur le même principe que le binaire, sauf qu'il utilise les 16 chiffres suivants :

Chiffre hexadécimal 0 1 2 3 4 5 6 7 8 9 A B C D E F
Nombre décimal correspondant 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
Notation binaire 0000 0001 0010 0011 0100 0101 0110 0111 1000 1001 1010 1011 1100 1101 1110 1111

Dans les textes, afin de différencier les nombres décimaux des nombres hexadécimaux, les nombres hexadécimaux sont suivis par un petit h, indiqué en indice. Si cette notation n'existait pas, des nombres comme 2546 seraient ambigus : on ne saurait pas dire sans autre indication s'ils sont écrits en décimal ou en hexadécimal. Avec la notation, on sait de suite que 2546 est en décimal et que 2546h est en hexadécimal.

Dans les codes sources des programmes, la notation diffère selon le langage de programmation. Certains supportent le suffixe h pour les nombres hexadécimaux, d'autres utilisent un préfixe 0x ou 0h.

La conversion hexadécimal↔décimal

[modifier | modifier le wikicode]

Pour convertir un nombre hexadécimal en décimal, il suffit de multiplier chaque chiffre par la puissance de 16 qui lui est attribuée. Là encore, la position d'un chiffre indique par quelle puissance celui-ci doit être multiplié : le chiffre le plus à droite est celui des unités, le second chiffre le plus à droite doit être multiplié par 16, le troisième chiffre en partant de la droite doit être multiplié par 256 (16 * 16) et ainsi de suite. La technique pour convertir un nombre décimal vers de l’hexadécimal est similaire à celle utilisée pour traduire un nombre du décimal vers le binaire. On retrouve une suite de divisions successives, mais cette fois-ci les divisions ne sont pas des divisions par 2 : ce sont des divisions par 16.

La conversion hexadécimal↔binaire

[modifier | modifier le wikicode]

La conversion inverse, de l'hexadécimal vers le binaire est très simple, nettement plus simple que les autres conversions. Pour passer de l'hexadécimal au binaire, il suffit de traduire chaque chiffre en sa valeur binaire, celle indiquée dans le tableau au tout début du paragraphe nommé « Hexadécimal ». Une fois cela fait, il suffit de faire le remplacement. La traduction inverse est tout aussi simple : il suffit de grouper les bits du nombre par 4, en commençant par la droite (si un groupe est incomplet, on le remplit avec des zéros). Il suffit alors de remplacer le groupe de 4 bits par le chiffre hexadécimal qui correspond.

Interlude propédeutique : la capacité d'un entier et les débordements d'entiers

[modifier | modifier le wikicode]

Dans la section précédente, nous avons vu comment coder des entiers positifs en binaire ou dans des représentations proches. La logique voudrait que l'on aborde ensuite le codage des entiers négatifs. Mais nous allons déroger à cette logique simple, pour des raisons pédagogiques. Nous allons faire un interlude qui introduira des notions utiles pour la suite du chapitre. De plus, ces concepts seront abordés de nombreuses fois dans ce wikilivre et l'introduire ici est de loin la solution idéale.

Les ordinateurs manipulent des nombres codés sur un nombre fixe de bits

[modifier | modifier le wikicode]

Vous avez certainement déjà entendu parler de processeurs 32 ou 64 bits. Et si vous avez joué aux jeux vidéos durant votre jeunesse et êtes assez agé, vous avez entendu parler de consoles de jeu 8 bits, 16 bits, 32 bits, voire 64 bits (pour la Jaguar, et c'était un peu trompeur). Derrière cette appellation qu'on retrouvait autrefois comme argument commercial dans la presse se cache un concept simple. Tout ordinateur manipule des nombres entiers dont le nombre de bits est toujours le même : on dit qu'ils sont de taille fixe. Une console 16 bits manipulait des entiers codés en binaire sur 16 bits, pas un de plus, pas un de moins. Pareil pour les anciens ordinateurs 32 bits, qui manipulaient des nombres entiers codés sur 32 bits.

Aujourd'hui, les ordinateurs modernes utilisent presque un nombre de bits qui est une puissance de 2 : 8, 16, 32, 64, 128, 256, voire 512 bits. Mais cette règle souffre évidemment d'exceptions. Aux tout débuts de l'informatique, certaines machines utilisaient 3, 7, 13, 17, 23, 36 et 48 bits ; mais elles sont aujourd'hui tombées en désuétude. De nos jours, il ne reste que les processeurs dédiés au traitement de signal audio, que l'on trouve dans les chaînes HIFI, les décodeurs TNT, les lecteurs DVD, etc. Ceux-ci utilisent des nombres entiers de 24 bits, car l'information audio est souvent codée par des nombres de 24 bits.

Anecdote amusante, il a existé des ordinateurs de 1 bit, qui sont capables de manipuler des nombres codés sur 1 bit, pas plus. Un exemple est le Motorola MC14500B, commercialisé de 1976.

Le lien entre nombre de bits et valeurs codables

[modifier | modifier le wikicode]

Évidemment, on ne peut pas coder tous les entiers possibles et imaginables avec seulement 8 bits, ou 16 bits. Et il parait intuitif que l'on ait plus de valeurs codables sur 16 bits qu'avec 8 bits, par exemple. Plus le nombre de bits est important, plus on pourra coder de valeurs entières différentes. Mais combien de plus ? Par exemple, si je passe de 8 bits à 16 bits, est-ce que le nombre de valeurs que l'on peut coder double, quadruple, pentuple ? De même, combien de valeurs différentes on peut coder avec bits. Par exemple, combien de nombres différents peut-on coder avec 4, 8 ou 16 bits ? La section précédente vous l'expliquer.

Avec bits, on peut coder valeurs différentes, dont le , ce qui fait qu'on peut compter de à . N'oubliez pas cette formule : elle sera assez utile dans la suite de ce tutoriel. Pour exemple, on peut coder 16 valeurs avec 4 bits, qui vont de 0 à 15. De même, on peut coder 256 valeurs avec un octet, qui vont de 0 à 255. Le tableau ci-dessous donne quelques exemples communs.

Nombre de bits Nombre de valeurs codables
4 16
8 256
16 65 536
32 4 294 967 296
64 18 446 744 073 709 551 615

Inversement, on peut se demander combien de bits il faut pour coder une valeur quelconque, que nous noterons N. Pour cela, il faut utiliser la formule précédente, mais à l'envers. On cherche alors tel que . L'opération qui donne est appelée le logarithme, et plus précisément un logarithme en base 2, noté . Le problème est que le résultat du logarithme ne tombe juste que si le nombre X est une puissance de 2. Si ce n'est pas le cas, le résultat est un nombre à virgule, ce qui n'a pas de sens pratique. Par exemple, la formule nous dit que pour coder le nombre 13, on a besoin de 3,70043971814 bits, ce qui est impossible. Pour que le résultat ait un sens, il faut arrondir à l'entier supérieur. Pour l'exemple précédent, les 3,70043971814 bits s'arrondissent en 4 bits.

Le lien entre nombre de chiffres hexadécimaux et valeurs codables

[modifier | modifier le wikicode]

Maintenant, voyons combien de valeurs peut-on coder avec chiffres hexadécimaux. La réponse n'est pas très différente de celle obtenue en binaire, si ce n'est qu'il faut remplacer le 2 par un 16 dans la formule précédente. Avec chiffres hexadécimaux, on peut coder valeurs différentes, dont le , ce qui fait qu'on peut compter de à . Le tableau ci-dessous donne quelques exemples communs.

Nombre de chiffres héxadécimaux Nombre de valeurs codables
1 (4 bits) 16
2 (8 bits) 256
4 (16 bits) 65 536
8 (32 bits) 4 294 967 296
16 (64 bits) 18 446 744 073 709 551 615

Inversement, on peut se demander combien faut-il de chiffres hexadécimaux pour coder une valeur quelconque en hexadécimal. La formule est là encore la même qu'en binaire, sauf qu'on remplace le 2 par un 16. Pour trouver le nombre de chiffres hexadécimaux pour encoder un nombre X, il faut calculer . Notons que le logarithme utilisé est un logarithme en base 16, et non un logarithme en base 2, comme pour le binaire. Là encore, le résultat ne tombe juste que si le nombre X est une puissance de 16 et il faut arrondir à l'entier supérieur si ce n'est pas le cas.

Une propriété mathématique des logarithmes nous dit que l'on peut passer d'un logarithme en base X et à un logarithme en base Y avec une simple division, en utilisant la formule suivante :

Dans le cas qui nous intéresse, on a Y = 2 et X = 16, ce qui donne :

Or, est tout simplement égal à 4, car il faut 4 bits pour coder la valeur 16. On a donc :

En clair, il faut quatre fois moins de chiffres hexadécimaux que de bits, ce qui est assez intuitif vu qu'il faut 4 bits pour coder un chiffre hexadécimal.

Les débordements d'entier

[modifier | modifier le wikicode]

On vient de voir que tout ordinateur manipule des nombres dont le nombre de bits est toujours le même : on dit qu'ils sont de taille fixe. Et cela limite les valeurs qu'il peut encoder, qui sont comprises dans un intervalle bien précis. Mais que ce passe-t-il si jamais le résultat d'un calcul ne rentre pas dans cet intervalle ? Par exemple, pour du binaire normal, que faire si le résultat d'un calcul atteint ou dépasse  ? Dans ce cas, le résultat ne peut pas être représenté par l'ordinateur et il se produit ce qu'on appelle un débordement d'entier.

On peut imaginer d'autres codages pour lesquels les entiers ne commencent pas à zéro ou ne terminent pas à . On peut prendre le cas où l'ordinateur gère les nombres négatifs, par exemple. Dans le cas général, l'ordinateur peut coder les valeurs comprises dans un intervalle, qui va de la valeur la plus basse à la valeur la plus grande . Et encore une fois, si un résultat de calcul sort de cet intervalle, on fait face à 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. Pour les nombres entiers, la valeur haute de débordement vaut , avec la plus grande valeur codable par l'ordinateur.

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 basse de débordement vaut , avec la plus petite valeur codable par l'ordinateur.

La gestion des débordements d'entiers

[modifier | modifier le wikicode]

Face à un débordement d'entier, l'ordinateur peut utiliser deux méthodes : l'arithmétique saturée ou l'arithmétique modulaire.

L'arithmétique saturée consiste à arrondir le résultat pour prendre la plus grande ou la plus petite valeur. Si le résultat d'un calcul dépasse la valeur haute de débordement, le résultat est remplacé par le plus grand entier supporté par l'ordinateur. La même chose est possible quand le résultat est inférieur à la plus petite valeur possible, par exemple lors d'une soustraction : l'ordinateur arrondit au plus petit entier possible.

Pour donner un exemple, voici ce que cela donne avec des entiers codés sur 4 bits, qui codent des nombres de 0 à 15. Si je fais le calcul 8 + 9, le résultat normal vaut 17, ce qui ne rentre pas dans l'intervalle. Le résultat est alors arrondi à 15. Inversement, si je fais le calcul 8 - 9, le résultat sera de -1, ce qui ne rentre pas dans l'intervalle : le résultat est alors arrondi à 0.

Exemple de débordement d'entier sur un compteur mécanique quelconque. L'image montre clairement que le compteur revient à zéro une fois la valeur maximale dépassée.

L'arithmétique modulaire est plus compliquée et c'est elle qui va nous intéresser dans ce qui suit. Pour simplifier, imaginons que l'on décompte à partir de zéro. Quand on arrive à la valeur haute de débordement, on recommence à compter à partir de zéro. L'arithmétique modulaire n'est pas si contre-intuitive et vous l'utilisez sans doute au quotidien. Après tout, c'est comme cela que l'on compte les heures, les minutes et les secondes. Quand on compte les minutes, on revient à 0 au bout de 60 minutes. Pareil pour les heures : on revient à zéro quand on arrive à 24 heures. Divers compteurs mécaniques fonctionnent sur le même principe et reviennent à zéro quand ils dépassent la plus grande valeur possible, l'image ci-contre en montrant un exemple.

Mathématiquement, l'arithmétique modulaire implique des divisions euclidiennes, celles qui donnent un quotient et un reste. Lors d'un débordement, le résultat s'obtient comme suit : on divise le nombre qui déborde par la valeur haute de débordement, et que l'on conserve le reste de la division. Au passage, l'opération qui consiste à faire une division et à garder le reste au lieu du quotient est appelée le modulo. Prenons l'exemple où l'ordinateur peut coder tous les nombres entre 0 et 1023, soit une valeur haute de débordement de 1024. Pour coder le nombre 4563, on fait le calcul 4563 / 1024. On obtient : . Le reste de la division est de 467 et c'est lui qui sera utilisé pour coder la valeur de départ, 4563. Le nombre 4563 est donc codé par la valeur 467 dans un tel ordinateur en arithmétique modulaire. Au passage, la valeur haute de débordement est toujours codée par un zéro dans ce genre d'arithmétique.

Les ordinateurs utilisent le plus souvent une valeur haute de débordement de , avec n le nombre de bits utilisé pour coder les nombres entiers positifs. En faisant cela, l'opération modulo devient très simple et revient à éliminer les bits de poids forts au-delà du énième bit. Par exemple, reprenons l'exemple d'un ordinateur qui code ses nombres sur 4 bits. Imaginons qu'il fasse le calcul , soit 1101 + 0011 = 1 0000 en binaire. Le résultat entraîne un débordement d'entier et l'ordinateur ne conserve que les 4 bits de poids faible. Cela donne : 1101 + 0011 = 0000. Comme autre exemple l'addition 1111 + 0010 ne donnera pas 17 (1 0001), mais 1 (0001).

L'avantage est que les calculs sont beaucoup plus simples avec cette méthode qu'avec les autres. L'ordinateur a juste à ne pas calculer les bits de poids fort. Pas besoin de faire une division pour calculer un modulo, pas besoin de corriger le résultat pour faire de l'arithmétique saturée.

Aparté : quelques valeurs particulières en binaire

[modifier | modifier le wikicode]

Plus haut, on a dit qu'avec n bits, on peut encoder toutes les valeurs allant de à . Ce simple fait permet de déterminer quelle est la valeur de certains entiers à vue d’œil. Dans ce qui va suivre, nous allons poser quelques bases que nous réutiliserons dans la suite du chapitre. Il s'agit de quelques trivias qui sont cependant assez utiles.

Le premier trivia concerne la valeur maximale  : elle est encodée par un nombre dont tous les bits sont à 1.

Le deuxième trivia concerne les nombres de la forme 000...000 1111 1111, à savoir des nombres dont les x bits de poids faible sont à 1 et tous les autres valent 0. Par définition, de tels nombres codent la plus grande valeur possible sur x bits, ce qui fait qu'ils valent .

Le troisième trivia concerne les nombres de la forme 11111...0000. En clair, des nombres où on a une suite consécutive de 1 dans les bits de poids fort et les x bits de poids faibles à 0. De tels nombres valent : . La preuve est assez simple : ils s'obtiennent en prenant la valeur maximale , et en soustrayant un nombre de la forme .

Valeurs particulières en binaire

Si on utilise l'arithmétique modulaire, la valeur n'est autre que la valeur de débordement haute pour les nombres stockés sur n bits, et se code comme le zéro (il faudrait 1 bit de plus pour stocker le 1 de poids fort de ). Les 1er et 3ème nombres évoqués dans les paragraphes précédents peuvent donc se passer de dans leur expression :

  • est codé 1111111111111111...111 (n bits à 1)
  • est codé 11111...111000..000000 (x bits à 0 précédés de n-x bits à 1)

Les nombres entiers négatifs

[modifier | modifier le wikicode]

Passons maintenant aux entiers négatifs en binaire : comment représenter le signe moins ("-") avec des 0 et des 1 ? Eh bien, il existe plusieurs méthodes :

  • la représentation en signe-valeur absolue ;
  • la représentation en complément à un ;
  • la représentation en complément à deux;
  • la représentation par excès ;
  • la représentation dans une base négative ;
  • d'autres représentations encore moins utilisées que les autres.

La représentation en signe-valeur absolue

[modifier | modifier le wikicode]

La solution la plus simple pour représenter un entier négatif consiste à coder sa valeur absolue en binaire, et rajouter un bit qui précise si c'est un entier positif ou un entier négatif. Par convention, ce bit de signe vaut 0 si le nombre est positif et 1 s'il est négatif. On parle alors de représentation en signe-valeur absolue, aussi appelée représentation en signe-magnitude.

Avec cette technique, il y autant de nombres positifs que négatifs. Mieux : pour chaque nombre représentable en représentation signe-valeur absolue, son inverse l'est aussi. Ce qui fait qu'avec cette méthode, le zéro est codé deux fois : on a un -0, et un +0. Cela pose des problèmes lorsqu'on demande à notre ordinateur d'effectuer des calculs ou des comparaisons avec zéro.

Codage sur 4 bits en signe-valeur absolue

La représentation en complément à un

[modifier | modifier le wikicode]

La représentation en complément à un peut être vue, en première approximation, comme une variante de la représentation en signe-magnitude. Et je dis bien : "em première approximation", car il y a beaucoup à dire sur la représentation en complément à un, mais nous verrons cela dans la section suivante sur le complément à deux. Conceptuellement, la représentation en signe-magnitude et celle du complément à un sont drastiquement différentes et sont basées sur des concepts mathématiques totalement différents. Mais elles ont des ressemblances de surface, qui font que faire la comparaison entre les deux est assez utile pédagogiquement parlant.

En complément à un, les nombres sont codés en utilisant un bit de signe qui indique si le nombre de positif ou négatif, couplé à une valeur absolue. Par contre, la valeur absolue est codée différemment en binaire. Pour les nombres positifs, la valeur absolue est codée en binaire normal, pas de changement comparé aux autres représentations. Mais pour les valeurs négatives, la valeur absolue est codée en binaire normal, puis tous les bits sont inversés : les 0 deviennent des 1 et réciproquement. En clair, on utilise une opération de complémentation pour les nombres négatifs.

Prenons un exemple avec un nombre codé sur 4 bits, avec un cinquième bit de signe. La valeur 5 est codée comme suit : 0 pour le bit de signe, 5 donne 0101 en binaire, le résultat est 0 0101. Pour la valeur -5, le bit de signe est 1, 5 donne 0101 en binaire, on inverse les bits ce qui donne 1010 : cela donne 11010. Notez qu'on peut passer d'un résultat à l'autre avec une opération de complémentation, à savoir en inversant les bits de l'autre et réciproquement.

Il s'agit là d'une propriété générale avec le complément à 1 : l'opposé d'un nombre, à savoir passer de sa valeur positive à sa valeur négative ou inversement, se calcule avec une opération NOT, une opération de complémentation (d'où son nom). L'avantage est que les ordinateurs gèrent naturellement d'opération de complémentation. Par contre, en signe-magnitude, inverser le bit de signe est une opération spécifique et ne sert qu'à ça. Il faut donc rajouter une opération en plus pour calculer l'opposé d'un nombre.

La représentation en complément à un garde les défauts de la représentation en signe-magnitude. Le zéro est codé deux fois, avec un zéro positif et un zéro négatif. La différence est que les valeurs négatives sont dans l'ordre inverse, il y a une symétrie un peu meilleure. Les deux zéros sont d'ailleurs totalement éloignés, ils correspondent aux valeurs extrêmes encodables : le zéro positif a tous ses bits à 0, le zéro négatif a tous ses bits à 1. Le résultat est que l'implémentation des comparaisons et de certains calculs est plus complexe qu'avec le signe-magnitude, mais de peu.

Codage sur 4 bits en complément à 1.

La représentation par excès

[modifier | modifier le wikicode]

La représentation par excès consiste à ajouter un biais aux nombres à encoder afin de les encoder par un entier positif. Pour encoder tous les nombres compris entre -X et Y en représentation par excès, il suffit de prendre la valeur du nombre à encoder, et de lui ajouter un biais égal à X. Ainsi, la valeur -X sera encodée par zéro, et toutes les autres valeurs le seront par un entier positif, le zéro sera encodé par X, 1, par X+1, etc. Par exemple, prenons des nombres compris entre -127 et 128. On va devoir ajouter un biais égal à 127, ce qui donne :

Valeur avant encodage Valeur après encodage
-127 0
-126 1
-125 2
0 127
127 254
128 255

La représentation en complément à deux

[modifier | modifier le wikicode]

La représentation en complément à deux est basée sur les mêmes mathématiques que le complément à un, mais fonctionne très différemment en pratique. Si on regarde de loin, son principe est assez différent : il n'y a pas de bit de signe ni quoi que ce soit d'autre. A la place, un nombre en complément à deux est encodé comme en binaire normal, à un point près : le bit de poids fort est soustrait, et non additionné aux autres. Il a une valeur négative : on soustrait la puissance de deux associée s'il vaut 1, on ne la tient pas en compte s'il vaut 0.

Par exemple, la valeur du nombre noté 111001 en complément à deux s'obtient comme suit :

-32 16 8 4 2 1
1 1 1 0 0 1

Sa valeur est ainsi de (−32×1)+(16×1)+(8×1)+(4×0)+(2×1)+(1×1) = −32+16+8+1 = -7.

Avec le complément à deux, comme avec le complément à un, le bit de poids fort vaut 0 pour les nombres positifs et 1 pour les négatifs.

L'avantage de cette représentation est qu'elle n'a pas de double zéro : le zéro n'est encodé que par une seule valeur. Par contre, la valeur autrefois prise par le zéro négatif est réutilisée pour encoder une valeur négative. La conséquence est que l'on a un nombre négatif en plus d'encodé, il n'y a plus le même nombre de valeurs strictement positives et de valeurs négatives encodées. Le nombre négatif en question est appelé le nombre le plus négatif, ce nom trahit le fait que c'est celui qui a la plus petite valeur (la plus grande valeur absolue). Il est impossible de coder l'entier positif associé, sa valeur absolue.

Codage sur 4 bits en complément à 2.

Le calcul du complément à deux : première méthode

[modifier | modifier le wikicode]

Les représentations en complément à un et en complément à deux sont basées sur le même principe mathématique. Leur idée est de remplacer chaque nombre négatif par un nombre positif équivalent, appelé le complément. Par équivalent, on veut dire que tout calcul donne le même résultat si on remplace un nombre négatif par son complément (idem avec un nombre positif, son complément aura un signe inverse). Et pour faire cela, elles se basent sur les débordements d'entier pour fonctionner, et plus précisément sur l'arithmétique modulaire abordée plus haut.

Si on fait les calculs avec le complément, les résultats du calcul entraînent un débordement d'entier, qui sera résolu par un modulo : l'ordinateur ne conserve que les bits de poids faible du résultat, les autres bits sont oubliés. Par exemple, prenons l'addition 15 + 2, 1111 + 0010 en binaire : le résultat ne sera pas 17 (10001), vu qu'on n'a pas assez de bits pour encoder le résultat, mais 1 (0001). Et le résultat après modulo sera identique au résultat qu'on aurait obtenu avec le nombre négatif sans modulo. En clair, c'est la gestion des débordements qui permet de corriger le résultat de manière à ce que l'opération avec le complément donne le même résultat qu'avec le nombre négatif voulu. Ainsi, on peut coder un nombre négatif en utilisant son complément positif.

Cela ressemble beaucoup à la méthode de soustraction basée sur un complément à 9 pour ceux qui connaissent, sauf que c'est une version binaire qui nous intéresse ici.

Prenons un exemple, qui permettra d'introduire la suite. Encore une fois, on utilise un codage sur 4 bits dont la valeur haute de débordement est de 16. Prenons l'addition de 13 + 3 = 16. Avec l'arithmétique modulaire, 16 est équivalent à 0, ce qui donne : 13 + 3 = 0 ! On peut aussi reformuler en disant que 13 = -3, ou encore que 3 = -13. Dit autrement, 3 est le complément de -13 pour ce codage. Et ne croyez pas que ça marche uniquement dans cet exemple : cela se généralise assez rapidement à tout nombre négatif.

Prenons un nombre N dont un veut calculer le complément à deux K. Dans le cas général, on a :

, vu que en utilisant le modulo.

En réorganisant très légèrement les termes pour isoler K, on a :

La formule précédent permet de calculer la complément à deux assez simplement, en faisant le calcul à la main.

Avant de poursuivre, prenons un exemple très intéressant : le cas où N = -1. Son complément à deux vaut donc :

Le terme devrait vous rappeler quelque chose : il s'agit d'un nombre dont tous les bits sont à 1. En clair, le complément à deux de -1 est un nombre de la forme 1111...111. Et cela vaut quelque soit le nombre de bits n. La représentation de -1 est similaire, peu importe que l'on utilise des nombres de 16 bits, 32 bits, 64 bits, etc.

Voyons maintenant le cas des puissance de deux, par exemple 2, 4, 8, 16, etc. Leur complément à deux vaut :

Nous avions vu précédemment dans le chapitre que ces nombres sont de la forme 11111...0000. En clair, des nombres dont les x bits de poids faible sont à 0, et tous les autres bits sont à 1. Par exemple, le complément à deux de -2 est un nombre dont les bits sont à 1, sauf le bit de poids faible. De même, le complément à deux de -4 a tous ses bits à 1, sauf les deux bits de poids faible. Et le complément à deux de -8 a tous ses bits à 1, sauf les trois bits de poids faible. Pour résumer, tout nombre de la forme a tous ses bits à 1, sauf les x bits de poids faible qui sont à 0.

Exemple avec des nombres de 8 bits
-1 1111 1111
-2 1111 1110
-4 1111 1100
-8 1111 1000
-16 1111 0000
-32 1110 0000
-64 1100 0000
-128 1000 0000
0 / - 256 0000 0000

Le calcul du complément à deux : seconde méthode

[modifier | modifier le wikicode]

Il existe une seconde méthode pour calculer le complément à deux d'un nombre, la voici. Pour les nombres positifs, encodez-les comme en binaire normale. Pour les nombres négatifs, faites pareil, puis inversez tous les bits, avant d'ajouter un. La procédure est identique à celle du complément à un, sauf que l'on incrémente le résultat final. Et ce n'est pas une coïncidence, comme nous allons le voir immédiatement.

Pour comprendre pourquoi la méthode marche, repartons de la formule précédente . Elle peut se reformuler comme suit :

La valeur est par définition un nombre dont tous les bits sont à 1. À cette valeur, on soustrait un nombre dont certains bits sont à 1 et d'autres à 0. En clair, pour chaque colonne, on a deux possibilités : soit on doit faire la soustraction , soit la soustraction . Or, les règles de l'arithmétique binaire disent que et . En regardant attentivement, on se rend compte que le bit du résultat est l'inverse du bit de départ. De plus, les deux cas ne donnent pas de retenue : le calcul pour chaque bit n'influence pas les bits voisins.

Le terme est donc le complément à un du nombre N, un nombre égal à sa représentation en binaire dont tous les bits sont inversés. Notons le nombre formé en inversant tous les bits de N. On a alors :

En clair, le complément à deux s'obtient en prenant le complément à un et en ajoutant 1. Dit autrement, il faut prendre le nombre N, en inverser tous les bits et ajouter 1.

Une autre manière équivalente consiste à faire le calcul suivant :

On prend le nombre dont on veut le complément, on soustrait 1 et on inverse les bits.

Notons que tout ce qui a été dit plus haut marche aussi pour le complément à un, avec cependant une petite différence : la valeur haute de débordement n'est pas la même, ce qui change les calculs. Pour des nombres codés sur bits, la valeur haute de débordement est égale à en complément à deux, alors qu'elle est de en complément à un. De ce fait, la gestion des débordements est plus simple en complément à deux.

L'extension de signe

[modifier | modifier le wikicode]

Dans les ordinateurs, tous les nombres sont codés sur un nombre fixé et constant de bits. Ainsi, les circuits d'un ordinateur ne peuvent manipuler que des nombres de 4, 8, 12, 16, 32, 48, 64 bits, suivant la machine. Si l'on veut utiliser un entier codé sur 16 bits et que l'ordinateur ne peut manipuler que des nombres de 32 bits, il faut bien trouver un moyen de convertir le nombre de 16 bits en un nombre de 32 bits, sans changer sa valeur et en conservant son signe. Cette conversion d'un entier en un entier plus grand, qui conserve valeur et signe s'appelle l'extension de signe.

L'extension de signe des nombres positifs consiste à remplir les bits de poids fort avec des 0 jusqu’à arriver à la taille voulue : c'est la même chose qu'en décimal, où rajouter des zéros à gauche d'un nombre positif ne changera pas sa valeur. Pour les nombres négatifs, il faut remplir les bits à gauche du nombre à convertir avec des 1, jusqu'à obtenir le bon nombre de bits : par exemple, 1000 0000 (-128 codé sur 8 bits) donnera 1111 1111 1000 000 après extension de signe sur 16 bits. L'extension de signe d'un nombre codé en complément à 2 se résume donc en une phrase : il faut recopier le bit de poids fort de notre nombre à convertir à gauche de celui-ci jusqu’à atteindre le nombre de bits voulu.

L'explication plus simple tient dans la manière de coder le bit de poids fort. Prenons l'exemple de la conversion d'un entier de 5 bits en un entier de 8bits. Les 4 bits de poids faible ont un poids positif (on les additionne), alors que le bit de poids fort a un poids négatif. Le nombre encodé vaut : . La valeur encodée sur 4 bits reste la même après extension de signe, car les poids des bits de poids faible ne changent pas. Par contre, le bit de poids fort change. Sur 8 bits, la valeur -16 est encodée par un nombre de la forme 1111 0000. En remplaçant le bit de poids fort par sa valeur calculée sur plus de bits, on remarque que les bits de poids fort ont été remplacés par des 1.

La représentation négabinaire

[modifier | modifier le wikicode]

Enfin, il existe une dernière méthode, assez simple à comprendre, appelée représentation négabinaire. Dans cette méthode, les nombres sont codés non en base 2, mais en base -2. Oui, vous avez bien lu : la base est un nombre négatif. Dans les faits, la base -2 est similaire à la base 2 : il y a toujours deux chiffres (0 et 1), et la position dans un chiffre indique toujours par quelle puissance de 2 il faut multiplier, sauf qu'il faudra ajouter un signe moins une fois sur 2. Concrètement, les puissances de -2 sont les suivantes : 1, -2, 4, -8, 16, -32, 64, etc. En effet, un nombre négatif multiplié par un nombre négatif donne un nombre positif, ce qui fait qu'une puissance sur deux est négative, alors que les autres sont positives. Ainsi, on peut représenter des nombres négatifs, mais aussi des nombres positifs dans une puissance négative.

Par exemple, la valeur du nombre noté 11011 en base -2 s'obtient comme suit :

-32 16 -8 4 -2 1
1 1 1 0 1 1

Sa valeur est ainsi de (−32×1)+(16×1)+(−8×1)+(4×0)+(−2×1)+(1×1)=−32+16−8−2+1=−25.

Les nombres à virgule

[modifier | modifier le wikicode]

On sait donc comment sont stockés nos nombres entiers dans un ordinateur. Néanmoins, les nombres entiers ne sont pas les seuls nombres que l'on utilise au quotidien : il nous arrive d'en utiliser à virgule. Notre ordinateur n'est pas en reste : il est lui aussi capable de manipuler de tels nombres. Dans les grandes lignes, il peut utiliser deux méthodes pour coder des nombres à virgule en binaire : La virgule fixe et la virgule flottante.

Les nombres à virgule fixe

[modifier | modifier le wikicode]

La méthode de la virgule fixe consiste à émuler les nombres à virgule à partir de nombres entiers. Un nombre à virgule fixe est codé par un nombre entier proportionnel au nombre à virgule fixe. Pour obtenir la valeur de notre nombre à virgule fixe, il suffit de diviser l'entier servant à le représenter par le facteur de proportionnalité. Par exemple, pour coder 1,23 en virgule fixe, on peut choisir comme « facteur de conversion » 1000, ce qui donne l'entier 1230.

Généralement, les informaticiens utilisent une puissance de deux comme facteur de conversion, pour simplifier les calculs. En faisant cela, on peut écrire les nombres en binaire et les traduire en décimal facilement. Pour l'exemple, cela permet d'écrire des nombres à virgule en binaire comme ceci : 1011101,1011001. Et ces nombres peuvent se traduire en décimal avec la même méthode que des nombres entier, modulo une petite différence. Comme pour les chiffres situés à gauche de la virgule, chaque bit situé à droite de la virgule doit être multiplié par la puissance de deux adéquate. La différence, c'est que les chiffres situés à droite de la virgule sont multipliés par une puissance négative de deux, c'est à dire par , , , , , ...

Cette méthode est assez peu utilisée de nos jours, quoiqu'elle puisse avoir quelques rares applications relativement connue. Un bon exemple est celui des banques : les sommes d'argent déposées sur les comptes ou transférées sont codés en virgule fixe. Les sommes manipulées par les ordinateurs ne sont pas exprimées en euros, mais en centimes d'euros. Et c'est une forme de codage en virgule fixe dont le facteur de conversion est égal à 100. La raison de ce choix est que les autres méthodes de codage des nombres à virgule peuvent donner des résultats imprécis : il se peut que les résultats doivent être tronqués ou arrondis, suivant les opérandes. Cela n'arrive jamais en virgule fixe, du moins quand on se limite aux additions et soustractions.

Les nombres flottants

[modifier | modifier le wikicode]

Les nombres à virgule fixe ont aujourd'hui été remplacés par les nombres à virgule flottante, où le nombre de chiffres après la virgule est variable. Le codage d'un nombre flottant est basée sur son écriture scientifique. Pour rappel, en décimal, l’écriture scientifique d'un nombre consiste à écrire celui-ci comme un produit entre un nombre et une puissance de 10. Ce qui donne :

, avec

Le nombre est appelé le significande et il est compris entre 1 (inclus) et 10 (exclu). Cette contrainte garantit que l'écriture scientifique d'un nombre est unique, qu'il n'y a qu'une seule façon d'écrire un nombre en notation scientifique. Pour cela, on impose le nombre de chiffre à gauche de la virgule et le plus simple est que celui-ci soit égal à 1. Mais il faut aussi que celui-ci ne soit pas nul. En effet, si on autorise de mettre un 0 à gauche de la virgule, il y a plusieurs manières équivalentes d'écrire un nombre. Ces deux contraintes font que le significande doit être égal ou plus grand que 1, mais strictement inférieur à 10. Par contre, on peut mettre autant de décimales que l'on veut.

En binaire, c'est la même chose, mais avec une puissance de deux. Cela implique de modifier la puissance utilisée : au lieu d'utiliser une puissance de 10, on utilise une puissance de 2.

, avec

Le significande est aussi altéré, au même titre que la puissance, même si les contraintes sont similaires à celles en base 10. En effet, le nombre ne possède toujours qu'un seul chiffre à gauche de la virgule, comme en base 10. Vu que seuls deux chiffres sont possibles (0 et 1) en binaire, on s'attend à ce que le chiffre situé à gauche de la virgule soit un zéro ou un 1. Mais rappelons que le chiffre à gauche doit être non-nul, pour les mêmes raisons qu'en décimal. En clair, le significande a forcément un bit à 1 à gauche de la virgule. Pour récapituler, l'écriture scientifique binaire d'un nombre consiste à écrire celui-ci sous la forme :

, avec

La partie fractionnaire du nombre , qu'on appelle la mantisse.

Écriture scientifique (anglais).

Traduire un nombre en écriture scientifique binaire

[modifier | modifier le wikicode]

Pour déterminer l'écriture scientifique en binaire d'un nombre quelconque, la procédure dépend de la valeur du nombre en question. Tout dépend s'il est dans l'intervalle , au-delà de 2 ou en-dessous de 1.

  • Pour un nombre entre 1 (inclus) et 2 (exclu), il suffit de le traduire en binaire. Son exposant est 0.
  • Pour un nombre au-delà de 2, il faut le diviser par 2 autant de fois qu'il faut pour qu'il rentre dans l’intervalle . L'exposant est alors le nombre de fois qu'il a fallu diviser par 2.
  • Pour un nombre plus petit que 1, il faut le multiplier par 2 autant de fois qu'il faut pour qu'il rentre dans l’intervalle . L'exposant se calcule en prenant le nombre de fois qu'il a fallu multiplier par 2, et en prenant l'opposé (en mettant un signe - devant le résultat).

Le codage des nombres flottants et la norme IEEE 754

[modifier | modifier le wikicode]

Pour coder cette écriture scientifique avec des nombres, l'idée la plus simple est d'utiliser trois nombres, pour coder respectivement la mantisse, l'exposant et un bit de signe. Coder la mantisse implique que le bit à gauche de la virgule vaut toujours 1, mais nous verrons qu'il y a quelques rares exceptions à cette règle. Quelques nombres flottants spécialisés, les dénormaux, ne sont pas codés en respectant les règles pour le significande et ont un 0 à gauche de la virgule. Un bon exemple est tout simplement la valeur zéro, que l'on peut coder en virgule flottante, mais seulement en passant outre les règles sur le significande. Toujours est-il que le bit à gauche de la virgule n'est pas codé, que ce soit pour les flottants normaux ou les fameux dénormaux qui font exception. On verra que ce bit peut se déduire en fonction de l'exposant utilisé pour encoder le nombre à virgule, ce qui lui vaut le nom de bit implicite. L'exposant peut être aussi bien positif que négatif (pour permettre de coder des nombres très petits), et est encodé en représentation par excès sur n bits avec un biais égal à .

IEEE754 Format Général

Le standard pour le codage des nombres à virgule flottante est la norme IEEE 754. Cette norme va (entre autres) définir quatre types de flottants différents, qui pourront stocker plus ou moins de valeurs différentes.

Classe de nombre flottant Nombre de bits utilisés pour coder un flottant Nombre de bits de l'exposant Nombre de bits pour la mantisse Décalage
Simple précision 32 8 23 127
Double précision 64 11 52 1023
Double précision étendue 80 ou plus 15 ou plus 64 ou plus 16383 ou plus

IEEE754 impose aussi le support de certains nombres flottants spéciaux qui servent notamment à stocker des valeurs comme l'infini. Commençons notre revue des flottants spéciaux par les dénormaux, aussi appelés flottants dénormalisés. Ces flottants ont une particularité : leur bit implicite vaut 0. Ces dénormaux sont des nombres flottants où l'exposant est le plus petit possible. Le zéro est un dénormal particulier dont la mantisse est nulle. Au fait, remarquez que le zéro est codé deux fois à cause du bit de signe : on se retrouve avec un -0 et un +0.

Bit de signe Exposant Mantisse
0 ou 1 Valeur minimale (0 en binaire) Mantisse différente de zéro (dénormal strict) ou égale à zéro (zéro)

Fait étrange, la norme IEEE754 permet de représenter l'infini, aussi bien en positif qu'en négatif. Celui-ci est codé en mettant l'exposant à sa valeur maximale et la mantisse à zéro. Et le pire, c'est qu'on peut effectuer des calculs sur ces flottants infinis. Mais cela a peu d'utilité.

Bit de signe Exposant Mantisse
0 ou 1 Valeur maximale Mantisse égale à zéro

Mais malheureusement, l'invention des flottants infinis n'a pas réglé tous les problèmes. Par exemple, quel est le résultat de  ? Ou encore  ? Autant prévenir tout de suite : mathématiquement, on ne peut pas savoir quel est le résultat de ces opérations. Pour pouvoir résoudre ces calculs, il a fallu inventer un nombre flottant qui signifie « je ne sais pas quel est le résultat de ton calcul pourri ». Ce nombre, c'est NaN. NaN est l'abréviation de Not A Number, ce qui signifie : n'est pas un nombre. Ce NaN a un exposant dont la valeur est maximale, mais une mantisse différente de zéro. Pour être plus précis, il existe différents types de NaN, qui diffèrent par la valeur de leur mantisse, ainsi que par les effets qu'ils peuvent avoir. Malgré son nom explicite, on peut faire des opérations avec NaN, mais cela ne sert pas vraiment à grand chose : une opération arithmétique appliquée avec un NaN aura un résultat toujours égal à NaN.

Bit de signe Exposant Mantisse
0 ou 1 Valeur maximale Mantisse différente de zéro

Les arrondis et exceptions

[modifier | modifier le wikicode]

La norme impose aussi une gestion des arrondis ou erreurs, qui arrivent lors de calculs particuliers. En voici la liste :

Nom de l’exception Description
Invalid operation Opération qui produit un NAN. Elle est levée dans le cas de calculs ayant un résultat qui est un nombre complexe, ou quand le calcul est une forme indéterminée. Pour ceux qui ne savent pas ce que sont les formes indéterminées, voici en exclusivité la liste des calculs qui retournent NaN : , , , , .
Overflow Résultat trop grand pour être stocké dans un flottant. Le plus souvent, on traite l'erreur en arrondissant le résultat vers Image non disponible ;
Underflow Pareil que le précédent, mais avec un résultat trop petit. Le plus souvent, on traite l'erreur en arrondissant le résultat vers 0.
Division par zéro Le nom parle de lui-même. La réponse la plus courante est de répondre + ou - l'infini.
Inexact Le résultat ne peut être représenté par un flottant et on doit l'arrondir.

La gestion des arrondis pose souvent problème. Pour donner un exemple, on va prendre le nombre 0,1. En binaire, ce nombre s'écrit comme ceci : 0,1100110011001100... et ainsi de suite jusqu'à l'infini. Notre nombre utilise une infinité de décimales. Bien évidemment, on ne peut pas utiliser une infinité de bits pour stocker notre nombre et on doit impérativement l'arrondir. Comme vous le voyez avec la dernière exception, le codage des nombres flottants peut parfois poser problème : dans un ordinateur, il se peut qu'une opération sur deux nombres flottants donne un résultat qui ne peut être codé par un flottant. On est alors obligé d'arrondir ou de tronquer le résultat de façon à le faire rentrer dans un flottant. Pour éviter que des ordinateurs différents utilisent des méthodes d'arrondis différentes, on a décidé de normaliser les calculs sur les nombres flottants et les méthodes d'arrondis. Pour cela, la norme impose le support de quatre modes d'arrondis :

  • Arrondir vers + l'infini ;
  • vers - l'infini ;
  • vers zéro ;
  • vers le nombre flottant le plus proche.

Les nombres flottants logarithmiques

[modifier | modifier le wikicode]

Les nombres flottants logarithmiques sont une spécialisation des nombres flottants IEEE754, ou tout du moins une spécialisation des flottants écrits en écriture scientifique. Un nombre logarithmique est donc composé d'un bit de signe et d'un exposant, sans mantisse. La mantisse est totalement implicite : tous les flottants logarithmiques ont la même mantisse, qui vaut 1.

Pour résumer, il ne reste que l'exposant, qui est tout simplement le logarithme en base 2 du nombre encodé, d'où le nombre de codage flottant logarithmique donné à cette méthode. Attention toutefois : l'exposant est ici un nombre fractionnaire, codé en virgule fixe. Le choix d'un exposant fractionnaire permet de représenter pas mal de nombres de taille diverses.

Bit de signe Exposant
Représentation binaire 0 01110010101111
Représentation décimale + 1040,13245464

L'utilité de cette représentation est de simplifier certains calculs, comme les multiplications, divisions, puissances, etc. En effet, les mathématiques nous disent que le logarithme d'un produit est égal à la somme des logarithmes : . Or, il se trouve que les ordinateurs sont plus rapides pour faire des additions/soustractions que pour faire des multiplications/divisions. Donc, la représentation logarithmique permet de remplacer les multiplications/divisions par des additions/soustractions, plus simples et plus rapides pour l'ordinateur.

Évidemment, les applications des flottants logarithmiques sont rares, limitées à quelques situations bien précises (traitement d'image, calcul scientifique spécifiques).

Les nombres à virgule non-représentables en binaire

[modifier | modifier le wikicode]

Quel que soit la façon de représenter les nombres à virgules, il existe des nombres qui ne peuvent être représentés de manière exacte, à savoir avec un nombre fini de décimales après la virgule. En soi, ce n'est pas spécifique au binaire, on a la même chose en décimal. Par exemple, la fraction 1/3 en décimal s'écrit 0.3333333..., avec une infinité de 3. La même chose existe en binaire, mais pour des nombres différents.

Déjà, évacuons le cas des nombres irrationnels, à savoir les nombres qui ne peuvent pas s'écrire sous la forme d'une fraction, comme ou . Ils ont une infinité de décimales que ce soit en binaire, en décimal, en hexadécimal, ou autre. Ils ne sont pas représentables avec un nombre fini de décimales quelle que soit la base utilisée. Concentrons-nous sur des nombres qui ne sont pas dans ce cas, et qui ont un nombre fini ou infini de décimales.

Le passage de la base 10 à la base 2 change le nombre de décimales, et peut faire passer d'un nombre fini de décimales à un nombre infini. Par exemple, est représenté en binaire avec une séquence infinie de bits : . Les nombres étant en base binaire et représenté avec un nombre limité de bits, il existe certains nombres décimaux triviaux qui ne sont pas représentables avec un nombre fini de décimales.

Et la réciproque n'est pas vraie : tout nombre binaire avec un nombre fini de décimales en binaire est représentable avec un nombre fini de décimales en base 10. Ceci est lié à la décomposition en facteur premier des bases utilisées :

Le nombre 10 possède tous les facteurs premiers de 2, mais 2 n'a pas de 5 dans sa décomposition.

Les encodages hybrides entre décimal et binaire

[modifier | modifier le wikicode]

Dans cette section, nous allons voir les représentations qui mixent binaire et décimal. Il s'agit en réalité d'encodage qui permettent de manipuler des nombres codés en décimal, mais les chiffres sont codés en utilisant des bits. L'encodage Binary Coded Decimal est le plus connu de cette catégorie, mais il y en a quelques autres, qui sont moins connus. De telles représentations étaient très utilisées au début de l'informatique, mais sont aujourd'hui tombées en désuétude. Pour désigner ces encodages, nous parlerons d'encodages bit-décimaux : décimaux pour préciser qu'ils encodent des nombres codés en décimal, bit pour préciser que les chiffres sont codés avec des bits.

De tels encodages étaient utilisés sur les tous premiers ordinateurs pour faciliter le travail des programmeurs, mais aussi sur les premières calculettes. Ils sont utiles dans les applications où on doit manipuler chaque chiffre décimal séparément des autres. Un exemple classique est celui d'une horloge digitale. Ils ont aussi l'avantage de bien se marier avec les nombres à virgule. Les représentations des nombres à virgule fixe ou flottante ont le défaut que des arrondis peuvent survenir. Par exemple, la valeur 0,2 est codée comme suit en binaire normal : 0.00110011001100... et ainsi de suite jusqu’à l'infini. Avec les encodages bit-décimaux, on n'a pas ce problème, 0,2 étant codé 0000 , 0010.

Il a existé des ordinateurs qui travaillaient uniquement avec de tels encodages, appelés des ordinateurs décimaux. Ils étaient assez courants entre les années 60 et 70, même s'ils ne représentent pas la majorité des architectures de l'époque. Avec eux, la mémoire n'était pas organisée en octets, mais elle stockait des chiffres décimaux codés sur 5-6 bits. Le processeur faisait des calculs sur des chiffres bit-décimaux directement.

Leur grand avantage est leur très bonne performance dans les taches de bureautique, de comptabilité, et autres. Les processeurs de l'époque recevaient des entiers codés en bit-décimal de la part des entrées-sorties, et devaient les traiter. Les processeurs binaires devaient faire des conversions décimal-binaire pour communiquer avec les entrées-sorties, mais pas les processeurs décimaux. Le gain en performance pouvait être substantiel dans certaines applications.

Les ordinateurs décimaux se classent en deux sous-types bien précis. Les premiers gèrent des entiers qui ont un nombre de chiffres fixes. Par exemple, l'IBM 7070 gérait des entiers de 10 chiffres, plus un signe +/- pour les entiers signés. Le processeur faisait les calculs directement en bit-décimal, il gérait des entiers faisant environ 10-15 chiffres décimaux et savait faire des calculs avec de tels nombres. Le second sous-type effectue les calculs chiffre par chiffre et géraient des nombres de taille variable, sans limite de chiffres ! Nous reparlerons de ces derniers dans un chapitre ultérieur, quand nous parlerons de la différence entre byte et mot. Pour le moment, ne gardez à l'esprit que les processeurs gérant un nombre fixe de chiffres décimaux, plus simples à comprendre.

Le Binary Coded Decimal

[modifier | modifier le wikicode]

Le Binary Coded Decimal, abrévié BCD, est une représentation qui mixe binaire et décimal. Avec cette représentation, les nombres sont écrits en décimal, comme nous en avons l'habitude dans la vie courante, sauf que chaque chiffre décimal est directement traduit en binaire sur 4 bits. Prenons l'exemple du nombre 624 : le 6, le 2 et le 4 sont codés en binaire séparément, ce qui donne 0110 0010 0100.

Codage BCD
Nombre encodé (décimal) BCD
0 0 0 0 0
1 0 0 0 1
2 0 0 1 0
3 0 0 1 1
4 0 1 0 0
5 0 1 0 1
6 0 1 1 0
7 0 1 1 1
8 1 0 0 0
9 1 0 0 1

On peut remarquer que 4 bits permettent de coder 16 valeurs, là où il n'y a que 10 chiffres. Dans le BCD proprement dit, les combinaisons de bits qui correspondent à 10, 11, 12, 13, 14 ou 15 ne sont tout simplement pas prises en compte. Sur quelques ordinateurs, ces combinaisons codent des chiffres décimaux en double : certains chiffres pouvaient être codés de deux manières différentes. Il est aussi possible d'utiliser ces valeurs pour coder quelque chose d'autre que des chiffres. Par exemple, il est possible de les utiliser pour coder un signe + ou -, afin de gérer les entiers relatifs. Une autre possibilité, complémentaire de la précédente, utilise ces valeurs en trop pour coder une virgule, afin de gérer les nombres non-entiers. Les possibilités sont nombreuses.

Le support du BCD implique souvent que le processeur supporte des opérations BCD, à savoir des opérations capables de travailler sur des opérandes en BCD et de donner un résultat en BCD. Il faut bien faire la différence entre les opérations en binaire et les opérations en BCD. Par exemple, on n'effectue pas une addition de la même manière en binaire et en décimal/BCD, même si les grandes lignes sont presque identiques. Les différences font que les processeurs doivent avoir des opérations différentes pour les deux encodages, de la même manière que les processeurs gèrent les opérations sur les flottants et les entiers séparément.

Le support du codage BCD est abandonné de nos jours, c'est surtout quelque chose qu'on trouve sur les anciens processeurs. Les architectures 8 et 16 bits supportaient à la fois des opérations binaires et des opérations BCD. Mais il a aussi existé une méthode intermédiaire, qui utilisait des additions binaires normales sur des opérandes BCD. L'idée est que le résultat de l'addition est incorrect, car les valeurs 10 à 15 peuvent apparaître comme chiffre dans le résultat. Mais on peut le corriger pour obtenir le résultat exact en BCD. Pour résumé, les processeurs faisaient des additions en binaire, et corrigeaient le résultat avec une opération spécifique pour obtenir un résultat en BCD. Nous en reparlerons dans le chapitre sur le langage machine et l'assembleur, dans lequel nous étudierons les différentes opérations que supporte un processeur.

Les encodages compacts du BCD

[modifier | modifier le wikicode]

Des variantes du BCD visent à réduire le nombre de bits utilisés pour encoder un nombre décimal. Nous allons les appeler les encodages BCD compacts. Avec certaines variantes, on peut utiliser seulement 7 bits pour coder deux chiffres décimaux, au lieu de 8 bits en BCD normal. Idem pour les nombres à 3 chiffres décimaux, qui prennent 10 bits au lieu de 12. Cette économie est réalisée par une variante du BCD assez compliquée, appelée l'encodage Chen-Ho. Une alternative, appelée le Densely Packed Decimal, arrive à une compression identique, mais avec quelques avantages au niveau de l'encodage. Ces encodages sont cependant assez compliqués à expliquer, surtout à ce niveau du cours, aussi je me contente de simplement mentionner leur existence.

Les encodages BCD compacts sont utilisés par les programmeurs pour stocker des données dans un fichier, en mémoire, mais guère plus. Ils ne sont pas gérés par le processeur directement, on ne peut pas faire de calculs avec, le processeur ne gére pas d'opération BCD supportant de tels encodages. C'est théoriquement possible, mais ça n'a jamais été implémenté dans un processeur, le cout en circuit n'en valait pas la chandelle. Pour faire des calculs sur des nombres en BCD compact, on doit les décompresser et les convertir en BCD normal ou en binaire, puis faire les calculs avec des opérations BCD/binaire usuelles.

L'encodage Excess-3

[modifier | modifier le wikicode]

La représentation Excess-3 (XS-3) est une variante du BCD, qui a autrefois était utilisée sur d'anciens ordinateurs décimaux. Il s'agit d'une sorte d'hybride entre une représentation par excès et BCD. Chaque chiffre décimal est codé sur plusieurs bits en utilisant une représentation binaire par excès. Le biais est de 3 pour la représentation XS-3, il en existe des variantes avec un excès de 4, 5, mais elles sont moins utilisées. Avec elle, la conversion d'un chiffre décimal se fait comme suit : on prend le chiffre décimal, on ajoute 3, puis on traduit en binaire.

Codage en Excess-3
Décimal Binaire
-3 0 0 0 0
-2 0 0 0 1
-1 0 0 1 0
0 0 0 1 1
1 0 1 0 0
2 0 1 0 1
3 0 1 1 0
4 0 1 1 1
5 1 0 0 0
6 1 0 0 1
7 1 0 1 0
8 1 0 1 1
9 1 1 0 0
10 1 1 0 1
11 1 1 1 0
12 1 1 1 1

L'avantage de cette représentation est que l'on peut facilement calculer les soustractions en utilisant une méthode bien précise (celle du complément à 10, je ne détaille pas plus). Le défaut est que le calcul des additions est légèrement plus complexe.

L'XS-3 a été utilisé sur quelques ordinateurs décimaux assez anciens, notamment sur l'UNIVAC I et II.

Le code 2 parmi 5

[modifier | modifier le wikicode]

Les codes 2 parmi 5 permettent d'encoder 10 chiffres décimaux sur 5 bits. Le code garantit que sur les 5 bits, deux sont à 1, les trois restants sont à 0, les bits à 0/1 n'étant pas les mêmes selon le chiffre encodé et le code utilisé. Il existe plusieurs codes 2 parmi 5, la plupart sont utilisés sur les codes barres dans les magasins, mais ce ne sont pas ceux utilisés dans les ordinateurs d'antan.

L'ordinateur IBM 7070 et ses déclinaisons utilisait un code 2 parmi 5 qui codait les 10 chiffres décimaux, ainsi que les signes + et -, et un caractère . Il gérait des entiers décimaux codés sur 10 chiffres plus un caractère +/- pour le signe. Les caractères pour le texte étaient codés en utilisant un code à deux chiffres décimaux, c’est-à-dire sur 10 bits.

0 1 2 3 4 5 6 7 8 9 A - +
01100 11000 10100 10010 01010 00110 10001 01001 00101 00011 1––10 1––01 0––11

Les codes bi-quinaires

[modifier | modifier le wikicode]

Les codes bi-quinaires codent des chiffres sur 7 bits. Les 7 bits sont découpés en deux sections : 2 bits pour la première, 5 pour l'autre. Les 5 bits permettent de compter de 0 à 4 ou de 5 à 9, un seul bit est à 1, les quatre autres sont à 0. Le bit mis à 1 indique la valeur encodée. Les deux bits restants déterminent si la valeur est inférieure ou supérieure/égale à 5, un seul des deux bits est à 1. Voici les deux encodages les plus courants :

Code bi-quinaire basique
Code bi-quinaire réfléchi.

L'originalité de ce codage est qu'il permet de facilement compter sur ses doigts : on a deux mains, cinq doigts. De nombreuses langues utilisent ce système pour coder des nombres, comme le Wolof. Et ce système est utilisé dans certains pays pour compter sur ses doigts, voire dans la vie de tous les jours. Et il a été utilisé sur d'anciens ordinateurs, dont l'IBM 650, l'UNIVAC Solid State et le UNIVAC LARC.

Pour rentrer vraiment dans le détail, voici les encodages utilisés sur ces machines. La lecture est facultative, il est possible de passer directement à la section suivante :

IBM 650 Remington Rand 409 UNIVAC Solid State UNIVAC LARC
Chiffre 1357-9 bits 05-01234 p-5-421 bits p-5-qqq bits
0 10-10000 0000-0 1-0-000 1-0-000
1 10-01000 1000-0 0-0-001 0-0-001
2 10-00100 1000-1 0-0-010 1-0-011
3 10-00010 0100-0 1-0-011 0-0-111
4 10-00001 0100-1 0-0-100 1-0-110
5 01-10000 0010-0 0-1-000 0-1-000
6 01-01000 0010-1 1-1-001 1-1-001
7 01-00100 0001-0 1-1-010 0-1-011
8 01-00010 0001-1 0-1-011 1-1-111
9 01-00001 0000-1 1-1-100 0-1-110

Les encodages alternatifs

[modifier | modifier le wikicode]

Outre le binaire et le BCD que nous venons de voir, il existe d'autres manières de coder des nombres en binaires. Et nous allons les aborder dans cette section. Parmi celle-ci, nous parlerons du code Gray, de la représentation one hot, du unaire et du ternaire. Nous en parlons car elles seront utiles dans la suite du cours, bien que de manière assez limitée. Autant nous passerons notre temps à parler du binaire normal, autant les représentations que nous allons voir sont aujourd'hui utilisées dans des cas assez spécifiques. Et elles sont plus courantes que vous ne le pensez.

Le code Gray est un encodage binaire qui a une particularité très intéressante : deux nombres consécutifs n'ont qu'un seul bit de différence. Pour exemple, voici ce que donne le codage des 8 premiers entiers sur 3 bits :

Décimal Binaire naturel Codage Gray
0 000 000
1 001 001
2 010 011
3 011 010
4 100 110
5 101 111
6 110 101
7 111 100

Les utilisations du code Gray sont assez nombreuses, bien qu'on n'en croise pas tous les jours. Un exemple : le code Gray est très utile dans certains circuits appelés les compteurs, qui mémorisent un nombre et l'incrémentent (+1) ou le décrémentent (-1) suivant les besoins de l'utilisateur. Il est aussi utilisé dans des scénarios difficiles à expliquer ici (des codes correcteurs d'erreur ou des histoires de passages de domaines d'horloge). Mais le point important est que ce code sera absolument nécessaire dans quelques chapitres, quand nous parlerons des tables de Karnaugh, un concept important pour la conception de circuits électroniques. Ne passez pas à côté de cette section.

Pour construire ce code Gray, on peut procéder en suivant plusieurs méthodes, les deux plus connues étant la méthode du miroir et la méthode de l'inversion.

Construction d'un code gray par la méthode du miroir.

La méthode du miroir est relativement simple. Pour connaître le code Gray des nombres codés sur n bits, il faut :

  • partir du code Gray sur n-1 bits ;
  • symétriser verticalement les nombres déjà obtenus (comme une réflexion dans un miroir) ;
  • rajouter un 0 au début des anciens nombres, et un 1 au début des nouveaux nombres.

Il suffit de connaître le code Gray sur 1 bit pour appliquer la méthode : 0 est codé par le bit 0 et 1 par le bit 1.

Une autre méthode pour construire la suite des nombres en code Gray sur n bits est la méthode de l'inversion. Celle-ci permet de connaître le codage du nombre n à partir du codage du nombre n-1, comme la méthode du dessus. On part du nombre 0, systématiquement codé avec uniquement des zéros. Par la suite, on décide quel est le bit à inverser pour obtenir le nombre suivant, avec la règle suivante :

  • si le nombre de 1 est pair, il faut inverser le dernier chiffre.
  • si le nombre de 1 est impair, il faut localiser le 1 le plus à droite et inverser le chiffre situé à sa gauche.

Pour vous entraîner, essayez par vous-même avec 2, 3, voire 5.

Les représentations one-hot et unaire

[modifier | modifier le wikicode]

La représentation one-hot et la représentation unaire sont deux représentations assez liées, mais légèrement différentes. Avec les deux représentations, les nombres sont codés avec un seul bit à 1, tous les autres sont à 0. Cela laisse peu de valeurs possibles : pour N bits, on peut encoder seulement N valeurs, voire N+1 valeurs en comptant le 0.

La représentation unaire est assez intuitive. Un zéro est codé en mettant tous les bits du nombre à zéro, la valeur 1 est encodée avec la valeur 000...0001, le deux avec 000...0010, etc. Pour résumer, le nombre N est encodé en mettant le énième bit à 1, à l'exception du zéro qui est encodé...par un zéro. Elle permet d'encoder N+1 valeurs sur N bits, car le zéro est encodé à part.

Décimal Binaire Unaire
0 000 0000 0000
1 001 0000 0001
2 010 0000 0010
3 011 0000 0100
4 100 0000 1000
5 101 0001 0000
6 110 0010 0000
7 111 0100 0000
8 1000 1000 0000

La représentation one-hot utilise N bits pour encoder N valeurs, zéro inclus. La différence avec le unaire est que dans cette représentation, le zéro est n'est PAS codé en mettant tous les bits à 0. En one-hot, la valeur 00000...0000 n'encode aucune valeur, c'est une valeur interdite. A la place, le zéro est codé en mettant le bit de poids faible à 1. Et pour ça, tout est décalé d'un cran comparé à la représentation unaire. Par exemple, si le bit de poids faible (celui de poids 0) est à 1, alors on code la valeur 0. Si le bit de poids numéro 1 est à 1, alors on code la valeur 1. Et ainsi de suite. En conséquence, on peut coder une valeur en moins avec le même nombre de bits.

Décimal Binaire One-hot
0 000 00000001
1 001 00000010
2 010 00000100
3 011 00001000
4 100 00010000
5 101 00100000
6 110 01000000
7 111 10000000

L'utilité de ces deux représentations n'est pas évidente. Mais sachez qu'elle le deviendra quand nous parlerons des circuits appelés les "compteurs", tout comme ce sera le cas pour le code Gray. Elles sont très utilisées dans des circuits appelés des machines à état, qui doivent incorporer des circuits compteurs efficients. Et ces représentations permettent d'avoir des circuits pour compter qui sont très simples, efficaces, rapides et économes en circuits électroniques. La représentation unaire sera aussi utile à la toute fin du cours, dans les chapitres liés à l'exécution dans le désordre. Il en sera fait référence quand nous parlerons de fenêtres d'instruction, d'émission dans l'ordre, de scoreboarding, etc.

Les encodages ternaires

[modifier | modifier le wikicode]

Après avoir vu le binaire, il est temps de finir en beauté avec le ternaire ! Le ternaire n'a en soi rien à voir avec le binaire, c'est un encodage au même titre que le binaire, le décimal, l'hexadécimal, ou autre. Il encode un nombre non pas en base 10 comme le décimal, en base 2 comme le binaire, ou en base 16 comme l'hexadécimal, mais en base 3 ! Il encode un nombre en utilisant non pas des chiffres, ni des bits, mais des trits qui peuvent prendre trois valeurs.

Dans le ternaire normal, les trois valeurs d'un trit sont 0, 1 et 2. L'encodage multiple un trit par une puissance de 3, à savoir 1, 3, 9, 27, etc. On parle alors de ternaire non-équilibré. Inutile de développer plus, le principe est le même qu'en binaire ou en décimal, mais en base 3. De plus, un tel encodage n'est pas très utilisé en informatique, aucun ordinateur ne l’utilise, contrairement aux suivants.

Le ternaire non-équilibré est une variante du ternaire normal, où chaque trit encode les trois valeurs suivantes : 0, 1 et -1. Le trit est donc signé ! L'équivalent en binaire serait d'encoder les trois valeurs sur deux bits : un bit de signe et un bit pour coder 0/1. La différence est qu'utiliser deux bits fait qu'on a un zéro positif et un zéro négatif.

Valeurs encodables par un trit
Ternaire non-équilibré 0 1 2
Ternaire équilibré −1 0 1

Le ternaire non-équilibré a été utilisé sur d'anciens ordinateurs ternaires, dont le plus connu est l'ordinateur ETUN de l'université de Moscou. Il s'agissait de véritables ordinateurs ternaires, qui utilisaient des mémoires et des processeurs ternaires, qui géraient des trits directement. Il ne s'agit pas d'équivalents aux ordinateurs décimaux, qui encodaient des chiffres décimaux en binaire, qui étaient des hybrides entre ordinateurs décimaux et binaire, avec une interface décimale, mais une conception fondamentalement binaire. Les ordinateurs ternaires sont fondamentalement ternaires, il n'y a de bit nulle part dans de tels ordinateurs, seulement des trits !

Les ordinateurs ternaires utilisaient tous le ternaire équilibré. L'avantage de cet encodage est qu'il permet de représenter des entiers positifs comme négatifs, sans compter que certaines opérations sont bien plus simples à implémenter qu'en binaire. Par exemple, calculs l'opposé d'un nombre, son complément, est très simple : il suffit d'inverser tous les trits du nombre : les trits à 1 sont remplacés par des -1, et inversement, les zéros sont laissés tels quels. La soustraction demande de calculer le complément de l'opérande à soustraire, et d'additionner, encore plus simple qu'en complément à deux. L'addition et la multiplication sont plus simples, car la gestion des retenues et des tables de multiplication est légèrement plus simple.


Dans le chapitre précédent, nous avons vu comment l'ordinateur faisait pour coder des nombres. Les nombres en question sont mémorisés dans des mémoires plus ou moins complexes. Les mémoires les plus simples sont des registres, qui mémorisent un nombre. D'autres mémoires plus complexes mémorisent plusieurs nombres, un grand nombre. Et ces mémoires ne sont pas des dispositifs parfaits, elles peuvent subir des corruptions. Les corruptions en question se traduisent le plus souvent par l'inversion d'un bit : un bit censé être à 0 passe à 1, ou inversement. Pour donner un exemple, on peut citer l'incident du 18 mai 2003 dans la petite ville belge de Schaerbeek. Lors d'une élection, la machine à voter électronique enregistra un écart de 4096 voix entre le dépouillement traditionnel et le dépouillement électronique. La faute à un rayon cosmique, qui avait modifié l'état d'un bit de la mémoire de la machine à voter.

Mais qu'on se rassure : certains codages des nombres permettent de détecter et corriger ces erreurs. Les codes de détection et de correction d'erreur, qui ajoutent tous des bits de correction/détection d'erreur aux données. Les bits en question sont calculés à partir des données à transmettre/stocker et servent à détecter et éventuellement corriger toute erreur de transmission/stockage. Plus le nombre de bits ajoutés est important, plus la fiabilité des données sera importante. Ils sont peu utilisées dans les ordinateurs grand public, mais elles sont très importantes dans les domaines demandant des ordinateurs fiables, comme dans l'automobile, l'aviation, le spatial, l'industrie, etc. Et ce chapitre va expliquer ce qu'elles sont, et aussi comment les circuits élaborés permettent de s'en protéger.

Dans ce qui suit, nous parlerons parfois de codes ECC, bien que ce soit un abus de langage : ECC est l'abréviation de Error Correction Code, mais certains de ces codes se contentent de détecter qu'une erreur a eu lieu, sans la corriger. Ceci étant dit, les codes ECC sont utilisés sur les mémoires comme les mémoires RAM, parfois sur les disques durs ou les SSDs, afin d'éviter des corruptions de données. Ils sont aussi utilisés quand on doit transmettre des données, que ce soit sur les bus de communication ou sur un support réseau. Par exemple, les données transmises via internet incorporent un code ECC pour détecter les erreurs de transmission, idem pour les transmissions sur un réseau local.

Le bit de parité

[modifier | modifier le wikicode]

Nous allons commercer par aborder le bit de parité/imparité. Le bit de parité est un bit ajouté à la donnée à mémoriser/transmettre. Sa valeur est telle que le nombre stocké (bit de parité inclus) contient toujours un nombre pair de bits à 1. Ainsi, le bit de parité vaut 0 si le nombre contient déjà un nombre pair de 1, et 1 si le nombre de 1 est impair.

Si un bit s'inverse, quelle qu'en soit la raison, la parité du nombre total de 1 est modifié : ce nombre deviendra impair si un bit est modifié. Et ce qui est valable avec un bit l'est aussi pour 3, 5, 7, et pour tout nombre impair de bits modifiés. Mais tout change si un nombre pair de bit est modifié : la parité ne changera pas. Il permet de détecter des corruptions qui touchent un nombre impair de bits. Si un nombre pair de bit est modifié, il est impossible de détecter l'erreur avec un bit de parité. Ainsi, on peut vérifier si un bit (ou un nombre impair) a été modifié : il suffit de vérifier si le nombre de 1 est impair. Il faut noter que le bit de parité, utilisé seul, ne permet pas de localiser le bit corrompu.

Le bit d'imparité est similaire au bit de parité, si ce n'est que le nombre total de bits doit être impair, et non pair comme avec un bit de parité. Sa valeur est l'inverse du bit de parité du nombre : quand le premier vaut 1, le second vaut 0, et réciproquement. Mais celui-ci n'est pas meilleur que le bit de parité : on retrouve l'impossibilité de détecter une erreur qui corrompt un nombre pair de bits.

Valeurs valides et invalides avec un bit de parité. Les valeurs valides sont en vert, les autres en noir.

Il est maintenant temps de parler de si un bit de parité est efficace ou non. Que ce soit avec un bit de parité ou d'imparité, environ la moitié des valeurs encodées sont invalides. En effet, si on prend un nombre codé sur N bits, bit de parité, inclut, on pourra encoder 2^n valeurs différentes. La moitié d'entre elle aura un bit de parité à 0, l'autre un bit de parité à 1. Et la moitié aura un nombre de bit à 1 qui soit pair, l'autre un nombre impair. En faisant les compte, seules la moitié des valeurs seront valides. Le diagramme ci-contre montre le cas pour trois bits, avec deux bits de données et un bit de parité.

L'octet/mot de parité et ses variantes

[modifier | modifier le wikicode]

L'octet de parité est une extension de la technique du bit de parité, qui s'applique à plusieurs octets. L'idée de base est de calculer un bit de parité par octet, et c'est plus ou moins ce que fait le mot de parité, mais avec quelques subtilités dans les détails.

Illustration du mot de parité.

La technique s'applique en général sur toute donnée qu'on peut découper en blocs d'une taille fixe. Dans les exemples qui vont suivre, les blocs en question seront des octets, pour simplifier les explications, mais il est parfaitement possible de prendre des blocs plus grands, de plusieurs octets. La méthode fonctionne de la même manière. On parle alors de mot de parité et non d'octet de parité.

Le calcul du mot de parité

[modifier | modifier le wikicode]

Le calcul du mot de parité se calcule en disposant chaque octet l'un au-dessus des autres, le tout donnant un tableau dont les lignes sont des octets. Le mot de parité se calcule en calculant le bit de parité de chaque colonne du tableau, et en le plaçant en bas de la colonne. Le résultat obtenu sur la dernière ligne est un octet de parité.

  • 1100 0010 : nombre ;
  • 1000 1000 : nombre ;
  • 0100 1010 : nombre ;
  • 1001 0000 : nombre ;
  • 1000 1001 : nombre ;
  • 1001 0001 : nombre ;
  • 0100 0001 : nombre ;
  • 0110 0101 : nombre ;
  • ------------------------------------
  • 1010 1100 : octet de parité.

Le calcul de l'octet de parité se fait en utilisant des opérations XOR. Pour rappel, une opération XOR est équivalente à une addition binaire dans laquelle on ne tiendrait pas compte des retenues. L'opération prend deux bits et effectue le calcul suivant :

  • 0 0 = 0 ;
  • 0 1 = 1 ;
  • 1 0 = 1 ;
  • 1 1 = 0.

En faisant un XOR entre deux octets, on obtient l'octet de parité des deux octets opérandes. Et cela se généralise à N opérandes : il suffit de faire un XOR entre les opérandes pour obtenir l'octet de parité de ces N opérandes. Il suffit de faire un XOR entre les deux premières opérandes, puis de faire un XOR entre le résultat et la troisième opérande, puis de refaire un XOR entre le nouveau résultat et le quatrième opérande, et ainsi de suite. Le calcul du mot de parité se fait aussi avec des opérations XOR, cela marche au-delà de l'octet.

La récupération des données manquantes/effacées

[modifier | modifier le wikicode]

L'avantage de cette technique est qu'elle permet de reconstituer une donnée manquante. Par exemple, dans l'exemple précédent, si une ligne du calcul disparaissait, on pourrait la retrouver à partir du mot de parité. Il suffit de déterminer, pour chaque colonne, quel valeur 0/1 est compatible avec la valeur du bit de parité associé. C'est d'ailleurs pour cette raison que le mot de parité est utilisé sur les disques durs montés en RAID 3, 5 6, et autres. Grâce à elle, si un disque dur ne fonctionne plus, on peut retirer le disque dur endommagé et reconstituer ses données.

Pour cela, il faut faire faire XOR entre les données non-manquantes et le mot de parité. Pour comprendre pourquoi cela fonctionne, il faut savoir deux choses : faire un XOR entre un nombre et lui-même donne 0, faire un XOR entre une opérande et zéro redonne l'opérande comme résultat. Si on XOR un nombre avec le mot de parité, cela va annuler la présence de ce nombre (son XOR) dans le mot de parité : le résultat correspondra au mot de parité des nombres, nombre xoré exclu. Ce faisant, en faisant un XOR avec tous les nombres connus, ceux-ci disparaîtront du mot de parité, ne laissant que le nombre manquant. Un exemple sera certainement plus parlant.

Prenons le cas où on calcule l'octet de parité de quatre octets nommés O1, O2, O3 et O4, et notant le résultat . On a alors :

Maintenant, imaginons que l'on veuille retrouver la valeur du second octet O2, qui a été corrompu ou perdu. Dans ce cas, on fait un XOR avec O1, O3 et enfin O4 :

On injecte alors l'équation .

On réorganise les termes :

On se rappelle que A XOR A = 0, ce qui simplifie grandement le tout :

On retrouve bien l'octet manquant.

La combinaison d'un mot de parité avec plusieurs bits de parité

[modifier | modifier le wikicode]

Avec un octet/mot de parité, on peut détecter qu'une erreur a eu lieu, mais aussi récupérer un octet/mot effacé. Mais si un bit est modifié, on ne peut pas corriger l'erreur. En effet, on ne sait pas détecter quel octet a été modifié par l'erreur. Maintenant, ajoutons un bit de parité à chaque octet, en plus de l'octet de parité.

Exemple avec quatre octets
Octets Bit de parité de chaque octet
Octet 1 0 0 1 1 1 1 1 1 0
Octet 2 0 0 1 0 1 0 1 1 0
Octet 3 1 1 0 1 1 0 0 1 1
Octet 4 0 1 1 0 1 0 0 0 1
Octet de parité 1 0 1 0 0 1 0 1

En faisant cela, on peut détecter qu'un bit a été modifié, mais aussi corriger l'erreur assez simplement. En cas d'erreur, deux bits de parité seront faussés : celui associé à l'octet, celui dans l'octet de parité. On peut alors détecter le bit erroné. Une autre méthode est de regarder les bits de parité associés aux octets, pour détecter l'octet erroné. Reste alors à corriger l'erreur, en supprimant l'octet invalide et en récupérant l'octet initial en utilisant le mot de parité.

Exemple avec quatre octets
Octets Bit de parité de chaque octet
Octet 1 0 0 1 1 1 1 1 1 0
Octet 2 0 0 1 0 1 0 1 1 0
Octet 3 1 1 0 0 1 0 0 1 1
Octet 4 0 1 1 0 1 0 0 0 1
Octet de parité 1 0 1 0 0 1 0 1

Le nombre de bits de parité total est élevé

[modifier | modifier le wikicode]

Le tout demande d'utiliser beaucoup de bits de parité. Pour N octets, il faut un bit de parité par octet et un octet de parité, ce qui donne N + 8 bits de parité. Pour 64 bits, soit 8 octets, cela fait 16 bits de parité nécessaires, soit 25% de bits en plus. Maintenant, prenons le cas général où on n'utilise pas des octets, mais des mots de M bits, plus longs ou plus courts qu'un octet. Dans ce cas, on a N + M bits de parité.

Notons que la technique peut s'appliquer avec des octets ou des nibbles, si on organise les bits correctement. Par exemple, prenons un nibble (4 bits). On peut l'organiser en un carré de deux bits de côté et ajouter : un bit de parité par colonne, un bit de parité par colonne (le mot de parité). Le tout donne 4 bits de parité, pour 4 bits de données : on double la taille de la donnée. Il est aussi possible de faire pareil avec un octet, l'organisant en deux lignes de 4 bits. Le résultat est de 6 bits de parité, ce qui est un petit peu mieux qu'avec un nibble : on passe à 3/4 de bits de plus.

Exemple d'octet avec parité ajoutée
0 0 1 1 0
1 0 0 1 0
1 0 1 0

Il est possible de faire la même chose pour des données de plusieurs octets. Pour un nombre de 16 bits, l'idéal est de faire 4 lignes de 4 bits chacune, ce qui fait 8 bits de parité au total. Pour 32 bits, on passe à 12 bits de parité, etc. Au total, voici la quantité de bits de parité nécessaires suivant la longueur de la donnée :

Exemple d'octet avec parité ajoutée
4 8 16 32 64 128 256 ...
4 6 8 12 16 24 32 ...

Il existe cependant des techniques plus économes, que nous allons voir dans ce qui suit. Par plus économes, il faut comprendre qu'elles utilisent moins de bits de parité, pour une fiabilité identique, voire meilleure. C'est là un défaut de la technique précédente : elle utilise beaucoup de bits de parités pour pas grand chose.

Les capacités de correction de la technique

[modifier | modifier le wikicode]

Notons que cette solution permet de corriger plus d'une erreur. Dans le pire des cas, on peut détecter et corriger une erreur. Si toutes les erreurs sont toutes dans le même mot/octet, alors on peut récupérer l'octet manquant. Le bit de parité permet de détecter un nombre impair d'erreur, soit 1, 3, 5, 7, ... erreurs. Il faut donc que le nombre d'erreurs dans l'octet soit impair.

Exemple avec quatre octets
Octets Bit de parité de chaque octet
Octet 1 0 0 1 1 1 1 1 1 0
Octet 2 0 0 1 0 1 0 1 1 0
Octet 3 1 1 0 0 1 0 0 1 1
Octet 4 0 1 1 0 1 0 0 0 1
Octet de parité 1 0 1 0 0 1 0 1

Par contre, si le nombre d'erreurs dans un octet est pair, alors le bit de parité associé à l'octet ne remarque pas l'erreur. On sait sur quelles colonnes sont les erreurs, pas la ligne.

Exemple avec quatre octets
Octets Bit de parité de chaque octet
Octet 1 0 0 1 1 1 1 1 1 0
Octet 2 0 0 1 0 1 0 1 1 0
Octet 3 1 1 0 0 1 0 0 1 1
Octet 4 0 1 1 0 1 0 0 0 1
Octet de parité 1 0 1 0 0 1 0 1

De même, si les erreurs touchent deux octets, alors on ne peut rien corriger. On peut détecter les erreurs, mais pas les corriger.

Exemple avec quatre octets
Octets Bit de parité de chaque octet
Octet 1 0 0 1 1 1 1 1 1 0
Octet 2 0 0 1 0 1 0 1 1 0
Octet 3 1 1 0 0 1 0 0 1 1
Octet 4 0 1 1 0 1 0 0 0 1
Octet de parité 1 0 1 0 0 1 0 1

Pour résumer, pour des mots de M bits, on peut corriger entre 1 et M/2 erreurs. Pour un octet, cela permet de détecter 1 erreur, 3/5/7 erreurs si elles ont lieu dans le même octet.

La protection des bits de parité

[modifier | modifier le wikicode]

Avec la méthode précédente, les bits de parité ne sont pas protégés : la moindre corruption des bits de parité fait que la méthode ne marche plus. Pour cela, il y a une solution toute simple : calculer un bit de parité qui tient compte de tous les bits de parité. Ainsi, si un bit de parité est corrompu, alors ce super-bit de parité détecterait l'erreur.

Dans les tableaux précédents, cela revient à ajouter un bit de parité dans la case tout en bas à droite. Le bit de parité calculé à partie des autres bits de parité est en rouge dans le tableau suivant. Il peut se calculer de plusieurs manières. La plus simple est calculer le bit de parité des 6 bits de parité, ceux de l'octet de parité et les autres. En faisant ainsi, on ganratit que tous les bits de parités sont protégés.

Exemple avec un octet
0 0 1 1 0
1 0 0 1 0
1 0 1 0 0

Avec cette technique, il faut faire la différence entre les bits de parité primaire, qui calculent la parité de tout ou partie des données, et le bit de parité secondaire, qui est calculé à partie des bits de parité primaire. Généralement, les codes correcteurs/détecteurs d'erreur avec des bits de parité secondaires sont assez peu efficaces, que ce soit en termes de fiabilité ou d'économies de bits. Ils utilisent beaucoup de bits et ne protègent que peu contre les erreurs. De plus, les bits de parité secondaires ne font que repousser le problème : le bit de parité secondaire peut être modifié lui aussi ! Les bits de parité secondaires ne protègent que contre la modification des bits de parité primaire, mais pas de leurs modifications propres. Mais qu'on se rassure : on peut protéger les bits de parité primaire sans recourir à des bits de parité secondaires, avec le codage que nous allons voir dans ce qui suit.

Les codes de Hamming

[modifier | modifier le wikicode]

Le code de Hamming se base sur l'usage de plusieurs bits de parité pour un seul nombre. Chaque bit de parité est calculé à partir d'un sous-ensemble des bits. Chaque bit de parité a son propre sous-ensemble, tous étant différents, mais pouvant avoir des bits en commun. Le but étant que deux sous-ensembles partagent un bit : si ce bit est modifié, cela modifiera les deux bits de parité associés. Et la modification de ce bit est la seule possibilité pour que ces deux bits soient modifiés en même temps : si ces deux bits de parité sont modifiés en même temps, on sait que le bit partagé a été modifié.

Pour résumer, un code de Hamming utilise plusieurs bits de parité, calculés chacun à partir de bits différents, souvent partagés entre bits de parité. Mais cela est aussi vrai pour la technique précédente. Un point important est que si un bit de parité est corrompu et change de valeur, les autres bits de parité ne le seront pas et c'est ce qui permettra de détecter l'erreur. Si un bit de données est inversé, plusieurs bits de parité sont touchés, systématiquement. Donc si un seul bit de parité est incompatible avec les bits de données, alors on sait qu'il a été inversé et qu'il est l'erreur. Pas besoin de faire comme avec la technique précédente, avec un mot de parité complété avec des bits de parité, avec un bit de parité secondaire.

Hamming(7,4)

Le code de Hamming le plus connu est certainement le code 7-4-3, un code de Hamming parmi les plus simples à comprendre. Celui-ci prend des données sur 4 bits, et leur ajoute 3 bits de parité, ce qui fait en tout 7 bits : c'est de là que vient le nom de 7-4-3 du code. Chaque bit de parité se calcule à partir de 3 bits du nombre, mais aussi des autres bits de parité. Pour poursuivre, nous allons noter les bits de parité p1, p2 et p3, tandis que les bits de données seront notés d1, d2, d3 et d4.

Bits de parité incorrects Bit modifié
Les trois bits de parité : p1, p2 et p3 Bit d4
p1 et p2 d1
p2 et p3 d3
p1 et p3 d2

Il faut préciser que toute modification d'un bit de donnée entraîne la modification de plusieurs bits de parité. Si un seul bit de parité est incorrect, il est possible que ce bit de parité a été corrompu et que les données sont correctes. Ou alors, il se peut que deux bits de données ont été modifiés, sans qu'on sache lesquels.

Le code 8-4-4 est un code 7-4-3 auquel on a ajouté un bit de parité supplémentaire. Celui-ci est calculé à partir de tous les bits, bits de parités ajoutés par le code 7-4-3 inclus. Ainsi, on permet de se prémunir contre une corruption de plusieurs bits de parité.

Hamming(8,4)

Évidemment, il est possible de créer des codes de Hamming sur un nombre plus grand que bits. Le cas le plus classique est le code 11-7-4.

Hamming(11,7)

Les codes de Hamming sont généralement plus économes que la technique précédente, avec un mot de parité combiné à plusieurs bits de parité. Par exemple, pour 4 bits, le code de Hamming 7-4-3 n'utilise que 3 bits de parité, contre 4 avec l'autre technique. Pour 7 bits, elle n'en utilise que 4, contre 6. Voici un tableau qui donne combien on peut protéger avec N bits de parité en utilisant un code de Hamming. On voit que les codes de Hamming sont bien plus économes que le mot de parité, tout en étant tout aussi puissant (ou presque).

Bits de parité 2 3 4 5 6 7 8 9
Données 1 4 11 26 57 120 247 502

Les sommes de contrôle

[modifier | modifier le wikicode]

Les sommes de contrôle sont des techniques de correction d'erreur, où les bits de correction d'erreur sont ajoutés à la suite des données. Les bits de correction d'erreur, ajoutés à la fin du nombre à coder, sont appelés la somme de contrôle. La vérification d'une erreur de transmission est assez simple : on calcule la somme de contrôle à partir des données transmises et on vérifie qu'elle est identique à celle envoyée avec les données. Si ce n'est pas le cas, il y a eu une erreur de transmission.

Techniquement, les techniques précédentes font partie des sommes de contrôle au sens large, mais il existe un sens plus restreint pour le terme de somme de contrôle. Il est souvent utilisé pour regrouper des techniques telle l'addition modulaire, le CRC, et quelques autres. Toutes ont en commun de traiter les données à coder comme un gros nombre entier, sur lequel on effectue des opérations arithmétiques pour calculer les bits de correction d'erreur. La seule différence est que l'arithmétique utilisée est quelque peu différente de l'arithmétique binaire usuelle. Dans les calculs de CRC, on utilise une arithmétique où les retenues ne sont pas propagées, ce qui fait que les additions et soustractions se résument à des XOR.

La première méthode consiste à diviser les données à envoyer par un nombre entier arbitraire et à utiliser le reste de la division euclidienne comme somme de contrôle. Cette méthode, qui n'a pas de nom, est similaire à celle utilisée dans les Codes de Redondance Cyclique.

Avec cette méthode, on remplace la division par une opération légèrement différente. L'idée est de faire comme une division, mais dont on aurait remplacé les soustractions par des opérations XOR. Nous appellerons cette opération une pseudo-division dans ce qui suit. Une pseudo-division donne un quotient et un reste, comme le ferait une division normale. Le calcul d'un CRC pseudo-divise les données par un diviseur et on utilise le reste de la pseudo-division comme somme de contrôle.

Il existe plusieurs CRC différents et ils se distinguent surtout par le diviseur utilisé, qui est standardisé pour chaque CRC. La technique peut sembler bizarre, mais cela marche. Cependant, expliquer pourquoi demanderait d'utiliser des concepts mathématiques de haute volée qui n'ont pas leur place dans ce cours, comme la division polynomiale, les codes linéaires ou encore les codes polynomiaux cycliques.


Les circuits électroniques

[modifier | modifier le wikicode]

Grâce au chapitre précédent, on sait enfin comment sont représentées nos données les plus simples avec des bits. On n'est pas encore allés bien loin : on ne sait pas comment représenter des bits dans notre ordinateur ou les modifier, les manipuler, ni faire quoi que ce soit avec. On sait juste transformer nos données en paquets de bits (et encore, on ne sait vraiment le faire que pour des nombres entiers, des nombres à virgule et du texte...). C'est pas mal, mais il reste du chemin à parcourir ! Rassurez-vous, ce chapitre est là pour corriger ce petit défaut. On va vous expliquer quels traitements élémentaires notre ordinateur va effectuer sur nos bits.

Les portes logiques de base

[modifier | modifier le wikicode]

Les portes logiques sont des circuits qui reçoivent plusieurs bits en entrée, et fournissent un bit en guise de résultat. Tous les composants d'un ordinateur sont fabriqués avec ce genre de circuits. Elles possèdent des entrées sur lesquelles on va placer des bits, et une sortie sur laquelle se trouve le bit de résultat. Les entrées ne sont rien d'autre que des morceaux de « fil » conducteur sur lesquels on envoie un bit (une tension). La sortie est similaire, si ce n'est qu'on récupère le bit de résultat.

Sur les schémas qui vont suivre, les entrées des portes logiques seront à gauche et les sorties à droite !

Les portes logiques ont différent symboles selon le pays et l'organisme de normalisation :

  • Commission électrotechnique internationale (CEI) ou International Electrotechnical Commission (IEC),
  • Deutsches Institut für Normung (DIN, Institut allemand de normalisation),
  • American National Standards Institute (ANSI).

La porte OUI/BUFFER

[modifier | modifier le wikicode]

La première porte fondamentale est la porte OUI, qui agit sur un seul bit : sa sortie est exactement égale à l'entrée. En clair, elle recopie le bit en entrée sur sa sortie. Pour simplifier la compréhension, je vais rassembler les états de sortie en fonction des entrées pour chaque porte logique dans un tableau que l'on appelle table de vérité.

Entrée Sortie
0 0
1 1
Symboles d'une porte OUI(BUFFER).
CEI DIN ANSI

Mine de rien, la porte OUI est parfois utile. Elle sert surtout pour recopier un signal électrique qui risque de se dissiper dans un fil trop long. On place alors une porte OUI au beau milieu du fil, pour éviter tout problème, la porte logique régénérant le signal électrique, comme on le verra dans le chapitre suivant. Cela lui vaut parfois le nom de porte BUFFER, ce qui veut dire tampon. Les portes OUI sont aussi utilisées dans certaines mémoires RAM (les mémoires SRAM), comme nous le verrons dans quelques chapitres.

La seconde porte fondamentale est la porte NON, qui agit sur un seul bit : la sortie d'une porte NON est exactement le contraire de l'entrée. Son symbole ressemble beaucoup au symbole d'une porte OUI, la seule différence étant le petit rond au bout du triangle.

Entrée Sortie
0 1
1 0
Symboles d'une porte NON (NOT).
CEI DIN ANSI

La porte ET possède plusieurs entrées, mais une seule sortie. Cette porte logique met sa sortie à 1 quand toutes ses entrées valent 1.

Entrée 1 Entrée 2 Sortie
0 0 0
0 1 0
1 0 0
1 1 1
Symboles d'une porte ET (AND).
CEI DIN ANSI

La porte NAND donne l'exact inverse de la sortie d'une porte ET. En clair, sa sortie ne vaut 1 que si au moins une entrée est nulle. Dans le cas contraire, si toutes les entrées sont à 1, la sortie vaut 0.

Entrée 1 Entrée 2 Sortie
0 0 1
0 1 1
1 0 1
1 1 0
Symboles d'une porte NON-ET (NAND).
CEI DIN ANSI

Au fait, si vous regardez le schéma de la porte NAND, vous verrez que son symbole est presque identique à celui d'une porte ET : seul un petit rond (blanc pour ANSI, noir pour DIN) ou une barre (CEI) sur la sortie de la porte a été rajouté. Il s'agit d'une sorte de raccourci pour schématiser une porte NON.

La porte OU est une porte dont la sortie vaut 1 si et seulement si au moins une entrée vaut 1. Dit autrement, sa sortie est à 0 si toutes les entrées sont à 0.

Entrée 1 Entrée 2 Sortie
0 0 0
0 1 1
1 0 1
1 1 1
Symboles d'une porte OU (OR).
CEI DIN ANSI

La porte NOR donne l'exact inverse de la sortie d'une porte OU.

Entrée 1 Entrée 2 Sortie
0 0 1
0 1 0
1 0 0
1 1 0
Symboles d'une porte NON-OU (NOR).
CEI DIN ANSI

Avec une porte OU, deux ET et deux portes NON, on peut créer une porte nommée XOR. Cette porte est souvent appelée porte OU exclusif. Sa sortie est à 1 quand les deux bits placés sur ses entrées sont différents, et vaut 0 sinon.

Entrée 1 Entrée 2 Sortie
0 0 0
0 1 1
1 0 1
1 1 0
Symboles d'une porte OU-exclusif (XOR).
CEI DIN ANSI

La porte XOR possède une petite sœur : la XNOR. Sa sortie est à 1 quand les deux entrées sont identiques, et vaut 0 sinon (elle est équivalente à une porte XOR suivie d'une porte NON).

Entrée 1 Entrée 2 Sortie
0 0 1
0 1 0
1 0 0
1 1 1
Symboles d'une porte NON-OU-exclusif (XNOR).
CEI DIN ANSI

Interlude propédeutique : combien il y a-t-il de portes logiques différentes ?

[modifier | modifier le wikicode]

Les portes logiques que nous venons de voir ne sont pas les seules. En fait, il existe un grand nombre de portes logiques différentes, certaines ayant plus d'intérêt que d'autres. Mais avant toute chose, nous allons parler d'un point important : combien y a-t-il de portes logiques en tout ? La question a une réponse très claire, pour peu qu'on précise la question. Les portes que nous avons vu précédemment ont respectivement 1 et 2 bits d'entrée, mais il existe aussi des portes à 3, 4, 5, bits d’entrée, voire plus. Il faut donc se demander combien il existe de portes logiques, dont les entrées font N bits. Par exemple, combien y a-t-il de portes logiques avec un bit d'entrée ? Avec deux bits d'entrée ? Avec 3 bits ?

Pour cela, un petit raisonnement peut nous donner la réponse. Vous avez vu plus haut qu'une porte logique est définie par une table de vérité, qui liste le bit de sortie pour chaque combinaison possible des entrées. Le raisonnement se fait en deux étapes. La première détermine, pour n bits d'entrée, combien il y a de lignes dans la table de vérité. La seconde détermine combien de tables de vérité à c lignes existent.

Les 16 portes logiques à deux entrées possibles.

Le nombre de lignes de la table de vérité se calcule facilement quand on se rend compte qu'une porte logique reçoit en entrée un "nombre" codé sur n bits, et fournit un bit de résultat qui dépend du "nombre" envoyé en entrée. Chaque ligne de la table de vérité correspond à une valeur possible pour le "nombre" envoyé en entrée. Pour n bits en entrée, la table de vérité fait donc lignes.

Ensuite, calculons combien de portes logiques en tout on peut créer c lignes. Là encore, le raisonnement est simple : chaque combinaison peut donner deux résultats en sortie, 0 et 1, le résultat de chaque combinaison est indépendant des autres, ce qui fait :

.

Pour les portes logiques à 1 bit d’entrée, cela fait 4 portes logiques. Pour les portes logiques à 2 bits d’entrée, cela fait 16 portes logiques.

Les portes logiques à un bit d'entrée

[modifier | modifier le wikicode]

Il existe quatre portes logiques de 1 bit. Il est facile de toutes les trouver avec un petit peu de réflexion, en testant tous les cas possibles.

  • La première donne toujours un zéro en sortie, c'est la porte FALSE ;
  • La seconde recopie l'entrée sur sa sortie, c'est la porte OUI, aussi appelée la porte BUFFER ;
  • La troisième est la porte NON vue plus haut ;
  • La première donne toujours un 1 en sortie, c'est la porte TRUE.
Tables de vérité des portes logiques à une entrée
Entrée FALSE OUI NON TRUE
0 0 0 1 1
1 0 1 0 1

On peut fabriquer une porte OUI en faisant suivre deux portes NON l'une à la suite de l'autre. Inverser un bit deux fois redonne le bit original.

Porte OUI/Buffer fabriquée à partie de deux portes NON.

Les portes logiques TRUE et FALSE sont des portes logiques un peu à part, qu'on appelle des portes triviales. Elles sont absolument inutiles et n'ont même pas de symbole attitré. Il est possible de fabriquer une porte FALSE à partir d'une porte TRUE suivie d'une porte NON, et inversement, de créer une porte TRUE en inversant la sortie d'une porte FALSE. Pour résumer, toutes les portes à une entrée peuvent se fabriquer en prenant une porte NON, couplée avec soit une porte FALSE, soit une porte TRUE. C'est étrange que l'on doive faire un choix arbitraire, mais c'est comme ça et la même chose arrivera quand on parlera des portes à deux entrées.

Les portes logiques à deux bits d'entrée

[modifier | modifier le wikicode]

Les portes logiques à 2 bits d'entrée sont au nombre de 16. Nous avions déjà vu les portes OU, NOR, ET, NAND, XOR et NXOR. À part ces 6 là, peu de portes logiques sont réellement utiles. La liste complète des tables de vérité est regroupée dans le tableau ci-dessous.

Entrée FALSE NOR NCONVERSE NON A NIMPLY NON (B) XOR NAND ET NXOR OUI (B) IMPLY OUI (A) CONVERSE OU TRUE
00 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1
01 0 0 1 1 0 0 1 1 0 0 1 1 0 0 1 1
10 0 0 0 0 1 1 1 1 0 0 0 0 1 1 1 1
11 0 0 0 0 0 0 0 0 1 1 1 1 1 1 1 1

Dans ce tableau, on retrouve les deux portes logiques triviales, à savoir la porte FALSE et TRUE, qui donnent toujours respectivement 0 et 1 en sortie. De plus, on retrouve aussi les portes OUI et NON, mais elles sont chacune en double. En cherchant dans le tableau, on trouve une porte qui recopie l'entrée A, une autre qui recopie l'entrée B. De même, on trouve une porte qui inverse l'entrée A et une autre qui inverse l'entrée B.

En clair, sur les 16 portes logiques à deux entrées, 6 d'entre elles sont de fausses portes à deux entrées : elles ont deux entrées, mais l'une d'entre elle ne sert à rien et l'est pas prise en compte pour calculer le résultat. Seules 10 sont de vraies portes à deux entrées, et non des portes à une entrée déguisées. Le tout est illustré dans le tableau ci-dessous, avec les portes à une entrée illustrées en jaune.

Entrée FALSE NOR NCONVERSE NON A NIMPLY NON (B) XOR NAND ET NXOR OUI (B) IMPLY OUI (A) CONVERSE OU TRUE
00 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1
01 0 0 1 1 0 0 1 1 0 0 1 1 0 0 1 1
10 0 0 0 0 1 1 1 1 0 0 0 0 1 1 1 1
11 0 0 0 0 0 0 0 0 1 1 1 1 1 1 1

On vient de voir que sur les 16 portes logiques à deux entrées, 6 d'entre elles sont des portes à une entrée déguisées. Reste à étudier les portes logiques restantes. Toutes les portes logiques peuvent se fabriquer en combinant d'autres portes logiques de base.

Par exemple, certaines portes sont l'inverse l'une de l'autre. La porte ET et la porte NAND sont l'inverse l'une de l'autre : il suffit d'en combiner une avec une porte NON pour obtenir l'autre. Même chose pour les portes OU et NOR, ainsi que les portes XOR et NXOR.

Porte ET AND from NAND and NOT
Porte OU OR from NOR and NOT

De fait, si on dispose de la porte NON, on peut diviser par 2 le nombre de portes logique, la moitié des portes logiques étant l'inverse de l'autre. Il ne reste alors que les portes logiques suivantes, appelées portes de base :

  • TRUE ou FALSE (le choix ne change pas grand chose)
  • NON ;
  • ET/OU/XOR ;
  • CONVERSE ;
  • IMPLY.

Mais d'autres possibilités existent et nous allons les voir dans le détail dans ce qui suit. Nous allons voir que certaines de ces portes logiques ne sont en fait que des dérivées des portes ET et des portes OU. En combinant une porte ET/OU avec une ou plusieurs portes NON, on arrive à émuler ces portes logiques restantes. Les portes logiques dérivées de la porte ET sont illustrées en rouge dans le tableau suivant, celles dérivées de la porte OU sont en vert, les portes XOR/NXOR sont en jaune, le reste est les portes à une entrée.

Entrée FALSE NOR NCONVERSE NON A NIMPLY NON (B) XOR NAND ET NXOR OUI (B) IMPLY OUI (A) CONVERSE OU TRUE
00 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1
01 0 0 1 1 0 0 1 1 0 0 1 1 0 0 1 1
10 0 0 0 0 1 1 1 1 0 0 0 0 1 1 1 1
11 0 0 0 0 0 0 0 0 1 1 1 1 1 1 1 1

Les portes dérivées de la porte OU

[modifier | modifier le wikicode]

Les portes dérivées de la porte OU regroupent les deux portes OU et NAND, ainsi que deux nouvelles portes : IMPLY et CONVERSE.

La porte CONVERSE a la table de vérité suivante :

Entrée 1 Entrée 2 Sortie
0 0 1
0 1 0
1 0 1
1 1 1

La porte IMPLY a la table de vérité suivante :

Entrée 1 Entrée 2 Sortie
0 0 1
0 1 1
1 0 0
1 1 1

Leur comportement se comprend facilement quand on sait qu'elles sont équivalentes à une porte OU dont on aurait inversé une des entrées. La porte CONVERSE met sa sortie à 1 soit quand l'entrée 1 est à 1, soit quand l'entrée 2 est à 0. La porte IMPLY fait la même chose, sauf qu'il faut que l'entrée 2 soit à 1 et l'entrée 1 à 0. Leurs symboles trahissent cet état de fait, jugez-en vous-même :

Porte CONVERSE.
Porte IMPLY.

Vous vous demandez certainement ce qui se passe quand on inverse les deux entrées avant le OU. Pour cela, regardons ce que fait le circuit en étudiant sa table de vérité.

Entrée 1 Entrée 2 Sortie
0 0 1
0 1 1
1 0 1
1 1 0

C'est la table de vérité d'une porte NAND. En clair, une porte NAND est équivalente à une porte OU dont on aurait inversé les deux entrées.

Porte NOR fabriquée avec une porte ET et deux portes NON.

Les portes dérivées d'un OU mettent leur sortie à 1 pour trois lignes de la table de vérité, pour trois entrées possibles. Aussi, on les appellera des portes 3-combinaisons.

Entrée NAND OR CONVERSE IMPLY
00 1 0 1 1
01 1 1 0 1
10 1 1 1 0
11 0 1 1 1

Les portes dérivées de la porte ET

[modifier | modifier le wikicode]

Les portes dérivées de la porte ET regroupent les deux portes NOR et ET, ainsi que deux nouvelles portes : NCONVERSE et NIMPLY, qui sont respectivement l'inverse des portes CONVERSE et IMPLY.

La porte NCONVERSE a la table de vérité suivante :

Entrée 1 Entrée 2 Sortie
0 0 0
0 1 1
1 0 0
1 1 0

La porte NIMPLY a la table de vérité suivante :

Entrée 1 Entrée 2 Sortie
0 0 0
0 1 0
1 0 1
1 1 0

Vous pouvez le voir, elles ont une sortie à 1 à condition que l'une des entrées soit à 1, et l'autre entrée soit à 0. On devine rapidement que ces deux portes peuvent se fabriquer en prenant une porte ET et une porte NON. Il suffit de mettre la porte NON devant l'entrée devant être à 0, et de mettre un ET à la suite. Au passage, cela se ressent dans les symboles utilisés pour ces deux portes, qui sont les suivants :

Porte NCONVERSE.
Porte NIMPLY.

Vous vous demandez certainement ce qui se passe quand on inverse les deux entrées avant le ET. Pour cela, regardons ce que fait le circuit en étudiant sa table de vérité.

Entrée 1 Entrée 2 Sortie
0 0 1
0 1 0
1 0 0
1 1 0

C'est la table de vérité d'une porte NOR. En clair, une porte NOR est équivalente à une porte ET dont on aurait inversé les deux entrées.

Porte NOR fabriquée avec une porte ET et deux portes NON.

Les portes dérivées d'un ET mettent leur sortie à 1 pour une seule combinaison d'entrée, une seule ligne de la table de vérité. Aussi, nous allons les appeler des portes 1-combinaison.

Entrée NOR NCONVERSE NIMPLY ET
00 1 0 0 0
01 0 1 0 0
10 0 0 1 0
11 0 0 0 1

Les liens entre portes 1 et 3-combinaisons

[modifier | modifier le wikicode]

La section précédente a montré qu'on pouvait en théorie créer toute porte logique avec seulement des portes NON, ET et OU. Mais en réalité, on peut faire avec beaucoup moins. Dans cette section, nous allons montrer que toute porte peut être créée avec seulement des portes ET et NON, sans porte OU. Et il est possible de faire de même, mais avec seulement des portes NON et des portes OU, sans porte ET.

Pour comprendre pourquoi, regardez le tableau suivant, qui liste les portes 1 et 3-combinaison en paires. On voit que chaque porte 1-combinaison est l'exact inverse d'une porte 3-combinaison et inversement ! La conséquence est que l'on peut créer n'importe quelle porte 3-combinaison à partie d'une porte 1-combinaison et inversement.

Entrée NOR OU NCONVERSE CONVERSE IMPLY NIMPLY ET NAND
00 1 0 0 1 0 1 0 1
01 0 1 1 0 0 1 0 1
10 0 1 0 1 1 0 0 1
11 0 1 0 1 0 1 1 0

Par exemple, nous avions vu plus haut que la porte NOR est une porte dérivée d'une porte ET. On peut créer une porte OU en ajoutant une porte NON à une porte NOR basée sur un ET, ce qui donne le circuit ci-dessous.

Porte OU fabriquée avec des portes NON et ET.

Ou encore, nous avions vu plus haut que la porte NAND est une porte dérivée d'une porte OU. On peut créer une porte ET en ajoutant une porte NON à une porte NAND basée sur un OU, ce qui donne le circuit ci-dessous.

Porte ET fabriquée avec des portes NON et OU.

Les portes XOR/NXOR sont "superflues"

[modifier | modifier le wikicode]

Dans cette section, nous allons montrer que les portes XOR/NXOR peuvent se fabriquer à partir d'autres portes logiques. Il y a deux grandes manières pour concevoir une porte XOR/NXOR à partir de portes plus simples. Les deux méthodes peuvent servir à créer n'importe quelle porte logique, pas seulement les XOR/NXOR. Aussi, il est intéressant de les étudier. La première méthode combine plusieurs portes 1-combinaison, leur résultat étant combiné avec une porte OU. L'autre méthode fait l'inverse : on prend plusieurs portes 3-combinaisons, on combine les résultats avec une porte ET. Voyons en détail ces deux techniques.

La première méthode : combiner des portes dérivée d'un ET avec un OU

[modifier | modifier le wikicode]

La première technique part d'un raisonnement assez simple, qui se comprend bien quand on étudie quelques exemples.

Commençons par le cas d'une porte XOR. La sortie d'une porte XOR est à 1 dans deux situations : soit la première entrée est à 1 et l'autre à 0, soit c'est l'inverse. Les deux cas correspondent respectivement aux portes NCONVERSE et NIMPLY, vue précédemment.

Entrée 1 Entrée 2 NCONVERSE NIMPLY (NCONVERSE) OU (NIMPLY) = XOR
0 0 0 0 0
0 1 0 1 1
1 0 1 0 1
1 1 0 0 0

La sortie des deux circuits est combinée avec une porte OU, car une seule des deux situations rencontrées met la sortie à 1. Les deux portes sont conçues à partir d'une porte ET et de portes NON. Le circuit obtenu est le suivant :

Porte XOR fabriquée à partir de portes ET/OU/NON.
Notons que ce circuit nous donne une idée pour créer une porte NXOR : il suffit de remplacer la porte OU finale par une porte NOR.

La porte NXOR peut se concevoir à parti du même raisonnement. La porte NXOR sort un 1 dans deux cas : soit quand ses deux entrées sont à 1, soit quand elles sont toutes deux à 0. La porte ET a sa sortie à 1 dans le premier cas, alors que la porte NOR (une OU suivie d'une NOT) a sa sortie à 1 dans le second cas.

Entrée 1 Entrée 2 NOR ET NXOR
0 0 1 0 1
0 1 0 0 0
1 0 0 0 0
1 1 0 1 1

Il faut ajouter une porte pour combiner le résultat de la porte NOR avec celui de la porte ET pour obtenir un résultat valide. Vu que la sortie doit être à 1 dans l'un des deux cas, c’est-à-dire quand l'une des deux portes ET/NOR est à 1, la porte finale est naturellement une porte OU. Le circuit obtenu est le suivant :

Porte NXOR fabriquée à partir de portes ET/OU/NON, alternative.
Notons que ce circuit nous donne une troisième possibilité pour créer une porte XOR : il suffit de remplacer la porte OU finale par une porte NOR.

Il s'agit là d'une technique qui marche au-delà des portes XOR, et qui marche pour toutes les portes logiques. L'idée est de lister toutes les lignes de la table de vérité où la porte sort un 1. Pour chaque ligne, on prend la porte 1-combinaison adéquate : celle qui sort un 1 pour cette ligne. On effectue ensuite un OU entre toutes les portes dérivées d'un ET.

Prenons comme exemple la porte logique qui met la sortie à 1 pour la seconde et quatrième ligne de la table de vérité. On peut la concevoir en prenant deux portes 1-combinaison : celle qui a sa sortie à 1 pour la seconde ligne de la table de vérité, et celle pour la quatrième ligne. En faisant un OU entre ces deux portes, le résultat sera la porte demandé. Et on peut appliquer le même raisonnement pour n'importe quelle porte logique. Il suffit alors d'utiliser un OU à 2, 3 ou 4 entrées.

Entrée 1 Entrée 2 NCONVERSE ET Porte voulue
0 0 0 0 0
0 1 1 0 1
1 0 0 0 0
1 1 0 1 1

Cette technique permet de fabriquer directement toutes les portes logiques à deux entrées, sauf la porte FALSE. C'est la seule qui ne puisse être fabriquée à partir de portes 1-combinaison seules et qui demande d'utiliser une porte NON pour. On peut donc, en théorie, fabriquer toutes les portes logiques à partir de seulement les portes ET, OU et NON.

La seconde méthode : combiner des portes dérivées d'un OU avec un ET

[modifier | modifier le wikicode]

Une autre possibilité fait l'inverse de la méthode précédente. Elle conçoit une porte logique en prenant plusieurs portes logiques, mais la combinaison des résultats de celles-ci est différente. Au lieu de procéder par addition, on procède par soustraction. Cette méthode demande de prendre des portes 3-combinaison et de les combiner avec une porte ET. Elle permet de fabriquer toutes les portes logiques, sauf la porte TRUE.

Par exemple, prenons la porte XOR. On part du principe qu'un XOR est un OU, sauf dans le cas où les deux entrées sont à 1, cas qui peut se détecter avec une porte ET. Voici ce que cela donne :

Entrée 1 Entrée 2 OU ET XOR
0 0 0 0 0
0 1 1 0 1
1 0 1 0 1
1 1 1 1 0

La porte à choisir pour combiner les deux résultats n'est pas évidente en regardant le tableau. Une alternative est de remplacer la porte ET par une porte NAND :

Entrée 1 Entrée 2 OU NAND XOR
0 0 0 1 0
0 1 1 1 1
1 0 1 1 1
1 1 1 0 0

On voit qu'en faisant un ET entre les sortie des portes OU et NAND, on obtient le résultat voulu.

Porte XOR fabriquée à partir de portes ET/OU/NON, alternative.
Notons que ce circuit nous donne une idée pour créer une porte NXOR : il suffit de remplacer la porte ET finale par une porte NAND.

Les portes NAND et NOR permettent de fabriquer toutes les autres portes

[modifier | modifier le wikicode]

Dans la section précédente, nous avons vu qu'il existe deux possibilités : soit on supprime les portes ET/NAND et on garde les portes OU/NOR, soit on fait l'inverse. Les deux possibilités sont équivalentes et permettent chacune de fabriquer toutes les portes logiques restantes. Cependant, supposons que je conserve les portes ET/NAND : dois-je conserver la porte ET, ou la porte NAND ? Les deux solutions ne sont pas équivalentes, car l'une permet de se passer de porte NON et pas l'autre ! Pour comprendre pourquoi, nous allons essayer de créer toutes les portes à une entrée à partir de portes ET/OU/NAND/NOR.

Il est possible de créer une porte OUI en utilisant une porte ET ou encore une porte OU, comme illustré ci-dessous. La raison est que si on fait un ET/OU entre un bit et lui-même, on retrouve le bit initial. Il s'agit d'une propriété particulière de ces portes sur laquelle nous reviendrons rapidement dans le chapitre sur les circuits combinatoires, et qui sera très utile dans le chapitre sur les opérations bit à bit. De plus, elle sera très utile vers la fin du chapitre.

Porte Buffer faite à partir d'un OU.
Porte Buffer faite à partir d'un ET.

Il est aussi possible de créer des portes TRUE et FALSE en modifiant les montages précédents. D'ailleurs, en guise d'exercice, essayez de créer des portes TRUE et FALSE à partir des deux montages précédents. Un indice : vous aurez besoin d'une à deux portes NON. Par contre, impossible de créer une porte NON facilement.

Mais que se passe-t-il si on utilise une porte NAND/NOR ? La réponse est simple : on obtient une porte NON ! Pour comprendre pourquoi cela marche, il faut imaginer que la porte NAND/NOR est composée d'une porte ET/OU suivie par une porte NON. Le bit d'entrée va subir un ET/OU avec lui-même, avant d'être inversé. Mais le passage dans le ET/OU ne changera pas le bit (cette étape se comporte comme une porte OUI), alors que la porte NON l'inversera.

Porte NON fabriquée avec des portes NAND/NOR
Circuit équivalent avec des NAND Circuit équivalent avec des NOR
Porte NON
NOT from NAND
NOT from NAND
NOT from NOR
NOT from NOR
Vous vous demandez peut-être ce qu'il se passe quand on fait la même chose avec une porte XOR, en faisant un XOR entre un bit et lui-même. Et bien le résultat est une porte FALSE. En effet, la porte XOR fournit un zéro quand les deux bits d'entrée sont identiques, ce qui est le cas quand on XOR un bit avec lui-même. Et inversement, une porte TRUE peut se fabriquer en utilisant une porte NXOR. Il s'agit là d'une propriété particulière de la porte XOR/NXOR sur laquelle nous reviendrons rapidement dans le chapitre sur les circuits combinatoires, et qui sera très utile dans le chapitre sur les opérations bit à bit.

Créer les autres portes logiques est alors un jeu d'enfant avec ce qu'on a appris dans les sections précédentes. Il suffit de remplacer les portes NON et ET par leurs équivalents fabriqués avec des NAND.

Circuit équivalent avec des NAND Circuit équivalent avec des NOR
Porte ET
AND from NAND
AND from NAND
AND from NOR
AND from NOR
Porte OU
OR from NAND
OR from NAND
OR from NOR
OR from NOR
Porte NOR
NOR from NAND
NOR from NAND
Porte NAND
NAND from NOR
NAND from NOR
Porte XOR
XOR from NAND
XOR from NAND
XOR from NOR
XOR from NOR
XOR from NAND
XOR from NAND
XOR from NOR
XOR from NOR
Porte NXOR
NXOR from NAND
NXOR from NAND
NXOR from NOR
NXOR from NOR
NXOR from NAND
NXOR from NAND
NXOR from NOR
NXOR from NOR

On vient de voir qu'il est possible de fabriquer tout circuit avec seulement un type de porte logique : soit on construit le circuit avec uniquement des NAND, soit avec uniquement des NOR. Pour donner un exemple, sachez que les ordinateurs chargés du pilotage et de la navigation des missions Appollo étaient intégralement conçus avec des portes NOR.

Les portes logiques à plus de deux entrées

[modifier | modifier le wikicode]

En théorie, les portes logiques regroupent tous les circuits à une ou deux entrées, mais pas au-delà. Mais dans les faits, certains circuits assez simples sont considérés comme des portes logiques, même s'ils ont plus de deux entrées. En fait, une porte logique est un circuit simple, qui sert de brique de base pour d'autres circuits. Il doit être raisonnablement simple et doit se fabriquer sans recourir à des portes logiques plus simples. En clair, les portes logiques sont des circuits élémentaires, et sont aux circuits électroniques ce que les atomes sont aux molécules. Dans ce qui suit, nous allons voir des portes logiques qui ont plus de 2 entrées, et en ont 3, 4, 5, voire plus. S'il est difficile d'expliquer en quoi ce sont des portes logiques, tout deviendra plus évident dans le chapitre suivant, quand nous verrons comment sont fabriquées ces portes logiques avec des transistors. Nous verrons que les circuits que nous allons voir se fabriquent très simplement en quelques transistors, sans recourir à des portes ET/OU/NAND/NOR. De plus, beaucoup de ces circuits sont très utiles et reviendront régulièrement dans la suite du cours.

Les portes ET/OU/NAND/NOR à plusieurs entrées

[modifier | modifier le wikicode]

Les premières portes logiques à plusieurs entrées que nous allons voir sont les portes ET/OU/NAND/NOR à plus de 2 entrées.

Il existe des portes ET qui ont plus de deux entrées. Elles peuvent en avoir 3, 4, 5, 6, 7, etc. Comme pour une porte ET normale, leur sortie ne vaut 1 que si toutes les entrées valent 1 : dans le cas contraire, la sortie de la porte ET vaut 0. Dit autrement, si une seule entrée vaut 0, la sortie de la porte ET vaut 0.

Porte ET à trois entrées, symbole ANSI
Porte ET à trois entrées, symbole CEI

De même, il existe des portes OU/NOR à plus de deux entrées. Pour les portes OU à plusieurs entrées, leur sortie est à 1 quand au moins une de ses entrées vaut 1. Une autre manière de le dire est que leur sortie est à 0 si et seulement si toutes les entrées sont à 0.

Porte OU à trois entrées, symbole CEI
Porte ET à trois entrées, symbole DIN

Les versions NAND et NOR existent elles aussiet leur sortie/comportement est l'inverse de celle d'une porte ET/OU à plusieurs entrées. Pour les portes NAND, leur sortie ne vaut 1 que si au moins une entrée est nulle : dans le cas contraire, la sortie de la porte NAND vaut 0. Dit autrement, si toutes les entrées sont à 1, la sortie vaut 0.

Porte NOR à trois entrées, symbole CEI
Porte NAND à trois entrées, symbole CEI
Porte ET à trois entrées conçue à partir de portes ET à deux entrées.
Porte OU à quatre entrées conçue à partir de portes OU à deux entrées.

Bien sur, ces portes logiques peuvent se créer en combinant plusieurs portes ET/OU/NOR/NAND à deux entrées. Cependant, faire ainsi n'est pas la seule solution et nous verrons dans le chapitre suivant que l'on peut faire nettement mieux avec quelques transistors. Elles sont très utiles dans la conception de circuits électroniques, mais elles sont aussi fortement utiles au niveau pédagogique. Nous en ferons un grand usage dans la suite du cours, car elles permettent de simplifier fortement les schémas et les explications pour certains circuits complexes. Sans elles, certains circuits seraient plus compliqués à comprendre et certains schémas seraient trop chargés en portes ET/OU pour être lisibles.

Les portes ET-OU-NON

[modifier | modifier le wikicode]

Les portes ET/OU/NON sont des portes logiques qui combinent plusieurs portes ET et une porte NOR en une seule porte logique. Il en existe de nombreux types, mais nous allons voir les deux principaux : les portes 2-2 et 2-1.

La porte 2-1 est une porte à 3 entrées, que nous allons appeler A, B, C, D. Elle fait un ET entre les entrées A et B, puis fait un NOR entre le résultat et la troisième entrée. Et le tout peut encore une fois s'implémenter en une seule porte logique, pas forcément en enchainant deux ou trois portes à la suite.

Porte ET-OU-NON de type 2-1, symbole.

La porte 2-2 est une porte à 4 entrées, que nous allons appeler A, B, C, D. Elle fait un ET entre les entrées A et B, un autre ET entre C et D, puis fait un NOR entre le résultat des deux ET. Et le tout peut s'implémenter en une seule porte logique, pas forcément en enchainant deux ou trois portes à la suite.

Porte ET-OU-NON de type 2-2, symbole.

Et il s'agit là des versions les plus simples de la porte, mais on peut imaginer des versions plus complexes, où les ET et les OU sont à 3, 4, voire 5 entrées.

Exemple de porte ET-OU-NON à huit entrées.

Il existe aussi des portes OU-ET-NON, où la position des portes ET et OU sont inversées.

Ces portes combinées permettent de grandement simplifier certains circuits. Mais nous les utiliserons assez peu, sauf dans le chapitre sur les additionneurs où nous y ferons référence. La raison est que les portes ET/OU/NON et assimilées étaient utilisées uniquement sur d'anciens circuits assez anciens, appelés circuits TTL. Ils utilisaient une ancienne technologie de fabrication, ils utilisaient des transistors spécialisés, ce qui fait que concevoir de telles portes était assez facile. Mais avec les technologies de fabrication CMOS actuelles, ce n'est plus le cas. Cependant, il est intéressant de savoir que de telles portes ont existé.

La porte à majorité

[modifier | modifier le wikicode]

La porte à majorité est une porte à plusieurs entrées, qui met sa sortie à 1 quand une plus de la moitié des entrées sont à 1, et sort un 0 sinon. En général, le nombre d'entrée de cette porte est toujours impair, afin d'éviter une situation où exactement la moitié des entrées sont à 1 et l'autre à 0. Avec un nombre impair d'entrée, il y a toujours un déséquilibre des entrées, pas une égalité parfaite. Il existe cependant des portes logiques à 4, 6, 8 entrées, mais elles sont plus rares.

Dans tous les cas, une porte à majorité est actuellement fabriquée à partir de portes logiques simples (ET, OU, NON, NAND, NOR). Mais on considère que c'est une porte logique car c'est un circuit simple et assez utile. De plus, il est possible de créer une grande partie des circuits électroniques possibles en utilisant seulement des portes à majorité ! C'est surtout cette possibilité qui fait que la porte à majorité est considérée comme une porte logique, pas comme un circuit simple et utile.

Une porte à majorité à trois entrées mettra sa sortie quand deux sorties sont à 1. Il existe plusieurs possibilités pour cela, qui sont presque toutes plus simples que le circuit précédent. La plus simple utilise une couche de portes ET suivie par une porte OU à plusieurs entrées. En voici une autre :

Porte à majorité à trois bits d'entrée.

Voici le circuit d'une porte à majorité à 4 bits d'entrées :

Porte à majorité à quatre bits d'entrée.

Les deux circuits précédents nous disent comment fabriquer une porte à majorité générale. Pour la porte à trois entrée, on prend toutes les paires d'entrées possibles, on fait un ET entre les bits de chaque paire, puis on fait un OU entre le résultat des ET. Pareil pour la porte à 4 entrées : on prend toutes les combinaisons de trois entrées possibles, on fait un ET par combinaison, et on fait un OU entre tout le reste. Pour une porte à 5 entrées, on devrait utiliser là encore les combinaisons de trois entrées possibles. En fait, la recette générale est la suivante : pour une porte à N entrées, on toutes les combinaisons de (N+1)/2 entrées, on fait un ET par combinaison, puis on fait un OU entre les résultats des ET.

La porte à transmission

[modifier | modifier le wikicode]

La porte à transmission est une porte logique assez particulière, qui mérite d'être vue dans ce chapitre. Pour simplifier, il s'agit d'un interrupteur commandable. Le circuit peut soit connecter l'entrée et la sortie, soit les déconnecter. Pour rappel, un interrupteur fermé laisse passer le courant, alors qu'un interrupteur fermé ne le laisse pas passer. Pour choisir entre les deux, une porte à transmission possède une entrée de commande sur laquelle on envoie un bit de commande, qui ouvre ou ferme l'interrupteur. La porte est typiquement fermée si le bit de commande est à 1, ouvert s'il est à 1.

Porte à transmission.

Il est possible de la voir comme une porte OUI améliorée dont la table de vérité est celle-ci :

Commande Entrée Sortie
0 0 Déconnexion
0 1 Déconnexion
1 0 0
1 1 1
Porte à transmission, symbole DIN

Les portes à transmission sont très utilisés dans certains circuits très communs, que nous aborderons dans quelques chapitres, comme les multiplexeurs ou les démultiplexeurs.

Un défaut de ces portes logique est qu'elles sont électriquement équivalentes à des interrupteurs. Les autres portes logiques peuvent générer un 1 ou un 0 distinct de ce qu'il y a sur leur entrée. Et ce n'est pas un prodige, c'est juste que les portes logiques sont toutes reliées à la tension d'alimentation et à la masse (le 0 volt). Elles sont alimentées en électricité, pour fournir un 1 en sortie si l'entrée est à 0. Pas les portes à transmission, qui ne sont pas reliées à l'alimentation. Ce détail ne pose pas de problèmes tant qu'on n'enchaine pas de portes à transmission les unes à la suite des autres.


Les circuits combinatoires

[modifier | modifier le wikicode]

Dans ce chapitre, nous allons aborder les circuits combinatoires. Ces circuits font comme tous les autres circuits : ils prennent des données sur leurs entrées, et fournissent un résultat en sortie. Le truc, c'est que ce qui est fourni en sortie ne dépend que du résultat sur les entrées, et de rien d'autre (ce n'est pas le cas pour tous les circuits). Pour donner quelques exemples, on peut citer les circuits qui effectuent des additions, des multiplications, ou d'autres opérations arithmétiques du genre.

Quelle que soit la complexité du circuit combinatoire à créer, celui-ci peut être construit en reliant des portes logiques entre elles. La conception d'un circuit combinatoire demande cependant de respecter quelques contraintes. La première est qu'il n'y ait pas de boucles dans le circuit : impossible de relier la sortie d'une porte logique sur son entrée, ou de faire la même chose avec un morceau de circuit. Si une boucle est présente dans un circuit, celui-ci n'est pas un circuit combinatoire, mais appartient à la classe des circuits séquentiels, que nous verrons dans le prochain chapitre.

Dans ce qui va suivre, nous allons voir comment concevoir ce genre de circuits. Il existe des méthodes et procédures assez simples qui permettent à n'importe qui de créer n'importe quel circuit combinatoire. Nous allons voir comment créer des circuits combinatoires à plusieurs entrées, mais à une seule sortie. Pour simplifier, on peut considérer que les bits envoyés en entrée sont un nombre, et que le circuit calcule un bit à partir du nombre envoyé en entrée.

Exemple d'un circuit électronique à une seule sortie.

C'est à partir de circuits de ce genre que l'on peut créer des circuits à plusieurs sorties : il suffit d'assembler plusieurs circuits à une sortie. La méthode pour ce faire est très simple : chaque sortie est calculée indépendamment des autres, uniquement à partir des entrées. Ainsi, pour chaque sortie du circuit, on crée un circuit à plusieurs entrées et une sortie : ce circuit déduit quoi mettre sur cette sortie à partir des entrées. En assemblant ces circuits à plusieurs entrées et une sortie, on peut ainsi calculer toutes les sorties.

Comment créer un circuit à plusieurs sorties avec des sous-circuits à une sortie.

Décrire un circuit : tables de vérité et équations logiques

[modifier | modifier le wikicode]

Dans ce qui va suivre, nous aurons besoin de décrire un circuit électronique, le plus souvent un circuit que l'on souhaite concevoir ou utiliser. Et pour cela, il existe plusieurs grandes méthodes : la table de vérité, les équations logiques et un schéma du circuit. Les schémas de circuits électroniques ne sont rien de plus que les schémas avec des portes logiques, que nous avons déjà utilisé dans les chapitres précédents. Reste à voir la table de vérité et les équations logiques. La différence entre les deux est que la table de vérité décrit ce que fait un circuit, alors qu'une équation logique décrit la manière dont il est câblé. D'un côté la table de vérité considère le circuit comme une boite noire dont elle décrit le fonctionnement, de l'autre les équations décrivent ce qu'il y a à l'intérieur.

La table de vérité

[modifier | modifier le wikicode]

La table de vérité décrit ce que fait le circuit, mais ne se préoccupe pas de dire comment. Elle ne dit pas quelles sont les portes logiques utilisées pour fabriquer le circuit, ni comment celles-ci sont reliées. Il s'agit d'une description du comportement du circuit, pas du circuit lui-même. En effet, elle se borne à donner la valeur de la sortie pour chaque entrée. Pour l'obtenir, il suffit de lister la valeur de chaque sortie pour toute valeur possible en entrée : on obtient alors la table de vérité du circuit. Pour créer cette table de vérité, il faut commencer par lister toutes les valeurs possibles des entrées dans un tableau, et écrire à côté les valeurs des sorties qui correspondent à ces entrées. Cela peut être assez long : pour un circuit ayant entrées, ce tableau aura lignes. Mais c'est la méthode la plus simple, la plus facile à appliquer.

Un premier exemple

[modifier | modifier le wikicode]

Le premier exemple sera très simple. Le circuit que l'on va créer sera un inverseur commandable, qui fonctionnera soit comme une porte NON, soit se contentera de recopier le bit fournit en entrée. Pour faire le choix du mode de fonctionnement (inverseur ou non), un bit de commande dira s'il faut que le circuit inverse ou non l'autre bit d'entrée :

  • quand le bit de commande vaut zéro, l'autre bit est recopié sur la sortie ;
  • quand il vaut 1, le bit de sortie est égal à l'inverse du bit d'entrée (pas le bit de commande, l'autre).

La table de vérité obtenue est celle d'une porte XOR :

Entrées Sortie
00 0
01 1
10 1
11 0

Un second exemple

[modifier | modifier le wikicode]

Pour donner un autre exemple, on va prendre un circuit calculant le bit de parité d'un nombre. Ce bit de parité est un bit qu'on ajoute aux données à stocker afin de détecter des erreurs de transmission ou d’éventuelles corruptions de données. Le but d'un bit de parité est que le nombre de bits à 1 dans le nombre à stocker, bit de parité inclus, soit toujours un nombre pair. Ce bit de parité vaut : zéro si le nombre de bits à 1 dans le nombre à stocker (bit de parité exclu) est pair et 1 si ce nombre est impair. Détecter une erreur demande de compter le nombre de 1 dans le nombre stocké, bit de parité inclus : si ce nombre est impair, on sait qu'un nombre impair de bits a été modifié. Dans notre cas, on va créer un circuit qui calcule le bit de parité d'un nombre de 3 bits.

Entrées Sortie
000 0
001 1
010 1
011 0
100 1
101 0
110 0
111 1

Un troisième exemple

[modifier | modifier le wikicode]

Pour le dernier exemple, nous allons prendre en entrée un nombre de 3 bits. Le but du circuit à concevoir sera de déterminer le bit majoritaire dans ce nombre : celui-ci contient-il plus de 1 ou de 0 ? Par exemple :

  • le nombre 010 contient deux 0 et un seul 1 : le bit majoritaire est 0 ;
  • le nombre 011 contient deux 1 et un seul 0 : le bit majoritaire est 1 ;
  • le nombre 000 contient trois 0 et aucun 1 : le bit majoritaire est 0 ;
  • le nombre 110 contient deux 1 et un seul 0 : le bit majoritaire est 1 ;
  • etc.
Entrées Sortie
000 0
001 0
010 0
011 1
100 0
101 1
110 1
111 1

Les équations logiques

[modifier | modifier le wikicode]

Il peut être utile d'écrire un circuit non sous forme d'une table de vérité, ou d'un schéma, mais sous forme d'équations logiques. Mais attention : il ne s'agit pas des équations auxquelles vous êtes habitués. Ces équations logiques ne font que travailler avec des 1 et des 0, et n'effectuent pas d'opérations arithmétiques mais seulement des ET, des OU, et des NON. Dans le détail, les variables sont des bits (les entrées du circuit considéré), alors que les opérations sont des ET, OU et NON. Voici résumé dans ce tableau les différentes opérations, ainsi que leur notation. a et b sont des bits.

Opérateur Notation 1 Notation 2
NON a
a ET b a.b
a OU b a+b
a XOR b

Avec ce petit tableau, vous savez comment écrire des équations logiques… Enfin presque, il ne faut pas oublier le plus important : les parenthèses, pour éviter quelques ambiguïtés. C'est un peu comme avec des équations normales : donne un résultat différent de . Avec nos équations logiques, on peut trouver des situations similaires : par exemple, est différent de .

Les formes normales conjonctives et disjonctives

[modifier | modifier le wikicode]

Dans la jungle des équations logiques, deux types se démarquent des autres. Ces deux catégories portent les noms barbares de formes normales conjonctives et de formes normales disjonctives. Derrière ces termes se cache cependant deux concepts assez simples. Il s'agit d'équations qui impliquent uniquement des portes ET, OU et NON. Pour simplifier, ces équations décrivent des circuits composés de trois couches de portes logiques : une couche de portes NON, une couche de portes ET et une couche de portes OU. La couche de portes NON est placée immédiatement à la suite des entrées, dont elle inverse certains bits. Les couches de portes ET et OU sont placées après la couche de portes NON, et deux cas sont alors possibles : soit on met la couche de portes ET avant la couche de OU, soit on fait l'inverse. Le premier cas, avec les portes ET avant les portes OU, donne une forme normale disjonctive. La forme normale conjonctive est l'exact inverse, à savoir celui où la couche de portes OU est placée avant la couche de portes ET.

Les équations obtenues ont une forme similaire aux exemples qui vont suivre. Ces exemples sont donnés pour un circuit à trois entrées nommées a, b et c, et une sortie s. On voit que certaines entrées sont inversées avec une porte NON, et que le résultat de l'inversion est ensuite combiné avec des portes ET et OU.

  • Exemple de forme normale conjonctive : .
  • Exemple de forme normale disjonctive : .

Il faut savoir que tout circuit combinatoire peut se décrire avec une forme normale conjonctive et avec une forme normale disjonctive. Mais l'équation obtenue n'est pas forcément la manière idéale de concevoir un circuit. Celui-ci peut par exemple être simplifié en utilisant des portes XOR, ou en remaniant le circuit. Mais obtenir une forme normale est très souvent utile, quitte à simplifier celle-ci par la suite. D'ailleurs, les méthodes que nous allons voir plus bas ne font que cela : elles traduisent une table de vérité en forme normale conjonctive ou disjonctive, avant de la simplifier et de traduire le tout en circuit.

Conversions entre équation, circuit et table de vérité

[modifier | modifier le wikicode]
Conversion d'un schéma de circuit en équation logique.

Une équation logique se traduit en circuit assez facilement : il suffit de substituer chaque terme de l'équation avec la porte logique qui correspond. Les parenthèses et priorités opératoires indiquent l'ordre dans lequel relier les différentes portes logiques. Elles donnent une idée de comment doit être faite cette substitution. Les schémas ci-dessous montrent un exemple d'équation logique et le circuit qui correspond, tout en montrant les différentes substitutions intermédiaires. À ce propos, concevoir un circuit demande simplement d'établir son équation logique : il suffit de traduire l'équation obtenue en circuit, et le tour est joué !

La signification symboles sur les exemples est donnée dans la section « Les équations logiques » ci-dessus.

Premier exemple. Second exemple.
Troisième exemple.
Quatrième exemple.
Quatrième exemple.

Il est aussi intéressant de parler des liens entre tables de vérité et équation logique. Il faut savoir qu'il est possible de trouver l'équation d'un circuit à partir de sa table de vérité, et réciproquement. C'est d'ailleurs ce que font les méthodes de conception de circuit que nous allons voir plus bas : elles traduisent la table de vérité d'un circuit en équation logique. On commence par établir la table de vérité, ce qui est assez simple, avant d'établir une équation logique et de la traduire en circuit. On pourrait croire qu'à chaque table de vérité correspond une seule équation logique, mais ce n'est pas le cas. En réalité, il existe plusieurs équations logiques différentes pour chaque table de vérité. La raison à cela est que des équations différentes peuvent donner des circuits qui se comportent de la même manière. Après tout, on peut concevoir un circuit de différente manières et des circuits câblés différemment peuvent parfaitement faire la même chose. Des équations logiques qui décrivent la même table de vérité sont dites équivalentes. Par équivalente, on veut dire qu'elles décrivent des circuits différents, mais qui ont la même table de vérité - ils font la même chose. À ce propos, il faut savoir qu'il est possible de convertir une équation logique en une autre équation équivalente., chose que nous apprendrons à faire dans la suite de ce chapitre.

Concevoir un circuit combinatoire avec la méthode des minterms

[modifier | modifier le wikicode]

Comme dit plus haut, créer un circuit demande d'établir sa table de vérité, avant de la traduire en équation logique, puis en circuit. Nous allons maintenant voir la première étape, celle de la conversion entre table de vérité et équation. Il existe deux grandes méthodes de ce type, pour concevoir un circuit intégré, qui portent les noms de méthode des minterms et de méthode des maxterms. La différence entre les deux est que la première donne une forme normale disjonctive, alors que la seconde donne une forme normale conjonctive. Dans cette section, nous allons voir la méthode des minterms, avant de voir la méthode des maxterms. Pour chaque méthode, nous allons commencer par montrer comment appliquer ces méthodes sans rentrer dans le formalisme, avant de montrer le formalisme en question. Précisons cependant que ces deux méthodes font la même chose : elles traduisent une table de vérité en équation logique. La première étape de ces deux méthodes est donc d'établir la table de vérité. Voyons un peu de quoi il retourne.

La méthode des minterms, expliquée sans formalisme

[modifier | modifier le wikicode]

La méthode des minterms est de loin la plus simple à comprendre. Ses principes sont en effet assez intuitifs et elle est assez facile à appliquer, pour qui connaît ses principes sous-jacents. Pour l'expliquer, nous allons commencer par voir un circuit, qui compare son entrée avec une constante, dépendante du circuit. Par la suite, nous allons voir comment combiner ce circuit avec des portes logiques pour obtenir le circuit désiré.

Les minterms (comparateurs avec une constante)

[modifier | modifier le wikicode]

Nous allons maintenant étudier un comparateur qui vérifie si le nombre d'entrée est égal à une certaine constante (2, 3, 5, 8, ou tout autre nombre) qui dépend du circuit et renvoie un 1 seulement si c’est le cas. Ainsi, on peut créer un circuit qui mettra sa sortie à 1 uniquement si on envoie le nombre 5 sur ses entrées. Ou encore, créer un circuit qui met sa sortie à 1 uniquement quand l'entrée vaut 126. Et ainsi de suite : tout nombre peut servir de constante à vérifier. Le circuit possède plusieurs entrées, sur lesquelles on place les bits du nombre à comparer. Sa sortie est un simple bit, qui vaut 1 si le nombre en entrée est égal à la constante et 0 sinon. Nous allons voir qu'il y en a deux types, qui ressemblent aux deux types de comparateurs avec zéro. Le premier type est basé sur une porte NOR, à laquelle on ajoute des portes NON. Le second est basé sur une porte ET précédée de portes NON.

Le premier circuit de ce type est composé d'une couche de portes NON et d'une porte ET à plusieurs entrées. Créer un tel circuit se fait en trois étapes. En premier lieu, il faut convertir la constante à vérifier en binaire : dans ce qui suit, nous nommerons cette constante k. En second lieu, il faut créer la couche de portes NON. Pour cela, rien de plus simple : on place des portes NON pour les entrées de la constante k qui sont à 0, et on ne met rien pour les bits à 1. Par la suite, on place une porte ET à plusieurs entrées à la suite de la couche de portes NON.

Exemples de comparateurs (la constante est indiquée au-dessus du circuit).

Pour comprendre pourquoi on procède ainsi, il faut simplement regarder ce que l'on trouve en sortie de la couche de portes NON :

  • si on envoie la constante, tous les bits à 0 seront inversés alors que les autres resteront à 1 : on se retrouve avec un nombre dont tous les bits sont à 1 ;
  • si on envoie un autre nombre, soit certains 0 du nombre en entrée ne seront pas inversés, ou alors des bits à 1 le seront : il y aura au moins un bit à 0 en sortie de la couche de portes NON.

Ainsi, on sait que le nombre envoyé en entrée est égal à la constante k si et seulement si tous les bits sont à 1 en sortie de la couche de portes NON. Dit autrement, la sortie du circuit doit être à 1 si et seulement si tous les bits en sortie des portes NON sont à 1 : il ne reste plus qu'à trouver un circuit qui prenne ces bits en entrée et ne mette sa sortie à 1 que si tous les bits d'entrée sont à 1. Il existe une porte logique qui fonctionne ainsi : il s'agit de la porte ET à plusieurs entrées.

Fonctionnement d'un comparateur avec une constante.

Le second type de comparateur avec une constante est fabriqué avec une porte NOR précédée de portes NON. Il se fabrique comme le comparateur précédent, sauf que cette fois-ci, il faut mettre une porte NON pour chaque bit à 1 de l'opérande, et non chaque bit à zéro. En clair, la couche de portes NON est l'exact inverse de celle du circuit précédent. Le tout est suivi par une porte NOR.

Exemples de comparateurs de type NON-NOR.

Pour comprendre pourquoi on procède ainsi, il faut simplement regarder ce que l'on trouve en sortie de la couche de portes NON :

  • si on envoie la constante, tous les bits à 0 seront inversés alors que les autres resteront à 1 : on se retrouve avec un zéro en sortie ;
  • si on envoie un autre nombre, soit certains 1 du nombre en entrée ne seront pas inversés, ou alors des bits à 0 le seront : il y aura au moins un bit à 1 en sortie de la couche de portes NON.

La porte NOR, quant à elle est un comparateur avec zéro, comme on l'a vu plus haut. Pour résumer, la première couche de portes NON transforme l'opérande et que la porte NOR vérifie si l'opérande transformée vaut zéro. La transformation de l'opérande est telle que son résultat est nul seulement si l’opérande est égale à la constante testée.

Fonctionnement des comparateurs de type NON-NOR.

Combiner les comparateurs avec une constante

[modifier | modifier le wikicode]

On peut créer n'importe quel circuit à une seule sortie avec ces comparateurs, en les couplant avec une porte OU à plusieurs entrées. Pour comprendre pourquoi, rappelons que les entrées du circuit peuvent prendre plusieurs valeurs : pour une entrée de bits, on peut placer valeurs différentes sur l'entrée. Mais seules certaines valeurs doivent mettre la sortie à 1, les autres la laissant à 0. Les valeurs d'entrée qui mettent la sortie 1 sont aussi appelées des minterms. Ainsi, pour savoir s’il faut mettre un 1 en sortie, il suffit de vérifier que l'entrée est égale à un minterm. Pour savoir si l'entrée est égale à un minterm, on doit utiliser un comparateur avec une constante pour chaque minterm. Par exemple, pour un circuit dont la sortie est à 1 si son entrée vaut 0000, 0010, 0111 ou 1111, il suffit d'utiliser :

  • un comparateur qui vérifie si l'entrée vaut 0000 ;
  • un comparateur qui vérifie si l'entrée vaut 0010 ;
  • un comparateur qui vérifie si l'entrée vaut 0111 ;
  • et un comparateur qui vérifie si l'entrée vaut 1111.

Reste à combiner les sorties de ces comparateurs pour obtenir une seule sortie, ce qui est fait en utilisant un circuit relativement simple. On peut remarquer que la sortie du circuit est à 1 si un seul comparateur a sa sortie à 1. Or, on connaît un circuit qui fonctionne comme cela : la porte OU à plusieurs entrées. En clair, on peut créer tout circuit avec seulement des comparateurs et une porte OU à plusieurs entrées.

Conception d'un circuit à partir de minterms

Méthode des minterms, version formalisée

[modifier | modifier le wikicode]

On peut formaliser la méthode précédente, ce qui donne la méthode des minterms. Celle-ci permet d'obtenir un circuit à partir d'une description basique du circuit. Mais le circuit n'est pas vraiment optimisé et peut être fortement simplifié. Nous verrons plus tard comment simplifier des circuits obtenus avec la méthode que nous allons exposer.

Lister les entrées de la table de vérité qui valident l'entrée

[modifier | modifier le wikicode]

La première étape demande d'établir la table de vérité du circuit, afin de déterminer ce que fait le circuit voulu. Maintenant que l'on a la table de vérité, il faut lister les valeurs en entrée pour lesquelles la sortie vaut 1. On rappelle que ces valeurs sont appelées des minterms. Il faudra utiliser un comparateur avec une constante pour chaque minterm afin d'obtenir le circuit final. Pour l'exemple, nous allons reprendre le circuit de calcul d'inverseur commandable, vu plus haut.

Entrées Sortie
00 0
01 1
10 1
11 0

Listons les lignes de la table où la sortie vaut 1.

Entrées Sortie
01 1
10 1

Pour ce circuit, la sortie vaut 1 si et seulement si l'entrée du circuit vaut 01 ou 10. Dans ce cas, on doit créer deux comparateurs qui vérifient si leur entrée vaut respectivement 01 et 10. Une fois ces deux comparateurs crée, il faut ajouter la porte OU.

Établir l'équation du circuit

[modifier | modifier le wikicode]

Les deux étapes précédentes sont les seules réellement nécessaires : quelqu'un qui sait créer un comparateur avec une constante (ce qu'on a vu plus haut), devrait pouvoir s'en sortir. Reste à savoir comment transformer une table de vérité en équations logiques, et enfin en circuit. Pour cela, il n'y a pas trente-six solutions : on va écrire une équation logique qui permettra de calculer la valeur (0 ou 1) d'une sortie en fonction de toutes les entrées du circuit. Et on fera cela pour toutes les sorties du circuit que l'on veut concevoir. Pour ce faire, on peut utiliser ce qu'on appelle la méthode des minterms, qui est strictement équivalente à la méthode vue au-dessus. Elle permet de créer un circuit en quelques étapes simples :

  • lister les lignes de la table de vérité pour lesquelles la sortie vaut 1 (comme avant) ;
  • écrire l'équation logique pour chacune de ces lignes (qui est celle d'un comparateur) ;
  • faire un OU entre toutes ces équations logiques, en n'oubliant pas de les entourer par des parenthèses.

Pour écrire l'équation logique d'une ligne, il faut simplement :

  • lister toutes les entrées de la ligne ;
  • faire un NON sur chaque entrée à 0 ;
  • et faire un ET avec le tout.

Vous remarquerez que la succession d'étapes précédente permet de créer un comparateur qui vérifie que l'entrée est égale à la valeur sur la ligne sélectionnée.

Pour illustrer le tout, on va reprendre notre exemple avec le bit de parité. La première étape consiste donc à lister les lignes de la table de vérité dont la sortie est à 1.

Entrées Sortie
001 1
010 1
100 1
111 1

On a alors :

  • la première ligne où l'entrée vaut 001 : son équation logique vaut  ;
  • la seconde ligne où l'entrée vaut 010 : son équation logique vaut  ;
  • la troisième ligne où l'entrée vaut 100 : son équation logique vaut  ;
  • la quatrième ligne où l'entrée vaut 111 : son équation logique vaut .

On a alors obtenu nos équations logiques. Reste à faire un OU entre toutes ces équations, et le tour est joué !

Nous allons maintenant montrer un deuxième exemple, avec le circuit de calcul du bit majoritaire vu juste au-dessus. Première étape, lister les lignes de la table de vérité dont la sortie vaut 1 :

Entrées Sortie
011 1
101 1
110 1
111 1

Seconde étape, écrire les équations de chaque ligne. Essayez par vous-même, avant de voir la solution ci-dessous.

  • Pour la première ligne, l'équation obtenue est : .
  • Pour la seconde ligne, l'équation obtenue est : .
  • Pour la troisième ligne, l'équation obtenue est : .
  • Pour la quatrième ligne, l'équation obtenue est : .

Il suffit ensuite de faire un OU entre les équations obtenues au-dessus.

Traduire l'équation en circuit

[modifier | modifier le wikicode]

Enfin, il est temps de traduire l'équation obtenue en circuit, en remplaçant chaque terme de l'équation par le circuit équivalent. Notons que les parenthèses donnent une idée de comment doit être faite cette substitution.

Concevoir un circuit avec la méthode des maxterms

[modifier | modifier le wikicode]

La méthode des minterms, vue précédemment, n'est pas la seule qui permet de traduire une table de vérité en équation logique. Elle est secondée par une méthode assez similaire : la méthode des maxterms. Les deux donnent des équations logiques, et donc des circuits, différents. Les deux commencent par une couche de portes NON, suivie par deux couches de portes ET et OU, mais l'ordre des portes ET et OU est inversé. Dit autrement, la méthode des minterms donne une forme normale disjonctive, alors que celle des maxterms donnera une forme normale conjonctive.

La méthode des maxterms : formalisme

[modifier | modifier le wikicode]

La méthode des maxterms fonctionne sur un principe assez tordu, mais qui fonctionne cependant. Avec celle-ci, on effectue trois étapes, chacune correspondant à l'exact inverse de l'étape équivalente avec les minterms. Les 0 sont remplacés par des 1 et les portes ET par des portes OU.

  • Premièrement on doit lister les lignes de la table de vérité qui mettent la sortie à 0, ce qui est l'exact inverse de l'étape équivalente avec les minterms.
  • Ensuite, on traduit chaque ligne en équation logique. La traduction de chaque ligne en équation logique est aussi inversée par rapport à la méthode des minterms : on doit inverser les bits à 1 avec une porte NON et faire un OU entre chaque bit.
  • Et enfin, on doit faire un ET entre tous les résultats précédents.

Un exemple d'application

[modifier | modifier le wikicode]

Par exemple, prenons la table de vérité suivante :

Entrée a Entrée b Entrée c Sortie S
0 0 0 0
0 0 1 1
0 1 0 0
0 1 1 1
1 0 0 1
1 0 1 1
1 1 0 1
1 1 1 0

La première étape est de lister les entrées associées à une sortie à 0. Ce qui donne :

Entrée a Entrée b Entrée c Sortie S
0 0 0 0
0 1 0 0
1 1 1 0

Vient ensuite la traduction de chaque ligne en équation logique. Cette fois-ci, les bits à 1 dans l'entrée sont ceux qui doivent être inversés, les autres restants tels quels. De plus, on doit faire un OU entre ces bits. On a donc :

  • pour la première ligne ;
  • pour la seconde ligne ;
  • pour la troisième ligne.

Et enfin, il faut faire un ET entre ces maxterms. Ce qui donne l'équation suivante :

Quelle méthode choisir ?

[modifier | modifier le wikicode]

On peut se demander quelle méthode choisir entre minterms et maxterms. L'exemple précédent nous donne un indice. Si on applique la méthode des minterms sur l'exemple précédent, vous allez prendre du temps et obtenir une équation logique bien plus compliquée, avec beaucoup de minterms. Alors que c'est plus rapide avec les maxterms, l'équation obtenue étant beaucoup plus simple. Cela vient du fait que dans l'exemple précédent, il y a beaucoup de lignes associées à une sortie à 1. On a donc plus de minterms que de maxterms, ce qui rend la méthode des minterms plus longue. Par contre, on pourrait trouver des exemples où c'est l'inverse. Si un circuit a plus de lignes où la sortie est à 0, alors la méthode des minterms sera plus rapide. Bref, tout dépend du nombre de minterms/maxterms dans la table de vérité. En comptant le nombre de cas où la sortie est à 1 ou à 0, on peut savoir quelle méthode est la plus rapide : minterm si on a plus de cas avec une sortie à 0, maxterm sinon.

Le principe caché derrière la méthode des maxterms

[modifier | modifier le wikicode]

La méthode des maxterms fonctionne sur un principe un peu différent de la méthode des minterms. Rappelons que chaque valeur d'entrée qui met une sortie à 0 est appelée un maxterm, alors que celles qui la mettent à 1 sont des minterms. Un circuit conçu selon avec des minterms vérifie si l'entrée met la sortie à 1, alors qu'un circuit maxterm vérifie si l'entrée ne met pas la sortie à 0. Dit autrement, ils vérifient soit que l'entrée est un minterm, soit que l'entrée n'est pas un maxterm.

L’aperçu complet de la méthode

[modifier | modifier le wikicode]

Pour cela, un circuit conçu avec la méthode des maxterms procède en deux étapes : il compare l'entrée avec chaque maxterm possible, et combine les résultats avec une porte à plusieurs entrées.

  • Pour commencer, le circuit vérifie si l'entrée est un maxterm avec plusieurs comparateurs avec une constante modifiée : un pour chaque maxterm. Chaque comparateur dit si l'entrée est différente du maxterm associé : il renvoie un 1 si l'entrée ne correspond pas au maxterm et 0 sinon.
  • La seconde étape combine les résultats de tous les maxterms pour déduire la sortie. Si tous les comparateurs renvoient un 1, cela signifie que l'entrée est différente de tous les maxterms : ce n'en est pas un. La sortie doit alors être mise à 1. Si l'entrée correspond à un maxterm, alors le comparateur associé au maxterm donnera un 0 en sortie : il y aura au moins un comparateur qui donnera un 0. Dans ce cas, la sortie doit être mise à 0. On remarque rapidement que ce comportement est celui d'une porte ET à plusieurs entrées.
Conception d'un circuit à partir de maxterms.

Le circuit de comparaison

[modifier | modifier le wikicode]

Le circuit de comparaison fonctionne sur le principe suivant : il compare l'entrée avec le maxterm bit par bit, chaque bit étant comparé indépendamment des autres, en parallèle. Les résultats des comparaisons sont ensuite combinées pour donner le bit de résultat.

Le circuit de comparaison donne un 1 quand les bits sont différents et un 0 s'ils sont égaux. La comparaison bit à bit est effectuée par une simple porte logique, qui n'est autre que la porte NON en entrée du circuit (ou son absence). Pour comprendre pourquoi, regardons la table de vérité du circuit de comparaison, illustré ci-dessous. On voit que si le bit du maxterm est 0, alors la sortie est égale au bit d'entrée. Mais si le bit du maxterm est à 1, alors la sortie est l'inverse du bit d'entrée.

Bit du maxterm Bit d'entrée Bit de sortie
0 0 0
0 1 1
1 0 1
1 1 0

Maintenant, passons à la combinaison des résultats. Si au moins un bit d'entrée est différent du bit du maxterm, alors l'entrée ne correspond pas au maxterm. On devine donc qu'on doit combiner les résultats avec une porte OU.

Simplifier un circuit

[modifier | modifier le wikicode]

Comme on l'a vu, la méthode précédente donne une équation logique qui décrit un circuit. Mais quelle équation : on se retrouve avec un gros paquet de ET et de OU ! heureusement, il est possible de simplifier cette équation. Pour donner un exemple, sachez que cette équation :  ; peut se simplifier en : avec les règles de simplifications que nous allons voir. Dans cet exemple, on passe donc de 17 portes logiques à seulement 3 ! Bien sûr, on peut simplifier cette équation juste pour se simplifier la vie lors de la traduction de cette équation en circuit, mais cela sert aussi à autre chose : cela permet d'obtenir un circuit plus rapide et/ou utilisant moins de portes logiques. Autant vous dire qu'apprendre à simplifier ces équations est quelque chose de crucial, particulièrement si vous voulez concevoir des circuits un tant soit peu rapides.

L'algèbre de Boole

[modifier | modifier le wikicode]

Pour simplifier une équation logique, on peut utiliser certaines propriétés mathématiques simples pour factoriser ou développer comme on le ferait avec une équation mathématique normale. Ces propriétés forment ce qu'on appelle l’algèbre de Boole. En utilisant ces règles algébriques, on peut factoriser ou développer certaines expressions, comme on le ferait avec une équation normale, ce qui permet de simplifier une équation logique assez intuitivement. Le tout est de bien faire ces simplifications en appliquant correctement ces règles, ce qui peut demander un peu de réflexion.

Les théorèmes de base de l’algèbre de Boole peuvent se classer en plusieurs types séparés, qui sont les suivantes :

  • l'associativité, la commutativité et la distributivité ;
  • la double négation et les lois de de Morgan ;
  • les autres règles, appelées règles bit à bit.

L'associativité, la distributivité et la commutativité des opérateurs logiques

[modifier | modifier le wikicode]

L'associativité, la commutativité et la distributivité ressemblent beaucoup aux règles arithmétiques usuelles, ce qui fait qu'on ne les détaillera pas ici.

Associativité, commutativité et distributivité
Commutativité



Associativité



Distributivité


Les autres règles sont par contre plus importantes.

Les règles de type bit à bit

[modifier | modifier le wikicode]

Les règles bit à bit ne sont utiles que dans le cas où certaines entrées d'un circuit sont fixées, ou lors de la simplification de certaines équations logiques. Elles regroupent plusieurs cas distincts :

  • soit on fait un ET/OU/XOR entre un bit et lui-même ;
  • soit on fait un ET/OU/XOR entre un bit et son inverse ;
  • soit on fait un ET/OU/XOR entre un bit et 1 ;
  • soit on fait un ET/OU/XOR entre un bit et 0.

Le premier cas regroupe les trois formules suivantes :

Le second cas regroupe les trois formules suivantes :

,
.

Le troisième cas regroupe les trois formules suivantes :

,
.

Le dernier cas regroupe les trois formules suivantes :

,
.

Voici la liste de ces règles, classées par leur nom mathématique :

Règles bit à bit
Idempotence


Élément nul


Élément Neutre



Complémentarité





Ces relations permettent de simplifier des équations logiques, mais peuvent avoir des utilisations totalement différentes.

Nous avons déjà utilisé implicitement les formules du premier cas, à savoir et dans le chapitre sur les portes logiques. En effet, nous avions vu qu'il est possible de fabriquer une porte NON à partir d'une porte NAND ou d'une porte NOR. L'idée était d'envoyer le bit à inverser sur les deux entrées d'une NOR/NAND, le ET/OU recopiant le bit sur sa sortie et le NON l'inversant. Pour retrouver ce résultat, il suffit d'ajouter une porte NON dans les formules et , ce qui donne :

Au passage, la formule nous dit pourquoi cela ne marcherait pas du tout avec une porte XOR.

Pour la formule , elle sert dans certaines situations particulières, où l'on veut initialiser un nombre à zéro, peu importe que ce nombre soit une variable, la sortie d'un circuit combinatoire ou un registre. La formule nous dit que le résultat d'un XOR entre un bit et lui-même est toujours zéro. Et cela s'applique aussi à des nombres : si on XOR un nombre avec lui-même, chacun de ses bits est XORé avec lui-même et est donc mis à zéro. Conséquence : un nombre XOR lui-même donnera toujours zéro. Cette propriété est utilisée pour mettre à zéro un registre, pour le calcul des bits de parité ou pour échanger une valeur entre deux registres. Les formules du second cas, à savoir , et , permettent de faire quelque chose de similaire. La première formule dit que faire un ET entre un nombre et son inverse donnera toujours zéro, comme un XOR entre un nombre et lui-même. Pour les deux autres formules, elles disent que le résultat sera toujours un bit à 1. Cela sert cette fois-ci si on veut initialiser une variable, un registre ou une sortie de circuit à une valeur où tous les bits sont à 1.

Les formules du troisième et quatrième cas seront utilisées dans le chapitre sur les circuits de calcul logique et bit à bit, dans la section sur les masques. C'est dans cette section que nous verrons en quoi ces formules sont utiles en dehors du cas d'une simplification de circuit. Pour le moment, nous ne pouvons pas en dire plus.

Les lois de de Morgan et la double négation

[modifier | modifier le wikicode]

Parmi les règles de l’algèbre de Boole, les lois de de Morgan et la double négation sont de loin les plus importantes à retenir. Elles sont les seules à impliquer les négations. Voici ces deux règles :

Règles sur les négations
Double négation
Loi de De Morgan


La première loi de de Morgan nous dit simplement qu'une porte NAND peut se fabriquer avec une porte OU précédée de deux portes NON, comme nous l'avions vu au chapitre précédent.

Porte NAND fabriquée avec des portes NON et OU

La seconde loi, quant à elle, dit qu'une porte NOR peut se fabriquer avec une porte ET précédée de deux portes NON, comme nous l'avions vu au chapitre précédent.

Porte NOR fabriquée avec des portes NON et ET

Les règles de Morgan pour deux entrées sont résumées dans le tableau ci-dessous.

Illustration des lois de De Morgan
1. Theorem.svg
2. Theorem.svg

Les lois de de Morgan peut se généraliser pour plus de deux entrées.

1ère loi de de Morgan :
2nd loi de de Morgan :

En les combinant avec la loi de la double négation, les lois de de Morgan permettent de transformer une équation écrite sous forme normale conjonctive en une équation équivalente sous forme normale disjonctive, et réciproquement. Elles permettent de passer d'une équation obtenue avec les minterms à l'équation obtenue avec les maxterms.

La formule équivalente de la porte XOR et de la porte NXOR

[modifier | modifier le wikicode]

Avec les règles précédentes, il est possible de démontrer que les portes XOR et NXOR peuvent se construire avec uniquement des portes ET/OU/NON. Nous l'avions vu dans le chapitre précédent, et montré quelques exemples de circuits équivalents.

En utilisant la méthode des minterms, on arrive à l'expression suivante pour la porte XOR et la porte NXOR :

XOR :
NXOR :

La formule obtenue avec les minterms pour la porte XOR donne ce circuit :

Porte XOR fabriquée à partir de portes ET/OU/NON.

La formule obtenue avec les minterms pour la porte NXOR donne ce circuit :

Porte NXOR fabriquée à partir de portes ET/OU/NON, alternative.

Il est possible d'obtenir une formule équivalente pour la porte XOR, en utilisant l’algèbre de Boole sur les formules précédentes. Pour cela, partons de l'équation de la porte NXOR obtenue avec la méthode des minterms :

Appliquons une porte NON pour obtenir la porte XOR :

Appliquons la loi de de Morgan entre les parenthèses :

Appliquons la loi de de Morgan dans les parenthèses :

Simplifions les doubles négations :

Porte XOR obtenue avec la méthode des maxterms.

Il est possible de faire la même chose, mais pour la porte NXOR. Pour cela, partons de l'équation de la porte XOR obtenue avec la méthode des minterms :

Une porte NXOR est, par définition, l'inverse d'une porte XOR. En appliquant un NON sur l'équation précédente, on trouve donc :

On applique la loi de de Morgan pour le OU entre les parenthèses :

On applique la loi de de Morgan, mais cette fois-ci à l'intérieur des parenthèses :

On simplifie les doubles inversions :

La formule est similaire à celle d'un XOR, la seule différence étant qu'il faut inverser de place les ET et les OU : le ET est dans les parenthèses pour le XOR et entre pour le NXOR, et inversement pour le OU.

Dans les deux exemples précédents, on voit que l'on a pu passer d'une forme normale conjonctive à une forme normale disjonctive et réciproquement, en utilisant la loi de de Morgan. C'est un principe assez général qui se retrouve souvent dans les démonstrations d'équations logiques.

Exemples complets

[modifier | modifier le wikicode]

Comme premier exemple, nous allons travailler sur cette équation : . On peut la simplifier en trois étapes :

  • Appliquer la règle de distributivité du ET sur le OU pour factoriser le facteur e1.e0, ce qui donne  ;
  • Appliquer la règle de complémentarité sur le terme entre parenthèses , ce qui donne 1.e1.e0 ;
  • Et enfin, utiliser la règle de l’élément neutre du ET, qui nous dit que a.1=a, ce qui donne : e1.e0.

En guise de second exemple, nous allons simplifier . Cela se fait en suivant les étapes suivantes :

  • Factoriser e0, ce qui donne : ;
  • Utiliser la règle du XOR qui dit que , ce qui donne .

Les tableaux de Karnaugh

[modifier | modifier le wikicode]

Il existe d'autres méthodes pour simplifier nos circuits. Les plus connues étant les tableaux de Karnaugh et l'algorithme de Quine Mc Cluskey. On ne parlera pas de la dernière méthode, trop complexe pour ce cours. Ces deux méthodes possèdent quelques défauts qui nous empêchent de créer de très gros circuits avec. Pour le dire franchement, elles sont trop longues à utiliser quand le nombre d'entrée du circuit dépasse 5 ou 6. Nous allons cependant aborder la méthode du tableau de Karnaugh, qui peut être assez utile pour des circuits simples. La simplification des équations avec un tableau de Karnaugh demande plusieurs étapes, que nous allons maintenant décrire.

Première étape : créer le tableau de Karnaugh

[modifier | modifier le wikicode]
Tableau de Karnaugh à quatre variables.

D'abord, il faut créer une table de vérité pour chaque bit de sortie du circuit à simplifier, qu'on utilise pour construire ce tableau. La première étape consiste à obtenir un tableau plus ou moins carré à partir d'une table de vérité, organisé en lignes et colonnes. Si on a n variables, on crée deux paquets avec le même nombre de variables (à une variable près pour un nombre impair de variables). Par exemple, supposons que j'aie quatre variables : a, b, c et d. Je peux créer deux paquets en regroupant les quatre variables comme ceci : ab et cd. Ou encore comme ceci : ac et bd. Il arrive que le nombre de variables soit impair : dans ce cas, il y a aura un paquet qui aura une variable de plus.

Seconde étape : remplir ce tableau

[modifier | modifier le wikicode]

Ensuite, pour le premier paquet, on place les valeurs que peut prendre ce paquet sur la première ligne. Pour faire simple, considérez ce paquet de variables comme un nombre, et écrivez toutes les valeurs que peut prendre ce paquet en binaire. Rien de bien compliqué, mais ces variables doivent être encodées en code Gray : on ne doit changer qu'un seul bit en passant d'une ligne à sa voisine. Pour le second paquet, faites pareil, mais avec les colonnes. Là encore, les valeurs doivent être codées en code Gray.

Pour chaque ligne et chaque colonne, on prend les deux paquets : ces deux paquets sont avant tout des rassemblements de variables, dans lesquels chacune a une valeur bien précise. Ces deux paquets précisent ainsi les valeurs de toutes les entrées, et correspondent donc à une ligne dans la table de vérité. Sur cette ligne, on prend le bit de la sortie, et on le place à l'intersection de la ligne et de la colonne. On fait cela pour chaque case du tableau, et on le remplit totalement.

Troisième étape : faire des regroupements

[modifier | modifier le wikicode]

Troisième étape de l'algorithme : faire des regroupements. Par regroupement, on veut dire que les 1 dans le tableau doivent être regroupés en paquets de 1, 2, 4, 8, 16, 32, etc. Le nombre de 1 dans un paquet doit TOUJOURS être une puissance de deux. De plus, ces regroupements doivent obligatoirement former des rectangles dans le tableau de Karnaugh. De manière générale, il vaut mieux faire des paquets les plus gros possible, afin de simplifier l'équation au maximum.

Exemple de regroupement valide.
Exemple de regroupement invalide.
Regroupements par les bords du tableau de Karnaugh, avec recouvrement.

Il faut noter que les regroupements peuvent se recouvrir. Non seulement c'est possible, mais c'est même conseillé : cela permet d'obtenir des regroupements plus gros. De plus, ces regroupements peuvent passer au travers des bords du tableau : il suffit de les faire revenir de l'autre côté. Et c'est possible aussi bien pour les bords horizontaux (gauche et droite) que pour les bords verticaux (haut et bas). Le même principe peut s'appliquer aux coins.

Quatrième étape : convertir chaque regroupement en équation logique

[modifier | modifier le wikicode]

Trouver l'équation qui correspond à un regroupement est un processus en plusieurs étapes, que nous illustrerons dans ce qui va suivre. Ce processus demande de :

  • trouver la variable qui ne varie pas dans les lignes et colonnes attribuées au regroupement ;
  • inverser la variable si celle-ci vaut toujours zéro dans le regroupement ;
  • faire un ET entre les variables qui ne varient pas.
  • faire un OU entre les équations de chaque regroupement, et on obtient l'équation finale de la sortie.


Dans ce chapitre, nous allons voir les opérations bit à bit, un ensemble d'opérations qui appliquent une opération binaire sur un ou deux nombres. La plus simple d'entre elle est l'opération NON, aussi appelée opération de complémentation, qui inverse tous les bits d'un nombre. Il s'agit de l'opération la plus simple et nous en avions déjà parlé dans les chapitres précédents. Mais il existe des opérations bit à bit un chouia plus complexes, comme celles qui font un ET/OU/XOR entre deux nombres. Pour être plus précis, elles font un ET/OU/XOR entre les deux bits de même poids. L'exemple du OU bit à bit est illustré ci-dessous, les exemples du ET et du XOR sont similaires.

Opération OU bit à bit.

De telles opérations sont appelées bit à bit car elles combinent les bits de même poids de deux opérandes. Par contre, il n'y a pas de calculs entre bits de poids différents, les colonnes sont traitées indépendamment. Elles sont très utilisées en programmation, et tout ordinateur digne de ce nom contient un circuit capable d'effectuer ces opérations. Dans ce chapitre, nous allons voir divers circuits capables d'effectuer des opérations bit à bit, et voir comment les combiner.

Les opérations bit à bit classiques peuvent prendre une ou deux opérandes. La plupart en prenant deux comme les opérations ET/OU/XOR, l'opération NON en prend une seule. Les opérations bit à bit sur deux opérandes sont au nombre de 16, ce qui correspond au nombre de portes logiques à deux entrées possibles. Mais ce chiffre de 16 inclut les opérations bit à bit sur une opérande unique, qui sont au nombre de 4. Les opérations bit à bit sur une seule opérande sont plus simples à voir, nous verrons les opérations bit à bit à deux opérandes plus tard.

Les opérations bit à bit à une opérande

[modifier | modifier le wikicode]

Les opérations bit à bit sur une opérande sont au nombre de quatre :

  • Mettre à zéro l'opérande (porte FALSE).
  • Mettre à 11111...11111 l'opérande (porte TRUE).
  • Inverser les bits de l'opérande (porte NON).
  • Recopier l'opérande (porte OUI).

Dans ce qui va suivre, nous allons créer un circuit qui prend en entrée une opérande, un nombre, et applique une des quatre opérations précédente sur chacun de ses bits. On peut choisir l'opération voulue grâce à plusieurs bits de commande, idéalement deux. Le circuit est composé à partir de circuits plus simples, au maximum trois : un circuit qui inverse le bit d'entrée à la demande, un autre qui le met à 1, un autre qui le met à 0. Ces trois circuits ont une entrée de commande qui détermine s'il faut faire l'opération, ou si le circuit doit se comporter comme une simple porte OUi, qui recopie sont entrée sur sa sortie et ne fait donc aucune opération. Le circuit recopie le bit d'entrée si cette entrée est à 0, mais il inverse/set/reset le bit d'entrée si elle est à 1.

Pour comprendre comment concevoir ces circuits, il faut rappeler les relations suivantes, qui donnent le résultat d'un ET/OU/XOR entre un bit quelconque noté a et un bit qui vaut 0 ou 1.

Opération Interprétation du résultat
Porte ET Mise à zéro du bit d'entrée
Recopie du bit d'entrée
Porte OU Mise à 1 du bit d'entrée
Recopie du bit d'entrée
Porte XOR Recopie du bit d'entrée
Inversion du bit d'entrée

Pour résumer ce qui va suivre :

  • Le circuit de mise à 1 commandable est une porte simple OU.
  • Le circuit d’inversion commandable est une simple porte XOR.
  • Le circuit de Reset, qui permet de mettre à zéro un bit si besoin, est une porte ET un peu modifiée.

Le circuit de mise à la valeur maximale

[modifier | modifier le wikicode]

Dans cette section, nous allons voir un circuit qui prend en entrée un nombre et met sa sortie à la valeur maximale si une condition est respectée. Pour le dire autrement, le circuit va soit recopier l'entrée telle quelle sur sa sortie, soit la mettre à 11111...111. Le choix entre les deux situations est réalisé par une entrée Set de 1 bit : un 1 sur cette entrée met la sortie à la valeur maximale, un 0 signifie que l'entrée est recopiée en sortie.

La porte OU est toute indiquée pour cela. La mise à 1 d'un bit d'entrée demande de faire un OU de celui-ci avec un 1, alors que recopier un bit d'entrée demande de faire un OU de celui-ci avec un 0.

Circuit de mise à 1111111...11

Ce circuit est utilisé pour gérer les débordements d'entier dans les circuits de calculs qui utilise l'arithmétique saturée (voir le chapitre sur le codage des entiers pour plus d'explications). Les circuits de calculs sont souvent suivis par ce circuit de mise à 111111...111, pour gérer le cas où le calcul déborde, afin de mettre la sortie à la valeur maximale. Évidemment, le circuit de calcul doit non seulement faire le calcul, mais aussi détecter les débordements d'entiers, afin de fournir le bit pour l'entrée Set. Mais nous verrons cela dans le chapitre sur les circuits de calcul entier.

Le circuit de mise à zéro

[modifier | modifier le wikicode]

Le circuit de Reset prend entrée le bit d'entrée, puis un bit de commande qui indique s'il faut mettre à zéro le bit d'entrée ou non. Le bit de commande en question est appelé le bit Reset. Si le signal Reset est à 1, alors on met à zéro le bit d'entrée, mais on le laisse intact sinon.

Le tableau ci-dessus nous dit que la porte ET est adaptée : elle recopie le bit d'entrée si le bit de commande vaut 1, et elle le met à 0 si le bit de commande vaut 0. Cependant, rappelons que l'on souhaite que le le circuit fasse un Reset si le bit de commande est à 1, pas 0, et la porte ET fait l'inverse. Pour corriger cela, on doit ajouter une porte NON. Le tout donne le circuit ci-dessous.

Circuit de mise à zéro d'un bit

Un circuit qui met à zéro un nombre est composé de plusieurs circuits ci-dessus, à la différence que la porte NON est potentiellement partagée. Par contre, chaque bit est bien relié à une porte ET.

Circuit de mise à zéro

L'inverseur commandable

[modifier | modifier le wikicode]

Dans cette section, nous allons voir un inverseur commandable, un circuit qui, comme son nom l'indique, inverse les bits d'un nombre passé en entrée. Ce circuit inverse un nombre quand le bit de commande, souvent nommé Invert, vaut 1.

La porte XOR est toute indiquée pour, ce qui fait que le circuit d'inversion commandable est composé d'une couche de portes XOR, chaque porte ayant une entrée connectée au bit de commande.

Inverseur commandable par un bit.

Le circuit qui combine les trois précédents

[modifier | modifier le wikicode]

Voyons maintenant un circuit qui combine les trois circuits précédents. L'implémentation naïve met les trois circuits les uns à la suite des autres, ce qui donne pour chaque bit d'opérande trois portes logiques ET/OU/XOR en série. Le problème est qu'il faut préciser trois bits de commandes, alors qu'on peut en théorie se débrouiller avec seulement 2 bits. Il faut alors ajouter un circuit combinatoire pour calculer les trois bits de commande à partir des deux bits initiaux.

Porte logique universelle de 1 bit, faite avec trois portes

Mais il y a moyen de se passer d'une porte logique ! L'idée est que mettre à 0 et mettre à 1 sont deux opérations inverses l'une de l'autre. Mettre à 1 revient à mettre à 0, puis à inverser le résultat. Et inversement, mettre à 0 revient à mettre à 1 avant d'inverser le tout. Il suffit donc de mettre le circuit d'inversion commandable à la fin du circuit, juste après un circuit de mise à 0 ou de mise à 1, au choix. En faisant comme cela, il ne reste que deux portes logiques, donc deux entrées. En choisissant bien les valeurs sur l'entrée de commande, on peut connecter les entrées de commande directement sur les opérandes des deux portes, sans passer par un circuit combinatoire.

Porte logique universelle de 1 bit, faite avec deux portes

Les opérations bit à bit à deux opérandes

[modifier | modifier le wikicode]

Les opérations bit à bit à deux opérandes effectuent un ET, un OU, ou un XOR entre deux opérandes. Ici, le ET/OU/XOR se fait entre deux bits de même poids dans une opérande. Les circuits qui effectuent ces opérations sont assez simples, ils sont composés de portes logiques placées les unes à côté des autres. Il n'y a pas de possibilité de combiner des portes comme c'était le cas dans la section précédente.

Inverseur commandable avec un masque

Les opérations de masquage

[modifier | modifier le wikicode]

Il est intéressant de donner quelques exemples d'utilisation des opérations bit à bit ET/OU/XOR. L'utilité des opérations bit est bit est en effet loin d'être évidente. L'exemple que nous allons prendre est celui des opérations de masquage, très connue des programmeurs bas niveau. Leur but est de modifier certains bits d'un opérande, mais de laisser certains intouchés. Les bits modifiés peuvent être forcés à 1, forcés à 0, ou inversés. Pour cela, on combine l'opérande avec un second opérande, qui est appelée le masque. Les bits à modifier sont indiqués par le masque : chaque bit du masque indique s'il faut modifier ou laisser intact le bit correspondant dans l'opérande.

Pour donner un exemple d'utilisation, parlons des droits d'accès à un fichier. Ceux-ci sont regroupés dans une suite de bits : un des bits indique s'il est accessible en écriture, un autre pour les accès en lecture, un autre s'il est exécutable, etc. Bref, modifier les droits en écriture de ce fichier demande de modifier le bit associé à 1 ou à 0, sans toucher aux autres. Cela peut se faire facilement en utilisant une instruction bit à bit avec un masque bien choisie.

Un autre cas typique est celui où un développeur compacte plusieurs données dans un seul entier. Par exemple, prenons le cas d'une date, exprimée sous la forme jour/mois/année. Un développeur normal stockera cette date dans trois entiers : un pour le jour, un pour le mois, et un pour la date. Mais un programmeur plus pointilleux sera capable d'utiliser un seul entier pour stocker le jour, le mois et l'année. Pour cela, il raisonnera comme suit :

  • un mois comporte maximum 31 jours : on doit donc encoder tous les nombres compris entre 1 et 31, ce qui peut se faire en 5 bits ;
  • une année comporte 12 mois, ce qui tient dans 4 bits ;
  • et enfin, en supposant que l'on doive gérer les années depuis la naissance de Jésus jusqu'à l'année 2047, 11 bits peuvent suffire.

Dans ces conditions, notre développeur décidera d'utiliser un entier de 32 bits pour le stockage des dates :

  • les 5 bits de poids forts serviront à stocker le jour ;
  • les 4 bits suivants stockeront le mois ;
  • et les bits qui restent stockeront l'année.

Le développeur qui souhaite modifier le jour ou le mois d'une date devra modifier une partie des bits, tout en laissant les autres intacts. Encore une fois, cela peut se faire facilement en utilisant une instruction bit à bit avec un masque bien choisi.

Le résultat d'une opération de masquage

[modifier | modifier le wikicode]

Maintenant, regardons ce que l'on peut faire avec une opération bit à bit entre un opérande et un masque. Le résultat dépend suivant que l'opération est un ET, un OU ou un XOR. Nous allons vous demander d'accepter les résultats sans les comprendre, vous allez comprendre comment cela fonctionne dans la section suivante.

Faire un ET entre l'opérande et le masque va mettre certains bits de l’opérande à 0 et va recopier les autres. Les bits mis à 0 sont ceux où le bit du masque correspondant est à 0, tandis que les autres sont recopiés tels quels.

La même chose a lieu avec l'opération OU, sauf que cette fois-ci, les bits de l'opérande sont soit recopiés, soit mis à 1. Les bits mis à 1 sont ceux pour lesquels le bit du masque correspondant est un 1.

Dans le cas d'un XOR, les bits sont inversés. Les bits inversés sont ceux pour lesquels le bit du masque correspondant est un 1.

Masquage des n bits de poids faible

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.

Décodeur à 3 entrées et 8 sorties.

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é bits et 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 bits ;
  • avec 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

[modifier | modifier le wikicode]

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 :

Equations logiques et circuit d'un décodeur 2 vers 4.

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.

Conception d'un circuit combinatoire quelconque à partir d'un décodeur.

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

[modifier | modifier le wikicode]

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.

Exemple d'un décodeur à 8 sorties.

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.

Décodeur 3 vers 8 conçu à partir de décodeurs 2 vers 4.

Le démultiplexeur

[modifier | modifier le wikicode]

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

[modifier | modifier le wikicode]

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.

Démultiplexeur à 2 sorties.

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 :

Démultiplexeur à deux sorties.

Les démultiplexeurs à plus de deux sorties

[modifier | modifier le wikicode]

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.

Démultiplexeur fabriqué avec une table de vérité.

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.

Circuit d'un démultiplexeur à 4 sorties, conçu à partir de démultiplexeurs à 2 sorties.

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 :

Démultiplexeur conçu à partir d'un décodeur.

La couche de portes ET en aval du décodeur peut être remplacée par une couche de portes à transmission, ce qui est plus simple. Chaque entrée est connectée à la sortie à travers une porte à transmission, la commande de chaque porte à transmission étant le fait du décodeur.

Le multiplexeur

[modifier | modifier le wikicode]

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.

Multiplexeur à 4 entrées.

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

[modifier | modifier le wikicode]

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.

Multiplexeur à deux entrées - symbole.

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 : , la seconde donne l'équation , la troisième l'équation et la quatrième l'équation . L'équation finale obtenue est donc :

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

Ensuite, factorisons dans le premier terme et dans le second :

Les termes et valent 1 :

On sait que , ce qui fait que l'équation simplifiée est la suivante :

Le circuit qui correspond est :

Multiplexeur à deux entrées - circuit.

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 :

Multiplexeur fabriqué avec des portes à transmission

Les multiplexeurs à plus de deux entrées

[modifier | modifier le wikicode]

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.

Multiplexeur conçu à partir de sa table de vérité.

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.

Multiplexeur conçu à partir de multiplexeurs plus simples.

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.

Multiplexeur 2 vers 4 conçu à partir d'un décodeur.
Multiplexeur conçu à partir de tampons trois-états.

Il est possible de remplacer les portes ET par des interrupteurs, comme on l'a fait pour le démultiplexeur. Les interrupteurs peuvent être des portes à transmission, ou des tampons trois-états. Les deux circuits en question sont une amélioration de la porte OUI. Suivant ce qu'on met sur leur entrée de commande, soit ils se comportent comme une porte OUI normale, soit ils déconnectent l'entrée de la sortie. Ils fonctionnent donc comme des interrupteurs, en un peu améliorés.

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 :

Multiplexeur basé sur des interrupteurs.
Encodeur à 8 entrées (et 3 sorties).

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 bits et sorties de 1 bit, l'encodeur a à l'inverse entrées de 1 bit avec une sortie de 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.

Entrées et sorties d'un encodeur.

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

[modifier | modifier le wikicode]

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 :

Le tout donne le circuit suivant :

Exemple d'encodeur à 4 entrées et 2 sorties.

Les encodeurs à plus de deux sorties

[modifier | modifier le wikicode]

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.

Encodeur fabriqué à partir d'encodeurs plus petits.

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 :

Encodeur 8 vers 3.

L'encodeur à priorité

[modifier | modifier le wikicode]

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 entrées numérotées de 0 à et N sorties. Une autre manière plus intuitive de le dire est qu'il possède N entrées et 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é

[modifier | modifier le wikicode]

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 :

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 :

,

Le circuit obtenu est le suivant :

Encodeur à priorité 4 vers 2.

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.

Table de vérité d'un encodeur à priorité 8 vers 3.

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

[modifier | modifier le wikicode]

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.

Construction d'un encodeur à priorité à partir d'encodeur à priorité plus petits.

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

[modifier | modifier le wikicode]

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.

Encodeur à priorité.

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.

Circuit de gestion des priorités.

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.

Encodeur à priorités

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


Dans ce chapitre et les suivants, nous allons voir comment implémenter sous forme de circuits certaines opérations extrêmement courantes dans un ordinateur. Les quelques circuits que nous allons voir seront réutilisés massivement dans les chapitres qui suivront, aussi nous allons passer quelque temps sur ces bases. Pour simplifier, les opérations réalisées par un ordinateur se classent en trois types :

  • Les opérations arithmétiques, à savoir les additions, multiplications et autres, qui auront chacune leur propre chapitre.
  • Les décalages et rotations, où on décale tous les bits d'un nombre d'un ou plusieurs crans vers la gauche/droite, qui feront l'objet d'un futur chapitre.
  • Les opérations logiques qui font l'objet de ce chapitre.

Les portes logiques universelles commandables

[modifier | modifier le wikicode]

Il y a quelques chapitres, nous avions parlé des opérations bit à bit et vu quelques circuits capables d'effectuer ces opérations. Ici, nous allons revenir sur ces circuits, mais allons en proposer une implémentation plus complexe et plus efficiente. Nous allons profiter du fait que l'on ne soit plus limités à quelques portes logiques, mais que nous pouvons maintenant utiliser des multiplexeurs, des démultiplexeurs et d'autres circuits pour améliorer les circuits de calcul bit à bit.

Dans cette section, nous allons voir comment créer un circuit capable d'effectuer plusieurs opérations logiques, le choix de l'opération étant le fait d'une entrée de commande. Par exemple, imaginons un circuit capable de faire à la fois un ET, un OU, un XOR et un NXOR. Le circuit contiendra une entrée de commande de 2 bits, et la valeur sur cette entrée permet de sélectionner quelle opération faire : 00 pour un ET, 01 pour un OU, 11 pour un XOR, 01 pour le NXOR Nous allons créer un tel circuit, sauf qu'il est capable de faire toutes les opérations entre deux bits et regroupe donc les 16 portes logiques existantes. Nous allons aussi voir la même chose, mais pour les portes logiques de 1 bit.

Les circuits de calcul bit à bit, le retour !

[modifier | modifier le wikicode]

Sachez qu'avec un simple multiplexeur, on peut créer un circuit qui effectue toutes les opérations bit à bit possible avec deux bits. Et cela a déjà été utilisé sur de vrais ordinateurs. Pour deux bits, divers théorèmes de l’algèbre de Boole nous disent que ces opérations sont au nombre de 16, ce qui inclus les traditionnels ET, OU, XOR, NAND, NOR et NXOR. Voici la liste complète de ces opérations, avec leur table de vérité ci-dessous (le nom des opérations n'est pas indiqué) :

  • Les opérateurs nommés 0 et 1, qui renvoient systématiquement 0 ou 1 quel que soit l'entrée ;
  • L'opérateur OUI qui recopie l'entrée a ou b, et l'opérateur NON qui l'inverse : , , ,  ;
  • L’opérateur ET, avec éventuellement une négation des opérandes : , , ,  ;
  • La même chose avec l’opérateur OU : , , ,  ;
  • Et enfin les opérateurs XOR et NXOR : , .
a b
0 0 - 0 0 0 0 0 0 0 0 1 1 1 1 1 1 1 1
0 1 - 0 0 0 0 1 1 1 1 0 0 0 0 1 1 1 1
1 0 - 0 0 1 1 0 0 1 1 0 0 1 1 0 0 1 1
1 1 - 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1

Le circuit à concevoir prend deux bits, que nous noterons a et b, et fournit sur sa sortie : soit a ET b, soit a OU b, soit a XOR b, etc. Pour sélectionner l'opération, une entrée du circuit indique quelle est l'opération à effectuer, chaque opération étant codée par un nombre. On pourrait penser que concevoir ce circuit serait assez complexe, mais il n'en est rien grâce à une astuce particulièrement intelligente. Regardez le tableau ci-dessus : vous voyez que chaque colonne forme une suite de bits, qui peut être interprétée comme un nombre. Il suffit d'attribuer ce nombre à l'opération de la colonne ! En faisant ainsi, le nombre attribué à chaque opération contient tous les résultats de celle-ci. Il suffit de sélectionner le bon bit parmi ce nombre pour obtenir le résultat. Et on peut faire cela avec un simple multiplexeur, comme indiqué dans le schéma ci-dessous !

Unité de calcul bit à bit de 2 bits, capable d'effectuer toute opération bit à bit.

Il faut noter que le raisonnement peut se généraliser avec 3, 4, 5 bits, voire plus ! Par exemple, il est possible d'implémenter toutes les opérations bit à bit possibles entre trois bits en utilisant un multiplexeur 8 vers 3.

Les unités de calcul logique

[modifier | modifier le wikicode]

Maintenant que nous sommes armés des portes logiques universelles, nous pouvons implémenter un circuit généraliste, qui peut effectuer la même opération logique sur tous les bits. Ce circuit est appelé une unité de calcul logique. Elle prend en entrée deux opérandes, ainsi qu'une entrée de commande sur laquelle on précise quelle opération il faut faire.

Elle est simplement composée d'autant de portes universelles 2 bits qu'il n'y a de bits dans les deux opérandes. Par exemple, si on veut un circuit qui manipule des opérandes 8 bits, il faut prendre 8 portes universelles deux bits. Toutes les entrées de commande des portes sont reliées à la même entrée de commande.

Unité de calcul bit à bit de 4 bits, capable d'effectuer toute opération bit à bit

Les opérations FFS, FFZ, CTO et CLO

[modifier | modifier le wikicode]

Dans cette section, nous allons aborder plusieurs opérations fortement liées entre elles, illustrées dans le schéma ci-dessous. Elles sont très courantes sur la plupart des ordinateurs, surtout dans les ordinateurs embarqués. Beaucoup d'ordinateurs, comme les anciens mac avec des processeurs type Power PC et les processeurs MIPS ou RISC ont des instructions pour effectuer ces opérations.

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", "les 0 de poids faible" et quelques autres du même genre. Quand nous parlerons du 0 de poids faible, nous voudrons parler du premier 0 que l'on croise dans un nombre en partant de sa droite. Par exemple, dans le nombre 0011 1011, le 0 de poids faible est le troisième bit en partant de la droite. Quand nous parlerons du 1 de poids faible, c'est la même chose, mais pour le premier bit à 1. Par exemple, dans le nombre 0110 1000, le 1 de poids faible est le quatrième bit. Quant aux expressions "le 1 de poids fort" et "les 0 de poids fort" elles sont identiques aux précédentes, sauf qu'on parcourt le nombre à partir de sa gauche.

Par contre, les expressions "LES 1 de poids faible" ou "LES 0 de poids faible" ne parlent pas de la même chose. Quand nous voudrons parler des 1 de poids faible, au pluriel, nous voulons dire : tous les bits situés avant le 0 de poids faible. Par exemple, prenons le nombre 0011 0011 : les 1 de poids faible correspondent ici aux deux premiers bits en partant de la droite. Même chose quand on parle des zéros de poids faible au pluriel. Quant aux expressions "les 1 de poids fort" ou "les 0 de poids fort" elles sont identiques aux précédentes, sauf qu'on parcourt le nombre à partir de sa gauche.

Les opérations bit à bit complexes

[modifier | modifier le wikicode]

La première opération que nous allons aborder, Find First Set, donne la position du 1 de poids faible. Cette opération est liée à l'opération Count Trailing Zeros, qui donne le nombre de zéros situés à droite de ce 1 de poids faible. L'opération Find First Set est opposée au calcul du Find highest set, qui donne la position du 1 de poids fort. Le nombre de zéros situés à gauche de ce bit à 1 est appelé le Count Leading Zeros.

Ces quatre opérations ont leur équivalents en remplaçant les 0 par des 1 et réciproquement. Par exemple, l'opération Find First Zero donne la position du 0 de poids faible (le plus à droite) et l'opération Find Highest Zero donne la position du 0 de poids fort (le plus à gauche). L'opération Count Trailing Ones donnent le nombre de 1 situés à gauche du 0 de poids fort, tandis que l'opération Count Leading Ones donne le nombre de 1 situés à droite du 0 de poids faible.

Opérations Find First Set ; Find First Zero ; Find Highest Set (le logarithme binaire) ; Find Highest Zero ; Count Leading Zeros ; Count Trailing Zeros ; Count Leading Ones et Count Trailing Ones.

La numérotation des bits et l'équivalence de certaines opérations

[modifier | modifier le wikicode]

Dans toutes ces opérations, les bits sont numérotés, leur numéro étant appelé leur position ou leur indice. La position d'un bit est donc donnée par ce numéro. Ces opérations varient selon la méthode utilisée pour numéroter les bits. On peut commencer à compter les bits à partir de 0, le 0 étant le numéro du bit de poids faible. Mais on peut aussi compter à partir de 1, le bit de poids faible étant celui de numéro 1. Ces deux conventions ne sont pas équivalentes. Si on choisit la première convention, certaines opérations sont équivalentes. Par exemple, les opérations Count Trailing Zeros et Find First Set donnent toutes les deux le même résultat. Avec l'autre convention, les deux différent de 1 systématiquement.

Avec la première convention, pour un nombre codé sur bits, on a :

On voit que certaines opérations sont équivalentes, ce qui nous arrange bien. De plus, on peut calculer le résultat de l'opération FHS à partir de l'opération CLZ et réciproquement. De même le résultat de FHZ peut se calculer à partir du résultat de CLO et inversement. Il suffit d'effectuer une simple soustraction, avec une constante qui plus est, ce qui est simple à fabriquer. Ce qui revient à ajouter un circuit pour faire le calcul .

De fait, nous n'aurons qu'à aborder quatre calculs :

  • le Find First Set, abréviée FFS ;
  • le Find highest set, abrévié FHS ;
  • le Find First Zero, abréviée FFZ ;
  • le Find highest Zero, abrévié FHZ.

On peut même aller plus loin et éliminer encore plus le nombre d'opérations différentes à effectuer. En effet, les opérations FHS et FHZ peuvent se déduire l'une de l'autre, en changeant le nombre passé en entrée. Pour cela, il suffit d'inverser les bits de l'entrée, avec un inverseur commandable. Avec l'inversion, le 1 de poids fort deviendra le 0 de poids fort et inversement. Idem pour les opérations FFS et FFZ. En inversant l'entrée, le 1 de poids faible deviendra le 0 de poids faible et inversement.

Circuit qui effectue les opérations FHS, FFS, CLZ et autres.

L'encodeur à priorité

[modifier | modifier le wikicode]

Reste à voir comment effectuer les deux opérations restantes, à savoir Find Highest Set et Find First Set. Et pour cela, nous devons utiliser un encodeur à priorité. 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. Ces encodeurs à priorité réalisent l'opération Find Highest Set, qui est reliée à l’opération count leading zeros. Ce qui leur vaut le nom anglais de leading zero detector (LZD) ou encore de leading zero counter (LZC). Mais dans d'autres cas, l'encodeur à priorité donne la position du 1 de poids faible, ce qui correspond à l'opération Count Trailing Zeros. Il existe aussi des encodeurs qui donnent la position du zéro de poids faible, voire du zéro de poids fort, ce qui correspond respectivement aux opérations Find First Zero et Find highest Zero. En clair, pour les quatre opérations précédentes, il existe un encodeur à priorité qui s'en charge.

Les circuits générateurs/vérificateurs d'ECC

[modifier | modifier le wikicode]

Au tout début de ce cours, nous avions vu les codes ECC, qui détectent ou corrigent des corruptions de données. Si un bit est altéré, ils permettent de détecter que le bit en question a été inversé, et peuvent éventuellement le corriger pour retrouver la donnée initiale. Les deux codes ECC les plus connus sont le bit de parité et les codes de Hamming. Dans ce qui suit, nous allons voir des circuits qui calculent soit un bit de parité, soit le code de Hamming d'un nombre.

Le générateur de parité

[modifier | modifier le wikicode]

Pour rappel, le bit de parité est une technique qui permet de détecter si une donnée a été corrompue. Elle permet de détecter qu'un bit a été inversé, à savoir qu'un bit censé être à 1 est passé à 0 ou inversement. Pour cela, on ajoute un bit de parité aux données à sécuriser, afin que le nombre de bits à 1 soit pair, bit de parité inclu. En clair, si la donnée a un nombre de bit à 1 pair, alors le bit de parité vaut 0. Mais si le nombre est impair, alors le bit de parité vaut 1. Dans cette section, nous allons voir un circuit qui calcule le bit de parité d'une opérande.

Intuitivement, on se dit qu'il faut compter les 1 dans l'opérande, avant de calculer sa parité et d'en déduire le bit de parité. Compter les 1 dans un nombre est une opération tellement courante qu'elle porte un nom : on l'appelle la population count, ou encore poids de Hamming. Malheureusement, son calcul demande de faire des additions, ce qui fait qu'on le verra dans quelques chapitres. Mais heureusement, il existe une autre méthode bien plus simple, plus rapide, et plus économe en circuits.

Pour comprendre comment, nous allons commencer avec un cas simple : le calcul à partir d'une opérande de 2 bits. Le circuit étant simple, il suffit d'utiliser les techniques vues précédemment, avec la table de vérité. En écrivant la table de vérité du circuit, on remarque rapidement que la table de vérité donne la table de vérité d'une porte XOR.

Bit 1 Bit 2 Bit de parité
0 0 0
0 1 1
1 0 1
1 1 0

Pour la suite, nous allons partir d'un nombre de trois bits. On pourrait tenter de créer ce circuit à partir d'une table de vérité, mais nous allons utiliser une autre méthode, qui nous donnera un indice important. Ce nombre de 3 bits est composé d'un nombre de 2 bits auquel on a jouté un troisième bit. L'ajout de ce troisième bit modifie naturellement le bit de parité du nombre précédent. Dans ce qui va suivre, nous allons créer un circuit qui calcule le bit de parité final, à partir : du bit de parité du nombre de 2 bits, et du bit ajouté. On voit alors que la table de vérité est celle d'une porte XOR.

Bit de parité précédent Bit ajouté Bit de parité final
0 0 0
0 1 1
1 0 1
1 1 0

Chose assez intéressante, ce mécanisme fonctionne quel que soit le nombre de bits de l'opérande. Ajouter un bit à un nombre modifie sa parité, celle-ci état alors égale à : bit ajouté XOR bit-parité du nombre. L’explication est relativement simple : ajouter n 0 ne modifie pas le nombre de 1, et donc le bit de parité, tandis qu'ajouter un 1 inverse le bit de parité.

Avec cette logique, on peut créer un générateur de parité parallèle., un circuit qui calcule le bit de parité d'une opérande, en faisant un XOR entre tous ses bits. Effectué naïvement, il suffit d’enchaîner des portes XOR les unes à la suite des autres. En réfléchissant, on devien qu'on peut structurer les portes XOR comme ceci :

Circuit de parité

Le circuit précédent calcule le bit de parité d'une opérande. Pour ce qui est de vérifier si une donnée est corrompue, rien de plus simple : il suffit de générer le bit de parité de la donnée seule, et de le comparer avec le bit de parité stocké dans la donnée avec la porte logique adaptée. Le circuit qui génère un bit de parité et celui qui vérifie si le bit de parité est valide sont donc très similaires.

Le générateur/checker d'ECC

[modifier | modifier le wikicode]

Pour ce qui est des codes de Hamming, ils calculent plusieurs bits de parité, qui sont calculé en prenant en compte une partie des bits de l'opérande. Un circuit qui génère le code de Hamming est donc composé de plusieurs circuits de génération de parité. Idem pour un circuit qui vérifie le code de Hamming d'une opérande.

Hamming(7,4)

Par exemple, voici ci-dessous le circuit pour vérifier un code de Hamming de type 7,4. Pour rappel, celui-ci prend des données sur 4 bits, et leur ajoute 3 bits de parité, ce qui fait en tout 7 bits : c'est de là que vient le nom de 7-4-3 du code. Chaque bit de parité se calcule à partir de 3 bits du nombre. Le schéma ci-contre indique quels sont les bits de données utilisés pour calculer un bit de parité : les bits de parité sont noté p, les bits de données d.

Le circuit est composé d'une première couche de portes XOR qui calculent le code de Hamming des 4 bits de données. Une seconde couche de portes XOR compare ce code calculé avec les trois bits d'ECC présents dans l'opérande. Si les deux valeurs correspondent, il n'y a pas d'erreur. Mais si les bits ne correspondent pas, alors on sait quel bit est erroné en regardant quel bit d'ECC est invalide. Uen couche de portes ET/NON sert de pseudo-décodeur, qui sélectionne le bit à corriger. Elle génére un masque de 4 bits qui indique quel bit inverser : celui dont le bit du masque est à 1. La dernière couche de portes XOR prend ce masque et l'applique aux 4 bits de données, ce qui inverse le bit adéquat.

Circuit de vérification d'un code de Hamming 7,4.


Les circuits séquentiels

[modifier | modifier le wikicode]

La totalité de l'électronique grand public est basée sur des circuits combinatoires auxquels on ajoute des mémoires. Pour le moment, on sait créer des circuits combinatoires, mais on ne sait pas faire des mémoires. Pourtant, on a déjà tout ce qu'il faut. Il est en effet parfaitement possible de créer des mémoires avec des portes logiques. Toutes les mémoires sont conçues à partir de circuits capables de mémoriser un ou plusieurs bits. Ces circuits sont ce qu'on appelle des bascules, ou flip-flops. Pour une question de simplicité, ce chapitre parlera des circuits capables de mémoriser un bit seulement, pas plusieurs. Nous verrons comment combiner ces bits pour former une mémoire ou des compteurs dans le chapitre suivant.

L'interface d'une bascule

[modifier | modifier le wikicode]

Avant de voir comment sont fabriquées les bascules, nous allons voir quelles sont leurs entrées et leurs sorties. La raison à cela est que des bascules aux entrées-sorties similaires peuvent être construites sur des principes suffisamment différents pour qu'on les voie à part. Si on ne regarde que les entrées-sorties, on peut grosso-modo classer les bascules en quelques grands types principaux : les bascules RS, les bascules JK et les bascules D. Nous ne parlerons pas des bascules JK dans ce qui va suivre, car elles sont très peu utilisées et que nous n'en ferons pas usage dans le reste du cours.

Les bascules D

[modifier | modifier le wikicode]
Interface d'une bascule D.

En premier lieu, on trouve les bascules D, les bascules les plus simples, qui ont deux entrées et deux sorties. Les deux entrées sont appelées D et E : D pour Data, E pour Enable. Le bit à mémoriser est envoyé directement sur l'entrée D. L'entrée Enable permet d'autoriser ou d'interdire les écritures dans la bascule. Ainsi, tant que cette entrée Enable reste à 0, le bit mémorisé par la bascule reste le même, peu importe ce qu'on met sur l'entrée D : il faut que l'entrée Enable passe à 1 pour que l'entrée soit recopiée dans la bascule et mémorisée. On trouve ensuite deux sorties complémentaires qui sont simplement l'inverse l'une de l'autre. La première sortie permet de lire le bit mémorisé dans la bascule RS, la seconde est simplement l'inverse de ce bit.

Les bascules RS

[modifier | modifier le wikicode]
Interface d'une bascule RS.

En second lieu, on trouve les bascules RS, qui possèdent deux entrées et deux sorties. La première sortie permet de lire le bit mémorisé dans la bascule RS, la seconde est simplement l'inverse de ce bit. Les deux sorties sont simplement l'inverse l'une de l'autre. Les deux entrées permettent de placer un 1 ou un 0 dans la bascule. L'entrée R permet de mettre un 1, l'entrée S permet d'y injecter un 0. Pour vous en rappeler, sachez que les entrées de la bascule ne sont nommées ainsi par hasard : R signifie Reset (qui signifie mise à zéro en anglais) et S signifie Set (qui veut dire mise à un en anglais).

Le principe de ces bascules est assez simple :

  • si on met un 1 sur l'entrée R et un 0 sur l'entrée S, la bascule mémorise un zéro ;
  • si on met un 0 sur l'entrée R et un 1 sur l'entrée S, la bascule mémorise un un ;
  • si on met un zéro sur les deux entrées, la sortie Q sera égale à la valeur mémorisée juste avant.
  • Si on met un 1 sur les deux entrées, on ne sait pas ce qui arrivera sur ses sorties. Après tout, quelle idée de mettre la bascule à un en même temps qu'on la met à zéro !
Entrée Reset Entrée Set Sortie Q
0 0 Bit mémorisé par la bascule
0 1 1
1 0 0
1 1 Dépend de la bascule

Le comportement obtenu quand on met deux 1 en entrée dépend de la bascule. Sur certaines bascules, appelées bascules à entrées non-dominantes, la combinaison est interdite : elle fait dysfonctionner le circuit et le résultat est imprédictible. Il faut dire que cette combinaison demande de mettre le circuit à la fois à 0 (entrée R) et à 1 (entrée S). Mais sur d'autres bascules dites à entrée R ou S dominante, l'entrée R sera prioritaire sur l'entrée S ou inversement. Sur les bascules à entrée R dominante, l'entrée R surpasse l'entrée S : la bascule est mise à 0 quand les deux entrées sont à 1. A l'inverse, sur les bascules à entrée S dominante, l'entrée S surpasse l'entrée R : la bascule est mise à 1 quand les deux entrées sont à 1.

Les bascules RS inversées

[modifier | modifier le wikicode]
Bascule RS inversée.

Il existe aussi des bascules RS inversées, où les entrées doivent être mises à 0 pour faire ce qu'on leur demande. Ces bascules fonctionnent différemment de la bascule précédente :

  • si on met un 1 sur l'entrée R et un 0 sur l'entrée S, la bascule mémorise un 1 ;
  • si on met un 0 sur l'entrée R et un 1 sur l'entrée S, la bascule mémorise un 0 ;
  • si on met un 1 sur les deux entrées, la sortie Q sera égale à la valeur mémorisée juste avant ;
  • si on met un 0 sur les deux entrées, le résultat est indéterminé.
Entrée /R Entrée /S Sortie Q
0 0 Interdit
0 1 0
1 0 1
1 1 Bit mémorisé par la bascule

Là encore, quand les deux entrées sont à 0, on fait face à trois possibilités, comme sur les bascules RS normales : soit le résultat est indéterminé, soit l'entrée R prédomine, soit l'entrée S prédomine.

Les bascules JK

[modifier | modifier le wikicode]
Bascule JK.

Les bascules JK peuvent être vues comme des bascules RS améliorées. La seule différence est ce qui se passe quand on envoie un 1 sur les entrées R et S. Sur une bascule RS, le résultat dépend de la bascule, il est indéterminé. Sur les bascules JK, le contenu de la bascule est inversée.

Entrée J Entrée K Sortie Q
0 0 Bit mémorisé par la bascule
0 1 1
1 0 0
1 1 inversion du bit mémorisé

Les bascules JK, RS et RS inversées à entrée Enable

[modifier | modifier le wikicode]
Bascule RS à entrée Enable.

Il est possible de modifier les bascules JK, RS et RS inversées, pour faire permettre d' « activer » ou d' « éteindre » les entrées R et S à volonté. En faisant cela, les entrées R et S ne fonctionnent que si l'on autorise la bascule à prendre en compte ses entrées.

Pour cela, il suffit de rajouter une entrée E à notre circuit. Suivant la valeur de cette entrée, l'écriture dans la bascule sera autorisée ou interdite. Si l'entrée E vaut zéro, alors tout ce qui se passe sur les entrées RS ou JK ne fera rien : la bascule conservera le bit mémorisé, sans le changer. Par contre, si l'entrée E vaut 1, alors les entrées RS ou JK feront ce qu'il faut et la bascule fonctionnera comme une bascule RS/JK normale.

La porte C, une bascule spéciale

[modifier | modifier le wikicode]
Porte-C

Enfin, nous allons voir la porte C, une bascule particulière qui sera utilisée quand nous verrons les circuits et les bus asynchrones. Elle a deux entrées A et B, comme les bascules RS et les bascules D, mais seulement une sortie. Quand les deux entrées sont identiques, la sortie de la bascule correspond à la valeur des entrées (cette valeur est mémorisée). Quand les deux entrées différent, la sortie correspond au bit mémorisé.

Entrée A Entrée B Sortie
0 0 0
0 1 Bit mémorisé par la bascule
1 0 Bit mémorisé par la bascule
1 1 1

L'implémentation des bascules avec des portes logiques

[modifier | modifier le wikicode]

Le principe qui se cache derrière toutes ces bascules est le même. Elles sont organisées autour d'un circuit dont on boucle la sortie sur son entrée. Cela veut dire que sa sortie est connectée à une de ses entrées, les autres entrées étant utilisées pour commander la bascule. Nous allons distinguer l'entrée bouclée et la ou les entrées de commande.

Bascule - fonctionnement interne.

Le circuit doit avoir une particularité bien précise : si l'entrée de commande est à la bonne valeur (0 sur certaines bascules, 1 sur d'autres), l'entrée bouclée est recopiée sur la sortie à l'identique. On dit que le circuit a des entrées potentiellement idempotentes. Ainsi, tant que l'entrée de commande est à la bonne valeur, la bascule sera dans un état stable où la sortie et l'entrée de commande restons à la valeur mémorisée. Le circuit en question peut être une porte logique centrale, qui peut être une porte ET, OU, XOR, NAND, NOR, NXOR, ou un multiplexeur.

Bascule - boucle de rétroaction

Toujours est-il qu'un circuit séquentiel contient toujours au moins une entrée reliée sur une sortie, contrairement aux circuits combinatoires, qui ne contiennent jamais la moindre boucle !

La bascule D fabriquée avec un multiplexeur

[modifier | modifier le wikicode]

Le cas le plus simple de circuit bouclé est la bascule D conçue à partir d'un multiplexeur. L'idée est très simple. Quand l'entrée Enable est à 0, la sortie du circuit est bouclée sur l'entrée : le bit mémorisé, qui était présent sur la sortie, est alors renvoyé en entrée, formant une boucle. Cette boucle reproduit en permanence le bit mémorisé. Par contre, quand l'entrée Enable vaut 1, la sortie du multiplexeur est reliée à l'entrée D. Ainsi, ce bit est alors renvoyé sur l'autre entrée : les deux entrées du multiplexeur valent le bit envoyé en entrée, mémorisant le bit dans la bascule.

Bascule D créée avec un multiplexeur.
Bascule D créée avec un multiplexeur.

Un défaut de cette implémentation est qu'elle ne fournit par la sortie , avec le bit inversée. Pour cela, il est possible d'ajouter une porte NON au circuit précédent.

Multiplexeur à deux entrées - circuit
Multiplexeur fabriqué avec des portes à transmission

Pour rappel, un multiplexeur peut s'implémenter de différentes manières, comme nous l'avons vu dans le chapitre sur les circuits de sélection. Il est possible de l'implémenter en utilisant seulement des portes logiques, ou alors en utilisant des portes à transmission. Les deux possibilités sont illustrées ci-contre.

Pour un multiplexeur fabriqué avec des portes logiques uniquement, boucler sa sortie sur son entrée ne pose aucun problème particulier. Mais un problème survient quand on veut fabriquer une bascule D avec de tels multiplexeurs. Le problème est qu'une porte à transmission est électriquement équivalente à un simple interrupteur, ce qui réduit le circuit à une boucle entre un interrupteur et un fil. Le courant qui circule dans le fil et l'interrupteur se dissipe rapidement du fait de la résistance du fil et disparait en quelques micro- ou millisecondes. Il n'y a pas de porte logique alimentée en courant/tension qui peut régénérer le signal électrique.

La solution est de rajouter des portes logiques dans la boucle. La solution la plus simple et la plus évidente, est de rajouter une porte OUI, la porte logique qui recopie son entrée sur sa sortie et dont l'utilité n'était pas évidente. Et la manière la plus simple de fabriquer une porte OUI est d'utiliser deux portes NON qui se suivent, ce qui donne le circuit ci-dessous. Cela garantit que la boucle est alimentée en courant/tension quand elle est fermée. Son contenu ne s'efface pas avec le temps, mais est automatiquement régénéré par les portes NON. L'ensemble sera stable tant que la boucle est fermée.

Implémentation conceptuelle d'une bascule D

L'avantage du circuit précédent est qu'il est plus économe en portes logiques que les autres bascules, même si ce n'est pas évident. Disons que son implémentation utilise moins de transistors, bien qu'on devra en reparler dans un chapitre ultérieur. Un autre avantage est que ce circuit permet d'avoir les deux sorties Q : la sortie Q inversée est prise en sortie de la première porte NON. Une variante du circuit précédent est utilisée dans les mémoires dites SRAM, qui sont utilisées pour les registres du processeur ou ses mémoires caches.

Les bascules D avec entrées de Set/Reset

[modifier | modifier le wikicode]
Circuit de mise à zéro d'un bit

Certaines bascules D ont une entrée R, qui met à zéro le bit mémorisé dans la bascule quand l'entrée R est à 1. Pour cela, elles ajoutent un circuit de mise à zéro, que nous avons déjà vu dans le chapitre sur les opérations bit à bit. Pour rappel, il s'agit d'un circuit qui prend un bit d'entrée, en plus du bit d'entrée R, et fournit la sortie adéquate. Le bit de sortie est soit égal au bit d'entrée si R = 0, soit égal à 0 si R = 1. e porte ET couplée à une porte NON, comme illustré ci-contre. Ce circuit de mise à zéro est placé après la seconde porte NON, et sa sortie est bouclée sur l'entrée du circuit. Le circuit obtenu est le suivant :

Bascule D avec entrée Reset

Le circuit peut se simplement fortement en fusionnant les trois portes situées entre les deux sorties Q, à savoir la porte ET et les deux portes NON qui la précédent. La loi de De Morgan nous dit que l'ensemble est équivalent à une porte NOR, ce qui donne le circuit suivant :

Bascule D avec entrée Reset, simplifiée

La bascule RS fabriquée avec une porte OU et une porte ET

[modifier | modifier le wikicode]

Un autre exemple est la forme la plus basique de bascule RS : la bascule RS de type ET-OU. Dans celle-ci, on trouve entre trois portes : une porte ET, une porte OU, et éventuellement une porte NON. Un exemple de porte RS de ce type est le suivant, d'autres manières de connecter le tout qui donnent le même résultat. On peut par exemple se passer d'inverseur, ou inverser l'ordre des portes logiques ET et OU. L'essentiel est la boucle indiquée en vert, qui fait que le bit de sortie est recopié sur les entrées bouclées des deux portes, l'ensemble formant une boucle qui relie la sortie à elle-même.

Bascule RS de type ET-OU.

Son fonctionnement est assez simple à expliquer. La porte ET a deux entrées, dont une est bouclée et l'autre est une entrée de commande. Les deux portes recopient leur entrée en sortie si on place ce qu'il faut sur l'entrée de commande. Par contre, toute autre valeur modifie le bit inséré dans la bascule.

  • Si on place un 0 sur l'entrée de commande de la porte OU, elle recopiera l'entrée bouclée sur sa sortie. Par contre, y mettre un 1 donnera un 1 en sortie, peu importe le contenu de l'entrée bouclée. En clair, l'entrée de commande de la porte OU sert d'entrée S à la bascule.
  • La porte ET recopie l'entrée bouclée, mais seulement si on place un 1 sur l'entrée de commande. Si on place un 0, elle aura une sortie égale à 0, peu importe l'entrée bouclée. En clair, l'entrée de commande de la porte ET est l'inverse de ce qu'on attend de l'entrée R à la bascule RS. Pour obtenir une véritable entrée R, il est possible d'ajouter une porte NON sur l'entrée /R, sur l'entrée de la porte ET. En faisant cela, on obtient une vraie bascule RS.

Si on essaye de concevoir le circuit, on se retrouve alors face à un choix : l'ordre dans lequel mettre les portes ET et OU. Les deux portes ET et OU peuvent être mises dans n'importe quel ordre : soit on met la porte ET avant la porte OU, soit on fait l'inverse. La seule différence sera ce qu'il se passe quand on active les deux entrées à la fois. Si la porte ET est située après la porte OU, l'entrée Reset sera prioritaire sur l'entrée Set quand elles sont toutes les deux à 1. Et inversement, si la porte OU est située après, ce sera le signal Set qui sera prioritaire. Voici ci-dessous les tables de vérité correspondantes pour chaque circuit.

Circuit avec la porte ET avant la porte OU
Entrée Reset Entrée Set Sortie Q Circuit
0 0 Bit mémorisé par la bascule Porte OU avant la porte ET.
1 0 0
X (0 ou 1) 1 1
Circuit avec la porte OU avant la porte ET
Entrée Reset Entrée Set Sortie Q Circuit
0 0 Bit mémorisé par la bascule Porte OU avant la porte ET.
0 1 0
1 X (0 ou 1) 1

Une autre manière de voir les choses est que ce circuit possède deux sorties Q équivalentes, situées aux deux points entre les portes OU et ET. Cette façon de voir les choses sera très utile dans ce qui va suivre.

Les bascules RS à NOR et à NAND

[modifier | modifier le wikicode]

Le circuit précédent a bien une sortie Q, mais pas de sortie /Q. Pour la rajouter, il suffit simplement d'ajouter une porte NON sur la sortie Q. Mais faire ainsi ne permet pas de profiter de certaines simplifications bien appréciables. Il est en effet possible de se débarrasser des deux portes NON, celle en amont de la porte ET, et de celle sur la sortie /Q. Pour cela, nous allons procéder d'une autre manière. Au lieu d'ajouter une seule porte NON, nous allons ajouter deux portes, en amont de la porte OU. En faisant, le circuit devient celui-ci :

Bascule RS à NOR - conception à partir d'une bascule ET-OU - 1

On peut alors regrouper des portes logiques consécutives. Premièrement, on peut regrouper la porte OU avec la porte NON immédiatement à sa suite. Mais on peut aussi regrouper la porte ET et les deux portes NON restantes. En effet, nous avons vu dans le chapitre sur les circuits combinatoires que cette combinaison de portes est équivalente à une porte NOR. Le circuit devient donc :

Bascule RS à NOR - conception à partir d'une bascule ET-OU - 2

Le résultat est ce qu'on appelle une bascule RS à NOR, qui tire son nom du fait qu'elle est fabriquée exclusivement avec des portes logiques NOR. En réorganisant le circuit, on trouve ceci :

Circuit d'une bascule RS à NOR.

Il est possible de faire la même manipulation, mais cette fois-ci sur une bascule RS inversée, de type ET-OU encore une fois. La bascule RS inversée est identique à la bascule ET-OU précédente, si ce n'est que la porte NON est placée sur l'entrée R et non sur l'entrée S. En clair, elle est sur une entrée de la porte OU et non sur la porte ET. Le résultat est une bascule RS à NAND, qui est une bascule RS inversée à deux sorties (Q et /Q), composée intégralement de portes NAND.

Circuit d'une bascule RS à NAND.

Les bascules peuvent se fabriquer à partir d'autres bascules

[modifier | modifier le wikicode]

Il y a quelques chapitres, nous avons vu qu'il est possible de créer une porte logique en combinant d'autres portes logiques. Et bien sachez qu'il est possible de faire la même chose pour des bascules. On peut par exemple fabriquer une bascule RS à partir d'une bascule D, et réciproquement. Ou encore, on peut fabriquer une bascule D à partir d'une bascule JK, et inversement. Les possibilités sont nombreuses. Et pour cela, il suffit juste d'ajouter un circuit combinatoire qui traduit les entrées de la bascule voulue vers les entrées de la bascule utilisée.

Le passage d'une bascule RS à une bascule RS inversée (et inversement)

[modifier | modifier le wikicode]

Il est possible de créér une bascule RS normale à partir d'une bascule RS inversée en inversant simplement les entrées R et S avec une porte NON. Et inversement, le passage d'une bascule RS normale à une bascule RS inversée se fait de la même manière. Il est possible de faire cela avec une bascule RS à portes NOR ou NAND, mais ainsi qu'avec une bascule RS à ET-OU.

Bascule RS conçue avec une bascule RS inversée.

Il s'agit d'une méthode simple, qui a la particularité de garder le caractère dominant/non-dominant des entrées. Rappelez-vous que pour des bascules RS à NOR ou RS à NAND, il y a une combinaison d'entrée qui est interdite. Mettez à 1 les deux entrées sur une RS à NOR, et le résultat sera indéterminé, pareil si vous mettez les deux entrées à 0 sur une bascule RS à NAND. Il faut dire qu'une telle combinaison demande de mettre à la bascule à la fois à zéro (signal R) et à 1 (entrée S). De telles bascules sont dites à entrées non-dominantes. Avec une bascule RS de type ET-OU, on n'a pas ce problème : suivant la bascule, on aura l'entrée R qui sera prioritaire, ou l'entrée S (suivant l'ordre des portes logiques : le ET avant le OU ou inversement). On dit alors que l'entrée R est dominante dans le premier cas, que l'entrée S est dominante dans le second. Et bien en mettant deux portes NON en entrée d'une bascule RS inversée, la bascule RS finale gardera le caractère dominant/non-dominant de ses entrées.

Cependant, il est possible de partir d'une bascule RS inversée/normale non-dominante, et d'en faire une bascule RS normale/inversée à entrée R ou S dominante. Pour cela, au lieu d'ajouter deux portes NON en entrée du circuit, on ajoute un petit circuit spécialement conçu. Ce circuit de conversion traduit les signaux d’entrée R et S en signaux /R et /S, (ou inversement). Prenons l'exemple d'une bascule RS normale à entrée R prioritaire, fabriquée à partir d'une bascule RS à NAND (inversée à entrée non-dominantes). La table de vérité du circuit de conversion des entrées est la suivante. Rappelez-vous que l'on veut que l'entrée R soit prioritaire. Ce qui veut dire que si R est à 1, alors on garantit que le signal /R est actif et que /S est inactif. On a donc :

R S
0 0 1 1
0 1 1 0
1 0 0 1
1 1 0 1

L'entrée n'est autre que l'inverse de l'entrée R, ce qui fait qu'une simple porte NON suffit.

L'entrée a pour équation logique :

Le tout donne le circuit suivant :

FF NAND-RS R-dominant

L'implémentation des bascules RS avec une entrée Enable

[modifier | modifier le wikicode]
Bascule RS à entrée Enable fabriquée à partir d'une bascule RS normale.

Dans la section sur l'interface d'une bascule, nous avons vu l'entrée Enable, qui active ou désactive l'écriture dans une bascule. Elle est toujours présente sur les bascules D, mais facultative sur les bascules RS. Les bascules précédentes ne disposent pas de l'entrée Enable, sauf dans le cas de la bascule D. Elles sont donc conceptuellement plus simples que celles qui disposent d'une entrée Enable. Et vous l'avez peut-être senti venir, mais il est possible de modifier les bascules sans entrée Enable, pour leur ajouter cette entrée. Notamment, il est possible de modifier une bascules RS normale pour lui ajouter une entrée Enable. Pour cela, il suffit d'ajouter un circuit avant les entrées R et S, qui inactivera celles-ci si l'entrée E vaut zéro. La table de vérité de ce circuit est identique à celle d'une simple porte ET.

Le circuit obtenu en utilisant une bascule RS à NOR est celui-ci :

Circuit d'une bascule RS à entrée Enable.

Et le circuit obtenu avec une bascule à portes NAND est le suivant. Le circuit est simple à comprendre : on part d'une porte RS inversée à NAND, on ajoute deux portes NON pour en faire une porte RS normale, puis on ajoute les deux portes ET pour l'entrée Enable. Ensuite, on simplifie le circuit en fusionnant les portes ET avec les portes NON, ce qui donne des portes NAND. Le tout donne le circuit simplifié suivant :

Circuit d'une bascule RS à entrée Enable, second.

Les bascules D conçues à partir de bascules RS à entrée Enable

[modifier | modifier le wikicode]

On peut construire une bascule D à partir d'une simple bascule RS ou RS inversée. Il suffit d'ajouter un circuit qui déduise quoi mettre sur les entrées R et S suivant la valeur sur D. Mais en réfléchissant un peu, on se rend compte qu'il est préférable d'utiliser une bascule RS à entrée Enable. En effet, l'entrée Enable de la bascule D et de la bascule RS sont la même, elles ont exactement le même comportement et la même utilité. Dans ce cas, il suffit de prendre une bascule RS à entrée Enable et d'ajouter un circuit qui convertit l'entrée D en Entrées R et S.

Bascule D fabriquée avec une bascule RS, à NOR.

Pour une bascule RS normale, on peut remarquer que l'entrée R est toujours égale à l'inverse de D, alors que S est toujours strictement égale à D. Il suffit d'ajouter une porte NON avant l'entrée R d'une bascule RS à entrée Enable, pour obtenir une bascule D. On peut faire cela à la fois avec des bascules RS à NOR ou à NAND.

Bascule D à NAND.
Bascule D fabriquée à partir d'une bascule RS à entrée Enable de type NAND.

Il est possible d'améliorer légèrement le circuit précédent, afin de retirer la porte NON, en changeant le câblage du circuit. En effet, la porte NON inverse l'entrée D tout le temps, quelle que soit la valeur de l'entrée Enable. Mais on n'en a besoin que lorsque l'entrée Enable est à 1. On peut donc remplacer la porte NON par une porte qui sort un 0 quand l'entrée D et l'entrée Enable sont à 1, mais qui sort un 1 sinon. Il s'agit ni plus ni moins qu'une porte NAND, et le circuit précédent la contient déjà : c'est celle en haut à gauche. On peut donc prendre sa sortie pour l'envoyer au bon endroit, ce qui donne le circuit suivant :

Bascule D à NAND.

Il est aussi possible de fabriquer une bascule D avec une bascule RS à ET/OU. Le circuit obtenu est alors identique au circuit obtenu avec un multiplexeur basé sur des portes logiques.

Les bascules JK conçues à partir de bascules RS

[modifier | modifier le wikicode]

Il est possible de construire une bascule JK à partir d'une bascule RS. Ce qui n'est pas étonnant, vu que les bascules RS et JK sont très ressemblantes. Il suffit d'ajouter un circuit qui déduise quoi mettre sur les entrées R et S suivant la valeur sur les entrées J et K. Le circuit en question est composé de deux portes ET, une par entrée.

Bascule JK obtenue à partir d'une bascule RS.

Il est possible de faire la même chose avec une bascule RS à entrée Enable, qui donne une bascule JK à entrée Enable.

Bascule JK obtenue à partir d'une bascule RS à entrée Enable.


Les bascules sont rarement utilisées seules. Elles sont combinées avec des circuits combinatoires pour former des circuits qui possèdent une capacité de mémorisation, appelés circuits séquentiels. L'ensemble des informations mémorisées dans un circuit séquentiel, le contenu de ses bascules, forme ce qu'on appelle l'état du circuit, aussi appelé la mémoire du circuit séquentiel. Un circuit séquentiel peut ainsi être découpé en deux morceaux : des bascules qui stockent l'état du circuit, et des circuits combinatoires pour mettre à jour l'état du circuit et sa sortie. Suivant la méthode utilisée pour déterminer la sortie, on peut classer les circuits séquentiels en deux catégories :

  • les automates de Moore, où la sortie ne dépend que de l'état mémorisé ;
  • et les automates de Mealy, où la sortie dépend de l'état du circuit et de ses entrées.
Ces derniers ont tendance à utiliser moins de portes logiques que les automates de Moore.
Automate de Moore et de Mealy

Concevoir des circuits séquentiels demande d'utiliser un formalisme assez complexe et des outils comme des machines à état finis (finite state machine). Mais nous ne parlerons pas de cela dans ce cours, car nous n'aurons heureusement pas à les utiliser.

La majorité des circuits séquentiels possèdent plusieurs bascules, dont certaines doivent être synchronisées entre elles. Sauf qu'un léger détail vient mettre son grain de sel : tous les circuits combinatoires ne vont pas à la même vitesse ! Si on change l'entrée d'un circuit combinatoire, cela se répercutera sur ses sorties. Mais toutes les sorties ne sont pas mises en même temps et certaines sorties seront mises à jour avant les autres ! Cela ne pose pas de problèmes avec un circuit combinatoire, mais ce n'est pas le cas si une boucle est impliquée, comme dans les circuits séquentiels. Si les sorties sont renvoyées sur les entrées, alors le résultat sur l'entrée sera un mix entre certaines sorties en avance et certaines sorties non-mises à jour. Le circuit combinatoire donnera alors un résultat erroné en sortie. Certes, la présence de l'entrée Enable permet de limiter ce problème, mais rien ne garantit qu'elle soit mise à jour au bon moment. En conséquence, les bascules ne sont pas mises à jour en même temps, ce qui pose quelques problèmes relativement fâcheux si aucune mesure n'est prise.

Le temps de propagation

[modifier | modifier le wikicode]

Pour commencer, il nous faut expliquer pourquoi tous les circuits combinatoires ne vont pas à la même vitesse. Tout circuit, quel qu'il soit, va mettre un petit peu de temps avant de réagir. Ce temps mis par le circuit pour propager un changement sur les entrées vers la sortie s'appelle le temps de propagation. Pour faire simple, c'est le temps que met un circuit à faire ce qu'on lui demande : plus ce temps de propagation est élevé, plus le circuit est lent. Ce temps de propagation dépend de pas mal de paramètres, aussi je ne vais citer que les principaux.

Le temps de propagation des portes logiques

[modifier | modifier le wikicode]

Une porte logique n'est pas un système parfait et reste soumis aux lois de la physique. Notamment, il n'a pas une évolution instantanée et met toujours un petit peu de temps avant de changer d'état. Quand un bit à l'entrée d'une porte logique change, elle met du temps avant de changer sa sortie. Ce temps de réaction pour propager un changement fait sur les entrées vers la sortie s'appelle le temps de propagation de la porte logique. Pour être plus précis, il existe deux temps de propagation : un temps pour passer la sortie de 0 à 1, et un temps pour la passer de 1 à 0. Les électroniciens utilisent souvent la moyenne entre ces deux temps de propagation, et la nomment le retard de propagation, noté .

Temps de propagation d'une porte logique.

Le chemin critique

[modifier | modifier le wikicode]
Délai de propagation dans un circuit simple.

Si le temps de propagation de chaque porte logique a son importance, il faut aussi tenir compte de la manière dont elles sont reliées. La relation entre "temps de propagation d'un circuit" et "temps de propagation de ses portes" n'est pas simple. Deux paramètres vont venir jouer les trouble-fêtes : le chemin critique et la sortance des portes logiques. Commençons par voir le chemin critique, qui n'est autre que le nombre maximal de portes logiques entre une entrée et une sortie de notre circuit. Pour donner un exemple, nous allons prendre le schéma ci-contre. Pour ce circuit, le chemin critique est dessiné en rouge. En suivant ce chemin, on va traverser trois portes logiques, contre deux ou une dans les autres chemins.

Le temps de propagation total, lié au chemin critique, se calcule à partie de plusieurs paramètres. Premièrement, il faut déterminer quel est le temps de propagation pour chaque porte logique du circuit. En effet, chaque porte logique met un certain temps avant de fournir son résultat en sortie : quand les entrées sont modifiées, il faut un peu de temps pour que sa sortie change. Ensuite, pour chaque porte, il faut ajouter le temps de propagation des portes qui précédent. Si plusieurs portes sont reliées sur les entrées, on prend le temps le plus élevé. Enfin, il faut identifier le chemin critique, le plus long : le temps de propagation de ce chemin est le temps qui donne le tempo maximal du circuit.

Temps de propagation par porte logique.
Temps de propagation pour chaque chemin.
Identification du chemin critique.

La sortance des portes logiques

[modifier | modifier le wikicode]

Passons maintenant au second paramètre lié à l'interconnexion entre portes logiques : la sortance. Dans les circuits complexes, il n'est pas rare que la sortie d'une porte logique soit reliée à plusieurs entrées (d'autre portes logiques). Le nombre d'entrées connectées à une sortie est appelé la sortance de la sortie. Il se trouve que plus on connecte de portes logiques sur une sortie, (plus sa sortance est élevée), plus il faudra du temps pour que la tension à l'entrée de ces portes passe de 1 à 0 (ou inversement). La raison en est que la porte logique fournit un courant fixe sur sa sortie, qui charge les entrées en tension électrique. Un courant positif assez fort charge les entrées à 1, alors qu'un courant nul ne charge pas les entrées qui retombent à 0. Avec plusieurs entrées, la répartition est approximativement équitable et chaque entrée reçoit seulement une partie du courant de sortie. Elles mettent plus de temps à se remplir de charges, ce qui fait que la tension met plus de temps à monter jusqu'à 1.

Influence de la sortance d'un circuit sur sa fréquence-période

Le temps de latence des fils

[modifier | modifier le wikicode]

Enfin, il faut tenir compte du temps de propagation dans les fils, celui mis par notre tension pour se propager dans les fils qui relient les portes logiques entre elles. Ce temps perdu dans les fils devient de plus en plus important au cours du temps, les transistors et portes logiques devenant de plus en plus rapides à force de les miniaturiser. Par exemple, si vous comptez créer un circuit avec des entrées de 256 à 512 bits, il vaut mieux le modifier pour minimiser le temps perdu dans les interconnexions que de diminuer le chemin critique.

Les circuits synchrones et asynchrones

[modifier | modifier le wikicode]

Sur les circuits purement combinatoires, le temps de propagation n'est que rarement un souci, à moins de rencontrer des soucis de métastabilité assez compliqués. Par contre, le temps de propagation doit être pris en compte quand on crée un circuit séquentiel : sans ça on ne sait pas quand mettre à jour les bascules du circuit. Si on le fait trop tôt, le circuit combinatoire peut sauter des états : il se peut parfaitement qu'on change le bit placé sur l'entrée avant qu'il ne soit mémorisé. De plus, les différents circuits d'un composant électronique n'ont pas tous le même temps de propagation, et ceux-ci vont fonctionner à des vitesses différentes. Si l'on ne fait rien, on peut se retrouver avec des dysfonctionnements : par exemple, un circuit lent peut rater deux ou trois nombres envoyés par un composant un peu trop rapide.

Pour éviter les ennuis dus à l'existence de ce temps de propagation, il existe deux grandes solutions, qui permettent de faire la différence entre circuits asynchrones et synchrones. Dans les circuits synchrones, les bascules sont mises à jour en même temps. A l'opposé, les circuits asynchrones préviennent les bascules quand ils veulent la mettre à jour. Quand le circuit combinatoire et les bascules sont tous les deux prêts, on autorise l'écriture dans les bascules.

Les circuits asynchrones ne sont presque pas utilisés dans la quasi-totalité des ordinateurs modernes, qui sont des circuits synchrones. Nous ne verrons pas beaucoup de circuits asynchrones dans la suite du cours. Il faut dire que la grosse majorité des processeurs, mémoires et périphériques, sont des composants synchrones. La seule exception que nous verrons dans ce cours sont les anciennes mémoires DRAM asynchrones, qui sont aujourd'hui obsolètes, mais ont été utilisées dans les anciens PCs. Aussi, nous allons nous concentrer sur les circuits synchrones dans ce qui suit.

Le signal d'horloge des circuits synchrones

[modifier | modifier le wikicode]

Les circuits synchrones mettent à jour leurs bascules à intervalles réguliers. La durée entre deux mises à jour est constante et doit être plus grande que le temps de propagation le plus long du circuit : on se cale donc sur le circuit combinatoire le plus lent. Les concepteurs d'un circuit doivent estimer le pire temps de propagation possible pour le circuit et ajouter une marge de sûreté.

Pour mettre à jour les circuits à intervalles réguliers, le signal d'autorisation d'écriture est une tension qui varie de façon cyclique : on parle alors de signal d'horloge. Le temps que met la tension pour effectuer un cycle est ce qu'on appelle la période. Le nombre de périodes par seconde est appelé la fréquence. Elle se mesure en hertz. On voit sur ce schéma que la tension ne peut pas varier instantanément : elle met un certain temps pour passer de 0 à 1 et de 1 à 0. On appelle cela un front. Le passage de 0 à 1 est appelé un front montant et le passage de 1 à 0 un front descendant.

Fréquence et période.

Un point important est que le signal d'horloge passe régulièrement de 0 à 1. Dans le cas idéal, 50% de la période est à l'état 1 et les 50% restants à l'état 0. On a alors un signal carré. Mais il arrive que le temps passé à l'état 1 ne soit pas forcément le même que le temps passé à 0. Par exemple, le signal peut passer 10% de la période à l'état 1 et 90% du temps à l'état 0. C'est assez rare, mais possible et même parfois utile. Le signal n'est alors pas appelé un signal carré, mais un signal rectangulaire. Le signal d'horloge est donc à 1 durant un certain pourcentage de la période. Ce pourcentage est appelé le rapport cyclique (duty cycle).

Signaux d'horloge asymétriques

En faisant cela, le circuit mettra ses sorties à jour lors d'un front montant (ou descendant) sur son entrée d'horloge. Entre deux fronts montants (ou descendants), le circuit ne réagit pas aux variations des entrées. Rappelons que seuls les circuits séquentiels doivent être synchronisés ainsi, les circuits combinatoires étant épargnés par les problématiques de synchronisation. Pour que les circuits séquentiels soient cadencés par une horloge, les bascules du circuit sont modifiées de manière à réagir aux fronts montants et/ou aux fronts descendants, ce qui fait que la mise à jour de l'état interne du circuit est synchronisée sur l'horloge. Évidemment, l’horloge est envoyée au circuit via une entrée spéciale : l'entrée d'horloge. L'horloge est ensuite distribuée à l'intérieur du composant, jusqu'aux bascules, par un ensemble de connexions qui relient l'entrée d'horloge aux bascules.

Circuit séquentiel synchrone.

La fréquence influence la performance d'un circuit

[modifier | modifier le wikicode]

En théorie, plus un composant utilise une fréquence élevée, plus il est rapide. C'est assez intuitif : plus un composant peut changer d'état un grand nombre de fois par seconde, plus, il peut faire de calculs, et plus il est performant. Mais attention : un processeur de 4 gigahertz peut être bien plus rapide qu'un processeur de 20 gigahertz, pour des raisons techniques qu'on verra plus tard dans ce cours. Si dans les grandes lignes, une fréquence plus élevée signifie une performance plus élevée, ce n'est qu'une règle heuristique assez imparfaite.

Un autre point important est que plus la fréquence d'un composant est élevée, plus il chauffe et consomme d'énergie. Nous ne pouvons pas expliquer pourquoi pour le moment, mais sachez que nous détaillerons cela dans le chapitre sur les tendances technologiques. Disons pour simplifier que plus la fréquence d'un composant est élevée, plus il change d'état fréquemment par seconde, et que chaque changement d'état consomme de l'énergie. Sur les circuits CMOS modernes, la consommation d'énergie est proportionnelle à la fréquence. Ne vous étonnez donc pas que les circuits qui fonctionnent à haute fréquence, comme le processeur, chauffent plus que les circuits de basse fréquence. S'il y a un radiateur et un ventilateur sur un processeur, c'est en partie à cause de ça.

Dans un ordinateur moderne, chaque composant a sa propre horloge, qui peut être plus ou moins rapide que les autres. Par exemple, le processeur fonctionne avec une horloge différente de l'horloge de la mémoire RAM ou des périphériques. La présence de plusieurs horloges vient du fait que certains composants sont plus lents que d'autres. Plutôt que de caler tous les composants d'un ordinateur sur le plus lent en utilisant une seule horloge, il vaut mieux utiliser une horloge différente pour chacun. Les mises à jour des registres sont synchronisées à l'intérieur d'un composant (dans un processeur, ou une mémoire), alors que les composants eux-mêmes synchronisent leurs communications avec d'autres mécanismes. Ces multiples signaux d'horloge dérivent d'une horloge de base qui est « transformée » en plusieurs horloges, grâce à des montages électroniques spécialisés (des PLL ou des montages à portes logiques un peu particuliers).

Les bascules synchrones

[modifier | modifier le wikicode]

Utiliser une horloge demande cependant d'adapter les circuits vus précédemment, les bascules devant être modifiées. En effet, les bascules précédentes sont mises à jour quand un signal d'autorisation est mis à 1. Mais avec un signal d'horloge, les bascules doivent être mises à jour lors d'un front, montant ou descendant, peu importe. Pour cela, les bascules ont une entrée d'autorisation d'écriture modifiée, qui réagit au signal d'horloge. Les bascules commandées par une horloge sont appelées des bascules synchrones.

Les bascules synchrones peuvent mettre à jour leur contenu soit lors d'un front montant, soit d'un front descendant, soit les deux, soit lorsque la tension d'horloge est à 1. Suivant le cas, le symbole utilisé pour représenter l'entrée d'horloge est différent, comme illustré ci-dessous.

Symb