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>
8 from functools
import reduce
9 from operator
import add
10 from collections
import OrderedDict
13 from nmigen
.hdl
.rec
import *
14 from nmigen
.utils
import log2_int
16 import gram
.stream
as stream
18 # Helpers ------------------------------------------------------------------------------------------
30 def get_cl_cw(memtype
, tck
):
31 f_to_cl_cwl
= OrderedDict()
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)
47 for f
, (cl
, cwl
) in f_to_cl_cwl
.items():
53 def get_sys_latency(nphases
, cas_latency
):
54 return math
.ceil(cas_latency
/nphases
)
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
62 # PHY Pads Transformers ----------------------------------------------------------------------------
68 Reduce DRAM pads to only use specific modules.
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.
74 def __init__(self
, pads
, modules
):
76 self
.modules
= modules
78 def __getattr__(self
, name
):
80 return Array([getattr(self
.pads
, name
)[8*i
+ j
]
83 if name
in ["dm", "dqs", "dqs_p", "dqs_n"]:
84 return Array([getattr(self
.pads
, name
)[i
] for i
in self
.modules
])
86 return getattr(self
.pads
, name
)
89 class PHYPadsCombiner
:
92 Combine DRAM pads from fully dissociated chips in a unique DRAM pads structure.
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.
100 def __init__(self
, pads
):
101 if not isinstance(pads
, list):
107 def sel_group(self
, n
):
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
))])
116 return getattr(self
.groups
[self
.sel
], name
)
118 # DQS Pattern --------------------------------------------------------------------------------------
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
126 self
._wlevel
_en
= wlevel_en
127 self
._wlevel
_strobe
= wlevel_strobe
128 self
._register
= register
130 def elaborate(self
, platform
):
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)
141 m
.d
.comb
+= self
.o
.eq(0b00000000)
143 m
.d
.comb
+= self
.o
.eq(0b01010101)
146 o
= Signal
.like(self
.o
)
147 m
.d
.sync
+= o
.eq(self
.o
)
152 # Settings -----------------------------------------------------------------------------------------
156 def set_attributes(self
, attributes
):
157 for k
, v
in attributes
.items():
161 class PhySettings(Settings
):
162 def __init__(self
, phytype
, memtype
, databits
, dfi_databits
,
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
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())
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"
183 self
.set_attributes(locals())
186 class GeomSettings(Settings
):
187 def __init__(self
, bankbits
, rowbits
, colbits
):
188 self
.set_attributes(locals())
189 self
.addressbits
= max(rowbits
, colbits
)
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())
196 # Layouts/Interface --------------------------------------------------------------------------------
199 def cmd_layout(address_width
):
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
207 ("wdata_ready", 1, DIR_FANIN
),
208 ("rdata_valid", 1, DIR_FANIN
)
212 def data_layout(data_width
):
214 ("wdata", data_width
, DIR_FANOUT
),
215 ("wdata_we", data_width
//8, DIR_FANOUT
),
216 ("rdata", data_width
, DIR_FANIN
)
220 def cmd_description(address_width
):
223 ("addr", address_width
)
227 def wdata_description(data_width
):
229 ("data", data_width
),
230 ("we", data_width
//8)
234 def rdata_description(data_width
):
235 return [("data", data_width
)]
238 def cmd_request_layout(a
, ba
):
248 def cmd_request_rw_layout(a
, ba
):
249 return cmd_request_layout(a
, ba
) + [
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
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
)
272 # Ports --------------------------------------------------------------------------------------------
275 class gramNativePort(Settings
):
276 def __init__(self
, mode
, address_width
, data_width
, clock_domain
="sync", id=0):
277 self
.set_attributes(locals())
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
))
285 self
.flush
= Signal()
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
]
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
:
295 return Cat(self
.cmd
.addr
[:cba_shift
], self
.cmd
.addr
[cba_upper
:])
297 return self
.cmd
.addr
[cba_upper
:]
299 return self
.cmd
.addr
[:cba_shift
]
302 class gramNativeWritePort(gramNativePort
):
303 def __init__(self
, *args
, **kwargs
):
304 gramNativePort
.__init
__(self
, "write", *args
, **kwargs
)
307 class gramNativeReadPort(gramNativePort
):
308 def __init__(self
, *args
, **kwargs
):
309 gramNativePort
.__init
__(self
, "read", *args
, **kwargs
)
312 # Timing Controllers -------------------------------------------------------------------------------
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})
320 def elaborate(self
, platform
):
323 if self
._txxd
is not None:
324 count
= Signal(range(max(self
._txxd
, 2)))
325 with m
.If(self
.valid
):
327 count
.eq(self
._txxd
-1),
328 self
.ready
.eq((self
._txxd
- 1) == 0),
331 m
.d
.sync
+= count
.eq(count
-1)
332 with m
.If(count
== 1):
333 m
.d
.sync
+= self
.ready
.eq(1)
337 class tFAWController(Elaboratable
):
338 def __init__(self
, tfaw
):
339 self
.valid
= Signal()
340 self
.ready
= Signal(reset
=1, attrs
={"no_retiming": True})
343 def elaborate(self
, platform
):
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
)
356 m
.d
.sync
+= self
.ready
.eq(1)