Allow the formal engine to perform a same-cycle result in the ALU
[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 openpower.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 i_data.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 immediate_ok = yield dec2.e.do.imm_data.ok
63 if immediate_ok:
64 immediate = yield dec2.e.do.imm_data.data
65 else:
66 immediate = None
67 rc = inp["rc"] if has_third_input else None
68 return pia.InstructionInput(ra=inp.get("ra"), rb=inp.get("rb"),
69 immediate=immediate,
70 rc=rc, overflow=overflow)
71
72
73 class MulTestHelper(unittest.TestCase):
74 def execute(self, pdecode2, test, instruction, alu, has_third_input, sim):
75 program = test.program
76 isa_sim = ISA(pdecode2, test.regs, test.sprs, test.cr,
77 test.mem, test.msr,
78 bigendian=bigendian)
79 gen = program.generate_instructions()
80 instructions = list(zip(gen, program.assembly.splitlines()))
81 yield Settle()
82
83 index = isa_sim.pc.CIA.value//4
84 while index < len(instructions):
85 ins, code = instructions[index]
86
87 print("instruction: 0x{:X}".format(ins & 0xffffffff))
88 print(code)
89 if 'XER' in isa_sim.spr:
90 so = 1 if isa_sim.spr['XER'][XER_bits['SO']] else 0
91 ov = 1 if isa_sim.spr['XER'][XER_bits['OV']] else 0
92 ov32 = 1 if isa_sim.spr['XER'][XER_bits['OV32']] else 0
93 print("before: so/ov/32", so, ov, ov32)
94
95 # ask the decoder to decode this binary data (endian'd)
96 yield pdecode2.dec.bigendian.eq(bigendian) # little / big?
97 yield instruction.eq(ins) # raw binary instr.
98 yield Delay(0.1e-6)
99 fn_unit = yield pdecode2.e.do.fn_unit
100 self.assertEqual(fn_unit, Function.MUL.value)
101 pia_inputs = yield from set_alu_inputs(alu, pdecode2, isa_sim,
102 has_third_input)
103
104 # set valid for one cycle, propagate through pipeline...
105 yield alu.p.i_valid.eq(1)
106 yield
107 yield alu.p.i_valid.eq(0)
108
109 opname = code.split(' ')[0]
110 fnname = opname.replace(".", "_")
111 print(f"{fnname}({pia_inputs})")
112 pia_res = getattr(pia, fnname)(pia_inputs)
113 print(f"-> {pia_res}")
114
115 yield from isa_sim.call(opname)
116 index = isa_sim.pc.CIA.value//4
117
118 # ...wait for valid to pop out the end
119 vld = yield alu.n.o_valid
120 while not vld:
121 yield
122 yield Delay(0.1e-6)
123 vld = yield alu.n.o_valid
124 yield Delay(0.1e-6)
125
126 # XXX sim._engine is an internal variable
127 # Waiting on https://github.com/nmigen/nmigen/issues/443
128 try:
129 print(f"check time: {sim._engine.now * 1e6}us")
130 except AttributeError:
131 pass
132 msg = (f"{code!r} {program.assembly!r} "
133 f"{list(map(hex, test.regs))!r}")
134 yield from self.check_alu_outputs(alu, pdecode2, isa_sim, msg,
135 pia_res)
136 yield
137
138 def run_all(self, test_data, file_name_prefix, has_third_input):
139 m = Module()
140 comb = m.d.comb
141 instruction = Signal(32)
142
143 fn_name = "MUL"
144 opkls = MulPipeSpec.opsubsetkls
145
146 m.submodules.pdecode2 = pdecode2 = PowerDecode2(None, opkls, fn_name)
147 pdecode = pdecode2.dec
148
149 pspec = MulPipeSpec(id_wid=2, parent_pspec=None)
150 m.submodules.alu = alu = MulBasePipe(pspec)
151
152 comb += alu.p.i_data.ctx.op.eq_from_execute1(pdecode2.do)
153 comb += alu.n.i_ready.eq(1)
154 comb += pdecode2.dec.raw_opcode_in.eq(instruction)
155 sim = Simulator(m)
156
157 sim.add_clock(1e-6)
158
159 def process():
160 for test in test_data:
161 print(test.name)
162 with self.subTest(test.name):
163 yield from self.execute(pdecode2, test, instruction, alu,
164 has_third_input, sim)
165
166 sim.add_sync_process(process)
167 with sim.write_vcd(f"{file_name_prefix}.vcd"):
168 sim.run()
169
170 def check_alu_outputs(self, alu, dec2, sim, code, pia_res):
171
172 rc = yield dec2.e.do.rc.rc
173 cridx_ok = yield dec2.e.write_cr.ok
174 cridx = yield dec2.e.write_cr.data
175
176 print("check extra output", repr(code), cridx_ok, cridx)
177 if rc:
178 self.assertEqual(cridx, 0, code)
179
180 oe = yield dec2.e.do.oe.oe
181 oe_ok = yield dec2.e.do.oe.ok
182 if not oe or not oe_ok:
183 # if OE not enabled, XER SO and OV must correspondingly be false
184 so_ok = yield alu.n.o_data.xer_so.ok
185 ov_ok = yield alu.n.o_data.xer_ov.ok
186 self.assertEqual(so_ok, False, code)
187 self.assertEqual(ov_ok, False, code)
188
189 sim_o = {}
190 res = {}
191
192 yield from ALUHelpers.get_cr_a(res, alu, dec2)
193 yield from ALUHelpers.get_xer_ov(res, alu, dec2)
194 yield from ALUHelpers.get_int_o(res, alu, dec2)
195 yield from ALUHelpers.get_xer_so(res, alu, dec2)
196
197 print("res output", res)
198
199 yield from ALUHelpers.get_sim_int_o(sim_o, sim, dec2)
200 yield from ALUHelpers.get_wr_sim_cr_a(sim_o, sim, dec2)
201 yield from ALUHelpers.get_sim_xer_ov(sim_o, sim, dec2)
202 yield from ALUHelpers.get_sim_xer_so(sim_o, sim, dec2)
203
204 print("sim output", sim_o)
205
206 print("power-instruction-analyzer result:")
207 print(pia_res)
208 if pia_res is not None:
209 with self.subTest(check="pia", sim_o=sim_o, pia_res=str(pia_res)):
210 pia_o = pia_res_to_output(pia_res)
211 ALUHelpers.check_int_o(self, res, pia_o, code)
212 ALUHelpers.check_cr_a(self, res, pia_o, code)
213 ALUHelpers.check_xer_ov(self, res, pia_o, code)
214 ALUHelpers.check_xer_so(self, res, pia_o, code)
215
216 with self.subTest(check="sim", sim_o=sim_o, pia_res=str(pia_res)):
217 ALUHelpers.check_int_o(self, res, sim_o, code)
218 ALUHelpers.check_xer_ov(self, res, sim_o, code)
219 ALUHelpers.check_xer_so(self, res, sim_o, code)
220 ALUHelpers.check_cr_a(self, res, sim_o, "CR%d %s" % (cridx, code))