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
.asserts
import Assert
, Assume
14 from nmigen
.hdl
.rec
import *
15 from nmigen
.utils
import log2_int
17 import gram
.stream
as stream
19 # Helpers ------------------------------------------------------------------------------------------
31 def get_cl_cw(memtype
, tck
):
32 f_to_cl_cwl
= OrderedDict()
34 f_to_cl_cwl
[400e6
] = (3, 2)
35 f_to_cl_cwl
[533e6
] = (4, 3)
36 f_to_cl_cwl
[677e6
] = (5, 4)
37 f_to_cl_cwl
[800e6
] = (6, 5)
38 f_to_cl_cwl
[1066e6
] = (7, 5)
39 elif memtype
== "DDR3":
40 f_to_cl_cwl
[800e6
] = (6, 5)
41 f_to_cl_cwl
[1066e6
] = (7, 6)
42 f_to_cl_cwl
[1333e6
] = (10, 7)
43 f_to_cl_cwl
[1600e6
] = (11, 8)
44 elif memtype
== "DDR4":
45 f_to_cl_cwl
[1600e6
] = (11, 9)
48 for f
, (cl
, cwl
) in f_to_cl_cwl
.items():
54 def get_sys_latency(nphases
, cas_latency
):
55 return math
.ceil(cas_latency
/nphases
)
58 def get_sys_phases(nphases
, sys_latency
, cas_latency
):
59 dat_phase
= sys_latency
*nphases
- cas_latency
60 cmd_phase
= (dat_phase
- 1) % nphases
61 return cmd_phase
, dat_phase
63 # BitSlip ---------------------------------------------------------------
65 class BitSlip(Elaboratable
):
66 """BitSlip: provides a delay-buffer by N clock cycles for data of width dw
67 * rst will reset the delay back to zero
68 * slp will increment the counter. it must be held for {cycles} cycles
69 for the input data to appear on the output buffer
71 def __init__(self
, dw
, rst
=None, slp
=None, cycles
=1):
74 self
.rst
= Signal() if rst
is None else rst
75 self
.slp
= Signal() if slp
is None else slp
79 def elaborate(self
, platform
):
81 comb
, sync
= m
.d
.comb
, m
.d
.sync
82 vcount
= self
.cycles
* self
.dw
83 value
= Signal(vcount
.bit_length())
87 with m
.Elif(self
.slp
):
88 sync
+= value
.eq(value
+1)
90 # Shift Register using input i.
91 r
= Signal((self
.cycles
+1)*self
.dw
, reset_less
=True)
92 sync
+= r
.eq(Cat(r
[self
.dw
:], self
.i
))
94 # note the slightly strange arrangement: whilst the shift register
95 # shuffles along by {dw} bits, if dw is not 1, the output can contain
96 # parts of data from previous clocks.
98 for i
in range(self
.cycles
*self
.dw
):
100 comb
+= self
.o
.eq(r
[i
:self
.dw
+i
])
103 # Settings -----------------------------------------------------------------------------------------
107 def set_attributes(self
, attributes
):
108 for k
, v
in attributes
.items():
112 class PhySettings(Settings
):
113 def __init__(self
, phytype
, memtype
, databits
, dfi_databits
,
116 rdcmdphase
, wrcmdphase
,
117 cl
, read_latency
, write_latency
, nranks
=1, cwl
=None):
118 self
.set_attributes(locals())
119 self
.cwl
= cl
if cwl
is None else cwl
120 self
.is_rdimm
= False
122 # Optional DDR3/DDR4 electrical settings:
123 # rtt_nom: Non-Writes on-die termination impedance
124 # rtt_wr: Writes on-die termination impedance
125 # ron: Output driver impedance
126 def add_electrical_settings(self
, rtt_nom
, rtt_wr
, ron
):
127 assert self
.memtype
in ["DDR3", "DDR4"]
128 self
.set_attributes(locals())
131 class GeomSettings(Settings
):
132 def __init__(self
, bankbits
, rowbits
, colbits
):
133 self
.set_attributes(locals())
134 self
.addressbits
= max(rowbits
, colbits
)
137 class TimingSettings(Settings
):
138 def __init__(self
, tRP
, tRCD
, tWR
, tWTR
, tREFI
, tRFC
, tFAW
, tCCD
, tRRD
, tRC
, tRAS
, tZQCS
):
139 self
.set_attributes(locals())
141 # Layouts/Interface --------------------------------------------------------------------------------
144 def cmd_layout(address_width
):
146 ("valid", 1, DIR_FANOUT
),
147 ("ready", 1, DIR_FANIN
),
148 ("we", 1, DIR_FANOUT
),
149 ("addr", address_width
, DIR_FANOUT
),
150 ("lock", 1, DIR_FANIN
), # only used internally
152 ("wdata_ready", 1, DIR_FANIN
),
153 ("rdata_valid", 1, DIR_FANIN
)
157 def data_layout(data_width
):
159 ("wdata", data_width
, DIR_FANOUT
),
160 ("wdata_we", data_width
//8, DIR_FANOUT
),
161 ("rdata", data_width
, DIR_FANIN
)
165 def cmd_description(address_width
):
168 ("addr", address_width
)
172 def wdata_description(data_width
):
174 ("data", data_width
),
175 ("we", data_width
//8)
179 def rdata_description(data_width
):
180 return [("data", data_width
)]
183 def cmd_request_layout(a
, ba
):
193 def cmd_request_rw_layout(a
, ba
):
194 return cmd_request_layout(a
, ba
) + [
201 class gramInterface(Record
):
202 def __init__(self
, address_align
, settings
):
203 rankbits
= log2_int(settings
.phy
.nranks
)
204 self
.address_align
= address_align
205 self
.address_width
= settings
.geom
.rowbits
+ \
206 settings
.geom
.colbits
+ rankbits
- address_align
207 self
.data_width
= settings
.phy
.dfi_databits
*settings
.phy
.nphases
208 self
.nbanks
= settings
.phy
.nranks
*(2**settings
.geom
.bankbits
)
209 self
.nranks
= settings
.phy
.nranks
210 self
.settings
= settings
212 layout
= [("bank"+str(i
), cmd_layout(self
.address_width
))
213 for i
in range(self
.nbanks
)]
214 layout
+= data_layout(self
.data_width
)
215 Record
.__init
__(self
, layout
)
217 # Ports --------------------------------------------------------------------------------------------
220 class gramNativePort(Settings
):
221 def __init__(self
, mode
, address_width
, data_width
, clock_domain
="sync", id=0):
222 self
.set_attributes(locals())
224 if mode
not in ["both", "read", "write"]:
225 raise ValueError("mode must be either both/read/write, not {!r}".format(mode
))
229 self
.cmd
= stream
.Endpoint(cmd_description(address_width
))
230 self
.wdata
= stream
.Endpoint(wdata_description(data_width
))
231 self
.rdata
= stream
.Endpoint(rdata_description(data_width
))
233 self
.flush
= Signal()
235 self
.data_width
= data_width
237 def get_bank_address(self
, bank_bits
, cba_shift
):
238 cba_upper
= cba_shift
+ bank_bits
239 return self
.cmd
.addr
[cba_shift
:cba_upper
]
241 def get_row_column_address(self
, bank_bits
, rca_bits
, cba_shift
):
242 cba_upper
= cba_shift
+ bank_bits
243 if cba_shift
< rca_bits
:
245 return Cat(self
.cmd
.addr
[:cba_shift
], self
.cmd
.addr
[cba_upper
:])
247 return self
.cmd
.addr
[cba_upper
:]
249 return self
.cmd
.addr
[:cba_shift
]
252 # Timing Controllers -------------------------------------------------------------------------------
254 class tXXDController(Elaboratable
):
255 def __init__(self
, txxd
):
256 self
.valid
= Signal()
257 self
.ready
= ready
= Signal(reset
=txxd
is None, attrs
={"no_retiming": True})
260 def elaborate(self
, platform
):
263 if self
._txxd
is not None:
264 count
= Signal(range(max(self
._txxd
, 2)))
265 with m
.If(self
.valid
):
267 count
.eq(self
._txxd
-1),
268 self
.ready
.eq((self
._txxd
- 1) == 0),
270 with m
.Elif(~self
.ready
):
271 m
.d
.sync
+= count
.eq(count
-1)
272 with m
.If(count
== 1):
273 m
.d
.sync
+= self
.ready
.eq(1)
275 if platform
== "formal":
276 if self
._txxd
is not None and self
._txxd
> 0:
277 hasSeenValid
= Signal()
278 with m
.If(self
.valid
):
279 m
.d
.sync
+= hasSeenValid
.eq(1)
281 m
.d
.sync
+= Assert((hasSeenValid
& (count
== 0)).implies(self
.ready
== 1))
286 class tFAWController(Elaboratable
):
287 def __init__(self
, tfaw
):
288 self
.valid
= Signal()
289 self
.ready
= Signal(reset
=1, attrs
={"no_retiming": True})
292 def elaborate(self
, platform
):
295 if self
._tfaw
is not None:
296 count
= Signal(range(max(self
._tfaw
, 2)))
297 window
= Signal(self
._tfaw
)
298 m
.d
.sync
+= window
.eq(Cat(self
.valid
, window
))
299 m
.d
.comb
+= count
.eq(reduce(add
, [window
[i
] for i
in range(self
._tfaw
)]))
300 with m
.If(count
< 4):
301 with m
.If(count
== 3):
302 m
.d
.sync
+= self
.ready
.eq(~self
.valid
)
304 m
.d
.sync
+= self
.ready
.eq(1)