65aa0801e0b17157092dcda32e84c54821b676b3
[soc.git] / src / soc / fu / div / 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, InternalOp, 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.div.pipeline import DIVBasePipe
17 from soc.fu.div.pipe_data import DIVPipeSpec
18 import random
19
20 def log_rand(n, min_val=1):
21 logrange = random.randint(1, n)
22 return random.randint(min_val, (1<<logrange)-1)
23
24 def get_cu_inputs(dec2, sim):
25 """naming (res) must conform to DIVFunctionUnit 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
39 def set_alu_inputs(alu, dec2, sim):
40 # TODO: see https://bugs.libre-soc.org/show_bug.cgi?id=305#c43
41 # detect the immediate here (with m.If(self.i.ctx.op.imm_data.imm_ok))
42 # and place it into data_i.b
43
44 inp = yield from get_cu_inputs(dec2, sim)
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 DIVTestCase 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 DIVTestCase(FHDLTestCase):
71 test_data = []
72
73 def __init__(self, name):
74 super().__init__(name)
75 self.test_name = name
76
77 def run_tst_program(self, prog, initial_regs=None, initial_sprs=None):
78 tc = TestCase(prog, self.test_name, initial_regs, initial_sprs)
79 self.test_data.append(tc)
80
81 def tst_0_regression(self):
82 for i in range(40):
83 lst = ["divwo 3, 1, 2"]
84 initial_regs = [0] * 32
85 initial_regs[1] = 0xbc716835f32ac00c
86 initial_regs[2] = 0xcdf69a7f7042db66
87 self.run_tst_program(Program(lst, bigendian), initial_regs)
88
89 def tst_1_regression(self):
90 lst = ["divwo 3, 1, 2"]
91 initial_regs = [0] * 32
92 initial_regs[1] = 0x10000000000000000-4
93 initial_regs[2] = 0x10000000000000000-2
94 self.run_tst_program(Program(lst, bigendian), initial_regs)
95
96 def tst_2_regression(self):
97 lst = ["divwo 3, 1, 2"]
98 initial_regs = [0] * 32
99 initial_regs[1] = 0xffffffffffff9321
100 initial_regs[2] = 0xffffffffffff7012
101 self.run_tst_program(Program(lst, bigendian), initial_regs)
102
103 def tst_3_regression(self):
104 lst = ["divwo. 3, 1, 2"]
105 initial_regs = [0] * 32
106 initial_regs[1] = 0x1b8e32f2458746af
107 initial_regs[2] = 0x6b8aee2ccf7d62e9
108 self.run_tst_program(Program(lst, bigendian), initial_regs)
109
110 def tst_4_regression(self):
111 lst = ["divw 3, 1, 2"]
112 initial_regs = [0] * 32
113 initial_regs[1] = 0x1c4e6c2f3aa4a05c
114 initial_regs[2] = 0xe730c2eed6cc8dd7
115 self.run_tst_program(Program(lst, bigendian), initial_regs)
116
117 def tst_5_regression(self):
118 lst = ["divw 3, 1, 2",
119 "divwo. 6, 4, 5"]
120 initial_regs = [0] * 32
121 initial_regs[1] = 0x1c4e6c2f3aa4a05c
122 initial_regs[2] = 0xe730c2eed6cc8dd7
123 initial_regs[4] = 0x1b8e32f2458746af
124 initial_regs[5] = 0x6b8aee2ccf7d62e9
125 self.run_tst_program(Program(lst, bigendian), initial_regs)
126
127 def tst_6_regression(self):
128 # CR0 not getting set properly for this one
129 # turns out that overflow is not set correctly in
130 # fu/div/output_stage.py calc_overflow
131 # https://bugs.libre-soc.org/show_bug.cgi?id=425
132 lst = ["divw. 3, 1, 2"]
133 initial_regs = [0] * 32
134 initial_regs[1] = 0x61c1cc3b80f2a6af
135 initial_regs[2] = 0x9dc66a7622c32bc0
136 self.run_tst_program(Program(lst, bigendian), initial_regs)
137
138 def test_7_regression(self):
139 # https://bugs.libre-soc.org/show_bug.cgi?id=425
140 lst = ["divw. 3, 1, 2"]
141 initial_regs = [0] * 32
142 initial_regs[1] = 0xf1791627e05e8096
143 initial_regs[2] = 0xffc868bf4573da0b
144 self.run_tst_program(Program(lst, bigendian), initial_regs)
145
146 def tst_divw_by_zero_1(self):
147 lst = ["divw. 3, 1, 2"]
148 initial_regs = [0] * 32
149 initial_regs[1] = 0x1
150 initial_regs[2] = 0x0
151 self.run_tst_program(Program(lst, bigendian), initial_regs)
152
153 def tst_divw_overflow2(self):
154 lst = ["divw. 3, 1, 2"]
155 initial_regs = [0] * 32
156 initial_regs[1] = 0x80000000
157 initial_regs[2] = 0xffffffffffffffff # top bits don't seem to matter
158 self.run_tst_program(Program(lst, bigendian), initial_regs)
159
160 def tst_divw_overflow3(self):
161 lst = ["divw. 3, 1, 2"]
162 initial_regs = [0] * 32
163 initial_regs[1] = 0x80000000
164 initial_regs[2] = 0xffffffff
165 self.run_tst_program(Program(lst, bigendian), initial_regs)
166
167 def tst_rand_divw(self):
168 insns = ["divw", "divw.", "divwo", "divwo."]
169 for i in range(40):
170 choice = random.choice(insns)
171 lst = [f"{choice} 3, 1, 2"]
172 initial_regs = [0] * 32
173 initial_regs[1] = log_rand(32)
174 initial_regs[2] = log_rand(32)
175 self.run_tst_program(Program(lst, bigendian), initial_regs)
176
177 def tst_divwuo_regression_1(self):
178 lst = ["divwuo. 3, 1, 2"]
179 initial_regs = [0] * 32
180 initial_regs[1] = 0x7591a398c4e32b68
181 initial_regs[2] = 0x48674ab432867d69
182 self.run_tst_program(Program(lst, bigendian), initial_regs)
183
184 def tst_divwuo_1(self):
185 lst = ["divwuo. 3, 1, 2"]
186 initial_regs = [0] * 32
187 initial_regs[1] = 0x50
188 initial_regs[2] = 0x2
189 self.run_tst_program(Program(lst, bigendian), initial_regs)
190
191 def tst_rand_divwu(self):
192 insns = ["divwu", "divwu.", "divwuo", "divwuo."]
193 for i in range(40):
194 choice = random.choice(insns)
195 lst = [f"{choice} 3, 1, 2"]
196 initial_regs = [0] * 32
197 initial_regs[1] = log_rand(32)
198 initial_regs[2] = log_rand(32)
199 self.run_tst_program(Program(lst, bigendian), initial_regs)
200
201 def tst_ilang(self):
202 pspec = DIVPipeSpec(id_wid=2)
203 alu = DIVBasePipe(pspec)
204 vl = rtlil.convert(alu, ports=alu.ports())
205 with open("div_pipeline.il", "w") as f:
206 f.write(vl)
207
208
209 class TestRunner(FHDLTestCase):
210 def __init__(self, test_data):
211 super().__init__("run_all")
212 self.test_data = test_data
213
214 def run_all(self):
215 m = Module()
216 comb = m.d.comb
217 instruction = Signal(32)
218
219 pdecode = create_pdecode()
220
221 m.submodules.pdecode2 = pdecode2 = PowerDecode2(pdecode)
222
223 pspec = DIVPipeSpec(id_wid=2)
224 m.submodules.alu = alu = DIVBasePipe(pspec)
225
226 comb += alu.p.data_i.ctx.op.eq_from_execute1(pdecode2.e)
227 comb += alu.n.ready_i.eq(1)
228 comb += pdecode2.dec.raw_opcode_in.eq(instruction)
229 sim = Simulator(m)
230
231 sim.add_clock(1e-6)
232 def process():
233 for test in self.test_data:
234 print(test.name)
235 program = test.program
236 self.subTest(test.name)
237 sim = ISA(pdecode2, test.regs, test.sprs, test.cr,
238 test.mem, test.msr,
239 bigendian=bigendian)
240 gen = program.generate_instructions()
241 instructions = list(zip(gen, program.assembly.splitlines()))
242 yield Settle()
243
244 index = sim.pc.CIA.value//4
245 while index < len(instructions):
246 ins, code = instructions[index]
247
248 print("instruction: 0x{:X}".format(ins & 0xffffffff))
249 print(code)
250 if 'XER' in sim.spr:
251 so = 1 if sim.spr['XER'][XER_bits['SO']] else 0
252 ov = 1 if sim.spr['XER'][XER_bits['OV']] else 0
253 ov32 = 1 if sim.spr['XER'][XER_bits['OV32']] else 0
254 print ("before: so/ov/32", so, ov, ov32)
255
256 # ask the decoder to decode this binary data (endian'd)
257 yield pdecode2.dec.bigendian.eq(bigendian) # little / big?
258 yield instruction.eq(ins) # raw binary instr.
259 yield Settle()
260 fn_unit = yield pdecode2.e.do.fn_unit
261 self.assertEqual(fn_unit, Function.DIV.value)
262 yield from set_alu_inputs(alu, pdecode2, sim)
263
264 # set valid for one cycle, propagate through pipeline...
265 yield alu.p.valid_i.eq(1)
266 yield
267 yield alu.p.valid_i.eq(0)
268
269 opname = code.split(' ')[0]
270 yield from sim.call(opname)
271 index = sim.pc.CIA.value//4
272
273 vld = yield alu.n.valid_o
274 while not vld:
275 yield
276 vld = yield alu.n.valid_o
277 # bug #425 investigation
278 do = alu.pipe_end.div_out
279 ctx_op = do.i.ctx.op
280 is_32bit = yield ctx_op.is_32bit
281 is_signed = yield ctx_op.is_signed
282 quotient_root = yield do.i.core.quotient_root
283 quotient_65 = yield do.quotient_65
284 dive_abs_ov32 = yield do.i.dive_abs_ov32
285 div_by_zero = yield do.i.div_by_zero
286 quotient_neg = yield do.quotient_neg
287 print ("32bit", hex(is_32bit))
288 print ("signed", hex(is_signed))
289 print ("quotient_root", hex(quotient_root))
290 print ("quotient_65", hex(quotient_65))
291 print ("div_by_zero", hex(div_by_zero))
292 print ("dive_abs_ov32", hex(dive_abs_ov32))
293 print ("quotient_neg", hex(quotient_neg))
294 print ("")
295 yield
296
297 yield from self.check_alu_outputs(alu, pdecode2, sim, code)
298 yield Settle()
299
300 sim.add_sync_process(process)
301 with sim.write_vcd("div_simulator.vcd", "div_simulator.gtkw",
302 traces=[]):
303 sim.run()
304
305 def check_alu_outputs(self, alu, dec2, sim, code):
306
307 rc = yield dec2.e.do.rc.data
308 cridx_ok = yield dec2.e.write_cr.ok
309 cridx = yield dec2.e.write_cr.data
310
311 print ("check extra output", repr(code), cridx_ok, cridx)
312 if rc:
313 self.assertEqual(cridx, 0, code)
314
315 sim_o = {}
316 res = {}
317
318 yield from ALUHelpers.get_cr_a(res, alu, dec2)
319 yield from ALUHelpers.get_xer_ov(res, alu, dec2)
320 yield from ALUHelpers.get_int_o(res, alu, dec2)
321 yield from ALUHelpers.get_xer_so(res, alu, dec2)
322
323 print ("res output", res)
324
325 yield from ALUHelpers.get_sim_int_o(sim_o, sim, dec2)
326 yield from ALUHelpers.get_wr_sim_cr_a(sim_o, sim, dec2)
327 yield from ALUHelpers.get_sim_xer_ov(sim_o, sim, dec2)
328 yield from ALUHelpers.get_sim_xer_so(sim_o, sim, dec2)
329
330 print ("sim output", sim_o)
331
332 ALUHelpers.check_int_o(self, res, sim_o, code)
333 ALUHelpers.check_cr_a(self, res, sim_o, "CR%d %s" % (cridx, code))
334 ALUHelpers.check_xer_ov(self, res, sim_o, code)
335 ALUHelpers.check_xer_so(self, res, sim_o, code)
336
337 oe = yield dec2.e.do.oe.oe
338 oe_ok = yield dec2.e.do.oe.ok
339 print ("oe, oe_ok", oe, oe_ok)
340 if not oe or not oe_ok:
341 # if OE not enabled, XER SO and OV must not be activated
342 so_ok = yield alu.n.data_o.xer_so.ok
343 ov_ok = yield alu.n.data_o.xer_ov.ok
344 print ("so, ov", so_ok, ov_ok)
345 self.assertEqual(ov_ok, False, code)
346 self.assertEqual(so_ok, False, code)
347
348
349 if __name__ == "__main__":
350 unittest.main(exit=False)
351 suite = unittest.TestSuite()
352 suite.addTest(TestRunner(DIVTestCase.test_data))
353
354 runner = unittest.TextTestRunner()
355 runner.run(suite)