Diese Seite gehört zu einer Serie von Seiten über Timing. Die vorherigen Seiten haben die Theorie hinter Timing-Berechnungen erklärt, die Taktperioden-Vorgabe besprochen und die Prinzipien des Timing-Closure vorgestellt. Nun ist es an der Zeit, mit den technischen Details der Timing-Vorgaben (timing constraints) zu beginnen.
Die Bedeutung von create_clock als Tcl-Befehl
Auf den bisherigen Seiten drehte sich alles um diese Timing-Vorgabe:
create_clock -period 4 -name clk [get_ports clk]
Ich habe die Syntax dieser Zeile bewusst bisher nicht besprochen. Es ist also an der Zeit zu erklären, was das tatsächlich bedeutet.
Diese Timing-Vorgabe ist im SDC-Format (Synopsys Design Constraints) geschrieben, dem am weitesten verbreiteten Format für Timing-Vorgaben. Vivado und Quartus verwenden dieses Format, ebenso wie einige andere FPGA-Tools.
Eine SDC-Datei ist im Wesentlichen ein Skript (script), das in Tcl geschrieben ist. Der Inhalt einer SDC-Datei ist also ein kurzes Computerprogramm und nicht nur eine Sammlung von Informationen. Die Möglichkeiten einer SDC-Datei als Skript sind jedoch auf eine kleine Teilmenge von Befehlen beschränkt, die zum Schreiben von Vorgaben gedacht sind: Nicht alles, was in einem Tcl-Skript erlaubt ist, kann in einer SDC-Datei getan werden.
Der Befehl create_clock wird verwendet, um eine Timing-Vorgabe zu definieren. Tatsächlich weist dieser Befehl die FPGA-Tools jedoch an, ein neues Taktobjekt (clock object) zu erzeugen. Das Wort „Objekt“ bedeutet hier, was es in der Softwaretechnik üblicherweise bedeutet. Das neue Taktobjekt ist also etwas, das im Speicher des Tcl-Interpreters als Objekt mit eigenen Eigenschaften abgelegt ist.
Zum Beispiel setzt der Teil des create_clock-Befehls, der „-name clk“ lautet, die Eigenschaft namens „name“ auf den Wert „clk“. Zur Erinnerung von einer der vorherigen Seiten: Dieser Name wurde in den Timing-Reports verwendet. Der Name „clk“ erschien zusammen mit Timing-Pfaden (paths), die wegen dieser Vorgabe (oder genauer: wegen dieses Taktobjekts) berechnet wurden.
Später haben wir gesehen, dass es weitere Taktnamen gab, zum Beispiel clk_out1_clk_wiz_1 und clk_out2_clk_wiz_1. Das waren in Wirklichkeit Namen weiterer Taktobjekte, die automatisch von den Tools erzeugt wurden.
Es gibt einen Tcl-Befehl zum Auflisten aller Takte: get_clocks. Beim Beispiel mit zwei Takten von der vorherigen Seite sieht eine Sitzung auf Vivados Tcl-Konsole also so aus:
> get_clocks clk clkfbout_clk_wiz_1 clk_out1_clk_wiz_1 clk_out2_clk_wiz_1
get_clocks und ähnliche Befehle werden auf der nächsten Seite genauer erklärt.
Man kann sich auch die Eigenschaften dieser Objekte ansehen. Es ist nicht nötig, alle diese Eigenschaften zu verstehen; ich zeige das nur, um zu verdeutlichen, dass ein Takt ein Objekt ist. Ich persönlich hatte nie das Bedürfnis, eine Objekteigenschaft direkt zu verändern.
> report_property [get_clocks clk] Property Type Read-only Value CLASS string true clock INPUT_JITTER double true 0.040 IS_GENERATED bool true 0 IS_PROPAGATED bool true 1 IS_USER_GENERATED bool true 0 IS_VIRTUAL bool true 0 NAME string true clk PERIOD double true 4.000 SOURCE_PINS string* true clk SYSTEM_JITTER double true 0.050 WAVEFORM double* true 0.000 2.000 > report_property [get_clocks clk_out1_clk_wiz_1] Property Type Read-only Value CLASS string true clock EDGES int* true 1 2 3 EDGE_SHIFT double* true 0.000 2.000 4.000 INPUT_JITTER double true 0.000 IS_GENERATED bool true 1 IS_INVERTED bool true 0 IS_PROPAGATED bool true 1 IS_RENAMED bool true 0 IS_USER_GENERATED bool true 0 IS_VIRTUAL bool true 0 MASTER_CLOCK clock true clk NAME string true clk_out1_clk_wiz_1 PERIOD double true 8.000 SOURCE pin true pll_i/inst/mmcme3_adv_inst/CLKIN1 SOURCE_PINS string* true pll_i/inst/mmcme3_adv_inst/CLKOUT0 SYSTEM_JITTER double true 0.050 WAVEFORM double* true 0.000 4.000
Das Wichtige ist zu erkennen, dass create_clock nur ein Objekt erzeugt. Die Parameter dieses Befehls bestimmen lediglich, wie die Eigenschaften dieses Objekts gesetzt werden. Zum Beispiel bedeutet der Teil „-period 4“ (in der Timing-Vorgabe, die ich wiederholt gezeigt habe) nur, dass eine bestimmte Eigenschaft namens „PERIOD“ den Wert 4 haben soll.
Falls Sie diese Tcl-Befehle selbst ausprobieren möchten, beachten Sie, dass es Unterschiede zwischen den FPGA-Tools gibt.
In Vivado können diese Befehle erst nach dem Öffnen des Implemented Design verwendet werden.
In Quartus öffnen Sie zuerst den TimeQuest Timing Analyzer, und klicken Sie dann Create Timing Netlist, Read SDC File und Update Timing Netlist. Probieren Sie dann einige Befehle auf der Tcl-Konsole aus, zum Beispiel:
> join [ query_collection -all [ get_clocks ] ] "\n" > get_clock_info -waveform [get_clocks clk]
Die Bedeutung des Begriffs „clock“
Wenn FPGA-Tools das Wort „clock“ verwenden, meinen sie damit üblicherweise ein Taktobjekt und nicht das physische Signal im FPGA. Das gilt insbesondere für Timing-Reports.
Zur Erinnerung von der vorherigen Seite: Ich habe dort mehrfach den Begriff „theoretische Takte“ verwendet. Das sind in Wirklichkeit Taktobjekte. Sie werden im Timing-Report als „Takte“ bezeichnet, aber ihre Verwendung in der Timing-Analyse zeigt, dass sie nur Container für Informationen sind.
Was ist also die Verbindung zwischen diesen Taktobjekten und realen Signalen? Wir haben die Namen der Taktobjekte bereits in der Timing-Analyse gesehen. Wie passt das alles zusammen?
Wenn die Tools die statische Timing-Analyse des Designs durchführen, werden alle Pfade untersucht. Wenn ein Pfad an einem Flipflop beginnt, untersuchen die Tools das Signal (also das Netz), das mit dem Takteingang des Flipflops verbunden ist: Gibt es ein Taktobjekt, das zu diesem Signal gehört? Wenn das Signal zum Beispiel @clk ist, ist das zugehörige Taktobjekt dasjenige, dem mit dem Befehl create_clock der Name „clk“ gegeben wurde. Nachdem das zutreffende Taktobjekt gefunden wurde, können die Tools die nötigen Informationen aus den Eigenschaften dieses Objekts holen.
Dasselbe geschieht mit dem Flipflop am Ende des Pfads. Die Tools haben also nun die beiden Taktobjekte, die zu diesem Pfad gehören. Mit den Informationen aus diesen Objekten führen die Tools die Timing-Analyse durch.
Und natürlich gilt dasselbe Verfahren für jedes sequentielle Element, nicht nur für Flipflops.
Warum ist es wichtig, das zu verstehen? Unter anderem, weil im Timing-Report manchmal eine Fehlermeldung steht, dass es Register ohne Takt gibt. Das bedeutet normalerweise nicht, dass ein Flipflop vorhanden ist, an dessen Takteingang nichts angeschlossen ist. Vielmehr bedeutet es, dass die Tools kein Taktobjekt gefunden haben, das zu diesem Takteingang gehört. Mit anderen Worten: Die Tools haben keine Informationen über den Takteingang dieses Flipflops gefunden. Das Problem liegt also meist nicht im Logikdesign, sondern eine Timing-Vorgabe fehlt (oder ist falsch geschrieben).
Es ist noch einmal erwähnenswert: Wenn in einem Timing-Report von „clock“ die Rede ist, bedeutet das nicht, dass es im Logikdesign ein Signal mit diesem Namen gibt, sondern dass ein Taktobjekt mit diesem Namen erzeugt wurde. Woher wissen wir, zu welchem Signal es gehört? Das ist das nächste Thema.
Zu welchem Signal gehört dieser Takt?
Eine der Dinge, die den Timing-Report schwer lesbar machen, sind die Namen der Takte. Die meisten Taktsignale in einem Logikdesign werden von einer PLL erzeugt, und wir haben bereits gesehen, dass der Name im Timing-Report wenig hilfreich sein kann. Die meisten FPGA-Tools erlauben es, die Taktobjekte durch zusätzliche Befehle in der SDC-Datei umzubenennen, aber in den meisten Projekten wird das nicht getan. Und Goldene Regel #4 besagt, Dinge zu vermeiden, die nur in Ihrem Projekt speziell sind.
Das Problem mit den Namen wird noch schwieriger, wenn die Quelle des Takts ein IP-Core (IP core) ist (z.B. ein Gigabit-Transceiver, ein PCIe-Block oder ein On-Chip-Prozessorkern). In diesem Fall sagt der Name des Takts oft nur wenig darüber aus, woher er stammt und womit er zusammenhängt.
Wie wird dieses Problem also gelöst? Beginnen wir mit der einfachsten Situation, wenn der Taktname aus dem create_clock-Befehl in unserer eigenen SDC-Datei stammt. Es ist wieder dieselbe Timing-Vorgabe:
create_clock -period 4 -name clk [get_ports clk]
Der letzte Teil dieses Befehls ist „[get_ports clk]“. In der Tcl-Sprache bedeuten eckige Klammern, dass der Inhalt der Klammern als Tcl-Befehl ausgeführt und das Ergebnis dieses Befehls anstelle der Klammern verwendet wird.
Der Befehl get_ports findet einen I/O-Port mit dem Namen „clk“. Das Ergebnis dieses Befehls ist das Objekt, das diesen Port repräsentiert. In dem obigen create_clock-Befehl ist dieses Objekt also ein Argument dieses Befehls. Auf diese Weise stellt create_clock die Verbindung zwischen dem Taktobjekt und einem realen Signal her.
Beachten Sie, dass sowohl der Name des Ports als auch der Name des Objekts „clk“ lauten. Es ist nicht erforderlich, dass sie gleich sind, aber es ist empfehlenswert: Der Name des Objekts erscheint in den Timing-Reports. Der Name des Ports ist daher meist die beste Wahl.
Es ist auch möglich, Netzobjekte (net objects) und Pinobjekte (pin objects) als Kennung eines Signals zu verwenden. Das ist bei Timing-Vorgaben üblich, die automatisch im Namen von IP-Cores erzeugt werden. Wenn Sie jedoch das Bedürfnis verspüren, das in Ihren eigenen Vorgaben zu tun, ist die Wahrscheinlichkeit groß, dass Sie etwas falsch machen.
Wenn also in einer SDC-Datei ein create_clock-Befehl verwendet wurde, lässt sich leicht sagen, zu welchem Signal das Taktobjekt gehört. Aber was ist mit Taktobjekten, die automatisch von den Tools erzeugt wurden?
In diesem Fall erkennt man den Takt am besten im Timing-Report. Welches Taktobjekt gehört zum Beispiel zu @pll_clk_8 im Beispiel von der vorherigen Seite? Eine einfache Methode ist, im Timing-Report eine Textsuche durchzuführen. Bei der Suche nach „pll_clk_8“ findet man den folgenden Ausschnitt:
Location Delay type Incr(ns) Path(ns) Netlist Resource(s)
-------------------------------------------------------------- -------------------
(clock clk_out1_clk_wiz_1 rise edge)
16.000 16.000
AG12 0.000 16.000 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 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 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 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 pll_i/inst/clkout1_buf/O
X2Y0 (CLOCK_ROOT) net (fo=1, routed) 1.369 15.400 pll_clk_8
SLICE_X49Y58 FDRE foo_reg_reg/C
Das ist der Quell-Taktpfad für clk_out1_clk_wiz_1, und dort steht die Antwort auf die Frage.
Eine andere Methode ist, die Information mit einem Tcl-Befehl zu beschaffen. Wie das genau funktioniert, ist von FPGA-Tool zu FPGA-Tool unterschiedlich. In Vivado kann nach dem Öffnen des Implemented Design etwa der folgende Befehl verwendet werden:
> get_clocks -of_objects [ get_nets pll_clk_8 ] clk_out1_clk_wiz_1
Diese Methode erfordert, dass man den Namen des Netzes kennt. Manchmal ist das so einfach wie in diesem Beispiel, manchmal muss man den Namen dieses Netzes erst herausfinden. Die FPGA-Tools bieten dafür üblicherweise eine Möglichkeit in der grafischen Oberfläche (GUI). Es ist auch möglich, dafür Tcl-Befehle zu verwenden.
Ich hoffe, die wenigen Tcl-Beispiele haben Sie davon überzeugt, wie wichtig es ist, richtig mit Tcl umgehen zu können. Genau darum geht es auf der nächsten Seite.
Die Bedeutung von get_port
Im obigen Beispiel stützt sich der create_clock-Befehl auf get_port, um die Verbindung zwischen dem Taktobjekt und einem physischen Eingangspin herzustellen. Wie oben erwähnt, ist diese Verbindung notwendig, um zu wissen, welche Logikelemente an diesem Takt angeschlossen sind (oder an Takten, die daraus erzeugt werden).
Aber die Verwendung von get_port ist nicht die einzige Möglichkeit. Man kann sich zum Beispiel auch auf den Ausgangspin eines globalen Taktpuffers beziehen. Zum Beispiel so:
create_clock -name clk -period 4 [get_pins my_BUFG_inst/O]
Der Unterschied besteht darin, dass die Tools den Ausgangspin des globalen Taktpuffers als Ursprung des Takts betrachten. Mit anderen Worten: Die Berechnung der Taktpfade beginnt an dieser Position. Die erste Taktflanke an diesem Ursprung tritt bei 0 ns auf; dieser Ausgangspin wird also zur Zeitreferenz.
Das ist eine zulässige Timing-Vorgabe, aber sie hat zwei wichtige Nachteile:
- Die Tools müssen ausdrücklich angewiesen werden, diesen Takt im Verhältnis zu allen anderen Takten als unabhängigen Takt (unrelated clock) zu betrachten. Das gilt sogar für Takte, die mit derselben PLL erzeugt werden. Der Grund ist, dass die Tools den Ausgangspin als Zeitreferenz ansehen. Unterschiede zwischen den Takten hinsichtlich ihrer Taktpfadverzögerungen werden also nicht ausgeglichen – Taktversätze (clock skews) bleiben also unberücksichtigt.
- Es ist unmöglich (oder sehr schwierig), I/O-Timing-Vorgaben zu definieren, die sich auf einen Takt beziehen, der außerhalb des FPGAs sichtbar ist. Der Grund ist, dass solche Vorgaben ein Taktobjekt als Zeitreferenz benötigen. Aber die Zeitreferenz ist eben der Ausgangspin des globalen Taktpuffers. Der Taktversatz (clock skew) vom externen Takt zum globalen Taktpuffer ist unbekannt (er ändert sich zum Beispiel mit der Temperatur).
Daher sollte nach Möglichkeit immer get_port verwendet werden. Andernfalls sollte das Timing des Takts im Verhältnis zu allem außer sich selbst als unbekannt betrachtet werden.
Auf dieser Seite wurden viele Tcl-Befehle gezeigt, aber nicht ausreichend erklärt. Die nächste Seite füllt diese Lücke.