add a BitSlip module
[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
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))
93
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.
97 with m.Switch(value):
98 for i in range(self.cycles*self.dw):
99 with m.Case(i):
100 comb += self.o.eq(r[i:self.dw+i])
101 return m
102
103 # Settings -----------------------------------------------------------------------------------------
104
105
106 class Settings:
107 def set_attributes(self, attributes):
108 for k, v in attributes.items():
109 setattr(self, k, v)
110
111
112 class PhySettings(Settings):
113 def __init__(self, phytype, memtype, databits, dfi_databits,
114 nphases,
115 rdphase, wrphase,
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
121
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())
129
130
131 class GeomSettings(Settings):
132 def __init__(self, bankbits, rowbits, colbits):
133 self.set_attributes(locals())
134 self.addressbits = max(rowbits, colbits)
135
136
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())
140
141 # Layouts/Interface --------------------------------------------------------------------------------
142
143
144 def cmd_layout(address_width):
145 return [
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
151
152 ("wdata_ready", 1, DIR_FANIN),
153 ("rdata_valid", 1, DIR_FANIN)
154 ]
155
156
157 def data_layout(data_width):
158 return [
159 ("wdata", data_width, DIR_FANOUT),
160 ("wdata_we", data_width//8, DIR_FANOUT),
161 ("rdata", data_width, DIR_FANIN)
162 ]
163
164
165 def cmd_description(address_width):
166 return [
167 ("we", 1),
168 ("addr", address_width)
169 ]
170
171
172 def wdata_description(data_width):
173 return [
174 ("data", data_width),
175 ("we", data_width//8)
176 ]
177
178
179 def rdata_description(data_width):
180 return [("data", data_width)]
181
182
183 def cmd_request_layout(a, ba):
184 return [
185 ("a", a),
186 ("ba", ba),
187 ("cas", 1),
188 ("ras", 1),
189 ("we", 1)
190 ]
191
192
193 def cmd_request_rw_layout(a, ba):
194 return cmd_request_layout(a, ba) + [
195 ("is_cmd", 1),
196 ("is_read", 1),
197 ("is_write", 1)
198 ]
199
200
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
211
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)
216
217 # Ports --------------------------------------------------------------------------------------------
218
219
220 class gramNativePort(Settings):
221 def __init__(self, mode, address_width, data_width, clock_domain="sync", id=0):
222 self.set_attributes(locals())
223
224 if mode not in ["both", "read", "write"]:
225 raise ValueError("mode must be either both/read/write, not {!r}".format(mode))
226
227 self.lock = Signal()
228
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))
232
233 self.flush = Signal()
234
235 self.data_width = data_width
236
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]
240
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:
244 if cba_shift:
245 return Cat(self.cmd.addr[:cba_shift], self.cmd.addr[cba_upper:])
246 else:
247 return self.cmd.addr[cba_upper:]
248 else:
249 return self.cmd.addr[:cba_shift]
250
251
252 # Timing Controllers -------------------------------------------------------------------------------
253
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})
258 self._txxd = txxd
259
260 def elaborate(self, platform):
261 m = Module()
262
263 if self._txxd is not None:
264 count = Signal(range(max(self._txxd, 2)))
265 with m.If(self.valid):
266 m.d.sync += [
267 count.eq(self._txxd-1),
268 self.ready.eq((self._txxd - 1) == 0),
269 ]
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)
274
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)
280
281 m.d.sync += Assert((hasSeenValid & (count == 0)).implies(self.ready == 1))
282
283 return m
284
285
286 class tFAWController(Elaboratable):
287 def __init__(self, tfaw):
288 self.valid = Signal()
289 self.ready = Signal(reset=1, attrs={"no_retiming": True})
290 self._tfaw = tfaw
291
292 def elaborate(self, platform):
293 m = Module()
294
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)
303 with m.Else():
304 m.d.sync += self.ready.eq(1)
305
306 return m