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