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