Logique séquentielle
2023-2024
Cette partie du cours est consacrée à la logique séquentielle synchrone. Nous allons aborder deux aspects:
Rappel
L’élément de base de la logique séquentielle synchrone permet d’échantillonner une donnée à un instant précis (un front d’horloge) et de mémoriser sa valeur jusqu’au front suivant en ignorant (filtrant) tous les changements d’état intermédiaires.
La logique séquentielle synchrone permet de garantir le fonctionnement correct de la logique combinatoire, on peut:
Ce fonctionnement est garanti, tant que la période d’horloge est compatible avec le temps de propagation dans la logique combinatoire.
Dans la suite, nous allons voir les principes permettant de concevoir un tel élément mémorisant, actif sur fronts: la bascule D (ou le registre, s’il y en a plusieurs) à partir des autres portes logiques.
L’élément mémorisant sur front
clk | D | Q |
---|---|---|
\uparrow | 0 | 0 |
\uparrow | 1 | 1 |
\downarrow | x | Q ^{-1} |
0 | x | Q ^{-1} |
1 | x | Q ^{-1} |
Ce qu’il faut retenir ici c’est que nous avons un élément mémorisant actif sur front. À chaque front montant (passage de 0 à 1) du signal d’horloge, on capture la valeur de l’entrée que l’on mémorise jusqu’au prochain front.
C’est l’élément de base de la logique synchrone que nous utiliserons dans la suite pour construire des applications. C’est le même type d’éléments, regroupé en registres, qui sont utilisés pour construire des éléments de calcul synchrones et ceci jusqu’aux processeurs.
Dans la suite de ce chapitre, nous allons montrer comment pourrait être construite une bascule D avec les portes logiques précédemment vues.
Notez que ceci n’est donné qu’à titre indicatif. Les bascules D sont conçues au niveau transistor pour garantir leur comportement. Ce qui est montré dans ce chapitre n’est là que pour présenter les concepts.
Conserver et régénérer l’état électrique.
Deux inverseurs tête-bêche permettent de conserver et régénérer le
signal électrique Q. Tant ce système
est alimenté, le signal nQ sera
l’inverse de Q grâce à l’inverseur
inv1
, et l’inverseur inv0
nous régénérera un
signal correct sur la sortie Q.
La fonction de transfert de la boucle composée des deux inverseurs est la fonction identité. Nous aurions pu utiliser un amplificateur non inverseur (un buffer), mais comme vu précédemment, en technologie CMOS, la porte inverseur est la porte la plus simple à construire (et un buffer serait composé de deux inverseurs, ce qui revient au même).
Cette structure est appelée un point mémoire statique (statique veut dire que tant que l’alimentation est présente, l’état est conservé). Cette structure réalise bien la fonction de mémorisation, mais telle quelle, il manque de quoi modifier la valeur mémorisée (au-delà de ce qui sera présenté dans ce cours).
Aussi, il nous manque la possibilité de contrôler l’instant d’activation de cette mémoire (le front de l’horloge).
Considérons la structure suivante:
ena | D | Q | état |
---|---|---|---|
1 | 0 | 0 | transparent (Tr) |
1 | 1 | 1 | transparent (Tr) |
0 | x | Q ^{-1} | mémorisant (Cl) |
Avec un multiplexeur (MUX) rebouclé nous arrivons à construire un élément mémorisant. Quand la boucle est fermée, la fonction de transfert est aussi l’identité, ce qui permet de régénérer la valeur présente en sortie (un peu comme avec le point mémoire précédent).
Cette structure porte le nom de verrou (Latch en anglais, ou élément mémorisant actif sur niveau).
Maintenant, avec cette entrée de contrôle, nous avons deux états possibles:
Nous avons maintenant un élément mémorisant que l’on peut contrôler, mais on ne maîtrise pas précisément l’instant ou la capture de la valeur de l’entrée est faite. En effet, tant que le Latch est transparent, les changements sur l’entrée sont reproduits sur la sortie.
Dans l’étape suivante, nous allons voir comment utiliser ce composant pour obtenir le comportement désiré pour notre bascule D.
On chaîne de verrous (Latches) en reliant leur signal
d’activation à un signal d’horloge périodique (clk
dans le
schéma) comme suit:
Quand le premier Latch est passant, le second est mémorisant et visse versa. Dans le chronogramme, en vert les périodes où le Latch est passant et en rouge celle durant lesquelles il est bloqué.
Quand clk
est à l’état bas: Le premier
Latch est transparent et la valeur de l’entrée D est copiée sur le signal interne (Int. dans le schéma), mais elle n’apparaît
pas sur la sortie Q qui garde son état
précédent, car le second Latch est dans l’état
mémorisant.
Quand clk
est à l’état haut: Le second
Latch devient transparent et la valeur du signal interne est
copiée sur la sortie. Par contre, comme le premier Latch est
bloqué, les changements de valeur du signal d’entrée ne sont pas
propagés vers la sortie.
L’ensemble se comporte donc comme si on avait capturé la valeur du
signal d’entrée au moment où le signal clk
passe de 0 à 1
(au front).
On a construit un élément mémorisant sur front!
Notez que les vraies Latches ne sont pas construits avec des multiplexeurs, le concept de la structure maître/esclave est réaliste. Aussi, l’inversion du signal d’horloge est un peu plus complexe à mettre en place, du fait du retard qui serait ajouté sur l’un des chemins par un seul inverseur.
Finalement, vu l’importance des bascules D, il existe des structures optimisées au niveau transistors dont le comportement est validé par les concepteurs. On ne peut pas garantir ce comportement en assemblant des Latches sans descendre au niveau électrique!
Pour qu’une bascule échantillonne correctement la valeur de son entrée, il faut que celle-ci soit stable autour du front d’horloge. Ceci permet de capturer sa valeur puis de la propager vers la sortie de la bascule.
Les concepteurs des bascules D vont définir un intervalle temporel de stabilité, en dehors duquel le comportement de la bascule n’est plus garanti.
Autour du front, nous devons respecter:
De plus, t_{co} (clock-to-output) indiquera le délai entre le front de l’horloge et le moment où la donnée est valide à la sortie de la bascule. On peut voir ce temps comme le temps de propagation de la bascule.
Par construction, t_{co} est plus grand que le temps de maintien t_h, ce qui permet de directement connecter la sortie de la bascule sur l’entrée d’une autre bascule (pour construire des registres à décalage).
T_{clk} \geq t_{co} + t_{crit} + t_{su} \;\text{ou}\; F_{clk} \leq \frac{1}{t_{co} + t_{crit} + t_{su}}
On retrouve ici le schéma qui met en relation la période de l’horloge et le temps de propagation dans la logique combinatoire.
Avec t_{crit} le temps de propagation le plus long dans le bloc de logique combinatoire (le pire cas).
Ces différents temps peuvent être retrouvés à partir des données associées aux portes logiques utilisées et des modèles plus ou moins précis (allant du modèle simple additif à la simulation électrique).
module dff( input logic clk,
input logic d,
output logic q );
always_ff @(posedge clk)
begin
q <= d;end
endmodule
module reg4 ( input logic clk,
input logic [3:0] d,
output logic [3:0] q );
always_ff @(posedge clk)
begin
q <= d;end
endmodule
La logique séquentielle synchrone est décrite dans des processus en
utilisant le mot-clé always_ff
. Comme l’action décrite dans
le processus est exécutée à chaque front montant de l’horloge
on doit le préciser en ajoutant @(posedge clk)
.
Note @(...)
est appelé liste de
sensibilité et permet de préciser l’évènement pour lequel le
comportement décrit dans le processus doit être exécuté.
Notez aussi que l’opérateur d’affectation est différent
(<=
). Il est appelé opérateur d’affectation différée et
il est obligatoire quand on décrit de la logique
séquentielle synchrone. Il permet de décrire des changements d’états qui
ne sont effectifs qu’après le font montant de l’horloge.
<=
)module shift_reg ( input logic clk,
input logic i,
output logic q );
logic q0, q1, q2, q3;
always_ff @(posedge clk)
begin
q0 <= i;
q1 <= q0;
q2 <= q1;
q3 <= q2;end
assign q = q3;
endmodule
Ceci est un exemple de registre à décalage de 4 bits (4 bascules D). Notez que du fait de l’utilisation de l’opérateur d’affectation différée, les signaux ne changent qu’après le front d’horloge. Conceptuellement, les quatre affectations décrivent des actions qui sont faites en parallèle.
Donc ce bloc d’affectations est équivalent à:
begin
q3 <= q2;
q2 <= q1;
q1 <= q0;
q0 <= i;end
Pour décrire de façon compacte le registre à décalage, nous aurions aussi pu exploiter l’opérateur de concaténation comme le montre l’exemple suivant.
module shift_reg ( input logic clk,
input logic i,
output logic q );
logic [3:0] q_r;
always_ff @(posedge clk)
begin
2:0],i};
q_r <= {q_r[end
assign q = q_r[3];
endmodule
Comme la sortie q
est connectée au dernier registre,
nous pouvons aussi écrire:
module shift_reg ( input logic clk,
input logic i,
output logic q );
// La 4e bascule est q
logic [2:0] q_r;
always_ff @(posedge clk)
begin
2:0],i};
{q,q_r} <= {q_r[end
endmodule
Le nombre de bascules correspond au nombre de signaux modifiés par les affectations différées.
Travail à faire tester ces différentes versions sur DigitalJS.
module dffe( input logic clk,
input logic ena,
input logic d,
output logic q ) ;
always_ff @(posedge clk)
begin
if(ena)
q <= d;end
endmodule
Ce code décrit le comportement d’une bascule D avec une entrée d’activation. La sortie n’est mise à jour au front d’horloge que si l’entrée d’activation est à l’état haut.
Dans le schéma, nous l’avons représenté par une bascule précédée un multiplexeur. Ce dernier, si l’entrée d’activation est à l’état bas, redirige vers l’entrée de la bascule sa sortie, ce qui fait qu’elle n’évolue pas. Sinon, il dirige l’entrée vers la bascule pour capturer une nouvelle donnée.
Ce code est donc équivalent au code suivant:
module dffe( input logic clk,
input logic ena,
input logic d,
output logic q);
logic q_next;
assign q_next = ena? d : q;
always_ff @(posedge clk)
begin
q <= q_next;end
endmodule
ou en utilisant le processus always_comb
pour la logique
combinatoire:
module dffe( input logic clk,
input logic ena,
input logic d,
output logic q);
logic q_next;
always_comb
begin
if(ena)
q_next = d;else
q_next = q;end
always_ff @(posedge clk)
begin
q <= q_next;end
endmodule
Notez dans cette dernière version, que le bloc
if...else
doit être complet. En effet, pour de la logique
combinatoire, tous les cas doivent êtres explicites.
Alors que pour de la logique séquentielle, les cas manquants veulent seulement dire que le signal ne change pas, il garde sa valeur, on mémorise. Ce qui veut dire que pour la logique séquentielle les cas implicites sont autorisés.
always_ff @(posedge clk)
begin
if(ena)
q <= d;// else
// q <= q; // implicite
end
module cpt ( input logic clk,
output logic [3:0] q);
always_ff @(posedge clk)
begin
1;
q <= q + end
endmodule
Le comportement de ce module est assez simple. Quelle que soit la valeur du signal q, au front d’horloge sa valeur est incrémentée. Aussi, comme le signal est sur 4 bits, s’il atteint la valeur 15, il passera automatiquement à 0 au cycle suivant. En suite, il reproduira, en permanence, la séquence des valeurs allant de 0 à 15. D’où le nom, compteur modulo 16.
Nous pouvons forcer le passage à 0 avant d’atteindre la valeur 15, pour avoir une séquence plus courte (modulo une valeur différente).
Par exemple, le code suivant représente un compteur modulo 10.
module cpt ( input logic clk,
output logic [3:0] q);
always_ff @(posedge clk)
begin
1;
q <= q + if(q == 9) q <= 0;
end
endmodule
Du fait de l’utilisation des affectations différées, le test
q == 9
se fait sur la valeur actuelle du registre
q
. Et nous aurons bien la séquence des valeurs allant de 0
à 9.
En fait ce code est équivalent à:
module cpt ( input logic clk,
output logic [3:0] q);
always_ff @(posedge clk)
begin
if(q == 9)
0;
q <= else
1;
q <= q + end
endmodule
Problème avec cette construction, nous avons la garantie de parcourir toutes les valeurs prévues, par contre, au démarrage (à la mise sous tension) du système, on ne peut pas prédire l’état initial des registres.
Nous somme obligé de prévoir un mécanisme pour forcer l’état des registres. Ce mécanisme d’initialisation, est appelé Reset (indépendamment de la valeur initiale souhaitée). Dans la section suivante nous expliquons comment le décrire en SystemVerilog.
module reg4( input logic clk,
input logic rst,
input logic [3:0] d,
output logic [3:0] q);
always_ff @(posedge clk)
if(rst)
begin
0;
q <= end
else
begin
q <= d;end
endmodule
Si le signal rst
est à l’état haut, la sortie des portes
ET est forcée à 0. Au front d’horloge suivant, la sortie du registre
passera forcément à 0. Cet état sera conservé tant que l’entrée
rst
reste à 1.
Ce mécanisme de remise à 0 est appelé reset synchrone, car son effet n’est visible qu’après un front d’horloge. Il est simple à mettre en œuvre puisque il ne nécessite que des portes logiques et bascules D standards.
Notez que dans le code nous avons séparé le comportement en deux parties, l’une correspondant à la remise à 0 et l’autre correspondant au fonctionnement normal. Il est important de conserver ce style d’écriture pour permettre aux outils d’identifier la partie correspondant au reset.
Remise à zéro asynchrone
module dffa_1( input logic clk,
input logic rst,
input logic d,
output logic q);
always_ff @(posedge clk or posedge rst)
if(rst)
begin
0;
q <= end
else
begin
q <= d;end
endmodule
module dffa_0( input logic clk,
input logic nrst,
input logic d,
output logic q);
always_ff @(posedge clk or negedge nrst)
if(~nrst)
begin
0;
q <= end
else
begin
q <= d;end
endmodule
Un signal de remise à zéro asynchrone agit immédiatement sur la
sortie du registre. Ce comportement est décrit en SystemVerilog et
ajoutant le signal de reset à la liste de sensibilité du processus
always_ff
. Plus précisément, il faut ajouter l’évènement
qui rend le reset actif:
La syntaxe de SystemVerilog n’a pas prévu de mot-clé dédié pour le reset et a réutilisé le mot-clé prévu pour l’horloge. Malgré cela, il ne faut pas l’interpréter comme une seconde horloge, les bascules n’ont qu’une seule horloge!
Aussi, la séparation du comportement en deux parties distincte est obligatoire.
La mise en œuvre de ce type de reset électroniquement se fait forcément au niveau transistor dans la bascule D qui sera différente d’une bascule D standard. Elle est souvent plus compacte que l’ajout d’une porte logique supplémentaire. On le représente juste par une entrée supplémentaire de la bascule.
Nous voulons détecter le passage de 0 à 1 d’un signal
key
venant du monde extérieur dans un système synchrone à
l’horloge clk
.
top
le signal en sortie indique ce changement d’état en
passant à 1
durant exactement 1 cycle
d’horloge à chaque fois que l’entrée passe de 0 à 1.
Nous recevons une séquence de données codées sur 8 bits.
Pour tester votre solution en SystemVerilog sur DigitalJS vous disposez d’un environnement de test:
filter
.data_in
codées sur 1 bit.en_in
indique si le bit data_in
est une donnée
valide.data_out
de 4 bits, accompagnée d’un signal
en_out
indiquant si la donnée
data_out
est valide.Réalisez un chronogramme montrant un exemple de transmission.
Déterminez le ou les signaux supplémentaires nécessaires au fonctionnement du dispositif.
Déterminez les éléments logiques nécessaires à la réalisation du dispositif.
Faites un schéma.
Écrivez le code SystemVerilog équivalent.
Pour tester votre solution en SystemVerilog sur DigitalJS vous disposez d’un environnement de test:
serial2parallel
.© Copyright 2022-2024, Télécom Paris. | |
Le contenu de cette page est mis à disposition selon les termes de la licence Creative Commons Attribution - Partage dans les Mêmes Conditions 4.0 International. |