Mots-clés : division par nombre pair, division par nombre impair, division par demi-entier, division fractionnaire

Lorsqu'on débute en Verilog, de nombreux modules sont composés de compteurs et de diviseurs, comme la modulation de largeur d'impulsion PWM, les compteurs de fréquence, etc. La logique de division est souvent implémentée au moyen d'une logique de comptage. Cette section fournit principalement un bref résumé de la division par nombre pair, de la division par nombre impair, de la division par demi-entier et de la division fractionnaire.

Division par nombre pair

En reliant la sortie inverseuse d'une bascule à son entrée, on peut construire un simple circuit de division par 2.

Une mise en cascade basée sur ce principe permet de former des circuits de division par 4 et par 8.

L'implémentation du circuit est illustrée dans la figure ci-dessous. Lors de sa description en Verilog, une simple logique d'inversion suffit.

Si le facteur de division pair est trop grand, il est nécessaire de compter de manière cyclique le facteur de division N. Lorsque la période de comptage atteint la valeur médiane N/2 du facteur de division, l'horloge est basculée, ce qui garantit un rapport cyclique de 50 % pour l'horloge divisée. Comme il s'agit d'une division paire, le comptage peut également être effectué de manière cyclique jusqu'à la valeur médiane N/2.

Un exemple de description Verilog pour une division paire est le suivant.

Exemple

module even_divisor
  # (parameter DIV_CLK = 10 )
    (
    input               rstn ,
    input               clk,
    output              clk_div2,
    output              clk_div4,
    output              clk_div10
    );

   //diviser par 2
   reg                  clk_div2_r ;
   always @(posedge clk or negedge rstn) begin
      if (!rstn) begin
         clk_div2_r     <= 'b0 ;
      end
      else begin
         clk_div2_r     <= ~clk_div2_r ;
      end
   end
   assign       clk_div2 = clk_div2_r ;

   //division par 4
   reg                  clk_div4_r ;
   always @(posedge clk_div2 or negedge rstn) begin
      if (!rstn) begin
         clk_div4_r     <= 'b0 ;
      end
      else begin
         clk_div4_r     <= ~clk_div4_r ;
      end
   end
   assign clk_div4      = clk_div4_r ;

   //comptage N/2
   reg [3:0]            cnt ;
   always @(posedge clk or negedge rstn) begin
      if (!rstn) begin
         cnt    <= 'b0 ;
      end
      else if (cnt == (DIV_CLK/2)-1) begin
         cnt    <= 'b0 ;
      end
      else begin
         cnt    <= cnt + 1'b1 ;
      end
   end

   //horloge de sortie
   reg                  clk_div10_r ;
   always @(posedge clk or negedge rstn) begin
      if (!rstn) begin
         clk_div10_r <= 1'b0 ;
      end
      else if (cnt == (DIV_CLK/2)-1 ) begin
         clk_div10_r <= ~clk_div10_r ;
      end
   end
   assign clk_div10 = clk_div10_r ;
endmodule

Dans le banc de test, il suffit de fournir l'horloge de stimulation et les autres signaux, il ne sera donc pas listé ici.

Les résultats de la simulation sont les suivants.

Division impaire

Si la division impaire ne nécessite pas un rapport cyclique de 50 %, elle peut être réalisée de la même manière que la division paire. C'est-à-dire que le compteur compte de façon cyclique le facteur de division N, puis un certain rapport cyclique est sélectionné en fonction de la valeur de comptage pour délivrer l'horloge divisée.

Si les niveaux haut et bas de l'horloge de sortie à division impaire ne diffèrent que d'un cycle, la caractéristique à double front de l'horloge source peut être utilisée, combinée à une « opération ET » ou à une « opération OU », pour ajuster le rapport cyclique de l'horloge divisée à 50 %.

Opération OU pour ajuster le rapport cyclique

Le diagramme temporel pour générer une horloge divisée par 3 avec un rapport cyclique de 50 % à l'aide de l'« opération OU » est présenté ci-dessous.

Utiliser le front montant de l'horloge source pour générer une horloge divisée par 3 avec un niveau haut de 1 cycle et un niveau bas de 2 cycles.

Utiliser le front descendant de l'horloge source pour générer une horloge divisée par 3 avec un niveau haut de 1 cycle et un niveau bas de 2 cycles.

Les deux horloges divisées par 3 doivent être générées avec la même valeur de compteur mais sur des fronts différents, avec une différence de phase d'un demi-cycle d'horloge. Ensuite, en effectuant une « opération OU » sur les deux horloges, on peut obtenir une horloge divisée par 3 avec un rapport cyclique de 50 %.

De même, pour la division par 9, il est nécessaire de générer des horloges divisées par 9 avec 4 cycles à niveau haut et 5 cycles à niveau bas sur les fronts montants et descendants, puis d'effectuer une « opération OU » sur les deux horloges. La description Verilog est la suivante.

Exemple

module odo_div_or
  #(parameter DIV_CLK = 9)
   (
    input               rstn ,
    input               clk,
    output              clk_div9
    );

   //compteur
   reg [3:0]            cnt ;
   always @(posedge clk or negedge rstn) begin
      if (!rstn) begin
         cnt    <= 'b0 ;
      end
      else if (cnt == DIV_CLK-1) begin
         cnt    <= 'b0 ;
      end
      else begin
         cnt    <= cnt + 1'b1 ;
      end
   end

   //générer un diviseur par 9 sur front montant
   reg                  clkp_div9_r ;
   always @(posedge clk or negedge rstn) begin
      if (!rstn) begin
         clkp_div9_r <= 1'b0 ;
      end
      else if (cnt == (DIV_CLK>>1)-1 ) begin //compter 4-8 comme niveau bas
        clkp_div9_r <= 0 ;
      end
      else if (cnt == DIV_CLK-1) begin //compter 0-3 comme niveau haut
        clkp_div9_r <= 1 ;
      end
   end
 
   //générer un diviseur par 9 sur front descendant
   reg                  clkn_div9_r ;
   always @(negedge clk or negedge rstn) begin
      if (!rstn) begin
         clkn_div9_r <= 1'b0 ;
      end
      else if (cnt == (DIV_CLK>>1)-1 ) begin
        clkn_div9_r <= 0 ;
      end
      else if (cnt == DIV_CLK-1) begin
        clkn_div9_r <= 1 ;
      end
   end

   //opération OU, généralement à l'aide d'une bibliothèque de cellules logiques de base
   // or (clk_div9, clkp_div9_r, clkn_div9_r) ;
   assign clk_div9 = clkp_div9_r | clkn_div9_r ;

endmodule

Les résultats de la simulation sont les suivants.

Opération AND pour ajuster le rapport cyclique

Le diagramme temporel pour générer une horloge divisée par 3 avec un rapport cyclique de 50% en utilisant une « opération AND » est montré ci-dessous.

Utiliser le front montant de l'horloge source pour générer une horloge divisée par 3 avec un niveau haut de 2 cycles et un niveau bas de 1 cycle.

Utiliser le front descendant de l'horloge source pour générer une horloge divisée par 3 avec un niveau haut de 2 cycles et un niveau bas de 1 cycle.

Les deux horloges divisées par 3 doivent être générées avec la même valeur de compteur mais sur des fronts différents, avec une différence de phase d'un demi-cycle d'horloge. Ensuite, en effectuant une « opération AND » sur les deux horloges, on peut obtenir une horloge divisée par 3 avec un rapport cyclique de 50%.

De même, pour la division par 9, il est nécessaire de générer des horloges divisées par 9 avec 5 cycles à niveau haut et 4 cycles à niveau bas sur les fronts montant et descendant, puis d'effectuer une « opération AND » sur les deux horloges. La description Verilog est la suivante.

Exemple

module odo_div_and
   #( parameter DIV_CLK = 9 )
   (
    input               rstn ,
    input               clk,
    output              clk_div9
    );

   //compteur
   reg [3:0]            cnt ;
   always @(posedge clk or negedge rstn) begin
      if (!rstn) begin
         cnt    <= 'b0 ;
      end
      else if (cnt == DIV_CLK-1) begin
         cnt    <= 'b0 ;
      end
      else begin
         cnt    <= cnt + 1'b1 ;
      end
   end

   //générer un diviseur par 9 sur front montant
   reg                  clkp_div9_r ;
   always @(posedge clk or negedge rstn) begin
      if (!rstn) begin
         clkp_div9_r <= 1'b0 ;
      end
      else if (cnt == (DIV_CLK>>1) ) begin //compter 5-8 comme niveau bas
        clkp_div9_r <= 0 ;
      end
      else if (cnt == DIV_CLK-1) begin //compter 0-4 comme niveau haut
        clkp_div9_r <= 1 ;
      end
   end

   //générer un diviseur par 9 sur front descendant
   reg                  clkn_div9_r ;
   always @(negedge clk or negedge rstn) begin
      if (!rstn) begin
         clkn_div9_r <= 1'b0 ;
      end
      else if (cnt == (DIV_CLK>>1) ) begin
        clkn_div9_r <= 0 ;
      end
      else if (cnt == DIV_CLK-1) begin
        clkn_div9_r <= 1 ;
      end
   end

   //opération AND, en utilisant généralement une bibliothèque de cellules logiques de base
   //and (clk_div9, clkp_div9_r, clkn_div9_r) ;
   assign clk_div9 = clkp_div9_r & clkn_div9_r ;

endmodule

Les résultats de la simulation sont les suivants.

Division par demi-entier

En utilisant la logique à double front de l'horloge, une division par demi-entier peut être effectuée. Cependant, quelle que soit la manière dont elle est ajustée, le rapport cyclique de la division par demi-entier ne peut pas être de 50 %. Il existe de nombreuses méthodes pour la division par demi-entier ; ici, seule une méthode similaire à l'ajustement du rapport cyclique pour la division par nombre impair est présentée.

  • (1) Par exemple, lors d'une division par 3,5, le compteur compte cycliquement jusqu'à 7, générant deux horloges divisées composées respectivement de 4 et 3 cycles d'horloge source. Du point de vue selon lequel 2 horloges divisées sont générées à partir de 7 horloges source, le processus accomplit une opération de division par 3,5, mais chaque horloge divisée n'est pas strictement un résultat de division par 3,5.
  • (2) Ensuite, ajustez les horloges divisées à périodes inégales. Dans un cycle de comptage, générez deux horloges divisées composées respectivement de 4 et 3 cycles d'horloge source sur le front descendant de l'horloge source. Comparé aux deux horloges à période inégale générées la première fois, la phase des deux horloges générées cette fois est respectivement retardée d'une demi-période d'horloge source et avancée d'une demi-période d'horloge source.
  • (3) En effectuant une « opération OR » sur les deux horloges générées, une horloge de division par 3,5 à période uniforme peut être obtenue. Le diagramme d'ondes de l'horloge divisée est présenté ci-dessous.

La description Verilog de l'horloge de division par 3,5 est la suivante.

Exemple

module half_divisor(
    input               rstn ,
    input               clk,
    output              clk_div3p5
    );

   //compteur
   parameter            MUL2_DIV_CLK = 7 ;
   reg [3:0]            cnt ;
   always @(posedge clk or negedge rstn) begin
      if (!rstn) begin
         cnt    <= 'b0 ;
      end
      else if (cnt == MUL2_DIV_CLK-1) begin //compter jusqu'à 2 fois le rapport de division
         cnt    <= 'b0 ;
      end
      else begin
         cnt    <= cnt + 1'b1 ;
      end
   end

   reg                  clk_ave_r ;
   always @(posedge clk or negedge rstn) begin
      if (!rstn) begin
         clk_ave_r <= 1'b0 ;
      end
      //premier cycle : 4 cycles d'horloge source
      else if (cnt == 0) begin
         clk_ave_r <= 1 ;
      end
      //2e cycle : 3 cycles d'horloge source
      else if (cnt == (MUL2_DIV_CLK/2)+1) begin
         clk_ave_r <= 1 ;
      end
      else begin
         clk_ave_r <= 0 ;
      end
   end

   //ajuster
   reg                  clk_adjust_r ;
   always @(negedge clk or negedge rstn) begin
      if (!rstn) begin
         clk_adjust_r <= 1'b0 ;
      end
      //cette horloge sert uniquement à ajuster un rapport cyclique constant
      else if (cnt == 1) begin
         clk_adjust_r <= 1 ;
      end
      //cette horloge sert uniquement à ajuster un rapport de division précis et constant
      else if (cnt == (MUL2_DIV_CLK/2)+1 ) begin
         clk_adjust_r <= 1 ;
      end
      else begin
         clk_adjust_r <= 0 ;
      end
   end

   assign clk_div3p5 = clk_adjust_r | clk_ave_r ;

endmodule

Les résultats de la simulation sont les suivants.

Division fractionnaire

Principe de base

La division fractionnaire irrégulière ne peut pas faire de chaque cycle d'horloge divisé un multiple fractionnaire exact du cycle d'horloge source, ni ne peut rendre le rapport cyclique de l'horloge divisée égal à 50 %, car Verilog ne peut pas effectuer de comptage fractionnaire sur l'horloge. Comme le concept de "fréquence moyenne" introduit dans la première étape de division de la division en demi-entiers, la division fractionnaire est également implémentée sur la base d'une division variable et de plusieurs méthodes de moyennage.

Par exemple, pour une division par 7.6, il suffit de garantir que la durée de 76 cycles d'horloge source soit égale à la durée de 10 cycles d'horloge divisée. Dans ce cas, il est nécessaire d'effectuer 6 opérations de division par 8 et 4 opérations de division par 7 sur 76 cycles d'horloge source. Autre exemple : pour une division par 5.76, il est nécessaire d'effectuer 76 opérations de division par 6 et 24 opérations de division par 5 sur 576 cycles d'horloge source.

Le processus de calcul de ces paramètres de division est expliqué ci-dessous.

Lorsqu'on effectue une division par 7, cela peut être compris comme l'exécution de 10 opérations de division par 7 sur 70 cycles d'horloge source. Si 10 divisions sont encore effectuées sur 76 cycles d'horloge source, cela équivaut à répartir les 6 cycles d'horloge source supplémentaires dans les 70 cycles d'horloge source, ce qui permet de réaliser l'opération de division par 7.6. Dans le processus de division, 6 horloges divisées doivent être obtenues par division par 8, tandis que les 4 horloges divisées restantes conserveront l'état de division par 7 d'origine.

De nombreux endroits donnent un système de deux équations linéaires pour calculer ces paramètres de division, comme indiqué ci-dessous. Le principe est le même que l'analyse ci-dessus, et il est recommandé de maîtriser le processus de calcul décrit ci-dessus.

7N + 8M = 76
N  + M   = 10 

Ici, 7 est le facteur de division entier, N est le nombre de fois où la division entière est effectuée ; 8 est le facteur de division obtenu en ajoutant un au facteur de division entier, et M est le nombre de fois où cette division est effectuée.

Principe de moyennage

En prenant la division par 7.6 comme exemple, l'ordre d'exécution des divisions par 7 et par 8 présente généralement les 4 cas suivants :

  • (1) Effectuer d'abord 4 fois la division par 7, puis effectuer 6 fois la division par 8 ;
  • (2) Effectuer d'abord 6 fois la division par 8, puis effectuer 4 fois la division par 7 ;
  • (3) Insérer uniformément les 4 opérations de division par 7 parmi les 6 opérations de division par 8 ;
  • (4) Insérer uniformément les 6 opérations de division par 8 parmi les 4 opérations de division par 7.

Les deux premières méthodes entraînent des fréquences d'horloge irrégulières et une gigue de phase importante, de sorte que les deux dernières méthodes d'insertion moyenne sont généralement utilisées pour la division fractionnaire.

L'insertion moyenne peut être mise en œuvre en accumulant la différence des nombres de divisions. Le processus de mise en œuvre de la division par 7.6 est le suivant :

  • (1) La première différence de nombre de divisions est 76-10*7 = 6 < 10, donc la première division est une division par 7.
  • (2) Le deuxième résultat de différence accumulée est 6+6=12 > 10, donc la deuxième division utilise une division par 8, et la différence est modifiée en 12-10=2.
  • (3) Le troisième résultat de différence accumulée est 2+6=8 < 10, donc la troisième division utilise une division par 7.
  • (4) Le quatrième résultat de différence accumulée est 8+6=14 > 10, donc la quatrième division utilise une division par 8, et la différence est modifiée en 14-10=4.

Par analogie, complétez le processus d'insertion uniforme de six opérations de division par 8 dans quatre opérations de division par 7.

Le tableau suivant présente le processus de division de fréquence de la méthode d'insertion moyenne.

Nombre de divisionsAccumulation de la différenceModification de la différenceCycle de division
1667
26+6=1228
32+6=887
48+6=1448
54+6=1008
6667
76+6=1228
82+6=887
98+6=1448
104+6=1008

Simulation de conception

La description Verilog basée sur la méthode d'implémentation de division de fréquence fractionnaire ci-dessus est la suivante.

Exemple

module frac_divisor
  #(
   parameter            SOURCE_NUM = 76 , //cycles de l'horloge source
   parameter            DEST_NUM   = 10  //cycles de l'horloge de destination
   )
   (
    input               rstn ,
    input               clk,
    output              clk_frac
    );
 
   //paramètre de division par 7, paramètre de division par 8, différence de comptage
   parameter    SOURCE_DIV = SOURCE_NUM/DEST_NUM ;
   parameter    DEST_DIV   = SOURCE_DIV + 1;
   parameter    DIFF_ACC   = SOURCE_NUM - SOURCE_DIV*DEST_NUM ;


   reg [3:0]            cnt_end_r ;  //cycle de division variable
   reg [3:0]            main_cnt ;   //compteur principal
   reg                  clk_frac_r ; //sortie d'horloge, avec une durée à niveau haut de 1 cycle
   always @(posedge clk or negedge rstn) begin
      if (!rstn) begin
         main_cnt    <= 'b0 ;
         clk_frac_r  <= 1'b0 ;
      end
      else if (main_cnt == cnt_end_r) begin
         main_cnt    <= 'b0 ;
         clk_frac_r  <= 1'b1 ;
      end
      else begin
         main_cnt    <= main_cnt + 1'b1 ;
         clk_frac_r  <= 1'b0 ;
      end
   end
   //horloge de sortie
   assign       clk_frac        = clk_frac_r ;
   //commande d'activation de l'accumulateur de différence
   wire         diff_cnt_en     = main_cnt == cnt_end_r ;

   //logique de l'accumulateur de différence
   reg [4:0]            diff_cnt_r ;
   wire [4:0]           diff_cnt = diff_cnt_r >= DEST_NUM ?
                                   diff_cnt_r -10 + DIFF_ACC :
                                   diff_cnt_r + DIFF_ACC ;                                
   always @(posedge clk or negedge rstn) begin
      if (!rstn) begin
         diff_cnt_r <= 0 ;
      end
      else if (diff_cnt_en) begin
         diff_cnt_r <= diff_cnt ;
      end
   end

   //logique de contrôle pour la variable de cycle de division
   always @(posedge clk or negedge rstn) begin
      if (!rstn) begin
         cnt_end_r      <= SOURCE_DIV-1 ;
      end
      //lorsque l'accumulateur de différence déborde, modifier le cycle de division
      else if (diff_cnt >= 10) begin
         cnt_end_r      <= DEST_DIV-1 ;
      end
      else begin
         cnt_end_r      <= SOURCE_DIV-1 ;
      end
   end

endmodule

Les résultats de la simulation sont les suivants.

Résumé de la division

La division de fréquence paire peut réaliser une division d'horloge à 50% de rapport cyclique sans utiliser la logique à double front de l'horloge, ce qui en fait la condition de division de fréquence la plus idéale.

Pour la division de fréquence impaire, si une horloge divisée à 50% de rapport cyclique est requise, la logique à double front de l'horloge doit être utilisée. Si le rapport cyclique n'est pas requis, la méthode d'implémentation est similaire à celle de la division de fréquence paire.

La division de fréquence par demi-entier est un type particulier de division de fréquence fractionnaire et peut être conçue à l'aide d'une logique à double front. Lorsque l'on combine deux signaux d'horloge à double front en une horloge de sortie finale au moyen d'une certaine logique, il est recommandé de ne pas utiliser de logique de sélection. En effet, des glitches sont susceptibles de se produire et le circuit introduira également une incertitude supplémentaire. Par exemple, la description suivante n'est pas recommandée.

assign clk_div3p5 = (cnt == 1 || cnt ==2) ? clk_ave_r
                                          : clk_adjust_r ;

L'idée de base de la division de fréquence fractionnaire est que la fréquence moyenne de l'horloge de sortie sur une période de temps satisfait à l'exigence de division. Cependant, en tenant compte de la gigue de phase, il est également nécessaire de faire la moyenne de la logique de division là où le coefficient de division change.

Téléchargement du code source de ce chapitre

Téléchargement