Verilog Clock Generator: Design & Implementierungsleitfaden
Takte sind fundamental für den Aufbau digitaler Schaltungen, da sie es verschiedenen Blöcken ermöglichen, miteinander synchron zu sein.
Eigenschaften eines Takts
Die wichtigsten Eigenschaften eines digitalen Takts sind seine Frequenz, die die Taktperiode bestimmt, sein Duty Cycle und die Taktphase in Relation zu anderen Takten.
Taktperiode
Die Frequenz gibt an, wie viele Zyklen in einem bestimmten Zeitraum zu finden sind. Und deshalb ist die Taktperiode die Zeit, die benötigt wird, um 1 Zyklus abzuschließen.
Duty Cycle
Die Zeit, in der der Takt high ist, im Vergleich zu seiner Periode definiert den Duty Cycle.
Taktphase
Wenn ein Zyklus eines Takts als ein vollständiger Kreis mit 360 Grad betrachtet werden kann, kann ein anderer Takt relativ an einer anderen Stelle im Kreis platziert werden, die eine andere Phase einnimmt. Zum Beispiel kann ein anderer Takt derselben Periode, der um 1/4 seiner Periode nach rechts verschoben ist, als 90 Grad Phasenunterschied bezeichnet werden.
Verilog Clock Generator
Simulationen müssen auf einem gegebenen Timescale operieren, der eine begrenzte Präzision hat, wie durch die Timescale-Direktive spezifiziert. Daher ist es wichtig, dass die Präzision des Timescale gut genug ist, um eine Taktperiode darzustellen. Zum Beispiel, wenn die Frequenz des Takts auf 640000 kHz gesetzt ist, dann wird seine Taktperiode 1.5625 ns betragen, wofür eine Timescale-Präzision von 1ps nicht ausreichen wird, weil ein zusätzlicher Punkt dargestellt werden muss. Daher wird die Simulation die letzte Ziffer runden, um in die 3-Stellen-Timescale-Präzision zu passen. Dies erhöht die Taktperiode auf 1.563, was tatsächlich 639795 kHz repräsentiert!
Das folgende Verilog-Clock-Generator-Modul hat drei Parameter, um die drei oben diskutierten Eigenschaften anzupassen. Das Modul hat einen Input Enable, der es erlaubt, den Takt nach Bedarf zu deaktivieren und zu aktivieren. Wenn mehrere Takte durch ein gemeinsames Enable-Signal gesteuert werden, können sie relativ einfach gephased werden.
`timescale 1ns/1ps
module clock_gen (
input enable,
output reg clk
);
parameter FREQ = 100000; // in kHz
parameter PHASE = 0; // in Grad
parameter DUTY = 50; // in Prozent
real clk_pd = 1.0/(FREQ * 1e3) * 1e9; // in ns umrechnen
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
// Variablen auf Null initialisieren
initial begin
clk <= 0;
start_clk <= 0;
end
// Wenn Takt aktiviert ist, das Treiben des Takts auf 1 verzögern, um
// den Phase-Effekt zu erreichen. start_dly ist auf die korrekte
// Verzögerung für die konfigurierte Phase konfiguriert. Wenn enable 0 ist,
// genug Zeit lassen, um die aktuelle Taktperiode abzuschließen
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
// Duty Cycle durch verzerrte Takt-ein/aus-Zeit erreichen und dies
// so lange laufen lassen, wie die Takte eingeschaltet sind.
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
Testbench mit verschiedenen Taktfrequenzen
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
Simulationslog:
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
Testbench mit verschiedenen Taktphasen
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
Simulationslog:
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
Testbench mit verschiedenen Duty Cycles
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
Simulationslog:
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
Enable toggeln, um Takte zu starten/stoppen
Verilog
- Verilog meistern: Komplettes Tutorial für digitales Schaltungsdesign
- Verilog Concatenation meistern: Praktischer Leitfaden
- Verilog Assignments verstehen: Typen, Syntax und Best Practices
- Verilog Blocking vs. Non-Blocking: Wichtige Unterschiede erklärt
- Verilog Functions meistern für effizientes RTL-Design
- Verilog Tasks verstehen: Functions vs. Tasks, Syntax und Verwendung
- Verilog Math Functions meistern für effizientes Hardware-Design
- Verilog $timeformat meistern: Präzision und Anzeigesteuerung
- Verilog Timescales an jedem Scope verstehen und ausgeben
- Datei-E/A in Verilog meistern: Öffnen, Lesen, Schreiben und Schließen