5. * Mémoire

Les transistors, les portes logiques et leur représentation en tables de vérités, permettent de manipuler des 0 et des 1 au niveau physique… Tant qu’un courant électrique se déplace dans les circuits, on est capable de le transformer, de le laisser passer ou de l’arrêter, dans le but d’exprimer des portes « ouvertes » ou des portes « fermées » et donc des nombres binaires. L’ALU, explorée au chapitre précédent, va une étape plus loin et permet de choisir une opération à effectuer en fonction de bits de contrôle supplémentaire, et livre le résultat de l’opération arithmétique ou logique choisie.

Mais comment faire pour stocker cette information ? Comment faire pour que l’on se rappelle le résultat d’une addition effectuée par une ALU afin de pouvoir réutiliser cette valeur plus tard ? C’est là que nous avons besoin de mémoire.

Dans les ordinateurs, il y a en fait plusieurs types de mémoires, qu’on peut classer en deux grandes catégories. La mémoire volatile, et la mémoire non volatile. La mémoire volatile s’efface quand la machine et éteinte. C’est le cas de la RAM (random-access memory), par exemple. La mémoire non volatile, elle, persiste. C’est le cas d’un disque dur ou d’un SSD (solid-state drive). Si un smartphone s’éteint inopinément alors qu’on est en train de retoucher une photo sans avoir validé les modifications, ces retouches disparaissent. Elles étaient stockées sur la mémoire volatile. Par contre, au moment où ces retouches sont sauvegardées, elles s’inscrivent dans la mémoire non volatile.

On peut se demander pourquoi on n’utiliserait pas que de la mémoire non volatile, vu les « risques » posés par la mémoire volatile. La réponse est que la mémoire non volatile va probablement être entre 100 et 100 000 fois moins rapide que la mémoire volatile. On privilégie donc la mémoire volatile comme mémoire de travail rapide d’un ordinateur.

Dans les sections qui suivent, on propose de s’intéresser au cas le plus simple: la construction d’une cellule de mémoire volatile qui sera à même de stocker un bit. Par la suite, nous discuterons de la manière dont ce genre de mémoire est utilisée au cœur des microprocesseurs.

5.1. Le verrou SR

L’idée principale derrière la conception d’un circuit logique qui est capable de stocker un signal est que l’on va utiliser la ou les sorties du circuit en les reconnectant à certaines de ses entrées. Essayons par exemple ce circuit simple avec une seule porte OU :

Au début, les deux entrées de la porte valent 0, comme sa sortie. Si l’on essaie de faire passer l’entrée \(X\) à 1, on voit que la sortie \(Z\) passera à 1 elle aussi, comme il s’agit d’une porte OU. Mais comme \(Z\) est aussi relié à l’autre entrée de la porte, on a maintenant un circuit dont on ne peut plus modifier la sortie : même si \(X\) passe de nouveau à 0, l’autre entrée reste à 1 et suffit donc pour que \(Z\) vale maintenant 1 indéfiniment. On est obligé de remettre le circuit complètement à zéro (l’équivalent de débrancher la prise de courant et de la rebrancher) pour obtenir à nouveau un 0 sur la sortie \(Z\).

Assurément, ce circuit n’est pas très intéressant : il se bloque dans un état sans retour possible. Serait-ce possible de construire un circuit un peu plus élaboré qui permettrait de choisir la valeur de sa sortie et de la conserver ? Ces circuits existent en effet et sont à la base du stockage de l’information dans les microprocesseurs. On appelle ces circuits des verrous, vu qu’ils « verrouillent » une valeur donnée.

Examinons le circuit ci-dessous : c’est le verrou dit « SR », pour set/reset, en anglais.

Ce circuit stocke un bit de donnée un 0 ou un 1, qu’on va pouvoir lire via la sortie \(Q\) et modifier avec les deux entrées \(R\) et \(S\). (La seconde sortie \(\bar{Q}\) est ici toujours l’inverse de \(Q\).)

Dans l’état normal de ce verrou, la sortie \(Q\) vaut soit 1, soit 0, et les deux entrées \(S\) et \(R\) restent à 0. Testez le circuit ci-dessus et observez l’effet de \(R\) et \(S\). \(S\), pour set, sert à changer l’état du verrou pour lui faire dorénavant stocker un 1. « Allumer » \(S\) cause ainsi \(Q\) à passer à 1. Ce qu’il y a d’intéressant, c’est qu’une fois que \(Q\) est passé à 1, on peut sans autre « éteindre » le signal \(S\) et le faire repasser à 0, et la sortie \(Q\), elle, reste à 1 — alors que les deux entrées du circuit \(S\) et \(R\) sont maintenant à nouveau les mêmes qu’avant, lorsque la sortie \(Q\) valait 0.

De manière similaire, l’entrée \(R\), pour reset, sert à faire passer la valeur stockée par le du verrou à 0, et cet état reste 0 même lorsque \(R\) est de nouveau « éteint ».

On essaie en général d’éviter d’avoir un 1 sur \(R\) et sur \(S\) en même temps, cela place le verrou dans un état où \(\bar{Q}\) n’est plus l’inverse de \(Q\). Pour cette raison, nous allons plutôt créer le circuit comme suit — les connexions sont exactement les mêmes, mais les entrées \(S\) et \(R\) ne restent pas à 1 lorsqu’on clique dessus, elles retombent à 0 dès que le clic se termine.

Ces verrous sont communs, et pour le reste du chapitre, on simplifiera la notation pour les représenter ainsi, sans changement de fonctionnalité, mais en faisant abstraction des détails internes :

5.2. La bascule D

Un souci avec le verrou SR est qu’on a rarement un signal d’entrée qui soit facilement exploitable pour être « converti » en cette logique set/reset. La plupart du temps, on a simplement un signal donné, disons \(D\), pour « donnée » (ou data en anglais), et c’est ce signal-ci qu’on aimerait stocker. Avec ce système, il serait impossible de connecter \(D\) à ce verrou ; on ne peut le brancher directement ni à l’entrée \(S\), ni à l’entrée \(R\).

On va utiliser pour cela un circuit similaire, mais qui fonctionne un peu différemment, qui s’appelle une bascule D1 :

Cette bascule va stocker son entrée \(D\) et la propager sur sa sortie \(Q\) uniquement lorsque l’entrée spéciale \(Horloge\) passe de 0 à 1. Le reste du temps, \(Q\) et \(\overline{Q}\) garderont leur valeur précédente. Notez que cette bascule a aussi deux entrées \(S\) et \(R\), qui servent à forcer l’état interne à valoir 1 ou 0, respectivement, indépendamment du signal \(D\) et de l’horloge.

Testez cette bascule. Réglez l’entrée de données \(D\) à 1 ou 0 et observez comme la bascule ne réagit pas : sa sortie \(Q\) reste telle quelle. Donnez ensuite une impulsion en cliquant sur l’entrée \(Horloge\) et voyez comme la valeur de \(D\) est maintenant stockée sur la bascule.

Exercice 6 : stocker deux bits

Créez un circuit qui calcule, d’une part, le OU de deux entrées \(X\) et \(Y\), et, d’autre part, le ET de ces deux mêmes entrées. À l’aide de bascules D, complétez le circuit de manière à ce qu’il stocke ces deux valeurs calculées lors d’un coup d’horloge et les sorte sur les sorties \(P\) et \(Q\), respectivement. Faites finalement en sorte que le signal \(Reset\), si activé, réinitialise les bascules à 0. Vérifiez qu’une fois les valeurs stockées par les bascules, des changements sur les entrées \(X\) et \(Y\) n’aient pas d’effet direct sur \(P\) et \(Q\).

Exercice 7 : signal alternatif

À l’aide d’une bascule, créez un circuit avec une sortie \(Q\) qui s’inverse à chaque coup d’horloge.

Exercice 8 : jeu de fréquences

Observez le circuit ci-dessous. L’horloge principale \(A\) fonctionne ici toute seule et produit un coup d’horloge par seconde (elle a donc une fréquence d’un hertz — 1 Hz). Que pouvez-vous dire des signaux \(B\) et \(C\) par rapport au signal \(A\) ? Comment expliquer cela avec ce que vous savez des bascules ? (Pour simplifier, le délai de propagation est ici presque nul.)

Vous pouvez mettre l’animation en pause et exécuter chaque transition pas à pas pour mieux comprendre ce qui se passe.

5.3. Addition en plusieurs étapes

Dans cet exemple final, nous allons construire un circuit capable d’effectuer l’addition de plusieurs nombres ; par exemple, d’évaluer la somme \(1 + 4 + 5 + 3\) pour trouver \(13\).

Si ce calcul a l’air simple, il s’y cache une subtilité : nous n’avons aucun circuit auquel nous pourrions donner quatre nombres et qui en ferait la somme. Nous ne savons additionner que deux nombres à la fois. Mais nous pouvons additionner progressivement les nombres un à un à une sorte d’« accumulateur » qui stockerait les résultats intermédiaires. Au début, avant d’avoir additionné quoi que ce soit, cet accumulateur représenterait un 0. Ensuite, on y additionnerait, l’un après l’autre, chacun des nombres du calcul ainsi :

\[\begin{split}\begin{aligned} 0 + 1 &= 1 \\ 1 + 4 &= 5 \\ 5 + 5 &= 10 \\ 10 + 3 &= 13 \end{aligned}\end{split}\]

Chacune de ces lignes a la forme « accumulateur + nombre à additionner = nouvel accumulateur ».

L’avantage de procéder ainsi, en décomposant à l’extrême, est que chaque étape est une addition de précisément deux nombres — et nous savons faire de telles additions avec une ALU.

Commençons à créer un circuit capable de faire ceci. Notre ALU opérant sur des nombres de 4 bits, prenons le parti de représenter notre accumulateur via également 4 bits — 4 cellules mémoire, et donc 4 bascules. Pour remettre l’accumulateur à zéro, nous allons connecter un signal unique au reset de chacune de ces bascules. Nous allons aussi, comme chaque fois, connecter un signal d’horloge aux bascules, pour leur indiquer leur moment où elles doivent stocker les valeurs qui sont sur leurs entrées respectives. Ajoutons aussi une ALU pour effectuer l’addition et un afficheur décimal pour les 4 bits stockés dans les bascules.

Cela nous donne ce début de circuit, qui pour l’instant n’est pas fonctionnel :

Connectons maintenant les entrées de l’ALU. On se rappelle qu’à chaque étape, l’ALU calculera une addition de la forme « accumulateur + nombre à additionner = nouvel accumulateur ». L’entrée \(A\) de l’ALU est la valeur de l’accumulateur, donc ce qui est stocké par nos bascules. On connecte donc la sortie \(Q\) de chaque bascule vers le bit d’entrée \(A\) correspondant de l’ALU.

L’entrée \(B\) de l’ALU est le nouveau nombre à additionner. Pour cela, nous ajoutons simplement quatre entrées normales, ainsi qu’un afficheur décimal pour nous simplifier la lecture du nombre représenté par ces entrées :

Il reste à connecter la sortie \(S\) de l’ALU. Cette sortie nous livre la prochaine valeur à stocker dans l’accumulateur, et nous pouvons ainsi la connecter aux quatre entrées \(D\) des bascules.

Voici le circuit final :

Ce circuit fonctionne ainsi : au début du calcul, on réinitialise les bascules à zéro avec le signal \(Reset\). Ensuite, on compose le prochain nombre à additionner sur l’entrée \(B\). L’ALU va calculer immédiatement la somme \(A + B\), mais ce n’est qu’au prochain coup d’horloge que cette somme sera stockée dans les bascules et apparaîtra ainsi à droite. Après avoir donné ce coup d’horloge, donc, on pourra à nouveau composer sur l’entrée \(B\) le prochain nombre à additionner, et ainsi de suite.

On réalise ici l’importance du coup d’horloge : si les bascules stockaient immédiatement la valeur livrée par l’ALU sans attendre le coup d’horloge, on retrouverait presque sans délai cette valeur sur la sortie des bascules et donc… à l’entrée \(A\) de l’ALU, qui recalculerait immédiatement la somme de cette valeur et de l’entrée \(B\), livrerait le résultat sur la sortie vers les bascules, qui feraient à nouveau la propagation immédiate de ceci sur leurs sorties et sur l’entrée \(A\) de l’ALU, etc. — le système s’emballerait. Le signal d’horloge veille à ce que l’opération de stockage et de propagation soit coordonnée et se passe au bon moment.

Exercice 9 : additions avec bascules

Suivez la procédure décrite ci-dessus pour effectuer l’addition \(1 + 4 + 5 + 3 = 13\).

5.4. Récapitulatif

Au cours des quatre chapitres précédents, nous avons vu comment les portes logiques sont utilisées comme composants de base des ordinateurs. Nous avons d’abord exploré des portes simples comme OU et ET, puis montré comment ces portes peuvent être combinées en systèmes logiques plus complexes.

Avec des portes, nous avons construit un additionneur de deux bits. Nous avons ensuite été à même, en enchaînant plusieurs additionneurs, de créer un système qui peut additionner non pas simplement deux bits, mais deux nombres entiers codés sur 4 bits chacun.

Nous avons ensuite découvert l’unité arithmétique et logique, capable de réaliser plusieurs opérations différentes avec ses entrées en fonction de bits supplémentaires qui permettent de sélectionner l’opération à effectuer.

Notre dernière étape d’exploration des systèmes logiques nous a menés aux verrous et aux bascules, des composants pensés pour stocker des bits de données et ainsi constituer des cellules de mémoire pour l’ordinateur. Nous avons enfin été capables, avec une ALU et une série de bascules, d’additionner à la chaîne plusieurs nombres, en nous rappelant les résultats des additions intermédiaires.

Il existe bien d’autres éléments qui composent les ordinateurs et nous n’avons pas l’occasion de tous les explorer en détail. Dans la section qui suit, faisons un saut conceptuel et parlons de l’architecture générale des ordinateurs et de la manière dont les grands composants sont interconnectés pour permettre à un ordinateur de remplir les fonctions que nous lui connaissons.


1

Il y a une différence conceptuelle fondamentale entre les verrous et les bascules : les verrous sont des composants dits asynchrones, dont l’état peut changer dès qu’une des entrées change, alors que les bascules sont des composants dits synchrones, qui ont une entrée appelée Horloge, et dont l’état ne changera qu’au moment où le signal d’horloge effectuera une transition (dans notre cas, passera de 0 à 1). Une discussion plus poussée de ces différences dépasse le cadre de ce cours.