Hallo!
Ich komme bei meiner SDRAM Ansteuerung mal wieder nicht weiter :( . Ich
teste inzwischen auf der HW. Es funktioniert, einen Wert zu schreiben
und ihn anschließend wieder auszulesen, aber andere Dinge funktionieren
nicht und ich habe ein Problem mit dem Precharge im Verdacht, ist
allerdings auch nicht so klar.
Es funktioniert also, einen Wert zu schreiben und ihn gleich
anschließend wieder auszulesen. Wenn ich allerdings 200 us mit dem
zurücklesen warte, lese ich einen falschen Wert zurück. Da das Refresh
ungefähr alle 4 us aufgerufen wird, erfährt die row in die ich
geschrieben habe einen Precharge, da könnte er also der Übeltäter sein.
Es funktioniert nur teilweise, zwei Werte hintereinander in verschiedene
Rows zu schreiben und sind anschließend zurückzulesen, es wird nur der
zweite Wert korrekt gelesen, der erste nicht. Schreibe ich keinen
zweiten Wert, wird der erste korrekt zurückgelesen. Hier ist das ganze
mysteriös, wenn der erste durchs Precharge zerstört wurde, warum der
zweite nicht?
Und schließlich funktioniert der Refresh überhaupt nicht, bei 180 ms
Wartezeit zwischen Schreiben und Lesen stimmt der Wert nur in
Ausnahmefällen, darüber gar nicht mehr. Der Refresh ist jetzt mit einem
Adresszähler und einer Activate-Precharge-Sequenz implementiert, davor
habe ich einfach Refresh-KOmmandos geschickt, das hat aber auch nicht
funktioniert.
In der funktionalen Simulation mit einem Micron-Modell funktioniert das
ganze, und auch in einer Simulation mit dem vho-File von Quartus und dem
sdf-Timing-File gab es keine Probleme, keine Warnings von den Timing
Checks oder ähnliches.
Mir fällt nicht ein, woran es sonst noch liegen könnte außer dass beim
Schließen der Rows Daten zerstört werden. Oder gibt es da noch andere
Möglichkeiten? Dass es ein Problem mangelnder Refreshs ist glaube ich
nicht, weil die Zugriffe trotz Wartezeiten zeitlich sehr nahe beinander
liegen.
Vielleicht fällt ja jemand noch was ein, was ich testen könnte bzw ein
guter Weg, das Problem besser einzugrenzen. Danke jedenfalls schon mal
im Vorhinein.
Hmmm, ohne Source ist hier mal wieder die Glaskugel gefragt...
Also: (Auto-)Refresh darf man ja nur im Idle Zustand initiieren. D.h.
Read/Write/Burst-Kommandos müssen vorher abgeschlossen werden. Ich habe
mir angewöhnt, das immer mit einem Precharge zu machen, dann kann man
mit dem nächsten Kommando ohne weitere Bedingungen gleich aus dem Idle
Zustand loslegen. Dass man die Precharge-Zeit (tRP) einhalten muss ist
eh' klar - oder?
Kommen Deine Zugriffe völlig wahlfrei oder könntest Du z.B.
aufeinanderfolgende Write-Zugriffe in einer FIFO sammeln und als Burst
abarbeiten (lohnt so richtig aber erst ab 8-er Bursts, zumindest wenn
man u.U. jedes mal vorher das Mode-Register umladen muss)?
Oder führst Du jeden Zugriff als einzelnes Kommando durch
(Activate-Read-Precharge bzw. Activate-Write-Precharge)? Dann kann es
eigentlich nur an nicht eingehaltenen Wartezeiten liegen (tRCD, tRAS,
tRP...). Manchmal muss man auch ein wenig mit den Timing-Angaben aus dem
Datenblatt "spielen": Mal hier und da einen Takt zugeben und schon läuft
maches evtl. etwas besser.
Ach ja: und die funktionale Simulation ist nur die halbe Wahrheit. Ein
reales SDRAM legt u.U. auch Wert auf korrekte Setup- und Hold Zeiten.
Hast Du mal die Timing-simulation daraufhin ausgewertet?
Was hast Du für die SRAM-Signale überhaupt für eine Statemachine? Mealy
oder Moore?
Grüße aus Berlin-Tempelhof,
Stefan
Naja, der Source ist inzwischen schon recht umfangreich und mein
Controller macht auch was ich möchte, aber die HW reagiert darauf nicht
wie sie sollte. Ich könnte mal einen Screenshot von der Simulation
anhängen (bin aber jetzt nicht zuhause).
Ich precharge die Row nach einem Zugriff nicht, der Precharge erfolgt
entweder wenn ich refreshe oder der nächste Zugriff in eine andere
Row/Bank fällt. Daher bleibt beim Schreiben/Lesen die aktuelle Row auch
offen, es kommt also zu ACT-WR-(Warten)-RD. Beim schreiben/lesen in
verschiedene Rows ist die Reihenfolge:
ACT-WR-PRE-ACT-WR-PRE-ACT-RD-PRE-ACT-RD. Burst ist noch überhaupt nicht
drin, das soll kommen, wenn ich den Controller, wie ich es plane, mit
einem kleinen Cache aufpeppe. Aber jetzt soll es mal möglichst simpel
zum Laufen gebracht werden. Ich hab auch schon zusätzliche Wait-States
eingebaut, das hat aber auch nichts gebracht.
Mein Modell für die Simulation führt Timing-Checks durch, nicht nur für
die ganzen tRAS, tRC etc Werte sondern auch für Setup- und Holdzeiten.
Darum habe ich dann auch die Timing-Simulation durchgeführt und diese
Checks aktiviert (die ich für die funktionale Simulation immer
auskommentiere, da in der die Hold-Zeiten natürlich nicht eingehalten
werden), da konnte ich aber auch nicht sehen, woran es scheitert, die
Signale liegen immer schön rund um die steigende Clock-Flanke an.
Bzgl State-Machine, das müsste eine Mealy sein. Ich hab einen
Refresh-Part, der jedes Mal ganz abgearbeitet wird und jeweils eine
Write- und eine Read-Sequenz, die die ganze Sequenz PRE-ACT-RD/WR
enthalten und in die jeweils danach, was gebraucht wird, verzweigt wird.
Ich hab mir noch überlegt, ob die Probleme evtl aus einer fehlerhaften
Initialisierungssequenz kommen könnten. Ich habe das CS durchgehend auf
LOW, ob da evtl irgendetwas intern im RAM nicht richtig initialisiert
wird und das zeigt sich, wenn ich in verschiedene Rows schreibe?
Matthias wrote:
>... die Signale liegen immer schön rund um die steigende Clock-Flanke an.
...der RAM Baustein erhält den invertierten Statemachine Clock?
> Bzgl State-Machine, das müsste eine Mealy sein. Ich hab einen> Refresh-Part, der jedes Mal ganz abgearbeitet wird und jeweils eine> Write- und eine Read-Sequenz, die die ganze Sequenz PRE-ACT-RD/WR> enthalten und in die jeweils danach, was gebraucht wird, verzweigt wird.
Bei Mealy werden die Ausgangssignale kombinatorisch aus den
Statesignalen erzeugt, was u.U. zu ziemlichem Skew führen kann. Bei
Moore werden die Ausgangssignale durch Register gebildet und die Logik
sitzt davor. Damit erhält man schön synchrone Signale, muss aber immer
schon einen Takt vorher festlegen, was passieren soll (bzw. hat -
andersrum betrachtet - 1 Clock Latency).
> Ich hab mir noch überlegt, ob die Probleme evtl aus einer fehlerhaften> Initialisierungssequenz kommen könnten.
Oh ja: SDRAMs können ziemlich zickig sein, wenn die Init nicht stimmt.
Ich schau (wenn ich dazu komme) am Montag in der 4ma mal meine Init
durch, ob ich da irgendwas auffälliges finde...
> der Precharge erfolgt entweder wenn ich refreshe oder der nächste Zugriff in >
eine andere Row.
Ich weiss nicht welches SDRAM du verwendest, aber es gibt eine
Maximalzeit zwischen Activate und Precharge (120 us).
Du solltest zumindest einen Timer einbauen, der den Precharge macht
bevor diese Zeit abläuft.
Klaus
Danke für die Antworten, ich kann im Moment leider keine Sim schicken
und auch nichts testen, weil ich erst wieder morgen am Board sitzen
werde. Bzgl Moore/Mealy: Die State Machine, die die Kommandos ausgibt
geht aus dem Idle-Zustand in den Folgezustand und gibt ein Kommando aus
je nachdem, welche Befehlsleitung (da gibt es für jede Sequenz eine) auf
HIGH geht, das wäre also Mealy, danach werden die Ausgänge aber nur mehr
abhängig vom aktuellen Zustand gesetzt, das müsste Moore sein. Ich hab
ja so lange mit dem Timing gekämpft aber laut statischer Timing-Analyse
geht es sich jetzt aus. Ich kann morgen noch eine Timing-Simulation mit
maximum delays laufen lassen aber ich denke/hoffe, dass es nicht daran
liegt.
Ich hätte eine Theorie, die das Verhalten erklären würde, wenn nämlich
der Wert in einem Register oder Latch auf dem Datenpfad aber nicht im
eigentlichen Speicher landen würde. Dann wäre es logisch, dass ich den
zuletzt geschriebenen Wert zurücklesen kann aber davor geschriebene
verloren gehen. Mal schauen, was ich zurückbekomme, wenn ich schreibe
und von einer anderen Adresse zurücklese.
@Klaus Falser: Aha, das mit den 120 us war mir gar nicht bewußt, ich
habe mich für den regelmäßigen Refresh entschieden, weil die worst case
Wartezeit auf den Speicher dann kürzer ist. Es wäre nett, wenn das ganze
dann mal fertig ist, eine Worst-Case-Zeit angeben zu können, die
möglichst kurz ist.
...nur mal so rein interessehalber: Welchen Baustein und welche
SDRAM-Taktfrequenz benutzt Du?
(Ich hab' gerade 'nen MACHXO2280 von Lattice mit 125MHz in der Mache.
Das SDRAM ist von Samsung (1Mx16x4))
Grüße aus Berlin-Tempelhof,
Stefan
@ Stefan Wimmer: Es handelt sich um einen Samsung M464S3254ETS und mein
Ziel sind die 100Mhz. Es ist ja fast beschämend, mit einem SDR SDRAM so
lange zu kämpfen aber de facto war ich absoluter Anfänger, als ich damit
begonnen habe und dass ich jetzt auf der HW arbeite ist für mich schon
ein Erfolg (und so einfach ist es ja auch nicht, ein Zulieferer der
Firma in der ich arbeite kämpft schon seit Wochen mit dem Timing für
einen DDR2 SDRAM und der ist kein Anfänger).
Ich hab in den letzten Tagen wenig Zeit gefunden, daran zu arbeiten,
aber heute hab ich ausprobiert, zu schreiben und dann von einer anderen
Adresse zurückzulesen und tatsächlich kam da der Wert zurück, den ich
vorher an eine andere Stelle geschrieben hatte. Ich habe dann an der
Initialisierung gedreht, zuerst die Wartezeit von 250us auf 500us
geändert und das DSEL Kommando anstatt dem NOP geschickt, dann war das
Verhalten auf einmal ganz anders denn jetzt funktioniert
schreiben-zurücklesen auch nur teilweise, so ca. jedes 3. oder 4. Mal.
Ich bin echt schon gespannt darauf, was ich da falsch mache.
Ich würd ja gern weitermachen aber ich muss ins Bett, muss morgen
aufstehen. Ordentliche Infos kann ich erst morgen abliefern, wenn ich
mich mit klarem Kopf wieder drangesetzt habe, danke jedenfalls allen,
die hier Anteil nehmen.
P.S.: Ich habe auch die PLL, die einen Eingangstakt von 25MHz hat und
auf einen Multiplikator von 4 eingestellt ist auf einen Multiplikator 2
und dann 1 eingestellt und es damit probiert, das Verhalten war das
selbe, nur langsamer. Also am Timing liegt es wohl nicht. Wie gesagt,
ich werde der Sache morgen wieder mehr Zeit widmen.
constantBurstLength_256:std_logic_vector(2downto0):="111";-- full page
Wenn Du die Angaben aus Datenblatt und JEDEC mit den aktuellen Werten
vergleichst, siehst Du, dass da durchaus mit den Werten mal etwas
"gespielt" werden kann.
Ich kann mich noch erinnern, dass ich die Startup-NOP-Zeit etwas
verlängerte (200µS), die tRFC etwas erhöht habe und dass das RAM es
komischerweise lieber hat, wenn während der Refresh-Zyklen die DQMs high
sind. Das kann aber alles bauteilespezifisch sein. Vielleicht
"massierst" Du Dein Timing auch ein wenig... ;-)
Danke, ich hab mir das Datenblatt meines RAMs auch noch einmal
angeschaut, die Werte sind sehr ähnlich und ich denke, dass ich sie
einhalte. Aber ich hab jetzt auch Bilder von der funktionalen Simulation
zu bieten, da sieht man das Timing hoffentlich eh gut.
Hmm, entweder man kann hier nur eine Datei anhängen oder ich habe was
übersehen. Also muss ich wohl vier Beiträge schreiben. In diesem
Screenshot sieht man das Ende eines PRE-ACT-PRE Zyklus der für den
Refresh dient und daran anschließend ein ACT-WR.
Und schließlich wird der erste Wert noch einmal gelesen und verglichen.
Unten sind die beiden LEDs, die mir am Board anzeigen, was sich tut. Und
da bleibt LED1 dunkel, anders als in der Simulation (die LEDs sind
LOW-aktiv).
Wenn ich dazwischen keinen Wert in eine andere Row schreibe leuchtet
LED1 allerdings.
Diese Aussagen gelten allerdings unter Vorbehalt, der Test läuft die
ganze Zeit (ein Counter läuft durch und stößt dadurch regelmäßig die
Operationen an) und jetzt hab ich LED1 grad für einen Durchlauf mal
leuchten gesehen, manchmal leuchtet auch LED0 für einen Zyklus nicht. Es
ist seltsam.
Ich hoffe, Genie kommt von genieren, denn sonst gibt es für mich wohl
wenig Hoffnung.
Wenn in der Beschreibung des Boards steht, dass der SDRAM am PLL clock
output pin der PLL1 hängt heißt das nicht, dass einem der Weihnachtsmann
diesen Pin richtig beschaltet :| . Jetzt weiß ichs auch. Lang hats
gebraucht.
Jetzt, nachdem ich den c2 Ausgang meiner PLL auf diesen Output-Pin
ausgebe, funktioniert fast alles, wie es soll, hin und wieder stimmt der
Wert, der durchs Refresh überlebt, nicht, aber das krieg ich sicher noch
hin.
Der Rest funktioniert, insbesondere das Schreiben und Lesen in/aus
verschiedenen Rows.
Danke für die Antworten, insbesondere Stefan Wimmer, Grüße aus Wien, wo
die Dösis anscheinend wirklich ein wenig langsamer denken ;) .
LOL :-)
Wie war der Spruch?
"In der Theorie (Simulation) ist Praxis ganz einfach..."
Nun kannst Du ja probehalber wieder auf den Autorefresh umschalten und
sehen, ob der besser hält...
Wien? Ah, da werden meine FPGAs/SDRAMs auch bald öfters durchrauschen
(im Railjet).
Oh, den Railjet kenne ich ja noch gar nicht. Schaut fesch aus.
Das Refresh-Problem muss ich noch suchen, es tritt jedenfalls selten auf
im niederen einstelligen Prozentbereich. Vielleicht liegt es auch daran,
dass meine State Machine Refresh-Zyklen verpasst, wenn eine
Lese/Schreiboperation am Laufen ist während die Anforderung zum Refresh
kommt. Ich muss mir das noch durchdenken. Vielleicht probiere ich es
auch mit den DQMs auf HIGH. Es muss auch noch eine bessere Testbench
her. Aber als proof of concept reicht mir das mal für heute.
Eine Frage aus Interesse hätte ich noch: Wenn Du für die Eisenbahn
entwickelst musst Du Dir auch Gedanken über Fehlertoleranz machen? Das
Thema interessiert mich grundsätzlich, wenn ich mal mehr Erfahung mit
FPGAs habe schaue ich vielleicht, dass ich in so etwas arbeite.
Matthias wrote:
> Das Refresh-Problem muss ich noch suchen, es tritt jedenfalls selten auf> im niederen einstelligen Prozentbereich. Vielleicht liegt es auch daran,> dass meine State Machine Refresh-Zyklen verpasst, wenn eine> Lese/Schreiboperation am Laufen ist während die Anforderung zum Refresh> kommt.
...dann solltest Du den Refresh-Request in einem Flipflop speichern und
bei nächster Gelegenheit abarbeiten und dann das FF wieder löschen.
> Eine Frage aus Interesse hätte ich noch: Wenn Du für die Eisenbahn> entwickelst musst Du Dir auch Gedanken über Fehlertoleranz machen?
Nein, ich mache einfach keine Fehler... ;-)))))