Forum: FPGA, VHDL & Co. LUTRAM Adresskollision


von Markus W. (mwww)


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

von Bradward B. (Firma: Starfleet) (ltjg_boimler)


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.

: Bearbeitet durch User
von Gustl B. (gustl_b)


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.

von Markus W. (mwww)


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.

von Gustl B. (-gb-)


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.

von Bradward B. (Firma: Starfleet) (ltjg_boimler)


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.

Bitte melde dich an um einen Beitrag zu schreiben. Anmeldung ist kostenlos und dauert nur eine Minute.
Bestehender Account
Schon ein Account bei Google/GoogleMail? Keine Anmeldung erforderlich!
Mit Google-Account einloggen
Noch kein Account? Hier anmelden.