Hallo, ich habe nun ein Beispiel für ein FIFO im BlockRAM gesucht, bin aber nicht wirklich fündig geworden. Hat jemand ein Beispiel für mich? ;-) (Ziel: 16bit breit, ca. 16Stellen tief) Ansonsten habe ich mir überlegt, das FIFO in etwa wie folgt aufzubauen: Aufbau: - vollständig synchron, gleicher Takt für Lesen und Schreiben - nicht zeitkritisch, full/empty usw. Signale müssen nicht schon innerhalb von einem Taktzyklus oder so aktualisiert sein - BlockRAM als Speicher nehmen: RAMB_S16_S16 - überzählige RAM Zellen einfach nicht adressieren (oder FIFO ohne Zusatzaufwand bis zu 1k Tiefe auslegen) Lesen/Schreiben: - FIFO Count mit Zählbereich der FIFO Tiefe - Schreibadresse 2^n Bit breit - Leseadresse 2^n Bit breit - Write Impuls zum Schreiben von Daten - bei jedem Write Impuls: Write Adresse um 1 erhöhen -> darf auch überlaufen auf 0, FIFO Count um 1 erhöhen - Leseadresse 2^n Bit breit - Read Impuls zum Lesen von Daten -> in Register übernehmen - bei jedem Read Impuls: Read Adresse um 1 erhöhen -> darf auch überlaufen auf 0, FIFO Count um 1 vermindern Flusskontrolle: - Empty Flag: 0 wenn FIFO Count = 0, sonst 1 - Full Flag: 1 wenn FIFO Count = Maxwert, sonst 0 - Sperren der weiteren Write Impulse bei Full, Setzen eines Overflow Flags - Sperren der weiteren Read Impulse bei Empty, 0 als Resultat liefern Initialzustand: - beide Adressen auf 0 - Counter auf 0 - Empty Flag auf 1 - Full Flag auf 0 Würde das FIFO so wohl einigermassen gut funktionieren? Habe ich etwas Wichtiges vergessen? Viel einfacher kann man das wohl nicht aufbauen. Hat jemand für dieses FIFO ein besseres Konzept? Oder hat gar jemand eine Vorlage für mich ;-) Gruss, Martin Kohler
Hallo Martin, wenn du Webpack (Xilinx) oder Quartus (Altera) verwendest, dann kannst du mit dem IP-Wizard ein komplettes FIFO erzeugen lassen - ist eine Standardkomponente. Wenn's Hausaufgabe für die Uni sein soll, dann sehen die fertige IP-Cores wohl weniger gerne ;-) Grüße Thomas
Gast wrote: > Wenn's Hausaufgabe für die Uni sein soll, dann sehen die fertige > IP-Cores wohl weniger gerne ;-) Wäre doch mal ein netter Versuch... Nein, ich bin hier auf Arbeit dran, das FIFO zu entwerfen und zu implementieren. Das Problem an den fertigen IP Cores ist, dass hier in der Firma die Meinung vorherrscht, dass möglichst vieles möglichst plattformunabhängig sein soll. Der Chef sieht lieber ein in VHDL auscodiertes FIFO als ein Interface mit angehängtem .ncd File aus dem Core Genrator. Naja, die Verfechter der reinen Lehre... Mir kanns recht sein, so kann ich mich VHDL-technisch etwas lernen dabei, auch dank dieses Forums.
Platformunabhängig, aber dann doch den BlockRam? Der wird doch auch bei jedem FPGA Hersteller leicht anders sein. Das ist ein unsinniges Argument. Und kostet massig Zeit, was ja auch massig Geld ist.
Christian R. wrote: > Platformunabhängig, aber dann doch den BlockRam? Wenn ich den FIFO Speicher als Array definiere, dann nehme ich an, dass XST dies in ein BlockRAM umsetzt. Das wäre dann die "noch reinere Lehre", als wenn ich RAMB_S16_S16 im VHDL Code verwende.
1 | |
2 | |
Gibt es evtl. noch Rückmeldungen zum konzeptionellen Aufbau meines FIFOs? Oder hat gar einer einen VHDL Beispielcode? Über Sinn oder Unsinn möchte ich mich nicht weiter unterhalten, wie gesagt, die "reine Lehre"...
> Wenn ich den FIFO Speicher als Array definiere, dann nehme ich an, dass XST dies
in ein BlockRAM umsetzt.
Das wage ich zu bezweifeln.
Duke Scarring wrote: > Sowas?: > http://mysite.verizon.net/miketreseler/sync_fifo.vhd Ja. Sieht auf den ersten Blick gut aus, ist evtl. noch etwas zu vereinfachen. lala wrote: > Das wage ich zu bezweifeln. Begründung? Erfahrung? -> meine Aussage basierte auf entsprechendem Erfahrungswert für das angegebene Array, welches 8mal vorhanden war in dem Design.
>... FIFO Count um 1 erhöhen ... >... FIFO Count um 1 vermindern ... Aber aufpassen, wenn gleichzeitig gelesen und geschreiben wird. Die letzte Zuweisung im Prozess zählt ;-) Lass den Fifo-Count weg, der bringt dich irgendwann in Bedrängnis. Wenn du einen Schreib-Pointer hast, und einen Lese-Pointer, dann kannst du dir den Fifo-Count leicht aus den aktuellen Pointer-Werten ausrechnen. Z.B.: Fifo mit 16 Elementen (Zweierpotenzen sind gut für den Wrap-Around) Wenn WR_Addr = RD_Addr --> Fifo leer Wenn WR_Addr /= RD_Addr --> mindestens 1 Element ist im Fifo Wenn (WR_Addr>RD_Addr) Anzahl der Elemente <= WR_Addr - RD_Addr sonst Anzahl der Elemente <= 16 + WR_Addr - RD_Addr > Wenn ich den FIFO Speicher als Array definiere, dann nehme ich an, > dass XST dies in ein BlockRAM umsetzt. Ein 16x16Bit tiefes Fifo im BRAM ist eigentlich Ressourcenverschwendung. Wie auch immer: Im ISE Menüpunkt EDIT --> Language Templates und dann VHDL --> Synthesis Constructs --> Coding Examples --> RAM --> Block Ram --> Dual Port findet sich ein DP-RAM mit 1 Schreib und Leseport. Der dürfte eigentlich gut geeignet sein. EDIT: >> http://mysite.verizon.net/miketreseler/sync_fifo.vhd > Sieht auf den ersten Blick gut aus, ist evtl. noch etwas zu vereinfachen. Naja, der Mensch hat das mit dem Enable-Signalen noch nicht so richtig inne:
1 | |
Meine Fifos pushen und poppen solange WR bzw. RD aktiv (enabled) sind, nicht auf irgendwelchen Flanken dieser Signale.
Lothar Miller wrote: > Lass den Fifo-Count weg, der bringt dich irgendwann in Bedrängnis. Mach ich. > Wenn du einen Schreib-Pointer hast, und einen Lese-Pointer, dann kannst > du dir den Fifo-Count leicht aus den aktuellen Pointer-Werten > ausrechnen. Wäre dann einfacher als einen Counter mitzuschleppen, ja. > Wenn WR_Addr = RD_Addr --> Fifo leer
1 | |
--> klar > Wenn (WR_Addr>RD_Addr) > Anzahl der Elemente <= WR_Addr - RD_Addr > sonst > Anzahl der Elemente <= 16 + WR_Addr - RD_Addr So was in der Art? (Pseudocode)
1 | |
full lässt sich nun aber nicht einfach so beschreiben, da sind die Adressen ja auch wieder gleich. > Ein 16x16Bit tiefes Fifo im BRAM ist eigentlich Ressourcenverschwendung. ist aber immer noch besser, als das BRAM brach liegen zu lassen ;-) > Im ISE Menüpunkt EDIT --> Language Templates und dann > VHDL --> Synthesis Constructs --> Coding Examples --> RAM --> Block Ram > --> Dual Port findet sich ein DP-RAM mit 1 Schreib und Leseport. > Der dürfte eigentlich gut geeignet sein. ??? Dort befindet sich die Vorlage für die Ansteuerung (r/w) des RAM, nicht aber das RAM selbst. >
1 | |
> Meine Fifos pushen und poppen solange WR bzw. RD aktiv (enabled) sind, > nicht auf irgendwelchen Flanken dieser Signale. Meine Designs arbeiten auch pegelgesteuert, zur Ansteuerung werden Triggerpulse generiert, welche genau 1 Clock lang sind. --> ist einfacher als zu realisieren als flankengesteuert.
> So was in der Art? (Pseudocode) So sollte es passen. Achte auf signed und unsigned bei den Adressen. Ich lasse auch gern die Adressen auf 32 Bit laufen und nehme nur die unteren Bits zum Adressieren. Als "Abfallprodukt" ergibt sich dann die Zahl der übertragenen Daten. Aber auch hier mußt du auf den Überlauf achten. > full lässt sich nun aber nicht einfach so beschreiben, da sind die > Adressen ja auch wieder gleich. Ja, dann melde FULL, wenn der Schreibindex eines hinter dem Leseindex ist ;-) So richtig "saugend" voll sollte dein Fifo sowieso nicht werden, denn sonst reicht entweder die Fifo-Tiefe nicht oder die Verarbeitungsbandbreite ist zu gering. > Dort befindet sich die Vorlage für die Ansteuerung (r/w) des RAM, nicht > aber das RAM selbst. Das RAM selbst ist auch in diesem Vorlagen-Zweig, es ist ein einfach Array von Vektoren.
Lothar Miller wrote:
> Achte auf signed und unsigned bei den Adressen.
Ich habe vor, die Adressen als std_logic_vector zu definieren und das
DPRAM mit conv_integer(vector) zu addressieren.1 | |
Kann man eigentlich direkt mit std_logic_vector rechnen? Für das empty Flag passt das locker ( = Operator), der Test auf "full when rd = wr + 1", geht das auch? > Ich lasse auch gern die Adressen auf 32 Bit laufen und nehme nur die > unteren Bits zum Adressieren. Als "Abfallprodukt" ergibt sich dann die > Zahl der übertragenen Daten. Aber auch hier mußt du auf den Überlauf > achten. Gute Idee. Danke. > Ja, dann melde FULL, wenn der Schreibindex eines hinter dem Leseindex > ist ;-) > So richtig "saugend" voll sollte dein Fifo sowieso nicht werden, denn > sonst reicht entweder die Fifo-Tiefe nicht oder die > Verarbeitungsbandbreite ist zu gering. Klar.
> Kann man eigentlich direkt mit std_logic_vector rechnen?
Nicht, wenn du die numeric_std Lib verwendest. Dafür gibts dann Casts
nach unsigned/singend und Konvertierungen nach integer und zurück. Aber
für einen Array-Zugriff brauchst du sowieso einen Integer. Warum nicht
die Adressen gleich als Integer verwalten?
Good old school style mit1 | |
2 | |
3 | |
4 | |
5 | |
mit der herstellerunabhängigen numeric_std
1 | |
2 | |
3 | |
4 | |
> "full when rd = wr + 1", geht das auch?
Mit integer und unsigned geht das. Aber du solltest dir nochmal die
Überlaufgrenzen ansehen. Denn auch hier kann z.B. rd=15 sein und wr=0.
Lothar Miller wrote: > Warum nicht die Adressen gleich als Integer verwalten? Wahrscheinlich weil ich bisher für Adressierungen immer std_logic_vector verwendet habe. Würdest du die Adressen denn im gesamten Design als Integer verwalten oder nur im FIFO Modul drin? > mit der herstellerunabhängigen numeric_std herstellerunabhängig ist immer gut ;-) > Aber du solltest dir nochmal die Überlaufgrenzen ansehen. Hab ich noch nicht getan. Ich werde dann mal ein FIFO Modul samt passender Testbench erstellen, dann sehen wir weiter.
> Würdest du die Adressen denn im gesamten Design als Integer verwalten > oder nur im FIFO Modul drin? An den Schnittstellen habe ich immer std_logic. Innerhalb der Module wird das dann sofort auf ein Integer-Signal zugewiesen, und mit dem weitergearbeitet.
Lothar Miller wrote: > An den Schnittstellen habe ich immer std_logic. Dann bin ich beruhigt ;-) > Innerhalb der Module wird das dann sofort auf ein Integer-Signal > zugewiesen, und mit dem weitergearbeitet. Gut, werde ich auch so versuchen. wobei - die Adressen brauch ich ja ausserhalb des Moduls gar nicht, da kann ich auch gleich durchgängig mit "integer" resp. "natural" arbeiten. Gibt es einen Vorteil von natural gegenüber integer, wenn bei beiden der Range "0 to 255" definiert ist?
> Gibt es einen Vorteil von natural gegenüber integer, wenn bei beiden der > Range "0 to 255" definiert ist?
1 | |
Du schränkst damit nur den eingeschränkten Wertebereich von natural noch weiter ein. Also Antwort: nein.
Lothar Miller wrote: >> Kann man eigentlich direkt mit std_logic_vector rechnen? > Nicht, wenn du die numeric_std Lib verwendest. Dafür gibts dann Casts > nach unsigned/singend und Konvertierungen nach integer und zurück. Aber > für einen Array-Zugriff brauchst du sowieso einen Integer. Warum nicht > die Adressen gleich als Integer verwalten? Ich habe nun versucht, die Adressen mit Integer zu verwalten:
1 | |
2 | |
3 | |
4 | |
5 | |
6 | |
7 | |
8 | |
9 | |
10 | |
11 | |
12 | |
13 | |
14 | |
15 | |
16 | |
17 | |
18 | |
Das funktioniert in der Simulation auch ganz wunderbar - bis zu Adresse 15. Sehe ich das richtig, dass sich die als Integer verwalteten Adressen nur bis vor den Überlauf verwalten lassen? Der Überlauf führt im ModelSim zu Fehler "# ** Fatal: (vsim-3421) Value 16 for addr_wr is out of range 0 to 15." Wie kann ich nun das Design auch mit Überlauf simulieren? Doch wieder std_logic_vector für die Adresse verwenden?
Martin Kohler wrote: > Wie kann ich nun das Design auch mit Überlauf simulieren? > Doch wieder std_logic_vector für die Adresse verwenden? Nein, nimm für den Überlauf einen unsigned (numeric_std) ;-) Probiers mal so:
1 | |
wenn du die alten Synopsys-Libs verwendest, mußt du auf std_logic_vector und zurück casten. Alternativ kannst du auch schreiben:
1 | |
2 | |
3 | |
4 | |
5 | |
Da wäre ich mal gespannt, ob der Synthesizer rafft, dass er einfach weiterzählen kann. Ich werde das in der Mittagspause mal probieren ;-) Schreib ein Speicherarray statt:
1 | |
besser so:
1 | |
Dann kannst du das Speicherarray in der "gewohnten" Reihenfolge mit := initialisieren.
Hier wie versprochen der Test der beiden Beschreibungen:
1 | |
2 | |
3 | |
4 | |
5 | |
6 | |
7 | |
8 | |
9 | |
10 | |
11 | |
12 | |
13 | |
14 | |
15 | |
16 | |
17 | |
18 | |
19 | |
20 | |
21 | |
22 | |
23 | |
24 | |
25 | |
26 | |
27 | |
28 | |
29 | |
30 | |
31 | |
32 | |
Und das Ergebnis der Synthese (Screenshot): Der Synthesizer setzt wie erwartet die beiden Beschreibungen in einen 4-Bit-Zähler (mit Wrap-Around) um.
Ich bin nun endlich dazu gekommen, das FIFO zu codieren, inkl. Testbench natürlich... Kommentare dazu?
die Testbench
... und noch die Control Signale
Jetzt hab ich doch tatsächlich den lesenden Zugriff nicht synchron gestaltet, so dass der Synthesizer mir ein distributed RAM bauen wollte... der process sieht nun so aus:
1 | |
2 | |
3 | |
4 | |
5 | |
6 | |
7 | |
8 | |
9 | |
10 | |
11 | |
12 | |
13 | |
14 | |
15 | |
16 | |
17 | |
18 | |
19 | |
20 | |
21 | |
22 | |
23 | |
24 | |
25 | |
26 | |
27 | |
28 | |
29 | |
30 | |
31 | |
32 | |
33 | |
34 | |
35 | |
36 | |
Hallo an alle !!!
ich habe mich direkt gefreut, als ich hier gelandet bin. Extrem hilfreich. Aber ich hätte dazu noch eine Frage. Ich möchte ein Block RAM mit diesem FIFO betreiben. Meine Frage lautet: brauche ich extra einen Kode für das RAM selbst? Falls ja was für einen RAM-Kode (FIFO-Konfiguration, dualport blockram Konfiguration). Entschuldigen Sie, ob es eine dumme Frage ist, ich bin gerade in die FPGA- Welt eingestiegen.
ich bin gerade in die FPGA- Welt eingestiegen.
Sieh dir den User Guide deines Synthesizers an. Da drin sind die Möglichkeiten zur Instantiierung eines BlockRAMs aufgeführt.
Der Chef sieht lieber ein in VHDL auscodiertes FIFO als ein Interface mit angehängtem .ncd File aus dem Core Genrator.
Dafür gibt es auch gute Gründe. Neben der Portierbarkeit geht es vor allem um die Loslösung von IPCores. Da gibt es einige Firmen, die von Core-GEN Dateien wegwollen (oder "müssen").
Bei den FIFOs und BRAMs ist es allerdings so, dass dort zu einem großen Teil echte hardware genutzt wird, z.B. für die Counter, Resets und die Dekoder. Daher kann man nicht alles so wie vom Hersteller gedacht nutzen, wenn man das nicht sinnvoll instanziiert.
Es kann daher passieren, dass man in VHDL etwas hinschreibt, was nicht zu einer optimalen Struktur führt und man hinter dem zurückbleibt, was durch konkrete Nutzung eines IP Makros und Anpassung an dieselbe möglich gewesen wäre. Ungeschickte Bildung der Gray-Counter und deren Abfragen zum Vergleich sind solche Kandidaten. Generell fährt man, wenn man dedizierte FIFOs benutzt, besser, weil sie schneller takten, als BRAMs. Wenn man aber zu viel drum herumbaut wird es wieder schlechter und man ist mit händisch gebauten FIFOs aus Block-RAMs besser dran. Gilt aber nur, wenn man seine Block-RAMs genau kennt und optimal nutzt.
Ganz generell kann man IP Cores nur dann toppen, wenn man wirklich genau weiss man man tut.
Generell fährt man, wenn man dedizierte FIFOs benutzt, besser, weil sie schneller takten, als BRAMs.
In welchem FPGA gibt es dedizierte FIFOs? Das ist doch immer normales BRAM das auch als FIFO verwendet werden kann. Der IP Core bei Xilinx macht das nicht anders.
J. S. schrieb:
Generell fährt man, wenn man dedizierte FIFOs benutzt, besser, weil sie schneller takten, als BRAMs.
In welchem FPGA gibt es dedizierte FIFOs? Das ist doch immer normales BRAM das auch als FIFO verwendet werden kann. Der IP Core bei Xilinx macht das nicht anders.
Hab ich auch noch nicht gehört - kenne vieles aber auch nicht 😂
Dachte das wird einfach immer als Dual-Port-RAM mit ein bisserl Ansteuerung außenrum implementiert 🤔
J. S. schrieb:
Generell fährt man, wenn man dedizierte FIFOs benutzt, besser, weil sie schneller takten, als BRAMs.
In welchem FPGA gibt es dedizierte FIFOs? Das ist doch immer normales BRAM das auch als FIFO verwendet werden kann. Der IP Core bei Xilinx macht das nicht anders.
Hab ich auch noch nicht gehört - kenne vieles aber auch nicht 😂
Da muß man über ein Jahrzehnt "weggehört haben" wenn bswp. Xilinx von den dedizierten IO-Fifos in den IO-Zellen der Series Seven spricht. (s. UG471, p. 173) https://docs.xilinx.com/v/u/en-US/ug471_7Series_SelectIO (Ausschnitt im Anhang).
Dachte das wird einfach immer als Dual-Port-RAM mit ein bisserl Ansteuerung außenrum implementiert
Falsch/garnicht gedacht, resp. schlecht/garnicht recherchiert.
Der FIFO-Generator bietet (schon immer) die Möglichkeit an, den FIFO ggf. aus distributed RAM oder SRL16 (SRL16E, SRL32) shiftregister macros zu bauen. (Siehe angehangenes snippet aus UG175 (http://wla.berkeley.edu/~cs150/fa13/resources/fifo_generator_ug175.pdf)).
Und "bisserl Ansteuerung" drumherum ist mindest im Fall von Synchronisierungs-ff "spezielle" "integrated control-logic", die sich eben nicht mit ein bisserl 0815-C-programmer-like VHDL generieren lässt. Für FPGA's muß man eben Schaltungstechnik und deren typische Probleme wie bspw. CDC (clock Domain Crossing) und simultanous read/write bei Dualport kennen.
Ungeschickte Bildung der Gray-Counter und deren Abfragen zum Vergleich sind solche Kandidaten. Generell fährt man, wenn man dedizierte FIFOs benutzt, besser, weil sie schneller takten, als BRAMs.
Eben. Wobei hier der Begriff "dedizierte FIFO" mehrdeutig ist, weil er zu einem "generierte (Netzlisten-)Fifo aus dem Core-Generator" meinen kann, wie eben auch die dedizierten "(IO)-Fifos".
Für die Selbsterkenntnis der eigenen Unfähigkeit kann man ja mal versuchen, eine Fifo mit unabhängigen read/write-clocks in VHDL zu basteln, bei denen die Flag-Generierung "Full/empty") tadellos funktioniert. Da read wie write-pointer in eigenen clock domainen laufen ist ein Vergleich beider Latenzkorrekt nicht so trivial, wie es sich anhören mag. Und es gibt im Bereich counter robustere und schnellere Implementierungen als der Standard Binärcounter der durchschnittlichen VHDL-Coder. Stichwort "Gray-encoding" und LSFR.
Der FIFO-Generator bietet (schon immer) die Möglichkeit an, den FIFO ggf. aus distributed RAM oder SRL16 (SRL16E, SRL32) shiftregister macros zu bauen.
Für einen FIFO würde ich beides nicht benutzen ...
Distributed RAM macht man eh nur, wenn es umbedingt sein muss - weil man zB keine Blockrams mehr hat.
und ein BRAM als Ringpuffer mit ein paar Pointern und bisserl FIFO Logik implementiert ist besser als Distributed RAM oder SLRs.
Aber ich würde das nicht mehr selbst machen, aber ganz stark davon ausgehen, dass der IP-Wizard genau das macht.
Xilinx von den dedizierten IO-Fifos in den IO-Zellen der Series Seven spricht.
Tatsache, die gibt es und sie sind sehr klein.
die sich eben nicht mit ein bisserl 0815-C-programmer-like VHDL generieren lässt.
So sollte man generell kein VHDL schreiben. Aber man kann es selber schreiben und es funktioniert wenn man es richtig macht.
Antwort schreiben
Bitte melde dich an, um einen Beitrag zu schreiben.



