move mul pipe ilang test to separate file
[soc.git] / src / soc / fu / mul / test / test_pipe_caller.py
1 from nmigen import Module, Signal
2
3 # NOTE: to use cxxsim, export NMIGEN_SIM_MODE=cxxsim from the shell
4 # Also, check out the cxxsim nmigen branch, and latest yosys from git
5 from nmutil.sim_tmp_alternative import Simulator, Delay, Settle
6
7 from nmigen.cli import rtlil
8 import unittest
9 from soc.decoder.isa.caller import ISACaller, special_sprs
10 from soc.decoder.power_decoder import (create_pdecode)
11 from soc.decoder.power_decoder2 import (PowerDecode2)
12 from soc.decoder.power_enums import (XER_bits, Function, MicrOp, CryIn)
13 from soc.decoder.selectable_int import SelectableInt
14 from soc.simulator.program import Program
15 from soc.decoder.isa.all import ISA
16 from soc.config.endian import bigendian
17
18 from soc.fu.test.common import (TestAccumulatorBase, TestCase, ALUHelpers)
19 from soc.fu.mul.pipeline import MulBasePipe
20 from soc.fu.mul.pipe_data import MulPipeSpec
21 import random
22
23
24 def get_cu_inputs(dec2, sim):
25 """naming (res) must conform to MulFunctionUnit input regspec
26 """
27 res = {}
28
29 yield from ALUHelpers.get_sim_int_ra(res, sim, dec2) # RA
30 yield from ALUHelpers.get_sim_int_rb(res, sim, dec2) # RB
31 yield from ALUHelpers.get_sim_xer_so(res, sim, dec2) # XER.so
32
33 print("alu get_cu_inputs", res)
34
35 return res
36
37
38 def set_alu_inputs(alu, dec2, sim):
39 # TODO: see https://bugs.libre-soc.org/show_bug.cgi?id=305#c43
40 # detect the immediate here (with m.If(self.i.ctx.op.imm_data.imm_ok))
41 # and place it into data_i.b
42
43 inp = yield from get_cu_inputs(dec2, sim)
44 print("set alu inputs", inp)
45 yield from ALUHelpers.set_int_ra(alu, dec2, inp)
46 yield from ALUHelpers.set_int_rb(alu, dec2, inp)
47
48 yield from ALUHelpers.set_xer_so(alu, dec2, inp)
49
50
51 # This test bench is a bit different than is usual. Initially when I
52 # was writing it, I had all of the tests call a function to create a
53 # device under test and simulator, initialize the dut, run the
54 # simulation for ~2 cycles, and assert that the dut output what it
55 # should have. However, this was really slow, since it needed to
56 # create and tear down the dut and simulator for every test case.
57
58 # Now, instead of doing that, every test case in MulTestCase puts some
59 # data into the test_data list below, describing the instructions to
60 # be tested and the initial state. Once all the tests have been run,
61 # test_data gets passed to TestRunner which then sets up the DUT and
62 # simulator once, runs all the data through it, and asserts that the
63 # results match the pseudocode sim at every cycle.
64
65 # By doing this, I've reduced the time it takes to run the test suite
66 # massively. Before, it took around 1 minute on my computer, now it
67 # takes around 3 seconds
68
69
70 class MulTestCase(TestAccumulatorBase):
71
72 def case_0_mullw(self):
73 lst = [f"mullw 3, 1, 2"]
74 initial_regs = [0] * 32
75 #initial_regs[1] = 0xffffffffffffffff
76 #initial_regs[2] = 0xffffffffffffffff
77 initial_regs[1] = 0x2ffffffff
78 initial_regs[2] = 0x2
79 self.add_case(Program(lst, bigendian), initial_regs)
80
81 def case_1_mullwo_(self):
82 lst = [f"mullwo. 3, 1, 2"]
83 initial_regs = [0] * 32
84 initial_regs[1] = 0x3b34b06f
85 initial_regs[2] = 0xfdeba998
86 self.add_case(Program(lst, bigendian), initial_regs)
87
88 def case_2_mullwo(self):
89 lst = [f"mullwo 3, 1, 2"]
90 initial_regs = [0] * 32
91 initial_regs[1] = 0xffffffffffffa988 # -5678
92 initial_regs[2] = 0xffffffffffffedcc # -1234
93 self.add_case(Program(lst, bigendian), initial_regs)
94
95 def case_3_mullw(self):
96 lst = ["mullw 3, 1, 2",
97 "mullw 3, 1, 2"]
98 initial_regs = [0] * 32
99 initial_regs[1] = 0x6
100 initial_regs[2] = 0xe
101 self.add_case(Program(lst, bigendian), initial_regs)
102
103 def case_4_mullw_rand(self):
104 for i in range(40):
105 lst = ["mullw 3, 1, 2"]
106 initial_regs = [0] * 32
107 initial_regs[1] = random.randint(0, (1 << 64)-1)
108 initial_regs[2] = random.randint(0, (1 << 64)-1)
109 self.add_case(Program(lst, bigendian), initial_regs)
110
111 def case_4_mullw_nonrand(self):
112 for i in range(40):
113 lst = ["mullw 3, 1, 2"]
114 initial_regs = [0] * 32
115 initial_regs[1] = i+1
116 initial_regs[2] = i+20
117 self.add_case(Program(lst, bigendian), initial_regs)
118
119 def case_mulhw__regression_1(self):
120 lst = ["mulhw. 3, 1, 2"
121 ]
122 initial_regs = [0] * 32
123 initial_regs[1] = 0x7745b36eca6646fa
124 initial_regs[2] = 0x47dfba3a63834ba2
125 self.add_case(Program(lst, bigendian), initial_regs)
126
127 def case_rand_mul_lh(self):
128 insns = ["mulhw", "mulhw.", "mulhwu", "mulhwu."]
129 for i in range(40):
130 choice = random.choice(insns)
131 lst = [f"{choice} 3, 1, 2"]
132 initial_regs = [0] * 32
133 initial_regs[1] = random.randint(0, (1 << 64)-1)
134 initial_regs[2] = random.randint(0, (1 << 64)-1)
135 self.add_case(Program(lst, bigendian), initial_regs)
136
137 def case_rand_mullw(self):
138 insns = ["mullw", "mullw.", "mullwo", "mullwo."]
139 for i in range(40):
140 choice = random.choice(insns)
141 lst = [f"{choice} 3, 1, 2"]
142 initial_regs = [0] * 32
143 initial_regs[1] = random.randint(0, (1 << 64)-1)
144 initial_regs[2] = random.randint(0, (1 << 64)-1)
145 self.add_case(Program(lst, bigendian), initial_regs)
146
147 def case_rand_mulld(self):
148 insns = ["mulld", "mulld.", "mulldo", "mulldo."]
149 for i in range(40):
150 choice = random.choice(insns)
151 lst = [f"{choice} 3, 1, 2"]
152 initial_regs = [0] * 32
153 initial_regs[1] = random.randint(0, (1 << 64)-1)
154 initial_regs[2] = random.randint(0, (1 << 64)-1)
155 self.add_case(Program(lst, bigendian), initial_regs)
156
157 def case_rand_mulhd(self):
158 insns = ["mulhd", "mulhd."]
159 for i in range(40):
160 choice = random.choice(insns)
161 lst = [f"{choice} 3, 1, 2"]
162 initial_regs = [0] * 32
163 initial_regs[1] = random.randint(0, (1 << 64)-1)
164 initial_regs[2] = random.randint(0, (1 << 64)-1)
165 self.add_case(Program(lst, bigendian), initial_regs)
166
167 def case_0_mullhw_regression(self):
168 lst = [f"mulhwu 3, 1, 2"]
169 initial_regs = [0] * 32
170 initial_regs[1] = 0x4000000000000000
171 initial_regs[2] = 0x0000000000000002
172 self.add_case(Program(lst, bigendian), initial_regs)
173
174 # TODO add test case for these 3 operand cases (madd
175 # needs to be implemented)
176 # "maddhd","maddhdu","maddld"
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 fn_name = "MUL"
190 opkls = MulPipeSpec.opsubsetkls
191
192 m.submodules.pdecode2 = pdecode2 = PowerDecode2(None, opkls, fn_name)
193 pdecode = pdecode2.dec
194
195 pspec = MulPipeSpec(id_wid=2)
196 m.submodules.alu = alu = MulBasePipe(pspec)
197
198 comb += alu.p.data_i.ctx.op.eq_from_execute1(pdecode2.do)
199 comb += alu.n.ready_i.eq(1)
200 comb += pdecode2.dec.raw_opcode_in.eq(instruction)
201 sim = Simulator(m)
202
203 sim.add_clock(1e-6)
204
205 def process():
206 for test in self.test_data:
207 print(test.name)
208 program = test.program
209 self.subTest(test.name)
210 sim = ISA(pdecode2, test.regs, test.sprs, test.cr,
211 test.mem, test.msr,
212 bigendian=bigendian)
213 gen = program.generate_instructions()
214 instructions = list(zip(gen, program.assembly.splitlines()))
215 yield Settle()
216
217 index = sim.pc.CIA.value//4
218 while index < len(instructions):
219 ins, code = instructions[index]
220
221 print("instruction: 0x{:X}".format(ins & 0xffffffff))
222 print(code)
223 if 'XER' in sim.spr:
224 so = 1 if sim.spr['XER'][XER_bits['SO']] else 0
225 ov = 1 if sim.spr['XER'][XER_bits['OV']] else 0
226 ov32 = 1 if sim.spr['XER'][XER_bits['OV32']] else 0
227 print("before: so/ov/32", so, ov, ov32)
228
229 # ask the decoder to decode this binary data (endian'd)
230 yield pdecode2.dec.bigendian.eq(bigendian) # little / big?
231 yield instruction.eq(ins) # raw binary instr.
232 yield Settle()
233 fn_unit = yield pdecode2.e.do.fn_unit
234 self.assertEqual(fn_unit, Function.MUL.value)
235 yield from set_alu_inputs(alu, pdecode2, sim)
236
237 # set valid for one cycle, propagate through pipeline...
238 yield alu.p.valid_i.eq(1)
239 yield
240 yield alu.p.valid_i.eq(0)
241
242 opname = code.split(' ')[0]
243 yield from sim.call(opname)
244 index = sim.pc.CIA.value//4
245
246 # ...wait for valid to pop out the end
247 vld = yield alu.n.valid_o
248 while not vld:
249 yield
250 vld = yield alu.n.valid_o
251 yield
252
253 yield from self.check_alu_outputs(alu, pdecode2, sim, code)
254 yield Settle()
255
256 sim.add_sync_process(process)
257 with sim.write_vcd("mul_simulator.vcd", "mul_simulator.gtkw",
258 traces=[]):
259 sim.run()
260
261 def check_alu_outputs(self, alu, dec2, sim, code):
262
263 rc = yield dec2.e.do.rc.rc
264 cridx_ok = yield dec2.e.write_cr.ok
265 cridx = yield dec2.e.write_cr.data
266
267 print("check extra output", repr(code), cridx_ok, cridx)
268 if rc:
269 self.assertEqual(cridx, 0, code)
270
271 oe = yield dec2.e.do.oe.oe
272 oe_ok = yield dec2.e.do.oe.ok
273 if not oe or not oe_ok:
274 # if OE not enabled, XER SO and OV must correspondingly be false
275 so_ok = yield alu.n.data_o.xer_so.ok
276 ov_ok = yield alu.n.data_o.xer_ov.ok
277 self.assertEqual(so_ok, False, code)
278 self.assertEqual(ov_ok, False, code)
279
280 sim_o = {}
281 res = {}
282
283 yield from ALUHelpers.get_cr_a(res, alu, dec2)
284 yield from ALUHelpers.get_xer_ov(res, alu, dec2)
285 yield from ALUHelpers.get_int_o(res, alu, dec2)
286 yield from ALUHelpers.get_xer_so(res, alu, dec2)
287
288 yield from ALUHelpers.get_sim_int_o(sim_o, sim, dec2)
289 yield from ALUHelpers.get_wr_sim_cr_a(sim_o, sim, dec2)
290 yield from ALUHelpers.get_sim_xer_ov(sim_o, sim, dec2)
291 yield from ALUHelpers.get_sim_xer_so(sim_o, sim, dec2)
292
293 ALUHelpers.check_int_o(self, res, sim_o, code)
294 ALUHelpers.check_xer_ov(self, res, sim_o, code)
295 ALUHelpers.check_xer_so(self, res, sim_o, code)
296 ALUHelpers.check_cr_a(self, res, sim_o, "CR%d %s" % (cridx, code))
297
298
299 if __name__ == "__main__":
300 unittest.main(exit=False)
301 suite = unittest.TestSuite()
302 suite.addTest(TestRunner(MulTestCase().test_data))
303
304 runner = unittest.TextTestRunner()
305 runner.run(suite)