1 # This file is Copyright (c) 2015 Sebastien Bourdeauducq <sb@m-labs.hk>
2 # This file is Copyright (c) 2015-2020 Florent Kermarrec <florent@enjoy-digital.fr>
3 # This file is Copyright (c) 2018 Tim 'mithro' Ansell <me@mith.ro>
9 from migen
.util
.misc
import xdir
10 from migen
.genlib
import fifo
11 from migen
.genlib
.cdc
import MultiReg
, PulseSynchronizer
13 from litex
.soc
.interconnect
.csr
import *
15 # Endpoint -----------------------------------------------------------------------------------------
17 (DIR_SINK
, DIR_SOURCE
) = range(2)
19 def _make_m2s(layout
):
22 if isinstance(f
[1], (int, tuple)):
23 r
.append((f
[0], f
[1], DIR_M_TO_S
))
25 r
.append((f
[0], _make_m2s(f
[1])))
28 def set_reset_less(field
):
29 if isinstance(field
, Signal
):
30 field
.reset_less
= True
31 elif isinstance(field
, Record
):
32 for s
, _
in field
.iter_flat():
35 class EndpointDescription
:
36 def __init__(self
, payload_layout
, param_layout
=[]):
37 self
.payload_layout
= payload_layout
38 self
.param_layout
= param_layout
40 def get_full_layout(self
):
41 reserved
= {"valid", "ready", "payload", "param", "first", "last", "description"}
43 for f
in self
.payload_layout
+ self
.param_layout
:
44 if f
[0] in attributed
:
45 raise ValueError(f
[0] + " already attributed in payload or param layout")
47 raise ValueError(f
[0] + " cannot be used in endpoint layout")
51 ("valid", 1, DIR_M_TO_S
),
52 ("ready", 1, DIR_S_TO_M
),
53 ("first", 1, DIR_M_TO_S
),
54 ("last", 1, DIR_M_TO_S
),
55 ("payload", _make_m2s(self
.payload_layout
)),
56 ("param", _make_m2s(self
.param_layout
))
61 class Endpoint(Record
):
62 def __init__(self
, description_or_layout
, name
=None, **kwargs
):
63 if isinstance(description_or_layout
, EndpointDescription
):
64 self
.description
= description_or_layout
66 self
.description
= EndpointDescription(description_or_layout
)
67 Record
.__init
__(self
, self
.description
.get_full_layout(), name
, **kwargs
)
68 set_reset_less(self
.first
)
69 set_reset_less(self
.last
)
70 set_reset_less(self
.payload
)
71 set_reset_less(self
.param
)
73 def __getattr__(self
, name
):
75 return getattr(object.__getattribute
__(self
, "payload"), name
)
77 return getattr(object.__getattribute
__(self
, "param"), name
)
79 # Actor --------------------------------------------------------------------------------------------
81 def _rawbits_layout(l
):
82 if isinstance(l
, int):
83 return [("rawbits", l
)]
87 def pack_layout(l
, n
):
88 return [("chunk"+str(i
), l
) for i
in range(n
)]
90 def get_endpoints(obj
, filt
=Endpoint
):
91 if hasattr(obj
, "get_endpoints") and callable(obj
.get_endpoints
):
92 return obj
.get_endpoints(filt
)
94 for k
, v
in xdir(obj
, True):
95 if isinstance(v
, filt
):
99 def get_single_ep(obj
, filt
):
100 eps
= get_endpoints(obj
, filt
)
102 raise ValueError("More than one endpoint")
103 return list(eps
.items())[0]
106 class BinaryActor(Module
):
107 def __init__(self
, *args
, **kwargs
):
108 self
.build_binary_control(self
.sink
, self
.source
, *args
, **kwargs
)
110 def build_binary_control(self
, sink
, source
):
111 raise NotImplementedError("Binary actor classes must overload build_binary_control_fragment")
114 class CombinatorialActor(BinaryActor
):
115 def build_binary_control(self
, sink
, source
):
117 source
.valid
.eq(sink
.valid
),
118 source
.first
.eq(sink
.first
),
119 source
.last
.eq(sink
.last
),
120 sink
.ready
.eq(source
.ready
),
124 class PipelinedActor(BinaryActor
):
125 def __init__(self
, latency
):
126 self
.latency
= latency
127 self
.pipe_ce
= Signal()
129 BinaryActor
.__init
__(self
, latency
)
131 def build_binary_control(self
, sink
, source
, latency
):
134 for i
in range(latency
):
136 self
.sync
+= If(self
.pipe_ce
, valid_n
.eq(valid
))
141 self
.pipe_ce
.eq(source
.ready | ~valid
),
142 sink
.ready
.eq(self
.pipe_ce
),
143 source
.valid
.eq(valid
),
146 first
= sink
.valid
& sink
.first
147 last
= sink
.valid
& sink
.last
148 for i
in range(latency
):
149 first_n
= Signal(reset_less
=True)
150 last_n
= Signal(reset_less
=True)
159 source
.first
.eq(first
),
163 # FIFO ---------------------------------------------------------------------------------------------
165 class _FIFOWrapper(Module
):
166 def __init__(self
, fifo_class
, layout
, depth
):
167 self
.sink
= sink
= Endpoint(layout
)
168 self
.source
= source
= Endpoint(layout
)
172 description
= sink
.description
174 ("payload", description
.payload_layout
),
175 ("param", description
.param_layout
),
180 self
.submodules
.fifo
= fifo
= fifo_class(layout_len(fifo_layout
), depth
)
181 fifo_in
= Record(fifo_layout
)
182 fifo_out
= Record(fifo_layout
)
184 fifo
.din
.eq(fifo_in
.raw_bits()),
185 fifo_out
.raw_bits().eq(fifo
.dout
)
189 sink
.ready
.eq(fifo
.writable
),
190 fifo
.we
.eq(sink
.valid
),
191 fifo_in
.first
.eq(sink
.first
),
192 fifo_in
.last
.eq(sink
.last
),
193 fifo_in
.payload
.eq(sink
.payload
),
194 fifo_in
.param
.eq(sink
.param
),
196 source
.valid
.eq(fifo
.readable
),
197 source
.first
.eq(fifo_out
.first
),
198 source
.last
.eq(fifo_out
.last
),
199 source
.payload
.eq(fifo_out
.payload
),
200 source
.param
.eq(fifo_out
.param
),
201 fifo
.re
.eq(source
.ready
)
205 class SyncFIFO(_FIFOWrapper
):
206 def __init__(self
, layout
, depth
, buffered
=False):
209 _FIFOWrapper
.__init
__(self
,
210 fifo_class
= fifo
.SyncFIFOBuffered
if buffered
else fifo
.SyncFIFO
,
213 self
.depth
= self
.fifo
.depth
214 self
.level
= self
.fifo
.level
217 self
.submodules
+= buf
219 self
.source
= buf
.source
221 self
.level
= Signal()
223 self
.sink
= Endpoint(layout
)
224 self
.source
= Endpoint(layout
)
225 self
.comb
+= self
.sink
.connect(self
.source
)
227 self
.level
= Signal()
230 class AsyncFIFO(_FIFOWrapper
):
231 def __init__(self
, layout
, depth
, buffered
=False):
233 _FIFOWrapper
.__init
__(self
,
234 fifo_class
= fifo
.AsyncFIFOBuffered
if buffered
else fifo
.AsyncFIFO
,
238 # Mux/Demux ----------------------------------------------------------------------------------------
240 class Multiplexer(Module
):
241 def __init__(self
, layout
, n
):
242 self
.source
= Endpoint(layout
)
245 sink
= Endpoint(layout
)
246 setattr(self
, "sink"+str(i
), sink
)
248 self
.sel
= Signal(max=n
)
253 for i
, sink
in enumerate(sinks
):
254 cases
[i
] = sink
.connect(self
.source
)
255 self
.comb
+= Case(self
.sel
, cases
)
258 class Demultiplexer(Module
):
259 def __init__(self
, layout
, n
):
260 self
.sink
= Endpoint(layout
)
263 source
= Endpoint(layout
)
264 setattr(self
, "source"+str(i
), source
)
265 sources
.append(source
)
266 self
.sel
= Signal(max=n
)
271 for i
, source
in enumerate(sources
):
272 cases
[i
] = self
.sink
.connect(source
)
273 self
.comb
+= Case(self
.sel
, cases
)
275 # Converter ----------------------------------------------------------------------------------------
277 class _UpConverter(Module
):
278 def __init__(self
, nbits_from
, nbits_to
, ratio
, reverse
):
279 self
.sink
= sink
= Endpoint([("data", nbits_from
)])
280 self
.source
= source
= Endpoint([("data", nbits_to
), ("valid_token_count", bits_for(ratio
))])
286 demux
= Signal(max=ratio
)
288 strobe_all
= Signal()
290 sink
.ready
.eq(~strobe_all | source
.ready
),
291 source
.valid
.eq(strobe_all
),
292 load_part
.eq(sink
.valid
& sink
.ready
)
295 demux_last
= ((demux
== (ratio
- 1)) | sink
.last
)
298 If(source
.ready
, strobe_all
.eq(0)),
307 If(source
.valid
& source
.ready
,
308 If(sink
.valid
& sink
.ready
,
309 source
.first
.eq(sink
.first
),
310 source
.last
.eq(sink
.last
)
315 ).Elif(sink
.valid
& sink
.ready
,
316 source
.first
.eq(sink
.first | source
.first
),
317 source
.last
.eq(sink
.last | source
.last
)
323 for i
in range(ratio
):
324 n
= ratio
-i
-1 if reverse
else i
325 cases
[i
] = source
.data
[n
*nbits_from
:(n
+1)*nbits_from
].eq(sink
.data
)
326 self
.sync
+= If(load_part
, Case(demux
, cases
))
329 self
.sync
+= If(load_part
, source
.valid_token_count
.eq(demux
+ 1))
332 class _DownConverter(Module
):
333 def __init__(self
, nbits_from
, nbits_to
, ratio
, reverse
):
334 self
.sink
= sink
= Endpoint([("data", nbits_from
)])
335 self
.source
= source
= Endpoint([("data", nbits_to
), ("valid_token_count", 1)])
341 mux
= Signal(max=ratio
)
346 last
.eq(mux
== (ratio
-1)),
347 source
.valid
.eq(sink
.valid
),
348 source
.first
.eq(sink
.first
& first
),
349 source
.last
.eq(sink
.last
& last
),
350 sink
.ready
.eq(last
& source
.ready
)
353 If(source
.valid
& source
.ready
,
363 for i
in range(ratio
):
364 n
= ratio
-i
-1 if reverse
else i
365 cases
[i
] = source
.data
.eq(sink
.data
[n
*nbits_to
:(n
+1)*nbits_to
])
366 self
.comb
+= Case(mux
, cases
).makedefault()
369 self
.comb
+= source
.valid_token_count
.eq(last
)
372 class _IdentityConverter(Module
):
373 def __init__(self
, nbits_from
, nbits_to
, ratio
, reverse
):
374 self
.sink
= sink
= Endpoint([("data", nbits_from
)])
375 self
.source
= source
= Endpoint([("data", nbits_to
), ("valid_token_count", 1)])
381 sink
.connect(source
),
382 source
.valid_token_count
.eq(1)
386 def _get_converter_ratio(nbits_from
, nbits_to
):
387 if nbits_from
> nbits_to
:
388 converter_cls
= _DownConverter
389 if nbits_from
% nbits_to
:
390 raise ValueError("Ratio must be an int")
391 ratio
= nbits_from
//nbits_to
392 elif nbits_from
< nbits_to
:
393 converter_cls
= _UpConverter
394 if nbits_to
% nbits_from
:
395 raise ValueError("Ratio must be an int")
396 ratio
= nbits_to
//nbits_from
398 converter_cls
= _IdentityConverter
400 return converter_cls
, ratio
403 class Converter(Module
):
404 def __init__(self
, nbits_from
, nbits_to
,
406 report_valid_token_count
= False):
407 self
.cls
, self
.ratio
= _get_converter_ratio(nbits_from
, nbits_to
)
411 converter
= self
.cls(nbits_from
, nbits_to
, self
.ratio
, reverse
)
412 self
.submodules
+= converter
413 self
.latency
= converter
.latency
415 self
.sink
= converter
.sink
416 if report_valid_token_count
:
417 self
.source
= converter
.source
419 self
.source
= Endpoint([("data", nbits_to
)])
420 self
.comb
+= converter
.source
.connect(self
.source
, omit
=set(["valid_token_count"]))
423 class StrideConverter(Module
):
424 def __init__(self
, description_from
, description_to
, reverse
=False):
425 self
.sink
= sink
= Endpoint(description_from
)
426 self
.source
= source
= Endpoint(description_to
)
430 nbits_from
= len(sink
.payload
.raw_bits())
431 nbits_to
= len(source
.payload
.raw_bits())
433 converter
= Converter(nbits_from
, nbits_to
, reverse
)
434 self
.submodules
+= converter
436 # Cast sink to converter.sink (user fields --> raw bits)
438 converter
.sink
.valid
.eq(sink
.valid
),
439 converter
.sink
.first
.eq(sink
.first
),
440 converter
.sink
.last
.eq(sink
.last
),
441 sink
.ready
.eq(converter
.sink
.ready
)
443 if converter
.cls
== _DownConverter
:
444 ratio
= converter
.ratio
445 for i
in range(ratio
):
447 for name
, width
in source
.description
.payload_layout
:
448 src
= getattr(sink
, name
)[i
*width
:(i
+1)*width
]
449 dst
= converter
.sink
.data
[i
*nbits_to
+j
:i
*nbits_to
+j
+width
]
450 self
.comb
+= dst
.eq(src
)
453 self
.comb
+= converter
.sink
.data
.eq(sink
.payload
.raw_bits())
456 # Cast converter.source to source (raw bits --> user fields)
458 source
.valid
.eq(converter
.source
.valid
),
459 source
.first
.eq(converter
.source
.first
),
460 source
.last
.eq(converter
.source
.last
),
461 converter
.source
.ready
.eq(source
.ready
)
463 if converter
.cls
== _UpConverter
:
464 ratio
= converter
.ratio
465 for i
in range(ratio
):
467 for name
, width
in sink
.description
.payload_layout
:
468 src
= converter
.source
.data
[i
*nbits_from
+j
:i
*nbits_from
+j
+width
]
469 dst
= getattr(source
, name
)[i
*width
:(i
+1)*width
]
470 self
.comb
+= dst
.eq(src
)
473 self
.comb
+= source
.payload
.raw_bits().eq(converter
.source
.data
)
476 if converter
.latency
== 0:
477 self
.comb
+= source
.param
.eq(sink
.param
)
478 elif converter
.latency
== 1:
479 self
.sync
+= source
.param
.eq(sink
.param
)
483 # Gearbox ------------------------------------------------------------------------------------------
486 return (a
*b
)//math
.gcd(a
, b
)
490 return [s
.eq(s
+ 1), If(s
== (m
-1), s
.eq(0))]
493 class Gearbox(Module
):
494 def __init__(self
, i_dw
, o_dw
, msb_first
=True):
495 self
.sink
= sink
= Endpoint([("data", i_dw
)])
496 self
.source
= source
= Endpoint([("data", o_dw
)])
500 io_lcm
= lcm(i_dw
, o_dw
)
504 level
= Signal(max=io_lcm
)
506 i_count
= Signal(max=io_lcm
//i_dw
)
508 o_count
= Signal(max=io_lcm
//o_dw
)
511 sink
.ready
.eq(level
< (io_lcm
- i_dw
)),
512 source
.valid
.eq(level
>= o_dw
),
515 i_inc
.eq(sink
.valid
& sink
.ready
),
516 o_inc
.eq(source
.valid
& source
.ready
)
519 If(i_inc
, *inc_mod(i_count
, io_lcm
//i_dw
)),
520 If(o_inc
, *inc_mod(o_count
, io_lcm
//o_dw
)),
521 If(i_inc
& ~o_inc
, level
.eq(level
+ i_dw
)),
522 If(~i_inc
& o_inc
, level
.eq(level
- o_dw
)),
523 If(i_inc
& o_inc
, level
.eq(level
+ i_dw
- o_dw
)),
528 shift_register
= Signal(io_lcm
, reset_less
=True)
531 i_data
= Signal(i_dw
)
533 self
.comb
+= i_data
.eq(sink
.data
)
535 self
.comb
+= i_data
.eq(sink
.data
[::-1])
536 for i
in range(io_lcm
//i_dw
):
537 i_cases
[i
] = shift_register
[io_lcm
- i_dw
*(i
+1):io_lcm
- i_dw
*i
].eq(i_data
)
538 self
.sync
+= If(sink
.valid
& sink
.ready
, Case(i_count
, i_cases
))
541 o_data
= Signal(o_dw
)
542 for i
in range(io_lcm
//o_dw
):
543 o_cases
[i
] = o_data
.eq(shift_register
[io_lcm
- o_dw
*(i
+1):io_lcm
- o_dw
*i
])
544 self
.comb
+= Case(o_count
, o_cases
)
546 self
.comb
+= source
.data
.eq(o_data
)
548 self
.comb
+= source
.data
.eq(o_data
[::-1])
550 # Monitor ------------------------------------------------------------------------------------------
552 class Monitor(Module
, AutoCSR
):
553 def __init__(self
, endpoint
, count_width
=32, clock_domain
="sys",
555 with_overflows
= False,
556 with_underflows
= False):
561 self
.tokens
= CSRStatus(count_width
)
563 self
.overflows
= CSRStatus(count_width
)
565 self
.underflows
= CSRStatus(count_width
)
571 if clock_domain
== "sys":
572 self
.comb
+= reset
.eq(self
.reset
.re
)
573 self
.comb
+= latch
.eq(self
.latch
.re
)
575 reset_ps
= PulseSynchronizer("sys", clock_domain
)
576 latch_ps
= PulseSynchronizer("sys", clock_domain
)
577 self
.submodules
+= reset_ps
, latch_ps
578 self
.comb
+= reset_ps
.i
.eq(self
.reset
.re
)
579 self
.comb
+= reset
.eq(reset_ps
.o
)
580 self
.comb
+= latch_ps
.i
.eq(self
.latch
.re
)
581 self
.comb
+= latch
.eq(latch_ps
.o
)
583 # Generic Monitor Counter ------------------------------------------------------------------
584 class MonitorCounter(Module
):
585 def __init__(self
, reset
, latch
, enable
, count
):
586 _count
= Signal
.like(count
)
587 _count_latched
= Signal
.like(count
)
588 _sync
= getattr(self
.sync
, clock_domain
)
592 _count_latched
.eq(0),
594 If(_count
!= (2**len(count
)-1),
595 _count
.eq(_count
+ 1)
599 _count_latched
.eq(_count
)
602 self
.specials
+= MultiReg(_count_latched
, count
)
604 # Tokens Count -----------------------------------------------------------------------------
606 tokens_counter
= MonitorCounter(reset
, latch
, endpoint
.valid
& endpoint
.ready
, self
.tokens
.status
)
607 self
.submodules
+= token_counter
609 # Overflows Count (only useful when endpoint is expected to always be ready) ---------------
611 overflow_counter
= MonitorCounter(reset
, latch
, endpoint
.valid
& ~endpoint
.ready
, self
.overflows
.status
)
612 self
.submodules
+= overflow_counter
614 # Underflows Count (only useful when endpoint is expected to always be valid) --------------
616 underflow_counter
= MonitorCounter(reset
, latch
, ~endpoint
.valid
& endpoint
.ready
, self
.underflows
.status
)
617 self
.submodules
+= underflow_counter
619 # Pipe ---------------------------------------------------------------------------------------------
621 class PipeValid(Module
):
622 """Pipe valid/payload to cut timing path"""
623 def __init__(self
, layout
):
624 self
.sink
= sink
= Endpoint(layout
)
625 self
.source
= source
= Endpoint(layout
)
629 # Pipe when source is not valid or is ready.
631 If(~source
.valid | source
.ready
,
632 source
.valid
.eq(sink
.valid
),
633 source
.first
.eq(sink
.first
),
634 source
.last
.eq(sink
.last
),
635 source
.payload
.eq(sink
.payload
),
636 source
.param
.eq(sink
.param
),
639 self
.comb
+= sink
.ready
.eq(~source
.valid | source
.ready
)
642 class PipeReady(Module
):
643 """Pipe ready to cut timing path"""
644 def __init__(self
, layout
):
645 self
.sink
= sink
= Endpoint(layout
)
646 self
.source
= source
= Endpoint(layout
)
651 sink_d
= Endpoint(layout
)
654 If(sink
.valid
& ~source
.ready
,
659 If(~source
.ready
& ~valid
,
664 sink
.ready
.eq(~valid
),
666 sink_d
.connect(source
, omit
={"ready"})
668 sink
.connect(source
, omit
={"ready"})
672 # Buffer -------------------------------------------------------------------------------------------
674 class Buffer(PipeValid
): pass # FIXME: Replace Buffer with PipeValid in codebase?
676 # Cast ---------------------------------------------------------------------------------------------
678 class Cast(CombinatorialActor
):
679 def __init__(self
, layout_from
, layout_to
, reverse_from
=False, reverse_to
=False):
680 self
.sink
= Endpoint(_rawbits_layout(layout_from
))
681 self
.source
= Endpoint(_rawbits_layout(layout_to
))
682 CombinatorialActor
.__init
__(self
)
686 sigs_from
= self
.sink
.payload
.flatten()
688 sigs_from
= list(reversed(sigs_from
))
689 sigs_to
= self
.source
.payload
.flatten()
691 sigs_to
= list(reversed(sigs_to
))
692 if sum(len(s
) for s
in sigs_from
) != sum(len(s
) for s
in sigs_to
):
694 self
.comb
+= Cat(*sigs_to
).eq(Cat(*sigs_from
))
696 # Unpack/Pack --------------------------------------------------------------------------------------
698 class Unpack(Module
):
699 def __init__(self
, n
, layout_to
, reverse
=False):
700 self
.source
= source
= Endpoint(layout_to
)
701 description_from
= Endpoint(layout_to
).description
702 description_from
.payload_layout
= pack_layout(description_from
.payload_layout
, n
)
703 self
.sink
= sink
= Endpoint(description_from
)
712 last
.eq(mux
== (n
-1)),
713 source
.valid
.eq(sink
.valid
),
714 sink
.ready
.eq(last
& source
.ready
)
717 If(source
.valid
& source
.ready
,
727 chunk
= n
-i
-1 if reverse
else i
728 cases
[i
] = [source
.payload
.raw_bits().eq(getattr(sink
.payload
, "chunk"+str(chunk
)).raw_bits())]
729 self
.comb
+= Case(mux
, cases
).makedefault()
731 for f
in description_from
.param_layout
:
732 src
= getattr(self
.sink
, f
[0])
733 dst
= getattr(self
.source
, f
[0])
734 self
.comb
+= dst
.eq(src
)
737 source
.first
.eq(sink
.first
& first
),
738 source
.last
.eq(sink
.last
& last
)
743 def __init__(self
, layout_from
, n
, reverse
=False):
744 self
.sink
= sink
= Endpoint(layout_from
)
745 description_to
= Endpoint(layout_from
).description
746 description_to
.payload_layout
= pack_layout(description_to
.payload_layout
, n
)
747 self
.source
= source
= Endpoint(description_to
)
751 demux
= Signal(max=n
)
754 strobe_all
= Signal()
757 chunk
= n
-i
-1 if reverse
else i
758 cases
[i
] = [getattr(source
.payload
, "chunk"+str(chunk
)).raw_bits().eq(sink
.payload
.raw_bits())]
760 sink
.ready
.eq(~strobe_all | source
.ready
),
761 source
.valid
.eq(strobe_all
),
762 load_part
.eq(sink
.valid
& sink
.ready
)
765 for f
in description_to
.param_layout
:
766 src
= getattr(self
.sink
, f
[0])
767 dst
= getattr(self
.source
, f
[0])
768 self
.sync
+= If(load_part
, dst
.eq(src
))
770 demux_last
= ((demux
== (n
- 1)) | sink
.last
)
773 If(source
.ready
, strobe_all
.eq(0)),
783 If(source
.valid
& source
.ready
,
784 source
.first
.eq(sink
.first
),
785 source
.last
.eq(sink
.last
),
786 ).Elif(sink
.valid
& sink
.ready
,
787 source
.first
.eq(sink
.first | source
.first
),
788 source
.last
.eq(sink
.last | source
.last
)
792 # Pipeline -----------------------------------------------------------------------------------------
794 class Pipeline(Module
):
795 def __init__(self
, *modules
):
798 # expose sink of first module
800 if hasattr(m
, "sink"):
802 for i
in range(1, n
):
804 if isinstance(m
, Endpoint
):
808 if isinstance(m_n
, Endpoint
):
813 self
.comb
+= source
.connect(sink
)
815 # expose source of last module
817 if hasattr(m
, "source"):
818 self
.source
= m
.source
820 # BufferizeEndpoints -------------------------------------------------------------------------------
822 # Add buffers on Endpoints (can be used to improve timings)
823 class BufferizeEndpoints(ModuleTransformer
):
824 def __init__(self
, endpoint_dict
):
825 self
.endpoint_dict
= endpoint_dict
827 def transform_instance(self
, submodule
):
828 for name
, direction
in self
.endpoint_dict
.items():
829 endpoint
= getattr(submodule
, name
)
830 # add buffer on sinks
831 if direction
== DIR_SINK
:
832 buf
= Buffer(endpoint
.description
)
833 submodule
.submodules
+= buf
834 setattr(submodule
, name
, buf
.sink
)
835 submodule
.comb
+= buf
.source
.connect(endpoint
)
836 # add buffer on sources
837 elif direction
== DIR_SOURCE
:
838 buf
= Buffer(endpoint
.description
)
839 submodule
.submodules
+= buf
840 submodule
.comb
+= endpoint
.connect(buf
.sink
)
841 setattr(submodule
, name
, buf
.source
)