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