Noch einmal über Verzögerungen im Quellcode des FPGA-Projekts oder eine einfache Frage für ein Interview für einen FPGA-Entwicklerjob



Vor einiger Zeit entstand während einer Diskussion in Begleitung professioneller FPGA-Entwickler eine Diskussion über das Bestehen eines Interviews. Welche Fragen werden dort gestellt und was könnte gestellt werden? Ich schlug zwei Fragen vor:

  1. Geben Sie ein Beispiel für einen synchronen Code ohne Verzögerungen, der beim Modellieren und Arbeiten in realen Geräten zu unterschiedlichen Ergebnissen führt
  2. Korrigieren Sie diesen Code mit Verzögerungen.

Nach dieser Frage folgte eine lebhafte Diskussion, weshalb ich mich entschied, dieses Thema genauer zu betrachten.

Ich habe dieses Problem bereits im vorherigen Artikel ein wenig angesprochen. Jetzt genauer. Hier ist ein Beispieltext:

library IEEE; use IEEE.STD_LOGIC_1164.all; entity delta_delay is end delta_delay; architecture delta_delay of delta_delay is signal clk1 : std_logic:='0'; signal clk2 : std_logic; alias clk3 : std_logic is clk1; --    clk1 signal a : std_logic; signal b : std_logic; signal c : std_logic; signal d : std_logic; begin ---    --- clk1 <= not clk1 after 5 ns; pr_a: process begin a <= '0' after 1 ns; wait until rising_edge( clk1 ); wait until rising_edge( clk1 ); a <= '1' after 1 ns; wait until rising_edge( clk1 ); wait until rising_edge( clk1 ); wait until rising_edge( clk1 ); wait until rising_edge( clk1 ); end process; ---   -    --- clk2 <= clk1; --    ,        ---  1 -     --- b <= a when rising_edge( clk1 ); c <= b when rising_edge( clk1 ); d <= b when rising_edge( clk2 ); ---  2 -     --- -- --clk2 <= clk1; --b <= a after 1 ns when rising_edge( clk1 ); --c <= b after 1 ns when rising_edge( clk1 ); --d <= b after 1 ns when rising_edge( clk2 ); ---  3 -          alias --- --b <= a when rising_edge( clk1 ); --c <= b when rising_edge( clk1 ); --d <= b when rising_edge( clk3 ); end delta_delay; 

Der Einfachheit halber wird der gesamte Code in einer Komponente platziert.

Die Signale clk1 und a sind Testbelichtungssignale . clk1 ist eine Taktfrequenz von 100 MHz, Signal a hält zwei Taktzyklen bei 0 und vier Taktzyklen bei 1. Das Signal a wird mit einer Verzögerung von 1 nc relativ zur ansteigenden Flanke von clk1 erzeugt . Diese beiden Signale reichen aus, um das Problem zu beschreiben.

Verschiedene synthetisierte Codeoptionen können unkommentiert und modelliert werden.
Betrachten Sie die erste Option, dies ist ein synthetisierter Code ohne Verzögerung und unter Verwendung der Neuzuweisung der Taktfrequenz.

Hier sind die Simulationsergebnisse fĂĽr Option 1:



Das Diagramm zeigt visuell, dass die Taktsignale clk1 und clk2 zusammenfallen, aber tatsächlich ist clk2 relativ zu clk1 um den Wert der Delta-Verzögerung verzögert. Das Signal c verzögert das Signal b um einen Taktzyklus. Das ist richtig. Das Signal d muss jedoch mit dem Signal c übereinstimmen, dies geschieht jedoch nicht. Es funktioniert früher.

Erinnern wir uns, was Delta-Verzögerung ist. Dies ist ein grundlegendes Konzept, das auf der Arbeit von Ereignissimulatoren basiert, die wir bei der Modellierung von Logikschaltungen verwenden.

Der Simulator hat das Konzept der Modellzeit. Alle Ereignisse im System sind an diese Modellzeit gebunden. Schauen wir uns die Bildung der Taktfrequenz an:

 clk1 <= not clk1 after 5 ns; 

Angenommen, wir modellieren jetzt nur clk1 , es gibt keine anderen Signale.
Zum anfänglichen Zeitpunkt ist clk1 0, dies wird gesetzt, wenn das Signal deklariert wird. Der Simulator sieht eine Anforderung zum Invertieren des Signals. Das Schlüsselwort after enthält Anweisungen zum Zuweisen eines neuen Werts in 5 ns relativ zur aktuellen Modellzeit. Der Simulator sieht dies und notiert, dass zum Zeitpunkt 5 ns der Wert von clk1 1 sein wird. Dies ist zwar eine Modellzukunft, kann sich aber übrigens noch ändern. Als nächstes scannt der Simulator die verbleibenden Signale. Der Simulator sieht, dass für einen bestimmten Moment in der Modellzeit alles erledigt ist und er den nächsten Moment berechnen kann. Es stellt sich die Frage: Was ist der nächste Moment? Grundsätzlich sind verschiedene Optionen möglich. Zum Beispiel hat Simulink einen Modus mit fester Tonhöhe. In diesem Fall erhöht sich die Modellzeit um einen gewissen Betrag und die Berechnungen werden fortgesetzt.

Digitale Schaltungssimulationssysteme machen das anders. Sie fahren mit dem nächsten Ereignis fort, das sie bereits in Zukunft auf ihrer Achse der Modellzeit platziert haben. In diesem Fall sind es 5 ns. Der Simulator erkennt, dass sich clk1 geändert hat, und berechnet einen neuen Wert dafür. Er ist 0, der ebenfalls mit einer Verzögerung von 5 ns auf der Zeitachse platziert wird. Das heißt, es wird 10 ns sein. Der Vorgang wird also fortgesetzt, bis die angegebene Simulationszeit endet.

FĂĽgen wir nun die Signale a und b hinzu .

Das Signal a wird dabei zugewiesen. FĂĽr Signal b wird die bedingte Konstruktion verwendet; Die Funktion running_edge ( clk1 ) analysiert clk1 und gibt true zurĂĽck , wenn die Front fest ist, d. H. Der vorherige Wert ist 0 und der aktuelle Wert ist 1.

Zur Modellzeit von 5 ns ändert sich clk1 . Es wird gleich 1 und für den Moment von 10 ns wird ein Ereignis erstellt, bei dem es auf 0 gesetzt wird. Dies ist jedoch später. Während wir uns noch im Moment von 5 ns befinden, setzen wir die Berechnungen fort. Der Simulator geht zur Leitung
 b<=a when rising_edge(clk1); 
Da es eine Funktion gibt, die von clk1 abhängt , berechnet der Simulator den Wert der Funktion, stellt fest, dass sie true zurückgibt, und weist sie zu
 b<=a; 


Hier beginnt der interessanteste Teil - wenn es notwendig ist, den Wert von b zu ändern. Es scheint notwendig, es jetzt zu diesem Zeitpunkt zu ändern. Wir haben aber parallele Prozesse. Vielleicht brauchen wir noch den Wert von b , um andere Signale zu berechnen. Und hier kommt das Konzept der Delta-Verzögerung. Dies ist der Mindestwert, um den sich die Modellzeit verschiebt. Dieser Wert hat nicht einmal die Dimension der Zeit. Dies ist nur ein Delta. Aber es kann viele von ihnen geben. Und so sehr, dass der Simulator einfach versehentlich anhält oder einfriert.
Daher wird für den Moment 5 ns + 1 ein neuer Wert von b eingestellt (1 ist die erste Delta-Verzögerung). Der Simulator sieht, dass für den Moment 5 ns bereits nichts zu berechnen ist, und geht zum nächsten Moment über, und dies ist 5 ns + 1; In diesem Moment funktioniert Rising_edge (ckl1) nicht. Und der Wert von b wird auf 1 gesetzt. Danach geht der Simulator auf den Moment 10 nc.

FĂĽgen wir nun die Signale c , d hinzu und sehen, warum sie unterschiedlich sind.
Es ist am besten, den Moment der Modellzeit von 25 ns unter Berücksichtigung von Delta-Verzögerungen zu berücksichtigen

Deltaclk1clk2re (clk1)re (clk2)bcd
010wahrfalsch000
111falschwahr100
210falschfalsch101

Hinweis: Re-Rising_edge

Die Tabelle zeigt, dass zum Zeitpunkt des Auslösens der Funktion ascend_edge ( clk2 ) der Wert von b bereits 1 ist. Daher wird er dem Signal d zugewiesen.

Basierend auf dem gesunden Menschenverstand ist dies nicht das Verhalten, das wir vom Code erwartet haben. SchlieĂźlich haben wir einfach das Signal clk1 zu clk2 neu zugewiesen und erwartet, dass die Signale c und d gleich sind. Nach der Logik des Simulators ist dies jedoch nicht der Fall. Dies ist eine PRINCIPAL- Funktion. Diese Funktion muss den Entwicklern von FPGA-Projekten natĂĽrlich bekannt sein, daher ist dies eine gute und notwendige Frage fĂĽr ein Interview.

Was wird während der Synthese passieren? Aber der Synthesizer wird dem gesunden Menschenverstand folgen, er wird die Signale clk2 und clk1 zu einem Signal machen und daher werden auch c und d gleich sein. Und mit bestimmten Synthesizer-Einstellungen werden sie auch zu einem Signal kombiniert.

Dies ist nur dann der Fall, wenn das Modellieren und Arbeiten in realen Geräten zu unterschiedlichen Ergebnissen führt. Ich möchte darauf hinweisen, dass der Grund für die unterschiedlichen Ergebnisse die unterschiedliche Logik von Simulator und Synthesizer ist. Dies ist ein grundlegender Unterschied. Dies hat nichts mit zeitlichen Einschränkungen zu tun. Und wenn Ihr Projekt im Modell und im Bügeleisen unterschiedliche Ergebnisse zeigt, überprüfen Sie, ob sich dort möglicherweise ein solches Design eingeschlichen hat

 clk2 <= clk1 

Die zweite Frage besteht nun darin, diesen Code mit Verzögerungen zu beheben.
Dies ist Option 2. Sie kann unkommentiert und modelliert werden.
Hier ist das Ergebnis.



Das Ergebnis ist korrekt. Was ist passiert? Lassen Sie uns noch einmal eine Tabelle fĂĽr ein Intervall von 25 - 36 ns erstellen
ZeitDeltaclk1clk2re (clk1)re (clk2)bcd
25010wahrfalsch000
25111falschwahr000
26011falschfalsch100
35010wahrfalsch100
35111falschwahr100
36011falschfalsch111

Es ist ersichtlich, dass sich der Wert von b in den Momenten der Fronten clk1 , clk2 nicht ändert. Eine Verzögerung von 1 ns dauert in dem Moment, in dem sich das Signal über die Flankenantwortzone hinaus ändert. Dieser Code kommt der Realität näher. In einer realen Schaltung gibt es einige Zeit, bis der Trigger ausgelöst wird und sich das Signal ausbreitet. Diese Zeit sollte kürzer sein als die Periode der Taktfrequenz. Tatsächlich ist dies das, was der Tracer tut, und dies ist, was die Zeitanalyse überprüft.

Die Fehlerursache ist die Neuzuweisung des Taktsignals durch die übliche Zuordnung, bei der eine Delta-Verzögerung auftritt. Die VHDL-Sprache verfügt jedoch über ein Alias-Konstrukt. Auf diese Weise können Sie einen anderen Namen für das Signal erhalten. Hier ist die Ankündigung:

 alias clk3 : std_logic is clk1; 

Im Beispieltext können Sie Option 3 auskommentieren - sie funktioniert ordnungsgemäß.

Dieses Beispiel ist in VHDL geschrieben. Vielleicht ist das nur das Problem dieser Sprache? Aber hier sind die gleichen Optionen in Verilog.

Versteckter Text
 `timescale 1 ns / 1 ps module delta_delay_2 (); reg clk1 = 1'b0; reg clk2; wire clk3; reg a = 1'b0; reg b; reg c; reg d; initial begin forever clk1 = #5 ~clk1; end initial begin repeat(10) begin #20 a = 1'b1; #60 a = 1'b0; end end //   -    --- always @(clk1) clk2 <= clk1; //  1 -     always @(posedge clk2) d <= b; always @(posedge clk1) begin c <= b; b <= a; end //  2 -     //always @(posedge clk1) b = #1 a; // //always @(posedge clk1) c = #1 b; // //always @(posedge clk2) d = #1 b; //  3 -     //      assign //assign clk3 = clk1; // //always @(posedge clk3) d <= b; // //always @(posedge clk1) //begin // c <= b; // b <= a; //end endmodule 



  • Option 1 - keine Verzögerung. Es funktioniert nicht richtig.
  • Option 2 - mit Verzögerungen. Es funktioniert richtig.
  • Option 3 - Neuzuweisung per Kabel. Es funktioniert richtig.

Verilog hat das Konzept von reg und wire. In diesem Fall sieht die Neuzuweisung des Taktsignals ĂĽber Draht natĂĽrlicher aus. Dies ist analog zu einer Aliaszuweisung in VHDL. Dies entlastet das Problem etwas, aber Sie mĂĽssen dies noch wissen.
Verilog hat auch das Konzept der blockierenden und nicht blockierenden Zuweisung. Die Signalzuordnung b und c kann auf andere Weise geschrieben werden:

 always @(posedge clk1) begin c = b; b = a; end 

Und Sie können dies tun:

 always @(posedge clk1) begin b = a; c = b; end 

Abhängig von der Reihenfolge der Zeilen ist das Ergebnis unterschiedlich.

Um auf das Thema des Interviews zurückzukommen, möchte ich noch einmal betonen, dass diese Fragen dazu dienen, das Wesentliche des Problems zu verstehen. Und aus dem Verständnis des Problems kann man verschiedene Schlussfolgerungen ziehen, zum Beispiel, welcher Codestil verwendet werden soll. Persönlich verwende ich immer die Verzögerungszuweisung.

Beispieldateien finden Sie hier.

Source: https://habr.com/ru/post/de419875/


All Articles