convert mul test to accumulator style
[soc.git] / src / soc / fu / mul / test / test_pipe_caller.py
1 from nmigen import Module, Signal
2 from nmigen.back.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_4_mullw_rand(self):
124 for i in range(40):
125 lst = ["mullw 3, 1, 2"]
126 initial_regs = [0] * 32
127 initial_regs[1] = random.randint(0, (1 << 64)-1)
128 initial_regs[2] = random.randint(0, (1 << 64)-1)
129 self.add_case(Program(lst, bigendian), initial_regs)
130
131 def case_rand_mul_lh(self):
132 insns = ["mulhw", "mulhw.", "mulhwu", "mulhwu."]
133 for i in range(40):
134 choice = random.choice(insns)
135 lst = [f"{choice} 3, 1, 2"]
136 initial_regs = [0] * 32
137 initial_regs[1] = random.randint(0, (1 << 64)-1)
138 initial_regs[2] = random.randint(0, (1 << 64)-1)
139 self.add_case(Program(lst, bigendian), initial_regs)
140
141 def case_rand_mullw(self):
142 insns = ["mullw", "mullw.", "mullwo", "mullwo."]
143 for i in range(40):
144 choice = random.choice(insns)
145 lst = [f"{choice} 3, 1, 2"]
146 initial_regs = [0] * 32
147 initial_regs[1] = random.randint(0, (1 << 64)-1)
148 initial_regs[2] = random.randint(0, (1 << 64)-1)
149 self.add_case(Program(lst, bigendian), initial_regs)
150
151 def case_rand_mulld(self):
152 insns = ["mulld", "mulld.", "mulldo", "mulldo."]
153 for i in range(40):
154 choice = random.choice(insns)
155 lst = [f"{choice} 3, 1, 2"]
156 initial_regs = [0] * 32
157 initial_regs[1] = random.randint(0, (1 << 64)-1)
158 initial_regs[2] = random.randint(0, (1 << 64)-1)
159 self.add_case(Program(lst, bigendian), initial_regs)
160
161 def case_rand_mulhd(self):
162 insns = ["mulhd", "mulhd."]
163 for i in range(40):
164 choice = random.choice(insns)
165 lst = [f"{choice} 3, 1, 2"]
166 initial_regs = [0] * 32
167 initial_regs[1] = random.randint(0, (1 << 64)-1)
168 initial_regs[2] = random.randint(0, (1 << 64)-1)
169 self.add_case(Program(lst, bigendian), initial_regs)
170
171 def case_ilang(self):
172 pspec = MulPipeSpec(id_wid=2)
173 alu = MulBasePipe(pspec)
174 vl = rtlil.convert(alu, ports=alu.ports())
175 with open("mul_pipeline.il", "w") as f:
176 f.write(vl)
177
178
179 class TestRunner(unittest.TestCase):
180 def __init__(self, test_data):
181 super().__init__("run_all")
182 self.test_data = test_data
183
184 def run_all(self):
185 m = Module()
186 comb = m.d.comb
187 instruction = Signal(32)
188
189 pdecode = create_pdecode()
190
191 m.submodules.pdecode2 = pdecode2 = PowerDecode2(pdecode)
192
193 pspec = MulPipeSpec(id_wid=2)
194 m.submodules.alu = alu = MulBasePipe(pspec)
195
196 comb += alu.p.data_i.ctx.op.eq_from_execute1(pdecode2.e)
197 comb += alu.n.ready_i.eq(1)
198 comb += pdecode2.dec.raw_opcode_in.eq(instruction)
199 sim = Simulator(m)
200
201 sim.add_clock(1e-6)
202
203 def process():
204 for test in self.test_data:
205 print(test.name)
206 program = test.program
207 self.subTest(test.name)
208 sim = ISA(pdecode2, test.regs, test.sprs, test.cr,
209 test.mem, test.msr,
210 bigendian=bigendian)
211 gen = program.generate_instructions()
212 instructions = list(zip(gen, program.assembly.splitlines()))
213 yield Settle()
214
215 index = sim.pc.CIA.value//4
216 while index < len(instructions):
217 ins, code = instructions[index]
218
219 print("instruction: 0x{:X}".format(ins & 0xffffffff))
220 print(code)
221 if 'XER' in sim.spr:
222 so = 1 if sim.spr['XER'][XER_bits['SO']] else 0
223 ov = 1 if sim.spr['XER'][XER_bits['OV']] else 0
224 ov32 = 1 if sim.spr['XER'][XER_bits['OV32']] else 0
225 print("before: so/ov/32", so, ov, ov32)
226
227 # ask the decoder to decode this binary data (endian'd)
228 yield pdecode2.dec.bigendian.eq(bigendian) # little / big?
229 yield instruction.eq(ins) # raw binary instr.
230 yield Settle()
231 fn_unit = yield pdecode2.e.do.fn_unit
232 self.assertEqual(fn_unit, Function.MUL.value)
233 yield from set_alu_inputs(alu, pdecode2, sim)
234
235 # set valid for one cycle, propagate through pipeline...
236 yield alu.p.valid_i.eq(1)
237 yield
238 yield alu.p.valid_i.eq(0)
239
240 opname = code.split(' ')[0]
241 yield from sim.call(opname)
242 index = sim.pc.CIA.value//4
243
244 # ...wait for valid to pop out the end
245 vld = yield alu.n.valid_o
246 while not vld:
247 yield
248 vld = yield alu.n.valid_o
249 yield
250
251 yield from self.check_alu_outputs(alu, pdecode2, sim, code)
252 yield Settle()
253
254 sim.add_sync_process(process)
255 with sim.write_vcd("mul_simulator.vcd", "mul_simulator.gtkw",
256 traces=[]):
257 sim.run()
258
259 def check_alu_outputs(self, alu, dec2, sim, code):
260
261 rc = yield dec2.e.do.rc.data
262 cridx_ok = yield dec2.e.write_cr.ok
263 cridx = yield dec2.e.write_cr.data
264
265 print("check extra output", repr(code), cridx_ok, cridx)
266 if rc:
267 self.assertEqual(cridx, 0, code)
268
269 oe = yield dec2.e.do.oe.oe
270 oe_ok = yield dec2.e.do.oe.ok
271 if not oe or not oe_ok:
272 # if OE not enabled, XER SO and OV must correspondingly be false
273 so_ok = yield alu.n.data_o.xer_so.ok
274 ov_ok = yield alu.n.data_o.xer_ov.ok
275 self.assertEqual(so_ok, False, code)
276 self.assertEqual(ov_ok, False, code)
277
278 sim_o = {}
279 res = {}
280
281 yield from ALUHelpers.get_cr_a(res, alu, dec2)
282 yield from ALUHelpers.get_xer_ov(res, alu, dec2)
283 yield from ALUHelpers.get_int_o(res, alu, dec2)
284 yield from ALUHelpers.get_xer_so(res, alu, dec2)
285
286 yield from ALUHelpers.get_sim_int_o(sim_o, sim, dec2)
287 yield from ALUHelpers.get_wr_sim_cr_a(sim_o, sim, dec2)
288 yield from ALUHelpers.get_sim_xer_ov(sim_o, sim, dec2)
289 yield from ALUHelpers.get_sim_xer_so(sim_o, sim, dec2)
290
291 ALUHelpers.check_int_o(self, res, sim_o, code)
292 ALUHelpers.check_xer_ov(self, res, sim_o, code)
293 ALUHelpers.check_xer_so(self, res, sim_o, code)
294 ALUHelpers.check_cr_a(self, res, sim_o, "CR%d %s" % (cridx, code))
295
296
297 if __name__ == "__main__":
298 unittest.main(exit=False)
299 suite = unittest.TestSuite()
300 suite.addTest(TestRunner(MulTestCase().test_data))
301
302 runner = unittest.TextTestRunner()
303 runner.run(suite)