1 # This file is Copyright (c) 2020 LambdaConcept <contact@lambdaconcept.com>
4 from nmigen
.hdl
.rec
import *
5 from nmigen
.lib
import fifo
8 __all__
= ["Endpoint", "SyncFIFO", "AsyncFIFO", "Buffer"]
11 def _make_fanout(layout
):
14 if isinstance(f
[1], (int, tuple)):
15 r
.append((f
[0], f
[1], DIR_FANOUT
))
17 r
.append((f
[0], _make_fanout(f
[1])))
21 class EndpointDescription
:
22 def __init__(self
, payload_layout
):
23 self
.payload_layout
= payload_layout
25 def get_full_layout(self
):
26 reserved
= {"valid", "ready", "first", "last", "payload"}
28 for f
in self
.payload_layout
:
29 if f
[0] in attributed
:
31 f
[0] + " already attributed in payload layout")
33 raise ValueError(f
[0] + " cannot be used in endpoint layout")
37 ("valid", 1, DIR_FANOUT
),
38 ("ready", 1, DIR_FANIN
),
39 ("first", 1, DIR_FANOUT
),
40 ("last", 1, DIR_FANOUT
),
41 ("payload", _make_fanout(self
.payload_layout
))
46 class Endpoint(Record
):
47 def __init__(self
, layout_or_description
, **kwargs
):
48 if isinstance(layout_or_description
, EndpointDescription
):
49 self
.description
= layout_or_description
51 self
.description
= EndpointDescription(layout_or_description
)
52 super().__init
__(self
.description
.get_full_layout(), src_loc_at
=1, **kwargs
)
54 def __getattr__(self
, name
):
56 return super().__getattr
__(name
)
57 except AttributeError:
58 return self
.fields
["payload"][name
]
62 def __init__(self
, payload_layout
):
63 self
.sink
= Endpoint(payload_layout
)
64 self
.source
= Endpoint(payload_layout
)
66 self
.layout
= Layout([
67 ("payload", self
.sink
.description
.payload_layout
),
68 ("first", 1, DIR_FANOUT
),
69 ("last", 1, DIR_FANOUT
)
72 def elaborate(self
, platform
):
75 fifo
= m
.submodules
.fifo
= self
.fifo
76 fifo_din
= Record(self
.layout
)
77 fifo_dout
= Record(self
.layout
)
79 fifo
.w_data
.eq(fifo_din
),
80 fifo_dout
.eq(fifo
.r_data
),
82 self
.sink
.ready
.eq(fifo
.w_rdy
),
83 fifo
.w_en
.eq(self
.sink
.valid
),
84 fifo_din
.first
.eq(self
.sink
.first
),
85 fifo_din
.last
.eq(self
.sink
.last
),
86 fifo_din
.payload
.eq(self
.sink
.payload
),
88 self
.source
.valid
.eq(fifo
.r_rdy
),
89 self
.source
.first
.eq(fifo_dout
.first
),
90 self
.source
.last
.eq(fifo_dout
.last
),
91 self
.source
.payload
.eq(fifo_dout
.payload
),
92 fifo
.r_en
.eq(self
.source
.ready
)
98 class SyncFIFO(Elaboratable
, _FIFOWrapper
):
99 def __init__(self
, layout
, depth
, fwft
=True, buffered
=False):
100 super().__init
__(layout
)
102 self
.fifo
= fifo
.SyncFIFOBuffered(
103 width
=len(Record(self
.layout
)), depth
=depth
, fwft
=fwft
)
105 self
.fifo
= fifo
.SyncFIFO(
106 width
=len(Record(self
.layout
)), depth
=depth
, fwft
=fwft
)
107 self
.depth
= self
.fifo
.depth
108 self
.level
= self
.fifo
.level
111 class AsyncFIFO(Elaboratable
, _FIFOWrapper
):
112 def __init__(self
, layout
, depth
, r_domain
="read", w_domain
="write"):
113 super().__init
__(layout
)
114 self
.fifo
= fifo
.AsyncFIFO(width
=len(Record(self
.layout
)), depth
=depth
,
115 r_domain
=r_domain
, w_domain
=w_domain
)
116 self
.depth
= self
.fifo
.depth
119 class PipeValid(Elaboratable
):
120 """Pipe valid/payload to cut timing path"""
122 def __init__(self
, layout
):
123 self
.sink
= Endpoint(layout
)
124 self
.source
= Endpoint(layout
)
126 def elaborate(self
, platform
):
129 # Pipe when source is not valid or is ready.
130 with m
.If(~self
.source
.valid | self
.source
.ready
):
132 self
.source
.valid
.eq(self
.sink
.valid
),
133 self
.source
.first
.eq(self
.sink
.first
),
134 self
.source
.last
.eq(self
.sink
.last
),
135 self
.source
.payload
.eq(self
.sink
.payload
),
136 # self.source.param.eq(self.sink.param), # TODO ensure this can be commented
138 m
.d
.comb
+= self
.sink
.ready
.eq(~self
.source
.valid | self
.source
.ready
)
143 class Buffer(PipeValid
):
144 pass # FIXME: Replace Buffer with PipeValid in codebase?