LUTRAM Adresskollision

OP #7688961
Lesenswert?

TLDR: Benötigt man bei Xilinx LUTRAM auf der Ultrascale-Architektur zwingend einen externen Mechanismus zur Vermeidung von Adresskollisionen?

Ich bin gerade darüber gestolpert, dass Vivado für einen per xpm instanziierten RAM mit der Einstellung "auto" einen distributed RAM statt eines erwarteten BRAM erzeugt hat. Der RAM ist dual-port mit unabhängigen Takten. Im timing report gab es eine Violation zwischen diesen Ports.

Im UG974 [1] bin ich fündig geworden, wie das zu lösen ist (siehe unten). Man muss für LUTRAM ein false_path constraint setzen. Allerdings steht dort explizit dabei, dass das Design sich um Adresskollisionen kümmern muss.

Ich bin mir nicht sicher, wie das zu interpretieren ist. Im Normalfall sind mir Adresskollisionen eigentlich egal, weil ich per Design sicher bin, dass ich das Ergebnis der Leseoperation nie brauche, wenn gerade auf derselben Adresse geschrieben wird. Im Grunde geht es mir nur darum, dass die Schreiboperation auch durchgeführt wird, auch wenn die Leseadresse zufällig gerade auf derselben Adresse steht. Bei BRAM ist das ja auch kein Problem.

Bei LUTRAM könnte das evtl. anders sein, da dass den Speicher der LUTs nutzt. Dort könnte je nach interner Implementierung etwas undefined werden, wenn Schreib- und Leseoperation auf den gleichen Speicher gehen. Hat jemand Erfahrungen, ob das wirklich so ist (für die Ultrascale-Architektur)? D.h. muss ich mich wirklich um einen externen Adresskollisionsmechanismus kümmern? Laut UG574 [2] sieht das eigentlich unkritisch aus. Auf die Simulation will ich da auch nicht vertrauen, weil das Modell u.U. nicht alles unterstützt, was in HW passiert (wie z.B. bei den WRITE_MODEs). Der WRITE_MODE wäre mir aber auch egal, da ich das Ergebnis einer Leseoperation auf derselben Adresse zum gleichen Zeitpunkt wie gesagt nicht brauche.

1
set_false_path constraint is needed for the independent clock distributed RAM based memory if the design takes care of avoiding address collision (write address != read address at any given point of time). Set USE_EMBEDDED_CONSTRAINT = 1 if XPM_MEMORY needs to take care of necessary constraints. If USE_EMBEDDED_CONSTRAINT = 0, Vivado may trigger Timing-6 or Timing-7 or both. Alternatively, you can also add the constraint when USE_EMBEDDED_CONSTRAINT = 0. An example of adding this constraint is provided below. If Port-B also has write permissions for an Independent clock configuration, then a similar constraint needs to be added for clkb as well.
2

3
set_false_path -from [filter [all_fanout -from [get_ports clka]
4
-flat -endpoints_only] {IS_LEAF}] -through [get_pins -of_objects
5
[get_cells -hier * -filter {PRIMITIVE_SUBGROUP==LUTRAM ||
6
PRIMITIVE_SUBGROUP==dram || PRIMITIVE_SUBGROUP==drom}]
7
-filter {DIRECTION==OUT}]

[1] https://docs.amd.com/r/en-US/ug974-vivado-ultrascale-libraries/XPM_MEMORY_TDPRAM

[2] https://docs.amd.com/v/u/en-US/ug574-ultrascale-clb

(Firma: Starfleet) #7689178
Lesenswert?

Für Ultrascale müsst ich erst recherchieren, bei älteren Architekturen kann man doch den Synchronisationsmodus explizit angeben: write-first, read-first, no-change.

https://docs.amd.com/r/en-US/ug901-vivado-synthesis/Block-RAM-Read/Write-Synchronization-Modes

Ist jetzt mit addresskollision gemeint, das der RAM zwei unabhängige ports hat und an beiden die selbe addresse anliegt ?! Und LUT-RAM ist das selbe wie distributed-RAM ? (Der Link oben ist für BRAM)

In folgenden Forum-beitrag wird auf die Möglichkeit des asynchronen read bei distributed hingewiesen, was das selbe wie erite first wäre: https://www.eevblog.com/forum/fpga/xilinx-slicem-luts-as-distributed-rams-read-first-write-first-or-selectable/

IMHO sollte man zuerst versuchen einen BRAM zu "erzwingen", ggf mit direkter Instantiierung des BRAM-Makros oder eines generierten memory-IP-Cores.

#7689327
Lesenswert?

UG574 passt schon. Das RAM sind dann die LUTs im SLICEM. Das hat 8 LUTs mit 6 Eingängen, also 8*64 Bit.

So ein RAM hat aber immer nur einen Takteingang. Weil das Lesen aber asynchron passiert, kannst du die Leseadresse mit einem anderen Takt erzeugen. Und mit diesem Takt kannst du dann auch die gelesenen Daten in einer Registerstufe takten.

Man kann jetzt aus diesen RAMs recht flexibel zusammenbauen was man will. Singleport, Dualport, ...

Wobei man aufpassen muss mit den Bezeichnungen. Dualport heißt das im UG schon, wenn es eine getrennte Schreib- und Leseadresse gibt. Damit kann man also nicht gleichzeitig an zwei verschiedene Adressen schreiben.

Das set_false_path sagt doch eigentlich nur, dass die Timinganalyse das nicht beachten soll. Und das willst du in dem Fall ja auch. Weil die Leseadresse aus einer anderen Taktdomäne stammt gibt es kein definiertes Timing zwischen den Schreibdaten und Lesedaten. Daten werden getaktet geschrieben und liegen dann nach dem einen Takt im Speicher. Und erst danach können die auch korrekt gelesen werden.

OP #7690941
Lesenswert?

Danke für eure Antworten.

Bradward B. schrieb:

Für Ultrascale müsst ich erst recherchieren, bei älteren Architekturen kann man doch den Synchronisationsmodus explizit angeben: write-first, read-first, no-change.

Ja, bei Ultrascale auch, allerdings kann distributed RAM nativ erstmal nur read-first (bzw. liest asynchron). Aber der Schreibmodus ist mir wie gesagt egal.

Bradward B. schrieb:

Ist jetzt mit addresskollision gemeint, das der RAM zwei unabhängige ports hat und an beiden die selbe addresse anliegt ?! Und LUT-RAM ist das selbe wie distributed-RAM ?

Ja und ja.

Bradward B. schrieb:

In folgenden Forum-beitrag wird auf die Möglichkeit des asynchronen read bei distributed hingewiesen, was das selbe wie erite first wäre:

Ich denke, das ist read first.

Bradward B. schrieb:

IMHO sollte man zuerst versuchen einen BRAM zu "erzwingen", ggf mit direkter Instantiierung des BRAM-Makros oder eines generierten memory-IP-Cores.

So ein xpm-template ist ja ein Makro. Ich muss etwas mit BRAM sparen und für solche schmalen RAMs wie 512x4Bit verwendet Vivado bei der Einstellung "auto" dann LUTRAM.

Gustl B. schrieb:

Das set_false_path sagt doch eigentlich nur, dass die Timinganalyse das nicht beachten soll. Und das willst du in dem Fall ja auch.

Ja, das will ich. Mich wundert eben nur diese Aussage in UG974 "(write address != read address at any given point of time)". Das klingt so, als müsste man sich da eben drum kümmern, auch wenn es einen eigentlich per Design nicht interessiert. Und dann müsste ich eben Zauber zwischen den Taktdomänen machen, da Leseadresse und Schreibadresse einen verschiedenen Takt haben.

Aber gut, ich muss das wahrscheinlich einfach langfristig ausprobieren. Bis jetzt läuft es.

Interessant ist nebenbei auch, dass Vivado auf der Einstellung "auto" einen LUTRAM mit write mode "NO_CHANGE" ohne Fehlermeldung nimmt (warnings habe ich noch nicht geschaut). Stelle ich explizit "distributed" ein, gibt es eine Fehlermeldung, dass ich zwingend auch "read-first" einstellen soll.

#7690974
Lesenswert?

Markus W. schrieb:

Das klingt so, als müsste man sich da eben drum kümmern

Nein, musst du nicht. Lesen ist asynchron, es kommen eben immer die Daten raus die an der Leseadresse liegen. Und genau während eines Schreibvorgangs kann man nicht genau sagen wie lange noch die alten und wann die neuen Daten rauskommen. Aber wenn du damit leben kannst dann ist das völlig OK.

(Firma: Starfleet) #7691610
Lesenswert?

Gustl B. schrieb:

Das set_false_path sagt doch eigentlich nur, dass die Timinganalyse das nicht beachten soll. Und das willst du in dem Fall ja auch.

Ja, das will ich. Mich wundert eben nur diese Aussage in UG974 "(write address != read address at any given point of time)". Das klingt so, als müsste man sich da eben drum kümmern, auch wenn es einen eigentlich per Design nicht interessiert. Und dann müsste ich eben Zauber zwischen den Taktdomänen machen, da Leseadresse und Schreibadresse einen verschiedenen Takt haben.

Bloß nicht selber zwischen den Tahtdomäinen "zaubern" wenn man statt dessen eine erprobte Lösung des Toolchainherstellers wie bspw. ein FIFO-Makro nehmen kann.

Im Gesamt-text lese ich jetzt nicht so, das man diese Kollision unbedingt vermeiden müßte, da steht eher, das wenn man diese Adress-Kollision vermeidet (durch spezielle Hardware [Komperator] zwischen den Addressen der beiden Ports) man auch set_fals_path setzen muß - was IMHO selbstverständlich ist, weil eben die Timingt Analyse die mit dieses set_fakse_path gesteuert wird, das Szenario unabhängige clocks nicht lösen kann.

"set_false_path constraint is needed for the independent clock ... if the design takes care of avoiding address collision ..."

Timing analyse kommt eben mit Pfaden zwischen verschiedenen Clock domains nicht zurecht, weil eben die Grundvoraussetzung für die Anwendung der Timing-analyse (quell und ziel in clockdomains mit fixen phasen-bezug) nicht erfüllt ist.

Aber gut, ich muss das wahrscheinlich einfach langfristig ausprobieren. Bis jetzt läuft es.

Wenn man mit "langfristig ausprobieren" eigentlich "Qualifikation im Dauerlauf" meint ist das sicher eine gute Entwicklungsstrategie. Um mehrere Dauerläufe während der Entwicklung untereinander vergleichen zu können, sollte man möglichst von "auto"-Einstellungen wegkommen. Gerade beim Placement/mapping gibt es Fälle wo ein Design funktioniert, wenn der LUT's "Rechts oben" genommen werden, aber "weniger" zuverlässig sind wenn verstreute LUT's genutzt werden.

Interessant ist nebenbei auch, dass Vivado auf der Einstellung "auto" einen LUTRAM mit write mode "NO_CHANGE" ohne Fehlermeldung nimmt (warnings habe ich noch nicht geschaut). Stelle ich explizit "distributed" ein, gibt es eine Fehlermeldung, dass ich zwingend auch "read-first" einstellen soll.

Vielleicht ist es ja funktional weitgehend das selbe, wenn etwas zuerste gelesen wird, dann wird es ja auch in diesen Moment nicht geändert, ganz im Unterschied zu "write_first". den Unterschied zu "no_change" würde man erst beim zweiten Lesen von der selben Adresse bemerken - allerdings gibt es bei FIFO als Speicherkonfigurationen mit automatischer Address-Weiterschaltung dieses Szenario (unmittelbar wiederholtes Lesen von der selben Adresse nicht). Und wenn die Tools keine Ferhlermeldung ausgeben, heisst es nicht, das es funktional korrekte Einstellung ist. Es heisst lediglich, das es so im FPGA einstellbar ist und die Tollss davon ausgehen, das der user schon weiss warum er diese ("unsinnigen" aber möglichen) Einstellungen so wählt.

Antwort schreiben

Bitte melde dich an, um einen Beitrag zu schreiben.

oder

Mit Google-Account einloggen

Die Registrierung ist kostenlos und dauert nur eine Minute.

Jetzt registrieren