Add tests for core/refresher.py
[gram.git] / gram / common.py
1 # This file is Copyright (c) 2016-2019 Florent Kermarrec <florent@enjoy-digital.fr>
2 # This file is Copyright (c) 2018 John Sully <john@csquare.ca>
3 # This file is Copyright (c) 2018 bunnie <bunnie@kosagi.com>
4 # This file is Copyright (c) 2020 LambdaConcept <contact@lambdaconcept.com>
5 # License: BSD
6
7 import math
8 from functools import reduce
9 from operator import add
10 from collections import OrderedDict
11
12 from nmigen import *
13 from nmigen.hdl.rec import *
14 from nmigen.utils import log2_int
15
16 import gram.stream as stream
17
18 # Helpers ------------------------------------------------------------------------------------------
19
20 burst_lengths = {
21 "SDR": 1,
22 "DDR": 4,
23 "LPDDR": 4,
24 "DDR2": 4,
25 "DDR3": 8,
26 "DDR4": 8
27 }
28
29
30 def get_cl_cw(memtype, tck):
31 f_to_cl_cwl = OrderedDict()
32 if memtype == "DDR2":
33 f_to_cl_cwl[400e6] = (3, 2)
34 f_to_cl_cwl[533e6] = (4, 3)
35 f_to_cl_cwl[677e6] = (5, 4)
36 f_to_cl_cwl[800e6] = (6, 5)
37 f_to_cl_cwl[1066e6] = (7, 5)
38 elif memtype == "DDR3":
39 f_to_cl_cwl[800e6] = (6, 5)
40 f_to_cl_cwl[1066e6] = (7, 6)
41 f_to_cl_cwl[1333e6] = (10, 7)
42 f_to_cl_cwl[1600e6] = (11, 8)
43 elif memtype == "DDR4":
44 f_to_cl_cwl[1600e6] = (11, 9)
45 else:
46 raise ValueError
47 for f, (cl, cwl) in f_to_cl_cwl.items():
48 if tck >= 2/f:
49 return cl, cwl
50 raise ValueError
51
52
53 def get_sys_latency(nphases, cas_latency):
54 return math.ceil(cas_latency/nphases)
55
56
57 def get_sys_phases(nphases, sys_latency, cas_latency):
58 dat_phase = sys_latency*nphases - cas_latency
59 cmd_phase = (dat_phase - 1) % nphases
60 return cmd_phase, dat_phase
61
62 # PHY Pads Transformers ----------------------------------------------------------------------------
63
64
65 class PHYPadsReducer:
66 """PHY Pads Reducer
67
68 Reduce DRAM pads to only use specific modules.
69
70 For testing purposes, we often need to use only some of the DRAM modules. PHYPadsReducer allows
71 selecting specific modules and avoid re-definining dram pins in the Platform for this.
72 """
73
74 def __init__(self, pads, modules):
75 self.pads = pads
76 self.modules = modules
77
78 def __getattr__(self, name):
79 if name in ["dq"]:
80 return Array([getattr(self.pads, name)[8*i + j]
81 for i in self.modules
82 for j in range(8)])
83 if name in ["dm", "dqs", "dqs_p", "dqs_n"]:
84 return Array([getattr(self.pads, name)[i] for i in self.modules])
85 else:
86 return getattr(self.pads, name)
87
88
89 class PHYPadsCombiner:
90 """PHY Pads Combiner
91
92 Combine DRAM pads from fully dissociated chips in a unique DRAM pads structure.
93
94 Most generally, DRAM chips are sharing command/address lines between chips (using a fly-by
95 topology since DDR3). On some boards, the DRAM chips are using separate command/address lines
96 and this combiner can be used to re-create a single pads structure (that will be compatible with
97 LiteDRAM's PHYs) to create a single DRAM controller from multiple fully dissociated DRAMs chips.
98 """
99
100 def __init__(self, pads):
101 if not isinstance(pads, list):
102 self.groups = [pads]
103 else:
104 self.groups = pads
105 self.sel = 0
106
107 def sel_group(self, n):
108 self.sel = n
109
110 def __getattr__(self, name):
111 if name in ["dm", "dq", "dqs", "dqs_p", "dqs_n"]:
112 return Array([getattr(self.groups[j], name)[i]
113 for i in range(len(getattr(self.groups[0], name)))
114 for j in range(len(self.groups))])
115 else:
116 return getattr(self.groups[self.sel], name)
117
118 # BitSlip ------------------------------------------------------------------------------------------
119
120
121 class BitSlip(Elaboratable):
122 def __init__(self, dw, rst=None, slp=None, cycles=1):
123 self.i = Signal(dw)
124 self.o = Signal(dw)
125 self.rst = Signal() if rst is None else rst
126 self.slp = Signal() if slp is None else slp
127 self._cycles = cycles
128
129 def elaborate(self, platform):
130 m = Module()
131
132 value = Signal(range(self._cycles*dw))
133 with m.If(self.slp):
134 m.d.sync += value.eq(value+1)
135 with m.Elif(self.rst):
136 m.d.sync += value.eq(0)
137
138 r = Signal((self._cycles+1)*dw, reset_less=True)
139 m.d.sync += r.eq(Cat(r[dw:], self.i))
140 cases = {}
141 for i in range(self._cycles*dw):
142 cases[i] = self.o.eq(r[i:dw+i])
143 m.d.comb += Case(value, cases)
144
145 return m
146
147 # DQS Pattern --------------------------------------------------------------------------------------
148
149
150 class DQSPattern(Elaboratable):
151 def __init__(self, preamble=None, postamble=None, wlevel_en=0, wlevel_strobe=0, register=False):
152 self.preamble = Signal() if preamble is None else preamble
153 self.postamble = Signal() if postamble is None else postamble
154 self.o = Signal(8)
155 self._wlevel_en = wlevel_en
156 self._wlevel_strobe = wlevel_strobe
157 self._register = register
158
159 def elaborate(self, platform):
160 m = Module()
161
162 with m.If(self.preamble):
163 m.d.comb += self.o.eq(0b00010101)
164 with m.Elif(self.postamble):
165 m.d.comb += self.o.eq(0b01010100)
166 with m.Elif(self._wlevel_en):
167 with m.If(self._wlevel_strobe):
168 m.d.comb += self.o.eq(0b00000001)
169 with m.Else():
170 m.d.comb += self.o.eq(0b00000000)
171 with m.Else():
172 m.d.comb += self.o.eq(0b01010101)
173
174 if self._register:
175 o = Signal.like(self.o)
176 m.d.sync += o.eq(self.o)
177 self.o = o
178
179 return m
180
181 # Settings -----------------------------------------------------------------------------------------
182
183
184 class Settings:
185 def set_attributes(self, attributes):
186 for k, v in attributes.items():
187 setattr(self, k, v)
188
189
190 class PhySettings(Settings):
191 def __init__(self, phytype, memtype, databits, dfi_databits,
192 nphases,
193 rdphase, wrphase,
194 rdcmdphase, wrcmdphase,
195 cl, read_latency, write_latency, nranks=1, cwl=None):
196 self.set_attributes(locals())
197 self.cwl = cl if cwl is None else cwl
198 self.is_rdimm = False
199
200 # Optional DDR3/DDR4 electrical settings:
201 # rtt_nom: Non-Writes on-die termination impedance
202 # rtt_wr: Writes on-die termination impedance
203 # ron: Output driver impedance
204 def add_electrical_settings(self, rtt_nom, rtt_wr, ron):
205 assert self.memtype in ["DDR3", "DDR4"]
206 self.set_attributes(locals())
207
208 # Optional RDIMM configuration
209 def set_rdimm(self, tck, rcd_pll_bypass, rcd_ca_cs_drive, rcd_odt_cke_drive, rcd_clk_drive):
210 assert self.memtype == "DDR4"
211 self.is_rdimm = True
212 self.set_attributes(locals())
213
214
215 class GeomSettings(Settings):
216 def __init__(self, bankbits, rowbits, colbits):
217 self.set_attributes(locals())
218 self.addressbits = max(rowbits, colbits)
219
220
221 class TimingSettings(Settings):
222 def __init__(self, tRP, tRCD, tWR, tWTR, tREFI, tRFC, tFAW, tCCD, tRRD, tRC, tRAS, tZQCS):
223 self.set_attributes(locals())
224
225 # Layouts/Interface --------------------------------------------------------------------------------
226
227
228 def cmd_layout(address_width):
229 return [
230 ("valid", 1, DIR_FANOUT),
231 ("ready", 1, DIR_FANIN),
232 ("we", 1, DIR_FANOUT),
233 ("addr", address_width, DIR_FANOUT),
234 ("lock", 1, DIR_FANIN), # only used internally
235
236 ("wdata_ready", 1, DIR_FANIN),
237 ("rdata_valid", 1, DIR_FANIN)
238 ]
239
240
241 def data_layout(data_width):
242 return [
243 ("wdata", data_width, DIR_FANOUT),
244 ("wdata_we", data_width//8, DIR_FANOUT),
245 ("rdata", data_width, DIR_FANIN)
246 ]
247
248
249 def cmd_description(address_width):
250 return [
251 ("we", 1),
252 ("addr", address_width)
253 ]
254
255
256 def wdata_description(data_width):
257 return [
258 ("data", data_width),
259 ("we", data_width//8)
260 ]
261
262
263 def rdata_description(data_width):
264 return [("data", data_width)]
265
266
267 def cmd_request_layout(a, ba):
268 return [
269 ("a", a),
270 ("ba", ba),
271 ("cas", 1),
272 ("ras", 1),
273 ("we", 1)
274 ]
275
276
277 def cmd_request_rw_layout(a, ba):
278 return cmd_request_layout(a, ba) + [
279 ("is_cmd", 1),
280 ("is_read", 1),
281 ("is_write", 1)
282 ]
283
284
285 class gramInterface(Record):
286 def __init__(self, address_align, settings):
287 rankbits = log2_int(settings.phy.nranks)
288 self.address_align = address_align
289 self.address_width = settings.geom.rowbits + \
290 settings.geom.colbits + rankbits - address_align
291 self.data_width = settings.phy.dfi_databits*settings.phy.nphases
292 self.nbanks = settings.phy.nranks*(2**settings.geom.bankbits)
293 self.nranks = settings.phy.nranks
294 self.settings = settings
295
296 layout = [("bank"+str(i), cmd_layout(self.address_width))
297 for i in range(self.nbanks)]
298 layout += data_layout(self.data_width)
299 Record.__init__(self, layout)
300
301 # Ports --------------------------------------------------------------------------------------------
302
303
304 class gramNativePort(Settings):
305 def __init__(self, mode, address_width, data_width, clock_domain="sync", id=0):
306 self.set_attributes(locals())
307
308 self.lock = Signal()
309
310 self.cmd = stream.Endpoint(cmd_description(address_width))
311 self.wdata = stream.Endpoint(wdata_description(data_width))
312 self.rdata = stream.Endpoint(rdata_description(data_width))
313
314 self.flush = Signal()
315
316 def get_bank_address(self, bank_bits, cba_shift):
317 cba_upper = cba_shift + bank_bits
318 return self.cmd.addr[cba_shift:cba_upper]
319
320 def get_row_column_address(self, bank_bits, rca_bits, cba_shift):
321 cba_upper = cba_shift + bank_bits
322 if cba_shift < rca_bits:
323 if cba_shift:
324 return Cat(self.cmd.addr[:cba_shift], self.cmd.addr[cba_upper:])
325 else:
326 return self.cmd.addr[cba_upper:]
327 else:
328 return self.cmd.addr[:cba_shift]
329
330
331 class gramNativeWritePort(gramNativePort):
332 def __init__(self, *args, **kwargs):
333 gramNativePort.__init__(self, "write", *args, **kwargs)
334
335
336 class gramNativeReadPort(gramNativePort):
337 def __init__(self, *args, **kwargs):
338 gramNativePort.__init__(self, "read", *args, **kwargs)
339
340
341 # Timing Controllers -------------------------------------------------------------------------------
342
343 class tXXDController(Elaboratable):
344 def __init__(self, txxd):
345 self.valid = Signal()
346 self.ready = ready = Signal(reset=txxd is None, attrs={"no_retiming": True})
347 self._txxd = txxd
348
349 def elaborate(self, platform):
350 m = Module()
351
352 if self._txxd is not None:
353 count = Signal(range(max(self._txxd, 2)))
354 with m.If(self.valid):
355 m.d.sync += [
356 count.eq(self._txxd-1),
357 self.ready.eq((self._txxd - 1) == 0),
358 ]
359 with m.Else():
360 m.d.sync += count.eq(count-1)
361 with m.If(count == 1):
362 m.d.sync += self.ready.eq(1)
363 return m
364
365
366 class tFAWController(Elaboratable):
367 def __init__(self, tfaw):
368 self.valid = Signal()
369 self.ready = Signal(reset=1, attrs={"no_retiming": True})
370 self._tfaw = tfaw
371
372 def elaborate(self, platform):
373 m = Module()
374
375 if self._tfaw is not None:
376 count = Signal(range(max(self._tfaw, 2)))
377 window = Signal(self._tfaw)
378 m.d.sync += window.eq(Cat(self.valid, window))
379 m.d.comb += count.eq(reduce(add, [window[i]
380 for i in range(self._tfaw)]))
381 with m.If(count < 4):
382 with m.If(count == 3):
383 m.d.sync += self.ready.eq(~self.valid)
384 with m.Else():
385 m.d.sync += self.ready.eq(1)
386
387 return m