initialise bitslip with a specific value rather than an incrementor
[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.asserts import Assert, Assume
14 from nmigen.hdl.rec import *
15 from nmigen.utils import log2_int
16
17 import gram.stream as stream
18
19 # Helpers ------------------------------------------------------------------------------------------
20
21 burst_lengths = {
22 "SDR": 1,
23 "DDR": 4,
24 "LPDDR": 4,
25 "DDR2": 4,
26 "DDR3": 8,
27 "DDR4": 8
28 }
29
30
31 def get_cl_cw(memtype, tck):
32 f_to_cl_cwl = OrderedDict()
33 if memtype == "DDR2":
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)
46 else:
47 raise ValueError
48 for f, (cl, cwl) in f_to_cl_cwl.items():
49 if tck >= 2/f:
50 return cl, cwl
51 raise ValueError
52
53
54 def get_sys_latency(nphases, cas_latency):
55 return math.ceil(cas_latency/nphases)
56
57
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
62
63 # BitSlip ---------------------------------------------------------------
64
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
70 """
71 def __init__(self, dw, rst=None, slp=None, cycles=1):
72 self.i = Signal(dw)
73 self.o = Signal(dw)
74 #self.rst = Signal() if rst is None else rst
75 self.slp = Signal() if slp is None else slp
76 self.dw = dw
77 self.cycles = cycles
78
79 def elaborate(self, platform):
80 m = Module()
81 comb, sync = m.d.comb, m.d.sync
82 vcount = self.cycles * self.dw
83 value = Signal(vcount.bit_length())
84
85 #with m.If(self.rst):
86 # sync += value.eq(0)
87 #with m.Elif(self.slp):
88 # sync += value.eq(value+1)
89 comb += value.eq(self.slp)
90
91 # Shift Register using input i.
92 r = Signal((self.cycles+1)*self.dw, reset_less=True)
93 sync += r.eq(Cat(r[self.dw:], self.i))
94
95 # note the slightly strange arrangement: whilst the shift register
96 # shuffles along by {dw} bits, if dw is not 1, the output can contain
97 # parts of data from previous clocks.
98 with m.Switch(value):
99 for i in range(self.cycles*self.dw):
100 with m.Case(i):
101 comb += self.o.eq(r[i:self.dw+i])
102 return m
103
104 # Settings -----------------------------------------------------------------------------------------
105
106
107 class Settings:
108 def set_attributes(self, attributes):
109 for k, v in attributes.items():
110 setattr(self, k, v)
111
112
113 class PhySettings(Settings):
114 def __init__(self, phytype, memtype, databits, dfi_databits,
115 nphases,
116 rdphase, wrphase,
117 rdcmdphase, wrcmdphase,
118 cl, read_latency, write_latency, nranks=1, cwl=None):
119 self.set_attributes(locals())
120 self.cwl = cl if cwl is None else cwl
121 self.is_rdimm = False
122
123 # Optional DDR3/DDR4 electrical settings:
124 # rtt_nom: Non-Writes on-die termination impedance
125 # rtt_wr: Writes on-die termination impedance
126 # ron: Output driver impedance
127 def add_electrical_settings(self, rtt_nom, rtt_wr, ron):
128 assert self.memtype in ["DDR3", "DDR4"]
129 self.set_attributes(locals())
130
131
132 class GeomSettings(Settings):
133 def __init__(self, bankbits, rowbits, colbits):
134 self.set_attributes(locals())
135 self.addressbits = max(rowbits, colbits)
136
137
138 class TimingSettings(Settings):
139 def __init__(self, tRP, tRCD, tWR, tWTR, tREFI, tRFC, tFAW, tCCD, tRRD, tRC, tRAS, tZQCS):
140 self.set_attributes(locals())
141
142 # Layouts/Interface --------------------------------------------------------------------------------
143
144
145 def cmd_layout(address_width):
146 return [
147 ("valid", 1, DIR_FANOUT),
148 ("ready", 1, DIR_FANIN),
149 ("we", 1, DIR_FANOUT),
150 ("addr", address_width, DIR_FANOUT),
151 ("lock", 1, DIR_FANIN), # only used internally
152
153 ("wdata_ready", 1, DIR_FANIN),
154 ("rdata_valid", 1, DIR_FANIN)
155 ]
156
157
158 def data_layout(data_width):
159 return [
160 ("wdata", data_width, DIR_FANOUT),
161 ("wdata_we", data_width//8, DIR_FANOUT),
162 ("rdata", data_width, DIR_FANIN)
163 ]
164
165
166 def cmd_description(address_width):
167 return [
168 ("we", 1),
169 ("addr", address_width)
170 ]
171
172
173 def wdata_description(data_width):
174 return [
175 ("data", data_width),
176 ("we", data_width//8)
177 ]
178
179
180 def rdata_description(data_width):
181 return [("data", data_width)]
182
183
184 def cmd_request_layout(a, ba):
185 return [
186 ("a", a),
187 ("ba", ba),
188 ("cas", 1),
189 ("ras", 1),
190 ("we", 1)
191 ]
192
193
194 def cmd_request_rw_layout(a, ba):
195 return cmd_request_layout(a, ba) + [
196 ("is_cmd", 1),
197 ("is_read", 1),
198 ("is_write", 1)
199 ]
200
201
202 class gramInterface(Record):
203 def __init__(self, address_align, settings):
204 rankbits = log2_int(settings.phy.nranks)
205 self.address_align = address_align
206 self.address_width = settings.geom.rowbits + \
207 settings.geom.colbits + rankbits - address_align
208 self.data_width = settings.phy.dfi_databits*settings.phy.nphases
209 self.nbanks = settings.phy.nranks*(2**settings.geom.bankbits)
210 self.nranks = settings.phy.nranks
211 self.settings = settings
212
213 layout = [("bank"+str(i), cmd_layout(self.address_width))
214 for i in range(self.nbanks)]
215 layout += data_layout(self.data_width)
216 Record.__init__(self, layout)
217
218 # Ports --------------------------------------------------------------------------------------------
219
220
221 class gramNativePort(Settings):
222 def __init__(self, mode, address_width, data_width, clock_domain="sync", id=0):
223 self.set_attributes(locals())
224
225 if mode not in ["both", "read", "write"]:
226 raise ValueError("mode must be either both/read/write, not {!r}".format(mode))
227
228 self.lock = Signal()
229
230 self.cmd = stream.Endpoint(cmd_description(address_width))
231 self.wdata = stream.Endpoint(wdata_description(data_width))
232 self.rdata = stream.Endpoint(rdata_description(data_width))
233
234 self.flush = Signal()
235
236 self.data_width = data_width
237
238 def get_bank_address(self, bank_bits, cba_shift):
239 cba_upper = cba_shift + bank_bits
240 return self.cmd.addr[cba_shift:cba_upper]
241
242 def get_row_column_address(self, bank_bits, rca_bits, cba_shift):
243 cba_upper = cba_shift + bank_bits
244 if cba_shift < rca_bits:
245 if cba_shift:
246 return Cat(self.cmd.addr[:cba_shift], self.cmd.addr[cba_upper:])
247 else:
248 return self.cmd.addr[cba_upper:]
249 else:
250 return self.cmd.addr[:cba_shift]
251
252
253 # Timing Controllers -------------------------------------------------------------------------------
254
255 class tXXDController(Elaboratable):
256 def __init__(self, txxd):
257 self.valid = Signal()
258 self.ready = ready = Signal(reset=txxd is None, attrs={"no_retiming": True})
259 self._txxd = txxd
260
261 def elaborate(self, platform):
262 m = Module()
263
264 if self._txxd is not None:
265 count = Signal(range(max(self._txxd, 2)))
266 with m.If(self.valid):
267 m.d.sync += [
268 count.eq(self._txxd-1),
269 self.ready.eq((self._txxd - 1) == 0),
270 ]
271 with m.Elif(~self.ready):
272 m.d.sync += count.eq(count-1)
273 with m.If(count == 1):
274 m.d.sync += self.ready.eq(1)
275
276 if platform == "formal":
277 if self._txxd is not None and self._txxd > 0:
278 hasSeenValid = Signal()
279 with m.If(self.valid):
280 m.d.sync += hasSeenValid.eq(1)
281
282 m.d.sync += Assert((hasSeenValid & (count == 0)).implies(self.ready == 1))
283
284 return m
285
286
287 class tFAWController(Elaboratable):
288 def __init__(self, tfaw):
289 self.valid = Signal()
290 self.ready = Signal(reset=1, attrs={"no_retiming": True})
291 self._tfaw = tfaw
292
293 def elaborate(self, platform):
294 m = Module()
295
296 if self._tfaw is not None:
297 count = Signal(range(max(self._tfaw, 2)))
298 window = Signal(self._tfaw)
299 m.d.sync += window.eq(Cat(self.valid, window))
300 m.d.comb += count.eq(reduce(add, [window[i] for i in range(self._tfaw)]))
301 with m.If(count < 4):
302 with m.If(count == 3):
303 m.d.sync += self.ready.eq(~self.valid)
304 with m.Else():
305 m.d.sync += self.ready.eq(1)
306
307 return m