< Fonctionnement d'un ordinateur

Avec les circuits combinatoires, on sait traiter et manipuler de l’information. Il nous manque encore une chose : la mémoriser. La valeur de la sortie de ces circuits ne dépend que de l'entrée et pas de ce qui s'est passé auparavant : les circuits combinatoires n'ont pas de mémoire. Pour répondre à ce besoin, les électroniciens ont inventé des circuits séquentiels qui possèdent une capacité de mémorisation. L'ensemble des informations mémorisées dans un circuit séquentiel forme ce qu'on appelle l'état du circuit. Pour mémoriser cet état, un circuit doit posséder des composants pour stocker un ou plusieurs bits : ce sont des mémoires. On verra dans la suite de ce tutoriel comment les mémoires actuelles font pour stocker des bits : elles peuvent utiliser aussi bien un support magnétique (disques durs), optique (CD-ROM, DVD-ROM, etc), que des transistors (mémoires RAM, FLASH, ROM, etc), etc.

Les mémoires d'un circuit séquentiel permettent de mémoriser des informations, que l'on peut récupérer plus tard. Ainsi, on peut parfaitement consulter une mémoire pour récupérer tout ou partie de son contenu : cette opération est ce qu'on appelle une opération de lecture. Mais évidemment, on peut aussi ranger des informations dans la mémoire ou les modifier : on effectue alors une opération d'écriture. Généralement, les informations lues sont disponibles sur la sortie de la mémoire, tandis que l'information à écrire dans une mémoire est envoyée sur son entrée (le circuit effectuant l'écriture sous certaines conditions qu'on verra plus tard).

De manière générale, il se peut que l'état mémorisé dans la mémoire ne dépende pas que de la donnée envoyée sur l'entrée, mais aussi de l'état antérieur de la mémoire. Pour cela, le circuit contient un circuit combinatoire qui calcule le nouvel état en fonction de l'ancien état et des valeurs des entrées. Un circuit séquentiel peut ainsi être découpé en deux morceaux : des mémoires qui stockent l'état du circuit, et des circuits combinatoires pour mettre à jour l'état du circuit et sa sortie. Cette mise à jour de l'état du circuit dépend de l'entrée mais aussi de l'ancien état. Suivant la méthode utilisée pour déterminer la sortie en fonction de l'état, 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

Les bascules : des mémoires de 1 bit

On vient de voir que la logique séquentielle se base 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 : avec nos portes logiques, on peut créer des circuits capables de mémoriser un bit. Ces circuits sont ce qu'on appelle des bascules, ou flip-flop. 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. Mais le principe qui se cache derrière toutes ces bascules est le même. La solution pour créer une bascule consiste à boucler la sortie d'un circuit sur son entrée, de façon à ce que la sortie rafraîchisse le contenu de l'entrée en permanence. 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 !

Pour créer une bascule, nous allons partir d'un circuit très simple, une vulgaire porte NON. Le circuit doit mémoriser ce qu'on place sur son entrée (celle de la porte NON). Si on boucle la sortie d'une porte NON sur son entrée, cela naïvement ne fonctionnera pas, vu que la sortie sera inversée et correspondra à l'inverse de l'entrée. Il faut donc rajouter une seconde porte NON pour ré-obtenir l'entrée initiale. Par exemple, si on place l'entrée de la première porte NON à zéro, la sortie de celle-ci passera à 1. Ce 1 sera inversé par la seconde porte NON, donnant un zéro. Zéro qui sera alors ré-envoyé sur l'entrée initiale. L'ensemble sera stable : on peut déconnecter l'entrée du premier inverseur, celle-ci sera alors rafraîchie en permanence par l'autre inverseur, avec sa valeur précédente. Le même raisonnement fonctionne si on met un 1 en sortie.

Bascule obtenue en bouclant la sortie d'une porte NON sur son entrée.
Animation du fonctionnement de la bascule précédente.

Les bascules RS

Le circuit précédent met de côté un léger détail : boucler la sortie sur une entrée demande d'ajouter une entrée supplémentaire, ce qui fait que l'on doit faire quelques modifications à un circuit combinatoire pour le transformer en circuit séquentiel: il faut rajouter quelques portes logiques ou en changer. Pour cela, la porte NON du circuit précédent doit être modifiée pour obtenir une bascule. Pour cela, il faudra rajouter une seconde entrée : la porte NON devient soit une porte ET, soit une porte OU (ou une NAND/NOR). Les bascules RS appliquent directement ce principe. Les bascules RS possèdent :

  • une sortie pour récupérer le bit mémorisé; avec éventuellement une autre sortie qui fournit l'inverse de ce bit ;
  • deux entrées qui permettent de le modifier : une entrée permet de le mettre à 0, tandis que l'autre le met à 1.

On classe ces bascules RS suivant ce qu'il faut mettre sur les entrées pour modifier le bit mémorisé, ce qui permet de distinguer les bascules RS à NOR des bascules RS à NAND.

Bascules RS à NOR

Les bascules RS à NOR comportent deux entrées R et S et une sortie Q, sur laquelle on peut lire le bit stocké. 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.

Pour vous rappeler de ceci, 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). Petite remarque : 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 ResetEntrée SetSortie Q
00Bit mémorisé par la bascule
011
100
11Interdit

Les bascules RS à NAND

Les bascules RS à NAND utilisent des portes NAND pour créer une bascule. Cela n'a pas d'avantages, mais c'est une possibilité comme une autre. 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 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.
Entrée ResetEntrée SetSortie Q
00Bit mémorisé par la bascule
011
100
11Interdit

Pour faciliter la compréhension, il est plus judicieux de raisonner avec des entrée /R et /S.

  • si on met un 0 sur l'entrée /R et un 1 sur l'entrée /S, la bascule mémorise un zéro ;
  • si on met un 1 sur l'entrée /R et un 0 sur l'entrée /S, la bascule mémorise un un ;
  • si on met un 1 sur les deux entrées, la sortie Q sera égale à la valeur mémorisée juste avant.

Les bascules RS à entrée Enable

Dans la bascule RS à NAND du dessus, le bit mémorisé change dès que l'on envoie un bit à 1 sur une des deux entrées R et S. On verra plus tard qu'il peut être utile d'autoriser ou d'interdire cette modification dans certains cas. Dans ces conditions, on peut faire en sorte de créer une bascule où l'on pourrait « activer » ou « éteindre » les entrées R et S à volonté. Cette bascule mémoriserait un bit et aurait toujours des entrées R et S. Mais ces entrées ne fonctionneront 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 R et S 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 R et S feront ce qu'il faut et la bascule fonctionnera comme une bascule RS normale. On peut aussi faire la même chose, mais avec une bascule RS à NOR, mais le circuit n'est alors pas tout à fait le même. Dans tous les cas, on obtient alors une bascule RS à entrée Enable. Pour créer un tel circuit, rien de plus simple : nous allons 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 est donc celui-ci :

La bascule D

Les bascules D sont différentes des bascules RS, même si elles ont deux entrées. La différence tient dans ce que l'on doit mettre sur les entrées pour mémoriser un bit. Le bit à mémoriser est envoyé directement sur une des entrées, notée D : la bascule a directement connaissance du bit à mémoriser. L'autre entrée, l'entrée Enable, permet d'indiquer quand la bascule doit mettre son contenu à jour : elle 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.

Interface d'une bascule D.

On peut créer une bascule D avec un simple 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.

On peut aussi construire une bascule D à partir d'une simple bascule RS à entrée Enable : il suffit d'ajouter un circuit qui déduise quoi mettre sur les entrées R et S suivant la valeur sur D. On peut alors remarquer que l'entrée R est toujours égale à l'inverse de D, alors que S est toujours strictement égale à D. On obtient alors le circuit suivant. On peut aussi faire la même chose, mais avec la bascule RS à NAND.

La porte C

Enfin, nous allons voir la dernière porte logique : la porte C. Celle-ci sera utilisée quand nous verrons les circuits et les bus asynchrones.

Porte-C

Celle-ci est une bascule qui comprend deux entrées A et B. 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
000
01Pas de changement
10Pas de changement
111

Cette bascule peut être réalisée d'un grand nombre de manière différentes. La plus simple, basée sur des portes logiques, est celle indiquée dans le schéma suivant. Mais il existe une myriade de manières de construire des portes C avec des transistors.

C-element-from-NANDs

Les registres : des mémoires de plusieurs bits

Registre de 4 Bits. On voit que celui-ci contient 4 entrées (à gauche), et 4 sorties (à droite). On peut aussi remarquer une entrée CLK, qui joue le rôle d'entrée d'autorisation.

On vient de voir comment créer des bascules, des circuits capables de mémoriser un seul bit. Il se trouve que l'on peut assembler des bascules pour créer des circuits capables de mémoriser plusieurs bits : ces circuits sont appelés des registres.

Les registres simples

Les registres les plus simples sont capables de mémoriser un nombre, codé sur une quantité fixe de bits. On peut à tout moment récupérer le nombre mémorisé dans le registre : on dit alors qu'on effectue une lecture. On peut aussi mettre à jour le nombre mémorisé dans le registre, le remplacer par un autre : on dit qu'on effectue une écriture. Ainsi, les registres possèdent :

  • des sorties de lecture, sur lesquelles on peut récupérer/lire le nombre mémorisé ;
  • des entrées d'écriture, sur lesquelles on envoie le nombre à mémoriser (celui qui remplacera le contenu du registre) ;
  • et une entrée Enable, qui a le même rôle que pour une bascule.

Si l'entrée Enable est à 0, le registre n'est pas mis à jour : on peut mettre n'importe quelle valeur sur les entrées, le registre n'en tiendra pas compte et ne remplacera pas son contenu par ce qu'il y a sur l'entrée. La mise à jour, l'écriture dans un registre n'a lieu que si l'entrée Enable est mise à 1. Pour résumer, l'entrée Enable sert donc à indiquer au registre si son contenu doit être mis à jour quand une écriture a lieu. Ainsi, un registre est composé de plusieurs bascules qui sont toutes mises à jour en même temps : pour cela, toutes les entrées Enable sont reliées au même signal, à la même entrée de commande.

Registre à lecture et écriture parallèle.

Les registres à décalage

Certains registres sont toutefois plus complexes. On peut notamment citer les registres à décalage, des registres dont le contenu est décalé d'un cran vers la gauche ou la droite sur commande. On peut les classer selon le caractère de l'entrée, qui peut être parallèle (entrée de plusieurs bits) ou série (entrée d'un seul bit).

Registre à décalage.

Avec les registres à entrée et sortie série, on peut mettre à jour un bit à la fois, de même qu'on ne peut en récupérer qu'un à la fois. Ces registres servent essentiellement à mettre en attente des bits tout en gardant leur ordre : un bit envoyé en entrée ressortira sur la sortie après plusieurs commandes de mise à jour sur l'entrée Enable.

Registre à entrée et sortie série.

Les registres à décalage à entrée série et sortie parallèle sont similaires aux précédents : on peut ajouter un nouveau bit en commandant l'entrée Enable et les anciens bits sont alors décalés d'un cran. Par contre, on peut récupérer (lire) tous les bits en une seule fois. Ils permettent notamment de reconstituer un nombre qui est envoyé bit par bit sur un fil (un bus série).

Registre à entrée série et sortie parallèle.

Enfin, il reste les registres à entrée parallèle et sortie série. Ces registres sont utiles quand on veut transmettre un nombre sur un fil : on peut ainsi envoyer les bits un par un. On initialise les bascules, avant de déconnecter les entrées : les bits se propageront alors de bascule en bascule vers la sortie à chaque front ou signal sur l'entrée Enable.

Registre à entrée parallèle et sortie série.

L'animation suivante illustre le fonctionnement d'un registre à décalage à entrée parallèle et sortie série.

Registre à décalage.

Les registres à décalage à rétroaction linéaire

Les registres à décalage à rétroaction linéaire sont des registres à décalage un peu bidouillés. Avec eux, le bit qui rentre dans le nombre n'est pas fourni sur une entrée, mais est calculé en fonction du contenu du registre par un circuit combinatoire. la fonction qui permet de calculer le bit en sortie est assez spéciale. Dans le cas le plus simple, on dit qu'elle est linéaire, ce qui veut dire que le bit de sortie se calcule à partir en multipliant les bits d'entrée par 0 ou 1, et en additionnant le tout. En clair, ce bit de sortie se calcule par une formule du style : 0∗a3+1∗a2+1∗a1+0∗a0 (on ne garde que le bit de poids faible du résultat). Penchons-nous un peu sur cette addition qui ne garde que le bit de poids faible : je ne sais pas si vous avez remarqué, mais il s'agit ni plus ni moins que d'un calcul de parité paire. En effet, si on additionne N bits, le bit de poids faible vaut zéro pour un nombre pair, et 1 pour un nombre impair. Le circuit combinatoire chargé de calculer le bit de résultat est donc un circuit qui calcule la parité de la somme des bits choisis. Pour cela, il suffit d'effectuer une série de XOR entre tous les bits à additionner.

Registre à décalage à rétroaction de Fibonnaci.

Il existe une variante de ce genre de registre, qui modifie légèrement son fonctionnement. Il s'agit des registres à décalages à rétroaction affine. Avec ces registres, la fonction qui calcule le bit de résultat n'est pas linéaire, mais affine. En clair, ce bit de sortie se calcule par une formule du style : 0∗a3+1∗a2+1∗a1+0∗a0+1. Notez le + 1 à la fin de la formule : c'est la seule différence. Avec ce genre de registre, le bit de résultat est donc calculé en faisant le calcul d'un bit d'imparité de certains (ou de la totalité) des bits du registre. Un tel circuit est donc composé de portes NXOR, comparé à son comparse linéaire, composé à partir de portes XOR. Petite remarque : si je prends un registre à rétroaction linéaire et un registre à rétroaction affine avec les mêmes coefficients sur les mêmes bits, le résultat du premier sera égal à l'inverse de l'autre.

Les registres à décalage à rétroaction précédents sont appelés des registres à rétroaction linéaire de Fibonacci. Il existe un deuxième type de registres à décalage à rétroaction : les registres à décalage à rétroaction de Gallois. Ceux-ci sont un peu l'inverse des registres à décalages à rétroaction de Fibonacci. Dans ces derniers, on prenait plusieurs bits du registre à décalage pour en déduire un seul bit. Avec les registres à décalage à rétroaction de Gallois, c'est l'inverse :

  • on prend le bit qui sort du nombre lors d'un décalage ;
  • et on en déduit plusieurs bits à partir d'un circuit combinatoire ;
  • et on fait rentrer ces bits à divers endroits bien choisis de notre registre à décalage.

Bien sûr, la fonction qui calcule des différents bits à partir du bit d'entrée conserve les mêmes propriétés que celle utilisée pour les registres à décalages à rétroaction linéaire : elle est affine ou linéaire, et se calcule avec uniquement des portes XOR pour les fonctions linéaires, ou NXOR pour les fonctions affines.

Registre à décalage à rétroaction de Galois.

Les compteurs et décompteurs : des circuits qui comptent

Illustration du fonctionnement d'un compteur modulaire binaire de 4 bits, avec un pas de compteur de 1 (le contenu est augmenté de 1 à chaque mise à jour).

Les compteurs/décompteurs sont des circuits électroniques qui mémorisent un nombre qu'ils mettent à jour régulièrement. Cette mise à jour augmente ou diminue le compteur d'une quantité fixe, appelée le pas du compteur. La plupart des compteurs utilisent un pas constant, qui est fixé à la création du compteur, ce qui simplifie la conception du circuit combinatoire. D'autres permettent un pas variable, et ont donc une entrée supplémentaire sur laquelle on peut envoyer le pas du compteur. Suivant la valeur du pas, on fait la différence entre les compteurs d'un côté et les décompteurs de l'autre. Comme leur nom l'indique, les compteurs comptent alors que les décompteurs décomptent. Les compteurs augmentent le contenu du compteur à chaque mise à jour, alors que les décompteurs le diminuent. Dit autrement, le pas d'un compteur est positif, alors que le pas d'un décompteur est négatif. Les compteurs-décompteurs peuvent faire les deux, suivant ce qu'on leur demande.

Suivant le compteur, la représentation du nombre mémorisé change : certains utilisent le binaire traditionnel, d'autres le BCD, d'autre le code Gray, etc. Mais tous les compteurs que nous allons voir seront des compteurs/décompteurs binaires, à savoir que les nombres qu'ils utilisent sont codés sur bits. Au passage, le nombre de bits du compteur est appelé la taille du compteur, par analogie avec les registres. Vu que la taille d'un compteur est limitée, il cesse de compter au-delà d'une valeur maximale. On peut penser que tous les compteurs comptent de 0 à , avec la taille du compteur. C'est le cas pour la majorité des compteurs, mais d'autres compteurs ne comptent pas jusque-là : leur limite est plus basse que . Par exemple, certains compteurs ne comptent que jusqu'à 10, 150, etc. Outre la valeur de la limite du compteur, il est aussi intéressant de se pencher sur ce qui se passe quand le compteur atteint cette limite. Certains restent bloqués sur cette valeur maximale tant qu'on ne les remet pas à zéro "manuellement" : ce sont des compteurs à saturation. D'autres recommencent à compter naturellement à partir de zéro : ce sont des compteurs modulaires.

Le circuit d'un compteur : généralités

Un compteur/décompteur peut être vu comme une sorte de registre (il peuvent stocker un nombre), mais qu'on aurait amélioré de manière à le rendre capable de compter/décompter. Tous les compteurs/décompteurs utilisent un registre pour mémoriser le nombre, ainsi que des circuits combinatoires pour calculer la prochaine valeur du compteur. Ce circuit combinatoire est le plus souvent, mais pas toujours, un circuit capable de réaliser des additions (compteur), des soustractions (décompteurs), voire les deux (compteur-décompteur). Plus rarement, il s'agit de circuits conçus sur mesure, dans le cas où le pas du compteur est fié une bonne fois pour toute.

Comme tout registre, un compteur/décompteur peut être initialisé avec la valeur de notre choix. Pour cela, ils possèdent une entrée d'initialisation sur laquelle on peut placer le nombre initial, couplée à une entrée Reset qui indique si le compteur doit être réinitialisé ou non. Certains compteurs/décompteurs spécifiques n'ont pas d'entrée d'initialisation, mais seulement une entrée de reset, mais il s'agit là d'utilisations assez particulières où le compteur ne peut qu'être réinitialisé à une valeur par défaut. Pour les compteurs/décompteurs, il faut aussi rajouter une entrée qui précise s'il faut compter ou décompter.

Fonctionnement d'un compteur (décompteur), schématique

Dans le cas le plus fréquent, les compteurs parcourent toutes les valeurs possibles que peut prendre leur registre. Ils ne se remettent à zéro qu'une fois qu'ils ont dépassé la valeur maximale. De tels compteurs compteurs sont appelés des compteurs modulo. Mais, comme dit plus haut, certains compteurs ont une valeur maximale qui est plus faible que la valeur maximale du registre. Par exemple, on peut imaginer un compteur qui compte de 0 à 9 : celui-ci est construit à partir d'un registre de 4 bits qui peut donc compter de 0 à 15 ! Ces compteurs sont construits à partir d'un compteur modulo, auquel on rajoute un circuit combinatoire. Ce dernier détecte le dépassement de la valeur maximale et remet à zéro le registre quand c'est nécessaire, via l'entrée de Remise à zéro (entrée Reset).

Compteur modulo N.

L'incrémenteur/décrémenteur

Certains compteurs, aussi appelés incrémenteurs comptent de un en un. Les décompteurs analogues sont appelés des décrementeurs. Nous allons voir comment créer ceux-ci dans ce qui va suivre. Il faut savoir qu'il existe deux méthodes pour créer des incrémenteurs/décrémenteurs. La première donne ce qu'on appelle des incrémenteurs asynchrones, et l'autre des incrémenteurs synchrones. Nous allons commencer par voir comment fabriquer un incrémenteur synchrone, avant de passer aux incrémenteurs asynchrones.

L'incrémenteur/décrémenteur asynchrone

Pour fabriquer un incrémenteur synchrone, la première méthode, il suffit de regarder la séquence des premiers entiers, puis de prendre des paires de colonnes adjacentes :

  • 000 ;
  • 001 ;
  • 010 ;
  • 011 ;
  • 100 ;
  • 101 ;
  • 110 ;
  • 111.

On remarque que le bit sur une colonne change quand le bit de la colonne précédente passe de 1 à 0. Maintenant que l'on sait cela, on peut facilement créer un compteur avec quelques bascules. Pour la colonne la plus à droite (celle des bits de poids faible), on remarque que celle-ci inverse son contenu à chaque cycle d'horloge. Pour cela, on utilise le fait que certaines bascules contiennent une sortie qui fournit l'inverse du bit stocké dans la bascule : il suffit de boucler cette sortie sur l'entrée de la bascule. Pour les autres colonnes, il faut que l'inversion du bit ne se produise que lorsque le bit de la bascule précédente passe de 1 à 0. Le mieux est d'autoriser la mise à jour une fois la transition de la colonne précédente effectuée, c'est à dire quand le bit de la colonne précédente vaut 0. Ainsi, la méthode vue au-dessus reste valable à un changement près : l'entrée Enable de la bascule n'est pas reliée au signal d'horloge, mais à l'inverse de la sortie de la bascule de la colonne précédente.

Compteur asynchrone, sans initialisation

Le circuit précédent a cependant un problème : il ne peut pas être réinitialisé. Pour que cela soit possible, il faut ajouter quelque chose au compteur. Si les bascules du compteur ont une entrée de réinitialisation Reset, qui les force à se remettre à zéro, alors on peut l'utiliser. Il suffit d'ajouter une entrée Reset au compteur, et de la connecter aux entrées Reset des bascules. Ce faisant, le compteur peut être remis à zéro sur simple demande. Mais cela ne permet pas de réinitialiser le compteur à une valeur non-nulle, ce qui demande des modifications supplémentaires. Pour cela, il faut ajouter une entrée au compteur, sur laquelle on présente la valeur d’initialisation. Chaque bit de cette entrée est reliée à un multiplexeur, qui choisir quel bit mémoriser dans la bascule : celui fournit par la mise à jour du compteur, ou celui présenté sur l'entrée d'initialisation. On obtient le circuit décrit dans le schéma qui suit. Quand l'entrée Reset est activée, les multiplexeurs connectent les bascules aux bits sur l'entrée d'initialisation. Dans le cas contraire, le compteur fonctionne normalement, les multiplexeur s connectant l'entrée de chaque bascule à sa sortie.

Compteur asynchrone, avec initialisation.

Un décrémenteur est strictement identique à un incrémenteur auquel on a inversé tous les bits. On peut donc réutiliser le compteur du dessus, à part que les sorties du compteurs sont reliées aux sorties Q des bascules.

L'incrémenteur/décrémenteur synchrone

Passons maintenant à l'incrémenteur synchrone. Pour le fabriquer, on repart de la séquence des premiers entiers. Dans ce qui va suivre, nous allons créer un circuit qui compte de 1 en 1, sans utiliser d'additionneur. Pour comprendre comment créer un tel compteur, nous allons reprendre la séquence d'un compteur, déjà vue dans le premier extrait :

  • 000
  • 001
  • 010
  • 011
  • 100
  • 101
  • 110
  • 111

On peut remarquer quelque chose dans ce tableau : peu importe la colonne, un bit s'inversera au prochain cycle d’horloge quand tous les bits des colonnes précédentes valent 1. Et c'est vrai quelque soit la taille du compteur ou sa valeur ! Ainsi, prenons le cas où le compteur vaut 110111 :

  • les deux premiers 1 sont respectivement précédés par la séquence 10111 et 0111 : vu qu'il y a un zéro dans ces séquences, ils ne s'inverseront pas au cycle suivant ;
  • le bit qui vaut zéro est précédé de la séquence de bit 111 : il s'inversera au cycle suivant ;
  • le troisième 1 en partant de la gauche est précédé de la séquence de bits 11 : il s'inversera aussi ;
  • même raisonnement pour le quatrième 1 en partant de la gauche ;
  • 1 le plus à droite correspond au bit de poids faible, qui s'inverse tous les cycles.

Pour résumer, un bit s'inverse (à la prochaine mise à jour) quand tous les bits des colonnes précédentes valent 1. Pour implanter cela en circuit, on a besoin de deux circuits par bascules : un qui détermine si les bits des colonnes précédentes sont à 1, et un autre qui inverse le bit de la bascule. Le circuit qui détermine si tous les bits précédents sont à 1 est un simple ET entre les bits en question. L'autre circuit prend en entrée le contenu de la bascule et un bit qui indique s'il faut inverser ou pas. En écrivant sa table de vérité, on s’aperçoit qu'il s'agit d'un simple XOR.

Compteur synchrone à incrémenteur

On peut appliquer la même logique pour un décrémenteur. Avec ce circuit, un bit s'inverse lorsque tous les bits précédents sont à zéro. En utilisant le même raisonnement que celui utilisé pour concevoir un incrémenteur, on obtient un circuit presque identique, si ce n'est que les sorties des bascules doivent être inversées avant d'être envoyée à la porte XOR qui suit.

Les compteurs en anneau et de Johnson

En plus des compteurs précédents, on trouve des compteurs plus simples à fabriquer, qui ont donc tendance à être plus rapides que leurs concurrents. Les compteurs en anneau sont des registres à décalage SIPO dont on a bouclé la sortie sur l'entrée. Avec n bits, ce compteur peut compter avec n nombres différents, qui ont tous un seul bit à 1. Petit détail : on peut créer un compteur "normal" en reliant un compteur en anneau avec un encodeur : la sortie de l'encodeur nous donne la valeur normale du compteur.

Évolution du contenu d'un compteur en anneau.

La séquence de ce compteur 4 bits est celle-ci :

  • 1000 ;
  • 0100 ;
  • 0010 ;
  • 0001.
Compteur en anneau de 4 bits

Dans d'autres cas, le bit de sortie est inversé avant d'être bouclé sur l'entrée : ce sont des compteurs de Johnson. Ce compteur a une limite supérieure double de celle d'un compteur en anneau. La séquence d'un tel compteur est :

  • 1000 ;
  • 1100 ;
  • 1110 ;
  • 1111 ;
  • 0111 ;
  • 0011 ;
  • 0001 ;
  • 0000.
Compteur de Johnson de 4 bits

Les mémoires adressables : un avant-gout des prochains chapitres

On a vu qu'il était possible d'assembler plusieurs bascules pour obtenir un registre. Et bien sachez qu'il est possible de faire la même chose avec des registres : on peut en assembler plusieurs pour obtenir un dispositif de mémorisation plus "gros". Dans certains cas, on peut ainsi assembler plusieurs registres pour obtenir un registre plus gros : par exemple, on peut fabriquer un registre de 32 bits à partir de 2 registres de 16 bits, ou de 4 registres de 8 bits. Mais il est aussi possible d'organiser le tout d'une manière plus intelligente, de manière à ce qu'il soit possible de sélectionner le registre qu'on veut consulter ou modifier. On obtient alors ce qu'on appelle une mémoire adressable.

L'adressage mémoire

Pour préciser le registre à sélectionner, chacun d'entre eux se voit attribuer un nombre : l'adresse. On peut comparer une adresse à un numéro de téléphone (ou à une adresse d'appartement) : chacun de vos correspondants a un numéro de téléphone et vous savez que pour appeler telle personne, vous devez composer tel numéro. Les adresses mémoires en sont l'équivalent pour les registres d'une mémoire adressable. Il existe des mémoires qui ne fonctionnent pas sur ce principe, mais passons : ce sera pour la suite. La mémoire contient alors une entrée de plusieurs bits, sur laquelle on peut envoyer l'adresse, une entrée R/W sur laquelle un bit précise si on veut lire ou écrire, une sortie sur laquelle on peut récupérer le registre sélectionné (on dit qu'on lit le registre) et une entrée sur laquelle on peut envoyer un nombre pour modifier le contenu d'un registre (on dit qu'on écrit le registre).

Exemple : on demande à notre mémoire de sélectionner le byte d'adresse 1002 et on récupère son contenu (ici, 17).

Une telle mémoire peut se fabriquer assez simplement : il suffit d'un ou de plusieurs multiplexeurs et de registres. Quand on présente l'adresse sur l'entrée de sélection du multiplexeur, celui-ci va connecter le registre demandé à la sortie (ou à l'entrée).

Intérieur d'une RAM fabriquée avec des registres et des multiplexeurs.

les mémoire mortes et mémoires vives

Les mémoires vues plus haut, dont les registres peuvent être modifiés, sont communément appelées des mémoires vives. On entend parfois parler de mémoires RAM pour de telles mémoires, bien que ce soit un abus de langage (mais laissons cela à plus tard). Cependant, il faut signaler que certaines mémoires de ce type ont des registres qui ne peuvent être modifiés. Ceux-ci ne sont pas fabriqués avec des bascules, mais le sont d'une autre manière. Par exemple, on peut directement connecter les entrées des multiplexeurs directement sur la masse (le 0 Volts) ou la tension d'alimentation, selon que le bit en question 0 ou 1. Dans cette situation, on obtient alors une mémoire ROM, aussi appelée mémoire morte.

De telles mémoires seront utiles dans les chapitres qui vont suivre. La raison en est que tout circuit combinatoire peut être remplacé par une mémoire adressable ! Imaginons que l'on souhaite créer un circuit combinatoire qui pour toute entrée A fournisse la sortie B. Celui-ci est équivalent à une ROM dont la lecture de l'adresse A renvoie B sur la sortie. Cette logique est notamment utilisée dans certains circuits programmables, les FPGA, comme on le verra plus tard.

Cet article est issu de Wikibooks. Le texte est sous licence Creative Commons - Attribution - Partage dans les Mêmes. Des conditions supplémentaires peuvent s'appliquer aux fichiers multimédias.