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