set parent pspec to class with XLEN = 64
[soc.git] / src / soc / fu / div / test / helper.py
1 import random
2 import unittest
3 import power_instruction_analyzer as pia
4 from nmigen import Module, Signal
5
6 # NOTE: to use cxxsim, export NMIGEN_SIM_MODE=cxxsim from the shell
7 # Also, check out the cxxsim nmigen branch, and latest yosys from git
8 from nmutil.sim_tmp_alternative import Simulator, Delay
9
10 from openpower.decoder.power_decoder import (create_pdecode)
11 from openpower.decoder.power_decoder2 import (PowerDecode2)
12 from openpower.decoder.power_enums import XER_bits, Function
13 from openpower.decoder.isa.all import ISA
14 from openpower.endian import bigendian
15
16 from openpower.test.common import ALUHelpers
17 from soc.fu.test.pia import pia_res_to_output
18 from soc.fu.div.pipeline import DivBasePipe
19 from soc.fu.div.pipe_data import DivPipeSpec
20
21
22 def log_rand(n, min_val=1):
23 logrange = random.randint(1, n)
24 return random.randint(min_val, (1 << logrange)-1)
25
26
27 def get_cu_inputs(dec2, sim):
28 """naming (res) must conform to DivFunctionUnit input regspec
29 """
30 res = {}
31
32 yield from ALUHelpers.get_sim_int_ra(res, sim, dec2) # RA
33 yield from ALUHelpers.get_sim_int_rb(res, sim, dec2) # RB
34 yield from ALUHelpers.get_sim_xer_so(res, sim, dec2) # XER.so
35
36 print("alu get_cu_inputs", res)
37
38 return res
39
40
41 def set_alu_inputs(alu, dec2, sim):
42 # TODO: see https://bugs.libre-soc.org/show_bug.cgi?id=305#c43
43 # detect the immediate here (with m.If(self.i.ctx.op.imm_data.imm_ok))
44 # and place it into i_data.b
45
46 inp = yield from get_cu_inputs(dec2, sim)
47 yield from ALUHelpers.set_int_ra(alu, dec2, inp)
48 yield from ALUHelpers.set_int_rb(alu, dec2, inp)
49
50 yield from ALUHelpers.set_xer_so(alu, dec2, inp)
51
52 overflow = None
53 if 'xer_so' in inp:
54 so = inp['xer_so']
55 overflow = pia.OverflowFlags(so=bool(so),
56 ov=False,
57 ov32=False)
58 return pia.InstructionInput(ra=inp["ra"], rb=inp["rb"], overflow=overflow)
59
60
61 class DivTestHelper(unittest.TestCase):
62 def execute(self, alu, instruction, pdecode2, test, div_pipe_kind, sim):
63 prog = test.program
64 isa_sim = ISA(pdecode2, test.regs, test.sprs, test.cr,
65 test.mem, test.msr,
66 bigendian=bigendian)
67 gen = prog.generate_instructions()
68 instructions = list(zip(gen, prog.assembly.splitlines()))
69 yield Delay(0.1e-6)
70
71 index = isa_sim.pc.CIA.value//4
72 while index < len(instructions):
73 ins, code = instructions[index]
74
75 print("instruction: 0x{:X}".format(ins & 0xffffffff))
76 print(code)
77 spr = isa_sim.spr
78 if 'XER' in spr:
79 so = 1 if spr['XER'][XER_bits['SO']] else 0
80 ov = 1 if spr['XER'][XER_bits['OV']] else 0
81 ov32 = 1 if spr['XER'][XER_bits['OV32']] else 0
82 print("before: so/ov/32", so, ov, ov32)
83
84 # ask the decoder to decode this binary data (endian'd)
85 # little / big?
86 yield pdecode2.dec.bigendian.eq(bigendian)
87 yield instruction.eq(ins) # raw binary instr.
88 yield Delay(0.1e-6)
89 fn_unit = yield pdecode2.e.do.fn_unit
90 self.assertEqual(fn_unit, Function.DIV.value)
91 pia_inputs = yield from set_alu_inputs(alu, pdecode2,
92 isa_sim)
93
94 # set valid for one cycle, propagate through pipeline..
95 # note that it is critically important to do this
96 # for DIV otherwise it starts trying to produce
97 # multiple results.
98 yield alu.p.i_valid.eq(1)
99 yield
100 yield alu.p.i_valid.eq(0)
101
102 opname = code.split(' ')[0]
103 fnname = opname.replace(".", "_")
104 print(f"{fnname}({pia_inputs})")
105 pia_res = getattr(
106 pia, opname.replace(".", "_"))(pia_inputs)
107 print(f"-> {pia_res}")
108
109 yield from isa_sim.call(opname)
110 index = isa_sim.pc.CIA.value//4
111
112 vld = yield alu.n.o_valid
113 while not vld:
114 yield
115 yield Delay(0.1e-6)
116 # XXX sim._engine is an internal variable
117 # Waiting on https://github.com/nmigen/nmigen/issues/443
118 try:
119 print(f"time: {sim._engine.now * 1e6}us")
120 except AttributeError:
121 pass
122 vld = yield alu.n.o_valid
123 # bug #425 investigation
124 do = alu.pipe_end.div_out
125 ctx_op = do.i.ctx.op
126 is_32bit = yield ctx_op.is_32bit
127 is_signed = yield ctx_op.is_signed
128 quotient_root = yield do.i.core.quotient_root
129 quotient_65 = yield do.quotient_65
130 dive_abs_ov32 = yield do.i.dive_abs_ov32
131 div_by_zero = yield do.i.div_by_zero
132 quotient_neg = yield do.quotient_neg
133 print("32bit", hex(is_32bit))
134 print("signed", hex(is_signed))
135 print("quotient_root", hex(quotient_root))
136 print("quotient_65", hex(quotient_65))
137 print("div_by_zero", hex(div_by_zero))
138 print("dive_abs_ov32", hex(dive_abs_ov32))
139 print("quotient_neg", hex(quotient_neg))
140 print("vld", vld)
141 print("")
142
143 yield Delay(0.1e-6)
144 # XXX sim._engine is an internal variable
145 # Waiting on https://github.com/nmigen/nmigen/issues/443
146 try:
147 print(f"check time: {sim._engine.now * 1e6}us")
148 except AttributeError:
149 pass
150 msg = "%s: %s" % (div_pipe_kind.name, code)
151 msg += f" {prog.assembly!r} {list(map(hex, test.regs))!r}"
152 yield from self.check_alu_outputs(alu, pdecode2,
153 isa_sim, msg,
154 pia_res)
155 yield
156
157 def run_all(self, test_data, div_pipe_kind, file_name_prefix):
158 m = Module()
159 comb = m.d.comb
160 instruction = Signal(32)
161
162 pdecode = create_pdecode()
163
164 m.submodules.pdecode2 = pdecode2 = PowerDecode2(pdecode)
165
166 class PPspec:
167 XLEN = 64
168 pps = PPspec()
169 pspec = DivPipeSpec(
170 id_wid=2, div_pipe_kind=div_pipe_kind, parent_pspec=pps)
171 m.submodules.alu = alu = DivBasePipe(pspec)
172
173 comb += alu.p.i_data.ctx.op.eq_from_execute1(pdecode2.do)
174 comb += alu.n.i_ready.eq(1)
175 comb += pdecode2.dec.raw_opcode_in.eq(instruction)
176 sim = Simulator(m)
177
178 sim.add_clock(1e-6)
179
180 def process():
181 for test in test_data:
182 print(test.name)
183 with self.subTest(test.name):
184 yield from self.execute(alu, instruction, pdecode2,
185 test, div_pipe_kind, sim)
186
187 sim.add_sync_process(process)
188 with sim.write_vcd(f"{file_name_prefix}_{div_pipe_kind.name}.vcd"):
189 sim.run()
190
191 def check_alu_outputs(self, alu, dec2, sim, code, pia_res):
192
193 rc = yield dec2.e.do.rc.data
194 cridx_ok = yield dec2.e.write_cr.ok
195 cridx = yield dec2.e.write_cr.data
196
197 print("check extra output", repr(code), cridx_ok, cridx)
198 if rc:
199 self.assertEqual(cridx, 0, code)
200
201 sim_o = {}
202 res = {}
203
204 yield from ALUHelpers.get_cr_a(res, alu, dec2)
205 yield from ALUHelpers.get_xer_ov(res, alu, dec2)
206 yield from ALUHelpers.get_int_o(res, alu, dec2)
207 yield from ALUHelpers.get_xer_so(res, alu, dec2)
208
209 print("res output", res)
210
211 yield from ALUHelpers.get_sim_int_o(sim_o, sim, dec2)
212 yield from ALUHelpers.get_wr_sim_cr_a(sim_o, sim, dec2)
213 yield from ALUHelpers.get_sim_xer_ov(sim_o, sim, dec2)
214 yield from ALUHelpers.get_sim_xer_so(sim_o, sim, dec2)
215
216 print("sim output", sim_o)
217
218 print("power-instruction-analyzer result:")
219 print(pia_res)
220 if pia_res is not None:
221 with self.subTest(check="pia", sim_o=sim_o, pia_res=str(pia_res)):
222 pia_o = pia_res_to_output(pia_res)
223 ALUHelpers.check_int_o(self, res, pia_o, code)
224 ALUHelpers.check_cr_a(self, res, pia_o, code)
225 ALUHelpers.check_xer_ov(self, res, pia_o, code)
226 ALUHelpers.check_xer_so(self, res, pia_o, code)
227
228 with self.subTest(check="sim", sim_o=sim_o, pia_res=str(pia_res)):
229 ALUHelpers.check_int_o(self, res, sim_o, code)
230 ALUHelpers.check_cr_a(self, res, sim_o, code)
231 ALUHelpers.check_xer_ov(self, res, sim_o, code)
232 ALUHelpers.check_xer_so(self, res, sim_o, code)
233
234 oe = yield dec2.e.do.oe.oe
235 oe_ok = yield dec2.e.do.oe.ok
236 print("oe, oe_ok", oe, oe_ok)
237 if not oe or not oe_ok:
238 # if OE not enabled, XER SO and OV must not be activated
239 so_ok = yield alu.n.o_data.xer_so.ok
240 ov_ok = yield alu.n.o_data.xer_ov.ok
241 print("so, ov", so_ok, ov_ok)
242 self.assertEqual(ov_ok, False, code)
243 self.assertEqual(so_ok, False, code)