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