import migen in litex/gen
[litex.git] / litex / soc / misoc / interconnect / stream.py
1 from migen import *
2 from migen.genlib.record import *
3 from migen.genlib import fifo
4
5
6 def _make_m2s(layout):
7 r = []
8 for f in layout:
9 if isinstance(f[1], (int, tuple)):
10 r.append((f[0], f[1], DIR_M_TO_S))
11 else:
12 r.append((f[0], _make_m2s(f[1])))
13 return r
14
15
16 class EndpointDescription:
17 def __init__(self, payload_layout, packetized=False):
18 self.payload_layout = payload_layout
19 self.packetized = packetized
20
21 def get_full_layout(self):
22 reserved = {"stb", "ack", "payload", "sop", "eop", "description"}
23 attributed = set()
24 for f in self.payload_layout:
25 if f[0] in attributed:
26 raise ValueError(f[0] + " already attributed in payload layout")
27 if f[0] in reserved:
28 raise ValueError(f[0] + " cannot be used in endpoint layout")
29 attributed.add(f[0])
30
31 full_layout = [
32 ("payload", _make_m2s(self.payload_layout)),
33 ("stb", 1, DIR_M_TO_S),
34 ("ack", 1, DIR_S_TO_M)
35 ]
36 if self.packetized:
37 full_layout += [
38 ("sop", 1, DIR_M_TO_S),
39 ("eop", 1, DIR_M_TO_S)
40 ]
41 return full_layout
42
43
44 class _Endpoint(Record):
45 def __init__(self, description_or_layout):
46 if isinstance(description_or_layout, EndpointDescription):
47 self.description = description_or_layout
48 else:
49 self.description = EndpointDescription(description_or_layout)
50 Record.__init__(self, self.description.get_full_layout())
51
52 def __getattr__(self, name):
53 return getattr(object.__getattribute__(self, "payload"), name)
54
55
56 class Source(_Endpoint):
57 def connect(self, sink):
58 return Record.connect(self, sink)
59
60
61 class Sink(_Endpoint):
62 def connect(self, source):
63 return source.connect(self)
64
65
66 class _FIFOWrapper(Module):
67 def __init__(self, fifo_class, layout, depth):
68 self.sink = Sink(layout)
69 self.source = Source(layout)
70 self.busy = Signal()
71
72 ###
73
74 description = self.sink.description
75 fifo_layout = [("payload", description.payload_layout)]
76 if description.packetized:
77 fifo_layout += [("sop", 1), ("eop", 1)]
78
79 self.submodules.fifo = fifo_class(layout_len(fifo_layout), depth)
80 fifo_in = Record(fifo_layout)
81 fifo_out = Record(fifo_layout)
82 self.comb += [
83 self.fifo.din.eq(fifo_in.raw_bits()),
84 fifo_out.raw_bits().eq(self.fifo.dout)
85 ]
86
87 self.comb += [
88 self.sink.ack.eq(self.fifo.writable),
89 self.fifo.we.eq(self.sink.stb),
90 fifo_in.payload.eq(self.sink.payload),
91
92 self.source.stb.eq(self.fifo.readable),
93 self.source.payload.eq(fifo_out.payload),
94 self.fifo.re.eq(self.source.ack)
95 ]
96 if description.packetized:
97 self.comb += [
98 fifo_in.sop.eq(self.sink.sop),
99 fifo_in.eop.eq(self.sink.eop),
100 self.source.sop.eq(fifo_out.sop),
101 self.source.eop.eq(fifo_out.eop)
102 ]
103
104
105 class SyncFIFO(_FIFOWrapper):
106 def __init__(self, layout, depth, buffered=False):
107 _FIFOWrapper.__init__(
108 self,
109 fifo.SyncFIFOBuffered if buffered else fifo.SyncFIFO,
110 layout, depth)
111
112
113 class AsyncFIFO(_FIFOWrapper):
114 def __init__(self, layout, depth):
115 _FIFOWrapper.__init__(self, fifo.AsyncFIFO, layout, depth)
116
117
118 class Multiplexer(Module):
119 def __init__(self, layout, n):
120 self.source = Source(layout)
121 sinks = []
122 for i in range(n):
123 sink = Sink(layout)
124 setattr(self, "sink"+str(i), sink)
125 sinks.append(sink)
126 self.sel = Signal(max=n)
127
128 # # #
129
130 cases = {}
131 for i, sink in enumerate(sinks):
132 cases[i] = Record.connect(sink, self.source)
133 self.comb += Case(self.sel, cases)
134
135
136 class Demultiplexer(Module):
137 def __init__(self, layout, n):
138 self.sink = Sink(layout)
139 sources = []
140 for i in range(n):
141 source = Source(layout)
142 setattr(self, "source"+str(i), source)
143 sources.append(source)
144 self.sel = Signal(max=n)
145
146 # # #
147
148 cases = {}
149 for i, source in enumerate(sources):
150 cases[i] = Record.connect(self.sink, source)
151 self.comb += Case(self.sel, cases)
152
153 # TODO: clean up code below
154 # XXX
155
156 from copy import copy
157 from migen.util.misc import xdir
158
159 def pack_layout(l, n):
160 return [("chunk"+str(i), l) for i in range(n)]
161
162 def get_endpoints(obj, filt=_Endpoint):
163 if hasattr(obj, "get_endpoints") and callable(obj.get_endpoints):
164 return obj.get_endpoints(filt)
165 r = dict()
166 for k, v in xdir(obj, True):
167 if isinstance(v, filt):
168 r[k] = v
169 return r
170
171 def get_single_ep(obj, filt):
172 eps = get_endpoints(obj, filt)
173 if len(eps) != 1:
174 raise ValueError("More than one endpoint")
175 return list(eps.items())[0]
176
177
178 class BinaryActor(Module):
179 def __init__(self, *args, **kwargs):
180 self.busy = Signal()
181 sink = get_single_ep(self, Sink)[1]
182 source = get_single_ep(self, Source)[1]
183 self.build_binary_control(sink, source, *args, **kwargs)
184
185 def build_binary_control(self, sink, source):
186 raise NotImplementedError("Binary actor classes must overload build_binary_control_fragment")
187
188
189 class CombinatorialActor(BinaryActor):
190 def build_binary_control(self, sink, source):
191 self.comb += [
192 source.stb.eq(sink.stb),
193 sink.ack.eq(source.ack),
194 self.busy.eq(0)
195 ]
196 if sink.description.packetized:
197 self.comb += [
198 source.sop.eq(sink.sop),
199 source.eop.eq(sink.eop)
200 ]
201
202
203 class Unpack(Module):
204 def __init__(self, n, layout_to, reverse=False):
205 self.source = source = Source(layout_to)
206 description_from = copy(source.description)
207 description_from.payload_layout = pack_layout(description_from.payload_layout, n)
208 self.sink = sink = Sink(description_from)
209
210 self.busy = Signal()
211
212 ###
213
214 mux = Signal(max=n)
215 first = Signal()
216 last = Signal()
217 self.comb += [
218 first.eq(mux == 0),
219 last.eq(mux == (n-1)),
220 source.stb.eq(sink.stb),
221 sink.ack.eq(last & source.ack)
222 ]
223 self.sync += [
224 If(source.stb & source.ack,
225 If(last,
226 mux.eq(0)
227 ).Else(
228 mux.eq(mux + 1)
229 )
230 )
231 ]
232 cases = {}
233 for i in range(n):
234 chunk = n-i-1 if reverse else i
235 cases[i] = [source.payload.raw_bits().eq(getattr(sink.payload, "chunk"+str(chunk)).raw_bits())]
236 self.comb += Case(mux, cases).makedefault()
237
238 if description_from.packetized:
239 self.comb += [
240 source.sop.eq(sink.sop & first),
241 source.eop.eq(sink.eop & last)
242 ]
243
244
245 class Pack(Module):
246 def __init__(self, layout_from, n, reverse=False):
247 self.sink = sink = Sink(layout_from)
248 description_to = copy(sink.description)
249 description_to.payload_layout = pack_layout(description_to.payload_layout, n)
250 self.source = source = Source(description_to)
251 self.busy = Signal()
252
253 ###
254
255 demux = Signal(max=n)
256
257 load_part = Signal()
258 strobe_all = Signal()
259 cases = {}
260 for i in range(n):
261 chunk = n-i-1 if reverse else i
262 cases[i] = [getattr(source.payload, "chunk"+str(chunk)).raw_bits().eq(sink.payload.raw_bits())]
263 self.comb += [
264 self.busy.eq(strobe_all),
265 sink.ack.eq(~strobe_all | source.ack),
266 source.stb.eq(strobe_all),
267 load_part.eq(sink.stb & sink.ack)
268 ]
269
270 if description_to.packetized:
271 demux_last = ((demux == (n - 1)) | sink.eop)
272 else:
273 demux_last = (demux == (n - 1))
274
275 self.sync += [
276 If(source.ack, strobe_all.eq(0)),
277 If(load_part,
278 Case(demux, cases),
279 If(demux_last,
280 demux.eq(0),
281 strobe_all.eq(1)
282 ).Else(
283 demux.eq(demux + 1)
284 )
285 )
286 ]
287
288 if description_to.packetized:
289 self.sync += [
290 If(source.stb & source.ack,
291 source.sop.eq(sink.sop),
292 source.eop.eq(sink.eop),
293 ).Elif(sink.stb & sink.ack,
294 source.sop.eq(sink.sop | source.sop),
295 source.eop.eq(sink.eop | source.eop)
296 )
297 ]
298
299
300 class Chunkerize(CombinatorialActor):
301 def __init__(self, layout_from, layout_to, n, reverse=False):
302 self.sink = Sink(layout_from)
303 if isinstance(layout_to, EndpointDescription):
304 layout_to = copy(layout_to)
305 layout_to.payload_layout = pack_layout(layout_to.payload_layout, n)
306 else:
307 layout_to = pack_layout(layout_to, n)
308 self.source = Source(layout_to)
309 CombinatorialActor.__init__(self)
310
311 ###
312
313 for i in range(n):
314 chunk = n-i-1 if reverse else i
315 for f in self.sink.description.payload_layout:
316 src = getattr(self.sink, f[0])
317 dst = getattr(getattr(self.source, "chunk"+str(chunk)), f[0])
318 self.comb += dst.eq(src[i*len(src)//n:(i+1)*len(src)//n])
319
320
321 class Unchunkerize(CombinatorialActor):
322 def __init__(self, layout_from, n, layout_to, reverse=False):
323 if isinstance(layout_from, EndpointDescription):
324 fields = layout_from.payload_layout
325 layout_from = copy(layout_from)
326 layout_from.payload_layout = pack_layout(layout_from.payload_layout, n)
327 else:
328 fields = layout_from
329 layout_from = pack_layout(layout_from, n)
330 self.sink = Sink(layout_from)
331 self.source = Source(layout_to)
332 CombinatorialActor.__init__(self)
333
334 ###
335
336 for i in range(n):
337 chunk = n-i-1 if reverse else i
338 for f in fields:
339 src = getattr(getattr(self.sink, "chunk"+str(chunk)), f[0])
340 dst = getattr(self.source, f[0])
341 self.comb += dst[i*len(dst)//n:(i+1)*len(dst)//n].eq(src)
342
343
344 class Converter(Module):
345 def __init__(self, layout_from, layout_to, reverse=False):
346 self.sink = Sink(layout_from)
347 self.source = Source(layout_to)
348 self.busy = Signal()
349
350 ###
351
352 width_from = len(self.sink.payload.raw_bits())
353 width_to = len(self.source.payload.raw_bits())
354
355 # downconverter
356 if width_from > width_to:
357 if width_from % width_to:
358 raise ValueError
359 ratio = width_from//width_to
360 self.submodules.chunkerize = Chunkerize(layout_from, layout_to, ratio, reverse)
361 self.submodules.unpack = Unpack(ratio, layout_to)
362
363 self.comb += [
364 Record.connect(self.sink, self.chunkerize.sink),
365 Record.connect(self.chunkerize.source, self.unpack.sink),
366 Record.connect(self.unpack.source, self.source),
367 self.busy.eq(self.unpack.busy)
368 ]
369 # upconverter
370 elif width_to > width_from:
371 if width_to % width_from:
372 raise ValueError
373 ratio = width_to//width_from
374 self.submodules.pack = Pack(layout_from, ratio)
375 self.submodules.unchunkerize = Unchunkerize(layout_from, ratio, layout_to, reverse)
376
377 self.comb += [
378 Record.connect(self.sink, self.pack.sink),
379 Record.connect(self.pack.source, self.unchunkerize.sink),
380 Record.connect(self.unchunkerize.source, self.source),
381 self.busy.eq(self.pack.busy)
382 ]
383 # direct connection
384 else:
385 self.comb += Record.connect(self.sink, self.source)
386
387 # XXX