Remove mulli from instrs in test case_all*, add TODO for mulli special
[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 # TODO add special test case for "mulli"
175
176 # TODO add test case for these 3 operand cases
177 # ,"maddhd","maddhdu","maddld"
178
179 test_values = [
180 0x0,
181 0x1,
182 0x2,
183 0xFFFF_FFFF_FFFF_FFFF,
184 0xFFFF_FFFF_FFFF_FFFE,
185 0x7FFF_FFFF_FFFF_FFFF,
186 0x8000_0000_0000_0000,
187 0x1234_5678_0000_0000,
188 0x1234_5678_8000_0000,
189 0x1234_5678_FFFF_FFFF,
190 0x1234_5678_7FFF_FFFF,
191 0xffffffff,
192 0x7fffffff,
193 0x80000000,
194 0xfffffffe,
195 0xfffffffd
196 ]
197
198 for instr in instrs:
199 l = [f"{instr} 3, 1, 2"]
200 for ra in test_values:
201 for rb in test_values:
202 initial_regs = [0] * 32
203 initial_regs[1] = ra
204 initial_regs[2] = rb
205 # use "with" so as to close the files used
206 with Program(l, bigendian) as prog:
207 self.add_case(prog, initial_regs)
208
209 def case_all_rb_randint(self):
210 instrs = ["mulhw",
211 "mulhw.","mullw",
212 "mullw.","mullwo",
213 "mullwo.","mulhwu",
214 "mulhwu.","mulld",
215 "mulld.","mulldo",
216 "mulldo.","mulhd",
217 "mulhd.","mulhdu",
218 "mulhdu."]
219
220 test_values = [
221 0x0,
222 0x1,
223 0x2,
224 0xFFFF_FFFF_FFFF_FFFF,
225 0xFFFF_FFFF_FFFF_FFFE,
226 0x7FFF_FFFF_FFFF_FFFF,
227 0x8000_0000_0000_0000,
228 0x1234_5678_0000_0000,
229 0x1234_5678_8000_0000,
230 0x1234_5678_FFFF_FFFF,
231 0x1234_5678_7FFF_FFFF,
232 0xffffffff,
233 0x7fffffff,
234 0x80000000,
235 0xfffffffe,
236 0xfffffffd
237 ]
238
239 for instr in instrs:
240 l = [f"{instr} 3, 1, 2"]
241 for ra in test_values:
242 initial_regs = [0] * 32
243 initial_regs[1] = ra
244 initial_regs[2] = random.randint(0, (1 << 64)-1)
245 # use "with" so as to close the files used
246 with Program(l, bigendian) as prog:
247 self.add_case(prog, initial_regs)
248
249 def case_ilang(self):
250 pspec = MulPipeSpec(id_wid=2)
251 alu = MulBasePipe(pspec)
252 vl = rtlil.convert(alu, ports=alu.ports())
253 with open("mul_pipeline.il", "w") as f:
254 f.write(vl)
255
256
257 class TestRunner(unittest.TestCase):
258 def __init__(self, test_data):
259 super().__init__("run_all")
260 self.test_data = test_data
261
262 def run_all(self):
263 m = Module()
264 comb = m.d.comb
265 instruction = Signal(32)
266
267 pdecode = create_pdecode()
268
269 m.submodules.pdecode2 = pdecode2 = PowerDecode2(pdecode)
270
271 pspec = MulPipeSpec(id_wid=2)
272 m.submodules.alu = alu = MulBasePipe(pspec)
273
274 comb += alu.p.data_i.ctx.op.eq_from_execute1(pdecode2.e)
275 comb += alu.n.ready_i.eq(1)
276 comb += pdecode2.dec.raw_opcode_in.eq(instruction)
277 sim = Simulator(m)
278
279 sim.add_clock(1e-6)
280
281 def process():
282 for test in self.test_data:
283 print(test.name)
284 program = test.program
285 self.subTest(test.name)
286 sim = ISA(pdecode2, test.regs, test.sprs, test.cr,
287 test.mem, test.msr,
288 bigendian=bigendian)
289 gen = program.generate_instructions()
290 instructions = list(zip(gen, program.assembly.splitlines()))
291 yield Settle()
292
293 index = sim.pc.CIA.value//4
294 while index < len(instructions):
295 ins, code = instructions[index]
296
297 print("instruction: 0x{:X}".format(ins & 0xffffffff))
298 print(code)
299 if 'XER' in sim.spr:
300 so = 1 if sim.spr['XER'][XER_bits['SO']] else 0
301 ov = 1 if sim.spr['XER'][XER_bits['OV']] else 0
302 ov32 = 1 if sim.spr['XER'][XER_bits['OV32']] else 0
303 print("before: so/ov/32", so, ov, ov32)
304
305 # ask the decoder to decode this binary data (endian'd)
306 yield pdecode2.dec.bigendian.eq(bigendian) # little / big?
307 yield instruction.eq(ins) # raw binary instr.
308 yield Settle()
309 fn_unit = yield pdecode2.e.do.fn_unit
310 self.assertEqual(fn_unit, Function.MUL.value)
311 yield from set_alu_inputs(alu, pdecode2, sim)
312
313 # set valid for one cycle, propagate through pipeline...
314 yield alu.p.valid_i.eq(1)
315 yield
316 yield alu.p.valid_i.eq(0)
317
318 opname = code.split(' ')[0]
319 yield from sim.call(opname)
320 index = sim.pc.CIA.value//4
321
322 # ...wait for valid to pop out the end
323 vld = yield alu.n.valid_o
324 while not vld:
325 yield
326 vld = yield alu.n.valid_o
327 yield
328
329 yield from self.check_alu_outputs(alu, pdecode2, sim, code)
330 yield Settle()
331
332 sim.add_sync_process(process)
333 with sim.write_vcd("mul_simulator.vcd", "mul_simulator.gtkw",
334 traces=[]):
335 sim.run()
336
337 def check_alu_outputs(self, alu, dec2, sim, code):
338
339 rc = yield dec2.e.do.rc.data
340 cridx_ok = yield dec2.e.write_cr.ok
341 cridx = yield dec2.e.write_cr.data
342
343 print("check extra output", repr(code), cridx_ok, cridx)
344 if rc:
345 self.assertEqual(cridx, 0, code)
346
347 oe = yield dec2.e.do.oe.oe
348 oe_ok = yield dec2.e.do.oe.ok
349 if not oe or not oe_ok:
350 # if OE not enabled, XER SO and OV must correspondingly be false
351 so_ok = yield alu.n.data_o.xer_so.ok
352 ov_ok = yield alu.n.data_o.xer_ov.ok
353 self.assertEqual(so_ok, False, code)
354 self.assertEqual(ov_ok, False, code)
355
356 sim_o = {}
357 res = {}
358
359 yield from ALUHelpers.get_cr_a(res, alu, dec2)
360 yield from ALUHelpers.get_xer_ov(res, alu, dec2)
361 yield from ALUHelpers.get_int_o(res, alu, dec2)
362 yield from ALUHelpers.get_xer_so(res, alu, dec2)
363
364 yield from ALUHelpers.get_sim_int_o(sim_o, sim, dec2)
365 yield from ALUHelpers.get_wr_sim_cr_a(sim_o, sim, dec2)
366 yield from ALUHelpers.get_sim_xer_ov(sim_o, sim, dec2)
367 yield from ALUHelpers.get_sim_xer_so(sim_o, sim, dec2)
368
369 ALUHelpers.check_int_o(self, res, sim_o, code)
370 ALUHelpers.check_xer_ov(self, res, sim_o, code)
371 ALUHelpers.check_xer_so(self, res, sim_o, code)
372 ALUHelpers.check_cr_a(self, res, sim_o, "CR%d %s" % (cridx, code))
373
374
375 if __name__ == "__main__":
376 unittest.main(exit=False)
377 suite = unittest.TestSuite()
378 suite.addTest(TestRunner(MulTestCase().test_data))
379
380 runner = unittest.TextTestRunner()
381 runner.run(suite)