1 """ Combinatorial Multi-input multiplexer block conforming to Pipeline API
5 from nmigen
import Signal
, Cat
, Const
, Mux
, Module
, Array
6 from nmigen
.cli
import verilog
, rtlil
7 from nmigen
.lib
.coding
import PriorityEncoder
8 from nmigen
.hdl
.rec
import Record
, Layout
10 from collections
.abc
import Sequence
12 from example_buf_pipe
import eq
, NextControl
, PrevControl
, ExampleStage
15 class MultiInControlBase
:
16 """ Common functions for Pipeline API
18 def __init__(self
, in_multi
=None, p_len
=1):
19 """ Multi-input Control class. Conforms to same API as ControlBase...
20 mostly. has additional indices to the *multiple* input stages
22 * p: contains ready/valid to the previous stages PLURAL
23 * n: contains ready/valid to the next stage
26 * add i_data members to PrevControl and
27 * add o_data member to NextControl
29 # set up input and output IO ACK (prev/next ready/valid)
31 for i
in range(p_len
):
32 p
.append(PrevControl(in_multi
))
34 self
.n
= NextControl()
36 def connect_to_next(self
, nxt
, p_idx
=0):
37 """ helper function to connect to the next stage data/valid/ready.
39 return self
.n
.connect_to_next(nxt
.p
[p_idx
])
41 def _connect_in(self
, prev
, idx
=0, prev_idx
=None):
42 """ helper function to connect stage to an input source. do not
43 use to connect stage-to-stage!
46 return self
.p
[idx
]._connect
_in
(prev
.p
)
47 return self
.p
[idx
]._connect
_in
(prev
.p
[prev_idx
])
49 def _connect_out(self
, nxt
):
50 """ helper function to connect stage to an output source. do not
51 use to connect stage-to-stage!
54 return self
.n
._connect
_out
(nxt
.n
)
55 return self
.n
._connect
_out
(nxt
.n
)
57 def set_input(self
, i
, idx
=0):
58 """ helper function to set the input data
60 return eq(self
.p
[idx
].i_data
, i
)
64 for i
in range(len(self
.p
)):
65 res
+= [self
.p
[i
].i_valid
, self
.p
[i
].o_ready
,
66 self
.p
[i
].i_data
]# XXX need flattening!]
67 res
+= [self
.n
.i_ready
, self
.n
.o_valid
,
68 self
.n
.o_data
] # XXX need flattening!]
73 class MultiOutControlBase
:
74 """ Common functions for Pipeline API
76 def __init__(self
, n_len
=1):
77 """ Multi-output Control class. Conforms to same API as ControlBase...
78 mostly. has additional indices to the multiple *output* stages
79 [MultiInControlBase has multiple *input* stages]
81 * p: contains ready/valid to the previou stage
82 * n: contains ready/valid to the next stages PLURAL
85 * add i_data member to PrevControl and
86 * add o_data members to NextControl
89 # set up input and output IO ACK (prev/next ready/valid)
90 self
.p
= PrevControl(in_multi
)
91 for i
in range(n_len
):
92 n
.append(NextControl())
95 def connect_to_next(self
, nxt
, n_idx
=0):
96 """ helper function to connect to the next stage data/valid/ready.
98 return self
.n
[n_idx
].connect_to_next(nxt
.p
)
100 def _connect_in(self
, prev
, idx
=0):
101 """ helper function to connect stage to an input source. do not
102 use to connect stage-to-stage!
104 return self
.n
[idx
]._connect
_in
(prev
.p
)
106 def _connect_out(self
, nxt
, idx
=0, nxt_idx
=None):
107 """ helper function to connect stage to an output source. do not
108 use to connect stage-to-stage!
111 return self
.n
[idx
]._connect
_out
(nxt
.n
)
112 return self
.n
[idx
]._connect
_out
(nxt
.n
[nxt_idx
])
114 def set_input(self
, i
):
115 """ helper function to set the input data
117 return eq(self
.p
.i_data
, i
)
121 res
+= [self
.p
.i_valid
, self
.p
.o_ready
,
122 self
.p
.i_data
] # XXX need flattening!
123 for i
in range(len(self
.n
)):
124 res
+= [self
.n
[i
].i_ready
, self
.n
[i
].o_valid
,
125 self
.n
[i
].o_data
] # XXX need flattening!
130 class CombMultiOutPipeline(MultiOutControlBase
):
131 """ A multi-input Combinatorial block conforming to the Pipeline API
135 p.i_data : StageInput, shaped according to ispec
137 p.o_data : StageOutput, shaped according to ospec
139 r_data : input_shape according to ispec
140 A temporary (buffered) copy of a prior (valid) input.
141 This is HELD if the output is not ready. It is updated
145 def __init__(self
, stage
, n_len
, n_mux
):
146 MultiOutControlBase
.__init
__(self
, n_len
=n_len
)
150 # set up the input and output data
151 for i
in range(p_len
):
152 self
.p
[i
].i_data
= stage
.ispec() # input type
153 self
.n
.o_data
= stage
.ospec()
155 def elaborate(self
, platform
):
158 m
.submodules
+= self
.p_mux
160 # need buffer register conforming to *input* spec
161 r_data
= self
.stage
.ispec() # input type
162 if hasattr(self
.stage
, "setup"):
163 self
.stage
.setup(m
, r_data
)
168 for i
in range(n_len
):
169 data_valid
.append(Signal(name
="data_valid", reset_less
=True))
170 n_i_readyn
.append(Signal(name
="n_i_readyn", reset_less
=True))
171 n_i_readyn
= Array(n_i_readyn
)
172 data_valid
= Array(data_valid
)
174 p_i_valid
= Signal(reset_less
=True)
175 m
.d
.comb
+= p_i_valid
.eq(self
.p
.i_valid_logic())
177 mid
= self
.p_mux
.m_id
179 for i
in range(p_len
):
180 m
.d
.comb
+= data_valid
[i
].eq(0)
181 m
.d
.comb
+= n_i_readyn
[i
].eq(1)
182 m
.d
.comb
+= self
.n
[i
].o_valid
.eq(data_valid
[i
])
183 m
.d
.comb
+= self
.p
[mid
].o_ready
.eq(~data_valid
[mid
] | self
.n
.i_ready
)
184 m
.d
.comb
+= n_i_readyn
[mid
].eq(~self
.n
[mid
].i_ready
& data_valid
[mid
])
185 anyvalid
= Signal(i
, reset_less
=True)
187 for i
in range(p_len
):
188 av
.append(~data_valid
[i
] | self
.n
[i
].i_ready
)
190 m
.d
.comb
+= self
.p
.o_ready
.eq(anyvalid
.bool())
191 m
.d
.comb
+= data_valid
[mid
].eq(p_i_valid | \
192 (n_i_readyn
[mid
] & data_valid
[mid
]))
194 with m
.If(self
.p
.i_valid
& self
.p
.o_ready
):
195 m
.d
.comb
+= eq(r_data
, self
.p
.i_data
)
196 m
.d
.comb
+= eq(self
.n
[mid
].o_data
, self
.stage
.process(r_data
))
201 class CombMultiInPipeline(MultiInControlBase
):
202 """ A multi-input Combinatorial block conforming to the Pipeline API
206 p.i_data : StageInput, shaped according to ispec
208 p.o_data : StageOutput, shaped according to ospec
210 r_data : input_shape according to ispec
211 A temporary (buffered) copy of a prior (valid) input.
212 This is HELD if the output is not ready. It is updated
216 def __init__(self
, stage
, p_len
, p_mux
):
217 MultiInControlBase
.__init
__(self
, p_len
=p_len
)
221 # set up the input and output data
222 for i
in range(p_len
):
223 self
.p
[i
].i_data
= stage
.ispec() # input type
224 self
.n
.o_data
= stage
.ospec()
226 def elaborate(self
, platform
):
229 m
.submodules
+= self
.p_mux
231 # need an array of buffer registers conforming to *input* spec
237 for i
in range(p_len
):
238 r
= self
.stage
.ispec() # input type
240 data_valid
.append(Signal(name
="data_valid", reset_less
=True))
241 p_i_valid
.append(Signal(name
="p_i_valid", reset_less
=True))
242 n_i_readyn
.append(Signal(name
="n_i_readyn", reset_less
=True))
243 if hasattr(self
.stage
, "setup"):
244 self
.stage
.setup(m
, r
)
246 r_data
= Array(r_data
)
247 p_i_valid
= Array(p_i_valid
)
248 n_i_readyn
= Array(n_i_readyn
)
249 data_valid
= Array(data_valid
)
251 mid
= self
.p_mux
.m_id
252 for i
in range(p_len
):
253 m
.d
.comb
+= data_valid
[i
].eq(0)
254 m
.d
.comb
+= n_i_readyn
[i
].eq(1)
255 m
.d
.comb
+= p_i_valid
[i
].eq(0)
256 m
.d
.comb
+= self
.p
[i
].o_ready
.eq(0)
257 m
.d
.comb
+= p_i_valid
[mid
].eq(self
.p_mux
.active
)
258 m
.d
.comb
+= self
.p
[mid
].o_ready
.eq(~data_valid
[mid
] | self
.n
.i_ready
)
259 m
.d
.comb
+= n_i_readyn
[mid
].eq(~self
.n
.i_ready
& data_valid
[mid
])
260 anyvalid
= Signal(i
, reset_less
=True)
262 for i
in range(p_len
):
263 av
.append(data_valid
[i
])
265 m
.d
.comb
+= self
.n
.o_valid
.eq(anyvalid
.bool())
266 m
.d
.comb
+= data_valid
[mid
].eq(p_i_valid
[mid
] | \
267 (n_i_readyn
[mid
] & data_valid
[mid
]))
269 for i
in range(p_len
):
270 vr
= Signal(reset_less
=True)
271 m
.d
.comb
+= vr
.eq(self
.p
[i
].i_valid
& self
.p
[i
].o_ready
)
273 m
.d
.comb
+= eq(r_data
[i
], self
.p
[i
].i_data
)
275 m
.d
.comb
+= eq(self
.n
.o_data
, self
.stage
.process(r_data
[mid
]))
280 class InputPriorityArbiter
:
281 def __init__(self
, pipe
, num_rows
):
283 self
.num_rows
= num_rows
284 self
.mmax
= int(log(self
.num_rows
) / log(2))
285 self
.m_id
= Signal(self
.mmax
, reset_less
=True) # multiplex id
286 self
.active
= Signal(reset_less
=True)
288 def elaborate(self
, platform
):
291 assert len(self
.pipe
.p
) == self
.num_rows
, \
292 "must declare input to be same size"
293 pe
= PriorityEncoder(self
.num_rows
)
294 m
.submodules
.selector
= pe
296 # connect priority encoder
298 for i
in range(self
.num_rows
):
299 p_i_valid
= Signal(reset_less
=True)
300 m
.d
.comb
+= p_i_valid
.eq(self
.pipe
.p
[i
].i_valid_logic())
301 in_ready
.append(p_i_valid
)
302 m
.d
.comb
+= pe
.i
.eq(Cat(*in_ready
)) # array of input "valids"
303 m
.d
.comb
+= self
.active
.eq(~pe
.n
) # encoder active (one input valid)
304 m
.d
.comb
+= self
.m_id
.eq(pe
.o
) # output one active input
309 return [self
.m_id
, self
.active
]
313 class ExamplePipeline(CombMultiInPipeline
):
314 """ an example of how to use the combinatorial pipeline.
317 def __init__(self
, p_len
=2):
318 p_mux
= InputPriorityArbiter(self
, p_len
)
319 CombMultiInPipeline
.__init
__(self
, ExampleStage
, p_len
, p_mux
)
322 if __name__
== '__main__':
324 dut
= ExamplePipeline()
325 vl
= rtlil
.convert(dut
, ports
=dut
.ports())
326 with
open("test_combpipe.il", "w") as f
: