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