remove unneeded data_value
[ieee754fpu.git] / src / add / multipipe.py
1 """ Combinatorial Multi-input and Multi-output multiplexer blocks
2 conforming to Pipeline API
3
4 Multi-input is complex because if any one input is ready, the output
5 can be ready, and the decision comes from a separate module.
6
7 Multi-output is simple (pretty much identical to UnbufferedPipeline),
8 and the selection is just a mux. The only proviso (difference) being:
9 the outputs not being selected have to have their o_ready signals
10 DEASSERTED.
11 """
12
13 from math import log
14 from nmigen import Signal, Cat, Const, Mux, Module, Array
15 from nmigen.cli import verilog, rtlil
16 from nmigen.lib.coding import PriorityEncoder
17 from nmigen.hdl.rec import Record, Layout
18
19 from collections.abc import Sequence
20
21 from example_buf_pipe import eq, NextControl, PrevControl, ExampleStage
22
23
24 class MultiInControlBase:
25 """ Common functions for Pipeline API
26 """
27 def __init__(self, in_multi=None, p_len=1):
28 """ Multi-input Control class. Conforms to same API as ControlBase...
29 mostly. has additional indices to the *multiple* input stages
30
31 * p: contains ready/valid to the previous stages PLURAL
32 * n: contains ready/valid to the next stage
33
34 User must also:
35 * add i_data members to PrevControl and
36 * add o_data member to NextControl
37 """
38 # set up input and output IO ACK (prev/next ready/valid)
39 p = []
40 for i in range(p_len):
41 p.append(PrevControl(in_multi))
42 self.p = Array(p)
43 self.n = NextControl()
44
45 def connect_to_next(self, nxt, p_idx=0):
46 """ helper function to connect to the next stage data/valid/ready.
47 """
48 return self.n.connect_to_next(nxt.p[p_idx])
49
50 def _connect_in(self, prev, idx=0, prev_idx=None):
51 """ helper function to connect stage to an input source. do not
52 use to connect stage-to-stage!
53 """
54 if prev_idx is None:
55 return self.p[idx]._connect_in(prev.p)
56 return self.p[idx]._connect_in(prev.p[prev_idx])
57
58 def _connect_out(self, nxt):
59 """ helper function to connect stage to an output source. do not
60 use to connect stage-to-stage!
61 """
62 if nxt_idx is None:
63 return self.n._connect_out(nxt.n)
64 return self.n._connect_out(nxt.n)
65
66 def set_input(self, i, idx=0):
67 """ helper function to set the input data
68 """
69 return eq(self.p[idx].i_data, i)
70
71 def ports(self):
72 res = []
73 for i in range(len(self.p)):
74 res += [self.p[i].i_valid, self.p[i].o_ready,
75 self.p[i].i_data]# XXX need flattening!]
76 res += [self.n.i_ready, self.n.o_valid,
77 self.n.o_data] # XXX need flattening!]
78 return res
79
80
81 class MultiOutControlBase:
82 """ Common functions for Pipeline API
83 """
84 def __init__(self, n_len=1, in_multi=None):
85 """ Multi-output Control class. Conforms to same API as ControlBase...
86 mostly. has additional indices to the multiple *output* stages
87 [MultiInControlBase has multiple *input* stages]
88
89 * p: contains ready/valid to the previou stage
90 * n: contains ready/valid to the next stages PLURAL
91
92 User must also:
93 * add i_data member to PrevControl and
94 * add o_data members to NextControl
95 """
96
97 # set up input and output IO ACK (prev/next ready/valid)
98 self.p = PrevControl(in_multi)
99 n = []
100 for i in range(n_len):
101 n.append(NextControl())
102 self.n = Array(n)
103
104 def connect_to_next(self, nxt, n_idx=0):
105 """ helper function to connect to the next stage data/valid/ready.
106 """
107 return self.n[n_idx].connect_to_next(nxt.p)
108
109 def _connect_in(self, prev, idx=0):
110 """ helper function to connect stage to an input source. do not
111 use to connect stage-to-stage!
112 """
113 return self.n[idx]._connect_in(prev.p)
114
115 def _connect_out(self, nxt, idx=0, nxt_idx=None):
116 """ helper function to connect stage to an output source. do not
117 use to connect stage-to-stage!
118 """
119 if nxt_idx is None:
120 return self.n[idx]._connect_out(nxt.n)
121 return self.n[idx]._connect_out(nxt.n[nxt_idx])
122
123 def set_input(self, i):
124 """ helper function to set the input data
125 """
126 return eq(self.p.i_data, i)
127
128 def ports(self):
129 res = []
130 res += [self.p.i_valid, self.p.o_ready,
131 self.p.i_data] # XXX need flattening!
132 for i in range(len(self.n)):
133 res += [self.n[i].i_ready, self.n[i].o_valid,
134 self.n[i].o_data] # XXX need flattening!
135 return res
136
137
138
139 class CombMultiOutPipeline(MultiOutControlBase):
140 """ A multi-input Combinatorial block conforming to the Pipeline API
141
142 Attributes:
143 -----------
144 p.i_data : StageInput, shaped according to ispec
145 The pipeline input
146 p.o_data : StageOutput, shaped according to ospec
147 The pipeline output
148 r_data : input_shape according to ispec
149 A temporary (buffered) copy of a prior (valid) input.
150 This is HELD if the output is not ready. It is updated
151 SYNCHRONOUSLY.
152 """
153
154 def __init__(self, stage, n_len, n_mux):
155 MultiOutControlBase.__init__(self, n_len=n_len)
156 self.stage = stage
157 self.n_mux = n_mux
158
159 # set up the input and output data
160 self.p.i_data = stage.ispec() # input type
161 for i in range(n_len):
162 self.n[i].o_data = stage.ospec() # output type
163
164 def elaborate(self, platform):
165 m = Module()
166
167 if hasattr(self.n_mux, "elaborate"): # TODO: identify submodule?
168 m.submodules += self.n_mux
169
170 # need buffer register conforming to *input* spec
171 r_data = self.stage.ispec() # input type
172 if hasattr(self.stage, "setup"):
173 self.stage.setup(m, r_data)
174
175 mid = self.n_mux.m_id
176
177 p_i_valid = Signal(reset_less=True)
178 m.d.comb += p_i_valid.eq(self.p.i_valid_logic())
179
180 for i in range(len(self.n)):
181 m.d.comb += self.n[i].o_valid.eq(0)
182 data_valid = self.n[mid].o_valid
183 m.d.comb += self.p.o_ready.eq(~data_valid | self.n[mid].i_ready)
184 m.d.comb += data_valid.eq(p_i_valid | \
185 (~self.n[mid].i_ready & data_valid))
186 with m.If(self.p.i_valid & self.p.o_ready):
187 m.d.comb += eq(r_data, self.p.i_data)
188 m.d.comb += eq(self.n[mid].o_data, self.stage.process(r_data))
189
190 return m
191
192
193 class CombMultiInPipeline(MultiInControlBase):
194 """ A multi-input Combinatorial block conforming to the Pipeline API
195
196 Attributes:
197 -----------
198 p.i_data : StageInput, shaped according to ispec
199 The pipeline input
200 p.o_data : StageOutput, shaped according to ospec
201 The pipeline output
202 r_data : input_shape according to ispec
203 A temporary (buffered) copy of a prior (valid) input.
204 This is HELD if the output is not ready. It is updated
205 SYNCHRONOUSLY.
206 """
207
208 def __init__(self, stage, p_len, p_mux):
209 MultiInControlBase.__init__(self, p_len=p_len)
210 self.stage = stage
211 self.p_mux = p_mux
212
213 # set up the input and output data
214 for i in range(p_len):
215 self.p[i].i_data = stage.ispec() # input type
216 self.n.o_data = stage.ospec()
217
218 def elaborate(self, platform):
219 m = Module()
220
221 m.submodules += self.p_mux
222
223 # need an array of buffer registers conforming to *input* spec
224 r_data = []
225 data_valid = []
226 p_i_valid = []
227 n_i_readyn = []
228 p_len = len(self.p)
229 for i in range(p_len):
230 r = self.stage.ispec() # input type
231 r_data.append(r)
232 data_valid.append(Signal(name="data_valid", reset_less=True))
233 p_i_valid.append(Signal(name="p_i_valid", reset_less=True))
234 n_i_readyn.append(Signal(name="n_i_readyn", reset_less=True))
235 if hasattr(self.stage, "setup"):
236 self.stage.setup(m, r)
237 if len(r_data) > 1:
238 r_data = Array(r_data)
239 p_i_valid = Array(p_i_valid)
240 n_i_readyn = Array(n_i_readyn)
241 data_valid = Array(data_valid)
242
243 mid = self.p_mux.m_id
244 for i in range(p_len):
245 m.d.comb += data_valid[i].eq(0)
246 m.d.comb += n_i_readyn[i].eq(1)
247 m.d.comb += p_i_valid[i].eq(0)
248 m.d.comb += self.p[i].o_ready.eq(0)
249 m.d.comb += p_i_valid[mid].eq(self.p_mux.active)
250 m.d.comb += self.p[mid].o_ready.eq(~data_valid[mid] | self.n.i_ready)
251 m.d.comb += n_i_readyn[mid].eq(~self.n.i_ready & data_valid[mid])
252 anyvalid = Signal(i, reset_less=True)
253 av = []
254 for i in range(p_len):
255 av.append(data_valid[i])
256 anyvalid = Cat(*av)
257 m.d.comb += self.n.o_valid.eq(anyvalid.bool())
258 m.d.comb += data_valid[mid].eq(p_i_valid[mid] | \
259 (n_i_readyn[mid] & data_valid[mid]))
260
261 for i in range(p_len):
262 vr = Signal(reset_less=True)
263 m.d.comb += vr.eq(self.p[i].i_valid & self.p[i].o_ready)
264 with m.If(vr):
265 m.d.comb += eq(r_data[i], self.p[i].i_data)
266
267 m.d.comb += eq(self.n.o_data, self.stage.process(r_data[mid]))
268
269 return m
270
271
272 class InputPriorityArbiter:
273 def __init__(self, pipe, num_rows):
274 self.pipe = pipe
275 self.num_rows = num_rows
276 self.mmax = int(log(self.num_rows) / log(2))
277 self.m_id = Signal(self.mmax, reset_less=True) # multiplex id
278 self.active = Signal(reset_less=True)
279
280 def elaborate(self, platform):
281 m = Module()
282
283 assert len(self.pipe.p) == self.num_rows, \
284 "must declare input to be same size"
285 pe = PriorityEncoder(self.num_rows)
286 m.submodules.selector = pe
287
288 # connect priority encoder
289 in_ready = []
290 for i in range(self.num_rows):
291 p_i_valid = Signal(reset_less=True)
292 m.d.comb += p_i_valid.eq(self.pipe.p[i].i_valid_logic())
293 in_ready.append(p_i_valid)
294 m.d.comb += pe.i.eq(Cat(*in_ready)) # array of input "valids"
295 m.d.comb += self.active.eq(~pe.n) # encoder active (one input valid)
296 m.d.comb += self.m_id.eq(pe.o) # output one active input
297
298 return m
299
300 def ports(self):
301 return [self.m_id, self.active]
302
303
304
305 class ExamplePipeline(CombMultiInPipeline):
306 """ an example of how to use the combinatorial pipeline.
307 """
308
309 def __init__(self, p_len=2):
310 p_mux = InputPriorityArbiter(self, p_len)
311 CombMultiInPipeline.__init__(self, ExampleStage, p_len, p_mux)
312
313
314 if __name__ == '__main__':
315
316 dut = ExamplePipeline()
317 vl = rtlil.convert(dut, ports=dut.ports())
318 with open("test_combpipe.il", "w") as f:
319 f.write(vl)