debugging feedback pipe
[ieee754fpu.git] / src / nmutil / test / test_inout_feedback_pipe.py
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
3 and cancellation
4
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.
7 """
8
9 from random import randint
10 from math import log
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
14
15 from nmutil.multipipe import CombMultiOutPipeline, CombMuxOutPipe
16 from nmutil.multipipe import PriorityCombMuxInPipe
17 from nmutil.singlepipe import MaskCancellable, RecordObject, Object
18
19
20 class PassData(Object):
21 def __init__(self):
22 Object.__init__(self)
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
28
29
30 class PassThroughStage:
31 def __init__(self):
32 self.o = self.ospec()
33 def ispec(self):
34 return PassData()
35 def ospec(self):
36 return self.ispec() # same as ospec
37 def _setup(self, m, i):
38 comb = m.d.comb
39 #comb += self.o.eq(i)
40 def process(self, i):
41 return i
42
43
44 class SplitRouteStage:
45 def __init__(self):
46 self.o = self.ospec()
47
48 def ispec(self):
49 return PassData()
50 def ospec(self):
51 return PassData()
52
53 def setup(self, m, i):
54 comb = m.d.comb
55 comb += self.o.eq(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
60 with m.Else():
61 comb += self.o.routeid.eq(0) # selects 2nd output in CombMuxOutPipe
62
63 def process(self, i):
64 return self.o
65
66
67 class DecisionPipe(MaskCancellable):
68 def __init__(self, maskwid):
69 stage = SplitRouteStage()
70 MaskCancellable.__init__(self, stage, maskwid)
71
72 class RouteBackPipe(CombMuxOutPipe):
73 """ routes data back to start of pipeline
74 """
75 def __init__(self):
76 stage = PassThroughStage()
77 CombMuxOutPipe.__init__(self, stage, n_len=2,
78 maskwid=4, muxidname="routeid",
79 routemask=True)
80
81
82 class MergeRoutePipe(PriorityCombMuxInPipe):
83 """ merges data coming from end of pipe (with operator now == 1)
84 """
85 def __init__(self):
86 stage = PassThroughStage()
87 PriorityCombMuxInPipe.__init__(self, stage, p_len=2, maskwid=4,
88 routemask=True)
89
90
91
92 class PassThroughPipe(MaskCancellable):
93 def __init__(self, maskwid):
94 MaskCancellable.__init__(self, PassThroughStage(), maskwid)
95
96
97 class InputTest:
98 def __init__(self, dut, tlen):
99 self.dut = dut
100 self.di = {}
101 self.do = {}
102 self.sent = {}
103 self.tlen = tlen
104 for muxid in range(dut.num_rows):
105 self.di[muxid] = {}
106 self.do[muxid] = {}
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]
111
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)
122 yield
123 o_p_ready = yield rs.ready_o
124 while not o_p_ready:
125 yield
126 o_p_ready = yield rs.ready_o
127
128 print ("send", muxid, i, hex(op2), op2)
129 self.sent[muxid].append(i)
130
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]:
135 yield
136
137 # wait random period of time before queueing another value
138 for i in range(randint(0, 3)):
139 yield
140
141 yield rs.valid_i.eq(0)
142 yield
143
144 print ("send ended", muxid)
145
146 ## wait random period of time before queueing another value
147 #for i in range(randint(0, 3)):
148 # yield
149
150 #send_range = randint(0, 3)
151 #if send_range == 0:
152 # send = True
153 #else:
154 # send = randint(0, send_range) != 0
155
156 def rcv(self, muxid):
157 rs = self.dut.p[muxid]
158 while True:
159
160 # check cancellation
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:
169 break
170
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)
175 # yield
176
177 n = self.dut.n[muxid]
178 yield n.ready_i.eq(1)
179 yield
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:
184 continue
185
186 out_muxid = yield n.data_o.muxid
187 out_i = yield n.data_o.idx
188 out_v = yield n.data_o.data
189
190 print ("recv", out_muxid, out_i, hex(out_v), hex(out_v))
191
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)
197 continue
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]
204
205 # check if there's any more outputs
206 if len(self.do[muxid]) == 0:
207 break
208
209 print ("recv ended", muxid)
210
211
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)
218
219
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,
225 maskwid=1)
226
227
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
240
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()
244
245 def elaborate(self, platform):
246 m = Module()
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
256
257 m.d.comb += self.inpipe.n.connect_to_next(self.mergein.p[0])
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[1])
265 m.d.comb += self.splitback.n[0].connect_to_next(self.outpipe.p)
266
267 return m
268
269 def ports(self):
270 return self._ports
271
272
273 def test1():
274 dut = TestInOutPipe()
275 vl = rtlil.convert(dut, ports=dut.ports())
276 with open("test_inoutmux_feedback_pipe.il", "w") as f:
277 f.write(vl)
278
279 tlen = 5
280
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),
286 ],
287 vcd_name="test_inoutmux_feedback_pipe.vcd")
288
289
290 if __name__ == '__main__':
291 #from cProfile import Profile
292 #p = Profile()
293 #p.enable()
294 test1()
295 #p.disable()
296 #p.print_stats()