reversal of FXM mask for one-hot selection in OP_MTCR decode
[soc.git] / src / soc / fu / cr / test / test_pipe_caller.py
1 from nmigen import Module, Signal
2 from nmigen.back.pysim import Simulator, Delay, Settle
3 from nmigen.cli import rtlil
4 import unittest
5 from soc.decoder.isa.caller import ISACaller, special_sprs
6 from soc.decoder.power_decoder import (create_pdecode)
7 from soc.decoder.power_decoder2 import (PowerDecode2)
8 from soc.decoder.power_enums import (XER_bits, Function)
9 from soc.decoder.selectable_int import SelectableInt
10 from soc.simulator.program import Program
11 from soc.decoder.isa.all import ISA
12 from soc.config.endian import bigendian
13
14 from soc.fu.test.common import TestAccumulatorBase, TestCase, ALUHelpers
15 from soc.fu.test.common import mask_extend
16 from soc.fu.cr.pipeline import CRBasePipe
17 from soc.fu.cr.pipe_data import CRPipeSpec
18 import random
19
20
21 # This test bench is a bit different than is usual. Initially when I
22 # was writing it, I had all of the tests call a function to create a
23 # device under test and simulator, initialize the dut, run the
24 # simulation for ~2 cycles, and assert that the dut output what it
25 # should have. However, this was really slow, since it needed to
26 # create and tear down the dut and simulator for every test case.
27
28 # Now, instead of doing that, every test case in ALUTestCase puts some
29 # data into the test_data list below, describing the instructions to
30 # be tested and the initial state. Once all the tests have been run,
31 # test_data gets passed to TestRunner which then sets up the DUT and
32 # simulator once, runs all the data through it, and asserts that the
33 # results match the pseudocode sim at every cycle.
34
35 # By doing this, I've reduced the time it takes to run the test suite
36 # massively. Before, it took around 1 minute on my computer, now it
37 # takes around 3 seconds
38
39
40 class CRTestCase(TestAccumulatorBase):
41
42 def case_crop(self):
43 insns = ["crand", "cror", "crnand", "crnor", "crxor", "creqv",
44 "crandc", "crorc"]
45 for i in range(40):
46 choice = random.choice(insns)
47 ba = random.randint(0, 31)
48 bb = random.randint(0, 31)
49 bt = random.randint(0, 31)
50 lst = [f"{choice} {ba}, {bb}, {bt}"]
51 cr = random.randint(0, (1 << 32)-1)
52 self.add_case(Program(lst, bigendian), initial_cr=cr)
53
54 def case_crand(self):
55 for i in range(20):
56 lst = ["crand 0, 11, 13"]
57 cr = random.randint(0, (1 << 32)-1)
58 self.add_case(Program(lst, bigendian), initial_cr=cr)
59
60 def case_1_mcrf(self):
61 for i in range(20):
62 src = random.randint(0, 7)
63 dst = random.randint(0, 7)
64 lst = [f"mcrf {src}, {dst}"]
65 cr = random.randint(0, (1 << 32)-1)
66 self.add_case(Program(lst, bigendian), initial_cr=cr)
67
68 def case_0_mcrf(self):
69 for i in range(8):
70 lst = [f"mcrf 5, {i}"]
71 cr = 0xfeff0001
72 self.add_case(Program(lst, bigendian), initial_cr=cr)
73
74 def case_mtcrf(self):
75 for i in range(1):
76 mask = random.randint(0, 255)
77 lst = [f"mtcrf {mask}, 2"]
78 cr = random.randint(0, (1 << 32)-1)
79 initial_regs = [0] * 32
80 initial_regs[2] = random.randint(0, (1 << 32)-1)
81 self.add_case(Program(lst, bigendian), initial_regs=initial_regs,
82 initial_cr=cr)
83
84 def case_mtocrf(self):
85 for i in range(20):
86 mask = 1 << random.randint(0, 7)
87 lst = [f"mtocrf {mask}, 2"]
88 cr = random.randint(0, (1 << 32)-1)
89 initial_regs = [0] * 32
90 initial_regs[2] = random.randint(0, (1 << 32)-1)
91 self.add_case(Program(lst, bigendian), initial_regs=initial_regs,
92 initial_cr=cr)
93
94 def case_mfcr(self):
95 for i in range(1):
96 lst = ["mfcr 2"]
97 cr = random.randint(0, (1 << 32)-1)
98 self.add_case(Program(lst, bigendian), initial_cr=cr)
99
100 def case_mfocrf_regression(self):
101 """bit of a bad hack. comes from microwatt 1.bin instruction 0x106d0
102 as the mask is non-standard, gnu-as barfs. so we fake it up directly
103 from the binary
104 """
105 mask = 0b10000111
106 dis = [f"mfocrf 2, {mask}"]
107 lst = bytes([0x26, 0x78, 0xb8, 0x7c]) # 0x7cb87826
108 cr = 0x5F9E080E
109 p = Program(lst, bigendian)
110 p.assembly = '\n'.join(dis)+'\n'
111 self.add_case(p, initial_cr=cr)
112
113 def case_mtocrf_regression(self):
114 """microwatt 1.bin regression, same hack as above.
115 106b4: 21 d9 96 7d .long 0x7d96d921 # mtocrf 12, 0b01101101
116 """
117 mask = 0b01101101
118 dis = [f"mtocrf 12, {mask}"]
119 lst = bytes([0x21, 0xd9, 0x96, 0x7d]) # 0x7d96d921
120 cr = 0x529e08fe
121 initial_regs = [0] * 32
122 initial_regs[12] = 0xffffffffffffffff
123 p = Program(lst, bigendian)
124 p.assembly = '\n'.join(dis)+'\n'
125 self.add_case(p, initial_regs=initial_regs, initial_cr=cr)
126
127 def case_mfocrf_1(self):
128 lst = [f"mfocrf 2, 1"]
129 cr = 0x1234
130 self.add_case(Program(lst, bigendian), initial_cr=cr)
131
132 def case_mfocrf(self):
133 for i in range(1):
134 mask = 1 << random.randint(0, 7)
135 lst = [f"mfocrf 2, {mask}"]
136 cr = random.randint(0, (1 << 32)-1)
137 self.add_case(Program(lst, bigendian), initial_cr=cr)
138
139 def case_isel(self):
140 for i in range(20):
141 bc = random.randint(0, 31)
142 lst = [f"isel 1, 2, 3, {bc}"]
143 cr = random.randint(0, (1 << 64)-1)
144 initial_regs = [0] * 32
145 initial_regs[2] = random.randint(0, (1 << 64)-1)
146 initial_regs[3] = random.randint(0, (1 << 64)-1)
147 #initial_regs[2] = i*2
148 #initial_regs[3] = i*2+1
149 self.add_case(Program(lst, bigendian),
150 initial_regs=initial_regs, initial_cr=cr)
151
152 def case_setb(self):
153 for i in range(20):
154 bfa = random.randint(0, 7)
155 lst = [f"setb 1, {bfa}"]
156 cr = random.randint(0, (1 << 32)-1)
157 self.add_case(Program(lst, bigendian), initial_cr=cr)
158
159 def case_regression_setb(self):
160 lst = [f"setb 1, 6"]
161 cr = random.randint(0, 0x66f6b106)
162 self.add_case(Program(lst, bigendian), initial_cr=cr)
163
164 def case_ilang(self):
165 pspec = CRPipeSpec(id_wid=2)
166 alu = CRBasePipe(pspec)
167 vl = rtlil.convert(alu, ports=alu.ports())
168 with open("cr_pipeline.il", "w") as f:
169 f.write(vl)
170
171
172 def get_cu_inputs(dec2, sim):
173 """naming (res) must conform to CRFunctionUnit input regspec
174 """
175 res = {}
176 full_reg = yield dec2.e.do.read_cr_whole.data
177 full_reg_ok = yield dec2.e.do.read_cr_whole.ok
178 full_cr_mask = mask_extend(full_reg, 8, 4)
179
180 # full CR
181 print(sim.cr.get_range().value)
182 if full_reg_ok:
183 res['full_cr'] = sim.cr.get_range().value & full_cr_mask
184 else:
185 # CR A
186 cr1_en = yield dec2.e.read_cr1.ok
187 if cr1_en:
188 cr1_sel = yield dec2.e.read_cr1.data
189 res['cr_a'] = sim.crl[cr1_sel].get_range().value
190 cr2_en = yield dec2.e.read_cr2.ok
191 # CR B
192 if cr2_en:
193 cr2_sel = yield dec2.e.read_cr2.data
194 res['cr_b'] = sim.crl[cr2_sel].get_range().value
195 cr3_en = yield dec2.e.read_cr3.ok
196 # CR C
197 if cr3_en:
198 cr3_sel = yield dec2.e.read_cr3.data
199 res['cr_c'] = sim.crl[cr3_sel].get_range().value
200
201 # RA/RC
202 reg1_ok = yield dec2.e.read_reg1.ok
203 if reg1_ok:
204 data1 = yield dec2.e.read_reg1.data
205 res['ra'] = sim.gpr(data1).value
206
207 # RB (or immediate)
208 reg2_ok = yield dec2.e.read_reg2.ok
209 if reg2_ok:
210 data2 = yield dec2.e.read_reg2.data
211 res['rb'] = sim.gpr(data2).value
212
213 print("get inputs", res)
214 return res
215
216
217 class TestRunner(unittest.TestCase):
218 def __init__(self, test_data):
219 super().__init__("run_all")
220 self.test_data = test_data
221
222 def set_inputs(self, alu, dec2, simulator):
223 inp = yield from get_cu_inputs(dec2, simulator)
224 yield from ALUHelpers.set_full_cr(alu, dec2, inp)
225 yield from ALUHelpers.set_cr_a(alu, dec2, inp)
226 yield from ALUHelpers.set_cr_b(alu, dec2, inp)
227 yield from ALUHelpers.set_cr_c(alu, dec2, inp)
228 yield from ALUHelpers.set_int_ra(alu, dec2, inp)
229 yield from ALUHelpers.set_int_rb(alu, dec2, inp)
230
231 def assert_outputs(self, alu, dec2, simulator, code):
232 whole_reg_ok = yield dec2.e.do.write_cr_whole.ok
233 whole_reg_data = yield dec2.e.do.write_cr_whole.data
234 full_cr_mask = mask_extend(whole_reg_data, 8, 4)
235
236 cr_en = yield dec2.e.write_cr.ok
237 if whole_reg_ok:
238 full_cr = yield alu.n.data_o.full_cr.data & full_cr_mask
239 expected_cr = simulator.cr.get_range().value
240 print("CR whole: expected %x, actual: %x mask: %x" % \
241 (expected_cr, full_cr, full_cr_mask))
242 # HACK: only look at the bits that we expected to change
243 self.assertEqual(expected_cr & full_cr_mask, full_cr, code)
244 elif cr_en:
245 cr_sel = yield dec2.e.write_cr.data
246 expected_cr = simulator.cr.get_range().value
247 print(f"CR whole: {expected_cr:x}, sel {cr_sel}")
248 expected_cr = simulator.crl[cr_sel].get_range().value
249 real_cr = yield alu.n.data_o.cr.data
250 print(f"CR part: expected {expected_cr:x}, actual: {real_cr:x}")
251 self.assertEqual(expected_cr, real_cr, code)
252 alu_out = yield alu.n.data_o.o.data
253 out_reg_valid = yield dec2.e.write_reg.ok
254 if out_reg_valid:
255 write_reg_idx = yield dec2.e.write_reg.data
256 expected = simulator.gpr(write_reg_idx).value
257 print(f"expected {expected:x}, actual: {alu_out:x}")
258 self.assertEqual(expected, alu_out, code)
259
260 def execute(self, alu, instruction, pdecode2, test):
261 program = test.program
262 sim = ISA(pdecode2, test.regs, test.sprs, test.cr, test.mem,
263 test.msr,
264 bigendian=bigendian)
265 gen = program.generate_instructions()
266 instructions = list(zip(gen, program.assembly.splitlines()))
267
268 index = sim.pc.CIA.value//4
269 while index < len(instructions):
270 ins, code = instructions[index]
271
272 print("0x{:X}".format(ins & 0xffffffff))
273 print(code)
274
275 # ask the decoder to decode this binary data (endian'd)
276 yield pdecode2.dec.bigendian.eq(bigendian) # little / big?
277 yield instruction.eq(ins) # raw binary instr.
278 yield Settle()
279 yield from self.set_inputs(alu, pdecode2, sim)
280 yield alu.p.valid_i.eq(1)
281 fn_unit = yield pdecode2.e.do.fn_unit
282 self.assertEqual(fn_unit, Function.CR.value, code)
283 yield
284 opname = code.split(' ')[0]
285 yield from sim.call(opname)
286 index = sim.pc.CIA.value//4
287
288 vld = yield alu.n.valid_o
289 while not vld:
290 yield
291 vld = yield alu.n.valid_o
292 yield
293 yield from self.assert_outputs(alu, pdecode2, sim, code)
294
295 def run_all(self):
296 m = Module()
297 comb = m.d.comb
298 instruction = Signal(32)
299
300 pdecode = create_pdecode()
301
302 m.submodules.pdecode2 = pdecode2 = PowerDecode2(pdecode)
303
304 pspec = CRPipeSpec(id_wid=2)
305 m.submodules.alu = alu = CRBasePipe(pspec)
306
307 comb += alu.p.data_i.ctx.op.eq_from_execute1(pdecode2.e)
308 comb += alu.n.ready_i.eq(1)
309 comb += pdecode2.dec.raw_opcode_in.eq(instruction)
310 sim = Simulator(m)
311
312 sim.add_clock(1e-6)
313
314 def process():
315 for test in self.test_data:
316 print(test.name)
317 with self.subTest(test.name):
318 yield from self.execute(alu, instruction, pdecode2, test)
319
320 sim.add_sync_process(process)
321 with sim.write_vcd("cr_simulator.vcd"):
322 sim.run()
323
324
325 if __name__ == "__main__":
326 unittest.main(exit=False)
327 suite = unittest.TestSuite()
328 suite.addTest(TestRunner(CRTestCase().test_data))
329
330 runner = unittest.TextTestRunner()
331 runner.run(suite)