fu/mul/test/test_pipe_caller.py test case_all_rb_close_to_ov change rb
[soc.git] / src / soc / fu / mul / test / test_pipe_caller.py
1 from nmigen import Module, Signal
2 from nmigen.sim.pysim import Simulator, Delay, Settle
3 from nmigen.cli import rtlil
4 import unittest
5 from soc.decoder.isa.caller import ISACaller, special_sprs
6 from soc.decoder.power_decoder import (create_pdecode)
7 from soc.decoder.power_decoder2 import (PowerDecode2)
8 from soc.decoder.power_enums import (XER_bits, Function, MicrOp, CryIn)
9 from soc.decoder.selectable_int import SelectableInt
10 from soc.simulator.program import Program
11 from soc.decoder.isa.all import ISA
12 from soc.config.endian import bigendian
13
14 from soc.fu.test.common import (TestAccumulatorBase, TestCase, ALUHelpers)
15 from soc.fu.mul.pipeline import MulBasePipe
16 from soc.fu.mul.pipe_data import MulPipeSpec
17 import random
18
19
20 def get_cu_inputs(dec2, sim):
21 """naming (res) must conform to MulFunctionUnit input regspec
22 """
23 res = {}
24
25 yield from ALUHelpers.get_sim_int_ra(res, sim, dec2) # RA
26 yield from ALUHelpers.get_sim_int_rb(res, sim, dec2) # RB
27 yield from ALUHelpers.get_sim_xer_so(res, sim, dec2) # XER.so
28
29 print("alu get_cu_inputs", res)
30
31 return res
32
33
34 def set_alu_inputs(alu, dec2, sim):
35 # TODO: see https://bugs.libre-soc.org/show_bug.cgi?id=305#c43
36 # detect the immediate here (with m.If(self.i.ctx.op.imm_data.imm_ok))
37 # and place it into data_i.b
38
39 inp = yield from get_cu_inputs(dec2, sim)
40 print("set alu inputs", inp)
41 yield from ALUHelpers.set_int_ra(alu, dec2, inp)
42 yield from ALUHelpers.set_int_rb(alu, dec2, inp)
43
44 yield from ALUHelpers.set_xer_so(alu, dec2, inp)
45
46
47 # This test bench is a bit different than is usual. Initially when I
48 # was writing it, I had all of the tests call a function to create a
49 # device under test and simulator, initialize the dut, run the
50 # simulation for ~2 cycles, and assert that the dut output what it
51 # should have. However, this was really slow, since it needed to
52 # create and tear down the dut and simulator for every test case.
53
54 # Now, instead of doing that, every test case in MulTestCase puts some
55 # data into the test_data list below, describing the instructions to
56 # be tested and the initial state. Once all the tests have been run,
57 # test_data gets passed to TestRunner which then sets up the DUT and
58 # simulator once, runs all the data through it, and asserts that the
59 # results match the pseudocode sim at every cycle.
60
61 # By doing this, I've reduced the time it takes to run the test suite
62 # massively. Before, it took around 1 minute on my computer, now it
63 # takes around 3 seconds
64
65
66 class MulTestCase(TestAccumulatorBase):
67
68 def case_0_mullw(self):
69 lst = [f"mullw 3, 1, 2"]
70 initial_regs = [0] * 32
71 #initial_regs[1] = 0xffffffffffffffff
72 #initial_regs[2] = 0xffffffffffffffff
73 initial_regs[1] = 0x2ffffffff
74 initial_regs[2] = 0x2
75 self.add_case(Program(lst, bigendian), initial_regs)
76
77 def case_1_mullwo_(self):
78 lst = [f"mullwo. 3, 1, 2"]
79 initial_regs = [0] * 32
80 initial_regs[1] = 0x3b34b06f
81 initial_regs[2] = 0xfdeba998
82 self.add_case(Program(lst, bigendian), initial_regs)
83
84 def case_2_mullwo(self):
85 lst = [f"mullwo 3, 1, 2"]
86 initial_regs = [0] * 32
87 initial_regs[1] = 0xffffffffffffa988 # -5678
88 initial_regs[2] = 0xffffffffffffedcc # -1234
89 self.add_case(Program(lst, bigendian), initial_regs)
90
91 def case_3_mullw(self):
92 lst = ["mullw 3, 1, 2",
93 "mullw 3, 1, 2"]
94 initial_regs = [0] * 32
95 initial_regs[1] = 0x6
96 initial_regs[2] = 0xe
97 self.add_case(Program(lst, bigendian), initial_regs)
98
99 def case_4_mullw_rand(self):
100 for i in range(40):
101 lst = ["mullw 3, 1, 2"]
102 initial_regs = [0] * 32
103 initial_regs[1] = random.randint(0, (1 << 64)-1)
104 initial_regs[2] = random.randint(0, (1 << 64)-1)
105 self.add_case(Program(lst, bigendian), initial_regs)
106
107 def case_4_mullw_nonrand(self):
108 for i in range(40):
109 lst = ["mullw 3, 1, 2"]
110 initial_regs = [0] * 32
111 initial_regs[1] = i+1
112 initial_regs[2] = i+20
113 self.add_case(Program(lst, bigendian), initial_regs)
114
115 def case_mulhw__regression_1(self):
116 lst = ["mulhw. 3, 1, 2"
117 ]
118 initial_regs = [0] * 32
119 initial_regs[1] = 0x7745b36eca6646fa
120 initial_regs[2] = 0x47dfba3a63834ba2
121 self.add_case(Program(lst, bigendian), initial_regs)
122
123 def case_rand_mul_lh(self):
124 insns = ["mulhw", "mulhw.", "mulhwu", "mulhwu."]
125 for i in range(40):
126 choice = random.choice(insns)
127 lst = [f"{choice} 3, 1, 2"]
128 initial_regs = [0] * 32
129 initial_regs[1] = random.randint(0, (1 << 64)-1)
130 initial_regs[2] = random.randint(0, (1 << 64)-1)
131 self.add_case(Program(lst, bigendian), initial_regs)
132
133 def case_rand_mullw(self):
134 insns = ["mullw", "mullw.", "mullwo", "mullwo."]
135 for i in range(40):
136 choice = random.choice(insns)
137 lst = [f"{choice} 3, 1, 2"]
138 initial_regs = [0] * 32
139 initial_regs[1] = random.randint(0, (1 << 64)-1)
140 initial_regs[2] = random.randint(0, (1 << 64)-1)
141 self.add_case(Program(lst, bigendian), initial_regs)
142
143 def case_rand_mulld(self):
144 insns = ["mulld", "mulld.", "mulldo", "mulldo."]
145 for i in range(40):
146 choice = random.choice(insns)
147 lst = [f"{choice} 3, 1, 2"]
148 initial_regs = [0] * 32
149 initial_regs[1] = random.randint(0, (1 << 64)-1)
150 initial_regs[2] = random.randint(0, (1 << 64)-1)
151 self.add_case(Program(lst, bigendian), initial_regs)
152
153 def case_rand_mulhd(self):
154 insns = ["mulhd", "mulhd."]
155 for i in range(40):
156 choice = random.choice(insns)
157 lst = [f"{choice} 3, 1, 2"]
158 initial_regs = [0] * 32
159 initial_regs[1] = random.randint(0, (1 << 64)-1)
160 initial_regs[2] = random.randint(0, (1 << 64)-1)
161 self.add_case(Program(lst, bigendian), initial_regs)
162
163 def case_all(self):
164 instrs = ["mulhw",
165 "mulhw.", "mullw",
166 "mullw.", "mullwo",
167 "mullwo.", "mulhwu",
168 "mulhwu.", "mulld",
169 "mulld.", "mulldo",
170 "mulldo.", "mulhd",
171 "mulhd.", "mulhdu",
172 "mulhdu."]
173
174 test_values = [
175 0x0,
176 0x1,
177 0x2,
178 0xFFFF_FFFF_FFFF_FFFF,
179 0xFFFF_FFFF_FFFF_FFFE,
180 0x7FFF_FFFF_FFFF_FFFF,
181 0x8000_0000_0000_0000,
182 0x1234_5678_0000_0000,
183 0x1234_5678_8000_0000,
184 0x1234_5678_FFFF_FFFF,
185 0x1234_5678_7FFF_FFFF,
186 0xffffffff,
187 0x7fffffff,
188 0x80000000,
189 0xfffffffe,
190 0xfffffffd
191 ]
192
193 for instr in instrs:
194 l = [f"{instr} 3, 1, 2"]
195 for ra in test_values:
196 for rb in test_values:
197 initial_regs = [0] * 32
198 initial_regs[1] = ra
199 initial_regs[2] = rb
200 # use "with" so as to close the files used
201 with Program(l, bigendian) as prog:
202 self.add_case(prog, initial_regs)
203
204 def case_all_rb_randint(self):
205 instrs = ["mulhw",
206 "mulhw.", "mullw",
207 "mullw.", "mullwo",
208 "mullwo.", "mulhwu",
209 "mulhwu.", "mulld",
210 "mulld.", "mulldo",
211 "mulldo.", "mulhd",
212 "mulhd.", "mulhdu",
213 "mulhdu."]
214
215 test_values = [
216 0x0,
217 0x1,
218 0x2,
219 0xFFFF_FFFF_FFFF_FFFF,
220 0xFFFF_FFFF_FFFF_FFFE,
221 0x7FFF_FFFF_FFFF_FFFF,
222 0x8000_0000_0000_0000,
223 0x1234_5678_0000_0000,
224 0x1234_5678_8000_0000,
225 0x1234_5678_FFFF_FFFF,
226 0x1234_5678_7FFF_FFFF,
227 0xffffffff,
228 0x7fffffff,
229 0x80000000,
230 0xfffffffe,
231 0xfffffffd
232 ]
233
234 for instr in instrs:
235 l = [f"{instr} 3, 1, 2"]
236 for ra in test_values:
237 initial_regs = [0] * 32
238 initial_regs[1] = ra
239 initial_regs[2] = random.randint(0, (1 << 64)-1)
240 # use "with" so as to close the files used
241 with Program(l, bigendian) as prog:
242 self.add_case(prog, initial_regs)
243
244 def case_all_rb_close_to_ov(self):
245 instrs = ["mulhw",
246 "mulhw.", "mullw",
247 "mullw.", "mullwo",
248 "mullwo.", "mulhwu",
249 "mulhwu.", "mulld",
250 "mulld.", "mulldo",
251 "mulldo.", "mulhd",
252 "mulhd.", "mulhdu",
253 "mulhdu."]
254
255 test_values = [
256 0x0,
257 0x1,
258 0x2,
259 0xFFFF_FFFF_FFFF_FFFF,
260 0xFFFF_FFFF_FFFF_FFFE,
261 0x7FFF_FFFF_FFFF_FFFF,
262 0x8000_0000_0000_0000,
263 0x1234_5678_0000_0000,
264 0x1234_5678_8000_0000,
265 0x1234_5678_FFFF_FFFF,
266 0x1234_5678_7FFF_FFFF,
267 0xffffffff,
268 0x7fffffff,
269 0x80000000,
270 0xfffffffe,
271 0xfffffffd
272 ]
273
274 for instr in instrs:
275 for i in range(20):
276 x = 0x7fffffff + random.randint((-1 << 31), (1 << 31) - 1)
277 ra = random.randint(0, (1 << 32)-1)
278 rb = x // ra
279
280 l = [f"{instr} 3, 1, 2"]
281 initial_regs = [0] * 32
282 initial_regs[1] = ra
283 initial_regs[2] = rb
284 # use "with" so as to close the files used
285 with Program(l, bigendian) as prog:
286 self.add_case(prog, initial_regs)
287
288 def case_mulli(self):
289
290 imm_values = [-32768, -32767, -32766, -2, -1, 0, 1, 2, 32766, 32767]
291
292 ra_values = [
293 0x0,
294 0x1,
295 0x2,
296 0xFFFF_FFFF_FFFF_FFFF,
297 0xFFFF_FFFF_FFFF_FFFE,
298 0x7FFF_FFFF_FFFF_FFFF,
299 0x8000_0000_0000_0000,
300 0x1234_5678_0000_0000,
301 0x1234_5678_8000_0000,
302 0x1234_5678_FFFF_FFFF,
303 0x1234_5678_7FFF_FFFF,
304 0xffffffff,
305 0x7fffffff,
306 0x80000000,
307 0xfffffffe,
308 0xfffffffd
309 ]
310
311 for i in range(20):
312 imm_values.append(random.randint(-1 << 15, (1 << 15) - 1))
313
314 for i in range(14):
315 ra_values.append(random.randint(0, (1 << 64) - 1))
316
317 for ra in ra_values:
318 for imm in imm_values:
319 l = [f"mulli 0, 1, {imm}"]
320 initial_regs = [0] * 32
321 initial_regs[1] = ra
322 # use "with" so as to close the files used
323 with Program(l, bigendian) as prog:
324 self.add_case(prog, initial_regs)
325
326 # TODO add test case for these 3 operand cases (madd
327 # needs to be implemented)
328 # "maddhd","maddhdu","maddld"
329
330 def case_ilang(self):
331 pspec = MulPipeSpec(id_wid=2)
332 alu = MulBasePipe(pspec)
333 vl = rtlil.convert(alu, ports=alu.ports())
334 with open("mul_pipeline.il", "w") as f:
335 f.write(vl)
336
337
338 class TestRunner(unittest.TestCase):
339 def __init__(self, test_data):
340 super().__init__("run_all")
341 self.test_data = test_data
342
343 def run_all(self):
344 m = Module()
345 comb = m.d.comb
346 instruction = Signal(32)
347
348 pdecode = create_pdecode()
349
350 m.submodules.pdecode2 = pdecode2 = PowerDecode2(pdecode)
351
352 pspec = MulPipeSpec(id_wid=2)
353 m.submodules.alu = alu = MulBasePipe(pspec)
354
355 comb += alu.p.data_i.ctx.op.eq_from_execute1(pdecode2.e)
356 comb += alu.n.ready_i.eq(1)
357 comb += pdecode2.dec.raw_opcode_in.eq(instruction)
358 sim = Simulator(m)
359
360 sim.add_clock(1e-6)
361
362 def process():
363 for test in self.test_data:
364 print(test.name)
365 program = test.program
366 self.subTest(test.name)
367 sim = ISA(pdecode2, test.regs, test.sprs, test.cr,
368 test.mem, test.msr,
369 bigendian=bigendian)
370 gen = program.generate_instructions()
371 instructions = list(zip(gen, program.assembly.splitlines()))
372 yield Settle()
373
374 index = sim.pc.CIA.value//4
375 while index < len(instructions):
376 ins, code = instructions[index]
377
378 print("instruction: 0x{:X}".format(ins & 0xffffffff))
379 print(code)
380 if 'XER' in sim.spr:
381 so = 1 if sim.spr['XER'][XER_bits['SO']] else 0
382 ov = 1 if sim.spr['XER'][XER_bits['OV']] else 0
383 ov32 = 1 if sim.spr['XER'][XER_bits['OV32']] else 0
384 print("before: so/ov/32", so, ov, ov32)
385
386 # ask the decoder to decode this binary data (endian'd)
387 yield pdecode2.dec.bigendian.eq(bigendian) # little / big?
388 yield instruction.eq(ins) # raw binary instr.
389 yield Settle()
390 fn_unit = yield pdecode2.e.do.fn_unit
391 self.assertEqual(fn_unit, Function.MUL.value)
392 yield from set_alu_inputs(alu, pdecode2, sim)
393
394 # set valid for one cycle, propagate through pipeline...
395 yield alu.p.valid_i.eq(1)
396 yield
397 yield alu.p.valid_i.eq(0)
398
399 opname = code.split(' ')[0]
400 yield from sim.call(opname)
401 index = sim.pc.CIA.value//4
402
403 # ...wait for valid to pop out the end
404 vld = yield alu.n.valid_o
405 while not vld:
406 yield
407 vld = yield alu.n.valid_o
408 yield
409
410 yield from self.check_alu_outputs(alu, pdecode2, sim, code)
411 yield Settle()
412
413 sim.add_sync_process(process)
414 with sim.write_vcd("mul_simulator.vcd", "mul_simulator.gtkw",
415 traces=[]):
416 sim.run()
417
418 def check_alu_outputs(self, alu, dec2, sim, code):
419
420 rc = yield dec2.e.do.rc.data
421 cridx_ok = yield dec2.e.write_cr.ok
422 cridx = yield dec2.e.write_cr.data
423
424 print("check extra output", repr(code), cridx_ok, cridx)
425 if rc:
426 self.assertEqual(cridx, 0, code)
427
428 oe = yield dec2.e.do.oe.oe
429 oe_ok = yield dec2.e.do.oe.ok
430 if not oe or not oe_ok:
431 # if OE not enabled, XER SO and OV must correspondingly be false
432 so_ok = yield alu.n.data_o.xer_so.ok
433 ov_ok = yield alu.n.data_o.xer_ov.ok
434 self.assertEqual(so_ok, False, code)
435 self.assertEqual(ov_ok, False, code)
436
437 sim_o = {}
438 res = {}
439
440 yield from ALUHelpers.get_cr_a(res, alu, dec2)
441 yield from ALUHelpers.get_xer_ov(res, alu, dec2)
442 yield from ALUHelpers.get_int_o(res, alu, dec2)
443 yield from ALUHelpers.get_xer_so(res, alu, dec2)
444
445 yield from ALUHelpers.get_sim_int_o(sim_o, sim, dec2)
446 yield from ALUHelpers.get_wr_sim_cr_a(sim_o, sim, dec2)
447 yield from ALUHelpers.get_sim_xer_ov(sim_o, sim, dec2)
448 yield from ALUHelpers.get_sim_xer_so(sim_o, sim, dec2)
449
450 ALUHelpers.check_int_o(self, res, sim_o, code)
451 ALUHelpers.check_xer_ov(self, res, sim_o, code)
452 ALUHelpers.check_xer_so(self, res, sim_o, code)
453 ALUHelpers.check_cr_a(self, res, sim_o, "CR%d %s" % (cridx, code))
454
455
456 if __name__ == "__main__":
457 unittest.main(exit=False)
458 suite = unittest.TestSuite()
459 suite.addTest(TestRunner(MulTestCase().test_data))
460
461 runner = unittest.TextTestRunner()
462 runner.run(suite)