Warum gibt der Xilinx-Block-RAM in einem Spartan-3E nicht konsistent Daten in einem einzigen Taktzyklus zurück?

Ich erstelle ein Design mit Verilog auf einem Xilinx Spartan-3E (XC3S500E), das mehrere Dual-Port-Block-RAMs verwendet, die alle durch Verilog-Primitive wie RAMB16_S18_S18. Ich verwende einen Port sowohl zum Lesen als auch zum Schreiben (mit Write-Enable) und den zweiten nur zum Lesen (durch Setzen WEBauf 0). Beide Ports teilen sich die gleiche Uhr. Der Block-RAM ist auf 18 Bit Breite eingestellt, aber ich ignoriere die Paritätsdaten (d. h. ich verwende nicht ihren Ausgabewert und schreibe immer Nullen in Paritätsbits).

Ich verwende Xilinx ISE 13.4 und synthetisiere/implementiere mithilfe des GUI-Workflows mit Standardeinstellungen. (Nicht-Standardeinstellungen wie aggressive Timing-Optimierung und/oder physikalische Synthese hatten keinen Unterschied in Bezug auf dieses Problem)

Ich habe Timing-Einschränkungen für mein einziges Taktnetz vorgenommen, und es stimmt mit dem tatsächlich eingegebenen Taktsignal überein (die Einschränkung gilt für 50 MHz, es gibt einen 50-MHz-Takt auf der von mir verwendeten Entwicklungsplatine, und der Timing-Bericht gibt an dass die maximale Frequenz für mein Design 64,7 MHz beträgt.Die Uhr läuft durch einen Taktmultiplexer, der als Taktfreigabe verwendet wird, bevor sie zur Gesamtheit meiner Logik geht.

In meinem Code habe ich eine Zustandsmaschine mit drei Zuständen (Übergang bei steigender Flanke desselben 50-MHz-Takts, den der Block-RAM verwendet):

  1. Adresse in ein Register schreiben, das mit den Adresseingängen der Ports A und B verbunden ist.
  2. Lesen Sie Daten, führen Sie einige logische Operationen daran aus und schreiben Sie sie in einen, reg[15:0]der mit DIA (Daten in A) im Block-RAM verbunden ist (wobei sich die Adresse nicht ändert). Schalten Sie die Schreibfreigabe für Port A ein.
  3. Lesen Sie einige IO-Pins in nicht verwandte Register. Deaktivieren Sie die Block-RAM-Schreibaktivierung.

Dies gelingt durchweg in einer Verhaltenssimulation in ISim und (obwohl es weniger Tests als der Simulator hatte) durchweg erfolgreich an Port A. Port B hat identische Logik (einfach Bit-Slicing für Adresse und dieselbe Konfiguration für SSR, SRVAL, INIT ), aber es gelingt ihm nicht, während dieses einen Taktzyklus zu lesen. Durch einfaches Hinzufügen eines zusätzlichen Zustands zwischen 1 und 2 (wodurch sich eine Einrichtungszeit von über einem ganzen Taktzyklus für die Adresse ergibt) funktioniert das Design, obwohl ich als Anfänger der FPGA-Entwicklung gerne wissen würde, warum und wie man dies verhindern kann.

Gemäß dem Xilinx-Datenblatt DS312 und den darin enthaltenen Timing-Diagrammen sollte dies eine akzeptable Art der Verwendung des Block-RAM sein. In demselben Datenblatt sind Setup- und Hold-Zeiten angegeben, aber die ISE-Tools sollten sich dieser bereits bewusst sein und sie während der Timing-Analyse anwenden, wenn ich mich nicht irre. Außerdem habe ich den Block-RAM-Abschnitt von UG331 (Spartan-3 Generation User Guide) einige Male erneut gelesen und konnte keine Inkonsistenzen zwischen den Anweisungen und meiner Verwendung des Block-RAM finden.

Die Timing-Berichtsliste der langsamsten Pfade listet auf mysteriöse Weise keine Pfade auf, die zum fehlerhaften RAM-Port führen.

Wenn jemand eine Empfehlung abgeben könnte, wäre ich dankbar, da ich eine ganze Weile damit verbracht habe, dies zu debuggen, und befürchte, dass ich einen Anfängerfehler machen könnte. Wenn zusätzliche Informationen benötigt werden, lassen Sie es mich bitte wissen, damit ich sie bereitstellen kann.

BlockRAMs haben eine Verzögerung von 2 Zyklen. Sie verwenden synchrones Schreiben und Lesen.
@Paebbels Kannst du mir zeigen, wo das in einem der Datenblätter steht? Ich habe es vielleicht übersehen, da ich mit der FPGA-Technologie und der Datenblattorganisation noch nicht zu 100 % vertraut bin. Außerdem hatte ich den Eindruck, dass gemäß dem Timing-Diagramm auf Seite 41 des von mir verlinkten Datenblatts (und nach einigen Diskussionen über IRC) die Ausgabe in zwei Zyklen verfügbar wäre, wenn nach der Ausgabe ein zusätzliches synchrones Register vorhanden wäre. was es in meinem Design nicht gibt.
@Paebbels Auch UG331: "Eine Leseoperation erfordert nur eine Taktflanke." Ich bin mir nicht sicher, ob es sich um eine Art Pipelining handelt (es wird nicht erwähnt und es stimmt auch nicht mit den identischen Zeitdiagrammen wie im Datenblatt DS312 überein).
Früher war es so, dass Sie Zeitbeschränkungen zwischen FFs und BlockRams sowohl als Start- als auch als Endpunkt explizit festlegen mussten. Wenn Sie Ergebnisse im nächsten Zyklus erhalten (BlockRams sind synchron, wie Paebbels sagt, aber auf ungewöhnliche Weise), aber inkonsistent zwischen PAR-Läufen, sehen Sie sich das an. Wenn ich eine wirklich alte UCF-Datei ausgraben kann, werde ich eine Antwort posten.
@BrianDrummond Ich werde mich darum kümmern. Da ich ein Anfänger bin, werde ich wahrscheinlich ein wenig Zeit brauchen, um die entsprechende Dokumentation zu finden und zu lesen; Ich bin mit der Semantik und Syntax von Zeitbeschränkungen nicht sehr vertraut, außer denen, die die erforderliche Frequenz für eine Uhr und alles, was dazu synchron ist, regeln.
BlockRams sind - wie ich es verstehe - ungewöhnlich darin, die Adresse (und Schreibdaten) zu pipenen, nicht die Lesedaten - daher erfordert "Lesevorgang eine Taktflanke". Sie können die Adresse ziemlich spät in einem Zyklus präsentieren (weshalb ich glaube, dass es die Adresse ist, die gepipelinet wird). Im nächsten Zyklus erhalten Sie die Daten ... nach einer erheblichen Verzögerung (was wiederum darauf hindeutet, dass die Daten NICHT gepipelinet werden) und in Geräten dieser Zeit benötigen Sie explizite Einschränkungen für die Zeitanalyse, um diese Verzögerung zu berücksichtigen.
Schlüsselwörter - Leitfaden zu Einschränkungen: cgd.pdf; ZEITSPEZIFIKATION VON BRAM NACH FF (und umgekehrt). (Aus dem Gedächtnis ist BRAM möglicherweise das falsche Wort. Die UCF-Syntax ist seltsam, inkonsistent, manchmal wird zwischen Groß- und Kleinschreibung unterschieden, und Fehler darin werden, wenn überhaupt, schlecht diagnostiziert.
@BrianDrummond Danke. Ich schaue gerade durch den Constraint Guide.
@BrianDrummond Ich bin mir nicht sicher, was ich hier einschränke. Ich gehe von einem Block-RAM zur Logik zur Eingangsadresse eines anderen RAM (über ein Register). Möchte ich eine Einschränkung von Block-RAM zu Block-RAM oder benötige ich zwei separate Einschränkungen, eine von einem Block-RAM zu einem FF und die andere von dem FF zum nächsten Block-RAM, sodass die Summe ihrer Zeiten kleiner ist als der Zeitraum (Verzögerung + Setup)?
Sie brauchen wahrscheinlich BRAM zu BRAM. Ich erinnere mich vage, dass ich FF auf FF, BRAM auf BRAM, FF auf BRAM, BRAM auf FF eingestellt habe, alles auf die gleiche Zeit wie meine Taktperiode. Das hat funktioniert. Experimentieren Sie mit der Bearbeitung von UCF und der erneuten Ausführung der Timing-Analyse – hoffentlich sehen Sie neue Fehler.
@BrianDrummond OK, danke. Ich werde es versuchen (obwohl es eine Weile dauern kann, meine ISE-Installation läuft ansonsten aufgrund von Treiberproblemen in einer langsamen VM)

Antworten (1)

Neu geschriebene Daten an der steigenden Flanke sind direkt nach dieser Flanke nur am selben Port verfügbar. Tatsächlich wird der Dateneingang intern an den Datenausgang desselben RAM-Ports weitergeleitet. WRITE_FIRSTAuch Modus genannt .

Es wird jedoch nie an den Ausgang des anderen RAM-Ports weitergeleitet, unabhängig von der angegebenen WRITE_MODE. Es steht zum Lesen zur Verfügung (natürlich bei einer weiteren steigenden Flanke), nachdem das interne Schreiben in den Speicher abgeschlossen ist. In Ihrem Beispiel ist es nur die nächste steigende Taktflanke, da die interne Schreibzeit immer kleiner (schneller) als die minimal zulässige Taktperiode ist.

Dieses Verhalten wird in XAPP 463 Using Block RAM in Spartan-3 Generation FPGAs im Abschnitt Dual-Port RAM Conflicts and Resolution beschrieben . Das dort gegebene Beispiel verwendet unterschiedliche Takte, gilt aber auch, wenn für beide Ports derselbe Takt verwendet wird.

Dieses Verhalten ist bei aktuellen FPGAs von Xilinx und Altera immer noch gleich.

Die Weiterleitung auf den anderen RAM-Port muss von Ihrer eigenen mit umgebender Logik erfolgen.

Dies ist eine ziemlich alte Frage, daher müsste ich mir das Design noch einmal ansehen, aber ich glaube nicht, dass die fragliche Adresse an beiden Ports gleich war.