convert mul test to use Power Decode subset
[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_0_mullhw_regression(self):
164 lst = [f"mulhwu 3, 1, 2"]
165 initial_regs = [0] * 32
166 initial_regs[1] = 0x4000000000000000
167 initial_regs[2] = 0x0000000000000002
168 self.add_case(Program(lst, bigendian), initial_regs)
169
170 # TODO add test case for these 3 operand cases (madd
171 # needs to be implemented)
172 # "maddhd","maddhdu","maddld"
173
174 def case_ilang(self):
175 pspec = MulPipeSpec(id_wid=2)
176 alu = MulBasePipe(pspec)
177 vl = rtlil.convert(alu, ports=alu.ports())
178 with open("mul_pipeline.il", "w") as f:
179 f.write(vl)
180
181
182 class TestRunner(unittest.TestCase):
183 def __init__(self, test_data):
184 super().__init__("run_all")
185 self.test_data = test_data
186
187 def run_all(self):
188 m = Module()
189 comb = m.d.comb
190 instruction = Signal(32)
191
192 fn_name = "MUL"
193 opkls = MulPipeSpec.opsubsetkls
194
195 m.submodules.pdecode2 = pdecode2 = PowerDecode2(None, opkls, fn_name)
196 pdecode = pdecode2.dec
197
198 pspec = MulPipeSpec(id_wid=2)
199 m.submodules.alu = alu = MulBasePipe(pspec)
200
201 comb += alu.p.data_i.ctx.op.eq_from_execute1(pdecode2.e)
202 comb += alu.n.ready_i.eq(1)
203 comb += pdecode2.dec.raw_opcode_in.eq(instruction)
204 sim = Simulator(m)
205
206 sim.add_clock(1e-6)
207
208 def process():
209 for test in self.test_data:
210 print(test.name)
211 program = test.program
212 self.subTest(test.name)
213 sim = ISA(pdecode2, test.regs, test.sprs, test.cr,
214 test.mem, test.msr,
215 bigendian=bigendian)
216 gen = program.generate_instructions()
217 instructions = list(zip(gen, program.assembly.splitlines()))
218 yield Settle()
219
220 index = sim.pc.CIA.value//4
221 while index < len(instructions):
222 ins, code = instructions[index]
223
224 print("instruction: 0x{:X}".format(ins & 0xffffffff))
225 print(code)
226 if 'XER' in sim.spr:
227 so = 1 if sim.spr['XER'][XER_bits['SO']] else 0
228 ov = 1 if sim.spr['XER'][XER_bits['OV']] else 0
229 ov32 = 1 if sim.spr['XER'][XER_bits['OV32']] else 0
230 print("before: so/ov/32", so, ov, ov32)
231
232 # ask the decoder to decode this binary data (endian'd)
233 yield pdecode2.dec.bigendian.eq(bigendian) # little / big?
234 yield instruction.eq(ins) # raw binary instr.
235 yield Settle()
236 fn_unit = yield pdecode2.e.do.fn_unit
237 self.assertEqual(fn_unit, Function.MUL.value)
238 yield from set_alu_inputs(alu, pdecode2, sim)
239
240 # set valid for one cycle, propagate through pipeline...
241 yield alu.p.valid_i.eq(1)
242 yield
243 yield alu.p.valid_i.eq(0)
244
245 opname = code.split(' ')[0]
246 yield from sim.call(opname)
247 index = sim.pc.CIA.value//4
248
249 # ...wait for valid to pop out the end
250 vld = yield alu.n.valid_o
251 while not vld:
252 yield
253 vld = yield alu.n.valid_o
254 yield
255
256 yield from self.check_alu_outputs(alu, pdecode2, sim, code)
257 yield Settle()
258
259 sim.add_sync_process(process)
260 with sim.write_vcd("mul_simulator.vcd", "mul_simulator.gtkw",
261 traces=[]):
262 sim.run()
263
264 def check_alu_outputs(self, alu, dec2, sim, code):
265
266 rc = yield dec2.e.do.rc.rc
267 cridx_ok = yield dec2.e.write_cr.ok
268 cridx = yield dec2.e.write_cr.data
269
270 print("check extra output", repr(code), cridx_ok, cridx)
271 if rc:
272 self.assertEqual(cridx, 0, code)
273
274 oe = yield dec2.e.do.oe.oe
275 oe_ok = yield dec2.e.do.oe.ok
276 if not oe or not oe_ok:
277 # if OE not enabled, XER SO and OV must correspondingly be false
278 so_ok = yield alu.n.data_o.xer_so.ok
279 ov_ok = yield alu.n.data_o.xer_ov.ok
280 self.assertEqual(so_ok, False, code)
281 self.assertEqual(ov_ok, False, code)
282
283 sim_o = {}
284 res = {}
285
286 yield from ALUHelpers.get_cr_a(res, alu, dec2)
287 yield from ALUHelpers.get_xer_ov(res, alu, dec2)
288 yield from ALUHelpers.get_int_o(res, alu, dec2)
289 yield from ALUHelpers.get_xer_so(res, alu, dec2)
290
291 yield from ALUHelpers.get_sim_int_o(sim_o, sim, dec2)
292 yield from ALUHelpers.get_wr_sim_cr_a(sim_o, sim, dec2)
293 yield from ALUHelpers.get_sim_xer_ov(sim_o, sim, dec2)
294 yield from ALUHelpers.get_sim_xer_so(sim_o, sim, dec2)
295
296 ALUHelpers.check_int_o(self, res, sim_o, code)
297 ALUHelpers.check_xer_ov(self, res, sim_o, code)
298 ALUHelpers.check_xer_so(self, res, sim_o, code)
299 ALUHelpers.check_cr_a(self, res, sim_o, "CR%d %s" % (cridx, code))
300
301
302 if __name__ == "__main__":
303 unittest.main(exit=False)
304 suite = unittest.TestSuite()
305 suite.addTest(TestRunner(MulTestCase().test_data))
306
307 runner = unittest.TextTestRunner()
308 runner.run(suite)