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