01signal.com

Timing-Vorgaben und Taktdomänenübergang

Diese Seite gehört zu einer Serie von Seiten über Timing. Auf den vorherigen Seiten wurden die Theorie hinter Timing-Berechnungen erklärt, das Schreiben mehrerer Timing-Vorgaben (timing constraints) gezeigt und die Prinzipien des Timing-Closure besprochen. Diese Seite erklärt, wie man Timing-Vorgaben formuliert, die mit Taktdomänen (clock domains) zusammenhängen.

Einführung

Mit dem Wissen darüber, wie man bestimmte Logikelemente findet und wie man Pfade (paths) definiert, können wir uns nun den Timing-Vorgaben zuwenden, die von diesem Wissen Gebrauch machen.

Zunächst betrachten wir die Beziehung zwischen Timing-Vorgaben und Taktdomänen (clock domains). Diese beiden Themen sind eng miteinander verknüpft, und man kann das eine nicht besprechen, ohne das andere einzubeziehen. Ich empfehle daher, die Serie von Seiten über Taktdomänen zu lesen, falls Ihnen dieses Thema neu ist. Unter anderem ist das notwendig, um die Terminologie zu verstehen, die ich im Folgenden verwenden werde.

Die Logik definiert die Beziehung zwischen den Takten

Sehen wir uns dieses Beispiel für Verilog-Code an:

module top(
    input clk,
    input foo,
    output reg bar_reg,
    output reg baz
);
    reg foo_reg;
    reg bar;
    reg baz_metaguard;
    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

always @(posedge clk)
  begin
    baz_metaguard <= bar;
    baz <= baz_metaguard;
  end

Dies ist fast dasselbe Beispiel mit einer PLL wie wir es bereits weiter oben gesehen haben. Der Unterschied besteht darin, dass ein neuer Taktdomänenübergang (clock domain crossing) hinzugekommen ist: Der Wert von @bar wird über ein Metastabilitäts-Schutzregister nach @baz kopiert.

In diesem Beispiel gibt es also zwei Arten von Taktdomänenübergängen:

Bisher habe ich nur ein Kriterium zur Bestimmung der Art des Taktdomänenübergangs betrachtet: das Vorhandensein oder das Fehlen eines Metastabilitäts-Schutzregisters. Wenn die Logik unabhängige Takte erwartet, verwendet sie die notwendigen Sicherheitsmechanismen. Folglich ist alles andere unerheblich: Selbst wenn es möglich wäre, die Timing-Anforderungen auf den Pfaden zwischen diesen Takten zu erfüllen, gibt es keinen Grund dafür.

Und umgekehrt: Erwartet die Logik zusammenhängende Takte, gibt es keinen Schutz gegen Timing-Verletzungen. Daher müssen die Timing-Anforderungen auf allen Pfaden zwischen diesen Takten erfüllt werden.

Zusammenfassend trifft die Logik also immer Annahmen darüber, welche Takte zusammenhängende Takte sind und welche nicht. Diese Annahmen zeigen sich im Vorhandensein oder Fehlen von Schutzmechanismen. Aber die FPGA-Tools sind dafür verantwortlich, dass diese Annahmen Wirklichkeit werden. Insbesondere stellen die Tools sicher, dass die Timing-Anforderungen auf Pfaden zwischen zusammenhängenden Takten erfüllt werden.

Wir müssen also dafür sorgen, dass die Tools die Annahmen der Logik über die Takte und ihre Beziehungen kennen.

Beachten Sie, dass die Tools im obigen Beispiel keine Möglichkeit haben, die Erwartungen der Logik bezüglich @pll_clk_6 und @clk zu kennen: Die Logik könnte annehmen, dass es zusammenhängende Takte sind, oder das Gegenteil. Tatsächlich habe ich bereits weiter oben ein ähnliches Beispiel verwendet (siehe „Idee #9“), um einen Pfad zwischen zusammenhängenden Takten zu demonstrieren. Im obigen Beispiel werden diese beiden Takte als unabhängige Takte behandelt.

Vielleicht könnten die Tools aus dem Namen @baz_metaguard einen intelligenten Schluss ziehen. Vielleicht würde die typische Struktur eines Metastabilitäts-Schutzregisters einen Hinweis geben. Aber das reicht nicht, um eine so wichtige Entscheidung zu treffen.

Den Tools die Takte mitteilen

In den bisherigen Beispielen gab es nur eine einzige Timing-Vorgabe:

create_clock -period 4.000 -name clk [get_ports clk]

Die erste Frage, die sich stellt, ist, wie die Tools den Taktdomänenübergang von @pll_clk_6 nach @clk standardmäßig behandeln. Wenn dies die einzige Timing-Vorgabe ist, werden die Tools dann auf dem Pfad zwischen @bar und @baz_metaguard irgendetwas durchsetzen?

Die Antwort lautet: Das hängt vom verwendeten FPGA-Tool ab. Auch wenn praktisch alle FPGA-Tools @pll_clk_6 und @pll_clk_8 als zusammenhängende Takte betrachten, ist nicht ohne Weiteres klar, was mit anderen Taktpaaren geschieht. Normalerweise behandeln die Tools standardmäßig alle Takte als zusammenhängend, aber darauf sollte man sich nicht verlassen.

Viele FPGA-Tools unterstützen den Befehl set_clock_groups. Das ist die beste Möglichkeit, die Beziehung zwischen Takten zu definieren. Wegen der Bedeutung dieses Befehls ist es eine gute Idee, vor der Verwendung im Design die Dokumentation des Tools zu lesen.

Für das obige Beispiel könnte der Befehl in Vivado zum Beispiel so verwendet werden (bei anderen Tools geht es ähnlich):

set_clock_groups -asynchronous \
  -group [list \
     [get_clocks -of_objects [get_pins pll_i/clk_out1] ] \
     [get_clocks -of_objects [get_pins pll_i/clk_out2] ] ] \
  -group [get_clocks -of_objects [ get_pins pll_i/clk_in1] ]

Die Verwendung von get_clocks und get_pins wurde bereits erklärt: Jeder get_clocks-Befehl findet ein Taktobjekt anhand des Namens des betreffenden Ports von clk_wiz_1 (beachten Sie, dass die Instanziierung von clk_wiz_1 den Namen pll_i trägt). Der letzte get_clocks-Befehl holt beispielsweise das Taktobjekt für @clk.

Dieses Beispiel zeigt, wie set_clock_groups Gruppen von Takten definiert. In diesem Fall besteht eine Gruppe aus @pll_clk_6 und @pll_clk_8, die zweite Gruppe nur aus @clk. Diese Vorgabe teilt den Tools mit, dass alle Takte in derselben Gruppe zusammenhängende Takte sind. Gehören zwei Takte zu verschiedenen Gruppen, betrachten die Tools sie als unabhängige Takte.

Mit anderen Worten: Die Tools setzen also genau dann Timing-Vorgaben auf einem Pfad durch, wenn die Takte auf beiden Seiten des Pfads zur selben Gruppe gehören.

In einem Design sind mehrere set_clock_groups-Befehle erlaubt. Es ist aber am besten, einen einzigen set_clock_groups-Befehl zu verwenden, um alle Takte in Gruppen einzuteilen. Das vermeidet Widersprüche und hilft auch, Verwirrung zu verhindern: Der Hauptvorteil dieses Befehls liegt darin, dass er die Beziehungen zwischen den Takten prägnant beschreibt. Kurz, prägnant und mathematisch klar. Genau so wollen wir es haben.

Beachten Sie, dass set_clock_groups nach allen create_clock-Befehlen verwendet werden sollte, weil die genannten Taktobjekte dann bereits existieren müssen.

Inkonsistente Beziehungen zwischen Takten

Wenn die Takte an manchen Stellen im Design als zusammenhängende Takte gelten und an anderen als unabhängige Takte, kann set_clock_groups nicht verwendet werden.

@clk und @pll_clk_6 werden im obigen Verilog-Code als unabhängige Takte behandelt. Theoretisch könnte es aber zusätzliche Logik geben, die sie als zusammenhängende Takte behandelt. Auch wenn das keine gute Idee ist, ist es möglich, für diese zusätzliche Logik Timing-Vorgaben zwischen @pll_clk_6 und @clk durchzusetzen.

Wenn set_clock_groups aus diesem Grund nicht verwendet werden kann, fragen Sie sich zuerst, ob Sie nicht die Logik so ändern können, dass set_clock_groups verwendet wird. Nicht nur, weil dieser Befehl so gut ist: Ohne eine einfache Regel, welche Takte zusammenhängend sind und welche nicht, sind Fehler bei Taktdomänenübergängen leicht möglich. Wenn Sie die Logik trotzdem nicht ändern möchten, besteht die Lösung darin, False Paths (false paths) zu definieren.

Kurz zum Befehl set_false_path

Es gibt zwei Hauptbefehle zum Deklarieren von False Paths: set_clock_groups und set_false_path.

Der Befehl set_clock_groups wurde oben vorgestellt: Jeder Pfad zwischen zwei Takten aus verschiedenen Gruppen wird als False Path betrachtet. Aber set_clock_groups kann nicht immer für alle Pfade verwendet werden, die als False Path deklariert werden sollen. In manchen Fällen muss die Auswahl der Pfade genauer sein, als es die Definition von Taktgruppen erlaubt. Der Befehl set_false_path löst dieses Problem, weil er eine gezielte Auswahl von Pfaden ermöglicht.

Ersetzen wir zum Beispiel den set_clock_groups-Befehl von oben durch einen set_false_path-Befehl. Das Einzige, was korrigiert werden muss, ist der Pfad von @bar nach @baz_metaguard. Die Timing-Anforderungen für alle anderen Pfade werden bereits so, wie sie sind, korrekt durchgesetzt. So lässt sich der set_false_path-Befehl für diesen Pfad schreiben. Aber nehmen Sie sich dieses Beispiel nicht zum Vorbild:

set_false_path -from [get_cells bar_reg__0] -to [get_cells baz_metaguard_reg]

Dieser Befehl verwendet get_cells, um Anfang und Ende des Pfads auszuwählen. Dafür muss man die Namen der betreffenden Cell-Objekte kennen. In diesem Fall haben wir den Namen „bar_reg__0“, der das Problem der Namensabhängigkeit zeigt: Dieser Name hätte „bar_reg“ lauten sollen, wurde aber wie bereits erklärt wegen eines Zufalls zu „bar_reg__0“.

Dieses Beispiel zeigt also eines der Probleme von set_false_path: Für die detaillierte Auswahl von Logikelementen im Design muss man sich oft auf Namen von Objekten verlassen. Dieses Problem und mögliche Lösungen wurden bereits besprochen.

Man kann den Befehl vereinfachen: Da @baz_metaguard ein Metastabilitäts-Schutzregister ist, spielt es keine Rolle, woher die Pfade zu diesem Register kommen. Warum also nicht alle Pfade zu diesem Register ignorieren?

set_false_path -to [get_cells baz_metaguard_reg]

Die Wirkung dieses Befehls ist exakt dieselbe.

set_false_path statt set_clock_groups

Eine andere Möglichkeit ist, die Pfade über die Takte zu definieren. Tatsächlich kann der obige set_clock_groups-Befehl durch diese Vorgaben ersetzt werden:

set_false_path -from [get_clocks -of_objects [get_pins pll_i/clk_out1]] \
               -to [get_clocks -of_objects [ get_pins pll_i/clk_in1] ]

set_false_path -from [get_clocks -of_objects [get_pins pll_i/clk_out2] ] \
               -to [get_clocks -of_objects [ get_pins pll_i/clk_in1] ]

set_false_path -from [get_clocks -of_objects [ get_pins pll_i/clk_in1] ] \
               -to [get_clocks -of_objects [get_pins pll_i/clk_out1] ]

set_false_path -from [get_clocks -of_objects [ get_pins pll_i/clk_in1] ] \
               -to [get_clocks -of_objects [get_pins pll_i/clk_out2] ]

Das ist der lange und detaillierte Weg, dieselben False Paths zu erzeugen. Statt die Takte in Gruppen einzuteilen, gibt es für jedes Paar unabhängiger Takte zwei set_false_path-Befehle: je einen für jede Richtung. Es ist offensichtlich, wie leicht man sich bei so vielen Vorgaben vertun kann. Und das ist nur ein einfaches Beispiel mit drei Takten.

Dennoch wird set_false_path auf diese Weise oft anstelle von set_clock_groups verwendet. Das liegt höchstwahrscheinlich daran, dass jemand die Timing-Vorgaben von irgendwoher kopiert hat.

Ein Beispiel dafür, wie ein Fehler passieren kann

Sehen wir uns ein einfaches Beispiel für einen möglichen Fehler an: Aus der obigen Diskussion geht hervor, dass nur ein Pfad als False Path deklariert werden muss, nämlich der von @bar nach @baz_metaguard. Daher hat von den vier set_false_path-Befehlen im letzten Beispiel nur dieser eine Bedeutung:

set_false_path -from [get_clocks -of_objects [get_pins pll_i/clk_out2] ] \
               -to [get_clocks -of_objects [ get_pins pll_i/clk_in1] ]

Die drei anderen set_false_path-Befehle betreffen keine Pfade. Aber Moment – wie wäre es, die Vorgabe noch weiter zu vereinfachen? Vielleicht sollen einfach alle Pfade, die bei @clk enden, False Paths sein? Wie wäre es damit?

set_false_path -to [get_clocks -of_objects [ get_pins pll_i/clk_in1] ]

Eigentlich kann, weil der create_clock-Befehl, der „clk“ definiert, direkt über dem set_false_path-Befehl steht, stattdessen Folgendes geschrieben werden:

set_false_path -to [get_clocks clk]

Diese Vorgabe ist kurz und elegant. Leider ist sie furchtbar falsch: Ich habe vorgeschlagen, dass alle Pfade, die bei @clk enden, als False Paths behandelt werden sollen. Aber was ist mit dem Pfad von @baz_metaguard nach @baz? Das ist ein Pfad von @clk nach @clk. Dieser Pfad darf natürlich auf keinen Fall ein False Path sein. Die beiden zuletzt gezeigten set_false_path-Befehle schließen jedoch alle Pfade ein, die bei @clk enden – auch wenn der Pfad bei @clk beginnt.

Fehler dieser Art passieren leicht, insbesondere wenn die False-Path-Vorgaben nicht mit der Präzision einer mathematischen Denkweise formuliert werden. Es kann helfen, spezielle Timing-Reports zu erstellen, die zeigen, welche Pfade False Paths sind und welche nicht.


Damit ist die praktische Besprechung von Timing-Vorgaben für Pfade innerhalb des FPGAs abgeschlossen. Die nächste Seite erklärt Multicycle-Pfade (multi-cycle paths), die ebenfalls zu diesem Thema gehören. Allerdings sind Multicycle-Pfad-Vorgaben normalerweise nicht zu empfehlen. Daher ist es in Ordnung, stattdessen zur Einführung in die I/O-Vorgaben zu springen.

Diese Seite wurde maschinell aus dem Englischen übersetzt. Im Zweifelsfall siehe den Originaltext.
Copyright © 2021-2026. All rights reserved. (dcc38493)