aea582dd69d39121d5f60c8fdf167dcbe6a21dc6
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
, Const
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
:
53 def setup(self
, m
, i
):
56 with m
.If(i
.operator
== Const(1, 2)):
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(2) # don't get into infinite loop
61 comb
+= self
.o
.routeid
.eq(0) # selects 2nd output in CombMuxOutPipe
67 class DecisionPipe(MaskCancellable
):
68 def __init__(self
, maskwid
):
69 stage
= SplitRouteStage()
70 MaskCancellable
.__init
__(self
, stage
, maskwid
)
72 class RouteBackPipe(CombMuxOutPipe
):
73 """ routes data back to start of pipeline
76 stage
= PassThroughStage()
77 CombMuxOutPipe
.__init
__(self
, stage
, n_len
=2,
78 maskwid
=4, muxidname
="routeid",
82 class MergeRoutePipe(PriorityCombMuxInPipe
):
83 """ merges data coming from end of pipe (with operator now == 1)
86 stage
= PassThroughStage()
87 PriorityCombMuxInPipe
.__init
__(self
, stage
, p_len
=2, maskwid
=4,
92 class PassThroughPipe(MaskCancellable
):
93 def __init__(self
, maskwid
):
94 MaskCancellable
.__init
__(self
, PassThroughStage(), maskwid
)
98 def __init__(self
, dut
, tlen
):
104 for muxid
in range(dut
.num_rows
):
107 self
.sent
[muxid
] = []
108 for i
in range(self
.tlen
):
109 self
.di
[muxid
][i
] = randint(0, 255) + (muxid
<<8)
110 self
.do
[muxid
][i
] = self
.di
[muxid
][i
]
112 def send(self
, muxid
):
113 for i
in range(self
.tlen
):
114 op2
= self
.di
[muxid
][i
]
115 rs
= self
.dut
.p
[muxid
]
116 yield rs
.valid_i
.eq(1)
117 yield rs
.data_i
.data
.eq(op2
)
118 yield rs
.data_i
.idx
.eq(i
)
119 yield rs
.data_i
.muxid
.eq(muxid
)
120 yield rs
.data_i
.operator
.eq(1)
121 yield rs
.mask_i
.eq(1)
123 o_p_ready
= yield rs
.ready_o
126 o_p_ready
= yield rs
.ready_o
128 print ("send", muxid
, i
, hex(op2
), op2
)
129 self
.sent
[muxid
].append(i
)
131 yield rs
.valid_i
.eq(0)
132 yield rs
.mask_i
.eq(0)
133 # wait until it's received
134 while i
in self
.do
[muxid
]:
137 # wait random period of time before queueing another value
138 for i
in range(randint(0, 3)):
141 yield rs
.valid_i
.eq(0)
144 print ("send ended", muxid
)
146 ## wait random period of time before queueing another value
147 #for i in range(randint(0, 3)):
150 #send_range = randint(0, 3)
154 # send = randint(0, send_range) != 0
156 def rcv(self
, muxid
):
157 rs
= self
.dut
.p
[muxid
]
161 if False and self
.sent
[muxid
] and randint(0, 2) == 0:
162 todel
= self
.sent
[muxid
].pop()
163 print ("to delete", muxid
, self
.sent
[muxid
], todel
)
164 if todel
in self
.do
[muxid
]:
165 del self
.do
[muxid
][todel
]
166 yield rs
.stop_i
.eq(1)
167 print ("left", muxid
, self
.do
[muxid
])
168 if len(self
.do
[muxid
]) == 0:
171 #stall_range = randint(0, 3)
172 #for j in range(randint(1,10)):
173 # stall = randint(0, stall_range) != 0
174 # yield self.dut.n[0].ready_i.eq(stall)
177 n
= self
.dut
.n
[muxid
]
178 yield n
.ready_i
.eq(1)
180 yield rs
.stop_i
.eq(0) # resets cancel mask
181 o_n_valid
= yield n
.valid_o
182 i_n_ready
= yield n
.ready_i
183 if not o_n_valid
or not i_n_ready
:
186 out_muxid
= yield n
.data_o
.muxid
187 out_i
= yield n
.data_o
.idx
188 out_v
= yield n
.data_o
.data
190 print ("recv", out_muxid
, out_i
, hex(out_v
), hex(out_v
))
192 # see if this output has occurred already, delete it if it has
193 assert muxid
== out_muxid
, \
194 "out_muxid %d not correct %d" % (out_muxid
, muxid
)
195 if out_i
not in self
.sent
[muxid
]:
196 print ("cancelled/recv", muxid
, out_i
)
198 assert out_i
in self
.do
[muxid
], "out_i %d not in array %s" % \
199 (out_i
, repr(self
.do
[muxid
]))
200 assert self
.do
[muxid
][out_i
] + 1 == out_v
# check data
201 del self
.do
[muxid
][out_i
]
202 todel
= self
.sent
[muxid
].index(out_i
)
203 del self
.sent
[muxid
][todel
]
205 # check if there's any more outputs
206 if len(self
.do
[muxid
]) == 0:
209 print ("recv ended", muxid
)
212 class TestPriorityMuxPipe(PriorityCombMuxInPipe
):
213 def __init__(self
, num_rows
):
214 self
.num_rows
= num_rows
215 stage
= PassThroughStage()
216 PriorityCombMuxInPipe
.__init
__(self
, stage
,
217 p_len
=self
.num_rows
, maskwid
=1)
220 class TestMuxOutPipe(CombMuxOutPipe
):
221 def __init__(self
, num_rows
):
222 self
.num_rows
= num_rows
223 stage
= PassThroughStage()
224 CombMuxOutPipe
.__init
__(self
, stage
, n_len
=self
.num_rows
,
228 class TestInOutPipe(Elaboratable
):
229 def __init__(self
, num_rows
=4):
230 self
.num_rows
= nr
= num_rows
231 self
.inpipe
= TestPriorityMuxPipe(nr
) # fan-in (combinatorial)
232 self
.mergein
= MergeRoutePipe() # merge in feedback
233 self
.pipe1
= PassThroughPipe(nr
) # stage 1 (clock-sync)
234 self
.pipe2
= DecisionPipe(nr
) # stage 2 (clock-sync)
235 #self.pipe3 = PassThroughPipe(nr) # stage 3 (clock-sync)
236 #self.pipe4 = PassThroughPipe(nr) # stage 4 (clock-sync)
237 self
.splitback
= RouteBackPipe() # split back to mergein
238 self
.outpipe
= TestMuxOutPipe(nr
) # fan-out (combinatorial)
239 self
.fifoback
= PassThroughPipe(nr
) # temp route-back store
241 self
.p
= self
.inpipe
.p
# kinda annoying,
242 self
.n
= self
.outpipe
.n
# use pipe in/out as this class in/out
243 self
._ports
= self
.inpipe
.ports() + self
.outpipe
.ports()
245 def elaborate(self
, platform
):
247 m
.submodules
.inpipe
= self
.inpipe
248 m
.submodules
.mergein
= self
.mergein
249 m
.submodules
.pipe1
= self
.pipe1
250 m
.submodules
.pipe2
= self
.pipe2
251 #m.submodules.pipe3 = self.pipe3
252 #m.submodules.pipe4 = self.pipe4
253 m
.submodules
.splitback
= self
.splitback
254 m
.submodules
.outpipe
= self
.outpipe
255 m
.submodules
.fifoback
= self
.fifoback
257 m
.d
.comb
+= self
.inpipe
.n
.connect_to_next(self
.mergein
.p
[1])
258 m
.d
.comb
+= self
.mergein
.n
.connect_to_next(self
.pipe1
.p
)
259 m
.d
.comb
+= self
.pipe1
.connect_to_next(self
.pipe2
)
260 #m.d.comb += self.pipe2.connect_to_next(self.pipe3)
261 #m.d.comb += self.pipe3.connect_to_next(self.pipe4)
262 m
.d
.comb
+= self
.pipe2
.connect_to_next(self
.splitback
)
263 m
.d
.comb
+= self
.splitback
.n
[1].connect_to_next(self
.fifoback
.p
)
264 m
.d
.comb
+= self
.fifoback
.n
.connect_to_next(self
.mergein
.p
[0])
265 m
.d
.comb
+= self
.splitback
.n
[0].connect_to_next(self
.outpipe
.p
)
274 dut
= TestInOutPipe()
275 vl
= rtlil
.convert(dut
, ports
=dut
.ports())
276 with
open("test_inoutmux_feedback_pipe.il", "w") as f
:
281 test
= InputTest(dut
, tlen
)
282 run_simulation(dut
, [test
.rcv(0), test
.rcv(1),
283 #test.rcv(3), test.rcv(2),
284 test
.send(0), test
.send(1),
285 #test.send(3), test.send(2),
287 vcd_name
="test_inoutmux_feedback_pipe.vcd")
289 if __name__
== '__main__':