Add more memory tests
[gram.git] / gram / core / refresher.py
1 # This file is Copyright (c) 2015 Sebastien Bourdeauducq <sb@m-labs.hk>
2 # This file is Copyright (c) 2016-2019 Florent Kermarrec <florent@enjoy-digital.fr>
3 # This file is Copyright (c) 2020 LambdaConcept <contact@lambdaconcept.com>
4 # License: BSD
5
6 """LiteDRAM Refresher."""
7
8 from nmigen import *
9 from nmigen.utils import log2_int
10 from nmigen.asserts import Assert, Assume
11
12 from gram.core.multiplexer import *
13 from gram.compat import Timeline
14 import gram.stream as stream
15
16 # RefreshExecuter ----------------------------------------------------------------------------------
17
18
19 class RefreshExecuter(Elaboratable):
20 """Refresh Executer
21
22 Execute the refresh sequence to the DRAM:
23 - Send a "Precharge All" command
24 - Wait tRP
25 - Send an "Auto Refresh" command
26 - Wait tRFC
27 """
28
29 def __init__(self, abits, babits, trp, trfc):
30 self.start = Signal()
31 self.done = Signal()
32 self._trp = trp
33 self._trfc = trfc
34
35 self.a = Signal(abits)
36 self.ba = Signal(babits)
37 self.cas = Signal()
38 self.ras = Signal()
39 self.we = Signal()
40
41 def elaborate(self, platform):
42 m = Module()
43
44 trp = self._trp
45 trfc = self._trfc
46
47 m.submodules.timeline = tl = Timeline([
48 # Precharge All
49 (0, [
50 self.a.eq(2**10),
51 self.ba.eq(0),
52 self.cas.eq(0),
53 self.ras.eq(1),
54 self.we.eq(1)
55 ]),
56 # Auto Refresh after tRP
57 (trp, [
58 self.a.eq(0),
59 self.ba.eq(0),
60 self.cas.eq(1),
61 self.ras.eq(1),
62 self.we.eq(0),
63 ]),
64 # Done after tRP + tRFC
65 (trp + trfc, [
66 self.a.eq(0),
67 self.ba.eq(0),
68 self.cas.eq(0),
69 self.ras.eq(0),
70 self.we.eq(0),
71 self.done.eq(1),
72 ]),
73 ])
74 m.d.comb += tl.trigger.eq(self.start)
75
76 return m
77
78 # RefreshSequencer ---------------------------------------------------------------------------------
79
80
81 class RefreshSequencer(Elaboratable):
82 """Refresh Sequencer
83
84 Sequence N refreshs to the DRAM.
85 """
86
87 def __init__(self, abits, babits, trp, trfc, postponing=1):
88 self.start = Signal()
89 self.done = Signal()
90
91 self._trp = trp
92 self._trfc = trfc
93 self._postponing = postponing
94 self._abits = abits
95 self._babits = babits
96
97 self.a = Signal(abits)
98 self.ba = Signal(babits)
99 self.cas = Signal()
100 self.ras = Signal()
101 self.we = Signal()
102
103 def elaborate(self, platform):
104 m = Module()
105
106 m.submodules.executer = executer = RefreshExecuter(self._abits, self._babits, self._trp, self._trfc)
107 m.d.comb += [
108 self.a.eq(executer.a),
109 self.ba.eq(executer.ba),
110 self.cas.eq(executer.cas),
111 self.ras.eq(executer.ras),
112 self.we.eq(executer.we),
113 ]
114
115 countEqZero = Signal(reset=(self._postponing <= 1))
116 countDiffZero = Signal(reset=(self._postponing > 1))
117
118 count = Signal(range(self._postponing), reset=self._postponing-1)
119 with m.If(self.start):
120 m.d.sync += [
121 count.eq(count.reset),
122 countEqZero.eq(self._postponing <= 1),
123 countDiffZero.eq(self._postponing > 1),
124 ]
125 with m.Elif(executer.done):
126 with m.If(count != 0):
127 m.d.sync += count.eq(count-1)
128
129 with m.If(count == 1):
130 m.d.sync += [
131 countEqZero.eq(1),
132 countDiffZero.eq(0),
133 ]
134 with m.Else():
135 m.d.sync += [
136 countEqZero.eq(0),
137 countDiffZero.eq(1),
138 ]
139
140 m.d.comb += [
141 executer.start.eq(self.start | countDiffZero),
142 self.done.eq(executer.done & countEqZero),
143 ]
144
145 if platform == "formal":
146 m.d.comb += [
147 Assert(countEqZero == (count == 0)),
148 Assert(countDiffZero == (count != 0)),
149 ]
150
151 return m
152
153 # RefreshTimer -------------------------------------------------------------------------------------
154
155
156 class RefreshTimer(Elaboratable):
157 """Refresh Timer
158
159 Generate periodic pulses (tREFI period) to trigger DRAM refresh.
160 """
161
162 def __init__(self, trefi):
163 # TODO: we don't pass formal verification for trefi = 1
164 assert trefi != 1
165
166 self.wait = Signal()
167 self.done = Signal()
168 self.count = Signal(range(trefi), reset=trefi-1)
169 self._trefi = trefi
170
171 def elaborate(self, platform):
172 m = Module()
173
174 trefi = self._trefi
175
176 with m.If(self.wait & (self.count != 0)):
177 m.d.sync += self.count.eq(self.count-1)
178
179 with m.If(self.count == 1):
180 m.d.sync += self.done.eq(1)
181 with m.Else():
182 m.d.sync += [
183 self.count.eq(self.count.reset),
184 self.done.eq(0),
185 ]
186
187 if platform == "formal":
188 m.d.comb += Assert(self.done == (self.count == 0))
189
190 return m
191
192 # RefreshPostponer -------------------------------------------------------------------------------
193
194
195 class RefreshPostponer(Elaboratable):
196 """Refresh Postponer
197
198 Postpone N Refresh requests and generate a request when N is reached.
199 """
200
201 def __init__(self, postponing=1):
202 self.req_i = Signal()
203 self.req_o = Signal()
204 self._postponing = postponing
205
206 def elaborate(self, platform):
207 m = Module()
208
209 count = Signal(range(self._postponing), reset=self._postponing-1)
210
211 with m.If(self.req_i):
212 with m.If(count == 0):
213 m.d.sync += [
214 count.eq(count.reset),
215 self.req_o.eq(1),
216 ]
217 with m.Else():
218 m.d.sync += [
219 count.eq(count-1),
220 self.req_o.eq(0),
221 ]
222 with m.Else():
223 m.d.sync += self.req_o.eq(0)
224
225 return m
226
227 # ZQCSExecuter ----------------------------------------------------------------------------------
228
229
230 class ZQCSExecuter(Elaboratable):
231 """ZQ Short Calibration Executer
232
233 Execute the ZQCS sequence to the DRAM:
234 - Send a "Precharge All" command
235 - Wait tRP
236 - Send an "ZQ Short Calibration" command
237 - Wait tZQCS
238 """
239
240 def __init__(self, abits, babits, trp, tzqcs):
241 self.start = Signal()
242 self.done = Signal()
243 self._trp = trp
244 self._tzqcs = tzqcs
245
246 self.a = Signal(abits)
247 self.ba = Signal(babits)
248 self.cas = Signal()
249 self.ras = Signal()
250 self.we = Signal()
251
252 def elaborate(self, platform):
253 m = Module()
254
255 trp = self._trp
256 tzqcs = self._tzqcs
257
258 m.submodules.timeline = tl = Timeline([
259 # Precharge All
260 (0, [
261 self.a.eq(2**10),
262 self.ba.eq(0),
263 self.cas.eq(0),
264 self.ras.eq(1),
265 self.we.eq(1),
266 self.done.eq(0)
267 ]),
268 # ZQ Short Calibration after tRP
269 (trp, [
270 self.a.eq(0),
271 self.ba.eq(0),
272 self.cas.eq(0),
273 self.ras.eq(0),
274 self.we.eq(1),
275 self.done.eq(0),
276 ]),
277 # Done after tRP + tZQCS
278 (trp + tzqcs, [
279 self.a.eq(0),
280 self.ba.eq(0),
281 self.cas.eq(0),
282 self.ras.eq(0),
283 self.we.eq(0),
284 self.done.eq(1)
285 ]),
286 ])
287 m.d.comb += tl.trigger.eq(self.start)
288
289 return m
290
291 # Refresher ----------------------------------------------------------------------------------------
292
293
294 class Refresher(Elaboratable):
295 """Refresher
296
297 Manage DRAM refresh.
298
299 The DRAM needs to be periodically refreshed with a tREFI period to avoid data corruption. During
300 a refresh, the controller send a "Precharge All" command to close and precharge all rows and then
301 send a "Auto Refresh" command.
302
303 Before executing the refresh, the Refresher advertises the Controller that a refresh should occur,
304 this allows the Controller to finish the current transaction and block next transactions. Once all
305 transactions are done, the Refresher can execute the refresh Sequence and release the Controller.
306
307 """
308
309 def __init__(self, settings, clk_freq, zqcs_freq=1e0, postponing=1):
310 assert postponing <= 8
311 self._abits = settings.geom.addressbits
312 self._babits = settings.geom.bankbits + log2_int(settings.phy.nranks)
313 self.cmd = cmd = stream.Endpoint(
314 cmd_request_rw_layout(a=self._abits, ba=self._babits))
315 self._postponing = postponing
316 self._settings = settings
317 self._clk_freq = clk_freq
318 self._zqcs_freq = zqcs_freq
319
320 def elaborate(self, platform):
321 m = Module()
322
323 wants_refresh = Signal()
324
325 settings = self._settings
326
327 # Refresh Timer ----------------------------------------------------------------------------
328 timer = RefreshTimer(settings.timing.tREFI)
329 m.submodules.timer = timer
330 m.d.comb += timer.wait.eq(~timer.done)
331
332 # Refresh Postponer ------------------------------------------------------------------------
333 postponer = RefreshPostponer(self._postponing)
334 m.submodules.postponer = postponer
335 m.d.comb += [
336 postponer.req_i.eq(timer.done),
337 wants_refresh.eq(postponer.req_o),
338 ]
339
340 # Refresh Sequencer ------------------------------------------------------------------------
341 sequencer = RefreshSequencer(self._abits, self._babits, settings.timing.tRP, settings.timing.tRFC, self._postponing)
342 m.submodules.sequencer = sequencer
343
344 if settings.timing.tZQCS is not None:
345 wants_zqcs = Signal()
346
347 # ZQCS Timer ---------------------------------------------------------------------------
348 zqcs_timer = RefreshTimer(int(self._clk_freq/self._zqcs_freq))
349 m.submodules.zqcs_timer = zqcs_timer
350 m.d.comb += wants_zqcs.eq(zqcs_timer.done)
351
352 # ZQCS Executer ------------------------------------------------------------------------
353 zqcs_executer = ZQCSExecuter(self._abits, self._babits, settings.timing.tRP, settings.timing.tZQCS)
354 m.submodules.zqs_executer = zqcs_executer
355 m.d.comb += zqcs_timer.wait.eq(~zqcs_executer.done)
356
357 # Refresh FSM ------------------------------------------------------------------------------
358 with m.FSM():
359 with m.State("Idle"):
360 with m.If(settings.with_refresh & wants_refresh):
361 m.next = "Wait-Bank-Machines"
362
363 with m.State("Wait-Bank-Machines"):
364 m.d.comb += self.cmd.valid.eq(1)
365 with m.If(self.cmd.ready):
366 m.d.comb += sequencer.start.eq(1)
367 m.next = "Do-Refresh"
368
369 if settings.timing.tZQCS is None:
370 with m.State("Do-Refresh"):
371 m.d.comb += self.cmd.valid.eq(1)
372 with m.If(sequencer.done):
373 m.d.comb += [
374 self.cmd.valid.eq(0),
375 self.cmd.last.eq(1),
376 ]
377 m.next = "Idle"
378 else:
379 with m.State("Do-Refresh"):
380 m.d.comb += self.cmd.valid.eq(1)
381 with m.If(sequencer.done):
382 with m.If(wants_zqcs):
383 m.d.comb += zqcs_executer.start.eq(1)
384 m.next = "Do-Zqcs"
385 with m.Else():
386 m.d.comb += [
387 self.cmd.valid.eq(0),
388 self.cmd.last.eq(1),
389 ]
390 m.next = "Idle"
391
392 with m.State("Do-Zqcs"):
393 m.d.comb += self.cmd.valid.eq(1)
394 with m.If(zqcs_executer.done):
395 m.d.comb += [
396 self.cmd.valid.eq(0),
397 self.cmd.last.eq(1),
398 ]
399 m.next = "Idle"
400
401 # Connect sequencer/executer outputs to cmd
402 if settings.timing.tZQCS is None:
403 m.d.comb += [
404 self.cmd.a.eq(sequencer.a),
405 self.cmd.ba.eq(sequencer.ba),
406 self.cmd.cas.eq(sequencer.cas),
407 self.cmd.ras.eq(sequencer.ras),
408 self.cmd.we.eq(sequencer.we),
409 ]
410 else:
411 m.d.comb += [
412 self.cmd.a.eq(zqcs_executer.a),
413 self.cmd.ba.eq(zqcs_executer.ba),
414 self.cmd.cas.eq(zqcs_executer.cas),
415 self.cmd.ras.eq(zqcs_executer.ras),
416 self.cmd.we.eq(zqcs_executer.we),
417 ]
418
419
420 return m