add experimental feedback pipe test
[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
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 self.ispec() # same as ospec
52
53 def setup(self, m, i):
54 comb = m.d.comb
55 comb += self.o.eq(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
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 RouteBackPipe(CombMuxOutPipe):
68 """ routes data back to start of pipeline
69 """
70 def __init__(self):
71 self.num_rows = 2
72 stage = SplitRouteStage()
73 CombMuxOutPipe.__init__(self, stage, n_len=2,
74 maskwid=4, muxidname="routeid",
75 routemask=True)
76
77
78 class MergeRoutePipe(PriorityCombMuxInPipe):
79 """ merges data coming from end of pipe (with operator now == 1)
80 """
81 def __init__(self):
82 self.num_rows = 2
83 stage = PassThroughStage()
84 PriorityCombMuxInPipe.__init__(self, stage, p_len=2, maskwid=4,
85 routemask=True)
86
87
88
89 class PassThroughPipe(MaskCancellable):
90 def __init__(self, maskwid):
91 MaskCancellable.__init__(self, PassThroughStage(), maskwid)
92
93
94 class InputTest:
95 def __init__(self, dut, tlen):
96 self.dut = dut
97 self.di = {}
98 self.do = {}
99 self.sent = {}
100 self.tlen = tlen
101 for muxid in range(dut.num_rows):
102 self.di[muxid] = {}
103 self.do[muxid] = {}
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]
108
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)
118 yield
119 o_p_ready = yield rs.ready_o
120 while not o_p_ready:
121 yield
122 o_p_ready = yield rs.ready_o
123
124 print ("send", muxid, i, hex(op2), op2)
125 self.sent[muxid].append(i)
126
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]:
131 yield
132
133 # wait random period of time before queueing another value
134 for i in range(randint(0, 3)):
135 yield
136
137 yield rs.valid_i.eq(0)
138 yield
139
140 print ("send ended", muxid)
141
142 ## wait random period of time before queueing another value
143 #for i in range(randint(0, 3)):
144 # yield
145
146 #send_range = randint(0, 3)
147 #if send_range == 0:
148 # send = True
149 #else:
150 # send = randint(0, send_range) != 0
151
152 def rcv(self, muxid):
153 rs = self.dut.p[muxid]
154 while True:
155
156 # check cancellation
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:
165 break
166
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)
171 yield
172
173 n = self.dut.n[muxid]
174 yield n.ready_i.eq(1)
175 yield
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:
180 continue
181
182 out_muxid = yield n.data_o.muxid
183 out_i = yield n.data_o.idx
184 out_v = yield n.data_o.data
185
186 print ("recv", out_muxid, out_i, hex(out_v), out_v)
187
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)
193 continue
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]
200
201 # check if there's any more outputs
202 if len(self.do[muxid]) == 0:
203 break
204
205 print ("recv ended", muxid)
206
207
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)
214
215
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,
221 maskwid=1)
222
223
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)
235
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()
239
240 def elaborate(self, platform):
241 m = Module()
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
250
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)
259
260 return m
261
262 def ports(self):
263 return self._ports
264
265
266 def test1():
267 dut = TestInOutPipe()
268 vl = rtlil.convert(dut, ports=dut.ports())
269 with open("test_inoutmux_feedback_pipe.il", "w") as f:
270 f.write(vl)
271
272 tlen = 20
273
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),
279 ],
280 vcd_name="test_inoutmux_feedback_pipe.vcd")
281
282 if __name__ == '__main__':
283 test1()