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 import gram
.stream
as stream
14 # Helpers ------------------------------------------------------------------------------------------
25 def get_cl_cw(memtype
, tck
):
26 f_to_cl_cwl
= OrderedDict()
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)
42 for f
, (cl
, cwl
) in f_to_cl_cwl
.items():
47 def get_sys_latency(nphases
, cas_latency
):
48 return math
.ceil(cas_latency
/nphases
)
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
55 # PHY Pads Transformers ----------------------------------------------------------------------------
60 Reduce DRAM pads to only use specific modules.
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.
65 def __init__(self
, pads
, modules
):
67 self
.modules
= modules
69 def __getattr__(self
, name
):
71 return Array([getattr(self
.pads
, name
)[8*i
+ j
]
74 if name
in ["dm", "dqs", "dqs_p", "dqs_n"]:
75 return Array([getattr(self
.pads
, name
)[i
] for i
in self
.modules
])
77 return getattr(self
.pads
, name
)
79 class PHYPadsCombiner
:
82 Combine DRAM pads from fully dissociated chips in a unique DRAM pads structure.
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.
89 def __init__(self
, pads
):
90 if not isinstance(pads
, list):
96 def sel_group(self
, n
):
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
))])
105 return getattr(self
.groups
[self
.sel
], name
)
107 # BitSlip ------------------------------------------------------------------------------------------
109 class BitSlip(Elaboratable
):
110 def __init__(self
, dw
, rst
=None, slp
=None, cycles
=1):
113 self
.rst
= Signal() if rst
is None else rst
114 self
.slp
= Signal() if slp
is None else slp
115 self
._cycles
= cycles
117 def elaborate(self
, platform
):
120 value
= Signal(max=self
._cycles
*dw
)
122 m
.d
.sync
+= value
.eq(value
+1)
123 with m
.Elif(self
.rst
):
124 m
.d
.sync
+= value
.eq(0)
126 r
= Signal((self
._cycles
+1)*dw
, reset_less
=True)
127 m
.d
.sync
+= r
.eq(Cat(r
[dw
:], self
.i
))
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
)
135 # DQS Pattern --------------------------------------------------------------------------------------
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
142 self
._wlevel
_en
= wlevel_en
143 self
._wlevel
_strobe
= wlevel_strobe
144 self
._register
= register
146 def elaborate(self
, platform
):
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)
157 m
.d
.comb
+= self
.o
.eq(0b00000000)
159 m
.d
.comb
+= self
.o
.eq(0b01010101)
162 o
= Signal
.like(self
.o
)
163 m
.d
.sync
+= o
.eq(self
.o
)
168 # Settings -----------------------------------------------------------------------------------------
171 def set_attributes(self
, attributes
):
172 for k
, v
in attributes
.items():
176 class PhySettings(Settings
):
177 def __init__(self
, phytype
, memtype
, databits
, dfi_databits
,
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
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())
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"
198 self
.set_attributes(locals())
200 class GeomSettings(Settings
):
201 def __init__(self
, bankbits
, rowbits
, colbits
):
202 self
.set_attributes(locals())
203 self
.addressbits
= max(rowbits
, colbits
)
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())
210 # Layouts/Interface --------------------------------------------------------------------------------
212 def cmd_layout(address_width
):
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
220 ("wdata_ready", 1, DIR_FANIN
),
221 ("rdata_valid", 1, DIR_FANIN
)
224 def data_layout(data_width
):
226 ("wdata", data_width
, DIR_FANOUT
),
227 ("wdata_we", data_width
//8, DIR_FANOUT
),
228 ("rdata", data_width
, DIR_FANIN
)
231 def cmd_description(address_width
):
234 ("addr", address_width
)
237 def wdata_description(data_width
):
239 ("data", data_width
),
240 ("we", data_width
//8)
243 def rdata_description(data_width
):
244 return [("data", data_width
)]
246 def cmd_request_layout(a
, ba
):
255 def cmd_request_rw_layout(a
, ba
):
256 return cmd_request_layout(a
, ba
) + [
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
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
)
277 # Ports --------------------------------------------------------------------------------------------
279 class LiteDRAMNativePort(Settings
):
280 def __init__(self
, mode
, address_width
, data_width
, clock_domain
="sys", id=0):
281 self
.set_attributes(locals())
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
))
289 self
.flush
= Signal()
291 # retro-compatibility # FIXME: remove
292 self
.aw
= self
.address_width
293 self
.dw
= self
.data_width
294 self
.cd
= self
.clock_domain
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
]
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
:
304 return Cat(self
.cmd
.addr
[:cba_shift
], self
.cmd
.addr
[cba_upper
:])
306 return self
.cmd
.addr
[cba_upper
:]
308 return self
.cmd
.addr
[:cba_shift
]
311 class LiteDRAMNativeWritePort(LiteDRAMNativePort
):
312 def __init__(self
, *args
, **kwargs
):
313 LiteDRAMNativePort
.__init
__(self
, "write", *args
, **kwargs
)
316 class LiteDRAMNativeReadPort(LiteDRAMNativePort
):
317 def __init__(self
, *args
, **kwargs
):
318 LiteDRAMNativePort
.__init
__(self
, "read", *args
, **kwargs
)
321 # Timing Controllers -------------------------------------------------------------------------------
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
329 def elaborate(self
, platform
):
333 count
= Signal(range(max(txxd
, 2)))
334 with m
.If(self
.valid
):
337 self
.ready
.eq((txxd
- 1) == 0),
340 m
.d
.sync
+= count
.eq(count
-1)
341 with m
.If(count
== 1):
342 m
.d
.sync
+= self
.ready
.eq(1)
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
352 def elaborate(self
, platform
):
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
)
364 m
.d
.sync
+= self
.ready
.eq(1)