Générateur d'Horloge Verilog : Guide de Conception et d'Implémentation
Les horloges sont fondamentales pour construire des circuits numériques car elles permettent à différents blocs d'être synchronisés entre eux.
Propriétés d'une horloge
Les propriétés clés d'une horloge numérique sont sa fréquence qui détermine la période d'horloge, son cycle de travail (duty cycle) et la phase d'horloge par rapport aux autres horloges.
Période d'Horloge
La fréquence indique combien de cycles peuvent être trouvés dans une certaine période de temps. Et donc la période d'horloge est le temps pris pour compléter 1 cycle.
Cycle de Travail (Duty Cycle)
La quantité de temps pendant laquelle l'horloge est haute comparée à sa période définit le cycle de travail.
Phase d'Horloge
Si un cycle d'horloge peut être vu comme un cercle complet avec 360 degrés, une autre horloge peut être relativement placée à un endroit différent dans le cercle qui occupe une phase différente. Par exemple, une autre horloge de la même période décalée vers la droite par 1/4 de sa période peut être dite à 90 degrés de différence de phase.
Générateur d'Horloge Verilog
Les simulations sont requises pour opérer sur un timescale donné qui a une précision limitée comme spécifié par la directive timescale. Il est donc important que la précision du timescale soit assez bonne pour représenter une période d'horloge. Par exemple, si la fréquence de l'horloge est fixée à 640000 kHz, alors sa période d'horloge sera de 1.5625 ns pour laquelle une précision de timescale de 1ps ne suffira pas car il y a un chiffre supplémentaire à représenter. Donc la simulation arrondira le dernier chiffre pour s'adapter à la précision de timescale à 3 points. Cela fera passer la période d'horloge à 1.563 ce qui représente en fait 639795 kHz !
Le module générateur d'horloge Verilog suivant a trois paramètres pour ajuster les trois propriétés différentes discutées ci-dessus. Le module a une entrée enable qui permet à l'horloge d'être désactivée et activée selon les besoins. Quand plusieurs horloges sont contrôlées par un signal enable commun, elles peuvent être relativement phasées facilement.
`timescale 1ns/1ps
module clock_gen (
input enable,
output reg clk
);
parameter FREQ = 100000; // en kHZ
parameter PHASE = 0; // en degrés
parameter DUTY = 50; // en pourcentage
real clk_pd = 1.0/(FREQ * 1e3) * 1e9; // convertir en ns
real clk_on = DUTY/100.0 * clk_pd;
real clk_off = (100.0 - DUTY)/100.0 * clk_pd;
real quarter = clk_pd/4;
real start_dly = quarter * PHASE/90;
reg start_clk;
initial begin
$display("FREQ = %0d kHz", FREQ);
$display("PHASE = %0d deg", PHASE);
$display("DUTY = %0d %%", DUTY);
$display("PERIOD = %0.3f ns", clk_pd);
$display("CLK_ON = %0.3f ns", clk_on);
$display("CLK_OFF = %0.3f ns", clk_off);
$display("QUARTER = %0.3f ns", quarter);
$display("START_DLY = %0.3f ns", start_dly);
end
// Initialiser les variables à zéro
initial begin
clk <= 0;
start_clk <= 0;
end
// Quand l'horloge est activée, retarder le pilotage de l'horloge à 1 afin
// d'atteindre l'effet de phase. start_dly est configuré au
// délai correct pour la phase configurée. Quand enable est 0,
// permettre assez de temps pour compléter la période d'horloge courante
always @ (posedge enable or negedge enable) begin
if (enable) begin
#(start_dly) start_clk = 1;
end else begin
#(start_dly) start_clk = 0;
end
end
// Atteindre le cycle de travail par un temps d'allumage/extinction d'horloge décalé et laisser
// cela tourner aussi longtemps que les horloges sont allumées.
always @(posedge start_clk) begin
if (start_clk) begin
clk = 1;
while (start_clk) begin
#(clk_on) clk = 0;
#(clk_off) clk = 1;
end
clk = 0;
end
end
endmodule
Banc de test avec différentes fréquences d'horloge
module tb;
wire clk1, clk2, clk3, clk4;
reg enable;
reg [7:0] dly;
clock_gen u0(enable, clk1);
clock_gen #(.FREQ(200000)) u1(enable, clk2);
clock_gen #(.FREQ(400000)) u2(enable, clk3);
clock_gen #(.FREQ(800000)) u3(enable, clk4);
initial begin
enable <= 0;
for (int i = 0; i < 10; i= i+1) begin
dly = $random;
#(dly) enable <= ~enable;
$display("i=%0d dly=%0d", i, dly);
#50;
end
#50 $finish;
end
endmodule
Journal de Simulation
xcelium> run FREQ = 100000 kHz PHASE = 0 deg DUTY = 50 % PERIOD = 10.000 ns CLK_ON = 5.000 ns CLK_OFF = 5.000 ns QUARTER = 2.500 ns START_DLY = 0.000 ns FREQ = 200000 kHz PHASE = 0 deg DUTY = 50 % PERIOD = 5.000 ns CLK_ON = 2.500 ns CLK_OFF = 2.500 ns QUARTER = 1.250 ns START_DLY = 0.000 ns FREQ = 400000 kHz PHASE = 0 deg DUTY = 50 % PERIOD = 2.500 ns CLK_ON = 1.250 ns CLK_OFF = 1.250 ns QUARTER = 0.625 ns START_DLY = 0.000 ns FREQ = 800000 kHz PHASE = 0 deg DUTY = 50 % PERIOD = 1.250 ns CLK_ON = 0.625 ns CLK_OFF = 0.625 ns QUARTER = 0.312 ns START_DLY = 0.000 ns i=0 dly=36 i=1 dly=129 i=2 dly=9 i=3 dly=99 i=4 dly=13 i=5 dly=141 i=6 dly=101 i=7 dly=18 i=8 dly=1 i=9 dly=13 Simulation complete via $finish(1) at time 1110 NS + 0
Banc de test avec différentes phases d'horloge
module tb;
wire clk1, clk2;
reg enable;
reg [7:0] dly;
clock_gen u0(enable, clk1);
clock_gen #(.FREQ(50000), .PHASE(90)) u1(enable, clk2);
initial begin
enable <= 0;
for (int i = 0; i < 10; i=i+1) begin
dly = $random;
#(dly) enable <= ~enable;
$display("i=%0d dly=%0d", i, dly);
end
#50 $finish;
end
endmodule
Journal de Simulation
xcelium> run FREQ = 100000 kHz PHASE = 0 deg DUTY = 50 % PERIOD = 10.000 ns CLK_ON = 5.000 ns CLK_OFF = 5.000 ns QUARTER = 2.500 ns START_DLY = 0.000 ns FREQ = 100000 kHz PHASE = 90 deg DUTY = 50 % PERIOD = 10.000 ns CLK_ON = 5.000 ns CLK_OFF = 5.000 ns QUARTER = 2.500 ns START_DLY = 2.500 ns FREQ = 100000 kHz PHASE = 180 deg DUTY = 50 % PERIOD = 10.000 ns CLK_ON = 5.000 ns CLK_OFF = 5.000 ns QUARTER = 2.500 ns START_DLY = 5.000 ns FREQ = 100000 kHz PHASE = 270 deg DUTY = 50 % PERIOD = 10.000 ns CLK_ON = 5.000 ns CLK_OFF = 5.000 ns QUARTER = 2.500 ns START_DLY = 7.500 ns i=0 dly=36 i=1 dly=129 i=2 dly=9 i=3 dly=99 i=4 dly=13 i=5 dly=141 i=6 dly=101 i=7 dly=18 i=8 dly=1 i=9 dly=13 Simulation complete via $finish(1) at time 1110 NS + 0
Banc de test avec différents cycles de travail
module tb;
wire clk1, clk2, clk3, clk4;
reg enable;
reg [7:0] dly;
clock_gen u0(enable, clk1);
clock_gen #(.DUTY(25)) u1(enable, clk2);
clock_gen #(.DUTY(75)) u2(enable, clk3);
clock_gen #(.DUTY(90)) u3(enable, clk4);
initial begin
enable <= 0;
for (int i = 0; i < 10; i= i+1) begin
dly = $random;
#(dly) enable <= ~enable;
$display("i=%0d dly=%0d", i, dly);
#50;
end
#50 $finish;
end
endmodule
Journal de Simulation
xcelium> run FREQ = 100000 kHz PHASE = 0 deg DUTY = 50 % PERIOD = 10.000 ns CLK_ON = 5.000 ns CLK_OFF = 5.000 ns QUARTER = 2.500 ns START_DLY = 0.000 ns FREQ = 100000 kHz PHASE = 0 deg DUTY = 25 % PERIOD = 10.000 ns CLK_ON = 2.500 ns CLK_OFF = 7.500 ns QUARTER = 2.500 ns START_DLY = 0.000 ns FREQ = 100000 kHz PHASE = 0 deg DUTY = 75 % PERIOD = 10.000 ns CLK_ON = 7.500 ns CLK_OFF = 2.500 ns QUARTER = 2.500 ns START_DLY = 0.000 ns FREQ = 100000 kHz PHASE = 0 deg DUTY = 90 % PERIOD = 10.000 ns CLK_ON = 9.000 ns CLK_OFF = 1.000 ns QUARTER = 2.500 ns START_DLY = 0.000 ns i=0 dly=36 i=1 dly=129 i=2 dly=9 i=3 dly=99 i=4 dly=13 i=5 dly=141 i=6 dly=101 i=7 dly=18 i=8 dly=1 i=9 dly=13 Simulation complete via $finish(1) at time 1110 NS + 0
Basculer enable pour démarrer/arrêter les horloges
La forme d'onde ci-dessous montre que les horloges sont arrêtées quand enable est bas et démarrées quand enable est mis haut.
Verilog
- Maîtriser Verilog : Tutoriel complet pour la conception de circuits numériques
- Maîtriser la concaténation Verilog : Guide pratique
- Comprendre les affectations Verilog : Types, Syntaxe et Bonnes Pratiques
- Verilog Blocking vs Non-Blocking : Différences Clés Expliquées
- Maîtriser les Fonctions Verilog pour une Conception RTL Efficace
- Comprendre les Tasks Verilog : Fonctions vs Tasks, Syntaxe et Utilisation
- Maîtriser les Fonctions Mathématiques Verilog pour une Conception Matérielle Efficace
- Maîtriser Verilog $timeformat : Contrôle de Précision et d'Affichage
- Comprendre et Afficher les Timescales Verilog à Toute Portée