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". 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.
Les compteurs/décompteurs : généralités.
Les compteurs et décompteurs sont très variés et ils se distinguent sur un grand nombre de points.
La plupart des compteurs utilisent un pas constant, qui est fixé à la création du compteur, ce qui simplifie la conception du circuit. D'autres permettent un pas variable, et ont donc une entrée supplémentaire sur laquelle on peut envoyer le pas du compteur.
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 formula_1 bits. Au passage, le nombre de bits formula_1 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. La plupart des compteurs comptent de 0 à formula_3, avec formula_1 la taille du compteur. Ils sont appelés des compteurs "modulo". D'autres compteurs ne comptent pas jusque-là : leur limite est plus basse que formula_3. 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".
L'interface d'un compteur/décompteur.
Compteurs et décompteurs sont presque tous des circuits synchrones, à savoir cadencés par une horloge. Du moins, c'est le cas des compteurs/décompteurs que nous allons voir dans ce chapitre, par souci de simplicité. Un compteur/décompteur est donc relié à une entrée d'horloge.
De plus, certains compteurs ont une entrée Enable qui active/désactive le comptage. Le compteur s'incrémente/décrémente seulement si l'entrée Enable est à 1, mais ne fait rien si elle est à 0. Les compteurs les plus simples sont incrémentés/décrémentés à chaque cycle d'horloge et n'ont pas d'entrée Enable, mais les compteurs plus complexes en ont une. Ce faisant, le compteur/décompteur est incrémenté seulement si deux conditions sont réunies : un front montant sur le signal d'horloge, et une entrée Enable à 1. Dans les schémas qui vont suivre, nous ferons mention soit au signal d'horloge, soit à l'entrée Enable. Si on fait référence à l'entrée Enable, il est sous-entendu que les bascules du registre sont cadencées par une horloge, qui leur dit quand s'actualiser.
En outre, les compteurs ont souvent une entrée Reset qui permet de les remettre à zéro.
Sur les compteurs/décompteurs, il y a une entrée qui décide s'il faut compter ou décompter. Typiquement, elle est à 1 s'il faut compter et 0 s'il faut décompter.
Un compteur/décompteur peut parfois ê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.
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 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).
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. La différence entre les deux est la suivante. Les bits d'un compteur asynchrones sont mis à jour dans un certain ordre, typiquement en commençant par celui de poids faible, pour remonter vers les bits de poids fort. A l'inverse, les compteurs synchrones mettent à jour tous les bits du compteur en même temps, ou presque. Nous allons commencer par voir comment fabriquer un incrémenteur asynchrone, avant de passer aux incrémenteurs synchrones.
L'incrémenteur/décrémenteur asynchrone.
Pour fabriquer un incrémenteur asynchrone, la première méthode, il suffit de regarder la séquence des premiers entiers, puis de prendre des paires de colonnes adjacentes :
On remarque que le bit sur une colonne change quand le bit de la colonne précédente passe de 1 à 0, en clair, lorsqu'on a un front descendant sur la colonne précédente. Pour la colonne la plus à droite (celle des bits de poids faible), on remarque que celle-ci inverse son contenu à chaque cycle d'horloge. Maintenant que l'on sait cela, on peut facilement créer un compteur avec quelques bascules. On peut les créer avec des bascules T, D, JK, et bien d'autres. Nous allons d'abord voir ceux fabriqués avec des bascules T, plus simples, puis ceux fabriqués avec des bascules D.
Les incrémenteurs/décrémenteurs à base de bascules T.
Pour rappel, les bascules T inversent leur contenu à chaque cycle d'horloge. Par simplicité, nous allons utiliser des bascules avec une sortie qui fournit l'inverse du bit stocké.
La première colonne inverse son contenu à chaque cycle, elle correspond donc à une bascule T simplifiée reliée directement à l'horloge. Les autres colonnes s'inversent quand survient un front descendant sur la colonne précédente. Le circuit qui correspond est illustré ci-dessous, avec des bascules T activées sur front descendant. Attention, cependant : la bascule la plus à gauche stocke le bit de poids FAIBLE, pas celui de poids fort. En fait, le nombre binaire est ici stocké de gauche à droite et non de droite à gauche ! Cela sera pareil dans tous les schémas qui suivront.
Il est aussi possible d'utiliser des bascules T actives sur front montant. En effet, notons qu'un front descendant sur la sortie Q correspond à un front montant sur la sortie /Q. En clair, il suffit de relier la sortie /Q d'une colonne sur l'entrée d'horloge de la suivante. Le circuit est donc le suivant :
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.
Il est possible de fusionner un incrémenteur et un décrémenteur asynchrone, de manière à créer un circuit qui puisse soit incrémenter, soit décrémenter le compteur. Le choix de l'opération est réalisé par un bit d'entrée, qui vaut 1 pour une incrémentation et 0 pour une décrémentation. L'idée est que les entrées des bascules sont combinées avec ce bit, pour donner les entrées compatibles avec l'opération demandée.
Les circuits précédents ont cependant un problème : ils ne peuvent pas être réinitialisés. Pour que cela soit possible, il faut que les bascules du compteur aient une entrée de réinitialisation "Reset", qui les force à se remettre à zéro. 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.
Cette réinitialisation est utile pour créer des compteurs modulo. Il suffit de créer un circuit qui vérifie si la valeur maximale du comparateur est atteinte et qui met à 1 l'entrée "Reset" si c'est le cas. Un exemple est illustré ci-dessous.
L'incrémenteur asynchrone à base de bascules D.
Il est aussi possible d'utiliser des bascules D pour créer un compteur comme les deux précédents. En effet, une bascule T simplifiée est identique à une bascule D dont on boucle la sortie /Q sur l'entrée de données.
Cette implémentation peut être modifiée pour facilement réinitialiser le compteur à une valeur non-nulle. 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 multiplexeurs connectant l'entrée de chaque bascule à sa sortie.
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 :
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 quel que soit la taille du compteur ou sa valeur ! Ainsi, prenons le cas où le compteur vaut 110111 :
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 d'ajouter un circuit qui détermine si les bits des colonnes précédentes sont à 1, qui n'est autre qu'un simple ET entre les bits en question. On pourrait croire que chaque bascule est précédée par une porte ET à plusieurs entrée qui fait un ET avec toutes les colonnes précédente. Mais en réalité, il y a moyen d'utiliser des portes plus simples, avec une banale porte ET à deux entrées pour chaque bascule. Le résultat est indiqué ci-dessous.
Voici le même circuit, mais réalisé avec des bascules JK.
L’implémentation de circuit avec des bascules D est légèrement plus complexe. Il faut ajouter un circuit qui 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.
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 basés sur des registres à décalage.
Il existe des compteurs qui sont basés sur un registre à décalage modifiés. Ils regroupent les compteurs en anneau, les compteurs de Johnson et les registres à décalage à rétroaction linéaire. Les plus connus sont basés sur des registres à décalage avec une entrée série, à savoir qu'ils ont une entrée d'un seul bit. Ils peuvent donc insérer un bit dans le registre, à chaque cycle d’horloge. Le bit inséré est calculé soit en fonction du contenu du registre, soit en fonction du bit sortant du registre.
Les compteurs en anneau.
Les compteurs en anneau sont des registres à décalage dont le bit sortant est renvoyé sur l'entrée. Naturellement, il existe une version qui peut être remise à zéro.
Avec n bits, ce registre peut prendre N valeurs différentes, qui ont toutes un seul bit à 1. La séquence d'un compteur en anneau de 4 bits est illustrée ci-dessous. Elle donne ceci : 1000, 0100, 0010, 0001.
Les compteurs de Johnson.
Sur les compteurs de Johnson, le bit sortant est inversé avant d'être bouclé sur l'entrée. Ce compteur parcours deux fois plus de valeurs différentes, comparé à un compteur en anneau. La séquence d'un tel compteur est :
Les registres à décalage à rétroaction linéaire.
Sur les registres à décalage à rétroaction linéaire de Fibonacci, le bit d'entrée est calculé en fonction du contenu du registre par un circuit combinatoire. La fonction mathématique qui calcule le bit en entrée 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 : :
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.
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 :
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 de Gallois sont un peu l'inverse des registres à décalages à rétroaction de Fibonacci vus juste avant. 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 sortant et on en déduit plusieurs bits à partir d'un circuit combinatoire, qui sont chacun insérés dans le registre à décalage à un endroit bien précis. 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.