Hilfe gesucht, um Signale zu erklären, die mit einer höheren Frequenz als der Takt kommen, und wie man damit umgeht

Ich lerne Verilog und FPGA-Programmierung kennen. Also habe ich ein einfaches Modul geschrieben, das zwei Eingänge verarbeitet - Tastensignal und eine Uhr. Anfänglich leuchtet es zwei LEDs auf und wenn der Benutzer die Taste drückt und loslässt, schaltet es diese beiden aus und leuchtet zwei weitere auf. Hier ist mein bisheriger Code (Kritik ist sehr willkommen):

`timescale 1ns / 1ps

module led_flip_flop(input wire clk,
                     input wire button,
                     output wire [3:0] led);

   parameter state_on_idle = 3'b000;
   parameter state_on_push = 3'b001;
   parameter state_on_pop = 3'b010;
   parameter state_off_idle = 3'b011;
   parameter state_off_push = 3'b100;
   parameter state_off_pop = 3'b101;

   reg                                 led_sig;
   reg [2:0]                           state;
   reg [2:0]                           next_state;

   initial
     begin
        led_sig = 1'b1;
        state = state_on_idle;
        next_state = state_on_idle;
     end

   always @ (posedge clk)
     begin
        // Clock-synchronized state change feed-back loop.
        // No reset is implemented yet.
        state <= next_state;
     end

   // Button press/release & led on/off flip FSM.
   always @ (state or button)
     case (state)
       state_on_idle:
         // LED is on, button was untouched.
         // If the button is pressed now, switch to next state
         // to wait for release.
         begin
            led_sig = 1'b1;
            next_state = button ? state_on_push : state_on_idle;
         end

       state_on_push:
         // LED is on, button was pressed.
         // If the button is release now, switch to next state
         // that will turn off the LED and go IDLE with LEDs off.
         begin
            led_sig = 1'b1;
            next_state = button ? state_on_push : state_on_pop;
         end

       state_on_pop:
         begin
            // LED was on, button got pressed and released.
            // Switch the LED off, go into IDLE state.
            led_sig = 1'b0;
            next_state = state_off_idle;
         end

       state_off_idle:
         begin
            // IDLE state with LEDs off. Button was untouched.
            // If button is pressed then go to the next state
            // that waits for the button release in order to
            // turn on LEDs.
            led_sig = 1'b0;
            next_state = button ? state_off_push : state_off_idle;
         end

       state_off_push:
         begin
            // LEDs are off. Button was pressed. Wait for the button
            // release in order to enable LED(s).
            led_sig = 1'b0;
            next_state = button ? state_off_push : state_off_pop;
         end

       state_off_pop:
         begin
            // LEDs were off, button clicked. Enable LED(s) and
            // go into IDLE state with high beams.
            led_sig = 1'b1;
            next_state = state_on_idle;
         end

       default:
         // Some foobar state... Recover.
         begin
            led_sig = 1'b1;
            next_state = state_on_idle;
         end
     endcase

   assign led[0] = led_sig;
   assign led[1] = ~led_sig;
   assign led[2] = led_sig;
   assign led[3] = ~led_sig;

endmodule

Es wurde für das Spartan-6 LX9 FPGA synthetisiert. Hier sind die Schaltpläne:

Von Xilinx ISE generierte Schaltpläne

Ich habe es manuell auf einem echten Gerät getestet und es scheint zu funktionieren. Also habe ich heute Prüfstandssimulation geübt und den folgenden Prüfstand geschrieben:

`timescale 1ns / 1ps

module test_bench();

   reg clk;
   reg button;
   wire [3:0] led;

   led_flip_flop prog(clk, button, led);

   initial begin
      #20000 $finish;
   end

   initial
     begin
       clk = 0;
       forever #5 clk = ~clk;
     end

   initial
     begin
       button = 0;
       forever #({$random} % 11) button = ~button;
     end

endmodule

Vorher hatte ich eine feste Verzögerung für die button = ~buttonAussage und Signale in der Simulation sahen gut aus. Mit der zufälligen Verzögerung scheint es jedoch so, als könnten Signale zum Drücken und Loslassen von Tasten zwischen Taktimpulse geraten, und die Signale sehen so aus:

Wellen

Einige Wellen, die für mich schlecht aussehen, sind rot markiert.

So wie ich es verstehe, gibt es ein Problem, wenn das Signal von der Taste kurz ist und zwischen Taktzyklen kommt. Natürlich kann es im wirklichen Leben nur Chuck Norris schaffen, einen Knopf schnell genug zu drücken und loszulassen, um zwischen den ansteigenden Flanken einer 66-MHz-Uhr zu wechseln.

Allerdings warf das einige Fragen in meinem Kopf auf..

  1. Wenn externe Signale mit einer höheren Frequenz als der Taktfrequenz kommen (oder wenn die Verarbeitung eines Signals mehrere Taktzyklen dauert), was sind generische Ansätze zur Verarbeitung solcher Signale?
  2. Soll die Uhr immer dabei sein? Kann ich zum Beispiel dasselbe erreichen, ohne die Uhr überhaupt zu verwenden?
  3. Nennt man das "Crossing a Clock Domain"?

Oder mache ich vielleicht etwas völlig falsch und dumm?

Jede Hilfe ist willkommen. Danke schön!

Es wird normalerweise als schlechte Praxis angesehen, Drähte zu verwenden, um Ihren Ausgang zu speisen, typischerweise wird es als schlechte Fan-Out-Eigenschaften angesehen.

Antworten (3)

Wenn externe Signale mit einer höheren Frequenz als der Taktfrequenz kommen (oder wenn die Verarbeitung eines Signals mehrere Taktzyklen dauert), was sind generische Ansätze zur Verarbeitung solcher Signale?

Ein Ansatz besteht darin, eine schnellere Uhr zu verwenden. Die meisten FPGAs (einschließlich Ihres Spartan 6) verfügen über eine dedizierte Logik zum Generieren unterschiedlicher Takte aus einem Quelltakt. Xilinx hat sie in der Vergangenheit DCM (Digital Clock Managers) genannt, obwohl ich denke, dass Spartan 6 eine tatsächliche PLL und DCM-ähnliche Blöcke hat (kann heutzutage anders genannt werden).

Diese sind nett. Sie können einen 12-MHz-Kristall auf Ihrer Leiterplatte platzieren, um Emissionen zu minimieren, und dann können Sie im FPGA den Takt auf z. B. 96 MHz (eine Multiplikation von 8x) multiplizieren.

Sie können auch versuchen, die Logik an der fallenden Flanke und der steigenden Flanke zu takten. Dies würde Ihnen das effektive Aussehen eines 2x-Taktes (auch DDR genannt) geben, aber Ihre Logik wird kompliziert, weil Sie das Design zwischen Logik mit fallender Flanke und steigender Flanke aufteilen müssen.

DCMs können auch Takte erzeugen, die gegenüber dem eingehenden Takt um 90, 180 oder 270 Grad phasenverschoben sind. Wenn Sie also ein Signal wirklich schneller abtasten müssen, als das FPGA verarbeiten kann, können Sie den 90-Grad-Takt verwenden und denselben DDR-Trick anwenden. Dies würde Ihnen vier Gelegenheiten geben, ein eingehendes Signal mit dem schnellstmöglichen FPGA-Takt abzutasten, aber auch dies wird kompliziert, da Sie jetzt die Logik zwischen zwei Sätzen ansteigender und abfallender Flanken aushandeln.

.

Soll die Uhr immer dabei sein? Kann ich zum Beispiel dasselbe erreichen, ohne die Uhr überhaupt zu verwenden?

Sie könnten der reinen kombinatorischen Logik eine Chance geben. Aber im Allgemeinen stellt es einen Timing-Albtraum dar. FPGAs haben ein Meer von Flip-Flops und sie sind sehr wichtig, um Timing Closure zu erreichen. Es ist wirklich am besten, alles zu takten, was Sie sich leisten können. FPGA-IOBs (die Eingangs-Ausgangs-Puffer, die Signale an die Außenwelt senden oder empfangen) haben integrierte Flip-Flops mit dem ausdrücklichen Zweck, das Timing Closure einfacher zu erreichen.

Ich bin mir nicht einmal wirklich sicher, wie Sie das ohne Taktung machen würden.

.

Nennt man das "Crossing a Clock Domain"?

Fast. Das Überqueren einer Taktdomäne, wie oben erwähnt, tritt auf, wenn der Ausgang und der Eingang keinen gemeinsamen Takt teilen. Der Grund dafür ist, dass diese Uhren typischerweise asynchron sind. Als Ingenieur sollten Sie immer bedenken, dass sich asynchrone I/O zum ungünstigsten Zeitpunkt ändern.

Tasteneingaben sind asynchron, daher leiden sie unter den gleichen Problemen wie das Überqueren von Taktdomänen. Das Problem bei einem asynchronen Signal besteht darin, dass Flip-Flops Parameter haben, die als Setup- und Haltezeiten bekannt sind. Die Setup-Zeit ist, wie lange das Signal vor der Taktflanke stabil sein muss. Die Haltezeit gibt an, wie lange das Signal nach der Taktflanke stabil sein muss. Wenn Sie eine Setup- oder Hold-Verletzung haben, kann das Flip-Flop metastabil werden. Das bedeutet, dass Sie keine Ahnung haben, was die Ausgabe sein wird. Bei der nächsten Taktflanke repariert es sich im Allgemeinen von selbst. (Flip-Flops können auch metastabil werden, wenn während der Taktflanke eine Spannung zwischen Vih und Vil angelegt wird, aber das ist im FPGA kein Problem.) Ich glaube, ich habe auch einmal irgendwo gelesen, dass die Haltezeit in FPGAs normalerweise als gilt 0, und der Hauptgrund, warum Sie keinen Timing-Abschluss erhalten, ist eine unzureichende Setup-Zeit.

Um asynchrone Eingaben zu verarbeiten, verwenden wir Synchronisierer. Im Wesentlichen setzen wir mehrere Flip-Flops zwischen den asynchronen Eingang und die Logik, mit der er verbunden ist. Wenn der erste Flip-Flop metastabil wird, wird er den zweiten Flip-Flop nicht vermasseln ... hoffentlich. Es kann zwar immer noch, aber die mittlere Zeit zwischen Ausfällen könnte Jahre betragen. Wem das noch nicht reicht, der setzt noch mehr Flip-Flops ein, um die MTBF auf ein akzeptables Maß zu reduzieren.

In Bezug auf "Crossing Clock Domains" benötigen Sie den Synchronizer nur, wenn Ihre Uhren asynchron zueinander sind. Einer der Vorteile des DCM besteht darin, dass es mehrere Taktfrequenzen erzeugen kann, die perfekt synchronisiert sind. Wenn Sie also eine "große" Berechnung haben, die lange dauert, können Sie einen 12-MHz-Takt verwenden, während der 96-MHz-Takt die Eingänge abtastet. Da beide aus dem DCM stammen, stimmt jede 12-MHz-Flanke perfekt mit einer 96-MHz-Flanke überein, sodass kein Synchronisierer erforderlich ist.

EDIT: Antwort auf Kommentar

Uhren können mein Flip-Flop also nicht asynchron passieren, offensichtlich weil es nur ein Signal gleichzeitig geben kann (reine Physik). Die Synchronisation wird also benötigt, um eine Uhr zu handhaben, die zwischen den anderen Taktzyklen "tickt". Wenn das richtig ist, was passiert, wenn meine Logik (kombinierte Gate-Verzögerung) größer als der Eingangstakt ist? Werde ich stumm geschraubt, "verschwindet" das Signal einfach auf seinem Weg durch die Tore oder geht der Tick des Eingangstakts verloren?

Ich werde hier etwas wählerisch in Bezug auf die Sprache sein, weil es manchmal wichtig ist, Unterscheidungen zu treffen. Die Uhren sind asynchron zueinander, aber jedes Flip-Flop ist mit seiner eigenen Uhr synchronisiert. Das asynchrone Signal ist der Ausgang des Takts in einer Domäne und der Eingang eines Takts in der anderen Domäne.

Angenommen, Sie haben zwei Quarze auf einer Platine bei 48 MHz. Sie sind nicht beide genau48 MHz. Wenn Sie also zwei Serien-Flip-Flops mit den beiden Kristallen und einem Inverter am Ende takten, damit sie in jedem Zyklus schalten, werden Sie schließlich dazu führen, dass das erste Flip-Flop ein Signal ausgibt, das die Setup-Zeit des Eingangs des zweiten Flip-Flops relativ zu verletzt die Uhr des zweiten Flipflops; aber was die uhr des ersten flipflops angeht, läuft alles nach plan. Die zweite Uhr wird für einen Zyklus stören; es kann hoch gehen, es kann niedrig werden, es kann irgendwo dazwischen gehen, es könnte beginnen, in eine Richtung zu gehen und dann mitten im Taktzyklus abrupt in eine andere zu wechseln usw. Nach dem nächsten zweiten Takt wird es wahrscheinlich seinen Eingang korrekt abtasten und aktualisieren Sie seine Ausgabe ... wahrscheinlich. Aus diesem Grund setzen sie für eine wirklich hohe Zuverlässigkeit mehr Flip-Flops in den Synchronisierer,

Das ist für ein Beispiel etwas erfunden, aber es ist leicht zu erkennen, wie diese Uhren einen periodischen metastabilen Vorfall haben würden, der eine Funktion der Differenz der Kristallgeschwindigkeiten ist. Bei Dingen wie Knöpfen passiert es im Grunde völlig zufällig. Dies ist auch der Grund, warum einige Protokolle irgendwie ihre eigene Uhr tragen (z. B. RS232, USB hat eine implizite Uhr in den Daten; I2C, SPI haben eine explizite Uhrspur), um ein Kreuzen von Taktdomänen zu vermeiden.

.

Wenn Ihr Design synthetisiert/zugeordnet/platziert/geroutet ist, teilen Ihnen die Tools die maximale Ausbreitungsverzögerung für jede Logik mit, die ein Flip-Flop in Ihrem Design berührt. Wenn Sie diese Frequenz überschreiten, haben Sie gegen die Regeln verstoßen und der FPGA ist nicht verantwortlich. Tatsächlich möchten Sie im Allgemeinen etwas langsamer fahren. Was Sie also tun, ist den Design-Tools zu sagen (für Xilinx wird eine Einschränkungsdatei verwendet): "Wenn Sie Platz und Route machen, versuchen Sie, alles höchstens 19 ns voneinander entfernt zu halten" (~ 52 MHz), dann verwenden Sie tatsächlich eine 48 MHz-Quarz (20,8 ns), was Ihnen 1,8 ns Schlupf (ca. 9 %) gibt.

Das Erreichen Ihres Designs mit der gewünschten Geschwindigkeit wird als „Timing Closure“ bezeichnet. Bei komplexeren Designs müssen Sie überlegen, wo Sie zusätzliche Flip-Flops platzieren, um den Prozess zu leiten und die Taktgeschwindigkeit hoch zu halten. Der Pentium 4 ist ein extremes Beispiel; Die Pipeline-Stufen 5 und 20 existierten nur zu dem Zweck, die Ausbreitung von Signalen über den Chip zu ermöglichen, was es ihnen ermöglichte, die Taktgeschwindigkeit sehr hoch zu machen. Da das FPGA über ein DCM verfügt, können Sie, wenn Sie ein sehr großes Stück Logik in einer Stufe takten müssen (z. B. einen sehr großen Decoder oder Mux), das DCM einen „langsamen Logik“-Takt ausführen lassen, um diesen Teil zu verarbeiten, und dann haben eine "schnelle Logik" Uhr für die anderen Teile. Dies hilft auch irgendwie beim Stromverbrauch, für jede Stufe, die mit der langsamen Logikuhr läuft.

Für reine kombinatorische Logik sagen Ihnen die Tools die maximale Pad-zu-Pad-Verzögerung; das heißt, wie viel Zeit ein Signal benötigt, um vom Eingangspad durch die gesamte Logik und zurück zum Ausgangspad zu gelangen. Das Problem mit einer solchen Logik ist, dass sie kein Konzept der Vergangenheit hat. Es kann sich nicht erinnern, ob das Licht an oder aus war. Sie könnten wahrscheinlich Ihre eigenen Feedback-Elemente entwerfen (z. B. SR-Latch), vielleicht sogar die Set/Resets der integrierten Flip-Flops verwenden, aber an diesem Punkt beginnen Sie, sequentielle Logik zu werden, also können Sie genauso gut den Sprung wagen und alles zuverlässig getaktet bekommen.

Das ist ausführlich, vielen Dank! Ich habe noch eine dumme Frage, wenn ich darf. Uhren können also nicht asynchron an meinem Flip-Flop vorbeigehen, offensichtlich weil es nur ein Signal gleichzeitig geben kann (reine Physik). Die Synchronisation wird also benötigt, um eine Uhr zu handhaben, die zwischen den anderen Taktzyklen "tickt". Wenn das richtig ist, was passiert, wenn meine Logik (kombinierte Gate-Verzögerung) größer als der Eingangstakt ist? Werde ich stumm geschraubt, "verschwindet" das Signal einfach auf seinem Weg durch die Tore oder geht der Tick des Eingangstakts verloren? Mir ist nicht klar, was "Timing-Albtraum" genau bedeutet. Danke!
Ich habe meine Antwort mit einer Antwort auf Ihren Kommentar bearbeitet, da sie nicht in einen Kommentar passen würde.

Wenn externe Signale mit einer höheren Frequenz als der Taktfrequenz kommen (oder wenn die Verarbeitung eines Signals mehrere Taktzyklen dauert), was sind generische Ansätze zur Verarbeitung solcher Signale?

Wenn Sie all diese Ereignisse erkennen müssen, benötigen Sie eine schnellere Uhr. Möglicherweise können Sie das Eingangssignal mit einem extraschnellen Takt in ein Schieberegister einspeisen und dann mit dem langsameren Takt mehrere Samples des Eingangssignals auslesen und parallel verarbeiten.

Wenn Sie besonders schlau sind, können Sie sich wahrscheinlich eine taktlose Logik mit Handshaking-Signalen usw. vorstellen oder den Eingang mit dem Taktstift eines Zählers verbinden, um die Anzahl der Eingangsereignisse zu zählen. Aber diese Art von Logik funktioniert nicht gut mit FPGA-Designtools.

Soll die Uhr immer dabei sein? Kann ich zum Beispiel dasselbe erreichen, ohne die Uhr überhaupt zu verwenden?

Für einen Fall, in dem Sie nicht alle eingehenden Ereignisse sehen müssen, aber wissen müssen, ob ein oder mehrere Ereignisse seit der letzten Taktflanke aufgetreten sind, können Sie versuchen, das eingehende Signal mit dem (asynchronen) Set oder Reset zu verbinden Eingang eines Flipflops; Verwenden Sie dann ein anderes Signal, um das Flip-Flop wieder in einen Standardzustand zu versetzen, nachdem Sie seine Änderung in Ihrer anderen Logik erkannt haben.

Nennt man das "Crossing a Clock Domain"?

„Crossing a clock domain“ deckt Fälle ab, in denen Signale, die synchron mit einem Takt erzeugt werden, von einer Logik gelesen werden müssen, die von einem anderen Takt gesteuert wird. In diesem Fall ist Ihre Eingabe völlig asynchron und hat überhaupt keinen steuernden Takt, daher halte ich es nicht für ein reines Beispiel, aber es ist ein sehr ähnliches Problem.

Schaltflächenübergang hat viele Sprünge. Daher wird ein "Switch De-Bounce" -Latch oder -Filter benötigt.

Danke. Das mit Knöpfen wusste ich nicht! Verdammte Elektrizität, die Dinge vermasselt! Ich habe hier eine nette Erklärung mit einigen Codebeispielen nachgeschlagen - fpga4fun.com/Debouncer.html
Tatsächlich ist ein Sprung von < 0,5 ms außergewöhnlich. Früher hatten wir mit einem Sprung von 10 ms zu tun. Vielen Dank an die Mechaniker.
Die vereinbarte Entprellzeit von 10 ms wird allgemein akzeptiert, was ich gesehen habe. Vergessen Sie nicht, dass Ihr Schalter heute zwar nur 4 oder 5 ms lang springt, aber mit zunehmendem Alter sehr viel länger hüpfen kann. Außerdem können je nach Herstellungstoleranzen unterschiedliche Schalter unterschiedlich stark prellen.