Fix formal checks for RefreshTimer
[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 == (count == 0)),
150 Assert(countDiffZero == (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 # TODO: we don't pass formal verification for trefi = 1
166 assert trefi != 1
167
168 self.wait = Signal()
169 self.done = Signal()
170 self.count = Signal(range(trefi), reset=trefi-1)
171 self._trefi = trefi
172
173 def elaborate(self, platform):
174 m = Module()
175
176 trefi = self._trefi
177
178 with m.If(self.wait & (self.count != 0)):
179 m.d.sync += self.count.eq(self.count-1)
180
181 with m.If(self.count == 1):
182 m.d.sync += self.done.eq(1)
183 with m.Else():
184 m.d.sync += [
185 self.count.eq(self.count.reset),
186 self.done.eq(0),
187 ]
188
189 if platform == "formal":
190 m.d.comb += Assert(self.done == (self.count == 0))
191
192 return m
193
194 # RefreshPostponer -------------------------------------------------------------------------------
195
196
197 class RefreshPostponer(Elaboratable):
198 """Refresh Postponer
199
200 Postpone N Refresh requests and generate a request when N is reached.
201 """
202
203 def __init__(self, postponing=1):
204 self.req_i = Signal()
205 self.req_o = Signal()
206 self._postponing = postponing
207
208 def elaborate(self, platform):
209 m = Module()
210
211 count = Signal(range(self._postponing), reset=self._postponing-1)
212
213 with m.If(self.req_i):
214 with m.If(count == 0):
215 m.d.sync += [
216 count.eq(count.reset),
217 self.req_o.eq(1),
218 ]
219 with m.Else():
220 m.d.sync += [
221 count.eq(count-1),
222 self.req_o.eq(0),
223 ]
224 with m.Else():
225 m.d.sync += self.req_o.eq(0)
226
227 return m
228
229 # ZQCSExecuter ----------------------------------------------------------------------------------
230
231
232 class ZQCSExecuter(Elaboratable):
233 """ZQ Short Calibration Executer
234
235 Execute the ZQCS sequence to the DRAM:
236 - Send a "Precharge All" command
237 - Wait tRP
238 - Send an "ZQ Short Calibration" command
239 - Wait tZQCS
240 """
241
242 def __init__(self, abits, babits, trp, tzqcs):
243 self.start = Signal()
244 self.done = Signal()
245 self._trp = trp
246 self._tzqcs = tzqcs
247
248 self.a = Signal(abits)
249 self.ba = Signal(babits)
250 self.cas = Signal()
251 self.ras = Signal()
252 self.we = Signal()
253
254 def elaborate(self, platform):
255 m = Module()
256
257 trp = self._trp
258 tzqcs = self._tzqcs
259
260 tl = Timeline([
261 # Precharge All
262 (0, [
263 self.a.eq(2**10),
264 self.ba.eq(0),
265 self.cas.eq(0),
266 self.ras.eq(1),
267 self.we.eq(1),
268 self.done.eq(0)
269 ]),
270 # ZQ Short Calibration after tRP
271 (trp, [
272 self.a.eq(0),
273 self.ba.eq(0),
274 self.cas.eq(0),
275 self.ras.eq(0),
276 self.we.eq(1),
277 self.done.eq(0),
278 ]),
279 # Done after tRP + tZQCS
280 (trp + tzqcs, [
281 self.a.eq(0),
282 self.ba.eq(0),
283 self.cas.eq(0),
284 self.ras.eq(0),
285 self.we.eq(0),
286 self.done.eq(1)
287 ]),
288 ])
289 m.submodules += tl
290 m.d.comb += tl.trigger.eq(self.start)
291
292 return m
293
294 # Refresher ----------------------------------------------------------------------------------------
295
296
297 class Refresher(Elaboratable):
298 """Refresher
299
300 Manage DRAM refresh.
301
302 The DRAM needs to be periodically refreshed with a tREFI period to avoid data corruption. During
303 a refresh, the controller send a "Precharge All" command to close and precharge all rows and then
304 send a "Auto Refresh" command.
305
306 Before executing the refresh, the Refresher advertises the Controller that a refresh should occur,
307 this allows the Controller to finish the current transaction and block next transactions. Once all
308 transactions are done, the Refresher can execute the refresh Sequence and release the Controller.
309
310 """
311
312 def __init__(self, settings, clk_freq, zqcs_freq=1e0, postponing=1):
313 assert postponing <= 8
314 self._abits = settings.geom.addressbits
315 self._babits = settings.geom.bankbits + log2_int(settings.phy.nranks)
316 self.cmd = cmd = stream.Endpoint(
317 cmd_request_rw_layout(a=self._abits, ba=self._babits))
318 self._postponing = postponing
319 self._settings = settings
320 self._clk_freq = clk_freq
321 self._zqcs_freq = zqcs_freq
322
323 def elaborate(self, platform):
324 m = Module()
325
326 wants_refresh = Signal()
327
328 settings = self._settings
329
330 # Refresh Timer ----------------------------------------------------------------------------
331 timer = RefreshTimer(settings.timing.tREFI)
332 m.submodules.timer = timer
333 m.d.comb += timer.wait.eq(~timer.done)
334
335 # Refresh Postponer ------------------------------------------------------------------------
336 postponer = RefreshPostponer(self._postponing)
337 m.submodules.postponer = postponer
338 m.d.comb += [
339 postponer.req_i.eq(timer.done),
340 wants_refresh.eq(postponer.req_o),
341 ]
342
343 # Refresh Sequencer ------------------------------------------------------------------------
344 sequencer = RefreshSequencer(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(self._abits, self._babits, settings.timing.tRP, settings.timing.tZQCS)
357 m.submodules.zqs_executer = zqcs_executer
358 m.d.comb += zqcs_timer.wait.eq(~zqcs_executer.done)
359
360 # Refresh FSM ------------------------------------------------------------------------------
361 with m.FSM():
362 with m.State("Idle"):
363 with m.If(settings.with_refresh & wants_refresh):
364 m.next = "Wait-Bank-Machines"
365
366 with m.State("Wait-Bank-Machines"):
367 m.d.comb += self.cmd.valid.eq(1)
368 with m.If(self.cmd.ready):
369 m.d.comb += sequencer.start.eq(1)
370 m.next = "Do-Refresh"
371
372 if settings.timing.tZQCS is None:
373 with m.State("Do-Refresh"):
374 m.d.comb += self.cmd.valid.eq(1)
375 with m.If(sequencer.done):
376 m.d.comb += [
377 self.cmd.valid.eq(0),
378 self.cmd.last.eq(1),
379 ]
380 m.next = "Idle"
381 else:
382 with m.State("Do-Refresh"):
383 m.d.comb += self.cmd.valid.eq(1)
384 with m.If(sequencer.done):
385 with m.If(wants_zqcs):
386 m.d.comb += zqcs_executer.start.eq(1)
387 m.next = "Do-Zqcs"
388 with m.Else():
389 m.d.comb += [
390 self.cmd.valid.eq(0),
391 self.cmd.last.eq(1),
392 ]
393 m.next = "Idle"
394
395 with m.State("Do-Zqcs"):
396 m.d.comb += self.cmd.valid.eq(1)
397 with m.If(zqcs_executer.done):
398 m.d.comb += [
399 self.cmd.valid.eq(0),
400 self.cmd.last.eq(1),
401 ]
402 m.next = "Idle"
403
404 # Connect sequencer/executer outputs to cmd
405 if settings.timing.tZQCS is None:
406 m.d.comb += [
407 self.cmd.a.eq(sequencer.a),
408 self.cmd.ba.eq(sequencer.ba),
409 self.cmd.cas.eq(sequencer.cas),
410 self.cmd.ras.eq(sequencer.ras),
411 self.cmd.we.eq(sequencer.we),
412 ]
413 else:
414 m.d.comb += [
415 self.cmd.a.eq(zqcs_executer.a),
416 self.cmd.ba.eq(zqcs_executer.ba),
417 self.cmd.cas.eq(zqcs_executer.cas),
418 self.cmd.ras.eq(zqcs_executer.ras),
419 self.cmd.we.eq(zqcs_executer.we),
420 ]
421
422
423 return m