Diese Seite gehört zu einer Serie von Seiten über Timing. Nach einer kurzen Einführung in die Theorie hinter Timing-Vorgaben und der ersten Seite über die Taktperioden-Vorgabe ist es an der Zeit, einige realistische Szenarien mit dieser Vorgabe anzusehen.
Der nächste Schritt: Einsatz einer PLL
In dem Beispiel, das auf der vorherigen Seite gezeigt wurde, war der externe Takteingangspin direkt mit der Logik verbunden. In den meisten realen Designs wird eine PLL eingesetzt, um den Takt für die Logik zu erzeugen. Der offensichtlichste Grund ist, dass die Logik eine andere Frequenz benötigt als die des externen Takts. Eine PLL kann aber auch helfen, den externen Takt von Unvollkommenheiten zu bereinigen, insbesondere von Jitter.
Eine PLL kann dem Design mit folgendem Verilog-Code hinzugefügt werden:
module top(
input clk,
input foo,
output reg bar_reg
);
reg foo_reg;
reg bar;
wire pll_clk;
clk_wiz_0 pll_i
(.clk_in1(clk),
.clk_out1(pll_clk));
always @(posedge pll_clk)
begin
foo_reg <= foo;
bar <= !foo_reg;
bar_reg <= bar;
end
endmodule
Das ähnelt dem vorherigen Beispiel, aber diesmal ist der Takt der Flipflops @pll_clk statt @clk. Die PLL wird mit dem Clocking Wizard IP-Core von Vivado erzeugt, der im Verilog-Code als Modul mit dem Namen clk_wiz_0 verwendet wird.
Für dieses Beispiel wurde der Clocking Wizard so konfiguriert, dass er einen Referenztakt mit 250 MHz annimmt und einen 125-MHz-Takt am Ausgangsport erzeugt (also @clk_out1). Um das Beispiel einfach zu halten, hat clk_wiz_0 keinen Reset-Eingang und keinen „locked“-Ausgang. In den meisten realen Designs empfiehlt es sich, diese Ports zu aktivieren und zu verwenden.
Eine weitere Sache an clk_wiz_0 ist, dass die Option zur Phasenausrichtung (phase alignment) aktiviert ist. Dadurch werden die Taktflanken von @pll_clk an den Taktflanken von @clk ausgerichtet. Diese Option ist nützlich, wenn das Design I/O-Ports enthält, die synchron zum externen Takt sind: Durch die aktivierte Phasenausrichtung wird das Timing-Verhältnis zwischen externem Takt und internem Takt vorhersagbar. Das ist bei I/O-Ports nützlich, die Timing-Anforderungen relativ zum externen Takt erfüllen müssen.
Um bei Xilinx’ Terminologie genau zu sein: Xilinx-FPGAs haben zwei Arten von PLLs. Die eine Art heißt PLL, die zweite heißt MMCM. Der Unterschied ist für dieses Beispiel unerheblich. clk_wiz_0 ist ein MMCM, aber der Klarheit halber werde ich es weiterhin als PLL bezeichnen.
Es ist noch einmal erwähnenswert, dass alles, was hier über die PLL gesagt wird, für alle FPGAs auf dem Markt gilt. Das Beispiel wird mit Vivado gezeigt, aber eine PLL, die genau das Gleiche tut wie clk_wiz_0, lässt sich für alle FPGAs erzeugen.
Die Timing-Vorgabe mit einer PLL
Das Wichtigste, was man über Timing-Vorgaben mit einer PLL wissen muss, ist, dass man dabei nichts Besonderes tun muss. Die Timing-Vorgabe wird mit Bezug auf den externen Pin geschrieben (im Beispiel @clk). Wenn die PLL einen Takt mit einer anderen Frequenz erzeugt, ist es Aufgabe der Tools, das zu berücksichtigen.
Es sei noch einmal gesagt: Es sollte nie nötig sein, wegen einer verwendeten PLL eine zusätzliche Timing-Vorgabe zu schreiben. Wenn man das jemals für nötig hält, ist die Wahrscheinlichkeit groß, dass im Design etwas nicht stimmt. Das Problem mit einer zusätzlichen Timing-Vorgabe zu „beheben“, wird das eigentliche Problem nicht lösen.
Wie zuvor lautet die Timing-Vorgabe also einfach:
create_clock -period 4.000 -name clk [get_ports clk]
Wer Quartus verwendet, sollte die Falle kennen, die auf dieser Seite beschrieben wird.
Der Timing-Report mit einer PLL
Wir schauen uns nun den Timing-Report desselben Pfads an wie im Beispiel auf der vorherigen Seite. Der einzige Unterschied ist, dass die PLL hinzugefügt wurde, wie oben gezeigt. Die Analyse des relevanten Pfads ist hier in derselben Reihenfolge dargestellt, in der sie im Timing-Report erscheint. Zuerst also die Zusammenfassung der Analyse:
Slack (MET) : 7.288ns (required time - arrival time) Source: foo_reg_reg/C (rising edge-triggered cell FDRE clocked by clk_out1_clk_wiz_0 {rise@0.000ns fall@4.000ns period=8.000ns}) Destination: bar_reg__0/D (rising edge-triggered cell FDRE clocked by clk_out1_clk_wiz_0 {rise@0.000ns fall@4.000ns period=8.000ns}) Path Group: clk_out1_clk_wiz_0 Path Type: Setup (Max at Slow Process Corner) Requirement: 8.000ns (clk_out1_clk_wiz_0 rise@8.000ns - clk_out1_clk_wiz_0 rise@0.000ns) Data Path Delay: 0.669ns (logic 0.382ns (57.100%) route 0.287ns (42.900%)) Logic Levels: 1 (LUT1=1) Clock Path Skew: -0.048ns (DCD - SCD + CPR) Destination Clock Delay (DCD): -0.715ns = ( 7.285 - 8.000 ) Source Clock Delay (SCD): -0.616ns Clock Pessimism Removal (CPR): 0.051ns Clock Uncertainty: 0.062ns ((TSJ^2 + DJ^2)^1/2) / 2 + PE Total System Jitter (TSJ): 0.071ns Discrete Jitter (DJ): 0.103ns Phase Error (PE): 0.000ns Clock Net Delay (Source): 1.389ns (routing 0.002ns, distribution 1.387ns) Clock Net Delay (Destination): 1.218ns (routing 0.002ns, distribution 1.216ns)
Es gibt mehrere bemerkenswerte Unterschiede. Zunächst beträgt die Requirement (Anforderung) 8 ns statt vorher 4 ns. Das ist zu erwarten, weil der Ausgang der PLL 125 MHz liefert. Die Tools haben für diesen Ausgang automatisch eine zusätzliche Timing-Vorgabe erzeugt, mit einer Taktperiode von 8 ns. Da die Taktperiode um 4 ns länger geworden ist und sich der Datenpfad nicht geändert hat, ist die Zeitmarge (Slack) um etwa 4 ns gestiegen.
Ein weiteres Anzeichen für die automatische Timing-Vorgabe ist, dass die Pfadgruppe (Path Group) in diesem Report „clk_out1_clk_wiz_0“ lautet. Vorher war es „clk“. Tatsächlich steht in diesem Report überall dort „clk_out1_clk_wiz_0“, wo vorher „clk“ stand. Wie die automatischen Timing-Vorgaben im Timing-Report dargestellt werden, wird weiter unten im Zusammenhang mit mehreren Takten besprochen.
Eine kleinere Folge der PLL ist, dass die Clock-Uncertainty auf 0,062 ns gestiegen ist: Im vorherigen Beispiel waren es 0,035 ns. Das liegt daran, dass der Discrete Jitter nun 0,103 ns beträgt und nicht null.
Kommen wir nun zur Timing-Analyse selbst. Diesmal sind der Quell-Taktpfad, der Datenpfad und der Taktpfad zum Ziel genau so gemeinsam dargestellt, wie sie im echten Report erscheinen:
Location Delay type Incr(ns) Path(ns) Netlist Resource(s)
------------------------------------------------------------------- -------------------
(clock clk_out1_clk_wiz_0 rise edge)
0.000 0.000 r
AG12 0.000 0.000 r clk (IN)
net (fo=0) 0.000 0.000 pll_i/inst/clkin1_ibuf/I
AG12 INBUF (Prop_INBUF_HRIO_PAD_O)
0.738 0.738 r pll_i/inst/clkin1_ibuf/INBUF_INST/O
net (fo=1, routed) 0.105 0.843 pll_i/inst/clkin1_ibuf/OUT
AG12 IBUFCTRL (Prop_IBUFCTRL_HRIO_I_O)
0.049 0.892 r pll_i/inst/clkin1_ibuf/IBUFCTRL_INST/O
net (fo=1, routed) 0.975 1.867 pll_i/inst/clk_in1_clk_wiz_0
MMCME3_ADV_X1Y0 MMCME3_ADV (Prop_MMCME3_ADV_CLKIN1_CLKOUT0)
-4.474 -2.607 r pll_i/inst/mmcme3_adv_inst/CLKOUT0
net (fo=1, routed) 0.501 -2.106 pll_i/inst/clk_out1_clk_wiz_0
BUFGCE_X1Y0 BUFGCE (Prop_BUFCE_BUFGCE_I_O)
0.101 -2.005 r pll_i/inst/clkout1_buf/O
X2Y0 (CLOCK_ROOT) net (fo=3, routed) 1.389 -0.616 pll_clk
SLICE_X49Y58 FDRE r foo_reg_reg/C
------------------------------------------------------------------- -------------------
SLICE_X49Y58 FDRE (Prop_EFF2_SLICEL_C_Q)
0.138 -0.478 f foo_reg_reg/Q
net (fo=1, routed) 0.241 -0.237 foo_reg
SLICE_X49Y58 LUT1 (Prop_D5LUT_SLICEL_I0_O)
0.244 0.007 r bar__0_i_1/O
net (fo=1, routed) 0.046 0.053 p_0_in
SLICE_X49Y58 FDRE r bar_reg__0/D
------------------------------------------------------------------- -------------------
(clock clk_out1_clk_wiz_0 rise edge)
8.000 8.000 r
AG12 0.000 8.000 r clk (IN)
net (fo=0) 0.000 8.000 pll_i/inst/clkin1_ibuf/I
AG12 INBUF (Prop_INBUF_HRIO_PAD_O)
0.515 8.515 r pll_i/inst/clkin1_ibuf/INBUF_INST/O
net (fo=1, routed) 0.066 8.581 pll_i/inst/clkin1_ibuf/OUT
AG12 IBUFCTRL (Prop_IBUFCTRL_HRIO_I_O)
0.034 8.615 r pll_i/inst/clkin1_ibuf/IBUFCTRL_INST/O
net (fo=1, routed) 0.873 9.488 pll_i/inst/clk_in1_clk_wiz_0
MMCME3_ADV_X1Y0 MMCME3_ADV (Prop_MMCME3_ADV_CLKIN1_CLKOUT0)
-3.934 5.554 r pll_i/inst/mmcme3_adv_inst/CLKOUT0
net (fo=1, routed) 0.422 5.976 pll_i/inst/clk_out1_clk_wiz_0
BUFGCE_X1Y0 BUFGCE (Prop_BUFCE_BUFGCE_I_O)
0.091 6.067 r pll_i/inst/clkout1_buf/O
X2Y0 (CLOCK_ROOT) net (fo=3, routed) 1.218 7.285 pll_clk
SLICE_X49Y58 FDRE r bar_reg__0/C
clock pessimism 0.051 7.336
clock uncertainty -0.062 7.274
SLICE_X49Y58 FDRE (Setup_DFF2_SLICEL_C_D)
0.067 7.341 bar_reg__0
-------------------------------------------------------------------
required time 7.341
arrival time -0.053
-------------------------------------------------------------------
slack 7.288
Im Vergleich zum vorherigen Beispiel gibt es in den Pfaden nur einen Unterschied: Die PLL (die im Report als MMCME3_ADV_X1Y0 erscheint) wurde zwischen den externen Taktpin und den globalen Taktpuffer eingefügt.
Diese PLL hat einen dramatischen Effekt: Im Quell-Taktpfad beträgt die Verzögerung der PLL −4,474 ns, im Taktpfad zum Ziel beträgt dieselbe Verzögerung −3,934 ns. Diese negative Verzögerung spiegelt wider, dass die PLL die Taktflanke so justiert, dass der globale Takt etwas früher kommt als der Takt am Eingangspin. Man beachte, dass die Gesamtverzögerung des Takts am Ausgang des globalen Taktpuffers im Quell-Taktpfad −0,616 ns beträgt. Derselbe Wert beträgt im Taktpfad zum Ziel 7,285 ns. Das ist 0,715 ns früher als die zweite Taktflanke (bei 8 ns).
Mit anderen Worten: Der externe Taktpin und der globale Takt (der an die Logik geliefert wird) haben in beiden Taktpfaden fast denselben Zeitabstand. Obwohl der eine Taktpfad mit maximalen Verzögerungen und der zweite mit minimalen Verzögerungen berechnet wurde, ist das Gesamtergebnis fast gleich.
Das ist kein Zufall: Die PLL verwendet den globalen Taktausgang als Referenz, sodass die Phase am Eingang des globalen Taktpuffers so eingestellt wird, dass die Beziehung zum Takteingangspin gewährleistet ist. Die Unterschiede zwischen minimalen und maximalen Verzögerungen werden von der PLL ausgeglichen. Die Timing-Berechnungen spiegeln das dadurch wider, dass der Unterschied zwischen schnellstem und langsamstem Fall nur 0,1 ns beträgt. Im vorherigen Beispiel war dieser Unterschied viel größer, weil es dort keine PLL gab (0,575 ns; siehe „Clock-Pessimismus-Entfernung“ auf der vorherigen Seite).
Warum der globale Takt etwa 0,6 ns vor dem externen Eingang justiert wird und nicht auf einen anderen Wert, ist eine andere Geschichte. Das erleichtert in vielen Fällen das Erfüllen von Timing-Vorgaben, die mit I/O-Pins zusammenhängen. Deshalb treffen die Tools diese Wahl automatisch. Dennoch bieten alle FPGAs die Möglichkeit, diese Verzögerung zu beeinflussen.
Zwei zusammenhängende Takte (related clocks)
Ziemlich oft benötigt ein Logikdesign mehr als einen Takt. Die Existenz mehrerer Takte in einem FPGA-Design ist ein eigenes Thema, das in der Einführung in Taktdomänen (clock domains) behandelt wird. Die beiden Themen Taktdomänen und Timing sind untrennbar miteinander verbunden. Daher wird empfohlen, diese Einführung (möglicherweise nur kurz) zu lesen, bevor man hier weitermacht. Wegen der engen Beziehung zwischen diesen Themen gibt es einige Überschneidungen zwischen jener Einführung und dieser Seitenreihe.
Im Folgenden werde ich oft Ausdrücke verwenden wie „das Signal X ist synchron mit @clk“. Das bedeutet, dass X der Ausgang eines Flipflops ist, das seinen Wert nur als Reaktion auf eine steigende Flanke eines Takts mit dem Namen „clk“ ändert (abgesehen von asynchronen Resets (asynchronous resets), die hier aber keine Rolle spielen). Wenn zwei Signale Ausgänge von Flipflops sind, die auf denselben Takt reagieren, sind diese beiden Signale selbstverständlich „mit demselben Takt synchron“.
Wir betrachten nun zwei Takte, die von derselben PLL erzeugt werden. Das ist interessant, weil diese beiden Takte in den meisten Fällen als zusammenhängende Takte (related clocks) gelten. Um zu verstehen, warum das interessant ist, nehmen wir an, dass ein Flipflop mit einem dieser Takte synchron ist und ein weiteres Flipflop mit dem zweiten Takt. In diesem Fall ist es in Ordnung, Signale zwischen diesen beiden Flipflops zu verbinden, als wären sie mit demselben Takt synchron. Die FPGA-Tools stellen sicher, dass die Timing-Anforderungen in diesem Fall erfüllt werden.
Für ein besseres Verständnis der Arbeit mit mehreren Takten gibt es eine eigene Seite über zusammenhängende Takte und unabhängige Takte (unrelated clocks). Diese Seite ist die Einführung in das Thema Taktdomänenübergang (clock domain crossing). Hier konzentrieren wir uns auf die Timing-Aspekte zusammenhängender Takte und insbesondere auf den Timing-Report für solche Takte.
Um die untenstehenden Timing-Reports zu erzeugen, wurde im Beispiel eine andere PLL verwendet (also ein Clocking-Wizard-IP-Core). Der Name dieser neuen PLL ist clk_wiz_1. Dabei kam folgender Verilog-Code zum Einsatz:
module top(
input clk,
input foo,
output reg bar_reg
);
reg foo_reg;
reg bar;
wire pll_clk_8, pll_clk_6;
clk_wiz_1 pll_i
(.clk_in1(clk),
.clk_out1(pll_clk_8),
.clk_out2(pll_clk_6));
always @(posedge pll_clk_8)
foo_reg <= foo;
always @(posedge pll_clk_6)
begin
bar <= !foo_reg;
bar_reg <= bar;
end
Wie zuvor beträgt der Eingang dieser PLL (also @clk) 250 MHz, aber sie hat zwei Ausgänge: Einer ist mit @pll_clk_8 verbunden, das mit 125 MHz läuft (die Taktperiode beträgt also 8 ns, daher der Name des Signals). Das ist genau wie @pll_clk im vorherigen Beispiel. Der zweite Ausgang ist mit @pll_clk_6 verbunden, dessen Taktperiode 6 ns beträgt, also ungefähr 166,67 MHz.
Da @pll_clk_8 und @pll_clk_6 von derselben PLL erzeugt werden, sind sie zusammenhängende Takte (related clocks).
clk_wiz_1 hat ebenfalls die Option zur Phasenausrichtung (phase alignment) aktiviert, insbesondere um mit dem vorherigen Beispiel konsistent zu bleiben. Und falls daran Zweifel bestanden: Es gibt weiterhin nur eine einzige Timing-Vorgabe, dieselbe wie zuvor:
create_clock -period 4.000 -name clk [get_ports clk]
Die Taktübersicht verstehen
Die Informationen über alle Takte sind am Beginn des Timing-Reports zusammengefasst. Ich erwähne das erst jetzt, weil die Taktübersicht bei mehreren Takten interessanter ist. Alle FPGA-Tools erzeugen eine solche Übersicht im Report, und es ist immer eine gute Idee, sich diesen Teil anzusehen:
-------------------------------------------------------------------------
| Clock Summary
| -------------
-------------------------------------------------------------------------
Clock Waveform(ns) Period(ns) Frequency(MHz)
----- ------------ ---------- --------------
clk {0.000 2.000} 4.000 250.000
clk_out1_clk_wiz_1 {0.000 4.000} 8.000 125.000
clk_out2_clk_wiz_1 {0.000 3.000} 6.000 166.667
clkfbout_clk_wiz_1 {0.000 2.000} 4.000 250.000
Der Hauptvorteil dieses Teils liegt darin, dass er leicht zu verstehen ist. Außerdem lässt sich damit leicht der häufigste Fehler bei Timing-Vorgaben überprüfen: ob ein Takt die richtige Frequenz hat.
Diese Taktübersicht zeigt, dass die Timing-Vorgabe korrekt interpretiert wurde: Es gibt einen externen Takt mit dem Namen clk und einer Taktperiode von 4 ns. Zusätzlich gibt es drei abgeleitete Takte: clk_out1_clk_wiz_1, clk_out2_clk_wiz_1 und clkfbout_clk_wiz_1. Wie ihre Namen schon andeuten, wurden sie automatisch wegen der PLL mit dem Namen clk_wiz_1 erzeugt.
Die ersten beiden Takte (clk_out1_clk_wiz_1 und clk_out2_clk_wiz_1) sind die beiden Ausgänge der PLL. Der dritte Takt (clkfbout_clk_wiz_1) wird von der PLL verwendet, um die Phase der globalen Takte an den externen Takt anzupassen. clkfbout_clk_wiz_1 hat dieselbe Frequenz wie @clk.
Wenn die Option zur Phasenausrichtung aktiviert ist, ist der Rückkopplungstakt der PLL (clkfbout_clk_wiz_1) mit dem globalen Taktpuffer verbunden. Da PLLs immer zwischen ihrem Eingangstakt und dem Rückkopplungstakt synchronisieren (indem sie diese Takte ausrichten oder eine feste Verzögerung zwischen ihnen einhalten), stellt die Tatsache, dass clkfbout_clk_wiz_1 ein globaler Takt ist, auch eine bekannte Verzögerung zwischen dem Eingangstakt und den anderen Ausgangstakten sicher.
Es ist wichtig zu beachten, dass clk_out1_clk_wiz_1, clk_out2_clk_wiz_1 und clkfbout_clk_wiz_1 keine Takte definieren, die in der Realität existieren. Sie sind nur Symbole für die Takte, die die Software für Timing-Berechnungen verwendet. Dass diese Takte theoretischer Natur sind, zeigt sich unter anderem an ihren Signalformen, die in der Taktübersicht angegeben sind: Diese Signalformen spiegeln das Tastverhältnis (duty cycle) jedes dieser Takte wider. Es bedeutet aber nichts, dass alle vier Takte (clk und die drei theoretischen Takte) bei exakt 0 ns eine steigende Flanke haben. Insbesondere bedeutet es nicht, dass diese vier Takte perfekt zueinander ausgerichtet sind. Selbst wenn sie ausgerichtet sind, lässt sich das an den Timing-Berechnungen erkennen, und die reale Ausrichtung ist nicht perfekt.
Wie diese theoretischen Takte verwendet werden, wird weiter unten im Zusammenhang mit der Timing-Berechnung besprochen, die zwei dieser Takte betrifft.
Eine weitere wichtige Sache an diesen theoretischen Takten ist, dass es keine explizite Timing-Vorgabe für ihre Erzeugung gibt: Die Erzeugung von clk_out1_clk_wiz_* existiert nirgends in den Design-Quellen oder in den von den Tools erzeugten Dateien. In den meisten Fällen wäre es falsch, eine solche Timing-Vorgabe schreiben zu wollen, weil die Verzögerungen des Taktpfads auf diese Weise nicht korrekt berücksichtigt würden. Wenn solche Vorgaben geschrieben werden, berechnen die Tools die relativen Timing-Verhältnisse zwischen den Takten falsch, und Pfade zwischen zusammenhängenden Taktdomänen werden nicht korrekt berechnet.
Also noch einmal: Eine einzige Timing-Vorgabe sollte automatisch die Timing-Vorgaben für alle Ausgangstakte der PLL erzeugen.
Der Timing-Report mit zwei zusammenhängenden Takten
Wie aus dem obigen Verilog-Code hervorgeht, ist @foo_reg mit @pll_clk_8 synchron und @bar mit @pll_clk_6. Die Anweisung, die @bar aktualisiert, beinhaltet also einen Taktdomänenübergang (clock domain crossing):
bar <= !foo_reg;
Die beiden Takte sind zusammenhängende Takte (related clocks). Daher stellen die Tools sicher, dass die Timing-Anforderungen von @bar durch diese Berechnung erfüllt werden (für tsu):
Slack (MET) : 0.475ns (required time - arrival time)
Source: foo_reg_reg/C
(rising edge-triggered cell FDRE clocked by clk_out1_clk_wiz_1 {rise@0.000ns fall@4.000ns period=8.000ns})
Destination: bar_reg__0/D
(rising edge-triggered cell FDRE clocked by clk_out2_clk_wiz_1 {rise@0.000ns fall@3.000ns period=6.000ns})
Path Group: clk_out2_clk_wiz_1
Path Type: Setup (Max at Slow Process Corner)
Requirement: 2.000ns (clk_out2_clk_wiz_1 rise@18.000ns - clk_out1_clk_wiz_1 rise@16.000ns)
Data Path Delay: 1.160ns (logic 0.307ns (26.466%) route 0.853ns (73.534%))
Logic Levels: 1 (LUT1=1)
Clock Path Skew: -0.250ns (DCD - SCD + CPR)
Destination Clock Delay (DCD): -0.681ns = ( 17.319 - 18.000 )
Source Clock Delay (SCD): -0.600ns = ( 15.400 - 16.000 )
Clock Pessimism Removal (CPR): -0.169ns
Clock Uncertainty: 0.182ns ((TSJ^2 + DJ^2)^1/2) / 2 + PE
Total System Jitter (TSJ): 0.071ns
Discrete Jitter (DJ): 0.103ns
Phase Error (PE): 0.120ns
Clock Net Delay (Source): 1.369ns (routing 0.002ns, distribution 1.367ns)
Clock Net Delay (Destination): 1.208ns (routing 0.002ns, distribution 1.206ns)
Location Delay type Incr(ns) Path(ns) Netlist Resource(s)
------------------------------------------------------------------- -------------------
(clock clk_out1_clk_wiz_1 rise edge)
16.000 16.000 r
AG12 0.000 16.000 r clk (IN)
net (fo=0) 0.000 16.000 pll_i/inst/clkin1_ibuf/I
AG12 INBUF (Prop_INBUF_HRIO_PAD_O)
0.738 16.738 r pll_i/inst/clkin1_ibuf/INBUF_INST/O
net (fo=1, routed) 0.105 16.843 pll_i/inst/clkin1_ibuf/OUT
AG12 IBUFCTRL (Prop_IBUFCTRL_HRIO_I_O)
0.049 16.892 r pll_i/inst/clkin1_ibuf/IBUFCTRL_INST/O
net (fo=1, routed) 0.975 17.867 pll_i/inst/clk_in1_clk_wiz_1
MMCME3_ADV_X1Y0 MMCME3_ADV (Prop_MMCME3_ADV_CLKIN1_CLKOUT0)
-4.438 13.429 r pll_i/inst/mmcme3_adv_inst/CLKOUT0
net (fo=1, routed) 0.501 13.930 pll_i/inst/clk_out1_clk_wiz_1
BUFGCE_X1Y1 BUFGCE (Prop_BUFCE_BUFGCE_I_O)
0.101 14.031 r pll_i/inst/clkout1_buf/O
X2Y0 (CLOCK_ROOT) net (fo=1, routed) 1.369 15.400 pll_clk_8
SLICE_X49Y58 FDRE r foo_reg_reg/C
------------------------------------------------------------------- -------------------
SLICE_X49Y58 FDRE (Prop_EFF_SLICEL_C_Q)
0.139 15.539 f foo_reg_reg/Q
net (fo=1, routed) 0.807 16.346 foo_reg
SLICE_X49Y58 LUT1 (Prop_D5LUT_SLICEL_I0_O)
0.168 16.514 r bar__0_i_1/O
net (fo=1, routed) 0.046 16.560 p_0_in
SLICE_X49Y58 FDRE r bar_reg__0/D
------------------------------------------------------------------- -------------------
(clock clk_out2_clk_wiz_1 rise edge)
18.000 18.000 r
AG12 0.000 18.000 r clk (IN)
net (fo=0) 0.000 18.000 pll_i/inst/clkin1_ibuf/I
AG12 INBUF (Prop_INBUF_HRIO_PAD_O)
0.515 18.515 r pll_i/inst/clkin1_ibuf/INBUF_INST/O
net (fo=1, routed) 0.066 18.581 pll_i/inst/clkin1_ibuf/OUT
AG12 IBUFCTRL (Prop_IBUFCTRL_HRIO_I_O)
0.034 18.615 r pll_i/inst/clkin1_ibuf/IBUFCTRL_INST/O
net (fo=1, routed) 0.873 19.488 pll_i/inst/clk_in1_clk_wiz_1
MMCME3_ADV_X1Y0 MMCME3_ADV (Prop_MMCME3_ADV_CLKIN1_CLKOUT1)
-3.890 15.598 r pll_i/inst/mmcme3_adv_inst/CLKOUT1
net (fo=1, routed) 0.422 16.020 pll_i/inst/clk_out2_clk_wiz_1
BUFGCE_X1Y0 BUFGCE (Prop_BUFCE_BUFGCE_I_O)
0.091 16.111 r pll_i/inst/clkout2_buf/O
X2Y0 (CLOCK_ROOT) net (fo=2, routed) 1.208 17.319 pll_clk_6
SLICE_X49Y58 FDRE r bar_reg__0/C
clock pessimism -0.169 17.150
clock uncertainty -0.182 16.967
SLICE_X49Y58 FDRE (Setup_DFF2_SLICEL_C_D)
0.067 17.034 bar_reg__0
-------------------------------------------------------------------
required time 17.034
arrival time -16.560
-------------------------------------------------------------------
slack 0.475
Wie bereits erwähnt, ist eine Timing-Berechnung ein gedachtes Experiment, bei dem eine gedachte Stoppuhr zusammen mit Taktflanken gestartet wird. Im vorherigen Beispiel startete diese Stoppuhr bei 0 ns. In dieser Berechnung startet sie jedoch mit der Taktflanke bei 16 ns. Das liegt daran, dass die Taktperioden der beiden Takte nicht gleich sind. Die Berechnung erfolgt für die ungünstigste Kombination aus erstem und zweitem Takt.
Die steigenden Flanken des ersten Takts liegen bei 0 ns, 8 ns, 16 ns, 24 ns usw. Die steigenden Flanken des zweiten Takts liegen bei 0 ns, 6 ns, 12 ns, 18 ns, 24 ns usw. Der kleinste zeitliche Abstand zwischen dem ersten und dem zweiten Takt besteht also zwischen der Flanke des ersten Takts bei 16 ns und der Flanke des zweiten Takts bei 18 ns. Diese Situation untersucht die vorliegende Timing-Berechnung.
Das bedeutet, dass der Datenpfad auf ungefähr 2 ns begrenzt ist, was etwa 500 MHz entspricht. Das ist eine sehr strenge Anforderung. Da aber im Datenpfad nur eine LUT liegt und beide Flipflops in derselben Slice untergebracht sind, war dieses Ziel leicht zu erreichen. Dieses Beispiel zeigt jedoch, warum es wichtig ist, für zusammenhängende Takte Frequenzen zu wählen, die gut zusammenpassen. Oft wird die Frequenz des einen Takts als ganzzahliges Vielfaches der Frequenz des anderen Takts gewählt. So werden Szenarien wie der 2-ns-Abstand in diesem Beispiel vermieden.
Wenn es übrigens unmöglich ist, gut zusammenpassende Frequenzen zu wählen, besteht die Lösung darin, die Takte als unabhängige Takte (unrelated clocks) zu behandeln.
Die abgeleiteten Takte
Ich habe weiter oben erwähnt, dass clk_out1_clk_wiz_1 und clk_out2_clk_wiz_1 theoretische Takte sind. Im Timing-Report zeigt sich das folgendermaßen: Beachten Sie, dass beide Pfade mit dem externen Pin (AG12) als Ausgangspunkt berechnet werden. Wie kann das sinnvoll sein? In Wirklichkeit ist das Signal an diesem Pin der Referenztakt und keiner dieser beiden Takte.
Nehmen wir also clk_out1_clk_wiz_1 als Beispiel: Die Idee hinter der Berechnung ist, so zu tun, als gäbe es einen Takt mit 125 MHz am externen Pin. In Wirklichkeit existiert ein Takt mit 125 MHz nur am Ausgang der PLL, also am Ausgang von MMCME3_ADV_X1Y0. Statt jedoch die Frequenzumsetzung der PLL in die Timing-Berechnung einzubeziehen, wird ein theoretischer Takt verwendet. Dieser Takt hat eine ideale Signalform, die bei 0 ns beginnt. Die PLL wird so behandelt, als würde sie die Frequenz keines Takts ändern, sondern nur Verzögerungen hinzufügen.
Der theoretische Takt (z.B. clk_out1_clk_wiz_1) definiert also die Signalform des Takts (Frequenz, Tastverhältnis, Jitter usw.). Dieser theoretische Takt definiert jedoch nicht die Timing-Beziehungen zu anderen Takten, die als reale Signale auf dem FPGA existieren.
Wie können wir nun erkennen, dass @pll_clk_6 und @pll_clk_8 tatsächlich ausgerichtet sind? Die Antwort liegt im Vergleich zwischen dem tatsächlichen Timing und dem idealen Timing dieser Takte. Die theoretische Taktflanke von clk_out1_clk_wiz_1 liegt in der obigen Berechnung beispielsweise bei 16 ns. Die Taktflanke von clk_out2_clk_wiz_1 liegt dagegen bei 18 ns, also 2 ns später. Nun vergleichen wir die Taktflanken an den Ausgängen des globalen Taktbaums: Laut Timing-Report kommt die erste Taktflanke bei 15,400 ns an. Für die zweite Taktflanke sagt der Report 17,319 ns. Laut Berechnung beträgt der Zeitunterschied also 1,919 ns statt 2 ns. Das ist nur 0,081 ns weniger als die ideale Differenz. Die Takte sind also definitiv ausgerichtet.
Vergleichen wir das mit dem vorherigen Beispiel mit nur einem Takt: Der ideale Zeitabstand zwischen zwei Taktflanken war die Taktperiode, also 8 ns. Laut dem entsprechenden Timing-Report (siehe oben) kam die erste Taktflanke jedoch bei −0,616 ns an und die zweite bei 7,285 ns. Der ideale Zeitabstand war 8 ns, tatsächlich waren es 7,901 ns. Die zweite Taktflanke kam also 0,099 ns früher als erwartet.
Bei zwei zusammenhängenden Takten beträgt die Abweichung vom idealen Zeitabstand also 0,081 ns. Bei nur einem Takt war diese Abweichung mit 0,099 ns ungefähr genauso groß. In beiden Fällen liegt der Grund darin, dass die Berechnung der ersten Taktflanke mit maximalen Verzögerungen erfolgt und für die zweite Taktflanke minimale Verzögerungen verwendet werden.
Die Schlussfolgerung ist also, dass @pll_clk_6 und @pll_clk_8 mit etwa derselben Genauigkeit ausgerichtet sind, als wären sie ein einziger Takt.
Man beachte, dass diese Takte auch ohne die Option zur Phasenausrichtung der PLL gegenseitig ausgerichtet wären: Die Ausgänge einer PLL sind ohnehin normalerweise ausgerichtet. Diese Ausrichtung wird durch globale Taktpuffer erreicht, die nahezu gleiche Verzögerungen haben, unabhängig von ihrem Fan-out. Mit anderen Worten: Es spielt keine Rolle, mit wie vielen Logikelementen jeder dieser Taktpuffer verbunden ist; die Verzögerung vom PLL-Ausgang zum Ziel ist ungefähr dieselbe.
Was wir über zusammenhängende Takte gelernt haben
Die Analyse dieses Timing-Reports zeigt, dass ein Pfad zwischen zwei zusammenhängenden Taktdomänen in etwa einem Pfad innerhalb einer Taktdomäne entspricht. Ganz genau ist es aber nicht dasselbe. Insbesondere ist die Clock-Uncertainty von 0,062 ns auf 0,182 ns gestiegen, weil jeder Takt seinen eigenen Jitter hat und auch die Ausrichtung nicht perfekt ist.
Dieser Timing-Report hat auch gezeigt, wie sich die Ausrichtung der beiden Takte in den Timing-Berechnungen niederschlägt.
Wenn es in einem FPGA-Design Taktdomänenübergänge (clock domain crossings) gibt, ist es eine gute Idee, die Wechselwirkungen zwischen diesen Takten im Timing-Report zu untersuchen. Ziel ist es sicherzustellen, dass die Tools die Takte so behandeln, wie wir es erwarten. Die beiden wichtigsten Punkte sind:
- Wenn das Design zwei Takte als zusammenhängende Takte betrachtet: Prüfen Sie, ob für alle Pfade zwischen diesen Takten Timing-Berechnungen durchgeführt werden.
- Wenn das Design zwei Takte als unabhängige Takte betrachtet: Prüfen Sie, dass für Pfade zwischen diesen beiden Takten keine Timing-Berechnungen durchgeführt werden.
Vivado kann einen Clock Interaction Report erzeugen, der die Taktdomänenübergänge des Designs grafisch darstellt und zeigt, wie jeder dieser Übergänge von den Tools behandelt wird. Andere FPGA-Tools bieten eine ähnliche Funktion, z.B. der CDC Viewer von Quartus Pro. Wenn möglich, sollte man diesen Report erzeugen und untersuchen.
Ein weiterer Punkt ist, ob die Takte ausgerichtet sind. Wenn das Design versehentlich einen nicht ausgerichteten Takt verwendet, kann das unnötige Schwierigkeiten beim Erfüllen der Timing-Vorgaben verursachen. Wenn es zum Beispiel einen Pfad zwischen @clk und @pll_clk_8 gibt, werden die Tools die Timing-Vorgaben durchsetzen und damit sicherstellen, dass dieser Pfad zuverlässig arbeitet. Man beachte jedoch, dass @clk der Referenztakt ist, der in die PLL geht, und @pll_clk_8 der Ausgang dieser PLL. Diese beiden Takte sind also nicht ausgerichtet. Infolgedessen arbeiten die Tools unter Umständen unnötig hart, um die Timing-Vorgaben für diesen Pfad zu erfüllen. Andere Pfade können wegen dieses unnötigen Aufwands ihre Timing-Vorgaben verfehlen.
Und noch einmal: Das Thema Taktdomänen wird in dieser Seitenreihe behandelt.
Auch thold ist wichtig
Alle Timing-Berechnungen, die ich bisher gezeigt habe, betrafen die tsu-Anforderung. Es ist naheliegend, sich auf diese Anforderung zu konzentrieren, denn wenn die Tools die Timing-Vorgaben nicht erfüllen, liegt das fast immer daran, dass mindestens ein Pfad die tsu-Anforderung nicht erfüllt hat.
Es ist dennoch wichtig, thold im Hinterkopf zu behalten: Um diese Anforderung zu erfüllen, verlangsamen die Tools den Datenpfad manchmal künstlich, indem sie zusätzliche Routing-Verzögerung einfügen. Auch wenn die thold-Anforderung selten als Grund für das Verfehlen der Timing-Vorgaben genannt wird, kann sie der verborgene Grund für das Scheitern sein.
Tatsächlich ist etwas Ähnliches mit der Routing-Verzögerung der Leitung passiert, die die beiden Flipflops verbindet: In allen Timing-Reports mit nur einem Takt lag diese Leitung innerhalb derselben Slice (SLICE_X49Y58). Daher betrug die Verzögerung dieser Leitung in all diesen Reports 0,241 ns. Sobald jedoch zwei Takte an dem Pfad beteiligt waren, stieg diese Verzögerung auf 0,807 ns.
Die Erklärung ist, dass 0,241 ns die minimale Verzögerung ist, die zwischen zwei Flipflops in derselben Slice erreicht werden kann. Die längere Verzögerung (0,807 ns) ist das Ergebnis eines anderen Routings dieser Leitung. Das ist kein Zufall: Die Tools haben diese extra lange Verdrahtung absichtlich herbeigeführt, um die thold-Anforderung zu erfüllen. Das spiegelt sich auch in der Zeile „Data Path Delay“ im Report wider: Der Logikanteil beträgt nur 26,5 %, der Rest ist Routing. Allein das ist ein Zeichen, dass etwas passiert ist. Mehr dazu weiter unten.
Analyse der thold-Anforderung
Zur Erinnerung: Auf der theoretischen Seite dieser Serie wurde erklärt, dass thold die Zeitspanne ist, während der der Dateneingang des zweiten Flipflops nach der Taktflanke stabil bleiben muss. Beschreiben wir die Situation, in der thold verletzt wird: Eine Taktflanke erreicht das erste Flipflop, und das zweite Flipflop erhält etwa zur gleichen Zeit ebenfalls eine Taktflanke (diese Taktflanken können vom selben Takt oder von verschiedenen Takten stammen). Als Reaktion auf seine Taktflanke aktualisiert das erste Flipflop seinen Ausgang nach einer Verzögerung (Takt-zu-Ausgang-Verzögerung, clock-to-output). Der aktualisierte Wert erreicht das zweite Flipflop jedoch zu früh. Dadurch kann dieses Flipflop den vorherigen Wert nicht zuverlässig abtasten: Der neue Wert kam an, bevor das zweite Flipflop Zeit hatte, seine Reaktion auf die Taktflanke abzuschließen. Mit anderen Worten: Die thold-Anforderung ist verletzt.
Wenn beide Flipflops mit demselben Takt betrieben werden, kann das vor allem wegen eines Taktversatzes (clock skew) passieren: Wenn die Taktflanke früher am ersten Flipflop ankommt, ist es möglich, dass das erste Flipflop seinen Ausgang zu früh ändert. Dadurch besteht die Möglichkeit, dass das aktualisierte Signal das zweite Flipflop schnell genug erreicht, um die thold-Anforderung zu verletzen. Man beachte, dass es dieselbe Taktflanke ist, die beide Flipflops erreicht. Weder die Taktfrequenz noch die Höhe des Taktjitters spielen also eine Rolle. Nur eine unterschiedliche Verzögerung – also der Taktversatz (clock skew) – ist von Bedeutung.
Für die folgende Timing-Analyse bleiben wir jedoch beim letzten Beispiel mit zwei Takten: @pll_clk_8 und @pll_clk_6. Eine Timing-Analyse mit nur einem Takt wäre ähnlich, aber nicht so interessant, weil es mit einem einzigen Takt zu leicht ist, die thold-Anforderung zu erfüllen.
Der Timing-Report, den wir uns also ansehen, ist für zwei zusammenhängende Takte erstellt. Diese beiden Takte haben unterschiedliche Frequenzen, und wie zuvor gibt es verschiedene Kombinationen zwischen den Zeitpunkten der Taktflanken des ersten und des zweiten Takts. Im Gegensatz zur Berechnung für tsu ist der schlechteste Fall für thold der, in dem beide Taktflanken bei 0 ns liegen: thold-Verletzungen treten auf, wenn die beiden Taktflanken nahezu gleichzeitig kommen. Keine andere Kombination ist also schlechter.
Mit diesen Erkenntnissen im Hinterkopf sehen wir uns nun den Timing-Report an:
Slack (MET) : 0.093ns (arrival time - required time) Source: foo_reg_reg/C (rising edge-triggered cell FDRE clocked by clk_out1_clk_wiz_1 {rise@0.000ns fall@4.000ns period=8.000ns}) Destination: bar_reg__0/D (rising edge-triggered cell FDRE clocked by clk_out2_clk_wiz_1 {rise@0.000ns fall@3.000ns period=6.000ns}) Path Group: clk_out2_clk_wiz_1 Path Type: Hold (Min at Fast Process Corner) Requirement: 0.000ns (clk_out2_clk_wiz_1 rise@0.000ns - clk_out1_clk_wiz_1 rise@0.000ns) Data Path Delay: 0.458ns (logic 0.104ns (22.707%) route 0.354ns (77.293%)) Logic Levels: 1 (LUT1=1) Clock Path Skew: 0.127ns (DCD - SCD - CPR) Destination Clock Delay (DCD): -0.542ns Source Clock Delay (SCD): -0.248ns Clock Pessimism Removal (CPR): -0.421ns Clock Uncertainty: 0.182ns ((TSJ^2 + DJ^2)^1/2) / 2 + PE Total System Jitter (TSJ): 0.071ns Discrete Jitter (DJ): 0.103ns Phase Error (PE): 0.120ns Clock Net Delay (Source): 0.495ns (routing 0.002ns, distribution 0.493ns) Clock Net Delay (Destination): 0.576ns (routing 0.002ns, distribution 0.574ns) Location Delay type Incr(ns) Path(ns) Netlist Resource(s) ------------------------------------------------------------------- ------------------- (clock clk_out1_clk_wiz_1 rise edge) 0.000 0.000 r AG12 0.000 0.000 r clk (IN) net (fo=0) 0.000 0.000 pll_i/inst/clkin1_ibuf/I AG12 INBUF (Prop_INBUF_HRIO_PAD_O) 0.339 0.339 r pll_i/inst/clkin1_ibuf/INBUF_INST/O net (fo=1, routed) 0.025 0.364 pll_i/inst/clkin1_ibuf/OUT AG12 IBUFCTRL (Prop_IBUFCTRL_HRIO_I_O) 0.015 0.379 r pll_i/inst/clkin1_ibuf/IBUFCTRL_INST/O net (fo=1, routed) 0.405 0.784 pll_i/inst/clk_in1_clk_wiz_1 MMCME3_ADV_X1Y0 MMCME3_ADV (Prop_MMCME3_ADV_CLKIN1_CLKOUT0) -1.721 -0.937 r pll_i/inst/mmcme3_adv_inst/CLKOUT0 net (fo=1, routed) 0.167 -0.770 pll_i/inst/clk_out1_clk_wiz_1 BUFGCE_X1Y1 BUFGCE (Prop_BUFCE_BUFGCE_I_O) 0.027 -0.743 r pll_i/inst/clkout1_buf/O X2Y0 (CLOCK_ROOT) net (fo=1, routed) 0.495 -0.248 pll_clk_8 SLICE_X49Y58 FDRE r foo_reg_reg/C ------------------------------------------------------------------- ------------------- SLICE_X49Y58 FDRE (Prop_EFF_SLICEL_C_Q) 0.049 -0.199 f foo_reg_reg/Q net (fo=1, routed) 0.343 0.144 foo_reg SLICE_X49Y58 LUT1 (Prop_D5LUT_SLICEL_I0_O) 0.055 0.199 r bar__0_i_1/O net (fo=1, routed) 0.011 0.210 p_0_in SLICE_X49Y58 FDRE r bar_reg__0/D ------------------------------------------------------------------- ------------------- (clock clk_out2_clk_wiz_1 rise edge) 0.000 0.000 r AG12 0.000 0.000 r clk (IN) net (fo=0) 0.000 0.000 pll_i/inst/clkin1_ibuf/I AG12 INBUF (Prop_INBUF_HRIO_PAD_O) 0.595 0.595 r pll_i/inst/clkin1_ibuf/INBUF_INST/O net (fo=1, routed) 0.042 0.637 pll_i/inst/clkin1_ibuf/OUT AG12 IBUFCTRL (Prop_IBUFCTRL_HRIO_I_O) 0.022 0.659 r pll_i/inst/clkin1_ibuf/IBUFCTRL_INST/O net (fo=1, routed) 0.457 1.116 pll_i/inst/clk_in1_clk_wiz_1 MMCME3_ADV_X1Y0 MMCME3_ADV (Prop_MMCME3_ADV_CLKIN1_CLKOUT1) -2.474 -1.358 r pll_i/inst/mmcme3_adv_inst/CLKOUT1 net (fo=1, routed) 0.209 -1.149 pll_i/inst/clk_out2_clk_wiz_1 BUFGCE_X1Y0 BUFGCE (Prop_BUFCE_BUFGCE_I_O) 0.031 -1.118 r pll_i/inst/clkout2_buf/O X2Y0 (CLOCK_ROOT) net (fo=2, routed) 0.576 -0.542 pll_clk_6 SLICE_X49Y58 FDRE r bar_reg__0/C clock pessimism 0.421 -0.121 clock uncertainty 0.182 0.061 SLICE_X49Y58 FDRE (Hold_DFF2_SLICEL_C_D) 0.056 0.117 bar_reg__0 ------------------------------------------------------------------- required time -0.117 arrival time 0.210 ------------------------------------------------------------------- slack 0.093
Zunächst gibt es einige offensichtliche Unterschiede: Der Pfadtyp (Path Type) ist Hold und nicht Setup wie zuvor. Das ist zu erwarten. Außerdem steht dort „Min at Fast Process Corner“, also das Gegenteil von dem, was beim Setup-Pfad stand: Diese Berechnung verwendet für den Datenpfad die minimalen Verzögerungen, nicht die maximalen. Das passt zu einer Worst-Case-Berechnung für die Situation, in der sich der Dateneingang zu früh ändert.
Die Requirement (Anforderung) beträgt 0 ns, was für einen Hold-Pfad typisch ist. Die Frequenzen der Takte spielen hier keine Rolle: Untersucht wird das Szenario, in dem beide Takte gleichzeitig eine steigende Flanke haben.
Was die Taktpfade betrifft, fällt auf, dass die Verzögerungen jeder Komponente im Quell-Taktpfad durchweg kürzer sind als die entsprechenden Verzögerungen im Taktpfad zum Ziel (und nicht länger). Auch das passt zum Zweck dieser Berechnung, denn der schlechteste Fall ist, wenn sich die Daten zu früh ändern, relativ zur Ankunft der Taktflanke am zweiten Flipflop. Die Multi-Corner-Timing-Analyse wurde auf der vorherigen Seite erklärt.
Das Wichtigste an diesem Timing-Report ist jedoch, dass die Zeitmarge (Slack) klein ist: nur 0,093 ns. Das ist oft ein Hinweis darauf, dass die Tools sich anstrengen mussten, um die Anforderung zu erfüllen. Eine kleine Zeitmarge bedeutet aber nicht unbedingt, dass es ein Problem gab, das gelöst werden musste.
Bei der Timing-Analyse für thold ist eine kleine Zeitmarge durchaus üblich. Warum ist dieser Pfad also verdächtig? In erster Linie wegen der größeren Routing-Verzögerung zwischen zwei Flipflops in derselben Slice (SLICE_X49Y58), wie oben erwähnt. Vermutlich hat die Software während der frühen Phasen von Platzierung und Verdrahtung (Place and Route) festgestellt, dass die thold-Anforderung zwischen diesen beiden Flipflops nicht erfüllt war. Wie wurde das korrigiert?
Wenn die thold-Anforderung nicht erfüllt wird, bedeutet das, dass das Datensignal relativ zur Taktflanke des zweiten Flipflops zu früh ankommt. Das wird korrigiert, indem dem Datenpfad künstlich Verzögerung hinzugefügt wird. Dadurch ändert der Dateneingang am zweiten Flipflop seinen Wert etwas später. Die Tools haben vermutlich die Verdrahtung zwischen den beiden Flipflops verlängert. Das erhöht die Routing-Verzögerung und löst das Problem mit thold. Eine solche Verzögerungsvergrößerung hätte ein Problem beim Erfüllen der tsu-Anforderung verursachen können, aber in diesem Fall gab es kein solches Problem: Die Timing-Vorgabe wurde für diesen Pfad erfüllt (d.h. sowohl die tsu- als auch die thold-Anforderung waren erfüllt).
Dennoch zeigt dieses Beispiel, wie die Notwendigkeit, ein thold-Problem zu lösen, ein scheinbar nicht damit zusammenhängendes tsu-Problem erzeugen kann. Das sollte man im Hinterkopf behalten, wenn das Verhältnis zwischen Logikverzögerung und Routing-Verzögerung eines Pfads sehr niedrig ist. Es gibt natürlich auch andere mögliche Gründe für eine große Routing-Verzögerung, insbesondere einen hohen Fan-out. Wenn der Fan-out jedoch niedrig ist (wie in diesem Fall, wo er 1 beträgt), lohnt es sich zu fragen, ob die große Routing-Verzögerung von den Tools absichtlich eingefügt wurde, um ein thold-Problem zu lösen.
Aber warum gab es nur im Fall mit zwei Takten ein Problem mit thold? Die Antwort ist, dass bei zwei Takten eine größere Unsicherheit darüber besteht, wann die Taktflanken an den beiden Flipflops ankommen. Mehrere Faktoren tragen zu dieser Unsicherheit bei, insbesondere Taktversatz (clock skew) und Jitter. Da die Berechnung für thold von 0 ns bis 0 ns erfolgt, reagiert sie empfindlicher auf kleine Unsicherheiten beim Eintreffen der Taktflanken.
Die mit zwei zusammenhängenden Takten verbundene Unsicherheit erfordert daher häufig Korrekturmaßnahmen, um die thold-Anforderung zu erfüllen. Diese Korrekturen nimmt natürlich automatisch das Tool vor. Es ist dennoch wichtig, diese Korrekturen zu kennen, wenn es Probleme beim Erfüllen der Timing-Vorgaben gibt.
Zusammenfassung
Die letzten beiden Seiten dieser Serie haben einige Beispiele für Timing-Reports gezeigt, die alle aus einer einzigen konkreten Timing-Vorgabe resultierten. Alle diese Timing-Reports bezogen sich außerdem auf ein konkretes und einfaches Logikbeispiel. Hoffentlich haben diese Beispiele dabei geholfen, ein Verständnis für die Grundlagen des Timings aufzubauen.
An dieser Stelle empfiehlt es sich, sich Timing-Reports des eigenen Designs anzusehen, um zu erkennen, wie dieselben Prinzipien auf Pfade mit mehr als einer LUT zutreffen.
Die nächste Seite beginnt mit der Besprechung von Timing-Problemen und deren Lösungen.