use openpower.test.common
[soc.git] / src / soc / fu / mul / test / helper.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 import power_instruction_analyzer as pia
8
9 from nmigen.cli import rtlil
10 import unittest
11 from openpower.decoder.isa.caller import ISACaller, special_sprs
12 from openpower.decoder.power_decoder import (create_pdecode)
13 from openpower.decoder.power_decoder2 import (PowerDecode2)
14 from openpower.decoder.power_enums import (XER_bits, Function, MicrOp, CryIn)
15 from openpower.decoder.selectable_int import SelectableInt
16 from openpower.simulator.program import Program
17 from openpower.decoder.isa.all import ISA
18 from soc.config.endian import bigendian
19
20 from openpower.test.common import (TestAccumulatorBase, TestCase, ALUHelpers)
21 from soc.fu.test.pia import pia_res_to_output
22 from soc.fu.mul.pipeline import MulBasePipe
23 from soc.fu.mul.pipe_data import MulPipeSpec
24 import random
25
26
27 def get_cu_inputs(dec2, sim):
28 """naming (res) must conform to MulFunctionUnit 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_int_rc(res, sim, dec2) # RC
35 yield from ALUHelpers.get_sim_xer_so(res, sim, dec2) # XER.so
36
37 print("alu get_cu_inputs", res)
38
39 return res
40
41
42 def set_alu_inputs(alu, dec2, sim, has_third_input):
43 # TODO: see https://bugs.libre-soc.org/show_bug.cgi?id=305#c43
44 # detect the immediate here (with m.If(self.i.ctx.op.imm_data.imm_ok))
45 # and place it into data_i.b
46
47 inp = yield from get_cu_inputs(dec2, sim)
48 print("set alu inputs", inp)
49 yield from ALUHelpers.set_int_ra(alu, dec2, inp)
50 yield from ALUHelpers.set_int_rb(alu, dec2, inp)
51 if has_third_input:
52 yield from ALUHelpers.set_int_rc(alu, dec2, inp)
53
54 yield from ALUHelpers.set_xer_so(alu, dec2, inp)
55
56 overflow = None
57 if 'xer_so' in inp:
58 so = inp['xer_so']
59 overflow = pia.OverflowFlags(so=bool(so),
60 ov=False,
61 ov32=False)
62 rc = inp["rc"] if has_third_input else None
63 return pia.InstructionInput(ra=inp.get("ra"), rb=inp.get("rb"),
64 rc=rc, overflow=overflow)
65
66
67 class MulTestHelper(unittest.TestCase):
68 def execute(self, pdecode2, test, instruction, alu, has_third_input, sim):
69 program = test.program
70 isa_sim = ISA(pdecode2, test.regs, test.sprs, test.cr,
71 test.mem, test.msr,
72 bigendian=bigendian)
73 gen = program.generate_instructions()
74 instructions = list(zip(gen, program.assembly.splitlines()))
75 yield Settle()
76
77 index = isa_sim.pc.CIA.value//4
78 while index < len(instructions):
79 ins, code = instructions[index]
80
81 print("instruction: 0x{:X}".format(ins & 0xffffffff))
82 print(code)
83 if 'XER' in isa_sim.spr:
84 so = 1 if isa_sim.spr['XER'][XER_bits['SO']] else 0
85 ov = 1 if isa_sim.spr['XER'][XER_bits['OV']] else 0
86 ov32 = 1 if isa_sim.spr['XER'][XER_bits['OV32']] else 0
87 print("before: so/ov/32", so, ov, ov32)
88
89 # ask the decoder to decode this binary data (endian'd)
90 yield pdecode2.dec.bigendian.eq(bigendian) # little / big?
91 yield instruction.eq(ins) # raw binary instr.
92 yield Delay(0.1e-6)
93 fn_unit = yield pdecode2.e.do.fn_unit
94 self.assertEqual(fn_unit, Function.MUL.value)
95 pia_inputs = yield from set_alu_inputs(alu, pdecode2, isa_sim,
96 has_third_input)
97
98 # set valid for one cycle, propagate through pipeline...
99 yield alu.p.valid_i.eq(1)
100 yield
101 yield alu.p.valid_i.eq(0)
102
103 opname = code.split(' ')[0]
104 fnname = opname.replace(".", "_")
105 print(f"{fnname}({pia_inputs})")
106 pia_res = None
107 try:
108 pia_res = getattr(pia, fnname)(pia_inputs)
109 except AttributeError:
110 EXPECTED_FAILURES = ["mulli"]
111 if fnname not in EXPECTED_FAILURES:
112 raise
113 else:
114 print("not implemented, as expected.")
115 print(f"-> {pia_res}")
116
117 yield from isa_sim.call(opname)
118 index = isa_sim.pc.CIA.value//4
119
120 # ...wait for valid to pop out the end
121 vld = yield alu.n.valid_o
122 while not vld:
123 yield
124 yield Delay(0.1e-6)
125 vld = yield alu.n.valid_o
126 yield Delay(0.1e-6)
127
128 # XXX sim._engine is an internal variable
129 # Waiting on https://github.com/nmigen/nmigen/issues/443
130 try:
131 print(f"check time: {sim._engine.now * 1e6}us")
132 except AttributeError:
133 pass
134 msg = (f"{code!r} {program.assembly!r} "
135 f"{list(map(hex, test.regs))!r}")
136 yield from self.check_alu_outputs(alu, pdecode2, isa_sim, msg,
137 pia_res)
138 yield
139
140 def run_all(self, test_data, file_name_prefix, has_third_input):
141 m = Module()
142 comb = m.d.comb
143 instruction = Signal(32)
144
145 fn_name = "MUL"
146 opkls = MulPipeSpec.opsubsetkls
147
148 m.submodules.pdecode2 = pdecode2 = PowerDecode2(None, opkls, fn_name)
149 pdecode = pdecode2.dec
150
151 pspec = MulPipeSpec(id_wid=2)
152 m.submodules.alu = alu = MulBasePipe(pspec)
153
154 comb += alu.p.data_i.ctx.op.eq_from_execute1(pdecode2.do)
155 comb += alu.n.ready_i.eq(1)
156 comb += pdecode2.dec.raw_opcode_in.eq(instruction)
157 sim = Simulator(m)
158
159 sim.add_clock(1e-6)
160
161 def process():
162 for test in test_data:
163 print(test.name)
164 with self.subTest(test.name):
165 yield from self.execute(pdecode2, test, instruction, alu,
166 has_third_input, sim)
167
168 sim.add_sync_process(process)
169 with sim.write_vcd(f"{file_name_prefix}.vcd"):
170 sim.run()
171
172 def check_alu_outputs(self, alu, dec2, sim, code, pia_res):
173
174 rc = yield dec2.e.do.rc.rc
175 cridx_ok = yield dec2.e.write_cr.ok
176 cridx = yield dec2.e.write_cr.data
177
178 print("check extra output", repr(code), cridx_ok, cridx)
179 if rc:
180 self.assertEqual(cridx, 0, code)
181
182 oe = yield dec2.e.do.oe.oe
183 oe_ok = yield dec2.e.do.oe.ok
184 if not oe or not oe_ok:
185 # if OE not enabled, XER SO and OV must correspondingly be false
186 so_ok = yield alu.n.data_o.xer_so.ok
187 ov_ok = yield alu.n.data_o.xer_ov.ok
188 self.assertEqual(so_ok, False, code)
189 self.assertEqual(ov_ok, False, code)
190
191 sim_o = {}
192 res = {}
193
194 yield from ALUHelpers.get_cr_a(res, alu, dec2)
195 yield from ALUHelpers.get_xer_ov(res, alu, dec2)
196 yield from ALUHelpers.get_int_o(res, alu, dec2)
197 yield from ALUHelpers.get_xer_so(res, alu, dec2)
198
199 print("res output", res)
200
201 yield from ALUHelpers.get_sim_int_o(sim_o, sim, dec2)
202 yield from ALUHelpers.get_wr_sim_cr_a(sim_o, sim, dec2)
203 yield from ALUHelpers.get_sim_xer_ov(sim_o, sim, dec2)
204 yield from ALUHelpers.get_sim_xer_so(sim_o, sim, dec2)
205
206 print("sim output", sim_o)
207
208 print("power-instruction-analyzer result:")
209 print(pia_res)
210 if pia_res is not None:
211 with self.subTest(check="pia", sim_o=sim_o, pia_res=str(pia_res)):
212 pia_o = pia_res_to_output(pia_res)
213 ALUHelpers.check_int_o(self, res, pia_o, code)
214 ALUHelpers.check_cr_a(self, res, pia_o, code)
215 ALUHelpers.check_xer_ov(self, res, pia_o, code)
216 ALUHelpers.check_xer_so(self, res, pia_o, code)
217
218 with self.subTest(check="sim", sim_o=sim_o, pia_res=str(pia_res)):
219 ALUHelpers.check_int_o(self, res, sim_o, code)
220 ALUHelpers.check_xer_ov(self, res, sim_o, code)
221 ALUHelpers.check_xer_so(self, res, sim_o, code)
222 ALUHelpers.check_cr_a(self, res, sim_o, "CR%d %s" % (cridx, code))