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
# (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
#(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
#( 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
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 divisions | Accumulation de la différence | Modification de la différence | Cycle de division |
|---|---|---|---|
| 1 | 6 | 6 | 7 |
| 2 | 6+6=12 | 2 | 8 |
| 3 | 2+6=8 | 8 | 7 |
| 4 | 8+6=14 | 4 | 8 |
| 5 | 4+6=10 | 0 | 8 |
| 6 | 6 | 6 | 7 |
| 7 | 6+6=12 | 2 | 8 |
| 8 | 2+6=8 | 8 | 7 |
| 9 | 8+6=14 | 4 | 8 |
| 10 | 4+6=10 | 0 | 8 |
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
#(
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.
: 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