Mots-clés : Compétition, Aléa, normes de codage
Causes
Dans les circuits numériques, la transmission des signaux et les transitions d'état ont toutes deux certains délais.
- Dans les circuits logiques combinatoires, lorsque les changements de signal d'entrée se propagent par différents chemins jusqu'au même point de circuit au niveau des portes, ils le font plus tôt ou plus tard dans le temps. Cette différence temporelle causée par l'ordre est appelée compétition.
- En raison de l'existence de la compétition, le signal de sortie a besoin d'un certain temps pour atteindre l'état attendu. Pendant la période de transition, des sorties erronées momentanées telles que des impulsions parasites peuvent se produire. Ce phénomène est appelé aléa.
- La compétition ne conduit pas nécessairement à un aléa, mais un aléa implique toujours une compétition. Par exemple, pour la logique donnéeF = A & A', le circuit est représenté sur la figure en bas à gauche.
En raison de la présence du circuit inverseur, le temps de propagation du signalA'vers l'entrée de la porte ET, par rapport au signalA, accusera un retard, ce qui peut faire que le résultat de sortie final de la porte ETFprésente une impulsion parasite. Comme le montre la figure en bas à droite.

En fait, dans les circuits matériels réels, tant que les retards aux entrées d'un circuit de portes sont différents, une compétition et un aléa peuvent se produire.
Par exemple, pour une simple porte ET, si les sources de signal d'entrée ne sont pas nécessairement générées par la même transition de signal, une compétition et un aléa peuvent également se produire en raison du processus matériel et d'autres circuits à retard, comme le montre la figure ci-dessous.

Méthodes de jugement
Méthode algébrique
Dans une expression logique, maintenez une variable fixe, remplacez toutes les autres variables restantes par 0 ou 1. Si l'expression logique finale peut être simplifiée en
Y = A + A'
ou
Y = A · A'
sous la forme, alors on peut déterminer que cette logique présente une compétition et un aléa.
Par exemple, l'expression logiqueY = AB + A'C, sous la condition deB=C=1l'expression peut être simplifiée enY = A + A'. De toute évidence,Ale changement d'état provoquera inévitablement une compétition et un aléa dans le circuit.
Méthode du tableau de Karnaugh
Lorsqu'il y a deux boucles de Karnaugh tangentes, et que le point de tangence n'est pas entouré par d'autres boucles de Karnaugh, une compétition et un aléa peuvent se produire.
Par exemple, la figure en bas à gauche présente une compétition et un aléa, tandis que la figure en bas à droite n'en présente pas.
En fait, un tableau de Karnaugh est essentiellement une analyse de l'expression logique, mais il permet un jugement intuitif.
Par exemple, l'expression logique de la figure en haut à gauche peut être simplifiée enY = A'B' + AC, lorsqueB=0etC=1, cette expression logique peut aussi être exprimée parY = A' + A. Par conséquent, une compétition et un aléa existent certainement.
L'expression logique de la figure en haut à droite peut être simplifiée enY = A'B' + AB, de toute évidenceBqu'elle soit égale à1ou à0, cette expression ne se simplifiera jamais enY = A' + A. Par conséquent, cette logique ne présente ni compétition ni aléa.
Il convient de noter que le tableau de Karnaugh possède une adjacence circulaire. Comme le montre la figure ci-dessous, bien qu'il semble que les deux boucles de Karnaugh ne soient pas tangentes, en fait, m6 et m4 sont également adjacents, de sorte que la logique numérique représentée par le tableau de Karnaugh ci-dessous produira également une compétition et un aléa.

Pour d'autres cas plus complexes, des méthodes telles que « analyse assistée par ordinateur + expérimentation » peuvent être nécessaires.
Méthodes d'élimination
Pour les circuits numériques, il existe principalement 4 méthodes courantes pour éviter la compétition et l'aléa.
1) Ajouter un condensateur de filtrage pour filtrer les impulsions étroites
Cette méthode nécessite de connecter un petit condensateur en parallèle à la sortie pour atténuer l'amplitude de l'impulsion parasite en dessous du seuil du circuit de porte.
Bien que cette méthode soit simple, elle augmente le temps de transition de la tension de sortie et peut facilement dégrader la forme d'onde.
2) Modifier la logique pour ajouter des termes redondants
À l'aide du tableau de Karnaugh, ajoutez une boucle de Karnaugh entre les deux cercles tangents et incluez-la dans l'expression logique.
Comme le montre la figure ci-dessous, pour la logique numériqueY = A'B' + ACajouter le terme redondantB'C, la logique du circuit peut alors s'exprimer parY = A'B' + AC + B'C. À ce stade, le circuit ne présentera plus de compétition ni d'aléa.

3) Utiliser un circuit synchronisé par horloge et recourir à des bascules pour le retard cyclique
Dans les circuits synchrones, les changements de signal se produisent sur le front d'horloge. Pour l'entrée D d'une bascule, tant qu'un parasite n'apparaît pas sur le front montant de l'horloge et ne satisfait pas les temps d'établissement et de maintien des données, il ne nuira pas au système. Par conséquent, l'entrée D d'une bascule D peut être considérée comme insensible aux parasites. En utilisant cette caractéristique, retarder un signal de logique combinatoire de cycles d'horloge sous la commande du front d'horloge peut éliminer la compétition et l'aléa.
Retarder d'un cycle d'horloge réduira l'apparition de compétition et d'aléa avec une certaine probabilité. Les expériences montrent que la durée de retard cyclique la plus sûre est de 3 cycles d'horloge, ce qui peut effectivement réduire l'apparition de compétition et d'aléa.
Bien sûr, en fin de compte, vous devez encore appliquer un retard cyclique raisonnable aux signaux selon les exigences de votre conception.
Pour illustrer que l'application d'un retard d'un cycle aux signaux peut éliminer la compétition et les aléas, nous construisons le modèle de code suivant.
Exemple
(
input clk ,
input rstn ,
input en ,
input din_rvs ,
output reg flag
);
wire condition = din_rvs & en ; //logique combinatoire
always @(posedge clk or negedge !rstn) begin
if (!rstn) begin
flag <= 1'b0 ;
end
else begin
flag <= condition ;
end
end
endmodule
Le banc de test est décrit comme suit :
Exemple
module test ;
reg clk, rstn ;
reg en ;
reg din_rvs ;
wire flag_safe, flag_dgs ;
//génération d'horloge et de rstn
initial begin
rstn = 1'b0 ;
clk = 1'b0 ;
#5 rstn = 1'b1 ;
forever begin
#5 clk = ~clk ;
end
end
initial begin
en = 1'b0 ;
din_rvs = 1'b1 ;
#19 ; en = 1'b1 ;
#1 ; din_rvs = 1'b0 ;
end
competition_hazard u_dgs
(
.clk (clk ),
.rstn (rstn ),
.en (en ),
.din_rvs (din_rvs ),
.flag (flag_dgs ));
initial begin
forever begin
#100;
if ($time >= 1000) $finish ;
end
end
endmodule // test
Les résultats de simulation sont les suivants :
Comme on peut le voir sur la figure, une impulsion parasite apparaît sur le signal condition. Cela est dû au fait que le signal din_rvs et le signal en sont tous deux asynchrones par rapport à l'horloge interne du module, donc leurs retards lorsqu'ils atteignent le circuit de portes interne sont différents, ce qui peut provoquer une compétition et un aléa.
Bien que le résultat final de simulation flag soit toujours 0, ce qui semble être le résultat souhaité, dans un circuit réel cette impulsion parasite est très proche du front d'horloge dans le temps, elle peut donc être capturée par l'horloge et produire un résultat erroné.

Ensuite, nous améliorons le modèle en ajoutant une logique de retard d'un cycle, comme suit :
Exemple
(
input clk ,
input rstn ,
input en ,
input din_rvs ,
output reg flag
);
reg din_rvs_r ;
reg en_r ;
always @(posedge clk or !rstn) begin
if (!rstn) begin
din_rvs_r <= 1'b0 ;
en_r <= 1'b0 ;
end
else begin
din_rvs_r <= din_rvs ;
en_r <= en ;
end
end
wire condition = din_rvs_r & en_r ;
always @(posedge clk or negedge !rstn) begin
if (!rstn) begin
flag <= 1'b0 ;
end
else begin
flag <= condition ;
end
end // always @ (posedge clk or negedge !rstn)
endmodule
Instanciez ce module dans le testbench ci-dessus et les résultats de simulation suivants sont obtenus.
Comme on peut le voir sur la figure, le signal condition n'est plus perturbé par des impulsions parasites, et flag vaut 0 dans les résultats de simulation, comme prévu.
En fait, lorsque le signal d'entrée est très proche du front d'horloge, l'échantillonnage du signal d'entrée par l'horloge reste incertain, mais des impulsions parasites ne se produiront pas. Retarder le signal d'entrée de 2 cycles d'horloge supplémentaires est une meilleure approche et permet une meilleure suppression des compétitions et des aléas.

4) Utiliser un compteur en code Gray
Pour un compteur multi-bits incrémenté, la valeur de comptage présente parfois plusieurs transitions de bits.
Par exemple, lorsque la variable de comptage counter compte de 5 à 6, les nombres binaires correspondants passent de 4'b101 à 4'b110. En raison du retard de chaque bit, le processus de transition de counter peut être :4'b101 -> 4'b111 -> 4'b110. S'il y a la description logique suivante, le signal cout peut présenter une brève impulsion parasite, ce qui est manifestement contraire à la conception.
cout = counter[3:0] == 4'd7 ;
Le compteur en code Gray, en revanche, n'a qu'un seul bit de données qui change entre des valeurs de comptage adjacentes, ce qui lui permet d'éviter efficacement les compétitions et les aléas.
Heureusement, dans les conceptions Verilog, les compteurs sont le plus souvent des conceptions synchrones. Même s'il est possible que plusieurs bits basculent simultanément pendant le comptage, sous l'action de bascules pilotées par l'horloge, tant que les contraintes temporelles entre signaux sont respectées, 100% des compétitions et des aléas peuvent être éliminés.
Résumé
D'une manière générale, l'ajout de condensateurs de filtrage et de redondance logique pour éliminer la concurrence et les aléas ne fait pas partie des considérations de la conception Verilog.
L'utilisation de compteurs en code Gray pour compter est principalement appliquée dans les scénarios d'horloge à haute vitesse afin de réduire le taux de basculement des signaux et ainsi réduire la consommation d'énergie.
L'utilisation de bascules pour retarder les signaux asynchrones d'un certain nombre de cycles d'horloge dans un circuit synchronisé par horloge est une méthode couramment utilisée dans la conception Verilog.
De plus, pour éliminer la concurrence et les aléas, il y a certains points à prendre en compte lors du codage Verilog ; voir la sous-section suivante pour plus de détails.
Normes de codage Verilog
Faire davantage attention aux points suivants lors de la programmation peut également éviter la plupart des problèmes de concurrence et d'aléas.
- 1) Lors de la modélisation de circuits séquentiels, utilisez des affectations non bloquantes.
- 2) Lors de la modélisation de logique combinatoire, utilisez des affectations bloquantes.
- 3) Lors de la modélisation de logique séquentielle et combinatoire dans le même bloc always, utilisez des affectations non bloquantes.
- 4) N'utilisez pas à la fois des affectations bloquantes et non bloquantes dans le même bloc always.
- 5) N'affectez pas la même variable dans plusieurs blocs always.
- 6) Évitez de générer des loquets.
Ci-dessous, nous analysons les précautions ci-dessus une par une.
1) Lors de la modélisation de circuits séquentiels, utilisez des affectations non bloquantes
Précédemment, lors de la discussion de l'affectation non bloquante, il a été indiqué que dans les circuits séquentiels, l'affectation non bloquante peut éliminer les conditions de course.
Par exemple, comme décrit dans le code suivant, étant donné que l'ordre des affectations bloquantes pour a et b ne peut pas être déterminé, des conditions de course peuvent survenir.
always @(posedge clk) begin
a = b ;
b = a ;
end
Lorsque l'affectation non bloquante est utilisée, les opérations d'affectation sont effectuées simultanément, donc aucune condition de course n'est introduite, comme décrit dans le code suivant.
always @(posedge clk) begin
a <= b ;
b <= a ;
end
2) Lors de la modélisation de logique combinatoire, utilisez des affectations bloquantes
Par exemple, si nous voulons implémenter les fonctions logiques combinatoires C = A&B, F = C&D, les instructions d'affectation non bloquantes sont les suivantes.Les deux instructions d'affectation s'exécutent simultanément. Dans F <= C & D, l'ancienne valeur du signal C est utilisée, donc la logique à ce moment est erronée ; la valeur logique de F n'est pas égale à A&B&D.
De plus, le signal C doit avoir une capacité de stockage, mais il n'est pas piloté par une horloge, donc C peut être synthétisé en verrou, ce qui provoque des conditions de course.
always @(*) begin
C <= A & B ;
F <= C & D ;
end
Modifiez le code comme suit. L'opération F = C & D doit être après C = A & B ; à ce moment, la valeur logique de F est égale à A&B&D, ce qui est conforme à la conception.
always @(*) begin
C = A & B ;
F = C & D ;
end
3) Lors de la modélisation de logique séquentielle et combinatoire dans le même bloc always, utilisez des affectations non bloquantes
Bien que les circuits séquentiels puissent impliquer de la logique combinatoire, si l'affectation non bloquante est utilisée pour les opérations d'affectation, des problèmes similaires à ceux mentionnés dans la règle 1 peuvent encore survenir.
Par exemple, pour implémenter la fonction logique d'une porte AND sous commande d'horloge, référez-vous au code ci-dessous.
Exemple
if (!rst_n) begin
q <= 1'b0;
end
else begin
q <= a & b; //Même s'il y a de la logique combinatoire, ne l'écrivez pas comme : q = a & b
end
end
4) N'utilisez pas à la fois des affectations bloquantes et non bloquantes dans le même bloc always
Lorsque la logique combinatoire dans un bloc always contient à la fois des affectations bloquantes et non bloquantes, des résultats inattendus peuvent se produire, comme décrit dans le code suivant.
À ce stade, après que le signal C a réalisé une affectation bloquante, le signal F est affecté par une affectation non bloquante ; le résultat de simulation peut être correct.
Toutefois, si le signal F a d'autres charges, la dernière valeur de F ne peut pas être transmise immédiatement ; le temps de validité des données reste au prochain front de déclenchement. À ce stade, F doit avoir une capacité de stockage et peut être synthétisé en un latch, provoquant des conditions de course.
always @(*) begin
C = A & B ;
F <= C & D ;
end
Comme décrit dans le code ci-dessous, du point de vue de la simulation, le signal C est affecté par une affectation non bloquante et ne devient valide qu'au prochain front de déclenchement. Bien que F = C & D soit une affectation bloquante, le signal C n'est pas une affectation bloquante, donc la logique de F utilise encore l'ancienne valeur de C.
always @(*) begin
C <= A & B ;
F = C & D ;
endAnalysons maintenant ce qui se passerait si un circuit séquentiel contenait à la fois des affectations bloquantes et non bloquantes, comme indiqué dans le code ci-dessous.
Si la réinitialisation est synchrone avec l'horloge, alors le fait que le signal q soit à 0 en raison de la réinitialisation ne devient valide qu'au cycle d'horloge suivant.
Si q devient 0 à cause des signaux a ou b, il est valide dans le cycle d'horloge courant.
Si q a d'autres charges, le timing de q sera particulièrement chaotique, ce qui ne répond manifestement pas aux exigences de conception.
Exemple
if (!rst_n) begin //On suppose que reset est synchrone avec l'horloge
q <= 1'b0;
end
else begin
q = a & b;
end
end
Il convient de noter que de nombreux compilateurs prennent en charge ce style d'écriture, et que l'analyse ci-dessus est basée sur la simulation. En pratique, si des affectations bloquantes et non bloquantes sont mélangées, le timing du circuit synthétisé sera désordonné, ce qui n'est pas propice à l'analyse et au débogage.
5) Ne pas assigner la même variable dans plusieurs blocs always
Contrairement au langage C, Verilog ne permet pas d'assigner à la même variable dans plusieurs blocs always. Dans ce cas, le signal a plusieurs pilotes, ce qui est interdit. Bien sûr, les instructions assign ne sont pas non plus autorisées à assigner plusieurs fois à la même variable pour la connexion. Du point de vue du signal, avec plusieurs pilotes, la même variable de signal se voit assigner différentes valeurs plusieurs fois en une très courte période, ce qui peut provoquer des conditions de course.
D'un point de vue syntaxique, de nombreux compilateurs signaleront également une erreur lorsqu'ils détectent plusieurs pilotes.
6) Éviter de générer des latches
Pour une analyse détaillée, voir le chapitre suivant :《Éviter le latch》。
Téléchargement du code source
Téléchargement