add mask cancellation to FPDIV and to fpmux unit test
[ieee754fpu.git] / src / ieee754 / fpcommon / test / fpmux.py
1 """ key strategic example showing how to do multi-input fan-in into a
2 multi-stage pipeline, then multi-output fanout.
3
4 the multiplex ID from the fan-in is passed in to the pipeline, preserved,
5 and used as a routing ID on the fanout.
6 """
7
8 from random import randint
9 from nmigen.compat.sim import run_simulation
10 from nmigen.cli import verilog, rtlil
11
12
13 class MuxInOut:
14 def __init__(self, dut, width, fpkls, fpop, vals, single_op, opcode,
15 cancel=False):
16 self.cancel = cancel # allow (test) cancellation
17 self.dut = dut
18 self.fpkls = fpkls
19 self.fpop = fpop
20 self.single_op = single_op
21 self.opcode = opcode
22 self.di = {}
23 self.do = {}
24 self.sent = {}
25 self.tlen = len(vals) // dut.num_rows
26 self.width = width
27 for muxid in range(dut.num_rows):
28 self.di[muxid] = {}
29 self.do[muxid] = {}
30 self.sent[muxid] = []
31
32 for i in range(self.tlen):
33 if self.single_op:
34 #print ("vals", vals)
35 op1 = vals.pop(0)
36 if isinstance(op1, tuple):
37 assert len(op1) == 1
38 op1 = op1[0]
39 res = self.fpop(self.fpkls(op1))
40 self.di[muxid][i] = (op1, )
41 else:
42 (op1, op2, ) = vals.pop(0)
43 #print ("test", hex(op1), hex(op2))
44 res = self.fpop(self.fpkls(op1), self.fpkls(op2))
45 self.di[muxid][i] = (op1, op2)
46 if hasattr(res, "bits"):
47 self.do[muxid][i] = res.bits
48 else:
49 self.do[muxid][i] = res # for FP to INT
50
51 def send(self, muxid):
52 rs = self.dut.p[muxid]
53 for i in range(self.tlen):
54 if self.single_op:
55 op1, = self.di[muxid][i]
56 else:
57 op1, op2 = self.di[muxid][i]
58 yield rs.valid_i.eq(1)
59 yield rs.data_i.a.eq(op1)
60 if self.opcode is not None:
61 yield rs.data_i.ctx.op.eq(self.opcode)
62 if not self.single_op:
63 yield rs.data_i.b.eq(op2)
64 yield rs.data_i.muxid.eq(muxid)
65 if hasattr(rs, "mask_i"):
66 yield rs.mask_i.eq(1) # TEMPORARY HACK
67 yield
68 o_p_ready = yield rs.ready_o
69 while not o_p_ready:
70 yield
71 o_p_ready = yield rs.ready_o
72
73 if self.single_op:
74 fop1 = self.fpkls(op1)
75 res = self.fpop(fop1)
76 if hasattr(res, "bits"):
77 r = res.bits
78 else:
79 r = res
80 print("send", muxid, i, hex(op1), hex(r),
81 fop1, res)
82 else:
83 fop1 = self.fpkls(op1)
84 fop2 = self.fpkls(op2)
85 res = self.fpop(fop1, fop2)
86 print("send", muxid, i, hex(op1), hex(op2), hex(res.bits),
87 fop1, fop2, res)
88
89 self.sent[muxid].append(i)
90
91 yield rs.valid_i.eq(0)
92 if hasattr(rs, "mask_i"):
93 yield rs.mask_i.eq(0) # TEMPORARY HACK
94 # wait until it's received
95 while i in self.sent[muxid]:
96 yield
97
98 # wait random period of time before queueing another value
99 for i in range(randint(0, 3)):
100 yield
101
102 yield rs.valid_i.eq(0)
103 yield
104
105 print("send ended", muxid)
106
107 ## wait random period of time before queueing another value
108 #for i in range(randint(0, 3)):
109 # yield
110
111 #send_range = randint(0, 3)
112 #if send_range == 0:
113 # send = True
114 #else:
115 # send = randint(0, send_range) != 0
116
117 def rcv(self, muxid):
118 rs = self.dut.p[muxid]
119 while True:
120
121 # check cancellation
122 cancel = self.cancel and (randint(0, 2) == 0)
123 if hasattr(rs, "mask_i") and len(self.sent[muxid]) > 0 and cancel:
124 todel = self.sent[muxid].pop()
125 print ("to delete", muxid, self.sent[muxid], todel)
126 if todel in self.do[muxid]:
127 del self.do[muxid][todel]
128 yield rs.stop_i.eq(1)
129 print ("left", muxid, self.do[muxid])
130 if len(self.do[muxid]) == 0:
131 break
132
133 #stall_range = randint(0, 3)
134 #for j in range(randint(1,10)):
135 # stall = randint(0, stall_range) != 0
136 # yield self.dut.n[0].ready_i.eq(stall)
137 # yield
138 n = self.dut.n[muxid]
139 yield n.ready_i.eq(1)
140 yield
141 if hasattr(rs, "mask_i"):
142 yield rs.stop_i.eq(0) # resets cancel mask
143
144 o_n_valid = yield n.valid_o
145 i_n_ready = yield n.ready_i
146 if not o_n_valid or not i_n_ready:
147 continue
148
149 out_muxid = yield n.data_o.muxid
150 out_z = yield n.data_o.z
151
152 if not self.sent[muxid]:
153 print ("cancelled/recv", muxid, hex(out_z))
154 continue
155
156 out_i = self.sent[muxid].pop()
157
158 print("recv", out_muxid, hex(out_z), "expected",
159 hex(self.do[muxid][out_i]))
160
161 # see if this output has occurred already, delete it if it has
162 assert muxid == out_muxid, "out_muxid %d not correct %d" % \
163 (out_muxid, muxid)
164
165 assert self.do[muxid][out_i] == out_z
166
167 print ("senddel", muxid, out_i, self.sent[muxid])
168 del self.do[muxid][out_i]
169
170 # check if there's any more outputs
171 if len(self.do[muxid]) == 0:
172 break
173
174 print("recv ended", muxid)
175
176
177 def create_random(num_rows, width, single_op=False, n_vals=10):
178 vals = []
179 for muxid in range(num_rows):
180 for i in range(n_vals):
181 if single_op:
182 op1 = randint(0, (1 << width)-1)
183 #op1 = 0x40900000
184 #op1 = 0x94607b66
185 #op1 = 0x889cd8c
186 #op1 = 0xe98646d7
187 #op1 = 0x3340f2a7
188 #op1 = 0xfff13f05
189 #op1 = 0x453eb000
190 #op1 = 0x3a05de50
191 #op1 = 0xc27ff989
192 #op1 = 0x41689000
193 #op1 = 0xbbc0edec
194 #op1 = 0x2EDBE6FF
195 #op1 = 0x358637BD
196 #op1 = 0x3340f2a7
197 #op1 = 0x33D6BF95
198 #op1 = 0x9885020648d8c0e8
199 #op1 = 0xc26b
200 #op1 = 3
201
202 #op1 = 0x3a66
203 #op1 = 0x5299
204 #op1 = 0xe0eb
205 #op1 = 0x3954
206 #op1 = 0x4dea
207 #op1 = 0x65eb
208
209 #op1 = 0x1841
210
211 # FSQRT
212 #op1 = 0x3449f9a9
213 #op1 = 0x1ba94baa
214
215 #if i % 2:
216 # op1 = 0x0001
217 #else:
218 # op1 = 0x3C00
219
220 # FRSQRT
221 #op1 = 0x3686
222 #op1 = 0x4400
223 #op1 = 0x4800
224 #op1 = 0x48f0
225 #op1 = 0x429
226 #op1 = 0x2631
227 #op1 = 0x3001
228 #op1 = 0x3f2ad8eb
229
230 # f2int
231 #op1 = 0x4dc0
232 #op1 = 0x3b81
233 #op1 = 0xfcb6
234 #op1 = 0x4f8d77b3
235
236 # f2int signed
237 #op1 = 0xc913
238 #op1 = 0x7b97
239 #op1 = 0xaae2
240 #op1 = 0x7fca
241
242 # FCLASS
243 #op1 = 0x87d1
244 #op1 = 0x75e
245 #op1 = 0x7f8c
246 #op1 = 0x7c57
247 #op1 = 0xfea8
248 #op1 = 0xfd57
249
250 # f2int unsigned (fp64 to ui16)
251 #op1 = 0x40e6f5bc4d88b0cc
252
253 # f2int signed (fp64 to i16)
254 #op1 = 0xff292cf09f159ddb
255 #op1 = 0x5880e09f7cb716a1
256
257 # f2int signed (fp64 to i32)
258 #op1 = 0x5beb66ffc69a9a64
259 #op1 = 0xd4cdd178a1f2cdec
260
261 vals.append((op1,))
262 else:
263 op1 = randint(0, (1 << width)-1)
264 op2 = randint(0, (1 << width)-1)
265 # op1 = 0x3F800000 # 1.0f32
266 # op2 = 0x40000000 # 2.0f32
267
268 #op2 = 0x4000
269 #op1 = 0x3c50
270 #op2 = 0x3e00
271 #op2 = 0xb371
272 #op1 = 0x4400
273 #op1 = 0x656c
274 #op1 = 0x738c
275
276 vals.append((op1, op2,))
277 return vals
278
279
280 def repeat(num_rows, vals):
281 """ bit of a hack: repeats the last value to create a list
282 that will be accepted by the muxer, all mux lists to be
283 of equal length
284 """
285 vals = list(vals)
286 n_to_repeat = len(vals) % num_rows
287 #print ("repeat", vals)
288 return vals + [vals[-1]] * n_to_repeat
289
290
291 def pipe_cornercases_repeat(dut, name, mod, fmod, width, fn, cc, fpfn, count,
292 single_op=False, opcode=None):
293 for i, fixed_num in enumerate(cc(mod)):
294 vals = fn(mod, fixed_num, count, width, single_op)
295 vals = repeat(dut.num_rows, vals)
296 #print ("repeat", i, fn, single_op, list(vals))
297 fmt = "test_pipe_fp%d_%s_cornercases_%d"
298 runfp(dut, width, fmt % (width, name, i),
299 fmod, fpfn, vals=vals, single_op=single_op, opcode=opcode)
300
301
302 def runfp(dut, width, name, fpkls, fpop, single_op=False, n_vals=10,
303 vals=None, opcode=None, cancel=False):
304 vl = rtlil.convert(dut, ports=dut.ports())
305 with open("%s.il" % name, "w") as f:
306 f.write(vl)
307
308 if vals is None:
309 vals = create_random(dut.num_rows, width, single_op, n_vals)
310
311 test = MuxInOut(dut, width, fpkls, fpop, vals, single_op, opcode=opcode)
312 fns = []
313 for i in range(dut.num_rows):
314 fns.append(test.rcv(i))
315 fns.append(test.send(i))
316 run_simulation(dut, fns, vcd_name="%s.vcd" % name)