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