45d88f7192493f6e3b874358e831e7b1fbb19b62
[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(cmd_request_rw_layout(a=self._abits, ba=self._babits))
314 self._postponing = postponing
315 self._settings = settings
316 self._clk_freq = clk_freq
317 self._zqcs_freq = zqcs_freq
318
319 def elaborate(self, platform):
320 m = Module()
321
322 wants_refresh = Signal()
323
324 settings = self._settings
325
326 # Refresh Timer ----------------------------------------------------------------------------
327 timer = RefreshTimer(settings.timing.tREFI)
328 m.submodules.timer = timer
329 m.d.comb += timer.wait.eq(~timer.done)
330
331 # Refresh Postponer ------------------------------------------------------------------------
332 postponer = RefreshPostponer(self._postponing)
333 m.submodules.postponer = postponer
334 m.d.comb += [
335 postponer.req_i.eq(timer.done),
336 wants_refresh.eq(postponer.req_o),
337 ]
338
339 # Refresh Sequencer ------------------------------------------------------------------------
340 sequencer = RefreshSequencer(self._abits, self._babits, settings.timing.tRP, settings.timing.tRFC, self._postponing)
341 m.submodules.sequencer = sequencer
342
343 if settings.timing.tZQCS is not None:
344
345 # ZQCS Timer ---------------------------------------------------------------------------
346 zqcs_timer = RefreshTimer(int(self._clk_freq/self._zqcs_freq))
347 m.submodules.zqcs_timer = zqcs_timer
348
349 # ZQCS Executer ------------------------------------------------------------------------
350 zqcs_executer = ZQCSExecuter(self._abits, self._babits, settings.timing.tRP, settings.timing.tZQCS)
351 m.submodules.zqcs_executer = zqcs_executer
352 m.d.comb += zqcs_timer.wait.eq(~zqcs_executer.done)
353
354 # Refresh FSM ------------------------------------------------------------------------------
355 with m.FSM():
356 with m.State("Idle"):
357 with m.If(settings.with_refresh & wants_refresh):
358 m.next = "Wait-Bank-Machines"
359
360 with m.State("Wait-Bank-Machines"):
361 m.d.comb += self.cmd.valid.eq(1)
362 with m.If(self.cmd.ready):
363 m.d.comb += sequencer.start.eq(1)
364 m.next = "Do-Refresh"
365
366 if settings.timing.tZQCS is None:
367 with m.State("Do-Refresh"):
368 m.d.comb += self.cmd.valid.eq(~sequencer.done)
369 with m.If(sequencer.done):
370 m.d.comb += self.cmd.last.eq(1)
371 m.next = "Idle"
372 else:
373 with m.State("Do-Refresh"):
374 m.d.comb += self.cmd.valid.eq(zqcs_timer.done & ~sequencer.done)
375 with m.If(sequencer.done):
376 with m.If(zqcs_timer.done):
377 m.d.comb += zqcs_executer.start.eq(1)
378 m.next = "Do-Zqcs"
379 with m.Else():
380 m.d.comb += self.cmd.last.eq(1)
381 m.next = "Idle"
382
383 with m.State("Do-Zqcs"):
384 m.d.comb += self.cmd.valid.eq(~zqcs_executer.done)
385 with m.If(zqcs_executer.done):
386 m.d.comb += self.cmd.last.eq(1)
387 m.next = "Idle"
388
389 # Connect sequencer/executer outputs to cmd
390 if settings.timing.tZQCS is None:
391 m.d.comb += [
392 self.cmd.a.eq(sequencer.a),
393 self.cmd.ba.eq(sequencer.ba),
394 self.cmd.cas.eq(sequencer.cas),
395 self.cmd.ras.eq(sequencer.ras),
396 self.cmd.we.eq(sequencer.we),
397 ]
398 else:
399 m.d.comb += [
400 self.cmd.a.eq(zqcs_executer.a),
401 self.cmd.ba.eq(zqcs_executer.ba),
402 self.cmd.cas.eq(zqcs_executer.cas),
403 self.cmd.ras.eq(zqcs_executer.ras),
404 self.cmd.we.eq(zqcs_executer.we),
405 ]
406
407
408 return m