Rename VCD file output
[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 # DQS Pattern --------------------------------------------------------------------------------------
119
120
121 class DQSPattern(Elaboratable):
122 def __init__(self, preamble=None, postamble=None, wlevel_en=0, wlevel_strobe=0, register=False):
123 self.preamble = Signal() if preamble is None else preamble
124 self.postamble = Signal() if postamble is None else postamble
125 self.o = Signal(8)
126 self._wlevel_en = wlevel_en
127 self._wlevel_strobe = wlevel_strobe
128 self._register = register
129
130 def elaborate(self, platform):
131 m = Module()
132
133 with m.If(self.preamble):
134 m.d.comb += self.o.eq(0b00010101)
135 with m.Elif(self.postamble):
136 m.d.comb += self.o.eq(0b01010100)
137 with m.Elif(self._wlevel_en):
138 with m.If(self._wlevel_strobe):
139 m.d.comb += self.o.eq(0b00000001)
140 with m.Else():
141 m.d.comb += self.o.eq(0b00000000)
142 with m.Else():
143 m.d.comb += self.o.eq(0b01010101)
144
145 if self._register:
146 o = Signal.like(self.o)
147 m.d.sync += o.eq(self.o)
148 self.o = o
149
150 return m
151
152 # Settings -----------------------------------------------------------------------------------------
153
154
155 class Settings:
156 def set_attributes(self, attributes):
157 for k, v in attributes.items():
158 setattr(self, k, v)
159
160
161 class PhySettings(Settings):
162 def __init__(self, phytype, memtype, databits, dfi_databits,
163 nphases,
164 rdphase, wrphase,
165 rdcmdphase, wrcmdphase,
166 cl, read_latency, write_latency, nranks=1, cwl=None):
167 self.set_attributes(locals())
168 self.cwl = cl if cwl is None else cwl
169 self.is_rdimm = False
170
171 # Optional DDR3/DDR4 electrical settings:
172 # rtt_nom: Non-Writes on-die termination impedance
173 # rtt_wr: Writes on-die termination impedance
174 # ron: Output driver impedance
175 def add_electrical_settings(self, rtt_nom, rtt_wr, ron):
176 assert self.memtype in ["DDR3", "DDR4"]
177 self.set_attributes(locals())
178
179 # Optional RDIMM configuration
180 def set_rdimm(self, tck, rcd_pll_bypass, rcd_ca_cs_drive, rcd_odt_cke_drive, rcd_clk_drive):
181 assert self.memtype == "DDR4"
182 self.is_rdimm = True
183 self.set_attributes(locals())
184
185
186 class GeomSettings(Settings):
187 def __init__(self, bankbits, rowbits, colbits):
188 self.set_attributes(locals())
189 self.addressbits = max(rowbits, colbits)
190
191
192 class TimingSettings(Settings):
193 def __init__(self, tRP, tRCD, tWR, tWTR, tREFI, tRFC, tFAW, tCCD, tRRD, tRC, tRAS, tZQCS):
194 self.set_attributes(locals())
195
196 # Layouts/Interface --------------------------------------------------------------------------------
197
198
199 def cmd_layout(address_width):
200 return [
201 ("valid", 1, DIR_FANOUT),
202 ("ready", 1, DIR_FANIN),
203 ("we", 1, DIR_FANOUT),
204 ("addr", address_width, DIR_FANOUT),
205 ("lock", 1, DIR_FANIN), # only used internally
206
207 ("wdata_ready", 1, DIR_FANIN),
208 ("rdata_valid", 1, DIR_FANIN)
209 ]
210
211
212 def data_layout(data_width):
213 return [
214 ("wdata", data_width, DIR_FANOUT),
215 ("wdata_we", data_width//8, DIR_FANOUT),
216 ("rdata", data_width, DIR_FANIN)
217 ]
218
219
220 def cmd_description(address_width):
221 return [
222 ("we", 1),
223 ("addr", address_width)
224 ]
225
226
227 def wdata_description(data_width):
228 return [
229 ("data", data_width),
230 ("we", data_width//8)
231 ]
232
233
234 def rdata_description(data_width):
235 return [("data", data_width)]
236
237
238 def cmd_request_layout(a, ba):
239 return [
240 ("a", a),
241 ("ba", ba),
242 ("cas", 1),
243 ("ras", 1),
244 ("we", 1)
245 ]
246
247
248 def cmd_request_rw_layout(a, ba):
249 return cmd_request_layout(a, ba) + [
250 ("is_cmd", 1),
251 ("is_read", 1),
252 ("is_write", 1)
253 ]
254
255
256 class gramInterface(Record):
257 def __init__(self, address_align, settings):
258 rankbits = log2_int(settings.phy.nranks)
259 self.address_align = address_align
260 self.address_width = settings.geom.rowbits + \
261 settings.geom.colbits + rankbits - address_align
262 self.data_width = settings.phy.dfi_databits*settings.phy.nphases
263 self.nbanks = settings.phy.nranks*(2**settings.geom.bankbits)
264 self.nranks = settings.phy.nranks
265 self.settings = settings
266
267 layout = [("bank"+str(i), cmd_layout(self.address_width))
268 for i in range(self.nbanks)]
269 layout += data_layout(self.data_width)
270 Record.__init__(self, layout)
271
272 # Ports --------------------------------------------------------------------------------------------
273
274
275 class gramNativePort(Settings):
276 def __init__(self, mode, address_width, data_width, clock_domain="sync", id=0):
277 self.set_attributes(locals())
278
279 self.lock = Signal()
280
281 self.cmd = stream.Endpoint(cmd_description(address_width))
282 self.wdata = stream.Endpoint(wdata_description(data_width))
283 self.rdata = stream.Endpoint(rdata_description(data_width))
284
285 self.flush = Signal()
286
287 def get_bank_address(self, bank_bits, cba_shift):
288 cba_upper = cba_shift + bank_bits
289 return self.cmd.addr[cba_shift:cba_upper]
290
291 def get_row_column_address(self, bank_bits, rca_bits, cba_shift):
292 cba_upper = cba_shift + bank_bits
293 if cba_shift < rca_bits:
294 if cba_shift:
295 return Cat(self.cmd.addr[:cba_shift], self.cmd.addr[cba_upper:])
296 else:
297 return self.cmd.addr[cba_upper:]
298 else:
299 return self.cmd.addr[:cba_shift]
300
301
302 class gramNativeWritePort(gramNativePort):
303 def __init__(self, *args, **kwargs):
304 gramNativePort.__init__(self, "write", *args, **kwargs)
305
306
307 class gramNativeReadPort(gramNativePort):
308 def __init__(self, *args, **kwargs):
309 gramNativePort.__init__(self, "read", *args, **kwargs)
310
311
312 # Timing Controllers -------------------------------------------------------------------------------
313
314 class tXXDController(Elaboratable):
315 def __init__(self, txxd):
316 self.valid = Signal()
317 self.ready = ready = Signal(reset=txxd is None, attrs={"no_retiming": True})
318 self._txxd = txxd
319
320 def elaborate(self, platform):
321 m = Module()
322
323 if self._txxd is not None:
324 count = Signal(range(max(self._txxd, 2)))
325 with m.If(self.valid):
326 m.d.sync += [
327 count.eq(self._txxd-1),
328 self.ready.eq((self._txxd - 1) == 0),
329 ]
330 with m.Else():
331 m.d.sync += count.eq(count-1)
332 with m.If(count == 1):
333 m.d.sync += self.ready.eq(1)
334 return m
335
336
337 class tFAWController(Elaboratable):
338 def __init__(self, tfaw):
339 self.valid = Signal()
340 self.ready = Signal(reset=1, attrs={"no_retiming": True})
341 self._tfaw = tfaw
342
343 def elaborate(self, platform):
344 m = Module()
345
346 if self._tfaw is not None:
347 count = Signal(range(max(self._tfaw, 2)))
348 window = Signal(self._tfaw)
349 m.d.sync += window.eq(Cat(self.valid, window))
350 m.d.comb += count.eq(reduce(add, [window[i]
351 for i in range(self._tfaw)]))
352 with m.If(count < 4):
353 with m.If(count == 3):
354 m.d.sync += self.ready.eq(~self.valid)
355 with m.Else():
356 m.d.sync += self.ready.eq(1)
357
358 return m