d8b127e9672ed691d582fcb597f5c8491eb4505e
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
:
30 raise ValueError(f
[0] + " already attributed in payload layout")
32 raise ValueError(f
[0] + " cannot be used in endpoint layout")
36 ("valid", 1, DIR_FANOUT
),
37 ("ready", 1, DIR_FANIN
),
38 ("first", 1, DIR_FANOUT
),
39 ("last", 1, DIR_FANOUT
),
40 ("payload", _make_fanout(self
.payload_layout
))
45 class Endpoint(Record
):
46 def __init__(self
, layout_or_description
, **kwargs
):
47 if isinstance(layout_or_description
, EndpointDescription
):
48 self
.description
= layout_or_description
50 self
.description
= EndpointDescription(layout_or_description
)
51 super().__init
__(self
.description
.get_full_layout(), src_loc_at
=1, **kwargs
)
53 def __getattr__(self
, name
):
55 return super().__getattr
__(name
)
56 except AttributeError:
57 return self
.fields
["payload"][name
]
61 def __init__(self
, payload_layout
):
62 self
.sink
= Endpoint(payload_layout
)
63 self
.source
= Endpoint(payload_layout
)
65 self
.layout
= Layout([
66 ("payload", self
.sink
.description
.payload_layout
),
67 ("first", 1, DIR_FANOUT
),
68 ("last", 1, DIR_FANOUT
)
71 def elaborate(self
, platform
):
74 fifo
= m
.submodules
.fifo
= self
.fifo
75 fifo_din
= Record(self
.layout
)
76 fifo_dout
= Record(self
.layout
)
78 fifo
.w_data
.eq(fifo_din
),
79 fifo_dout
.eq(fifo
.r_data
),
81 self
.sink
.ready
.eq(fifo
.w_rdy
),
82 fifo
.w_en
.eq(self
.sink
.valid
),
83 fifo_din
.first
.eq(self
.sink
.first
),
84 fifo_din
.last
.eq(self
.sink
.last
),
85 fifo_din
.payload
.eq(self
.sink
.payload
),
87 self
.source
.valid
.eq(fifo
.r_rdy
),
88 self
.source
.first
.eq(fifo_dout
.first
),
89 self
.source
.last
.eq(fifo_dout
.last
),
90 self
.source
.payload
.eq(fifo_dout
.payload
),
91 fifo
.r_en
.eq(self
.source
.ready
)
97 class SyncFIFO(Elaboratable
, _FIFOWrapper
):
98 def __init__(self
, layout
, depth
, fwft
=True, buffered
=False):
99 super().__init
__(layout
)
101 self
.fifo
= fifo
.SyncFIFOBuffered(width
=len(Record(self
.layout
)), depth
=depth
, fwft
=fwft
)
103 self
.fifo
= fifo
.SyncFIFO(width
=len(Record(self
.layout
)), depth
=depth
, fwft
=fwft
)
104 self
.depth
= self
.fifo
.depth
105 self
.level
= self
.fifo
.level
108 class AsyncFIFO(Elaboratable
, _FIFOWrapper
):
109 def __init__(self
, layout
, depth
, r_domain
="read", w_domain
="write"):
110 super().__init
__(layout
)
111 self
.fifo
= fifo
.AsyncFIFO(width
=len(Record(self
.layout
)), depth
=depth
,
112 r_domain
=r_domain
, w_domain
=w_domain
)
113 self
.depth
= self
.fifo
.depth
115 class PipeValid(Elaboratable
):
116 """Pipe valid/payload to cut timing path"""
117 def __init__(self
, layout
):
118 self
.sink
= Endpoint(layout
)
119 self
.source
= Endpoint(layout
)
121 def elaborate(self
, platform
):
124 # Pipe when source is not valid or is ready.
125 with m
.If(~self
.source
.valid | self
.source
.ready
):
127 self
.source
.valid
.eq(self
.sink
.valid
),
128 self
.source
.first
.eq(self
.sink
.first
),
129 self
.source
.last
.eq(self
.sink
.last
),
130 self
.source
.payload
.eq(self
.sink
.payload
),
131 #self.source.param.eq(self.sink.param), # TODO ensure this can be commented
133 m
.d
.comb
+= self
.sink
.ready
.eq(~self
.source
.valid | self
.source
.ready
)
137 class Buffer(PipeValid
): pass # FIXME: Replace Buffer with PipeValid in codebase?