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?
Ein SIM wäre sicher von Vorteil, um wieter zu kommen und von hier aus was sagen zu können.
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
Entschuldigung, habe übersehen dass der Refresh alle 4 us kommt. 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.
So, hab' jetzt mal in meine Sourcen geschaut. Hier erst mal ein paar Angaben zum Timing:
1 | -- From JEDEC PC100/133 standard (www.jedec.org):
|
2 | --
|
3 | -- tRC >= 70ns (RAS Cycle time)
|
4 | -- tRRD >= 20ns (RAS to RAS Bank activate delay)
|
5 | -- tRCD >= 20ns (Activate to command delay - RAS to CAS delay)
|
6 | -- tRAS >= 50ns (RAS Active time)
|
7 | -- tRP >= 20ns (RAS Precharge time)
|
8 | -- tMRD >= 3 tCK
|
9 | -- tRFC <= 80ns (Row refresh cycle time)
|
10 | |
11 | -- tREF <= 64ms (Refresh period for 4096 rows, so 64ms/4096 = 15.625us per row)
|
12 | |
13 | -- From MT48LC4M16A2-6 datasheet:
|
14 | --
|
15 | -- tRC >= 60ns (RAS Cycle time)
|
16 | -- tRRD >= 12ns (RAS to RAS Bank activate delay)
|
17 | -- tRCD >= 18ns (Activate to command delay - RAS to CAS delay)
|
18 | -- tRAS >= 42ns (RAS Active time)
|
19 | -- tRP >= 18ns (RAS Precharge time)
|
20 | -- tWR >= 15ns (time to internal complete a write burst)
|
21 | -- tMRD >= 2 tCK
|
22 | -- tRFC <= 60ns (Row refresh cycle time)
|
23 | |
24 | -- tREF <= 64ms (Refresh period for 4096 rows, so 64ms/4096 = 15.625us per row)
|
25 | |
26 | -- From K4S641632C datasheet:
|
27 | --
|
28 | -- tRC >= 70ns (RAS Cycle time)
|
29 | -- tRRD >= 20ns (RAS to RAS Bank activate delay)
|
30 | -- tRCD >= 20ns (Activate to command delay - RAS to CAS delay)
|
31 | -- tRAS >= 50ns (RAS Active time)
|
32 | -- tRP >= 20ns (RAS Precharge time)
|
33 | -- tWR >= 1 tCK (time to internal complete a write burst)
|
34 | -- tMRD >= 2 tCK
|
35 | |
36 | -- tREF <= 64ms (Refresh period for 4096 rows, so 64ms/4096 = 15.625us per row)
|
37 | |
38 | constant CLOCK_PERIOD : natural := 8; -- Clock period in ns. |
39 | |
40 | -- Timing constants in ns:
|
41 | constant tRC : natural := 70; |
42 | constant tRRD : natural := 20; |
43 | constant tRCD : natural := 20; |
44 | constant tRAS : natural := 50; |
45 | constant tRP : natural := 20; |
46 | constant tWR : natural := 15; |
47 | constant tRFC : natural := 90; -- This value was massaged. |
48 | constant tSTARTUP_NOP : natural := 200000; --ns |
49 | |
50 | -- Timing constants in cycles
|
51 | constant tRC_CYCLES : natural := tRC / CLOCK_PERIOD; |
52 | constant tRRD_CYCLES : natural := tRRD / CLOCK_PERIOD; |
53 | constant tRCD_CYCLES : natural := tRCD / CLOCK_PERIOD; |
54 | constant tRAS_CYCLES : natural := tRAS / CLOCK_PERIOD; |
55 | constant tRP_CYCLES : natural := tRP / CLOCK_PERIOD + 1; |
56 | constant tWR_CYCLES : natural := tWR / CLOCK_PERIOD; -- + 1; |
57 | constant tMRD_CYCLES : natural := 3; |
58 | constant tRFC_CYCLES : natural := tRFC / CLOCK_PERIOD; |
59 | |
60 | constant tSTARTUP_NOP_CYCLES : positive := tSTARTUP_NOP / CLOCK_PERIOD; |
61 | |
62 | |
63 | |
64 | -- Memory Configuration:
|
65 | --
|
66 | -- Bank Row Column
|
67 | -- |<->|<- ->|<- ->|
|
68 | -- -------------------------------------
|
69 | -- 21 20 8 0
|
70 | --
|
71 | --constant AddressWidth : natural := 22;
|
72 | constant BankSize : natural := 2; |
73 | constant RowSize : natural := 12; |
74 | constant ColSize : natural := 8; |
75 | constant BankStart : natural := 20; |
76 | constant RowStart : natural := 8; |
77 | constant ColStart : natural := 0; |
78 | |
79 | Constant c_CAS_Latency : natural := 3; |
80 | |
81 | -- Constants for the Mode Register
|
82 | |
83 | constant CAS_Latency : std_logic_vector(2 downto 0) := "011"; -- CAS latency = 3 |
84 | constant Burst_Type : std_logic := '0'; -- Sequential if '0' |
85 | constant Burst_Mode : std_logic := '0'; -- enabled if '0' |
86 | constant BurstLength_8 : std_logic_vector(2 downto 0) := "011"; |
87 | constant BurstLength_256 : std_logic_vector(2 downto 0) := "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.
Hier wird das soeben geschriebene gleich wieder zurückgelesen.
Dann wird in eine andere Row geschrieben, man sieht, dass zuerst die offene Row, in der eben das Schreiben-Lesen passiert ist, geschlossen wird.
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 wiederhole mal meine Frage von oben: Der Takt in Deinen Diagrammen - ist das der, der auch an's SDRAM geht?
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... ;-)))))
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.