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