Hallo zusammen,
ich arbeite aktuell an einem Projekt für die Universität.
Ziel ist es ein Interface zwischen einem Mikrocontroller und einem FPGA
in Betrieb zu nehmen. Das Interface basiert auf dem Ansatz einen RAM im
FPGA anzulegen und diesen mit dem Memory Interface des Controllers
anzusteuern.
Nun habe ich folgende Schritte gemacht:
RAM Modul entwickelt in VHDL
RAM Interface des µC in Betrieb genommen und mit dem Logic Analyzer
alles inspiziert damit ich meine Testbench schreiben kann.
RAM in die Testbench geworfen (Sehr rudimentäre Testbench - Ich weiß
ehrlich gesagt nicht wie ich das besser machen soll?)
Was ich jetzt für Probleme habe:
Ich bekomme folgende Fehler während der Synthese die mich stutzig
machen:
(1)[Synth 8-3331] design main has unconnected port clk
(2)[Synth 8-327] inferring latch for variable 'ram_reg[0]'
["C:/Users/rddlr/Documents/Bachelor/01_Vivado/memory_interface_test_sram
/memory_interface_test_sram.srcs/sources_1/new/async_sram_tristate_new_n
Bit_test.vhd":92]
Ich bekomme folgende Fehler in der Implementierung die mich stutzig
machen:
(3)[DRC PLIO-3] Placement Constraints Check for IO constraints:
Partially locked IO Bus is found. Following components of the IO Bus
ram_addr[5:0] are not locked: ram_addr[1]
(4)Bad Practice (534)
Ich würde gerne verstehen wie diese Fehler zu stande kommen und wie ich
sie beheben kann.
Marcel schrieb:> Ich bekomme folgende Fehler während der Synthese die mich stutzig> machen:
Beides sind Warnungen. Wenn es Fehler wären, dann würde er abbrechen und
du könntest gar keine Implementierung starten.
Trotzdem macht es Sinn, wie du es hier gerade machst, sie zu verstehen.
In den Warnungen sind immer wieder sehr nützliche Informationen
versteckt.
Marcel schrieb:> (1)[Synth 8-3331] design main has unconnected port clk
Du deklarierst einen Port namens clk, der aber nicht angeschlossen ist.
Bei einem Clock üblicherweise ganz schlecht. :-)
Wenn es gewünscht ist, einen Port nicht anzuschliessen, dann kannst du
das mit dem Schlüsselwort "open" explizit hinschreiben. Dann ist die
Warnung weg und jeder weiss beim lesen des Source Codes, dass das so
sein soll.
Marcel schrieb:> (2)[Synth 8-327] inferring latch for variable 'ram_reg[0]'> ["C:/Users/rddlr/Documents/Bachelor/01_Vivado/memory_interface_test_sram> /memory_interface_test_sram.srcs/sources_1/new/async_sram_tristate_new_n> Bit_test.vhd":92]
In FPGAs gibt es eigentlich nur synchrone Designs. In einem synchronen
Design soll es nur Register und sicher keine Latches geben. Darum
meckert der Synthesizer jedes Latch an, das generiert wird.
Dein asynchrones RAM Interface ist asynchron implementiert und benötigt
darum zu recht Latches. Aber da gibt es wohl noch andere, die hierzu
diskutieren möchten.
Marcel schrieb:> (3)[DRC PLIO-3] Placement Constraints Check for IO constraints:> Partially locked IO Bus is found. Following components of the IO Bus> ram_addr[5:0] are not locked: ram_addr[1]
Tönt danach, als würde im Constraint File die Pinzuweisung für
ram_addr[1] fehlen.
Christoph Z. schrieb:> Wenn es gewünscht ist, einen Port nicht anzuschliessen, dann kannst du> das mit dem Schlüsselwort "open" explizit hinschreiben. Dann ist die> Warnung weg und jeder weiss beim lesen des Source Codes, dass das so> sein soll.
Aber nur bei einem out Port. Inputs duerfen nicht open sein!
Nick M. schrieb:> Wolltest du das wirklich so schreiben?> (2**(ADDR_WIDTH-1) - 1 downto 0)
Ja, ich denke schon. Es klingt so als würdest du da ein Problem erkennen
das ich nicht sehe.
Der RAM wird unterteilt in zwei Teile - einer ist read only und einer
write only.
Also dachhte ich mir einer geht von 0 bis 127 und der andere von 128 bis
256.
127 wären doch dann 2**(n-1)-1 oder?
Christoph Z. schrieb:> Dein asynchrones RAM Interface ist asynchron implementiert und benötigt> darum zu recht Latches.
Das ist gut zu hören - ich hatte mir das bereits gedacht war mir aber
nach dem Blogeintrag von Nandland über Latches nicht mehr sicher. Er
schreibt das er in 30 Jahren kein Design entwickelt hat, das Latches
benötigt.
Christoph Z. schrieb:> Du deklarierst einen Port namens clk, der aber nicht angeschlossen ist.> Bei einem Clock üblicherweise ganz schlecht. :-)
Genau das mache ich allerdings. Ich weise den Port in der main.vhd dem
component sram zu und im sram benutzte ich die clk in meiner sensitivity
list.
Christoph Z. schrieb:> Tönt danach, als würde im Constraint File die Pinzuweisung für> ram_addr[1] fehlen.
Alles ist zugewiesen (nach meiner Meinung).
Auszug:
Marcel schrieb:> Ich weise den Port in der main.vhd dem> component sram zu und im sram benutzte ich die clk in meiner sensitivity> list.
Aber nur in der sensitivity list, nicht in der synthetisierten
Schaltung. (d.h. innerhalb des Prozesses fragst du nicht die Taktflanke
von CLK ab).
Dementsprechend ist auch die Auflistung von clk in der sensitivity list
irreführend bis falsch: du baust hier kein Design, das taktsynchon mit
clk arbeitet.
Aber da der Name deiner Komponente mit "asynch_sram" beginnt, ist das
wohl auch gar nicht deine Absicht, ein synchrones Design zu bauen. Dann
solltest du aber auch das irreführende Signal clk weglassen und dich
nicht über die Bildung von Latches wundern.
Marcel schrieb:> Christoph Z. schrieb:>> Du deklarierst einen Port namens clk, der aber nicht angeschlossen ist.>> Bei einem Clock üblicherweise ganz schlecht. :-)>> Genau das mache ich allerdings. Ich weise den Port in der main.vhd dem> component sram zu und im sram benutzte ich die clk in meiner sensitivity> list.
Wie Achim schreibt, was du nicht brauchst, solltest du auch nicht in der
Entity auflisten.
Marcel schrieb:> Christoph Z. schrieb:>> Dein asynchrones RAM Interface ist asynchron implementiert und benötigt>> darum zu recht Latches.>> Das ist gut zu hören - ich hatte mir das bereits gedacht war mir aber> nach dem Blogeintrag von Nandland über Latches nicht mehr sicher. Er> schreibt das er in 30 Jahren kein Design entwickelt hat, das Latches> benötigt.
Hehe, ja, im Prinzip sollte das auch dein Ziel sein, keine Designs für
FPGAs zu bauen, die Latches benötigen. FPGAs und ihre Tools sind nicht
wirklich dafür vorgesehen asynchrone Interfaces zu implementieren.
Habe ich aber auch schon gemacht, weil andere vorher das so ausgesucht
hatten. Das muss mit Constraints sauber definiert werden, sonst gibt es
Überraschungen.
Falls die Zugriffszeiten des CPU es erlauben, kannst du dein SRAM
Interface im FPGA auch synchron bauen (du musst es nur schaffen,
innerhalb der erlaubten Zeit die Daten an den Ausgang zu legen).
Marcel schrieb:> Christoph Z. schrieb:>> Dein asynchrones RAM Interface ist asynchron implementiert und benötigt>> darum zu recht Latches.> Das ist gut zu hören
Das nennt sich auch "schönsaufen".
> - ich hatte mir das bereits gedacht war mir aber> nach dem Blogeintrag von Nandland über Latches nicht mehr sicher. Er> schreibt das er in 30 Jahren kein Design entwickelt hat, das Latches> benötigt.
Wenn man sein Design aus Erfahrung von vorn herein so auslegt, dass
man keine Latches benötigt, dann braucht man auch keine. Aber wenn ich
das hier so sehe(**):
1
useieee.numeric_std.all;
2
useIEEE.std_logic_unsigned.all;
3
useIEEE.std_logic_arith.all;
dann hast du dieses RAM nicht aus Erfahrung asynchron gemacht.
Denn das hier gibts im realen Leben nicht:
1
if(falling_edge(nWE)orfalling_edge(nOE))then
Alles, das mit _edge() oder 'event beschrieben wird, ergibt ein
Flipflop.
Und das, was du da beschreibst, ist ein "Dual-Clock-Input-Flipflop".
Sowas gibt es nicht.
Weil der Synthesizer das nicht kann, bastelt er irgendwie ein Latch
daraus.
Und wenn ich jetzt noch einen Schritt "nach aussen" gehe und das hier
sehe:
1
if(nCS='0')then-- if nCS = '0' the adress is valid but it can change due one write cycle
2
if(falling_edge(nWE)orfalling_edge(nOE))then
dann frage ich mich und dich: wo im Handbuch des Synthesizers steht,
dass man es so machen soll oder kann?
Achim S. schrieb:> du baust hier kein Design, das taktsynchon mit clk arbeitet.
Und das ist der erste Schritt in Richtung "Probleme!"
Marcel schrieb:> Ich weise den Port in der main.vhd dem component sram zu und im sram> benutzte ich die clk in meiner sensitivity list.
Die Sensitivliste ist dem Synthesizer schnurz. Sie wird nur vom
Simulator benutzt, um anzuzeigen, bei Änderung welcher Signale der
Prozess für die Simulation neu berechnet werden muss.
Der Synthesizer gibt bei einer falschen Sensitivliste bestenfalls eine
Meldung, dass die Simulation nicht mehr zur Implementation passt.
Christoph Z. schrieb:> Hehe, ja, im Prinzip sollte das auch dein Ziel sein, keine Designs für> FPGAs zu bauen, die Latches benötigen.
Nur Anfänger und Profis bauen Latches in ihr Design ein. Erstere aus
Versehen und zweitere nur dann, wenn es unbedingt nötig ist.
(**)
Never ever die numeric_std und die Synopsys-Libs gelcihzeitig. Du wirst
sonst eingenartige Fehler und Effekte mit Doppeldefinitionen von
Datentypen bekommen.
Achim S. schrieb:> Dementsprechend ist auch die Auflistung von clk in der sensitivity list> irreführend bis falsch: du baust hier kein Design, das taktsynchon mit> clk arbeitet.
Aha, das hatte ich mich schon länger gefragt ob ich das zusätzlich
machen muss.
Wie kann ich denn die ganze clk weglassen und dafür sorgen das die
Latches verschwinden? Hast du da einen Tipp?
Christoph Z. schrieb:> Falls die Zugriffszeiten des CPU es erlauben, kannst du dein SRAM> Interface im FPGA auch synchron bauen (du musst es nur schaffen,> innerhalb der erlaubten Zeit die Daten an den Ausgang zu legen).
Die würden es erlauben. Allerdings würde ich mich erst einmal freuen
wenn überhaupt etwas funktioniert.
Christoph Z. schrieb:> Wie Achim schreibt, was du nicht brauchst, solltest du auch nicht in der> Entity auflisten.
Könnte ich nur alle Signale die ich im if abfrage, in die Sensitivity
List schreiben?
Mir ist außerdem nicht klar geworden ob mein Design so überhaupt
funktioniert oder ich hier gerade viele falsche Wege eingeschlagen habe.
Desweiteren verstehe ich nicht ob der FPGA BRAM nutzt als Speicher oder
LUTs - weiß einer von euch wo ich das rausfinden kann?
Marcel schrieb:> Desweiteren verstehe ich nicht ob der FPGA BRAM nutzt als Speicher oder> LUTs - weiß einer von euch wo ich das rausfinden kann?
Im Synthesebericht steht irgendwas von "inferring yxz RAM" und später
taucht noch irgendwas zu "Ressources" auf.
Aber dein Design muss zwingend in LUTs abgebildet werden, weil du ja das
gesamte Array als Ausgang deklariert hast und quasi parallel darauf
zugreifen könntest:
Das kann dir ein Blockram nicht bieten. An beiden Ports kann das nur ein
einziges Wort Schreiben und Lesen. Aber auch hierzu empfehle ich das
Lesen des Syntehsizer User Guides und des Blockram User Guides.
Marcel schrieb:> Wie kann ich denn die ganze clk weglassen und dafür sorgen das die> Latches verschwinden? Hast du da einen Tipp?
Geht nicht. Wenn du was speichern willst, brauchst du entweder Flipflops
oder Latches. Flipflops brauchen einen Takt:
1
-- nebenläufig
2
wert<=datainwhenrising_edge(CSn);
3
4
-- sequentiell
5
process(CSn)
6
begin
7
ifrising_edge(CSn)then
8
wert<=datain;
9
endif;
10
endprocess;
Ein Latch ist pegelgestuert:
1
-- nebenläufig
2
wert<=datainwhenCSn='0'elsewert;
3
4
-- sequentiell
5
process(datain,CSn)
6
begin
7
ifCSn='0'then
8
wert<=datain;
9
else
10
wert<=wert;
11
endif;
12
endprocess;
BTW:
Du kannst Eingängen keinen Defaultwert geben:
1
nCS:instd_logic:='1';-- low-active Chip-Enable of the SRAM device; defaults to '1' (inactive)
2
nOE:instd_logic:='1';-- low-active Output-Enable of the SRAM device; defaults to '1' (inactive)
3
nWE:instd_logic:='1';-- low-active Write-Enable of the SRAM device; defaults to '1' (inactive)
Wie sollte dieser Defaultwert denn hardwaremäßig implementiert werden?
Du kannst lediglich speichernden Signalen und Ausgängen einen
Defaultwert geben. Und im Gunde auch nur dann, wenn das Signal oder der
Ausgang ein Flipflop ist.
Lothar M. schrieb:> dann hast du dieses RAM nicht aus Erfahrung asynchron gemacht.
Da würde ich dir erstmal zustimmen.
Danke für dein Feedback. Kannst du mir vielleicht 2-3 Tipps geben wie
ich besser werden kann? Ich weiß das dieses Design ein Griff ins Klo
war. Ich finde es akutell schwer Feedback zu bekommen da ich niemanden
kenne der sich wirklich auskennt. Mir bleibt häufig nur der Weg in
Foren. Ich denke der ein oder andere kennt es vielleicht.
Falls mir jemand ein paar Hinweise geben könnte oder mir wenigstens
sagen kann A ist falsch weil lies in B nach, wäre ich sehr sehr dankbar.
Deine Seite Lothar Miller besuche ich oft um mir die Artikel
durchzulesen!
Vielen Dank!
Lothar M. schrieb:> Lesen des Syntehsizer User Guides und des Blockram User Guides.
Das werde ich gleich morgen machen!
Lothar M. schrieb:> Geht nicht. Wenn du was speichern willst, brauchst du entweder Flipflops> oder Latches. Flipflops brauchen einen Takt:
Guter Punkt das hatte ich so nicht betrachtet!
Marcel schrieb:> Lothar M. schrieb:>> Flipflops brauchen einen Takt:> Guter Punkt das hatte ich so nicht betrachtet!
Dazu noch ein Wort: such mal nach "Postulate" hier im Forum. Eines davon
lautet "nur 1 und der selbe Takt im ganzen Design". Das ist der
einfachste Weg zur sicheren Funktion.
Insofern ist das mit dem rising_edge(CSn) natürlich nur exemplarisch
gemeint und nicht zur Nachahmung empfohlen... ;-)
In meinem Design würde dort mit sehr hoher Wahrscheinlichkeit (>99%) ein
rising_edge(clk) stehen. So wie in allen anderen getakteten Prozessen
auch.
Marcel schrieb:> Alles ist zugewiesen (nach meiner Meinung).
Du hast hier schön die Pin-Assignments für ram_dat aufgelistet.
Angemeckert wird aber ram_addr[1]?
Marcel schrieb:> (1)[Synth 8-3331] design main has unconnected port clk
clk kommt zwar von irgendwo her und wird auch schön von oben nach unten
durchgereicht, ist aber am Ziel (unten) nirgends
verwendet/angeschlossen. Du hast also ein vollkommen asynchrones Design
gebastelt.
Das mag vielleicht funktionieren (hab' ich noch nie probiert),
Timing-Tools sind dabei aber vollkommen aufgeschmissen (schliesslich
gibt es ja kein Timing) und können dir nicht helfen, wenn es das nicht
tut.
Dieses Thema erinnert mich auch an eine meiner ersten Aufgaben, wo ich
noch wenig Ahnung hatte. Leider auch die Aufgabensteller.
Auch wenn man vom µC den Memory-Interface-Controller verwendet, dann
sollte dies nicht asynchron geschehen. Es geht, aber das Constraining
ist furchtbar (man sieht ja jetzt schon die Code-Verrenkungen) und da du
keinen Takt hast, kann der RAM nur Register-frei angesteuert werden. Das
macht das Resultat sehr langsam...glitch-anfällig ist es auch noch.
Wenn das Interface einen synchronen Takt dazu hat, wird es schon besser.
Ansonsten rate ich das Interface mit Oversampling zu betreiben. Das
schafft der FPGA locker und alles wird einfach.
Fazit: neues Konzept wäre besser
Lothar M. schrieb:> In meinem Design würde dort mit sehr hoher Wahrscheinlichkeit (>99%) ein> rising_edge(clk) stehen. So wie in allen anderen getakteten Prozessen> auch.
Hierzu eine Frage: Kann ich das nicht in jeden Prozess einbinden um
alles Taktsynchron zu halten. Denn ich muss ja nur genug Zeit haben
damit die clk des FPGA schnell genug den Prozess triggert und der
Controller mit Daten beliefert wird.
Frage: Wie mache ich das mit dem ein und ausgeben der Daten die vom FPGA
kommen. Ist es richtig beides in einen Prozess zu packen oder muss ich
da etwas beachten?
1
write_ram : process(nWE, nOE, clk)
2
begin
3
if(rising_edge(clk)) then
4
if (falling_edge(nWE)) then
5
if (addr_buffer < ADRR_HALF) then
6
ram(addr_buffer) <= ram_dat;
7
end if;
8
end if;
9
--------------------------- SO RICHTIG? ------------------------------
10
ram(2**(ADDR_WIDTH-1) to (2**ADDR_WIDTH)-1) <= data_fpga_write;
11
data_fpga_read <= ram(0 to (2**(ADDR_WIDTH-1)- 1));
Frage: Ich baue am Ende eine kleine Platine mit dem FPGA und dem
Mikrocontroller. Das ich den Code vorher entwerfe ist auch meiner
mangelnden Erfahrung geschuldet. Wird das Interface an beliebigen Pins
funkionieren? Ich würde alle Pins auf eine Bank legen und die clk Quelle
ebenfalls an einen Pin in dieser Bank.
Verbesserung:
Lothar M. schrieb:> In meinem Design würde dort mit sehr hoher Wahrscheinlichkeit (>99%) ein> rising_edge(clk) stehen. So wie in allen anderen getakteten Prozessen> auch.
Ich habe mein Design verbessert und noch eine spezifische Frage:
Soll ich nun lieber die Flanke abfragen?
Marcel schrieb:> if(rising_edge(clk)) then> if (falling_edge(nWE)) then
Zur realen Hardware: es gibt nach wie vor keine
"Dual-Clock-Input-Flipflops".
Und zur Theorie: weil eine Flanke in der Theorie 0,0000...0ps lang ist,
werden niemals zwei Flanken gleichzeitig kommen. Ergo wird das zweite
if nie erfüllt.
Und zu dem Codeschnipsel hier ein paar Worte:
1
write_ram:process(nWE,nOE,clk)
2
begin
3
if(rising_edge(clk))then
4
if(falling_edge(nWE))then
5
if(addr_buffer<ADRR_HALF)then
6
ram(addr_buffer)<=ram_dat;
7
endif;
8
endif;
9
--------------------------- SO RICHTIG? ------------------------------
Abgesehen von der falschen Sensitivliste mit unnötigem nWE und nicht
verwendetem nOE sowie fehlenden data_fpga_write und ram vermischst du da
im Prinzip eine getaktete Beschreibung und mit einer kombinatorischen
Beschreibung dahinter.
Das ist prinzipiell schlechter Stil, weil keiner hinter/in einem
getakteten Prozess eine zusätzliche Kombinatorik erwartet.
Im Grunde ist deine Beschreibung das Selbe wie das hier:
Marcel schrieb:> Soll ich nun lieber die Flanke abfragen?
Du kannst da nichts "abfragen".
VHDL ist eine Hardwarebeschreibungssprache. Du musst also erstmal ein
klares Bild davon haben, wie die Hardware deines Businterfaces mit Logik
und Flipflops aussieht und beschreibst dann diesen Aufbau mit VHDL.
Hinterher kannst du im RTL-Schaltplan nachschauen, ob der Synthesizer
deine Beschreibung richtig verstanden hat.
Auch wenn ein Prozess nach dem Lehrbuch "eigentlich" Zeile für Zeile
"abgearbeitet" wird, so hat er doch eine "Durchlaufzeit" von 0. Denn in
der realen Hardware wird der Prozess eben nicht abgearbeitet, sondern in
einer parallelen Logik abgebildet. Da ist dann nichts mehr mit
"nacheinander".
Marcel schrieb:> Frage: Ich baue am Ende eine kleine Platine mit dem FPGA und dem> Mikrocontroller.
Und du willst dafür ein altbekanntes asynchrones Interface verwenden,
wie es im letzten Jahrtausend zuletzt eingesetzt wurde?
> if(rising_edge(clk)) then> -------------------------------------------------------------> if (nOE = 0) then -- WICHTIGE STELLE
Wenn schon, dann nur so: ein und der selbe Takt im ganzen Design, der
Rest wird nach Einsynchroniserung der asynchronen Signale taktsynchron
abgearbeitet.
Aber packen wir den Spieß doch mal am Griff: was hast du für einen µC?
Hat der so ein asynchrones Adress-/Daten-/Steuerbus-Interface? Und wenns
doch unbedingt ein Adress-/Daten-/Steuerbus-Interface mit
biderektionalen Daten sein muss: wie schnell soll das denn laufen? Wie
schnell ist da ein Buszyklus? Kannst du im Buscontroller des µC
Watistates konfigurieren?
Und nicht zuletzt: was spricht gegen ein synchrones serielles Interface
wie SPI?
Lothar M. schrieb:> Zur realen Hardware: es gibt nach wie vor keine> "Dual-Clock-Input-Flipflops".
Gut ich habe wohl wieder übersehen das es das genauso ist als würde ich
beide Abfragen in ein if schreiben.
Lothar M. schrieb:> Im Grunde ist deine Beschreibung das Selbe wie das hier:
Also kann ich Statements problemlos aus dem Process lösen?
Lothar M. schrieb:> Und du willst dafür ein altbekanntes asynchrones Interface verwenden,> wie es im letzten Jahrtausend zuletzt eingesetzt wurde?
Ich habe das Interface vorgeschlagen bekommen. Der Betreuer sagte das es
am einfachsten ist und vergleichsweise wenig Zeit in Anspruch nimmt.
Was wäre die Alternative?
Lothar M. schrieb:> Aber packen wir den Spieß doch mal am Griff: was hast du für einen µC?> Hat der so ein asynchrones Adress-/Daten-/Steuerbus-Interface? Und wenns> doch unbedingt ein Adress-/Daten-/Steuerbus-Interface mit> biderektionalen Daten sein muss: wie schnell soll das denn laufen? Wie> schnell ist da ein Buszyklus? Kannst du im Buscontroller des µC> Watistates konfigurieren?
Es ist ein C2000 der Marke TI. Die Zugriffszeiten (SETUP, STROBE, HOLD)
kann man konfigurieren und es gibt sogar die Möglichkeit ein wait Pin zu
nutzen mit dem man den Buszugriff weiter verzögern kann.
Anbei eine valide Konfiguration mit nahezu maximaler Verzögerung in
jedem State.
Lothar M. schrieb:> Und nicht zuletzt: was spricht gegen ein synchrones serielles Interface> wie SPI?
Ich denke das der gewünschte Datendurchsatz nicht erreicht werden kann.
Desweiteren wird dieses Interface hier schon genutzt.
Marcel schrieb:> Lothar M. schrieb:>> Und nicht zuletzt: was spricht gegen ein synchrones serielles Interface>> wie SPI?>> Ich denke das der gewünschte Datendurchsatz nicht erreicht werden kann.> Desweiteren wird dieses Interface hier schon genutzt.
SPI kann mehrere Slaves bedienen. Dafür gibt es Chip Select (CS)
Signale. Der C2000 kann das sogar ziemlich schnell und mit DMA per
McBSP. Je nach Typ hast du mehrere McBSP.
Marcel schrieb:> Es ist ein C2000 der Marke TI. Die Zugriffszeiten (SETUP, STROBE, HOLD)> kann man konfigurieren und es gibt sogar die Möglichkeit ein wait Pin zu> nutzen mit dem man den Buszugriff weiter verzögern kann.> Anbei eine valide Konfiguration mit nahezu maximaler Verzögerung in> jedem State.
Kommt mir bekannt vor.
Variante 1 ist, dass du das Interface als asynchron betrachtest aber im
FPGA trotzdem ein synchrones Design baust. Das gibt zwei Taktzyklen
Verzögerung auf den CS, OEn und WEn Eingängen, da diese zuerst in deine
lokale FPGA Takdomäne einsynchronisiert werden müssen.
Variante 2 ist, dass der FPGA mit dem Clock vom C2000 betrieben wird
(der hat einen passenden Ausgang), der ja intern auch genutzt wird um
das parallele Interface zu bedienen. Wenn das Layout für die
Taktfrequenz soweit OK ist, kannst du alle Signale zwischen C2000 und
FPGA als synchron betrachten. Kein Einsynchronisieren nötig, keine
Latches, wenige übersichtliche Constraints, nur ein einzelner
Quarzoszillator auf der Leiterplatte.
Marcel schrieb:> Lothar M. schrieb:>> Zur realen Hardware: es gibt nach wie vor keine>> "Dual-Clock-Input-Flipflops".>> Gut ich habe wohl wieder übersehen das es das genauso ist als würde ich> beide Abfragen in ein if schreiben.>> Lothar M. schrieb:>> Im Grunde ist deine Beschreibung das Selbe wie das hier:>> Also kann ich Statements problemlos aus dem Process lösen?
Aber mir scheint, dass hier zuerst mal Grundlagenwissen aufgebaut werden
sollte.
Um aus der Softwaredenkweise herauszukommen und um zu verstehen, was man
mit VHDL eigentlich (be-)schreibt war für mich das Buch "VHDL Synthese"
von Reichhard und Schwarz sehr erhellend (Vermutlich gibt es das in
eurer Uni Bibliothek).
Marcel schrieb:> Ich denke das der gewünschte Datendurchsatz nicht erreicht werden kann.
Das kann man ganz ohne Denken ermitteln: einfach so einen Buszyklus
aufzeichnen und schauen, wie schnell das Ganze sein muss (und zwar
getrennt für Lesen und Schreiben).
> Ich denke das der gewünschte Datendurchsatz nicht erreicht werden kann.
Hast du Zahlen zum gewünschten Durchsatz? Denn dazu wird scih der
Betreuer hoffentlich Gedanken gemacht haben, wenn er schon diesen Bus
vorschlägt...
Marcel schrieb:> Ich habe das Interface vorgeschlagen bekommen. Der Betreuer sagte das es> am einfachsten ist und vergleichsweise wenig Zeit in Anspruch nimmt.
Genau wie ich es vermutet habe. Die Alternativen habe ich und andere
bereits dir hier hingeschrieben.
Man sieht, dass das asynchrone interface doch mehr Zeit zum designen
beansprucht. Schnell wird es auch nicht werden. Soll es das überhaupt?
SPI/mcbsp mit DMA ist schon recht flott für den uC und ich finde das
einen guten Ansatz. Dafür ist es auch konzipiert.
Dein Betreuer hat keine Ahnung von dem Thema.
Tim schrieb:> Dein Betreuer hat keine Ahnung von dem Thema.
Na ja, so streng wäre ich da erst mal nicht. Das Interface ist
tatsächlich das simpelste - hat ja keiner gesagt, dass es auf FPGA-Seite
unbedingt asynchron implementiert werden muss (nein, ich bin nicht der
Betreuer).
Tim schrieb:> SPI/mcbsp mit DMA ist schon recht flott für den uC und ich finde das> einen guten Ansatz. Dafür ist es auch konzipiert.
Wir kennen die Anwendung nicht, aber ein Blick ins Datenblatt verrät,
dass das asynchrone RAM-Interface beachtliche 60+ MB/s Datentransfer im
Random Access erreicht - das dürfte mit SPI (da scheint mir auf den
ersten Blick bei 50 MHz Schluss zu sein) nicht erreichbar sein.
Markus F. schrieb:> Wir kennen die Anwendung nicht, aber ein Blick ins Datenblatt verrät,> dass das asynchrone RAM-Interface beachtliche 60+ MB/s Datentransfer im> Random Access erreicht - das dürfte mit SPI (da scheint mir auf den> ersten Blick bei 50 MHz Schluss zu sein) nicht erreichbar sein.
Man sollte da nicht so optimistisch sein. Wir reden hier vom C2000 EMIF
im 16bit ASRAM-Betrieb.
Hier hat das netterweise jemand theoretisch durchgerechnet:
http://e2e.ti.com/support/microcontrollers/c2000/f/171/t/704812?TMS320F28379D-Theoretical-maximum-read-and-write-speed-of-the-EMIF-connected-to-ASRAM
Die Quelle kommt auf theoretisch, maximal 51,3 MB/s für Lesen. Ließt man
weiter, kommt noch der Einwurf, dass die DMA-Anbindung gar nicht so
effektiv arbeitet. Auch das kann ich von ähnlichen Controllern
bestätigen.
Aus Erfahrung würde ich 20-30 MByte/s realistisch sehen, wenn man das
Interface optimal betreibt UND die DMA große Bursts überträgt. Nagut,
das ist schon schneller als SPI. Vielleicht ist damit der geheimnisvolle
Durchsatz erreicht ;)
Marcel schrieb:> Wird das Interface an beliebigen Pins> funkionieren?
Hier solltest du nochmal genau nachschauen. Es kann sein, dass dafür nur
bestimmte Pins explizit vorgesehen sind.
Tim schrieb:> Die Quelle kommt auf theoretisch, maximal 51,3 MB/s für Lesen. Ließt man> weiter, kommt noch der Einwurf, dass die DMA-Anbindung gar nicht so> effektiv arbeitet. Auch das kann ich von ähnlichen Controllern> bestätigen.
Hier: http://www.ti.com/lit/an/sprac96a/sprac96a.pdf (S. 11) stehen
etwas unter 50 MB/s für Reads und etwa 80 MB/s für Writes (CPU, DMA
scheint etwas langsamer zu sein).
Die Diagramm-Fussnoten implizieren, dass das keine theoretischen,
sondern (sicherlich unter absolut optimalen Bedingungen, sonst wär's ja
nicht von TI) gemessene Werte seien.
Auf einem älteren ARM9 haben wir mal reichlich 140 MBit/s (~14 MByte/s)
über das Extended Memory Interface (EMI, 16Bit WIMRE) realisiert.
Man brauch auf beiden Seiten relativ viele Pins. Statt einem größen FPGA
kann man dann gleich zum Zynq greifen.
Duke
Vielen Dank an alle die mir geholfen haben!
Ich habe derweil meinen Code verbessert und versuche nun die letzten
Fehler ausfindig zu machen.
Das es durchaus bessere Interfaces gibt ist mir derweil auch
aufgefallen. Nichts desto trotz möchte ich meine Arbeit nicht wegwerfen,
denn ich habe unglaublich viel gelernt bis hier hin!
--------------------------------------------------------------
Tim schrieb:> Hier solltest du nochmal genau nachschauen. Es kann sein, dass dafür nur> bestimmte Pins explizit vorgesehen sind.
Ich meinte am FPGA. Am Controller sind sie sehr statisch vorgegeben.
Christoph Z. schrieb:> Um aus der Softwaredenkweise herauszukommen und um zu verstehen, was man> mit VHDL eigentlich (be-)schreibt war für mich das Buch "VHDL Synthese"> von Reichhard und Schwarz sehr erhellend (Vermutlich gibt es das in> eurer Uni Bibliothek).
Vielen Dank für diesen Tipp!
---------------------------------------------------------------
Ich habe das Interface unterteilt in einen Read only und Write only Part
und schiebe die Daten dann von einem Teil in den anderen damit ich sie
am Mikrocontroller wieder auslesen kann.
Nun habe ich festgestellt das meine Simulation (Post Implementation)
einen XXXX state erzeugt beim lesen der gewünschten Daten aus dem RAM.
Vielleicht kann mir jemand ein paar Tipps zur Fehlersuche geben.
....
Grundsätzlich sieht in der Simulation aber alles gut aus. Das Signal das
den X State erzeugt ist das Signal das aus dem Tristate Block die Daten
in den RAM schreiben soll. Ich weiß leider nicht warum es zu diesem
State kommt und wie ich das Problem weiter debuggen soll.
Vielleicht habe ich auch in der Simulation einen Schnitzer.
Im Anhang ein Screenshot der die o.g. Leseoperation abbildet. Wie man
sieht schreibt das Modul die gewünschten Daten an den Port ram_dat.
Allerdings ist in der Zwischenzeit der Zustand XXXX.
Marcel schrieb:> Allerdings ist in der Zwischenzeit der Zustand XXXX.
Eine Testbench hat keinen Port nach aussen. Mach den mal weg und
deklariere das Signal (wie schon geplant) in der Testbench.
Lothar M. schrieb:> Eine Testbench hat keinen Port nach aussen. Mach den mal weg und> deklariere das Signal (wie schon geplant) in der Testbench.
Habe ich jetzt wieder zurück geändert. Das ändert erstmal nichts an der
Simulation.
Das letzte mal lag der Fehler im Test Skript. Gehe ich richtig in der
Annahme das wenn ich den Datenpins "ZZZZ" zuweise (in der Testbench),
das dann dieser Zustand überschrieben wird und keinen "XXXX" Zustand
erzeugen kann?
Marcel schrieb:> Gehe ich richtig in der> Annahme das wenn ich den Datenpins "ZZZZ" zuweise (in der Testbench),> das dann dieser Zustand überschrieben wird und keinen "XXXX" Zustand> erzeugen kann?
Ja, einer sagt "z" der andere sagt "was anderes" dann ist der Wert
deines Signals "was anderes".
X bedeutet, dass zwei (oder sogar mehr) Treiber auf das Signal schreiben
und zwar etwas anderes als "z".
In VHDL ist dafür die "resolution function" zuständig die für den Typ
std_logic definiert ist und ausgeführt wird, wenn zwei Treiber auf ein
std_logic Signal schreiben. Die Tabelle dazu ist z. B. hier:
https://vhdlwhiz.com/std_logic/
Christoph Z. schrieb:> X bedeutet, dass zwei (oder sogar mehr) Treiber auf das Signal schreiben> und zwar etwas anderes als "z".
În dem Link den du mir geschickt hast steht allerdings das alle anderen
Zustände "Z" überschreiben und somit "Z" rezessiv ist. Da ich auch X und
- nicht benutze muss ich irgendwo eigentlich eine 1 und 0 gleichzeitig
senden.
Das ist eine schlechte Idee:
use IEEE.std_logic_unsigned.all;
use IEEE.numeric_std.all;
Du solltest dich entscheiden: entweder die alte std_logic_arith (zu der
die std_logic_unsigned gehört), oder besser nur die numeric_std. Wenn du
beide verwendest kannst du dir kuriose Effekte wegen doppelt definierten
Datentypen einfangen...
Marcel schrieb:> Allerdings ist in der Zwischenzeit der Zustand XXXX.
Die Verhaltensimulation mit ISE und dem ram_dat als lokales Signal sieht
aber gut aus. Was simulierst du da?
Lothar M. schrieb:> Was simulierst du da?
Ich schreibe den RAM einmal voll mit "random" Daten und lese sie danach
wieder aus. Die Daten für die Simulation habe ich mit dem Logic Analyzer
aufgenommen. Die Daten stammen direkt aus dem TI C2000.
Nun habe ich das Problem das sobald ich das Interface auf der Hardware
laufen lasse, es nicht so funktioniert wie ich das gerne hätte.
Ich habe schon im .xdc File nach Fehlern gesucht und andere Fehler in
meinem Code oder der Hardware selbst in Betracht gezogen.
Im Anhang einmal der Lesevorgang. Dieser passiert nach dem ich die Daten
in den RAM geschrieben habe.
Lothar M. schrieb:> Die Verhaltensimulation mit ISE und dem ram_dat als lokales Signal sieht> aber gut aus. Was simulierst du da?
Ich habe glaube ich deine Aussage Fehlinterpretiert.
Ich mache eine Post Implementation Simulation da ich die Vermutung hatte
das bei der Synthese/Impl. etwas nicht so gemacht wird wie ich es gerne
hätte.
Das Signal das ich mir anschaue ist der IN Teil vom Port ram_dat. Dieser
wird über einen Treiber zurück zum RAM geführt um auch Daten einlesen zu
können.