1 """ key strategic example showing how to do multi-input fan-in into a
2 multi-stage pipeline, then multi-output fanout, with an unary muxid
5 the multiplex ID from the fan-in is passed in to the pipeline, preserved,
6 and used as a routing ID on the fanout.
9 from random
import randint
11 from nmigen
import Module
, Signal
, Cat
, Value
, Elaboratable
12 from nmigen
.compat
.sim
import run_simulation
13 from nmigen
.cli
import verilog
, rtlil
15 from nmutil
.multipipe
import CombMultiOutPipeline
, CombMuxOutPipe
16 from nmutil
.multipipe
import PriorityCombMuxInPipe
17 from nmutil
.singlepipe
import MaskCancellable
, RecordObject
, Object
20 class PassData(Object
):
23 self
.muxid
= Signal(2, reset_less
=True)
24 self
.idx
= Signal(8, reset_less
=True)
25 self
.data
= Signal(16, reset_less
=True)
26 self
.operator
= Signal(2, reset_less
=True)
27 self
.routeid
= Signal(2, reset_less
=True) # muxidname
30 class PassThroughStage
:
36 return self
.ispec() # same as ospec
37 def setup(self
, m
, i
):
44 class SplitRouteStage
:
51 return self
.ispec() # same as ospec
53 def setup(self
, m
, i
):
56 with m
.If(i
.operator
== 0):
57 #comb += self.o.routeid.eq(1) # selects 2nd output in CombMuxOutPipe
58 comb
+= self
.o
.data
.eq(i
.data
+ 1) # add 1 to say "we did it"
59 comb
+= self
.o
.operator
.eq(1) # don't get into infinite loop
61 comb
+= self
.o
.routeid
.eq(0) # selects 2nd output in CombMuxOutPipe
67 class RouteBackPipe(CombMuxOutPipe
):
68 """ routes data back to start of pipeline
72 stage
= SplitRouteStage()
73 CombMuxOutPipe
.__init
__(self
, stage
, n_len
=2,
74 maskwid
=4, muxidname
="routeid",
78 class MergeRoutePipe(PriorityCombMuxInPipe
):
79 """ merges data coming from end of pipe (with operator now == 1)
83 stage
= PassThroughStage()
84 PriorityCombMuxInPipe
.__init
__(self
, stage
, p_len
=2, maskwid
=4,
89 class PassThroughPipe(MaskCancellable
):
90 def __init__(self
, maskwid
):
91 MaskCancellable
.__init
__(self
, PassThroughStage(), maskwid
)
95 def __init__(self
, dut
, tlen
):
101 for muxid
in range(dut
.num_rows
):
104 self
.sent
[muxid
] = []
105 for i
in range(self
.tlen
):
106 self
.di
[muxid
][i
] = randint(0, 255) + (muxid
<<8)
107 self
.do
[muxid
][i
] = self
.di
[muxid
][i
]
109 def send(self
, muxid
):
110 for i
in range(self
.tlen
):
111 op2
= self
.di
[muxid
][i
]
112 rs
= self
.dut
.p
[muxid
]
113 yield rs
.valid_i
.eq(1)
114 yield rs
.data_i
.data
.eq(op2
)
115 yield rs
.data_i
.idx
.eq(i
)
116 yield rs
.data_i
.muxid
.eq(muxid
)
117 yield rs
.mask_i
.eq(1)
119 o_p_ready
= yield rs
.ready_o
122 o_p_ready
= yield rs
.ready_o
124 print ("send", muxid
, i
, hex(op2
), op2
)
125 self
.sent
[muxid
].append(i
)
127 yield rs
.valid_i
.eq(0)
128 yield rs
.mask_i
.eq(0)
129 # wait until it's received
130 while i
in self
.do
[muxid
]:
133 # wait random period of time before queueing another value
134 for i
in range(randint(0, 3)):
137 yield rs
.valid_i
.eq(0)
140 print ("send ended", muxid
)
142 ## wait random period of time before queueing another value
143 #for i in range(randint(0, 3)):
146 #send_range = randint(0, 3)
150 # send = randint(0, send_range) != 0
152 def rcv(self
, muxid
):
153 rs
= self
.dut
.p
[muxid
]
157 if False and self
.sent
[muxid
] and randint(0, 2) == 0:
158 todel
= self
.sent
[muxid
].pop()
159 print ("to delete", muxid
, self
.sent
[muxid
], todel
)
160 if todel
in self
.do
[muxid
]:
161 del self
.do
[muxid
][todel
]
162 yield rs
.stop_i
.eq(1)
163 print ("left", muxid
, self
.do
[muxid
])
164 if len(self
.do
[muxid
]) == 0:
167 stall_range
= randint(0, 3)
168 for j
in range(randint(1,10)):
169 stall
= randint(0, stall_range
) != 0
170 yield self
.dut
.n
[0].ready_i
.eq(stall
)
173 n
= self
.dut
.n
[muxid
]
174 yield n
.ready_i
.eq(1)
176 yield rs
.stop_i
.eq(0) # resets cancel mask
177 o_n_valid
= yield n
.valid_o
178 i_n_ready
= yield n
.ready_i
179 if not o_n_valid
or not i_n_ready
:
182 out_muxid
= yield n
.data_o
.muxid
183 out_i
= yield n
.data_o
.idx
184 out_v
= yield n
.data_o
.data
186 print ("recv", out_muxid
, out_i
, hex(out_v
), out_v
)
188 # see if this output has occurred already, delete it if it has
189 assert muxid
== out_muxid
, \
190 "out_muxid %d not correct %d" % (out_muxid
, muxid
)
191 if out_i
not in self
.sent
[muxid
]:
192 print ("cancelled/recv", muxid
, out_i
)
194 assert out_i
in self
.do
[muxid
], "out_i %d not in array %s" % \
195 (out_i
, repr(self
.do
[muxid
]))
196 assert self
.do
[muxid
][out_i
] == out_v
+ 1 # check data
197 del self
.do
[muxid
][out_i
]
198 todel
= self
.sent
[muxid
].index(out_i
)
199 del self
.sent
[muxid
][todel
]
201 # check if there's any more outputs
202 if len(self
.do
[muxid
]) == 0:
205 print ("recv ended", muxid
)
208 class TestPriorityMuxPipe(PriorityCombMuxInPipe
):
209 def __init__(self
, num_rows
):
210 self
.num_rows
= num_rows
211 stage
= PassThroughStage()
212 PriorityCombMuxInPipe
.__init
__(self
, stage
,
213 p_len
=self
.num_rows
, maskwid
=1)
216 class TestMuxOutPipe(CombMuxOutPipe
):
217 def __init__(self
, num_rows
):
218 self
.num_rows
= num_rows
219 stage
= PassThroughStage()
220 CombMuxOutPipe
.__init
__(self
, stage
, n_len
=self
.num_rows
,
224 class TestInOutPipe(Elaboratable
):
225 def __init__(self
, num_rows
=4):
226 self
.num_rows
= nr
= num_rows
227 self
.inpipe
= TestPriorityMuxPipe(nr
) # fan-in (combinatorial)
228 self
.mergein
= MergeRoutePipe() # merge in feedback
229 self
.pipe1
= PassThroughPipe(nr
) # stage 1 (clock-sync)
230 self
.pipe2
= PassThroughPipe(nr
) # stage 2 (clock-sync)
231 #self.pipe3 = PassThroughPipe(nr) # stage 3 (clock-sync)
232 #self.pipe4 = PassThroughPipe(nr) # stage 4 (clock-sync)
233 self
.splitback
= RouteBackPipe() # split back to mergein
234 self
.outpipe
= TestMuxOutPipe(nr
) # fan-out (combinatorial)
236 self
.p
= self
.inpipe
.p
# kinda annoying,
237 self
.n
= self
.outpipe
.n
# use pipe in/out as this class in/out
238 self
._ports
= self
.inpipe
.ports() + self
.outpipe
.ports()
240 def elaborate(self
, platform
):
242 m
.submodules
.inpipe
= self
.inpipe
243 m
.submodules
.mergein
= self
.mergein
244 m
.submodules
.pipe1
= self
.pipe1
245 m
.submodules
.pipe2
= self
.pipe2
246 #m.submodules.pipe3 = self.pipe3
247 #m.submodules.pipe4 = self.pipe4
248 m
.submodules
.splitback
= self
.splitback
249 m
.submodules
.outpipe
= self
.outpipe
251 m
.d
.comb
+= self
.inpipe
.n
.connect_to_next(self
.mergein
.p
[0])
252 m
.d
.comb
+= self
.mergein
.n
.connect_to_next(self
.pipe1
.p
)
253 m
.d
.comb
+= self
.pipe1
.connect_to_next(self
.pipe2
)
254 #m.d.comb += self.pipe2.connect_to_next(self.pipe3)
255 #m.d.comb += self.pipe3.connect_to_next(self.pipe4)
256 m
.d
.comb
+= self
.pipe2
.connect_to_next(self
.splitback
)
257 m
.d
.comb
+= self
.splitback
.n
[1].connect_to_next(self
.mergein
.p
[1])
258 m
.d
.comb
+= self
.splitback
.n
[0].connect_to_next(self
.outpipe
.p
)
267 dut
= TestInOutPipe()
268 vl
= rtlil
.convert(dut
, ports
=dut
.ports())
269 with
open("test_inoutmux_feedback_pipe.il", "w") as f
:
274 test
= InputTest(dut
, tlen
)
275 run_simulation(dut
, [test
.rcv(1), test
.rcv(0),
276 test
.rcv(3), test
.rcv(2),
277 test
.send(0), test
.send(1),
278 test
.send(3), test
.send(2),
280 vcd_name
="test_inoutmux_feedback_pipe.vcd")
282 if __name__
== '__main__':