Wie werden Verilog-Anweisungen "immer" in Hardware implementiert?

8

Die Verilog- alwaysAussage, nämlich

always @(/* condition */)
    /* block of code */

führt das aus, block of codewann immer conditiones erfüllt ist. Wie ist ein solcher alwaysBlock in Hardware implementiert?

Zufälliges Blau
quelle
Ich denke, es hängt stark davon ab, was das block of codeist ..
m.Alin
1
Und ob die Bedingung ist posedge xoder nurx
Justin
@ Justin: Nehmen wir an, es gibt keine posedge.
Randomblue

Antworten:

16

Beachten Sie zunächst, dass nicht alle Verilog-Designs synthetisierbar sind. Normalerweise kann nur eine sehr spezifische Teilmenge von Konstrukten in einem Entwurf verwendet werden, der in Hardware realisiert werden soll.

Eine wichtige Einschränkung, die auftaucht, ist, dass jede regVariable nur in höchstens einer alwaysAnweisung zugewiesen werden kann . Mit anderen Worten, regs haben eine Affinität zu alwaysBlöcken.

Die folgenden Arten von alwaysBlöcken können im Allgemeinen verwendet werden.

always @(*) begin
    // combinational
end

always @(posedge clk) begin
    // sequential
end

Im ersteren Fall gibt das *an, dass der Block ausgeführt werden soll, wenn sich ein im Block verwendetes Signal ändert, oder äquivalent, dass der Block kontinuierlich ausgeführt werden soll. Daher werden regs, die eine Affinität zu Kombinationsblöcken alwayshaben, als Signale implementiert, die aus anderen Signalen unter Verwendung von Kombinationslogik, dh Gattern, berechnet werden.

Register, die eine Affinität zu alwaysBlöcken des letzteren Typs haben, sind andererseits Ausgänge von D-Flipflops, die an der ansteigenden Flanke von getaktet werden clk(fallende Flanke, wenn negedgeverwendet wird). Eingaben in die Flip-Flops werden wiederum mit kombinatorischer Logik aus anderen Signalen berechnet.

Betrachten Sie das folgende, etwas erfundene Beispiel.

reg out, out_n;
always @(*) begin
    out_n = !out;
end
always @(posedge clk) begin
    out <= !out;
end

Hier out_nist mit dem ersten alwaysBlock, outmit dem zweiten verbunden. out_nwird mit einem einzelnen NICHT-Gatter implementiert, das ansteuert out_nund von dem aus angesteuert wird out(beachten Sie, dass es sich um eine reine kombinatorische Logik handelt). Auf der anderen Seite outwird von einem Flip-Flop getaktet, von dem getaktet wird clk. Der Eingang zum Flip-Flop wird wieder von einem NOT-Gatter berechnet out(das vom oben genannten Flip-Flop angesteuert wird). Durch die Optimierung von Synthesizern werden die beiden NOT-Gatter kombiniert und ein NOT-Gatter und ein Flip-Flop verwendet.

Abhängig von der verfügbaren Hardware können andere Arten von Konstrukten verwendet werden. Wenn die Flip-Flops beispielsweise asynchrone Resets haben, ist das folgende Konstrukt ebenfalls synthetisierbar.

always @(posedge clk or posedge rst) begin
    if (rst)
        // reset
    else
        // sequential
end
Avakar
quelle
Vielen Dank. In Bezug auf das *dachte ich, dass es angezeigt wird, dass der Block ausgeführt werden sollte, wenn sich ein Signal im Block ändert (im Gegensatz zum Design ).
Randomblue
@Randomblue, du hast recht, ich werde die Antwort korrigieren. Beachten Sie jedoch, dass die beiden Verhaltensweisen gleichwertig sind.
Avakar
Wahr; Meinetwegen!
Randomblue
2

Ein alwaysBlock wird üblicherweise verwendet, um ein Flip-Flop, einen Latch oder einen Multiplexer zu beschreiben. Der Code würde mit einem Flip-Flop, einem Latch oder einem Multiplexer implementiert.

In einem FPGA sind ein Flip-Flop und ein Latch im Allgemeinen nur zwei verschiedene Konfigurationen eines allgemeineren Registergeräts. Ein Multiplexer würde aus einem oder mehreren Allzweck-Logikelementen (LUTs) aufgebaut sein.

Im Allgemeinen gibt es zwei Möglichkeiten, mit Verilog zu entwerfen:

  1. Visualisieren Sie die gewünschte Logik in Bezug auf Gates und Register und finden Sie dann heraus, wie Sie sie in Verilog beschreiben können. In den Synthesehandbüchern der FPGA- oder Synthesewerkzeughersteller finden Sie eine Kesselplatte für die gängigsten Strukturen, mit denen Sie möglicherweise arbeiten möchten.

  2. Schreiben Sie einfach Verilog und machen Sie sich keine Sorgen darüber, wie die zugrunde liegende Hardware aussieht. Selbst wenn Sie dies tun, müssen Sie dennoch wissen, was synthetisierbar ist und was nicht. Sie sehen sich also erneut das von Ihrem Werkzeughersteller bereitgestellte Boilerplate an und passen es an Ihre Anwendung an.

BEARBEITEN

Avakars Antwort ist viel besser für Ihre Frage, aber dies hat einige interessante Diskussionen über die Unterschiede zwischen Xilinx und Altera ausgelöst, sodass ich sie nicht löschen werde.

Das Photon
quelle
"Flip-Flop und ein Latch sind im Allgemeinen nur zwei verschiedene Konfigurationen" Sind sie? Ich würde erwarten, dass Latches mit LUTs implementiert werden (vorsichtig, wenn die LUTs nicht störungsfrei sind).
Avakar
@avakar, ich weiß, dass in allen Xilinx-FPGAs (oder zumindest in allen neueren) die Latches dieselbe Hardware wie ein Flip-Flop verwenden und sich im Konfigurationsbitstream nur um ein einziges Bit unterscheiden. Bei anderen Marken bin ich mir nicht sicher.
Kevin Cathcart
Hmm. Einige ältere Altera-Designs hatten Rückkopplungspfade, über die LUT zum Implementieren von Latches verwendet werden konnte. Es sieht fast so aus, als ob das Hauptrouting erforderlich sein könnte, um Latches in den neueren Designs überhaupt zu implementieren. Dies ist jedoch nicht überraschend, da im modernen RTL-Design tatsächliche Latches (anstelle von Flip-Flops) selten erwünscht sind.
Kevin Cathcart
@avakar, ich kenne Xilinx besser, wo das Registergerät als Flip-Flop oder Latch konfiguriert werden kann. Wenn dies in Altera oder einem anderen Anbieter nicht möglich ist, wird der allgemeine Ratschlag "Nicht mit Riegeln konstruieren" noch stärker.
Das Photon
@ KevinCathcart und Photon: Ich verstehe, ich kenne Xilinx nicht, nur die Altera Cyclone-Serie, die keine dedizierte Latch-Schaltung hat.
Avakar
0

Wie gesagt, sind nicht immer alle Blöcke synthetisierbar. Es gibt auch einige Blöcke, die von den Synthesewerkzeugen akzeptiert werden, die jedoch Ergebnisse liefern, die sich von denen eines Simulators unterscheiden.

Zuerst die Empfindlichkeitsliste. Die übliche Regel ist, dass es entweder nur Kantenerkennungskonstrukte enthalten darf (und es gibt normalerweise eine begrenzte Auswahl möglicher Kombinationen) oder dass es (möglicherweise durch Verwendung von * oder systemverilogs always_comb) jedes Signal enthalten muss, das als Eingabe für den Block verwendet wird. Wir nennen den ersteren einen kombinatorischen Block und den letzteren einen sequentiellen oder Block. Wenn Sie nur eine Teilmenge von Eingaben in ein kombinatorisches Blocksynthesewerkzeug aufnehmen, werden Sie normalerweise einfach ignoriert und so getan, als ob die vollständige Liste angegeben worden wäre (Erstellen von Simulations- / Synthesekonflikten).

Zweite Blockierung gegen Noblocking-Aufgaben. In einem kombinatorischen Block spielt der Unterschied keine große Rolle, aber in einem sequentiellen Block spielt er eine große Rolle.

In einem sequentiellen Block, der nicht blockierende Zuweisungen modelliert, wird ein Register ziemlich direkt modelliert, während Zuweisungsmodellvariablen blockiert werden (was abhängig von der Reihenfolge des Einstellens und Lesens Register implizieren kann oder nicht). In der Regel sollte ein "reg" -Satz, der blockierende Zuweisungen in einem sequentiellen Block verwendet, nur in demselben Block gelesen werden, und blockierende und nicht blockierende Zuweisungen sollten nicht in demselben "reg" gemischt werden.

Das Mischen von blockierenden und nicht blockierenden Zuweisungen zu demselben Element führt wahrscheinlich zu Synthesefehlern. Das Erstellen einer Blockierungszuordnung in einem Block und das Lesen in einem anderen Block führt wahrscheinlich zu Fehlanpassungen bei der Simulation / Synthese (und möglicherweise sogar zu Fehlanpassungen zwischen verschiedenen Simulationsläufen).

Jetzt haben wir die Grundregeln aus dem Weg, um zu überlegen, wie der Compiler Code in Logik umwandelt.

Der erste Schritt besteht darin, alle Schleifen abzuwickeln. Dies bedeutet, dass Schleifen eine maximale Iterationszahl haben müssen, die zum Zeitpunkt der Synthese bestimmt werden kann, da sonst ein Synthesefehler auftritt.

Anschließend kann das Tool den Kontrollfluss des Blocks analysieren und in einen Datenfluss umwandeln. Jede Variable wird zu einem oder mehreren Signalen. Jede if-Anweisung oder ein ähnliches Konstrukt wird zu einem oder mehreren Multiplexern, die auswählen, welche Ergebnismenge tatsächlich verwendet wird.

Das Tool wird dann wahrscheinlich versuchen, einige Optimierungen anzuwenden.

In Quartus können Sie die Ergebnisse dieses Prozesses nach dem Erstellen Ihres Projekts sehen, indem Sie auf "tools-> netlist views-> rtl viewer" gehen.

Nachdem diese strukturelle Darstellung in Bezug auf abstrakte Logikelemente erzeugt wurde, fährt das Werkzeug fort, diese abstrakten Elemente auf die Ressourcen abzubilden, über die der Chip tatsächlich verfügt.

Peter Green
quelle