1 """ key strategic example showing how to do multi-input fan-in into a
2 multi-stage pipeline, then multi-output fanout.
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.
9 from random
import randint
10 from nmigen
.compat
.sim
import run_simulation
11 from nmigen
.cli
import verilog
, rtlil
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
21 self
.single_op
= single_op
26 self
.tlen
= len(vals
) // dut
.num_rows
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
):
35 self
.di
[muxid_in
] = {}
36 self
.do
[muxid_out
] = {}
37 self
.sent
[muxid_in
] = []
39 for i
in range(self
.tlen
):
43 if isinstance(op1
, tuple):
46 res
= self
.fpop(self
.fpkls(op1
))
47 self
.di
[muxid_in
][i
] = (op1
, )
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
56 self
.do
[muxid_out
][i
] = res
# for FP to INT
58 def send(self
, muxid
):
59 rs
= self
.dut
.p
[muxid
]
60 for i
in range(self
.tlen
):
62 op1
, = self
.di
[muxid
][i
]
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
75 o_p_ready
= yield rs
.ready_o
78 o_p_ready
= yield rs
.ready_o
81 fop1
= self
.fpkls(op1
)
83 if hasattr(res
, "bits"):
87 print("send", muxid
, i
, hex(op1
), hex(r
),
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
),
96 self
.sent
[muxid
].append(i
)
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
]:
105 # wait random period of time before queueing another value
106 for i
in range(randint(0, 3)):
109 yield rs
.valid_i
.eq(0)
112 print("send ended", muxid
)
114 # wait random period of time before queueing another value
115 # for i in range(randint(0, 3)):
118 #send_range = randint(0, 3)
119 # if send_range == 0:
122 # send = randint(0, send_range) != 0
124 def rcv(self
, muxid
):
125 rs
= self
.dut
.p
[muxid
]
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:
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)
145 n
= self
.dut
.n
[muxid
]
146 yield n
.ready_i
.eq(1)
148 if hasattr(rs
, "mask_i"):
149 yield rs
.stop_i
.eq(0) # resets cancel mask
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
:
156 out_muxid
= yield n
.data_o
.muxid
157 out_z
= yield n
.data_o
.z
159 if not self
.sent
[muxid
]:
160 print("cancelled/recv", muxid
, hex(out_z
))
163 out_i
= self
.sent
[muxid
].pop()
165 print("recv", out_muxid
, hex(out_z
), "expected",
166 hex(self
.do
[muxid
][out_i
]))
168 # see if this output has occurred already, delete it if it has
169 assert muxid
== out_muxid
, "out_muxid %d not correct %d" % \
172 assert self
.do
[muxid
][out_i
] == out_z
174 print("senddel", muxid
, out_i
, self
.sent
[muxid
])
175 del self
.do
[muxid
][out_i
]
177 # check if there's any more outputs
178 if len(self
.do
[muxid
]) == 0:
181 print("recv ended", muxid
)
184 def create_random(num_rows
, width
, single_op
=False, n_vals
=10):
186 for muxid
in range(num_rows
):
187 for i
in range(n_vals
):
189 op1
= randint(0, (1 << width
)-1)
205 #op1 = 0x9885020648d8c0e8
257 # f2int unsigned (fp64 to ui16)
258 #op1 = 0x40e6f5bc4d88b0cc
260 # f2int signed (fp64 to i16)
261 #op1 = 0xff292cf09f159ddb
262 #op1 = 0x5880e09f7cb716a1
264 # f2int signed (fp64 to i32)
265 #op1 = 0x5beb66ffc69a9a64
266 #op1 = 0xd4cdd178a1f2cdec
270 op1
= randint(0, (1 << width
)-1)
271 op2
= randint(0, (1 << width
)-1)
272 # op1 = 0x3F800000 # 1.0f32
273 # op2 = 0x40000000 # 2.0f32
283 vals
.append((op1
, op2
,))
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
293 n_to_repeat
= len(vals
) % num_rows
294 #print ("repeat", vals)
295 return vals
+ [vals
[-1]] * n_to_repeat
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
)
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
:
318 vals
= create_random(dut
.num_rows
, width
, single_op
, n_vals
)
320 test
= MuxInOut(dut
, width
, fpkls
, fpop
, vals
, single_op
, opcode
=opcode
)
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
)