Diskrepanz zwischen Simulation auf RTL-Ebene und Post-Synthese-Simulation mit xilinx xst

Ich habe einen Verilog-Code geschrieben und die RTL-Simulation funktioniert einwandfrei. Danach habe ich das Design mit dem XST-Tool in Xilinx ISE 13.2 synthetisiert. Die Post-Synthese-Simulation zeigt einige unerwartete Ergebnisse. Ich weiß nicht, was schief gelaufen ist, da es während der Simulation keine Warnungen gab. Was sollte ich jetzt tun? Gibt es eine Möglichkeit, die Netzliste auf Post-Synthese-Ebene zu debuggen? Woher weiß ich, was mein Synthesetool (XST) mit meinem Design macht? Ich habe einen Teil meines Quellcodes beigefügt. Es dient zur Steuerung des FSM meines Designs.

always @ (posedge clock)
begin
case (state)   // s0, s1, s2, s3, s_reset are parameters
s_reset:    
        begin
            if(start_proc)
                state <= s0;
            else
                state <= s_reset;
        end
s0: begin
            pixel_value <= dataOut_bigimage;
            pixel_ref <= dataOut_smallimage;
            j <= j+1;
            if(j==1'b1)
            i <= i+1;
            if ({i,j} == 2'b11)
                if(base_col == 3'b101)
                begin
                    base_row_prev <= base_row;
                    base_col_prev <= base_col;
                    base_col <= 3'b000;
                    base_row <= base_row+1;
                end
                else
                begin
                    base_col <= base_col+1;
                    base_col_prev <= base_col;
                    base_row_prev <= base_row;
                end

            state <= s1;
        end

s1: begin
            if (pixel_value <= pixel_ref)
                accumulator <= accumulator+1;
            else
                accumulator <= accumulator;
            if({i,j} == 2'b00)
                state <= s2;
            else state <= s0;
        end

s2: begin
            if (accumulator > 2'b01)
                begin
                    matchcount <= matchcount+1;
                    rowmatch[matchcount] <= base_row_prev;
                    colmatch[matchcount] <= base_col_prev;
                end
            accumulator <= 2'b00;
            if (base_row == 2'b11)
                state <= s3;
            else 
                state <= s0;
        end

s3: begin
            task_done <= 1'b1;
            state <= s3;
        end

Alles innerhalb des Always-Blocks ist vom Reg-Datentyp und wird ordnungsgemäß in einem separaten Anfangsblock initialisiert

Vielen Dank im Voraus

Was ist Ihre Definition eines "zufälligen Ergebnisses"?
@Tim, es ist nicht dasselbe wie das, das ich in der Verhaltenssimulation bekommen habe. Mein Problem ist, dass ich nach der Synthese nur Top-Level-Ports meines Designs überwachen kann. Ich kann also nur sagen, dass mein Design nach der Synthese nicht funktioniert :(
Mir ist kein 'Standardweg' zum Debuggen einer Postsynthese-Netzliste bekannt, außer Ihren Ingenieurhut aufzusetzen und zu versuchen, das Design rückwärts durchzuarbeiten, um zu sehen, wo es schief geht. Ich kenne die Besonderheiten Ihres Werkzeugs nicht, aber vielleicht gibt es eine Möglichkeit, Haltenetze zu Schlüsselsignalen in Ihrem Design hinzuzufügen und zu sehen, ob sie sich richtig verhalten. Sie sagen, dass Sie nur die Top-Level-Ports sehen können. Gibt es einen Grund, warum Sie die Simulation nicht gegen Ihre Synthese-Netzliste ausführen und die vollständige Wellenform ausgeben können?
Ich weiß auch nicht, ob dies eine 50-Gate- oder 500.000-Gate-Synthese ist, aber wenn Ihr Projekt klein ist, können Sie vielleicht die Quelle hinzufügen und jemand kann nach offensichtlichen Problemen wie Latches oder möglicherweise X-Prop-Problemen suchen.
@Tim, die Netzliste bezieht sich auf FPGA-Primitive. Daher kann ich seine Bedeutung nicht nachvollziehen. Außerdem glaube ich nicht, dass es Probleme wie Latches gibt, da XST Warnungen für solche Probleme anzeigt. Ich füge die Quelle hier hinzu. Mal sehen, ob jemand helfen kann.
Es könnte auch hilfreich sein, wenn Sie Wellenformen zeigen, die ein gutes Simulationsergebnis mit einem schlechten Synthesesimulationsergebnis vergleichen, und beschreiben, was genau fehlschlägt.

Antworten (5)

Fügen Sie ein Reset-Signal hinzu (und verwenden Sie es).

Xilinx FPGAs verfügen über ein globales Set/Reset (GSR)-Signal, das alle Register in ihren Standardzustand oder wie in der Registerdeklaration angegeben versetzt (dies ist im XST-Benutzerhandbuch am Anfang von Kapitel 5 dokumentiert). AFAIK, der @initial Block wird ignoriert.

Beim Start des FPGAs geht es allerdings chaotisch zu, denn:

  • Der GSR ist asynchron.
  • PLLs sind nicht gesperrt
  • Nicht alle PLLs sperren gleichzeitig
  • Race Conditions gibt es überall

Die anfänglichen Flip-Flop-Werte nach dem GSR reichen also nicht aus.

Erstellen Sie ein Modul, das ein Reset-Signal für jede Taktdomäne generiert. Sie können es erstellen, indem Sie relevante asynchrone Rücksetzsignale UND-verknüpfen, z. B. einen externen Rücksetzstift, PLL/DCM-verriegelte Signale, und es wie folgt mit einem Synchronisierer verwenden:

Schaltung zurücksetzen

(Quelle: Wie setze ich mein FPGA zurück? )

Hier ist ein Teil Ihres Codes, der jemandem, der neu bei Verilog ist, ein Bein stellen könnte:

        j <= j+1;
        if(j==1'b1)
           i <= i+1;
        if ({i,j} == 2'b11)
           ...

In diesem Code jwird für den Vergleich ( if(j==1'b1)) der alte Wert von verwendet j, nicht der neu inkrementierte Wert. Aber ich vermute, Sie wussten das bereits, und wenn dies Ihr Problem wäre, hätten Sie es auf jeden Fall in der Verhaltenssimulation gesehen (es sei denn, Sie hätten eine blockierende Zuweisung verwendet, wie bei der Verhaltenssimulation j = j+1).

Eine weitere seltsame Sache an Ihrem Code ist, dass Sie feststecken, sobald Sie Zustand 3 erreichen. Sie haben keinen Mechanismus bereitgestellt, um in den Reset-Zustand oder in den Zustand 0 zurückzukehren, was ein ungewöhnliches Design ist, aber auch nicht nach dem Problem klingt, nach dem Sie fragen.

Eigentlich ist es der alte Wert von j, den ich in der if-Anweisung vergleichen möchte. Das ist also kein Problem. Außerdem tritt mein Design in Zustand 3 ein, wenn meine Aufgabe abgeschlossen ist. Dort soll es also hängen bleiben

Anscheinend gibt es eine werkzeugspezifische Funktion, mit der in Ihrem Fall erste Blöcke synthetisiert werden können. Das war mir nicht bewusst, als ich das geschrieben habe. Ich frage mich, warum sie dieses Feature hinzufügen würden, weil es nur einen schlechten Programmierstil fördern würde. Sinnvoller wäre eine Power-on-Reset-Megafunktion. Nehmen Sie diese Antwort also als allgemeinen Rat.

Alles innerhalb des Always-Blocks ist vom Reg-Datentyp und wird ordnungsgemäß in einem separaten Anfangsblock initialisiert

Anfängliche Blöcke sind normalerweise nicht synthetisierbar. Obwohl einige Tools den anfänglichen Block möglicherweise korrekt implementieren, ist es aus Gründen der Portabilität nicht ratsam, sich auf dieses Verhalten zu verlassen. Wenn Sie beispielsweise Werkzeuge wechseln, kann dies Ihr Design beschädigen. Sie sollten sie nicht in Blöcken verwenden, die Sie im Allgemeinen synthetisieren möchten, da das Verhalten vor und nach der Synthese möglicherweise nicht übereinstimmt.

Ihr Block hat kein Rücksetzsignal, und normalerweise bedeutet dies, dass keine Initialisierung in der Hardware erfolgt. Um dieses Problem zu beheben, fügen Sie Ihrem Code ein Reset-Signal und eine Reset-Bedingung hinzu. Platzieren Sie dann den Inhalt des Anfangsblocks in dem neuen Rücksetzbedingungsblock.

Ich stimme zwar Ihrem Rat bezüglich der Synthese von Anfangsblöcken zu, aber ich habe festgestellt, dass sie mit Xilinx-Tools und FPGAs verwendet werden können. Xilinx-FPGAs durchlaufen eine interne Reset- und Programmiersequenz, und es ist möglich, Register als Teil dieses Prozesses zu initialisieren.
@Joe Wo hast du herausgefunden, dass Anfangsblöcke während der Synthese verwendet werden? Soweit ich weiß, wird nur der in ihrer Deklaration angegebene Wert verwendet.
@JoeHass, die Verwendung von reset ist immer noch die bevorzugte Route, da das Verlassen auf ein solches Tool-spezifisches Verhalten zu Portabilitätsproblemen führt.
@apalopohapa Ich könnte Ihnen keine Referenz nennen, aber ich unterrichte digitales Design mit Xilinx-Tools, und einige Studenten verwenden Anfangsblöcke in synthetisiertem Code, trotz meiner Warnungen, dies nicht zu tun. Zu meiner Überraschung hat es funktioniert. Ich denke, das liegt daran, dass die Xilinx-Chips von Natur aus einen globalen Power-Up-Reset erzeugen.
@trav1s Wie ich ursprünglich sagte, stimme ich Ihrem Rat zu. Es ist jedoch nicht ganz richtig zu sagen, dass sie nicht synthetisierbar sind.
@JoeHass ah, guter Punkt. Ich werde die Bearbeitung vornehmen.
@apalopohapa Entschuldigung, ich hätte genauer sein sollen. Soweit ich weiß, kann in einem Anfangsblock nur die Initialisierung von Registern erfolgen.

Ich würde Ihnen raten, einen kombinatorischen Block zu haben und die gesamte Steuerlogik dorthin zu verschieben und nur die Datenübertragungslogik im sequentiellen Block zu belassen. Ich habe Ihren Code nicht ausgeführt, aber ich habe das Gefühl, dass im Fall des Zustands s0 Ihre beiden if-Bedingungen (if j==1'b1) und (if {i,j} == 2'b11) sein könnten miteinander kollidieren. Bei einer normalen Verhaltenssimulation simuliert das Tool das Design ohne Verzögerungen. Nach der Synthese werden die Verzögerungen jedoch zum Design hinzugefügt und können das Problem des "zufälligen Ergebnisses" verursachen, das Sie sehen.

Könnte Ihrer Einschätzung zustimmen, um die Logik etwas sauberer aufzuteilen, aber ich kann mir kein Problem vorstellen, das durch die beiden von Ihnen ifhervorgehobenen Aussagen verursacht werden könnte. Sie würden auf keine Weise, die ich sehen kann, "kollidieren".
Ich habe viele Register in den Block der obersten Ebene aufgenommen, indem ich einfach Regs definiert und ihnen Werte in dem im Code gezeigten Always-Block zugewiesen habe. Ist das das Problem?? Soll ich diese Registrierungen in separaten Modulen definieren und sie dann im Block der obersten Ebene instanziieren?
Ich stimme diesem Rat zu. Ihr Programmierstil ist sehr gefährlich. Trennen Sie die kombinatorische Logik in einen eigenen prozeduralen Block mit blockierenden Zuweisungen. Die getaktete Prozedur sollte nichts anderes tun als state <= nextstate;
@JoeHass Dieser Verfahrensblock ist hauptsächlich sequentiell. Ich weise Reg-Datentypen abhängig von einem Eingangssignal Werte zu. Eine Aufteilung würde bedeuten, einige Registermodule mit Load-Enable-Eingängen zu definieren und diese Load-Enable-Signale von diesem Block aus zu steuern. Aber wird es dann nicht so sein, als würde man nach Schaltplänen entwerfen?
@Tim, mein Fehler. Ich habe die Tatsache verpasst, dass OP trotzdem j erhöht.

Ich habe im XTS-Benutzerhandbuch keine Erwähnung darüber gefunden, dass initialBlöcke die richtige Methode zum Initialisieren von Registerwerten sind .

Andererseits fand ich auf Seite 109 folgende Aussage: "Da Initialblöcke während der Synthese ignoriert werden, werden immer nur Blöcke diskutiert".

Nun, es scheint, dass Ihr Synthese-Tool die Initialisierung von Registern in Blöcken nicht unterstützt initial. Sie haben zwei Möglichkeiten:

  1. Definieren Sie ein geeignetes Reset-Signal und verwenden Sie es, um Ihr Design zu initialisieren (siehe andere Antworten für ausführlichere Erklärungen).
  2. Verwenden Sie die unterstützte Syntax für die Initialisierung mit internem Reset (Seite 104):reg [3:0] arb_priority = 4'b1011;

Generell rate ich dringend davon ab, initialStatements zur Initialisierung von Werten zu verwenden (außer zur Initialisierung von SRAM-Inhalten und Initialisierung von Testbench-Signalen).