Remove unused code
[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="sys", 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 # retro-compatibility # FIXME: remove
317 self.aw = self.address_width
318 self.dw = self.data_width
319 self.cd = self.clock_domain
320
321 def get_bank_address(self, bank_bits, cba_shift):
322 cba_upper = cba_shift + bank_bits
323 return self.cmd.addr[cba_shift:cba_upper]
324
325 def get_row_column_address(self, bank_bits, rca_bits, cba_shift):
326 cba_upper = cba_shift + bank_bits
327 if cba_shift < rca_bits:
328 if cba_shift:
329 return Cat(self.cmd.addr[:cba_shift], self.cmd.addr[cba_upper:])
330 else:
331 return self.cmd.addr[cba_upper:]
332 else:
333 return self.cmd.addr[:cba_shift]
334
335
336 class gramNativeWritePort(gramNativePort):
337 def __init__(self, *args, **kwargs):
338 gramNativePort.__init__(self, "write", *args, **kwargs)
339
340
341 class gramNativeReadPort(gramNativePort):
342 def __init__(self, *args, **kwargs):
343 gramNativePort.__init__(self, "read", *args, **kwargs)
344
345
346 # Timing Controllers -------------------------------------------------------------------------------
347
348 class tXXDController(Elaboratable):
349 def __init__(self, txxd):
350 self.valid = Signal()
351 self.ready = ready = Signal(reset=txxd is None)
352 # ready.attr.add("no_retiming") TODO
353 self._txxd = txxd
354
355 def elaborate(self, platform):
356 m = Module()
357
358 if self._txxd is not None:
359 count = Signal(range(max(self._txxd, 2)))
360 with m.If(self.valid):
361 m.d.sync += [
362 count.eq(self._txxd-1),
363 self.ready.eq((self._txxd - 1) == 0),
364 ]
365 with m.Else():
366 m.d.sync += count.eq(count-1)
367 with m.If(count == 1):
368 m.d.sync += self.ready.eq(1)
369 return m
370
371
372 class tFAWController(Elaboratable):
373 def __init__(self, tfaw):
374 self.valid = Signal()
375 self.ready = Signal(reset=1)
376 # ready.attr.add("no_retiming") TODO
377 self._tfaw = tfaw
378
379 def elaborate(self, platform):
380 m = Module()
381
382 if self._tfaw is not None:
383 count = Signal(range(max(self._tfaw, 2)))
384 window = Signal(self._tfaw)
385 m.d.sync += window.eq(Cat(self.valid, window))
386 m.d.comb += count.eq(reduce(add, [window[i]
387 for i in range(self._tfaw)]))
388 with m.If(count < 4):
389 with m.If(count == 3):
390 m.d.sync += self.ready.eq(~self.valid)
391 with m.Else():
392 m.d.sync += self.ready.eq(1)
393
394 return m