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

    module competition_hazard
        (
          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

    `timescale 1ns/1ns

    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

    module clap_delay
        (
          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

    always @(posedge clk or negedge rst_n)
        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 ;
    end

    Analysons 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

    always @(posedge clk or negedge rst_n)
        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