Forum: FPGA, VHDL & Co. SDRAM-Problem: Precharge funktioniert nicht?


von Matthias (Gast)


Lesenswert?

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.

von Stefan W. (wswbln)


Lesenswert?

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

von Matthias (Gast)


Lesenswert?

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?

von Hardwareonkel (Gast)


Lesenswert?

Ein SIM wäre sicher von Vorteil, um wieter zu kommen und von hier aus 
was sagen zu können.

von Stefan W. (wswbln)


Lesenswert?

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...

von Klaus Falser (Gast)


Lesenswert?

> 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

von Klaus Falser (Gast)


Lesenswert?

Entschuldigung, habe übersehen dass der Refresh alle 4 us kommt.
Klaus

von Matthias (Gast)


Lesenswert?

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.

von Stefan W. (wswbln)


Lesenswert?

...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

von Matthias (Gast)


Lesenswert?

@ 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.

von Matthias (Gast)


Lesenswert?

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.

von Stefan W. (wswbln)


Lesenswert?

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... ;-)

von Matthias (Gast)


Angehängte Dateien:

Lesenswert?

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.

von Matthias (Gast)


Angehängte Dateien:

Lesenswert?

Hier wird das soeben geschriebene gleich wieder zurückgelesen.

von Matthias (Gast)


Angehängte Dateien:

Lesenswert?

Dann wird in eine andere Row geschrieben, man sieht, dass zuerst die 
offene Row, in der eben das Schreiben-Lesen passiert ist, geschlossen 
wird.

von Matthias (Gast)


Angehängte Dateien:

Lesenswert?

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.

von Stefan W. (wswbln)


Lesenswert?

...ich wiederhole mal meine Frage von oben: Der Takt in Deinen 
Diagrammen - ist das der, der auch an's SDRAM geht?

von Matthias (Gast)


Lesenswert?

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 ;) .

von Stefan W. (wswbln)


Lesenswert?

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).

von Matthias (Gast)


Lesenswert?

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.

von Stefan W. (wswbln)


Lesenswert?

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
Noch kein Account? Hier anmelden.