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