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>
7 from functools
import reduce
8 from operator
import add
9 from collections
import OrderedDict
12 from nmigen
.hdl
.rec
import *
13 from nmigen
.utils
import log2_int
15 import gram
.stream
as stream
17 # Helpers ------------------------------------------------------------------------------------------
28 def get_cl_cw(memtype
, tck
):
29 f_to_cl_cwl
= OrderedDict()
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)
45 for f
, (cl
, cwl
) in f_to_cl_cwl
.items():
50 def get_sys_latency(nphases
, cas_latency
):
51 return math
.ceil(cas_latency
/nphases
)
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
58 # PHY Pads Transformers ----------------------------------------------------------------------------
63 Reduce DRAM pads to only use specific modules.
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.
68 def __init__(self
, pads
, modules
):
70 self
.modules
= modules
72 def __getattr__(self
, name
):
74 return Array([getattr(self
.pads
, name
)[8*i
+ j
]
77 if name
in ["dm", "dqs", "dqs_p", "dqs_n"]:
78 return Array([getattr(self
.pads
, name
)[i
] for i
in self
.modules
])
80 return getattr(self
.pads
, name
)
82 class PHYPadsCombiner
:
85 Combine DRAM pads from fully dissociated chips in a unique DRAM pads structure.
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.
92 def __init__(self
, pads
):
93 if not isinstance(pads
, list):
99 def sel_group(self
, n
):
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
))])
108 return getattr(self
.groups
[self
.sel
], name
)
110 # BitSlip ------------------------------------------------------------------------------------------
112 class BitSlip(Elaboratable
):
113 def __init__(self
, dw
, rst
=None, slp
=None, cycles
=1):
116 self
.rst
= Signal() if rst
is None else rst
117 self
.slp
= Signal() if slp
is None else slp
118 self
._cycles
= cycles
120 def elaborate(self
, platform
):
123 value
= Signal(range(self
._cycles
*dw
))
125 m
.d
.sync
+= value
.eq(value
+1)
126 with m
.Elif(self
.rst
):
127 m
.d
.sync
+= value
.eq(0)
129 r
= Signal((self
._cycles
+1)*dw
, reset_less
=True)
130 m
.d
.sync
+= r
.eq(Cat(r
[dw
:], self
.i
))
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
)
138 # DQS Pattern --------------------------------------------------------------------------------------
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
145 self
._wlevel
_en
= wlevel_en
146 self
._wlevel
_strobe
= wlevel_strobe
147 self
._register
= register
149 def elaborate(self
, platform
):
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)
160 m
.d
.comb
+= self
.o
.eq(0b00000000)
162 m
.d
.comb
+= self
.o
.eq(0b01010101)
165 o
= Signal
.like(self
.o
)
166 m
.d
.sync
+= o
.eq(self
.o
)
171 # Settings -----------------------------------------------------------------------------------------
174 def set_attributes(self
, attributes
):
175 for k
, v
in attributes
.items():
179 class PhySettings(Settings
):
180 def __init__(self
, phytype
, memtype
, databits
, dfi_databits
,
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
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())
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"
201 self
.set_attributes(locals())
203 class GeomSettings(Settings
):
204 def __init__(self
, bankbits
, rowbits
, colbits
):
205 self
.set_attributes(locals())
206 self
.addressbits
= max(rowbits
, colbits
)
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())
213 # Layouts/Interface --------------------------------------------------------------------------------
215 def cmd_layout(address_width
):
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
223 ("wdata_ready", 1, DIR_FANIN
),
224 ("rdata_valid", 1, DIR_FANIN
)
227 def data_layout(data_width
):
229 ("wdata", data_width
, DIR_FANOUT
),
230 ("wdata_we", data_width
//8, DIR_FANOUT
),
231 ("rdata", data_width
, DIR_FANIN
)
234 def cmd_description(address_width
):
237 ("addr", address_width
)
240 def wdata_description(data_width
):
242 ("data", data_width
),
243 ("we", data_width
//8)
246 def rdata_description(data_width
):
247 return [("data", data_width
)]
249 def cmd_request_layout(a
, ba
):
258 def cmd_request_rw_layout(a
, ba
):
259 return cmd_request_layout(a
, ba
) + [
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
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
)
280 # Ports --------------------------------------------------------------------------------------------
282 class gramNativePort(Settings
):
283 def __init__(self
, mode
, address_width
, data_width
, clock_domain
="sys", id=0):
284 self
.set_attributes(locals())
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
))
292 self
.flush
= Signal()
294 # retro-compatibility # FIXME: remove
295 self
.aw
= self
.address_width
296 self
.dw
= self
.data_width
297 self
.cd
= self
.clock_domain
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
]
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
:
307 return Cat(self
.cmd
.addr
[:cba_shift
], self
.cmd
.addr
[cba_upper
:])
309 return self
.cmd
.addr
[cba_upper
:]
311 return self
.cmd
.addr
[:cba_shift
]
314 class gramNativeWritePort(gramNativePort
):
315 def __init__(self
, *args
, **kwargs
):
316 LiteDRAMNativePort
.__init
__(self
, "write", *args
, **kwargs
)
319 class gramNativeReadPort(gramNativePort
):
320 def __init__(self
, *args
, **kwargs
):
321 LiteDRAMNativePort
.__init
__(self
, "read", *args
, **kwargs
)
324 # Timing Controllers -------------------------------------------------------------------------------
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
333 def elaborate(self
, platform
):
336 if self
._txxd
is not None:
337 count
= Signal(range(max(self
._txxd
, 2)))
338 with m
.If(self
.valid
):
340 count
.eq(self
._txxd
-1),
341 self
.ready
.eq((self
._txxd
- 1) == 0),
344 m
.d
.sync
+= count
.eq(count
-1)
345 with m
.If(count
== 1):
346 m
.d
.sync
+= self
.ready
.eq(1)
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
357 def elaborate(self
, platform
):
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
)
369 m
.d
.sync
+= self
.ready
.eq(1)