Hallo! Ich arbeite an einem SDRAM-Controller für einen CycloneII und hab mich dabei wohl zu lange mit Modelsim beschäftigt. Ich hatte gestern schon eine laufende Version, die aber laut Simulation mit Back Annotation Werten von Quartus maximal 50Mhz schaffte, darum bin ich heute lange gesessen und habe meine State Machine umgebaut, Bank Interleaving rausgelassen und generell versucht, alles kürzer zu machen, mein Ziel wären 100Mhz. Jetzt kämpfe ich seit einiger Zeit mit einigen Problemen, so bekomme ich haufenweise Warnings wegen inferred latches. Bei einigen Signalen kommen mir latches ganz angebracht vor, wie zb das Signal, das die Notwendigkeit eines Refresh anzeigt. Das kann ja kommen, wenn ich mitten in einer Operation bin und soll dann bis zum nächsten Idle-Zustand gesetzt sein, um dann den Refresh durchzuführen. Wenn es ginge würde ich das natürlich gern sauber machen, aber ein einfaches Kochrezept dafür habe ich im Netz bisher nicht gefunden. Bei einigen der Latches meldet Quartus, diese könnten "unsafe behaviour" zeigen, darunter welche, die ich gar nicht als Signale eingeführt habe, sondern die wohl in der Synthese entstanden sind, diese tragen Namen wie "comb_1234" usw. Was ich mit denen tun soll, ist mir überhaupt nicht klar. Jetzt bin ich an einem Punkt angelangt, an dem mir Quartus mitteilt, dass das Design evtl nicht funktionieren wird, denn: "Warning: Circuit may not operate. Detected 201 non-operational path(s) clocked by clock "clk" with clock skew larger than data delay. See Compilation Report for details." (201, na servus :( ) In der Timing Simulation bewahrheitet sich das dann leider, es kommt de facto Müll heraus. In der Theorie sind mir die Probleme klar, wenn zb Latches dasselbe Signal am D- und am ENA-Pin haben oder das mit dem Clock Skew. Aber in der Praxis fehlt mir der Durchblick, wie ich in VHDL coden soll, um diese Probleme zu vermeiden. Ich habe eine State Machine, die in einem Prozess auf Basis des aktuellen state und der Inputs asynchron den next_state setzt, in einem zweiten auf Basis des next_state asynchron die nächsten Outputs und in einem dritten Prozess wird synchron mit jeder clk-Flanke der next_state an den state zugewiesen. Soweit sollte das State-of-the-Art sein. Wie muss man die Inputs lesen und die Outputs setzen, damit man nicht diese Probleme bekommt? Bin über jede hilfe dankbar, insbesondere auch gute Links, in denen man sich dazu was durchlesen kann. ligrü Matthias
Latches solltest du erst einmal als "böse" betrachten und vermeiden. Bei dem angesprochenen Refresh-Signal tut es auch ein Flipflop mit Clockenable. Weist du jedem Signal in jedem möglichen Fall von if oder case einen Wert zu, so hast du schon mal 95% der Latches vermieden. Die anderen 5% wirst du los durch Verwendung von Clockenable und durch registrieren aller Signale bevor sie weiterverwendet werden.
Hallo Matthias, würde Dir der VHDL-Code für einen getesteten DDR-SDRAM-Controller was helfen (133 MHz). Ist allerdings für XILINX geschrieben, aber vielleicht als Beispiel brauchbar?
Eine weitere Möglichkeit ist die Vorbelegung mit default. Ferner sollte man alle Signale im reset verdrahten. Das anze ist aber nicht trivial, hast schon recht. Ich selber habe aber auch immer noch Probleme mit einigen Latches in einigen Designs, die sich mir einfach nicht erschließen wollen, weil es aehnlich verdrahtete Signale gibt, die Quartus nicht anmeckert.
@Jan M.: Meine State Machine ist schon ziemlich umfangreich und auch die Zahl der Signale ist nicht klein, in Summe führt das zu einer Menge von Zuweisungen, die ich dann nur mehr schwer überblicke. Ich weiß nicht, ob es besser wird, wenn ich es feiner strukturiere. Mein Ziel wäre natürlich, diese Fehler komplett loszuwerden, aber da fehlt mir eben der Durchblick. Wenn Du schreibst: "Bei dem angesprochenen Refresh-Signal tut es auch ein Flipflop mit Clockenable." kann ich damit in der Theorie zwar etwas anfangen, aber wie ich das in vhdl mache (ein eigener Prozess, der auf rising_edge(clk) reagiert und das Signal zuweist? kann ich fürs rücksetzen des signals, ebenfalls wieder flankengesteuert den wert des state-signals verwenden und zb beim letzten refresh-zustand zurücksetzen?) sodass es nach der Synthese funktioniert ist mir noch nicht klar. "registrieren aller Signale" habe ich auch noch nicht ganz verstanden, ist damit das Zwischenspeichern in einem Register gemeint? Eine der Dinge, die ich erreichen möchte, ist dass ich eine Anfrage gleich (also bis zum nächsten Clock Cycle) an den SDRAM durchschalten möchte, wenn die entsprechende Bank/Row schon geöffnet ist. Wenn ich die Daten dazwischen in ein flankengesteuertes Register schreibe muss ich doch einen zusätzlichen Wait State einführen, oder? @Mark: Danke, würde wohl was helfen, aber das ganze ist eine Sache für Uni, also leider nicht so, dass ich einfach was funktionierendes nehme und mich dann anderen Dingen zuwende. Meine Motivation ist auch eher, das Ganze abseits der Theorie in der praktischen Umsetzung zu begreifen, darum möchte ich lieber die Probleme meines Modells lösen, als mir woanders was abzuschauen. Aber wenn Du den Source online stellen kannst schaue ich ihn mir sicher interessiert an.
@tippgeber: Uups, habe selber so lange an meiner Antwort geschrieben, dass cih Deine nicht gesehen habe. Wenigstens beruhigend, dass ich nicht der einzige bin, der sich mit so etwas herumärgert. Bzgl "Ferner sollte man alle Signale im reset verdrahten.": Kommt mir auch logisch vor, aber es war jetzt eben so, dass ich im VHDL-Modell Fehler hatte, wenn die Outputs beim Anliegen des Reset-Signals keinen fixen Wert haben, darum habe ich dort eine Abfrage wie folgt installiert: if reset = '1' then -- setze alle Outputs auf sinnvolle Werte else case next_state is when ... end if; Und das war der Punkt, an dem das Timing vollkommen dahin war und Quartus die Warnung geliefert hat, dass der Clock Skew größer wird als die Durchlaufzeit für die Daten. Darum denke ich, dass ich mir da mehr dazu durchlesen muss, für mich ist das einfach nicht nachvollziehbar, warum diese Änderung das Design so komplett zusammenhaut.
>die Zahl der Signale ist nicht klein, in Summe führt das zu einer Menge >von Zuweisungen, die ich dann nur mehr schwer überblicke. Meine state machines sehen immer folgendermaßen aus: Zuerst werden allen Signalen default-Werte zugewiesen, also entweder next_signal <= 0; oder next_signal <= signal; , je nach dem, was gerade gebraucht wird. Dann erst folgt die Auswertung der Zustände. So kann mankein Signal vergessen und allen Signalen ist immer ein Wert zugewiesen. Dann der synchrone Prozess, der alle Signale registriert. Und nur diese registrierten Signale erscheinen irgendwo in Abfragen oder in Zuweisungen auf der rechten Seite. >"registrieren aller Signale" habe ich auch noch nicht ganz verstanden, >ist damit das Zwischenspeichern in einem Register gemeint? Ja genau. >if reset = '1' then > -- setze alle Outputs auf sinnvolle Werte >else > case next_state is Der reset gehört in den synchronen Prozess, auch wenn er asynchron ist:
1 | if reset then |
2 | else if risingedge(clk) |
3 | else |
4 | endif |
oder
1 | if risingedge(clk) then |
2 | if reset |
3 | else |
4 | endif |
5 | else |
6 | endif |
>>"Bei dem angesprochenen Refresh-Signal tut es auch ein Flipflop mit >>Clockenable." >kann ich damit in der Theorie zwar etwas anfangen, aber wie ich das in >vhdl mache (ein eigener Prozess, der auf rising_edge(clk) reagiert und >das Signal zuweist? kann ich fürs rücksetzen des signals, ebenfalls >wieder flankengesteuert den wert des state-signals verwenden und zb beim >letzten refresh-zustand zurücksetzen?) sodass es nach der Synthese >funktioniert ist mir noch nicht klar. Kombinatorisch die Bedingung für den Refresh:
1 | refresh_ff_en <= refresh_faellig or refresh_abgearbeitet; |
Diese beiden Signale dürfen natürliche beide immer nur einen Takt lang sein. Dann komt ein toggle-flipflop:
1 | if risingedge and refresh_ff_en then |
2 | mach_refresh <= not mach_refresh; |
>Eine der Dinge, die ich erreichen möchte, ist dass ich eine Anfrage >gleich (also bis zum nächsten Clock Cycle) an den SDRAM durchschalten >möchte, wenn >die entsprechende Bank/Row schon geöffnet ist. Wenn ich die Daten >dazwischen in ein flankengesteuertes Register schreibe muss ich doch >einen zusätzlichen Wait State einführen, oder? Denke in der Logik schon einen Takt weiter, dann brauchst du keine waitstates. Wenn die Bank offen ist und ich sie jetzt nicht schließe, dann wird sie im nächsten Takt auch noch offen sein.
Nachdem ich heute bedingt durch meine Freundin ein paar waitstates hatte
;) habe ich mich mit dem "Digital Design"-Buch von Wakerly hingesetzt
und anschließend meine State Machine genau so wie er das vorschlägt
gecoded. Jetzt bin ich die Latches alle los :), es schaut so aus: zu
jedem Signal xxx gibt es ein next_xxx Signal. Das Ganze läuft in drei
Prozessen, einer ist der synchrone STATE_MEMORY und weist jeweils xxx <=
next_xxx zu, ein zweiter ist für die NEXT_STATE_LOGIC und weist die
next_xxx asynchron auf Basis von state und Inputs zu und ein dritter ist
OUTPUT_LOGIC, der die outputs asynchron auf Basis von state und inputs
setzt. Ich denke, bei der Struktur werde ich bleiben weil sie die
Mealy-State-Machine sehr logisch abbildet. Damit kommt genau das heraus,
was Jan M. schreibt, die registrierten Signale werden nur im synchronen
Prozess gesetzt, in den anderen Prozessen werden für jeden state-wert
die next_xxx zugewiesen.
Eine Frage an Jan M.: Du schreibst:
> Der reset gehört in den synchronen Prozess, auch wenn er asynchron ist:
Das gilt für alle xxx Signale, aber die next_xxx und die outputs werden
ja jeweils in einem anderen Prozess gesetzt, darum habe ich jetzt in
jedem der drei Prozesse eine reset-Abfrage, die jeweils die zugehörigen
Werte setzt. Das geht doch nicht anders, da ich ein Signal nur jeweils
in einem Prozess zuweisen kann, oder?
Und die Timing-Probleme sind leider nicht zu Ende, mein Ziel wären ja
die 100Mhz, aber das scheint schwierig zu werden, da ich teilweise
kombinatorische und Propagation Delays weit über 10 ns, teils über 20 ns
habe. Wenn ich zb eine Schreibeanforderung im nächsten Zyklus an den
SDRAM weiterreichen möchte, muss ich einige Dinge abfragen: zuerst das
Schreibesignal selbst, dann ob die Operation evtl schon vor einigen
Zyklen (zb während einem Refresh) beantragt wurde, dann ob Bank und Row
gleich geblieben sind und dann erst kann ich das Kommando an den SDRAM
weitergeben. Ich habe schon ein bisschen mit Timing Constraints im
Quartus herumgespielt, aber bei der Timing Simulation hat es mich bis
jezt leider immer geschmissen.
Entweder es gibt da noch ein geniales Rezept oder ich brauche doch einen
zusätzlichen waitstate, während dem ich zb eine Pipeline mit den nötigen
Komandos füllen könnte.
Die next_xxx Signale brauchst du nicht zu reseten, es reicht der reset von xxx: Da diese auf Basis der synchronen Signale asynchron zugewiesen werden, haben diese innerhalb des ersten Taktes des reset definierte Werte. In Zuweisungen werden sie nicht benutzt, deswegen ist es egal, wenn sie in diesem ersten Takt undefinierte Werte haben sollten. Ich kenne mich mit der Ansteuerung von sdram nicht aus, aber du beschreibst das, als müsstest du die ganzen Überprüfungen nacheinander machen - geht das nicht parallel?
"einer ist der synchrone STATE_MEMORY und weist jeweils xxx <= next_xxx zu, ein zweiter ist für die NEXT_STATE_LOGIC und weist die next_xxx asynchron auf Basis von state und Inputs zu und ein dritter ist OUTPUT_LOGIC, der die outputs asynchron " Welch Erkenntnis. Das lernt man eigentlich im ersten Semester. Leutz, ihr muesst mehr reale Schaltungen zusammenlöten, daß ihr die Signale VOR und NACH den FLipflops / Kombinatorik auseinanderhalten könnt - auch wenn die später im FPGA bei schneller (schlampiger Programmierung) denselben Namen haben ...
Matthias wrote: > ein dritter ist > OUTPUT_LOGIC, der die outputs asynchron auf Basis von state und inputs > setzt. Kann das nicht zu Problemen führen wenn am Ausgang kein Register ist (Glitches)?
@ Jan M.: Wie soll ich das parallel machen? Zb der Vergleich der abgefragten Bank/Row mit der offenen, der wohl das aufwändigste ist: Soll ich asynchron immer einen Vergleich der offenen mit der abgefragten Bank machen und das Ergebnis dann nach Auslesen der Request-Signale verarbeiten? Muss ich da nicht höllisch auf die Laufzeit der einzelnen Gatter achten? @ tippgeber: Mag sein, unsere VHDL-Übung auf der Uni war sehr dürftig, gerade mal, dass man im Max+Plus die richtigen Befehle findet und Syntaxfehler in einem vorgegebenen Design ausbessert. Den SDRAM-Controller from scratch zu schreiben fand mein Betreuer auch schon ziemlich ambitioniert. @ Andreas Schwarz: Ja, es passieren mir auch Glitsches, aber solange das Signal bei der Clk-Flanke stabil ist, habe ich das nicht als Problem betrachtet.
Hallo Matthias, "Danke, würde wohl was helfen, aber das ganze ist eine Sache für Uni, also leider nicht so, dass ich einfach was funktionierendes nehme und mich dann anderen Dingen zuwende. Meine Motivation ist auch eher, das Ganze abseits der Theorie in der praktischen Umsetzung zu begreifen, darum möchte ich lieber die Probleme meines Modells lösen, als mir woanders was abzuschauen. Aber wenn Du den Source online stellen kannst schaue ich ihn mir sicher interessiert an." -> der Code steht online bei opencores.org -> DDR SDRAM Controller Core habe ich selber geschrieben und auf einem Virtex-Eval-Board getestet. Sowas ist allerdings kein Anfängerprojekt. Nach den Postings zu urteilen würde ich sagen, Deine einzige Chance was in dieser Art umzusetzen ist anhand von solchen Beispielen. Ansonsten werkelst Du noch monatelang rum, wenn Du Dir das alles selbst erarbeiten willst. Wenn so eine RAM-Ansteuerung laufen soll musst Du fit sein in der Implementierung von schnellen State-Machines, musst komplett verstanden haben was Dein Synthesetool aus dem VHDL-Code macht und Timing-Constaraints im Schlaf setzen können. Sonst wirds schwer - sorry.
Danke, ich quäl mich ja schon länger damit rum, da ist es jedenfalls besser zu hören, dass es schwer ist, wenn es leicht wäre, würde es mir wohl an der nötigen Hirnmasse für diesen Bereich fehlen. Ich hab mir den DDR Controller jetzt ein Weilchen angeschaut, meiner ist nur SDR aber ich denke, ich versteh das Timing halbwegs. Mir kommt vor, ich will einfach zu viel von meinem, Dein Controller setzt wohl mehr auf Pipelining und ist dadurch aber deutlich schneller, weil er die Ansprüche an tpd und tco erfüllen kann. Ich werde wohl wirklich Cycles opfern und versuchen, meinen damit schneller zu bekommen.
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
Mit Google-Account einloggen
Noch kein Account? Hier anmelden.